0/4 Support for Toshiba TMIO multifunction devices
PATCH 1/1 0/4 Support for Toshiba TMIO multifunction devices
Hi guys.
This patchset contains support for three toshiba multifunction devices.
it introduces a small but useful bit of code as a library for similar devices, and includes support for these chips on the toshiba e-series family of handhelds.
please copy me on reply!
Hi,
Quoted 3 lines · ian
Hi guys.
This patchset contains support for three toshiba multifunction devices.
Just to note, that there is an alternative implementation for at least the tc6393 chip devices. Most current version of those patches can be found in the OpenEmbedded monotone.
Quoted 7 lines
it introduces a small but useful bit of code as a library for similar devices, and includes support for these chips on the toshiba e-series family of handhelds.
please copy me on reply!
Signature · 2 lines
With best wishes Dmitry
Quoted 3 lines · Dmitry Baryshkov
Just to note, that there is an alternative implementation for at least the tc6393 chip devices. Most current version of those patches can be found in the OpenEmbedded monotone.
Yes, this is true. The core code in both cases originates from my and Dirks work.
I'd just like to get something pushed up to mainline so that work can begin on expanding the number of chips supported.
This would include the ASICs found in many iPAQ PDAs (their MMC module seems to be an IRQ-less TMIO chip).
I havent submitted the subdevice drivers yet, only the base/core drivers.
The OE versions probably have better USB and video subdevice support (which I shall evaluate as I go on), wheras the NAND flash and MMC drivers in hh.org are probably more up to date (I run root on SD usinf the hh.org tmio_mmc driver on all three of the MFD chips in this patch series on a daily basis).
merging the best of these patch series is my goal and it shouldnt be too hard.
Roughly went through the patch, looks good, here comes the remind, though :-)
1. is it possible to use some name other than "soc_core", maybe "tmio_core" so that
other multifunction chips sharing a core base will live easier.
2. those C++ style comments "//" are not so pleasant...
- eric
Quoted 35 lines · ian
Quoted 3 lines · Dmitry Baryshkov
Just to note, that there is an alternative implementation for at least the tc6393 chip devices. Most current version of those patches can be found in the OpenEmbedded monotone.
Yes, this is true. The core code in both cases originates from my and Dirks work.
I'd just like to get something pushed up to mainline so that work can begin on expanding the number of chips supported.
This would include the ASICs found in many iPAQ PDAs (their MMC module seems to be an IRQ-less TMIO chip).
I havent submitted the subdevice drivers yet, only the base/core drivers.
The OE versions probably have better USB and video subdevice support (which I shall evaluate as I go on), wheras the NAND flash and MMC drivers in hh.org are probably more up to date (I run root on SD usinf the hh.org
tmio_mmcdriver on all three of the MFD chips in this patch series on a daily basis).merging the best of these patch series is my goal and it shouldnt be too hard.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Signature · 2 lines
Cheers - eric
Quoted 5 lines · eric miao
Roughly went through the patch, looks good, here comes the remind, though :-)
1. is it possible to use some name other than "
soc_core", maybe "tmio_core" so that other multifunction chips sharing a core base will live easier.
It's (soc-core) not tmio MFD specific - its already used by other MFD chips (although obviously not ones in mainline (yet!)
it might be better named 'mfd-core' though, as thats its intended use...
Quoted 1 line
2. those C++ style comments "//" are not so pleasant...
Should I clean them up and resubmit?
More to the point, who should I be submitting them to? the files under arm/ are obviously for RMK to peruse, but I couldnt find an entry for drivers/mfd in MAINTAINERS...
Quoted 7 lines · ian
Quoted 5 lines · eric miao
Roughly went through the patch, looks good, here comes the remind, though :-)
1. is it possible to use some name other than "
soc_core", maybe "tmio_core" so that other multifunction chips sharing a core base will live easier.
Quoted 9 lines
It's (soc-core) not tmio MFD specific - its already used by other MFD chips (although obviously not ones in mainline (yet!)
it might be better named 'mfd-core' though, as thats its intended use...
Quoted 1 line
2. those C++ style comments "//" are not so pleasant...
Should I clean them up and resubmit?
Will be nice then, anyway, could you inline them so others can comment?
Quoted 4 lines
More to the point, who should I be submitting them to? the files under arm/ are obviously for RMK to peruse, but I couldnt find an entry for
drivers/mfdinMAINTAINERS...
Well, I briefly went through the git history, looks like Russell is the proper one you could sent them to (probably not) :-)
Quoted 2 lines
Signature · 2 lines
Cheers - eric
PATCH 1/1 Support for Toshiba TMIO multifunction devices
Quoted 10 lines · eric miao
Quoted 9 lines · ian
Quoted 5 lines · eric miao
Roughly went through the patch, looks good, here comes the remind, though :-)
1. is it possible to use some name other than "
soc_core", maybe "tmio_core" so that other multifunction chips sharing a core base will live easier.It's (soc-core) not tmio MFD specific - its already used by other MFD chips (although obviously not ones in mainline (yet!)
I've renamed soc-core to mfd-core in the patches attached to this message.
Quoted 5 lines
Quoted 3 lines
Quoted 1 line
2. those C++ style comments "//" are not so pleasant...
Should I clean them up and resubmit?
Will be nice then, anyway, could you inline them so others can comment?
All done.
Quoted 2 lines
Well, I briefly went through the git history, looks like Russell is the proper one you could sent them to (probably not) :-)
I've added RMK to the CC.
I've ommitted the platform support for e-series - I'll send that to RMK once this is merged.
Patches follow:
Quoted 1 line
From
9c4ffb764ae2366368a0038a6fbdd9a19ce430c4Mon Sep 17 00:00:00 2001
From: Ian Molton <[email protected]>
Date: Wed, 21 Nov 2007 23:32:37 +0000
Subject: [PATCH] Reuseable MFD core code suitable for multifunction chips with
built in IRQ multiplexing and local RAM.
--- drivers/mfd/Kconfig | 25 ++++++++++++ drivers/mfd/Makefile | 3 + drivers/mfd/mfd-core.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/mfd-core.h | 26 ++++++++++++ include/linux/ioport.h | 3 + 5 files changed, 159 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/mfd-core.c create mode 100644 drivers/mfd/mfd-core.h
drivers/mfd/Kconfig +25 -0
@@ -15,6 +15,31 @@ config MFD_SM501 interface. The device may be connected by PCI or local bus with varying functions enabled. +config MFD_T7L66XB + bool "Toshiba T7L66XB SoC support" + ---help--- + This driver supports the T7L66XB, which incorporates SD/MMC, and + USB host functionality. associated subdevices are: + tmio_mmc + tmio_ohci + +config MFD_TC6387XB + bool "Toshiba TC6387XB SoC support" + ---help--- + This driver supports the TC6393XB, which incorporates SD/MMC, NAND, + Video, and USB host functionality. associated subdevices are: + tmio_mmc + +config MFD_TC6393XB + bool "Toshiba TC6393XB SoC support" + ---help--- + This driver supports the TC6393XB, which incorporates SD/MMC, NAND, + Video, and USB host functionality. associated subdevices are: + tmio_mmc + tmio_nand + tmio_fb + tmio_ohci + endmenu menu "Multimedia Capabilities Port drivers"
drivers/mfd/Makefile +3 -0
@@ -3,6 +3,9 @@ # obj-$(CONFIG_MFD_SM501) += sm501.o +obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o mfd-core.o +obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o mfd-core.o +obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o mfd-core.o obj-$(CONFIG_MCP) += mcp-core.o obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
drivers/mfd/mfd-core.c new file +33 -0
@@ -0,0 +1,102 @@ +/* +* drivers/mfd/mfd-core.c +* +* core MFD support +* Copyright (c) 2006 Ian Molton +* +* 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. +* +*/ + +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include "mfd-core.h" + +void mfd_free_devices (struct platform_device *devices,int nr_devs) +{ +struct platform_device *dev = devices; +int i; + +for (i =0 ; i < nr_devs; i++) { +struct resource *res = dev->resource; +platform_device_unregister (dev++); +kfree (res); + } +kfree (devices); +} +EXPORT_SYMBOL_GPL (mfd_free_devices); + +#define SIGNED_SHIFT (val, shift) ((shift) >= 0 ? ((val) << (shift)) :
((val) >> -(shift)))
+
+struct platform_device *mfd_add_devices(struct platform_device *dev,
+ struct mfd_device_data *mfd, int nr_devs,
+ struct resource *mem,
+ int relative_addr_shift, int irq_base)
+{
+ struct platform_device *devices;
+ int i, r, base;
+
+ devices = kzalloc(nr_devs * sizeof(struct platform_device),GFP_KERNEL);
+ if (!devices)
+ return NULL;
+
+ for (i = 0; i < nr_devs; i++) {
+ struct platform_device *sdev = &devices[i];
+ struct mfd_device_data *blk = &mfd[i];
+ struct resource *res;
+
+ sdev->id = -1;
+ sdev->name = blk->name;
+
+ sdev->dev.parent = &dev->dev;
+ sdev->dev.platform_data = (void *)blk->hwconfig;
+ sdev->num_resources = blk->num_resources;
+
+ /* Allocate space for the subdevice resources */
+ res = kzalloc(blk->num_resources * sizeof (struct resource),GFP_KERNEL);
+ if (!res)
+ goto fail;
+
+ for (r = 0; r < blk->num_resources; r++) {
+ res[r].name = blk->res[r].name; // Fixme - should copy
+
+ /* Find out base to use */
+ base = 0;
+ if (blk->res[r].flags & IORESOURCE_MEM) {
+ base = mem->start;
+ } else if ((blk->res[r].flags & IORESOURCE_IRQ) &&
+ (blk->res[r].flags & IORESOURCE_IRQ_MFD_SUBDEVICE)) {
+ base = irq_base;
+ }
+
+ /* Adjust resource */
+ if (blk->res[r].flags & IORESOURCE_MEM) {
+ res[r].parent = mem;
+ res[r].start = base + SIGNED_SHIFT(blk->res[r].start,relative_addr_shift);
+ res[r].end = base + SIGNED_SHIFT(blk->res[r].end,relative_addr_shift);
+ } else {
+ res[r].start = base + blk->res[r].start;
+ res[r].end = base + blk->res[r].end;
+ }
+ res[r].flags = blk->res[r].flags;
+ }
+
+ sdev->resource = res;
+ if (platform_device_register(sdev)) {
+ kfree(res);
+ goto fail;
+ }
+
+ printk(KERN_INFO "MFD: registering %s\n", blk->name);
+ }
+ return devices;
+
+fail:
+ mfd_free_devices(devices, i + 1);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(mfd_add_devices);
drivers/mfd/mfd-core.h new file +26 -0
@@ -0,0 +1,26 @@ +/* +* drivers/mfd/mfd-core.h +* +* core MFD support +* Copyright (c) 2006 Ian Molton +* +* 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. +* +*/ + +struct mfd_device_data { +char *name; +struct resource *res; +int num_resources; +void *hwconfig;/* platform_data to pass to the subdevice */ +}; + +struct platform_device *mfd_add_devices (struct platform_device *dev, +struct mfd_device_data *mfd,int n_devs, +struct resource *mem, +int relative_addr_shift,int irq_base); + +void mfd_free_devices (struct platform_device *devices,int nr_devs); +
include/linux/ioport.h +3 -0
@@ -57,6 +57,9 @@ struct resource_list {#define IORESOURCE_IRQ_LOWLEVEL (1<<3)#define IORESOURCE_IRQ_SHAREABLE (1<<4) +/* MFD device IRQ specific bits (IORESOURCE_BITS) */ +#define IORESOURCE_IRQ_MFD_SUBDEVICE (1<<5) +/* ISA PnP DMA specific bits (IORESOURCE_BITS) */ #define IORESOURCE_DMA_TYPE_MASK (3<<0)#define IORESOURCE_DMA_8BIT (0<<0)
Signature · 1 line
1.5.3.5.737.gdee1b
Quoted 1 line
From
53de37ad4c365b801ed2a5248272bed82958b908Mon Sep 17 00:00:00 2001
From: Ian Molton <[email protected]>
Date: Thu, 22 Nov 2007 00:17:41 +0000
Subject: [PATCH] Preliminary support for Toshibas TMIO based multifunction chips.
Includes support for:
* t7l66xb
* tc6387xb
* tc6393xb
--- drivers/mfd/t7l66xb.c | 291 +++++++++++++++++++++++++++++++++++ drivers/mfd/tc6387xb.c | 143 +++++++++++++++++ drivers/mfd/tc6393xb.c | 374 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/t7l66xb.h | 56 +++++++ include/linux/tc6387xb.h | 34 ++++ include/linux/tc6393.h | 136 ++++++++++++++++ include/linux/tmio_mmc.h | 26 +++ include/linux/tmio_nand.h | 15 ++ include/linux/tmio_ohci.h | 15 ++ 9 files changed, 1090 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/t7l66xb.c create mode 100644 drivers/mfd/tc6387xb.c create mode 100644 drivers/mfd/tc6393xb.c create mode 100644 include/linux/t7l66xb.h create mode 100644 include/linux/tc6387xb.h create mode 100644 include/linux/tc6393.h create mode 100644 include/linux/tmio_mmc.h create mode 100644 include/linux/tmio_nand.h create mode 100644 include/linux/tmio_ohci.h
drivers/mfd/t7l66xb.c new file +78 -0
78 lines not on this page · raw message
*desc)
+{
+ int req, i;
+ struct t7l66xb_data *data = get_irq_data(irq);
+
+ /* Acknowledge the parent IRQ */
+ desc->chip->ack(irq);
+
+ while ( (req = (readb(data->mapbase + T7L66XB_SYS_ISR)
+ & ~(readb(data->mapbase + T7L66XB_SYS_IMR)))) ) {
+ for (i = 0; i <= 7; i++) {
+ int dev_irq = data->irq_base + i;
+ struct irq_desc *d = NULL;
+ if ((req & (1<<i))) {
+ d = irq_desc + dev_irq;
+ d->handle_irq(dev_irq, d);
+ }
+ }
+ }
+}
+
+
+static void t7l66xb_mask_irq(unsigned int irq)
+{
+ struct t7l66xb_data *data = get_irq_chip_data(irq);
+
+ writeb(readb(data->mapbase + T7L66XB_SYS_IMR) | 1 << (irq -data->irq_base), data->mapbase + T7L66XB_SYS_IMR);
+}
+
+static void t7l66xb_unmask_irq(unsigned int irq)
+{
+ struct t7l66xb_data *data = get_irq_chip_data(irq);
+
+ writeb(readb(data->mapbase + T7L66XB_SYS_IMR) & ~( 1 << (irq -
data->irq_base)),data->mapbase + T7L66XB_SYS_IMR);
+}
+
+static struct irq_chip t7l66xb_chip = {
+ .name = "t7l66xb",
+ .ack = t7l66xb_mask_irq,
+ .mask = t7l66xb_mask_irq,
+ .unmask = t7l66xb_unmask_irq,
+};
+
+/* Install the IRQ handler */
+static void t7l66xb_setup_irq(struct t7l66xb_data *tchip) +{
+ int i;
+
+ for (i = 0; i < T7L66XB_NR_IRQS; i++) {
+ int irq = tchip->irq_base + i;
+ set_irq_chip (irq, &t7l66xb_chip);
+ set_irq_chip_data (irq, tchip);
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+
+ set_irq_data (tchip->irq_nr, tchip);
+ set_irq_chained_handler (tchip->irq_nr, t7l66xb_irq_handler);
+ set_irq_type (tchip->irq_nr, IRQT_FALLING);
+}
+
+static void t7l66xb_hwinit(struct platform_device *dev) +{
+ struct t7l66xb_platform_data *pdata = platform_get_platdata(dev);
+
+ if (pdata && pdata->hw_init)
+ pdata->hw_init();
+}
+
+
+#ifdef CONFIG_PM
+
+static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct t7l66xb_platform_data *pdata = platform_get_platdata(dev);
+
+
+ if (pdata && pdata->suspend)
+ pdata->suspend();
+
+ return 0;
+}
+
+static int t7l66xb_resume(struct platform_device *dev)
+{
+ struct t7l66xb_platform_data *pdata = platform_get_platdata(dev);
+
+ if (pdata && pdata->resume)
+ pdata->resume();
+
+ t7l66xb_hwinit(dev);
+
+ return 0;
+}
+#endif
+
+static int t7l66xb_probe(struct platform_device *dev)
+{
+ struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
+ unsigned long pbase = (unsigned long)dev->resource[0].start;
+ unsigned long plen = dev->resource[0].end - dev->resource[0].start;
+ int err = -ENOMEM;
+ struct t7l66xb_data *data;
+
+ data = kmalloc (sizeof (struct t7l66xb_data), GFP_KERNEL);
+ if (!data)
+ goto out;
+
+ data->irq_base = pdata->irq_base;
+ data->irq_nr = dev->resource[1].start;
+
+ if (!data->irq_base) {
+ printk("t7166xb: uninitialized irq_base!\n");
+ goto out_free_data;
+ }
+
+ data->mapbase = ioremap(pbase, plen);
+ if(!data->mapbase)
+ goto out_free_irqs;
+
+ platform_set_drvdata(dev, data);
+ t7l66xb_setup_irq(data);
+ t7l66xb_hwinit(dev);
+
+ /* Mask IRQs -- should we mask/unmask on suspend/resume? */
+ writew(0xbf, data->mapbase + T7L66XB_SYS_IMR);
+
+ printk(KERN_INFO "%s rev %d @ 0x%08lx using irq %d-%d on irq %d\n",
+ dev->name, readw(data->mapbase + T7L66XB_SYS_RIDR), + (unsigned long)data->mapbase, data->irq_base, + data->irq_base + T7L66XB_NR_IRQS - 1, data->irq_nr);
+
+ data->devices = mfd_add_devices(dev, t7l66xb_devices,
+ ARRAY_SIZE(t7l66xb_devices),
+ &dev->resource[0], 0, data->irq_base);
+
+ if(!data->devices){
+ printk(KERN_INFO "%s: Failed to allocate devices!\n",
+ dev->name);
+ goto out_free_devices;
+ }
+
+ return 0;
+
+out_free_devices:
+ mfd_free_devices(data->devices, ARRAY_SIZE(t7l66xb_devices));
+out_free_irqs:
+out_free_data:
+ kfree(data);
+out:
+ return err;
+}
+
+static int t7l66xb_remove(struct platform_device *dev) +{
+ struct t7l66xb_data *tchip = platform_get_drvdata(dev);
+ int i;
+
+ /* Free the subdevice resources */
+ for (i = 0; i < tchip->ndevices; i++) {
+ platform_device_unregister (&tchip->devices[i]);
+ kfree (tchip->devices[i].resource);
+ }
+
+ /* Take down IRQ handling */
+ for (i = 0; i < T7L66XB_NR_IRQS; i++) {
+ int irq = i + tchip->irq_base; + set_irq_handler (irq, NULL); + set_irq_chip (irq, NULL); + set_irq_chip_data (irq, NULL);
+ }
+ set_irq_chained_handler (tchip->irq_nr, NULL);
+
+ /* Free core resources */
+ iounmap (tchip->mapbase);
+ mfd_free_devices(tchip->devices, ARRAY_SIZE(t7l66xb_devices));
+ kfree (tchip);
+
+ return 0;
+}
+
+
+static struct platform_driver t7l66xb_platform_driver = {
+ .driver = {
+ .name = "t7l66xb",
+ },
+ .probe = t7l66xb_probe,
+ .remove = t7l66xb_remove,
+#ifdef CONFIG_PM
+ .suspend = t7l66xb_suspend,
+ .resume = t7l66xb_resume,
+#endif
+};
+
+
+static int __init t7l66xb_init(void)
+{
+ int retval = 0;
+
+ retval = platform_driver_register (&t7l66xb_platform_driver);
+ return retval;
+}
+
+static void __exit t7l66xb_exit(void)
+{
+ platform_driver_unregister(&t7l66xb_platform_driver);
+}
+
+module_init(t7l66xb_init); +module_exit(t7l66xb_exit);
+
+MODULE_DESCRIPTION("Toshiba T7L66XB core driver"); +MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ian Molton and Dirk Opfer");
drivers/mfd/tc6387xb.c new file +46 -0
46 lines not on this page · raw message
state)
+{
+ struct tc6387xb_data *data = platform_get_drvdata(dev);
+
+ if (data && data->platform && data->platform->suspend)
+ data->platform->suspend();
+
+ return 0;
+}
+
+static int tc6387xb_resume(struct platform_device *dev) +{
+ struct tc6387xb_data *data = platform_get_drvdata(dev);
+
+ if (data && data->platform && data->platform->resume)
+ data->platform->resume();
+
+ return 0;
+}
+#endif
+
+static struct resource tc6387xb_mmc_resources[] = {
+ {
+ .name = "control",
+ .start = TC6387XB_MMC_CTL_BASE,
+ .end = TC6387XB_MMC_CTL_BASE + 0x1ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "config",
+ .start = TC6387XB_MMC_CNF_BASE,
+ .end = TC6387XB_MMC_CNF_BASE + 0xff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = TC6387XB_MMC_IRQ,
+ .end = TC6387XB_MMC_IRQ,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,+ },
+};
+
+static struct mfd_device_data tc6387xb_devices[] = {
+ {
+ .name = "tmio_mmc", + .res = tc6387xb_mmc_resources, + .num_resources = ARRAY_SIZE(tc6387xb_mmc_resources),
+ },
+};
+
+
+static int tc6387xb_probe(struct platform_device *pdev) +{
+ struct tc6387xb_data *data;
+
+ data = kmalloc(sizeof(struct tc6387xb_data), GFP_KERNEL);
+ if(!data)
+ return -ENOMEM;
+
+ data->irq = pdev->resource[1].start;
+ data->platform = pdev->dev.platform_data;
+ platform_set_drvdata(pdev, data);
+
+ tc6387xb_hwinit(pdev);
+
+ mfd_add_devices(pdev, tc6387xb_devices, ARRAY_SIZE(tc6387xb_devices),
&pdev->resource[0], 0, data->irq);
+
+ /* Init finished. */
+ return 0;
+}
+
+static struct platform_driver tc6387xb_platform_driver = {
+ .driver = {
+ .name = "tc6387xb",
+ },
+ .probe = tc6387xb_probe,
+#ifdef CONFIG_PM
+ .suspend = tc6387xb_suspend,
+ .resume = tc6387xb_resume,
+#endif
+};
+
+
+static int __init tc6387xb_init(void)
+{
+ return platform_driver_register (&tc6387xb_platform_driver);
+}
+
+static void __exit tc6387xb_exit(void)
+{
+ platform_driver_unregister(&tc6387xb_platform_driver);
+}
+
+module_init(tc6387xb_init); +module_exit(tc6387xb_exit);
+
+MODULE_DESCRIPTION("Toshiba TC6387XB core driver"); +MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ian Molton");
drivers/mfd/tc6393xb.c new file +70 -0
70 lines not on this page · raw message
state) {
+ struct tc6393xb_data *chip = platform_get_drvdata(sdev);
+ unsigned char tmp;
+
+ if(state == MMC_CLOCK_ENABLED){
+ tmp = readw(chip->mapbase + TC6393_SYS_GPIBCR1);
+ writew(tmp | CK32KEN, chip->mapbase + TC6393_SYS_GPIBCR1);
+ }
+}
+
+static struct tmio_mmc_hwconfig tc6393xb_mmc_hwconfig = {
+ .set_mmc_clock = tc6393xb_mmc_set_clock,
+};
+
+static struct resource tc6393xb_mmc_resources[] = {
+ {
+ .name = "control",
+ .start = TC6393XB_MMC_CTL_BASE,
+ .end = TC6393XB_MMC_CTL_BASE + 0x1ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "config",
+ .start = TC6393XB_MMC_CNF_BASE,
+ .end = TC6393XB_MMC_CNF_BASE + 0xff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = TC6393XB_MMC_IRQ,
+ .end = TC6393XB_MMC_IRQ,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
+ },
+};
+
+static struct resource tc6393xb_nand_resources[] = {
+ {
+ .name = "control",
+ .start = TC6393XB_NAND_CTL_BASE,
+ .end = TC6393XB_NAND_CTL_BASE + 0x1ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "config",
+ .start = TC6393XB_NAND_CNF_BASE,
+ .end = TC6393XB_NAND_CNF_BASE + 0xff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct mfd_device_data tc6393xb_devices[] = {
+ {
+ .name = "tmio_mmc",
+ .res = tc6393xb_mmc_resources,
+ .num_resources = ARRAY_SIZE(tc6393xb_mmc_resources),
+ .hwconfig = &tc6393xb_mmc_hwconfig,
+ },
+ {
+ .name = "tmio-nand",
+ .res = tc6393xb_nand_resources,
+ .num_resources = ARRAY_SIZE(tc6393xb_nand_resources),
+ .hwconfig = &tc6393xb_nand_hwconfig,
+ },
+};
+
+/** TC6393 interrupt handling stuff.
+ * NOTE: TC6393 has a 1 to many mapping on all of its IRQs.
+ * that is, there is only one real hardware interrupt
+ * we determine which interrupt it is by reading some IO memory.
+ * We have two levels of expansion, first in the handler for the
+ * hardware interrupt we generate an interrupt
+ * IRQ_TC6393_*_BASE and those handlers generate more interrupts
+ *
+ */
+static void tc6393xb_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ int req, i;
+ struct tc6393xb_data *data = get_irq_data(irq);
+
+ /* Acknowledge the parent IRQ */
+ desc->chip->ack(irq);
+
+ while ( (req = (readb(data->mapbase + TC6393_SYS_ISR)
+ & ~(readb(data->mapbase + TC6393_SYS_IMR)))) ) {
+ for (i = 0; i <= 7; i++) {
+ int dev_irq = data->irq_base + i;
+ struct irq_desc *d = NULL;
+ if ((req & (1<<i))) {
+ if(i != 1) printk("IRQ! from %d\n", i);
+ d = irq_desc + dev_irq;
+ d->handle_irq(dev_irq, d);
+ }
+ }
+ }
+}
+
+static void tc6393xb_mask_irq(unsigned int irq)
+{
+ struct tc6393xb_data *tc6393 = get_irq_chip_data(irq);
+
+ writeb(readb(tc6393->mapbase + TC6393_SYS_IMR) | 1 << (irq -
tc6393->irq_base),tc6393->mapbase + TC6393_SYS_IMR);
+}
+
+static void tc6393xb_unmask_irq(unsigned int irq)
+{
+ struct tc6393xb_data *tc6393 = get_irq_chip_data(irq);
+
+ writeb(readb(tc6393->mapbase + TC6393_SYS_IMR) & ~( 1 << (irq -
tc6393->irq_base)),tc6393->mapbase + TC6393_SYS_IMR);
+}
+
+static struct irq_chip tc6393xb_chip = {
+ .ack = tc6393xb_mask_irq,
+ .mask = tc6393xb_mask_irq,
+ .unmask = tc6393xb_unmask_irq,
+};
+
+
+static void tc6393xb_setup_irq(struct tc6393xb_data *tchip) +{
+ int i;
+
+ for (i = 0; i < TC6393XB_NR_IRQS; i++) {
+ int irq = tchip->irq_base + i;
+ set_irq_chip (irq, &tc6393xb_chip);
+ set_irq_chip_data (irq, tchip);
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+
+ set_irq_data (tchip->irq_nr, tchip);
+ set_irq_chained_handler (tchip->irq_nr, tc6393xb_irq_handler);
+ set_irq_type (tchip->irq_nr, IRQT_FALLING);
+}
+
+void tc6393xb_hwinit(struct platform_device *dev)
+{
+ struct tc6393xb_platform_data *pdata = platform_get_platdata(dev);
+ struct tc6393xb_data *tchip = platform_get_drvdata(dev);
+
+ if(!pdata || !tchip){
+ BUG_ON("no driver data!\n");
+ return;
+ }
+
+ if (pdata->hw_init)
+ pdata->hw_init();
+
+ writew(0, tchip->mapbase + TC6393_SYS_FER);
+
+ /* Clock setting */
+ writew(pdata->sys_pll2cr, tchip->mapbase + TC6393_SYS_PLL2CR); + writew(pdata->sys_ccr, tchip->mapbase + TC6393_SYS_CCR); + writew(pdata->sys_mcr, tchip->mapbase + TC6393_SYS_MCR);
+
+ /* GPIO */
+ writew(pdata->sys_gper, tchip->mapbase + TC6393_SYS_GPER);
+ writew(pdata->sys_gpodsr1, tchip->mapbase + TC6393_SYS_GPODSR1);
+ writew(pdata->sys_gpooecr1, tchip->mapbase + TC6393_SYS_GPOOECR1);
+}
+
+
+#ifdef CONFIG_PM
+
+static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct tc6393xb_platform_data *pdata = platform_get_platdata(dev);
+
+ if (pdata && pdata->suspend)
+ pdata->suspend();
+
+ return 0;
+}
+
+static int tc6393xb_resume(struct platform_device *dev)
+{
+ struct tc6393xb_platform_data *pdata = platform_get_platdata(dev);
+
+ if (pdata && pdata->resume)
+ pdata->resume();
+
+ tc6393xb_hwinit(dev);
+
+ return 0;
+}
+
+#endif
+
+static int tc6393xb_probe(struct platform_device *dev)
+{
+ struct tc6393xb_platform_data *pdata = dev->dev.platform_data;
+ unsigned long pbase = (unsigned long)dev->resource[0].start;
+ unsigned long plen = dev->resource[0].end - dev->resource[0].start;
+ int err = -ENOMEM;
+ struct tc6393xb_data *data;
+
+ data = kmalloc (sizeof (struct tc6393xb_data), GFP_KERNEL);
+ if (!data)
+ goto out;
+
+ data->irq_base = pdata->irq_base;
+ data->irq_nr = dev->resource[1].start;
+
+ if (!data->irq_base) {
+ printk("tc6393xb: uninitialized irq_base!\n");
+ goto out_free_data;
+ }
+
+ data->mapbase = ioremap(pbase, plen);
+ if(!data->mapbase)
+ goto out_free_irqs;
+
+ platform_set_drvdata(dev, data);
+ tc6393xb_setup_irq (data);
+ tc6393xb_hwinit(dev);
+
+ /* Enable (but mask!) our IRQs */
+ writew(0, data->mapbase + TC6393_SYS_IRR);
+ writew(0xbf, data->mapbase + TC6393_SYS_IMR);
+
+ printk(KERN_INFO "%s rev %d @ 0x%08lx using irq %d-%d on irq %d\n",
+ dev->name, readw(data->mapbase + TC6393_SYS_RIDR), + (unsigned long)data->mapbase, data->irq_base, + data->irq_base + TC6393XB_NR_IRQS - 1, data->irq_nr);
+
+ data->devices = mfd_add_devices(dev, tc6393xb_devices,
+ ARRAY_SIZE(tc6393xb_devices),
+ &dev->resource[0], 0, data->irq_base);
+
+ if(!data->devices) {
+ printk(KERN_INFO "%s: Failed to allocate devices!\n",
+ dev->name);
+ goto out_free_devices;
+ }
+
+ return 0;
+
+out_free_devices:
+ mfd_free_devices(data->devices, ARRAY_SIZE(tc6393xb_devices));
+out_free_irqs:
+out_free_data:
+ kfree(data);
+out:
+ return err;
+}
+
+static int tc6393xb_remove(struct platform_device *dev) +{
+ struct tc6393xb_data *tchip = platform_get_drvdata(dev);
+ int i;
+
+ /* Free the subdevice resources */
+ for (i = 0; i < tchip->ndevices; i++) {
+ platform_device_unregister (&tchip->devices[i]);
+ kfree (tchip->devices[i].resource);
+ }
+
+ /* Take down IRQ handling */
+ for (i = 0; i < TC6393XB_NR_IRQS; i++) {
+ int irq = i + tchip->irq_base;
+ set_irq_handler (irq, NULL);
+ set_irq_chip (irq, NULL);
+ set_irq_chip_data (irq, NULL);
+ }
+
+ set_irq_chained_handler (tchip->irq_nr, NULL);
+
+ /* Free core resources */
+ iounmap (tchip->mapbase); + mfd_free_devices(tchip->devices, ARRAY_SIZE(tc6393xb_devices)); + kfree (tchip);
+
+ return 0;
+}
+
+
+static struct platform_driver tc6393xb_device_driver = {
+ .driver = {
+ .name = "tc6393xb",
+ },
+ .probe = tc6393xb_probe,
+ .remove = tc6393xb_remove,
+#ifdef CONFIG_PM
+ .suspend = tc6393xb_suspend,
+ .resume = tc6393xb_resume,
+#endif
+};
+
+
+static int __init tc6393xb_init(void)
+{
+ int retval = 0;
+ retval = platform_driver_register (&tc6393xb_device_driver);
+ return retval;
+}
+
+static void __exit tc6393xb_exit(void)
+{
+ platform_driver_unregister(&tc6393xb_device_driver);
+}
+
+module_init(tc6393xb_init); +module_exit(tc6393xb_exit);
+
+MODULE_DESCRIPTION("Toshiba TC6393 core driver"); +MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dirk Opfer and Ian Molton");
include/linux/t7l66xb.h new file +56 -0
56 lines not on this page · raw message
include/linux/tc6387xb.h new file +34 -0
@@ -0,0 +1,34 @@ +/* +* +* (C) Copyright 2005 Ian Molton <[email protected]> +* +* May be copied or modified under the terms of the GNU General Public +* License. See linux/COPYING for more information. +* +*/ + +#include <linux/platform_device.h> + +/* FIXME - this needs to be a common struct to all TMIO based MFDs. */ +struct tmio_hwconfig { +void (*hwinit)(struct platform_device *sdev); +void (*suspend)(struct platform_device *sdev); +void (*resume)(struct platform_device *sdev); +}; + +struct tc6387xb_platform_data +{ +/* Standard MFD properties */ +int irq_base; +struct platform_device **child_devs; +int num_child_devs; + +void (* hw_init) (void ); +void (* suspend) (void ); +void (* resume) (void ); +}; + +#define TC6387XB_MMC_CNF_BASE (0x000200) +#define TC6387XB_MMC_CTL_BASE (0x000800) +#define TC6387XB_MMC_IRQ (0) +
include/linux/tc6393.h new file +94 -0
94 lines not on this page · raw message
Contorol 1
+#define TC6393_SYS_GPIARCR2 0x082 // GP Internal Active Register
Contorol 2
+#define TC6393_SYS_GPIARLCR1 0x084 // GP Internal Active Register
Level Contorol 1
+#define TC6393_SYS_GPIARLCR2 0x086 // GP Internal Active Register
Level Contorol 2
+
+#define TC6393_SYS_GPIBCR1 0x089 // GPa Internal Activ Register
Contorol 1
+
+#define TC6393_SYS_GPIBCR2 0x08A // GPa Internal Activ Register
Contorol 2
+#define TC6393_SYS_GPaIARCR 0x08C +#define TC6393_SYS_GPaIARLCR 0x090 +#define TC6393_SYS_GPaIBCR 0x094 +#define TC6393_SYS_CCR 0x098 /* Clock Control Register */ +#define TC6393_SYS_PLL2CR 0x09A // PLL2 Control +#define TC6393_SYS_PLL1CR1 0x09C // PLL1 Control 1 +#define TC6393_SYS_PLL1CR2 0x09E // PLL1 Control 2 +#define TC6393_SYS_DCR 0x0A0 +#define TC6393_SYS_FER 0x0E0 /* Function Enable Register */ +#define TC6393_SYS_MCR 0x0E4 +#define TC6393_SYS_ConfigCR 0x0FC + +/* GPIO bit */ +#define TC6393_GPIO19 ( 1 << 19 ) +#define TC6393_GPIO18 ( 1 << 18 ) +#define TC6393_GPIO17 ( 1 << 17 ) +#define TC6393_GPIO16 ( 1 << 16 ) +#define TC6393_GPIO15 ( 1 << 15 ) +#define TC6393_GPIO14 ( 1 << 14 ) +#define TC6393_GPIO13 ( 1 << 13 ) +#define TC6393_GPIO12 ( 1 << 12 ) +#define TC6393_GPIO11 ( 1 << 11 ) +#define TC6393_GPIO10 ( 1 << 10 ) +#define TC6393_GPIO9 ( 1 << 9 ) +#define TC6393_GPIO8 ( 1 << 8 ) +#define TC6393_GPIO7 ( 1 << 7 ) +#define TC6393_GPIO6 ( 1 << 6 ) +#define TC6393_GPIO5 ( 1 << 5 ) +#define TC6393_GPIO4 ( 1 << 4 ) +#define TC6393_GPIO3 ( 1 << 3 ) +#define TC6393_GPIO2 ( 1 << 2 ) +#define TC6393_GPIO1 ( 1 << 1 ) +#define TC6393_GPIO0 ( 1 << 0 )
+
+#define TC6393XB_NR_IRQS 8
include/linux/tmio_mmc.h new file +26 -0
@@ -0,0 +1,26 @@ +/* +* Copyright (c) 2005 Ian Molton +* +* 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. +* +*/ + +#include <linux/platform_device.h> + +#define MMC_CLOCK_DISABLED 0 +#define MMC_CLOCK_ENABLED 1 + +#define TMIO_WP_ALWAYS_RW ((void*)-1) + +struct tmio_mmc_hwconfig { +void (*hwinit)(struct platform_device *sdev); +void (*set_mmc_clock)(struct platform_device *sdev,int state); + +/* NULL - use ASIC3 signal, +TMIO_WP_ALWAYS_RW - assume always R/W (e.g. miniSD) +otherwise - machine-specific handler */ +int (*mmc_get_ro)(struct platform_device *pdev); +short address_shift; +};
include/linux/tmio_nand.h new file +15 -0
@@ -0,0 +1,15 @@ +/* +* Copyright (c) 2005 Ian Molton +* +* 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. +* +*/ + +struct tmio_nand_hwconfig { +void (*hwinit)(struct platform_device *sdev); +void (*suspend)(struct platform_device *sdev); +void (*resume)(struct platform_device *sdev); +}; +
include/linux/tmio_ohci.h new file +15 -0
@@ -0,0 +1,15 @@ +/* +* Copyright (c) 2005 Ian Molton +* +* 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. +* +*/ + +struct tmio_ohci_hwconfig { +void (*hwinit)(struct platform_device *sdev); +void (*suspend)(struct platform_device *sdev); +void (*resume)(struct platform_device *sdev); +}; +
Signature · 1 line
1.5.3.5.737.gdee1b
Re: [UPDATED PATCH] Support for Toshiba TMIO multifunction devices
drivers/mfd/mfd-core.c line 31
Quoted 13 lines · ian
drivers/mfd/mfd-core.c new file +13
-0+void mfd_free_devices (struct platform_device *devices,int nr_devs) +{ +struct platform_device *dev = devices; +int i; + +for (i =0 ; i < nr_devs; i++) { +struct resource *res = dev->resource; +platform_device_unregister (dev++); +kfree (res); + } +kfree (devices); +} +EXPORT_SYMBOL_GPL (mfd_free_devices);
Unfortunately, this is broken as designed (in fact this whole file is.) I'm not sure why people just don't get it. sysfs. devices. device tree. It has object lifetime rules. You can _not_ go around unregistering things and then immediately freeing them - something else might _still_ be using stuff even after the call to unregister returns.
It's a potential OOPS just waiting to happen.
That's why we have a proper management API for platform devices. Please use it, I didn't add the code for just for fun.
See platform_device_alloc() + platform_device_add_resources() + platform_device_add_data() + platform_device_add() to create, and platform_device_unregister() to destroy.
(Not looked at the rest because you really really need to get this right first.)
Re: [UPDATED PATCH] Support for Toshiba TMIO multifunction devices
Quoted 3 lines · Russell King - ARM Linux
On Thu, Nov 22, 2007 at 12:34:09AM +0000, ian wrote:
Unfortunately, this is broken as designed (in fact this whole file is.)
Fix attached below.
Quoted 2 lines
(Not looked at the rest because you really really need to get this right first.)
Since this core handles all the resource management, this fix should sort that for all three drivers.
Here goes then - pass three...
Quoted 1 line
From
ac98fb6ac3d4c44b81347228d35bb5714f968e2eMon Sep 17 00:00:00 2001
From: Ian Molton <[email protected]>
Date: Thu, 22 Nov 2007 10:48:47 +0000
Subject: [PATCH] Reuseable MFD core code suitable for multifunction chips with
built in IRQ multiplexing and local RAM.
--- drivers/mfd/mfd-core.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mfd/mfd-core.h | 26 ++++++++++++ include/linux/ioport.h | 3 + 3 files changed, 133 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/mfd-core.c create mode 100644 drivers/mfd/mfd-core.h
drivers/mfd/mfd-core.c new file +30 -0
@@ -0,0 +1,104 @@ +/* +* drivers/mfd/mfd-core.c +* +* core MFD support +* Copyright (c) 2006 Ian Molton +* +* 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. +* +*/ + +#include <linux/ioport.h> +#include <linux/slab.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include "mfd-core.h" + +void mfd_free_devices (struct platform_device **devices,int nr_devs) +{ +int i; + +for (i =0 ; i < nr_devs; i++) { +platform_device_unregister (*devices++); + } +kfree (devices); +} +EXPORT_SYMBOL_GPL (mfd_free_devices); + +#define SIGNED_SHIFT (val, shift) ((shift) >= 0 ? ((val) << (shift)) :
((val) >> -(shift)))
+
+struct platform_device **mfd_add_devices(struct platform_device *dev,
+ struct mfd_device_data *mfd,
+ int nr_devs,
+ struct resource *mem,
+ int relative_addr_shift, int irq_base)
+{
+ struct platform_device **devices;
+ int i, r, base;
+
+ devices = kzalloc(nr_devs * sizeof(struct platform_device*),
+ GFP_KERNEL);
+ if (!devices)
+ return NULL;
+
+ for (i = 0; i < nr_devs; i++) {
+ struct platform_device *sdev;
+ struct mfd_device_data *blk = &mfd[i];
+ struct resource *res;
+
+ if (!(sdev = platform_device_alloc (blk->name, -1)))
+ goto fail;
+
+ sdev->dev.parent = &dev->dev;
+ devices[i] = sdev;
+ platform_device_add_data (sdev, &(blk->hwconfig),
+ sizeof(void*));
+
+ /* Allocate space for the subdevice resources temporarily so
+ that we can modify them */
+
+ res = kzalloc(blk->num_resources * sizeof (struct
resource), GFP_KERNEL);
+ if (!res)
+ goto fail;
+
+ for (r = 0; r < blk->num_resources; r++) {
+ res[r].name = blk->res[r].name;
+
+ /* Find out base to use */
+ base = 0;
+ if (blk->res[r].flags & IORESOURCE_MEM) {
+ base = mem->start;
+ } else if ((blk->res[r].flags & IORESOURCE_IRQ) &&
+ (blk->res[r].flags & IORESOURCE_IRQ_MFD_SUBDEVICE)) {
+ base = irq_base;
+ }
+
+ /* Adjust resource */
+ if (blk->res[r].flags & IORESOURCE_MEM) {
+ res[r].parent = mem;
+ res[r].start = base + SIGNED_SHIFT(blk->res[r].start,relative_addr_shift);
+ res[r].end = base + SIGNED_SHIFT(blk->res[r].end,relative_addr_shift);
+ } else {
+ res[r].start = base + blk->res[r].start;
+ res[r].end = base + blk->res[r].end;
+ }
+ res[r].flags = blk->res[r].flags;
+ }
+ platform_device_add_resources(sdev, res, blk->num_resources);
+ kfree(res);
+
+ if (platform_device_add(sdev)) {
+ goto fail;
+ }
+
+ printk(KERN_INFO "MFD: registering %s\n", blk->name);
+ }
+ return devices;
+
+fail:
+ mfd_free_devices(devices, i + 1);
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(mfd_add_devices);
drivers/mfd/mfd-core.h new file +26 -0
@@ -0,0 +1,26 @@ +/* +* drivers/mfd/mfd-core.h +* +* core MFD support +* Copyright (c) 2006 Ian Molton +* +* 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. +* +*/ + +struct mfd_device_data { +char *name; +struct resource *res; +int num_resources; +void *hwconfig;/* platform_data to pass to the subdevice */ +}; + +struct platform_device **mfd_add_devices (struct platform_device *dev, +struct mfd_device_data *mfd,int n_devs, +struct resource *mem, +int relative_addr_shift,int irq_base); + +void mfd_free_devices (struct platform_device **devices,int nr_devs); +
include/linux/ioport.h +3 -0
@@ -57,6 +57,9 @@ struct resource_list {#define IORESOURCE_IRQ_LOWLEVEL (1<<3)#define IORESOURCE_IRQ_SHAREABLE (1<<4) +/* MFD device IRQ specific bits (IORESOURCE_BITS) */ +#define IORESOURCE_IRQ_MFD_SUBDEVICE (1<<5) +/* ISA PnP DMA specific bits (IORESOURCE_BITS) */ #define IORESOURCE_DMA_TYPE_MASK (3<<0)#define IORESOURCE_DMA_8BIT (0<<0)
Signature · 1 line
1.5.3.5.737.gdee1b
Quoted 1 line
From
0ff1dd5ecedd43794515c46f503abf606ea5ce12Mon Sep 17 00:00:00 2001
From: Ian Molton <[email protected]>
Date: Thu, 22 Nov 2007 00:17:41 +0000
Subject: [PATCH] Preliminary support for Toshibas TMIO based multifunction chips.
Includes support for:
* t7l66xb
* tc6387xb
* tc6393xb
--- drivers/mfd/Kconfig | 25 +++ drivers/mfd/Makefile | 3 + drivers/mfd/t7l66xb.c | 286 +++++++++++++++++++++++++++++++++++ drivers/mfd/tc6387xb.c | 143 +++++++++++++++++ drivers/mfd/tc6393xb.c | 369 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/t7l66xb.h | 56 +++++++ include/linux/tc6387xb.h | 34 ++++ include/linux/tc6393.h | 136 +++++++++++++++++ include/linux/tmio_mmc.h | 26 +++ include/linux/tmio_nand.h | 15 ++ include/linux/tmio_ohci.h | 15 ++ 11 files changed, 1108 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/t7l66xb.c create mode 100644 drivers/mfd/tc6387xb.c create mode 100644 drivers/mfd/tc6393xb.c create mode 100644 include/linux/t7l66xb.h create mode 100644 include/linux/tc6387xb.h create mode 100644 include/linux/tc6393.h create mode 100644 include/linux/tmio_mmc.h create mode 100644 include/linux/tmio_nand.h create mode 100644 include/linux/tmio_ohci.h
drivers/mfd/Kconfig +25 -0
@@ -15,6 +15,31 @@ config MFD_SM501 interface. The device may be connected by PCI or local bus with varying functions enabled. +config MFD_T7L66XB + bool "Toshiba T7L66XB SoC support" + ---help--- + This driver supports the T7L66XB, which incorporates SD/MMC, and + USB host functionality. associated subdevices are: + tmio_mmc + tmio_ohci + +config MFD_TC6387XB + bool "Toshiba TC6387XB SoC support" + ---help--- + This driver supports the TC6393XB, which incorporates SD/MMC, NAND, + Video, and USB host functionality. associated subdevices are: + tmio_mmc + +config MFD_TC6393XB + bool "Toshiba TC6393XB SoC support" + ---help--- + This driver supports the TC6393XB, which incorporates SD/MMC, NAND, + Video, and USB host functionality. associated subdevices are: + tmio_mmc + tmio_nand + tmio_fb + tmio_ohci + endmenu menu "Multimedia Capabilities Port drivers"
drivers/mfd/Makefile +3 -0
@@ -3,6 +3,9 @@ # obj-$(CONFIG_MFD_SM501) += sm501.o +obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o mfd-core.o +obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o mfd-core.o +obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o mfd-core.o obj-$(CONFIG_MCP) += mcp-core.o obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
drivers/mfd/t7l66xb.c new file +78 -0
78 lines not on this page · raw message
*desc)
+{
+ int req, i;
+ struct t7l66xb_data *data = get_irq_data(irq);
+
+ /* Acknowledge the parent IRQ */
+ desc->chip->ack(irq);
+
+ while ( (req = (readb(data->mapbase + T7L66XB_SYS_ISR)
+ & ~(readb(data->mapbase + T7L66XB_SYS_IMR)))) ) {
+ for (i = 0; i <= 7; i++) {
+ int dev_irq = data->irq_base + i;
+ struct irq_desc *d = NULL;
+ if ((req & (1<<i))) {
+ d = irq_desc + dev_irq;
+ d->handle_irq(dev_irq, d);
+ }
+ }
+ }
+}
+
+
+static void t7l66xb_mask_irq(unsigned int irq)
+{
+ struct t7l66xb_data *data = get_irq_chip_data(irq);
+
+ writeb(readb(data->mapbase + T7L66XB_SYS_IMR) | 1 << (irq -data->irq_base), data->mapbase + T7L66XB_SYS_IMR);
+}
+
+static void t7l66xb_unmask_irq(unsigned int irq)
+{
+ struct t7l66xb_data *data = get_irq_chip_data(irq);
+
+ writeb(readb(data->mapbase + T7L66XB_SYS_IMR) & ~( 1 << (irq -
data->irq_base)),data->mapbase + T7L66XB_SYS_IMR);
+}
+
+static struct irq_chip t7l66xb_chip = {
+ .name = "t7l66xb",
+ .ack = t7l66xb_mask_irq,
+ .mask = t7l66xb_mask_irq,
+ .unmask = t7l66xb_unmask_irq,
+};
+
+/* Install the IRQ handler */
+static void t7l66xb_setup_irq(struct t7l66xb_data *tchip) +{
+ int i;
+
+ for (i = 0; i < T7L66XB_NR_IRQS; i++) {
+ int irq = tchip->irq_base + i;
+ set_irq_chip (irq, &t7l66xb_chip);
+ set_irq_chip_data (irq, tchip);
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+
+ set_irq_data (tchip->irq_nr, tchip);
+ set_irq_chained_handler (tchip->irq_nr, t7l66xb_irq_handler);
+ set_irq_type (tchip->irq_nr, IRQT_FALLING);
+}
+
+static void t7l66xb_hwinit(struct platform_device *dev) +{
+ struct t7l66xb_platform_data *pdata = platform_get_platdata(dev);
+
+ if (pdata && pdata->hw_init)
+ pdata->hw_init();
+}
+
+
+#ifdef CONFIG_PM
+
+static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct t7l66xb_platform_data *pdata = platform_get_platdata(dev);
+
+
+ if (pdata && pdata->suspend)
+ pdata->suspend();
+
+ return 0;
+}
+
+static int t7l66xb_resume(struct platform_device *dev)
+{
+ struct t7l66xb_platform_data *pdata = platform_get_platdata(dev);
+
+ if (pdata && pdata->resume)
+ pdata->resume();
+
+ t7l66xb_hwinit(dev);
+
+ return 0;
+}
+#endif
+
+static int t7l66xb_probe(struct platform_device *dev)
+{
+ struct t7l66xb_platform_data *pdata = dev->dev.platform_data;
+ unsigned long pbase = (unsigned long)dev->resource[0].start;
+ unsigned long plen = dev->resource[0].end - dev->resource[0].start;
+ int err = -ENOMEM;
+ struct t7l66xb_data *data;
+
+ data = kmalloc (sizeof (struct t7l66xb_data), GFP_KERNEL);
+ if (!data)
+ goto out;
+
+ data->irq_base = pdata->irq_base;
+ data->irq_nr = dev->resource[1].start;
+
+ if (!data->irq_base) {
+ printk("t7166xb: uninitialized irq_base!\n");
+ goto out_free_data;
+ }
+
+ data->mapbase = ioremap(pbase, plen);
+ if(!data->mapbase)
+ goto out_free_irqs;
+
+ platform_set_drvdata(dev, data);
+ t7l66xb_setup_irq(data);
+ t7l66xb_hwinit(dev);
+
+ /* Mask IRQs -- should we mask/unmask on suspend/resume? */
+ writew(0xbf, data->mapbase + T7L66XB_SYS_IMR);
+
+ printk(KERN_INFO "%s rev %d @ 0x%08lx using irq %d-%d on irq %d\n",
+ dev->name, readw(data->mapbase + T7L66XB_SYS_RIDR), + (unsigned long)data->mapbase, data->irq_base, + data->irq_base + T7L66XB_NR_IRQS - 1, data->irq_nr);
+
+ data->devices = mfd_add_devices(dev, t7l66xb_devices,
+ ARRAY_SIZE(t7l66xb_devices),
+ &dev->resource[0], 0, data->irq_base);
+
+ if(!data->devices){
+ printk(KERN_INFO "%s: Failed to allocate devices!\n",
+ dev->name);
+ goto out_free_devices;
+ }
+
+ return 0;
+
+out_free_devices:
+ mfd_free_devices(data->devices, ARRAY_SIZE(t7l66xb_devices));
+out_free_irqs:
+out_free_data:
+ kfree(data);
+out:
+ return err;
+}
+
+static int t7l66xb_remove(struct platform_device *dev) +{
+ struct t7l66xb_data *tchip = platform_get_drvdata(dev);
+ int i;
+
+ /* Free subdevices */
+ mfd_free_devices(tchip->devices, ARRAY_SIZE(t7l66xb_devices));
+
+ /* Take down IRQ handling */
+ for (i = 0; i < T7L66XB_NR_IRQS; i++) {
+ int irq = i + tchip->irq_base; + set_irq_handler (irq, NULL); + set_irq_chip (irq, NULL); + set_irq_chip_data (irq, NULL);
+ }
+ set_irq_chained_handler (tchip->irq_nr, NULL);
+
+ /* Free core resources */
+ iounmap (tchip->mapbase);
+
+ return 0;
+}
+
+
+static struct platform_driver t7l66xb_platform_driver = {
+ .driver = {
+ .name = "t7l66xb",
+ },
+ .probe = t7l66xb_probe,
+ .remove = t7l66xb_remove,
+#ifdef CONFIG_PM
+ .suspend = t7l66xb_suspend,
+ .resume = t7l66xb_resume,
+#endif
+};
+
+
+static int __init t7l66xb_init(void)
+{
+ int retval = 0;
+
+ retval = platform_driver_register (&t7l66xb_platform_driver);
+ return retval;
+}
+
+static void __exit t7l66xb_exit(void)
+{
+ platform_driver_unregister(&t7l66xb_platform_driver);
+}
+
+module_init(t7l66xb_init); +module_exit(t7l66xb_exit);
+
+MODULE_DESCRIPTION("Toshiba T7L66XB core driver"); +MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ian Molton and Dirk Opfer");
drivers/mfd/tc6387xb.c new file +46 -0
46 lines not on this page · raw message
state)
+{
+ struct tc6387xb_data *data = platform_get_drvdata(dev);
+
+ if (data && data->platform && data->platform->suspend)
+ data->platform->suspend();
+
+ return 0;
+}
+
+static int tc6387xb_resume(struct platform_device *dev) +{
+ struct tc6387xb_data *data = platform_get_drvdata(dev);
+
+ if (data && data->platform && data->platform->resume)
+ data->platform->resume();
+
+ return 0;
+}
+#endif
+
+static struct resource tc6387xb_mmc_resources[] = {
+ {
+ .name = "control",
+ .start = TC6387XB_MMC_CTL_BASE,
+ .end = TC6387XB_MMC_CTL_BASE + 0x1ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "config",
+ .start = TC6387XB_MMC_CNF_BASE,
+ .end = TC6387XB_MMC_CNF_BASE + 0xff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = TC6387XB_MMC_IRQ,
+ .end = TC6387XB_MMC_IRQ,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,+ },
+};
+
+static struct mfd_device_data tc6387xb_devices[] = {
+ {
+ .name = "tmio_mmc", + .res = tc6387xb_mmc_resources, + .num_resources = ARRAY_SIZE(tc6387xb_mmc_resources),
+ },
+};
+
+
+static int tc6387xb_probe(struct platform_device *pdev) +{
+ struct tc6387xb_data *data;
+
+ data = kmalloc(sizeof(struct tc6387xb_data), GFP_KERNEL);
+ if(!data)
+ return -ENOMEM;
+
+ data->irq = pdev->resource[1].start;
+ data->platform = pdev->dev.platform_data;
+ platform_set_drvdata(pdev, data);
+
+ tc6387xb_hwinit(pdev);
+
+ mfd_add_devices(pdev, tc6387xb_devices, ARRAY_SIZE(tc6387xb_devices),
&pdev->resource[0], 0, data->irq);
+
+ /* Init finished. */
+ return 0;
+}
+
+static struct platform_driver tc6387xb_platform_driver = {
+ .driver = {
+ .name = "tc6387xb",
+ },
+ .probe = tc6387xb_probe,
+#ifdef CONFIG_PM
+ .suspend = tc6387xb_suspend,
+ .resume = tc6387xb_resume,
+#endif
+};
+
+
+static int __init tc6387xb_init(void)
+{
+ return platform_driver_register (&tc6387xb_platform_driver);
+}
+
+static void __exit tc6387xb_exit(void)
+{
+ platform_driver_unregister(&tc6387xb_platform_driver);
+}
+
+module_init(tc6387xb_init); +module_exit(tc6387xb_exit);
+
+MODULE_DESCRIPTION("Toshiba TC6387XB core driver"); +MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ian Molton");
drivers/mfd/tc6393xb.c new file +70 -0
70 lines not on this page · raw message
state) {
+ struct tc6393xb_data *chip = platform_get_drvdata(sdev);
+ unsigned char tmp;
+
+ if(state == MMC_CLOCK_ENABLED){
+ tmp = readw(chip->mapbase + TC6393_SYS_GPIBCR1);
+ writew(tmp | CK32KEN, chip->mapbase + TC6393_SYS_GPIBCR1);
+ }
+}
+
+static struct tmio_mmc_hwconfig tc6393xb_mmc_hwconfig = {
+ .set_mmc_clock = tc6393xb_mmc_set_clock,
+};
+
+static struct resource tc6393xb_mmc_resources[] = {
+ {
+ .name = "control",
+ .start = TC6393XB_MMC_CTL_BASE,
+ .end = TC6393XB_MMC_CTL_BASE + 0x1ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "config",
+ .start = TC6393XB_MMC_CNF_BASE,
+ .end = TC6393XB_MMC_CNF_BASE + 0xff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = TC6393XB_MMC_IRQ,
+ .end = TC6393XB_MMC_IRQ,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE,
+ },
+};
+
+static struct resource tc6393xb_nand_resources[] = {
+ {
+ .name = "control",
+ .start = TC6393XB_NAND_CTL_BASE,
+ .end = TC6393XB_NAND_CTL_BASE + 0x1ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "config",
+ .start = TC6393XB_NAND_CNF_BASE,
+ .end = TC6393XB_NAND_CNF_BASE + 0xff,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct mfd_device_data tc6393xb_devices[] = {
+ {
+ .name = "tmio_mmc",
+ .res = tc6393xb_mmc_resources,
+ .num_resources = ARRAY_SIZE(tc6393xb_mmc_resources),
+ .hwconfig = &tc6393xb_mmc_hwconfig,
+ },
+ {
+ .name = "tmio-nand",
+ .res = tc6393xb_nand_resources,
+ .num_resources = ARRAY_SIZE(tc6393xb_nand_resources),
+ .hwconfig = &tc6393xb_nand_hwconfig,
+ },
+};
+
+/** TC6393 interrupt handling stuff.
+ * NOTE: TC6393 has a 1 to many mapping on all of its IRQs.
+ * that is, there is only one real hardware interrupt
+ * we determine which interrupt it is by reading some IO memory.
+ * We have two levels of expansion, first in the handler for the
+ * hardware interrupt we generate an interrupt
+ * IRQ_TC6393_*_BASE and those handlers generate more interrupts
+ *
+ */
+static void tc6393xb_irq_handler(unsigned int irq, struct irq_desc *desc)
+{
+ int req, i;
+ struct tc6393xb_data *data = get_irq_data(irq);
+
+ /* Acknowledge the parent IRQ */
+ desc->chip->ack(irq);
+
+ while ( (req = (readb(data->mapbase + TC6393_SYS_ISR)
+ & ~(readb(data->mapbase + TC6393_SYS_IMR)))) ) {
+ for (i = 0; i <= 7; i++) {
+ int dev_irq = data->irq_base + i;
+ struct irq_desc *d = NULL;
+ if ((req & (1<<i))) {
+ if(i != 1) printk("IRQ! from %d\n", i);
+ d = irq_desc + dev_irq;
+ d->handle_irq(dev_irq, d);
+ }
+ }
+ }
+}
+
+static void tc6393xb_mask_irq(unsigned int irq)
+{
+ struct tc6393xb_data *tc6393 = get_irq_chip_data(irq);
+
+ writeb(readb(tc6393->mapbase + TC6393_SYS_IMR) | 1 << (irq -
tc6393->irq_base),tc6393->mapbase + TC6393_SYS_IMR);
+}
+
+static void tc6393xb_unmask_irq(unsigned int irq)
+{
+ struct tc6393xb_data *tc6393 = get_irq_chip_data(irq);
+
+ writeb(readb(tc6393->mapbase + TC6393_SYS_IMR) & ~( 1 << (irq -
tc6393->irq_base)),tc6393->mapbase + TC6393_SYS_IMR);
+}
+
+static struct irq_chip tc6393xb_chip = {
+ .ack = tc6393xb_mask_irq,
+ .mask = tc6393xb_mask_irq,
+ .unmask = tc6393xb_unmask_irq,
+};
+
+
+static void tc6393xb_setup_irq(struct tc6393xb_data *tchip) +{
+ int i;
+
+ for (i = 0; i < TC6393XB_NR_IRQS; i++) {
+ int irq = tchip->irq_base + i;
+ set_irq_chip (irq, &tc6393xb_chip);
+ set_irq_chip_data (irq, tchip);
+ set_irq_handler(irq, handle_level_irq);
+ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE);
+ }
+
+ set_irq_data (tchip->irq_nr, tchip);
+ set_irq_chained_handler (tchip->irq_nr, tc6393xb_irq_handler);
+ set_irq_type (tchip->irq_nr, IRQT_FALLING);
+}
+
+void tc6393xb_hwinit(struct platform_device *dev)
+{
+ struct tc6393xb_platform_data *pdata = platform_get_platdata(dev);
+ struct tc6393xb_data *tchip = platform_get_drvdata(dev);
+
+ if(!pdata || !tchip){
+ BUG_ON("no driver data!\n");
+ return;
+ }
+
+ if (pdata->hw_init)
+ pdata->hw_init();
+
+ writew(0, tchip->mapbase + TC6393_SYS_FER);
+
+ /* Clock setting */
+ writew(pdata->sys_pll2cr, tchip->mapbase + TC6393_SYS_PLL2CR); + writew(pdata->sys_ccr, tchip->mapbase + TC6393_SYS_CCR); + writew(pdata->sys_mcr, tchip->mapbase + TC6393_SYS_MCR);
+
+ /* GPIO */
+ writew(pdata->sys_gper, tchip->mapbase + TC6393_SYS_GPER);
+ writew(pdata->sys_gpodsr1, tchip->mapbase + TC6393_SYS_GPODSR1);
+ writew(pdata->sys_gpooecr1, tchip->mapbase + TC6393_SYS_GPOOECR1);
+}
+
+
+#ifdef CONFIG_PM
+
+static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct tc6393xb_platform_data *pdata = platform_get_platdata(dev);
+
+ if (pdata && pdata->suspend)
+ pdata->suspend();
+
+ return 0;
+}
+
+static int tc6393xb_resume(struct platform_device *dev)
+{
+ struct tc6393xb_platform_data *pdata = platform_get_platdata(dev);
+
+ if (pdata && pdata->resume)
+ pdata->resume();
+
+ tc6393xb_hwinit(dev);
+
+ return 0;
+}
+
+#endif
+
+static int tc6393xb_probe(struct platform_device *dev)
+{
+ struct tc6393xb_platform_data *pdata = dev->dev.platform_data;
+ unsigned long pbase = (unsigned long)dev->resource[0].start;
+ unsigned long plen = dev->resource[0].end - dev->resource[0].start;
+ int err = -ENOMEM;
+ struct tc6393xb_data *data;
+
+ data = kmalloc (sizeof (struct tc6393xb_data), GFP_KERNEL);
+ if (!data)
+ goto out;
+
+ data->irq_base = pdata->irq_base;
+ data->irq_nr = dev->resource[1].start;
+
+ if (!data->irq_base) {
+ printk("tc6393xb: uninitialized irq_base!\n");
+ goto out_free_data;
+ }
+
+ data->mapbase = ioremap(pbase, plen);
+ if(!data->mapbase)
+ goto out_free_irqs;
+
+ platform_set_drvdata(dev, data);
+ tc6393xb_setup_irq (data);
+ tc6393xb_hwinit(dev);
+
+ /* Enable (but mask!) our IRQs */
+ writew(0, data->mapbase + TC6393_SYS_IRR);
+ writew(0xbf, data->mapbase + TC6393_SYS_IMR);
+
+ printk(KERN_INFO "%s rev %d @ 0x%08lx using irq %d-%d on irq %d\n",
+ dev->name, readw(data->mapbase + TC6393_SYS_RIDR), + (unsigned long)data->mapbase, data->irq_base, + data->irq_base + TC6393XB_NR_IRQS - 1, data->irq_nr);
+
+ data->devices = mfd_add_devices(dev, tc6393xb_devices,
+ ARRAY_SIZE(tc6393xb_devices),
+ &dev->resource[0], 0, data->irq_base);
+
+ if(!data->devices) {
+ printk(KERN_INFO "%s: Failed to allocate devices!\n",
+ dev->name);
+ goto out_free_devices;
+ }
+
+ return 0;
+
+out_free_devices:
+ mfd_free_devices(data->devices, ARRAY_SIZE(tc6393xb_devices));
+out_free_irqs:
+out_free_data:
+ kfree(data);
+out:
+ return err;
+}
+
+static int tc6393xb_remove(struct platform_device *dev) +{
+ struct tc6393xb_data *tchip = platform_get_drvdata(dev);
+ int i;
+
+ /* Free subdevices */
+ mfd_free_devices(tchip->devices, ARRAY_SIZE(tc6393xb_devices));
+
+ /* Take down IRQ handling */
+ for (i = 0; i < TC6393XB_NR_IRQS; i++) {
+ int irq = i + tchip->irq_base;
+ set_irq_handler (irq, NULL);
+ set_irq_chip (irq, NULL);
+ set_irq_chip_data (irq, NULL);
+ }
+
+ set_irq_chained_handler (tchip->irq_nr, NULL);
+
+ /* Free core resources */
+ iounmap (tchip->mapbase);
+
+ return 0;
+}
+
+
+static struct platform_driver tc6393xb_device_driver = {
+ .driver = {
+ .name = "tc6393xb",
+ },
+ .probe = tc6393xb_probe,
+ .remove = tc6393xb_remove,
+#ifdef CONFIG_PM
+ .suspend = tc6393xb_suspend,
+ .resume = tc6393xb_resume,
+#endif
+};
+
+
+static int __init tc6393xb_init(void)
+{
+ int retval = 0;
+ retval = platform_driver_register (&tc6393xb_device_driver);
+ return retval;
+}
+
+static void __exit tc6393xb_exit(void)
+{
+ platform_driver_unregister(&tc6393xb_device_driver);
+}
+
+module_init(tc6393xb_init); +module_exit(tc6393xb_exit);
+
+MODULE_DESCRIPTION("Toshiba TC6393 core driver"); +MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dirk Opfer and Ian Molton");
include/linux/t7l66xb.h new file +56 -0
56 lines not on this page · raw message
include/linux/tc6387xb.h new file +34 -0
@@ -0,0 +1,34 @@ +/* +* +* (C) Copyright 2005 Ian Molton <[email protected]> +* +* May be copied or modified under the terms of the GNU General Public +* License. See linux/COPYING for more information. +* +*/ + +#include <linux/platform_device.h> + +/* FIXME - this needs to be a common struct to all TMIO based MFDs. */ +struct tmio_hwconfig { +void (*hwinit)(struct platform_device *sdev); +void (*suspend)(struct platform_device *sdev); +void (*resume)(struct platform_device *sdev); +}; + +struct tc6387xb_platform_data +{ +/* Standard MFD properties */ +int irq_base; +struct platform_device **child_devs; +int num_child_devs; + +void (* hw_init) (void ); +void (* suspend) (void ); +void (* resume) (void ); +}; + +#define TC6387XB_MMC_CNF_BASE (0x000200) +#define TC6387XB_MMC_CTL_BASE (0x000800) +#define TC6387XB_MMC_IRQ (0) +
include/linux/tc6393.h new file +94 -0
94 lines not on this page · raw message
Contorol 1
+#define TC6393_SYS_GPIARCR2 0x082 // GP Internal Active Register
Contorol 2
+#define TC6393_SYS_GPIARLCR1 0x084 // GP Internal Active Register
Level Contorol 1
+#define TC6393_SYS_GPIARLCR2 0x086 // GP Internal Active Register
Level Contorol 2
+
+#define TC6393_SYS_GPIBCR1 0x089 // GPa Internal Activ Register
Contorol 1
+
+#define TC6393_SYS_GPIBCR2 0x08A // GPa Internal Activ Register
Contorol 2
+#define TC6393_SYS_GPaIARCR 0x08C +#define TC6393_SYS_GPaIARLCR 0x090 +#define TC6393_SYS_GPaIBCR 0x094 +#define TC6393_SYS_CCR 0x098 /* Clock Control Register */ +#define TC6393_SYS_PLL2CR 0x09A // PLL2 Control +#define TC6393_SYS_PLL1CR1 0x09C // PLL1 Control 1 +#define TC6393_SYS_PLL1CR2 0x09E // PLL1 Control 2 +#define TC6393_SYS_DCR 0x0A0 +#define TC6393_SYS_FER 0x0E0 /* Function Enable Register */ +#define TC6393_SYS_MCR 0x0E4 +#define TC6393_SYS_ConfigCR 0x0FC + +/* GPIO bit */ +#define TC6393_GPIO19 ( 1 << 19 ) +#define TC6393_GPIO18 ( 1 << 18 ) +#define TC6393_GPIO17 ( 1 << 17 ) +#define TC6393_GPIO16 ( 1 << 16 ) +#define TC6393_GPIO15 ( 1 << 15 ) +#define TC6393_GPIO14 ( 1 << 14 ) +#define TC6393_GPIO13 ( 1 << 13 ) +#define TC6393_GPIO12 ( 1 << 12 ) +#define TC6393_GPIO11 ( 1 << 11 ) +#define TC6393_GPIO10 ( 1 << 10 ) +#define TC6393_GPIO9 ( 1 << 9 ) +#define TC6393_GPIO8 ( 1 << 8 ) +#define TC6393_GPIO7 ( 1 << 7 ) +#define TC6393_GPIO6 ( 1 << 6 ) +#define TC6393_GPIO5 ( 1 << 5 ) +#define TC6393_GPIO4 ( 1 << 4 ) +#define TC6393_GPIO3 ( 1 << 3 ) +#define TC6393_GPIO2 ( 1 << 2 ) +#define TC6393_GPIO1 ( 1 << 1 ) +#define TC6393_GPIO0 ( 1 << 0 )
+
+#define TC6393XB_NR_IRQS 8
include/linux/tmio_mmc.h new file +26 -0
@@ -0,0 +1,26 @@ +/* +* Copyright (c) 2005 Ian Molton +* +* 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. +* +*/ + +#include <linux/platform_device.h> + +#define MMC_CLOCK_DISABLED 0 +#define MMC_CLOCK_ENABLED 1 + +#define TMIO_WP_ALWAYS_RW ((void*)-1) + +struct tmio_mmc_hwconfig { +void (*hwinit)(struct platform_device *sdev); +void (*set_mmc_clock)(struct platform_device *sdev,int state); + +/* NULL - use ASIC3 signal, +TMIO_WP_ALWAYS_RW - assume always R/W (e.g. miniSD) +otherwise - machine-specific handler */ +int (*mmc_get_ro)(struct platform_device *pdev); +short address_shift; +};
include/linux/tmio_nand.h new file +15 -0
@@ -0,0 +1,15 @@ +/* +* Copyright (c) 2005 Ian Molton +* +* 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. +* +*/ + +struct tmio_nand_hwconfig { +void (*hwinit)(struct platform_device *sdev); +void (*suspend)(struct platform_device *sdev); +void (*resume)(struct platform_device *sdev); +}; +
include/linux/tmio_ohci.h new file +15 -0
@@ -0,0 +1,15 @@ +/* +* Copyright (c) 2005 Ian Molton +* +* 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. +* +*/ + +struct tmio_ohci_hwconfig { +void (*hwinit)(struct platform_device *sdev); +void (*suspend)(struct platform_device *sdev); +void (*resume)(struct platform_device *sdev); +}; +
Signature · 1 line
1.5.3.5.737.gdee1b
Quoted 1 line
From
9d9be4115972ae4c75d27c1a6b7aa1e3a840a98fMon Sep 17 00:00:00 2001
From: Ian Molton <[email protected]>
Date: Thu, 22 Nov 2007 00:00:20 +0000
Subject: [PATCH] Add eseries platform support for the relevant TMIO multifunction devices used.
--- arch/arm/mach-pxa/Makefile | 5 ++ arch/arm/mach-pxa/e330_tc6387xb.c | 98 +++++++++++++++++++++++++++++++++ arch/arm/mach-pxa/e400_t7l66xb.c | 107 ++++++++++++++++++++++++++++++++++++ arch/arm/mach-pxa/e740_t7l66xb.c | 103 +++++++++++++++++++++++++++++++++++ arch/arm/mach-pxa/e750_tc6393xb.c | 109 +++++++++++++++++++++++++++++++++++++ 5 files changed, 422 insertions(+), 0 deletions(-) create mode 100644 arch/arm/mach-pxa/e330_tc6387xb.c create mode 100644 arch/arm/mach-pxa/e400_t7l66xb.c create mode 100644 arch/arm/mach-pxa/e740_t7l66xb.c create mode 100644 arch/arm/mach-pxa/e750_tc6393xb.c
arch/arm/mach-pxa/Makefile +5 -0
@@ -30,6 +30,11 @@ ifeq ($(CONFIG_MACH_ZYLONITE),y) endif obj-$(CONFIG_ARCH_PXA_ESERIES) += eseries.o +obj-$(CONFIG_MACH_E330) += e330_tc6387xb.o +obj-$(CONFIG_MACH_E740) += e740_t7l66xb.o +obj-$(CONFIG_MACH_E750) += e750_tc6393xb.o +obj-$(CONFIG_MACH_E400) += e400_t7l66xb.o +obj-$(CONFIG_MACH_E800) += e750_tc6393xb.o obj-$(CONFIG_MACH_ARMCORE) += cm-x270.o
(quoted diff) +0 -0
b/arch/arm/mach-pxa/e330_tc6387xb.c
new file mode 100644
index 0000000..9859de2
arch/arm/mach-pxa/e330_tc6387xb.c new file +5 -0
@@ -0,0 +1,98 @@ +/* +* +* Copyright (C) Ian Molton. +* +* 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/irq.h>
+
+#include <asm/arch/eseries-irq.h>
+#include <asm/arch/eseries-gpio.h>
+
+#include <linux/tc6387xb.h>
+
+static struct resource e330_tc6387xb_resources[] = {
+ [0] = {
+ .start = PXA_CS4_PHYS,
+ .end = PXA_CS4_PHYS + 0x1fffff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_GPIO(5),
+ .end = IRQ_GPIO(5),
+ .flags = IORESOURCE_IRQ,+ },
+
+};
+
+/* FIXME - who should really be setting up the clock? bootloader or kernel ? */
+static void e330_tc6387xb_hwinit(void) {
+ GPCR(45) = GPIO_bit(45); /* #SUSPEND low */
+ GPCR(19) = GPIO_bit(19); /* #PCLR low (reset)
*/
+ /* 33MHz is supplied from an Xtal on the e330 (I think) */
+ pxa_gpio_mode(GPIO12_32KHz_MD);
+ mdelay(10);
+ GPSR(45) = GPIO_bit(45); /* #SUSPEND high */
+ mdelay(10);
+ GPSR(19) = GPIO_bit(19); /* #PCLR high */
+ mdelay(10);
+}
+
+static void e330_tc6387xb_suspend(void) {
+ GPCR(45) = GPIO_bit(45); /* #SUSPEND low */
+ mdelay(10);
+#if 0
+ GPCR(19) = GPIO_bit(19); /* #PCLR low */
+#endif
+}
+
+static void e330_tc6387xb_resume(void) {
+ GPSR(45) = GPIO_bit(45); /* #SUSPEND high */
+ mdelay(10);
+}
+
+static struct tc6387xb_platform_data e330_tc6387xb_info = {
+ .hw_init = &e330_tc6387xb_hwinit,
+ .suspend = &e330_tc6387xb_suspend,
+ .resume = &e330_tc6387xb_resume,
+};
+
+static struct platform_device e330_tc6387xb_device = {
+ .name = "tc6387xb",
+ .id = -1,
+ .dev = {+ .platform_data = &e330_tc6387xb_info,
+ },
+ .num_resources = ARRAY_SIZE(e330_tc6387xb_resources),
+ .resource = e330_tc6387xb_resources,
+};
+
+static int __init e330_tc6387xb_init(void) +{
+ if(!machine_is_e330())
+ return -ENODEV;
+
+ platform_device_register(&e330_tc6387xb_device);
+ return 0;
+}
+
+module_init(e330_tc6387xb_init);
+
+MODULE_AUTHOR("Ian Molton <[email protected]>"); +MODULE_DESCRIPTION("e330 tc6387xb device support"); +MODULE_LICENSE("GPLv2");
+
+
(quoted diff) +0 -0
b/arch/arm/mach-pxa/e400_t7l66xb.c
new file mode 100644
index 0000000..5c77388
arch/arm/mach-pxa/e400_t7l66xb.c new file +5 -0
@@ -0,0 +1,107 @@ +/* +* +* Copyright (C) Ian Molton. +* +* 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.
+ *
+ */
+
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/irq.h>
+
+#include <asm/arch/eseries-irq.h>
+#include <asm/arch/eseries-gpio.h>
+
+#include <linux/t7l66xb.h>
+
+/* FIXME - set properly when known */
+#define GPIO_E400_TMIO_IRQ GPIO_ESERIES_TMIO_IRQ
+
+static struct resource e400_t7l66xb_resources[] = {
+ [0] = {
+ .start = PXA_CS4_PHYS,
+ .end = PXA_CS4_PHYS + 0x1fffff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_GPIO(GPIO_E400_TMIO_IRQ),
+ .end = IRQ_GPIO(GPIO_E400_TMIO_IRQ),
+ .flags = IORESOURCE_IRQ,+ },
+
+};
+
+/* FIXME - who should really be setting up the clock? bootloader or kernel ? */
+static void e400_t7l66xb_hwinit(void) {
+ return;
+ GPCR(19) = GPIO_bit(19); /* #SUSPEND low */
+ GPCR(7) = GPIO_bit(7); /* #PCLR low (reset) */
+ pxa_gpio_mode(GPIO7_48MHz_MD);
+ pxa_gpio_mode(GPIO12_32KHz_MD);
+ mdelay(10);
+ GPSR(19) = GPIO_bit(19); /* #SUSPEND high */
+ mdelay(10);
+ GPSR(7) = GPIO_bit(7); /* #PCLR high */
+ mdelay(10);
+}
+
+static void e400_t7l66xb_suspend(void) {
+ return;
+ GPCR(19) = GPIO_bit(19); /* #SUSPEND low */
+ mdelay(10);
+ GPCR(7) = GPIO_bit(7); /* #PCLR low */
+
+#if 0
+ /* kill clock */
+ pxa_gpio_mode(GPIO7_48MHz_MD|GPIO_OUT);
+ GPSR0 = GPIO_bit(GPIO7_48MHz);
+#endif
+}
+
+static void e400_t7l66xb_resume(void) {
+ e400_t7l66xb_hwinit();
+}
+
+static struct t7l66xb_platform_data e400_t7l66xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .hw_init = &e400_t7l66xb_hwinit,
+ .suspend = &e400_t7l66xb_suspend,
+ .resume = &e400_t7l66xb_resume,
+};
+
+static struct platform_device e400_t7l66xb_device = {
+ .name = "t7l66xb",
+ .id = -1,
+ .dev = {+ .platform_data = &e400_t7l66xb_info,
+ },
+ .num_resources = ARRAY_SIZE(e400_t7l66xb_resources),
+ .resource = e400_t7l66xb_resources,
+};
+
+static int __init e400_t7l66xb_init(void) +{
+ if(!machine_is_e400())
+ return -ENODEV;
+
+ platform_device_register(&e400_t7l66xb_device);
+ return 0;
+}
+
+module_init(e400_t7l66xb_init);
+
+MODULE_AUTHOR("Ian Molton <[email protected]>"); +MODULE_DESCRIPTION("e400 t7l66xb device support"); +MODULE_LICENSE("GPLv2");
+
(quoted diff) +0 -0
b/arch/arm/mach-pxa/e740_t7l66xb.c
new file mode 100644
index 0000000..26d1252
arch/arm/mach-pxa/e740_t7l66xb.c new file +5 -0
@@ -0,0 +1,103 @@ +/* +* +* Copyright (C) Ian Molton. +* +* 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/irq.h>
+
+#include <asm/arch/eseries-irq.h>
+#include <asm/arch/eseries-gpio.h>
+
+#include <linux/t7l66xb.h>
+
+static struct resource e740_t7l66xb_resources[] = {
+ [0] = {
+ .start = PXA_CS4_PHYS,
+ .end = PXA_CS4_PHYS + 0x1fffff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_GPIO(GPIO_ESERIES_TMIO_IRQ),
+ .end = IRQ_GPIO(GPIO_ESERIES_TMIO_IRQ),
+ .flags = IORESOURCE_IRQ,+ },
+
+};
+
+/* FIXME - who should really be setting up the clock? bootloader or kernel ? */
+static void e740_t7l66xb_hwinit(void) {
+
+ GPCR(19) = GPIO_bit(19); /* #SUSPEND low */
+ GPCR(7) = GPIO_bit(7); /* #PCLR low (reset) */
+ pxa_gpio_mode(GPIO7_48MHz_MD);
+ pxa_gpio_mode(GPIO12_32KHz_MD);
+ mdelay(10);
+ GPSR(19) = GPIO_bit(19); /* #SUSPEND high */
+ mdelay(10);
+ GPSR(7) = GPIO_bit(7); /* #PCLR high */
+ mdelay(10);
+}
+
+static void e740_t7l66xb_suspend(void) {
+ GPCR(19) = GPIO_bit(19); /* #SUSPEND low */
+ mdelay(10);
+ GPCR(7) = GPIO_bit(7); /* #PCLR low */
+
+#if 0
+ /* kill clock */
+ pxa_gpio_mode(GPIO7_48MHz_MD|GPIO_OUT);
+ GPSR0 = GPIO_bit(GPIO7_48MHz);
+#endif
+}
+
+static void e740_t7l66xb_resume(void) {
+ e740_t7l66xb_hwinit();
+}
+
+static struct t7l66xb_platform_data e740_t7l66xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .hw_init = &e740_t7l66xb_hwinit,
+ .suspend = &e740_t7l66xb_suspend,
+ .resume = &e740_t7l66xb_resume,
+};
+
+static struct platform_device e740_t7l66xb_device = {
+ .name = "t7l66xb",
+ .id = -1,
+ .dev = {+ .platform_data = &e740_t7l66xb_info,
+ },
+ .num_resources = ARRAY_SIZE(e740_t7l66xb_resources),
+ .resource = e740_t7l66xb_resources,
+};
+
+static int __init e740_t7l66xb_init(void) +{
+ if(!machine_is_e740())
+ return -ENODEV;
+
+ platform_device_register(&e740_t7l66xb_device);
+ return 0;
+}
+
+module_init(e740_t7l66xb_init);
+
+MODULE_AUTHOR("Ian Molton <[email protected]>"); +MODULE_DESCRIPTION("e740 t7l66xb device support"); +MODULE_LICENSE("GPLv2");
+
+
(quoted diff) +0 -0
b/arch/arm/mach-pxa/e750_tc6393xb.c
new file mode 100644
index 0000000..c204e2f
arch/arm/mach-pxa/e750_tc6393xb.c new file +5 -0
@@ -0,0 +1,109 @@ +/* +* +* Copyright (C) Ian Molton. +* +* 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-types.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/pxa-regs.h>
+#include <asm/irq.h>
+
+#include <asm/arch/eseries-irq.h>
+#include <asm/arch/eseries-gpio.h>
+
+#include <linux/tc6393.h>
+
+static struct resource e750_tc6393xb_resources[] = {
+ [0] = {
+ .start = PXA_CS4_PHYS,
+ .end = PXA_CS4_PHYS + 0x1fffff,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = IRQ_GPIO(GPIO_ESERIES_TMIO_IRQ),
+ .end = IRQ_GPIO(GPIO_ESERIES_TMIO_IRQ),
+ .flags = IORESOURCE_IRQ,+ },
+
+};
+
+/* FIXME - who should really be setting up the clock? bootloader or kernel ? */
+static void e750_tc6393xb_hwinit(void) {
+ pxa_gpio_mode(GPIO11_3_6MHz_MD);
+
+ GPCR(45) = GPIO_bit(45); /* #SUSPEND low */
+ GPCR(19) = GPIO_bit(19); /* #PCLR low (reset) */
+ mdelay(1);
+ GPSR(45) = GPIO_bit(45); /* #SUSPEND high */
+ mdelay(10); /* FIXME - probably 1000x too long */
+ GPSR(19) = GPIO_bit(19); /* #PCLR high */
+}
+
+static void e750_tc6393xb_suspend(void) {
+ GPSR(45) = GPIO_bit(45); /* #SUSPEND high */
+ mdelay(10);
+ GPSR(19) = GPIO_bit(19); /* #PCLR high */
+
+ pxa_gpio_mode(GPIO11_3_6MHz_MD|GPIO_OUT);
+ GPSR0 = GPIO_bit(GPIO11_3_6MHz);
+}
+
+/*
+MCR : 80aa
+CCR: 1310
+PLL2CR: 0c01
+PLL1CR1: f743
+PLL1CR2: 00f2
+SYS_DCR: 1033
+*/
+
+static struct tc6393xb_platform_data e750_tc6393xb_info = {
+ .irq_base = IRQ_BOARD_START,
+ .sys_pll2cr = 0x0cc1,
+ .sys_ccr = 0x1310,
+ .sys_mcr = 0x80aa,
+ .sys_gper = 0,
+ .sys_gpodsr1 = 0,
+ .sys_gpooecr1 = 0,
+ .hw_init = &e750_tc6393xb_hwinit,
+ .suspend = &e750_tc6393xb_suspend,
+};
+
+static struct platform_device e750_tc6393xb_device = {
+ .name = "tc6393xb",
+ .id = -1,
+ .dev = {+ .platform_data = &e750_tc6393xb_info,
+ },
+ .num_resources = ARRAY_SIZE(e750_tc6393xb_resources),
+ .resource = e750_tc6393xb_resources,
+};
+
+static int __init e750_tc6393xb_init(void) +{
+ if(!(machine_is_e750() || machine_is_e800()))
+ return -ENODEV;
+
+ platform_device_register(&e750_tc6393xb_device);
+ return 0;
+}
+
+module_init(e750_tc6393xb_init);
+
+MODULE_AUTHOR("Ian Molton <[email protected]>"); +MODULE_DESCRIPTION("e740 tc6393 device support"); +MODULE_LICENSE("GPLv2");
+
+
Signature · 1 line
1.5.3.5.737.gdee1b
Re: [UPDATED PATCH] Support for Toshiba TMIO multifunction devices
Quoted 6 lines · ian
Quoted 3 lines · Russell King - ARM Linux
On Thu, Nov 22, 2007 at 12:34:09AM +0000, ian wrote:
Unfortunately, this is broken as designed (in fact this whole file is.)
Fix attached below.
Thanks.
Quoted 5 lines
(quoted diff) +4
-0+ /* Allocate space for the subdevice resources temporarily so + that we can modify them */ + + res = kzalloc(blk->num_resources * sizeof (structresource),
GFP_KERNEL);
Looks like your mailer wrapped the patches.
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (!res) + goto fail;
Plus weird indentation.
Review
Quoted 13 lines
(quoted diff) +13
-0+ platform_device_add_resources(sdev, res, blk->num_resources); + kfree(res); + + if (platform_device_add(sdev)) { + goto fail; + } + + printk(KERN_INFO "MFD: registering %s\n", blk->name); + } + return devices; + +fail: + mfd_free_devices(devices, i + 1);
If 'sdev' failed to register, you don't want to call platform_device_del() on it (via platform_device_unregister()) since it's not been registered into the device tree.
The removal procedure for platform devices is:
if platform_device_alloc() fails, return -ENOMEM.
if platform_device_add() fails, call platform_device_put() on it. if platform_device_add() suceeds, call either
platform_device_del() + platform_device_put() _or_ platform_device_unregister()
include/linux/ioport.h struct resource_list { · line 62
Quoted 11 lines
include/linux/ioport.h +3
-0@@ -57,6 +57,9 @@ struct resource_list {#define IORESOURCE_IRQ_LOWLEVEL (1<<3)#define IORESOURCE_IRQ_SHAREABLE (1<<4) +/* MFD device IRQ specific bits (IORESOURCE_BITS) */ +#define IORESOURCE_IRQ_MFD_SUBDEVICE (1<<5) +
Something others (== mainline) needs to see first.
Quoted 14 lines
drivers/mfd/t7l66xb.c new file +8
-0@@ -0,0 +1,286 @@ +/* +* drivers/mfd/t7l66xb.c +* +* Toshiba T7L66XB support +* Copyright (c) 2005 Ian Molton +* +* 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
drivers/mfd/t7l66xb.c line 18
Quoted 10 lines
drivers/mfd/t7l66xb.c new file +10
-0+* published by the Free Software Foundation. +* +* This is based on my and Dirk Opfers work on the tc6393xb SoC. +* +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/delay.h>
Do you need linux/delay.h ?
drivers/mfd/t7l66xb.c line 21
Quoted 3 lines
drivers/mfd/t7l66xb.c new file +3
-0+#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/irq.h>
Do you need linux/irq.h ?
drivers/mfd/t7l66xb.c line 27
Quoted 6 lines
drivers/mfd/t7l66xb.c new file +6
-0+#include <linux/device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <asm/hardware.h> +#include <asm/mach-types.h>
Doesn't use machine types in this file.
drivers/mfd/t7l66xb.c line 35
Quoted 8 lines
drivers/mfd/t7l66xb.c new file +8
-0+#include <asm/io.h> +#include <asm/arch/pxa-regs.h> + +#include <linux/t7l66xb.h> +#include <linux/tmio_mmc.h> +#include "mfd-core.h" + +#define platform_get_platdata (_dev) ((_dev)->dev.platform_data)
Should this be something generic? Eg in platform_device.h ?
drivers/mfd/t7l66xb.c line 37
Quoted 2 lines
drivers/mfd/t7l66xb.c new file +2
-0+while ( (req = (readb (data->mapbase +T7L66XB_SYS_ISR ) + & ~(readb (data->mapbase +T7L66XB_SYS_IMR )))) ) {
Do the additional parens buy anything here (other than more line noise) ?
drivers/mfd/t7l66xb.c line 41
Quoted 4 lines
drivers/mfd/t7l66xb.c new file +4
-0+for (i =0 ; i <=7 ; i++) { +int dev_irq = data->irq_base + i; +struct irq_desc *d =NULL ; +if ((req & (1 <<i))) {
Ditto.
drivers/mfd/t7l66xb.c line 43
Quoted 2 lines
drivers/mfd/t7l66xb.c new file +2
-0+set_irq_data (tchip->irq_nr, tchip); +set_irq_chained_handler (tchip->irq_nr, t7l66xb_irq_handler);
Weird indentation.
Quoted 43 lines
drivers/mfd/t7l66xb.c new file +15
-0+set_irq_type (tchip->irq_nr,IRQT_FALLING ); +} + +static void t7l66xb_hwinit (struct platform_device *dev) +{ +struct t7l66xb_platform_data *pdata =platform_get_platdata (dev); + +if (pdata && pdata->hw_init) + pdata->hw_init (); +} + + +#ifdef CONFIG_PM + +static int t7l66xb_suspend(struct platform_device *dev, pm_message_tstate)
drivers/mfd/t7l66xb.c new file +27
-0+{ +struct t7l66xb_platform_data *pdata =platform_get_platdata (dev); + + +if (pdata && pdata->suspend) + pdata->suspend (); + +return 0 ; +} + +static int t7l66xb_resume (struct platform_device *dev) +{ +struct t7l66xb_platform_data *pdata =platform_get_platdata (dev); + +if (pdata && pdata->resume) + pdata->resume (); + +t7l66xb_hwinit (dev); + +return 0 ; +} +#endif + +static int t7l66xb_probe (struct platform_device *dev) +{ +struct t7l66xb_platform_data *pdata = dev->dev.platform_data; +unsigned long pbase = (unsigned long )dev->resource[0 ].start;
drivers/mfd/t7l66xb.c
Quoted 10 lines
drivers/mfd/t7l66xb.c new file +10
-0+unsigned long plen = dev->resource[0 ].end - dev->resource[0 ].start; +int err = -ENOMEM ; +struct t7l66xb_data *data; + + data =kmalloc (sizeof (struct t7l66xb_data ),GFP_KERNEL ); +if (!data) + goto out; + + data->irq_base = pdata->irq_base; + data->irq_nr = dev->resource[1 ].start;
platform_get_irq() ?
drivers/mfd/t7l66xb.c
Quoted 3 lines
drivers/mfd/t7l66xb.c new file +3
-0+ +if (!data->irq_base) { +printk ("t7166xb: uninitialized irq_base!\n" );
KERN_ERR ?
Quoted 12 lines
drivers/mfd/t7l66xb.c new file +12
-0+ goto out_free_data; + } + + data->mapbase =ioremap (pbase, plen); +if (!data->mapbase) + goto out_free_irqs; + +platform_set_drvdata (dev, data); +t7l66xb_setup_irq (data); +t7l66xb_hwinit (dev); + +/* Mask IRQs -- should we mask/unmask on suspend/resume? */
drivers/mfd/t7l66xb.c
Quoted 10 lines
drivers/mfd/t7l66xb.c new file +10
-0+writew (0xbf , data->mapbase +T7L66XB_SYS_IMR ); + +printk (KERN_INFO "%s rev %d @ 0x%08lx using irq %d-%d on irq %d\n" , + dev->name,readw (data->mapbase +T7L66XB_SYS_RIDR ), + (unsigned long )data->mapbase, data->irq_base, + data->irq_base +T7L66XB_NR_IRQS -1 , data->irq_nr); + + data->devices =mfd_add_devices (dev, t7l66xb_devices, +ARRAY_SIZE (t7l66xb_devices), + &dev->resource[0 ],0 , data->irq_base);
dev->resource === &dev->resource[0]
drivers/mfd/t7l66xb.c
Quoted 2 lines
drivers/mfd/t7l66xb.c new file +2
-0+ +if (!data->devices){
should be:
if (!data->devices) {drivers/mfd/t7l66xb.c
Quoted 1 line
drivers/mfd/t7l66xb.c new file +1
-0+ printk(KERN_INFO "%s: Failed to allocate devices!\n",
KERN_ERR ?
Quoted 12 lines
drivers/mfd/t7l66xb.c new file +12
-0+ dev->name); + goto out_free_devices; + } + +return 0 ; + +out_free_devices: +mfd_free_devices (data->devices,ARRAY_SIZE (t7l66xb_devices)); +out_free_irqs: +out_free_data: +kfree (data); +out:
drivers/mfd/t7l66xb.c
Quoted 10 lines
drivers/mfd/t7l66xb.c new file +10
-0+return err; +} + +static int t7l66xb_remove (struct platform_device *dev) +{ +struct t7l66xb_data *tchip =platform_get_drvdata (dev); +int i; + +/* Free subdevices */ +mfd_free_devices (tchip->devices,ARRAY_SIZE (t7l66xb_devices));
Weird indentation.
drivers/mfd/t7l66xb.c
Quoted 8 lines
drivers/mfd/t7l66xb.c new file +8
-0+ +/* Take down IRQ handling */ +for (i =0 ; i <T7L66XB_NR_IRQS ; i++) { +int irq = i + tchip->irq_base; +set_irq_handler (irq,NULL ); +set_irq_chip (irq,NULL ); +set_irq_chip_data (irq,NULL ); + }
Ditto for most of the above lines.
drivers/mfd/t7l66xb.c
Quoted 6 lines
drivers/mfd/t7l66xb.c new file +6
-0+static int __init t7l66xb_init (void ) +{ +int retval =0 ; + + retval =platform_driver_register (&t7l66xb_platform_driver); +return retval;
Looks like a long winded way of writing
return platform_driver_register (&t7l66xb_platform_driver);
Quoted 12 lines
drivers/mfd/tc6387xb.c new file +6
-0@@ -0,0 +1,143 @@ +/* +* drivers/mfd/tc6387xb.c +* +* Toshiba TC6387XB support +* Copyright (c) 2005 Ian Molton +*
drivers/mfd/tc6387xb.c line 16
Quoted 10 lines
drivers/mfd/tc6387xb.c new file +10
-0+* 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. +* +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/delay.h>
Delays not used?
drivers/mfd/tc6387xb.c line 24
Quoted 8 lines
drivers/mfd/tc6387xb.c new file +8
-0+#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <asm/hardware.h> +#include <asm/mach-types.h>
No machine types used?
Quoted 16 lines
drivers/mfd/tc6393xb.c new file +10
-0@@ -0,0 +1,369 @@ +/* +* drivers/mfd/tc6393xb.c +* +* Toshiba TC6393 support +* Copyright (c) 2005 Dirk Opfer +* Copyright (c) 2005 Ian Molton <[email protected]> +* +* Based on code written by Sharp/Lineo for 2.4 kernels +* Based on locomo.c +*
drivers/mfd/tc6393xb.c line 20
Quoted 10 lines
drivers/mfd/tc6393xb.c new file +10
-0+* 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. +* +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/delay.h>
linux/delay.h not used?
drivers/mfd/tc6393xb.c line 23
Quoted 3 lines
drivers/mfd/tc6393xb.c new file +3
-0+#include <linux/errno.h> +#include <linux/ioport.h> +#include <linux/irq.h>
linux/irq.h not used?
drivers/mfd/tc6393xb.c line 29
Quoted 6 lines
drivers/mfd/tc6393xb.c new file +6
-0+#include <linux/device.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#include <asm/hardware.h> +#include <asm/mach-types.h>
machine types not used?
drivers/mfd/tc6393xb.c line 39
Quoted 10 lines
drivers/mfd/tc6393xb.c new file +10
-0+#include <asm/io.h> +#include <asm/arch/pxa-regs.h> + +#include <linux/tmio_mmc.h> +#include <linux/tmio_nand.h> +#include <linux/tmio_ohci.h> +#include <linux/tc6393.h> +#include "mfd-core.h" + +#define platform_get_platdata (_dev) ((_dev)->dev.platform_data)
should be in linux/platform_device.h ?
drivers/mfd/tc6393xb.c line 41
Quoted 2 lines
drivers/mfd/tc6393xb.c new file +2
-0+static struct tmio_mmc_hwconfig tc6393xb_mmc_hwconfig = { + .set_mmc_clock = tc6393xb_mmc_set_clock,
Indentation?
drivers/mfd/tc6393xb.c line 43
Quoted 2 lines
drivers/mfd/tc6393xb.c new file +2
-0+while ( (req = (readb (data->mapbase +TC6393_SYS_ISR ) + & ~(readb (data->mapbase +TC6393_SYS_IMR )))) ) {
Revenge of the parens.
drivers/mfd/tc6393xb.c line 47
Quoted 4 lines
drivers/mfd/tc6393xb.c new file +4
-0+for (i =0 ; i <=7 ; i++) { +int dev_irq = data->irq_base + i; +struct irq_desc *d =NULL ; +if ((req & (1 <<i))) {
Parens strike back.
drivers/mfd/tc6393xb.c line 48
Quoted 1 line
drivers/mfd/tc6393xb.c new file +1
-0+if (i !=1 )printk ("IRQ! from %d\n" , i);
But strange spacing moves in for the kill.
drivers/mfd/tc6393xb.c line 49
Quoted 1 line
drivers/mfd/tc6393xb.c new file +1
-0+if (!pdata || !tchip){
And again. Life points reducing.
drivers/mfd/tc6393xb.c line 51
Quoted 2 lines
drivers/mfd/tc6393xb.c new file +2
-0+ data->irq_base = pdata->irq_base; + data->irq_nr = dev->resource[1 ].start;
platform_get_irq() ?
drivers/mfd/tc6393xb.c line 54
Quoted 3 lines
drivers/mfd/tc6393xb.c new file +3
-0+ +if (!data->irq_base) { +printk ("tc6393xb: uninitialized irq_base!\n" );
KERN_ERR ?
drivers/mfd/tc6393xb.c line 59
Quoted 5 lines
drivers/mfd/tc6393xb.c new file +5
-0+ goto out_free_data; + } + + data->mapbase =ioremap (pbase, plen); +if (!data->mapbase)
Spacing.
Quoted 8 lines
drivers/mfd/tc6393xb.c new file +8
-0+ goto out_free_irqs; + +platform_set_drvdata (dev, data); +tc6393xb_setup_irq (data); +tc6393xb_hwinit (dev); + +/* Enable (but mask!) our IRQs */ +writew (0 , data->mapbase +TC6393_SYS_IRR );
drivers/mfd/tc6393xb.c line 77
Quoted 10 lines
drivers/mfd/tc6393xb.c new file +10
-0+writew (0xbf , data->mapbase +TC6393_SYS_IMR ); + +printk (KERN_INFO "%s rev %d @ 0x%08lx using irq %d-%d on irq %d\n" , + dev->name,readw (data->mapbase +TC6393_SYS_RIDR ), + (unsigned long )data->mapbase, data->irq_base, + data->irq_base +TC6393XB_NR_IRQS -1 , data->irq_nr); + + data->devices =mfd_add_devices (dev, tc6393xb_devices, +ARRAY_SIZE (tc6393xb_devices), + &dev->resource[0 ],0 , data->irq_base);
dev->resource === &dev->resource[0]
drivers/mfd/tc6393xb.c line 79
Quoted 2 lines
drivers/mfd/tc6393xb.c new file +2
-0+ +if (!data->devices) {
Spacing.
drivers/mfd/tc6393xb.c line 80
Quoted 1 line
drivers/mfd/tc6393xb.c new file +1
-0+ printk(KERN_INFO "%s: Failed to allocate devices!\n",
KERN_ERR.
drivers/mfd/tc6393xb.c line 87
Quoted 7 lines
drivers/mfd/tc6393xb.c new file +7
-0+static int tc6393xb_remove (struct platform_device *dev) +{ +struct tc6393xb_data *tchip =platform_get_drvdata (dev); +int i; + +/* Free subdevices */ +mfd_free_devices (tchip->devices,ARRAY_SIZE (tc6393xb_devices));
Indentation.
drivers/mfd/tc6393xb.c line 100
Quoted 13 lines
drivers/mfd/tc6393xb.c new file +13
-0+ +/* Take down IRQ handling */ +for (i =0 ; i <TC6393XB_NR_IRQS ; i++) { +int irq = i + tchip->irq_base; +set_irq_handler (irq,NULL ); +set_irq_chip (irq,NULL ); +set_irq_chip_data (irq,NULL ); + } + +set_irq_chained_handler (tchip->irq_nr,NULL ); + +/* Free core resources */ +iounmap (tchip->mapbase);
Indentation.
I think by now you get the idea, so I'm not going to continue with this review. Maybe if you can fix up the above plus further instances of them in the remainder of the patch set, it can be reviewed again.
Note that leaving soo many such trivialities for a reviewer to find causes reviewer tiredness, increases the number of reviews, and decreases the motivation for reviewers to read your code.
There's a tool (scripts/checkpatch.pl) designed to aid you in identifying trivialities like the above. Please consider using it. (It won't find all of the above, and it's not always 100% correct, but that is where a review by humans come in.)
Hi Ian,
Personally I'm very appreciate your patches, they'll will help submitting HP iPaqs SOCs/MFDs, you know... ;-)
Thus, much thanks in advance.
Few comments...
Quoted 7 lines · ian
Quoted 5 lines · eric miao
Roughly went through the patch, looks good, here comes the remind, though :-)
1. is it possible to use some name other than "
soc_core", maybe "tmio_core" so that other multifunction chips sharing a core base will live easier.
Quoted 8 lines
It's (soc-core) not tmio MFD specific - its already used by other MFD chips (although obviously not ones in mainline (yet!)
it might be better named 'mfd-core' though, as thats its intended use...
Quoted 1 line
2. those C++ style comments "//" are not so pleasant...
Should I clean them up and resubmit?
I'd resubmit cleaned up version. I think four or even more resubmissions is inevitable for such patch-set (new general code + a lot of drivers).
About patches their self... I think soc_add_devices could be split into two small functions, thus you'll get rid of high indentation level + code will be more reader friendly.
Ideally, checkpatch.pl should be happy. If it will, then there will be less nitpicks somebody can pull. ;-)
Here it is:
- - - -
~/linux-2.6$ scripts/checkpatch.pl ~/0001-Reuseable-SOC-core-code-suitable-for-multifunction-c.patch
WARNING: line over 80 characters
#57: FILE: drivers/mfd/soc-core.c:37:
+#define SIGNED_SHIFT(val, shift) ((shift) >= 0 ? ((val) << (shift)) : ((val) >> -(shift)))
WARNING: line over 80 characters
#60: FILE: drivers/mfd/soc-core.c:40:
+ struct soc_device_data *soc, int nr_devs,
WARNING: line over 80 characters
#84: FILE: drivers/mfd/soc-core.c:64:
+ res = kzalloc(blk->num_resources * sizeof (struct resource), GFP_KERNEL);
WARNING: no space between function name and open parenthesis '(' #84: FILE: drivers/mfd/soc-core.c:64:
+ res = kzalloc(blk->num_resources * sizeof (struct resource), GFP_KERNEL);
ERROR: do not use C99 // comments
#89: FILE: drivers/mfd/soc-core.c:69:
+ res[r].name = blk->res[r].name; // Fixme - should copy
WARNING: braces {} are not necessary for single statement blocks #93: FILE: drivers/mfd/soc-core.c:73:
+ if (blk->res[r].flags & IORESOURCE_MEM) {
+ base = mem->start;
+ } else if ((blk->res[r].flags & IORESOURCE_IRQ) &&
WARNING: braces {} are not necessary for single statement blocks #95: FILE: drivers/mfd/soc-core.c:75:
+ } else if ((blk->res[r].flags & IORESOURCE_IRQ) &&
+ (blk->res[r].flags & IORESOURCE_IRQ_SOC_SUBDEVICE)) {
+ base = irq_base;
+ }
WARNING: line over 80 characters
#96: FILE: drivers/mfd/soc-core.c:76:
+ (blk->res[r].flags & IORESOURCE_IRQ_SOC_SUBDEVICE)) {
WARNING: line over 80 characters
#103: FILE: drivers/mfd/soc-core.c:83:
+ res[r].start = base + SIGNED_SHIFT(blk->res[r].start, relative_addr_shift);
WARNING: line over 80 characters
#104: FILE: drivers/mfd/soc-core.c:84:
+ res[r].end = base + SIGNED_SHIFT(blk->res[r].end, relative_addr_shift);
ERROR: Missing Signed-off-by: line(s)
total: 2 errors, 9 warnings, 145 lines checked
Your patch has style problems, please review. If any of these errors are false positives report them to the maintainer, see CHECKPATCH in MAINTAINERS.
- - - -
There is false positive though:
if (...) {
single_stmt;
} else {
one;
two;
}^^^ is perfectly OK and preferred, IIRC. checkpatch isn't ideal, but it's mostly good.
Quoted 3 lines
More to the point, who should I be submitting them to? the files under arm/ are obviously for RMK to peruse, but I couldnt find an entry for
drivers/mfdinMAINTAINERS...
Well, don't know about drivers/mfd/*. Probably there simply isn't any [official] maintainer, thus lkml is the right place.
There is one not so obvious thing though: you should not submit patches with To/Cc'ing lkml (open list) and linux-arm-kernel (subscribers-only).
Russell King will probably point to linux-arm-kernel etiquette article
(http://www.arm.linux.org.uk/mailinglists/etiquette.php
"Cross-posting between linux-arm* lists and other lists.")
So, either place linux-arm-kernel into Bcc:, or duplicate stuff for lkml and linux-arm-kernel separately, thus they'll not see each others' To/Cc.
Looking forward to your patches!
Signature · 4 lines
Anton Vorontsov email: [email protected] backup email: [email protected] irc://irc.freenode.net/bd2