diff mbox

[v3,1/7] mfd: add pruss mfd driver.

Message ID 1299592667-21367-2-git-send-email-subhasish@mistralsolutions.com (mailing list archive)
State Superseded
Headers show

Commit Message

Subhasish Ghosh March 8, 2011, 1:57 p.m. UTC
None

Comments

Subhasish Ghosh April 5, 2011, 6:40 a.m. UTC | #1
> On Wednesday 30 March 2011, Subhasish Ghosh wrote:
>>
>> >> +s32 pruss_disable(struct device *dev, u8 pruss_num)
>> >> +{
>> >> + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
>> >> + struct da8xx_prusscore_regs *h_pruss;
>> >> + struct pruss_map *pruss_mmap = (struct pruss_map *)pruss->ioaddr;
>> >> + u32 temp_reg;
>> >> + u32 delay_cnt;
>> >
>> > Can you explain the significance of pruss_num? As far as I
>> > can tell, you always pass constants in here, so it should
>> > be possible to determine the number from the device.
>>
>> SG - The number is not programmed in the device, I need something to 
>> decide
>> which PRU to disable or enable.
>
> I still don't understand. Please explain how the devices
> relate to the multiple PRUs in hardware.

There are two devices, CAN and UART, in our case we use the PRU as follows:
1. CAN-TX on PRU0, CAN-RX on PRU1
2. SUART-TX on PRU0, SUART-RX on PRU1
3. SUART-TXRX on PRU0, SUART-TXRX on PRU1
diff mbox

Patch

diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index fd01836..6c437df 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -81,6 +81,16 @@  config MFD_DM355EVM_MSP
 	  boards.  MSP430 firmware manages resets and power sequencing,
 	  inputs from buttons and the IR remote, LEDs, an RTC, and more.
 
+config MFD_DA8XX_PRUSS
+	tristate "Texas Instruments DA8XX PRUSS support"
+	depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850
+	select MFD_CORE
+	help
+	  This driver provides support api's for the programmable
+	  realtime unit (PRU) present on TI's da8xx processors. It
+	  provides basic read, write, config, enable, disable
+	  routines to facilitate devices emulated on it.
+
 config HTC_EGPIO
 	bool "HTC EGPIO support"
 	depends on GENERIC_HARDIRQS && GPIOLIB && ARM
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index a54e2c7..670d6b0 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -13,6 +13,7 @@  obj-$(CONFIG_HTC_PASIC3)	+= htc-pasic3.o
 obj-$(CONFIG_HTC_I2CPLD)	+= htc-i2cpld.o
 
 obj-$(CONFIG_MFD_DAVINCI_VOICECODEC)	+= davinci_voicecodec.o
+obj-$(CONFIG_MFD_DA8XX_PRUSS)	+= da8xx_pru.o
 obj-$(CONFIG_MFD_DM355EVM_MSP)	+= dm355evm_msp.o
 
 obj-$(CONFIG_MFD_STMPE)		+= stmpe.o
diff --git a/drivers/mfd/da8xx_pru.c b/drivers/mfd/da8xx_pru.c
new file mode 100644
index 0000000..0fd9bb2
--- /dev/null
+++ b/drivers/mfd/da8xx_pru.c
@@ -0,0 +1,560 @@ 
+/*
+ * Copyright (C) 2011 Texas Instruments Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as  published by the
+ * Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/mfd/da8xx/da8xx_prucore.h>
+#include <linux/mfd/da8xx/da8xx_pru.h>
+#include <linux/mfd/core.h>
+#include <linux/io.h>
+#include <mach/da8xx.h>
+
+struct da8xx_pruss {
+	struct device *dev;
+	spinlock_t lock;
+	struct resource *res;
+	struct clk *clk;
+	u32 clk_freq;
+	void __iomem *ioaddr;
+};
+
+u32 pruss_get_clk_freq(struct device *dev)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+
+	return pruss->clk_freq;
+}
+EXPORT_SYMBOL(pruss_get_clk_freq);
+
+s32 pruss_disable(struct device *dev, u8 pruss_num)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	struct da8xx_prusscore_regs *h_pruss;
+	struct pruss_map *pruss_mmap = (struct pruss_map *)pruss->ioaddr;
+	u32 temp_reg;
+	u32 delay_cnt;
+
+	if ((pruss_num != DA8XX_PRUCORE_0) && (pruss_num != DA8XX_PRUCORE_1))
+		return -EINVAL;
+
+	spin_lock(&pruss->lock);
+	if (pruss_num == DA8XX_PRUCORE_0) {
+		/* pruss deinit */
+		__raw_writel(0xFFFFFFFF, (PRUSS_INTC_STATCLRINT0 & 0xFFFF));
+		/* Disable PRU0  */
+		h_pruss = (struct da8xx_prusscore_regs *)
+			&pruss_mmap->core[DA8XX_PRUCORE_0];
+
+		temp_reg = __raw_readl(&h_pruss->CONTROL);
+		temp_reg = (temp_reg &
+				~DA8XX_PRUCORE_CONTROL_COUNTENABLE_MASK) |
+				((DA8XX_PRUCORE_CONTROL_COUNTENABLE_DISABLE <<
+				DA8XX_PRUCORE_CONTROL_COUNTENABLE_SHIFT) &
+				DA8XX_PRUCORE_CONTROL_COUNTENABLE_MASK);
+		__raw_writel(temp_reg, &h_pruss->CONTROL);
+
+		for (delay_cnt = 0x10000; delay_cnt > 0; delay_cnt--) {
+
+			temp_reg = __raw_readl(&h_pruss->CONTROL);
+			temp_reg = (temp_reg &
+				~DA8XX_PRUCORE_CONTROL_ENABLE_MASK) |
+				((DA8XX_PRUCORE_CONTROL_ENABLE_DISABLE <<
+				DA8XX_PRUCORE_CONTROL_ENABLE_SHIFT) &
+				DA8XX_PRUCORE_CONTROL_ENABLE_MASK);
+			__raw_writel(temp_reg, &h_pruss->CONTROL);
+		}
+
+		/* Reset PRU0 */
+		for (delay_cnt = 0x10000; delay_cnt > 0; delay_cnt--)
+			__raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,
+					&h_pruss->CONTROL);
+
+	} else if (pruss_num == DA8XX_PRUCORE_1) {
+		/* pruss deinit */
+		__raw_writel(0xFFFFFFFF, (PRUSS_INTC_STATCLRINT1 & 0xFFFF));
+		/* Disable PRU1 */
+		h_pruss = (struct da8xx_prusscore_regs *)
+			&pruss_mmap->core[DA8XX_PRUCORE_1];
+		temp_reg = __raw_readl(&h_pruss->CONTROL);
+		temp_reg = (temp_reg &
+				~DA8XX_PRUCORE_CONTROL_COUNTENABLE_MASK) |
+				((DA8XX_PRUCORE_CONTROL_COUNTENABLE_DISABLE <<
+				DA8XX_PRUCORE_CONTROL_COUNTENABLE_SHIFT) &
+				DA8XX_PRUCORE_CONTROL_COUNTENABLE_MASK);
+		__raw_writel(temp_reg, &h_pruss->CONTROL);
+
+		for (delay_cnt = 0x10000; delay_cnt > 0; delay_cnt--) {
+
+			temp_reg = __raw_readl(&h_pruss->CONTROL);
+			temp_reg = (temp_reg &
+				~DA8XX_PRUCORE_CONTROL_ENABLE_MASK) |
+				((DA8XX_PRUCORE_CONTROL_ENABLE_DISABLE <<
+				DA8XX_PRUCORE_CONTROL_ENABLE_SHIFT) &
+				DA8XX_PRUCORE_CONTROL_ENABLE_MASK);
+			__raw_writel(temp_reg, &h_pruss->CONTROL);
+		}
+
+		/* Reset PRU1 */
+		for (delay_cnt = 0x10000; delay_cnt > 0; delay_cnt--)
+			__raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,
+							&h_pruss->CONTROL);
+	}
+	spin_unlock(&pruss->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_disable);
+
+s32 pruss_enable(struct device *dev, u8 pruss_num)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	struct da8xx_prusscore_regs *h_pruss;
+	struct pruss_map *pruss_mmap = (struct pruss_map *)pruss->ioaddr;
+
+	if ((pruss_num != DA8XX_PRUCORE_0) && (pruss_num != DA8XX_PRUCORE_1))
+		return -EINVAL;
+
+	spin_lock(&pruss->lock);
+	if (pruss_num == DA8XX_PRUCORE_0) {
+		/* Reset PRU0 */
+		h_pruss = (struct da8xx_prusscore_regs *)
+			&pruss_mmap->core[DA8XX_PRUCORE_0];
+		__raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,
+			&h_pruss->CONTROL);
+	} else if (pruss_num == DA8XX_PRUCORE_1) {
+		/* Reset PRU1  */
+		h_pruss = (struct da8xx_prusscore_regs *)
+			&pruss_mmap->core[DA8XX_PRUCORE_1];
+		__raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL,
+			&h_pruss->CONTROL);
+	}
+	spin_unlock(&pruss->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_enable);
+
+/* Load the specified PRU with code */
+s32 pruss_load(struct device *dev, u8 pruss_num,
+			u32 *pruss_code, u32 code_size_in_words)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	struct pruss_map *pruss_mmap = (struct pruss_map *)pruss->ioaddr;
+	u32 *pruss_iram;
+	u32 i;
+
+	if (pruss_num == DA8XX_PRUCORE_0)
+		pruss_iram = (u32 *)&pruss_mmap->iram0;
+	else if (pruss_num == DA8XX_PRUCORE_1)
+		pruss_iram = (u32 *)&pruss_mmap->iram1;
+	else
+		return -EINVAL;
+
+	pruss_enable(dev, pruss_num);
+
+	spin_lock(&pruss->lock);
+	/* Copy dMAX code to its instruction RAM  */
+	for (i = 0; i < code_size_in_words; i++)
+		__raw_writel(pruss_code[i], (pruss_iram + i));
+
+	spin_unlock(&pruss->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_load);
+
+s32 pruss_run(struct device *dev, u8 pruss_num)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	struct da8xx_prusscore_regs *h_pruss;
+	struct pruss_map *pruss_mmap = (struct pruss_map *)pruss->ioaddr;
+	u32 temp_reg;
+
+	if (pruss_num == DA8XX_PRUCORE_0) {
+		/* DA8XX_PRUCORE_0_REGS; */
+		h_pruss = (struct da8xx_prusscore_regs *)
+			&pruss_mmap->core[DA8XX_PRUCORE_0];
+	} else if (pruss_num == DA8XX_PRUCORE_1) {
+		/* DA8XX_PRUCORE_1_REGS; */
+		h_pruss = (struct da8xx_prusscore_regs *)
+			&pruss_mmap->core[DA8XX_PRUCORE_1];
+	} else
+		return -EINVAL;
+
+	/* Enable dMAX, let it execute the code we just copied */
+	spin_lock(&pruss->lock);
+	temp_reg = __raw_readl(&h_pruss->CONTROL);
+	temp_reg = (temp_reg &
+			~DA8XX_PRUCORE_CONTROL_COUNTENABLE_MASK) |
+			((DA8XX_PRUCORE_CONTROL_COUNTENABLE_ENABLE <<
+			DA8XX_PRUCORE_CONTROL_COUNTENABLE_SHIFT) &
+			DA8XX_PRUCORE_CONTROL_COUNTENABLE_MASK);
+	__raw_writel(temp_reg, &h_pruss->CONTROL);
+
+	temp_reg = __raw_readl(&h_pruss->CONTROL);
+	temp_reg = (temp_reg &
+			~DA8XX_PRUCORE_CONTROL_ENABLE_MASK) |
+			((DA8XX_PRUCORE_CONTROL_ENABLE_ENABLE <<
+			DA8XX_PRUCORE_CONTROL_ENABLE_SHIFT) &
+			DA8XX_PRUCORE_CONTROL_ENABLE_MASK);
+	__raw_writel(temp_reg, &h_pruss->CONTROL);
+	 spin_unlock(&pruss->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_run);
+
+s32 pruss_wait_for_halt(struct device *dev, u8 pruss_num, u32 timeout)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	struct da8xx_prusscore_regs *h_pruss;
+	struct pruss_map *pruss_mmap = (struct pruss_map *)pruss->ioaddr;
+	u32 temp_reg;
+	u32 cnt = timeout;
+
+	if (pruss_num == DA8XX_PRUCORE_0) {
+		/* DA8XX_PRUCORE_0_REGS; */
+		h_pruss = (struct da8xx_prusscore_regs *)
+			&pruss_mmap->core[DA8XX_PRUCORE_0];
+	} else if (pruss_num == DA8XX_PRUCORE_1) {
+		/* DA8XX_PRUCORE_1_REGS; */
+		h_pruss = (struct da8xx_prusscore_regs *)
+			&pruss_mmap->core[DA8XX_PRUCORE_1];
+	} else
+		return -EINVAL;
+
+	while (cnt--) {
+		temp_reg = __raw_readl(&h_pruss->CONTROL);
+		if (((temp_reg & DA8XX_PRUCORE_CONTROL_RUNSTATE_MASK) >>
+				DA8XX_PRUCORE_CONTROL_RUNSTATE_SHIFT) ==
+					DA8XX_PRUCORE_CONTROL_RUNSTATE_HALT)
+			break;
+	}
+	if (cnt == 0)
+		return -EBUSY;
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_wait_for_halt);
+
+s32 pruss_writeb(struct device *dev, u32 offset,
+		u8 *pdatatowrite, u16 bytestowrite)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	u8 *paddresstowrite;
+	u16 loop;
+	offset = (u32)pruss->ioaddr + offset;
+	paddresstowrite = (u8 *) (offset);
+
+	for (loop = 0; loop < bytestowrite; loop++)
+		__raw_writeb(*pdatatowrite++, paddresstowrite++);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_writeb);
+
+s32 pruss_rmwb(struct device *dev, u32 offset, u8 mask, u8 val)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	u32 *paddress;
+	u32 preg_data;
+
+	paddress = (u32 *)((u32)pruss->ioaddr + offset);
+
+	spin_lock(&pruss->lock);
+	preg_data = __raw_readb(paddress);
+	preg_data &= ~mask;
+	preg_data |= val;
+	__raw_writeb(preg_data, paddress);
+	spin_unlock(&pruss->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_rmwb);
+
+s32 pruss_readb(struct device *dev, u32 offset,
+		u8 *pdatatoread, u16 bytestoread)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	u8 *paddresstoread;
+	u16 loop;
+	offset = (u32)pruss->ioaddr + offset;
+	paddresstoread = (u8 *) (offset);
+
+	for (loop = 0; loop < bytestoread; loop++)
+		*pdatatoread++ = __raw_readb(paddresstoread++);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_readb);
+
+s32 pruss_writel(struct device *dev, u32 offset,
+		u32 *pdatatowrite, s16 wordstowrite)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	u32 *paddresstowrite;
+	s16 loop;
+
+	offset = (u32)pruss->ioaddr + offset;
+	paddresstowrite = (u32 *)(offset);
+
+	for (loop = 0; loop < wordstowrite; loop++)
+		__raw_writel(*pdatatowrite++, paddresstowrite++);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_writel);
+
+s32 pruss_rmwl(struct device *dev, u32 offset, u32 mask, u32 val)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	u32 *paddress;
+	u32 preg_data;
+
+	paddress = (u32 *)((u32)pruss->ioaddr + offset);
+
+	spin_lock(&pruss->lock);
+	preg_data = __raw_readl(paddress);
+	preg_data &= ~mask;
+	preg_data |= val;
+	__raw_writel(preg_data, paddress);
+	spin_unlock(&pruss->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_rmwl);
+
+s32 pruss_readl(struct device *dev, u32 offset,
+		u32 *pdatatoread, s16 wordstoread)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	u32 *paddresstoread;
+	s16 loop;
+
+	offset = (u32)pruss->ioaddr + offset;
+	paddresstoread = (u32 *)(offset);
+
+	for (loop = 0; loop < wordstoread; loop++)
+		*pdatatoread++ = __raw_readl(paddresstoread++);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_readl);
+
+s32 pruss_writew(struct device *dev, u32 offset,
+		u16 *pdatatowrite, s16 wordstowrite)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	u32 *paddresstowrite;
+	s16 loop;
+
+	offset = (u32)pruss->ioaddr + offset;
+	paddresstowrite = (u32 *)(offset);
+
+	for (loop = 0; loop < wordstowrite; loop++)
+		__raw_writew(*(pdatatowrite++), (paddresstowrite++));
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_writew);
+
+s32 pruss_rmww(struct device *dev, u32 offset, u16 mask, u16 val)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	u32 *paddress;
+	u32 preg_data;
+
+	paddress = (u32 *)((u32)pruss->ioaddr + offset);
+
+	spin_lock(&pruss->lock);
+	preg_data = __raw_readw(paddress);
+	preg_data &= ~mask;
+	preg_data |= val;
+	__raw_writew(preg_data, paddress);
+	spin_unlock(&pruss->lock);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_rmww);
+
+s32 pruss_readw(struct device *dev, u32 offset,
+			u16 *pdatatoread, s16 wordstoread)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	u32 *paddresstoread;
+	s16 loop;
+
+	offset = (u32)pruss->ioaddr + offset;
+	paddresstoread = (u32 *)(offset);
+
+	for (loop = 0; loop < wordstoread; loop++)
+		*pdatatoread++ = __raw_readw(paddresstoread++);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_readw);
+
+s32 pruss_idx_writel(struct device *dev, u32 offset, u32 value)
+{
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent);
+	u32 *paddresstowrite;
+
+	paddresstowrite = (u32 *)(pruss->ioaddr + offset);
+	__raw_writel(value, paddresstowrite);
+
+	return 0;
+}
+EXPORT_SYMBOL(pruss_idx_writel);
+
+static int pruss_mfd_add_devices(struct platform_device *pdev)
+{
+	struct da8xx_pruss_devices *dev_data = pdev->dev.platform_data;
+	struct device *dev = &pdev->dev;
+	struct mfd_cell cell;
+	u32 err, count;
+
+	for (count = 0; dev_data[count].dev_name != NULL; count++) {
+		memset(&cell, 0, sizeof(struct mfd_cell));
+		cell.id			= count;
+		cell.name		= dev_data[count].dev_name;
+		cell.platform_data	= dev_data[count].pdata;
+		cell.data_size		= dev_data[count].pdata_size;
+		cell.resources		= dev_data[count].resources;
+		cell.num_resources	= dev_data[count].num_resources;
+
+		err = mfd_add_devices(dev, 0, &cell, 1, NULL, 0);
+		if (err) {
+			dev_err(dev, "cannot add mfd cells\n");
+			return err;
+		}
+		dev_info(dev, "mfd: added %s device\n",
+				dev_data[count].dev_name);
+	}
+
+	return err;
+}
+
+static int __devinit da8xx_pruss_probe(struct platform_device *pdev)
+{
+	struct da8xx_pruss *pruss_dev = NULL;
+	u32 err;
+
+	pruss_dev = kzalloc(sizeof(struct da8xx_pruss), GFP_KERNEL);
+	if (!pruss_dev)
+		return -ENOMEM;
+
+	pruss_dev->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!pruss_dev->res) {
+		dev_err(&pdev->dev,
+		"unable to get pruss memory resources!\n");
+		err = -ENODEV;
+		goto probe_exit_kfree;
+	}
+
+	if (!request_mem_region(pruss_dev->res->start,
+		resource_size(pruss_dev->res), dev_name(&pdev->dev))) {
+		dev_err(&pdev->dev, "pruss memory region already claimed!\n");
+		err = -EBUSY;
+		goto probe_exit_kfree;
+	}
+
+	pruss_dev->ioaddr = ioremap(pruss_dev->res->start,
+	resource_size(pruss_dev->res));
+	if (!pruss_dev->ioaddr) {
+		dev_err(&pdev->dev, "ioremap failed\n");
+		err = -ENOMEM;
+		goto probe_exit_free_region;
+	}
+
+	pruss_dev->clk = clk_get(NULL, "pruss");
+	if (IS_ERR(pruss_dev->clk)) {
+		dev_err(&pdev->dev, "no clock available: pruss\n");
+		err = -ENODEV;
+		pruss_dev->clk = NULL;
+		goto probe_exit_iounmap;
+	}
+	spin_lock_init(&pruss_dev->lock);
+
+	clk_enable(pruss_dev->clk);
+	pruss_dev->clk_freq = clk_get_rate(pruss_dev->clk);
+
+	err = pruss_mfd_add_devices(pdev);
+	if (err)
+		goto probe_exit_clock;
+
+	platform_set_drvdata(pdev, pruss_dev);
+	pruss_dev->dev = &pdev->dev;
+	return 0;
+
+probe_exit_clock:
+	clk_put(pruss_dev->clk);
+	clk_disable(pruss_dev->clk);
+probe_exit_iounmap:
+	iounmap(pruss_dev->ioaddr);
+probe_exit_free_region:
+	release_mem_region(pruss_dev->res->start,
+			resource_size(pruss_dev->res));
+probe_exit_kfree:
+	kfree(pruss_dev);
+	return err;
+}
+
+static int __devexit da8xx_pruss_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct da8xx_pruss *pruss = dev_get_drvdata(dev);
+
+	mfd_remove_devices(dev);
+	pruss_disable(dev, DA8XX_PRUCORE_0);
+	pruss_disable(dev, DA8XX_PRUCORE_1);
+	clk_disable(pruss->clk);
+	clk_put(pruss->clk);
+	iounmap(pruss->ioaddr);
+	release_mem_region(pruss->res->start, resource_size(pruss->res));
+	kfree(pruss);
+	dev_set_drvdata(dev, NULL);
+	return 0;
+}
+
+static struct platform_driver da8xx_pruss_driver = {
+	.probe	= da8xx_pruss_probe,
+	.remove	= __devexit_p(da8xx_pruss_remove),
+	.driver	= {
+		.name	= "pruss_mfd",
+		.owner	= THIS_MODULE,
+	}
+};
+
+static int __init da8xx_pruss_init(void)
+{
+	return platform_driver_register(&da8xx_pruss_driver);
+}
+module_init(da8xx_pruss_init);
+
+static void __exit da8xx_pruss_exit(void)
+{
+	platform_driver_unregister(&da8xx_pruss_driver);
+}
+module_exit(da8xx_pruss_exit);
+
+MODULE_DESCRIPTION("Programmable Realtime Unit (PRU) Driver");
+MODULE_AUTHOR("Subhasish Ghosh");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/da8xx/da8xx_pru.h b/include/linux/mfd/da8xx/da8xx_pru.h
new file mode 100644
index 0000000..ba65ec0
--- /dev/null
+++ b/include/linux/mfd/da8xx/da8xx_pru.h
@@ -0,0 +1,135 @@ 
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Author: Jitendra Kumar <jitendra@mistralsolutions.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as  published by the
+ * Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _PRUSS_H_
+#define _PRUSS_H_
+
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include "da8xx_prucore.h"
+
+#define PRUSS_NUM0			DA8XX_PRUCORE_0
+#define PRUSS_NUM1			DA8XX_PRUCORE_1
+
+#define PRUSS_PRU0_BASE_ADDRESS		0
+#define PRUSS_INTC_BASE_ADDRESS		(PRUSS_PRU0_BASE_ADDRESS + 0x4000)
+#define PRUSS_INTC_GLBLEN		(PRUSS_INTC_BASE_ADDRESS + 0x10)
+#define PRUSS_INTC_GLBLNSTLVL		(PRUSS_INTC_BASE_ADDRESS + 0x1C)
+#define PRUSS_INTC_STATIDXSET		(PRUSS_INTC_BASE_ADDRESS + 0x20)
+#define PRUSS_INTC_STATIDXCLR		(PRUSS_INTC_BASE_ADDRESS + 0x24)
+#define PRUSS_INTC_ENIDXSET		(PRUSS_INTC_BASE_ADDRESS + 0x28)
+#define PRUSS_INTC_ENIDXCLR		(PRUSS_INTC_BASE_ADDRESS + 0x2C)
+#define PRUSS_INTC_HSTINTENIDXSET	(PRUSS_INTC_BASE_ADDRESS + 0x34)
+#define PRUSS_INTC_HSTINTENIDXCLR	(PRUSS_INTC_BASE_ADDRESS + 0x38)
+#define PRUSS_INTC_GLBLPRIIDX		(PRUSS_INTC_BASE_ADDRESS + 0x80)
+#define PRUSS_INTC_STATSETINT0		(PRUSS_INTC_BASE_ADDRESS + 0x200)
+#define PRUSS_INTC_STATSETINT1		(PRUSS_INTC_BASE_ADDRESS + 0x204)
+#define PRUSS_INTC_STATCLRINT0		(PRUSS_INTC_BASE_ADDRESS + 0x280)
+#define PRUSS_INTC_STATCLRINT1		(PRUSS_INTC_BASE_ADDRESS + 0x284)
+#define PRUSS_INTC_ENABLESET0		(PRUSS_INTC_BASE_ADDRESS + 0x300)
+#define PRUSS_INTC_ENABLESET1		(PRUSS_INTC_BASE_ADDRESS + 0x304)
+#define PRUSS_INTC_ENABLECLR0		(PRUSS_INTC_BASE_ADDRESS + 0x380)
+#define PRUSS_INTC_ENABLECLR1		(PRUSS_INTC_BASE_ADDRESS + 0x384)
+#define PRUSS_INTC_CHANMAP0		(PRUSS_INTC_BASE_ADDRESS + 0x400)
+#define PRUSS_INTC_CHANMAP1		(PRUSS_INTC_BASE_ADDRESS + 0x404)
+#define PRUSS_INTC_CHANMAP2		(PRUSS_INTC_BASE_ADDRESS + 0x408)
+#define PRUSS_INTC_CHANMAP3		(PRUSS_INTC_BASE_ADDRESS + 0x40C)
+#define PRUSS_INTC_CHANMAP4		(PRUSS_INTC_BASE_ADDRESS + 0x410)
+#define PRUSS_INTC_CHANMAP5		(PRUSS_INTC_BASE_ADDRESS + 0x414)
+#define PRUSS_INTC_CHANMAP6		(PRUSS_INTC_BASE_ADDRESS + 0x418)
+#define PRUSS_INTC_CHANMAP7		(PRUSS_INTC_BASE_ADDRESS + 0x41C)
+#define PRUSS_INTC_CHANMAP8		(PRUSS_INTC_BASE_ADDRESS + 0x420)
+#define PRUSS_INTC_CHANMAP9		(PRUSS_INTC_BASE_ADDRESS + 0x424)
+#define PRUSS_INTC_CHANMAP10		(PRUSS_INTC_BASE_ADDRESS + 0x428)
+#define PRUSS_INTC_CHANMAP11		(PRUSS_INTC_BASE_ADDRESS + 0x42C)
+#define PRUSS_INTC_CHANMAP12		(PRUSS_INTC_BASE_ADDRESS + 0x430)
+#define PRUSS_INTC_CHANMAP13		(PRUSS_INTC_BASE_ADDRESS + 0x434)
+#define PRUSS_INTC_CHANMAP14		(PRUSS_INTC_BASE_ADDRESS + 0x438)
+#define PRUSS_INTC_CHANMAP15		(PRUSS_INTC_BASE_ADDRESS + 0x43C)
+#define PRUSS_INTC_HOSTMAP0		(PRUSS_INTC_BASE_ADDRESS + 0x800)
+#define PRUSS_INTC_HOSTMAP1		(PRUSS_INTC_BASE_ADDRESS + 0x804)
+#define PRUSS_INTC_HOSTMAP2		(PRUSS_INTC_BASE_ADDRESS + 0x808)
+#define PRUSS_INTC_POLARITY0		(PRUSS_INTC_BASE_ADDRESS + 0xD00)
+#define PRUSS_INTC_POLARITY1		(PRUSS_INTC_BASE_ADDRESS + 0xD04)
+#define PRUSS_INTC_TYPE0		(PRUSS_INTC_BASE_ADDRESS + 0xD80)
+#define PRUSS_INTC_TYPE1		(PRUSS_INTC_BASE_ADDRESS + 0xD84)
+#define PRUSS_INTC_HOSTINTEN		(PRUSS_INTC_BASE_ADDRESS + 0x1500)
+#define PRUSS_INTC_HOSTINTLVL_MAX	9
+
+#define PRU_INTC_HOSTMAP0_CHAN			(0x03020100)
+#define PRU_INTC_HOSTMAP1_CHAN			(0x07060504)
+#define PRU_INTC_HOSTMAP2_CHAN			(0x00000908)
+
+#define PRU_INTC_CHANMAP7_SYS_EVT31		(0x00000000)
+#define PRU_INTC_CHANMAP8_FULL			(0x02020100)
+#define PRU_INTC_CHANMAP9_FULL			(0x04040303)
+#define PRU_INTC_CHANMAP10_FULL			(0x06060505)
+#define PRU_INTC_CHANMAP11_FULL			(0x08080707)
+#define PRU_INTC_CHANMAP12_FULL			(0x00010909)
+#define PRU_INTC_CHANMAP8_HALF			(0x03020100)
+#define PRU_INTC_CHANMAP9_HALF			(0x07060504)
+#define PRU_INTC_CHANMAP10_HALF			(0x03020908)
+#define PRU_INTC_CHANMAP11_HALF			(0x07060504)
+#define PRU_INTC_CHANMAP12_HALF			(0x00010908)
+
+#define PRU_INTC_REGMAP_MASK			(0xFFFFFFFF)
+
+struct da8xx_pruss_devices {
+	const char *dev_name;
+	void *pdata;
+	size_t pdata_size;
+	int (*setup)(void);
+	u32 num_resources;
+	struct resource *resources;
+};
+
+u32 pruss_get_clk_freq(struct device *dev);
+
+s32 pruss_enable(struct device *dev, u8 pruss_num);
+
+s32 pruss_load(struct device *dev, u8 pruss_num,
+	u32 *pruss_code, u32 code_size_in_words);
+
+s32 pruss_run(struct device *dev, u8 pruss_num);
+
+s32 pruss_wait_for_halt(struct device *dev, u8 pruss_num, u32 timeout);
+
+s32 pruss_disable(struct device *dev, u8 pruss_num);
+
+s32 pruss_writeb(struct device *dev, u32 offset,
+		u8 *pdatatowrite, u16 wordstowrite);
+
+s32 pruss_rmwb(struct device *dev, u32 offset, u8 mask, u8 val);
+
+s32 pruss_readb(struct device *dev, u32 offset,
+		u8 *pdatatoread, u16 wordstoread);
+
+s32 pruss_readl(struct device *dev, u32 offset,
+		u32 *pdatatoread, s16 wordstoread);
+
+s32 pruss_writel(struct device *dev, u32 offset,
+		u32 *pdatatoread, s16 wordstoread);
+
+s32 pruss_rmwl(struct device *dev, u32 offset, u32 mask, u32 val);
+
+s32 pruss_idx_writel(struct device *dev, u32 offset, u32 value);
+
+s32 pruss_writew(struct device *dev, u32 offset,
+		u16 *datatowrite, s16 wordstowrite);
+
+s32 pruss_rmww(struct device *dev, u32 offset, u16 mask, u16 val);
+
+s32 pruss_readw(struct device *dev, u32 offset,
+		u16 *pdatatoread, s16 wordstoread);
+#endif	/* End _PRUSS_H_ */
diff --git a/include/linux/mfd/da8xx/da8xx_prucore.h b/include/linux/mfd/da8xx/da8xx_prucore.h
new file mode 100644
index 0000000..913d56f
--- /dev/null
+++ b/include/linux/mfd/da8xx/da8xx_prucore.h
@@ -0,0 +1,129 @@ 
+/*
+ * Copyright (C) 2010 Texas Instruments Incorporated
+ * Author: Jitendra Kumar <jitendra@mistralsolutions.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as  published by the
+ * Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _DA8XX_PRUCORE_H_
+#define _DA8XX_PRUCORE_H_
+
+#include <linux/types.h>
+
+#define DA8XX_PRUCORE_0		(0)
+#define DA8XX_PRUCORE_1		(1)
+
+#define DA8XX_PRUCORE_CONTROL_PCRESETVAL_MASK			(0xFFFF0000u)
+#define DA8XX_PRUCORE_CONTROL_PCRESETVAL_SHIFT			(0x00000010u)
+#define DA8XX_PRUCORE_CONTROL_PCRESETVAL_RESETVAL		(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_RUNSTATE_MASK			(0x00008000u)
+#define DA8XX_PRUCORE_CONTROL_RUNSTATE_SHIFT			(0x0000000Fu)
+#define DA8XX_PRUCORE_CONTROL_RUNSTATE_RESETVAL			(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_RUNSTATE_HALT			(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_RUNSTATE_RUN			(0x00000001u)
+#define DA8XX_PRUCORE_CONTROL_SINGLESTEP_MASK			(0x00000100u)
+#define DA8XX_PRUCORE_CONTROL_SINGLESTEP_SHIFT			(0x00000008u)
+#define DA8XX_PRUCORE_CONTROL_SINGLESTEP_RESETVAL		(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_SINGLESTEP_FREERUN		(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_SINGLESTEP_SINGLE			(0x00000001u)
+#define DA8XX_PRUCORE_CONTROL_COUNTENABLE_MASK			(0x00000008u)
+#define DA8XX_PRUCORE_CONTROL_COUNTENABLE_SHIFT			(0x00000003u)
+#define DA8XX_PRUCORE_CONTROL_COUNTENABLE_RESETVAL		(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_COUNTENABLE_DISABLE		(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_COUNTENABLE_ENABLE		(0x00000001u)
+#define DA8XX_PRUCORE_CONTROL_SLEEPING_MASK			(0x00000004u)
+#define DA8XX_PRUCORE_CONTROL_SLEEPING_SHIFT			(0x00000002u)
+#define DA8XX_PRUCORE_CONTROL_SLEEPING_RESETVAL			(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_SLEEPING_NOTASLEEP		(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_SLEEPING_ASLEEP			(0x00000001u)
+#define DA8XX_PRUCORE_CONTROL_ENABLE_MASK			(0x00000002u)
+#define DA8XX_PRUCORE_CONTROL_ENABLE_SHIFT			(0x00000001u)
+#define DA8XX_PRUCORE_CONTROL_ENABLE_RESETVAL			(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_ENABLE_DISABLE			(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_ENABLE_ENABLE			(0x00000001u)
+#define DA8XX_PRUCORE_CONTROL_SOFTRESET_MASK			(0x00000001u)
+#define DA8XX_PRUCORE_CONTROL_SOFTRESET_SHIFT			(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_SOFTRESET_RESETVAL		(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_SOFTRESET_RESET			(0x00000000u)
+#define DA8XX_PRUCORE_CONTROL_SOFTRESET_OUT_OF_RESET		(0x00000001u)
+#define DA8XX_PRUCORE_CONTROL_RESETVAL				(0x00000000u)
+
+struct da8xx_prusscore_regs {
+	u32 CONTROL;
+	u32 STATUS;
+	u32 WAKEUP;
+	u32 CYCLECNT;
+	u32 STALLCNT;
+	u8  RSVD0[12];
+	u32 CONTABBLKIDX0;
+	u32 CONTABBLKIDX1;
+	u32 CONTABPROPTR0;
+	u32 CONTABPROPTR1;
+	u8  RSVD1[976];
+	u32 INTGPR[32];
+	u32 INTCTER[32];
+	u8  RSVD2[768];
+};
+
+struct pruss_intc_regs {
+	u32 REVID;
+	u32 CONTROL;
+	u8 RES1[8];
+	u32 GLBLEN;
+	u8 RES2[8];
+	u32 GLBLNSTLVL;
+	u32 STATIDXSET;
+	u32 STATIDXCLR;
+	u32 ENIDXSET;
+	u32 ENIDXCLR;
+	u8 RES3[4];
+	u32 HOSTINTENIDXSET;
+	u32 HOSTINTENIDXCLR;
+	u8 RES4[68];
+	u32 GLBLPRIIDX;
+	u8 RES5[380];
+	u32 STATSETINT[2];
+	u8 RES6[120];
+	u32 STATCLRINT[2];
+	u8 RES7[120];
+	u32 ENABLESET[2];
+	u8 RES8[120];
+	u32 ENABLECLR[2];
+	u8 RES9[120];
+	u32 CHANMAP[16];
+	u8 RES10[960];
+	u32 HOSTMAP[2];
+	u8 RES11[248];
+	u32 HOSTINTPRIIDX[10];
+	u8 RES12[984];
+	u32 POLARITY[2];
+	u8 RES13[120];
+	u32 TYPE[2];
+	u8 RES14[888];
+	u32 HOSTINTNSTLVL[10];
+	u8 RES15[984];
+	u32 HOSTINTEN;
+	u8 RES16[6907];
+};
+
+struct pruss_map {
+	u8 dram0[512];
+	u8 res1[7680];
+	u8 dram1[512];
+	u8 res2[7680];
+	struct pruss_intc_regs intc;
+	struct da8xx_prusscore_regs core[2];
+	u8 iram0[4096];
+	u8 res3[12288];
+	u8 iram1[4096];
+	u8 res4[12288];
+};
+
+#endif