Blame view

drivers/mfd/asic3.c 25.9 KB
fa9ff4b18   Samuel Ortiz   ASIC3 driver
1
2
3
4
5
6
7
8
9
10
11
  /*
   * driver/mfd/asic3.c
   *
   * Compaq ASIC3 support.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   * Copyright 2001 Compaq Computer Corporation.
   * Copyright 2004-2005 Phil Blundell
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
12
   * Copyright 2007-2008 OpenedHand Ltd.
fa9ff4b18   Samuel Ortiz   ASIC3 driver
13
14
15
16
17
   *
   * Authors: Phil Blundell <pb@handhelds.org>,
   *	    Samuel Ortiz <sameo@openedhand.com>
   *
   */
fa9ff4b18   Samuel Ortiz   ASIC3 driver
18
  #include <linux/kernel.h>
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
19
  #include <linux/delay.h>
fa9ff4b18   Samuel Ortiz   ASIC3 driver
20
  #include <linux/irq.h>
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
21
  #include <linux/gpio.h>
5d4a357d8   Paul Gortmaker   mfd: Add export.h...
22
  #include <linux/export.h>
fa9ff4b18   Samuel Ortiz   ASIC3 driver
23
  #include <linux/io.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
24
  #include <linux/slab.h>
fa9ff4b18   Samuel Ortiz   ASIC3 driver
25
26
27
28
  #include <linux/spinlock.h>
  #include <linux/platform_device.h>
  
  #include <linux/mfd/asic3.h>
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
29
30
  #include <linux/mfd/core.h>
  #include <linux/mfd/ds1wm.h>
09f05ce85   Philipp Zabel   mfd: asic3: enabl...
31
  #include <linux/mfd/tmio.h>
fa9ff4b18   Samuel Ortiz   ASIC3 driver
32

e956a2a87   Philipp Zabel   mfd: asic3: add c...
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
  enum {
  	ASIC3_CLOCK_SPI,
  	ASIC3_CLOCK_OWM,
  	ASIC3_CLOCK_PWM0,
  	ASIC3_CLOCK_PWM1,
  	ASIC3_CLOCK_LED0,
  	ASIC3_CLOCK_LED1,
  	ASIC3_CLOCK_LED2,
  	ASIC3_CLOCK_SD_HOST,
  	ASIC3_CLOCK_SD_BUS,
  	ASIC3_CLOCK_SMBUS,
  	ASIC3_CLOCK_EX0,
  	ASIC3_CLOCK_EX1,
  };
  
  struct asic3_clk {
  	int enabled;
  	unsigned int cdex;
  	unsigned long rate;
  };
  
  #define INIT_CDEX(_name, _rate)	\
  	[ASIC3_CLOCK_##_name] = {		\
  		.cdex = CLOCK_CDEX_##_name,	\
  		.rate = _rate,			\
  	}
59f2ad2e0   Mark Brown   mfd: Staticise un...
59
  static struct asic3_clk asic3_clk_init[] __initdata = {
e956a2a87   Philipp Zabel   mfd: asic3: add c...
60
61
62
63
64
65
66
67
68
69
70
71
72
  	INIT_CDEX(SPI, 0),
  	INIT_CDEX(OWM, 5000000),
  	INIT_CDEX(PWM0, 0),
  	INIT_CDEX(PWM1, 0),
  	INIT_CDEX(LED0, 0),
  	INIT_CDEX(LED1, 0),
  	INIT_CDEX(LED2, 0),
  	INIT_CDEX(SD_HOST, 24576000),
  	INIT_CDEX(SD_BUS, 12288000),
  	INIT_CDEX(SMBUS, 0),
  	INIT_CDEX(EX0, 32768),
  	INIT_CDEX(EX1, 24576000),
  };
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
73
74
75
76
77
78
79
80
81
  struct asic3 {
  	void __iomem *mapping;
  	unsigned int bus_shift;
  	unsigned int irq_nr;
  	unsigned int irq_base;
  	spinlock_t lock;
  	u16 irq_bothedge[4];
  	struct gpio_chip gpio;
  	struct device *dev;
64e8867ba   Ian Molton   mfd: tmio_mmc har...
82
  	void __iomem *tmio_cnf;
e956a2a87   Philipp Zabel   mfd: asic3: add c...
83
84
  
  	struct asic3_clk clocks[ARRAY_SIZE(asic3_clk_init)];
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
85
86
87
  };
  
  static int asic3_gpio_get(struct gpio_chip *chip, unsigned offset);
13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
88
  void asic3_write_register(struct asic3 *asic, unsigned int reg, u32 value)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
89
  {
b32661e06   Al Viro   mfd/asic3: ioread...
90
  	iowrite16(value, asic->mapping +
fa9ff4b18   Samuel Ortiz   ASIC3 driver
91
92
  		  (reg >> asic->bus_shift));
  }
13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
93
  EXPORT_SYMBOL_GPL(asic3_write_register);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
94

13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
95
  u32 asic3_read_register(struct asic3 *asic, unsigned int reg)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
96
  {
b32661e06   Al Viro   mfd/asic3: ioread...
97
  	return ioread16(asic->mapping +
fa9ff4b18   Samuel Ortiz   ASIC3 driver
98
99
  			(reg >> asic->bus_shift));
  }
13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
100
  EXPORT_SYMBOL_GPL(asic3_read_register);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
101

59f2ad2e0   Mark Brown   mfd: Staticise un...
102
  static void asic3_set_register(struct asic3 *asic, u32 reg, u32 bits, bool set)
6483c1b5e   Philipp Zabel   mfd: asic3: add a...
103
104
105
106
107
108
109
110
111
112
113
114
115
  {
  	unsigned long flags;
  	u32 val;
  
  	spin_lock_irqsave(&asic->lock, flags);
  	val = asic3_read_register(asic, reg);
  	if (set)
  		val |= bits;
  	else
  		val &= ~bits;
  	asic3_write_register(asic, reg, val);
  	spin_unlock_irqrestore(&asic->lock, flags);
  }
fa9ff4b18   Samuel Ortiz   ASIC3 driver
116
117
  /* IRQs */
  #define MAX_ASIC_ISR_LOOPS    20
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
118
119
  #define ASIC3_GPIO_BASE_INCR \
  	(ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
120
121
122
123
124
125
126
127
128
  
  static void asic3_irq_flip_edge(struct asic3 *asic,
  				u32 base, int bit)
  {
  	u16 edge;
  	unsigned long flags;
  
  	spin_lock_irqsave(&asic->lock, flags);
  	edge = asic3_read_register(asic,
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
129
  				   base + ASIC3_GPIO_EDGE_TRIGGER);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
130
131
  	edge ^= bit;
  	asic3_write_register(asic,
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
132
  			     base + ASIC3_GPIO_EDGE_TRIGGER, edge);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
133
134
135
136
137
  	spin_unlock_irqrestore(&asic->lock, flags);
  }
  
  static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc)
  {
52a7d6077   Thomas Gleixner   mfd: asic3: Clean...
138
139
  	struct asic3 *asic = irq_desc_get_handler_data(desc);
  	struct irq_data *data = irq_desc_get_irq_data(desc);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
140
141
  	int iter, i;
  	unsigned long flags;
fa9ff4b18   Samuel Ortiz   ASIC3 driver
142

a09aee8b6   Axel Lin   mfd: Fix asic3 bu...
143
  	data->chip->irq_ack(data);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
144
145
146
147
148
149
150
  
  	for (iter = 0 ; iter < MAX_ASIC_ISR_LOOPS; iter++) {
  		u32 status;
  		int bank;
  
  		spin_lock_irqsave(&asic->lock, flags);
  		status = asic3_read_register(asic,
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
151
  					     ASIC3_OFFSET(INTR, P_INT_STAT));
fa9ff4b18   Samuel Ortiz   ASIC3 driver
152
153
154
155
156
157
158
159
160
161
  		spin_unlock_irqrestore(&asic->lock, flags);
  
  		/* Check all ten register bits */
  		if ((status & 0x3ff) == 0)
  			break;
  
  		/* Handle GPIO IRQs */
  		for (bank = 0; bank < ASIC3_NUM_GPIO_BANKS; bank++) {
  			if (status & (1 << bank)) {
  				unsigned long base, istat;
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
162
163
  				base = ASIC3_GPIO_A_BASE
  				       + bank * ASIC3_GPIO_BASE_INCR;
fa9ff4b18   Samuel Ortiz   ASIC3 driver
164
165
166
167
  
  				spin_lock_irqsave(&asic->lock, flags);
  				istat = asic3_read_register(asic,
  							    base +
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
168
  							    ASIC3_GPIO_INT_STATUS);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
169
170
171
  				/* Clearing IntStatus */
  				asic3_write_register(asic,
  						     base +
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
172
  						     ASIC3_GPIO_INT_STATUS, 0);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
173
174
175
176
177
178
179
180
181
182
183
184
  				spin_unlock_irqrestore(&asic->lock, flags);
  
  				for (i = 0; i < ASIC3_GPIOS_PER_BANK; i++) {
  					int bit = (1 << i);
  					unsigned int irqnr;
  
  					if (!(istat & bit))
  						continue;
  
  					irqnr = asic->irq_base +
  						(ASIC3_GPIOS_PER_BANK * bank)
  						+ i;
52a7d6077   Thomas Gleixner   mfd: asic3: Clean...
185
  					generic_handle_irq(irqnr);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
186
187
188
189
190
191
192
193
194
195
  					if (asic->irq_bothedge[bank] & bit)
  						asic3_irq_flip_edge(asic, base,
  								    bit);
  				}
  			}
  		}
  
  		/* Handle remaining IRQs in the status register */
  		for (i = ASIC3_NUM_GPIOS; i < ASIC3_NR_IRQS; i++) {
  			/* They start at bit 4 and go up */
52a7d6077   Thomas Gleixner   mfd: asic3: Clean...
196
197
  			if (status & (1 << (i - ASIC3_NUM_GPIOS + 4)))
  				generic_handle_irq(asic->irq_base + i);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
198
199
200
201
  		}
  	}
  
  	if (iter >= MAX_ASIC_ISR_LOOPS)
24f4f2eef   Samuel Ortiz   mfd: use dev_* ma...
202
203
  		dev_err(asic->dev, "interrupt processing overrun
  ");
fa9ff4b18   Samuel Ortiz   ASIC3 driver
204
205
206
207
208
209
210
  }
  
  static inline int asic3_irq_to_bank(struct asic3 *asic, int irq)
  {
  	int n;
  
  	n = (irq - asic->irq_base) >> 4;
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
211
  	return (n * (ASIC3_GPIO_B_BASE - ASIC3_GPIO_A_BASE));
fa9ff4b18   Samuel Ortiz   ASIC3 driver
212
213
214
215
216
217
  }
  
  static inline int asic3_irq_to_index(struct asic3 *asic, int irq)
  {
  	return (irq - asic->irq_base) & 0xf;
  }
0f76aaebe   Mark Brown   mfd: Convert ASIC...
218
  static void asic3_mask_gpio_irq(struct irq_data *data)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
219
  {
0f76aaebe   Mark Brown   mfd: Convert ASIC...
220
  	struct asic3 *asic = irq_data_get_irq_chip_data(data);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
221
222
  	u32 val, bank, index;
  	unsigned long flags;
0f76aaebe   Mark Brown   mfd: Convert ASIC...
223
224
  	bank = asic3_irq_to_bank(asic, data->irq);
  	index = asic3_irq_to_index(asic, data->irq);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
225
226
  
  	spin_lock_irqsave(&asic->lock, flags);
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
227
  	val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
228
  	val |= 1 << index;
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
229
  	asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
230
231
  	spin_unlock_irqrestore(&asic->lock, flags);
  }
0f76aaebe   Mark Brown   mfd: Convert ASIC...
232
  static void asic3_mask_irq(struct irq_data *data)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
233
  {
0f76aaebe   Mark Brown   mfd: Convert ASIC...
234
  	struct asic3 *asic = irq_data_get_irq_chip_data(data);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
235
236
237
238
239
  	int regval;
  	unsigned long flags;
  
  	spin_lock_irqsave(&asic->lock, flags);
  	regval = asic3_read_register(asic,
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
240
241
  				     ASIC3_INTR_BASE +
  				     ASIC3_INTR_INT_MASK);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
242
243
  
  	regval &= ~(ASIC3_INTMASK_MASK0 <<
0f76aaebe   Mark Brown   mfd: Convert ASIC...
244
  		    (data->irq - (asic->irq_base + ASIC3_NUM_GPIOS)));
fa9ff4b18   Samuel Ortiz   ASIC3 driver
245
246
  
  	asic3_write_register(asic,
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
247
248
  			     ASIC3_INTR_BASE +
  			     ASIC3_INTR_INT_MASK,
fa9ff4b18   Samuel Ortiz   ASIC3 driver
249
250
251
  			     regval);
  	spin_unlock_irqrestore(&asic->lock, flags);
  }
0f76aaebe   Mark Brown   mfd: Convert ASIC...
252
  static void asic3_unmask_gpio_irq(struct irq_data *data)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
253
  {
0f76aaebe   Mark Brown   mfd: Convert ASIC...
254
  	struct asic3 *asic = irq_data_get_irq_chip_data(data);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
255
256
  	u32 val, bank, index;
  	unsigned long flags;
0f76aaebe   Mark Brown   mfd: Convert ASIC...
257
258
  	bank = asic3_irq_to_bank(asic, data->irq);
  	index = asic3_irq_to_index(asic, data->irq);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
259
260
  
  	spin_lock_irqsave(&asic->lock, flags);
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
261
  	val = asic3_read_register(asic, bank + ASIC3_GPIO_MASK);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
262
  	val &= ~(1 << index);
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
263
  	asic3_write_register(asic, bank + ASIC3_GPIO_MASK, val);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
264
265
  	spin_unlock_irqrestore(&asic->lock, flags);
  }
0f76aaebe   Mark Brown   mfd: Convert ASIC...
266
  static void asic3_unmask_irq(struct irq_data *data)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
267
  {
0f76aaebe   Mark Brown   mfd: Convert ASIC...
268
  	struct asic3 *asic = irq_data_get_irq_chip_data(data);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
269
270
271
272
273
  	int regval;
  	unsigned long flags;
  
  	spin_lock_irqsave(&asic->lock, flags);
  	regval = asic3_read_register(asic,
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
274
275
  				     ASIC3_INTR_BASE +
  				     ASIC3_INTR_INT_MASK);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
276
277
  
  	regval |= (ASIC3_INTMASK_MASK0 <<
0f76aaebe   Mark Brown   mfd: Convert ASIC...
278
  		   (data->irq - (asic->irq_base + ASIC3_NUM_GPIOS)));
fa9ff4b18   Samuel Ortiz   ASIC3 driver
279
280
  
  	asic3_write_register(asic,
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
281
282
  			     ASIC3_INTR_BASE +
  			     ASIC3_INTR_INT_MASK,
fa9ff4b18   Samuel Ortiz   ASIC3 driver
283
284
285
  			     regval);
  	spin_unlock_irqrestore(&asic->lock, flags);
  }
0f76aaebe   Mark Brown   mfd: Convert ASIC...
286
  static int asic3_gpio_irq_type(struct irq_data *data, unsigned int type)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
287
  {
0f76aaebe   Mark Brown   mfd: Convert ASIC...
288
  	struct asic3 *asic = irq_data_get_irq_chip_data(data);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
289
290
291
  	u32 bank, index;
  	u16 trigger, level, edge, bit;
  	unsigned long flags;
0f76aaebe   Mark Brown   mfd: Convert ASIC...
292
293
  	bank = asic3_irq_to_bank(asic, data->irq);
  	index = asic3_irq_to_index(asic, data->irq);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
294
295
296
297
  	bit = 1<<index;
  
  	spin_lock_irqsave(&asic->lock, flags);
  	level = asic3_read_register(asic,
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
298
  				    bank + ASIC3_GPIO_LEVEL_TRIGGER);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
299
  	edge = asic3_read_register(asic,
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
300
  				   bank + ASIC3_GPIO_EDGE_TRIGGER);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
301
  	trigger = asic3_read_register(asic,
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
302
  				      bank + ASIC3_GPIO_TRIGGER_TYPE);
0f76aaebe   Mark Brown   mfd: Convert ASIC...
303
  	asic->irq_bothedge[(data->irq - asic->irq_base) >> 4] &= ~bit;
fa9ff4b18   Samuel Ortiz   ASIC3 driver
304

6cab48602   Dmitry Eremin-Solenikov   [ARM] 5179/1: Rep...
305
  	if (type == IRQ_TYPE_EDGE_RISING) {
fa9ff4b18   Samuel Ortiz   ASIC3 driver
306
307
  		trigger |= bit;
  		edge |= bit;
6cab48602   Dmitry Eremin-Solenikov   [ARM] 5179/1: Rep...
308
  	} else if (type == IRQ_TYPE_EDGE_FALLING) {
fa9ff4b18   Samuel Ortiz   ASIC3 driver
309
310
  		trigger |= bit;
  		edge &= ~bit;
6cab48602   Dmitry Eremin-Solenikov   [ARM] 5179/1: Rep...
311
  	} else if (type == IRQ_TYPE_EDGE_BOTH) {
fa9ff4b18   Samuel Ortiz   ASIC3 driver
312
  		trigger |= bit;
0f76aaebe   Mark Brown   mfd: Convert ASIC...
313
  		if (asic3_gpio_get(&asic->gpio, data->irq - asic->irq_base))
fa9ff4b18   Samuel Ortiz   ASIC3 driver
314
315
316
  			edge &= ~bit;
  		else
  			edge |= bit;
0f76aaebe   Mark Brown   mfd: Convert ASIC...
317
  		asic->irq_bothedge[(data->irq - asic->irq_base) >> 4] |= bit;
6cab48602   Dmitry Eremin-Solenikov   [ARM] 5179/1: Rep...
318
  	} else if (type == IRQ_TYPE_LEVEL_LOW) {
fa9ff4b18   Samuel Ortiz   ASIC3 driver
319
320
  		trigger &= ~bit;
  		level &= ~bit;
6cab48602   Dmitry Eremin-Solenikov   [ARM] 5179/1: Rep...
321
  	} else if (type == IRQ_TYPE_LEVEL_HIGH) {
fa9ff4b18   Samuel Ortiz   ASIC3 driver
322
323
324
325
  		trigger &= ~bit;
  		level |= bit;
  	} else {
  		/*
6cab48602   Dmitry Eremin-Solenikov   [ARM] 5179/1: Rep...
326
  		 * if type == IRQ_TYPE_NONE, we should mask interrupts, but
fa9ff4b18   Samuel Ortiz   ASIC3 driver
327
328
329
  		 * be careful to not unmask them if mask was also called.
  		 * Probably need internal state for mask.
  		 */
24f4f2eef   Samuel Ortiz   mfd: use dev_* ma...
330
331
  		dev_notice(asic->dev, "irq type not changed
  ");
fa9ff4b18   Samuel Ortiz   ASIC3 driver
332
  	}
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
333
  	asic3_write_register(asic, bank + ASIC3_GPIO_LEVEL_TRIGGER,
fa9ff4b18   Samuel Ortiz   ASIC3 driver
334
  			     level);
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
335
  	asic3_write_register(asic, bank + ASIC3_GPIO_EDGE_TRIGGER,
fa9ff4b18   Samuel Ortiz   ASIC3 driver
336
  			     edge);
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
337
  	asic3_write_register(asic, bank + ASIC3_GPIO_TRIGGER_TYPE,
fa9ff4b18   Samuel Ortiz   ASIC3 driver
338
339
340
341
342
343
344
  			     trigger);
  	spin_unlock_irqrestore(&asic->lock, flags);
  	return 0;
  }
  
  static struct irq_chip asic3_gpio_irq_chip = {
  	.name		= "ASIC3-GPIO",
0f76aaebe   Mark Brown   mfd: Convert ASIC...
345
346
347
348
  	.irq_ack	= asic3_mask_gpio_irq,
  	.irq_mask	= asic3_mask_gpio_irq,
  	.irq_unmask	= asic3_unmask_gpio_irq,
  	.irq_set_type	= asic3_gpio_irq_type,
fa9ff4b18   Samuel Ortiz   ASIC3 driver
349
350
351
352
  };
  
  static struct irq_chip asic3_irq_chip = {
  	.name		= "ASIC3",
0f76aaebe   Mark Brown   mfd: Convert ASIC...
353
354
355
  	.irq_ack	= asic3_mask_irq,
  	.irq_mask	= asic3_mask_irq,
  	.irq_unmask	= asic3_unmask_irq,
fa9ff4b18   Samuel Ortiz   ASIC3 driver
356
  };
065032f61   Philipp Zabel   mfd: move asic3 p...
357
  static int __init asic3_irq_probe(struct platform_device *pdev)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
358
359
360
361
  {
  	struct asic3 *asic = platform_get_drvdata(pdev);
  	unsigned long clksel = 0;
  	unsigned int irq, irq_base;
c491b2ffa   Roel Kluin   asic3: platform_g...
362
  	int ret;
fa9ff4b18   Samuel Ortiz   ASIC3 driver
363

c491b2ffa   Roel Kluin   asic3: platform_g...
364
365
366
367
  	ret = platform_get_irq(pdev, 0);
  	if (ret < 0)
  		return ret;
  	asic->irq_nr = ret;
fa9ff4b18   Samuel Ortiz   ASIC3 driver
368
369
370
371
372
373
374
375
376
377
  
  	/* turn on clock to IRQ controller */
  	clksel |= CLOCK_SEL_CX;
  	asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL),
  			     clksel);
  
  	irq_base = asic->irq_base;
  
  	for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) {
  		if (irq < asic->irq_base + ASIC3_NUM_GPIOS)
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
378
  			irq_set_chip(irq, &asic3_gpio_irq_chip);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
379
  		else
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
380
  			irq_set_chip(irq, &asic3_irq_chip);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
381

d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
382
383
  		irq_set_chip_data(irq, asic);
  		irq_set_handler(irq, handle_level_irq);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
384
385
  		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
  	}
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
386
  	asic3_write_register(asic, ASIC3_OFFSET(INTR, INT_MASK),
fa9ff4b18   Samuel Ortiz   ASIC3 driver
387
  			     ASIC3_INTMASK_GINTMASK);
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
388
389
390
  	irq_set_chained_handler(asic->irq_nr, asic3_irq_demux);
  	irq_set_irq_type(asic->irq_nr, IRQ_TYPE_EDGE_RISING);
  	irq_set_handler_data(asic->irq_nr, asic);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
391
392
393
394
395
396
397
398
399
400
401
402
403
  
  	return 0;
  }
  
  static void asic3_irq_remove(struct platform_device *pdev)
  {
  	struct asic3 *asic = platform_get_drvdata(pdev);
  	unsigned int irq, irq_base;
  
  	irq_base = asic->irq_base;
  
  	for (irq = irq_base; irq < irq_base + ASIC3_NR_IRQS; irq++) {
  		set_irq_flags(irq, 0);
d6f7ce9f7   Thomas Gleixner   mfd: Fold irq_set...
404
  		irq_set_chip_and_handler(irq, NULL, NULL);
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
405
  		irq_set_chip_data(irq, NULL);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
406
  	}
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
407
  	irq_set_chained_handler(asic->irq_nr, NULL);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
408
409
410
  }
  
  /* GPIOs */
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
411
412
413
414
415
416
417
418
419
420
  static int asic3_gpio_direction(struct gpio_chip *chip,
  				unsigned offset, int out)
  {
  	u32 mask = ASIC3_GPIO_TO_MASK(offset), out_reg;
  	unsigned int gpio_base;
  	unsigned long flags;
  	struct asic3 *asic;
  
  	asic = container_of(chip, struct asic3, gpio);
  	gpio_base = ASIC3_GPIO_TO_BASE(offset);
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
421
  	if (gpio_base > ASIC3_GPIO_D_BASE) {
24f4f2eef   Samuel Ortiz   mfd: use dev_* ma...
422
423
424
  		dev_err(asic->dev, "Invalid base (0x%x) for gpio %d
  ",
  			gpio_base, offset);
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
425
426
427
428
  		return -EINVAL;
  	}
  
  	spin_lock_irqsave(&asic->lock, flags);
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
429
  	out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_DIRECTION);
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
430
431
432
433
434
435
  
  	/* Input is 0, Output is 1 */
  	if (out)
  		out_reg |= mask;
  	else
  		out_reg &= ~mask;
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
436
  	asic3_write_register(asic, gpio_base + ASIC3_GPIO_DIRECTION, out_reg);
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
  
  	spin_unlock_irqrestore(&asic->lock, flags);
  
  	return 0;
  
  }
  
  static int asic3_gpio_direction_input(struct gpio_chip *chip,
  				      unsigned offset)
  {
  	return asic3_gpio_direction(chip, offset, 0);
  }
  
  static int asic3_gpio_direction_output(struct gpio_chip *chip,
  				       unsigned offset, int value)
  {
  	return asic3_gpio_direction(chip, offset, 1);
  }
  
  static int asic3_gpio_get(struct gpio_chip *chip,
  			  unsigned offset)
  {
  	unsigned int gpio_base;
  	u32 mask = ASIC3_GPIO_TO_MASK(offset);
  	struct asic3 *asic;
  
  	asic = container_of(chip, struct asic3, gpio);
  	gpio_base = ASIC3_GPIO_TO_BASE(offset);
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
465
  	if (gpio_base > ASIC3_GPIO_D_BASE) {
24f4f2eef   Samuel Ortiz   mfd: use dev_* ma...
466
467
468
  		dev_err(asic->dev, "Invalid base (0x%x) for gpio %d
  ",
  			gpio_base, offset);
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
469
470
  		return -EINVAL;
  	}
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
471
  	return asic3_read_register(asic, gpio_base + ASIC3_GPIO_STATUS) & mask;
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
472
473
474
475
476
477
478
479
480
481
482
483
  }
  
  static void asic3_gpio_set(struct gpio_chip *chip,
  			   unsigned offset, int value)
  {
  	u32 mask, out_reg;
  	unsigned int gpio_base;
  	unsigned long flags;
  	struct asic3 *asic;
  
  	asic = container_of(chip, struct asic3, gpio);
  	gpio_base = ASIC3_GPIO_TO_BASE(offset);
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
484
  	if (gpio_base > ASIC3_GPIO_D_BASE) {
24f4f2eef   Samuel Ortiz   mfd: use dev_* ma...
485
486
487
  		dev_err(asic->dev, "Invalid base (0x%x) for gpio %d
  ",
  			gpio_base, offset);
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
488
489
490
491
492
493
  		return;
  	}
  
  	mask = ASIC3_GPIO_TO_MASK(offset);
  
  	spin_lock_irqsave(&asic->lock, flags);
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
494
  	out_reg = asic3_read_register(asic, gpio_base + ASIC3_GPIO_OUT);
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
495
496
497
498
499
  
  	if (value)
  		out_reg |= mask;
  	else
  		out_reg &= ~mask;
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
500
  	asic3_write_register(asic, gpio_base + ASIC3_GPIO_OUT, out_reg);
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
501
502
503
504
505
  
  	spin_unlock_irqrestore(&asic->lock, flags);
  
  	return;
  }
065032f61   Philipp Zabel   mfd: move asic3 p...
506
507
  static __init int asic3_gpio_probe(struct platform_device *pdev,
  				   u16 *gpio_config, int num)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
508
  {
fa9ff4b18   Samuel Ortiz   ASIC3 driver
509
  	struct asic3 *asic = platform_get_drvdata(pdev);
3b26bf172   Samuel Ortiz   mfd: New asic3 gp...
510
511
512
513
  	u16 alt_reg[ASIC3_NUM_GPIO_BANKS];
  	u16 out_reg[ASIC3_NUM_GPIO_BANKS];
  	u16 dir_reg[ASIC3_NUM_GPIO_BANKS];
  	int i;
fa9ff4b18   Samuel Ortiz   ASIC3 driver
514

59f0cb0fd   Russell King   [ARM] remove memz...
515
516
517
  	memset(alt_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
  	memset(out_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
  	memset(dir_reg, 0, ASIC3_NUM_GPIO_BANKS * sizeof(u16));
3b26bf172   Samuel Ortiz   mfd: New asic3 gp...
518
519
  
  	/* Enable all GPIOs */
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
520
521
522
523
  	asic3_write_register(asic, ASIC3_GPIO_OFFSET(A, MASK), 0xffff);
  	asic3_write_register(asic, ASIC3_GPIO_OFFSET(B, MASK), 0xffff);
  	asic3_write_register(asic, ASIC3_GPIO_OFFSET(C, MASK), 0xffff);
  	asic3_write_register(asic, ASIC3_GPIO_OFFSET(D, MASK), 0xffff);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
524

3b26bf172   Samuel Ortiz   mfd: New asic3 gp...
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
  	for (i = 0; i < num; i++) {
  		u8 alt, pin, dir, init, bank_num, bit_num;
  		u16 config = gpio_config[i];
  
  		pin = ASIC3_CONFIG_GPIO_PIN(config);
  		alt = ASIC3_CONFIG_GPIO_ALT(config);
  		dir = ASIC3_CONFIG_GPIO_DIR(config);
  		init = ASIC3_CONFIG_GPIO_INIT(config);
  
  		bank_num = ASIC3_GPIO_TO_BANK(pin);
  		bit_num = ASIC3_GPIO_TO_BIT(pin);
  
  		alt_reg[bank_num] |= (alt << bit_num);
  		out_reg[bank_num] |= (init << bit_num);
  		dir_reg[bank_num] |= (dir << bit_num);
  	}
  
  	for (i = 0; i < ASIC3_NUM_GPIO_BANKS; i++) {
  		asic3_write_register(asic,
  				     ASIC3_BANK_TO_BASE(i) +
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
545
  				     ASIC3_GPIO_DIRECTION,
3b26bf172   Samuel Ortiz   mfd: New asic3 gp...
546
547
  				     dir_reg[i]);
  		asic3_write_register(asic,
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
548
  				     ASIC3_BANK_TO_BASE(i) + ASIC3_GPIO_OUT,
3b26bf172   Samuel Ortiz   mfd: New asic3 gp...
549
550
551
  				     out_reg[i]);
  		asic3_write_register(asic,
  				     ASIC3_BANK_TO_BASE(i) +
3b8139f8b   Samuel Ortiz   mfd: Use uppercas...
552
  				     ASIC3_GPIO_ALT_FUNCTION,
3b26bf172   Samuel Ortiz   mfd: New asic3 gp...
553
  				     alt_reg[i]);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
554
  	}
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
555
  	return gpiochip_add(&asic->gpio);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
556
  }
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
557
  static int asic3_gpio_remove(struct platform_device *pdev)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
558
  {
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
559
560
561
  	struct asic3 *asic = platform_get_drvdata(pdev);
  
  	return gpiochip_remove(&asic->gpio);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
562
  }
c29a81270   Paul Parsons   mfd: Make asic3_c...
563
  static void asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
e956a2a87   Philipp Zabel   mfd: asic3: add c...
564
565
566
567
568
569
570
571
572
573
574
  {
  	unsigned long flags;
  	u32 cdex;
  
  	spin_lock_irqsave(&asic->lock, flags);
  	if (clk->enabled++ == 0) {
  		cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX));
  		cdex |= clk->cdex;
  		asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex);
  	}
  	spin_unlock_irqrestore(&asic->lock, flags);
e956a2a87   Philipp Zabel   mfd: asic3: add c...
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
  }
  
  static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk)
  {
  	unsigned long flags;
  	u32 cdex;
  
  	WARN_ON(clk->enabled == 0);
  
  	spin_lock_irqsave(&asic->lock, flags);
  	if (--clk->enabled == 0) {
  		cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX));
  		cdex &= ~clk->cdex;
  		asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex);
  	}
  	spin_unlock_irqrestore(&asic->lock, flags);
  }
fa9ff4b18   Samuel Ortiz   ASIC3 driver
592

9461f65a8   Philipp Zabel   mfd: asic3: enabl...
593
594
595
  /* MFD cells (SPI, PWM, LED, DS1WM, MMC) */
  static struct ds1wm_driver_data ds1wm_pdata = {
  	.active_high = 1,
f607e7fc5   Jean-François Dagenais   w1: ds1wm: add a ...
596
  	.reset_recover_delay = 1,
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
597
598
599
600
601
602
603
604
605
606
  };
  
  static struct resource ds1wm_resources[] = {
  	{
  		.start = ASIC3_OWM_BASE,
  		.end   = ASIC3_OWM_BASE + 0x13,
  		.flags = IORESOURCE_MEM,
  	},
  	{
  		.start = ASIC3_IRQ_OWM,
fe421425d   Mark Brown   mfd: Correct ASIC...
607
  		.end   = ASIC3_IRQ_OWM,
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
  		.flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
  	},
  };
  
  static int ds1wm_enable(struct platform_device *pdev)
  {
  	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
  
  	/* Turn on external clocks and the OWM clock */
  	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
  	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
  	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_OWM]);
  	msleep(1);
  
  	/* Reset and enable DS1WM */
  	asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET),
  			   ASIC3_EXTCF_OWM_RESET, 1);
  	msleep(1);
  	asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET),
  			   ASIC3_EXTCF_OWM_RESET, 0);
  	msleep(1);
  	asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
  			   ASIC3_EXTCF_OWM_EN, 1);
  	msleep(1);
  
  	return 0;
  }
  
  static int ds1wm_disable(struct platform_device *pdev)
  {
  	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
  
  	asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
  			   ASIC3_EXTCF_OWM_EN, 0);
  
  	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_OWM]);
  	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
  	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
  
  	return 0;
  }
  
  static struct mfd_cell asic3_cell_ds1wm = {
  	.name          = "ds1wm",
  	.enable        = ds1wm_enable,
  	.disable       = ds1wm_disable,
121ea573a   Samuel Ortiz   w1: Use device pl...
654
655
  	.platform_data = &ds1wm_pdata,
  	.pdata_size    = sizeof(ds1wm_pdata),
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
656
657
658
  	.num_resources = ARRAY_SIZE(ds1wm_resources),
  	.resources     = ds1wm_resources,
  };
64e8867ba   Ian Molton   mfd: tmio_mmc har...
659
660
661
662
663
664
665
666
667
668
669
670
671
  static void asic3_mmc_pwr(struct platform_device *pdev, int state)
  {
  	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
  
  	tmio_core_mmc_pwr(asic->tmio_cnf, 1 - asic->bus_shift, state);
  }
  
  static void asic3_mmc_clk_div(struct platform_device *pdev, int state)
  {
  	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
  
  	tmio_core_mmc_clk_div(asic->tmio_cnf, 1 - asic->bus_shift, state);
  }
09f05ce85   Philipp Zabel   mfd: asic3: enabl...
672
  static struct tmio_mmc_data asic3_mmc_data = {
64e8867ba   Ian Molton   mfd: tmio_mmc har...
673
674
675
  	.hclk           = 24576000,
  	.set_pwr        = asic3_mmc_pwr,
  	.set_clk_div    = asic3_mmc_clk_div,
09f05ce85   Philipp Zabel   mfd: asic3: enabl...
676
677
678
679
680
681
682
683
684
  };
  
  static struct resource asic3_mmc_resources[] = {
  	{
  		.start = ASIC3_SD_CTRL_BASE,
  		.end   = ASIC3_SD_CTRL_BASE + 0x3ff,
  		.flags = IORESOURCE_MEM,
  	},
  	{
09f05ce85   Philipp Zabel   mfd: asic3: enabl...
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
  		.start = 0,
  		.end   = 0,
  		.flags = IORESOURCE_IRQ,
  	},
  };
  
  static int asic3_mmc_enable(struct platform_device *pdev)
  {
  	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
  
  	/* Not sure if it must be done bit by bit, but leaving as-is */
  	asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
  			   ASIC3_SDHWCTRL_LEVCD, 1);
  	asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
  			   ASIC3_SDHWCTRL_LEVWP, 1);
  	asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
  			   ASIC3_SDHWCTRL_SUSPEND, 0);
  	asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
  			   ASIC3_SDHWCTRL_PCLR, 0);
  
  	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
  	/* CLK32 used for card detection and for interruption detection
  	 * when HCLK is stopped.
  	 */
  	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
  	msleep(1);
  
  	/* HCLK 24.576 MHz, BCLK 12.288 MHz: */
  	asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL),
  		CLOCK_SEL_CX | CLOCK_SEL_SD_HCLK_SEL);
  
  	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_SD_HOST]);
  	asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_SD_BUS]);
  	msleep(1);
  
  	asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
  			   ASIC3_EXTCF_SD_MEM_ENABLE, 1);
  
  	/* Enable SD card slot 3.3V power supply */
  	asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
  			   ASIC3_SDHWCTRL_SDPWR, 1);
64e8867ba   Ian Molton   mfd: tmio_mmc har...
726
727
728
  	/* ASIC3_SD_CTRL_BASE assumes 32-bit addressing, TMIO is 16-bit */
  	tmio_core_mmc_enable(asic->tmio_cnf, 1 - asic->bus_shift,
  			     ASIC3_SD_CTRL_BASE >> 1);
09f05ce85   Philipp Zabel   mfd: asic3: enabl...
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
  	return 0;
  }
  
  static int asic3_mmc_disable(struct platform_device *pdev)
  {
  	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
  
  	/* Put in suspend mode */
  	asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
  			   ASIC3_SDHWCTRL_SUSPEND, 1);
  
  	/* Disable clocks */
  	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_SD_HOST]);
  	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_SD_BUS]);
  	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
  	asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
  	return 0;
  }
  
  static struct mfd_cell asic3_cell_mmc = {
  	.name          = "tmio-mmc",
  	.enable        = asic3_mmc_enable,
  	.disable       = asic3_mmc_disable,
3c6e36537   Paul Parsons   mfd: Fix asic3 ba...
752
753
  	.suspend       = asic3_mmc_disable,
  	.resume        = asic3_mmc_enable,
ec71974f2   Samuel Ortiz   mmc: Use device p...
754
755
  	.platform_data = &asic3_mmc_data,
  	.pdata_size    = sizeof(asic3_mmc_data),
09f05ce85   Philipp Zabel   mfd: asic3: enabl...
756
757
758
  	.num_resources = ARRAY_SIZE(asic3_mmc_resources),
  	.resources     = asic3_mmc_resources,
  };
13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
  static const int clock_ledn[ASIC3_NUM_LEDS] = {
  	[0] = ASIC3_CLOCK_LED0,
  	[1] = ASIC3_CLOCK_LED1,
  	[2] = ASIC3_CLOCK_LED2,
  };
  
  static int asic3_leds_enable(struct platform_device *pdev)
  {
  	const struct mfd_cell *cell = mfd_get_cell(pdev);
  	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
  
  	asic3_clk_enable(asic, &asic->clocks[clock_ledn[cell->id]]);
  
  	return 0;
  }
  
  static int asic3_leds_disable(struct platform_device *pdev)
  {
  	const struct mfd_cell *cell = mfd_get_cell(pdev);
  	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
  
  	asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]);
  
  	return 0;
  }
e0b13b5b6   Paul Parsons   mfd: Add asic3 ba...
784
785
786
787
788
789
790
791
792
793
794
795
  static int asic3_leds_suspend(struct platform_device *pdev)
  {
  	const struct mfd_cell *cell = mfd_get_cell(pdev);
  	struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
  
  	while (asic3_gpio_get(&asic->gpio, ASIC3_GPIO(C, cell->id)) != 0)
  		msleep(1);
  
  	asic3_clk_disable(asic, &asic->clocks[clock_ledn[cell->id]]);
  
  	return 0;
  }
13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
796
797
798
799
800
801
  static struct mfd_cell asic3_cell_leds[ASIC3_NUM_LEDS] = {
  	[0] = {
  		.name          = "leds-asic3",
  		.id            = 0,
  		.enable        = asic3_leds_enable,
  		.disable       = asic3_leds_disable,
e0b13b5b6   Paul Parsons   mfd: Add asic3 ba...
802
803
  		.suspend       = asic3_leds_suspend,
  		.resume        = asic3_leds_enable,
13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
804
805
806
807
808
809
  	},
  	[1] = {
  		.name          = "leds-asic3",
  		.id            = 1,
  		.enable        = asic3_leds_enable,
  		.disable       = asic3_leds_disable,
e0b13b5b6   Paul Parsons   mfd: Add asic3 ba...
810
811
  		.suspend       = asic3_leds_suspend,
  		.resume        = asic3_leds_enable,
13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
812
813
814
815
816
817
  	},
  	[2] = {
  		.name          = "leds-asic3",
  		.id            = 2,
  		.enable        = asic3_leds_enable,
  		.disable       = asic3_leds_disable,
e0b13b5b6   Paul Parsons   mfd: Add asic3 ba...
818
819
  		.suspend       = asic3_leds_suspend,
  		.resume        = asic3_leds_enable,
13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
820
821
  	},
  };
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
822
  static int __init asic3_mfd_probe(struct platform_device *pdev,
13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
823
  				  struct asic3_platform_data *pdata,
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
824
825
826
  				  struct resource *mem)
  {
  	struct asic3 *asic = platform_get_drvdata(pdev);
09f05ce85   Philipp Zabel   mfd: asic3: enabl...
827
828
829
830
831
832
833
834
835
836
837
838
  	struct resource *mem_sdio;
  	int irq, ret;
  
  	mem_sdio = platform_get_resource(pdev, IORESOURCE_MEM, 1);
  	if (!mem_sdio)
  		dev_dbg(asic->dev, "no SDIO MEM resource
  ");
  
  	irq = platform_get_irq(pdev, 1);
  	if (irq < 0)
  		dev_dbg(asic->dev, "no SDIO IRQ resource
  ");
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
839
840
841
842
843
844
845
  
  	/* DS1WM */
  	asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
  			   ASIC3_EXTCF_OWM_SMB, 0);
  
  	ds1wm_resources[0].start >>= asic->bus_shift;
  	ds1wm_resources[0].end   >>= asic->bus_shift;
09f05ce85   Philipp Zabel   mfd: asic3: enabl...
846
  	/* MMC */
64e8867ba   Ian Molton   mfd: tmio_mmc har...
847
  	asic->tmio_cnf = ioremap((ASIC3_SD_CONFIG_BASE >> asic->bus_shift) +
74e32d1b6   Paul Parsons   mfd: Fix ASIC3 SD...
848
849
  				 mem_sdio->start,
  				 ASIC3_SD_CONFIG_SIZE >> asic->bus_shift);
64e8867ba   Ian Molton   mfd: tmio_mmc har...
850
851
852
853
854
855
  	if (!asic->tmio_cnf) {
  		ret = -ENOMEM;
  		dev_dbg(asic->dev, "Couldn't ioremap SD_CONFIG
  ");
  		goto out;
  	}
09f05ce85   Philipp Zabel   mfd: asic3: enabl...
856
857
  	asic3_mmc_resources[0].start >>= asic->bus_shift;
  	asic3_mmc_resources[0].end   >>= asic->bus_shift;
09f05ce85   Philipp Zabel   mfd: asic3: enabl...
858

9461f65a8   Philipp Zabel   mfd: asic3: enabl...
859
860
  	ret = mfd_add_devices(&pdev->dev, pdev->id,
  			&asic3_cell_ds1wm, 1, mem, asic->irq_base);
09f05ce85   Philipp Zabel   mfd: asic3: enabl...
861
862
  	if (ret < 0)
  		goto out;
13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
863
  	if (mem_sdio && (irq >= 0)) {
09f05ce85   Philipp Zabel   mfd: asic3: enabl...
864
865
  		ret = mfd_add_devices(&pdev->dev, pdev->id,
  			&asic3_cell_mmc, 1, mem_sdio, irq);
13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
866
867
868
869
870
871
872
873
874
875
876
877
878
879
  		if (ret < 0)
  			goto out;
  	}
  
  	if (pdata->leds) {
  		int i;
  
  		for (i = 0; i < ASIC3_NUM_LEDS; ++i) {
  			asic3_cell_leds[i].platform_data = &pdata->leds[i];
  			asic3_cell_leds[i].pdata_size = sizeof(pdata->leds[i]);
  		}
  		ret = mfd_add_devices(&pdev->dev, 0,
  			asic3_cell_leds, ASIC3_NUM_LEDS, NULL, 0);
  	}
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
880

09f05ce85   Philipp Zabel   mfd: asic3: enabl...
881
   out:
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
882
883
884
885
886
  	return ret;
  }
  
  static void asic3_mfd_remove(struct platform_device *pdev)
  {
64e8867ba   Ian Molton   mfd: tmio_mmc har...
887
  	struct asic3 *asic = platform_get_drvdata(pdev);
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
888
  	mfd_remove_devices(&pdev->dev);
64e8867ba   Ian Molton   mfd: tmio_mmc har...
889
  	iounmap(asic->tmio_cnf);
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
890
  }
fa9ff4b18   Samuel Ortiz   ASIC3 driver
891
  /* Core */
065032f61   Philipp Zabel   mfd: move asic3 p...
892
  static int __init asic3_probe(struct platform_device *pdev)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
893
894
895
896
897
  {
  	struct asic3_platform_data *pdata = pdev->dev.platform_data;
  	struct asic3 *asic;
  	struct resource *mem;
  	unsigned long clksel;
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
898
  	int ret = 0;
fa9ff4b18   Samuel Ortiz   ASIC3 driver
899
900
  
  	asic = kzalloc(sizeof(struct asic3), GFP_KERNEL);
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
901
902
903
  	if (asic == NULL) {
  		printk(KERN_ERR "kzalloc failed
  ");
fa9ff4b18   Samuel Ortiz   ASIC3 driver
904
  		return -ENOMEM;
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
905
  	}
fa9ff4b18   Samuel Ortiz   ASIC3 driver
906
907
908
909
910
911
912
913
  
  	spin_lock_init(&asic->lock);
  	platform_set_drvdata(pdev, asic);
  	asic->dev = &pdev->dev;
  
  	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (!mem) {
  		ret = -ENOMEM;
24f4f2eef   Samuel Ortiz   mfd: use dev_* ma...
914
915
  		dev_err(asic->dev, "no MEM resource
  ");
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
916
  		goto out_free;
fa9ff4b18   Samuel Ortiz   ASIC3 driver
917
  	}
be584bd5a   Philipp Zabel   mfd: asic3: use r...
918
  	asic->mapping = ioremap(mem->start, resource_size(mem));
fa9ff4b18   Samuel Ortiz   ASIC3 driver
919
920
  	if (!asic->mapping) {
  		ret = -ENOMEM;
24f4f2eef   Samuel Ortiz   mfd: use dev_* ma...
921
922
  		dev_err(asic->dev, "Couldn't ioremap
  ");
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
923
  		goto out_free;
fa9ff4b18   Samuel Ortiz   ASIC3 driver
924
925
926
  	}
  
  	asic->irq_base = pdata->irq_base;
99cdb0c8c   Philipp Zabel   mfd: let asic3 us...
927
  	/* calculate bus shift from mem resource */
be584bd5a   Philipp Zabel   mfd: asic3: use r...
928
  	asic->bus_shift = 2 - (resource_size(mem) >> 12);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
929
930
931
932
933
934
  
  	clksel = 0;
  	asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), clksel);
  
  	ret = asic3_irq_probe(pdev);
  	if (ret < 0) {
24f4f2eef   Samuel Ortiz   mfd: use dev_* ma...
935
936
  		dev_err(asic->dev, "Couldn't probe IRQs
  ");
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
937
938
  		goto out_unmap;
  	}
d8e4a88b7   Paul Parsons   mfd: Define asic3...
939
  	asic->gpio.label = "asic3";
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
940
941
942
943
944
945
  	asic->gpio.base = pdata->gpio_base;
  	asic->gpio.ngpio = ASIC3_NUM_GPIOS;
  	asic->gpio.get = asic3_gpio_get;
  	asic->gpio.set = asic3_gpio_set;
  	asic->gpio.direction_input = asic3_gpio_direction_input;
  	asic->gpio.direction_output = asic3_gpio_direction_output;
3b26bf172   Samuel Ortiz   mfd: New asic3 gp...
946
947
948
  	ret = asic3_gpio_probe(pdev,
  			       pdata->gpio_config,
  			       pdata->gpio_config_num);
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
949
  	if (ret < 0) {
24f4f2eef   Samuel Ortiz   mfd: use dev_* ma...
950
951
  		dev_err(asic->dev, "GPIO probe failed
  ");
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
952
  		goto out_irq;
fa9ff4b18   Samuel Ortiz   ASIC3 driver
953
  	}
fa9ff4b18   Samuel Ortiz   ASIC3 driver
954

e956a2a87   Philipp Zabel   mfd: asic3: add c...
955
956
957
958
  	/* Making a per-device copy is only needed for the
  	 * theoretical case of multiple ASIC3s on one board:
  	 */
  	memcpy(asic->clocks, asic3_clk_init, sizeof(asic3_clk_init));
13ca4f661   Paul Parsons   mfd: Add ASIC3 LE...
959
  	asic3_mfd_probe(pdev, pdata, mem);
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
960

24f4f2eef   Samuel Ortiz   mfd: use dev_* ma...
961
962
  	dev_info(asic->dev, "ASIC3 Core driver
  ");
fa9ff4b18   Samuel Ortiz   ASIC3 driver
963
964
  
  	return 0;
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
965
966
967
968
   out_irq:
  	asic3_irq_remove(pdev);
  
   out_unmap:
fa9ff4b18   Samuel Ortiz   ASIC3 driver
969
  	iounmap(asic->mapping);
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
970
971
  
   out_free:
fa9ff4b18   Samuel Ortiz   ASIC3 driver
972
973
974
975
  	kfree(asic);
  
  	return ret;
  }
1e3edaf6c   Uwe Kleine-König   mfd: Move asic3_r...
976
  static int __devexit asic3_remove(struct platform_device *pdev)
fa9ff4b18   Samuel Ortiz   ASIC3 driver
977
  {
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
978
  	int ret;
fa9ff4b18   Samuel Ortiz   ASIC3 driver
979
  	struct asic3 *asic = platform_get_drvdata(pdev);
9461f65a8   Philipp Zabel   mfd: asic3: enabl...
980
  	asic3_mfd_remove(pdev);
6f2384c4b   Samuel Ortiz   mfd: asic3 gpioli...
981
982
983
  	ret = asic3_gpio_remove(pdev);
  	if (ret < 0)
  		return ret;
fa9ff4b18   Samuel Ortiz   ASIC3 driver
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
  	asic3_irq_remove(pdev);
  
  	asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), 0);
  
  	iounmap(asic->mapping);
  
  	kfree(asic);
  
  	return 0;
  }
  
  static void asic3_shutdown(struct platform_device *pdev)
  {
  }
  
  static struct platform_driver asic3_device_driver = {
  	.driver		= {
  		.name	= "asic3",
  	},
fa9ff4b18   Samuel Ortiz   ASIC3 driver
1003
1004
1005
1006
1007
1008
1009
  	.remove		= __devexit_p(asic3_remove),
  	.shutdown	= asic3_shutdown,
  };
  
  static int __init asic3_init(void)
  {
  	int retval = 0;
065032f61   Philipp Zabel   mfd: move asic3 p...
1010
  	retval = platform_driver_probe(&asic3_device_driver, asic3_probe);
fa9ff4b18   Samuel Ortiz   ASIC3 driver
1011
1012
1013
1014
  	return retval;
  }
  
  subsys_initcall(asic3_init);