From ohad at wizery.com Fri Jul 1 10:13:54 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Fri, 1 Jul 2011 18:13:54 +0300 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> <20110627222121.GD20865@ponder.secretlab.ca> Message-ID: On Wed, Jun 29, 2011 at 6:43 PM, Grant Likely wrote: > So you are right that device_unregister drops the refcount to zero, > but the code still needs to be fixed to not call kfree() directly. Good point, thanks ! > It also looks like rpmsg_destroy_channel() needs to be fixed to remove > the kfree call Yes, I need to remove this direct kfree as well, and indeed just let rpmsg_release_device do its thing when the last reference is dropped. I should also remove the direct kfree when device_register fails: in that case, I need only to put_device and let the release handler do its thing too. > and an extra put_device() call. We need that extra put_device in rpmsg_destroy_channel because device_find_child() is doing get_device before returning it. Thanks, Grant! Ohad. From manjunath.hadli at ti.com Mon Jul 4 00:58:06 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Mon, 4 Jul 2011 11:28:06 +0530 Subject: [ RFC PATCH 0/8] RFC for Media Controller capture driver for DM365 In-Reply-To: <20110630135736.GK12671@valkosipuli.localdomain> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> <20110630135736.GK12671@valkosipuli.localdomain> Message-ID: Sakari, Thank you for the comments. My responses inlined. -Manjunath On Thu, Jun 30, 2011 at 19:27:36, Sakari Ailus wrote: > Hi Manjunath, > > Thanks for the patches. > > On Thu, Jun 30, 2011 at 06:43:09PM +0530, Manjunath Hadli wrote: > > Thease are the RFC patches for the DM365 video capture, of which the > > current set includes only CCDC and the VPFE framework. Once the > > present set is reviewed, I will send out the other parts like H3A, > > sensor additions etc. > > > > Introduction > > ------------ > > This is the proposal of the initial version of design and > > implementation of the Davinci family (dm644x,dm355,dm365)VPFE (Video > > Port Front End) drivers using Media Controloler , the initial version > > which supports the following: > > 1) dm365 vpfe > > 2) ccdc,previewer,resizer,h3a,af blocks > > 3) supports both continuous and single-shot modes > > 4) supports user pointer exchange and memory mapped modes for buffer > > allocation > > > > This driver bases its design on Laurent Pinchart's Media Controller > > Design whose patches for Media Controller and subdev enhancements form the base. > > The driver also takes copious elements taken from Laurent Pinchart and > > others' OMAP ISP driver based on Media Controller. So thank you all > > the people who are responsible for the Media Controller and the OMAP ISP driver. > > This may be a stupid question, but how much changes are there between this driver and the OMAP 3 ISP driver? The elements which pertain to how to write to media controller driver for a capture device have been imbibed from the OMAP3 but as such the code is very different. For example the v4l2 video routines as an almost separate library is a good element we took as a design, but the rest of it is entirely different. > > I understand that not all the blocks are there. Are there any major functional differences between those in Davinci and those in OMAP 3? Could the OMAP 3 ISP driver made support Davinci ISP as well? Yes, there are a lot of major differences between OMAP3 and Dm365/Dm355, both in terms of features, there IP, and the software interface, including all the registers which are entirely different. The closest omap3 would come to is only to DM6446. I do not think OMAP3 driver can be made to support Dm355 and Dm365. It is good to keep the OMAP3 neat and clean to cater for OMAP4 and beyond, and keep the Davinci family separate. The names might look similar and hence confusing for you, but the names can as well be made the same as Dm365 blocks like ISIF and IPIPE and IPIPEIF which are different. > > There are number of possible improvements that still should be made to the OMAP 3 ISP driver so this way both of the driver would easily get them. To mention some: > > - Multiple output pipeline > - Switch to videobuf2 > - Switch to GENIRQ Sure. There is definitely a design element convergence, and overtime I think some of these would get into the core v4l2 infrastructure. > > Cc Laurent. > > Regards, > > -- > Sakari Ailus > sakari.ailus at iki.fi > From Jon.Povey at racelogic.co.uk Mon Jul 4 02:40:35 2011 From: Jon.Povey at racelogic.co.uk (Jon Povey) Date: Mon, 4 Jul 2011 08:40:35 +0100 Subject: Attention TI people: Problems with TI E2E Message-ID: <70E876B0EA86DD4BAF101844BC814DFE0B304E2021@Cloud.RL.local> This is a little OT for the list, but I am having problems using the TI E2E website and there is no obvious support contact for it. There is forum-based support, but it's the forums that are broken. When I try to post or reply it tries to contact sam01-prod.itg.ti.com, there is no itg.ti.com according to TI's DNS servers. This is since the 29th of June or so. Hoping someone from TI will see this and pass it along to the appropriate people. Thanks, -- Jon Povey jon.povey at racelogic.co.uk Racelogic is a limited company registered in England. Registered number 2743719 . Registered Office Unit 10, Swan Business Centre, Osier Way, Buckingham, Bucks, MK18 1TB . The information contained in this electronic mail transmission is intended by Racelogic Ltd for the use of the named individual or entity to which it is directed and may contain information that is confidential or privileged. If you have received this electronic mail transmission in error, please delete it from your system without copying or forwarding it, and notify the sender of the error by reply email so that the sender's address records can be corrected. The views expressed by the sender of this communication do not necessarily represent those of Racelogic Ltd. Please note that Racelogic reserves the right to monitor e-mail communications passing through its network From Jon.Povey at racelogic.co.uk Mon Jul 4 02:47:57 2011 From: Jon.Povey at racelogic.co.uk (Jon Povey) Date: Mon, 4 Jul 2011 08:47:57 +0100 Subject: Attention TI people: Problems with TI E2E In-Reply-To: Message-ID: <70E876B0EA86DD4BAF101844BC814DFE0B304E2028@Cloud.RL.local> Thanks, CC'd to list in case others are having the same problem. Maybe someone could post to the list when things are fixed? Meanwhile if I really have to use E2E I may install another browser. Would rather avoid though. Netagunte, Nagabhushana wrote: > Jon, > > I think it is already being looked into. I believe we will > have solution soon. > > For a time being, use different browser. I faced same problem > with firefox, hence shifted to Opera. Things are fine. I > heard that google-chrome is fine too. > > Hope this helps, > Nag > Racelogic is a limited company registered in England. Registered number 2743719 . Registered Office Unit 10, Swan Business Centre, Osier Way, Buckingham, Bucks, MK18 1TB . The information contained in this electronic mail transmission is intended by Racelogic Ltd for the use of the named individual or entity to which it is directed and may contain information that is confidential or privileged. If you have received this electronic mail transmission in error, please delete it from your system without copying or forwarding it, and notify the sender of the error by reply email so that the sender's address records can be corrected. The views expressed by the sender of this communication do not necessarily represent those of Racelogic Ltd. Please note that Racelogic reserves the right to monitor e-mail communications passing through its network ________________________________________ > From: davinci-linux-open-source-bounces at linux.davincidsp.com > [davinci-linux-open-source-bounces at linux.davincidsp.com] On > Behalf Of Jon Povey [Jon.Povey at racelogic.co.uk] > Sent: Monday, July 04, 2011 1:10 PM > To: davinci-linux-open-source at linux.davincidsp.com > Subject: Attention TI people: Problems with TI E2E > > This is a little OT for the list, but I am having problems using the > TI E2E website and there is no obvious support contact for it. > > There is forum-based support, but it's the forums that are broken. > When I try to post or reply it tries to contact sam01-prod.itg.ti.com, > there is no itg.ti.com according to TI's DNS servers. This is since > the 29th of June or so. > > Hoping someone from TI will see this and pass it along to the > appropriate people. > > Thanks, -- Jon Povey jon.povey at racelogic.co.uk From sudhakar.raj at ti.com Mon Jul 4 08:07:18 2011 From: sudhakar.raj at ti.com (Rajashekhara, Sudhakar) Date: Mon, 4 Jul 2011 18:37:18 +0530 Subject: [PATCH v2] davinci: da850 EVM: read mac address from SPI flash Message-ID: <1309784838-30903-1-git-send-email-sudhakar.raj@ti.com> DA850/OMAP-L138 EMAC driver uses random mac address instead of a fixed one because the mac address is not stuffed into EMAC platform data. This patch provides a function which reads the mac address stored in SPI flash (registered as MTD device) and populates the EMAC platform data. The function which reads the mac address is registered as a callback which gets called upon addition of MTD device. NOTE: In case the MAC address stored in SPI flash is erased, follow the instructions at [1] to restore it. [1] http://processors.wiki.ti.com/index.php/GSG:_OMAP-L138_DVEVM_Additional_Procedures#Restoring_MAC_address_on_SPI_Flash Signed-off-by: Rajashekhara, Sudhakar --- Since v1: Guarded registering the mtd_notifier only when MTD is enabled. Earlier this was handled using mtd_has_partitions() call, but this has been removed in Linux v3.0. arch/arm/mach-davinci/board-da850-evm.c | 28 ++++++++++++++++++++++++++++ 1 files changed, 28 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..f994a1f 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -115,6 +115,23 @@ static struct spi_board_info da850evm_spi_info[] = { }, }; +static void da850_evm_m25p80_notify_add(struct mtd_info *mtd) +{ + char *mac_addr = davinci_soc_info.emac_pdata->mac_addr; + size_t retlen; + + if (!strcmp(mtd->name, "MAC-Address")) { + mtd->read(mtd, 0, ETH_ALEN, &retlen, mac_addr); + if (retlen == ETH_ALEN) + pr_info("Read MAC addr from SPI Flash: %pM\n", + mac_addr); + } +} + +static struct mtd_notifier da850evm_spi_notifier = { + .add = da850_evm_m25p80_notify_add, +}; + static struct mtd_partition da850_evm_norflash_partition[] = { { .name = "bootloaders + env", @@ -1117,6 +1134,15 @@ static __init int da850_evm_init_cpufreq(void) static __init int da850_evm_init_cpufreq(void) { return 0; } #endif +#ifdef CONFIG_MTD +static void da850_evm_register_mtd_user(struct mtd_notifier *notify) +{ + register_mtd_user(notify); +} +#else +static void da850_evm_register_mtd_user(struct mtd_notifier *notify) { } +#endif + static __init void da850_evm_init(void) { int ret; @@ -1237,6 +1263,8 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: spi 1 registration failed: %d\n", ret); + + da850_evm_register_mtd_user(&da850evm_spi_notifier); } #ifdef CONFIG_SERIAL_8250_CONSOLE -- 1.7.1 From laurent.pinchart at ideasonboard.com Mon Jul 4 08:22:37 2011 From: laurent.pinchart at ideasonboard.com (Laurent Pinchart) Date: Mon, 4 Jul 2011 15:22:37 +0200 Subject: [ RFC PATCH 0/8] RFC for Media Controller capture driver for DM365 In-Reply-To: References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> <20110630135736.GK12671@valkosipuli.localdomain> Message-ID: <201107041522.37437.laurent.pinchart@ideasonboard.com> Hi Manjunath, On Monday 04 July 2011 07:58:06 Hadli, Manjunath wrote: > On Thu, Jun 30, 2011 at 19:27:36, Sakari Ailus wrote: [snip] > > I understand that not all the blocks are there. Are there any major > > functional differences between those in Davinci and those in OMAP 3? > > Could the OMAP 3 ISP driver made support Davinci ISP as well? > > Yes, there are a lot of major differences between OMAP3 and Dm365/Dm355, > both in terms of features, there IP, and the software interface, including > all the registers which are entirely different. The closest omap3 would > come to is only to DM6446. I do not think OMAP3 driver can be made to > support Dm355 and Dm365. It is good to keep the OMAP3 neat and clean to > cater for OMAP4 and beyond, and keep the Davinci family separate. The > names might look similar and hence confusing for you, but the names can as > well be made the same as Dm365 blocks like ISIF and IPIPE and IPIPEIF > which are different. The DM6446 ISP is very similar to the OMAP3 ISP, and thus quite different from the DM355/365 ISPs. Should the DM6446 be supported by the OMAP3 ISP driver, and the DM355/365 by this driver ? > > There are number of possible improvements that still should be made to > > the OMAP 3 ISP driver so this way both of the driver would easily get > > them. To mention some: > > > > - Multiple output pipeline > > - Switch to videobuf2 > > - Switch to GENIRQ > > Sure. There is definitely a design element convergence, and overtime I > think some of these would get into the core v4l2 infrastructure. -- Regards, Laurent Pinchart From manjunath.hadli at ti.com Mon Jul 4 09:55:34 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Mon, 4 Jul 2011 20:25:34 +0530 Subject: [ RFC PATCH 0/8] RFC for Media Controller capture driver for DM365 In-Reply-To: <201107041522.37437.laurent.pinchart@ideasonboard.com> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> <20110630135736.GK12671@valkosipuli.localdomain> <201107041522.37437.laurent.pinchart@ideasonboard.com> Message-ID: Thank you Laurent. On Mon, Jul 04, 2011 at 18:52:37, Laurent Pinchart wrote: > Hi Manjunath, > > On Monday 04 July 2011 07:58:06 Hadli, Manjunath wrote: > > On Thu, Jun 30, 2011 at 19:27:36, Sakari Ailus wrote: > > [snip] > > > > I understand that not all the blocks are there. Are there any major > > > functional differences between those in Davinci and those in OMAP 3? > > > Could the OMAP 3 ISP driver made support Davinci ISP as well? > > > > Yes, there are a lot of major differences between OMAP3 and > > Dm365/Dm355, both in terms of features, there IP, and the software > > interface, including all the registers which are entirely different. > > The closest omap3 would come to is only to DM6446. I do not think > > OMAP3 driver can be made to support Dm355 and Dm365. It is good to > > keep the OMAP3 neat and clean to cater for OMAP4 and beyond, and keep > > the Davinci family separate. The names might look similar and hence > > confusing for you, but the names can as well be made the same as Dm365 > > blocks like ISIF and IPIPE and IPIPEIF which are different. > > The DM6446 ISP is very similar to the OMAP3 ISP, and thus quite different from the DM355/365 ISPs. Should the DM6446 be supported by the OMAP3 ISP driver, and the DM355/365 by this driver ? DM6446 capture IP is in some respects similar to OMAP3 for some features, but there are a large number of differences also (MMU, VRFB, a lot of display interfaces etc). Having a single driver catering to Since DM6446 and OMAP3 is going to be unwieldy. Also, DM6446 belongs to the Davinci family of chips, it should be clubbed with the other Davinci SoCs as it will simplify a lot of other things including directory subdirectory/file naming, organization of machine/platform code etc among other things. Other than Video a lot of other system registers and features which are common with the rest of Davinci SoCs which if treated together is a good thing, whereas OMAP3 can be modified and developed with those on the OMAP family (OMAP4 for ex). > > > > There are number of possible improvements that still should be made > > > to the OMAP 3 ISP driver so this way both of the driver would easily > > > get them. To mention some: > > > > > > - Multiple output pipeline > > > - Switch to videobuf2 > > > - Switch to GENIRQ > > > > Sure. There is definitely a design element convergence, and overtime I > > think some of these would get into the core v4l2 infrastructure. > > -- > Regards, > > Laurent Pinchart > From sakari.ailus at iki.fi Mon Jul 4 11:13:09 2011 From: sakari.ailus at iki.fi (Sakari Ailus) Date: Mon, 04 Jul 2011 19:13:09 +0300 Subject: [ RFC PATCH 0/8] RFC for Media Controller capture driver for DM365 In-Reply-To: References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> <20110630135736.GK12671@valkosipuli.localdomain> <201107041522.37437.laurent.pinchart@ideasonboard.com> Message-ID: <4E11E695.9090508@iki.fi> Hadli, Manjunath wrote: > Thank you Laurent. Hi Manjunath, > On Mon, Jul 04, 2011 at 18:52:37, Laurent Pinchart wrote: >> Hi Manjunath, >> >> On Monday 04 July 2011 07:58:06 Hadli, Manjunath wrote: >>> On Thu, Jun 30, 2011 at 19:27:36, Sakari Ailus wrote: >> >> [snip] >> >>>> I understand that not all the blocks are there. Are there any >>>> major functional differences between those in Davinci and those >>>> in OMAP 3? Could the OMAP 3 ISP driver made support Davinci ISP >>>> as well? >>> >>> Yes, there are a lot of major differences between OMAP3 and >>> Dm365/Dm355, both in terms of features, there IP, and the >>> software interface, including all the registers which are >>> entirely different. The closest omap3 would come to is only to >>> DM6446. I do not think OMAP3 driver can be made to support Dm355 >>> and Dm365. It is good to keep the OMAP3 neat and clean to cater >>> for OMAP4 and beyond, and keep the Davinci family separate. The >>> names might look similar and hence confusing for you, but the >>> names can as well be made the same as Dm365 blocks like ISIF and >>> IPIPE and IPIPEIF which are different. >> >> The DM6446 ISP is very similar to the OMAP3 ISP, and thus quite >> different from the DM355/365 ISPs. Should the DM6446 be supported >> by the OMAP3 ISP driver, and the DM355/365 by this driver ? > > DM6446 capture IP is in some respects similar to OMAP3 for some > features, but there are a large number of differences also (MMU, > VRFB, a lot of display interfaces etc). Having a single driver > catering to Since DM6446 and OMAP3 is going to be unwieldy. Also, > DM6446 belongs to the Davinci family of chips, it should be clubbed > with the other Davinci SoCs as it will simplify a lot of other things > including directory subdirectory/file naming, organization of > machine/platform code etc among other things. Other than Video a lot > of other system registers and features which are common with the rest > of Davinci SoCs which if treated together is a good thing, whereas > OMAP3 can be modified and developed with those on the OMAP family > (OMAP4 for ex). Thanks for the clarifications. What about the DM3730? As far as I understand, the ISP on that one is supported by the OMAP 3 ISP driver. But it looks like that it's more continuation for the OMAP family of the chips than the Davinci. I glanced at the DM6446 documentation and at the register level the interface looks somewhat different although some register names are the same. I didn't found a proper TRM which would be as detailed as the OMAP ones --- does TI have one available in public? OMAP 4 has a quite different ISS --- which the ISP is a part of, and which also is very different to the OMAP 3 one --- so it's unlikely that the same driver would support OMAP 3 and OMAP 4 ISPs. Kind regards, -- Sakari Ailus sakari.ailus at iki.fi From nsekhar at ti.com Tue Jul 5 00:10:58 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Tue, 5 Jul 2011 10:40:58 +0530 Subject: [RFC/RFT 0/2] gpio: move tnetv107x gpio support to drivers/gpio Message-ID: This patch series moves the tnetv107x GPIO support from arch/arm/mach-davinci/ to drivers/gpio. I do not have the tnetv107x EVM hardware so I have not been able to test this. Any Tested-bys or Acked-bys are welcome from those who have the hardware. The patches apply to latest of gpio/next on Grant's tree. Sekhar Nori (2): gpio/basic_mmio: add support for enable register davinci: use generic memory mapped gpio for tnetv107x arch/arm/mach-davinci/Kconfig | 1 + arch/arm/mach-davinci/Makefile | 1 - arch/arm/mach-davinci/devices-tnetv107x.c | 68 ++++++++++ arch/arm/mach-davinci/gpio-tnetv107x.c | 205 ----------------------------- arch/arm/mach-davinci/tnetv107x.c | 3 - drivers/gpio/gpio-ep93xx.c | 2 +- drivers/gpio/gpio-generic.c | 45 ++++++- drivers/gpio/gpio-mxc.c | 2 +- drivers/gpio/gpio-mxs.c | 2 +- include/linux/basic_mmio_gpio.h | 5 + 10 files changed, 121 insertions(+), 213 deletions(-) delete mode 100644 arch/arm/mach-davinci/gpio-tnetv107x.c -- 1.7.3.2 From nsekhar at ti.com Tue Jul 5 00:11:00 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Tue, 5 Jul 2011 10:41:00 +0530 Subject: [RFC/RFT 2/2] davinci: use generic memory mapped gpio for tnetv107x In-Reply-To: References: Message-ID: <6639b07562e3e6643dd07d5ed3907cb5158ce16b.1309840042.git.nsekhar@ti.com> The GPIO controller on TNETV107x SoC can use the generic memory mapped GPIO driver. Shift to the generic driver instead of the private implementation. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/Kconfig | 1 + arch/arm/mach-davinci/Makefile | 1 - arch/arm/mach-davinci/devices-tnetv107x.c | 68 ++++++++++ arch/arm/mach-davinci/gpio-tnetv107x.c | 205 ----------------------------- arch/arm/mach-davinci/tnetv107x.c | 3 - 5 files changed, 69 insertions(+), 209 deletions(-) delete mode 100644 arch/arm/mach-davinci/gpio-tnetv107x.c diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index c0deaca..ec0c6d3 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -53,6 +53,7 @@ config ARCH_DAVINCI_DM365 config ARCH_DAVINCI_TNETV107X select CPU_V6 select CP_INTC + select GPIO_GENERIC_PLATFORM bool "TNETV107X based system" comment "DaVinci Board Type" diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile index 0b87a1c..40557c8 100644 --- a/arch/arm/mach-davinci/Makefile +++ b/arch/arm/mach-davinci/Makefile @@ -17,7 +17,6 @@ obj-$(CONFIG_ARCH_DAVINCI_DM365) += dm365.o devices.o obj-$(CONFIG_ARCH_DAVINCI_DA830) += da830.o devices-da8xx.o obj-$(CONFIG_ARCH_DAVINCI_DA850) += da850.o devices-da8xx.o obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += tnetv107x.o devices-tnetv107x.o -obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o obj-$(CONFIG_AINTC) += irq.o obj-$(CONFIG_CP_INTC) += cp_intc.o diff --git a/arch/arm/mach-davinci/devices-tnetv107x.c b/arch/arm/mach-davinci/devices-tnetv107x.c index 6162cae..eab43b1 100644 --- a/arch/arm/mach-davinci/devices-tnetv107x.c +++ b/arch/arm/mach-davinci/devices-tnetv107x.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -31,6 +32,7 @@ #define TNETV107X_TPTC0_BASE 0x01c10000 #define TNETV107X_TPTC1_BASE 0x01c10400 #define TNETV107X_WDOG_BASE 0x08086700 +#define TNETV107X_GPIO_BASE 0x08088000 #define TNETV107X_TSC_BASE 0x08088500 #define TNETV107X_SDIO0_BASE 0x08088700 #define TNETV107X_SDIO1_BASE 0x08088800 @@ -362,11 +364,77 @@ static struct platform_device ssp_device = { .resource = ssp_resources, }; +#define TNETV107X_N_GPIO_DEV DIV_ROUND_UP(TNETV107X_N_GPIO, 32) + +static struct resource gpio_resources[TNETV107X_N_GPIO_DEV][4] = { + [0 ... TNETV107X_N_GPIO_DEV - 1] = { + { + .name = "dat", + .start = TNETV107X_GPIO_BASE + 0x4, + .end = TNETV107X_GPIO_BASE + 0x4 + 0x4 - 1, + }, + { + .name = "set", + .start = TNETV107X_GPIO_BASE + 0x10, + .end = TNETV107X_GPIO_BASE + 0x10 + 0x4 - 1, + }, + { + .name = "dirin", + .start = TNETV107X_GPIO_BASE + 0x1c, + .end = TNETV107X_GPIO_BASE + 0x1c + 0x4 - 1, + }, + { + .name = "en", + .start = TNETV107X_GPIO_BASE + 0x28, + .end = TNETV107X_GPIO_BASE + 0x28 + 0x4 - 1, + }, + }, +}; + +static struct platform_device gpio_device[] = { + [0 ... TNETV107X_N_GPIO_DEV - 1] = { + .name = "basic-mmio-gpio", + .num_resources = 4, + }, +}; + +static struct bgpio_pdata gpio_pdata[TNETV107X_N_GPIO_DEV]; + +static void __init tnetv107x_gpio_init(void) +{ + int i, j; + + for (i = 1; i < TNETV107X_N_GPIO_DEV; i++) { + for (j = 0; j < 4; j++) { + gpio_resources[i][j].start += 0x4 * i; + gpio_resources[i][j].end += 0x4 * i; + } + } + + for (i = 0; i < TNETV107X_N_GPIO_DEV; i++) { + int base = i * 32; + + gpio_device[i].id = i; + gpio_device[i].resource = gpio_resources[i]; + + gpio_pdata[i].base = base; + gpio_pdata[i].ngpio = TNETV107X_N_GPIO - base; + if (gpio_pdata[i].ngpio > 32) + gpio_pdata[i].ngpio = 32; + + gpio_device[i].dev.platform_data = &gpio_pdata[i]; + + platform_device_register(&gpio_device[i]); + } +} + void __init tnetv107x_devices_init(struct tnetv107x_device_info *info) { int i, error; struct clk *tsc_clk; + tnetv107x_gpio_init(); + /* * The reset defaults for tnetv107x tsc clock divider is set too high. * This forces the clock down to a range that allows the ADC to diff --git a/arch/arm/mach-davinci/gpio-tnetv107x.c b/arch/arm/mach-davinci/gpio-tnetv107x.c deleted file mode 100644 index 3fa3e28..0000000 --- a/arch/arm/mach-davinci/gpio-tnetv107x.c +++ /dev/null @@ -1,205 +0,0 @@ -/* - * Texas Instruments TNETV107X GPIO Controller - * - * Copyright (C) 2010 Texas Instruments - * - * 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 -#include -#include - -#include -#include - -struct tnetv107x_gpio_regs { - u32 idver; - u32 data_in[3]; - u32 data_out[3]; - u32 direction[3]; - u32 enable[3]; -}; - -#define gpio_reg_index(gpio) ((gpio) >> 5) -#define gpio_reg_bit(gpio) BIT((gpio) & 0x1f) - -#define gpio_reg_rmw(reg, mask, val) \ - __raw_writel((__raw_readl(reg) & ~(mask)) | (val), (reg)) - -#define gpio_reg_set_bit(reg, gpio) \ - gpio_reg_rmw((reg) + gpio_reg_index(gpio), 0, gpio_reg_bit(gpio)) - -#define gpio_reg_clear_bit(reg, gpio) \ - gpio_reg_rmw((reg) + gpio_reg_index(gpio), gpio_reg_bit(gpio), 0) - -#define gpio_reg_get_bit(reg, gpio) \ - (__raw_readl((reg) + gpio_reg_index(gpio)) & gpio_reg_bit(gpio)) - -#define chip2controller(chip) \ - container_of(chip, struct davinci_gpio_controller, chip) - -#define TNETV107X_GPIO_CTLRS DIV_ROUND_UP(TNETV107X_N_GPIO, 32) - -static struct davinci_gpio_controller chips[TNETV107X_GPIO_CTLRS]; - -static int tnetv107x_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - struct davinci_gpio_controller *ctlr = chip2controller(chip); - struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; - unsigned gpio = chip->base + offset; - unsigned long flags; - - spin_lock_irqsave(&ctlr->lock, flags); - - gpio_reg_set_bit(regs->enable, gpio); - - spin_unlock_irqrestore(&ctlr->lock, flags); - - return 0; -} - -static void tnetv107x_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - struct davinci_gpio_controller *ctlr = chip2controller(chip); - struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; - unsigned gpio = chip->base + offset; - unsigned long flags; - - spin_lock_irqsave(&ctlr->lock, flags); - - gpio_reg_clear_bit(regs->enable, gpio); - - spin_unlock_irqrestore(&ctlr->lock, flags); -} - -static int tnetv107x_gpio_dir_in(struct gpio_chip *chip, unsigned offset) -{ - struct davinci_gpio_controller *ctlr = chip2controller(chip); - struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; - unsigned gpio = chip->base + offset; - unsigned long flags; - - spin_lock_irqsave(&ctlr->lock, flags); - - gpio_reg_set_bit(regs->direction, gpio); - - spin_unlock_irqrestore(&ctlr->lock, flags); - - return 0; -} - -static int tnetv107x_gpio_dir_out(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct davinci_gpio_controller *ctlr = chip2controller(chip); - struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; - unsigned gpio = chip->base + offset; - unsigned long flags; - - spin_lock_irqsave(&ctlr->lock, flags); - - if (value) - gpio_reg_set_bit(regs->data_out, gpio); - else - gpio_reg_clear_bit(regs->data_out, gpio); - - gpio_reg_clear_bit(regs->direction, gpio); - - spin_unlock_irqrestore(&ctlr->lock, flags); - - return 0; -} - -static int tnetv107x_gpio_get(struct gpio_chip *chip, unsigned offset) -{ - struct davinci_gpio_controller *ctlr = chip2controller(chip); - struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; - unsigned gpio = chip->base + offset; - int ret; - - ret = gpio_reg_get_bit(regs->data_in, gpio); - - return ret ? 1 : 0; -} - -static void tnetv107x_gpio_set(struct gpio_chip *chip, - unsigned offset, int value) -{ - struct davinci_gpio_controller *ctlr = chip2controller(chip); - struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; - unsigned gpio = chip->base + offset; - unsigned long flags; - - spin_lock_irqsave(&ctlr->lock, flags); - - if (value) - gpio_reg_set_bit(regs->data_out, gpio); - else - gpio_reg_clear_bit(regs->data_out, gpio); - - spin_unlock_irqrestore(&ctlr->lock, flags); -} - -static int __init tnetv107x_gpio_setup(void) -{ - int i, base; - unsigned ngpio; - struct davinci_soc_info *soc_info = &davinci_soc_info; - struct tnetv107x_gpio_regs *regs; - struct davinci_gpio_controller *ctlr; - - if (soc_info->gpio_type != GPIO_TYPE_TNETV107X) - return 0; - - ngpio = soc_info->gpio_num; - if (ngpio == 0) { - pr_err("GPIO setup: how many GPIOs?\n"); - return -EINVAL; - } - - if (WARN_ON(TNETV107X_N_GPIO < ngpio)) - ngpio = TNETV107X_N_GPIO; - - regs = ioremap(soc_info->gpio_base, SZ_4K); - if (WARN_ON(!regs)) - return -EINVAL; - - for (i = 0, base = 0; base < ngpio; i++, base += 32) { - ctlr = &chips[i]; - - ctlr->chip.label = "tnetv107x"; - ctlr->chip.can_sleep = 0; - ctlr->chip.base = base; - ctlr->chip.ngpio = ngpio - base; - if (ctlr->chip.ngpio > 32) - ctlr->chip.ngpio = 32; - - ctlr->chip.request = tnetv107x_gpio_request; - ctlr->chip.free = tnetv107x_gpio_free; - ctlr->chip.direction_input = tnetv107x_gpio_dir_in; - ctlr->chip.get = tnetv107x_gpio_get; - ctlr->chip.direction_output = tnetv107x_gpio_dir_out; - ctlr->chip.set = tnetv107x_gpio_set; - - spin_lock_init(&ctlr->lock); - - ctlr->regs = regs; - ctlr->set_data = ®s->data_out[i]; - ctlr->clr_data = ®s->data_out[i]; - ctlr->in_data = ®s->data_in[i]; - - gpiochip_add(&ctlr->chip); - } - - soc_info->gpio_ctlrs = chips; - soc_info->gpio_ctlrs_num = DIV_ROUND_UP(ngpio, 32); - return 0; -} -pure_initcall(tnetv107x_gpio_setup); diff --git a/arch/arm/mach-davinci/tnetv107x.c b/arch/arm/mach-davinci/tnetv107x.c index 1b28fdd..11399d4 100644 --- a/arch/arm/mach-davinci/tnetv107x.c +++ b/arch/arm/mach-davinci/tnetv107x.c @@ -39,7 +39,6 @@ #define TNETV107X_TIMER0_BASE 0x08086500 #define TNETV107X_TIMER1_BASE 0x08086600 #define TNETV107X_CHIP_CFG_BASE 0x08087000 -#define TNETV107X_GPIO_BASE 0x08088000 #define TNETV107X_CLOCK_CONTROL_BASE 0x0808a000 #define TNETV107X_PSC_BASE 0x0808b000 @@ -746,9 +745,7 @@ static struct davinci_soc_info tnetv107x_soc_info = { .intc_irq_prios = irq_prios, .intc_irq_num = TNETV107X_N_CP_INTC_IRQ, .intc_host_map = intc_host_map, - .gpio_base = TNETV107X_GPIO_BASE, .gpio_type = GPIO_TYPE_TNETV107X, - .gpio_num = TNETV107X_N_GPIO, .timer_info = &timer_info, .serial_dev = &tnetv107x_serial_device, .reset = tnetv107x_watchdog_reset, -- 1.7.3.2 From nsekhar at ti.com Tue Jul 5 00:10:59 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Tue, 5 Jul 2011 10:40:59 +0530 Subject: [RFC/RFT 1/2] gpio/basic_mmio: add support for enable register In-Reply-To: References: Message-ID: <83915224c24e43224272b1bf570cddb9545279a6.1309840042.git.nsekhar@ti.com> Some GPIO controllers have an enable register which needs to be written to before a GPIO can be used. Add support for enabling the GPIO. At this time inverted logic for enabling the GPIO is not supported. This can be done by adding a disable register as and when a controller with this comes along. Signed-off-by: Sekhar Nori --- drivers/gpio/gpio-ep93xx.c | 2 +- drivers/gpio/gpio-generic.c | 45 ++++++++++++++++++++++++++++++++++++++- drivers/gpio/gpio-mxc.c | 2 +- drivers/gpio/gpio-mxs.c | 2 +- include/linux/basic_mmio_gpio.h | 5 ++++ 5 files changed, 52 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-ep93xx.c b/drivers/gpio/gpio-ep93xx.c index 3bfd341..8ed498a 100644 --- a/drivers/gpio/gpio-ep93xx.c +++ b/drivers/gpio/gpio-ep93xx.c @@ -314,7 +314,7 @@ static int ep93xx_gpio_add_bank(struct bgpio_chip *bgc, struct device *dev, void __iomem *dir = mmio_base + bank->dir; int err; - err = bgpio_init(bgc, dev, 1, data, NULL, NULL, dir, NULL, false); + err = bgpio_init(bgc, dev, 1, data, NULL, NULL, dir, NULL, NULL, false); if (err) return err; diff --git a/drivers/gpio/gpio-generic.c b/drivers/gpio/gpio-generic.c index 231714d..cf7d596 100644 --- a/drivers/gpio/gpio-generic.c +++ b/drivers/gpio/gpio-generic.c @@ -247,6 +247,34 @@ static int bgpio_dir_out_inv(struct gpio_chip *gc, unsigned int gpio, int val) return 0; } +static int bgpio_request(struct gpio_chip *gc, unsigned int gpio) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned long flags; + + spin_lock_irqsave(&bgc->lock, flags); + + bgc->en |= bgc->pin2mask(bgc, gpio); + bgc->write_reg(bgc->reg_en, bgc->en); + + spin_unlock_irqrestore(&bgc->lock, flags); + + return 0; +} + +static void bgpio_free(struct gpio_chip *gc, unsigned int gpio) +{ + struct bgpio_chip *bgc = to_bgpio_chip(gc); + unsigned long flags; + + spin_lock_irqsave(&bgc->lock, flags); + + bgc->en &= ~bgc->pin2mask(bgc, gpio); + bgc->write_reg(bgc->reg_en, bgc->en); + + spin_unlock_irqrestore(&bgc->lock, flags); +} + static int bgpio_setup_accessors(struct device *dev, struct bgpio_chip *bgc, bool be) @@ -302,6 +330,10 @@ static int bgpio_setup_accessors(struct device *dev, * indicates the GPIO is an output. * - an input direction register (named "dirin") where a 1 bit indicates * the GPIO is an input. + * + * To enable and disable a GPIO at the time of requesting it there is a + * a simple enable register supported where a 1 bit indicates that the GPIO + * is enabled. */ static int bgpio_setup_io(struct bgpio_chip *bgc, void __iomem *dat, @@ -369,6 +401,7 @@ int __devinit bgpio_init(struct bgpio_chip *bgc, void __iomem *clr, void __iomem *dirout, void __iomem *dirin, + void __iomem *en, bool big_endian) { int ret; @@ -398,6 +431,11 @@ int __devinit bgpio_init(struct bgpio_chip *bgc, if (ret) return ret; + if (en) { + bgc->gc.request = bgpio_request; + bgc->gc.free = bgpio_free; + } + bgc->data = bgc->read_reg(bgc->reg_dat); return ret; @@ -453,6 +491,7 @@ static int __devinit bgpio_pdev_probe(struct platform_device *pdev) void __iomem *clr; void __iomem *dirout; void __iomem *dirin; + void __iomem *en; unsigned long sz; bool be; int err; @@ -485,13 +524,17 @@ static int __devinit bgpio_pdev_probe(struct platform_device *pdev) if (err) return err; + en = bgpio_map(pdev, "en", sz, &err); + if (err) + return err; + be = !strcmp(platform_get_device_id(pdev)->name, "basic-mmio-gpio-be"); bgc = devm_kzalloc(&pdev->dev, sizeof(*bgc), GFP_KERNEL); if (!bgc) return -ENOMEM; - err = bgpio_init(bgc, dev, sz, dat, set, clr, dirout, dirin, be); + err = bgpio_init(bgc, dev, sz, dat, set, clr, dirout, dirin, en, be); if (err) return err; diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 2f6a81b..5ce98c6 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -300,7 +300,7 @@ static int __devinit mxc_gpio_probe(struct platform_device *pdev) err = bgpio_init(&port->bgc, &pdev->dev, 4, port->base + GPIO_PSR, port->base + GPIO_DR, NULL, - port->base + GPIO_GDIR, NULL, false); + port->base + GPIO_GDIR, NULL, NULL, false); if (err) goto out_iounmap; diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index d8cafba..f3b78bf 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -241,7 +241,7 @@ static int __devinit mxs_gpio_probe(struct platform_device *pdev) err = bgpio_init(&port->bgc, &pdev->dev, 4, port->base + PINCTRL_DIN(port->id), port->base + PINCTRL_DOUT(port->id), NULL, - port->base + PINCTRL_DOE(port->id), NULL, false); + port->base + PINCTRL_DOE(port->id), NULL, NULL, false); if (err) goto out_iounmap; diff --git a/include/linux/basic_mmio_gpio.h b/include/linux/basic_mmio_gpio.h index 98999cf..fc2e1cc 100644 --- a/include/linux/basic_mmio_gpio.h +++ b/include/linux/basic_mmio_gpio.h @@ -35,6 +35,7 @@ struct bgpio_chip { void __iomem *reg_set; void __iomem *reg_clr; void __iomem *reg_dir; + void __iomem *reg_en; /* Number of bits (GPIOs): * 8. */ int bits; @@ -56,6 +57,9 @@ struct bgpio_chip { /* Shadowed direction registers to clear/set direction safely. */ unsigned long dir; + + /* Shadowed enable register to enable/disable safely. */ + unsigned long en; }; static inline struct bgpio_chip *to_bgpio_chip(struct gpio_chip *gc) @@ -72,6 +76,7 @@ int __devinit bgpio_init(struct bgpio_chip *bgc, void __iomem *clr, void __iomem *dirout, void __iomem *dirin, + void __iomem *en, bool big_endian); #endif /* __BASIC_MMIO_GPIO_H */ -- 1.7.3.2 From nsekhar at ti.com Tue Jul 5 00:29:46 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 5 Jul 2011 10:59:46 +0530 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-6-git-send-email-ohad@wizery.com> <4E035B78.3080200@mvista.com> <4E04A9AE.3030801@mvista.com> Message-ID: Hi Mark, On Tue, Jun 28, 2011 at 00:01:41, Grosen, Mark wrote: > > From: Nori, Sekhar > > Sent: Friday, June 24, 2011 8:44 AM > > To assert/de-assert local reset when enabling or disabling PSC, > > you could use a flag in the clock structure to indicate the need > > for this. This way, if there is any other module needing a local > > reset, it can just define the same flag. Similarly, if the DSP > > does not need a local reset on a particular platform, that > > platform can simply skip the flag. > > > > This can be done in a manner similar to how the support for > > a forced transition PSC was added here: > > > > https://patchwork.kernel.org/patch/662941/ > > Yes, I like this idea - much cleaner. For example, the start() method > becomes (pseudo-code): > > start() > { > /* bootaddrreg derived from platform data */ > bootaddrreg = boot_address; > > clk_enable(); > } > > Referring to your patch above, would it be better to just pass > the flags into the davinci_psc_config() function rather than breaking out more > arguments for each flag? I did dwell on this quite a bit while writing that patch, but finally decided on passing an argument instead since I was not sure if there are going to be more modifiers required. Now that you have the need for one more flag, I think we should go ahead and pass the flags directly to davinci_psc_config(). My original patch is not upstream yet. I will re-spin it and you can build on top of it. Thanks, Sekhar From nsekhar at ti.com Tue Jul 5 00:34:59 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 5 Jul 2011 11:04:59 +0530 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-6-git-send-email-ohad@wizery.com> <4E035B78.3080200@mvista.com> <4E04A9AE.3030801@mvista.com> Message-ID: Hi Mark, On Tue, Jun 28, 2011 at 00:01:41, Grosen, Mark wrote: > > From: Nori, Sekhar > > Sent: Friday, June 24, 2011 8:44 AM > > > > Hi Mark, > > Sekhar, thanks for your feedback and ideas. Comments below. > > Mark > > > Since procedure to set the boot address varies across DaVinci > > platforms, you could have a callback populated in platform data > > which will be implemented differently for original DaVinci and > > DA8xx devices. > > I looked at DM6467 and it's the same as OMAPL13x, except at a different > address. Rather than a callback, it could be just an address in the > platform data. Sounds okay as long as _all_ the DaVinci devices have the same bit to be set. Plus, I hope there are no other users of the register so that there is no race with other platform code using the same register. Thanks, Sekhar From nsekhar at ti.com Tue Jul 5 03:32:00 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 5 Jul 2011 14:02:00 +0530 Subject: [RFC/RFT 1/2] gpio/basic_mmio: add support for enable register In-Reply-To: <4E12AC10.9020206@gmail.com> References: <83915224c24e43224272b1bf570cddb9545279a6.1309840042.git.nsekhar@ti.com> <4E12AC10.9020206@gmail.com> Message-ID: Hi Ryan, On Tue, Jul 05, 2011 at 11:45:44, Ryan Mallon wrote: > On 05/07/11 15:10, Sekhar Nori wrote: > > Some GPIO controllers have an enable register > > which needs to be written to before a GPIO > > can be used. > > > > Add support for enabling the GPIO. At this > > time inverted logic for enabling the GPIO > > is not supported. This can be done by adding > > a disable register as and when a controller > > with this comes along. > > > > Signed-off-by: Sekhar Nori > > --- > > > > > > static int bgpio_setup_io(struct bgpio_chip *bgc, > > void __iomem *dat, > > @@ -369,6 +401,7 @@ int __devinit bgpio_init(struct bgpio_chip *bgc, > > void __iomem *clr, > > void __iomem *dirout, > > void __iomem *dirin, > > + void __iomem *en, > > bool big_endian) > > The arguments to this function are getting a bit unwieldy :-). Maybe we > need to introduce something like: > > struct bgpio_chip_info { > struct device *dev; > unsigned long sz; > void __iomem *dat; > void __iomem *set; > void __iomem *clr; > void __iomem *dirout; > void __iomem *dirin; > void __iomem *en; > bool big_endian; > }; > > and pass that to bgpio_init instead. It would have the added benefits of > making the drivers more readable and that bgpio_chip_info structs in the > drivers can probably be marked __initdata also. > > Since you are already having to touch all of the call sites for > bgpio_init this could be done as a separate patch along with this series. Agreed. I can do this if Grant too thinks this is a pre-requisite to adding new functionality. Thanks, Sekhar From prakash.pm at ti.com Tue Jul 5 05:21:20 2011 From: prakash.pm at ti.com (Manjunathappa, Prakash) Date: Tue, 5 Jul 2011 15:51:20 +0530 Subject: [PATCH v2] video: da8xx-fb: Interrupt configuration of revised LCDC IP Message-ID: <1309861280-32240-1-git-send-email-prakash.pm@ti.com> An upcoming SoC of TI comes with an LCD controller which is an updated version of that found on TI's DA850 SoC. The da8xx-fb driver can support this LCD with some enhancements. This patch adds support for updated interrupt configuration on the new SoC. 1) Registers for setting and clearing interrupts are different. 2) Raw and masked status registers are different. The updates have been tested on an emulation platform for new features and the patch has been tested on DA850 platform to make sure nothing existing breaks. Signed-off-by: Manjunathappa, Prakash --- Since v1: 1)Added verification details in commit message. 2)Removed random white spaces in code. drivers/video/da8xx-fb.c | 151 +++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 142 insertions(+), 9 deletions(-) diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index fcdac87..620f1c3 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -35,6 +35,9 @@ #define DRIVER_NAME "da8xx_lcdc" +#define LCD_VERSION_1 1 +#define LCD_VERSION_2 2 + /* LCD Status Register */ #define LCD_END_OF_FRAME1 BIT(9) #define LCD_END_OF_FRAME0 BIT(8) @@ -49,7 +52,9 @@ #define LCD_DMA_BURST_4 0x2 #define LCD_DMA_BURST_8 0x3 #define LCD_DMA_BURST_16 0x4 -#define LCD_END_OF_FRAME_INT_ENA BIT(2) +#define LCD_V1_END_OF_FRAME_INT_ENA BIT(2) +#define LCD_V2_END_OF_FRAME0_INT_ENA BIT(8) +#define LCD_V2_END_OF_FRAME1_INT_ENA BIT(9) #define LCD_DUAL_FRAME_BUFFER_ENABLE BIT(0) /* LCD Control Register */ @@ -65,12 +70,18 @@ #define LCD_MONO_8BIT_MODE BIT(9) #define LCD_RASTER_ORDER BIT(8) #define LCD_TFT_MODE BIT(7) -#define LCD_UNDERFLOW_INT_ENA BIT(6) -#define LCD_PL_ENABLE BIT(4) +#define LCD_V1_UNDERFLOW_INT_ENA BIT(6) +#define LCD_V2_UNDERFLOW_INT_ENA BIT(5) +#define LCD_V1_PL_INT_ENA BIT(4) +#define LCD_V2_PL_INT_ENA BIT(6) #define LCD_MONOCHROME_MODE BIT(1) #define LCD_RASTER_ENABLE BIT(0) #define LCD_TFT_ALT_ENABLE BIT(23) #define LCD_STN_565_ENABLE BIT(24) +#define LCD_V2_DMA_CLK_EN BIT(2) +#define LCD_V2_LIDD_CLK_EN BIT(1) +#define LCD_V2_CORE_CLK_EN BIT(0) +#define LCD_V2_LPP_B10 26 /* LCD Raster Timing 2 Register */ #define LCD_AC_BIAS_TRANSITIONS_PER_INT(x) ((x) << 16) @@ -82,6 +93,7 @@ #define LCD_INVERT_FRAME_CLOCK BIT(20) /* LCD Block */ +#define LCD_PID_REG 0x0 #define LCD_CTRL_REG 0x4 #define LCD_STAT_REG 0x8 #define LCD_RASTER_CTRL_REG 0x28 @@ -94,6 +106,17 @@ #define LCD_DMA_FRM_BUF_BASE_ADDR_1_REG 0x4C #define LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG 0x50 +/* Interrupt Registers available only in Version 2 */ +#define LCD_RAW_STAT_REG 0x58 +#define LCD_MASKED_STAT_REG 0x5c +#define LCD_INT_ENABLE_SET_REG 0x60 +#define LCD_INT_ENABLE_CLR_REG 0x64 +#define LCD_END_OF_INT_IND_REG 0x68 + +/* Clock registers available only on Version 2 */ +#define LCD_CLK_ENABLE_REG 0x6c +#define LCD_CLK_RESET_REG 0x70 + #define LCD_NUM_BUFFERS 2 #define WSI_TIMEOUT 50 @@ -105,6 +128,8 @@ static resource_size_t da8xx_fb_reg_base; static struct resource *lcdc_regs; +static unsigned int lcd_revision; +static irq_handler_t lcdc_irq_handler; static inline unsigned int lcdc_read(unsigned int addr) { @@ -240,6 +265,7 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par) u32 end; u32 reg_ras; u32 reg_dma; + u32 reg_int; /* init reg to clear PLM (loading mode) fields */ reg_ras = lcdc_read(LCD_RASTER_CTRL_REG); @@ -252,7 +278,14 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par) end = par->dma_end; reg_ras |= LCD_PALETTE_LOAD_MODE(DATA_ONLY); - reg_dma |= LCD_END_OF_FRAME_INT_ENA; + if (lcd_revision == LCD_VERSION_1) { + reg_dma |= LCD_V1_END_OF_FRAME_INT_ENA; + } else { + reg_int = lcdc_read(LCD_INT_ENABLE_SET_REG) | + LCD_V2_END_OF_FRAME0_INT_ENA | + LCD_V2_END_OF_FRAME1_INT_ENA; + lcdc_write(reg_int, LCD_INT_ENABLE_SET_REG); + } reg_dma |= LCD_DUAL_FRAME_BUFFER_ENABLE; lcdc_write(start, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); @@ -264,7 +297,14 @@ static void lcd_blit(int load_mode, struct da8xx_fb_par *par) end = start + par->palette_sz - 1; reg_ras |= LCD_PALETTE_LOAD_MODE(PALETTE_ONLY); - reg_ras |= LCD_PL_ENABLE; + + if (lcd_revision == LCD_VERSION_1) { + reg_ras |= LCD_V1_PL_INT_ENA; + } else { + reg_int = lcdc_read(LCD_INT_ENABLE_SET_REG) | + LCD_V2_PL_INT_ENA; + lcdc_write(reg_int, LCD_INT_ENABLE_SET_REG); + } lcdc_write(start, LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); lcdc_write(end, LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG); @@ -348,6 +388,7 @@ static void lcd_cfg_vertical_sync(int back_porch, int pulse_width, static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) { u32 reg; + u32 reg_int; reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(LCD_TFT_MODE | LCD_MONO_8BIT_MODE | @@ -375,7 +416,13 @@ static int lcd_cfg_display(const struct lcd_ctrl_config *cfg) } /* enable additional interrupts here */ - reg |= LCD_UNDERFLOW_INT_ENA; + if (lcd_revision == LCD_VERSION_1) { + reg |= LCD_V1_UNDERFLOW_INT_ENA; + } else { + reg_int = lcdc_read(LCD_INT_ENABLE_SET_REG) | + LCD_V2_UNDERFLOW_INT_ENA; + lcdc_write(reg_int, LCD_INT_ENABLE_SET_REG); + } lcdc_write(reg, LCD_RASTER_CTRL_REG); @@ -511,6 +558,9 @@ static void lcd_reset(struct da8xx_fb_par *par) /* DMA has to be disabled */ lcdc_write(0, LCD_DMA_CTRL_REG); lcdc_write(0, LCD_RASTER_CTRL_REG); + + if (lcd_revision == LCD_VERSION_2) + lcdc_write(0, LCD_INT_ENABLE_SET_REG); } static void lcd_calc_clk_divider(struct da8xx_fb_par *par) @@ -523,6 +573,11 @@ static void lcd_calc_clk_divider(struct da8xx_fb_par *par) /* Configure the LCD clock divisor. */ lcdc_write(LCD_CLK_DIVISOR(div) | (LCD_RASTER_MODE & 0x1), LCD_CTRL_REG); + + if (lcd_revision == LCD_VERSION_2) + lcdc_write(LCD_V2_DMA_CLK_EN | LCD_V2_LIDD_CLK_EN | + LCD_V2_CORE_CLK_EN, LCD_CLK_ENABLE_REG); + } static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, @@ -583,7 +638,63 @@ static int lcd_init(struct da8xx_fb_par *par, const struct lcd_ctrl_config *cfg, return 0; } -static irqreturn_t lcdc_irq_handler(int irq, void *arg) +/* IRQ handler for version 2 of LCDC */ +static irqreturn_t lcdc_irq_handler_rev02(int irq, void *arg) +{ + struct da8xx_fb_par *par = arg; + u32 stat = lcdc_read(LCD_MASKED_STAT_REG); + u32 reg_int; + + if ((stat & LCD_SYNC_LOST) && (stat & LCD_FIFO_UNDERFLOW)) { + lcd_disable_raster(); + lcdc_write(stat, LCD_MASKED_STAT_REG); + lcd_enable_raster(); + } else if (stat & LCD_PL_LOAD_DONE) { + /* + * Must disable raster before changing state of any control bit. + * And also must be disabled before clearing the PL loading + * interrupt via the following write to the status register. If + * this is done after then one gets multiple PL done interrupts. + */ + lcd_disable_raster(); + + lcdc_write(stat, LCD_MASKED_STAT_REG); + + /* Disable PL completion inerrupt */ + reg_int = lcdc_read(LCD_INT_ENABLE_CLR_REG) | + (LCD_V2_PL_INT_ENA); + lcdc_write(reg_int, LCD_INT_ENABLE_CLR_REG); + + /* Setup and start data loading mode */ + lcd_blit(LOAD_DATA, par); + } else { + lcdc_write(stat, LCD_MASKED_STAT_REG); + + if (stat & LCD_END_OF_FRAME0) { + lcdc_write(par->dma_start, + LCD_DMA_FRM_BUF_BASE_ADDR_0_REG); + lcdc_write(par->dma_end, + LCD_DMA_FRM_BUF_CEILING_ADDR_0_REG); + par->vsync_flag = 1; + wake_up_interruptible(&par->vsync_wait); + } + + if (stat & LCD_END_OF_FRAME1) { + lcdc_write(par->dma_start, + LCD_DMA_FRM_BUF_BASE_ADDR_1_REG); + lcdc_write(par->dma_end, + LCD_DMA_FRM_BUF_CEILING_ADDR_1_REG); + par->vsync_flag = 1; + wake_up_interruptible(&par->vsync_wait); + } + } + + lcdc_write(0, LCD_END_OF_INT_IND_REG); + return IRQ_HANDLED; +} + +/* IRQ handler for version 1 LCDC */ +static irqreturn_t lcdc_irq_handler_rev01(int irq, void *arg) { struct da8xx_fb_par *par = arg; u32 stat = lcdc_read(LCD_STAT_REG); @@ -606,7 +717,7 @@ static irqreturn_t lcdc_irq_handler(int irq, void *arg) /* Disable PL completion inerrupt */ reg_ras = lcdc_read(LCD_RASTER_CTRL_REG); - reg_ras &= ~LCD_PL_ENABLE; + reg_ras &= ~LCD_V1_PL_INT_ENA; lcdc_write(reg_ras, LCD_RASTER_CTRL_REG); /* Setup and start data loading mode */ @@ -945,6 +1056,22 @@ static int __devinit fb_probe(struct platform_device *device) if (ret) goto err_clk_put; + /* Determine LCD IP Version */ + switch (lcdc_read(LCD_PID_REG)) { + case 0x4C100102: + lcd_revision = LCD_VERSION_1; + break; + case 0x4F200800: + lcd_revision = LCD_VERSION_2; + break; + default: + dev_warn(&device->dev, "Unknown PID Reg value 0x%x, " + "defaulting to LCD revision 1\n", + lcdc_read(LCD_PID_REG)); + lcd_revision = LCD_VERSION_1; + break; + } + for (i = 0, lcdc_info = known_lcd_panels; i < ARRAY_SIZE(known_lcd_panels); i++, lcdc_info++) { @@ -1085,7 +1212,13 @@ static int __devinit fb_probe(struct platform_device *device) } #endif - ret = request_irq(par->irq, lcdc_irq_handler, 0, DRIVER_NAME, par); + if (lcd_revision == LCD_VERSION_1) + lcdc_irq_handler = lcdc_irq_handler_rev01; + else + lcdc_irq_handler = lcdc_irq_handler_rev02; + + ret = request_irq(par->irq, lcdc_irq_handler, 0, + DRIVER_NAME, par); if (ret) goto irq_freq; return 0; -- 1.7.1 From christian.riesch at omicron.at Tue Jul 5 06:15:52 2011 From: christian.riesch at omicron.at (Christian Riesch) Date: Tue, 5 Jul 2011 13:15:52 +0200 Subject: MDSTAT_STATE_MASK 0x1f or 0x3f? Message-ID: <95DC1AA8EC908B48939B72CF375AA5E3011CFA1BA1@alice.at.omicron.at> Hi, According to the register description of the MDSTATn registers of the AM1808 SoC the module state with regard to the power and sleep controller (PSC) is given in bits 5-0. Hence, the bitmask that is used to read the module state should be 0x3f. However, the MDSTAT_STATE_MASK defined in arch/arm/mach-davinci/include/mach/psc.h is set to 0x1f. This was already reported by Sergei Shtylyov [1] but apparently it has not been changed. Is there any special reason why this was not corrected? Best regards, Christian [1] http://linux.davincidsp.com/pipermail/davinci-linux-open-source/2009-April/012618.html From rmallon at gmail.com Tue Jul 5 01:15:44 2011 From: rmallon at gmail.com (Ryan Mallon) Date: Tue, 05 Jul 2011 16:15:44 +1000 Subject: [RFC/RFT 1/2] gpio/basic_mmio: add support for enable register In-Reply-To: <83915224c24e43224272b1bf570cddb9545279a6.1309840042.git.nsekhar@ti.com> References: <83915224c24e43224272b1bf570cddb9545279a6.1309840042.git.nsekhar@ti.com> Message-ID: <4E12AC10.9020206@gmail.com> On 05/07/11 15:10, Sekhar Nori wrote: > Some GPIO controllers have an enable register > which needs to be written to before a GPIO > can be used. > > Add support for enabling the GPIO. At this > time inverted logic for enabling the GPIO > is not supported. This can be done by adding > a disable register as and when a controller > with this comes along. > > Signed-off-by: Sekhar Nori > --- > > static int bgpio_setup_io(struct bgpio_chip *bgc, > void __iomem *dat, > @@ -369,6 +401,7 @@ int __devinit bgpio_init(struct bgpio_chip *bgc, > void __iomem *clr, > void __iomem *dirout, > void __iomem *dirin, > + void __iomem *en, > bool big_endian) The arguments to this function are getting a bit unwieldy :-). Maybe we need to introduce something like: struct bgpio_chip_info { struct device *dev; unsigned long sz; void __iomem *dat; void __iomem *set; void __iomem *clr; void __iomem *dirout; void __iomem *dirin; void __iomem *en; bool big_endian; }; and pass that to bgpio_init instead. It would have the added benefits of making the drivers more readable and that bgpio_chip_info structs in the drivers can probably be marked __initdata also. Since you are already having to touch all of the call sites for bgpio_init this could be done as a separate patch along with this series. ~Ryan From nsekhar at ti.com Tue Jul 5 10:41:58 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 5 Jul 2011 21:11:58 +0530 Subject: MDSTAT_STATE_MASK 0x1f or 0x3f? In-Reply-To: <95DC1AA8EC908B48939B72CF375AA5E3011CFA1BA1@alice.at.omicron.at> References: <95DC1AA8EC908B48939B72CF375AA5E3011CFA1BA1@alice.at.omicron.at> Message-ID: Hi Christian, On Tue, Jul 05, 2011 at 16:45:52, Christian Riesch wrote: > Hi, > > According to the register description of the MDSTATn registers of the > AM1808 SoC the module state with regard to the power and sleep controller > (PSC) is given in bits 5-0. Hence, the bitmask that is used to read the > module state should be 0x3f. However, the MDSTAT_STATE_MASK defined in > arch/arm/mach-davinci/include/mach/psc.h is set to 0x1f. > > This was already reported by Sergei Shtylyov [1] but apparently it has > not been changed. Is there any special reason why this was not corrected? I don't think there is any special reason. As Kevin said in the thread, the fix should have been a separate patch - which probably never came. Curious as to why this came up? Did you hit any issue? Thanks, Sekhar From greg at kroah.com Tue Jul 5 11:15:58 2011 From: greg at kroah.com (Greg KH) Date: Tue, 5 Jul 2011 09:15:58 -0700 Subject: [PATCH v5 1/1] drivers:staging:pruss: add pruss staging mfd driver. In-Reply-To: <20110628210112.GA31081@kroah.com> References: <1306827939-4133-1-git-send-email-subhasish@mistralsolutions.com> <1306827939-4133-2-git-send-email-subhasish@mistralsolutions.com> <20110628210112.GA31081@kroah.com> Message-ID: <20110705161558.GA26309@kroah.com> On Tue, Jun 28, 2011 at 02:01:12PM -0700, Greg KH wrote: > On Tue, May 31, 2011 at 01:15:39PM +0530, Subhasish Ghosh wrote: > > This patch adds the pruss MFD driver and associated include files. > > For details regarding the PRUSS please refer the folowing link: > > http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem > > > > The rational behind the MFD driver being the fact that multiple devices can > > be implemented on the cores independently. This is determined by the nature > > of the program which is loaded into the PRU's instruction memory. > > A device may be de-initialized and another loaded or two different devices > > can be run simultaneously on the two cores. > > It's also possible, as in our case, to implement a single device on both > > the PRU's resulting in improved load sharing. > > > > Signed-off-by: Subhasish Ghosh > > Please refresh my memory as to why this can't get merged into the > "normal" part of the kernel? Dropped from my queue due to lack of response :( Please resend when you get the chance, and address the above comment in your changelog entry (and in your TODO file as well.) thanks, greg k-h From mgrosen at ti.com Tue Jul 5 11:54:14 2011 From: mgrosen at ti.com (Grosen, Mark) Date: Tue, 5 Jul 2011 16:54:14 +0000 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-6-git-send-email-ohad@wizery.com> <4E035B78.3080200@mvista.com> <4E04A9AE.3030801@mvista.com> Message-ID: > From: Nori, Sekhar > Sent: Monday, July 04, 2011 10:30 PM ... > > > https://patchwork.kernel.org/patch/662941/ > > > > Yes, I like this idea - much cleaner. For example, the start() method > > becomes (pseudo-code): > > > > start() > > { > > /* bootaddrreg derived from platform data */ > > bootaddrreg = boot_address; > > > > clk_enable(); > > } > > > > Referring to your patch above, would it be better to just pass > > the flags into the davinci_psc_config() function rather than breaking > out more > > arguments for each flag? > > I did dwell on this quite a bit while writing that > patch, but finally decided on passing an argument > instead since I was not sure if there are going > to be more modifiers required. > > Now that you have the need for one more flag, I > think we should go ahead and pass the flags directly > to davinci_psc_config(). My original patch is not > upstream yet. I will re-spin it and you can build > on top of it. Thanks, Sekhar, this sounds good. I'll look for your patch and utilize it in the next rev of this patch. Mark From mgrosen at ti.com Tue Jul 5 11:54:21 2011 From: mgrosen at ti.com (Grosen, Mark) Date: Tue, 5 Jul 2011 16:54:21 +0000 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-6-git-send-email-ohad@wizery.com> <4E035B78.3080200@mvista.com> <4E04A9AE.3030801@mvista.com> Message-ID: > From: Nori, Sekhar > Sent: Monday, July 04, 2011 10:35 PM > To: Grosen, Mark; Sergei Shtylyov ... > > > Since procedure to set the boot address varies across DaVinci > > > platforms, you could have a callback populated in platform data > > > which will be implemented differently for original DaVinci and > > > DA8xx devices. > > > > I looked at DM6467 and it's the same as OMAPL13x, except at a > different > > address. Rather than a callback, it could be just an address in the > > platform data. > > Sounds okay as long as _all_ the DaVinci devices have the same > bit to be set. Plus, I hope there are no other users of the > register so that there is no race with other platform code using > the same register. Sekhar, The register is a dedicated 32-bit register that holds the start/boot address for the DSP, so no other platform code should be using it. Once the LRST is de-asserted (via the PSC code enhancement), the DSP starts execution at the address in this register. Thanks, Mark From m-watkins at ti.com Tue Jul 5 13:43:03 2011 From: m-watkins at ti.com (Watkins, Melissa) Date: Tue, 5 Jul 2011 13:43:03 -0500 Subject: [PATCH v5 1/1] drivers:staging:pruss: add pruss staging mfd driver. In-Reply-To: <20110705161558.GA26309@kroah.com> References: <1306827939-4133-1-git-send-email-subhasish@mistralsolutions.com> <1306827939-4133-2-git-send-email-subhasish@mistralsolutions.com> <20110628210112.GA31081@kroah.com> <20110705161558.GA26309@kroah.com> Message-ID: <131E5DFBE7373E4C8D813795A6AA7F08034B5C4C48@dlee06.ent.ti.com> On Tue, Jun 28, 2011 at 02:01:12PM -0700, Greg KH wrote: > On Tue, May 31, 2011 at 01:15:39PM +0530, Subhasish Ghosh wrote: > > > This patch adds the pruss MFD driver and associated include files. > > > For details regarding the PRUSS please refer the folowing link: > > > http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem > > > > > > The rational behind the MFD driver being the fact that multiple devices can > > > be implemented on the cores independently. This is determined by the nature > > > of the program which is loaded into the PRU's instruction memory. > > > A device may be de-initialized and another loaded or two different devices > > > can be run simultaneously on the two cores. > > > It's also possible, as in our case, to implement a single device on both > > > the PRU's resulting in improved load sharing. > > > > > > Signed-off-by: Subhasish Ghosh > > [MW] Greg, thanks for reviewing Mistral's patch submission. I have been following the traffic on their patches and will provide an initial response to address some of your questions. > > Please refresh my memory as to why this can't get merged into the > > "normal" part of the kernel? [MW] Arnd had recommended moving the driver to the staging area until all interfaces have stabilized. One of the main reasons it cannot currently be merged in the "normal" part of the kernel is the question of how firmware should be configured and loaded. (https://lkml.org/lkml/2011/5/24/230, https://lkml.org/lkml/2011/5/10/463) > Dropped from my queue due to lack of response :( > Please resend when you get the chance, and address the above comment in > your changelog entry (and in your TODO file as well.) [MW] Please let us know if Mistral still needs to resubmit their patch or if we can continue discussions on this submission. > > > --- /dev/null > > > +++ b/drivers/staging/pruss/TODO > > > @@ -0,0 +1,14 @@ > > > +TODO: > > > + > > > +0. Functionality wise, everything works. > > > + > > > +1. Currently the plan is to add sysfs attributes for > > > + a. prux/load > > > + b. prux/unload > > > + c. prux/run > > > + d. prux/halt > > > + These will add to a more dynamic firmware management for the PRU. > > Why sysfs? [MW] Sysfs was discussed between Subhasish and Arnd in the following thread: https://lkml.org/lkml/2011/5/5/137. Arnd had recommended three options for loading a firmware-- the third of which involved sysfs. > > > + > > > +2. But, not sure how the fdt will effect these entries. > > > + > > > +Please send patches to Greg Kroah-Hartman . > > Don't you want people to Cc: you as well? > > I'm not going to adopt this driver, I have enough as it is :) From gregkh at suse.de Tue Jul 5 21:00:49 2011 From: gregkh at suse.de (Greg KH) Date: Tue, 5 Jul 2011 19:00:49 -0700 Subject: [PATCH v5 1/1] drivers:staging:pruss: add pruss staging mfd driver. In-Reply-To: <131E5DFBE7373E4C8D813795A6AA7F08034B5C4C48@dlee06.ent.ti.com> References: <1306827939-4133-1-git-send-email-subhasish@mistralsolutions.com> <1306827939-4133-2-git-send-email-subhasish@mistralsolutions.com> <20110628210112.GA31081@kroah.com> <20110705161558.GA26309@kroah.com> <131E5DFBE7373E4C8D813795A6AA7F08034B5C4C48@dlee06.ent.ti.com> Message-ID: <20110706020049.GB15988@suse.de> On Tue, Jul 05, 2011 at 01:43:03PM -0500, Watkins, Melissa wrote: > On Tue, Jun 28, 2011 at 02:01:12PM -0700, Greg KH wrote: > > On Tue, May 31, 2011 at 01:15:39PM +0530, Subhasish Ghosh wrote: > > > > This patch adds the pruss MFD driver and associated include files. > > > > For details regarding the PRUSS please refer the folowing link: > > > > http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem > > > > > > > > The rational behind the MFD driver being the fact that multiple devices can > > > > be implemented on the cores independently. This is determined by the nature > > > > of the program which is loaded into the PRU's instruction memory. > > > > A device may be de-initialized and another loaded or two different devices > > > > can be run simultaneously on the two cores. > > > > It's also possible, as in our case, to implement a single device on both > > > > the PRU's resulting in improved load sharing. > > > > > > > > Signed-off-by: Subhasish Ghosh > > > > > [MW] Greg, thanks for reviewing Mistral's patch submission. I have > been following the traffic on their patches and will provide an > initial response to address some of your questions. > > > > Please refresh my memory as to why this can't get merged into the > > > "normal" part of the kernel? > > [MW] Arnd had recommended moving the driver to the staging area until > all interfaces have stabilized. One of the main reasons it cannot > currently be merged in the "normal" part of the kernel is the question > of how firmware should be configured and loaded. > (https://lkml.org/lkml/2011/5/24/230, > https://lkml.org/lkml/2011/5/10/463) > > > Dropped from my queue due to lack of response :( > > > Please resend when you get the chance, and address the above comment in > > your changelog entry (and in your TODO file as well.) > > [MW] Please let us know if Mistral still needs to resubmit their patch > or if we can continue discussions on this submission. Please resend it, with the above information in the changelog and in the TODO file. greg k-h From christian.riesch at omicron.at Tue Jul 5 23:13:22 2011 From: christian.riesch at omicron.at (Christian Riesch) Date: Wed, 6 Jul 2011 06:13:22 +0200 Subject: MDSTAT_STATE_MASK 0x1f or 0x3f? In-Reply-To: References: <95DC1AA8EC908B48939B72CF375AA5E3011CFA1BA1@alice.at.omicron.at> Message-ID: Hi Sekhar, On Tuesday, July 5, 2011, Nori, Sekhar wrote: > Hi Christian, > > On Tue, Jul 05, 2011 at 16:45:52, Christian Riesch wrote: >> Hi, >> >> According to the register description of the MDSTATn registers of the >> AM1808 SoC the module state with regard to the power and sleep controller >> (PSC) is given in bits 5-0. Hence, the bitmask that is used to read the >> module state should be 0x3f. However, the MDSTAT_STATE_MASK defined in >> arch/arm/mach-davinci/include/mach/psc.h is set to 0x1f. >> >> This was already reported by Sergei Shtylyov [1] but apparently it has >> not been changed. Is there any special reason why this was not corrected? > > I don't think there is any special reason. As Kevin said in the thread, > the fix should have been a separate patch - which probably never came. Ok. > Curious as to why this came up? Did you hit any issue? No, I didn't, I came across it when debugging a PSC related issue in u-boot [1], which uses the same bitmask. However, the bitmask was not the reason for my problems, 0x1f worked fine. But of course it took me some time to figure that out so I guess it should be corrected to prevent others from having to think about it. Thanks, Christian [1] http://marc.info/?l=u-boot&m=130857573323691&w=2 From nsekhar at ti.com Tue Jul 5 23:36:16 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 6 Jul 2011 10:06:16 +0530 Subject: [RFC 5/8] remoteproc: add davinci implementation In-Reply-To: References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-6-git-send-email-ohad@wizery.com> <4E035B78.3080200@mvista.com> <4E04A9AE.3030801@mvista.com> Message-ID: Hi Mark, On Tue, Jul 05, 2011 at 22:24:21, Grosen, Mark wrote: > > From: Nori, Sekhar > > Sent: Monday, July 04, 2011 10:35 PM > > To: Grosen, Mark; Sergei Shtylyov > > ... > > > > Since procedure to set the boot address varies across DaVinci > > > > platforms, you could have a callback populated in platform data > > > > which will be implemented differently for original DaVinci and > > > > DA8xx devices. > > > > > > I looked at DM6467 and it's the same as OMAPL13x, except at a > > different > > > address. Rather than a callback, it could be just an address in the > > > platform data. > > > > Sounds okay as long as _all_ the DaVinci devices have the same > > bit to be set. Plus, I hope there are no other users of the > > register so that there is no race with other platform code using > > the same register. > > Sekhar, > > The register is a dedicated 32-bit register that holds the start/boot > address for the DSP, so no other platform code should be using it. Once > the LRST is de-asserted (via the PSC code enhancement), the DSP starts > execution at the address in this register. Okay. I had misunderstood this as a bit which is used to reset the DSP. Thanks for clarifying. Regards, Sekhar From manjunath.hadli at ti.com Wed Jul 6 00:40:37 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Wed, 6 Jul 2011 11:10:37 +0530 Subject: [ RFC PATCH 0/8] RFC for Media Controller capture driver for DM365 In-Reply-To: <4E11E695.9090508@iki.fi> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> <20110630135736.GK12671@valkosipuli.localdomain> <201107041522.37437.laurent.pinchart@ideasonboard.com> <4E11E695.9090508@iki.fi> Message-ID: Hi Sakari, On Mon, Jul 04, 2011 at 21:43:09, Sakari Ailus wrote: > Hadli, Manjunath wrote: > > Thank you Laurent. > > Hi Manjunath, > > > On Mon, Jul 04, 2011 at 18:52:37, Laurent Pinchart wrote: > >> Hi Manjunath, > >> > >> On Monday 04 July 2011 07:58:06 Hadli, Manjunath wrote: > >>> On Thu, Jun 30, 2011 at 19:27:36, Sakari Ailus wrote: > >> > >> [snip] > >> > >>>> I understand that not all the blocks are there. Are there any major > >>>> functional differences between those in Davinci and those in OMAP > >>>> 3? Could the OMAP 3 ISP driver made support Davinci ISP as well? > >>> > >>> Yes, there are a lot of major differences between OMAP3 and > >>> Dm365/Dm355, both in terms of features, there IP, and the software > >>> interface, including all the registers which are entirely different. > >>> The closest omap3 would come to is only to DM6446. I do not think > >>> OMAP3 driver can be made to support Dm355 and Dm365. It is good to > >>> keep the OMAP3 neat and clean to cater for OMAP4 and beyond, and > >>> keep the Davinci family separate. The names might look similar and > >>> hence confusing for you, but the names can as well be made the same > >>> as Dm365 blocks like ISIF and IPIPE and IPIPEIF which are different. > >> > >> The DM6446 ISP is very similar to the OMAP3 ISP, and thus quite > >> different from the DM355/365 ISPs. Should the DM6446 be supported by > >> the OMAP3 ISP driver, and the DM355/365 by this driver ? > > > > DM6446 capture IP is in some respects similar to OMAP3 for some > > features, but there are a large number of differences also (MMU, VRFB, > > a lot of display interfaces etc). Having a single driver catering to > > Since DM6446 and OMAP3 is going to be unwieldy. Also, > > DM6446 belongs to the Davinci family of chips, it should be clubbed > > with the other Davinci SoCs as it will simplify a lot of other things > > including directory subdirectory/file naming, organization of > > machine/platform code etc among other things. Other than Video a lot > > of other system registers and features which are common with the rest > > of Davinci SoCs which if treated together is a good thing, whereas > > OMAP3 can be modified and developed with those on the OMAP family > > (OMAP4 for ex). > > Thanks for the clarifications. > > What about the DM3730? As far as I understand, the ISP on that one is supported by the OMAP 3 ISP driver. But it looks like that it's more continuation for the OMAP family of the chips than the Davinci. Let me say that for all practical purposes, for developers, DM3730 is OMAP3. So a distinction between OMAP3 and DM3730 need not be made at all. As to why it is a Davinci device, has more to do with things outside the realm of development. So Dm3730 for us, including you and me, can be OMAP3, As the TRM says - " It is OMAP3 compatible". > > I glanced at the DM6446 documentation and at the register level the interface looks somewhat different although some register names are the same. I didn't found a proper TRM which would be as detailed as the OMAP ones --- does TI have one available in public? TRMs for Davinci devices are slightly in a different format - split into multiple documents for each peripheral and system functionalities unlike a big singe doc for OMAP. But all the required documents are in public domain and can be found at : http://focus.ti.com/docs/prod/folders/print/tms320dm6446.html under the user guides category. If you are looking for some particular information, let me know and I can help you locate it. > > OMAP 4 has a quite different ISS --- which the ISP is a part of, and which also is very different to the OMAP 3 one --- so it's unlikely that the same driver would support OMAP 3 and OMAP 4 ISPs. > > Kind regards, > > -- > Sakari Ailus > sakari.ailus at iki.fi > Regards, -Manjunath From nsekhar at ti.com Wed Jul 6 01:01:19 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 6 Jul 2011 11:31:19 +0530 Subject: [PATCH 0/5] davinci: da850: add SATA support Message-ID: This series add support for platform AHCI driver on DA850/OMAP-L138/AM18x. Towards this purpose, it also adds support for forced module transtitions on PSC. Sekhar Nori (5): davinci: psc.h: clean up indentation done using spaces davinci: pass clock flags to davinci_psc_config() davinci: enable forced transitions on PSC davinci: da850: add support for SATA interface davinci: da850 evm: register SATA device arch/arm/mach-davinci/board-da850-evm.c | 7 ++ arch/arm/mach-davinci/clock.c | 8 +- arch/arm/mach-davinci/clock.h | 1 + arch/arm/mach-davinci/da850.c | 9 ++ arch/arm/mach-davinci/devices-da8xx.c | 126 +++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/da8xx.h | 2 + arch/arm/mach-davinci/include/mach/psc.h | 151 ++++++++++++++-------------- arch/arm/mach-davinci/psc.c | 14 +++- 8 files changed, 237 insertions(+), 81 deletions(-) -- 1.7.3.2 From nsekhar at ti.com Wed Jul 6 01:01:24 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 6 Jul 2011 11:31:24 +0530 Subject: [PATCH 5/5] davinci: da850 evm: register SATA device In-Reply-To: References: Message-ID: Register the platform device for SATA interface present on the DA850/OMAP-L138/AM18x EVM. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/board-da850-evm.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..3d2c0d7 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1117,6 +1117,8 @@ static __init int da850_evm_init_cpufreq(void) static __init int da850_evm_init_cpufreq(void) { return 0; } #endif +#define DA850EVM_SATA_REFCLKPN_RATE (100 * 1000 * 1000) + static __init void da850_evm_init(void) { int ret; @@ -1237,6 +1239,11 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: spi 1 registration failed: %d\n", ret); + + ret = da850_register_sata(DA850EVM_SATA_REFCLKPN_RATE); + if (ret) + pr_warning("da850_evm_init: sata registration failed: %d\n", + ret); } #ifdef CONFIG_SERIAL_8250_CONSOLE -- 1.7.3.2 From nsekhar at ti.com Wed Jul 6 01:01:23 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 6 Jul 2011 11:31:23 +0530 Subject: [PATCH v3 4/5] davinci: da850: add support for SATA interface In-Reply-To: References: Message-ID: Add support for SATA controller on the DA850/OMAP-L138/AM18x devices. The patch adds the necessary clocks, platform resources and a routine to initialize the SATA controller. The PHY configuration in this patch is courtesy the work done by Zegeye Alemu, Swaminathan and Mansoor Ahamed from TI. While testing this patch, enable port multiplier support iff you are actually using one. The reasons of this behaviour are discussed here: http://patchwork.ozlabs.org/patch/78163/ ChangeLog: v3: Removed fields which were being initialized to zero in PHY configuration. Moved SATA base address definition to the top of the file to make it inline with what is done for the rest of the modules. v2: Addressed comments from Sergei. Removed unnecessary braces and removed unnecessary else after goto. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/da850.c | 9 ++ arch/arm/mach-davinci/devices-da8xx.c | 126 ++++++++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/da8xx.h | 2 + 3 files changed, 137 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 133aac4..2c1fad5 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -373,6 +373,14 @@ static struct clk spi1_clk = { .flags = DA850_CLK_ASYNC3, }; +static struct clk sata_clk = { + .name = "sata", + .parent = &pll0_sysclk2, + .lpsc = DA850_LPSC1_SATA, + .gpsc = 1, + .flags = PSC_FORCE, +}; + static struct clk_lookup da850_clks[] = { CLK(NULL, "ref", &ref_clk), CLK(NULL, "pll0", &pll0_clk), @@ -419,6 +427,7 @@ static struct clk_lookup da850_clks[] = { CLK(NULL, "usb20", &usb20_clk), CLK("spi_davinci.0", NULL, &spi0_clk), CLK("spi_davinci.1", NULL, &spi1_clk), + CLK("ahci", NULL, &sata_clk), CLK(NULL, NULL, NULL), }; diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index fc4e98e..2f7e719 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -33,6 +35,7 @@ #define DA8XX_SPI0_BASE 0x01c41000 #define DA830_SPI1_BASE 0x01e12000 #define DA8XX_LCD_CNTRL_BASE 0x01e13000 +#define DA850_SATA_BASE 0x01e18000 #define DA850_MMCSD1_BASE 0x01e1b000 #define DA8XX_EMAC_CPPI_PORT_BASE 0x01e20000 #define DA8XX_EMAC_CPGMACSS_BASE 0x01e22000 @@ -842,3 +845,126 @@ int __init da8xx_register_spi(int instance, struct spi_board_info *info, return platform_device_register(&da8xx_spi_device[instance]); } + +#ifdef CONFIG_ARCH_DAVINCI_DA850 + +static struct resource da850_sata_resources[] = { + { + .start = DA850_SATA_BASE, + .end = DA850_SATA_BASE + 0x1fff, + .flags = IORESOURCE_MEM, + }, + { + .start = IRQ_DA850_SATAINT, + .flags = IORESOURCE_IRQ, + }, +}; + +/* SATA PHY Control Register offset from AHCI base */ +#define SATA_P0PHYCR_REG 0x178 + +#define SATA_PHY_MPY(x) ((x) << 0) +#define SATA_PHY_LOS(x) ((x) << 6) +#define SATA_PHY_RXCDR(x) ((x) << 10) +#define SATA_PHY_RXEQ(x) ((x) << 13) +#define SATA_PHY_TXSWING(x) ((x) << 19) +#define SATA_PHY_ENPLL(x) ((x) << 31) + +static struct clk *da850_sata_clk; +static unsigned long da850_sata_refclkpn; + +/* Supported DA850 SATA crystal frequencies */ +#define KHZ_TO_HZ(freq) ((freq) * 1000) +static unsigned long da850_sata_xtal[] = { + KHZ_TO_HZ(300000), + KHZ_TO_HZ(250000), + 0, /* Reserved */ + KHZ_TO_HZ(187500), + KHZ_TO_HZ(150000), + KHZ_TO_HZ(125000), + KHZ_TO_HZ(120000), + KHZ_TO_HZ(100000), + KHZ_TO_HZ(75000), + KHZ_TO_HZ(60000), +}; + +static int da850_sata_init(struct device *dev, void __iomem *addr) +{ + int i, ret; + unsigned int val; + + da850_sata_clk = clk_get(dev, NULL); + if (IS_ERR(da850_sata_clk)) + return PTR_ERR(da850_sata_clk); + + ret = clk_enable(da850_sata_clk); + if (ret) + goto err0; + + /* Enable SATA clock receiver */ + val = __raw_readl(DA8XX_SYSCFG1_VIRT(DA8XX_PWRDN_REG)); + val &= ~BIT(0); + __raw_writel(val, DA8XX_SYSCFG1_VIRT(DA8XX_PWRDN_REG)); + + /* Get the multiplier needed for 1.5GHz PLL output */ + for (i = 0; i < ARRAY_SIZE(da850_sata_xtal); i++) + if (da850_sata_xtal[i] == da850_sata_refclkpn) + break; + + if (i == ARRAY_SIZE(da850_sata_xtal)) { + ret = -EINVAL; + goto err1; + } + + val = SATA_PHY_MPY(i + 1) | + SATA_PHY_LOS(1) | + SATA_PHY_RXCDR(4) | + SATA_PHY_RXEQ(1) | + SATA_PHY_TXSWING(3) | + SATA_PHY_ENPLL(1); + + __raw_writel(val, addr + SATA_P0PHYCR_REG); + + return 0; + +err1: + clk_disable(da850_sata_clk); +err0: + clk_put(da850_sata_clk); + return ret; +} + +static void da850_sata_exit(struct device *dev) +{ + clk_disable(da850_sata_clk); + clk_put(da850_sata_clk); +} + +static struct ahci_platform_data da850_sata_pdata = { + .init = da850_sata_init, + .exit = da850_sata_exit, +}; + +static u64 da850_sata_dmamask = DMA_BIT_MASK(32); + +static struct platform_device da850_sata_device = { + .name = "ahci", + .id = -1, + .dev = { + .platform_data = &da850_sata_pdata, + .dma_mask = &da850_sata_dmamask, + .coherent_dma_mask = DMA_BIT_MASK(32), + }, + .num_resources = ARRAY_SIZE(da850_sata_resources), + .resource = da850_sata_resources, +}; + +int __init da850_register_sata(unsigned long refclkpn) +{ + da850_sata_refclkpn = refclkpn; + if (!da850_sata_refclkpn) + return -EINVAL; + + return platform_device_register(&da850_sata_device); +} +#endif diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index ad64da7..eaca7d8 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -57,6 +57,7 @@ extern unsigned int da850_max_speed; #define DA8XX_SYSCFG1_BASE (IO_PHYS + 0x22C000) #define DA8XX_SYSCFG1_VIRT(x) (da8xx_syscfg1_base + (x)) #define DA8XX_DEEPSLEEP_REG 0x8 +#define DA8XX_PWRDN_REG 0x18 #define DA8XX_PSC0_BASE 0x01c10000 #define DA8XX_PLL0_BASE 0x01c11000 @@ -89,6 +90,7 @@ int da850_register_cpufreq(char *async_clk); int da8xx_register_cpuidle(void); void __iomem * __init da8xx_get_mem_ctlr(void); int da850_register_pm(struct platform_device *pdev); +int __init da850_register_sata(unsigned long refclkpn); extern struct platform_device da8xx_serial_device; extern struct emac_platform_data da8xx_emac_pdata; -- 1.7.3.2 From nsekhar at ti.com Wed Jul 6 01:01:22 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 6 Jul 2011 11:31:22 +0530 Subject: [PATCH v2 3/5] davinci: enable forced transitions on PSC In-Reply-To: References: Message-ID: <16e4a0ff95e42ddbe4a031af5f7a236f6837effd.1309927428.git.nsekhar@ti.com> Some DaVinci modules like the SATA on DA850 need forced module state transitions. Define a "force" flag which can be passed to the PSC config function to enable it to make forced transitions. Forced transitions shouldn't normally be attempted, unless the TRM explicitly specifies its usage. ChangeLog: v2: Modified to take care of the fact that davinci_psc_config() now takes the flags directly. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/clock.h | 1 + arch/arm/mach-davinci/include/mach/psc.h | 1 + arch/arm/mach-davinci/psc.c | 2 ++ 3 files changed, 4 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h index 0dd2203..48ee462 100644 --- a/arch/arm/mach-davinci/clock.h +++ b/arch/arm/mach-davinci/clock.h @@ -111,6 +111,7 @@ struct clk { #define CLK_PLL BIT(4) /* PLL-derived clock */ #define PRE_PLL BIT(5) /* source is before PLL mult/div */ #define PSC_SWRSTDISABLE BIT(6) /* Disable state is SwRstDisable */ +#define PSC_FORCE BIT(7) /* Force module state transtition */ #define CLK(dev, con, ck) \ { \ diff --git a/arch/arm/mach-davinci/include/mach/psc.h b/arch/arm/mach-davinci/include/mach/psc.h index be9d63e..47fd0bc 100644 --- a/arch/arm/mach-davinci/include/mach/psc.h +++ b/arch/arm/mach-davinci/include/mach/psc.h @@ -244,6 +244,7 @@ #define PSC_STATE_ENABLE 3 #define MDSTAT_STATE_MASK 0x1f +#define MDCTL_FORCE BIT(31) #ifndef __ASSEMBLER__ diff --git a/arch/arm/mach-davinci/psc.c b/arch/arm/mach-davinci/psc.c index 823cb1b..1fb6bdf 100644 --- a/arch/arm/mach-davinci/psc.c +++ b/arch/arm/mach-davinci/psc.c @@ -75,6 +75,8 @@ void davinci_psc_config(unsigned int domain, unsigned int ctlr, mdctl = __raw_readl(psc_base + MDCTL + 4 * id); mdctl &= ~MDSTAT_STATE_MASK; mdctl |= next_state; + if (flags & PSC_FORCE) + mdctl |= MDCTL_FORCE; __raw_writel(mdctl, psc_base + MDCTL + 4 * id); pdstat = __raw_readl(psc_base + PDSTAT); -- 1.7.3.2 From nsekhar at ti.com Wed Jul 6 01:01:21 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 6 Jul 2011 11:31:21 +0530 Subject: [PATCH 2/5] davinci: pass clock flags to davinci_psc_config() In-Reply-To: References: Message-ID: <595d0b3fe19ad626eaa8edc11b1cc872c06ab176.1309927427.git.nsekhar@ti.com> Enabling or disabling a PSC can take certain modifiers like "disable with reset", "force enable/disable" and "enable/disable with local reset" apart from the regular clock gating functionality. Pass a flags argument to davinci_psc_config() so these variations can be supported there. At this time only "disable with reset" is supported, but other functionality will be added in subsequent patches. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/clock.c | 8 +++----- arch/arm/mach-davinci/include/mach/psc.h | 2 +- arch/arm/mach-davinci/psc.c | 12 +++++++++++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/arch/arm/mach-davinci/clock.c b/arch/arm/mach-davinci/clock.c index e4e3af1..d0450ac 100644 --- a/arch/arm/mach-davinci/clock.c +++ b/arch/arm/mach-davinci/clock.c @@ -44,7 +44,7 @@ static void __clk_enable(struct clk *clk) __clk_enable(clk->parent); if (clk->usecount++ == 0 && (clk->flags & CLK_PSC)) davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc, - PSC_STATE_ENABLE); + true, clk->flags); } static void __clk_disable(struct clk *clk) @@ -54,8 +54,7 @@ static void __clk_disable(struct clk *clk) if (--clk->usecount == 0 && !(clk->flags & CLK_PLL) && (clk->flags & CLK_PSC)) davinci_psc_config(psc_domain(clk), clk->gpsc, clk->lpsc, - (clk->flags & PSC_SWRSTDISABLE) ? - PSC_STATE_SWRSTDISABLE : PSC_STATE_DISABLE); + false, clk->flags); if (clk->parent) __clk_disable(clk->parent); } @@ -239,8 +238,7 @@ static int __init clk_disable_unused(void) pr_debug("Clocks: disable unused %s\n", ck->name); davinci_psc_config(psc_domain(ck), ck->gpsc, ck->lpsc, - (ck->flags & PSC_SWRSTDISABLE) ? - PSC_STATE_SWRSTDISABLE : PSC_STATE_DISABLE); + false, ck->flags); } spin_unlock_irq(&clockfw_lock); diff --git a/arch/arm/mach-davinci/include/mach/psc.h b/arch/arm/mach-davinci/include/mach/psc.h index 1110fdd..be9d63e 100644 --- a/arch/arm/mach-davinci/include/mach/psc.h +++ b/arch/arm/mach-davinci/include/mach/psc.h @@ -249,7 +249,7 @@ extern int davinci_psc_is_clk_active(unsigned int ctlr, unsigned int id); extern void davinci_psc_config(unsigned int domain, unsigned int ctlr, - unsigned int id, u32 next_state); + unsigned int id, bool enable, u32 flags); #endif diff --git a/arch/arm/mach-davinci/psc.c b/arch/arm/mach-davinci/psc.c index a415804..823cb1b 100644 --- a/arch/arm/mach-davinci/psc.c +++ b/arch/arm/mach-davinci/psc.c @@ -25,6 +25,8 @@ #include #include +#include "clock.h" + /* Return nonzero iff the domain's clock is active */ int __init davinci_psc_is_clk_active(unsigned int ctlr, unsigned int id) { @@ -48,11 +50,12 @@ int __init davinci_psc_is_clk_active(unsigned int ctlr, unsigned int id) /* Enable or disable a PSC domain */ void davinci_psc_config(unsigned int domain, unsigned int ctlr, - unsigned int id, u32 next_state) + unsigned int id, bool enable, u32 flags) { u32 epcpr, ptcmd, ptstat, pdstat, pdctl1, mdstat, mdctl; void __iomem *psc_base; struct davinci_soc_info *soc_info = &davinci_soc_info; + u32 next_state = PSC_STATE_ENABLE; if (!soc_info->psc_bases || (ctlr >= soc_info->psc_bases_num)) { pr_warning("PSC: Bad psc data: 0x%x[%d]\n", @@ -62,6 +65,13 @@ void davinci_psc_config(unsigned int domain, unsigned int ctlr, psc_base = ioremap(soc_info->psc_bases[ctlr], SZ_4K); + if (!enable) { + if (flags & PSC_SWRSTDISABLE) + next_state = PSC_STATE_SWRSTDISABLE; + else + next_state = PSC_STATE_DISABLE; + } + mdctl = __raw_readl(psc_base + MDCTL + 4 * id); mdctl &= ~MDSTAT_STATE_MASK; mdctl |= next_state; -- 1.7.3.2 From nsekhar at ti.com Wed Jul 6 01:01:20 2011 From: nsekhar at ti.com (Sekhar Nori) Date: Wed, 6 Jul 2011 11:31:20 +0530 Subject: [PATCH 1/5] davinci: psc.h: clean up indentation done using spaces In-Reply-To: References: Message-ID: <2112f84888cfb283ddb12f97ebe3d06116e39d94.1309927427.git.nsekhar@ti.com> psc.h has indentation using spaces at a number of places. Fix this by indenting using tabs instead. Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/include/mach/psc.h | 148 +++++++++++++++--------------- 1 files changed, 74 insertions(+), 74 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/psc.h b/arch/arm/mach-davinci/include/mach/psc.h index a47e6f2..1110fdd 100644 --- a/arch/arm/mach-davinci/include/mach/psc.h +++ b/arch/arm/mach-davinci/include/mach/psc.h @@ -30,47 +30,47 @@ #define DAVINCI_PWR_SLEEP_CNTRL_BASE 0x01C41000 /* Power and Sleep Controller (PSC) Domains */ -#define DAVINCI_GPSC_ARMDOMAIN 0 -#define DAVINCI_GPSC_DSPDOMAIN 1 +#define DAVINCI_GPSC_ARMDOMAIN 0 +#define DAVINCI_GPSC_DSPDOMAIN 1 -#define DAVINCI_LPSC_VPSSMSTR 0 -#define DAVINCI_LPSC_VPSSSLV 1 -#define DAVINCI_LPSC_TPCC 2 -#define DAVINCI_LPSC_TPTC0 3 -#define DAVINCI_LPSC_TPTC1 4 -#define DAVINCI_LPSC_EMAC 5 -#define DAVINCI_LPSC_EMAC_WRAPPER 6 -#define DAVINCI_LPSC_USB 9 -#define DAVINCI_LPSC_ATA 10 -#define DAVINCI_LPSC_VLYNQ 11 -#define DAVINCI_LPSC_UHPI 12 -#define DAVINCI_LPSC_DDR_EMIF 13 -#define DAVINCI_LPSC_AEMIF 14 -#define DAVINCI_LPSC_MMC_SD 15 -#define DAVINCI_LPSC_McBSP 17 -#define DAVINCI_LPSC_I2C 18 -#define DAVINCI_LPSC_UART0 19 -#define DAVINCI_LPSC_UART1 20 -#define DAVINCI_LPSC_UART2 21 -#define DAVINCI_LPSC_SPI 22 -#define DAVINCI_LPSC_PWM0 23 -#define DAVINCI_LPSC_PWM1 24 -#define DAVINCI_LPSC_PWM2 25 -#define DAVINCI_LPSC_GPIO 26 -#define DAVINCI_LPSC_TIMER0 27 -#define DAVINCI_LPSC_TIMER1 28 -#define DAVINCI_LPSC_TIMER2 29 -#define DAVINCI_LPSC_SYSTEM_SUBSYS 30 -#define DAVINCI_LPSC_ARM 31 -#define DAVINCI_LPSC_SCR2 32 -#define DAVINCI_LPSC_SCR3 33 -#define DAVINCI_LPSC_SCR4 34 -#define DAVINCI_LPSC_CROSSBAR 35 -#define DAVINCI_LPSC_CFG27 36 -#define DAVINCI_LPSC_CFG3 37 -#define DAVINCI_LPSC_CFG5 38 -#define DAVINCI_LPSC_GEM 39 -#define DAVINCI_LPSC_IMCOP 40 +#define DAVINCI_LPSC_VPSSMSTR 0 +#define DAVINCI_LPSC_VPSSSLV 1 +#define DAVINCI_LPSC_TPCC 2 +#define DAVINCI_LPSC_TPTC0 3 +#define DAVINCI_LPSC_TPTC1 4 +#define DAVINCI_LPSC_EMAC 5 +#define DAVINCI_LPSC_EMAC_WRAPPER 6 +#define DAVINCI_LPSC_USB 9 +#define DAVINCI_LPSC_ATA 10 +#define DAVINCI_LPSC_VLYNQ 11 +#define DAVINCI_LPSC_UHPI 12 +#define DAVINCI_LPSC_DDR_EMIF 13 +#define DAVINCI_LPSC_AEMIF 14 +#define DAVINCI_LPSC_MMC_SD 15 +#define DAVINCI_LPSC_McBSP 17 +#define DAVINCI_LPSC_I2C 18 +#define DAVINCI_LPSC_UART0 19 +#define DAVINCI_LPSC_UART1 20 +#define DAVINCI_LPSC_UART2 21 +#define DAVINCI_LPSC_SPI 22 +#define DAVINCI_LPSC_PWM0 23 +#define DAVINCI_LPSC_PWM1 24 +#define DAVINCI_LPSC_PWM2 25 +#define DAVINCI_LPSC_GPIO 26 +#define DAVINCI_LPSC_TIMER0 27 +#define DAVINCI_LPSC_TIMER1 28 +#define DAVINCI_LPSC_TIMER2 29 +#define DAVINCI_LPSC_SYSTEM_SUBSYS 30 +#define DAVINCI_LPSC_ARM 31 +#define DAVINCI_LPSC_SCR2 32 +#define DAVINCI_LPSC_SCR3 33 +#define DAVINCI_LPSC_SCR4 34 +#define DAVINCI_LPSC_CROSSBAR 35 +#define DAVINCI_LPSC_CFG27 36 +#define DAVINCI_LPSC_CFG3 37 +#define DAVINCI_LPSC_CFG5 38 +#define DAVINCI_LPSC_GEM 39 +#define DAVINCI_LPSC_IMCOP 40 #define DM355_LPSC_TIMER3 5 #define DM355_LPSC_SPI1 6 @@ -102,39 +102,39 @@ /* * LPSC Assignments */ -#define DM646X_LPSC_ARM 0 -#define DM646X_LPSC_C64X_CPU 1 -#define DM646X_LPSC_HDVICP0 2 -#define DM646X_LPSC_HDVICP1 3 -#define DM646X_LPSC_TPCC 4 -#define DM646X_LPSC_TPTC0 5 -#define DM646X_LPSC_TPTC1 6 -#define DM646X_LPSC_TPTC2 7 -#define DM646X_LPSC_TPTC3 8 -#define DM646X_LPSC_PCI 13 -#define DM646X_LPSC_EMAC 14 -#define DM646X_LPSC_VDCE 15 -#define DM646X_LPSC_VPSSMSTR 16 -#define DM646X_LPSC_VPSSSLV 17 -#define DM646X_LPSC_TSIF0 18 -#define DM646X_LPSC_TSIF1 19 -#define DM646X_LPSC_DDR_EMIF 20 -#define DM646X_LPSC_AEMIF 21 -#define DM646X_LPSC_McASP0 22 -#define DM646X_LPSC_McASP1 23 -#define DM646X_LPSC_CRGEN0 24 -#define DM646X_LPSC_CRGEN1 25 -#define DM646X_LPSC_UART0 26 -#define DM646X_LPSC_UART1 27 -#define DM646X_LPSC_UART2 28 -#define DM646X_LPSC_PWM0 29 -#define DM646X_LPSC_PWM1 30 -#define DM646X_LPSC_I2C 31 -#define DM646X_LPSC_SPI 32 -#define DM646X_LPSC_GPIO 33 -#define DM646X_LPSC_TIMER0 34 -#define DM646X_LPSC_TIMER1 35 -#define DM646X_LPSC_ARM_INTC 45 +#define DM646X_LPSC_ARM 0 +#define DM646X_LPSC_C64X_CPU 1 +#define DM646X_LPSC_HDVICP0 2 +#define DM646X_LPSC_HDVICP1 3 +#define DM646X_LPSC_TPCC 4 +#define DM646X_LPSC_TPTC0 5 +#define DM646X_LPSC_TPTC1 6 +#define DM646X_LPSC_TPTC2 7 +#define DM646X_LPSC_TPTC3 8 +#define DM646X_LPSC_PCI 13 +#define DM646X_LPSC_EMAC 14 +#define DM646X_LPSC_VDCE 15 +#define DM646X_LPSC_VPSSMSTR 16 +#define DM646X_LPSC_VPSSSLV 17 +#define DM646X_LPSC_TSIF0 18 +#define DM646X_LPSC_TSIF1 19 +#define DM646X_LPSC_DDR_EMIF 20 +#define DM646X_LPSC_AEMIF 21 +#define DM646X_LPSC_McASP0 22 +#define DM646X_LPSC_McASP1 23 +#define DM646X_LPSC_CRGEN0 24 +#define DM646X_LPSC_CRGEN1 25 +#define DM646X_LPSC_UART0 26 +#define DM646X_LPSC_UART1 27 +#define DM646X_LPSC_UART2 28 +#define DM646X_LPSC_PWM0 29 +#define DM646X_LPSC_PWM1 30 +#define DM646X_LPSC_I2C 31 +#define DM646X_LPSC_SPI 32 +#define DM646X_LPSC_GPIO 33 +#define DM646X_LPSC_TIMER0 34 +#define DM646X_LPSC_TIMER1 35 +#define DM646X_LPSC_ARM_INTC 45 /* PSC0 defines */ #define DA8XX_LPSC0_TPCC 0 @@ -243,7 +243,7 @@ #define PSC_STATE_DISABLE 2 #define PSC_STATE_ENABLE 3 -#define MDSTAT_STATE_MASK 0x1f +#define MDSTAT_STATE_MASK 0x1f #ifndef __ASSEMBLER__ -- 1.7.3.2 From nsekhar at ti.com Wed Jul 6 04:59:10 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 6 Jul 2011 15:29:10 +0530 Subject: [PATCH 1/1] davinci: dm646x: move vpif related code to driver core header from platform In-Reply-To: References: <1305899929-2509-1-git-send-email-manjunath.hadli@ti.com> Message-ID: Hi Mauro, On Wed, Jun 22, 2011 at 09:35:58, Nori, Sekhar wrote: > On Thu, Jun 02, 2011 at 22:51:58, Nori, Sekhar wrote: > > Hi Mauro, > > > > On Fri, May 20, 2011 at 19:28:49, Hadli, Manjunath wrote: > > > move vpif related code for capture and display drivers > > > from dm646x platform header file to vpif.h as these definitions > > > are related to driver code more than the platform or board. > > > > > > Signed-off-by: Manjunath Hadli > > > > Will you be taking this patch through your tree? > > > > If not, with your ack, I can queue it for inclusion > > through the ARM tree. > > > > Ping :) Can you please provide your ack so that I can queue this for v3.1? Thanks, Sekhar From nsekhar at ti.com Wed Jul 6 10:52:52 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 6 Jul 2011 21:22:52 +0530 Subject: [PATCH v2] davinci: da850 EVM: read mac address from SPI flash In-Reply-To: <1309784838-30903-1-git-send-email-sudhakar.raj@ti.com> References: <1309784838-30903-1-git-send-email-sudhakar.raj@ti.com> Message-ID: Hi Sudhakar, On Mon, Jul 04, 2011 at 18:37:18, Rajashekhara, Sudhakar wrote: > DA850/OMAP-L138 EMAC driver uses random mac address instead of > a fixed one because the mac address is not stuffed into EMAC > platform data. > > This patch provides a function which reads the mac address > stored in SPI flash (registered as MTD device) and populates the > EMAC platform data. The function which reads the mac address is > registered as a callback which gets called upon addition of MTD > device. > > NOTE: In case the MAC address stored in SPI flash is erased, follow > the instructions at [1] to restore it. > > [1] http://processors.wiki.ti.com/index.php/GSG:_OMAP-L138_DVEVM_Additional_Procedures#Restoring_MAC_address_on_SPI_Flash > > Signed-off-by: Rajashekhara, Sudhakar > --- > Since v1: > Guarded registering the mtd_notifier only when MTD is enabled. > Earlier this was handled using mtd_has_partitions() call, but > this has been removed in Linux v3.0. Current trend is to include the change log into the commit message itself. It might be useful to examine at a later time what transformation the patch went through. It also simplifies things for you since you don't have to maintain this separately. > +static void da850_evm_m25p80_notify_add(struct mtd_info *mtd) > +{ > + char *mac_addr = davinci_soc_info.emac_pdata->mac_addr; > + size_t retlen; > + > + if (!strcmp(mtd->name, "MAC-Address")) { > + mtd->read(mtd, 0, ETH_ALEN, &retlen, mac_addr); > + if (retlen == ETH_ALEN) > + pr_info("Read MAC addr from SPI Flash: %pM\n", > + mac_addr); > + } > +} > + > +static struct mtd_notifier da850evm_spi_notifier = { > + .add = da850_evm_m25p80_notify_add, > +}; The function and data structure above are not required when CONFIG_MTD is not defined so these should be under #ifdef CONFIG_MTD too. Towards this.. > +#ifdef CONFIG_MTD > +static void da850_evm_register_mtd_user(struct mtd_notifier *notify) > +{ > + register_mtd_user(notify); > +} .. rename this function as something like da850_evm_setup_mac_addr() and make it take void as argument and use: register_mtd_user(&da850evm_spi_notifier); Thanks, Sekhar > +#else > +static void da850_evm_register_mtd_user(struct mtd_notifier *notify) { } > +#endif > + > static __init void da850_evm_init(void) > { > int ret; > @@ -1237,6 +1263,8 @@ static __init void da850_evm_init(void) > if (ret) > pr_warning("da850_evm_init: spi 1 registration failed: %d\n", > ret); > + > + da850_evm_register_mtd_user(&da850evm_spi_notifier); > } > > #ifdef CONFIG_SERIAL_8250_CONSOLE > -- > 1.7.1 > > _______________________________________________ > Davinci-linux-open-source mailing list > Davinci-linux-open-source at linux.davincidsp.com > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source > From nsekhar at ti.com Wed Jul 6 11:36:45 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 6 Jul 2011 22:06:45 +0530 Subject: [PATCH v2] davinci: da850: add a .set_rate method to ref_clk In-Reply-To: <1309273851-9238-1-git-send-email-christian.riesch@omicron.at> References: <1309273851-9238-1-git-send-email-christian.riesch@omicron.at> Message-ID: Hi Christian, On Tue, Jun 28, 2011 at 20:40:51, Christian Riesch wrote: > This patch allows setting the input clock frequency of the SoC from > the board specific code using the davinci_set_refclk_rate function. > > Suggested-by: Kevin Hilman > Signed-off-by: Christian Riesch > --- > > v2: fixed the commit message and indentation. > Regards, Christian > > arch/arm/mach-davinci/da850.c | 1 + > 1 files changed, 1 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 133aac4..27fe3ac 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -58,6 +58,7 @@ static struct pll_data pll0_data = { > static struct clk ref_clk = { > .name = "ref_clk", > .rate = DA850_REF_FREQ, > + .set_rate = davinci_simple_set_rate, > }; > > static struct clk pll0_clk = { Thanks, will queue this as a 'feature' for next merge window. Thanks, Sekhar From grant.likely at secretlab.ca Wed Jul 6 16:10:54 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Wed, 6 Jul 2011 15:10:54 -0600 Subject: [RFC/RFT 1/2] gpio/basic_mmio: add support for enable register In-Reply-To: <4E12AC10.9020206@gmail.com> References: <83915224c24e43224272b1bf570cddb9545279a6.1309840042.git.nsekhar@ti.com> <4E12AC10.9020206@gmail.com> Message-ID: <20110706211054.GE5371@ponder.secretlab.ca> On Tue, Jul 05, 2011 at 04:15:44PM +1000, Ryan Mallon wrote: > On 05/07/11 15:10, Sekhar Nori wrote: > >Some GPIO controllers have an enable register > >which needs to be written to before a GPIO > >can be used. > > > >Add support for enabling the GPIO. At this > >time inverted logic for enabling the GPIO > >is not supported. This can be done by adding > >a disable register as and when a controller > >with this comes along. > > > >Signed-off-by: Sekhar Nori > >--- > > > > > >static int bgpio_setup_io(struct bgpio_chip *bgc, > > void __iomem *dat, > >@@ -369,6 +401,7 @@ int __devinit bgpio_init(struct bgpio_chip *bgc, > > void __iomem *clr, > > void __iomem *dirout, > > void __iomem *dirin, > >+ void __iomem *en, > > bool big_endian) > > The arguments to this function are getting a bit unwieldy :-). Maybe > we need to introduce something like: > > struct bgpio_chip_info { > struct device *dev; > unsigned long sz; > void __iomem *dat; > void __iomem *set; > void __iomem *clr; > void __iomem *dirout; > void __iomem *dirin; > void __iomem *en; > bool big_endian; > }; > > and pass that to bgpio_init instead. It would have the added > benefits of making the drivers more readable and that > bgpio_chip_info structs in the drivers can probably be marked > __initdata also. Or, what may be better is to make callers directly update the bgpio_chip structure. From Paul_Stuart at seektech.com Wed Jul 6 16:13:02 2011 From: Paul_Stuart at seektech.com (Paul Stuart) Date: Wed, 6 Jul 2011 14:13:02 -0700 Subject: DM365 DEI interlaced input format? In-Reply-To: <1308020859.2562.6.camel@digitalsys> References: <1308020859.2562.6.camel@digitalsys> Message-ID: <7F1B6BBBDF05C649BBBA3C853F488A611AF22621FF@Hawking.deepsea.com> Hello, About to add the DEI algo to our system for deinterlacing. Video input is from a TVP5151. How should the interlaced video be passed into the DEI? For example, does the DEI expect the two fields of a frame to appear sequentially in memory passed in together, or should they actually be interlaced line-by-line? Or do you pass each field of the frame in as it arrives? Also, we currently run the previewer/resizer in chained mode, which dumps the top field of every frame. To pass both fields to the DEI, am I in for a raft load of work to modify the kernel driver? Thanks, Paul From grant.likely at secretlab.ca Wed Jul 6 17:02:40 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Wed, 6 Jul 2011 16:02:40 -0600 Subject: [RFC/RFT 2/2] davinci: use generic memory mapped gpio for tnetv107x In-Reply-To: <6639b07562e3e6643dd07d5ed3907cb5158ce16b.1309840042.git.nsekhar@ti.com> References: <6639b07562e3e6643dd07d5ed3907cb5158ce16b.1309840042.git.nsekhar@ti.com> Message-ID: <20110706220240.GF5371@ponder.secretlab.ca> On Tue, Jul 05, 2011 at 10:41:00AM +0530, Sekhar Nori wrote: > The GPIO controller on TNETV107x SoC can use > the generic memory mapped GPIO driver. > > Shift to the generic driver instead of the > private implementation. > > Signed-off-by: Sekhar Nori > --- > arch/arm/mach-davinci/Kconfig | 1 + > arch/arm/mach-davinci/Makefile | 1 - > arch/arm/mach-davinci/devices-tnetv107x.c | 68 ++++++++++ > arch/arm/mach-davinci/gpio-tnetv107x.c | 205 ----------------------------- > arch/arm/mach-davinci/tnetv107x.c | 3 - > 5 files changed, 69 insertions(+), 209 deletions(-) > delete mode 100644 arch/arm/mach-davinci/gpio-tnetv107x.c > > diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig > index c0deaca..ec0c6d3 100644 > --- a/arch/arm/mach-davinci/Kconfig > +++ b/arch/arm/mach-davinci/Kconfig > @@ -53,6 +53,7 @@ config ARCH_DAVINCI_DM365 > config ARCH_DAVINCI_TNETV107X > select CPU_V6 > select CP_INTC > + select GPIO_GENERIC_PLATFORM > bool "TNETV107X based system" > > comment "DaVinci Board Type" > diff --git a/arch/arm/mach-davinci/Makefile b/arch/arm/mach-davinci/Makefile > index 0b87a1c..40557c8 100644 > --- a/arch/arm/mach-davinci/Makefile > +++ b/arch/arm/mach-davinci/Makefile > @@ -17,7 +17,6 @@ obj-$(CONFIG_ARCH_DAVINCI_DM365) += dm365.o devices.o > obj-$(CONFIG_ARCH_DAVINCI_DA830) += da830.o devices-da8xx.o > obj-$(CONFIG_ARCH_DAVINCI_DA850) += da850.o devices-da8xx.o > obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += tnetv107x.o devices-tnetv107x.o > -obj-$(CONFIG_ARCH_DAVINCI_TNETV107X) += gpio-tnetv107x.o > > obj-$(CONFIG_AINTC) += irq.o > obj-$(CONFIG_CP_INTC) += cp_intc.o > diff --git a/arch/arm/mach-davinci/devices-tnetv107x.c b/arch/arm/mach-davinci/devices-tnetv107x.c > index 6162cae..eab43b1 100644 > --- a/arch/arm/mach-davinci/devices-tnetv107x.c > +++ b/arch/arm/mach-davinci/devices-tnetv107x.c > @@ -18,6 +18,7 @@ > #include > #include > #include > +#include > > #include > #include > @@ -31,6 +32,7 @@ > #define TNETV107X_TPTC0_BASE 0x01c10000 > #define TNETV107X_TPTC1_BASE 0x01c10400 > #define TNETV107X_WDOG_BASE 0x08086700 > +#define TNETV107X_GPIO_BASE 0x08088000 > #define TNETV107X_TSC_BASE 0x08088500 > #define TNETV107X_SDIO0_BASE 0x08088700 > #define TNETV107X_SDIO1_BASE 0x08088800 > @@ -362,11 +364,77 @@ static struct platform_device ssp_device = { > .resource = ssp_resources, > }; > > +#define TNETV107X_N_GPIO_DEV DIV_ROUND_UP(TNETV107X_N_GPIO, 32) > + > +static struct resource gpio_resources[TNETV107X_N_GPIO_DEV][4] = { > + [0 ... TNETV107X_N_GPIO_DEV - 1] = { If all the data is identical, why does this need to be an array? > + { > + .name = "dat", > + .start = TNETV107X_GPIO_BASE + 0x4, > + .end = TNETV107X_GPIO_BASE + 0x4 + 0x4 - 1, > + }, > + { > + .name = "set", > + .start = TNETV107X_GPIO_BASE + 0x10, > + .end = TNETV107X_GPIO_BASE + 0x10 + 0x4 - 1, > + }, > + { > + .name = "dirin", > + .start = TNETV107X_GPIO_BASE + 0x1c, > + .end = TNETV107X_GPIO_BASE + 0x1c + 0x4 - 1, > + }, > + { > + .name = "en", > + .start = TNETV107X_GPIO_BASE + 0x28, > + .end = TNETV107X_GPIO_BASE + 0x28 + 0x4 - 1, > + }, > + }, > +}; Wow, this ends up looking horrible. (yes, I know it is not your fault). I backed off earlier on using resources for the offsets, but I want to change my mind again and make interface a register range + offsets to the control registers. > + > +static struct platform_device gpio_device[] = { > + [0 ... TNETV107X_N_GPIO_DEV - 1] = { > + .name = "basic-mmio-gpio", > + .num_resources = 4, > + }, > +}; > + > +static struct bgpio_pdata gpio_pdata[TNETV107X_N_GPIO_DEV]; > + > +static void __init tnetv107x_gpio_init(void) > +{ > + int i, j; > + > + for (i = 1; i < TNETV107X_N_GPIO_DEV; i++) { > + for (j = 0; j < 4; j++) { > + gpio_resources[i][j].start += 0x4 * i; > + gpio_resources[i][j].end += 0x4 * i; > + } > + } > + > + for (i = 0; i < TNETV107X_N_GPIO_DEV; i++) { > + int base = i * 32; > + > + gpio_device[i].id = i; > + gpio_device[i].resource = gpio_resources[i]; > + > + gpio_pdata[i].base = base; > + gpio_pdata[i].ngpio = TNETV107X_N_GPIO - base; ? This doesn't look right. Shouldn't ngpio be the same for each gpio controller instance? > + if (gpio_pdata[i].ngpio > 32) > + gpio_pdata[i].ngpio = 32; > + > + gpio_device[i].dev.platform_data = &gpio_pdata[i]; > + > + platform_device_register(&gpio_device[i]); > + } > +} > + > void __init tnetv107x_devices_init(struct tnetv107x_device_info *info) > { > int i, error; > struct clk *tsc_clk; > > + tnetv107x_gpio_init(); > + > /* > * The reset defaults for tnetv107x tsc clock divider is set too high. > * This forces the clock down to a range that allows the ADC to > diff --git a/arch/arm/mach-davinci/gpio-tnetv107x.c b/arch/arm/mach-davinci/gpio-tnetv107x.c > deleted file mode 100644 > index 3fa3e28..0000000 > --- a/arch/arm/mach-davinci/gpio-tnetv107x.c > +++ /dev/null > @@ -1,205 +0,0 @@ > -/* > - * Texas Instruments TNETV107X GPIO Controller > - * > - * Copyright (C) 2010 Texas Instruments > - * > - * 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 > -#include > -#include > - > -#include > -#include > - > -struct tnetv107x_gpio_regs { > - u32 idver; > - u32 data_in[3]; > - u32 data_out[3]; > - u32 direction[3]; > - u32 enable[3]; > -}; > - > -#define gpio_reg_index(gpio) ((gpio) >> 5) > -#define gpio_reg_bit(gpio) BIT((gpio) & 0x1f) > - > -#define gpio_reg_rmw(reg, mask, val) \ > - __raw_writel((__raw_readl(reg) & ~(mask)) | (val), (reg)) > - > -#define gpio_reg_set_bit(reg, gpio) \ > - gpio_reg_rmw((reg) + gpio_reg_index(gpio), 0, gpio_reg_bit(gpio)) > - > -#define gpio_reg_clear_bit(reg, gpio) \ > - gpio_reg_rmw((reg) + gpio_reg_index(gpio), gpio_reg_bit(gpio), 0) > - > -#define gpio_reg_get_bit(reg, gpio) \ > - (__raw_readl((reg) + gpio_reg_index(gpio)) & gpio_reg_bit(gpio)) > - > -#define chip2controller(chip) \ > - container_of(chip, struct davinci_gpio_controller, chip) > - > -#define TNETV107X_GPIO_CTLRS DIV_ROUND_UP(TNETV107X_N_GPIO, 32) > - > -static struct davinci_gpio_controller chips[TNETV107X_GPIO_CTLRS]; > - > -static int tnetv107x_gpio_request(struct gpio_chip *chip, unsigned offset) > -{ > - struct davinci_gpio_controller *ctlr = chip2controller(chip); > - struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; > - unsigned gpio = chip->base + offset; > - unsigned long flags; > - > - spin_lock_irqsave(&ctlr->lock, flags); > - > - gpio_reg_set_bit(regs->enable, gpio); > - > - spin_unlock_irqrestore(&ctlr->lock, flags); > - > - return 0; > -} > - > -static void tnetv107x_gpio_free(struct gpio_chip *chip, unsigned offset) > -{ > - struct davinci_gpio_controller *ctlr = chip2controller(chip); > - struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; > - unsigned gpio = chip->base + offset; > - unsigned long flags; > - > - spin_lock_irqsave(&ctlr->lock, flags); > - > - gpio_reg_clear_bit(regs->enable, gpio); > - > - spin_unlock_irqrestore(&ctlr->lock, flags); > -} > - > -static int tnetv107x_gpio_dir_in(struct gpio_chip *chip, unsigned offset) > -{ > - struct davinci_gpio_controller *ctlr = chip2controller(chip); > - struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; > - unsigned gpio = chip->base + offset; > - unsigned long flags; > - > - spin_lock_irqsave(&ctlr->lock, flags); > - > - gpio_reg_set_bit(regs->direction, gpio); > - > - spin_unlock_irqrestore(&ctlr->lock, flags); > - > - return 0; > -} > - > -static int tnetv107x_gpio_dir_out(struct gpio_chip *chip, > - unsigned offset, int value) > -{ > - struct davinci_gpio_controller *ctlr = chip2controller(chip); > - struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; > - unsigned gpio = chip->base + offset; > - unsigned long flags; > - > - spin_lock_irqsave(&ctlr->lock, flags); > - > - if (value) > - gpio_reg_set_bit(regs->data_out, gpio); > - else > - gpio_reg_clear_bit(regs->data_out, gpio); > - > - gpio_reg_clear_bit(regs->direction, gpio); > - > - spin_unlock_irqrestore(&ctlr->lock, flags); > - > - return 0; > -} > - > -static int tnetv107x_gpio_get(struct gpio_chip *chip, unsigned offset) > -{ > - struct davinci_gpio_controller *ctlr = chip2controller(chip); > - struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; > - unsigned gpio = chip->base + offset; > - int ret; > - > - ret = gpio_reg_get_bit(regs->data_in, gpio); > - > - return ret ? 1 : 0; > -} > - > -static void tnetv107x_gpio_set(struct gpio_chip *chip, > - unsigned offset, int value) > -{ > - struct davinci_gpio_controller *ctlr = chip2controller(chip); > - struct tnetv107x_gpio_regs __iomem *regs = ctlr->regs; > - unsigned gpio = chip->base + offset; > - unsigned long flags; > - > - spin_lock_irqsave(&ctlr->lock, flags); > - > - if (value) > - gpio_reg_set_bit(regs->data_out, gpio); > - else > - gpio_reg_clear_bit(regs->data_out, gpio); > - > - spin_unlock_irqrestore(&ctlr->lock, flags); > -} > - > -static int __init tnetv107x_gpio_setup(void) > -{ > - int i, base; > - unsigned ngpio; > - struct davinci_soc_info *soc_info = &davinci_soc_info; > - struct tnetv107x_gpio_regs *regs; > - struct davinci_gpio_controller *ctlr; > - > - if (soc_info->gpio_type != GPIO_TYPE_TNETV107X) > - return 0; > - > - ngpio = soc_info->gpio_num; > - if (ngpio == 0) { > - pr_err("GPIO setup: how many GPIOs?\n"); > - return -EINVAL; > - } > - > - if (WARN_ON(TNETV107X_N_GPIO < ngpio)) > - ngpio = TNETV107X_N_GPIO; > - > - regs = ioremap(soc_info->gpio_base, SZ_4K); > - if (WARN_ON(!regs)) > - return -EINVAL; > - > - for (i = 0, base = 0; base < ngpio; i++, base += 32) { > - ctlr = &chips[i]; > - > - ctlr->chip.label = "tnetv107x"; > - ctlr->chip.can_sleep = 0; > - ctlr->chip.base = base; > - ctlr->chip.ngpio = ngpio - base; > - if (ctlr->chip.ngpio > 32) > - ctlr->chip.ngpio = 32; > - > - ctlr->chip.request = tnetv107x_gpio_request; > - ctlr->chip.free = tnetv107x_gpio_free; > - ctlr->chip.direction_input = tnetv107x_gpio_dir_in; > - ctlr->chip.get = tnetv107x_gpio_get; > - ctlr->chip.direction_output = tnetv107x_gpio_dir_out; > - ctlr->chip.set = tnetv107x_gpio_set; > - > - spin_lock_init(&ctlr->lock); > - > - ctlr->regs = regs; > - ctlr->set_data = ®s->data_out[i]; > - ctlr->clr_data = ®s->data_out[i]; > - ctlr->in_data = ®s->data_in[i]; > - > - gpiochip_add(&ctlr->chip); > - } > - > - soc_info->gpio_ctlrs = chips; > - soc_info->gpio_ctlrs_num = DIV_ROUND_UP(ngpio, 32); > - return 0; > -} > -pure_initcall(tnetv107x_gpio_setup); > diff --git a/arch/arm/mach-davinci/tnetv107x.c b/arch/arm/mach-davinci/tnetv107x.c > index 1b28fdd..11399d4 100644 > --- a/arch/arm/mach-davinci/tnetv107x.c > +++ b/arch/arm/mach-davinci/tnetv107x.c > @@ -39,7 +39,6 @@ > #define TNETV107X_TIMER0_BASE 0x08086500 > #define TNETV107X_TIMER1_BASE 0x08086600 > #define TNETV107X_CHIP_CFG_BASE 0x08087000 > -#define TNETV107X_GPIO_BASE 0x08088000 > #define TNETV107X_CLOCK_CONTROL_BASE 0x0808a000 > #define TNETV107X_PSC_BASE 0x0808b000 > > @@ -746,9 +745,7 @@ static struct davinci_soc_info tnetv107x_soc_info = { > .intc_irq_prios = irq_prios, > .intc_irq_num = TNETV107X_N_CP_INTC_IRQ, > .intc_host_map = intc_host_map, > - .gpio_base = TNETV107X_GPIO_BASE, > .gpio_type = GPIO_TYPE_TNETV107X, > - .gpio_num = TNETV107X_N_GPIO, > .timer_info = &timer_info, > .serial_dev = &tnetv107x_serial_device, > .reset = tnetv107x_watchdog_reset, > -- > 1.7.3.2 > From sudhakar.raj at ti.com Thu Jul 7 00:27:26 2011 From: sudhakar.raj at ti.com (Rajashekhara, Sudhakar) Date: Thu, 7 Jul 2011 10:57:26 +0530 Subject: [PATCH v2] davinci: da850 EVM: read mac address from SPI flash In-Reply-To: References: <1309784838-30903-1-git-send-email-sudhakar.raj@ti.com> Message-ID: Hi Sekhar, On Wed, Jul 06, 2011 at 21:22:52, Nori, Sekhar wrote: > Hi Sudhakar, > > On Mon, Jul 04, 2011 at 18:37:18, Rajashekhara, Sudhakar wrote: > > DA850/OMAP-L138 EMAC driver uses random mac address instead of > > a fixed one because the mac address is not stuffed into EMAC > > platform data. > > > > This patch provides a function which reads the mac address > > stored in SPI flash (registered as MTD device) and populates the > > EMAC platform data. The function which reads the mac address is > > registered as a callback which gets called upon addition of MTD > > device. > > > > NOTE: In case the MAC address stored in SPI flash is erased, follow > > the instructions at [1] to restore it. > > > > [1] http://processors.wiki.ti.com/index.php/GSG:_OMAP-L138_DVEVM_Additional_Procedures#Restoring_MAC_address_on_SPI_Flash > > > > Signed-off-by: Rajashekhara, Sudhakar > > --- > > Since v1: > > Guarded registering the mtd_notifier only when MTD is enabled. > > Earlier this was handled using mtd_has_partitions() call, but > > this has been removed in Linux v3.0. > > Current trend is to include the change log into the > commit message itself. It might be useful to examine > at a later time what transformation the patch went > through. It also simplifies things for you since you > don't have to maintain this separately. > You are right, this is very helpful. I'll move the change log into the commit message. > > +static void da850_evm_m25p80_notify_add(struct mtd_info *mtd) > > +{ > > + char *mac_addr = davinci_soc_info.emac_pdata->mac_addr; > > + size_t retlen; > > + > > + if (!strcmp(mtd->name, "MAC-Address")) { > > + mtd->read(mtd, 0, ETH_ALEN, &retlen, mac_addr); > > + if (retlen == ETH_ALEN) > > + pr_info("Read MAC addr from SPI Flash: %pM\n", > > + mac_addr); > > + } > > +} > > + > > +static struct mtd_notifier da850evm_spi_notifier = { > > + .add = da850_evm_m25p80_notify_add, > > +}; > > The function and data structure above are not required > when CONFIG_MTD is not defined so these should be > under #ifdef CONFIG_MTD too. Towards this.. > Ok. > > +#ifdef CONFIG_MTD > > +static void da850_evm_register_mtd_user(struct mtd_notifier *notify) > > +{ > > + register_mtd_user(notify); > > +} > > .. rename this function as something like da850_evm_setup_mac_addr() and > make it take void as argument and use: > > register_mtd_user(&da850evm_spi_notifier); > Agreed. I'll include these modifications and send the updated version. Thanks, Sudhakar From sundaram at ti.com Thu Jul 7 07:15:04 2011 From: sundaram at ti.com (Raju, Sundaram) Date: Thu, 7 Jul 2011 17:45:04 +0530 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: <1308203119.10976.234.camel@vkoul-udesk3> References: <20110609124723.GA24636@n2100.arm.linux.org.uk> <1307686140.10976.111.camel@vkoul-udesk3> <1307724439.10976.188.camel@vkoul-udesk3> <1308203119.10976.234.camel@vkoul-udesk3> Message-ID: > -----Original Message----- > From: Koul, Vinod [mailto:vinod.koul at intel.com] > Sent: Thursday, June 16, 2011 11:15 AM > To: Raju, Sundaram > Cc: Russell King - ARM Linux; Linus Walleij; Dan; davinci-linux-open- > source at linux.davincidsp.com; linux-omap at vger.kernel.org; linux- > kernel at vger.kernel.org; linux-arm-kernel at lists.infradead.org > Subject: RE: [RFC] dmaengine: add new api for preparing simple slave transfer > > On Tue, 2011-06-14 at 12:12 +0530, Raju, Sundaram wrote: > Okay now things are more clear on what you are trying to do... Vinod, Sorry for the delayed response. I was OOO and not well. Thanks for going through the design documents and giving options. > > 2) I think this can be achieved in two ways: > a) you use current standard sg_list mechanism, the dmac driver parses > the list and programs the dma offsets into dmac > Pros: you can use existing APIs, no changes to i/f. > If dmac has this capability they program dmac accordingly > Cons: you need to create sg-list in client driver > This option does not satisfy the requirements. As Russell has pointed out > > > > > > The overall conclusion which I'm coming to is that we already support > > > what you're asking for, but the problem is that we're using different > > > (and I'd argue standard) terminology to describe what we have. > > > > > > The only issue which I see that we don't cover is the case where you want > > > to describe a single buffer which is organised as N bytes to be transferred, > > > M following bytes to be skipped, N bytes to be transferred, M bytes to be > > > skipped. I doubt there are many controllers which can be programmed with > > > both 'N' and 'M' parameters directly. > > > > > > > Yes this is what I wanted to communicate and discuss in the list. > > Thanks for describing it in the standard terminology, and pointing me in the > > right direction. > > The sg_list caters to all the parameters that a client driver has to pass to the DMAC driver except for the STRIDE related info of skipping certain bytes within a single buffer entry of the sg_list. > > b) create a new api to describe these offset values, something like: > prep_buffer_offset(struct offset_description *buffer,.....) > I would not like to change the current API for this and rather have a > new API for this, this should better then overriding current. > Yes, it would be better not to change the existing API. This option seems good. But new API has to be added for this option. Linus, had suggested something similar, but different, Using the control API with a new dma_ctrl_cmd enum defined for TI DMAC special configuration to be passed. | Sundaram is this how your controller works? | I mean the hardware can skip over sequences like this? | | When we added the config interface to DMAengine I originally included | a "custom config" call, but Dan wanted me to keep it out until we | had some specific usecase for it. FSLDMA recently started | to use it. | | Notice how dmaengine_slave_config() is implemented: | | static inline int dmaengine_slave_config(struct dma_chan *chan, | struct dma_slave_config *config) | { | return dmaengine_device_control(chan, DMA_SLAVE_CONFIG, | (unsigned long)config); | } | | So what is passed to the driver is just an unsigned long. | | This is actually modeled to be ioctl()-like so you can pass in a | custom config to the same callback on the device driver, | just use some other enumerator than DMA_SLAVE_CONFIG, | say like FSLDMA already does with FSLDMA_EXTERNAL_START. | | Just put some enumerator in enum dma_ctrl_cmd in | dmaengine.h such as SDMA_TEXAS_STRIDE_CONFIG and call | like this: | | /* However that config struct needs to look, basically */ | static struct sdma_ti_stride_cgf = { | take = M, | skip = N, | }; | | ret = chan->device->device_control(chan, SDMA_TEXAS_STRIDE_CONFIG, | &sdma_ti_stride_cfg); | | Or something like this. I think this is better option than your 2b. This requires just an addition of one more enum in the dma_ctrl_cmd. What do you think about this? If Dan and you are okay with this I will send a small patch for this asap. Regards, Sundaram From nsekhar at ti.com Thu Jul 7 07:18:32 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 7 Jul 2011 17:48:32 +0530 Subject: [RFC/RFT 2/2] davinci: use generic memory mapped gpio for tnetv107x In-Reply-To: <20110706220240.GF5371@ponder.secretlab.ca> References: <6639b07562e3e6643dd07d5ed3907cb5158ce16b.1309840042.git.nsekhar@ti.com> <20110706220240.GF5371@ponder.secretlab.ca> Message-ID: Hi Grant, On Thu, Jul 07, 2011 at 03:32:40, Grant Likely wrote: > On Tue, Jul 05, 2011 at 10:41:00AM +0530, Sekhar Nori wrote: > > +#define TNETV107X_N_GPIO_DEV DIV_ROUND_UP(TNETV107X_N_GPIO, 32) > > + > > +static struct resource gpio_resources[TNETV107X_N_GPIO_DEV][4] = { > > + [0 ... TNETV107X_N_GPIO_DEV - 1] = { > > If all the data is identical, why does this need to be an array? The data is initialized identically, but there is a loop down below which updates the start and end address for each GPIO device. This way I get to initialize the name and flags statically. BTW, I just noticed that I forgot to initialize the flags in the resource structure below. Will fix that. > > > + { > > + .name = "dat", > > + .start = TNETV107X_GPIO_BASE + 0x4, > > + .end = TNETV107X_GPIO_BASE + 0x4 + 0x4 - 1, > > + }, > > + { > > + .name = "set", > > + .start = TNETV107X_GPIO_BASE + 0x10, > > + .end = TNETV107X_GPIO_BASE + 0x10 + 0x4 - 1, > > + }, > > + { > > + .name = "dirin", > > + .start = TNETV107X_GPIO_BASE + 0x1c, > > + .end = TNETV107X_GPIO_BASE + 0x1c + 0x4 - 1, > > + }, > > + { > > + .name = "en", > > + .start = TNETV107X_GPIO_BASE + 0x28, > > + .end = TNETV107X_GPIO_BASE + 0x28 + 0x4 - 1, > > + }, > > + }, > > +}; > > Wow, this ends up looking horrible. (yes, I know it is not your > fault). I backed off earlier on using resources for the offsets, but > I want to change my mind again and make interface a register range + > offsets to the control registers. Okay. More work on cleaning the generic driver :) > > > + > > +static struct platform_device gpio_device[] = { > > + [0 ... TNETV107X_N_GPIO_DEV - 1] = { > > + .name = "basic-mmio-gpio", > > + .num_resources = 4, > > + }, > > +}; > > + > > +static struct bgpio_pdata gpio_pdata[TNETV107X_N_GPIO_DEV]; > > + > > +static void __init tnetv107x_gpio_init(void) > > +{ > > + int i, j; > > + > > + for (i = 1; i < TNETV107X_N_GPIO_DEV; i++) { > > + for (j = 0; j < 4; j++) { > > + gpio_resources[i][j].start += 0x4 * i; > > + gpio_resources[i][j].end += 0x4 * i; > > + } > > + } > > + > > + for (i = 0; i < TNETV107X_N_GPIO_DEV; i++) { > > + int base = i * 32; > > + > > + gpio_device[i].id = i; > > + gpio_device[i].resource = gpio_resources[i]; > > + > > + gpio_pdata[i].base = base; > > + gpio_pdata[i].ngpio = TNETV107X_N_GPIO - base; > > ? This doesn't look right. Shouldn't ngpio be the same for each gpio > controller instance? Yes, ngpio is same (32) for each GPIO device except for the last GPIO device which may have lesser number of valid GPIO pins. The if statement below takes care of that. > > > + if (gpio_pdata[i].ngpio > 32) > > + gpio_pdata[i].ngpio = 32; > > + > > + gpio_device[i].dev.platform_data = &gpio_pdata[i]; > > + > > + platform_device_register(&gpio_device[i]); > > + } > > +} > > + Thanks, Sekhar From nsekhar at ti.com Thu Jul 7 11:45:31 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 7 Jul 2011 22:15:31 +0530 Subject: [RFC/RFT 1/2] gpio/basic_mmio: add support for enable register In-Reply-To: <20110706211054.GE5371@ponder.secretlab.ca> References: <83915224c24e43224272b1bf570cddb9545279a6.1309840042.git.nsekhar@ti.com> <4E12AC10.9020206@gmail.com> <20110706211054.GE5371@ponder.secretlab.ca> Message-ID: Hi Grant, On Thu, Jul 07, 2011 at 02:40:54, Grant Likely wrote: > On Tue, Jul 05, 2011 at 04:15:44PM +1000, Ryan Mallon wrote: > > On 05/07/11 15:10, Sekhar Nori wrote: > > >Some GPIO controllers have an enable register > > >which needs to be written to before a GPIO > > >can be used. > > > > > >Add support for enabling the GPIO. At this > > >time inverted logic for enabling the GPIO > > >is not supported. This can be done by adding > > >a disable register as and when a controller > > >with this comes along. > > > > > >Signed-off-by: Sekhar Nori > > >--- > > > > > > > > > >static int bgpio_setup_io(struct bgpio_chip *bgc, > > > void __iomem *dat, > > >@@ -369,6 +401,7 @@ int __devinit bgpio_init(struct bgpio_chip *bgc, > > > void __iomem *clr, > > > void __iomem *dirout, > > > void __iomem *dirin, > > >+ void __iomem *en, > > > bool big_endian) > > > > The arguments to this function are getting a bit unwieldy :-). Maybe > > we need to introduce something like: > > > > struct bgpio_chip_info { > > struct device *dev; > > unsigned long sz; > > void __iomem *dat; > > void __iomem *set; > > void __iomem *clr; > > void __iomem *dirout; > > void __iomem *dirin; > > void __iomem *en; > > bool big_endian; > > }; > > > > and pass that to bgpio_init instead. It would have the added > > benefits of making the drivers more readable and that > > bgpio_chip_info structs in the drivers can probably be marked > > __initdata also. > > Or, what may be better is to make callers directly update the > bgpio_chip structure. I started implementing it this way, but felt that the bgpio_chip structure also has many internal members (like the spinlock) and this method will require users to spend quite a bit of time figuring out which structure members they should initialize and which to leave for bgpio_init() to do for them. There is also the case of direction register which is set from either dirout or dirin depending upon whether the logic is inverted or not. The bgpio_chip however needs to deal with a single direction register offset. Considering these, I am getting inclined towards Ryan's suggestion. Thoughts? Thanks, Sekhar From grant.likely at secretlab.ca Thu Jul 7 13:37:34 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Thu, 7 Jul 2011 12:37:34 -0600 Subject: [RFC/RFT 1/2] gpio/basic_mmio: add support for enable register In-Reply-To: References: <83915224c24e43224272b1bf570cddb9545279a6.1309840042.git.nsekhar@ti.com> <4E12AC10.9020206@gmail.com> <20110706211054.GE5371@ponder.secretlab.ca> Message-ID: <20110707183734.GE2824@ponder.secretlab.ca> On Thu, Jul 07, 2011 at 10:15:31PM +0530, Nori, Sekhar wrote: > Hi Grant, > > On Thu, Jul 07, 2011 at 02:40:54, Grant Likely wrote: > > On Tue, Jul 05, 2011 at 04:15:44PM +1000, Ryan Mallon wrote: > > > On 05/07/11 15:10, Sekhar Nori wrote: > > > >Some GPIO controllers have an enable register > > > >which needs to be written to before a GPIO > > > >can be used. > > > > > > > >Add support for enabling the GPIO. At this > > > >time inverted logic for enabling the GPIO > > > >is not supported. This can be done by adding > > > >a disable register as and when a controller > > > >with this comes along. > > > > > > > >Signed-off-by: Sekhar Nori > > > >--- > > > > > > > > > > > > > >static int bgpio_setup_io(struct bgpio_chip *bgc, > > > > void __iomem *dat, > > > >@@ -369,6 +401,7 @@ int __devinit bgpio_init(struct bgpio_chip *bgc, > > > > void __iomem *clr, > > > > void __iomem *dirout, > > > > void __iomem *dirin, > > > >+ void __iomem *en, > > > > bool big_endian) > > > > > > The arguments to this function are getting a bit unwieldy :-). Maybe > > > we need to introduce something like: > > > > > > struct bgpio_chip_info { > > > struct device *dev; > > > unsigned long sz; > > > void __iomem *dat; > > > void __iomem *set; > > > void __iomem *clr; > > > void __iomem *dirout; > > > void __iomem *dirin; > > > void __iomem *en; > > > bool big_endian; > > > }; > > > > > > and pass that to bgpio_init instead. It would have the added > > > benefits of making the drivers more readable and that > > > bgpio_chip_info structs in the drivers can probably be marked > > > __initdata also. > > > > Or, what may be better is to make callers directly update the > > bgpio_chip structure. > > I started implementing it this way, but felt that the bgpio_chip > structure also has many internal members (like the spinlock) and > this method will require users to spend quite a bit of time figuring > out which structure members they should initialize and which to leave > for bgpio_init() to do for them. > > There is also the case of direction register which is set from > either dirout or dirin depending upon whether the logic is inverted > or not. The bgpio_chip however needs to deal with a single direction > register offset. We *absolutely* still need the helper function for the complex settings, but for the non-complex ones, I'd rather just directly access the structure. The kerneldoc documentation of the structure can and should be explicit about what the caller is allowed to do. g. From rmallon at gmail.com Thu Jul 7 16:23:55 2011 From: rmallon at gmail.com (Ryan Mallon) Date: Fri, 08 Jul 2011 07:23:55 +1000 Subject: [RFC/RFT 1/2] gpio/basic_mmio: add support for enable register In-Reply-To: <20110707183734.GE2824@ponder.secretlab.ca> References: <83915224c24e43224272b1bf570cddb9545279a6.1309840042.git.nsekhar@ti.com> <4E12AC10.9020206@gmail.com> <20110706211054.GE5371@ponder.secretlab.ca> <20110707183734.GE2824@ponder.secretlab.ca> Message-ID: <4E1623EB.3010203@gmail.com> On 08/07/11 04:37, Grant Likely wrote: > On Thu, Jul 07, 2011 at 10:15:31PM +0530, Nori, Sekhar wrote: >> Hi Grant, >> >> On Thu, Jul 07, 2011 at 02:40:54, Grant Likely wrote: >>> On Tue, Jul 05, 2011 at 04:15:44PM +1000, Ryan Mallon wrote: >>>> On 05/07/11 15:10, Sekhar Nori wrote: >>>>> Some GPIO controllers have an enable register >>>>> which needs to be written to before a GPIO >>>>> can be used. >>>>> >>>>> Add support for enabling the GPIO. At this >>>>> time inverted logic for enabling the GPIO >>>>> is not supported. This can be done by adding >>>>> a disable register as and when a controller >>>>> with this comes along. >>>>> >>>>> Signed-off-by: Sekhar Nori >>>>> --- >>>>> >>>> >>>> >>>>> static int bgpio_setup_io(struct bgpio_chip *bgc, >>>>> void __iomem *dat, >>>>> @@ -369,6 +401,7 @@ int __devinit bgpio_init(struct bgpio_chip *bgc, >>>>> void __iomem *clr, >>>>> void __iomem *dirout, >>>>> void __iomem *dirin, >>>>> + void __iomem *en, >>>>> bool big_endian) >>>> The arguments to this function are getting a bit unwieldy :-). Maybe >>>> we need to introduce something like: >>>> >>>> struct bgpio_chip_info { >>>> struct device *dev; >>>> unsigned long sz; >>>> void __iomem *dat; >>>> void __iomem *set; >>>> void __iomem *clr; >>>> void __iomem *dirout; >>>> void __iomem *dirin; >>>> void __iomem *en; >>>> bool big_endian; >>>> }; >>>> >>>> and pass that to bgpio_init instead. It would have the added >>>> benefits of making the drivers more readable and that >>>> bgpio_chip_info structs in the drivers can probably be marked >>>> __initdata also. >>> Or, what may be better is to make callers directly update the >>> bgpio_chip structure. >> I started implementing it this way, but felt that the bgpio_chip >> structure also has many internal members (like the spinlock) and >> this method will require users to spend quite a bit of time figuring >> out which structure members they should initialize and which to leave >> for bgpio_init() to do for them. >> >> There is also the case of direction register which is set from >> either dirout or dirin depending upon whether the logic is inverted >> or not. The bgpio_chip however needs to deal with a single direction >> register offset. > We *absolutely* still need the helper function for the complex > settings, but for the non-complex ones, I'd rather just directly > access the structure. The kerneldoc documentation of the structure > can and should be explicit about what the caller is allowed to do. > You could pull out all of the user accessible parts the bgpio_chip structure in to a structure called bgpio_chip_info (or whatever) that the drivers fill in and pass to bgpio_init, which then gets assigned as a member of bgpio_chip. Not sure what you mean about the helper function. If bgpio_init takes a structure with all of the information about the particular chip then initialisation of either a simple or complex gpio chip is done using the same function. Only the number of fields in the bgpio_chip_info structure which need to be filled in should change. ~Ryan From ido at wizery.com Thu Jul 7 16:33:05 2011 From: ido at wizery.com (Ido Yariv) Date: Fri, 8 Jul 2011 00:33:05 +0300 Subject: [PATCH 0/5] arm: davinci: DA850: wl12xx expansion board Message-ID: <1310074390-4277-1-git-send-email-ido@wizery.com> The following series adds support for an optional wl12xx based expansion board for the DA850: http://processors.wiki.ti.com/index.php/AM18x_%2B_WL1271 The first couple of patches address issues which had to be fixed for this series to work. However, these should be fixed regardless of the DA850 expansion board support, and could influence other Davinci platforms. Only the Wi-Fi functionality of the expansion board is supported. Bluetooth functionality is not yet supported. The patches are based on v3.0-rc6. Ido. Ido Yariv (5): arm: davinci: Fix low level gpio irq handlers' argument arm: davinci: Allow EVENTQ_0 as a default queue arm: davinci: DA850: Set a default queue for CC1 arm: davinci: mmc: Add support for set_power callback arm: davinci: DA850: Add wl12xx expansion board support arch/arm/mach-davinci/Kconfig | 31 +++++++ arch/arm/mach-davinci/board-da850-evm.c | 128 +++++++++++++++++++++++++++++ arch/arm/mach-davinci/da850.c | 9 ++ arch/arm/mach-davinci/devices-da8xx.c | 3 + arch/arm/mach-davinci/dm365.c | 4 +- arch/arm/mach-davinci/dma.c | 5 +- arch/arm/mach-davinci/gpio.c | 16 +++- arch/arm/mach-davinci/include/mach/edma.h | 2 +- arch/arm/mach-davinci/include/mach/mmc.h | 3 + arch/arm/mach-davinci/include/mach/mux.h | 10 ++ drivers/mmc/host/davinci_mmc.c | 13 +++ 11 files changed, 216 insertions(+), 8 deletions(-) -- 1.7.4.1 From ido at wizery.com Thu Jul 7 16:33:06 2011 From: ido at wizery.com (Ido Yariv) Date: Fri, 8 Jul 2011 00:33:06 +0300 Subject: [PATCH 1/5] arm: davinci: Fix low level gpio irq handlers' argument In-Reply-To: <1310074390-4277-1-git-send-email-ido@wizery.com> References: <1310074390-4277-1-git-send-email-ido@wizery.com> Message-ID: <1310074390-4277-2-git-send-email-ido@wizery.com> Commit 7416401 ("arm: davinci: Fix fallout from generic irq chip conversion") introduced a bug, causing low level interrupt handlers to get a bogus irq number as an argument. The gpio irq handler falsely assumes that the handler data is the irq base number and that is no longer true. Fix this by converting gpio_irq_handler's bank_irq argument to the corresponding irq base number. Signed-off-by: Ido Yariv CC: Thomas Gleixner --- arch/arm/mach-davinci/gpio.c | 16 ++++++++++++---- 1 files changed, 12 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-davinci/gpio.c b/arch/arm/mach-davinci/gpio.c index e722139..db6355a 100644 --- a/arch/arm/mach-davinci/gpio.c +++ b/arch/arm/mach-davinci/gpio.c @@ -249,8 +249,16 @@ static struct irq_chip gpio_irqchip = { .flags = IRQCHIP_SET_TYPE_MASKED, }; +static unsigned int first_irq_in_bankirq(unsigned int bank_irq) +{ + struct davinci_soc_info *soc_info = &davinci_soc_info; + + /* Each irq bank consists of up to 16 irqs */ + return gpio_to_irq(0) + (16 * (bank_irq - soc_info->gpio_irq)); +} + static void -gpio_irq_handler(unsigned irq, struct irq_desc *desc) +gpio_irq_handler(unsigned bank_irq, struct irq_desc *desc) { struct davinci_gpio_regs __iomem *g; u32 mask = 0xffff; @@ -258,7 +266,7 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc) g = (__force struct davinci_gpio_regs __iomem *) irq_desc_get_handler_data(desc); /* we only care about one bank */ - if (irq & 1) + if (bank_irq & 1) mask <<= 16; /* temporarily mask (level sensitive) parent IRQ */ @@ -274,11 +282,11 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc) if (!status) break; __raw_writel(status, &g->intstat); - if (irq & 1) + if (bank_irq & 1) status >>= 16; /* now demux them to the right lowlevel handler */ - n = (int)irq_get_handler_data(irq); + n = first_irq_in_bankirq(bank_irq); while (status) { res = ffs(status); n += res; -- 1.7.4.1 From ido at wizery.com Thu Jul 7 16:33:07 2011 From: ido at wizery.com (Ido Yariv) Date: Fri, 8 Jul 2011 00:33:07 +0300 Subject: [PATCH 2/5] arm: davinci: Allow EVENTQ_0 as a default queue In-Reply-To: <1310074390-4277-1-git-send-email-ido@wizery.com> References: <1310074390-4277-1-git-send-email-ido@wizery.com> Message-ID: <1310074390-4277-3-git-send-email-ido@wizery.com> Davinci platforms may define a default queue for each channel controller. If one is not defined, the default queue is set to EVENTQ_1. However, there's no way to distinguish between an unset default queue to one that is set to EVENTQ_0, as EVENTQ_0 = 0. In order to keep existing behaviour on platforms which don't specify a default_queue member, the default_queue member was modified to be a pointer to enum dma_event_q. A NULL value means that this member was not specified. Signed-off-by: Ido Yariv --- arch/arm/mach-davinci/dm365.c | 4 +++- arch/arm/mach-davinci/dma.c | 5 +++-- arch/arm/mach-davinci/include/mach/edma.h | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 4604e72..b1cf08e 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -827,6 +827,8 @@ dm365_queue_priority_mapping[][2] = { {-1, -1}, }; +static enum dma_event_q dm365_edma_cc0_default_queue = EVENTQ_3; + static struct edma_soc_info edma_cc0_info = { .n_channel = 64, .n_region = 4, @@ -835,7 +837,7 @@ static struct edma_soc_info edma_cc0_info = { .n_cc = 1, .queue_tc_mapping = dm365_queue_tc_mapping, .queue_priority_mapping = dm365_queue_priority_mapping, - .default_queue = EVENTQ_3, + .default_queue_ptr = &dm365_edma_cc0_default_queue, }; static struct edma_soc_info *dm365_edma_info[EDMA_MAX_CC] = { diff --git a/arch/arm/mach-davinci/dma.c b/arch/arm/mach-davinci/dma.c index 6b96698..11c71e7 100644 --- a/arch/arm/mach-davinci/dma.c +++ b/arch/arm/mach-davinci/dma.c @@ -1449,8 +1449,9 @@ static int __init edma_probe(struct platform_device *pdev) edma_cc[j]->num_cc = min_t(unsigned, info[j]->n_cc, EDMA_MAX_CC); - edma_cc[j]->default_queue = info[j]->default_queue; - if (!edma_cc[j]->default_queue) + if (info[j]->default_queue_ptr) + edma_cc[j]->default_queue = *info[j]->default_queue_ptr; + else edma_cc[j]->default_queue = EVENTQ_1; dev_dbg(&pdev->dev, "DMA REG BASE ADDR=%p\n", diff --git a/arch/arm/mach-davinci/include/mach/edma.h b/arch/arm/mach-davinci/include/mach/edma.h index 20c77f2..7f81812 100644 --- a/arch/arm/mach-davinci/include/mach/edma.h +++ b/arch/arm/mach-davinci/include/mach/edma.h @@ -250,7 +250,7 @@ struct edma_soc_info { unsigned n_slot; unsigned n_tc; unsigned n_cc; - enum dma_event_q default_queue; + enum dma_event_q *default_queue_ptr; /* Resource reservation for other cores */ struct edma_rsv_info *rsv; -- 1.7.4.1 From ido at wizery.com Thu Jul 7 16:33:08 2011 From: ido at wizery.com (Ido Yariv) Date: Fri, 8 Jul 2011 00:33:08 +0300 Subject: [PATCH 3/5] arm: davinci: DA850: Set a default queue for CC1 In-Reply-To: <1310074390-4277-1-git-send-email-ido@wizery.com> References: <1310074390-4277-1-git-send-email-ido@wizery.com> Message-ID: <1310074390-4277-4-git-send-email-ido@wizery.com> If a default queue is not set for a channel controller, EVENTQ_1 is used. The second channel controller has only one event queue, and so, EVENTQ_1 is an invalid option. Fix this by explicitly setting the default queue for CC1 to be EVENTQ_0. Signed-off-by: Ido Yariv --- arch/arm/mach-davinci/devices-da8xx.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index fc4e98e..74114c0 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -139,6 +139,8 @@ static struct edma_soc_info *da830_edma_info[EDMA_MAX_CC] = { &da830_edma_cc0_info, }; +static enum dma_event_q da850_edma_cc1_default_queue = EVENTQ_0; + static struct edma_soc_info da850_edma_cc_info[] = { { .n_channel = 32, @@ -157,6 +159,7 @@ static struct edma_soc_info da850_edma_cc_info[] = { .n_cc = 1, .queue_tc_mapping = da850_queue_tc_mapping, .queue_priority_mapping = da850_queue_priority_mapping, + .default_queue_ptr = &da850_edma_cc1_default_queue, }, }; -- 1.7.4.1 From ido at wizery.com Thu Jul 7 16:33:09 2011 From: ido at wizery.com (Ido Yariv) Date: Fri, 8 Jul 2011 00:33:09 +0300 Subject: [PATCH 4/5] arm: davinci: mmc: Add support for set_power callback In-Reply-To: <1310074390-4277-1-git-send-email-ido@wizery.com> References: <1310074390-4277-1-git-send-email-ido@wizery.com> Message-ID: <1310074390-4277-5-git-send-email-ido@wizery.com> Some devices connected to the MMC bus are power controlled by external means. For instance, an SDIO device may be powered down/up by an external gpio line. In order to avoid toggling power from within the MMC host driver, add a set_power callback function, which will be called by set_ios upon powering down/up. Signed-off-by: Ido Yariv --- arch/arm/mach-davinci/include/mach/mmc.h | 3 +++ drivers/mmc/host/davinci_mmc.c | 13 +++++++++++++ 2 files changed, 16 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/mmc.h b/arch/arm/mach-davinci/include/mach/mmc.h index d4f1e96..5ba6b22 100644 --- a/arch/arm/mach-davinci/include/mach/mmc.h +++ b/arch/arm/mach-davinci/include/mach/mmc.h @@ -12,6 +12,9 @@ struct davinci_mmc_config { /* get_cd()/get_wp() may sleep */ int (*get_cd)(int module); int (*get_ro)(int module); + + void (*set_power)(int module, bool on); + /* wires == 0 is equivalent to wires == 4 (4-bit parallel) */ u8 wires; diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 0076c74..64a8325 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -807,12 +807,25 @@ static void calculate_clk_divider(struct mmc_host *mmc, struct mmc_ios *ios) static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmc_davinci_host *host = mmc_priv(mmc); + struct platform_device *pdev = to_platform_device(mmc->parent); + struct davinci_mmc_config *config = pdev->dev.platform_data; dev_dbg(mmc_dev(host->mmc), "clock %dHz busmode %d powermode %d Vdd %04x\n", ios->clock, ios->bus_mode, ios->power_mode, ios->vdd); + switch (ios->power_mode) { + case MMC_POWER_OFF: + if (config && config->set_power) + config->set_power(pdev->id, false); + break; + case MMC_POWER_UP: + if (config && config->set_power) + config->set_power(pdev->id, true); + break; + } + switch (ios->bus_width) { case MMC_BUS_WIDTH_8: dev_dbg(mmc_dev(host->mmc), "Enabling 8 bit mode\n"); -- 1.7.4.1 From ido at wizery.com Thu Jul 7 16:33:10 2011 From: ido at wizery.com (Ido Yariv) Date: Fri, 8 Jul 2011 00:33:10 +0300 Subject: [PATCH 5/5] arm: davinci: DA850: Add wl12xx expansion board support In-Reply-To: <1310074390-4277-1-git-send-email-ido@wizery.com> References: <1310074390-4277-1-git-send-email-ido@wizery.com> Message-ID: <1310074390-4277-6-git-send-email-ido@wizery.com> The DA850 supports an optional wl12xx based expansion board, adding WLAN & BT capabilities. The wl12xx is a 4-wire, 1.8V, embedded SDIO WLAN device with an external IRQ line and is power-controlled by a GPIO-based fixed regulator. This patch adds support for the WLAN capabilities of this expansion board. Signed-off-by: Ido Yariv --- arch/arm/mach-davinci/Kconfig | 31 +++++++ arch/arm/mach-davinci/board-da850-evm.c | 128 ++++++++++++++++++++++++++++++ arch/arm/mach-davinci/da850.c | 9 ++ arch/arm/mach-davinci/include/mach/mux.h | 10 +++ 4 files changed, 178 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index c0deaca..1a9149c 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -192,6 +192,37 @@ config DA850_UI_RMII endchoice +config DA850_WL12XX + bool "DA850 wl12xx expansion board" + depends on MACH_DAVINCI_DA850_EVM + ---help--- + Say Y if you want to use a wl12xx expansion board connected to the + DA850 EVM. + +choice + prompt "FREF reference clock used by the wl12XX expansion board" + default DA850_WL12XX_FREF_38_4 + depends on DA850_WL12XX + help + There are currently two kinds of such daughter boards, one made by + Mistral and another by LS Research. These (and perhaps others) use + different FREF reference clocks. Select the correct one according to + the daughter board's type. + +config DA850_WL12XX_FREF_19_2 + bool "19.2MHz" +config DA850_WL12XX_FREF_26 + bool "26MHz" +config DA850_WL12XX_FREF_38_4 + bool "38.4MHz" +config DA850_WL12XX_FREF_52 + bool "52MHz" +config DA850_WL12XX_FREF_XTAL_26 + bool "XTAL 26MHz" +config DA850_WL12XX_FREF_XTAL_38_4 + bool "XTAL 38MHz" +endchoice + config GPIO_PCA953X default MACH_DAVINCI_DA850_EVM diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..a567379 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include @@ -49,6 +51,9 @@ #define DA850_MMCSD_CD_PIN GPIO_TO_PIN(4, 0) #define DA850_MMCSD_WP_PIN GPIO_TO_PIN(4, 1) +#define DA850_WLAN_EN GPIO_TO_PIN(6, 9) +#define DA850_WLAN_IRQ GPIO_TO_PIN(6, 10) + #define DA850_MII_MDIO_CLKEN_PIN GPIO_TO_PIN(2, 6) static struct mtd_partition da850evm_spiflash_part[] = { @@ -1117,6 +1122,127 @@ static __init int da850_evm_init_cpufreq(void) static __init int da850_evm_init_cpufreq(void) { return 0; } #endif +#ifdef CONFIG_DA850_WL12XX + +static int da850_wl12xx_fref = -1; + +static int __init setup_da850_wl12xx_fref(char *fref) +{ + if (!strcmp(fref, "19.2")) + da850_wl12xx_fref = WL12XX_REFCLOCK_19; + else if (!strcmp(fref, "26")) + da850_wl12xx_fref = WL12XX_REFCLOCK_26; + else if (!strcmp(fref, "38.4")) + da850_wl12xx_fref = WL12XX_REFCLOCK_38; + else if (!strcmp(fref, "52")) + da850_wl12xx_fref = WL12XX_REFCLOCK_52; + else if (!strcmp(fref, "XTAL26")) + da850_wl12xx_fref = WL12XX_REFCLOCK_26_XTAL; + else if (!strcmp(fref, "XTAL38.4")) + da850_wl12xx_fref = WL12XX_REFCLOCK_38_XTAL; + else + pr_info("da850_wl12xx_fref is invalid. Valid options: " + "19.2, 26, 38.4, 52, XTAL26 or XTAL38.4\n"); + return 0; +} +__setup("da850_wl12xx_fref=", setup_da850_wl12xx_fref); + +static void wl12xx_set_power(int slot, bool power_on) +{ + static bool power_state; + + pr_debug("Powering %s wl12xx", (power_on ? "on" : "off")); + + if (power_on == power_state) + return; + power_state = power_on; + + if (power_on) { + /* Power up sequence required for wl127x devices */ + gpio_set_value(DA850_WLAN_EN, 1); + mdelay(15); + gpio_set_value(DA850_WLAN_EN, 0); + mdelay(1); + gpio_set_value(DA850_WLAN_EN, 1); + mdelay(70); + } else { + gpio_set_value(DA850_WLAN_EN, 0); + } +} + +static struct davinci_mmc_config da850_mmc_wl12xx_config = { + .get_ro = NULL, + .get_cd = NULL, + .set_power = wl12xx_set_power, + .wires = 4, + .max_freq = 25000000, + .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NONREMOVABLE | + MMC_CAP_POWER_OFF_CARD, + .version = MMC_CTLR_VERSION_2, +}; + +static const short da850_evm_mmc_wl12xx_pins[] __initconst = { + DA850_MMCSD1_DAT_0, DA850_MMCSD1_DAT_1, DA850_MMCSD1_DAT_2, + DA850_MMCSD1_DAT_3, DA850_MMCSD1_CLK, DA850_MMCSD1_CMD, + DA850_GPIO6_9, DA850_GPIO6_10, + -1 +}; + +static struct wl12xx_platform_data da850_wl12xx_wlan_data __initdata = { + .irq = -1, +#ifdef CONFIG_DA850_WL12XX_FREF_19_2 + .board_ref_clock = WL12XX_REFCLOCK_19, +#elif defined CONFIG_DA850_WL12XX_FREF_26 + .board_ref_clock = WL12XX_REFCLOCK_26, +#elif defined CONFIG_DA850_WL12XX_FREF_38_4 + .board_ref_clock = WL12XX_REFCLOCK_38, +#elif defined CONFIG_DA850_WL12XX_FREF_52 + .board_ref_clock = WL12XX_REFCLOCK_52, +#elif defined CONFIG_DA850_WL12XX_FREF_XTAL_26 + .board_ref_clock = WL12XX_REFCLOCK_26_XTAL, +#elif defined CONFIG_DA850_WL12XX_FREF_XTAL_38_4 + .board_ref_clock = WL12XX_REFCLOCK_38_XTAL, +#endif + .platform_quirks = WL12XX_PLATFORM_QUIRK_EDGE_IRQ, +}; + +static void da850_wl12xx_init(void) +{ + int ret; + + ret = davinci_cfg_reg_list(da850_evm_mmc_wl12xx_pins); + if (ret) + pr_warning("da850_evm_init: wl12xx/mmc mux setup failed:" + " %d\n", ret); + + ret = da850_register_mmcsd1(&da850_mmc_wl12xx_config); + if (ret) + pr_warning("da850_evm_init: wl12xx/mmc registration failed:" + " %d\n", ret); + + if (gpio_request(DA850_WLAN_EN, "wl12xx_en") || + gpio_direction_output(DA850_WLAN_EN, 0)) + pr_err("Error initializing the wl12xx enable gpio\n"); + + if (gpio_request(DA850_WLAN_IRQ, "wl12xx_irq") || + gpio_direction_input(DA850_WLAN_IRQ)) + pr_err("Error initializing the wl12xx irq gpio\n"); + else + da850_wl12xx_wlan_data.irq = gpio_to_irq(DA850_WLAN_IRQ); + + if (da850_wl12xx_fref != -1) + da850_wl12xx_wlan_data.board_ref_clock = da850_wl12xx_fref; + + if (wl12xx_set_platform_data(&da850_wl12xx_wlan_data)) + pr_err("Error setting wl12xx data\n"); +} + +#else /* CONFIG_DA850_WL12XX */ + +static void da850_wl12xx_init(void) { } + +#endif /* CONFIG_DA850_WL12XX */ + static __init void da850_evm_init(void) { int ret; @@ -1169,6 +1295,8 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: mmcsd0 registration failed:" " %d\n", ret); + + da850_wl12xx_init(); } davinci_serial_init(&da850_evm_uart_config); diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 133aac4..bfe9b71 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -525,6 +525,13 @@ static const struct mux_config da850_pins[] = { MUX_CFG(DA850, MMCSD0_DAT_3, 10, 20, 15, 2, false) MUX_CFG(DA850, MMCSD0_CLK, 10, 0, 15, 2, false) MUX_CFG(DA850, MMCSD0_CMD, 10, 4, 15, 2, false) + /* MMC/SD1 function */ + MUX_CFG(DA850, MMCSD1_DAT_0, 18, 8, 15, 2, false) + MUX_CFG(DA850, MMCSD1_DAT_1, 19, 16, 15, 2, false) + MUX_CFG(DA850, MMCSD1_DAT_2, 19, 12, 15, 2, false) + MUX_CFG(DA850, MMCSD1_DAT_3, 19, 8, 15, 2, false) + MUX_CFG(DA850, MMCSD1_CLK, 18, 12, 15, 2, false) + MUX_CFG(DA850, MMCSD1_CMD, 18, 16, 15, 2, false) /* EMIF2.5/EMIFA function */ MUX_CFG(DA850, EMA_D_7, 9, 0, 15, 1, false) MUX_CFG(DA850, EMA_D_6, 9, 4, 15, 1, false) @@ -583,6 +590,8 @@ static const struct mux_config da850_pins[] = { MUX_CFG(DA850, GPIO3_13, 7, 8, 15, 8, false) MUX_CFG(DA850, GPIO4_0, 10, 28, 15, 8, false) MUX_CFG(DA850, GPIO4_1, 10, 24, 15, 8, false) + MUX_CFG(DA850, GPIO6_9, 13, 24, 15, 8, false) + MUX_CFG(DA850, GPIO6_10, 13, 20, 15, 8, false) MUX_CFG(DA850, GPIO6_13, 13, 8, 15, 8, false) MUX_CFG(DA850, RTC_ALARM, 0, 28, 15, 2, false) #endif diff --git a/arch/arm/mach-davinci/include/mach/mux.h b/arch/arm/mach-davinci/include/mach/mux.h index 5d4e0fe..a7e92fc 100644 --- a/arch/arm/mach-davinci/include/mach/mux.h +++ b/arch/arm/mach-davinci/include/mach/mux.h @@ -857,6 +857,14 @@ enum davinci_da850_index { DA850_MMCSD0_CLK, DA850_MMCSD0_CMD, + /* MMC/SD1 function */ + DA850_MMCSD1_DAT_0, + DA850_MMCSD1_DAT_1, + DA850_MMCSD1_DAT_2, + DA850_MMCSD1_DAT_3, + DA850_MMCSD1_CLK, + DA850_MMCSD1_CMD, + /* EMIF2.5/EMIFA function */ DA850_EMA_D_7, DA850_EMA_D_6, @@ -916,6 +924,8 @@ enum davinci_da850_index { DA850_GPIO3_13, DA850_GPIO4_0, DA850_GPIO4_1, + DA850_GPIO6_9, + DA850_GPIO6_10, DA850_GPIO6_13, DA850_RTC_ALARM, }; -- 1.7.4.1 From linus.walleij at linaro.org Thu Jul 7 14:37:53 2011 From: linus.walleij at linaro.org (Linus Walleij) Date: Thu, 7 Jul 2011 21:37:53 +0200 Subject: [RFC] dmaengine: add new api for preparing simple slave transfer In-Reply-To: References: <20110609124723.GA24636@n2100.arm.linux.org.uk> <1307686140.10976.111.camel@vkoul-udesk3> <1307724439.10976.188.camel@vkoul-udesk3> <1308203119.10976.234.camel@vkoul-udesk3> Message-ID: 2011/7/7 Raju, Sundaram : > | Just put some enumerator in enum dma_ctrl_cmd in > | dmaengine.h such as SDMA_TEXAS_STRIDE_CONFIG and call > | like this: > | > | /* However that config struct needs to look, basically */ > | static struct sdma_ti_stride_cgf = { > | ? ? ?take = M, > | ? ? ?skip = N, > | }; > | > | ret = chan->device->device_control(chan, SDMA_TEXAS_STRIDE_CONFIG, > | &sdma_ti_stride_cfg); > | > | Or something like this. > > I think this is better option than your 2b. This requires just an addition of > one more enum in the dma_ctrl_cmd. What do you think about this? > If Dan and you are okay with this I will send a small patch for this asap. I think this is the best way to solve it atleast. It's clear what is being done, and it's easy for a client trying to create such a slave transfer to back out if it turns out that the DMAC in use does not support this striding. Send a patch out and I'll Ack it, FWIW. Linus Walleij From nsekhar at ti.com Fri Jul 8 04:06:03 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 8 Jul 2011 14:36:03 +0530 Subject: [GIT PULL] DaVinci cleanups for v3.1 Message-ID: Hi Arnd, Please pull the following DaVinci clean-ups for the v3.1 merge window. The patch "davinci: dm6467/T EVM: fix setting up of reference clock rate" ends up adding extra code, but should make adding new boards with different reference clock rate very simple. There are further clean-ups pending merge of SRAM consolidation patch by Russell. If I see that getting merged, I can send a follow-up pull request. There is also another patch for which I am awaiting Mauro's ack. Thanks, Sekhar The following changes since commit fe0d42203cb5616eeff68b14576a0f7e2dd56625: Linus Torvalds (1): Linux 3.0-rc6 are available in the git repository at: git://gitorious.org/linux-davinci/linux-davinci.git cleanup Sekhar Nori (2): davinci: psc.h: clean up indentation done using spaces davinci: dm6467/T EVM: fix setting up of reference clock rate arch/arm/mach-davinci/board-dm646x-evm.c | 17 +-- arch/arm/mach-davinci/clock.c | 38 +++++++ arch/arm/mach-davinci/clock.h | 2 + arch/arm/mach-davinci/dm646x.c | 4 +- arch/arm/mach-davinci/include/mach/dm646x.h | 2 - arch/arm/mach-davinci/include/mach/psc.h | 148 +++++++++++++------------- 6 files changed, 123 insertions(+), 88 deletions(-) From nsekhar at ti.com Fri Jul 8 04:13:04 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 8 Jul 2011 14:43:04 +0530 Subject: [GIT PULL] DaVinci features for v3.1 Message-ID: Hi Arnd, Please pull the following DaVinci features for v3.1. This pull request depends on the patch "davinci: dm6467/T EVM: fix setting up of reference clock rate" in clean-up pull request for successful build. Pulling this after the earlier clean-up pull request generates this merge conflict: <<<<<<< HEAD:arch/arm/mach-davinci/include/mach/psc.h #define MDSTAT_STATE_MASK 0x1f ======= #define MDSTAT_STATE_MASK 0x1f #define MDCTL_FORCE BIT(31) >>>>>>> 8bb2c4813c534d26eba3beb7888fbd3cbc931f62:arch/arm/mach-davinci/include/mach/psc.h The resolution is: #define MDSTAT_STATE_MASK 0x1f #define MDCTL_FORCE BIT(31) Thanks, Sekhar The following changes since commit fe0d42203cb5616eeff68b14576a0f7e2dd56625: Linus Torvalds (1): Linux 3.0-rc6 are available in the git repository at: git://gitorious.org/linux-davinci/linux-davinci.git features Christian Riesch (1): davinci: da850: add a .set_rate method to ref_clk Sekhar Nori (4): davinci: pass clock flags to davinci_psc_config() davinci: enable forced transitions on PSC davinci: da850: add support for SATA interface davinci: da850 evm: register SATA device arch/arm/mach-davinci/board-da850-evm.c | 7 ++ arch/arm/mach-davinci/clock.c | 8 +- arch/arm/mach-davinci/clock.h | 1 + arch/arm/mach-davinci/da850.c | 10 ++ arch/arm/mach-davinci/devices-da8xx.c | 126 ++++++++++++++++++++++++++++ arch/arm/mach-davinci/include/mach/da8xx.h | 2 + arch/arm/mach-davinci/include/mach/psc.h | 3 +- arch/arm/mach-davinci/psc.c | 14 +++- 8 files changed, 164 insertions(+), 7 deletions(-) From sshtylyov at mvista.com Fri Jul 8 05:25:23 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 08 Jul 2011 14:25:23 +0400 Subject: [PATCH 2/5] arm: davinci: Allow EVENTQ_0 as a default queue In-Reply-To: <1310074390-4277-3-git-send-email-ido@wizery.com> References: <1310074390-4277-1-git-send-email-ido@wizery.com> <1310074390-4277-3-git-send-email-ido@wizery.com> Message-ID: <4E16DB13.90806@mvista.com> Hello. On 08-07-2011 1:33, Ido Yariv wrote: > Davinci platforms may define a default queue for each channel > controller. If one is not defined, the default queue is set to EVENTQ_1. > However, there's no way to distinguish between an unset default queue to > one that is set to EVENTQ_0, as EVENTQ_0 = 0. > In order to keep existing behaviour on platforms which don't specify a > default_queue member, the default_queue member was modified to be a > pointer to enum dma_event_q. A NULL value means that this member was not > specified. Mmm, perhaps it's better to drop the default EVENTQ_1 concept and explicitly initilaize that field for every SoC. It's also possible to offset the event queue number by 1. I don't like the pointer solution. > Signed-off-by: Ido Yariv WBR, Sergei From sshtylyov at mvista.com Fri Jul 8 05:39:32 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 08 Jul 2011 14:39:32 +0400 Subject: [PATCH 5/5] arm: davinci: DA850: Add wl12xx expansion board support In-Reply-To: <1310074390-4277-6-git-send-email-ido@wizery.com> References: <1310074390-4277-1-git-send-email-ido@wizery.com> <1310074390-4277-6-git-send-email-ido@wizery.com> Message-ID: <4E16DE64.5090205@mvista.com> Hello. On 08-07-2011 1:33, Ido Yariv wrote: > The DA850 supports an optional wl12xx based expansion board, adding WLAN > & BT capabilities. The wl12xx is a 4-wire, 1.8V, embedded SDIO WLAN > device with an external IRQ line and is power-controlled by a GPIO-based > fixed regulator. > This patch adds support for the WLAN capabilities of this expansion > board. > Signed-off-by: Ido Yariv > --- > arch/arm/mach-davinci/Kconfig | 31 +++++++ > arch/arm/mach-davinci/board-da850-evm.c | 128 ++++++++++++++++++++++++++++++ > arch/arm/mach-davinci/da850.c | 9 ++ > arch/arm/mach-davinci/include/mach/mux.h | 10 +++ > 4 files changed, 178 insertions(+), 0 deletions(-) > diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig > index c0deaca..1a9149c 100644 > --- a/arch/arm/mach-davinci/Kconfig > +++ b/arch/arm/mach-davinci/Kconfig > @@ -192,6 +192,37 @@ config DA850_UI_RMII > > endchoice > > +config DA850_WL12XX > + bool "DA850 wl12xx expansion board" > + depends on MACH_DAVINCI_DA850_EVM > + ---help--- > + Say Y if you want to use a wl12xx expansion board connected to the > + DA850 EVM. If I don't mistake, this expansion board is rather used with AM180x EVM? > + > +choice > + prompt "FREF reference clock used by the wl12XX expansion board" > + default DA850_WL12XX_FREF_38_4 > + depends on DA850_WL12XX > + help > + There are currently two kinds of such daughter boards, one made by > + Mistral and another by LS Research. These (and perhaps others) use > + different FREF reference clocks. Select the correct one according to > + the daughter board's type. > + > +config DA850_WL12XX_FREF_19_2 > + bool "19.2MHz" > +config DA850_WL12XX_FREF_26 > + bool "26MHz" > +config DA850_WL12XX_FREF_38_4 > + bool "38.4MHz" > +config DA850_WL12XX_FREF_52 > + bool "52MHz" > +config DA850_WL12XX_FREF_XTAL_26 > + bool "XTAL 26MHz" > +config DA850_WL12XX_FREF_XTAL_38_4 > + bool "XTAL 38MHz" Could you add emoty lines between items? > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index a7b41bf..a567379 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c [...] > @@ -1117,6 +1122,127 @@ static __init int da850_evm_init_cpufreq(void) > static __init int da850_evm_init_cpufreq(void) { return 0; } > #endif > > +#ifdef CONFIG_DA850_WL12XX > + > +static int da850_wl12xx_fref = -1; > + > +static int __init setup_da850_wl12xx_fref(char *fref) > +{ > + if (!strcmp(fref, "19.2")) > + da850_wl12xx_fref = WL12XX_REFCLOCK_19; > + else if (!strcmp(fref, "26")) > + da850_wl12xx_fref = WL12XX_REFCLOCK_26; > + else if (!strcmp(fref, "38.4")) > + da850_wl12xx_fref = WL12XX_REFCLOCK_38; > + else if (!strcmp(fref, "52")) > + da850_wl12xx_fref = WL12XX_REFCLOCK_52; > + else if (!strcmp(fref, "XTAL26")) > + da850_wl12xx_fref = WL12XX_REFCLOCK_26_XTAL; > + else if (!strcmp(fref, "XTAL38.4")) > + da850_wl12xx_fref = WL12XX_REFCLOCK_38_XTAL; > + else > + pr_info("da850_wl12xx_fref is invalid. Valid options: " > + "19.2, 26, 38.4, 52, XTAL26 or XTAL38.4\n"); > + return 0; > +} > +__setup("da850_wl12xx_fref=", setup_da850_wl12xx_fref); Why then also have a Kconfig 'choice' for that? > +static void wl12xx_set_power(int slot, bool power_on) > +{ > + static bool power_state; > + > + pr_debug("Powering %s wl12xx", (power_on ? "on" : "off")); Parens not needed around ?:. > + if (power_on) { > + /* Power up sequence required for wl127x devices */ > + gpio_set_value(DA850_WLAN_EN, 1); > + mdelay(15); > + gpio_set_value(DA850_WLAN_EN, 0); > + mdelay(1); > + gpio_set_value(DA850_WLAN_EN, 1); > + mdelay(70); Perhaps msleep()? > +static void da850_wl12xx_init(void) > +{ > + int ret; > + > + ret = davinci_cfg_reg_list(da850_evm_mmc_wl12xx_pins); > + if (ret) > + pr_warning("da850_evm_init: wl12xx/mmc mux setup failed:" > + " %d\n", ret); > + > + ret = da850_register_mmcsd1(&da850_mmc_wl12xx_config); > + if (ret) > + pr_warning("da850_evm_init: wl12xx/mmc registration failed:" > + " %d\n", ret); If these fail, does it makse sense to continue? I doubt it... > + if (gpio_request(DA850_WLAN_EN, "wl12xx_en") || > + gpio_direction_output(DA850_WLAN_EN, 0)) Use gpio_request_one() instead of this pair. > + pr_err("Error initializing the wl12xx enable gpio\n"); > + > + if (gpio_request(DA850_WLAN_IRQ, "wl12xx_irq") || > + gpio_direction_input(DA850_WLAN_IRQ)) Same here. > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 133aac4..bfe9b71 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -525,6 +525,13 @@ static const struct mux_config da850_pins[] = { > MUX_CFG(DA850, MMCSD0_DAT_3, 10, 20, 15, 2, false) > MUX_CFG(DA850, MMCSD0_CLK, 10, 0, 15, 2, false) > MUX_CFG(DA850, MMCSD0_CMD, 10, 4, 15, 2, false) > + /* MMC/SD1 function */ > + MUX_CFG(DA850, MMCSD1_DAT_0, 18, 8, 15, 2, false) > + MUX_CFG(DA850, MMCSD1_DAT_1, 19, 16, 15, 2, false) > + MUX_CFG(DA850, MMCSD1_DAT_2, 19, 12, 15, 2, false) > + MUX_CFG(DA850, MMCSD1_DAT_3, 19, 8, 15, 2, false) > + MUX_CFG(DA850, MMCSD1_CLK, 18, 12, 15, 2, false) > + MUX_CFG(DA850, MMCSD1_CMD, 18, 16, 15, 2, false) > /* EMIF2.5/EMIFA function */ > MUX_CFG(DA850, EMA_D_7, 9, 0, 15, 1, false) > MUX_CFG(DA850, EMA_D_6, 9, 4, 15, 1, false) > @@ -583,6 +590,8 @@ static const struct mux_config da850_pins[] = { > MUX_CFG(DA850, GPIO3_13, 7, 8, 15, 8, false) > MUX_CFG(DA850, GPIO4_0, 10, 28, 15, 8, false) > MUX_CFG(DA850, GPIO4_1, 10, 24, 15, 8, false) > + MUX_CFG(DA850, GPIO6_9, 13, 24, 15, 8, false) > + MUX_CFG(DA850, GPIO6_10, 13, 20, 15, 8, false) > MUX_CFG(DA850, GPIO6_13, 13, 8, 15, 8, false) > MUX_CFG(DA850, RTC_ALARM, 0, 28, 15, 2, false) > #endif > diff --git a/arch/arm/mach-davinci/include/mach/mux.h b/arch/arm/mach-davinci/include/mach/mux.h > index 5d4e0fe..a7e92fc 100644 > --- a/arch/arm/mach-davinci/include/mach/mux.h > +++ b/arch/arm/mach-davinci/include/mach/mux.h > @@ -857,6 +857,14 @@ enum davinci_da850_index { > DA850_MMCSD0_CLK, > DA850_MMCSD0_CMD, > > + /* MMC/SD1 function */ > + DA850_MMCSD1_DAT_0, > + DA850_MMCSD1_DAT_1, > + DA850_MMCSD1_DAT_2, > + DA850_MMCSD1_DAT_3, > + DA850_MMCSD1_CLK, > + DA850_MMCSD1_CMD, > + > /* EMIF2.5/EMIFA function */ > DA850_EMA_D_7, > DA850_EMA_D_6, > @@ -916,6 +924,8 @@ enum davinci_da850_index { > DA850_GPIO3_13, > DA850_GPIO4_0, > DA850_GPIO4_1, > + DA850_GPIO6_9, > + DA850_GPIO6_10, > DA850_GPIO6_13, > DA850_RTC_ALARM, > }; Please modify these 2 files a sperate patch. Maybe even 2 patches: one for MMC1 pins and one for GPIO pins... WBR, Sergei From sshtylyov at mvista.com Fri Jul 8 05:41:08 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 08 Jul 2011 14:41:08 +0400 Subject: [GIT PULL] DaVinci features for v3.1 In-Reply-To: References: Message-ID: <4E16DEC4.4070401@mvista.com> Hello. On 08-07-2011 13:13, Nori, Sekhar wrote: > Please pull the following DaVinci features for v3.1. > This pull request depends on the patch "davinci: dm6467/T EVM: fix setting > up of reference clock rate" in clean-up pull request for successful build. > Pulling this after the earlier clean-up pull request generates this merge > conflict: > <<<<<<< HEAD:arch/arm/mach-davinci/include/mach/psc.h > #define MDSTAT_STATE_MASK 0x1f > ======= > #define MDSTAT_STATE_MASK 0x1f > #define MDCTL_FORCE BIT(31) >>>>>>>> 8bb2c4813c534d26eba3beb7888fbd3cbc931f62:arch/arm/mach-davinci/include/mach/psc.h > The resolution is: > #define MDSTAT_STATE_MASK 0x1f > #define MDCTL_FORCE BIT(31) BTW, I think I'll send a patch fixing MDSTAT_STATE_MASK to 0x3f today. WBR, Sergei From arnd at arndb.de Fri Jul 8 06:35:45 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Fri, 8 Jul 2011 13:35:45 +0200 Subject: [GIT PULL] DaVinci cleanups for v3.1 In-Reply-To: References: Message-ID: <201107081335.45343.arnd@arndb.de> On Friday 08 July 2011, Nori, Sekhar wrote: > are available in the git repository at: > > git://gitorious.org/linux-davinci/linux-davinci.git cleanup Pulled, thanks! Arnd From ido at wizery.com Fri Jul 8 09:27:17 2011 From: ido at wizery.com (Ido Yariv) Date: Fri, 8 Jul 2011 17:27:17 +0300 Subject: [PATCH 2/5] arm: davinci: Allow EVENTQ_0 as a default queue In-Reply-To: <4E16DB13.90806@mvista.com> References: <1310074390-4277-1-git-send-email-ido@wizery.com> <1310074390-4277-3-git-send-email-ido@wizery.com> <4E16DB13.90806@mvista.com> Message-ID: <20110708142717.GG2176@WorkStation> Hi, On Fri, Jul 08, 2011 at 02:25:23PM +0400, Sergei Shtylyov wrote: > On 08-07-2011 1:33, Ido Yariv wrote: > > >Davinci platforms may define a default queue for each channel > >controller. If one is not defined, the default queue is set to EVENTQ_1. > >However, there's no way to distinguish between an unset default queue to > >one that is set to EVENTQ_0, as EVENTQ_0 = 0. > > >In order to keep existing behaviour on platforms which don't specify a > >default_queue member, the default_queue member was modified to be a > >pointer to enum dma_event_q. A NULL value means that this member was not > >specified. > > Mmm, perhaps it's better to drop the default EVENTQ_1 concept and > explicitly initilaize that field for every SoC. It's also possible > to offset the event queue number by 1. I don't like the pointer > solution. Dropping the default EVENTQ_1 is probably the best solution, but doing so has the potential of breaking some Davinci platforms (not currently in mainline) which don't specify this member. The second option was also considered - modifying the dma_event_q enum, incrementing all EVENTQ_X constants, and decrementing the value back in map_dmach_queue. This approach has two minor drawbacks: * There are other users of this enum, besides the default queue. However, AFAIK, there's only one function (map_dmach_queue) that actually cares about the numerical values. * If someone currently defines a queue numerically, this code could break. Obviously, defining a queue numerically is not something that should be done. If there aren't any objections, we could go for the first option. Thanks for your review, Ido. From ido at wizery.com Fri Jul 8 09:27:50 2011 From: ido at wizery.com (Ido Yariv) Date: Fri, 8 Jul 2011 17:27:50 +0300 Subject: [PATCH 5/5] arm: davinci: DA850: Add wl12xx expansion board support In-Reply-To: <4E16DE64.5090205@mvista.com> References: <1310074390-4277-1-git-send-email-ido@wizery.com> <1310074390-4277-6-git-send-email-ido@wizery.com> <4E16DE64.5090205@mvista.com> Message-ID: <20110708142750.GH2176@WorkStation> Hi, On Fri, Jul 08, 2011 at 02:39:32PM +0400, Sergei Shtylyov wrote: > Hello. > > On 08-07-2011 1:33, Ido Yariv wrote: > > >The DA850 supports an optional wl12xx based expansion board, adding WLAN > >& BT capabilities. The wl12xx is a 4-wire, 1.8V, embedded SDIO WLAN > >device with an external IRQ line and is power-controlled by a GPIO-based > >fixed regulator. > > >This patch adds support for the WLAN capabilities of this expansion > >board. > > >Signed-off-by: Ido Yariv > >--- > > arch/arm/mach-davinci/Kconfig | 31 +++++++ > > arch/arm/mach-davinci/board-da850-evm.c | 128 ++++++++++++++++++++++++++++++ > > arch/arm/mach-davinci/da850.c | 9 ++ > > arch/arm/mach-davinci/include/mach/mux.h | 10 +++ > > 4 files changed, 178 insertions(+), 0 deletions(-) > > >diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig > >index c0deaca..1a9149c 100644 > >--- a/arch/arm/mach-davinci/Kconfig > >+++ b/arch/arm/mach-davinci/Kconfig > >@@ -192,6 +192,37 @@ config DA850_UI_RMII > > > > endchoice > > > >+config DA850_WL12XX > >+ bool "DA850 wl12xx expansion board" > >+ depends on MACH_DAVINCI_DA850_EVM > >+ ---help--- > >+ Say Y if you want to use a wl12xx expansion board connected to the > >+ DA850 EVM. > > If I don't mistake, this expansion board is rather used with AM180x EVM? I'm actually not sure about the compatibility between the two, so yes, it might be a good idea to rename the configuration option and description. [...] > >+config DA850_WL12XX_FREF_19_2 > >+ bool "19.2MHz" > >+config DA850_WL12XX_FREF_26 > >+ bool "26MHz" > >+config DA850_WL12XX_FREF_38_4 > >+ bool "38.4MHz" > >+config DA850_WL12XX_FREF_52 > >+ bool "52MHz" > >+config DA850_WL12XX_FREF_XTAL_26 > >+ bool "XTAL 26MHz" > >+config DA850_WL12XX_FREF_XTAL_38_4 > >+ bool "XTAL 38MHz" > > Could you add emoty lines between items? Sure. [...] > >+static int da850_wl12xx_fref = -1; > >+ > >+static int __init setup_da850_wl12xx_fref(char *fref) > >+{ > >+ if (!strcmp(fref, "19.2")) > >+ da850_wl12xx_fref = WL12XX_REFCLOCK_19; > >+ else if (!strcmp(fref, "26")) > >+ da850_wl12xx_fref = WL12XX_REFCLOCK_26; > >+ else if (!strcmp(fref, "38.4")) > >+ da850_wl12xx_fref = WL12XX_REFCLOCK_38; > >+ else if (!strcmp(fref, "52")) > >+ da850_wl12xx_fref = WL12XX_REFCLOCK_52; > >+ else if (!strcmp(fref, "XTAL26")) > >+ da850_wl12xx_fref = WL12XX_REFCLOCK_26_XTAL; > >+ else if (!strcmp(fref, "XTAL38.4")) > >+ da850_wl12xx_fref = WL12XX_REFCLOCK_38_XTAL; > >+ else > >+ pr_info("da850_wl12xx_fref is invalid. Valid options: " > >+ "19.2, 26, 38.4, 52, XTAL26 or XTAL38.4\n"); > >+ return 0; > >+} > >+__setup("da850_wl12xx_fref=", setup_da850_wl12xx_fref); > > Why then also have a Kconfig 'choice' for that? We could choose a default value arbitrarily, but AFAIK there isn't a good one. The two currently available expansion boards use different reference clocks, so one of them will not work out of the box. Having it configurable by a boot argument can be handy when switching between expansion boards during development. Naturally, it's not a must. > >+static void wl12xx_set_power(int slot, bool power_on) > >+{ > >+ static bool power_state; > >+ > >+ pr_debug("Powering %s wl12xx", (power_on ? "on" : "off")); > > Parens not needed around ?:. Sure, will be fixed. > > >+ if (power_on) { > >+ /* Power up sequence required for wl127x devices */ > >+ gpio_set_value(DA850_WLAN_EN, 1); > >+ mdelay(15); > >+ gpio_set_value(DA850_WLAN_EN, 0); > >+ mdelay(1); > >+ gpio_set_value(DA850_WLAN_EN, 1); > >+ mdelay(70); > > Perhaps msleep()? Sure, will be fixed. > >+static void da850_wl12xx_init(void) > >+{ > >+ int ret; > >+ > >+ ret = davinci_cfg_reg_list(da850_evm_mmc_wl12xx_pins); > >+ if (ret) > >+ pr_warning("da850_evm_init: wl12xx/mmc mux setup failed:" > >+ " %d\n", ret); > >+ > >+ ret = da850_register_mmcsd1(&da850_mmc_wl12xx_config); > >+ if (ret) > >+ pr_warning("da850_evm_init: wl12xx/mmc registration failed:" > >+ " %d\n", ret); > > If these fail, does it makse sense to continue? I doubt it... Right, will be fixed. > > >+ if (gpio_request(DA850_WLAN_EN, "wl12xx_en") || > >+ gpio_direction_output(DA850_WLAN_EN, 0)) > > Use gpio_request_one() instead of this pair. > > >+ pr_err("Error initializing the wl12xx enable gpio\n"); > >+ > >+ if (gpio_request(DA850_WLAN_IRQ, "wl12xx_irq") || > >+ gpio_direction_input(DA850_WLAN_IRQ)) > > Same here. Sure, will be fixed. > >diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > >index 133aac4..bfe9b71 100644 > >--- a/arch/arm/mach-davinci/da850.c > >+++ b/arch/arm/mach-davinci/da850.c > >@@ -525,6 +525,13 @@ static const struct mux_config da850_pins[] = { > > MUX_CFG(DA850, MMCSD0_DAT_3, 10, 20, 15, 2, false) > > MUX_CFG(DA850, MMCSD0_CLK, 10, 0, 15, 2, false) > > MUX_CFG(DA850, MMCSD0_CMD, 10, 4, 15, 2, false) > >+ /* MMC/SD1 function */ > >+ MUX_CFG(DA850, MMCSD1_DAT_0, 18, 8, 15, 2, false) > >+ MUX_CFG(DA850, MMCSD1_DAT_1, 19, 16, 15, 2, false) > >+ MUX_CFG(DA850, MMCSD1_DAT_2, 19, 12, 15, 2, false) > >+ MUX_CFG(DA850, MMCSD1_DAT_3, 19, 8, 15, 2, false) > >+ MUX_CFG(DA850, MMCSD1_CLK, 18, 12, 15, 2, false) > >+ MUX_CFG(DA850, MMCSD1_CMD, 18, 16, 15, 2, false) > > /* EMIF2.5/EMIFA function */ > > MUX_CFG(DA850, EMA_D_7, 9, 0, 15, 1, false) > > MUX_CFG(DA850, EMA_D_6, 9, 4, 15, 1, false) > >@@ -583,6 +590,8 @@ static const struct mux_config da850_pins[] = { > > MUX_CFG(DA850, GPIO3_13, 7, 8, 15, 8, false) > > MUX_CFG(DA850, GPIO4_0, 10, 28, 15, 8, false) > > MUX_CFG(DA850, GPIO4_1, 10, 24, 15, 8, false) > >+ MUX_CFG(DA850, GPIO6_9, 13, 24, 15, 8, false) > >+ MUX_CFG(DA850, GPIO6_10, 13, 20, 15, 8, false) > > MUX_CFG(DA850, GPIO6_13, 13, 8, 15, 8, false) > > MUX_CFG(DA850, RTC_ALARM, 0, 28, 15, 2, false) > > #endif > > >diff --git a/arch/arm/mach-davinci/include/mach/mux.h b/arch/arm/mach-davinci/include/mach/mux.h > >index 5d4e0fe..a7e92fc 100644 > >--- a/arch/arm/mach-davinci/include/mach/mux.h > >+++ b/arch/arm/mach-davinci/include/mach/mux.h > >@@ -857,6 +857,14 @@ enum davinci_da850_index { > > DA850_MMCSD0_CLK, > > DA850_MMCSD0_CMD, > > > >+ /* MMC/SD1 function */ > >+ DA850_MMCSD1_DAT_0, > >+ DA850_MMCSD1_DAT_1, > >+ DA850_MMCSD1_DAT_2, > >+ DA850_MMCSD1_DAT_3, > >+ DA850_MMCSD1_CLK, > >+ DA850_MMCSD1_CMD, > >+ > > /* EMIF2.5/EMIFA function */ > > DA850_EMA_D_7, > > DA850_EMA_D_6, > >@@ -916,6 +924,8 @@ enum davinci_da850_index { > > DA850_GPIO3_13, > > DA850_GPIO4_0, > > DA850_GPIO4_1, > >+ DA850_GPIO6_9, > >+ DA850_GPIO6_10, > > DA850_GPIO6_13, > > DA850_RTC_ALARM, > > }; > > Please modify these 2 files a sperate patch. Maybe even 2 > patches: one for MMC1 pins and one for GPIO pins... Sure, will be fixed. Thanks for your review, Ido. From subhasish at mistralsolutions.com Fri Jul 8 09:53:29 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 14:53:29 -0000 Subject: [PATCH v1 1/9] mfd: pruss mfd driver. In-Reply-To: <1296571667-12049-1-git-send-email-subhasish@mistralsolutions.com> References: <1296571667-12049-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1296571667-12049-2-git-send-email-subhasish@mistralsolutions.com> This patch adds the pruss MFD driver and associated include files. Signed-off-by: Subhasish Ghosh --- drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/da8xx_pru.c | 403 +++++++++++++++++++++++++++++++ include/linux/mfd/pruss/da8xx_pru.h | 102 ++++++++ include/linux/mfd/pruss/da8xx_prucore.h | 74 ++++++ 5 files changed, 590 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/da8xx_pru.c create mode 100644 include/linux/mfd/pruss/da8xx_pru.h create mode 100644 include/linux/mfd/pruss/da8xx_prucore.h 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..4657054 --- /dev/null +++ b/drivers/mfd/da8xx_pru.c @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2010 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct da8xx_pruss { + struct device *dev; + 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); + +u32 pruss_disable(struct device *dev, u8 pruss_num) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + da8xx_prusscore_regs h_pruss; + u32 temp_reg; + + if (pruss_num == DA8XX_PRUCORE_0) { + /* Disable PRU0 */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7000); + + 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); + + 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 */ + h_pruss->CONTROL = DA8XX_PRUCORE_CONTROL_RESETVAL; + + } else if (pruss_num == DA8XX_PRUCORE_1) { + /* Disable PRU1 */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7800); + 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); + + 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 */ + h_pruss->CONTROL = DA8XX_PRUCORE_CONTROL_RESETVAL; + } else + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(pruss_disable); + +u32 pruss_enable(struct device *dev, u8 pruss_num) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + da8xx_prusscore_regs h_pruss; + + if (pruss_num == DA8XX_PRUCORE_0) { + /* Reset PRU0 */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7000); + h_pruss->CONTROL = DA8XX_PRUCORE_CONTROL_RESETVAL; + } else if (pruss_num == DA8XX_PRUCORE_1) { + /* Reset PRU1 */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7800); + h_pruss->CONTROL = DA8XX_PRUCORE_CONTROL_RESETVAL; + } else + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(pruss_enable); + +/* Load the specified PRU with code */ +u32 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); + u32 *pruss_iram; + u32 i; + + if (pruss_num == DA8XX_PRUCORE_0) { + pruss_iram = (u32 *) ((u32) pruss->ioaddr + 0x8000); + } else if (pruss_num == DA8XX_PRUCORE_1) { + pruss_iram = (u32 *) ((u32) pruss->ioaddr + 0xc000); + } else + return -EINVAL; + + pruss_enable(dev, pruss_num); + + /* Copy dMAX code to its instruction RAM */ + for (i = 0; i < code_size_in_words; i++) { + pruss_iram[i] = pruss_code[i]; + } + return 0; +} +EXPORT_SYMBOL(pruss_load); + +u32 pruss_run(struct device *dev, u8 pruss_num) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + da8xx_prusscore_regs h_pruss; + + u32 temp_reg; + + if (pruss_num == DA8XX_PRUCORE_0) { + /* DA8XX_PRUCORE_0_REGS; */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7000); + } else if (pruss_num == DA8XX_PRUCORE_1) { + /* DA8XX_PRUCORE_1_REGS; */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7800); + } else + return -EINVAL; + + /* Enable dMAX, let it execute the code we just copied */ + 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); + return 0; +} +EXPORT_SYMBOL(pruss_run); + +u32 pruss_wait_for_halt(struct device *dev, u8 pruss_num, u32 timeout) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + da8xx_prusscore_regs h_pruss; + u32 temp_reg; + u32 cnt = timeout; + + if (pruss_num == DA8XX_PRUCORE_0) { + /* DA8XX_PRUCORE_0_REGS; */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7000); + } else if (pruss_num == DA8XX_PRUCORE_1) { + /* DA8XX_PRUCORE_1_REGS; */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7800); + } 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); + +s16 pruss_writeb(struct device *dev, u32 u32offset, + u8 *pu8datatowrite, u16 u16bytestowrite) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + u8 *pu8addresstowrite; + u16 u16loop; + u32offset = (u32)pruss->ioaddr + u32offset; + pu8addresstowrite = (u8 *) (u32offset); + + for (u16loop = 0; u16loop < u16bytestowrite; u16loop++) + __raw_writeb(*pu8datatowrite++, pu8addresstowrite++); + return 0; +} +EXPORT_SYMBOL(pruss_writeb); + +s16 pruss_readb(struct device *dev, u32 u32offset, + u8 *pu8datatoread, u16 u16bytestoread) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + u8 *pu8addresstoread; + u16 u16loop; + u32offset = (u32)pruss->ioaddr + u32offset; + pu8addresstoread = (u8 *) (u32offset); + + for (u16loop = 0; u16loop < u16bytestoread; u16loop++) + *pu8datatoread++ = __raw_readb(pu8addresstoread++); + return 0; +} +EXPORT_SYMBOL(pruss_readb); + +s16 pruss_writel(struct device *dev, u32 u32offset, + u32 *pu32datatowrite, s16 u16wordstowrite) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + u32 *pu32addresstowrite; + s16 u16loop; + + /* TODO: Get all the driver API's fixed */ + u32offset = (u32)pruss->ioaddr + u32offset; + pu32addresstowrite = (u32 *)(u32offset); + + for (u16loop = 0; u16loop < u16wordstowrite; u16loop++) + __raw_writel(*pu32datatowrite++, pu32addresstowrite++); + return 0; +} +EXPORT_SYMBOL(pruss_writel); + +s16 pruss_readl(struct device *dev, u32 u32offset, + u32 *pu32datatoread, s16 u16wordstoread) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + u32 *pu32addresstoread; + s16 u16loop; + + /* TODO: Get all the driver API's fixed */ + u32offset = (u32)pruss->ioaddr + u32offset; + pu32addresstoread = (u32 *)(u32offset); + + for (u16loop = 0; u16loop < u16wordstoread; u16loop++) + *pu32datatoread++ = __raw_readl(pu32addresstoread++); + return 0; +} +EXPORT_SYMBOL(pruss_readl); + +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; + + err = mfd_add_devices(dev, 0, &cell, 1, NULL, 0); + if (err) { + dev_err(dev, "cannot add mfd cells\n"); + return err; + } + } + 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; + } + + 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); + 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 = "da8xx_pruss", + .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/pruss/da8xx_pru.h b/include/linux/mfd/pruss/da8xx_pru.h new file mode 100644 index 0000000..318dad8 --- /dev/null +++ b/include/linux/mfd/pruss/da8xx_pru.h @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 +#include +#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 + +struct da8xx_pruss_devices { + const char *dev_name; + void *pdata; + size_t pdata_size; + int (*setup)(void); +}; + +u32 pruss_get_clk_freq(struct device *dev); + +u32 pruss_enable(struct device *dev, u8 pruss_num); + +u32 pruss_load(struct device *dev, u8 pruss_num, u32 *pruss_code, + u32 code_size_in_words); + +u32 pruss_run(struct device *dev, u8 pruss_num); + +u32 pruss_wait_for_halt(struct device *dev, u8 pruss_num, u32 timeout); + +u32 pruss_disable(struct device *dev, u8 pruss_num); + +s16 pruss_writeb(struct device *dev, u32 u32offset, + u8 *pu8datatowrite, u16 u16wordstowrite); + +s16 pruss_readb(struct device *dev, u32 u32offset, + u8 *pu8datatoread, u16 u16wordstoread); + +s16 pruss_readl(struct device *dev, u32 u32offset, + u32 *pu32datatoread, s16 s16wordstoread); + +s16 pruss_writel(struct device *dev, u32 u32offset, + u32 *pu32datatoread, s16 s16wordstoread); + +#endif /* End _PRUSS_H_ */ diff --git a/include/linux/mfd/pruss/da8xx_prucore.h b/include/linux/mfd/pruss/da8xx_prucore.h new file mode 100644 index 0000000..56b0eac --- /dev/null +++ b/include/linux/mfd/pruss/da8xx_prucore.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 + +#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) + +typedef struct { + 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]; +} *da8xx_prusscore_regs; + +#endif -- 1.7.2.3 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From manjunath.hadli at ti.com Fri Jul 8 09:55:51 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 08 Jul 2011 14:55:51 -0000 Subject: [PATCH v15 04/13] davinci vpbe: VENC( Video Encoder) implementation Message-ID: <1300197465-4178-1-git-send-email-manjunath.hadli@ti.com> This patch adds the VENC or the Video encoder, which is responsible for the blending of all source planes and timing generation for Video modes like NTSC, PAL and other digital outputs. the VENC implementation currently supports COMPOSITE and COMPONENT outputs and NTSC and PAL resolutions through the analog DACs. The venc block is implemented as a subdevice, allowing for additional external and internal encoders of other kind to plug-in. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/vpbe_venc.c | 556 ++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_venc_regs.h | 177 ++++++++ include/media/davinci/vpbe_venc.h | 41 ++ 3 files changed, 774 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpbe_venc.c create mode 100644 drivers/media/video/davinci/vpbe_venc_regs.h create mode 100644 include/media/davinci/vpbe_venc.h diff --git a/drivers/media/video/davinci/vpbe_venc.c b/drivers/media/video/davinci/vpbe_venc.c new file mode 100644 index 0000000..1131e2d --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc.c @@ -0,0 +1,556 @@ +/* + * Copyright (C) 2010 Texas Instruments Inc + * + * 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 in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "vpbe_venc_regs.h" + +#define MODULE_NAME VPBE_VENC_SUBDEV_NAME + +static int debug = 2; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level 0-2"); + +struct venc_state { + struct v4l2_subdev sd; + struct venc_callback *callback; + struct venc_platform_data *pdata; + struct device *pdev; + u32 output; + v4l2_std_id std; + spinlock_t lock; + void __iomem *venc_base; + void __iomem *vdaccfg_reg; +}; + +static inline struct venc_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct venc_state, sd); +} + +static inline u32 venc_read(struct v4l2_subdev *sd, u32 offset) +{ + struct venc_state *venc = to_state(sd); + + return readl(venc->venc_base + offset); +} + +static inline u32 venc_write(struct v4l2_subdev *sd, u32 offset, u32 val) +{ + struct venc_state *venc = to_state(sd); + writel(val, (venc->venc_base + offset)); + return val; +} + +static inline u32 venc_modify(struct v4l2_subdev *sd, u32 offset, + u32 val, u32 mask) +{ + u32 new_val = (venc_read(sd, offset) & ~mask) | (val & mask); + + venc_write(sd, offset, new_val); + return new_val; +} + +static inline u32 vdaccfg_write(struct v4l2_subdev *sd, u32 val) +{ + struct venc_state *venc = to_state(sd); + + writel(val, venc->vdaccfg_reg); + + val = readl(venc->vdaccfg_reg); + return val; +} + +/* This function sets the dac of the VPBE for various outputs + */ +static int venc_set_dac(struct v4l2_subdev *sd, u32 out_index) +{ + int ret = 0; + + switch (out_index) { + case 0: + v4l2_dbg(debug, 1, sd, "Setting output to Composite\n"); + venc_write(sd, VENC_DACSEL, 0); + break; + case 1: + v4l2_dbg(debug, 1, sd, "Setting output to S-Video\n"); + venc_write(sd, VENC_DACSEL, 0x210); + break; + case 2: + venc_write(sd, VENC_DACSEL, 0x543); + break; + default: + ret = -EINVAL; + } + return ret; +} + +static void venc_enabledigitaloutput(struct v4l2_subdev *sd, int benable) +{ + v4l2_dbg(debug, 2, sd, "venc_enabledigitaloutput\n"); + + if (benable) { + venc_write(sd, VENC_VMOD, 0); + venc_write(sd, VENC_CVBS, 0); + venc_write(sd, VENC_LCDOUT, 0); + venc_write(sd, VENC_HSPLS, 0); + venc_write(sd, VENC_HSTART, 0); + venc_write(sd, VENC_HVALID, 0); + venc_write(sd, VENC_HINT, 0); + venc_write(sd, VENC_VSPLS, 0); + venc_write(sd, VENC_VSTART, 0); + venc_write(sd, VENC_VVALID, 0); + venc_write(sd, VENC_VINT, 0); + venc_write(sd, VENC_YCCCTL, 0); + venc_write(sd, VENC_DACSEL, 0); + + } else { + venc_write(sd, VENC_VMOD, 0); + /* disable VCLK output pin enable */ + venc_write(sd, VENC_VIDCTL, 0x141); + + /* Disable output sync pins */ + venc_write(sd, VENC_SYNCCTL, 0); + + /* Disable DCLOCK */ + venc_write(sd, VENC_DCLKCTL, 0); + venc_write(sd, VENC_DRGBX1, 0x0000057C); + + /* Disable LCD output control (accepting default polarity) */ + venc_write(sd, VENC_LCDOUT, 0); + venc_write(sd, VENC_CMPNT, 0x100); + venc_write(sd, VENC_HSPLS, 0); + venc_write(sd, VENC_HINT, 0); + venc_write(sd, VENC_HSTART, 0); + venc_write(sd, VENC_HVALID, 0); + + venc_write(sd, VENC_VSPLS, 0); + venc_write(sd, VENC_VINT, 0); + venc_write(sd, VENC_VSTART, 0); + venc_write(sd, VENC_VVALID, 0); + + venc_write(sd, VENC_HSDLY, 0); + venc_write(sd, VENC_VSDLY, 0); + + venc_write(sd, VENC_YCCCTL, 0); + venc_write(sd, VENC_VSTARTA, 0); + + /* Set OSD clock and OSD Sync Adavance registers */ + venc_write(sd, VENC_OSDCLK0, 1); + venc_write(sd, VENC_OSDCLK1, 2); + } +} + +/* + * setting NTSC mode + */ +static int venc_set_ntsc(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + struct venc_platform_data *pdata = venc->pdata; + + v4l2_dbg(debug, 2, sd, "venc_set_ntsc\n"); + + /* Setup clock at VPSS & VENC for SD */ + vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1); + if (pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_525_60) < 0) + return -EINVAL; + + venc_enabledigitaloutput(sd, 0); + + /* to set VENC CLK DIV to 1 - final clock is 54 MHz */ + venc_modify(sd, VENC_VIDCTL, 0, 1 << 1); + /* Set REC656 Mode */ + venc_write(sd, VENC_YCCCTL, 0x1); + venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAFRQ); + venc_modify(sd, VENC_VDPRO, 0, VENC_VDPRO_DAUPS); + + venc_write(sd, VENC_VMOD, 0); + venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), + VENC_VMOD_VIE); + venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_VMD), VENC_VMOD_VMD); + venc_modify(sd, VENC_VMOD, (0 << VENC_VMOD_TVTYP_SHIFT), + VENC_VMOD_TVTYP); + venc_write(sd, VENC_DACTST, 0x0); + venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); + return 0; +} + +/* + * setting PAL mode + */ +static int venc_set_pal(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + + v4l2_dbg(debug, 2, sd, "venc_set_pal\n"); + + /* Setup clock at VPSS & VENC for SD */ + vpss_enable_clock(VPSS_VENC_CLOCK_SEL, 1); + if (venc->pdata->setup_clock(VPBE_ENC_STD, V4L2_STD_625_50) < 0) + return -EINVAL; + + venc_enabledigitaloutput(sd, 0); + + /* to set VENC CLK DIV to 1 - final clock is 54 MHz */ + venc_modify(sd, VENC_VIDCTL, 0, 1 << 1); + /* Set REC656 Mode */ + venc_write(sd, VENC_YCCCTL, 0x1); + + venc_modify(sd, VENC_SYNCCTL, 1 << VENC_SYNCCTL_OVD_SHIFT, + VENC_SYNCCTL_OVD); + venc_write(sd, VENC_VMOD, 0); + venc_modify(sd, VENC_VMOD, + (1 << VENC_VMOD_VIE_SHIFT), + VENC_VMOD_VIE); + venc_modify(sd, VENC_VMOD, + (0 << VENC_VMOD_VMD), VENC_VMOD_VMD); + venc_modify(sd, VENC_VMOD, + (1 << VENC_VMOD_TVTYP_SHIFT), + VENC_VMOD_TVTYP); + venc_write(sd, VENC_DACTST, 0x0); + venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); + return 0; +} + +/* + * venc_set_480p59_94 + * + * This function configures the video encoder to EDTV(525p) component setting. + */ +static int venc_set_480p59_94(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + struct venc_platform_data *pdata = venc->pdata; + + v4l2_dbg(debug, 2, sd, "venc_set_480p59_94\n"); + + /* Setup clock at VPSS & VENC for SD */ + if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_480P59_94) < 0) + return -EINVAL; + + venc_enabledigitaloutput(sd, 0); + + venc_write(sd, VENC_OSDCLK0, 0); + venc_write(sd, VENC_OSDCLK1, 1); + venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, + VENC_VDPRO_DAFRQ); + venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, + VENC_VDPRO_DAUPS); + venc_write(sd, VENC_VMOD, 0); + venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), + VENC_VMOD_VIE); + venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD); + venc_modify(sd, VENC_VMOD, (HDTV_525P << VENC_VMOD_TVTYP_SHIFT), + VENC_VMOD_TVTYP); + venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 << + VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD); + + venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); + return 0; +} + +/* + * venc_set_625p + * + * This function configures the video encoder to HDTV(625p) component setting + */ +static int venc_set_576p50(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + struct venc_platform_data *pdata = venc->pdata; + + v4l2_dbg(debug, 2, sd, "venc_set_576p50\n"); + + /* Setup clock at VPSS & VENC for SD */ + if (pdata->setup_clock(VPBE_ENC_DV_PRESET, V4L2_DV_576P50) < 0) + return -EINVAL; + + venc_enabledigitaloutput(sd, 0); + + venc_write(sd, VENC_OSDCLK0, 0); + venc_write(sd, VENC_OSDCLK1, 1); + + venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAFRQ, + VENC_VDPRO_DAFRQ); + venc_modify(sd, VENC_VDPRO, VENC_VDPRO_DAUPS, + VENC_VDPRO_DAUPS); + + venc_write(sd, VENC_VMOD, 0); + venc_modify(sd, VENC_VMOD, (1 << VENC_VMOD_VIE_SHIFT), + VENC_VMOD_VIE); + venc_modify(sd, VENC_VMOD, VENC_VMOD_HDMD, VENC_VMOD_HDMD); + venc_modify(sd, VENC_VMOD, (HDTV_625P << VENC_VMOD_TVTYP_SHIFT), + VENC_VMOD_TVTYP); + + venc_modify(sd, VENC_VMOD, VENC_VMOD_VDMD_YCBCR8 << + VENC_VMOD_VDMD_SHIFT, VENC_VMOD_VDMD); + venc_modify(sd, VENC_VMOD, VENC_VMOD_VENC, VENC_VMOD_VENC); + return 0; +} + +static int venc_s_std_output(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + v4l2_dbg(debug, 1, sd, "venc_s_std_output\n"); + + if (norm & V4L2_STD_525_60) + return venc_set_ntsc(sd); + else if (norm & V4L2_STD_625_50) + return venc_set_pal(sd); + return -EINVAL; +} + +static int venc_s_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *dv_preset) +{ + v4l2_dbg(debug, 1, sd, "venc_s_dv_preset\n"); + + if (dv_preset->preset == V4L2_DV_576P50) + return venc_set_576p50(sd); + else if (dv_preset->preset == V4L2_DV_480P59_94) + return venc_set_480p59_94(sd); + return -EINVAL; +} + +static int venc_s_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct venc_state *venc = to_state(sd); + int max_output, lcd_out_index, ret = 0; + + v4l2_dbg(debug, 1, sd, "venc_s_routing\n"); + + max_output = 2 + venc->pdata->num_lcd_outputs; + lcd_out_index = 3; + + if (output >= max_output) + return -EINVAL; + + if (output < lcd_out_index) + ret = venc_set_dac(sd, output); + if (!ret) + venc->output = output; + return ret; +} + +static long venc_ioctl(struct v4l2_subdev *sd, + unsigned int cmd, + void *arg) +{ + u32 val; + switch (cmd) { + case VENC_GET_FLD: + val = venc_read(sd, VENC_VSTAT); + *((int *)arg) = ((val & VENC_VSTAT_FIDST) == + VENC_VSTAT_FIDST); + break; + default: + v4l2_err(sd, "Wrong IOCTL cmd\n"); + break; + } + return 0; +} + +static const struct v4l2_subdev_core_ops venc_core_ops = { + .ioctl = venc_ioctl, +}; + +static const struct v4l2_subdev_video_ops venc_video_ops = { + .s_routing = venc_s_routing, + .s_std_output = venc_s_std_output, + .s_dv_preset = venc_s_dv_preset, +}; + +static const struct v4l2_subdev_ops venc_ops = { + .core = &venc_core_ops, + .video = &venc_video_ops, +}; + +static int venc_initialize(struct v4l2_subdev *sd) +{ + struct venc_state *venc = to_state(sd); + int ret = 0; + + /* Set default to output to composite and std to NTSC */ + venc->output = 0; + venc->std = V4L2_STD_525_60; + + ret = venc_s_routing(sd, 0, venc->output, 0); + if (ret < 0) { + v4l2_err(sd, "Error setting output during init\n"); + return -EINVAL; + } + + ret = venc_s_std_output(sd, venc->std); + if (ret < 0) { + v4l2_err(sd, "Error setting std during init\n"); + return -EINVAL; + } + return ret; +} + +static int venc_device_get(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct venc_state **venc = data; + + if (strcmp(MODULE_NAME, pdev->name) == 0) + *venc = platform_get_drvdata(pdev); + return 0; +} + +struct v4l2_subdev *venc_sub_dev_init(struct v4l2_device *v4l2_dev, + const char *venc_name) +{ + struct venc_state *venc; + int err; + + err = bus_for_each_dev(&platform_bus_type, NULL, &venc, + venc_device_get); + if (venc == NULL) + return NULL; + + v4l2_subdev_init(&venc->sd, &venc_ops); + + strcpy(venc->sd.name, venc_name); + if (v4l2_device_register_subdev(v4l2_dev, &venc->sd) < 0) { + v4l2_err(v4l2_dev, + "vpbe unable to register venc sub device\n"); + return NULL; + } + if (venc_initialize(&venc->sd)) { + v4l2_err(v4l2_dev, + "vpbe venc initialization failed\n"); + return NULL; + } + return &venc->sd; +} +EXPORT_SYMBOL(venc_sub_dev_init); + +static int venc_probe(struct platform_device *pdev) +{ + struct venc_state *venc; + struct resource *res; + int ret; + + venc = kzalloc(sizeof(struct venc_state), GFP_KERNEL); + if (venc == NULL) + return -ENOMEM; + + venc->pdev = &pdev->dev; + venc->pdata = pdev->dev.platform_data; + if (NULL == venc->pdata) { + dev_err(venc->pdev, "Unable to get platform data for" + " VENC sub device"); + ret = -ENOENT; + goto free_mem; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(venc->pdev, + "Unable to get VENC register address map\n"); + ret = -ENODEV; + goto free_mem; + } + + if (!request_mem_region(res->start, resource_size(res), "venc")) { + dev_err(venc->pdev, "Unable to reserve VENC MMIO region\n"); + ret = -ENODEV; + goto free_mem; + } + + venc->venc_base = ioremap_nocache(res->start, resource_size(res)); + if (!venc->venc_base) { + dev_err(venc->pdev, "Unable to map VENC IO space\n"); + ret = -ENODEV; + goto release_venc_mem_region; + } + + spin_lock_init(&venc->lock); + platform_set_drvdata(pdev, venc); + dev_notice(venc->pdev, "VENC sub device probe success\n"); + return 0; + +release_venc_mem_region: + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); +free_mem: + kfree(venc); + return ret; +} + +static int venc_remove(struct platform_device *pdev) +{ + struct venc_state *venc = platform_get_drvdata(pdev); + struct resource *res; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap((void *)venc->venc_base); + release_mem_region(res->start, resource_size(res)); + kfree(venc); + return 0; +} + +static struct platform_driver venc_driver = { + .probe = venc_probe, + .remove = venc_remove, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, +}; + +static int venc_init(void) +{ + if (platform_driver_register(&venc_driver)) { + printk(KERN_ERR "Unable to register venc driver\n"); + return -ENODEV; + } + return 0; +} + +static void venc_exit(void) +{ + platform_driver_unregister(&venc_driver); + return; +} + +module_init(venc_init); +module_exit(venc_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("VPBE VENC Driver"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/drivers/media/video/davinci/vpbe_venc_regs.h b/drivers/media/video/davinci/vpbe_venc_regs.h new file mode 100644 index 0000000..947cb15 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_venc_regs.h @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2006-2010 Texas Instruments Inc + * + * 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 in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _VPBE_VENC_REGS_H +#define _VPBE_VENC_REGS_H + +/* VPBE Video Encoder / Digital LCD Subsystem Registers (VENC) */ +#define VENC_VMOD 0x00 +#define VENC_VIDCTL 0x04 +#define VENC_VDPRO 0x08 +#define VENC_SYNCCTL 0x0C +#define VENC_HSPLS 0x10 +#define VENC_VSPLS 0x14 +#define VENC_HINT 0x18 +#define VENC_HSTART 0x1C +#define VENC_HVALID 0x20 +#define VENC_VINT 0x24 +#define VENC_VSTART 0x28 +#define VENC_VVALID 0x2C +#define VENC_HSDLY 0x30 +#define VENC_VSDLY 0x34 +#define VENC_YCCCTL 0x38 +#define VENC_RGBCTL 0x3C +#define VENC_RGBCLP 0x40 +#define VENC_LINECTL 0x44 +#define VENC_CULLLINE 0x48 +#define VENC_LCDOUT 0x4C +#define VENC_BRTS 0x50 +#define VENC_BRTW 0x54 +#define VENC_ACCTL 0x58 +#define VENC_PWMP 0x5C +#define VENC_PWMW 0x60 +#define VENC_DCLKCTL 0x64 +#define VENC_DCLKPTN0 0x68 +#define VENC_DCLKPTN1 0x6C +#define VENC_DCLKPTN2 0x70 +#define VENC_DCLKPTN3 0x74 +#define VENC_DCLKPTN0A 0x78 +#define VENC_DCLKPTN1A 0x7C +#define VENC_DCLKPTN2A 0x80 +#define VENC_DCLKPTN3A 0x84 +#define VENC_DCLKHS 0x88 +#define VENC_DCLKHSA 0x8C +#define VENC_DCLKHR 0x90 +#define VENC_DCLKVS 0x94 +#define VENC_DCLKVR 0x98 +#define VENC_CAPCTL 0x9C +#define VENC_CAPDO 0xA0 +#define VENC_CAPDE 0xA4 +#define VENC_ATR0 0xA8 +#define VENC_ATR1 0xAC +#define VENC_ATR2 0xB0 +#define VENC_VSTAT 0xB8 +#define VENC_RAMADR 0xBC +#define VENC_RAMPORT 0xC0 +#define VENC_DACTST 0xC4 +#define VENC_YCOLVL 0xC8 +#define VENC_SCPROG 0xCC +#define VENC_CVBS 0xDC +#define VENC_CMPNT 0xE0 +#define VENC_ETMG0 0xE4 +#define VENC_ETMG1 0xE8 +#define VENC_ETMG2 0xEC +#define VENC_ETMG3 0xF0 +#define VENC_DACSEL 0xF4 +#define VENC_ARGBX0 0x100 +#define VENC_ARGBX1 0x104 +#define VENC_ARGBX2 0x108 +#define VENC_ARGBX3 0x10C +#define VENC_ARGBX4 0x110 +#define VENC_DRGBX0 0x114 +#define VENC_DRGBX1 0x118 +#define VENC_DRGBX2 0x11C +#define VENC_DRGBX3 0x120 +#define VENC_DRGBX4 0x124 +#define VENC_VSTARTA 0x128 +#define VENC_OSDCLK0 0x12C +#define VENC_OSDCLK1 0x130 +#define VENC_HVLDCL0 0x134 +#define VENC_HVLDCL1 0x138 +#define VENC_OSDHADV 0x13C +#define VENC_CLKCTL 0x140 +#define VENC_GAMCTL 0x144 +#define VENC_XHINTVL 0x174 + +/* bit definitions */ +#define VPBE_PCR_VENC_DIV (1 << 1) +#define VPBE_PCR_CLK_OFF (1 << 0) + +#define VENC_VMOD_VDMD_SHIFT 12 +#define VENC_VMOD_VDMD_YCBCR16 0 +#define VENC_VMOD_VDMD_YCBCR8 1 +#define VENC_VMOD_VDMD_RGB666 2 +#define VENC_VMOD_VDMD_RGB8 3 +#define VENC_VMOD_VDMD_EPSON 4 +#define VENC_VMOD_VDMD_CASIO 5 +#define VENC_VMOD_VDMD_UDISPQVGA 6 +#define VENC_VMOD_VDMD_STNLCD 7 +#define VENC_VMOD_VIE_SHIFT 1 +#define VENC_VMOD_VDMD (7 << 12) +#define VENC_VMOD_ITLCL (1 << 11) +#define VENC_VMOD_ITLC (1 << 10) +#define VENC_VMOD_NSIT (1 << 9) +#define VENC_VMOD_HDMD (1 << 8) +#define VENC_VMOD_TVTYP_SHIFT 6 +#define VENC_VMOD_TVTYP (3 << 6) +#define VENC_VMOD_SLAVE (1 << 5) +#define VENC_VMOD_VMD (1 << 4) +#define VENC_VMOD_BLNK (1 << 3) +#define VENC_VMOD_VIE (1 << 1) +#define VENC_VMOD_VENC (1 << 0) + +/* VMOD TVTYP options for HDMD=0 */ +#define SDTV_NTSC 0 +#define SDTV_PAL 1 +/* VMOD TVTYP options for HDMD=1 */ +#define HDTV_525P 0 +#define HDTV_625P 1 +#define HDTV_1080I 2 +#define HDTV_720P 3 + +#define VENC_VIDCTL_VCLKP (1 << 14) +#define VENC_VIDCTL_VCLKE_SHIFT 13 +#define VENC_VIDCTL_VCLKE (1 << 13) +#define VENC_VIDCTL_VCLKZ_SHIFT 12 +#define VENC_VIDCTL_VCLKZ (1 << 12) +#define VENC_VIDCTL_SYDIR_SHIFT 8 +#define VENC_VIDCTL_SYDIR (1 << 8) +#define VENC_VIDCTL_DOMD_SHIFT 4 +#define VENC_VIDCTL_DOMD (3 << 4) +#define VENC_VIDCTL_YCDIR_SHIFT 0 +#define VENC_VIDCTL_YCDIR (1 << 0) + +#define VENC_VDPRO_ATYCC_SHIFT 5 +#define VENC_VDPRO_ATYCC (1 << 5) +#define VENC_VDPRO_ATCOM_SHIFT 4 +#define VENC_VDPRO_ATCOM (1 << 4) +#define VENC_VDPRO_DAFRQ (1 << 3) +#define VENC_VDPRO_DAUPS (1 << 2) +#define VENC_VDPRO_CUPS (1 << 1) +#define VENC_VDPRO_YUPS (1 << 0) + +#define VENC_SYNCCTL_VPL_SHIFT 3 +#define VENC_SYNCCTL_VPL (1 << 3) +#define VENC_SYNCCTL_HPL_SHIFT 2 +#define VENC_SYNCCTL_HPL (1 << 2) +#define VENC_SYNCCTL_SYEV_SHIFT 1 +#define VENC_SYNCCTL_SYEV (1 << 1) +#define VENC_SYNCCTL_SYEH_SHIFT 0 +#define VENC_SYNCCTL_SYEH (1 << 0) +#define VENC_SYNCCTL_OVD_SHIFT 14 +#define VENC_SYNCCTL_OVD (1 << 14) + +#define VENC_DCLKCTL_DCKEC_SHIFT 11 +#define VENC_DCLKCTL_DCKEC (1 << 11) +#define VENC_DCLKCTL_DCKPW_SHIFT 0 +#define VENC_DCLKCTL_DCKPW (0x3f << 0) + +#define VENC_VSTAT_FIDST (1 << 4) + +#define VENC_CMPNT_MRGB_SHIFT 14 +#define VENC_CMPNT_MRGB (1 << 14) + +#endif /* _VPBE_VENC_REGS_H */ diff --git a/include/media/davinci/vpbe_venc.h b/include/media/davinci/vpbe_venc.h new file mode 100644 index 0000000..e289047 --- /dev/null +++ b/include/media/davinci/vpbe_venc.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2010 Texas Instruments Inc + * + * 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 in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _VPBE_VENC_H +#define _VPBE_VENC_H + +#include + +#define VPBE_VENC_SUBDEV_NAME "vpbe-venc" + +/* venc events */ +#define VENC_END_OF_FRAME BIT(0) +#define VENC_FIRST_FIELD BIT(1) +#define VENC_SECOND_FIELD BIT(2) + +struct venc_platform_data { + enum vpbe_types venc_type; + int (*setup_clock)(enum vpbe_enc_timings_type type, + unsigned int mode); + /* Number of LCD outputs supported */ + int num_lcd_outputs; +}; + +enum venc_ioctls { + VENC_GET_FLD = 1, +}; + +#endif -- 1.6.2.4 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From arnd at arndb.de Fri Jul 8 10:25:12 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Fri, 8 Jul 2011 17:25:12 +0200 Subject: [GIT PULL] DaVinci features for v3.1 In-Reply-To: References: Message-ID: <201107081725.13174.arnd@arndb.de> On Friday 08 July 2011, Nori, Sekhar wrote: > Please pull the following DaVinci features for v3.1. > > This pull request depends on the patch "davinci: dm6467/T EVM: fix setting > up of reference clock rate" in clean-up pull request for successful build. > > Pulling this after the earlier clean-up pull request generates this merge > conflict: > > <<<<<<< HEAD:arch/arm/mach-davinci/include/mach/psc.h > #define MDSTAT_STATE_MASK 0x1f > ======= > #define MDSTAT_STATE_MASK 0x1f > #define MDCTL_FORCE BIT(31) > >>>>>>> 8bb2c4813c534d26eba3beb7888fbd3cbc931f62:arch/arm/mach-davinci/include/mach/psc.h > > The resolution is: > > #define MDSTAT_STATE_MASK 0x1f > #define MDCTL_FORCE BIT(31) Ok. > arch/arm/mach-davinci/board-da850-evm.c | 7 ++ > arch/arm/mach-davinci/clock.c | 8 +- > arch/arm/mach-davinci/clock.h | 1 + > arch/arm/mach-davinci/da850.c | 10 ++ > arch/arm/mach-davinci/devices-da8xx.c | 126 ++++++++++++++++++++++++++++ > arch/arm/mach-davinci/include/mach/da8xx.h | 2 + > arch/arm/mach-davinci/include/mach/psc.h | 3 +- > arch/arm/mach-davinci/psc.c | 14 +++- > 8 files changed, 164 insertions(+), 7 deletions(-) I think we'll have to revisit the AHCI change at some point, I think spreading the initialization code over all subarchitectures is not going to scale in the long run. It's ok for now, but I'll keep an eye on it so we don't add more of these. > git://gitorious.org/linux-davinci/linux-davinci.git cleanup I've pulled it into the davinci/devel branch, to follow the same naming as the as imx feature branch I've pulled earlier. Thanks, Arnd From subhasish at mistralsolutions.com Fri Jul 8 10:25:37 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 15:25:37 -0000 Subject: [PATCH v2 09/13] can: pruss CAN driver. In-Reply-To: <1297435892-28278-1-git-send-email-subhasish@mistralsolutions.com> References: <1297435892-28278-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1297435892-28278-10-git-send-email-subhasish@mistralsolutions.com> This patch adds support for the CAN device emulated on PRUSS. Signed-off-by: Subhasish Ghosh --- drivers/net/can/Kconfig | 1 + drivers/net/can/Makefile | 1 + drivers/net/can/da8xx_pruss/Kconfig | 73 ++ drivers/net/can/da8xx_pruss/Makefile | 7 + drivers/net/can/da8xx_pruss/pruss_can.c | 758 +++++++++++++++++ drivers/net/can/da8xx_pruss/pruss_can_api.c | 1227 +++++++++++++++++++++++++++ drivers/net/can/da8xx_pruss/pruss_can_api.h | 290 +++++++ 7 files changed, 2357 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/da8xx_pruss/Kconfig create mode 100644 drivers/net/can/da8xx_pruss/Makefile create mode 100644 drivers/net/can/da8xx_pruss/pruss_can.c create mode 100644 drivers/net/can/da8xx_pruss/pruss_can_api.c create mode 100644 drivers/net/can/da8xx_pruss/pruss_can_api.h diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index d5a9db6..ae8f0f9 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -112,6 +112,7 @@ config PCH_CAN This driver can access CAN bus. source "drivers/net/can/mscan/Kconfig" +source "drivers/net/can/da8xx_pruss/Kconfig" source "drivers/net/can/sja1000/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 07ca159..849cdbf 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_AT91) += at91_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o +obj-$(CONFIG_CAN_TI_DA8XX_PRU) += da8xx_pruss/ obj-$(CONFIG_CAN_MCP251X) += mcp251x.o obj-$(CONFIG_CAN_BFIN) += bfin_can.o obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o diff --git a/drivers/net/can/da8xx_pruss/Kconfig b/drivers/net/can/da8xx_pruss/Kconfig new file mode 100644 index 0000000..8b68f68 --- /dev/null +++ b/drivers/net/can/da8xx_pruss/Kconfig @@ -0,0 +1,73 @@ +# +# CAN Lite Kernel Configuration +# +config CAN_TI_DA8XX_PRU + depends on CAN_DEV && ARCH_DAVINCI && ARCH_DAVINCI_DA850 + tristate "PRU based CAN emulation for DA8XX" + ---help--- + Enable this to emulate a CAN controller on the PRU of DA8XX. + If not sure, mark N + +config DA8XX_PRU_CANID_MBX0 + hex "CANID for mailbox 0" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 0 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX1 + hex "CANID for mailbox 1" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 1 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX2 + hex "CANID for mailbox 2" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 2 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX3 + hex "CANID for mailbox 3" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 3 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX4 + hex "CANID for mailbox 4" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 4 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX5 + hex "CANID for mailbox 5" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 5 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX6 + hex "CANID for mailbox 6" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 6 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX7 + hex "CANID for mailbox 7" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 7 + Default value is set to 0x123, change this as required. diff --git a/drivers/net/can/da8xx_pruss/Makefile b/drivers/net/can/da8xx_pruss/Makefile new file mode 100644 index 0000000..48f3055 --- /dev/null +++ b/drivers/net/can/da8xx_pruss/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for CAN Lite emulation +# +can_emu-objs := pruss_can.o \ + pruss_can_api.o + +obj-$(CONFIG_CAN_TI_DA8XX_PRU) += can_emu.o diff --git a/drivers/net/can/da8xx_pruss/pruss_can.c b/drivers/net/can/da8xx_pruss/pruss_can.c new file mode 100644 index 0000000..1b3afde --- /dev/null +++ b/drivers/net/can/da8xx_pruss/pruss_can.c @@ -0,0 +1,758 @@ +/* + * TI DA8XX PRU CAN Emulation device driver + * Author: subhasish at mistralsolutions.com + * + * This driver supports TI's PRU CAN Emulation and the + * specs for the same is available at + * + * Copyright (C) 2009 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "pruss_can_api.h" + +#define DRV_NAME "da8xx_pruss_can" +#define DRV_DESC "TI PRU CAN Controller Driver v0.1" +#define PRU_CAN_START 1 +#define PRU_CAN_STOP 0 +#define MB_MIN 0 +#define MB_MAX 7 + +#define PRU_CANMID_IDE BIT(29) /* Extended frame format */ + +#define PRU_CAN_ISR_BIT_CCI BIT(15) +#define PRU_CAN_ISR_BIT_ESI BIT(14) +#define PRU_CAN_ISR_BIT_SRDI BIT(13) +#define PRU_CAN_ISR_BIT_RRI BIT(8) + +#define PRU_CAN_MBXSR_BIT_STATE BIT(7) +#define PRU_CAN_MBXSR_BIT_TC BIT(6) +#define PRU_CAN_MBXSR_BIT_ERR BIT(5) +#define PRU_CAN_MBXSR_BIT_OF BIT(0) + +#define PRU_CAN_GSR_BIT_TXM BIT(7) +#define PRU_CAN_GSR_BIT_RXM BIT(6) +#define PRU_CAN_GSR_BIT_CM BIT(5) +#define PRU_CAN_GSR_BIT_EPM BIT(4) +#define PRU_CAN_GSR_BIT_BFM BIT(3) +#define RTR_MBX_NO 8 +#define MAX_INIT_RETRIES 20 +#define L138_PRU_ARM_FREQ 312000 +#define DFLT_PRU_FREQ 156000000 +#define DFLT_PRU_BITRATE 125000 + +#define CONFIG_DA8XX_PRU_CANID_MBX0 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX1 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX2 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX3 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX4 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX5 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX6 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX7 0x123 + +#ifdef __CAN_DEBUG +#define __can_debug(fmt, args...) printk(KERN_DEBUG "can_debug: " fmt, ## args) +#else +#define __can_debug(fmt, args...) +#endif +#define __can_err(fmt, args...) printk(KERN_ERR "can_err: " fmt, ## args) + +/* + * omapl_pru can private data + */ +struct omapl_pru_can_priv { + struct can_priv can; + struct workqueue_struct *pru_can_wQ; + struct work_struct rx_work; + struct net_device *ndev; + struct device *dev; /* pdev->dev */ + struct clk *clk_timer; + u32 timer_freq; + can_emu_app_hndl can_tx_hndl; + can_emu_app_hndl can_rx_hndl; + const struct firmware *fw_rx; + const struct firmware *fw_tx; + spinlock_t mbox_lock; + u32 trx_irq; + u32 tx_head; + u32 tx_tail; + u32 tx_next; + u32 rx_next; +}; + +static int omapl_pru_can_get_state(const struct net_device *ndev, + enum can_state *state) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + *state = priv->can.state; + return 0; +} + +static int omapl_pru_can_set_bittiming(struct net_device *ndev) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + struct can_bittiming *bt = &priv->can.bittiming; + long bit_error = 0; + + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) { + dev_warn(priv->dev, "WARN: Triple" + "sampling not set due to h/w limitations"); + } + if (pru_can_calc_timing(priv->dev, priv->can.clock.freq, + bt->bitrate) != 0) + return -EINVAL; + bit_error = + (((priv->timer_freq / (priv->timer_freq / bt->bitrate)) - + bt->bitrate) * 1000) / bt->bitrate; + if (bit_error) { + bit_error = + (((priv->timer_freq / (priv->timer_freq / bt->bitrate)) - + bt->bitrate) * 1000000) / bt->bitrate; + printk(KERN_INFO "\nBitrate error %ld.%ld%%\n", + bit_error / 10000, bit_error % 1000); + } else + printk(KERN_INFO "\nBitrate error 0.0%%\n"); + + return 0; +} + +static void omapl_pru_can_stop(struct net_device *ndev) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + u16 int_mask = 0; + + pru_can_mask_ints(priv->dev, int_mask); /* mask all ints */ + pru_can_start_abort_tx(priv->dev, PRU_CAN_STOP); + priv->can.state = CAN_STATE_STOPPED; +} + +/* + * This is to just set the can state to ERROR_ACTIVE + * ip link set canX up type can bitrate 125000 + */ +static void omapl_pru_can_start(struct net_device *ndev) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + u16 int_mask = 0xFFFF; + + if (priv->can.state != CAN_STATE_STOPPED) + omapl_pru_can_stop(ndev); + + pru_can_mask_ints(priv->dev, int_mask); /* unmask all ints */ + + pru_can_get_global_status(priv->dev, &priv->can_tx_hndl); + pru_can_get_global_status(priv->dev, &priv->can_rx_hndl); + + if (PRU_CAN_GSR_BIT_EPM & priv->can_tx_hndl.u32globalstatus) + priv->can.state = CAN_STATE_ERROR_PASSIVE; + else if (PRU_CAN_GSR_BIT_BFM & priv->can_tx_hndl.u32globalstatus) + priv->can.state = CAN_STATE_BUS_OFF; + else + priv->can.state = CAN_STATE_ERROR_ACTIVE; +} + +static int omapl_pru_can_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret = 0; + + switch (mode) { + case CAN_MODE_START: + omapl_pru_can_start(ndev); + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + break; + case CAN_MODE_STOP: + omapl_pru_can_stop(ndev); + if (!netif_queue_stopped(ndev)) + netif_stop_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + return ret; +} + +static netdev_tx_t omapl_pru_can_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + int count; + u8 *data = cf->data; + u8 dlc = cf->can_dlc; + u8 *ptr8data = NULL; + + netif_stop_queue(ndev); + if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ + *((u32 *) &priv->can_tx_hndl.strcanmailbox) = + (cf->can_id & CAN_EFF_MASK) | PRU_CANMID_IDE; + else /* Standard frame format */ + *((u32 *) &priv->can_tx_hndl.strcanmailbox) = + (cf->can_id & CAN_SFF_MASK) << 18; + + if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */ + *((u32 *) &priv->can_tx_hndl.strcanmailbox) |= CAN_RTR_FLAG; + + ptr8data = &priv->can_tx_hndl.strcanmailbox.u8data7 + (dlc - 1); + for (count = 0; count < (u8) dlc; count++) { + *ptr8data-- = *data++; + } + *((u32 *) &priv->can_tx_hndl.strcanmailbox.u16datalength) = (u32) dlc; +/* + * search for the next available mbx + * if the next mbx is busy, then try the next + 1 + * do this until the head is reached. + * if still unable to tx, stop accepting any packets + * if able to tx and the head is reached, then reset next to tail, i.e mbx0 + * if head is not reached, then just point to the next mbx + */ + for (; priv->tx_next <= priv->tx_head; priv->tx_next++) { + priv->can_tx_hndl.ecanmailboxnumber = + (can_mailbox_number) priv->tx_next; + if (-1 == pru_can_write_data_to_mailbox(priv->dev, + &priv->can_tx_hndl)) { + if (priv->tx_next == priv->tx_head) { + priv->tx_next = priv->tx_tail; + if (!netif_queue_stopped(ndev)) + netif_stop_queue(ndev); /* IF stalled */ + dev_err(priv->dev, + "%s: no tx mbx available", __func__); + return NETDEV_TX_BUSY; + } else + continue; + } else { + /* set transmit request */ + pru_can_tx(priv->dev, priv->tx_next, CAN_TX_PRU_1); + pru_can_tx_mode_set(priv->dev, false, ecanreceive); + pru_can_tx_mode_set(priv->dev, true, ecantransmit); + pru_can_start_abort_tx(priv->dev, PRU_CAN_START); + priv->tx_next++; + can_put_echo_skb(skb, ndev, 0); + break; + } + } + if (priv->tx_next > priv->tx_head) { + priv->tx_next = priv->tx_tail; + } + return NETDEV_TX_OK; +} + +static int omapl_pru_can_rx(struct net_device *ndev, u32 mbxno) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 pru_can_mbx_data; + u8 *data = NULL; + u8 *ptr8data = NULL; + int count = 0; + + skb = alloc_can_skb(ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + dev_err(priv->dev, + "alloc_can_skb() failed\n"); + return -ENOMEM; + } + data = cf->data; + /* get payload */ + priv->can_rx_hndl.ecanmailboxnumber = (can_mailbox_number) mbxno; + if (pru_can_get_data_from_mailbox(priv->dev, &priv->can_rx_hndl)) { + __can_err("failed to get data from mailbox\n"); + return -EAGAIN; + } + /* give ownweship to pru */ + pru_can_tx(priv->dev, mbxno, CAN_RX_PRU_0); + + /* get data length code */ + cf->can_dlc = + get_can_dlc(* + ((u32 *) &priv->can_rx_hndl.strcanmailbox. + u16datalength) & 0xF); + if (cf->can_dlc <= 4) { + ptr8data = + &priv->can_rx_hndl.strcanmailbox.u8data3 + (4 - + cf->can_dlc); + for (count = 0; count < cf->can_dlc; count++) { + *data++ = *ptr8data++; + } + } else { + ptr8data = &priv->can_rx_hndl.strcanmailbox.u8data3; + for (count = 0; count < 4; count++) { + *data++ = *ptr8data++; + } + ptr8data = + &priv->can_rx_hndl.strcanmailbox.u8data4 - (cf->can_dlc - + 5); + for (count = 0; count < cf->can_dlc - 4; count++) { + *data++ = *ptr8data++; + } + } + + pru_can_mbx_data = *((u32 *) &priv->can_rx_hndl.strcanmailbox); + /* get id extended or std */ + if (pru_can_mbx_data & PRU_CANMID_IDE) + cf->can_id = (pru_can_mbx_data & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (pru_can_mbx_data >> 18) & CAN_SFF_MASK; + + if (pru_can_mbx_data & CAN_RTR_FLAG) + cf->can_id |= CAN_RTR_FLAG; + + netif_rx_ni(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + return 0; +} + +static int omapl_pru_can_err(struct net_device *ndev, int int_status, + int err_status) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + int tx_err_cnt, rx_err_cnt; + + /* propogate the error condition to the can stack */ + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + dev_err(priv->dev, + "alloc_can_err_skb() failed\n"); + return -ENOMEM; + } + + if (err_status & PRU_CAN_GSR_BIT_EPM) { /* error passive int */ + priv->can.state = CAN_STATE_ERROR_PASSIVE; + ++priv->can.can_stats.error_passive; + cf->can_id |= CAN_ERR_CRTL; + tx_err_cnt = pru_can_get_error_cnt(priv->dev, CAN_TX_PRU_1); + rx_err_cnt = pru_can_get_error_cnt(priv->dev, CAN_RX_PRU_0); + if (tx_err_cnt > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + if (rx_err_cnt > 127) + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + + dev_dbg(priv->ndev->dev.parent, "Error passive interrupt\n"); + } + + if (err_status & PRU_CAN_GSR_BIT_BFM) { + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + /* + * Disable all interrupts in bus-off to avoid int hog + * this should be handled by the pru + */ + pru_can_mask_ints(priv->dev, 0xFFFF); + can_bus_off(ndev); + dev_dbg(priv->ndev->dev.parent, "Bus off mode\n"); + } + + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + return 0; +} + +void omapl_pru_can_rx_wQ(struct work_struct *work) +{ + struct omapl_pru_can_priv *priv = container_of(work, + struct omapl_pru_can_priv, rx_work); + struct net_device *ndev = priv->ndev; + u32 bit_set, mbxno = 0; + + if (-1 == pru_can_get_intr_status(priv->dev, &priv->can_rx_hndl)) + return; + + if (PRU_CAN_ISR_BIT_RRI & priv->can_rx_hndl.u32interruptstatus) { + mbxno = RTR_MBX_NO; + omapl_pru_can_rx(ndev, mbxno); + } else { + /* Extract the mboxno from the status */ + for (bit_set = 0; ((priv->can_rx_hndl.u32interruptstatus & 0xFF) + >> bit_set != 0); bit_set++) + ; + if (0 == bit_set) { + dev_err(priv->dev, + "%s: invalid mailbox number: %X\n", __func__, + priv->can_rx_hndl.u32interruptstatus); + } else { + mbxno = bit_set - 1; + if (PRU_CAN_ISR_BIT_ESI & priv->can_rx_hndl. + u32interruptstatus) { + pru_can_get_global_status(priv->dev, + &priv->can_rx_hndl); + omapl_pru_can_err(ndev, + priv->can_rx_hndl.u32interruptstatus, + priv->can_rx_hndl.u32globalstatus); + } else { + omapl_pru_can_rx(ndev, mbxno); + } + } + } +} + +irqreturn_t omapl_tx_can_intr(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u32 bit_set, mbxno; + + pru_can_get_intr_status(priv->dev, &priv->can_tx_hndl); + if ((PRU_CAN_ISR_BIT_CCI & priv->can_tx_hndl.u32interruptstatus) + || (PRU_CAN_ISR_BIT_SRDI & priv->can_tx_hndl.u32interruptstatus)) { + __can_debug("tx_int_status = 0x%X\n", + priv->can_tx_hndl.u32interruptstatus); + can_free_echo_skb(ndev, 0); + } else { + for (bit_set = 0; ((priv->can_tx_hndl.u32interruptstatus & 0xFF) + >> bit_set != 0); bit_set++) + ; + if (0 == bit_set) { + __can_err("%s: invalid mailbox number\n", __func__); + can_free_echo_skb(ndev, 0); + } else { + mbxno = bit_set - 1; /* mail box numbering starts from 0 */ + if (PRU_CAN_ISR_BIT_ESI & priv->can_tx_hndl. + u32interruptstatus) { + /* read gsr and ack pru */ + pru_can_get_global_status(priv->dev, &priv->can_tx_hndl); + omapl_pru_can_err(ndev, + priv->can_tx_hndl. + u32interruptstatus, + priv->can_tx_hndl. + u32globalstatus); + } else { + stats->tx_packets++; + /* stats->tx_bytes += dlc; */ + /*can_get_echo_skb(ndev, 0);*/ + } + } + } + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + + can_get_echo_skb(ndev, 0); + pru_can_tx_mode_set(priv->dev, true, ecanreceive); + return IRQ_HANDLED; +} + +irqreturn_t omapl_rx_can_intr(int irq, void *dev_id) +{ + + struct net_device *ndev = dev_id; + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + u32 intc_status = 0; + + intc_status = pru_can_get_intc_status(priv->dev); + if (intc_status & 4) + return omapl_tx_can_intr(irq, dev_id); + if (intc_status & 2) { + if (!work_pending(&priv->rx_work)) + queue_work(priv->pru_can_wQ, &priv->rx_work); + } + + return IRQ_HANDLED; +} + +static int omapl_pru_can_open(struct net_device *ndev) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + int err; + + /* register interrupt handler */ + err = request_irq(priv->trx_irq, &omapl_rx_can_intr, IRQF_SHARED, + "pru_can_irq", ndev); + if (err) { + dev_err(priv->dev, "error requesting rx interrupt\n"); + goto exit_trx_irq; + } + /* common open */ + err = open_candev(ndev); + if (err) { + dev_err(priv->dev, "open_candev() failed %d\n", err); + goto exit_open; + } + + pru_can_emu_init(priv->dev, priv->can.clock.freq); + priv->tx_tail = MB_MIN; + priv->tx_head = MB_MAX; + + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX0, 0); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX1, 1); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX2, 2); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX3, 3); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX4, 4); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX5, 5); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX6, 6); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX7, 7); + + omapl_pru_can_start(ndev); + netif_start_queue(ndev); + return 0; + +exit_open: + free_irq(priv->trx_irq, ndev); +exit_trx_irq: + return err; +} + +static int omapl_pru_can_close(struct net_device *ndev) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + + if (!netif_queue_stopped(ndev)) + netif_stop_queue(ndev); + + close_candev(ndev); + + free_irq(priv->trx_irq, ndev); + return 0; +} + +static const struct net_device_ops omapl_pru_can_netdev_ops = { + .ndo_open = omapl_pru_can_open, + .ndo_stop = omapl_pru_can_close, + .ndo_start_xmit = omapl_pru_can_start_xmit, +}; + +static int __devinit omapl_pru_can_probe(struct platform_device *pdev) +{ + struct net_device *ndev = NULL; + const struct da8xx_pru_can_data *pdata; + struct omapl_pru_can_priv *priv = NULL; + struct device *dev = &pdev->dev; + u32 err; + + pdata = dev->platform_data; + if (!pdata) { + dev_err(&pdev->dev, "platform data not found\n"); + return -EINVAL; + } + + ndev = alloc_candev(sizeof(struct omapl_pru_can_priv), MB_MAX + 1); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev failed\n"); + err = -ENOMEM; + goto probe_exit; + } + priv = netdev_priv(ndev); + + priv->trx_irq = platform_get_irq(to_platform_device(dev->parent), 0); + if (!priv->trx_irq) { + dev_err(&pdev->dev, "unable to get pru interrupt resources!\n"); + err = -ENODEV; + goto probe_exit; + } + + priv->ndev = ndev; + priv->dev = dev; /* priv->dev = pdev->dev */ + + priv->can.bittiming_const = NULL; + priv->can.do_set_bittiming = omapl_pru_can_set_bittiming; + priv->can.do_set_mode = omapl_pru_can_set_mode; + priv->can.do_get_state = omapl_pru_can_get_state; + priv->can_tx_hndl.u8prunumber = CAN_TX_PRU_1; + priv->can_rx_hndl.u8prunumber = CAN_RX_PRU_0; + + /* we support local echo, no arp */ + ndev->flags |= (IFF_ECHO | IFF_NOARP); + + /* pdev->dev->device_private->driver_data = ndev */ + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + ndev->netdev_ops = &omapl_pru_can_netdev_ops; + + priv->can.clock.freq = pruss_get_clk_freq(priv->dev); + + priv->clk_timer = clk_get(&pdev->dev, "pll1_sysclk2"); + if (IS_ERR(priv->clk_timer)) { + dev_err(&pdev->dev, "no timer clock available\n"); + err = PTR_ERR(priv->clk_timer); + priv->clk_timer = NULL; + goto probe_exit_candev; + } + priv->timer_freq = clk_get_rate(priv->clk_timer); + + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "register_candev() failed\n"); + err = -ENODEV; + goto probe_exit_clk; + } + + err = request_firmware(&priv->fw_tx, "PRU_CAN_Emulation_Tx.bin", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "can't load firmware\n"); + err = -ENODEV; + goto probe_exit_clk; + } + + dev_info(&pdev->dev, "fw_tx size %d. downloading...\n", + priv->fw_tx->size); + + err = request_firmware(&priv->fw_rx, "PRU_CAN_Emulation_Rx.bin", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "can't load firmware\n"); + err = -ENODEV; + goto probe_release_fw; + } + dev_info(&pdev->dev, "fw_rx size %d. downloading...\n", + priv->fw_rx->size); + + /* init the pru */ + pru_can_emu_init(priv->dev, priv->can.clock.freq); + udelay(200); + + pruss_enable(priv->dev, CAN_RX_PRU_0); + pruss_enable(priv->dev, CAN_TX_PRU_1); + + /* download firmware into pru */ + err = pruss_load(priv->dev, CAN_RX_PRU_0, + (u32 *)priv->fw_rx->data, (priv->fw_rx->size / 4)); + if (err) { + dev_err(&pdev->dev, "firmware download error\n"); + err = -ENODEV; + goto probe_release_fw_1; + } + err = pruss_load(priv->dev, CAN_TX_PRU_1, + (u32 *)priv->fw_tx->data, (priv->fw_tx->size / 4)); + if (err) { + dev_err(&pdev->dev, "firmware download error\n"); + err = -ENODEV; + goto probe_release_fw_1; + } + + if (pru_can_calc_timing(priv->dev, DFLT_PRU_FREQ, + DFLT_PRU_BITRATE) != 0) + return -EINVAL; + + pruss_run(priv->dev, CAN_RX_PRU_0); + pruss_run(priv->dev, CAN_TX_PRU_1); + + /*Create The Work Queue */ + priv->pru_can_wQ = create_freezeable_workqueue("omapl_pru_wQ"); + if (priv->pru_can_wQ == NULL) { + dev_err(&pdev->dev, "failed to create work queue\n"); + err = -ENODEV; + goto probe_release_fw_1; + } + + INIT_WORK(&priv->rx_work, omapl_pru_can_rx_wQ); + dev_info(&pdev->dev, + "%s device registered (trx_irq = %d, clk = %d)\n", + DRV_NAME, priv->trx_irq, priv->can.clock.freq); + + return 0; + +probe_release_fw_1: + release_firmware(priv->fw_rx); +probe_release_fw: + release_firmware(priv->fw_tx); +probe_exit_clk: + clk_put(priv->clk_timer); +probe_exit_candev: + if (NULL != ndev) + free_candev(ndev); +probe_exit: + return err; +} + +static int __devexit omapl_pru_can_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + + omapl_pru_can_stop(ndev); + + pru_can_emu_exit(priv->dev); + release_firmware(priv->fw_tx); + release_firmware(priv->fw_rx); + clk_put(priv->clk_timer); + flush_workqueue(priv->pru_can_wQ); + destroy_workqueue(priv->pru_can_wQ); + unregister_candev(ndev); + free_candev(ndev); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int omapl_pru_can_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + dev_info(&pdev->dev, "%s not yet implemented\n", __func__); + return 0; +} + +static int omapl_pru_can_resume(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s not yet implemented\n", __func__); + return 0; +} +#else +#define omapl_pru_can_suspend NULL +#define omapl_pru_can_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver omapl_pru_can_driver = { + .probe = omapl_pru_can_probe, + .remove = __devexit_p(omapl_pru_can_remove), + .suspend = omapl_pru_can_suspend, + .resume = omapl_pru_can_resume, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init omapl_pru_can_init(void) +{ + __can_debug(KERN_INFO DRV_DESC "\n"); + return platform_driver_register(&omapl_pru_can_driver); +} + +module_init(omapl_pru_can_init); + +static void __exit omapl_pru_can_exit(void) +{ + __can_debug(KERN_INFO DRV_DESC " unloaded\n"); + platform_driver_unregister(&omapl_pru_can_driver); +} + +module_exit(omapl_pru_can_exit); + +MODULE_AUTHOR("Subhasish Ghosh "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("omapl pru CAN netdevice driver"); diff --git a/drivers/net/can/da8xx_pruss/pruss_can_api.c b/drivers/net/can/da8xx_pruss/pruss_can_api.c new file mode 100644 index 0000000..2f7438a --- /dev/null +++ b/drivers/net/can/da8xx_pruss/pruss_can_api.c @@ -0,0 +1,1227 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Wilfred Felix + * + * 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 "pruss_can_api.h" + +static can_emu_drv_inst gstr_can_inst[ecanmaxinst]; + +/* + * pru_can_set_brp() Updates the BRP register of PRU0 + * and PRU1 of OMAP L138. This API will be called by the + * Application to updtae the BRP register of PRU0 and PRU1 + * + * param u16bitrateprescaler The can bus bitrate + * prescaler value be set + * + * return SUCCESS or FAILURE + */ +s16 pru_can_set_brp(struct device *dev, u16 u16bitrateprescaler) +{ + + u32 u32offset; + + if (u16bitrateprescaler > 255) { + return -1; + } + + u32offset = (PRU_CAN_RX_CLOCK_BRP_REGISTER); + pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1); + + u32offset = (PRU_CAN_TX_CLOCK_BRP_REGISTER); + pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1); + + return 0; + +} + +/* + * pru_can_set_bit_timing() Updates the timing register + * of PRU0 and PRU1 of OMAP L138. This API will be called by + * the Application to updtae the timing register of PRU0 and PRU1 + * + * param pstrbittiming Pointer to structure holding + * the bit timing values for can bus. + * + * return SUCCESS or FAILURE + */ +s16 pru_can_set_bit_timing(struct device *dev, + can_bit_timing_consts *pstrbittiming) +{ + + u32 u32offset; + u32 u32serregister; + + u32serregister = 0; + + if (pstrbittiming == NULL) { + return -1; + } + + if ((pstrbittiming->u8syncjumpwidth > PRU_CAN_MAX_SJW) || + (pstrbittiming->u8phseg1 > PRU_CAN_MAX_PHSEG1) || + (pstrbittiming->u8phseg2 > PRU_CAN_MAX_PHSEG2)) { + return -1; + } + + u32serregister = u32serregister | + ((pstrbittiming->u8syncjumpwidth << 7) | + (pstrbittiming->u8phseg1 << 3) | + (pstrbittiming->u8phseg2)); + + u32offset = (PRU_CAN_TX_TIMING_REGISTER); + pruss_writel(dev, u32offset, (u32 *) &u32serregister, 1); + + u32offset = (PRU_CAN_RX_TIMING_REGISTER); + pruss_writel(dev, u32offset, (u32 *) &u32serregister, 1); + + return 0; +} + + +/* + * pru_can_calc_timing() + * Updates the timing values of PRU0 and PRU1 of OMAP L138. + * This API will be called by the + * Application to updtae the timing values of PRU0 and PRU1 + * + * return SUCCESS or FAILURE + */ + +s16 pru_can_calc_timing(struct device *dev, u32 pru_freq, u32 bit_rate) +{ + u16 u16phaseseg1; + u16 u16phaseseg2; + u32 u32offset; + u32 u32timing_value; + u32 u32setup_value; + u32timing_value = TIMER_CLK_FREQ / bit_rate; + u32offset = (PRU_CAN_TIMING_VAL_TX); + pruss_writel(dev, u32offset, (u32 *) &u32timing_value, 4); + pruss_readl(dev, u32offset, (u32 *) &u32timing_value, 4); + u32setup_value = + (GPIO_SETUP_DELAY * (pru_freq / 1000000) / 1000) / + DELAY_LOOP_LENGTH; + u32offset = (PRU_CAN_TIMING_VAL_TX_SJW); + pruss_writel(dev, u32offset, (u32 *) &u32setup_value, 4); + u16phaseseg1 = (u16) (u32timing_value / 2); + u16phaseseg2 = u32timing_value - u16phaseseg1; + u16phaseseg1 -= TIMER_SETUP_DELAY; + u16phaseseg2 -= TIMER_SETUP_DELAY; + u32setup_value = (u16phaseseg1 << 16) | u16phaseseg2; + u32offset = (PRU_CAN_TIMING_VAL_RX); + pruss_writel(dev, u32offset, (u32 *) &u32setup_value, 4); + u32offset = (PRU_CAN_TIMING_VAL_RX + 4); + pruss_writel(dev, u32offset, (u32 *) &u32timing_value, 4); + + return 0; +} + +/* + * pru_can_write_data_to_mailbox() + * Updates the transmit mailboxes of PRU1 of OMAP L138. + * This API will be called by the Application to update + * the transmit mailboxes of PRU1 + * + * param pu16canframedata Can mailbox data buffer + * + * param u8mailboxnum Mailbox to be updated + * + * return SUCCESS or FAILURE + */ +s16 pru_can_write_data_to_mailbox(struct device *dev, + can_emu_app_hndl *pstremuapphndl) +{ + s16 s16subrtnretval; + u32 u32offset; + + if (pstremuapphndl == NULL) { + return -1; + } + + switch ((u8) pstremuapphndl->ecanmailboxnumber) { + case 0: + u32offset = (PRU_CAN_TX_MAILBOX0); + break; + case 1: + u32offset = (PRU_CAN_TX_MAILBOX1); + break; + case 2: + u32offset = (PRU_CAN_TX_MAILBOX2); + break; + case 3: + u32offset = (PRU_CAN_TX_MAILBOX3); + break; + case 4: + u32offset = (PRU_CAN_TX_MAILBOX4); + break; + case 5: + u32offset = (PRU_CAN_TX_MAILBOX5); + break; + case 6: + u32offset = (PRU_CAN_TX_MAILBOX6); + break; + case 7: + u32offset = (PRU_CAN_TX_MAILBOX7); + break; + default: + return -1; + } + + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &(pstremuapphndl->strcanmailbox), 4); + if (s16subrtnretval == -1) { + return -1; + } + return 0; +} + +/* + * pru_can_get_data_from_mailbox() + * Receive data from the receive mailboxes of PRU0 of OMAP L138. + * This API will be called by the Application to get data from + * the receive mailboxes of PRU0 + * + * param pu16canframedata Can mailbox data buffer + * + * param u8mailboxnum Mailbox to be updated + * + * return SUCCESS or FAILURE + */ +s16 pru_can_get_data_from_mailbox(struct device *dev, + can_emu_app_hndl *pstremuapphndl) +{ + s16 s16subrtnretval; + u32 u32offset; + + if (pstremuapphndl == NULL) { + return -1; + } + + switch ((u8) pstremuapphndl->ecanmailboxnumber) { + case 0: + u32offset = (PRU_CAN_RX_MAILBOX0); + break; + case 1: + u32offset = (PRU_CAN_RX_MAILBOX1); + break; + case 2: + u32offset = (PRU_CAN_RX_MAILBOX2); + break; + case 3: + u32offset = (PRU_CAN_RX_MAILBOX3); + break; + case 4: + u32offset = (PRU_CAN_RX_MAILBOX4); + break; + case 5: + u32offset = (PRU_CAN_RX_MAILBOX5); + break; + case 6: + u32offset = (PRU_CAN_RX_MAILBOX6); + break; + case 7: + u32offset = (PRU_CAN_RX_MAILBOX7); + break; + case 8: + u32offset = (PRU_CAN_RX_MAILBOX8); + break; + default: + return -1; + } + + s16subrtnretval = + pruss_readl(dev, u32offset, + (u32 *) &(pstremuapphndl->strcanmailbox), + 4); + if (s16subrtnretval == -1) { + return -1; + } + return 0; +} + +/* + * pru_can_receive_id_map() + * Receive mailboxes ID Mapping of PRU0 of OMAP L138. + * This API will be called by the Application + * to map the IDs to receive mailboxes of PRU0 + * + * param u32nodeid Can node ID + * + * param ecanmailboxno Mailbox to be mapped + * + * return SUCCESS or FAILURE + */ +s16 pru_can_rx_id_map(struct device *dev, u32 u32nodeid, + can_mailbox_number ecanmailboxno) +{ + + pruss_writel(dev, (PRU_CAN_ID_MAP + + (((u8) ecanmailboxno) * 4)), (u32 *) &u32nodeid, 1); + + return 0; +} + +/* + * pru_can_get_intr_status() + * Gets the interrupts status register value. + * This API will be called by the Application + * to get the interrupts status register value + * + * param u8prunumber PRU number for which IntStatusReg + * has to be read + * + * return SUCCESS or FAILURE + */ +s16 pru_can_get_intr_status(struct device *dev, + can_emu_app_hndl *pstremuapphndl) +{ + u32 u32offset; + s16 s16subrtnretval = -1; + + if (pstremuapphndl == NULL) { + return -1; + } + + if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) { + u32offset = (PRU_CAN_TX_INTERRUPT_STATUS_REGISTER); + } else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) { + u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER); + } else { + return -1; + } + + s16subrtnretval = pruss_readl(dev, u32offset, + (u32 *) &pstremuapphndl->u32interruptstatus, 1); + if (s16subrtnretval == -1) { + return -1; + } + + return 0; +} + +/* + * pru_can_get_global_status() Gets the globalstatus + * register value. This API will be called by the Application + * to get the global status register value + * + * return SUCCESS or FAILURE + */ +s16 pru_can_get_global_status(struct device *dev, + can_emu_app_hndl *pstremuapphndl) +{ + u32 u32offset; + int s16subrtnretval = -1; + + if (pstremuapphndl == NULL) { + return -1; + } + + if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) { + u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER); + } else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) { + u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER); + } else { + return -1; + } + + s16subrtnretval = pruss_readl(dev, u32offset, + (u32 *) &pstremuapphndl->u32globalstatus, 1); + if (s16subrtnretval == -1) { + return -1; + } + + return 0; +} + +/* + * pru_can_get_mailbox_status() Gets the mailbox status + * register value. This API will be called by the Application + * to get the mailbox status register value + * + * return SUCCESS or FAILURE + */ +s16 pru_can_get_mailbox_status(struct device *dev, + can_emu_app_hndl *pstremuapphndl) +{ + u32 u32offset; + s16 s16subrtnretval = -1; + + if (pstremuapphndl == NULL) { + return -1; + } + + if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) { + switch (pstremuapphndl->ecanmailboxnumber) { + case 0: + u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER); + break; + case 1: + u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER); + break; + case 2: + u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER); + break; + case 3: + u32offset = (PRU_CAN_TX_MAILBOX3_STATUS_REGISTER); + break; + case 4: + u32offset = (PRU_CAN_TX_MAILBOX4_STATUS_REGISTER); + break; + case 5: + u32offset = (PRU_CAN_TX_MAILBOX5_STATUS_REGISTER); + break; + case 6: + u32offset = (PRU_CAN_TX_MAILBOX6_STATUS_REGISTER); + break; + case 7: + u32offset = (PRU_CAN_TX_MAILBOX7_STATUS_REGISTER); + break; + default: + return -1; + } + } + + else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) { + switch (pstremuapphndl->ecanmailboxnumber) { + case 0: + u32offset = (PRU_CAN_RX_MAILBOX0_STATUS_REGISTER); + break; + case 1: + u32offset = (PRU_CAN_RX_MAILBOX1_STATUS_REGISTER); + break; + case 2: + u32offset = (PRU_CAN_RX_MAILBOX2_STATUS_REGISTER); + break; + case 3: + u32offset = (PRU_CAN_RX_MAILBOX3_STATUS_REGISTER); + break; + case 4: + u32offset = (PRU_CAN_RX_MAILBOX4_STATUS_REGISTER); + break; + case 5: + u32offset = (PRU_CAN_RX_MAILBOX5_STATUS_REGISTER); + break; + case 6: + u32offset = (PRU_CAN_RX_MAILBOX6_STATUS_REGISTER); + break; + case 7: + u32offset = (PRU_CAN_RX_MAILBOX7_STATUS_REGISTER); + break; + case 8: + u32offset = (PRU_CAN_RX_MAILBOX8_STATUS_REGISTER); + break; + default: + return -1; + } + } + + else { + return -1; + } + + s16subrtnretval = pruss_readl(dev, u32offset, + (u32 *) &pstremuapphndl->u32mailboxstatus, 1); + if (s16subrtnretval == -1) { + return -1; + } + + return 0; +} + +s16 pru_can_tx_mode_set(struct device *dev, bool btransfer_flag, + can_transfer_direction ecan_trx) +{ + u32 u32offset; + u32 u32value; + + if (ecan_trx == ecantransmit) { + u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER); + pruss_readl(dev, u32offset, &u32value, 1); + if (btransfer_flag == true) { + u32value &= 0x1F; + u32value |= 0x80; + } else { + u32value &= 0x7F; + } + pruss_writel(dev, u32offset, &u32value, 1); + u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER); + pruss_writel(dev, u32offset, &u32value, 1); + } else if (ecan_trx == ecanreceive) { + u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER); + pruss_readl(dev, u32offset, &u32value, 1); + if (btransfer_flag == true) { + u32value &= 0x1F; + u32value |= 0x40; + } else { + u32value &= 0xBF; + } + pruss_writel(dev, u32offset, &u32value, 1); + u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER); + pruss_writel(dev, u32offset, &u32value, 1); + } else + return -1; + + return 0; +} + +/* + * pru_can_config_mode_set() Sets the timing value + * for data transfer. This API will be called by the Application + * to set timing valus for data transfer + * + * return SUCCESS or FAILURE + */ +s16 pru_can_config_mode_set(struct device *dev, bool bconfigmodeflag) +{ + + u32 u32bitrateprescaler; + u32 u32canbittiming; + + pruss_readl(dev, (PRU_CAN_TX_CLOCK_BRP_REGISTER), + (u32 *) &u32bitrateprescaler, 1); + pruss_readl(dev, (PRU_CAN_TX_TIMING_REGISTER), + (u32 *) &u32canbittiming, 1); + + if (bconfigmodeflag == 1) { + pru_can_calc_timing(dev, u32canbittiming, u32bitrateprescaler); + } + + else { + pru_can_calc_timing(dev, 0, 0); + } + + return 0; +} + +/* + * pru_can_emu_init() Initializes the Can + * Emulation Parameters. This API will be called by the Application + * to Initialize the Can Emulation Parameters + * + * param u32pruclock PRU Clock value + * + * return SUCCESS or FAILURE + */ +s16 pru_can_emu_init(struct device *dev, u32 u32pruclock) +{ + u32 u32offset; + u32 u32value; + s16 s16subrtnretval = -1; + u8 u8loop; + + for (u8loop = 0; u8loop < (u8) ecanmaxinst; u8loop++) { + gstr_can_inst[u8loop].bcaninststate = (bool) 0; + gstr_can_inst[u8loop].ecantransferdirection = + (can_transfer_direction) 0; + gstr_can_inst[u8loop].u32apphandlerptr = 0; + } + + u32offset = (PRU_CAN_TX_GLOBAL_CONTROL_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000040; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000040; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_INTERRUPT_MASK_REGISTER & 0xFFFF); + u32value = 0x00004000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_INTERRUPT_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = + pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX3_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX4_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX5_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX6_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX7_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_ERROR_COUNTER_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_TIMING_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_CLOCK_BRP_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_ERROR_COUNTER_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_POLARITY0 & 0xFFFF); + u32value = 0xFFFFFFFF; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + u32offset = (PRUSS_INTC_POLARITY1 & 0xFFFF); + u32value = 0xFFFFFFFF; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + u32offset = (PRUSS_INTC_TYPE0 & 0xFFFF); + u32value = 0x1C000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + u32offset = (PRUSS_INTC_TYPE1 & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_HSTINTENIDXCLR & 0xFFFF); + u32value = 0x0; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_GLBLEN & 0xFFFF); + u32value = 0x1; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + /* tx intr map arm->pru */ + u32offset = (PRUSS_INTC_HSTINTENIDXSET & 0xFFFF); + u32value = 0x0; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_HOSTMAP0 & 0xFFFF); + u32value = 0x03020100; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_HOSTMAP1 & 0xFFFF); + u32value = 0x07060504; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_HOSTMAP2 & 0xFFFF); + u32value = 0x0000908; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_CHANMAP0 & 0xFFFF); + u32value = 0; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_CHANMAP8 & 0xFFFF); + u32value = 0x00020200; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF); + u32value = 32; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF); + u32value = 19; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 19; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF); + u32value = 18; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 18; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF); + u32value = 34; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 34; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 32; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_HOSTINTEN & 0xFFFF); + u32value = 0x5; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + +/* PRU0 - Rx Internal Registers Initializations */ + + u32offset = (PRU_CAN_RX_GLOBAL_CONTROL_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000040; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_INTERRUPT_MASK_REGISTER & 0xFFFF); + u32value = 0x00004000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX0_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX1_STATUS_REGISTER & 0xFFFF); + u32value = 0x0000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX2_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX3_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX4_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX5_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX6_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX7_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_ERROR_COUNTER_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_TIMING_REGISTER & 0xFFFF); + u32value = 0x0000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_CLOCK_BRP_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + return 0; +} + + +/* + * pru_can_emu_open() Opens the can emu for + * application to use. This API will be called by the Application + * to Open the can emu for application to use. + * + * param pstremuapphndl Pointer to application handler + * structure + * + * return SUCCESS or FAILURE + */ +s16 pru_can_emu_open(struct device *dev, can_emu_app_hndl *pstremuapphndl) +{ + + if (pstremuapphndl == NULL) { + return -1; + } + + if (gstr_can_inst[pstremuapphndl->ecaninstance].bcaninststate == 1) { + return -1; + } + + gstr_can_inst[(u8) pstremuapphndl->ecaninstance]. + bcaninststate = (bool)1; + gstr_can_inst[(u8) pstremuapphndl-> + ecaninstance].ecantransferdirection = + (can_transfer_direction)(u8)pstremuapphndl->ecantransferdirection; + gstr_can_inst[(u8) pstremuapphndl->ecaninstance]. + u32apphandlerptr = (u32) pstremuapphndl; + + return 0; +} + + +/* + * brief pru_can_emu_close() Closes the can emu for other + * applications to use. This API will be called by the Application to Close + * the can emu for other applications to use + * + * param pstremuapphndl Pointer to application handler structure + * + * return SUCCESS or FAILURE + */ +s16 pru_can_emu_close(struct device *dev, can_emu_app_hndl *pstremuapphndl) +{ + + if (pstremuapphndl == NULL) { + return -1; + } + if (gstr_can_inst[pstremuapphndl->ecaninstance].bcaninststate == 0) { + return -1; + } + if ((u32) pstremuapphndl != gstr_can_inst[(u8) pstremuapphndl-> + ecaninstance].u32apphandlerptr){ + return -1; + } + gstr_can_inst[(u8) pstremuapphndl->ecaninstance].bcaninststate + = (bool) 0; + gstr_can_inst[(u8) pstremuapphndl-> + ecaninstance].ecantransferdirection = (can_transfer_direction) 0; + gstr_can_inst[(u8) pstremuapphndl->ecaninstance].u32apphandlerptr = 0; + + return 0; +} + +/* + * brief pru_can_emu_exit() Diables all the PRUs + * This API will be called by the Application to disable all PRUs + * param None + * return SUCCESS or FAILURE + */ +s16 pru_can_emu_exit(struct device *dev) +{ + s16 s16subrtnretval; + + s16subrtnretval = pruss_disable(dev, CAN_RX_PRU_0); + if (s16subrtnretval == -1) + return -1; + s16subrtnretval = pruss_disable(dev, CAN_TX_PRU_1); + if (s16subrtnretval == -1) + return -1; + + return 0; +} + +s16 pru_can_emu_sreset(struct device *dev) +{ + return 0; +} + +s16 pru_can_tx(struct device *dev, u8 u8mailboxnumber, u8 u8prunumber) +{ + u32 u32offset = 0; + u32 u32value = 0; + s16 s16subrtnretval = -1; + + if (DA8XX_PRUCORE_1 == u8prunumber) { + switch (u8mailboxnumber) { + case 0: + u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 1: + u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 2: + u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 3: + u32offset = (PRU_CAN_TX_MAILBOX3_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 4: + u32offset = (PRU_CAN_TX_MAILBOX4_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 5: + u32offset = (PRU_CAN_TX_MAILBOX5_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 6: + u32offset = (PRU_CAN_TX_MAILBOX6_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 7: + u32offset = (PRU_CAN_TX_MAILBOX7_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + default: + return -1; + } + } else { + + u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_readl(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + u32value = u32value & ~(1 << u8mailboxnumber); + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + switch (u8mailboxnumber) { + case 0: + u32offset = (PRU_CAN_RX_MAILBOX0_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 1: + u32offset = (PRU_CAN_RX_MAILBOX1_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 2: + u32offset = (PRU_CAN_RX_MAILBOX2_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 3: + u32offset = (PRU_CAN_RX_MAILBOX3_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 4: + u32offset = (PRU_CAN_RX_MAILBOX4_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 5: + u32offset = (PRU_CAN_RX_MAILBOX5_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 6: + u32offset = (PRU_CAN_RX_MAILBOX6_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 7: + u32offset = (PRU_CAN_RX_MAILBOX7_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + default: + return -1; + } + } + return 0; +} + +s16 pru_can_start_abort_tx(struct device *dev, bool bcantransmitabortflag) +{ + u32 u32offset; + u32 u32value; + s16 s16subrtnretval; + u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF); + u32value = 32; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + + u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 32; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + + u32offset = (PRUSS_INTC_STATIDXSET & 0xFFFF); + u32value = 32; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + return 0; +} + +s16 pru_can_mask_ints(struct device *dev, u32 int_mask) +{ + return 0; +} + +int pru_can_get_error_cnt(struct device *dev, u8 u8prunumber) +{ + return 0; +} + +int pru_can_get_intc_status(struct device *dev) +{ + u32 u32offset = 0; + u32 u32getvalue = 0; + u32 u32clrvalue = 0; + + u32offset = (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + pruss_readl(dev, u32offset, (u32 *) &u32getvalue, 1); + + if (u32getvalue & 4) + u32clrvalue = 34; /* CLR Event 34 */ + + if (u32getvalue & 2) + u32clrvalue = 33; /* CLR Event 33 */ + + if (u32clrvalue) { + u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF); + pruss_writel(dev, u32offset, &u32clrvalue, 1); + } else + return -1; + + return u32getvalue; +} diff --git a/drivers/net/can/da8xx_pruss/pruss_can_api.h b/drivers/net/can/da8xx_pruss/pruss_can_api.h new file mode 100644 index 0000000..7550456 --- /dev/null +++ b/drivers/net/can/da8xx_pruss/pruss_can_api.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Ganeshan N + * + * 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 _PRU_CAN_API_H_ +#define _PRU_CAN_API_H_ + +#include +#include + + +#define CAN_BIT_TIMINGS (0x273) + +/* Timer Clock is sourced from DDR freq (PLL1 SYS CLK 2) */ +#define TIMER_CLK_FREQ 132000000 + +#define TIMER_SETUP_DELAY 14 +#define GPIO_SETUP_DELAY 150 + +#define CAN_RX_PRU_0 PRUSS_NUM0 +#define CAN_TX_PRU_1 PRUSS_NUM1 + +/* Number of Instruction in the Delay loop */ +#define DELAY_LOOP_LENGTH 2 + +#define PRU1_BASE_ADDR 0x2000 + +#define PRU_CAN_TX_GLOBAL_CONTROL_REGISTER (PRU1_BASE_ADDR) +#define PRU_CAN_TX_GLOBAL_STATUS_REGISTER (PRU1_BASE_ADDR + 0x04) +#define PRU_CAN_TX_INTERRUPT_MASK_REGISTER (PRU1_BASE_ADDR + 0x08) +#define PRU_CAN_TX_INTERRUPT_STATUS_REGISTER (PRU1_BASE_ADDR + 0x0C) +#define PRU_CAN_TX_MAILBOX0_STATUS_REGISTER (PRU1_BASE_ADDR + 0x10) +#define PRU_CAN_TX_MAILBOX1_STATUS_REGISTER (PRU1_BASE_ADDR + 0x14) +#define PRU_CAN_TX_MAILBOX2_STATUS_REGISTER (PRU1_BASE_ADDR + 0x18) +#define PRU_CAN_TX_MAILBOX3_STATUS_REGISTER (PRU1_BASE_ADDR + 0x1C) +#define PRU_CAN_TX_MAILBOX4_STATUS_REGISTER (PRU1_BASE_ADDR + 0x20) +#define PRU_CAN_TX_MAILBOX5_STATUS_REGISTER (PRU1_BASE_ADDR + 0x24) +#define PRU_CAN_TX_MAILBOX6_STATUS_REGISTER (PRU1_BASE_ADDR + 0x28) +#define PRU_CAN_TX_MAILBOX7_STATUS_REGISTER (PRU1_BASE_ADDR + 0x2C) +#define PRU_CAN_TX_ERROR_COUNTER_REGISTER (PRU1_BASE_ADDR + 0x30) +#define PRU_CAN_TX_TIMING_REGISTER (PRU1_BASE_ADDR + 0x34) +#define PRU_CAN_TX_CLOCK_BRP_REGISTER (PRU1_BASE_ADDR + 0x38) + +#define PRU_CAN_TX_MAILBOX0 (PRU1_BASE_ADDR + 0x40) +#define PRU_CAN_TX_MAILBOX1 (PRU1_BASE_ADDR + 0x50) +#define PRU_CAN_TX_MAILBOX2 (PRU1_BASE_ADDR + 0x60) +#define PRU_CAN_TX_MAILBOX3 (PRU1_BASE_ADDR + 0x70) +#define PRU_CAN_TX_MAILBOX4 (PRU1_BASE_ADDR + 0x80) +#define PRU_CAN_TX_MAILBOX5 (PRU1_BASE_ADDR + 0x90) +#define PRU_CAN_TX_MAILBOX6 (PRU1_BASE_ADDR + 0xA0) +#define PRU_CAN_TX_MAILBOX7 (PRU1_BASE_ADDR + 0xB0) + +#define PRU_CAN_TIMING_VAL_TX (PRU1_BASE_ADDR + 0xC0) +#define PRU_CAN_TIMING_VAL_TX_SJW (PRU1_BASE_ADDR + 0xC4) +#define PRU_CAN_TRANSMIT_FRAME (PRU1_BASE_ADDR + 0xE0) + +#define PRU0_BASE_ADDR 0 + +#define PRU_CAN_RX_GLOBAL_CONTROL_REGISTER (PRU0_BASE_ADDR) +#define PRU_CAN_RX_GLOBAL_STATUS_REGISTER (PRU0_BASE_ADDR + 0x04) +#define PRU_CAN_RX_INTERRUPT_MASK_REGISTER (PRU0_BASE_ADDR + 0x08) +#define PRU_CAN_RX_INTERRUPT_STATUS_REGISTER (PRU0_BASE_ADDR + 0x0C) +#define PRU_CAN_RX_MAILBOX0_STATUS_REGISTER (PRU0_BASE_ADDR + 0x10) +#define PRU_CAN_RX_MAILBOX1_STATUS_REGISTER (PRU0_BASE_ADDR + 0x14) +#define PRU_CAN_RX_MAILBOX2_STATUS_REGISTER (PRU0_BASE_ADDR + 0x18) +#define PRU_CAN_RX_MAILBOX3_STATUS_REGISTER (PRU0_BASE_ADDR + 0x1C) +#define PRU_CAN_RX_MAILBOX4_STATUS_REGISTER (PRU0_BASE_ADDR + 0x20) +#define PRU_CAN_RX_MAILBOX5_STATUS_REGISTER (PRU0_BASE_ADDR + 0x24) +#define PRU_CAN_RX_MAILBOX6_STATUS_REGISTER (PRU0_BASE_ADDR + 0x28) +#define PRU_CAN_RX_MAILBOX7_STATUS_REGISTER (PRU0_BASE_ADDR + 0x2C) +#define PRU_CAN_RX_MAILBOX8_STATUS_REGISTER (PRU0_BASE_ADDR + 0x30) +#define PRU_CAN_RX_ERROR_COUNTER_REGISTER (PRU0_BASE_ADDR + 0x34) +#define PRU_CAN_RX_TIMING_REGISTER (PRU0_BASE_ADDR + 0x38) +#define PRU_CAN_RX_CLOCK_BRP_REGISTER (PRU0_BASE_ADDR + 0x3C) + +#define PRU_CAN_RX_MAILBOX0 (PRU0_BASE_ADDR + 0x40) +#define PRU_CAN_RX_MAILBOX1 (PRU0_BASE_ADDR + 0x50) +#define PRU_CAN_RX_MAILBOX2 (PRU0_BASE_ADDR + 0x60) +#define PRU_CAN_RX_MAILBOX3 (PRU0_BASE_ADDR + 0x70) +#define PRU_CAN_RX_MAILBOX4 (PRU0_BASE_ADDR + 0x80) +#define PRU_CAN_RX_MAILBOX5 (PRU0_BASE_ADDR + 0x90) +#define PRU_CAN_RX_MAILBOX6 (PRU0_BASE_ADDR + 0xA0) +#define PRU_CAN_RX_MAILBOX7 (PRU0_BASE_ADDR + 0xB0) +#define PRU_CAN_RX_MAILBOX8 (PRU0_BASE_ADDR + 0xC0) + +#define PRU_CAN_TIMING_VAL_RX (PRU0_BASE_ADDR + 0xD0) +#define PRU_CAN_RECEIVE_FRAME (PRU0_BASE_ADDR + 0xD4) +#define PRU_CAN_ID_MAP (PRU0_BASE_ADDR + 0xF0) + +#define PRU_CAN_ERROR_ACTIVE 128 + +#define CAN_ACK_FAILED 0xE +#define CAN_ARBTR_FAIL 0xD +#define CAN_BIT_ERROR 0xC +#define CAN_TRANSMISSION_SUCCESS 0xA + +#define STD_DATA_FRAME 0x1 +#define EXTD_DATA_FRAME 0x2 +#define STD_REMOTE_FRAME 0x3 +#define EXTD_REMOTE_FRAME 0x4 + +#define PRU_CAN_MAX_SJW 8 +#define PRU_CAN_MAX_PHSEG1 25 +#define PRU_CAN_MAX_PHSEG2 25 + +#define DA8XX_PRUCANCORE_0_REGS 0x7000 +#define DA8XX_PRUCANCORE_1_REGS 0x7800 +#define PRU0_PROG_RAM_START_OFFSET 0x8000 +#define PRU1_PROG_RAM_START_OFFSET 0xC000 +#define PRU_CAN_INIT_MAX_TIMEOUT 0xFF + +typedef enum { + ecaninst0 = 0, + ecaninst1, + ecanmaxinst +} can_instance_enum; + +typedef enum { + ecanmailbox0 = 0, + ecanmailbox1, + ecanmailbox2, + ecanmailbox3, + ecanmailbox4, + ecanmailbox5, + ecanmailbox6, + ecanmailbox7 +} can_mailbox_number; + +typedef enum { + ecandirectioninit = 0, + ecantransmit, + ecanreceive +} can_transfer_direction; + +typedef struct { + u16 u16extendedidentifier; + u16 u16baseidentifier; + u8 u8data7; + u8 u8data6; + u8 u8data5; + u8 u8data4; + u8 u8data3; + u8 u8data2; + u8 u8data1; + u8 u8data0; + u16 u16datalength; + u16 u16crc; +} can_mail_box_structure; + +typedef struct { + can_transfer_direction ecantransferdirection; +} can_mailbox_config; + +typedef struct { + can_instance_enum ecaninstance; + can_transfer_direction ecantransferdirection; + can_mail_box_structure strcanmailbox; + can_mailbox_number ecanmailboxnumber; + u8 u8prunumber; + u32 u32globalstatus; + u32 u32interruptstatus; + u32 u32mailboxstatus; +} can_emu_app_hndl; + +typedef struct { + bool bcaninststate; + can_transfer_direction ecantransferdirection; + u32 u32apphandlerptr; +} can_emu_drv_inst; + +typedef struct { + u8 u8syncjumpwidth; + u8 u8phseg1; + u8 u8phseg2; +} can_bit_timing_consts; + +/* Field Definition Macros */ + +/* CONTROL */ + +/* + * pru_can_set_brp() Updates the BRP register of PRU. + */ +s16 pru_can_set_brp(struct device *dev, u16 u16prescaler); + +/* + * pru_can_set_bit_timing() Updates the timing register of PRU + */ +s16 pru_can_set_bit_timing(struct device *dev, + can_bit_timing_consts *pstrbittiming); + +/* + * pru_can_calc_timing() Updates the timing values of PRU + */ +s16 pru_can_calc_timing(struct device *dev, + u32 u32bittiming, u32 u32bitrateprescaler); + +/* + * pru_can_write_data_to_mailbox() Updates the transmit mailboxes of PRU1 + */ +s16 pru_can_write_data_to_mailbox(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + +/* + * pru_can_get_data_from_mailbox() Receive data from receive mailboxes + */ +s16 pru_can_get_data_from_mailbox(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + +/* + * pru_can_rx_id_map() Receive mailboxes ID Mapping of PRU0 + */ +s16 pru_can_rx_id_map(struct device *dev, + u32 u32nodeid, can_mailbox_number ecanmailboxno); + +/* + *pru_can_get_intr_status() Get interrupts status register value + */ +s16 pru_can_get_intr_status(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + + +/* + * pru_can_get_global_status() Get the globalstatus register value + */ +s16 pru_can_get_global_status(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + +/* + * pru_can_get_mailbox_status() Get mailbox status reg value + */ +s16 pru_can_get_mailbox_status(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + +/* + * pru_can_configuration_mode_set() Sets timing val for data transfer + */ +s16 pru_can_config_mode_set(struct device *dev, + bool bconfig_modeflag); + +/* + * pru_can_emu_init() Initializes Can Emulation Parameters + */ +s16 pru_can_emu_init(struct device *dev, + u32 u32pruclock); + +/* + * pru_can_emu_open() Opens can emu for application to use + */ +s16 pru_can_emu_open(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + +/* + * pru_can_emu_close() Closes can emu for applications to use + */ +s16 pru_can_emu_close(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + +/* + * pru_can_emu_exit() Diables all the PRUs + */ +s16 pru_can_emu_exit(struct device *dev); + +s16 pru_can_tx_mode_set(struct device *dev, bool btransfer_flag, + can_transfer_direction ecan_trx); + +s16 pru_can_emu_sreset(struct device *dev); + +s16 pru_can_tx(struct device *dev, + u8 u8mailboxnumber, u8 u8prunumber); + +s16 pru_can_start_abort_tx(struct device *dev, + bool btxabort_flag); + +s16 pru_can_mask_ints(struct device *dev, u32 int_mask); + +s32 pru_can_get_error_cnt(struct device *dev, u8 u8prunumber); + +s32 pru_can_get_intc_status(struct device *dev); +#endif -- 1.7.2.3 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From sshtylyov at ru.mvista.com Fri Jul 8 10:24:57 2011 From: sshtylyov at ru.mvista.com (Sergei Shtylyov) Date: Fri, 8 Jul 2011 19:24:57 +0400 Subject: [PATCH] DaVinci: correct MDSTAT_STATE_MASK Message-ID: <201107081924.57577.sshtylyov@ru.mvista.com> MDSTAT.STATE occupies bits 0..5 according to all available documentation, so fix the #define MDSTAT_STATE_MASK at last. Using the wrong value seems to have been harmless though... This was noticed by me back in 2009 but I didn't follow up with the patch back then... :-/ Signed-off-by: Sergei Shtylyov --- The patch is against the recent DaVinci tree. arch/arm/mach-davinci/include/mach/psc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) Index: linux-davinci/arch/arm/mach-davinci/include/mach/psc.h =================================================================== --- linux-davinci.orig/arch/arm/mach-davinci/include/mach/psc.h +++ linux-davinci/arch/arm/mach-davinci/include/mach/psc.h @@ -243,7 +243,7 @@ #define PSC_STATE_DISABLE 2 #define PSC_STATE_ENABLE 3 -#define MDSTAT_STATE_MASK 0x1f +#define MDSTAT_STATE_MASK 0x3f #define MDCTL_FORCE BIT(31) #ifndef __ASSEMBLER__ From sshtylyov at mvista.com Fri Jul 8 10:30:26 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 08 Jul 2011 19:30:26 +0400 Subject: [PATCH] DaVinci: correct MDSTAT_STATE_MASK In-Reply-To: <201107081924.57577.sshtylyov@ru.mvista.com> References: <201107081924.57577.sshtylyov@ru.mvista.com> Message-ID: <4E172292.6090600@mvista.com> Hello. I wrote: > MDSTAT.STATE occupies bits 0..5 according to all available documentation, so fix > the #define MDSTAT_STATE_MASK at last. Using the wrong value seems to have been > harmless though... Except maybe sleep.S -- davinci_ddr_psc_config() could exit too early when disabling DDR clock. The same is true with davinci_psc_config() in general but I don't think that anything depended on its timely return in that case. > This was noticed by me back in 2009 but I didn't follow up with the patch back > then... :-/ Shame on me. :-) > Signed-off-by: Sergei Shtylyov WBR, Sergei From subhasish at mistralsolutions.com Fri Jul 8 11:02:05 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 16:02:05 -0000 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> This patch adds support for the TTY compliant Soft-UART device emulated on PRUSS. This patch depends on: davinci: macro rename DA8XX_LPSC0_DMAX to DA8XX_LPSC0_PRUSS. https://patchwork.kernel.org/patch/615681/ davinci: changed SRAM allocator to shared ram. https://patchwork.kernel.org/patch/549351/ Signed-off-by: Subhasish Ghosh --- drivers/tty/serial/Kconfig | 18 + drivers/tty/serial/Makefile | 6 + drivers/tty/serial/pruss_suart.c | 1061 ++++++++++++++++++++ drivers/tty/serial/pruss_suart.h | 1038 +++++++++++++++++++ drivers/tty/serial/pruss_suart_api.c | 1710 ++++++++++++++++++++++++++++++++ drivers/tty/serial/pruss_suart_utils.c | 393 ++++++++ include/linux/serial_core.h | 2 + 7 files changed, 4228 insertions(+), 0 deletions(-) create mode 100644 drivers/tty/serial/pruss_suart.c create mode 100644 drivers/tty/serial/pruss_suart.h create mode 100644 drivers/tty/serial/pruss_suart_api.c create mode 100644 drivers/tty/serial/pruss_suart_utils.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 2b83346..6c26ebf 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1596,4 +1596,22 @@ config SERIAL_PCH_UART This driver is for PCH(Platform controller Hub) UART of Intel EG20T which is an IOH(Input/Output Hub) for x86 embedded processor. Enabling PCH_DMA, this PCH UART works as DMA mode. + +config SERIAL_PRUSS_SUART + depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850 + select SERIAL_CORE + tristate "PRUSS based SoftUART emulation on DA8XX" + ---help--- + This driver emulates up to eight different UARTs on the PRUSS. + You may modify the NR_SUARTS macro in the driver to emulate + less number of UARTS as per your requirement. + If not sure, mark No + +config PRUSS_SUART_MCASP + depends on ARCH_DAVINCI_DA830 && SERIAL_PRUSS_SUART + default "0" + int "McASP number" + ---help--- + Enter the McASP number to use with SUART (0, 1 or 2). + You will need to recompile the kernel if this is changed. endmenu diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 8ea92e9..e1eaaf3 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -92,3 +92,9 @@ obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o + +pruss_uart-objs := pruss_suart.o \ + pruss_suart_api.o \ + pruss_suart_utils.o + +obj-$(CONFIG_SERIAL_PRUSS_SUART) += pruss_uart.o diff --git a/drivers/tty/serial/pruss_suart.c b/drivers/tty/serial/pruss_suart.c new file mode 100644 index 0000000..37c3c21 --- /dev/null +++ b/drivers/tty/serial/pruss_suart.c @@ -0,0 +1,1061 @@ +/* + * PRUSS SUART Emulation device driver + * Author: subhasish at mistralsolutions.com + * + * This driver supports TI's PRU SUART Emulation and the + * specs for the same is available at + * + * Copyright (C) 2010, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pruss_suart.h" + +#define NR_SUART 8 +#define DRV_NAME "da8xx_pruss_uart" +#define DRV_DESC "PRUSS SUART Driver v1.0" +#define MAX_SUART_RETRIES 100 +#define SUART_CNTX_SZ 512 +#define SUART_FIFO_TIMEOUT_DFLT 5 +#define SUART_FIFO_TIMEOUT_MIN 4 +#define SUART_FIFO_TIMEOUT_MAX 500 + +/* Default timeout set to 5ms */ +static s16 suart_timeout = SUART_FIFO_TIMEOUT_DFLT; +module_param(suart_timeout, short, S_IRUGO); +MODULE_PARM_DESC(suart_timeout, + "fifo timeout in milli seconds (min: 4; max: 500)"); + +struct suart_fifo { + void *fifo_vaddr_buff_tx; + void *fifo_vaddr_buff_rx; + void *fifo_phys_addr_tx; + void *fifo_phys_addr_rx; +}; + +struct omapl_pru_suart { + struct uart_port port[NR_SUART]; + struct device *dev; + unsigned long tx_empty[NR_SUART]; + struct clk *clk_mcasp; + struct suart_fifo suart_fifo_addr[NR_SUART]; + struct suart_handle suart_hdl[NR_SUART]; + struct pruss_suart_iomap suart_iomap; + struct tasklet_struct tx_task[NR_SUART]; + u32 clk_freq_pru; + u32 clk_freq_mcasp; + u32 tx_loadsz; +}; + +static u32 suart_get_duplex(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + return soft_uart->suart_hdl[uart_no].uart_type; +} + +static inline void __stop_tx(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + struct uart_port *port = &soft_uart->port[uart_no]; + u16 txready; + u32 i; + + /* Check if any TX in progress */ + for (i = 0, txready = 1; (i < 10000) && txready; i++) { + txready = (pru_softuart_get_tx_status + (dev, &soft_uart->suart_hdl[uart_no]) & + CHN_TXRX_STATUS_RDY); + } + /* To stop tx, disable the TX interrupt */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_clrmask(dev, soft_uart->suart_hdl[uart_no].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + pru_softuart_clr_tx_status(dev, &soft_uart->suart_hdl[uart_no]); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void pruss_suart_stop_tx(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + + __stop_tx(soft_uart, port->line); +} + +static void omapl_pru_tx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + struct circ_buf *xmit = &soft_uart->port[uart_no].state->xmit; + struct device *dev = soft_uart->dev; + s32 count = 0; + + if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_TX)) + return; + + if (uart_circ_empty(xmit) || + uart_tx_stopped(&soft_uart->port[uart_no])) { + pruss_suart_stop_tx(&soft_uart->port[uart_no]); + set_bit(0, &soft_uart->tx_empty[uart_no]); + return; + } + + for (count = 0; count <= soft_uart->tx_loadsz; count++) { + *((s8 *)soft_uart->suart_fifo_addr[uart_no].fifo_vaddr_buff_tx + + count) = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + soft_uart->port[uart_no].icount.tx++; + if (uart_circ_empty(xmit)) { + uart_circ_clear(xmit); + break; + } + } + + if (count == (SUART_FIFO_LEN + 1)) + count = SUART_FIFO_LEN; + + /* Write the character to the data port */ + if (pru_softuart_write(dev, + &soft_uart->suart_hdl[uart_no], + (u32 *)&soft_uart->suart_fifo_addr + [uart_no].fifo_phys_addr_tx, count) != 0) { + dev_err(dev, "failed to tx data\n"); + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&soft_uart->port[uart_no]); + +#if 0 + if (uart_circ_empty(xmit)) + __stop_tx(soft_uart, uart_no); +#endif +} + +static void suart_tx_task(unsigned long data) +{ + struct uart_port *port = (struct uart_port *)data; + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + + omapl_pru_tx_chars(soft_uart, port->line); +} + +static void omapl_pru_rx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + struct tty_struct *tty = NULL; + struct device *dev = soft_uart->dev; + s8 flags = TTY_NORMAL; + u16 rx_status, data_len = SUART_FIFO_LEN; + u32 data_len_read; + u8 suart_data[SUART_FIFO_LEN + 1]; + s32 i = 0; + + if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_RX)) + return; + + /* read the status */ + rx_status = pru_softuart_get_rx_status(dev, + &soft_uart->suart_hdl[uart_no]); + + pru_softuart_read_data(dev, &soft_uart->suart_hdl[uart_no], + suart_data, data_len + 1, &data_len_read); + + tty = tty_port_tty_get(&soft_uart->port[uart_no].state->port); + + if (!tty) + return; + + /* check for errors */ + if (rx_status & CHN_TXRX_STATUS_ERR) { + if (rx_status & CHN_TXRX_STATUS_FE) + soft_uart->port[uart_no].icount.frame++; + if (rx_status & CHN_TXRX_STATUS_OVRNERR) + soft_uart->port[uart_no].icount.overrun++; + if (rx_status & CHN_TXRX_STATUS_BI) + soft_uart->port[uart_no].icount.brk++; + rx_status &= soft_uart->port[uart_no]. + read_status_mask; + if (rx_status & CHN_TXRX_STATUS_FE) + flags = TTY_FRAME; + if (rx_status & CHN_TXRX_STATUS_OVRNERR) + flags = TTY_OVERRUN; + if (rx_status & CHN_TXRX_STATUS_BI) + flags = TTY_BREAK; + +#ifdef SUPPORT_SYSRQ + soft_uart->port[uart_no].sysrq = 0; +#endif + } else { + for (i = 0; i <= data_len_read; i++) { + soft_uart->port[uart_no].icount.rx++; + /* check for sys rq */ + if (uart_handle_sysrq_char + (&soft_uart->port[uart_no], suart_data)) + continue; + } + tty_insert_flip_string(tty, suart_data, data_len_read); + } + + /* push data into tty */ + pru_softuart_clr_rx_status(dev, &soft_uart->suart_hdl[uart_no]); + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} + +static irqreturn_t pruss_suart_interrupt(s32 irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + u16 txrx_flag; + u32 ret; + unsigned long flags = 0; + u16 uart_num = port->line + 1; + + spin_lock_irqsave(&port->lock, flags); + + do { + ret = pru_softuart_get_isrstatus(dev, uart_num, &txrx_flag); + if (ret != 0) { + dev_err(dev, "suart%d: failed to get interrupt, ret:" + " 0x%X txrx_flag 0x%X\n", + port->line, ret, txrx_flag); + spin_unlock_irqrestore(&port->lock, flags); + return IRQ_NONE; + } + if ((PRU_RX_INTR & txrx_flag) == PRU_RX_INTR) { + pru_intr_clr_isrstatus(dev, uart_num, PRU_RX_INTR); + if ((soft_uart->port[port->line].ignore_status_mask & + CHN_TXRX_STATUS_RDY) == CHN_TXRX_STATUS_RDY) { + pru_softuart_clr_rx_status(dev, + &soft_uart->suart_hdl + [port->line]); + } else { + omapl_pru_rx_chars(soft_uart, port->line); + } + } + + if ((PRU_TX_INTR & txrx_flag) == PRU_TX_INTR) { + pru_intr_clr_isrstatus(dev, uart_num, PRU_TX_INTR); + pru_softuart_clr_tx_status(dev, &soft_uart->suart_hdl + [port->line]); + tasklet_schedule(&soft_uart->tx_task[port->line]); + } + } while (txrx_flag & (PRU_RX_INTR | PRU_TX_INTR)); + + spin_unlock_irqrestore(&port->lock, flags); + return IRQ_HANDLED; +} + +static void pruss_suart_stop_rx(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + spin_lock_irqsave(&port->lock, flags); + /* disable rx interrupt */ + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void pruss_suart_enable_ms(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + dev_err(dev, "modem control timer not supported\n"); +} + +static void pruss_suart_start_tx(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + /* unmask the tx interrupts */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + spin_unlock_irqrestore(&port->lock, flags); + + if (test_and_clear_bit(0, &soft_uart->tx_empty[port->line])) + omapl_pru_tx_chars(soft_uart, port->line); +} + +static u32 pruss_suart_tx_empty(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + + return (pru_softuart_get_tx_status(dev, + &soft_uart->suart_hdl[port->line]) + & CHN_TXRX_STATUS_RDY) ? 0 : TIOCSER_TEMT; +} + +static u32 pruss_suart_get_mctrl(struct uart_port *port) +{ + return -ENOTSUPP; +} + +static void pruss_suart_set_mctrl(struct uart_port *port, u32 mctrl) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + dev_dbg(dev, "modem control not supported\n"); +} + +static void pruss_suart_break_ctl(struct uart_port *port, s32 break_state) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + spin_lock_irqsave(&port->lock, flags); + + if (break_state == -1) + suart_intr_clrmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + else + suart_intr_setmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void pruss_suart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + u8 cval = 0; + unsigned long flags = 0; + u32 baud = 0; + u32 old_csize = old ? old->c_cflag & CSIZE : CS8; + +/* + * Do not allow unsupported configurations to be set + */ + if (1) { + termios->c_cflag &= ~(CRTSCTS | CMSPAR | CSTOPB + | PARENB | PARODD | CMSPAR); + } + + switch (termios->c_cflag & CSIZE) { + case CS6: + cval = ePRU_SUART_DATA_BITS6; + break; + case CS7: + cval = ePRU_SUART_DATA_BITS7; + break; + default: + case CS8: + cval = ePRU_SUART_DATA_BITS8; + break; + } + /* + * We do not support CS5. + */ + if ((termios->c_cflag & CSIZE) == CS5) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + } + if (pru_softuart_setdatabits + (dev, &soft_uart->suart_hdl[port->line], cval, cval) != 0) + dev_err(dev, "failed to set data bits to: %d\n", cval); + +/* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 16); + +/* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&port->lock, flags); + + /* Set the baud */ + if (pru_softuart_setbaud(dev, &soft_uart->suart_hdl[port->line], + SUART_DEFAULT_BAUD / baud, + SUART_DEFAULT_BAUD / baud) != 0) + dev_err(dev, "failed to set baud to: %d\n", baud); + +/* + * update port->read_config_mask and port->ignore_config_mask + * to indicate the events we are interested in receiving + */ + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, SUART_GBL_INTR_ERR_MASK); + port->read_status_mask = 0; + if (termios->c_iflag & INPCK) { /* Input parity check not supported, + just enabled FE */ + port->read_status_mask |= CHN_TXRX_STATUS_FE; + suart_intr_setmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_FE); + } + if (termios->c_iflag & (BRKINT | PARMRK)) { + port->read_status_mask |= CHN_TXRX_STATUS_BI; + suart_intr_setmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + } +/* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= CHN_TXRX_STATUS_BI; + /* + * If we're ignoring break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) { + port->ignore_status_mask |= + (CHN_TXRX_STATUS_OVRNERR | CHN_TXRX_STATUS_FE); + /* + * Overrun in case of RX + * Underrun in case of TX + */ + suart_intr_clrmask(dev, soft_uart-> + suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_FE); + } + suart_intr_clrmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + } +/* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) { + port->ignore_status_mask |= CHN_TXRX_STATUS_RDY; + pruss_suart_stop_rx(port); + } + /* + * update the per port timeout + */ + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +/* + * Grab any interrupt resources and initialise any low level driver + * state. Enable the port for reception. It should not activate + * RTS nor DTR; this will be done via a separate call to set_mctrl. + * + * This method will only be called when the port is initially opened. + * + * Locking: port_sem taken. + * Interrupts: globally disabled. + */ +static s32 pruss_suart_startup(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + s32 retval; + + /* + * Disable interrupts from this port + */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + spin_unlock_irqrestore(&port->lock, flags); + + retval = request_irq(port->irq, pruss_suart_interrupt, + port->irqflags, "suart_irq", port); + if (retval) { + free_irq(port->irq, port); /* should we free this if err */ + goto out; + } + /* + * enable interrupts from this port + */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, SUART_GBL_INTR_ERR_MASK); + + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + spin_unlock_irqrestore(&port->lock, flags); + + if ((suart_get_duplex(soft_uart, port->line) & ePRU_SUART_HALF_TX) + == ePRU_SUART_HALF_TX) { + suart_pru_to_host_intr_enable(dev, soft_uart-> + suart_hdl[port->line].uart_num, PRU_TX_INTR, true); + } + /* Seed RX if port is half-rx or full-duplex */ + if ((suart_get_duplex(soft_uart, port->line) & ePRU_SUART_HALF_RX) + == ePRU_SUART_HALF_RX) { + suart_pru_to_host_intr_enable(dev, soft_uart-> + suart_hdl[port->line].uart_num, PRU_RX_INTR, true); + pru_softuart_read(dev, &soft_uart->suart_hdl[port->line], + (u32 *)&soft_uart->suart_fifo_addr[port->line]. + fifo_phys_addr_rx, SUART_FIFO_LEN); + } +out: + return retval; +} + +/* + * Disable the port, disable any break condition that may be in + * effect, and free any interrupt resources. It should not disable + * RTS nor DTR; this will have already been done via a separate + * call to set_mctrl. + * + * Drivers must not access port->info once this call has completed. + * + * This method will only be called when there are no more users of + * this port. + * + * Locking: port_sem taken. + * Interrupts: caller dependent. + */ + +static void pruss_suart_shutdown(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + /* + * Disable interrupts from this port + */ + /* Disable BI and FE intr */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + spin_unlock_irqrestore(&port->lock, flags); + + /* free interrupts */ + free_irq(port->irq, port); +} + +/* + * Return a pointer to a string constant describing the specified + * port, or return NULL, in which case the string 'unknown' is + * substituted. + * + * Locking: none. + * Interrupts: caller dependent. + */ + +static const char *pruss_suart_type(struct uart_port *port) +{ + return "suart_tty"; +} + +/* + * Release any memory and IO region resources currently in use by + * the port. + * + * Locking: none. + * Interrupts: caller dependent. + */ + +static void pruss_suart_release_port(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct platform_device *pdev = to_platform_device(port->dev); + + if (0 != pru_softuart_close(&soft_uart->suart_hdl[port->line])) + dev_err(&pdev->dev, "failed to close suart\n"); + + return; +} + +/* + * Request any memory and IO region resources required by the port. + * If any fail, no resources should be registered when this function + * returns, and it should return -EBUSY on failure. + * + * Locking: none. + * Interrupts: caller dependent. + * + * We need to d/l the f/w in probe and since this api + * is called per uart, the request_mem_region should + * be called in probe itself. + */ +static s32 pruss_suart_request_port(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct platform_device *pdev = to_platform_device(port->dev); + struct device *dev = soft_uart->dev; + struct suart_config pru_suart_config; + s16 timeout = 0; + u32 err = 0; + + if (soft_uart == NULL) { + dev_err(&pdev->dev, "soft_uart ptr failed\n"); + return -ENODEV; + } + err = pru_softuart_open(&soft_uart->suart_hdl[port->line]); + if (err != 0) { + dev_err(&pdev->dev, "failed to open suart: %d\n", err); + err = -ENODEV; + goto exit; + } + set_bit(0, &soft_uart->tx_empty[port->line]); + + /* set fifo /timeout */ + if (SUART_FIFO_TIMEOUT_MIN > suart_timeout) { + dev_err(&pdev->dev, "fifo timeout less than %d ms not supported\n", + SUART_FIFO_TIMEOUT_MIN); + suart_timeout = SUART_FIFO_TIMEOUT_MIN; + } else if (SUART_FIFO_TIMEOUT_MAX < suart_timeout) { + dev_err(&pdev->dev, "fifo timeout more than %d ms not supported\n", + SUART_FIFO_TIMEOUT_MAX); + suart_timeout = SUART_FIFO_TIMEOUT_MAX; + } + + /* This is only for x8 */ + timeout = (SUART_DEFAULT_BAUD * suart_timeout) / 1000; + pru_set_fifo_timeout(dev, timeout); + + if (soft_uart->suart_hdl[port->line].uart_num == PRU_SUART_UART1) { + pru_suart_config.tx_serializer = PRU_SUART0_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART0_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART2) { + pru_suart_config.tx_serializer = PRU_SUART1_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART1_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART3) { + pru_suart_config.tx_serializer = PRU_SUART2_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART2_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART4) { + pru_suart_config.tx_serializer = PRU_SUART3_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART3_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART5) { + pru_suart_config.tx_serializer = PRU_SUART4_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART4_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART6) { + pru_suart_config.tx_serializer = PRU_SUART5_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART5_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART7) { + pru_suart_config.tx_serializer = PRU_SUART6_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART6_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART8) { + pru_suart_config.tx_serializer = PRU_SUART7_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART7_CONFIG_RX_SER; + } else { + return -ENOTSUPP; + } + + /* Some defaults to startup. reconfigured by terimos later */ + pru_suart_config.tx_clk_divisor = 1; + pru_suart_config.rx_clk_divisor = 1; + pru_suart_config.tx_bits_per_char = ePRU_SUART_DATA_BITS8; + pru_suart_config.rx_bits_per_char = ePRU_SUART_DATA_BITS8; + pru_suart_config.oversampling = SUART_DEFAULT_OVRSMPL; + + if (pru_softuart_setconfig(dev, &soft_uart->suart_hdl[port->line], + &pru_suart_config) != 0) { + dev_err(&pdev->dev, + "pru_softuart_setconfig: failed to set config: %X\n", + err); + } +exit: + return err; +} + +/* + * Perform any autoconfiguration steps required for the port. `flag` + * contains a bit mask of the required configuration. UART_CONFIG_TYPE + * indicates that the port requires detection and identification. + * port->type should be set to the type found, or PORT_UNKNOWN if + * no port was detected. + * + * UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal, + * which should be probed using standard kernel autoprobing techniques. + * This is not necessary on platforms where ports have interrupts + * internally hard wired (eg, system on a chip implementations). + * + * Locking: none. + * Interrupts: caller dependent. + */ + +static void pruss_suart_config_port(struct uart_port *port, s32 flags) +{ + if (flags & UART_CONFIG_TYPE && pruss_suart_request_port(port) == 0) + port->type = PORT_DA8XX_PRU_SUART; +} + +/* + * Verify the new serial port information contained within serinfo is + * suitable for this port type. + * + * Locking: none. + * Interrupts: caller dependent. + */ +static s32 pruss_suart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + s32 ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_DA8XX_PRU_SUART) + ret = -EINVAL; + if (soft_uart->port[port->line].irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != UPIO_MEM) + ret = -EINVAL; + if (soft_uart->port[port->line].uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)soft_uart->port[port->line].mapbase != ser->iomem_base) + ret = -EINVAL; + if (soft_uart->port[port->line].iobase != ser->port) + ret = -EINVAL; + return ret; +} + +static struct uart_ops pruss_suart_ops = { + .tx_empty = pruss_suart_tx_empty, + .set_mctrl = pruss_suart_set_mctrl, + .get_mctrl = pruss_suart_get_mctrl, + .stop_tx = pruss_suart_stop_tx, + .start_tx = pruss_suart_start_tx, + .stop_rx = pruss_suart_stop_rx, + .enable_ms = pruss_suart_enable_ms, + .break_ctl = pruss_suart_break_ctl, + .startup = pruss_suart_startup, + .shutdown = pruss_suart_shutdown, + .set_termios = pruss_suart_set_termios, + .type = pruss_suart_type, + .release_port = pruss_suart_release_port, + .request_port = pruss_suart_request_port, + .config_port = pruss_suart_config_port, + .verify_port = pruss_suart_verify_port, +}; + +static struct uart_driver pruss_suart_reg = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttySU", + .major = 0, + .minor = 16, + .nr = NR_SUART, +}; + +static struct pruss_suart_initparams init_params = { + .tx_baud_value = SUART_DEFAULT_BAUD, + .rx_baud_value = SUART_DEFAULT_BAUD, + .oversampling = SUART_DEFAULT_OVRSMPL, +}; + +static s32 __devinit pruss_suart_probe(struct platform_device *pdev) +{ + struct omapl_pru_suart *soft_uart; + const struct da850_evm_pruss_suart_data *pdata; + struct device *dev = &pdev->dev; + struct resource *res; + struct clk *clk_pruss = NULL; + const struct firmware *fw; + s32 err, i; + + pdata = dev->platform_data; + if (!pdata) { + dev_err(&pdev->dev, "platform data not found\n"); + return -EINVAL; + } + (pdata->setup)(); + + soft_uart = kzalloc(sizeof(struct omapl_pru_suart), GFP_KERNEL); + if (!soft_uart) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get resource"); + return -ENOMEM; + } + + if (!request_mem_region(res->start, + resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "mcasp memory region already claimed!\n"); + err = -EBUSY; + goto probe_exit; + } + + soft_uart->suart_iomap.mcasp_io_addr = ioremap(res->start, + resource_size(res)); + if (!soft_uart->suart_iomap.mcasp_io_addr) { + dev_err(&pdev->dev, "mcasp ioremap failed\n"); + err = -EFAULT; + goto probe_exit_1; + } + + soft_uart->suart_iomap.p_fifo_buff_virt_base = + sram_alloc(SUART_CNTX_SZ * NR_SUART * 2, + (dma_addr_t *) &soft_uart->suart_iomap.p_fifo_buff_phys_base); + if (!soft_uart->suart_iomap.p_fifo_buff_virt_base) + goto probe_exit_iounmap; + + clk_pruss = clk_get(NULL, "pruss"); + if (IS_ERR(clk_pruss)) { + dev_err(&pdev->dev, "no clock available: pruss\n"); + err = -ENODEV; + goto probe_exit_iounmap; + } + soft_uart->clk_freq_pru = clk_get_rate(clk_pruss); + clk_put(clk_pruss); + + soft_uart->clk_mcasp = clk_get(&pdev->dev, NULL); + if (IS_ERR(soft_uart->clk_mcasp)) { + dev_err(&pdev->dev, "no clock available: mcasp\n"); + err = -ENODEV; + soft_uart->clk_mcasp = NULL; + goto probe_exit_sram_free; + } + + soft_uart->clk_freq_mcasp = clk_get_rate(soft_uart->clk_mcasp); + clk_enable(soft_uart->clk_mcasp); + + err = request_firmware(&fw, "PRU_SUART_Emulation.bin", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "can't load firmware\n"); + err = -ENODEV; + goto probe_exit_clk; + } + dev_info(&pdev->dev, "fw size %td. downloading...\n", fw->size); + + /* download firmware into pru & init */ + err = pru_softuart_init(dev, &init_params, fw->data, fw->size, + soft_uart->clk_freq_pru / 1000000, + &soft_uart->suart_iomap); + if (err) { + dev_err(&pdev->dev, "pruss init error\n"); + err = -ENODEV; + goto probe_release_fw; + } + release_firmware(fw); + + platform_set_drvdata(pdev, &soft_uart->port[0]); + soft_uart->dev = dev; + + for (i = 0; i < NR_SUART; i++) { + soft_uart->port[i].ops = &pruss_suart_ops; + soft_uart->port[i].iotype = UPIO_MEM; + soft_uart->port[i].flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; + soft_uart->port[i].mapbase = + (u32)soft_uart->suart_iomap.p_fifo_buff_virt_base; + soft_uart->port[i].membase = + soft_uart->suart_iomap.mcasp_io_addr; + soft_uart->port[i].type = PORT_DA8XX_PRU_SUART; + soft_uart->port[i].irq = + platform_get_irq(to_platform_device(dev->parent), i); + soft_uart->port[i].dev = &pdev->dev; + soft_uart->port[i].irqflags = IRQF_SHARED; + soft_uart->port[i].uartclk = soft_uart->clk_freq_mcasp; + soft_uart->port[i].fifosize = SUART_FIFO_LEN; + soft_uart->tx_loadsz = SUART_FIFO_LEN; + soft_uart->port[i].custom_divisor = 1; + soft_uart->port[i].line = i; + soft_uart->suart_hdl[i].uart_num = i + 1; + soft_uart->port[i].serial_in = NULL; + + soft_uart->suart_fifo_addr[i].fifo_vaddr_buff_tx = + soft_uart->suart_iomap.p_fifo_buff_virt_base + + (2 * SUART_CNTX_SZ * i); + + soft_uart->suart_fifo_addr[i].fifo_vaddr_buff_rx = + soft_uart->suart_iomap.p_fifo_buff_virt_base + + ((2 * SUART_CNTX_SZ * i) + SUART_CNTX_SZ); + + soft_uart->suart_fifo_addr[i].fifo_phys_addr_tx = + soft_uart->suart_iomap.p_fifo_buff_phys_base + + (2 * SUART_CNTX_SZ * i); + + soft_uart->suart_fifo_addr[i].fifo_phys_addr_rx = + soft_uart->suart_iomap.p_fifo_buff_phys_base + + ((2 * SUART_CNTX_SZ * i) + SUART_CNTX_SZ); + + soft_uart->port[i].serial_out = NULL; + tasklet_init(&soft_uart->tx_task[i], suart_tx_task, + (unsigned long)&soft_uart->port[i]); + uart_add_one_port(&pruss_suart_reg, &soft_uart->port[i]); + } + + dev_info(&pdev->dev, + "%s device registered (pru_clk=%d, asp_clk=%d)\n", + DRV_NAME, soft_uart->clk_freq_pru, soft_uart->clk_freq_mcasp); + + return 0; + +probe_release_fw: + release_firmware(fw); +probe_exit_clk: + clk_put(soft_uart->clk_mcasp); + clk_disable(soft_uart->clk_mcasp); +probe_exit_sram_free: + sram_free(soft_uart->suart_iomap.p_fifo_buff_virt_base, + SUART_CNTX_SZ * NR_SUART * 2); +probe_exit_iounmap: + iounmap(soft_uart->suart_iomap.mcasp_io_addr); +probe_exit_1: + release_mem_region(res->start, + resource_size(res)); +probe_exit: + kfree(soft_uart); + return err; +} + +static s32 __devexit pruss_suart_remove(struct platform_device *pdev) +{ + struct omapl_pru_suart *soft_uart = platform_get_drvdata(pdev); + const struct da850_evm_pruss_suart_data *pdata; + struct device *dev = &pdev->dev; + struct resource *res; + int i; + + pdata = dev->platform_data; + if (!pdata) + dev_err(&pdev->dev, "platform data not found\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get resource"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, NULL); + + if (soft_uart) { + for (i = 0; i < NR_SUART; i++) { + uart_remove_one_port(&pruss_suart_reg, + &soft_uart->port[i]); + } + } + + sram_free(soft_uart->suart_iomap.p_fifo_buff_virt_base, + SUART_CNTX_SZ * NR_SUART * 2); + clk_put(soft_uart->clk_mcasp); + pru_mcasp_deinit(); + clk_disable(soft_uart->clk_mcasp); + iounmap(soft_uart->suart_iomap.mcasp_io_addr); + if (pdata) { + release_mem_region(res->start, + resource_size(res)); + } + kfree(soft_uart); + return 0; +} + +#define pruss_suart_suspend NULL +#define pruss_suart_resume NULL + +static struct platform_driver serial_pruss_driver = { + .probe = pruss_suart_probe, + .remove = __devexit_p(pruss_suart_remove), + .suspend = pruss_suart_suspend, + .resume = pruss_suart_resume, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static s32 __init pruss_suart_init(void) +{ + s32 ret; + + pruss_suart_reg.nr = NR_SUART; + ret = uart_register_driver(&pruss_suart_reg); + if (ret) + return ret; + ret = platform_driver_register(&serial_pruss_driver); + if (ret) + goto out; + + pr_debug("SUART serial driver loaded\n"); + return ret; +out: + uart_unregister_driver(&pruss_suart_reg); + return ret; +} + +module_init(pruss_suart_init); + +static void __exit pruss_suart_exit(void) +{ + platform_driver_unregister(&serial_pruss_driver); + uart_unregister_driver(&pruss_suart_reg); + pr_debug("SUART serial driver unloaded\n"); +} + +module_exit(pruss_suart_exit); + +/* Module information */ +MODULE_AUTHOR("Subhasish Ghosh "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRV_DESC); diff --git a/drivers/tty/serial/pruss_suart.h b/drivers/tty/serial/pruss_suart.h new file mode 100644 index 0000000..f3a2a9d --- /dev/null +++ b/drivers/tty/serial/pruss_suart.h @@ -0,0 +1,1038 @@ +/* + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 _SUART_API_H_ +#define _SUART_API_H_ + +#include +#include +#include +#include + +#define SINGLE_PRU 0 +#define BOTH_PRU 1 +#define PRU_ACTIVE BOTH_PRU +#define PRU_CLK_228 228 +#define PRU_CLK_186 186 + +#define PRU_SUART_SERIALIZER_0 (0u) +#define PRU_SUART_SERIALIZER_1 (1u) +#define PRU_SUART_SERIALIZER_2 (2u) +#define PRU_SUART_SERIALIZER_3 (3u) +#define PRU_SUART_SERIALIZER_4 (4u) +#define PRU_SUART_SERIALIZER_5 (5u) +#define PRU_SUART_SERIALIZER_6 (6u) +#define PRU_SUART_SERIALIZER_7 (7u) +#define PRU_SUART_SERIALIZER_8 (8u) +#define PRU_SUART_SERIALIZER_9 (9u) +#define PRU_SUART_SERIALIZER_10 (10u) +#define PRU_SUART_SERIALIZER_11 (11u) +#define PRU_SUART_SERIALIZER_12 (12u) +#define PRU_SUART_SERIALIZER_13 (13u) +#define PRU_SUART_SERIALIZER_14 (14u) +#define PRU_SUART_SERIALIZER_15 (15u) +#define PRU_SUART_SERIALIZER_NONE (16u) + +#define PRU_SUART_UART1 (1u) +#define PRU_SUART_UART2 (2u) +#define PRU_SUART_UART3 (3u) +#define PRU_SUART_UART4 (4u) +#define PRU_SUART_UART5 (5u) +#define PRU_SUART_UART6 (6u) +#define PRU_SUART_UART7 (7u) +#define PRU_SUART_UART8 (8u) +#define PRU_SUART_UARTx_INVALID (9u) + +#define PRU_SUART_HALF_TX (1u) +#define PRU_SUART_HALF_RX (2u) +#define PRU_SUART_HALF_TX_DISABLED (4u) +#define PRU_SUART_HALF_RX_DISABLED (8u) + +#define PRU_SUART0_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART0_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART0_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART1_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ + PRU_SUART_HALF_RX) +#define PRU_SUART1_CONFIG_RX_SER (PRU_SUART_SERIALIZER_7) +#define PRU_SUART1_CONFIG_TX_SER (PRU_SUART_SERIALIZER_8) + +#define PRU_SUART2_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ + PRU_SUART_HALF_RX) +#define PRU_SUART2_CONFIG_RX_SER (PRU_SUART_SERIALIZER_9) +#define PRU_SUART2_CONFIG_TX_SER (PRU_SUART_SERIALIZER_10) + +#define PRU_SUART3_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ + PRU_SUART_HALF_RX) +#define PRU_SUART3_CONFIG_RX_SER (PRU_SUART_SERIALIZER_13) +#define PRU_SUART3_CONFIG_TX_SER (PRU_SUART_SERIALIZER_14) + +#define PRU_SUART4_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART4_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART4_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART5_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART5_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART5_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART6_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART6_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART6_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART7_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART7_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART7_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define SUART_NUM_OF_CHANNELS_PER_SUART 2 +#define SUART_NUM_OF_BYTES_PER_CHANNEL 16 + +#define PRU_TX_INTR 1 +#define PRU_RX_INTR 2 + +#define CHN_TXRX_STATUS_TIMEOUT BIT(6) +#define CHN_TXRX_STATUS_BI BIT(5) +#define CHN_TXRX_STATUS_FE BIT(4) +#define CHN_TXRX_STATUS_UNERR BIT(3) +#define CHN_TXRX_STATUS_OVRNERR BIT(3) +#define CHN_TXRX_STATUS_ERR BIT(2) +#define CHN_TXRX_STATUS_CMPLT BIT(1) +#define CHN_TXRX_STATUS_RDY BIT(0) + +#define CHN_TXRX_IE_MASK_TIMEOUT BIT(14) +#define CHN_TXRX_IE_MASK_BI BIT(13) +#define CHN_TXRX_IE_MASK_FE BIT(12) +#define CHN_TXRX_IE_MASK_CMPLT BIT(1) + +#define SUART_GBL_INTR_ERR_MASK BIT(9) +#define SUART_PRU_ID_MASK 0xFF + +#define SUART_FIFO_LEN 15 +#define SUART_8X_OVRSMPL 1 +#define SUART_16X_OVRSMPL 2 +#define SUART_TX_OVRSMPL 0 +#define SUART_DEFAULT_OVRSMPL SUART_8X_OVRSMPL + +#define SUART_DEFAULT_OVRSMPL_OFFSET 26 +#define SUART_CHN_OFFSET 31 +#define SERIALIZER_OFFSET 8 + +#if (SUART_DEFAULT_OVRSMPL == SUART_16X_OVRSMPL) +#define SUART_DEFAULT_BAUD 57600 +#else +#define SUART_DEFAULT_BAUD 115200 +#endif + +#define PRU_MODE_INVALID 0x0 +#define PRU_MODE_TX_ONLY 0x1 +#define PRU_MODE_RX_ONLY 0x2 +#define PRU_MODE_RX_TX_BOTH 0x3 + +#if (PRU_ACTIVE == BOTH_PRU) +#define PRU0_MODE PRU_MODE_RX_ONLY +#define PRU1_MODE PRU_MODE_TX_ONLY +#elif (PRU_ACTIVE == SINGLE_PRU) +#define PRU0_MODE PRU_MODE_RX_TX_BOTH +#define PRU1_MODE PRU_MODE_INVALID +#else +#define PRU0_MODE PRU_MODE_INVALID +#define PRU1_MODE PRU_MODE_INVALID +#endif + +#define MCASP_XBUF_BASE_ADDR (0x01d00200) +#define MCASP_RBUF_BASE_ADDR (0x01d00280) +#define MCASP_SRCTL_BASE_ADDR (0x01d00180) + +#define MCASP_SRCTL_TX_MODE (0x000D) +#define MCASP_SRCTL_RX_MODE (0x000E) + +/* Since only PRU0 can work as RX */ +#define RX_DEFAULT_DATA_DUMP_ADDR (0x00001FC) +#define PRU_NUM_OF_CHANNELS (16) + +/* MCASP */ + +#define OMAPL_MCASP_PFUNC_AFSR_MASK (0x80000000u) +#define OMAPL_MCASP_PFUNC_AFSR_SHIFT (0x0000001Fu) +#define OMAPL_MCASP_PFUNC_AFSR_RESETVAL (0x00000000u) +/* AFSR Tokens */ +#define OMAPL_MCASP_PFUNC_AFSR_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AFSR_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AHCLKR_MASK (0x40000000u) +#define OMAPL_MCASP_PFUNC_AHCLKR_SHIFT (0x0000001Eu) +#define OMAPL_MCASP_PFUNC_AHCLKR_RESETVAL (0x00000000u) +/* AHCLKR Tokens */ +#define OMAPL_MCASP_PFUNC_AHCLKR_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AHCLKR_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_ACLKR_MASK (0x20000000u) +#define OMAPL_MCASP_PFUNC_ACLKR_SHIFT (0x0000001Du) +#define OMAPL_MCASP_PFUNC_ACLKR_RESETVAL (0x00000000u) +/* ACLKR Tokens */ +#define OMAPL_MCASP_PFUNC_ACLKR_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_ACLKR_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AFSX_MASK (0x10000000u) +#define OMAPL_MCASP_PFUNC_AFSX_SHIFT (0x0000001Cu) +#define OMAPL_MCASP_PFUNC_AFSX_RESETVAL (0x00000000u) +/* AFSX Tokens */ +#define OMAPL_MCASP_PFUNC_AFSX_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AFSX_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AHCLKX_MASK (0x08000000u) +#define OMAPL_MCASP_PFUNC_AHCLKX_SHIFT (0x0000001Bu) +#define OMAPL_MCASP_PFUNC_AHCLKX_RESETVAL (0x00000000u) +/* AHCLKX Tokens */ +#define OMAPL_MCASP_PFUNC_AHCLKX_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AHCLKX_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_ACLKX_MASK (0x04000000u) +#define OMAPL_MCASP_PFUNC_ACLKX_SHIFT (0x0000001Au) +#define OMAPL_MCASP_PFUNC_ACLKX_RESETVAL (0x00000000u) +/* ACLKX Tokens */ +#define OMAPL_MCASP_PFUNC_ACLKX_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_ACLKX_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AMUTE_MASK (0x02000000u) +#define OMAPL_MCASP_PFUNC_AMUTE_SHIFT (0x00000019u) +#define OMAPL_MCASP_PFUNC_AMUTE_RESETVAL (0x00000000u) +/* AMUTE Tokens */ +#define OMAPL_MCASP_PFUNC_AMUTE_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AMUTE_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR15_MASK (0x00008000u) +#define OMAPL_MCASP_PFUNC_AXR15_SHIFT (0x0000000Fu) +#define OMAPL_MCASP_PFUNC_AXR15_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR15_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR15_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR14_MASK (0x00004000u) +#define OMAPL_MCASP_PFUNC_AXR14_SHIFT (0x0000000Eu) +#define OMAPL_MCASP_PFUNC_AXR14_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR14_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR14_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR13_MASK (0x00002000u) +#define OMAPL_MCASP_PFUNC_AXR13_SHIFT (0x0000000Du) +#define OMAPL_MCASP_PFUNC_AXR13_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR13_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR13_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR12_MASK (0x00001000u) +#define OMAPL_MCASP_PFUNC_AXR12_SHIFT (0x0000000Cu) +#define OMAPL_MCASP_PFUNC_AXR12_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR12_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR12_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR11_MASK (0x00000800u) +#define OMAPL_MCASP_PFUNC_AXR11_SHIFT (0x0000000Bu) +#define OMAPL_MCASP_PFUNC_AXR11_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR11_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR11_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR10_MASK (0x00000400u) +#define OMAPL_MCASP_PFUNC_AXR10_SHIFT (0x0000000Au) +#define OMAPL_MCASP_PFUNC_AXR10_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR10_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR10_GPIO (0x00000001u) +#define OMAPL_MCASP_PFUNC_AXR9_MASK (0x00000200u) +#define OMAPL_MCASP_PFUNC_AXR9_SHIFT (0x00000009u) +#define OMAPL_MCASP_PFUNC_AXR9_RESETVAL (0x00000000u) +/* AXR9 Token */ +#define OMAPL_MCASP_PFUNC_AXR9_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR9_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR8_MASK (0x00000100u) +#define OMAPL_MCASP_PFUNC_AXR8_SHIFT (0x00000008u) +#define OMAPL_MCASP_PFUNC_AXR8_RESETVAL (0x00000000u) +/* AXR8 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR8_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR8_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR7_MASK (0x00000080u) +#define OMAPL_MCASP_PFUNC_AXR7_SHIFT (0x00000007u) +#define OMAPL_MCASP_PFUNC_AXR7_RESETVAL (0x00000000u) +/* AXR7 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR7_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR7_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR6_MASK (0x00000040u) +#define OMAPL_MCASP_PFUNC_AXR6_SHIFT (0x00000006u) +#define OMAPL_MCASP_PFUNC_AXR6_RESETVAL (0x00000000u) +/* AXR6 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR6_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR6_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR5_MASK (0x00000020u) +#define OMAPL_MCASP_PFUNC_AXR5_SHIFT (0x00000005u) +#define OMAPL_MCASP_PFUNC_AXR5_RESETVAL (0x00000000u) +/* AXR5 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR5_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR5_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR4_MASK (0x00000010u) +#define OMAPL_MCASP_PFUNC_AXR4_SHIFT (0x00000004u) +#define OMAPL_MCASP_PFUNC_AXR4_RESETVAL (0x00000000u) +/* AXR4 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR4_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR4_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR3_MASK (0x00000008u) +#define OMAPL_MCASP_PFUNC_AXR3_SHIFT (0x00000003u) +#define OMAPL_MCASP_PFUNC_AXR3_RESETVAL (0x00000000u) +/* AXR3 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR3_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR3_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR2_MASK (0x00000004u) +#define OMAPL_MCASP_PFUNC_AXR2_SHIFT (0x00000002u) +#define OMAPL_MCASP_PFUNC_AXR2_RESETVAL (0x00000000u) +/* AXR2 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR2_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR2_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR1_MASK (0x00000002u) +#define OMAPL_MCASP_PFUNC_AXR1_SHIFT (0x00000001u) +#define OMAPL_MCASP_PFUNC_AXR1_RESETVAL (0x00000000u) +/* AXR1 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR1_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR1_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR0_MASK (0x00000001u) +#define OMAPL_MCASP_PFUNC_AXR0_SHIFT (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR0_RESETVAL (0x00000000u) +/* AXR0 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR0_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR0_GPIO (0x00000001u) +#define OMAPL_MCASP_PFUNC_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_PDIR_AFSR_MASK (0x80000000u) +#define OMAPL_MCASP_PDIR_AFSR_SHIFT (0x0000001Fu) +#define OMAPL_MCASP_PDIR_AFSR_RESETVAL (0x00000000u) +/* AFSR Tokens */ +#define OMAPL_MCASP_PDIR_AFSR_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AFSR_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AHCLKR_MASK (0x40000000u) +#define OMAPL_MCASP_PDIR_AHCLKR_SHIFT (0x0000001Eu) +#define OMAPL_MCASP_PDIR_AHCLKR_RESETVAL (0x00000000u) +/* AHCLKR Tokens */ +#define OMAPL_MCASP_PDIR_AHCLKR_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AHCLKR_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_ACLKR_MASK (0x20000000u) +#define OMAPL_MCASP_PDIR_ACLKR_SHIFT (0x0000001Du) +#define OMAPL_MCASP_PDIR_ACLKR_RESETVAL (0x00000000u) +/* ACLKR Tokens */ +#define OMAPL_MCASP_PDIR_ACLKR_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_ACLKR_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AFSX_MASK (0x10000000u) +#define OMAPL_MCASP_PDIR_AFSX_SHIFT (0x0000001Cu) +#define OMAPL_MCASP_PDIR_AFSX_RESETVAL (0x00000000u) +/* AFSX Tokens */ +#define OMAPL_MCASP_PDIR_AFSX_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AFSX_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AHCLKX_MASK (0x08000000u) +#define OMAPL_MCASP_PDIR_AHCLKX_SHIFT (0x0000001Bu) +#define OMAPL_MCASP_PDIR_AHCLKX_RESETVAL (0x00000000u) +/* AHCLKX Tokens */ +#define OMAPL_MCASP_PDIR_AHCLKX_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AHCLKX_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_ACLKX_MASK (0x04000000u) +#define OMAPL_MCASP_PDIR_ACLKX_SHIFT (0x0000001Au) +#define OMAPL_MCASP_PDIR_ACLKX_RESETVAL (0x00000000u) +/* ACLKX Tokens */ +#define OMAPL_MCASP_PDIR_ACLKX_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_ACLKX_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AMUTE_MASK (0x02000000u) +#define OMAPL_MCASP_PDIR_AMUTE_SHIFT (0x00000019u) +#define OMAPL_MCASP_PDIR_AMUTE_RESETVAL (0x00000000u) +/* AMUTE Tokens */ +#define OMAPL_MCASP_PDIR_AMUTE_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AMUTE_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR15_MASK (0x00008000u) +#define OMAPL_MCASP_PDIR_AXR15_SHIFT (0x0000000Fu) +#define OMAPL_MCASP_PDIR_AXR15_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR15_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR15_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR14_MASK (0x00004000u) +#define OMAPL_MCASP_PDIR_AXR14_SHIFT (0x0000000Eu) +#define OMAPL_MCASP_PDIR_AXR14_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR14_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR14_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR13_MASK (0x00002000u) +#define OMAPL_MCASP_PDIR_AXR13_SHIFT (0x0000000Du) +#define OMAPL_MCASP_PDIR_AXR13_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR13_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR13_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR12_MASK (0x00001000u) +#define OMAPL_MCASP_PDIR_AXR12_SHIFT (0x0000000Cu) +#define OMAPL_MCASP_PDIR_AXR12_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR12_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR12_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR11_MASK (0x00000800u) +#define OMAPL_MCASP_PDIR_AXR11_SHIFT (0x0000000Bu) +#define OMAPL_MCASP_PDIR_AXR11_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR11_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR11_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR10_MASK (0x00000400u) +#define OMAPL_MCASP_PDIR_AXR10_SHIFT (0x0000000Au) +#define OMAPL_MCASP_PDIR_AXR10_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR10_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR10_OUTPUT (0x00000001u) +#define OMAPL_MCASP_PDIR_AXR9_MASK (0x00000200u) +#define OMAPL_MCASP_PDIR_AXR9_SHIFT (0x00000009u) +#define OMAPL_MCASP_PDIR_AXR9_RESETVAL (0x00000000u) +/* AXR9 Tokens */ +#define OMAPL_MCASP_PDIR_AXR9_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR9_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR8_MASK (0x00000100u) +#define OMAPL_MCASP_PDIR_AXR8_SHIFT (0x00000008u) +#define OMAPL_MCASP_PDIR_AXR8_RESETVAL (0x00000000u) +/* AXR8 Tokens */ +#define OMAPL_MCASP_PDIR_AXR8_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR8_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR7_MASK (0x00000080u) +#define OMAPL_MCASP_PDIR_AXR7_SHIFT (0x00000007u) +#define OMAPL_MCASP_PDIR_AXR7_RESETVAL (0x00000000u) +/*----AXR7 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR7_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR7_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR6_MASK (0x00000040u) +#define OMAPL_MCASP_PDIR_AXR6_SHIFT (0x00000006u) +#define OMAPL_MCASP_PDIR_AXR6_RESETVAL (0x00000000u) +/*----AXR6 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR6_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR6_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR5_MASK (0x00000020u) +#define OMAPL_MCASP_PDIR_AXR5_SHIFT (0x00000005u) +#define OMAPL_MCASP_PDIR_AXR5_RESETVAL (0x00000000u) +/*----AXR5 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR5_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR5_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR4_MASK (0x00000010u) +#define OMAPL_MCASP_PDIR_AXR4_SHIFT (0x00000004u) +#define OMAPL_MCASP_PDIR_AXR4_RESETVAL (0x00000000u) +/*----AXR4 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR4_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR4_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR3_MASK (0x00000008u) +#define OMAPL_MCASP_PDIR_AXR3_SHIFT (0x00000003u) +#define OMAPL_MCASP_PDIR_AXR3_RESETVAL (0x00000000u) +/*----AXR3 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR3_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR3_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR2_MASK (0x00000004u) +#define OMAPL_MCASP_PDIR_AXR2_SHIFT (0x00000002u) +#define OMAPL_MCASP_PDIR_AXR2_RESETVAL (0x00000000u) +/*----AXR2 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR2_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR2_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR1_MASK (0x00000002u) +#define OMAPL_MCASP_PDIR_AXR1_SHIFT (0x00000001u) +#define OMAPL_MCASP_PDIR_AXR1_RESETVAL (0x00000000u) +/*----AXR1 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR1_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR1_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR0_MASK (0x00000001u) +#define OMAPL_MCASP_PDIR_AXR0_SHIFT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR0_RESETVAL (0x00000000u) +/*----AXR0 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR0_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR0_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_ACLKXCTL_CLKXP_MASK (0x00000080u) +#define OMAPL_MCASP_ACLKXCTL_CLKXP_SHIFT (0x00000007u) +#define OMAPL_MCASP_ACLKXCTL_CLKXP_RESETVAL (0x00000000u) +/*----CLKXP Tokens----*/ +#define OMAPL_MCASP_ACLKXCTL_CLKXP_RISINGEDGE (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_CLKXP_FALLINGEDGE (0x00000001u) + +#define OMAPL_MCASP_ACLKXCTL_ASYNC_MASK (0x00000040u) +#define OMAPL_MCASP_ACLKXCTL_ASYNC_SHIFT (0x00000006u) +#define OMAPL_MCASP_ACLKXCTL_ASYNC_RESETVAL (0x00000001u) +/*----ASYNC Tokens----*/ +#define OMAPL_MCASP_ACLKXCTL_ASYNC_SYNC (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_ASYNC_ASYNC (0x00000001u) + +#define OMAPL_MCASP_ACLKXCTL_CLKXM_MASK (0x00000020u) +#define OMAPL_MCASP_ACLKXCTL_CLKXM_SHIFT (0x00000005u) +#define OMAPL_MCASP_ACLKXCTL_CLKXM_RESETVAL (0x00000001u) +/*----CLKXM Tokens----*/ +#define OMAPL_MCASP_ACLKXCTL_CLKXM_EXTERNAL (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_CLKXM_INTERNAL (0x00000001u) + +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_MASK (0x0000001Fu) +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_ACLKXCTL_RESETVAL (0x00000060u) + +/* AHCLKXCTL */ +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_MASK (0x00008000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_SHIFT (0x0000000Fu) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_RESETVAL (0x00000001u) +/*----HCLKXM Tokens----*/ +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_EXTERNAL (0x00000000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_INTERNAL (0x00000001u) + +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_MASK (0x00004000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_SHIFT (0x0000000Eu) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_RESETVAL (0x00000000u) +/*----HCLKXP Tokens----*/ +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_NOTINVERTED (0x00000000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_INVERTED (0x00000001u) + +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_MASK (0x00000FFFu) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT (0x00000000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_AHCLKXCTL_RESETVAL (0x00008000u) + +#define MCASP_SUART_GBLCTL (0X00000000) +#define MCASP_SUART_RGBLCTL (0X00000000) +#define MCASP_SUART_XGBLCTL (0X00000000) +#define MCASP_SUART_RMASK_8 (0x000000FF) +#define MCASP_SUART_RMASK_16 (0x0000FFFF) +#define MCASP_SUART_RFMT_8 (0x0000A038) +#define MCASP_SUART_RFMT_16 (0x0000A078) +#define MCASP_SUART_FSRM (0X00000002) +#define MCASP_SUART_CLKRM_CLKRP (0X000000A0) +#define MCASP_SUART_HCLKRP (0X00008000) +#define MCASP_SUART_RTDMS0 (0X00000001) +#define MCASP_SUART_RSYNCERR (0X00000002) +#define MCASP_SUART_RMAX_RPS_256 (0x00FF0008) +#define MCASP_SUART_XMASK_0_31 (0X0000FFFF) +#define MCASP_SUART_XBUSEL_XSSZ_16_XPAD_0 (0x00002078) +#define MCASP_SUART_FSXM (0x00000002) +#define MCASP_SUART_CLKXM_ASYNC_CLKXP (0x000000E0) +#define MCASP_SUART_HCLKXM (0x00008000) +#define MCASP_SUART_XTDMS0 (0X00000001) +#define MCASP_SUART_XSYNCERR (0x00000002) +#define MCASP_SUART_XMAX_XPS_256 (0x00FF0008) +#define MCASP_SUART_SRCTL_DISMOD (0x0000000c) +#define MCASP_SUART_DIT_DISABLE (0X00000000) +#define MCASP_SUART_LOOPBACK_DISABLE (0x00000000) +#define MCASP_SUART_AMUTE_DISABLE (0X00000000) +#define MCASP_SUART_XSTAT (0x0000FFFF) +#define MCASP_SUART_RSTAT (0x0000FFFF) + +/* SUART REGS */ + +/* PRU0 DATA RAM base address */ +#define PRU0_DATARAM_OFFSET (0x0000u) +/* PRU1 DATA RAM base address */ +#define PRU1_DATARAM_OFFSET (0x2000u) + +/* PRU0 DATA RAM size */ +#define PRU0_DATARAM_SIZE (0x200u) +/* PRU1 DATA RAM size */ +#define PRU1_DATARAM_SIZE (0x200u) + +#define PRU_SUART_PRU0_CH0_OFFSET (0x0000) +#define PRU_SUART_PRU0_CH1_OFFSET (0x0010) +#define PRU_SUART_PRU0_CH2_OFFSET (0x0020) +#define PRU_SUART_PRU0_CH3_OFFSET (0x0030) +#define PRU_SUART_PRU0_CH4_OFFSET (0x0040) +#define PRU_SUART_PRU0_CH5_OFFSET (0x0050) +#define PRU_SUART_PRU0_CH6_OFFSET (0x0060) +#define PRU_SUART_PRU0_CH7_OFFSET (0x0070) +#define PRU_SUART_PRU0_IMR_OFFSET (0x0080) +/* Interrupt Mask Register */ +#define PRU_SUART_PRU0_ISR_OFFSET (0x0082) +/* Interrupt Status Register */ +#define PRU_SUART_PRU0_ID_ADDR (0x0084) +/* PRU ID Register */ +#define PRU_SUART_PRU0_RX_TX_MODE (0x0085) +#define PRU_SUART_PRU0_DELAY_OFFSET (0x0086) +#define PRU_SUART_PRU0_IDLE_TIMEOUT_OFFSET (0x0088) + +/* PRU 1 Macros */ +#define PRU_SUART_PRU1_CH0_OFFSET (0x2000) +#define PRU_SUART_PRU1_CH1_OFFSET (0x2010) +#define PRU_SUART_PRU1_CH2_OFFSET (0x2020) +#define PRU_SUART_PRU1_CH3_OFFSET (0x2030) +#define PRU_SUART_PRU1_CH4_OFFSET (0x2040) +#define PRU_SUART_PRU1_CH5_OFFSET (0x2050) +#define PRU_SUART_PRU1_CH6_OFFSET (0x2060) +#define PRU_SUART_PRU1_CH7_OFFSET (0x2070) +#define PRU_SUART_PRU1_IMR_OFFSET (0x2080) +#define PRU_SUART_PRU1_ISR_OFFSET (0x2082) +#define PRU_SUART_PRU1_ID_ADDR (0x2084) +#define PRU_SUART_PRU1_RX_TX_MODE (0x2085) +#define PRU_SUART_PRU1_DELAY_OFFSET (0x2086) +#define PRU_SUART_PRU1_IDLE_TIMEOUT_OFFSET (0x2088) + +/* SUART Channel Control Register bit descriptions */ +#define PRU_SUART_CH_CTRL_MODE_SHIFT 0x0000 +#define PRU_SUART_CH_CTRL_MODE_MASK 0x0003 +#define PRU_SUART_CH_CTRL_TX_MODE 0x0001 +#define PRU_SUART_CH_CTRL_RX_MODE 0x0002 + +/* Service Request */ +#define PRU_SUART_CH_CTRL_SREQ_SHIFT 0x0002 +#define PRU_SUART_CH_CTRL_SREQ_MASK 0x0004 +#define PRU_SUART_CH_CTRL_SREQ 0x0001 + +/* McASP Instance */ +#define PRU_SUART_CH_CTRL_MCASP_SHIFT 0x0003 +#define PRU_SUART_CH_CTRL_MCASP_MASK 0x0018 +#define PRU_SUART_CH_CTRL_SR_SHIFT 0x0008 +#define PRU_SUART_CH_CTRL_SR_MASK 0x0F00 + +/* SUART channel configuration1 register descriptions */ + +/* clock divisor - relative baud value */ +#define PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT 0x0000 +#define PRU_SUART_CH_CONFIG1_DIVISOR_MASK 0x03FF +/* oversampling */ +#define PRU_SUART_CH_CONFIG1_OVS_SHIFT 0x000A +#define PRU_SUART_CH_CONFIG1_OVS_MASK 0x0C00 + +/* SUART channel configuration2 register descriptions */ +/* Bits per character */ +#define PRU_SUART_CH_CONFIG2_BITPERCHAR_SHIFT 0x0000 +#define PRU_SUART_CH_CONFIG2_BITPERCHAR_MASK 0x000F + +/* Bits per character */ +#define PRU_SUART_CH_CONFIG2_DATALEN_SHIFT 0x0008 +#define PRU_SUART_CH_CONFIG2_DATALEN_MASK 0x0F00 + +/* SUART Channel STATUS Register*/ +#define PRU_SUART_CH_STATUS_EN_BIT_MASK 0x8000 + +/* SUART Channel register offsets */ +#define PRU_SUART_CH_CTRL_OFFSET 0x00 +#define PRU_SUART_CH_CONFIG1_OFFSET 0x02 +#define PRU_SUART_CH_CONFIG2_OFFSET 0x04 +#define PRU_SUART_CH_TXRXSTATUS_OFFSET 0x06 +#define PRU_SUART_CH_TXRXDATA_OFFSET 0x08 +#define PRU_SUART_CH_BYTESDONECNTR_OFFSET 0x0C + +/* SUART Event Numbers macros */ +#define PRU_SUART0_TX_EVT 34 +#define PRU_SUART0_RX_EVT 35 +#define PRU_SUART1_TX_EVT 36 +#define PRU_SUART1_RX_EVT 37 +#define PRU_SUART2_TX_EVT 38 +#define PRU_SUART2_RX_EVT 39 +#define PRU_SUART3_TX_EVT 40 +#define PRU_SUART3_RX_EVT 41 +#define PRU_SUART4_TX_EVT 42 +#define PRU_SUART4_RX_EVT 43 +#define PRU_SUART5_TX_EVT 44 +#define PRU_SUART5_RX_EVT 45 +#define PRU_SUART6_TX_EVT 46 +#define PRU_SUART6_RX_EVT 47 +#define PRU_SUART7_TX_EVT 48 +#define PRU_SUART7_RX_EVT 49 + +#define PRU_SUART0_TX_EVT_BIT BIT(2) +#define PRU_SUART0_RX_EVT_BIT BIT(3) +#define PRU_SUART1_TX_EVT_BIT BIT(4) +#define PRU_SUART1_RX_EVT_BIT BIT(5) +#define PRU_SUART2_TX_EVT_BIT BIT(6) +#define PRU_SUART2_RX_EVT_BIT BIT(7) +#define PRU_SUART3_TX_EVT_BIT BIT(8) +#define PRU_SUART3_RX_EVT_BIT BIT(9) +#define PRU_SUART4_TX_EVT_BIT BIT(10) +#define PRU_SUART4_RX_EVT_BIT BIT(11) +#define PRU_SUART5_TX_EVT_BIT BIT(12) +#define PRU_SUART5_RX_EVT_BIT BIT(13) +#define PRU_SUART6_TX_EVT_BIT BIT(14) +#define PRU_SUART6_RX_EVT_BIT BIT(15) +#define PRU_SUART7_TX_EVT_BIT BIT(16) +#define PRU_SUART7_RX_EVT_BIT BIT(17) + +/* Total number of baud rates supported */ +#define SUART_NUM_OF_BAUDS_SUPPORTED 13 + +#define MCASP_PDIR_VAL ( \ + OMAPL_MCASP_PDIR_AFSR_OUTPUT< + * + * 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 +#include +#include "pruss_suart.h" + +static u8 uart_statu_table[8]; +static struct pruss_suart_iomap suart_iomap; + +static u32 uart_rx[8] = {PRU_SUART0_CONFIG_RX_SER, PRU_SUART1_CONFIG_RX_SER, + PRU_SUART2_CONFIG_RX_SER, PRU_SUART3_CONFIG_RX_SER, + PRU_SUART4_CONFIG_RX_SER, PRU_SUART5_CONFIG_RX_SER, + PRU_SUART6_CONFIG_RX_SER, PRU_SUART7_CONFIG_RX_SER}; + +static u32 uart_tx[8] = {PRU_SUART0_CONFIG_TX_SER, PRU_SUART1_CONFIG_TX_SER, + PRU_SUART2_CONFIG_TX_SER, PRU_SUART3_CONFIG_TX_SER, + PRU_SUART4_CONFIG_TX_SER, PRU_SUART5_CONFIG_TX_SER, + PRU_SUART6_CONFIG_TX_SER, PRU_SUART7_CONFIG_TX_SER}; + +static u32 uart_config[8] = {PRU_SUART0_CONFIG_DUPLEX, PRU_SUART1_CONFIG_DUPLEX, + PRU_SUART2_CONFIG_DUPLEX, PRU_SUART3_CONFIG_DUPLEX, + PRU_SUART4_CONFIG_DUPLEX, PRU_SUART5_CONFIG_DUPLEX, + PRU_SUART6_CONFIG_DUPLEX, PRU_SUART7_CONFIG_DUPLEX}; + +static s32 pru_softuart_clr_rx_fifo(struct device *dev, + struct suart_handle *h_uart); +static s32 arm_to_pru_intr_init(struct device *dev); + +#if (PRU_ACTIVE == BOTH_PRU) +static void pru_set_ram_data(struct device *dev, + struct pruss_suart_iomap *pruss_ioaddr) +{ + u32 datatowrite; + u32 i; + struct pru_suart_regs_ovly *pru_suart_regs = NULL; + u32 __iomem *p_sr_ctl_addr = (u32 __iomem *)(pruss_ioaddr-> + mcasp_io_addr + 0x180); + struct pru_suart_tx_cntx_priv *pru_suart_tx_priv = NULL; + struct pru_suart_rx_cntx_priv *pru_suart_rx_priv = NULL; + + /* RX PRU - 0 Chanel 0-7 context information */ + for (i = 0; i < 8; i++, pru_suart_regs++) { + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + 0x3, SUART_CHN_RX); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0xF << SERIALIZER_OFFSET), + ((0xF & uart_rx[i]) << SERIALIZER_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), + (SUART_DEFAULT_OVRSMPL << + SUART_DEFAULT_OVRSMPL_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_config2_txrx_status, + 0xF, 8); + if ((uart_config[i] & PRU_SUART_HALF_RX_DISABLED) == + PRU_SUART_HALF_RX_DISABLED) { + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); + } else { + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); + iowrite32(MCASP_SRCTL_RX_MODE, p_sr_ctl_addr + + uart_rx[i]); + } + /* + * RX is active by default, write the dummy received data at + * PRU RAM addr 0x1FC to avoid memory corruption. + */ + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_txrx_data, + 0xFFFF, RX_DEFAULT_DATA_DUMP_ADDR); + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, 0xFFFF, 0); + /* SUART1 RX context base addr */ + pru_suart_rx_priv = (struct pru_suart_rx_cntx_priv *) + (PRU0_DATARAM_OFFSET + (0x090 + (i * 0x020))); + datatowrite = (MCASP_RBUF_BASE_ADDR + (uart_rx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rbuf_base, + datatowrite); + datatowrite = (MCASP_SRCTL_BASE_ADDR + (uart_rx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rsrctl_base, + datatowrite); + } + + /* PRU1 RAM BASE ADDR */ + pru_suart_regs = (struct pru_suart_regs_ovly *) PRU1_DATARAM_OFFSET; + + /* TX PRU - 1 */ + /* Channel 0-7 context information */ + for (i = 0; i < 8; i++, pru_suart_regs++) { + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + 0x3, SUART_CHN_TX); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0xF << SERIALIZER_OFFSET), + ((0xF & uart_tx[i]) << SERIALIZER_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), + (SUART_DEFAULT_OVRSMPL << + SUART_DEFAULT_OVRSMPL_OFFSET)); + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, 0xF, 8); + + if ((uart_config[i] & PRU_SUART_HALF_TX_DISABLED) == + PRU_SUART_HALF_TX_DISABLED) { + pruss_rmwl(dev, (u32) + &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); + } else { + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); + iowrite32(MCASP_SRCTL_TX_MODE, + p_sr_ctl_addr + uart_tx[i]); + } + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, 0xFFFF, 1); + + /* SUART1 TX context base addr */ + pru_suart_tx_priv = (struct pru_suart_tx_cntx_priv *) + (PRU1_DATARAM_OFFSET + (0x0B0 + (i * 0x02C))); + datatowrite = (MCASP_SRCTL_BASE_ADDR + (uart_tx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xsrctl_base, + datatowrite); + datatowrite = (MCASP_XBUF_BASE_ADDR + (uart_tx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xbuf_base, + datatowrite); + /* SUART1 TX formatted data base addr */ + datatowrite = (0x0090 + (i * 0x002C)); + pruss_writel(dev, (u32) &pru_suart_tx_priv->buff_addr, + datatowrite); + } +} +#else +static void pru_set_ram_data(struct device *dev, + struct pruss_suart_iomap *pruss_ioaddr) +{ + + struct pru_suart_regs_ovly *pru_suart_regs = + (struct pru_suart_regs_ovly *)pruss_ioaddr->pru_io_addr; + u32 i; + u32 *p_sr_ctl_addr = (u32 *)(pruss_ioaddr->mcasp_io_addr + 0x180); + struct pru_suart_tx_cntx_priv *pru_suart_tx_priv = NULL; + struct pru_suart_rx_cntx_priv *pru_suart_rx_priv = NULL; + + /* Channel 0 context information is Tx */ + for (i = 0; i < 4; i++, pru_suart_regs++) { + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + 0x3, SUART_CHN_TX); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0xF << SERIALIZER_OFFSET), + ((0xF & uart_tx[i]) << SERIALIZER_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), + (SUART_DEFAULT_OVRSMPL << + SUART_DEFAULT_OVRSMPL_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_config2_txrx_status, + 0xF, 8); + if ((uart_config[i] & PRU_SUART_HALF_TX_DISABLED) == + PRU_SUART_HALF_TX_DISABLED){ + pruss_rmwl(dev, (u32) + &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); + } else { + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); + iowrite32(MCASP_SRCTL_TX_MODE, + p_sr_ctl_addr + uart_tx[i]); + } + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, 0xFFFF, 1); + + /* SUART1 TX context base addr */ + pru_suart_tx_priv = (struct pru_suart_tx_cntx_priv *) + (PRU0_DATARAM_OFFSET + (0x0B0 + (i * 0x50))); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xsrctl_base, + (MCASP_SRCTL_BASE_ADDR + (uart_tx[i] << 2))); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xbuf_base, + (MCASP_XBUF_BASE_ADDR + (uart_tx[i] << 2))); + /* SUART1 TX formatted data base addr */ + pruss_writel(dev, (u32) &pru_suart_tx_priv->buff_addr, + (0x0090 + (i * 0x050))); + + /* Channel 1 is Rx context information */ + pru_suart_regs++; + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + 0x3, SUART_CHN_RX); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0xF << SERIALIZER_OFFSET), + ((0xF & uart_rx[i]) << SERIALIZER_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), + (SUART_DEFAULT_OVRSMPL << + SUART_DEFAULT_OVRSMPL_OFFSET)); + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, 0xF, 8); + + if ((uart_config[i] & PRU_SUART_HALF_RX_DISABLED) == + PRU_SUART_HALF_RX_DISABLED) { + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); + } else { + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); + iowrite32(MCASP_SRCTL_RX_MODE, + p_sr_ctl_addr + uart_rx[i]); + } + /* + * RX is active by default, write the dummy received data + * at PRU RAM addr 0x1FC to avoid memory corruption + */ + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_txrx_data, + 0xFFFF, RX_DEFAULT_DATA_DUMP_ADDR); + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, 0xFFFF, 0); + /* SUART1 RX context base addr */ + pru_suart_rx_priv = (struct pru_suart_rx_cntx_priv *) + (PRU0_DATARAM_OFFSET + (0x0C0 + (i * 0x50))); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rbuf_base, + (MCASP_RBUF_BASE_ADDR + (uart_rx[i] << 2))); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rsrctl_base, + (MCASP_SRCTL_BASE_ADDR + (uart_rx[i] << 2))); + } +} +#endif + +static void pru_set_rx_tx_mode(struct device *dev, u32 pru_mode, u32 pru_num) +{ + u32 pru_offset; + + if (pru_num == PRUSS_NUM0) + pru_offset = PRU_SUART_PRU0_RX_TX_MODE; + else if (pru_num == PRUSS_NUM1) + pru_offset = PRU_SUART_PRU1_RX_TX_MODE; + else + return; + pruss_writeb(dev, pru_offset, (u8) pru_mode); +} + +static void pru_set_delay_count(struct device *dev, u32 pru_freq) +{ + u32 delay_cnt; + + if (pru_freq == PRU_CLK_228) + delay_cnt = 5; + else if (pru_freq == PRU_CLK_186) + delay_cnt = 5; + else + delay_cnt = 3; + + /* PRU 0 */ + pruss_writeb(dev, PRU_SUART_PRU0_DELAY_OFFSET, + (u8) delay_cnt); + + /* PRU 1 */ + pruss_writeb(dev, PRU_SUART_PRU1_DELAY_OFFSET, + (u8) delay_cnt); +} + +static s32 suart_set_pru_id(struct device *dev, u32 pru_no) +{ + u32 offset; + u8 reg_val = 0; + + if (PRUSS_NUM0 == pru_no) + offset = PRU_SUART_PRU0_ID_ADDR; + else if (PRUSS_NUM1 == pru_no) + offset = PRU_SUART_PRU1_ID_ADDR; + else + return -EINVAL; + + reg_val = pru_no; + pruss_writeb(dev, offset, reg_val); + + return 0; +} + +/* + * suart Initialization routine + */ +s32 pru_softuart_init(struct device *dev, + struct pruss_suart_initparams *init_params, + const u8 *pru_suart_emu_code, u32 fw_size, + u32 clk_rate_pruss, + struct pruss_suart_iomap *pruss_ioaddr) +{ + u32 datatowrite[128] = {0}; + s16 status = 0; + s16 idx; + s16 retval; + u16 i; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) && + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) + return -EINVAL; + + suart_iomap.mcasp_io_addr = pruss_ioaddr->mcasp_io_addr; + suart_iomap.p_fifo_buff_phys_base = + pruss_ioaddr->p_fifo_buff_phys_base; + suart_iomap.p_fifo_buff_virt_base = + pruss_ioaddr->p_fifo_buff_virt_base; + /* Configure McASP0 */ + suart_mcasp_config(init_params->tx_baud_value, + init_params->rx_baud_value, + init_params->oversampling, pruss_ioaddr); + pruss_enable(dev, PRUSS_NUM0); + + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_enable(dev, PRUSS_NUM1); + + /* Reset PRU RAM */ + for (i = 0; i < (PRU0_DATARAM_SIZE / sizeof(int)); i++) + pruss_writel(dev, (PRU0_DATARAM_OFFSET + (i * sizeof(int))), + datatowrite[i]); + if (PRU1_MODE != PRU_MODE_INVALID) { + for (i = 0; i < (PRU1_DATARAM_SIZE / sizeof(int)); i++) + pruss_writel(dev, (PRU1_DATARAM_OFFSET + + (i * sizeof(int))), datatowrite[i]); + } + + pruss_load(dev, PRUSS_NUM0, (u32 *)pru_suart_emu_code, + (fw_size / sizeof(u32))); + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_load(dev, PRUSS_NUM1, (u32 *)pru_suart_emu_code, + (fw_size / sizeof(u32))); + + retval = arm_to_pru_intr_init(dev); + if (-1 == retval) + return status; + pru_set_delay_count(dev, clk_rate_pruss); + suart_set_pru_id(dev, PRUSS_NUM0); + if (PRU1_MODE != PRU_MODE_INVALID) + suart_set_pru_id(dev, PRUSS_NUM1); + + pru_set_rx_tx_mode(dev, PRU0_MODE, PRUSS_NUM0); + if (PRU1_MODE != PRU_MODE_INVALID) + pru_set_rx_tx_mode(dev, PRU1_MODE, PRUSS_NUM1); + + pru_set_ram_data(dev, pruss_ioaddr); + pruss_run(dev, PRUSS_NUM0); + + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_run(dev, PRUSS_NUM1); + + /* Initialize uart_statu_table */ + for (idx = 0; idx < 8; idx++) + uart_statu_table[idx] = ePRU_SUART_UART_FREE; + + return status; +} + +void pru_set_fifo_timeout(struct device *dev, s16 timeout) +{ + pruss_writew(dev, PRU_SUART_PRU0_IDLE_TIMEOUT_OFFSET, (u16) timeout); + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_writew(dev, PRU_SUART_PRU1_IDLE_TIMEOUT_OFFSET, + (u16) timeout); +} + +void pru_mcasp_deinit(void) +{ + suart_mcasp_reset(&suart_iomap); +} + +/* suart Instance open routine */ +s32 pru_softuart_open(struct suart_handle *h_suart) +{ + s16 status = 0; + u16 uart_num = h_suart->uart_num - 1; + + if (uart_statu_table[h_suart->uart_num - 1] == + ePRU_SUART_UART_IN_USE) { + return -EUSERS; + } else { + h_suart->uart_type = uart_config[uart_num]; + h_suart->uart_tx_channel = uart_tx[uart_num]; + h_suart->uart_rx_channel = uart_rx[uart_num]; + h_suart->uart_status = ePRU_SUART_UART_IN_USE; + uart_statu_table[h_suart->uart_num - 1] = + ePRU_SUART_UART_IN_USE; + } + return status; +} + +/* suart instance close routine */ +s32 pru_softuart_close(struct suart_handle *h_uart) +{ + s16 status = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } else { + uart_statu_table[h_uart->uart_num - 1] = + ePRU_SUART_UART_FREE; + /* Reset the Instance to Invalid */ + h_uart->uart_num = PRU_SUART_UARTx_INVALID; + h_uart->uart_status = ePRU_SUART_UART_FREE; + } + return status; +} + +static s32 search_chnum(u16 uart_num, u16 *ch_num, u32 *pru_offset, u16 mode) +{ + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + *ch_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (uart_num <= 4) { + *pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + *pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + *ch_num -= 8; + } + (mode == 2) ? ++*ch_num : *ch_num; + } else if (mode == 1) { + if (PRU0_MODE == PRU_MODE_TX_ONLY) + *pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + else if (PRU1_MODE == PRU_MODE_TX_ONLY) + *pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if (mode == 2) { + if (PRU0_MODE == PRU_MODE_RX_ONLY) + *pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + else if (PRU1_MODE == PRU_MODE_RX_ONLY) + *pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } + return 0; +} + +/* + * suart routine for setting relative baud rate + */ +s32 pru_softuart_setbaud(struct device *dev, struct suart_handle *h_uart, + u16 tx_clk_divisor, u16 rx_clk_divisor) +{ + u32 offset; + u32 pru_offset; + s16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + u16 regval = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + /* Set the clock divisor value s32o the McASP */ + if ((tx_clk_divisor > 385) || (tx_clk_divisor == 0)) + return -EINVAL; + if ((rx_clk_divisor > 385) || (rx_clk_divisor == 0)) + return -EINVAL; + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + if (tx_clk_divisor != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= (~0x3FF); + regval |= tx_clk_divisor; + pruss_writew(dev, offset, regval); + } + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return 0; + } + regval = 0; + if (rx_clk_divisor != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= (~0x3FF); + regval |= tx_clk_divisor; + pruss_writew(dev, offset, regval); + } + return status; +} + +/* + * suart routine for setting number of bits per character for a specific uart + */ +s32 pru_softuart_setdatabits(struct device *dev, struct suart_handle *h_uart, + u16 tx_data_bits, u16 rx_data_bits) +{ + u32 offset; + u32 pru_offset; + s16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + u32 reg_val; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + /* + * NOTE: + * The supported data bits are 6,7,8,9,10,11,12 bits per character + */ + + if ((tx_data_bits < ePRU_SUART_DATA_BITS6) + || (tx_data_bits > ePRU_SUART_DATA_BITS12)) + return -EINVAL; + + if ((rx_data_bits < ePRU_SUART_DATA_BITS6) + || (rx_data_bits > ePRU_SUART_DATA_BITS12)) + return -EINVAL; + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + if (tx_data_bits != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val); + reg_val &= ~(0xF); + reg_val |= tx_data_bits; + pruss_writeb(dev, offset, (u8) reg_val); + } + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return 0; + } + if (rx_data_bits != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val); + reg_val &= ~(0xF); + reg_val |= rx_data_bits; + pruss_writeb(dev, offset, (u8) rx_data_bits); + } + + return status; +} + +/* + * suart routine to configure specific uart + */ +s32 pru_softuart_setconfig(struct device *dev, struct suart_handle *h_uart, + struct suart_config *config_uart) +{ + u32 offset; + u32 pru_offset; + s16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + u16 reg_val = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + /* + * NOTE: + * Dependent baud rate for the given UART,the value MUST BE LESS THAN OR + * EQUAL TO 64, preScalarValue <= 64 + */ + if ((config_uart->tx_clk_divisor > 384) + || (config_uart->rx_clk_divisor > 384)) { + return -EINVAL; + } + if ((config_uart->tx_bits_per_char < 8) + || (config_uart->tx_bits_per_char > 14)) { + return -EINVAL; + } + if ((config_uart->rx_bits_per_char < 8) + || (config_uart->rx_bits_per_char > 14)) { + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + /* + * Configuring the Transmit part of the given UART + * Serializer has been as TX in mcasp config, by writing 1 in bits + * corresponding to tx serializer in PFUNC regsiter ie already set + * to GPIO mode PRU code will set then back to MCASP mode once TX + * request for that serializer is posted.It is required because at this + * pos32 Mcasp is accessed by both PRU and DSP have lower priority for + * Mcasp in comparison to PRU and DPS keeps on looping there only + * + * suart_mcasp_tx_serialzier_set + * (config_uart->tx_serializer, &suart_iomap); + */ + + /* Configuring TX serializer */ + if (config_uart->tx_serializer != PRU_SUART_SERIALIZER_NONE) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val = reg_val | (config_uart->tx_serializer << + PRU_SUART_CH_CTRL_SR_SHIFT); + pruss_writew(dev, offset, reg_val); + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val = reg_val | (config_uart->tx_clk_divisor << + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + pruss_writew(dev, offset, reg_val); + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val = reg_val | (config_uart->tx_bits_per_char << + PRU_SUART_CH_CONFIG2_BITPERCHAR_SHIFT); + pruss_writew(dev, offset, reg_val); + } + + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return 0; + } + + /* Configuring the Transmit part of the given UART */ + if (config_uart->rx_serializer != PRU_SUART_SERIALIZER_NONE) { + /* Configuring RX serializer */ + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val = reg_val | (config_uart->rx_serializer << + PRU_SUART_CH_CTRL_SR_SHIFT); + pruss_writew(dev, offset, reg_val); + + /* Configuring RX prescalar value and Oversampling */ + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val = reg_val | (config_uart->rx_clk_divisor << + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT) | + (config_uart->oversampling << + PRU_SUART_CH_CONFIG1_OVS_SHIFT); + pruss_writew(dev, offset, reg_val); + + /* Configuring RX bits per character value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val = reg_val | (config_uart->rx_bits_per_char << + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + pruss_writew(dev, offset, reg_val); + } + return status; +} + +/* + * suart routine for getting the number of bytes transfered + */ +s32 pru_softuart_get_tx_data_len(struct device *dev, + struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 ch_num = h_uart->uart_num - 1; + u16 read_value = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) &read_value); + read_value = ((read_value & PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + return read_value; +} + +/* + * suart routine for getting the number of bytes received + */ +s32 pru_softuart_get_rx_data_len(struct device *dev, + struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 ch_num = h_uart->uart_num - 1; + u16 read_value = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) &read_value); + read_value = ((read_value & PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + return read_value; +} + +/* + * suart routine to get the configuration information from a specific uart + */ +s32 pru_softuart_getconfig(struct device *dev, + struct suart_handle *h_uart, + struct suart_config *config_uart) +{ + u32 offset; + u32 pru_offset; + u16 ch_num = h_uart->uart_num - 1; + u16 reg_val = 0; + s16 status = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + /* + * NOTE: + * Dependent baud rate for the given UART,the value MUST BE LESS THAN OR + * EQUAL TO 64, preScalarValue <= 64 + */ + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + /* Configuring the Transmit part of the given UART */ + /* Configuring TX serializer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + config_uart->tx_serializer = ((reg_val & PRU_SUART_CH_CTRL_SR_MASK) >> + PRU_SUART_CH_CTRL_SR_SHIFT); + /* Configuring TX prescalar value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + config_uart->tx_clk_divisor = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) >> + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + + /* Configuring TX bits per character value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + config_uart->tx_bits_per_char = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) >> + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return 0; + } + /* Configuring the Transmit part of the given UART */ + /* Configuring RX serializer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + config_uart->rx_serializer = ((reg_val & PRU_SUART_CH_CTRL_SR_MASK) >> + PRU_SUART_CH_CTRL_SR_SHIFT); + + /* Configuring RX prescalar value and oversampling */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + config_uart->rx_clk_divisor = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + config_uart->oversampling = ((reg_val & + PRU_SUART_CH_CONFIG1_OVS_MASK) >> + PRU_SUART_CH_CONFIG1_OVS_SHIFT); + + /* Configuring RX bits per character value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + config_uart->rx_bits_per_char = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + + return status; +} + +s32 pru_softuart_pending_tx_request(struct device *dev) +{ + u32 offset = 0; + u32 ISR_value = 0; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + return 0; + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { + /* Read PRU Interrupt Status Register from PRU */ + offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + pruss_readl(dev, offset, (u32 *)&ISR_value); + if ((ISR_value & 0x1) == 0x1) + return -EINVAL; + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { + /* Read PRU Interrupt Status Register from PRU */ + offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + pruss_readl(dev, offset, (u32 *)&ISR_value); + if ((ISR_value & 0x2) == 0x2) + return -EINVAL; + } else { + return 0; + } + + return 0; +} + +/* + * suart data transmit routine + */ +s32 pru_softuart_write(struct device *dev, struct suart_handle *h_uart, + u32 *pt_tx_data_buf, u16 data_len) +{ + u32 offset = 0; + u32 pru_offset; + s16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + u16 reg_val = 0; + u16 pru_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) + pru_num = h_uart->uart_num; + else if (PRU0_MODE == PRU_MODE_TX_ONLY) + pru_num = 0; + else if (PRU1_MODE == PRU_MODE_TX_ONLY) + pru_num = 1; + else + return 0; + + /* Writing data length to SUART channel register */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val &= ~PRU_SUART_CH_CONFIG2_DATALEN_MASK; + reg_val = reg_val | (data_len << PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + pruss_writew(dev, offset, reg_val); + + /* Writing the data pos32er to channel TX data pointer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXDATA_OFFSET; + pruss_writel(dev, offset, (u32) *pt_tx_data_buf); + + /* Service Request to PRU */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | + PRU_SUART_CH_CTRL_SREQ_MASK); + reg_val |= (PRU_SUART_CH_CTRL_TX_MODE << + PRU_SUART_CH_CTRL_MODE_SHIFT) | (PRU_SUART_CH_CTRL_SREQ << + PRU_SUART_CH_CTRL_SREQ_SHIFT); + pruss_writew(dev, offset, reg_val); + + /* generate ARM->PRU event */ + suart_arm_to_pru_intr(dev, pru_num); + + return status; +} + +/* + * suart data receive routine + */ +s32 pru_softuart_read(struct device *dev, struct suart_handle *h_uart, + u32 *ptDataBuf, u16 data_len) +{ + u32 offset = 0; + u32 pru_offset; + s16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + u16 reg_val = 0; + u16 pru_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + pru_num = h_uart->uart_num; + } else if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_num = 0; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_num = 1; + } else { + return 0; + } + /* Writing data length to SUART channel register */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val &= ~PRU_SUART_CH_CONFIG2_DATALEN_MASK; + reg_val = reg_val | (data_len << PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + pruss_writew(dev, offset, reg_val); + + /* Writing the data pos32er to channel RX data pointer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXDATA_OFFSET; + pruss_writel(dev, offset, (u32) *ptDataBuf); + + /* Service Request to PRU */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | + PRU_SUART_CH_CTRL_SREQ_MASK); + reg_val |= (PRU_SUART_CH_CTRL_RX_MODE << + PRU_SUART_CH_CTRL_MODE_SHIFT) | (PRU_SUART_CH_CTRL_SREQ << + PRU_SUART_CH_CTRL_SREQ_SHIFT); + pruss_writew(dev, offset, reg_val); + + /* enable the timeout s32errupt */ + suart_intr_setmask(dev, h_uart->uart_num, PRU_RX_INTR, + CHN_TXRX_IE_MASK_TIMEOUT); + + /* generate ARM->PRU event */ + suart_arm_to_pru_intr(dev, pru_num); + + return status; +} + +/* + * suart routine to read the data from the RX FIFO + */ +s32 pru_softuart_read_data(struct device *dev, struct suart_handle *h_uart, + u8 *p_data_buffer, s32 max_len, + u32 *pdata_read) +{ + s16 ret_val = 0; + u8 *psrc_addr = NULL; + u32 data_read = 0; + u32 data_len = 0; + u32 char_len = 0; + u32 offset = 0; + u32 pru_offset; + u16 ch_num = h_uart->uart_num - 1; + u16 status = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + /* Get the data pos32er from channel RX data pointer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXDATA_OFFSET; + pruss_readb_multi(dev, offset, (u8 *) &psrc_addr, 4); + + /* Reading data length from SUART channel register */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readw(dev, offset, (u16 *) &data_len); + + /* read the character length */ + char_len = data_len & PRU_SUART_CH_CONFIG2_BITPERCHAR_MASK; + char_len -= 2; /* remove the START & STOP bit */ + + data_len &= PRU_SUART_CH_CONFIG2_DATALEN_MASK; + data_len = data_len >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT; + data_len++; + + /* if the character length is greater than 8, then the size doubles */ + if (char_len > 8) + data_len *= 2; + + /* Check if the time-out had occured. If, yes, then we need to find the + * number of bytes read from PRU. Else, we need to + * read the requested bytes + */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status); + if (CHN_TXRX_STATUS_TIMEOUT == (status & CHN_TXRX_STATUS_TIMEOUT)) { + /* determine the number of bytes read s32o the FIFO */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_BYTESDONECNTR_OFFSET; + pruss_readb(dev, offset, (u8 *) &data_read); + + /* if the character length is greater than 8, + then the size doubles */ + if (char_len > 8) + data_read *= 2; + +/* + * the data corresponding is loaded in second + * half during the timeout + */ + if (data_read > data_len) { + data_read -= data_len; + psrc_addr += data_len; + } + + pru_softuart_clr_rx_fifo(dev, h_uart); + } else { + data_read = data_len; +/* + * if the bit is set, the data is in the first + * half of the FIFO else the data is in the second half + */ + /* Determine the buffer index by reading FIFO_OddEven flag*/ + if (status & CHN_TXRX_STATUS_CMPLT) + psrc_addr += data_len; + } + + /* we should be copying only max len given by the application */ + if (data_read > max_len) + data_read = max_len; + +/* evaluate the virtual address of the FIFO address + * based on the physical addr + */ + psrc_addr = (u8 *)((u32) psrc_addr - + (u32) suart_iomap.p_fifo_buff_phys_base + + (u32) suart_iomap.p_fifo_buff_virt_base); + + /* Now we have both the data length and the source address. copy */ + for (offset = 0; offset < data_read; offset++) + *p_data_buffer++ = *psrc_addr++; + *pdata_read = data_read; + ret_val = 0; + + return ret_val; +} + +/* + * suart routine to disable the receive functionality. + * This routine stops the PRU from receiving on selected + * UART and also disables the McASP serializer corresponding + * to this UART Rx line. + */ +s32 pru_softuart_stop_receive(struct device *dev, struct suart_handle *h_uart) +{ + u16 ret_status = 0; + u32 offset; + u32 pru_offset; + u16 ch_num = h_uart->uart_num - 1; + u16 status; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + /* read the existing value of status flag */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status); + + /* we need to clear the busy bit corresponding to receive channel */ + status &= ~(CHN_TXRX_STATUS_RDY); + pruss_writeb(dev, offset, (u8) status); + + /* get the serizlizer number being used for this Rx channel */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) &status); + status &= PRU_SUART_CH_CTRL_SR_MASK; + status = status >> PRU_SUART_CH_CTRL_SR_SHIFT; + + /* we need to de-activate the serializer corresponding to this rx */ + ret_status = suart_asp_serializer_deactivate(status, &suart_iomap); + + return ret_status; +} + +/* + * suart routine to get the tx status for a specific uart + */ +s32 pru_softuart_get_tx_status(struct device *dev, struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status); + return status; +} + +s32 pru_softuart_clr_tx_status(struct device *dev, struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status); + status &= ~(0x2); + pruss_writeb(dev, offset, (u8) status); + return status; +} + +/* + * suart routine to get the rx status for a specific uart + */ +s32 pru_softuart_get_rx_status(struct device *dev, struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status); + return status; +} + +static s32 pru_softuart_clr_rx_fifo(struct device *dev, + struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + u16 reg_val; + u16 uart_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + uart_num = h_uart->uart_num; + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + if (PRU0_MODE == PRU_MODE_RX_ONLY) + uart_num = 0; + else if (PRU1_MODE == PRU_MODE_RX_ONLY) + uart_num = 1; + + /* Reset the number of bytes read into the FIFO */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_BYTESDONECNTR_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val &= 0x00; + pruss_writew(dev, offset, reg_val); + + + /* Service Request to PRU */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val); + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | PRU_SUART_CH_CTRL_SREQ_MASK); + reg_val |= (PRU_SUART_CH_CTRL_RX_MODE << PRU_SUART_CH_CTRL_MODE_SHIFT) | + (PRU_SUART_CH_CTRL_SREQ << PRU_SUART_CH_CTRL_SREQ_SHIFT); + pruss_writew(dev, offset, reg_val); + suart_intr_setmask(dev, h_uart->uart_num, PRU_RX_INTR, + CHN_TXRX_IE_MASK_TIMEOUT); + + /* generate ARM->PRU event */ + suart_arm_to_pru_intr(dev, uart_num); + + return status; +} + +s32 pru_softuart_clr_rx_status(struct device *dev, struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num = h_uart->uart_num - 1; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status); + status &= ~(0x3C); + pruss_writeb(dev, offset, (u8) status); + return status; +} + +/* + * suart_s32r_status_read: Gets the Global Interrupt status register + * for the specified SUART. + * uart_num < 1 to 6 > + * txrx_flag < Indicates TX or RX s32errupt for the uart > + */ +s32 pru_softuart_get_isrstatus(struct device *dev, u16 uart_num, u16 *txrx_flag) +{ + u32 intc_offset; + u32 ch_num = 0xFF; + u32 reg_val = 0; + u32 reg_val2 = 0; + u32 ISR_value = 0; + u32 ack_reg_val = 0; + u32 stat_inx_clr_regoffset = 0; + + /* initialize the status & Flag to known value */ + *txrx_flag = 0; + + stat_inx_clr_regoffset = (u32) (PRUSS_INTC_STATIDXCLR & 0xFFFF); + + /* Read PRU Interrupt Status Register from PRU */ + intc_offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + + pruss_readl(dev, intc_offset, (u32 *)&ISR_value); + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* Check if the interrupt occured for Tx */ + ch_num = uart_num * 2 - 2; + reg_val2 = PRU_SUART0_TX_EVT_BIT << ((uart_num - 1) * 2); + if (ISR_value & reg_val2) { + /* interupt occured for TX */ + *txrx_flag |= PRU_TX_INTR; + /* acknowledge the RX interrupt */ + ack_reg_val = ch_num + PRU_SUART0_TX_EVT; + pruss_writel(dev, stat_inx_clr_regoffset, + ack_reg_val); + } + + /* Check if the interrupt occured for Rx */ + reg_val2 = PRU_SUART0_RX_EVT_BIT << ((uart_num - 1) * 2); + pruss_readl(dev, intc_offset, (u32 *)&ISR_value); + if (ISR_value & reg_val2) { + /* interupt occured for RX */ + *txrx_flag |= PRU_RX_INTR; + ch_num += 1; + + /* acknowledge the RX interrupt */ + ack_reg_val = ch_num + PRU_SUART0_TX_EVT; + pruss_writel(dev, stat_inx_clr_regoffset, + ack_reg_val); + } + } else { + ch_num = uart_num - 1; + if ((ISR_value & 0x03FC) != 0) { + reg_val2 = 1 << (uart_num + 1); + if (ISR_value & reg_val2) { + /* acknowledge the s32errupt */ + ack_reg_val = ch_num + PRU_SUART0_TX_EVT; + pruss_writel(dev, stat_inx_clr_regoffset, + ack_reg_val); + *txrx_flag |= PRU_RX_INTR; + } + } + pruss_readl(dev, intc_offset, (u32 *)&ISR_value); + if (ISR_value & 0x3FC00) { + reg_val2 = 1 << (uart_num + 9); + if (ISR_value & reg_val2) { + /* acknowledge the s32errupt */ + ack_reg_val = ch_num + PRU_SUART4_TX_EVT; + pruss_writel(dev, stat_inx_clr_regoffset, + ack_reg_val); + *txrx_flag |= PRU_TX_INTR; + } + } + } + return reg_val; +} + +s32 pru_intr_clr_isrstatus(struct device *dev, u16 uart_num, u32 txrxmode) +{ + u32 offset; + u16 txrx_flag = 0; + u16 chn_num; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (uart_num <= 4) { + /* PRU0 */ + offset = PRU_SUART_PRU0_ISR_OFFSET + 1; + } else { + /* PRU1 */ + offset = PRU_SUART_PRU1_ISR_OFFSET + 1; + /* First 8 channel corresponds to PRU0 */ + chn_num -= 8; + } + if (2 == txrxmode) + chn_num++; + } else if (PRU0_MODE == txrxmode) { + offset = PRU_SUART_PRU0_ISR_OFFSET + 1; + } else if (PRU1_MODE == txrxmode) { + offset = PRU_SUART_PRU1_ISR_OFFSET + 1; + } else { + return 0; + } + + pruss_readb(dev, offset, (u8 *) &txrx_flag); + txrx_flag &= ~(0x2); + pruss_writeb(dev, offset, (u8) txrx_flag); + + return 0; +} + +s32 suart_arm_to_pru_intr(struct device *dev, u16 uart_num) +{ + u32 value; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + if ((uart_num > 0) && (uart_num <= 4)) + value = 0x20; /* PRU0 SYS_EVT32 */ + else if ((uart_num > 4) && (uart_num <= 8)) + value = 0x21; /* PRU0 SYS_EVT33 */ + else + return -EINVAL; + } + if ((PRU0_MODE == PRU_MODE_RX_ONLY) + || (PRU1_MODE == PRU_MODE_RX_ONLY) + || (PRU0_MODE == PRU_MODE_TX_ONLY) + || (PRU1_MODE == PRU_MODE_TX_ONLY)) { + if (uart_num == PRUSS_NUM0) + value = 0x20; /* PRU0 SYS_EVT32 */ + else if (uart_num == PRUSS_NUM1) + value = 0x21; /* PRU0 SYS_EVT33 */ + else + return -EINVAL; + } + return pruss_writel(dev, PRUSS_INTC_STATIDXSET, value); +} + +static s32 arm_to_pru_intr_init(struct device *dev) +{ + u32 value; + u32 int_offset; + + /* Clear all the host interrupts */ + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; + int_offset++) + pruss_idx_writel(dev, PRUSS_INTC_HSTINTENIDXCLR, int_offset); + + /* Enable the global s32errupt */ + pruss_rmwl(dev, (u32) (PRUSS_INTC_GLBLEN & 0xFFFF), 0, 1); + + /* Enable the Host interrupts for all host channels */ + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; + int_offset++) + pruss_rmwl(dev, (u32) (PRUSS_INTC_HSTINTENIDXSET & 0xFFFF), + 0, int_offset); + pruss_rmwl(dev, (u32) (PRUSS_INTC_HOSTMAP0 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_HOSTMAP0_CHAN); + pruss_rmwl(dev, (u32) (PRUSS_INTC_HOSTMAP1 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_HOSTMAP1_CHAN); + pruss_rmwl(dev, (u32) (PRUSS_INTC_HOSTMAP2 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_HOSTMAP2_CHAN); + + /* MAP Channel 0 to SYS_EVT31 */ + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP7 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP7_SYS_EVT31); + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* Sets the channels for the system interrupt */ + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP8 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP8_FULL); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP9_FULL); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP10 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP10_FULL); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP11 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP11_FULL); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP12 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP12_FULL); + } + if ((PRU0_MODE == PRU_MODE_RX_ONLY) + || (PRU1_MODE == PRU_MODE_RX_ONLY) + || (PRU0_MODE == PRU_MODE_TX_ONLY) + || (PRU1_MODE == PRU_MODE_TX_ONLY)) { + + /* Sets the channels for the system interrupt */ + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP8 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP8_HALF); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP9_HALF); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP10 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP10_HALF); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP11 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP11_HALF); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP12 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP12_HALF); + } + + /* Clear required set of system events + * and enable them using indexed register + */ + for (int_offset = 0; int_offset < 18; int_offset++) { + value = 32 + int_offset; + pruss_idx_writel(dev, PRUSS_INTC_STATIDXCLR, value); + } + + /* enable only the HOST to PRU interrupts and let the PRU to Host events + * enabled by the separate API on demand basis. + */ + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 31); + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 32); + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 33); + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 50); + pruss_rmwl(dev, (u32) (PRUSS_INTC_GLBLEN & 0xFFFF), 0, 1); + + /* Enable the Host interrupts for all host channels */ + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; + int_offset++) + pruss_idx_writel(dev, PRUSS_INTC_HSTINTENIDXSET, int_offset); + + return 0; +} + +s32 suart_pru_to_host_intr_enable(struct device *dev, u16 uart_num, + u32 txrxmode, s32 flag) +{ + u32 chn_num; + u32 value; + s16 retval = 0; + + if (uart_num > 8) + return -EINVAL; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + chn_num = (uart_num * 2) - 2; + if (2 == txrxmode) /* Rx mode */ + chn_num++; + value = 34 + chn_num; + } else if ((PRU_MODE_RX_ONLY == txrxmode) + && (PRU0_MODE == PRU_MODE_RX_ONLY)) + value = 34 + chn_num; + else if ((PRU_MODE_RX_ONLY == txrxmode) + && (PRU1_MODE == PRU_MODE_RX_ONLY)) + value = 42 + chn_num; + else if ((PRU_MODE_TX_ONLY == txrxmode) + && (PRU0_MODE == PRU_MODE_TX_ONLY)) + value = 34 + chn_num; + else if ((PRU_MODE_TX_ONLY == txrxmode) + && (PRU1_MODE == PRU_MODE_TX_ONLY)) + value = 42 + chn_num; + else + return -EINVAL; + + retval = flag ? pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, value) : + pruss_idx_writel(dev, PRUSS_INTC_ENIDXCLR, value); + return retval; +} + +s32 suart_intr_setmask(struct device *dev, u16 uart_num, + u32 txrxmode, u32 rmask) +{ + u32 offset; + u32 pru_offset; + u32 regval = 0; + u32 chn_num = uart_num - 1; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if ((uart_num > 0) && (uart_num <= 4)) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if ((uart_num > 4) && (uart_num <= 8)) { + offset = PRU_SUART_PRU1_IMR_OFFSET; + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + chn_num -= 8; + } else { + return -EINVAL; + } + if (2 == txrxmode) + chn_num++; + } else if (PRU0_MODE == txrxmode) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if (PRU1_MODE == txrxmode) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + offset = PRU_SUART_PRU1_IMR_OFFSET; + } else + return 0; + + regval = 1 << chn_num; + if (CHN_TXRX_IE_MASK_CMPLT == (rmask & CHN_TXRX_IE_MASK_CMPLT)) + pruss_rmww(dev, offset, regval, regval); + + if ((rmask & SUART_GBL_INTR_ERR_MASK) == + SUART_GBL_INTR_ERR_MASK) { + regval = SUART_GBL_INTR_ERR_MASK; + pruss_rmww(dev, offset, regval, regval); + } + + offset = pru_offset + + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + /* Framing Error Interrupt Masked */ + if ((rmask & CHN_TXRX_IE_MASK_FE) == CHN_TXRX_IE_MASK_FE) { + regval = 0; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= ~(CHN_TXRX_IE_MASK_FE); + regval |= CHN_TXRX_IE_MASK_FE; + pruss_writew(dev, offset, (u16) regval); + } + + /* Break Indicator Interrupt Masked */ + if (CHN_TXRX_IE_MASK_BI == (rmask & CHN_TXRX_IE_MASK_BI)) { + regval = 0; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= ~(CHN_TXRX_IE_MASK_BI); + regval |= CHN_TXRX_IE_MASK_BI; + pruss_writew(dev, offset, (u16) regval); + } + + /* Timeout error Interrupt Masked */ + if (CHN_TXRX_IE_MASK_TIMEOUT == + (rmask & CHN_TXRX_IE_MASK_TIMEOUT)) { + regval = 0; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= ~(CHN_TXRX_IE_MASK_TIMEOUT); + regval |= CHN_TXRX_IE_MASK_TIMEOUT; + pruss_writew(dev, offset, (u16) regval); + } + return 0; +} + +s32 suart_intr_clrmask(struct device *dev, u16 uart_num, + u32 txrxmode, u32 rmask) +{ + u32 offset; + u32 pru_offset; + u16 regval = 0; + u16 chn_num; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if ((uart_num > 0) && (uart_num <= 4)) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if ((uart_num > 4) && (uart_num <= 8)) { + /* PRU1 */ + offset = PRU_SUART_PRU1_IMR_OFFSET; + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + /* First 8 channel corresponds to PRU0 */ + chn_num -= 8; + } else + return -EINVAL; + if (2 == txrxmode) + chn_num++; + } else if (PRU0_MODE == txrxmode) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if (PRU1_MODE == txrxmode) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + offset = PRU_SUART_PRU1_IMR_OFFSET; + } else + return 0; + + regval = 1 << chn_num; + if (CHN_TXRX_IE_MASK_CMPLT == (rmask & CHN_TXRX_IE_MASK_CMPLT)) + pruss_rmww(dev, offset, regval, 0); + + if ((rmask & SUART_GBL_INTR_ERR_MASK) == SUART_GBL_INTR_ERR_MASK) + pruss_rmww(dev, offset, SUART_GBL_INTR_ERR_MASK, 0); + + offset = pru_offset + + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + + /* Framing Error Interrupt Masked */ + if ((rmask & CHN_TXRX_IE_MASK_FE) == CHN_TXRX_IE_MASK_FE) { + regval = 0; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= ~(CHN_TXRX_IE_MASK_FE); + pruss_writew(dev, offset, regval); + } + + /* Break Indicator Interrupt Masked */ + if (CHN_TXRX_IE_MASK_BI == (rmask & CHN_TXRX_IE_MASK_BI)) { + regval = 0; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= ~(CHN_TXRX_IE_MASK_BI); + pruss_writew(dev, offset, regval); + } + + /* Timeout error Interrupt Masked */ + if (CHN_TXRX_IE_MASK_TIMEOUT == + (rmask & CHN_TXRX_IE_MASK_TIMEOUT)) { + regval = 0; + pruss_readw(dev, offset, (u16 *) ®val); + regval &= ~(CHN_TXRX_IE_MASK_TIMEOUT); + pruss_writew(dev, offset, regval); + } + return 0; +} + +s32 suart_intr_getmask(struct device *dev, u16 uart_num, + u32 txrxmode, u32 rmask) +{ + u16 chn_num; + u32 offset; + u16 txrx_flag; + u16 regval = 1; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if ((uart_num > 0) && (uart_num <= 4)) { + + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if ((uart_num > 4) && (uart_num <= 8)) { + /* PRU1 */ + offset = PRU_SUART_PRU1_IMR_OFFSET; + /* First 8 channel corresponds to PRU0 */ + chn_num -= 8; + } else + return -EINVAL; + + if (2 == txrxmode) + chn_num++; + + } else if (PRU0_MODE == txrxmode) + offset = PRU_SUART_PRU0_IMR_OFFSET; + else if (PRU1_MODE == txrxmode) + offset = PRU_SUART_PRU1_IMR_OFFSET; + else + return 0; + + regval = regval << chn_num; + pruss_readw(dev, offset, (u16 *) &txrx_flag); + txrx_flag &= regval; + + if ((rmask && (txrx_flag == regval)) || (!rmask && !txrx_flag)) + return 1; + + return 0; +} diff --git a/drivers/tty/serial/pruss_suart_utils.c b/drivers/tty/serial/pruss_suart_utils.c new file mode 100644 index 0000000..5ee340e --- /dev/null +++ b/drivers/tty/serial/pruss_suart_utils.c @@ -0,0 +1,393 @@ +/* + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 +#include "pruss_suart.h" + +#define SUART_TRX_DIV_CONF_SZ 4 + +static s16 suart_mcasp_tx_baud_set(u32 tx_baud_value, + struct pruss_suart_iomap *pruss_ioaddr); +static s16 suart_mcasp_rx_baud_set(u32 rx_baud_value, u32 oversampling, + struct pruss_suart_iomap *pruss_ioaddr); + +/* + * Lookup table for TX baud rate + * The divisor value is calculated using the formula + * + * ACLKX = (AUXCLK)/(CLKXDIV * HCLKXDIV) + * + * Where + * CLKXDIV takes values from 1-32 + * HCLKXDIV takes values from 1-4096 + * Here + * AUXCLK = 24MHz + */ +static u32 lt_tx_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { + /*BaudRate, Divisor, CLKXDIV,HCLKXDIV */ + {300, 80000, 24, 3200}, + {600, 40000, 15, 2500}, + {1800, 13333, 10, 1212}, + {2400, 10000, 4, 2000}, + {4800, 5000, 1, 2500}, + {7200, 3333, 0, 3333}, + {9600, 2500, 0, 2500}, + {14400, 1666, 0, 1666}, + {19200, 1250, 0, 1250}, + {38400, 625, 0, 625}, + {57600, 416, 0, 416}, + {115200, 208, 0, 208}, + {230400, 104, 0, 104} +}; + +/* + * Lookup table for RX baud rate for 8 bit oversampling + * The divisor value is calculated using the formula + * + * ACLKR = (AUXCLK)/(CLKRDIV * HCLKRDIV) * Oversampling + * + * Where + * CLKRDIV takes values from 1-32 + * HCLKRDIV takes values from 1-4096 + * Here + * AUXCLK = 24MHz + */ +static u32 lt_rx_8x_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { +/* BaudRate, Divisor, CLKXDIV, HCLKXDIV */ + {300, 10000, 4, 2000}, + {600, 5000, 1, 2500}, + {1800, 1667, 0, 1667}, + {2400, 1250, 0, 1250}, + {7200, 417, 0, 417}, + {4800, 625, 0, 625}, + {9600, 312, 0, 312}, + {14400, 208, 0, 208}, + {19200, 156, 0, 156}, + {38400, 78, 0, 78}, + {57600, 52, 0, 52}, + {115200, 26, 0, 26}, + {230400, 13, 0, 13} +}; + +/* + * Lookup table for RX baud rate for 16 bit oversampling + * The divisor value is calculated using the formula + * + * ACLKR = (AUXCLK)/(CLKRDIV * HCLKRDIV) * Oversampling + * + * Where + * CLKRDIV takes values from 1-32 + * HCLKRDIV takes values from 1-4096 + * Here + * AUXCLK = 24MHz + */ +static u32 lt_rx_16x_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { +/*BaudRate, Divisor, CLKXDIV, HCLKXDIV */ + {300, 5000, 1, 2500}, + {600, 2500, 0, 2500}, + {1800, 833, 0, 833}, + {2400, 625, 0, 625}, + {4800, 312, 0, 312}, + {7200, 208, 0, 208}, + {9600, 156, 0, 156}, + {14400, 104, 0, 104}, + {19200, 78, 0, 78}, + {38400, 39, 0, 39}, + {57600, 26, 0, 26}, + {115200, 13, 0, 13}, + {230400, 6, 0, 6} +}; + +/* + * McASP configuration routine + */ + +void suart_mcasp_reset(struct pruss_suart_iomap *pruss_ioaddr) +{ + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = + pruss_ioaddr->mcasp_io_addr; + /* reset mcasp. */ + iowrite32(MCASP_SUART_GBLCTL, &mcasp0_regs->gblctl); + iowrite32(MCASP_SUART_RGBLCTL, &mcasp0_regs->rgblctl); + iowrite32(MCASP_SUART_XGBLCTL, &mcasp0_regs->xgblctl); + iowrite32(MCASP_SUART_XSTAT, &mcasp0_regs->xstat); + iowrite32(MCASP_SUART_RSTAT, &mcasp0_regs->rstat); +} + +void suart_mcasp_config(u32 tx_baud_value, + u32 rx_baud_value, + u32 oversampling, + struct pruss_suart_iomap *pruss_ioaddr) +{ + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = + pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + + /* reset mcasp */ + iowrite32(MCASP_SUART_GBLCTL, &mcasp0_regs->gblctl); + iowrite32(MCASP_SUART_RGBLCTL, &mcasp0_regs->rgblctl); + iowrite32(MCASP_SUART_XGBLCTL, &mcasp0_regs->xgblctl); + + /* configure receive registers */ + if ((SUART_8X_OVRSMPL == oversampling) || (0 == oversampling)) { + iowrite32(MCASP_SUART_RMASK_8, &mcasp0_regs->rmask); + iowrite32(MCASP_SUART_RFMT_8, &mcasp0_regs->rfmt); + } + if (SUART_16X_OVRSMPL == oversampling) { + iowrite32(MCASP_SUART_RMASK_16, &mcasp0_regs->rmask); + iowrite32(MCASP_SUART_RFMT_16, &mcasp0_regs->rfmt); + + } + + iowrite32(MCASP_SUART_FSRM, &mcasp0_regs->afsrctl); + iowrite32(MCASP_SUART_CLKRM_CLKRP, &mcasp0_regs->aclkrctl); + iowrite32(MCASP_SUART_HCLKRP, &mcasp0_regs->ahclkrctl); + suart_mcasp_rx_baud_set(rx_baud_value, oversampling, pruss_ioaddr); + iowrite32(MCASP_SUART_RTDMS0, &mcasp0_regs->rtdm); + iowrite32(MCASP_SUART_RSYNCERR, &mcasp0_regs->rintctl); + iowrite32(MCASP_SUART_RMAX_RPS_256, &mcasp0_regs->rclkchk); + + /* configure transmit registers. */ + iowrite32(MCASP_SUART_XMASK_0_31, &mcasp0_regs->xmask); + iowrite32(MCASP_SUART_XBUSEL_XSSZ_16_XPAD_0, &mcasp0_regs->xfmt); + iowrite32(MCASP_SUART_FSXM, &mcasp0_regs->afsxctl); + iowrite32(MCASP_SUART_CLKXM_ASYNC_CLKXP, &mcasp0_regs->aclkxctl); + iowrite32(MCASP_SUART_HCLKXM, &mcasp0_regs->ahclkxctl); + + suart_mcasp_tx_baud_set(tx_baud_value, pruss_ioaddr); + iowrite32(MCASP_SUART_XTDMS0, &mcasp0_regs->xtdm); + iowrite32(MCASP_SUART_XSYNCERR, &mcasp0_regs->xintctl); + iowrite32(MCASP_SUART_XMAX_XPS_256, &mcasp0_regs->xclkchk); + + /* Serializer as a transmitter */ + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl0); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl1); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl2); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl3); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl4); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl5); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl6); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl7); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl8); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl9); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl10); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl11); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl12); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl13); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl14); + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl15); + + /* Configure all AXR[n] as McASP pins */ + + /* + * Setting all TX MCASP AXR[n] Pin mapped to Even Serializer number + * (0,2,4,6,8,10,12,14) to GPIO Mode by default. During setting the + * serializer to TX mode in PRU assembly code, the MCASP AXR[n] Pin + * would get configured to MCASP mode of operation, + * before Actual Data Transfer + */ + + /* Setting all TX Pin to GPIO Mode by default */ + temp_reg = (OMAPL_MCASP_PFUNC_RESETVAL) | + (1 << PRU_SUART0_CONFIG_TX_SER) | (1 << PRU_SUART1_CONFIG_TX_SER) | + (1 << PRU_SUART2_CONFIG_TX_SER) | (1 << PRU_SUART3_CONFIG_TX_SER) | + (1 << PRU_SUART4_CONFIG_TX_SER) | (1 << PRU_SUART5_CONFIG_TX_SER) | + (1 << PRU_SUART6_CONFIG_TX_SER) | (1 << PRU_SUART7_CONFIG_TX_SER); + iowrite32(temp_reg, &mcasp0_regs->pfunc); + + iowrite32(0xFFF, &mcasp0_regs->pdout); + + /* config pin function and direction */ + iowrite32(0x00000000, &mcasp0_regs->pdir); + temp_reg = + (1 << PRU_SUART0_CONFIG_TX_SER) | (1 << PRU_SUART1_CONFIG_TX_SER) | + (1 << PRU_SUART2_CONFIG_TX_SER) | (1 << PRU_SUART3_CONFIG_TX_SER) | + (1 << PRU_SUART4_CONFIG_TX_SER) | (1 << PRU_SUART5_CONFIG_TX_SER) | + (1 << PRU_SUART6_CONFIG_TX_SER) | (1 << PRU_SUART7_CONFIG_TX_SER) | + (MCASP_PDIR_VAL); + iowrite32(temp_reg, &mcasp0_regs->pdir); + + iowrite32(MCASP_SUART_DIT_DISABLE, &mcasp0_regs->ditctl); + iowrite32(MCASP_SUART_LOOPBACK_DISABLE, &mcasp0_regs->dlbctl); + iowrite32(MCASP_SUART_AMUTE_DISABLE, &mcasp0_regs->amute); + + iowrite32(MCASP_SUART_XSTAT, &mcasp0_regs->xstat); + iowrite32(MCASP_SUART_RSTAT, &mcasp0_regs->rstat); +} + +void suart_mcasp_tx_serialzier_set(u32 serializer_num, + struct pruss_suart_iomap *pruss_ioaddr) +{ + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = + pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + temp_reg = ioread32(&mcasp0_regs->pfunc); + temp_reg |= (0x1 << serializer_num); + iowrite32(temp_reg, &mcasp0_regs->pfunc); +} + +/* + * mcasp TX buard rate setting routine + */ +static s16 suart_mcasp_tx_baud_set(u32 tx_baud_value, + struct pruss_suart_iomap *pruss_ioaddr) +{ + u32 clk_div_val; + u32 loop_cnt; + s16 status = 0; + s16 found_val = false; + + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = + pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + + /* Search the supported baud rate in the table */ + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (tx_baud_value == lt_tx_baud_rate[loop_cnt][0]) { + found_val = true; + break; + } + } + if (found_val == true) { + clk_div_val = lt_tx_baud_rate[loop_cnt][2]; + temp_reg = ioread32(&mcasp0_regs->aclkxctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->aclkxctl); + clk_div_val = lt_tx_baud_rate[loop_cnt][3]; + temp_reg = ioread32(&mcasp0_regs->ahclkxctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->ahclkxctl); + } else { + return -EINVAL ; + } + return status; +} + +/* + * mcasp RX buard rate setting routine + */ +static s16 suart_mcasp_rx_baud_set(u32 rx_baud_value, + u32 oversampling, struct pruss_suart_iomap *pruss_ioaddr) +{ + u32 clk_div_val = 0; + u32 loop_cnt = 0; + s16 status = 0; + u32 temp_reg = 0; + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = + pruss_ioaddr->mcasp_io_addr; + + switch (oversampling) { + case SUART_8X_OVRSMPL: + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (rx_baud_value == lt_rx_8x_baud_rate[loop_cnt][0]) { + clk_div_val = lt_rx_8x_baud_rate[loop_cnt][2]; + temp_reg = ioread32(&mcasp0_regs->aclkrctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->aclkrctl); + + clk_div_val = + lt_rx_8x_baud_rate[loop_cnt][3] - 1; + + temp_reg = ioread32(&mcasp0_regs->ahclkrctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->ahclkrctl); + break; + } + } + status = -EINVAL; + break; + case SUART_16X_OVRSMPL: + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (rx_baud_value == lt_rx_16x_baud_rate[loop_cnt][0]) { + clk_div_val = lt_rx_16x_baud_rate[loop_cnt][2]; + temp_reg = ioread32(&mcasp0_regs->aclkrctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->aclkrctl); + clk_div_val = lt_rx_16x_baud_rate[loop_cnt][3]; + temp_reg = ioread32(&mcasp0_regs->ahclkrctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->ahclkrctl); + break; + } + } + status = -EINVAL; + break; + case SUART_TX_OVRSMPL: + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (rx_baud_value == lt_tx_baud_rate[loop_cnt][0]) { + clk_div_val = lt_tx_baud_rate[loop_cnt][2]; + temp_reg = ioread32(&mcasp0_regs->aclkrctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->aclkrctl); + clk_div_val = lt_tx_baud_rate[loop_cnt][3]; + temp_reg = ioread32(&mcasp0_regs->ahclkrctl); + temp_reg |= (clk_div_val << + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + iowrite32(temp_reg, &mcasp0_regs->ahclkrctl); + break; + } + } + status = -EINVAL; + break; + default: + status = -EINVAL; + break; + } + + return status; +} + +/* + * mcasp buard rate setting routine + */ +s16 suart_asp_baud_set(u32 tx_baud_value, u32 rx_baud_value, u32 oversampling, + struct pruss_suart_iomap *pruss_ioaddr) +{ + s16 status = 0; + + status = suart_mcasp_tx_baud_set(tx_baud_value, pruss_ioaddr); + status = suart_mcasp_rx_baud_set(rx_baud_value, oversampling, + pruss_ioaddr); + + return status; +} + +/* + * mcasp deactivate the selected serializer + */ +s16 suart_asp_serializer_deactivate(u16 sr_num, + struct pruss_suart_iomap *pruss_ioaddr) +{ + s16 status = 0; + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = + pruss_ioaddr->mcasp_io_addr; + if (sr_num > 15) + status = -EINVAL; + else + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl0); + + return status; +} diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 758c5b0..eae37fe 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -202,6 +202,8 @@ /* VIA VT8500 SoC */ #define PORT_VT8500 97 +#define PORT_DA8XX_PRU_SUART 98 + #ifdef __KERNEL__ #include -- 1.7.2.3 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From cyril at ti.com Fri Jul 8 11:04:07 2011 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 08 Jul 2011 16:04:07 -0000 Subject: [PATCH v7 06/12] regulator: add driver for tps6524x regulator In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-7-git-send-email-cyril@ti.com> TPS6524X provides three step-down converters and two general-purpose LDO voltage regulators. This device is interfaced using SPI. Acked-by: Mark Brown Signed-off-by: Cyril Chemparathy --- drivers/regulator/Kconfig | 10 + drivers/regulator/Makefile | 1 + drivers/regulator/tps6524x-regulator.c | 692 ++++++++++++++++++++++++++++++++ 3 files changed, 703 insertions(+), 0 deletions(-) create mode 100644 drivers/regulator/tps6524x-regulator.c diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index dd30e88..da34981 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -250,5 +250,15 @@ config REGULATOR_TPS6586X help This driver supports TPS6586X voltage regulator chips. +config REGULATOR_TPS6524X + tristate "TI TPS6524X Power regulators" + depends on SPI + help + This driver supports TPS6524X voltage regulator chips. TPS6524X + provides three step-down converters and two general-purpose LDO + voltage regulators. This device is interfaced using a customized + serial interface currently supported on the sequencer serial + port controller. + endif diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index bff8157..cf71df7 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_REGULATOR_AB3100) += ab3100.o obj-$(CONFIG_REGULATOR_TPS65023) += tps65023-regulator.o obj-$(CONFIG_REGULATOR_TPS6507X) += tps6507x-regulator.o +obj-$(CONFIG_REGULATOR_TPS6524X) += tps6524x-regulator.o obj-$(CONFIG_REGULATOR_88PM8607) += 88pm8607.o obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o diff --git a/drivers/regulator/tps6524x-regulator.c b/drivers/regulator/tps6524x-regulator.c new file mode 100644 index 0000000..dea1260 --- /dev/null +++ b/drivers/regulator/tps6524x-regulator.c @@ -0,0 +1,692 @@ +/* + * Regulator driver for TPS6524x PMIC + * + * Copyright (C) 2010 Texas Instruments + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define REG_LDO_SET 0x0 +#define LDO_ILIM_MASK 1 /* 0 = 400-800, 1 = 900-1500 */ +#define LDO_VSEL_MASK 0x0f +#define LDO2_ILIM_SHIFT 12 +#define LDO2_VSEL_SHIFT 4 +#define LDO1_ILIM_SHIFT 8 +#define LDO1_VSEL_SHIFT 0 + +#define REG_BLOCK_EN 0x1 +#define BLOCK_MASK 1 +#define BLOCK_LDO1_SHIFT 0 +#define BLOCK_LDO2_SHIFT 1 +#define BLOCK_LCD_SHIFT 2 +#define BLOCK_USB_SHIFT 3 + +#define REG_DCDC_SET 0x2 +#define DCDC_VDCDC_MASK 0x1f +#define DCDC_VDCDC1_SHIFT 0 +#define DCDC_VDCDC2_SHIFT 5 +#define DCDC_VDCDC3_SHIFT 10 + +#define REG_DCDC_EN 0x3 +#define DCDCDCDC_EN_MASK 0x1 +#define DCDCDCDC1_EN_SHIFT 0 +#define DCDCDCDC1_PG_MSK BIT(1) +#define DCDCDCDC2_EN_SHIFT 2 +#define DCDCDCDC2_PG_MSK BIT(3) +#define DCDCDCDC3_EN_SHIFT 4 +#define DCDCDCDC3_PG_MSK BIT(5) + +#define REG_USB 0x4 +#define USB_ILIM_SHIFT 0 +#define USB_ILIM_MASK 0x3 +#define USB_TSD_SHIFT 2 +#define USB_TSD_MASK 0x3 +#define USB_TWARN_SHIFT 4 +#define USB_TWARN_MASK 0x3 +#define USB_IWARN_SD BIT(6) +#define USB_FAST_LOOP BIT(7) + +#define REG_ALARM 0x5 +#define ALARM_LDO1 BIT(0) +#define ALARM_DCDC1 BIT(1) +#define ALARM_DCDC2 BIT(2) +#define ALARM_DCDC3 BIT(3) +#define ALARM_LDO2 BIT(4) +#define ALARM_USB_WARN BIT(5) +#define ALARM_USB_ALARM BIT(6) +#define ALARM_LCD BIT(9) +#define ALARM_TEMP_WARM BIT(10) +#define ALARM_TEMP_HOT BIT(11) +#define ALARM_NRST BIT(14) +#define ALARM_POWERUP BIT(15) + +#define REG_INT_ENABLE 0x6 +#define INT_LDO1 BIT(0) +#define INT_DCDC1 BIT(1) +#define INT_DCDC2 BIT(2) +#define INT_DCDC3 BIT(3) +#define INT_LDO2 BIT(4) +#define INT_USB_WARN BIT(5) +#define INT_USB_ALARM BIT(6) +#define INT_LCD BIT(9) +#define INT_TEMP_WARM BIT(10) +#define INT_TEMP_HOT BIT(11) +#define INT_GLOBAL_EN BIT(15) + +#define REG_INT_STATUS 0x7 +#define STATUS_LDO1 BIT(0) +#define STATUS_DCDC1 BIT(1) +#define STATUS_DCDC2 BIT(2) +#define STATUS_DCDC3 BIT(3) +#define STATUS_LDO2 BIT(4) +#define STATUS_USB_WARN BIT(5) +#define STATUS_USB_ALARM BIT(6) +#define STATUS_LCD BIT(9) +#define STATUS_TEMP_WARM BIT(10) +#define STATUS_TEMP_HOT BIT(11) + +#define REG_SOFTWARE_RESET 0xb +#define REG_WRITE_ENABLE 0xd +#define REG_REV_ID 0xf + +#define N_DCDC 3 +#define N_LDO 2 +#define N_SWITCH 2 +#define N_REGULATORS (3 /* DCDC */ + \ + 2 /* LDO */ + \ + 2 /* switch */) + +#define FIXED_ILIMSEL BIT(0) +#define FIXED_VOLTAGE BIT(1) + +#define CMD_READ(reg) ((reg) << 6) +#define CMD_WRITE(reg) (BIT(5) | (reg) << 6) +#define STAT_CLK BIT(3) +#define STAT_WRITE BIT(2) +#define STAT_INVALID BIT(1) +#define STAT_WP BIT(0) + +struct field { + int reg; + int shift; + int mask; +}; + +struct supply_info { + const char *name; + int n_voltages; + const int *voltages; + int fixed_voltage; + int n_ilimsels; + const int *ilimsels; + int fixed_ilimsel; + int flags; + struct field enable, voltage, ilimsel; +}; + +struct tps6524x { + struct device *dev; + struct spi_device *spi; + struct mutex lock; + struct regulator_desc desc[N_REGULATORS]; + struct regulator_dev *rdev[N_REGULATORS]; +}; + +static int __read_reg(struct tps6524x *hw, int reg) +{ + int error = 0; + u16 cmd = CMD_READ(reg), in; + u8 status; + struct spi_message m; + struct spi_transfer t[3]; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = &cmd; + t[0].len = 2; + t[0].bits_per_word = 12; + spi_message_add_tail(&t[0], &m); + + t[1].rx_buf = ∈ + t[1].len = 2; + t[1].bits_per_word = 16; + spi_message_add_tail(&t[1], &m); + + t[2].rx_buf = &status; + t[2].len = 1; + t[2].bits_per_word = 4; + spi_message_add_tail(&t[2], &m); + + error = spi_sync(hw->spi, &m); + if (error < 0) + return error; + + dev_dbg(hw->dev, "read reg %d, data %x, status %x\n", + reg, in, status); + + if (!(status & STAT_CLK) || (status & STAT_WRITE)) + return -EIO; + + if (status & STAT_INVALID) + return -EINVAL; + + return in; +} + +static int read_reg(struct tps6524x *hw, int reg) +{ + int ret; + + mutex_lock(&hw->lock); + ret = __read_reg(hw, reg); + mutex_unlock(&hw->lock); + + return ret; +} + +static int __write_reg(struct tps6524x *hw, int reg, int val) +{ + int error = 0; + u16 cmd = CMD_WRITE(reg), out = val; + u8 status; + struct spi_message m; + struct spi_transfer t[3]; + + spi_message_init(&m); + memset(t, 0, sizeof(t)); + + t[0].tx_buf = &cmd; + t[0].len = 2; + t[0].bits_per_word = 12; + spi_message_add_tail(&t[0], &m); + + t[1].tx_buf = &out; + t[1].len = 2; + t[1].bits_per_word = 16; + spi_message_add_tail(&t[1], &m); + + t[2].rx_buf = &status; + t[2].len = 1; + t[2].bits_per_word = 4; + spi_message_add_tail(&t[2], &m); + + error = spi_sync(hw->spi, &m); + if (error < 0) + return error; + + dev_dbg(hw->dev, "wrote reg %d, data %x, status %x\n", + reg, out, status); + + if (!(status & STAT_CLK) || !(status & STAT_WRITE)) + return -EIO; + + if (status & (STAT_INVALID | STAT_WP)) + return -EINVAL; + + return error; +} + +static int __rmw_reg(struct tps6524x *hw, int reg, int mask, int val) +{ + int ret; + + ret = __read_reg(hw, reg); + if (ret < 0) + return ret; + + ret &= ~mask; + ret |= val; + + ret = __write_reg(hw, reg, ret); + + return (ret < 0) ? ret : 0; +} + +static int rmw_protect(struct tps6524x *hw, int reg, int mask, int val) +{ + int ret; + + mutex_lock(&hw->lock); + + ret = __write_reg(hw, REG_WRITE_ENABLE, 1); + if (ret) { + dev_err(hw->dev, "failed to set write enable\n"); + goto error; + } + + ret = __rmw_reg(hw, reg, mask, val); + if (ret) + dev_err(hw->dev, "failed to rmw register %d\n", reg); + + ret = __write_reg(hw, REG_WRITE_ENABLE, 0); + if (ret) { + dev_err(hw->dev, "failed to clear write enable\n"); + goto error; + } + +error: + mutex_unlock(&hw->lock); + + return ret; +} + +static int read_field(struct tps6524x *hw, const struct field *field) +{ + int tmp; + + tmp = read_reg(hw, field->reg); + if (tmp < 0) + return tmp; + + return (tmp >> field->shift) & field->mask; +} + +static int write_field(struct tps6524x *hw, const struct field *field, + int val) +{ + if (val & ~field->mask) + return -EOVERFLOW; + + return rmw_protect(hw, field->reg, + field->mask << field->shift, + val << field->shift); +} + +static const int dcdc1_voltages[] = { + 800000, 825000, 850000, 875000, + 900000, 925000, 950000, 975000, + 1000000, 1025000, 1050000, 1075000, + 1100000, 1125000, 1150000, 1175000, + 1200000, 1225000, 1250000, 1275000, + 1300000, 1325000, 1350000, 1375000, + 1400000, 1425000, 1450000, 1475000, + 1500000, 1525000, 1550000, 1575000, +}; + +static const int dcdc2_voltages[] = { + 1400000, 1450000, 1500000, 1550000, + 1600000, 1650000, 1700000, 1750000, + 1800000, 1850000, 1900000, 1950000, + 2000000, 2050000, 2100000, 2150000, + 2200000, 2250000, 2300000, 2350000, + 2400000, 2450000, 2500000, 2550000, + 2600000, 2650000, 2700000, 2750000, + 2800000, 2850000, 2900000, 2950000, +}; + +static const int dcdc3_voltages[] = { + 2400000, 2450000, 2500000, 2550000, 2600000, + 2650000, 2700000, 2750000, 2800000, 2850000, + 2900000, 2950000, 3000000, 3050000, 3100000, + 3150000, 3200000, 3250000, 3300000, 3350000, + 3400000, 3450000, 3500000, 3550000, 3600000, +}; + +static const int ldo1_voltages[] = { + 4300000, 4350000, 4400000, 4450000, + 4500000, 4550000, 4600000, 4650000, + 4700000, 4750000, 4800000, 4850000, + 4900000, 4950000, 5000000, 5050000, +}; + +static const int ldo2_voltages[] = { + 1100000, 1150000, 1200000, 1250000, + 1300000, 1700000, 1750000, 1800000, + 1850000, 1900000, 3150000, 3200000, + 3250000, 3300000, 3350000, 3400000, +}; + +static const int ldo_ilimsel[] = { + 400000, 1500000 +}; + +static const int usb_ilimsel[] = { + 200000, 400000, 800000, 1000000 +}; + +#define __MK_FIELD(_reg, _mask, _shift) \ + { .reg = (_reg), .mask = (_mask), .shift = (_shift), } + +static const struct supply_info supply_info[N_REGULATORS] = { + { + .name = "DCDC1", + .flags = FIXED_ILIMSEL, + .n_voltages = ARRAY_SIZE(dcdc1_voltages), + .voltages = dcdc1_voltages, + .fixed_ilimsel = 2400000, + .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, + DCDCDCDC1_EN_SHIFT), + .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, + DCDC_VDCDC1_SHIFT), + }, + { + .name = "DCDC2", + .flags = FIXED_ILIMSEL, + .n_voltages = ARRAY_SIZE(dcdc2_voltages), + .voltages = dcdc2_voltages, + .fixed_ilimsel = 1200000, + .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, + DCDCDCDC2_EN_SHIFT), + .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, + DCDC_VDCDC2_SHIFT), + }, + { + .name = "DCDC3", + .flags = FIXED_ILIMSEL, + .n_voltages = ARRAY_SIZE(dcdc3_voltages), + .voltages = dcdc3_voltages, + .fixed_ilimsel = 1200000, + .enable = __MK_FIELD(REG_DCDC_EN, DCDCDCDC_EN_MASK, + DCDCDCDC3_EN_SHIFT), + .voltage = __MK_FIELD(REG_DCDC_SET, DCDC_VDCDC_MASK, + DCDC_VDCDC3_SHIFT), + }, + { + .name = "LDO1", + .n_voltages = ARRAY_SIZE(ldo1_voltages), + .voltages = ldo1_voltages, + .n_ilimsels = ARRAY_SIZE(ldo_ilimsel), + .ilimsels = ldo_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_LDO1_SHIFT), + .voltage = __MK_FIELD(REG_LDO_SET, LDO_VSEL_MASK, + LDO1_VSEL_SHIFT), + .ilimsel = __MK_FIELD(REG_LDO_SET, LDO_ILIM_MASK, + LDO1_ILIM_SHIFT), + }, + { + .name = "LDO2", + .n_voltages = ARRAY_SIZE(ldo2_voltages), + .voltages = ldo2_voltages, + .n_ilimsels = ARRAY_SIZE(ldo_ilimsel), + .ilimsels = ldo_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_LDO2_SHIFT), + .voltage = __MK_FIELD(REG_LDO_SET, LDO_VSEL_MASK, + LDO2_VSEL_SHIFT), + .ilimsel = __MK_FIELD(REG_LDO_SET, LDO_ILIM_MASK, + LDO2_ILIM_SHIFT), + }, + { + .name = "USB", + .flags = FIXED_VOLTAGE, + .fixed_voltage = 5000000, + .n_ilimsels = ARRAY_SIZE(usb_ilimsel), + .ilimsels = usb_ilimsel, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_USB_SHIFT), + .ilimsel = __MK_FIELD(REG_USB, USB_ILIM_MASK, + USB_ILIM_SHIFT), + }, + { + .name = "LCD", + .flags = FIXED_VOLTAGE | FIXED_ILIMSEL, + .fixed_voltage = 5000000, + .fixed_ilimsel = 400000, + .enable = __MK_FIELD(REG_BLOCK_EN, BLOCK_MASK, + BLOCK_LCD_SHIFT), + }, +}; + +static int list_voltage(struct regulator_dev *rdev, unsigned selector) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_VOLTAGE) + return selector ? -EINVAL : info->fixed_voltage; + + if (selector < 0 || selector >= info->n_voltages) + return -EINVAL; + + return info->voltages[selector]; +} + +static int set_voltage(struct regulator_dev *rdev, int min_uV, int max_uV) +{ + const struct supply_info *info; + struct tps6524x *hw; + int i; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_VOLTAGE) + return -EINVAL; + + for (i = 0; i < info->n_voltages; i++) + if (min_uV <= info->voltages[i] && + max_uV >= info->voltages[i]) + break; + + if (i >= info->n_voltages) + return -EINVAL; + + return write_field(hw, &info->voltage, i); +} + +static int get_voltage(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + int ret; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_VOLTAGE) + return info->fixed_voltage; + + ret = read_field(hw, &info->voltage); + if (ret < 0) + return ret; + if (WARN_ON(ret >= info->n_voltages)) + return -EIO; + + return info->voltages[ret]; +} + +static int set_current_limit(struct regulator_dev *rdev, int min_uA, + int max_uA) +{ + const struct supply_info *info; + struct tps6524x *hw; + int i; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_ILIMSEL) + return -EINVAL; + + for (i = 0; i < info->n_ilimsels; i++) + if (min_uA <= info->ilimsels[i] && + max_uA >= info->ilimsels[i]) + break; + + if (i >= info->n_ilimsels) + return -EINVAL; + + return write_field(hw, &info->ilimsel, i); +} + +static int get_current_limit(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + int ret; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + if (info->flags & FIXED_ILIMSEL) + return info->fixed_ilimsel; + + ret = read_field(hw, &info->ilimsel); + if (ret < 0) + return ret; + if (WARN_ON(ret >= info->n_ilimsels)) + return -EIO; + + return info->ilimsels[ret]; +} + +static int enable_supply(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + return write_field(hw, &info->enable, 1); +} + +static int disable_supply(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + return write_field(hw, &info->enable, 0); +} + +static int is_supply_enabled(struct regulator_dev *rdev) +{ + const struct supply_info *info; + struct tps6524x *hw; + + hw = rdev_get_drvdata(rdev); + info = &supply_info[rdev_get_id(rdev)]; + + return read_field(hw, &info->enable); +} + +static struct regulator_ops regulator_ops = { + .is_enabled = is_supply_enabled, + .enable = enable_supply, + .disable = disable_supply, + .get_voltage = get_voltage, + .set_voltage = set_voltage, + .list_voltage = list_voltage, + .set_current_limit = set_current_limit, + .get_current_limit = get_current_limit, +}; + +static int __devexit pmic_remove(struct spi_device *spi) +{ + struct tps6524x *hw = spi_get_drvdata(spi); + int i; + + if (!hw) + return 0; + for (i = 0; i < N_REGULATORS; i++) { + if (hw->rdev[i]) + regulator_unregister(hw->rdev[i]); + hw->rdev[i] = NULL; + } + spi_set_drvdata(spi, NULL); + kfree(hw); + return 0; +} + +static int __devinit pmic_probe(struct spi_device *spi) +{ + struct tps6524x *hw; + struct device *dev = &spi->dev; + const struct supply_info *info = supply_info; + struct regulator_init_data *init_data; + int ret = 0, i; + + init_data = dev->platform_data; + if (!init_data) { + dev_err(dev, "could not find regulator platform data\n"); + return -EINVAL; + } + + hw = kzalloc(sizeof(struct tps6524x), GFP_KERNEL); + if (!hw) { + dev_err(dev, "cannot allocate regulator private data\n"); + return -ENOMEM; + } + spi_set_drvdata(spi, hw); + + memset(hw, 0, sizeof(struct tps6524x)); + hw->dev = dev; + hw->spi = spi_dev_get(spi); + mutex_init(&hw->lock); + + for (i = 0; i < N_REGULATORS; i++, info++, init_data++) { + hw->desc[i].name = info->name; + hw->desc[i].id = i; + hw->desc[i].n_voltages = info->n_voltages; + hw->desc[i].ops = ®ulator_ops; + hw->desc[i].type = REGULATOR_VOLTAGE; + hw->desc[i].owner = THIS_MODULE; + + if (info->flags & FIXED_VOLTAGE) + hw->desc[i].n_voltages = 1; + + hw->rdev[i] = regulator_register(&hw->desc[i], dev, + init_data, hw); + if (IS_ERR(hw->rdev[i])) { + ret = PTR_ERR(hw->rdev[i]); + hw->rdev[i] = NULL; + goto fail; + } + } + + return 0; + +fail: + pmic_remove(spi); + return ret; +} + +static struct spi_driver pmic_driver = { + .probe = pmic_probe, + .remove = __devexit_p(pmic_remove), + .driver = { + .name = "tps6524x", + .owner = THIS_MODULE, + }, +}; + +static int __init pmic_driver_init(void) +{ + return spi_register_driver(&pmic_driver); +} +module_init(pmic_driver_init); + +static void __exit pmic_driver_exit(void) +{ + spi_unregister_driver(&pmic_driver); +} +module_exit(pmic_driver_exit); + +MODULE_DESCRIPTION("TPS6524X PMIC Driver"); +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("spi:tps6524x"); -- 1.7.1 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From manjunath.hadli at ti.com Fri Jul 8 11:08:33 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 08 Jul 2011 16:08:33 -0000 Subject: [PATCH v15 01/13] davinci vpbe: V4L2 display driver for DM644X SoC Message-ID: <1300197408-3850-1-git-send-email-manjunath.hadli@ti.com> This is the display driver for Texas Instruments's DM644X family SoC. This patch contains the main implementation of the driver with the V4L2 interface. The driver is implements the streaming model with support for both kernel allocated buffers and user pointers. It also implements all of the necessary IOCTLs necessary and supported by the video display device. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/vpbe_display.c | 2084 ++++++++++++++++++++++++++++ include/media/davinci/vpbe_display.h | 146 ++ include/media/davinci/vpbe_types.h | 91 ++ 3 files changed, 2321 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpbe_display.c create mode 100644 include/media/davinci/vpbe_display.h create mode 100644 include/media/davinci/vpbe_types.h diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c new file mode 100644 index 0000000..05ecd74 --- /dev/null +++ b/drivers/media/video/davinci/vpbe_display.c @@ -0,0 +1,2084 @@ +/* + * 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 as + * published by the Free Software Foundation version 2. + * + * This program is distributed 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vpbe_venc_regs.h" + +#define VPBE_DISPLAY_DRIVER "vpbe-v4l2" + +static int debug; +static u32 video2_numbuffers = 3; +static u32 video3_numbuffers = 3; + +#define VPBE_DISPLAY_SD_BUF_SIZE (720*576*2) +#define VPBE_DEFAULT_NUM_BUFS 3 + +static u32 video2_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; +static u32 video3_bufsize = VPBE_DISPLAY_SD_BUF_SIZE; + +module_param(video2_numbuffers, uint, S_IRUGO); +module_param(video3_numbuffers, uint, S_IRUGO); +module_param(video2_bufsize, uint, S_IRUGO); +module_param(video3_bufsize, uint, S_IRUGO); +module_param(debug, int, 0644); + +static struct buf_config_params display_buf_config_params = { + .min_numbuffers = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[0] = VPBE_DEFAULT_NUM_BUFS, + .numbuffers[1] = VPBE_DEFAULT_NUM_BUFS, + .min_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .min_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[0] = VPBE_DISPLAY_SD_BUF_SIZE, + .layer_bufsize[1] = VPBE_DISPLAY_SD_BUF_SIZE, +}; + +static struct vpbe_device *vpbe_dev; +static struct osd_state *osd_device; +static int vpbe_display_nr[] = { 2, 3 }; + +static struct v4l2_capability vpbe_display_videocap = { + .driver = VPBE_DISPLAY_DRIVER, + .bus_info = "platform", + .version = VPBE_DISPLAY_VERSION_CODE, + .capabilities = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING, +}; + +static u8 layer_first_int[VPBE_DISPLAY_MAX_DEVICES]; + +static int venc_is_second_field() +{ + int ret = 0; + int val; + ret = v4l2_subdev_call(vpbe_dev->venc, + core, + ioctl, + VENC_GET_FLD, + &val); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in getting Field ID 0\n"); + } + return val; +} + +/* + * vpbe_display_isr() + * ISR function. It changes status of the displayed buffer, takes next buffer + * from the queue and sets its address in VPBE registers + */ +static void vpbe_display_isr(unsigned int event, void *disp_obj) +{ + unsigned long jiffies_time = get_jiffies_64(); + struct timeval timevalue; + int i, fid; + unsigned long addr = 0; + struct vpbe_display_obj *layer = NULL; + struct vpbe_display *disp_dev = (struct vpbe_display *)disp_obj; + + /* Convert time represention from jiffies to timeval */ + jiffies_to_timeval(jiffies_time, &timevalue); + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + layer = disp_dev->dev[i]; + /* If streaming is started in this layer */ + if (!layer->started) + continue; + /* Check the field format */ + if ((V4L2_FIELD_NONE == layer->pix_fmt.field) && + (event & OSD_END_OF_FRAME)) { + /* Progressive mode */ + if (layer_first_int[i]) + layer_first_int[i] = 0; + continue; + /* + * Mark status of the cur_frm to + * done and unlock semaphore on it + */ + + if (layer->cur_frm != layer->next_frm) { + layer->cur_frm->ts = timevalue; + layer->cur_frm->state = VIDEOBUF_DONE; + wake_up_interruptible( + &layer->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + layer->cur_frm = layer->next_frm; + } + /* Get the next buffer from buffer queue */ + spin_lock(&disp_dev->dma_queue_lock); + if (!list_empty(&layer->dma_queue)) { + layer->next_frm = + list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove that buffer from the buffer queue */ + list_del(&layer->next_frm->queue); + /* Mark status of the buffer as active */ + layer->next_frm->state = VIDEOBUF_ACTIVE; + + addr = videobuf_to_dma_contig(layer->next_frm); + osd_device->ops.start_layer(osd_device, + layer->layer_info.id, + addr, disp_dev->cbcr_ofst); + } + spin_unlock(&disp_dev->dma_queue_lock); + } else { + /* + * Interlaced mode + * If it is first interrupt, ignore it + */ + if (layer_first_int[i]) { + layer_first_int[i] = 0; + return; + } + + layer->field_id ^= 1; + if (event & OSD_FIRST_FIELD) + fid = 0; + else if (event & OSD_SECOND_FIELD) + fid = 1; + else + return; + + /* + * If field id does not match with stored + * field id + */ + if (fid != layer->field_id) { + /* Make them in sync */ + if (0 == fid) + layer->field_id = fid; + + return; + } + /* + * device field id and local field id are + * in sync. If this is even field + */ + if (0 == fid) { + if (layer->cur_frm == layer->next_frm) + continue; + /* + * one frame is displayed If next frame is + * available, release cur_frm and move on + * copy frame display time + */ + layer->cur_frm->ts = timevalue; + /* Change status of the cur_frm */ + layer->cur_frm->state = VIDEOBUF_DONE; + /* unlock semaphore on cur_frm */ + wake_up_interruptible(&layer->cur_frm->done); + /* Make cur_frm pointing to next_frm */ + layer->cur_frm = layer->next_frm; + } else if (1 == fid) { /* odd field */ + + if (list_empty(&layer->dma_queue) + || (layer->cur_frm != layer->next_frm)) + continue; + + /* + * one field is displayed configure + * the next frame if it is available + * otherwise hold on current frame + * Get next from the buffer queue + */ + spin_lock(&disp_dev->dma_queue_lock); + layer->next_frm = list_entry( + layer->dma_queue.next, + struct videobuf_buffer, + queue); + + /* Remove that from the buffer queue */ + list_del(&layer->next_frm->queue); + + /* Mark state of the frame to active */ + layer->next_frm->state = VIDEOBUF_ACTIVE; + addr = videobuf_to_dma_contig(layer->next_frm); + osd_device->ops.start_layer(osd_device, + layer->layer_info.id, + addr, + disp_dev->cbcr_ofst); + spin_unlock(&disp_dev->dma_queue_lock); + } + } + } +} + +/* interrupt service routine */ +static irqreturn_t venc_isr(int irq, void *arg) +{ + static unsigned last_event; + unsigned event = 0; + + if (venc_is_second_field()) + event |= VENC_SECOND_FIELD; + else + event |= VENC_FIRST_FIELD; + + if (event == (last_event & ~VENC_END_OF_FRAME)) { + /* + * If the display is non-interlaced, then we need to flag the + * end-of-frame event at every interrupt regardless of the + * value of the FIDST bit. We can conclude that the display is + * non-interlaced if the value of the FIDST bit is unchanged + * from the previous interrupt. + */ + event |= VENC_END_OF_FRAME; + } else if (event == VENC_SECOND_FIELD) { + /* end-of-frame for interlaced display */ + event |= VENC_END_OF_FRAME; + } + last_event = event; + + vpbe_display_isr(event, arg); + return IRQ_HANDLED; +} + +/* + * vpbe_buffer_prepare() + * This is the callback function called from videobuf_qbuf() function + * the buffer is prepared and user space virtual address is converted into + * physical address + */ +static int vpbe_buffer_prepare(struct videobuf_queue *q, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct vpbe_fh *fh = q->priv_data; + struct vpbe_display_obj *layer = fh->layer; + unsigned long addr; + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe_buffer_prepare\n"); + + /* If buffer is not initialized, initialize it */ + if (VIDEOBUF_NEEDS_INIT == vb->state) { + vb->width = layer->pix_fmt.width; + vb->height = layer->pix_fmt.height; + vb->size = layer->pix_fmt.sizeimage; + vb->field = field; + + ret = videobuf_iolock(q, vb, NULL); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Failed to map \ + user address\n"); + return -EINVAL; + } + + addr = videobuf_to_dma_contig(vb); + + if (q->streaming) { + if (!IS_ALIGNED(addr, 8)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "buffer_prepare:offset is \ + not aligned to 32 bytes\n"); + return -EINVAL; + } + } + vb->state = VIDEOBUF_PREPARED; + } + return 0; +} + +/* + * vpbe_buffer_setup() + * This function allocates memory for the buffers + */ +static int vpbe_buffer_setup(struct videobuf_queue *q, + unsigned int *count, + unsigned int *size) +{ + /* Get the file handle object and layer object */ + struct vpbe_fh *fh = q->priv_data; + struct vpbe_display_obj *layer = fh->layer; + int buf_size; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_buffer_setup\n"); + + *size = layer->pix_fmt.sizeimage; + buf_size = + display_buf_config_params.layer_bufsize[layer->device_id]; + /* + * For MMAP, limit the memory allocation as per bootarg + * configured buffer size + */ + if (V4L2_MEMORY_MMAP == layer->memory) + if (*size > buf_size) + *size = buf_size; + + /* Store number of buffers allocated in numbuffer member */ + if (*count < display_buf_config_params.min_numbuffers) + *count = layer->numbuffers = + display_buf_config_params.numbuffers[layer->device_id]; + + return 0; +} + +/* + * vpbe_buffer_queue() + * This function adds the buffer to DMA queue + */ +static void vpbe_buffer_queue(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and layer object */ + struct vpbe_fh *fh = q->priv_data; + struct vpbe_display_obj *layer = fh->layer; + struct vpbe_display *disp = fh->disp_dev; + unsigned long flags; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe_buffer_queue\n"); + + /* add the buffer to the DMA queue */ + spin_lock_irqsave(&disp->dma_queue_lock, flags); + list_add_tail(&vb->queue, &layer->dma_queue); + spin_unlock_irqrestore(&disp->dma_queue_lock, flags); + /* Change state of the buffer */ + vb->state = VIDEOBUF_QUEUED; +} + +/* + * vpbe_buffer_release() + * This function is called from the videobuf layer to free memory allocated to + * the buffers + */ +static void vpbe_buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb) +{ + /* Get the file handle object and layer object */ + struct vpbe_fh *fh = q->priv_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe_buffer_release\n"); + + if (V4L2_MEMORY_USERPTR != layer->memory) + videobuf_dma_contig_free(q, vb); + + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops video_qops = { + .buf_setup = vpbe_buffer_setup, + .buf_prepare = vpbe_buffer_prepare, + .buf_queue = vpbe_buffer_queue, + .buf_release = vpbe_buffer_release, +}; + +static +struct vpbe_display_obj* +_vpbe_display_get_other_win(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer) +{ + enum vpbe_display_device_id thiswin, otherwin; + thiswin = layer->device_id; + + otherwin = (thiswin == VPBE_DISPLAY_DEVICE_0) ? + VPBE_DISPLAY_DEVICE_1 : VPBE_DISPLAY_DEVICE_0; + return disp_dev->dev[otherwin]; +} + +static int vpbe_set_video_display_params(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + unsigned long addr; + int ret = 0; + + addr = videobuf_to_dma_contig(layer->cur_frm); + /* Set address in the display registers */ + osd_device->ops.start_layer(osd_device, + layer->layer_info.id, + addr, + disp_dev->cbcr_ofst); + + ret = osd_device->ops.enable_layer(osd_device, + layer->layer_info.id, 0); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in enabling osd window layer 0\n"); + return -1; + } + + /* Enable the window */ + layer->layer_info.enable = 1; + if (cfg->pixfmt == PIXFMT_NV12) { + struct vpbe_display_obj *otherlayer = + _vpbe_display_get_other_win(disp_dev, layer); + + ret = osd_device->ops.enable_layer(osd_device, + otherlayer->layer_info.id, 1); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in enabling osd window layer 1\n"); + return -1; + } + otherlayer->layer_info.enable = 1; + } + return 0; +} + +static void +vpbe_disp_calculate_scale_factor(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer, + int expected_xsize, int expected_ysize) +{ + struct display_layer_info *layer_info = &layer->layer_info; + struct v4l2_pix_format *pixfmt = &layer->pix_fmt; + struct osd_layer_config *cfg = &layer->layer_info.config; + int h_scale = 0, v_scale = 0, h_exp = 0, v_exp = 0, temp; + v4l2_std_id standard_id = vpbe_dev->current_timings.timings.std_id; + + /* + * Application initially set the image format. Current display + * size is obtained from the vpbe display controller. expected_xsize + * and expected_ysize are set through S_CROP ioctl. Based on this, + * driver will calculate the scale factors for vertical and + * horizontal direction so that the image is displayed scaled + * and expanded. Application uses expansion to display the image + * in a square pixel. Otherwise it is displayed using displays + * pixel aspect ratio.It is expected that application chooses + * the crop coordinates for cropped or scaled display. if crop + * size is less than the image size, it is displayed cropped or + * it is displayed scaled and/or expanded. + * + * to begin with, set the crop window same as expected. Later we + * will override with scaled window size + */ + + cfg->xsize = pixfmt->width; + cfg->ysize = pixfmt->height; + layer_info->h_zoom = ZOOM_X1; /* no horizontal zoom */ + layer_info->v_zoom = ZOOM_X1; /* no horizontal zoom */ + layer_info->h_exp = H_EXP_OFF; /* no horizontal zoom */ + layer_info->v_exp = V_EXP_OFF; /* no horizontal zoom */ + + if (pixfmt->width < expected_xsize) { + h_scale = vpbe_dev->current_timings.xres / pixfmt->width; + if (h_scale < 2) + h_scale = 1; + else if (h_scale >= 4) + h_scale = 4; + else + h_scale = 2; + cfg->xsize *= h_scale; + if (cfg->xsize < expected_xsize) { + if ((standard_id & V4L2_STD_525_60) || + (standard_id & V4L2_STD_625_50)) { + temp = (cfg->xsize * + VPBE_DISPLAY_H_EXP_RATIO_N) / + VPBE_DISPLAY_H_EXP_RATIO_D; + if (temp <= expected_xsize) { + h_exp = 1; + cfg->xsize = temp; + } + } + } + if (h_scale == 2) + layer_info->h_zoom = ZOOM_X2; + else if (h_scale == 4) + layer_info->h_zoom = ZOOM_X4; + if (h_exp) + layer_info->h_exp = H_EXP_9_OVER_8; + } else { + /* no scaling, only cropping. Set display area to crop area */ + cfg->xsize = expected_xsize; + } + + if (pixfmt->height < expected_ysize) { + v_scale = expected_ysize / pixfmt->height; + if (v_scale < 2) + v_scale = 1; + else if (v_scale >= 4) + v_scale = 4; + else + v_scale = 2; + cfg->ysize *= v_scale; + if (cfg->ysize < expected_ysize) { + if ((standard_id & V4L2_STD_625_50)) { + temp = (cfg->ysize * + VPBE_DISPLAY_V_EXP_RATIO_N) / + VPBE_DISPLAY_V_EXP_RATIO_D; + if (temp <= expected_ysize) { + v_exp = 1; + cfg->ysize = temp; + } + } + } + if (v_scale == 2) + layer_info->v_zoom = ZOOM_X2; + else if (v_scale == 4) + layer_info->v_zoom = ZOOM_X4; + if (v_exp) + layer_info->h_exp = V_EXP_6_OVER_5; + } else { + /* no scaling, only cropping. Set display area to crop area */ + cfg->ysize = expected_ysize; + } + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "crop display xsize = %d, ysize = %d\n", + cfg->xsize, cfg->ysize); +} + +static void vpbe_disp_adj_position(struct vpbe_display *disp_dev, + struct vpbe_display_obj *layer, + int top, int left) +{ + struct osd_layer_config *cfg = &layer->layer_info.config; + + cfg->xpos = cfg->ypos = 0; + if (left + cfg->xsize <= vpbe_dev->current_timings.xres) + cfg->xpos = left; + if (top + cfg->ysize <= vpbe_dev->current_timings.yres) + cfg->ypos = top; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "new xpos = %d, ypos = %d\n", + cfg->xpos, cfg->ypos); +} + +static int vpbe_disp_check_window_params(struct vpbe_display *disp_dev, + struct v4l2_rect *c) +{ + if ((c->width == 0) || + ((c->width + c->left) > vpbe_dev->current_timings.xres) || + (c->height == 0) || ((c->height + c->top) > + vpbe_dev->current_timings.yres)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid crop values\n"); + return -1; + } + if ((c->height & 0x1) && (vpbe_dev->current_timings.interlaced)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "window height must be even for interlaced display\n"); + return -1; + } + return 0; +} + +/** + * vpbe_try_format() + * If user application provides width and height, and have bytesperline set + * to zero, driver calculates bytesperline and sizeimage based on hardware + * limits. If application likes to add pads at the end of each line and + * end of the buffer , it can set bytesperline to line size and sizeimage to + * bytesperline * height of the buffer. If driver fills zero for active + * video width and height, and has requested user bytesperline and sizeimage, + * width and height is adjusted to maximum display limit or buffer width + * height which ever is lower + */ +static int vpbe_try_format(struct vpbe_display *disp_dev, + struct v4l2_pix_format *pixfmt, int check) +{ + int min_sizeimage, bpp, min_height = 1, min_width = 32, + max_width, max_height, user_info = 0; + + if ((pixfmt->pixelformat != V4L2_PIX_FMT_UYVY) && + (pixfmt->pixelformat != V4L2_PIX_FMT_NV12)) + /* choose default as V4L2_PIX_FMT_UYVY */ + pixfmt->pixelformat = V4L2_PIX_FMT_UYVY; + + /* Check the field format */ + if (pixfmt->field == V4L2_FIELD_ANY) { + if (vpbe_dev->current_timings.interlaced) + pixfmt->field = V4L2_FIELD_INTERLACED; + else + pixfmt->field = V4L2_FIELD_NONE; + } + + if (pixfmt->field == V4L2_FIELD_INTERLACED) + min_height = 2; + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + bpp = 1; + else + bpp = 2; + + max_width = vpbe_dev->current_timings.xres; + max_height = vpbe_dev->current_timings.yres; + + min_width /= bpp; + + if (!pixfmt->width && !pixfmt->bytesperline) { + v4l2_err(&vpbe_dev->v4l2_dev, "bytesperline and width" + " cannot be zero\n"); + return -EINVAL; + } + + /* if user provided bytesperline, it must provide sizeimage as well */ + if (pixfmt->bytesperline && !pixfmt->sizeimage) { + v4l2_err(&vpbe_dev->v4l2_dev, + "sizeimage must be non zero, when user" + " provides bytesperline\n"); + return -EINVAL; + } + + /* adjust bytesperline as per hardware - multiple of 32 */ + if (!pixfmt->width) + pixfmt->width = pixfmt->bytesperline / bpp; + + if (!pixfmt->bytesperline) + pixfmt->bytesperline = pixfmt->width * bpp; + else + user_info = 1; + pixfmt->bytesperline = ((pixfmt->bytesperline + 31) & ~31); + + if (pixfmt->width < min_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is less than minimum," + "input width = %d, min_width = %d\n", + pixfmt->width, min_width); + return -EINVAL; + } + pixfmt->width = min_width; + } + + if (pixfmt->width > max_width) { + if (check) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is more than maximum," + "input width = %d, max_width = %d\n", + pixfmt->width, max_width); + return -EINVAL; + } + pixfmt->width = max_width; + } + + /* + * If height is zero, then atleast we need to have sizeimage + * to calculate height + */ + if (!pixfmt->height) { + if (user_info) { + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) { + /* + * for NV12 format, sizeimage is y-plane size + * + CbCr plane which is half of y-plane + */ + pixfmt->height = pixfmt->sizeimage / + (pixfmt->bytesperline + + (pixfmt->bytesperline >> 1)); + } else + pixfmt->height = pixfmt->sizeimage/ + pixfmt->bytesperline; + } + } + + if (pixfmt->height > max_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "height is more than maximum," + "input height = %d, max_height = %d\n", + pixfmt->height, max_height); + return -EINVAL; + } + pixfmt->height = max_height; + } + + if (pixfmt->height < min_height) { + if (check && !user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, + "width is less than minimum," + "input height = %d, min_height = %d\n", + pixfmt->height, min_height); + return -EINVAL; + } + pixfmt->height = min_width; + } + + /* if user has not provided bytesperline calculate it based on width */ + if (!user_info) + pixfmt->bytesperline = (((pixfmt->width * bpp) + 31) & ~31); + + if (pixfmt->pixelformat == V4L2_PIX_FMT_NV12) + min_sizeimage = pixfmt->bytesperline * pixfmt->height + + (pixfmt->bytesperline * pixfmt->height >> 1); + else + min_sizeimage = pixfmt->bytesperline * pixfmt->height; + + if (pixfmt->sizeimage < min_sizeimage) { + if (check && user_info) { + v4l2_err(&vpbe_dev->v4l2_dev, "sizeimage is less, %d\n", + min_sizeimage); + return -EINVAL; + } + pixfmt->sizeimage = min_sizeimage; + } + return 0; +} + +static int vpbe_display_g_priority(struct file *file, void *priv, + enum v4l2_priority *p) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + *p = v4l2_prio_max(&layer->prio); + + return 0; +} + +static int vpbe_display_s_priority(struct file *file, void *priv, + enum v4l2_priority p) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + int ret; + + ret = v4l2_prio_change(&layer->prio, &fh->prio, p); + + return ret; +} + +static int vpbe_display_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QUERYCAP, layer id = %d\n", layer->device_id); + *cap = vpbe_display_videocap; + + return 0; +} + +static int vpbe_display_s_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct vpbe_display *disp_dev = video_drvdata(file); + struct osd_layer_config *cfg = &layer->layer_info.config; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_S_CROP, layer id = %d\n", layer->device_id); + + if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + struct v4l2_rect *rect = &crop->c; + + if (rect->top < 0 || rect->left < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + + if (vpbe_disp_check_window_params(disp_dev, rect)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error:S_CROP params\n"); + return -EINVAL; + } + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + + vpbe_disp_calculate_scale_factor(disp_dev, layer, + rect->width, + rect->height); + vpbe_disp_adj_position(disp_dev, layer, rect->top, + rect->left); + ret = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, cfg); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in set layer config:\n"); + return -EINVAL; + } + + /* apply zooming and h or v expansion */ + osd_device->ops.set_zoom(osd_device, + layer->layer_info.id, + layer->layer_info.h_zoom, + layer->layer_info.v_zoom); + ret = osd_device->ops.set_vid_expansion(osd_device, + layer->layer_info.h_exp, + layer->layer_info.v_exp); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in set vid expansion:\n"); + return -EINVAL; + } + + if ((layer->layer_info.h_zoom != ZOOM_X1) || + (layer->layer_info.v_zoom != ZOOM_X1) || + (layer->layer_info.h_exp != H_EXP_OFF) || + (layer->layer_info.v_exp != V_EXP_OFF)) + /* Enable expansion filter */ + osd_device->ops.set_interpolation_filter(osd_device, 1); + else + osd_device->ops.set_interpolation_filter(osd_device, 0); + + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + return -EINVAL; + } + + return ret; +} + +static int vpbe_display_g_crop(struct file *file, void *priv, + struct v4l2_crop *crop) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_G_CROP, layer id = %d\n", + layer->device_id); + + if (crop->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) { + struct v4l2_rect *rect = &crop->c; + if (ret) + return ret; + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + rect->top = cfg->ypos; + rect->left = cfg->xpos; + rect->width = cfg->xsize; + rect->height = cfg->ysize; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buf type\n"); + ret = -EINVAL; + } + + return ret; +} + +static int vpbe_display_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_CROPCAP ioctl\n"); + + cropcap->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + cropcap->bounds.left = 0; + cropcap->bounds.top = 0; + cropcap->bounds.width = vpbe_dev->current_timings.xres; + cropcap->bounds.height = vpbe_dev->current_timings.yres; + cropcap->pixelaspect = vpbe_dev->current_timings.aspect; + cropcap->defrect = cropcap->bounds; + return 0; +} + +static int vpbe_display_g_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_G_FMT, layer id = %d\n", + layer->device_id); + + /* If buffer type is video output */ + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Fill in the information about format */ + *pixfmt = layer->pix_fmt; + } else { + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } + + return 0; +} + +static int vpbe_display_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + unsigned int index = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_ENUM_FMT, layer id = %d\n", + layer->device_id); + if (fmt->index > 0) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid format index\n"); + return -EINVAL; + } + + /* Fill in the information about format */ + index = fmt->index; + memset(fmt, 0, sizeof(*fmt)); + fmt->index = index; + fmt->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + if (index == 0) { + strcpy(fmt->description, "YUV 4:2:2 - UYVY"); + fmt->pixelformat = V4L2_PIX_FMT_UYVY; + } else if (index == 1) { + strcpy(fmt->description, "Y/CbCr 4:2:0"); + fmt->pixelformat = V4L2_PIX_FMT_NV12; + } + return 0; +} + +static int vpbe_display_s_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_S_FMT, layer id = %d\n", + layer->device_id); + + /* If streaming is started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != fmt->type) { + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; + } else { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Check for valid pixel format */ + ret = vpbe_try_format(disp_dev, pixfmt, 1); + if (ret) + return ret; + + /* YUV420 is requested, check availability of the + other video window */ + + layer->pix_fmt = *pixfmt; + + /* Get osd layer config */ + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + /* Store the pixel format in the layer object */ + cfg->xsize = pixfmt->width; + cfg->ysize = pixfmt->height; + cfg->line_length = pixfmt->bytesperline; + cfg->ypos = 0; + cfg->xpos = 0; + cfg->interlaced = vpbe_dev->current_timings.interlaced; + + /* Change of the default pixel format for both video windows */ + if (V4L2_PIX_FMT_NV12 == pixfmt->pixelformat) { + struct vpbe_display_obj *otherlayer; + cfg->pixfmt = PIXFMT_NV12; + otherlayer = _vpbe_display_get_other_win(disp_dev, + layer); + otherlayer->layer_info.config.pixfmt = PIXFMT_NV12; + } + + /* Set the layer config in the osd window */ + ret = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, cfg); + if (ret < 0) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Error in S_FMT params:\n"); + return -EINVAL; + } + + /* Readback and fill the local copy of current pix format */ + osd_device->ops.get_layer_config(osd_device, + layer->layer_info.id, cfg); + + /* verify if readback values are as expected */ + if (layer->pix_fmt.width != cfg->xsize || + layer->pix_fmt.height != cfg->ysize || + layer->pix_fmt.bytesperline != layer->layer_info. + config.line_length || (cfg->interlaced && + layer->pix_fmt.field != V4L2_FIELD_INTERLACED) || + (!cfg->interlaced && layer->pix_fmt.field != + V4L2_FIELD_NONE)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "mismatch:layer conf params:\n"); + return -EINVAL; + } + } + + return 0; +} + +static int vpbe_display_try_fmt(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_TRY_FMT\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT == fmt->type) { + struct v4l2_pix_format *pixfmt = &fmt->fmt.pix; + /* Check for valid field format */ + return vpbe_try_format(disp_dev, pixfmt, 0); + } + v4l2_err(&vpbe_dev->v4l2_dev, "invalid type\n"); + return -EINVAL; +} + +/** + * vpbe_display_s_std - Set the given standard in the encoder + * + * Sets the standard if supported by the current encoder. Return the status. + * 0 - success & -EINVAL on error + */ +static int vpbe_display_s_std(struct file *file, void *priv, + v4l2_std_id *std_id) +{ + struct vpbe_fh *fh = priv; + struct vpbe_display_obj *layer = fh->layer; + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_STD\n"); + + /* If streaming is started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + if (NULL != vpbe_dev->ops.s_std) { + ret = vpbe_dev->ops.s_std(vpbe_dev, std_id); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Failed to set standard for sub devices\n"); + return -EINVAL; + } + } + return 0; +} + +/** + * vpbe_display_g_std - Get the standard in the current encoder + * + * Get the standard in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int vpbe_display_g_std(struct file *file, void *priv, + v4l2_std_id *std_id) +{ + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_STD\n"); + + /* Get the standard from the current encoder */ + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { + *std_id = vpbe_dev->current_timings.timings.std_id; + return 0; + } + return -EINVAL; +} + +/** + * vpbe_display_enum_output - enumerate outputs + * + * Enumerates the outputs available at the vpbe display + * returns the status, -EINVAL if end of output list + */ +static int vpbe_display_enum_output(struct file *file, void *priv, + struct v4l2_output *output) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_OUTPUT\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_outputs) { + ret = vpbe_dev->ops.enum_outputs(vpbe_dev, output); + if (ret) { + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "Failed to enumerate outputs\n"); + return -EINVAL; + } + } + return 0; +} + +/** + * vpbe_display_s_output - Set output to + * the output specified by the index + */ +static int vpbe_display_s_output(struct file *file, void *priv, + unsigned int i) +{ + struct vpbe_fh *fh = priv; + struct vpbe_display_obj *layer = fh->layer; + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_OUTPUT\n"); + /* If streaming is started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + if (NULL != vpbe_dev->ops.set_output) { + ret = vpbe_dev->ops.set_output(vpbe_dev, i); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Failed to set output for sub devices\n"); + return -EINVAL; + } + } + return ret; +} + +/** + * vpbe_display_g_output - Get output from subdevice + * for a given by the index + */ +static int vpbe_display_g_output(struct file *file, void *priv, + unsigned int *i) +{ + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_OUTPUT\n"); + /* Get the standard from the current encoder */ + *i = vpbe_dev->current_out_index; + + return 0; +} + +/** + * vpbe_display_enum_dv_presets - Enumerate the dv presets + * + * enum the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int +vpbe_display_enum_dv_presets(struct file *file, void *priv, + struct v4l2_dv_enum_preset *preset) +{ + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_ENUM_DV_PRESETS\n"); + + /* Enumerate outputs */ + + if (NULL != vpbe_dev->ops.enum_dv_presets) { + ret = vpbe_dev->ops.enum_dv_presets(vpbe_dev, preset); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Failed to enumerate dv presets info\n"); + return -EINVAL; + } + } + + return ret; +} + +/** + * vpbe_display_s_dv_preset - Set the dv presets + * + * Set the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int +vpbe_display_s_dv_preset(struct file *file, void *priv, + struct v4l2_dv_preset *preset) +{ + struct vpbe_fh *fh = priv; + struct vpbe_display_obj *layer = fh->layer; + int ret = 0; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_S_DV_PRESETS\n"); + + + /* If streaming is started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "Streaming is started\n"); + return -EBUSY; + } + + /* Set the given standard in the encoder */ + if (NULL != vpbe_dev->ops.s_dv_preset) { + ret = vpbe_dev->ops.s_dv_preset(vpbe_dev, preset); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Failed to set the dv presets info\n"); + return -EINVAL; + } + } + /* set the current norm to zero to be consistent. If STD is used + * v4l2 layer will set the norm properly on successful s_std call + */ + layer->video_dev->current_norm = 0; + return ret; +} + +/** + * vpbe_display_g_dv_preset - Set the dv presets + * + * Get the preset in the current encoder. Return the status. 0 - success + * -EINVAL on error + */ +static int +vpbe_display_g_dv_preset(struct file *file, void *priv, + struct v4l2_dv_preset *dv_preset) +{ + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_G_DV_PRESETS\n"); + + /* Get the given standard in the encoder */ + + if (vpbe_dev->current_timings.timings_type & + VPBE_ENC_DV_PRESET) { + dv_preset->preset = + vpbe_dev->current_timings.timings.dv_preset; + } else { + return -EINVAL; + } + return 0; +} + +static int vpbe_display_streamoff(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_STREAMOFF,layer id = %d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* If io is allowed for this file handle, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); + return -EACCES; + } + + /* If streaming is not started, return error */ + if (!layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "streaming not started in layer" + " id = %d\n", layer->device_id); + return -EINVAL; + } + + osd_device->ops.disable_layer(osd_device, + layer->layer_info.id); + layer->started = 0; + ret = videobuf_streamoff(&layer->buffer_queue); + + return ret; +} + +static int vpbe_display_streamon(struct file *file, void *priv, + enum v4l2_buf_type buf_type) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer = fh->layer; + + osd_device->ops.disable_layer(osd_device, + layer->layer_info.id); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "VIDIOC_STREAMON, layerid=%d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf_type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* If file handle is not allowed IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); + return -EACCES; + } + /* If Streaming is already started, return error */ + if (layer->started) { + v4l2_err(&vpbe_dev->v4l2_dev, "layer is already streaming\n"); + return -EBUSY; + } + + /* + * Call videobuf_streamon to start streaming + * in videobuf + */ + ret = videobuf_streamon(&layer->buffer_queue); + if (ret) { + v4l2_err(&vpbe_dev->v4l2_dev, + "error in videobuf_streamon\n"); + return ret; + } + /* If buffer queue is empty, return error */ + if (list_empty(&layer->dma_queue)) { + v4l2_err(&vpbe_dev->v4l2_dev, "buffer queue is empty\n"); + goto streamoff; + } + /* Get the next frame from the buffer queue */ + layer->next_frm = layer->cur_frm = list_entry(layer->dma_queue.next, + struct videobuf_buffer, queue); + /* Remove buffer from the buffer queue */ + list_del(&layer->cur_frm->queue); + /* Mark state of the current frame to active */ + layer->cur_frm->state = VIDEOBUF_ACTIVE; + /* Initialize field_id and started member */ + layer->field_id = 0; + + /* Set parameters in OSD and VENC */ + ret = vpbe_set_video_display_params(disp_dev, layer); + if (ret < 0) + goto streamoff; + + /* + * if request format is yuv420 semiplanar, need to + * enable both video windows + */ + layer->started = 1; + + layer_first_int[layer->device_id] = 1; + + return ret; +streamoff: + ret = videobuf_streamoff(&layer->buffer_queue); + return ret; +} + +static int vpbe_display_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_DQBUF, layer id = %d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + /* If this file handle is not allowed to do IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); + return -EACCES; + } + if (file->f_flags & O_NONBLOCK) + /* Call videobuf_dqbuf for non blocking mode */ + ret = videobuf_dqbuf(&layer->buffer_queue, buf, 1); + else + /* Call videobuf_dqbuf for blocking mode */ + ret = videobuf_dqbuf(&layer->buffer_queue, buf, 0); + return ret; +} + +static int vpbe_display_qbuf(struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QBUF, layer id = %d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != p->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* If this file handle is not allowed to do IO, return error */ + if (!fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "No io_allowed\n"); + return -EACCES; + } + + return videobuf_qbuf(&layer->buffer_queue, p); +} + +static int vpbe_display_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "VIDIOC_QUERYBUF, layer id = %d\n", + layer->device_id); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != buf->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* Call videobuf_querybuf to get information */ + ret = videobuf_querybuf(&layer->buffer_queue, buf); + + return ret; +} + +static int vpbe_display_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req_buf) +{ + int ret = 0; + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_reqbufs\n"); + + if (V4L2_BUF_TYPE_VIDEO_OUTPUT != req_buf->type) { + v4l2_err(&vpbe_dev->v4l2_dev, "Invalid buffer type\n"); + return -EINVAL; + } + + /* If io users of the layer is not zero, return error */ + if (0 != layer->io_usrs) { + v4l2_err(&vpbe_dev->v4l2_dev, "not IO user\n"); + return -EBUSY; + } + /* Initialize videobuf queue as per the buffer type */ + videobuf_queue_dma_contig_init(&layer->buffer_queue, + &video_qops, + vpbe_dev->pdev, + &layer->irqlock, + V4L2_BUF_TYPE_VIDEO_OUTPUT, + layer->pix_fmt.field, + sizeof(struct videobuf_buffer), + fh, NULL); + + /* Set io allowed member of file handle to TRUE */ + fh->io_allowed = 1; + /* Increment io usrs member of layer object to 1 */ + layer->io_usrs = 1; + /* Store type of memory requested in layer object */ + layer->memory = req_buf->memory; + /* Initialize buffer queue */ + INIT_LIST_HEAD(&layer->dma_queue); + /* Allocate buffers */ + ret = videobuf_reqbufs(&layer->buffer_queue, req_buf); + + return ret; +} + +/* + * vpbe_display_mmap() + * It is used to map kernel space buffers into user spaces + */ +static int vpbe_display_mmap(struct file *filep, struct vm_area_struct *vma) +{ + /* Get the layer object and file handle object */ + struct vpbe_fh *fh = filep->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_mmap\n"); + return videobuf_mmap_mapper(&layer->buffer_queue, vma); +} + +/* vpbe_display_poll(): It is used for select/poll system call + */ +static unsigned int vpbe_display_poll(struct file *filep, poll_table *wait) +{ + unsigned int err = 0; + struct vpbe_fh *fh = filep->private_data; + struct vpbe_display_obj *layer = fh->layer; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_poll\n"); + if (layer->started) + err = videobuf_poll_stream(filep, &layer->buffer_queue, wait); + return err; +} + +static int vpbe_display_cfg_layer_default(enum vpbe_display_device_id id, + struct vpbe_display *disp_dev) +{ + int err = 0; + struct osd_layer_config *layer_config; + struct vpbe_display_obj *layer = disp_dev->dev[id]; + struct osd_layer_config *cfg = &layer->layer_info.config; + + /* First claim the layer for this device */ + err = osd_device->ops.request_layer(osd_device, + layer->layer_info.id); + if (err < 0) { + /* Couldn't get layer */ + v4l2_err(&vpbe_dev->v4l2_dev, + "Display Manager failed to allocate layer\n"); + return -EBUSY; + } + + layer_config = cfg; + /* Set the default image and crop values */ + layer_config->pixfmt = PIXFMT_YCbCrI; + layer->pix_fmt.pixelformat = V4L2_PIX_FMT_UYVY; + layer->pix_fmt.bytesperline = layer_config->line_length = + vpbe_dev->current_timings.xres * 2; + + layer->pix_fmt.width = layer_config->xsize = + vpbe_dev->current_timings.xres; + layer->pix_fmt.height = layer_config->ysize = + vpbe_dev->current_timings.yres; + layer->pix_fmt.sizeimage = + layer->pix_fmt.bytesperline * layer->pix_fmt.height; + layer_config->xpos = 0; + layer_config->ypos = 0; + layer_config->interlaced = vpbe_dev->current_timings.interlaced; + + /* + * turn off ping-pong buffer and field inversion to fix + * the image shaking problem in 1080I mode + */ + + if (cfg->interlaced) + layer->pix_fmt.field = V4L2_FIELD_INTERLACED; + else + layer->pix_fmt.field = V4L2_FIELD_NONE; + + err = osd_device->ops.set_layer_config(osd_device, + layer->layer_info.id, + layer_config); + if (err < 0) { + /* Couldn't set layer */ + v4l2_err(&vpbe_dev->v4l2_dev, + "Display Manager failed to set osd layer\n"); + return -EBUSY; + } + + return 0; +} + +/* + * vpbe_display_open() + * It creates object of file handle structure and stores it in private_data + * member of filepointer + */ +static int vpbe_display_open(struct file *file) +{ + int minor = iminor(file->f_path.dentry->d_inode); + struct vpbe_display *disp_dev = video_drvdata(file); + struct vpbe_display_obj *layer; + struct vpbe_fh *fh = NULL; + int found = -1; + int i = 0; + + /* Check for valid minor number */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + layer = disp_dev->dev[i]; + if (minor == layer->video_dev->minor) { + found = i; + break; + } + } + + /* If not found, return error no device */ + if (0 > found) { + v4l2_err(&vpbe_dev->v4l2_dev, "device not found\n"); + return -ENODEV; + } + + /* Allocate memory for the file handle object */ + fh = kmalloc(sizeof(struct vpbe_fh), GFP_KERNEL); + if (fh == NULL) { + v4l2_err(&vpbe_dev->v4l2_dev, + "unable to allocate memory for file handle object\n"); + return -ENOMEM; + } + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe display open plane = %d\n", + layer->device_id); + + /* store pointer to fh in private_data member of filep */ + file->private_data = fh; + fh->layer = layer; + fh->disp_dev = disp_dev; + + if (!layer->usrs) { + /* Configure the default values for the layer */ + if (vpbe_display_cfg_layer_default(layer->device_id, + disp_dev)) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to configure video layer" + " for id = %d\n", layer->device_id); + return -EINVAL; + } + } + /* Increment layer usrs counter */ + layer->usrs++; + /* Set io_allowed member to false */ + fh->io_allowed = 0; + /* Initialize priority of this instance to default priority */ + fh->prio = V4L2_PRIORITY_UNSET; + v4l2_prio_open(&layer->prio, &fh->prio); + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, + "vpbe display device opened successfully\n"); + return 0; +} + +/* + * vpbe_display_release() + * This function deletes buffer queue, frees the buffers and the davinci + * display file * handle + */ +static int vpbe_display_release(struct file *file) +{ + /* Get the layer object and file handle object */ + struct vpbe_fh *fh = file->private_data; + struct vpbe_display_obj *layer = fh->layer; + struct osd_layer_config *cfg = &layer->layer_info.config; + struct vpbe_display *disp_dev = video_drvdata(file); + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_release\n"); + /* If this is doing IO and other layer are not closed */ + if ((layer->usrs != 1) && fh->io_allowed) { + v4l2_err(&vpbe_dev->v4l2_dev, "Close other instances\n"); + return -EAGAIN; + } + + /* if this instance is doing IO */ + if (fh->io_allowed) { + /* Reset io_usrs member of layer object */ + layer->io_usrs = 0; + + osd_device->ops.disable_layer(osd_device, + layer->layer_info.id); + layer->started = 0; + /* Free buffers allocated */ + videobuf_queue_cancel(&layer->buffer_queue); + videobuf_mmap_free(&layer->buffer_queue); + } + + /* Decrement layer usrs counter */ + layer->usrs--; + /* If this file handle has initialize encoder device, reset it */ + if (!layer->usrs) { + if (cfg->pixfmt == PIXFMT_NV12) { + struct vpbe_display_obj *otherlayer; + otherlayer = + _vpbe_display_get_other_win(disp_dev, layer); + osd_device->ops.disable_layer(osd_device, + otherlayer->layer_info.id); + osd_device->ops.release_layer(osd_device, + otherlayer->layer_info.id); + } + osd_device->ops.disable_layer(osd_device, + layer->layer_info.id); + osd_device->ops.release_layer(osd_device, + layer->layer_info.id); + } + /* Close the priority */ + v4l2_prio_close(&layer->prio, fh->prio); + file->private_data = NULL; + + /* Free memory allocated to file handle object */ + kfree(fh); + + disp_dev->cbcr_ofst = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int vpbe_display_g_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + struct v4l2_dbg_match *match = ®->match; + + if (match->type >= 2) { + v4l2_subdev_call(vpbe_dev->venc, + core, + g_register, + reg); + } + + return 0; +} + +static int vpbe_display_s_register(struct file *file, void *priv, + struct v4l2_dbg_register *reg) +{ + return 0; +} +#endif + +/* vpbe capture ioctl operations */ +static const struct v4l2_ioctl_ops vpbe_ioctl_ops = { + .vidioc_querycap = vpbe_display_querycap, + .vidioc_g_fmt_vid_out = vpbe_display_g_fmt, + .vidioc_enum_fmt_vid_out = vpbe_display_enum_fmt, + .vidioc_s_fmt_vid_out = vpbe_display_s_fmt, + .vidioc_try_fmt_vid_out = vpbe_display_try_fmt, + .vidioc_reqbufs = vpbe_display_reqbufs, + .vidioc_querybuf = vpbe_display_querybuf, + .vidioc_qbuf = vpbe_display_qbuf, + .vidioc_dqbuf = vpbe_display_dqbuf, + .vidioc_streamon = vpbe_display_streamon, + .vidioc_streamoff = vpbe_display_streamoff, + .vidioc_cropcap = vpbe_display_cropcap, + .vidioc_g_crop = vpbe_display_g_crop, + .vidioc_s_crop = vpbe_display_s_crop, + .vidioc_g_priority = vpbe_display_g_priority, + .vidioc_s_priority = vpbe_display_s_priority, + .vidioc_s_std = vpbe_display_s_std, + .vidioc_g_std = vpbe_display_g_std, + .vidioc_enum_output = vpbe_display_enum_output, + .vidioc_s_output = vpbe_display_s_output, + .vidioc_g_output = vpbe_display_g_output, + .vidioc_s_dv_preset = vpbe_display_s_dv_preset, + .vidioc_g_dv_preset = vpbe_display_g_dv_preset, + .vidioc_enum_dv_presets = vpbe_display_enum_dv_presets, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vpbe_display_g_register, + .vidioc_s_register = vpbe_display_s_register, +#endif +}; + +static struct v4l2_file_operations vpbe_fops = { + .owner = THIS_MODULE, + .open = vpbe_display_open, + .release = vpbe_display_release, + .unlocked_ioctl = video_ioctl2, + .mmap = vpbe_display_mmap, + .poll = vpbe_display_poll +}; + +static int vpbe_device_get(struct device *dev, void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + + if (strcmp("vpbe_controller", pdev->name) == 0) + vpbe_dev = platform_get_drvdata(pdev); + + if (strcmp("vpbe-osd", pdev->name) == 0) + osd_device = platform_get_drvdata(pdev); + + return 0; +} + +/*Configure the channels, buffer size */ +static int init_vpbe_layer_objects(int i) +{ + int free_buffer_index; + + /* Default number of buffers should be 3 */ + if ((video2_numbuffers > 0) && + (video2_numbuffers < display_buf_config_params.min_numbuffers)) + video2_numbuffers = display_buf_config_params.min_numbuffers; + if ((video3_numbuffers > 0) && + (video3_numbuffers < display_buf_config_params.min_numbuffers)) + video3_numbuffers = display_buf_config_params.min_numbuffers; + + /* + * Set buffer size to min buffers size if invalid + * buffer size is given + */ + if (video2_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]) + video2_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_0]; + + if (video3_bufsize < + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]) + video3_bufsize = + display_buf_config_params.min_bufsize[VPBE_DISPLAY_DEVICE_1]; + + /* set number of buffers, they could come from boot/args */ + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_0] = + video2_numbuffers; + display_buf_config_params.numbuffers[VPBE_DISPLAY_DEVICE_1] = + video3_numbuffers; + + if (display_buf_config_params.numbuffers[0] == 0) + printk(KERN_ERR "no vid2 buffer allocated\n"); + if (display_buf_config_params.numbuffers[1] == 0) + printk(KERN_ERR "no vid3 buffer allocated\n"); + free_buffer_index = display_buf_config_params.numbuffers[i - 1]; + + return 0; +} + + +/* + * vpbe_display_probe() + * This function creates device entries by register itself to the V4L2 driver + * and initializes fields of each layer objects + */ +static __init int vpbe_display_probe(struct platform_device *pdev) +{ + int i, j = 0, k, err = 0; + struct vpbe_display *disp_dev; + struct video_device *vbd = NULL; + struct vpbe_display_obj *vpbe_display_layer = NULL; + struct resource *res; + int irq; + + printk(KERN_DEBUG "vpbe_display_probe\n"); + + /* Allocate memory for vpbe_display */ + disp_dev = kzalloc(sizeof(struct vpbe_display), GFP_KERNEL); + if (!disp_dev) { + printk(KERN_ERR "ran out of memory\n"); + return -ENOMEM; + } + + /* Allocate memory for four plane display objects */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + disp_dev->dev[i] = + kmalloc(sizeof(struct vpbe_display_obj), GFP_KERNEL); + /* If memory allocation fails, return error */ + if (!disp_dev->dev[i]) { + printk(KERN_ERR "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + spin_lock_init(&disp_dev->dev[i]->irqlock); + mutex_init(&disp_dev->dev[i]->opslock); + } + spin_lock_init(&disp_dev->dma_queue_lock); + + err = init_vpbe_layer_objects(i); + if (err) { + printk(KERN_ERR "Error initializing vpbe display\n"); + return err; + } + + /* + * Scan all the platform devices to find the vpbe + * controller device and get the vpbe_dev object + */ + err = bus_for_each_dev(&platform_bus_type, NULL, NULL, + vpbe_device_get); + if (err < 0) + return err; + + /* Initialize the vpbe display controller */ + if (NULL != vpbe_dev->ops.initialize) { + err = vpbe_dev->ops.initialize(&pdev->dev, vpbe_dev); + if (err) { + v4l2_err(&vpbe_dev->v4l2_dev, "Error initing vpbe\n"); + err = -ENOMEM; + goto probe_out; + } + } + + /* check the name of davinci device */ + if (vpbe_dev->cfg->module_name != NULL) + strcpy(vpbe_display_videocap.card, + vpbe_dev->cfg->module_name); + + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + /* Allocate memory for video device */ + vbd = video_device_alloc(); + if (vbd == NULL) { + for (j = 0; j < i; j++) { + video_device_release( + disp_dev->dev[j]->video_dev); + } + v4l2_err(&vpbe_dev->v4l2_dev, "ran out of memory\n"); + err = -ENOMEM; + goto probe_out; + } + /* Initialize field of video device */ + vbd->release = video_device_release; + vbd->fops = &vpbe_fops; + vbd->ioctl_ops = &vpbe_ioctl_ops; + vbd->minor = -1; + vbd->v4l2_dev = &vpbe_dev->v4l2_dev; + vbd->lock = &vpbe_display_layer->opslock; + + if (vpbe_dev->current_timings.timings_type & VPBE_ENC_STD) { + vbd->tvnorms = (V4L2_STD_525_60 | V4L2_STD_625_50); + vbd->current_norm = + vpbe_dev->current_timings.timings.std_id; + } else + vbd->current_norm = 0; + + snprintf(vbd->name, sizeof(vbd->name), + "DaVinci_VPBE Display_DRIVER_V%d.%d.%d", + (VPBE_DISPLAY_VERSION_CODE >> 16) & 0xff, + (VPBE_DISPLAY_VERSION_CODE >> 8) & 0xff, + (VPBE_DISPLAY_VERSION_CODE) & 0xff); + + /* Set video_dev to the video device */ + vpbe_display_layer->video_dev = vbd; + vpbe_display_layer->device_id = i; + + vpbe_display_layer->layer_info.id = + ((i == VPBE_DISPLAY_DEVICE_0) ? WIN_VID0 : WIN_VID1); + if (display_buf_config_params.numbuffers[i] == 0) + vpbe_display_layer->memory = V4L2_MEMORY_USERPTR; + else + vpbe_display_layer->memory = V4L2_MEMORY_MMAP; + + /* Initialize field of the display layer objects */ + vpbe_display_layer->usrs = 0; + vpbe_display_layer->io_usrs = 0; + vpbe_display_layer->started = 0; + + /* Initialize prio member of layer object */ + v4l2_prio_init(&vpbe_display_layer->prio); + + /* Register video device */ + v4l2_info(&vpbe_dev->v4l2_dev, + "Trying to register VPBE display device.\n"); + v4l2_info(&vpbe_dev->v4l2_dev, + "layer=%x,layer->video_dev=%x\n", + (int)vpbe_display_layer, + (int)&vpbe_display_layer->video_dev); + + err = video_register_device(vpbe_display_layer-> + video_dev, + VFL_TYPE_GRABBER, + vpbe_display_nr[i]); + if (err) + goto probe_out; + /* set the driver data in platform device */ + platform_set_drvdata(pdev, disp_dev); + video_set_drvdata(vpbe_display_layer->video_dev, disp_dev); + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + v4l2_err(&vpbe_dev->v4l2_dev, + "Unable to get VENC interrupt resource\n"); + err = -ENODEV; + goto probe_out; + } + irq = res->start; + if (request_irq(irq, venc_isr, IRQF_DISABLED, VPBE_DISPLAY_DRIVER, + disp_dev)) { + v4l2_err(&vpbe_dev->v4l2_dev, "Unable to request interrupt\n"); + err = -ENODEV; + goto probe_out; + } + printk(KERN_DEBUG "Successfully completed the probing of vpbe v4l2 device\n"); + return 0; +probe_out: + kfree(disp_dev); + + for (k = 0; k < j; k++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[k]; + /* Unregister video device */ + video_unregister_device(vpbe_display_layer->video_dev); + /* Release video device */ + video_device_release(vpbe_display_layer->video_dev); + vpbe_display_layer->video_dev = NULL; + } + return err; +} + +/* + * vpbe_display_remove() + * It un-register hardware layer from V4L2 driver + */ +static int vpbe_display_remove(struct platform_device *pdev) +{ + int i; + struct vpbe_display_obj *vpbe_display_layer; + struct vpbe_display *disp_dev = platform_get_drvdata(pdev); + struct resource *res; + + v4l2_dbg(1, debug, &vpbe_dev->v4l2_dev, "vpbe_display_remove\n"); + + /* unregister irq */ + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + free_irq(res->start, disp_dev); + + /* deinitialize the vpbe display controller */ + if (NULL != vpbe_dev->ops.deinitialize) + vpbe_dev->ops.deinitialize(&pdev->dev, vpbe_dev); + /* un-register device */ + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + /* Get the pointer to the layer object */ + vpbe_display_layer = disp_dev->dev[i]; + /* Unregister video device */ + video_unregister_device(vpbe_display_layer->video_dev); + + vpbe_display_layer->video_dev = NULL; + } + for (i = 0; i < VPBE_DISPLAY_MAX_DEVICES; i++) { + kfree(disp_dev->dev[i]); + disp_dev->dev[i] = NULL; + } + + return 0; +} + +static struct platform_driver vpbe_display_driver = { + .driver = { + .name = VPBE_DISPLAY_DRIVER, + .owner = THIS_MODULE, + .bus = &platform_bus_type, + }, + .probe = vpbe_display_probe, + .remove = __devexit_p(vpbe_display_remove), +}; + +/* + * vpbe_display_init() + * This function registers device and driver to the kernel, requests irq + * handler and allocates memory for layer objects + */ +static __init int vpbe_display_init(void) +{ + int err = 0; + + printk(KERN_DEBUG "vpbe_display_init\n"); + + /* Register driver to the kernel */ + err = platform_driver_register(&vpbe_display_driver); + if (0 != err) + return err; + + printk(KERN_DEBUG "vpbe_display_init:" + "VPBE V4L2 Display Driver V1.0 loaded\n"); + return 0; +} + +/* + * vpbe_display_cleanup() + * This function un-registers device and driver to the kernel, frees requested + * irq handler and de-allocates memory allocated for layer objects. + */ +static void vpbe_display_cleanup(void) +{ + printk(KERN_DEBUG "vpbe_display_cleanup\n"); + + /* platform driver unregister */ + platform_driver_unregister(&vpbe_display_driver); +} + +/* Function for module initialization and cleanup */ +module_init(vpbe_display_init); +module_exit(vpbe_display_cleanup); + +MODULE_DESCRIPTION("TI DMXXX VPBE Display controller"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/include/media/davinci/vpbe_display.h b/include/media/davinci/vpbe_display.h new file mode 100644 index 0000000..d5cce40 --- /dev/null +++ b/include/media/davinci/vpbe_display.h @@ -0,0 +1,146 @@ +/* + * 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 as + * published by the Free Software Foundation version 2. + * + * This program is distributed 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 VPBE_DISPLAY_H +#define VPBE_DISPLAY_H + +#ifdef __KERNEL__ + +/* Header files */ +#include +#include +#include +#include +#include +#include + +#define VPBE_DISPLAY_MAX_DEVICES 2 + +enum vpbe_display_device_id { + VPBE_DISPLAY_DEVICE_0, + VPBE_DISPLAY_DEVICE_1 +}; + +#define VPBE_DISPLAY_DRV_NAME "vpbe-display" + +#define VPBE_DISPLAY_MAJOR_RELEASE 1 +#define VPBE_DISPLAY_MINOR_RELEASE 0 +#define VPBE_DISPLAY_BUILD 1 +#define VPBE_DISPLAY_VERSION_CODE ((VPBE_DISPLAY_MAJOR_RELEASE << 16) | \ + (VPBE_DISPLAY_MINOR_RELEASE << 8) | \ + VPBE_DISPLAY_BUILD) + +#define VPBE_DISPLAY_VALID_FIELD(field) ((V4L2_FIELD_NONE == field) || \ + (V4L2_FIELD_ANY == field) || (V4L2_FIELD_INTERLACED == field)) + +/* Exp ratio numerator and denominator constants */ +#define VPBE_DISPLAY_H_EXP_RATIO_N (9) +#define VPBE_DISPLAY_H_EXP_RATIO_D (8) +#define VPBE_DISPLAY_V_EXP_RATIO_N (6) +#define VPBE_DISPLAY_V_EXP_RATIO_D (5) + +/* Zoom multiplication factor */ +#define VPBE_DISPLAY_ZOOM_4X (4) +#define VPBE_DISPLAY_ZOOM_2X (2) + +/* Structures */ +struct display_layer_info { + int enable; + /* Layer ID used by Display Manager */ + enum osd_layer id; + struct osd_layer_config config; + enum osd_zoom_factor h_zoom; + enum osd_zoom_factor v_zoom; + enum osd_h_exp_ratio h_exp; + enum osd_v_exp_ratio v_exp; +}; + +/* vpbe display object structure */ +struct vpbe_display_obj { + /* number of buffers in fbuffers */ + unsigned int numbuffers; + /* Pointer pointing to current v4l2_buffer */ + struct videobuf_buffer *cur_frm; + /* Pointer pointing to next v4l2_buffer */ + struct videobuf_buffer *next_frm; + /* videobuf specific parameters + * Buffer queue used in video-buf + */ + struct videobuf_queue buffer_queue; + /* Queue of filled frames */ + struct list_head dma_queue; + /* Used in video-buf */ + spinlock_t irqlock; + /* V4l2 specific parameters */ + /* Identifies video device for this layer */ + struct video_device *video_dev; + /* This field keeps track of type of buffer exchange mechanism user + * has selected + */ + enum v4l2_memory memory; + /* Used to keep track of state of the priority */ + struct v4l2_prio_state prio; + /* Used to store pixel format */ + struct v4l2_pix_format pix_fmt; + enum v4l2_field buf_field; + /* Video layer configuration params */ + struct display_layer_info layer_info; + /* vpbe specific parameters + * enable window for display + */ + unsigned char window_enable; + /* number of open instances of the layer */ + unsigned int usrs; + /* number of users performing IO */ + unsigned int io_usrs; + /* Indicates id of the field which is being displayed */ + unsigned int field_id; + /* Indicates whether streaming started */ + unsigned char started; + /* Identifies device object */ + enum vpbe_display_device_id device_id; + /* facilitation of ioctl ops lock by v4l2*/ + struct mutex opslock; +}; + +/* vpbe device structure */ +struct vpbe_display { + /* layer specific parameters */ + /* lock for isr updates to buf layers*/ + spinlock_t dma_queue_lock; + /* C-Plane offset from start of y-plane */ + unsigned int cbcr_ofst; + struct vpbe_display_obj *dev[VPBE_DISPLAY_MAX_DEVICES]; +}; + +/* File handle structure */ +struct vpbe_fh { + /* vpbe device structure */ + struct vpbe_display *disp_dev; + /* pointer to layer object for opened device */ + struct vpbe_display_obj *layer; + /* Indicates whether this file handle is doing IO */ + unsigned char io_allowed; + /* Used to keep track priority of this instance */ + enum v4l2_priority prio; +}; + +struct buf_config_params { + unsigned char min_numbuffers; + unsigned char numbuffers[VPBE_DISPLAY_MAX_DEVICES]; + unsigned int min_bufsize[VPBE_DISPLAY_MAX_DEVICES]; + unsigned int layer_bufsize[VPBE_DISPLAY_MAX_DEVICES]; +}; + +static int venc_is_second_field(void); +#endif /* end of __KERNEL__ */ +#endif /* VPBE_DISPLAY_H */ diff --git a/include/media/davinci/vpbe_types.h b/include/media/davinci/vpbe_types.h new file mode 100644 index 0000000..24a358b --- /dev/null +++ b/include/media/davinci/vpbe_types.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2010 Texas Instruments Inc + * + * 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 in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _VPBE_TYPES_H +#define _VPBE_TYPES_H + +enum vpbe_types { + VPBE_VERSION_1 = 1, + VPBE_VERSION_2, + VPBE_VERSION_3, +}; + +/* vpbe_timing_type - Timing types used in vpbe device */ +enum vpbe_enc_timings_type { + VPBE_ENC_STD = 0x1, + VPBE_ENC_DV_PRESET = 0x2, + VPBE_ENC_CUSTOM_TIMINGS = 0x4, + /* Used when set timings through FB device interface */ + VPBE_ENC_TIMINGS_INVALID = 0x8, +}; + +union vpbe_timings { + v4l2_std_id std_id; + unsigned int dv_preset; +}; + +/* + * struct vpbe_enc_mode_info + * @name: ptr to name string of the standard, "NTSC", "PAL" etc + * @std: standard or non-standard mode. 1 - standard, 0 - nonstandard + * @interlaced: 1 - interlaced, 0 - non interlaced/progressive + * @xres: x or horizontal resolution of the display + * @yres: y or vertical resolution of the display + * @fps: frame per second + * @left_margin: left margin of the display + * @right_margin: right margin of the display + * @upper_margin: upper margin of the display + * @lower_margin: lower margin of the display + * @hsync_len: h-sync length + * @vsync_len: v-sync length + * @flags: bit field: bit usage is documented below + * + * Description: + * Structure holding timing and resolution information of a standard. + * Used by vpbe_device to set required non-standard timing in the + * venc when lcd controller output is connected to a external encoder. + * A table of timings is maintained in vpbe device to set this in + * venc when external encoder is connected to lcd controller output. + * Encoder may provide a g_dv_timings() API to override these values + * as needed. + * + * Notes + * ------ + * if_type should be used only by encoder manager and encoder. + * flags usage + * b0 (LSB) - hsync polarity, 0 - negative, 1 - positive + * b1 - vsync polarity, 0 - negative, 1 - positive + * b2 - field id polarity, 0 - negative, 1 - positive + */ +struct vpbe_enc_mode_info { + unsigned char *name; + enum vpbe_enc_timings_type timings_type; + union vpbe_timings timings; + unsigned int interlaced; + unsigned int xres; + unsigned int yres; + struct v4l2_fract aspect; + struct v4l2_fract fps; + unsigned int left_margin; + unsigned int right_margin; + unsigned int upper_margin; + unsigned int lower_margin; + unsigned int hsync_len; + unsigned int vsync_len; + unsigned int flags; +}; + +#endif -- 1.6.2.4 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From subhasish at mistralsolutions.com Fri Jul 8 11:40:07 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 16:40:07 -0000 Subject: [PATCH v5 0/1] pruss mfd drivers. Message-ID: <1306827939-4133-1-git-send-email-subhasish@mistralsolutions.com> PRUSS Functional Block Diagram /-------------------------------------------------------\ | | | |------| | 32GPO<------->PRU CORE-0 <----->| |<----> DRAM 0 | 30GPI<------->(4KB IRAM) | S | (512 Bytes) | | | | | 32GPO<------->PRU CORE-1 <----->| C |<----> DRAM 1 | 30GPI<------->(4KB IRAM) | | (512 Bytes) | | | R | | | | | | Ints to ARM/ | Interrupt <---->| |-------------------------> Master I/F (to SCR2) DSP INTC <------------->Controller | |<------------------------- Slave I/F (from SCR2) Events from | (INTC) |------| | Periph + PRUs | | \-------------------------------------------------------/ Programmable Realtime Unit (PRU) is basically a 32-bit RISC processor available within TI's DA8XX SOCs. It consists of local instruction and data RAM and also has access to SOC resources via a Switched Central Resource (SCR). There are two PRU's available within DA8XX SOC's PRUSS, hence providing two execution cores. Devices/Protocols can be emulated on these utilizing either both or only one of the PRUs independently. The rational behind the MFD driver being the fact that multiple devices can be implemented on the cores independently. It's also possible, as in our case, to implement a single device on both the PRU's resulting in improved load sharing. A detailed description is also available here: http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem version 5: ========= MFD: * Moved the MFD drivers into staging. * Added v4 code comments. version 4: ========= MFD: * added mfd_cells in the board file * exported seperate multiwrite/read functions * removed clk_get_pruss api * improved coding style * removed code duplications * renamed files to pruss based * used make C=1 * added __iomem cookie for io addresses * used iowrite/read instead of __raw variants SUART: * changes for the new mfd_cell implementation * code cleanup * used iowrite/read instead of __raw variants * used make C=1 NOTE: * as requested by the owner the CAN driver patch is submitted seperately. version 3: ========= * added locking to mfd driver. * removed typedefs. * resource allocation through mfd. * renamed da8xx_register_pruss to da8xx_register_pruss_mfd. * removed da8xx_pruss directory for suart. * combined all suart headers. * modified register structure for pruss. * added function clk_add_alias_mcasp. * suart api layer cleanup. * removed semaphore usage. * updated to latest internal code base. * removed __suart_err/dbg. version 2: ========== * added pruss TTY Soft-UART driver. * added pruss Soft-UART board and platform changes. * fixed previous review comments. * reordered patch sequence. version 1: ========== * added pruss mfd driver. Subhasish Ghosh (1): drivers:staging:pruss: add pruss staging mfd driver. drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/pruss/Kconfig | 16 ++ drivers/staging/pruss/Makefile | 5 + drivers/staging/pruss/TODO | 14 + drivers/staging/pruss/pruss.c | 483 ++++++++++++++++++++++++++++++++++++ drivers/staging/pruss/pruss.h | 130 ++++++++++ drivers/staging/pruss/pruss_core.h | 132 ++++++++++ 8 files changed, 783 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/pruss/Kconfig create mode 100644 drivers/staging/pruss/Makefile create mode 100644 drivers/staging/pruss/TODO create mode 100644 drivers/staging/pruss/pruss.c create mode 100644 drivers/staging/pruss/pruss.h create mode 100644 drivers/staging/pruss/pruss_core.h -- 1.7.2.3 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From michael.williamson at criticallink.com Fri Jul 8 11:50:15 2011 From: michael.williamson at criticallink.com (Michael Williamson) Date: Fri, 08 Jul 2011 16:50:15 -0000 Subject: [PATCH v1 3/4] davinci: add spi devices support for da850/omap-l138/am18x evm In-Reply-To: <1296596979-18524-1-git-send-email-michael.williamson@criticallink.com> References: <1296596979-18524-1-git-send-email-michael.williamson@criticallink.com> Message-ID: <1296596979-18524-4-git-send-email-michael.williamson@criticallink.com> From: Sekhar Nori This patch adds the on-board SPI flash device to the DA850/OMAP-L138/AM18x EVM. It also registers the SPI flash device to the MTD subsystem. Based on SPI flash device support for MityDSP-L138F platform. Signed-off-by: Sekhar Nori [michael.williamson at criticallink.com: moved da850_evm_spi1_pdata to devices-da8xx.c] Signed-off-by: Michael Williamson --- arch/arm/mach-davinci/board-da850-evm.c | 84 +++++++++++++++++++++++++++++++ 1 files changed, 84 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 11f986b..487bd3a 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -29,6 +29,8 @@ #include #include #include +#include +#include #include #include @@ -38,6 +40,7 @@ #include #include #include +#include #define DA850_EVM_PHY_ID "0:00" #define DA850_LCD_PWR_PIN GPIO_TO_PIN(2, 8) @@ -48,6 +51,85 @@ #define DA850_MII_MDIO_CLKEN_PIN GPIO_TO_PIN(2, 6) +static struct mtd_partition da850evm_spiflash_part[] = { + [0] = { + .name = "UBL", + .offset = 0, + .size = SZ_64K, + .mask_flags = MTD_WRITEABLE, + }, + [1] = { + .name = "U-Boot", + .offset = MTDPART_OFS_APPEND, + .size = SZ_512K, + .mask_flags = MTD_WRITEABLE, + }, + [2] = { + .name = "U-Boot-Env", + .offset = MTDPART_OFS_APPEND, + .size = SZ_64K, + .mask_flags = MTD_WRITEABLE, + }, + [3] = { + .name = "Kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M + SZ_512K, + .mask_flags = 0, + }, + [4] = { + .name = "Filesystem", + .offset = MTDPART_OFS_APPEND, + .size = SZ_4M, + .mask_flags = 0, + }, + [5] = { + .name = "MAC-Address", + .offset = SZ_8M - SZ_64K, + .size = SZ_64K, + .mask_flags = MTD_WRITEABLE, + }, +}; + +static struct flash_platform_data da850evm_spiflash_data = { + .name = "m25p80", + .parts = da850evm_spiflash_part, + .nr_parts = ARRAY_SIZE(da850evm_spiflash_part), + .type = "m25p64", +}; + +static struct davinci_spi_config da850evm_spiflash_cfg = { + .io_type = SPI_IO_TYPE_DMA, + .c2tdelay = 8, + .t2cdelay = 8, +}; + +static struct spi_board_info da850evm_spi_info[] = { + { + .modalias = "m25p80", + .platform_data = &da850evm_spiflash_data, + .controller_data = &da850evm_spiflash_cfg, + .mode = SPI_MODE_0, + .max_speed_hz = 30000000, + .bus_num = 1, + .chip_select = 0, + }, +}; + +static void __init da850evm_init_spi1(struct spi_board_info *info, unsigned len) +{ + int ret; + + ret = spi_register_board_info(info, len); + if (ret) + pr_warning("failed to register board info : %d\n", ret); + + da8xx_spi_pdata[1].num_chipselect = len; + + ret = da8xx_register_spi(1); + if (ret) + pr_warning("failed to register spi 1 device : %d\n", ret); +} + static struct mtd_partition da850_evm_norflash_partition[] = { { .name = "bootloaders + env", @@ -1167,6 +1249,8 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: suspend registration failed: %d\n", ret); + + da850evm_init_spi1(da850evm_spi_info, ARRAY_SIZE(da850evm_spi_info)); } #ifdef CONFIG_SERIAL_8250_CONSOLE -- 1.7.0.4 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From nsekhar at ti.com Fri Jul 8 11:51:52 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 8 Jul 2011 22:21:52 +0530 Subject: [RFC PATCH v4] Consolidate SRAM support In-Reply-To: <1306371777-20431-1-git-send-email-plagnioj@jcrosoft.com> References: <20110512174546.GB8633@n2100.arm.linux.org.uk> <1306371777-20431-1-git-send-email-plagnioj@jcrosoft.com> Message-ID: Hi Jean-Christophe, On Thu, May 26, 2011 at 06:32:57, Jean-Christophe PLAGNIOL-VILLARD wrote: > From: Russell King - ARM Linux > > We have two SoCs using SRAM, both with their own allocation systems, > and both with their own ways of copying functions into the SRAM. > > Let's unify this before we have additional SoCs re-implementing this > obviously common functionality themselves. > > For this use the generic allocator and the newly introduce > gen_pool_add_virt and gen_pool_virt_to_phys > > Uio_pruss should probably take the SRAM pool pointer via > platform data so that it doesn't have to include Davinci specific > includes. > > Signed-off-by: Russell King > Signed-off-by: Jean-Christophe PLAGNIOL-VILLARD > Cc: Sekhar Nori > Cc: Kevin Hilman > Cc: Tony Lindgren > Cc: Sascha Hauer > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > index f4b7dfa..5ec5e5f 100644 > --- a/arch/arm/Kconfig > +++ b/arch/arm/Kconfig > @@ -848,6 +848,7 @@ config ARCH_DAVINCI > bool "TI DaVinci" > select GENERIC_CLOCKEVENTS > select ARCH_REQUIRE_GPIOLIB > + select GENERIC_ALLOCATOR > select ZONE_DMA > select HAVE_IDE > select CLKDEV_LOOKUP GENERIC_ALLOCATOR is already selected for DaVinci (below the "select CLKDEV_LOOKUP" line). Please drop this hunk. Thanks, Sekhar From nsekhar at ti.com Fri Jul 8 11:58:33 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Fri, 8 Jul 2011 22:28:33 +0530 Subject: [RFC PATCH v4] Consolidate SRAM support In-Reply-To: References: <20110512174546.GB8633@n2100.arm.linux.org.uk> <1306371777-20431-1-git-send-email-plagnioj@jcrosoft.com> Message-ID: On Fri, Jul 08, 2011 at 22:21:52, Nori, Sekhar wrote: > Hi Jean-Christophe, > > On Thu, May 26, 2011 at 06:32:57, Jean-Christophe PLAGNIOL-VILLARD wrote: > > From: Russell King - ARM Linux > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > > index f4b7dfa..5ec5e5f 100644 > > --- a/arch/arm/Kconfig > > +++ b/arch/arm/Kconfig > > @@ -848,6 +848,7 @@ config ARCH_DAVINCI > > bool "TI DaVinci" > > select GENERIC_CLOCKEVENTS > > select ARCH_REQUIRE_GPIOLIB > > + select GENERIC_ALLOCATOR > > select ZONE_DMA > > select HAVE_IDE > > select CLKDEV_LOOKUP > > GENERIC_ALLOCATOR is already selected for > DaVinci (below the "select CLKDEV_LOOKUP" > line). > > Please drop this hunk. This mail popped up at the top of my inbox as an unread message and I replied to it without looking at the sent date assuming it is a patch resend. Sorry about the noise. Thanks, Sekhar From subhasish at mistralsolutions.com Fri Jul 8 12:30:34 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 17:30:34 -0000 Subject: [PATCH v4 1/1] can: add pruss CAN driver. In-Reply-To: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474267-6344-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474267-6344-2-git-send-email-subhasish@mistralsolutions.com> This patch adds support for the CAN device emulated on PRUSS. Signed-off-by: Subhasish Ghosh --- drivers/net/can/Kconfig | 7 + drivers/net/can/Makefile | 1 + drivers/net/can/pruss_can.c | 1074 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1082 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/pruss_can.c diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index 5dec456..4682a4f 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -111,6 +111,13 @@ config PCH_CAN embedded processor. This driver can access CAN bus. +config CAN_TI_DA8XX_PRU + depends on CAN_DEV && ARCH_DAVINCI && ARCH_DAVINCI_DA850 + tristate "PRU based CAN emulation for DA8XX" + ---help--- + Enable this to emulate a CAN controller on the PRU of DA8XX. + If not sure, mark N + source "drivers/net/can/mscan/Kconfig" source "drivers/net/can/sja1000/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 53c82a7..d0b7cbd 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_AT91) += at91_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o +obj-$(CONFIG_CAN_TI_DA8XX_PRU) += pruss_can.o obj-$(CONFIG_CAN_MCP251X) += mcp251x.o obj-$(CONFIG_CAN_BFIN) += bfin_can.o obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o diff --git a/drivers/net/can/pruss_can.c b/drivers/net/can/pruss_can.c new file mode 100644 index 0000000..7702509 --- /dev/null +++ b/drivers/net/can/pruss_can.c @@ -0,0 +1,1074 @@ +/* + * TI DA8XX PRU CAN Emulation device driver + * Author: subhasish at mistralsolutions.com + * + * This driver supports TI's PRU CAN Emulation and the + * specs for the same is available at + * + * Copyright (C) 2010, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PRUSS_CAN_RX_PRU_0 PRUSS_NUM0 +#define PRUSS_CAN_TX_PRU_1 PRUSS_NUM1 +#define PRUSS_CAN_TX_SYS_EVT 34 +#define PRUSS_CAN_RX_SYS_EVT 33 +#define PRUSS_CAN_ARM2DSP_SYS_EVT 32 +#define PRUSS_CAN_DELAY_LOOP_LENGTH 2 +#define PRUSS_CAN_TIMER_SETUP_DELAY 14 +#define PRUSS_CAN_GPIO_SETUP_DELAY 150 +#define PRUSS_CAN_TX_GBL_STAT_REG (PRUSS_PRU1_BASE_ADDRESS + 0x04) +#define PRUSS_CAN_TX_INTR_MASK_REG (PRUSS_PRU1_BASE_ADDRESS + 0x08) +#define PRUSS_CAN_TX_INTR_STAT_REG (PRUSS_PRU1_BASE_ADDRESS + 0x0C) +#define PRUSS_CAN_TX_MBOX0_STAT_REG (PRUSS_PRU1_BASE_ADDRESS + 0x10) +#define PRUSS_CAN_TX_ERR_CNTR_REG (PRUSS_PRU1_BASE_ADDRESS + 0x30) +#define PRUSS_CAN_TX_INT_STAT 0x2 +#define PRUSS_CAN_MBOX_OFFSET 0x40 +#define PRUSS_CAN_MBOX_SIZE 0x10 +#define PRUSS_CAN_MBOX_STAT_OFFSET 0x10 +#define PRUSS_CAN_MBOX_STAT_SIZE 0x04 +#define PRUSS_CAN_GBL_STAT_REG_VAL 0x00000040 +#define PRUSS_CAN_INTR_MASK_REG_VAL 0x00004000 +#define PRUSS_CAN_TIMING_VAL_TX (PRUSS_PRU1_BASE_ADDRESS + 0xC0) +#define PRUSS_CAN_TIMING_SETUP (PRUSS_PRU1_BASE_ADDRESS + 0xC4) +#define PRUSS_CAN_RX_GBL_STAT_REG (PRUSS_PRU0_BASE_ADDRESS + 0x04) +#define PRUSS_CAN_RX_INTR_MASK_REG (PRUSS_PRU0_BASE_ADDRESS + 0x08) +#define PRUSS_CAN_RX_INTR_STAT_REG (PRUSS_PRU0_BASE_ADDRESS + 0x0C) +#define PRUSS_CAN_RX_ERR_CNTR_REG (PRUSS_PRU0_BASE_ADDRESS + 0x34) +#define PRUSS_CAN_TIMING_VAL_RX (PRUSS_PRU0_BASE_ADDRESS + 0xD0) +#define PRUSS_CAN_BIT_TIMING_VAL_RX (PRUSS_PRU0_BASE_ADDRESS + 0xD4) +#define PRUSS_CAN_ID_MAP (PRUSS_PRU0_BASE_ADDRESS + 0xF0) +#define PRUSS_CAN_ERROR_ACTIVE 128 +#define PRUSS_CAN_GBL_STAT_REG_MASK 0x1F +#define PRUSS_CAN_GBL_STAT_REG_SET_TX 0x80 +#define PRUSS_CAN_GBL_STAT_REG_SET_RX 0x40 +#define PRUSS_CAN_GBL_STAT_REG_STOP_TX 0x7F +#define PRUSS_CAN_GBL_STAT_REG_STOP_RX 0xBF +#define PRUSS_CAN_SET_TX_REQ 0x00000080 +#define PRUSS_CAN_STD_FRAME_MASK 18 +#define PRUSS_CAN_START 1 +#define PRUSS_CAN_MB_MIN 0 +#define PRUSS_CAN_MB_MAX 7 +#define PRUSS_CAN_MID_IDE BIT(29) +#define PRUSS_CAN_ISR_BIT_CCI BIT(15) +#define PRUSS_CAN_ISR_BIT_ESI BIT(14) +#define PRUSS_CAN_ISR_BIT_SRDI BIT(13) +#define PRUSS_CAN_ISR_BIT_RRI BIT(8) +#define PRUSS_CAN_GSR_BIT_EPM BIT(4) +#define PRUSS_CAN_GSR_BIT_BFM BIT(3) +#define PRUSS_CAN_RTR_BUFF_NUM 8 +#define PRUSS_DEF_NAPI_WEIGHT 8 +#define PRUSS_CAN_DRV_NAME "da8xx_pruss_can" +#define PRUSS_CAN_DRV_DESC "TI PRU CAN Controller Driver v1.0" + +enum can_tx_dir { + TRANSMIT = 1, + RECEIVE +}; + +struct can_mbox { + canid_t can_id; + u8 data[8]; + u16 datalength; + u16 crc; +}; + +struct can_emu_cntx { + enum can_tx_dir txdir; + struct can_mbox mbox; + u32 mboxno; + u8 pruno; + u32 gbl_stat; + u32 intr_stat; + u32 mbox_stat; +}; + +struct can_emu_priv { + struct can_priv can; + struct napi_struct napi; + struct net_device *ndev; + struct device *dev; + struct clk *clk_timer; + struct can_emu_cntx can_tx_cntx; + struct can_emu_cntx can_rx_cntx; + unsigned int clk_freq_pru; + unsigned int trx_irq; + unsigned int tx_head; + unsigned int tx_tail; + unsigned int tx_next; + unsigned int mbx_id[8]; +}; + +static struct can_bittiming_const pru_can_bittiming_const = { + .name = PRUSS_CAN_DRV_NAME, + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 256, + .brp_inc = 1, +}; + +static int pru_can_mbox_write(struct device *dev, + struct can_emu_cntx *emu_cntx) +{ + u32 offset = 0; + + offset = PRUSS_PRU1_BASE_ADDRESS + PRUSS_CAN_MBOX_OFFSET + + (PRUSS_CAN_MBOX_SIZE * emu_cntx->mboxno); + + pruss_writel_multi(dev, offset, (u32 *) &(emu_cntx->mbox), 4); + + return 0; +} + +static int pru_can_mbox_read(struct device *dev, + struct can_emu_cntx *emu_cntx) +{ + u32 offset = 0; + + offset = PRUSS_PRU0_BASE_ADDRESS + PRUSS_CAN_MBOX_OFFSET + + (PRUSS_CAN_MBOX_SIZE * emu_cntx->mboxno); + + pruss_readl_multi(dev, offset, (u32 *) &emu_cntx->mbox, 4); + + return 0; +} + +static int pru_can_rx_id_set(struct device *dev, u32 nodeid, u32 mboxno) +{ + pruss_writel(dev, (PRUSS_CAN_ID_MAP + (mboxno * 4)), nodeid); + + return 0; +} + +static int pru_can_intr_stat_get(struct device *dev, + struct can_emu_cntx *emu_cntx) +{ + if (emu_cntx->pruno == PRUCORE_1) + pruss_readl(dev, PRUSS_CAN_TX_INTR_STAT_REG, + &emu_cntx->intr_stat); + else if (emu_cntx->pruno == PRUCORE_0) + pruss_readl(dev, PRUSS_CAN_RX_INTR_STAT_REG, + &emu_cntx->intr_stat); + else + return -EINVAL; + + return 0; +} + +static int pru_can_gbl_stat_get(struct device *dev, + struct can_emu_cntx *emu_cntx) +{ + if (emu_cntx->pruno == PRUCORE_1) + pruss_readl(dev, PRUSS_CAN_TX_GBL_STAT_REG, + &emu_cntx->gbl_stat); + else if (emu_cntx->pruno == PRUCORE_0) + pruss_readl(dev, PRUSS_CAN_RX_GBL_STAT_REG, + &emu_cntx->gbl_stat); + else + return -EINVAL; + return 0; +} + +static int pru_can_tx_mode_set(struct device *dev, bool transfer_flag, + enum can_tx_dir ecan_trx) +{ + u32 value; + + if (ecan_trx == TRANSMIT) { + pruss_readl(dev, PRUSS_CAN_RX_GBL_STAT_REG, &value); + if (transfer_flag) { + value &= PRUSS_CAN_GBL_STAT_REG_MASK; + value |= PRUSS_CAN_GBL_STAT_REG_SET_TX; + } else + value &= PRUSS_CAN_GBL_STAT_REG_STOP_TX; + + pruss_writel(dev, PRUSS_CAN_RX_GBL_STAT_REG, value); + pruss_writel(dev, PRUSS_CAN_TX_GBL_STAT_REG, value); + } else if (ecan_trx == RECEIVE) { + pruss_readl(dev, PRUSS_CAN_RX_GBL_STAT_REG, &value); + if (transfer_flag) { + value &= PRUSS_CAN_GBL_STAT_REG_MASK; + value |= PRUSS_CAN_GBL_STAT_REG_SET_RX; + } else + value &= PRUSS_CAN_GBL_STAT_REG_STOP_RX; + + pruss_writel(dev, PRUSS_CAN_RX_GBL_STAT_REG, value); + pruss_writel(dev, PRUSS_CAN_TX_GBL_STAT_REG, value); + } else + return -EINVAL; + + return 0; +} + +static u32 pruss_intc_init[19][3] = { + {PRUSS_INTC_POLARITY0, PRU_INTC_REGMAP_MASK, 0xFFFFFFFF}, + {PRUSS_INTC_POLARITY1, PRU_INTC_REGMAP_MASK, 0xFFFFFFFF}, + {PRUSS_INTC_TYPE0, PRU_INTC_REGMAP_MASK, 0x1C000000}, + {PRUSS_INTC_TYPE1, PRU_INTC_REGMAP_MASK, 0}, + {PRUSS_INTC_GLBLEN, 0, 1}, + {PRUSS_INTC_HOSTMAP0, PRU_INTC_REGMAP_MASK, 0x03020100}, + {PRUSS_INTC_HOSTMAP1, PRU_INTC_REGMAP_MASK, 0x07060504}, + {PRUSS_INTC_HOSTMAP2, PRU_INTC_REGMAP_MASK, 0x0000908}, + {PRUSS_INTC_CHANMAP0, PRU_INTC_REGMAP_MASK, 0}, + {PRUSS_INTC_CHANMAP8, PRU_INTC_REGMAP_MASK, 0x00020200}, + {PRUSS_INTC_STATIDXCLR, 0, 32}, + {PRUSS_INTC_STATIDXCLR, 0, 19}, + {PRUSS_INTC_ENIDXSET, 0, 19}, + {PRUSS_INTC_STATIDXCLR, 0, 18}, + {PRUSS_INTC_ENIDXSET, 0, 18}, + {PRUSS_INTC_STATIDXCLR, 0, 34}, + {PRUSS_INTC_ENIDXSET, 0, 34}, + {PRUSS_INTC_ENIDXSET, 0, 32}, + {PRUSS_INTC_HOSTINTEN, 0, 5} +}; + +static int pru_can_emu_init(struct device *dev, u32 pruclock) +{ + u32 value[8] = {[0 ... 7] = 1}; + u32 i; + + /* PRU Internal Registers Initializations */ + pruss_writel_multi(dev, PRUSS_CAN_TX_MBOX0_STAT_REG, value, 8); + + *value = PRUSS_CAN_GBL_STAT_REG_VAL; + pruss_writel(dev, PRUSS_CAN_TX_GBL_STAT_REG, value[0]); + pruss_writel(dev, PRUSS_CAN_RX_GBL_STAT_REG, value[0]); + + *value = PRUSS_CAN_INTR_MASK_REG_VAL; + pruss_writel(dev, PRUSS_CAN_TX_INTR_MASK_REG, value[0]); + pruss_writel(dev, PRUSS_CAN_RX_INTR_MASK_REG, value[0]); + + for (i = 0; i < 19; i++) + (i < 12) ? pruss_rmwl(dev, pruss_intc_init[i][0], + pruss_intc_init[i][1], + pruss_intc_init[i][2]) : + pruss_idx_writel(dev, pruss_intc_init[i][0], + pruss_intc_init[i][2]); + return 0; +} + +static void pru_can_emu_exit(struct device *dev) +{ + pruss_disable(dev, PRUSS_CAN_RX_PRU_0); + pruss_disable(dev, PRUSS_CAN_TX_PRU_1); +} + +static int pru_can_tx(struct device *dev, u8 mboxnumber, u8 pruno) +{ + u32 value = 0; + + if (PRUCORE_1 == pruno) { + value = PRUSS_CAN_SET_TX_REQ; + pruss_writel(dev, ((PRUSS_PRU1_BASE_ADDRESS + + (PRUSS_CAN_MBOX_STAT_OFFSET + + (PRUSS_CAN_MBOX_STAT_SIZE * mboxnumber))) + & 0xFFFF), value); + } else if (PRUCORE_0 == pruno) { + pruss_readl(dev, PRUSS_CAN_RX_INTR_STAT_REG, &value); + value = value & ~(1 << mboxnumber); + pruss_writel(dev, PRUSS_CAN_RX_INTR_STAT_REG, value); + value = 0; + pruss_writel(dev, ((PRUSS_PRU0_BASE_ADDRESS + + (PRUSS_CAN_MBOX_STAT_OFFSET + + (PRUSS_CAN_MBOX_STAT_SIZE * mboxnumber))) + & 0xFFFF), value); + } else + return -EINVAL; + return 0; +} + +static int pru_can_reset_tx(struct device *dev) +{ + pruss_idx_writel(dev, PRUSS_INTC_STATIDXCLR, PRUSS_CAN_ARM2DSP_SYS_EVT); + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, PRUSS_CAN_ARM2DSP_SYS_EVT); + pruss_idx_writel(dev, PRUSS_INTC_STATIDXSET, PRUSS_CAN_ARM2DSP_SYS_EVT); + return 0; +} + +static void pru_can_mask_ints(struct device *dev, u32 trx, bool enable) +{ + u32 value = 0; + + value = (trx == PRUSS_CAN_TX_PRU_1) ? + PRUSS_CAN_TX_SYS_EVT : PRUSS_CAN_RX_SYS_EVT; + enable ? pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, value) : + pruss_idx_writel(dev, PRUSS_INTC_ENIDXCLR, value); +} + +static unsigned int pru_can_get_error_cnt(struct device *dev, u8 pruno) +{ + u32 value = 0; + + if (pruno == PRUSS_CAN_RX_PRU_0) + pruss_readl(dev, PRUSS_CAN_RX_ERR_CNTR_REG, &value); + else if (pruno == PRUSS_CAN_TX_PRU_1) + pruss_readl(dev, PRUSS_CAN_TX_ERR_CNTR_REG, &value); + else + return -EINVAL; + + return value; +} + +static unsigned int pru_can_get_intc_status(struct device *dev) +{ + u32 value = 0; + + pruss_readl(dev, PRUSS_INTC_STATCLRINT1, &value); + return value; +} + +static void pru_can_clr_intc_status(struct device *dev, u32 trx) +{ + u32 value = 0; + + value = (trx == PRUSS_CAN_TX_PRU_1) ? + PRUSS_CAN_TX_SYS_EVT : PRUSS_CAN_RX_SYS_EVT; + pruss_idx_writel(dev, PRUSS_INTC_STATIDXCLR, value); +} + +static int pru_can_get_state(const struct net_device *ndev, + enum can_state *state) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + *state = priv->can.state; + + return 0; +} + +static int pru_can_set_bittiming(struct net_device *ndev) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + struct can_bittiming *bt = &priv->can.bittiming; + u32 value; + + value = priv->can.clock.freq / bt->bitrate; + pruss_writel(priv->dev, PRUSS_CAN_TIMING_VAL_TX, value); + pruss_writel(priv->dev, PRUSS_CAN_BIT_TIMING_VAL_RX, value); + + value = (bt->phase_seg2 + bt->phase_seg1 + + bt->prop_seg + 1) * bt->brp; + value = (value >> 1) - PRUSS_CAN_TIMER_SETUP_DELAY; + value = (value << 16) | value; + pruss_writel(priv->dev, PRUSS_CAN_TIMING_VAL_RX, value); + + value = (PRUSS_CAN_GPIO_SETUP_DELAY * + (priv->clk_freq_pru / 1000000) / 1000) / + PRUSS_CAN_DELAY_LOOP_LENGTH; + + pruss_writel(priv->dev, PRUSS_CAN_TIMING_SETUP, value); + return 0; +} + +static void pru_can_stop(struct net_device *ndev) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + + pru_can_mask_ints(priv->dev, PRUSS_CAN_TX_PRU_1, false); + pru_can_mask_ints(priv->dev, PRUSS_CAN_RX_PRU_0, false); + pru_can_reset_tx(priv->dev); + priv->can.state = CAN_STATE_STOPPED; +} + +/* + * This is to just set the can state to ERROR_ACTIVE + * ip link set canX up type can bitrate 125000 + */ +static void pru_can_start(struct net_device *ndev) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + + if (priv->can.state != CAN_STATE_STOPPED) + pru_can_stop(ndev); + + pru_can_mask_ints(priv->dev, PRUSS_CAN_TX_PRU_1, true); + pru_can_mask_ints(priv->dev, PRUSS_CAN_RX_PRU_0, true); + + pru_can_gbl_stat_get(priv->dev, &priv->can_tx_cntx); + pru_can_gbl_stat_get(priv->dev, &priv->can_rx_cntx); + + if (PRUSS_CAN_GSR_BIT_EPM & priv->can_tx_cntx.gbl_stat) + priv->can.state = CAN_STATE_ERROR_PASSIVE; + else if (PRUSS_CAN_GSR_BIT_BFM & priv->can_tx_cntx.gbl_stat) + priv->can.state = CAN_STATE_BUS_OFF; + else + priv->can.state = CAN_STATE_ERROR_ACTIVE; +} + +static int pru_can_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret = 0; + + switch (mode) { + case CAN_MODE_START: + pru_can_start(ndev); + netif_wake_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + return ret; +} + +static netdev_tx_t pru_can_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + int count; + u8 *data = cf->data; + u8 dlc = cf->can_dlc; + u8 *pdata = NULL; + + if (can_dropped_invalid_skb(ndev, skb)) + return NETDEV_TX_OK; + + netif_stop_queue(ndev); + if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ + priv->can_tx_cntx.mbox.can_id = + (cf->can_id & CAN_EFF_MASK) | PRUSS_CAN_MID_IDE; + else /* Standard frame format */ + priv->can_tx_cntx.mbox.can_id = + (cf->can_id & CAN_SFF_MASK) << PRUSS_CAN_STD_FRAME_MASK; + + if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */ + priv->can_tx_cntx.mbox.can_id |= CAN_RTR_FLAG; + + pdata = &priv->can_tx_cntx.mbox.data[0] + (dlc - 1); + for (count = 0; count < (u8) dlc; count++) + *pdata-- = *data++; + + priv->can_tx_cntx.mbox.datalength = (u16) dlc; + priv->can_tx_cntx.mbox.crc = 0; +/* + * search for the next available mbx + * if the next mbx is busy, then try the next + 1 + * do this until the head is reached. + * if still unable to tx, stop accepting any packets + * if able to tx and the head is reached, then reset next to tail, i.e mbx0 + * if head is not reached, then just point to the next mbx + */ + for (; priv->tx_next <= priv->tx_head; priv->tx_next++) { + priv->can_tx_cntx.mboxno = priv->tx_next; + if (-1 == pru_can_mbox_write(priv->dev, + &priv->can_tx_cntx)) { + if (priv->tx_next == priv->tx_head) { + priv->tx_next = priv->tx_tail; + netif_stop_queue(ndev); /* IF stalled */ + dev_err(priv->dev, + "%s: no tx mbx available", __func__); + return NETDEV_TX_BUSY; + } else + continue; + } else { + /* set transmit request */ + pru_can_tx(priv->dev, priv->tx_next, + PRUSS_CAN_TX_PRU_1); + pru_can_tx_mode_set(priv->dev, false, RECEIVE); + pru_can_tx_mode_set(priv->dev, true, TRANSMIT); + pru_can_reset_tx(priv->dev); + priv->tx_next++; + can_put_echo_skb(skb, ndev, 0); + break; + } + } + if (priv->tx_next > priv->tx_head) + priv->tx_next = priv->tx_tail; + + return NETDEV_TX_OK; +} + +static int pru_can_rx(struct net_device *ndev, u32 mbxno) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u8 *data = NULL; + u8 *pdata = NULL; + int count = 0; + + skb = alloc_can_skb(ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + dev_err(priv->dev, + "alloc_can_skb() failed\n"); + return -ENOMEM; + } + data = cf->data; + /* get payload */ + priv->can_rx_cntx.mboxno = mbxno; + if (pru_can_mbox_read(priv->dev, &priv->can_rx_cntx)) { + dev_err(priv->dev, "failed to get data from mailbox\n"); + return -EAGAIN; + } + /* give ownweship to pru */ + pru_can_tx(priv->dev, mbxno, PRUSS_CAN_RX_PRU_0); + + /* get data length code */ + cf->can_dlc = get_can_dlc(priv->can_rx_cntx.mbox.datalength & 0xF); + if (cf->can_dlc <= 4) { + pdata = &priv->can_rx_cntx.mbox.data[4] + (4 - cf->can_dlc); + for (count = 0; count < cf->can_dlc; count++) + *data++ = *pdata++; + } else { + pdata = &priv->can_rx_cntx.mbox.data[4]; + for (count = 0; count < 4; count++) + *data++ = *pdata++; + pdata = &priv->can_rx_cntx.mbox.data[3] - (cf->can_dlc - 5); + for (count = 0; count < cf->can_dlc - 4; count++) + *data++ = *pdata++; + } + + /* get id extended or std */ + if (priv->can_rx_cntx.mbox.can_id & PRUSS_CAN_MID_IDE) + cf->can_id = (priv->can_rx_cntx.mbox.can_id & CAN_EFF_MASK) + | CAN_EFF_FLAG; + else + cf->can_id = (priv->can_rx_cntx.mbox.can_id >> 18) + & CAN_SFF_MASK; + + if (priv->can_rx_cntx.mbox.can_id & CAN_RTR_FLAG) + cf->can_id |= CAN_RTR_FLAG; + + stats->rx_bytes += cf->can_dlc; + netif_receive_skb(skb); + stats->rx_packets++; + return 0; +} + +static int pru_can_err(struct net_device *ndev, int int_status, + int err_status) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 tx_err_cnt, rx_err_cnt; + + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + dev_err(priv->dev, + "alloc_can_err_skb() failed\n"); + return -ENOMEM; + } + + if (err_status & PRUSS_CAN_GSR_BIT_EPM) { /* error passive int */ + priv->can.state = CAN_STATE_ERROR_PASSIVE; + ++priv->can.can_stats.error_passive; + cf->can_id |= CAN_ERR_CRTL; + tx_err_cnt = pru_can_get_error_cnt(priv->dev, + PRUSS_CAN_TX_PRU_1); + rx_err_cnt = pru_can_get_error_cnt(priv->dev, + PRUSS_CAN_RX_PRU_0); + if (tx_err_cnt > PRUSS_CAN_ERROR_ACTIVE - 1) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + if (rx_err_cnt > PRUSS_CAN_ERROR_ACTIVE - 1) + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + + dev_dbg(priv->ndev->dev.parent, "Error passive interrupt\n"); + } + + if (err_status & PRUSS_CAN_GSR_BIT_BFM) { + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + /* + * Disable all interrupts in bus-off to avoid int hog + * this should be handled by the pru + */ + pru_can_mask_ints(priv->dev, PRUSS_CAN_TX_PRU_1, false); + pru_can_mask_ints(priv->dev, PRUSS_CAN_RX_PRU_0, false); + can_bus_off(ndev); + dev_dbg(priv->ndev->dev.parent, "Bus off mode\n"); + } + + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + return 0; +} + +static int pru_can_rx_poll(struct napi_struct *napi, int quota) +{ + struct net_device *ndev = napi->dev; + struct can_emu_priv *priv = netdev_priv(ndev); + u32 bit_set, mbxno = 0; + u32 num_pkts = 0; + + if (!netif_running(ndev)) + return 0; + + do { + /* rx int sys_evt -> 33 */ + pru_can_clr_intc_status(priv->dev, PRUSS_CAN_RX_PRU_0); + if (pru_can_intr_stat_get(priv->dev, &priv->can_rx_cntx)) + return num_pkts; + + if (PRUSS_CAN_ISR_BIT_RRI & priv->can_rx_cntx.intr_stat) { + mbxno = PRUSS_CAN_RTR_BUFF_NUM; + pru_can_rx(ndev, mbxno); + num_pkts++; + } else { + /* Extract the mboxno from the status */ + bit_set = fls(priv->can_rx_cntx.intr_stat & 0xFF); + if (bit_set) { + num_pkts++; + mbxno = bit_set - 1; + if (PRUSS_CAN_ISR_BIT_ESI & priv->can_rx_cntx. + intr_stat) { + pru_can_gbl_stat_get(priv->dev, + &priv->can_rx_cntx); + pru_can_err(ndev, + priv->can_rx_cntx.intr_stat, + priv->can_rx_cntx.gbl_stat); + } else + pru_can_rx(ndev, mbxno); + } else + break; + } + } while (((PRUSS_CAN_TX_INT_STAT & pru_can_get_intc_status(priv->dev)) + && (num_pkts < quota))); + + /* Enable packet interrupt if all pkts are handled */ + if (!(PRUSS_CAN_TX_INT_STAT & pru_can_get_intc_status(priv->dev))) { + napi_complete(napi); + /* Re-enable RX mailbox interrupts */ + pru_can_mask_ints(priv->dev, PRUSS_CAN_RX_PRU_0, true); + } + return num_pkts; +} + +static irqreturn_t pru_tx_can_intr(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct can_emu_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u32 bit_set, mbxno; + + pru_can_intr_stat_get(priv->dev, &priv->can_tx_cntx); + if ((PRUSS_CAN_ISR_BIT_CCI & priv->can_tx_cntx.intr_stat) + || (PRUSS_CAN_ISR_BIT_SRDI & priv->can_tx_cntx.intr_stat)) { + dev_dbg(priv->ndev->dev.parent, "tx_int_status = 0x%X\n", + priv->can_tx_cntx.intr_stat); + can_free_echo_skb(ndev, 0); + } else { + bit_set = fls(priv->can_tx_cntx.intr_stat & 0xFF); + if (!bit_set) { + dev_err(priv->dev, "%s: invalid mailbox number\n", + __func__); + can_free_echo_skb(ndev, 0); + } else { + mbxno = bit_set - 1; + if (PRUSS_CAN_ISR_BIT_ESI & priv->can_tx_cntx. + intr_stat) { + /* read gsr and ack pru */ + pru_can_gbl_stat_get(priv->dev, + &priv->can_tx_cntx); + pru_can_err(ndev, priv->can_tx_cntx.intr_stat, + priv->can_tx_cntx.gbl_stat); + } else { + stats->tx_packets++; + /* stats->tx_bytes += dlc; */ + /*can_get_echo_skb(ndev, 0);*/ + } + } + } + netif_wake_queue(ndev); + can_get_echo_skb(ndev, 0); + pru_can_tx_mode_set(priv->dev, true, RECEIVE); + return IRQ_HANDLED; +} + +static irqreturn_t pru_rx_can_intr(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct can_emu_priv *priv = netdev_priv(ndev); + u32 intc_status = 0; + + intc_status = pru_can_get_intc_status(priv->dev); + + /* tx int sys_evt -> 34 */ + if (intc_status & 4) { + pru_can_clr_intc_status(priv->dev, PRUSS_CAN_TX_PRU_1); + return pru_tx_can_intr(irq, dev_id); + } + /* Disable RX mailbox interrupts and let NAPI reenable them */ + if (intc_status & 2) { + pru_can_mask_ints(priv->dev, PRUSS_CAN_RX_PRU_0, false); + napi_schedule(&priv->napi); + } + + return IRQ_HANDLED; +} + +static int pru_can_open(struct net_device *ndev) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + s32 err; + + /* register interrupt handler */ + err = request_irq(priv->trx_irq, &pru_rx_can_intr, IRQF_SHARED, + "pru_can_irq", ndev); + if (err) { + dev_err(priv->dev, "error requesting rx interrupt\n"); + goto exit_trx_irq; + } + err = open_candev(ndev); + if (err) { + dev_err(priv->dev, "open_candev() failed %d\n", err); + goto exit_open; + } + + pru_can_emu_init(priv->dev, priv->can.clock.freq); + priv->tx_tail = PRUSS_CAN_MB_MIN; + priv->tx_head = PRUSS_CAN_MB_MAX; + pru_can_start(ndev); + napi_enable(&priv->napi); + netif_start_queue(ndev); + return 0; + +exit_open: + free_irq(priv->trx_irq, ndev); +exit_trx_irq: + return err; +} + +static int pru_can_close(struct net_device *ndev) +{ + struct can_emu_priv *priv = netdev_priv(ndev); + + netif_stop_queue(ndev); + napi_disable(&priv->napi); + close_candev(ndev); + free_irq(priv->trx_irq, ndev); + return 0; +} + +static const struct net_device_ops pru_can_netdev_ops = { + .ndo_open = pru_can_open, + .ndo_stop = pru_can_close, + .ndo_start_xmit = pru_can_start_xmit, +}; + +/* Shows all the mailbox IDs */ +static ssize_t pru_sysfs_mbx_id_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct can_emu_priv *priv = netdev_priv(to_net_dev(dev)); + + return snprintf(buf, PAGE_SIZE, "\n" + "0:0x%X 1:0x%X 2:0x%X 3:0x%X " + "4:0x%X 5:0x%X 6:0x%X 7:0x%X\n", + priv->mbx_id[0], priv->mbx_id[1], + priv->mbx_id[2], priv->mbx_id[3], + priv->mbx_id[4], priv->mbx_id[5], + priv->mbx_id[6], priv->mbx_id[7]); +} + +/* + * Sets Mailbox IDs + * This should be programmed as mbx_num:mbx_id (in hex) + * eg: $ echo 0:0x123 > /sys/class/net/can0/mbx_id + */ +static ssize_t pru_sysfs_mbx_id_set(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct net_device *ndev = to_net_dev(dev); + struct can_emu_priv *priv = netdev_priv(ndev); + unsigned long can_id; + unsigned long mbx_num; + char mbx[2] = {*buf, '\0'}; /* mbx num */ + ssize_t ret = count; + s32 err; + + if (ndev->flags & IFF_UP) { + ret = -EBUSY; + goto out; + } + + if (*(buf + 1) != ':') { + ret = -EINVAL; + goto out; + } + + err = strict_strtoul(mbx, 0, &mbx_num); + if (err) { + ret = err; + goto out; + } + + if (mbx_num > 7) { + ret = -EINVAL; + goto out; + } + + err = strict_strtoul((buf + 2), 0, &can_id); + if (err) { + ret = err; + goto out; + } + + priv->mbx_id[mbx_num] = can_id; + pru_can_rx_id_set(priv->dev, priv->mbx_id[mbx_num], mbx_num); + + return ret; +out: + dev_err(priv->dev, "invalid buffer format\n"); + return ret; +} + +static DEVICE_ATTR(mbx_id, S_IWUSR | S_IRUGO, + pru_sysfs_mbx_id_show, pru_sysfs_mbx_id_set); + +static struct attribute *pru_sysfs_attrs[] = { + &dev_attr_mbx_id.attr, + NULL, +}; + +static struct attribute_group pru_sysfs_attr_group = { + .attrs = pru_sysfs_attrs, +}; + +static int __devinit pru_can_probe(struct platform_device *pdev) +{ + struct net_device *ndev = NULL; + const struct da850_evm_pruss_can_data *pdata; + struct can_emu_priv *priv = NULL; + struct device *dev = &pdev->dev; + struct clk *clk_pruss; + const struct firmware *fw_rx; + const struct firmware *fw_tx; + u32 err; + + pdata = dev->platform_data; + if (!pdata) { + dev_err(&pdev->dev, "platform data not found\n"); + return -EINVAL; + } + (pdata->setup)(); + + ndev = alloc_candev(sizeof(struct can_emu_priv), PRUSS_CAN_MB_MAX + 1); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev failed\n"); + err = -ENOMEM; + goto probe_exit; + } + + ndev->sysfs_groups[0] = &pru_sysfs_attr_group; + + priv = netdev_priv(ndev); + + priv->trx_irq = platform_get_irq(to_platform_device(dev->parent), 0); + if (!priv->trx_irq) { + dev_err(&pdev->dev, "unable to get pru " + "interrupt resources!\n"); + err = -ENODEV; + goto probe_exit; + } + + priv->ndev = ndev; + priv->dev = dev; + + priv->can.bittiming_const = &pru_can_bittiming_const; + priv->can.do_set_bittiming = pru_can_set_bittiming; + priv->can.do_set_mode = pru_can_set_mode; + priv->can.do_get_state = pru_can_get_state; + priv->can_tx_cntx.pruno = PRUSS_CAN_TX_PRU_1; + priv->can_rx_cntx.pruno = PRUSS_CAN_RX_PRU_0; + + /* we support local echo, no arp */ + ndev->flags |= (IFF_ECHO | IFF_NOARP); + + /* pdev->dev->device_private->driver_data = ndev */ + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + ndev->netdev_ops = &pru_can_netdev_ops; + + priv->clk_timer = clk_get(&pdev->dev, "pll1_sysclk2"); + if (IS_ERR(priv->clk_timer)) { + dev_err(&pdev->dev, "no timer clock available\n"); + err = PTR_ERR(priv->clk_timer); + priv->clk_timer = NULL; + goto probe_exit_candev; + } + + priv->can.clock.freq = clk_get_rate(priv->clk_timer); + + clk_pruss = clk_get(NULL, "pruss"); + if (IS_ERR(clk_pruss)) { + dev_err(&pdev->dev, "no clock available: pruss\n"); + err = -ENODEV; + goto probe_exit_clk; + } + priv->clk_freq_pru = clk_get_rate(clk_pruss); + clk_put(clk_pruss); + + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "register_candev() failed\n"); + err = -ENODEV; + goto probe_exit_clk; + } + + err = request_firmware(&fw_tx, "PRU_CAN_Emulation_Tx.bin", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "can't load firmware\n"); + err = -ENODEV; + goto probe_exit_clk; + } + + dev_info(&pdev->dev, "fw_tx size %d. downloading...\n", fw_tx->size); + + err = request_firmware(&fw_rx, "PRU_CAN_Emulation_Rx.bin", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "can't load firmware\n"); + err = -ENODEV; + goto probe_release_fw; + } + dev_info(&pdev->dev, "fw_rx size %d. downloading...\n", fw_rx->size); + + /* init the pru */ + pru_can_emu_init(priv->dev, priv->can.clock.freq); + udelay(200); + + netif_napi_add(ndev, &priv->napi, pru_can_rx_poll, + PRUSS_DEF_NAPI_WEIGHT); + + pruss_enable(priv->dev, PRUSS_CAN_RX_PRU_0); + pruss_enable(priv->dev, PRUSS_CAN_TX_PRU_1); + + /* download firmware into pru */ + err = pruss_load(priv->dev, PRUSS_CAN_RX_PRU_0, + (u32 *)fw_rx->data, (fw_rx->size / 4)); + if (err) { + dev_err(&pdev->dev, "firmware download error\n"); + err = -ENODEV; + goto probe_release_fw_1; + } + release_firmware(fw_rx); + err = pruss_load(priv->dev, PRUSS_CAN_TX_PRU_1, + (u32 *)fw_tx->data, (fw_tx->size / 4)); + if (err) { + dev_err(&pdev->dev, "firmware download error\n"); + err = -ENODEV; + goto probe_release_fw_1; + } + release_firmware(fw_tx); + + pruss_run(priv->dev, PRUSS_CAN_RX_PRU_0); + pruss_run(priv->dev, PRUSS_CAN_TX_PRU_1); + + dev_info(&pdev->dev, + "%s device registered (trx_irq = %d, clk = %d)\n", + PRUSS_CAN_DRV_NAME, priv->trx_irq, priv->can.clock.freq); + + return 0; + +probe_release_fw_1: + release_firmware(fw_rx); +probe_release_fw: + release_firmware(fw_tx); +probe_exit_clk: + clk_put(priv->clk_timer); +probe_exit_candev: + if (NULL != ndev) + free_candev(ndev); +probe_exit: + return err; +} + +static int __devexit pru_can_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct can_emu_priv *priv = netdev_priv(ndev); + + pru_can_stop(ndev); + pru_can_emu_exit(priv->dev); + clk_put(priv->clk_timer); + unregister_candev(ndev); + free_candev(ndev); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int pru_can_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + dev_info(&pdev->dev, "%s not yet implemented\n", __func__); + return 0; +} + +static int pru_can_resume(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s not yet implemented\n", __func__); + return 0; +} +#else +#define pru_can_suspend NULL +#define pru_can_resume NULL +#endif + +static struct platform_driver omapl_pru_can_driver = { + .probe = pru_can_probe, + .remove = __devexit_p(pru_can_remove), + .suspend = pru_can_suspend, + .resume = pru_can_resume, + .driver = { + .name = PRUSS_CAN_DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init pru_can_init(void) +{ + pr_debug(KERN_INFO PRUSS_CAN_DRV_DESC "\n"); + return platform_driver_register(&omapl_pru_can_driver); +} + +module_init(pru_can_init); + +static void __exit pru_can_exit(void) +{ + pr_debug(KERN_INFO PRUSS_CAN_DRV_DESC " unloaded\n"); + platform_driver_unregister(&omapl_pru_can_driver); +} + +module_exit(pru_can_exit); + +MODULE_AUTHOR("Subhasish Ghosh "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("omapl pru CAN netdevice driver"); -- 1.7.2.3 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From grant.likely at secretlab.ca Fri Jul 8 12:31:37 2011 From: grant.likely at secretlab.ca (Grant Likely) Date: Fri, 08 Jul 2011 17:31:37 -0000 Subject: [PATCH v7 04/12] spi: add ti-ssp spi master driver In-Reply-To: <1291733522-3626-5-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> <1291733522-3626-5-git-send-email-cyril@ti.com> Message-ID: <20101231062655.GB3733@angua.secretlab.ca> On Tue, Dec 07, 2010 at 09:51:54AM -0500, Cyril Chemparathy wrote: > This patch adds an SPI master implementation that operates on top of an > underlying TI-SSP port. > > Signed-off-by: Cyril Chemparathy Mostly looks okay, but I'm not thrilled with the ss scheme (see comments below). g. > --- > drivers/spi/Kconfig | 10 + > drivers/spi/Makefile | 1 + > drivers/spi/ti-ssp-spi.c | 402 ++++++++++++++++++++++++++++++++++++++++++++ > include/linux/mfd/ti_ssp.h | 6 + > 4 files changed, 419 insertions(+), 0 deletions(-) > create mode 100644 drivers/spi/ti-ssp-spi.c > > diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig > index 78f9fd0..7f0ed2a 100644 > --- a/drivers/spi/Kconfig > +++ b/drivers/spi/Kconfig > @@ -336,6 +336,16 @@ config SPI_TEGRA > help > SPI driver for NVidia Tegra SoCs > > +config SPI_TI_SSP > + tristate "TI Sequencer Serial Port - SPI Support" > + depends on MFD_TI_SSP > + help > + This selects an SPI master implementation using a TI sequencer > + serial port. > + > + To compile this driver as a module, choose M here: the > + module will be called ti-ssp-spi. > + > config SPI_TOPCLIFF_PCH > tristate "Topcliff PCH SPI Controller" > depends on PCI > diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile > index 8bc1a5a..595e5b8 100644 > --- a/drivers/spi/Makefile > +++ b/drivers/spi/Makefile > @@ -40,6 +40,7 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o > obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o > obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o > obj-$(CONFIG_SPI_TEGRA) += spi_tegra.o > +obj-$(CONFIG_SPI_TI_SSP) += ti-ssp-spi.o > obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o > obj-$(CONFIG_SPI_TXX9) += spi_txx9.o > obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o > diff --git a/drivers/spi/ti-ssp-spi.c b/drivers/spi/ti-ssp-spi.c > new file mode 100644 > index 0000000..30fc0e2 > --- /dev/null > +++ b/drivers/spi/ti-ssp-spi.c > @@ -0,0 +1,402 @@ > +/* > + * Sequencer Serial Port (SSP) based SPI master driver > + * > + * Copyright (C) 2010 Texas Instruments Inc > + * > + * 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; either version 2 of the License, or > + * (at your option) any later version. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + * You should have received a copy of the GNU General Public License > + * along with this program; if not, write to the Free Software > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > + */ > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define MODE_BITS (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH) > + > +struct ti_ssp_spi { > + const struct ti_ssp_spi_data *pdata; Ideally the driver wouldn't keep the pdata pointer around at all. Instead, the relevant fields would be copied out of pdata and into ti_ssp_spi in the .probe() hook. From what I can tell, only the select function is being used for this purpose. > + struct spi_master *master; > + struct device *dev; > + spinlock_t lock; > + struct list_head msg_queue; > + struct completion complete; > + int shutdown:1; Instead of a bitfield, Just make this a 'bool'. > + struct workqueue_struct *workqueue; > + struct work_struct work; > + u8 mode, bpw; > + int cs_active; > + u32 pc_en, pc_dis, pc_wr, pc_rd; > +}; > + > +static u32 do_read_data(struct ti_ssp_spi *hw) As commented in my other reply. All the static private functions still need the ti_ssp_spi_ prefix. > +{ > + u32 ret; > + > + ti_ssp_run(hw->dev, hw->pc_rd, 0, &ret); > + return ret; > +} > + > +static void do_write_data(struct ti_ssp_spi *hw, u32 data) > +{ > + ti_ssp_run(hw->dev, hw->pc_wr, data << (32 - hw->bpw), NULL); > +} > + > +static int do_transfer(struct ti_ssp_spi *hw, struct spi_message *msg, > + struct spi_transfer *t) > +{ > + int count; > + > + if (hw->bpw <= 8) { > + u8 *rx = t->rx_buf; > + const u8 *tx = t->tx_buf; > + > + for (count = 0; count < t->len; count += 1) { > + if (t->tx_buf) > + do_write_data(hw, *tx++); > + if (t->rx_buf) > + *rx++ = do_read_data(hw); > + } > + } else if (hw->bpw <= 16) { > + u16 *rx = t->rx_buf; > + const u16 *tx = t->tx_buf; > + > + for (count = 0; count < t->len; count += 2) { > + if (t->tx_buf) > + do_write_data(hw, *tx++); > + if (t->rx_buf) > + *rx++ = do_read_data(hw); > + } > + } else { > + u32 *rx = t->rx_buf; > + const u32 *tx = t->tx_buf; > + > + for (count = 0; count < t->len; count += 4) { > + if (t->tx_buf) > + do_write_data(hw, *tx++); > + if (t->rx_buf) > + *rx++ = do_read_data(hw); > + } > + } > + > + msg->actual_length += count; /* bytes transferred */ > + > + dev_dbg(&msg->spi->dev, "xfer %s%s, %d bytes, %d bpw, count %d%s\n", > + t->tx_buf ? "tx" : "", t->rx_buf ? "rx" : "", t->len, > + hw->bpw, count, (count < t->len) ? " (under)" : ""); > + > + return (count < t->len) ? -EIO : 0; /* left over data */ > +} > + > +static void chip_select(struct ti_ssp_spi *hw, int cs_active) > +{ > + cs_active = !!cs_active; > + if (cs_active == hw->cs_active) > + return; > + ti_ssp_run(hw->dev, cs_active ? hw->pc_en : hw->pc_dis, 0, NULL); > + hw->cs_active = cs_active; > +} > + > +#define __SHIFT_OUT(bits) (SSP_OPCODE_SHIFT | SSP_OUT_MODE | \ > + cs_en | clk | SSP_COUNT((bits) * 2 - 1)) > +#define __SHIFT_IN(bits) (SSP_OPCODE_SHIFT | SSP_IN_MODE | \ > + cs_en | clk | SSP_COUNT((bits) * 2 - 1)) > + > +static int setup_xfer(struct ti_ssp_spi *hw, u8 bpw, u8 mode) > +{ > + int error, idx = 0; > + u32 seqram[16]; > + u32 cs_en, cs_dis, clk; > + u32 topbits, botbits; > + > + mode &= MODE_BITS; > + if (mode == hw->mode && bpw == hw->bpw) > + return 0; > + > + cs_en = (mode & SPI_CS_HIGH) ? SSP_CS_HIGH : SSP_CS_LOW; > + cs_dis = (mode & SPI_CS_HIGH) ? SSP_CS_LOW : SSP_CS_HIGH; > + clk = (mode & SPI_CPOL) ? SSP_CLK_HIGH : SSP_CLK_LOW; > + > + /* Construct instructions */ > + > + /* Disable Chip Select */ > + hw->pc_dis = idx; > + seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_dis | clk; > + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_dis | clk; > + > + /* Enable Chip Select */ > + hw->pc_en = idx; > + seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_en | clk; > + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; > + > + /* Reads and writes need to be split for bpw > 16 */ > + topbits = (bpw > 16) ? 16 : bpw; > + botbits = bpw - topbits; > + > + /* Write */ > + hw->pc_wr = idx; > + seqram[idx++] = __SHIFT_OUT(topbits) | SSP_ADDR_REG; > + if (botbits) > + seqram[idx++] = __SHIFT_OUT(botbits) | SSP_DATA_REG; > + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; > + > + /* Read */ > + hw->pc_rd = idx; > + if (botbits) > + seqram[idx++] = __SHIFT_IN(botbits) | SSP_ADDR_REG; > + seqram[idx++] = __SHIFT_IN(topbits) | SSP_DATA_REG; > + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; > + > + error = ti_ssp_load(hw->dev, 0, seqram, idx); > + if (error < 0) > + return error; > + > + error = ti_ssp_set_mode(hw->dev, ((mode & SPI_CPHA) ? > + 0 : SSP_EARLY_DIN)); > + if (error < 0) > + return error; > + > + hw->bpw = bpw; > + hw->mode = mode; > + > + return error; > +} > + > +static void ti_ssp_spi_work(struct work_struct *work) > +{ > + struct ti_ssp_spi *hw = container_of(work, struct ti_ssp_spi, work); > + > + spin_lock(&hw->lock); > + > + while (!list_empty(&hw->msg_queue)) { > + struct spi_message *m; > + struct spi_device *spi; > + struct spi_transfer *t = NULL; > + int status = 0; > + > + m = container_of(hw->msg_queue.next, struct spi_message, > + queue); > + > + list_del_init(&m->queue); > + > + spin_unlock(&hw->lock); > + > + spi = m->spi; > + > + if (hw->pdata->select) > + hw->pdata->select(spi->chip_select); Rather than yet another driver-specific slave select function being defined, I'd be happier to see the driver accept a list of gpio numbers to use for non-native ss lines. Using the gpio infrastructure reduces the amount of custom code needed in each bsp. > + > + list_for_each_entry(t, &m->transfers, transfer_list) { > + int bpw = spi->bits_per_word; > + int xfer_status; > + > + if (t->bits_per_word) > + bpw = t->bits_per_word; > + > + if (setup_xfer(hw, bpw, spi->mode) < 0) > + break; > + > + chip_select(hw, 1); > + > + xfer_status = do_transfer(hw, m, t); > + if (xfer_status < 0) > + status = xfer_status; > + > + if (t->delay_usecs) > + udelay(t->delay_usecs); > + > + if (t->cs_change) > + chip_select(hw, 0); > + } > + > + chip_select(hw, 0); > + m->status = status; > + m->complete(m->context); > + > + spin_lock(&hw->lock); > + } > + > + if (hw->shutdown) > + complete(&hw->complete); > + > + spin_unlock(&hw->lock); > +} > + > +static int ti_ssp_spi_setup(struct spi_device *spi) > +{ > + if (spi->bits_per_word > 32) > + return -EINVAL; > + > + return 0; > +} > + > +static int ti_ssp_spi_transfer(struct spi_device *spi, struct spi_message *m) > +{ > + struct ti_ssp_spi *hw; > + struct spi_transfer *t; > + int error = 0; > + > + m->actual_length = 0; > + m->status = -EINPROGRESS; > + > + hw = spi_master_get_devdata(spi->master); > + > + if (list_empty(&m->transfers) || !m->complete) > + return -EINVAL; > + > + list_for_each_entry(t, &m->transfers, transfer_list) { > + if (t->len && !(t->rx_buf || t->tx_buf)) { > + dev_err(&spi->dev, "invalid xfer, no buffer\n"); > + return -EINVAL; > + } > + > + if (t->len && t->rx_buf && t->tx_buf) { > + dev_err(&spi->dev, "invalid xfer, full duplex\n"); > + return -EINVAL; > + } > + > + if (t->bits_per_word > 32) { > + dev_err(&spi->dev, "invalid xfer width %d\n", > + t->bits_per_word); > + return -EINVAL; > + } > + } > + > + spin_lock(&hw->lock); > + if (hw->shutdown) { > + error = -ESHUTDOWN; > + goto error_unlock; > + } > + list_add_tail(&m->queue, &hw->msg_queue); > + queue_work(hw->workqueue, &hw->work); > +error_unlock: > + spin_unlock(&hw->lock); > + return error; > +} > + > +static int __devinit ti_ssp_spi_probe(struct platform_device *pdev) > +{ > + const struct ti_ssp_spi_data *pdata; > + struct ti_ssp_spi *hw; > + struct spi_master *master; > + struct device *dev = &pdev->dev; > + int error = 0; > + > + pdata = dev->platform_data; > + if (!pdata) { > + dev_err(dev, "platform data not found\n"); > + return -EINVAL; > + } > + > + master = spi_alloc_master(dev, sizeof(struct ti_ssp_spi)); > + if (!master) { > + dev_err(dev, "cannot allocate SPI master\n"); > + return -ENOMEM; > + } > + > + hw = spi_master_get_devdata(master); > + platform_set_drvdata(pdev, hw); > + > + hw->master = master; > + hw->dev = dev; > + hw->pdata = pdata; > + > + spin_lock_init(&hw->lock); > + init_completion(&hw->complete); > + INIT_LIST_HEAD(&hw->msg_queue); > + INIT_WORK(&hw->work, ti_ssp_spi_work); > + > + hw->workqueue = create_singlethread_workqueue(dev_name(dev)); > + if (!hw->workqueue) { > + error = -ENOMEM; > + dev_err(dev, "work queue creation failed\n"); > + goto error_wq; > + } > + > + error = ti_ssp_set_iosel(hw->dev, hw->pdata->iosel); > + if (error < 0) { > + dev_err(dev, "io setup failed\n"); > + goto error_iosel; > + } > + > + master->bus_num = pdev->id; > + master->num_chipselect = hw->pdata->num_cs; > + master->mode_bits = MODE_BITS; > + master->flags = SPI_MASTER_HALF_DUPLEX; > + master->setup = ti_ssp_spi_setup; > + master->transfer = ti_ssp_spi_transfer; > + > + error = spi_register_master(master); > + if (error) { > + dev_err(dev, "master registration failed\n"); > + goto error_reg; > + } > + > + return 0; > + > +error_reg: > +error_iosel: > + destroy_workqueue(hw->workqueue); > +error_wq: > + spi_master_put(master); > + return error; > +} > + > +static int __devexit ti_ssp_spi_remove(struct platform_device *pdev) > +{ > + struct ti_ssp_spi *hw = platform_get_drvdata(pdev); > + int error; > + > + hw->shutdown = 1; > + while (!list_empty(&hw->msg_queue)) { > + error = wait_for_completion_interruptible(&hw->complete); > + if (error < 0) { > + hw->shutdown = 0; > + return error; > + } > + } > + destroy_workqueue(hw->workqueue); > + spi_unregister_master(hw->master); > + > + return 0; > +} > + > +static struct platform_driver ti_ssp_spi_driver = { > + .probe = ti_ssp_spi_probe, > + .remove = __devexit_p(ti_ssp_spi_remove), > + .driver = { > + .name = "ti-ssp-spi", > + .owner = THIS_MODULE, > + }, > +}; > + > +static int __init ti_ssp_spi_init(void) > +{ > + return platform_driver_register(&ti_ssp_spi_driver); > +} > +module_init(ti_ssp_spi_init); > + > +static void __exit ti_ssp_spi_exit(void) > +{ > + platform_driver_unregister(&ti_ssp_spi_driver); > +} > +module_exit(ti_ssp_spi_exit); > + > +MODULE_DESCRIPTION("SSP SPI Master"); > +MODULE_AUTHOR("Cyril Chemparathy"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("platform:ti-ssp-spi"); > diff --git a/include/linux/mfd/ti_ssp.h b/include/linux/mfd/ti_ssp.h > index 021fe09..dbb4b43 100644 > --- a/include/linux/mfd/ti_ssp.h > +++ b/include/linux/mfd/ti_ssp.h > @@ -32,6 +32,12 @@ struct ti_ssp_data { > struct ti_ssp_dev_data dev_data[2]; > }; > > +struct ti_ssp_spi_data { > + unsigned long iosel; > + int num_cs; > + void (*select)(int cs); > +}; > + > /* > * Sequencer port IO pin configuration bits. These do not correlate 1-1 with > * the hardware. The iosel field in the port data combines iosel1 and iosel2, > -- > 1.7.1 > _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From cyril at ti.com Fri Jul 8 12:35:52 2011 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 08 Jul 2011 17:35:52 -0000 Subject: [PATCH v7 01/12] misc: add driver for sequencer serial port In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-2-git-send-email-cyril@ti.com> TI's sequencer serial port (TI-SSP) is a jack-of-all-trades type of serial port device. It has a built-in programmable execution engine that can be programmed to operate as almost any serial bus (I2C, SPI, EasyScale, and others). This patch adds a driver for this controller device. The driver does not expose a user-land interface. Protocol drivers built on top of this layer are expected to remain in-kernel. Signed-off-by: Cyril Chemparathy --- drivers/mfd/Kconfig | 11 + drivers/mfd/Makefile | 1 + drivers/mfd/ti-ssp.c | 476 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/ti_ssp.h | 87 ++++++++ 4 files changed, 575 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/ti-ssp.c create mode 100644 include/linux/mfd/ti_ssp.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3a1493b..a4b4b70 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -81,6 +81,17 @@ 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_TI_SSP + tristate "TI Sequencer Serial Port support" + depends on ARCH_DAVINCI_TNETV107X + select MFD_CORE + ---help--- + Say Y here if you want support for the Sequencer Serial Port + in a Texas Instruments TNETV107X SoC. + + To compile this driver as a module, choose M here: the + module will be called ti-ssp. + config HTC_EGPIO bool "HTC EGPIO support" depends on GENERIC_HARDIRQS && GPIOLIB && ARM diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index f54b365..f64cf13 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_HTC_I2CPLD) += htc-i2cpld.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 obj-$(CONFIG_MFD_STMPE) += stmpe.o obj-$(CONFIG_MFD_TC35892) += tc35892.o diff --git a/drivers/mfd/ti-ssp.c b/drivers/mfd/ti-ssp.c new file mode 100644 index 0000000..af9ab0e --- /dev/null +++ b/drivers/mfd/ti-ssp.c @@ -0,0 +1,476 @@ +/* + * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs + * + * Copyright (C) 2010 Texas Instruments Inc + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register Offsets */ +#define REG_REV 0x00 +#define REG_IOSEL_1 0x04 +#define REG_IOSEL_2 0x08 +#define REG_PREDIV 0x0c +#define REG_INTR_ST 0x10 +#define REG_INTR_EN 0x14 +#define REG_TEST_CTRL 0x18 + +/* Per port registers */ +#define PORT_CFG_2 0x00 +#define PORT_ADDR 0x04 +#define PORT_DATA 0x08 +#define PORT_CFG_1 0x0c +#define PORT_STATE 0x10 + +#define SSP_PORT_CONFIG_MASK (SSP_EARLY_DIN | SSP_DELAY_DOUT) +#define SSP_PORT_CLKRATE_MASK 0x0f + +#define SSP_SEQRAM_WR_EN BIT(4) +#define SSP_SEQRAM_RD_EN BIT(5) +#define SSP_START BIT(15) +#define SSP_BUSY BIT(10) +#define SSP_PORT_ASL BIT(7) +#define SSP_PORT_CFO1 BIT(6) + +#define SSP_PORT_SEQRAM_SIZE 32 + +static const int ssp_port_base[] = {0x040, 0x080}; +static const int ssp_port_seqram[] = {0x100, 0x180}; + +struct ti_ssp { + struct resource *res; + struct device *dev; + void __iomem *regs; + spinlock_t lock; + struct clk *clk; + int irq; + wait_queue_head_t wqh; + + /* + * Some of the iosel2 register bits always read-back as 0, we need to + * remember these values so that we don't clobber previously set + * values. + */ + u32 iosel2; +}; + +static inline struct ti_ssp *dev_to_ssp(struct device *dev) +{ + return dev_get_drvdata(dev->parent); +} + +static inline int dev_to_port(struct device *dev) +{ + return to_platform_device(dev)->id; +} + +/* Register Access Helpers, rmw() functions need to run locked */ +static inline u32 ssp_read(struct ti_ssp *ssp, int reg) +{ + return __raw_readl(ssp->regs + reg); +} + +static inline void ssp_write(struct ti_ssp *ssp, int reg, u32 val) +{ + __raw_writel(val, ssp->regs + reg); +} + +static inline void ssp_rmw(struct ti_ssp *ssp, int reg, u32 mask, u32 bits) +{ + ssp_write(ssp, reg, (ssp_read(ssp, reg) & ~mask) | bits); +} + +static inline u32 ssp_port_read(struct ti_ssp *ssp, int port, int reg) +{ + return ssp_read(ssp, ssp_port_base[port] + reg); +} + +static inline void ssp_port_write(struct ti_ssp *ssp, int port, int reg, + u32 val) +{ + ssp_write(ssp, ssp_port_base[port] + reg, val); +} + +static inline void ssp_port_rmw(struct ti_ssp *ssp, int port, int reg, + u32 mask, u32 bits) +{ + ssp_rmw(ssp, ssp_port_base[port] + reg, mask, bits); +} + +static inline void ssp_port_clr_bits(struct ti_ssp *ssp, int port, int reg, + u32 bits) +{ + ssp_port_rmw(ssp, port, reg, bits, 0); +} + +static inline void ssp_port_set_bits(struct ti_ssp *ssp, int port, int reg, + u32 bits) +{ + ssp_port_rmw(ssp, port, reg, 0, bits); +} + +/* Called to setup port clock mode, caller must hold ssp->lock */ +static int __set_mode(struct ti_ssp *ssp, int port, int mode) +{ + mode &= SSP_PORT_CONFIG_MASK; + ssp_port_rmw(ssp, port, PORT_CFG_1, SSP_PORT_CONFIG_MASK, mode); + + return 0; +} + +int ti_ssp_set_mode(struct device *dev, int mode) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int ret; + + spin_lock(&ssp->lock); + ret = __set_mode(ssp, port, mode); + spin_unlock(&ssp->lock); + + return ret; +} +EXPORT_SYMBOL(ti_ssp_set_mode); + +/* Called to setup iosel2, caller must hold ssp->lock */ +static void __set_iosel2(struct ti_ssp *ssp, u32 mask, u32 val) +{ + ssp->iosel2 = (ssp->iosel2 & ~mask) | val; + ssp_write(ssp, REG_IOSEL_2, ssp->iosel2); +} + +/* Called to setup port iosel, caller must hold ssp->lock */ +static void __set_iosel(struct ti_ssp *ssp, int port, u32 iosel) +{ + unsigned val, shift = port ? 16 : 0; + + /* IOSEL1 gets the least significant 16 bits */ + val = ssp_read(ssp, REG_IOSEL_1); + val &= 0xffff << (port ? 0 : 16); + val |= (iosel & 0xffff) << (port ? 16 : 0); + ssp_write(ssp, REG_IOSEL_1, val); + + /* IOSEL2 gets the most significant 16 bits */ + val = (iosel >> 16) & 0x7; + __set_iosel2(ssp, 0x7 << shift, val << shift); +} + +int ti_ssp_set_iosel(struct device *dev, u32 iosel) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + + spin_lock(&ssp->lock); + __set_iosel(ssp, port, iosel); + spin_unlock(&ssp->lock); + + return 0; +} +EXPORT_SYMBOL(ti_ssp_set_iosel); + +int ti_ssp_load(struct device *dev, int offs, u32* prog, int len) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int i; + + if (len > SSP_PORT_SEQRAM_SIZE) + return -ENOSPC; + + spin_lock(&ssp->lock); + + /* Enable SeqRAM access */ + ssp_port_set_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN); + + /* Copy code */ + for (i = 0; i < len; i++) { + __raw_writel(prog[i], ssp->regs + offs + 4*i + + ssp_port_seqram[port]); + } + + /* Disable SeqRAM access */ + ssp_port_clr_bits(ssp, port, PORT_CFG_2, SSP_SEQRAM_WR_EN); + + spin_unlock(&ssp->lock); + + return 0; +} +EXPORT_SYMBOL(ti_ssp_load); + +int ti_ssp_raw_read(struct device *dev) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int shift = port ? 27 : 11; + + return (ssp_read(ssp, REG_IOSEL_2) >> shift) & 0xf; +} +EXPORT_SYMBOL(ti_ssp_raw_read); + +int ti_ssp_raw_write(struct device *dev, u32 val) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev), shift; + + spin_lock(&ssp->lock); + + shift = port ? 22 : 6; + val &= 0xf; + __set_iosel2(ssp, 0xf << shift, val << shift); + + spin_unlock(&ssp->lock); + + return 0; +} +EXPORT_SYMBOL(ti_ssp_raw_write); + +static inline int __xfer_done(struct ti_ssp *ssp, int port) +{ + return !(ssp_port_read(ssp, port, PORT_CFG_1) & SSP_BUSY); +} + +int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output) +{ + struct ti_ssp *ssp = dev_to_ssp(dev); + int port = dev_to_port(dev); + int ret; + + if (pc & ~(0x3f)) + return -EINVAL; + + /* Grab ssp->lock to serialize rmw on ssp registers */ + spin_lock(&ssp->lock); + + ssp_port_write(ssp, port, PORT_ADDR, input >> 16); + ssp_port_write(ssp, port, PORT_DATA, input & 0xffff); + ssp_port_rmw(ssp, port, PORT_CFG_1, 0x3f, pc); + + /* grab wait queue head lock to avoid race with the isr */ + spin_lock_irq(&ssp->wqh.lock); + + /* kick off sequence execution in hardware */ + ssp_port_set_bits(ssp, port, PORT_CFG_1, SSP_START); + + /* drop ssp lock; no register writes beyond this */ + spin_unlock(&ssp->lock); + + ret = wait_event_interruptible_locked_irq(ssp->wqh, + __xfer_done(ssp, port)); + spin_unlock_irq(&ssp->wqh.lock); + + if (ret < 0) + return ret; + + if (output) { + *output = (ssp_port_read(ssp, port, PORT_ADDR) << 16) | + (ssp_port_read(ssp, port, PORT_DATA) & 0xffff); + } + + ret = ssp_port_read(ssp, port, PORT_STATE) & 0x3f; /* stop address */ + + return ret; +} +EXPORT_SYMBOL(ti_ssp_run); + +static irqreturn_t ti_ssp_interrupt(int irq, void *dev_data) +{ + struct ti_ssp *ssp = dev_data; + + spin_lock(&ssp->wqh.lock); + + ssp_write(ssp, REG_INTR_ST, 0x3); + wake_up_locked(&ssp->wqh); + + spin_unlock(&ssp->wqh.lock); + + return IRQ_HANDLED; +} + +static int __devinit ti_ssp_probe(struct platform_device *pdev) +{ + static struct ti_ssp *ssp; + const struct ti_ssp_data *pdata = pdev->dev.platform_data; + int error = 0, prediv = 0xff, id; + unsigned long sysclk; + struct device *dev = &pdev->dev; + struct mfd_cell cells[2]; + + ssp = kzalloc(sizeof(*ssp), GFP_KERNEL); + if (!ssp) { + dev_err(dev, "cannot allocate device info\n"); + return -ENOMEM; + } + + ssp->dev = dev; + dev_set_drvdata(dev, ssp); + + ssp->res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!ssp->res) { + error = -ENODEV; + dev_err(dev, "cannot determine register area\n"); + goto error_res; + } + + if (!request_mem_region(ssp->res->start, resource_size(ssp->res), + pdev->name)) { + error = -ENOMEM; + dev_err(dev, "cannot claim register memory\n"); + goto error_res; + } + + ssp->regs = ioremap(ssp->res->start, resource_size(ssp->res)); + if (!ssp->regs) { + error = -ENOMEM; + dev_err(dev, "cannot map register memory\n"); + goto error_map; + } + + ssp->clk = clk_get(dev, NULL); + if (IS_ERR(ssp->clk)) { + error = PTR_ERR(ssp->clk); + dev_err(dev, "cannot claim device clock\n"); + goto error_clk; + } + + ssp->irq = platform_get_irq(pdev, 0); + if (ssp->irq < 0) { + error = -ENODEV; + dev_err(dev, "unknown irq\n"); + goto error_irq; + } + + error = request_threaded_irq(ssp->irq, NULL, ti_ssp_interrupt, 0, + dev_name(dev), ssp); + if (error < 0) { + dev_err(dev, "cannot acquire irq\n"); + goto error_irq; + } + + spin_lock_init(&ssp->lock); + init_waitqueue_head(&ssp->wqh); + + /* Power on and initialize SSP */ + error = clk_enable(ssp->clk); + if (error) { + dev_err(dev, "cannot enable device clock\n"); + goto error_enable; + } + + /* Reset registers to a sensible known state */ + ssp_write(ssp, REG_IOSEL_1, 0); + ssp_write(ssp, REG_IOSEL_2, 0); + ssp_write(ssp, REG_INTR_EN, 0x3); + ssp_write(ssp, REG_INTR_ST, 0x3); + ssp_write(ssp, REG_TEST_CTRL, 0); + ssp_port_write(ssp, 0, PORT_CFG_1, SSP_PORT_ASL); + ssp_port_write(ssp, 1, PORT_CFG_1, SSP_PORT_ASL); + ssp_port_write(ssp, 0, PORT_CFG_2, SSP_PORT_CFO1); + ssp_port_write(ssp, 1, PORT_CFG_2, SSP_PORT_CFO1); + + sysclk = clk_get_rate(ssp->clk); + if (pdata && pdata->out_clock) + prediv = (sysclk / pdata->out_clock) - 1; + prediv = clamp(prediv, 0, 0xff); + ssp_rmw(ssp, REG_PREDIV, 0xff, prediv); + + memset(cells, 0, sizeof(cells)); + for (id = 0; id < 2; id++) { + const struct ti_ssp_dev_data *data = &pdata->dev_data[id]; + + cells[id].id = id; + cells[id].name = data->dev_name; + cells[id].platform_data = data->pdata; + cells[id].data_size = data->pdata_size; + } + + error = mfd_add_devices(dev, 0, cells, 2, NULL, 0); + if (error < 0) { + dev_err(dev, "cannot add mfd cells\n"); + goto error_enable; + } + + return 0; + +error_enable: + free_irq(ssp->irq, ssp); +error_irq: + clk_put(ssp->clk); +error_clk: + iounmap(ssp->regs); +error_map: + release_mem_region(ssp->res->start, resource_size(ssp->res)); +error_res: + kfree(ssp); + return error; +} + +static int __devexit ti_ssp_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ti_ssp *ssp = dev_get_drvdata(dev); + + mfd_remove_devices(dev); + clk_disable(ssp->clk); + free_irq(ssp->irq, ssp); + clk_put(ssp->clk); + iounmap(ssp->regs); + release_mem_region(ssp->res->start, resource_size(ssp->res)); + kfree(ssp); + dev_set_drvdata(dev, NULL); + return 0; +} + +static struct platform_driver ti_ssp_driver = { + .probe = ti_ssp_probe, + .remove = __devexit_p(ti_ssp_remove), + .driver = { + .name = "ti-ssp", + .owner = THIS_MODULE, + } +}; + +static int __init ti_ssp_init(void) +{ + return platform_driver_register(&ti_ssp_driver); +} +module_init(ti_ssp_init); + +static void __exit ti_ssp_exit(void) +{ + platform_driver_unregister(&ti_ssp_driver); +} +module_exit(ti_ssp_exit); + +MODULE_DESCRIPTION("Sequencer Serial Port (SSP) Driver"); +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ti-ssp"); diff --git a/include/linux/mfd/ti_ssp.h b/include/linux/mfd/ti_ssp.h new file mode 100644 index 0000000..021fe09 --- /dev/null +++ b/include/linux/mfd/ti_ssp.h @@ -0,0 +1,87 @@ +/* + * Sequencer Serial Port (SSP) driver for Texas Instruments' SoCs + * + * Copyright (C) 2010 Texas Instruments Inc + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __TI_SSP_H__ +#define __TI_SSP_H__ + +struct ti_ssp_dev_data { + const char *dev_name; + void *pdata; + size_t pdata_size; +}; + +struct ti_ssp_data { + unsigned long out_clock; + struct ti_ssp_dev_data dev_data[2]; +}; + +/* + * Sequencer port IO pin configuration bits. These do not correlate 1-1 with + * the hardware. The iosel field in the port data combines iosel1 and iosel2, + * and is therefore not a direct map to register space. It is best to use the + * macros below to construct iosel values. + * + * least significant 16 bits --> iosel1 + * most significant 16 bits --> iosel2 + */ + +#define SSP_IN 0x0000 +#define SSP_DATA 0x0001 +#define SSP_CLOCK 0x0002 +#define SSP_CHIPSEL 0x0003 +#define SSP_OUT 0x0004 +#define SSP_PIN_SEL(pin, v) ((v) << ((pin) * 3)) +#define SSP_PIN_MASK(pin) SSP_PIN_SEL(pin, 0x7) +#define SSP_INPUT_SEL(pin) ((pin) << 16) + +/* Sequencer port config bits */ +#define SSP_EARLY_DIN BIT(8) +#define SSP_DELAY_DOUT BIT(9) + +/* Sequence map definitions */ +#define SSP_CLK_HIGH BIT(0) +#define SSP_CLK_LOW 0 +#define SSP_DATA_HIGH BIT(1) +#define SSP_DATA_LOW 0 +#define SSP_CS_HIGH BIT(2) +#define SSP_CS_LOW 0 +#define SSP_OUT_MODE BIT(3) +#define SSP_IN_MODE 0 +#define SSP_DATA_REG BIT(4) +#define SSP_ADDR_REG 0 + +#define SSP_OPCODE_DIRECT ((0x0) << 5) +#define SSP_OPCODE_TOGGLE ((0x1) << 5) +#define SSP_OPCODE_SHIFT ((0x2) << 5) +#define SSP_OPCODE_BRANCH0 ((0x4) << 5) +#define SSP_OPCODE_BRANCH1 ((0x5) << 5) +#define SSP_OPCODE_BRANCH ((0x6) << 5) +#define SSP_OPCODE_STOP ((0x7) << 5) +#define SSP_BRANCH(addr) ((addr) << 8) +#define SSP_COUNT(cycles) ((cycles) << 8) + +int ti_ssp_raw_read(struct device *dev); +int ti_ssp_raw_write(struct device *dev, u32 val); +int ti_ssp_load(struct device *dev, int offs, u32* prog, int len); +int ti_ssp_run(struct device *dev, u32 pc, u32 input, u32 *output); +int ti_ssp_set_mode(struct device *dev, int mode); +int ti_ssp_set_iosel(struct device *dev, u32 iosel); + +#endif /* __TI_SSP_H__ */ -- 1.7.1 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From subhasish at mistralsolutions.com Fri Jul 8 12:40:07 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 17:40:07 -0000 Subject: [PATCH v3 1/7] mfd: add pruss mfd driver. In-Reply-To: <1299592667-21367-1-git-send-email-subhasish@mistralsolutions.com> References: <1299592667-21367-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1299592667-21367-2-git-send-email-subhasish@mistralsolutions.com> This patch adds the pruss MFD driver and associated include files. For details regarding the PRUSS please refer the folowing link: http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem The rational behind the MFD driver being the fact that multiple devices can be implemented on the cores independently. This is determined by the nature of the program which is loaded into the PRU's instruction memory. A device may be de-initialized and another loaded or two different devices can be run simultaneously on the two cores. It's also possible, as in our case, to implement a single device on both the PRU's resulting in improved load sharing. Signed-off-by: Subhasish Ghosh --- drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/da8xx_pru.c | 560 +++++++++++++++++++++++++++++++ include/linux/mfd/da8xx/da8xx_pru.h | 135 ++++++++ include/linux/mfd/da8xx/da8xx_prucore.h | 129 +++++++ 5 files changed, 835 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/da8xx_pru.c create mode 100644 include/linux/mfd/da8xx/da8xx_pru.h create mode 100644 include/linux/mfd/da8xx/da8xx_prucore.h 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 + * + * 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 +#include +#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 + * + * 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 + +#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 -- 1.7.2.3 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From subhasish at mistralsolutions.com Fri Jul 8 13:25:20 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 18:25:20 -0000 Subject: [PATCH v4 01/11] mfd: add pruss mfd driver. In-Reply-To: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1303474109-6212-2-git-send-email-subhasish@mistralsolutions.com> This patch adds the pruss MFD driver and associated include files. For details regarding the PRUSS please refer the folowing link: http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem The rational behind the MFD driver being the fact that multiple devices can be implemented on the cores independently. This is determined by the nature of the program which is loaded into the PRU's instruction memory. A device may be de-initialized and another loaded or two different devices can be run simultaneously on the two cores. It's also possible, as in our case, to implement a single device on both the PRU's resulting in improved load sharing. Signed-off-by: Subhasish Ghosh --- drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/pruss.c | 513 ++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/pruss.h | 130 ++++++++++ include/linux/mfd/pruss_core.h | 128 ++++++++++ 5 files changed, 782 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/pruss.c create mode 100644 include/linux/mfd/pruss.h create mode 100644 include/linux/mfd/pruss_core.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 0284c53..41479e4 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -92,6 +92,16 @@ config MFD_TI_SSP To compile this driver as a module, choose M here: the module will be called ti-ssp. +config MFD_DA8XX_PRUSS + tristate "Texas Instruments DA8XX PRUSS support" + depends on ARCH_DAVINCI_DA850 + select MFD_CORE + help + This driver provides support API 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 c56b6c7..8015dea 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) += pruss.o obj-$(CONFIG_MFD_DM355EVM_MSP) += dm355evm_msp.o obj-$(CONFIG_MFD_TI_SSP) += ti-ssp.o diff --git a/drivers/mfd/pruss.c b/drivers/mfd/pruss.c new file mode 100644 index 0000000..6836d5a --- /dev/null +++ b/drivers/mfd/pruss.c @@ -0,0 +1,513 @@ +/* + * Copyright (C) 2010, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct pruss_priv { + struct device *dev; + spinlock_t lock; + struct resource *res; + struct clk *clk; + void __iomem *ioaddr; +}; + +s32 pruss_disable(struct device *dev, u8 pruss_num) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct prusscore_regs __iomem *h_pruss; + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 temp_reg; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + spin_lock(&pruss->lock); + + /* pruss deinit */ + iowrite32(0xFFFFFFFF, &pruss_mmap->intc.statclrint[pruss_num]); + + /* Disable PRU */ + h_pruss = &pruss_mmap->core[pruss_num]; + temp_reg = ioread32(&h_pruss->control); + temp_reg = (temp_reg & + ~PRUCORE_CONTROL_COUNTENABLE_MASK) | + ((PRUCORE_CONTROL_COUNTENABLE_DISABLE << + PRUCORE_CONTROL_COUNTENABLE_SHIFT) & + PRUCORE_CONTROL_COUNTENABLE_MASK); + iowrite32(temp_reg, &h_pruss->control); + + temp_reg = ioread32(&h_pruss->control); + temp_reg = (temp_reg & + ~PRUCORE_CONTROL_ENABLE_MASK) | + ((PRUCORE_CONTROL_ENABLE_DISABLE << + PRUCORE_CONTROL_ENABLE_SHIFT) & + PRUCORE_CONTROL_ENABLE_MASK); + iowrite32(temp_reg, &h_pruss->control); + + /* Reset PRU */ + iowrite32(PRUCORE_CONTROL_RESETVAL, + &h_pruss->control); + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_disable); + +s32 pruss_enable(struct device *dev, u8 pruss_num) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct prusscore_regs __iomem *h_pruss; + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 i; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + h_pruss = &pruss_mmap->core[pruss_num]; + + /* Reset PRU */ + spin_lock(&pruss->lock); + iowrite32(PRUCORE_CONTROL_RESETVAL, &h_pruss->control); + spin_unlock(&pruss->lock); + + /* Reset any garbage in the ram */ + if (pruss_num == PRUCORE_0) + for (i = 0; i < PRUSS_PRU0_RAM_SZ; i++) + iowrite32(0x0, &pruss_mmap->dram0[i]); + else if (pruss_num == PRUCORE_1) + for (i = 0; i < PRUSS_PRU1_RAM_SZ; i++) + iowrite32(0x0, &pruss_mmap->dram1[i]); + + return 0; +} +EXPORT_SYMBOL_GPL(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 pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 __iomem *pruss_iram; + u32 i; + + if (pruss_num == PRUCORE_0) + pruss_iram = (u32 __iomem *)&pruss_mmap->iram0; + else if (pruss_num == PRUCORE_1) + pruss_iram = (u32 __iomem *)&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++) + iowrite32(pruss_code[i], (pruss_iram + i)); + + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_load); + +s32 pruss_run(struct device *dev, u8 pruss_num) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct prusscore_regs __iomem *h_pruss; + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 temp_reg; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + h_pruss = &pruss_mmap->core[pruss_num]; + + /* Enable dMAX, let it execute the code we just copied */ + spin_lock(&pruss->lock); + temp_reg = ioread32(&h_pruss->control); + temp_reg = (temp_reg & + ~PRUCORE_CONTROL_COUNTENABLE_MASK) | + ((PRUCORE_CONTROL_COUNTENABLE_ENABLE << + PRUCORE_CONTROL_COUNTENABLE_SHIFT) & + PRUCORE_CONTROL_COUNTENABLE_MASK); + iowrite32(temp_reg, &h_pruss->control); + + temp_reg = ioread32(&h_pruss->control); + temp_reg = (temp_reg & + ~PRUCORE_CONTROL_ENABLE_MASK) | + ((PRUCORE_CONTROL_ENABLE_ENABLE << + PRUCORE_CONTROL_ENABLE_SHIFT) & + PRUCORE_CONTROL_ENABLE_MASK); + iowrite32(temp_reg, &h_pruss->control); + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_run); + +s32 pruss_wait_for_halt(struct device *dev, u8 pruss_num, u32 timeout) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct prusscore_regs __iomem *h_pruss; + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 temp_reg; + u32 cnt = timeout; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + h_pruss = &pruss_mmap->core[pruss_num]; + + while (cnt--) { + temp_reg = ioread32(&h_pruss->control); + if (((temp_reg & PRUCORE_CONTROL_RUNSTATE_MASK) >> + PRUCORE_CONTROL_RUNSTATE_SHIFT) == + PRUCORE_CONTROL_RUNSTATE_HALT) + break; + } + if (!cnt) + return -EBUSY; + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_wait_for_halt); + +s32 pruss_writeb(struct device *dev, u32 offset, u8 pdatatowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstowrite; + + paddresstowrite = pruss->ioaddr + offset; + iowrite8(pdatatowrite, paddresstowrite); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_writeb); + +s32 pruss_rmwb(struct device *dev, u32 offset, u8 mask, u8 val) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddress; + u32 preg_data; + + paddress = pruss->ioaddr + offset; + + spin_lock(&pruss->lock); + preg_data = ioread8(paddress); + preg_data &= ~mask; + preg_data |= val; + iowrite8(preg_data, paddress); + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_rmwb); + +s32 pruss_readb(struct device *dev, u32 offset, u8 *pdatatoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstoread; + + paddresstoread = pruss->ioaddr + offset ; + *pdatatoread = ioread8(paddresstoread); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_readb); + +s32 pruss_readb_multi(struct device *dev, u32 offset, + u8 *pdatatoread, u16 bytestoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + u8 __iomem *paddresstoread; + u16 i; + + paddresstoread = pruss->ioaddr + offset; + + for (i = 0; i < bytestoread; i++) + *pdatatoread++ = ioread8(paddresstoread++); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_readb_multi); + +s32 pruss_writel(struct device *dev, u32 offset, + u32 pdatatowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstowrite; + + paddresstowrite = pruss->ioaddr + offset; + iowrite32(pdatatowrite, paddresstowrite); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_writel); + +s32 pruss_writel_multi(struct device *dev, u32 offset, + u32 *pdatatowrite, u16 wordstowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + u32 __iomem *paddresstowrite; + u16 i; + + paddresstowrite = pruss->ioaddr + offset; + + for (i = 0; i < wordstowrite; i++) + iowrite32(*pdatatowrite++, paddresstowrite++); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_writel_multi); + +s32 pruss_rmwl(struct device *dev, u32 offset, u32 mask, u32 val) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddress; + u32 preg_data; + + paddress = pruss->ioaddr + offset; + + spin_lock(&pruss->lock); + preg_data = ioread32(paddress); + preg_data &= ~mask; + preg_data |= val; + iowrite32(preg_data, paddress); + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_rmwl); + +s32 pruss_readl(struct device *dev, u32 offset, u32 *pdatatoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstoread; + + paddresstoread = pruss->ioaddr + offset; + *pdatatoread = ioread32(paddresstoread); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_readl); + +s32 pruss_readl_multi(struct device *dev, u32 offset, + u32 *pdatatoread, u16 wordstoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + u32 __iomem *paddresstoread; + u16 i; + + paddresstoread = pruss->ioaddr + offset; + for (i = 0; i < wordstoread; i++) + *pdatatoread++ = ioread32(paddresstoread++); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_readl_multi); + +s32 pruss_writew(struct device *dev, u32 offset, u16 pdatatowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstowrite; + + paddresstowrite = pruss->ioaddr + offset; + iowrite16(pdatatowrite, paddresstowrite); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_writew); + +s32 pruss_rmww(struct device *dev, u32 offset, u16 mask, u16 val) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddress; + u32 preg_data; + + paddress = pruss->ioaddr + offset; + + spin_lock(&pruss->lock); + preg_data = ioread16(paddress); + preg_data &= ~mask; + preg_data |= val; + iowrite16(preg_data, paddress); + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_rmww); + +s32 pruss_readw(struct device *dev, u32 offset, u16 *pdatatoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstoread; + + paddresstoread = pruss->ioaddr + offset; + *pdatatoread = ioread16(paddresstoread); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_readw); + +s32 pruss_idx_writel(struct device *dev, u32 offset, u32 value) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *paddresstowrite; + + paddresstowrite = pruss->ioaddr + offset; + iowrite32(value, paddresstowrite); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_idx_writel); + +static int pruss_mfd_add_devices(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mfd_cell *cell = pdev->dev.platform_data; + s32 err, i, num_devices = 0; + + for (i = 0; cell[i].name; i++) { + err = mfd_add_devices(dev, 0, &cell[i], 1, NULL, 0); + if (err) { + dev_err(dev, "cannot add mfd cell: %s\n", + cell[i].name); + continue; + } + num_devices++; + dev_info(dev, "mfd: added %s device\n", cell[i].name); + } + + return num_devices; +} + +static int __devinit pruss_probe(struct platform_device *pdev) +{ + struct pruss_priv *pruss_dev = NULL; + s32 err; + + pruss_dev = kzalloc(sizeof(struct pruss_priv), 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); + + 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 pruss_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pruss_priv *pruss = dev_get_drvdata(dev); + + mfd_remove_devices(dev); + pruss_disable(dev, PRUCORE_0); + pruss_disable(dev, 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 pruss_driver = { + .probe = pruss_probe, + .remove = __devexit_p(pruss_remove), + .driver = { + .name = "pruss_mfd", + .owner = THIS_MODULE, + } +}; + +static int __init pruss_init(void) +{ + return platform_driver_register(&pruss_driver); +} +module_init(pruss_init); + +static void __exit pruss_exit(void) +{ + platform_driver_unregister(&pruss_driver); +} +module_exit(pruss_exit); + +MODULE_DESCRIPTION("Programmable Realtime Unit (PRU) Driver"); +MODULE_AUTHOR("Subhasish Ghosh"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/pruss.h b/include/linux/mfd/pruss.h new file mode 100644 index 0000000..8ef25b3 --- /dev/null +++ b/include/linux/mfd/pruss.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 +#include +#include "pruss_core.h" + +#define PRUSS_NUM0 PRUCORE_0 +#define PRUSS_NUM1 PRUCORE_1 + +#define PRUSS_PRU0_RAM_SZ 512 +#define PRUSS_PRU1_RAM_SZ 512 +#define PRUSS_PRU0_BASE_ADDRESS 0 +#define PRUSS_PRU1_BASE_ADDRESS 0x2000 +#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) + +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); + +s32 pruss_rmwb(struct device *dev, u32 offset, u8 mask, u8 val); + +s32 pruss_readb(struct device *dev, u32 offset, u8 *pdatatoread); + +s32 pruss_readb_multi(struct device *dev, u32 offset, + u8 *pdatatoread, u16 bytestoread); + +s32 pruss_readl(struct device *dev, u32 offset, u32 *pdatatoread); + +s32 pruss_readl_multi(struct device *dev, u32 offset, + u32 *pdatatoread, u16 wordstoread); + +s32 pruss_writel(struct device *dev, u32 offset, u32 pdatatowrite); + +s32 pruss_writel_multi(struct device *dev, u32 offset, + u32 *pdatatowrite, u16 wordstowrite); + +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); + +s32 pruss_rmww(struct device *dev, u32 offset, u16 mask, u16 val); + +s32 pruss_readw(struct device *dev, u32 offset, u16 *pdatatoread); + +#endif /* End _PRUSS_H_ */ diff --git a/include/linux/mfd/pruss_core.h b/include/linux/mfd/pruss_core.h new file mode 100644 index 0000000..48e2b99 --- /dev/null +++ b/include/linux/mfd/pruss_core.h @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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_CORE_H_ +#define _PRUSS_CORE_H_ + +#include + +#define PRUCORE_0 (0) +#define PRUCORE_1 (1) + +#define PRUCORE_CONTROL_PCRESETVAL_MASK (0xFFFF0000u) +#define PRUCORE_CONTROL_PCRESETVAL_SHIFT (0x00000010u) +#define PRUCORE_CONTROL_PCRESETVAL_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_RUNSTATE_MASK (0x00008000u) +#define PRUCORE_CONTROL_RUNSTATE_SHIFT (0x0000000Fu) +#define PRUCORE_CONTROL_RUNSTATE_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_RUNSTATE_HALT (0x00000000u) +#define PRUCORE_CONTROL_RUNSTATE_RUN (0x00000001u) +#define PRUCORE_CONTROL_SINGLESTEP_MASK (0x00000100u) +#define PRUCORE_CONTROL_SINGLESTEP_SHIFT (0x00000008u) +#define PRUCORE_CONTROL_SINGLESTEP_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_SINGLESTEP_FREERUN (0x00000000u) +#define PRUCORE_CONTROL_SINGLESTEP_SINGLE (0x00000001u) +#define PRUCORE_CONTROL_COUNTENABLE_MASK (0x00000008u) +#define PRUCORE_CONTROL_COUNTENABLE_SHIFT (0x00000003u) +#define PRUCORE_CONTROL_COUNTENABLE_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_COUNTENABLE_DISABLE (0x00000000u) +#define PRUCORE_CONTROL_COUNTENABLE_ENABLE (0x00000001u) +#define PRUCORE_CONTROL_SLEEPING_MASK (0x00000004u) +#define PRUCORE_CONTROL_SLEEPING_SHIFT (0x00000002u) +#define PRUCORE_CONTROL_SLEEPING_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_SLEEPING_NOTASLEEP (0x00000000u) +#define PRUCORE_CONTROL_SLEEPING_ASLEEP (0x00000001u) +#define PRUCORE_CONTROL_ENABLE_MASK (0x00000002u) +#define PRUCORE_CONTROL_ENABLE_SHIFT (0x00000001u) +#define PRUCORE_CONTROL_ENABLE_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_ENABLE_DISABLE (0x00000000u) +#define PRUCORE_CONTROL_ENABLE_ENABLE (0x00000001u) +#define PRUCORE_CONTROL_SOFTRESET_MASK (0x00000001u) +#define PRUCORE_CONTROL_SOFTRESET_SHIFT (0x00000000u) +#define PRUCORE_CONTROL_SOFTRESET_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_SOFTRESET_RESET (0x00000000u) +#define PRUCORE_CONTROL_SOFTRESET_OUT_OF_RESET (0x00000001u) +#define PRUCORE_CONTROL_RESETVAL (0x00000000u) + +struct 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 prusscore_regs core[2]; + u8 iram0[4096]; + u8 res3[12288]; + u8 iram1[4096]; + u8 res4[12288]; +}; +#endif -- 1.7.2.3 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From subhasish at mistralsolutions.com Fri Jul 8 13:25:25 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 18:25:25 -0000 Subject: [PATCH v2 13/13] tty: pruss SUART driver In-Reply-To: <1297435892-28278-1-git-send-email-subhasish@mistralsolutions.com> References: <1297435892-28278-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1297435892-28278-14-git-send-email-subhasish@mistralsolutions.com> This patch adds support for the TTY compliant Soft-UART device emulated on PRUSS. Signed-off-by: Subhasish Ghosh --- drivers/tty/serial/Kconfig | 2 + drivers/tty/serial/Makefile | 1 + drivers/tty/serial/da8xx_pruss/Kconfig | 19 + drivers/tty/serial/da8xx_pruss/Makefile | 9 + drivers/tty/serial/da8xx_pruss/pruss_suart.c | 1014 +++++++++ drivers/tty/serial/da8xx_pruss/pruss_suart_api.c | 2350 ++++++++++++++++++++ drivers/tty/serial/da8xx_pruss/pruss_suart_api.h | 345 +++ drivers/tty/serial/da8xx_pruss/pruss_suart_board.h | 58 + drivers/tty/serial/da8xx_pruss/pruss_suart_err.h | 48 + drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h | 526 +++++ drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h | 153 ++ drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c | 384 ++++ drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h | 63 + include/linux/serial_core.h | 2 + 14 files changed, 4974 insertions(+), 0 deletions(-) create mode 100644 drivers/tty/serial/da8xx_pruss/Kconfig create mode 100644 drivers/tty/serial/da8xx_pruss/Makefile create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart.c create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_api.c create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_api.h create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_board.h create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_err.h create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c create mode 100644 drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index b1682d7..68c40c2 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1595,4 +1595,6 @@ config SERIAL_PCH_UART This driver is for PCH(Platform controller Hub) UART of Intel EG20T which is an IOH(Input/Output Hub) for x86 embedded processor. Enabling PCH_DMA, this PCH UART works as DMA mode. + +source "drivers/tty/serial/da8xx_pruss/Kconfig" endmenu diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 8ea92e9..51c89e9 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -92,3 +92,4 @@ obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o +obj-$(CONFIG_SERIAL_PRUSS_SUART) += da8xx_pruss/ diff --git a/drivers/tty/serial/da8xx_pruss/Kconfig b/drivers/tty/serial/da8xx_pruss/Kconfig new file mode 100644 index 0000000..23ac53f --- /dev/null +++ b/drivers/tty/serial/da8xx_pruss/Kconfig @@ -0,0 +1,19 @@ +# +# SUART Kernel Configuration +# + +config SERIAL_PRUSS_SUART + depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850 + select SERIAL_CORE + tristate "PRUSS based SoftUART emulation on DA8XX" + ---help--- + Enable this to emulate a UART controller on the PRUSS of DA8XX. + If not sure, mark N + +config PRUSS_SUART_MCASP + int "McASP number" + depends on ARCH_DAVINCI && ARCH_DAVINCI_DA830 && SERIAL_PRUSS_SUART + default "0" + ---help--- + Enter the McASP number to use with SUART (0, 1 or 2). + You will need to recompile the kernel if this is changed. diff --git a/drivers/tty/serial/da8xx_pruss/Makefile b/drivers/tty/serial/da8xx_pruss/Makefile new file mode 100644 index 0000000..34a5b1f --- /dev/null +++ b/drivers/tty/serial/da8xx_pruss/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for SoftUART emulation +# + +suart_emu-objs := pruss_suart.o \ + pruss_suart_api.o \ + pruss_suart_utils.o + +obj-$(CONFIG_SERIAL_PRUSS_SUART) += suart_emu.o diff --git a/drivers/tty/serial/da8xx_pruss/pruss_suart.c b/drivers/tty/serial/da8xx_pruss/pruss_suart.c new file mode 100644 index 0000000..d222e2e --- /dev/null +++ b/drivers/tty/serial/da8xx_pruss/pruss_suart.c @@ -0,0 +1,1014 @@ +/* + * PRUSS SUART Emulation device driver + * Author: subhasish at mistralsolutions.com + * + * This driver supports TI's PRU SUART Emulation and the + * specs for the same is available at + * + * Copyright (C) 2009 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pruss_suart_board.h" +#include "pruss_suart_api.h" +#include "pruss_suart_utils.h" +#include "pruss_suart_err.h" + +#define NR_SUART 8 +#define DRV_NAME "da8xx_pruss_uart" +#define DRV_DESC "PRUSS SUART Driver v1.0" +#define MAX_SUART_RETRIES 100 +#define SUART_CNTX_SZ 512 +#define SUART_FIFO_TIMEOUT_DFLT 5 +#define SUART_FIFO_TIMEOUT_MIN 4 +#define SUART_FIFO_TIMEOUT_MAX 500 + +#ifdef __SUART_DEBUG +#define __suart_debug(fmt, args...) \ + printk(KERN_DEBUG "suart_debug: " fmt, ## args) +#else +#define __suart_debug(fmt, args...) +#endif + +#define __suart_err(fmt, args...) printk(KERN_ERR "suart_err: " fmt, ## args) + +/* Default timeout set to 5ms */ +static s16 suart_timeout = SUART_FIFO_TIMEOUT_DFLT; +module_param(suart_timeout, short, S_IRUGO); +MODULE_PARM_DESC(suart_timeout, + "fifo timeout in milli seconds (min: 4; max: 500)"); + +struct suart_fifo { + void *fifo_vaddr_buff_tx; + void *fifo_vaddr_buff_rx; + void *fifo_phys_addr_tx; + void *fifo_phys_addr_rx; +}; + +struct omapl_pru_suart { + struct uart_port port[NR_SUART]; + struct device *dev; /* pdev->dev */ + struct semaphore port_sem[NR_SUART]; + struct clk *clk_mcasp; + struct suart_fifo suart_fifo_addr[NR_SUART]; + const struct firmware *fw; + suart_struct_handle suart_hdl[NR_SUART]; + pruss_suart_iomap suart_iomap; + u32 clk_freq_pru; + u32 clk_freq_mcasp; + u32 tx_loadsz; +}; + +static u32 suart_get_duplex(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + return soft_uart->suart_hdl[uart_no].uart_type; +} + +static inline void __stop_tx(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + struct device *dev = soft_uart->dev; + u16 txready; + u32 i; + + /* Check if any TX in progress */ + for (i = 0, txready = 1; (i < 10000) && txready; i++) { + txready = (pru_softuart_get_tx_status + (dev, &soft_uart->suart_hdl[uart_no]) & + CHN_TXRX_STATUS_RDY); + } + /* To stop tx, disable the TX interrupt */ + suart_intr_clrmask(dev, soft_uart->suart_hdl[uart_no].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + pru_softuart_clr_tx_status(dev, &soft_uart->suart_hdl[uart_no]); +} + +static void pruss_suart_stop_tx(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + __stop_tx(soft_uart, port->line); +} + +static void omapl_pru_tx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + struct circ_buf *xmit = &soft_uart->port[uart_no].state->xmit; + struct device *dev = soft_uart->dev; + s32 count = 0; + + if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_TX)) + return; + + if (down_trylock(&soft_uart->port_sem[uart_no])) + return; + + if (uart_circ_empty(xmit) || + uart_tx_stopped(&soft_uart->port[uart_no])) { + pruss_suart_stop_tx(&soft_uart->port[uart_no]); + up(&soft_uart->port_sem[uart_no]); + return; + } + + for (count = 0; count <= soft_uart->tx_loadsz; count++) { + *((s8 *)soft_uart->suart_fifo_addr[uart_no].fifo_vaddr_buff_tx + + count) = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + soft_uart->port[uart_no].icount.tx++; + if (uart_circ_empty(xmit)) { + uart_circ_clear(xmit); + break; + } + } + + if (count == (SUART_FIFO_LEN + 1)) + count = SUART_FIFO_LEN; + + /* Write the character to the data port */ + if (SUART_SUCCESS != pru_softuart_write(dev, + &soft_uart->suart_hdl[uart_no], + (u32 *)&soft_uart->suart_fifo_addr + [uart_no].fifo_phys_addr_tx, count)) { + __suart_err("failed to tx data\n"); + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&soft_uart->port[uart_no]); + +#if 0 + if (uart_circ_empty(xmit)) + __stop_tx(soft_uart, uart_no); +#endif +} + +static void omapl_pru_rx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + struct tty_struct *tty = soft_uart->port[uart_no].state->port.tty; + struct device *dev = soft_uart->dev; + s8 flags = TTY_NORMAL; + u16 rx_status, data_len = SUART_FIFO_LEN; + u32 data_len_read; + u8 suart_data[SUART_FIFO_LEN + 1]; + s32 i = 0; + + if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_RX)) + return; + /* read the status */ + rx_status = pru_softuart_get_rx_status(dev, + &soft_uart->suart_hdl[uart_no]); + + /* check for errors */ + if (rx_status & CHN_TXRX_STATUS_ERR) { + if (rx_status & CHN_TXRX_STATUS_FE) + soft_uart->port[uart_no].icount.frame++; + if (rx_status & CHN_TXRX_STATUS_OVRNERR) + soft_uart->port[uart_no].icount.overrun++; + if (rx_status & CHN_TXRX_STATUS_BI) + soft_uart->port[uart_no].icount.brk++; + rx_status &= soft_uart->port[uart_no].read_status_mask; + if (rx_status & CHN_TXRX_STATUS_FE) + flags = TTY_FRAME; + if (rx_status & CHN_TXRX_STATUS_OVRNERR) + flags = TTY_OVERRUN; + if (rx_status & CHN_TXRX_STATUS_BI) + flags = TTY_BREAK; + +#ifdef SUPPORT_SYSRQ + soft_uart->port[uart_no].sysrq = 0; +#endif + } else { + pru_softuart_read_data(dev, &soft_uart->suart_hdl[uart_no], + suart_data, data_len + 1, + &data_len_read); + + for (i = 0; i <= data_len_read; i++) { + soft_uart->port[uart_no].icount.rx++; + /* check for sys rq */ + if (uart_handle_sysrq_char + (&soft_uart->port[uart_no], suart_data)) + continue; + } + /* update the tty data structure */ + tty_insert_flip_string(tty, suart_data, data_len_read); + } + + /* push data into tty */ + pru_softuart_clr_rx_status(dev, &soft_uart->suart_hdl[uart_no]); + spin_unlock(&soft_uart->port[uart_no].lock); + tty_flip_buffer_push(tty); + spin_lock(&soft_uart->port[uart_no].lock); +} + +static irqreturn_t pruss_suart_interrupt(s32 irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + u16 txrx_flag; + u32 ret; + unsigned long flags = 0; + u16 uart_num = port->line + 1; + + spin_lock_irqsave(&soft_uart->port[port->line].lock, flags); + do { + ret = pru_softuart_get_isrstatus(dev, uart_num, &txrx_flag); + if (PRU_SUART_SUCCESS != ret) { + __suart_err("suart%d: failed to get interrupt, ret:" + " 0x%X txrx_flag 0x%X\n", + port->line, ret, txrx_flag); + spin_unlock_irqrestore(&soft_uart-> + port[port->line].lock, flags); + return IRQ_NONE; + } + if ((PRU_RX_INTR & txrx_flag) == PRU_RX_INTR) { + pru_intr_clr_isrstatus(dev, uart_num, PRU_RX_INTR); + if ((soft_uart->port[port->line].ignore_status_mask & + CHN_TXRX_STATUS_RDY) == CHN_TXRX_STATUS_RDY) { + pru_softuart_clr_rx_status(dev, + &soft_uart->suart_hdl + [port->line]); + } else { + omapl_pru_rx_chars(soft_uart, port->line); + } + } + + if ((PRU_TX_INTR & txrx_flag) == PRU_TX_INTR) { + pru_intr_clr_isrstatus(dev, uart_num, PRU_TX_INTR); + pru_softuart_clr_tx_status(dev, &soft_uart->suart_hdl + [port->line]); + up(&soft_uart->port_sem[port->line]); + omapl_pru_tx_chars(soft_uart, port->line); + } + } while (txrx_flag & (PRU_RX_INTR | PRU_TX_INTR)); + + spin_unlock_irqrestore(&soft_uart->port[port->line].lock, flags); + return IRQ_HANDLED; +} + +static void pruss_suart_stop_rx(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + /* disable rx interrupt */ + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); +} + +static void pruss_suart_enable_ms(struct uart_port *port) +{ + __suart_err("modem control timer not supported\n"); +} + +static void pruss_suart_start_tx(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + /* unmask the tx interrupts */ + + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + omapl_pru_tx_chars(soft_uart, port->line); +} + +static u32 pruss_suart_tx_empty(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + + return (pru_softuart_get_tx_status(dev, + &soft_uart->suart_hdl[port->line]) + & CHN_TXRX_STATUS_RDY) ? 0 : TIOCSER_TEMT; +} + +static u32 pruss_suart_get_mctrl(struct uart_port *port) +{ + return -ENOTSUPP; +} + +static void pruss_suart_set_mctrl(struct uart_port *port, u32 mctrl) +{ + __suart_debug("modem control not supported\n"); +} + +static void pruss_suart_break_ctl(struct uart_port *port, s32 break_state) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + spin_lock_irqsave(&port->lock, flags); + + if (break_state == -1) + suart_intr_clrmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + else + suart_intr_setmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void pruss_suart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + u8 cval = 0; + unsigned long flags = 0; + u32 baud = 0; + u32 old_csize = old ? old->c_cflag & CSIZE : CS8; + +/* + * Do not allow unsupported configurations to be set + */ + if (1) { + termios->c_cflag &= ~(HUPCL | CRTSCTS | CMSPAR | CSTOPB + | PARENB | PARODD | CMSPAR); + termios->c_cflag |= CLOCAL; + } + + switch (termios->c_cflag & CSIZE) { + case CS6: + cval = ePRU_SUART_DATA_BITS6; + break; + case CS7: + cval = ePRU_SUART_DATA_BITS7; + break; + default: + case CS8: + cval = ePRU_SUART_DATA_BITS8; + break; + } + /* + * We do not support CS5. + */ + if ((termios->c_cflag & CSIZE) == CS5) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + } + if (SUART_SUCCESS != pru_softuart_setdatabits + (dev, &soft_uart->suart_hdl[port->line], cval, cval)) + __suart_err("failed to set data bits to: %d\n", cval); + +/* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 16); + +/* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&port->lock, flags); + + /* Set the baud */ + if (SUART_SUCCESS != + pru_softuart_setbaud(dev, &soft_uart->suart_hdl[port->line], + SUART_DEFAULT_BAUD / baud, + SUART_DEFAULT_BAUD / baud)) + __suart_err("failed to set baud to: %d\n", baud); + +/* + * update port->read_config_mask and port->ignore_config_mask + * to indicate the events we are interested in receiving + */ + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, SUART_GBL_INTR_ERR_MASK); + port->read_status_mask = 0; + if (termios->c_iflag & INPCK) { /* Input parity check not supported, + just enabled FE */ + port->read_status_mask |= CHN_TXRX_STATUS_FE; + suart_intr_setmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_FE); + } + if (termios->c_iflag & (BRKINT | PARMRK)) { + port->read_status_mask |= CHN_TXRX_STATUS_BI; + suart_intr_setmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + } +/* + * Characteres to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= CHN_TXRX_STATUS_BI; + /* + * If we're ignoring break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) { + port->ignore_status_mask |= + (CHN_TXRX_STATUS_OVRNERR | CHN_TXRX_STATUS_FE); + /* + * Overrun in case of RX + * Underrun in case of TX + */ + suart_intr_clrmask(dev, soft_uart-> + suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_FE); + } + suart_intr_clrmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + } +/* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) { + port->ignore_status_mask |= CHN_TXRX_STATUS_RDY; + pruss_suart_stop_rx(port); + } + + spin_unlock_irqrestore(&port->lock, flags); + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +/* + * Grab any interrupt resources and initialise any low level driver + * state. Enable the port for reception. It should not activate + * RTS nor DTR; this will be done via a separate call to set_mctrl. + * + * This method will only be called when the port is initially opened. + * + * Locking: port_sem taken. + * Interrupts: globally disabled. + */ +static s32 pruss_suart_startup(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + s32 retval; + + /* + * Disable interrupts from this port + */ + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + + retval = request_irq(port->irq, pruss_suart_interrupt, + port->irqflags, "suart_irq", port); + if (retval) { + free_irq(port->irq, port); /* should we free this if err */ + goto out; + } + /* + * enable interrupts from this port + */ + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, SUART_GBL_INTR_ERR_MASK); + + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + + if ((suart_get_duplex(soft_uart, port->line) & ePRU_SUART_HALF_TX) + == ePRU_SUART_HALF_TX) { + suart_pru_to_host_intr_enable(dev, soft_uart-> + suart_hdl[port->line].uart_num, PRU_TX_INTR, true); + } + /* Seed RX if port is half-rx or full-duplex */ + if ((suart_get_duplex(soft_uart, port->line) & ePRU_SUART_HALF_RX) + == ePRU_SUART_HALF_RX) { + suart_pru_to_host_intr_enable(dev, soft_uart-> + suart_hdl[port->line].uart_num, PRU_RX_INTR, true); + pru_softuart_read(dev, &soft_uart->suart_hdl[port->line], + (u32 *)&soft_uart->suart_fifo_addr[port->line]. + fifo_phys_addr_rx, SUART_FIFO_LEN); + } +out: + return retval; +} + +/* + * Disable the port, disable any break condition that may be in + * effect, and free any interrupt resources. It should not disable + * RTS nor DTR; this will have already been done via a separate + * call to set_mctrl. + * + * Drivers must not access port->info once this call has completed. + * + * This method will only be called when there are no more users of + * this port. + * + * Locking: port_sem taken. + * Interrupts: caller dependent. + */ + +static void pruss_suart_shutdown(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + + /* + * Disable interrupts from this port + */ + /* Disable BI and FE intr */ + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + + /* free interrupts */ + free_irq(port->irq, port); +} + +/* + * Return a pointer to a string constant describing the specified + * port, or return NULL, in which case the string 'unknown' is + * substituted. + * + * Locking: none. + * Interrupts: caller dependent. + */ + +static const char *pruss_suart_type(struct uart_port *port) +{ + return "suart_tty"; +} + +/* + * Release any memory and IO region resources currently in use by + * the port. + * + * Locking: none. + * Interrupts: caller dependent. + */ + +static void pruss_suart_release_port(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct platform_device *pdev = to_platform_device(port->dev); + + if (0 != pru_softuart_close(&soft_uart->suart_hdl[port->line])) + dev_err(&pdev->dev, "failed to close suart\n"); + + return; +} + +/* + * Request any memory and IO region resources required by the port. + * If any fail, no resources should be registered when this function + * returns, and it should return -EBUSY on failure. + * + * Locking: none. + * Interrupts: caller dependent. + * + * We need to d/l the f/w in probe and since this api + * is called per uart, the request_mem_region should + * be called in probe itself. + */ +static s32 pruss_suart_request_port(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct platform_device *pdev = to_platform_device(port->dev); + struct device *dev = soft_uart->dev; + suart_config pru_suart_config; + s16 timeout = 0; + u32 err = 0; + + if (soft_uart == NULL) { + __suart_err("soft_uart ptr failed\n"); + return -ENODEV; + } + err = pru_softuart_open(&soft_uart->suart_hdl[port->line]); + if (PRU_SUART_SUCCESS != err) { + dev_err(&pdev->dev, "failed to open suart: %d\n", err); + err = -ENODEV; + goto exit; + } + + /* set fifo /timeout */ + if (SUART_FIFO_TIMEOUT_MIN > suart_timeout) { + __suart_err("fifo timeout less than %d ms not supported\n", + SUART_FIFO_TIMEOUT_MIN); + suart_timeout = SUART_FIFO_TIMEOUT_MIN; + } else if (SUART_FIFO_TIMEOUT_MAX < suart_timeout) { + __suart_err("fifo timeout more than %d ms not supported\n", + SUART_FIFO_TIMEOUT_MAX); + suart_timeout = SUART_FIFO_TIMEOUT_MAX; + } + + /* This is only for x8 */ + timeout = (SUART_DEFAULT_BAUD * suart_timeout) / 1000; + pru_set_fifo_timeout(dev, timeout); + + if (soft_uart->suart_hdl[port->line].uart_num == PRU_SUART_UART1) { + pru_suart_config.tx_serializer = PRU_SUART0_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART0_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART2) { + pru_suart_config.tx_serializer = PRU_SUART1_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART1_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART3) { + pru_suart_config.tx_serializer = PRU_SUART2_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART2_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART4) { + pru_suart_config.tx_serializer = PRU_SUART3_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART3_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART5) { + pru_suart_config.tx_serializer = PRU_SUART4_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART4_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART6) { + pru_suart_config.tx_serializer = PRU_SUART5_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART5_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART7) { + pru_suart_config.tx_serializer = PRU_SUART6_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART6_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART8) { + pru_suart_config.tx_serializer = PRU_SUART7_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART7_CONFIG_RX_SER; + } else { + return -ENOTSUPP; + } + + /* Some defaults to startup. reconfigured by terimos later */ + pru_suart_config.tx_clk_divisor = 1; + pru_suart_config.rx_clk_divisor = 1; + pru_suart_config.tx_bits_per_char = ePRU_SUART_DATA_BITS8; + pru_suart_config.rx_bits_per_char = ePRU_SUART_DATA_BITS8; + pru_suart_config.oversampling = SUART_DEFAULT_OVRSMPL; + + if (PRU_SUART_SUCCESS != + pru_softuart_setconfig(dev, &soft_uart->suart_hdl[port->line], + &pru_suart_config)) { + dev_err(&pdev->dev, + "pru_softuart_setconfig: failed to set config: %X\n", + err); + } +exit: + return err; +} + +/* + * Perform any autoconfiguration steps required for the port. `flag` + * contains a bit mask of the required configuration. UART_CONFIG_TYPE + * indicates that the port requires detection and identification. + * port->type should be set to the type found, or PORT_UNKNOWN if + * no port was detected. + * + * UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal, + * which should be probed using standard kernel autoprobing techniques. + * This is not necessary on platforms where ports have interrupts + * internally hard wired (eg, system on a chip implementations). + * + * Locking: none. + * Interrupts: caller dependent. + */ + +static void pruss_suart_config_port(struct uart_port *port, s32 flags) +{ + if (flags & UART_CONFIG_TYPE && pruss_suart_request_port(port) == 0) + port->type = PORT_DA8XX_PRU_SUART; +} + +/* + * Verify the new serial port information contained within serinfo is + * suitable for this port type. + * + * Locking: none. + * Interrupts: caller dependent. + */ +static s32 pruss_suart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + s32 ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_DA8XX_PRU_SUART) + ret = -EINVAL; + if (soft_uart->port[port->line].irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != UPIO_MEM) + ret = -EINVAL; + if (soft_uart->port[port->line].uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)soft_uart->port[port->line].mapbase != ser->iomem_base) + ret = -EINVAL; + if (soft_uart->port[port->line].iobase != ser->port) + ret = -EINVAL; + return ret; +} + +static struct uart_ops pruss_suart_ops = { + .tx_empty = pruss_suart_tx_empty, + .set_mctrl = pruss_suart_set_mctrl, + .get_mctrl = pruss_suart_get_mctrl, + .stop_tx = pruss_suart_stop_tx, + .start_tx = pruss_suart_start_tx, + .stop_rx = pruss_suart_stop_rx, + .enable_ms = pruss_suart_enable_ms, + .break_ctl = pruss_suart_break_ctl, + .startup = pruss_suart_startup, + .shutdown = pruss_suart_shutdown, + .set_termios = pruss_suart_set_termios, + .type = pruss_suart_type, + .release_port = pruss_suart_release_port, + .request_port = pruss_suart_request_port, + .config_port = pruss_suart_config_port, + .verify_port = pruss_suart_verify_port, +}; + +static struct uart_driver pruss_suart_reg = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttySU", + .major = 0, + .minor = 16, + .nr = NR_SUART, +}; + +static s32 __devinit pruss_suart_probe(struct platform_device *pdev) +{ + struct omapl_pru_suart *soft_uart; + const struct da850_evm_pruss_suart_data *pdata; + struct device *dev = &pdev->dev; + s32 err, i; + u8 *fw_data = NULL; + + pdata = dev->platform_data; + if (!pdata) { + dev_err(&pdev->dev, "platform data not found\n"); + return -EINVAL; + } + + soft_uart = kzalloc(sizeof(struct omapl_pru_suart), GFP_KERNEL); + if (!soft_uart) + return -ENOMEM; + + if (!request_mem_region(pdata->resource.start, + resource_size(&pdata->resource), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "mcasp memory region already claimed!\n"); + err = -EBUSY; + goto probe_exit; + } + + soft_uart->suart_iomap.mcasp_io_addr = + ioremap(pdata->resource.start, + resource_size(&pdata->resource)); + if (!soft_uart->suart_iomap.mcasp_io_addr) { + dev_err(&pdev->dev, "mcasp ioremap failed\n"); + err = -EFAULT; + goto probe_exit_1; + } + + soft_uart->suart_iomap.p_fifo_buff_virt_base = + sram_alloc(SUART_CNTX_SZ * NR_SUART * 2, + (dma_addr_t *) &soft_uart->suart_iomap.p_fifo_buff_phys_base); + if (!soft_uart->suart_iomap.p_fifo_buff_virt_base) + goto probe_exit_iounmap; + + soft_uart->clk_freq_pru = pruss_get_clk_freq(dev); + + soft_uart->clk_mcasp = clk_get(&pdev->dev, NULL); + if (IS_ERR(soft_uart->clk_mcasp)) { + dev_err(&pdev->dev, "no clock available: mcasp\n"); + err = -ENODEV; + soft_uart->clk_mcasp = NULL; + goto probe_exit_sram_free; + } + + soft_uart->clk_freq_mcasp = clk_get_rate(soft_uart->clk_mcasp); + clk_enable(soft_uart->clk_mcasp); + + err = request_firmware(&soft_uart->fw, "PRU_SUART_Emulation.bin", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "can't load firmware\n"); + err = -ENODEV; + goto probe_exit_clk; + } + dev_info(&pdev->dev, "fw size %td. downloading...\n", + soft_uart->fw->size); + + /* download firmware into pru & init */ + fw_data = kmalloc(soft_uart->fw->size, GFP_KERNEL); + memcpy((void *)fw_data, (const void *)soft_uart->fw->data, + soft_uart->fw->size); + + soft_uart->suart_iomap.pru_clk_freq = + (soft_uart->clk_freq_pru / 1000000); + + err = pru_softuart_init(dev, SUART_DEFAULT_BAUD, SUART_DEFAULT_BAUD, + SUART_DEFAULT_OVRSMPL, fw_data, + soft_uart->fw->size, &soft_uart->suart_iomap); + if (err) { + dev_err(&pdev->dev, "pruss init error\n"); + err = -ENODEV; + kfree((const void *)fw_data); + goto probe_release_fw; + } + kfree((const void *)fw_data); + + platform_set_drvdata(pdev, &soft_uart->port[0]); + soft_uart->dev = dev; + + for (i = 0; i < NR_SUART; i++) { + soft_uart->port[i].ops = &pruss_suart_ops; + soft_uart->port[i].iotype = UPIO_MEM; + soft_uart->port[i].flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; + soft_uart->port[i].mapbase = + (u32)soft_uart->suart_iomap.p_fifo_buff_virt_base; + soft_uart->port[i].membase = + (u8 *)&soft_uart->suart_iomap; + soft_uart->port[i].type = PORT_DA8XX_PRU_SUART; + soft_uart->port[i].irq = + platform_get_irq(to_platform_device(dev->parent), i); + soft_uart->port[i].dev = &pdev->dev; + soft_uart->port[i].irqflags = IRQF_SHARED; + soft_uart->port[i].uartclk = soft_uart->clk_freq_mcasp; + soft_uart->port[i].fifosize = SUART_FIFO_LEN; + soft_uart->tx_loadsz = SUART_FIFO_LEN; + soft_uart->port[i].custom_divisor = 1; + soft_uart->port[i].line = i; + soft_uart->suart_hdl[i].uart_num = i + 1; + spin_lock_init(&soft_uart->port[i].lock); + soft_uart->port[i].serial_in = NULL; + + soft_uart->suart_fifo_addr[i].fifo_vaddr_buff_tx = + soft_uart->suart_iomap.p_fifo_buff_virt_base + + (2 * SUART_CNTX_SZ * i); + + soft_uart->suart_fifo_addr[i].fifo_vaddr_buff_rx = + soft_uart->suart_iomap.p_fifo_buff_virt_base + + ((2 * SUART_CNTX_SZ * i) + SUART_CNTX_SZ); + + soft_uart->suart_fifo_addr[i].fifo_phys_addr_tx = + soft_uart->suart_iomap.p_fifo_buff_phys_base + + (2 * SUART_CNTX_SZ * i); + + soft_uart->suart_fifo_addr[i].fifo_phys_addr_rx = + soft_uart->suart_iomap.p_fifo_buff_phys_base + + ((2 * SUART_CNTX_SZ * i) + SUART_CNTX_SZ); + + soft_uart->port[i].serial_out = NULL; + uart_add_one_port(&pruss_suart_reg, &soft_uart->port[i]); + sema_init(&soft_uart->port_sem[i], 1); + } + + dev_info(&pdev->dev, + "%s device registered (pru_clk=%d, asp_clk=%d)\n", + DRV_NAME, soft_uart->clk_freq_pru, soft_uart->clk_freq_mcasp); + + return 0; + +probe_release_fw: + release_firmware(soft_uart->fw); +probe_exit_clk: + clk_put(soft_uart->clk_mcasp); + clk_disable(soft_uart->clk_mcasp); +probe_exit_sram_free: + sram_free(soft_uart->suart_iomap.p_fifo_buff_virt_base, + SUART_CNTX_SZ * NR_SUART * 2); +probe_exit_iounmap: + iounmap(soft_uart->suart_iomap.mcasp_io_addr); +probe_exit_1: + release_mem_region(pdata->resource.start, + resource_size(&pdata->resource)); +probe_exit: + kfree(soft_uart); + return err; +} + +static s32 __devexit pruss_suart_remove(struct platform_device *pdev) +{ + struct omapl_pru_suart *soft_uart = platform_get_drvdata(pdev); + const struct da850_evm_pruss_suart_data *pdata; + struct device *dev = &pdev->dev; + int i; + + pdata = dev->platform_data; + if (!pdata) + dev_err(&pdev->dev, "platform data not found\n"); + + platform_set_drvdata(pdev, NULL); + + if (soft_uart) { + for (i = 0; i < NR_SUART; i++) { + uart_remove_one_port(&pruss_suart_reg, + &soft_uart->port[i]); + } + } + + sram_free(soft_uart->suart_iomap.p_fifo_buff_virt_base, + SUART_CNTX_SZ * NR_SUART * 2); + release_firmware(soft_uart->fw); + clk_put(soft_uart->clk_mcasp); + clk_disable(soft_uart->clk_mcasp); + iounmap(soft_uart->suart_iomap.mcasp_io_addr); + if (pdata) { + release_mem_region(pdata->resource.start, + resource_size(&pdata->resource)); + } + kfree(soft_uart); + return 0; +} + +#define pruss_suart_suspend NULL +#define pruss_suart_resume NULL + +static struct platform_driver serial_pruss_driver = { + .probe = pruss_suart_probe, + .remove = __devexit_p(pruss_suart_remove), + .suspend = pruss_suart_suspend, + .resume = pruss_suart_resume, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static s32 __init pruss_suart_init(void) +{ + s32 ret; + + pruss_suart_reg.nr = NR_SUART; + ret = uart_register_driver(&pruss_suart_reg); + if (ret) + return ret; + ret = platform_driver_register(&serial_pruss_driver); + if (ret) + goto out; + + __suart_debug("SUART serial driver loaded\n"); + return ret; +out: + uart_unregister_driver(&pruss_suart_reg); + return ret; +} + +module_init(pruss_suart_init); + +static void __exit pruss_suart_exit(void) +{ + platform_driver_unregister(&serial_pruss_driver); + uart_unregister_driver(&pruss_suart_reg); + __suart_debug("SUART serial driver unloaded\n"); +} + +module_exit(pruss_suart_exit); + +/* Module information */ +MODULE_AUTHOR("Subhasish Ghosh "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRV_DESC); diff --git a/drivers/tty/serial/da8xx_pruss/pruss_suart_api.c b/drivers/tty/serial/da8xx_pruss/pruss_suart_api.c new file mode 100644 index 0000000..d809dd3 --- /dev/null +++ b/drivers/tty/serial/da8xx_pruss/pruss_suart_api.c @@ -0,0 +1,2350 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 +#include +#include +#include +#include "pruss_suart_api.h" +#include "pruss_suart_regs.h" +#include "pruss_suart_board.h" +#include "pruss_suart_utils.h" +#include "pruss_suart_err.h" + +static u8 g_uart_statu_table[8]; +static pruss_suart_iomap suart_iomap; + +static u32 uart_rx[8] = {PRU_SUART0_CONFIG_RX_SER, PRU_SUART1_CONFIG_RX_SER, + PRU_SUART2_CONFIG_RX_SER, PRU_SUART3_CONFIG_RX_SER, + PRU_SUART4_CONFIG_RX_SER, PRU_SUART5_CONFIG_RX_SER, + PRU_SUART6_CONFIG_RX_SER, PRU_SUART7_CONFIG_RX_SER}; + +static u32 uart_tx[8] = {PRU_SUART0_CONFIG_TX_SER, PRU_SUART1_CONFIG_TX_SER, + PRU_SUART2_CONFIG_TX_SER, PRU_SUART3_CONFIG_TX_SER, + PRU_SUART4_CONFIG_TX_SER, PRU_SUART5_CONFIG_TX_SER, + PRU_SUART6_CONFIG_TX_SER, PRU_SUART7_CONFIG_TX_SER}; + +static u32 uart_config[8] = {PRU_SUART0_CONFIG_DUPLEX, PRU_SUART1_CONFIG_DUPLEX, + PRU_SUART2_CONFIG_DUPLEX, PRU_SUART3_CONFIG_DUPLEX, + PRU_SUART4_CONFIG_DUPLEX, PRU_SUART5_CONFIG_DUPLEX, + PRU_SUART6_CONFIG_DUPLEX, PRU_SUART7_CONFIG_DUPLEX}; + +#if (PRU_ACTIVE == BOTH_PRU) +#if 1 +void pru_set_ram_data(struct device *dev, pruss_suart_iomap *pruss_ioaddr) +{ + u16 u16datatowrite; + u32 u32datatowrite; + u32 i; + pru_suart_regs_ovly pru_suart_regs = PRU0_DATARAM_OFFSET; + u32 *pu32_sr_ctl_addr = (u32 *)(pruss_ioaddr->mcasp_io_addr + 0x180); + pru_suart_tx_cntx_priv *pru_suart_tx_priv = NULL; + pru_suart_rx_cntx_priv *pru_suart_rx_priv = NULL; + + /* RX PRU - 0 Chanel 0-7 context information */ + for (i = 0; i < 8; i++, pru_suart_regs++) { + pruss_readw(dev, (u32) &pru_suart_regs->ch_ctrl, &u16datatowrite, 1); + u16datatowrite |= (SUART_CHN_RX << 0); + pruss_writew(dev, (u32) &pru_suart_regs->ch_ctrl, + &u16datatowrite, 1); + + pruss_readw(dev, (u32) &pru_suart_regs->ch_ctrl, &u16datatowrite, 1); + u16datatowrite |= ((0xF & uart_rx[i]) << 8); + pruss_writew(dev, (u32) &pru_suart_regs->ch_ctrl, + &u16datatowrite, 1); + + pruss_readw(dev, (u32) &pru_suart_regs->ch_config1, + &u16datatowrite, 1); + u16datatowrite |= (SUART_DEFAULT_OVRSMPL << 10); + pruss_writew(dev, (u32) &pru_suart_regs->ch_config1, + &u16datatowrite, 1); + + pruss_readw(dev, (u32) &pru_suart_regs->ch_config2, + &u16datatowrite, 1); + u16datatowrite |= 8; + pruss_writew(dev, (u32) &pru_suart_regs->ch_config2, + &u16datatowrite, 1); + + if ((uart_config[i] & PRU_SUART_HALF_RX_DISABLED) == + PRU_SUART_HALF_RX_DISABLED) { + pruss_readw(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + u16datatowrite |= (SUART_CHN_DISABLED << 15) ; + pruss_writew(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + } else { + pruss_readw(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + u16datatowrite |= (SUART_CHN_ENABLED << 15); + pruss_writew(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + __raw_writel(MCASP_SRCTL_RX_MODE, pu32_sr_ctl_addr + + uart_rx[i]); + } + /* + * RX is active by default, write the dummy received data at + * PRU RAM addr 0x1FC to avoid memory corruption. + */ + pruss_readl(dev, (u32) &pru_suart_regs->ch_txrx_data, + &u32datatowrite, 1); + u32datatowrite |= RX_DEFAULT_DATA_DUMP_ADDR; + pruss_writel(dev, (u32) &pru_suart_regs->ch_txrx_data, + &u32datatowrite, 1); + pruss_readl(dev, (u32) &pru_suart_regs->reserved1, &u32datatowrite, 1); + u32datatowrite = 0; + pruss_writel(dev, (u32) &pru_suart_regs->reserved1, + &u32datatowrite, 1); + /* SUART1 RX context base addr */ + pru_suart_rx_priv = (pru_suart_rx_cntx_priv *) + (PRU0_DATARAM_OFFSET + (0x090 + (i * 0x020))); + u32datatowrite = (MCASP_RBUF_BASE_ADDR + (uart_rx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rbuf_base, + &u32datatowrite, 1); + u32datatowrite = (MCASP_SRCTL_BASE_ADDR + (uart_rx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rsrctl_base, + &u32datatowrite, 1); + } + + /* ****************** PRU1 RAM BASE ADDR ************************ */ + pru_suart_regs = (pru_suart_regs_ovly) PRU1_DATARAM_OFFSET; + + /* ******************* TX PRU - 1 *********************** */ + /* Channel 0-7 context information */ + for (i = 0; i < 8; i++, pru_suart_regs++) { + pruss_readw(dev, (u32) &pru_suart_regs->ch_ctrl, &u16datatowrite, 1); + u16datatowrite |= (SUART_CHN_TX << 0); + pruss_writew(dev, (u32) &pru_suart_regs->ch_ctrl, + &u16datatowrite, 1); + pruss_readw(dev, (u32) &pru_suart_regs->ch_ctrl, &u16datatowrite, 1); + u16datatowrite |= (0xF & uart_tx[i] << 8); + pruss_writew(dev, (u32) &pru_suart_regs->ch_ctrl, + &u16datatowrite, 1); + pruss_readw(dev, (u32) &pru_suart_regs->ch_config1, + &u16datatowrite, 1); + u16datatowrite |= (SUART_DEFAULT_OVRSMPL << 10); + pruss_writew(dev, (u32) &pru_suart_regs->ch_config1, + &u16datatowrite, 1); + pruss_readw(dev, (u32) &pru_suart_regs->ch_config2, + &u16datatowrite, 1); + u16datatowrite |= 8; + pruss_writew(dev, (u32) &pru_suart_regs->ch_config2, + &u16datatowrite, 1); + if ((uart_config[i] & PRU_SUART_HALF_TX_DISABLED) == + PRU_SUART_HALF_TX_DISABLED) { + pruss_readw(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + u16datatowrite |= (SUART_CHN_DISABLED << 15); + pruss_writew(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + } else { + pruss_readw(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + u16datatowrite |= (SUART_CHN_ENABLED << 15); + pruss_writew(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + __raw_writel(MCASP_SRCTL_TX_MODE, + pu32_sr_ctl_addr + uart_tx[i]); + } + pruss_readl(dev, (u32) &pru_suart_regs->reserved1, &u32datatowrite, 1); + u32datatowrite |= 1; + pruss_writel(dev, (u32) &pru_suart_regs->reserved1, + &u32datatowrite, 1); + /* SUART1 TX context base addr */ + pru_suart_tx_priv = (pru_suart_tx_cntx_priv *) + (PRU1_DATARAM_OFFSET + (0x0B0 + (i * 0x02C))); + u32datatowrite = (MCASP_SRCTL_BASE_ADDR + (uart_tx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xsrctl_base, + &u32datatowrite, 1); + u32datatowrite = (MCASP_XBUF_BASE_ADDR + (uart_tx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xbuf_base, + &u32datatowrite, 1); + /* SUART1 TX formatted data base addr */ + u32datatowrite = (0x0090 + (i * 0x002C)); + pruss_writel(dev, (u32) &pru_suart_tx_priv->buff_addr, + &u32datatowrite, 1); + } +} +#endif +#else +void pru_set_ram_data(struct device *dev, pruss_suart_iomap *pruss_ioaddr) +{ + + pru_suart_regs_ovly pru_suart_regs = (pru_suart_regs_ovly) + pruss_ioaddr->pru_io_addr; + u32 i; + u32 *pu32_sr_ctl_addr = (u32 *)(pruss_ioaddr->mcasp_io_addr + 0x180); + pru_suart_tx_cntx_priv *pru_suart_tx_priv = NULL; + pru_suart_rx_cntx_priv *pru_suart_rx_priv = NULL; + + /* ***************** UART 0 ************************ */ + /* Channel 0 context information is Tx */ + for (i = 0; i < 4; i++, pru_suart_regs++) { + pruss_readw(dev, (u32) &pru_suart_regs->ch_ctrl, &u16datatowrite, 1); + u16datatowrite |= (SUART_CHN_TX << 8); + pruss_writew(dev, (u32) &pru_suart_regs->ch_ctrl, + &u16datatowrite, 1); + pruss_readw(dev, (u32) &pru_suart_regs->ch_ctrl, &u16datatowrite, 1); + u16datatowrite |= (0xF & uart_tx[i]); + pruss_writew(dev, (u32) &pru_suart_regs->ch_ctrl, + &u16datatowrite, 1); + pruss_readw(dev, (u32) &pru_suart_regs->ch_config1, + &u16datatowrite, 1); + u16datatowrite |= (SUART_DEFAULT_OVRSMPL << 10); + pruss_writew(dev, (u32) &pru_suart_regs->ch_config1, + &u16datatowrite, 1); + pruss_readw(dev, (u32) &pru_suart_regs->ch_config2, + &u16datatowrite, 1); + u16datatowrite |= 8; + pruss_writew(dev, (u32) &pru_suart_regs->ch_config2, + &u16datatowrite, 1); + if ((uart_config[i] & PRU_SUART_HALF_TX_DISABLED) == + PRU_SUART_HALF_TX_DISABLED){ + pruss_readw(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + u16datatowrite |= (SUART_CHN_DISABLED << 15); + pruss_writew(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + } else { + pruss_readw(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + u16datatowrite |= (SUART_CHN_ENABLED << 15); + pruss_writew(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + __raw_writel(MCASP_SRCTL_TX_MODE, + pu32_sr_ctl_addr + uart_tx[i]); + } + pruss_readl(dev, (u32) &pru_suart_regs->reserved1, &u32datatowrite, 1); + u32datatowrite |= 1; + pruss_writel(dev, (u32) &pru_suart_regs->reserved1, + &u32datatowrite, 1); + /* SUART1 TX context base addr */ + pru_suart_tx_priv = (pru_suart_tx_cntx_priv *) + (PRU0_DATARAM_OFFSET + (0x0B0 + (i * 0x50))); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xsrctl_base, + (MCASP_SRCTL_BASE_ADDR + (uart_tx[i] << 2)), 1); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xbuf_base, + (MCASP_XBUF_BASE_ADDR + (uart_tx[i] << 2)), 1); + /* SUART1 TX formatted data base addr */ + pruss_writel(dev, (u32) &pru_suart_tx_priv->buff_addr, + (0x0090 + (i * 0x050)), 1); + + /* Channel 1 is Rx context information */ + pru_suart_regs++; + pruss_readw(dev, (u32) &pru_suart_regs->ch_ctrl, &u16datatowrite, 1); + u16datatowrite |= (SUART_CHN_RX << 8); + pruss_writew(dev, (u32) &pru_suart_regs->ch_ctrl, + &u16datatowrite, 1); + pruss_readw(dev, (u32) &pru_suart_regs->ch_ctrl, &u16datatowrite, 1); + u16datatowrite |= (0xF & uart_rx[i]); + pruss_writew(dev, (u32) &pru_suart_regs->ch_ctrl, + &u16datatowrite, 1); + pruss_readw(dev, (u32) &pru_suart_regs->ch_config1, + &u16datatowrite, 1); + u16datatowrite |= (SUART_DEFAULT_OVRSMPL << 10); + pruss_writew(dev, (u32) &pru_suart_regs->ch_config1, + &u16datatowrite, 1); + pruss_readw(dev, (u32) &pru_suart_regs->ch_config2, + &u16datatowrite, 1); + u16datatowrite |= 8; + pruss_writew(dev, (u32) &pru_suart_regs->ch_config2, + &u16datatowrite, 1); + if ((uart_config[i] & PRU_SUART_HALF_RX_DISABLED) == + PRU_SUART_HALF_RX_DISABLED) { + pruss_readw(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + u16datatowrite |= (SUART_CHN_DISABLED << 15); + pruss_writew(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + } else { + pruss_readw(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + u16datatowrite |= (SUART_CHN_ENABLED << 15); + pruss_writew(dev, (u32) &pru_suart_regs->ch_txrx_status, + &u16datatowrite, 1); + __raw_writel(MCASP_SRCTL_RX_MODE, + pu32_sr_ctl_addr + uart_rx[i]); + } + /* RX is active by default, write the dummy received data + * at PRU RAM addr 0x1FC to avoid memory corruption + */ + pruss_readl(dev, (u32) &pru_surt_regs->ch_txrx_data, + &u32datatowrite, 1); + u32datatowrite |= RX_DEFAULT_DATA_DUMP_ADDR; + pruss_writel(dev, (u32) &pru_suart_regs->ch_txrx_data, + &u32datatowrite, 1); + pruss_readl(dev, (u32) &pru_suart_regs->reserved1, &u32datatowrite, 1); + u32datatowrite = 0; + pruss_writel(dev, (u32) &pru_suart_regs->reserved1, &u32datatowrite, 1); + /* SUART1 RX context base addr */ + pru_suart_rx_priv = (pru_suart_rx_cntx_priv *) + (PRU0_DATARAM_OFFSET + (0x0C0 + (i * 0x50))); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rbuf_base, + (MCASP_RBUF_BASE_ADDR + (uart_rx[i] << 2)), 1); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rsrctl_base, + (MCASP_SRCTL_BASE_ADDR + (uart_rx[i] << 2)), 1); + } +} +#endif + +static void pru_set_rx_tx_mode(struct device *dev, u32 pru_mode, u32 pru_num) +{ + + u32 pru_offset; + + if (pru_num == PRUSS_NUM0) + pru_offset = PRU_SUART_PRU0_RX_TX_MODE; + else if (pru_num == PRUSS_NUM1) + pru_offset = PRU_SUART_PRU1_RX_TX_MODE; + else + return; + pruss_writeb(dev, pru_offset, (u8 *) &pru_mode, 1); +} + +static void pru_set_delay_count(struct device *dev, u32 pru_freq) +{ + u32 u32delay_cnt; + + if (pru_freq == 228) + u32delay_cnt = 5; + else if (pru_freq == 186) + u32delay_cnt = 5; + else + u32delay_cnt = 3; + + /* PRU 0 */ + pruss_writeb(dev, PRU_SUART_PRU0_DELAY_OFFSET, + (u8 *) &u32delay_cnt, 1); + + /* PRU 1 */ + pruss_writeb(dev, PRU_SUART_PRU1_DELAY_OFFSET, + (u8 *) &u32delay_cnt, 1); +} + +static s32 suart_set_pru_id(struct device *dev, u32 pru_no) +{ + u32 offset; + u8 reg_val = 0; + + if (0 == pru_no) + offset = PRU_SUART_PRU0_ID_ADDR; + else if (1 == pru_no) + offset = PRU_SUART_PRU1_ID_ADDR; + else + return PRU_SUART_FAILURE; + + reg_val = pru_no; + pruss_writeb(dev, offset, (u8 *) ®_val, 1); + + return PRU_SUART_SUCCESS; +} + +/* + * suart Initialization routine + */ +s16 pru_softuart_init(struct device *dev, u32 tx_baud_value, + u32 rx_baud_value, u32 oversampling, + u8 *pru_suart_emu_code, u32 fw_size, + pruss_suart_iomap *pruss_ioaddr) +{ + u32 u32datatowrite[128] = {0}; + s16 status = PRU_SUART_SUCCESS; + s16 idx; + s16 retval; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) && + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) + return PRU_SUART_FAILURE; + + suart_iomap.mcasp_io_addr = pruss_ioaddr->mcasp_io_addr; + suart_iomap.p_fifo_buff_phys_base = + pruss_ioaddr->p_fifo_buff_phys_base; + suart_iomap.p_fifo_buff_virt_base = + pruss_ioaddr->p_fifo_buff_virt_base; + suart_iomap.pru_clk_freq = pruss_ioaddr->pru_clk_freq; + /* Configure McASP0 */ + suart_mcasp_config(tx_baud_value, + rx_baud_value, oversampling, pruss_ioaddr); + pruss_enable(dev, PRUSS_NUM0); + pruss_enable(dev, PRUSS_NUM1); + + /* Reset PRU RAM */ + pruss_writel(dev, PRU0_DATARAM_OFFSET, u32datatowrite, + (PRU0_DATARAM_SIZE / sizeof(int))); + pruss_writel(dev, PRU1_DATARAM_OFFSET, u32datatowrite, + (PRU1_DATARAM_SIZE / sizeof(int))); + pruss_load(dev, PRUSS_NUM0, (u32 *)pru_suart_emu_code, + (fw_size / sizeof(u32))); + pruss_load(dev, PRUSS_NUM1, (u32 *)pru_suart_emu_code, + (fw_size / sizeof(u32))); + retval = arm_to_pru_intr_init(dev); + if (-1 == retval) + return status; + pru_set_delay_count(dev, pruss_ioaddr->pru_clk_freq); + suart_set_pru_id(dev, PRUSS_NUM0); + suart_set_pru_id(dev, PRUSS_NUM1); + pru_set_rx_tx_mode(dev, PRU0_MODE, PRUSS_NUM0); + pru_set_rx_tx_mode(dev, PRU1_MODE, PRUSS_NUM1); + pru_set_ram_data(dev, pruss_ioaddr); + pruss_run(dev, PRUSS_NUM0); + pruss_run(dev, PRUSS_NUM1); + + /* Initialize g_uart_statu_table */ + for (idx = 0; idx < 8; idx++) + g_uart_statu_table[idx] = ePRU_SUART_UART_FREE; + + return status; +} + +void pru_set_fifo_timeout(struct device *dev, s16 timeout) +{ + pruss_writew(dev, PRU_SUART_PRU0_IDLE_TIMEOUT_OFFSET, + &timeout, 1); + pruss_writew(dev, PRU_SUART_PRU1_IDLE_TIMEOUT_OFFSET, + &timeout, 1); +} + +s16 pru_softuart_deinit(struct device *dev) +{ + u32 offset; + s16 s16retval = 0; + u32 u32value = 0; + + offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + u32value = 0xFFFFFFFF; + s16retval = pruss_writel(dev, offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + offset = (u32) (PRUSS_INTC_STATCLRINT0 & 0xFFFF); + u32value = 0xFFFFFFFF; + s16retval = pruss_writel(dev, offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + pruss_disable(dev, 0); + pruss_disable(dev, 1); + + return PRU_SUART_SUCCESS; +} + +/* suart Instance open routine */ +s16 pru_softuart_open(suart_handle h_suart) +{ + s16 status = PRU_SUART_SUCCESS; + + switch (h_suart->uart_num) { + case PRU_SUART_UART1: + if (g_uart_statu_table[PRU_SUART_UART1 - 1] == + ePRU_SUART_UART_IN_USE) { + status = SUART_UART_IN_USE; + return status; + } else { + h_suart->uart_status = ePRU_SUART_UART_IN_USE; + h_suart->uart_type = PRU_SUART0_CONFIG_DUPLEX; + h_suart->uart_tx_channel = PRU_SUART0_CONFIG_TX_SER; + h_suart->uart_rx_channel = PRU_SUART0_CONFIG_RX_SER; + g_uart_statu_table[PRU_SUART_UART1 - 1] = + ePRU_SUART_UART_IN_USE; + } + break; + + case PRU_SUART_UART2: + if (g_uart_statu_table[PRU_SUART_UART2 - 1] == + ePRU_SUART_UART_IN_USE) { + status = SUART_UART_IN_USE; + return status; + } else { + + h_suart->uart_status = ePRU_SUART_UART_IN_USE; + h_suart->uart_type = PRU_SUART1_CONFIG_DUPLEX; + h_suart->uart_tx_channel = PRU_SUART1_CONFIG_TX_SER; + h_suart->uart_rx_channel = PRU_SUART1_CONFIG_RX_SER; + g_uart_statu_table[PRU_SUART_UART2 - 1] = + ePRU_SUART_UART_IN_USE; + } + break; + + case PRU_SUART_UART3: + if (g_uart_statu_table[PRU_SUART_UART3 - 1] == + ePRU_SUART_UART_IN_USE) { + status = SUART_UART_IN_USE; + return status; + } else { + + h_suart->uart_status = ePRU_SUART_UART_IN_USE; + h_suart->uart_type = PRU_SUART2_CONFIG_DUPLEX; + h_suart->uart_tx_channel = PRU_SUART2_CONFIG_TX_SER; + h_suart->uart_rx_channel = PRU_SUART2_CONFIG_RX_SER; + g_uart_statu_table[PRU_SUART_UART3 - 1] = + ePRU_SUART_UART_IN_USE; + } + break; + + case PRU_SUART_UART4: + if (g_uart_statu_table[PRU_SUART_UART4 - 1] == + ePRU_SUART_UART_IN_USE) { + status = SUART_UART_IN_USE; + return status; + } else { + + h_suart->uart_status = ePRU_SUART_UART_IN_USE; + h_suart->uart_type = PRU_SUART3_CONFIG_DUPLEX; + h_suart->uart_tx_channel = PRU_SUART3_CONFIG_TX_SER; + h_suart->uart_rx_channel = PRU_SUART3_CONFIG_RX_SER; + + g_uart_statu_table[PRU_SUART_UART4 - 1] = + ePRU_SUART_UART_IN_USE; + } + break; + + case PRU_SUART_UART5: + if (g_uart_statu_table[PRU_SUART_UART5 - 1] == + ePRU_SUART_UART_IN_USE) { + status = SUART_UART_IN_USE; + return status; + } else { + h_suart->uart_status = ePRU_SUART_UART_IN_USE; + h_suart->uart_type = PRU_SUART4_CONFIG_DUPLEX; + h_suart->uart_tx_channel = PRU_SUART4_CONFIG_TX_SER; + h_suart->uart_rx_channel = PRU_SUART4_CONFIG_RX_SER; + + g_uart_statu_table[PRU_SUART_UART5 - 1] = + ePRU_SUART_UART_IN_USE; + } + break; + + case PRU_SUART_UART6: + if (g_uart_statu_table[PRU_SUART_UART6 - 1] == + ePRU_SUART_UART_IN_USE) { + status = SUART_UART_IN_USE; + return status; + } else { + h_suart->uart_status = ePRU_SUART_UART_IN_USE; + h_suart->uart_type = PRU_SUART5_CONFIG_DUPLEX; + h_suart->uart_tx_channel = PRU_SUART5_CONFIG_TX_SER; + h_suart->uart_rx_channel = PRU_SUART5_CONFIG_RX_SER; + g_uart_statu_table[PRU_SUART_UART6 - 1] = + ePRU_SUART_UART_IN_USE; + } + break; + + case PRU_SUART_UART7: + if (g_uart_statu_table[PRU_SUART_UART7 - 1] == + ePRU_SUART_UART_IN_USE) { + status = SUART_UART_IN_USE; + return status; + } else { + h_suart->uart_status = ePRU_SUART_UART_IN_USE; + h_suart->uart_type = PRU_SUART6_CONFIG_DUPLEX; + h_suart->uart_tx_channel = PRU_SUART6_CONFIG_TX_SER; + h_suart->uart_rx_channel = PRU_SUART6_CONFIG_RX_SER; + g_uart_statu_table[PRU_SUART_UART7 - 1] = + ePRU_SUART_UART_IN_USE; + } + break; + + case PRU_SUART_UART8: + if (g_uart_statu_table[PRU_SUART_UART8 - 1] == + ePRU_SUART_UART_IN_USE) { + status = SUART_UART_IN_USE; + return status; + } else { + h_suart->uart_status = ePRU_SUART_UART_IN_USE; + h_suart->uart_type = PRU_SUART7_CONFIG_DUPLEX; + h_suart->uart_tx_channel = PRU_SUART7_CONFIG_TX_SER; + h_suart->uart_rx_channel = PRU_SUART7_CONFIG_RX_SER; + g_uart_statu_table[PRU_SUART_UART8 - 1] = + ePRU_SUART_UART_IN_USE; + } + break; + + default: + status = SUART_INVALID_UART_NUM; + break; + } + return status; +} + +/* suart instance close routine */ +s16 pru_softuart_close(suart_handle h_uart) +{ + s16 status = SUART_SUCCESS; + + if (h_uart == NULL) { + return PRU_SUART_ERR_HANDLE_INVALID; + } else { + g_uart_statu_table[h_uart->uart_num - 1] = + ePRU_SUART_UART_FREE; + /* Reset the Instance to Invalid */ + h_uart->uart_num = PRU_SUART_UARTx_INVALID; + h_uart->uart_status = ePRU_SUART_UART_FREE; + } + return status; +} + +/* + * suart routine for setting relative baud rate + */ +s16 pru_softuart_setbaud(struct device *dev, suart_handle h_uart, + u16 tx_clk_divisor, u16 rx_clk_divisor) +{ + u32 offset; + u32 pru_offset; + s16 status = SUART_SUCCESS; + u16 ch_num; + u16 regval = 0; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + + /* Set the clock divisor value s32o the McASP */ + if ((tx_clk_divisor > 385) || (tx_clk_divisor == 0)) + return SUART_INVALID_CLKDIVISOR; + if ((rx_clk_divisor > 385) || (rx_clk_divisor == 0)) + return SUART_INVALID_CLKDIVISOR; + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (h_uart->uart_num <= 4) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + ch_num -= 8; + } + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + + if (tx_clk_divisor != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= (~0x3FF); + regval |= tx_clk_divisor; + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return PRU_MODE_INVALID; + } + regval = 0; + if (rx_clk_divisor != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= (~0x3FF); + regval |= tx_clk_divisor; + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + return status; +} + +/* + * suart routine for setting number of bits per character for a specific uart + */ +s16 pru_softuart_setdatabits(struct device *dev, suart_handle h_uart, + u16 tx_data_bits, u16 rx_data_bits) +{ + u32 offset; + u32 pru_offset; + s16 status = SUART_SUCCESS; + u16 ch_num; + u32 reg_val; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + + /* + * NOTE: + * The supported data bits are 6,7,8,9,10,11,12 bits per character + */ + + if ((tx_data_bits < ePRU_SUART_DATA_BITS6) + || (tx_data_bits > ePRU_SUART_DATA_BITS12)) + return PRU_SUART_ERR_PARAMETER_INVALID; + + if ((rx_data_bits < ePRU_SUART_DATA_BITS6) + || (rx_data_bits > ePRU_SUART_DATA_BITS12)) + return PRU_SUART_ERR_PARAMETER_INVALID; + + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (h_uart->uart_num <= 4) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + ch_num -= 8; + } + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + + if (tx_data_bits != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 1); + reg_val &= ~(0xF); + reg_val |= tx_data_bits; + pruss_writeb(dev, offset, (u8 *) ®_val, 1); + } + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return PRU_MODE_INVALID; + } + if (rx_data_bits != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 1); + reg_val &= ~(0xF); + reg_val |= rx_data_bits; + pruss_writeb(dev, offset, (u8 *) &rx_data_bits, 1); + } + + return status; +} + +/* + * suart routine to configure specific uart + */ +s16 pru_softuart_setconfig(struct device *dev, suart_handle h_uart, + suart_config *config_uart) +{ + u32 offset; + u32 pru_offset; + s16 status = SUART_SUCCESS; + u16 ch_num; + u16 reg_val = 0; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + + /* + * NOTE: + * Dependent baud rate for the given UART,the value MUST BE LESS THAN OR + * EQUAL TO 64, preScalarValue <= 64 + */ + if ((config_uart->tx_clk_divisor > 384) + || (config_uart->rx_clk_divisor > 384)) { + return SUART_INVALID_CLKDIVISOR; + } + if ((config_uart->tx_bits_per_char < 8) + || (config_uart->tx_bits_per_char > 14)) { + return PRU_SUART_ERR_PARAMETER_INVALID; + } + if ((config_uart->rx_bits_per_char < 8) + || (config_uart->rx_bits_per_char > 14)) { + return PRU_SUART_ERR_PARAMETER_INVALID; + } + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num = (h_uart->uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) + - 2; + if (h_uart->uart_num <= 4) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + ch_num -= 8; + } + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + + /* Configuring the Transmit part of the given UART */ + /* Serializer has been as TX in mcasp config, by writing 1 in bits + * corresponding to tx serializer in PFUNC regsiter ie already set + * to GPIO mode PRU code will set then back to MCASP mode once TX + * request for that serializer is posted.It is required because at this + * pos32 Mcasp is accessed by both PRU and DSP have lower priority for + * Mcasp in comparison to PRU and DPS keeps on looping there only + */ + /* + * suart_mcasp_tx_serialzier_set + * (config_uart->tx_serializer, &suart_iomap); + */ + /* Configuring TX serializer */ + if (config_uart->tx_serializer != PRU_SUART_SERIALIZER_NONE) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val = reg_val | (config_uart->tx_serializer << + PRU_SUART_CH_CTRL_SR_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val = reg_val | (config_uart->tx_clk_divisor << + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val = reg_val | (config_uart->tx_bits_per_char << + PRU_SUART_CH_CONFIG2_BITPERCHAR_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + offset = 8; + pru_softuart_write(dev, h_uart, &offset, 0); + } + + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return PRU_MODE_INVALID; + } + + /* Configuring the Transmit part of the given UART */ + if (config_uart->rx_serializer != PRU_SUART_SERIALIZER_NONE) { + /* Configuring RX serializer */ + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val = reg_val | (config_uart->rx_serializer << + PRU_SUART_CH_CTRL_SR_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + + /* Configuring RX prescalar value and Oversampling */ + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val = reg_val | (config_uart->rx_clk_divisor << + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT) | + (config_uart->oversampling << + PRU_SUART_CH_CONFIG1_OVS_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + + /* Configuring RX bits per character value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val = reg_val | (config_uart->rx_bits_per_char << + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + } + return status; +} + +/* + * suart routine for getting the number of bytes transfered + */ +s16 pru_softuart_get_tx_data_len(struct device *dev, suart_handle h_uart) +{ + u32 offset; + u32 pru_offset; + u16 ch_num; + u16 u16_read_value = 0; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + + ch_num = h_uart->uart_num - 1; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num = (h_uart->uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) + - 2; + if (h_uart->uart_num <= 4) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + ch_num -= 8; + } + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) &u16_read_value, 2); + u16_read_value = ((u16_read_value & PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + return u16_read_value; +} + +/* + * suart routine for getting the number of bytes received + */ +s16 pru_softuart_get_rx_data_len(struct device *dev, suart_handle h_uart) +{ + u32 offset; + u32 pru_offset; + u16 ch_num; + u16 u16_read_value = 0; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (h_uart->uart_num <= 4) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + ch_num -= 8; + } + ch_num++; + } else if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) &u16_read_value, 2); + u16_read_value = ((u16_read_value & PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + + return u16_read_value; +} + +/* + * suart routine to get the configuration information from a specific uart + */ +s16 pru_softuart_getconfig(struct device *dev, + suart_handle h_uart, suart_config *config_uart) +{ + u32 offset; + u32 pru_offset; + u16 ch_num; + u16 reg_val = 0; + s16 status = SUART_SUCCESS; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + + /* + * NOTE: + * Dependent baud rate for the given UART,the value MUST BE LESS THAN OR + * EQUAL TO 64, preScalarValue <= 64 + */ + + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) + - 2; + if (h_uart->uart_num <= 4) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + ch_num -= 8; + } + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + + /* Configuring the Transmit part of the given UART */ + /* Configuring TX serializer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + config_uart->tx_serializer = ((reg_val & PRU_SUART_CH_CTRL_SR_MASK) >> + PRU_SUART_CH_CTRL_SR_SHIFT); + /* Configuring TX prescalar value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + config_uart->tx_clk_divisor = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) >> + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + + /* Configuring TX bits per character value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + config_uart->tx_bits_per_char = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) >> + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return PRU_MODE_INVALID; + } + /* Configuring the Transmit part of the given UART */ + /* Configuring RX serializer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + config_uart->rx_serializer = ((reg_val & PRU_SUART_CH_CTRL_SR_MASK) >> + PRU_SUART_CH_CTRL_SR_SHIFT); + + /* Configuring RX prescalar value and oversampling */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + config_uart->rx_clk_divisor = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + config_uart->oversampling = ((reg_val & + PRU_SUART_CH_CONFIG1_OVS_MASK) >> + PRU_SUART_CH_CONFIG1_OVS_SHIFT); + + /* Configuring RX bits per character value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + config_uart->rx_bits_per_char = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + + return status; +} + +s32 pru_softuart_pending_tx_request(struct device *dev) +{ + u32 offset = 0; + u32 u32ISR_value = 0; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + return SUART_SUCCESS; + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { + /* Read PRU Interrupt Status Register from PRU */ + offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + pruss_readl(dev, offset, (u32 *)&u32ISR_value, 1); + if ((u32ISR_value & 0x1) == 0x1) + return PRU_SUART_FAILURE; + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { + /* Read PRU Interrupt Status Register from PRU */ + offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + pruss_readl(dev, offset, (u32 *)&u32ISR_value, 1); + if ((u32ISR_value & 0x2) == 0x2) + return PRU_SUART_FAILURE; + } else { + return PRU_MODE_INVALID; + } + + return SUART_SUCCESS; +} + +/* + * suart data transmit routine + */ +s16 pru_softuart_write(struct device *dev, suart_handle h_uart, + u32 *pt_tx_data_buf, u16 data_len) +{ + u32 offset = 0; + u32 pru_offset; + s16 status = SUART_SUCCESS; + u16 ch_num; + u16 reg_val = 0; + u16 pru_num; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (h_uart->uart_num <= 4) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + pru_num = h_uart->uart_num; + } else { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + ch_num -= 8; + pru_num = h_uart->uart_num; + } + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + pru_num = 0; + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + pru_num = 1; + } else { + return PRU_MODE_INVALID; + } + + /* Writing data length to SUART channel register */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val &= ~PRU_SUART_CH_CONFIG2_DATALEN_MASK; + reg_val = reg_val | (data_len << PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + + /* Writing the data pos32er to channel TX data pointer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXDATA_OFFSET; + pruss_writeb(dev, offset, (u8 *) pt_tx_data_buf, 4); + + /* Service Request to PRU */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | + PRU_SUART_CH_CTRL_SREQ_MASK); + reg_val |= (PRU_SUART_CH_CTRL_TX_MODE << + PRU_SUART_CH_CTRL_MODE_SHIFT) | (PRU_SUART_CH_CTRL_SREQ << + PRU_SUART_CH_CTRL_SREQ_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + + /* generate ARM->PRU event */ + suart_arm_to_pru_intr(dev, pru_num); + + return status; +} + +/* + * suart data receive routine + */ +s16 pru_softuart_read(struct device *dev, suart_handle h_uart, + u32 *ptDataBuf, u16 data_len) +{ + u32 offset = 0; + u32 pru_offset; + s16 status = SUART_SUCCESS; + u16 ch_num; + u16 reg_val = 0; + u16 pru_num; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (h_uart->uart_num <= 4) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + pru_num = h_uart->uart_num; + } else { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + ch_num -= 8; + pru_num = h_uart->uart_num; + } + ch_num++; + } else if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + pru_num = 0; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + pru_num = 1; + } else { + return PRU_MODE_INVALID; + } + /* Writing data length to SUART channel register */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val &= ~PRU_SUART_CH_CONFIG2_DATALEN_MASK; + reg_val = reg_val | (data_len << PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + + /* Writing the data pos32er to channel RX data pointer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXDATA_OFFSET; + pruss_writeb(dev, offset, (u8 *) ptDataBuf, 4); + + /* Service Request to PRU */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | + PRU_SUART_CH_CTRL_SREQ_MASK); + reg_val |= (PRU_SUART_CH_CTRL_RX_MODE << + PRU_SUART_CH_CTRL_MODE_SHIFT) | (PRU_SUART_CH_CTRL_SREQ << + PRU_SUART_CH_CTRL_SREQ_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + + /* enable the timeout s32errupt */ + suart_intr_setmask(dev, h_uart->uart_num, PRU_RX_INTR, + CHN_TXRX_IE_MASK_TIMEOUT); + + /* generate ARM->PRU event */ + suart_arm_to_pru_intr(dev, pru_num); + + return status; +} + +/* + * suart routine to read the data from the RX FIFO + */ +s16 pru_softuart_read_data(struct device *dev, suart_handle h_uart, + u8 *p_data_buffer, s32 s32max_len, + u32 *pu32data_read) +{ + s16 ret_val = PRU_SUART_SUCCESS; + u8 *pu8src_addr = NULL; + u32 u32data_read = 0; + u32 u32data_len = 0; + u32 u32char_len = 0; + u32 offset = 0; + u32 pru_offset; + u16 ch_num; + u16 u16status = 0; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (h_uart->uart_num <= 4) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + ch_num -= 8; + } + ch_num++; + } else if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + + /* Get the data pos32er from channel RX data pointer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXDATA_OFFSET; + pruss_readb(dev, offset, (u8 *) &pu8src_addr, 4); + + /* Reading data length from SUART channel register */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) &u32data_len, 2); + + /* read the character length */ + u32char_len = u32data_len & PRU_SUART_CH_CONFIG2_BITPERCHAR_MASK; + u32char_len -= 2; /* remove the START & STOP bit */ + + u32data_len &= PRU_SUART_CH_CONFIG2_DATALEN_MASK; + u32data_len = u32data_len >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT; + u32data_len++; + + /* if the character length is greater than 8, then the size doubles */ + if (u32char_len > 8) + u32data_len *= 2; + + /* Check if the time-out had occured. If, yes, then we need to find the + * number of bytes read from PRU. Else, we need to + * read the requested bytes + */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &u16status, 1); + if (CHN_TXRX_STATUS_TIMEOUT == (u16status & CHN_TXRX_STATUS_TIMEOUT)) { + /* determine the number of bytes read s32o the FIFO */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_BYTESDONECNTR_OFFSET; + pruss_readb(dev, offset, (u8 *) &u32data_read, 1); + + /* if the character length is greater than 8, + then the size doubles */ + if (u32char_len > 8) + u32data_read *= 2; + +/* + * the data corresponding is loaded in second + * half during the timeout + */ + if (u32data_read > u32data_len) { + u32data_read -= u32data_len; + pu8src_addr += (u32data_len + 1); + } + + pru_softuart_clr_rx_fifo(dev, h_uart); + } else { + u32data_read = u32data_len; +/* + * if the bit is set, the data is in the first + * half of the FIFO else the data is in the second half + */ + /* Determine the buffer index by reading FIFO_OddEven flag*/ + if (u16status & CHN_TXRX_STATUS_CMPLT) + pu8src_addr += u32data_len; + } + + /* we should be copying only max len given by the application */ + if (u32data_read > s32max_len) + u32data_read = s32max_len; + +/* evaluate the virtual address of the FIFO address + * based on the physical addr + */ + pu8src_addr = (u8 *)((u32) pu8src_addr - + (u32) suart_iomap.p_fifo_buff_phys_base + + (u32) suart_iomap.p_fifo_buff_virt_base); + + /* Now we have both the data length and the source address. copy */ + for (offset = 0; offset < u32data_read; offset++) { + *p_data_buffer++ = *pu8src_addr++; + } + *pu32data_read = u32data_read; + ret_val = PRU_SUART_SUCCESS; + + return ret_val; +} + +/* + * suart routine to disable the receive functionality. + * This routine stops the PRU from receiving on selected + * UART and also disables the McASP serializer corresponding + * to this UART Rx line. + */ +s16 pru_softuart_stop_receive(struct device *dev, suart_handle h_uart) +{ + u16 status = SUART_SUCCESS; + u32 offset; + u32 pru_offset; + u16 ch_num; + u16 u16status; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if (h_uart->uart_num <= 4) { + /* PRU0 */ + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + /* PRU1 */ + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + /* First 8 channel corresponds to PRU0 */ + ch_num -= 8; + } + ch_num++; + } else if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + + /* read the existing value of status flag */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &u16status, 1); + + /* we need to clear the busy bit corresponding to receive channel */ + u16status &= ~(CHN_TXRX_STATUS_RDY); + pruss_writeb(dev, offset, (u8 *) &u16status, 1); + + /* get the serizlizer number being used for this Rx channel */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) &u16status, 2); + u16status &= PRU_SUART_CH_CTRL_SR_MASK; + u16status = u16status >> PRU_SUART_CH_CTRL_SR_SHIFT; + + /* we need to de-activate the serializer corresponding to this rx */ + status = suart_asp_serializer_deactivate(u16status, &suart_iomap); + + return status; +} + +/* + * suart routine to get the tx status for a specific uart + */ +s16 pru_softuart_get_tx_status(struct device *dev, suart_handle h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = SUART_SUCCESS; + u16 ch_num; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if (h_uart->uart_num <= 4) { + /* PRU0 */ + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + /* PRU1 */ + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + /* First 8 channel corresponds to PRU0 */ + ch_num -= 8; + } + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status, 1); + return status; +} + +s16 pru_softuart_clr_tx_status(struct device *dev, suart_handle h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = SUART_SUCCESS; + u16 ch_num; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + + ch_num = h_uart->uart_num - 1; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if (h_uart->uart_num <= 4) { + /* PRU0 */ + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + /* PRU1 */ + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + /* First 8 channel corresponds to PRU0 */ + ch_num -= 8; + } + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status, 1); + status &= ~(0x2); + pruss_writeb(dev, offset, (u8 *) &status, 1); + return status; +} + +/* + * suart routine to get the rx status for a specific uart + */ +s16 pru_softuart_get_rx_status(struct device *dev, suart_handle h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = SUART_SUCCESS; + u16 ch_num; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if (h_uart->uart_num <= 4) { + /* PRU0 */ + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + /* PRU1 */ + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + /* First 8 channel corresponds to PRU0 */ + ch_num -= 8; + } + ch_num++; + } else if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status, 1); + return status; +} + +s16 pru_softuart_clr_rx_fifo(struct device *dev, suart_handle h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = SUART_SUCCESS; + u16 ch_num; + u16 reg_val; + u16 uart_num; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + uart_num = h_uart->uart_num; + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) + - 2; + + if (h_uart->uart_num <= 4) { + /* PRU0 */ + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + /* PRU1 */ + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + /* First 8 channel corresponds to PRU0 */ + ch_num -= 8; + } + ch_num++; + } else if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + uart_num = 0; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + uart_num = 1; + } else { + return PRU_MODE_INVALID; + } + + /* Service Request to PRU */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | PRU_SUART_CH_CTRL_SREQ_MASK); + reg_val |= (PRU_SUART_CH_CTRL_RX_MODE << PRU_SUART_CH_CTRL_MODE_SHIFT) | + (PRU_SUART_CH_CTRL_SREQ << PRU_SUART_CH_CTRL_SREQ_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + suart_intr_setmask(dev, h_uart->uart_num, PRU_RX_INTR, + CHN_TXRX_IE_MASK_TIMEOUT); + + /* generate ARM->PRU event */ + suart_arm_to_pru_intr(dev, uart_num); + + return status; +} + +s16 pru_softuart_clr_rx_status(struct device *dev, suart_handle h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = SUART_SUCCESS; + u16 ch_num; + + if (h_uart == NULL) + return PRU_SUART_ERR_HANDLE_INVALID; + + ch_num = h_uart->uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if (h_uart->uart_num <= 4) { + /* PRU0 */ + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + /* PRU1 */ + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + /* First 8 channel corresponds to PRU0 */ + ch_num -= 8; + } + ch_num++; + } else if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status, 1); + status &= ~(0x3C); + pruss_writeb(dev, offset, (u8 *) &status, 1); + return status; +} + +/* + * suart_s32r_status_read: Gets the Global Interrupt status register + * for the specified SUART. + * uart_num < 1 to 6 > + * txrx_flag < Indicates TX or RX s32errupt for the uart > + */ +s16 pru_softuart_get_isrstatus(struct device *dev, u16 uart_num, u16 *txrx_flag) +{ + u32 offset; + u32 u32intc_offset; + u32 ch_num = 0xFF; + u32 reg_val = 0; + u32 u32reg_val = 0; + u32 u32ISR_value = 0; + u32 u32ack_reg_val = 0; + u8 pru0_mode = PRU_MODE_INVALID; + u8 pru1_mode = PRU_MODE_INVALID; + u32 u32stat_inx_clr_regoffset = 0; + + /* initialize the status & Flag to known value */ + *txrx_flag = 0; + + u32stat_inx_clr_regoffset = (u32) (PRUSS_INTC_STATIDXCLR & 0xFFFF); + + /* Read PRU Interrupt Status Register from PRU */ + u32intc_offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + + pruss_readl(dev, u32intc_offset, (u32 *)&u32ISR_value, 1); + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* determine if the s32errupt occured current UART context */ + u32reg_val = (PRU_SUART0_TX_EVT_BIT | PRU_SUART0_RX_EVT_BIT) << + ((uart_num - 1) * 2); + + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = uart_num * 2 - 2; + if (uart_num <= 4) + pru0_mode = PRU_MODE_RX_TX_BOTH; + else + pru1_mode = PRU_MODE_RX_TX_BOTH; + } else { + ch_num = uart_num - 1; + if ((u32ISR_value & 0x03FC) != 0) { + u32reg_val = 0; + + offset = PRU_SUART_PRU0_RX_TX_MODE; + pruss_readb(dev, offset, (u8 *) &pru0_mode, 1); + u32reg_val |= 1 << (uart_num + 1); + if (u32ISR_value & u32reg_val) { + /* acknowledge the s32errupt */ + u32ack_reg_val = ch_num + PRU_SUART0_TX_EVT; + pruss_writel(dev, u32stat_inx_clr_regoffset, + (u32 *)&u32ack_reg_val, 1); + *txrx_flag |= PRU_RX_INTR; + } + } + pruss_readl(dev, u32intc_offset, (u32 *)&u32ISR_value, 1); + if (u32ISR_value & 0x3FC00) { + u32reg_val = 0; + offset = PRU_SUART_PRU1_RX_TX_MODE; + pruss_readb(dev, offset, (u8 *) &pru1_mode, 1); + u32reg_val |= 1 << (uart_num + 9); + if (u32ISR_value & u32reg_val) { + /* acknowledge the s32errupt */ + u32ack_reg_val = ch_num + PRU_SUART4_TX_EVT; + pruss_writel(dev, u32stat_inx_clr_regoffset, + (u32 *)&u32ack_reg_val, 1); + *txrx_flag |= PRU_TX_INTR; + } + } + } + if (u32ISR_value & u32reg_val) { + if ((pru0_mode == PRU_MODE_RX_TX_BOTH) + || (pru1_mode == PRU_MODE_RX_TX_BOTH)) { + /* Check if the s32errupt occured for Tx */ + u32reg_val = PRU_SUART0_TX_EVT_BIT << ((uart_num - 1) + * 2); + if (u32ISR_value & u32reg_val) { + /* s32erupt occured for TX */ + *txrx_flag |= PRU_TX_INTR; + /* acknowledge the RX s32errupt */ + u32ack_reg_val = ch_num + PRU_SUART0_TX_EVT; + pruss_writel(dev, u32stat_inx_clr_regoffset, + (u32 *)&u32ack_reg_val, 1); + } + + /* Check if the s32errupt occured for Rx */ + u32reg_val = PRU_SUART0_RX_EVT_BIT<<((uart_num - 1) + * 2); + if (u32ISR_value & u32reg_val) { + /* s32erupt occured for RX */ + *txrx_flag |= PRU_RX_INTR; + ch_num += 1; + + /* acknowledge the RX s32errupt */ + u32ack_reg_val = ch_num + PRU_SUART0_TX_EVT; + pruss_writel(dev, u32stat_inx_clr_regoffset, + (u32 *)&u32ack_reg_val, 1); + } + } + reg_val = SUART_SUCCESS; + } + return reg_val; +} + +s32 pru_intr_clr_isrstatus(struct device *dev, u16 uart_num, u32 txrxmode) +{ + u32 offset; + u16 txrx_flag = 0; + u16 chn_num; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (uart_num <= 4) { + /* PRU0 */ + offset = PRU_SUART_PRU0_ISR_OFFSET + 1; + } else { + /* PRU1 */ + offset = PRU_SUART_PRU1_ISR_OFFSET + 1; + /* First 8 channel corresponds to PRU0 */ + chn_num -= 8; + } + if (2 == txrxmode) + chn_num++; + } else if (PRU0_MODE == txrxmode) { + offset = PRU_SUART_PRU0_ISR_OFFSET + 1; + } else if (PRU1_MODE == txrxmode) { + offset = PRU_SUART_PRU1_ISR_OFFSET + 1; + } else { + return PRU_MODE_INVALID; + } + + pruss_readb(dev, offset, (u8 *) &txrx_flag, 1); + txrx_flag &= ~(0x2); + pruss_writeb(dev, offset, (u8 *) &txrx_flag, 1); + + return 0; +} + +s16 suart_arm_to_pru_intr(struct device *dev, u16 uart_num) +{ + u32 u32offset; + u32 u32value; + s16 s16retval; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + if ((uart_num > 0) && (uart_num <= 4)) { + /* PRU0 SYS_EVT32 */ + u32value = 0x20; + } else if ((uart_num > 4) && (uart_num <= 8)) { + /* PRU1 SYS_EVT33 */ + u32value = 0x21; + } else { + return SUART_INVALID_UART_NUM; + } + } + + if ((PRU0_MODE == PRU_MODE_RX_ONLY) || (PRU1_MODE == PRU_MODE_RX_ONLY) + || (PRU0_MODE == PRU_MODE_TX_ONLY) + || (PRU1_MODE == PRU_MODE_TX_ONLY)) { + if (uart_num == PRUSS_NUM0) { + /* PRU0 SYS_EVT32 */ + u32value = 0x20; + } + + if (uart_num == PRUSS_NUM1) { + /* PRU0 SYS_EVT33 */ + u32value = 0x21; + } + } + + u32offset = (u32) (PRUSS_INTC_STATIDXSET & 0xFFFF); + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + return 0; +} + +s16 arm_to_pru_intr_init(struct device *dev) +{ + u32 u32offset; + u32 u32value; + u32 int_offset; + s16 s16retval = -1; +#if 0 + /* Set the MCASP Event to PRU0 as Edge Triggered */ + u32offset = (u32)(PRU_INTC_TYPE0 & 0xFFFF); + u32value = 0x80000000; + s16retval = + pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + +#endif + /* Clear all the host s32errupts */ + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; + int_offset++) { + u32offset = (u32) (PRUSS_INTC_HSTINTENIDXCLR & 0xFFFF); + u32value = int_offset; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + } + + /* Enable the global s32errupt */ + u32offset = (u32) (PRUSS_INTC_GLBLEN & 0xFFFF); + u32value = 0x1; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + + /* Enable the Host s32errupts for all host channels */ + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; + int_offset++) { + u32offset = (u32) (PRUSS_INTC_HSTINTENIDXSET & 0xFFFF); + u32value = int_offset; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + } + + u32offset = (u32) (PRUSS_INTC_HOSTMAP0 & 0xFFFF); + u32value = PRU_INTC_CHAN_123_HOST; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + + u32offset = (u32) (PRUSS_INTC_HOSTMAP1 & 0xFFFF); + u32value = PRU_INTC_CHAN_4567_HOST; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + u32offset = (u32) (PRUSS_INTC_HOSTMAP2 & 0xFFFF); + u32value = PRU_INTC_CHAN_89_HOST; + s16retval = + pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + + /* Set the channel for System s32rrupts + * MAP Channel 0 to SYS_EVT31 + */ + u32offset = (u32) (PRUSS_INTC_CHANMAP7 & 0xFFFF); + u32value = PRU_INTC_CHAN_0_SYSEVT_31; + s16retval = + pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + u32offset = (u32) (PRUSS_INTC_CHANMAP8 & 0xFFFF); + u32value = PRU_INTC_CHAN_12_SYSEVT; + s16retval = + pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + + /* Sets the channel for the system s32errupt + * MAP channel 3 to SYS_EVT36 SUART1-Tx + * MAP channel 3 to SYS_EVT37 SUART1-Rx + * MAP channel 4 to SYS_EVT38 SUART2-Tx + * MAP channel 4 to SYS_EVT39 SUART2-Rx + */ + u32offset = (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF); + u32value = PRU_INTC_CHAN_34_SYSEVT_36_39; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + + /* Sets the channel for the system s32errupt + * MAP channel 5 to SYS_EVT40 SUART3-Tx + * MAP channel 5 to SYS_EVT41 SUART3-Rx + * MAP channel 6 to SYS_EVT42 SUART4-Tx + * MAP channel 6 to SYS_EVT43 SUART4-Rx + */ + u32offset = (u32) (PRUSS_INTC_CHANMAP10 & 0xFFFF); + u32value = PRU_INTC_CHAN_56_SYSEVT_40_43; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + + /* Sets the channel for the system s32errupt + * MAP channel 7 to SYS_EVT44 SUART5-Tx + * MAP channel 7 to SYS_EVT45 SUART5-Rx + * MAP channel 8 to SYS_EVT46 SUART6-Tx + * MAP channel 8 to SYS_EVT47 SUART6-Rx + */ + u32offset = (u32) (PRUSS_INTC_CHANMAP11 & 0xFFFF); + u32value = PRU_INTC_CHAN_78_SYSEVT_44_47; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + + /* Sets the channel for the system s32errupt + * MAP channel 9 to SYS_EVT48 SUART7-Tx + * MAP channel 9 to SYS_EVT49 SUART7-Rx + */ + u32offset = (u32) (PRUSS_INTC_CHANMAP12 & 0xFFFF); + u32value = PRU_INTC_CHAN_9_SYSEVT_48_49; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + } + if ((PRU0_MODE == PRU_MODE_RX_ONLY) || (PRU1_MODE == PRU_MODE_RX_ONLY) + || (PRU0_MODE == PRU_MODE_TX_ONLY) + || (PRU1_MODE == PRU_MODE_TX_ONLY)) { + /* Sets the channel for the system s32errupt + * MAP channel 0 to SYS_EVT32 + * MAP channel 1 to SYS_EVT33 + * MAP channel 2 to SYS_EVT34 SUART0 + * MAP channel 3 to SYS_EVT35 SUART1 + */ + u32offset = (u32) (PRUSS_INTC_CHANMAP8 & 0xFFFF); + u32value = PRU_INTC_CHAN_0123_SYSEVT_32_35; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + + /* Sets the channel for the system s32errupt + * MAP channel 4 to SYS_EVT36 SUART2 + * MAP channel 5 to SYS_EVT37 SUART3 + * MAP channel 6 to SYS_EVT38 SUART4 + * MAP channel 7 to SYS_EVT39 SUART5 + */ + u32offset = (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF); + u32value = PRU_INTC_CHAN_4567_SYSEVT_36_39; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + + /* Sets the channel for the system s32errupt + * MAP channel 8 to SYS_EVT40 SUART6 + * MAP channel 9 to SYS_EVT41 SUART7 + * MAP channel 2 to SYS_EVT42 SUART0 + * MAP channel 3 to SYS_EVT43 SUART1 + */ + u32offset = (u32) (PRUSS_INTC_CHANMAP10 & 0xFFFF); + u32value = PRU_INTC_CHAN_8923_SYSEVT_40_43; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + + /* Sets the channel for the system s32errupt + * MAP channel 4 to SYS_EVT44 SUART2 + * MAP channel 5 to SYS_EVT45 SUART3 + * MAP channel 6 to SYS_EVT46 SUART4 + * MAP channel 7 to SYS_EVT47 SUART5 + */ + u32offset = (u32) (PRUSS_INTC_CHANMAP11 & 0xFFFF); + u32value = PRU_INTC_CHAN_4567_SYSEVT_44_47; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + + /* Sets the channel for the system s32errupt + * MAP channel 8 to SYS_EVT48 SUART6 + * MAP channel 9 to SYS_EVT49 SUART7 + */ + u32offset = (u32) (PRUSS_INTC_CHANMAP12 & 0xFFFF); + u32value = 0x00010908; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (-1 == s16retval) + return -1; + } + + /* Clear required set of system events + * and enable them using indexed register + */ + for (int_offset = 0; int_offset < 18; int_offset++) { + u32offset = (u32) (PRUSS_INTC_STATIDXCLR & 0xFFFF); + u32value = 32 + int_offset; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + + } + /* enable only the HOST to PRU s32errupts and let the PRU to Host events + * enabled by the separate API on demand basis. + */ + u32offset = (u32) (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 31; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + + u32offset = (u32) (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 32; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + u32offset = (u32) (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 33; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + u32offset = (u32) (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 50; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + + /* Enable the global s32errupt */ + u32offset = (u32) (PRUSS_INTC_GLBLEN & 0xFFFF); + u32value = 0x1; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + + /* Enable the Host s32errupts for all host channels */ + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; + int_offset++) { + u32offset = (u32) (PRUSS_INTC_HSTINTENIDXSET & 0xFFFF); + u32value = int_offset; + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + } + + return 0; +} + +s32 suart_pru_to_host_intr_enable(struct device *dev, u16 uart_num, + u32 txrxmode, s32 s32_flag) +{ + s32 ret_val = 0; + u32 u32offset; + u32 chn_num; + u32 u32value; + s16 s16retval = 0; + + if (uart_num > 8) + return SUART_INVALID_UART_NUM; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + chn_num = (uart_num * 2) - 2; + if (2 == txrxmode) /* Rx mode */ + chn_num++; + u32value = 34 + chn_num; + } else if ((PRU_MODE_RX_ONLY == txrxmode) + && (PRU0_MODE == PRU_MODE_RX_ONLY)) + u32value = 34 + chn_num; + else if ((PRU_MODE_RX_ONLY == txrxmode) + && (PRU1_MODE == PRU_MODE_RX_ONLY)) + u32value = 42 + chn_num; + else if ((PRU_MODE_TX_ONLY == txrxmode) + && (PRU0_MODE == PRU_MODE_TX_ONLY)) + u32value = 34 + chn_num; + else if ((PRU_MODE_TX_ONLY == txrxmode) + && (PRU1_MODE == PRU_MODE_TX_ONLY)) + u32value = 42 + chn_num; + else + return -1; + + if (SUART_TRUE == s32_flag) { + u32offset = (u32) (PRUSS_INTC_ENIDXSET & 0xFFFF); + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + } else { + u32offset = (u32) (PRUSS_INTC_ENIDXCLR & 0xFFFF); + s16retval = pruss_writel(dev, u32offset, (u32 *)&u32value, 1); + if (s16retval == -1) + return -1; + } + return ret_val; +} + +s32 suart_intr_setmask(struct device *dev, u16 uart_num, + u32 txrxmode, u32 s32rmask) +{ + u32 offset; + u32 pru_offset; + u32 txrx_flag; + u32 regval = 0; + u32 chn_num; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if ((uart_num > 0) && (uart_num <= 4)) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if ((uart_num > 4) && (uart_num <= 8)) { + offset = PRU_SUART_PRU1_IMR_OFFSET; + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + chn_num -= 8; + } else { + return SUART_INVALID_UART_NUM; + } + if (2 == txrxmode) { /* rx mode */ + chn_num++; + } + } else if (PRU0_MODE == txrxmode) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if (PRU1_MODE == txrxmode) { + offset = PRU_SUART_PRU1_IMR_OFFSET; + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + regval = 1 << chn_num; + if (CHN_TXRX_IE_MASK_CMPLT == (s32rmask & CHN_TXRX_IE_MASK_CMPLT)) { + pruss_readb(dev, offset, (u8 *) &txrx_flag, 2); + txrx_flag &= ~(regval); + txrx_flag |= regval; + pruss_writeb(dev, offset, (u8 *) &txrx_flag, 2); + } + if ((s32rmask & SUART_GBL_INTR_ERR_MASK) == + SUART_GBL_INTR_ERR_MASK) { + regval = 0; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(SUART_GBL_INTR_ERR_MASK); + regval |= (SUART_GBL_INTR_ERR_MASK); + pruss_writeb(dev, offset, (u8 *) ®val, 2); + + } + /* Break Indicator Interrupt Masked */ + if ((s32rmask & CHN_TXRX_IE_MASK_FE) == CHN_TXRX_IE_MASK_FE) { + regval = 0; + offset = pru_offset + + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(CHN_TXRX_IE_MASK_FE); + regval |= CHN_TXRX_IE_MASK_FE; + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + /* Framing Error Interrupt Masked */ + if (CHN_TXRX_IE_MASK_BI == (s32rmask & CHN_TXRX_IE_MASK_BI)) { + regval = 0; + offset = pru_offset + + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(CHN_TXRX_IE_MASK_BI); + regval |= CHN_TXRX_IE_MASK_BI; + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + /* Timeout error Interrupt Masked */ + if (CHN_TXRX_IE_MASK_TIMEOUT == + (s32rmask & CHN_TXRX_IE_MASK_TIMEOUT)) { + regval = 0; + offset = pru_offset + + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(CHN_TXRX_IE_MASK_TIMEOUT); + regval |= CHN_TXRX_IE_MASK_TIMEOUT; + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + return 0; +} + +s32 suart_intr_clrmask(struct device *dev, u16 uart_num, + u32 txrxmode, u32 s32rmask) +{ + u32 offset; + u32 pru_offset; + u16 txrx_flag; + u16 regval = 0; + u16 chn_num; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if ((uart_num > 0) && (uart_num <= 4)) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if ((uart_num > 4) && (uart_num <= 8)) { + /* PRU1 */ + offset = PRU_SUART_PRU1_IMR_OFFSET; + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + /* First 8 channel corresponds to PRU0 */ + chn_num -= 8; + } else { + return SUART_INVALID_UART_NUM; + } + if (2 == txrxmode) { /* rx mode */ + chn_num++; + } + } else if (PRU0_MODE == txrxmode) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if (PRU1_MODE == txrxmode) { + offset = PRU_SUART_PRU1_IMR_OFFSET; + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return PRU_MODE_INVALID; + } + regval = 1 << chn_num; + if (CHN_TXRX_IE_MASK_CMPLT == (s32rmask & CHN_TXRX_IE_MASK_CMPLT)) { + pruss_readb(dev, offset, (u8 *) &txrx_flag, 2); + txrx_flag &= ~(regval); + pruss_writeb(dev, offset, (u8 *) &txrx_flag, 2); + } + + if ((s32rmask & SUART_GBL_INTR_ERR_MASK) == SUART_GBL_INTR_ERR_MASK) { + regval = 0; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(SUART_GBL_INTR_ERR_MASK); + pruss_writeb(dev, offset, (u8 *) ®val, 2); + + } + /* Break Indicator Interrupt Masked */ + if ((s32rmask & CHN_TXRX_IE_MASK_FE) == CHN_TXRX_IE_MASK_FE) { + regval = 0; + offset = pru_offset + + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(CHN_TXRX_IE_MASK_FE); + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + /* Framing Error Interrupt Masked */ + if (CHN_TXRX_IE_MASK_BI == (s32rmask & CHN_TXRX_IE_MASK_BI)) { + regval = 0; + offset = pru_offset + + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(CHN_TXRX_IE_MASK_BI); + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + + /* Timeout error Interrupt Masked */ + if (CHN_TXRX_IE_MASK_TIMEOUT == + (s32rmask & CHN_TXRX_IE_MASK_TIMEOUT)) { + regval = 0; + offset = pru_offset + + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(CHN_TXRX_IE_MASK_TIMEOUT); + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + return 0; +} + +s32 suart_intr_getmask(struct device *dev, u16 uart_num, u32 txrxmode, + u32 s32rmask) +{ + u16 chn_num; + u32 offset; + u16 txrx_flag; + u16 regval = 1; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if ((uart_num > 0) && (uart_num <= 4)) { + + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if ((uart_num > 4) && (uart_num <= 8)) { + /* PRU1 */ + offset = PRU_SUART_PRU1_IMR_OFFSET; + /* First 8 channel corresponds to PRU0 */ + chn_num -= 8; + } else { + return SUART_INVALID_UART_NUM; + } + + if (2 == txrxmode) { /* rx mode */ + chn_num++; + } + } else if (PRU0_MODE == txrxmode) { + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if (PRU1_MODE == txrxmode) { + offset = PRU_SUART_PRU1_IMR_OFFSET; + } else { + return PRU_MODE_INVALID; + } + regval = regval << chn_num; + pruss_readb(dev, offset, (u8 *) &txrx_flag, 2); + txrx_flag &= regval; + if (0 == s32rmask) { + if (txrx_flag == 0) + return 1; + } + if (1 == s32rmask) { + if (txrx_flag == regval) + return 1; + } + return 0; +} diff --git a/drivers/tty/serial/da8xx_pruss/pruss_suart_api.h b/drivers/tty/serial/da8xx_pruss/pruss_suart_api.h new file mode 100644 index 0000000..4ce5621 --- /dev/null +++ b/drivers/tty/serial/da8xx_pruss/pruss_suart_api.h @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 _SUART_API_H_ +#define _SUART_API_H_ + +#include +#include +#include + +#define SINGLE_PRU 0 +#define BOTH_PRU 1 +#define PRU_ACTIVE BOTH_PRU + +#define SUART_NUM_OF_CHANNELS_PER_SUART 2 +#define SUART_NUM_OF_BYTES_PER_CHANNEL 16 + +#define SUART_PASS 0 +#define SUART_SUCCESS 0 +#define SUART_FAIL 1 +#define SUART_FALSE 0 +#define SUART_TRUE 1 + +#define PRU_TX_INTR 1 +#define PRU_RX_INTR 2 + +#define CHN_TXRX_STATUS_TIMEOUT BIT(6) +#define CHN_TXRX_STATUS_BI BIT(5) +#define CHN_TXRX_STATUS_FE BIT(4) +#define CHN_TXRX_STATUS_UNERR BIT(3) +#define CHN_TXRX_STATUS_OVRNERR BIT(3) +#define CHN_TXRX_STATUS_ERR BIT(2) +#define CHN_TXRX_STATUS_CMPLT BIT(1) +#define CHN_TXRX_STATUS_RDY BIT(0) + +#define CHN_TXRX_IE_MASK_TIMEOUT BIT(14) +#define CHN_TXRX_IE_MASK_BI BIT(13) +#define CHN_TXRX_IE_MASK_FE BIT(12) +#define CHN_TXRX_IE_MASK_CMPLT BIT(1) + +#define SUART_GBL_INTR_ERR_MASK BIT(9) +#define SUART_PRU_ID_MASK 0xFF + +#define SUART_FIFO_LEN 15 +#define SUART_8X_OVRSMPL 1 +#define SUART_16X_OVRSMPL 2 +#define SUART_DEFAULT_OVRSMPL SUART_8X_OVRSMPL + +#if (SUART_DEFAULT_OVRSMPL == SUART_16X_OVRSMPL) +#define SUART_DEFAULT_BAUD 57600 +#else +#define SUART_DEFAULT_BAUD 115200 +#endif + +#define PRU_MODE_INVALID 0x00 +#define PRU_MODE_TX_ONLY 0x1 +#define PRU_MODE_RX_ONLY 0x2 +#define PRU_MODE_RX_TX_BOTH 0x3 + +#if (PRU_ACTIVE == BOTH_PRU) +#define PRU0_MODE PRU_MODE_RX_ONLY +#define PRU1_MODE PRU_MODE_TX_ONLY +#elif (PRU_ACTIVE == SINGLE_PRU) +#define PRU0_MODE PRU_MODE_RX_TX_BOTH +#define PRU1_MODE PRU_MODE_INVALID +#else +#define PRU0_MODE PRU_MODE_INVALID +#define PRU1_MODE PRU_MODE_INVALID +#endif + +#define MCASP_XBUF_BASE_ADDR (0x01d00200) +#define MCASP_RBUF_BASE_ADDR (0x01d00280) +#define MCASP_SRCTL_BASE_ADDR (0x01d00180) + +#define MCASP_SRCTL_TX_MODE (0x000D) +#define MCASP_SRCTL_RX_MODE (0x000E) + +/* Since only PRU0 can work as RX */ +#define RX_DEFAULT_DATA_DUMP_ADDR (0x00001FC) +#define PRU_NUM_OF_CHANNELS (16) + +#define PRU_SUART_UART1 (1u) +#define PRU_SUART_UART2 (2u) +#define PRU_SUART_UART3 (3u) +#define PRU_SUART_UART4 (4u) +#define PRU_SUART_UART5 (5u) +#define PRU_SUART_UART6 (6u) +#define PRU_SUART_UART7 (7u) +#define PRU_SUART_UART8 (8u) + +#define PRU_SUART_UARTx_INVALID (9u) + +#define PRU_SUART_HALF_TX (1u) +#define PRU_SUART_HALF_RX (2u) +#define PRU_SUART_HALF_TX_DISABLED (4u) +#define PRU_SUART_HALF_RX_DISABLED (8u) + + +/* + * This enum is used to specify the direction of the channel in UART + */ +typedef enum { + SUART_CHN_TX = 1, + SUART_CHN_RX = 2 +} SUART_CHN_DIR; + +/* + * This enum is used to specify the state of the channel in UART. It + * is either enabled or disabled. + */ +typedef enum { + SUART_CHN_DISABLED = 0, + SUART_CHN_ENABLED = 1 +} SUART_CHN_STATE; + +typedef enum { + ePRU_SUART_DATA_BITS6 = 8, + ePRU_SUART_DATA_BITS7, + ePRU_SUART_DATA_BITS8, + ePRU_SUART_DATA_BITS9, + ePRU_SUART_DATA_BITS10, + ePRU_SUART_DATA_BITS11, + ePRU_SUART_DATA_BITS12 + } SUART_EN_BITSPERCHAR; + +typedef enum { + ePRU_SUART_NUM_1 = 1, + ePRU_SUART_NUM_2, + ePRU_SUART_NUM_3, + ePRU_SUART_NUM_4, + ePRU_SUART_NUM_5, + ePRU_SUART_NUM_6, + ePRU_SUART_NUM_7, + ePRU_SUART_NUM_8 +} SUART_EN_UARTNUM; + +typedef enum { + ePRU_SUART_HALF_TX = 1, + ePRU_SUART_HALF_RX, + ePRU_SUART_FULL_TX_RX +} SUART_EN_UARTTYPE; + +typedef enum { + ePRU_SUART_TX_CH0 = 0, + ePRU_SUART_TX_CH1, + ePRU_SUART_TX_CH2, + ePRU_SUART_TX_CH3, + ePRU_SUART_TX_CH4, + ePRU_SUART_TX_CH5, + ePRU_SUART_TX_CH6, + ePRU_SUART_TX_CH7 +} SUART_EN_TXCHANNEL; + +typedef enum { + ePRU_SUART_RX_CH0 = 0, + ePRU_SUART_RX_CH1, + ePRU_SUART_RX_CH2, + ePRU_SUART_RX_CH3, + ePRU_SUART_RX_CH4, + ePRU_SUART_RX_CH5, + ePRU_SUART_RX_CH6, + ePRU_SUART_RX_CH7 +} SUART_EN_RXCHANNEL; + +typedef enum { + ePRU_SUART_UART_FREE = 0, + ePRU_SUART_UART_IN_USE +} SUART_EN_UART_STATUS; + +typedef struct { + u16 mode:2; + u16 service_req:1; + u16 asp_id:2; + u16 reserved1:3; + u16 serializer_num:4; + u16 reserved2:4; + +} pru_suart_chn_cntrl; + +typedef struct { + u16 presacler:10; + u16 over_sampling:2; + u16 framing_mask:1; + u16 break_mask:1; + u16 timeout_mask:1; + u16 reserved:1; +} pru_suart_cnh_config1; + +typedef struct { + u16 bits_per_char:4; + u16 reserved1:4; + u16 data_len:4; + u16 reserved:4; +} pru_suart_chn_config2; + +typedef struct { + u16 txrx_ready:1; + u16 txrx_complete:1; + u16 txrx_error:1; + u16 txrx_underrun:1; + u16 framing_error:1; + u16 break_error:1; + u16 timeout_error:1; + u16 reserved:8; + u16 chn_state:1; +} pru_suart_chn_status; + +typedef struct { + pru_suart_chn_cntrl ch_ctrl; + pru_suart_cnh_config1 ch_config1; + pru_suart_chn_config2 ch_config2; + pru_suart_chn_status ch_txrx_status; + u32 ch_txrx_data; + u32 reserved1; +} pru_suart_regs, *pru_suart_regs_ovly; + +typedef struct { + u32 asp_xsrctl_base; + u32 asp_xbuf_base; + u16 buff_addr; + u8 buff_size; + u8 bits_loaded; +} pru_suart_tx_cntx_priv, *ppru_suart_tx_cntx_priv; + +typedef struct { + u32 asp_rbuf_base; + u32 asp_rsrctl_base; + u32 reserved1; + u32 reserved2; + u32 reserved3; + u32 reserved4; +} pru_suart_rx_cntx_priv, *ppru_suart_rx_cntx_priv; + +typedef struct { + u8 tx_serializer; + u8 rx_serializer; + u16 tx_clk_divisor; + u16 rx_clk_divisor; + u8 tx_bits_per_char; + u8 rx_bits_per_char; + u8 oversampling; + u8 bi_inter_mask; + u8 fe_intr_mask; +} suart_config; + +typedef struct { + u16 uart_num; + u16 uart_type; + u16 uart_tx_channel; + u16 uart_rx_channel; + u16 uart_status; +} suart_struct_handle, *suart_handle; + +typedef struct { + void *mcasp_io_addr; + void *p_fifo_buff_phys_base; + void *p_fifo_buff_virt_base; + u32 pru_clk_freq; +} pruss_suart_iomap; + +s16 pru_softuart_init(struct device *dev, u32 tx_baud_value, + u32 rx_baud_value, u32 oversampling, + u8 *pru_suart_emu_code, u32 fw_size, + pruss_suart_iomap *pruss_ioaddr); + +s16 pru_softuart_open(suart_handle h_suart); + +s16 pru_softuart_close(suart_handle h_uart); + +s16 pru_softuart_setbaud(struct device *dev, suart_handle h_uart, + u16 tx_clk_divisor, u16 rx_clk_divisor); + +s16 pru_softuart_setdatabits(struct device *dev, suart_handle h_uart, + u16 tx_data_bits, u16 rx_data_bits); + +s16 pru_softuart_setconfig(struct device *dev, suart_handle h_uart, + suart_config *config_uart); + +s16 pru_softuart_getconfig(struct device *dev, suart_handle h_uart, + suart_config *config_uart); + +s32 pru_softuart_pending_tx_request(struct device *dev); + +s16 pru_softuart_write(struct device *dev, suart_handle h_uart, + u32 *pt_tx_data_buf, u16 data_len); + +s16 pru_softuart_read(struct device *dev, suart_handle h_uart, + u32 *pt_data_buf, u16 data_len); + +s32 suart_intr_clrmask(struct device *dev, u16 uart_num, u32 txrxmode, + u32 intrmask); + +s16 pru_softuart_clr_tx_status(struct device *dev, suart_handle h_uart); + +s16 pru_softuart_get_tx_status(struct device *dev, suart_handle h_uart); + +s16 pru_softuart_clr_rx_status(struct device *dev, suart_handle h_uart); + +s16 pru_softuart_get_rx_status(struct device *dev, suart_handle h_uart); + +s16 pru_softuart_get_isrstatus(struct device *dev, u16 uart_num, + u16 *txrx_flag); + +s32 pru_intr_clr_isrstatus(struct device *dev, u16 uart_num, u32 txrxmode); + +s32 suart_intr_getmask(struct device *dev, u16 uart_num, u32 txrxmode, + u32 intrmask); + +s32 suart_intr_setmask(struct device *dev, u16 uart_num, + u32 txrxmode, u32 intrmask); + +s16 pru_softuart_get_tx_data_len(struct device *dev, suart_handle h_uart); + +s16 pru_softuart_get_rx_data_len(struct device *dev, suart_handle h_uart); + +s16 suart_arm_to_pru_intr(struct device *dev, u16 uart_num); + +s16 arm_to_pru_intr_init(struct device *dev); + +s16 pru_softuart_deinit(struct device *dev); + +s16 pru_softuart_clr_rx_fifo(struct device *dev, suart_handle h_uart); + +s16 pru_softuart_read_data(struct device *dev, suart_handle h_uart, + u8 *p_data_buffer, s32 s32_max_len, + u32 *pu32data_read); + +s16 pru_softuart_stop_receive(struct device *dev, suart_handle h_uart); + +s32 suart_pru_to_host_intr_enable(struct device *dev, u16 uart_num, + u32 txrxmode, s32 s32flag); + +void pru_set_fifo_timeout(struct device *dev, s16 timeout); +#endif diff --git a/drivers/tty/serial/da8xx_pruss/pruss_suart_board.h b/drivers/tty/serial/da8xx_pruss/pruss_suart_board.h new file mode 100644 index 0000000..58cc9e4 --- /dev/null +++ b/drivers/tty/serial/da8xx_pruss/pruss_suart_board.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: subhasish at 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 _OMAPL_SUART_BOARD_H_ +#define _OMAPL_SUART_BOARD_H_ + +#define PRU_SUART0_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART0_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART0_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART1_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ + PRU_SUART_HALF_RX) +#define PRU_SUART1_CONFIG_RX_SER (PRU_SUART_SERIALIZER_7) +#define PRU_SUART1_CONFIG_TX_SER (PRU_SUART_SERIALIZER_8) + +#define PRU_SUART2_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ + PRU_SUART_HALF_RX) +#define PRU_SUART2_CONFIG_RX_SER (PRU_SUART_SERIALIZER_9) +#define PRU_SUART2_CONFIG_TX_SER (PRU_SUART_SERIALIZER_10) + +#define PRU_SUART3_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART3_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART3_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART4_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART4_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART4_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART5_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART5_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART5_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART6_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART6_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART6_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART7_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART7_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART7_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#endif /* End of _OMAPL_SUART_BOARD_H_ */ diff --git a/drivers/tty/serial/da8xx_pruss/pruss_suart_err.h b/drivers/tty/serial/da8xx_pruss/pruss_suart_err.h new file mode 100644 index 0000000..dd94f4e --- /dev/null +++ b/drivers/tty/serial/da8xx_pruss/pruss_suart_err.h @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: jitendra at 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 _SUART_ERR_H_ +#define _SUART_ERR_H_ + +#define PRU_SUART_SUCCESS (0u) +#define PRU_SUART_FAILURE (-1) + +#define PRU_SUART_ERR_DEVICE_NOT_OPEN (1u) +#define PRU_SUART_ERR_UARTS_INIT_FAIL (2u) +#define PRU_SUART_ERR_UARTS_RESET_FAIL (3u) +#define PRU_SUART_ERR_HANDLE_INVALID (4u) +#define PRU_SUART_ERR_PARAMETER_INVALID (5u) + +#define PRU_SUART_ERR_TX (6u) +#define PRU_SUART_TX_COMPLETE (7u) +#define PRU_SUART_TX_BUSY (8u) +#define PRU_SUART_TX_UNDERRUN (9u) + +#define PRU_SUART_ERR_RX (10u) +#define PRU_SUART_RX_COMPLETE (11u) +#define PRU_SUART_RX_BUSY (12u) +#define PRU_SUART_RX_OVERRUN (13u) + +/* API Specific Errors */ +#define SUART_INVALID_TX_BAUD (14u) +#define SUART_INVALID_OVERSAMPLING (15u) +#define SUART_INVALID_RX_BAUD (16u) + +#define SUART_UART_IN_USE (17u) + +#define SUART_INVALID_CLKDIVISOR (18u) +#define SUART_INVALID_UART_NUM (19u) +#define SUART_INVALID_SR_NUM (20u) + +#endif diff --git a/drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h b/drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h new file mode 100644 index 0000000..fa99c6b --- /dev/null +++ b/drivers/tty/serial/da8xx_pruss/pruss_suart_mcasp.h @@ -0,0 +1,526 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 _OMAPLR_MCASP_H_ +#define _OMAPLR_MCASP_H_ + +#include +#include +#include + +typedef struct { + u32 REVID; + u32 RSVD0[3]; + u32 PFUNC; + u32 PDIR; + u32 PDOUT; + u32 PDIN; + u32 PDCLR; + u32 RSVD1[8]; + u32 GBLCTL; + u32 AMUTE; + u32 DLBCTL; + u32 DITCTL; + u32 RSVD2[3]; + u32 RGBLCTL; + u32 RMASK; + u32 RFMT; + u32 AFSRCTL; + u32 ACLKRCTL; + u32 AHCLKRCTL; + u32 RTDM; + u32 RINTCTL; + u32 RSTAT; + u32 RSLOT; + u32 RCLKCHK; + u32 REVTCTL; + u32 RSVD3[4]; + u32 XGBLCTL; + u32 XMASK; + u32 XFMT; + u32 AFSXCTL; + u32 ACLKXCTL; + u32 AHCLKXCTL; + u32 XTDM; + u32 XINTCTL; + u32 XSTAT; + u32 XSLOT; + u32 XCLKCHK; + u32 XEVTCTL; + u32 RSVD4[12]; + u32 DITCSRA0; + u32 DITCSRA1; + u32 DITCSRA2; + u32 DITCSRA3; + u32 DITCSRA4; + u32 DITCSRA5; + u32 DITCSRB0; + u32 DITCSRB1; + u32 DITCSRB2; + u32 DITCSRB3; + u32 DITCSRB4; + u32 DITCSRB5; + u32 DITUDRA0; + u32 DITUDRA1; + u32 DITUDRA2; + u32 DITUDRA3; + u32 DITUDRA4; + u32 DITUDRA5; + u32 DITUDRB0; + u32 DITUDRB1; + u32 DITUDRB2; + u32 DITUDRB3; + u32 DITUDRB4; + u32 DITUDRB5; + u32 RSVD5[8]; + u32 SRCTL0; + u32 SRCTL1; + u32 SRCTL2; + u32 SRCTL3; + u32 SRCTL4; + u32 SRCTL5; + u32 SRCTL6; + u32 SRCTL7; + u32 SRCTL8; + u32 SRCTL9; + u32 SRCTL10; + u32 SRCTL11; + u32 SRCTL12; + u32 SRCTL13; + u32 SRCTL14; + u32 SRCTL15; + u32 RSVD6[16]; + u32 XBUF0; + u32 XBUF1; + u32 XBUF2; + u32 XBUF3; + u32 XBUF4; + u32 XBUF5; + u32 XBUF6; + u32 XBUF7; + u32 XBUF8; + u32 XBUF9; + u32 XBUF10; + u32 XBUF11; + u32 XBUF12; + u32 XBUF13; + u32 XBUF14; + u32 XBUF15; + u32 RSVD7[16]; + u32 RBUF0; + u32 RBUF1; + u32 RBUF2; + u32 RBUF3; + u32 RBUF4; + u32 RBUF5; + u32 RBUF6; + u32 RBUF7; + u32 RBUF8; + u32 RBUF9; + u32 RBUF10; + u32 RBUF11; + u32 RBUF12; + u32 RBUF13; + u32 RBUF14; + u32 RBUF15; +} omapl_mcasp_regs, *omapl_mcasp_regs_ovly; + + +#define OMAPL_MCASP_PFUNC_AFSR_MASK (0x80000000u) +#define OMAPL_MCASP_PFUNC_AFSR_SHIFT (0x0000001Fu) +#define OMAPL_MCASP_PFUNC_AFSR_RESETVAL (0x00000000u) +/* AFSR Tokens */ +#define OMAPL_MCASP_PFUNC_AFSR_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AFSR_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AHCLKR_MASK (0x40000000u) +#define OMAPL_MCASP_PFUNC_AHCLKR_SHIFT (0x0000001Eu) +#define OMAPL_MCASP_PFUNC_AHCLKR_RESETVAL (0x00000000u) +/* AHCLKR Tokens */ +#define OMAPL_MCASP_PFUNC_AHCLKR_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AHCLKR_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_ACLKR_MASK (0x20000000u) +#define OMAPL_MCASP_PFUNC_ACLKR_SHIFT (0x0000001Du) +#define OMAPL_MCASP_PFUNC_ACLKR_RESETVAL (0x00000000u) +/* ACLKR Tokens */ +#define OMAPL_MCASP_PFUNC_ACLKR_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_ACLKR_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AFSX_MASK (0x10000000u) +#define OMAPL_MCASP_PFUNC_AFSX_SHIFT (0x0000001Cu) +#define OMAPL_MCASP_PFUNC_AFSX_RESETVAL (0x00000000u) +/* AFSX Tokens */ +#define OMAPL_MCASP_PFUNC_AFSX_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AFSX_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AHCLKX_MASK (0x08000000u) +#define OMAPL_MCASP_PFUNC_AHCLKX_SHIFT (0x0000001Bu) +#define OMAPL_MCASP_PFUNC_AHCLKX_RESETVAL (0x00000000u) +/* AHCLKX Tokens */ +#define OMAPL_MCASP_PFUNC_AHCLKX_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AHCLKX_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_ACLKX_MASK (0x04000000u) +#define OMAPL_MCASP_PFUNC_ACLKX_SHIFT (0x0000001Au) +#define OMAPL_MCASP_PFUNC_ACLKX_RESETVAL (0x00000000u) +/* ACLKX Tokens */ +#define OMAPL_MCASP_PFUNC_ACLKX_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_ACLKX_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AMUTE_MASK (0x02000000u) +#define OMAPL_MCASP_PFUNC_AMUTE_SHIFT (0x00000019u) +#define OMAPL_MCASP_PFUNC_AMUTE_RESETVAL (0x00000000u) +/* AMUTE Tokens */ +#define OMAPL_MCASP_PFUNC_AMUTE_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AMUTE_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR15_MASK (0x00008000u) +#define OMAPL_MCASP_PFUNC_AXR15_SHIFT (0x0000000Fu) +#define OMAPL_MCASP_PFUNC_AXR15_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR15_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR15_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR14_MASK (0x00004000u) +#define OMAPL_MCASP_PFUNC_AXR14_SHIFT (0x0000000Eu) +#define OMAPL_MCASP_PFUNC_AXR14_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR14_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR14_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR13_MASK (0x00002000u) +#define OMAPL_MCASP_PFUNC_AXR13_SHIFT (0x0000000Du) +#define OMAPL_MCASP_PFUNC_AXR13_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR13_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR13_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR12_MASK (0x00001000u) +#define OMAPL_MCASP_PFUNC_AXR12_SHIFT (0x0000000Cu) +#define OMAPL_MCASP_PFUNC_AXR12_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR12_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR12_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR11_MASK (0x00000800u) +#define OMAPL_MCASP_PFUNC_AXR11_SHIFT (0x0000000Bu) +#define OMAPL_MCASP_PFUNC_AXR11_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR11_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR11_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR10_MASK (0x00000400u) +#define OMAPL_MCASP_PFUNC_AXR10_SHIFT (0x0000000Au) +#define OMAPL_MCASP_PFUNC_AXR10_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR10_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR10_GPIO (0x00000001u) +#define OMAPL_MCASP_PFUNC_AXR9_MASK (0x00000200u) +#define OMAPL_MCASP_PFUNC_AXR9_SHIFT (0x00000009u) +#define OMAPL_MCASP_PFUNC_AXR9_RESETVAL (0x00000000u) +/* AXR9 Token */ +#define OMAPL_MCASP_PFUNC_AXR9_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR9_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR8_MASK (0x00000100u) +#define OMAPL_MCASP_PFUNC_AXR8_SHIFT (0x00000008u) +#define OMAPL_MCASP_PFUNC_AXR8_RESETVAL (0x00000000u) +/* AXR8 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR8_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR8_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR7_MASK (0x00000080u) +#define OMAPL_MCASP_PFUNC_AXR7_SHIFT (0x00000007u) +#define OMAPL_MCASP_PFUNC_AXR7_RESETVAL (0x00000000u) +/* AXR7 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR7_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR7_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR6_MASK (0x00000040u) +#define OMAPL_MCASP_PFUNC_AXR6_SHIFT (0x00000006u) +#define OMAPL_MCASP_PFUNC_AXR6_RESETVAL (0x00000000u) +/* AXR6 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR6_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR6_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR5_MASK (0x00000020u) +#define OMAPL_MCASP_PFUNC_AXR5_SHIFT (0x00000005u) +#define OMAPL_MCASP_PFUNC_AXR5_RESETVAL (0x00000000u) +/* AXR5 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR5_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR5_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR4_MASK (0x00000010u) +#define OMAPL_MCASP_PFUNC_AXR4_SHIFT (0x00000004u) +#define OMAPL_MCASP_PFUNC_AXR4_RESETVAL (0x00000000u) +/* AXR4 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR4_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR4_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR3_MASK (0x00000008u) +#define OMAPL_MCASP_PFUNC_AXR3_SHIFT (0x00000003u) +#define OMAPL_MCASP_PFUNC_AXR3_RESETVAL (0x00000000u) +/* AXR3 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR3_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR3_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR2_MASK (0x00000004u) +#define OMAPL_MCASP_PFUNC_AXR2_SHIFT (0x00000002u) +#define OMAPL_MCASP_PFUNC_AXR2_RESETVAL (0x00000000u) +/* AXR2 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR2_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR2_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR1_MASK (0x00000002u) +#define OMAPL_MCASP_PFUNC_AXR1_SHIFT (0x00000001u) +#define OMAPL_MCASP_PFUNC_AXR1_RESETVAL (0x00000000u) +/* AXR1 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR1_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR1_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR0_MASK (0x00000001u) +#define OMAPL_MCASP_PFUNC_AXR0_SHIFT (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR0_RESETVAL (0x00000000u) +/* AXR0 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR0_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR0_GPIO (0x00000001u) +#define OMAPL_MCASP_PFUNC_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_PDIR_AFSR_MASK (0x80000000u) +#define OMAPL_MCASP_PDIR_AFSR_SHIFT (0x0000001Fu) +#define OMAPL_MCASP_PDIR_AFSR_RESETVAL (0x00000000u) +/* AFSR Tokens */ +#define OMAPL_MCASP_PDIR_AFSR_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AFSR_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AHCLKR_MASK (0x40000000u) +#define OMAPL_MCASP_PDIR_AHCLKR_SHIFT (0x0000001Eu) +#define OMAPL_MCASP_PDIR_AHCLKR_RESETVAL (0x00000000u) +/* AHCLKR Tokens */ +#define OMAPL_MCASP_PDIR_AHCLKR_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AHCLKR_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_ACLKR_MASK (0x20000000u) +#define OMAPL_MCASP_PDIR_ACLKR_SHIFT (0x0000001Du) +#define OMAPL_MCASP_PDIR_ACLKR_RESETVAL (0x00000000u) +/* ACLKR Tokens */ +#define OMAPL_MCASP_PDIR_ACLKR_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_ACLKR_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AFSX_MASK (0x10000000u) +#define OMAPL_MCASP_PDIR_AFSX_SHIFT (0x0000001Cu) +#define OMAPL_MCASP_PDIR_AFSX_RESETVAL (0x00000000u) +/* AFSX Tokens */ +#define OMAPL_MCASP_PDIR_AFSX_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AFSX_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AHCLKX_MASK (0x08000000u) +#define OMAPL_MCASP_PDIR_AHCLKX_SHIFT (0x0000001Bu) +#define OMAPL_MCASP_PDIR_AHCLKX_RESETVAL (0x00000000u) +/* AHCLKX Tokens */ +#define OMAPL_MCASP_PDIR_AHCLKX_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AHCLKX_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_ACLKX_MASK (0x04000000u) +#define OMAPL_MCASP_PDIR_ACLKX_SHIFT (0x0000001Au) +#define OMAPL_MCASP_PDIR_ACLKX_RESETVAL (0x00000000u) +/* ACLKX Tokens */ +#define OMAPL_MCASP_PDIR_ACLKX_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_ACLKX_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AMUTE_MASK (0x02000000u) +#define OMAPL_MCASP_PDIR_AMUTE_SHIFT (0x00000019u) +#define OMAPL_MCASP_PDIR_AMUTE_RESETVAL (0x00000000u) +/* AMUTE Tokens */ +#define OMAPL_MCASP_PDIR_AMUTE_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AMUTE_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR15_MASK (0x00008000u) +#define OMAPL_MCASP_PDIR_AXR15_SHIFT (0x0000000Fu) +#define OMAPL_MCASP_PDIR_AXR15_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR15_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR15_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR14_MASK (0x00004000u) +#define OMAPL_MCASP_PDIR_AXR14_SHIFT (0x0000000Eu) +#define OMAPL_MCASP_PDIR_AXR14_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR14_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR14_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR13_MASK (0x00002000u) +#define OMAPL_MCASP_PDIR_AXR13_SHIFT (0x0000000Du) +#define OMAPL_MCASP_PDIR_AXR13_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR13_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR13_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR12_MASK (0x00001000u) +#define OMAPL_MCASP_PDIR_AXR12_SHIFT (0x0000000Cu) +#define OMAPL_MCASP_PDIR_AXR12_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR12_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR12_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR11_MASK (0x00000800u) +#define OMAPL_MCASP_PDIR_AXR11_SHIFT (0x0000000Bu) +#define OMAPL_MCASP_PDIR_AXR11_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR11_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR11_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR10_MASK (0x00000400u) +#define OMAPL_MCASP_PDIR_AXR10_SHIFT (0x0000000Au) +#define OMAPL_MCASP_PDIR_AXR10_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR10_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR10_OUTPUT (0x00000001u) +#define OMAPL_MCASP_PDIR_AXR9_MASK (0x00000200u) +#define OMAPL_MCASP_PDIR_AXR9_SHIFT (0x00000009u) +#define OMAPL_MCASP_PDIR_AXR9_RESETVAL (0x00000000u) +/* AXR9 Tokens */ +#define OMAPL_MCASP_PDIR_AXR9_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR9_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR8_MASK (0x00000100u) +#define OMAPL_MCASP_PDIR_AXR8_SHIFT (0x00000008u) +#define OMAPL_MCASP_PDIR_AXR8_RESETVAL (0x00000000u) +/* AXR8 Tokens */ +#define OMAPL_MCASP_PDIR_AXR8_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR8_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR7_MASK (0x00000080u) +#define OMAPL_MCASP_PDIR_AXR7_SHIFT (0x00000007u) +#define OMAPL_MCASP_PDIR_AXR7_RESETVAL (0x00000000u) +/*----AXR7 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR7_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR7_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR6_MASK (0x00000040u) +#define OMAPL_MCASP_PDIR_AXR6_SHIFT (0x00000006u) +#define OMAPL_MCASP_PDIR_AXR6_RESETVAL (0x00000000u) +/*----AXR6 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR6_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR6_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR5_MASK (0x00000020u) +#define OMAPL_MCASP_PDIR_AXR5_SHIFT (0x00000005u) +#define OMAPL_MCASP_PDIR_AXR5_RESETVAL (0x00000000u) +/*----AXR5 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR5_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR5_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR4_MASK (0x00000010u) +#define OMAPL_MCASP_PDIR_AXR4_SHIFT (0x00000004u) +#define OMAPL_MCASP_PDIR_AXR4_RESETVAL (0x00000000u) +/*----AXR4 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR4_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR4_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR3_MASK (0x00000008u) +#define OMAPL_MCASP_PDIR_AXR3_SHIFT (0x00000003u) +#define OMAPL_MCASP_PDIR_AXR3_RESETVAL (0x00000000u) +/*----AXR3 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR3_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR3_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR2_MASK (0x00000004u) +#define OMAPL_MCASP_PDIR_AXR2_SHIFT (0x00000002u) +#define OMAPL_MCASP_PDIR_AXR2_RESETVAL (0x00000000u) +/*----AXR2 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR2_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR2_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR1_MASK (0x00000002u) +#define OMAPL_MCASP_PDIR_AXR1_SHIFT (0x00000001u) +#define OMAPL_MCASP_PDIR_AXR1_RESETVAL (0x00000000u) +/*----AXR1 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR1_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR1_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR0_MASK (0x00000001u) +#define OMAPL_MCASP_PDIR_AXR0_SHIFT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR0_RESETVAL (0x00000000u) +/*----AXR0 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR0_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR0_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_ACLKXCTL_CLKXP_MASK (0x00000080u) +#define OMAPL_MCASP_ACLKXCTL_CLKXP_SHIFT (0x00000007u) +#define OMAPL_MCASP_ACLKXCTL_CLKXP_RESETVAL (0x00000000u) +/*----CLKXP Tokens----*/ +#define OMAPL_MCASP_ACLKXCTL_CLKXP_RISINGEDGE (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_CLKXP_FALLINGEDGE (0x00000001u) + +#define OMAPL_MCASP_ACLKXCTL_ASYNC_MASK (0x00000040u) +#define OMAPL_MCASP_ACLKXCTL_ASYNC_SHIFT (0x00000006u) +#define OMAPL_MCASP_ACLKXCTL_ASYNC_RESETVAL (0x00000001u) +/*----ASYNC Tokens----*/ +#define OMAPL_MCASP_ACLKXCTL_ASYNC_SYNC (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_ASYNC_ASYNC (0x00000001u) + +#define OMAPL_MCASP_ACLKXCTL_CLKXM_MASK (0x00000020u) +#define OMAPL_MCASP_ACLKXCTL_CLKXM_SHIFT (0x00000005u) +#define OMAPL_MCASP_ACLKXCTL_CLKXM_RESETVAL (0x00000001u) +/*----CLKXM Tokens----*/ +#define OMAPL_MCASP_ACLKXCTL_CLKXM_EXTERNAL (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_CLKXM_INTERNAL (0x00000001u) + +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_MASK (0x0000001Fu) +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_ACLKXCTL_RESETVAL (0x00000060u) + +/* AHCLKXCTL */ +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_MASK (0x00008000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_SHIFT (0x0000000Fu) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_RESETVAL (0x00000001u) +/*----HCLKXM Tokens----*/ +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_EXTERNAL (0x00000000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_INTERNAL (0x00000001u) + +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_MASK (0x00004000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_SHIFT (0x0000000Eu) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_RESETVAL (0x00000000u) +/*----HCLKXP Tokens----*/ +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_NOTINVERTED (0x00000000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_INVERTED (0x00000001u) + +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_MASK (0x00000FFFu) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT (0x00000000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_AHCLKXCTL_RESETVAL (0x00008000u) + +#define MCASP_SUART_GBLCTL (0X00000000) +#define MCASP_SUART_RGBLCTL (0X00000000) +#define MCASP_SUART_XGBLCTL (0X00000000) +#define MCASP_SUART_RMASK_8 (0x000000FF) +#define MCASP_SUART_RMASK_16 (0x0000FFFF) +#define MCASP_SUART_RFMT_8 (0x0000A038) +#define MCASP_SUART_RFMT_16 (0x0000A078) +#define MCASP_SUART_FSRM (0X00000002) +#define MCASP_SUART_CLKRM_CLKRP (0X000000A0) +#define MCASP_SUART_HCLKRP (0X00008000) +#define MCASP_SUART_RTDMS0 (0X00000001) +#define MCASP_SUART_RSYNCERR (0X00000002) +#define MCASP_SUART_RMAX_RPS_256 (0x00FF0008) +#define MCASP_SUART_XMASK_0_31 (0X0000FFFF) +#define MCASP_SUART_XBUSEL_XSSZ_16_XPAD_0 (0x00002078) +#define MCASP_SUART_FSXM (0x00000002) +#define MCASP_SUART_CLKXM_ASYNC_CLKXP (0x000000E0) +#define MCASP_SUART_HCLKXM (0x00008000) +#define MCASP_SUART_XTDMS0 (0X00000001) +#define MCASP_SUART_XSYNCERR (0x00000002) +#define MCASP_SUART_XMAX_XPS_256 (0x00FF0008) +#define MCASP_SUART_SRCTL_DISMOD (0x0000000c) +#define MCASP_SUART_DIT_DISABLE (0X00000000) +#define MCASP_SUART_LOOPBACK_DISABLE (0x00000000) +#define MCASP_SUART_AMUTE_DISABLE (0X00000000) +#define MCASP_SUART_XSTAT (0x0000FFFF) +#define MCASP_SUART_RSTAT (0x0000FFFF) +#endif diff --git a/drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h b/drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h new file mode 100644 index 0000000..c2aa9d7 --- /dev/null +++ b/drivers/tty/serial/da8xx_pruss/pruss_suart_regs.h @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: jitendra at 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 _SUART_PRU_REGS_H_ +#define _SUART_PRU_REGS_H_ + +#include + +/** PRU0 DATA RAM base address */ +#define PRU0_DATARAM_OFFSET (0x0000u) +/** PRU1 DATA RAM base address */ +#define PRU1_DATARAM_OFFSET (0x2000u) + +/** PRU0 DATA RAM size */ +#define PRU0_DATARAM_SIZE (0x200u) +/** PRU1 DATA RAM size */ +#define PRU1_DATARAM_SIZE (0x200u) + +#define PRU_SUART_PRU0_CH0_OFFSET (0x0000) +#define PRU_SUART_PRU0_CH1_OFFSET (0x0010) +#define PRU_SUART_PRU0_CH2_OFFSET (0x0020) +#define PRU_SUART_PRU0_CH3_OFFSET (0x0030) +#define PRU_SUART_PRU0_CH4_OFFSET (0x0040) +#define PRU_SUART_PRU0_CH5_OFFSET (0x0050) +#define PRU_SUART_PRU0_CH6_OFFSET (0x0060) +#define PRU_SUART_PRU0_CH7_OFFSET (0x0070) +#define PRU_SUART_PRU0_IMR_OFFSET (0x0080) +/** Interrupt Mask Register */ +#define PRU_SUART_PRU0_ISR_OFFSET (0x0082) +/** Interrupt Status Register */ +#define PRU_SUART_PRU0_ID_ADDR (0x0084) +/** PRU ID Register */ +#define PRU_SUART_PRU0_RX_TX_MODE (0x0085) +#define PRU_SUART_PRU0_DELAY_OFFSET (0x0086) +#define PRU_SUART_PRU0_IDLE_TIMEOUT_OFFSET (0x0088) + +/* ********* PRU 1 Macros ************* */ +#define PRU_SUART_PRU1_CH0_OFFSET (0x2000) +#define PRU_SUART_PRU1_CH1_OFFSET (0x2010) +#define PRU_SUART_PRU1_CH2_OFFSET (0x2020) +#define PRU_SUART_PRU1_CH3_OFFSET (0x2030) +#define PRU_SUART_PRU1_CH4_OFFSET (0x2040) +#define PRU_SUART_PRU1_CH5_OFFSET (0x2050) +#define PRU_SUART_PRU1_CH6_OFFSET (0x2060) +#define PRU_SUART_PRU1_CH7_OFFSET (0x2070) +#define PRU_SUART_PRU1_IMR_OFFSET (0x2080) +#define PRU_SUART_PRU1_ISR_OFFSET (0x2082) +#define PRU_SUART_PRU1_ID_ADDR (0x2084) +#define PRU_SUART_PRU1_RX_TX_MODE (0x2085) +#define PRU_SUART_PRU1_DELAY_OFFSET (0x2086) +#define PRU_SUART_PRU1_IDLE_TIMEOUT_OFFSET (0x2088) + +/* SUART Channel Control Register bit descriptions */ +#define PRU_SUART_CH_CTRL_MODE_SHIFT 0x0000 +#define PRU_SUART_CH_CTRL_MODE_MASK 0x0003 +#define PRU_SUART_CH_CTRL_TX_MODE 0x0001 +#define PRU_SUART_CH_CTRL_RX_MODE 0x0002 + +/* Service Request */ +#define PRU_SUART_CH_CTRL_SREQ_SHIFT 0x0002 +#define PRU_SUART_CH_CTRL_SREQ_MASK 0x0004 +#define PRU_SUART_CH_CTRL_SREQ 0x0001 + +/* McASP Instance */ +#define PRU_SUART_CH_CTRL_MCASP_SHIFT 0x0003 +#define PRU_SUART_CH_CTRL_MCASP_MASK 0x0018 +#define PRU_SUART_CH_CTRL_SR_SHIFT 0x0008 +#define PRU_SUART_CH_CTRL_SR_MASK 0x0F00 + +/* SUART channel configuration1 register descriptions */ + +/* clock divisor - relative baud value */ +#define PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT 0x0000 +#define PRU_SUART_CH_CONFIG1_DIVISOR_MASK 0x03FF +/* oversampling */ +#define PRU_SUART_CH_CONFIG1_OVS_SHIFT 0x000A +#define PRU_SUART_CH_CONFIG1_OVS_MASK 0x0C00 + +/* SUART channel configuration2 register descriptions */ +/* Bits per character */ +#define PRU_SUART_CH_CONFIG2_BITPERCHAR_SHIFT 0x0000 +#define PRU_SUART_CH_CONFIG2_BITPERCHAR_MASK 0x000F + +/* Bits per character */ +#define PRU_SUART_CH_CONFIG2_DATALEN_SHIFT 0x0008 +#define PRU_SUART_CH_CONFIG2_DATALEN_MASK 0x0F00 + +/* SUART Channel register offsets */ +#define PRU_SUART_CH_CTRL_OFFSET 0x00 +#define PRU_SUART_CH_CONFIG1_OFFSET 0x02 +#define PRU_SUART_CH_CONFIG2_OFFSET 0x04 +#define PRU_SUART_CH_TXRXSTATUS_OFFSET 0x06 +#define PRU_SUART_CH_TXRXDATA_OFFSET 0x08 +#define PRU_SUART_CH_BYTESDONECNTR_OFFSET 0x0C + +/* SUART Event Numbers macros */ +#define PRU_SUART0_TX_EVT 34 +#define PRU_SUART0_RX_EVT 35 +#define PRU_SUART1_TX_EVT 36 +#define PRU_SUART1_RX_EVT 37 +#define PRU_SUART2_TX_EVT 38 +#define PRU_SUART2_RX_EVT 39 +#define PRU_SUART3_TX_EVT 40 +#define PRU_SUART3_RX_EVT 41 +#define PRU_SUART4_TX_EVT 42 +#define PRU_SUART4_RX_EVT 43 +#define PRU_SUART5_TX_EVT 44 +#define PRU_SUART5_RX_EVT 45 +#define PRU_SUART6_TX_EVT 46 +#define PRU_SUART6_RX_EVT 47 +#define PRU_SUART7_TX_EVT 48 +#define PRU_SUART7_RX_EVT 49 + +#define PRU_SUART0_TX_EVT_BIT BIT(2) +#define PRU_SUART0_RX_EVT_BIT BIT(3) +#define PRU_SUART1_TX_EVT_BIT BIT(4) +#define PRU_SUART1_RX_EVT_BIT BIT(5) +#define PRU_SUART2_TX_EVT_BIT BIT(6) +#define PRU_SUART2_RX_EVT_BIT BIT(7) +#define PRU_SUART3_TX_EVT_BIT BIT(8) +#define PRU_SUART3_RX_EVT_BIT BIT(9) +#define PRU_SUART4_TX_EVT_BIT BIT(10) +#define PRU_SUART4_RX_EVT_BIT BIT(11) +#define PRU_SUART5_TX_EVT_BIT BIT(12) +#define PRU_SUART5_RX_EVT_BIT BIT(13) +#define PRU_SUART6_TX_EVT_BIT BIT(14) +#define PRU_SUART6_RX_EVT_BIT BIT(15) +#define PRU_SUART7_TX_EVT_BIT BIT(16) +#define PRU_SUART7_RX_EVT_BIT BIT(17) + +/* + * SUART Config regs + */ +typedef struct { + u16 chn_ctrl; + u16 chn_config1; + u16 chn_config2; + u16 chn_txrx_status; + u32 chn_txrx_data; +} suart_struct_pru_regs; + +#endif diff --git a/drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c b/drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c new file mode 100644 index 0000000..7ced423 --- /dev/null +++ b/drivers/tty/serial/da8xx_pruss/pruss_suart_utils.c @@ -0,0 +1,384 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 +#include "pruss_suart_mcasp.h" +#include "pruss_suart_api.h" +#include "pruss_suart_regs.h" +#include "pruss_suart_board.h" +#include "pruss_suart_utils.h" +#include "pruss_suart_err.h" + + +#define SUART_TRX_DIV_CONF_SZ 4 + +static s16 suart_mcasp_tx_baud_set(u32 tx_baud_value, + pruss_suart_iomap *pruss_ioaddr); +static s16 suart_mcasp_rx_baud_set(u32 rx_baud_value, u32 oversampling, + pruss_suart_iomap *pruss_ioaddr); + +/* + * Lookup table for TX baud rate + * The divisor value is calculated using the formula + * + * ACLKX = (AUXCLK)/(CLKXDIV * HCLKXDIV) + * + * Where + * CLKXDIV takes values from 1-32 + * HCLKXDIV takes values from 1-4096 + * Here + * AUXCLK = 24MHz + */ +u32 lt_tx_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { + /*BaudRate, Divisor, CLKXDIV,HCLKXDIV */ + {300, 80000, 24, 3200}, + {600, 40000, 15, 2500}, + {1800, 13333, 10, 1212}, + {2400, 10000, 4, 2000}, + {4800, 5000, 1, 2500}, + {7200, 3333, 0, 3333}, + {9600, 2500, 0, 2500}, + {14400, 1666, 0, 1666}, + {19200, 1250, 0, 1250}, + {38400, 625, 0, 625}, + {57600, 416, 0, 416}, + {115200, 208, 0, 208}, + {230400, 104, 0, 104} +}; + +/* + * Lookup table for RX baud rate for 8 bit oversampling + * The divisor value is calculated using the formula + * + * ACLKR = (AUXCLK)/(CLKRDIV * HCLKRDIV) * Oversampling + * + * Where + * CLKRDIV takes values from 1-32 + * HCLKRDIV takes values from 1-4096 + * Here + * AUXCLK = 24MHz + */ +u32 lt_rx_8x_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { +/* BaudRate, Divisor, CLKXDIV, HCLKXDIV */ + {300, 10000, 4, 2000}, + {600, 5000, 1, 2500}, + {1800, 1667, 0, 1667}, + {2400, 1250, 0, 1250}, + {7200, 417, 0, 417}, + {4800, 625, 0, 625}, + {9600, 312, 0, 312}, + {14400, 208, 0, 208}, + {19200, 156, 0, 156}, + {38400, 78, 0, 78}, + {57600, 52, 0, 52}, + {115200, 26, 0, 26}, + {230400, 13, 0, 13} +}; + +/* + * Lookup table for RX baud rate for 16 bit oversampling + * The divisor value is calculated using the formula + * + * ACLKR = (AUXCLK)/(CLKRDIV * HCLKRDIV) * Oversampling + * + * Where + * CLKRDIV takes values from 1-32 + * HCLKRDIV takes values from 1-4096 + * Here + * AUXCLK = 24MHz + */ +u32 lt_rx_16x_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { +/*BaudRate, Divisor, CLKXDIV, HCLKXDIV */ + {300, 5000, 1, 2500}, + {600, 2500, 0, 2500}, + {1800, 833, 0, 833}, + {2400, 625, 0, 625}, + {4800, 312, 0, 312}, + {7200, 208, 0, 208}, + {9600, 156, 0, 156}, + {14400, 104, 0, 104}, + {19200, 78, 0, 78}, + {38400, 39, 0, 39}, + {57600, 26, 0, 26}, + {115200, 13, 0, 13}, + {230400, 6, 0, 6} +}; + +/* + * McASP configuration routine + */ +void suart_mcasp_config(u32 tx_baud_value, + u32 rx_baud_value, + u32 oversampling, + pruss_suart_iomap *pruss_ioaddr) +{ + omapl_mcasp_regs_ovly mcasp0_regs = + (omapl_mcasp_regs_ovly) pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + + /* reset mcasp */ + __raw_writel(MCASP_SUART_GBLCTL, &mcasp0_regs->GBLCTL); + __raw_writel(MCASP_SUART_RGBLCTL, &mcasp0_regs->RGBLCTL); + __raw_writel(MCASP_SUART_XGBLCTL, &mcasp0_regs->XGBLCTL); + + /* configure receive registers */ + if ((SUART_8X_OVRSMPL == oversampling) || (0 == oversampling)) { + __raw_writel(MCASP_SUART_RMASK_8, &mcasp0_regs->RMASK); + __raw_writel(MCASP_SUART_RFMT_8, &mcasp0_regs->RFMT); + } + if (SUART_16X_OVRSMPL == oversampling) { + __raw_writel(MCASP_SUART_RMASK_16, &mcasp0_regs->RMASK); + __raw_writel(MCASP_SUART_RFMT_16, &mcasp0_regs->RFMT); + + } + + __raw_writel(MCASP_SUART_FSRM, &mcasp0_regs->AFSRCTL); + __raw_writel(MCASP_SUART_CLKRM_CLKRP, &mcasp0_regs->ACLKRCTL); + __raw_writel(MCASP_SUART_HCLKRP, &mcasp0_regs->AHCLKRCTL); + suart_mcasp_rx_baud_set(rx_baud_value, oversampling, pruss_ioaddr); + __raw_writel(MCASP_SUART_RTDMS0, &mcasp0_regs->RTDM); + __raw_writel(MCASP_SUART_RSYNCERR, &mcasp0_regs->RINTCTL); + __raw_writel(MCASP_SUART_RMAX_RPS_256, &mcasp0_regs->RCLKCHK); + + /* configure transmit registers. */ + __raw_writel(MCASP_SUART_XMASK_0_31, &mcasp0_regs->XMASK); + __raw_writel(MCASP_SUART_XBUSEL_XSSZ_16_XPAD_0, &mcasp0_regs->XFMT); + __raw_writel(MCASP_SUART_FSXM, &mcasp0_regs->AFSXCTL); + __raw_writel(MCASP_SUART_CLKXM_ASYNC_CLKXP, &mcasp0_regs->ACLKXCTL); + __raw_writel(MCASP_SUART_HCLKXM, &mcasp0_regs->AHCLKXCTL); + + suart_mcasp_tx_baud_set(tx_baud_value, pruss_ioaddr); + __raw_writel(MCASP_SUART_XTDMS0, &mcasp0_regs->XTDM); + __raw_writel(MCASP_SUART_XSYNCERR, &mcasp0_regs->XINTCTL); + __raw_writel(MCASP_SUART_XMAX_XPS_256, &mcasp0_regs->XCLKCHK); + + /* Serializer as a transmitter */ + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL0); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL1); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL2); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL3); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL4); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL5); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL6); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL7); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL8); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL9); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL10); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL11); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL12); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL13); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL14); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL15); + + /* Configure all AXR[n] as McASP pins */ + + /* + * Setting all TX MCASP AXR[n] Pin mapped to Even Serializer number + * (0,2,4,6,8,10,12,14) to GPIO Mode by default. During setting the + * serializer to TX mode in PRU assembly code, the MCASP AXR[n] Pin + * would get configured to MCASP mode of operation, + * before Actual Data Transfer + */ + + /* Setting all TX Pin to GPIO Mode by default */ + temp_reg = (OMAPL_MCASP_PFUNC_RESETVAL) | + (1 << PRU_SUART0_CONFIG_TX_SER) | (1 << PRU_SUART1_CONFIG_TX_SER) | + (1 << PRU_SUART2_CONFIG_TX_SER) | (1 << PRU_SUART3_CONFIG_TX_SER) | + (1 << PRU_SUART4_CONFIG_TX_SER) | (1 << PRU_SUART5_CONFIG_TX_SER) | + (1 << PRU_SUART6_CONFIG_TX_SER) | (1 << PRU_SUART7_CONFIG_TX_SER); + __raw_writel(temp_reg, &mcasp0_regs->PFUNC); + + __raw_writel(0xFFF, &mcasp0_regs->PDOUT); + + /* config pin function and direction */ + __raw_writel(0x00000000, &mcasp0_regs->PDIR); + temp_reg = + (1 << PRU_SUART0_CONFIG_TX_SER) | (1 << PRU_SUART1_CONFIG_TX_SER) | + (1 << PRU_SUART2_CONFIG_TX_SER) | (1 << PRU_SUART3_CONFIG_TX_SER) | + (1 << PRU_SUART4_CONFIG_TX_SER) | (1 << PRU_SUART5_CONFIG_TX_SER) | + (1 << PRU_SUART6_CONFIG_TX_SER) | (1 << PRU_SUART7_CONFIG_TX_SER) | + (MCASP_PDIR_VAL); + __raw_writel(temp_reg, &mcasp0_regs->PDIR); + + __raw_writel(MCASP_SUART_DIT_DISABLE, &mcasp0_regs->DITCTL); + __raw_writel(MCASP_SUART_LOOPBACK_DISABLE, &mcasp0_regs->DLBCTL); + __raw_writel(MCASP_SUART_AMUTE_DISABLE, &mcasp0_regs->AMUTE); + + __raw_writel(MCASP_SUART_XSTAT, &mcasp0_regs->XSTAT); + __raw_writel(MCASP_SUART_RSTAT, &mcasp0_regs->RSTAT); +} + +void suart_mcasp_tx_serialzier_set(u32 serializer_num, + pruss_suart_iomap *pruss_ioaddr) +{ + omapl_mcasp_regs_ovly mcasp0_regs = + (omapl_mcasp_regs_ovly) pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + temp_reg = mcasp0_regs->PFUNC | (0x1 << serializer_num); + __raw_writel(temp_reg, &mcasp0_regs->PFUNC); +} + +/* + * mcasp TX buard rate setting routine + */ +s16 suart_mcasp_tx_baud_set(u32 tx_baud_value, + pruss_suart_iomap *pruss_ioaddr) +{ + u32 clk_div_val; + u32 loop_cnt; + s16 status = SUART_SUCCESS; + s16 found_val = SUART_FALSE; + + omapl_mcasp_regs_ovly mcasp0_regs = + (omapl_mcasp_regs_ovly) pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + + /* Search the supported baud rate in the table */ + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (tx_baud_value == lt_tx_baud_rate[loop_cnt][0]) { + found_val = SUART_TRUE; + break; + } + } + if (found_val == SUART_TRUE) { + clk_div_val = lt_tx_baud_rate[loop_cnt][2]; + temp_reg = mcasp0_regs->ACLKXCTL | + clk_div_val << OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT; + __raw_writel(temp_reg, &mcasp0_regs->ACLKXCTL); + clk_div_val = lt_tx_baud_rate[loop_cnt][3]; + temp_reg = mcasp0_regs->AHCLKXCTL | + clk_div_val << OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT; + __raw_writel(temp_reg, &mcasp0_regs->AHCLKXCTL); + } else { + return SUART_INVALID_TX_BAUD; + } + return status; +} + +/* + * mcasp RX buard rate setting routine + */ +s16 suart_mcasp_rx_baud_set(u32 rx_baud_value, + u32 oversampling, pruss_suart_iomap *pruss_ioaddr) +{ + u32 clk_div_val; + u32 loop_cnt; + s16 status = SUART_SUCCESS; + s16 found_val = SUART_FALSE; + + omapl_mcasp_regs_ovly mcasp0_regs = + (omapl_mcasp_regs_ovly) pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + + if (oversampling == SUART_8X_OVRSMPL) { + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (rx_baud_value == lt_rx_8x_baud_rate[loop_cnt][0]) { + clk_div_val = lt_rx_8x_baud_rate[loop_cnt][2]; + temp_reg = mcasp0_regs->ACLKRCTL | (clk_div_val + << OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + + __raw_writel(temp_reg, &mcasp0_regs->ACLKRCTL); + + clk_div_val = + lt_rx_8x_baud_rate[loop_cnt][3] - 1; + + temp_reg = mcasp0_regs->AHCLKRCTL | (clk_div_val + << OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + + __raw_writel(temp_reg, &mcasp0_regs->AHCLKRCTL); + + found_val = SUART_TRUE; + break; + } + } + } else if (oversampling == SUART_16X_OVRSMPL) { + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (rx_baud_value == lt_rx_16x_baud_rate[loop_cnt][0]) { + clk_div_val = lt_rx_16x_baud_rate[loop_cnt][2]; + temp_reg = + mcasp0_regs->ACLKRCTL | (clk_div_val << + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + __raw_writel(temp_reg, &mcasp0_regs->ACLKRCTL); + clk_div_val = lt_rx_16x_baud_rate[loop_cnt][3]; + temp_reg = + mcasp0_regs->AHCLKRCTL | (clk_div_val << + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + __raw_writel(temp_reg, &mcasp0_regs->AHCLKRCTL); + found_val = SUART_TRUE; + break; + } + } + } else if (oversampling == 0) { + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (rx_baud_value == lt_tx_baud_rate[loop_cnt][0]) { + clk_div_val = lt_tx_baud_rate[loop_cnt][2]; + temp_reg = + mcasp0_regs->ACLKRCTL | (clk_div_val << + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + __raw_writel(temp_reg, &mcasp0_regs->ACLKRCTL); + clk_div_val = lt_tx_baud_rate[loop_cnt][3]; + temp_reg = + mcasp0_regs->AHCLKRCTL | (clk_div_val << + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + __raw_writel(temp_reg, &mcasp0_regs->AHCLKRCTL); + found_val = SUART_TRUE; + break; + } + } + } else { + return SUART_INVALID_OVERSAMPLING; + } + + if (found_val != SUART_TRUE) + return SUART_INVALID_RX_BAUD; + + return status; +} + +/* + * mcasp buard rate setting routine + */ +s16 suart_asp_baud_set(u32 tx_baud_value, u32 rx_baud_value, u32 oversampling, + pruss_suart_iomap *pruss_ioaddr) +{ + s16 status = SUART_SUCCESS; + + status = suart_mcasp_tx_baud_set(tx_baud_value, pruss_ioaddr); + status = suart_mcasp_rx_baud_set(rx_baud_value, oversampling, + pruss_ioaddr); + + return status; +} + +/* + * mcasp deactivate the selected serializer + */ +s16 suart_asp_serializer_deactivate(u16 u16sr_num, + pruss_suart_iomap *pruss_ioaddr) +{ + s16 status = SUART_SUCCESS; + omapl_mcasp_regs_ovly mcasp0_regs = (omapl_mcasp_regs_ovly) + pruss_ioaddr->mcasp_io_addr; + if (u16sr_num > 15) + status = SUART_INVALID_SR_NUM; + else + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL0); + + return status; +} diff --git a/drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h b/drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h new file mode 100644 index 0000000..7839eb6 --- /dev/null +++ b/drivers/tty/serial/da8xx_pruss/pruss_suart_utils.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: jitendra at 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 _SUART_UTILS_H_ +#define _SUART_UTILS_H_ + +#include + +/* ************ Serializers ***************** */ +#define PRU_SUART_SERIALIZER_0 (0u) +#define PRU_SUART_SERIALIZER_1 (1u) +#define PRU_SUART_SERIALIZER_2 (2u) +#define PRU_SUART_SERIALIZER_3 (3u) +#define PRU_SUART_SERIALIZER_4 (4u) +#define PRU_SUART_SERIALIZER_5 (5u) +#define PRU_SUART_SERIALIZER_6 (6u) +#define PRU_SUART_SERIALIZER_7 (7u) +#define PRU_SUART_SERIALIZER_8 (8u) +#define PRU_SUART_SERIALIZER_9 (9u) +#define PRU_SUART_SERIALIZER_10 (10u) +#define PRU_SUART_SERIALIZER_11 (11u) +#define PRU_SUART_SERIALIZER_12 (12u) +#define PRU_SUART_SERIALIZER_13 (13u) +#define PRU_SUART_SERIALIZER_14 (14u) +#define PRU_SUART_SERIALIZER_15 (15u) +#define PRU_SUART_SERIALIZER_NONE (16u) + +/* Total number of baud rates supported */ +#define SUART_NUM_OF_BAUDS_SUPPORTED 13 + +#define MCASP_PDIR_VAL ( \ + OMAPL_MCASP_PDIR_AFSR_OUTPUT< -- 1.7.2.3 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From subhasish at mistralsolutions.com Fri Jul 8 13:26:26 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 18:26:26 -0000 Subject: [PATCH v4 08/11] tty: add pruss SUART driver In-Reply-To: <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> References: <1303474109-6212-1-git-send-email-subhasish@mistralsolutions.com> <1303474109-6212-9-git-send-email-subhasish@mistralsolutions.com> Message-ID: <034A6447E0D54737B5CEE009A4D20B37@subhasishg> Hi Greg, I am observing a RX FIFO sized data loss problem with my driver. I observed that if I start the TX before the RX, then there are no errors, but, if I start a TX after a RX, then I observe the error (FIFO sized data chunks missing). Say, for example, if I use all three UARTS and on all three I start RX, then for every TX started I would observe one error on all the RX data buffer. So, if I start only two TX, then I will see only two errors on all the three RX UARTS. >From this observation I concluded that somehow the TX start was effecting the RX. So, I disabled the complete TX section in the driver, but even when the complete TX was disabled I observed the same errors in RX. >From here I concluded that the error was happening before my drivers TX routine was getting called. I traced it down to the below function, this is a sub-system specific function in the file serial-core.c static int uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = state->uart_port; int mctrl; spin_lock_irq(&uport->lock); uport->ops->enable_ms(uport); mctrl = uport->ops->get_mctrl(uport); spin_unlock_irq(&uport->lock); if (mctrl & TIOCM_CAR) { return 1; } return 0; } In this function I moved the "return 1" to the beginning of the function, this solved the bug that we are having. There was no data loss. I think, the two spin locks used in this function is somehow effecting the RX. I then modified this function to as follows and the error is not observed anymore. static int uart_carrier_raised(struct tty_port *port) { struct uart_state *state = container_of(port, struct uart_state, port); struct uart_port *uport = state->uart_port; int mctrl; unsigned long flags = 0; spin_lock_irqsave(&uport->lock, flags); uport->ops->enable_ms(uport); mctrl = uport->ops->get_mctrl(uport); spin_unlock_irqrestore(&uport->lock, flags); if (mctrl & TIOCM_CAR) { return 1; } return 0; } Is this a BUG in the TTY sub-system or am I doing something wrong in my driver. I also changed one driver function to as follows: static u32 pruss_suart_get_mctrl(struct uart_port *port) { return TIOCM_CAR; } Best Regards, Subhasish Ghosh -------------------------------------------------- From: "Subhasish Ghosh" Sent: Friday, April 22, 2011 5:38 PM To: Cc: ; ; ; ; "Subhasish Ghosh" ; "Greg Kroah-Hartman (maintainer:TTY LAYER,commit_signer:2/4=50%,commit_signer:1/2=50%)" ; "Andrew Morton (commit_signer:1/4=25%)" ; "Randy Dunlap (commit_signer:1/4=25%)" ; "open list" Subject: [PATCH v4 08/11] tty: add pruss SUART driver > This patch adds support for the TTY compliant > Soft-UART device emulated on PRUSS. > > This patch depends on: > davinci: macro rename DA8XX_LPSC0_DMAX to DA8XX_LPSC0_PRUSS. > https://patchwork.kernel.org/patch/615681/ > davinci: changed SRAM allocator to shared ram. > https://patchwork.kernel.org/patch/549351/ > > Signed-off-by: Subhasish Ghosh > --- > drivers/tty/serial/Kconfig | 18 + > drivers/tty/serial/Makefile | 6 + > drivers/tty/serial/pruss_suart.c | 1061 ++++++++++++++++++++ > drivers/tty/serial/pruss_suart.h | 1038 +++++++++++++++++++ > drivers/tty/serial/pruss_suart_api.c | 1710 > ++++++++++++++++++++++++++++++++ > drivers/tty/serial/pruss_suart_utils.c | 393 ++++++++ > include/linux/serial_core.h | 2 + > 7 files changed, 4228 insertions(+), 0 deletions(-) > create mode 100644 drivers/tty/serial/pruss_suart.c > create mode 100644 drivers/tty/serial/pruss_suart.h > create mode 100644 drivers/tty/serial/pruss_suart_api.c > create mode 100644 drivers/tty/serial/pruss_suart_utils.c > > diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig > index 2b83346..6c26ebf 100644 > --- a/drivers/tty/serial/Kconfig > +++ b/drivers/tty/serial/Kconfig > @@ -1596,4 +1596,22 @@ config SERIAL_PCH_UART > This driver is for PCH(Platform controller Hub) UART of Intel EG20T > which is an IOH(Input/Output Hub) for x86 embedded processor. > Enabling PCH_DMA, this PCH UART works as DMA mode. > + > +config SERIAL_PRUSS_SUART > + depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850 > + select SERIAL_CORE > + tristate "PRUSS based SoftUART emulation on DA8XX" > + ---help--- > + This driver emulates up to eight different UARTs on the PRUSS. > + You may modify the NR_SUARTS macro in the driver to emulate > + less number of UARTS as per your requirement. > + If not sure, mark No > + > +config PRUSS_SUART_MCASP > + depends on ARCH_DAVINCI_DA830 && SERIAL_PRUSS_SUART > + default "0" > + int "McASP number" > + ---help--- > + Enter the McASP number to use with SUART (0, 1 or 2). > + You will need to recompile the kernel if this is changed. > endmenu > diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile > index 8ea92e9..e1eaaf3 100644 > --- a/drivers/tty/serial/Makefile > +++ b/drivers/tty/serial/Makefile > @@ -92,3 +92,9 @@ obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o > obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o > obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o > obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o > + > +pruss_uart-objs := pruss_suart.o \ > + pruss_suart_api.o \ > + pruss_suart_utils.o > + > +obj-$(CONFIG_SERIAL_PRUSS_SUART) += pruss_uart.o > diff --git a/drivers/tty/serial/pruss_suart.c > b/drivers/tty/serial/pruss_suart.c > new file mode 100644 > index 0000000..37c3c21 > --- /dev/null > +++ b/drivers/tty/serial/pruss_suart.c > @@ -0,0 +1,1061 @@ > +/* > + * PRUSS SUART Emulation device driver > + * Author: subhasish at mistralsolutions.com > + * > + * This driver supports TI's PRU SUART Emulation and the > + * specs for the same is available at > + * > + * Copyright (C) 2010, 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 > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include "pruss_suart.h" > + > +#define NR_SUART 8 > +#define DRV_NAME "da8xx_pruss_uart" > +#define DRV_DESC "PRUSS SUART Driver v1.0" > +#define MAX_SUART_RETRIES 100 > +#define SUART_CNTX_SZ 512 > +#define SUART_FIFO_TIMEOUT_DFLT 5 > +#define SUART_FIFO_TIMEOUT_MIN 4 > +#define SUART_FIFO_TIMEOUT_MAX 500 > + > +/* Default timeout set to 5ms */ > +static s16 suart_timeout = SUART_FIFO_TIMEOUT_DFLT; > +module_param(suart_timeout, short, S_IRUGO); > +MODULE_PARM_DESC(suart_timeout, > + "fifo timeout in milli seconds (min: 4; max: 500)"); > + > +struct suart_fifo { > + void *fifo_vaddr_buff_tx; > + void *fifo_vaddr_buff_rx; > + void *fifo_phys_addr_tx; > + void *fifo_phys_addr_rx; > +}; > + > +struct omapl_pru_suart { > + struct uart_port port[NR_SUART]; > + struct device *dev; > + unsigned long tx_empty[NR_SUART]; > + struct clk *clk_mcasp; > + struct suart_fifo suart_fifo_addr[NR_SUART]; > + struct suart_handle suart_hdl[NR_SUART]; > + struct pruss_suart_iomap suart_iomap; > + struct tasklet_struct tx_task[NR_SUART]; > + u32 clk_freq_pru; > + u32 clk_freq_mcasp; > + u32 tx_loadsz; > +}; > + > +static u32 suart_get_duplex(struct omapl_pru_suart *soft_uart, u32 > uart_no) > +{ > + return soft_uart->suart_hdl[uart_no].uart_type; > +} > + > +static inline void __stop_tx(struct omapl_pru_suart *soft_uart, u32 > uart_no) > +{ > + struct device *dev = soft_uart->dev; > + unsigned long flags = 0; > + struct uart_port *port = &soft_uart->port[uart_no]; > + u16 txready; > + u32 i; > + > + /* Check if any TX in progress */ > + for (i = 0, txready = 1; (i < 10000) && txready; i++) { > + txready = (pru_softuart_get_tx_status > + (dev, &soft_uart->suart_hdl[uart_no]) & > + CHN_TXRX_STATUS_RDY); > + } > + /* To stop tx, disable the TX interrupt */ > + spin_lock_irqsave(&port->lock, flags); > + suart_intr_clrmask(dev, soft_uart->suart_hdl[uart_no].uart_num, > + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); > + pru_softuart_clr_tx_status(dev, &soft_uart->suart_hdl[uart_no]); > + spin_unlock_irqrestore(&port->lock, flags); > +} > + > +static void pruss_suart_stop_tx(struct uart_port *port) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + > + __stop_tx(soft_uart, port->line); > +} > + > +static void omapl_pru_tx_chars(struct omapl_pru_suart *soft_uart, u32 > uart_no) > +{ > + struct circ_buf *xmit = &soft_uart->port[uart_no].state->xmit; > + struct device *dev = soft_uart->dev; > + s32 count = 0; > + > + if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_TX)) > + return; > + > + if (uart_circ_empty(xmit) || > + uart_tx_stopped(&soft_uart->port[uart_no])) { > + pruss_suart_stop_tx(&soft_uart->port[uart_no]); > + set_bit(0, &soft_uart->tx_empty[uart_no]); > + return; > + } > + > + for (count = 0; count <= soft_uart->tx_loadsz; count++) { > + *((s8 *)soft_uart->suart_fifo_addr[uart_no].fifo_vaddr_buff_tx > + + count) = xmit->buf[xmit->tail]; > + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); > + soft_uart->port[uart_no].icount.tx++; > + if (uart_circ_empty(xmit)) { > + uart_circ_clear(xmit); > + break; > + } > + } > + > + if (count == (SUART_FIFO_LEN + 1)) > + count = SUART_FIFO_LEN; > + > + /* Write the character to the data port */ > + if (pru_softuart_write(dev, > + &soft_uart->suart_hdl[uart_no], > + (u32 *)&soft_uart->suart_fifo_addr > + [uart_no].fifo_phys_addr_tx, count) != 0) { > + dev_err(dev, "failed to tx data\n"); > + } > + > + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) > + uart_write_wakeup(&soft_uart->port[uart_no]); > + > +#if 0 > + if (uart_circ_empty(xmit)) > + __stop_tx(soft_uart, uart_no); > +#endif > +} > + > +static void suart_tx_task(unsigned long data) > +{ > + struct uart_port *port = (struct uart_port *)data; > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + > + omapl_pru_tx_chars(soft_uart, port->line); > +} > + > +static void omapl_pru_rx_chars(struct omapl_pru_suart *soft_uart, u32 > uart_no) > +{ > + struct tty_struct *tty = NULL; > + struct device *dev = soft_uart->dev; > + s8 flags = TTY_NORMAL; > + u16 rx_status, data_len = SUART_FIFO_LEN; > + u32 data_len_read; > + u8 suart_data[SUART_FIFO_LEN + 1]; > + s32 i = 0; > + > + if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_RX)) > + return; > + > + /* read the status */ > + rx_status = pru_softuart_get_rx_status(dev, > + &soft_uart->suart_hdl[uart_no]); > + > + pru_softuart_read_data(dev, &soft_uart->suart_hdl[uart_no], > + suart_data, data_len + 1, &data_len_read); > + > + tty = tty_port_tty_get(&soft_uart->port[uart_no].state->port); > + > + if (!tty) > + return; > + > + /* check for errors */ > + if (rx_status & CHN_TXRX_STATUS_ERR) { > + if (rx_status & CHN_TXRX_STATUS_FE) > + soft_uart->port[uart_no].icount.frame++; > + if (rx_status & CHN_TXRX_STATUS_OVRNERR) > + soft_uart->port[uart_no].icount.overrun++; > + if (rx_status & CHN_TXRX_STATUS_BI) > + soft_uart->port[uart_no].icount.brk++; > + rx_status &= soft_uart->port[uart_no]. > + read_status_mask; > + if (rx_status & CHN_TXRX_STATUS_FE) > + flags = TTY_FRAME; > + if (rx_status & CHN_TXRX_STATUS_OVRNERR) > + flags = TTY_OVERRUN; > + if (rx_status & CHN_TXRX_STATUS_BI) > + flags = TTY_BREAK; > + > +#ifdef SUPPORT_SYSRQ > + soft_uart->port[uart_no].sysrq = 0; > +#endif > + } else { > + for (i = 0; i <= data_len_read; i++) { > + soft_uart->port[uart_no].icount.rx++; > + /* check for sys rq */ > + if (uart_handle_sysrq_char > + (&soft_uart->port[uart_no], suart_data)) > + continue; > + } > + tty_insert_flip_string(tty, suart_data, data_len_read); > + } > + > + /* push data into tty */ > + pru_softuart_clr_rx_status(dev, &soft_uart->suart_hdl[uart_no]); > + tty_flip_buffer_push(tty); > + tty_kref_put(tty); > +} > + > +static irqreturn_t pruss_suart_interrupt(s32 irq, void *dev_id) > +{ > + struct uart_port *port = dev_id; > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + struct device *dev = soft_uart->dev; > + u16 txrx_flag; > + u32 ret; > + unsigned long flags = 0; > + u16 uart_num = port->line + 1; > + > + spin_lock_irqsave(&port->lock, flags); > + > + do { > + ret = pru_softuart_get_isrstatus(dev, uart_num, &txrx_flag); > + if (ret != 0) { > + dev_err(dev, "suart%d: failed to get interrupt, ret:" > + " 0x%X txrx_flag 0x%X\n", > + port->line, ret, txrx_flag); > + spin_unlock_irqrestore(&port->lock, flags); > + return IRQ_NONE; > + } > + if ((PRU_RX_INTR & txrx_flag) == PRU_RX_INTR) { > + pru_intr_clr_isrstatus(dev, uart_num, PRU_RX_INTR); > + if ((soft_uart->port[port->line].ignore_status_mask & > + CHN_TXRX_STATUS_RDY) == CHN_TXRX_STATUS_RDY) { > + pru_softuart_clr_rx_status(dev, > + &soft_uart->suart_hdl > + [port->line]); > + } else { > + omapl_pru_rx_chars(soft_uart, port->line); > + } > + } > + > + if ((PRU_TX_INTR & txrx_flag) == PRU_TX_INTR) { > + pru_intr_clr_isrstatus(dev, uart_num, PRU_TX_INTR); > + pru_softuart_clr_tx_status(dev, &soft_uart->suart_hdl > + [port->line]); > + tasklet_schedule(&soft_uart->tx_task[port->line]); > + } > + } while (txrx_flag & (PRU_RX_INTR | PRU_TX_INTR)); > + > + spin_unlock_irqrestore(&port->lock, flags); > + return IRQ_HANDLED; > +} > + > +static void pruss_suart_stop_rx(struct uart_port *port) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + struct device *dev = soft_uart->dev; > + unsigned long flags = 0; > + > + spin_lock_irqsave(&port->lock, flags); > + /* disable rx interrupt */ > + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, > + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI > + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT > + | CHN_TXRX_IE_MASK_TIMEOUT); > + spin_unlock_irqrestore(&port->lock, flags); > +} > + > +static void pruss_suart_enable_ms(struct uart_port *port) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + struct device *dev = soft_uart->dev; > + dev_err(dev, "modem control timer not supported\n"); > +} > + > +static void pruss_suart_start_tx(struct uart_port *port) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + struct device *dev = soft_uart->dev; > + unsigned long flags = 0; > + > + /* unmask the tx interrupts */ > + spin_lock_irqsave(&port->lock, flags); > + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, > + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); > + spin_unlock_irqrestore(&port->lock, flags); > + > + if (test_and_clear_bit(0, &soft_uart->tx_empty[port->line])) > + omapl_pru_tx_chars(soft_uart, port->line); > +} > + > +static u32 pruss_suart_tx_empty(struct uart_port *port) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + struct device *dev = soft_uart->dev; > + > + return (pru_softuart_get_tx_status(dev, > + &soft_uart->suart_hdl[port->line]) > + & CHN_TXRX_STATUS_RDY) ? 0 : TIOCSER_TEMT; > +} > + > +static u32 pruss_suart_get_mctrl(struct uart_port *port) > +{ > + return -ENOTSUPP; > +} > + > +static void pruss_suart_set_mctrl(struct uart_port *port, u32 mctrl) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + struct device *dev = soft_uart->dev; > + dev_dbg(dev, "modem control not supported\n"); > +} > + > +static void pruss_suart_break_ctl(struct uart_port *port, s32 > break_state) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + struct device *dev = soft_uart->dev; > + unsigned long flags = 0; > + > + spin_lock_irqsave(&port->lock, flags); > + > + if (break_state == -1) > + suart_intr_clrmask(dev, > + soft_uart->suart_hdl[port->line].uart_num, > + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); > + else > + suart_intr_setmask(dev, > + soft_uart->suart_hdl[port->line].uart_num, > + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); > + > + spin_unlock_irqrestore(&port->lock, flags); > +} > + > +static void pruss_suart_set_termios(struct uart_port *port, > + struct ktermios *termios, > + struct ktermios *old) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + struct device *dev = soft_uart->dev; > + u8 cval = 0; > + unsigned long flags = 0; > + u32 baud = 0; > + u32 old_csize = old ? old->c_cflag & CSIZE : CS8; > + > +/* > + * Do not allow unsupported configurations to be set > + */ > + if (1) { > + termios->c_cflag &= ~(CRTSCTS | CMSPAR | CSTOPB > + | PARENB | PARODD | CMSPAR); > + } > + > + switch (termios->c_cflag & CSIZE) { > + case CS6: > + cval = ePRU_SUART_DATA_BITS6; > + break; > + case CS7: > + cval = ePRU_SUART_DATA_BITS7; > + break; > + default: > + case CS8: > + cval = ePRU_SUART_DATA_BITS8; > + break; > + } > + /* > + * We do not support CS5. > + */ > + if ((termios->c_cflag & CSIZE) == CS5) { > + termios->c_cflag &= ~CSIZE; > + termios->c_cflag |= old_csize; > + } > + if (pru_softuart_setdatabits > + (dev, &soft_uart->suart_hdl[port->line], cval, cval) != 0) > + dev_err(dev, "failed to set data bits to: %d\n", cval); > + > +/* > + * Ask the core to calculate the divisor for us. > + */ > + baud = uart_get_baud_rate(port, termios, old, > + port->uartclk / 16 / 0xffff, > + port->uartclk / 16); > + > +/* > + * Ok, we're now changing the port state. Do it with > + * interrupts disabled. > + */ > + spin_lock_irqsave(&port->lock, flags); > + > + /* Set the baud */ > + if (pru_softuart_setbaud(dev, &soft_uart->suart_hdl[port->line], > + SUART_DEFAULT_BAUD / baud, > + SUART_DEFAULT_BAUD / baud) != 0) > + dev_err(dev, "failed to set baud to: %d\n", baud); > + > +/* > + * update port->read_config_mask and port->ignore_config_mask > + * to indicate the events we are interested in receiving > + */ > + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, > + PRU_RX_INTR, SUART_GBL_INTR_ERR_MASK); > + port->read_status_mask = 0; > + if (termios->c_iflag & INPCK) { /* Input parity check not supported, > + just enabled FE */ > + port->read_status_mask |= CHN_TXRX_STATUS_FE; > + suart_intr_setmask(dev, > + soft_uart->suart_hdl[port->line].uart_num, > + PRU_RX_INTR, CHN_TXRX_IE_MASK_FE); > + } > + if (termios->c_iflag & (BRKINT | PARMRK)) { > + port->read_status_mask |= CHN_TXRX_STATUS_BI; > + suart_intr_setmask(dev, > + soft_uart->suart_hdl[port->line].uart_num, > + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); > + } > +/* > + * Characters to ignore > + */ > + port->ignore_status_mask = 0; > + if (termios->c_iflag & IGNBRK) { > + port->ignore_status_mask |= CHN_TXRX_STATUS_BI; > + /* > + * If we're ignoring break indicators, > + * ignore overruns too (for real raw support). > + */ > + if (termios->c_iflag & IGNPAR) { > + port->ignore_status_mask |= > + (CHN_TXRX_STATUS_OVRNERR | CHN_TXRX_STATUS_FE); > + /* > + * Overrun in case of RX > + * Underrun in case of TX > + */ > + suart_intr_clrmask(dev, soft_uart-> > + suart_hdl[port->line].uart_num, > + PRU_RX_INTR, CHN_TXRX_IE_MASK_FE); > + } > + suart_intr_clrmask(dev, > + soft_uart->suart_hdl[port->line].uart_num, > + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); > + } > +/* > + * ignore all characters if CREAD is not set > + */ > + if ((termios->c_cflag & CREAD) == 0) { > + port->ignore_status_mask |= CHN_TXRX_STATUS_RDY; > + pruss_suart_stop_rx(port); > + } > + /* > + * update the per port timeout > + */ > + uart_update_timeout(port, termios->c_cflag, baud); > + > + spin_unlock_irqrestore(&port->lock, flags); > + > + /* Don't rewrite B0 */ > + if (tty_termios_baud_rate(termios)) > + tty_termios_encode_baud_rate(termios, baud, baud); > +} > + > +/* > + * Grab any interrupt resources and initialise any low level driver > + * state. Enable the port for reception. It should not activate > + * RTS nor DTR; this will be done via a separate call to set_mctrl. > + * > + * This method will only be called when the port is initially opened. > + * > + * Locking: port_sem taken. > + * Interrupts: globally disabled. > + */ > +static s32 pruss_suart_startup(struct uart_port *port) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + struct device *dev = soft_uart->dev; > + unsigned long flags = 0; > + s32 retval; > + > + /* > + * Disable interrupts from this port > + */ > + spin_lock_irqsave(&port->lock, flags); > + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, > + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); > + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, > + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI > + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT > + | CHN_TXRX_IE_MASK_TIMEOUT); > + spin_unlock_irqrestore(&port->lock, flags); > + > + retval = request_irq(port->irq, pruss_suart_interrupt, > + port->irqflags, "suart_irq", port); > + if (retval) { > + free_irq(port->irq, port); /* should we free this if err */ > + goto out; > + } > + /* > + * enable interrupts from this port > + */ > + spin_lock_irqsave(&port->lock, flags); > + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, > + PRU_RX_INTR, SUART_GBL_INTR_ERR_MASK); > + > + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, > + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI > + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT > + | CHN_TXRX_IE_MASK_TIMEOUT); > + > + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, > + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); > + spin_unlock_irqrestore(&port->lock, flags); > + > + if ((suart_get_duplex(soft_uart, port->line) & ePRU_SUART_HALF_TX) > + == ePRU_SUART_HALF_TX) { > + suart_pru_to_host_intr_enable(dev, soft_uart-> > + suart_hdl[port->line].uart_num, PRU_TX_INTR, true); > + } > + /* Seed RX if port is half-rx or full-duplex */ > + if ((suart_get_duplex(soft_uart, port->line) & ePRU_SUART_HALF_RX) > + == ePRU_SUART_HALF_RX) { > + suart_pru_to_host_intr_enable(dev, soft_uart-> > + suart_hdl[port->line].uart_num, PRU_RX_INTR, true); > + pru_softuart_read(dev, &soft_uart->suart_hdl[port->line], > + (u32 *)&soft_uart->suart_fifo_addr[port->line]. > + fifo_phys_addr_rx, SUART_FIFO_LEN); > + } > +out: > + return retval; > +} > + > +/* > + * Disable the port, disable any break condition that may be in > + * effect, and free any interrupt resources. It should not disable > + * RTS nor DTR; this will have already been done via a separate > + * call to set_mctrl. > + * > + * Drivers must not access port->info once this call has completed. > + * > + * This method will only be called when there are no more users of > + * this port. > + * > + * Locking: port_sem taken. > + * Interrupts: caller dependent. > + */ > + > +static void pruss_suart_shutdown(struct uart_port *port) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + struct device *dev = soft_uart->dev; > + unsigned long flags = 0; > + > + /* > + * Disable interrupts from this port > + */ > + /* Disable BI and FE intr */ > + spin_lock_irqsave(&port->lock, flags); > + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, > + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); > + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, > + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI > + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT > + | CHN_TXRX_IE_MASK_TIMEOUT); > + spin_unlock_irqrestore(&port->lock, flags); > + > + /* free interrupts */ > + free_irq(port->irq, port); > +} > + > +/* > + * Return a pointer to a string constant describing the specified > + * port, or return NULL, in which case the string 'unknown' is > + * substituted. > + * > + * Locking: none. > + * Interrupts: caller dependent. > + */ > + > +static const char *pruss_suart_type(struct uart_port *port) > +{ > + return "suart_tty"; > +} > + > +/* > + * Release any memory and IO region resources currently in use by > + * the port. > + * > + * Locking: none. > + * Interrupts: caller dependent. > + */ > + > +static void pruss_suart_release_port(struct uart_port *port) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + struct platform_device *pdev = to_platform_device(port->dev); > + > + if (0 != pru_softuart_close(&soft_uart->suart_hdl[port->line])) > + dev_err(&pdev->dev, "failed to close suart\n"); > + > + return; > +} > + > +/* > + * Request any memory and IO region resources required by the port. > + * If any fail, no resources should be registered when this function > + * returns, and it should return -EBUSY on failure. > + * > + * Locking: none. > + * Interrupts: caller dependent. > + * > + * We need to d/l the f/w in probe and since this api > + * is called per uart, the request_mem_region should > + * be called in probe itself. > + */ > +static s32 pruss_suart_request_port(struct uart_port *port) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + struct platform_device *pdev = to_platform_device(port->dev); > + struct device *dev = soft_uart->dev; > + struct suart_config pru_suart_config; > + s16 timeout = 0; > + u32 err = 0; > + > + if (soft_uart == NULL) { > + dev_err(&pdev->dev, "soft_uart ptr failed\n"); > + return -ENODEV; > + } > + err = pru_softuart_open(&soft_uart->suart_hdl[port->line]); > + if (err != 0) { > + dev_err(&pdev->dev, "failed to open suart: %d\n", err); > + err = -ENODEV; > + goto exit; > + } > + set_bit(0, &soft_uart->tx_empty[port->line]); > + > + /* set fifo /timeout */ > + if (SUART_FIFO_TIMEOUT_MIN > suart_timeout) { > + dev_err(&pdev->dev, "fifo timeout less than %d ms not supported\n", > + SUART_FIFO_TIMEOUT_MIN); > + suart_timeout = SUART_FIFO_TIMEOUT_MIN; > + } else if (SUART_FIFO_TIMEOUT_MAX < suart_timeout) { > + dev_err(&pdev->dev, "fifo timeout more than %d ms not supported\n", > + SUART_FIFO_TIMEOUT_MAX); > + suart_timeout = SUART_FIFO_TIMEOUT_MAX; > + } > + > + /* This is only for x8 */ > + timeout = (SUART_DEFAULT_BAUD * suart_timeout) / 1000; > + pru_set_fifo_timeout(dev, timeout); > + > + if (soft_uart->suart_hdl[port->line].uart_num == PRU_SUART_UART1) { > + pru_suart_config.tx_serializer = PRU_SUART0_CONFIG_TX_SER; > + pru_suart_config.rx_serializer = PRU_SUART0_CONFIG_RX_SER; > + } else if (soft_uart->suart_hdl[port->line].uart_num == > + PRU_SUART_UART2) { > + pru_suart_config.tx_serializer = PRU_SUART1_CONFIG_TX_SER; > + pru_suart_config.rx_serializer = PRU_SUART1_CONFIG_RX_SER; > + } else if (soft_uart->suart_hdl[port->line].uart_num == > + PRU_SUART_UART3) { > + pru_suart_config.tx_serializer = PRU_SUART2_CONFIG_TX_SER; > + pru_suart_config.rx_serializer = PRU_SUART2_CONFIG_RX_SER; > + } else if (soft_uart->suart_hdl[port->line].uart_num == > + PRU_SUART_UART4) { > + pru_suart_config.tx_serializer = PRU_SUART3_CONFIG_TX_SER; > + pru_suart_config.rx_serializer = PRU_SUART3_CONFIG_RX_SER; > + } else if (soft_uart->suart_hdl[port->line].uart_num == > + PRU_SUART_UART5) { > + pru_suart_config.tx_serializer = PRU_SUART4_CONFIG_TX_SER; > + pru_suart_config.rx_serializer = PRU_SUART4_CONFIG_RX_SER; > + } else if (soft_uart->suart_hdl[port->line].uart_num == > + PRU_SUART_UART6) { > + pru_suart_config.tx_serializer = PRU_SUART5_CONFIG_TX_SER; > + pru_suart_config.rx_serializer = PRU_SUART5_CONFIG_RX_SER; > + } else if (soft_uart->suart_hdl[port->line].uart_num == > + PRU_SUART_UART7) { > + pru_suart_config.tx_serializer = PRU_SUART6_CONFIG_TX_SER; > + pru_suart_config.rx_serializer = PRU_SUART6_CONFIG_RX_SER; > + } else if (soft_uart->suart_hdl[port->line].uart_num == > + PRU_SUART_UART8) { > + pru_suart_config.tx_serializer = PRU_SUART7_CONFIG_TX_SER; > + pru_suart_config.rx_serializer = PRU_SUART7_CONFIG_RX_SER; > + } else { > + return -ENOTSUPP; > + } > + > + /* Some defaults to startup. reconfigured by terimos later */ > + pru_suart_config.tx_clk_divisor = 1; > + pru_suart_config.rx_clk_divisor = 1; > + pru_suart_config.tx_bits_per_char = ePRU_SUART_DATA_BITS8; > + pru_suart_config.rx_bits_per_char = ePRU_SUART_DATA_BITS8; > + pru_suart_config.oversampling = SUART_DEFAULT_OVRSMPL; > + > + if (pru_softuart_setconfig(dev, &soft_uart->suart_hdl[port->line], > + &pru_suart_config) != 0) { > + dev_err(&pdev->dev, > + "pru_softuart_setconfig: failed to set config: %X\n", > + err); > + } > +exit: > + return err; > +} > + > +/* > + * Perform any autoconfiguration steps required for the port. `flag` > + * contains a bit mask of the required configuration. UART_CONFIG_TYPE > + * indicates that the port requires detection and identification. > + * port->type should be set to the type found, or PORT_UNKNOWN if > + * no port was detected. > + * > + * UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal, > + * which should be probed using standard kernel autoprobing techniques. > + * This is not necessary on platforms where ports have interrupts > + * internally hard wired (eg, system on a chip implementations). > + * > + * Locking: none. > + * Interrupts: caller dependent. > + */ > + > +static void pruss_suart_config_port(struct uart_port *port, s32 flags) > +{ > + if (flags & UART_CONFIG_TYPE && pruss_suart_request_port(port) == 0) > + port->type = PORT_DA8XX_PRU_SUART; > +} > + > +/* > + * Verify the new serial port information contained within serinfo is > + * suitable for this port type. > + * > + * Locking: none. > + * Interrupts: caller dependent. > + */ > +static s32 pruss_suart_verify_port(struct uart_port *port, > + struct serial_struct *ser) > +{ > + struct omapl_pru_suart *soft_uart = > + container_of(port, struct omapl_pru_suart, port[port->line]); > + s32 ret = 0; > + > + if (ser->type != PORT_UNKNOWN && ser->type != PORT_DA8XX_PRU_SUART) > + ret = -EINVAL; > + if (soft_uart->port[port->line].irq != ser->irq) > + ret = -EINVAL; > + if (ser->io_type != UPIO_MEM) > + ret = -EINVAL; > + if (soft_uart->port[port->line].uartclk / 16 != ser->baud_base) > + ret = -EINVAL; > + if ((void *)soft_uart->port[port->line].mapbase != ser->iomem_base) > + ret = -EINVAL; > + if (soft_uart->port[port->line].iobase != ser->port) > + ret = -EINVAL; > + return ret; > +} > + > +static struct uart_ops pruss_suart_ops = { > + .tx_empty = pruss_suart_tx_empty, > + .set_mctrl = pruss_suart_set_mctrl, > + .get_mctrl = pruss_suart_get_mctrl, > + .stop_tx = pruss_suart_stop_tx, > + .start_tx = pruss_suart_start_tx, > + .stop_rx = pruss_suart_stop_rx, > + .enable_ms = pruss_suart_enable_ms, > + .break_ctl = pruss_suart_break_ctl, > + .startup = pruss_suart_startup, > + .shutdown = pruss_suart_shutdown, > + .set_termios = pruss_suart_set_termios, > + .type = pruss_suart_type, > + .release_port = pruss_suart_release_port, > + .request_port = pruss_suart_request_port, > + .config_port = pruss_suart_config_port, > + .verify_port = pruss_suart_verify_port, > +}; > + > +static struct uart_driver pruss_suart_reg = { > + .owner = THIS_MODULE, > + .driver_name = DRV_NAME, > + .dev_name = "ttySU", > + .major = 0, > + .minor = 16, > + .nr = NR_SUART, > +}; > + > +static struct pruss_suart_initparams init_params = { > + .tx_baud_value = SUART_DEFAULT_BAUD, > + .rx_baud_value = SUART_DEFAULT_BAUD, > + .oversampling = SUART_DEFAULT_OVRSMPL, > +}; > + > +static s32 __devinit pruss_suart_probe(struct platform_device *pdev) > +{ > + struct omapl_pru_suart *soft_uart; > + const struct da850_evm_pruss_suart_data *pdata; > + struct device *dev = &pdev->dev; > + struct resource *res; > + struct clk *clk_pruss = NULL; > + const struct firmware *fw; > + s32 err, i; > + > + pdata = dev->platform_data; > + if (!pdata) { > + dev_err(&pdev->dev, "platform data not found\n"); > + return -EINVAL; > + } > + (pdata->setup)(); > + > + soft_uart = kzalloc(sizeof(struct omapl_pru_suart), GFP_KERNEL); > + if (!soft_uart) > + return -ENOMEM; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "failed to get resource"); > + return -ENOMEM; > + } > + > + if (!request_mem_region(res->start, > + resource_size(res), > + dev_name(&pdev->dev))) { > + dev_err(&pdev->dev, "mcasp memory region already claimed!\n"); > + err = -EBUSY; > + goto probe_exit; > + } > + > + soft_uart->suart_iomap.mcasp_io_addr = ioremap(res->start, > + resource_size(res)); > + if (!soft_uart->suart_iomap.mcasp_io_addr) { > + dev_err(&pdev->dev, "mcasp ioremap failed\n"); > + err = -EFAULT; > + goto probe_exit_1; > + } > + > + soft_uart->suart_iomap.p_fifo_buff_virt_base = > + sram_alloc(SUART_CNTX_SZ * NR_SUART * 2, > + (dma_addr_t *) &soft_uart->suart_iomap.p_fifo_buff_phys_base); > + if (!soft_uart->suart_iomap.p_fifo_buff_virt_base) > + goto probe_exit_iounmap; > + > + clk_pruss = clk_get(NULL, "pruss"); > + if (IS_ERR(clk_pruss)) { > + dev_err(&pdev->dev, "no clock available: pruss\n"); > + err = -ENODEV; > + goto probe_exit_iounmap; > + } > + soft_uart->clk_freq_pru = clk_get_rate(clk_pruss); > + clk_put(clk_pruss); > + > + soft_uart->clk_mcasp = clk_get(&pdev->dev, NULL); > + if (IS_ERR(soft_uart->clk_mcasp)) { > + dev_err(&pdev->dev, "no clock available: mcasp\n"); > + err = -ENODEV; > + soft_uart->clk_mcasp = NULL; > + goto probe_exit_sram_free; > + } > + > + soft_uart->clk_freq_mcasp = clk_get_rate(soft_uart->clk_mcasp); > + clk_enable(soft_uart->clk_mcasp); > + > + err = request_firmware(&fw, "PRU_SUART_Emulation.bin", > + &pdev->dev); > + if (err) { > + dev_err(&pdev->dev, "can't load firmware\n"); > + err = -ENODEV; > + goto probe_exit_clk; > + } > + dev_info(&pdev->dev, "fw size %td. downloading...\n", fw->size); > + > + /* download firmware into pru & init */ > + err = pru_softuart_init(dev, &init_params, fw->data, fw->size, > + soft_uart->clk_freq_pru / 1000000, > + &soft_uart->suart_iomap); > + if (err) { > + dev_err(&pdev->dev, "pruss init error\n"); > + err = -ENODEV; > + goto probe_release_fw; > + } > + release_firmware(fw); > + > + platform_set_drvdata(pdev, &soft_uart->port[0]); > + soft_uart->dev = dev; > + > + for (i = 0; i < NR_SUART; i++) { > + soft_uart->port[i].ops = &pruss_suart_ops; > + soft_uart->port[i].iotype = UPIO_MEM; > + soft_uart->port[i].flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; > + soft_uart->port[i].mapbase = > + (u32)soft_uart->suart_iomap.p_fifo_buff_virt_base; > + soft_uart->port[i].membase = > + soft_uart->suart_iomap.mcasp_io_addr; > + soft_uart->port[i].type = PORT_DA8XX_PRU_SUART; > + soft_uart->port[i].irq = > + platform_get_irq(to_platform_device(dev->parent), i); > + soft_uart->port[i].dev = &pdev->dev; > + soft_uart->port[i].irqflags = IRQF_SHARED; > + soft_uart->port[i].uartclk = soft_uart->clk_freq_mcasp; > + soft_uart->port[i].fifosize = SUART_FIFO_LEN; > + soft_uart->tx_loadsz = SUART_FIFO_LEN; > + soft_uart->port[i].custom_divisor = 1; > + soft_uart->port[i].line = i; > + soft_uart->suart_hdl[i].uart_num = i + 1; > + soft_uart->port[i].serial_in = NULL; > + > + soft_uart->suart_fifo_addr[i].fifo_vaddr_buff_tx = > + soft_uart->suart_iomap.p_fifo_buff_virt_base + > + (2 * SUART_CNTX_SZ * i); > + > + soft_uart->suart_fifo_addr[i].fifo_vaddr_buff_rx = > + soft_uart->suart_iomap.p_fifo_buff_virt_base + > + ((2 * SUART_CNTX_SZ * i) + SUART_CNTX_SZ); > + > + soft_uart->suart_fifo_addr[i].fifo_phys_addr_tx = > + soft_uart->suart_iomap.p_fifo_buff_phys_base + > + (2 * SUART_CNTX_SZ * i); > + > + soft_uart->suart_fifo_addr[i].fifo_phys_addr_rx = > + soft_uart->suart_iomap.p_fifo_buff_phys_base + > + ((2 * SUART_CNTX_SZ * i) + SUART_CNTX_SZ); > + > + soft_uart->port[i].serial_out = NULL; > + tasklet_init(&soft_uart->tx_task[i], suart_tx_task, > + (unsigned long)&soft_uart->port[i]); > + uart_add_one_port(&pruss_suart_reg, &soft_uart->port[i]); > + } > + > + dev_info(&pdev->dev, > + "%s device registered (pru_clk=%d, asp_clk=%d)\n", > + DRV_NAME, soft_uart->clk_freq_pru, soft_uart->clk_freq_mcasp); > + > + return 0; > + > +probe_release_fw: > + release_firmware(fw); > +probe_exit_clk: > + clk_put(soft_uart->clk_mcasp); > + clk_disable(soft_uart->clk_mcasp); > +probe_exit_sram_free: > + sram_free(soft_uart->suart_iomap.p_fifo_buff_virt_base, > + SUART_CNTX_SZ * NR_SUART * 2); > +probe_exit_iounmap: > + iounmap(soft_uart->suart_iomap.mcasp_io_addr); > +probe_exit_1: > + release_mem_region(res->start, > + resource_size(res)); > +probe_exit: > + kfree(soft_uart); > + return err; > +} > + > +static s32 __devexit pruss_suart_remove(struct platform_device *pdev) > +{ > + struct omapl_pru_suart *soft_uart = platform_get_drvdata(pdev); > + const struct da850_evm_pruss_suart_data *pdata; > + struct device *dev = &pdev->dev; > + struct resource *res; > + int i; > + > + pdata = dev->platform_data; > + if (!pdata) > + dev_err(&pdev->dev, "platform data not found\n"); > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) { > + dev_err(&pdev->dev, "failed to get resource"); > + return -ENOMEM; > + } > + > + platform_set_drvdata(pdev, NULL); > + > + if (soft_uart) { > + for (i = 0; i < NR_SUART; i++) { > + uart_remove_one_port(&pruss_suart_reg, > + &soft_uart->port[i]); > + } > + } > + > + sram_free(soft_uart->suart_iomap.p_fifo_buff_virt_base, > + SUART_CNTX_SZ * NR_SUART * 2); > + clk_put(soft_uart->clk_mcasp); > + pru_mcasp_deinit(); > + clk_disable(soft_uart->clk_mcasp); > + iounmap(soft_uart->suart_iomap.mcasp_io_addr); > + if (pdata) { > + release_mem_region(res->start, > + resource_size(res)); > + } > + kfree(soft_uart); > + return 0; > +} > + > +#define pruss_suart_suspend NULL > +#define pruss_suart_resume NULL > + > +static struct platform_driver serial_pruss_driver = { > + .probe = pruss_suart_probe, > + .remove = __devexit_p(pruss_suart_remove), > + .suspend = pruss_suart_suspend, > + .resume = pruss_suart_resume, > + .driver = { > + .name = DRV_NAME, > + .owner = THIS_MODULE, > + }, > +}; > + > +static s32 __init pruss_suart_init(void) > +{ > + s32 ret; > + > + pruss_suart_reg.nr = NR_SUART; > + ret = uart_register_driver(&pruss_suart_reg); > + if (ret) > + return ret; > + ret = platform_driver_register(&serial_pruss_driver); > + if (ret) > + goto out; > + > + pr_debug("SUART serial driver loaded\n"); > + return ret; > +out: > + uart_unregister_driver(&pruss_suart_reg); > + return ret; > +} > + > +module_init(pruss_suart_init); > + > +static void __exit pruss_suart_exit(void) > +{ > + platform_driver_unregister(&serial_pruss_driver); > + uart_unregister_driver(&pruss_suart_reg); > + pr_debug("SUART serial driver unloaded\n"); > +} > + > +module_exit(pruss_suart_exit); > + > +/* Module information */ > +MODULE_AUTHOR("Subhasish Ghosh "); > +MODULE_LICENSE("GPL"); > +MODULE_DESCRIPTION(DRV_DESC); > diff --git a/drivers/tty/serial/pruss_suart.h > b/drivers/tty/serial/pruss_suart.h > new file mode 100644 > index 0000000..f3a2a9d > --- /dev/null > +++ b/drivers/tty/serial/pruss_suart.h > @@ -0,0 +1,1038 @@ > +/* > + * Copyright (C) 2010, 2011 Texas Instruments Incorporated > + * Author: Jitendra Kumar > + * > + * 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 _SUART_API_H_ > +#define _SUART_API_H_ > + > +#include > +#include > +#include > +#include > + > +#define SINGLE_PRU 0 > +#define BOTH_PRU 1 > +#define PRU_ACTIVE BOTH_PRU > +#define PRU_CLK_228 228 > +#define PRU_CLK_186 186 > + > +#define PRU_SUART_SERIALIZER_0 (0u) > +#define PRU_SUART_SERIALIZER_1 (1u) > +#define PRU_SUART_SERIALIZER_2 (2u) > +#define PRU_SUART_SERIALIZER_3 (3u) > +#define PRU_SUART_SERIALIZER_4 (4u) > +#define PRU_SUART_SERIALIZER_5 (5u) > +#define PRU_SUART_SERIALIZER_6 (6u) > +#define PRU_SUART_SERIALIZER_7 (7u) > +#define PRU_SUART_SERIALIZER_8 (8u) > +#define PRU_SUART_SERIALIZER_9 (9u) > +#define PRU_SUART_SERIALIZER_10 (10u) > +#define PRU_SUART_SERIALIZER_11 (11u) > +#define PRU_SUART_SERIALIZER_12 (12u) > +#define PRU_SUART_SERIALIZER_13 (13u) > +#define PRU_SUART_SERIALIZER_14 (14u) > +#define PRU_SUART_SERIALIZER_15 (15u) > +#define PRU_SUART_SERIALIZER_NONE (16u) > + > +#define PRU_SUART_UART1 (1u) > +#define PRU_SUART_UART2 (2u) > +#define PRU_SUART_UART3 (3u) > +#define PRU_SUART_UART4 (4u) > +#define PRU_SUART_UART5 (5u) > +#define PRU_SUART_UART6 (6u) > +#define PRU_SUART_UART7 (7u) > +#define PRU_SUART_UART8 (8u) > +#define PRU_SUART_UARTx_INVALID (9u) > + > +#define PRU_SUART_HALF_TX (1u) > +#define PRU_SUART_HALF_RX (2u) > +#define PRU_SUART_HALF_TX_DISABLED (4u) > +#define PRU_SUART_HALF_RX_DISABLED (8u) > + > +#define PRU_SUART0_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ > + PRU_SUART_HALF_RX_DISABLED) > +#define PRU_SUART0_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) > +#define PRU_SUART0_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) > + > +#define PRU_SUART1_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ > + PRU_SUART_HALF_RX) > +#define PRU_SUART1_CONFIG_RX_SER (PRU_SUART_SERIALIZER_7) > +#define PRU_SUART1_CONFIG_TX_SER (PRU_SUART_SERIALIZER_8) > + > +#define PRU_SUART2_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ > + PRU_SUART_HALF_RX) > +#define PRU_SUART2_CONFIG_RX_SER (PRU_SUART_SERIALIZER_9) > +#define PRU_SUART2_CONFIG_TX_SER (PRU_SUART_SERIALIZER_10) > + > +#define PRU_SUART3_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ > + PRU_SUART_HALF_RX) > +#define PRU_SUART3_CONFIG_RX_SER (PRU_SUART_SERIALIZER_13) > +#define PRU_SUART3_CONFIG_TX_SER (PRU_SUART_SERIALIZER_14) > + > +#define PRU_SUART4_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ > + PRU_SUART_HALF_RX_DISABLED) > +#define PRU_SUART4_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) > +#define PRU_SUART4_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) > + > +#define PRU_SUART5_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ > + PRU_SUART_HALF_RX_DISABLED) > +#define PRU_SUART5_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) > +#define PRU_SUART5_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) > + > +#define PRU_SUART6_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ > + PRU_SUART_HALF_RX_DISABLED) > +#define PRU_SUART6_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) > +#define PRU_SUART6_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) > + > +#define PRU_SUART7_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ > + PRU_SUART_HALF_RX_DISABLED) > +#define PRU_SUART7_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) > +#define PRU_SUART7_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) > + > +#define SUART_NUM_OF_CHANNELS_PER_SUART 2 > +#define SUART_NUM_OF_BYTES_PER_CHANNEL 16 > + > +#define PRU_TX_INTR 1 > +#define PRU_RX_INTR 2 > + > +#define CHN_TXRX_STATUS_TIMEOUT BIT(6) > +#define CHN_TXRX_STATUS_BI BIT(5) > +#define CHN_TXRX_STATUS_FE BIT(4) > +#define CHN_TXRX_STATUS_UNERR BIT(3) > +#define CHN_TXRX_STATUS_OVRNERR BIT(3) > +#define CHN_TXRX_STATUS_ERR BIT(2) > +#define CHN_TXRX_STATUS_CMPLT BIT(1) > +#define CHN_TXRX_STATUS_RDY BIT(0) > + > +#define CHN_TXRX_IE_MASK_TIMEOUT BIT(14) > +#define CHN_TXRX_IE_MASK_BI BIT(13) > +#define CHN_TXRX_IE_MASK_FE BIT(12) > +#define CHN_TXRX_IE_MASK_CMPLT BIT(1) > + > +#define SUART_GBL_INTR_ERR_MASK BIT(9) > +#define SUART_PRU_ID_MASK 0xFF > + > +#define SUART_FIFO_LEN 15 > +#define SUART_8X_OVRSMPL 1 > +#define SUART_16X_OVRSMPL 2 > +#define SUART_TX_OVRSMPL 0 > +#define SUART_DEFAULT_OVRSMPL SUART_8X_OVRSMPL > + > +#define SUART_DEFAULT_OVRSMPL_OFFSET 26 > +#define SUART_CHN_OFFSET 31 > +#define SERIALIZER_OFFSET 8 > + > +#if (SUART_DEFAULT_OVRSMPL == SUART_16X_OVRSMPL) > +#define SUART_DEFAULT_BAUD 57600 > +#else > +#define SUART_DEFAULT_BAUD 115200 > +#endif > + > +#define PRU_MODE_INVALID 0x0 > +#define PRU_MODE_TX_ONLY 0x1 > +#define PRU_MODE_RX_ONLY 0x2 > +#define PRU_MODE_RX_TX_BOTH 0x3 > + > +#if (PRU_ACTIVE == BOTH_PRU) > +#define PRU0_MODE PRU_MODE_RX_ONLY > +#define PRU1_MODE PRU_MODE_TX_ONLY > +#elif (PRU_ACTIVE == SINGLE_PRU) > +#define PRU0_MODE PRU_MODE_RX_TX_BOTH > +#define PRU1_MODE PRU_MODE_INVALID > +#else > +#define PRU0_MODE PRU_MODE_INVALID > +#define PRU1_MODE PRU_MODE_INVALID > +#endif > + > +#define MCASP_XBUF_BASE_ADDR (0x01d00200) > +#define MCASP_RBUF_BASE_ADDR (0x01d00280) > +#define MCASP_SRCTL_BASE_ADDR (0x01d00180) > + > +#define MCASP_SRCTL_TX_MODE (0x000D) > +#define MCASP_SRCTL_RX_MODE (0x000E) > + > +/* Since only PRU0 can work as RX */ > +#define RX_DEFAULT_DATA_DUMP_ADDR (0x00001FC) > +#define PRU_NUM_OF_CHANNELS (16) > + > +/* MCASP */ > + > +#define OMAPL_MCASP_PFUNC_AFSR_MASK (0x80000000u) > +#define OMAPL_MCASP_PFUNC_AFSR_SHIFT (0x0000001Fu) > +#define OMAPL_MCASP_PFUNC_AFSR_RESETVAL (0x00000000u) > +/* AFSR Tokens */ > +#define OMAPL_MCASP_PFUNC_AFSR_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AFSR_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AHCLKR_MASK (0x40000000u) > +#define OMAPL_MCASP_PFUNC_AHCLKR_SHIFT (0x0000001Eu) > +#define OMAPL_MCASP_PFUNC_AHCLKR_RESETVAL (0x00000000u) > +/* AHCLKR Tokens */ > +#define OMAPL_MCASP_PFUNC_AHCLKR_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AHCLKR_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_ACLKR_MASK (0x20000000u) > +#define OMAPL_MCASP_PFUNC_ACLKR_SHIFT (0x0000001Du) > +#define OMAPL_MCASP_PFUNC_ACLKR_RESETVAL (0x00000000u) > +/* ACLKR Tokens */ > +#define OMAPL_MCASP_PFUNC_ACLKR_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_ACLKR_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AFSX_MASK (0x10000000u) > +#define OMAPL_MCASP_PFUNC_AFSX_SHIFT (0x0000001Cu) > +#define OMAPL_MCASP_PFUNC_AFSX_RESETVAL (0x00000000u) > +/* AFSX Tokens */ > +#define OMAPL_MCASP_PFUNC_AFSX_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AFSX_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AHCLKX_MASK (0x08000000u) > +#define OMAPL_MCASP_PFUNC_AHCLKX_SHIFT (0x0000001Bu) > +#define OMAPL_MCASP_PFUNC_AHCLKX_RESETVAL (0x00000000u) > +/* AHCLKX Tokens */ > +#define OMAPL_MCASP_PFUNC_AHCLKX_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AHCLKX_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_ACLKX_MASK (0x04000000u) > +#define OMAPL_MCASP_PFUNC_ACLKX_SHIFT (0x0000001Au) > +#define OMAPL_MCASP_PFUNC_ACLKX_RESETVAL (0x00000000u) > +/* ACLKX Tokens */ > +#define OMAPL_MCASP_PFUNC_ACLKX_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_ACLKX_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AMUTE_MASK (0x02000000u) > +#define OMAPL_MCASP_PFUNC_AMUTE_SHIFT (0x00000019u) > +#define OMAPL_MCASP_PFUNC_AMUTE_RESETVAL (0x00000000u) > +/* AMUTE Tokens */ > +#define OMAPL_MCASP_PFUNC_AMUTE_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AMUTE_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR15_MASK (0x00008000u) > +#define OMAPL_MCASP_PFUNC_AXR15_SHIFT (0x0000000Fu) > +#define OMAPL_MCASP_PFUNC_AXR15_RESETVAL (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR15_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR15_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR14_MASK (0x00004000u) > +#define OMAPL_MCASP_PFUNC_AXR14_SHIFT (0x0000000Eu) > +#define OMAPL_MCASP_PFUNC_AXR14_RESETVAL (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR14_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR14_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR13_MASK (0x00002000u) > +#define OMAPL_MCASP_PFUNC_AXR13_SHIFT (0x0000000Du) > +#define OMAPL_MCASP_PFUNC_AXR13_RESETVAL (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR13_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR13_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR12_MASK (0x00001000u) > +#define OMAPL_MCASP_PFUNC_AXR12_SHIFT (0x0000000Cu) > +#define OMAPL_MCASP_PFUNC_AXR12_RESETVAL (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR12_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR12_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR11_MASK (0x00000800u) > +#define OMAPL_MCASP_PFUNC_AXR11_SHIFT (0x0000000Bu) > +#define OMAPL_MCASP_PFUNC_AXR11_RESETVAL (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR11_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR11_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR10_MASK (0x00000400u) > +#define OMAPL_MCASP_PFUNC_AXR10_SHIFT (0x0000000Au) > +#define OMAPL_MCASP_PFUNC_AXR10_RESETVAL (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR10_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR10_GPIO (0x00000001u) > +#define OMAPL_MCASP_PFUNC_AXR9_MASK (0x00000200u) > +#define OMAPL_MCASP_PFUNC_AXR9_SHIFT (0x00000009u) > +#define OMAPL_MCASP_PFUNC_AXR9_RESETVAL (0x00000000u) > +/* AXR9 Token */ > +#define OMAPL_MCASP_PFUNC_AXR9_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR9_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR8_MASK (0x00000100u) > +#define OMAPL_MCASP_PFUNC_AXR8_SHIFT (0x00000008u) > +#define OMAPL_MCASP_PFUNC_AXR8_RESETVAL (0x00000000u) > +/* AXR8 Tokens */ > +#define OMAPL_MCASP_PFUNC_AXR8_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR8_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR7_MASK (0x00000080u) > +#define OMAPL_MCASP_PFUNC_AXR7_SHIFT (0x00000007u) > +#define OMAPL_MCASP_PFUNC_AXR7_RESETVAL (0x00000000u) > +/* AXR7 Tokens */ > +#define OMAPL_MCASP_PFUNC_AXR7_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR7_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR6_MASK (0x00000040u) > +#define OMAPL_MCASP_PFUNC_AXR6_SHIFT (0x00000006u) > +#define OMAPL_MCASP_PFUNC_AXR6_RESETVAL (0x00000000u) > +/* AXR6 Tokens */ > +#define OMAPL_MCASP_PFUNC_AXR6_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR6_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR5_MASK (0x00000020u) > +#define OMAPL_MCASP_PFUNC_AXR5_SHIFT (0x00000005u) > +#define OMAPL_MCASP_PFUNC_AXR5_RESETVAL (0x00000000u) > +/* AXR5 Tokens */ > +#define OMAPL_MCASP_PFUNC_AXR5_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR5_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR4_MASK (0x00000010u) > +#define OMAPL_MCASP_PFUNC_AXR4_SHIFT (0x00000004u) > +#define OMAPL_MCASP_PFUNC_AXR4_RESETVAL (0x00000000u) > +/* AXR4 Tokens */ > +#define OMAPL_MCASP_PFUNC_AXR4_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR4_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR3_MASK (0x00000008u) > +#define OMAPL_MCASP_PFUNC_AXR3_SHIFT (0x00000003u) > +#define OMAPL_MCASP_PFUNC_AXR3_RESETVAL (0x00000000u) > +/* AXR3 Tokens */ > +#define OMAPL_MCASP_PFUNC_AXR3_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR3_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR2_MASK (0x00000004u) > +#define OMAPL_MCASP_PFUNC_AXR2_SHIFT (0x00000002u) > +#define OMAPL_MCASP_PFUNC_AXR2_RESETVAL (0x00000000u) > +/* AXR2 Tokens */ > +#define OMAPL_MCASP_PFUNC_AXR2_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR2_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR1_MASK (0x00000002u) > +#define OMAPL_MCASP_PFUNC_AXR1_SHIFT (0x00000001u) > +#define OMAPL_MCASP_PFUNC_AXR1_RESETVAL (0x00000000u) > +/* AXR1 Tokens */ > +#define OMAPL_MCASP_PFUNC_AXR1_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR1_GPIO (0x00000001u) > + > +#define OMAPL_MCASP_PFUNC_AXR0_MASK (0x00000001u) > +#define OMAPL_MCASP_PFUNC_AXR0_SHIFT (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR0_RESETVAL (0x00000000u) > +/* AXR0 Tokens */ > +#define OMAPL_MCASP_PFUNC_AXR0_MCASP (0x00000000u) > +#define OMAPL_MCASP_PFUNC_AXR0_GPIO (0x00000001u) > +#define OMAPL_MCASP_PFUNC_RESETVAL (0x00000000u) > + > +#define OMAPL_MCASP_PDIR_AFSR_MASK (0x80000000u) > +#define OMAPL_MCASP_PDIR_AFSR_SHIFT (0x0000001Fu) > +#define OMAPL_MCASP_PDIR_AFSR_RESETVAL (0x00000000u) > +/* AFSR Tokens */ > +#define OMAPL_MCASP_PDIR_AFSR_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AFSR_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AHCLKR_MASK (0x40000000u) > +#define OMAPL_MCASP_PDIR_AHCLKR_SHIFT (0x0000001Eu) > +#define OMAPL_MCASP_PDIR_AHCLKR_RESETVAL (0x00000000u) > +/* AHCLKR Tokens */ > +#define OMAPL_MCASP_PDIR_AHCLKR_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AHCLKR_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_ACLKR_MASK (0x20000000u) > +#define OMAPL_MCASP_PDIR_ACLKR_SHIFT (0x0000001Du) > +#define OMAPL_MCASP_PDIR_ACLKR_RESETVAL (0x00000000u) > +/* ACLKR Tokens */ > +#define OMAPL_MCASP_PDIR_ACLKR_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_ACLKR_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AFSX_MASK (0x10000000u) > +#define OMAPL_MCASP_PDIR_AFSX_SHIFT (0x0000001Cu) > +#define OMAPL_MCASP_PDIR_AFSX_RESETVAL (0x00000000u) > +/* AFSX Tokens */ > +#define OMAPL_MCASP_PDIR_AFSX_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AFSX_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AHCLKX_MASK (0x08000000u) > +#define OMAPL_MCASP_PDIR_AHCLKX_SHIFT (0x0000001Bu) > +#define OMAPL_MCASP_PDIR_AHCLKX_RESETVAL (0x00000000u) > +/* AHCLKX Tokens */ > +#define OMAPL_MCASP_PDIR_AHCLKX_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AHCLKX_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_ACLKX_MASK (0x04000000u) > +#define OMAPL_MCASP_PDIR_ACLKX_SHIFT (0x0000001Au) > +#define OMAPL_MCASP_PDIR_ACLKX_RESETVAL (0x00000000u) > +/* ACLKX Tokens */ > +#define OMAPL_MCASP_PDIR_ACLKX_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_ACLKX_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AMUTE_MASK (0x02000000u) > +#define OMAPL_MCASP_PDIR_AMUTE_SHIFT (0x00000019u) > +#define OMAPL_MCASP_PDIR_AMUTE_RESETVAL (0x00000000u) > +/* AMUTE Tokens */ > +#define OMAPL_MCASP_PDIR_AMUTE_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AMUTE_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR15_MASK (0x00008000u) > +#define OMAPL_MCASP_PDIR_AXR15_SHIFT (0x0000000Fu) > +#define OMAPL_MCASP_PDIR_AXR15_RESETVAL (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR15_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR15_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR14_MASK (0x00004000u) > +#define OMAPL_MCASP_PDIR_AXR14_SHIFT (0x0000000Eu) > +#define OMAPL_MCASP_PDIR_AXR14_RESETVAL (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR14_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR14_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR13_MASK (0x00002000u) > +#define OMAPL_MCASP_PDIR_AXR13_SHIFT (0x0000000Du) > +#define OMAPL_MCASP_PDIR_AXR13_RESETVAL (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR13_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR13_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR12_MASK (0x00001000u) > +#define OMAPL_MCASP_PDIR_AXR12_SHIFT (0x0000000Cu) > +#define OMAPL_MCASP_PDIR_AXR12_RESETVAL (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR12_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR12_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR11_MASK (0x00000800u) > +#define OMAPL_MCASP_PDIR_AXR11_SHIFT (0x0000000Bu) > +#define OMAPL_MCASP_PDIR_AXR11_RESETVAL (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR11_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR11_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR10_MASK (0x00000400u) > +#define OMAPL_MCASP_PDIR_AXR10_SHIFT (0x0000000Au) > +#define OMAPL_MCASP_PDIR_AXR10_RESETVAL (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR10_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR10_OUTPUT (0x00000001u) > +#define OMAPL_MCASP_PDIR_AXR9_MASK (0x00000200u) > +#define OMAPL_MCASP_PDIR_AXR9_SHIFT (0x00000009u) > +#define OMAPL_MCASP_PDIR_AXR9_RESETVAL (0x00000000u) > +/* AXR9 Tokens */ > +#define OMAPL_MCASP_PDIR_AXR9_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR9_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR8_MASK (0x00000100u) > +#define OMAPL_MCASP_PDIR_AXR8_SHIFT (0x00000008u) > +#define OMAPL_MCASP_PDIR_AXR8_RESETVAL (0x00000000u) > +/* AXR8 Tokens */ > +#define OMAPL_MCASP_PDIR_AXR8_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR8_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR7_MASK (0x00000080u) > +#define OMAPL_MCASP_PDIR_AXR7_SHIFT (0x00000007u) > +#define OMAPL_MCASP_PDIR_AXR7_RESETVAL (0x00000000u) > +/*----AXR7 Tokens----*/ > +#define OMAPL_MCASP_PDIR_AXR7_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR7_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR6_MASK (0x00000040u) > +#define OMAPL_MCASP_PDIR_AXR6_SHIFT (0x00000006u) > +#define OMAPL_MCASP_PDIR_AXR6_RESETVAL (0x00000000u) > +/*----AXR6 Tokens----*/ > +#define OMAPL_MCASP_PDIR_AXR6_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR6_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR5_MASK (0x00000020u) > +#define OMAPL_MCASP_PDIR_AXR5_SHIFT (0x00000005u) > +#define OMAPL_MCASP_PDIR_AXR5_RESETVAL (0x00000000u) > +/*----AXR5 Tokens----*/ > +#define OMAPL_MCASP_PDIR_AXR5_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR5_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR4_MASK (0x00000010u) > +#define OMAPL_MCASP_PDIR_AXR4_SHIFT (0x00000004u) > +#define OMAPL_MCASP_PDIR_AXR4_RESETVAL (0x00000000u) > +/*----AXR4 Tokens----*/ > +#define OMAPL_MCASP_PDIR_AXR4_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR4_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR3_MASK (0x00000008u) > +#define OMAPL_MCASP_PDIR_AXR3_SHIFT (0x00000003u) > +#define OMAPL_MCASP_PDIR_AXR3_RESETVAL (0x00000000u) > +/*----AXR3 Tokens----*/ > +#define OMAPL_MCASP_PDIR_AXR3_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR3_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR2_MASK (0x00000004u) > +#define OMAPL_MCASP_PDIR_AXR2_SHIFT (0x00000002u) > +#define OMAPL_MCASP_PDIR_AXR2_RESETVAL (0x00000000u) > +/*----AXR2 Tokens----*/ > +#define OMAPL_MCASP_PDIR_AXR2_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR2_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR1_MASK (0x00000002u) > +#define OMAPL_MCASP_PDIR_AXR1_SHIFT (0x00000001u) > +#define OMAPL_MCASP_PDIR_AXR1_RESETVAL (0x00000000u) > +/*----AXR1 Tokens----*/ > +#define OMAPL_MCASP_PDIR_AXR1_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR1_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_AXR0_MASK (0x00000001u) > +#define OMAPL_MCASP_PDIR_AXR0_SHIFT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR0_RESETVAL (0x00000000u) > +/*----AXR0 Tokens----*/ > +#define OMAPL_MCASP_PDIR_AXR0_INPUT (0x00000000u) > +#define OMAPL_MCASP_PDIR_AXR0_OUTPUT (0x00000001u) > + > +#define OMAPL_MCASP_PDIR_RESETVAL (0x00000000u) > + > +#define OMAPL_MCASP_ACLKXCTL_CLKXP_MASK (0x00000080u) > +#define OMAPL_MCASP_ACLKXCTL_CLKXP_SHIFT (0x00000007u) > +#define OMAPL_MCASP_ACLKXCTL_CLKXP_RESETVAL (0x00000000u) > +/*----CLKXP Tokens----*/ > +#define OMAPL_MCASP_ACLKXCTL_CLKXP_RISINGEDGE (0x00000000u) > +#define OMAPL_MCASP_ACLKXCTL_CLKXP_FALLINGEDGE (0x00000001u) > + > +#define OMAPL_MCASP_ACLKXCTL_ASYNC_MASK (0x00000040u) > +#define OMAPL_MCASP_ACLKXCTL_ASYNC_SHIFT (0x00000006u) > +#define OMAPL_MCASP_ACLKXCTL_ASYNC_RESETVAL (0x00000001u) > +/*----ASYNC Tokens----*/ > +#define OMAPL_MCASP_ACLKXCTL_ASYNC_SYNC (0x00000000u) > +#define OMAPL_MCASP_ACLKXCTL_ASYNC_ASYNC (0x00000001u) > + > +#define OMAPL_MCASP_ACLKXCTL_CLKXM_MASK (0x00000020u) > +#define OMAPL_MCASP_ACLKXCTL_CLKXM_SHIFT (0x00000005u) > +#define OMAPL_MCASP_ACLKXCTL_CLKXM_RESETVAL (0x00000001u) > +/*----CLKXM Tokens----*/ > +#define OMAPL_MCASP_ACLKXCTL_CLKXM_EXTERNAL (0x00000000u) > +#define OMAPL_MCASP_ACLKXCTL_CLKXM_INTERNAL (0x00000001u) > + > +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_MASK (0x0000001Fu) > +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT (0x00000000u) > +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_RESETVAL (0x00000000u) > + > +#define OMAPL_MCASP_ACLKXCTL_RESETVAL (0x00000060u) > + > +/* AHCLKXCTL */ > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_MASK (0x00008000u) > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_SHIFT (0x0000000Fu) > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_RESETVAL (0x00000001u) > +/*----HCLKXM Tokens----*/ > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_EXTERNAL (0x00000000u) > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_INTERNAL (0x00000001u) > + > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_MASK (0x00004000u) > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_SHIFT (0x0000000Eu) > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_RESETVAL (0x00000000u) > +/*----HCLKXP Tokens----*/ > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_NOTINVERTED (0x00000000u) > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_INVERTED (0x00000001u) > + > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_MASK (0x00000FFFu) > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT (0x00000000u) > +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_RESETVAL (0x00000000u) > + > +#define OMAPL_MCASP_AHCLKXCTL_RESETVAL (0x00008000u) > + > +#define MCASP_SUART_GBLCTL (0X00000000) > +#define MCASP_SUART_RGBLCTL (0X00000000) > +#define MCASP_SUART_XGBLCTL (0X00000000) > +#define MCASP_SUART_RMASK_8 (0x000000FF) > +#define MCASP_SUART_RMASK_16 (0x0000FFFF) > +#define MCASP_SUART_RFMT_8 (0x0000A038) > +#define MCASP_SUART_RFMT_16 (0x0000A078) > +#define MCASP_SUART_FSRM (0X00000002) > +#define MCASP_SUART_CLKRM_CLKRP (0X000000A0) > +#define MCASP_SUART_HCLKRP (0X00008000) > +#define MCASP_SUART_RTDMS0 (0X00000001) > +#define MCASP_SUART_RSYNCERR (0X00000002) > +#define MCASP_SUART_RMAX_RPS_256 (0x00FF0008) > +#define MCASP_SUART_XMASK_0_31 (0X0000FFFF) > +#define MCASP_SUART_XBUSEL_XSSZ_16_XPAD_0 (0x00002078) > +#define MCASP_SUART_FSXM (0x00000002) > +#define MCASP_SUART_CLKXM_ASYNC_CLKXP (0x000000E0) > +#define MCASP_SUART_HCLKXM (0x00008000) > +#define MCASP_SUART_XTDMS0 (0X00000001) > +#define MCASP_SUART_XSYNCERR (0x00000002) > +#define MCASP_SUART_XMAX_XPS_256 (0x00FF0008) > +#define MCASP_SUART_SRCTL_DISMOD (0x0000000c) > +#define MCASP_SUART_DIT_DISABLE (0X00000000) > +#define MCASP_SUART_LOOPBACK_DISABLE (0x00000000) > +#define MCASP_SUART_AMUTE_DISABLE (0X00000000) > +#define MCASP_SUART_XSTAT (0x0000FFFF) > +#define MCASP_SUART_RSTAT (0x0000FFFF) > + > +/* SUART REGS */ > + > +/* PRU0 DATA RAM base address */ > +#define PRU0_DATARAM_OFFSET (0x0000u) > +/* PRU1 DATA RAM base address */ > +#define PRU1_DATARAM_OFFSET (0x2000u) > + > +/* PRU0 DATA RAM size */ > +#define PRU0_DATARAM_SIZE (0x200u) > +/* PRU1 DATA RAM size */ > +#define PRU1_DATARAM_SIZE (0x200u) > + > +#define PRU_SUART_PRU0_CH0_OFFSET (0x0000) > +#define PRU_SUART_PRU0_CH1_OFFSET (0x0010) > +#define PRU_SUART_PRU0_CH2_OFFSET (0x0020) > +#define PRU_SUART_PRU0_CH3_OFFSET (0x0030) > +#define PRU_SUART_PRU0_CH4_OFFSET (0x0040) > +#define PRU_SUART_PRU0_CH5_OFFSET (0x0050) > +#define PRU_SUART_PRU0_CH6_OFFSET (0x0060) > +#define PRU_SUART_PRU0_CH7_OFFSET (0x0070) > +#define PRU_SUART_PRU0_IMR_OFFSET (0x0080) > +/* Interrupt Mask Register */ > +#define PRU_SUART_PRU0_ISR_OFFSET (0x0082) > +/* Interrupt Status Register */ > +#define PRU_SUART_PRU0_ID_ADDR (0x0084) > +/* PRU ID Register */ > +#define PRU_SUART_PRU0_RX_TX_MODE (0x0085) > +#define PRU_SUART_PRU0_DELAY_OFFSET (0x0086) > +#define PRU_SUART_PRU0_IDLE_TIMEOUT_OFFSET (0x0088) > + > +/* PRU 1 Macros */ > +#define PRU_SUART_PRU1_CH0_OFFSET (0x2000) > +#define PRU_SUART_PRU1_CH1_OFFSET (0x2010) > +#define PRU_SUART_PRU1_CH2_OFFSET (0x2020) > +#define PRU_SUART_PRU1_CH3_OFFSET (0x2030) > +#define PRU_SUART_PRU1_CH4_OFFSET (0x2040) > +#define PRU_SUART_PRU1_CH5_OFFSET (0x2050) > +#define PRU_SUART_PRU1_CH6_OFFSET (0x2060) > +#define PRU_SUART_PRU1_CH7_OFFSET (0x2070) > +#define PRU_SUART_PRU1_IMR_OFFSET (0x2080) > +#define PRU_SUART_PRU1_ISR_OFFSET (0x2082) > +#define PRU_SUART_PRU1_ID_ADDR (0x2084) > +#define PRU_SUART_PRU1_RX_TX_MODE (0x2085) > +#define PRU_SUART_PRU1_DELAY_OFFSET (0x2086) > +#define PRU_SUART_PRU1_IDLE_TIMEOUT_OFFSET (0x2088) > + > +/* SUART Channel Control Register bit descriptions */ > +#define PRU_SUART_CH_CTRL_MODE_SHIFT 0x0000 > +#define PRU_SUART_CH_CTRL_MODE_MASK 0x0003 > +#define PRU_SUART_CH_CTRL_TX_MODE 0x0001 > +#define PRU_SUART_CH_CTRL_RX_MODE 0x0002 > + > +/* Service Request */ > +#define PRU_SUART_CH_CTRL_SREQ_SHIFT 0x0002 > +#define PRU_SUART_CH_CTRL_SREQ_MASK 0x0004 > +#define PRU_SUART_CH_CTRL_SREQ 0x0001 > + > +/* McASP Instance */ > +#define PRU_SUART_CH_CTRL_MCASP_SHIFT 0x0003 > +#define PRU_SUART_CH_CTRL_MCASP_MASK 0x0018 > +#define PRU_SUART_CH_CTRL_SR_SHIFT 0x0008 > +#define PRU_SUART_CH_CTRL_SR_MASK 0x0F00 > + > +/* SUART channel configuration1 register descriptions */ > + > +/* clock divisor - relative baud value */ > +#define PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT 0x0000 > +#define PRU_SUART_CH_CONFIG1_DIVISOR_MASK 0x03FF > +/* oversampling */ > +#define PRU_SUART_CH_CONFIG1_OVS_SHIFT 0x000A > +#define PRU_SUART_CH_CONFIG1_OVS_MASK 0x0C00 > + > +/* SUART channel configuration2 register descriptions */ > +/* Bits per character */ > +#define PRU_SUART_CH_CONFIG2_BITPERCHAR_SHIFT 0x0000 > +#define PRU_SUART_CH_CONFIG2_BITPERCHAR_MASK 0x000F > + > +/* Bits per character */ > +#define PRU_SUART_CH_CONFIG2_DATALEN_SHIFT 0x0008 > +#define PRU_SUART_CH_CONFIG2_DATALEN_MASK 0x0F00 > + > +/* SUART Channel STATUS Register*/ > +#define PRU_SUART_CH_STATUS_EN_BIT_MASK 0x8000 > + > +/* SUART Channel register offsets */ > +#define PRU_SUART_CH_CTRL_OFFSET 0x00 > +#define PRU_SUART_CH_CONFIG1_OFFSET 0x02 > +#define PRU_SUART_CH_CONFIG2_OFFSET 0x04 > +#define PRU_SUART_CH_TXRXSTATUS_OFFSET 0x06 > +#define PRU_SUART_CH_TXRXDATA_OFFSET 0x08 > +#define PRU_SUART_CH_BYTESDONECNTR_OFFSET 0x0C > + > +/* SUART Event Numbers macros */ > +#define PRU_SUART0_TX_EVT 34 > +#define PRU_SUART0_RX_EVT 35 > +#define PRU_SUART1_TX_EVT 36 > +#define PRU_SUART1_RX_EVT 37 > +#define PRU_SUART2_TX_EVT 38 > +#define PRU_SUART2_RX_EVT 39 > +#define PRU_SUART3_TX_EVT 40 > +#define PRU_SUART3_RX_EVT 41 > +#define PRU_SUART4_TX_EVT 42 > +#define PRU_SUART4_RX_EVT 43 > +#define PRU_SUART5_TX_EVT 44 > +#define PRU_SUART5_RX_EVT 45 > +#define PRU_SUART6_TX_EVT 46 > +#define PRU_SUART6_RX_EVT 47 > +#define PRU_SUART7_TX_EVT 48 > +#define PRU_SUART7_RX_EVT 49 > + > +#define PRU_SUART0_TX_EVT_BIT BIT(2) > +#define PRU_SUART0_RX_EVT_BIT BIT(3) > +#define PRU_SUART1_TX_EVT_BIT BIT(4) > +#define PRU_SUART1_RX_EVT_BIT BIT(5) > +#define PRU_SUART2_TX_EVT_BIT BIT(6) > +#define PRU_SUART2_RX_EVT_BIT BIT(7) > +#define PRU_SUART3_TX_EVT_BIT BIT(8) > +#define PRU_SUART3_RX_EVT_BIT BIT(9) > +#define PRU_SUART4_TX_EVT_BIT BIT(10) > +#define PRU_SUART4_RX_EVT_BIT BIT(11) > +#define PRU_SUART5_TX_EVT_BIT BIT(12) > +#define PRU_SUART5_RX_EVT_BIT BIT(13) > +#define PRU_SUART6_TX_EVT_BIT BIT(14) > +#define PRU_SUART6_RX_EVT_BIT BIT(15) > +#define PRU_SUART7_TX_EVT_BIT BIT(16) > +#define PRU_SUART7_RX_EVT_BIT BIT(17) > + > +/* Total number of baud rates supported */ > +#define SUART_NUM_OF_BAUDS_SUPPORTED 13 > + > +#define MCASP_PDIR_VAL ( \ > + OMAPL_MCASP_PDIR_AFSR_OUTPUT< + OMAPL_MCASP_PDIR_AHCLKR_OUTPUT< + OMAPL_MCASP_PDIR_ACLKR_OUTPUT< + OMAPL_MCASP_PDIR_AFSX_OUTPUT< + OMAPL_MCASP_PDIR_AHCLKX_OUTPUT< + OMAPL_MCASP_PDIR_ACLKX_OUTPUT< + > +/* > + * This enum is used to specify the direction of the channel in UART > + */ > +enum SUART_CHN_DIR { > + SUART_CHN_TX = 1, > + SUART_CHN_RX = 2 > +}; > + > +/* > + * This enum is used to specify the state of the channel in UART. It > + * is either enabled or disabled. > + */ > +enum SUART_CHN_STATE { > + SUART_CHN_DISABLED = 0, > + SUART_CHN_ENABLED = 1 > +}; > + > +enum SUART_EN_BITSPERCHAR { > + ePRU_SUART_DATA_BITS6 = 8, > + ePRU_SUART_DATA_BITS7, > + ePRU_SUART_DATA_BITS8, > + ePRU_SUART_DATA_BITS9, > + ePRU_SUART_DATA_BITS10, > + ePRU_SUART_DATA_BITS11, > + ePRU_SUART_DATA_BITS12 > +}; > + > +enum SUART_EN_UARTNUM { > + ePRU_SUART_NUM_1 = 1, > + ePRU_SUART_NUM_2, > + ePRU_SUART_NUM_3, > + ePRU_SUART_NUM_4, > + ePRU_SUART_NUM_5, > + ePRU_SUART_NUM_6, > + ePRU_SUART_NUM_7, > + ePRU_SUART_NUM_8 > +}; > + > +enum SUART_EN_UARTTYPE { > + ePRU_SUART_HALF_TX = 1, > + ePRU_SUART_HALF_RX, > + ePRU_SUART_FULL_TX_RX, > + ePRU_SUART_HALF_TX_DISABLED = 4, > + ePRU_SUART_HALF_RX_DISABLED = 8 > +}; > + > +enum SUART_EN_TXCHANNEL { > + ePRU_SUART_TX_CH0 = 0, > + ePRU_SUART_TX_CH1, > + ePRU_SUART_TX_CH2, > + ePRU_SUART_TX_CH3, > + ePRU_SUART_TX_CH4, > + ePRU_SUART_TX_CH5, > + ePRU_SUART_TX_CH6, > + ePRU_SUART_TX_CH7 > +}; > + > +enum SUART_EN_RXCHANNEL { > + ePRU_SUART_RX_CH0 = 0, > + ePRU_SUART_RX_CH1, > + ePRU_SUART_RX_CH2, > + ePRU_SUART_RX_CH3, > + ePRU_SUART_RX_CH4, > + ePRU_SUART_RX_CH5, > + ePRU_SUART_RX_CH6, > + ePRU_SUART_RX_CH7 > +}; > + > +enum SUART_EN_UART_STATUS { > + ePRU_SUART_UART_FREE = 0, > + ePRU_SUART_UART_IN_USE > +}; > + > +struct pru_suart_cnh_cntrl_config1 { > + u32 mode:2; > + u32 service_req:1; > + u32 asp_id:2; > + u32 reserved1:3; > + u32 serializer_num:4; > + u32 reserved2:4; > + u32 presacler:10; > + u32 over_sampling:2; > + u32 framing_mask:1; > + u32 break_mask:1; > + u32 timeout_mask:1; > + u32 reserved3:1; > +}; > + > +struct pru_suart_chn_config2_status { > + u32 bits_per_char:4; > + u32 reserved1:4; > + u32 data_len:4; > + u32 reserved2:4; > + u32 txrx_ready:1; > + u32 txrx_complete:1; > + u32 txrx_error:1; > + u32 txrx_underrun:1; > + u32 framing_error:1; > + u32 break_error:1; > + u32 timeout_error:1; > + u32 reserved3:8; > + u32 chn_state:1; > +}; > + > +struct pru_suart_regs_ovly { > + struct pru_suart_cnh_cntrl_config1 ch_ctrl_config1; > + struct pru_suart_chn_config2_status ch_config2_txrx_status; > + u32 ch_txrx_data; > + u32 reserved1; > +}; > + > +struct pru_suart_tx_cntx_priv { > + u32 asp_xsrctl_base; > + u32 asp_xbuf_base; > + u16 buff_addr; > + u8 buff_size; > + u8 bits_loaded; > +}; > + > +struct pru_suart_rx_cntx_priv { > + u32 asp_rbuf_base; > + u32 asp_rsrctl_base; > + u32 reserved1; > + u32 reserved2; > + u32 reserved3; > + u32 reserved4; > +}; > + > +struct suart_config { > + u8 tx_serializer; > + u8 rx_serializer; > + u16 tx_clk_divisor; > + u16 rx_clk_divisor; > + u8 tx_bits_per_char; > + u8 rx_bits_per_char; > + u8 oversampling; > + u8 bi_inter_mask; > + u8 fe_intr_mask; > +}; > + > +struct suart_handle { > + u16 uart_num; > + u16 uart_type; > + u16 uart_tx_channel; > + u16 uart_rx_channel; > + u16 uart_status; > +}; > + > +struct pruss_suart_iomap { > + void __iomem *mcasp_io_addr; > + void *p_fifo_buff_phys_base; > + void *p_fifo_buff_virt_base; > +}; > + > +struct pruss_suart_initparams { > + u32 tx_baud_value; > + u32 rx_baud_value; > + u32 oversampling; > +}; > + > +/* MCASP */ > +struct omapl_mcasp_regs_ovly { > + u32 revid; > + u32 rsvd0[3]; > + u32 pfunc; > + u32 pdir; > + u32 pdout; > + u32 pdin; > + u32 pdclr; > + u32 rsvd1[8]; > + u32 gblctl; > + u32 amute; > + u32 dlbctl; > + u32 ditctl; > + u32 rsvd2[3]; > + u32 rgblctl; > + u32 rmask; > + u32 rfmt; > + u32 afsrctl; > + u32 aclkrctl; > + u32 ahclkrctl; > + u32 rtdm; > + u32 rintctl; > + u32 rstat; > + u32 rslot; > + u32 rclkchk; > + u32 revtctl; > + u32 rsvd3[4]; > + u32 xgblctl; > + u32 xmask; > + u32 xfmt; > + u32 afsxctl; > + u32 aclkxctl; > + u32 ahclkxctl; > + u32 xtdm; > + u32 xintctl; > + u32 xstat; > + u32 xslot; > + u32 xclkchk; > + u32 xevtctl; > + u32 rsvd4[12]; > + u32 ditcsra[6]; > + u32 ditcsrb[6]; > + u32 ditudra[6]; > + u32 ditudrb[6]; > + u32 rsvd5[8]; > + u32 srctl0; > + u32 srctl1; > + u32 srctl2; > + u32 srctl3; > + u32 srctl4; > + u32 srctl5; > + u32 srctl6; > + u32 srctl7; > + u32 srctl8; > + u32 srctl9; > + u32 srctl10; > + u32 srctl11; > + u32 srctl12; > + u32 srctl13; > + u32 srctl14; > + u32 srctl15; > + u32 rsvd6[16]; > + u32 xbuf[16]; > + u32 rsvd7[16]; > + u32 rbuf[16]; > +}; > + > +/* > + * SUART Config regs > + */ > +struct suart_struct_pru_regs { > + u16 chn_ctrl; > + u16 chn_config1; > + u16 chn_config2; > + u16 chn_txrx_status; > + u32 chn_txrx_data; > +}; > + > +extern s32 pru_softuart_init(struct device *dev, > + struct pruss_suart_initparams *, > + const u8 *pru_suart_emu_code, u32 fw_size, > + u32 clk_rate_pruss, > + struct pruss_suart_iomap *pruss_ioaddr); > + > +extern s32 pru_softuart_open(struct suart_handle *h_suart); > + > +extern s32 pru_softuart_close(struct suart_handle *h_uart); > + > +extern s32 pru_softuart_setbaud(struct device *dev, > + struct suart_handle *h_uart, > + u16 tx_clk_divisor, u16 rx_clk_divisor); > + > +extern s32 pru_softuart_setdatabits(struct device *dev, > + struct suart_handle *h_uart, > + u16 tx_data_bits, u16 rx_data_bits); > + > +extern s32 pru_softuart_setconfig(struct device *dev, > + struct suart_handle *h_uart, > + struct suart_config *config_uart); > + > +extern s32 pru_softuart_getconfig(struct device *dev, > + struct suart_handle *h_uart, > + struct suart_config *config_uart); > + > +extern s32 pru_softuart_pending_tx_request(struct device *dev); > + > +extern s32 pru_softuart_write(struct device *dev, > + struct suart_handle *h_uart, > + u32 *pt_tx_data_buf, u16 data_len); > + > +extern s32 pru_softuart_read(struct device *dev, > + struct suart_handle *h_uart, > + u32 *pt_data_buf, u16 data_len); > + > +extern s32 suart_intr_clrmask(struct device *dev, u16 uart_num, > + u32 txrxmode, > + u32 intrmask); > + > +extern s32 pru_softuart_clr_tx_status(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 pru_softuart_get_tx_status(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 pru_softuart_clr_rx_status(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 pru_softuart_get_rx_status(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 pru_softuart_get_isrstatus(struct device *dev, u16 uart_num, > + u16 *txrx_flag); > + > +extern s32 pru_intr_clr_isrstatus(struct device *dev, u16 uart_num, > + u32 txrxmode); > + > +extern s32 suart_intr_getmask(struct device *dev, u16 uart_num, > + u32 txrxmode, > + u32 intrmask); > + > +extern s32 suart_intr_setmask(struct device *dev, u16 uart_num, > + u32 txrxmode, u32 intrmask); > + > +extern s32 pru_softuart_get_tx_data_len(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 pru_softuart_get_rx_data_len(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 suart_arm_to_pru_intr(struct device *dev, u16 uart_num); > + > +extern void pru_mcasp_deinit(void); > + > +extern s32 pru_softuart_read_data(struct device *dev, > + struct suart_handle *h_uart, > + u8 *p_data_buffer, s32 max_len, > + u32 *pdata_read); > + > +extern s32 pru_softuart_stop_receive(struct device *dev, > + struct suart_handle *h_uart); > + > +extern s32 suart_pru_to_host_intr_enable(struct device *dev, > + u16 uart_num, > + u32 txrxmode, s32 flag); > + > +extern void pru_set_fifo_timeout(struct device *dev, s16 timeout); > + > +extern void suart_mcasp_config(u32 tx_baud_value, > + u32 rx_baud_value, u32 oversampling, > + struct pruss_suart_iomap *pruss_ioaddr); > + > +extern void suart_mcasp_reset(struct pruss_suart_iomap *pruss_ioaddr); > + > +extern short suart_asp_baud_set(u32 tx_baud_value, > + u32 rx_baud_value, u32 oversampling, > + struct pruss_suart_iomap *pruss_ioaddr); > + > +extern short suart_asp_serializer_deactivate(u16 sr_num, > + struct pruss_suart_iomap *pruss_ioaddr); > + > +extern void suart_mcasp_tx_serialzier_set(u32 serializer_num, > + struct pruss_suart_iomap *pruss_ioaddr); > +#endif > diff --git a/drivers/tty/serial/pruss_suart_api.c > b/drivers/tty/serial/pruss_suart_api.c > new file mode 100644 > index 0000000..15178f5 > --- /dev/null > +++ b/drivers/tty/serial/pruss_suart_api.c > @@ -0,0 +1,1710 @@ > +/* > + * Copyright (C) 2010, 2011 Texas Instruments Incorporated > + * Author: Jitendra Kumar > + * > + * 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 > +#include > +#include "pruss_suart.h" > + > +static u8 uart_statu_table[8]; > +static struct pruss_suart_iomap suart_iomap; > + > +static u32 uart_rx[8] = {PRU_SUART0_CONFIG_RX_SER, > PRU_SUART1_CONFIG_RX_SER, > + PRU_SUART2_CONFIG_RX_SER, PRU_SUART3_CONFIG_RX_SER, > + PRU_SUART4_CONFIG_RX_SER, PRU_SUART5_CONFIG_RX_SER, > + PRU_SUART6_CONFIG_RX_SER, PRU_SUART7_CONFIG_RX_SER}; > + > +static u32 uart_tx[8] = {PRU_SUART0_CONFIG_TX_SER, > PRU_SUART1_CONFIG_TX_SER, > + PRU_SUART2_CONFIG_TX_SER, PRU_SUART3_CONFIG_TX_SER, > + PRU_SUART4_CONFIG_TX_SER, PRU_SUART5_CONFIG_TX_SER, > + PRU_SUART6_CONFIG_TX_SER, PRU_SUART7_CONFIG_TX_SER}; > + > +static u32 uart_config[8] = {PRU_SUART0_CONFIG_DUPLEX, > PRU_SUART1_CONFIG_DUPLEX, > + PRU_SUART2_CONFIG_DUPLEX, PRU_SUART3_CONFIG_DUPLEX, > + PRU_SUART4_CONFIG_DUPLEX, PRU_SUART5_CONFIG_DUPLEX, > + PRU_SUART6_CONFIG_DUPLEX, PRU_SUART7_CONFIG_DUPLEX}; > + > +static s32 pru_softuart_clr_rx_fifo(struct device *dev, > + struct suart_handle *h_uart); > +static s32 arm_to_pru_intr_init(struct device *dev); > + > +#if (PRU_ACTIVE == BOTH_PRU) > +static void pru_set_ram_data(struct device *dev, > + struct pruss_suart_iomap *pruss_ioaddr) > +{ > + u32 datatowrite; > + u32 i; > + struct pru_suart_regs_ovly *pru_suart_regs = NULL; > + u32 __iomem *p_sr_ctl_addr = (u32 __iomem *)(pruss_ioaddr-> > + mcasp_io_addr + 0x180); > + struct pru_suart_tx_cntx_priv *pru_suart_tx_priv = NULL; > + struct pru_suart_rx_cntx_priv *pru_suart_rx_priv = NULL; > + > + /* RX PRU - 0 Chanel 0-7 context information */ > + for (i = 0; i < 8; i++, pru_suart_regs++) { > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, > + 0x3, SUART_CHN_RX); > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, > + (0xF << SERIALIZER_OFFSET), > + ((0xF & uart_rx[i]) << SERIALIZER_OFFSET)); > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, > + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), > + (SUART_DEFAULT_OVRSMPL << > + SUART_DEFAULT_OVRSMPL_OFFSET)); > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_config2_txrx_status, > + 0xF, 8); > + if ((uart_config[i] & PRU_SUART_HALF_RX_DISABLED) == > + PRU_SUART_HALF_RX_DISABLED) { > + pruss_rmwl(dev, > + (u32) &pru_suart_regs->ch_config2_txrx_status, > + (0x1 << SUART_CHN_OFFSET), > + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); > + } else { > + pruss_rmwl(dev, > + (u32) &pru_suart_regs->ch_config2_txrx_status, > + (0x1 << SUART_CHN_OFFSET), > + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); > + iowrite32(MCASP_SRCTL_RX_MODE, p_sr_ctl_addr + > + uart_rx[i]); > + } > + /* > + * RX is active by default, write the dummy received data at > + * PRU RAM addr 0x1FC to avoid memory corruption. > + */ > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_txrx_data, > + 0xFFFF, RX_DEFAULT_DATA_DUMP_ADDR); > + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, 0xFFFF, 0); > + /* SUART1 RX context base addr */ > + pru_suart_rx_priv = (struct pru_suart_rx_cntx_priv *) > + (PRU0_DATARAM_OFFSET + (0x090 + (i * 0x020))); > + datatowrite = (MCASP_RBUF_BASE_ADDR + (uart_rx[i] << 2)); > + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rbuf_base, > + datatowrite); > + datatowrite = (MCASP_SRCTL_BASE_ADDR + (uart_rx[i] << 2)); > + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rsrctl_base, > + datatowrite); > + } > + > + /* PRU1 RAM BASE ADDR */ > + pru_suart_regs = (struct pru_suart_regs_ovly *) PRU1_DATARAM_OFFSET; > + > + /* TX PRU - 1 */ > + /* Channel 0-7 context information */ > + for (i = 0; i < 8; i++, pru_suart_regs++) { > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, > + 0x3, SUART_CHN_TX); > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, > + (0xF << SERIALIZER_OFFSET), > + ((0xF & uart_tx[i]) << SERIALIZER_OFFSET)); > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, > + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), > + (SUART_DEFAULT_OVRSMPL << > + SUART_DEFAULT_OVRSMPL_OFFSET)); > + pruss_rmwl(dev, > + (u32) &pru_suart_regs->ch_config2_txrx_status, 0xF, 8); > + > + if ((uart_config[i] & PRU_SUART_HALF_TX_DISABLED) == > + PRU_SUART_HALF_TX_DISABLED) { > + pruss_rmwl(dev, (u32) > + &pru_suart_regs->ch_config2_txrx_status, > + (0x1 << SUART_CHN_OFFSET), > + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); > + } else { > + pruss_rmwl(dev, > + (u32) &pru_suart_regs->ch_config2_txrx_status, > + (0x1 << SUART_CHN_OFFSET), > + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); > + iowrite32(MCASP_SRCTL_TX_MODE, > + p_sr_ctl_addr + uart_tx[i]); > + } > + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, 0xFFFF, 1); > + > + /* SUART1 TX context base addr */ > + pru_suart_tx_priv = (struct pru_suart_tx_cntx_priv *) > + (PRU1_DATARAM_OFFSET + (0x0B0 + (i * 0x02C))); > + datatowrite = (MCASP_SRCTL_BASE_ADDR + (uart_tx[i] << 2)); > + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xsrctl_base, > + datatowrite); > + datatowrite = (MCASP_XBUF_BASE_ADDR + (uart_tx[i] << 2)); > + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xbuf_base, > + datatowrite); > + /* SUART1 TX formatted data base addr */ > + datatowrite = (0x0090 + (i * 0x002C)); > + pruss_writel(dev, (u32) &pru_suart_tx_priv->buff_addr, > + datatowrite); > + } > +} > +#else > +static void pru_set_ram_data(struct device *dev, > + struct pruss_suart_iomap *pruss_ioaddr) > +{ > + > + struct pru_suart_regs_ovly *pru_suart_regs = > + (struct pru_suart_regs_ovly *)pruss_ioaddr->pru_io_addr; > + u32 i; > + u32 *p_sr_ctl_addr = (u32 *)(pruss_ioaddr->mcasp_io_addr + 0x180); > + struct pru_suart_tx_cntx_priv *pru_suart_tx_priv = NULL; > + struct pru_suart_rx_cntx_priv *pru_suart_rx_priv = NULL; > + > + /* Channel 0 context information is Tx */ > + for (i = 0; i < 4; i++, pru_suart_regs++) { > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, > + 0x3, SUART_CHN_TX); > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, > + (0xF << SERIALIZER_OFFSET), > + ((0xF & uart_tx[i]) << SERIALIZER_OFFSET)); > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, > + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), > + (SUART_DEFAULT_OVRSMPL << > + SUART_DEFAULT_OVRSMPL_OFFSET)); > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_config2_txrx_status, > + 0xF, 8); > + if ((uart_config[i] & PRU_SUART_HALF_TX_DISABLED) == > + PRU_SUART_HALF_TX_DISABLED){ > + pruss_rmwl(dev, (u32) > + &pru_suart_regs->ch_config2_txrx_status, > + (0x1 << SUART_CHN_OFFSET), > + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); > + } else { > + pruss_rmwl(dev, > + (u32) &pru_suart_regs->ch_config2_txrx_status, > + (0x1 << SUART_CHN_OFFSET), > + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); > + iowrite32(MCASP_SRCTL_TX_MODE, > + p_sr_ctl_addr + uart_tx[i]); > + } > + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, 0xFFFF, 1); > + > + /* SUART1 TX context base addr */ > + pru_suart_tx_priv = (struct pru_suart_tx_cntx_priv *) > + (PRU0_DATARAM_OFFSET + (0x0B0 + (i * 0x50))); > + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xsrctl_base, > + (MCASP_SRCTL_BASE_ADDR + (uart_tx[i] << 2))); > + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xbuf_base, > + (MCASP_XBUF_BASE_ADDR + (uart_tx[i] << 2))); > + /* SUART1 TX formatted data base addr */ > + pruss_writel(dev, (u32) &pru_suart_tx_priv->buff_addr, > + (0x0090 + (i * 0x050))); > + > + /* Channel 1 is Rx context information */ > + pru_suart_regs++; > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, > + 0x3, SUART_CHN_RX); > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, > + (0xF << SERIALIZER_OFFSET), > + ((0xF & uart_rx[i]) << SERIALIZER_OFFSET)); > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, > + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), > + (SUART_DEFAULT_OVRSMPL << > + SUART_DEFAULT_OVRSMPL_OFFSET)); > + pruss_rmwl(dev, > + (u32) &pru_suart_regs->ch_config2_txrx_status, 0xF, 8); > + > + if ((uart_config[i] & PRU_SUART_HALF_RX_DISABLED) == > + PRU_SUART_HALF_RX_DISABLED) { > + pruss_rmwl(dev, > + (u32) &pru_suart_regs->ch_config2_txrx_status, > + (0x1 << SUART_CHN_OFFSET), > + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); > + } else { > + pruss_rmwl(dev, > + (u32) &pru_suart_regs->ch_config2_txrx_status, > + (0x1 << SUART_CHN_OFFSET), > + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); > + iowrite32(MCASP_SRCTL_RX_MODE, > + p_sr_ctl_addr + uart_rx[i]); > + } > + /* > + * RX is active by default, write the dummy received data > + * at PRU RAM addr 0x1FC to avoid memory corruption > + */ > + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_txrx_data, > + 0xFFFF, RX_DEFAULT_DATA_DUMP_ADDR); > + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, 0xFFFF, 0); > + /* SUART1 RX context base addr */ > + pru_suart_rx_priv = (struct pru_suart_rx_cntx_priv *) > + (PRU0_DATARAM_OFFSET + (0x0C0 + (i * 0x50))); > + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rbuf_base, > + (MCASP_RBUF_BASE_ADDR + (uart_rx[i] << 2))); > + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rsrctl_base, > + (MCASP_SRCTL_BASE_ADDR + (uart_rx[i] << 2))); > + } > +} > +#endif > + > +static void pru_set_rx_tx_mode(struct device *dev, u32 pru_mode, u32 > pru_num) > +{ > + u32 pru_offset; > + > + if (pru_num == PRUSS_NUM0) > + pru_offset = PRU_SUART_PRU0_RX_TX_MODE; > + else if (pru_num == PRUSS_NUM1) > + pru_offset = PRU_SUART_PRU1_RX_TX_MODE; > + else > + return; > + pruss_writeb(dev, pru_offset, (u8) pru_mode); > +} > + > +static void pru_set_delay_count(struct device *dev, u32 pru_freq) > +{ > + u32 delay_cnt; > + > + if (pru_freq == PRU_CLK_228) > + delay_cnt = 5; > + else if (pru_freq == PRU_CLK_186) > + delay_cnt = 5; > + else > + delay_cnt = 3; > + > + /* PRU 0 */ > + pruss_writeb(dev, PRU_SUART_PRU0_DELAY_OFFSET, > + (u8) delay_cnt); > + > + /* PRU 1 */ > + pruss_writeb(dev, PRU_SUART_PRU1_DELAY_OFFSET, > + (u8) delay_cnt); > +} > + > +static s32 suart_set_pru_id(struct device *dev, u32 pru_no) > +{ > + u32 offset; > + u8 reg_val = 0; > + > + if (PRUSS_NUM0 == pru_no) > + offset = PRU_SUART_PRU0_ID_ADDR; > + else if (PRUSS_NUM1 == pru_no) > + offset = PRU_SUART_PRU1_ID_ADDR; > + else > + return -EINVAL; > + > + reg_val = pru_no; > + pruss_writeb(dev, offset, reg_val); > + > + return 0; > +} > + > +/* > + * suart Initialization routine > + */ > +s32 pru_softuart_init(struct device *dev, > + struct pruss_suart_initparams *init_params, > + const u8 *pru_suart_emu_code, u32 fw_size, > + u32 clk_rate_pruss, > + struct pruss_suart_iomap *pruss_ioaddr) > +{ > + u32 datatowrite[128] = {0}; > + s16 status = 0; > + s16 idx; > + s16 retval; > + u16 i; > + > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) && > + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) > + return -EINVAL; > + > + suart_iomap.mcasp_io_addr = pruss_ioaddr->mcasp_io_addr; > + suart_iomap.p_fifo_buff_phys_base = > + pruss_ioaddr->p_fifo_buff_phys_base; > + suart_iomap.p_fifo_buff_virt_base = > + pruss_ioaddr->p_fifo_buff_virt_base; > + /* Configure McASP0 */ > + suart_mcasp_config(init_params->tx_baud_value, > + init_params->rx_baud_value, > + init_params->oversampling, pruss_ioaddr); > + pruss_enable(dev, PRUSS_NUM0); > + > + if (PRU1_MODE != PRU_MODE_INVALID) > + pruss_enable(dev, PRUSS_NUM1); > + > + /* Reset PRU RAM */ > + for (i = 0; i < (PRU0_DATARAM_SIZE / sizeof(int)); i++) > + pruss_writel(dev, (PRU0_DATARAM_OFFSET + (i * sizeof(int))), > + datatowrite[i]); > + if (PRU1_MODE != PRU_MODE_INVALID) { > + for (i = 0; i < (PRU1_DATARAM_SIZE / sizeof(int)); i++) > + pruss_writel(dev, (PRU1_DATARAM_OFFSET + > + (i * sizeof(int))), datatowrite[i]); > + } > + > + pruss_load(dev, PRUSS_NUM0, (u32 *)pru_suart_emu_code, > + (fw_size / sizeof(u32))); > + if (PRU1_MODE != PRU_MODE_INVALID) > + pruss_load(dev, PRUSS_NUM1, (u32 *)pru_suart_emu_code, > + (fw_size / sizeof(u32))); > + > + retval = arm_to_pru_intr_init(dev); > + if (-1 == retval) > + return status; > + pru_set_delay_count(dev, clk_rate_pruss); > + suart_set_pru_id(dev, PRUSS_NUM0); > + if (PRU1_MODE != PRU_MODE_INVALID) > + suart_set_pru_id(dev, PRUSS_NUM1); > + > + pru_set_rx_tx_mode(dev, PRU0_MODE, PRUSS_NUM0); > + if (PRU1_MODE != PRU_MODE_INVALID) > + pru_set_rx_tx_mode(dev, PRU1_MODE, PRUSS_NUM1); > + > + pru_set_ram_data(dev, pruss_ioaddr); > + pruss_run(dev, PRUSS_NUM0); > + > + if (PRU1_MODE != PRU_MODE_INVALID) > + pruss_run(dev, PRUSS_NUM1); > + > + /* Initialize uart_statu_table */ > + for (idx = 0; idx < 8; idx++) > + uart_statu_table[idx] = ePRU_SUART_UART_FREE; > + > + return status; > +} > + > +void pru_set_fifo_timeout(struct device *dev, s16 timeout) > +{ > + pruss_writew(dev, PRU_SUART_PRU0_IDLE_TIMEOUT_OFFSET, (u16) timeout); > + if (PRU1_MODE != PRU_MODE_INVALID) > + pruss_writew(dev, PRU_SUART_PRU1_IDLE_TIMEOUT_OFFSET, > + (u16) timeout); > +} > + > +void pru_mcasp_deinit(void) > +{ > + suart_mcasp_reset(&suart_iomap); > +} > + > +/* suart Instance open routine */ > +s32 pru_softuart_open(struct suart_handle *h_suart) > +{ > + s16 status = 0; > + u16 uart_num = h_suart->uart_num - 1; > + > + if (uart_statu_table[h_suart->uart_num - 1] == > + ePRU_SUART_UART_IN_USE) { > + return -EUSERS; > + } else { > + h_suart->uart_type = uart_config[uart_num]; > + h_suart->uart_tx_channel = uart_tx[uart_num]; > + h_suart->uart_rx_channel = uart_rx[uart_num]; > + h_suart->uart_status = ePRU_SUART_UART_IN_USE; > + uart_statu_table[h_suart->uart_num - 1] = > + ePRU_SUART_UART_IN_USE; > + } > + return status; > +} > + > +/* suart instance close routine */ > +s32 pru_softuart_close(struct suart_handle *h_uart) > +{ > + s16 status = 0; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } else { > + uart_statu_table[h_uart->uart_num - 1] = > + ePRU_SUART_UART_FREE; > + /* Reset the Instance to Invalid */ > + h_uart->uart_num = PRU_SUART_UARTx_INVALID; > + h_uart->uart_status = ePRU_SUART_UART_FREE; > + } > + return status; > +} > + > +static s32 search_chnum(u16 uart_num, u16 *ch_num, u32 *pru_offset, u16 > mode) > +{ > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) > + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + *ch_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; > + if (uart_num <= 4) { > + *pru_offset = PRU_SUART_PRU0_CH0_OFFSET; > + } else { > + *pru_offset = PRU_SUART_PRU1_CH0_OFFSET; > + *ch_num -= 8; > + } > + (mode == 2) ? ++*ch_num : *ch_num; > + } else if (mode == 1) { > + if (PRU0_MODE == PRU_MODE_TX_ONLY) > + *pru_offset = PRU_SUART_PRU0_CH0_OFFSET; > + else if (PRU1_MODE == PRU_MODE_TX_ONLY) > + *pru_offset = PRU_SUART_PRU1_CH0_OFFSET; > + } else if (mode == 2) { > + if (PRU0_MODE == PRU_MODE_RX_ONLY) > + *pru_offset = PRU_SUART_PRU0_CH0_OFFSET; > + else if (PRU1_MODE == PRU_MODE_RX_ONLY) > + *pru_offset = PRU_SUART_PRU1_CH0_OFFSET; > + } > + return 0; > +} > + > +/* > + * suart routine for setting relative baud rate > + */ > +s32 pru_softuart_setbaud(struct device *dev, struct suart_handle *h_uart, > + u16 tx_clk_divisor, u16 rx_clk_divisor) > +{ > + u32 offset; > + u32 pru_offset; > + s16 status = 0; > + u16 ch_num = h_uart->uart_num - 1; > + u16 regval = 0; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + > + /* Set the clock divisor value s32o the McASP */ > + if ((tx_clk_divisor > 385) || (tx_clk_divisor == 0)) > + return -EINVAL; > + if ((rx_clk_divisor > 385) || (rx_clk_divisor == 0)) > + return -EINVAL; > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); > + > + if (tx_clk_divisor != 0) { > + offset = pru_offset + > + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG1_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®val); > + regval &= (~0x3FF); > + regval |= tx_clk_divisor; > + pruss_writew(dev, offset, regval); > + } > + if (PRU0_MODE == PRU_MODE_RX_ONLY) { > + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; > + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { > + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; > + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || > + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + ch_num++; > + } else { > + return 0; > + } > + regval = 0; > + if (rx_clk_divisor != 0) { > + offset = pru_offset + > + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG1_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®val); > + regval &= (~0x3FF); > + regval |= tx_clk_divisor; > + pruss_writew(dev, offset, regval); > + } > + return status; > +} > + > +/* > + * suart routine for setting number of bits per character for a specific > uart > + */ > +s32 pru_softuart_setdatabits(struct device *dev, struct suart_handle > *h_uart, > + u16 tx_data_bits, u16 rx_data_bits) > +{ > + u32 offset; > + u32 pru_offset; > + s16 status = 0; > + u16 ch_num = h_uart->uart_num - 1; > + u32 reg_val; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + > + /* > + * NOTE: > + * The supported data bits are 6,7,8,9,10,11,12 bits per character > + */ > + > + if ((tx_data_bits < ePRU_SUART_DATA_BITS6) > + || (tx_data_bits > ePRU_SUART_DATA_BITS12)) > + return -EINVAL; > + > + if ((rx_data_bits < ePRU_SUART_DATA_BITS6) > + || (rx_data_bits > ePRU_SUART_DATA_BITS12)) > + return -EINVAL; > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); > + > + if (tx_data_bits != 0) { > + offset = pru_offset + > + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG2_OFFSET; > + pruss_readb(dev, offset, (u8 *) ®_val); > + reg_val &= ~(0xF); > + reg_val |= tx_data_bits; > + pruss_writeb(dev, offset, (u8) reg_val); > + } > + if (PRU0_MODE == PRU_MODE_RX_ONLY) { > + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; > + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { > + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; > + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) > + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + ch_num++; > + } else { > + return 0; > + } > + if (rx_data_bits != 0) { > + offset = pru_offset + > + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG2_OFFSET; > + pruss_readb(dev, offset, (u8 *) ®_val); > + reg_val &= ~(0xF); > + reg_val |= rx_data_bits; > + pruss_writeb(dev, offset, (u8) rx_data_bits); > + } > + > + return status; > +} > + > +/* > + * suart routine to configure specific uart > + */ > +s32 pru_softuart_setconfig(struct device *dev, struct suart_handle > *h_uart, > + struct suart_config *config_uart) > +{ > + u32 offset; > + u32 pru_offset; > + s16 status = 0; > + u16 ch_num = h_uart->uart_num - 1; > + u16 reg_val = 0; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + > + /* > + * NOTE: > + * Dependent baud rate for the given UART,the value MUST BE LESS THAN OR > + * EQUAL TO 64, preScalarValue <= 64 > + */ > + if ((config_uart->tx_clk_divisor > 384) > + || (config_uart->rx_clk_divisor > 384)) { > + return -EINVAL; > + } > + if ((config_uart->tx_bits_per_char < 8) > + || (config_uart->tx_bits_per_char > 14)) { > + return -EINVAL; > + } > + if ((config_uart->rx_bits_per_char < 8) > + || (config_uart->rx_bits_per_char > 14)) { > + return -EINVAL; > + } > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); > + > + /* > + * Configuring the Transmit part of the given UART > + * Serializer has been as TX in mcasp config, by writing 1 in bits > + * corresponding to tx serializer in PFUNC regsiter ie already set > + * to GPIO mode PRU code will set then back to MCASP mode once TX > + * request for that serializer is posted.It is required because at this > + * pos32 Mcasp is accessed by both PRU and DSP have lower priority for > + * Mcasp in comparison to PRU and DPS keeps on looping there only > + * > + * suart_mcasp_tx_serialzier_set > + * (config_uart->tx_serializer, &suart_iomap); > + */ > + > + /* Configuring TX serializer */ > + if (config_uart->tx_serializer != PRU_SUART_SERIALIZER_NONE) { > + offset = pru_offset + > + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CTRL_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + reg_val = reg_val | (config_uart->tx_serializer << > + PRU_SUART_CH_CTRL_SR_SHIFT); > + pruss_writew(dev, offset, reg_val); > + offset = pru_offset + > + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG1_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + reg_val = reg_val | (config_uart->tx_clk_divisor << > + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); > + pruss_writew(dev, offset, reg_val); > + offset = pru_offset + > + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG2_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + reg_val = reg_val | (config_uart->tx_bits_per_char << > + PRU_SUART_CH_CONFIG2_BITPERCHAR_SHIFT); > + pruss_writew(dev, offset, reg_val); > + } > + > + if (PRU0_MODE == PRU_MODE_RX_ONLY) { > + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; > + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { > + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; > + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || > + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + ch_num++; > + } else { > + return 0; > + } > + > + /* Configuring the Transmit part of the given UART */ > + if (config_uart->rx_serializer != PRU_SUART_SERIALIZER_NONE) { > + /* Configuring RX serializer */ > + offset = pru_offset + > + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CTRL_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + reg_val = reg_val | (config_uart->rx_serializer << > + PRU_SUART_CH_CTRL_SR_SHIFT); > + pruss_writew(dev, offset, reg_val); > + > + /* Configuring RX prescalar value and Oversampling */ > + offset = pru_offset + > + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG1_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + reg_val = reg_val | (config_uart->rx_clk_divisor << > + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT) | > + (config_uart->oversampling << > + PRU_SUART_CH_CONFIG1_OVS_SHIFT); > + pruss_writew(dev, offset, reg_val); > + > + /* Configuring RX bits per character value */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) > + + PRU_SUART_CH_CONFIG2_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + reg_val = reg_val | (config_uart->rx_bits_per_char << > + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); > + pruss_writew(dev, offset, reg_val); > + } > + return status; > +} > + > +/* > + * suart routine for getting the number of bytes transfered > + */ > +s32 pru_softuart_get_tx_data_len(struct device *dev, > + struct suart_handle *h_uart) > +{ > + u32 offset; > + u32 pru_offset; > + u16 ch_num = h_uart->uart_num - 1; > + u16 read_value = 0; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); > + > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG2_OFFSET; > + pruss_readw(dev, offset, (u16 *) &read_value); > + read_value = ((read_value & PRU_SUART_CH_CONFIG1_DIVISOR_MASK) > + >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); > + return read_value; > +} > + > +/* > + * suart routine for getting the number of bytes received > + */ > +s32 pru_softuart_get_rx_data_len(struct device *dev, > + struct suart_handle *h_uart) > +{ > + u32 offset; > + u32 pru_offset; > + u16 ch_num = h_uart->uart_num - 1; > + u16 read_value = 0; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); > + > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG2_OFFSET; > + pruss_readw(dev, offset, (u16 *) &read_value); > + read_value = ((read_value & PRU_SUART_CH_CONFIG1_DIVISOR_MASK) > + >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); > + return read_value; > +} > + > +/* > + * suart routine to get the configuration information from a specific > uart > + */ > +s32 pru_softuart_getconfig(struct device *dev, > + struct suart_handle *h_uart, > + struct suart_config *config_uart) > +{ > + u32 offset; > + u32 pru_offset; > + u16 ch_num = h_uart->uart_num - 1; > + u16 reg_val = 0; > + s16 status = 0; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + > + /* > + * NOTE: > + * Dependent baud rate for the given UART,the value MUST BE LESS THAN OR > + * EQUAL TO 64, preScalarValue <= 64 > + */ > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); > + > + /* Configuring the Transmit part of the given UART */ > + /* Configuring TX serializer */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CTRL_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + config_uart->tx_serializer = ((reg_val & PRU_SUART_CH_CTRL_SR_MASK) >> > + PRU_SUART_CH_CTRL_SR_SHIFT); > + /* Configuring TX prescalar value */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG1_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + config_uart->tx_clk_divisor = ((reg_val & > + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) >> > + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); > + > + /* Configuring TX bits per character value */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG2_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + config_uart->tx_bits_per_char = ((reg_val & > + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) >> > + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); > + > + if (PRU0_MODE == PRU_MODE_RX_ONLY) { > + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; > + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { > + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; > + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || > + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + ch_num++; > + } else { > + return 0; > + } > + /* Configuring the Transmit part of the given UART */ > + /* Configuring RX serializer */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CTRL_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + config_uart->rx_serializer = ((reg_val & PRU_SUART_CH_CTRL_SR_MASK) >> > + PRU_SUART_CH_CTRL_SR_SHIFT); > + > + /* Configuring RX prescalar value and oversampling */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG1_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + config_uart->rx_clk_divisor = ((reg_val & > + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) > + >> PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); > + config_uart->oversampling = ((reg_val & > + PRU_SUART_CH_CONFIG1_OVS_MASK) >> > + PRU_SUART_CH_CONFIG1_OVS_SHIFT); > + > + /* Configuring RX bits per character value */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG2_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + config_uart->rx_bits_per_char = ((reg_val & > + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) > + >> PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); > + > + return status; > +} > + > +s32 pru_softuart_pending_tx_request(struct device *dev) > +{ > + u32 offset = 0; > + u32 ISR_value = 0; > + > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) > + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + return 0; > + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { > + /* Read PRU Interrupt Status Register from PRU */ > + offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); > + pruss_readl(dev, offset, (u32 *)&ISR_value); > + if ((ISR_value & 0x1) == 0x1) > + return -EINVAL; > + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { > + /* Read PRU Interrupt Status Register from PRU */ > + offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); > + pruss_readl(dev, offset, (u32 *)&ISR_value); > + if ((ISR_value & 0x2) == 0x2) > + return -EINVAL; > + } else { > + return 0; > + } > + > + return 0; > +} > + > +/* > + * suart data transmit routine > + */ > +s32 pru_softuart_write(struct device *dev, struct suart_handle *h_uart, > + u32 *pt_tx_data_buf, u16 data_len) > +{ > + u32 offset = 0; > + u32 pru_offset; > + s16 status = 0; > + u16 ch_num = h_uart->uart_num - 1; > + u16 reg_val = 0; > + u16 pru_num; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || > + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) > + pru_num = h_uart->uart_num; > + else if (PRU0_MODE == PRU_MODE_TX_ONLY) > + pru_num = 0; > + else if (PRU1_MODE == PRU_MODE_TX_ONLY) > + pru_num = 1; > + else > + return 0; > + > + /* Writing data length to SUART channel register */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG2_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + reg_val &= ~PRU_SUART_CH_CONFIG2_DATALEN_MASK; > + reg_val = reg_val | (data_len << PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); > + pruss_writew(dev, offset, reg_val); > + > + /* Writing the data pos32er to channel TX data pointer */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_TXRXDATA_OFFSET; > + pruss_writel(dev, offset, (u32) *pt_tx_data_buf); > + > + /* Service Request to PRU */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CTRL_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | > + PRU_SUART_CH_CTRL_SREQ_MASK); > + reg_val |= (PRU_SUART_CH_CTRL_TX_MODE << > + PRU_SUART_CH_CTRL_MODE_SHIFT) | (PRU_SUART_CH_CTRL_SREQ << > + PRU_SUART_CH_CTRL_SREQ_SHIFT); > + pruss_writew(dev, offset, reg_val); > + > + /* generate ARM->PRU event */ > + suart_arm_to_pru_intr(dev, pru_num); > + > + return status; > +} > + > +/* > + * suart data receive routine > + */ > +s32 pru_softuart_read(struct device *dev, struct suart_handle *h_uart, > + u32 *ptDataBuf, u16 data_len) > +{ > + u32 offset = 0; > + u32 pru_offset; > + s16 status = 0; > + u16 ch_num = h_uart->uart_num - 1; > + u16 reg_val = 0; > + u16 pru_num; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || > + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + /* channel starts from 0 and uart instance starts from 1 */ > + ch_num = (h_uart->uart_num * > + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; > + pru_num = h_uart->uart_num; > + } else if (PRU0_MODE == PRU_MODE_RX_ONLY) { > + pru_num = 0; > + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { > + pru_num = 1; > + } else { > + return 0; > + } > + /* Writing data length to SUART channel register */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG2_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + reg_val &= ~PRU_SUART_CH_CONFIG2_DATALEN_MASK; > + reg_val = reg_val | (data_len << PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); > + pruss_writew(dev, offset, reg_val); > + > + /* Writing the data pos32er to channel RX data pointer */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_TXRXDATA_OFFSET; > + pruss_writel(dev, offset, (u32) *ptDataBuf); > + > + /* Service Request to PRU */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CTRL_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | > + PRU_SUART_CH_CTRL_SREQ_MASK); > + reg_val |= (PRU_SUART_CH_CTRL_RX_MODE << > + PRU_SUART_CH_CTRL_MODE_SHIFT) | (PRU_SUART_CH_CTRL_SREQ << > + PRU_SUART_CH_CTRL_SREQ_SHIFT); > + pruss_writew(dev, offset, reg_val); > + > + /* enable the timeout s32errupt */ > + suart_intr_setmask(dev, h_uart->uart_num, PRU_RX_INTR, > + CHN_TXRX_IE_MASK_TIMEOUT); > + > + /* generate ARM->PRU event */ > + suart_arm_to_pru_intr(dev, pru_num); > + > + return status; > +} > + > +/* > + * suart routine to read the data from the RX FIFO > + */ > +s32 pru_softuart_read_data(struct device *dev, struct suart_handle > *h_uart, > + u8 *p_data_buffer, s32 max_len, > + u32 *pdata_read) > +{ > + s16 ret_val = 0; > + u8 *psrc_addr = NULL; > + u32 data_read = 0; > + u32 data_len = 0; > + u32 char_len = 0; > + u32 offset = 0; > + u32 pru_offset; > + u16 ch_num = h_uart->uart_num - 1; > + u16 status = 0; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); > + > + /* Get the data pos32er from channel RX data pointer */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_TXRXDATA_OFFSET; > + pruss_readb_multi(dev, offset, (u8 *) &psrc_addr, 4); > + > + /* Reading data length from SUART channel register */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CONFIG2_OFFSET; > + pruss_readw(dev, offset, (u16 *) &data_len); > + > + /* read the character length */ > + char_len = data_len & PRU_SUART_CH_CONFIG2_BITPERCHAR_MASK; > + char_len -= 2; /* remove the START & STOP bit */ > + > + data_len &= PRU_SUART_CH_CONFIG2_DATALEN_MASK; > + data_len = data_len >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT; > + data_len++; > + > + /* if the character length is greater than 8, then the size doubles */ > + if (char_len > 8) > + data_len *= 2; > + > + /* Check if the time-out had occured. If, yes, then we need to find the > + * number of bytes read from PRU. Else, we need to > + * read the requested bytes > + */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_TXRXSTATUS_OFFSET; > + pruss_readb(dev, offset, (u8 *) &status); > + if (CHN_TXRX_STATUS_TIMEOUT == (status & CHN_TXRX_STATUS_TIMEOUT)) { > + /* determine the number of bytes read s32o the FIFO */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) > + + PRU_SUART_CH_BYTESDONECNTR_OFFSET; > + pruss_readb(dev, offset, (u8 *) &data_read); > + > + /* if the character length is greater than 8, > + then the size doubles */ > + if (char_len > 8) > + data_read *= 2; > + > +/* > + * the data corresponding is loaded in second > + * half during the timeout > + */ > + if (data_read > data_len) { > + data_read -= data_len; > + psrc_addr += data_len; > + } > + > + pru_softuart_clr_rx_fifo(dev, h_uart); > + } else { > + data_read = data_len; > +/* > + * if the bit is set, the data is in the first > + * half of the FIFO else the data is in the second half > + */ > + /* Determine the buffer index by reading FIFO_OddEven flag*/ > + if (status & CHN_TXRX_STATUS_CMPLT) > + psrc_addr += data_len; > + } > + > + /* we should be copying only max len given by the application */ > + if (data_read > max_len) > + data_read = max_len; > + > +/* evaluate the virtual address of the FIFO address > + * based on the physical addr > + */ > + psrc_addr = (u8 *)((u32) psrc_addr - > + (u32) suart_iomap.p_fifo_buff_phys_base + > + (u32) suart_iomap.p_fifo_buff_virt_base); > + > + /* Now we have both the data length and the source address. copy */ > + for (offset = 0; offset < data_read; offset++) > + *p_data_buffer++ = *psrc_addr++; > + *pdata_read = data_read; > + ret_val = 0; > + > + return ret_val; > +} > + > +/* > + * suart routine to disable the receive functionality. > + * This routine stops the PRU from receiving on selected > + * UART and also disables the McASP serializer corresponding > + * to this UART Rx line. > + */ > +s32 pru_softuart_stop_receive(struct device *dev, struct suart_handle > *h_uart) > +{ > + u16 ret_status = 0; > + u32 offset; > + u32 pru_offset; > + u16 ch_num = h_uart->uart_num - 1; > + u16 status; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); > + > + /* read the existing value of status flag */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_TXRXSTATUS_OFFSET; > + pruss_readb(dev, offset, (u8 *) &status); > + > + /* we need to clear the busy bit corresponding to receive channel */ > + status &= ~(CHN_TXRX_STATUS_RDY); > + pruss_writeb(dev, offset, (u8) status); > + > + /* get the serizlizer number being used for this Rx channel */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CTRL_OFFSET; > + pruss_readw(dev, offset, (u16 *) &status); > + status &= PRU_SUART_CH_CTRL_SR_MASK; > + status = status >> PRU_SUART_CH_CTRL_SR_SHIFT; > + > + /* we need to de-activate the serializer corresponding to this rx */ > + ret_status = suart_asp_serializer_deactivate(status, &suart_iomap); > + > + return ret_status; > +} > + > +/* > + * suart routine to get the tx status for a specific uart > + */ > +s32 pru_softuart_get_tx_status(struct device *dev, struct suart_handle > *h_uart) > +{ > + u32 offset; > + u32 pru_offset; > + u16 status = 0; > + u16 ch_num = h_uart->uart_num - 1; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_TXRXSTATUS_OFFSET; > + pruss_readb(dev, offset, (u8 *) &status); > + return status; > +} > + > +s32 pru_softuart_clr_tx_status(struct device *dev, struct suart_handle > *h_uart) > +{ > + u32 offset; > + u32 pru_offset; > + u16 status = 0; > + u16 ch_num = h_uart->uart_num - 1; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); > + > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_TXRXSTATUS_OFFSET; > + pruss_readb(dev, offset, (u8 *) &status); > + status &= ~(0x2); > + pruss_writeb(dev, offset, (u8) status); > + return status; > +} > + > +/* > + * suart routine to get the rx status for a specific uart > + */ > +s32 pru_softuart_get_rx_status(struct device *dev, struct suart_handle > *h_uart) > +{ > + u32 offset; > + u32 pru_offset; > + u16 status = 0; > + u16 ch_num = h_uart->uart_num - 1; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); > + > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_TXRXSTATUS_OFFSET; > + pruss_readb(dev, offset, (u8 *) &status); > + return status; > +} > + > +static s32 pru_softuart_clr_rx_fifo(struct device *dev, > + struct suart_handle *h_uart) > +{ > + u32 offset; > + u32 pru_offset; > + u16 status = 0; > + u16 ch_num = h_uart->uart_num - 1; > + u16 reg_val; > + u16 uart_num; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + uart_num = h_uart->uart_num; > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); > + if (PRU0_MODE == PRU_MODE_RX_ONLY) > + uart_num = 0; > + else if (PRU1_MODE == PRU_MODE_RX_ONLY) > + uart_num = 1; > + > + /* Reset the number of bytes read into the FIFO */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) > + + PRU_SUART_CH_BYTESDONECNTR_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + reg_val &= 0x00; > + pruss_writew(dev, offset, reg_val); > + > + > + /* Service Request to PRU */ > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_CTRL_OFFSET; > + pruss_readw(dev, offset, (u16 *) ®_val); > + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | PRU_SUART_CH_CTRL_SREQ_MASK); > + reg_val |= (PRU_SUART_CH_CTRL_RX_MODE << PRU_SUART_CH_CTRL_MODE_SHIFT) | > + (PRU_SUART_CH_CTRL_SREQ << PRU_SUART_CH_CTRL_SREQ_SHIFT); > + pruss_writew(dev, offset, reg_val); > + suart_intr_setmask(dev, h_uart->uart_num, PRU_RX_INTR, > + CHN_TXRX_IE_MASK_TIMEOUT); > + > + /* generate ARM->PRU event */ > + suart_arm_to_pru_intr(dev, uart_num); > + > + return status; > +} > + > +s32 pru_softuart_clr_rx_status(struct device *dev, struct suart_handle > *h_uart) > +{ > + u32 offset; > + u32 pru_offset; > + u16 status = 0; > + u16 ch_num = h_uart->uart_num - 1; > + > + if (h_uart == NULL) { > + WARN_ON(1); > + return -EINVAL; > + } > + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); > + > + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + > + PRU_SUART_CH_TXRXSTATUS_OFFSET; > + pruss_readb(dev, offset, (u8 *) &status); > + status &= ~(0x3C); > + pruss_writeb(dev, offset, (u8) status); > + return status; > +} > + > +/* > + * suart_s32r_status_read: Gets the Global Interrupt status register > + * for the specified SUART. > + * uart_num < 1 to 6 > > + * txrx_flag < Indicates TX or RX s32errupt for the uart > > + */ > +s32 pru_softuart_get_isrstatus(struct device *dev, u16 uart_num, u16 > *txrx_flag) > +{ > + u32 intc_offset; > + u32 ch_num = 0xFF; > + u32 reg_val = 0; > + u32 reg_val2 = 0; > + u32 ISR_value = 0; > + u32 ack_reg_val = 0; > + u32 stat_inx_clr_regoffset = 0; > + > + /* initialize the status & Flag to known value */ > + *txrx_flag = 0; > + > + stat_inx_clr_regoffset = (u32) (PRUSS_INTC_STATIDXCLR & 0xFFFF); > + > + /* Read PRU Interrupt Status Register from PRU */ > + intc_offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); > + > + pruss_readl(dev, intc_offset, (u32 *)&ISR_value); > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) > + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + /* Check if the interrupt occured for Tx */ > + ch_num = uart_num * 2 - 2; > + reg_val2 = PRU_SUART0_TX_EVT_BIT << ((uart_num - 1) * 2); > + if (ISR_value & reg_val2) { > + /* interupt occured for TX */ > + *txrx_flag |= PRU_TX_INTR; > + /* acknowledge the RX interrupt */ > + ack_reg_val = ch_num + PRU_SUART0_TX_EVT; > + pruss_writel(dev, stat_inx_clr_regoffset, > + ack_reg_val); > + } > + > + /* Check if the interrupt occured for Rx */ > + reg_val2 = PRU_SUART0_RX_EVT_BIT << ((uart_num - 1) * 2); > + pruss_readl(dev, intc_offset, (u32 *)&ISR_value); > + if (ISR_value & reg_val2) { > + /* interupt occured for RX */ > + *txrx_flag |= PRU_RX_INTR; > + ch_num += 1; > + > + /* acknowledge the RX interrupt */ > + ack_reg_val = ch_num + PRU_SUART0_TX_EVT; > + pruss_writel(dev, stat_inx_clr_regoffset, > + ack_reg_val); > + } > + } else { > + ch_num = uart_num - 1; > + if ((ISR_value & 0x03FC) != 0) { > + reg_val2 = 1 << (uart_num + 1); > + if (ISR_value & reg_val2) { > + /* acknowledge the s32errupt */ > + ack_reg_val = ch_num + PRU_SUART0_TX_EVT; > + pruss_writel(dev, stat_inx_clr_regoffset, > + ack_reg_val); > + *txrx_flag |= PRU_RX_INTR; > + } > + } > + pruss_readl(dev, intc_offset, (u32 *)&ISR_value); > + if (ISR_value & 0x3FC00) { > + reg_val2 = 1 << (uart_num + 9); > + if (ISR_value & reg_val2) { > + /* acknowledge the s32errupt */ > + ack_reg_val = ch_num + PRU_SUART4_TX_EVT; > + pruss_writel(dev, stat_inx_clr_regoffset, > + ack_reg_val); > + *txrx_flag |= PRU_TX_INTR; > + } > + } > + } > + return reg_val; > +} > + > +s32 pru_intr_clr_isrstatus(struct device *dev, u16 uart_num, u32 > txrxmode) > +{ > + u32 offset; > + u16 txrx_flag = 0; > + u16 chn_num; > + > + chn_num = uart_num - 1; > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) > + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + /* channel starts from 0 and uart instance starts from 1 */ > + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; > + if (uart_num <= 4) { > + /* PRU0 */ > + offset = PRU_SUART_PRU0_ISR_OFFSET + 1; > + } else { > + /* PRU1 */ > + offset = PRU_SUART_PRU1_ISR_OFFSET + 1; > + /* First 8 channel corresponds to PRU0 */ > + chn_num -= 8; > + } > + if (2 == txrxmode) > + chn_num++; > + } else if (PRU0_MODE == txrxmode) { > + offset = PRU_SUART_PRU0_ISR_OFFSET + 1; > + } else if (PRU1_MODE == txrxmode) { > + offset = PRU_SUART_PRU1_ISR_OFFSET + 1; > + } else { > + return 0; > + } > + > + pruss_readb(dev, offset, (u8 *) &txrx_flag); > + txrx_flag &= ~(0x2); > + pruss_writeb(dev, offset, (u8) txrx_flag); > + > + return 0; > +} > + > +s32 suart_arm_to_pru_intr(struct device *dev, u16 uart_num) > +{ > + u32 value; > + > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) > + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + if ((uart_num > 0) && (uart_num <= 4)) > + value = 0x20; /* PRU0 SYS_EVT32 */ > + else if ((uart_num > 4) && (uart_num <= 8)) > + value = 0x21; /* PRU0 SYS_EVT33 */ > + else > + return -EINVAL; > + } > + if ((PRU0_MODE == PRU_MODE_RX_ONLY) > + || (PRU1_MODE == PRU_MODE_RX_ONLY) > + || (PRU0_MODE == PRU_MODE_TX_ONLY) > + || (PRU1_MODE == PRU_MODE_TX_ONLY)) { > + if (uart_num == PRUSS_NUM0) > + value = 0x20; /* PRU0 SYS_EVT32 */ > + else if (uart_num == PRUSS_NUM1) > + value = 0x21; /* PRU0 SYS_EVT33 */ > + else > + return -EINVAL; > + } > + return pruss_writel(dev, PRUSS_INTC_STATIDXSET, value); > +} > + > +static s32 arm_to_pru_intr_init(struct device *dev) > +{ > + u32 value; > + u32 int_offset; > + > + /* Clear all the host interrupts */ > + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; > + int_offset++) > + pruss_idx_writel(dev, PRUSS_INTC_HSTINTENIDXCLR, int_offset); > + > + /* Enable the global s32errupt */ > + pruss_rmwl(dev, (u32) (PRUSS_INTC_GLBLEN & 0xFFFF), 0, 1); > + > + /* Enable the Host interrupts for all host channels */ > + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; > + int_offset++) > + pruss_rmwl(dev, (u32) (PRUSS_INTC_HSTINTENIDXSET & 0xFFFF), > + 0, int_offset); > + pruss_rmwl(dev, (u32) (PRUSS_INTC_HOSTMAP0 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_HOSTMAP0_CHAN); > + pruss_rmwl(dev, (u32) (PRUSS_INTC_HOSTMAP1 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_HOSTMAP1_CHAN); > + pruss_rmwl(dev, (u32) (PRUSS_INTC_HOSTMAP2 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_HOSTMAP2_CHAN); > + > + /* MAP Channel 0 to SYS_EVT31 */ > + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP7 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP7_SYS_EVT31); > + > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) > + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + /* Sets the channels for the system interrupt */ > + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP8 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP8_FULL); > + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP9_FULL); > + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP10 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP10_FULL); > + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP11 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP11_FULL); > + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP12 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP12_FULL); > + } > + if ((PRU0_MODE == PRU_MODE_RX_ONLY) > + || (PRU1_MODE == PRU_MODE_RX_ONLY) > + || (PRU0_MODE == PRU_MODE_TX_ONLY) > + || (PRU1_MODE == PRU_MODE_TX_ONLY)) { > + > + /* Sets the channels for the system interrupt */ > + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP8 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP8_HALF); > + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP9_HALF); > + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP10 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP10_HALF); > + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP11 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP11_HALF); > + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP12 & 0xFFFF), > + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP12_HALF); > + } > + > + /* Clear required set of system events > + * and enable them using indexed register > + */ > + for (int_offset = 0; int_offset < 18; int_offset++) { > + value = 32 + int_offset; > + pruss_idx_writel(dev, PRUSS_INTC_STATIDXCLR, value); > + } > + > + /* enable only the HOST to PRU interrupts and let the PRU to Host events > + * enabled by the separate API on demand basis. > + */ > + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 31); > + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 32); > + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 33); > + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 50); > + pruss_rmwl(dev, (u32) (PRUSS_INTC_GLBLEN & 0xFFFF), 0, 1); > + > + /* Enable the Host interrupts for all host channels */ > + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; > + int_offset++) > + pruss_idx_writel(dev, PRUSS_INTC_HSTINTENIDXSET, int_offset); > + > + return 0; > +} > + > +s32 suart_pru_to_host_intr_enable(struct device *dev, u16 uart_num, > + u32 txrxmode, s32 flag) > +{ > + u32 chn_num; > + u32 value; > + s16 retval = 0; > + > + if (uart_num > 8) > + return -EINVAL; > + > + chn_num = uart_num - 1; > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || > + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + chn_num = (uart_num * 2) - 2; > + if (2 == txrxmode) /* Rx mode */ > + chn_num++; > + value = 34 + chn_num; > + } else if ((PRU_MODE_RX_ONLY == txrxmode) > + && (PRU0_MODE == PRU_MODE_RX_ONLY)) > + value = 34 + chn_num; > + else if ((PRU_MODE_RX_ONLY == txrxmode) > + && (PRU1_MODE == PRU_MODE_RX_ONLY)) > + value = 42 + chn_num; > + else if ((PRU_MODE_TX_ONLY == txrxmode) > + && (PRU0_MODE == PRU_MODE_TX_ONLY)) > + value = 34 + chn_num; > + else if ((PRU_MODE_TX_ONLY == txrxmode) > + && (PRU1_MODE == PRU_MODE_TX_ONLY)) > + value = 42 + chn_num; > + else > + return -EINVAL; > + > + retval = flag ? pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, value) : > + pruss_idx_writel(dev, PRUSS_INTC_ENIDXCLR, value); > + return retval; > +} > + > +s32 suart_intr_setmask(struct device *dev, u16 uart_num, > + u32 txrxmode, u32 rmask) > +{ > + u32 offset; > + u32 pru_offset; > + u32 regval = 0; > + u32 chn_num = uart_num - 1; > + > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) > + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + /* channel starts from 0 and uart instance starts from 1 */ > + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; > + > + if ((uart_num > 0) && (uart_num <= 4)) { > + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; > + offset = PRU_SUART_PRU0_IMR_OFFSET; > + } else if ((uart_num > 4) && (uart_num <= 8)) { > + offset = PRU_SUART_PRU1_IMR_OFFSET; > + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; > + chn_num -= 8; > + } else { > + return -EINVAL; > + } > + if (2 == txrxmode) > + chn_num++; > + } else if (PRU0_MODE == txrxmode) { > + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; > + offset = PRU_SUART_PRU0_IMR_OFFSET; > + } else if (PRU1_MODE == txrxmode) { > + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; > + offset = PRU_SUART_PRU1_IMR_OFFSET; > + } else > + return 0; > + > + regval = 1 << chn_num; > + if (CHN_TXRX_IE_MASK_CMPLT == (rmask & CHN_TXRX_IE_MASK_CMPLT)) > + pruss_rmww(dev, offset, regval, regval); > + > + if ((rmask & SUART_GBL_INTR_ERR_MASK) == > + SUART_GBL_INTR_ERR_MASK) { > + regval = SUART_GBL_INTR_ERR_MASK; > + pruss_rmww(dev, offset, regval, regval); > + } > + > + offset = pru_offset + > + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) > + + PRU_SUART_CH_CONFIG1_OFFSET; > + /* Framing Error Interrupt Masked */ > + if ((rmask & CHN_TXRX_IE_MASK_FE) == CHN_TXRX_IE_MASK_FE) { > + regval = 0; > + pruss_readw(dev, offset, (u16 *) ®val); > + regval &= ~(CHN_TXRX_IE_MASK_FE); > + regval |= CHN_TXRX_IE_MASK_FE; > + pruss_writew(dev, offset, (u16) regval); > + } > + > + /* Break Indicator Interrupt Masked */ > + if (CHN_TXRX_IE_MASK_BI == (rmask & CHN_TXRX_IE_MASK_BI)) { > + regval = 0; > + pruss_readw(dev, offset, (u16 *) ®val); > + regval &= ~(CHN_TXRX_IE_MASK_BI); > + regval |= CHN_TXRX_IE_MASK_BI; > + pruss_writew(dev, offset, (u16) regval); > + } > + > + /* Timeout error Interrupt Masked */ > + if (CHN_TXRX_IE_MASK_TIMEOUT == > + (rmask & CHN_TXRX_IE_MASK_TIMEOUT)) { > + regval = 0; > + pruss_readw(dev, offset, (u16 *) ®val); > + regval &= ~(CHN_TXRX_IE_MASK_TIMEOUT); > + regval |= CHN_TXRX_IE_MASK_TIMEOUT; > + pruss_writew(dev, offset, (u16) regval); > + } > + return 0; > +} > + > +s32 suart_intr_clrmask(struct device *dev, u16 uart_num, > + u32 txrxmode, u32 rmask) > +{ > + u32 offset; > + u32 pru_offset; > + u16 regval = 0; > + u16 chn_num; > + > + chn_num = uart_num - 1; > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) > + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + /* channel starts from 0 and uart instance starts from 1 */ > + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; > + if ((uart_num > 0) && (uart_num <= 4)) { > + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; > + offset = PRU_SUART_PRU0_IMR_OFFSET; > + } else if ((uart_num > 4) && (uart_num <= 8)) { > + /* PRU1 */ > + offset = PRU_SUART_PRU1_IMR_OFFSET; > + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; > + /* First 8 channel corresponds to PRU0 */ > + chn_num -= 8; > + } else > + return -EINVAL; > + if (2 == txrxmode) > + chn_num++; > + } else if (PRU0_MODE == txrxmode) { > + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; > + offset = PRU_SUART_PRU0_IMR_OFFSET; > + } else if (PRU1_MODE == txrxmode) { > + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; > + offset = PRU_SUART_PRU1_IMR_OFFSET; > + } else > + return 0; > + > + regval = 1 << chn_num; > + if (CHN_TXRX_IE_MASK_CMPLT == (rmask & CHN_TXRX_IE_MASK_CMPLT)) > + pruss_rmww(dev, offset, regval, 0); > + > + if ((rmask & SUART_GBL_INTR_ERR_MASK) == SUART_GBL_INTR_ERR_MASK) > + pruss_rmww(dev, offset, SUART_GBL_INTR_ERR_MASK, 0); > + > + offset = pru_offset + > + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) > + + PRU_SUART_CH_CONFIG1_OFFSET; > + > + /* Framing Error Interrupt Masked */ > + if ((rmask & CHN_TXRX_IE_MASK_FE) == CHN_TXRX_IE_MASK_FE) { > + regval = 0; > + pruss_readw(dev, offset, (u16 *) ®val); > + regval &= ~(CHN_TXRX_IE_MASK_FE); > + pruss_writew(dev, offset, regval); > + } > + > + /* Break Indicator Interrupt Masked */ > + if (CHN_TXRX_IE_MASK_BI == (rmask & CHN_TXRX_IE_MASK_BI)) { > + regval = 0; > + pruss_readw(dev, offset, (u16 *) ®val); > + regval &= ~(CHN_TXRX_IE_MASK_BI); > + pruss_writew(dev, offset, regval); > + } > + > + /* Timeout error Interrupt Masked */ > + if (CHN_TXRX_IE_MASK_TIMEOUT == > + (rmask & CHN_TXRX_IE_MASK_TIMEOUT)) { > + regval = 0; > + pruss_readw(dev, offset, (u16 *) ®val); > + regval &= ~(CHN_TXRX_IE_MASK_TIMEOUT); > + pruss_writew(dev, offset, regval); > + } > + return 0; > +} > + > +s32 suart_intr_getmask(struct device *dev, u16 uart_num, > + u32 txrxmode, u32 rmask) > +{ > + u16 chn_num; > + u32 offset; > + u16 txrx_flag; > + u16 regval = 1; > + > + chn_num = uart_num - 1; > + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) > + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { > + /* channel starts from 0 and uart instance starts from 1 */ > + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; > + > + if ((uart_num > 0) && (uart_num <= 4)) { > + > + offset = PRU_SUART_PRU0_IMR_OFFSET; > + } else if ((uart_num > 4) && (uart_num <= 8)) { > + /* PRU1 */ > + offset = PRU_SUART_PRU1_IMR_OFFSET; > + /* First 8 channel corresponds to PRU0 */ > + chn_num -= 8; > + } else > + return -EINVAL; > + > + if (2 == txrxmode) > + chn_num++; > + > + } else if (PRU0_MODE == txrxmode) > + offset = PRU_SUART_PRU0_IMR_OFFSET; > + else if (PRU1_MODE == txrxmode) > + offset = PRU_SUART_PRU1_IMR_OFFSET; > + else > + return 0; > + > + regval = regval << chn_num; > + pruss_readw(dev, offset, (u16 *) &txrx_flag); > + txrx_flag &= regval; > + > + if ((rmask && (txrx_flag == regval)) || (!rmask && !txrx_flag)) > + return 1; > + > + return 0; > +} > diff --git a/drivers/tty/serial/pruss_suart_utils.c > b/drivers/tty/serial/pruss_suart_utils.c > new file mode 100644 > index 0000000..5ee340e > --- /dev/null > +++ b/drivers/tty/serial/pruss_suart_utils.c > @@ -0,0 +1,393 @@ > +/* > + * Copyright (C) 2010, 2011 Texas Instruments Incorporated > + * Author: Jitendra Kumar > + * > + * 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 > +#include "pruss_suart.h" > + > +#define SUART_TRX_DIV_CONF_SZ 4 > + > +static s16 suart_mcasp_tx_baud_set(u32 tx_baud_value, > + struct pruss_suart_iomap *pruss_ioaddr); > +static s16 suart_mcasp_rx_baud_set(u32 rx_baud_value, u32 oversampling, > + struct pruss_suart_iomap *pruss_ioaddr); > + > +/* > + * Lookup table for TX baud rate > + * The divisor value is calculated using the formula > + * > + * ACLKX = (AUXCLK)/(CLKXDIV * HCLKXDIV) > + * > + * Where > + * CLKXDIV takes values from 1-32 > + * HCLKXDIV takes values from 1-4096 > + * Here > + * AUXCLK = 24MHz > + */ > +static u32 lt_tx_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { > + /*BaudRate, Divisor, CLKXDIV,HCLKXDIV */ > + {300, 80000, 24, 3200}, > + {600, 40000, 15, 2500}, > + {1800, 13333, 10, 1212}, > + {2400, 10000, 4, 2000}, > + {4800, 5000, 1, 2500}, > + {7200, 3333, 0, 3333}, > + {9600, 2500, 0, 2500}, > + {14400, 1666, 0, 1666}, > + {19200, 1250, 0, 1250}, > + {38400, 625, 0, 625}, > + {57600, 416, 0, 416}, > + {115200, 208, 0, 208}, > + {230400, 104, 0, 104} > +}; > + > +/* > + * Lookup table for RX baud rate for 8 bit oversampling > + * The divisor value is calculated using the formula > + * > + * ACLKR = (AUXCLK)/(CLKRDIV * HCLKRDIV) * Oversampling > + * > + * Where > + * CLKRDIV takes values from 1-32 > + * HCLKRDIV takes values from 1-4096 > + * Here > + * AUXCLK = 24MHz > + */ > +static u32 lt_rx_8x_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { > +/* BaudRate, Divisor, CLKXDIV, HCLKXDIV */ > + {300, 10000, 4, 2000}, > + {600, 5000, 1, 2500}, > + {1800, 1667, 0, 1667}, > + {2400, 1250, 0, 1250}, > + {7200, 417, 0, 417}, > + {4800, 625, 0, 625}, > + {9600, 312, 0, 312}, > + {14400, 208, 0, 208}, > + {19200, 156, 0, 156}, > + {38400, 78, 0, 78}, > + {57600, 52, 0, 52}, > + {115200, 26, 0, 26}, > + {230400, 13, 0, 13} > +}; > + > +/* > + * Lookup table for RX baud rate for 16 bit oversampling > + * The divisor value is calculated using the formula > + * > + * ACLKR = (AUXCLK)/(CLKRDIV * HCLKRDIV) * Oversampling > + * > + * Where > + * CLKRDIV takes values from 1-32 > + * HCLKRDIV takes values from 1-4096 > + * Here > + * AUXCLK = 24MHz > + */ > +static u32 lt_rx_16x_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { > +/*BaudRate, Divisor, CLKXDIV, HCLKXDIV */ > + {300, 5000, 1, 2500}, > + {600, 2500, 0, 2500}, > + {1800, 833, 0, 833}, > + {2400, 625, 0, 625}, > + {4800, 312, 0, 312}, > + {7200, 208, 0, 208}, > + {9600, 156, 0, 156}, > + {14400, 104, 0, 104}, > + {19200, 78, 0, 78}, > + {38400, 39, 0, 39}, > + {57600, 26, 0, 26}, > + {115200, 13, 0, 13}, > + {230400, 6, 0, 6} > +}; > + > +/* > + * McASP configuration routine > + */ > + > +void suart_mcasp_reset(struct pruss_suart_iomap *pruss_ioaddr) > +{ > + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = > + pruss_ioaddr->mcasp_io_addr; > + /* reset mcasp. */ > + iowrite32(MCASP_SUART_GBLCTL, &mcasp0_regs->gblctl); > + iowrite32(MCASP_SUART_RGBLCTL, &mcasp0_regs->rgblctl); > + iowrite32(MCASP_SUART_XGBLCTL, &mcasp0_regs->xgblctl); > + iowrite32(MCASP_SUART_XSTAT, &mcasp0_regs->xstat); > + iowrite32(MCASP_SUART_RSTAT, &mcasp0_regs->rstat); > +} > + > +void suart_mcasp_config(u32 tx_baud_value, > + u32 rx_baud_value, > + u32 oversampling, > + struct pruss_suart_iomap *pruss_ioaddr) > +{ > + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = > + pruss_ioaddr->mcasp_io_addr; > + u32 temp_reg; > + > + /* reset mcasp */ > + iowrite32(MCASP_SUART_GBLCTL, &mcasp0_regs->gblctl); > + iowrite32(MCASP_SUART_RGBLCTL, &mcasp0_regs->rgblctl); > + iowrite32(MCASP_SUART_XGBLCTL, &mcasp0_regs->xgblctl); > + > + /* configure receive registers */ > + if ((SUART_8X_OVRSMPL == oversampling) || (0 == oversampling)) { > + iowrite32(MCASP_SUART_RMASK_8, &mcasp0_regs->rmask); > + iowrite32(MCASP_SUART_RFMT_8, &mcasp0_regs->rfmt); > + } > + if (SUART_16X_OVRSMPL == oversampling) { > + iowrite32(MCASP_SUART_RMASK_16, &mcasp0_regs->rmask); > + iowrite32(MCASP_SUART_RFMT_16, &mcasp0_regs->rfmt); > + > + } > + > + iowrite32(MCASP_SUART_FSRM, &mcasp0_regs->afsrctl); > + iowrite32(MCASP_SUART_CLKRM_CLKRP, &mcasp0_regs->aclkrctl); > + iowrite32(MCASP_SUART_HCLKRP, &mcasp0_regs->ahclkrctl); > + suart_mcasp_rx_baud_set(rx_baud_value, oversampling, pruss_ioaddr); > + iowrite32(MCASP_SUART_RTDMS0, &mcasp0_regs->rtdm); > + iowrite32(MCASP_SUART_RSYNCERR, &mcasp0_regs->rintctl); > + iowrite32(MCASP_SUART_RMAX_RPS_256, &mcasp0_regs->rclkchk); > + > + /* configure transmit registers. */ > + iowrite32(MCASP_SUART_XMASK_0_31, &mcasp0_regs->xmask); > + iowrite32(MCASP_SUART_XBUSEL_XSSZ_16_XPAD_0, &mcasp0_regs->xfmt); > + iowrite32(MCASP_SUART_FSXM, &mcasp0_regs->afsxctl); > + iowrite32(MCASP_SUART_CLKXM_ASYNC_CLKXP, &mcasp0_regs->aclkxctl); > + iowrite32(MCASP_SUART_HCLKXM, &mcasp0_regs->ahclkxctl); > + > + suart_mcasp_tx_baud_set(tx_baud_value, pruss_ioaddr); > + iowrite32(MCASP_SUART_XTDMS0, &mcasp0_regs->xtdm); > + iowrite32(MCASP_SUART_XSYNCERR, &mcasp0_regs->xintctl); > + iowrite32(MCASP_SUART_XMAX_XPS_256, &mcasp0_regs->xclkchk); > + > + /* Serializer as a transmitter */ > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl0); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl1); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl2); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl3); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl4); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl5); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl6); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl7); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl8); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl9); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl10); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl11); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl12); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl13); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl14); > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl15); > + > + /* Configure all AXR[n] as McASP pins */ > + > + /* > + * Setting all TX MCASP AXR[n] Pin mapped to Even Serializer number > + * (0,2,4,6,8,10,12,14) to GPIO Mode by default. During setting the > + * serializer to TX mode in PRU assembly code, the MCASP AXR[n] Pin > + * would get configured to MCASP mode of operation, > + * before Actual Data Transfer > + */ > + > + /* Setting all TX Pin to GPIO Mode by default */ > + temp_reg = (OMAPL_MCASP_PFUNC_RESETVAL) | > + (1 << PRU_SUART0_CONFIG_TX_SER) | (1 << PRU_SUART1_CONFIG_TX_SER) | > + (1 << PRU_SUART2_CONFIG_TX_SER) | (1 << PRU_SUART3_CONFIG_TX_SER) | > + (1 << PRU_SUART4_CONFIG_TX_SER) | (1 << PRU_SUART5_CONFIG_TX_SER) | > + (1 << PRU_SUART6_CONFIG_TX_SER) | (1 << PRU_SUART7_CONFIG_TX_SER); > + iowrite32(temp_reg, &mcasp0_regs->pfunc); > + > + iowrite32(0xFFF, &mcasp0_regs->pdout); > + > + /* config pin function and direction */ > + iowrite32(0x00000000, &mcasp0_regs->pdir); > + temp_reg = > + (1 << PRU_SUART0_CONFIG_TX_SER) | (1 << PRU_SUART1_CONFIG_TX_SER) | > + (1 << PRU_SUART2_CONFIG_TX_SER) | (1 << PRU_SUART3_CONFIG_TX_SER) | > + (1 << PRU_SUART4_CONFIG_TX_SER) | (1 << PRU_SUART5_CONFIG_TX_SER) | > + (1 << PRU_SUART6_CONFIG_TX_SER) | (1 << PRU_SUART7_CONFIG_TX_SER) | > + (MCASP_PDIR_VAL); > + iowrite32(temp_reg, &mcasp0_regs->pdir); > + > + iowrite32(MCASP_SUART_DIT_DISABLE, &mcasp0_regs->ditctl); > + iowrite32(MCASP_SUART_LOOPBACK_DISABLE, &mcasp0_regs->dlbctl); > + iowrite32(MCASP_SUART_AMUTE_DISABLE, &mcasp0_regs->amute); > + > + iowrite32(MCASP_SUART_XSTAT, &mcasp0_regs->xstat); > + iowrite32(MCASP_SUART_RSTAT, &mcasp0_regs->rstat); > +} > + > +void suart_mcasp_tx_serialzier_set(u32 serializer_num, > + struct pruss_suart_iomap *pruss_ioaddr) > +{ > + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = > + pruss_ioaddr->mcasp_io_addr; > + u32 temp_reg; > + temp_reg = ioread32(&mcasp0_regs->pfunc); > + temp_reg |= (0x1 << serializer_num); > + iowrite32(temp_reg, &mcasp0_regs->pfunc); > +} > + > +/* > + * mcasp TX buard rate setting routine > + */ > +static s16 suart_mcasp_tx_baud_set(u32 tx_baud_value, > + struct pruss_suart_iomap *pruss_ioaddr) > +{ > + u32 clk_div_val; > + u32 loop_cnt; > + s16 status = 0; > + s16 found_val = false; > + > + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = > + pruss_ioaddr->mcasp_io_addr; > + u32 temp_reg; > + > + /* Search the supported baud rate in the table */ > + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; > + loop_cnt++) { > + if (tx_baud_value == lt_tx_baud_rate[loop_cnt][0]) { > + found_val = true; > + break; > + } > + } > + if (found_val == true) { > + clk_div_val = lt_tx_baud_rate[loop_cnt][2]; > + temp_reg = ioread32(&mcasp0_regs->aclkxctl); > + temp_reg |= (clk_div_val << > + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); > + iowrite32(temp_reg, &mcasp0_regs->aclkxctl); > + clk_div_val = lt_tx_baud_rate[loop_cnt][3]; > + temp_reg = ioread32(&mcasp0_regs->ahclkxctl); > + temp_reg |= (clk_div_val << > + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); > + iowrite32(temp_reg, &mcasp0_regs->ahclkxctl); > + } else { > + return -EINVAL ; > + } > + return status; > +} > + > +/* > + * mcasp RX buard rate setting routine > + */ > +static s16 suart_mcasp_rx_baud_set(u32 rx_baud_value, > + u32 oversampling, struct pruss_suart_iomap *pruss_ioaddr) > +{ > + u32 clk_div_val = 0; > + u32 loop_cnt = 0; > + s16 status = 0; > + u32 temp_reg = 0; > + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = > + pruss_ioaddr->mcasp_io_addr; > + > + switch (oversampling) { > + case SUART_8X_OVRSMPL: > + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; > + loop_cnt++) { > + if (rx_baud_value == lt_rx_8x_baud_rate[loop_cnt][0]) { > + clk_div_val = lt_rx_8x_baud_rate[loop_cnt][2]; > + temp_reg = ioread32(&mcasp0_regs->aclkrctl); > + temp_reg |= (clk_div_val << > + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); > + iowrite32(temp_reg, &mcasp0_regs->aclkrctl); > + > + clk_div_val = > + lt_rx_8x_baud_rate[loop_cnt][3] - 1; > + > + temp_reg = ioread32(&mcasp0_regs->ahclkrctl); > + temp_reg |= (clk_div_val << > + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); > + iowrite32(temp_reg, &mcasp0_regs->ahclkrctl); > + break; > + } > + } > + status = -EINVAL; > + break; > + case SUART_16X_OVRSMPL: > + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; > + loop_cnt++) { > + if (rx_baud_value == lt_rx_16x_baud_rate[loop_cnt][0]) { > + clk_div_val = lt_rx_16x_baud_rate[loop_cnt][2]; > + temp_reg = ioread32(&mcasp0_regs->aclkrctl); > + temp_reg |= (clk_div_val << > + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); > + iowrite32(temp_reg, &mcasp0_regs->aclkrctl); > + clk_div_val = lt_rx_16x_baud_rate[loop_cnt][3]; > + temp_reg = ioread32(&mcasp0_regs->ahclkrctl); > + temp_reg |= (clk_div_val << > + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); > + iowrite32(temp_reg, &mcasp0_regs->ahclkrctl); > + break; > + } > + } > + status = -EINVAL; > + break; > + case SUART_TX_OVRSMPL: > + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; > + loop_cnt++) { > + if (rx_baud_value == lt_tx_baud_rate[loop_cnt][0]) { > + clk_div_val = lt_tx_baud_rate[loop_cnt][2]; > + temp_reg = ioread32(&mcasp0_regs->aclkrctl); > + temp_reg |= (clk_div_val << > + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); > + iowrite32(temp_reg, &mcasp0_regs->aclkrctl); > + clk_div_val = lt_tx_baud_rate[loop_cnt][3]; > + temp_reg = ioread32(&mcasp0_regs->ahclkrctl); > + temp_reg |= (clk_div_val << > + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); > + iowrite32(temp_reg, &mcasp0_regs->ahclkrctl); > + break; > + } > + } > + status = -EINVAL; > + break; > + default: > + status = -EINVAL; > + break; > + } > + > + return status; > +} > + > +/* > + * mcasp buard rate setting routine > + */ > +s16 suart_asp_baud_set(u32 tx_baud_value, u32 rx_baud_value, u32 > oversampling, > + struct pruss_suart_iomap *pruss_ioaddr) > +{ > + s16 status = 0; > + > + status = suart_mcasp_tx_baud_set(tx_baud_value, pruss_ioaddr); > + status = suart_mcasp_rx_baud_set(rx_baud_value, oversampling, > + pruss_ioaddr); > + > + return status; > +} > + > +/* > + * mcasp deactivate the selected serializer > + */ > +s16 suart_asp_serializer_deactivate(u16 sr_num, > + struct pruss_suart_iomap *pruss_ioaddr) > +{ > + s16 status = 0; > + struct omapl_mcasp_regs_ovly __iomem *mcasp0_regs = > + pruss_ioaddr->mcasp_io_addr; > + if (sr_num > 15) > + status = -EINVAL; > + else > + iowrite32(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->srctl0); > + > + return status; > +} > diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h > index 758c5b0..eae37fe 100644 > --- a/include/linux/serial_core.h > +++ b/include/linux/serial_core.h > @@ -202,6 +202,8 @@ > /* VIA VT8500 SoC */ > #define PORT_VT8500 97 > > +#define PORT_DA8XX_PRU_SUART 98 > + > #ifdef __KERNEL__ > > #include > -- > 1.7.2.3 > _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From michael.williamson at criticallink.com Fri Jul 8 13:28:32 2011 From: michael.williamson at criticallink.com (Michael Williamson) Date: Fri, 08 Jul 2011 18:28:32 -0000 Subject: [PATCH v1 4/4] davinci: add spi devices support for da830/omap-l137/am17x evm In-Reply-To: <1296596979-18524-1-git-send-email-michael.williamson@criticallink.com> References: <1296596979-18524-1-git-send-email-michael.williamson@criticallink.com> Message-ID: <1296596979-18524-5-git-send-email-michael.williamson@criticallink.com> From: Sekhar Nori This patch adds the on-board SPI flash device to the DA830/OMAP-L137/AM17x EVM. It also registers the SPI flash device to the MTD subsystem. Based on SPI flash device support for MityDSP-L138F platform. Signed-off-by: Sekhar Nori [michael.williamson at criticallink.com: moved da830evm_spi0_pdata to devices-da8xx.c] Signed-off-by: Michael Williamson --- arch/arm/mach-davinci/board-da830-evm.c | 78 +++++++++++++++++++++++++++++++ 1 files changed, 78 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index b52a3a1..01319bd 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include #include #include @@ -30,6 +32,7 @@ #include #include #include +#include #define DA830_EVM_PHY_ID "" /* @@ -534,6 +537,79 @@ static struct edma_rsv_info da830_edma_rsv[] = { }, }; +static struct mtd_partition da830evm_spiflash_part[] = { + [0] = { + .name = "DSP-UBL", + .offset = 0, + .size = SZ_8K, + .mask_flags = MTD_WRITEABLE, + }, + [1] = { + .name = "ARM-UBL", + .offset = MTDPART_OFS_APPEND, + .size = SZ_16K + SZ_8K, + .mask_flags = MTD_WRITEABLE, + }, + [2] = { + .name = "U-Boot", + .offset = MTDPART_OFS_APPEND, + .size = SZ_256K - SZ_32K, + .mask_flags = MTD_WRITEABLE, + }, + [3] = { + .name = "U-Boot-Environment", + .offset = MTDPART_OFS_APPEND, + .size = SZ_16K, + .mask_flags = 0, + }, + [4] = { + .name = "Kernel", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + .mask_flags = 0, + }, +}; + +static struct flash_platform_data da830evm_spiflash_data = { + .name = "m25p80", + .parts = da830evm_spiflash_part, + .nr_parts = ARRAY_SIZE(da830evm_spiflash_part), + .type = "w25x32", +}; + +static struct davinci_spi_config da830evm_spiflash_cfg = { + .io_type = SPI_IO_TYPE_DMA, + .c2tdelay = 8, + .t2cdelay = 8, +}; + +static struct spi_board_info da830evm_spi_info[] = { + { + .modalias = "m25p80", + .platform_data = &da830evm_spiflash_data, + .controller_data = &da830evm_spiflash_cfg, + .mode = SPI_MODE_0, + .max_speed_hz = 30000000, + .bus_num = 0, + .chip_select = 0, + }, +}; + +static void __init da830evm_init_spi0(struct spi_board_info *info, unsigned len) +{ + int ret; + + ret = spi_register_board_info(info, len); + if (ret) + pr_warning("failed to register board info : %d\n", ret); + + da8xx_spi_pdata[0].num_chipselect = len; + + ret = da8xx_register_spi(0); + if (ret) + pr_warning("failed to register spi 0 device : %d\n", ret); +} + static __init void da830_evm_init(void) { struct davinci_soc_info *soc_info = &davinci_soc_info; @@ -590,6 +666,8 @@ static __init void da830_evm_init(void) ret = da8xx_register_rtc(); if (ret) pr_warning("da830_evm_init: rtc setup failed: %d\n", ret); + + da830evm_init_spi0(da830evm_spi_info, ARRAY_SIZE(da830evm_spi_info)); } #ifdef CONFIG_SERIAL_8250_CONSOLE -- 1.7.0.4 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From plagnioj at jcrosoft.com Fri Jul 8 13:56:02 2011 From: plagnioj at jcrosoft.com (Jean-Christophe PLAGNIOL-VILLARD) Date: Fri, 08 Jul 2011 18:56:02 -0000 Subject: [RFC PATCH v2] Consolidate SRAM support In-Reply-To: <20110418085259.GA26044@n2100.arm.linux.org.uk> References: <20110415130607.GM1611@n2100.arm.linux.org.uk> <20110418085259.GA26044@n2100.arm.linux.org.uk> Message-ID: <20110419160135.GA4204@game.jcrosoft.org> Hi, I do post a patch to add the support to specify a virt and phys address to the generic allocator so the pv-pool.c is not needed we can just use the generic fucntion I'll post a v3 updated again it Best Regards, J. > --- /dev/null > +++ b/arch/arm/common/pv-pool.c > @@ -0,0 +1,69 @@ > +/* > + * Unified Phys/Virt allocator, based on mach-davinci/sram.c, which was > + * Copyright (C) 2009 David Brownell. > + * > + * 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; either version 2 of the License, or > + * (at your option) any later version. > + */ > +#include > +#include > +#include > +#include > +#include > + > +#include > + > +struct pv_pool { > + struct gen_pool *genpool; > + void *cpu_base; > + phys_addr_t phys_base; > +}; > + > +void *pv_pool_alloc(struct pv_pool *pool, size_t len, phys_addr_t *phys) > +{ > + void *addr = (void *)gen_pool_alloc(pool->genpool, len); > + > + if (phys) > + *phys = addr ? (pool->phys_base + (addr - pool->cpu_base)) : > + (phys_addr_t)-1ULL; > + > + return addr; > +} > +EXPORT_SYMBOL_GPL(pv_pool_alloc); > + > +void pv_pool_free(struct pv_pool *pool, void *addr, size_t len) > +{ > + gen_pool_free(pool->genpool, (unsigned long)addr, len); > +} > +EXPORT_SYMBOL_GPL(pv_pool_free); > + > +struct pv_pool *pv_pool_create(void *addr, phys_addr_t phys, size_t len, > + int min_alloc_order) > +{ > + struct pv_pool *pool = kzalloc(sizeof(struct pv_pool), GFP_KERNEL); > + > + if (pool) { > + pool->cpu_base = addr; > + pool->phys_base = phys; > + pool->genpool = gen_pool_create(min_alloc_order, -1); > + if (!pool->genpool) { > + kfree(pool); > + pool = NULL; > + } else { > + WARN_ON(gen_pool_add(pool->genpool, (unsigned long)addr, > + len, -1) < 0); > + } > + } > + > + return pool; > +} > +EXPORT_SYMBOL_GPL(pv_pool_create); > + > +void pv_pool_destroy(struct pv_pool *pool) > +{ > + gen_pool_destroy(pool->genpool); > + kfree(pool); > +} > +EXPORT_SYMBOL_GPL(pv_pool_destroy); > diff --git a/arch/arm/include/asm/pv-pool.h b/arch/arm/include/asm/pv-pool.h > new file mode 100644 > index 0000000..b7ae871 > --- /dev/null > +++ b/arch/arm/include/asm/pv-pool.h > @@ -0,0 +1,20 @@ > +#ifndef __ASMARM_PV_POOL_H > +#define __ASMARM_PV_POOL_H > + > +#include > + > +struct pv_pool; > + > +void *pv_pool_alloc(struct pv_pool *, size_t, phys_addr_t *); > +void pv_pool_free(struct pv_pool *, void *, size_t); > +struct pv_pool *pv_pool_create(void *, phys_addr_t, size_t, int); > +void pv_pool_destroy(struct pv_pool *); > + > +/* Macro to copy a function into SRAM, using the fncpy API */ > +#define pv_pool_fncpy(pool, funcp, size) ({ \ > + size_t _sz = size; \ > + void *_sram = pv_pool_alloc(pool, _sz, NULL); \ > + (_sram ? fncpy(_sram, &(funcp), _sz) : NULL); \ > +}) > + > +#endif > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index 68fe4c2..5eca128 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -1099,7 +1099,7 @@ static struct davinci_soc_info davinci_soc_info_da850 = { > .gpio_irq = IRQ_DA8XX_GPIO0, > .serial_dev = &da8xx_serial_device, > .emac_pdata = &da8xx_emac_pdata, > - .sram_dma = DA8XX_ARM_RAM_BASE, > + .sram_phys = DA8XX_ARM_RAM_BASE, > .sram_len = SZ_8K, > .reset_device = &da8xx_wdt_device, > }; > diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c > index 76364d1..3df8730 100644 > --- a/arch/arm/mach-davinci/dm355.c > +++ b/arch/arm/mach-davinci/dm355.c > @@ -850,7 +850,7 @@ static struct davinci_soc_info davinci_soc_info_dm355 = { > .gpio_num = 104, > .gpio_irq = IRQ_DM355_GPIOBNK0, > .serial_dev = &dm355_serial_device, > - .sram_dma = 0x00010000, > + .sram_phys = 0x00010000, > .sram_len = SZ_32K, > .reset_device = &davinci_wdt_device, > }; > diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c > index 4604e72..d306034 100644 > --- a/arch/arm/mach-davinci/dm365.c > +++ b/arch/arm/mach-davinci/dm365.c > @@ -1082,7 +1082,7 @@ static struct davinci_soc_info davinci_soc_info_dm365 = { > .gpio_unbanked = 8, /* really 16 ... skip muxed GPIOs */ > .serial_dev = &dm365_serial_device, > .emac_pdata = &dm365_emac_pdata, > - .sram_dma = 0x00010000, > + .sram_phys = 0x00010000, > .sram_len = SZ_32K, > .reset_device = &davinci_wdt_device, > }; > diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c > index 9a2376b..4ca7295 100644 > --- a/arch/arm/mach-davinci/dm644x.c > +++ b/arch/arm/mach-davinci/dm644x.c > @@ -764,7 +764,7 @@ static struct davinci_soc_info davinci_soc_info_dm644x = { > .gpio_irq = IRQ_GPIOBNK0, > .serial_dev = &dm644x_serial_device, > .emac_pdata = &dm644x_emac_pdata, > - .sram_dma = 0x00008000, > + .sram_phys = 0x00008000, > .sram_len = SZ_16K, > .reset_device = &davinci_wdt_device, > }; > diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c > index 1e0f809..a4365f7 100644 > --- a/arch/arm/mach-davinci/dm646x.c > +++ b/arch/arm/mach-davinci/dm646x.c > @@ -848,7 +848,7 @@ static struct davinci_soc_info davinci_soc_info_dm646x = { > .gpio_irq = IRQ_DM646X_GPIOBNK0, > .serial_dev = &dm646x_serial_device, > .emac_pdata = &dm646x_emac_pdata, > - .sram_dma = 0x10010000, > + .sram_phys = 0x10010000, > .sram_len = SZ_32K, > .reset_device = &davinci_wdt_device, > }; > diff --git a/arch/arm/mach-davinci/include/mach/common.h b/arch/arm/mach-davinci/include/mach/common.h > index a57cba2..665d049 100644 > --- a/arch/arm/mach-davinci/include/mach/common.h > +++ b/arch/arm/mach-davinci/include/mach/common.h > @@ -75,7 +75,7 @@ struct davinci_soc_info { > int gpio_ctlrs_num; > struct platform_device *serial_dev; > struct emac_platform_data *emac_pdata; > - dma_addr_t sram_dma; > + phys_addr_t sram_phys; > unsigned sram_len; > struct platform_device *reset_device; > void (*reset)(struct platform_device *); > diff --git a/arch/arm/mach-davinci/include/mach/sram.h b/arch/arm/mach-davinci/include/mach/sram.h > index 111f7cc..7d77e85 100644 > --- a/arch/arm/mach-davinci/include/mach/sram.h > +++ b/arch/arm/mach-davinci/include/mach/sram.h > @@ -10,18 +10,11 @@ > #ifndef __MACH_SRAM_H > #define __MACH_SRAM_H > > +#include > + > /* ARBITRARY: SRAM allocations are multiples of this 2^N size */ > #define SRAM_GRANULARITY 512 > > -/* > - * SRAM allocations return a CPU virtual address, or NULL on error. > - * If a DMA address is requested and the SRAM supports DMA, its > - * mapped address is also returned. > - * > - * Errors include SRAM memory not being available, and requesting > - * DMA mapped SRAM on systems which don't allow that. > - */ > -extern void *sram_alloc(size_t len, dma_addr_t *dma); > -extern void sram_free(void *addr, size_t len); > +extern struct pv_pool *davinci_pv_pool; > > #endif /* __MACH_SRAM_H */ > diff --git a/arch/arm/mach-davinci/pm.c b/arch/arm/mach-davinci/pm.c > index 1bd73a0..f69cd7b 100644 > --- a/arch/arm/mach-davinci/pm.c > +++ b/arch/arm/mach-davinci/pm.c > @@ -29,12 +29,6 @@ > static void (*davinci_sram_suspend) (struct davinci_pm_config *); > static struct davinci_pm_config *pdata; > > -static void davinci_sram_push(void *dest, void *src, unsigned int size) > -{ > - memcpy(dest, src, size); > - flush_icache_range((unsigned long)dest, (unsigned long)(dest + size)); > -} > - > static void davinci_pm_suspend(void) > { > unsigned val; > @@ -123,15 +117,13 @@ static int __init davinci_pm_probe(struct platform_device *pdev) > return -ENOENT; > } > > - davinci_sram_suspend = sram_alloc(davinci_cpu_suspend_sz, NULL); > + davinci_sram_suspend = pv_pool_fncpy(davinci_pv_pool, > + davinci_cpu_suspend, davinci_cpu_suspend_sz); > if (!davinci_sram_suspend) { > dev_err(&pdev->dev, "cannot allocate SRAM memory\n"); > return -ENOMEM; > } > > - davinci_sram_push(davinci_sram_suspend, davinci_cpu_suspend, > - davinci_cpu_suspend_sz); > - > suspend_set_ops(&davinci_pm_ops); > > return 0; > diff --git a/arch/arm/mach-davinci/sram.c b/arch/arm/mach-davinci/sram.c > index db0f778..ebd4d67 100644 > --- a/arch/arm/mach-davinci/sram.c > +++ b/arch/arm/mach-davinci/sram.c > @@ -10,40 +10,13 @@ > */ > #include > #include > -#include > +#include > > #include > #include > > -static struct gen_pool *sram_pool; > - > -void *sram_alloc(size_t len, dma_addr_t *dma) > -{ > - unsigned long vaddr; > - dma_addr_t dma_base = davinci_soc_info.sram_dma; > - > - if (dma) > - *dma = 0; > - if (!sram_pool || (dma && !dma_base)) > - return NULL; > - > - vaddr = gen_pool_alloc(sram_pool, len); > - if (!vaddr) > - return NULL; > - > - if (dma) > - *dma = dma_base + (vaddr - SRAM_VIRT); > - return (void *)vaddr; > - > -} > -EXPORT_SYMBOL(sram_alloc); > - > -void sram_free(void *addr, size_t len) > -{ > - gen_pool_free(sram_pool, (unsigned long) addr, len); > -} > -EXPORT_SYMBOL(sram_free); > - > +struct pv_pool *davinci_pv_pool; > +EXPORT_SYMBOL_GPL(davinci_pv_pool); > > /* > * REVISIT This supports CPU and DMA access to/from SRAM, but it > @@ -58,13 +31,12 @@ static int __init sram_init(void) > > if (len) { > len = min_t(unsigned, len, SRAM_SIZE); > - sram_pool = gen_pool_create(ilog2(SRAM_GRANULARITY), -1); > - if (!sram_pool) > + davinci_pv_pool = pv_pool_create((void *)SRAM_VIRT, > + davinci_soc_info.sram_phys, len, > + ilog2(SRAM_GRANULARITY)); > + if (!davinci_pv_pool) > status = -ENOMEM; > } > - if (sram_pool) > - status = gen_pool_add(sram_pool, SRAM_VIRT, len, -1); > - WARN_ON(status < 0); > return status; > } > core_initcall(sram_init); > diff --git a/arch/arm/plat-mxc/Kconfig b/arch/arm/plat-mxc/Kconfig > index b0cb425..f60d5ea 100644 > --- a/arch/arm/plat-mxc/Kconfig > +++ b/arch/arm/plat-mxc/Kconfig > @@ -118,6 +118,6 @@ config ARCH_MXC_AUDMUX_V2 > > config IRAM_ALLOC > bool > - select GENERIC_ALLOCATOR > + select PV_POOL > > endif > diff --git a/arch/arm/plat-mxc/include/mach/iram.h b/arch/arm/plat-mxc/include/mach/iram.h > index 022690c..543c6df 100644 > --- a/arch/arm/plat-mxc/include/mach/iram.h > +++ b/arch/arm/plat-mxc/include/mach/iram.h > @@ -17,25 +17,37 @@ > * MA 02110-1301, USA. > */ > #include > +#include > > #ifdef CONFIG_IRAM_ALLOC > > -int __init iram_init(unsigned long base, unsigned long size); > -void __iomem *iram_alloc(unsigned int size, unsigned long *dma_addr); > -void iram_free(unsigned long dma_addr, unsigned int size); > +int __init iram_init(phys_addr_t base, size_t size); > + > +extern struct pv_pool *mxc_iram_pool; > + > +static inline void *iram_alloc(size_t size, phys_addr_t *phys) > +{ > + return pv_pool_alloc(iram_pool, size, phys); > +} > + > +static inline void iram_free(void *addr, size_t size) > +{ > + pv_pool_free(iram_pool, addr, size); > +} > > #else > > -static inline int __init iram_init(unsigned long base, unsigned long size) > +static inline int __init iram_init(phys_addr_t base, size_t size) > { > return -ENOMEM; > } > > -static inline void __iomem *iram_alloc(unsigned int size, unsigned long *dma_addr) > +static inline void *iram_alloc(size_t size, phys_addr_t *phys) > { > + *phys = (phys_addr_t)-1ULL; > return NULL; > } > > -static inline void iram_free(unsigned long base, unsigned long size) {} > +static inline void iram_free(void *addr, size_t size) {} > > #endif > diff --git a/arch/arm/plat-mxc/iram_alloc.c b/arch/arm/plat-mxc/iram_alloc.c > index 074c386..1e3d437 100644 > --- a/arch/arm/plat-mxc/iram_alloc.c > +++ b/arch/arm/plat-mxc/iram_alloc.c > @@ -24,50 +24,24 @@ > #include > #include > > -static unsigned long iram_phys_base; > -static void __iomem *iram_virt_base; > -static struct gen_pool *iram_pool; > +struct pv_pool *mxc_iram_pool; > +EXPORT_SYMBOL(mxc_iram_pool); > > -static inline void __iomem *iram_phys_to_virt(unsigned long p) > +int __init iram_init(phys_addr_t base, size_t size) > { > - return iram_virt_base + (p - iram_phys_base); > -} > - > -void __iomem *iram_alloc(unsigned int size, unsigned long *dma_addr) > -{ > - if (!iram_pool) > - return NULL; > - > - *dma_addr = gen_pool_alloc(iram_pool, size); > - pr_debug("iram alloc - %dB at 0x%lX\n", size, *dma_addr); > - if (!*dma_addr) > - return NULL; > - return iram_phys_to_virt(*dma_addr); > -} > -EXPORT_SYMBOL(iram_alloc); > - > -void iram_free(unsigned long addr, unsigned int size) > -{ > - if (!iram_pool) > - return; > - > - gen_pool_free(iram_pool, addr, size); > -} > -EXPORT_SYMBOL(iram_free); > + void *addr = /*FIXME*/ ioremap(base, size); > > -int __init iram_init(unsigned long base, unsigned long size) > -{ > - iram_phys_base = base; > + if (!addr) > + return -EIO; > > - iram_pool = gen_pool_create(PAGE_SHIFT, -1); > - if (!iram_pool) > + mxc_iram_pool = pv_pool_create(addr, base, size, PAGE_SHIFT); > + if (!mxc_iram_pool) { > + iounmap(addr); > return -ENOMEM; > + } > > - gen_pool_add(iram_pool, base, size, -1); > - iram_virt_base = ioremap(iram_phys_base, size); > - if (!iram_virt_base) > - return -EIO; > + pr_debug("i.MX IRAM pool: %lu KB at 0x%08llx\n", size / 1024, > + (unsigned long long)base); > > - pr_debug("i.MX IRAM pool: %ld KB at 0x%p\n", size / 1024, iram_virt_base); > return 0; > } > diff --git a/arch/arm/plat-omap/include/plat/sram.h b/arch/arm/plat-omap/include/plat/sram.h > index f500fc3..9fc27d0 100644 > --- a/arch/arm/plat-omap/include/plat/sram.h > +++ b/arch/arm/plat-omap/include/plat/sram.h > @@ -12,16 +12,19 @@ > #define __ARCH_ARM_OMAP_SRAM_H > > #ifndef __ASSEMBLY__ > -#include > +#include > > -extern void *omap_sram_push_address(unsigned long size); > +extern struct pv_pool *omap_pv_pool; > > -/* Macro to push a function to the internal SRAM, using the fncpy API */ > +/* > + * Note that fncpy requires the SRAM address to be aligned to an 8-byte > + * boundary, so the min_alloc_order for the pool is set appropriately. > + */ > #define omap_sram_push(funcp, size) ({ \ > - typeof(&(funcp)) _res = NULL; \ > - void *_sram_address = omap_sram_push_address(size); \ > - if (_sram_address) \ > - _res = fncpy(_sram_address, &(funcp), size); \ > + typeof(&(funcp)) _res; \ > + _res = pv_pool_fncpy(omap_pv_pool, funcp, size); \ > + if (!_res) \ > + pr_err("Not enough space in SRAM\n"); \ > _res; \ > }) > > diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c > index a3f50b3..3588749 100644 > --- a/arch/arm/plat-omap/sram.c > +++ b/arch/arm/plat-omap/sram.c > @@ -75,7 +75,6 @@ > static unsigned long omap_sram_start; > static unsigned long omap_sram_base; > static unsigned long omap_sram_size; > -static unsigned long omap_sram_ceil; > > /* > * Depending on the target RAMFS firewall setup, the public usable amount of > @@ -104,6 +103,8 @@ static int is_sram_locked(void) > return 1; /* assume locked with no PPA or security driver */ > } > > +struct pv_pool *omap_pv_pool; > + > /* > * The amount of SRAM depends on the core type. > * Note that we cannot try to test for SRAM here because writes > @@ -182,7 +183,16 @@ static void __init omap_detect_sram(void) > omap_sram_size - SRAM_BOOTLOADER_SZ); > omap_sram_size -= reserved; > > - omap_sram_ceil = omap_sram_base + omap_sram_size; > + { > + /* The first SRAM_BOOTLOADER_SZ of SRAM are reserved */ > + void *base = (void *)omap_sram_base + SRAM_BOOTLOADER_SZ; > + phys_addr_t phys = omap_sram_start + SRAM_BOOTLOADER_SZ; > + size_t len = omap_sram_size - SRAM_BOOTLOADER_SZ; > + > + omap_pv_pool = pv_pool_create(base, phys, len, > + ilog2(FNCPY_ALIGN)); > + WARN_ON(!omap_pv_pool); > + } > } > > static struct map_desc omap_sram_io_desc[] __initdata = { > @@ -242,26 +252,6 @@ static void __init omap_map_sram(void) > omap_sram_size - SRAM_BOOTLOADER_SZ); > } > > -/* > - * Memory allocator for SRAM: calculates the new ceiling address > - * for pushing a function using the fncpy API. > - * > - * Note that fncpy requires the returned address to be aligned > - * to an 8-byte boundary. > - */ > -void *omap_sram_push_address(unsigned long size) > -{ > - if (size > (omap_sram_ceil - (omap_sram_base + SRAM_BOOTLOADER_SZ))) { > - printk(KERN_ERR "Not enough space in SRAM\n"); > - return NULL; > - } > - > - omap_sram_ceil -= size; > - omap_sram_ceil = ROUND_DOWN(omap_sram_ceil, FNCPY_ALIGN); > - > - return (void *)omap_sram_ceil; > -} > - > #ifdef CONFIG_ARCH_OMAP1 > > static void (*_omap_sram_reprogram_clock)(u32 dpllctl, u32 ckctl); > diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c > index daf6e77..2be3155 100644 > --- a/drivers/uio/uio_pruss.c > +++ b/drivers/uio/uio_pruss.c > @@ -62,7 +62,7 @@ MODULE_PARM_DESC(extram_pool_sz, "external ram pool size to allocate"); > struct uio_pruss_dev { > struct uio_info *info; > struct clk *pruss_clk; > - dma_addr_t sram_paddr; > + phys_addr_t sram_paddr; > dma_addr_t ddr_paddr; > void __iomem *prussio_vaddr; > void *sram_vaddr; > @@ -106,7 +106,7 @@ static void pruss_cleanup(struct platform_device *dev, > gdev->ddr_paddr); > } > if (gdev->sram_vaddr) > - sram_free(gdev->sram_vaddr, sram_pool_sz); > + pv_pool_free(davinci_pv_pool, gdev->sram_vaddr, sram_pool_sz); > kfree(gdev->info); > clk_put(gdev->pruss_clk); > kfree(gdev); > @@ -152,7 +152,8 @@ static int __devinit pruss_probe(struct platform_device *dev) > goto out_free; > } > > - gdev->sram_vaddr = sram_alloc(sram_pool_sz, &(gdev->sram_paddr)); > + gdev->sram_vaddr = pv_pool_alloc(davinci_pv_pool, sram_pool_sz, > + &(gdev->sram_paddr)); > if (!gdev->sram_vaddr) { > dev_err(&dev->dev, "Could not allocate SRAM pool\n"); > goto out_free; > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From subhasish at mistralsolutions.com Fri Jul 8 14:01:48 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 19:01:48 -0000 Subject: [PATCH v3 7/7] tty: add pruss SUART driver In-Reply-To: <1299592667-21367-1-git-send-email-subhasish@mistralsolutions.com> References: <1299592667-21367-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1299592667-21367-8-git-send-email-subhasish@mistralsolutions.com> This patch adds support for the TTY compliant Soft-UART device emulated on PRUSS. This patch depends on: davinci: macro rename DA8XX_LPSC0_DMAX to DA8XX_LPSC0_PRUSS. https://patchwork.kernel.org/patch/615681/ davinci: changed SRAM allocator to shared ram. https://patchwork.kernel.org/patch/549351/ Signed-off-by: Subhasish Ghosh --- drivers/tty/serial/Kconfig | 22 + drivers/tty/serial/Makefile | 10 + drivers/tty/serial/pruss_suart.c | 1058 +++++++++++++++++++ drivers/tty/serial/pruss_suart.h | 1081 ++++++++++++++++++++ drivers/tty/serial/pruss_suart_api.c | 1757 ++++++++++++++++++++++++++++++++ drivers/tty/serial/pruss_suart_utils.c | 391 +++++++ include/linux/serial_core.h | 2 + 7 files changed, 4321 insertions(+), 0 deletions(-) create mode 100644 drivers/tty/serial/pruss_suart.c create mode 100644 drivers/tty/serial/pruss_suart.h create mode 100644 drivers/tty/serial/pruss_suart_api.c create mode 100644 drivers/tty/serial/pruss_suart_utils.c diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig index 2b83346..14ea0a3 100644 --- a/drivers/tty/serial/Kconfig +++ b/drivers/tty/serial/Kconfig @@ -1596,4 +1596,26 @@ config SERIAL_PCH_UART This driver is for PCH(Platform controller Hub) UART of Intel EG20T which is an IOH(Input/Output Hub) for x86 embedded processor. Enabling PCH_DMA, this PCH UART works as DMA mode. + +# +# SUART Kernel Configuration +# + +config SERIAL_PRUSS_SUART + depends on ARCH_DAVINCI && ARCH_DAVINCI_DA850 + select SERIAL_CORE + tristate "PRUSS based SoftUART emulation on DA8XX" + ---help--- + This driver emulates upto eight different UARTs on the PRUSS. + You may modify the NR_SUARTS macro in the driver to emulate + less number of UARTS as per your requirement. + If not sure, mark N + +config PRUSS_SUART_MCASP + int "McASP number" + depends on ARCH_DAVINCI && ARCH_DAVINCI_DA830 && SERIAL_PRUSS_SUART + default "0" + ---help--- + Enter the McASP number to use with SUART (0, 1 or 2). + You will need to recompile the kernel if this is changed. endmenu diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile index 8ea92e9..f52a4eb 100644 --- a/drivers/tty/serial/Makefile +++ b/drivers/tty/serial/Makefile @@ -92,3 +92,13 @@ obj-$(CONFIG_SERIAL_MRST_MAX3110) += mrst_max3110.o obj-$(CONFIG_SERIAL_MFD_HSU) += mfd.o obj-$(CONFIG_SERIAL_IFX6X60) += ifx6x60.o obj-$(CONFIG_SERIAL_PCH_UART) += pch_uart.o + +# +# Makefile for SoftUART emulation +# + +suart_emu-objs := pruss_suart.o \ + pruss_suart_api.o \ + pruss_suart_utils.o + +obj-$(CONFIG_SERIAL_PRUSS_SUART) += suart_emu.o diff --git a/drivers/tty/serial/pruss_suart.c b/drivers/tty/serial/pruss_suart.c new file mode 100644 index 0000000..c411ff8 --- /dev/null +++ b/drivers/tty/serial/pruss_suart.c @@ -0,0 +1,1058 @@ +/* + * PRUSS SUART Emulation device driver + * Author: subhasish at mistralsolutions.com + * + * This driver supports TI's PRU SUART Emulation and the + * specs for the same is available at + * + * Copyright (C) 2009 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pruss_suart.h" + +#define NR_SUART 8 +#define DRV_NAME "da8xx_pruss_uart" +#define DRV_DESC "PRUSS SUART Driver v1.0" +#define MAX_SUART_RETRIES 100 +#define SUART_CNTX_SZ 512 +#define SUART_FIFO_TIMEOUT_DFLT 5 +#define SUART_FIFO_TIMEOUT_MIN 4 +#define SUART_FIFO_TIMEOUT_MAX 500 + +/* Default timeout set to 5ms */ +static s16 suart_timeout = SUART_FIFO_TIMEOUT_DFLT; +module_param(suart_timeout, short, S_IRUGO); +MODULE_PARM_DESC(suart_timeout, + "fifo timeout in milli seconds (min: 4; max: 500)"); + +struct suart_fifo { + void *fifo_vaddr_buff_tx; + void *fifo_vaddr_buff_rx; + void *fifo_phys_addr_tx; + void *fifo_phys_addr_rx; +}; + +struct omapl_pru_suart { + struct uart_port port[NR_SUART]; + struct device *dev; + unsigned long tx_empty[NR_SUART]; + struct clk *clk_mcasp; + struct suart_fifo suart_fifo_addr[NR_SUART]; + const struct firmware *fw; + struct suart_handle suart_hdl[NR_SUART]; + struct pruss_suart_iomap suart_iomap; + struct tasklet_struct tx_task[NR_SUART]; + u32 clk_freq_pru; + u32 clk_freq_mcasp; + u32 tx_loadsz; +}; + +static u32 suart_get_duplex(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + return soft_uart->suart_hdl[uart_no].uart_type; +} + +static inline void __stop_tx(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + struct uart_port *port = &soft_uart->port[uart_no]; + u16 txready; + u32 i; + + /* Check if any TX in progress */ + for (i = 0, txready = 1; (i < 10000) && txready; i++) { + txready = (pru_softuart_get_tx_status + (dev, &soft_uart->suart_hdl[uart_no]) & + CHN_TXRX_STATUS_RDY); + } + /* To stop tx, disable the TX interrupt */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_clrmask(dev, soft_uart->suart_hdl[uart_no].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + pru_softuart_clr_tx_status(dev, &soft_uart->suart_hdl[uart_no]); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void pruss_suart_stop_tx(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + + __stop_tx(soft_uart, port->line); +} + +static void omapl_pru_tx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + struct circ_buf *xmit = &soft_uart->port[uart_no].state->xmit; + struct device *dev = soft_uart->dev; + s32 count = 0; + + if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_TX)) + return; + + if (uart_circ_empty(xmit) || + uart_tx_stopped(&soft_uart->port[uart_no])) { + pruss_suart_stop_tx(&soft_uart->port[uart_no]); + set_bit(0, &soft_uart->tx_empty[uart_no]); + return; + } + + for (count = 0; count <= soft_uart->tx_loadsz; count++) { + *((s8 *)soft_uart->suart_fifo_addr[uart_no].fifo_vaddr_buff_tx + + count) = xmit->buf[xmit->tail]; + xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1); + soft_uart->port[uart_no].icount.tx++; + if (uart_circ_empty(xmit)) { + uart_circ_clear(xmit); + break; + } + } + + if (count == (SUART_FIFO_LEN + 1)) + count = SUART_FIFO_LEN; + + /* Write the character to the data port */ + if (pru_softuart_write(dev, + &soft_uart->suart_hdl[uart_no], + (u32 *)&soft_uart->suart_fifo_addr + [uart_no].fifo_phys_addr_tx, count) != 0) { + dev_err(dev, "failed to tx data\n"); + } + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(&soft_uart->port[uart_no]); + +#if 0 + if (uart_circ_empty(xmit)) + __stop_tx(soft_uart, uart_no); +#endif +} + +static void suart_tx_task(unsigned long data) +{ + struct uart_port *port = (struct uart_port *)data; + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + + omapl_pru_tx_chars(soft_uart, port->line); +} + +static void omapl_pru_rx_chars(struct omapl_pru_suart *soft_uart, u32 uart_no) +{ + struct tty_struct *tty = NULL; + struct device *dev = soft_uart->dev; + s8 flags = TTY_NORMAL; + u16 rx_status, data_len = SUART_FIFO_LEN; + u32 data_len_read; + u8 suart_data[SUART_FIFO_LEN + 1]; + s32 i = 0; + + if (!(suart_get_duplex(soft_uart, uart_no) & ePRU_SUART_HALF_RX)) + return; + + /* read the status */ + rx_status = pru_softuart_get_rx_status(dev, + &soft_uart->suart_hdl[uart_no]); + + pru_softuart_read_data(dev, &soft_uart->suart_hdl[uart_no], + suart_data, data_len + 1, &data_len_read); + + tty = tty_port_tty_get(&soft_uart->port[uart_no].state->port); + + if (!tty) + return; + + /* check for errors */ + if (rx_status & CHN_TXRX_STATUS_ERR) { + if (rx_status & CHN_TXRX_STATUS_FE) + soft_uart->port[uart_no].icount.frame++; + if (rx_status & CHN_TXRX_STATUS_OVRNERR) + soft_uart->port[uart_no].icount.overrun++; + if (rx_status & CHN_TXRX_STATUS_BI) + soft_uart->port[uart_no].icount.brk++; + rx_status &= soft_uart->port[uart_no]. + read_status_mask; + if (rx_status & CHN_TXRX_STATUS_FE) + flags = TTY_FRAME; + if (rx_status & CHN_TXRX_STATUS_OVRNERR) + flags = TTY_OVERRUN; + if (rx_status & CHN_TXRX_STATUS_BI) + flags = TTY_BREAK; + +#ifdef SUPPORT_SYSRQ + soft_uart->port[uart_no].sysrq = 0; +#endif + } else { + for (i = 0; i <= data_len_read; i++) { + soft_uart->port[uart_no].icount.rx++; + /* check for sys rq */ + if (uart_handle_sysrq_char + (&soft_uart->port[uart_no], suart_data)) + continue; + } + tty_insert_flip_string(tty, suart_data, data_len_read); + } + + /* push data into tty */ + pru_softuart_clr_rx_status(dev, &soft_uart->suart_hdl[uart_no]); + tty_flip_buffer_push(tty); + tty_kref_put(tty); +} + +static irqreturn_t pruss_suart_interrupt(s32 irq, void *dev_id) +{ + struct uart_port *port = dev_id; + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + u16 txrx_flag; + u32 ret; + unsigned long flags = 0; + u16 uart_num = port->line + 1; + + spin_lock_irqsave(&port->lock, flags); + + do { + ret = pru_softuart_get_isrstatus(dev, uart_num, &txrx_flag); + if (ret != 0) { + dev_err(dev, "suart%d: failed to get interrupt, ret:" + " 0x%X txrx_flag 0x%X\n", + port->line, ret, txrx_flag); + spin_unlock_irqrestore(&port->lock, flags); + return IRQ_NONE; + } + if ((PRU_RX_INTR & txrx_flag) == PRU_RX_INTR) { + pru_intr_clr_isrstatus(dev, uart_num, PRU_RX_INTR); + if ((soft_uart->port[port->line].ignore_status_mask & + CHN_TXRX_STATUS_RDY) == CHN_TXRX_STATUS_RDY) { + pru_softuart_clr_rx_status(dev, + &soft_uart->suart_hdl + [port->line]); + } else { + omapl_pru_rx_chars(soft_uart, port->line); + } + } + + if ((PRU_TX_INTR & txrx_flag) == PRU_TX_INTR) { + pru_intr_clr_isrstatus(dev, uart_num, PRU_TX_INTR); + pru_softuart_clr_tx_status(dev, &soft_uart->suart_hdl + [port->line]); + tasklet_schedule(&soft_uart->tx_task[port->line]); + } + } while (txrx_flag & (PRU_RX_INTR | PRU_TX_INTR)); + + spin_unlock_irqrestore(&port->lock, flags); + return IRQ_HANDLED; +} + +static void pruss_suart_stop_rx(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + spin_lock_irqsave(&port->lock, flags); + /* disable rx interrupt */ + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + spin_unlock_irqrestore(&port->lock, flags); +} + +static void pruss_suart_enable_ms(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + dev_err(dev, "modem control timer not supported\n"); +} + +static void pruss_suart_start_tx(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + /* unmask the tx interrupts */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + spin_unlock_irqrestore(&port->lock, flags); + + if (test_and_clear_bit(0, &soft_uart->tx_empty[port->line])) + omapl_pru_tx_chars(soft_uart, port->line); +} + +static u32 pruss_suart_tx_empty(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + + return (pru_softuart_get_tx_status(dev, + &soft_uart->suart_hdl[port->line]) + & CHN_TXRX_STATUS_RDY) ? 0 : TIOCSER_TEMT; +} + +static u32 pruss_suart_get_mctrl(struct uart_port *port) +{ + return -ENOTSUPP; +} + +static void pruss_suart_set_mctrl(struct uart_port *port, u32 mctrl) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + dev_dbg(dev, "modem control not supported\n"); +} + +static void pruss_suart_break_ctl(struct uart_port *port, s32 break_state) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + spin_lock_irqsave(&port->lock, flags); + + if (break_state == -1) + suart_intr_clrmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + else + suart_intr_setmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + + spin_unlock_irqrestore(&port->lock, flags); +} + +static void pruss_suart_set_termios(struct uart_port *port, + struct ktermios *termios, + struct ktermios *old) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + u8 cval = 0; + unsigned long flags = 0; + u32 baud = 0; + u32 old_csize = old ? old->c_cflag & CSIZE : CS8; + +/* + * Do not allow unsupported configurations to be set + */ + if (1) { + termios->c_cflag &= ~(CRTSCTS | CMSPAR | CSTOPB + | PARENB | PARODD | CMSPAR); + } + + switch (termios->c_cflag & CSIZE) { + case CS6: + cval = ePRU_SUART_DATA_BITS6; + break; + case CS7: + cval = ePRU_SUART_DATA_BITS7; + break; + default: + case CS8: + cval = ePRU_SUART_DATA_BITS8; + break; + } + /* + * We do not support CS5. + */ + if ((termios->c_cflag & CSIZE) == CS5) { + termios->c_cflag &= ~CSIZE; + termios->c_cflag |= old_csize; + } + if (pru_softuart_setdatabits + (dev, &soft_uart->suart_hdl[port->line], cval, cval) != 0) + dev_err(dev, "failed to set data bits to: %d\n", cval); + +/* + * Ask the core to calculate the divisor for us. + */ + baud = uart_get_baud_rate(port, termios, old, + port->uartclk / 16 / 0xffff, + port->uartclk / 16); + +/* + * Ok, we're now changing the port state. Do it with + * interrupts disabled. + */ + spin_lock_irqsave(&port->lock, flags); + + /* Set the baud */ + if (pru_softuart_setbaud(dev, &soft_uart->suart_hdl[port->line], + SUART_DEFAULT_BAUD / baud, + SUART_DEFAULT_BAUD / baud) != 0) + dev_err(dev, "failed to set baud to: %d\n", baud); + +/* + * update port->read_config_mask and port->ignore_config_mask + * to indicate the events we are interested in receiving + */ + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, SUART_GBL_INTR_ERR_MASK); + port->read_status_mask = 0; + if (termios->c_iflag & INPCK) { /* Input parity check not supported, + just enabled FE */ + port->read_status_mask |= CHN_TXRX_STATUS_FE; + suart_intr_setmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_FE); + } + if (termios->c_iflag & (BRKINT | PARMRK)) { + port->read_status_mask |= CHN_TXRX_STATUS_BI; + suart_intr_setmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + } +/* + * Characters to ignore + */ + port->ignore_status_mask = 0; + if (termios->c_iflag & IGNBRK) { + port->ignore_status_mask |= CHN_TXRX_STATUS_BI; + /* + * If we're ignoring break indicators, + * ignore overruns too (for real raw support). + */ + if (termios->c_iflag & IGNPAR) { + port->ignore_status_mask |= + (CHN_TXRX_STATUS_OVRNERR | CHN_TXRX_STATUS_FE); + /* + * Overrun in case of RX + * Underrun in case of TX + */ + suart_intr_clrmask(dev, soft_uart-> + suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_FE); + } + suart_intr_clrmask(dev, + soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI); + } +/* + * ignore all characters if CREAD is not set + */ + if ((termios->c_cflag & CREAD) == 0) { + port->ignore_status_mask |= CHN_TXRX_STATUS_RDY; + pruss_suart_stop_rx(port); + } + /* + * update the per port timeout + */ + uart_update_timeout(port, termios->c_cflag, baud); + + spin_unlock_irqrestore(&port->lock, flags); + + /* Don't rewrite B0 */ + if (tty_termios_baud_rate(termios)) + tty_termios_encode_baud_rate(termios, baud, baud); +} + +/* + * Grab any interrupt resources and initialise any low level driver + * state. Enable the port for reception. It should not activate + * RTS nor DTR; this will be done via a separate call to set_mctrl. + * + * This method will only be called when the port is initially opened. + * + * Locking: port_sem taken. + * Interrupts: globally disabled. + */ +static s32 pruss_suart_startup(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + s32 retval; + + /* + * Disable interrupts from this port + */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + spin_unlock_irqrestore(&port->lock, flags); + + retval = request_irq(port->irq, pruss_suart_interrupt, + port->irqflags, "suart_irq", port); + if (retval) { + free_irq(port->irq, port); /* should we free this if err */ + goto out; + } + /* + * enable interrupts from this port + */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, SUART_GBL_INTR_ERR_MASK); + + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + + suart_intr_setmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + spin_unlock_irqrestore(&port->lock, flags); + + if ((suart_get_duplex(soft_uart, port->line) & ePRU_SUART_HALF_TX) + == ePRU_SUART_HALF_TX) { + suart_pru_to_host_intr_enable(dev, soft_uart-> + suart_hdl[port->line].uart_num, PRU_TX_INTR, true); + } + /* Seed RX if port is half-rx or full-duplex */ + if ((suart_get_duplex(soft_uart, port->line) & ePRU_SUART_HALF_RX) + == ePRU_SUART_HALF_RX) { + suart_pru_to_host_intr_enable(dev, soft_uart-> + suart_hdl[port->line].uart_num, PRU_RX_INTR, true); + pru_softuart_read(dev, &soft_uart->suart_hdl[port->line], + (u32 *)&soft_uart->suart_fifo_addr[port->line]. + fifo_phys_addr_rx, SUART_FIFO_LEN); + } +out: + return retval; +} + +/* + * Disable the port, disable any break condition that may be in + * effect, and free any interrupt resources. It should not disable + * RTS nor DTR; this will have already been done via a separate + * call to set_mctrl. + * + * Drivers must not access port->info once this call has completed. + * + * This method will only be called when there are no more users of + * this port. + * + * Locking: port_sem taken. + * Interrupts: caller dependent. + */ + +static void pruss_suart_shutdown(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct device *dev = soft_uart->dev; + unsigned long flags = 0; + + /* + * Disable interrupts from this port + */ + /* Disable BI and FE intr */ + spin_lock_irqsave(&port->lock, flags); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_TX_INTR, CHN_TXRX_IE_MASK_CMPLT); + suart_intr_clrmask(dev, soft_uart->suart_hdl[port->line].uart_num, + PRU_RX_INTR, CHN_TXRX_IE_MASK_BI + | CHN_TXRX_IE_MASK_FE | CHN_TXRX_IE_MASK_CMPLT + | CHN_TXRX_IE_MASK_TIMEOUT); + spin_unlock_irqrestore(&port->lock, flags); + + /* free interrupts */ + free_irq(port->irq, port); +} + +/* + * Return a pointer to a string constant describing the specified + * port, or return NULL, in which case the string 'unknown' is + * substituted. + * + * Locking: none. + * Interrupts: caller dependent. + */ + +static const char *pruss_suart_type(struct uart_port *port) +{ + return "suart_tty"; +} + +/* + * Release any memory and IO region resources currently in use by + * the port. + * + * Locking: none. + * Interrupts: caller dependent. + */ + +static void pruss_suart_release_port(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct platform_device *pdev = to_platform_device(port->dev); + + if (0 != pru_softuart_close(&soft_uart->suart_hdl[port->line])) + dev_err(&pdev->dev, "failed to close suart\n"); + + return; +} + +/* + * Request any memory and IO region resources required by the port. + * If any fail, no resources should be registered when this function + * returns, and it should return -EBUSY on failure. + * + * Locking: none. + * Interrupts: caller dependent. + * + * We need to d/l the f/w in probe and since this api + * is called per uart, the request_mem_region should + * be called in probe itself. + */ +static s32 pruss_suart_request_port(struct uart_port *port) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + struct platform_device *pdev = to_platform_device(port->dev); + struct device *dev = soft_uart->dev; + struct suart_config pru_suart_config; + s16 timeout = 0; + u32 err = 0; + + if (soft_uart == NULL) { + dev_err(&pdev->dev, "soft_uart ptr failed\n"); + return -ENODEV; + } + err = pru_softuart_open(&soft_uart->suart_hdl[port->line]); + if (err != 0) { + dev_err(&pdev->dev, "failed to open suart: %d\n", err); + err = -ENODEV; + goto exit; + } + set_bit(0, &soft_uart->tx_empty[port->line]); + + /* set fifo /timeout */ + if (SUART_FIFO_TIMEOUT_MIN > suart_timeout) { + dev_err(&pdev->dev, "fifo timeout less than %d ms not supported\n", + SUART_FIFO_TIMEOUT_MIN); + suart_timeout = SUART_FIFO_TIMEOUT_MIN; + } else if (SUART_FIFO_TIMEOUT_MAX < suart_timeout) { + dev_err(&pdev->dev, "fifo timeout more than %d ms not supported\n", + SUART_FIFO_TIMEOUT_MAX); + suart_timeout = SUART_FIFO_TIMEOUT_MAX; + } + + /* This is only for x8 */ + timeout = (SUART_DEFAULT_BAUD * suart_timeout) / 1000; + pru_set_fifo_timeout(dev, timeout); + + if (soft_uart->suart_hdl[port->line].uart_num == PRU_SUART_UART1) { + pru_suart_config.tx_serializer = PRU_SUART0_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART0_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART2) { + pru_suart_config.tx_serializer = PRU_SUART1_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART1_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART3) { + pru_suart_config.tx_serializer = PRU_SUART2_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART2_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART4) { + pru_suart_config.tx_serializer = PRU_SUART3_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART3_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART5) { + pru_suart_config.tx_serializer = PRU_SUART4_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART4_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART6) { + pru_suart_config.tx_serializer = PRU_SUART5_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART5_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART7) { + pru_suart_config.tx_serializer = PRU_SUART6_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART6_CONFIG_RX_SER; + } else if (soft_uart->suart_hdl[port->line].uart_num == + PRU_SUART_UART8) { + pru_suart_config.tx_serializer = PRU_SUART7_CONFIG_TX_SER; + pru_suart_config.rx_serializer = PRU_SUART7_CONFIG_RX_SER; + } else { + return -ENOTSUPP; + } + + /* Some defaults to startup. reconfigured by terimos later */ + pru_suart_config.tx_clk_divisor = 1; + pru_suart_config.rx_clk_divisor = 1; + pru_suart_config.tx_bits_per_char = ePRU_SUART_DATA_BITS8; + pru_suart_config.rx_bits_per_char = ePRU_SUART_DATA_BITS8; + pru_suart_config.oversampling = SUART_DEFAULT_OVRSMPL; + + if (pru_softuart_setconfig(dev, &soft_uart->suart_hdl[port->line], + &pru_suart_config) != 0) { + dev_err(&pdev->dev, + "pru_softuart_setconfig: failed to set config: %X\n", + err); + } +exit: + return err; +} + +/* + * Perform any autoconfiguration steps required for the port. `flag` + * contains a bit mask of the required configuration. UART_CONFIG_TYPE + * indicates that the port requires detection and identification. + * port->type should be set to the type found, or PORT_UNKNOWN if + * no port was detected. + * + * UART_CONFIG_IRQ indicates autoconfiguration of the interrupt signal, + * which should be probed using standard kernel autoprobing techniques. + * This is not necessary on platforms where ports have interrupts + * internally hard wired (eg, system on a chip implementations). + * + * Locking: none. + * Interrupts: caller dependent. + */ + +static void pruss_suart_config_port(struct uart_port *port, s32 flags) +{ + if (flags & UART_CONFIG_TYPE && pruss_suart_request_port(port) == 0) + port->type = PORT_DA8XX_PRU_SUART; +} + +/* + * Verify the new serial port information contained within serinfo is + * suitable for this port type. + * + * Locking: none. + * Interrupts: caller dependent. + */ +static s32 pruss_suart_verify_port(struct uart_port *port, + struct serial_struct *ser) +{ + struct omapl_pru_suart *soft_uart = + container_of(port, struct omapl_pru_suart, port[port->line]); + s32 ret = 0; + + if (ser->type != PORT_UNKNOWN && ser->type != PORT_DA8XX_PRU_SUART) + ret = -EINVAL; + if (soft_uart->port[port->line].irq != ser->irq) + ret = -EINVAL; + if (ser->io_type != UPIO_MEM) + ret = -EINVAL; + if (soft_uart->port[port->line].uartclk / 16 != ser->baud_base) + ret = -EINVAL; + if ((void *)soft_uart->port[port->line].mapbase != ser->iomem_base) + ret = -EINVAL; + if (soft_uart->port[port->line].iobase != ser->port) + ret = -EINVAL; + return ret; +} + +static struct uart_ops pruss_suart_ops = { + .tx_empty = pruss_suart_tx_empty, + .set_mctrl = pruss_suart_set_mctrl, + .get_mctrl = pruss_suart_get_mctrl, + .stop_tx = pruss_suart_stop_tx, + .start_tx = pruss_suart_start_tx, + .stop_rx = pruss_suart_stop_rx, + .enable_ms = pruss_suart_enable_ms, + .break_ctl = pruss_suart_break_ctl, + .startup = pruss_suart_startup, + .shutdown = pruss_suart_shutdown, + .set_termios = pruss_suart_set_termios, + .type = pruss_suart_type, + .release_port = pruss_suart_release_port, + .request_port = pruss_suart_request_port, + .config_port = pruss_suart_config_port, + .verify_port = pruss_suart_verify_port, +}; + +static struct uart_driver pruss_suart_reg = { + .owner = THIS_MODULE, + .driver_name = DRV_NAME, + .dev_name = "ttySU", + .major = 0, + .minor = 16, + .nr = NR_SUART, +}; + +static s32 __devinit pruss_suart_probe(struct platform_device *pdev) +{ + struct omapl_pru_suart *soft_uart; + const struct da850_evm_pruss_suart_data *pdata; + struct device *dev = &pdev->dev; + struct resource *res; + s32 err, i; + u8 *fw_data = NULL; + + pdata = dev->platform_data; + if (!pdata) { + dev_err(&pdev->dev, "platform data not found\n"); + return -EINVAL; + } + + soft_uart = kzalloc(sizeof(struct omapl_pru_suart), GFP_KERNEL); + if (!soft_uart) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get resource"); + return -ENOMEM; + } + + if (!request_mem_region(res->start, + resource_size(res), + dev_name(&pdev->dev))) { + dev_err(&pdev->dev, "mcasp memory region already claimed!\n"); + err = -EBUSY; + goto probe_exit; + } + + soft_uart->suart_iomap.mcasp_io_addr = ioremap(res->start, + resource_size(res)); + if (!soft_uart->suart_iomap.mcasp_io_addr) { + dev_err(&pdev->dev, "mcasp ioremap failed\n"); + err = -EFAULT; + goto probe_exit_1; + } + + soft_uart->suart_iomap.p_fifo_buff_virt_base = + sram_alloc(SUART_CNTX_SZ * NR_SUART * 2, + (dma_addr_t *) &soft_uart->suart_iomap.p_fifo_buff_phys_base); + if (!soft_uart->suart_iomap.p_fifo_buff_virt_base) + goto probe_exit_iounmap; + + soft_uart->clk_freq_pru = pruss_get_clk_freq(dev); + + soft_uart->clk_mcasp = clk_get(&pdev->dev, NULL); + if (IS_ERR(soft_uart->clk_mcasp)) { + dev_err(&pdev->dev, "no clock available: mcasp\n"); + err = -ENODEV; + soft_uart->clk_mcasp = NULL; + goto probe_exit_sram_free; + } + + soft_uart->clk_freq_mcasp = clk_get_rate(soft_uart->clk_mcasp); + clk_enable(soft_uart->clk_mcasp); + + err = request_firmware(&soft_uart->fw, "PRU_SUART_Emulation.bin", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "can't load firmware\n"); + err = -ENODEV; + goto probe_exit_clk; + } + dev_info(&pdev->dev, "fw size %td. downloading...\n", + soft_uart->fw->size); + + /* download firmware into pru & init */ + fw_data = kmalloc(soft_uart->fw->size, GFP_KERNEL); + memcpy((void *)fw_data, (const void *)soft_uart->fw->data, + soft_uart->fw->size); + + soft_uart->suart_iomap.pru_clk_freq = + (soft_uart->clk_freq_pru / 1000000); + + err = pru_softuart_init(dev, SUART_DEFAULT_BAUD, SUART_DEFAULT_BAUD, + SUART_DEFAULT_OVRSMPL, fw_data, + soft_uart->fw->size, &soft_uart->suart_iomap); + if (err) { + dev_err(&pdev->dev, "pruss init error\n"); + err = -ENODEV; + kfree((const void *)fw_data); + goto probe_release_fw; + } + kfree((const void *)fw_data); + + platform_set_drvdata(pdev, &soft_uart->port[0]); + soft_uart->dev = dev; + + for (i = 0; i < NR_SUART; i++) { + soft_uart->port[i].ops = &pruss_suart_ops; + soft_uart->port[i].iotype = UPIO_MEM; + soft_uart->port[i].flags = UPF_BOOT_AUTOCONF | UPF_IOREMAP; + soft_uart->port[i].mapbase = + (u32)soft_uart->suart_iomap.p_fifo_buff_virt_base; + soft_uart->port[i].membase = + (u8 *)&soft_uart->suart_iomap; + soft_uart->port[i].type = PORT_DA8XX_PRU_SUART; + soft_uart->port[i].irq = + platform_get_irq(to_platform_device(dev->parent), i); + soft_uart->port[i].dev = &pdev->dev; + soft_uart->port[i].irqflags = IRQF_SHARED; + soft_uart->port[i].uartclk = soft_uart->clk_freq_mcasp; + soft_uart->port[i].fifosize = SUART_FIFO_LEN; + soft_uart->tx_loadsz = SUART_FIFO_LEN; + soft_uart->port[i].custom_divisor = 1; + soft_uart->port[i].line = i; + soft_uart->suart_hdl[i].uart_num = i + 1; + soft_uart->port[i].serial_in = NULL; + + soft_uart->suart_fifo_addr[i].fifo_vaddr_buff_tx = + soft_uart->suart_iomap.p_fifo_buff_virt_base + + (2 * SUART_CNTX_SZ * i); + + soft_uart->suart_fifo_addr[i].fifo_vaddr_buff_rx = + soft_uart->suart_iomap.p_fifo_buff_virt_base + + ((2 * SUART_CNTX_SZ * i) + SUART_CNTX_SZ); + + soft_uart->suart_fifo_addr[i].fifo_phys_addr_tx = + soft_uart->suart_iomap.p_fifo_buff_phys_base + + (2 * SUART_CNTX_SZ * i); + + soft_uart->suart_fifo_addr[i].fifo_phys_addr_rx = + soft_uart->suart_iomap.p_fifo_buff_phys_base + + ((2 * SUART_CNTX_SZ * i) + SUART_CNTX_SZ); + + soft_uart->port[i].serial_out = NULL; + tasklet_init(&soft_uart->tx_task[i], suart_tx_task, + (unsigned long)&soft_uart->port[i]); + uart_add_one_port(&pruss_suart_reg, &soft_uart->port[i]); + } + + dev_info(&pdev->dev, + "%s device registered (pru_clk=%d, asp_clk=%d)\n", + DRV_NAME, soft_uart->clk_freq_pru, soft_uart->clk_freq_mcasp); + + return 0; + +probe_release_fw: + release_firmware(soft_uart->fw); +probe_exit_clk: + clk_put(soft_uart->clk_mcasp); + clk_disable(soft_uart->clk_mcasp); +probe_exit_sram_free: + sram_free(soft_uart->suart_iomap.p_fifo_buff_virt_base, + SUART_CNTX_SZ * NR_SUART * 2); +probe_exit_iounmap: + iounmap(soft_uart->suart_iomap.mcasp_io_addr); +probe_exit_1: + release_mem_region(res->start, + resource_size(res)); +probe_exit: + kfree(soft_uart); + return err; +} + +static s32 __devexit pruss_suart_remove(struct platform_device *pdev) +{ + struct omapl_pru_suart *soft_uart = platform_get_drvdata(pdev); + const struct da850_evm_pruss_suart_data *pdata; + struct device *dev = &pdev->dev; + struct resource *res; + int i; + + pdata = dev->platform_data; + if (!pdata) + dev_err(&pdev->dev, "platform data not found\n"); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get resource"); + return -ENOMEM; + } + + platform_set_drvdata(pdev, NULL); + + if (soft_uart) { + for (i = 0; i < NR_SUART; i++) { + uart_remove_one_port(&pruss_suart_reg, + &soft_uart->port[i]); + } + } + + sram_free(soft_uart->suart_iomap.p_fifo_buff_virt_base, + SUART_CNTX_SZ * NR_SUART * 2); + release_firmware(soft_uart->fw); + clk_put(soft_uart->clk_mcasp); + pru_mcasp_deinit(); + clk_disable(soft_uart->clk_mcasp); + iounmap(soft_uart->suart_iomap.mcasp_io_addr); + if (pdata) { + release_mem_region(res->start, + resource_size(res)); + } + kfree(soft_uart); + return 0; +} + +#define pruss_suart_suspend NULL +#define pruss_suart_resume NULL + +static struct platform_driver serial_pruss_driver = { + .probe = pruss_suart_probe, + .remove = __devexit_p(pruss_suart_remove), + .suspend = pruss_suart_suspend, + .resume = pruss_suart_resume, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static s32 __init pruss_suart_init(void) +{ + s32 ret; + + pruss_suart_reg.nr = NR_SUART; + ret = uart_register_driver(&pruss_suart_reg); + if (ret) + return ret; + ret = platform_driver_register(&serial_pruss_driver); + if (ret) + goto out; + + pr_debug("SUART serial driver loaded\n"); + return ret; +out: + uart_unregister_driver(&pruss_suart_reg); + return ret; +} + +module_init(pruss_suart_init); + +static void __exit pruss_suart_exit(void) +{ + platform_driver_unregister(&serial_pruss_driver); + uart_unregister_driver(&pruss_suart_reg); + pr_debug("SUART serial driver unloaded\n"); +} + +module_exit(pruss_suart_exit); + +/* Module information */ +MODULE_AUTHOR("Subhasish Ghosh "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION(DRV_DESC); diff --git a/drivers/tty/serial/pruss_suart.h b/drivers/tty/serial/pruss_suart.h new file mode 100644 index 0000000..8e0b40d --- /dev/null +++ b/drivers/tty/serial/pruss_suart.h @@ -0,0 +1,1081 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 _SUART_API_H_ +#define _SUART_API_H_ + +#include +#include +#include +#include + +#define SINGLE_PRU 0 +#define BOTH_PRU 1 +#define PRU_ACTIVE BOTH_PRU +#define PRU_CLK_228 228 +#define PRU_CLK_186 186 + +#define PRU_SUART_SERIALIZER_0 (0u) +#define PRU_SUART_SERIALIZER_1 (1u) +#define PRU_SUART_SERIALIZER_2 (2u) +#define PRU_SUART_SERIALIZER_3 (3u) +#define PRU_SUART_SERIALIZER_4 (4u) +#define PRU_SUART_SERIALIZER_5 (5u) +#define PRU_SUART_SERIALIZER_6 (6u) +#define PRU_SUART_SERIALIZER_7 (7u) +#define PRU_SUART_SERIALIZER_8 (8u) +#define PRU_SUART_SERIALIZER_9 (9u) +#define PRU_SUART_SERIALIZER_10 (10u) +#define PRU_SUART_SERIALIZER_11 (11u) +#define PRU_SUART_SERIALIZER_12 (12u) +#define PRU_SUART_SERIALIZER_13 (13u) +#define PRU_SUART_SERIALIZER_14 (14u) +#define PRU_SUART_SERIALIZER_15 (15u) +#define PRU_SUART_SERIALIZER_NONE (16u) + +#define PRU_SUART_UART1 (1u) +#define PRU_SUART_UART2 (2u) +#define PRU_SUART_UART3 (3u) +#define PRU_SUART_UART4 (4u) +#define PRU_SUART_UART5 (5u) +#define PRU_SUART_UART6 (6u) +#define PRU_SUART_UART7 (7u) +#define PRU_SUART_UART8 (8u) +#define PRU_SUART_UARTx_INVALID (9u) + +#define PRU_SUART_HALF_TX (1u) +#define PRU_SUART_HALF_RX (2u) +#define PRU_SUART_HALF_TX_DISABLED (4u) +#define PRU_SUART_HALF_RX_DISABLED (8u) + +#define PRU_SUART0_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART0_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART0_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART1_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ + PRU_SUART_HALF_RX) +#define PRU_SUART1_CONFIG_RX_SER (PRU_SUART_SERIALIZER_7) +#define PRU_SUART1_CONFIG_TX_SER (PRU_SUART_SERIALIZER_8) + +#define PRU_SUART2_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ + PRU_SUART_HALF_RX) +#define PRU_SUART2_CONFIG_RX_SER (PRU_SUART_SERIALIZER_9) +#define PRU_SUART2_CONFIG_TX_SER (PRU_SUART_SERIALIZER_10) + +#define PRU_SUART3_CONFIG_DUPLEX (PRU_SUART_HALF_TX | \ + PRU_SUART_HALF_RX) +#define PRU_SUART3_CONFIG_RX_SER (PRU_SUART_SERIALIZER_13) +#define PRU_SUART3_CONFIG_TX_SER (PRU_SUART_SERIALIZER_14) + +#define PRU_SUART4_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART4_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART4_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART5_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART5_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART5_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART6_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART6_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART6_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define PRU_SUART7_CONFIG_DUPLEX (PRU_SUART_HALF_TX_DISABLED | \ + PRU_SUART_HALF_RX_DISABLED) +#define PRU_SUART7_CONFIG_RX_SER (PRU_SUART_SERIALIZER_NONE) +#define PRU_SUART7_CONFIG_TX_SER (PRU_SUART_SERIALIZER_NONE) + +#define SUART_NUM_OF_CHANNELS_PER_SUART 2 +#define SUART_NUM_OF_BYTES_PER_CHANNEL 16 + +#define PRU_TX_INTR 1 +#define PRU_RX_INTR 2 + +#define CHN_TXRX_STATUS_TIMEOUT BIT(6) +#define CHN_TXRX_STATUS_BI BIT(5) +#define CHN_TXRX_STATUS_FE BIT(4) +#define CHN_TXRX_STATUS_UNERR BIT(3) +#define CHN_TXRX_STATUS_OVRNERR BIT(3) +#define CHN_TXRX_STATUS_ERR BIT(2) +#define CHN_TXRX_STATUS_CMPLT BIT(1) +#define CHN_TXRX_STATUS_RDY BIT(0) + +#define CHN_TXRX_IE_MASK_TIMEOUT BIT(14) +#define CHN_TXRX_IE_MASK_BI BIT(13) +#define CHN_TXRX_IE_MASK_FE BIT(12) +#define CHN_TXRX_IE_MASK_CMPLT BIT(1) + +#define SUART_GBL_INTR_ERR_MASK BIT(9) +#define SUART_PRU_ID_MASK 0xFF + +#define SUART_FIFO_LEN 15 +#define SUART_8X_OVRSMPL 1 +#define SUART_16X_OVRSMPL 2 +#define SUART_DEFAULT_OVRSMPL SUART_8X_OVRSMPL + +#define SUART_DEFAULT_OVRSMPL_OFFSET 26 +#define SUART_CHN_OFFSET 31 +#define SERIALIZER_OFFSET 8 + +#if (SUART_DEFAULT_OVRSMPL == SUART_16X_OVRSMPL) +#define SUART_DEFAULT_BAUD 57600 +#else +#define SUART_DEFAULT_BAUD 115200 +#endif + +#define PRU_MODE_INVALID 0x0 +#define PRU_MODE_TX_ONLY 0x1 +#define PRU_MODE_RX_ONLY 0x2 +#define PRU_MODE_RX_TX_BOTH 0x3 + +#if (PRU_ACTIVE == BOTH_PRU) +#define PRU0_MODE PRU_MODE_RX_ONLY +#define PRU1_MODE PRU_MODE_TX_ONLY +#elif (PRU_ACTIVE == SINGLE_PRU) +#define PRU0_MODE PRU_MODE_RX_TX_BOTH +#define PRU1_MODE PRU_MODE_INVALID +#else +#define PRU0_MODE PRU_MODE_INVALID +#define PRU1_MODE PRU_MODE_INVALID +#endif + +#define MCASP_XBUF_BASE_ADDR (0x01d00200) +#define MCASP_RBUF_BASE_ADDR (0x01d00280) +#define MCASP_SRCTL_BASE_ADDR (0x01d00180) + +#define MCASP_SRCTL_TX_MODE (0x000D) +#define MCASP_SRCTL_RX_MODE (0x000E) + +/* Since only PRU0 can work as RX */ +#define RX_DEFAULT_DATA_DUMP_ADDR (0x00001FC) +#define PRU_NUM_OF_CHANNELS (16) + +/* MCASP */ + +#define OMAPL_MCASP_PFUNC_AFSR_MASK (0x80000000u) +#define OMAPL_MCASP_PFUNC_AFSR_SHIFT (0x0000001Fu) +#define OMAPL_MCASP_PFUNC_AFSR_RESETVAL (0x00000000u) +/* AFSR Tokens */ +#define OMAPL_MCASP_PFUNC_AFSR_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AFSR_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AHCLKR_MASK (0x40000000u) +#define OMAPL_MCASP_PFUNC_AHCLKR_SHIFT (0x0000001Eu) +#define OMAPL_MCASP_PFUNC_AHCLKR_RESETVAL (0x00000000u) +/* AHCLKR Tokens */ +#define OMAPL_MCASP_PFUNC_AHCLKR_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AHCLKR_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_ACLKR_MASK (0x20000000u) +#define OMAPL_MCASP_PFUNC_ACLKR_SHIFT (0x0000001Du) +#define OMAPL_MCASP_PFUNC_ACLKR_RESETVAL (0x00000000u) +/* ACLKR Tokens */ +#define OMAPL_MCASP_PFUNC_ACLKR_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_ACLKR_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AFSX_MASK (0x10000000u) +#define OMAPL_MCASP_PFUNC_AFSX_SHIFT (0x0000001Cu) +#define OMAPL_MCASP_PFUNC_AFSX_RESETVAL (0x00000000u) +/* AFSX Tokens */ +#define OMAPL_MCASP_PFUNC_AFSX_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AFSX_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AHCLKX_MASK (0x08000000u) +#define OMAPL_MCASP_PFUNC_AHCLKX_SHIFT (0x0000001Bu) +#define OMAPL_MCASP_PFUNC_AHCLKX_RESETVAL (0x00000000u) +/* AHCLKX Tokens */ +#define OMAPL_MCASP_PFUNC_AHCLKX_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AHCLKX_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_ACLKX_MASK (0x04000000u) +#define OMAPL_MCASP_PFUNC_ACLKX_SHIFT (0x0000001Au) +#define OMAPL_MCASP_PFUNC_ACLKX_RESETVAL (0x00000000u) +/* ACLKX Tokens */ +#define OMAPL_MCASP_PFUNC_ACLKX_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_ACLKX_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AMUTE_MASK (0x02000000u) +#define OMAPL_MCASP_PFUNC_AMUTE_SHIFT (0x00000019u) +#define OMAPL_MCASP_PFUNC_AMUTE_RESETVAL (0x00000000u) +/* AMUTE Tokens */ +#define OMAPL_MCASP_PFUNC_AMUTE_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AMUTE_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR15_MASK (0x00008000u) +#define OMAPL_MCASP_PFUNC_AXR15_SHIFT (0x0000000Fu) +#define OMAPL_MCASP_PFUNC_AXR15_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR15_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR15_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR14_MASK (0x00004000u) +#define OMAPL_MCASP_PFUNC_AXR14_SHIFT (0x0000000Eu) +#define OMAPL_MCASP_PFUNC_AXR14_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR14_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR14_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR13_MASK (0x00002000u) +#define OMAPL_MCASP_PFUNC_AXR13_SHIFT (0x0000000Du) +#define OMAPL_MCASP_PFUNC_AXR13_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR13_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR13_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR12_MASK (0x00001000u) +#define OMAPL_MCASP_PFUNC_AXR12_SHIFT (0x0000000Cu) +#define OMAPL_MCASP_PFUNC_AXR12_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR12_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR12_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR11_MASK (0x00000800u) +#define OMAPL_MCASP_PFUNC_AXR11_SHIFT (0x0000000Bu) +#define OMAPL_MCASP_PFUNC_AXR11_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR11_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR11_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR10_MASK (0x00000400u) +#define OMAPL_MCASP_PFUNC_AXR10_SHIFT (0x0000000Au) +#define OMAPL_MCASP_PFUNC_AXR10_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR10_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR10_GPIO (0x00000001u) +#define OMAPL_MCASP_PFUNC_AXR9_MASK (0x00000200u) +#define OMAPL_MCASP_PFUNC_AXR9_SHIFT (0x00000009u) +#define OMAPL_MCASP_PFUNC_AXR9_RESETVAL (0x00000000u) +/* AXR9 Token */ +#define OMAPL_MCASP_PFUNC_AXR9_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR9_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR8_MASK (0x00000100u) +#define OMAPL_MCASP_PFUNC_AXR8_SHIFT (0x00000008u) +#define OMAPL_MCASP_PFUNC_AXR8_RESETVAL (0x00000000u) +/* AXR8 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR8_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR8_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR7_MASK (0x00000080u) +#define OMAPL_MCASP_PFUNC_AXR7_SHIFT (0x00000007u) +#define OMAPL_MCASP_PFUNC_AXR7_RESETVAL (0x00000000u) +/* AXR7 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR7_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR7_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR6_MASK (0x00000040u) +#define OMAPL_MCASP_PFUNC_AXR6_SHIFT (0x00000006u) +#define OMAPL_MCASP_PFUNC_AXR6_RESETVAL (0x00000000u) +/* AXR6 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR6_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR6_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR5_MASK (0x00000020u) +#define OMAPL_MCASP_PFUNC_AXR5_SHIFT (0x00000005u) +#define OMAPL_MCASP_PFUNC_AXR5_RESETVAL (0x00000000u) +/* AXR5 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR5_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR5_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR4_MASK (0x00000010u) +#define OMAPL_MCASP_PFUNC_AXR4_SHIFT (0x00000004u) +#define OMAPL_MCASP_PFUNC_AXR4_RESETVAL (0x00000000u) +/* AXR4 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR4_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR4_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR3_MASK (0x00000008u) +#define OMAPL_MCASP_PFUNC_AXR3_SHIFT (0x00000003u) +#define OMAPL_MCASP_PFUNC_AXR3_RESETVAL (0x00000000u) +/* AXR3 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR3_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR3_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR2_MASK (0x00000004u) +#define OMAPL_MCASP_PFUNC_AXR2_SHIFT (0x00000002u) +#define OMAPL_MCASP_PFUNC_AXR2_RESETVAL (0x00000000u) +/* AXR2 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR2_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR2_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR1_MASK (0x00000002u) +#define OMAPL_MCASP_PFUNC_AXR1_SHIFT (0x00000001u) +#define OMAPL_MCASP_PFUNC_AXR1_RESETVAL (0x00000000u) +/* AXR1 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR1_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR1_GPIO (0x00000001u) + +#define OMAPL_MCASP_PFUNC_AXR0_MASK (0x00000001u) +#define OMAPL_MCASP_PFUNC_AXR0_SHIFT (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR0_RESETVAL (0x00000000u) +/* AXR0 Tokens */ +#define OMAPL_MCASP_PFUNC_AXR0_MCASP (0x00000000u) +#define OMAPL_MCASP_PFUNC_AXR0_GPIO (0x00000001u) +#define OMAPL_MCASP_PFUNC_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_PDIR_AFSR_MASK (0x80000000u) +#define OMAPL_MCASP_PDIR_AFSR_SHIFT (0x0000001Fu) +#define OMAPL_MCASP_PDIR_AFSR_RESETVAL (0x00000000u) +/* AFSR Tokens */ +#define OMAPL_MCASP_PDIR_AFSR_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AFSR_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AHCLKR_MASK (0x40000000u) +#define OMAPL_MCASP_PDIR_AHCLKR_SHIFT (0x0000001Eu) +#define OMAPL_MCASP_PDIR_AHCLKR_RESETVAL (0x00000000u) +/* AHCLKR Tokens */ +#define OMAPL_MCASP_PDIR_AHCLKR_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AHCLKR_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_ACLKR_MASK (0x20000000u) +#define OMAPL_MCASP_PDIR_ACLKR_SHIFT (0x0000001Du) +#define OMAPL_MCASP_PDIR_ACLKR_RESETVAL (0x00000000u) +/* ACLKR Tokens */ +#define OMAPL_MCASP_PDIR_ACLKR_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_ACLKR_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AFSX_MASK (0x10000000u) +#define OMAPL_MCASP_PDIR_AFSX_SHIFT (0x0000001Cu) +#define OMAPL_MCASP_PDIR_AFSX_RESETVAL (0x00000000u) +/* AFSX Tokens */ +#define OMAPL_MCASP_PDIR_AFSX_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AFSX_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AHCLKX_MASK (0x08000000u) +#define OMAPL_MCASP_PDIR_AHCLKX_SHIFT (0x0000001Bu) +#define OMAPL_MCASP_PDIR_AHCLKX_RESETVAL (0x00000000u) +/* AHCLKX Tokens */ +#define OMAPL_MCASP_PDIR_AHCLKX_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AHCLKX_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_ACLKX_MASK (0x04000000u) +#define OMAPL_MCASP_PDIR_ACLKX_SHIFT (0x0000001Au) +#define OMAPL_MCASP_PDIR_ACLKX_RESETVAL (0x00000000u) +/* ACLKX Tokens */ +#define OMAPL_MCASP_PDIR_ACLKX_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_ACLKX_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AMUTE_MASK (0x02000000u) +#define OMAPL_MCASP_PDIR_AMUTE_SHIFT (0x00000019u) +#define OMAPL_MCASP_PDIR_AMUTE_RESETVAL (0x00000000u) +/* AMUTE Tokens */ +#define OMAPL_MCASP_PDIR_AMUTE_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AMUTE_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR15_MASK (0x00008000u) +#define OMAPL_MCASP_PDIR_AXR15_SHIFT (0x0000000Fu) +#define OMAPL_MCASP_PDIR_AXR15_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR15_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR15_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR14_MASK (0x00004000u) +#define OMAPL_MCASP_PDIR_AXR14_SHIFT (0x0000000Eu) +#define OMAPL_MCASP_PDIR_AXR14_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR14_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR14_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR13_MASK (0x00002000u) +#define OMAPL_MCASP_PDIR_AXR13_SHIFT (0x0000000Du) +#define OMAPL_MCASP_PDIR_AXR13_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR13_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR13_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR12_MASK (0x00001000u) +#define OMAPL_MCASP_PDIR_AXR12_SHIFT (0x0000000Cu) +#define OMAPL_MCASP_PDIR_AXR12_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR12_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR12_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR11_MASK (0x00000800u) +#define OMAPL_MCASP_PDIR_AXR11_SHIFT (0x0000000Bu) +#define OMAPL_MCASP_PDIR_AXR11_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR11_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR11_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR10_MASK (0x00000400u) +#define OMAPL_MCASP_PDIR_AXR10_SHIFT (0x0000000Au) +#define OMAPL_MCASP_PDIR_AXR10_RESETVAL (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR10_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR10_OUTPUT (0x00000001u) +#define OMAPL_MCASP_PDIR_AXR9_MASK (0x00000200u) +#define OMAPL_MCASP_PDIR_AXR9_SHIFT (0x00000009u) +#define OMAPL_MCASP_PDIR_AXR9_RESETVAL (0x00000000u) +/* AXR9 Tokens */ +#define OMAPL_MCASP_PDIR_AXR9_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR9_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR8_MASK (0x00000100u) +#define OMAPL_MCASP_PDIR_AXR8_SHIFT (0x00000008u) +#define OMAPL_MCASP_PDIR_AXR8_RESETVAL (0x00000000u) +/* AXR8 Tokens */ +#define OMAPL_MCASP_PDIR_AXR8_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR8_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR7_MASK (0x00000080u) +#define OMAPL_MCASP_PDIR_AXR7_SHIFT (0x00000007u) +#define OMAPL_MCASP_PDIR_AXR7_RESETVAL (0x00000000u) +/*----AXR7 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR7_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR7_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR6_MASK (0x00000040u) +#define OMAPL_MCASP_PDIR_AXR6_SHIFT (0x00000006u) +#define OMAPL_MCASP_PDIR_AXR6_RESETVAL (0x00000000u) +/*----AXR6 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR6_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR6_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR5_MASK (0x00000020u) +#define OMAPL_MCASP_PDIR_AXR5_SHIFT (0x00000005u) +#define OMAPL_MCASP_PDIR_AXR5_RESETVAL (0x00000000u) +/*----AXR5 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR5_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR5_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR4_MASK (0x00000010u) +#define OMAPL_MCASP_PDIR_AXR4_SHIFT (0x00000004u) +#define OMAPL_MCASP_PDIR_AXR4_RESETVAL (0x00000000u) +/*----AXR4 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR4_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR4_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR3_MASK (0x00000008u) +#define OMAPL_MCASP_PDIR_AXR3_SHIFT (0x00000003u) +#define OMAPL_MCASP_PDIR_AXR3_RESETVAL (0x00000000u) +/*----AXR3 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR3_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR3_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR2_MASK (0x00000004u) +#define OMAPL_MCASP_PDIR_AXR2_SHIFT (0x00000002u) +#define OMAPL_MCASP_PDIR_AXR2_RESETVAL (0x00000000u) +/*----AXR2 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR2_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR2_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR1_MASK (0x00000002u) +#define OMAPL_MCASP_PDIR_AXR1_SHIFT (0x00000001u) +#define OMAPL_MCASP_PDIR_AXR1_RESETVAL (0x00000000u) +/*----AXR1 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR1_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR1_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_AXR0_MASK (0x00000001u) +#define OMAPL_MCASP_PDIR_AXR0_SHIFT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR0_RESETVAL (0x00000000u) +/*----AXR0 Tokens----*/ +#define OMAPL_MCASP_PDIR_AXR0_INPUT (0x00000000u) +#define OMAPL_MCASP_PDIR_AXR0_OUTPUT (0x00000001u) + +#define OMAPL_MCASP_PDIR_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_ACLKXCTL_CLKXP_MASK (0x00000080u) +#define OMAPL_MCASP_ACLKXCTL_CLKXP_SHIFT (0x00000007u) +#define OMAPL_MCASP_ACLKXCTL_CLKXP_RESETVAL (0x00000000u) +/*----CLKXP Tokens----*/ +#define OMAPL_MCASP_ACLKXCTL_CLKXP_RISINGEDGE (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_CLKXP_FALLINGEDGE (0x00000001u) + +#define OMAPL_MCASP_ACLKXCTL_ASYNC_MASK (0x00000040u) +#define OMAPL_MCASP_ACLKXCTL_ASYNC_SHIFT (0x00000006u) +#define OMAPL_MCASP_ACLKXCTL_ASYNC_RESETVAL (0x00000001u) +/*----ASYNC Tokens----*/ +#define OMAPL_MCASP_ACLKXCTL_ASYNC_SYNC (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_ASYNC_ASYNC (0x00000001u) + +#define OMAPL_MCASP_ACLKXCTL_CLKXM_MASK (0x00000020u) +#define OMAPL_MCASP_ACLKXCTL_CLKXM_SHIFT (0x00000005u) +#define OMAPL_MCASP_ACLKXCTL_CLKXM_RESETVAL (0x00000001u) +/*----CLKXM Tokens----*/ +#define OMAPL_MCASP_ACLKXCTL_CLKXM_EXTERNAL (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_CLKXM_INTERNAL (0x00000001u) + +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_MASK (0x0000001Fu) +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT (0x00000000u) +#define OMAPL_MCASP_ACLKXCTL_CLKXDIV_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_ACLKXCTL_RESETVAL (0x00000060u) + +/* AHCLKXCTL */ +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_MASK (0x00008000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_SHIFT (0x0000000Fu) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_RESETVAL (0x00000001u) +/*----HCLKXM Tokens----*/ +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_EXTERNAL (0x00000000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXM_INTERNAL (0x00000001u) + +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_MASK (0x00004000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_SHIFT (0x0000000Eu) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_RESETVAL (0x00000000u) +/*----HCLKXP Tokens----*/ +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_NOTINVERTED (0x00000000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXP_INVERTED (0x00000001u) + +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_MASK (0x00000FFFu) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT (0x00000000u) +#define OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_RESETVAL (0x00000000u) + +#define OMAPL_MCASP_AHCLKXCTL_RESETVAL (0x00008000u) + +#define MCASP_SUART_GBLCTL (0X00000000) +#define MCASP_SUART_RGBLCTL (0X00000000) +#define MCASP_SUART_XGBLCTL (0X00000000) +#define MCASP_SUART_RMASK_8 (0x000000FF) +#define MCASP_SUART_RMASK_16 (0x0000FFFF) +#define MCASP_SUART_RFMT_8 (0x0000A038) +#define MCASP_SUART_RFMT_16 (0x0000A078) +#define MCASP_SUART_FSRM (0X00000002) +#define MCASP_SUART_CLKRM_CLKRP (0X000000A0) +#define MCASP_SUART_HCLKRP (0X00008000) +#define MCASP_SUART_RTDMS0 (0X00000001) +#define MCASP_SUART_RSYNCERR (0X00000002) +#define MCASP_SUART_RMAX_RPS_256 (0x00FF0008) +#define MCASP_SUART_XMASK_0_31 (0X0000FFFF) +#define MCASP_SUART_XBUSEL_XSSZ_16_XPAD_0 (0x00002078) +#define MCASP_SUART_FSXM (0x00000002) +#define MCASP_SUART_CLKXM_ASYNC_CLKXP (0x000000E0) +#define MCASP_SUART_HCLKXM (0x00008000) +#define MCASP_SUART_XTDMS0 (0X00000001) +#define MCASP_SUART_XSYNCERR (0x00000002) +#define MCASP_SUART_XMAX_XPS_256 (0x00FF0008) +#define MCASP_SUART_SRCTL_DISMOD (0x0000000c) +#define MCASP_SUART_DIT_DISABLE (0X00000000) +#define MCASP_SUART_LOOPBACK_DISABLE (0x00000000) +#define MCASP_SUART_AMUTE_DISABLE (0X00000000) +#define MCASP_SUART_XSTAT (0x0000FFFF) +#define MCASP_SUART_RSTAT (0x0000FFFF) + +/* SUART REGS */ + +/* PRU0 DATA RAM base address */ +#define PRU0_DATARAM_OFFSET (0x0000u) +/* PRU1 DATA RAM base address */ +#define PRU1_DATARAM_OFFSET (0x2000u) + +/* PRU0 DATA RAM size */ +#define PRU0_DATARAM_SIZE (0x200u) +/* PRU1 DATA RAM size */ +#define PRU1_DATARAM_SIZE (0x200u) + +#define PRU_SUART_PRU0_CH0_OFFSET (0x0000) +#define PRU_SUART_PRU0_CH1_OFFSET (0x0010) +#define PRU_SUART_PRU0_CH2_OFFSET (0x0020) +#define PRU_SUART_PRU0_CH3_OFFSET (0x0030) +#define PRU_SUART_PRU0_CH4_OFFSET (0x0040) +#define PRU_SUART_PRU0_CH5_OFFSET (0x0050) +#define PRU_SUART_PRU0_CH6_OFFSET (0x0060) +#define PRU_SUART_PRU0_CH7_OFFSET (0x0070) +#define PRU_SUART_PRU0_IMR_OFFSET (0x0080) +/* Interrupt Mask Register */ +#define PRU_SUART_PRU0_ISR_OFFSET (0x0082) +/* Interrupt Status Register */ +#define PRU_SUART_PRU0_ID_ADDR (0x0084) +/* PRU ID Register */ +#define PRU_SUART_PRU0_RX_TX_MODE (0x0085) +#define PRU_SUART_PRU0_DELAY_OFFSET (0x0086) +#define PRU_SUART_PRU0_IDLE_TIMEOUT_OFFSET (0x0088) + +/* PRU 1 Macros */ +#define PRU_SUART_PRU1_CH0_OFFSET (0x2000) +#define PRU_SUART_PRU1_CH1_OFFSET (0x2010) +#define PRU_SUART_PRU1_CH2_OFFSET (0x2020) +#define PRU_SUART_PRU1_CH3_OFFSET (0x2030) +#define PRU_SUART_PRU1_CH4_OFFSET (0x2040) +#define PRU_SUART_PRU1_CH5_OFFSET (0x2050) +#define PRU_SUART_PRU1_CH6_OFFSET (0x2060) +#define PRU_SUART_PRU1_CH7_OFFSET (0x2070) +#define PRU_SUART_PRU1_IMR_OFFSET (0x2080) +#define PRU_SUART_PRU1_ISR_OFFSET (0x2082) +#define PRU_SUART_PRU1_ID_ADDR (0x2084) +#define PRU_SUART_PRU1_RX_TX_MODE (0x2085) +#define PRU_SUART_PRU1_DELAY_OFFSET (0x2086) +#define PRU_SUART_PRU1_IDLE_TIMEOUT_OFFSET (0x2088) + +/* SUART Channel Control Register bit descriptions */ +#define PRU_SUART_CH_CTRL_MODE_SHIFT 0x0000 +#define PRU_SUART_CH_CTRL_MODE_MASK 0x0003 +#define PRU_SUART_CH_CTRL_TX_MODE 0x0001 +#define PRU_SUART_CH_CTRL_RX_MODE 0x0002 + +/* Service Request */ +#define PRU_SUART_CH_CTRL_SREQ_SHIFT 0x0002 +#define PRU_SUART_CH_CTRL_SREQ_MASK 0x0004 +#define PRU_SUART_CH_CTRL_SREQ 0x0001 + +/* McASP Instance */ +#define PRU_SUART_CH_CTRL_MCASP_SHIFT 0x0003 +#define PRU_SUART_CH_CTRL_MCASP_MASK 0x0018 +#define PRU_SUART_CH_CTRL_SR_SHIFT 0x0008 +#define PRU_SUART_CH_CTRL_SR_MASK 0x0F00 + +/* SUART channel configuration1 register descriptions */ + +/* clock divisor - relative baud value */ +#define PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT 0x0000 +#define PRU_SUART_CH_CONFIG1_DIVISOR_MASK 0x03FF +/* oversampling */ +#define PRU_SUART_CH_CONFIG1_OVS_SHIFT 0x000A +#define PRU_SUART_CH_CONFIG1_OVS_MASK 0x0C00 + +/* SUART channel configuration2 register descriptions */ +/* Bits per character */ +#define PRU_SUART_CH_CONFIG2_BITPERCHAR_SHIFT 0x0000 +#define PRU_SUART_CH_CONFIG2_BITPERCHAR_MASK 0x000F + +/* Bits per character */ +#define PRU_SUART_CH_CONFIG2_DATALEN_SHIFT 0x0008 +#define PRU_SUART_CH_CONFIG2_DATALEN_MASK 0x0F00 + +/* SUART Channel STATUS Register*/ +#define PRU_SUART_CH_STATUS_EN_BIT_MASK 0x8000 + +/* SUART Channel register offsets */ +#define PRU_SUART_CH_CTRL_OFFSET 0x00 +#define PRU_SUART_CH_CONFIG1_OFFSET 0x02 +#define PRU_SUART_CH_CONFIG2_OFFSET 0x04 +#define PRU_SUART_CH_TXRXSTATUS_OFFSET 0x06 +#define PRU_SUART_CH_TXRXDATA_OFFSET 0x08 +#define PRU_SUART_CH_BYTESDONECNTR_OFFSET 0x0C + +/* SUART Event Numbers macros */ +#define PRU_SUART0_TX_EVT 34 +#define PRU_SUART0_RX_EVT 35 +#define PRU_SUART1_TX_EVT 36 +#define PRU_SUART1_RX_EVT 37 +#define PRU_SUART2_TX_EVT 38 +#define PRU_SUART2_RX_EVT 39 +#define PRU_SUART3_TX_EVT 40 +#define PRU_SUART3_RX_EVT 41 +#define PRU_SUART4_TX_EVT 42 +#define PRU_SUART4_RX_EVT 43 +#define PRU_SUART5_TX_EVT 44 +#define PRU_SUART5_RX_EVT 45 +#define PRU_SUART6_TX_EVT 46 +#define PRU_SUART6_RX_EVT 47 +#define PRU_SUART7_TX_EVT 48 +#define PRU_SUART7_RX_EVT 49 + +#define PRU_SUART0_TX_EVT_BIT BIT(2) +#define PRU_SUART0_RX_EVT_BIT BIT(3) +#define PRU_SUART1_TX_EVT_BIT BIT(4) +#define PRU_SUART1_RX_EVT_BIT BIT(5) +#define PRU_SUART2_TX_EVT_BIT BIT(6) +#define PRU_SUART2_RX_EVT_BIT BIT(7) +#define PRU_SUART3_TX_EVT_BIT BIT(8) +#define PRU_SUART3_RX_EVT_BIT BIT(9) +#define PRU_SUART4_TX_EVT_BIT BIT(10) +#define PRU_SUART4_RX_EVT_BIT BIT(11) +#define PRU_SUART5_TX_EVT_BIT BIT(12) +#define PRU_SUART5_RX_EVT_BIT BIT(13) +#define PRU_SUART6_TX_EVT_BIT BIT(14) +#define PRU_SUART6_RX_EVT_BIT BIT(15) +#define PRU_SUART7_TX_EVT_BIT BIT(16) +#define PRU_SUART7_RX_EVT_BIT BIT(17) + +/* Total number of baud rates supported */ +#define SUART_NUM_OF_BAUDS_SUPPORTED 13 + +#define MCASP_PDIR_VAL ( \ + OMAPL_MCASP_PDIR_AFSR_OUTPUT< + * + * 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 +#include +#include +#include +#include "pruss_suart.h" + +static u8 uart_statu_table[8]; +static struct pruss_suart_iomap suart_iomap; + +static u32 uart_rx[8] = {PRU_SUART0_CONFIG_RX_SER, PRU_SUART1_CONFIG_RX_SER, + PRU_SUART2_CONFIG_RX_SER, PRU_SUART3_CONFIG_RX_SER, + PRU_SUART4_CONFIG_RX_SER, PRU_SUART5_CONFIG_RX_SER, + PRU_SUART6_CONFIG_RX_SER, PRU_SUART7_CONFIG_RX_SER}; + +static u32 uart_tx[8] = {PRU_SUART0_CONFIG_TX_SER, PRU_SUART1_CONFIG_TX_SER, + PRU_SUART2_CONFIG_TX_SER, PRU_SUART3_CONFIG_TX_SER, + PRU_SUART4_CONFIG_TX_SER, PRU_SUART5_CONFIG_TX_SER, + PRU_SUART6_CONFIG_TX_SER, PRU_SUART7_CONFIG_TX_SER}; + +static u32 uart_config[8] = {PRU_SUART0_CONFIG_DUPLEX, PRU_SUART1_CONFIG_DUPLEX, + PRU_SUART2_CONFIG_DUPLEX, PRU_SUART3_CONFIG_DUPLEX, + PRU_SUART4_CONFIG_DUPLEX, PRU_SUART5_CONFIG_DUPLEX, + PRU_SUART6_CONFIG_DUPLEX, PRU_SUART7_CONFIG_DUPLEX}; + +static s16 pru_softuart_clr_rx_fifo(struct device *dev, + struct suart_handle *h_uart); +static s16 arm_to_pru_intr_init(struct device *dev); + +#if (PRU_ACTIVE == BOTH_PRU) +static void pru_set_ram_data(struct device *dev, + struct pruss_suart_iomap *pruss_ioaddr) +{ + u32 datatowrite; + u32 i; + struct pru_suart_regs_ovly *pru_suart_regs = PRU0_DATARAM_OFFSET; + u32 *p_sr_ctl_addr = (u32 *)(pruss_ioaddr->mcasp_io_addr + 0x180); + struct pru_suart_tx_cntx_priv *pru_suart_tx_priv = NULL; + struct pru_suart_rx_cntx_priv *pru_suart_rx_priv = NULL; + + /* RX PRU - 0 Chanel 0-7 context information */ + for (i = 0; i < 8; i++, pru_suart_regs++) { + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + 0x3, SUART_CHN_RX); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0xF << SERIALIZER_OFFSET), + ((0xF & uart_rx[i]) << SERIALIZER_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), + (SUART_DEFAULT_OVRSMPL << + SUART_DEFAULT_OVRSMPL_OFFSET)); + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + 0xF, 8); + if ((uart_config[i] & PRU_SUART_HALF_RX_DISABLED) == + PRU_SUART_HALF_RX_DISABLED) { + pruss_rmwl(dev, (u32) + &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); + } else { + pruss_rmwl(dev, (u32) + &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); + __raw_writel(MCASP_SRCTL_RX_MODE, p_sr_ctl_addr + + uart_rx[i]); + } + /* + * RX is active by default, write the dummy received data at + * PRU RAM addr 0x1FC to avoid memory corruption. + */ + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_txrx_data, + 0xFFFF, RX_DEFAULT_DATA_DUMP_ADDR); + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, + 0xFFFF, 0); + /* SUART1 RX context base addr */ + pru_suart_rx_priv = (struct pru_suart_rx_cntx_priv *) + (PRU0_DATARAM_OFFSET + (0x090 + (i * 0x020))); + datatowrite = (MCASP_RBUF_BASE_ADDR + (uart_rx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rbuf_base, + &datatowrite, 1); + datatowrite = (MCASP_SRCTL_BASE_ADDR + (uart_rx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rsrctl_base, + &datatowrite, 1); + } + + /* ****************** PRU1 RAM BASE ADDR ************************ */ + pru_suart_regs = (struct pru_suart_regs_ovly *) PRU1_DATARAM_OFFSET; + + /* ******************* TX PRU - 1 *********************** */ + /* Channel 0-7 context information */ + for (i = 0; i < 8; i++, pru_suart_regs++) { + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + 0x3, SUART_CHN_TX); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0xF << SERIALIZER_OFFSET), + ((0xF & uart_tx[i]) << SERIALIZER_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), + (SUART_DEFAULT_OVRSMPL << + SUART_DEFAULT_OVRSMPL_OFFSET)); + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + 0xF, 8); + if ((uart_config[i] & PRU_SUART_HALF_TX_DISABLED) == + PRU_SUART_HALF_TX_DISABLED) { + pruss_rmwl(dev, (u32) + &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); + } else { + pruss_rmwl(dev, (u32) + &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); + __raw_writel(MCASP_SRCTL_TX_MODE, + p_sr_ctl_addr + uart_tx[i]); + } + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, + 0xFFFF, 1); + /* SUART1 TX context base addr */ + pru_suart_tx_priv = (struct pru_suart_tx_cntx_priv *) + (PRU1_DATARAM_OFFSET + (0x0B0 + (i * 0x02C))); + datatowrite = (MCASP_SRCTL_BASE_ADDR + (uart_tx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xsrctl_base, + &datatowrite, 1); + datatowrite = (MCASP_XBUF_BASE_ADDR + (uart_tx[i] << 2)); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xbuf_base, + &datatowrite, 1); + /* SUART1 TX formatted data base addr */ + datatowrite = (0x0090 + (i * 0x002C)); + pruss_writel(dev, (u32) &pru_suart_tx_priv->buff_addr, + &datatowrite, 1); + } +} +#else +static void pru_set_ram_data(struct device *dev, + struct pruss_suart_iomap *pruss_ioaddr) +{ + + struct pru_suart_regs_ovly *pru_suart_regs = + (struct pru_suart_regs_ovly *)pruss_ioaddr->pru_io_addr; + u32 i; + u32 *p_sr_ctl_addr = (u32 *)(pruss_ioaddr->mcasp_io_addr + 0x180); + struct pru_suart_tx_cntx_priv *pru_suart_tx_priv = NULL; + struct pru_suart_rx_cntx_priv *pru_suart_rx_priv = NULL; + + /* ***************** UART 0 ************************ */ + /* Channel 0 context information is Tx */ + for (i = 0; i < 4; i++, pru_suart_regs++) { + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + 0x3, SUART_CHN_TX); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0xF << SERIALIZER_OFFSET), + ((0xF & uart_tx[i]) << SERIALIZER_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), + (SUART_DEFAULT_OVRSMPL << + SUART_DEFAULT_OVRSMPL_OFFSET)); + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + 0xF, 8); + if ((uart_config[i] & PRU_SUART_HALF_TX_DISABLED) == + PRU_SUART_HALF_TX_DISABLED){ + pruss_rmwl(dev, (u32) + &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); + } else { + pruss_rmwl(dev, (u32) + &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); + __raw_writel(MCASP_SRCTL_TX_MODE, + p_sr_ctl_addr + uart_tx[i]); + } + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, + 0xFFFF, 1); + /* SUART1 TX context base addr */ + pru_suart_tx_priv = (struct pru_suart_tx_cntx_priv *) + (PRU0_DATARAM_OFFSET + (0x0B0 + (i * 0x50))); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xsrctl_base, + (MCASP_SRCTL_BASE_ADDR + (uart_tx[i] << 2)), 1); + pruss_writel(dev, (u32) &pru_suart_tx_priv->asp_xbuf_base, + (MCASP_XBUF_BASE_ADDR + (uart_tx[i] << 2)), 1); + /* SUART1 TX formatted data base addr */ + pruss_writel(dev, (u32) &pru_suart_tx_priv->buff_addr, + (0x0090 + (i * 0x050)), 1); + + /* Channel 1 is Rx context information */ + pru_suart_regs++; + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + 0x3, SUART_CHN_RX); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0xF << SERIALIZER_OFFSET), + ((0xF & uart_rx[i]) << SERIALIZER_OFFSET)); + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_ctrl_config1, + (0x3 << SUART_DEFAULT_OVRSMPL_OFFSET), + (SUART_DEFAULT_OVRSMPL << + SUART_DEFAULT_OVRSMPL_OFFSET)); + pruss_rmwl(dev, + (u32) &pru_suart_regs->ch_config2_txrx_status, + 0xF, 8); + + if ((uart_config[i] & PRU_SUART_HALF_RX_DISABLED) == + PRU_SUART_HALF_RX_DISABLED) { + pruss_rmwl(dev, (u32) + &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_DISABLED << SUART_CHN_OFFSET)); + } else { + pruss_rmwl(dev, (u32) + &pru_suart_regs->ch_config2_txrx_status, + (0x1 << SUART_CHN_OFFSET), + (SUART_CHN_ENABLED << SUART_CHN_OFFSET)); + __raw_writel(MCASP_SRCTL_RX_MODE, + p_sr_ctl_addr + uart_rx[i]); + } + /* RX is active by default, write the dummy received data + * at PRU RAM addr 0x1FC to avoid memory corruption + */ + pruss_rmwl(dev, (u32) &pru_suart_regs->ch_txrx_data, + 0xFFFF, RX_DEFAULT_DATA_DUMP_ADDR); + pruss_rmwl(dev, (u32) &pru_suart_regs->reserved1, + 0xFFFF, 0); + /* SUART1 RX context base addr */ + pru_suart_rx_priv = (struct pru_suart_rx_cntx_priv *) + (PRU0_DATARAM_OFFSET + (0x0C0 + (i * 0x50))); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rbuf_base, + (MCASP_RBUF_BASE_ADDR + (uart_rx[i] << 2)), 1); + pruss_writel(dev, (u32) &pru_suart_rx_priv->asp_rsrctl_base, + (MCASP_SRCTL_BASE_ADDR + (uart_rx[i] << 2)), 1); + } +} +#endif + +static void pru_set_rx_tx_mode(struct device *dev, u32 pru_mode, u32 pru_num) +{ + + u32 pru_offset; + + if (pru_num == PRUSS_NUM0) + pru_offset = PRU_SUART_PRU0_RX_TX_MODE; + else if (pru_num == PRUSS_NUM1) + pru_offset = PRU_SUART_PRU1_RX_TX_MODE; + else + return; + pruss_writeb(dev, pru_offset, (u8 *) &pru_mode, 1); +} + +static void pru_set_delay_count(struct device *dev, u32 pru_freq) +{ + u32 delay_cnt; + + if (pru_freq == PRU_CLK_228) + delay_cnt = 5; + else if (pru_freq == PRU_CLK_186) + delay_cnt = 5; + else + delay_cnt = 3; + + /* PRU 0 */ + pruss_writeb(dev, PRU_SUART_PRU0_DELAY_OFFSET, + (u8 *) &delay_cnt, 1); + + /* PRU 1 */ + pruss_writeb(dev, PRU_SUART_PRU1_DELAY_OFFSET, + (u8 *) &delay_cnt, 1); +} + +static s32 suart_set_pru_id(struct device *dev, u32 pru_no) +{ + u32 offset; + u8 reg_val = 0; + + if (PRUSS_NUM0 == pru_no) + offset = PRU_SUART_PRU0_ID_ADDR; + else if (PRUSS_NUM1 == pru_no) + offset = PRU_SUART_PRU1_ID_ADDR; + else + return -EINVAL; + + reg_val = pru_no; + pruss_writeb(dev, offset, (u8 *) ®_val, 1); + + return 0; +} + +/* + * suart Initialization routine + */ +s16 pru_softuart_init(struct device *dev, u32 tx_baud_value, + u32 rx_baud_value, u32 oversampling, + u8 *pru_suart_emu_code, u32 fw_size, + struct pruss_suart_iomap *pruss_ioaddr) +{ + u32 datatowrite[128] = {0}; + s16 status = 0; + s16 idx; + s16 retval; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) && + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) + return -EINVAL; + + suart_iomap.mcasp_io_addr = pruss_ioaddr->mcasp_io_addr; + suart_iomap.p_fifo_buff_phys_base = + pruss_ioaddr->p_fifo_buff_phys_base; + suart_iomap.p_fifo_buff_virt_base = + pruss_ioaddr->p_fifo_buff_virt_base; + suart_iomap.pru_clk_freq = pruss_ioaddr->pru_clk_freq; + /* Configure McASP0 */ + suart_mcasp_config(tx_baud_value, + rx_baud_value, oversampling, pruss_ioaddr); + pruss_enable(dev, PRUSS_NUM0); + + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_enable(dev, PRUSS_NUM1); + + /* Reset PRU RAM */ + pruss_writel(dev, PRU0_DATARAM_OFFSET, datatowrite, + (PRU0_DATARAM_SIZE / sizeof(int))); + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_writel(dev, PRU1_DATARAM_OFFSET, datatowrite, + (PRU1_DATARAM_SIZE / sizeof(int))); + + pruss_load(dev, PRUSS_NUM0, (u32 *)pru_suart_emu_code, + (fw_size / sizeof(u32))); + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_load(dev, PRUSS_NUM1, (u32 *)pru_suart_emu_code, + (fw_size / sizeof(u32))); + + retval = arm_to_pru_intr_init(dev); + if (-1 == retval) + return status; + pru_set_delay_count(dev, pruss_ioaddr->pru_clk_freq); + suart_set_pru_id(dev, PRUSS_NUM0); + if (PRU1_MODE != PRU_MODE_INVALID) + suart_set_pru_id(dev, PRUSS_NUM1); + + pru_set_rx_tx_mode(dev, PRU0_MODE, PRUSS_NUM0); + if (PRU1_MODE != PRU_MODE_INVALID) + pru_set_rx_tx_mode(dev, PRU1_MODE, PRUSS_NUM1); + + pru_set_ram_data(dev, pruss_ioaddr); + pruss_run(dev, PRUSS_NUM0); + + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_run(dev, PRUSS_NUM1); + + /* Initialize uart_statu_table */ + for (idx = 0; idx < 8; idx++) + uart_statu_table[idx] = ePRU_SUART_UART_FREE; + + return status; +} + +void pru_set_fifo_timeout(struct device *dev, s16 timeout) +{ + pruss_writew(dev, PRU_SUART_PRU0_IDLE_TIMEOUT_OFFSET, + &timeout, 1); + if (PRU1_MODE != PRU_MODE_INVALID) + pruss_writew(dev, PRU_SUART_PRU1_IDLE_TIMEOUT_OFFSET, + &timeout, 1); +} + +void pru_mcasp_deinit(void) +{ + suart_mcasp_reset(&suart_iomap); +} + +/* suart Instance open routine */ +s16 pru_softuart_open(struct suart_handle *h_suart) +{ + s16 status = 0; + u16 uart_num = h_suart->uart_num - 1; + + if (uart_statu_table[h_suart->uart_num - 1] == + ePRU_SUART_UART_IN_USE) { + status = EUSERS; + return status; + } else { + h_suart->uart_type = uart_config[uart_num]; + h_suart->uart_tx_channel = uart_tx[uart_num]; + h_suart->uart_rx_channel = uart_rx[uart_num]; + h_suart->uart_status = ePRU_SUART_UART_IN_USE; + uart_statu_table[h_suart->uart_num - 1] = + ePRU_SUART_UART_IN_USE; + } + return status; +} + +/* suart instance close routine */ +s16 pru_softuart_close(struct suart_handle *h_uart) +{ + s16 status = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } else { + uart_statu_table[h_uart->uart_num - 1] = + ePRU_SUART_UART_FREE; + /* Reset the Instance to Invalid */ + h_uart->uart_num = PRU_SUART_UARTx_INVALID; + h_uart->uart_status = ePRU_SUART_UART_FREE; + } + return status; +} + +static s16 search_chnum(u16 uart_num, u16 *ch_num, u32 *pru_offset, u16 mode) +{ + *ch_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + *ch_num = (uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (uart_num <= 4) { + *pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else { + *pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + *ch_num -= 8; + } + if (mode == 2) + *ch_num = *ch_num + 1; + + } else if ((mode == 1) || (mode == 2)) { + if (mode == 1) { + if (PRU0_MODE == PRU_MODE_TX_ONLY) + *pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + else if (PRU1_MODE == PRU_MODE_TX_ONLY) + *pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if (mode == 2) { + if (PRU0_MODE == PRU_MODE_RX_ONLY) + *pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + else if (PRU1_MODE == PRU_MODE_RX_ONLY) + *pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } + } else { + return 0; + } + + return 0; +} + +/* + * suart routine for setting relative baud rate + */ +s16 pru_softuart_setbaud(struct device *dev, struct suart_handle *h_uart, + u16 tx_clk_divisor, u16 rx_clk_divisor) +{ + u32 offset; + u32 pru_offset; + s16 status = 0; + u16 ch_num; + u16 regval = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + /* Set the clock divisor value s32o the McASP */ + if ((tx_clk_divisor > 385) || (tx_clk_divisor == 0)) + return -EINVAL; + if ((rx_clk_divisor > 385) || (rx_clk_divisor == 0)) + return -EINVAL; + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + if (tx_clk_divisor != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= (~0x3FF); + regval |= tx_clk_divisor; + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return 0; + } + regval = 0; + if (rx_clk_divisor != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= (~0x3FF); + regval |= tx_clk_divisor; + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + return status; +} + +/* + * suart routine for setting number of bits per character for a specific uart + */ +s16 pru_softuart_setdatabits(struct device *dev, struct suart_handle *h_uart, + u16 tx_data_bits, u16 rx_data_bits) +{ + u32 offset; + u32 pru_offset; + s16 status = 0; + u16 ch_num; + u32 reg_val; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + /* + * NOTE: + * The supported data bits are 6,7,8,9,10,11,12 bits per character + */ + + if ((tx_data_bits < ePRU_SUART_DATA_BITS6) + || (tx_data_bits > ePRU_SUART_DATA_BITS12)) + return -EINVAL; + + if ((rx_data_bits < ePRU_SUART_DATA_BITS6) + || (rx_data_bits > ePRU_SUART_DATA_BITS12)) + return -EINVAL; + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + if (tx_data_bits != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 1); + reg_val &= ~(0xF); + reg_val |= tx_data_bits; + pruss_writeb(dev, offset, (u8 *) ®_val, 1); + } + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return 0; + } + if (rx_data_bits != 0) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 1); + reg_val &= ~(0xF); + reg_val |= rx_data_bits; + pruss_writeb(dev, offset, (u8 *) &rx_data_bits, 1); + } + + return status; +} + +/* + * suart routine to configure specific uart + */ +s16 pru_softuart_setconfig(struct device *dev, struct suart_handle *h_uart, + struct suart_config *config_uart) +{ + u32 offset; + u32 pru_offset; + s16 status = 0; + u16 ch_num; + u16 reg_val = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + /* + * NOTE: + * Dependent baud rate for the given UART,the value MUST BE LESS THAN OR + * EQUAL TO 64, preScalarValue <= 64 + */ + if ((config_uart->tx_clk_divisor > 384) + || (config_uart->rx_clk_divisor > 384)) { + return -EINVAL; + } + if ((config_uart->tx_bits_per_char < 8) + || (config_uart->tx_bits_per_char > 14)) { + return -EINVAL; + } + if ((config_uart->rx_bits_per_char < 8) + || (config_uart->rx_bits_per_char > 14)) { + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + /* Configuring the Transmit part of the given UART */ + /* Serializer has been as TX in mcasp config, by writing 1 in bits + * corresponding to tx serializer in PFUNC regsiter ie already set + * to GPIO mode PRU code will set then back to MCASP mode once TX + * request for that serializer is posted.It is required because at this + * pos32 Mcasp is accessed by both PRU and DSP have lower priority for + * Mcasp in comparison to PRU and DPS keeps on looping there only + */ + /* + * suart_mcasp_tx_serialzier_set + * (config_uart->tx_serializer, &suart_iomap); + */ + /* Configuring TX serializer */ + if (config_uart->tx_serializer != PRU_SUART_SERIALIZER_NONE) { + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val = reg_val | (config_uart->tx_serializer << + PRU_SUART_CH_CTRL_SR_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val = reg_val | (config_uart->tx_clk_divisor << + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val = reg_val | (config_uart->tx_bits_per_char << + PRU_SUART_CH_CONFIG2_BITPERCHAR_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + } + + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return 0; + } + + /* Configuring the Transmit part of the given UART */ + if (config_uart->rx_serializer != PRU_SUART_SERIALIZER_NONE) { + /* Configuring RX serializer */ + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val = reg_val | (config_uart->rx_serializer << + PRU_SUART_CH_CTRL_SR_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + + /* Configuring RX prescalar value and Oversampling */ + offset = pru_offset + + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val = reg_val | (config_uart->rx_clk_divisor << + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT) | + (config_uart->oversampling << + PRU_SUART_CH_CONFIG1_OVS_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + + /* Configuring RX bits per character value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val = reg_val | (config_uart->rx_bits_per_char << + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + } + return status; +} + +/* + * suart routine for getting the number of bytes transfered + */ +s16 pru_softuart_get_tx_data_len(struct device *dev, + struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 ch_num; + u16 read_value = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) &read_value, 2); + read_value = ((read_value & PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + return read_value; +} + +/* + * suart routine for getting the number of bytes received + */ +s16 pru_softuart_get_rx_data_len(struct device *dev, + struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 ch_num; + u16 read_value = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) &read_value, 2); + read_value = ((read_value & PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + + return read_value; +} + +/* + * suart routine to get the configuration information from a specific uart + */ +s16 pru_softuart_getconfig(struct device *dev, + struct suart_handle *h_uart, + struct suart_config *config_uart) +{ + u32 offset; + u32 pru_offset; + u16 ch_num; + u16 reg_val = 0; + s16 status = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + + /* + * NOTE: + * Dependent baud rate for the given UART,the value MUST BE LESS THAN OR + * EQUAL TO 64, preScalarValue <= 64 + */ + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + /* Configuring the Transmit part of the given UART */ + /* Configuring TX serializer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + config_uart->tx_serializer = ((reg_val & PRU_SUART_CH_CTRL_SR_MASK) >> + PRU_SUART_CH_CTRL_SR_SHIFT); + /* Configuring TX prescalar value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + config_uart->tx_clk_divisor = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) >> + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + + /* Configuring TX bits per character value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + config_uart->tx_bits_per_char = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) >> + PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + + if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + ch_num++; + } else { + return 0; + } + /* Configuring the Transmit part of the given UART */ + /* Configuring RX serializer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + config_uart->rx_serializer = ((reg_val & PRU_SUART_CH_CTRL_SR_MASK) >> + PRU_SUART_CH_CTRL_SR_SHIFT); + + /* Configuring RX prescalar value and oversampling */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + config_uart->rx_clk_divisor = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + config_uart->oversampling = ((reg_val & + PRU_SUART_CH_CONFIG1_OVS_MASK) >> + PRU_SUART_CH_CONFIG1_OVS_SHIFT); + + /* Configuring RX bits per character value */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + config_uart->rx_bits_per_char = ((reg_val & + PRU_SUART_CH_CONFIG1_DIVISOR_MASK) + >> PRU_SUART_CH_CONFIG1_DIVISOR_SHIFT); + + return status; +} + +s32 pru_softuart_pending_tx_request(struct device *dev) +{ + u32 offset = 0; + u32 ISR_value = 0; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + return 0; + } else if (PRU0_MODE == PRU_MODE_TX_ONLY) { + /* Read PRU Interrupt Status Register from PRU */ + offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + pruss_readl(dev, offset, (u32 *)&ISR_value, 1); + if ((ISR_value & 0x1) == 0x1) + return -EINVAL; + } else if (PRU1_MODE == PRU_MODE_TX_ONLY) { + /* Read PRU Interrupt Status Register from PRU */ + offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + pruss_readl(dev, offset, (u32 *)&ISR_value, 1); + if ((ISR_value & 0x2) == 0x2) + return -EINVAL; + } else { + return 0; + } + + return 0; +} + +/* + * suart data transmit routine + */ +s16 pru_softuart_write(struct device *dev, struct suart_handle *h_uart, + u32 *pt_tx_data_buf, u16 data_len) +{ + u32 offset = 0; + u32 pru_offset; + s16 status = 0; + u16 ch_num; + u16 reg_val = 0; + u16 pru_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) + pru_num = h_uart->uart_num; + else if (PRU0_MODE == PRU_MODE_TX_ONLY) + pru_num = 0; + else if (PRU1_MODE == PRU_MODE_TX_ONLY) + pru_num = 1; + else + return 0; + + /* Writing data length to SUART channel register */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val &= ~PRU_SUART_CH_CONFIG2_DATALEN_MASK; + reg_val = reg_val | (data_len << PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + + /* Writing the data pos32er to channel TX data pointer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXDATA_OFFSET; + pruss_writeb(dev, offset, (u8 *) pt_tx_data_buf, 4); + + /* Service Request to PRU */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | + PRU_SUART_CH_CTRL_SREQ_MASK); + reg_val |= (PRU_SUART_CH_CTRL_TX_MODE << + PRU_SUART_CH_CTRL_MODE_SHIFT) | (PRU_SUART_CH_CTRL_SREQ << + PRU_SUART_CH_CTRL_SREQ_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + + /* generate ARM->PRU event */ + suart_arm_to_pru_intr(dev, pru_num); + + return status; +} + +/* + * suart data receive routine + */ +s16 pru_softuart_read(struct device *dev, struct suart_handle *h_uart, + u32 *ptDataBuf, u16 data_len) +{ + u32 offset = 0; + u32 pru_offset; + s16 status = 0; + u16 ch_num; + u16 reg_val = 0; + u16 pru_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + ch_num = (h_uart->uart_num * + SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + pru_num = h_uart->uart_num; + } else if (PRU0_MODE == PRU_MODE_RX_ONLY) { + pru_num = 0; + } else if (PRU1_MODE == PRU_MODE_RX_ONLY) { + pru_num = 1; + } else { + return 0; + } + /* Writing data length to SUART channel register */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val &= ~PRU_SUART_CH_CONFIG2_DATALEN_MASK; + reg_val = reg_val | (data_len << PRU_SUART_CH_CONFIG2_DATALEN_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + + /* Writing the data pos32er to channel RX data pointer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXDATA_OFFSET; + pruss_writeb(dev, offset, (u8 *) ptDataBuf, 4); + + /* Service Request to PRU */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | + PRU_SUART_CH_CTRL_SREQ_MASK); + reg_val |= (PRU_SUART_CH_CTRL_RX_MODE << + PRU_SUART_CH_CTRL_MODE_SHIFT) | (PRU_SUART_CH_CTRL_SREQ << + PRU_SUART_CH_CTRL_SREQ_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + + /* enable the timeout s32errupt */ + suart_intr_setmask(dev, h_uart->uart_num, PRU_RX_INTR, + CHN_TXRX_IE_MASK_TIMEOUT); + + /* generate ARM->PRU event */ + suart_arm_to_pru_intr(dev, pru_num); + + return status; +} + +/* + * suart routine to read the data from the RX FIFO + */ +s16 pru_softuart_read_data(struct device *dev, struct suart_handle *h_uart, + u8 *p_data_buffer, s32 max_len, + u32 *pdata_read) +{ + s16 ret_val = 0; + u8 *psrc_addr = NULL; + u32 data_read = 0; + u32 data_len = 0; + u32 char_len = 0; + u32 offset = 0; + u32 pru_offset; + u16 ch_num; + u16 status = 0; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + /* Get the data pos32er from channel RX data pointer */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXDATA_OFFSET; + pruss_readb(dev, offset, (u8 *) &psrc_addr, 4); + + /* Reading data length from SUART channel register */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG2_OFFSET; + pruss_readb(dev, offset, (u8 *) &data_len, 2); + + /* read the character length */ + char_len = data_len & PRU_SUART_CH_CONFIG2_BITPERCHAR_MASK; + char_len -= 2; /* remove the START & STOP bit */ + + data_len &= PRU_SUART_CH_CONFIG2_DATALEN_MASK; + data_len = data_len >> PRU_SUART_CH_CONFIG2_DATALEN_SHIFT; + data_len++; + + /* if the character length is greater than 8, then the size doubles */ + if (char_len > 8) + data_len *= 2; + + /* Check if the time-out had occured. If, yes, then we need to find the + * number of bytes read from PRU. Else, we need to + * read the requested bytes + */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status, 1); + if (CHN_TXRX_STATUS_TIMEOUT == (status & CHN_TXRX_STATUS_TIMEOUT)) { + /* determine the number of bytes read s32o the FIFO */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_BYTESDONECNTR_OFFSET; + pruss_readb(dev, offset, (u8 *) &data_read, 1); + + /* if the character length is greater than 8, + then the size doubles */ + if (char_len > 8) + data_read *= 2; + +/* + * the data corresponding is loaded in second + * half during the timeout + */ + if (data_read > data_len) { + data_read -= data_len; + psrc_addr += data_len; + } + + pru_softuart_clr_rx_fifo(dev, h_uart); + } else { + data_read = data_len; +/* + * if the bit is set, the data is in the first + * half of the FIFO else the data is in the second half + */ + /* Determine the buffer index by reading FIFO_OddEven flag*/ + if (status & CHN_TXRX_STATUS_CMPLT) + psrc_addr += data_len; + } + + /* we should be copying only max len given by the application */ + if (data_read > max_len) + data_read = max_len; + +/* evaluate the virtual address of the FIFO address + * based on the physical addr + */ + psrc_addr = (u8 *)((u32) psrc_addr - + (u32) suart_iomap.p_fifo_buff_phys_base + + (u32) suart_iomap.p_fifo_buff_virt_base); + + /* Now we have both the data length and the source address. copy */ + for (offset = 0; offset < data_read; offset++) + *p_data_buffer++ = *psrc_addr++; + *pdata_read = data_read; + ret_val = 0; + + return ret_val; +} + +/* + * suart routine to disable the receive functionality. + * This routine stops the PRU from receiving on selected + * UART and also disables the McASP serializer corresponding + * to this UART Rx line. + */ +s16 pru_softuart_stop_receive(struct device *dev, struct suart_handle *h_uart) +{ + u16 ret_status = 0; + u32 offset; + u32 pru_offset; + u16 ch_num; + u16 status; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + /* read the existing value of status flag */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status, 1); + + /* we need to clear the busy bit corresponding to receive channel */ + status &= ~(CHN_TXRX_STATUS_RDY); + pruss_writeb(dev, offset, (u8 *) &status, 1); + + /* get the serizlizer number being used for this Rx channel */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) &status, 2); + status &= PRU_SUART_CH_CTRL_SR_MASK; + status = status >> PRU_SUART_CH_CTRL_SR_SHIFT; + + /* we need to de-activate the serializer corresponding to this rx */ + ret_status = suart_asp_serializer_deactivate(status, &suart_iomap); + + return ret_status; +} + +/* + * suart routine to get the tx status for a specific uart + */ +s16 pru_softuart_get_tx_status(struct device *dev, struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status, 1); + return status; +} + +s16 pru_softuart_clr_tx_status(struct device *dev, struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 1); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status, 1); + status &= ~(0x2); + pruss_writeb(dev, offset, (u8 *) &status, 1); + return status; +} + +/* + * suart routine to get the rx status for a specific uart + */ +s16 pru_softuart_get_rx_status(struct device *dev, struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status, 1); + return status; +} + +static s16 pru_softuart_clr_rx_fifo(struct device *dev, + struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num; + u16 reg_val; + u16 uart_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + uart_num = h_uart->uart_num; + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + if (PRU0_MODE == PRU_MODE_RX_ONLY) + uart_num = 0; + else if (PRU1_MODE == PRU_MODE_RX_ONLY) + uart_num = 1; + + /* Reset the number of bytes read into the FIFO */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_BYTESDONECNTR_OFFSET; + pruss_readw(dev, offset, (u16 *) ®_val, 1); + reg_val &= 0x00; + pruss_writew(dev, offset, (u16 *) ®_val, 1); + + + /* Service Request to PRU */ + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CTRL_OFFSET; + pruss_readb(dev, offset, (u8 *) ®_val, 2); + reg_val &= ~(PRU_SUART_CH_CTRL_MODE_MASK | PRU_SUART_CH_CTRL_SREQ_MASK); + reg_val |= (PRU_SUART_CH_CTRL_RX_MODE << PRU_SUART_CH_CTRL_MODE_SHIFT) | + (PRU_SUART_CH_CTRL_SREQ << PRU_SUART_CH_CTRL_SREQ_SHIFT); + pruss_writeb(dev, offset, (u8 *) ®_val, 2); + suart_intr_setmask(dev, h_uart->uart_num, PRU_RX_INTR, + CHN_TXRX_IE_MASK_TIMEOUT); + + /* generate ARM->PRU event */ + suart_arm_to_pru_intr(dev, uart_num); + + return status; +} + +s16 pru_softuart_clr_rx_status(struct device *dev, struct suart_handle *h_uart) +{ + u32 offset; + u32 pru_offset; + u16 status = 0; + u16 ch_num; + + if (h_uart == NULL) { + WARN_ON(1); + return -EINVAL; + } + search_chnum(h_uart->uart_num, &ch_num, &pru_offset, 2); + + offset = pru_offset + (ch_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_TXRXSTATUS_OFFSET; + pruss_readb(dev, offset, (u8 *) &status, 1); + status &= ~(0x3C); + pruss_writeb(dev, offset, (u8 *) &status, 1); + return status; +} + +/* + * suart_s32r_status_read: Gets the Global Interrupt status register + * for the specified SUART. + * uart_num < 1 to 6 > + * txrx_flag < Indicates TX or RX s32errupt for the uart > + */ +s16 pru_softuart_get_isrstatus(struct device *dev, u16 uart_num, u16 *txrx_flag) +{ + u32 intc_offset; + u32 ch_num = 0xFF; + u32 reg_val = 0; + u32 reg_val2 = 0; + u32 ISR_value = 0; + u32 ack_reg_val = 0; + u32 stat_inx_clr_regoffset = 0; + + /* initialize the status & Flag to known value */ + *txrx_flag = 0; + + stat_inx_clr_regoffset = (u32) (PRUSS_INTC_STATIDXCLR & 0xFFFF); + + /* Read PRU Interrupt Status Register from PRU */ + intc_offset = (u32) (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + + pruss_readl(dev, intc_offset, (u32 *)&ISR_value, 1); + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* Check if the interrupt occured for Tx */ + ch_num = uart_num * 2 - 2; + reg_val2 = PRU_SUART0_TX_EVT_BIT << ((uart_num - 1) * 2); + if (ISR_value & reg_val2) { + /* interupt occured for TX */ + *txrx_flag |= PRU_TX_INTR; + /* acknowledge the RX interrupt */ + ack_reg_val = ch_num + PRU_SUART0_TX_EVT; + pruss_writel(dev, stat_inx_clr_regoffset, + (u32 *)&ack_reg_val, 1); + } + + /* Check if the interrupt occured for Rx */ + reg_val2 = PRU_SUART0_RX_EVT_BIT << ((uart_num - 1) * 2); + pruss_readl(dev, intc_offset, (u32 *)&ISR_value, 1); + if (ISR_value & reg_val2) { + /* interupt occured for RX */ + *txrx_flag |= PRU_RX_INTR; + ch_num += 1; + + /* acknowledge the RX interrupt */ + ack_reg_val = ch_num + PRU_SUART0_TX_EVT; + pruss_writel(dev, stat_inx_clr_regoffset, + (u32 *)&ack_reg_val, 1); + } + } else { + ch_num = uart_num - 1; + if ((ISR_value & 0x03FC) != 0) { + reg_val2 = 1 << (uart_num + 1); + if (ISR_value & reg_val2) { + /* acknowledge the s32errupt */ + ack_reg_val = ch_num + PRU_SUART0_TX_EVT; + pruss_writel(dev, stat_inx_clr_regoffset, + (u32 *)&ack_reg_val, 1); + *txrx_flag |= PRU_RX_INTR; + } + } + pruss_readl(dev, intc_offset, (u32 *)&ISR_value, 1); + if (ISR_value & 0x3FC00) { + reg_val2 = 1 << (uart_num + 9); + if (ISR_value & reg_val2) { + /* acknowledge the s32errupt */ + ack_reg_val = ch_num + PRU_SUART4_TX_EVT; + pruss_writel(dev, stat_inx_clr_regoffset, + (u32 *)&ack_reg_val, 1); + *txrx_flag |= PRU_TX_INTR; + } + } + } + return reg_val; +} + +s32 pru_intr_clr_isrstatus(struct device *dev, u16 uart_num, u32 txrxmode) +{ + u32 offset; + u16 txrx_flag = 0; + u16 chn_num; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if (uart_num <= 4) { + /* PRU0 */ + offset = PRU_SUART_PRU0_ISR_OFFSET + 1; + } else { + /* PRU1 */ + offset = PRU_SUART_PRU1_ISR_OFFSET + 1; + /* First 8 channel corresponds to PRU0 */ + chn_num -= 8; + } + if (2 == txrxmode) + chn_num++; + } else if (PRU0_MODE == txrxmode) { + offset = PRU_SUART_PRU0_ISR_OFFSET + 1; + } else if (PRU1_MODE == txrxmode) { + offset = PRU_SUART_PRU1_ISR_OFFSET + 1; + } else { + return 0; + } + + pruss_readb(dev, offset, (u8 *) &txrx_flag, 1); + txrx_flag &= ~(0x2); + pruss_writeb(dev, offset, (u8 *) &txrx_flag, 1); + + return 0; +} + +s16 suart_arm_to_pru_intr(struct device *dev, u16 uart_num) +{ + u32 offset; + u32 value; + s16 retval; + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + if ((uart_num > 0) && (uart_num <= 4)) { + /* PRU0 SYS_EVT32 */ + value = 0x20; + } else if ((uart_num > 4) && (uart_num <= 8)) { + /* PRU1 SYS_EVT33 */ + value = 0x21; + } else { + return -EINVAL; + } + } + + if ((PRU0_MODE == PRU_MODE_RX_ONLY) || (PRU1_MODE == PRU_MODE_RX_ONLY) + || (PRU0_MODE == PRU_MODE_TX_ONLY) + || (PRU1_MODE == PRU_MODE_TX_ONLY)) { + if (uart_num == PRUSS_NUM0) { + /* PRU0 SYS_EVT32 */ + value = 0x20; + } + + if (uart_num == PRUSS_NUM1) { + /* PRU0 SYS_EVT33 */ + value = 0x21; + } + } + + offset = (u32) (PRUSS_INTC_STATIDXSET & 0xFFFF); + retval = pruss_writel(dev, offset, (u32 *)&value, 1); + if (retval == -1) + return -1; + return 0; +} + +static s16 arm_to_pru_intr_init(struct device *dev) +{ + u32 value; + u32 int_offset; + + /* Clear all the host interrupts */ + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; + int_offset++) + pruss_idx_writel(dev, PRUSS_INTC_HSTINTENIDXCLR, int_offset); + + /* Enable the global s32errupt */ + pruss_rmwl(dev, (u32) (PRUSS_INTC_GLBLEN & 0xFFFF), 0, 1); + + /* Enable the Host interrupts for all host channels */ + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; + int_offset++) + pruss_rmwl(dev, (u32) (PRUSS_INTC_HSTINTENIDXSET & 0xFFFF), + 0, int_offset); + pruss_rmwl(dev, (u32) (PRUSS_INTC_HOSTMAP0 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_HOSTMAP0_CHAN); + pruss_rmwl(dev, (u32) (PRUSS_INTC_HOSTMAP1 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_HOSTMAP1_CHAN); + pruss_rmwl(dev, (u32) (PRUSS_INTC_HOSTMAP2 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_HOSTMAP2_CHAN); + + /* MAP Channel 0 to SYS_EVT31 */ + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP7 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP7_SYS_EVT31); + + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* Sets the channels for the system interrupt */ + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP8 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP8_FULL); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP9_FULL); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP10 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP10_FULL); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP11 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP11_FULL); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP12 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP12_FULL); + } + if ((PRU0_MODE == PRU_MODE_RX_ONLY) || (PRU1_MODE == PRU_MODE_RX_ONLY) + || (PRU0_MODE == PRU_MODE_TX_ONLY) + || (PRU1_MODE == PRU_MODE_TX_ONLY)) { + + /* Sets the channels for the system interrupt */ + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP8 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP8_HALF); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP9 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP9_HALF); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP10 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP10_HALF); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP11 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP11_HALF); + pruss_rmwl(dev, (u32) (PRUSS_INTC_CHANMAP12 & 0xFFFF), + PRU_INTC_REGMAP_MASK, PRU_INTC_CHANMAP12_HALF); + } + + /* Clear required set of system events + * and enable them using indexed register + */ + for (int_offset = 0; int_offset < 18; int_offset++) { + value = 32 + int_offset; + pruss_idx_writel(dev, PRUSS_INTC_STATIDXCLR, value); + } + + /* enable only the HOST to PRU interrupts and let the PRU to Host events + * enabled by the separate API on demand basis. + */ + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 31); + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 32); + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 33); + pruss_idx_writel(dev, PRUSS_INTC_ENIDXSET, 50); + pruss_rmwl(dev, (u32) (PRUSS_INTC_GLBLEN & 0xFFFF), 0, 1); + + /* Enable the Host interrupts for all host channels */ + for (int_offset = 0; int_offset <= PRUSS_INTC_HOSTINTLVL_MAX; + int_offset++) + pruss_idx_writel(dev, PRUSS_INTC_HSTINTENIDXSET, int_offset); + + return 0; +} + +s32 suart_pru_to_host_intr_enable(struct device *dev, u16 uart_num, + u32 txrxmode, s32 flag) +{ + s32 ret_val = 0; + u32 offset; + u32 chn_num; + u32 value; + s16 retval = 0; + + if (uart_num > 8) + return -EINVAL; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) || + (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + chn_num = (uart_num * 2) - 2; + if (2 == txrxmode) /* Rx mode */ + chn_num++; + value = 34 + chn_num; + } else if ((PRU_MODE_RX_ONLY == txrxmode) + && (PRU0_MODE == PRU_MODE_RX_ONLY)) + value = 34 + chn_num; + else if ((PRU_MODE_RX_ONLY == txrxmode) + && (PRU1_MODE == PRU_MODE_RX_ONLY)) + value = 42 + chn_num; + else if ((PRU_MODE_TX_ONLY == txrxmode) + && (PRU0_MODE == PRU_MODE_TX_ONLY)) + value = 34 + chn_num; + else if ((PRU_MODE_TX_ONLY == txrxmode) + && (PRU1_MODE == PRU_MODE_TX_ONLY)) + value = 42 + chn_num; + else + return -1; + + if (true == flag) { + offset = (u32) (PRUSS_INTC_ENIDXSET & 0xFFFF); + retval = pruss_writel(dev, offset, (u32 *)&value, 1); + if (retval == -1) + return -1; + } else { + offset = (u32) (PRUSS_INTC_ENIDXCLR & 0xFFFF); + retval = pruss_writel(dev, offset, (u32 *)&value, 1); + if (retval == -1) + return -1; + } + return ret_val; +} + +s32 suart_intr_setmask(struct device *dev, u16 uart_num, + u32 txrxmode, u32 rmask) +{ + u32 offset; + u32 pru_offset; + u32 regval = 0; + u32 chn_num; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if ((uart_num > 0) && (uart_num <= 4)) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if ((uart_num > 4) && (uart_num <= 8)) { + offset = PRU_SUART_PRU1_IMR_OFFSET; + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + chn_num -= 8; + } else { + return -EINVAL; + } + if (2 == txrxmode) { /* rx mode */ + chn_num++; + } + } else if (PRU0_MODE == txrxmode) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if (PRU1_MODE == txrxmode) { + offset = PRU_SUART_PRU1_IMR_OFFSET; + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return 0; + } + regval = 1 << chn_num; + if (CHN_TXRX_IE_MASK_CMPLT == (rmask & CHN_TXRX_IE_MASK_CMPLT)) + pruss_rmww(dev, offset, regval, regval); + + if ((rmask & SUART_GBL_INTR_ERR_MASK) == + SUART_GBL_INTR_ERR_MASK) { + regval = SUART_GBL_INTR_ERR_MASK; + pruss_rmww(dev, offset, regval, regval); + } + + offset = pru_offset + + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + /* Framing Error Interrupt Masked */ + if ((rmask & CHN_TXRX_IE_MASK_FE) == CHN_TXRX_IE_MASK_FE) { + regval = 0; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(CHN_TXRX_IE_MASK_FE); + regval |= CHN_TXRX_IE_MASK_FE; + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + + /* Break Indicator Interrupt Masked */ + if (CHN_TXRX_IE_MASK_BI == (rmask & CHN_TXRX_IE_MASK_BI)) { + regval = 0; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(CHN_TXRX_IE_MASK_BI); + regval |= CHN_TXRX_IE_MASK_BI; + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + + /* Timeout error Interrupt Masked */ + if (CHN_TXRX_IE_MASK_TIMEOUT == + (rmask & CHN_TXRX_IE_MASK_TIMEOUT)) { + regval = 0; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(CHN_TXRX_IE_MASK_TIMEOUT); + regval |= CHN_TXRX_IE_MASK_TIMEOUT; + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + return 0; +} + +s32 suart_intr_clrmask(struct device *dev, u16 uart_num, + u32 txrxmode, u32 rmask) +{ + u32 offset; + u32 pru_offset; + u16 regval = 0; + u16 chn_num; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + if ((uart_num > 0) && (uart_num <= 4)) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if ((uart_num > 4) && (uart_num <= 8)) { + /* PRU1 */ + offset = PRU_SUART_PRU1_IMR_OFFSET; + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + /* First 8 channel corresponds to PRU0 */ + chn_num -= 8; + } else { + return -EINVAL; + } + if (2 == txrxmode) { /* rx mode */ + chn_num++; + } + } else if (PRU0_MODE == txrxmode) { + pru_offset = PRU_SUART_PRU0_CH0_OFFSET; + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if (PRU1_MODE == txrxmode) { + offset = PRU_SUART_PRU1_IMR_OFFSET; + pru_offset = PRU_SUART_PRU1_CH0_OFFSET; + } else { + return 0; + } + regval = 1 << chn_num; + if (CHN_TXRX_IE_MASK_CMPLT == (rmask & CHN_TXRX_IE_MASK_CMPLT)) + pruss_rmww(dev, offset, regval, 0); + + if ((rmask & SUART_GBL_INTR_ERR_MASK) == SUART_GBL_INTR_ERR_MASK) + pruss_rmww(dev, offset, SUART_GBL_INTR_ERR_MASK, 0); + + offset = pru_offset + + (chn_num * SUART_NUM_OF_BYTES_PER_CHANNEL) + + PRU_SUART_CH_CONFIG1_OFFSET; + + /* Framing Error Interrupt Masked */ + if ((rmask & CHN_TXRX_IE_MASK_FE) == CHN_TXRX_IE_MASK_FE) { + regval = 0; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(CHN_TXRX_IE_MASK_FE); + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + + /* Break Indicator Interrupt Masked */ + if (CHN_TXRX_IE_MASK_BI == (rmask & CHN_TXRX_IE_MASK_BI)) { + regval = 0; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(CHN_TXRX_IE_MASK_BI); + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + + /* Timeout error Interrupt Masked */ + if (CHN_TXRX_IE_MASK_TIMEOUT == + (rmask & CHN_TXRX_IE_MASK_TIMEOUT)) { + regval = 0; + pruss_readb(dev, offset, (u8 *) ®val, 2); + regval &= ~(CHN_TXRX_IE_MASK_TIMEOUT); + pruss_writeb(dev, offset, (u8 *) ®val, 2); + } + + return 0; +} + +s32 suart_intr_getmask(struct device *dev, u16 uart_num, u32 txrxmode, + u32 rmask) +{ + u16 chn_num; + u32 offset; + u16 txrx_flag; + u16 regval = 1; + + chn_num = uart_num - 1; + if ((PRU0_MODE == PRU_MODE_RX_TX_BOTH) + || (PRU1_MODE == PRU_MODE_RX_TX_BOTH)) { + /* channel starts from 0 and uart instance starts from 1 */ + chn_num = (uart_num * SUART_NUM_OF_CHANNELS_PER_SUART) - 2; + + if ((uart_num > 0) && (uart_num <= 4)) { + + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if ((uart_num > 4) && (uart_num <= 8)) { + /* PRU1 */ + offset = PRU_SUART_PRU1_IMR_OFFSET; + /* First 8 channel corresponds to PRU0 */ + chn_num -= 8; + } else { + return -EINVAL; + } + + if (2 == txrxmode) { /* rx mode */ + chn_num++; + } + } else if (PRU0_MODE == txrxmode) { + offset = PRU_SUART_PRU0_IMR_OFFSET; + } else if (PRU1_MODE == txrxmode) { + offset = PRU_SUART_PRU1_IMR_OFFSET; + } else { + return 0; + } + regval = regval << chn_num; + pruss_readb(dev, offset, (u8 *) &txrx_flag, 2); + txrx_flag &= regval; + if (0 == rmask) { + if (txrx_flag == 0) + return 1; + } + if (1 == rmask) { + if (txrx_flag == regval) + return 1; + } + return 0; +} diff --git a/drivers/tty/serial/pruss_suart_utils.c b/drivers/tty/serial/pruss_suart_utils.c new file mode 100644 index 0000000..2bd6983 --- /dev/null +++ b/drivers/tty/serial/pruss_suart_utils.c @@ -0,0 +1,391 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 +#include "pruss_suart.h" + +#define SUART_TRX_DIV_CONF_SZ 4 + +static s16 suart_mcasp_tx_baud_set(u32 tx_baud_value, + struct pruss_suart_iomap *pruss_ioaddr); +static s16 suart_mcasp_rx_baud_set(u32 rx_baud_value, u32 oversampling, + struct pruss_suart_iomap *pruss_ioaddr); + +/* + * Lookup table for TX baud rate + * The divisor value is calculated using the formula + * + * ACLKX = (AUXCLK)/(CLKXDIV * HCLKXDIV) + * + * Where + * CLKXDIV takes values from 1-32 + * HCLKXDIV takes values from 1-4096 + * Here + * AUXCLK = 24MHz + */ +u32 lt_tx_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { + /*BaudRate, Divisor, CLKXDIV,HCLKXDIV */ + {300, 80000, 24, 3200}, + {600, 40000, 15, 2500}, + {1800, 13333, 10, 1212}, + {2400, 10000, 4, 2000}, + {4800, 5000, 1, 2500}, + {7200, 3333, 0, 3333}, + {9600, 2500, 0, 2500}, + {14400, 1666, 0, 1666}, + {19200, 1250, 0, 1250}, + {38400, 625, 0, 625}, + {57600, 416, 0, 416}, + {115200, 208, 0, 208}, + {230400, 104, 0, 104} +}; + +/* + * Lookup table for RX baud rate for 8 bit oversampling + * The divisor value is calculated using the formula + * + * ACLKR = (AUXCLK)/(CLKRDIV * HCLKRDIV) * Oversampling + * + * Where + * CLKRDIV takes values from 1-32 + * HCLKRDIV takes values from 1-4096 + * Here + * AUXCLK = 24MHz + */ +u32 lt_rx_8x_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { +/* BaudRate, Divisor, CLKXDIV, HCLKXDIV */ + {300, 10000, 4, 2000}, + {600, 5000, 1, 2500}, + {1800, 1667, 0, 1667}, + {2400, 1250, 0, 1250}, + {7200, 417, 0, 417}, + {4800, 625, 0, 625}, + {9600, 312, 0, 312}, + {14400, 208, 0, 208}, + {19200, 156, 0, 156}, + {38400, 78, 0, 78}, + {57600, 52, 0, 52}, + {115200, 26, 0, 26}, + {230400, 13, 0, 13} +}; + +/* + * Lookup table for RX baud rate for 16 bit oversampling + * The divisor value is calculated using the formula + * + * ACLKR = (AUXCLK)/(CLKRDIV * HCLKRDIV) * Oversampling + * + * Where + * CLKRDIV takes values from 1-32 + * HCLKRDIV takes values from 1-4096 + * Here + * AUXCLK = 24MHz + */ +u32 lt_rx_16x_baud_rate[][SUART_TRX_DIV_CONF_SZ] = { +/*BaudRate, Divisor, CLKXDIV, HCLKXDIV */ + {300, 5000, 1, 2500}, + {600, 2500, 0, 2500}, + {1800, 833, 0, 833}, + {2400, 625, 0, 625}, + {4800, 312, 0, 312}, + {7200, 208, 0, 208}, + {9600, 156, 0, 156}, + {14400, 104, 0, 104}, + {19200, 78, 0, 78}, + {38400, 39, 0, 39}, + {57600, 26, 0, 26}, + {115200, 13, 0, 13}, + {230400, 6, 0, 6} +}; + +/* + * McASP configuration routine + */ + +void suart_mcasp_reset(struct pruss_suart_iomap *pruss_ioaddr) +{ + struct omapl_mcasp_regs_ovly *mcasp0_regs = + (struct omapl_mcasp_regs_ovly *) pruss_ioaddr->mcasp_io_addr; + /* reset mcasp. */ + __raw_writel(MCASP_SUART_GBLCTL, &mcasp0_regs->GBLCTL); + __raw_writel(MCASP_SUART_RGBLCTL, &mcasp0_regs->RGBLCTL); + __raw_writel(MCASP_SUART_XGBLCTL, &mcasp0_regs->XGBLCTL); + __raw_writel(MCASP_SUART_XSTAT, &mcasp0_regs->XSTAT); + __raw_writel(MCASP_SUART_RSTAT, &mcasp0_regs->RSTAT); +} + +void suart_mcasp_config(u32 tx_baud_value, + u32 rx_baud_value, + u32 oversampling, + struct pruss_suart_iomap *pruss_ioaddr) +{ + struct omapl_mcasp_regs_ovly *mcasp0_regs = + (struct omapl_mcasp_regs_ovly *) pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + + /* reset mcasp */ + __raw_writel(MCASP_SUART_GBLCTL, &mcasp0_regs->GBLCTL); + __raw_writel(MCASP_SUART_RGBLCTL, &mcasp0_regs->RGBLCTL); + __raw_writel(MCASP_SUART_XGBLCTL, &mcasp0_regs->XGBLCTL); + + /* configure receive registers */ + if ((SUART_8X_OVRSMPL == oversampling) || (0 == oversampling)) { + __raw_writel(MCASP_SUART_RMASK_8, &mcasp0_regs->RMASK); + __raw_writel(MCASP_SUART_RFMT_8, &mcasp0_regs->RFMT); + } + if (SUART_16X_OVRSMPL == oversampling) { + __raw_writel(MCASP_SUART_RMASK_16, &mcasp0_regs->RMASK); + __raw_writel(MCASP_SUART_RFMT_16, &mcasp0_regs->RFMT); + + } + + __raw_writel(MCASP_SUART_FSRM, &mcasp0_regs->AFSRCTL); + __raw_writel(MCASP_SUART_CLKRM_CLKRP, &mcasp0_regs->ACLKRCTL); + __raw_writel(MCASP_SUART_HCLKRP, &mcasp0_regs->AHCLKRCTL); + suart_mcasp_rx_baud_set(rx_baud_value, oversampling, pruss_ioaddr); + __raw_writel(MCASP_SUART_RTDMS0, &mcasp0_regs->RTDM); + __raw_writel(MCASP_SUART_RSYNCERR, &mcasp0_regs->RINTCTL); + __raw_writel(MCASP_SUART_RMAX_RPS_256, &mcasp0_regs->RCLKCHK); + + /* configure transmit registers. */ + __raw_writel(MCASP_SUART_XMASK_0_31, &mcasp0_regs->XMASK); + __raw_writel(MCASP_SUART_XBUSEL_XSSZ_16_XPAD_0, &mcasp0_regs->XFMT); + __raw_writel(MCASP_SUART_FSXM, &mcasp0_regs->AFSXCTL); + __raw_writel(MCASP_SUART_CLKXM_ASYNC_CLKXP, &mcasp0_regs->ACLKXCTL); + __raw_writel(MCASP_SUART_HCLKXM, &mcasp0_regs->AHCLKXCTL); + + suart_mcasp_tx_baud_set(tx_baud_value, pruss_ioaddr); + __raw_writel(MCASP_SUART_XTDMS0, &mcasp0_regs->XTDM); + __raw_writel(MCASP_SUART_XSYNCERR, &mcasp0_regs->XINTCTL); + __raw_writel(MCASP_SUART_XMAX_XPS_256, &mcasp0_regs->XCLKCHK); + + /* Serializer as a transmitter */ + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL0); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL1); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL2); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL3); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL4); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL5); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL6); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL7); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL8); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL9); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL10); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL11); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL12); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL13); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL14); + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL15); + + /* Configure all AXR[n] as McASP pins */ + + /* + * Setting all TX MCASP AXR[n] Pin mapped to Even Serializer number + * (0,2,4,6,8,10,12,14) to GPIO Mode by default. During setting the + * serializer to TX mode in PRU assembly code, the MCASP AXR[n] Pin + * would get configured to MCASP mode of operation, + * before Actual Data Transfer + */ + + /* Setting all TX Pin to GPIO Mode by default */ + temp_reg = (OMAPL_MCASP_PFUNC_RESETVAL) | + (1 << PRU_SUART0_CONFIG_TX_SER) | (1 << PRU_SUART1_CONFIG_TX_SER) | + (1 << PRU_SUART2_CONFIG_TX_SER) | (1 << PRU_SUART3_CONFIG_TX_SER) | + (1 << PRU_SUART4_CONFIG_TX_SER) | (1 << PRU_SUART5_CONFIG_TX_SER) | + (1 << PRU_SUART6_CONFIG_TX_SER) | (1 << PRU_SUART7_CONFIG_TX_SER); + __raw_writel(temp_reg, &mcasp0_regs->PFUNC); + + __raw_writel(0xFFF, &mcasp0_regs->PDOUT); + + /* config pin function and direction */ + __raw_writel(0x00000000, &mcasp0_regs->PDIR); + temp_reg = + (1 << PRU_SUART0_CONFIG_TX_SER) | (1 << PRU_SUART1_CONFIG_TX_SER) | + (1 << PRU_SUART2_CONFIG_TX_SER) | (1 << PRU_SUART3_CONFIG_TX_SER) | + (1 << PRU_SUART4_CONFIG_TX_SER) | (1 << PRU_SUART5_CONFIG_TX_SER) | + (1 << PRU_SUART6_CONFIG_TX_SER) | (1 << PRU_SUART7_CONFIG_TX_SER) | + (MCASP_PDIR_VAL); + __raw_writel(temp_reg, &mcasp0_regs->PDIR); + + __raw_writel(MCASP_SUART_DIT_DISABLE, &mcasp0_regs->DITCTL); + __raw_writel(MCASP_SUART_LOOPBACK_DISABLE, &mcasp0_regs->DLBCTL); + __raw_writel(MCASP_SUART_AMUTE_DISABLE, &mcasp0_regs->AMUTE); + + __raw_writel(MCASP_SUART_XSTAT, &mcasp0_regs->XSTAT); + __raw_writel(MCASP_SUART_RSTAT, &mcasp0_regs->RSTAT); +} + +void suart_mcasp_tx_serialzier_set(u32 serializer_num, + struct pruss_suart_iomap *pruss_ioaddr) +{ + struct omapl_mcasp_regs_ovly *mcasp0_regs = + (struct omapl_mcasp_regs_ovly *) pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + temp_reg = mcasp0_regs->PFUNC | (0x1 << serializer_num); + __raw_writel(temp_reg, &mcasp0_regs->PFUNC); +} + +/* + * mcasp TX buard rate setting routine + */ +s16 suart_mcasp_tx_baud_set(u32 tx_baud_value, + struct pruss_suart_iomap *pruss_ioaddr) +{ + u32 clk_div_val; + u32 loop_cnt; + s16 status = 0; + s16 found_val = false; + + struct omapl_mcasp_regs_ovly *mcasp0_regs = + (struct omapl_mcasp_regs_ovly *) pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + + /* Search the supported baud rate in the table */ + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (tx_baud_value == lt_tx_baud_rate[loop_cnt][0]) { + found_val = true; + break; + } + } + if (found_val == true) { + clk_div_val = lt_tx_baud_rate[loop_cnt][2]; + temp_reg = mcasp0_regs->ACLKXCTL | + clk_div_val << OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT; + __raw_writel(temp_reg, &mcasp0_regs->ACLKXCTL); + clk_div_val = lt_tx_baud_rate[loop_cnt][3]; + temp_reg = mcasp0_regs->AHCLKXCTL | + clk_div_val << OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT; + __raw_writel(temp_reg, &mcasp0_regs->AHCLKXCTL); + } else { + return -EINVAL ; + } + return status; +} + +/* + * mcasp RX buard rate setting routine + */ +s16 suart_mcasp_rx_baud_set(u32 rx_baud_value, + u32 oversampling, struct pruss_suart_iomap *pruss_ioaddr) +{ + u32 clk_div_val; + u32 loop_cnt; + s16 status = 0; + s16 found_val = false; + + struct omapl_mcasp_regs_ovly *mcasp0_regs = + (struct omapl_mcasp_regs_ovly *) pruss_ioaddr->mcasp_io_addr; + u32 temp_reg; + + if (oversampling == SUART_8X_OVRSMPL) { + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (rx_baud_value == lt_rx_8x_baud_rate[loop_cnt][0]) { + clk_div_val = lt_rx_8x_baud_rate[loop_cnt][2]; + temp_reg = mcasp0_regs->ACLKRCTL | (clk_div_val + << OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + + __raw_writel(temp_reg, &mcasp0_regs->ACLKRCTL); + + clk_div_val = + lt_rx_8x_baud_rate[loop_cnt][3] - 1; + + temp_reg = mcasp0_regs->AHCLKRCTL | (clk_div_val + << OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + + __raw_writel(temp_reg, &mcasp0_regs->AHCLKRCTL); + + found_val = true; + break; + } + } + } else if (oversampling == SUART_16X_OVRSMPL) { + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (rx_baud_value == lt_rx_16x_baud_rate[loop_cnt][0]) { + clk_div_val = lt_rx_16x_baud_rate[loop_cnt][2]; + temp_reg = + mcasp0_regs->ACLKRCTL | (clk_div_val << + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + __raw_writel(temp_reg, &mcasp0_regs->ACLKRCTL); + clk_div_val = lt_rx_16x_baud_rate[loop_cnt][3]; + temp_reg = + mcasp0_regs->AHCLKRCTL | (clk_div_val << + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + __raw_writel(temp_reg, &mcasp0_regs->AHCLKRCTL); + found_val = true; + break; + } + } + } else if (oversampling == 0) { + for (loop_cnt = 0; loop_cnt < SUART_NUM_OF_BAUDS_SUPPORTED; + loop_cnt++) { + if (rx_baud_value == lt_tx_baud_rate[loop_cnt][0]) { + clk_div_val = lt_tx_baud_rate[loop_cnt][2]; + temp_reg = + mcasp0_regs->ACLKRCTL | (clk_div_val << + OMAPL_MCASP_ACLKXCTL_CLKXDIV_SHIFT); + __raw_writel(temp_reg, &mcasp0_regs->ACLKRCTL); + clk_div_val = lt_tx_baud_rate[loop_cnt][3]; + temp_reg = + mcasp0_regs->AHCLKRCTL | (clk_div_val << + OMAPL_MCASP_AHCLKXCTL_HCLKXDIV_SHIFT); + __raw_writel(temp_reg, &mcasp0_regs->AHCLKRCTL); + found_val = true; + break; + } + } + } else { + return -EINVAL ; + } + + if (found_val != true) + return -EINVAL ; + + return status; +} + +/* + * mcasp buard rate setting routine + */ +s16 suart_asp_baud_set(u32 tx_baud_value, u32 rx_baud_value, u32 oversampling, + struct pruss_suart_iomap *pruss_ioaddr) +{ + s16 status = 0; + + status = suart_mcasp_tx_baud_set(tx_baud_value, pruss_ioaddr); + status = suart_mcasp_rx_baud_set(rx_baud_value, oversampling, + pruss_ioaddr); + + return status; +} + +/* + * mcasp deactivate the selected serializer + */ +s16 suart_asp_serializer_deactivate(u16 sr_num, + struct pruss_suart_iomap *pruss_ioaddr) +{ + s16 status = 0; + struct omapl_mcasp_regs_ovly *mcasp0_regs = + (struct omapl_mcasp_regs_ovly *)pruss_ioaddr->mcasp_io_addr; + if (sr_num > 15) + status = -EINVAL; + else + __raw_writel(MCASP_SUART_SRCTL_DISMOD, &mcasp0_regs->SRCTL0); + + return status; +} diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index 758c5b0..eae37fe 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -202,6 +202,8 @@ /* VIA VT8500 SoC */ #define PORT_VT8500 97 +#define PORT_DA8XX_PRU_SUART 98 + #ifdef __KERNEL__ #include -- 1.7.2.3 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From subhasish at mistralsolutions.com Fri Jul 8 14:06:03 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 19:06:03 -0000 Subject: [PATCH v2 01/13] mfd: pruss mfd driver. In-Reply-To: <1297435892-28278-1-git-send-email-subhasish@mistralsolutions.com> References: <1297435892-28278-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1297435892-28278-2-git-send-email-subhasish@mistralsolutions.com> This patch adds the pruss MFD driver and associated include files. Signed-off-by: Subhasish Ghosh --- drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 1 + drivers/mfd/da8xx_pru.c | 446 +++++++++++++++++++++++++++++++ include/linux/mfd/pruss/da8xx_pru.h | 122 +++++++++ include/linux/mfd/pruss/da8xx_prucore.h | 74 +++++ 5 files changed, 653 insertions(+), 0 deletions(-) create mode 100644 drivers/mfd/da8xx_pru.c create mode 100644 include/linux/mfd/pruss/da8xx_pru.h create mode 100644 include/linux/mfd/pruss/da8xx_prucore.h 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..f7868a4 --- /dev/null +++ b/drivers/mfd/da8xx_pru.c @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2010 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct da8xx_pruss { + struct device *dev; + 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); + +u32 pruss_disable(struct device *dev, u8 pruss_num) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + da8xx_prusscore_regs h_pruss; + u32 temp_reg; + + if (pruss_num == DA8XX_PRUCORE_0) { + /* Disable PRU0 */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7000); + + 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); + + 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 */ + __raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL, + &h_pruss->CONTROL); + } else if (pruss_num == DA8XX_PRUCORE_1) { + /* Disable PRU1 */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7800); + 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); + + 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 */ + __raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL, &h_pruss->CONTROL); + } else + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(pruss_disable); + +u32 pruss_enable(struct device *dev, u8 pruss_num) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + da8xx_prusscore_regs h_pruss; + + if (pruss_num == DA8XX_PRUCORE_0) { + /* Reset PRU0 */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7000); + __raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL, + &h_pruss->CONTROL); + } else if (pruss_num == DA8XX_PRUCORE_1) { + /* Reset PRU1 */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7800); + __raw_writel(DA8XX_PRUCORE_CONTROL_RESETVAL, + &h_pruss->CONTROL); + } else + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(pruss_enable); + +/* Load the specified PRU with code */ +u32 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); + u32 *pruss_iram; + u32 i; + + if (pruss_num == DA8XX_PRUCORE_0) { + pruss_iram = (u32 *) ((u32) pruss->ioaddr + 0x8000); + } else if (pruss_num == DA8XX_PRUCORE_1) { + pruss_iram = (u32 *) ((u32) pruss->ioaddr + 0xc000); + } else + return -EINVAL; + + pruss_enable(dev, pruss_num); + + /* Copy dMAX code to its instruction RAM */ + for (i = 0; i < code_size_in_words; i++) { + __raw_writel(pruss_code[i], (pruss_iram + i)); + } + return 0; +} +EXPORT_SYMBOL(pruss_load); + +u32 pruss_run(struct device *dev, u8 pruss_num) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + da8xx_prusscore_regs h_pruss; + + u32 temp_reg; + + if (pruss_num == DA8XX_PRUCORE_0) { + /* DA8XX_PRUCORE_0_REGS; */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7000); + } else if (pruss_num == DA8XX_PRUCORE_1) { + /* DA8XX_PRUCORE_1_REGS; */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7800); + } else + return -EINVAL; + + /* Enable dMAX, let it execute the code we just copied */ + 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); + return 0; +} +EXPORT_SYMBOL(pruss_run); + +u32 pruss_wait_for_halt(struct device *dev, u8 pruss_num, u32 timeout) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + da8xx_prusscore_regs h_pruss; + u32 temp_reg; + u32 cnt = timeout; + + if (pruss_num == DA8XX_PRUCORE_0) { + /* DA8XX_PRUCORE_0_REGS; */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7000); + } else if (pruss_num == DA8XX_PRUCORE_1) { + /* DA8XX_PRUCORE_1_REGS; */ + h_pruss = (da8xx_prusscore_regs) + ((u32) pruss->ioaddr + 0x7800); + } 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); + +s16 pruss_writeb(struct device *dev, u32 u32offset, + u8 *pu8datatowrite, u16 u16bytestowrite) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + u8 *pu8addresstowrite; + u16 u16loop; + u32offset = (u32)pruss->ioaddr + u32offset; + pu8addresstowrite = (u8 *) (u32offset); + + for (u16loop = 0; u16loop < u16bytestowrite; u16loop++) + __raw_writeb(*pu8datatowrite++, pu8addresstowrite++); + return 0; +} +EXPORT_SYMBOL(pruss_writeb); + +s16 pruss_readb(struct device *dev, u32 u32offset, + u8 *pu8datatoread, u16 u16bytestoread) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + u8 *pu8addresstoread; + u16 u16loop; + u32offset = (u32)pruss->ioaddr + u32offset; + pu8addresstoread = (u8 *) (u32offset); + + for (u16loop = 0; u16loop < u16bytestoread; u16loop++) + *pu8datatoread++ = __raw_readb(pu8addresstoread++); + return 0; +} +EXPORT_SYMBOL(pruss_readb); + +s16 pruss_writel(struct device *dev, u32 u32offset, + u32 *pu32datatowrite, s16 u16wordstowrite) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + u32 *pu32addresstowrite; + s16 u16loop; + + /* TODO: Get all the driver API's fixed */ + u32offset = (u32)pruss->ioaddr + u32offset; + pu32addresstowrite = (u32 *)(u32offset); + + for (u16loop = 0; u16loop < u16wordstowrite; u16loop++) + __raw_writel(*pu32datatowrite++, pu32addresstowrite++); + return 0; +} +EXPORT_SYMBOL(pruss_writel); + +s16 pruss_readl(struct device *dev, u32 u32offset, + u32 *pu32datatoread, s16 u16wordstoread) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + u32 *pu32addresstoread; + s16 u16loop; + + /* TODO: Get all the driver API's fixed */ + u32offset = (u32)pruss->ioaddr + u32offset; + pu32addresstoread = (u32 *)(u32offset); + + for (u16loop = 0; u16loop < u16wordstoread; u16loop++) + *pu32datatoread++ = __raw_readl(pu32addresstoread++); + return 0; +} +EXPORT_SYMBOL(pruss_readl); + +s16 pruss_writew(struct device *dev, u32 u32offset, + u16 *pu16datatowrite, s16 u16wordstowrite) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + u32 *pu32addresstowrite; + s16 u16loop; + + /* TODO: Get all the driver API's fixed */ + u32offset = (u32)pruss->ioaddr + u32offset; + pu32addresstowrite = (u32 *)(u32offset); + + for (u16loop = 0; u16loop < u16wordstowrite; u16loop++) { + __raw_writew(*(pu16datatowrite++), (pu32addresstowrite++)); + } + return 0; +} +EXPORT_SYMBOL(pruss_writew); + +s16 pruss_readw(struct device *dev, u32 u32offset, + u16 *pu16datatoread, s16 u16wordstoread) +{ + struct da8xx_pruss *pruss = dev_get_drvdata(dev->parent); + u32 *pu32addresstoread; + s16 u16loop; + + /* TODO: Get all the driver API's fixed */ + u32offset = (u32)pruss->ioaddr + u32offset; + pu32addresstoread = (u32 *)(u32offset); + + for (u16loop = 0; u16loop < u16wordstoread; u16loop++) + *pu16datatoread++ = __raw_readw(pu32addresstoread++); + return 0; +} +EXPORT_SYMBOL(pruss_readw); + +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; + + err = mfd_add_devices(dev, 0, &cell, 1, NULL, 0); + if (err) { + dev_err(dev, "cannot add mfd cells\n"); + return err; + } + } + 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; + } + + 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); + 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 = "da8xx_pruss", + .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/pruss/da8xx_pru.h b/include/linux/mfd/pruss/da8xx_pru.h new file mode 100644 index 0000000..68d8421 --- /dev/null +++ b/include/linux/mfd/pruss/da8xx_pru.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 +#include +#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_CHAN_123_HOST (0x03020100) +#define PRU_INTC_CHAN_4567_HOST (0x07060504) +#define PRU_INTC_CHAN_89_HOST (0x00000908) + +#define PRU_INTC_CHAN_0_SYSEVT_31 (0x00000000) +#define PRU_INTC_CHAN_12_SYSEVT (0x02020100) +#define PRU_INTC_CHAN_34_SYSEVT_36_39 (0x04040303) +#define PRU_INTC_CHAN_56_SYSEVT_40_43 (0x06060505) +#define PRU_INTC_CHAN_78_SYSEVT_44_47 (0x08080707) +#define PRU_INTC_CHAN_9_SYSEVT_48_49 (0x00010909) +#define PRU_INTC_CHAN_0123_SYSEVT_32_35 (0x03020100) +#define PRU_INTC_CHAN_4567_SYSEVT_36_39 (0x07060504) +#define PRU_INTC_CHAN_8923_SYSEVT_40_43 (0x03020908) +#define PRU_INTC_CHAN_4567_SYSEVT_44_47 (0x07060504) + +struct da8xx_pruss_devices { + const char *dev_name; + void *pdata; + size_t pdata_size; + int (*setup)(void); +}; + +u32 pruss_get_clk_freq(struct device *dev); + +u32 pruss_enable(struct device *dev, u8 pruss_num); + +u32 pruss_load(struct device *dev, u8 pruss_num, u32 *pruss_code, + u32 code_size_in_words); + +u32 pruss_run(struct device *dev, u8 pruss_num); + +u32 pruss_wait_for_halt(struct device *dev, u8 pruss_num, u32 timeout); + +u32 pruss_disable(struct device *dev, u8 pruss_num); + +s16 pruss_writeb(struct device *dev, u32 u32offset, + u8 *pu8datatowrite, u16 u16wordstowrite); + +s16 pruss_readb(struct device *dev, u32 u32offset, + u8 *pu8datatoread, u16 u16wordstoread); + +s16 pruss_readl(struct device *dev, u32 u32offset, + u32 *pu32datatoread, s16 s16wordstoread); + +s16 pruss_writel(struct device *dev, u32 u32offset, + u32 *pu32datatoread, s16 s16wordstoread); + +s16 pruss_writew(struct device *dev, u32 u32offset, + u16 *u16datatowrite, s16 u16wordstowrite); + +s16 pruss_readw(struct device *dev, u32 u32offset, + u16 *pu32datatoread, s16 u16wordstoread); +#endif /* End _PRUSS_H_ */ diff --git a/include/linux/mfd/pruss/da8xx_prucore.h b/include/linux/mfd/pruss/da8xx_prucore.h new file mode 100644 index 0000000..81f2ff9 --- /dev/null +++ b/include/linux/mfd/pruss/da8xx_prucore.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 + +#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) + +typedef struct { + 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]; +} *da8xx_prusscore_regs; + +#endif -- 1.7.2.3 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From subhasish at mistralsolutions.com Fri Jul 8 14:07:08 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 19:07:08 -0000 Subject: [PATCH v1 4/9] can: pruss CAN driver. In-Reply-To: <1296571667-12049-1-git-send-email-subhasish@mistralsolutions.com> References: <1296571667-12049-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1296571667-12049-5-git-send-email-subhasish@mistralsolutions.com> This patch adds support for the CAN device emulated on PRUSS. Signed-off-by: Subhasish Ghosh --- drivers/net/can/Kconfig | 1 + drivers/net/can/Makefile | 1 + drivers/net/can/da8xx_pruss/Kconfig | 73 ++ drivers/net/can/da8xx_pruss/Makefile | 7 + drivers/net/can/da8xx_pruss/pruss_can.c | 759 +++++++++++++++++ drivers/net/can/da8xx_pruss/pruss_can_api.c | 1224 +++++++++++++++++++++++++++ drivers/net/can/da8xx_pruss/pruss_can_api.h | 290 +++++++ 7 files changed, 2355 insertions(+), 0 deletions(-) create mode 100644 drivers/net/can/da8xx_pruss/Kconfig create mode 100644 drivers/net/can/da8xx_pruss/Makefile create mode 100644 drivers/net/can/da8xx_pruss/pruss_can.c create mode 100644 drivers/net/can/da8xx_pruss/pruss_can_api.c create mode 100644 drivers/net/can/da8xx_pruss/pruss_can_api.h diff --git a/drivers/net/can/Kconfig b/drivers/net/can/Kconfig index d5a9db6..ae8f0f9 100644 --- a/drivers/net/can/Kconfig +++ b/drivers/net/can/Kconfig @@ -112,6 +112,7 @@ config PCH_CAN This driver can access CAN bus. source "drivers/net/can/mscan/Kconfig" +source "drivers/net/can/da8xx_pruss/Kconfig" source "drivers/net/can/sja1000/Kconfig" diff --git a/drivers/net/can/Makefile b/drivers/net/can/Makefile index 07ca159..849cdbf 100644 --- a/drivers/net/can/Makefile +++ b/drivers/net/can/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_CAN_SJA1000) += sja1000/ obj-$(CONFIG_CAN_MSCAN) += mscan/ obj-$(CONFIG_CAN_AT91) += at91_can.o obj-$(CONFIG_CAN_TI_HECC) += ti_hecc.o +obj-$(CONFIG_CAN_TI_DA8XX_PRU) += da8xx_pruss/ obj-$(CONFIG_CAN_MCP251X) += mcp251x.o obj-$(CONFIG_CAN_BFIN) += bfin_can.o obj-$(CONFIG_CAN_JANZ_ICAN3) += janz-ican3.o diff --git a/drivers/net/can/da8xx_pruss/Kconfig b/drivers/net/can/da8xx_pruss/Kconfig new file mode 100644 index 0000000..8b68f68 --- /dev/null +++ b/drivers/net/can/da8xx_pruss/Kconfig @@ -0,0 +1,73 @@ +# +# CAN Lite Kernel Configuration +# +config CAN_TI_DA8XX_PRU + depends on CAN_DEV && ARCH_DAVINCI && ARCH_DAVINCI_DA850 + tristate "PRU based CAN emulation for DA8XX" + ---help--- + Enable this to emulate a CAN controller on the PRU of DA8XX. + If not sure, mark N + +config DA8XX_PRU_CANID_MBX0 + hex "CANID for mailbox 0" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 0 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX1 + hex "CANID for mailbox 1" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 1 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX2 + hex "CANID for mailbox 2" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 2 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX3 + hex "CANID for mailbox 3" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 3 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX4 + hex "CANID for mailbox 4" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 4 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX5 + hex "CANID for mailbox 5" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 5 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX6 + hex "CANID for mailbox 6" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 6 + Default value is set to 0x123, change this as required. + +config DA8XX_PRU_CANID_MBX7 + hex "CANID for mailbox 7" + depends on CAN_TI_DA8XX_PRU + default "0x123" + ---help--- + Enter the CANID for mailbox 7 + Default value is set to 0x123, change this as required. diff --git a/drivers/net/can/da8xx_pruss/Makefile b/drivers/net/can/da8xx_pruss/Makefile new file mode 100644 index 0000000..48f3055 --- /dev/null +++ b/drivers/net/can/da8xx_pruss/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for CAN Lite emulation +# +can_emu-objs := pruss_can.o \ + pruss_can_api.o + +obj-$(CONFIG_CAN_TI_DA8XX_PRU) += can_emu.o diff --git a/drivers/net/can/da8xx_pruss/pruss_can.c b/drivers/net/can/da8xx_pruss/pruss_can.c new file mode 100644 index 0000000..44f60fa --- /dev/null +++ b/drivers/net/can/da8xx_pruss/pruss_can.c @@ -0,0 +1,759 @@ +/* + * TI DA8XX PRU CAN Emulation device driver + * Author: subhasish at mistralsolutions.com + * + * This driver supports TI's PRU CAN Emulation and the + * specs for the same is available at + * + * Copyright (C) 2009 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 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include "pruss_can_api.h" + +#define DRV_NAME "da8xx_pruss_can" +#define DRV_DESC "TI PRU CAN Controller Driver v0.1" +#define PRU_CAN_START 1 +#define PRU_CAN_STOP 0 +#define MB_MIN 0 +#define MB_MAX 7 + +#define PRU_CANMID_IDE BIT(29) /* Extended frame format */ + +#define PRU_CAN_ISR_BIT_CCI BIT(15) +#define PRU_CAN_ISR_BIT_ESI BIT(14) +#define PRU_CAN_ISR_BIT_SRDI BIT(13) +#define PRU_CAN_ISR_BIT_RRI BIT(8) + +#define PRU_CAN_MBXSR_BIT_STATE BIT(7) +#define PRU_CAN_MBXSR_BIT_TC BIT(6) +#define PRU_CAN_MBXSR_BIT_ERR BIT(5) +#define PRU_CAN_MBXSR_BIT_OF BIT(0) + +#define PRU_CAN_GSR_BIT_TXM BIT(7) +#define PRU_CAN_GSR_BIT_RXM BIT(6) +#define PRU_CAN_GSR_BIT_CM BIT(5) +#define PRU_CAN_GSR_BIT_EPM BIT(4) +#define PRU_CAN_GSR_BIT_BFM BIT(3) +#define RTR_MBX_NO 8 +#define MAX_INIT_RETRIES 20 +#define L138_PRU_ARM_FREQ 312000 +#define DFLT_PRU_FREQ 156000000 +#define DFLT_PRU_BITRATE 125000 + +#define CONFIG_DA8XX_PRU_CANID_MBX0 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX1 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX2 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX3 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX4 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX5 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX6 0x123 +#define CONFIG_DA8XX_PRU_CANID_MBX7 0x123 + +#ifdef __CAN_DEBUG +#define __can_debug(fmt, args...) printk(KERN_DEBUG "can_debug: " fmt, ## args) +#else +#define __can_debug(fmt, args...) +#endif +#define __can_err(fmt, args...) printk(KERN_ERR "can_err: " fmt, ## args) + +/* + * omapl_pru can private data + */ +struct omapl_pru_can_priv { + struct can_priv can; + struct workqueue_struct *pru_can_wQ; + struct work_struct rx_work; + struct net_device *ndev; + struct device *dev; /* pdev->dev */ + struct clk *clk_timer; + u32 timer_freq; + can_emu_app_hndl can_tx_hndl; + can_emu_app_hndl can_rx_hndl; + const struct firmware *fw_rx; + const struct firmware *fw_tx; + spinlock_t mbox_lock; + u32 trx_irq; + u32 tx_head; + u32 tx_tail; + u32 tx_next; + u32 rx_next; +}; + +static int omapl_pru_can_get_state(const struct net_device *ndev, + enum can_state *state) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + *state = priv->can.state; + return 0; +} + +static int omapl_pru_can_set_bittiming(struct net_device *ndev) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + struct can_bittiming *bt = &priv->can.bittiming; + long bit_error = 0; + + if (priv->can.ctrlmode & CAN_CTRLMODE_3_SAMPLES) { + dev_warn(priv->dev, "WARN: Triple" + "sampling not set due to h/w limitations"); + } + if (pru_can_calc_timing(priv->dev, priv->can.clock.freq, bt->bitrate) != 0) + return -EINVAL; + bit_error = + (((priv->timer_freq / (priv->timer_freq / bt->bitrate)) - + bt->bitrate) * 1000) / bt->bitrate; + if (bit_error) { + bit_error = + (((priv->timer_freq / (priv->timer_freq / bt->bitrate)) - + bt->bitrate) * 1000000) / bt->bitrate; + printk(KERN_INFO "\nBitrate error %ld.%ld%%\n", + bit_error / 10000, bit_error % 1000); + } else + printk(KERN_INFO "\nBitrate error 0.0%%\n"); + + return 0; +} + +static void omapl_pru_can_stop(struct net_device *ndev) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + u16 int_mask = 0; + + pru_can_mask_ints(priv->dev, int_mask); /* mask all ints */ + pru_can_start_abort_tx(priv->dev, PRU_CAN_STOP); + priv->can.state = CAN_STATE_STOPPED; +} + +/* + * This is to just set the can state to ERROR_ACTIVE + * ip link set canX up type can bitrate 125000 + */ +static void omapl_pru_can_start(struct net_device *ndev) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + u16 int_mask = 0xFFFF; + + if (priv->can.state != CAN_STATE_STOPPED) + omapl_pru_can_stop(ndev); + + pru_can_mask_ints(priv->dev, int_mask); /* unmask all ints */ + + pru_can_get_global_status(priv->dev, &priv->can_tx_hndl); + pru_can_get_global_status(priv->dev, &priv->can_rx_hndl); + + if (PRU_CAN_GSR_BIT_EPM & priv->can_tx_hndl.u32globalstatus) + priv->can.state = CAN_STATE_ERROR_PASSIVE; + else if (PRU_CAN_GSR_BIT_BFM & priv->can_tx_hndl.u32globalstatus) + priv->can.state = CAN_STATE_BUS_OFF; + else + priv->can.state = CAN_STATE_ERROR_ACTIVE; +} + +static int omapl_pru_can_set_mode(struct net_device *ndev, enum can_mode mode) +{ + int ret = 0; + + switch (mode) { + case CAN_MODE_START: + omapl_pru_can_start(ndev); + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + break; + case CAN_MODE_STOP: + omapl_pru_can_stop(ndev); + if (!netif_queue_stopped(ndev)) + netif_stop_queue(ndev); + break; + default: + ret = -EOPNOTSUPP; + break; + } + return ret; +} + +static netdev_tx_t omapl_pru_can_start_xmit(struct sk_buff *skb, + struct net_device *ndev) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + struct can_frame *cf = (struct can_frame *)skb->data; + int count; + u8 *data = cf->data; + u8 dlc = cf->can_dlc; + u8 *ptr8data = NULL; + + netif_stop_queue(ndev); + if (cf->can_id & CAN_EFF_FLAG) /* Extended frame format */ + *((u32 *) &priv->can_tx_hndl.strcanmailbox) = + (cf->can_id & CAN_EFF_MASK) | PRU_CANMID_IDE; + else /* Standard frame format */ + *((u32 *) &priv->can_tx_hndl.strcanmailbox) = + (cf->can_id & CAN_SFF_MASK) << 18; + + if (cf->can_id & CAN_RTR_FLAG) /* Remote transmission request */ + *((u32 *) &priv->can_tx_hndl.strcanmailbox) |= CAN_RTR_FLAG; + + ptr8data = &priv->can_tx_hndl.strcanmailbox.u8data7 + (dlc - 1); + for (count = 0; count < (u8) dlc; count++) { + *ptr8data-- = *data++; + } + *((u32 *) &priv->can_tx_hndl.strcanmailbox.u16datalength) = (u32) dlc; +/* + * search for the next available mbx + * if the next mbx is busy, then try the next + 1 + * do this until the head is reached. + * if still unable to tx, stop accepting any packets + * if able to tx and the head is reached, then reset next to tail, i.e mbx0 + * if head is not reached, then just point to the next mbx + */ + for (; priv->tx_next <= priv->tx_head; priv->tx_next++) { + priv->can_tx_hndl.ecanmailboxnumber = + (can_mailbox_number) priv->tx_next; + if (-1 == pru_can_write_data_to_mailbox(priv->dev, + &priv->can_tx_hndl)) { + if (priv->tx_next == priv->tx_head) { + priv->tx_next = priv->tx_tail; + if (!netif_queue_stopped(ndev)) + netif_stop_queue(ndev); /* IF stalled */ + dev_err(priv->dev, + "%s: no tx mbx available", __func__); + return NETDEV_TX_BUSY; + } else + continue; + } else { + /* set transmit request */ + pru_can_tx(priv->dev, priv->tx_next, CAN_TX_PRU_1); + pru_can_tx_mode_set(priv->dev, false, ecanreceive); + pru_can_tx_mode_set(priv->dev, true, ecantransmit); + pru_can_start_abort_tx(priv->dev, PRU_CAN_START); + priv->tx_next++; + can_put_echo_skb(skb, ndev, 0); + break; + } + } + if (priv->tx_next > priv->tx_head) { + priv->tx_next = priv->tx_tail; + } + return NETDEV_TX_OK; +} + +static int omapl_pru_can_rx(struct net_device *ndev, u32 mbxno) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + u32 pru_can_mbx_data; + u8 *data = NULL; + u8 *ptr8data = NULL; + int count = 0; + + skb = alloc_can_skb(ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + dev_err(priv->dev, + "alloc_can_skb() failed\n"); + return -ENOMEM; + } + data = cf->data; + /* get payload */ + priv->can_rx_hndl.ecanmailboxnumber = (can_mailbox_number) mbxno; + if (pru_can_get_data_from_mailbox(priv->dev, &priv->can_rx_hndl)) { + __can_err("failed to get data from mailbox\n"); + return -EAGAIN; + } + /* give ownweship to pru */ + pru_can_tx(priv->dev, mbxno, CAN_RX_PRU_0); + + /* get data length code */ + cf->can_dlc = + get_can_dlc(* + ((u32 *) &priv->can_rx_hndl.strcanmailbox. + u16datalength) & 0xF); + if (cf->can_dlc <= 4) { + ptr8data = + &priv->can_rx_hndl.strcanmailbox.u8data3 + (4 - + cf->can_dlc); + for (count = 0; count < cf->can_dlc; count++) { + *data++ = *ptr8data++; + } + } else { + ptr8data = &priv->can_rx_hndl.strcanmailbox.u8data3; + for (count = 0; count < 4; count++) { + *data++ = *ptr8data++; + } + ptr8data = + &priv->can_rx_hndl.strcanmailbox.u8data4 - (cf->can_dlc - + 5); + for (count = 0; count < cf->can_dlc - 4; count++) { + *data++ = *ptr8data++; + } + } + + pru_can_mbx_data = *((u32 *) &priv->can_rx_hndl.strcanmailbox); + /* get id extended or std */ + if (pru_can_mbx_data & PRU_CANMID_IDE) + cf->can_id = (pru_can_mbx_data & CAN_EFF_MASK) | CAN_EFF_FLAG; + else + cf->can_id = (pru_can_mbx_data >> 18) & CAN_SFF_MASK; + + if (pru_can_mbx_data & CAN_RTR_FLAG) + cf->can_id |= CAN_RTR_FLAG; + + netif_rx_ni(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + return 0; +} + +static int omapl_pru_can_err(struct net_device *ndev, int int_status, + int err_status) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + struct can_frame *cf; + struct sk_buff *skb; + int tx_err_cnt, rx_err_cnt; + + /* propogate the error condition to the can stack */ + skb = alloc_can_err_skb(ndev, &cf); + if (!skb) { + if (printk_ratelimit()) + dev_err(priv->dev, + "alloc_can_err_skb() failed\n"); + return -ENOMEM; + } + + if (err_status & PRU_CAN_GSR_BIT_EPM) { /* error passive int */ + priv->can.state = CAN_STATE_ERROR_PASSIVE; + ++priv->can.can_stats.error_passive; + cf->can_id |= CAN_ERR_CRTL; + tx_err_cnt = pru_can_get_error_cnt(priv->dev, CAN_TX_PRU_1); + rx_err_cnt = pru_can_get_error_cnt(priv->dev, CAN_RX_PRU_0); + if (tx_err_cnt > 127) + cf->data[1] |= CAN_ERR_CRTL_TX_PASSIVE; + if (rx_err_cnt > 127) + cf->data[1] |= CAN_ERR_CRTL_RX_PASSIVE; + + dev_dbg(priv->ndev->dev.parent, "Error passive interrupt\n"); + } + + if (err_status & PRU_CAN_GSR_BIT_BFM) { + priv->can.state = CAN_STATE_BUS_OFF; + cf->can_id |= CAN_ERR_BUSOFF; + /* + * Disable all interrupts in bus-off to avoid int hog + * this should be handled by the pru + */ + pru_can_mask_ints(priv->dev, 0xFFFF); + can_bus_off(ndev); + dev_dbg(priv->ndev->dev.parent, "Bus off mode\n"); + } + + netif_rx(skb); + stats->rx_packets++; + stats->rx_bytes += cf->can_dlc; + return 0; +} + +void omapl_pru_can_rx_wQ(struct work_struct *work) +{ + struct omapl_pru_can_priv *priv = container_of(work, + struct + omapl_pru_can_priv, + rx_work); + struct net_device *ndev = priv->ndev; + u32 bit_set, mbxno = 0; + + if (-1 == pru_can_get_intr_status(priv->dev, &priv->can_rx_hndl)) + return; + + if (PRU_CAN_ISR_BIT_RRI & priv->can_rx_hndl.u32interruptstatus) { + mbxno = RTR_MBX_NO; + omapl_pru_can_rx(ndev, mbxno); + } else { + /* Extract the mboxno from the status */ + for (bit_set = 0; ((priv->can_rx_hndl.u32interruptstatus & 0xFF) + >> bit_set != 0); bit_set++) + ; + if (0 == bit_set) { + dev_err(priv->dev, + "%s: invalid mailbox number: %X\n", __func__, + priv->can_rx_hndl.u32interruptstatus); + } else { + mbxno = bit_set - 1; /* mail box numbering starts from 0 */ + if (PRU_CAN_ISR_BIT_ESI & priv->can_rx_hndl. + u32interruptstatus) { + pru_can_get_global_status(priv->dev, &priv->can_rx_hndl); + omapl_pru_can_err(ndev, + priv->can_rx_hndl. + u32interruptstatus, + priv->can_rx_hndl. + u32globalstatus); + } else { + omapl_pru_can_rx(ndev, mbxno); + } + } + } +} + +irqreturn_t omapl_tx_can_intr(int irq, void *dev_id) +{ + struct net_device *ndev = dev_id; + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + struct net_device_stats *stats = &ndev->stats; + u32 bit_set, mbxno; + + pru_can_get_intr_status(priv->dev, &priv->can_tx_hndl); + if ((PRU_CAN_ISR_BIT_CCI & priv->can_tx_hndl.u32interruptstatus) + || (PRU_CAN_ISR_BIT_SRDI & priv->can_tx_hndl.u32interruptstatus)) { + __can_debug("tx_int_status = 0x%X\n", + priv->can_tx_hndl.u32interruptstatus); + can_free_echo_skb(ndev, 0); + } else { + for (bit_set = 0; ((priv->can_tx_hndl.u32interruptstatus & 0xFF) + >> bit_set != 0); bit_set++) + ; + if (0 == bit_set) { + __can_err("%s: invalid mailbox number\n", __func__); + can_free_echo_skb(ndev, 0); + } else { + mbxno = bit_set - 1; /* mail box numbering starts from 0 */ + if (PRU_CAN_ISR_BIT_ESI & priv->can_tx_hndl. + u32interruptstatus) { + /* read gsr and ack pru */ + pru_can_get_global_status(priv->dev, &priv->can_tx_hndl); + omapl_pru_can_err(ndev, + priv->can_tx_hndl. + u32interruptstatus, + priv->can_tx_hndl. + u32globalstatus); + } else { + stats->tx_packets++; + /* stats->tx_bytes += dlc; */ + /*can_get_echo_skb(ndev, 0);*/ + } + } + } + if (netif_queue_stopped(ndev)) + netif_wake_queue(ndev); + + can_get_echo_skb(ndev, 0); + pru_can_tx_mode_set(priv->dev, true, ecanreceive); + return IRQ_HANDLED; +} + +irqreturn_t omapl_rx_can_intr(int irq, void *dev_id) +{ + + struct net_device *ndev = dev_id; + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + u32 intc_status = 0; + + intc_status = pru_can_get_intc_status(priv->dev); + if (intc_status & 4) + return omapl_tx_can_intr(irq, dev_id); + if (intc_status & 2) { + if (!work_pending(&priv->rx_work)) + queue_work(priv->pru_can_wQ, &priv->rx_work); + } + + return IRQ_HANDLED; +} + +static int omapl_pru_can_open(struct net_device *ndev) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + int err; + + /* register interrupt handler */ + err = request_irq(priv->trx_irq, &omapl_rx_can_intr, IRQF_SHARED, + "pru_can_irq", ndev); + if (err) { + dev_err(priv->dev, "error requesting rx interrupt\n"); + goto exit_trx_irq; + } + /* common open */ + err = open_candev(ndev); + if (err) { + dev_err(priv->dev, "open_candev() failed %d\n", err); + goto exit_open; + } + + pru_can_emu_init(priv->dev, priv->can.clock.freq); + priv->tx_tail = MB_MIN; + priv->tx_head = MB_MAX; + + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX0, 0); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX1, 1); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX2, 2); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX3, 3); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX4, 4); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX5, 5); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX6, 6); + pru_can_rx_id_map(priv->dev, CONFIG_DA8XX_PRU_CANID_MBX7, 7); + + omapl_pru_can_start(ndev); + netif_start_queue(ndev); + return 0; + +exit_open: + free_irq(priv->trx_irq, ndev); +exit_trx_irq: + return err; +} + +static int omapl_pru_can_close(struct net_device *ndev) +{ + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + + if (!netif_queue_stopped(ndev)) + netif_stop_queue(ndev); + + close_candev(ndev); + + free_irq(priv->trx_irq, ndev); + return 0; +} + +static const struct net_device_ops omapl_pru_can_netdev_ops = { + .ndo_open = omapl_pru_can_open, + .ndo_stop = omapl_pru_can_close, + .ndo_start_xmit = omapl_pru_can_start_xmit, +}; + +static int __devinit omapl_pru_can_probe(struct platform_device *pdev) +{ + struct net_device *ndev = NULL; + const struct da8xx_pru_can_data *pdata; + struct omapl_pru_can_priv *priv = NULL; + struct device *dev = &pdev->dev; + u32 err; + + pdata = dev->platform_data; + if (!pdata) { + dev_err(&pdev->dev, "platform data not found\n"); + return -EINVAL; + } + + ndev = alloc_candev(sizeof(struct omapl_pru_can_priv), MB_MAX + 1); + if (!ndev) { + dev_err(&pdev->dev, "alloc_candev failed\n"); + err = -ENOMEM; + goto probe_exit; + } + priv = netdev_priv(ndev); + + priv->trx_irq = platform_get_irq(to_platform_device(dev->parent), 0); + if (!priv->trx_irq) { + dev_err(&pdev->dev, "unable to get pru interrupt resources!\n"); + err = -ENODEV; + goto probe_exit; + } + + priv->ndev = ndev; + priv->dev = dev; /* priv->dev = pdev->dev */ + + priv->can.bittiming_const = NULL; + priv->can.do_set_bittiming = omapl_pru_can_set_bittiming; + priv->can.do_set_mode = omapl_pru_can_set_mode; + priv->can.do_get_state = omapl_pru_can_get_state; + priv->can_tx_hndl.u8prunumber = CAN_TX_PRU_1; + priv->can_rx_hndl.u8prunumber = CAN_RX_PRU_0; + + /* we support local echo, no arp */ + ndev->flags |= (IFF_ECHO | IFF_NOARP); + + /* pdev->dev->device_private->driver_data = ndev */ + platform_set_drvdata(pdev, ndev); + SET_NETDEV_DEV(ndev, &pdev->dev); + ndev->netdev_ops = &omapl_pru_can_netdev_ops; + + priv->can.clock.freq = pruss_get_clk_freq(priv->dev); + + priv->clk_timer = clk_get(&pdev->dev, "pll1_sysclk2"); + if (IS_ERR(priv->clk_timer)) { + dev_err(&pdev->dev, "no timer clock available\n"); + err = PTR_ERR(priv->clk_timer); + priv->clk_timer = NULL; + goto probe_exit_candev; + } + priv->timer_freq = clk_get_rate(priv->clk_timer); + + err = register_candev(ndev); + if (err) { + dev_err(&pdev->dev, "register_candev() failed\n"); + err = -ENODEV; + goto probe_exit_clk; + } + + err = request_firmware(&priv->fw_tx, "PRU_CAN_Emulation_Tx.bin", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "can't load firmware\n"); + err = -ENODEV; + goto probe_exit_clk; + } + + dev_info(&pdev->dev, "fw_tx size %d. downloading...\n", + priv->fw_tx->size); + + err = request_firmware(&priv->fw_rx, "PRU_CAN_Emulation_Rx.bin", + &pdev->dev); + if (err) { + dev_err(&pdev->dev, "can't load firmware\n"); + err = -ENODEV; + goto probe_release_fw; + } + dev_info(&pdev->dev, "fw_rx size %d. downloading...\n", + priv->fw_rx->size); + + /* init the pru */ + pru_can_emu_init(priv->dev, priv->can.clock.freq); + udelay(200); + + pruss_enable(priv->dev, CAN_RX_PRU_0); + pruss_enable(priv->dev, CAN_TX_PRU_1); + + /* download firmware into pru */ + err = pruss_load(priv->dev, CAN_RX_PRU_0, + (u32 *)priv->fw_rx->data, (priv->fw_rx->size / 4)); + if (err) { + dev_err(&pdev->dev, "firmware download error\n"); + err = -ENODEV; + goto probe_release_fw_1; + } + err = pruss_load(priv->dev, CAN_TX_PRU_1, + (u32 *)priv->fw_tx->data, (priv->fw_tx->size / 4)); + if (err) { + dev_err(&pdev->dev, "firmware download error\n"); + err = -ENODEV; + goto probe_release_fw_1; + } + + if (pru_can_calc_timing(priv->dev, DFLT_PRU_FREQ, DFLT_PRU_BITRATE) != 0) + return -EINVAL; + + pruss_run(priv->dev, CAN_RX_PRU_0); + pruss_run(priv->dev, CAN_TX_PRU_1); + + /*Create The Work Queue */ + priv->pru_can_wQ = create_freezeable_workqueue("omapl_pru_wQ"); + if (priv->pru_can_wQ == NULL) { + dev_err(&pdev->dev, "failed to create work queue\n"); + err = -ENODEV; + goto probe_release_fw_1; + } + + INIT_WORK(&priv->rx_work, omapl_pru_can_rx_wQ); + dev_info(&pdev->dev, + "%s device registered (trx_irq = %d, clk = %d)\n", + DRV_NAME, priv->trx_irq, priv->can.clock.freq); + + return 0; + +probe_release_fw_1: + release_firmware(priv->fw_rx); +probe_release_fw: + release_firmware(priv->fw_tx); +probe_exit_clk: + clk_put(priv->clk_timer); +probe_exit_candev: + if (NULL != ndev) + free_candev(ndev); +probe_exit: + return err; +} + +static int __devexit omapl_pru_can_remove(struct platform_device *pdev) +{ + struct net_device *ndev = platform_get_drvdata(pdev); + struct omapl_pru_can_priv *priv = netdev_priv(ndev); + + omapl_pru_can_stop(ndev); + + pru_can_emu_exit(priv->dev); + release_firmware(priv->fw_tx); + release_firmware(priv->fw_rx); + clk_put(priv->clk_timer); + flush_workqueue(priv->pru_can_wQ); + destroy_workqueue(priv->pru_can_wQ); + unregister_candev(ndev); + free_candev(ndev); + platform_set_drvdata(pdev, NULL); + return 0; +} + +#ifdef CONFIG_PM +static int omapl_pru_can_suspend(struct platform_device *pdev, + pm_message_t mesg) +{ + dev_info(&pdev->dev, "%s not yet implemented\n", __func__); + return 0; +} + +static int omapl_pru_can_resume(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s not yet implemented\n", __func__); + return 0; +} +#else +#define omapl_pru_can_suspend NULL +#define omapl_pru_can_resume NULL +#endif /* CONFIG_PM */ + +static struct platform_driver omapl_pru_can_driver = { + .probe = omapl_pru_can_probe, + .remove = __devexit_p(omapl_pru_can_remove), + .suspend = omapl_pru_can_suspend, + .resume = omapl_pru_can_resume, + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init omapl_pru_can_init(void) +{ + __can_debug(KERN_INFO DRV_DESC "\n"); + return platform_driver_register(&omapl_pru_can_driver); +} + +module_init(omapl_pru_can_init); + +static void __exit omapl_pru_can_exit(void) +{ + __can_debug(KERN_INFO DRV_DESC " unloaded\n"); + platform_driver_unregister(&omapl_pru_can_driver); +} + +module_exit(omapl_pru_can_exit); + +MODULE_AUTHOR("Subhasish Ghosh "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("omapl pru CAN netdevice driver"); diff --git a/drivers/net/can/da8xx_pruss/pruss_can_api.c b/drivers/net/can/da8xx_pruss/pruss_can_api.c new file mode 100644 index 0000000..e48c877 --- /dev/null +++ b/drivers/net/can/da8xx_pruss/pruss_can_api.c @@ -0,0 +1,1224 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Wilfred Felix + * + * 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 "pruss_can_api.h" + +static can_emu_drv_inst gstr_can_inst[ecanmaxinst]; + +/* + * pru_can_set_brp() Updates the BRP register of PRU0 + * and PRU1 of OMAP L138. This API will be called by the + * Application to updtae the BRP register of PRU0 and PRU1 + * + * param u16bitrateprescaler The can bus bitrate + * prescaler value be set + * + * return SUCCESS or FAILURE + */ +s16 pru_can_set_brp(struct device *dev, u16 u16bitrateprescaler) +{ + + u32 u32offset; + + if (u16bitrateprescaler > 255) { + return -1; + } + + u32offset = (PRU_CAN_RX_CLOCK_BRP_REGISTER); + pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1); + + u32offset = (PRU_CAN_TX_CLOCK_BRP_REGISTER); + pruss_writel(dev, u32offset, (u32 *) &u16bitrateprescaler, 1); + + return 0; + +} + +/* + * pru_can_set_bit_timing() Updates the timing register + * of PRU0 and PRU1 of OMAP L138. This API will be called by + * the Application to updtae the timing register of PRU0 and PRU1 + * + * param pstrbittiming Pointer to structure holding + * the bit timing values for can bus. + * + * return SUCCESS or FAILURE + */ +s16 pru_can_set_bit_timing(struct device *dev, + can_bit_timing_consts *pstrbittiming) +{ + + u32 u32offset; + u32 u32serregister; + + u32serregister = 0; + + if (pstrbittiming == NULL) { + return -1; + } + + if ((pstrbittiming->u8syncjumpwidth > PRU_CAN_MAX_SJW) || + (pstrbittiming->u8phseg1 > PRU_CAN_MAX_PHSEG1) || + (pstrbittiming->u8phseg2 > PRU_CAN_MAX_PHSEG2)) { + return -1; + } + + u32serregister = u32serregister | + ((pstrbittiming->u8syncjumpwidth << 7) | + (pstrbittiming->u8phseg1 << 3) | + (pstrbittiming->u8phseg2)); + + u32offset = (PRU_CAN_TX_TIMING_REGISTER); + pruss_writel(dev, u32offset, (u32 *) &u32serregister, 1); + + u32offset = (PRU_CAN_RX_TIMING_REGISTER); + pruss_writel(dev, u32offset, (u32 *) &u32serregister, 1); + + return 0; +} + + +/* + * pru_can_calc_timing() Updates the timing values of + * PRU0 and PRU1 of OMAP L138. This API will be called by the + * Application to updtae the timing values of PRU0 and PRU1 + * + * return SUCCESS or FAILURE + */ +s16 pru_can_calc_timing(struct device *dev, u32 pru_freq, u32 bit_rate) +{ + u16 u16phaseseg1; + u16 u16phaseseg2; + u32 u32offset; + u32 u32timing_value; + u32 u32setup_value; + u32timing_value = TIMER_CLK_FREQ / bit_rate; + u32offset = (PRU_CAN_TIMING_VAL_TX); + pruss_writel(dev, u32offset, (u32 *) &u32timing_value, 4); + pruss_readl(dev, u32offset, (u32 *) &u32timing_value, 4); + u32setup_value = + (GPIO_SETUP_DELAY * (pru_freq / 1000000) / 1000) / + DELAY_LOOP_LENGTH; + u32offset = (PRU_CAN_TIMING_VAL_TX_SJW); + pruss_writel(dev, u32offset, (u32 *) &u32setup_value, 4); + u16phaseseg1 = (u16) (u32timing_value / 2); + u16phaseseg2 = u32timing_value - u16phaseseg1; + u16phaseseg1 -= TIMER_SETUP_DELAY; + u16phaseseg2 -= TIMER_SETUP_DELAY; + u32setup_value = (u16phaseseg1 << 16) | u16phaseseg2; + u32offset = (PRU_CAN_TIMING_VAL_RX); + pruss_writel(dev, u32offset, (u32 *) &u32setup_value, 4); + u32offset = (PRU_CAN_TIMING_VAL_RX + 4); + pruss_writel(dev, u32offset, (u32 *) &u32timing_value, 4); + + return 0; +} + +/* + * pru_can_write_data_to_mailbox() Updates the transmit + * mailboxes of PRU1 of OMAP L138. This API will be called by + * the Application to updtae the transmit mailboxes of PRU1 + * + * param pu16canframedata Can mailbox data buffer + * + * param u8mailboxnum Mailbox to be updated + * + * return SUCCESS or FAILURE + */ +s16 pru_can_write_data_to_mailbox(struct device *dev, + can_emu_app_hndl *pstremuapphndl) +{ + s16 s16subrtnretval; + u32 u32offset; + + if (pstremuapphndl == NULL) { + return -1; + } + + switch ((u8) pstremuapphndl->ecanmailboxnumber) { + case 0: + u32offset = (PRU_CAN_TX_MAILBOX0); + break; + case 1: + u32offset = (PRU_CAN_TX_MAILBOX1); + break; + case 2: + u32offset = (PRU_CAN_TX_MAILBOX2); + break; + case 3: + u32offset = (PRU_CAN_TX_MAILBOX3); + break; + case 4: + u32offset = (PRU_CAN_TX_MAILBOX4); + break; + case 5: + u32offset = (PRU_CAN_TX_MAILBOX5); + break; + case 6: + u32offset = (PRU_CAN_TX_MAILBOX6); + break; + case 7: + u32offset = (PRU_CAN_TX_MAILBOX7); + break; + default: + return -1; + } + + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &(pstremuapphndl->strcanmailbox), 4); + if (s16subrtnretval == -1) { + return -1; + } + return 0; +} + +/* + * pru_can_get_data_from_mailbox() Receive data from the + * receive mailboxes of PRU0 of OMAP L138. This API will be called by + * the Application to get data from the receive mailboxes of PRU0 + * + * param pu16canframedata Can mailbox data buffer + * + * param u8mailboxnum Mailbox to be updated + * + * return SUCCESS or FAILURE + */ +s16 pru_can_get_data_from_mailbox(struct device *dev, + can_emu_app_hndl *pstremuapphndl) +{ + s16 s16subrtnretval; + u32 u32offset; + + if (pstremuapphndl == NULL) { + return -1; + } + + switch ((u8) pstremuapphndl->ecanmailboxnumber) { + case 0: + u32offset = (PRU_CAN_RX_MAILBOX0); + break; + case 1: + u32offset = (PRU_CAN_RX_MAILBOX1); + break; + case 2: + u32offset = (PRU_CAN_RX_MAILBOX2); + break; + case 3: + u32offset = (PRU_CAN_RX_MAILBOX3); + break; + case 4: + u32offset = (PRU_CAN_RX_MAILBOX4); + break; + case 5: + u32offset = (PRU_CAN_RX_MAILBOX5); + break; + case 6: + u32offset = (PRU_CAN_RX_MAILBOX6); + break; + case 7: + u32offset = (PRU_CAN_RX_MAILBOX7); + break; + case 8: + u32offset = (PRU_CAN_RX_MAILBOX8); + break; + default: + return -1; + } + + s16subrtnretval = + pruss_readl(dev, u32offset, + (u32 *) &(pstremuapphndl->strcanmailbox), + 4); + if (s16subrtnretval == -1) { + return -1; + } + return 0; +} + +/* + * pru_can_receive_id_map() Receive mailboxes ID Mapping of + * PRU0 of OMAP L138. This API will be called by the Application + * to map the IDs to receive mailboxes of PRU0 + * + * param u32nodeid Can node ID + * + * param ecanmailboxno Mailbox to be mapped + * + * return SUCCESS or FAILURE + */ +s16 pru_can_rx_id_map(struct device *dev, u32 u32nodeid, + can_mailbox_number ecanmailboxno) +{ + + pruss_writel(dev, (PRU_CAN_ID_MAP + + (((u8) ecanmailboxno) * 4)), (u32 *) &u32nodeid, 1); + + return 0; +} + +/* + * pru_can_get_intr_status() Gets the interrupts + * status register value. This API will be called by the Application + * to get the interrupts status register value + * + * param u8prunumber PRU number for which IntStatusReg + * has to be read + * + * return SUCCESS or FAILURE + */ +s16 pru_can_get_intr_status(struct device *dev, + can_emu_app_hndl *pstremuapphndl) +{ + u32 u32offset; + s16 s16subrtnretval = -1; + + if (pstremuapphndl == NULL) { + return -1; + } + + if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) { + u32offset = (PRU_CAN_TX_INTERRUPT_STATUS_REGISTER); + } else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) { + u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER); + } else { + return -1; + } + + s16subrtnretval = pruss_readl(dev, u32offset, + (u32 *) &pstremuapphndl->u32interruptstatus, 1); + if (s16subrtnretval == -1) { + return -1; + } + + return 0; +} + +/* + * pru_can_get_global_status() Gets the globalstatus + * register value. This API will be called by the Application + * to get the global status register value + * + * return SUCCESS or FAILURE + */ +s16 pru_can_get_global_status(struct device *dev, + can_emu_app_hndl *pstremuapphndl) +{ + u32 u32offset; + int s16subrtnretval = -1; + + if (pstremuapphndl == NULL) { + return -1; + } + + if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) { + u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER); + } else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) { + u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER); + } else { + return -1; + } + + s16subrtnretval = pruss_readl(dev, u32offset, + (u32 *) &pstremuapphndl->u32globalstatus, 1); + if (s16subrtnretval == -1) { + return -1; + } + + return 0; +} + +/* + * pru_can_get_mailbox_status() Gets the mailbox status + * register value. This API will be called by the Application + * to get the mailbox status register value + * + * return SUCCESS or FAILURE + */ +s16 pru_can_get_mailbox_status(struct device *dev, + can_emu_app_hndl *pstremuapphndl) +{ + u32 u32offset; + s16 s16subrtnretval = -1; + + if (pstremuapphndl == NULL) { + return -1; + } + + if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_1) { + switch (pstremuapphndl->ecanmailboxnumber) { + case 0: + u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER); + break; + case 1: + u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER); + break; + case 2: + u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER); + break; + case 3: + u32offset = (PRU_CAN_TX_MAILBOX3_STATUS_REGISTER); + break; + case 4: + u32offset = (PRU_CAN_TX_MAILBOX4_STATUS_REGISTER); + break; + case 5: + u32offset = (PRU_CAN_TX_MAILBOX5_STATUS_REGISTER); + break; + case 6: + u32offset = (PRU_CAN_TX_MAILBOX6_STATUS_REGISTER); + break; + case 7: + u32offset = (PRU_CAN_TX_MAILBOX7_STATUS_REGISTER); + break; + default: + return -1; + } + } + + else if (pstremuapphndl->u8prunumber == DA8XX_PRUCORE_0) { + switch (pstremuapphndl->ecanmailboxnumber) { + case 0: + u32offset = (PRU_CAN_RX_MAILBOX0_STATUS_REGISTER); + break; + case 1: + u32offset = (PRU_CAN_RX_MAILBOX1_STATUS_REGISTER); + break; + case 2: + u32offset = (PRU_CAN_RX_MAILBOX2_STATUS_REGISTER); + break; + case 3: + u32offset = (PRU_CAN_RX_MAILBOX3_STATUS_REGISTER); + break; + case 4: + u32offset = (PRU_CAN_RX_MAILBOX4_STATUS_REGISTER); + break; + case 5: + u32offset = (PRU_CAN_RX_MAILBOX5_STATUS_REGISTER); + break; + case 6: + u32offset = (PRU_CAN_RX_MAILBOX6_STATUS_REGISTER); + break; + case 7: + u32offset = (PRU_CAN_RX_MAILBOX7_STATUS_REGISTER); + break; + case 8: + u32offset = (PRU_CAN_RX_MAILBOX8_STATUS_REGISTER); + break; + default: + return -1; + } + } + + else { + return -1; + } + + s16subrtnretval = pruss_readl(dev, u32offset, + (u32 *) &pstremuapphndl->u32mailboxstatus, 1); + if (s16subrtnretval == -1) { + return -1; + } + + return 0; +} + +s16 pru_can_tx_mode_set(struct device *dev, bool btransfer_flag, + can_transfer_direction ecan_trx) +{ + u32 u32offset; + u32 u32value; + + if (ecan_trx == ecantransmit) { + u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER); + pruss_readl(dev, u32offset, &u32value, 1); + if (btransfer_flag == true) { + u32value &= 0x1F; + u32value |= 0x80; + } else { + u32value &= 0x7F; + } + pruss_writel(dev, u32offset, &u32value, 1); + u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER); + pruss_writel(dev, u32offset, &u32value, 1); + } else if (ecan_trx == ecanreceive) { + u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER); + pruss_readl(dev, u32offset, &u32value, 1); + if (btransfer_flag == true) { + u32value &= 0x1F; + u32value |= 0x40; + } else { + u32value &= 0xBF; + } + pruss_writel(dev, u32offset, &u32value, 1); + u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER); + pruss_writel(dev, u32offset, &u32value, 1); + } else + return -1; + + return 0; +} + +/* + * pru_can_config_mode_set() Sets the timing value + * for data transfer. This API will be called by the Application + * to set timing valus for data transfer + * + * return SUCCESS or FAILURE + */ +s16 pru_can_config_mode_set(struct device *dev, bool bconfigmodeflag) +{ + + u32 u32bitrateprescaler; + u32 u32canbittiming; + + pruss_readl(dev, (PRU_CAN_TX_CLOCK_BRP_REGISTER), + (u32 *) &u32bitrateprescaler, 1); + pruss_readl(dev, (PRU_CAN_TX_TIMING_REGISTER), + (u32 *) &u32canbittiming, 1); + + if (bconfigmodeflag == 1) { + pru_can_calc_timing(dev, u32canbittiming, u32bitrateprescaler); + } + + else { + pru_can_calc_timing(dev, 0, 0); + } + + return 0; +} + +/* + * pru_can_emu_init() Initializes the Can + * Emulation Parameters. This API will be called by the Application + * to Initialize the Can Emulation Parameters + * + * param u32pruclock PRU Clock value + * + * return SUCCESS or FAILURE + */ +s16 pru_can_emu_init(struct device *dev, u32 u32pruclock) +{ + u32 u32offset; + u32 u32value; + s16 s16subrtnretval = -1; + u8 u8loop; + + for (u8loop = 0; u8loop < (u8) ecanmaxinst; u8loop++) { + gstr_can_inst[u8loop].bcaninststate = (bool) 0; + gstr_can_inst[u8loop].ecantransferdirection = + (can_transfer_direction) 0; + gstr_can_inst[u8loop].u32apphandlerptr = 0; + } + + u32offset = (PRU_CAN_TX_GLOBAL_CONTROL_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_GLOBAL_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000040; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000040; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_INTERRUPT_MASK_REGISTER & 0xFFFF); + u32value = 0x00004000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_INTERRUPT_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = + pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX3_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX4_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX5_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX6_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_MAILBOX7_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000001; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_ERROR_COUNTER_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_TIMING_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_CLOCK_BRP_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_TX_ERROR_COUNTER_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_POLARITY0 & 0xFFFF); + u32value = 0xFFFFFFFF; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + u32offset = (PRUSS_INTC_POLARITY1 & 0xFFFF); + u32value = 0xFFFFFFFF; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + u32offset = (PRUSS_INTC_TYPE0 & 0xFFFF); + u32value = 0x1C000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + u32offset = (PRUSS_INTC_TYPE1 & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_HSTINTENIDXCLR & 0xFFFF); + u32value = 0x0; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_GLBLEN & 0xFFFF); + u32value = 0x1; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + /* tx intr map arm->pru */ + u32offset = (PRUSS_INTC_HSTINTENIDXSET & 0xFFFF); + u32value = 0x0; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_HOSTMAP0 & 0xFFFF); + u32value = 0x03020100; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_HOSTMAP1 & 0xFFFF); + u32value = 0x07060504; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_HOSTMAP2 & 0xFFFF); + u32value = 0x0000908; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_CHANMAP0 & 0xFFFF); + u32value = 0; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_CHANMAP8 & 0xFFFF); + u32value = 0x00020200; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF); + u32value = 32; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF); + u32value = 19; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 19; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF); + u32value = 18; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 18; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF); + u32value = 34; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 34; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 32; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRUSS_INTC_HOSTINTEN & 0xFFFF); + u32value = 0x5; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + +/* PRU0 - Rx Internal Registers Initializations */ + + u32offset = (PRU_CAN_RX_GLOBAL_CONTROL_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_GLOBAL_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000040; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_INTERRUPT_MASK_REGISTER & 0xFFFF); + u32value = 0x00004000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX0_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX1_STATUS_REGISTER & 0xFFFF); + u32value = 0x0000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX2_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX3_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX4_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX5_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX6_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_MAILBOX7_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_ERROR_COUNTER_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_TIMING_REGISTER & 0xFFFF); + u32value = 0x0000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + u32offset = (PRU_CAN_RX_CLOCK_BRP_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + return 0; +} + + +/* + * pru_can_emu_open() Opens the can emu for + * application to use. This API will be called by the Application + * to Open the can emu for application to use. + * + * param pstremuapphndl Pointer to application handler + * structure + * + * return SUCCESS or FAILURE + */ +s16 pru_can_emu_open(struct device *dev, can_emu_app_hndl *pstremuapphndl) +{ + + if (pstremuapphndl == NULL) { + return -1; + } + + if (gstr_can_inst[pstremuapphndl->ecaninstance].bcaninststate + == 1) { + return -1; + } + + gstr_can_inst[(u8) pstremuapphndl->ecaninstance]. + bcaninststate = (bool)1; + gstr_can_inst[(u8) pstremuapphndl-> + ecaninstance].ecantransferdirection = + (can_transfer_direction)(u8)pstremuapphndl->ecantransferdirection; + gstr_can_inst[(u8) pstremuapphndl->ecaninstance]. + u32apphandlerptr = (u32) pstremuapphndl; + + return 0; +} + + +/* + * brief pru_can_emu_close() Closes the can emu for other + * applications to use. This API will be called by the Application to Close + * the can emu for other applications to use + * + * param pstremuapphndl Pointer to application handler structure + * + * return SUCCESS or FAILURE + */ +s16 pru_can_emu_close(struct device *dev, can_emu_app_hndl *pstremuapphndl) +{ + + if (pstremuapphndl == NULL) { + return -1; + } + if (gstr_can_inst[pstremuapphndl->ecaninstance].bcaninststate + == 0) { + return -1; + } + if ((u32) pstremuapphndl != gstr_can_inst[(u8) pstremuapphndl-> + ecaninstance].u32apphandlerptr){ + return -1; + } + gstr_can_inst[(u8) pstremuapphndl->ecaninstance].bcaninststate + = (bool) 0; + gstr_can_inst[(u8) pstremuapphndl-> + ecaninstance].ecantransferdirection = (can_transfer_direction) 0; + gstr_can_inst[(u8) pstremuapphndl-> + ecaninstance].u32apphandlerptr = 0; + + return 0; +} + +/* + * brief pru_can_emu_exit() Diables all the PRUs + * This API will be called by the Application to disable all PRUs + * param None + * return SUCCESS or FAILURE + */ +s16 pru_can_emu_exit(struct device *dev) +{ + s16 s16subrtnretval; + + s16subrtnretval = pruss_disable(dev, CAN_RX_PRU_0); + if (s16subrtnretval == -1) + return -1; + s16subrtnretval = pruss_disable(dev, CAN_TX_PRU_1); + if (s16subrtnretval == -1) + return -1; + + return 0; +} + +s16 pru_can_emu_sreset(struct device *dev) +{ + return 0; +} + +s16 pru_can_tx(struct device *dev, u8 u8mailboxnumber, u8 u8prunumber) +{ + u32 u32offset = 0; + u32 u32value = 0; + s16 s16subrtnretval = -1; + + if (DA8XX_PRUCORE_1 == u8prunumber) { + switch (u8mailboxnumber) { + case 0: + u32offset = (PRU_CAN_TX_MAILBOX0_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 1: + u32offset = (PRU_CAN_TX_MAILBOX1_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 2: + u32offset = (PRU_CAN_TX_MAILBOX2_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 3: + u32offset = (PRU_CAN_TX_MAILBOX3_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 4: + u32offset = (PRU_CAN_TX_MAILBOX4_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 5: + u32offset = (PRU_CAN_TX_MAILBOX5_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 6: + u32offset = (PRU_CAN_TX_MAILBOX6_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 7: + u32offset = (PRU_CAN_TX_MAILBOX7_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000080; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + default: + return -1; + } + } else { + + u32offset = (PRU_CAN_RX_INTERRUPT_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_readl(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + u32value = u32value & ~(1 << u8mailboxnumber); + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + + switch (u8mailboxnumber) { + case 0: + u32offset = (PRU_CAN_RX_MAILBOX0_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 1: + u32offset = (PRU_CAN_RX_MAILBOX1_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 2: + u32offset = (PRU_CAN_RX_MAILBOX2_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 3: + u32offset = (PRU_CAN_RX_MAILBOX3_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 4: + u32offset = (PRU_CAN_RX_MAILBOX4_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 5: + u32offset = (PRU_CAN_RX_MAILBOX5_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 6: + u32offset = (PRU_CAN_RX_MAILBOX6_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + case 7: + u32offset = (PRU_CAN_RX_MAILBOX7_STATUS_REGISTER & 0xFFFF); + u32value = 0x00000000; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + break; + default: + return -1; + } + } + return 0; +} + +s16 pru_can_start_abort_tx(struct device *dev, bool bcantransmitabortflag) +{ + u32 u32offset; + u32 u32value; + s16 s16subrtnretval; + u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF); + u32value = 32; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + + u32offset = (PRUSS_INTC_ENIDXSET & 0xFFFF); + u32value = 32; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + + u32offset = (PRUSS_INTC_STATIDXSET & 0xFFFF); + u32value = 32; + s16subrtnretval = pruss_writel(dev, u32offset, + (u32 *) &u32value, 1); + if (s16subrtnretval == -1) { + return -1; + } + return 0; +} + +s16 pru_can_mask_ints(struct device *dev, u32 int_mask) +{ + return 0; +} + +int pru_can_get_error_cnt(struct device *dev, u8 u8prunumber) +{ + return 0; +} + +int pru_can_get_intc_status(struct device *dev) +{ + u32 u32offset = 0; + u32 u32getvalue = 0; + u32 u32clrvalue = 0; + + u32offset = (PRUSS_INTC_STATCLRINT1 & 0xFFFF); + pruss_readl(dev, u32offset, (u32 *) &u32getvalue, 1); + + if (u32getvalue & 4) + u32clrvalue = 34; /* CLR Event 34 */ + + if (u32getvalue & 2) + u32clrvalue = 33; /* CLR Event 33 */ + + if (u32clrvalue) { + u32offset = (PRUSS_INTC_STATIDXCLR & 0xFFFF); + pruss_writel(dev, u32offset, &u32clrvalue, 1); + } else + return -1; + + return u32getvalue; +} diff --git a/drivers/net/can/da8xx_pruss/pruss_can_api.h b/drivers/net/can/da8xx_pruss/pruss_can_api.h new file mode 100644 index 0000000..f5f704b --- /dev/null +++ b/drivers/net/can/da8xx_pruss/pruss_can_api.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2010 Texas Instruments Incorporated + * Author: Ganeshan N + * + * 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 _PRU_CAN_API_H_ +#define _PRU_CAN_API_H_ + +#include +#include + + +#define CAN_BIT_TIMINGS (0x273) + +/* Timer Clock is sourced from DDR freq (PLL1 SYS CLK 2) */ +#define TIMER_CLK_FREQ 132000000 + +#define TIMER_SETUP_DELAY 14 +#define GPIO_SETUP_DELAY 150 + +#define CAN_RX_PRU_0 PRUSS_NUM0 +#define CAN_TX_PRU_1 PRUSS_NUM1 + +/* Number of Instruction in the Delay loop */ +#define DELAY_LOOP_LENGTH 2 + +#define PRU1_BASE_ADDR 0x2000 + +#define PRU_CAN_TX_GLOBAL_CONTROL_REGISTER (PRU1_BASE_ADDR) +#define PRU_CAN_TX_GLOBAL_STATUS_REGISTER (PRU1_BASE_ADDR + 0x04) +#define PRU_CAN_TX_INTERRUPT_MASK_REGISTER (PRU1_BASE_ADDR + 0x08) +#define PRU_CAN_TX_INTERRUPT_STATUS_REGISTER (PRU1_BASE_ADDR + 0x0C) +#define PRU_CAN_TX_MAILBOX0_STATUS_REGISTER (PRU1_BASE_ADDR + 0x10) +#define PRU_CAN_TX_MAILBOX1_STATUS_REGISTER (PRU1_BASE_ADDR + 0x14) +#define PRU_CAN_TX_MAILBOX2_STATUS_REGISTER (PRU1_BASE_ADDR + 0x18) +#define PRU_CAN_TX_MAILBOX3_STATUS_REGISTER (PRU1_BASE_ADDR + 0x1C) +#define PRU_CAN_TX_MAILBOX4_STATUS_REGISTER (PRU1_BASE_ADDR + 0x20) +#define PRU_CAN_TX_MAILBOX5_STATUS_REGISTER (PRU1_BASE_ADDR + 0x24) +#define PRU_CAN_TX_MAILBOX6_STATUS_REGISTER (PRU1_BASE_ADDR + 0x28) +#define PRU_CAN_TX_MAILBOX7_STATUS_REGISTER (PRU1_BASE_ADDR + 0x2C) +#define PRU_CAN_TX_ERROR_COUNTER_REGISTER (PRU1_BASE_ADDR + 0x30) +#define PRU_CAN_TX_TIMING_REGISTER (PRU1_BASE_ADDR + 0x34) +#define PRU_CAN_TX_CLOCK_BRP_REGISTER (PRU1_BASE_ADDR + 0x38) + +#define PRU_CAN_TX_MAILBOX0 (PRU1_BASE_ADDR + 0x40) +#define PRU_CAN_TX_MAILBOX1 (PRU1_BASE_ADDR + 0x50) +#define PRU_CAN_TX_MAILBOX2 (PRU1_BASE_ADDR + 0x60) +#define PRU_CAN_TX_MAILBOX3 (PRU1_BASE_ADDR + 0x70) +#define PRU_CAN_TX_MAILBOX4 (PRU1_BASE_ADDR + 0x80) +#define PRU_CAN_TX_MAILBOX5 (PRU1_BASE_ADDR + 0x90) +#define PRU_CAN_TX_MAILBOX6 (PRU1_BASE_ADDR + 0xA0) +#define PRU_CAN_TX_MAILBOX7 (PRU1_BASE_ADDR + 0xB0) + +#define PRU_CAN_TIMING_VAL_TX (PRU1_BASE_ADDR + 0xC0) +#define PRU_CAN_TIMING_VAL_TX_SJW (PRU1_BASE_ADDR + 0xC4) +#define PRU_CAN_TRANSMIT_FRAME (PRU1_BASE_ADDR + 0xE0) + +#define PRU0_BASE_ADDR 0 + +#define PRU_CAN_RX_GLOBAL_CONTROL_REGISTER (PRU0_BASE_ADDR) +#define PRU_CAN_RX_GLOBAL_STATUS_REGISTER (PRU0_BASE_ADDR + 0x04) +#define PRU_CAN_RX_INTERRUPT_MASK_REGISTER (PRU0_BASE_ADDR + 0x08) +#define PRU_CAN_RX_INTERRUPT_STATUS_REGISTER (PRU0_BASE_ADDR + 0x0C) +#define PRU_CAN_RX_MAILBOX0_STATUS_REGISTER (PRU0_BASE_ADDR + 0x10) +#define PRU_CAN_RX_MAILBOX1_STATUS_REGISTER (PRU0_BASE_ADDR + 0x14) +#define PRU_CAN_RX_MAILBOX2_STATUS_REGISTER (PRU0_BASE_ADDR + 0x18) +#define PRU_CAN_RX_MAILBOX3_STATUS_REGISTER (PRU0_BASE_ADDR + 0x1C) +#define PRU_CAN_RX_MAILBOX4_STATUS_REGISTER (PRU0_BASE_ADDR + 0x20) +#define PRU_CAN_RX_MAILBOX5_STATUS_REGISTER (PRU0_BASE_ADDR + 0x24) +#define PRU_CAN_RX_MAILBOX6_STATUS_REGISTER (PRU0_BASE_ADDR + 0x28) +#define PRU_CAN_RX_MAILBOX7_STATUS_REGISTER (PRU0_BASE_ADDR + 0x2C) +#define PRU_CAN_RX_MAILBOX8_STATUS_REGISTER (PRU0_BASE_ADDR + 0x30) +#define PRU_CAN_RX_ERROR_COUNTER_REGISTER (PRU0_BASE_ADDR + 0x34) +#define PRU_CAN_RX_TIMING_REGISTER (PRU0_BASE_ADDR + 0x38) +#define PRU_CAN_RX_CLOCK_BRP_REGISTER (PRU0_BASE_ADDR + 0x3C) + +#define PRU_CAN_RX_MAILBOX0 (PRU0_BASE_ADDR + 0x40) +#define PRU_CAN_RX_MAILBOX1 (PRU0_BASE_ADDR + 0x50) +#define PRU_CAN_RX_MAILBOX2 (PRU0_BASE_ADDR + 0x60) +#define PRU_CAN_RX_MAILBOX3 (PRU0_BASE_ADDR + 0x70) +#define PRU_CAN_RX_MAILBOX4 (PRU0_BASE_ADDR + 0x80) +#define PRU_CAN_RX_MAILBOX5 (PRU0_BASE_ADDR + 0x90) +#define PRU_CAN_RX_MAILBOX6 (PRU0_BASE_ADDR + 0xA0) +#define PRU_CAN_RX_MAILBOX7 (PRU0_BASE_ADDR + 0xB0) +#define PRU_CAN_RX_MAILBOX8 (PRU0_BASE_ADDR + 0xC0) + +#define PRU_CAN_TIMING_VAL_RX (PRU0_BASE_ADDR + 0xD0) +#define PRU_CAN_RECEIVE_FRAME (PRU0_BASE_ADDR + 0xD4) +#define PRU_CAN_ID_MAP (PRU0_BASE_ADDR + 0xF0) + +#define PRU_CAN_ERROR_ACTIVE 128 + +#define CAN_ACK_FAILED 0xE +#define CAN_ARBTR_FAIL 0xD +#define CAN_BIT_ERROR 0xC +#define CAN_TRANSMISSION_SUCCESS 0xA + +#define STD_DATA_FRAME 0x1 +#define EXTD_DATA_FRAME 0x2 +#define STD_REMOTE_FRAME 0x3 +#define EXTD_REMOTE_FRAME 0x4 + +#define PRU_CAN_MAX_SJW 8 +#define PRU_CAN_MAX_PHSEG1 25 +#define PRU_CAN_MAX_PHSEG2 25 + +#define DA8XX_PRUCANCORE_0_REGS 0x7000 +#define DA8XX_PRUCANCORE_1_REGS 0x7800 +#define PRU0_PROG_RAM_START_OFFSET 0x8000 +#define PRU1_PROG_RAM_START_OFFSET 0xC000 +#define PRU_CAN_INIT_MAX_TIMEOUT 0xFF + +typedef enum { + ecaninst0 = 0, + ecaninst1, + ecanmaxinst +} can_instance_enum; + +typedef enum { + ecanmailbox0 = 0, + ecanmailbox1, + ecanmailbox2, + ecanmailbox3, + ecanmailbox4, + ecanmailbox5, + ecanmailbox6, + ecanmailbox7 +} can_mailbox_number; + +typedef enum { + ecandirectioninit = 0, + ecantransmit, + ecanreceive +} can_transfer_direction; + +typedef struct { + u16 u16extendedidentifier; + u16 u16baseidentifier; + u8 u8data7; + u8 u8data6; + u8 u8data5; + u8 u8data4; + u8 u8data3; + u8 u8data2; + u8 u8data1; + u8 u8data0; + u16 u16datalength; + u16 u16crc; +} can_mail_box_structure; + +typedef struct { + can_transfer_direction ecantransferdirection; +} can_mailbox_config; + +typedef struct { + can_instance_enum ecaninstance; + can_transfer_direction ecantransferdirection; + can_mail_box_structure strcanmailbox; + can_mailbox_number ecanmailboxnumber; + u8 u8prunumber; + u32 u32globalstatus; + u32 u32interruptstatus; + u32 u32mailboxstatus; +} can_emu_app_hndl; + +typedef struct { + bool bcaninststate; + can_transfer_direction ecantransferdirection; + u32 u32apphandlerptr; +} can_emu_drv_inst; + +typedef struct { + u8 u8syncjumpwidth; + u8 u8phseg1; + u8 u8phseg2; +} can_bit_timing_consts; + +/* Field Definition Macros */ + +/* CONTROL */ + +/* + * pru_can_set_brp() Updates the BRP register of PRU. + */ +s16 pru_can_set_brp(struct device *dev, u16 u16prescaler); + +/* + * pru_can_set_bit_timing() Updates the timing register of PRU + */ +s16 pru_can_set_bit_timing(struct device *dev, + can_bit_timing_consts *pstrbittiming); + +/* + * pru_can_calc_timing() Updates the timing values of PRU + */ +s16 pru_can_calc_timing(struct device *dev, + u32 u32bittiming, u32 u32bitrateprescaler); + +/* + * pru_can_write_data_to_mailbox() Updates the transmit mailboxes of PRU1 + */ +s16 pru_can_write_data_to_mailbox(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + +/* + * pru_can_get_data_from_mailbox() Receive data from receive mailboxes + */ +s16 pru_can_get_data_from_mailbox(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + +/* + * pru_can_rx_id_map()Receive mailboxes ID Mapping of PRU0 + */ +s16 pru_can_rx_id_map(struct device *dev, + u32 u32nodeid, can_mailbox_number ecanmailboxno); + +/* + *pru_can_get_intr_status() Get interrupts status register value + */ +s16 pru_can_get_intr_status(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + + +/* + * pru_can_get_global_status() Get the globalstatus register value + */ +s16 pru_can_get_global_status(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + +/* + * pru_can_get_mailbox_status() Get mailbox status reg value + */ +s16 pru_can_get_mailbox_status(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + +/* + * pru_can_configuration_mode_set() Sets timing val for data transfer + */ +s16 pru_can_config_mode_set(struct device *dev, + bool bconfig_modeflag); + +/* + * pru_can_emu_init() Initializes Can Emulation Parameters + */ +s16 pru_can_emu_init(struct device *dev, + u32 u32pruclock); + +/* + * pru_can_emu_open() Opens can emu for application to use + */ +s16 pru_can_emu_open(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + +/* + * pru_can_emu_close() Closes can emu for applications to use + */ +s16 pru_can_emu_close(struct device *dev, + can_emu_app_hndl *pstremuapphndl); + +/* + * pru_can_emu_exit() Diables all the PRUs + */ +s16 pru_can_emu_exit(struct device *dev); + +s16 pru_can_tx_mode_set(struct device *dev, bool btransfer_flag, + can_transfer_direction ecan_trx); + +s16 pru_can_emu_sreset(struct device *dev); + +s16 pru_can_tx(struct device *dev, + u8 u8mailboxnumber, u8 u8prunumber); + +s16 pru_can_start_abort_tx(struct device *dev, + bool btxabort_flag); + +s16 pru_can_mask_ints(struct device *dev, u32 int_mask); + +s32 pru_can_get_error_cnt(struct device *dev, u8 u8prunumber); + +s32 pru_can_get_intc_status(struct device *dev); +#endif -- 1.7.2.3 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From subhasish at mistralsolutions.com Fri Jul 8 14:42:16 2011 From: subhasish at mistralsolutions.com (Subhasish Ghosh) Date: Fri, 08 Jul 2011 19:42:16 -0000 Subject: [PATCH v5 1/1] drivers:staging:pruss: add pruss staging mfd driver. In-Reply-To: <1306827939-4133-1-git-send-email-subhasish@mistralsolutions.com> References: <1306827939-4133-1-git-send-email-subhasish@mistralsolutions.com> Message-ID: <1306827939-4133-2-git-send-email-subhasish@mistralsolutions.com> This patch adds the pruss MFD driver and associated include files. For details regarding the PRUSS please refer the folowing link: http://processors.wiki.ti.com/index.php/Programmable_Realtime_Unit_Subsystem The rational behind the MFD driver being the fact that multiple devices can be implemented on the cores independently. This is determined by the nature of the program which is loaded into the PRU's instruction memory. A device may be de-initialized and another loaded or two different devices can be run simultaneously on the two cores. It's also possible, as in our case, to implement a single device on both the PRU's resulting in improved load sharing. Signed-off-by: Subhasish Ghosh --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/pruss/Kconfig | 16 ++ drivers/staging/pruss/Makefile | 5 + drivers/staging/pruss/TODO | 14 + drivers/staging/pruss/pruss.c | 483 ++++++++++++++++++++++++++++++++++++ drivers/staging/pruss/pruss.h | 130 ++++++++++ drivers/staging/pruss/pruss_core.h | 132 ++++++++++ 8 files changed, 783 insertions(+), 0 deletions(-) create mode 100644 drivers/staging/pruss/Kconfig create mode 100644 drivers/staging/pruss/Makefile create mode 100644 drivers/staging/pruss/TODO create mode 100644 drivers/staging/pruss/pruss.c create mode 100644 drivers/staging/pruss/pruss.h create mode 100644 drivers/staging/pruss/pruss_core.h diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 5c8fcfc..9cbf201 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -179,5 +179,7 @@ source "drivers/staging/cptm1217/Kconfig" source "drivers/staging/ste_rmi4/Kconfig" +source "drivers/staging/pruss/Kconfig" + endif # !STAGING_EXCLUDE_BUILD endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index d538863..2296c34 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -70,3 +70,4 @@ obj-$(CONFIG_SND_INTEL_SST) += intel_sst/ obj-$(CONFIG_SPEAKUP) += speakup/ obj-$(CONFIG_TOUCHSCREEN_CLEARPAD_TM1217) += cptm1217/ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ +obj-$(CONFIG_MFD_DA8XX_PRUSS) += pruss/ diff --git a/drivers/staging/pruss/Kconfig b/drivers/staging/pruss/Kconfig new file mode 100644 index 0000000..f426b67 --- /dev/null +++ b/drivers/staging/pruss/Kconfig @@ -0,0 +1,16 @@ +# +# TI's pruss staging drivers +# + +menu "Texas Instruments pruss drivers" + +config MFD_DA8XX_PRUSS + tristate "Texas Instruments DA8XX PRUSS support" + depends on ARCH_DAVINCI_DA850 + select MFD_CORE + help + This driver provides support API 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. +endmenu diff --git a/drivers/staging/pruss/Makefile b/drivers/staging/pruss/Makefile new file mode 100644 index 0000000..a9c657b --- /dev/null +++ b/drivers/staging/pruss/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for TI's pruss drivers +# + +obj-$(CONFIG_MFD_DA8XX_PRUSS) += pruss.o diff --git a/drivers/staging/pruss/TODO b/drivers/staging/pruss/TODO new file mode 100644 index 0000000..38a8ec7 --- /dev/null +++ b/drivers/staging/pruss/TODO @@ -0,0 +1,14 @@ +TODO: + +0. Functionality wise, everything works. + +1. Currently the plan is to add sysfs attributes for + a. prux/load + b. prux/unload + c. prux/run + d. prux/halt + These will add to a more dynamic firmware management for the PRU. + +2. But, not sure how the fdt will effect these entries. + +Please send patches to Greg Kroah-Hartman . diff --git a/drivers/staging/pruss/pruss.c b/drivers/staging/pruss/pruss.c new file mode 100644 index 0000000..8f1455b --- /dev/null +++ b/drivers/staging/pruss/pruss.c @@ -0,0 +1,483 @@ +/* + * Copyright (C) 2010, 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* #include */ +#include "pruss.h" +#include +#include +#include +#include + +struct pruss_priv { + struct device *dev; + spinlock_t lock; + struct resource *res; + struct clk *clk; + void __iomem *ioaddr; +}; + +int pruss_disable(struct device *dev, u8 pruss_num) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct prusscore_regs __iomem *h_pruss; + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 temp_reg; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + spin_lock(&pruss->lock); + + /* pruss deinit */ + iowrite32(0xFFFFFFFF, &pruss_mmap->intc.statclrint[pruss_num]); + + /* Disable PRU */ + h_pruss = &pruss_mmap->core[pruss_num]; + temp_reg = ioread32(&h_pruss->control); + temp_reg = (temp_reg & + ~PRUCORE_CONTROL_COUNTENABLE_MASK) | + ((PRUCORE_CONTROL_COUNTENABLE_DISABLE << + PRUCORE_CONTROL_COUNTENABLE_SHIFT) & + PRUCORE_CONTROL_COUNTENABLE_MASK); + iowrite32(temp_reg, &h_pruss->control); + + temp_reg = ioread32(&h_pruss->control); + temp_reg = (temp_reg & + ~PRUCORE_CONTROL_ENABLE_MASK) | + ((PRUCORE_CONTROL_ENABLE_DISABLE << + PRUCORE_CONTROL_ENABLE_SHIFT) & + PRUCORE_CONTROL_ENABLE_MASK); + iowrite32(temp_reg, &h_pruss->control); + + /* Reset PRU */ + iowrite32(PRUCORE_CONTROL_RESETVAL, + &h_pruss->control); + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_disable); + +int pruss_enable(struct device *dev, u8 pruss_num) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct prusscore_regs __iomem *h_pruss; + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + int i; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + h_pruss = &pruss_mmap->core[pruss_num]; + + /* Reset PRU */ + spin_lock(&pruss->lock); + iowrite32(PRUCORE_CONTROL_RESETVAL, &h_pruss->control); + spin_unlock(&pruss->lock); + + /* Reset any garbage in the ram */ + for (i = 0; i < PRUSS_PRU0_RAM_SZ; i++) + iowrite32(0x0, &pruss_mmap->dram[pruss_num].dram_dt[i]); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_enable); + +/* Load the specified PRU with code */ +int pruss_load(struct device *dev, u8 pruss_num, + u32 *pruss_code, u32 code_size_in_words) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 __iomem *pruss_iram; + int i; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + pruss_iram = (u32 __iomem *)&pruss_mmap->iram[pruss_num].iram_dt; + + 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++) + iowrite32(pruss_code[i], (pruss_iram + i)); + + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_load); + +int pruss_run(struct device *dev, u8 pruss_num) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct prusscore_regs __iomem *h_pruss; + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 temp_reg; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + h_pruss = &pruss_mmap->core[pruss_num]; + + /* Enable dMAX, let it execute the code we just copied */ + spin_lock(&pruss->lock); + temp_reg = ioread32(&h_pruss->control); + temp_reg = (temp_reg & + ~PRUCORE_CONTROL_COUNTENABLE_MASK) | + ((PRUCORE_CONTROL_COUNTENABLE_ENABLE << + PRUCORE_CONTROL_COUNTENABLE_SHIFT) & + PRUCORE_CONTROL_COUNTENABLE_MASK); + iowrite32(temp_reg, &h_pruss->control); + + temp_reg = ioread32(&h_pruss->control); + temp_reg = (temp_reg & + ~PRUCORE_CONTROL_ENABLE_MASK) | + ((PRUCORE_CONTROL_ENABLE_ENABLE << + PRUCORE_CONTROL_ENABLE_SHIFT) & + PRUCORE_CONTROL_ENABLE_MASK); + iowrite32(temp_reg, &h_pruss->control); + spin_unlock(&pruss->lock); + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_run); + +int pruss_wait_for_halt(struct device *dev, u8 pruss_num, u32 timeout) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + struct prusscore_regs __iomem *h_pruss; + struct pruss_map __iomem *pruss_mmap = pruss->ioaddr; + u32 temp_reg; + u32 cnt = timeout; + + if ((pruss_num != PRUCORE_0) && (pruss_num != PRUCORE_1)) + return -EINVAL; + + h_pruss = &pruss_mmap->core[pruss_num]; + + while (cnt--) { + temp_reg = ioread32(&h_pruss->control); + if (((temp_reg & PRUCORE_CONTROL_RUNSTATE_MASK) >> + PRUCORE_CONTROL_RUNSTATE_SHIFT) == + PRUCORE_CONTROL_RUNSTATE_HALT) + break; + } + if (!cnt) + return -EBUSY; + + return 0; +} +EXPORT_SYMBOL_GPL(pruss_wait_for_halt); + +void pruss_writeb(struct device *dev, u32 offset, u8 datatowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addresstowrite; + + addresstowrite = pruss->ioaddr + offset; + iowrite8(datatowrite, addresstowrite); +} +EXPORT_SYMBOL_GPL(pruss_writeb); + +void pruss_rmwb(struct device *dev, u32 offset, u8 mask, u8 val) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addr; + u32 preg_data; + + addr = pruss->ioaddr + offset; + + spin_lock(&pruss->lock); + preg_data = ioread8(addr); + preg_data &= ~mask; + preg_data |= val; + iowrite8(preg_data, addr); + spin_unlock(&pruss->lock); +} +EXPORT_SYMBOL_GPL(pruss_rmwb); + +void pruss_readb(struct device *dev, u32 offset, u8 *datatoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addrtoread; + + addrtoread = pruss->ioaddr + offset ; + *datatoread = ioread8(addrtoread); +} +EXPORT_SYMBOL_GPL(pruss_readb); + +void pruss_readb_multi(struct device *dev, u32 offset, + u8 *datatoread, u16 bytestoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + u8 __iomem *addrtoread; + int i; + + addrtoread = pruss->ioaddr + offset; + + for (i = 0; i < bytestoread; i++) + *datatoread++ = ioread8(addrtoread++); +} +EXPORT_SYMBOL_GPL(pruss_readb_multi); + +void pruss_writel(struct device *dev, u32 offset, + u32 datatowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addresstowrite; + + addresstowrite = pruss->ioaddr + offset; + iowrite32(datatowrite, addresstowrite); +} +EXPORT_SYMBOL_GPL(pruss_writel); + +void pruss_writel_multi(struct device *dev, u32 offset, + u32 *datatowrite, u16 wordstowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + u32 __iomem *addresstowrite; + int i; + + addresstowrite = pruss->ioaddr + offset; + + for (i = 0; i < wordstowrite; i++) + iowrite32(*datatowrite++, addresstowrite++); +} +EXPORT_SYMBOL_GPL(pruss_writel_multi); + +void pruss_rmwl(struct device *dev, u32 offset, u32 mask, u32 val) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addr; + u32 preg_data; + + addr = pruss->ioaddr + offset; + + spin_lock(&pruss->lock); + preg_data = ioread32(addr); + preg_data &= ~mask; + preg_data |= val; + iowrite32(preg_data, addr); + spin_unlock(&pruss->lock); +} +EXPORT_SYMBOL_GPL(pruss_rmwl); + +void pruss_readl(struct device *dev, u32 offset, u32 *datatoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addrtoread; + + addrtoread = pruss->ioaddr + offset; + *datatoread = ioread32(addrtoread); +} +EXPORT_SYMBOL_GPL(pruss_readl); + +void pruss_readl_multi(struct device *dev, u32 offset, + u32 *datatoread, u16 wordstoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + u32 __iomem *addrtoread; + int i; + + addrtoread = pruss->ioaddr + offset; + for (i = 0; i < wordstoread; i++) + *datatoread++ = ioread32(addrtoread++); +} +EXPORT_SYMBOL_GPL(pruss_readl_multi); + +void pruss_writew(struct device *dev, u32 offset, u16 datatowrite) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addresstowrite; + + addresstowrite = pruss->ioaddr + offset; + iowrite16(datatowrite, addresstowrite); +} +EXPORT_SYMBOL_GPL(pruss_writew); + +void pruss_rmww(struct device *dev, u32 offset, u16 mask, u16 val) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addr; + u32 preg_data; + + addr = pruss->ioaddr + offset; + + spin_lock(&pruss->lock); + preg_data = ioread16(addr); + preg_data &= ~mask; + preg_data |= val; + iowrite16(preg_data, addr); + spin_unlock(&pruss->lock); +} +EXPORT_SYMBOL_GPL(pruss_rmww); + +void pruss_readw(struct device *dev, u32 offset, u16 *datatoread) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addrtoread; + + addrtoread = pruss->ioaddr + offset; + *datatoread = ioread16(addrtoread); +} +EXPORT_SYMBOL_GPL(pruss_readw); + +void pruss_idx_writel(struct device *dev, u32 offset, u32 value) +{ + struct pruss_priv *pruss = dev_get_drvdata(dev->parent); + void __iomem *addresstowrite; + + addresstowrite = pruss->ioaddr + offset; + iowrite32(value, addresstowrite); +} +EXPORT_SYMBOL_GPL(pruss_idx_writel); + +static int pruss_mfd_add_devices(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mfd_cell *cell = pdev->dev.platform_data; + s32 err, num_devices = 0; + int i; + + for (i = 0; cell[i].name; i++) { + err = mfd_add_devices(dev, 0, &cell[i], 1, NULL, 0); + if (err) { + dev_err(dev, "cannot add mfd cell: %s\n", + cell[i].name); + continue; + } + num_devices++; + dev_info(dev, "mfd: added %s device\n", cell[i].name); + } + + return num_devices; +} + +static int __devinit pruss_probe(struct platform_device *pdev) +{ + struct pruss_priv *pruss_dev = NULL; + s32 err; + + pruss_dev = kzalloc(sizeof(struct pruss_priv), 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); + + 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 pruss_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pruss_priv *pruss = dev_get_drvdata(dev); + + mfd_remove_devices(dev); + pruss_disable(dev, PRUCORE_0); + pruss_disable(dev, 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 pruss_driver = { + .probe = pruss_probe, + .remove = __devexit_p(pruss_remove), + .driver = { + .name = "pruss_mfd", + .owner = THIS_MODULE, + } +}; + +static int __init pruss_init(void) +{ + return platform_driver_register(&pruss_driver); +} +module_init(pruss_init); + +static void __exit pruss_exit(void) +{ + platform_driver_unregister(&pruss_driver); +} +module_exit(pruss_exit); + +MODULE_DESCRIPTION("Programmable Realtime Unit (PRU) Driver"); +MODULE_AUTHOR("Subhasish Ghosh"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/pruss/pruss.h b/drivers/staging/pruss/pruss.h new file mode 100644 index 0000000..dd3b2d6 --- /dev/null +++ b/drivers/staging/pruss/pruss.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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 +#include +#include "pruss_core.h" + +#define PRUSS_NUM0 PRUCORE_0 +#define PRUSS_NUM1 PRUCORE_1 + +#define PRUSS_PRU0_RAM_SZ 512 +#define PRUSS_PRU1_RAM_SZ 512 +#define PRUSS_PRU0_BASE_ADDRESS 0 +#define PRUSS_PRU1_BASE_ADDRESS 0x2000 +#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) + +int pruss_enable(struct device *dev, u8 pruss_num); + +int pruss_load(struct device *dev, u8 pruss_num, + u32 *pruss_code, u32 code_size_in_words); + +int pruss_run(struct device *dev, u8 pruss_num); + +int pruss_wait_for_halt(struct device *dev, u8 pruss_num, u32 timeout); + +int pruss_disable(struct device *dev, u8 pruss_num); + +void pruss_writeb(struct device *dev, u32 offset, u8 pdatatowrite); + +void pruss_rmwb(struct device *dev, u32 offset, u8 mask, u8 val); + +void pruss_readb(struct device *dev, u32 offset, u8 *pdatatoread); + +void pruss_readb_multi(struct device *dev, u32 offset, + u8 *pdatatoread, u16 bytestoread); + +void pruss_readl(struct device *dev, u32 offset, u32 *pdatatoread); + +void pruss_readl_multi(struct device *dev, u32 offset, + u32 *pdatatoread, u16 wordstoread); + +void pruss_writel(struct device *dev, u32 offset, u32 pdatatowrite); + +void pruss_writel_multi(struct device *dev, u32 offset, + u32 *pdatatowrite, u16 wordstowrite); + +void pruss_rmwl(struct device *dev, u32 offset, u32 mask, u32 val); + +void pruss_idx_writel(struct device *dev, u32 offset, u32 value); + +void pruss_writew(struct device *dev, u32 offset, u16 datatowrite); + +void pruss_rmww(struct device *dev, u32 offset, u16 mask, u16 val); + +void pruss_readw(struct device *dev, u32 offset, u16 *pdatatoread); + +#endif /* End _PRUSS_H_ */ diff --git a/drivers/staging/pruss/pruss_core.h b/drivers/staging/pruss/pruss_core.h new file mode 100644 index 0000000..8ecb0e5 --- /dev/null +++ b/drivers/staging/pruss/pruss_core.h @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2010, 2011 Texas Instruments Incorporated + * Author: Jitendra Kumar + * + * 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_CORE_H_ +#define _PRUSS_CORE_H_ + +#include + +#define PRUCORE_0 (0) +#define PRUCORE_1 (1) + +#define PRUCORE_CONTROL_PCRESETVAL_MASK (0xFFFF0000u) +#define PRUCORE_CONTROL_PCRESETVAL_SHIFT (0x00000010u) +#define PRUCORE_CONTROL_PCRESETVAL_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_RUNSTATE_MASK (0x00008000u) +#define PRUCORE_CONTROL_RUNSTATE_SHIFT (0x0000000Fu) +#define PRUCORE_CONTROL_RUNSTATE_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_RUNSTATE_HALT (0x00000000u) +#define PRUCORE_CONTROL_RUNSTATE_RUN (0x00000001u) +#define PRUCORE_CONTROL_SINGLESTEP_MASK (0x00000100u) +#define PRUCORE_CONTROL_SINGLESTEP_SHIFT (0x00000008u) +#define PRUCORE_CONTROL_SINGLESTEP_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_SINGLESTEP_FREERUN (0x00000000u) +#define PRUCORE_CONTROL_SINGLESTEP_SINGLE (0x00000001u) +#define PRUCORE_CONTROL_COUNTENABLE_MASK (0x00000008u) +#define PRUCORE_CONTROL_COUNTENABLE_SHIFT (0x00000003u) +#define PRUCORE_CONTROL_COUNTENABLE_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_COUNTENABLE_DISABLE (0x00000000u) +#define PRUCORE_CONTROL_COUNTENABLE_ENABLE (0x00000001u) +#define PRUCORE_CONTROL_SLEEPING_MASK (0x00000004u) +#define PRUCORE_CONTROL_SLEEPING_SHIFT (0x00000002u) +#define PRUCORE_CONTROL_SLEEPING_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_SLEEPING_NOTASLEEP (0x00000000u) +#define PRUCORE_CONTROL_SLEEPING_ASLEEP (0x00000001u) +#define PRUCORE_CONTROL_ENABLE_MASK (0x00000002u) +#define PRUCORE_CONTROL_ENABLE_SHIFT (0x00000001u) +#define PRUCORE_CONTROL_ENABLE_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_ENABLE_DISABLE (0x00000000u) +#define PRUCORE_CONTROL_ENABLE_ENABLE (0x00000001u) +#define PRUCORE_CONTROL_SOFTRESET_MASK (0x00000001u) +#define PRUCORE_CONTROL_SOFTRESET_SHIFT (0x00000000u) +#define PRUCORE_CONTROL_SOFTRESET_RESETVAL (0x00000000u) +#define PRUCORE_CONTROL_SOFTRESET_RESET (0x00000000u) +#define PRUCORE_CONTROL_SOFTRESET_OUT_OF_RESET (0x00000001u) +#define PRUCORE_CONTROL_RESETVAL (0x00000000u) + +struct 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_dram { + u8 dram_dt[512]; + u8 res[7680]; +}; + +struct pruss_iram { + u8 iram_dt[4096]; + u8 res[12288]; +}; + +struct pruss_map { + struct pruss_dram dram[2]; + struct pruss_intc_regs intc; + struct prusscore_regs core[2]; + struct pruss_iram iram[2]; +}; +#endif -- 1.7.2.3 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From manjunath.hadli at ti.com Fri Jul 8 14:42:20 2011 From: manjunath.hadli at ti.com (Manjunath Hadli) Date: Fri, 08 Jul 2011 19:42:20 -0000 Subject: [PATCH v15 03/13] davinci vpbe: OSD(On Screen Display) block Message-ID: <1300197449-4113-1-git-send-email-manjunath.hadli@ti.com> This patch implements the functionality of the OSD block of the VPBE. The OSD in total supports 4 planes or Video sources - 2 mainly RGB and 2 Video. The patch implements general handling of all the planes, with specific emphasis on the Video plane capabilities as the Video planes are supported through the V4L2 driver. Signed-off-by: Manjunath Hadli Acked-by: Muralidharan Karicheri Acked-by: Hans Verkuil --- drivers/media/video/davinci/vpbe_osd.c | 1216 +++++++++++++++++++++++++++ drivers/media/video/davinci/vpbe_osd_regs.h | 364 ++++++++ include/media/davinci/vpbe_osd.h | 397 +++++++++ 3 files changed, 1977 insertions(+), 0 deletions(-) create mode 100644 drivers/media/video/davinci/vpbe_osd.c create mode 100644 drivers/media/video/davinci/vpbe_osd_regs.h create mode 100644 include/media/davinci/vpbe_osd.h diff --git a/drivers/media/video/davinci/vpbe_osd.c b/drivers/media/video/davinci/vpbe_osd.c new file mode 100644 index 0000000..bbc43ea --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd.c @@ -0,0 +1,1216 @@ +/* + * Copyright (C) 2007-2010 Texas Instruments Inc + * Copyright (C) 2007 MontaVista Software, Inc. + * + * Andy Lowe (alowe at mvista.com), MontaVista Software + * - Initial version + * Murali Karicheri (mkaricheri at gmail.com), Texas Instruments Ltd. + * - ported to sub device interface + * + * 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 in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "vpbe_osd_regs.h" + +#define MODULE_NAME VPBE_OSD_SUBDEV_NAME + +/* register access routines */ +static inline u32 osd_read(struct osd_state *sd, u32 offset) +{ + struct osd_state *osd = sd; + return readl(osd->osd_base + offset); +} + +static inline u32 osd_write(struct osd_state *sd, u32 val, u32 offset) +{ + struct osd_state *osd = sd; + writel(val, osd->osd_base + offset); + return val; +} + +static inline u32 osd_set(struct osd_state *sd, u32 mask, u32 offset) +{ + struct osd_state *osd = sd; + + u32 addr = osd->osd_base + offset; + u32 val = readl(addr) | mask; + + writel(val, addr); + return val; +} + +static inline u32 osd_clear(struct osd_state *sd, u32 mask, u32 offset) +{ + struct osd_state *osd = sd; + u32 addr = osd->osd_base + offset; + u32 val = readl(addr) & ~mask; + + writel(val, addr); + return val; +} + +static inline u32 osd_modify(struct osd_state *sd, u32 mask, u32 val, + u32 offset) +{ + struct osd_state *osd = sd; + + u32 addr = osd->osd_base + offset; + u32 new_val = (readl(addr) & ~mask) | (val & mask); + writel(new_val, addr); + return new_val; +} + +/* define some macros for layer and pixfmt classification */ +#define is_osd_win(layer) (((layer) == WIN_OSD0) || ((layer) == WIN_OSD1)) +#define is_vid_win(layer) (((layer) == WIN_VID0) || ((layer) == WIN_VID1)) +#define is_rgb_pixfmt(pixfmt) \ + (((pixfmt) == PIXFMT_RGB565) || ((pixfmt) == PIXFMT_RGB888)) +#define is_yc_pixfmt(pixfmt) \ + (((pixfmt) == PIXFMT_YCbCrI) || ((pixfmt) == PIXFMT_YCrCbI) || \ + ((pixfmt) == PIXFMT_NV12)) +#define MAX_WIN_SIZE OSD_VIDWIN0XP_V0X +#define MAX_LINE_LENGTH (OSD_VIDWIN0OFST_V0LO << 5) + +/** + * _osd_dm6446_vid0_pingpong() - field inversion fix for DM6446 + * @sd - ptr to struct osd_state + * @field_inversion - inversion flag + * @fb_base_phys - frame buffer address + * @lconfig - ptr to layer config + * + * This routine implements a workaround for the field signal inversion silicon + * erratum described in Advisory 1.3.8 for the DM6446. The fb_base_phys and + * lconfig parameters apply to the vid0 window. This routine should be called + * whenever the vid0 layer configuration or start address is modified, or when + * the OSD field inversion setting is modified. + * Returns: 1 if the ping-pong buffers need to be toggled in the vsync isr, or + * 0 otherwise + */ +static int _osd_dm6446_vid0_pingpong(struct osd_state *sd, + int field_inversion, + unsigned long fb_base_phys, + const struct osd_layer_config *lconfig) +{ + struct osd_platform_data *pdata; + pdata = (struct osd_platform_data *)sd->dev->platform_data; + if (pdata->field_inv_wa_enable) { + + if (!field_inversion || !lconfig->interlaced) { + osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR); + osd_write(sd, fb_base_phys & ~0x1F, OSD_PPVWIN0ADR); + osd_modify(sd, OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, 0, + OSD_MISCCTL); + return 0; + } else { + unsigned miscctl = OSD_MISCCTL_PPRV; + + osd_write(sd, + (fb_base_phys & ~0x1F) - lconfig->line_length, + OSD_VIDWIN0ADR); + osd_write(sd, + (fb_base_phys & ~0x1F) + lconfig->line_length, + OSD_PPVWIN0ADR); + osd_modify(sd, + OSD_MISCCTL_PPSW | OSD_MISCCTL_PPRV, miscctl, + OSD_MISCCTL); + + return 1; + } + } + return 0; +} + +static void _osd_set_field_inversion(struct osd_state *sd, int enable) +{ + unsigned fsinv = 0; + + if (enable) + fsinv = OSD_MODE_FSINV; + + osd_modify(sd, OSD_MODE_FSINV, fsinv, OSD_MODE); +} + +static void _osd_set_blink_attribute(struct osd_state *sd, int enable, + enum osd_blink_interval blink) +{ + u32 osdatrmd = 0; + + if (enable) { + osdatrmd |= OSD_OSDATRMD_BLNK; + osdatrmd |= blink << OSD_OSDATRMD_BLNKINT_SHIFT; + } + /* caller must ensure that OSD1 is configured in attribute mode */ + osd_modify(sd, OSD_OSDATRMD_BLNKINT | OSD_OSDATRMD_BLNK, osdatrmd, + OSD_OSDATRMD); +} + +static void _osd_set_rom_clut(struct osd_state *sd, + enum osd_rom_clut rom_clut) +{ + if (rom_clut == ROM_CLUT0) + osd_clear(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL); + else + osd_set(sd, OSD_MISCCTL_RSEL, OSD_MISCCTL); +} + +static void _osd_set_palette_map(struct osd_state *sd, + enum osd_win_layer osdwin, + unsigned char pixel_value, + unsigned char clut_index, + enum osd_pix_format pixfmt) +{ + int bmp_reg, bmp_offset, bmp_mask, bmp_shift; + static const int map_1bpp[] = { 0, 15 }; + static const int map_2bpp[] = { 0, 5, 10, 15 }; + + switch (pixfmt) { + case PIXFMT_1BPP: + bmp_reg = map_1bpp[pixel_value & 0x1]; + break; + case PIXFMT_2BPP: + bmp_reg = map_2bpp[pixel_value & 0x3]; + break; + case PIXFMT_4BPP: + bmp_reg = pixel_value & 0xf; + break; + default: + return; + } + + switch (osdwin) { + case OSDWIN_OSD0: + bmp_offset = OSD_W0BMP01 + (bmp_reg >> 1) * sizeof(u32); + break; + case OSDWIN_OSD1: + bmp_offset = OSD_W1BMP01 + (bmp_reg >> 1) * sizeof(u32); + break; + default: + return; + } + + if (bmp_reg & 1) { + bmp_shift = 8; + bmp_mask = 0xff << 8; + } else { + bmp_shift = 0; + bmp_mask = 0xff; + } + + osd_modify(sd, bmp_mask, clut_index << bmp_shift, bmp_offset); +} + +static void _osd_set_rec601_attenuation(struct osd_state *sd, + enum osd_win_layer osdwin, int enable) +{ + switch (osdwin) { + case OSDWIN_OSD0: + osd_modify(sd, OSD_OSDWIN0MD_ATN0E, + enable ? OSD_OSDWIN0MD_ATN0E : 0, + OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + osd_modify(sd, OSD_OSDWIN1MD_ATN1E, + enable ? OSD_OSDWIN1MD_ATN1E : 0, + OSD_OSDWIN1MD); + break; + } +} + +static void _osd_set_blending_factor(struct osd_state *sd, + enum osd_win_layer osdwin, + enum osd_blending_factor blend) +{ + switch (osdwin) { + case OSDWIN_OSD0: + osd_modify(sd, OSD_OSDWIN0MD_BLND0, + blend << OSD_OSDWIN0MD_BLND0_SHIFT, OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + osd_modify(sd, OSD_OSDWIN1MD_BLND1, + blend << OSD_OSDWIN1MD_BLND1_SHIFT, OSD_OSDWIN1MD); + break; + } +} + +static void _osd_enable_color_key(struct osd_state *sd, + enum osd_win_layer osdwin, + unsigned colorkey, + enum osd_pix_format pixfmt) +{ + switch (pixfmt) { + case PIXFMT_RGB565: + osd_write(sd, colorkey & OSD_TRANSPVAL_RGBTRANS, + OSD_TRANSPVAL); + break; + default: + break; + } + + switch (osdwin) { + case OSDWIN_OSD0: + osd_set(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + osd_set(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD); + break; + } +} + +static void _osd_disable_color_key(struct osd_state *sd, + enum osd_win_layer osdwin) +{ + switch (osdwin) { + case OSDWIN_OSD0: + osd_clear(sd, OSD_OSDWIN0MD_TE0, OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + osd_clear(sd, OSD_OSDWIN1MD_TE1, OSD_OSDWIN1MD); + break; + } +} + +static void _osd_set_osd_clut(struct osd_state *sd, + enum osd_win_layer osdwin, + enum osd_clut clut) +{ + u32 winmd = 0; + + switch (osdwin) { + case OSDWIN_OSD0: + if (clut == RAM_CLUT) + winmd |= OSD_OSDWIN0MD_CLUTS0; + osd_modify(sd, OSD_OSDWIN0MD_CLUTS0, winmd, OSD_OSDWIN0MD); + break; + case OSDWIN_OSD1: + if (clut == RAM_CLUT) + winmd |= OSD_OSDWIN1MD_CLUTS1; + osd_modify(sd, OSD_OSDWIN1MD_CLUTS1, winmd, OSD_OSDWIN1MD); + break; + } +} + +static void _osd_set_zoom(struct osd_state *sd, enum osd_layer layer, + enum osd_zoom_factor h_zoom, + enum osd_zoom_factor v_zoom) +{ + u32 winmd = 0; + + switch (layer) { + case WIN_OSD0: + winmd |= (h_zoom << OSD_OSDWIN0MD_OHZ0_SHIFT); + winmd |= (v_zoom << OSD_OSDWIN0MD_OVZ0_SHIFT); + osd_modify(sd, OSD_OSDWIN0MD_OHZ0 | OSD_OSDWIN0MD_OVZ0, winmd, + OSD_OSDWIN0MD); + break; + case WIN_VID0: + winmd |= (h_zoom << OSD_VIDWINMD_VHZ0_SHIFT); + winmd |= (v_zoom << OSD_VIDWINMD_VVZ0_SHIFT); + osd_modify(sd, OSD_VIDWINMD_VHZ0 | OSD_VIDWINMD_VVZ0, winmd, + OSD_VIDWINMD); + break; + case WIN_OSD1: + winmd |= (h_zoom << OSD_OSDWIN1MD_OHZ1_SHIFT); + winmd |= (v_zoom << OSD_OSDWIN1MD_OVZ1_SHIFT); + osd_modify(sd, OSD_OSDWIN1MD_OHZ1 | OSD_OSDWIN1MD_OVZ1, winmd, + OSD_OSDWIN1MD); + break; + case WIN_VID1: + winmd |= (h_zoom << OSD_VIDWINMD_VHZ1_SHIFT); + winmd |= (v_zoom << OSD_VIDWINMD_VVZ1_SHIFT); + osd_modify(sd, OSD_VIDWINMD_VHZ1 | OSD_VIDWINMD_VVZ1, winmd, + OSD_VIDWINMD); + break; + } +} + +static void _osd_disable_layer(struct osd_state *sd, enum osd_layer layer) +{ + switch (layer) { + case WIN_OSD0: + osd_clear(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD); + break; + case WIN_VID0: + osd_clear(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD); + break; + case WIN_OSD1: + /* disable attribute mode as well as disabling the window */ + osd_clear(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1, + OSD_OSDWIN1MD); + break; + case WIN_VID1: + osd_clear(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD); + break; + } +} + +static void osd_disable_layer(struct osd_state *sd, enum osd_layer layer) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + if (!win->is_enabled) { + spin_unlock_irqrestore(&osd->lock, flags); + return; + } + win->is_enabled = 0; + + _osd_disable_layer(sd, layer); + + spin_unlock_irqrestore(&osd->lock, flags); +} + +static void _osd_enable_attribute_mode(struct osd_state *sd) +{ + /* enable attribute mode for OSD1 */ + osd_set(sd, OSD_OSDWIN1MD_OASW, OSD_OSDWIN1MD); +} + +static void _osd_enable_layer(struct osd_state *sd, enum osd_layer layer) +{ + switch (layer) { + case WIN_OSD0: + osd_set(sd, OSD_OSDWIN0MD_OACT0, OSD_OSDWIN0MD); + break; + case WIN_VID0: + osd_set(sd, OSD_VIDWINMD_ACT0, OSD_VIDWINMD); + break; + case WIN_OSD1: + /* enable OSD1 and disable attribute mode */ + osd_modify(sd, OSD_OSDWIN1MD_OASW | OSD_OSDWIN1MD_OACT1, + OSD_OSDWIN1MD_OACT1, OSD_OSDWIN1MD); + break; + case WIN_VID1: + osd_set(sd, OSD_VIDWINMD_ACT1, OSD_VIDWINMD); + break; + } +} + +static int osd_enable_layer(struct osd_state *sd, enum osd_layer layer, + int otherwin) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + spin_lock_irqsave(&osd->lock, flags); + + /* + * use otherwin flag to know this is the other vid window + * in YUV420 mode, if is, skip this check + */ + if (!otherwin && (!win->is_allocated || + !win->fb_base_phys || + !cfg->line_length || + !cfg->xsize || + !cfg->ysize)) { + spin_unlock_irqrestore(&osd->lock, flags); + return -1; + } + + if (win->is_enabled) { + spin_unlock_irqrestore(&osd->lock, flags); + return 0; + } + win->is_enabled = 1; + + if (cfg->pixfmt != PIXFMT_OSD_ATTR) + _osd_enable_layer(sd, layer); + else { + _osd_enable_attribute_mode(sd); + _osd_set_blink_attribute(sd, osd->is_blinking, osd->blink); + } + + spin_unlock_irqrestore(&osd->lock, flags); + return 0; +} + +static void _osd_start_layer(struct osd_state *sd, enum osd_layer layer, + unsigned long fb_base_phys, + unsigned long cbcr_ofst) +{ + switch (layer) { + case WIN_OSD0: + osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN0ADR); + break; + case WIN_VID0: + osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN0ADR); + break; + case WIN_OSD1: + osd_write(sd, fb_base_phys & ~0x1F, OSD_OSDWIN1ADR); + break; + case WIN_VID1: + osd_write(sd, fb_base_phys & ~0x1F, OSD_VIDWIN1ADR); + break; + } +} + +static void osd_start_layer(struct osd_state *sd, enum osd_layer layer, + unsigned long fb_base_phys, + unsigned long cbcr_ofst) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + struct osd_layer_config *cfg = &win->lconfig; + + spin_lock_irqsave(&osd->lock, flags); + + win->fb_base_phys = fb_base_phys & ~0x1F; + _osd_start_layer(sd, layer, fb_base_phys, cbcr_ofst); + + if (layer == WIN_VID0) { + osd->pingpong = + _osd_dm6446_vid0_pingpong(sd, osd->field_inversion, + win->fb_base_phys, + cfg); + } + + spin_unlock_irqrestore(&osd->lock, flags); +} + +static void osd_get_layer_config(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + *lconfig = win->lconfig; + + spin_unlock_irqrestore(&osd->lock, flags); +} + +/** + * try_layer_config() - Try a specific configuration for the layer + * @sd - ptr to struct osd_state + * @layer - layer to configure + * @lconfig - layer configuration to try + * + * If the requested lconfig is completely rejected and the value of lconfig on + * exit is the current lconfig, then try_layer_config() returns 1. Otherwise, + * try_layer_config() returns 0. A return value of 0 does not necessarily mean + * that the value of lconfig on exit is identical to the value of lconfig on + * entry, but merely that it represents a change from the current lconfig. + */ +static int try_layer_config(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + int bad_config = 0; + + /* verify that the pixel format is compatible with the layer */ + switch (lconfig->pixfmt) { + case PIXFMT_1BPP: + case PIXFMT_2BPP: + case PIXFMT_4BPP: + case PIXFMT_8BPP: + case PIXFMT_RGB565: + bad_config = !is_osd_win(layer); + break; + case PIXFMT_YCbCrI: + case PIXFMT_YCrCbI: + bad_config = !is_vid_win(layer); + break; + case PIXFMT_RGB888: + bad_config = !is_vid_win(layer); + break; + case PIXFMT_NV12: + bad_config = 1; + break; + case PIXFMT_OSD_ATTR: + bad_config = (layer != WIN_OSD1); + break; + default: + bad_config = 1; + break; + } + if (bad_config) { + /* + * The requested pixel format is incompatible with the layer, + * so keep the current layer configuration. + */ + *lconfig = win->lconfig; + return bad_config; + } + + /* DM6446: */ + /* only one OSD window at a time can use RGB pixel formats */ + if (is_osd_win(layer) && is_rgb_pixfmt(lconfig->pixfmt)) { + enum osd_pix_format pixfmt; + if (layer == WIN_OSD0) + pixfmt = osd->win[WIN_OSD1].lconfig.pixfmt; + else + pixfmt = osd->win[WIN_OSD0].lconfig.pixfmt; + + if (is_rgb_pixfmt(pixfmt)) { + /* + * The other OSD window is already configured for an + * RGB, so keep the current layer configuration. + */ + *lconfig = win->lconfig; + return 1; + } + } + + /* DM6446: only one video window at a time can use RGB888 */ + if (is_vid_win(layer) && lconfig->pixfmt == PIXFMT_RGB888) { + enum osd_pix_format pixfmt; + + if (layer == WIN_VID0) + pixfmt = osd->win[WIN_VID1].lconfig.pixfmt; + else + pixfmt = osd->win[WIN_VID0].lconfig.pixfmt; + + if (pixfmt == PIXFMT_RGB888) { + /* + * The other video window is already configured for + * RGB888, so keep the current layer configuration. + */ + *lconfig = win->lconfig; + return 1; + } + } + + /* window dimensions must be non-zero */ + if (!lconfig->line_length || !lconfig->xsize || !lconfig->ysize) { + *lconfig = win->lconfig; + return 1; + } + + /* round line_length up to a multiple of 32 */ + lconfig->line_length = ((lconfig->line_length + 31) / 32) * 32; + lconfig->line_length = + min(lconfig->line_length, (unsigned)MAX_LINE_LENGTH); + lconfig->xsize = min(lconfig->xsize, (unsigned)MAX_WIN_SIZE); + lconfig->ysize = min(lconfig->ysize, (unsigned)MAX_WIN_SIZE); + lconfig->xpos = min(lconfig->xpos, (unsigned)MAX_WIN_SIZE); + lconfig->ypos = min(lconfig->ypos, (unsigned)MAX_WIN_SIZE); + lconfig->interlaced = (lconfig->interlaced != 0); + if (lconfig->interlaced) { + /* ysize and ypos must be even for interlaced displays */ + lconfig->ysize &= ~1; + lconfig->ypos &= ~1; + } + + return 0; +} + +static void _osd_disable_vid_rgb888(struct osd_state *sd) +{ + /* + * The DM6446 supports RGB888 pixel format in a single video window. + * This routine disables RGB888 pixel format for both video windows. + * The caller must ensure that neither video window is currently + * configured for RGB888 pixel format. + */ + osd_clear(sd, OSD_MISCCTL_RGBEN, OSD_MISCCTL); +} + +static void _osd_enable_vid_rgb888(struct osd_state *sd, + enum osd_layer layer) +{ + /* + * The DM6446 supports RGB888 pixel format in a single video window. + * This routine enables RGB888 pixel format for the specified video + * window. The caller must ensure that the other video window is not + * currently configured for RGB888 pixel format, as this routine will + * disable RGB888 pixel format for the other window. + */ + if (layer == WIN_VID0) { + osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN, + OSD_MISCCTL_RGBEN, OSD_MISCCTL); + } else if (layer == WIN_VID1) { + osd_modify(sd, OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN, + OSD_MISCCTL_RGBEN | OSD_MISCCTL_RGBWIN, + OSD_MISCCTL); + } +} + +static void _osd_set_cbcr_order(struct osd_state *sd, + enum osd_pix_format pixfmt) +{ + /* + * The caller must ensure that all windows using YC pixfmt use the same + * Cb/Cr order. + */ + if (pixfmt == PIXFMT_YCbCrI) + osd_clear(sd, OSD_MODE_CS, OSD_MODE); + else if (pixfmt == PIXFMT_YCrCbI) + osd_set(sd, OSD_MODE_CS, OSD_MODE); +} + +static void _osd_set_layer_config(struct osd_state *sd, enum osd_layer layer, + const struct osd_layer_config *lconfig) +{ + u32 winmd = 0, winmd_mask = 0, bmw = 0; + + _osd_set_cbcr_order(sd, lconfig->pixfmt); + + switch (layer) { + case WIN_OSD0: + winmd_mask |= OSD_OSDWIN0MD_RGB0E; + if (lconfig->pixfmt == PIXFMT_RGB565) + winmd |= OSD_OSDWIN0MD_RGB0E; + + winmd_mask |= OSD_OSDWIN0MD_BMW0 | OSD_OSDWIN0MD_OFF0; + + switch (lconfig->pixfmt) { + case PIXFMT_1BPP: + bmw = 0; + break; + case PIXFMT_2BPP: + bmw = 1; + break; + case PIXFMT_4BPP: + bmw = 2; + break; + case PIXFMT_8BPP: + bmw = 3; + break; + default: + break; + } + winmd |= (bmw << OSD_OSDWIN0MD_BMW0_SHIFT); + + if (lconfig->interlaced) + winmd |= OSD_OSDWIN0MD_OFF0; + + osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN0MD); + osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN0OFST); + osd_write(sd, lconfig->xpos, OSD_OSDWIN0XP); + osd_write(sd, lconfig->xsize, OSD_OSDWIN0XL); + if (lconfig->interlaced) { + osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN0YP); + osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN0YL); + } else { + osd_write(sd, lconfig->ypos, OSD_OSDWIN0YP); + osd_write(sd, lconfig->ysize, OSD_OSDWIN0YL); + } + break; + case WIN_VID0: + winmd_mask |= OSD_VIDWINMD_VFF0; + if (lconfig->interlaced) + winmd |= OSD_VIDWINMD_VFF0; + + osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD); + osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN0OFST); + osd_write(sd, lconfig->xpos, OSD_VIDWIN0XP); + osd_write(sd, lconfig->xsize, OSD_VIDWIN0XL); + /* + * For YUV420P format the register contents are + * duplicated in both VID registers + */ + if (lconfig->interlaced) { + osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN0YP); + osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN0YL); + } else { + osd_write(sd, lconfig->ypos, OSD_VIDWIN0YP); + osd_write(sd, lconfig->ysize, OSD_VIDWIN0YL); + } + break; + case WIN_OSD1: + /* + * The caller must ensure that OSD1 is disabled prior to + * switching from a normal mode to attribute mode or from + * attribute mode to a normal mode. + */ + if (lconfig->pixfmt == PIXFMT_OSD_ATTR) { + winmd_mask |= + OSD_OSDWIN1MD_ATN1E | OSD_OSDWIN1MD_RGB1E | + OSD_OSDWIN1MD_CLUTS1 | + OSD_OSDWIN1MD_BLND1 | OSD_OSDWIN1MD_TE1; + } else { + winmd_mask |= OSD_OSDWIN1MD_RGB1E; + if (lconfig->pixfmt == PIXFMT_RGB565) + winmd |= OSD_OSDWIN1MD_RGB1E; + + winmd_mask |= OSD_OSDWIN1MD_BMW1; + switch (lconfig->pixfmt) { + case PIXFMT_1BPP: + bmw = 0; + break; + case PIXFMT_2BPP: + bmw = 1; + break; + case PIXFMT_4BPP: + bmw = 2; + break; + case PIXFMT_8BPP: + bmw = 3; + break; + default: + break; + } + winmd |= (bmw << OSD_OSDWIN1MD_BMW1_SHIFT); + } + + winmd_mask |= OSD_OSDWIN1MD_OFF1; + if (lconfig->interlaced) + winmd |= OSD_OSDWIN1MD_OFF1; + + osd_modify(sd, winmd_mask, winmd, OSD_OSDWIN1MD); + osd_write(sd, lconfig->line_length >> 5, OSD_OSDWIN1OFST); + osd_write(sd, lconfig->xpos, OSD_OSDWIN1XP); + osd_write(sd, lconfig->xsize, OSD_OSDWIN1XL); + if (lconfig->interlaced) { + osd_write(sd, lconfig->ypos >> 1, OSD_OSDWIN1YP); + osd_write(sd, lconfig->ysize >> 1, OSD_OSDWIN1YL); + } else { + osd_write(sd, lconfig->ypos, OSD_OSDWIN1YP); + osd_write(sd, lconfig->ysize, OSD_OSDWIN1YL); + } + break; + case WIN_VID1: + winmd_mask |= OSD_VIDWINMD_VFF1; + if (lconfig->interlaced) + winmd |= OSD_VIDWINMD_VFF1; + + osd_modify(sd, winmd_mask, winmd, OSD_VIDWINMD); + osd_write(sd, lconfig->line_length >> 5, OSD_VIDWIN1OFST); + osd_write(sd, lconfig->xpos, OSD_VIDWIN1XP); + osd_write(sd, lconfig->xsize, OSD_VIDWIN1XL); + /* + * For YUV420P format the register contents are + * duplicated in both VID registers + */ + osd_modify(sd, OSD_MISCCTL_S420D, ~OSD_MISCCTL_S420D, + OSD_MISCCTL); + + if (lconfig->interlaced) { + osd_write(sd, lconfig->ypos >> 1, OSD_VIDWIN1YP); + osd_write(sd, lconfig->ysize >> 1, OSD_VIDWIN1YL); + } else { + osd_write(sd, lconfig->ypos, OSD_VIDWIN1YP); + osd_write(sd, lconfig->ysize, OSD_VIDWIN1YL); + } + break; + } +} + +static int osd_set_layer_config(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + struct osd_layer_config *cfg = &win->lconfig; + int reject_config; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + reject_config = try_layer_config(sd, layer, lconfig); + if (reject_config) { + spin_unlock_irqrestore(&osd->lock, flags); + return reject_config; + } + + /* update the current Cb/Cr order */ + if (is_yc_pixfmt(lconfig->pixfmt)) + osd->yc_pixfmt = lconfig->pixfmt; + + /* + * If we are switching OSD1 from normal mode to attribute mode or from + * attribute mode to normal mode, then we must disable the window. + */ + if (layer == WIN_OSD1) { + if (((lconfig->pixfmt == PIXFMT_OSD_ATTR) && + (cfg->pixfmt != PIXFMT_OSD_ATTR)) || + ((lconfig->pixfmt != PIXFMT_OSD_ATTR) && + (cfg->pixfmt == PIXFMT_OSD_ATTR))) { + win->is_enabled = 0; + _osd_disable_layer(sd, layer); + } + } + + _osd_set_layer_config(sd, layer, lconfig); + + if (layer == WIN_OSD1) { + struct osd_osdwin_state *osdwin_state = + &osd->osdwin[OSDWIN_OSD1]; + + if ((lconfig->pixfmt != PIXFMT_OSD_ATTR) && + (cfg->pixfmt == PIXFMT_OSD_ATTR)) { + /* + * We just switched OSD1 from attribute mode to normal + * mode, so we must initialize the CLUT select, the + * blend factor, transparency colorkey enable, and + * attenuation enable (DM6446 only) bits in the + * OSDWIN1MD register. + */ + _osd_set_osd_clut(sd, OSDWIN_OSD1, + osdwin_state->clut); + _osd_set_blending_factor(sd, OSDWIN_OSD1, + osdwin_state->blend); + if (osdwin_state->colorkey_blending) { + _osd_enable_color_key(sd, OSDWIN_OSD1, + osdwin_state-> + colorkey, + lconfig->pixfmt); + } else + _osd_disable_color_key(sd, OSDWIN_OSD1); + _osd_set_rec601_attenuation(sd, OSDWIN_OSD1, + osdwin_state-> + rec601_attenuation); + } else if ((lconfig->pixfmt == PIXFMT_OSD_ATTR) && + (cfg->pixfmt != PIXFMT_OSD_ATTR)) { + /* + * We just switched OSD1 from normal mode to attribute + * mode, so we must initialize the blink enable and + * blink interval bits in the OSDATRMD register. + */ + _osd_set_blink_attribute(sd, osd->is_blinking, + osd->blink); + } + } + + /* + * If we just switched to a 1-, 2-, or 4-bits-per-pixel bitmap format + * then configure a default palette map. + */ + if ((lconfig->pixfmt != cfg->pixfmt) && + ((lconfig->pixfmt == PIXFMT_1BPP) || + (lconfig->pixfmt == PIXFMT_2BPP) || + (lconfig->pixfmt == PIXFMT_4BPP))) { + enum osd_win_layer osdwin = + ((layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1); + struct osd_osdwin_state *osdwin_state = + &osd->osdwin[osdwin]; + unsigned char clut_index; + unsigned char clut_entries = 0; + + switch (lconfig->pixfmt) { + case PIXFMT_1BPP: + clut_entries = 2; + break; + case PIXFMT_2BPP: + clut_entries = 4; + break; + case PIXFMT_4BPP: + clut_entries = 16; + break; + default: + break; + } + /* + * The default palette map maps the pixel value to the clut + * index, i.e. pixel value 0 maps to clut entry 0, pixel value + * 1 maps to clut entry 1, etc. + */ + for (clut_index = 0; clut_index < 16; clut_index++) { + osdwin_state->palette_map[clut_index] = clut_index; + if (clut_index < clut_entries) { + _osd_set_palette_map(sd, osdwin, clut_index, + clut_index, + lconfig->pixfmt); + } + } + } + + *cfg = *lconfig; + /* DM6446: configure the RGB888 enable and window selection */ + if (osd->win[WIN_VID0].lconfig.pixfmt == PIXFMT_RGB888) + _osd_enable_vid_rgb888(sd, WIN_VID0); + else if (osd->win[WIN_VID1].lconfig.pixfmt == PIXFMT_RGB888) + _osd_enable_vid_rgb888(sd, WIN_VID1); + else + _osd_disable_vid_rgb888(sd); + + if (layer == WIN_VID0) { + osd->pingpong = + _osd_dm6446_vid0_pingpong(sd, osd->field_inversion, + win->fb_base_phys, + cfg); + } + + spin_unlock_irqrestore(&osd->lock, flags); + + return 0; +} + +static void osd_init_layer(struct osd_state *sd, enum osd_layer layer) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + enum osd_win_layer osdwin; + struct osd_osdwin_state *osdwin_state; + struct osd_layer_config *cfg = &win->lconfig; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + win->is_enabled = 0; + _osd_disable_layer(sd, layer); + + win->h_zoom = ZOOM_X1; + win->v_zoom = ZOOM_X1; + _osd_set_zoom(sd, layer, win->h_zoom, win->v_zoom); + + win->fb_base_phys = 0; + _osd_start_layer(sd, layer, win->fb_base_phys, 0); + + cfg->line_length = 0; + cfg->xsize = 0; + cfg->ysize = 0; + cfg->xpos = 0; + cfg->ypos = 0; + cfg->interlaced = 0; + switch (layer) { + case WIN_OSD0: + case WIN_OSD1: + osdwin = (layer == WIN_OSD0) ? OSDWIN_OSD0 : OSDWIN_OSD1; + osdwin_state = &osd->osdwin[osdwin]; + /* + * Other code relies on the fact that OSD windows default to a + * bitmap pixel format when they are deallocated, so don't + * change this default pixel format. + */ + cfg->pixfmt = PIXFMT_8BPP; + _osd_set_layer_config(sd, layer, cfg); + osdwin_state->clut = RAM_CLUT; + _osd_set_osd_clut(sd, osdwin, osdwin_state->clut); + osdwin_state->colorkey_blending = 0; + _osd_disable_color_key(sd, osdwin); + osdwin_state->blend = OSD_8_VID_0; + _osd_set_blending_factor(sd, osdwin, osdwin_state->blend); + osdwin_state->rec601_attenuation = 0; + _osd_set_rec601_attenuation(sd, osdwin, + osdwin_state-> + rec601_attenuation); + if (osdwin == OSDWIN_OSD1) { + osd->is_blinking = 0; + osd->blink = BLINK_X1; + } + break; + case WIN_VID0: + case WIN_VID1: + cfg->pixfmt = osd->yc_pixfmt; + _osd_set_layer_config(sd, layer, cfg); + break; + } + + spin_unlock_irqrestore(&osd->lock, flags); +} + +static void osd_release_layer(struct osd_state *sd, enum osd_layer layer) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + if (!win->is_allocated) { + spin_unlock_irqrestore(&osd->lock, flags); + return; + } + + spin_unlock_irqrestore(&osd->lock, flags); + osd_init_layer(sd, layer); + spin_lock_irqsave(&osd->lock, flags); + + win->is_allocated = 0; + + spin_unlock_irqrestore(&osd->lock, flags); +} + +static int osd_request_layer(struct osd_state *sd, enum osd_layer layer) +{ + struct osd_state *osd = sd; + struct osd_window_state *win = &osd->win[layer]; + unsigned long flags; + + spin_lock_irqsave(&osd->lock, flags); + + if (win->is_allocated) { + spin_unlock_irqrestore(&osd->lock, flags); + return -1; + } + win->is_allocated = 1; + + spin_unlock_irqrestore(&osd->lock, flags); + return 0; +} + +static void _osd_init(struct osd_state *sd) +{ + osd_write(sd, 0, OSD_MODE); + osd_write(sd, 0, OSD_VIDWINMD); + osd_write(sd, 0, OSD_OSDWIN0MD); + osd_write(sd, 0, OSD_OSDWIN1MD); + osd_write(sd, 0, OSD_RECTCUR); + osd_write(sd, 0, OSD_MISCCTL); +} + +static void osd_set_left_margin(struct osd_state *sd, u32 val) +{ + osd_write(sd, val, OSD_BASEPX); +} + +static void osd_set_top_margin(struct osd_state *sd, u32 val) +{ + osd_write(sd, val, OSD_BASEPY); +} + +static int osd_initialize(struct osd_state *osd) +{ + if (osd == NULL) + return -ENODEV; + _osd_init(osd); + + /* set default Cb/Cr order */ + osd->yc_pixfmt = PIXFMT_YCbCrI; + + _osd_set_field_inversion(osd, osd->field_inversion); + _osd_set_rom_clut(osd, osd->rom_clut); + + osd_init_layer(osd, WIN_OSD0); + osd_init_layer(osd, WIN_VID0); + osd_init_layer(osd, WIN_OSD1); + osd_init_layer(osd, WIN_VID1); + + return 0; +} + +static const struct vpbe_osd_ops osd_ops = { + .initialize = osd_initialize, + .request_layer = osd_request_layer, + .release_layer = osd_release_layer, + .enable_layer = osd_enable_layer, + .disable_layer = osd_disable_layer, + .set_layer_config = osd_set_layer_config, + .get_layer_config = osd_get_layer_config, + .start_layer = osd_start_layer, + .set_left_margin = osd_set_left_margin, + .set_top_margin = osd_set_top_margin, +}; + +static int osd_probe(struct platform_device *pdev) +{ + struct osd_state *osd; + struct resource *res; + struct osd_platform_data *pdata; + int ret = 0; + + osd = kzalloc(sizeof(struct osd_state), GFP_KERNEL); + if (osd == NULL) + return -ENOMEM; + + osd->dev = &pdev->dev; + pdata = (struct osd_platform_data *)pdev->dev.platform_data; + osd->vpbe_type = (enum vpbe_types)pdata->vpbe_type; + if (NULL == pdev->dev.platform_data) { + dev_err(osd->dev, "No platform data defined for OSD" + " sub device\n"); + ret = -ENOENT; + goto free_mem; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(osd->dev, "Unable to get OSD register address map\n"); + ret = -ENODEV; + goto free_mem; + } + osd->osd_base_phys = res->start; + osd->osd_size = res->end - res->start + 1; + if (!request_mem_region(osd->osd_base_phys, osd->osd_size, + MODULE_NAME)) { + dev_err(osd->dev, "Unable to reserve OSD MMIO region\n"); + ret = -ENODEV; + goto free_mem; + } + osd->osd_base = (unsigned long)ioremap_nocache(res->start, + osd->osd_size); + if (!osd->osd_base) { + dev_err(osd->dev, "Unable to map the OSD region\n"); + ret = -ENODEV; + goto release_mem_region; + } + spin_lock_init(&osd->lock); + osd->ops = osd_ops; + platform_set_drvdata(pdev, osd); + dev_notice(osd->dev, "OSD sub device probe success\n"); + return ret; + +release_mem_region: + release_mem_region(osd->osd_base_phys, osd->osd_size); +free_mem: + kfree(osd); + return ret; +} + +static int osd_remove(struct platform_device *pdev) +{ + struct osd_state *osd = platform_get_drvdata(pdev); + + iounmap((void *)osd->osd_base); + release_mem_region(osd->osd_base_phys, osd->osd_size); + kfree(osd); + return 0; +} + +static struct platform_driver osd_driver = { + .probe = osd_probe, + .remove = osd_remove, + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + }, +}; + +static int osd_init(void) +{ + if (platform_driver_register(&osd_driver)) { + printk(KERN_ERR "Unable to register davinci osd driver\n"); + return -ENODEV; + } + + return 0; +} + +static void osd_exit(void) +{ + platform_driver_unregister(&osd_driver); +} + +module_init(osd_init); +module_exit(osd_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("DaVinci OSD Manager Driver"); +MODULE_AUTHOR("Texas Instruments"); diff --git a/drivers/media/video/davinci/vpbe_osd_regs.h b/drivers/media/video/davinci/vpbe_osd_regs.h new file mode 100644 index 0000000..584520f --- /dev/null +++ b/drivers/media/video/davinci/vpbe_osd_regs.h @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2006-2010 Texas Instruments Inc + * + * 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 in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#ifndef _VPBE_OSD_REGS_H +#define _VPBE_OSD_REGS_H + +/* VPBE Global Registers */ +#define VPBE_PID 0x0 +#define VPBE_PCR 0x4 + +/* VPSS CLock Registers */ +#define VPSSCLK_PID 0x00 +#define VPSSCLK_CLKCTRL 0x04 + +/* VPSS Buffer Logic Registers */ +#define VPSSBL_PID 0x00 +#define VPSSBL_PCR 0x04 +#define VPSSBL_BCR 0x08 +#define VPSSBL_INTSTAT 0x0C +#define VPSSBL_INTSEL 0x10 +#define VPSSBL_EVTSEL 0x14 +#define VPSSBL_MEMCTRL 0x18 +#define VPSSBL_CCDCMUX 0x1C + +/* DM365 ISP5 system configuration */ +#define ISP5_PID 0x0 +#define ISP5_PCCR 0x4 +#define ISP5_BCR 0x8 +#define ISP5_INTSTAT 0xC +#define ISP5_INTSEL1 0x10 +#define ISP5_INTSEL2 0x14 +#define ISP5_INTSEL3 0x18 +#define ISP5_EVTSEL 0x1c +#define ISP5_CCDCMUX 0x20 + +/* VPBE On-Screen Display Subsystem Registers (OSD) */ +#define OSD_MODE 0x00 +#define OSD_VIDWINMD 0x04 +#define OSD_OSDWIN0MD 0x08 +#define OSD_OSDWIN1MD 0x0C +#define OSD_OSDATRMD 0x0C +#define OSD_RECTCUR 0x10 +#define OSD_VIDWIN0OFST 0x18 +#define OSD_VIDWIN1OFST 0x1C +#define OSD_OSDWIN0OFST 0x20 +#define OSD_OSDWIN1OFST 0x24 +#define OSD_VIDWINADH 0x28 +#define OSD_VIDWIN0ADL 0x2C +#define OSD_VIDWIN0ADR 0x2C +#define OSD_VIDWIN1ADL 0x30 +#define OSD_VIDWIN1ADR 0x30 +#define OSD_OSDWINADH 0x34 +#define OSD_OSDWIN0ADL 0x38 +#define OSD_OSDWIN0ADR 0x38 +#define OSD_OSDWIN1ADL 0x3C +#define OSD_OSDWIN1ADR 0x3C +#define OSD_BASEPX 0x40 +#define OSD_BASEPY 0x44 +#define OSD_VIDWIN0XP 0x48 +#define OSD_VIDWIN0YP 0x4C +#define OSD_VIDWIN0XL 0x50 +#define OSD_VIDWIN0YL 0x54 +#define OSD_VIDWIN1XP 0x58 +#define OSD_VIDWIN1YP 0x5C +#define OSD_VIDWIN1XL 0x60 +#define OSD_VIDWIN1YL 0x64 +#define OSD_OSDWIN0XP 0x68 +#define OSD_OSDWIN0YP 0x6C +#define OSD_OSDWIN0XL 0x70 +#define OSD_OSDWIN0YL 0x74 +#define OSD_OSDWIN1XP 0x78 +#define OSD_OSDWIN1YP 0x7C +#define OSD_OSDWIN1XL 0x80 +#define OSD_OSDWIN1YL 0x84 +#define OSD_CURXP 0x88 +#define OSD_CURYP 0x8C +#define OSD_CURXL 0x90 +#define OSD_CURYL 0x94 +#define OSD_W0BMP01 0xA0 +#define OSD_W0BMP23 0xA4 +#define OSD_W0BMP45 0xA8 +#define OSD_W0BMP67 0xAC +#define OSD_W0BMP89 0xB0 +#define OSD_W0BMPAB 0xB4 +#define OSD_W0BMPCD 0xB8 +#define OSD_W0BMPEF 0xBC +#define OSD_W1BMP01 0xC0 +#define OSD_W1BMP23 0xC4 +#define OSD_W1BMP45 0xC8 +#define OSD_W1BMP67 0xCC +#define OSD_W1BMP89 0xD0 +#define OSD_W1BMPAB 0xD4 +#define OSD_W1BMPCD 0xD8 +#define OSD_W1BMPEF 0xDC +#define OSD_VBNDRY 0xE0 +#define OSD_EXTMODE 0xE4 +#define OSD_MISCCTL 0xE8 +#define OSD_CLUTRAMYCB 0xEC +#define OSD_CLUTRAMCR 0xF0 +#define OSD_TRANSPVAL 0xF4 +#define OSD_TRANSPVALL 0xF4 +#define OSD_TRANSPVALU 0xF8 +#define OSD_TRANSPBMPIDX 0xFC +#define OSD_PPVWIN0ADR 0xFC + +/* bit definitions */ +#define VPBE_PCR_VENC_DIV (1 << 1) +#define VPBE_PCR_CLK_OFF (1 << 0) + +#define VPSSBL_INTSTAT_HSSIINT (1 << 14) +#define VPSSBL_INTSTAT_CFALDINT (1 << 13) +#define VPSSBL_INTSTAT_IPIPE_INT5 (1 << 12) +#define VPSSBL_INTSTAT_IPIPE_INT4 (1 << 11) +#define VPSSBL_INTSTAT_IPIPE_INT3 (1 << 10) +#define VPSSBL_INTSTAT_IPIPE_INT2 (1 << 9) +#define VPSSBL_INTSTAT_IPIPE_INT1 (1 << 8) +#define VPSSBL_INTSTAT_IPIPE_INT0 (1 << 7) +#define VPSSBL_INTSTAT_IPIPEIFINT (1 << 6) +#define VPSSBL_INTSTAT_OSDINT (1 << 5) +#define VPSSBL_INTSTAT_VENCINT (1 << 4) +#define VPSSBL_INTSTAT_H3AINT (1 << 3) +#define VPSSBL_INTSTAT_CCDC_VDINT2 (1 << 2) +#define VPSSBL_INTSTAT_CCDC_VDINT1 (1 << 1) +#define VPSSBL_INTSTAT_CCDC_VDINT0 (1 << 0) + +/* DM365 ISP5 bit definitions */ +#define ISP5_INTSTAT_VENCINT (1 << 21) +#define ISP5_INTSTAT_OSDINT (1 << 20) + +/* VMOD TVTYP options for HDMD=0 */ +#define SDTV_NTSC 0 +#define SDTV_PAL 1 +/* VMOD TVTYP options for HDMD=1 */ +#define HDTV_525P 0 +#define HDTV_625P 1 +#define HDTV_1080I 2 +#define HDTV_720P 3 + +#define OSD_MODE_CS (1 << 15) +#define OSD_MODE_OVRSZ (1 << 14) +#define OSD_MODE_OHRSZ (1 << 13) +#define OSD_MODE_EF (1 << 12) +#define OSD_MODE_VVRSZ (1 << 11) +#define OSD_MODE_VHRSZ (1 << 10) +#define OSD_MODE_FSINV (1 << 9) +#define OSD_MODE_BCLUT (1 << 8) +#define OSD_MODE_CABG_SHIFT 0 +#define OSD_MODE_CABG (0xff << 0) + +#define OSD_VIDWINMD_VFINV (1 << 15) +#define OSD_VIDWINMD_V1EFC (1 << 14) +#define OSD_VIDWINMD_VHZ1_SHIFT 12 +#define OSD_VIDWINMD_VHZ1 (3 << 12) +#define OSD_VIDWINMD_VVZ1_SHIFT 10 +#define OSD_VIDWINMD_VVZ1 (3 << 10) +#define OSD_VIDWINMD_VFF1 (1 << 9) +#define OSD_VIDWINMD_ACT1 (1 << 8) +#define OSD_VIDWINMD_V0EFC (1 << 6) +#define OSD_VIDWINMD_VHZ0_SHIFT 4 +#define OSD_VIDWINMD_VHZ0 (3 << 4) +#define OSD_VIDWINMD_VVZ0_SHIFT 2 +#define OSD_VIDWINMD_VVZ0 (3 << 2) +#define OSD_VIDWINMD_VFF0 (1 << 1) +#define OSD_VIDWINMD_ACT0 (1 << 0) + +#define OSD_OSDWIN0MD_ATN0E (1 << 14) +#define OSD_OSDWIN0MD_RGB0E (1 << 13) +#define OSD_OSDWIN0MD_BMP0MD_SHIFT 13 +#define OSD_OSDWIN0MD_BMP0MD (3 << 13) +#define OSD_OSDWIN0MD_CLUTS0 (1 << 12) +#define OSD_OSDWIN0MD_OHZ0_SHIFT 10 +#define OSD_OSDWIN0MD_OHZ0 (3 << 10) +#define OSD_OSDWIN0MD_OVZ0_SHIFT 8 +#define OSD_OSDWIN0MD_OVZ0 (3 << 8) +#define OSD_OSDWIN0MD_BMW0_SHIFT 6 +#define OSD_OSDWIN0MD_BMW0 (3 << 6) +#define OSD_OSDWIN0MD_BLND0_SHIFT 3 +#define OSD_OSDWIN0MD_BLND0 (7 << 3) +#define OSD_OSDWIN0MD_TE0 (1 << 2) +#define OSD_OSDWIN0MD_OFF0 (1 << 1) +#define OSD_OSDWIN0MD_OACT0 (1 << 0) + +#define OSD_OSDWIN1MD_OASW (1 << 15) +#define OSD_OSDWIN1MD_ATN1E (1 << 14) +#define OSD_OSDWIN1MD_RGB1E (1 << 13) +#define OSD_OSDWIN1MD_BMP1MD_SHIFT 13 +#define OSD_OSDWIN1MD_BMP1MD (3 << 13) +#define OSD_OSDWIN1MD_CLUTS1 (1 << 12) +#define OSD_OSDWIN1MD_OHZ1_SHIFT 10 +#define OSD_OSDWIN1MD_OHZ1 (3 << 10) +#define OSD_OSDWIN1MD_OVZ1_SHIFT 8 +#define OSD_OSDWIN1MD_OVZ1 (3 << 8) +#define OSD_OSDWIN1MD_BMW1_SHIFT 6 +#define OSD_OSDWIN1MD_BMW1 (3 << 6) +#define OSD_OSDWIN1MD_BLND1_SHIFT 3 +#define OSD_OSDWIN1MD_BLND1 (7 << 3) +#define OSD_OSDWIN1MD_TE1 (1 << 2) +#define OSD_OSDWIN1MD_OFF1 (1 << 1) +#define OSD_OSDWIN1MD_OACT1 (1 << 0) + +#define OSD_OSDATRMD_OASW (1 << 15) +#define OSD_OSDATRMD_OHZA_SHIFT 10 +#define OSD_OSDATRMD_OHZA (3 << 10) +#define OSD_OSDATRMD_OVZA_SHIFT 8 +#define OSD_OSDATRMD_OVZA (3 << 8) +#define OSD_OSDATRMD_BLNKINT_SHIFT 6 +#define OSD_OSDATRMD_BLNKINT (3 << 6) +#define OSD_OSDATRMD_OFFA (1 << 1) +#define OSD_OSDATRMD_BLNK (1 << 0) + +#define OSD_RECTCUR_RCAD_SHIFT 8 +#define OSD_RECTCUR_RCAD (0xff << 8) +#define OSD_RECTCUR_CLUTSR (1 << 7) +#define OSD_RECTCUR_RCHW_SHIFT 4 +#define OSD_RECTCUR_RCHW (7 << 4) +#define OSD_RECTCUR_RCVW_SHIFT 1 +#define OSD_RECTCUR_RCVW (7 << 1) +#define OSD_RECTCUR_RCACT (1 << 0) + +#define OSD_VIDWIN0OFST_V0LO (0x1ff << 0) + +#define OSD_VIDWIN1OFST_V1LO (0x1ff << 0) + +#define OSD_OSDWIN0OFST_O0LO (0x1ff << 0) + +#define OSD_OSDWIN1OFST_O1LO (0x1ff << 0) + +#define OSD_WINOFST_AH_SHIFT 9 + +#define OSD_VIDWIN0OFST_V0AH (0xf << 9) +#define OSD_VIDWIN1OFST_V1AH (0xf << 9) +#define OSD_OSDWIN0OFST_O0AH (0xf << 9) +#define OSD_OSDWIN1OFST_O1AH (0xf << 9) + +#define OSD_VIDWINADH_V1AH_SHIFT 8 +#define OSD_VIDWINADH_V1AH (0x7f << 8) +#define OSD_VIDWINADH_V0AH_SHIFT 0 +#define OSD_VIDWINADH_V0AH (0x7f << 0) + +#define OSD_VIDWIN0ADL_V0AL (0xffff << 0) + +#define OSD_VIDWIN1ADL_V1AL (0xffff << 0) + +#define OSD_OSDWINADH_O1AH_SHIFT 8 +#define OSD_OSDWINADH_O1AH (0x7f << 8) +#define OSD_OSDWINADH_O0AH_SHIFT 0 +#define OSD_OSDWINADH_O0AH (0x7f << 0) + +#define OSD_OSDWIN0ADL_O0AL (0xffff << 0) + +#define OSD_OSDWIN1ADL_O1AL (0xffff << 0) + +#define OSD_BASEPX_BPX (0x3ff << 0) + +#define OSD_BASEPY_BPY (0x1ff << 0) + +#define OSD_VIDWIN0XP_V0X (0x7ff << 0) + +#define OSD_VIDWIN0YP_V0Y (0x7ff << 0) + +#define OSD_VIDWIN0XL_V0W (0x7ff << 0) + +#define OSD_VIDWIN0YL_V0H (0x7ff << 0) + +#define OSD_VIDWIN1XP_V1X (0x7ff << 0) + +#define OSD_VIDWIN1YP_V1Y (0x7ff << 0) + +#define OSD_VIDWIN1XL_V1W (0x7ff << 0) + +#define OSD_VIDWIN1YL_V1H (0x7ff << 0) + +#define OSD_OSDWIN0XP_W0X (0x7ff << 0) + +#define OSD_OSDWIN0YP_W0Y (0x7ff << 0) + +#define OSD_OSDWIN0XL_W0W (0x7ff << 0) + +#define OSD_OSDWIN0YL_W0H (0x7ff << 0) + +#define OSD_OSDWIN1XP_W1X (0x7ff << 0) + +#define OSD_OSDWIN1YP_W1Y (0x7ff << 0) + +#define OSD_OSDWIN1XL_W1W (0x7ff << 0) + +#define OSD_OSDWIN1YL_W1H (0x7ff << 0) + +#define OSD_CURXP_RCSX (0x7ff << 0) + +#define OSD_CURYP_RCSY (0x7ff << 0) + +#define OSD_CURXL_RCSW (0x7ff << 0) + +#define OSD_CURYL_RCSH (0x7ff << 0) + +#define OSD_EXTMODE_EXPMDSEL (1 << 15) +#define OSD_EXTMODE_SCRNHEXP_SHIFT 13 +#define OSD_EXTMODE_SCRNHEXP (3 << 13) +#define OSD_EXTMODE_SCRNVEXP (1 << 12) +#define OSD_EXTMODE_OSD1BLDCHR (1 << 11) +#define OSD_EXTMODE_OSD0BLDCHR (1 << 10) +#define OSD_EXTMODE_ATNOSD1EN (1 << 9) +#define OSD_EXTMODE_ATNOSD0EN (1 << 8) +#define OSD_EXTMODE_OSDHRSZ15 (1 << 7) +#define OSD_EXTMODE_VIDHRSZ15 (1 << 6) +#define OSD_EXTMODE_ZMFILV1HEN (1 << 5) +#define OSD_EXTMODE_ZMFILV1VEN (1 << 4) +#define OSD_EXTMODE_ZMFILV0HEN (1 << 3) +#define OSD_EXTMODE_ZMFILV0VEN (1 << 2) +#define OSD_EXTMODE_EXPFILHEN (1 << 1) +#define OSD_EXTMODE_EXPFILVEN (1 << 0) + +#define OSD_MISCCTL_BLDSEL (1 << 15) +#define OSD_MISCCTL_S420D (1 << 14) +#define OSD_MISCCTL_BMAPT (1 << 13) +#define OSD_MISCCTL_DM365M (1 << 12) +#define OSD_MISCCTL_RGBEN (1 << 7) +#define OSD_MISCCTL_RGBWIN (1 << 6) +#define OSD_MISCCTL_DMANG (1 << 6) +#define OSD_MISCCTL_TMON (1 << 5) +#define OSD_MISCCTL_RSEL (1 << 4) +#define OSD_MISCCTL_CPBSY (1 << 3) +#define OSD_MISCCTL_PPSW (1 << 2) +#define OSD_MISCCTL_PPRV (1 << 1) + +#define OSD_CLUTRAMYCB_Y_SHIFT 8 +#define OSD_CLUTRAMYCB_Y (0xff << 8) +#define OSD_CLUTRAMYCB_CB_SHIFT 0 +#define OSD_CLUTRAMYCB_CB (0xff << 0) + +#define OSD_CLUTRAMCR_CR_SHIFT 8 +#define OSD_CLUTRAMCR_CR (0xff << 8) +#define OSD_CLUTRAMCR_CADDR_SHIFT 0 +#define OSD_CLUTRAMCR_CADDR (0xff << 0) + +#define OSD_TRANSPVAL_RGBTRANS (0xffff << 0) + +#define OSD_TRANSPVALL_RGBL (0xffff << 0) + +#define OSD_TRANSPVALU_Y_SHIFT 8 +#define OSD_TRANSPVALU_Y (0xff << 8) +#define OSD_TRANSPVALU_RGBU_SHIFT 0 +#define OSD_TRANSPVALU_RGBU (0xff << 0) + +#define OSD_TRANSPBMPIDX_BMP1_SHIFT 8 +#define OSD_TRANSPBMPIDX_BMP1 (0xff << 8) +#define OSD_TRANSPBMPIDX_BMP0_SHIFT 0 +#define OSD_TRANSPBMPIDX_BMP0 0xff + +#endif /* _DAVINCI_VPBE_H_ */ diff --git a/include/media/davinci/vpbe_osd.h b/include/media/davinci/vpbe_osd.h new file mode 100644 index 0000000..7e0e34a --- /dev/null +++ b/include/media/davinci/vpbe_osd.h @@ -0,0 +1,397 @@ +/* + * Copyright (C) 2007-2009 Texas Instruments Inc + * Copyright (C) 2007 MontaVista Software, Inc. + * + * Andy Lowe (alowe at mvista.com), MontaVista Software + * - Initial version + * Murali Karicheri (mkaricheri at gmail.com), Texas Instruments Ltd. + * - ported to sub device interface + * + * 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 in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ +#ifndef _OSD_H +#define _OSD_H + +#define VPBE_OSD_SUBDEV_NAME "vpbe-osd" + +/** + * enum osd_layer + * @WIN_OSD0: On-Screen Display Window 0 + * @WIN_VID0: Video Window 0 + * @WIN_OSD1: On-Screen Display Window 1 + * @WIN_VID1: Video Window 1 + * + * Description: + * An enumeration of the osd display layers. + */ +enum osd_layer { + WIN_OSD0, + WIN_VID0, + WIN_OSD1, + WIN_VID1, +}; + +/** + * enum osd_win_layer + * @OSDWIN_OSD0: On-Screen Display Window 0 + * @OSDWIN_OSD1: On-Screen Display Window 1 + * + * Description: + * An enumeration of the OSD Window layers. + */ +enum osd_win_layer { + OSDWIN_OSD0, + OSDWIN_OSD1, +}; + +/** + * enum osd_pix_format + * @PIXFMT_1BPP: 1-bit-per-pixel bitmap + * @PIXFMT_2BPP: 2-bits-per-pixel bitmap + * @PIXFMT_4BPP: 4-bits-per-pixel bitmap + * @PIXFMT_8BPP: 8-bits-per-pixel bitmap + * @PIXFMT_RGB565: 16-bits-per-pixel RGB565 + * @PIXFMT_YCbCrI: YUV 4:2:2 + * @PIXFMT_RGB888: 24-bits-per-pixel RGB888 + * @PIXFMT_YCrCbI: YUV 4:2:2 with chroma swap + * @PIXFMT_NV12: YUV 4:2:0 planar + * @PIXFMT_OSD_ATTR: OSD Attribute Window pixel format (4bpp) + * + * Description: + * An enumeration of the DaVinci pixel formats. + */ +enum osd_pix_format { + PIXFMT_1BPP = 0, + PIXFMT_2BPP, + PIXFMT_4BPP, + PIXFMT_8BPP, + PIXFMT_RGB565, + PIXFMT_YCbCrI, + PIXFMT_RGB888, + PIXFMT_YCrCbI, + PIXFMT_NV12, + PIXFMT_OSD_ATTR, +}; + +/** + * enum osd_h_exp_ratio + * @H_EXP_OFF: no expansion (1/1) + * @H_EXP_9_OVER_8: 9/8 expansion ratio + * @H_EXP_3_OVER_2: 3/2 expansion ratio + * + * Description: + * An enumeration of the available horizontal expansion ratios. + */ +enum osd_h_exp_ratio { + H_EXP_OFF, + H_EXP_9_OVER_8, + H_EXP_3_OVER_2, +}; + +/** + * enum osd_v_exp_ratio + * @V_EXP_OFF: no expansion (1/1) + * @V_EXP_6_OVER_5: 6/5 expansion ratio + * + * Description: + * An enumeration of the available vertical expansion ratios. + */ +enum osd_v_exp_ratio { + V_EXP_OFF, + V_EXP_6_OVER_5, +}; + +/** + * enum osd_zoom_factor + * @ZOOM_X1: no zoom (x1) + * @ZOOM_X2: x2 zoom + * @ZOOM_X4: x4 zoom + * + * Description: + * An enumeration of the available zoom factors. + */ +enum osd_zoom_factor { + ZOOM_X1, + ZOOM_X2, + ZOOM_X4, +}; + +/** + * enum osd_clut + * @ROM_CLUT: ROM CLUT + * @RAM_CLUT: RAM CLUT + * + * Description: + * An enumeration of the available Color Lookup Tables (CLUTs). + */ +enum osd_clut { + ROM_CLUT, + RAM_CLUT, +}; + +/** + * enum osd_rom_clut + * @ROM_CLUT0: Macintosh CLUT + * @ROM_CLUT1: CLUT from DM270 and prior devices + * + * Description: + * An enumeration of the ROM Color Lookup Table (CLUT) options. + */ +enum osd_rom_clut { + ROM_CLUT0, + ROM_CLUT1, +}; + +/** + * enum osd_blending_factor + * @OSD_0_VID_8: OSD pixels are fully transparent + * @OSD_1_VID_7: OSD pixels contribute 1/8, video pixels contribute 7/8 + * @OSD_2_VID_6: OSD pixels contribute 2/8, video pixels contribute 6/8 + * @OSD_3_VID_5: OSD pixels contribute 3/8, video pixels contribute 5/8 + * @OSD_4_VID_4: OSD pixels contribute 4/8, video pixels contribute 4/8 + * @OSD_5_VID_3: OSD pixels contribute 5/8, video pixels contribute 3/8 + * @OSD_6_VID_2: OSD pixels contribute 6/8, video pixels contribute 2/8 + * @OSD_8_VID_0: OSD pixels are fully opaque + * + * Description: + * An enumeration of the DaVinci pixel blending factor options. + */ +enum osd_blending_factor { + OSD_0_VID_8, + OSD_1_VID_7, + OSD_2_VID_6, + OSD_3_VID_5, + OSD_4_VID_4, + OSD_5_VID_3, + OSD_6_VID_2, + OSD_8_VID_0, +}; + +/** + * enum osd_blink_interval + * @BLINK_X1: blink interval is 1 vertical refresh cycle + * @BLINK_X2: blink interval is 2 vertical refresh cycles + * @BLINK_X3: blink interval is 3 vertical refresh cycles + * @BLINK_X4: blink interval is 4 vertical refresh cycles + * + * Description: + * An enumeration of the DaVinci pixel blinking interval options. + */ +enum osd_blink_interval { + BLINK_X1, + BLINK_X2, + BLINK_X3, + BLINK_X4, +}; + +/** + * enum osd_cursor_h_width + * @H_WIDTH_1: horizontal line width is 1 pixel + * @H_WIDTH_4: horizontal line width is 4 pixels + * @H_WIDTH_8: horizontal line width is 8 pixels + * @H_WIDTH_12: horizontal line width is 12 pixels + * @H_WIDTH_16: horizontal line width is 16 pixels + * @H_WIDTH_20: horizontal line width is 20 pixels + * @H_WIDTH_24: horizontal line width is 24 pixels + * @H_WIDTH_28: horizontal line width is 28 pixels + */ +enum osd_cursor_h_width { + H_WIDTH_1, + H_WIDTH_4, + H_WIDTH_8, + H_WIDTH_12, + H_WIDTH_16, + H_WIDTH_20, + H_WIDTH_24, + H_WIDTH_28, +}; + +/** + * enum davinci_cursor_v_width + * @V_WIDTH_1: vertical line width is 1 line + * @V_WIDTH_2: vertical line width is 2 lines + * @V_WIDTH_4: vertical line width is 4 lines + * @V_WIDTH_6: vertical line width is 6 lines + * @V_WIDTH_8: vertical line width is 8 lines + * @V_WIDTH_10: vertical line width is 10 lines + * @V_WIDTH_12: vertical line width is 12 lines + * @V_WIDTH_14: vertical line width is 14 lines + */ +enum osd_cursor_v_width { + V_WIDTH_1, + V_WIDTH_2, + V_WIDTH_4, + V_WIDTH_6, + V_WIDTH_8, + V_WIDTH_10, + V_WIDTH_12, + V_WIDTH_14, +}; + +/** + * struct osd_cursor_config + * @xsize: horizontal size in pixels + * @ysize: vertical size in lines + * @xpos: horizontal offset in pixels from the left edge of the display + * @ypos: vertical offset in lines from the top of the display + * @interlaced: Non-zero if the display is interlaced, or zero otherwise + * @h_width: horizontal line width + * @v_width: vertical line width + * @clut: the CLUT selector (ROM or RAM) for the cursor color + * @clut_index: an index into the CLUT for the cursor color + * + * Description: + * A structure describing the configuration parameters of the hardware + * rectangular cursor. + */ +struct osd_cursor_config { + unsigned xsize; + unsigned ysize; + unsigned xpos; + unsigned ypos; + int interlaced; + enum osd_cursor_h_width h_width; + enum osd_cursor_v_width v_width; + enum osd_clut clut; + unsigned char clut_index; +}; + +/** + * struct osd_layer_config + * @pixfmt: pixel format + * @line_length: offset in bytes between start of each line in memory + * @xsize: number of horizontal pixels displayed per line + * @ysize: number of lines displayed + * @xpos: horizontal offset in pixels from the left edge of the display + * @ypos: vertical offset in lines from the top of the display + * @interlaced: Non-zero if the display is interlaced, or zero otherwise + * + * Description: + * A structure describing the configuration parameters of an On-Screen Display + * (OSD) or video layer related to how the image is stored in memory. + * @line_length must be a multiple of the cache line size (32 bytes). + */ +struct osd_layer_config { + enum osd_pix_format pixfmt; + unsigned line_length; + unsigned xsize; + unsigned ysize; + unsigned xpos; + unsigned ypos; + int interlaced; +}; + +/* OSD events. Should match with that in vpbe_venc.h */ +#define OSD_END_OF_FRAME BIT(0) +#define OSD_FIRST_FIELD BIT(1) +#define OSD_SECOND_FIELD BIT(2) + +/* parameters that apply on a per-window (OSD or video) basis */ +struct osd_window_state { + int is_allocated; + int is_enabled; + unsigned long fb_base_phys; + enum osd_zoom_factor h_zoom; + enum osd_zoom_factor v_zoom; + struct osd_layer_config lconfig; +}; + +/* parameters that apply on a per-OSD-window basis */ +struct osd_osdwin_state { + enum osd_clut clut; + enum osd_blending_factor blend; + int colorkey_blending; + unsigned colorkey; + int rec601_attenuation; + /* index is pixel value */ + unsigned char palette_map[16]; +}; + +/* hardware rectangular cursor parameters */ +struct osd_cursor_state { + int is_enabled; + struct osd_cursor_config config; +}; + +struct osd_state; + +struct vpbe_osd_ops { + int (*initialize)(struct osd_state *sd); + int (*request_layer)(struct osd_state *sd, enum osd_layer layer); + void (*release_layer)(struct osd_state *sd, enum osd_layer layer); + int (*enable_layer)(struct osd_state *sd, enum osd_layer layer, + int otherwin); + void (*disable_layer)(struct osd_state *sd, enum osd_layer layer); + int (*set_layer_config)(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig); + void (*get_layer_config)(struct osd_state *sd, enum osd_layer layer, + struct osd_layer_config *lconfig); + void (*start_layer)(struct osd_state *sd, enum osd_layer layer, + unsigned long fb_base_phys, + unsigned long cbcr_ofst); + void (*set_left_margin)(struct osd_state *sd, u32 val); + void (*set_top_margin)(struct osd_state *sd, u32 val); + void (*set_interpolation_filter)(struct osd_state *sd, int filter); + int (*set_vid_expansion)(struct osd_state *sd, + enum osd_h_exp_ratio h_exp, + enum osd_v_exp_ratio v_exp); + void (*get_vid_expansion)(struct osd_state *sd, + enum osd_h_exp_ratio *h_exp, + enum osd_v_exp_ratio *v_exp); + void (*set_zoom)(struct osd_state *sd, enum osd_layer layer, + enum osd_zoom_factor h_zoom, + enum osd_zoom_factor v_zoom); +}; + +struct osd_state { + enum vpbe_types vpbe_type; + spinlock_t lock; + struct device *dev; + dma_addr_t osd_base_phys; + unsigned long osd_base; + unsigned long osd_size; + /* 1-->the isr will toggle the VID0 ping-pong buffer */ + int pingpong; + int interpolation_filter; + int field_inversion; + enum osd_h_exp_ratio osd_h_exp; + enum osd_v_exp_ratio osd_v_exp; + enum osd_h_exp_ratio vid_h_exp; + enum osd_v_exp_ratio vid_v_exp; + enum osd_clut backg_clut; + unsigned backg_clut_index; + enum osd_rom_clut rom_clut; + int is_blinking; + /* attribute window blinking enabled */ + enum osd_blink_interval blink; + /* YCbCrI or YCrCbI */ + enum osd_pix_format yc_pixfmt; + /* columns are Y, Cb, Cr */ + unsigned char clut_ram[256][3]; + struct osd_cursor_state cursor; + /* OSD0, VID0, OSD1, VID1 */ + struct osd_window_state win[4]; + /* OSD0, OSD1 */ + struct osd_osdwin_state osdwin[2]; + /* OSD device Operations */ + struct vpbe_osd_ops ops; +}; + +struct osd_platform_data { + enum vpbe_types vpbe_type; + int field_inv_wa_enable; +}; + +#endif -- 1.6.2.4 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From cyril at ti.com Fri Jul 8 15:14:17 2011 From: cyril at ti.com (Cyril Chemparathy) Date: Fri, 08 Jul 2011 20:14:17 -0000 Subject: [PATCH v7 04/12] spi: add ti-ssp spi master driver In-Reply-To: <1291733522-3626-1-git-send-email-cyril@ti.com> References: <1291733522-3626-1-git-send-email-cyril@ti.com> Message-ID: <1291733522-3626-5-git-send-email-cyril@ti.com> This patch adds an SPI master implementation that operates on top of an underlying TI-SSP port. Signed-off-by: Cyril Chemparathy --- drivers/spi/Kconfig | 10 + drivers/spi/Makefile | 1 + drivers/spi/ti-ssp-spi.c | 402 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/ti_ssp.h | 6 + 4 files changed, 419 insertions(+), 0 deletions(-) create mode 100644 drivers/spi/ti-ssp-spi.c diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 78f9fd0..7f0ed2a 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -336,6 +336,16 @@ config SPI_TEGRA help SPI driver for NVidia Tegra SoCs +config SPI_TI_SSP + tristate "TI Sequencer Serial Port - SPI Support" + depends on MFD_TI_SSP + help + This selects an SPI master implementation using a TI sequencer + serial port. + + To compile this driver as a module, choose M here: the + module will be called ti-ssp-spi. + config SPI_TOPCLIFF_PCH tristate "Topcliff PCH SPI Controller" depends on PCI diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 8bc1a5a..595e5b8 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx_hw.o obj-$(CONFIG_SPI_S3C64XX) += spi_s3c64xx.o obj-$(CONFIG_SPI_TEGRA) += spi_tegra.o +obj-$(CONFIG_SPI_TI_SSP) += ti-ssp-spi.o obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi_topcliff_pch.o obj-$(CONFIG_SPI_TXX9) += spi_txx9.o obj-$(CONFIG_SPI_XILINX) += xilinx_spi.o diff --git a/drivers/spi/ti-ssp-spi.c b/drivers/spi/ti-ssp-spi.c new file mode 100644 index 0000000..30fc0e2 --- /dev/null +++ b/drivers/spi/ti-ssp-spi.c @@ -0,0 +1,402 @@ +/* + * Sequencer Serial Port (SSP) based SPI master driver + * + * Copyright (C) 2010 Texas Instruments Inc + * + * 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MODE_BITS (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH) + +struct ti_ssp_spi { + const struct ti_ssp_spi_data *pdata; + struct spi_master *master; + struct device *dev; + spinlock_t lock; + struct list_head msg_queue; + struct completion complete; + int shutdown:1; + struct workqueue_struct *workqueue; + struct work_struct work; + u8 mode, bpw; + int cs_active; + u32 pc_en, pc_dis, pc_wr, pc_rd; +}; + +static u32 do_read_data(struct ti_ssp_spi *hw) +{ + u32 ret; + + ti_ssp_run(hw->dev, hw->pc_rd, 0, &ret); + return ret; +} + +static void do_write_data(struct ti_ssp_spi *hw, u32 data) +{ + ti_ssp_run(hw->dev, hw->pc_wr, data << (32 - hw->bpw), NULL); +} + +static int do_transfer(struct ti_ssp_spi *hw, struct spi_message *msg, + struct spi_transfer *t) +{ + int count; + + if (hw->bpw <= 8) { + u8 *rx = t->rx_buf; + const u8 *tx = t->tx_buf; + + for (count = 0; count < t->len; count += 1) { + if (t->tx_buf) + do_write_data(hw, *tx++); + if (t->rx_buf) + *rx++ = do_read_data(hw); + } + } else if (hw->bpw <= 16) { + u16 *rx = t->rx_buf; + const u16 *tx = t->tx_buf; + + for (count = 0; count < t->len; count += 2) { + if (t->tx_buf) + do_write_data(hw, *tx++); + if (t->rx_buf) + *rx++ = do_read_data(hw); + } + } else { + u32 *rx = t->rx_buf; + const u32 *tx = t->tx_buf; + + for (count = 0; count < t->len; count += 4) { + if (t->tx_buf) + do_write_data(hw, *tx++); + if (t->rx_buf) + *rx++ = do_read_data(hw); + } + } + + msg->actual_length += count; /* bytes transferred */ + + dev_dbg(&msg->spi->dev, "xfer %s%s, %d bytes, %d bpw, count %d%s\n", + t->tx_buf ? "tx" : "", t->rx_buf ? "rx" : "", t->len, + hw->bpw, count, (count < t->len) ? " (under)" : ""); + + return (count < t->len) ? -EIO : 0; /* left over data */ +} + +static void chip_select(struct ti_ssp_spi *hw, int cs_active) +{ + cs_active = !!cs_active; + if (cs_active == hw->cs_active) + return; + ti_ssp_run(hw->dev, cs_active ? hw->pc_en : hw->pc_dis, 0, NULL); + hw->cs_active = cs_active; +} + +#define __SHIFT_OUT(bits) (SSP_OPCODE_SHIFT | SSP_OUT_MODE | \ + cs_en | clk | SSP_COUNT((bits) * 2 - 1)) +#define __SHIFT_IN(bits) (SSP_OPCODE_SHIFT | SSP_IN_MODE | \ + cs_en | clk | SSP_COUNT((bits) * 2 - 1)) + +static int setup_xfer(struct ti_ssp_spi *hw, u8 bpw, u8 mode) +{ + int error, idx = 0; + u32 seqram[16]; + u32 cs_en, cs_dis, clk; + u32 topbits, botbits; + + mode &= MODE_BITS; + if (mode == hw->mode && bpw == hw->bpw) + return 0; + + cs_en = (mode & SPI_CS_HIGH) ? SSP_CS_HIGH : SSP_CS_LOW; + cs_dis = (mode & SPI_CS_HIGH) ? SSP_CS_LOW : SSP_CS_HIGH; + clk = (mode & SPI_CPOL) ? SSP_CLK_HIGH : SSP_CLK_LOW; + + /* Construct instructions */ + + /* Disable Chip Select */ + hw->pc_dis = idx; + seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_dis | clk; + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_dis | clk; + + /* Enable Chip Select */ + hw->pc_en = idx; + seqram[idx++] = SSP_OPCODE_DIRECT | SSP_OUT_MODE | cs_en | clk; + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; + + /* Reads and writes need to be split for bpw > 16 */ + topbits = (bpw > 16) ? 16 : bpw; + botbits = bpw - topbits; + + /* Write */ + hw->pc_wr = idx; + seqram[idx++] = __SHIFT_OUT(topbits) | SSP_ADDR_REG; + if (botbits) + seqram[idx++] = __SHIFT_OUT(botbits) | SSP_DATA_REG; + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; + + /* Read */ + hw->pc_rd = idx; + if (botbits) + seqram[idx++] = __SHIFT_IN(botbits) | SSP_ADDR_REG; + seqram[idx++] = __SHIFT_IN(topbits) | SSP_DATA_REG; + seqram[idx++] = SSP_OPCODE_STOP | SSP_OUT_MODE | cs_en | clk; + + error = ti_ssp_load(hw->dev, 0, seqram, idx); + if (error < 0) + return error; + + error = ti_ssp_set_mode(hw->dev, ((mode & SPI_CPHA) ? + 0 : SSP_EARLY_DIN)); + if (error < 0) + return error; + + hw->bpw = bpw; + hw->mode = mode; + + return error; +} + +static void ti_ssp_spi_work(struct work_struct *work) +{ + struct ti_ssp_spi *hw = container_of(work, struct ti_ssp_spi, work); + + spin_lock(&hw->lock); + + while (!list_empty(&hw->msg_queue)) { + struct spi_message *m; + struct spi_device *spi; + struct spi_transfer *t = NULL; + int status = 0; + + m = container_of(hw->msg_queue.next, struct spi_message, + queue); + + list_del_init(&m->queue); + + spin_unlock(&hw->lock); + + spi = m->spi; + + if (hw->pdata->select) + hw->pdata->select(spi->chip_select); + + list_for_each_entry(t, &m->transfers, transfer_list) { + int bpw = spi->bits_per_word; + int xfer_status; + + if (t->bits_per_word) + bpw = t->bits_per_word; + + if (setup_xfer(hw, bpw, spi->mode) < 0) + break; + + chip_select(hw, 1); + + xfer_status = do_transfer(hw, m, t); + if (xfer_status < 0) + status = xfer_status; + + if (t->delay_usecs) + udelay(t->delay_usecs); + + if (t->cs_change) + chip_select(hw, 0); + } + + chip_select(hw, 0); + m->status = status; + m->complete(m->context); + + spin_lock(&hw->lock); + } + + if (hw->shutdown) + complete(&hw->complete); + + spin_unlock(&hw->lock); +} + +static int ti_ssp_spi_setup(struct spi_device *spi) +{ + if (spi->bits_per_word > 32) + return -EINVAL; + + return 0; +} + +static int ti_ssp_spi_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct ti_ssp_spi *hw; + struct spi_transfer *t; + int error = 0; + + m->actual_length = 0; + m->status = -EINPROGRESS; + + hw = spi_master_get_devdata(spi->master); + + if (list_empty(&m->transfers) || !m->complete) + return -EINVAL; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->len && !(t->rx_buf || t->tx_buf)) { + dev_err(&spi->dev, "invalid xfer, no buffer\n"); + return -EINVAL; + } + + if (t->len && t->rx_buf && t->tx_buf) { + dev_err(&spi->dev, "invalid xfer, full duplex\n"); + return -EINVAL; + } + + if (t->bits_per_word > 32) { + dev_err(&spi->dev, "invalid xfer width %d\n", + t->bits_per_word); + return -EINVAL; + } + } + + spin_lock(&hw->lock); + if (hw->shutdown) { + error = -ESHUTDOWN; + goto error_unlock; + } + list_add_tail(&m->queue, &hw->msg_queue); + queue_work(hw->workqueue, &hw->work); +error_unlock: + spin_unlock(&hw->lock); + return error; +} + +static int __devinit ti_ssp_spi_probe(struct platform_device *pdev) +{ + const struct ti_ssp_spi_data *pdata; + struct ti_ssp_spi *hw; + struct spi_master *master; + struct device *dev = &pdev->dev; + int error = 0; + + pdata = dev->platform_data; + if (!pdata) { + dev_err(dev, "platform data not found\n"); + return -EINVAL; + } + + master = spi_alloc_master(dev, sizeof(struct ti_ssp_spi)); + if (!master) { + dev_err(dev, "cannot allocate SPI master\n"); + return -ENOMEM; + } + + hw = spi_master_get_devdata(master); + platform_set_drvdata(pdev, hw); + + hw->master = master; + hw->dev = dev; + hw->pdata = pdata; + + spin_lock_init(&hw->lock); + init_completion(&hw->complete); + INIT_LIST_HEAD(&hw->msg_queue); + INIT_WORK(&hw->work, ti_ssp_spi_work); + + hw->workqueue = create_singlethread_workqueue(dev_name(dev)); + if (!hw->workqueue) { + error = -ENOMEM; + dev_err(dev, "work queue creation failed\n"); + goto error_wq; + } + + error = ti_ssp_set_iosel(hw->dev, hw->pdata->iosel); + if (error < 0) { + dev_err(dev, "io setup failed\n"); + goto error_iosel; + } + + master->bus_num = pdev->id; + master->num_chipselect = hw->pdata->num_cs; + master->mode_bits = MODE_BITS; + master->flags = SPI_MASTER_HALF_DUPLEX; + master->setup = ti_ssp_spi_setup; + master->transfer = ti_ssp_spi_transfer; + + error = spi_register_master(master); + if (error) { + dev_err(dev, "master registration failed\n"); + goto error_reg; + } + + return 0; + +error_reg: +error_iosel: + destroy_workqueue(hw->workqueue); +error_wq: + spi_master_put(master); + return error; +} + +static int __devexit ti_ssp_spi_remove(struct platform_device *pdev) +{ + struct ti_ssp_spi *hw = platform_get_drvdata(pdev); + int error; + + hw->shutdown = 1; + while (!list_empty(&hw->msg_queue)) { + error = wait_for_completion_interruptible(&hw->complete); + if (error < 0) { + hw->shutdown = 0; + return error; + } + } + destroy_workqueue(hw->workqueue); + spi_unregister_master(hw->master); + + return 0; +} + +static struct platform_driver ti_ssp_spi_driver = { + .probe = ti_ssp_spi_probe, + .remove = __devexit_p(ti_ssp_spi_remove), + .driver = { + .name = "ti-ssp-spi", + .owner = THIS_MODULE, + }, +}; + +static int __init ti_ssp_spi_init(void) +{ + return platform_driver_register(&ti_ssp_spi_driver); +} +module_init(ti_ssp_spi_init); + +static void __exit ti_ssp_spi_exit(void) +{ + platform_driver_unregister(&ti_ssp_spi_driver); +} +module_exit(ti_ssp_spi_exit); + +MODULE_DESCRIPTION("SSP SPI Master"); +MODULE_AUTHOR("Cyril Chemparathy"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ti-ssp-spi"); diff --git a/include/linux/mfd/ti_ssp.h b/include/linux/mfd/ti_ssp.h index 021fe09..dbb4b43 100644 --- a/include/linux/mfd/ti_ssp.h +++ b/include/linux/mfd/ti_ssp.h @@ -32,6 +32,12 @@ struct ti_ssp_data { struct ti_ssp_dev_data dev_data[2]; }; +struct ti_ssp_spi_data { + unsigned long iosel; + int num_cs; + void (*select)(int cs); +}; + /* * Sequencer port IO pin configuration bits. These do not correlate 1-1 with * the hardware. The iosel field in the port data combines iosel1 and iosel2, -- 1.7.1 _______________________________________________ linux-arm-kernel mailing list linux-arm-kernel at lists.infradead.org http://lists.infradead.org/mailman/listinfo/linux-arm-kernel From ido at wizery.com Sun Jul 10 08:14:33 2011 From: ido at wizery.com (Ido Yariv) Date: Sun, 10 Jul 2011 16:14:33 +0300 Subject: [PATCH v2 0/6] arm: davinci: DA850: wl12xx expansion card Message-ID: <1310303679-17936-1-git-send-email-ido@wizery.com> The following series adds support for an optional wl12xx based expansion card for the AM18x EVM: http://processors.wiki.ti.com/index.php/AM18x_%2B_WL1271 The first couple of patches address issues which had to be fixed for this series to work. However, these should be fixed regardless of the wl12xx daughter board support. Only the Wi-Fi functionality of the expansion card is supported. Bluetooth functionality is not yet supported. The patches are based on v3.0-rc6. Changes from v1: * Explicitly set the default queue for all channel controllers on all platforms. As it makes little sense to set DA850's default queue to an invalid value and then fix it, merge the two patches * Move pinmux configurations to separate patches * Remove FREF Kconfig entry, use a default value of 38MHz instead * Use msleep for long delays, usleep_range for shorter ones * Minor cleanups Ido. Ido Yariv (6): arm: davinci: Fix low level gpio irq handlers' argument arm: davinci: Explicitly set channel controllers' default queues arm: davinci: mmc: Add support for set_power callback arm: davinci: DA850: Add MMC/SD1 pinmux configuration arm: davinci: DA850: Add GPIO pinmux configuration for wl1271 arm: davinci: DA850: Add wl1271/wlan support arch/arm/mach-davinci/Kconfig | 10 +++ arch/arm/mach-davinci/board-da850-evm.c | 127 +++++++++++++++++++++++++++++ arch/arm/mach-davinci/da850.c | 9 ++ arch/arm/mach-davinci/devices-da8xx.c | 3 + arch/arm/mach-davinci/devices-tnetv107x.c | 1 + arch/arm/mach-davinci/dm355.c | 1 + arch/arm/mach-davinci/dm644x.c | 1 + arch/arm/mach-davinci/dm646x.c | 1 + arch/arm/mach-davinci/dma.c | 2 - arch/arm/mach-davinci/gpio.c | 32 +++++++- arch/arm/mach-davinci/include/mach/mmc.h | 3 + arch/arm/mach-davinci/include/mach/mux.h | 10 +++ drivers/mmc/host/davinci_mmc.c | 13 +++ 13 files changed, 207 insertions(+), 6 deletions(-) -- 1.7.4.1 From ido at wizery.com Sun Jul 10 08:14:34 2011 From: ido at wizery.com (Ido Yariv) Date: Sun, 10 Jul 2011 16:14:34 +0300 Subject: [PATCH v2 1/6] arm: davinci: Fix low level gpio irq handlers' argument In-Reply-To: <1310303679-17936-1-git-send-email-ido@wizery.com> References: <1310303679-17936-1-git-send-email-ido@wizery.com> Message-ID: <1310303679-17936-2-git-send-email-ido@wizery.com> Commit 7416401 ("arm: davinci: Fix fallout from generic irq chip conversion") introduced a bug, causing low level interrupt handlers to get a bogus irq number as an argument. The gpio irq handler falsely assumes that the handler data is the irq base number and that is no longer true. Fix this by converting gpio_irq_handler's bank_irq argument to the corresponding irq base number. Signed-off-by: Ido Yariv CC: Thomas Gleixner --- arch/arm/mach-davinci/gpio.c | 32 ++++++++++++++++++++++++++++---- 1 files changed, 28 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-davinci/gpio.c b/arch/arm/mach-davinci/gpio.c index e722139..ff43e2a 100644 --- a/arch/arm/mach-davinci/gpio.c +++ b/arch/arm/mach-davinci/gpio.c @@ -249,16 +249,40 @@ static struct irq_chip gpio_irqchip = { .flags = IRQCHIP_SET_TYPE_MASKED, }; +static inline int bankirq_to_irqbase(unsigned int bank_irq) +{ + int gpio; + int index; + + /* Each irq bank consists of up to 16 irqs */ + gpio = 16 * (bank_irq - davinci_soc_info.gpio_irq); + + /* Each controller controls 32 GPIOs */ + index = gpio / 32; + + if (unlikely(!davinci_soc_info.gpio_ctlrs)) + return -EINVAL; + + if (unlikely(index >= davinci_soc_info.gpio_ctlrs_num)) + return -EINVAL; + + return davinci_soc_info.gpio_ctlrs[index].irq_base; +} + static void -gpio_irq_handler(unsigned irq, struct irq_desc *desc) +gpio_irq_handler(unsigned bank_irq, struct irq_desc *desc) { struct davinci_gpio_regs __iomem *g; u32 mask = 0xffff; + int irqbase = bankirq_to_irqbase(bank_irq); + + if (unlikely(irqbase < 0)) + return; g = (__force struct davinci_gpio_regs __iomem *) irq_desc_get_handler_data(desc); /* we only care about one bank */ - if (irq & 1) + if (bank_irq & 1) mask <<= 16; /* temporarily mask (level sensitive) parent IRQ */ @@ -274,11 +298,11 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc) if (!status) break; __raw_writel(status, &g->intstat); - if (irq & 1) + if (bank_irq & 1) status >>= 16; /* now demux them to the right lowlevel handler */ - n = (int)irq_get_handler_data(irq); + n = irqbase; while (status) { res = ffs(status); n += res; -- 1.7.4.1 From ido at wizery.com Sun Jul 10 08:14:35 2011 From: ido at wizery.com (Ido Yariv) Date: Sun, 10 Jul 2011 16:14:35 +0300 Subject: [PATCH v2 2/6] arm: davinci: Explicitly set channel controllers' default queues In-Reply-To: <1310303679-17936-1-git-send-email-ido@wizery.com> References: <1310303679-17936-1-git-send-email-ido@wizery.com> Message-ID: <1310303679-17936-3-git-send-email-ido@wizery.com> Davinci platforms may define a default queue for each channel controller. If one is not defined, the default queue is set to EVENTQ_1. However, there's no way to distinguish between an unset default queue to one that is set to EVENTQ_0, as EVENTQ_0 = 0. Explicitly specify the default queue for all channel controllers on all Davinci platforms to EVENTQ_1, and don't overwrite it in the EDMA probe function. One exception is the DA850 board, for which EVENTQ_1 is not a valid option for its second channel controller. Use EVENTQ_0 instead for that channel controller. Signed-off-by: Ido Yariv --- arch/arm/mach-davinci/devices-da8xx.c | 3 +++ arch/arm/mach-davinci/devices-tnetv107x.c | 1 + arch/arm/mach-davinci/dm355.c | 1 + arch/arm/mach-davinci/dm644x.c | 1 + arch/arm/mach-davinci/dm646x.c | 1 + arch/arm/mach-davinci/dma.c | 2 -- 6 files changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index fc4e98e..502222a 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -133,6 +133,7 @@ static struct edma_soc_info da830_edma_cc0_info = { .n_cc = 1, .queue_tc_mapping = da8xx_queue_tc_mapping, .queue_priority_mapping = da8xx_queue_priority_mapping, + .default_queue = EVENTQ_1, }; static struct edma_soc_info *da830_edma_info[EDMA_MAX_CC] = { @@ -148,6 +149,7 @@ static struct edma_soc_info da850_edma_cc_info[] = { .n_cc = 1, .queue_tc_mapping = da8xx_queue_tc_mapping, .queue_priority_mapping = da8xx_queue_priority_mapping, + .default_queue = EVENTQ_1, }, { .n_channel = 32, @@ -157,6 +159,7 @@ static struct edma_soc_info da850_edma_cc_info[] = { .n_cc = 1, .queue_tc_mapping = da850_queue_tc_mapping, .queue_priority_mapping = da850_queue_priority_mapping, + .default_queue = EVENTQ_0, }, }; diff --git a/arch/arm/mach-davinci/devices-tnetv107x.c b/arch/arm/mach-davinci/devices-tnetv107x.c index 6162cae..6298d46 100644 --- a/arch/arm/mach-davinci/devices-tnetv107x.c +++ b/arch/arm/mach-davinci/devices-tnetv107x.c @@ -80,6 +80,7 @@ static struct edma_soc_info edma_cc0_info = { .n_cc = 1, .queue_tc_mapping = edma_tc_mapping, .queue_priority_mapping = edma_priority_mapping, + .default_queue = EVENTQ_1; }; static struct edma_soc_info *tnetv107x_edma_info[EDMA_MAX_CC] = { diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index a3a94e9..f1d9c75 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -591,6 +591,7 @@ static struct edma_soc_info edma_cc0_info = { .n_cc = 1, .queue_tc_mapping = queue_tc_mapping, .queue_priority_mapping = queue_priority_mapping, + .default_queue = EVENTQ_1; }; static struct edma_soc_info *dm355_edma_info[EDMA_MAX_CC] = { diff --git a/arch/arm/mach-davinci/dm644x.c b/arch/arm/mach-davinci/dm644x.c index 4c82c27..9971990 100644 --- a/arch/arm/mach-davinci/dm644x.c +++ b/arch/arm/mach-davinci/dm644x.c @@ -514,6 +514,7 @@ static struct edma_soc_info edma_cc0_info = { .n_cc = 1, .queue_tc_mapping = queue_tc_mapping, .queue_priority_mapping = queue_priority_mapping, + .default_queue = EVENTQ_1; }; static struct edma_soc_info *dm644x_edma_info[EDMA_MAX_CC] = { diff --git a/arch/arm/mach-davinci/dm646x.c b/arch/arm/mach-davinci/dm646x.c index 1e0f809..a40372d 100644 --- a/arch/arm/mach-davinci/dm646x.c +++ b/arch/arm/mach-davinci/dm646x.c @@ -551,6 +551,7 @@ static struct edma_soc_info edma_cc0_info = { .n_cc = 1, .queue_tc_mapping = dm646x_queue_tc_mapping, .queue_priority_mapping = dm646x_queue_priority_mapping, + .default_queue = EVENTQ_1; }; static struct edma_soc_info *dm646x_edma_info[EDMA_MAX_CC] = { diff --git a/arch/arm/mach-davinci/dma.c b/arch/arm/mach-davinci/dma.c index 6b96698..6ba4191 100644 --- a/arch/arm/mach-davinci/dma.c +++ b/arch/arm/mach-davinci/dma.c @@ -1450,8 +1450,6 @@ static int __init edma_probe(struct platform_device *pdev) EDMA_MAX_CC); edma_cc[j]->default_queue = info[j]->default_queue; - if (!edma_cc[j]->default_queue) - edma_cc[j]->default_queue = EVENTQ_1; dev_dbg(&pdev->dev, "DMA REG BASE ADDR=%p\n", edmacc_regs_base[j]); -- 1.7.4.1 From ido at wizery.com Sun Jul 10 08:14:36 2011 From: ido at wizery.com (Ido Yariv) Date: Sun, 10 Jul 2011 16:14:36 +0300 Subject: [PATCH v2 3/6] arm: davinci: mmc: Add support for set_power callback In-Reply-To: <1310303679-17936-1-git-send-email-ido@wizery.com> References: <1310303679-17936-1-git-send-email-ido@wizery.com> Message-ID: <1310303679-17936-4-git-send-email-ido@wizery.com> Some devices connected to the MMC bus are power controlled by external means. For instance, an SDIO device may be powered down/up by an external gpio line. In order to avoid toggling power from within the MMC host driver, add a set_power callback function, which will be called by set_ios upon powering down/up. Signed-off-by: Ido Yariv --- arch/arm/mach-davinci/include/mach/mmc.h | 3 +++ drivers/mmc/host/davinci_mmc.c | 13 +++++++++++++ 2 files changed, 16 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/mmc.h b/arch/arm/mach-davinci/include/mach/mmc.h index d4f1e96..5ba6b22 100644 --- a/arch/arm/mach-davinci/include/mach/mmc.h +++ b/arch/arm/mach-davinci/include/mach/mmc.h @@ -12,6 +12,9 @@ struct davinci_mmc_config { /* get_cd()/get_wp() may sleep */ int (*get_cd)(int module); int (*get_ro)(int module); + + void (*set_power)(int module, bool on); + /* wires == 0 is equivalent to wires == 4 (4-bit parallel) */ u8 wires; diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 0076c74..64a8325 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -807,12 +807,25 @@ static void calculate_clk_divider(struct mmc_host *mmc, struct mmc_ios *ios) static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmc_davinci_host *host = mmc_priv(mmc); + struct platform_device *pdev = to_platform_device(mmc->parent); + struct davinci_mmc_config *config = pdev->dev.platform_data; dev_dbg(mmc_dev(host->mmc), "clock %dHz busmode %d powermode %d Vdd %04x\n", ios->clock, ios->bus_mode, ios->power_mode, ios->vdd); + switch (ios->power_mode) { + case MMC_POWER_OFF: + if (config && config->set_power) + config->set_power(pdev->id, false); + break; + case MMC_POWER_UP: + if (config && config->set_power) + config->set_power(pdev->id, true); + break; + } + switch (ios->bus_width) { case MMC_BUS_WIDTH_8: dev_dbg(mmc_dev(host->mmc), "Enabling 8 bit mode\n"); -- 1.7.4.1 From ido at wizery.com Sun Jul 10 08:14:37 2011 From: ido at wizery.com (Ido Yariv) Date: Sun, 10 Jul 2011 16:14:37 +0300 Subject: [PATCH v2 4/6] arm: davinci: DA850: Add MMC/SD1 pinmux configuration In-Reply-To: <1310303679-17936-1-git-send-email-ido@wizery.com> References: <1310303679-17936-1-git-send-email-ido@wizery.com> Message-ID: <1310303679-17936-5-git-send-email-ido@wizery.com> AM18x has two MMC slots. Add the required pinmux configuration for the second slot. Signed-off-by: Ido Yariv --- arch/arm/mach-davinci/da850.c | 7 +++++++ arch/arm/mach-davinci/include/mach/mux.h | 8 ++++++++ 2 files changed, 15 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index 133aac4..f6eafb4 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -525,6 +525,13 @@ static const struct mux_config da850_pins[] = { MUX_CFG(DA850, MMCSD0_DAT_3, 10, 20, 15, 2, false) MUX_CFG(DA850, MMCSD0_CLK, 10, 0, 15, 2, false) MUX_CFG(DA850, MMCSD0_CMD, 10, 4, 15, 2, false) + /* MMC/SD1 function */ + MUX_CFG(DA850, MMCSD1_DAT_0, 18, 8, 15, 2, false) + MUX_CFG(DA850, MMCSD1_DAT_1, 19, 16, 15, 2, false) + MUX_CFG(DA850, MMCSD1_DAT_2, 19, 12, 15, 2, false) + MUX_CFG(DA850, MMCSD1_DAT_3, 19, 8, 15, 2, false) + MUX_CFG(DA850, MMCSD1_CLK, 18, 12, 15, 2, false) + MUX_CFG(DA850, MMCSD1_CMD, 18, 16, 15, 2, false) /* EMIF2.5/EMIFA function */ MUX_CFG(DA850, EMA_D_7, 9, 0, 15, 1, false) MUX_CFG(DA850, EMA_D_6, 9, 4, 15, 1, false) diff --git a/arch/arm/mach-davinci/include/mach/mux.h b/arch/arm/mach-davinci/include/mach/mux.h index 5d4e0fe..7fb7f1e 100644 --- a/arch/arm/mach-davinci/include/mach/mux.h +++ b/arch/arm/mach-davinci/include/mach/mux.h @@ -857,6 +857,14 @@ enum davinci_da850_index { DA850_MMCSD0_CLK, DA850_MMCSD0_CMD, + /* MMC/SD1 function */ + DA850_MMCSD1_DAT_0, + DA850_MMCSD1_DAT_1, + DA850_MMCSD1_DAT_2, + DA850_MMCSD1_DAT_3, + DA850_MMCSD1_CLK, + DA850_MMCSD1_CMD, + /* EMIF2.5/EMIFA function */ DA850_EMA_D_7, DA850_EMA_D_6, -- 1.7.4.1 From ido at wizery.com Sun Jul 10 08:14:38 2011 From: ido at wizery.com (Ido Yariv) Date: Sun, 10 Jul 2011 16:14:38 +0300 Subject: [PATCH v2 5/6] arm: davinci: DA850: Add GPIO pinmux configuration for wl1271 In-Reply-To: <1310303679-17936-1-git-send-email-ido@wizery.com> References: <1310303679-17936-1-git-send-email-ido@wizery.com> Message-ID: <1310303679-17936-6-git-send-email-ido@wizery.com> The wl1271 daughter board makes use of a few GPIOs: GPIO6_9 is used for powering down/up the WLAN functionality. GPIO6_10 is used as an input IRQ line from the WLAN chip. Add the required pinmux configuration for these GPIOs. Signed-off-by: Ido Yariv --- arch/arm/mach-davinci/da850.c | 2 ++ arch/arm/mach-davinci/include/mach/mux.h | 2 ++ 2 files changed, 4 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index f6eafb4..bfe9b71 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -590,6 +590,8 @@ static const struct mux_config da850_pins[] = { MUX_CFG(DA850, GPIO3_13, 7, 8, 15, 8, false) MUX_CFG(DA850, GPIO4_0, 10, 28, 15, 8, false) MUX_CFG(DA850, GPIO4_1, 10, 24, 15, 8, false) + MUX_CFG(DA850, GPIO6_9, 13, 24, 15, 8, false) + MUX_CFG(DA850, GPIO6_10, 13, 20, 15, 8, false) MUX_CFG(DA850, GPIO6_13, 13, 8, 15, 8, false) MUX_CFG(DA850, RTC_ALARM, 0, 28, 15, 2, false) #endif diff --git a/arch/arm/mach-davinci/include/mach/mux.h b/arch/arm/mach-davinci/include/mach/mux.h index 7fb7f1e..a7e92fc 100644 --- a/arch/arm/mach-davinci/include/mach/mux.h +++ b/arch/arm/mach-davinci/include/mach/mux.h @@ -924,6 +924,8 @@ enum davinci_da850_index { DA850_GPIO3_13, DA850_GPIO4_0, DA850_GPIO4_1, + DA850_GPIO6_9, + DA850_GPIO6_10, DA850_GPIO6_13, DA850_RTC_ALARM, }; -- 1.7.4.1 From ido at wizery.com Sun Jul 10 08:14:39 2011 From: ido at wizery.com (Ido Yariv) Date: Sun, 10 Jul 2011 16:14:39 +0300 Subject: [PATCH v2 6/6] arm: davinci: DA850: Add wl1271/wlan support In-Reply-To: <1310303679-17936-1-git-send-email-ido@wizery.com> References: <1310303679-17936-1-git-send-email-ido@wizery.com> Message-ID: <1310303679-17936-7-git-send-email-ido@wizery.com> The wl1271 daughter card for AM18x EVMs is a combo wireless connectivity add-on card, based on the LS Research TiWi module with Texas Instruments' wl1271 solution. It is a 4-wire, 1.8V, embedded SDIO WLAN device with an external IRQ line and is power-controlled by a GPIO-based fixed regulator. This patch adds support for the WLAN capabilities of this expansion board. Signed-off-by: Ido Yariv --- arch/arm/mach-davinci/Kconfig | 10 +++ arch/arm/mach-davinci/board-da850-evm.c | 127 +++++++++++++++++++++++++++++++ 2 files changed, 137 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index c0deaca..32d837d 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -192,6 +192,16 @@ config DA850_UI_RMII endchoice +config DA850_WL12XX + bool "AM18x wl1271 daughter board" + depends on MACH_DAVINCI_DA850_EVM + help + The wl1271 daughter card for AM18x EVMs is a combo wireless + connectivity add-on card, based on the LS Research TiWi module with + Texas Instruments' wl1271 solution. + Say Y if you want to use a wl1271 expansion card connected to the + AM18x EVM. + config GPIO_PCA953X default MACH_DAVINCI_DA850_EVM diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..2dae1a1 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include @@ -49,6 +51,9 @@ #define DA850_MMCSD_CD_PIN GPIO_TO_PIN(4, 0) #define DA850_MMCSD_WP_PIN GPIO_TO_PIN(4, 1) +#define DA850_WLAN_EN GPIO_TO_PIN(6, 9) +#define DA850_WLAN_IRQ GPIO_TO_PIN(6, 10) + #define DA850_MII_MDIO_CLKEN_PIN GPIO_TO_PIN(2, 6) static struct mtd_partition da850evm_spiflash_part[] = { @@ -1117,6 +1122,126 @@ static __init int da850_evm_init_cpufreq(void) static __init int da850_evm_init_cpufreq(void) { return 0; } #endif +#ifdef CONFIG_DA850_WL12XX + +static int da850_wl12xx_fref = WL12XX_REFCLOCK_38; + +static int __init setup_da850_wl12xx_fref(char *fref) +{ + if (!strcmp(fref, "19.2")) + da850_wl12xx_fref = WL12XX_REFCLOCK_19; + else if (!strcmp(fref, "26")) + da850_wl12xx_fref = WL12XX_REFCLOCK_26; + else if (!strcmp(fref, "38.4")) + da850_wl12xx_fref = WL12XX_REFCLOCK_38; + else if (!strcmp(fref, "52")) + da850_wl12xx_fref = WL12XX_REFCLOCK_52; + else if (!strcmp(fref, "XTAL26")) + da850_wl12xx_fref = WL12XX_REFCLOCK_26_XTAL; + else if (!strcmp(fref, "XTAL38.4")) + da850_wl12xx_fref = WL12XX_REFCLOCK_38_XTAL; + else + pr_info("da850_wl12xx_fref is invalid. Valid options: " + "19.2, 26, 38.4, 52, XTAL26 or XTAL38.4\n"); + return 0; +} +__setup("da850_wl12xx_fref=", setup_da850_wl12xx_fref); + +static void wl12xx_set_power(int slot, bool power_on) +{ + static bool power_state; + + pr_debug("Powering %s wl12xx", power_on ? "on" : "off"); + + if (power_on == power_state) + return; + power_state = power_on; + + if (power_on) { + /* Power up sequence required for wl127x devices */ + gpio_set_value(DA850_WLAN_EN, 1); + usleep_range(15000, 15000); + gpio_set_value(DA850_WLAN_EN, 0); + usleep_range(1000, 1000); + gpio_set_value(DA850_WLAN_EN, 1); + msleep(70); + } else { + gpio_set_value(DA850_WLAN_EN, 0); + } +} + +static struct davinci_mmc_config da850_mmc_wl12xx_config = { + .get_ro = NULL, + .get_cd = NULL, + .set_power = wl12xx_set_power, + .wires = 4, + .max_freq = 25000000, + .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NONREMOVABLE | + MMC_CAP_POWER_OFF_CARD, + .version = MMC_CTLR_VERSION_2, +}; + +static const short da850_evm_mmc_wl12xx_pins[] __initconst = { + DA850_MMCSD1_DAT_0, DA850_MMCSD1_DAT_1, DA850_MMCSD1_DAT_2, + DA850_MMCSD1_DAT_3, DA850_MMCSD1_CLK, DA850_MMCSD1_CMD, + DA850_GPIO6_9, DA850_GPIO6_10, + -1 +}; + +static struct wl12xx_platform_data da850_wl12xx_wlan_data __initdata = { + .irq = -1, + .board_ref_clock = -1, + .platform_quirks = WL12XX_PLATFORM_QUIRK_EDGE_IRQ, +}; + +static void da850_wl12xx_init(void) +{ + int ret; + + ret = davinci_cfg_reg_list(da850_evm_mmc_wl12xx_pins); + if (ret) { + pr_warning("da850_evm_init: wl12xx/mmc mux setup failed:" + " %d\n", ret); + return; + } + + ret = da850_register_mmcsd1(&da850_mmc_wl12xx_config); + if (ret) { + pr_warning("da850_evm_init: wl12xx/mmc registration failed:" + " %d\n", ret); + return; + } + + ret = gpio_request_one(DA850_WLAN_EN, GPIOF_OUT_INIT_LOW, "wl12xx_en"); + if (ret) { + pr_err("Error initializing the wl12xx enable gpio: %d\n", ret); + return; + } + + ret = gpio_request_one(DA850_WLAN_IRQ, GPIOF_IN, "wl12xx_irq"); + if (ret) { + pr_err("Error initializing the wl12xx irq gpio: %d\n", ret); + gpio_free(DA850_WLAN_EN); + return; + } + + da850_wl12xx_wlan_data.irq = gpio_to_irq(DA850_WLAN_IRQ); + da850_wl12xx_wlan_data.board_ref_clock = da850_wl12xx_fref; + + ret = wl12xx_set_platform_data(&da850_wl12xx_wlan_data); + if (ret) { + pr_err("Error setting wl12xx data: %d\n", ret); + gpio_free(DA850_WLAN_IRQ); + gpio_free(DA850_WLAN_EN); + } +} + +#else /* CONFIG_DA850_WL12XX */ + +static void da850_wl12xx_init(void) { } + +#endif /* CONFIG_DA850_WL12XX */ + static __init void da850_evm_init(void) { int ret; @@ -1169,6 +1294,8 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: mmcsd0 registration failed:" " %d\n", ret); + + da850_wl12xx_init(); } davinci_serial_init(&da850_evm_uart_config); -- 1.7.4.1 From sundaram at ti.com Sun Jul 10 10:03:23 2011 From: sundaram at ti.com (Sundaram Raju) Date: Sun, 10 Jul 2011 20:33:23 +0530 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: References: Message-ID: <1310310203-12288-1-git-send-email-sundaram@ti.com> Added new dma_ctrl_cmd TI_DMA_STRIDE_CONFIG to pass the TI DMA controller specific configurations on how a buffer must be walked through and how data is picked for transfer based on a specified pattern over the channel. The configuration passed is specific to the TI DMA controller used. Signed-off-by: Sundaram Raju --- include/linux/dmaengine.h | 5 +++++ 1 files changed, 5 insertions(+), 0 deletions(-) diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index eee7add..51dadc4 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -123,6 +123,10 @@ enum dma_ctrl_flags { * command. * @FSLDMA_EXTERNAL_START: this command will put the Freescale DMA controller * into external start mode. + * @TI_DMA_STRIDE_CONFIG: this command is only implemented by TI DMA + * controllers that need to pass special configuration on how to walk through + * the buffer to pick up data in a specified pattern to be transferred in + * the channel. */ enum dma_ctrl_cmd { DMA_TERMINATE_ALL, @@ -130,6 +134,7 @@ enum dma_ctrl_cmd { DMA_RESUME, DMA_SLAVE_CONFIG, FSLDMA_EXTERNAL_START, + TI_DMA_STRIDE_CONFIG, }; /** -- 1.6.2.4 From linus.walleij at linaro.org Mon Jul 11 04:28:24 2011 From: linus.walleij at linaro.org (Linus Walleij) Date: Mon, 11 Jul 2011 11:28:24 +0200 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: <1310310203-12288-1-git-send-email-sundaram@ti.com> References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: 2011/7/10 Sundaram Raju : > Added new dma_ctrl_cmd TI_DMA_STRIDE_CONFIG to pass the TI DMA > controller specific configurations on how a buffer must be walked > through and how data is picked for transfer based on a specified > pattern over the channel. > > The configuration passed is specific to the TI DMA controller used. > > Signed-off-by: Sundaram Raju This is exactly how I think we should do this. Acked-by: Linus Walleij Thanks, Linus Walleij From sudhakar.raj at ti.com Mon Jul 11 04:28:21 2011 From: sudhakar.raj at ti.com (Rajashekhara, Sudhakar) Date: Mon, 11 Jul 2011 14:58:21 +0530 Subject: [PATCH v3] davinci: da850 EVM: read mac address from SPI flash Message-ID: <1310376501-14189-1-git-send-email-sudhakar.raj@ti.com> DA850/OMAP-L138 EMAC driver uses random mac address instead of a fixed one because the mac address is not stuffed into EMAC platform data. This patch provides a function which reads the mac address stored in SPI flash (registered as MTD device) and populates the EMAC platform data. The function which reads the mac address is registered as a callback which gets called upon addition of MTD device. NOTE: In case the MAC address stored in SPI flash is erased, follow the instructions at [1] to restore it. [1] http://processors.wiki.ti.com/index.php/GSG:_OMAP-L138_DVEVM_Additional_Procedures#Restoring_MAC_address_on_SPI_Flash Modifications in v2: Guarded registering the mtd_notifier only when MTD is enabled. Earlier this was handled using mtd_has_partitions() call, but this has been removed in Linux v3.0. Modifications in v3: a. Guarded da850_evm_m25p80_notify_add() function and da850evm_spi_notifier structure with CONFIG_MTD macros. b. Renamed da850_evm_register_mtd_user() function to da850_evm_setup_mac_addr() and removed the struct mtd_notifier argument to this function. c. Passed the da850evm_spi_notifier structure to register_mtd_user() function. Signed-off-by: Rajashekhara, Sudhakar --- arch/arm/mach-davinci/board-da850-evm.c | 30 ++++++++++++++++++++++++++++++ 1 files changed, 30 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..5bb6556 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -115,6 +115,25 @@ static struct spi_board_info da850evm_spi_info[] = { }, }; +#ifdef CONFIG_MTD +static void da850_evm_m25p80_notify_add(struct mtd_info *mtd) +{ + char *mac_addr = davinci_soc_info.emac_pdata->mac_addr; + size_t retlen; + + if (!strcmp(mtd->name, "MAC-Address")) { + mtd->read(mtd, 0, ETH_ALEN, &retlen, mac_addr); + if (retlen == ETH_ALEN) + pr_info("Read MAC addr from SPI Flash: %pM\n", + mac_addr); + } +} + +static struct mtd_notifier da850evm_spi_notifier = { + .add = da850_evm_m25p80_notify_add, +}; +#endif + static struct mtd_partition da850_evm_norflash_partition[] = { { .name = "bootloaders + env", @@ -1117,6 +1136,15 @@ static __init int da850_evm_init_cpufreq(void) static __init int da850_evm_init_cpufreq(void) { return 0; } #endif +#ifdef CONFIG_MTD +static void da850_evm_setup_mac_addr(void) +{ + register_mtd_user(&da850evm_spi_notifier); +} +#else +static void da850_evm_setup_mac_addr(void) { } +#endif + static __init void da850_evm_init(void) { int ret; @@ -1237,6 +1265,8 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: spi 1 registration failed: %d\n", ret); + + da850_evm_setup_mac_addr(); } #ifdef CONFIG_SERIAL_8250_CONSOLE -- 1.7.1 From gary at mlbassoc.com Mon Jul 11 06:24:30 2011 From: gary at mlbassoc.com (Gary Thomas) Date: Mon, 11 Jul 2011 05:24:30 -0600 Subject: NAND support in U-Boot Message-ID: <4E1ADD6E.5040501@mlbassoc.com> Note: I'm not sure the best place to ask this question as it's very Davinci specific. Has anyone worked on the NAND drivers in U-Boot for the Davinci chips? In particular the OMAP-L138? It seems that the U-Boot drivers in use only use software ECC which is incompatible with the hardware ECC engine on the device. My board is booting from NAND and if I use U-Boot to update the U-Boot image in NAND, the ECC information is incorrect and the board stops booting. Any ideas/help? Should I ask this on a different list (please don't say e2e forums...) Thanks -- ------------------------------------------------------------ Gary Thomas | Consulting for the MLB Associates | Embedded world ------------------------------------------------------------ From nsekhar at ti.com Mon Jul 11 07:32:55 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 11 Jul 2011 18:02:55 +0530 Subject: [PATCH v3] davinci: da850 EVM: read mac address from SPI flash In-Reply-To: <1310376501-14189-1-git-send-email-sudhakar.raj@ti.com> References: <1310376501-14189-1-git-send-email-sudhakar.raj@ti.com> Message-ID: Hi Sudhakar, On Mon, Jul 11, 2011 at 14:58:21, Rajashekhara, Sudhakar wrote: > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index a7b41bf..5bb6556 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -115,6 +115,25 @@ static struct spi_board_info da850evm_spi_info[] = { > }, > }; > > +#ifdef CONFIG_MTD > +static void da850_evm_m25p80_notify_add(struct mtd_info *mtd) > +{ > + char *mac_addr = davinci_soc_info.emac_pdata->mac_addr; > + size_t retlen; > + > + if (!strcmp(mtd->name, "MAC-Address")) { > + mtd->read(mtd, 0, ETH_ALEN, &retlen, mac_addr); > + if (retlen == ETH_ALEN) > + pr_info("Read MAC addr from SPI Flash: %pM\n", > + mac_addr); > + } > +} > + > +static struct mtd_notifier da850evm_spi_notifier = { > + .add = da850_evm_m25p80_notify_add, > +}; > +#endif > + > static struct mtd_partition da850_evm_norflash_partition[] = { > { > .name = "bootloaders + env", > @@ -1117,6 +1136,15 @@ static __init int da850_evm_init_cpufreq(void) > static __init int da850_evm_init_cpufreq(void) { return 0; } > #endif > > +#ifdef CONFIG_MTD > +static void da850_evm_setup_mac_addr(void) > +{ > + register_mtd_user(&da850evm_spi_notifier); > +} > +#else > +static void da850_evm_setup_mac_addr(void) { } > +#endif Why not combine this ifdef construct with earlier one? Rest of it looks good to me. Thanks, Sekhar From nsekhar at ti.com Mon Jul 11 06:58:44 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 11 Jul 2011 17:28:44 +0530 Subject: [PATCH v2 1/6] arm: davinci: Fix low level gpio irq handlers' argument In-Reply-To: <1310303679-17936-2-git-send-email-ido@wizery.com> References: <1310303679-17936-1-git-send-email-ido@wizery.com> <1310303679-17936-2-git-send-email-ido@wizery.com> Message-ID: Hi Ido, On Sun, Jul 10, 2011 at 18:44:34, Ido Yariv wrote: > Commit 7416401 ("arm: davinci: Fix fallout from generic irq chip > conversion") introduced a bug, causing low level interrupt handlers to > get a bogus irq number as an argument. The gpio irq handler falsely > assumes that the handler data is the irq base number and that is no > longer true. > > Fix this by converting gpio_irq_handler's bank_irq argument to the > corresponding irq base number. > > Signed-off-by: Ido Yariv > CC: Thomas Gleixner > --- > arch/arm/mach-davinci/gpio.c | 32 ++++++++++++++++++++++++++++---- > 1 files changed, 28 insertions(+), 4 deletions(-) > > diff --git a/arch/arm/mach-davinci/gpio.c b/arch/arm/mach-davinci/gpio.c > index e722139..ff43e2a 100644 > --- a/arch/arm/mach-davinci/gpio.c > +++ b/arch/arm/mach-davinci/gpio.c > @@ -249,16 +249,40 @@ static struct irq_chip gpio_irqchip = { > .flags = IRQCHIP_SET_TYPE_MASKED, > }; > > +static inline int bankirq_to_irqbase(unsigned int bank_irq) > +{ > + int gpio; > + int index; > + > + /* Each irq bank consists of up to 16 irqs */ > + gpio = 16 * (bank_irq - davinci_soc_info.gpio_irq); > + > + /* Each controller controls 32 GPIOs */ > + index = gpio / 32; > + > + if (unlikely(!davinci_soc_info.gpio_ctlrs)) > + return -EINVAL; > + > + if (unlikely(index >= davinci_soc_info.gpio_ctlrs_num)) > + return -EINVAL; > + > + return davinci_soc_info.gpio_ctlrs[index].irq_base; > +} > + > static void > -gpio_irq_handler(unsigned irq, struct irq_desc *desc) > +gpio_irq_handler(unsigned bank_irq, struct irq_desc *desc) > { > struct davinci_gpio_regs __iomem *g; > u32 mask = 0xffff; > + int irqbase = bankirq_to_irqbase(bank_irq); > + > + if (unlikely(irqbase < 0)) > + return; > > g = (__force struct davinci_gpio_regs __iomem *) irq_desc_get_handler_data(desc); > > /* we only care about one bank */ > - if (irq & 1) > + if (bank_irq & 1) > mask <<= 16; > > /* temporarily mask (level sensitive) parent IRQ */ > @@ -274,11 +298,11 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc) > if (!status) > break; > __raw_writel(status, &g->intstat); > - if (irq & 1) > + if (bank_irq & 1) > status >>= 16; > > /* now demux them to the right lowlevel handler */ > - n = (int)irq_get_handler_data(irq); > + n = irqbase; > while (status) { > res = ffs(status); > n += res; Thanks for the bug fix. How about setting the handler data for bank IRQ in davinci_gpio_irq_setup() to &chips[bank]? chips[bank].regs should give you the register base address (g) and chips[bank].irq_base should give you 'n'. Also please drop the rename of irq to bank_irq as it is not central to the bug fix. If you spin in soon enough, we may make it to v3.0 Thanks, Sekhar From ido at wizery.com Mon Jul 11 15:33:56 2011 From: ido at wizery.com (Ido Yariv) Date: Mon, 11 Jul 2011 23:33:56 +0300 Subject: [PATCH v2 1/6] arm: davinci: Fix low level gpio irq handlers' argument In-Reply-To: References: <1310303679-17936-1-git-send-email-ido@wizery.com> <1310303679-17936-2-git-send-email-ido@wizery.com> Message-ID: <20110711203356.GA5633@WorkStation> Hi Sekhar, On Mon, Jul 11, 2011 at 05:28:44PM +0530, Nori, Sekhar wrote: > Thanks for the bug fix. > > How about setting the handler data for bank IRQ in > davinci_gpio_irq_setup() to &chips[bank]? > > chips[bank].regs should give you the register base address > (g) and chips[bank].irq_base should give you 'n'. > > Also please drop the rename of irq to bank_irq as it is not > central to the bug fix. > > If you spin in soon enough, we may make it to v3.0 Sure, I'll send a v3 in a few. Thanks for the review, Ido. From ido at wizery.com Mon Jul 11 16:03:11 2011 From: ido at wizery.com (Ido Yariv) Date: Tue, 12 Jul 2011 00:03:11 +0300 Subject: [PATCH v3 1/6] arm: davinci: Fix low level gpio irq handlers' argument In-Reply-To: References: Message-ID: <1310418191-27009-1-git-send-email-ido@wizery.com> Commit 7416401 ("arm: davinci: Fix fallout from generic irq chip conversion") introduced a bug, causing low level interrupt handlers to get a bogus irq number as an argument. The gpio irq handler falsely assumes that the handler data is the irq base number and that is no longer true. Set the irq handler data to be a pointer to the corresponding gpio controller. The chained irq handler can then use it to extract both the irq base number and the gpio registers structure. Signed-off-by: Ido Yariv CC: Thomas Gleixner --- arch/arm/mach-davinci/gpio.c | 14 +++++++++++--- 1 files changed, 11 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-davinci/gpio.c b/arch/arm/mach-davinci/gpio.c index e722139..7d64a07 100644 --- a/arch/arm/mach-davinci/gpio.c +++ b/arch/arm/mach-davinci/gpio.c @@ -254,8 +254,10 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc) { struct davinci_gpio_regs __iomem *g; u32 mask = 0xffff; + struct davinci_gpio_controller *ctl; - g = (__force struct davinci_gpio_regs __iomem *) irq_desc_get_handler_data(desc); + ctl = (struct davinci_gpio_controller *)irq_desc_get_handler_data(desc); + g = (struct davinci_gpio_regs __iomem *)ctl->regs; /* we only care about one bank */ if (irq & 1) @@ -278,7 +280,7 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc) status >>= 16; /* now demux them to the right lowlevel handler */ - n = (int)irq_get_handler_data(irq); + n = ctl->irq_base; while (status) { res = ffs(status); n += res; @@ -424,7 +426,13 @@ static int __init davinci_gpio_irq_setup(void) /* set up all irqs in this bank */ irq_set_chained_handler(bank_irq, gpio_irq_handler); - irq_set_handler_data(bank_irq, (__force void *)g); + + /* + * Each chip handles 32 gpios, and each irq bank consists of 16 + * gpio irqs. Pass the irq bank's corresponding controller to + * the chained irq handler. + */ + irq_set_handler_data(bank_irq, &chips[bank * 16 / 32]); for (i = 0; i < 16 && gpio < ngpio; i++, irq++, gpio++) { irq_set_chip(irq, &gpio_irqchip); -- 1.7.4.1 From dan.j.williams at intel.com Mon Jul 11 16:39:17 2011 From: dan.j.williams at intel.com (Dan Williams) Date: Mon, 11 Jul 2011 14:39:17 -0700 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: On Mon, Jul 11, 2011 at 2:28 AM, Linus Walleij wrote: > 2011/7/10 Sundaram Raju : > >> Added new dma_ctrl_cmd TI_DMA_STRIDE_CONFIG to pass the TI DMA >> controller specific configurations on how a buffer must be walked >> through and how data is picked for transfer based on a specified >> pattern over the channel. >> >> The configuration passed is specific to the TI DMA controller used. ...and I suspect the slave device drivers that use TI DMA are not expected to ever work with other dmaengines? Likely the case, but just wondering out loud. >> Signed-off-by: Sundaram Raju > > This is exactly how I think we should do this. > Acked-by: Linus Walleij From jassisinghbrar at gmail.com Mon Jul 11 23:17:19 2011 From: jassisinghbrar at gmail.com (Jassi Brar) Date: Tue, 12 Jul 2011 09:47:19 +0530 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: <1310310203-12288-1-git-send-email-sundaram@ti.com> References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: On Sun, Jul 10, 2011 at 8:33 PM, Sundaram Raju wrote: > Added new dma_ctrl_cmd TI_DMA_STRIDE_CONFIG to pass the TI DMA > controller specific configurations on how a buffer must be walked > through and how data is picked for transfer based on a specified > pattern over the channel. > > The configuration passed is specific to the TI DMA controller used. > > Signed-off-by: Sundaram Raju > --- > ?include/linux/dmaengine.h | ? ?5 +++++ > ?1 files changed, 5 insertions(+), 0 deletions(-) > > diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h > index eee7add..51dadc4 100644 > --- a/include/linux/dmaengine.h > +++ b/include/linux/dmaengine.h > @@ -123,6 +123,10 @@ enum dma_ctrl_flags { > ?* command. > ?* @FSLDMA_EXTERNAL_START: this command will put the Freescale DMA controller > ?* into external start mode. > + * @TI_DMA_STRIDE_CONFIG: this command is only implemented by TI DMA > + * controllers that need to pass special configuration on how to walk through > + * the buffer to pick up data in a specified pattern to be transferred in > + * the channel. > ?*/ > ?enum dma_ctrl_cmd { > ? ? ? ?DMA_TERMINATE_ALL, > @@ -130,6 +134,7 @@ enum dma_ctrl_cmd { > ? ? ? ?DMA_RESUME, > ? ? ? ?DMA_SLAVE_CONFIG, > ? ? ? ?FSLDMA_EXTERNAL_START, > + ? ? ? TI_DMA_STRIDE_CONFIG, > ?}; IMHO this isn't very correct. 1) Striding, in one form or other, is supported by other DMACs as well. The number will only increase in future. Are we to add _DMA_STRIDE_CONFIG for each case ? 2) As Dan noted, client drivers are going to have ifdef hackery in order to be common to other SoCs. 3) TI may not have just one DMAC IP used in all the SoCs. So if you want vendor specific defines anyway, please atleast also add DMAC version to it. Something like > DMA_SLAVE_CONFIG, > FSLDMA_EXTERNAL_START, > + TI_DMA_v1_STRIDE_CONFIG, From nsekhar at ti.com Tue Jul 12 04:22:17 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 12 Jul 2011 14:52:17 +0530 Subject: [PATCH v3 1/6] arm: davinci: Fix low level gpio irq handlers' argument In-Reply-To: <1310418191-27009-1-git-send-email-ido@wizery.com> References: <1310418191-27009-1-git-send-email-ido@wizery.com> Message-ID: Hi Ido, On Tue, Jul 12, 2011 at 02:33:11, Ido Yariv wrote: > Commit 7416401 ("arm: davinci: Fix fallout from generic irq chip > conversion") introduced a bug, causing low level interrupt handlers to > get a bogus irq number as an argument. The gpio irq handler falsely > assumes that the handler data is the irq base number and that is no > longer true. > > Set the irq handler data to be a pointer to the corresponding gpio > controller. The chained irq handler can then use it to extract both the > irq base number and the gpio registers structure. > > Signed-off-by: Ido Yariv > CC: Thomas Gleixner > --- > arch/arm/mach-davinci/gpio.c | 14 +++++++++++--- > 1 files changed, 11 insertions(+), 3 deletions(-) > > diff --git a/arch/arm/mach-davinci/gpio.c b/arch/arm/mach-davinci/gpio.c > index e722139..7d64a07 100644 > --- a/arch/arm/mach-davinci/gpio.c > +++ b/arch/arm/mach-davinci/gpio.c > @@ -254,8 +254,10 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc) > { > struct davinci_gpio_regs __iomem *g; > u32 mask = 0xffff; > + struct davinci_gpio_controller *ctl; Lets call the variable "d" to be consistent with the rest of the file. > > - g = (__force struct davinci_gpio_regs __iomem *) irq_desc_get_handler_data(desc); > + ctl = (struct davinci_gpio_controller *)irq_desc_get_handler_data(desc); > + g = (struct davinci_gpio_regs __iomem *)ctl->regs; > > /* we only care about one bank */ > if (irq & 1) > @@ -278,7 +280,7 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc) > status >>= 16; > > /* now demux them to the right lowlevel handler */ > - n = (int)irq_get_handler_data(irq); > + n = ctl->irq_base; I realized that this breaks for odd banks as the status is right shifted by 16. The GPIO you are using must have been in even bank? > while (status) { > res = ffs(status); > n += res; > @@ -424,7 +426,13 @@ static int __init davinci_gpio_irq_setup(void) > > /* set up all irqs in this bank */ > irq_set_chained_handler(bank_irq, gpio_irq_handler); > - irq_set_handler_data(bank_irq, (__force void *)g); > + > + /* > + * Each chip handles 32 gpios, and each irq bank consists of 16 > + * gpio irqs. Pass the irq bank's corresponding controller to > + * the chained irq handler. > + */ > + irq_set_handler_data(bank_irq, &chips[bank * 16 / 32]); This can simply be: irq_set_handler_data(bank_irq, &chips[gpio / 32]); In the interest of time, I did these fixes and pushed the patch to "fixes" branch of git://gitorious.org/linux-davinci/linux-davinci.git Can you please test it out and let me know if it works. Updated patch also attached. Thanks, Sekhar 8<--------------------- From: Ido Yariv Subject: arm: davinci: Fix low level gpio irq handlers' argument Commit 7416401 ("arm: davinci: Fix fallout from generic irq chip conversion") introduced a bug, causing low level interrupt handlers to get a bogus irq number as an argument. The gpio irq handler falsely assumes that the handler data is the irq base number and that is no longer true. Set the irq handler data to be a pointer to the corresponding gpio controller. The chained irq handler can then use it to extract both the irq base number and the gpio registers structure. Signed-off-by: Ido Yariv CC: Thomas Gleixner [nsekhar at ti.com: renamed "ctl" to "d", simplified indexing logic for chips and took care of odd bank handling in irq handler] Signed-off-by: Sekhar Nori --- arch/arm/mach-davinci/gpio.c | 21 ++++++++++++++++----- 1 files changed, 16 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-davinci/gpio.c b/arch/arm/mach-davinci/gpio.c index e722139..cafbe13 100644 --- a/arch/arm/mach-davinci/gpio.c +++ b/arch/arm/mach-davinci/gpio.c @@ -254,8 +254,10 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc) { struct davinci_gpio_regs __iomem *g; u32 mask = 0xffff; + struct davinci_gpio_controller *d; - g = (__force struct davinci_gpio_regs __iomem *) irq_desc_get_handler_data(desc); + d = (struct davinci_gpio_controller *)irq_desc_get_handler_data(desc); + g = (struct davinci_gpio_regs __iomem *)d->regs; /* we only care about one bank */ if (irq & 1) @@ -274,11 +276,14 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc) if (!status) break; __raw_writel(status, &g->intstat); - if (irq & 1) - status >>= 16; /* now demux them to the right lowlevel handler */ - n = (int)irq_get_handler_data(irq); + n = d->irq_base; + if (irq & 1) { + n += 16; + status >>= 16; + } + while (status) { res = ffs(status); n += res; @@ -424,7 +429,13 @@ static int __init davinci_gpio_irq_setup(void) /* set up all irqs in this bank */ irq_set_chained_handler(bank_irq, gpio_irq_handler); - irq_set_handler_data(bank_irq, (__force void *)g); + + /* + * Each chip handles 32 gpios, and each irq bank consists of 16 + * gpio irqs. Pass the irq bank's corresponding controller to + * the chained irq handler. + */ + irq_set_handler_data(bank_irq, &chips[gpio / 32]); for (i = 0; i < 16 && gpio < ngpio; i++, irq++, gpio++) { irq_set_chip(irq, &gpio_irqchip); From linus.walleij at linaro.org Tue Jul 12 04:58:04 2011 From: linus.walleij at linaro.org (Linus Walleij) Date: Tue, 12 Jul 2011 11:58:04 +0200 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: On Mon, Jul 11, 2011 at 11:39 PM, Dan Williams wrote: > On Mon, Jul 11, 2011 at 2:28 AM, Linus Walleij wrote: > ...and I suspect the slave device drivers that use TI DMA are not > expected to ever work with other dmaengines? ?Likely the case, but > just wondering out loud. Typically the OMAP/TI drivers are one-to-one with this specific DMA controller, but they *can* support controllers without stride options, and notice that striding will only be used for the display driver IIRC, pseudo-code: ret = dmaengine_device_control(chan, TI_DMA_STRIDE_CONFIG, (unsigned long) &my_stride_config); if (ret) { /* * OK no striding on this DMA engine, fall back to something else, * such as creating an SGlist which emulates the striding with one * sglist element per stride. */ } By injecting an error in the stride config path this can even be properly tested. So it will become an optional acceleration. Thanks, Linus Walleij From linus.walleij at linaro.org Tue Jul 12 05:03:22 2011 From: linus.walleij at linaro.org (Linus Walleij) Date: Tue, 12 Jul 2011 12:03:22 +0200 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: On Tue, Jul 12, 2011 at 6:17 AM, Jassi Brar wrote: > 1) Striding, in one form or other, is supported by other DMACs as well. > ? The number will only increase in future. > ? Are we to add ?_DMA_STRIDE_CONFIG for each case ? If we are sure about this and striding will work in a similar way on all then let's have the enum named DMA_STRIDE_CONFIG and move the passed-in struct to 2) As Dan noted, client drivers are going to have ifdef hackery in > order to be common > ?to other SoCs. Don't think so, why? This is a runtime config entirely, and I just illustrated in mail to Dan how that can be handled by falling back to a sglist I believe? We can *maybe* even put the fallback code into dmaengine, so that an emulated sglist in place for the DMAengine is done automatically of the DMA controller does not support striding. > 3) TI may not have just one DMAC IP used in all the SoCs. So if you want > ?vendor specific defines anyway, please atleast also add DMAC version to it. > ?Something like >> ? ? ? ?DMA_SLAVE_CONFIG, >> ? ? ? ?FSLDMA_EXTERNAL_START, >> + ? ? ? TI_DMA_v1_STRIDE_CONFIG, Yep unless we make it generic DMA_STRIDE_CONFIG simply, this makes a lot of sense. Linus Walleij From sundaram at ti.com Tue Jul 12 05:15:47 2011 From: sundaram at ti.com (Raju, Sundaram) Date: Tue, 12 Jul 2011 15:45:47 +0530 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: > -----Original Message----- > From: Linus Walleij [mailto:linus.walleij at linaro.org] > Sent: Tuesday, July 12, 2011 3:28 PM > To: Dan Williams > Cc: Raju, Sundaram; linux-arm-kernel at lists.infradead.org; linux- > kernel at vger.kernel.org; davinci-linux-open-source at linux.davincidsp.com; > linux at arm.linux.org.uk; linux-omap at vger.kernel.org > Subject: Re: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride > configuration > > On Mon, Jul 11, 2011 at 11:39 PM, Dan Williams > wrote: > > On Mon, Jul 11, 2011 at 2:28 AM, Linus Walleij > wrote: > > > ...and I suspect the slave device drivers that use TI DMA are not > > expected to ever work with other dmaengines? ?Likely the case, but > > just wondering out loud. > > Typically the OMAP/TI drivers are one-to-one with this specific DMA > controller, but they *can* support controllers without stride options, and > notice that striding will only be used for the display driver IIRC, > pseudo-code: > > ret = dmaengine_device_control(chan, TI_DMA_STRIDE_CONFIG, > (unsigned long) &my_stride_config); > if (ret) { > /* > * OK no striding on this DMA engine, fall back to something else, > * such as creating an SGlist which emulates the striding with one > * sglist element per stride. > */ > } > > By injecting an error in the stride config path this can even be > properly tested. So it will become an optional acceleration. Yes, this is exactly what I also wanted to say. :) But if the client driver does not implement a fallback like this then that driver will work only with TI DMA and not any other dmaengines. (Mentioning this because, there are client drivers which are tightly coupled to this special configuration) Keeping this in mind, I had started the original discussion with the suggestion of modifying the existing prepare API and adding an extra argument to it, which can be used to pass special configuration. And I also wanted to generalize that configuration passed. Anyways that design also will come down to this same path, and instead of modifying the existing API signatures, I think this is the best way we can go. Regards, Sundaram From Daniele.Bosi at mta.it Tue Jul 12 05:17:41 2011 From: Daniele.Bosi at mta.it (Bosi Daniele) Date: Tue, 12 Jul 2011 12:17:41 +0200 Subject: bad sd card write performance Message-ID: <531B6536C9F737458807DF75064873C801ED70DBD693@mta-digimail.MTA.INT> Hi all, I have an application that is writing 2 D1 h264 videos on the SD card and I see some slowdown on the thread that is saving the videos on sd card, when it is mounted with the "sync" option. I mean with a bitrate lesser than 1 MByte (2 D1 h264 videos), using a class 10 SD card mounted with the SYNC option, I can write on sd card at a frame rate of max 20 fps on both the videos, while the capture tasks are running at 25 fps. This means that I'm loosing 5 captured frames every sec on both the videos! So my question is: is there a way to improve the driver write performance on the SD card? BTW the used file system is FAT32 and the hardware has a DM368 cpu with Linux kernel 2.6.18 from Udworks For us the "sync" option is a requirement, because the system should be power cut safe. Does anyone encountered the same problems? Thanks in advance Daniele From sudhakar.raj at ti.com Tue Jul 12 05:34:38 2011 From: sudhakar.raj at ti.com (Rajashekhara, Sudhakar) Date: Tue, 12 Jul 2011 16:04:38 +0530 Subject: [PATCH v3] davinci: da850 EVM: read mac address from SPI flash In-Reply-To: References: <1310376501-14189-1-git-send-email-sudhakar.raj@ti.com> Message-ID: Hi, On Mon, Jul 11, 2011 at 18:02:55, Nori, Sekhar wrote: > Hi Sudhakar, > > On Mon, Jul 11, 2011 at 14:58:21, Rajashekhara, Sudhakar wrote: > > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > > index a7b41bf..5bb6556 100644 > > --- a/arch/arm/mach-davinci/board-da850-evm.c > > +++ b/arch/arm/mach-davinci/board-da850-evm.c > > @@ -115,6 +115,25 @@ static struct spi_board_info da850evm_spi_info[] = { > > }, > > }; > > > > +#ifdef CONFIG_MTD > > +static void da850_evm_m25p80_notify_add(struct mtd_info *mtd) > > +{ > > + char *mac_addr = davinci_soc_info.emac_pdata->mac_addr; > > + size_t retlen; > > + > > + if (!strcmp(mtd->name, "MAC-Address")) { > > + mtd->read(mtd, 0, ETH_ALEN, &retlen, mac_addr); > > + if (retlen == ETH_ALEN) > > + pr_info("Read MAC addr from SPI Flash: %pM\n", > > + mac_addr); > > + } > > +} > > + > > +static struct mtd_notifier da850evm_spi_notifier = { > > + .add = da850_evm_m25p80_notify_add, > > +}; > > +#endif > > + > > static struct mtd_partition da850_evm_norflash_partition[] = { > > { > > .name = "bootloaders + env", > > @@ -1117,6 +1136,15 @@ static __init int da850_evm_init_cpufreq(void) > > static __init int da850_evm_init_cpufreq(void) { return 0; } > > #endif > > > > +#ifdef CONFIG_MTD > > +static void da850_evm_setup_mac_addr(void) > > +{ > > + register_mtd_user(&da850evm_spi_notifier); > > +} > > +#else > > +static void da850_evm_setup_mac_addr(void) { } > > +#endif > > Why not combine this ifdef construct with > earlier one? > Ok. Updated patch will follow. Regards, Sudhakar From sudhakar.raj at ti.com Tue Jul 12 05:28:53 2011 From: sudhakar.raj at ti.com (Rajashekhara, Sudhakar) Date: Tue, 12 Jul 2011 15:58:53 +0530 Subject: [PATCH v4] davinci: da850 EVM: read mac address from SPI flash Message-ID: <1310466533-24474-1-git-send-email-sudhakar.raj@ti.com> DA850/OMAP-L138 EMAC driver uses random mac address instead of a fixed one because the mac address is not stuffed into EMAC platform data. This patch provides a function which reads the mac address stored in SPI flash (registered as MTD device) and populates the EMAC platform data. The function which reads the mac address is registered as a callback which gets called upon addition of MTD device. NOTE: In case the MAC address stored in SPI flash is erased, follow the instructions at [1] to restore it. [1] http://processors.wiki.ti.com/index.php/GSG:_OMAP-L138_DVEVM_Additional_Procedures#Restoring_MAC_address_on_SPI_Flash Modifications in v2: Guarded registering the mtd_notifier only when MTD is enabled. Earlier this was handled using mtd_has_partitions() call, but this has been removed in Linux v3.0. Modifications in v3: a. Guarded da850_evm_m25p80_notify_add() function and da850evm_spi_notifier structure with CONFIG_MTD macros. b. Renamed da850_evm_register_mtd_user() function to da850_evm_setup_mac_addr() and removed the struct mtd_notifier argument to this function. c. Passed the da850evm_spi_notifier structure to register_mtd_user() function. Modifications in v4: Moved the da850_evm_setup_mac_addr() function within the first CONFIG_MTD ifdef construct. Signed-off-by: Rajashekhara, Sudhakar --- arch/arm/mach-davinci/board-da850-evm.c | 28 ++++++++++++++++++++++++++++ 1 files changed, 28 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index a7b41bf..e83cc86 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -115,6 +115,32 @@ static struct spi_board_info da850evm_spi_info[] = { }, }; +#ifdef CONFIG_MTD +static void da850_evm_m25p80_notify_add(struct mtd_info *mtd) +{ + char *mac_addr = davinci_soc_info.emac_pdata->mac_addr; + size_t retlen; + + if (!strcmp(mtd->name, "MAC-Address")) { + mtd->read(mtd, 0, ETH_ALEN, &retlen, mac_addr); + if (retlen == ETH_ALEN) + pr_info("Read MAC addr from SPI Flash: %pM\n", + mac_addr); + } +} + +static struct mtd_notifier da850evm_spi_notifier = { + .add = da850_evm_m25p80_notify_add, +}; + +static void da850_evm_setup_mac_addr(void) +{ + register_mtd_user(&da850evm_spi_notifier); +} +#else +static void da850_evm_setup_mac_addr(void) { } +#endif + static struct mtd_partition da850_evm_norflash_partition[] = { { .name = "bootloaders + env", @@ -1237,6 +1263,8 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: spi 1 registration failed: %d\n", ret); + + da850_evm_setup_mac_addr(); } #ifdef CONFIG_SERIAL_8250_CONSOLE -- 1.7.1 From const at MakeLinux.com Tue Jul 12 05:52:36 2011 From: const at MakeLinux.com (const at MakeLinux.com) Date: Tue, 12 Jul 2011 13:52:36 +0300 Subject: [PATCH] [RESENDING] Enable USB on TI DM365 In-Reply-To: <4E085FEE.5090301@mvista.com> References: <4E085FEE.5090301@mvista.com> Message-ID: <1310467956-21739-1-git-send-email-const@MakeLinux.com> # Common objects obj-y := time.o clock.o serial.o io.o psc.o \ - gpio.o dma.o usb.o common.o sram.o aemif.o + gpio.o dma.o common.o sram.o aemif.o obj-$(CONFIG_DAVINCI_MUX) += mux.o @@ -40,3 +40,4 @@ obj-$(CONFIG_MACH_OMAPL138_HAWKBOARD) += board-omapl138-hawk.o obj-$(CONFIG_CPU_FREQ) += cpufreq.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_SUSPEND) += pm.o sleep.o +obj-$(CONFIG_USB_MUSB_DAVINCI) += usb.o diff --git a/arch/arm/mach-davinci/include/mach/usb.h b/arch/arm/mach-davinci/include/mach/usb.h index e0bc4ab..863c27f 100644 --- a/arch/arm/mach-davinci/include/mach/usb.h +++ b/arch/arm/mach-davinci/include/mach/usb.h @@ -54,6 +54,6 @@ struct da8xx_ohci_root_hub { u8 potpgt; }; -void davinci_setup_usb(unsigned mA, unsigned potpgt_ms); +int davinci_setup_usb(unsigned mA, unsigned potpgt_ms); #endif /* ifndef __ASM_ARCH_USB_H */ diff --git a/arch/arm/mach-davinci/usb.c b/arch/arm/mach-davinci/usb.c index 23d2b6d..daf5398 100644 --- a/arch/arm/mach-davinci/usb.c +++ b/arch/arm/mach-davinci/usb.c @@ -4,13 +4,14 @@ #include #include #include - #include #include #include #include #include +#include +#include #define DAVINCI_USB_OTG_BASE 0x01c64000 @@ -87,7 +88,7 @@ static struct platform_device usb_dev = { .num_resources = ARRAY_SIZE(usb_resources), }; -void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) +int __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) { usb_data.power = mA > 510 ? 255 : mA / 2; usb_data.potpgt = (potpgt_ms + 1) / 2; @@ -99,7 +100,7 @@ void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) } else /* other devices don't have dedicated CPPI IRQ */ usb_dev.num_resources = 2; - platform_device_register(&usb_dev); + return platform_device_register(&usb_dev); } #ifdef CONFIG_ARCH_DAVINCI_DA8XX @@ -132,7 +133,7 @@ int __init da8xx_register_usb20(unsigned mA, unsigned potpgt) #else -void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) +int __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) { } @@ -178,3 +179,23 @@ int __init da8xx_register_usb11(struct da8xx_ohci_root_hub *pdata) return platform_device_register(&da8xx_usb11_device); } #endif /* CONFIG_DAVINCI_DA8XX */ + +#ifdef ARCH_DAVINCI_DM365 +int __init dm365_usb_configure(void) +{ + /* GPIO33 is multiplexed with USB DRVVBUS */ + davinci_cfg_reg(DM365_GPIO33); + gpio_request(33, "usb"); + gpio_direction_output(33, 1); + return davinci_setup_usb(500, 8); +} +subsys_initcall(dm365_usb_configure); + +static void __exit dm365evm_usb_exit(void) +{ + platform_device_unregister(&usb_dev); +} +module_exit(dm365evm_usb_exit); +#endif + +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 2a2adf6..e6721e1 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -72,6 +72,15 @@ static inline void phy_on(void) /* power everything up; start the on-chip PHY and its PLL */ phy_ctrl &= ~(USBPHY_OSCPDWN | USBPHY_OTGPDWN | USBPHY_PHYPDWN); phy_ctrl |= USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON; + + if (cpu_is_davinci_dm365()) { + /* + * DM365 PHYCLKFREQ field [15:12] is set to 2 + * to get clock from 24MHz crystal + */ + phy_ctrl |= USBPHY_CLKFREQ_24MHZ; + } + __raw_writel(phy_ctrl, USB_PHY_CTRL); /* wait for PLL to lock before proceeding */ @@ -193,6 +202,9 @@ static void davinci_musb_source_power(struct musb *musb, int is_on, int immediat else schedule_work(&evm_vbus_work); } + + if (cpu_is_davinci_dm365()) + gpio_set_value(33, is_on); if (immediate) vbus_state = is_on; #endif diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h index 046c844..1bf50e6 100644 --- a/drivers/usb/musb/davinci.h +++ b/drivers/usb/musb/davinci.h @@ -17,6 +17,7 @@ /* Integrated highspeed/otg PHY */ #define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) #define USBPHY_DATAPOL BIT(11) /* (dm355) switch D+/D- */ +#define USBPHY_CLKFREQ_24MHZ BIT(13) #define USBPHY_PHYCLKGD BIT(8) #define USBPHY_SESNDEN BIT(7) /* v(sess_end) comparator */ #define USBPHY_VBDTCTEN BIT(6) /* v(bus) comparator */ From sundaram at ti.com Tue Jul 12 05:56:32 2011 From: sundaram at ti.com (Raju, Sundaram) Date: Tue, 12 Jul 2011 16:26:32 +0530 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: > -----Original Message----- > From: Linus Walleij [mailto:linus.walleij at linaro.org] > Sent: Tuesday, July 12, 2011 3:33 PM > To: Jassi Brar > Cc: Raju, Sundaram; linux-arm-kernel at lists.infradead.org; linux- > kernel at vger.kernel.org; davinci-linux-open-source at linux.davincidsp.com; > linux at arm.linux.org.uk; dan.j.williams at intel.com; linux-omap at vger.kernel.org > Subject: Re: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride > configuration > > On Tue, Jul 12, 2011 at 6:17 AM, Jassi Brar > wrote: > > > 1) Striding, in one form or other, is supported by other DMACs as well. > > ? The number will only increase in future. > > ? Are we to add ?_DMA_STRIDE_CONFIG for each case ? > > If we are sure about this and striding will work in a similar way on all > then let's have the enum named DMA_STRIDE_CONFIG and move the > passed-in struct to > Would that be: > > struct dma_stride_config { > u32 read_bytes; > u32 skip_bytes; > }; > > Or something more complex? > When I started this discussion on stride config, I received comments like this is too specific to TI DMAC, and there are not many DMACs which can do this. I actually wanted to generalize the configuration passed and put a comment on it similar to the one on top of dma_slave_config, which says | |/** | * The rationale for adding configuration information to this struct | * is as follows: if it is likely that most DMA slave controllers in | * the world will support the configuration option, then make it | * generic. If not: if it is fixed so that it be sent in static from | * the platform data, then prefer to do that. Else, if it is neither | * fixed at runtime, nor generic enough (such as bus mastership on | * some CPU family and whatnot) then create a custom slave config | * struct and pass that, then make this config a member of that | * struct, if applicable. | */ | If any other DMAC can do similar stride configuration, then we can generalize it. Till we generalize this stride configuration I think a custom configuration aligned between the client driver and the offload engine driver can be used. > > 2) As Dan noted, client drivers are going to have ifdef hackery in > > order to be common > > ?to other SoCs. > > Don't think so, why? This is a runtime config entirely, and I just illustrated > in mail to Dan how that can be handled by falling back to a sglist I believe? > > We can *maybe* even put the fallback code into dmaengine, so that an > emulated sglist in place for the DMAengine is done automatically of the > DMA controller does not support striding. > Good Idea. But the client might always have a better way to handle this fallback than this suggested fallback code in dmaengine, which will be a common implementation based on the received sg_list and the DMAC capabilities. If this is done then preference should be provided to the client's fallback implementation, if present. > > 3) TI may not have just one DMAC IP used in all the SoCs. So if you want > > ?vendor specific defines anyway, please atleast also add DMAC version to it. > > ?Something like > >> ? ? ? ?DMA_SLAVE_CONFIG, > >> ? ? ? ?FSLDMA_EXTERNAL_START, > >> + ? ? ? TI_DMA_v1_STRIDE_CONFIG, > > Yep unless we make it generic DMA_STRIDE_CONFIG simply, this makes > a lot of sense. > Okay, I can add one cmd for the EDMAC in DaVinci series of SoCs and one for SDMAC in OMAP series of SoCs. Regards, Sundaram From sshtylyov at mvista.com Tue Jul 12 05:56:17 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Tue, 12 Jul 2011 14:56:17 +0400 Subject: [PATCH] [RESENDING] Enable USB on TI DM365 In-Reply-To: <1310467956-21739-1-git-send-email-const@MakeLinux.com> References: <4E085FEE.5090301@mvista.com> <1310467956-21739-1-git-send-email-const@MakeLinux.com> Message-ID: <4E1C2851.2000806@mvista.com> Hello. On 12-07-2011 14:52, const at MakeLinux.com wrote: There's no use resending -- the patch won't be accepted as is. > # Common objects > obj-y := time.o clock.o serial.o io.o psc.o \ > - gpio.o dma.o usb.o common.o sram.o aemif.o > + gpio.o dma.o common.o sram.o aemif.o > > obj-$(CONFIG_DAVINCI_MUX) += mux.o > It seems the beginning of the patch got eaten by something. > @@ -132,7 +133,7 @@ int __init da8xx_register_usb20(unsigned mA, unsigned potpgt) > > #else > > -void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > +int __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > { You need the *return* statement here now... > } > WBR, Sergei From linus.walleij at linaro.org Tue Jul 12 06:09:31 2011 From: linus.walleij at linaro.org (Linus Walleij) Date: Tue, 12 Jul 2011 13:09:31 +0200 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: On Tue, Jul 12, 2011 at 12:56 PM, Raju, Sundaram wrote: > [Me] >> [Jassi] >> > 3) TI may not have just one DMAC IP used in all the SoCs. So if you want >> > ?vendor specific defines anyway, please atleast also add DMAC version to it. >> > ?Something like >> >> ? ? ? ?DMA_SLAVE_CONFIG, >> >> ? ? ? ?FSLDMA_EXTERNAL_START, >> >> + ? ? ? TI_DMA_v1_STRIDE_CONFIG, >> >> Yep unless we make it generic DMA_STRIDE_CONFIG simply, this makes >> a lot of sense. > > Okay, I can add one cmd for the EDMAC in DaVinci series of SoCs and > one for SDMAC in OMAP series of SoCs. Wait, that's two different silicon blocks right? Then you already have proof that this spans more than one DMAC and then you can just go for a generic DMA_STRIDE_CONFIG from day one. That both are TI does not matter, if they are totally unrelated implementations. Yours, Linus Walleij From c.aeschlimann at acn-group.ch Tue Jul 12 06:12:31 2011 From: c.aeschlimann at acn-group.ch (Christophe Aeschlimann) Date: Tue, 12 Jul 2011 13:12:31 +0200 Subject: NAND support in U-Boot In-Reply-To: <4E1ADD6E.5040501@mlbassoc.com> References: <4E1ADD6E.5040501@mlbassoc.com> Message-ID: <4E1C2C1F.1040907@acn-group.ch> Hi Gary, On 11.07.2011 13:24, Gary Thomas wrote: > Note: I'm not sure the best place to ask this question as it's very > Davinci specific. > > Has anyone worked on the NAND drivers in U-Boot for the Davinci chips? > In particular the OMAP-L138? It seems that the U-Boot drivers in use > only use software ECC which is incompatible with the hardware ECC engine > on the device. My board is booting from NAND and if I use U-Boot to > update the U-Boot image in NAND, the ECC information is incorrect and > the board stops booting. I don't think the problem is HW ECC vs SW ECC the problem is where the ECC bits are stored in the Out-Of-Band (OOB) area of the NAND. The ROM boot loader (RBL) expects them to be at a certain position where U-BOOT and the Linux Kernel have them stored in another place. More info here : http://processors.wiki.ti.com/index.php/DM365_Nand_ECC_layout A solution is to have a small dedicated partition that has the same ECC layout as the Rom Boot Loader that contain a very basic bootloader (e.g. TI's UBL) which then let you jump to U-Boot. Having the RBL loading UBL to load U-Boot to finally start Linux was too cumbersome for us so we just boot U-Boot directly from a SPI flash. > Any ideas/help? Should I ask this on a different list (please don't > say e2e forums...) Apart from the terrible user experience the site offers the persons active on these forums are very helpful. > Thanks Hope it helped, Best regards, -- Christophe Aeschlimann Embedded Software Engineer & IT Manager ACN Advanced Communications Networks S.A. Rue du Puits-Godet 8a 2000 Neuch?tel, Switzerland T?l. +41 32 724 74 31 c.aeschlimann at acn-group.ch From jassisinghbrar at gmail.com Tue Jul 12 06:20:37 2011 From: jassisinghbrar at gmail.com (Jassi Brar) Date: Tue, 12 Jul 2011 16:50:37 +0530 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: On Tue, Jul 12, 2011 at 3:33 PM, Linus Walleij wrote: > On Tue, Jul 12, 2011 at 6:17 AM, Jassi Brar wrote: > >> 1) Striding, in one form or other, is supported by other DMACs as well. >> ? The number will only increase in future. >> ? Are we to add ?_DMA_STRIDE_CONFIG for each case ? > > If we are sure about this and striding will work in a similar way on all > then let's have the enum named DMA_STRIDE_CONFIG and move the > passed-in struct to > Would that be: > > struct dma_stride_config { > ? ?u32 read_bytes; > ? ?u32 skip_bytes; > }; > > Or something more complex? Well, I am not sure if striding needs any special treatment at all. Why not have client drivers prepare and submit sg-list. Let the DMAC drivers interpret/parse the sg-list and program it as strides if the h/w supports it. If anything, we should make preparation and submission of sg-list as efficient as possible. >> 2) As Dan noted, client drivers are going to have ifdef hackery in >> order to be common >> ?to other SoCs. > > Don't think so, why? This is a runtime config entirely, and I just illustrated > in mail to Dan how that can be handled by falling back to a sglist I believe? Runtime decision isn't neat either. What if a client driver is common to 'N' SoCs each with different DMACs ? We would need a switch construct ! From sundaram at ti.com Tue Jul 12 06:31:23 2011 From: sundaram at ti.com (Raju, Sundaram) Date: Tue, 12 Jul 2011 17:01:23 +0530 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: > -----Original Message----- > From: Jassi Brar [mailto:jassisinghbrar at gmail.com] > Sent: Tuesday, July 12, 2011 4:51 PM > To: Linus Walleij > Cc: Raju, Sundaram; linux-arm-kernel at lists.infradead.org; linux- > kernel at vger.kernel.org; davinci-linux-open-source at linux.davincidsp.com; > linux at arm.linux.org.uk; dan.j.williams at intel.com; linux-omap at vger.kernel.org > Subject: Re: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride > configuration > > On Tue, Jul 12, 2011 at 3:33 PM, Linus Walleij > wrote: > > On Tue, Jul 12, 2011 at 6:17 AM, Jassi Brar > wrote: > > > >> 1) Striding, in one form or other, is supported by other DMACs as well. > >> ? The number will only increase in future. > >> ? Are we to add ?_DMA_STRIDE_CONFIG for each case ? > > > > If we are sure about this and striding will work in a similar way on all > > then let's have the enum named DMA_STRIDE_CONFIG and move the > > passed-in struct to > > > Would that be: > > > > struct dma_stride_config { > > ? ?u32 read_bytes; > > ? ?u32 skip_bytes; > > }; > > > > Or something more complex? > Well, I am not sure if striding needs any special treatment at all. > Why not have client drivers prepare and submit sg-list. > Let the DMAC drivers interpret/parse the sg-list and program it > as strides if the h/w supports it. > If anything, we should make preparation and submission of sg-list > as efficient as possible. Jassi, sg_lists describe only a bunch of disjoint buffers. But what if the DMAC can skip and read the bytes within each of the buffers in the sg_list? (like TI EDMAC and TI SDMAC) How can that information be passed to the offload engine driver from the client? ~Sundaram From manjunath.hadli at ti.com Tue Jul 12 07:01:52 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 12 Jul 2011 17:31:52 +0530 Subject: [ RFC PATCH 0/8] RFC for Media Controller capture driver for DM365 In-Reply-To: References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> <20110630135736.GK12671@valkosipuli.localdomain> <201107041522.37437.laurent.pinchart@ideasonboard.com> <4E11E695.9090508@iki.fi> Message-ID: Sakari/Laurent, Did you get some time to review the code? Will look forward for your comments. -Manju On Wed, Jul 06, 2011 at 11:10:38, Hadli, Manjunath wrote: > > Hi Sakari, > > On Mon, Jul 04, 2011 at 21:43:09, Sakari Ailus wrote: > > Hadli, Manjunath wrote: > > > Thank you Laurent. > > > > Hi Manjunath, > > > > > On Mon, Jul 04, 2011 at 18:52:37, Laurent Pinchart wrote: > > >> Hi Manjunath, > > >> > > >> On Monday 04 July 2011 07:58:06 Hadli, Manjunath wrote: > > >>> On Thu, Jun 30, 2011 at 19:27:36, Sakari Ailus wrote: > > >> > > >> [snip] > > >> > > >>>> I understand that not all the blocks are there. Are there any > > >>>> major functional differences between those in Davinci and those > > >>>> in OMAP 3? Could the OMAP 3 ISP driver made support Davinci ISP as well? > > >>> > > >>> Yes, there are a lot of major differences between OMAP3 and > > >>> Dm365/Dm355, both in terms of features, there IP, and the software > > >>> interface, including all the registers which are entirely different. > > >>> The closest omap3 would come to is only to DM6446. I do not think > > >>> OMAP3 driver can be made to support Dm355 and Dm365. It is good to > > >>> keep the OMAP3 neat and clean to cater for OMAP4 and beyond, and > > >>> keep the Davinci family separate. The names might look similar and > > >>> hence confusing for you, but the names can as well be made the > > >>> same as Dm365 blocks like ISIF and IPIPE and IPIPEIF which are different. > > >> > > >> The DM6446 ISP is very similar to the OMAP3 ISP, and thus quite > > >> different from the DM355/365 ISPs. Should the DM6446 be supported > > >> by the OMAP3 ISP driver, and the DM355/365 by this driver ? > > > > > > DM6446 capture IP is in some respects similar to OMAP3 for some > > > features, but there are a large number of differences also (MMU, > > > VRFB, a lot of display interfaces etc). Having a single driver > > > catering to Since DM6446 and OMAP3 is going to be unwieldy. Also, > > > DM6446 belongs to the Davinci family of chips, it should be clubbed > > > with the other Davinci SoCs as it will simplify a lot of other > > > things including directory subdirectory/file naming, organization of > > > machine/platform code etc among other things. Other than Video a lot > > > of other system registers and features which are common with the > > > rest of Davinci SoCs which if treated together is a good thing, > > > whereas > > > OMAP3 can be modified and developed with those on the OMAP family > > > (OMAP4 for ex). > > > > Thanks for the clarifications. > > > > What about the DM3730? As far as I understand, the ISP on that one is supported by the OMAP 3 ISP driver. But it looks like that it's more continuation for the OMAP family of the chips than the Davinci. > Let me say that for all practical purposes, for developers, DM3730 is OMAP3. So a distinction between OMAP3 and DM3730 need not be made at all. As to why it is a Davinci device, has more to do with things outside the realm of development. So Dm3730 for us, including you and me, can be OMAP3, As the TRM says - " It is OMAP3 compatible". > > > > > I glanced at the DM6446 documentation and at the register level the interface looks somewhat different although some register names are the same. I didn't found a proper TRM which would be as detailed as the OMAP ones --- does TI have one available in public? > TRMs for Davinci devices are slightly in a different format - split into multiple documents for each peripheral and system functionalities unlike a big singe doc for OMAP. > But all the required documents are in public domain and can be found at : > http://focus.ti.com/docs/prod/folders/print/tms320dm6446.html under the user guides category. If you are looking for some particular information, let me know and I can help you locate it. > > > > > > > OMAP 4 has a quite different ISS --- which the ISP is a part of, and which also is very different to the OMAP 3 one --- so it's unlikely that the same driver would support OMAP 3 and OMAP 4 ISPs. > > > > Kind regards, > > > > -- > > Sakari Ailus > > sakari.ailus at iki.fi > > > > Regards, > -Manjunath > From jassisinghbrar at gmail.com Tue Jul 12 07:45:22 2011 From: jassisinghbrar at gmail.com (Jassi Brar) Date: Tue, 12 Jul 2011 18:15:22 +0530 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: On Tue, Jul 12, 2011 at 5:01 PM, Raju, Sundaram wrote: >> -----Original Message----- >> From: Jassi Brar [mailto:jassisinghbrar at gmail.com] >> Sent: Tuesday, July 12, 2011 4:51 PM >> To: Linus Walleij >> Cc: Raju, Sundaram; linux-arm-kernel at lists.infradead.org; linux- >> kernel at vger.kernel.org; davinci-linux-open-source at linux.davincidsp.com; >> linux at arm.linux.org.uk; dan.j.williams at intel.com; linux-omap at vger.kernel.org >> Subject: Re: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride >> configuration >> >> On Tue, Jul 12, 2011 at 3:33 PM, Linus Walleij >> wrote: >> > On Tue, Jul 12, 2011 at 6:17 AM, Jassi Brar >> wrote: >> > >> >> 1) Striding, in one form or other, is supported by other DMACs as well. >> >> ? The number will only increase in future. >> >> ? Are we to add ?_DMA_STRIDE_CONFIG for each case ? >> > >> > If we are sure about this and striding will work in a similar way on all >> > then let's have the enum named DMA_STRIDE_CONFIG and move the >> > passed-in struct to > > >> > Would that be: >> > >> > struct dma_stride_config { >> > ? ?u32 read_bytes; >> > ? ?u32 skip_bytes; >> > }; >> > >> > Or something more complex? >> Well, I am not sure if striding needs any special treatment at all. >> Why not have client drivers prepare and submit sg-list. >> Let the DMAC drivers interpret/parse the sg-list and program it >> as strides if the h/w supports it. >> If anything, we should make preparation and submission of sg-list >> as efficient as possible. > Jassi, > > sg_lists describe only a bunch of disjoint buffers. But what if the > DMAC can skip and read the bytes within each of the buffers in > the sg_list? (like TI EDMAC and TI SDMAC) > How can that information be passed to the offload > engine driver from the client? > OK, I overlooked. We do need something new to handle these ultra-fine-grained sg-lists. But still we shouldn't add SoC specific API to the common sub-systems. Maybe a new api to pass fixed-format variable-length encoded message to the DMAC drivers? Which could be interpreted by DMAC drivers to extract all the needed xfer parameters from the 'header' section and instructions to program the xfers in the DMAC from the variable length body of the 'message' buffer. It might sound complicated but we can have helpers to make the job easy. Btw, the regular single/sg-list xfers could also be expressed by this method. From ebutera at gmail.com Tue Jul 12 10:11:33 2011 From: ebutera at gmail.com (Enrico Butera) Date: Tue, 12 Jul 2011 17:11:33 +0200 Subject: bad sd card write performance In-Reply-To: <531B6536C9F737458807DF75064873C801ED70DBD693@mta-digimail.MTA.INT> References: <531B6536C9F737458807DF75064873C801ED70DBD693@mta-digimail.MTA.INT> Message-ID: On Tue, Jul 12, 2011 at 12:17 PM, Bosi Daniele wrote: > Hi all, > I have an application that is writing 2 D1 h264 videos on the SD card and I see some slowdown on the thread that is saving the videos on sd card, ?when it is mounted with the "sync" option. > > I mean with a bitrate lesser than 1 MByte (2 D1 h264 videos), using a class 10 SD card mounted with the SYNC option, I can write on sd card at a frame rate of max 20 fps on both the videos, while the capture tasks are running at 25 fps. > > This means that I'm loosing 5 captured frames every sec on both the videos! > > So my question is: is there a way to improve the driver write performance on the SD card? > > BTW the used file system is FAT32 and the hardware has a DM368 cpu with Linux kernel 2.6.18 from Udworks > > For us the "sync" option is a requirement, because the system should be power cut safe. > > Does anyone encountered the same problems? > Silly question: are you buffering before writing on disk? I mean: if you make many small writes you'll never reach the SD theoretical write speed in sync mode, especially with two separate files. I made some tests (on an omap platform) and i found out that the best filesystem for this kind of work is nilfs but unfortunately i think your kernel is too old to support it. Ciao, Enrico From Daniele.Bosi at mta.it Tue Jul 12 10:40:44 2011 From: Daniele.Bosi at mta.it (Bosi Daniele) Date: Tue, 12 Jul 2011 17:40:44 +0200 Subject: R: bad sd card write performance In-Reply-To: Message-ID: <531B6536C9F737458807DF75064873C801ED70DBD695@mta-digimail.MTA.INT> Enrico, your one is not a silly question. I tested both "write" and "fwrite" functions, reaching the best performances with "fwrite" that AFAIK is buffered. I also tried splitting the write on the file pointer / file descriptor on packets of 32 kBytes (the drivers try to write 64 packets, 512 bytes long), but the best solution I found until now on the FAT file system is: fwrite without splitting the frames. So basically I have a loop for every video file where the software writes a frame at a time. Thanks for your help, ciao Daniele -----Messaggio originale----- Da: davinci-linux-open-source-bounces at linux.davincidsp.com [mailto:davinci-linux-open-source-bounces at linux.davincidsp.com] Per conto di Enrico Butera Inviato: marted? 12 luglio 2011 17.12 A: davinci-linux-open-source at linux.davincidsp.com Oggetto: Re: bad sd card write performance On Tue, Jul 12, 2011 at 12:17 PM, Bosi Daniele wrote: > Hi all, > I have an application that is writing 2 D1 h264 videos on the SD card and I see some slowdown on the thread that is saving the videos on sd card, when it is mounted with the "sync" option. > > I mean with a bitrate lesser than 1 MByte (2 D1 h264 videos), using a class 10 SD card mounted with the SYNC option, I can write on sd card at a frame rate of max 20 fps on both the videos, while the capture tasks are running at 25 fps. > > This means that I'm loosing 5 captured frames every sec on both the videos! > > So my question is: is there a way to improve the driver write performance on the SD card? > > BTW the used file system is FAT32 and the hardware has a DM368 cpu with Linux kernel 2.6.18 from Udworks > > For us the "sync" option is a requirement, because the system should be power cut safe. > > Does anyone encountered the same problems? > Silly question: are you buffering before writing on disk? I mean: if you make many small writes you'll never reach the SD theoretical write speed in sync mode, especially with two separate files. I made some tests (on an omap platform) and i found out that the best filesystem for this kind of work is nilfs but unfortunately i think your kernel is too old to support it. Ciao, Enrico _______________________________________________ Davinci-linux-open-source mailing list Davinci-linux-open-source at linux.davincidsp.com http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source From ebutera at gmail.com Tue Jul 12 11:07:24 2011 From: ebutera at gmail.com (Enrico Butera) Date: Tue, 12 Jul 2011 18:07:24 +0200 Subject: bad sd card write performance In-Reply-To: <531B6536C9F737458807DF75064873C801ED70DBD695@mta-digimail.MTA.INT> References: <531B6536C9F737458807DF75064873C801ED70DBD695@mta-digimail.MTA.INT> Message-ID: On Tue, Jul 12, 2011 at 5:40 PM, Bosi Daniele wrote: > Enrico, > your one is not a silly question. I tested both "write" and "fwrite" functions, reaching the best performances with "fwrite" that AFAIK is buffered. > I also tried splitting the write on the file pointer / file descriptor on packets of 32 kBytes (the drivers try to write 64 packets, 512 bytes long), but the best solution I found until now on the FAT file system is: > fwrite without splitting the frames. So basically I have a loop for every video file where the software writes a frame at a time. > > Thanks for your help, > ciao Daniele Just thinking about it, if you have a cbr h264 stream with 1 Mb bitrate you are writing 128 KB/s, or an average of 5 KB/s per frame. So you are writing 5 KB of data, 25 times a second. Grand total for two threads: 50 writes/s just to write 256KB! You can add logic to batch writes based on a threshold, but i think it's much more simple to try to disable sync and fflush every X seconds (even 1 or 2). Ciao, Enrico From marlon.smith10 at gmail.com Tue Jul 12 11:48:38 2011 From: marlon.smith10 at gmail.com (Marlon Smith) Date: Tue, 12 Jul 2011 09:48:38 -0700 Subject: NAND support in U-Boot In-Reply-To: <4E1C2C1F.1040907@acn-group.ch> References: <4E1ADD6E.5040501@mlbassoc.com> <4E1C2C1F.1040907@acn-group.ch> Message-ID: <1310489318.3053.2.camel@marlon-laptop> Hi Christophe, Sorry to hijack this thread, but it sounds like you have done what I am trying to do. I would like to be able to boot a DM365 board off of an SD card directly into U-boot, which it sounds like you have done just over SPI instead of SD. There is no NAND on the board I'm using. Can you point me a direction where I can find more information on how to do this? Thanks Marlon On Tue, 2011-07-12 at 13:12 +0200, Christophe Aeschlimann wrote: > Hi Gary, > > On 11.07.2011 13:24, Gary Thomas wrote: > > Note: I'm not sure the best place to ask this question as it's very > > Davinci specific. > > > > Has anyone worked on the NAND drivers in U-Boot for the Davinci chips? > > In particular the OMAP-L138? It seems that the U-Boot drivers in use > > only use software ECC which is incompatible with the hardware ECC engine > > on the device. My board is booting from NAND and if I use U-Boot to > > update the U-Boot image in NAND, the ECC information is incorrect and > > the board stops booting. > > I don't think the problem is HW ECC vs SW ECC the problem is where the > ECC bits are stored in the Out-Of-Band (OOB) area of the NAND. > > The ROM boot loader (RBL) expects them to be at a certain position where > U-BOOT and the Linux Kernel have them stored in another place. > > More info here : > http://processors.wiki.ti.com/index.php/DM365_Nand_ECC_layout > > A solution is to have a small dedicated partition that has the same ECC > layout as the Rom Boot Loader that contain a very basic bootloader (e.g. > TI's UBL) which then let you jump to U-Boot. > > Having the RBL loading UBL to load U-Boot to finally start Linux was too > cumbersome for us so we just boot U-Boot directly from a SPI flash. > > > Any ideas/help? Should I ask this on a different list (please don't > > say e2e forums...) > > Apart from the terrible user experience the site offers the persons > active on these forums are very helpful. > > > Thanks > > Hope it helped, > > Best regards, > -------------- next part -------------- An HTML attachment was scrubbed... URL: From sakari.ailus at iki.fi Tue Jul 12 16:22:44 2011 From: sakari.ailus at iki.fi ('Sakari Ailus') Date: Wed, 13 Jul 2011 00:22:44 +0300 Subject: [ RFC PATCH 0/8] RFC for Media Controller capture driver for DM365 In-Reply-To: References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> <20110630135736.GK12671@valkosipuli.localdomain> <201107041522.37437.laurent.pinchart@ideasonboard.com> <4E11E695.9090508@iki.fi> Message-ID: <20110712212244.GJ22072@valkosipuli.localdomain> On Tue, Jul 12, 2011 at 05:31:52PM +0530, Hadli, Manjunath wrote: > > Sakari/Laurent, > Did you get some time to review the code? Will look forward for your comments. > -Manju Hi Manju, I'll try to review this in near future. -- Sakari Ailus sakari.ailus at iki.fi From ido at wizery.com Tue Jul 12 17:19:15 2011 From: ido at wizery.com (Ido Yariv) Date: Wed, 13 Jul 2011 01:19:15 +0300 Subject: [PATCH v3 1/6] arm: davinci: Fix low level gpio irq handlers' argument In-Reply-To: References: <1310418191-27009-1-git-send-email-ido@wizery.com> Message-ID: <20110712221915.GD5633@WorkStation> Hi Sekhar, On Tue, Jul 12, 2011 at 02:52:17PM +0530, Nori, Sekhar wrote: > > - g = (__force struct davinci_gpio_regs __iomem *) irq_desc_get_handler_data(desc); > > + ctl = (struct davinci_gpio_controller *)irq_desc_get_handler_data(desc); > > + g = (struct davinci_gpio_regs __iomem *)ctl->regs; > > > > /* we only care about one bank */ > > if (irq & 1) > > @@ -278,7 +280,7 @@ gpio_irq_handler(unsigned irq, struct irq_desc *desc) > > status >>= 16; > > > > /* now demux them to the right lowlevel handler */ > > - n = (int)irq_get_handler_data(irq); > > + n = ctl->irq_base; > > I realized that this breaks for odd banks as the status is > right shifted by 16. The GPIO you are using must have been > in even bank? You're absolutely right, I missed that. And yes, I have been using an even bank GPIO. > > while (status) { > > res = ffs(status); > > n += res; > > @@ -424,7 +426,13 @@ static int __init davinci_gpio_irq_setup(void) > > > > /* set up all irqs in this bank */ > > irq_set_chained_handler(bank_irq, gpio_irq_handler); > > - irq_set_handler_data(bank_irq, (__force void *)g); > > + > > + /* > > + * Each chip handles 32 gpios, and each irq bank consists of 16 > > + * gpio irqs. Pass the irq bank's corresponding controller to > > + * the chained irq handler. > > + */ > > + irq_set_handler_data(bank_irq, &chips[bank * 16 / 32]); > > This can simply be: > > irq_set_handler_data(bank_irq, &chips[gpio / 32]); > > In the interest of time, I did these fixes and pushed the > patch to "fixes" branch of git://gitorious.org/linux-davinci/linux-davinci.git > > Can you please test it out and let me know if it works. This patch seems to work just fine. I'm afraid I can't test an odd bank GPIO here to verify that this indeed fixed the issue you raised, but it looks correct. Thanks, Ido. From toddpoynor at google.com Tue Jul 12 17:31:50 2011 From: toddpoynor at google.com (Todd Poynor) Date: Tue, 12 Jul 2011 15:31:50 -0700 Subject: [PATCH 1/4] ARM: davinci: Check for NULL return from irq_alloc_generic_chip Message-ID: <1310509910-4244-1-git-send-email-toddpoynor@google.com> Signed-off-by: Todd Poynor --- arch/arm/mach-davinci/irq.c | 7 +++++++ 1 files changed, 7 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/irq.c b/arch/arm/mach-davinci/irq.c index bfe68ec..ab7f688 100644 --- a/arch/arm/mach-davinci/irq.c +++ b/arch/arm/mach-davinci/irq.c @@ -52,6 +52,13 @@ davinci_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) struct irq_chip_type *ct; gc = irq_alloc_generic_chip("AINTC", 1, irq_start, base, handle_edge_irq); + + if (!gc) { + pr_err("%s: irq_alloc_generic_chip for IRQ %u failed\n", + __func__, irq_start); + return; + } + ct = gc->chip_types; ct->chip.irq_ack = irq_gc_ack; ct->chip.irq_mask = irq_gc_mask_clr_bit; -- 1.7.3.1 From nsekhar at ti.com Wed Jul 13 00:34:16 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 13 Jul 2011 11:04:16 +0530 Subject: [GIT PULL] DaVinci fix for v3.0 In-Reply-To: References: Message-ID: On Wed, Jul 13, 2011 at 10:59:47, Nori, Sekhar wrote: > Hi Arnd, > > Can you please pull this late breaking DaVinci fix for v3.0? > This is fallout of generic irq chip migration and prevents GPIO > interrupts from functioning correctly. Resent with fixed DaVinci Linux mailing list address. Thanks, Sekhar > > The following changes since commit 620917de59eeb934b9f8cf35cc2d95c1ac8ed0fc: > Linus Torvalds (1): > Linux 3.0-rc7 > > are available in the git repository at: > > git://gitorious.org/linux-davinci/linux-davinci.git fixes > > Ido Yariv (1): > arm: davinci: Fix low level gpio irq handlers' argument > > arch/arm/mach-davinci/gpio.c | 21 ++++++++++++++++----- > 1 files changed, 16 insertions(+), 5 deletions(-) > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > From lethal at linux-sh.org Wed Jul 13 03:18:43 2011 From: lethal at linux-sh.org (Paul Mundt) Date: Wed, 13 Jul 2011 17:18:43 +0900 Subject: [PATCH v2] video: da8xx-fb: Interrupt configuration of revised LCDC IP In-Reply-To: <1309861280-32240-1-git-send-email-prakash.pm@ti.com> References: <1309861280-32240-1-git-send-email-prakash.pm@ti.com> Message-ID: <20110713081843.GE13701@linux-sh.org> On Tue, Jul 05, 2011 at 03:51:20PM +0530, Manjunathappa, Prakash wrote: > An upcoming SoC of TI comes with an LCD controller which is an updated > version of that found on TI's DA850 SoC. The da8xx-fb driver can support > this LCD with some enhancements. > This patch adds support for updated interrupt configuration on the new > SoC. > 1) Registers for setting and clearing interrupts are different. > 2) Raw and masked status registers are different. > The updates have been tested on an emulation platform for new features > and the patch has been tested on DA850 platform to make sure nothing > existing breaks. > > Signed-off-by: Manjunathappa, Prakash Applied, thanks. From sshtylyov at mvista.com Wed Jul 13 05:46:24 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 13 Jul 2011 14:46:24 +0400 Subject: [PATCH 1/4] ARM: davinci: Check for NULL return from irq_alloc_generic_chip In-Reply-To: <1310509910-4244-1-git-send-email-toddpoynor@google.com> References: <1310509910-4244-1-git-send-email-toddpoynor@google.com> Message-ID: <4E1D7780.9050505@mvista.com> Hello. On 13-07-2011 2:31, Todd Poynor wrote: > Signed-off-by: Todd Poynor > --- > arch/arm/mach-davinci/irq.c | 7 +++++++ > 1 files changed, 7 insertions(+), 0 deletions(-) > diff --git a/arch/arm/mach-davinci/irq.c b/arch/arm/mach-davinci/irq.c > index bfe68ec..ab7f688 100644 > --- a/arch/arm/mach-davinci/irq.c > +++ b/arch/arm/mach-davinci/irq.c > @@ -52,6 +52,13 @@ davinci_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) > struct irq_chip_type *ct; > > gc = irq_alloc_generic_chip("AINTC", 1, irq_start, base, handle_edge_irq); > + Remove this empty line please. > + if (!gc) { > + pr_err("%s: irq_alloc_generic_chip for IRQ %u failed\n", > + __func__, irq_start); > + return; > + } > + > ct = gc->chip_types; > ct->chip.irq_ack = irq_gc_ack; > ct->chip.irq_mask = irq_gc_mask_clr_bit; WBR, Sergei From prakash.pm at ti.com Wed Jul 13 05:52:13 2011 From: prakash.pm at ti.com (Manjunathappa, Prakash) Date: Wed, 13 Jul 2011 16:22:13 +0530 Subject: [PATCH v2] video: da8xx-fb: Increased resolution configuration of revised LCDC IP Message-ID: <1310554333-25242-1-git-send-email-prakash.pm@ti.com> Revised LCD controller in upcoming TI SoC which is an updated version of LCDC IP that was found on TI's DA850 SoC supports 2048*2048 resolution. Below are the encoding details: Width: Pixels Per Line = {pplmsb, ppllsb, 4'b1111} + 1 Where pplmsb:1bit==>Raster Timing0[3], ppllsb:6bits==>Raster Timing0[9:4]. And encoded value can range from 16 to 2048 in multiples of 16. Height: Lines Per Panel = {lpp_b10, lpp} Where lpp:10bits==>Raster Timing1[9:0], lpp_b10:1bit==>Raster Timing2[26]. And encoded value can range from 1 to 2048, programmable range is 0 to 2047. Patch is verified on emulation platform of upcoming SoC for updated feature and on DA850 platform to make sure nothing existing breaks. Signed-off-by: Manjunathappa, Prakash --- Since v1: 1)Fixed the bug in configuration of lpp_b10 in Raster Timing2[26] register. 2)Reframed commit message. drivers/video/da8xx-fb.c | 31 ++++++++++++++++++++++++++++--- 1 files changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index 620f1c3..19ce407 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -460,18 +460,43 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, /* Set the Panel Width */ /* Pixels per line = (PPL + 1)*16 */ - /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/ - width &= 0x3f0; + if (lcd_revision == LCD_VERSION_1) { + /* + * 0x3F in bits 4..9 gives max horizontal resolution = 1024 + * pixels. + */ + width &= 0x3f0; + } else { + /* + * 0x7F in bits 3..9 gives max horizontal resolution = 2048 + * pixels. + */ + width &= 0x7f0; + } + reg = lcdc_read(LCD_RASTER_TIMING_0_REG); reg &= 0xfffffc00; - reg |= ((width >> 4) - 1) << 4; + if (lcd_revision == LCD_VERSION_1) { + reg |= ((width >> 4) - 1) << 4; + } else { + width = ((width >> 4) - 1); + reg |= ((width & 0x3f) << 4) | ((width & 0x40) >> 3); + } lcdc_write(reg, LCD_RASTER_TIMING_0_REG); /* Set the Panel Height */ + /* Set bits 9:0 of Lines Per Pixel */ reg = lcdc_read(LCD_RASTER_TIMING_1_REG); reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00); lcdc_write(reg, LCD_RASTER_TIMING_1_REG); + /* Set bit 10 of Lines Per Pixel */ + if (lcd_revision == LCD_VERSION_2) { + reg = lcdc_read(LCD_RASTER_TIMING_2_REG); + reg |= (((height - 1) & 0x400) << 16); + lcdc_write(reg, LCD_RASTER_TIMING_2_REG); + } + /* Set the Raster Order of the Frame Buffer */ reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(1 << 8); if (raster_order) -- 1.7.1 From gary at mlbassoc.com Wed Jul 13 07:48:49 2011 From: gary at mlbassoc.com (Gary Thomas) Date: Wed, 13 Jul 2011 06:48:49 -0600 Subject: NAND support in U-Boot In-Reply-To: <4E1C2C1F.1040907@acn-group.ch> References: <4E1ADD6E.5040501@mlbassoc.com> <4E1C2C1F.1040907@acn-group.ch> Message-ID: <4E1D9431.2040403@mlbassoc.com> On 07/12/2011 05:12 AM, Christophe Aeschlimann wrote: > Hi Gary, > > On 11.07.2011 13:24, Gary Thomas wrote: >> Note: I'm not sure the best place to ask this question as it's very >> Davinci specific. >> >> Has anyone worked on the NAND drivers in U-Boot for the Davinci chips? >> In particular the OMAP-L138? It seems that the U-Boot drivers in use >> only use software ECC which is incompatible with the hardware ECC engine >> on the device. My board is booting from NAND and if I use U-Boot to >> update the U-Boot image in NAND, the ECC information is incorrect and >> the board stops booting. > > I don't think the problem is HW ECC vs SW ECC the problem is where the > ECC bits are stored in the Out-Of-Band (OOB) area of the NAND. > > The ROM boot loader (RBL) expects them to be at a certain position where > U-BOOT and the Linux Kernel have them stored in another place. > > More info here : > http://processors.wiki.ti.com/index.php/DM365_Nand_ECC_layout > > A solution is to have a small dedicated partition that has the same ECC > layout as the Rom Boot Loader that contain a very basic bootloader (e.g. > TI's UBL) which then let you jump to U-Boot. I'm using the RBL->UBL->U-Boot method already. The information in the link above looks like it will help, I'm testing it now. > > Having the RBL loading UBL to load U-Boot to finally start Linux was too > cumbersome for us so we just boot U-Boot directly from a SPI flash. > >> Any ideas/help? Should I ask this on a different list (please don't >> say e2e forums...) > > Apart from the terrible user experience the site offers the persons > active on these forums are very helpful. Precisely; the horrible user experience keeps me away - I don't have time to constantly check/scan the forums for information I need. I find mailing lists, such as this one, a much better approach. Thanks -- ------------------------------------------------------------ Gary Thomas | Consulting for the MLB Associates | Embedded world ------------------------------------------------------------ From cbouatmailru at gmail.com Wed Jul 13 09:46:24 2011 From: cbouatmailru at gmail.com (Anton Vorontsov) Date: Wed, 13 Jul 2011 18:46:24 +0400 Subject: [RFC/RFT 2/2] davinci: use generic memory mapped gpio for tnetv107x In-Reply-To: <20110706220240.GF5371@ponder.secretlab.ca> References: <6639b07562e3e6643dd07d5ed3907cb5158ce16b.1309840042.git.nsekhar@ti.com> <20110706220240.GF5371@ponder.secretlab.ca> Message-ID: <20110713144624.GA7875@oksana.dev.rtsoft.ru> Hi Grant, On Wed, Jul 06, 2011 at 04:02:40PM -0600, Grant Likely wrote: [...] > > + { > > + .name = "dat", > > + .start = TNETV107X_GPIO_BASE + 0x4, > > + .end = TNETV107X_GPIO_BASE + 0x4 + 0x4 - 1, > > + }, > > + { > > + .name = "set", > > + .start = TNETV107X_GPIO_BASE + 0x10, > > + .end = TNETV107X_GPIO_BASE + 0x10 + 0x4 - 1, > > + }, > > + { > > + .name = "dirin", > > + .start = TNETV107X_GPIO_BASE + 0x1c, > > + .end = TNETV107X_GPIO_BASE + 0x1c + 0x4 - 1, > > + }, > > + { > > + .name = "en", > > + .start = TNETV107X_GPIO_BASE + 0x28, > > + .end = TNETV107X_GPIO_BASE + 0x28 + 0x4 - 1, > > + }, > > + }, > > +}; > > Wow, this ends up looking horrible. (yes, I know it is not your > fault). I backed off earlier on using resources for the offsets, but > I want to change my mind again and make interface a register range + > offsets to the control registers. Why is this horrible? Are you proposing a single resource + platform data for the offsets? If so, this won't look any better, but in return - this would complicate device registration logic and driver logic itself (i.e. we need to allocate platform data in the arch code, then parse and store the structure in the driver). The platform data is simply unnecessary -- we have resources that describe memory just fine, much better then raw 'unsigned long offset'; - we lose the ability to operate on spread registers (think of "enable" register is down below 2MB gap, near the pinmux registers block); - In the device tree, we really want to describe registers in the regs = <> property, because that's where memory resources should be. (We also want to map address position into resource name, but that's different story). Thanks, -- Anton Vorontsov Email: cbouatmailru at gmail.com From sakari.ailus at iki.fi Wed Jul 13 13:50:50 2011 From: sakari.ailus at iki.fi (Sakari Ailus) Date: Wed, 13 Jul 2011 21:50:50 +0300 Subject: [RFC PATCH 1/8] davinci: vpfe: add dm3xx IPIPEIF hardware support module In-Reply-To: <1309439597-15998-2-git-send-email-manjunath.hadli@ti.com> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> <1309439597-15998-2-git-send-email-manjunath.hadli@ti.com> Message-ID: <20110713185050.GC27451@valkosipuli.localdomain> Hi Manju, Thanks for the patchset! I have a few comments on this patch below. I haven't read the rest of the patches yet so I may have more comments on this one when I do that. On Thu, Jun 30, 2011 at 06:43:10PM +0530, Manjunath Hadli wrote: > add support for dm3xx IPIPEIF hardware setup. This is the > lowest software layer for the dm3x vpfe driver which directly > accesses hardware. Add support for features like default > pixel correction, dark frame substraction and hardware setup. > > Signed-off-by: Manjunath Hadli > Signed-off-by: Nagabhushana Netagunte > --- > drivers/media/video/davinci/dm3xx_ipipeif.c | 368 +++++++++++++++++++++++++++ > include/media/davinci/dm3xx_ipipeif.h | 292 +++++++++++++++++++++ > 2 files changed, 660 insertions(+), 0 deletions(-) > create mode 100644 drivers/media/video/davinci/dm3xx_ipipeif.c > create mode 100644 include/media/davinci/dm3xx_ipipeif.h > > diff --git a/drivers/media/video/davinci/dm3xx_ipipeif.c b/drivers/media/video/davinci/dm3xx_ipipeif.c > new file mode 100644 > index 0000000..36cb61b > --- /dev/null > +++ b/drivers/media/video/davinci/dm3xx_ipipeif.c > @@ -0,0 +1,368 @@ > +/* > +* Copyright (C) 2011 Texas Instruments Inc > +* > +* 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 in the hope that it will be useful, > +* but WITHOUT ANY WARRANTY; without even the implied warranty of > +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +* GNU General Public License for more details. > +* > +* You should have received a copy of the GNU General Public License > +* along with this program; if not, write to the Free Software > +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > +* > +* ipipe module to hold common functionality across DM355 and DM365 > +*/ > +#include > +#include > +#include > +#include > +#include > +#include > + > +#define DM355 0 > +#define DM365 1 > + > +static void *__iomem ipipeif_base_addr; This looks device specific. What about using dev_set/get_drvdata and remove this one? > +static int device_type; Ditto. Both should be in a device specific struct. > +static inline u32 regr_if(u32 offset) > +{ > + return readl(ipipeif_base_addr + offset); > +} > + > +static inline void regw_if(u32 val, u32 offset) > +{ > + writel(val, ipipeif_base_addr + offset); > +} > + > +void ipipeif_set_enable(char en, unsigned int mode) > +{ > + regw_if(1, IPIPEIF_ENABLE); > +} > + > +u32 ipipeif_get_enable(void) > +{ > + return regr_if(IPIPEIF_ENABLE); > +} > + > +int ipipeif_set_address(struct ipipeif *params, unsigned int address) > +{ If address is a value for a register you should use u32. > + unsigned int val1; > + unsigned int val; > + > + if (params->source != 0) { > + val = ((params->adofs >> 5) & IPIPEIF_ADOFS_LSB_MASK); > + regw_if(val, IPIPEIF_ADOFS); You may do without val as well. > + /* lower sixteen bit */ > + val = ((address >> IPIPEIF_ADDRL_SHIFT) & IPIPEIF_ADDRL_MASK); > + regw_if(val, IPIPEIF_ADDRL); > + > + /* upper next seven bit */ > + val1 = > + ((address >> IPIPEIF_ADDRU_SHIFT) & IPIPEIF_ADDRU_MASK); > + regw_if(val1, IPIPEIF_ADDRU); > + } else > + return -1; What's -1? If this is an error, Exxxxx codes should be used. The error check should become first and the rest of the function may be unindented by one tab stop. > + return 0; > +} > + > +static void ipipeif_config_dpc(struct ipipeif_dpc *dpc) > +{ > + u32 val; > + > + if (dpc->en) { > + val = ((dpc->en & 1) << IPIPEIF_DPC2_EN_SHIFT); > + val |= (dpc->thr & IPIPEIF_DPC2_THR_MASK); > + } else > + val = 0; > + > + regw_if(val, IPIPEIF_DPC2); > +} > + > +/* This function sets up IPIPEIF and is called from > + * ipipe_hw_setup() > + */ > +int ipipeif_hw_setup(struct ipipeif *params) > +{ > + enum v4l2_mbus_pixelcode isif_port_if; > + unsigned int val1 = 0x7; 7 looks like a magic number. > + unsigned int val; > + > + if (NULL == params) > + return -1; Same here, and I can also see elsewhere. > + /* Enable clock to IPIPEIF and IPIPE */ > + if (device_type == DM365) > + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); > + > + /* Combine all the fields to make CFG1 register of IPIPEIF */ > + val = params->mode << ONESHOT_SHIFT; > + val |= params->source << INPSRC_SHIFT; > + val |= params->clock_select << CLKSEL_SHIFT; > + val |= params->avg_filter << AVGFILT_SHIFT; > + val |= params->decimation << DECIM_SHIFT; > + > + if (device_type == DM355) { > + val |= params->var.if_base.ialaw << IALAW_SHIFT; > + val |= params->var.if_base.pack_mode << PACK8IN_SHIFT; > + val |= params->var.if_base.clk_div << CLKDIV_SHIFT; > + val |= params->var.if_base.data_shift << DATASFT_SHIFT; > + } else { > + /* DM365 IPIPE 5.1 */ > + val |= params->var.if_5_1.pack_mode << PACK8IN_SHIFT; > + val |= params->var.if_5_1.source1 << INPSRC1_SHIFT; > + if (params->source != SDRAM_YUV) > + val |= params->var.if_5_1.data_shift << DATASFT_SHIFT; > + else > + val &= (~(val1 << DATASFT_SHIFT)); > + } > + regw_if(val, IPIPEIF_GFG1); > + > + switch (params->source) { > + case CCDC: > + { > + regw_if(params->gain, IPIPEIF_GAIN); > + break; > + } Braces aren't needed here. > + case SDRAM_RAW: > + case CCDC_DARKFM: > + { > + regw_if(params->gain, IPIPEIF_GAIN); > + /* fall through */ > + } Ditto. > + case SDRAM_YUV: > + { > + val |= params->var.if_5_1.data_shift << DATASFT_SHIFT; > + regw_if(params->glob_hor_size, IPIPEIF_PPLN); > + regw_if(params->glob_ver_size, IPIPEIF_LPFR); > + regw_if(params->hnum, IPIPEIF_HNUM); > + regw_if(params->vnum, IPIPEIF_VNUM); > + > + break; > + } > + default: > + /* Do nothing */ > + ; Is this not an error? > + } > + > + /*check if decimation is enable or not */ > + if (params->decimation) > + regw_if(params->rsz, IPIPEIF_RSZ); > + > + if (device_type == DM365) { You can do an opposite check and then return if it's true. By removing the brackes from cases you can unindent this by two tab stops. The function is also very long. > + /* Setup sync alignment and initial rsz position */ > + val = params->var.if_5_1.align_sync & 1; > + val <<= IPIPEIF_INIRSZ_ALNSYNC_SHIFT; > + val |= (params->var.if_5_1.rsz_start & IPIPEIF_INIRSZ_MASK); > + regw_if(val, IPIPEIF_INIRSZ); > + > + /* Enable DPCM decompression */ > + switch (params->source) { > + case SDRAM_RAW: > + { > + val = 0; > + if (params->var.if_5_1.dpcm.en) { > + val = params->var.if_5_1.dpcm.en & 1; > + val |= (params->var.if_5_1.dpcm.type > + & 1) > + << IPIPEIF_DPCM_BITS_SHIFT; > + val |= > + (params->var.if_5_1.dpcm.pred & 1) > + << IPIPEIF_DPCM_PRED_SHIFT; > + } > + regw_if(val, IPIPEIF_DPCM); > + > + /* set DPC */ > + ipipeif_config_dpc(¶ms->var.if_5_1.dpc); > + > + regw_if(params->var.if_5_1.clip, IPIPEIF_OCLIP); > + /* fall through for SDRAM YUV mode */ > + isif_port_if = > + params->var.if_5_1.isif_port.if_type; > + /* configure CFG2 */ > + switch (isif_port_if) { > + case V4L2_MBUS_FMT_YUYV8_1X16: > + val |= > + (0 << > + IPIPEIF_CFG2_YUV8_SHIFT); This has no effect. > + val |= > + (1 << > + IPIPEIF_CFG2_YUV16_SHIFT); > + regw_if(val, IPIPEIF_CFG2); > + break; > + default: > + val |= > + (0 << > + IPIPEIF_CFG2_YUV8_SHIFT); > + val |= > + (0 << > + IPIPEIF_CFG2_YUV16_SHIFT); Neither do the above two statements. > + regw_if(val, IPIPEIF_CFG2); > + break; > + } > + } > + case SDRAM_YUV: > + { > + /* Set clock divider */ > + if (params->clock_select == SDRAM_CLK) { > + val |= > + ((params->var.if_5_1.clk_div.m - 1) > + << IPIPEIF_CLKDIV_M_SHIFT); > + val |= > + (params->var.if_5_1.clk_div.n - 1); > + regw_if(val, IPIPEIF_CLKDIV); > + } > + > + break; > + } > + case CCDC: > + case CCDC_DARKFM: > + { > + /* set DPC */ > + ipipeif_config_dpc(¶ms->var.if_5_1.dpc); > + > + /* Set DF gain & threshold control */ > + val = 0; > + if (params->var.if_5_1.df_gain_en) { > + val = (params->var.if_5_1.df_gain_thr > + & IPIPEIF_DF_GAIN_THR_MASK); > + regw_if(val, IPIPEIF_DFSGTH); > + val = ((params->var.if_5_1.df_gain_en > + & 1) > + << IPIPEIF_DF_GAIN_EN_SHIFT); > + val |= (params->var.if_5_1.df_gain > + & IPIPEIF_DF_GAIN_MASK); > + } > + regw_if(val, IPIPEIF_DFSGVL); > + isif_port_if = > + params->var.if_5_1.isif_port.if_type; Indentation. > + > + /* configure CFG2 */ > + val = > + params->var.if_5_1.isif_port.hdpol > + << IPIPEIF_CFG2_HDPOL_SHIFT; > + val |= > + params->var.if_5_1.isif_port.vdpol > + << IPIPEIF_CFG2_VDPOL_SHIFT; > + switch (isif_port_if) { > + case V4L2_MBUS_FMT_YUYV8_1X16: > + case V4L2_MBUS_FMT_YUYV10_1X20: > + { > + val |= > + (0 << > + IPIPEIF_CFG2_YUV8_SHIFT); > + val |= > + (1 << > + IPIPEIF_CFG2_YUV16_SHIFT); It might make sense to use #defines with descriptive names rather than zeros and ones. > + break; > + } > + case V4L2_MBUS_FMT_YUYV8_2X8: > + case V4L2_MBUS_FMT_Y8_1X8: > + case V4L2_MBUS_FMT_YUYV10_2X10: > + { > + val |= > + (1 << > + IPIPEIF_CFG2_YUV8_SHIFT); > + val |= > + (1 << > + IPIPEIF_CFG2_YUV16_SHIFT); > + val |= > + ((params->var.if_5_1. > + pix_order) > + << > + IPIPEIF_CFG2_YUV8P_SHIFT); > + break; > + } > + default: > + { > + /* Bayer */ > + regw_if(params->var.if_5_1.clip, > + IPIPEIF_OCLIP); > + val |= > + (0 << > + IPIPEIF_CFG2_YUV16_SHIFT); > + } > + } > + regw_if(val, IPIPEIF_CFG2); > + break; > + } > + default: > + /* do nothing */ > + ; > + } > + } > + return 0; > +} > + > +static int __devinit dm3xx_ipipeif_probe(struct platform_device *pdev) > +{ > + static resource_size_t res_len; > + struct resource *res; > + int status; > + > + if (NULL != pdev->dev.platform_data) Lvalue would be better right for readability. > + device_type = DM365; > + > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!res) > + return -ENOENT; > + > + res_len = res->end - res->start + 1; > + > + res = request_mem_region(res->start, res_len, res->name); > + if (!res) > + return -EBUSY; > + > + ipipeif_base_addr = ioremap_nocache(res->start, res_len); > + if (!ipipeif_base_addr) { > + status = -EBUSY; > + goto fail; > + } > + return 0; > + > +fail: > + release_mem_region(res->start, res_len); > + > + return status; > +} > + > +static int dm3xx_ipipeif_remove(struct platform_device *pdev) > +{ > + struct resource *res; > + > + iounmap(ipipeif_base_addr); > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (res) > + release_mem_region(res->start, res->end - res->start + 1); > + return 0; > +} > + > +static struct platform_driver dm3xx_ipipeif_driver = { > + .driver = { > + .name = "dm3xx_ipipeif", > + .owner = THIS_MODULE, > + }, > + .remove = __devexit_p(dm3xx_ipipeif_remove), > + .probe = dm3xx_ipipeif_probe, > +}; > + > +static int dm3xx_ipipeif_init(void) > +{ > + return platform_driver_register(&dm3xx_ipipeif_driver); > +} > + > +static void dm3xx_ipipeif_exit(void) > +{ > + platform_driver_unregister(&dm3xx_ipipeif_driver); > +} > + > +module_init(dm3xx_ipipeif_init); > +module_exit(dm3xx_ipipeif_exit); > + > +MODULE_LICENSE("GPL2"); > diff --git a/include/media/davinci/dm3xx_ipipeif.h b/include/media/davinci/dm3xx_ipipeif.h > new file mode 100644 > index 0000000..87389ff > --- /dev/null > +++ b/include/media/davinci/dm3xx_ipipeif.h > @@ -0,0 +1,292 @@ > +/* > +* Copyright (C) 2011 Texas Instruments Inc > +* > +* 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 in the hope that it will be useful, > +* but WITHOUT ANY WARRANTY; without even the implied warranty of > +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > +* GNU General Public License for more details. > +* > +* You should have received a copy of the GNU General Public License > +* along with this program; if not, write to the Free Software > +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA > +*/ > + > +#ifndef _DM3XX_IPIPEIF_H > +#define _DM3XX_IPIPEIF_H > +/* Used to shift input image data based on the data lines connected > + * to parallel port > + */ > +/* IPIPE base specific types */ > +enum ipipeif_data_shift { > + IPIPEIF_BITS15_2, > + IPIPEIF_BITS14_1, > + IPIPEIF_BITS13_0, > + IPIPEIF_BITS12_0, > + IPIPEIF_BITS11_0, > + IPIPEIF_BITS10_0, > + IPIPEIF_BITS9_0 > +}; > + > +enum ipipeif_clkdiv { > + IPIPEIF_DIVIDE_HALF, > + IPIPEIF_DIVIDE_THIRD, > + IPIPEIF_DIVIDE_FOURTH, > + IPIPEIF_DIVIDE_FIFTH, > + IPIPEIF_DIVIDE_SIXTH, > + IPIPEIF_DIVIDE_EIGHTH, > + IPIPEIF_DIVIDE_SIXTEENTH, > + IPIPEIF_DIVIDE_THIRTY > +}; > + > +/* IPIPE 5.1 interface types */ > +/* dpcm predicator for IPIPE 5.1 */ > +enum ipipeif_dpcm_pred { > + DPCM_SIMPLE_PRED, > + DPCM_ADV_PRED > +}; > +/* data shift for IPIPE 5.1 */ > +enum ipipeif_5_1_data_shift { > + IPIPEIF_5_1_BITS11_0, > + IPIPEIF_5_1_BITS10_0, > + IPIPEIF_5_1_BITS9_0, > + IPIPEIF_5_1_BITS8_0, > + IPIPEIF_5_1_BITS7_0, > + IPIPEIF_5_1_BITS15_4, > +}; > + > +/* clockdiv for IPIPE 5.1 */ > +struct ipipeif_5_1_clkdiv { > + unsigned char m; > + unsigned char n; > +}; > + > +/* DPC at the if for IPIPE 5.1 */ > +struct ipipeif_dpc { > + /* 0 - disable, 1 - enable */ > + unsigned char en; > + /* threshold */ > + unsigned short thr; > +}; > + > +enum ipipeif_decimation { > + IPIPEIF_DECIMATION_OFF, > + IPIPEIF_DECIMATION_ON > +}; If these are used as register values they should be explicitly defined. > +enum ipipeif_pixel_order { > + IPIPEIF_CBCR_Y, > + IPIPEIF_Y_CBCR > +}; > + > +#ifdef __KERNEL__ This file is under include/media which isn't included by user space --- user space headers belong under include/linux. Interface and internal definitions should be in a separate header file. > +#include > +#include > +#include > +#include > +#include > + > +enum ipipeif_clock { > + PIXCEL_CLK, > + SDRAM_CLK IPIPEIF prefix here? > +}; > + > +enum ipipeif_pack_mode { > + IPIPEIF_PACK_16_BIT, > + IPIPEIF_PACK_8_BIT > +}; > + > +enum ipipe_oper_mode { > + CONTINUOUS, > + ONE_SHOT > +}; > + > +enum ipipeif_5_1_pack_mode { > + IPIPEIF_5_1_PACK_16_BIT, > + IPIPEIF_5_1_PACK_8_BIT, > + IPIPEIF_5_1_PACK_8_BIT_A_LAW, > + IPIPEIF_5_1_PACK_12_BIT > +}; > + > +enum ipipeif_avg_filter { > + AVG_OFF, > + AVG_ON > +}; > + > +enum ipipeif_input_source { > + CCDC, > + SDRAM_RAW, > + CCDC_DARKFM, > + SDRAM_YUV > +}; > + > +enum ipipeif_ialaw { > + ALAW_OFF, > + ALAW_ON > +}; > + > +struct ipipeif_base { > + enum ipipeif_ialaw ialaw; > + enum ipipeif_pack_mode pack_mode; > + enum ipipeif_data_shift data_shift; > + enum ipipeif_clkdiv clk_div; > +}; > + > +enum ipipeif_input_src1 { > + SRC1_PARALLEL_PORT, > + SRC1_SDRAM_RAW, > + SRC1_ISIF_DARKFM, > + SRC1_SDRAM_YUV > +}; > + > +enum ipipeif_dpcm_type { > + DPCM_8BIT_10BIT, > + DPCM_8BIT_12BIT > +}; > + > +struct ipipeif_dpcm_decomp { > + unsigned char en; > + enum ipipeif_dpcm_type type; > + enum ipipeif_dpcm_pred pred; > +}; > + > +enum ipipeif_dfs_dir { > + IPIPEIF_PORT_MINUS_SDRAM, > + IPIPEIF_SDRAM_MINUS_PORT > +}; > + > +struct ipipeif_5_1 { > + enum ipipeif_5_1_pack_mode pack_mode; > + enum ipipeif_5_1_data_shift data_shift; > + enum ipipeif_input_src1 source1; > + struct ipipeif_5_1_clkdiv clk_div; > + /* Defect pixel correction */ > + struct ipipeif_dpc dpc; > + /* DPCM decompression */ > + struct ipipeif_dpcm_decomp dpcm; > + /* ISIF port pixel order */ > + enum ipipeif_pixel_order pix_order; > + /* interface parameters from isif */ > + struct vpfe_hw_if_param isif_port; > + /* clipped to this value */ > + unsigned short clip; > + /* Align HSync and VSync to rsz_start */ > + unsigned char align_sync; > + /* resizer start position */ > + unsigned int rsz_start; > + /* DF gain enable */ > + unsigned char df_gain_en; > + /* DF gain value */ > + unsigned short df_gain; > + /* DF gain threshold value */ > + unsigned short df_gain_thr; > +}; > + > +/* ipipeif structures common to DM350 and DM365 used by ipipeif API */ > +struct ipipeif { > + enum ipipe_oper_mode mode; > + enum ipipeif_input_source source; > + enum ipipeif_clock clock_select; > + unsigned int glob_hor_size; > + unsigned int glob_ver_size; > + unsigned int hnum; > + unsigned int vnum; > + unsigned int adofs; > + unsigned char rsz; > + enum ipipeif_decimation decimation; > + enum ipipeif_avg_filter avg_filter; > + unsigned short gain; > + /* IPIPE 5.1 */ > + union var_part { > + struct ipipeif_base if_base; > + struct ipipeif_5_1 if_5_1; > + } var; > +}; > + > +/* IPIPEIF Register Offsets from the base address */ > +#define IPIPEIF_ENABLE (0x00) > +#define IPIPEIF_GFG1 (0x04) > +#define IPIPEIF_PPLN (0x08) > +#define IPIPEIF_LPFR (0x0C) > +#define IPIPEIF_HNUM (0x10) > +#define IPIPEIF_VNUM (0x14) > +#define IPIPEIF_ADDRU (0x18) > +#define IPIPEIF_ADDRL (0x1C) > +#define IPIPEIF_ADOFS (0x20) > +#define IPIPEIF_RSZ (0x24) > +#define IPIPEIF_GAIN (0x28) > + > +/* Below registers are available only on IPIPE 5.1 */ > +#define IPIPEIF_DPCM (0x2C) > +#define IPIPEIF_CFG2 (0x30) > +#define IPIPEIF_INIRSZ (0x34) > +#define IPIPEIF_OCLIP (0x38) > +#define IPIPEIF_DTUDF (0x3C) > +#define IPIPEIF_CLKDIV (0x40) > +#define IPIPEIF_DPC1 (0x44) > +#define IPIPEIF_DPC2 (0x48) > +#define IPIPEIF_DFSGVL (0x4C) > +#define IPIPEIF_DFSGTH (0x50) > +#define IPIPEIF_RSZ3A (0x54) > +#define IPIPEIF_INIRSZ3A (0x58) > +#define IPIPEIF_RSZ_MIN (16) > +#define IPIPEIF_RSZ_MAX (112) > +#define IPIPEIF_RSZ_CONST (16) > +#define SETBIT(reg, bit) (reg = ((reg) | ((0x00000001)<<(bit)))) > +#define RESETBIT(reg, bit) (reg = ((reg) & (~(0x00000001<<(bit))))) > + > +#define IPIPEIF_ADOFS_LSB_MASK (0x1FF) > +#define IPIPEIF_ADOFS_LSB_SHIFT (5) > +#define IPIPEIF_ADOFS_MSB_MASK (0x200) > +#define IPIPEIF_ADDRU_MASK (0x7FF) > +#define IPIPEIF_ADDRL_SHIFT (5) > +#define IPIPEIF_ADDRL_MASK (0xFFFF) > +#define IPIPEIF_ADDRU_SHIFT (21) > +#define IPIPEIF_ADDRMSB_SHIFT (31) > +#define IPIPEIF_ADDRMSB_LEFT_SHIFT (10) > + > +/* CFG1 Masks and shifts */ > +#define ONESHOT_SHIFT (0) > +#define DECIM_SHIFT (1) > +#define INPSRC_SHIFT (2) > +#define CLKDIV_SHIFT (4) > +#define AVGFILT_SHIFT (7) > +#define PACK8IN_SHIFT (8) > +#define IALAW_SHIFT (9) > +#define CLKSEL_SHIFT (10) > +#define DATASFT_SHIFT (11) > +#define INPSRC1_SHIFT (14) IPIPEIF prefix. Are these related to a particular register or a set of registers? > +/* DPC2 */ > +#define IPIPEIF_DPC2_EN_SHIFT (12) > +#define IPIPEIF_DPC2_THR_MASK (0xFFF) > +#define IPIPEIF_DF_GAIN_EN_SHIFT (10) > +#define IPIPEIF_DF_GAIN_MASK (0x3FF) > +#define IPIPEIF_DF_GAIN_THR_MASK (0xFFF) > +/* DPCM */ > +#define IPIPEIF_DPCM_BITS_SHIFT (2) > +#define IPIPEIF_DPCM_PRED_SHIFT (1) > +/* CFG2 */ > +#define IPIPEIF_CFG2_HDPOL_SHIFT (1) > +#define IPIPEIF_CFG2_VDPOL_SHIFT (2) > +#define IPIPEIF_CFG2_YUV8_SHIFT (6) > +#define IPIPEIF_CFG2_YUV16_SHIFT (3) > +#define IPIPEIF_CFG2_YUV8P_SHIFT (7) > + > +/* INIRSZ */ > +#define IPIPEIF_INIRSZ_ALNSYNC_SHIFT (13) > +#define IPIPEIF_INIRSZ_MASK (0x1FFF) > + > +/* CLKDIV */ > +#define IPIPEIF_CLKDIV_M_SHIFT 8 > + > +int ipipeif_set_address(struct ipipeif *if_params, unsigned int address); > +void ipipeif_set_enable(char en, unsigned int mode); > +int ipipeif_hw_setup(struct ipipeif *if_params); > +u32 ipipeif_get_enable(void); > + > +#endif > +#endif > -- > 1.6.2.4 > > -- > To unsubscribe from this list: send the line "unsubscribe linux-media" in > the body of a message to majordomo at vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html -- Sakari Ailus sakari.ailus at iki.fi From nsekhar at ti.com Thu Jul 14 00:20:38 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 14 Jul 2011 10:50:38 +0530 Subject: [PATCH 1/4] ARM: davinci: Check for NULL return from irq_alloc_generic_chip In-Reply-To: <1310509910-4244-1-git-send-email-toddpoynor@google.com> References: <1310509910-4244-1-git-send-email-toddpoynor@google.com> Message-ID: Hi Todd, On Wed, Jul 13, 2011 at 04:01:50, Todd Poynor wrote: > Signed-off-by: Todd Poynor Can you please include some patch description (even if it turns out to be a restatement of patch headline)? Also, in general, including some background on how you discovered the issue and how it affected you can help determine the priority of the fix. Thanks, Sekhar > --- > arch/arm/mach-davinci/irq.c | 7 +++++++ > 1 files changed, 7 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-davinci/irq.c b/arch/arm/mach-davinci/irq.c > index bfe68ec..ab7f688 100644 > --- a/arch/arm/mach-davinci/irq.c > +++ b/arch/arm/mach-davinci/irq.c > @@ -52,6 +52,13 @@ davinci_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) > struct irq_chip_type *ct; > > gc = irq_alloc_generic_chip("AINTC", 1, irq_start, base, handle_edge_irq); > + > + if (!gc) { > + pr_err("%s: irq_alloc_generic_chip for IRQ %u failed\n", > + __func__, irq_start); > + return; > + } > + > ct = gc->chip_types; > ct->chip.irq_ack = irq_gc_ack; > ct->chip.irq_mask = irq_gc_mask_clr_bit; > -- > 1.7.3.1 > > From sshtylyov at mvista.com Thu Jul 14 05:35:35 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 14 Jul 2011 14:35:35 +0400 Subject: [PATCH v2] video: da8xx-fb: Increased resolution configuration of revised LCDC IP In-Reply-To: <1310554333-25242-1-git-send-email-prakash.pm@ti.com> References: <1310554333-25242-1-git-send-email-prakash.pm@ti.com> Message-ID: <4E1EC677.7010305@mvista.com> Hello. On 13-07-2011 14:52, Manjunathappa, Prakash wrote: > Revised LCD controller in upcoming TI SoC which is an updated version of > LCDC IP that was found on TI's DA850 SoC supports 2048*2048 resolution. > Below are the encoding details: > Width: > Pixels Per Line = {pplmsb, ppllsb, 4'b1111} + 1 > Where pplmsb:1bit==>Raster Timing0[3], ppllsb:6bits==>Raster Timing0[9:4]. > And encoded value can range from 16 to 2048 in multiples of 16. > Height: > Lines Per Panel = {lpp_b10, lpp} > Where lpp:10bits==>Raster Timing1[9:0], lpp_b10:1bit==>Raster Timing2[26]. > And encoded value can range from 1 to 2048, programmable range is 0 to > 2047. > Patch is verified on emulation platform of upcoming SoC for updated > feature and on DA850 platform to make sure nothing existing breaks. > Signed-off-by: Manjunathappa, Prakash > --- > Since v1: > 1)Fixed the bug in configuration of lpp_b10 in Raster Timing2[26] register. > 2)Reframed commit message. > drivers/video/da8xx-fb.c | 31 ++++++++++++++++++++++++++++--- > 1 files changed, 28 insertions(+), 3 deletions(-) > diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c > index 620f1c3..19ce407 100644 > --- a/drivers/video/da8xx-fb.c > +++ b/drivers/video/da8xx-fb.c > @@ -460,18 +460,43 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, > > /* Set the Panel Width */ > /* Pixels per line = (PPL + 1)*16 */ > - /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/ > - width&= 0x3f0; > + if (lcd_revision == LCD_VERSION_1) { > + /* > + * 0x3F in bits 4..9 gives max horizontal resolution = 1024 > + * pixels. > + */ > + width&= 0x3f0; > + } else { > + /* > + * 0x7F in bits 3..9 gives max horizontal resolution = 2048 Maybe bits 4..10? > + * pixels. > + */ > + width&= 0x7f0; > + } > + > reg = lcdc_read(LCD_RASTER_TIMING_0_REG); > reg &= 0xfffffc00; > - reg |= ((width >> 4) - 1) << 4; > + if (lcd_revision == LCD_VERSION_1) { > + reg |= ((width>> 4) - 1)<< 4; > + } else { > + width = ((width>> 4) - 1); Most outer parens not needed. > + reg |= ((width & 0x3f) << 4) | ((width& 0x40) >> 3); > + } > lcdc_write(reg, LCD_RASTER_TIMING_0_REG); > > /* Set the Panel Height */ > + /* Set bits 9:0 of Lines Per Pixel */ > reg = lcdc_read(LCD_RASTER_TIMING_1_REG); > reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00); > lcdc_write(reg, LCD_RASTER_TIMING_1_REG); > > + /* Set bit 10 of Lines Per Pixel */ > + if (lcd_revision == LCD_VERSION_2) { > + reg = lcdc_read(LCD_RASTER_TIMING_2_REG); > + reg |= (((height - 1) & 0x400) << 16); Most outer parens not needed. WBR, Sergei From prakash.pm at ti.com Thu Jul 14 06:02:21 2011 From: prakash.pm at ti.com (Manjunathappa, Prakash) Date: Thu, 14 Jul 2011 16:32:21 +0530 Subject: [PATCH v2] video: da8xx-fb: Increased resolution configuration of revised LCDC IP In-Reply-To: <4E1EC677.7010305@mvista.com> References: <1310554333-25242-1-git-send-email-prakash.pm@ti.com> <4E1EC677.7010305@mvista.com> Message-ID: Hi Sergei, On Thu, Jul 14, 2011 at 16:05:35, Sergei Shtylyov wrote: > Hello. > > On 13-07-2011 14:52, Manjunathappa, Prakash wrote: > > > Revised LCD controller in upcoming TI SoC which is an updated version of > > LCDC IP that was found on TI's DA850 SoC supports 2048*2048 resolution. > > Below are the encoding details: > > Width: > > Pixels Per Line = {pplmsb, ppllsb, 4'b1111} + 1 > > Where pplmsb:1bit==>Raster Timing0[3], ppllsb:6bits==>Raster Timing0[9:4]. > > And encoded value can range from 16 to 2048 in multiples of 16. > > > Height: > > Lines Per Panel = {lpp_b10, lpp} > > Where lpp:10bits==>Raster Timing1[9:0], lpp_b10:1bit==>Raster Timing2[26]. > > And encoded value can range from 1 to 2048, programmable range is 0 to > > 2047. > > > Patch is verified on emulation platform of upcoming SoC for updated > > feature and on DA850 platform to make sure nothing existing breaks. > > > Signed-off-by: Manjunathappa, Prakash > > --- > > Since v1: > > 1)Fixed the bug in configuration of lpp_b10 in Raster Timing2[26] register. > > 2)Reframed commit message. > > drivers/video/da8xx-fb.c | 31 ++++++++++++++++++++++++++++--- > > 1 files changed, 28 insertions(+), 3 deletions(-) > > > diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c > > index 620f1c3..19ce407 100644 > > --- a/drivers/video/da8xx-fb.c > > +++ b/drivers/video/da8xx-fb.c > > @@ -460,18 +460,43 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, > > > > /* Set the Panel Width */ > > /* Pixels per line = (PPL + 1)*16 */ > > - /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/ > > - width&= 0x3f0; > > + if (lcd_revision == LCD_VERSION_1) { > > + /* > > + * 0x3F in bits 4..9 gives max horizontal resolution = 1024 > > + * pixels. > > + */ > > + width&= 0x3f0; > > + } else { > > + /* > > + * 0x7F in bits 3..9 gives max horizontal resolution = 2048 > > Maybe bits 4..10? Correct, I will fix this comment. > > > + * pixels. > > + */ > > + width&= 0x7f0; > > + } > > + > > reg = lcdc_read(LCD_RASTER_TIMING_0_REG); > > reg &= 0xfffffc00; > > - reg |= ((width >> 4) - 1) << 4; > > + if (lcd_revision == LCD_VERSION_1) { > > + reg |= ((width>> 4) - 1)<< 4; > > + } else { > > + width = ((width>> 4) - 1); > > Most outer parens not needed. Ok. I will remove them. > > > + reg |= ((width & 0x3f) << 4) | ((width& 0x40) >> 3); > > + } > > lcdc_write(reg, LCD_RASTER_TIMING_0_REG); > > > > /* Set the Panel Height */ > > + /* Set bits 9:0 of Lines Per Pixel */ > > reg = lcdc_read(LCD_RASTER_TIMING_1_REG); > > reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00); > > lcdc_write(reg, LCD_RASTER_TIMING_1_REG); > > > > + /* Set bit 10 of Lines Per Pixel */ > > + if (lcd_revision == LCD_VERSION_2) { > > + reg = lcdc_read(LCD_RASTER_TIMING_2_REG); > > + reg |= (((height - 1) & 0x400) << 16); > > Most outer parens not needed. Ok. I will remove them. Thanks, Prakash > > WBR, Sergei > From Paul_Stuart at seektech.com Thu Jul 14 17:59:19 2011 From: Paul_Stuart at seektech.com (Paul Stuart) Date: Thu, 14 Jul 2011 15:59:19 -0700 Subject: DM365: How to disable deinterlacing in Chained Resizer mode In-Reply-To: References: <1310554333-25242-1-git-send-email-prakash.pm@ti.com> <4E1EC677.7010305@mvista.com> Message-ID: <7F1B6BBBDF05C649BBBA3C853F488A611B01647FE5@Hawking.deepsea.com> Hello, Bashing my head against the wall trying to get the VPFE drivers to send up both fields of D1 interlaced video so I can use the DEI algorithm for deinterlacing instead of the poor-man's method of just throwing a field away. Anyone have any clues as how to modify the driver to make this happen in chained mode? I was hoping that just preserving the vertical resolution in ipipe_set_input_win (and ipipe_get_input_win) would work, but that just makes my system hang. There have been allusions in other threads about the need to modify some ISR's to make this work. Anyone trod down this path? Thanks, Paul From toddpoynor at google.com Sun Jul 17 00:39:35 2011 From: toddpoynor at google.com (Todd Poynor) Date: Sat, 16 Jul 2011 22:39:35 -0700 Subject: [PATCH v2] ARM: davinci: Check for NULL return from irq_alloc_generic_chip Message-ID: <1310881175-26182-1-git-send-email-toddpoynor@google.com> Avoid NULL dereference of irq_alloc_generic_chip return in low memory conditions. Signed-off-by: Todd Poynor --- Found by inspection of the source; have not witnessed an actual crash. arch/arm/mach-davinci/irq.c | 6 ++++++ 1 files changed, 6 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/irq.c b/arch/arm/mach-davinci/irq.c index d8c1af0..952dc12 100644 --- a/arch/arm/mach-davinci/irq.c +++ b/arch/arm/mach-davinci/irq.c @@ -52,6 +52,12 @@ davinci_alloc_gc(void __iomem *base, unsigned int irq_start, unsigned int num) struct irq_chip_type *ct; gc = irq_alloc_generic_chip("AINTC", 1, irq_start, base, handle_edge_irq); + if (!gc) { + pr_err("%s: irq_alloc_generic_chip for IRQ %u failed\n", + __func__, irq_start); + return; + } + ct = gc->chip_types; ct->chip.irq_ack = irq_gc_ack_set_bit; ct->chip.irq_mask = irq_gc_mask_clr_bit; -- 1.7.3.1 From lanmanck at gmail.com Sun Jul 17 10:15:18 2011 From: lanmanck at gmail.com (yaojin liu) Date: Sun, 17 Jul 2011 23:15:18 +0800 Subject: EDMA only send ONE Byte when using SPI2 in DM365 Message-ID: hi,all. I am using SPI2 to access a SPI Flash for my application, the source is changed from kernel 2.6.32-17(DVSDK4.0). But I found the EDMA only send ONE byte ,and never send again. the charlen=8. >From datasheet, DMA rx and tx channel for SPI2 is 11 and 10, but I donot know how to set the EVENTQ. For SPI0 it is 3. I use the code following to allocate DMA: edma_alloc_channel(11,davinci_spi_dma_rx_callback, mydata, davinci_spi_dma->eventq); if (r < 0) { DBG("Unable to request DMA channel for MibSPI RX\n"); return -EAGAIN; } davinci_spi_dma->dma_rx_channel = r; r = edma_alloc_channel(10,davinci_spi_dma_tx_callback,mydata, davinci_spi_dma->eventq); if (r < 0) { edma_free_channel(davinci_spi_dma->dma_rx_channel); davinci_spi_dma->dma_rx_channel = -1; DBG("Unable to request DMA channel for MibSPI TX\n"); return -EAGAIN; } davinci_spi_dma->dma_tx_channel = r; and it seems OK. So i start DMA by these: count=8,data_type=1, edma_set_transfer_params(davinci_spi_dma->dma_tx_channel, data_type, count, 1, 0, ASYNC); edma_set_dest(davinci_spi_dma->dma_tx_channel, tx_reg, INCR, W8BIT); /*not fifo!*/ edma_set_src(davinci_spi_dma->dma_tx_channel, spix->tx_dma_addr, INCR, W8BIT); edma_set_src_index(davinci_spi_dma->dma_tx_channel, data_type, 0); edma_set_dest_index(davinci_spi_dma->dma_tx_channel, 0, 0); edma_start(davinci_spi_dma->dma_tx_channel); davinci_spi_set_dma_req(spix, 1); // But the DMA only send ONE byte ,and never send more data again. Someone has tips?Thanks! -------------- next part -------------- An HTML attachment was scrubbed... URL: From nsekhar at ti.com Sun Jul 17 23:38:32 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 18 Jul 2011 10:08:32 +0530 Subject: [GIT PULL] DaVinci fix for v3.0 In-Reply-To: References: Message-ID: Hi Arnd, On Wed, Jul 13, 2011 at 10:59:47, Nori, Sekhar wrote: > Hi Arnd, > > Can you please pull this late breaking DaVinci fix for v3.0? > This is fallout of generic irq chip migration and prevents GPIO > interrupts from functioning correctly. Can you please send this for v3.0? I checked your and Linus's tree and this fix is not there yet. Thanks, Sekhar > > Thanks, > Sekhar > > The following changes since commit 620917de59eeb934b9f8cf35cc2d95c1ac8ed0fc: > Linus Torvalds (1): > Linux 3.0-rc7 > > are available in the git repository at: > > git://gitorious.org/linux-davinci/linux-davinci.git fixes > > Ido Yariv (1): > arm: davinci: Fix low level gpio irq handlers' argument > > arch/arm/mach-davinci/gpio.c | 21 ++++++++++++++++----- > 1 files changed, 16 insertions(+), 5 deletions(-) > > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > From prakash.pm at ti.com Sun Jul 17 23:28:53 2011 From: prakash.pm at ti.com (Manjunathappa, Prakash) Date: Mon, 18 Jul 2011 09:58:53 +0530 Subject: [PATCH v3] video: da8xx-fb: Increased resolution configuration of revised LCDC IP Message-ID: <1310963333-29806-1-git-send-email-prakash.pm@ti.com> Revised LCD controller in upcoming TI SoC which is an updated version of LCDC IP that was found on TI's DA850 SoC supports 2048*2048 resolution. Below are the encoding details: Width: Pixels Per Line = {pplmsb, ppllsb, 4'b1111} + 1 Where pplmsb:1bit==>Raster Timing0[3], ppllsb:6bits==>Raster Timing0[9:4]. And encoded value can range from 16 to 2048 in multiples of 16. Height: Lines Per Panel = {lpp_b10, lpp} Where lpp:10bits==>Raster Timing1[9:0], lpp_b10:1bit==>Raster Timing2[26]. And encoded value can range from 1 to 2048, programmable range is 0 to 2047. Patch is verified on emulation platform of upcoming SoC for updated feature and on DA850 platform to make sure nothing existing breaks. Signed-off-by: Manjunathappa, Prakash --- Since v2: Corrected comment describing horizontal resolution bits and removed unnecessary outer parenthesis. Since v1: 1)Fixed the bug in configuration of lpp_b10 in Raster Timing2[26] register. 2)Reframed commit message. drivers/video/da8xx-fb.c | 31 ++++++++++++++++++++++++++++--- 1 files changed, 28 insertions(+), 3 deletions(-) diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c index 620f1c3..94b611a 100644 --- a/drivers/video/da8xx-fb.c +++ b/drivers/video/da8xx-fb.c @@ -460,18 +460,43 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, /* Set the Panel Width */ /* Pixels per line = (PPL + 1)*16 */ - /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/ - width &= 0x3f0; + if (lcd_revision == LCD_VERSION_1) { + /* + * 0x3F in bits 4..9 gives max horizontal resolution = 1024 + * pixels. + */ + width &= 0x3f0; + } else { + /* + * 0x7F in bits 4..10 gives max horizontal resolution = 2048 + * pixels. + */ + width &= 0x7f0; + } + reg = lcdc_read(LCD_RASTER_TIMING_0_REG); reg &= 0xfffffc00; - reg |= ((width >> 4) - 1) << 4; + if (lcd_revision == LCD_VERSION_1) { + reg |= ((width >> 4) - 1) << 4; + } else { + width = (width >> 4) - 1; + reg |= ((width & 0x3f) << 4) | ((width & 0x40) >> 3); + } lcdc_write(reg, LCD_RASTER_TIMING_0_REG); /* Set the Panel Height */ + /* Set bits 9:0 of Lines Per Pixel */ reg = lcdc_read(LCD_RASTER_TIMING_1_REG); reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00); lcdc_write(reg, LCD_RASTER_TIMING_1_REG); + /* Set bit 10 of Lines Per Pixel */ + if (lcd_revision == LCD_VERSION_2) { + reg = lcdc_read(LCD_RASTER_TIMING_2_REG); + reg |= ((height - 1) & 0x400) << 16; + lcdc_write(reg, LCD_RASTER_TIMING_2_REG); + } + /* Set the Raster Order of the Frame Buffer */ reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(1 << 8); if (raster_order) -- 1.7.1 From sundaram at ti.com Mon Jul 18 02:51:58 2011 From: sundaram at ti.com (Raju, Sundaram) Date: Mon, 18 Jul 2011 13:21:58 +0530 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: > -----Original Message----- > From: Jassi Brar [mailto:jassisinghbrar at gmail.com] > Sent: Tuesday, July 12, 2011 6:15 PM > To: Raju, Sundaram > Cc: Linus Walleij; linux-arm-kernel at lists.infradead.org; linux- > kernel at vger.kernel.org; davinci-linux-open-source at linux.davincidsp.com; > linux at arm.linux.org.uk; dan.j.williams at intel.com; linux-omap at vger.kernel.org > Subject: Re: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride > configuration > > On Tue, Jul 12, 2011 at 5:01 PM, Raju, Sundaram wrote: > >> -----Original Message----- > >> From: Jassi Brar [mailto:jassisinghbrar at gmail.com] > >> Sent: Tuesday, July 12, 2011 4:51 PM > >> To: Linus Walleij > >> Cc: Raju, Sundaram; linux-arm-kernel at lists.infradead.org; linux- > >> kernel at vger.kernel.org; davinci-linux-open-source at linux.davincidsp.com; > >> linux at arm.linux.org.uk; dan.j.williams at intel.com; linux- > omap at vger.kernel.org > >> Subject: Re: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride > >> configuration > >> > >> On Tue, Jul 12, 2011 at 3:33 PM, Linus Walleij > >> wrote: > >> > On Tue, Jul 12, 2011 at 6:17 AM, Jassi Brar > >> wrote: > >> > > >> >> 1) Striding, in one form or other, is supported by other DMACs as well. > >> >> ? The number will only increase in future. > >> >> ? Are we to add ?_DMA_STRIDE_CONFIG for each case ? > >> > > >> > If we are sure about this and striding will work in a similar way on all > >> > then let's have the enum named DMA_STRIDE_CONFIG and move the > >> > passed-in struct to >> > > >> > Would that be: > >> > > >> > struct dma_stride_config { > >> > ? ?u32 read_bytes; > >> > ? ?u32 skip_bytes; > >> > }; > >> > > >> > Or something more complex? > >> Well, I am not sure if striding needs any special treatment at all. > >> Why not have client drivers prepare and submit sg-list. > >> Let the DMAC drivers interpret/parse the sg-list and program it > >> as strides if the h/w supports it. > >> If anything, we should make preparation and submission of sg-list > >> as efficient as possible. > > Jassi, > > > > sg_lists describe only a bunch of disjoint buffers. But what if the > > DMAC can skip and read the bytes within each of the buffers in > > the sg_list? (like TI EDMAC and TI SDMAC) > > How can that information be passed to the offload > > engine driver from the client? > > > OK, I overlooked. > We do need something new to handle these ultra-fine-grained sg-lists. > But still we shouldn't add SoC specific API to the common sub-systems. > > Maybe a new api to pass fixed-format variable-length encoded message > to the DMAC drivers? > Which could be interpreted by DMAC drivers to extract all the needed xfer > parameters from the 'header' section and instructions to program the xfers > in the DMAC from the variable length body of the 'message' buffer. > It might sound complicated but we can have helpers to make the job easy. > Btw, the regular single/sg-list xfers could also be expressed by this method. Do you expect this variable length body of the message to be DMAC independent? I don't think so. In that case how is this different from what we have here already? If it can be DMAC independent, can you illustrate more on how this can be done? But the point to note is, if this can be made DMAC independent then the control command we have also can be made DMAC independent by generalizing the configuration structure passed to it. ~ Sundaram From jon.povey at racelogic.co.uk Mon Jul 18 22:30:11 2011 From: jon.povey at racelogic.co.uk (Jon Povey) Date: Tue, 19 Jul 2011 12:30:11 +0900 Subject: [PATCH] davinci: fix DM365 EVM video input mux bits Message-ID: <1311046211-2849-1-git-send-email-jon.povey@racelogic.co.uk> Video input mux settings for tvp7002 and imager inputs were swapped. Comment was correct. Tested on EVM with tvp7002 input. Signed-off-by: Jon Povey --- As a bugfix maybe this should try to get into 3.0? arch/arm/mach-davinci/board-dm365-evm.c | 4 ++-- 1 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index c67f684..09a87e6 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -520,7 +520,7 @@ fail: */ if (have_imager()) { label = "HD imager"; - mux |= 1; + mux |= 2; /* externally mux MMC1/ENET/AIC33 to imager */ mux |= BIT(6) | BIT(5) | BIT(3); @@ -540,7 +540,7 @@ fail: resets &= ~BIT(1); if (have_tvp7002()) { - mux |= 2; + mux |= 1; resets &= ~BIT(2); label = "tvp7002 HD"; } else { -- 1.6.3.3 From pavel at ucw.cz Mon Jul 18 17:07:21 2011 From: pavel at ucw.cz (Pavel Machek) Date: Mon, 18 Jul 2011 22:07:21 -0000 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: <1308640714-17961-8-git-send-email-ohad@wizery.com> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> Message-ID: <20110609171210.GB3877@ucw.cz> Hi! > @@ -0,0 +1,75 @@ > +What: /sys/bus/rpmsg/devices/.../name > +Date: June 2011 > +KernelVersion: 3.2 > +Contact: Ohad Ben-Cohen > +Description: > + Every rpmsg device is a communication channel with a remote > + processor. Channels are identified with a (textual) name, > + which is maximum 32 bytes long (defined as RPMSG_NAME_SIZE in > + rpmsg.h). > + > + This sysfs entry contains the name of this channel. > + > +What: /sys/bus/rpmsg/devices/.../src > +Date: June 2011 > +KernelVersion: 3.2 > +Contact: Ohad Ben-Cohen > +Description: > + Every rpmsg device is a communication channel with a remote > + processor. Channels have a local ("source") rpmsg address, > + and remote ("destination") rpmsg address. When an entity > + starts listening on one end of a channel, it assigns it with > + a unique rpmsg address (a 32 bits integer). This way when > + inbound messages arrive to this address, the rpmsg core > + dispatches them to the listening entity (a kernel driver). > + > + This sysfs entry contains the src (local) rpmsg address > + of this channel. If it contains 0xffffffff, then an address > + wasn't assigned (can happen if no driver exists for this > + channel). So this is basically networking... right? Why not implement it as sockets? (accept, connect, read, write)? Pavel -- (english) http://www.livejournal.com/~pavelmachek (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html From manjunath.hadli at ti.com Tue Jul 19 00:29:49 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 19 Jul 2011 10:59:49 +0530 Subject: [PATCH] davinci: fix DM365 EVM video input mux bits In-Reply-To: <1311046211-2849-1-git-send-email-jon.povey@racelogic.co.uk> References: <1311046211-2849-1-git-send-email-jon.povey@racelogic.co.uk> Message-ID: Jon, The fix looks correct. As a part of a different implementation which will go soon upstream, this has been taken care of, where we have used macros CPLD_VIDEO_INPUT_MUX_TVP7002 0x1 CPLD_VIDEO_INPUT_MUX_IMAGER 0x2 So, if you can do the same, it will look better. Thanks, -Manju On Tue, Jul 19, 2011 at 09:00:11, Jon Povey wrote: > Video input mux settings for tvp7002 and imager inputs were swapped. > Comment was correct. > > Tested on EVM with tvp7002 input. > > Signed-off-by: Jon Povey > --- > As a bugfix maybe this should try to get into 3.0? > > arch/arm/mach-davinci/board-dm365-evm.c | 4 ++-- > 1 files changed, 2 insertions(+), 2 deletions(-) > > diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c > index c67f684..09a87e6 100644 > --- a/arch/arm/mach-davinci/board-dm365-evm.c > +++ b/arch/arm/mach-davinci/board-dm365-evm.c > @@ -520,7 +520,7 @@ fail: > */ > if (have_imager()) { > label = "HD imager"; > - mux |= 1; > + mux |= 2; > > /* externally mux MMC1/ENET/AIC33 to imager */ > mux |= BIT(6) | BIT(5) | BIT(3); > @@ -540,7 +540,7 @@ fail: > resets &= ~BIT(1); > > if (have_tvp7002()) { > - mux |= 2; > + mux |= 1; > resets &= ~BIT(2); > label = "tvp7002 HD"; > } else { > -- > 1.6.3.3 > > _______________________________________________ > Davinci-linux-open-source mailing list > Davinci-linux-open-source at linux.davincidsp.com > http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source > From ohad at wizery.com Tue Jul 19 00:38:28 2011 From: ohad at wizery.com (Ohad Ben-Cohen) Date: Tue, 19 Jul 2011 08:38:28 +0300 Subject: [RFC 7/8] drivers: introduce rpmsg, a remote-processor messaging bus In-Reply-To: <20110609171210.GB3877@ucw.cz> References: <1308640714-17961-1-git-send-email-ohad@wizery.com> <1308640714-17961-8-git-send-email-ohad@wizery.com> <20110609171210.GB3877@ucw.cz> Message-ID: Hi Pavel, On Thu, Jun 9, 2011 at 8:12 PM, Pavel Machek wrote: > So this is basically networking... right? Why not implement it as > sockets? (accept, connect, read, write)? This patch focuses on adding the core rpmsg kernel infrastructure. The next step, after getting the basic stuff in, would be adding rpmsg drivers, and exposing user space API. For some use cases, where userland talks directly with remote entities (and otherwise requires no kernel involvement besides exposing the transport), socket API is very nice as it's flexible and prevalent. We already have several rpmsg drivers and a rpmsg-based socket API implementation too, but we'll get to it only after the core stuff gets in. Thanks, Ohad. From Jon.Povey at racelogic.co.uk Tue Jul 19 01:28:19 2011 From: Jon.Povey at racelogic.co.uk (Jon Povey) Date: Tue, 19 Jul 2011 07:28:19 +0100 Subject: [PATCH] davinci: fix DM365 EVM video input mux bits In-Reply-To: Message-ID: <70E876B0EA86DD4BAF101844BC814DFE0B4E0859D7@Cloud.RL.local> Hadli, Manjunath wrote: > Jon, > > The fix looks correct. As a part of a different > implementation which will go soon upstream, this has been > taken care of, where we have used macros > CPLD_VIDEO_INPUT_MUX_TVP7002 0x1 > CPLD_VIDEO_INPUT_MUX_IMAGER 0x2 > > So, if you can do the same, it will look better. If you have a patch you can split out that has your defines where and how you want them then great. Otherwise I'd suggest just getting this or something equivalent in to fix the bug first, and reimplement it later. -- Jon Povey jon.povey at racelogic.co.uk Racelogic is a limited company registered in England. Registered number 2743719 . Registered Office Unit 10, Swan Business Centre, Osier Way, Buckingham, Bucks, MK18 1TB . The information contained in this electronic mail transmission is intended by Racelogic Ltd for the use of the named individual or entity to which it is directed and may contain information that is confidential or privileged. If you have received this electronic mail transmission in error, please delete it from your system without copying or forwarding it, and notify the sender of the error by reply email so that the sender's address records can be corrected. The views expressed by the sender of this communication do not necessarily represent those of Racelogic Ltd. Please note that Racelogic reserves the right to monitor e-mail communications passing through its network From manjunath.hadli at ti.com Tue Jul 19 02:25:13 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 19 Jul 2011 12:55:13 +0530 Subject: [PATCH] davinci: fix DM365 EVM video input mux bits In-Reply-To: <70E876B0EA86DD4BAF101844BC814DFE0B4E0859D7@Cloud.RL.local> References: <70E876B0EA86DD4BAF101844BC814DFE0B4E0859D7@Cloud.RL.local> Message-ID: Will not be able to give you a sub-patch as it is combined as part of a larger implementation. Since the bug fix needs to go into 3.0, I am Ok if you fix the bug, and the macros can be substituted later. -Manju On Tue, Jul 19, 2011 at 11:58:19, Jon Povey wrote: > Hadli, Manjunath wrote: > > Jon, > > > > The fix looks correct. As a part of a different implementation which > > will go soon upstream, this has been taken care of, where we have used > > macros > > CPLD_VIDEO_INPUT_MUX_TVP7002 0x1 > > CPLD_VIDEO_INPUT_MUX_IMAGER 0x2 > > > > So, if you can do the same, it will look better. > > If you have a patch you can split out that has your defines where and how you want them then great. > > Otherwise I'd suggest just getting this or something equivalent in to fix the bug first, and reimplement it later. > > -- > Jon Povey > jon.povey at racelogic.co.uk > > Racelogic is a limited company registered in England. Registered number 2743719 . > Registered Office Unit 10, Swan Business Centre, Osier Way, Buckingham, Bucks, MK18 1TB . > > The information contained in this electronic mail transmission is intended by Racelogic Ltd for the use of the named individual or entity to which it is directed and may contain information that is confidential or privileged. If you have received this electronic mail transmission in error, please delete it from your system without copying or forwarding it, and notify the sender of the error by reply email so that the sender's address records can be corrected. The views expressed by the sender of this communication do not necessarily represent those of Racelogic Ltd. Please note that Racelogic reserves the right to monitor e-mail communications passing through its network > > > From mschnell at lumino.de Tue Jul 19 03:29:12 2011 From: mschnell at lumino.de (Michael Schnell) Date: Tue, 19 Jul 2011 10:29:12 +0200 Subject: AM 1808, Angstrom Message-ID: <4E254058.5020103@lumino.de> Hi experts. I suppose the AMxxx devices are not exactly "Davinci" branded, but as this seems to be a very active group of TI Software experts, I dare to ask here. We are starting to design a controller based on an AM1808 (or maybe an upcoming AM 335x) chip. Right now, we just have a Development kit (by Logic PD) that features an OMAP 138 Chip (that is pin-compatible with the AM1808 but additionally has a DSP) We started playing with this kit and now I have several questions that you might be able to answer. The board came with a "Angstrom" Linux distribution. (Question 1) Can you give us a hint, whether it's a good idea to use Angstrom for an embedded controller or do you favor other distributions (binary and/or cross compilable-source-code) I tried to update the Distribution via the "opkg" package manager. Lots of files were downloaded, but when opkg tried to install them I get an error message like "too small memory". The board has 32 MB RAM and the file system is close to empty. (At home I have another ARM hardware (a SLUG) with (IIRC) only 32 MB RAM and here updating via "apt" works just fine.) The board we are going to design will have 256 MB RAM, so I suppose this problem will not be seen there. Moreover I tried to install an additional package via opkg: same error. (Question 2) How to use opkg on this device or upgrading the distribution (including the Kernel)l and installing additional packages (like audio and X)? I found that "lsmod" shows no loaded modules. But as busybox is configured to feature the module specific commands I suppose the distribution does support modules (Question 3) Can this system be enhanced with Kernel modules ? Why are no modules loaded after starting ? We want to use I?S audio output. The kit does feature an I?S to analog converter. (Question 4) How to configure, use and test the audio output ? We want to use the LCD display. The kit features a socket for the chip's LCD/Video pins. We are going to get an LCD panel to connect to the board or to find a converter that allows connecting some standard monitor. (Question 5) How to configure, use and test the LCD and/or Video output ? We want to be able to run software that has a GUI. (Question 6) How to install a system that allows for running GUI software (X11, QT or GTK or ???, ...) We want to be able to run the system without a monitor connected. (Question 7) How to install and use VNC (or whatever) to remote-control the GUI of a running program ? Thanks in advance for any answers, tips and tricks. Have fun, -Michael From nsekhar at ti.com Tue Jul 19 03:56:50 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 19 Jul 2011 14:26:50 +0530 Subject: [PATCH] davinci: fix DM365 EVM video input mux bits In-Reply-To: <1311046211-2849-1-git-send-email-jon.povey@racelogic.co.uk> References: <1311046211-2849-1-git-send-email-jon.povey@racelogic.co.uk> Message-ID: Hi Jon, On Tue, Jul 19, 2011 at 09:00:11, Jon Povey wrote: > Video input mux settings for tvp7002 and imager inputs were swapped. > Comment was correct. > > Tested on EVM with tvp7002 input. > > Signed-off-by: Jon Povey Thanks for the fix. Since this part of the code hasn't changed since it was written, this is a good candidate for stable kernel series. You can just add: Cc: stable at kernel.org Also, this way if the fix fails to make it to 3.0, it can make it into its stable series. I will add the Cc line when I queue this patch. > --- > As a bugfix maybe this should try to get into 3.0? I am going queue this to Arnd for sending it for v3.0. He hasn't yet picked up my earlier pull request yet so keep fingers crossed :) BTW, I changed the subject to: "davinci: DM365 EVM: fix video input mux bits" Nothing really wrong with what you had, just that I prefer it this way. Thanks, Sekhar From nsekhar at ti.com Tue Jul 19 04:00:32 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 19 Jul 2011 14:30:32 +0530 Subject: [PATCH] davinci: fix DM365 EVM video input mux bits In-Reply-To: References: <70E876B0EA86DD4BAF101844BC814DFE0B4E0859D7@Cloud.RL.local> Message-ID: Hi Manju, On Tue, Jul 19, 2011 at 12:55:13, Hadli, Manjunath wrote: > Will not be able to give you a sub-patch as it is combined as part of a larger implementation. > > Since the bug fix needs to go into 3.0, I am Ok if you fix the bug, and the > macros can be substituted later. I am going to take this as your ack and add: Acked-by: Manjunath Hadli while committing. Thanks, Sekhar From nsekhar at ti.com Tue Jul 19 04:06:17 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 19 Jul 2011 14:36:17 +0530 Subject: [GIT PULL] DaVinci fix for v3.0 In-Reply-To: References: Message-ID: Hi Arnd, On Mon, Jul 18, 2011 at 10:08:32, Nori, Sekhar wrote: > Hi Arnd, > > On Wed, Jul 13, 2011 at 10:59:47, Nori, Sekhar wrote: > > Hi Arnd, > > > > Can you please pull this late breaking DaVinci fix for v3.0? > > This is fallout of generic irq chip migration and prevents GPIO > > interrupts from functioning correctly. > > Can you please send this for v3.0? I checked your > and Linus's tree and this fix is not there yet. I collected two more fixes since I sent this. Since you do not seem to have picked this as yet, I am going to send a replacement pull request soon. Thanks, Sekhar From nsekhar at ti.com Tue Jul 19 04:18:03 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 19 Jul 2011 14:48:03 +0530 Subject: [PATCH v2] ARM: davinci: Check for NULL return from irq_alloc_generic_chip In-Reply-To: <1310881175-26182-1-git-send-email-toddpoynor@google.com> References: <1310881175-26182-1-git-send-email-toddpoynor@google.com> Message-ID: On Sun, Jul 17, 2011 at 11:09:35, Todd Poynor wrote: > Avoid NULL dereference of irq_alloc_generic_chip return in low > memory conditions. > > Signed-off-by: Todd Poynor Thanks Todd, queuing this for v3.0. Thanks, Sekhar From nsekhar at ti.com Tue Jul 19 04:30:12 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Tue, 19 Jul 2011 15:00:12 +0530 Subject: [GIT PULL v2] DaVinci fixes for v3.0 Message-ID: Hi Arnd, Can you please pull these DaVinci fixes for v3.0? Thanks, Sekhar The following changes since commit 620917de59eeb934b9f8cf35cc2d95c1ac8ed0fc: Linus Torvalds (1): Linux 3.0-rc7 are available in the git repository at: git://gitorious.org/linux-davinci/linux-davinci.git fixes Ido Yariv (1): arm: davinci: Fix low level gpio irq handlers' argument Jon Povey (1): davinci: DM365 EVM: fix video input mux bits Todd Poynor (1): ARM: davinci: Check for NULL return from irq_alloc_generic_chip arch/arm/mach-davinci/board-dm365-evm.c | 4 ++-- arch/arm/mach-davinci/gpio.c | 21 ++++++++++++++++----- arch/arm/mach-davinci/irq.c | 6 ++++++ 3 files changed, 24 insertions(+), 7 deletions(-) From manjunath.hadli at ti.com Tue Jul 19 05:24:53 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 19 Jul 2011 15:54:53 +0530 Subject: [PATCH] davinci: fix DM365 EVM video input mux bits In-Reply-To: References: <70E876B0EA86DD4BAF101844BC814DFE0B4E0859D7@Cloud.RL.local> , Message-ID: Sure. Please do. Thanks and Regards, -Manju ________________________________________ From: Nori, Sekhar Sent: Tuesday, July 19, 2011 2:30 PM To: Hadli, Manjunath; 'Jon Povey'; davinci-linux-open-source at linux.davincidsp.com; linux-arm-kernel at lists.infradead.org Cc: linux-kernel at vger.kernel.org Subject: RE: [PATCH] davinci: fix DM365 EVM video input mux bits Hi Manju, On Tue, Jul 19, 2011 at 12:55:13, Hadli, Manjunath wrote: > Will not be able to give you a sub-patch as it is combined as part of a larger implementation. > > Since the bug fix needs to go into 3.0, I am Ok if you fix the bug, and the > macros can be substituted later. I am going to take this as your ack and add: Acked-by: Manjunath Hadli while committing. Thanks, Sekhar From manjunath.hadli at ti.com Tue Jul 19 05:51:17 2011 From: manjunath.hadli at ti.com (Hadli, Manjunath) Date: Tue, 19 Jul 2011 16:21:17 +0530 Subject: [RFC PATCH 1/8] davinci: vpfe: add dm3xx IPIPEIF hardware support module In-Reply-To: <20110713185050.GC27451@valkosipuli.localdomain> References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> <1309439597-15998-2-git-send-email-manjunath.hadli@ti.com> <20110713185050.GC27451@valkosipuli.localdomain> Message-ID: Sakari, Thank you for your comments. I agree with them and fix. Please comment on the rest of the patches as well. -Manju On Thu, Jul 14, 2011 at 00:20:50, Sakari Ailus wrote: > Hi Manju, > > Thanks for the patchset! > > I have a few comments on this patch below. I haven't read the rest of the patches yet so I may have more comments on this one when I do that. > > On Thu, Jun 30, 2011 at 06:43:10PM +0530, Manjunath Hadli wrote: > > add support for dm3xx IPIPEIF hardware setup. This is the lowest > > software layer for the dm3x vpfe driver which directly accesses > > hardware. Add support for features like default pixel correction, dark > > frame substraction and hardware setup. > > > > Signed-off-by: Manjunath Hadli > > Signed-off-by: Nagabhushana Netagunte > > --- > > drivers/media/video/davinci/dm3xx_ipipeif.c | 368 +++++++++++++++++++++++++++ > > include/media/davinci/dm3xx_ipipeif.h | 292 +++++++++++++++++++++ > > 2 files changed, 660 insertions(+), 0 deletions(-) create mode > > 100644 drivers/media/video/davinci/dm3xx_ipipeif.c > > create mode 100644 include/media/davinci/dm3xx_ipipeif.h > > > > diff --git a/drivers/media/video/davinci/dm3xx_ipipeif.c > > b/drivers/media/video/davinci/dm3xx_ipipeif.c > > new file mode 100644 > > index 0000000..36cb61b > > --- /dev/null > > +++ b/drivers/media/video/davinci/dm3xx_ipipeif.c > > @@ -0,0 +1,368 @@ > > +/* > > +* Copyright (C) 2011 Texas Instruments Inc > > +* > > +* 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 in the hope that it will be useful, > > +* but WITHOUT ANY WARRANTY; without even the implied warranty of > > +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > +* GNU General Public License for more details. > > +* > > +* You should have received a copy of the GNU General Public License > > +* along with this program; if not, write to the Free Software > > +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA > > +02111-1307 USA > > +* > > +* ipipe module to hold common functionality across DM355 and DM365 */ > > +#include #include #include > > + #include #include > > + #include > > + > > +#define DM355 0 > > +#define DM365 1 > > + > > +static void *__iomem ipipeif_base_addr; > > This looks device specific. What about using dev_set/get_drvdata and remove this one? > > > +static int device_type; > > Ditto. Both should be in a device specific struct. I will move both of the above to platform file. > > > +static inline u32 regr_if(u32 offset) { > > + return readl(ipipeif_base_addr + offset); } > > + > > +static inline void regw_if(u32 val, u32 offset) { > > + writel(val, ipipeif_base_addr + offset); } > > + > > +void ipipeif_set_enable(char en, unsigned int mode) { > > + regw_if(1, IPIPEIF_ENABLE); > > +} > > + > > +u32 ipipeif_get_enable(void) > > +{ > > + return regr_if(IPIPEIF_ENABLE); > > +} > > + > > +int ipipeif_set_address(struct ipipeif *params, unsigned int address) > > +{ > > If address is a value for a register you should use u32. Okay. > > > + unsigned int val1; > > + unsigned int val; > > + > > + if (params->source != 0) { > > + val = ((params->adofs >> 5) & IPIPEIF_ADOFS_LSB_MASK); > > + regw_if(val, IPIPEIF_ADOFS); > > You may do without val as well. > > > + /* lower sixteen bit */ > > + val = ((address >> IPIPEIF_ADDRL_SHIFT) & IPIPEIF_ADDRL_MASK); > > + regw_if(val, IPIPEIF_ADDRL); > > + > > + /* upper next seven bit */ > > + val1 = > > + ((address >> IPIPEIF_ADDRU_SHIFT) & IPIPEIF_ADDRU_MASK); > > + regw_if(val1, IPIPEIF_ADDRU); > > + } else > > + return -1; > > What's -1? If this is an error, Exxxxx codes should be used. > > The error check should become first and the rest of the function may be unindented by one tab stop. Okay. > > > + return 0; > > +} > > + > > +static void ipipeif_config_dpc(struct ipipeif_dpc *dpc) { > > + u32 val; > > + > > + if (dpc->en) { > > + val = ((dpc->en & 1) << IPIPEIF_DPC2_EN_SHIFT); > > + val |= (dpc->thr & IPIPEIF_DPC2_THR_MASK); > > + } else > > + val = 0; > > + > > + regw_if(val, IPIPEIF_DPC2); > > +} > > + > > +/* This function sets up IPIPEIF and is called from > > + * ipipe_hw_setup() > > + */ > > +int ipipeif_hw_setup(struct ipipeif *params) { > > + enum v4l2_mbus_pixelcode isif_port_if; > > + unsigned int val1 = 0x7; > > 7 looks like a magic number. Will fix. > > > + unsigned int val; > > + > > + if (NULL == params) > > + return -1; > > Same here, and I can also see elsewhere. Will fix. > > > + /* Enable clock to IPIPEIF and IPIPE */ > > + if (device_type == DM365) > > + vpss_enable_clock(VPSS_IPIPEIF_CLOCK, 1); > > + > > + /* Combine all the fields to make CFG1 register of IPIPEIF */ > > + val = params->mode << ONESHOT_SHIFT; > > + val |= params->source << INPSRC_SHIFT; > > + val |= params->clock_select << CLKSEL_SHIFT; > > + val |= params->avg_filter << AVGFILT_SHIFT; > > + val |= params->decimation << DECIM_SHIFT; > > + > > + if (device_type == DM355) { > > + val |= params->var.if_base.ialaw << IALAW_SHIFT; > > + val |= params->var.if_base.pack_mode << PACK8IN_SHIFT; > > + val |= params->var.if_base.clk_div << CLKDIV_SHIFT; > > + val |= params->var.if_base.data_shift << DATASFT_SHIFT; > > + } else { > > + /* DM365 IPIPE 5.1 */ > > + val |= params->var.if_5_1.pack_mode << PACK8IN_SHIFT; > > + val |= params->var.if_5_1.source1 << INPSRC1_SHIFT; > > + if (params->source != SDRAM_YUV) > > + val |= params->var.if_5_1.data_shift << DATASFT_SHIFT; > > + else > > + val &= (~(val1 << DATASFT_SHIFT)); > > + } > > + regw_if(val, IPIPEIF_GFG1); > > + > > + switch (params->source) { > > + case CCDC: > > + { > > + regw_if(params->gain, IPIPEIF_GAIN); > > + break; > > + } > > Braces aren't needed here. Okay. > > > + case SDRAM_RAW: > > + case CCDC_DARKFM: > > + { > > + regw_if(params->gain, IPIPEIF_GAIN); > > + /* fall through */ > > + } > > Ditto. > > > + case SDRAM_YUV: > > + { > > + val |= params->var.if_5_1.data_shift << DATASFT_SHIFT; > > + regw_if(params->glob_hor_size, IPIPEIF_PPLN); > > + regw_if(params->glob_ver_size, IPIPEIF_LPFR); > > + regw_if(params->hnum, IPIPEIF_HNUM); > > + regw_if(params->vnum, IPIPEIF_VNUM); > > + > > + break; > > + } > > + default: > > + /* Do nothing */ > > + ; > > Is this not an error? Yes. It is. Will fix. > > > + } > > + > > + /*check if decimation is enable or not */ > > + if (params->decimation) > > + regw_if(params->rsz, IPIPEIF_RSZ); > > + > > + if (device_type == DM365) { > > You can do an opposite check and then return if it's true. By removing the brackes from cases you can unindent this by two tab stops. The function is also very long. > > > + /* Setup sync alignment and initial rsz position */ > > + val = params->var.if_5_1.align_sync & 1; > > + val <<= IPIPEIF_INIRSZ_ALNSYNC_SHIFT; > > + val |= (params->var.if_5_1.rsz_start & IPIPEIF_INIRSZ_MASK); > > + regw_if(val, IPIPEIF_INIRSZ); > > + > > + /* Enable DPCM decompression */ > > + switch (params->source) { > > + case SDRAM_RAW: > > + { > > + val = 0; > > + if (params->var.if_5_1.dpcm.en) { > > + val = params->var.if_5_1.dpcm.en & 1; > > + val |= (params->var.if_5_1.dpcm.type > > + & 1) > > + << IPIPEIF_DPCM_BITS_SHIFT; > > + val |= > > + (params->var.if_5_1.dpcm.pred & 1) > > + << IPIPEIF_DPCM_PRED_SHIFT; > > + } > > + regw_if(val, IPIPEIF_DPCM); > > + > > + /* set DPC */ > > + ipipeif_config_dpc(¶ms->var.if_5_1.dpc); > > + > > + regw_if(params->var.if_5_1.clip, IPIPEIF_OCLIP); > > + /* fall through for SDRAM YUV mode */ > > + isif_port_if = > > + params->var.if_5_1.isif_port.if_type; > > + /* configure CFG2 */ > > + switch (isif_port_if) { > > + case V4L2_MBUS_FMT_YUYV8_1X16: > > + val |= > > + (0 << > > + IPIPEIF_CFG2_YUV8_SHIFT); > > This has no effect. Will fix. > > > + val |= > > + (1 << > > + IPIPEIF_CFG2_YUV16_SHIFT); > > + regw_if(val, IPIPEIF_CFG2); > > + break; > > + default: > > + val |= > > + (0 << > > + IPIPEIF_CFG2_YUV8_SHIFT); > > + val |= > > + (0 << > > + IPIPEIF_CFG2_YUV16_SHIFT); > > Neither do the above two statements. > Yes. > > + regw_if(val, IPIPEIF_CFG2); > > + break; > > + } > > + } > > + case SDRAM_YUV: > > + { > > + /* Set clock divider */ > > + if (params->clock_select == SDRAM_CLK) { > > + val |= > > + ((params->var.if_5_1.clk_div.m - 1) > > + << IPIPEIF_CLKDIV_M_SHIFT); > > + val |= > > + (params->var.if_5_1.clk_div.n - 1); > > + regw_if(val, IPIPEIF_CLKDIV); > > + } > > + > > + break; > > + } > > + case CCDC: > > + case CCDC_DARKFM: > > + { > > + /* set DPC */ > > + ipipeif_config_dpc(¶ms->var.if_5_1.dpc); > > + > > + /* Set DF gain & threshold control */ > > + val = 0; > > + if (params->var.if_5_1.df_gain_en) { > > + val = (params->var.if_5_1.df_gain_thr > > + & IPIPEIF_DF_GAIN_THR_MASK); > > + regw_if(val, IPIPEIF_DFSGTH); > > + val = ((params->var.if_5_1.df_gain_en > > + & 1) > > + << IPIPEIF_DF_GAIN_EN_SHIFT); > > + val |= (params->var.if_5_1.df_gain > > + & IPIPEIF_DF_GAIN_MASK); > > + } > > + regw_if(val, IPIPEIF_DFSGVL); > > + isif_port_if = > > + params->var.if_5_1.isif_port.if_type; > > Indentation. Okay. > > > + > > + /* configure CFG2 */ > > + val = > > + params->var.if_5_1.isif_port.hdpol > > + << IPIPEIF_CFG2_HDPOL_SHIFT; > > + val |= > > + params->var.if_5_1.isif_port.vdpol > > + << IPIPEIF_CFG2_VDPOL_SHIFT; > > + switch (isif_port_if) { > > + case V4L2_MBUS_FMT_YUYV8_1X16: > > + case V4L2_MBUS_FMT_YUYV10_1X20: > > + { > > + val |= > > + (0 << > > + IPIPEIF_CFG2_YUV8_SHIFT); > > + val |= > > + (1 << > > + IPIPEIF_CFG2_YUV16_SHIFT); > > It might make sense to use #defines with descriptive names rather than zeros and ones. Will do. > > > + break; > > + } > > + case V4L2_MBUS_FMT_YUYV8_2X8: > > + case V4L2_MBUS_FMT_Y8_1X8: > > + case V4L2_MBUS_FMT_YUYV10_2X10: > > + { > > + val |= > > + (1 << > > + IPIPEIF_CFG2_YUV8_SHIFT); > > + val |= > > + (1 << > > + IPIPEIF_CFG2_YUV16_SHIFT); > > + val |= > > + ((params->var.if_5_1. > > + pix_order) > > + << > > + IPIPEIF_CFG2_YUV8P_SHIFT); > > + break; > > + } > > + default: > > + { > > + /* Bayer */ > > + regw_if(params->var.if_5_1.clip, > > + IPIPEIF_OCLIP); > > + val |= > > + (0 << > > + IPIPEIF_CFG2_YUV16_SHIFT); > > + } > > + } > > + regw_if(val, IPIPEIF_CFG2); > > + break; > > + } > > + default: > > + /* do nothing */ > > + ; > > + } > > + } > > + return 0; > > +} > > + > > +static int __devinit dm3xx_ipipeif_probe(struct platform_device > > +*pdev) { > > + static resource_size_t res_len; > > + struct resource *res; > > + int status; > > + > > + if (NULL != pdev->dev.platform_data) > > Lvalue would be better right for readability. Okay. > > > + device_type = DM365; > > + > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + if (!res) > > + return -ENOENT; > > + > > + res_len = res->end - res->start + 1; > > + > > + res = request_mem_region(res->start, res_len, res->name); > > + if (!res) > > + return -EBUSY; > > + > > + ipipeif_base_addr = ioremap_nocache(res->start, res_len); > > + if (!ipipeif_base_addr) { > > + status = -EBUSY; > > + goto fail; > > + } > > + return 0; > > + > > +fail: > > + release_mem_region(res->start, res_len); > > + > > + return status; > > +} > > + > > +static int dm3xx_ipipeif_remove(struct platform_device *pdev) { > > + struct resource *res; > > + > > + iounmap(ipipeif_base_addr); > > + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > > + if (res) > > + release_mem_region(res->start, res->end - res->start + 1); > > + return 0; > > +} > > + > > +static struct platform_driver dm3xx_ipipeif_driver = { > > + .driver = { > > + .name = "dm3xx_ipipeif", > > + .owner = THIS_MODULE, > > + }, > > + .remove = __devexit_p(dm3xx_ipipeif_remove), > > + .probe = dm3xx_ipipeif_probe, > > +}; > > + > > +static int dm3xx_ipipeif_init(void) > > +{ > > + return platform_driver_register(&dm3xx_ipipeif_driver); > > +} > > + > > +static void dm3xx_ipipeif_exit(void) > > +{ > > + platform_driver_unregister(&dm3xx_ipipeif_driver); > > +} > > + > > +module_init(dm3xx_ipipeif_init); > > +module_exit(dm3xx_ipipeif_exit); > > + > > +MODULE_LICENSE("GPL2"); > > diff --git a/include/media/davinci/dm3xx_ipipeif.h > > b/include/media/davinci/dm3xx_ipipeif.h > > new file mode 100644 > > index 0000000..87389ff > > --- /dev/null > > +++ b/include/media/davinci/dm3xx_ipipeif.h > > @@ -0,0 +1,292 @@ > > +/* > > +* Copyright (C) 2011 Texas Instruments Inc > > +* > > +* 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 in the hope that it will be useful, > > +* but WITHOUT ANY WARRANTY; without even the implied warranty of > > +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > +* GNU General Public License for more details. > > +* > > +* You should have received a copy of the GNU General Public License > > +* along with this program; if not, write to the Free Software > > +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA > > +02111-1307 USA */ > > + > > +#ifndef _DM3XX_IPIPEIF_H > > +#define _DM3XX_IPIPEIF_H > > +/* Used to shift input image data based on the data lines connected > > + * to parallel port > > + */ > > +/* IPIPE base specific types */ > > +enum ipipeif_data_shift { > > + IPIPEIF_BITS15_2, > > + IPIPEIF_BITS14_1, > > + IPIPEIF_BITS13_0, > > + IPIPEIF_BITS12_0, > > + IPIPEIF_BITS11_0, > > + IPIPEIF_BITS10_0, > > + IPIPEIF_BITS9_0 > > +}; > > + > > +enum ipipeif_clkdiv { > > + IPIPEIF_DIVIDE_HALF, > > + IPIPEIF_DIVIDE_THIRD, > > + IPIPEIF_DIVIDE_FOURTH, > > + IPIPEIF_DIVIDE_FIFTH, > > + IPIPEIF_DIVIDE_SIXTH, > > + IPIPEIF_DIVIDE_EIGHTH, > > + IPIPEIF_DIVIDE_SIXTEENTH, > > + IPIPEIF_DIVIDE_THIRTY > > +}; > > + > > +/* IPIPE 5.1 interface types */ > > +/* dpcm predicator for IPIPE 5.1 */ > > +enum ipipeif_dpcm_pred { > > + DPCM_SIMPLE_PRED, > > + DPCM_ADV_PRED > > +}; > > +/* data shift for IPIPE 5.1 */ > > +enum ipipeif_5_1_data_shift { > > + IPIPEIF_5_1_BITS11_0, > > + IPIPEIF_5_1_BITS10_0, > > + IPIPEIF_5_1_BITS9_0, > > + IPIPEIF_5_1_BITS8_0, > > + IPIPEIF_5_1_BITS7_0, > > + IPIPEIF_5_1_BITS15_4, > > +}; > > + > > +/* clockdiv for IPIPE 5.1 */ > > +struct ipipeif_5_1_clkdiv { > > + unsigned char m; > > + unsigned char n; > > +}; > > + > > +/* DPC at the if for IPIPE 5.1 */ > > +struct ipipeif_dpc { > > + /* 0 - disable, 1 - enable */ > > + unsigned char en; > > + /* threshold */ > > + unsigned short thr; > > +}; > > + > > +enum ipipeif_decimation { > > + IPIPEIF_DECIMATION_OFF, > > + IPIPEIF_DECIMATION_ON > > +}; > > If these are used as register values they should be explicitly defined. > > > +enum ipipeif_pixel_order { > > + IPIPEIF_CBCR_Y, > > + IPIPEIF_Y_CBCR > > +}; > > + > > +#ifdef __KERNEL__ > > This file is under include/media which isn't included by user space --- user space headers belong under include/linux. Interface and internal definitions should be in a separate header file. The entire file is used in kernel. Will change. > > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +enum ipipeif_clock { > > + PIXCEL_CLK, > > + SDRAM_CLK > > IPIPEIF prefix here? Sure. > > > +}; > > + > > +enum ipipeif_pack_mode { > > + IPIPEIF_PACK_16_BIT, > > + IPIPEIF_PACK_8_BIT > > +}; > > + > > +enum ipipe_oper_mode { > > + CONTINUOUS, > > + ONE_SHOT > > +}; > > + > > +enum ipipeif_5_1_pack_mode { > > + IPIPEIF_5_1_PACK_16_BIT, > > + IPIPEIF_5_1_PACK_8_BIT, > > + IPIPEIF_5_1_PACK_8_BIT_A_LAW, > > + IPIPEIF_5_1_PACK_12_BIT > > +}; > > + > > +enum ipipeif_avg_filter { > > + AVG_OFF, > > + AVG_ON > > +}; > > + > > +enum ipipeif_input_source { > > + CCDC, > > + SDRAM_RAW, > > + CCDC_DARKFM, > > + SDRAM_YUV > > +}; > > + > > +enum ipipeif_ialaw { > > + ALAW_OFF, > > + ALAW_ON > > +}; > > + > > +struct ipipeif_base { > > + enum ipipeif_ialaw ialaw; > > + enum ipipeif_pack_mode pack_mode; > > + enum ipipeif_data_shift data_shift; > > + enum ipipeif_clkdiv clk_div; > > +}; > > + > > +enum ipipeif_input_src1 { > > + SRC1_PARALLEL_PORT, > > + SRC1_SDRAM_RAW, > > + SRC1_ISIF_DARKFM, > > + SRC1_SDRAM_YUV > > +}; > > + > > +enum ipipeif_dpcm_type { > > + DPCM_8BIT_10BIT, > > + DPCM_8BIT_12BIT > > +}; > > + > > +struct ipipeif_dpcm_decomp { > > + unsigned char en; > > + enum ipipeif_dpcm_type type; > > + enum ipipeif_dpcm_pred pred; > > +}; > > + > > +enum ipipeif_dfs_dir { > > + IPIPEIF_PORT_MINUS_SDRAM, > > + IPIPEIF_SDRAM_MINUS_PORT > > +}; > > + > > +struct ipipeif_5_1 { > > + enum ipipeif_5_1_pack_mode pack_mode; > > + enum ipipeif_5_1_data_shift data_shift; > > + enum ipipeif_input_src1 source1; > > + struct ipipeif_5_1_clkdiv clk_div; > > + /* Defect pixel correction */ > > + struct ipipeif_dpc dpc; > > + /* DPCM decompression */ > > + struct ipipeif_dpcm_decomp dpcm; > > + /* ISIF port pixel order */ > > + enum ipipeif_pixel_order pix_order; > > + /* interface parameters from isif */ > > + struct vpfe_hw_if_param isif_port; > > + /* clipped to this value */ > > + unsigned short clip; > > + /* Align HSync and VSync to rsz_start */ > > + unsigned char align_sync; > > + /* resizer start position */ > > + unsigned int rsz_start; > > + /* DF gain enable */ > > + unsigned char df_gain_en; > > + /* DF gain value */ > > + unsigned short df_gain; > > + /* DF gain threshold value */ > > + unsigned short df_gain_thr; > > +}; > > + > > +/* ipipeif structures common to DM350 and DM365 used by ipipeif API > > +*/ struct ipipeif { > > + enum ipipe_oper_mode mode; > > + enum ipipeif_input_source source; > > + enum ipipeif_clock clock_select; > > + unsigned int glob_hor_size; > > + unsigned int glob_ver_size; > > + unsigned int hnum; > > + unsigned int vnum; > > + unsigned int adofs; > > + unsigned char rsz; > > + enum ipipeif_decimation decimation; > > + enum ipipeif_avg_filter avg_filter; > > + unsigned short gain; > > + /* IPIPE 5.1 */ > > + union var_part { > > + struct ipipeif_base if_base; > > + struct ipipeif_5_1 if_5_1; > > + } var; > > +}; > > + > > +/* IPIPEIF Register Offsets from the base address */ > > +#define IPIPEIF_ENABLE (0x00) > > +#define IPIPEIF_GFG1 (0x04) > > +#define IPIPEIF_PPLN (0x08) > > +#define IPIPEIF_LPFR (0x0C) > > +#define IPIPEIF_HNUM (0x10) > > +#define IPIPEIF_VNUM (0x14) > > +#define IPIPEIF_ADDRU (0x18) > > +#define IPIPEIF_ADDRL (0x1C) > > +#define IPIPEIF_ADOFS (0x20) > > +#define IPIPEIF_RSZ (0x24) > > +#define IPIPEIF_GAIN (0x28) > > + > > +/* Below registers are available only on IPIPE 5.1 */ > > +#define IPIPEIF_DPCM (0x2C) > > +#define IPIPEIF_CFG2 (0x30) > > +#define IPIPEIF_INIRSZ (0x34) > > +#define IPIPEIF_OCLIP (0x38) > > +#define IPIPEIF_DTUDF (0x3C) > > +#define IPIPEIF_CLKDIV (0x40) > > +#define IPIPEIF_DPC1 (0x44) > > +#define IPIPEIF_DPC2 (0x48) > > +#define IPIPEIF_DFSGVL (0x4C) > > +#define IPIPEIF_DFSGTH (0x50) > > +#define IPIPEIF_RSZ3A (0x54) > > +#define IPIPEIF_INIRSZ3A (0x58) > > +#define IPIPEIF_RSZ_MIN (16) > > +#define IPIPEIF_RSZ_MAX (112) > > +#define IPIPEIF_RSZ_CONST (16) > > +#define SETBIT(reg, bit) (reg = ((reg) | ((0x00000001)<<(bit)))) > > +#define RESETBIT(reg, bit) (reg = ((reg) & (~(0x00000001<<(bit))))) > > + > > +#define IPIPEIF_ADOFS_LSB_MASK (0x1FF) > > +#define IPIPEIF_ADOFS_LSB_SHIFT (5) > > +#define IPIPEIF_ADOFS_MSB_MASK (0x200) > > +#define IPIPEIF_ADDRU_MASK (0x7FF) > > +#define IPIPEIF_ADDRL_SHIFT (5) > > +#define IPIPEIF_ADDRL_MASK (0xFFFF) > > +#define IPIPEIF_ADDRU_SHIFT (21) > > +#define IPIPEIF_ADDRMSB_SHIFT (31) > > +#define IPIPEIF_ADDRMSB_LEFT_SHIFT (10) > > + > > +/* CFG1 Masks and shifts */ > > +#define ONESHOT_SHIFT (0) > > +#define DECIM_SHIFT (1) > > +#define INPSRC_SHIFT (2) > > +#define CLKDIV_SHIFT (4) > > +#define AVGFILT_SHIFT (7) > > +#define PACK8IN_SHIFT (8) > > +#define IALAW_SHIFT (9) > > +#define CLKSEL_SHIFT (10) > > +#define DATASFT_SHIFT (11) > > +#define INPSRC1_SHIFT (14) > > IPIPEIF prefix. Are these related to a particular register or a set of registers? One register. Will need to add the IPIPEIF prefix. > > > +/* DPC2 */ > > +#define IPIPEIF_DPC2_EN_SHIFT (12) > > +#define IPIPEIF_DPC2_THR_MASK (0xFFF) > > +#define IPIPEIF_DF_GAIN_EN_SHIFT (10) > > +#define IPIPEIF_DF_GAIN_MASK (0x3FF) > > +#define IPIPEIF_DF_GAIN_THR_MASK (0xFFF) > > +/* DPCM */ > > +#define IPIPEIF_DPCM_BITS_SHIFT (2) > > +#define IPIPEIF_DPCM_PRED_SHIFT (1) > > +/* CFG2 */ > > +#define IPIPEIF_CFG2_HDPOL_SHIFT (1) > > +#define IPIPEIF_CFG2_VDPOL_SHIFT (2) > > +#define IPIPEIF_CFG2_YUV8_SHIFT (6) > > +#define IPIPEIF_CFG2_YUV16_SHIFT (3) > > +#define IPIPEIF_CFG2_YUV8P_SHIFT (7) > > + > > +/* INIRSZ */ > > +#define IPIPEIF_INIRSZ_ALNSYNC_SHIFT (13) > > +#define IPIPEIF_INIRSZ_MASK (0x1FFF) > > + > > +/* CLKDIV */ > > +#define IPIPEIF_CLKDIV_M_SHIFT 8 > > + > > +int ipipeif_set_address(struct ipipeif *if_params, unsigned int > > +address); void ipipeif_set_enable(char en, unsigned int mode); int > > +ipipeif_hw_setup(struct ipipeif *if_params); > > +u32 ipipeif_get_enable(void); > > + > > +#endif > > +#endif > > -- > > 1.6.2.4 > > > > -- > > To unsubscribe from this list: send the line "unsubscribe linux-media" > > in the body of a message to majordomo at vger.kernel.org More majordomo > > info at http://vger.kernel.org/majordomo-info.html > > -- > Sakari Ailus > sakari.ailus at iki.fi > From linux at arm.linux.org.uk Tue Jul 19 05:55:32 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Tue, 19 Jul 2011 11:55:32 +0100 Subject: [PATCH] davinci: fix DM365 EVM video input mux bits In-Reply-To: References: <1311046211-2849-1-git-send-email-jon.povey@racelogic.co.uk> Message-ID: <20110719105532.GF26574@n2100.arm.linux.org.uk> On Tue, Jul 19, 2011 at 02:26:50PM +0530, Nori, Sekhar wrote: > Since this part of the code hasn't changed since it > was written, this is a good candidate for stable > kernel series. You can just add: > > Cc: stable at kernel.org Be careful when doing that - if you have git mail the patches out for review, and it picks that up, it'll send it to the stable team as well. The stable team will then complain "that's not how to submit patches to them". So, while you can add "Cc: stable at kernel.org" to the patch, its best to only add that just before you publish the commit after review, but before merging. From arnd at arndb.de Tue Jul 19 08:58:32 2011 From: arnd at arndb.de (Arnd Bergmann) Date: Tue, 19 Jul 2011 15:58:32 +0200 Subject: [GIT PULL v2] DaVinci fixes for v3.0 In-Reply-To: References: Message-ID: <201107191558.32577.arnd@arndb.de> On Tuesday 19 July 2011, Nori, Sekhar wrote: > Can you please pull these DaVinci fixes for v3.0? > > Thanks, > Sekhar > > The following changes since commit 620917de59eeb934b9f8cf35cc2d95c1ac8ed0fc: > Linus Torvalds (1): > Linux 3.0-rc7 > > are available in the git repository at: > > git://gitorious.org/linux-davinci/linux-davinci.git fixes Hi Sekhar, I've pulled it now, thanks! Sorry for being slow, I was waiting for other fixes to come in, but there was only one. If it ends up being too late for 3.0, I'll tag the patches for stable backports and send them with the first batch of fixes for 3.1. Arnd From nagabhushana.netagunte at ti.com Tue Jul 19 09:02:36 2011 From: nagabhushana.netagunte at ti.com (Netagunte, Nagabhushana) Date: Tue, 19 Jul 2011 19:32:36 +0530 Subject: DM365: How to disable deinterlacing in Chained Resizer mode In-Reply-To: <7F1B6BBBDF05C649BBBA3C853F488A611B01647FE5@Hawking.deepsea.com> References: <1310554333-25242-1-git-send-email-prakash.pm@ti.com> <4E1EC677.7010305@mvista.com> , <7F1B6BBBDF05C649BBBA3C853F488A611B01647FE5@Hawking.deepsea.com> Message-ID: Paul, If you wan to capture D1 interlaced video from VPFE, why you wan to connect previewer and reszier in chained mode with CCDC? Simple way will be to capture from CCDC. You need to change ISR routines though. For example, If you are using arago source(http://arago-project.org/git/projects/?p=linux-davinci.git;a=shortlog;h=refs/heads/r37), you can change vpfe_isr() and vpfe_vdint1_isr() to capture interlaced video frames. In addition to above changes, you need to change programming of CCDC's SDOFST register in CCDC driver module [ Refer to section 6.1.15 of VPFE user guide]. Regards, Nag ________________________________________ From: davinci-linux-open-source-bounces at linux.davincidsp.com [davinci-linux-open-source-bounces at linux.davincidsp.com] On Behalf Of Paul Stuart [Paul_Stuart at seektech.com] Sent: Friday, July 15, 2011 4:29 AM To: davinci-linux-open-source at linux.davincidsp.com Subject: DM365: How to disable deinterlacing in Chained Resizer mode Hello, Bashing my head against the wall trying to get the VPFE drivers to send up both fields of D1 interlaced video so I can use the DEI algorithm for deinterlacing instead of the poor-man's method of just throwing a field away. Anyone have any clues as how to modify the driver to make this happen in chained mode? I was hoping that just preserving the vertical resolution in ipipe_set_input_win (and ipipe_get_input_win) would work, but that just makes my system hang. There have been allusions in other threads about the need to modify some ISR's to make this work. Anyone trod down this path? Thanks, Paul _______________________________________________ Davinci-linux-open-source mailing list Davinci-linux-open-source at linux.davincidsp.com http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source From Paul_Stuart at seektech.com Tue Jul 19 09:42:59 2011 From: Paul_Stuart at seektech.com (Paul Stuart) Date: Tue, 19 Jul 2011 07:42:59 -0700 Subject: DM365: How to disable deinterlacing in Chained Resizer mode In-Reply-To: References: <1310554333-25242-1-git-send-email-prakash.pm@ti.com> <4E1EC677.7010305@mvista.com> , <7F1B6BBBDF05C649BBBA3C853F488A611B01647FE5@Hawking.deepsea.com> Message-ID: <7F1B6BBBDF05C649BBBA3C853F488A611B016482A6@Hawking.deepsea.com> Nag, Thanks for getting in touch with me, really appreciate it. I'm missing the big picture here. What, in broad strokes, do I need to change about the ISR routine to make this work? Just need the general concept, 'cause I'm missing it. My understanding is that the two fields are stored interleaved in memory. Where is the magic, in chained mode, that makes the resizer skip every other line? In previous experiments, I modified SDOFST to not skip lines, but it still looked like "normal" video. Does this register change behavior in chained mode, or only in one shot when we pull from SDRAM? Thanks! Paul -----Original Message----- From: Netagunte, Nagabhushana [mailto:nagabhushana.netagunte at ti.com] Sent: Tuesday, July 19, 2011 7:03 AM To: Paul Stuart; davinci-linux-open-source at linux.davincidsp.com Subject: RE: DM365: How to disable deinterlacing in Chained Resizer mode Paul, If you wan to capture D1 interlaced video from VPFE, why you wan to connect previewer and reszier in chained mode with CCDC? Simple way will be to capture from CCDC. You need to change ISR routines though. For example, If you are using arago source(http://arago-project.org/git/projects/?p=linux-davinci.git;a=shortlog;h=refs/heads/r37), you can change vpfe_isr() and vpfe_vdint1_isr() to capture interlaced video frames. In addition to above changes, you need to change programming of CCDC's SDOFST register in CCDC driver module [ Refer to section 6.1.15 of VPFE user guide]. Regards, Nag ________________________________________ From: davinci-linux-open-source-bounces at linux.davincidsp.com [davinci-linux-open-source-bounces at linux.davincidsp.com] On Behalf Of Paul Stuart [Paul_Stuart at seektech.com] Sent: Friday, July 15, 2011 4:29 AM To: davinci-linux-open-source at linux.davincidsp.com Subject: DM365: How to disable deinterlacing in Chained Resizer mode Hello, Bashing my head against the wall trying to get the VPFE drivers to send up both fields of D1 interlaced video so I can use the DEI algorithm for deinterlacing instead of the poor-man's method of just throwing a field away. Anyone have any clues as how to modify the driver to make this happen in chained mode? I was hoping that just preserving the vertical resolution in ipipe_set_input_win (and ipipe_get_input_win) would work, but that just makes my system hang. There have been allusions in other threads about the need to modify some ISR's to make this work. Anyone trod down this path? Thanks, Paul _______________________________________________ Davinci-linux-open-source mailing list Davinci-linux-open-source at linux.davincidsp.com http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source From nagabhushana.netagunte at ti.com Tue Jul 19 10:35:29 2011 From: nagabhushana.netagunte at ti.com (Netagunte, Nagabhushana) Date: Tue, 19 Jul 2011 21:05:29 +0530 Subject: DM365: How to disable deinterlacing in Chained Resizer mode In-Reply-To: <7F1B6BBBDF05C649BBBA3C853F488A611B016482A6@Hawking.deepsea.com> References: <1310554333-25242-1-git-send-email-prakash.pm@ti.com> <4E1EC677.7010305@mvista.com> , <7F1B6BBBDF05C649BBBA3C853F488A611B01647FE5@Hawking.deepsea.com> , <7F1B6BBBDF05C649BBBA3C853F488A611B016482A6@Hawking.deepsea.com> Message-ID: Paul, Lets continue this discussion on e2e forum as I see lot of questions on forum . http://e2e.ti.com/support/dsp/davinci_digital_media_processors/f/100/p/123233/439517.aspx#439517 Regards, Nag ________________________________________ From: Paul Stuart [Paul_Stuart at seektech.com] Sent: Tuesday, July 19, 2011 8:12 PM To: Netagunte, Nagabhushana; davinci-linux-open-source at linux.davincidsp.com Subject: RE: DM365: How to disable deinterlacing in Chained Resizer mode Nag, Thanks for getting in touch with me, really appreciate it. I'm missing the big picture here. What, in broad strokes, do I need to change about the ISR routine to make this work? Just need the general concept, 'cause I'm missing it. My understanding is that the two fields are stored interleaved in memory. Where is the magic, in chained mode, that makes the resizer skip every other line? In previous experiments, I modified SDOFST to not skip lines, but it still looked like "normal" video. Does this register change behavior in chained mode, or only in one shot when we pull from SDRAM? Thanks! Paul -----Original Message----- From: Netagunte, Nagabhushana [mailto:nagabhushana.netagunte at ti.com] Sent: Tuesday, July 19, 2011 7:03 AM To: Paul Stuart; davinci-linux-open-source at linux.davincidsp.com Subject: RE: DM365: How to disable deinterlacing in Chained Resizer mode Paul, If you wan to capture D1 interlaced video from VPFE, why you wan to connect previewer and reszier in chained mode with CCDC? Simple way will be to capture from CCDC. You need to change ISR routines though. For example, If you are using arago source(http://arago-project.org/git/projects/?p=linux-davinci.git;a=shortlog;h=refs/heads/r37), you can change vpfe_isr() and vpfe_vdint1_isr() to capture interlaced video frames. In addition to above changes, you need to change programming of CCDC's SDOFST register in CCDC driver module [ Refer to section 6.1.15 of VPFE user guide]. Regards, Nag ________________________________________ From: davinci-linux-open-source-bounces at linux.davincidsp.com [davinci-linux-open-source-bounces at linux.davincidsp.com] On Behalf Of Paul Stuart [Paul_Stuart at seektech.com] Sent: Friday, July 15, 2011 4:29 AM To: davinci-linux-open-source at linux.davincidsp.com Subject: DM365: How to disable deinterlacing in Chained Resizer mode Hello, Bashing my head against the wall trying to get the VPFE drivers to send up both fields of D1 interlaced video so I can use the DEI algorithm for deinterlacing instead of the poor-man's method of just throwing a field away. Anyone have any clues as how to modify the driver to make this happen in chained mode? I was hoping that just preserving the vertical resolution in ipipe_set_input_win (and ipipe_get_input_win) would work, but that just makes my system hang. There have been allusions in other threads about the need to modify some ISR's to make this work. Anyone trod down this path? Thanks, Paul _______________________________________________ Davinci-linux-open-source mailing list Davinci-linux-open-source at linux.davincidsp.com http://linux.davincidsp.com/mailman/listinfo/davinci-linux-open-source From sakari.ailus at iki.fi Tue Jul 19 14:23:36 2011 From: sakari.ailus at iki.fi (Sakari Ailus) Date: Tue, 19 Jul 2011 22:23:36 +0300 Subject: [RFC PATCH 1/8] davinci: vpfe: add dm3xx IPIPEIF hardware support module In-Reply-To: References: <1309439597-15998-1-git-send-email-manjunath.hadli@ti.com> <1309439597-15998-2-git-send-email-manjunath.hadli@ti.com> <20110713185050.GC27451@valkosipuli.localdomain> Message-ID: <4E25D9B8.9070109@iki.fi> Hadli, Manjunath wrote: > Sakari, Thank you for your comments. I agree with them and fix. > Please comment on the rest of the patches as well. > -Manju> Hi Manju, I'll attempt to find more time for this. [clip] >>> +/* CFG1 Masks and shifts */ >>> +#define ONESHOT_SHIFT (0) >>> +#define DECIM_SHIFT (1) >>> +#define INPSRC_SHIFT (2) >>> +#define CLKDIV_SHIFT (4) >>> +#define AVGFILT_SHIFT (7) >>> +#define PACK8IN_SHIFT (8) >>> +#define IALAW_SHIFT (9) >>> +#define CLKSEL_SHIFT (10) >>> +#define DATASFT_SHIFT (11) >>> +#define INPSRC1_SHIFT (14) >> >> IPIPEIF prefix. Are these related to a particular register or a set of registers? > One register. Will need to add the IPIPEIF prefix. Assuming CFG1 is the name of the register, what about IPIPEIF_CFG1_...? >>> +/* DPC2 */ >>> +#define IPIPEIF_DPC2_EN_SHIFT (12) >>> +#define IPIPEIF_DPC2_THR_MASK (0xFFF) >>> +#define IPIPEIF_DF_GAIN_EN_SHIFT (10) >>> +#define IPIPEIF_DF_GAIN_MASK (0x3FF) >>> +#define IPIPEIF_DF_GAIN_THR_MASK (0xFFF) Also all of these should have DPC2 prefix before DPC2 if they're all for the same register. Regards, -- Sakari Ailus sakari.ailus at iki.fi From sudhakar.raj at ti.com Wed Jul 20 05:45:30 2011 From: sudhakar.raj at ti.com (Rajashekhara, Sudhakar) Date: Wed, 20 Jul 2011 16:15:30 +0530 Subject: [PATCH] ASoC: davinci: bug fixes for DM365 voice codec driver Message-ID: <1311158730-12412-1-git-send-email-sudhakar.raj@ti.com> This patch fixes the following bugs found in DaVinci voice codec driver. a. According to DM365 voice codec data sheet at [1], before starting recording or playback, ADC/DAC modules should follow a reset and enable cycle. Writing a 1 to the ADC/DAC bit in the register resets the module and clearing the bit to 0 will enable the module. But the driver seems to be doing the reverse of it. b. In davinci_vcif_trigger() function, a break() statement was missing causing the davinci_vcif_stop() function to be called as a fallback after calling davinci_vcif_start(). [1] http://focus.ti.com/lit/ug/sprufi9b/sprufi9b.pdf Signed-off-by: Rajashekhara, Sudhakar --- sound/soc/davinci/davinci-vcif.c | 9 +++++---- 1 files changed, 5 insertions(+), 4 deletions(-) diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index 9259f1f..1f11525 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c @@ -62,9 +62,9 @@ static void davinci_vcif_start(struct snd_pcm_substream *substream) w = readl(davinci_vc->base + DAVINCI_VC_CTRL); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0); else - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0); writel(w, davinci_vc->base + DAVINCI_VC_CTRL); } @@ -80,9 +80,9 @@ static void davinci_vcif_stop(struct snd_pcm_substream *substream) /* Reset transmitter/receiver and sample rate/frame sync generators */ w = readl(davinci_vc->base + DAVINCI_VC_CTRL); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1); else - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1); writel(w, davinci_vc->base + DAVINCI_VC_CTRL); } @@ -159,6 +159,7 @@ static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: davinci_vcif_start(substream); + break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -- 1.7.1 From nsekhar at ti.com Wed Jul 20 06:34:55 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 20 Jul 2011 17:04:55 +0530 Subject: [PATCH] ASoC: davinci: bug fixes for DM365 voice codec driver In-Reply-To: <1311158730-12412-1-git-send-email-sudhakar.raj@ti.com> References: <1311158730-12412-1-git-send-email-sudhakar.raj@ti.com> Message-ID: Hi Sudhakar, On Wed, Jul 20, 2011 at 16:15:30, Rajashekhara, Sudhakar wrote: > This patch fixes the following bugs found in DaVinci voice codec driver. > > a. According to DM365 voice codec data sheet at [1], before starting > recording or playback, ADC/DAC modules should follow a reset and > enable cycle. Writing a 1 to the ADC/DAC bit in the register resets > the module and clearing the bit to 0 will enable the module. But the > driver seems to be doing the reverse of it. > > b. In davinci_vcif_trigger() function, a break() statement was missing > causing the davinci_vcif_stop() function to be called as a fallback > after calling davinci_vcif_start(). These should be two different patches. Also, please CC the ALSA maintainers. Thanks, Sekhar From sudhakar.raj at ti.com Wed Jul 20 07:06:04 2011 From: sudhakar.raj at ti.com (Rajashekhara, Sudhakar) Date: Wed, 20 Jul 2011 17:36:04 +0530 Subject: [PATCH v2 1/2] ASoC: davinci: fix codec start and stop functions Message-ID: <1311163564-24526-1-git-send-email-sudhakar.raj@ti.com> According to DM365 voice codec data sheet at [1], before starting recording or playback, ADC/DAC modules should follow a reset and enable cycle. Writing a 1 to the ADC/DAC bit in the register resets the module and clearing the bit to 0 will enable the module. But the driver seems to be doing the reverse of it. [1] http://focus.ti.com/lit/ug/sprufi9b/sprufi9b.pdf Signed-off-by: Rajashekhara, Sudhakar --- sound/soc/davinci/davinci-vcif.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index 9259f1f..c957e9e 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c @@ -62,9 +62,9 @@ static void davinci_vcif_start(struct snd_pcm_substream *substream) w = readl(davinci_vc->base + DAVINCI_VC_CTRL); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0); else - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0); writel(w, davinci_vc->base + DAVINCI_VC_CTRL); } @@ -80,9 +80,9 @@ static void davinci_vcif_stop(struct snd_pcm_substream *substream) /* Reset transmitter/receiver and sample rate/frame sync generators */ w = readl(davinci_vc->base + DAVINCI_VC_CTRL); if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 0); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTDAC, 1); else - MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 0); + MOD_REG_BIT(w, DAVINCI_VC_CTRL_RSTADC, 1); writel(w, davinci_vc->base + DAVINCI_VC_CTRL); } -- 1.7.1 From sudhakar.raj at ti.com Wed Jul 20 07:19:34 2011 From: sudhakar.raj at ti.com (Rajashekhara, Sudhakar) Date: Wed, 20 Jul 2011 17:49:34 +0530 Subject: [PATCH] ASoC: davinci: bug fixes for DM365 voice codec driver In-Reply-To: References: <1311158730-12412-1-git-send-email-sudhakar.raj@ti.com> Message-ID: Hi Sekhar, On Wed, Jul 20, 2011 at 17:04:55, Nori, Sekhar wrote: > Hi Sudhakar, > > On Wed, Jul 20, 2011 at 16:15:30, Rajashekhara, Sudhakar wrote: > > This patch fixes the following bugs found in DaVinci voice codec driver. > > > > a. According to DM365 voice codec data sheet at [1], before starting > > recording or playback, ADC/DAC modules should follow a reset and > > enable cycle. Writing a 1 to the ADC/DAC bit in the register resets > > the module and clearing the bit to 0 will enable the module. But the > > driver seems to be doing the reverse of it. > > > > b. In davinci_vcif_trigger() function, a break() statement was missing > > causing the davinci_vcif_stop() function to be called as a fallback > > after calling davinci_vcif_start(). > > These should be two different patches. Also, please CC the ALSA maintainers. > I'll split the patches and resend them. Regards, Sudhakar From sudhakar.raj at ti.com Wed Jul 20 07:07:18 2011 From: sudhakar.raj at ti.com (Rajashekhara, Sudhakar) Date: Wed, 20 Jul 2011 17:37:18 +0530 Subject: [PATCH v2 2/2] ASoC: davinci: add missing break statement Message-ID: <1311163638-24712-1-git-send-email-sudhakar.raj@ti.com> In davinci_vcif_trigger() function, a break() statement was missing causing the davinci_vcif_stop() function to be called as a fallback after calling davinci_vcif_start(). Signed-off-by: Rajashekhara, Sudhakar --- sound/soc/davinci/davinci-vcif.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-) diff --git a/sound/soc/davinci/davinci-vcif.c b/sound/soc/davinci/davinci-vcif.c index c957e9e..1f11525 100644 --- a/sound/soc/davinci/davinci-vcif.c +++ b/sound/soc/davinci/davinci-vcif.c @@ -159,6 +159,7 @@ static int davinci_vcif_trigger(struct snd_pcm_substream *substream, int cmd, case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: davinci_vcif_start(substream); + break; case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_SUSPEND: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: -- 1.7.1 From lrg at ti.com Wed Jul 20 11:25:26 2011 From: lrg at ti.com (Liam Girdwood) Date: Wed, 20 Jul 2011 17:25:26 +0100 Subject: [PATCH v2 1/2] ASoC: davinci: fix codec start and stop functions In-Reply-To: <1311163564-24526-1-git-send-email-sudhakar.raj@ti.com> References: <1311163564-24526-1-git-send-email-sudhakar.raj@ti.com> Message-ID: <4E270176.5070904@ti.com> On 20/07/11 13:06, Rajashekhara, Sudhakar wrote: > According to DM365 voice codec data sheet at [1], before starting > recording or playback, ADC/DAC modules should follow a reset and > enable cycle. Writing a 1 to the ADC/DAC bit in the register resets > the module and clearing the bit to 0 will enable the module. But the > driver seems to be doing the reverse of it. > > [1] http://focus.ti.com/lit/ug/sprufi9b/sprufi9b.pdf > > Signed-off-by: Rajashekhara, Sudhakar > --- Both Acked-by: Liam Girdwood From nsekhar at ti.com Wed Jul 20 11:40:57 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 20 Jul 2011 22:10:57 +0530 Subject: [PATCH] davinci: fix DM365 EVM video input mux bits In-Reply-To: <20110719105532.GF26574@n2100.arm.linux.org.uk> References: <1311046211-2849-1-git-send-email-jon.povey@racelogic.co.uk> <20110719105532.GF26574@n2100.arm.linux.org.uk> Message-ID: Hi Russell, On Tue, Jul 19, 2011 at 16:25:32, Russell King - ARM Linux wrote: > On Tue, Jul 19, 2011 at 02:26:50PM +0530, Nori, Sekhar wrote: > > Since this part of the code hasn't changed since it > > was written, this is a good candidate for stable > > kernel series. You can just add: > > > > Cc: stable at kernel.org > > Be careful when doing that - if you have git mail the patches out for > review, and it picks that up, it'll send it to the stable team as well. > The stable team will then complain "that's not how to submit patches > to them". > > So, while you can add "Cc: stable at kernel.org" to the patch, its best > to only add that just before you publish the commit after review, but > before merging. But its very tough to guess what the last version ready for merging will be. Review comments can come at any stage. Would adding "Cc: stable at kernel.org" to the commit text but taking care to not actually send mail to stable at kernel.org help? The stable_kernel_rules.txt document seems to suggest this is an acceptable way: " - To have the patch automatically included in the stable tree, add the tag Cc: stable at kernel.org in the sign-off area. Once the patch is merged it will be applied to the stable tree without anything else needing to be done by the author or subsystem maintainer. " Thanks, Sekhar From linux at arm.linux.org.uk Wed Jul 20 11:48:07 2011 From: linux at arm.linux.org.uk (Russell King - ARM Linux) Date: Wed, 20 Jul 2011 17:48:07 +0100 Subject: [PATCH] davinci: fix DM365 EVM video input mux bits In-Reply-To: References: <1311046211-2849-1-git-send-email-jon.povey@racelogic.co.uk> <20110719105532.GF26574@n2100.arm.linux.org.uk> Message-ID: <20110720164807.GA20310@n2100.arm.linux.org.uk> On Wed, Jul 20, 2011 at 10:10:57PM +0530, Nori, Sekhar wrote: > Hi Russell, > > On Tue, Jul 19, 2011 at 16:25:32, Russell King - ARM Linux wrote: > > On Tue, Jul 19, 2011 at 02:26:50PM +0530, Nori, Sekhar wrote: > > > Since this part of the code hasn't changed since it > > > was written, this is a good candidate for stable > > > kernel series. You can just add: > > > > > > Cc: stable at kernel.org > > > > Be careful when doing that - if you have git mail the patches out for > > review, and it picks that up, it'll send it to the stable team as well. > > The stable team will then complain "that's not how to submit patches > > to them". > > > > So, while you can add "Cc: stable at kernel.org" to the patch, its best > > to only add that just before you publish the commit after review, but > > before merging. > > But its very tough to guess what the last version > ready for merging will be. Review comments can come > at any stage. > > Would adding "Cc: stable at kernel.org" to the commit text > but taking care to not actually send mail to stable at kernel.org > help? The stable_kernel_rules.txt document seems to suggest > this is an acceptable way: Correct. From broonie at opensource.wolfsonmicro.com Wed Jul 20 14:52:03 2011 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Wed, 20 Jul 2011 20:52:03 +0100 Subject: [PATCH v2 1/2] ASoC: davinci: fix codec start and stop functions In-Reply-To: <1311163564-24526-1-git-send-email-sudhakar.raj@ti.com> References: <1311163564-24526-1-git-send-email-sudhakar.raj@ti.com> Message-ID: <20110720195203.GB26043@opensource.wolfsonmicro.com> On Wed, Jul 20, 2011 at 05:36:04PM +0530, Rajashekhara, Sudhakar wrote: > According to DM365 voice codec data sheet at [1], before starting > recording or playback, ADC/DAC modules should follow a reset and > enable cycle. Writing a 1 to the ADC/DAC bit in the register resets > the module and clearing the bit to 0 will enable the module. But the > driver seems to be doing the reverse of it. Applied, thanks. From broonie at opensource.wolfsonmicro.com Wed Jul 20 14:52:12 2011 From: broonie at opensource.wolfsonmicro.com (Mark Brown) Date: Wed, 20 Jul 2011 20:52:12 +0100 Subject: [PATCH v2 2/2] ASoC: davinci: add missing break statement In-Reply-To: <1311163638-24712-1-git-send-email-sudhakar.raj@ti.com> References: <1311163638-24712-1-git-send-email-sudhakar.raj@ti.com> Message-ID: <20110720195212.GC26043@opensource.wolfsonmicro.com> On Wed, Jul 20, 2011 at 05:37:18PM +0530, Rajashekhara, Sudhakar wrote: > In davinci_vcif_trigger() function, a break() statement was missing > causing the davinci_vcif_stop() function to be called as a fallback > after calling davinci_vcif_start(). Applied, thanks. From nsekhar at ti.com Thu Jul 21 04:00:25 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 21 Jul 2011 14:30:25 +0530 Subject: AM 1808, Angstrom In-Reply-To: <4E254058.5020103@lumino.de> References: <4E254058.5020103@lumino.de> Message-ID: Hi Michael, On Tue, Jul 19, 2011 at 13:59:12, Michael Schnell wrote: > Hi experts. > > I suppose the AMxxx devices are not exactly "Davinci" branded, but as > this seems to be a very active group of TI Software experts, I dare to > ask here. This is a good forum to ask AM1x kernel questions. There are other AMx devices like AM37x which are "omap" devices. Kernel questions for those devices can be posted to linux-omap at vger.kernel.org. Which list to use depends on whether the device is supported under mach-davinci or mach-omap2. > We are starting to design a controller based on an AM1808 (or maybe an > upcoming AM 335x) chip. > > Right now, we just have a Development kit (by Logic PD) that features an > OMAP 138 Chip (that is pin-compatible with the AM1808 but additionally > has a DSP) > > We started playing with this kit and now I have several questions that > you might be able to answer. > > > > The board came with a "Angstrom" Linux distribution. > > (Question 1) Can you give us a hint, whether it's a good idea to use > Angstrom for an embedded controller or do you favor other distributions > (binary and/or cross compilable-source-code) This is not a good forum to ask Linux distribution questions. Those questions are better asked on the e2e.ti.com "Embedded Software" forum or on the arago support lists: http://arago-project.org/wiki/index.php/FAQ#Q:_Is_there_a_mailing_list_or_IRC_channel.3F I have skipped your other Ansgtrom related questions. > > We want to use I?S audio output. The kit does feature an I?S to analog > converter. Yes, the OMAP-L138 has an AIC3106 audio codec. > (Question 4) How to configure, use and test the audio output ? The audio driver is ALSA compliant. Some audio help for this platform is available here: http://processors.wiki.ti.com/index.php/OMAP-L1_Linux_Drivers_Usage#Audio > We want to use the LCD display. The kit features a socket for the chip's > LCD/Video pins. We are going to get an LCD panel to connect to the board > or to find a converter that allows connecting some standard monitor. > > (Question 5) How to configure, use and test the LCD and/or Video output ? Please see here: http://processors.wiki.ti.com/index.php/GSG:_Building_Software_Components_for_OMAP-L1#Graphical_LCD In general, please use this page as a starting point for kernel related information on OMAP-L138: http://processors.wiki.ti.com/index.php/Community_Linux_PSP_for_DA8x/OMAP-L1/AM1x Thanks, Sekhar From mschnell at lumino.de Thu Jul 21 04:47:05 2011 From: mschnell at lumino.de (Michael Schnell) Date: Thu, 21 Jul 2011 11:47:05 +0200 Subject: AM 1808, Angstrom In-Reply-To: References: <4E254058.5020103@lumino.de> Message-ID: <4E27F599.6040605@lumino.de> On 07/21/2011 11:00 AM, Nori, Sekhar wrote: > This is a good forum to ask AM1x kernel questions. There are other > AMx devices like AM37x which are "omap" devices. Kernel questions > for those devices can be posted to linux-omap at vger.kernel.org. > > Which list to use depends on whether the device is supported under > mach-davinci or mach-omap2. I see. Thanks for the pointers. I did find and the register Angstrom mailing list and they in fact were able to answer most of my current questions. > This is not a good forum to ask Linux distribution questions. > Those questions are better asked on the e2e.ti.com "Embedded > Software" forum or on the arago support lists: http://arago-project.org/wiki/index.php/FAQ#Q:_Is_there_a_mailing_list_or_IRC_channel.3F I see. Thanks. > Yes, the OMAP-L138 has an AIC3106 audio codec. I'll try to find out more on this. > The audio driver is ALSA compliant. Some audio help for this > platform is available here: http://processors.wiki.ti.com/index.php/OMAP-L1_Linux_Drivers_Usage#Audio I'll take a look ASAP. > Please see here: > http://processors.wiki.ti.com/index.php/GSG:_Building_Software_Components_for_OMAP-L1#Graphical_LCD Thanks for the pointers ! > In general, please use this page as a starting point for kernel > related information on OMAP-L138: > > http://processors.wiki.ti.com/index.php/Community_Linux_PSP_for_DA8x/OMAP-L1/AM1x > I'll take a look there. In fact we want finally to use an AM1808 or the upcoming AM 335x. But I suppose regarding the software the "OMAP" products are very similar. Thanks again, -Michael From sudhakar.raj at ti.com Thu Jul 21 04:39:23 2011 From: sudhakar.raj at ti.com (Rajashekhara, Sudhakar) Date: Thu, 21 Jul 2011 15:09:23 +0530 Subject: [PATCH] mtd: nand: davinci: Add cpufreq support Message-ID: <1311241163-4576-1-git-send-email-sudhakar.raj@ti.com> This patch adds cpufreq support for NAND driver. During cpufreq transition, 'davinci_aemif_setup_timing()' function will be called to reconfigure that AEMIF timings for the new frequency. Tested on TI DA850/OMAP-L138 EVM. Signed-off-by: Rajashekhara, Sudhakar --- drivers/mtd/nand/davinci_nand.c | 54 +++++++++++++++++++++++++++++++++++++++ 1 files changed, 54 insertions(+), 0 deletions(-) diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 1f34951..7e764af 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -74,6 +75,9 @@ struct davinci_nand_info { uint32_t core_chipsel; struct davinci_aemif_timing *timing; +#ifdef CONFIG_CPU_FREQ + struct notifier_block freq_transition; +#endif }; static DEFINE_SPINLOCK(davinci_nand_lock); @@ -519,6 +523,47 @@ static struct nand_ecclayout hwecc4_2048 __initconst = { }, }; +#ifdef CONFIG_CPU_FREQ +static int nand_davinci_cpufreq_transition(struct notifier_block *nb, + unsigned long val, void *data) +{ + struct davinci_nand_info *info; + + info = container_of(nb, struct davinci_nand_info, freq_transition); + + if (val == CPUFREQ_POSTCHANGE) + davinci_aemif_setup_timing(info->timing, info->base, + info->core_chipsel); + + return 0; +} + +static inline int nand_davinci_cpufreq_register(struct davinci_nand_info *info) +{ + info->freq_transition.notifier_call = nand_davinci_cpufreq_transition; + + return cpufreq_register_notifier(&info->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} + +static inline void nand_davinci_cpufreq_deregister(struct davinci_nand_info + *info) +{ + cpufreq_unregister_notifier(&info->freq_transition, + CPUFREQ_TRANSITION_NOTIFIER); +} +#else +static inline int nand_davinci_cpufreq_register(struct davinci_nand_info *info) +{ + return 0; +} + +static inline void nand_davinci_cpufreq_deregister(struct davinci_nand_info + *info) +{ +} +#endif + static int __init nand_davinci_probe(struct platform_device *pdev) { struct davinci_nand_pdata *pdata = pdev->dev.platform_data; @@ -782,12 +827,19 @@ syndrome_done: if (ret < 0) goto err_scan; + ret = nand_davinci_cpufreq_register(info); + if (ret) { + dev_err(&pdev->dev, "failed to register cpufreq\n"); + goto err_cpu_freq_fail; + } + val = davinci_nand_readl(info, NRCSR_OFFSET); dev_info(&pdev->dev, "controller rev. %d.%d\n", (val >> 8) & 0xff, val & 0xff); return 0; +err_cpu_freq_fail: err_scan: err_timing: clk_disable(info->clk); @@ -818,6 +870,8 @@ static int __exit nand_davinci_remove(struct platform_device *pdev) struct davinci_nand_info *info = platform_get_drvdata(pdev); int status; + nand_davinci_cpufreq_deregister(info); + status = mtd_device_unregister(&info->mtd); spin_lock_irq(&davinci_nand_lock); -- 1.7.1 From nsekhar at ti.com Thu Jul 21 05:19:47 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Thu, 21 Jul 2011 15:49:47 +0530 Subject: [PATCH] [RESENDING] Enable USB on TI DM365 In-Reply-To: <1310467956-21739-1-git-send-email-const@MakeLinux.com> References: <4E085FEE.5090301@mvista.com> <1310467956-21739-1-git-send-email-const@MakeLinux.com> Message-ID: Hello, Please Cc linux-arm-kernel at lists.infradead.org for all arch/arm/mach-davinci/* patch submissions. Apart from what Sergei said, please take care of the following: On Tue, Jul 12, 2011 at 16:22:36, const at MakeLinux.com wrote: > # Common objects > obj-y := time.o clock.o serial.o io.o psc.o \ > - gpio.o dma.o usb.o common.o sram.o aemif.o > + gpio.o dma.o common.o sram.o aemif.o > > obj-$(CONFIG_DAVINCI_MUX) += mux.o > > @@ -40,3 +40,4 @@ obj-$(CONFIG_MACH_OMAPL138_HAWKBOARD) += board-omapl138-hawk.o > obj-$(CONFIG_CPU_FREQ) += cpufreq.o > obj-$(CONFIG_CPU_IDLE) += cpuidle.o > obj-$(CONFIG_SUSPEND) += pm.o sleep.o > +obj-$(CONFIG_USB_MUSB_DAVINCI) += usb.o These makefile changes should be separate patch by itself since it affects all platforms not just DM365. > diff --git a/arch/arm/mach-davinci/include/mach/usb.h b/arch/arm/mach-davinci/include/mach/usb.h > index e0bc4ab..863c27f 100644 > --- a/arch/arm/mach-davinci/include/mach/usb.h > +++ b/arch/arm/mach-davinci/include/mach/usb.h > @@ -54,6 +54,6 @@ struct da8xx_ohci_root_hub { > u8 potpgt; > }; > > -void davinci_setup_usb(unsigned mA, unsigned potpgt_ms); > +int davinci_setup_usb(unsigned mA, unsigned potpgt_ms); > > #endif /* ifndef __ASM_ARCH_USB_H */ > diff --git a/arch/arm/mach-davinci/usb.c b/arch/arm/mach-davinci/usb.c > index 23d2b6d..daf5398 100644 > --- a/arch/arm/mach-davinci/usb.c > +++ b/arch/arm/mach-davinci/usb.c > @@ -4,13 +4,14 @@ > #include > #include > #include > - > #include > > #include > #include > #include > #include > +#include > +#include > > #define DAVINCI_USB_OTG_BASE 0x01c64000 > > @@ -87,7 +88,7 @@ static struct platform_device usb_dev = { > .num_resources = ARRAY_SIZE(usb_resources), > }; > > -void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > +int __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > { > usb_data.power = mA > 510 ? 255 : mA / 2; > usb_data.potpgt = (potpgt_ms + 1) / 2; > @@ -99,7 +100,7 @@ void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > } else /* other devices don't have dedicated CPPI IRQ */ > usb_dev.num_resources = 2; > > - platform_device_register(&usb_dev); > + return platform_device_register(&usb_dev); > } > > #ifdef CONFIG_ARCH_DAVINCI_DA8XX > @@ -132,7 +133,7 @@ int __init da8xx_register_usb20(unsigned mA, unsigned potpgt) > > #else > > -void __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > +int __init davinci_setup_usb(unsigned mA, unsigned potpgt_ms) > { > } This looks like a bugfix/enhancement which is not related to adding DM365 support. Please keep it separate. > > @@ -178,3 +179,23 @@ int __init da8xx_register_usb11(struct da8xx_ohci_root_hub *pdata) > return platform_device_register(&da8xx_usb11_device); > } > #endif /* CONFIG_DAVINCI_DA8XX */ > + > +#ifdef ARCH_DAVINCI_DM365 > +int __init dm365_usb_configure(void) > +{ > + /* GPIO33 is multiplexed with USB DRVVBUS */ > + davinci_cfg_reg(DM365_GPIO33); > + gpio_request(33, "usb"); This can fail. Also use gpio_request_one(). > + gpio_direction_output(33, 1); > + return davinci_setup_usb(500, 8); > +} > +subsys_initcall(dm365_usb_configure); > + > +static void __exit dm365evm_usb_exit(void) > +{ > + platform_device_unregister(&usb_dev); > +} > +module_exit(dm365evm_usb_exit); This is board code which should go into board-dm365-evm.c Also, please don't use initcalls for this kind of code, simply invoke the API in the board init sequence. > +#endif > + > +MODULE_LICENSE("GPL"); > diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c > index 2a2adf6..e6721e1 100644 > --- a/drivers/usb/musb/davinci.c > +++ b/drivers/usb/musb/davinci.c > @@ -72,6 +72,15 @@ static inline void phy_on(void) > /* power everything up; start the on-chip PHY and its PLL */ > phy_ctrl &= ~(USBPHY_OSCPDWN | USBPHY_OTGPDWN | USBPHY_PHYPDWN); > phy_ctrl |= USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON; > + > + if (cpu_is_davinci_dm365()) { > + /* > + * DM365 PHYCLKFREQ field [15:12] is set to 2 > + * to get clock from 24MHz crystal > + */ > + phy_ctrl |= USBPHY_CLKFREQ_24MHZ; > + } Please separate out the DM365 SoC specific changes into a separate patch and CC the USB maintainers Felipe/Greg K-H on it. Also, CC the linux-usb list. scripts/get_maintainer.pl is there to help you find the right maintainers for the code your are submitting. > + > __raw_writel(phy_ctrl, USB_PHY_CTRL); > > /* wait for PLL to lock before proceeding */ > @@ -193,6 +202,9 @@ static void davinci_musb_source_power(struct musb *musb, int is_on, int immediat > else > schedule_work(&evm_vbus_work); > } > + > + if (cpu_is_davinci_dm365()) > + gpio_set_value(33, is_on); This again looks like an EVM specific change so should be confined to board-dm365-evm.c. > if (immediate) > vbus_state = is_on; > #endif > diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h > index 046c844..1bf50e6 100644 > --- a/drivers/usb/musb/davinci.h > +++ b/drivers/usb/musb/davinci.h > @@ -17,6 +17,7 @@ > /* Integrated highspeed/otg PHY */ > #define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) > #define USBPHY_DATAPOL BIT(11) /* (dm355) switch D+/D- */ > +#define USBPHY_CLKFREQ_24MHZ BIT(13) Please use tabs for indentation. Thanks, Sekhar From sshtylyov at mvista.com Thu Jul 21 05:40:35 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Thu, 21 Jul 2011 14:40:35 +0400 Subject: [PATCH] [RESENDING] Enable USB on TI DM365 In-Reply-To: References: <4E085FEE.5090301@mvista.com> <1310467956-21739-1-git-send-email-const@MakeLinux.com> Message-ID: <4E280223.1070008@mvista.com> Hello. On 21-07-2011 14:19, Nori, Sekhar wrote: > Apart from what Sergei said, please take care of the following: I've alresdy reviewed the first posting in detail, and we've exchanged some porivate emails with Constantin too... >> diff --git a/arch/arm/mach-davinci/usb.c b/arch/arm/mach-davinci/usb.c >> index 23d2b6d..daf5398 100644 >> --- a/arch/arm/mach-davinci/usb.c >> +++ b/arch/arm/mach-davinci/usb.c [...] >> @@ -178,3 +179,23 @@ int __init da8xx_register_usb11(struct da8xx_ohci_root_hub *pdata) >> return platform_device_register(&da8xx_usb11_device); >> } >> #endif /* CONFIG_DAVINCI_DA8XX */ >> + >> +#ifdef ARCH_DAVINCI_DM365 >> +int __init dm365_usb_configure(void) >> +{ >> + /* GPIO33 is multiplexed with USB DRVVBUS */ >> + davinci_cfg_reg(DM365_GPIO33); >> + gpio_request(33, "usb"); > This can fail. Also use gpio_request_one(). The main question as it turned out is why GPIO API was used at all. DM365 has real VBUS signal and it is *multiplexed* to that GPIO. I keep wondering why VBUS signal was not directly controlled... >> + gpio_direction_output(33, 1); >> + return davinci_setup_usb(500, 8); >> +} >> +subsys_initcall(dm365_usb_configure); >> + >> +static void __exit dm365evm_usb_exit(void) >> +{ >> + platform_device_unregister(&usb_dev); >> +} >> +module_exit(dm365evm_usb_exit); > This is board code which should go into board-dm365-evm.c That I've noted when reviewing the first posting of this patch. >> diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c >> index 2a2adf6..e6721e1 100644 >> --- a/drivers/usb/musb/davinci.c >> +++ b/drivers/usb/musb/davinci.c >> @@ -72,6 +72,15 @@ static inline void phy_on(void) >> /* power everything up; start the on-chip PHY and its PLL */ >> phy_ctrl&= ~(USBPHY_OSCPDWN | USBPHY_OTGPDWN | USBPHY_PHYPDWN); >> phy_ctrl |= USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON; >> + >> + if (cpu_is_davinci_dm365()) { >> + /* >> + * DM365 PHYCLKFREQ field [15:12] is set to 2 >> + * to get clock from 24MHz crystal >> + */ >> + phy_ctrl |= USBPHY_CLKFREQ_24MHZ; >> + } > Please separate out the DM365 SoC specific changes into > a separate patch and CC the USB maintainers Felipe/Greg K-H > on it. Also, CC the linux-usb list. scripts/get_maintainer.pl > is there to help you find the right maintainers for the > code your are submitting. I think this code is actually board specific, and should be in board-dm365-evm.c instead... >> + >> __raw_writel(phy_ctrl, USB_PHY_CTRL); >> >> /* wait for PLL to lock before proceeding */ >> @@ -193,6 +202,9 @@ static void davinci_musb_source_power(struct musb *musb, int is_on, int immediat >> else >> schedule_work(&evm_vbus_work); >> } >> + >> + if (cpu_is_davinci_dm365()) >> + gpio_set_value(33, is_on); > This again looks like an EVM specific change so > should be confined to board-dm365-evm.c. This time it's DM365 specific as that GPIO is *multiplexed* to VBUS. WBR, Sergei From jassisinghbrar at gmail.com Sat Jul 23 15:35:28 2011 From: jassisinghbrar at gmail.com (Jassi Brar) Date: Sun, 24 Jul 2011 02:05:28 +0530 Subject: [PATCH] dmaengine: add dma_ctrl_cmd to pass buffer stride configuration In-Reply-To: References: <1310310203-12288-1-git-send-email-sundaram@ti.com> Message-ID: On Mon, Jul 18, 2011 at 1:21 PM, Raju, Sundaram wrote: >> >> Maybe a new api to pass fixed-format variable-length encoded message >> to the DMAC drivers? >> Which could be interpreted by DMAC drivers to extract all the needed xfer >> parameters from the 'header' section and instructions to program the xfers >> in the DMAC from the variable length body of the 'message' buffer. >> It might sound complicated but we can have helpers to make the job easy. >> Btw, the regular single/sg-list xfers could also be expressed by this method. > > Do you expect this variable length body of the message to be DMAC > independent? I don't think so. In that case how is this different from what > we have here already? Yes, this whould be DMAC independent. > If it can be DMAC independent, can you illustrate more on how this can > be done? But the point to note is, if this can be made DMAC independent > then the control command we have also can be made DMAC independent > by generalizing the configuration structure passed to it. The 'header' I suggest, would in fact be a structure body, only an extra pointer would point to the 'instructions' to convey actual location and sizes of mico-xfers. I don't think it is possible to have general definition of such transfers fully within a structure. If I understand what you ask. I have just posted an RFC. I kept the terms same so that it is easier to understand. Please have a look. You are CC'ed too. Thanks, Jassi From 445470891 at qq.com Sun Jul 24 20:57:34 2011 From: 445470891 at qq.com (=?gbk?B?s8K84Q==?=) Date: Mon, 25 Jul 2011 09:57:34 +0800 Subject: How to uninstall Sourcery G++ Lite 2009q1-203 for ARM GNU/Linux Message-ID: Hi,all I installed Sourcery G++ Lite 2009q1-203 for ARM GNU/Linux in the path /opt/arm-2009q1. My linux system is Ubuntu 11.04. I want to install Sourcery G++ Lite 2008q3-72 now. But i don't know how to uninstall the prior toolchain. Please help me. Thanks! ------------------ -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: not available Type: image/jpeg Size: 4353 bytes Desc: not available URL: From nsekhar at ti.com Mon Jul 25 08:15:51 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 25 Jul 2011 18:45:51 +0530 Subject: [PATCH v2 2/6] arm: davinci: Explicitly set channel controllers' default queues In-Reply-To: <1310303679-17936-3-git-send-email-ido@wizery.com> References: <1310303679-17936-1-git-send-email-ido@wizery.com> <1310303679-17936-3-git-send-email-ido@wizery.com> Message-ID: On Sun, Jul 10, 2011 at 18:44:35, Ido Yariv wrote: > Davinci platforms may define a default queue for each channel > controller. If one is not defined, the default queue is set to EVENTQ_1. > However, there's no way to distinguish between an unset default queue to > one that is set to EVENTQ_0, as EVENTQ_0 = 0. > > Explicitly specify the default queue for all channel controllers on all > Davinci platforms to EVENTQ_1, and don't overwrite it in the EDMA probe > function. > > One exception is the DA850 board, for which EVENTQ_1 is not a valid > option for its second channel controller. Use EVENTQ_0 instead for that > channel controller. > > Signed-off-by: Ido Yariv Looks good to me. Will queue for v3.2/fixes BTW, Arnd has indicated a preference for "ARM: davinci: " prefix so I will make that change while applying. Thanks, Sekhar From nsekhar at ti.com Mon Jul 25 08:19:31 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 25 Jul 2011 18:49:31 +0530 Subject: [PATCH v2 3/6] arm: davinci: mmc: Add support for set_power callback In-Reply-To: <1310303679-17936-4-git-send-email-ido@wizery.com> References: <1310303679-17936-1-git-send-email-ido@wizery.com> <1310303679-17936-4-git-send-email-ido@wizery.com> Message-ID: On Sun, Jul 10, 2011 at 18:44:36, Ido Yariv wrote: > Some devices connected to the MMC bus are power controlled by external > means. For instance, an SDIO device may be powered down/up by an > external gpio line. > > In order to avoid toggling power from within the MMC host driver, add a > set_power callback function, which will be called by set_ios upon > powering down/up. > > Signed-off-by: Ido Yariv > --- > arch/arm/mach-davinci/include/mach/mmc.h | 3 +++ > drivers/mmc/host/davinci_mmc.c | 13 +++++++++++++ > 2 files changed, 16 insertions(+), 0 deletions(-) This looks good to me, but needs Ack/Sign-off from MMC maintainer. Please repost keeping him in CC. Thanks, Sekhar From nsekhar at ti.com Mon Jul 25 08:23:51 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 25 Jul 2011 18:53:51 +0530 Subject: [PATCH v2 5/6] arm: davinci: DA850: Add GPIO pinmux configuration for wl1271 In-Reply-To: <1310303679-17936-6-git-send-email-ido@wizery.com> References: <1310303679-17936-1-git-send-email-ido@wizery.com> <1310303679-17936-6-git-send-email-ido@wizery.com> Message-ID: On Sun, Jul 10, 2011 at 18:44:38, Ido Yariv wrote: > The wl1271 daughter board makes use of a few GPIOs: > GPIO6_9 is used for powering down/up the WLAN functionality. > GPIO6_10 is used as an input IRQ line from the WLAN chip. > > Add the required pinmux configuration for these GPIOs. > > Signed-off-by: Ido Yariv 4/6 and 5/6 both look good, will queue for v3.2/features Thanks, Sekhar From sshtylyov at mvista.com Mon Jul 25 08:33:45 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Mon, 25 Jul 2011 17:33:45 +0400 Subject: [PATCH v2 2/6] arm: davinci: Explicitly set channel controllers' default queues In-Reply-To: References: <1310303679-17936-1-git-send-email-ido@wizery.com> <1310303679-17936-3-git-send-email-ido@wizery.com> Message-ID: <4E2D70B9.7060202@mvista.com> Hello. Nori, Sekhar wrote: >> Davinci platforms may define a default queue for each channel >> controller. If one is not defined, the default queue is set to EVENTQ_1. >> However, there's no way to distinguish between an unset default queue to >> one that is set to EVENTQ_0, as EVENTQ_0 = 0. >> Explicitly specify the default queue for all channel controllers on all >> Davinci platforms to EVENTQ_1, and don't overwrite it in the EDMA probe >> function. >> One exception is the DA850 board, for which EVENTQ_1 is not a valid >> option for its second channel controller. Use EVENTQ_0 instead for that >> channel controller. >> Signed-off-by: Ido Yariv > Looks good to me. Will queue for v3.2/fixes Why wait for 3.2? If this is considered a fix, it should be applied to 3.1, no? WBR, Sergei From nsekhar at ti.com Mon Jul 25 11:31:23 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 25 Jul 2011 22:01:23 +0530 Subject: [PATCH v2 2/6] arm: davinci: Explicitly set channel controllers' default queues In-Reply-To: <4E2D70B9.7060202@mvista.com> References: <1310303679-17936-1-git-send-email-ido@wizery.com> <1310303679-17936-3-git-send-email-ido@wizery.com> <4E2D70B9.7060202@mvista.com> Message-ID: Hi Sergei, On Mon, Jul 25, 2011 at 19:03:45, Sergei Shtylyov wrote: > Hello. > > Nori, Sekhar wrote: > > >> Davinci platforms may define a default queue for each channel > >> controller. If one is not defined, the default queue is set to EVENTQ_1. > >> However, there's no way to distinguish between an unset default queue to > >> one that is set to EVENTQ_0, as EVENTQ_0 = 0. > > >> Explicitly specify the default queue for all channel controllers on all > >> Davinci platforms to EVENTQ_1, and don't overwrite it in the EDMA probe > >> function. > > >> One exception is the DA850 board, for which EVENTQ_1 is not a valid > >> option for its second channel controller. Use EVENTQ_0 instead for that > >> channel controller. > > >> Signed-off-by: Ido Yariv > > > Looks good to me. Will queue for v3.2/fixes > > Why wait for 3.2? If this is considered a fix, it should be applied to 3.1, no? 3.2/fixes just indicates it will be queued as a fix/cleanup for 3.2 so it will have higher priority for merge when compared to a new feature. This patch doesn't really fix any existing broken functionality. It corrects event queue configuration for EDMA CC1 on DA850 for which there are no current users in mainline. So, not sending for v3.1. Thanks, Sekhar From nsekhar at ti.com Mon Jul 25 12:40:55 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Mon, 25 Jul 2011 23:10:55 +0530 Subject: [PATCH v2 6/6] arm: davinci: DA850: Add wl1271/wlan support In-Reply-To: <1310303679-17936-7-git-send-email-ido@wizery.com> References: <1310303679-17936-1-git-send-email-ido@wizery.com> <1310303679-17936-7-git-send-email-ido@wizery.com> Message-ID: On Sun, Jul 10, 2011 at 18:44:39, Ido Yariv wrote: > The wl1271 daughter card for AM18x EVMs is a combo wireless connectivity > add-on card, based on the LS Research TiWi module with Texas > Instruments' wl1271 solution. > It is a 4-wire, 1.8V, embedded SDIO WLAN device with an external IRQ > line and is power-controlled by a GPIO-based fixed regulator. > > This patch adds support for the WLAN capabilities of this expansion > board. > > Signed-off-by: Ido Yariv > --- > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index a7b41bf..2dae1a1 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -31,6 +31,8 @@ > +#ifdef CONFIG_DA850_WL12XX > + > +static int da850_wl12xx_fref = WL12XX_REFCLOCK_38; > + > +static int __init setup_da850_wl12xx_fref(char *fref) > +{ > + if (!strcmp(fref, "19.2")) > + da850_wl12xx_fref = WL12XX_REFCLOCK_19; > + else if (!strcmp(fref, "26")) > + da850_wl12xx_fref = WL12XX_REFCLOCK_26; > + else if (!strcmp(fref, "38.4")) > + da850_wl12xx_fref = WL12XX_REFCLOCK_38; > + else if (!strcmp(fref, "52")) > + da850_wl12xx_fref = WL12XX_REFCLOCK_52; > + else if (!strcmp(fref, "XTAL26")) > + da850_wl12xx_fref = WL12XX_REFCLOCK_26_XTAL; > + else if (!strcmp(fref, "XTAL38.4")) > + da850_wl12xx_fref = WL12XX_REFCLOCK_38_XTAL; > + else > + pr_info("da850_wl12xx_fref is invalid. Valid options: " > + "19.2, 26, 38.4, 52, XTAL26 or XTAL38.4\n"); > + return 0; > +} > +__setup("da850_wl12xx_fref=", setup_da850_wl12xx_fref); Adding a new kernel parameter requires update to Documentation/kernel-parameters.txt as well. I am Ccing a couple of folks in case they have ideas on whether there is a better way to pass this information to the kernel. I assume there is no way to detect this from hardware. > +static struct davinci_mmc_config da850_mmc_wl12xx_config = { > + .get_ro = NULL, > + .get_cd = NULL, You can get rid of these NULL initializers. > + .set_power = wl12xx_set_power, > + .wires = 4, > + .max_freq = 25000000, > + .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NONREMOVABLE | > + MMC_CAP_POWER_OFF_CARD, > + .version = MMC_CTLR_VERSION_2, > +}; > +static void da850_wl12xx_init(void) > +{ > + int ret; > + > + ret = davinci_cfg_reg_list(da850_evm_mmc_wl12xx_pins); > + if (ret) { > + pr_warning("da850_evm_init: wl12xx/mmc mux setup failed:" > + " %d\n", ret); > + return; > + } > + > + ret = da850_register_mmcsd1(&da850_mmc_wl12xx_config); > + if (ret) { > + pr_warning("da850_evm_init: wl12xx/mmc registration failed:" > + " %d\n", ret); > + return; > + } > + > + ret = gpio_request_one(DA850_WLAN_EN, GPIOF_OUT_INIT_LOW, "wl12xx_en"); > + if (ret) { > + pr_err("Error initializing the wl12xx enable gpio: %d\n", ret); > + return; > + } > + > + ret = gpio_request_one(DA850_WLAN_IRQ, GPIOF_IN, "wl12xx_irq"); > + if (ret) { > + pr_err("Error initializing the wl12xx irq gpio: %d\n", ret); > + gpio_free(DA850_WLAN_EN); > + return; > + } > + > + da850_wl12xx_wlan_data.irq = gpio_to_irq(DA850_WLAN_IRQ); > + da850_wl12xx_wlan_data.board_ref_clock = da850_wl12xx_fref; > + > + ret = wl12xx_set_platform_data(&da850_wl12xx_wlan_data); > + if (ret) { > + pr_err("Error setting wl12xx data: %d\n", ret); > + gpio_free(DA850_WLAN_IRQ); > + gpio_free(DA850_WLAN_EN); Why not just use the traditional goto based bail out mechanism? You will avoid the multiple gpio_free() calls. Thanks, Sekhar From prakash.pm at ti.com Mon Jul 25 23:20:12 2011 From: prakash.pm at ti.com (Manjunathappa, Prakash) Date: Tue, 26 Jul 2011 09:50:12 +0530 Subject: [PATCH v3] video: da8xx-fb: Increased resolution configuration of revised LCDC IP In-Reply-To: <1310963333-29806-1-git-send-email-prakash.pm@ti.com> References: <1310963333-29806-1-git-send-email-prakash.pm@ti.com> Message-ID: Hi Paul, Could you please accept this patch as there are no comments? Thanks, Prakash On Mon, Jul 18, 2011 at 09:58:53, Manjunathappa, Prakash wrote: > Revised LCD controller in upcoming TI SoC which is an updated version of > LCDC IP that was found on TI's DA850 SoC supports 2048*2048 resolution. > Below are the encoding details: > Width: > Pixels Per Line = {pplmsb, ppllsb, 4'b1111} + 1 > Where pplmsb:1bit==>Raster Timing0[3], ppllsb:6bits==>Raster Timing0[9:4]. > And encoded value can range from 16 to 2048 in multiples of 16. > > Height: > Lines Per Panel = {lpp_b10, lpp} > Where lpp:10bits==>Raster Timing1[9:0], lpp_b10:1bit==>Raster Timing2[26]. > And encoded value can range from 1 to 2048, programmable range is 0 to > 2047. > > Patch is verified on emulation platform of upcoming SoC for updated > feature and on DA850 platform to make sure nothing existing breaks. > > Signed-off-by: Manjunathappa, Prakash > --- > Since v2: > Corrected comment describing horizontal resolution bits and removed unnecessary > outer parenthesis. > Since v1: > 1)Fixed the bug in configuration of lpp_b10 in Raster Timing2[26] register. > 2)Reframed commit message. > > drivers/video/da8xx-fb.c | 31 ++++++++++++++++++++++++++++--- > 1 files changed, 28 insertions(+), 3 deletions(-) > > diff --git a/drivers/video/da8xx-fb.c b/drivers/video/da8xx-fb.c > index 620f1c3..94b611a 100644 > --- a/drivers/video/da8xx-fb.c > +++ b/drivers/video/da8xx-fb.c > @@ -460,18 +460,43 @@ static int lcd_cfg_frame_buffer(struct da8xx_fb_par *par, u32 width, u32 height, > > /* Set the Panel Width */ > /* Pixels per line = (PPL + 1)*16 */ > - /*0x3F in bits 4..9 gives max horisontal resolution = 1024 pixels*/ > - width &= 0x3f0; > + if (lcd_revision == LCD_VERSION_1) { > + /* > + * 0x3F in bits 4..9 gives max horizontal resolution = 1024 > + * pixels. > + */ > + width &= 0x3f0; > + } else { > + /* > + * 0x7F in bits 4..10 gives max horizontal resolution = 2048 > + * pixels. > + */ > + width &= 0x7f0; > + } > + > reg = lcdc_read(LCD_RASTER_TIMING_0_REG); > reg &= 0xfffffc00; > - reg |= ((width >> 4) - 1) << 4; > + if (lcd_revision == LCD_VERSION_1) { > + reg |= ((width >> 4) - 1) << 4; > + } else { > + width = (width >> 4) - 1; > + reg |= ((width & 0x3f) << 4) | ((width & 0x40) >> 3); > + } > lcdc_write(reg, LCD_RASTER_TIMING_0_REG); > > /* Set the Panel Height */ > + /* Set bits 9:0 of Lines Per Pixel */ > reg = lcdc_read(LCD_RASTER_TIMING_1_REG); > reg = ((height - 1) & 0x3ff) | (reg & 0xfffffc00); > lcdc_write(reg, LCD_RASTER_TIMING_1_REG); > > + /* Set bit 10 of Lines Per Pixel */ > + if (lcd_revision == LCD_VERSION_2) { > + reg = lcdc_read(LCD_RASTER_TIMING_2_REG); > + reg |= ((height - 1) & 0x400) << 16; > + lcdc_write(reg, LCD_RASTER_TIMING_2_REG); > + } > + > /* Set the Raster Order of the Frame Buffer */ > reg = lcdc_read(LCD_RASTER_CTRL_REG) & ~(1 << 8); > if (raster_order) > -- > 1.7.1 > > From gary at mlbassoc.com Tue Jul 26 11:03:18 2011 From: gary at mlbassoc.com (Gary Thomas) Date: Tue, 26 Jul 2011 10:03:18 -0600 Subject: USB on OMAP-L138 Message-ID: <4E2EE546.90406@mlbassoc.com> Maybe I'm missing something, but I can't find any L138 based board which supports USB. Has this been done & I just don't see it? What's involved in getting it going (USB0 == USB2.0 HCD)? Thanks for any pointers -- ------------------------------------------------------------ Gary Thomas | Consulting for the MLB Associates | Embedded world ------------------------------------------------------------ From anibal.pinto at efacec.com Tue Jul 26 13:15:21 2011 From: anibal.pinto at efacec.com (=?ISO-8859-1?Q?An=EDbal_Almeida_Pinto?=) Date: Tue, 26 Jul 2011 19:15:21 +0100 Subject: USB on OMAP-L138 In-Reply-To: <4E2EE546.90406@mlbassoc.com> References: <4E2EE546.90406@mlbassoc.com> Message-ID: <4E2F0439.8080008@efacec.com> Em 26-07-2011 17:03, Gary Thomas escreveu: > Maybe I'm missing something, but I can't find any L138 based board > which supports USB. > > Has this been done & I just don't see it? > What's involved in getting it going (USB0 == USB2.0 HCD)? Look at [1] I have tested and it works. [1] - http://e2e.ti.com/support/embedded/f/354/p/86598/299421.aspx > > Thanks for any pointers > From nsekhar at ti.com Wed Jul 27 00:02:01 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 27 Jul 2011 10:32:01 +0530 Subject: [PATCH v4] davinci: da850 EVM: read mac address from SPI flash In-Reply-To: <1310466533-24474-1-git-send-email-sudhakar.raj@ti.com> References: <1310466533-24474-1-git-send-email-sudhakar.raj@ti.com> Message-ID: On Tue, Jul 12, 2011 at 15:58:53, Rajashekhara, Sudhakar wrote: > DA850/OMAP-L138 EMAC driver uses random mac address instead of > a fixed one because the mac address is not stuffed into EMAC > platform data. > > This patch provides a function which reads the mac address > stored in SPI flash (registered as MTD device) and populates the > EMAC platform data. The function which reads the mac address is > registered as a callback which gets called upon addition of MTD > device. > > NOTE: In case the MAC address stored in SPI flash is erased, follow > the instructions at [1] to restore it. > > [1] http://processors.wiki.ti.com/index.php/GSG:_OMAP-L138_DVEVM_Additional_Procedures#Restoring_MAC_address_on_SPI_Flash > > Modifications in v2: > Guarded registering the mtd_notifier only when MTD is enabled. > Earlier this was handled using mtd_has_partitions() call, but > this has been removed in Linux v3.0. > > Modifications in v3: > a. Guarded da850_evm_m25p80_notify_add() function and > da850evm_spi_notifier structure with CONFIG_MTD macros. > b. Renamed da850_evm_register_mtd_user() function to > da850_evm_setup_mac_addr() and removed the struct mtd_notifier > argument to this function. > c. Passed the da850evm_spi_notifier structure to register_mtd_user() > function. > > Modifications in v4: > Moved the da850_evm_setup_mac_addr() function within the first > CONFIG_MTD ifdef construct. > > Signed-off-by: Rajashekhara, Sudhakar Its impossible to get a stable IP address on Da850 EVM without this patch so will send for v3.1. Will also add a stable tag while applying. Thanks, Sekhar From ibrahima_sakho at hotmail.com Wed Jul 27 03:39:38 2011 From: ibrahima_sakho at hotmail.com (ibrahima sakho) Date: Wed, 27 Jul 2011 10:39:38 +0200 Subject: DM355-SDHC memory card write performance Message-ID: Hello, We are using a dm355 evm board and we want to write large files to a SDHC memory card. The demo board running Linux 2.6.32-rc2-davinci (argo-project) and the SD card is used as a block device (/dev/mmcblk0p1) and the host controller use DMA and 4bit mode ( davinci_mmc davinci_mmc.0: Using DMA, 4-bit mode). The mount command that we used: $ mount -t vfat /dev/mmcblk0p1 /media/mmcblk0p1 -o async examine these write performances: Kingston 2GB ----------------------------------> 2.5 MBytes/s SanDisk 2GB ----------------------------------> 4.1 MBytes/s SandDisk (SDHC class 6) 4GB -----------> 4.7 MBytes/s Transcend (SDHC class 10) 8GB --------> 4.5 MBytes/s EMTEC (SDHC class 4) 4GB --------------> 3 MBytes/s EMTEC (SDHC class 6) 16GB ------------> 5 MBytes/s All these write performances was obtained using the dd command (time dd if=/dev/zero of=/media/mmcblk0p1/test_write bs=1M count=100). Thus, we are well below the theoretical write speed of each SD card. What could explain this?? A standard SD card (SanDisk 2GB) faster than a SDHC Card Class 4 (EMTEC 4GB), or SDHC class 6 faster than SDHC class 10 ??? we do not understand this.... On the datasheet (spruee2c.pdf, page 11), the MMC/SD card controller will support the SD Physical Layer Specification V1.1, while most of the SD card that we use are the Specification V2.0 . Is there compatibility issue ?? Yet, the driver high-speed mode function (mmc_switch_hs) is called for switch the compatible SD card on this mode, but without results. Indeed, in the datasheet (spruee2c.pdf, page 13), it is mentioned that the MMC/SD controller is capable of operating with a function clock up to 100 MHz. Whereas for us, this clock is 108 MHz (DM355 with 216 MHz for ARM clock). is this a problem? In case we need to reduce the frequency of this clock, we shall have to act on the PLL modules. The question is how we can change the peripherals clock frequency without changing the ARM Subsystem clock frequency ?? We want to have write speeds higher. Someone has an idea?? We need help... Thanks and Kind regards SAKHO -------------- next part -------------- An HTML attachment was scrubbed... URL: From franciskumar2001 at gmail.com Wed Jul 27 03:55:04 2011 From: franciskumar2001 at gmail.com (francis kumar) Date: Wed, 27 Jul 2011 14:25:04 +0530 Subject: davinci: DA850: GLCD support Message-ID: Hi, The LCD i used here as Sharp - 480*272. HEIGHT is 272, Width is 480. Now i can see the image on GLCD in Landscape mode. I want to change my LCD orientation in Portriat mode permanently. I modified timing parameters in linux-xx.xx./drivers/video/da8xx-fb.c as follow. /* Sharp LK043T1DG01 */ [1] = { .name = "Sharp_LK043T1DG01", .width = 272, .height = 480, .hfp = 3, .hbp = 3, .hsw = 10, .vfp = 2, .vbp = 2, .vsw = 41, .pxl_clk = 7833600, .invert_pxl_clk = 0, }, But i am not able to see my image in Portriat orientation. please any one can suggest and solve my issues. Regards, Francis -------------- next part -------------- An HTML attachment was scrubbed... URL: From nsekhar at ti.com Wed Jul 27 04:03:22 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 27 Jul 2011 14:33:22 +0530 Subject: davinci tree updated to v3.0, patchwork updated Message-ID: Hello All, The Linux DaVinci tree[1] has been updated to kernel version 3.0 that Linus released last Friday. Also, the patchwork[1] queue has been updated. Only a few patches from Ben and one from Manju for which I am still waiting for dependencies/acks to clear are pending in the queue. If any of your patches haven't received any feedback and also don't appear in the tree, please let me know. Thanks, Sekhar [1] git://gitorious.org/linux-davinci/linux-davinci.git [2] https://patchwork.kernel.org/project/linux-davinci/list/ From smithemail at gmail.com Thu Jul 21 12:53:47 2011 From: smithemail at gmail.com (Stephen Smith) Date: Thu, 21 Jul 2011 12:53:47 -0500 Subject: Is OpenMAX supported, DL, IL layer available? Message-ID: <1d9001cc47cf$24b3b060$6e1b1120$@gmail.com> Hello all, Research didn't really turn up a definitive answer (I probably just missed it or something). Specing out possible processors for project. Does TI support OpenMAX for the DaVinci line? If so, is there an OpenMAX DL and IL Layer available and is it open source? Thanks, Stephen -------------- next part -------------- An HTML attachment was scrubbed... URL: From sshtylyov at mvista.com Wed Jul 27 04:54:47 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Wed, 27 Jul 2011 13:54:47 +0400 Subject: davinci tree updated to v3.0, patchwork updated In-Reply-To: References: Message-ID: <4E2FE067.6050901@mvista.com> Hello. On 27-07-2011 13:03, Nori, Sekhar wrote: > The Linux DaVinci tree[1] has been updated to kernel > version 3.0 that Linus released last Friday. > Also, the patchwork[1] queue has been updated. Only > a few patches from Ben and one from Manju for which > I am still waiting for dependencies/acks to clear are > pending in the queue. > If any of your patches haven't received any feedback > and also don't appear in the tree, please let me know. I don't see my patch "DaVinci: correct MDSTAT_STATE_MASK" in either patchwork or git. > Thanks, > Sekhar > [1] git://gitorious.org/linux-davinci/linux-davinci.git > [2] https://patchwork.kernel.org/project/linux-davinci/list/ WBR, Sergei From mschnell at lumino.de Wed Jul 27 05:19:44 2011 From: mschnell at lumino.de (Michael Schnell) Date: Wed, 27 Jul 2011 12:19:44 +0200 Subject: USB on OMAP-L138 In-Reply-To: <4E2F0439.8080008@efacec.com> References: <4E2EE546.90406@mlbassoc.com> <4E2F0439.8080008@efacec.com> Message-ID: <4E2FE640.2050805@lumino.de> The "SOM" Modules and dev-kits by Logic-PD have both the v1.1 as the 2.0 OTGO USB interfaces: http://www.logicpd.com/products/system-modules/texas-instruments-omap-l138-som-m1#tabs-som-2 http://www.logicpd.com/products/development-kits/zoom-omap-l138-evm-development-kit -Michael From nsekhar at ti.com Wed Jul 27 10:46:40 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 27 Jul 2011 21:16:40 +0530 Subject: davinci tree updated to v3.0, patchwork updated In-Reply-To: <4E2FE067.6050901@mvista.com> References: <4E2FE067.6050901@mvista.com> Message-ID: Hi Sergei, On Wed, Jul 27, 2011 at 15:24:47, Sergei Shtylyov wrote: > Hello. > > On 27-07-2011 13:03, Nori, Sekhar wrote: > > > The Linux DaVinci tree[1] has been updated to kernel > > version 3.0 that Linus released last Friday. > > > Also, the patchwork[1] queue has been updated. Only > > a few patches from Ben and one from Manju for which > > I am still waiting for dependencies/acks to clear are > > pending in the queue. > > > If any of your patches haven't received any feedback > > and also don't appear in the tree, please let me know. > > I don't see my patch "DaVinci: correct MDSTAT_STATE_MASK" in either > patchwork or git. I missed your patch. Patchwork missed it as well. Will review and apply. Thanks for pointing out. Regards, Sekhar From nsekhar at ti.com Wed Jul 27 11:18:45 2011 From: nsekhar at ti.com (Nori, Sekhar) Date: Wed, 27 Jul 2011 21:48:45 +0530 Subject: [PATCH] DaVinci: correct MDSTAT_STATE_MASK In-Reply-To: <201107081924.57577.sshtylyov@ru.mvista.com> References: <201107081924.57577.sshtylyov@ru.mvista.com> Message-ID: On Fri, Jul 08, 2011 at 20:54:57, Sergei Shtylyov wrote: > MDSTAT.STATE occupies bits 0..5 according to all available documentation, so fix > the #define MDSTAT_STATE_MASK at last. Using the wrong value seems to have been > harmless though... > > This was noticed by me back in 2009 but I didn't follow up with the patch back > then... :-/ > > Signed-off-by: Sergei Shtylyov I applied this with two changes. Changed the headline to "ARM: davinci: correct MDSTAT_STATE_MASK" to get the standardization requested by Arnd. Also dropped the second paragraph of the commit text since it doesn't really belong to the permanent history ;) Thanks, Sekhar From const at MakeLinux.com Thu Jul 28 03:21:07 2011 From: const at MakeLinux.com (const at MakeLinux.com) Date: Thu, 28 Jul 2011 11:21:07 +0300 Subject: [PATCH] Enable USB on dm365 Message-ID: <1311841267-31932-1-git-send-email-const@MakeLinux.com> From: Constantine Shulyupin Signed-off-by: Constantine Shulyupin --- arch/arm/mach-davinci/board-dm365-evm.c | 2 ++ drivers/usb/musb/davinci.c | 6 ++++++ drivers/usb/musb/davinci.h | 1 + 3 files changed, 9 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index 8710614..c62ca53 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -39,6 +39,7 @@ #include #include #include +#include #include @@ -612,6 +613,7 @@ static __init void dm365_evm_init(void) dm365_init_spi0(BIT(0), dm365_evm_spi_info, ARRAY_SIZE(dm365_evm_spi_info)); + davinci_setup_usb(500, 8); } MACHINE_START(DAVINCI_DM365_EVM, "DaVinci DM365 EVM") diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c index 2a2adf6..905a107 100644 --- a/drivers/usb/musb/davinci.c +++ b/drivers/usb/musb/davinci.c @@ -72,6 +72,11 @@ static inline void phy_on(void) /* power everything up; start the on-chip PHY and its PLL */ phy_ctrl &= ~(USBPHY_OSCPDWN | USBPHY_OTGPDWN | USBPHY_PHYPDWN); phy_ctrl |= USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON; + + if (cpu_is_davinci_dm365()) { + phy_ctrl |= USBPHY_CLKFREQ_24MHZ; + } + __raw_writel(phy_ctrl, USB_PHY_CTRL); /* wait for PLL to lock before proceeding */ diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h index 046c844..1bf50e6 100644 --- a/drivers/usb/musb/davinci.h +++ b/drivers/usb/musb/davinci.h @@ -17,6 +17,7 @@ /* Integrated highspeed/otg PHY */ #define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) #define USBPHY_DATAPOL BIT(11) /* (dm355) switch D+/D- */ +#define USBPHY_CLKFREQ_24MHZ BIT(13) #define USBPHY_PHYCLKGD BIT(8) #define USBPHY_SESNDEN BIT(7) /* v(sess_end) comparator */ #define USBPHY_VBDTCTEN BIT(6) /* v(bus) comparator */ -- 1.7.0.4 From ido at wizery.com Thu Jul 28 13:34:54 2011 From: ido at wizery.com (Ido Yariv) Date: Thu, 28 Jul 2011 21:34:54 +0300 Subject: [PATCH v2 6/6] arm: davinci: DA850: Add wl1271/wlan support In-Reply-To: References: <1310303679-17936-1-git-send-email-ido@wizery.com> <1310303679-17936-7-git-send-email-ido@wizery.com> Message-ID: <20110728183454.GE1985@WorkStation> Hi Sekhar, On Mon, Jul 25, 2011 at 11:10:55PM +0530, Nori, Sekhar wrote: > Adding a new kernel parameter requires update to > Documentation/kernel-parameters.txt as well. > > I am Ccing a couple of folks in case they have ideas on > whether there is a better way to pass this information > to the kernel. I assume there is no way to detect > this from hardware. Unfortunately, auto-detection of the reference clock is not currently possible. However, it might be a better idea to have the ability to override this value with a wl12xx module parameter instead of a kernel parameter. I'll drop this kernel parameter. > > +static struct davinci_mmc_config da850_mmc_wl12xx_config = { > > + .get_ro = NULL, > > + .get_cd = NULL, > > You can get rid of these NULL initializers. Sure. > > + .set_power = wl12xx_set_power, > > + .wires = 4, > > + .max_freq = 25000000, > > + .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NONREMOVABLE | > > + MMC_CAP_POWER_OFF_CARD, > > + .version = MMC_CTLR_VERSION_2, > > +}; [...] > > + ret = gpio_request_one(DA850_WLAN_EN, GPIOF_OUT_INIT_LOW, "wl12xx_en"); > > + if (ret) { > > + pr_err("Error initializing the wl12xx enable gpio: %d\n", ret); > > + return; > > + } > > + > > + ret = gpio_request_one(DA850_WLAN_IRQ, GPIOF_IN, "wl12xx_irq"); > > + if (ret) { > > + pr_err("Error initializing the wl12xx irq gpio: %d\n", ret); > > + gpio_free(DA850_WLAN_EN); > > + return; > > + } > > + > > + da850_wl12xx_wlan_data.irq = gpio_to_irq(DA850_WLAN_IRQ); > > + da850_wl12xx_wlan_data.board_ref_clock = da850_wl12xx_fref; > > + > > + ret = wl12xx_set_platform_data(&da850_wl12xx_wlan_data); > > + if (ret) { > > + pr_err("Error setting wl12xx data: %d\n", ret); > > + gpio_free(DA850_WLAN_IRQ); > > + gpio_free(DA850_WLAN_EN); > > Why not just use the traditional goto based bail out > mechanism? You will avoid the multiple gpio_free() calls. Sure. Thanks for your review, Ido. From ido at wizery.com Thu Jul 28 15:49:09 2011 From: ido at wizery.com (Ido Yariv) Date: Thu, 28 Jul 2011 23:49:09 +0300 Subject: [PATCH REPOST v2] mmc: davinci: Add support for set_power callback In-Reply-To: References: Message-ID: <1311886149-3009-1-git-send-email-ido@wizery.com> Some devices connected to the MMC bus are power controlled by external means. For instance, an SDIO device may be powered down/up by an external gpio line. In order to avoid toggling power from within the MMC host driver, add a set_power callback function, which will be called by set_ios upon powering down/up. Signed-off-by: Ido Yariv CC: Chris Ball --- arch/arm/mach-davinci/include/mach/mmc.h | 3 +++ drivers/mmc/host/davinci_mmc.c | 13 +++++++++++++ 2 files changed, 16 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/mmc.h b/arch/arm/mach-davinci/include/mach/mmc.h index d4f1e96..5ba6b22 100644 --- a/arch/arm/mach-davinci/include/mach/mmc.h +++ b/arch/arm/mach-davinci/include/mach/mmc.h @@ -12,6 +12,9 @@ struct davinci_mmc_config { /* get_cd()/get_wp() may sleep */ int (*get_cd)(int module); int (*get_ro)(int module); + + void (*set_power)(int module, bool on); + /* wires == 0 is equivalent to wires == 4 (4-bit parallel) */ u8 wires; diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c index 0076c74..64a8325 100644 --- a/drivers/mmc/host/davinci_mmc.c +++ b/drivers/mmc/host/davinci_mmc.c @@ -807,12 +807,25 @@ static void calculate_clk_divider(struct mmc_host *mmc, struct mmc_ios *ios) static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) { struct mmc_davinci_host *host = mmc_priv(mmc); + struct platform_device *pdev = to_platform_device(mmc->parent); + struct davinci_mmc_config *config = pdev->dev.platform_data; dev_dbg(mmc_dev(host->mmc), "clock %dHz busmode %d powermode %d Vdd %04x\n", ios->clock, ios->bus_mode, ios->power_mode, ios->vdd); + switch (ios->power_mode) { + case MMC_POWER_OFF: + if (config && config->set_power) + config->set_power(pdev->id, false); + break; + case MMC_POWER_UP: + if (config && config->set_power) + config->set_power(pdev->id, true); + break; + } + switch (ios->bus_width) { case MMC_BUS_WIDTH_8: dev_dbg(mmc_dev(host->mmc), "Enabling 8 bit mode\n"); -- 1.7.4.1 From ido at wizery.com Thu Jul 28 15:52:53 2011 From: ido at wizery.com (Ido Yariv) Date: Thu, 28 Jul 2011 23:52:53 +0300 Subject: [PATCH v3] davinci: Add wl1271/wlan support for AM18x In-Reply-To: References: Message-ID: <1311886373-3060-1-git-send-email-ido@wizery.com> The wl1271 daughter card for AM18x EVMs is a combo wireless connectivity add-on card, based on the LS Research TiWi module with Texas Instruments' wl1271 solution. It is a 4-wire, 1.8V, embedded SDIO WLAN device with an external IRQ line and is power-controlled by a GPIO-based fixed regulator. Add support for the WLAN capabilities of this expansion board. Signed-off-by: Ido Yariv --- arch/arm/mach-davinci/Kconfig | 10 +++ arch/arm/mach-davinci/board-da850-evm.c | 114 +++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 0 deletions(-) diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig index c0deaca..32d837d 100644 --- a/arch/arm/mach-davinci/Kconfig +++ b/arch/arm/mach-davinci/Kconfig @@ -192,6 +192,16 @@ config DA850_UI_RMII endchoice +config DA850_WL12XX + bool "AM18x wl1271 daughter board" + depends on MACH_DAVINCI_DA850_EVM + help + The wl1271 daughter card for AM18x EVMs is a combo wireless + connectivity add-on card, based on the LS Research TiWi module with + Texas Instruments' wl1271 solution. + Say Y if you want to use a wl1271 expansion card connected to the + AM18x EVM. + config GPIO_PCA953X default MACH_DAVINCI_DA850_EVM diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index bd53945..d817626 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -31,6 +31,8 @@ #include #include #include +#include +#include #include #include @@ -49,6 +51,9 @@ #define DA850_MMCSD_CD_PIN GPIO_TO_PIN(4, 0) #define DA850_MMCSD_WP_PIN GPIO_TO_PIN(4, 1) +#define DA850_WLAN_EN GPIO_TO_PIN(6, 9) +#define DA850_WLAN_IRQ GPIO_TO_PIN(6, 10) + #define DA850_MII_MDIO_CLKEN_PIN GPIO_TO_PIN(2, 6) static struct mtd_partition da850evm_spiflash_part[] = { @@ -1117,6 +1122,110 @@ static __init int da850_evm_init_cpufreq(void) static __init int da850_evm_init_cpufreq(void) { return 0; } #endif +#ifdef CONFIG_DA850_WL12XX + +static void wl12xx_set_power(int index, bool power_on) +{ + static bool power_state; + + pr_debug("Powering %s wl12xx", power_on ? "on" : "off"); + + if (power_on == power_state) + return; + power_state = power_on; + + if (power_on) { + /* Power up sequence required for wl127x devices */ + gpio_set_value(DA850_WLAN_EN, 1); + usleep_range(15000, 15000); + gpio_set_value(DA850_WLAN_EN, 0); + usleep_range(1000, 1000); + gpio_set_value(DA850_WLAN_EN, 1); + msleep(70); + } else { + gpio_set_value(DA850_WLAN_EN, 0); + } +} + +static struct davinci_mmc_config da850_wl12xx_mmc_config = { + .set_power = wl12xx_set_power, + .wires = 4, + .max_freq = 25000000, + .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NONREMOVABLE | + MMC_CAP_POWER_OFF_CARD, + .version = MMC_CTLR_VERSION_2, +}; + +static const short da850_wl12xx_pins[] __initconst = { + DA850_MMCSD1_DAT_0, DA850_MMCSD1_DAT_1, DA850_MMCSD1_DAT_2, + DA850_MMCSD1_DAT_3, DA850_MMCSD1_CLK, DA850_MMCSD1_CMD, + DA850_GPIO6_9, DA850_GPIO6_10, + -1 +}; + +static struct wl12xx_platform_data da850_wl12xx_wlan_data __initdata = { + .irq = -1, + .board_ref_clock = WL12XX_REFCLOCK_38, + .platform_quirks = WL12XX_PLATFORM_QUIRK_EDGE_IRQ, +}; + +static __init int da850_wl12xx_init(void) +{ + int ret; + + ret = davinci_cfg_reg_list(da850_wl12xx_pins); + if (ret) { + pr_err("wl12xx/mmc mux setup failed: %d\n", ret); + goto exit; + } + + ret = da850_register_mmcsd1(&da850_wl12xx_mmc_config); + if (ret) { + pr_err("wl12xx/mmc registration failed: %d\n", ret); + goto exit; + } + + ret = gpio_request_one(DA850_WLAN_EN, GPIOF_OUT_INIT_LOW, "wl12xx_en"); + if (ret) { + pr_err("Could not request wl12xx enable gpio: %d\n", ret); + goto exit; + } + + ret = gpio_request_one(DA850_WLAN_IRQ, GPIOF_IN, "wl12xx_irq"); + if (ret) { + pr_err("Could not request wl12xx irq gpio: %d\n", ret); + goto free_wlan_en; + } + + da850_wl12xx_wlan_data.irq = gpio_to_irq(DA850_WLAN_IRQ); + + ret = wl12xx_set_platform_data(&da850_wl12xx_wlan_data); + if (ret) { + pr_err("Could not set wl12xx data: %d\n", ret); + goto free_wlan_irq; + } + + return 0; + +free_wlan_irq: + gpio_free(DA850_WLAN_IRQ); + +free_wlan_en: + gpio_free(DA850_WLAN_EN); + +exit: + return ret; +} + +#else /* CONFIG_DA850_WL12XX */ + +static __init int da850_wl12xx_init(void) +{ + return 0; +} + +#endif /* CONFIG_DA850_WL12XX */ + #define DA850EVM_SATA_REFCLKPN_RATE (100 * 1000 * 1000) static __init void da850_evm_init(void) @@ -1171,6 +1280,11 @@ static __init void da850_evm_init(void) if (ret) pr_warning("da850_evm_init: mmcsd0 registration failed:" " %d\n", ret); + + ret = da850_wl12xx_init(); + if (ret) + pr_warning("da850_evm_init: wl12xx initialization" + " failed: %d\n", ret); } davinci_serial_init(&da850_evm_uart_config); -- 1.7.4.1 From troy.kisky at boundarydevices.com Thu Jul 28 16:15:41 2011 From: troy.kisky at boundarydevices.com (Troy Kisky) Date: Thu, 28 Jul 2011 14:15:41 -0700 Subject: [PATCH v3] davinci: Add wl1271/wlan support for AM18x In-Reply-To: <1311886373-3060-1-git-send-email-ido@wizery.com> References: <1311886373-3060-1-git-send-email-ido@wizery.com> Message-ID: <4E31D17D.6050000@boundarydevices.com> On 7/28/2011 1:52 PM, Ido Yariv wrote: > The wl1271 daughter card for AM18x EVMs is a combo wireless connectivity > add-on card, based on the LS Research TiWi module with Texas > Instruments' wl1271 solution. > It is a 4-wire, 1.8V, embedded SDIO WLAN device with an external IRQ > line and is power-controlled by a GPIO-based fixed regulator. > > Add support for the WLAN capabilities of this expansion board. > > Signed-off-by: Ido Yariv > --- > arch/arm/mach-davinci/Kconfig | 10 +++ > arch/arm/mach-davinci/board-da850-evm.c | 114 +++++++++++++++++++++++++++++++ > 2 files changed, 124 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-davinci/Kconfig b/arch/arm/mach-davinci/Kconfig > index c0deaca..32d837d 100644 > --- a/arch/arm/mach-davinci/Kconfig > +++ b/arch/arm/mach-davinci/Kconfig > @@ -192,6 +192,16 @@ config DA850_UI_RMII > > endchoice > > +config DA850_WL12XX > + bool "AM18x wl1271 daughter board" > + depends on MACH_DAVINCI_DA850_EVM > + help > + The wl1271 daughter card for AM18x EVMs is a combo wireless > + connectivity add-on card, based on the LS Research TiWi module with > + Texas Instruments' wl1271 solution. > + Say Y if you want to use a wl1271 expansion card connected to the > + AM18x EVM. > + > config GPIO_PCA953X > default MACH_DAVINCI_DA850_EVM > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index bd53945..d817626 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -31,6 +31,8 @@ > #include > #include > #include > +#include > +#include > > #include > #include > @@ -49,6 +51,9 @@ > #define DA850_MMCSD_CD_PIN GPIO_TO_PIN(4, 0) > #define DA850_MMCSD_WP_PIN GPIO_TO_PIN(4, 1) > > +#define DA850_WLAN_EN GPIO_TO_PIN(6, 9) > +#define DA850_WLAN_IRQ GPIO_TO_PIN(6, 10) > + > #define DA850_MII_MDIO_CLKEN_PIN GPIO_TO_PIN(2, 6) > > static struct mtd_partition da850evm_spiflash_part[] = { > @@ -1117,6 +1122,110 @@ static __init int da850_evm_init_cpufreq(void) > static __init int da850_evm_init_cpufreq(void) { return 0; } > #endif > > +#ifdef CONFIG_DA850_WL12XX > + > +static void wl12xx_set_power(int index, bool power_on) > +{ > + static bool power_state; > + > + pr_debug("Powering %s wl12xx", power_on ? "on" : "off"); > + > + if (power_on == power_state) > + return; > + power_state = power_on; > + > + if (power_on) { > + /* Power up sequence required for wl127x devices */ > + gpio_set_value(DA850_WLAN_EN, 1); > + usleep_range(15000, 15000); > + gpio_set_value(DA850_WLAN_EN, 0); > + usleep_range(1000, 1000); > + gpio_set_value(DA850_WLAN_EN, 1); > + msleep(70); Why turn on, then off, and then back on? Isn't off, then back on sufficient? Also, why not use regulator API like panda board does? >From board-omap4panda.c, we have _________________________________________ static struct regulator_consumer_supply omap4_panda_vmmc5_supply = { .supply = "vmmc", .dev_name = "omap_hsmmc.4", }; static struct regulator_init_data panda_vmmc5 = { .constraints = { .valid_ops_mask = REGULATOR_CHANGE_STATUS, }, .num_consumer_supplies = 1, .consumer_supplies = &omap4_panda_vmmc5_supply, }; static struct fixed_voltage_config panda_vwlan = { .supply_name = "vwl1271", .microvolts = 1800000, /* 1.8V */ .gpio = GPIO_WIFI_PMENA, .startup_delay = 70000, /* 70msec */ .enable_high = 1, .enabled_at_boot = 0, .init_data = &panda_vmmc5, }; static struct platform_device omap_vwlan_device = { .name = "reg-fixed-voltage", .id = 1, .dev = { .platform_data = &panda_vwlan, }, }; _________________________________________ If this can't work for you, can you list why in the change log. If your mmc controller doesn't yet support vmmc control. It should be very easy to add it first. Thanks. > + } else { > + gpio_set_value(DA850_WLAN_EN, 0); > + } > +} > + > +static struct davinci_mmc_config da850_wl12xx_mmc_config = { > + .set_power = wl12xx_set_power, > + .wires = 4, > + .max_freq = 25000000, > + .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_NONREMOVABLE | > + MMC_CAP_POWER_OFF_CARD, > + .version = MMC_CTLR_VERSION_2, > +}; > + > +static const short da850_wl12xx_pins[] __initconst = { > + DA850_MMCSD1_DAT_0, DA850_MMCSD1_DAT_1, DA850_MMCSD1_DAT_2, > + DA850_MMCSD1_DAT_3, DA850_MMCSD1_CLK, DA850_MMCSD1_CMD, > + DA850_GPIO6_9, DA850_GPIO6_10, > + -1 > +}; > + > +static struct wl12xx_platform_data da850_wl12xx_wlan_data __initdata = { > + .irq = -1, > + .board_ref_clock = WL12XX_REFCLOCK_38, > + .platform_quirks = WL12XX_PLATFORM_QUIRK_EDGE_IRQ, > +}; > + > +static __init int da850_wl12xx_init(void) > +{ > + int ret; > + > + ret = davinci_cfg_reg_list(da850_wl12xx_pins); > + if (ret) { > + pr_err("wl12xx/mmc mux setup failed: %d\n", ret); > + goto exit; > + } > + > + ret = da850_register_mmcsd1(&da850_wl12xx_mmc_config); > + if (ret) { > + pr_err("wl12xx/mmc registration failed: %d\n", ret); > + goto exit; > + } > + > + ret = gpio_request_one(DA850_WLAN_EN, GPIOF_OUT_INIT_LOW, "wl12xx_en"); > + if (ret) { > + pr_err("Could not request wl12xx enable gpio: %d\n", ret); > + goto exit; > + } > + > + ret = gpio_request_one(DA850_WLAN_IRQ, GPIOF_IN, "wl12xx_irq"); > + if (ret) { > + pr_err("Could not request wl12xx irq gpio: %d\n", ret); > + goto free_wlan_en; > + } > + > + da850_wl12xx_wlan_data.irq = gpio_to_irq(DA850_WLAN_IRQ); > + > + ret = wl12xx_set_platform_data(&da850_wl12xx_wlan_data); > + if (ret) { > + pr_err("Could not set wl12xx data: %d\n", ret); > + goto free_wlan_irq; > + } > + > + return 0; > + > +free_wlan_irq: > + gpio_free(DA850_WLAN_IRQ); > + > +free_wlan_en: > + gpio_free(DA850_WLAN_EN); > + > +exit: > + return ret; > +} > + > +#else /* CONFIG_DA850_WL12XX */ > + > +static __init int da850_wl12xx_init(void) > +{ > + return 0; > +} > + > +#endif /* CONFIG_DA850_WL12XX */ > + > #define DA850EVM_SATA_REFCLKPN_RATE (100 * 1000 * 1000) > > static __init void da850_evm_init(void) > @@ -1171,6 +1280,11 @@ static __init void da850_evm_init(void) > if (ret) > pr_warning("da850_evm_init: mmcsd0 registration failed:" > " %d\n", ret); > + > + ret = da850_wl12xx_init(); > + if (ret) > + pr_warning("da850_evm_init: wl12xx initialization" > + " failed: %d\n", ret); > } > > davinci_serial_init(&da850_evm_uart_config); From ido at wizery.com Thu Jul 28 17:06:43 2011 From: ido at wizery.com (Ido Yariv) Date: Fri, 29 Jul 2011 01:06:43 +0300 Subject: [PATCH v3] davinci: Add wl1271/wlan support for AM18x In-Reply-To: <4E31D17D.6050000@boundarydevices.com> References: <1311886373-3060-1-git-send-email-ido@wizery.com> <4E31D17D.6050000@boundarydevices.com> Message-ID: <20110728220643.GG1985@WorkStation> Hi Troy, On Thu, Jul 28, 2011 at 02:15:41PM -0700, Troy Kisky wrote: > > + if (power_on) { > > + /* Power up sequence required for wl127x devices */ > > + gpio_set_value(DA850_WLAN_EN, 1); > > + usleep_range(15000, 15000); > > + gpio_set_value(DA850_WLAN_EN, 0); > > + usleep_range(1000, 1000); > > + gpio_set_value(DA850_WLAN_EN, 1); > > + msleep(70); > > Why turn on, then off, and then back on? > Isn't off, then back on sufficient? Unfortunately, no. This is a required power up sequence for some hardware revisions of the 1271 chip. > Also, why not use regulator API like panda board does? Unlike omap_hsmmc, davinci's MMC host driver does not toggle any regulators. To keep things simple, a set_power callback function was added to the mmc host as part of this patch series. I've considered adding regulator support instead, but found it to be a bit over-complicated for this task. In addition, it would require either modifying the fixed regulator or adding a new one in order to support the above power sequence. Thanks for your review, Ido. From brijesh.j at ti.com Fri Jul 29 02:57:29 2011 From: brijesh.j at ti.com (Jadav, Brijesh R) Date: Fri, 29 Jul 2011 13:27:29 +0530 Subject: davinci: DA850: GLCD support In-Reply-To: References: Message-ID: Hi Francis, Basically, you are trying to rotate image by 90. Can you check you LCD to see if it supports these kind of rotated timing parameters? If it does not, you will have to take care of rotating input image in the application. Thanks, Brijesh Jadav ________________________________ From: davinci-linux-open-source-bounces+brijesh.j=ti.com at linux.davincidsp.com [davinci-linux-open-source-bounces+brijesh.j=ti.com at linux.davincidsp.com] On Behalf Of francis kumar [franciskumar2001 at gmail.com] Sent: Wednesday, July 27, 2011 3:55 AM To: davinci-linux-open-source at linux.davincidsp.com Subject: davinci: DA850: GLCD support Hi, The LCD i used here as Sharp - 480*272. HEIGHT is 272, Width is 480. Now i can see the image on GLCD in Landscape mode. I want to change my LCD orientation in Portriat mode permanently. I modified timing parameters in linux-xx.xx./drivers/video/da8xx-fb.c as follow. /* Sharp LK043T1DG01 */ [1] = { .name = "Sharp_LK043T1DG01", .width = 272, .height = 480, .hfp = 3, .hbp = 3, .hsw = 10, .vfp = 2, .vbp = 2, .vsw = 41, .pxl_clk = 7833600, .invert_pxl_clk = 0, }, But i am not able to see my image in Portriat orientation. please any one can suggest and solve my issues. Regards, Francis -------------- next part -------------- An HTML attachment was scrubbed... URL: From brijesh.j at ti.com Fri Jul 29 03:18:13 2011 From: brijesh.j at ti.com (Jadav, Brijesh R) Date: Fri, 29 Jul 2011 13:48:13 +0530 Subject: davinci: DA850: GLCD support In-Reply-To: References: , Message-ID: Hi francis, This is actually console rotation, Linux console driver takes care of rotating you console. This does not rotate your normal frame buffer. Actually the underlying fb driver is totally ignorant of console rotation. I think there are some boot time parameters, you can use them to rotate your console. See if below text helps. 4. fbcon=rotate: This option changes the orientation angle of the console display. The value 'n' accepts the following: 0 - normal orientation (0 degree) 1 - clockwise orientation (90 degrees) 2 - upside down orientation (180 degrees) 3 - counterclockwise orientation (270 degrees) The angle can be changed anytime afterwards by 'echoing' the same numbers to any one of the 2 attributes found in /sys/class/graphics/fbcon rotate - rotate the display of the active console rotate_all - rotate the display of all consoles Console rotation will only become available if Console Rotation Support is compiled in your kernel. Thanks, Brijesh Jadav ________________________________ From: francis kumar [franciskumar2001 at gmail.com] Sent: Friday, July 29, 2011 3:13 AM To: Jadav, Brijesh R Subject: Re: davinci: DA850: GLCD support Hi Brijesh, Thanks for your mail. Basically my default GLCD settings are in Landscape(Display 480(W)x272(H)). I am able to rotate the image in portrait orientation(270) using "echo 3 > /sys/class/graphics/fbcon/rotate_all" command. Once it restart my target again it goes to default mode. We are developing our own customized Qt application. I want to viewport size as always in portrait mode. Any driver changes or something we can modify it in scripts. Please suggest me. Thanks, Francis On Fri, Jul 29, 2011 at 1:27 PM, Jadav, Brijesh R > wrote: Hi Francis, Basically, you are trying to rotate image by 90. Can you check you LCD to see if it supports these kind of rotated timing parameters? If it does not, you will have to take care of rotating input image in the application. Thanks, Brijesh Jadav ________________________________ From: davinci-linux-open-source-bounces+brijesh.j=ti.com@linux.davincidsp.com [davinci-linux-open-source-bounces+brijesh.j=ti.com@linux.davincidsp.com] On Behalf Of francis kumar [franciskumar2001 at gmail.com] Sent: Wednesday, July 27, 2011 3:55 AM To: davinci-linux-open-source at linux.davincidsp.com Subject: davinci: DA850: GLCD support Hi, The LCD i used here as Sharp - 480*272. HEIGHT is 272, Width is 480. Now i can see the image on GLCD in Landscape mode. I want to change my LCD orientation in Portriat mode permanently. I modified timing parameters in linux-xx.xx./drivers/video/da8xx-fb.c as follow. /* Sharp LK043T1DG01 */ [1] = { .name = "Sharp_LK043T1DG01", .width = 272, .height = 480, .hfp = 3, .hbp = 3, .hsw = 10, .vfp = 2, .vbp = 2, .vsw = 41, .pxl_clk = 7833600, .invert_pxl_clk = 0, }, But i am not able to see my image in Portriat orientation. please any one can suggest and solve my issues. Regards, Francis -------------- next part -------------- An HTML attachment was scrubbed... URL: From sshtylyov at mvista.com Fri Jul 29 05:35:40 2011 From: sshtylyov at mvista.com (Sergei Shtylyov) Date: Fri, 29 Jul 2011 14:35:40 +0400 Subject: [PATCH] Enable USB on dm365 In-Reply-To: <1311841267-31932-1-git-send-email-const@MakeLinux.com> References: <1311841267-31932-1-git-send-email-const@MakeLinux.com> Message-ID: <4E328CFC.8050105@mvista.com> Hello. On 28-07-2011 12:21, const at MakeLinux.com wrote: > From: Constantine Shulyupin Please in the future add "v2" (or whatever version it's gonna be) after "PATCH" in the subject, and describe the changes that you've done below your signoff and the --- tear line. > Signed-off-by: Constantine Shulyupin > diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c > index 8710614..c62ca53 100644 > --- a/arch/arm/mach-davinci/board-dm365-evm.c > +++ b/arch/arm/mach-davinci/board-dm365-evm.c > @@ -39,6 +39,7 @@ > #include > #include > #include > +#include > > #include > > @@ -612,6 +613,7 @@ static __init void dm365_evm_init(void) > > dm365_init_spi0(BIT(0), dm365_evm_spi_info, > ARRAY_SIZE(dm365_evm_spi_info)); > + davinci_setup_usb(500, 8); > } > > MACHINE_START(DAVINCI_DM365_EVM, "DaVinci DM365 EVM") The patch needs to be split here (and the second part pushed thru the 'linux-usb' mailing list. I also wonder whether we should setup PinMux to activate VBUS, once we dropped the GPIO manipulation. > diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c > index 2a2adf6..905a107 100644 > --- a/drivers/usb/musb/davinci.c > +++ b/drivers/usb/musb/davinci.c > @@ -72,6 +72,11 @@ static inline void phy_on(void) > /* power everything up; start the on-chip PHY and its PLL */ > phy_ctrl&= ~(USBPHY_OSCPDWN | USBPHY_OTGPDWN | USBPHY_PHYPDWN); > phy_ctrl |= USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON; > + > + if (cpu_is_davinci_dm365()) { > + phy_ctrl |= USBPHY_CLKFREQ_24MHZ; > + } I'm still thinking this should belong to the board code. That, and {} are not needed. > + > __raw_writel(phy_ctrl, USB_PHY_CTRL); > > /* wait for PLL to lock before proceeding */ > diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h > index 046c844..1bf50e6 100644 > --- a/drivers/usb/musb/davinci.h > +++ b/drivers/usb/musb/davinci.h > @@ -17,6 +17,7 @@ > /* Integrated highspeed/otg PHY */ > #define USBPHY_CTL_PADDR (DAVINCI_SYSTEM_MODULE_BASE + 0x34) > #define USBPHY_DATAPOL BIT(11) /* (dm355) switch D+/D- */ > +#define USBPHY_CLKFREQ_24MHZ BIT(13) Please indent with tabs, not spaces. WBR, Sergei From const at makelinux.com Fri Jul 29 08:50:53 2011 From: const at makelinux.com (Constantine Shulyupin) Date: Fri, 29 Jul 2011 16:50:53 +0300 Subject: [PATCH] Enable USB on dm365 In-Reply-To: <4E328CFC.8050105@mvista.com> References: <1311841267-31932-1-git-send-email-const@MakeLinux.com> <4E328CFC.8050105@mvista.com> Message-ID: Thank you for your feedback. There is no need to switch VBUS via GPIO. May be it is already done in u-boot. On Fri, Jul 29, 2011 at 1:35 PM, Sergei Shtylyov wrote: > Hello. > > On 28-07-2011 12:21, const at MakeLinux.com wrote: > >> From: Constantine Shulyupin > > ? Please in the future add "v2" (or whatever version it's gonna be) after > "PATCH" in the subject, and describe the changes that you've done below your > signoff and the --- tear line. > >> Signed-off-by: Constantine Shulyupin > >> diff --git a/arch/arm/mach-davinci/board-dm365-evm.c >> b/arch/arm/mach-davinci/board-dm365-evm.c >> index 8710614..c62ca53 100644 >> --- a/arch/arm/mach-davinci/board-dm365-evm.c >> +++ b/arch/arm/mach-davinci/board-dm365-evm.c >> @@ -39,6 +39,7 @@ >> ?#include >> ?#include >> ?#include >> +#include >> >> ?#include >> >> @@ -612,6 +613,7 @@ static __init void dm365_evm_init(void) >> >> ? ? ? ?dm365_init_spi0(BIT(0), dm365_evm_spi_info, >> ? ? ? ? ? ? ? ? ? ? ? ?ARRAY_SIZE(dm365_evm_spi_info)); >> + ? ? ? davinci_setup_usb(500, 8); >> ?} >> >> ?MACHINE_START(DAVINCI_DM365_EVM, "DaVinci DM365 EVM") > > ? The patch needs to be split here (and the second part pushed thru the > 'linux-usb' mailing list. > ? I also wonder whether we should setup PinMux to activate VBUS, once we > dropped the GPIO manipulation. > >> diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c >> index 2a2adf6..905a107 100644 >> --- a/drivers/usb/musb/davinci.c >> +++ b/drivers/usb/musb/davinci.c >> @@ -72,6 +72,11 @@ static inline void phy_on(void) >> ? ? ? ?/* power everything up; start the on-chip PHY and its PLL */ >> ? ? ? ?phy_ctrl&= ~(USBPHY_OSCPDWN | USBPHY_OTGPDWN | USBPHY_PHYPDWN); >> ? ? ? ?phy_ctrl |= USBPHY_SESNDEN | USBPHY_VBDTCTEN | USBPHY_PHYPLLON; >> + >> + ? ? ? if (cpu_is_davinci_dm365()) { >> + ? ? ? ? ? ? ? phy_ctrl |= USBPHY_CLKFREQ_24MHZ; >> + ? ? ? } > > ? I'm still thinking this should belong to the board code. That, and {} are > not needed. > >> + >> ? ? ? ?__raw_writel(phy_ctrl, USB_PHY_CTRL); >> >> ? ? ? ?/* wait for PLL to lock before proceeding */ >> diff --git a/drivers/usb/musb/davinci.h b/drivers/usb/musb/davinci.h >> index 046c844..1bf50e6 100644 >> --- a/drivers/usb/musb/davinci.h >> +++ b/drivers/usb/musb/davinci.h >> @@ -17,6 +17,7 @@ >> ?/* Integrated highspeed/otg PHY */ >> ?#define USBPHY_CTL_PADDR ? ? ?(DAVINCI_SYSTEM_MODULE_BASE + 0x34) >> ?#define USBPHY_DATAPOL ? ? ? ? ? ? ? ?BIT(11) /* (dm355) switch D+/D- */ >> +#define USBPHY_CLKFREQ_24MHZ ? ?BIT(13) > > ? Please indent with tabs, not spaces. > > WBR, Sergei > -- Constantine Shulyupin http://www.MakeLinux.com/ Embedded Linux Systems, Device Drivers, TI DaVinci From bengardiner at nanometrics.ca Fri Jul 29 09:42:36 2011 From: bengardiner at nanometrics.ca (Ben Gardiner) Date: Fri, 29 Jul 2011 10:42:36 -0400 Subject: [PATCH v2 2/6] arm: davinci: Explicitly set channel controllers' default queues In-Reply-To: References: <1310303679-17936-1-git-send-email-ido@wizery.com> <1310303679-17936-3-git-send-email-ido@wizery.com> <4E2D70B9.7060202@mvista.com> Message-ID: On Mon, Jul 25, 2011 at 12:31 PM, Nori, Sekhar wrote: > Hi Sergei, > > On Mon, Jul 25, 2011 at 19:03:45, Sergei Shtylyov wrote: >> Hello. >> >> Nori, Sekhar wrote: >> >> >> Davinci platforms may define a default queue for each channel >> >> controller. If one is not defined, the default queue is set to EVENTQ_1. >> >> However, there's no way to distinguish between an unset default queue to >> >> one that is set to EVENTQ_0, as EVENTQ_0 = 0. >> >> >> Explicitly specify the default queue for all channel controllers on all >> >> Davinci platforms to EVENTQ_1, and don't overwrite it in the EDMA probe >> >> function. >> >> >> One exception is the DA850 board, for which EVENTQ_1 is not a valid >> >> option for its second channel controller. Use EVENTQ_0 instead for that >> >> channel controller. >> >> >> Signed-off-by: Ido Yariv >> >> > Looks good to me. Will queue for v3.2/fixes >> >> ? ? Why wait for 3.2? If this is considered a fix, it should be applied to 3.1, no? > > 3.2/fixes just indicates it will be queued as a fix/cleanup > for 3.2 so it will have higher priority for merge when compared > to a new feature. > > This patch doesn't really fix any existing broken functionality. > It corrects event queue configuration for EDMA CC1 on DA850 for > which there are no current users in mainline. > > So, not sending for v3.1. If it's not too late: Tested this patch and it fixes SD/MMC1 support on da850. Tested-by: Ben Gardiner Thanks, Ido! Best Regards, Ben Gardiner --- Nanometrics Inc. http://www.nanometrics.ca From troy.kisky at boundarydevices.com Fri Jul 29 13:51:46 2011 From: troy.kisky at boundarydevices.com (Troy Kisky) Date: Fri, 29 Jul 2011 11:51:46 -0700 Subject: [PATCH v3] davinci: Add wl1271/wlan support for AM18x In-Reply-To: <20110728220643.GG1985@WorkStation> References: <1311886373-3060-1-git-send-email-ido@wizery.com> <4E31D17D.6050000@boundarydevices.com> <20110728220643.GG1985@WorkStation> Message-ID: <4E330142.6040809@boundarydevices.com> On 7/28/2011 3:06 PM, Ido Yariv wrote: > Hi Troy, > > On Thu, Jul 28, 2011 at 02:15:41PM -0700, Troy Kisky wrote: >>> + if (power_on) { >>> + /* Power up sequence required for wl127x devices */ >>> + gpio_set_value(DA850_WLAN_EN, 1); >>> + usleep_range(15000, 15000); >>> + gpio_set_value(DA850_WLAN_EN, 0); >>> + usleep_range(1000, 1000); >>> + gpio_set_value(DA850_WLAN_EN, 1); >>> + msleep(70); >> >> Why turn on, then off, and then back on? >> Isn't off, then back on sufficient? > > Unfortunately, no. This is a required power up sequence for some > hardware revisions of the 1271 chip. That's too bad. > >> Also, why not use regulator API like panda board does? > > Unlike omap_hsmmc, davinci's MMC host driver does not toggle any > regulators. To keep things simple, a set_power callback function was > added to the mmc host as part of this patch series. > I've considered adding regulator support instead, but found it to be > a bit over-complicated for this task. In addition, it would require > either modifying the fixed regulator or adding a new one in order to > support the above power sequence. Yes, that is more effort. I'm fine with your method, but with all the consolidation effort that's being expended currently, it would be nice if all boards could use a common method to power up sdio cards. It might make a device tree implementation for this driver easier too, though I am certainly no expert there. > > Thanks for your review, > Ido. > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > From cjb at laptop.org Thu Jul 28 17:02:04 2011 From: cjb at laptop.org (Chris Ball) Date: Thu, 28 Jul 2011 18:02:04 -0400 Subject: [PATCH REPOST v2] mmc: davinci: Add support for set_power callback In-Reply-To: <1311886149-3009-1-git-send-email-ido@wizery.com> (Ido Yariv's message of "Thu, 28 Jul 2011 23:49:09 +0300") References: <1311886149-3009-1-git-send-email-ido@wizery.com> Message-ID: Hi, On Thu, Jul 28 2011, Ido Yariv wrote: > Some devices connected to the MMC bus are power controlled by external > means. For instance, an SDIO device may be powered down/up by an > external gpio line. > > In order to avoid toggling power from within the MMC host driver, add a > set_power callback function, which will be called by set_ios upon > powering down/up. > > Signed-off-by: Ido Yariv Acked-by: Chris Ball > CC: Chris Ball > --- > arch/arm/mach-davinci/include/mach/mmc.h | 3 +++ > drivers/mmc/host/davinci_mmc.c | 13 +++++++++++++ > 2 files changed, 16 insertions(+), 0 deletions(-) > > diff --git a/arch/arm/mach-davinci/include/mach/mmc.h b/arch/arm/mach-davinci/include/mach/mmc.h > index d4f1e96..5ba6b22 100644 > --- a/arch/arm/mach-davinci/include/mach/mmc.h > +++ b/arch/arm/mach-davinci/include/mach/mmc.h > @@ -12,6 +12,9 @@ struct davinci_mmc_config { > /* get_cd()/get_wp() may sleep */ > int (*get_cd)(int module); > int (*get_ro)(int module); > + > + void (*set_power)(int module, bool on); > + > /* wires == 0 is equivalent to wires == 4 (4-bit parallel) */ > u8 wires; > > diff --git a/drivers/mmc/host/davinci_mmc.c b/drivers/mmc/host/davinci_mmc.c > index 0076c74..64a8325 100644 > --- a/drivers/mmc/host/davinci_mmc.c > +++ b/drivers/mmc/host/davinci_mmc.c > @@ -807,12 +807,25 @@ static void calculate_clk_divider(struct mmc_host *mmc, struct mmc_ios *ios) > static void mmc_davinci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) > { > struct mmc_davinci_host *host = mmc_priv(mmc); > + struct platform_device *pdev = to_platform_device(mmc->parent); > + struct davinci_mmc_config *config = pdev->dev.platform_data; > > dev_dbg(mmc_dev(host->mmc), > "clock %dHz busmode %d powermode %d Vdd %04x\n", > ios->clock, ios->bus_mode, ios->power_mode, > ios->vdd); > > + switch (ios->power_mode) { > + case MMC_POWER_OFF: > + if (config && config->set_power) > + config->set_power(pdev->id, false); > + break; > + case MMC_POWER_UP: > + if (config && config->set_power) > + config->set_power(pdev->id, true); > + break; > + } > + > switch (ios->bus_width) { > case MMC_BUS_WIDTH_8: > dev_dbg(mmc_dev(host->mmc), "Enabling 8 bit mode\n"); -- Chris Ball One Laptop Per Child