Blame view

drivers/mfd/t7l66xb.c 10.9 KB
1f192015c   Ian Molton   mfd: driver for t...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
  /*
   *
   * Toshiba T7L66XB core mfd support
   *
   * Copyright (c) 2005, 2007, 2008 Ian Molton
   * Copyright (c) 2008 Dmitry Baryshkov
   *
   * 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.
   *
   * T7L66 features:
   *
   * Supported in this driver:
   * SD/MMC
   * SM/NAND flash controller
   *
   * As yet not supported
   * GPIO interface (on NAND pins)
   * Serial interface
   * TFT 'interface converter'
   * PCMCIA interface logic
   */
  
  #include <linux/kernel.h>
  #include <linux/module.h>
7acb706ca   Ian Molton   mfd: update TMIO ...
27
  #include <linux/err.h>
1f192015c   Ian Molton   mfd: driver for t...
28
  #include <linux/io.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
29
  #include <linux/slab.h>
1f192015c   Ian Molton   mfd: driver for t...
30
  #include <linux/irq.h>
7acb706ca   Ian Molton   mfd: update TMIO ...
31
  #include <linux/clk.h>
1f192015c   Ian Molton   mfd: driver for t...
32
33
34
35
36
37
38
39
40
  #include <linux/platform_device.h>
  #include <linux/mfd/core.h>
  #include <linux/mfd/tmio.h>
  #include <linux/mfd/t7l66xb.h>
  
  enum {
  	T7L66XB_CELL_NAND,
  	T7L66XB_CELL_MMC,
  };
64e8867ba   Ian Molton   mfd: tmio_mmc har...
41
42
43
44
45
46
47
48
49
50
51
52
  static const struct resource t7l66xb_mmc_resources[] = {
  	{
  		.start = 0x800,
  		.end	= 0x9ff,
  		.flags = IORESOURCE_MEM,
  	},
  	{
  		.start = IRQ_T7L66XB_MMC,
  		.end	= IRQ_T7L66XB_MMC,
  		.flags = IORESOURCE_IRQ,
  	},
  };
1f192015c   Ian Molton   mfd: driver for t...
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  #define SCR_REVID	0x08		/* b Revision ID	*/
  #define SCR_IMR		0x42		/* b Interrupt Mask	*/
  #define SCR_DEV_CTL	0xe0		/* b Device control	*/
  #define SCR_ISR		0xe1		/* b Interrupt Status	*/
  #define SCR_GPO_OC	0xf0		/* b GPO output control	*/
  #define SCR_GPO_OS	0xf1		/* b GPO output enable	*/
  #define SCR_GPI_S	0xf2		/* w GPI status		*/
  #define SCR_APDC	0xf8		/* b Active pullup down ctrl */
  
  #define SCR_DEV_CTL_USB		BIT(0)	/* USB enable		*/
  #define SCR_DEV_CTL_MMC		BIT(1)	/* MMC enable		*/
  
  /*--------------------------------------------------------------------------*/
  
  struct t7l66xb {
  	void __iomem		*scr;
  	/* Lock to protect registers requiring read/modify/write ops. */
  	spinlock_t		lock;
  
  	struct resource		rscr;
7acb706ca   Ian Molton   mfd: update TMIO ...
73
74
  	struct clk		*clk48m;
  	struct clk		*clk32k;
1f192015c   Ian Molton   mfd: driver for t...
75
76
77
78
79
80
81
82
83
  	int			irq;
  	int			irq_base;
  };
  
  /*--------------------------------------------------------------------------*/
  
  static int t7l66xb_mmc_enable(struct platform_device *mmc)
  {
  	struct platform_device *dev = to_platform_device(mmc->dev.parent);
1f192015c   Ian Molton   mfd: driver for t...
84
85
86
  	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
  	unsigned long flags;
  	u8 dev_ctl;
7acb706ca   Ian Molton   mfd: update TMIO ...
87
  	clk_enable(t7l66xb->clk32k);
1f192015c   Ian Molton   mfd: driver for t...
88
89
90
91
92
93
94
95
  
  	spin_lock_irqsave(&t7l66xb->lock, flags);
  
  	dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL);
  	dev_ctl |= SCR_DEV_CTL_MMC;
  	tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL);
  
  	spin_unlock_irqrestore(&t7l66xb->lock, flags);
64e8867ba   Ian Molton   mfd: tmio_mmc har...
96
97
  	tmio_core_mmc_enable(t7l66xb->scr + 0x200, 0,
  		t7l66xb_mmc_resources[0].start & 0xfffe);
1f192015c   Ian Molton   mfd: driver for t...
98
99
100
101
102
103
  	return 0;
  }
  
  static int t7l66xb_mmc_disable(struct platform_device *mmc)
  {
  	struct platform_device *dev = to_platform_device(mmc->dev.parent);
1f192015c   Ian Molton   mfd: driver for t...
104
105
106
107
108
109
110
111
112
113
114
  	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
  	unsigned long flags;
  	u8 dev_ctl;
  
  	spin_lock_irqsave(&t7l66xb->lock, flags);
  
  	dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL);
  	dev_ctl &= ~SCR_DEV_CTL_MMC;
  	tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL);
  
  	spin_unlock_irqrestore(&t7l66xb->lock, flags);
7acb706ca   Ian Molton   mfd: update TMIO ...
115
  	clk_disable(t7l66xb->clk32k);
1f192015c   Ian Molton   mfd: driver for t...
116
117
118
  
  	return 0;
  }
64e8867ba   Ian Molton   mfd: tmio_mmc har...
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
  static void t7l66xb_mmc_pwr(struct platform_device *mmc, int state)
  {
  	struct platform_device *dev = to_platform_device(mmc->dev.parent);
  	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
  
  	tmio_core_mmc_pwr(t7l66xb->scr + 0x200, 0, state);
  }
  
  static void t7l66xb_mmc_clk_div(struct platform_device *mmc, int state)
  {
  	struct platform_device *dev = to_platform_device(mmc->dev.parent);
  	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
  
  	tmio_core_mmc_clk_div(t7l66xb->scr + 0x200, 0, state);
  }
1f192015c   Ian Molton   mfd: driver for t...
134
  /*--------------------------------------------------------------------------*/
4d3792e05   Samuel Ortiz   mfd: fix tmio rel...
135
  static struct tmio_mmc_data t7166xb_mmc_data = {
f0e46cc49   Philipp Zabel   MFD,mmc: tmio_mmc...
136
  	.hclk = 24000000,
64e8867ba   Ian Molton   mfd: tmio_mmc har...
137
138
  	.set_pwr = t7l66xb_mmc_pwr,
  	.set_clk_div = t7l66xb_mmc_clk_div,
1f192015c   Ian Molton   mfd: driver for t...
139
  };
3446d4bb9   Tobias Klauser   mfd: Storage clas...
140
  static const struct resource t7l66xb_nand_resources[] = {
1f192015c   Ian Molton   mfd: driver for t...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
  	{
  		.start	= 0xc00,
  		.end	= 0xc07,
  		.flags	= IORESOURCE_MEM,
  	},
  	{
  		.start	= 0x0100,
  		.end	= 0x01ff,
  		.flags	= IORESOURCE_MEM,
  	},
  	{
  		.start	= IRQ_T7L66XB_NAND,
  		.end	= IRQ_T7L66XB_NAND,
  		.flags	= IORESOURCE_IRQ,
  	},
  };
  
  static struct mfd_cell t7l66xb_cells[] = {
  	[T7L66XB_CELL_MMC] = {
  		.name = "tmio-mmc",
  		.enable = t7l66xb_mmc_enable,
  		.disable = t7l66xb_mmc_disable,
ec71974f2   Samuel Ortiz   mmc: Use device p...
163
164
  		.platform_data = &t7166xb_mmc_data,
  		.pdata_size    = sizeof(t7166xb_mmc_data),
1f192015c   Ian Molton   mfd: driver for t...
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
  		.num_resources = ARRAY_SIZE(t7l66xb_mmc_resources),
  		.resources = t7l66xb_mmc_resources,
  	},
  	[T7L66XB_CELL_NAND] = {
  		.name = "tmio-nand",
  		.num_resources = ARRAY_SIZE(t7l66xb_nand_resources),
  		.resources = t7l66xb_nand_resources,
  	},
  };
  
  /*--------------------------------------------------------------------------*/
  
  /* Handle the T7L66XB interrupt mux */
  static void t7l66xb_irq(unsigned int irq, struct irq_desc *desc)
  {
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
180
  	struct t7l66xb *t7l66xb = irq_get_handler_data(irq);
1f192015c   Ian Molton   mfd: driver for t...
181
182
183
184
185
186
187
188
189
190
191
  	unsigned int isr;
  	unsigned int i, irq_base;
  
  	irq_base = t7l66xb->irq_base;
  
  	while ((isr = tmio_ioread8(t7l66xb->scr + SCR_ISR) &
  				~tmio_ioread8(t7l66xb->scr + SCR_IMR)))
  		for (i = 0; i < T7L66XB_NR_IRQS; i++)
  			if (isr & (1 << i))
  				generic_handle_irq(irq_base + i);
  }
a4e7feadc   Mark Brown   mfd: Convert t716...
192
  static void t7l66xb_irq_mask(struct irq_data *data)
1f192015c   Ian Molton   mfd: driver for t...
193
  {
a4e7feadc   Mark Brown   mfd: Convert t716...
194
  	struct t7l66xb *t7l66xb = irq_data_get_irq_chip_data(data);
1f192015c   Ian Molton   mfd: driver for t...
195
196
197
198
199
  	unsigned long			flags;
  	u8 imr;
  
  	spin_lock_irqsave(&t7l66xb->lock, flags);
  	imr = tmio_ioread8(t7l66xb->scr + SCR_IMR);
a4e7feadc   Mark Brown   mfd: Convert t716...
200
  	imr |= 1 << (data->irq - t7l66xb->irq_base);
1f192015c   Ian Molton   mfd: driver for t...
201
202
203
  	tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR);
  	spin_unlock_irqrestore(&t7l66xb->lock, flags);
  }
a4e7feadc   Mark Brown   mfd: Convert t716...
204
  static void t7l66xb_irq_unmask(struct irq_data *data)
1f192015c   Ian Molton   mfd: driver for t...
205
  {
a4e7feadc   Mark Brown   mfd: Convert t716...
206
  	struct t7l66xb *t7l66xb = irq_data_get_irq_chip_data(data);
1f192015c   Ian Molton   mfd: driver for t...
207
208
209
210
211
  	unsigned long flags;
  	u8 imr;
  
  	spin_lock_irqsave(&t7l66xb->lock, flags);
  	imr = tmio_ioread8(t7l66xb->scr + SCR_IMR);
a4e7feadc   Mark Brown   mfd: Convert t716...
212
  	imr &= ~(1 << (data->irq - t7l66xb->irq_base));
1f192015c   Ian Molton   mfd: driver for t...
213
214
215
216
217
  	tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR);
  	spin_unlock_irqrestore(&t7l66xb->lock, flags);
  }
  
  static struct irq_chip t7l66xb_chip = {
a4e7feadc   Mark Brown   mfd: Convert t716...
218
219
220
221
  	.name		= "t7l66xb",
  	.irq_ack	= t7l66xb_irq_mask,
  	.irq_mask	= t7l66xb_irq_mask,
  	.irq_unmask	= t7l66xb_irq_unmask,
1f192015c   Ian Molton   mfd: driver for t...
222
223
224
225
226
227
228
229
230
231
232
233
234
  };
  
  /*--------------------------------------------------------------------------*/
  
  /* Install the IRQ handler */
  static void t7l66xb_attach_irq(struct platform_device *dev)
  {
  	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
  	unsigned int irq, irq_base;
  
  	irq_base = t7l66xb->irq_base;
  
  	for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) {
d6f7ce9f7   Thomas Gleixner   mfd: Fold irq_set...
235
  		irq_set_chip_and_handler(irq, &t7l66xb_chip, handle_level_irq);
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
236
  		irq_set_chip_data(irq, t7l66xb);
1f192015c   Ian Molton   mfd: driver for t...
237
238
239
240
  #ifdef CONFIG_ARM
  		set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
  #endif
  	}
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
241
242
243
  	irq_set_irq_type(t7l66xb->irq, IRQ_TYPE_EDGE_FALLING);
  	irq_set_handler_data(t7l66xb->irq, t7l66xb);
  	irq_set_chained_handler(t7l66xb->irq, t7l66xb_irq);
1f192015c   Ian Molton   mfd: driver for t...
244
245
246
247
248
249
250
251
  }
  
  static void t7l66xb_detach_irq(struct platform_device *dev)
  {
  	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
  	unsigned int irq, irq_base;
  
  	irq_base = t7l66xb->irq_base;
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
252
253
  	irq_set_chained_handler(t7l66xb->irq, NULL);
  	irq_set_handler_data(t7l66xb->irq, NULL);
1f192015c   Ian Molton   mfd: driver for t...
254
255
256
257
258
  
  	for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) {
  #ifdef CONFIG_ARM
  		set_irq_flags(irq, 0);
  #endif
d5bb12216   Thomas Gleixner   mfd: Cleanup irq ...
259
260
  		irq_set_chip(irq, NULL);
  		irq_set_chip_data(irq, NULL);
1f192015c   Ian Molton   mfd: driver for t...
261
262
263
264
265
266
267
268
  	}
  }
  
  /*--------------------------------------------------------------------------*/
  
  #ifdef CONFIG_PM
  static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state)
  {
7acb706ca   Ian Molton   mfd: update TMIO ...
269
  	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
1f192015c   Ian Molton   mfd: driver for t...
270
271
272
273
  	struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
  
  	if (pdata && pdata->suspend)
  		pdata->suspend(dev);
7acb706ca   Ian Molton   mfd: update TMIO ...
274
  	clk_disable(t7l66xb->clk48m);
1f192015c   Ian Molton   mfd: driver for t...
275
276
277
278
279
280
  
  	return 0;
  }
  
  static int t7l66xb_resume(struct platform_device *dev)
  {
7acb706ca   Ian Molton   mfd: update TMIO ...
281
  	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
1f192015c   Ian Molton   mfd: driver for t...
282
  	struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
7acb706ca   Ian Molton   mfd: update TMIO ...
283
  	clk_enable(t7l66xb->clk48m);
1f192015c   Ian Molton   mfd: driver for t...
284
285
  	if (pdata && pdata->resume)
  		pdata->resume(dev);
64e8867ba   Ian Molton   mfd: tmio_mmc har...
286
287
  	tmio_core_mmc_enable(t7l66xb->scr + 0x200, 0,
  		t7l66xb_mmc_resources[0].start & 0xfffe);
1f192015c   Ian Molton   mfd: driver for t...
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
  	return 0;
  }
  #else
  #define t7l66xb_suspend NULL
  #define t7l66xb_resume	NULL
  #endif
  
  /*--------------------------------------------------------------------------*/
  
  static int t7l66xb_probe(struct platform_device *dev)
  {
  	struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
  	struct t7l66xb *t7l66xb;
  	struct resource *iomem, *rscr;
  	int ret;
9ad285d6d   Samuel Ortiz   mfd: Check t7l66 ...
303
304
  	if (pdata == NULL)
  		return -EINVAL;
1f192015c   Ian Molton   mfd: driver for t...
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
  	iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
  	if (!iomem)
  		return -EINVAL;
  
  	t7l66xb = kzalloc(sizeof *t7l66xb, GFP_KERNEL);
  	if (!t7l66xb)
  		return -ENOMEM;
  
  	spin_lock_init(&t7l66xb->lock);
  
  	platform_set_drvdata(dev, t7l66xb);
  
  	ret = platform_get_irq(dev, 0);
  	if (ret >= 0)
  		t7l66xb->irq = ret;
  	else
  		goto err_noirq;
  
  	t7l66xb->irq_base = pdata->irq_base;
7acb706ca   Ian Molton   mfd: update TMIO ...
324
325
326
327
328
329
330
331
332
  	t7l66xb->clk32k = clk_get(&dev->dev, "CLK_CK32K");
  	if (IS_ERR(t7l66xb->clk32k)) {
  		ret = PTR_ERR(t7l66xb->clk32k);
  		goto err_clk32k_get;
  	}
  
  	t7l66xb->clk48m = clk_get(&dev->dev, "CLK_CK48M");
  	if (IS_ERR(t7l66xb->clk48m)) {
  		ret = PTR_ERR(t7l66xb->clk48m);
7acb706ca   Ian Molton   mfd: update TMIO ...
333
334
  		goto err_clk48m_get;
  	}
1f192015c   Ian Molton   mfd: driver for t...
335
336
337
338
339
340
341
342
343
  	rscr = &t7l66xb->rscr;
  	rscr->name = "t7l66xb-core";
  	rscr->start = iomem->start;
  	rscr->end = iomem->start + 0xff;
  	rscr->flags = IORESOURCE_MEM;
  
  	ret = request_resource(iomem, rscr);
  	if (ret)
  		goto err_request_scr;
c02e6a5ff   H Hartley Sweeten   mfd: Use resource...
344
  	t7l66xb->scr = ioremap(rscr->start, resource_size(rscr));
1f192015c   Ian Molton   mfd: driver for t...
345
346
347
348
  	if (!t7l66xb->scr) {
  		ret = -ENOMEM;
  		goto err_ioremap;
  	}
7acb706ca   Ian Molton   mfd: update TMIO ...
349
  	clk_enable(t7l66xb->clk48m);
1f192015c   Ian Molton   mfd: driver for t...
350
351
352
353
354
355
356
357
358
359
360
361
  	if (pdata && pdata->enable)
  		pdata->enable(dev);
  
  	/* Mask all interrupts */
  	tmio_iowrite8(0xbf, t7l66xb->scr + SCR_IMR);
  
  	printk(KERN_INFO "%s rev %d @ 0x%08lx, irq %d
  ",
  		dev->name, tmio_ioread8(t7l66xb->scr + SCR_REVID),
  		(unsigned long)iomem->start, t7l66xb->irq);
  
  	t7l66xb_attach_irq(dev);
7dc00a0d1   Samuel Ortiz   mtd: Use platform...
362
363
  	t7l66xb_cells[T7L66XB_CELL_NAND].platform_data = pdata->nand_data;
  	t7l66xb_cells[T7L66XB_CELL_NAND].pdata_size = sizeof(*pdata->nand_data);
8a4fbe013   Ian Molton   mfd: t7l66 MMC pl...
364

56bf2bda0   Samuel Ortiz   mfd: Fix 7l66 and...
365
366
367
  	ret = mfd_add_devices(&dev->dev, dev->id,
  			      t7l66xb_cells, ARRAY_SIZE(t7l66xb_cells),
  			      iomem, t7l66xb->irq_base);
1f192015c   Ian Molton   mfd: driver for t...
368
369
370
371
372
373
374
375
  
  	if (!ret)
  		return 0;
  
  	t7l66xb_detach_irq(dev);
  	iounmap(t7l66xb->scr);
  err_ioremap:
  	release_resource(&t7l66xb->rscr);
1f192015c   Ian Molton   mfd: driver for t...
376
  err_request_scr:
7acb706ca   Ian Molton   mfd: update TMIO ...
377
378
379
380
381
  	clk_put(t7l66xb->clk48m);
  err_clk48m_get:
  	clk_put(t7l66xb->clk32k);
  err_clk32k_get:
  err_noirq:
0e820ab60   Julia Lawall   mfd: Correct use ...
382
  	kfree(t7l66xb);
1f192015c   Ian Molton   mfd: driver for t...
383
384
385
386
387
388
389
390
391
392
  	return ret;
  }
  
  static int t7l66xb_remove(struct platform_device *dev)
  {
  	struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
  	struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
  	int ret;
  
  	ret = pdata->disable(dev);
7acb706ca   Ian Molton   mfd: update TMIO ...
393
394
  	clk_disable(t7l66xb->clk48m);
  	clk_put(t7l66xb->clk48m);
d2d272a96   Axel Lin   mfd: Properly fre...
395
396
  	clk_disable(t7l66xb->clk32k);
  	clk_put(t7l66xb->clk32k);
1f192015c   Ian Molton   mfd: driver for t...
397
398
399
  	t7l66xb_detach_irq(dev);
  	iounmap(t7l66xb->scr);
  	release_resource(&t7l66xb->rscr);
56bf2bda0   Samuel Ortiz   mfd: Fix 7l66 and...
400
  	mfd_remove_devices(&dev->dev);
1f192015c   Ian Molton   mfd: driver for t...
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
  	platform_set_drvdata(dev, NULL);
  	kfree(t7l66xb);
  
  	return ret;
  
  }
  
  static struct platform_driver t7l66xb_platform_driver = {
  	.driver = {
  		.name	= "t7l66xb",
  		.owner	= THIS_MODULE,
  	},
  	.suspend	= t7l66xb_suspend,
  	.resume		= t7l66xb_resume,
  	.probe		= t7l66xb_probe,
  	.remove		= t7l66xb_remove,
  };
  
  /*--------------------------------------------------------------------------*/
65349d60d   Mark Brown   mfd: Convert MFD ...
420
  module_platform_driver(t7l66xb_platform_driver);
1f192015c   Ian Molton   mfd: driver for t...
421
422
423
424
425
  
  MODULE_DESCRIPTION("Toshiba T7L66XB core driver");
  MODULE_LICENSE("GPL v2");
  MODULE_AUTHOR("Ian Molton");
  MODULE_ALIAS("platform:t7l66xb");