1 From 2e31fea352ca97988452f1f2c94809de2977ce40 Mon Sep 17 00:00:00 2001
2 From: Ian Molton <spyro@f2s.com>
3 Date: Sat, 29 Dec 2007 15:08:52 +0000
4 Subject: [PATCH 06/64] Add support for t7l66xb MFD core
7 drivers/mfd/Kconfig | 6 +
8 drivers/mfd/Makefile | 1 +
9 drivers/mfd/t7l66xb.c | 550 +++++++++++++++++++++++++++++++++++++++++++
10 include/linux/mfd/t7l66xb.h | 45 ++++
11 4 files changed, 602 insertions(+), 0 deletions(-)
12 create mode 100644 drivers/mfd/t7l66xb.c
13 create mode 100644 include/linux/mfd/t7l66xb.h
15 diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
16 index 1575323..f79a969 100644
17 --- a/drivers/mfd/Kconfig
18 +++ b/drivers/mfd/Kconfig
19 @@ -9,6 +9,12 @@ config MFD_CORE
24 + bool "Support Toshiba T7L66XB"
27 + Support for Toshiba Mobile IO Controller T7L66XB
30 bool "Support Toshiba TC6387XB"
32 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
33 index 41b2190..b2037ae 100644
34 --- a/drivers/mfd/Makefile
35 +++ b/drivers/mfd/Makefile
36 @@ -6,6 +6,7 @@ obj-$(CONFIG_MFD_SM501) += sm501.o
38 obj-$(CONFIG_MFD_CORE) += mfd-core.o
40 +obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o
41 obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o
42 obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o
44 diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c
46 index 0000000..308776a
48 +++ b/drivers/mfd/t7l66xb.c
52 + * Toshiba T7L66XB core mfd support
54 + * Copyright (c) 2005 Ian Molton
55 + * Copyright (c) 2007 Ian Molton
57 + * This program is free software; you can redistribute it and/or modify
58 + * it under the terms of the GNU General Public License version 2 as
59 + * published by the Free Software Foundation.
63 + * Supported in this driver:
65 + * SM/NAND flash controller
68 + * As yet not supported
69 + * GPIO interface (on NAND pins)
71 + * TFT 'interface converter'
72 + * PCMCIA interface logic
75 +#include <linux/kernel.h>
76 +#include <linux/module.h>
77 +#include <linux/io.h>
78 +#include <linux/irq.h>
79 +#include <linux/platform_device.h>
80 +#include <linux/fb.h>
81 +#include <linux/mfd-core.h>
82 +#include <linux/mfd/tmio.h>
83 +#include <linux/mfd/t7l66xb.h>
85 +union t7l66xb_dev_ctl {
88 + unsigned usb_en:1; /* D0 USB enable */
89 + unsigned mmc_en:1; /* D1 MMC enable */
90 +} __attribute__ ((packed));
91 +} __attribute__ ((packed));
96 + u8 revid; /* 0x08 Revision ID */
98 + u8 imr; /* 0x42 Interrupt Mask */
100 + union t7l66xb_dev_ctl dev_ctl; /* 0xe0 Device control */
101 + u8 isr; /* 0xe1 Interrupt Status */
103 + u8 gpio_output_ctl; /* 0xf0 */
104 + u8 gpio_output_status; /* 0xf1 */
105 + u16 gpio_input_status; /* 0xf2 */
107 + u8 active_pullup_down_ctl; /* 0xf8 */
109 +} __attribute__ ((packed));
112 +/*--------------------------------------------------------------------------*/
116 + struct t7l66xb_scr __iomem *scr;
119 + struct resource rscr;
120 + struct resource *iomem;
124 +/*--------------------------------------------------------------------------*/
126 +static int t7l66xb_ohci_enable(struct platform_device *ohci)
128 + struct platform_device *dev = to_platform_device(ohci->dev.parent);
129 + struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
130 + struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
131 + unsigned long flags;
132 + union t7l66xb_dev_ctl dev_ctl;
134 + spin_lock_irqsave(&t7l66xb->lock, flags);
136 + dev_ctl.raw = readb(&scr->dev_ctl);
137 + dev_ctl.usb_en = 1;
138 + writeb(dev_ctl.raw, &scr->dev_ctl);
140 + spin_unlock_irqrestore(&t7l66xb->lock, flags);
145 +static int t7l66xb_ohci_disable(struct platform_device *ohci)
147 + struct platform_device *dev = to_platform_device(ohci->dev.parent);
148 + struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
149 + struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
150 + unsigned long flags;
151 + union t7l66xb_dev_ctl dev_ctl;
153 + spin_lock_irqsave(&t7l66xb->lock, flags);
155 + dev_ctl.raw = readb(&scr->dev_ctl);
156 + dev_ctl.usb_en = 0;
157 + writeb(dev_ctl.raw, &scr->dev_ctl);
159 + spin_unlock_irqrestore(&t7l66xb->lock, flags);
164 +/*--------------------------------------------------------------------------*/
166 +static int t7l66xb_mmc_enable(struct platform_device *ohci)
168 + struct platform_device *dev = to_platform_device(ohci->dev.parent);
169 + struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
170 + struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
171 + struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
172 + unsigned long flags;
173 + union t7l66xb_dev_ctl dev_ctl;
175 + spin_lock_irqsave(&t7l66xb->lock, flags);
177 + if(pdata->enable_clk32k)
178 + pdata->enable_clk32k(dev);
179 + dev_ctl.raw = readb(&scr->dev_ctl);
180 + dev_ctl.mmc_en = 1;
181 + writeb(dev_ctl.raw, &scr->dev_ctl);
183 + spin_unlock_irqrestore(&t7l66xb->lock, flags);
188 +static int t7l66xb_mmc_disable(struct platform_device *ohci)
190 + struct platform_device *dev = to_platform_device(ohci->dev.parent);
191 + struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
192 + struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
193 + struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
194 + unsigned long flags;
195 + union t7l66xb_dev_ctl dev_ctl;
197 + spin_lock_irqsave(&t7l66xb->lock, flags);
199 + dev_ctl.raw = readb(&scr->dev_ctl);
200 + dev_ctl.mmc_en = 0;
201 + writeb(dev_ctl.raw, &scr->dev_ctl);
202 + if(pdata->disable_clk32k)
203 + pdata->disable_clk32k(dev);
205 + spin_unlock_irqrestore(&t7l66xb->lock, flags);
210 +/*--------------------------------------------------------------------------*/
212 +static int t7l66xb_nand_disable(struct platform_device *nand)
214 + struct platform_device *dev = to_platform_device(nand->dev.parent);
215 + struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
216 + struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
217 + unsigned long flags;
218 + union t7l66xb_dev_ctl dev_ctl;
220 + spin_lock_irqsave(&t7l66xb->lock, flags);
222 + dev_ctl.raw = readb(&scr->dev_ctl);
223 +// dev_ctl.nand_en = 0;
224 + writeb(dev_ctl.raw, &scr->dev_ctl);
226 + spin_unlock_irqrestore(&t7l66xb->lock, flags);
231 +static int t7l66xb_nand_enable(struct platform_device *nand)
233 + struct platform_device *dev = to_platform_device(nand->dev.parent);
234 + struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
235 + struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
236 + unsigned long flags;
237 + union t7l66xb_dev_ctl dev_ctl;
239 + spin_lock_irqsave(&t7l66xb->lock, flags);
241 + dev_ctl.raw = readb(&scr->dev_ctl);
242 + // dev_ctl.nand_en = 1;
243 + writeb(dev_ctl.raw, &scr->dev_ctl);
245 + spin_unlock_irqrestore(&t7l66xb->lock, flags);
250 +/*--------------------------------------------------------------------------*/
252 +const static struct resource t7l66xb_mmc_resources[] = {
254 + .name = TMIO_MMC_CONTROL,
257 + .flags = IORESOURCE_MEM,
260 + .name = TMIO_MMC_CONFIG,
263 + .flags = IORESOURCE_MEM,
266 + .name = TMIO_MMC_IRQ,
267 + .start = IRQ_T7L66XB_MMC,
268 + .end = IRQ_T7L66XB_MMC,
269 + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
273 +const static struct resource t7l66xb_ohci_resources[] = {
275 + .name = TMIO_OHCI_CONFIG,
278 + .flags = IORESOURCE_MEM,
281 + .name = TMIO_OHCI_CONTROL,
284 + .flags = IORESOURCE_MEM,
287 + .name = TMIO_OHCI_SRAM,
290 + .flags = IORESOURCE_MEM,
293 + .name = TMIO_OHCI_IRQ,
294 + .start = IRQ_T7L66XB_OHCI,
295 + .end = IRQ_T7L66XB_OHCI,
296 + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
300 +const static struct resource t7l66xb_nand_resources[] = {
302 + .name = TMIO_NAND_CONFIG,
305 + .flags = IORESOURCE_MEM,
308 + .name = TMIO_NAND_CONTROL,
311 + .flags = IORESOURCE_MEM,
314 + .name = TMIO_NAND_IRQ,
315 + .start = IRQ_T7L66XB_NAND,
316 + .end = IRQ_T7L66XB_NAND,
317 + .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
321 +static struct mfd_cell t7l66xb_cells[] = {
323 + .name = "tmio-mmc",
324 + .enable = t7l66xb_mmc_enable,
325 + .disable = t7l66xb_mmc_disable,
326 + .num_resources = ARRAY_SIZE(t7l66xb_mmc_resources),
327 + .resources = t7l66xb_mmc_resources,
330 + .name = "tmio-ohci",
331 + .enable = t7l66xb_ohci_enable,
332 + .disable = t7l66xb_ohci_disable,
333 + .num_resources = ARRAY_SIZE(t7l66xb_ohci_resources),
334 + .resources = t7l66xb_ohci_resources,
337 + .name = "tmio-nand",
338 + .enable = t7l66xb_nand_enable,
339 + .disable = t7l66xb_nand_disable,
340 + .num_resources = ARRAY_SIZE(t7l66xb_nand_resources),
341 + .resources = t7l66xb_nand_resources,
345 +/*--------------------------------------------------------------------------*/
347 +/* Handle the T7L66XB interrupt mux */
348 +static void t7l66xb_irq(unsigned int irq, struct irq_desc *desc)
350 + struct platform_device *dev = get_irq_chip_data(irq);
351 + struct t7l66xb_platform_data *tcpd = dev->dev.platform_data;
352 + struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
353 + struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
357 + desc->chip->ack(irq);
358 + while ((isr = readb(&scr->isr) & ~readb(&scr->imr)))
359 + for (i = 0; i < T7L66XB_NR_IRQS; i++)
360 + if (isr & (1 << i))
361 + desc_handle_irq(tcpd->irq_base + i,
362 + irq_desc + tcpd->irq_base + i);
365 +static void t7l66xb_irq_mask(unsigned int irq)
367 + struct platform_device *dev = get_irq_chip_data(irq);
368 + struct t7l66xb_platform_data *tcpd = dev->dev.platform_data;
369 + struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
370 + struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
371 + unsigned long flags;
373 + spin_lock_irqsave(&t7l66xb->lock, flags);
374 + iowrite8(ioread8(&scr->imr) | (1 << (irq - tcpd->irq_base)),
376 + spin_unlock_irqrestore(&t7l66xb->lock, flags);
379 +static void t7l66xb_irq_unmask(unsigned int irq)
381 + struct platform_device *dev = get_irq_chip_data(irq);
382 + struct t7l66xb_platform_data *tcpd = dev->dev.platform_data;
383 + struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
384 + struct t7l66xb_scr __iomem *scr = t7l66xb->scr;
385 + unsigned long flags;
387 + spin_lock_irqsave(&t7l66xb->lock, flags);
388 + iowrite8(ioread8(&scr->imr) & ~(1 << (irq - tcpd->irq_base)),
390 + spin_unlock_irqrestore(&t7l66xb->lock, flags);
393 +static struct irq_chip t7l66xb_chip = {
395 + .ack = t7l66xb_irq_mask,
396 + .mask = t7l66xb_irq_mask,
397 + .unmask = t7l66xb_irq_unmask,
400 +/*--------------------------------------------------------------------------*/
402 +/* Install the IRQ handler */
403 +static void t7l66xb_attach_irq(struct platform_device *dev)
405 + struct t7l66xb_platform_data *tcpd = dev->dev.platform_data;
406 + struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
410 + irq = tcpd->irq_base;
411 + irq <= tcpd->irq_base + T7L66XB_NR_IRQS;
413 + set_irq_chip (irq, &t7l66xb_chip);
414 + set_irq_chip_data (irq, dev);
415 + set_irq_handler(irq, handle_level_irq);
416 + set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
419 + set_irq_type (t7l66xb->irq, IRQT_FALLING);
420 + set_irq_chip_data (t7l66xb->irq, dev);
421 + set_irq_chained_handler (t7l66xb->irq, t7l66xb_irq);
424 +static void t7l66xb_detach_irq(struct platform_device *dev)
426 + struct t7l66xb_platform_data *tcpd = dev->dev.platform_data;
427 + struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
430 + set_irq_chained_handler(t7l66xb->irq, NULL);
431 + set_irq_chip_data(t7l66xb->irq, NULL);
434 + irq = tcpd->irq_base;
435 + irq <= tcpd->irq_base + T7L66XB_NR_IRQS;
437 + set_irq_flags(irq, 0);
438 + set_irq_chip(irq, NULL);
439 + set_irq_chip_data(irq, NULL);
443 +/*--------------------------------------------------------------------------*/
446 +static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state)
448 + struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
451 + if (pdata && pdata->suspend)
452 + pdata->suspend(dev);
457 +static int t7l66xb_resume(struct platform_device *dev)
459 + struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
461 + if (pdata && pdata->resume)
462 + pdata->resume(dev);
467 +#define t7l66xb_suspend NULL
468 +#define t7l66xb_resume NULL
471 +/*--------------------------------------------------------------------------*/
473 +static int t7l66xb_probe(struct platform_device *dev)
475 + struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
476 + struct t7l66xb *t7l66xb;
477 + struct resource *iomem;
478 + struct resource *rscr;
479 + int retval = -ENOMEM;
481 + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0);
485 + t7l66xb = kzalloc (sizeof *t7l66xb, GFP_KERNEL);
489 + spin_lock_init(&t7l66xb->lock);
491 + platform_set_drvdata(dev, t7l66xb);
492 + t7l66xb->iomem = iomem;
493 + t7l66xb->irq = platform_get_irq(dev, 0);
495 + rscr = &t7l66xb->rscr;
496 + rscr->name = "t7l66xb-core";
497 + rscr->start = iomem->start;
498 + rscr->end = iomem->start + 0xff;
499 + rscr->flags = IORESOURCE_MEM;
501 + if((retval = request_resource(iomem, rscr)))
502 + goto err_request_scr;
504 + t7l66xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1);
505 + if (!t7l66xb->scr) {
510 + if (pdata && pdata->enable)
511 + pdata->enable(dev);
513 + writeb(0xbf, &t7l66xb->scr->imr); /* Mask all interrupts */
515 + printk(KERN_INFO "%s rev %d @ 0x%08lx, irq %d\n",
516 + dev->name, readb(&t7l66xb->scr->revid),
517 + (unsigned long)t7l66xb->scr, t7l66xb->irq);
520 + t7l66xb_attach_irq(dev);
522 + t7l66xb_cells[2].driver_data = pdata->nand_data;
524 + if(!(retval = mfd_add_devices(dev, t7l66xb_cells,
525 + ARRAY_SIZE(t7l66xb_cells),
526 + iomem, 0, pdata->irq_base)))
530 + t7l66xb_detach_irq(dev);
532 + iounmap(t7l66xb->scr);
534 + release_resource(rscr);
538 + release_resource(iomem);
542 +static int t7l66xb_remove(struct platform_device *dev)
544 + struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
545 + struct t7l66xb *t7l66xb = platform_get_drvdata(dev);
549 + t7l66xb_detach_irq(dev);
551 + ret = pdata->disable(dev);
553 + iounmap(t7l66xb->scr);
554 + release_resource(&t7l66xb->rscr);
555 + release_resource(t7l66xb->iomem);
557 + mfd_remove_devices(dev);
559 + platform_set_drvdata(dev, NULL);
567 +static struct platform_driver t7l66xb_platform_driver = {
570 + .owner = THIS_MODULE,
572 + .suspend = t7l66xb_suspend,
573 + .resume = t7l66xb_resume,
574 + .probe = t7l66xb_probe,
575 + .remove = t7l66xb_remove,
578 +/*--------------------------------------------------------------------------*/
580 +static int __init t7l66xb_init(void)
584 + retval = platform_driver_register (&t7l66xb_platform_driver);
588 +static void __exit t7l66xb_exit(void)
590 + platform_driver_unregister(&t7l66xb_platform_driver);
593 +module_init(t7l66xb_init);
594 +module_exit(t7l66xb_exit);
596 +MODULE_DESCRIPTION("Toshiba T7L66XB core driver");
597 +MODULE_LICENSE("GPLv2");
598 +MODULE_AUTHOR("Ian Molton");
600 diff --git a/include/linux/mfd/t7l66xb.h b/include/linux/mfd/t7l66xb.h
602 index 0000000..06b8de5
604 +++ b/include/linux/mfd/t7l66xb.h
607 + * linux/include/asm-arm/hardware/t7l66xb.h
609 + * This file contains the definitions for the T7L66XB
611 + * (C) Copyright 2005 Ian Molton <spyro@f2s.com>
613 + * This program is free software; you can redistribute it and/or modify
614 + * it under the terms of the GNU General Public License version 2 as
615 + * published by the Free Software Foundation.
618 +#ifndef _ASM_ARCH_T7L66XB_SOC
619 +#define _ASM_ARCH_T7L66XB_SOC
621 +#include <linux/mfd-core.h>
622 +#include <linux/mfd/tmio.h>
625 +struct t7l66xb_platform_data
627 + int (*enable_clk32k)(struct platform_device *dev);
628 + int (*disable_clk32k)(struct platform_device *dev);
630 + int (*enable)(struct platform_device *dev);
631 + int (*disable)(struct platform_device *dev);
632 + int (*suspend)(struct platform_device *dev);
633 + int (*resume)(struct platform_device *dev);
635 + int irq_base; /* a base for cascaded irq */
637 + struct tmio_nand_data *nand_data;
641 +#define T7L66XB_NAND_CNF_BASE (0x000100)
642 +#define T7L66XB_NAND_CTL_BASE (0x001000)
644 +#define IRQ_T7L66XB_NAND (3)
645 +#define IRQ_T7L66XB_MMC (1)
646 +#define IRQ_T7L66XB_OHCI (2)
648 +#define T7L66XB_NR_IRQS 8