3 * Toshiba T7L66XB core mfd support
5 * Copyright (c) 2005, 2007, 2008 Ian Molton
6 * Copyright (c) 2008 Dmitry Baryshkov
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.
14 * Supported in this driver:
16 * SM/NAND flash controller
18 * As yet not supported
19 * GPIO interface (on NAND pins)
21 * TFT 'interface converter'
22 * PCMCIA interface logic
25 #include <linux/kernel.h>
26 #include <linux/module.h>
27 #include <linux/err.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>
41 static const struct resource t7l66xb_mmc_resources
[];
43 #define SCR_REVID 0x08 /* b Revision ID */
44 #define SCR_IMR 0x42 /* b Interrupt Mask */
45 #define SCR_DEV_CTL 0xe0 /* b Device control */
46 #define SCR_ISR 0xe1 /* b Interrupt Status */
47 #define SCR_GPO_OC 0xf0 /* b GPO output control */
48 #define SCR_GPO_OS 0xf1 /* b GPO output enable */
49 #define SCR_GPI_S 0xf2 /* w GPI status */
50 #define SCR_APDC 0xf8 /* b Active pullup down ctrl */
52 #define SCR_DEV_CTL_USB BIT(0) /* USB enable */
53 #define SCR_DEV_CTL_MMC BIT(1) /* MMC enable */
55 /*--------------------------------------------------------------------------*/
59 /* Lock to protect registers requiring read/modify/write ops. */
69 /*--------------------------------------------------------------------------*/
71 static int t7l66xb_mmc_enable(struct platform_device
*mmc
)
73 struct platform_device
*dev
= to_platform_device(mmc
->dev
.parent
);
74 struct t7l66xb
*t7l66xb
= platform_get_drvdata(dev
);
78 clk_enable(t7l66xb
->clk32k
);
80 spin_lock_irqsave(&t7l66xb
->lock
, flags
);
82 dev_ctl
= tmio_ioread8(t7l66xb
->scr
+ SCR_DEV_CTL
);
83 dev_ctl
|= SCR_DEV_CTL_MMC
;
84 tmio_iowrite8(dev_ctl
, t7l66xb
->scr
+ SCR_DEV_CTL
);
86 spin_unlock_irqrestore(&t7l66xb
->lock
, flags
);
88 tmio_core_mmc_enable(t7l66xb
->scr
+ 0x200,
89 t7l66xb_mmc_resources
[0].start
& 0xfffe);
94 static int t7l66xb_mmc_disable(struct platform_device
*mmc
)
96 struct platform_device
*dev
= to_platform_device(mmc
->dev
.parent
);
97 struct t7l66xb
*t7l66xb
= platform_get_drvdata(dev
);
101 spin_lock_irqsave(&t7l66xb
->lock
, flags
);
103 dev_ctl
= tmio_ioread8(t7l66xb
->scr
+ SCR_DEV_CTL
);
104 dev_ctl
&= ~SCR_DEV_CTL_MMC
;
105 tmio_iowrite8(dev_ctl
, t7l66xb
->scr
+ SCR_DEV_CTL
);
107 spin_unlock_irqrestore(&t7l66xb
->lock
, flags
);
109 clk_disable(t7l66xb
->clk32k
);
114 static void t7l66xb_mmc_pwr(struct platform_device
*mmc
, int state
)
116 struct platform_device
*dev
= to_platform_device(mmc
->dev
.parent
);
117 struct t7l66xb
*t7l66xb
= platform_get_drvdata(dev
);
119 tmio_core_mmc_pwr(t7l66xb
->scr
+ 0x200, state
);
122 static void t7l66xb_mmc_clk_div(struct platform_device
*mmc
, int state
)
124 struct platform_device
*dev
= to_platform_device(mmc
->dev
.parent
);
125 struct t7l66xb
*t7l66xb
= platform_get_drvdata(dev
);
127 tmio_core_mmc_clk_div(t7l66xb
->scr
+ 0x200, state
);
130 /*--------------------------------------------------------------------------*/
132 static struct tmio_mmc_data t7166xb_mmc_data
= {
134 .set_pwr
= t7l66xb_mmc_pwr
,
135 .set_no_clk_div
= t7l66xb_mmc_clk_div
,
138 static const struct resource t7l66xb_mmc_resources
[] = {
142 .flags
= IORESOURCE_MEM
,
145 .start
= IRQ_T7L66XB_MMC
,
146 .end
= IRQ_T7L66XB_MMC
,
147 .flags
= IORESOURCE_IRQ
,
151 static const struct resource t7l66xb_nand_resources
[] = {
155 .flags
= IORESOURCE_MEM
,
160 .flags
= IORESOURCE_MEM
,
163 .start
= IRQ_T7L66XB_NAND
,
164 .end
= IRQ_T7L66XB_NAND
,
165 .flags
= IORESOURCE_IRQ
,
169 static struct mfd_cell t7l66xb_cells
[] = {
170 [T7L66XB_CELL_MMC
] = {
172 .enable
= t7l66xb_mmc_enable
,
173 .disable
= t7l66xb_mmc_disable
,
174 .driver_data
= &t7166xb_mmc_data
,
175 .num_resources
= ARRAY_SIZE(t7l66xb_mmc_resources
),
176 .resources
= t7l66xb_mmc_resources
,
178 [T7L66XB_CELL_NAND
] = {
180 .num_resources
= ARRAY_SIZE(t7l66xb_nand_resources
),
181 .resources
= t7l66xb_nand_resources
,
185 /*--------------------------------------------------------------------------*/
187 /* Handle the T7L66XB interrupt mux */
188 static void t7l66xb_irq(unsigned int irq
, struct irq_desc
*desc
)
190 struct t7l66xb
*t7l66xb
= get_irq_data(irq
);
192 unsigned int i
, irq_base
;
194 irq_base
= t7l66xb
->irq_base
;
196 while ((isr
= tmio_ioread8(t7l66xb
->scr
+ SCR_ISR
) &
197 ~tmio_ioread8(t7l66xb
->scr
+ SCR_IMR
)))
198 for (i
= 0; i
< T7L66XB_NR_IRQS
; i
++)
200 generic_handle_irq(irq_base
+ i
);
203 static void t7l66xb_irq_mask(unsigned int irq
)
205 struct t7l66xb
*t7l66xb
= get_irq_chip_data(irq
);
209 spin_lock_irqsave(&t7l66xb
->lock
, flags
);
210 imr
= tmio_ioread8(t7l66xb
->scr
+ SCR_IMR
);
211 imr
|= 1 << (irq
- t7l66xb
->irq_base
);
212 tmio_iowrite8(imr
, t7l66xb
->scr
+ SCR_IMR
);
213 spin_unlock_irqrestore(&t7l66xb
->lock
, flags
);
216 static void t7l66xb_irq_unmask(unsigned int irq
)
218 struct t7l66xb
*t7l66xb
= get_irq_chip_data(irq
);
222 spin_lock_irqsave(&t7l66xb
->lock
, flags
);
223 imr
= tmio_ioread8(t7l66xb
->scr
+ SCR_IMR
);
224 imr
&= ~(1 << (irq
- t7l66xb
->irq_base
));
225 tmio_iowrite8(imr
, t7l66xb
->scr
+ SCR_IMR
);
226 spin_unlock_irqrestore(&t7l66xb
->lock
, flags
);
229 static struct irq_chip t7l66xb_chip
= {
231 .ack
= t7l66xb_irq_mask
,
232 .mask
= t7l66xb_irq_mask
,
233 .unmask
= t7l66xb_irq_unmask
,
236 /*--------------------------------------------------------------------------*/
238 /* Install the IRQ handler */
239 static void t7l66xb_attach_irq(struct platform_device
*dev
)
241 struct t7l66xb
*t7l66xb
= platform_get_drvdata(dev
);
242 unsigned int irq
, irq_base
;
244 irq_base
= t7l66xb
->irq_base
;
246 for (irq
= irq_base
; irq
< irq_base
+ T7L66XB_NR_IRQS
; irq
++) {
247 set_irq_chip(irq
, &t7l66xb_chip
);
248 set_irq_chip_data(irq
, t7l66xb
);
249 set_irq_handler(irq
, handle_level_irq
);
251 set_irq_flags(irq
, IRQF_VALID
| IRQF_PROBE
);
255 set_irq_type(t7l66xb
->irq
, IRQ_TYPE_EDGE_FALLING
);
256 set_irq_data(t7l66xb
->irq
, t7l66xb
);
257 set_irq_chained_handler(t7l66xb
->irq
, t7l66xb_irq
);
260 static void t7l66xb_detach_irq(struct platform_device
*dev
)
262 struct t7l66xb
*t7l66xb
= platform_get_drvdata(dev
);
263 unsigned int irq
, irq_base
;
265 irq_base
= t7l66xb
->irq_base
;
267 set_irq_chained_handler(t7l66xb
->irq
, NULL
);
268 set_irq_data(t7l66xb
->irq
, NULL
);
270 for (irq
= irq_base
; irq
< irq_base
+ T7L66XB_NR_IRQS
; irq
++) {
272 set_irq_flags(irq
, 0);
274 set_irq_chip(irq
, NULL
);
275 set_irq_chip_data(irq
, NULL
);
279 /*--------------------------------------------------------------------------*/
282 static int t7l66xb_suspend(struct platform_device
*dev
, pm_message_t state
)
284 struct t7l66xb
*t7l66xb
= platform_get_drvdata(dev
);
285 struct t7l66xb_platform_data
*pdata
= dev
->dev
.platform_data
;
287 if (pdata
&& pdata
->suspend
)
289 clk_disable(t7l66xb
->clk48m
);
294 static int t7l66xb_resume(struct platform_device
*dev
)
296 struct t7l66xb
*t7l66xb
= platform_get_drvdata(dev
);
297 struct t7l66xb_platform_data
*pdata
= dev
->dev
.platform_data
;
299 clk_enable(t7l66xb
->clk48m
);
300 if (pdata
&& pdata
->resume
)
303 tmio_core_mmc_enable(t7l66xb
->scr
+ 0x200,
304 t7l66xb_mmc_resources
[0].start
& 0xfffe);
309 #define t7l66xb_suspend NULL
310 #define t7l66xb_resume NULL
313 /*--------------------------------------------------------------------------*/
315 static int t7l66xb_probe(struct platform_device
*dev
)
317 struct t7l66xb_platform_data
*pdata
= dev
->dev
.platform_data
;
318 struct t7l66xb
*t7l66xb
;
319 struct resource
*iomem
, *rscr
;
322 iomem
= platform_get_resource(dev
, IORESOURCE_MEM
, 0);
326 t7l66xb
= kzalloc(sizeof *t7l66xb
, GFP_KERNEL
);
330 spin_lock_init(&t7l66xb
->lock
);
332 platform_set_drvdata(dev
, t7l66xb
);
334 ret
= platform_get_irq(dev
, 0);
340 t7l66xb
->irq_base
= pdata
->irq_base
;
342 t7l66xb
->clk32k
= clk_get(&dev
->dev
, "CLK_CK32K");
343 if (IS_ERR(t7l66xb
->clk32k
)) {
344 ret
= PTR_ERR(t7l66xb
->clk32k
);
348 t7l66xb
->clk48m
= clk_get(&dev
->dev
, "CLK_CK48M");
349 if (IS_ERR(t7l66xb
->clk48m
)) {
350 ret
= PTR_ERR(t7l66xb
->clk48m
);
351 clk_put(t7l66xb
->clk32k
);
355 rscr
= &t7l66xb
->rscr
;
356 rscr
->name
= "t7l66xb-core";
357 rscr
->start
= iomem
->start
;
358 rscr
->end
= iomem
->start
+ 0xff;
359 rscr
->flags
= IORESOURCE_MEM
;
361 ret
= request_resource(iomem
, rscr
);
363 goto err_request_scr
;
365 t7l66xb
->scr
= ioremap(rscr
->start
, rscr
->end
- rscr
->start
+ 1);
371 clk_enable(t7l66xb
->clk48m
);
373 if (pdata
&& pdata
->enable
)
376 /* Mask all interrupts */
377 tmio_iowrite8(0xbf, t7l66xb
->scr
+ SCR_IMR
);
379 printk(KERN_INFO
"%s rev %d @ 0x%08lx, irq %d\n",
380 dev
->name
, tmio_ioread8(t7l66xb
->scr
+ SCR_REVID
),
381 (unsigned long)iomem
->start
, t7l66xb
->irq
);
383 t7l66xb_attach_irq(dev
);
385 t7l66xb_cells
[T7L66XB_CELL_NAND
].driver_data
= pdata
->nand_data
;
386 t7l66xb_cells
[T7L66XB_CELL_NAND
].platform_data
=
387 &t7l66xb_cells
[T7L66XB_CELL_NAND
];
388 t7l66xb_cells
[T7L66XB_CELL_NAND
].data_size
=
389 sizeof(t7l66xb_cells
[T7L66XB_CELL_NAND
]);
391 t7l66xb_cells
[T7L66XB_CELL_MMC
].platform_data
=
392 &t7l66xb_cells
[T7L66XB_CELL_MMC
];
393 t7l66xb_cells
[T7L66XB_CELL_MMC
].data_size
=
394 sizeof(t7l66xb_cells
[T7L66XB_CELL_MMC
]);
396 ret
= mfd_add_devices(&dev
->dev
, dev
->id
,
397 t7l66xb_cells
, ARRAY_SIZE(t7l66xb_cells
),
398 iomem
, t7l66xb
->irq_base
);
403 t7l66xb_detach_irq(dev
);
404 iounmap(t7l66xb
->scr
);
406 release_resource(&t7l66xb
->rscr
);
409 clk_put(t7l66xb
->clk48m
);
411 clk_put(t7l66xb
->clk32k
);
417 static int t7l66xb_remove(struct platform_device
*dev
)
419 struct t7l66xb_platform_data
*pdata
= dev
->dev
.platform_data
;
420 struct t7l66xb
*t7l66xb
= platform_get_drvdata(dev
);
423 ret
= pdata
->disable(dev
);
424 clk_disable(t7l66xb
->clk48m
);
425 clk_put(t7l66xb
->clk48m
);
426 t7l66xb_detach_irq(dev
);
427 iounmap(t7l66xb
->scr
);
428 release_resource(&t7l66xb
->rscr
);
429 mfd_remove_devices(&dev
->dev
);
430 platform_set_drvdata(dev
, NULL
);
437 static struct platform_driver t7l66xb_platform_driver
= {
440 .owner
= THIS_MODULE
,
442 .suspend
= t7l66xb_suspend
,
443 .resume
= t7l66xb_resume
,
444 .probe
= t7l66xb_probe
,
445 .remove
= t7l66xb_remove
,
448 /*--------------------------------------------------------------------------*/
450 static int __init
t7l66xb_init(void)
454 retval
= platform_driver_register(&t7l66xb_platform_driver
);
458 static void __exit
t7l66xb_exit(void)
460 platform_driver_unregister(&t7l66xb_platform_driver
);
463 module_init(t7l66xb_init
);
464 module_exit(t7l66xb_exit
);
466 MODULE_DESCRIPTION("Toshiba T7L66XB core driver");
467 MODULE_LICENSE("GPL v2");
468 MODULE_AUTHOR("Ian Molton");
469 MODULE_ALIAS("platform:t7l66xb");