mfd: tmio_mmc hardware abstraction for CNF area
[safe/jmp/linux-2.6] / drivers / mfd / t7l66xb.c
1 /*
2  *
3  * Toshiba T7L66XB core mfd support
4  *
5  * Copyright (c) 2005, 2007, 2008 Ian Molton
6  * Copyright (c) 2008 Dmitry Baryshkov
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  *
12  * T7L66 features:
13  *
14  * Supported in this driver:
15  * SD/MMC
16  * SM/NAND flash controller
17  *
18  * As yet not supported
19  * GPIO interface (on NAND pins)
20  * Serial interface
21  * TFT 'interface converter'
22  * PCMCIA interface logic
23  */
24
25 #include <linux/kernel.h>
26 #include <linux/module.h>
27 #include <linux/err.h>
28 #include <linux/io.h>
29 #include <linux/irq.h>
30 #include <linux/clk.h>
31 #include <linux/platform_device.h>
32 #include <linux/mfd/core.h>
33 #include <linux/mfd/tmio.h>
34 #include <linux/mfd/t7l66xb.h>
35
36 enum {
37         T7L66XB_CELL_NAND,
38         T7L66XB_CELL_MMC,
39 };
40
41 static const struct resource t7l66xb_mmc_resources[] = {
42         {
43                 .start = 0x800,
44                 .end    = 0x9ff,
45                 .flags = IORESOURCE_MEM,
46         },
47         {
48                 .start = IRQ_T7L66XB_MMC,
49                 .end    = IRQ_T7L66XB_MMC,
50                 .flags = IORESOURCE_IRQ,
51         },
52 };
53
54 #define SCR_REVID       0x08            /* b Revision ID        */
55 #define SCR_IMR         0x42            /* b Interrupt Mask     */
56 #define SCR_DEV_CTL     0xe0            /* b Device control     */
57 #define SCR_ISR         0xe1            /* b Interrupt Status   */
58 #define SCR_GPO_OC      0xf0            /* b GPO output control */
59 #define SCR_GPO_OS      0xf1            /* b GPO output enable  */
60 #define SCR_GPI_S       0xf2            /* w GPI status         */
61 #define SCR_APDC        0xf8            /* b Active pullup down ctrl */
62
63 #define SCR_DEV_CTL_USB         BIT(0)  /* USB enable           */
64 #define SCR_DEV_CTL_MMC         BIT(1)  /* MMC enable           */
65
66 /*--------------------------------------------------------------------------*/
67
68 struct t7l66xb {
69         void __iomem            *scr;
70         /* Lock to protect registers requiring read/modify/write ops. */
71         spinlock_t              lock;
72
73         struct resource         rscr;
74         struct clk              *clk48m;
75         struct clk              *clk32k;
76         int                     irq;
77         int                     irq_base;
78 };
79
80 /*--------------------------------------------------------------------------*/
81
82 static int t7l66xb_mmc_enable(struct platform_device *mmc)
83 {
84         struct platform_device *dev = to_platform_device(mmc->dev.parent);
85         struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
86         unsigned long flags;
87         u8 dev_ctl;
88
89         clk_enable(t7l66xb->clk32k);
90
91         spin_lock_irqsave(&t7l66xb->lock, flags);
92
93         dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL);
94         dev_ctl |= SCR_DEV_CTL_MMC;
95         tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL);
96
97         spin_unlock_irqrestore(&t7l66xb->lock, flags);
98
99         tmio_core_mmc_enable(t7l66xb->scr + 0x200, 0,
100                 t7l66xb_mmc_resources[0].start & 0xfffe);
101
102         return 0;
103 }
104
105 static int t7l66xb_mmc_disable(struct platform_device *mmc)
106 {
107         struct platform_device *dev = to_platform_device(mmc->dev.parent);
108         struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
109         unsigned long flags;
110         u8 dev_ctl;
111
112         spin_lock_irqsave(&t7l66xb->lock, flags);
113
114         dev_ctl = tmio_ioread8(t7l66xb->scr + SCR_DEV_CTL);
115         dev_ctl &= ~SCR_DEV_CTL_MMC;
116         tmio_iowrite8(dev_ctl, t7l66xb->scr + SCR_DEV_CTL);
117
118         spin_unlock_irqrestore(&t7l66xb->lock, flags);
119
120         clk_disable(t7l66xb->clk32k);
121
122         return 0;
123 }
124
125 static void t7l66xb_mmc_pwr(struct platform_device *mmc, int state)
126 {
127         struct platform_device *dev = to_platform_device(mmc->dev.parent);
128         struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
129
130         tmio_core_mmc_pwr(t7l66xb->scr + 0x200, 0, state);
131 }
132
133 static void t7l66xb_mmc_clk_div(struct platform_device *mmc, int state)
134 {
135         struct platform_device *dev = to_platform_device(mmc->dev.parent);
136         struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
137
138         tmio_core_mmc_clk_div(t7l66xb->scr + 0x200, 0, state);
139 }
140
141 /*--------------------------------------------------------------------------*/
142
143 static struct tmio_mmc_data t7166xb_mmc_data = {
144         .hclk = 24000000,
145         .set_pwr = t7l66xb_mmc_pwr,
146         .set_clk_div = t7l66xb_mmc_clk_div,
147 };
148
149 static const struct resource t7l66xb_nand_resources[] = {
150         {
151                 .start  = 0xc00,
152                 .end    = 0xc07,
153                 .flags  = IORESOURCE_MEM,
154         },
155         {
156                 .start  = 0x0100,
157                 .end    = 0x01ff,
158                 .flags  = IORESOURCE_MEM,
159         },
160         {
161                 .start  = IRQ_T7L66XB_NAND,
162                 .end    = IRQ_T7L66XB_NAND,
163                 .flags  = IORESOURCE_IRQ,
164         },
165 };
166
167 static struct mfd_cell t7l66xb_cells[] = {
168         [T7L66XB_CELL_MMC] = {
169                 .name = "tmio-mmc",
170                 .enable = t7l66xb_mmc_enable,
171                 .disable = t7l66xb_mmc_disable,
172                 .driver_data = &t7166xb_mmc_data,
173                 .num_resources = ARRAY_SIZE(t7l66xb_mmc_resources),
174                 .resources = t7l66xb_mmc_resources,
175         },
176         [T7L66XB_CELL_NAND] = {
177                 .name = "tmio-nand",
178                 .num_resources = ARRAY_SIZE(t7l66xb_nand_resources),
179                 .resources = t7l66xb_nand_resources,
180         },
181 };
182
183 /*--------------------------------------------------------------------------*/
184
185 /* Handle the T7L66XB interrupt mux */
186 static void t7l66xb_irq(unsigned int irq, struct irq_desc *desc)
187 {
188         struct t7l66xb *t7l66xb = get_irq_data(irq);
189         unsigned int isr;
190         unsigned int i, irq_base;
191
192         irq_base = t7l66xb->irq_base;
193
194         while ((isr = tmio_ioread8(t7l66xb->scr + SCR_ISR) &
195                                 ~tmio_ioread8(t7l66xb->scr + SCR_IMR)))
196                 for (i = 0; i < T7L66XB_NR_IRQS; i++)
197                         if (isr & (1 << i))
198                                 generic_handle_irq(irq_base + i);
199 }
200
201 static void t7l66xb_irq_mask(unsigned int irq)
202 {
203         struct t7l66xb *t7l66xb = get_irq_chip_data(irq);
204         unsigned long                   flags;
205         u8 imr;
206
207         spin_lock_irqsave(&t7l66xb->lock, flags);
208         imr = tmio_ioread8(t7l66xb->scr + SCR_IMR);
209         imr |= 1 << (irq - t7l66xb->irq_base);
210         tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR);
211         spin_unlock_irqrestore(&t7l66xb->lock, flags);
212 }
213
214 static void t7l66xb_irq_unmask(unsigned int irq)
215 {
216         struct t7l66xb *t7l66xb = get_irq_chip_data(irq);
217         unsigned long flags;
218         u8 imr;
219
220         spin_lock_irqsave(&t7l66xb->lock, flags);
221         imr = tmio_ioread8(t7l66xb->scr + SCR_IMR);
222         imr &= ~(1 << (irq - t7l66xb->irq_base));
223         tmio_iowrite8(imr, t7l66xb->scr + SCR_IMR);
224         spin_unlock_irqrestore(&t7l66xb->lock, flags);
225 }
226
227 static struct irq_chip t7l66xb_chip = {
228         .name   = "t7l66xb",
229         .ack    = t7l66xb_irq_mask,
230         .mask   = t7l66xb_irq_mask,
231         .unmask = t7l66xb_irq_unmask,
232 };
233
234 /*--------------------------------------------------------------------------*/
235
236 /* Install the IRQ handler */
237 static void t7l66xb_attach_irq(struct platform_device *dev)
238 {
239         struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
240         unsigned int irq, irq_base;
241
242         irq_base = t7l66xb->irq_base;
243
244         for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) {
245                 set_irq_chip(irq, &t7l66xb_chip);
246                 set_irq_chip_data(irq, t7l66xb);
247                 set_irq_handler(irq, handle_level_irq);
248 #ifdef CONFIG_ARM
249                 set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
250 #endif
251         }
252
253         set_irq_type(t7l66xb->irq, IRQ_TYPE_EDGE_FALLING);
254         set_irq_data(t7l66xb->irq, t7l66xb);
255         set_irq_chained_handler(t7l66xb->irq, t7l66xb_irq);
256 }
257
258 static void t7l66xb_detach_irq(struct platform_device *dev)
259 {
260         struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
261         unsigned int irq, irq_base;
262
263         irq_base = t7l66xb->irq_base;
264
265         set_irq_chained_handler(t7l66xb->irq, NULL);
266         set_irq_data(t7l66xb->irq, NULL);
267
268         for (irq = irq_base; irq < irq_base + T7L66XB_NR_IRQS; irq++) {
269 #ifdef CONFIG_ARM
270                 set_irq_flags(irq, 0);
271 #endif
272                 set_irq_chip(irq, NULL);
273                 set_irq_chip_data(irq, NULL);
274         }
275 }
276
277 /*--------------------------------------------------------------------------*/
278
279 #ifdef CONFIG_PM
280 static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state)
281 {
282         struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
283         struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
284
285         if (pdata && pdata->suspend)
286                 pdata->suspend(dev);
287         clk_disable(t7l66xb->clk48m);
288
289         return 0;
290 }
291
292 static int t7l66xb_resume(struct platform_device *dev)
293 {
294         struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
295         struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
296
297         clk_enable(t7l66xb->clk48m);
298         if (pdata && pdata->resume)
299                 pdata->resume(dev);
300
301         tmio_core_mmc_enable(t7l66xb->scr + 0x200, 0,
302                 t7l66xb_mmc_resources[0].start & 0xfffe);
303
304         return 0;
305 }
306 #else
307 #define t7l66xb_suspend NULL
308 #define t7l66xb_resume  NULL
309 #endif
310
311 /*--------------------------------------------------------------------------*/
312
313 static int t7l66xb_probe(struct platform_device *dev)
314 {
315         struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
316         struct t7l66xb *t7l66xb;
317         struct resource *iomem, *rscr;
318         int ret;
319
320         iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
321         if (!iomem)
322                 return -EINVAL;
323
324         t7l66xb = kzalloc(sizeof *t7l66xb, GFP_KERNEL);
325         if (!t7l66xb)
326                 return -ENOMEM;
327
328         spin_lock_init(&t7l66xb->lock);
329
330         platform_set_drvdata(dev, t7l66xb);
331
332         ret = platform_get_irq(dev, 0);
333         if (ret >= 0)
334                 t7l66xb->irq = ret;
335         else
336                 goto err_noirq;
337
338         t7l66xb->irq_base = pdata->irq_base;
339
340         t7l66xb->clk32k = clk_get(&dev->dev, "CLK_CK32K");
341         if (IS_ERR(t7l66xb->clk32k)) {
342                 ret = PTR_ERR(t7l66xb->clk32k);
343                 goto err_clk32k_get;
344         }
345
346         t7l66xb->clk48m = clk_get(&dev->dev, "CLK_CK48M");
347         if (IS_ERR(t7l66xb->clk48m)) {
348                 ret = PTR_ERR(t7l66xb->clk48m);
349                 clk_put(t7l66xb->clk32k);
350                 goto err_clk48m_get;
351         }
352
353         rscr = &t7l66xb->rscr;
354         rscr->name = "t7l66xb-core";
355         rscr->start = iomem->start;
356         rscr->end = iomem->start + 0xff;
357         rscr->flags = IORESOURCE_MEM;
358
359         ret = request_resource(iomem, rscr);
360         if (ret)
361                 goto err_request_scr;
362
363         t7l66xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1);
364         if (!t7l66xb->scr) {
365                 ret = -ENOMEM;
366                 goto err_ioremap;
367         }
368
369         clk_enable(t7l66xb->clk48m);
370
371         if (pdata && pdata->enable)
372                 pdata->enable(dev);
373
374         /* Mask all interrupts */
375         tmio_iowrite8(0xbf, t7l66xb->scr + SCR_IMR);
376
377         printk(KERN_INFO "%s rev %d @ 0x%08lx, irq %d\n",
378                 dev->name, tmio_ioread8(t7l66xb->scr + SCR_REVID),
379                 (unsigned long)iomem->start, t7l66xb->irq);
380
381         t7l66xb_attach_irq(dev);
382
383         t7l66xb_cells[T7L66XB_CELL_NAND].driver_data = pdata->nand_data;
384         t7l66xb_cells[T7L66XB_CELL_NAND].platform_data =
385                 &t7l66xb_cells[T7L66XB_CELL_NAND];
386         t7l66xb_cells[T7L66XB_CELL_NAND].data_size =
387                 sizeof(t7l66xb_cells[T7L66XB_CELL_NAND]);
388
389         t7l66xb_cells[T7L66XB_CELL_MMC].platform_data =
390                 &t7l66xb_cells[T7L66XB_CELL_MMC];
391         t7l66xb_cells[T7L66XB_CELL_MMC].data_size =
392                 sizeof(t7l66xb_cells[T7L66XB_CELL_MMC]);
393
394         ret = mfd_add_devices(&dev->dev, dev->id,
395                               t7l66xb_cells, ARRAY_SIZE(t7l66xb_cells),
396                               iomem, t7l66xb->irq_base);
397
398         if (!ret)
399                 return 0;
400
401         t7l66xb_detach_irq(dev);
402         iounmap(t7l66xb->scr);
403 err_ioremap:
404         release_resource(&t7l66xb->rscr);
405 err_request_scr:
406         kfree(t7l66xb);
407         clk_put(t7l66xb->clk48m);
408 err_clk48m_get:
409         clk_put(t7l66xb->clk32k);
410 err_clk32k_get:
411 err_noirq:
412         return ret;
413 }
414
415 static int t7l66xb_remove(struct platform_device *dev)
416 {
417         struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
418         struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
419         int ret;
420
421         ret = pdata->disable(dev);
422         clk_disable(t7l66xb->clk48m);
423         clk_put(t7l66xb->clk48m);
424         t7l66xb_detach_irq(dev);
425         iounmap(t7l66xb->scr);
426         release_resource(&t7l66xb->rscr);
427         mfd_remove_devices(&dev->dev);
428         platform_set_drvdata(dev, NULL);
429         kfree(t7l66xb);
430
431         return ret;
432
433 }
434
435 static struct platform_driver t7l66xb_platform_driver = {
436         .driver = {
437                 .name   = "t7l66xb",
438                 .owner  = THIS_MODULE,
439         },
440         .suspend        = t7l66xb_suspend,
441         .resume         = t7l66xb_resume,
442         .probe          = t7l66xb_probe,
443         .remove         = t7l66xb_remove,
444 };
445
446 /*--------------------------------------------------------------------------*/
447
448 static int __init t7l66xb_init(void)
449 {
450         int retval = 0;
451
452         retval = platform_driver_register(&t7l66xb_platform_driver);
453         return retval;
454 }
455
456 static void __exit t7l66xb_exit(void)
457 {
458         platform_driver_unregister(&t7l66xb_platform_driver);
459 }
460
461 module_init(t7l66xb_init);
462 module_exit(t7l66xb_exit);
463
464 MODULE_DESCRIPTION("Toshiba T7L66XB core driver");
465 MODULE_LICENSE("GPL v2");
466 MODULE_AUTHOR("Ian Molton");
467 MODULE_ALIAS("platform:t7l66xb");