diff mbox

[v5,2/3] arm:davinci: move emif driver to mfd framework

Message ID 1330005504-25321-3-git-send-email-prakash.pm@ti.com
State Not Applicable
Headers show

Commit Message

Manjunathappa, Prakash Feb. 23, 2012, 1:58 p.m. UTC
Move emif handling code from platform folder to multi functional
devices frame work. emif MFD driver adds "davinci_nand" and
"physmap-flash" as slave devices.

Signed-off-by: Manjunathappa, Prakash <prakash.pm@ti.com>
---
Since v4:
Fix updating NAND/NOR platfrom data.
Since v3:
No change. Resending as 3/3 in patch changed.
Since v2:
Modified emif MFD driver to load multiple instance of NAND/NOR devices.
Since v1:
Patch generated using -M option.
 arch/arm/Kconfig                  |    1 +
 arch/arm/mach-davinci/Makefile    |    2 +-
 arch/arm/mach-davinci/aemif.c     |  133 ------------------------
 drivers/mfd/Makefile              |    1 +
 drivers/mfd/davinci_aemif.c       |  206 +++++++++++++++++++++++++++++++++++++
 include/linux/mfd/davinci_aemif.h |   14 +++
 6 files changed, 223 insertions(+), 134 deletions(-)
 delete mode 100644 arch/arm/mach-davinci/aemif.c
 create mode 100644 drivers/mfd/davinci_aemif.c

Comments

Samuel Ortiz Feb. 27, 2012, 2:26 p.m. UTC | #1
Hi Prakash,

On Thu, Feb 23, 2012 at 07:28:23PM +0530, Manjunathappa, Prakash wrote:
> +static int __init davinci_aemif_probe(struct platform_device *pdev)
> +{
> +	struct davinci_aemif_devices *davinci_aemif_devices =
> +		pdev->dev.platform_data;
> +	struct platform_device *devices;
> +	struct mfd_cell *cells;
> +	int i, ret, count;
> +
> +	devices = davinci_aemif_devices->devices;
> +
> +	cells = kzalloc(sizeof(struct mfd_cell) *
> +			davinci_aemif_devices->num_devices, GFP_KERNEL);
> +
> +	for (i = 0, count = 0; i < davinci_aemif_devices->num_devices; i++) {
> +		if (!strcmp(devices[i].name, "davinci_nand")) {
> +			cells[count].pdata_size =
> +				sizeof(struct davinci_nand_pdata);
> +		} else if (!strcmp(devices[i].name, "physmap-flash")) {
> +			cells[count].pdata_size =
> +				sizeof(struct physmap_flash_data);
> +		} else
> +			continue;
> +
> +		cells[count].name = devices[i].name;
> +		cells[count].platform_data =
> +			devices[i].dev.platform_data;
> +		cells[count].id = devices[i].id;
> +		cells[count].resources = devices[i].resource;
> +		cells[count].num_resources = devices[i].num_resources;
> +		count++;
> +	}
So it seems you're passing a platform devices array through your mfd aemif
platform data pointer. And from what I can see, it's mostly a 1 entry array
(for the NAND case) or a 2 entries array (for the NAND and NOR case).
In that case, adding an MFD driver in the middle brings basically nothing but
confusion and overhead (and 200+ lines of code).
So unless someone explains to me how this is doing any good to the kernel in
general, I'm not going to take this patchset.

Cheers,
Samuel.
Manjunathappa, Prakash Feb. 28, 2012, 5:44 a.m. UTC | #2
Hi Samuel,

On Mon, Feb 27, 2012 at 19:56:38, Samuel Ortiz wrote:
[snip]
> So it seems you're passing a platform devices array through your mfd aemif
> platform data pointer. And from what I can see, it's mostly a 1 entry array
> (for the NAND case) or a 2 entries array (for the NAND and NOR case).
> In that case, adding an MFD driver in the middle brings basically nothing but
> confusion and overhead (and 200+ lines of code).
> So unless someone explains to me how this is doing any good to the kernel in
> general, I'm not going to take this patchset.
> 
> Cheers,
> Samuel.
>

In this way we trying to isolate future modification of aemif driver not to depict
as platform code change, the need for this is based on discussion in below thread
http://davinci-linux-open-source.1494791.n2.nabble.com/PATCH-arm-davinci-configure-davinci-aemif-chipselects-through-OF-tt7059739.html#none

Earlier also concern was expressed to move aemif driver out of arch/arm to drivers folder.
Here is the link for the same: http://lists.infradead.org/pipermail/linux-mtd/2011-August/037308.html
Since aemif driver supports NAND/NOR devices, we feel MFD is the place holder.

Thanks,
Prakash
Manjunathappa, Prakash Feb. 29, 2012, 7:01 a.m. UTC | #3
Hi Samuel,

On Tue, Feb 28, 2012 at 11:14:39, Manjunathappa, Prakash wrote:
> Hi Samuel,
> 
> On Mon, Feb 27, 2012 at 19:56:38, Samuel Ortiz wrote:
> [snip]
> > So it seems you're passing a platform devices array through your mfd aemif
> > platform data pointer. And from what I can see, it's mostly a 1 entry array
> > (for the NAND case) or a 2 entries array (for the NAND and NOR case).
> > In that case, adding an MFD driver in the middle brings basically nothing but
> > confusion and overhead (and 200+ lines of code).
> > So unless someone explains to me how this is doing any good to the kernel in
> > general, I'm not going to take this patchset.
> > 
> > Cheers,
> > Samuel.
> >
> 
> In this way we trying to isolate future modification of aemif driver not to depict
> as platform code change, the need for this is based on discussion in below thread
> http://davinci-linux-open-source.1494791.n2.nabble.com/PATCH-arm-davinci-configure-davinci-aemif-chipselects-through-OF-tt7059739.html#none
> 
> Earlier also concern was expressed to move aemif driver out of arch/arm to drivers folder.
> Here is the link for the same: http://lists.infradead.org/pipermail/linux-mtd/2011-August/037308.html
> Since aemif driver supports NAND/NOR devices, we feel MFD is the place holder.
> 
> Thanks,
> Prakash

Some more information which I missed out earlier:
DaVinci AEMIF is an async memory interface, driver for which was implemented in
arch/arm/mach-davinci/aemif.c. It can be interfaced to NAND, NOR and other
asynchronous memories. Currently it just provides an API for timing value
configuration. The API is invoked by the MTD NAND driver. 

Specification here: http://www.ti.com/lit/ug/sprue20c/sprue20c.pdf

Reason for moving it to MFD frame work:
1) AEMIF is also present on devices belonging to arch-c6x and arch-omap2 architectures.
   So having the driver in architecture neutral place seems to be the way to go.
2) Other than NAND/NOR there are use cases where people are interfacing other devices,
   for eg: FPGAs through UIO framework. So MTD is not the place for AEMIF driver.

Taking above points into consideration Arnd Bergmann suggested to move AEMIF driver to
MFD framework. Here is the link:
http://lists.ozlabs.org/pipermail/devicetree-discuss/2011-December/010295.html

Thanks,
Prakash
Samuel Ortiz March 1, 2012, 11:23 a.m. UTC | #4
Hi Prakash,

On Tue, Feb 28, 2012 at 05:44:39AM +0000, Manjunathappa, Prakash wrote:
> Hi Samuel,
> 
> On Mon, Feb 27, 2012 at 19:56:38, Samuel Ortiz wrote:
> [snip]
> > So it seems you're passing a platform devices array through your mfd aemif
> > platform data pointer. And from what I can see, it's mostly a 1 entry array
> > (for the NAND case) or a 2 entries array (for the NAND and NOR case).
> > In that case, adding an MFD driver in the middle brings basically nothing but
> > confusion and overhead (and 200+ lines of code).
> > So unless someone explains to me how this is doing any good to the kernel in
> > general, I'm not going to take this patchset.
> > 
> > Cheers,
> > Samuel.
> >
> 
> In this way we trying to isolate future modification of aemif driver not to depict
> as platform code change, the need for this is based on discussion in below thread
> http://davinci-linux-open-source.1494791.n2.nabble.com/PATCH-arm-davinci-configure-davinci-aemif-chipselects-through-OF-tt7059739.html#none
> 
I fail to see how you're going to achieve that with adding an MFD platform
device registration in the middle.


> Earlier also concern was expressed to move aemif driver out of arch/arm to drivers folder.
> Here is the link for the same: http://lists.infradead.org/pipermail/linux-mtd/2011-August/037308.html
> Since aemif driver supports NAND/NOR devices, we feel MFD is the place holder.
I would disagree with that. And it certainly makes sense to move many drivers
out of arch/arm into a more appropriate place but I'd like to keep mfd as
something else than yet another misc.

Cheers,
Samuel.
Manjunathappa, Prakash March 6, 2012, 1:12 p.m. UTC | #5
Hi Samuel,

May be I did not do a good job giving complete information on this earlier.
So I have replied on top of my mail with below information (seems you missed it)

More information on AEMIF:
DaVinci AEMIF is an async memory interface, driver for which was implemented
in arch/arm/mach-davinci/aemif.c. It can be interfaced to NAND, NOR and other
asynchronous memories. Currently it just provides an API for timing value configuration.
The API is invoked by the MTD NAND driver. 

Specification here: http://www.ti.com/lit/ug/sprue20c/sprue20c.pdf

Reason for moving it to MFD frame work:
1) AEMIF is also present on devices belonging to arch-c6x and arch-omap2 architectures.
   So having the driver in architecture neutral place seems to be the way to go.
2) Other than NAND/NOR there are use cases where people are interfacing other devices,
   for eg: FPGAs through UIO framework. So MTD is not the place for AEMIF driver.

Taking above points into consideration Arnd Bergmann suggested to move AEMIF driver to
MFD framework [1], relevant portion of his mail as follows,

" If you want it to provide endpoint devices that are handled by
distinct subsystems in Linux, I would make it an mfd multifunction
device and make the common... "

[1] http://lists.ozlabs.org/pipermail/devicetree-discuss/2011-December/010295.html

Thanks,
Prakash

On Thu, Mar 01, 2012 at 16:53:29, Samuel Ortiz wrote:
> Hi Prakash,
> 
> On Tue, Feb 28, 2012 at 05:44:39AM +0000, Manjunathappa, Prakash wrote:
> > Hi Samuel,
> > 
> > On Mon, Feb 27, 2012 at 19:56:38, Samuel Ortiz wrote:
> > [snip]
> > > So it seems you're passing a platform devices array through your mfd aemif
> > > platform data pointer. And from what I can see, it's mostly a 1 entry array
> > > (for the NAND case) or a 2 entries array (for the NAND and NOR case).
> > > In that case, adding an MFD driver in the middle brings basically nothing but
> > > confusion and overhead (and 200+ lines of code).
> > > So unless someone explains to me how this is doing any good to the kernel in
> > > general, I'm not going to take this patchset.
> > > 
> > > Cheers,
> > > Samuel.
> > >
> > 
> > In this way we trying to isolate future modification of aemif driver not to depict
> > as platform code change, the need for this is based on discussion in below thread
> > http://davinci-linux-open-source.1494791.n2.nabble.com/PATCH-arm-davinci-configure-davinci-aemif-chipselects-through-OF-tt7059739.html#none
> > 
> I fail to see how you're going to achieve that with adding an MFD platform
> device registration in the middle.
> 
> 
> > Earlier also concern was expressed to move aemif driver out of arch/arm to drivers folder.
> > Here is the link for the same: http://lists.infradead.org/pipermail/linux-mtd/2011-August/037308.html
> > Since aemif driver supports NAND/NOR devices, we feel MFD is the place holder.
> I would disagree with that. And it certainly makes sense to move many drivers
> out of arch/arm into a more appropriate place but I'd like to keep mfd as
> something else than yet another misc.
> 
> Cheers,
> Samuel.
> 
> -- 
> Intel Open Source Technology Centre
> http://oss.intel.com/
>
Samuel Ortiz March 16, 2012, 5:29 p.m. UTC | #6
Hi Prakash,

On Tue, Mar 06, 2012 at 01:12:25PM +0000, Manjunathappa, Prakash wrote:
> Hi Samuel,
> 
> May be I did not do a good job giving complete information on this earlier.
> So I have replied on top of my mail with below information (seems you missed it)
> 
I did get it, sorry for not being able to reply earlier.


> Taking above points into consideration Arnd Bergmann suggested to move AEMIF driver to
> MFD framework [1], relevant portion of his mail as follows,
> 
> " If you want it to provide endpoint devices that are handled by
> distinct subsystems in Linux, I would make it an mfd multifunction
> device and make the common... "
You're missing that part of the quote:

"...code a driver that scans the connected memories in order to register
its child devices for each of the subsystems."

If you can do that, then I'd take that patch to the MFD subsystem. Otherwise,
the only incentive for me to take it would be to be able to share the 2
functions currently in this driver between several archs. But MFD would just
be a placeholder for this driver right now.

Cheers,
Samuel.
Manjunathappa, Prakash March 20, 2012, 12:57 p.m. UTC | #7
Hi Samuel,

On Fri, Mar 16, 2012 at 22:59:08, Samuel Ortiz wrote:
> Hi Prakash,
> 
> On Tue, Mar 06, 2012 at 01:12:25PM +0000, Manjunathappa, Prakash wrote:
> > Hi Samuel,
> > 
> > May be I did not do a good job giving complete information on this earlier.
> > So I have replied on top of my mail with below information (seems you missed it)
> > 
> I did get it, sorry for not being able to reply earlier.
> 
> 
> > Taking above points into consideration Arnd Bergmann suggested to move AEMIF driver to
> > MFD framework [1], relevant portion of his mail as follows,
> > 
> > " If you want it to provide endpoint devices that are handled by
> > distinct subsystems in Linux, I would make it an mfd multifunction
> > device and make the common... "
> You're missing that part of the quote:
> 
> "...code a driver that scans the connected memories in order to register
> its child devices for each of the subsystems."
> 

emif is not discoverable interface, does not support scanning for connected devices.

Could you please let me know if there is any other way to achieve this? I will be happy
to incorporate that and submit next version of the patch.

Thanks,
Prakash
diff mbox

Patch

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index a48aecc..09dcb94 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -934,6 +934,7 @@  config ARCH_DAVINCI
 	select GENERIC_ALLOCATOR
 	select GENERIC_IRQ_CHIP
 	select ARCH_HAS_HOLES_MEMORYMODEL
+	select MFD_CORE
 	help
 	  Support for TI's DaVinci platform.
 
diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile
index 2db78bd..8bab47c 100644
--- a/arch/arm/mach-davinci/Makefile
+++ b/arch/arm/mach-davinci/Makefile
@@ -5,7 +5,7 @@ 
 
 # Common objects
 obj-y 			:= time.o clock.o serial.o psc.o \
-			   dma.o usb.o common.o sram.o aemif.o
+			   dma.o usb.o common.o sram.o
 
 obj-$(CONFIG_DAVINCI_MUX)		+= mux.o
 
diff --git a/arch/arm/mach-davinci/aemif.c b/arch/arm/mach-davinci/aemif.c
deleted file mode 100644
index b67c115..0000000
--- a/arch/arm/mach-davinci/aemif.c
+++ /dev/null
@@ -1,133 +0,0 @@ 
-/*
- * AEMIF support for DaVinci SoCs
- *
- * Copyright (C) 2010 Texas Instruments Incorporated. http://www.ti.com/
- *
- * 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/kernel.h>
-#include <linux/io.h>
-#include <linux/err.h>
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/time.h>
-
-#include <linux/mfd/davinci_aemif.h>
-
-/* Timing value configuration */
-
-#define TA(x)		((x) << 2)
-#define RHOLD(x)	((x) << 4)
-#define RSTROBE(x)	((x) << 7)
-#define RSETUP(x)	((x) << 13)
-#define WHOLD(x)	((x) << 17)
-#define WSTROBE(x)	((x) << 20)
-#define WSETUP(x)	((x) << 26)
-
-#define TA_MAX		0x3
-#define RHOLD_MAX	0x7
-#define RSTROBE_MAX	0x3f
-#define RSETUP_MAX	0xf
-#define WHOLD_MAX	0x7
-#define WSTROBE_MAX	0x3f
-#define WSETUP_MAX	0xf
-
-#define TIMING_MASK	(TA(TA_MAX) | \
-				RHOLD(RHOLD_MAX) | \
-				RSTROBE(RSTROBE_MAX) |	\
-				RSETUP(RSETUP_MAX) | \
-				WHOLD(WHOLD_MAX) | \
-				WSTROBE(WSTROBE_MAX) | \
-				WSETUP(WSETUP_MAX))
-
-/*
- * aemif_calc_rate - calculate timing data.
- * @wanted: The cycle time needed in nanoseconds.
- * @clk: The input clock rate in kHz.
- * @max: The maximum divider value that can be programmed.
- *
- * On success, returns the calculated timing value minus 1 for easy
- * programming into AEMIF timing registers, else negative errno.
- */
-static int aemif_calc_rate(int wanted, unsigned long clk, int max)
-{
-	int result;
-
-	result = DIV_ROUND_UP((wanted * clk), NSEC_PER_MSEC) - 1;
-
-	pr_debug("%s: result %d from %ld, %d\n", __func__, result, clk, wanted);
-
-	/* It is generally OK to have a more relaxed timing than requested... */
-	if (result < 0)
-		result = 0;
-
-	/* ... But configuring tighter timings is not an option. */
-	else if (result > max)
-		result = -EINVAL;
-
-	return result;
-}
-
-/**
- * davinci_aemif_setup_timing - setup timing values for a given AEMIF interface
- * @t: timing values to be progammed
- * @base: The virtual base address of the AEMIF interface
- * @cs: chip-select to program the timing values for
- *
- * This function programs the given timing values (in real clock) into the
- * AEMIF registers taking the AEMIF clock into account.
- *
- * This function does not use any locking while programming the AEMIF
- * because it is expected that there is only one user of a given
- * chip-select.
- *
- * Returns 0 on success, else negative errno.
- */
-int davinci_aemif_setup_timing(struct davinci_aemif_timing *t,
-					void __iomem *base, unsigned cs)
-{
-	unsigned set, val;
-	int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup;
-	unsigned offset = A1CR_OFFSET + cs * 4;
-	struct clk *aemif_clk;
-	unsigned long clkrate;
-
-	if (!t)
-		return 0;	/* Nothing to do */
-
-	aemif_clk = clk_get(NULL, "aemif");
-	if (IS_ERR(aemif_clk))
-		return PTR_ERR(aemif_clk);
-
-	clkrate = clk_get_rate(aemif_clk);
-
-	clkrate /= 1000;	/* turn clock into kHz for ease of use */
-
-	ta	= aemif_calc_rate(t->ta, clkrate, TA_MAX);
-	rhold	= aemif_calc_rate(t->rhold, clkrate, RHOLD_MAX);
-	rstrobe	= aemif_calc_rate(t->rstrobe, clkrate, RSTROBE_MAX);
-	rsetup	= aemif_calc_rate(t->rsetup, clkrate, RSETUP_MAX);
-	whold	= aemif_calc_rate(t->whold, clkrate, WHOLD_MAX);
-	wstrobe	= aemif_calc_rate(t->wstrobe, clkrate, WSTROBE_MAX);
-	wsetup	= aemif_calc_rate(t->wsetup, clkrate, WSETUP_MAX);
-
-	if (ta < 0 || rhold < 0 || rstrobe < 0 || rsetup < 0 ||
-			whold < 0 || wstrobe < 0 || wsetup < 0) {
-		pr_err("%s: cannot get suitable timings\n", __func__);
-		return -EINVAL;
-	}
-
-	set = TA(ta) | RHOLD(rhold) | RSTROBE(rstrobe) | RSETUP(rsetup) |
-		WHOLD(whold) | WSTROBE(wstrobe) | WSETUP(wsetup);
-
-	val = __raw_readl(base + offset);
-	val &= ~TIMING_MASK;
-	val |= set;
-	__raw_writel(val, base + offset);
-
-	return 0;
-}
-EXPORT_SYMBOL(davinci_aemif_setup_timing);
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index b953bab..54fc267 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -11,6 +11,7 @@  obj-$(CONFIG_HTC_EGPIO)		+= htc-egpio.o
 obj-$(CONFIG_HTC_PASIC3)	+= htc-pasic3.o
 obj-$(CONFIG_HTC_I2CPLD)	+= htc-i2cpld.o
 
+obj-${CONFIG_ARCH_DAVINCI}	+= davinci_aemif.o
 obj-$(CONFIG_MFD_DAVINCI_VOICECODEC)	+= davinci_voicecodec.o
 obj-$(CONFIG_MFD_DM355EVM_MSP)	+= dm355evm_msp.o
 obj-$(CONFIG_MFD_TI_SSP)	+= ti-ssp.o
diff --git a/drivers/mfd/davinci_aemif.c b/drivers/mfd/davinci_aemif.c
new file mode 100644
index 0000000..54d3561
--- /dev/null
+++ b/drivers/mfd/davinci_aemif.c
@@ -0,0 +1,206 @@ 
+/*
+ * AEMIF support for DaVinci SoCs
+ *
+ * Copyright (C) 2010 Texas Instruments Incorporated. http://www.ti.com/
+ *
+ * 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/kernel.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/time.h>
+#include <linux/mfd/davinci_aemif.h>
+#include <linux/mtd/physmap.h>
+#include <linux/slab.h>
+#include <mach/nand.h>
+
+/* Timing value configuration */
+
+#define TA(x)		((x) << 2)
+#define RHOLD(x)	((x) << 4)
+#define RSTROBE(x)	((x) << 7)
+#define RSETUP(x)	((x) << 13)
+#define WHOLD(x)	((x) << 17)
+#define WSTROBE(x)	((x) << 20)
+#define WSETUP(x)	((x) << 26)
+
+#define TA_MAX		0x3
+#define RHOLD_MAX	0x7
+#define RSTROBE_MAX	0x3f
+#define RSETUP_MAX	0xf
+#define WHOLD_MAX	0x7
+#define WSTROBE_MAX	0x3f
+#define WSETUP_MAX	0xf
+
+#define TIMING_MASK	(TA(TA_MAX) | \
+				RHOLD(RHOLD_MAX) | \
+				RSTROBE(RSTROBE_MAX) |	\
+				RSETUP(RSETUP_MAX) | \
+				WHOLD(WHOLD_MAX) | \
+				WSTROBE(WSTROBE_MAX) | \
+				WSETUP(WSETUP_MAX))
+
+/*
+ * aemif_calc_rate - calculate timing data.
+ * @wanted: The cycle time needed in nanoseconds.
+ * @clk: The input clock rate in kHz.
+ * @max: The maximum divider value that can be programmed.
+ *
+ * On success, returns the calculated timing value minus 1 for easy
+ * programming into AEMIF timing registers, else negative errno.
+ */
+static int aemif_calc_rate(int wanted, unsigned long clk, int max)
+{
+	int result;
+
+	result = DIV_ROUND_UP((wanted * clk), NSEC_PER_MSEC) - 1;
+
+	pr_debug("%s: result %d from %ld, %d\n", __func__, result, clk, wanted);
+
+	/* It is generally OK to have a more relaxed timing than requested... */
+	if (result < 0)
+		result = 0;
+
+	/* ... But configuring tighter timings is not an option. */
+	else if (result > max)
+		result = -EINVAL;
+
+	return result;
+}
+
+/**
+ * davinci_aemif_setup_timing - setup timing values for a given AEMIF interface
+ * @t: timing values to be progammed
+ * @base: The virtual base address of the AEMIF interface
+ * @cs: chip-select to program the timing values for
+ *
+ * This function programs the given timing values (in real clock) into the
+ * AEMIF registers taking the AEMIF clock into account.
+ *
+ * This function does not use any locking while programming the AEMIF
+ * because it is expected that there is only one user of a given
+ * chip-select.
+ *
+ * Returns 0 on success, else negative errno.
+ */
+int davinci_aemif_setup_timing(struct davinci_aemif_timing *t,
+					void __iomem *base, unsigned cs)
+{
+	unsigned set, val;
+	int ta, rhold, rstrobe, rsetup, whold, wstrobe, wsetup;
+	unsigned offset = A1CR_OFFSET + cs * 4;
+	struct clk *aemif_clk;
+	unsigned long clkrate;
+
+	if (!t)
+		return 0;	/* Nothing to do */
+
+	aemif_clk = clk_get(NULL, "aemif");
+	if (IS_ERR(aemif_clk))
+		return PTR_ERR(aemif_clk);
+
+	clkrate = clk_get_rate(aemif_clk);
+
+	clkrate /= 1000;	/* turn clock into kHz for ease of use */
+
+	ta	= aemif_calc_rate(t->ta, clkrate, TA_MAX);
+	rhold	= aemif_calc_rate(t->rhold, clkrate, RHOLD_MAX);
+	rstrobe	= aemif_calc_rate(t->rstrobe, clkrate, RSTROBE_MAX);
+	rsetup	= aemif_calc_rate(t->rsetup, clkrate, RSETUP_MAX);
+	whold	= aemif_calc_rate(t->whold, clkrate, WHOLD_MAX);
+	wstrobe	= aemif_calc_rate(t->wstrobe, clkrate, WSTROBE_MAX);
+	wsetup	= aemif_calc_rate(t->wsetup, clkrate, WSETUP_MAX);
+
+	if (ta < 0 || rhold < 0 || rstrobe < 0 || rsetup < 0 ||
+			whold < 0 || wstrobe < 0 || wsetup < 0) {
+		pr_err("%s: cannot get suitable timings\n", __func__);
+		return -EINVAL;
+	}
+
+	set = TA(ta) | RHOLD(rhold) | RSTROBE(rstrobe) | RSETUP(rsetup) |
+		WHOLD(whold) | WSTROBE(wstrobe) | WSETUP(wsetup);
+
+	val = __raw_readl(base + offset);
+	val &= ~TIMING_MASK;
+	val |= set;
+	__raw_writel(val, base + offset);
+
+	return 0;
+}
+EXPORT_SYMBOL(davinci_aemif_setup_timing);
+
+static int __init davinci_aemif_probe(struct platform_device *pdev)
+{
+	struct davinci_aemif_devices *davinci_aemif_devices =
+		pdev->dev.platform_data;
+	struct platform_device *devices;
+	struct mfd_cell *cells;
+	int i, ret, count;
+
+	devices = davinci_aemif_devices->devices;
+
+	cells = kzalloc(sizeof(struct mfd_cell) *
+			davinci_aemif_devices->num_devices, GFP_KERNEL);
+
+	for (i = 0, count = 0; i < davinci_aemif_devices->num_devices; i++) {
+		if (!strcmp(devices[i].name, "davinci_nand")) {
+			cells[count].pdata_size =
+				sizeof(struct davinci_nand_pdata);
+		} else if (!strcmp(devices[i].name, "physmap-flash")) {
+			cells[count].pdata_size =
+				sizeof(struct physmap_flash_data);
+		} else
+			continue;
+
+		cells[count].name = devices[i].name;
+		cells[count].platform_data =
+			devices[i].dev.platform_data;
+		cells[count].id = devices[i].id;
+		cells[count].resources = devices[i].resource;
+		cells[count].num_resources = devices[i].num_resources;
+		count++;
+	}
+
+	ret = mfd_add_devices(&pdev->dev, 0, cells,
+			      count, NULL, 0);
+	if (ret != 0)
+		dev_err(&pdev->dev, "fail to register client devices\n");
+
+	return 0;
+}
+
+static int __devexit davinci_aemif_remove(struct platform_device *pdev)
+{
+	mfd_remove_devices(&pdev->dev);
+	return 0;
+}
+
+static struct platform_driver davinci_aemif_driver = {
+	.driver	= {
+		.name = "davinci_aemif",
+		.owner = THIS_MODULE,
+	},
+	.remove	= __devexit_p(davinci_aemif_remove),
+};
+
+static int __init davinci_aemif_init(void)
+{
+	return platform_driver_probe(&davinci_aemif_driver,
+			davinci_aemif_probe);
+}
+module_init(davinci_aemif_init);
+
+static void __exit davinci_aemif_exit(void)
+{
+	platform_driver_unregister(&davinci_aemif_driver);
+}
+module_exit(davinci_aemif_exit);
+
+MODULE_AUTHOR("Prakash Manjunathappa");
+MODULE_DESCRIPTION("Texas Instruments AEMIF Interface");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/davinci_aemif.h b/include/linux/mfd/davinci_aemif.h
index 05b2934..18650c3 100644
--- a/include/linux/mfd/davinci_aemif.h
+++ b/include/linux/mfd/davinci_aemif.h
@@ -10,6 +10,10 @@ 
 #ifndef _MACH_DAVINCI_AEMIF_H
 #define _MACH_DAVINCI_AEMIF_H
 
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/core.h>
+
 #define NRCSR_OFFSET		0x00
 #define AWCCR_OFFSET		0x04
 #define A1CR_OFFSET		0x10
@@ -18,6 +22,16 @@ 
 #define ACR_EW_MASK		BIT(30)
 #define ACR_SS_MASK		BIT(31)
 
+enum davinci_emif_cells {
+	DAVINCI_NAND_DEVICE_CELL,
+	DAVINCI_NOR_FLASH_CELL,
+};
+
+struct davinci_aemif_devices {
+	struct platform_device *devices;
+	unsigned int num_devices;
+};
+
 /* All timings in nanoseconds */
 struct davinci_aemif_timing {
 	u8	wsetup;