From: Tony Lindgren Date: Mon, 20 Mar 2006 13:50:53 +0000 (-0800) Subject: Merge with /home/tmlind/src/kernel/linux-2.6 X-Git-Tag: v2.6.16-omap1~30^2~2 X-Git-Url: http://www.pilppa.org/gitweb/gitweb.cgi?a=commitdiff_plain;h=667a3448a3f56f367cf32ea510981a5c9dacdb75;hp=7705a8792b0fc82fd7d4dd923724606bbfd9fb20;p=linux-2.6-omap-h63xx.git Merge with /home/tmlind/src/kernel/linux-2.6 --- diff --git a/Documentation/arm/OMAP/README b/Documentation/arm/OMAP/README new file mode 100644 index 00000000000..e002e324483 --- /dev/null +++ b/Documentation/arm/OMAP/README @@ -0,0 +1,436 @@ + + README for ARM based OMAP processor from TI + =========================================== + +This is the README for Linux 2.6 on ARM based TI OMAP processors. + +In the first section it gives some general hints how to start with OMAP Linux. + +When successfully build a OMAP Linux kernel with help of first section and no +bootloader is already on the board, section 2 gives some tips how to use +commercial JTAG tools. + +In March 2004 the Linux Kernel 2.6 for ARM based TI OMAP processors was cleaned. +The goal was to send clean patches to RMK's official ARM tree and to make it +easier to add new OMAP processors or boards to the kernel tree. To keep the +kernel tree clean now, this document describes also some steps how +to add code for a new OMAP processor or OMAP based board to the OMAP Linux 2.6 +kernel tree. This is what the third section of this document is about. + +Section 4 of this README reports some rules to be followed to write +clean code to make it ready for easy inclusion into public OMAP Linux kernel. + +For more information also see TI's 'Linux Community for Texas Instruments OMAP +Processors' web page: + +http://linux.omap.com + +There, various downloads and resources can be found (e.g. documentation how +to build the kernel, how to use u-boot with OMAP Linux, pre-built tool chain +etc.). + +The mailing list for OMAP Linux is hosted there, too: + +http://linux.omap.com/mailman/listinfo + + +1. General hints how to start with OMAP Linux +-------------------------------------------------------------- + +The minimal setup is a arm-linux-gcc cross compiler, make, and some editor. +You will also most likely need a JTAG to flash the bootloader for the first +time. + +The first step is to get a bootloader for your board, u-boot is the +recommended one: + +http://sourceforge.net/projects/uboot/ + +Then you need to compile it with the same cross compiler as you would use +for the Linux kernel. Then you need to flash it to the board either via the +serial port, or by using a JTAG. + +Once you have the bootloader running, you can compile the kernel. + +You can get the OMAP sources either from the BitKeeper tree, or by +applying patches. The BitKeeper tree has the most up to date sources +and is the recommended one. + +- Using BitKeeper: + +You need to download the bk tool from: + +http://www.bitmover.com/ + +Download the dynamic one (x86-glibc22-linux) instead of the static +(x86-static-linux). Some users reported problems with the static version. + +After bk tool is installed, to learn more about bitkeeper you can follow the +tutorial at: + +http://www.bitkeeper.com/UG/ + +Then you can clone OMAP Linux by: + +bk clone http://linux-omap.bkbits.net/main + +Now, you have a copy (clone) of the reprository. To work with it and to +compile the kernel you have to check out the files from your local +repository by: + +bk -r get (or: bk -r edit) + +For kernel related BitKeeper information see also kernel documentation in: + +Documentation/BK-usage/bk-kernel-howto.txt + +Hint: If you are sitting behind a firewall and have to use a proxy for +internet access, you can access BitKeeper by http by setting the +http_proxy envirionment variable: + +http_proxy=http://proxy_username:proxy_password@proxy_name:proxy_port/ + +If you use bash shell, then this might look like: + +export http_proxy=http://foo:123@abc.host.com:8080/ + +with: + +foo: Your user name for the proxy +123: Your password for the proxy +abc.host.com: The name of your proxy you use for internet access +8080: The port used on to access the proxy + + +- Using Patches: + +If you don't want to use BitKeeper, then you can do the same thing with patch. + +Download the latest OMAP Linux patch from: + +http://www.muru.com/linux/omap/ + +Get a matching Linux kernel from: + +ftp://ftp.kernel.org/pub/linux/kernel/v2.6/ + +For example, if you download Linux-2.6.4-omap1 from muru.com, then you need +linux-2.6.4 kernel from kernel.org: + +$ wget ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.4.tar.bz2 +$ tar xjf linux-2.6.4.tar.bz2 +$ cd linux-2.6.4 +$ cat ../Linux-2.6.4-omap1 | patch -p1 + +Note: If OMAP patch from muru.com is against a kernel release candidate, +marked by -rcX, then kernel can be found on kernel.org under v2.6/testing/ + +Now, if you have a local kernel tree, either by BitKeeper or by patch, you +should look into arch/arm/config/ to see which of the various omap_xxx +configurations there you want to use. For example, if you have a OMAP1510 +based Innovator board, you select omap_innovator_1510_defconfig by + +$ make omap_innovator_1510_defconfig + +at top level directory (linux-2.6.4 in the example above). + +Then you can compile the kernel with + +$ make vmlinux + +Or make Image or make zImage or make uImage. + +Once you have the kernel compiled, you can upload it to the board via serial +port or JTAG (see below). + +Then you need a root file system either as initrd or on the flash. + +Once you have the system booting to Linux, you can use pretty much any Linux +applications cross compiled for ARM. + + +2. JTAG usage +-------------------------------------------------------------- + +If the flash of your board is really 'empty' and no bootloader is on the board +(e.g. u-boot) then you need a JTAG connection. With JTAG you can write +a bootloader to board's flash or download OMAP Linux kernel. For OMAP +commercial JTAG tools are available, so you have to pay for it. + +Examples are TI's Code Composer Studio (CCS) or Lauterbach's TRACE32 JTAG. + +- Linux kernel download with CCS + +You can use CCS to directly load an ELF file to your board. For example, use +arch/arm/boot/compressed/vmlinux. zImage isn't suited because it is not an ELF +file. CCS looks for .out files, so copy arch/arm/boot/compressed/vmlinux +to vmlinux.out and load it using CCS. Or use the filter *.* to select +vmlinux directly. Remember to run arm-linux-strip on ELF file first as CCS +get stroppy about unstripped ELF files. + +If you want vmlinux to be linked to run at a specific address, you can use +the CONFIG_ZBOOT options in the kernel build. But first try without +CONFIG_ZBOOT as the compressed image should be able to run from address +zero (if your CCS .gel files map address zero.) + +Otherwise, use something like this: + +CONFIG_ZBOOT_ROM=y +CONFIG_ZBOOT_ROM_TEXT=10408000 +CONFIG_ZBOOT_ROM_BSS=10800000 + +Also note that CCS is pretty useless for debugging Linux as it doesn't +properly handle virtual memory. In other words, once the MMU is +turned on and Linux is using virtual memory, CCS can no longer +properly disassemble, set breakpoints or read memory. + + +- Linux kernel download with Lauterbach TRACE32 + +To be done. + + +3. How to add new processor or board to OMAP Linux kernel tree +-------------------------------------------------------------- + +It is assumed that the OMAP processor to be added is based on an already +supported ARM core (e.g. ARM925 or ARM926). How to add support for new ARM +processor core that is not supported by ARM Linux is not scope of this document. + +1. If a new OMAP processor should be added, identify the ARM core of this +processor. E.g. at time of writing this document in March 2004 OMAP730 (ARM926 +core), OMAP1510 (ARM925 core) and OMAP1610 (ARM926 core) are supported. + +For a new board or device, identify the OMAP processor on the board. E.g. at +time of writing this document in March 2004 four boards are supported: +Innovator1510 (OMAP1510 processor), Innovator1610 (OMAP1610 processor), +Perseus2 (OMAP730 processor) and H2 (OMAP1610 processor). + +/* Discussion needed: How to handle the tons of compatible processors? +E.g. what to do if OMAP16xx is mainly identical with OMAP16yy? */ + +2. Start with arch/arm/mach-omap/Kconfig and add a new processor or board +option. + +To add a new processor add a new config option to the "OMAP Core Type" choice. +See examples for the syntax. The config option has to be called "ARCH_OMAPxxxx" +where xxxx is the number of OMAP processor. Don't forget to select a existing +clock frequency or to add a new one in "OMAP Feature Selections" section for +your new processor. + +To add a new board or device, add a new config option to the "OMAP Board Type" +choice. See examples for the syntax. The config option for boards has to be +called "MACH_OMAP_yyyy" where yyyy is the board name. Don't forget to add a +short help. + +Note: Kernel 2.6 Kconfig system will automatically expand the configuration +names with a leading "CONFIG_". So "ARCH_OMAPxxxx" will be expanded to +"CONFIG_ARCH_OMAPxxxx" and "MACH_OMAP_yyy" will expand to +"CONFIG_MACH_OMAP_yyyy". In code this can then be used by macros like +"#ifdef CONFIG_ARCH_OMAPxxxx" and "#ifdef CONFIG_MACH_OMAP_yyyy". + +Note: How to handle boards which are compatible or extensions of other boards? +See MACH_OMAP_H2 for example. The H2 depends on MACH_OMAP_INNOVATOR and expands +it. This is done by an additional select MACH_OMAP_INNOVATOR in MACH_OMAP_H2 +configuration option. With this the whole MACH_OMAP_INNOVATOR configuration is +selected and an additional symbol CONFIG_MACH_OMAP_H2 is available to +distinguish between INNOVATOR and H2 where necessary. + +3a. Only for new processors: Add the ARCH_OMAPxxxx to the correct ARM core in +arch/arm/mm/Kconfig. E.g. ARCH_OMAP730 in CPU_ARM926T configuration. + +3b. Only for new boards: Register the board within ARM Linux machine +registration system from RMK. For the CONFIG_ section use the same name like +in arch/arm/mach-omap/Kconfig. E.g. MACH_OMAP_yyyy. For MACH_TYPE_ section use +OMAP_yyyy where yyyy is the board name like above. + +Note: The elements of RMKs machine registration are used in +arch/arm/tools/mach-types. While kernel compilation +include/asm-arm/mach-types.h is generated automagically from this file. The +content of mach-types.h then is used for machine identification by kernel +bootcode and can be used for board identification. + +Note: The ARM Linux machine registration system from RMK can be found under: + +www.arm.linux.org.uk/developer/machines/ + +Note: Only OMAP based boards should be registered to RMKs registration +system. Not processors. + +4. Add a processor or board specific header file in include/asm-arm/arch-omap/. +Use board-yyyy.h with yyyy board name or omapxxxx.h with xxxx processor number. + +5. Add a processor or board specific section into include/asm-arm/arch-omap/ +hardware.h. Use examples for syntax and use CONFIG_ names as defined in +arch/arm/mach-omap/Kconfig. + +6. Add processor or board specific macros to board-yyyy.h or omapxxxx.h. The +macros to these specific files have to be named OMAPxxxx_ with xxxx processor +number to make them unique. + +7a. Only for new boards: Add a file board-yyyy.c with yyyy board name to +arch/arm/mach-omap/. Put board specific initialization code and resource +description into this file. The first element of MACHINE_START must be equal to +MACH_TYPE_ section of machine registration (see arch/arm/tools/mach-types after +machine registration at RMKs registration system). + +Put only code into this file that is board specific and not common. See other +board files for examples. + +7b. Only for new processors: Add processor specific IO description and +iotable_init() to arch/arm/mach-omap/common.c. See examples for the syntax. + +If you have introduced new clock definition in 2., add support for this new +clock in include/asm-arm/arch-omap/clocks.h and arch/arm/mach-omap/clocks.c. + +8. Only for new boards: Add "obj-$(CONFIG_MACH_OMAP_yyyy) += board-yyyy.o" with +yyyy board name to arch/arm/mach-omap/Makefile. This is used to compile your new +board specific initialization code from 7a. + +9. Check if other of the existing files have to be adjusted for the new +processor or board. Things to check: + +- Pin multiplexing +- GPIO configuration +- Powermanagement +- Clocking +- Interrupt controller and interrupt configuration +- Additional board specific things (e.g. FPGAs) + +If other existing files or device drivers have to be changed, use the following +mechanism for processor specific things: + +#ifdef CONFIG_ARCH_OMAPxxxx + if (cpu_is_omapxxxx()) { + /* Do the OMAPxxxx processor specific magic */ + } +#endif + +Note: cpu_is_omapxxxx() macro is defined in include/asm-arm/arch-omap/hardware.h +and uses OMAP_ID_REG for runtime processor identifcation. + +For board differentiation use board macro from include/asm-arm/mach-types.h: + +#ifdef CONFIG_MACH_OMAP_yyyy + if (machine_is_omap_yyyy()) { + /* Do the board specific magic */ + } +#endif + +Note: If technically possible and already implemented the OMAP Linux kernel +has support for a "one binary fits all" machanism. That is, the goal is to be +able to enable support for multiple OMAP processors and/or boards in Kconfig +system. Then it is decided by bootparameters and at runtime on which processor +and/or board the kernel is actually running on. With this machanism it is +possible to use the same kernel binary on different OMAP processors or boards +without recompiling. This is achived by the cpu_is_omapxxxx() and +machine_is_omap_yyyy() macros. + +On the other hand, for memory limited embedded systems it should be possible +to compile the kernel with support for only one processor/board combination. +For this a kernel binary is necessary which isn't bloated with code for all +other (unused) processors and boards. This is achived by using the preprocessor +CONFIG_ARCH_OMAPxxxx and CONFIG_MACH_OMAP_yyyy macros around the runtime +cpu_is_omapxxxx() and machine_is_omap_yyyy() selection. + +At the moment, the price for this flexibility is a increased number of #ifdef's +throughout the code. + +10. Configure the kernel by make menuconfig or make xconfig and select the new +processor or board. + +11. Compile the kernel by an appropriate cross compilation toolchain. Make this +until the code compiles error and warning free. The kernel should also be +compiled with the various debug checking thingies enabled (e.g. +CONFIG_DEBUG_SPINLOCK, CONFIG_DEBUG_PAGEALLOC etc.). + +/* ToDo: Anything to say about toolchain? */ + +12. Download the kernel image to the board and test it until it works ;-) + +It's not in the scope of this document how to do this (use a appropriate +bootloader or JTAG download). + +Note: The kernel initialization code expects some special values in the +registers R0, R1 and R2 of the ARM processor. These registers have to be +written by bootloader or debugger before starting the kernel. R0 has to be +zero, R1 has to contain the machine number from machine registration in +arch/arm/tools/mach-types. R2 points to the physical address of tagged list +in system RAM. For more information see Documentation/arm/Booting. + +While testing a new processor or board configuration, it is recommended to +enable low level debugging. This uses low level output functions to print kernel +messages on serial line before console is working. Enable it by + +Kernel hacking -> Kernel debugging -> Kernel low-level debugging functions + +in kernel configuration system. + +13. Check that no other processors or boards are broken by the new code. A first +test is to successful compile the other omap_xxx configurations from +arch/arm/configs/. Do this by e.g. + +cd linux +make omap_innovator_1510_defconfig +Compile the kernel + +Even better: Enable support for several processors and boards in Kconfig +system and compile kernel successfully. + +14. Only for new boards: Add a new default board configuration to +arch/arm/configs. Use omap_yyyy_xxxx_defconfig with yyyy boardname and xxxx +processornumber as filename. + +15. If the new code works, compiles without warnings and seems to break no other +configurations, post a patch to linux-omap-open-source@list.ti.com. + +With sending a patch to the community, it is reviewed, can be used and tested by +other users. It then can be included into the public OMAP kernel tree. + +16. Then adapt device drivers or write additional drivers for non-existing +processor peripherals or board devices. Improve and maintain the code for your +new processor or board. + + +4. General guidelines to write clean and OMAP Linux compatible code +------------------------------------------------------------------- + +- For register access use the __REG8/16/32() macros. At the moment, see first +example in include/asm-arm/arch-omap/hardware.h. + +Allegedly __REG() makes at least some versions of GCC emit tighter code +than the more direct wrappers. Presumably by making it easier to use certain +addressing modes. + +Make sure that the registers names are clearly marked as being registers +(and not addresses of registers). This has to be done by adding a '_REG' +suffix. E.g. + +#define OMAP_ID_REG (__REG32(0xfffed400)) +#define DPLL_CTL_REG (__REG16(0xfffecf00)) + +__raw_read[bwl] and __raw_write[bwl] are deprecated. They will converted to +__REG8/16/32() syntax, soon. Don't use anything else like own pointer +definitions or in[bwl]/out[bwl] etc., too. + +- Make read-modify-write register access preemption save. Use spin_lock() and +spin_unlock() where necessary. If an IRQ handler can access the registers, +use spin_lock_irqsave(), too. + +- Functions declared as __init shouldn't have any references after the kernel +initialization phase is complete. Usually they should be static as well. + +- Don't use return statements at end of void functions. + +- Use consistent indentation style. Don't use space indentations. Use tab +indentations. + +- In general use Linux formatting style. See Documentation/CodingStyle for more +information. If you use GNU emacs, see also chapter 8 of that document how to +add a linux-c-mode to emacs. + + +------------------------------------------------------------------ +Last modified 13. June 2004 +The OMAP Linux Kernel Team +Dirk Behme diff --git a/Makefile b/Makefile index cb5790580fc..88a71bdbbd4 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,9 @@ NAME=Sliding Snow Leopard # Comments in this file are targeted only to the developer, do not # expect to learn how to build the kernel reading this file. +# Add custom flags here to avoid conflict with updates +EXTRAVERSION := $(EXTRAVERSION)-omap1 + # Do not print "Entering directory ..." MAKEFLAGS += --no-print-directory @@ -152,6 +155,7 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ -e s/arm.*/arm/ -e s/sa110/arm/ \ -e s/s390x/s390/ -e s/parisc64/parisc/ \ -e s/ppc.*/powerpc/ ) +SUBARCH := arm # Cross compiling and selecting different set of gcc/bin-utils # --------------------------------------------------------------------------- @@ -173,7 +177,7 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ \ # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile ARCH ?= $(SUBARCH) -CROSS_COMPILE ?= +CROSS_COMPILE ?= arm-linux- # Architecture as present in compile.h UTS_MACHINE := $(ARCH) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 32ba00bd0a2..d26e3b3fc9f 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -497,8 +497,7 @@ config LEDS system, but the driver will do nothing. config LEDS_TIMER - bool "Timer LED" if (!ARCH_CDB89712 && !ARCH_OMAP) || \ - MACH_OMAP_H2 || MACH_OMAP_PERSEUS2 + bool "Timer LED" if !ARCH_CDB89712 depends on LEDS default y if ARCH_EBSA110 help @@ -513,8 +512,7 @@ config LEDS_TIMER will overrule the CPU usage LED. config LEDS_CPU - bool "CPU usage LED" if (!ARCH_CDB89712 && !ARCH_EBSA110 && \ - !ARCH_OMAP) || MACH_OMAP_H2 || MACH_OMAP_PERSEUS2 + bool "CPU usage LED" if (!ARCH_CDB89712 && !ARCH_EBSA110) depends on LEDS help If you say Y here, the red LED will be used to give a good real @@ -819,6 +817,12 @@ source "drivers/usb/Kconfig" source "drivers/mmc/Kconfig" +source "drivers/ssi/Kconfig" + +if ARCH_OMAP +source "drivers/cbus/Kconfig" +endif + endmenu source "fs/Kconfig" diff --git a/arch/arm/Makefile b/arch/arm/Makefile index fbfc14a56b9..80ccb869cc7 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -54,7 +54,7 @@ tune-$(CONFIG_CPU_ARM926T) :=-mtune=arm9tdmi tune-$(CONFIG_CPU_SA110) :=-mtune=strongarm110 tune-$(CONFIG_CPU_SA1100) :=-mtune=strongarm1100 tune-$(CONFIG_CPU_XSCALE) :=$(call cc-option,-mtune=xscale,-mtune=strongarm110) -Wa,-mcpu=xscale -tune-$(CONFIG_CPU_V6) :=$(call cc-option,-mtune=arm1136j-s,-mtune=strongarm) +tune-$(CONFIG_CPU_V6) :=$(call cc-option,-mtune=arm1136jf-s,-mtune=arm1136jfs) ifeq ($(CONFIG_AEABI),y) CFLAGS_ABI :=-mabi=aapcs -mno-thumb-interwork diff --git a/arch/arm/boot/.gitignore b/arch/arm/boot/.gitignore new file mode 100644 index 00000000000..fc9b99cfcdd --- /dev/null +++ b/arch/arm/boot/.gitignore @@ -0,0 +1,3 @@ +Image +zImage +uImage diff --git a/arch/arm/boot/compressed/.gitignore b/arch/arm/boot/compressed/.gitignore new file mode 100644 index 00000000000..aefee20cbf9 --- /dev/null +++ b/arch/arm/boot/compressed/.gitignore @@ -0,0 +1 @@ +piggy.gz diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index 35ffe0f4ece..60e306eff77 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -51,7 +51,11 @@ OBJS += head-at91rm9200.o endif ifeq ($(CONFIG_DEBUG_ICEDCC),y) -OBJS += ice-dcc.o +OBJS += ice-dcc.o +endif + +ifeq ($(CONFIG_MACH_OMAP_PERSEUS2),y) +OBJS += head-omap.o endif ifeq ($(CONFIG_CPU_BIG_ENDIAN),y) diff --git a/arch/arm/boot/compressed/head-omap.S b/arch/arm/boot/compressed/head-omap.S new file mode 100644 index 00000000000..440e9a5edd3 --- /dev/null +++ b/arch/arm/boot/compressed/head-omap.S @@ -0,0 +1,19 @@ +/* + * linux/arch/arm/boot/compressed/head-omap.S + * + * OMAP specific tweaks. This is merged into head.S by the linker. + * + */ + +#include +#include +#include + + .section ".start", "ax" + +__OMAP_start: +#ifdef CONFIG_MACH_OMAP_PERSEUS2 + /* support for booting without u-boot */ + mov r7, #(MACH_TYPE_OMAP_PERSEUS2 & ~0xf) + orr r7, r7, #(MACH_TYPE_OMAP_PERSEUS2 & 0xf) +#endif diff --git a/arch/arm/boot/compressed/head.S b/arch/arm/boot/compressed/head.S index db3389d8e02..3158155ba9b 100644 --- a/arch/arm/boot/compressed/head.S +++ b/arch/arm/boot/compressed/head.S @@ -42,6 +42,20 @@ add \rb, \rb, #0x00010000 @ Ser1 #endif .endm +#elif defined(CONFIG_ARCH_OMAP2) + .macro loadsp, rb + mov \rb, #0x48000000 @ physical base address + add \rb, \rb, #0x0006a000 +#ifdef CONFIG_OMAP_LL_DEBUG_UART2 + add \rb, \rb, #0x00002000 +#endif +#ifdef CONFIG_OMAP_LL_DEBUG_UART3 + add \rb, \rb, #0x00004000 +#endif + .endm + .macro writeb, rb + strb \rb, [r3] + .endm #elif defined(CONFIG_ARCH_IOP331) .macro loadsp, rb mov \rb, #0xff000000 diff --git a/arch/arm/configs/n770_defconfig b/arch/arm/configs/n770_defconfig new file mode 100644 index 00000000000..ef33d828c14 --- /dev/null +++ b/arch/arm/configs/n770_defconfig @@ -0,0 +1,1342 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.16-rc2-omap1 +# Fri Feb 10 15:29:21 2006 +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_CALIBRATE_DELAY=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_UID16=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +CONFIG_SLAB=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_SLOB is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_KMOD is not set + +# +# Block layer +# + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +CONFIG_IOSCHED_CFQ=y +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +CONFIG_DEFAULT_CFQ=y +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="cfq" + +# +# System Type +# +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_AT91RM9200 is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP_OTG=y +CONFIG_ARCH_OMAP1=y +# CONFIG_ARCH_OMAP2 is not set + +# +# OMAP Feature Selections +# +CONFIG_OMAP_RESET_CLOCKS=y +CONFIG_OMAP_BOOT_TAG=y +CONFIG_OMAP_BOOT_REASON=y +CONFIG_OMAP_COMPONENT_VERSION=y +CONFIG_OMAP_GPIO_SWITCH=y +# CONFIG_OMAP_MUX is not set +CONFIG_OMAP_STI=y +CONFIG_OMAP_STI_CONSOLE=y +# CONFIG_OMAP_MPU_TIMER is not set +CONFIG_OMAP_32K_TIMER=y +CONFIG_OMAP_32K_TIMER_HZ=128 +CONFIG_OMAP_DM_TIMER=y +CONFIG_OMAP_LL_DEBUG_UART1=y +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +# CONFIG_OMAP_LL_DEBUG_UART3 is not set + +# +# OMAP Core Type +# +# CONFIG_ARCH_OMAP730 is not set +# CONFIG_ARCH_OMAP15XX is not set +CONFIG_ARCH_OMAP16XX=y + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP_INNOVATOR is not set +# CONFIG_MACH_OMAP_H2 is not set +# CONFIG_MACH_OMAP_H3 is not set +# CONFIG_MACH_OMAP_OSK is not set +CONFIG_MACH_NOKIA770=y +# CONFIG_MACH_OMAP_GENERIC is not set + +# +# OMAP CPU Speed +# +CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER=y +CONFIG_OMAP_ARM_216MHZ=y +# CONFIG_OMAP_ARM_192MHZ is not set +# CONFIG_OMAP_ARM_168MHZ is not set +# CONFIG_OMAP_ARM_120MHZ is not set +# CONFIG_OMAP_ARM_60MHZ is not set +# CONFIG_OMAP_ARM_30MHZ is not set +CONFIG_OMAP_DSP=y +# CONFIG_OMAP_DSP_MBCMD_VERBOSE is not set +CONFIG_OMAP_DSP_TASK_MULTIOPEN=y +CONFIG_OMAP_DSP_FBEXPORT=y + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set + +# +# Bus support +# + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +# CONFIG_PREEMPT is not set +CONFIG_NO_IDLE_HZ=y +# CONFIG_AEABI is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="root=1f03 rootfstype=jffs2 time" +# CONFIG_XIP_KERNEL is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_LEGACY is not set +# CONFIG_PM_DEBUG is not set +# CONFIG_APM is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_NETDEBUG is not set +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_BIC=y + +# +# IP: Virtual Server Configuration +# +# CONFIG_IP_VS is not set +# CONFIG_IPV6 is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set + +# +# Core Netfilter Configuration +# +CONFIG_NETFILTER_NETLINK=y +# CONFIG_NETFILTER_NETLINK_QUEUE is not set +# CONFIG_NETFILTER_NETLINK_LOG is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_IP_NF_CONNTRACK is not set +# CONFIG_IP_NF_QUEUE is not set + +# +# DCCP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set + +# +# TIPC Configuration (EXPERIMENTAL) +# +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=y + +# +# Bluetooth device drivers +# +# CONFIG_BT_HCIUSB is not set +# CONFIG_BT_HCIUART is not set +# CONFIG_BT_HCIBCM203X is not set +# CONFIG_BT_HCIBPA10X is not set +# CONFIG_BT_HCIBFUSB is not set +CONFIG_BT_HCIBRF6150=y +# CONFIG_BT_HCIVHCI is not set +# CONFIG_IEEE80211 is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_DEBUG_DRIVER is not set + +# +# Connector - unified userspace <-> kernelspace linker +# +CONFIG_CONNECTOR=y +# CONFIG_PROC_EVENTS is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_DATAFLASH is not set +# CONFIG_MTD_M25P80 is not set +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_TOTO is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_NANDSIM is not set +CONFIG_MTD_NAND_OMAP_HW=y + +# +# OneNAND Flash Device Drivers +# +# CONFIG_MTD_ONENAND is not set +# CONFIG_MTD_ONENAND_SYNC_READ is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +# CONFIG_BLK_DEV_RAM is not set +CONFIG_BLK_DEV_RAM_COUNT=16 +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +# CONFIG_SCSI_PROC_FS is not set + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_ISCSI_TCP is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +CONFIG_TUN=y + +# +# PHY device support +# +# CONFIG_PHYLIB is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_SMC91X is not set +# CONFIG_DM9000 is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y + +# +# Obsolete Wireless cards support (pre-802.11) +# +# CONFIG_STRIP is not set +# CONFIG_ATMEL is not set +# CONFIG_HOSTAP is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +# CONFIG_PPP_MPPE is not set +# CONFIG_PPPOE is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_KEYBOARD_OMAP=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=y +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_OMAP is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_OMAP_WATCHDOG=y +CONFIG_OMAP_RNG=y +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_OMAP_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# +# CONFIG_TCG_TPM is not set +# CONFIG_TELCLOCK is not set + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_PCA_ISA is not set +CONFIG_I2C_OMAP=y + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 is not set +# CONFIG_ISP1301_OMAP is not set +# CONFIG_TPS65010 is not set +CONFIG_SENSORS_TLV320AIC23=y +# CONFIG_GPIOEXPANDER_OMAP is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_RTC_X1205_I2C is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# SPI support +# +CONFIG_SPI=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +CONFIG_SPI_BITBANG=y +CONFIG_SPI_OMAP_UWIRE=y + +# +# SPI Protocol Masters +# + +# +# Hardware Monitoring support +# +# CONFIG_HWMON is not set +# CONFIG_HWMON_VID is not set + +# +# Misc devices +# + +# +# Multimedia Capabilities Port drivers +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +CONFIG_FB=y +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_OMAP=y +CONFIG_FB_OMAP_LCDC_EXTERNAL=y +CONFIG_FB_OMAP_LCDC_HWA742=y +CONFIG_FB_OMAP_MANUAL_UPDATE=y +CONFIG_FB_OMAP_LCD_LPH8923=y +# CONFIG_FB_OMAP_BOOTLOADER_INIT is not set +# CONFIG_FB_OMAP_DMA_TUNE is not set +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE is not set + +# +# Logo configuration +# +# CONFIG_LOGO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +CONFIG_SND_HWDEP=y +CONFIG_SND_RAWMIDI=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +# CONFIG_SND_SUPPORT_OLD_API is not set +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +CONFIG_SND_DUMMY=y +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set + +# +# ALSA ARM devices +# +CONFIG_SND_OMAP_AIC23=y + +# +# USB devices +# +CONFIG_SND_USB_AUDIO=y + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set + +# +# USB support +# +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_BANDWIDTH=y +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +CONFIG_USB_OTG=y +# CONFIG_USB_OTG_WHITELIST is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_OHCI_BIG_ENDIAN is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_SL811_HCD is not set + +# +# USB Device Class drivers +# +# CONFIG_OBSOLETE_OSS_USB_DRIVER is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# may also be needed; see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_USB_HIDINPUT_POWERBOOK is not set +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_ACECAD is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_ITMTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_YEALINK is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set +# CONFIG_USB_ATI_REMOTE2 is not set +# CONFIG_USB_KEYSPAN_REMOTE is not set +# CONFIG_USB_APPLETOUCH is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +CONFIG_USB_USBNET=y +# CONFIG_USB_NET_AX8817X is not set +CONFIG_USB_NET_CDCETHER=y +# CONFIG_USB_NET_GL620A is not set +CONFIG_USB_NET_NET1080=y +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +CONFIG_USB_NET_ZAURUS=y +# CONFIG_USB_ZD1201 is not set +# CONFIG_USB_MON is not set + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_CONSOLE=y +# CONFIG_USB_SERIAL_GENERIC is not set +# CONFIG_USB_SERIAL_AIRPRIME is not set +# CONFIG_USB_SERIAL_ANYDATA is not set +# CONFIG_USB_SERIAL_BELKIN is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +# CONFIG_USB_SERIAL_DIGI_ACCELEPORT is not set +# CONFIG_USB_SERIAL_CP2101 is not set +# CONFIG_USB_SERIAL_CYPRESS_M8 is not set +# CONFIG_USB_SERIAL_EMPEG is not set +# CONFIG_USB_SERIAL_FTDI_SIO is not set +# CONFIG_USB_SERIAL_VISOR is not set +# CONFIG_USB_SERIAL_IPAQ is not set +# CONFIG_USB_SERIAL_IR is not set +# CONFIG_USB_SERIAL_EDGEPORT is not set +# CONFIG_USB_SERIAL_EDGEPORT_TI is not set +# CONFIG_USB_SERIAL_GARMIN is not set +# CONFIG_USB_SERIAL_IPW is not set +# CONFIG_USB_SERIAL_KEYSPAN_PDA is not set +# CONFIG_USB_SERIAL_KEYSPAN is not set +# CONFIG_USB_SERIAL_KLSI is not set +# CONFIG_USB_SERIAL_KOBIL_SCT is not set +# CONFIG_USB_SERIAL_MCT_U232 is not set +CONFIG_USB_SERIAL_PL2303=y +# CONFIG_USB_SERIAL_HP4X is not set +# CONFIG_USB_SERIAL_SAFE is not set +# CONFIG_USB_SERIAL_TI is not set +# CONFIG_USB_SERIAL_CYBERJACK is not set +# CONFIG_USB_SERIAL_XIRCOM is not set +# CONFIG_USB_SERIAL_OMNINET is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETKIT is not set +# CONFIG_USB_PHIDGETSERVO is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TEST is not set + +# +# USB DSL modem support +# + +# +# USB Gadget Support +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_PXA2XX is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LH7A40X is not set +CONFIG_USB_GADGET_OMAP=y +CONFIG_USB_OMAP=y +# CONFIG_USB_GADGET_DUMMY_HCD is not set +# CONFIG_USB_GADGET_DUALSPEED is not set +# CONFIG_USB_ZERO is not set +CONFIG_USB_ETH=m +CONFIG_USB_ETH_RNDIS=y +# CONFIG_USB_GADGETFS is not set +CONFIG_USB_FILE_STORAGE=m +CONFIG_USB_FILE_STORAGE_TEST=y +# CONFIG_USB_G_SERIAL is not set + +# +# MMC/SD Card support +# +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BROKEN_RFD=y +CONFIG_MMC_BULKTRANSFER=y +CONFIG_MMC_OMAP=y + +# +# Synchronous Serial Interfaces (SSI) +# +CONFIG_OMAP_UWIRE=y +# CONFIG_OMAP_TSC2101 is not set + +# +# CBUS support +# +CONFIG_CBUS=y +CONFIG_CBUS_TAHVO=y +CONFIG_CBUS_TAHVO_USER=y +CONFIG_CBUS_TAHVO_USB=y +# CONFIG_CBUS_TAHVO_USB_HOST_BY_DEFAULT is not set +CONFIG_CBUS_RETU=y +CONFIG_CBUS_RETU_USER=y +CONFIG_CBUS_RETU_POWERBUTTON=y +CONFIG_CBUS_RETU_RTC=y +CONFIG_CBUS_RETU_WDT=y + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_INOTIFY is not set +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_RELAYFS_FS is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +CONFIG_JFFS2_SUMMARY=y +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +# CONFIG_ROOT_NFS is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_9P_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +CONFIG_NLS_CODEPAGE_852=y +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +CONFIG_NLS_ISO8859_15=y +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_KERNEL=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_DEBUG_SLAB is not set +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_DEBUG_VM is not set +CONFIG_FRAME_POINTER=y +CONFIG_FORCED_INLINING=y +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_WAITQ is not set +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +CONFIG_SECURITY=y +# CONFIG_SECURITY_NETWORK is not set +# CONFIG_SECURITY_CAPABILITIES is not set +# CONFIG_SECURITY_ROOTPLUG is not set +# CONFIG_SECURITY_SECLVL is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +CONFIG_CRC_CCITT=y +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y diff --git a/arch/arm/configs/omap_generic_1510_defconfig b/arch/arm/configs/omap_generic_1510_defconfig new file mode 100644 index 00000000000..5091946f1a3 --- /dev/null +++ b/arch/arm/configs/omap_generic_1510_defconfig @@ -0,0 +1,851 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_STANDALONE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_HOTPLUG is not set +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# System Type +# +# CONFIG_ARCH_ADIFCC is not set +# CONFIG_ARCH_ANAKIN is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_SHARK is not set +CONFIG_ARCH_OMAP=y + +# +# CLPS711X/EP721X Implementations +# + +# +# Epxa10db +# + +# +# Footbridge Implementations +# + +# +# IOP3xx Implementation Options +# +# CONFIG_ARCH_IOP310 is not set +# CONFIG_ARCH_IOP321 is not set + +# +# IOP3xx Chipset Features +# + +# +# Intel PXA250/210 Implementations +# + +# +# SA11x0 Implementations +# + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP15XX=y +# CONFIG_ARCH_OMAP1610 is not set +# CONFIG_OMAP_INNOVATOR is not set +CONFIG_MACH_OMAP_GENERIC=y +# CONFIG_INNOVATOR_MISSED_IRQS is not set + +# +# OMAP Feature Selections +# +# CONFIG_OMAP1510_PM is not set +CONFIG_OMAP_ARM_168MHZ=y +# CONFIG_OMAP_ARM_120MHZ is not set +# CONFIG_OMAP_ARM_60MHZ is not set +# CONFIG_OMAP_ARM_30MHZ is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM925T=y +CONFIG_CPU_32v4=y +CONFIG_CPU_ABRT_EV4T=y +CONFIG_CPU_CACHE_V4WT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y + +# +# Processor Features +# +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +CONFIG_CPU_DCACHE_WRITETHROUGH=y + +# +# General setup +# +# CONFIG_ZBOOT_ROM is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 + +# +# At least one math emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Generic Driver Options +# +CONFIG_PM=y +CONFIG_PREEMPT=y +# CONFIG_APM is not set +# CONFIG_ARTHUR is not set +CONFIG_CMDLINE="mem=64M console=ttyS2,115200 root=0803 ro init=/bin/sh" +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_DCSSBLK is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_INET_ECN is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_DECNET is not set +# CONFIG_BRIDGE is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +CONFIG_IPV6_SCTP__=y +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_NET_VENDOR_SMC=y +# CONFIG_SMC9194 is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# +CONFIG_PPP=y +CONFIG_PPP_MULTILINK=y +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPPOE is not set +# CONFIG_SLIP is not set + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Token Ring devices +# +# CONFIG_SHAPER is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set + +# +# Amateur Radio support +# +# CONFIG_HAMRADIO is not set + +# +# IrDA (infrared) support +# +# CONFIG_IRDA is not set + +# +# Bluetooth support +# +# CONFIG_BT is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +CONFIG_SCSI=y +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=y + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +CONFIG_SCSI_REPORT_LUNS=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_KEYBOARD_OMAP=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set + +# +# Mice +# +# CONFIG_BUSMOUSE is not set +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +CONFIG_OMAP_RTC=y +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_ELV is not set +# CONFIG_I2C_ISA is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_VELLEMAN is not set +CONFIG_I2C_OMAP1610=y + +# +# I2C Hardware Sensors Chip support +# +# CONFIG_I2C_SENSOR is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# MMC/SD Card support +# +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BROKEN_RFD=y +CONFIG_MMC_OMAP=y + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +CONFIG_AUTOFS_FS=y +CONFIG_AUTOFS4_FS=y + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +# CONFIG_ROOT_NFS is not set +CONFIG_LOCKD=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_GSS is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_NEC98_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_EFI_PARTITION is not set + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Misc devices +# + +# +# USB support +# +CONFIG_USB=y +CONFIG_USB_DEBUG=y + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_EHCI_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_UHCI_HCD is not set +# CONFIG_USB_SL811HS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_DPCM=y +CONFIG_USB_STORAGE_HP8200e=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_XPAD is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +CONFIG_USB_RTL8150=y +CONFIG_USB_USBNET=y + +# +# USB Host-to-Host Cables +# +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y +CONFIG_USB_GENESYS=y +CONFIG_USB_NET1080=y +CONFIG_USB_PL2301=y + +# +# Intelligent USB Devices/Gadgets +# +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_ZAURUS=y +CONFIG_USB_CDCETHER=y + +# +# USB Network Adapters +# +CONFIG_USB_AX8817X=y + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_BRLVGER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_TEST is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# Kernel hacking +# +CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_INFO is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SLAB is not set +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_WAITQ is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_CRC32=y diff --git a/arch/arm/configs/omap_generic_1610_defconfig b/arch/arm/configs/omap_generic_1610_defconfig new file mode 100644 index 00000000000..b772e7ed441 --- /dev/null +++ b/arch/arm/configs/omap_generic_1610_defconfig @@ -0,0 +1,883 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc3-omap1 +# Mon Oct 4 10:14:44 2004 +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_HOTPLUG is not set +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +CONFIG_KMOD=y + +# +# System Type +# +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_VERSATILE_PB is not set +# CONFIG_ARCH_IMX is not set + +# +# TI OMAP Implementations +# + +# +# OMAP Core Type +# +# CONFIG_ARCH_OMAP730 is not set +# CONFIG_ARCH_OMAP15XX is not set +CONFIG_ARCH_OMAP16XX=y +CONFIG_ARCH_OMAP_OTG=y + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP_INNOVATOR is not set +# CONFIG_MACH_OMAP_H2 is not set +# CONFIG_MACH_OMAP_H3 is not set +# CONFIG_MACH_OMAP_H4 is not set +# CONFIG_MACH_OMAP_OSK is not set +CONFIG_MACH_OMAP_GENERIC=y + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_BOOT_TAG is not set +CONFIG_OMAP_MUX=y +# CONFIG_OMAP_MUX_DEBUG is not set +CONFIG_OMAP_MUX_WARNINGS=y +CONFIG_OMAP_LL_DEBUG_UART1=y +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +# CONFIG_OMAP_LL_DEBUG_UART3 is not set +CONFIG_OMAP_ARM_192MHZ=y +# CONFIG_OMAP_ARM_168MHZ is not set +# CONFIG_OMAP_ARM_120MHZ is not set +# CONFIG_OMAP_ARM_60MHZ is not set +# CONFIG_OMAP_ARM_30MHZ is not set +# CONFIG_OMAP_DSP is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y + +# +# Processor Features +# +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set + +# +# General setup +# +# CONFIG_ZBOOT_ROM is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 + +# +# At least one math emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +# CONFIG_VFP is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_DEBUG_DRIVER is not set +CONFIG_PM=y +CONFIG_PREEMPT=y +# CONFIG_APM is not set +# CONFIG_ARTHUR is not set +CONFIG_CMDLINE="mem=64M console=ttyS2,115200 root=0803 ro init=/bin/sh" +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_NET_VENDOR_SMC=y +# CONFIG_SMC91X is not set +# CONFIG_SMC9194 is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +CONFIG_PPP=y +CONFIG_PPP_MULTILINK=y +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPPOE is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# SCSI device support +# +CONFIG_SCSI=y +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=y + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_RAW is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_KEYBOARD_OMAP=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +# CONFIG_SERIAL_8250_OMAP is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +CONFIG_OMAP_RTC=y +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_ISA is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_SCx200_ACB is not set +# CONFIG_I2C_PCA_ISA is not set +# CONFIG_I2C_OMAP is not set + +# +# Hardware Sensors Chip support +# +# CONFIG_I2C_SENSOR is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set + +# +# Other I2C Chip support +# +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 is not set +# CONFIG_ISP1301_OMAP is not set +# CONFIG_TPS65010 is not set +# CONFIG_GPIOEXPANDER_OMAP is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +CONFIG_AUTOFS_FS=y +CONFIG_AUTOFS4_FS=y + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +# CONFIG_ROOT_NFS is not set +CONFIG_LOCKD=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_EFI_PARTITION is not set + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Misc devices +# + +# +# USB support +# +CONFIG_USB=y +CONFIG_USB_DEBUG=y + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_EHCI_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_UHCI_HCD is not set +# CONFIG_USB_SL811HS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT is not set +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_DPCM=y +CONFIG_USB_STORAGE_HP8200e=y +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +CONFIG_USB_RTL8150=y +CONFIG_USB_USBNET=y + +# +# USB Host-to-Host Cables +# +CONFIG_USB_ALI_M5632=y +CONFIG_USB_AN2720=y +CONFIG_USB_BELKIN=y +CONFIG_USB_GENESYS=y +CONFIG_USB_NET1080=y +CONFIG_USB_PL2301=y + +# +# Intelligent USB Devices/Gadgets +# +CONFIG_USB_ARMLINUX=y +CONFIG_USB_EPSON2888=y +CONFIG_USB_ZAURUS=y +CONFIG_USB_CDCETHER=y + +# +# USB Network Adapters +# +CONFIG_USB_AX8817X=y + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETSERVO is not set +# CONFIG_USB_TEST is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# Synchronous Serial Interfaces (SSI) +# +# CONFIG_OMAP_UWIRE is not set + +# +# MMC/SD Card support +# +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BROKEN_RFD=y +CONFIG_MMC_OMAP=y +# CONFIG_MMC_OMAP16XX_BLOCK1 is not set +CONFIG_MMC_OMAP16XX_BLOCK2=y + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_WAITQ is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set diff --git a/arch/arm/configs/omap_generic_1710_defconfig b/arch/arm/configs/omap_generic_1710_defconfig new file mode 100644 index 00000000000..579cff1b056 --- /dev/null +++ b/arch/arm/configs/omap_generic_1710_defconfig @@ -0,0 +1,853 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.9-rc3-omap1 +# Mon Oct 4 10:14:57 2004 +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_IOMAP=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +# CONFIG_TINY_SHMEM is not set + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# System Type +# +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_VERSATILE_PB is not set +# CONFIG_ARCH_IMX is not set + +# +# TI OMAP Implementations +# + +# +# OMAP Core Type +# +# CONFIG_ARCH_OMAP730 is not set +# CONFIG_ARCH_OMAP15XX is not set +CONFIG_ARCH_OMAP16XX=y +CONFIG_ARCH_OMAP_OTG=y + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP_INNOVATOR is not set +# CONFIG_MACH_OMAP_H2 is not set +# CONFIG_MACH_OMAP_H3 is not set +# CONFIG_MACH_OMAP_H4 is not set +# CONFIG_MACH_OMAP_OSK is not set +CONFIG_MACH_OMAP_GENERIC=y + +# +# OMAP Feature Selections +# +CONFIG_OMAP_BOOT_TAG=y +# CONFIG_OMAP_BOOT_REASON is not set +# CONFIG_OMAP_MUX is not set +# CONFIG_OMAP_LL_DEBUG_UART1 is not set +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +CONFIG_OMAP_LL_DEBUG_UART3=y +CONFIG_OMAP_ARM_192MHZ=y +# CONFIG_OMAP_ARM_168MHZ is not set +# CONFIG_OMAP_ARM_120MHZ is not set +# CONFIG_OMAP_ARM_60MHZ is not set +# CONFIG_OMAP_ARM_30MHZ is not set +# CONFIG_OMAP_DSP is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y + +# +# Processor Features +# +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set + +# +# General setup +# +# CONFIG_ZBOOT_ROM is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 + +# +# PCMCIA/CardBus support +# +# CONFIG_PCMCIA is not set + +# +# At least one math emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +# CONFIG_VFP is not set +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set +CONFIG_PM=y +# CONFIG_PREEMPT is not set +# CONFIG_APM is not set +CONFIG_ARTHUR=y +CONFIG_CMDLINE="mem=64M console=tty0 console=ttyS2,115200 root=0801" +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +# CONFIG_NETLINK_DEV is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_NET_VENDOR_SMC=y +# CONFIG_SMC91X is not set +# CONFIG_SMC9194 is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y + +# +# Obsolete Wireless cards support (pre-802.11) +# +# CONFIG_STRIP is not set +# CONFIG_ATMEL is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPPOE is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# SCSI device support +# +CONFIG_SCSI=y +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set +# CONFIG_SERIO_RAW is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_KEYBOARD_OMAP=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_8250_OMAP=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_OMAP_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_DRM is not set +# CONFIG_RAW_DRIVER is not set + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V4=y +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +# CONFIG_ROOT_NFS is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +# CONFIG_EXPORTFS is not set +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_EFI_PARTITION is not set + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +CONFIG_NLS_CODEPAGE_852=y +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +CONFIG_NLS_ISO8859_15=y +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# Misc devices +# + +# +# USB support +# +CONFIG_USB=y +CONFIG_USB_DEBUG=y + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_BANDWIDTH=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_EHCI_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_UHCI_HCD is not set +# CONFIG_USB_SL811HS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_RW_DETECT is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +CONFIG_USB_USBNET=y + +# +# USB Host-to-Host Cables +# +CONFIG_USB_ALI_M5632=y +# CONFIG_USB_AN2720 is not set +# CONFIG_USB_BELKIN is not set +# CONFIG_USB_GENESYS is not set +# CONFIG_USB_NET1080 is not set +# CONFIG_USB_PL2301 is not set + +# +# Intelligent USB Devices/Gadgets +# +# CONFIG_USB_ARMLINUX is not set +# CONFIG_USB_EPSON2888 is not set +# CONFIG_USB_ZAURUS is not set +# CONFIG_USB_CDCETHER is not set + +# +# USB Network Adapters +# +CONFIG_USB_AX8817X=y + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETSERVO is not set +# CONFIG_USB_TEST is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# Synchronous Serial Interfaces (SSI) +# +# CONFIG_OMAP_UWIRE is not set + +# +# MMC/SD Card support +# +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_BLOCK=y +# CONFIG_MMC_BLOCK_BROKEN_RFD is not set +CONFIG_MMC_OMAP=y +# CONFIG_MMC_OMAP16XX_BLOCK1 is not set +CONFIG_MMC_OMAP16XX_BLOCK2=y + +# +# Kernel hacking +# +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_SLAB=y +CONFIG_DEBUG_SPINLOCK=y +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_USER is not set +CONFIG_DEBUG_WAITQ=y +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +CONFIG_SECURITY=y +# CONFIG_SECURITY_NETWORK is not set +CONFIG_SECURITY_CAPABILITIES=y +# CONFIG_SECURITY_ROOTPLUG is not set +# CONFIG_SECURITY_SELINUX is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WP512 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set diff --git a/arch/arm/configs/omap_generic_2420_defconfig b/arch/arm/configs/omap_generic_2420_defconfig new file mode 100644 index 00000000000..94d465901e8 --- /dev/null +++ b/arch/arm/configs/omap_generic_2420_defconfig @@ -0,0 +1,555 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.13-rc6-omap1 +# Thu Aug 11 10:05:10 2005 +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_CALIBRATE_DELAY=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_KMOD is not set + +# +# System Type +# +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_AAEC2000 is not set + +# +# TI OMAP Implementations +# +# CONFIG_ARCH_OMAP1 is not set +CONFIG_ARCH_OMAP2=y + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_RESET_CLOCKS is not set +CONFIG_OMAP_BOOT_TAG=y +# CONFIG_OMAP_BOOT_REASON is not set +# CONFIG_OMAP_GPIO_SWITCH is not set +# CONFIG_OMAP_MUX is not set +CONFIG_OMAP_MPU_TIMER=y +# CONFIG_OMAP_32K_TIMER is not set +# CONFIG_OMAP_LL_DEBUG_UART1 is not set +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +CONFIG_OMAP_LL_DEBUG_UART3=y +CONFIG_MACH_OMAP_GENERIC=y + +# +# OMAP Core Type +# +CONFIG_ARCH_OMAP24XX=y +CONFIG_ARCH_OMAP2420=y + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP_H4 is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_V6=y +CONFIG_CPU_32v6=y +CONFIG_CPU_ABRT_EV6=y +CONFIG_CPU_CACHE_V6=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V6=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y + +# +# Bus support +# +CONFIG_ISA_DMA_API=y + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +# CONFIG_SMP is not set +# CONFIG_PREEMPT is not set +# CONFIG_NO_IDLE_HZ is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +CONFIG_LEDS=y +# CONFIG_LEDS_TIMER is not set +# CONFIG_LEDS_CPU is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x10C08000 +CONFIG_ZBOOT_ROM_BSS=0x10200000 +# CONFIG_ZBOOT_ROM is not set +CONFIG_CMDLINE="" +# CONFIG_XIP_KERNEL is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set + +# +# Power management options +# +# CONFIG_PM is not set + +# +# Networking +# +# CONFIG_NET is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Network device support +# +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# + +# +# I2C support +# +# CONFIG_I2C is not set +# CONFIG_I2C_SENSOR is not set + +# +# Hardware Monitoring support +# +CONFIG_HWMON=y +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# + +# +# Graphics support +# +# CONFIG_FB is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# +# Synchronous Serial Interfaces (SSI) +# +# CONFIG_OMAP_TSC2101 is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_SECURITY is not set +# CONFIG_EXT2_FS_XIP is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set + +# +# XFS support +# +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +CONFIG_ROMFS_FS=y +CONFIG_INOTIFY=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +# CONFIG_DEVPTS_FS_XATTR is not set +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +# CONFIG_NLS is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_MAGIC_SYSRQ is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_SCHEDSTATS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_FS is not set +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_WAITQ is not set +CONFIG_DEBUG_ERRORS=y +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +CONFIG_CRC_CCITT=y +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set diff --git a/arch/arm/configs/omap_h2_1610_defconfig b/arch/arm/configs/omap_h2_1610_defconfig index ee3ecbd9002..097f210e10c 100644 --- a/arch/arm/configs/omap_h2_1610_defconfig +++ b/arch/arm/configs/omap_h2_1610_defconfig @@ -1,7 +1,7 @@ # # Automatically generated make config: don't edit -# Linux kernel version: 2.6.14 -# Wed Nov 9 18:53:40 2005 +# Linux kernel version: 2.6.15-rc1-omap1 +# Mon Nov 14 16:08:57 2005 # CONFIG_ARM=y CONFIG_MMU=y @@ -15,7 +15,6 @@ CONFIG_GENERIC_CALIBRATE_DELAY=y CONFIG_EXPERIMENTAL=y CONFIG_CLEAN_COMPILE=y CONFIG_BROKEN_ON_SMP=y -CONFIG_LOCK_KERNEL=y CONFIG_INIT_ENV_ARG_LIMIT=32 # @@ -35,6 +34,7 @@ CONFIG_KOBJECT_UEVENT=y CONFIG_INITRAMFS_SOURCE="" # CONFIG_EMBEDDED is not set CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set # CONFIG_KALLSYMS_EXTRA_PASS is not set CONFIG_PRINTK=y CONFIG_BUG=y @@ -115,12 +115,14 @@ CONFIG_ARCH_OMAP1=y # OMAP Feature Selections # # CONFIG_OMAP_RESET_CLOCKS is not set +CONFIG_OMAP_BOOT_TAG=y +# CONFIG_OMAP_BOOT_REASON is not set +# CONFIG_OMAP_GPIO_SWITCH is not set CONFIG_OMAP_MUX=y -# CONFIG_OMAP_MUX_DEBUG is not set +CONFIG_OMAP_MUX_DEBUG=y CONFIG_OMAP_MUX_WARNINGS=y -# CONFIG_OMAP_MPU_TIMER is not set -CONFIG_OMAP_32K_TIMER=y -CONFIG_OMAP_32K_TIMER_HZ=128 +CONFIG_OMAP_MPU_TIMER=y +# CONFIG_OMAP_32K_TIMER is not set # CONFIG_OMAP_DM_TIMER is not set CONFIG_OMAP_LL_DEBUG_UART1=y # CONFIG_OMAP_LL_DEBUG_UART2 is not set @@ -148,11 +150,12 @@ CONFIG_MACH_OMAP_H2=y # # CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER is not set # CONFIG_OMAP_ARM_216MHZ is not set -CONFIG_OMAP_ARM_192MHZ=y +# CONFIG_OMAP_ARM_192MHZ is not set # CONFIG_OMAP_ARM_168MHZ is not set # CONFIG_OMAP_ARM_120MHZ is not set -# CONFIG_OMAP_ARM_60MHZ is not set +CONFIG_OMAP_ARM_60MHZ=y # CONFIG_OMAP_ARM_30MHZ is not set +# CONFIG_OMAP_DSP is not set # # Processor Type @@ -187,8 +190,8 @@ CONFIG_ISA_DMA_API=y # # Kernel Features # -CONFIG_PREEMPT=y -CONFIG_NO_IDLE_HZ=y +# CONFIG_PREEMPT is not set +# CONFIG_NO_IDLE_HZ is not set # CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set CONFIG_SELECT_MEMORY_MODEL=y CONFIG_FLATMEM_MANUAL=y @@ -198,7 +201,9 @@ CONFIG_FLATMEM=y CONFIG_FLAT_NODE_MEM_MAP=y # CONFIG_SPARSEMEM_STATIC is not set CONFIG_SPLIT_PTLOCK_CPUS=4096 -# CONFIG_LEDS is not set +CONFIG_LEDS=y +# CONFIG_LEDS_TIMER is not set +# CONFIG_LEDS_CPU is not set CONFIG_ALIGNMENT_TRAP=y # @@ -206,24 +211,13 @@ CONFIG_ALIGNMENT_TRAP=y # CONFIG_ZBOOT_ROM_TEXT=0x0 CONFIG_ZBOOT_ROM_BSS=0x0 -CONFIG_CMDLINE="mem=32M console=ttyS0,115200n8 root=0801 ro init=/bin/sh" +CONFIG_CMDLINE="mem=32M console=ttyS0,115200n8 root=/dev/ram0 rw initrd=0x10600000,8M ramdisk_size=8192" # CONFIG_XIP_KERNEL is not set # # CPU Frequency scaling # -CONFIG_CPU_FREQ=y -CONFIG_CPU_FREQ_TABLE=y -# CONFIG_CPU_FREQ_DEBUG is not set -CONFIG_CPU_FREQ_STAT=y -# CONFIG_CPU_FREQ_STAT_DETAILS is not set -# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set -CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y -# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set -# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set -CONFIG_CPU_FREQ_GOV_USERSPACE=y -# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set -# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +# CONFIG_CPU_FREQ is not set # # Floating point emulation @@ -248,8 +242,7 @@ CONFIG_BINFMT_AOUT=y # # Power management options # -CONFIG_PM=y -# CONFIG_APM is not set +# CONFIG_PM is not set # # Networking @@ -333,11 +326,96 @@ CONFIG_TCP_CONG_BIC=y CONFIG_STANDALONE=y CONFIG_PREVENT_FIRMWARE_BUILD=y # CONFIG_FW_LOADER is not set +CONFIG_DEBUG_DRIVER=y + +# +# Connector - unified userspace <-> kernelspace linker +# +# CONFIG_CONNECTOR is not set # # Memory Technology Devices (MTD) # -# CONFIG_MTD is not set +CONFIG_MTD=y +CONFIG_MTD_DEBUG=y +CONFIG_MTD_DEBUG_VERBOSE=3 +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_XIP is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +# CONFIG_MTD_OMAP_NOR is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# OneNAND Flash Device Drivers +# +# CONFIG_MTD_ONENAND is not set +# CONFIG_MTD_ONENAND_SYNC_READ is not set # # Parallel port support @@ -355,51 +433,19 @@ CONFIG_PREVENT_FIRMWARE_BUILD=y CONFIG_BLK_DEV_LOOP=y # CONFIG_BLK_DEV_CRYPTOLOOP is not set # CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set CONFIG_BLK_DEV_RAM=y CONFIG_BLK_DEV_RAM_COUNT=16 CONFIG_BLK_DEV_RAM_SIZE=8192 CONFIG_BLK_DEV_INITRD=y # CONFIG_CDROM_PKTCDVD is not set -CONFIG_ATA_OVER_ETH=m +# CONFIG_ATA_OVER_ETH is not set # # SCSI device support # # CONFIG_RAID_ATTRS is not set -CONFIG_SCSI=y -CONFIG_SCSI_PROC_FS=y - -# -# SCSI support type (disk, tape, CD-ROM) -# -# CONFIG_BLK_DEV_SD is not set -# CONFIG_CHR_DEV_ST is not set -# CONFIG_CHR_DEV_OSST is not set -# CONFIG_BLK_DEV_SR is not set -# CONFIG_CHR_DEV_SG is not set -# CONFIG_CHR_DEV_SCH is not set - -# -# Some SCSI devices (e.g. CD jukebox) support multiple LUNs -# -# CONFIG_SCSI_MULTI_LUN is not set -# CONFIG_SCSI_CONSTANTS is not set -# CONFIG_SCSI_LOGGING is not set - -# -# SCSI Transport Attributes -# -# CONFIG_SCSI_SPI_ATTRS is not set -# CONFIG_SCSI_FC_ATTRS is not set -# CONFIG_SCSI_ISCSI_ATTRS is not set -# CONFIG_SCSI_SAS_ATTRS is not set - -# -# SCSI low-level drivers -# -# CONFIG_ISCSI_TCP is not set -# CONFIG_SCSI_SATA is not set -# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI is not set # # Multi-device support (RAID and LVM) @@ -559,8 +605,16 @@ CONFIG_WATCHDOG_NOWAYOUT=y # Watchdog Device Drivers # # CONFIG_SOFT_WATCHDOG is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +# CONFIG_OMAP_WATCHDOG is not set +# CONFIG_OMAP_RNG is not set # CONFIG_NVRAM is not set # CONFIG_RTC is not set +CONFIG_OMAP_RTC=y # CONFIG_DTLK is not set # CONFIG_R3964 is not set @@ -577,13 +631,82 @@ CONFIG_WATCHDOG_NOWAYOUT=y # # I2C support # -# CONFIG_I2C is not set +CONFIG_I2C=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_PCA_ISA is not set +CONFIG_I2C_OMAP=y + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 is not set +CONFIG_ISP1301_OMAP=y +CONFIG_TPS65010=y +# CONFIG_SENSORS_TLV320AIC23 is not set +# CONFIG_GPIOEXPANDER_OMAP is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_RTC_X1205_I2C is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set # # Hardware Monitoring support # CONFIG_HWMON=y # CONFIG_HWMON_VID is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_FSCPOS is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set # CONFIG_HWMON_DEBUG_CHIP is not set # @@ -615,6 +738,10 @@ CONFIG_FB=y CONFIG_FB_MODE_HELPERS=y # CONFIG_FB_TILEBLITTING is not set # CONFIG_FB_S1D13XXX is not set +CONFIG_FB_OMAP=y +CONFIG_FB_OMAP_LCDC_INTERNAL=y +# CONFIG_FB_OMAP_LCDC_EXTERNAL is not set +# CONFIG_FB_OMAP_DMA_TUNE is not set # CONFIG_FB_VIRTUAL is not set # @@ -624,18 +751,9 @@ CONFIG_FB_MODE_HELPERS=y CONFIG_DUMMY_CONSOLE=y CONFIG_FRAMEBUFFER_CONSOLE=y # CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set -CONFIG_FONTS=y +# CONFIG_FONTS is not set CONFIG_FONT_8x8=y CONFIG_FONT_8x16=y -# CONFIG_FONT_6x11 is not set -# CONFIG_FONT_7x14 is not set -# CONFIG_FONT_PEARL_8x8 is not set -# CONFIG_FONT_ACORN_8x8 is not set -# CONFIG_FONT_MINI_4x6 is not set -# CONFIG_FONT_SUN8x16 is not set -# CONFIG_FONT_SUN12x22 is not set -# CONFIG_FONT_10x18 is not set -# CONFIG_FONT_RL is not set # # Logo configuration @@ -649,45 +767,145 @@ CONFIG_LOGO_LINUX_CLUT224=y # # Sound # -CONFIG_SOUND=y +# CONFIG_SOUND is not set # -# Advanced Linux Sound Architecture +# USB support # -# CONFIG_SND is not set +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set # -# Open Sound System +# Miscellaneous USB options # -CONFIG_SOUND_PRIME=y -# CONFIG_OBSOLETE_OSS_DRIVER is not set -# CONFIG_SOUND_MSNDCLAS is not set -# CONFIG_SOUND_MSNDPIN is not set -# CONFIG_SOUND_OSS is not set +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +CONFIG_USB_OTG=y +CONFIG_USB_OTG_WHITELIST=y # -# USB support +# USB Host Controller Drivers # -CONFIG_USB_ARCH_HAS_HCD=y -CONFIG_USB_ARCH_HAS_OHCI=y -# CONFIG_USB is not set +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_OHCI_BIG_ENDIAN is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_SL811_HCD is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set # # NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' # +# +# may also be needed; see USB_STORAGE Help for more information +# +# CONFIG_USB_STORAGE is not set + +# +# USB Input Devices +# +# CONFIG_USB_HID is not set + +# +# USB HID Boot Protocol drivers +# +# CONFIG_USB_KBD is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_ACECAD is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_ITMTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_YEALINK is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set +# CONFIG_USB_KEYSPAN_REMOTE is not set +# CONFIG_USB_APPLETOUCH is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +CONFIG_USB_MON=y + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETKIT is not set +# CONFIG_USB_PHIDGETSERVO is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_LD is not set +CONFIG_USB_TEST=y + +# +# USB DSL modem support +# + # # USB Gadget Support # -# CONFIG_USB_GADGET is not set +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_SELECTED=y # CONFIG_USB_GADGET_NET2280 is not set # CONFIG_USB_GADGET_PXA2XX is not set # CONFIG_USB_GADGET_GOKU is not set # CONFIG_USB_GADGET_LH7A40X is not set -# CONFIG_USB_GADGET_OMAP is not set +CONFIG_USB_GADGET_OMAP=y +CONFIG_USB_OMAP=y # CONFIG_USB_GADGET_DUMMY_HCD is not set +# CONFIG_USB_GADGET_DUALSPEED is not set # CONFIG_USB_ZERO is not set -# CONFIG_USB_ETH is not set +CONFIG_USB_ETH=y +CONFIG_USB_ETH_RNDIS=y # CONFIG_USB_GADGETFS is not set # CONFIG_USB_FILE_STORAGE is not set # CONFIG_USB_G_SERIAL is not set @@ -697,6 +915,12 @@ CONFIG_USB_ARCH_HAS_OHCI=y # # CONFIG_MMC is not set +# +# Synchronous Serial Interfaces (SSI) +# +CONFIG_OMAP_UWIRE=y +# CONFIG_OMAP_TSC2101 is not set + # # File systems # @@ -753,6 +977,15 @@ CONFIG_RAMFS=y # CONFIG_BEFS_FS is not set # CONFIG_BFS_FS is not set # CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=2 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set CONFIG_CRAMFS=y # CONFIG_VXFS_FS is not set # CONFIG_HPFS_FS is not set @@ -764,18 +997,15 @@ CONFIG_CRAMFS=y # Network File Systems # CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -# CONFIG_NFS_V3_ACL is not set -CONFIG_NFS_V4=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set # CONFIG_NFS_DIRECTIO is not set # CONFIG_NFSD is not set CONFIG_ROOT_NFS=y CONFIG_LOCKD=y -CONFIG_LOCKD_V4=y CONFIG_NFS_COMMON=y CONFIG_SUNRPC=y -CONFIG_SUNRPC_GSS=y -CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_KRB5 is not set # CONFIG_RPCSEC_GSS_SPKM3 is not set # CONFIG_SMB_FS is not set # CONFIG_CIFS is not set @@ -843,11 +1073,25 @@ CONFIG_NLS_DEFAULT="iso8859-1" # Kernel hacking # # CONFIG_PRINTK_TIME is not set -# CONFIG_DEBUG_KERNEL is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_MAGIC_SYSRQ is not set CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_FS is not set +# CONFIG_DEBUG_VM is not set CONFIG_FRAME_POINTER=y -# CONFIG_DEBUG_USER is not set +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_WAITQ is not set +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_LL is not set # # Security options @@ -858,31 +1102,7 @@ CONFIG_FRAME_POINTER=y # # Cryptographic options # -CONFIG_CRYPTO=y -# CONFIG_CRYPTO_HMAC is not set -# CONFIG_CRYPTO_NULL is not set -# CONFIG_CRYPTO_MD4 is not set -CONFIG_CRYPTO_MD5=y -# CONFIG_CRYPTO_SHA1 is not set -# CONFIG_CRYPTO_SHA256 is not set -# CONFIG_CRYPTO_SHA512 is not set -# CONFIG_CRYPTO_WP512 is not set -# CONFIG_CRYPTO_TGR192 is not set -CONFIG_CRYPTO_DES=y -# CONFIG_CRYPTO_BLOWFISH is not set -# CONFIG_CRYPTO_TWOFISH is not set -# CONFIG_CRYPTO_SERPENT is not set -# CONFIG_CRYPTO_AES is not set -# CONFIG_CRYPTO_CAST5 is not set -# CONFIG_CRYPTO_CAST6 is not set -# CONFIG_CRYPTO_TEA is not set -# CONFIG_CRYPTO_ARC4 is not set -# CONFIG_CRYPTO_KHAZAD is not set -# CONFIG_CRYPTO_ANUBIS is not set -# CONFIG_CRYPTO_DEFLATE is not set -# CONFIG_CRYPTO_MICHAEL_MIC is not set -# CONFIG_CRYPTO_CRC32C is not set -# CONFIG_CRYPTO_TEST is not set +# CONFIG_CRYPTO is not set # # Hardware crypto devices @@ -896,3 +1116,4 @@ CONFIG_CRYPTO_DES=y CONFIG_CRC32=y # CONFIG_LIBCRC32C is not set CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y diff --git a/arch/arm/configs/omap_h3_1710_defconfig b/arch/arm/configs/omap_h3_1710_defconfig new file mode 100644 index 00000000000..bc9cb6b0f23 --- /dev/null +++ b/arch/arm/configs/omap_h3_1710_defconfig @@ -0,0 +1,1048 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.14-rc4-omap1 +# Tue Oct 18 17:58:27 2005 +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_CALIBRATE_DELAY=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +# CONFIG_HOTPLUG is not set +CONFIG_KOBJECT_UEVENT=y +# CONFIG_IKCONFIG is not set +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +# CONFIG_KMOD is not set + +# +# System Type +# +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_AAEC2000 is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP_OTG=y +CONFIG_ARCH_OMAP1=y +# CONFIG_ARCH_OMAP2 is not set + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_RESET_CLOCKS is not set +# CONFIG_OMAP_BOOT_TAG is not set +CONFIG_OMAP_MUX=y +# CONFIG_OMAP_MUX_DEBUG is not set +CONFIG_OMAP_MUX_WARNINGS=y +CONFIG_OMAP_MPU_TIMER=y +# CONFIG_OMAP_32K_TIMER is not set +# CONFIG_OMAP_DM_TIMER is not set +CONFIG_OMAP_LL_DEBUG_UART1=y +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +# CONFIG_OMAP_LL_DEBUG_UART3 is not set +CONFIG_OMAP_SERIAL_WAKE=y + +# +# OMAP Core Type +# +# CONFIG_ARCH_OMAP730 is not set +# CONFIG_ARCH_OMAP15XX is not set +CONFIG_ARCH_OMAP16XX=y + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP_INNOVATOR is not set +# CONFIG_MACH_OMAP_H2 is not set +CONFIG_MACH_OMAP_H3=y +# CONFIG_MACH_OMAP_OSK is not set +# CONFIG_MACH_OMAP_GENERIC is not set + +# +# OMAP CPU Speed +# +# CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER is not set +# CONFIG_OMAP_ARM_216MHZ is not set +# CONFIG_OMAP_ARM_192MHZ is not set +CONFIG_OMAP_ARM_168MHZ=y +# CONFIG_OMAP_ARM_120MHZ is not set +# CONFIG_OMAP_ARM_60MHZ is not set +# CONFIG_OMAP_ARM_30MHZ is not set +# CONFIG_OMAP_DSP is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set + +# +# Bus support +# +CONFIG_ISA_DMA_API=y + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +# CONFIG_PREEMPT is not set +# CONFIG_NO_IDLE_HZ is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +CONFIG_LEDS=y +# CONFIG_LEDS_TIMER is not set +# CONFIG_LEDS_CPU is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x10C08000 +CONFIG_ZBOOT_ROM_BSS=0x10200000 +# CONFIG_ZBOOT_ROM is not set +CONFIG_CMDLINE="mem=32M console=ttyS0,115200n8 initrd=0x10A00000,8M root=/dev/ram0 rw ip=dhcp devfs=mount" +# CONFIG_XIP_KERNEL is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_AOUT=y +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set + +# +# Power management options +# +# CONFIG_PM is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_BIC=y +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# DCCP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +CONFIG_IRDA=y + +# +# IrDA protocols +# +# CONFIG_IRLAN is not set +# CONFIG_IRNET is not set +# CONFIG_IRCOMM is not set +# CONFIG_IRDA_ULTRA is not set + +# +# IrDA options +# +# CONFIG_IRDA_CACHE_LAST_LSAP is not set +# CONFIG_IRDA_FAST_RR is not set +# CONFIG_IRDA_DEBUG is not set + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +# CONFIG_IRTTY_SIR is not set + +# +# Dongle support +# + +# +# Old SIR device drivers +# +# CONFIG_IRPORT_SIR is not set + +# +# Old Serial dongle support +# + +# +# FIR device drivers +# +# CONFIG_USB_IRDA is not set +# CONFIG_SIGMATEL_FIR is not set +# CONFIG_NSC_FIR is not set +# CONFIG_WINBOND_FIR is not set +CONFIG_OMAP1610_IR=y +# CONFIG_SMC_IRCC_FIR is not set +# CONFIG_ALI_FIR is not set +# CONFIG_VIA_FIR is not set +# CONFIG_BT is not set +# CONFIG_IEEE80211 is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=4096 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_ATA_OVER_ETH is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# PHY device support +# +# CONFIG_PHYLIB is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +CONFIG_PPP=y +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPPOE is not set +CONFIG_SLIP=y +CONFIG_SLIP_COMPRESSED=y +# CONFIG_SLIP_SMART is not set +# CONFIG_SLIP_MODE_SLIP6 is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_KEYBOARD_OMAP=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +# CONFIG_OMAP_WATCHDOG is not set +# CONFIG_OMAP_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +CONFIG_OMAP_RTC=y +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_PCA_ISA is not set +CONFIG_I2C_OMAP=y + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 is not set +CONFIG_ISP1301_OMAP=m +CONFIG_TPS65010=y +# CONFIG_SENSORS_TLV320AIC23 is not set +CONFIG_GPIOEXPANDER_OMAP=y +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# Hardware Monitoring support +# +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_FSCPOS is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Misc devices +# + +# +# Multimedia Capabilities Port drivers +# + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=y + +# +# Video For Linux +# + +# +# Video Adapters +# +# CONFIG_VIDEO_CPIA is not set +# CONFIG_VIDEO_SAA5246A is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_TUNER_3036 is not set +# CONFIG_VIDEO_OVCAMCHIP is not set +# CONFIG_VIDEO_OMAP_CAMERA is not set + +# +# Radio Adapters +# +# CONFIG_RADIO_MAESTRO is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +CONFIG_FB=y +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +CONFIG_FB_SOFT_CURSOR=y +# CONFIG_FB_MACMODES is not set +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_OMAP=y +CONFIG_FB_OMAP_LCDC_INTERNAL=y +# CONFIG_FB_OMAP_LCDC_EXTERNAL is not set +# CONFIG_FB_OMAP_DMA_TUNE is not set +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y + +# +# Logo configuration +# +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +# CONFIG_SND is not set + +# +# Open Sound System +# +CONFIG_SOUND_PRIME=y +# CONFIG_SOUND_OMAP is not set +# CONFIG_SOUND_MSNDCLAS is not set +# CONFIG_SOUND_MSNDPIN is not set +# CONFIG_SOUND_OSS is not set +# CONFIG_SOUND_TVMIXER is not set +# CONFIG_SOUND_AD1980 is not set + +# +# USB support +# +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB=m +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_OHCI_HCD=m +# CONFIG_USB_OHCI_BIG_ENDIAN is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_SL811_HCD is not set + +# +# USB Device Class drivers +# +# CONFIG_OBSOLETE_OSS_USB_DRIVER is not set +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# +# CONFIG_USB_STORAGE is not set + +# +# USB Input Devices +# +# CONFIG_USB_HID is not set + +# +# USB HID Boot Protocol drivers +# +# CONFIG_USB_KBD is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_ACECAD is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_ITMTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_YEALINK is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set +# CONFIG_USB_KEYSPAN_REMOTE is not set +# CONFIG_USB_APPLETOUCH is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set +# CONFIG_USB_VICAM is not set +# CONFIG_USB_DSBR is not set +# CONFIG_USB_IBMCAM is not set +# CONFIG_USB_KONICAWC is not set +# CONFIG_USB_OV511 is not set +# CONFIG_USB_SE401 is not set +# CONFIG_USB_SN9C102 is not set +# CONFIG_USB_STV680 is not set +# CONFIG_USB_PWC is not set + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET is not set +CONFIG_USB_MON=y + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETKIT is not set +# CONFIG_USB_PHIDGETSERVO is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_LD is not set + +# +# USB DSL modem support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_BLOCK=y +# CONFIG_MMC_BLOCK_BROKEN_RFD is not set +# CONFIG_MMC_BULKTRANSFER is not set +CONFIG_MMC_OMAP=y +# CONFIG_MMC_WBSD is not set + +# +# Synchronous Serial Interfaces (SSI) +# +CONFIG_OMAP_UWIRE=y +# CONFIG_OMAP_TSC2101 is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_SECURITY is not set +# CONFIG_EXT2_FS_XIP is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +CONFIG_ROMFS_FS=y +CONFIG_INOTIFY=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_RELAYFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_9P_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +# CONFIG_NLS is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_MAGIC_SYSRQ is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_FS is not set +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_WAITQ is not set +CONFIG_DEBUG_ERRORS=y +# CONFIG_DEBUG_LL is not set +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +CONFIG_CRC_CCITT=y +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set diff --git a/arch/arm/configs/omap_h4_2420_defconfig b/arch/arm/configs/omap_h4_2420_defconfig new file mode 100644 index 00000000000..518d1ea375e --- /dev/null +++ b/arch/arm/configs/omap_h4_2420_defconfig @@ -0,0 +1,1049 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.16-rc4-omap1 +# Wed Feb 22 02:25:55 2006 +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_CALIBRATE_DELAY=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_UID16=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_EMBEDDED=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_KALLSYMS_EXTRA_PASS=y +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +CONFIG_SLAB=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_SLOB is not set +CONFIG_OBSOLETE_INTERMODULE=y + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +CONFIG_MODVERSIONS=y +CONFIG_MODULE_SRCVERSION_ALL=y +CONFIG_KMOD=y + +# +# Block layer +# + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" + +# +# System Type +# +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_AT91RM9200 is not set + +# +# TI OMAP Implementations +# +# CONFIG_ARCH_OMAP1 is not set +CONFIG_ARCH_OMAP2=y + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_RESET_CLOCKS is not set +CONFIG_OMAP_BOOT_TAG=y +# CONFIG_OMAP_BOOT_REASON is not set +# CONFIG_OMAP_COMPONENT_VERSION is not set +# CONFIG_OMAP_GPIO_SWITCH is not set +CONFIG_OMAP_MUX=y +CONFIG_OMAP_MUX_DEBUG=y +CONFIG_OMAP_MUX_WARNINGS=y +# CONFIG_OMAP_STI is not set +CONFIG_OMAP_MPU_TIMER=y +# CONFIG_OMAP_32K_TIMER is not set +CONFIG_OMAP_LL_DEBUG_UART1=y +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +# CONFIG_OMAP_LL_DEBUG_UART3 is not set +CONFIG_OMAP_SERIAL_WAKE=y +CONFIG_MACH_OMAP_GENERIC=y + +# +# OMAP Core Type +# +CONFIG_ARCH_OMAP24XX=y +CONFIG_ARCH_OMAP2420=y + +# +# OMAP Board Type +# +CONFIG_MACH_OMAP_H4=y +# CONFIG_MACH_OMAP_APOLLON is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_V6=y +# CONFIG_CPU_32v6K is not set +CONFIG_CPU_32v6=y +CONFIG_CPU_ABRT_EV6=y +CONFIG_CPU_CACHE_V6=y +CONFIG_CPU_CACHE_VIPT=y +CONFIG_CPU_COPY_V6=y +CONFIG_CPU_TLB_V6=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_BPREDICT_DISABLE is not set + +# +# Bus support +# + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +# CONFIG_PREEMPT is not set +# CONFIG_NO_IDLE_HZ is not set +# CONFIG_AEABI is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +CONFIG_SPLIT_PTLOCK_CPUS=4 +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="root=/dev/ram0 rw console=ttyS0,115200n8 initrd=0x80600000,8M ramdisk_size=8192" +# CONFIG_XIP_KERNEL is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +CONFIG_BINFMT_MISC=y +# CONFIG_ARTHUR is not set + +# +# Power management options +# +# CONFIG_PM is not set +# CONFIG_APM is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_NETDEBUG is not set +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +CONFIG_NET_KEY=y +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +# CONFIG_IP_PNP_BOOTP is not set +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_BIC=y +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# DCCP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set + +# +# TIPC Configuration (EXPERIMENTAL) +# +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +CONFIG_IRDA=y + +# +# IrDA protocols +# +CONFIG_IRLAN=y +CONFIG_IRCOMM=y +# CONFIG_IRDA_ULTRA is not set + +# +# IrDA options +# +# CONFIG_IRDA_CACHE_LAST_LSAP is not set +# CONFIG_IRDA_FAST_RR is not set +# CONFIG_IRDA_DEBUG is not set + +# +# Infrared-port device drivers +# + +# +# SIR device drivers +# +# CONFIG_IRTTY_SIR is not set + +# +# Dongle support +# + +# +# Old SIR device drivers +# +# CONFIG_IRPORT_SIR is not set + +# +# Old Serial dongle support +# + +# +# FIR device drivers +# +CONFIG_OMAP_IR=y +# CONFIG_BT is not set +# CONFIG_IEEE80211 is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set + +# +# Connector - unified userspace <-> kernelspace linker +# +# CONFIG_CONNECTOR is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +CONFIG_MTD_OMAP_NOR=y +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# OneNAND Flash Device Drivers +# +# CONFIG_MTD_ONENAND is not set +# CONFIG_MTD_ONENAND_SYNC_READ is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=16384 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# PHY device support +# +# CONFIG_PHYLIB is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_KEYBOARD_OMAP=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=32 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +CONFIG_SERIAL_8250_EXTENDED=y +CONFIG_SERIAL_8250_MANY_PORTS=y +CONFIG_SERIAL_8250_SHARE_IRQ=y +CONFIG_SERIAL_8250_DETECT_IRQ=y +CONFIG_SERIAL_8250_RSA=y + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set +CONFIG_OMAP_WATCHDOG=y +# CONFIG_OMAP_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# +# CONFIG_TCG_TPM is not set +# CONFIG_TELCLOCK is not set + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_PCA_ISA is not set +CONFIG_I2C_OMAP=y + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 is not set +# CONFIG_TPS65010 is not set +# CONFIG_SENSORS_TLV320AIC23 is not set +CONFIG_GPIOEXPANDER_OMAP=y +CONFIG_MENELAUS=y +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_RTC_X1205_I2C is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set + +# +# Hardware Monitoring support +# +# CONFIG_HWMON is not set +# CONFIG_HWMON_VID is not set + +# +# Misc devices +# + +# +# Multimedia Capabilities Port drivers +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +CONFIG_FB=y +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_OMAP=y +# CONFIG_FB_OMAP_LCDC_EXTERNAL is not set +# CONFIG_FB_OMAP_LCD_LPH8923 is not set +# CONFIG_FB_OMAP_BOOTLOADER_INIT is not set +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y + +# +# Logo configuration +# +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BROKEN_RFD=y +CONFIG_MMC_BULKTRANSFER=y +CONFIG_MMC_OMAP=y + +# +# Synchronous Serial Interfaces (SSI) +# +# CONFIG_OMAP_TSC2101 is not set + +# +# CBUS support +# +# CONFIG_CBUS is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_FS_XATTR is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_QUOTA=y +# CONFIG_QFMT_V1 is not set +CONFIG_QFMT_V2=y +CONFIG_QUOTACTL=y +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_RELAYFS_FS is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_SUMMARY is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_9P_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_MAGIC_SYSRQ=y +CONFIG_DEBUG_KERNEL=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_DEBUG_SLAB is not set +CONFIG_DEBUG_MUTEXES=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_DEBUG_VM is not set +CONFIG_FRAME_POINTER=y +CONFIG_FORCED_INLINING=y +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_WAITQ is not set +# CONFIG_DEBUG_ERRORS is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_TGR192 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +CONFIG_CRC_CCITT=y +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y diff --git a/arch/arm/configs/omap_innovator_1510_defconfig b/arch/arm/configs/omap_innovator_1510_defconfig new file mode 100644 index 00000000000..28c087ec921 --- /dev/null +++ b/arch/arm/configs/omap_innovator_1510_defconfig @@ -0,0 +1,1082 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.14-rc4-omap1 +# Tue Oct 18 17:55:19 2005 +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_CALIBRATE_DELAY=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +# CONFIG_HOTPLUG is not set +CONFIG_KOBJECT_UEVENT=y +# CONFIG_IKCONFIG is not set +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y + +# +# System Type +# +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_AAEC2000 is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP1=y +# CONFIG_ARCH_OMAP2 is not set + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_RESET_CLOCKS is not set +# CONFIG_OMAP_BOOT_TAG is not set +CONFIG_OMAP_MUX=y +# CONFIG_OMAP_MUX_DEBUG is not set +CONFIG_OMAP_MUX_WARNINGS=y +CONFIG_OMAP_MPU_TIMER=y +# CONFIG_OMAP_32K_TIMER is not set +CONFIG_OMAP_LL_DEBUG_UART1=y +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +# CONFIG_OMAP_LL_DEBUG_UART3 is not set +CONFIG_OMAP_SERIAL_WAKE=y + +# +# OMAP Core Type +# +# CONFIG_ARCH_OMAP730 is not set +CONFIG_ARCH_OMAP15XX=y +# CONFIG_ARCH_OMAP16XX is not set + +# +# OMAP Board Type +# +CONFIG_MACH_OMAP_INNOVATOR=y +# CONFIG_MACH_VOICEBLUE is not set +# CONFIG_MACH_NETSTAR is not set +# CONFIG_MACH_OMAP_PALMTE is not set +# CONFIG_MACH_OMAP_GENERIC is not set + +# +# OMAP CPU Speed +# +# CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER is not set +CONFIG_OMAP_ARM_168MHZ=y +# CONFIG_OMAP_ARM_150MHZ is not set +# CONFIG_OMAP_ARM_120MHZ is not set +# CONFIG_OMAP_ARM_60MHZ is not set +# CONFIG_OMAP_ARM_30MHZ is not set +# CONFIG_OMAP_DSP is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM925T=y +CONFIG_CPU_32v4=y +CONFIG_CPU_ABRT_EV4T=y +CONFIG_CPU_CACHE_V4WT=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +CONFIG_CPU_DCACHE_WRITETHROUGH=y + +# +# Bus support +# +CONFIG_ISA_DMA_API=y + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_PREEMPT=y +# CONFIG_NO_IDLE_HZ is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +CONFIG_LEDS=y +# CONFIG_LEDS_TIMER is not set +# CONFIG_LEDS_CPU is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="console=ttyS0,115200n8 root=/dev/nfs ip=bootp noinitrd" +# CONFIG_XIP_KERNEL is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_APM is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_BIC=y +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# DCCP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_IEEE80211 is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_DEBUG_DRIVER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_ATA_OVER_ETH is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=y +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=y +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# PHY device support +# +# CONFIG_PHYLIB is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +CONFIG_PPP=y +CONFIG_PPP_MULTILINK=y +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=y +# CONFIG_PPP_SYNC_TTY is not set +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +# CONFIG_PPPOE is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=240 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=320 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_KEYBOARD_OMAP=y +CONFIG_OMAP_PS2=m +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +CONFIG_OMAP_RTC=y +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# + +# +# I2C support +# +CONFIG_I2C=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_PCA_ISA is not set +# CONFIG_I2C_OMAP is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 is not set +# CONFIG_TPS65010 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# Hardware Monitoring support +# +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_FSCPOS is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Misc devices +# + +# +# Multimedia Capabilities Port drivers +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +CONFIG_FB=y +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +CONFIG_FB_SOFT_CURSOR=y +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_OMAP=y +CONFIG_FB_OMAP_LCDC_INTERNAL=y +# CONFIG_FB_OMAP_LCDC_EXTERNAL is not set +# CONFIG_FB_OMAP_DMA_TUNE is not set +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set + +# +# Logo configuration +# +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +CONFIG_USB=y +CONFIG_USB_DEBUG=y + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +CONFIG_USB_SUSPEND=y +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_OHCI_BIG_ENDIAN is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_SL811_HCD is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' may also be needed; see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_DPCM=y +# CONFIG_USB_STORAGE_USBAT is not set +CONFIG_USB_STORAGE_SDDR09=y +CONFIG_USB_STORAGE_SDDR55=y +CONFIG_USB_STORAGE_JUMPSHOT=y + +# +# USB Input Devices +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_ACECAD is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_ITMTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_YEALINK is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set +# CONFIG_USB_KEYSPAN_REMOTE is not set +# CONFIG_USB_APPLETOUCH is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +CONFIG_USB_RTL8150=y +CONFIG_USB_USBNET=y +CONFIG_USB_NET_AX8817X=y +CONFIG_USB_NET_CDCETHER=y +# CONFIG_USB_NET_GL620A is not set +CONFIG_USB_NET_NET1080=y +# CONFIG_USB_NET_PLUSB is not set +# CONFIG_USB_NET_RNDIS_HOST is not set +# CONFIG_USB_NET_CDC_SUBSET is not set +CONFIG_USB_NET_ZAURUS=y +CONFIG_USB_MON=y + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGETKIT is not set +# CONFIG_USB_PHIDGETSERVO is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TEST is not set + +# +# USB DSL modem support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BROKEN_RFD=y +# CONFIG_MMC_BULKTRANSFER is not set +CONFIG_MMC_OMAP=y +# CONFIG_MMC_WBSD is not set + +# +# Synchronous Serial Interfaces (SSI) +# +# CONFIG_OMAP_UWIRE is not set +# CONFIG_OMAP_TSC2101 is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +CONFIG_AUTOFS_FS=y +CONFIG_AUTOFS4_FS=y +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +CONFIG_ISO9660_FS=y +CONFIG_JOLIET=y +# CONFIG_ZISOFS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_RELAYFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +CONFIG_NFS_V4=y +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +CONFIG_SUNRPC_GSS=y +CONFIG_RPCSEC_GSS_KRB5=y +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_9P_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_EFI_PARTITION is not set + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +# CONFIG_NLS_ISO8859_1 is not set +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_DEBUG_KERNEL=y +CONFIG_MAGIC_SYSRQ=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_DEBUG_SLAB is not set +CONFIG_DEBUG_PREEMPT=y +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_FS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_USER is not set +# CONFIG_DEBUG_WAITQ is not set +# CONFIG_DEBUG_ERRORS is not set +# CONFIG_DEBUG_LL is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +CONFIG_CRYPTO_MD5=y +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_TGR192 is not set +CONFIG_CRYPTO_DES=y +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_TEST is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +CONFIG_CRC_CCITT=y +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y diff --git a/arch/arm/configs/omap_innovator_1610_defconfig b/arch/arm/configs/omap_innovator_1610_defconfig new file mode 100644 index 00000000000..7baf3bcf46b --- /dev/null +++ b/arch/arm/configs/omap_innovator_1610_defconfig @@ -0,0 +1,750 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.14-rc4-omap1 +# Tue Oct 18 17:55:48 2005 +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_CALIBRATE_DELAY=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +# CONFIG_HOTPLUG is not set +CONFIG_KOBJECT_UEVENT=y +# CONFIG_IKCONFIG is not set +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y + +# +# System Type +# +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_AAEC2000 is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP_OTG=y +CONFIG_ARCH_OMAP1=y +# CONFIG_ARCH_OMAP2 is not set + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_RESET_CLOCKS is not set +# CONFIG_OMAP_BOOT_TAG is not set +CONFIG_OMAP_MUX=y +# CONFIG_OMAP_MUX_DEBUG is not set +CONFIG_OMAP_MUX_WARNINGS=y +CONFIG_OMAP_MPU_TIMER=y +# CONFIG_OMAP_32K_TIMER is not set +# CONFIG_OMAP_DM_TIMER is not set +CONFIG_OMAP_LL_DEBUG_UART1=y +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +# CONFIG_OMAP_LL_DEBUG_UART3 is not set +CONFIG_OMAP_SERIAL_WAKE=y + +# +# OMAP Core Type +# +# CONFIG_ARCH_OMAP730 is not set +# CONFIG_ARCH_OMAP15XX is not set +CONFIG_ARCH_OMAP16XX=y + +# +# OMAP Board Type +# +CONFIG_MACH_OMAP_INNOVATOR=y +# CONFIG_MACH_OMAP_H2 is not set +# CONFIG_MACH_OMAP_H3 is not set +# CONFIG_MACH_OMAP_OSK is not set +# CONFIG_MACH_OMAP_GENERIC is not set + +# +# OMAP CPU Speed +# +# CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER is not set +# CONFIG_OMAP_ARM_216MHZ is not set +CONFIG_OMAP_ARM_192MHZ=y +# CONFIG_OMAP_ARM_168MHZ is not set +# CONFIG_OMAP_ARM_120MHZ is not set +# CONFIG_OMAP_ARM_60MHZ is not set +# CONFIG_OMAP_ARM_30MHZ is not set +# CONFIG_OMAP_DSP is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y + +# +# Processor Features +# +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +CONFIG_CPU_DCACHE_WRITETHROUGH=y +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set + +# +# Bus support +# +CONFIG_ISA_DMA_API=y + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +# CONFIG_PREEMPT is not set +# CONFIG_NO_IDLE_HZ is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=32M console=tty0 console=ttyS0,115200 initrd=0x10200000,8M root=/dev/ram0 rw" +# CONFIG_XIP_KERNEL is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set + +# +# Power management options +# +# CONFIG_PM is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=m +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_BIC=y +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# DCCP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_IEEE80211 is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_ATA_OVER_ETH is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# PHY device support +# +# CONFIG_PHYLIB is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +CONFIG_PPP=y +CONFIG_PPP_MULTILINK=y +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPPOE is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_KEYBOARD_OMAP=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_OMAP_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_OMAP_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Hardware Monitoring support +# +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Misc devices +# + +# +# Multimedia Capabilities Port drivers +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +CONFIG_FB=y +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +CONFIG_FB_SOFT_CURSOR=y +# CONFIG_FB_MACMODES is not set +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_OMAP=y +CONFIG_FB_OMAP_LCDC_INTERNAL=y +# CONFIG_FB_OMAP_LCDC_EXTERNAL is not set +# CONFIG_FB_OMAP_DMA_TUNE is not set +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set + +# +# Logo configuration +# +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# +# Synchronous Serial Interfaces (SSI) +# +# CONFIG_OMAP_UWIRE is not set +# CONFIG_OMAP_TSC2101 is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +CONFIG_AUTOFS_FS=y +CONFIG_AUTOFS4_FS=y +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_RELAYFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +# CONFIG_ROOT_NFS is not set +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_9P_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +# CONFIG_NLS is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +# CONFIG_DEBUG_KERNEL is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_USER is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set diff --git a/arch/arm/configs/omap_osk_5912_defconfig b/arch/arm/configs/omap_osk_5912_defconfig new file mode 100644 index 00000000000..d190420599d --- /dev/null +++ b/arch/arm/configs/omap_osk_5912_defconfig @@ -0,0 +1,995 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.14-rc4-omap1 +# Tue Oct 18 17:56:19 2005 +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_CALIBRATE_DELAY=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_HOTPLUG=y +CONFIG_KOBJECT_UEVENT=y +# CONFIG_IKCONFIG is not set +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y + +# +# System Type +# +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_AAEC2000 is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP_OTG=y +CONFIG_ARCH_OMAP1=y +# CONFIG_ARCH_OMAP2 is not set + +# +# OMAP Feature Selections +# +CONFIG_OMAP_RESET_CLOCKS=y +# CONFIG_OMAP_BOOT_TAG is not set +CONFIG_OMAP_MUX=y +# CONFIG_OMAP_MUX_DEBUG is not set +CONFIG_OMAP_MUX_WARNINGS=y +# CONFIG_OMAP_MPU_TIMER is not set +CONFIG_OMAP_32K_TIMER=y +CONFIG_OMAP_32K_TIMER_HZ=128 +# CONFIG_OMAP_DM_TIMER is not set +CONFIG_OMAP_LL_DEBUG_UART1=y +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +# CONFIG_OMAP_LL_DEBUG_UART3 is not set +CONFIG_OMAP_SERIAL_WAKE=y + +# +# OMAP Core Type +# +# CONFIG_ARCH_OMAP730 is not set +# CONFIG_ARCH_OMAP15XX is not set +CONFIG_ARCH_OMAP16XX=y + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP_INNOVATOR is not set +# CONFIG_MACH_OMAP_H2 is not set +# CONFIG_MACH_OMAP_H3 is not set +CONFIG_MACH_OMAP_OSK=y +# CONFIG_OMAP_OSK_MISTRAL is not set +# CONFIG_MACH_OMAP_GENERIC is not set + +# +# OMAP CPU Speed +# +# CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER is not set +# CONFIG_OMAP_ARM_216MHZ is not set +CONFIG_OMAP_ARM_192MHZ=y +# CONFIG_OMAP_ARM_168MHZ is not set +# CONFIG_OMAP_ARM_120MHZ is not set +# CONFIG_OMAP_ARM_60MHZ is not set +# CONFIG_OMAP_ARM_30MHZ is not set +# CONFIG_OMAP_DSP is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y + +# +# Processor Features +# +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set + +# +# Bus support +# +CONFIG_ISA_DMA_API=y + +# +# PCCARD (PCMCIA/CardBus) support +# +CONFIG_PCCARD=y +# CONFIG_PCMCIA_DEBUG is not set +CONFIG_PCMCIA=y +CONFIG_PCMCIA_LOAD_CIS=y +CONFIG_PCMCIA_IOCTL=y + +# +# PC-card bridges +# +CONFIG_OMAP_CF=y + +# +# Kernel Features +# +# CONFIG_PREEMPT is not set +CONFIG_NO_IDLE_HZ=y +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=32M console=ttyS0,115200 initrd=0x10400000,8M root=/dev/ram0 rw" +# CONFIG_XIP_KERNEL is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_APM is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=m +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_BIC=y +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# DCCP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_IEEE80211 is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_XIP is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +# CONFIG_MTD_EDB7312 is not set +CONFIG_MTD_OMAP_NOR=y +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_ATA_OVER_ETH is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=m +CONFIG_BLK_DEV_IDE=m + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=m +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=m +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_IDE_TASK_IOCTL is not set + +# +# IDE chipset support/bugfixes +# +# CONFIG_IDE_GENERIC is not set +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# PHY device support +# +# CONFIG_PHYLIB is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# PCMCIA network device support +# +# CONFIG_NET_PCMCIA is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +CONFIG_PPP=y +CONFIG_PPP_MULTILINK=y +# CONFIG_PPP_FILTER is not set +# CONFIG_PPP_ASYNC is not set +# CONFIG_PPP_SYNC_TTY is not set +# CONFIG_PPP_DEFLATE is not set +# CONFIG_PPP_BSDCOMP is not set +# CONFIG_PPPOE is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_KEYBOARD_OMAP=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +CONFIG_TOUCHSCREEN_OMAP=y +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_CS is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_OMAP_RNG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_OMAP_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y + +# +# I2C Algorithms +# +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_PCA_ISA is not set +CONFIG_I2C_OMAP=y + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 is not set +# CONFIG_ISP1301_OMAP is not set +CONFIG_TPS65010=y +# CONFIG_SENSORS_TLV320AIC23 is not set +# CONFIG_GPIOEXPANDER_OMAP is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# Hardware Monitoring support +# +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ADM1025 is not set +# CONFIG_SENSORS_ADM1026 is not set +# CONFIG_SENSORS_ADM1031 is not set +# CONFIG_SENSORS_ADM9240 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_ATXP1 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_FSCPOS is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_GL520SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM63 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM77 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM87 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_LM92 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_PC87360 is not set +# CONFIG_SENSORS_SMSC47M1 is not set +# CONFIG_SENSORS_SMSC47B397 is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83792D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set +# CONFIG_SENSORS_W83627EHF is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Misc devices +# + +# +# Multimedia Capabilities Port drivers +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +CONFIG_FB=y +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +CONFIG_FB_SOFT_CURSOR=y +# CONFIG_FB_MACMODES is not set +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_OMAP=y +CONFIG_FB_OMAP_LCDC_INTERNAL=y +# CONFIG_FB_OMAP_LCDC_EXTERNAL is not set +# CONFIG_FB_OMAP_DMA_TUNE is not set +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +# CONFIG_FONT_8x16 is not set +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set + +# +# Logo configuration +# +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# +# Synchronous Serial Interfaces (SSI) +# +CONFIG_OMAP_UWIRE=y +CONFIG_OMAP_TSC2101=y + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +CONFIG_AUTOFS_FS=y +CONFIG_AUTOFS4_FS=y +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=m +CONFIG_MSDOS_FS=m +CONFIG_VFAT_FS=m +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_RELAYFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_9P_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=m +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=m +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=m +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +# CONFIG_DEBUG_KERNEL is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_USER is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y diff --git a/arch/arm/configs/omap_perseus2_730_defconfig b/arch/arm/configs/omap_perseus2_730_defconfig new file mode 100644 index 00000000000..7e08ad3dadc --- /dev/null +++ b/arch/arm/configs/omap_perseus2_730_defconfig @@ -0,0 +1,811 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.14-rc4-omap1 +# Tue Oct 18 17:57:11 2005 +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_CALIBRATE_DELAY=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_CLEAN_COMPILE=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +# CONFIG_HOTPLUG is not set +CONFIG_KOBJECT_UEVENT=y +# CONFIG_IKCONFIG is not set +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y + +# +# System Type +# +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_AAEC2000 is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP_OTG=y +CONFIG_ARCH_OMAP1=y +# CONFIG_ARCH_OMAP2 is not set + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_RESET_CLOCKS is not set +# CONFIG_OMAP_BOOT_TAG is not set +CONFIG_OMAP_MUX=y +# CONFIG_OMAP_MUX_DEBUG is not set +CONFIG_OMAP_MUX_WARNINGS=y +CONFIG_OMAP_MPU_TIMER=y +# CONFIG_OMAP_32K_TIMER is not set +CONFIG_OMAP_LL_DEBUG_UART1=y +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +# CONFIG_OMAP_LL_DEBUG_UART3 is not set +CONFIG_OMAP_SERIAL_WAKE=y + +# +# OMAP Core Type +# +CONFIG_ARCH_OMAP730=y +# CONFIG_ARCH_OMAP15XX is not set +# CONFIG_ARCH_OMAP16XX is not set + +# +# OMAP Board Type +# +CONFIG_MACH_OMAP_PERSEUS2=y + +# +# OMAP CPU Speed +# +# CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER is not set +# CONFIG_OMAP_ARM_195MHZ is not set +CONFIG_OMAP_ARM_182MHZ=y +# CONFIG_OMAP_ARM_168MHZ is not set +# CONFIG_OMAP_ARM_120MHZ is not set +# CONFIG_OMAP_ARM_60MHZ is not set +# CONFIG_OMAP_ARM_30MHZ is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y + +# +# Processor Features +# +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set + +# +# Bus support +# +CONFIG_ISA_DMA_API=y + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_PREEMPT=y +# CONFIG_NO_IDLE_HZ is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +CONFIG_LEDS=y +CONFIG_LEDS_TIMER=y +CONFIG_LEDS_CPU=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=32M console=ttyS0,115200 ip=dhcp" +# CONFIG_XIP_KERNEL is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set +# CONFIG_VFP is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_APM is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_IP_MROUTE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_BIC=y +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# DCCP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set +# CONFIG_NET_CLS_ROUTE is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_IEEE80211 is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_XIP is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +# CONFIG_MTD_EDB7312 is not set +CONFIG_MTD_OMAP_NOR=y +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set + +# +# NAND Flash Device Drivers +# +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +CONFIG_MTD_NAND_OMAP=y +# CONFIG_MTD_NAND_TOTO is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_NANDSIM is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +# CONFIG_ATA_OVER_ETH is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# PHY device support +# +# CONFIG_PHYLIB is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +CONFIG_SMC91X=y +# CONFIG_DM9000 is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_KEYBOARD_OMAP=y +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +CONFIG_SERIAL_8250_NR_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +CONFIG_OMAP_RTC=y +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# + +# +# I2C support +# +# CONFIG_I2C is not set + +# +# Hardware Monitoring support +# +CONFIG_HWMON=y +# CONFIG_HWMON_VID is not set +# CONFIG_HWMON_DEBUG_CHIP is not set + +# +# Misc devices +# + +# +# Multimedia Capabilities Port drivers +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +CONFIG_FB_SOFT_CURSOR=y +# CONFIG_FB_MACMODES is not set +CONFIG_FB_MODE_HELPERS=y +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_OMAP=y +CONFIG_FB_OMAP_LCDC_INTERNAL=y +# CONFIG_FB_OMAP_LCDC_EXTERNAL is not set +# CONFIG_FB_OMAP_DMA_TUNE is not set +CONFIG_FB_VIRTUAL=y + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE is not set + +# +# Logo configuration +# +# CONFIG_LOGO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# +# Synchronous Serial Interfaces (SSI) +# +CONFIG_OMAP_UWIRE=y +# CONFIG_OMAP_TSC2101 is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +# CONFIG_TMPFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_RELAYFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +# CONFIG_SMB_FS is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set +# CONFIG_9P_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +# CONFIG_NLS is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +# CONFIG_DEBUG_KERNEL is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_USER is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y diff --git a/arch/arm/configs/palmte_defconfig b/arch/arm/configs/palmte_defconfig new file mode 100644 index 00000000000..26a8999e366 --- /dev/null +++ b/arch/arm/configs/palmte_defconfig @@ -0,0 +1,648 @@ +# +# Palm Tungsten E default kernel configuration +# Created by Romain Goyet +# Linux kernel version: 2.6.13-rc6-omap1 +# Fri Aug 26 16:01:18 2005 +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_CALIBRATE_DELAY=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_CLEAN_COMPILE is not set +CONFIG_BROKEN=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +CONFIG_BSD_PROCESS_ACCT=y +# CONFIG_BSD_PROCESS_ACCT_V3 is not set +CONFIG_SYSCTL=y +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SHMEM=y +CONFIG_CC_ALIGN_FUNCTIONS=0 +CONFIG_CC_ALIGN_LABELS=0 +CONFIG_CC_ALIGN_LOOPS=0 +CONFIG_CC_ALIGN_JUMPS=0 +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# System Type +# +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_PXA is not set +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +CONFIG_ARCH_OMAP=y +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_AAEC2000 is not set + +# +# TI OMAP Implementations +# +CONFIG_ARCH_OMAP1=y +# CONFIG_ARCH_OMAP2 is not set + +# +# OMAP Feature Selections +# +# CONFIG_OMAP_RESET_CLOCKS is not set +# CONFIG_OMAP_BOOT_TAG is not set +CONFIG_OMAP_MUX=y +# CONFIG_OMAP_MUX_DEBUG is not set +CONFIG_OMAP_MUX_WARNINGS=y +CONFIG_OMAP_MPU_TIMER=y +# CONFIG_OMAP_32K_TIMER is not set +CONFIG_OMAP_LL_DEBUG_UART1=y +# CONFIG_OMAP_LL_DEBUG_UART2 is not set +# CONFIG_OMAP_LL_DEBUG_UART3 is not set +CONFIG_OMAP_SERIAL_WAKE=y + +# +# OMAP Core Type +# +# CONFIG_ARCH_OMAP730 is not set +CONFIG_ARCH_OMAP15XX=y +# CONFIG_ARCH_OMAP16XX is not set + +# +# OMAP Board Type +# +# CONFIG_MACH_OMAP_INNOVATOR is not set +# CONFIG_MACH_VOICEBLUE is not set +# CONFIG_MACH_NETSTAR is not set +CONFIG_MACH_OMAP_PALMTE=y +# CONFIG_MACH_OMAP_GENERIC is not set + +# +# OMAP CPU Speed +# +CONFIG_OMAP_CLOCKS_SET_BY_BOOTLOADER=y +# CONFIG_OMAP_ARM_168MHZ is not set +# CONFIG_OMAP_ARM_150MHZ is not set +# CONFIG_OMAP_ARM_120MHZ is not set +# CONFIG_OMAP_ARM_60MHZ is not set +# CONFIG_OMAP_ARM_30MHZ is not set +# CONFIG_OMAP_DSP is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM925T=y +CONFIG_CPU_32v4=y +CONFIG_CPU_ABRT_EV4T=y +CONFIG_CPU_CACHE_V4WT=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=y +CONFIG_CPU_TLB_V4WBI=y + +# +# Processor Features +# +# CONFIG_ARM_THUMB is not set +# CONFIG_CPU_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set + +# +# Bus support +# +CONFIG_ISA_DMA_API=y + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +# CONFIG_SMP is not set +# CONFIG_PREEMPT is not set +# CONFIG_NO_IDLE_HZ is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="" +# CONFIG_XIP_KERNEL is not set + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set + +# +# Power management options +# +# CONFIG_PM is not set + +# +# Networking +# +# CONFIG_NET is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +# CONFIG_STANDALONE is not set +# CONFIG_PREVENT_FIRMWARE_BUILD is not set +# CONFIG_FW_LOADER is not set + +# +# Memory Technology Devices (MTD) +# +# CONFIG_MTD is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_RAM is not set +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_INITRAMFS_SOURCE="" +# CONFIG_CDROM_PKTCDVD is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set + +# +# SCSI device support +# +# CONFIG_SCSI is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# + +# +# Network device support +# +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set + +# +# ISDN subsystem +# + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=320 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=320 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +CONFIG_OMAP_RTC=y +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# + +# +# I2C support +# +# CONFIG_I2C is not set +# CONFIG_I2C_SENSOR is not set + +# +# Hardware Monitoring support +# +# CONFIG_HWMON is not set + +# +# Misc devices +# + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# + +# +# Graphics support +# +CONFIG_FB=y +# CONFIG_FB_CFB_FILLRECT is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +CONFIG_FB_SOFT_CURSOR=y +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_OMAP=y +CONFIG_FB_OMAP_INTERNAL_LCDC=y +# CONFIG_FB_OMAP_EXTERNAL_LCDC is not set +# CONFIG_FB_OMAP_DMA_TUNE is not set +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y + +# +# Logo configuration +# +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# USB support +# +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB is not set + +# +# USB Gadget Support +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_PXA2XX is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LH7A40X is not set +CONFIG_USB_GADGET_OMAP=y +CONFIG_USB_OMAP=y +# CONFIG_USB_GADGET_DUMMY_HCD is not set +# CONFIG_USB_GADGET_DUALSPEED is not set +CONFIG_USB_ZERO=y +# CONFIG_USB_ETH is not set +# CONFIG_USB_GADGETFS is not set +# CONFIG_USB_FILE_STORAGE is not set +# CONFIG_USB_G_SERIAL is not set + +# +# MMC/SD Card support +# +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_BLOCK=y +# CONFIG_MMC_BLOCK_BROKEN_RFD is not set +# CONFIG_MMC_BULKTRANSFER is not set +CONFIG_MMC_OMAP=y +# CONFIG_MMC_WBSD is not set + +# +# Synchronous Serial Interfaces (SSI) +# +# CONFIG_OMAP_UWIRE is not set +# CONFIG_OMAP_TSC2101 is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +# CONFIG_EXT3_FS is not set +# CONFIG_JBD is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set + +# +# XFS support +# +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +# CONFIG_INOTIFY is not set +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=850 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +# CONFIG_DEVPTS_FS_XATTR is not set +CONFIG_TMPFS=y +# CONFIG_TMPFS_XATTR is not set +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_CRAMFS=y +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_EFI_PARTITION is not set + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +CONFIG_NLS_CODEPAGE_850=y +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +# CONFIG_DEBUG_KERNEL is not set +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_FRAME_POINTER=y +# CONFIG_DEBUG_USER is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Hardware crypto devices +# + +# +# Library routines +# +CONFIG_CRC_CCITT=y +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y diff --git a/arch/arm/kernel/vmlinux.lds.S b/arch/arm/kernel/vmlinux.lds.S index 2b254e88595..76064b58042 100644 --- a/arch/arm/kernel/vmlinux.lds.S +++ b/arch/arm/kernel/vmlinux.lds.S @@ -169,11 +169,3 @@ SECTIONS .stab.indexstr 0 : { *(.stab.indexstr) } .comment 0 : { *(.comment) } } - -/* - * These must never be empty - * If you have to comment these two assert statements out, your - * binutils is too old (for other reasons as well) - */ -ASSERT((__proc_info_end - __proc_info_begin), "missing CPU support") -ASSERT((__arch_info_end - __arch_info_begin), "no machine record defined") diff --git a/arch/arm/mach-omap1/Kconfig b/arch/arm/mach-omap1/Kconfig index 86a0f0d1434..0cad7be2c43 100644 --- a/arch/arm/mach-omap1/Kconfig +++ b/arch/arm/mach-omap1/Kconfig @@ -36,6 +36,7 @@ config MACH_OMAP_H2 config MACH_OMAP_H3 bool "TI H3 Support" depends on ARCH_OMAP1 && ARCH_OMAP16XX + select GPIOEXPANDER_OMAP help TI OMAP 1710 H3 board support. Say Y here if you have such a board. @@ -69,12 +70,6 @@ config MACH_VOICEBLUE Support for Voiceblue GSM/VoIP gateway. Say Y here if you have such a board. -config MACH_NETSTAR - bool "NetStar" - depends on ARCH_OMAP1 && ARCH_OMAP15XX - help - Support for NetStar PBX. Say Y here if you have such a board. - config MACH_OMAP_PALMTE bool "Palm Tungsten E" depends on ARCH_OMAP1 && ARCH_OMAP15XX @@ -85,6 +80,13 @@ config MACH_OMAP_PALMTE informations. Say Y here if you have such a PDA, say NO otherwise. +config MACH_NOKIA770 + bool "Nokia 770" + depends on ARCH_OMAP1 && ARCH_OMAP16XX + help + Support for the Nokia 770 Internet Tablet. Say Y here if you + have such a device. + config MACH_OMAP_GENERIC bool "Generic OMAP board" depends on ARCH_OMAP1 && (ARCH_OMAP15XX || ARCH_OMAP16XX) @@ -160,3 +162,5 @@ config OMAP_ARM_30MHZ help Enable 30MHz clock for OMAP CPU. If unsure, say N. +source "arch/arm/plat-omap/dsp/Kconfig" + diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile index b0b00156faa..5e0e023af7e 100644 --- a/arch/arm/mach-omap1/Makefile +++ b/arch/arm/mach-omap1/Makefile @@ -3,7 +3,13 @@ # # Common support -obj-y := io.o id.o clock.o irq.o time.o mux.o serial.o devices.o +obj-y := io.o id.o clock.o irq.o mux.o serial.o devices.o + +obj-$(CONFIG_OMAP_MPU_TIMER) += time.o + +# Power Management +obj-$(CONFIG_PM) += pm.o sleep.o + led-y := leds.o # Specific board support @@ -14,8 +20,8 @@ obj-$(CONFIG_MACH_OMAP_PERSEUS2) += board-perseus2.o obj-$(CONFIG_MACH_OMAP_OSK) += board-osk.o obj-$(CONFIG_MACH_OMAP_H3) += board-h3.o obj-$(CONFIG_MACH_VOICEBLUE) += board-voiceblue.o -obj-$(CONFIG_MACH_NETSTAR) += board-netstar.o obj-$(CONFIG_MACH_OMAP_PALMTE) += board-palmte.o +obj-$(CONFIG_MACH_NOKIA770) += board-nokia770.o ifeq ($(CONFIG_ARCH_OMAP15XX),y) # Innovator-1510 FPGA diff --git a/arch/arm/mach-omap1/board-generic.c b/arch/arm/mach-omap1/board-generic.c index a177e78b2b8..33d01adab1e 100644 --- a/arch/arm/mach-omap1/board-generic.c +++ b/arch/arm/mach-omap1/board-generic.c @@ -88,7 +88,7 @@ static struct omap_board_config_kernel generic_config[] = { static void __init omap_generic_init(void) { #ifdef CONFIG_ARCH_OMAP15XX - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { generic_config[0].data = &generic1510_usb_config; } #endif diff --git a/arch/arm/mach-omap1/board-h2.c b/arch/arm/mach-omap1/board-h2.c index 89f0cc74a51..4364d51ebd6 100644 --- a/arch/arm/mach-omap1/board-h2.c +++ b/arch/arm/mach-omap1/board-h2.c @@ -24,7 +24,9 @@ #include #include #include +#include #include +#include #include #include @@ -35,12 +37,54 @@ #include #include #include +#include #include +#include +#include #include extern int omap_gpio_init(void); -static struct mtd_partition h2_partitions[] = { +static int h2_keymap[] = { + KEY(0, 0, KEY_LEFT), + KEY(0, 1, KEY_RIGHT), + KEY(0, 2, KEY_3), + KEY(0, 3, KEY_F10), + KEY(0, 4, KEY_F5), + KEY(0, 5, KEY_9), + KEY(1, 0, KEY_DOWN), + KEY(1, 1, KEY_UP), + KEY(1, 2, KEY_2), + KEY(1, 3, KEY_F9), + KEY(1, 4, KEY_F7), + KEY(1, 5, KEY_0), + KEY(2, 0, KEY_ENTER), + KEY(2, 1, KEY_6), + KEY(2, 2, KEY_1), + KEY(2, 3, KEY_F2), + KEY(2, 4, KEY_F6), + KEY(2, 5, KEY_HOME), + KEY(3, 0, KEY_8), + KEY(3, 1, KEY_5), + KEY(3, 2, KEY_F12), + KEY(3, 3, KEY_F3), + KEY(3, 4, KEY_F8), + KEY(3, 5, KEY_END), + KEY(4, 0, KEY_7), + KEY(4, 1, KEY_4), + KEY(4, 2, KEY_F11), + KEY(4, 3, KEY_F1), + KEY(4, 4, KEY_F4), + KEY(4, 5, KEY_ESC), + KEY(5, 0, KEY_F13), + KEY(5, 1, KEY_F14), + KEY(5, 2, KEY_F15), + KEY(5, 3, KEY_F16), + KEY(5, 4, KEY_SLEEP), + 0 +}; + +static struct mtd_partition h2_nor_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ { .name = "bootloader", @@ -71,26 +115,83 @@ static struct mtd_partition h2_partitions[] = { } }; -static struct flash_platform_data h2_flash_data = { +static struct flash_platform_data h2_nor_data = { .map_name = "cfi_probe", .width = 2, - .parts = h2_partitions, - .nr_parts = ARRAY_SIZE(h2_partitions), + .parts = h2_nor_partitions, + .nr_parts = ARRAY_SIZE(h2_nor_partitions), }; -static struct resource h2_flash_resource = { +static struct resource h2_nor_resource = { /* This is on CS3, wherever it's mapped */ .flags = IORESOURCE_MEM, }; -static struct platform_device h2_flash_device = { +static struct platform_device h2_nor_device = { .name = "omapflash", .id = 0, .dev = { - .platform_data = &h2_flash_data, + .platform_data = &h2_nor_data, }, .num_resources = 1, - .resource = &h2_flash_resource, + .resource = &h2_nor_resource, +}; + +static struct mtd_partition h2_nand_partitions[] = { +#if 0 + /* REVISIT: enable these partitions if you make NAND BOOT + * work on your H2 (rev C or newer); published versions of + * x-load only support P2 and H3. + */ + { + .name = "xloader", + .offset = 0, + .size = 64 * 1024, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "bootloader", + .offset = MTDPART_OFS_APPEND, + .size = 256 * 1024, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = 192 * 1024, + }, + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = 2 * SZ_1M, + }, +#endif + { + .name = "filesystem", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + }, +}; + +/* dip switches control NAND chip access: 8 bit, 16 bit, or neither */ +static struct nand_platform_data h2_nand_data = { + .options = NAND_SAMSUNG_LP_OPTIONS, + .parts = h2_nand_partitions, + .nr_parts = ARRAY_SIZE(h2_nand_partitions), +}; + +static struct resource h2_nand_resource = { + .flags = IORESOURCE_MEM, +}; + +static struct platform_device h2_nand_device = { + .name = "omapnand", + .id = 0, + .dev = { + .platform_data = &h2_nand_data, + }, + .num_resources = 1, + .resource = &h2_nand_resource, }; static struct resource h2_smc91x_resources[] = { @@ -113,9 +214,84 @@ static struct platform_device h2_smc91x_device = { .resource = h2_smc91x_resources, }; +static struct resource h2_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_kp_platform_data h2_kp_data = { + .rows = 8, + .cols = 8, + .keymap = h2_keymap, + .rep = 1, +}; + +static struct platform_device h2_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &h2_kp_data, + }, + .num_resources = ARRAY_SIZE(h2_kp_resources), + .resource = h2_kp_resources, +}; + +#define H2_IRDA_FIRSEL_GPIO_PIN 17 + +#if defined(CONFIG_OMAP_IR) || defined(CONFIG_OMAP_IR_MODULE) +static int h2_transceiver_mode(struct device *dev, int state) +{ + if (state & IR_SIRMODE) + omap_set_gpio_dataout(H2_IRDA_FIRSEL_GPIO_PIN, 0); + else /* MIR/FIR */ + omap_set_gpio_dataout(H2_IRDA_FIRSEL_GPIO_PIN, 1); + + return 0; +} +#endif + +static struct omap_irda_config h2_irda_data = { + .transceiver_cap = IR_SIRMODE | IR_MIRMODE | IR_FIRMODE, + .rx_channel = OMAP_DMA_UART3_RX, + .tx_channel = OMAP_DMA_UART3_TX, + .dest_start = UART3_THR, + .src_start = UART3_RHR, + .tx_trigger = 0, + .rx_trigger = 0, +}; + +static struct resource h2_irda_resources[] = { + [0] = { + .start = INT_UART3, + .end = INT_UART3, + .flags = IORESOURCE_IRQ, + }, +}; +static struct platform_device h2_irda_device = { + .name = "omapirda", + .id = 0, + .dev = { + .platform_data = &h2_irda_data, + }, + .num_resources = ARRAY_SIZE(h2_irda_resources), + .resource = h2_irda_resources, +}; + +static struct platform_device h2_lcd_device = { + .name = "lcd_h2", + .id = -1, +}; + static struct platform_device *h2_devices[] __initdata = { - &h2_flash_device, + &h2_nor_device, + &h2_nand_device, &h2_smc91x_device, + &h2_irda_device, + &h2_kp_device, + &h2_lcd_device, }; static void __init h2_init_smc91x(void) @@ -164,7 +340,6 @@ static struct omap_uart_config h2_uart_config __initdata = { }; static struct omap_lcd_config h2_lcd_config __initdata = { - .panel_name = "h2", .ctrl_name = "internal", }; @@ -175,18 +350,48 @@ static struct omap_board_config_kernel h2_config[] = { { OMAP_TAG_LCD, &h2_lcd_config }, }; +#define H2_NAND_RB_GPIO_PIN 62 + +static int h2_nand_dev_ready(struct nand_platform_data *data) +{ + return omap_get_gpio_datain(H2_NAND_RB_GPIO_PIN); +} + static void __init h2_init(void) { - /* NOTE: revC boards support NAND-boot, which can put NOR on CS2B - * and NAND (either 16bit or 8bit) on CS3. + /* Here we assume the NOR boot config: NOR on CS3 (possibly swapped + * to address 0 by a dip switch), NAND on CS2B. The NAND driver will + * notice whether a NAND chip is enabled at probe time. + * + * FIXME revC boards (and H3) support NAND-boot, with a dip switch to + * put NOR on CS2B and NAND (which on H2 may be 16bit) on CS3. Try + * detecting that in code here, to avoid probing every possible flash + * configuration... */ - h2_flash_resource.end = h2_flash_resource.start = omap_cs3_phys(); - h2_flash_resource.end += SZ_32M - 1; + h2_nor_resource.end = h2_nor_resource.start = omap_cs3_phys(); + h2_nor_resource.end += SZ_32M - 1; + + h2_nand_resource.end = h2_nand_resource.start = OMAP_CS2B_PHYS; + h2_nand_resource.end += SZ_4K - 1; + if (!(omap_request_gpio(H2_NAND_RB_GPIO_PIN))) + h2_nand_data.dev_ready = h2_nand_dev_ready; + + omap_cfg_reg(L3_1610_FLASH_CS2B_OE); + omap_cfg_reg(M8_1610_FLASH_CS2B_WE); /* MMC: card detect and WP */ // omap_cfg_reg(U19_ARMIO1); /* CD */ omap_cfg_reg(BALLOUT_V8_ARMIO3); /* WP */ + /* Irda */ +#if defined(CONFIG_OMAP_IR) || defined(CONFIG_OMAP_IR_MODULE) + omap_writel(omap_readl(FUNC_MUX_CTRL_A) | 7, FUNC_MUX_CTRL_A); + if (!(omap_request_gpio(H2_IRDA_FIRSEL_GPIO_PIN))) { + omap_set_gpio_direction(H2_IRDA_FIRSEL_GPIO_PIN, 0); + h2_irda_data.transceiver_mode = h2_transceiver_mode; + } +#endif + platform_add_devices(h2_devices, ARRAY_SIZE(h2_devices)); omap_board_config = h2_config; omap_board_config_size = ARRAY_SIZE(h2_config); diff --git a/arch/arm/mach-omap1/board-h3.c b/arch/arm/mach-omap1/board-h3.c index d9f38626599..4b8d0ec73cb 100644 --- a/arch/arm/mach-omap1/board-h3.c +++ b/arch/arm/mach-omap1/board-h3.c @@ -21,8 +21,11 @@ #include #include #include +#include #include +#include #include +#include #include #include @@ -33,15 +36,59 @@ #include #include +#include #include #include #include +#include #include +#include +#include #include extern int omap_gpio_init(void); -static struct mtd_partition h3_partitions[] = { +static int h3_keymap[] = { + KEY(0, 0, KEY_LEFT), + KEY(0, 1, KEY_RIGHT), + KEY(0, 2, KEY_3), + KEY(0, 3, KEY_F10), + KEY(0, 4, KEY_F5), + KEY(0, 5, KEY_9), + KEY(1, 0, KEY_DOWN), + KEY(1, 1, KEY_UP), + KEY(1, 2, KEY_2), + KEY(1, 3, KEY_F9), + KEY(1, 4, KEY_F7), + KEY(1, 5, KEY_0), + KEY(2, 0, KEY_ENTER), + KEY(2, 1, KEY_6), + KEY(2, 2, KEY_1), + KEY(2, 3, KEY_F2), + KEY(2, 4, KEY_F6), + KEY(2, 5, KEY_HOME), + KEY(3, 0, KEY_8), + KEY(3, 1, KEY_5), + KEY(3, 2, KEY_F12), + KEY(3, 3, KEY_F3), + KEY(3, 4, KEY_F8), + KEY(3, 5, KEY_END), + KEY(4, 0, KEY_7), + KEY(4, 1, KEY_4), + KEY(4, 2, KEY_F11), + KEY(4, 3, KEY_F1), + KEY(4, 4, KEY_F4), + KEY(4, 5, KEY_ESC), + KEY(5, 0, KEY_F13), + KEY(5, 1, KEY_F14), + KEY(5, 2, KEY_F15), + KEY(5, 3, KEY_F16), + KEY(5, 4, KEY_SLEEP), + 0 +}; + + +static struct mtd_partition nor_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ { .name = "bootloader", @@ -72,26 +119,80 @@ static struct mtd_partition h3_partitions[] = { } }; -static struct flash_platform_data h3_flash_data = { +static struct flash_platform_data nor_data = { .map_name = "cfi_probe", .width = 2, - .parts = h3_partitions, - .nr_parts = ARRAY_SIZE(h3_partitions), + .parts = nor_partitions, + .nr_parts = ARRAY_SIZE(nor_partitions), }; -static struct resource h3_flash_resource = { +static struct resource nor_resource = { /* This is on CS3, wherever it's mapped */ .flags = IORESOURCE_MEM, }; -static struct platform_device flash_device = { +static struct platform_device nor_device = { .name = "omapflash", .id = 0, .dev = { - .platform_data = &h3_flash_data, + .platform_data = &nor_data, + }, + .num_resources = 1, + .resource = &nor_resource, +}; + +static struct mtd_partition nand_partitions[] = { +#if 0 + /* REVISIT: enable these partitions if you make NAND BOOT work */ + { + .name = "xloader", + .offset = 0, + .size = 64 * 1024, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "bootloader", + .offset = MTDPART_OFS_APPEND, + .size = 256 * 1024, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = 192 * 1024, + }, + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = 2 * SZ_1M, + }, +#endif + { + .name = "filesystem", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + }, +}; + +/* dip switches control NAND chip access: 8 bit, 16 bit, or neither */ +static struct nand_platform_data nand_data = { + .options = NAND_SAMSUNG_LP_OPTIONS, + .parts = nand_partitions, + .nr_parts = ARRAY_SIZE(nand_partitions), +}; + +static struct resource nand_resource = { + .flags = IORESOURCE_MEM, +}; + +static struct platform_device nand_device = { + .name = "omapnand", + .id = 0, + .dev = { + .platform_data = &nand_data, }, .num_resources = 1, - .resource = &h3_flash_resource, + .resource = &nand_resource, }; static struct resource smc91x_resources[] = { @@ -138,10 +239,136 @@ static struct platform_device intlat_device = { .resource = intlat_resources, }; +static struct resource h3_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_kp_platform_data h3_kp_data = { + .rows = 8, + .cols = 8, + .keymap = h3_keymap, + .rep = 1, +}; + +static struct platform_device h3_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &h3_kp_data, + }, + .num_resources = ARRAY_SIZE(h3_kp_resources), + .resource = h3_kp_resources, +}; + + +/* Select between the IrDA and aGPS module + */ +static int h3_select_irda(struct device *dev, int state) +{ + unsigned char expa; + int err = 0; + + if ((err = read_gpio_expa(&expa, 0x26))) { + printk(KERN_ERR "Error reading from I/O EXPANDER \n"); + return err; + } + + /* 'P6' enable/disable IRDA_TX and IRDA_RX */ + if (state & IR_SEL) { /* IrDA */ + if ((err = write_gpio_expa(expa | 0x40, 0x26))) { + printk(KERN_ERR "Error writing to I/O EXPANDER \n"); + return err; + } + } else { + if ((err = write_gpio_expa(expa & ~0x40, 0x26))) { + printk(KERN_ERR "Error writing to I/O EXPANDER \n"); + return err; + } + } + return err; +} + +static void set_trans_mode(void *data) +{ + int *mode = data; + unsigned char expa; + int err = 0; + + if ((err = read_gpio_expa(&expa, 0x27)) != 0) { + printk(KERN_ERR "Error reading from I/O expander\n"); + } + + expa &= ~0x03; + + if (*mode & IR_SIRMODE) { + expa |= 0x01; + } else { /* MIR/FIR */ + expa |= 0x03; + } + + if ((err = write_gpio_expa(expa, 0x27)) != 0) { + printk(KERN_ERR "Error writing to I/O expander\n"); + } +} + +static int h3_transceiver_mode(struct device *dev, int mode) +{ + struct omap_irda_config *irda_config = dev->platform_data; + + cancel_delayed_work(&irda_config->gpio_expa); + PREPARE_WORK(&irda_config->gpio_expa, set_trans_mode, &mode); + schedule_work(&irda_config->gpio_expa); + + return 0; +} + +static struct omap_irda_config h3_irda_data = { + .transceiver_cap = IR_SIRMODE | IR_MIRMODE | IR_FIRMODE, + .transceiver_mode = h3_transceiver_mode, + .select_irda = h3_select_irda, + .rx_channel = OMAP_DMA_UART3_RX, + .tx_channel = OMAP_DMA_UART3_TX, + .dest_start = UART3_THR, + .src_start = UART3_RHR, + .tx_trigger = 0, + .rx_trigger = 0, +}; + +static struct resource h3_irda_resources[] = { + [0] = { + .start = INT_UART3, + .end = INT_UART3, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device h3_irda_device = { + .name = "omapirda", + .id = 0, + .dev = { + .platform_data = &h3_irda_data, + }, + .num_resources = ARRAY_SIZE(h3_irda_resources), + .resource = h3_irda_resources, +}; + +static struct platform_device h3_lcd_device = { + .name = "lcd_h3", + .id = -1, +}; + static struct platform_device *devices[] __initdata = { - &flash_device, + &nor_device, + &nand_device, &smc91x_device, &intlat_device, + &h3_irda_device, + &h3_kp_device, + &h3_lcd_device, }; static struct omap_usb_config h3_usb_config __initdata = { @@ -171,7 +398,6 @@ static struct omap_uart_config h3_uart_config __initdata = { }; static struct omap_lcd_config h3_lcd_config __initdata = { - .panel_name = "h3", .ctrl_name = "internal", }; @@ -182,11 +408,36 @@ static struct omap_board_config_kernel h3_config[] = { { OMAP_TAG_LCD, &h3_lcd_config }, }; +#define H3_NAND_RB_GPIO_PIN 10 + +static int nand_dev_ready(struct nand_platform_data *data) +{ + return omap_get_gpio_datain(H3_NAND_RB_GPIO_PIN); +} + static void __init h3_init(void) { - h3_flash_resource.end = h3_flash_resource.start = omap_cs3_phys(); - h3_flash_resource.end += OMAP_CS3_SIZE - 1; - (void) platform_add_devices(devices, ARRAY_SIZE(devices)); + /* Here we assume the NOR boot config: NOR on CS3 (possibly swapped + * to address 0 by a dip switch), NAND on CS2B. The NAND driver will + * notice whether a NAND chip is enabled at probe time. + * + * H3 support NAND-boot, with a dip switch to put NOR on CS2B and NAND + * (which on H2 may be 16bit) on CS3. Try detecting that in code here, + * to avoid probing every possible flash configuration... + */ + nor_resource.end = nor_resource.start = omap_cs3_phys(); + nor_resource.end += SZ_32M - 1; + + nand_resource.end = nand_resource.start = OMAP_CS2B_PHYS; + nand_resource.end += SZ_4K - 1; + if (!(omap_request_gpio(H3_NAND_RB_GPIO_PIN))) + nand_data.dev_ready = nand_dev_ready; + + /* GPIO10 Func_MUX_CTRL reg bit 29:27, Configure V2 to mode1 as GPIO */ + /* GPIO10 pullup/down register, Enable pullup on GPIO10 */ + omap_cfg_reg(V2_1710_GPIO10); + + platform_add_devices(devices, ARRAY_SIZE(devices)); omap_board_config = h3_config; omap_board_config_size = ARRAY_SIZE(h3_config); omap_serial_init(); diff --git a/arch/arm/mach-omap1/board-innovator.c b/arch/arm/mach-omap1/board-innovator.c index a04e4332915..e90c137a4cf 100644 --- a/arch/arm/mach-omap1/board-innovator.c +++ b/arch/arm/mach-omap1/board-innovator.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -34,8 +35,22 @@ #include #include #include +#include #include +static int innovator_keymap[] = { + KEY(0, 0, KEY_F1), + KEY(0, 3, KEY_DOWN), + KEY(1, 1, KEY_F2), + KEY(1, 2, KEY_RIGHT), + KEY(2, 0, KEY_F3), + KEY(2, 1, KEY_F4), + KEY(2, 2, KEY_UP), + KEY(3, 2, KEY_ENTER), + KEY(3, 3, KEY_LEFT), + 0 +}; + static struct mtd_partition innovator_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ { @@ -97,6 +112,31 @@ static struct platform_device innovator_flash_device = { .resource = &innovator_flash_resource, }; +static struct resource innovator_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_kp_platform_data innovator_kp_data = { + .rows = 8, + .cols = 8, + .keymap = innovator_keymap, +}; + +static struct platform_device innovator_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &innovator_kp_data, + }, + .num_resources = ARRAY_SIZE(innovator_kp_resources), + .resource = innovator_kp_resources, +}; + + #ifdef CONFIG_ARCH_OMAP15XX /* Only FPGA needs to be mapped here. All others are done with ioremap */ @@ -129,9 +169,16 @@ static struct platform_device innovator1510_smc91x_device = { .resource = innovator1510_smc91x_resources, }; +static struct platform_device innovator1510_lcd_device = { + .name = "lcd_inn1510", + .id = -1, +}; + static struct platform_device *innovator1510_devices[] __initdata = { &innovator_flash_device, &innovator1510_smc91x_device, + &innovator_kp_device, + &innovator1510_lcd_device, }; #endif /* CONFIG_ARCH_OMAP15XX */ @@ -158,9 +205,16 @@ static struct platform_device innovator1610_smc91x_device = { .resource = innovator1610_smc91x_resources, }; +static struct platform_device innovator1610_lcd_device = { + .name = "inn1610_lcd", + .id = -1, +}; + static struct platform_device *innovator1610_devices[] __initdata = { &innovator_flash_device, &innovator1610_smc91x_device, + &innovator_kp_device, + &innovator1610_lcd_device, }; #endif /* CONFIG_ARCH_OMAP16XX */ @@ -206,7 +260,6 @@ static struct omap_usb_config innovator1510_usb_config __initdata = { }; static struct omap_lcd_config innovator1510_lcd_config __initdata = { - .panel_name = "inn1510", .ctrl_name = "internal", }; #endif @@ -228,7 +281,6 @@ static struct omap_usb_config h2_usb_config __initdata = { }; static struct omap_lcd_config innovator1610_lcd_config __initdata = { - .panel_name = "inn1610", .ctrl_name = "internal", }; #endif diff --git a/arch/arm/mach-omap1/board-netstar.c b/arch/arm/mach-omap1/board-netstar.c deleted file mode 100644 index 60d5f8a3339..00000000000 --- a/arch/arm/mach-omap1/board-netstar.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Modified from board-generic.c - * - * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl - * - * Code for Netstar OMAP board. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -extern void __init omap_init_time(void); -extern int omap_gpio_init(void); - -static struct resource netstar_smc91x_resources[] = { - [0] = { - .start = OMAP_CS1_PHYS + 0x300, - .end = OMAP_CS1_PHYS + 0x300 + 16, - .flags = IORESOURCE_MEM, - }, - [1] = { - .start = OMAP_GPIO_IRQ(8), - .end = OMAP_GPIO_IRQ(8), - .flags = IORESOURCE_IRQ, - }, -}; - -static struct platform_device netstar_smc91x_device = { - .name = "smc91x", - .id = 0, - .num_resources = ARRAY_SIZE(netstar_smc91x_resources), - .resource = netstar_smc91x_resources, -}; - -static struct platform_device *netstar_devices[] __initdata = { - &netstar_smc91x_device, -}; - -static struct omap_uart_config netstar_uart_config __initdata = { - .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)), -}; - -static struct omap_board_config_kernel netstar_config[] = { - { OMAP_TAG_UART, &netstar_uart_config }, -}; - -static void __init netstar_init_irq(void) -{ - omap1_init_common_hw(); - omap_init_irq(); - omap_gpio_init(); -} - -static void __init netstar_init(void) -{ - /* green LED */ - omap_request_gpio(4); - omap_set_gpio_direction(4, 0); - /* smc91x reset */ - omap_request_gpio(7); - omap_set_gpio_direction(7, 0); - omap_set_gpio_dataout(7, 1); - udelay(2); /* wait at least 100ns */ - omap_set_gpio_dataout(7, 0); - mdelay(50); /* 50ms until PHY ready */ - /* smc91x interrupt pin */ - omap_request_gpio(8); - - omap_request_gpio(12); - omap_request_gpio(13); - omap_request_gpio(14); - omap_request_gpio(15); - set_irq_type(OMAP_GPIO_IRQ(12), IRQT_FALLING); - set_irq_type(OMAP_GPIO_IRQ(13), IRQT_FALLING); - set_irq_type(OMAP_GPIO_IRQ(14), IRQT_FALLING); - set_irq_type(OMAP_GPIO_IRQ(15), IRQT_FALLING); - - platform_add_devices(netstar_devices, ARRAY_SIZE(netstar_devices)); - - /* Switch on green LED */ - omap_set_gpio_dataout(4, 0); - /* Switch off red LED */ - omap_writeb(0x00, OMAP_LPG1_PMR); /* Disable clock */ - omap_writeb(0x80, OMAP_LPG1_LCR); - - omap_board_config = netstar_config; - omap_board_config_size = ARRAY_SIZE(netstar_config); - omap_serial_init(); -} - -static void __init netstar_map_io(void) -{ - omap1_map_common_io(); -} - -#define MACHINE_PANICED 1 -#define MACHINE_REBOOTING 2 -#define MACHINE_REBOOT 4 -static unsigned long machine_state; - -static int panic_event(struct notifier_block *this, unsigned long event, - void *ptr) -{ - if (test_and_set_bit(MACHINE_PANICED, &machine_state)) - return NOTIFY_DONE; - - /* Switch off green LED */ - omap_set_gpio_dataout(4, 1); - /* Flash red LED */ - omap_writeb(0x78, OMAP_LPG1_LCR); - omap_writeb(0x01, OMAP_LPG1_PMR); /* Enable clock */ - - return NOTIFY_DONE; -} - -static struct notifier_block panic_block = { - .notifier_call = panic_event, -}; - -static int __init netstar_late_init(void) -{ - /* TODO: Setup front panel switch here */ - - /* Setup panic notifier */ - notifier_chain_register(&panic_notifier_list, &panic_block); - - return 0; -} - -postcore_initcall(netstar_late_init); - -MACHINE_START(NETSTAR, "NetStar OMAP5910") - /* Maintainer: Ladislav Michl */ - .phys_io = 0xfff00000, - .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, - .boot_params = 0x10000100, - .map_io = netstar_map_io, - .init_irq = netstar_init_irq, - .init_machine = netstar_init, - .timer = &omap_timer, -MACHINE_END diff --git a/arch/arm/mach-omap1/board-nokia770.c b/arch/arm/mach-omap1/board-nokia770.c new file mode 100644 index 00000000000..8133b5944ab --- /dev/null +++ b/arch/arm/mach-omap1/board-nokia770.c @@ -0,0 +1,260 @@ +/* + * linux/arch/arm/mach-omap1/board-nokia770.c + * + * Modified from board-generic.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void __init omap_nokia770_init_irq(void) +{ + omap1_init_common_hw(); + omap_init_irq(); +} + +static int nokia770_keymap[] = { + KEY(0, 1, GROUP_0 | KEY_UP), + KEY(0, 2, GROUP_1 | KEY_F5), + KEY(1, 0, GROUP_0 | KEY_LEFT), + KEY(1, 1, GROUP_0 | KEY_ENTER), + KEY(1, 2, GROUP_0 | KEY_RIGHT), + KEY(2, 0, GROUP_1 | KEY_ESC), + KEY(2, 1, GROUP_0 | KEY_DOWN), + KEY(2, 2, GROUP_1 | KEY_F4), + KEY(3, 0, GROUP_2 | KEY_F7), + KEY(3, 1, GROUP_2 | KEY_F8), + KEY(3, 2, GROUP_2 | KEY_F6), + 0 +}; + +static struct resource nokia770_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_kp_platform_data nokia770_kp_data = { + .rows = 8, + .cols = 8, + .keymap = nokia770_keymap +}; + +static struct platform_device nokia770_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &nokia770_kp_data, + }, + .num_resources = ARRAY_SIZE(nokia770_kp_resources), + .resource = nokia770_kp_resources, +}; + +static struct platform_device *nokia770_devices[] __initdata = { + &nokia770_kp_device, +}; + +static struct ads7846_platform_data nokia770_ads7846_platform_data __initdata = { + .x_max = 0x0fff, + .y_max = 0x0fff, + .x_plate_ohms = 120, + .pressure_max = 200, + .debounce_max = 10, + .debounce_tol = 3, +}; + +static struct spi_board_info nokia770_spi_board_info[] __initdata = { + [0] = { + .modalias = "lcd_lph8923", + .bus_num = 2, + .chip_select = 3, + .max_speed_hz = 12000000, + }, + [1] = { + .modalias = "ads7846", + .bus_num = 2, + .chip_select = 0, + .max_speed_hz = 2500000, + .irq = OMAP_GPIO_IRQ(15), + .platform_data = &nokia770_ads7846_platform_data, + }, +}; + + +/* assume no Mini-AB port */ + +static struct omap_usb_config nokia770_usb_config __initdata = { + .otg = 1, + .register_host = 1, + .register_dev = 1, + .hmc_mode = 16, + .pins[0] = 6, +}; + +static struct omap_mmc_config nokia770_mmc_config __initdata = { + .mmc[0] = { + .enabled = 0, + .wire4 = 0, + .wp_pin = -1, + .power_pin = -1, + .switch_pin = -1, + }, + .mmc[1] = { + .enabled = 0, + .wire4 = 0, + .wp_pin = -1, + .power_pin = -1, + .switch_pin = -1, + }, +}; + +static struct omap_board_config_kernel nokia770_config[] = { + { OMAP_TAG_USB, NULL }, + { OMAP_TAG_MMC, &nokia770_mmc_config }, +}; + +/* + * audio power control + */ +#define HEADPHONE_GPIO 14 +#define AMPLIFIER_CTRL_GPIO 58 + +static struct clk *dspxor_ck; +static DECLARE_MUTEX(audio_pwr_sem); +/* + * audio_pwr_state + * +--+-------------------------+---------------------------------------+ + * |-1|down |power-up request -> 0 | + * +--+-------------------------+---------------------------------------+ + * | 0|up |power-down(1) request -> 1 | + * | | |power-down(2) request -> (ignore) | + * +--+-------------------------+---------------------------------------+ + * | 1|up, |power-up request -> 0 | + * | |received down(1) request |power-down(2) request -> -1 | + * +--+-------------------------+---------------------------------------+ + */ +static int audio_pwr_state = -1; + +/* + * audio_pwr_up / down should be called under audio_pwr_sem + */ +static void nokia770_audio_pwr_up(void) +{ + clk_enable(dspxor_ck); + + /* Turn on codec */ + tlv320aic23_power_up(); + + if (omap_get_gpio_datain(HEADPHONE_GPIO)) + /* HP not connected, turn on amplifier */ + omap_set_gpio_dataout(AMPLIFIER_CTRL_GPIO, 1); + else + /* HP connected, do not turn on amplifier */ + printk("HP connected\n"); +} + +static void codec_delayed_power_down(void *arg) +{ + down(&audio_pwr_sem); + if (audio_pwr_state == -1) + tlv320aic23_power_down(); + up(&audio_pwr_sem); +} + +static DECLARE_WORK(codec_power_down_work, codec_delayed_power_down, NULL); + +static void nokia770_audio_pwr_down(void) +{ + clk_disable(dspxor_ck); + + /* Turn off amplifier */ + omap_set_gpio_dataout(AMPLIFIER_CTRL_GPIO, 0); + + /* Turn off codec: schedule delayed work */ + schedule_delayed_work(&codec_power_down_work, HZ / 20); /* 50ms */ +} + +void nokia770_audio_pwr_up_request(int stage) +{ + down(&audio_pwr_sem); + if (audio_pwr_state == -1) + nokia770_audio_pwr_up(); + /* force audio_pwr_state = 0, even if it was 1. */ + audio_pwr_state = 0; + up(&audio_pwr_sem); +} + +void nokia770_audio_pwr_down_request(int stage) +{ + down(&audio_pwr_sem); + switch (stage) { + case 1: + if (audio_pwr_state == 0) + audio_pwr_state = 1; + break; + case 2: + if (audio_pwr_state == 1) { + nokia770_audio_pwr_down(); + audio_pwr_state = -1; + } + break; + } + up(&audio_pwr_sem); +} + +static void __init omap_nokia770_init(void) +{ + nokia770_config[0].data = &nokia770_usb_config; + + platform_add_devices(nokia770_devices, ARRAY_SIZE(nokia770_devices)); + spi_register_board_info(nokia770_spi_board_info, + ARRAY_SIZE(nokia770_spi_board_info)); + omap_board_config = nokia770_config; + omap_board_config_size = ARRAY_SIZE(nokia770_config); + omap_serial_init(); + omap_dsp_audio_pwr_up_request = nokia770_audio_pwr_up_request; + omap_dsp_audio_pwr_down_request = nokia770_audio_pwr_down_request; + dspxor_ck = clk_get(0, "dspxor_ck"); +} + +static void __init omap_nokia770_map_io(void) +{ + omap1_map_common_io(); +} + +MACHINE_START(NOKIA770, "Nokia 770") + .phys_io = 0xfff00000, + .io_pg_offst = ((0xfef00000) >> 18) & 0xfffc, + .boot_params = 0x10000100, + .map_io = omap_nokia770_map_io, + .init_irq = omap_nokia770_init_irq, + .init_machine = omap_nokia770_init, + .timer = &omap_timer, +MACHINE_END diff --git a/arch/arm/mach-omap1/board-osk.c b/arch/arm/mach-omap1/board-osk.c index 543fa136106..56c8a4b12bb 100644 --- a/arch/arm/mach-omap1/board-osk.c +++ b/arch/arm/mach-omap1/board-osk.c @@ -33,6 +33,7 @@ #include #include +#include #include #include @@ -44,8 +45,23 @@ #include #include #include +#include #include +static int osk_keymap[] = { + KEY(0, 0, KEY_F1), + KEY(0, 3, KEY_UP), + KEY(1, 1, KEY_LEFTCTRL), + KEY(1, 2, KEY_LEFT), + KEY(2, 0, KEY_SPACE), + KEY(2, 1, KEY_ESC), + KEY(2, 2, KEY_DOWN), + KEY(3, 2, KEY_ENTER), + KEY(3, 3, KEY_RIGHT), + 0 +}; + + static struct mtd_partition osk_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ { @@ -138,11 +154,42 @@ static struct platform_device osk5912_mcbsp1_device = { .id = 1, }; +static struct resource osk5912_kp_resources[] = { + [0] = { + .start = INT_KEYBOARD, + .end = INT_KEYBOARD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_kp_platform_data osk_kp_data = { + .rows = 8, + .cols = 8, + .keymap = osk_keymap, +}; + +static struct platform_device osk5912_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &osk_kp_data, + }, + .num_resources = ARRAY_SIZE(osk5912_kp_resources), + .resource = osk5912_kp_resources, +}; + +static struct platform_device osk5912_lcd_device = { + .name = "lcd_osk", + .id = -1, +}; + static struct platform_device *osk5912_devices[] __initdata = { &osk5912_flash_device, &osk5912_smc91x_device, &osk5912_cf_device, &osk5912_mcbsp1_device, + &osk5912_kp_device, + &osk5912_lcd_device, }; static void __init osk_init_smc91x(void) @@ -197,7 +244,6 @@ static struct omap_uart_config osk_uart_config __initdata = { }; static struct omap_lcd_config osk_lcd_config __initdata = { - .panel_name = "osk", .ctrl_name = "internal", }; @@ -255,8 +301,18 @@ static void __init osk_mistral_init(void) static void __init osk_mistral_init(void) { } #endif +#define EMIFS_CS3_VAL (0x88013141) + static void __init osk_init(void) { + /* Workaround for wrong CS3 (NOR flash) timing + * There are some U-Boot versions out there which configure + * wrong CS3 memory timings. This mainly leads to CRC + * or similiar errors if you use NOR flash (e.g. with JFFS2) + */ + if (EMIFS_CCS(3) != EMIFS_CS3_VAL) + EMIFS_CCS(3) = EMIFS_CS3_VAL; + osk_flash_resource.end = osk_flash_resource.start = omap_cs3_phys(); osk_flash_resource.end += SZ_32M - 1; platform_add_devices(osk5912_devices, ARRAY_SIZE(osk5912_devices)); diff --git a/arch/arm/mach-omap1/board-palmte.c b/arch/arm/mach-omap1/board-palmte.c index e488f723677..4bc8a62909b 100644 --- a/arch/arm/mach-omap1/board-palmte.c +++ b/arch/arm/mach-omap1/board-palmte.c @@ -38,6 +38,15 @@ static void __init omap_generic_init_irq(void) omap_init_irq(); } +static struct platform_device palmte_lcd_device = { + .name = "lcd_palmte", + .id = -1, +}; + +static struct platform_device *devices[] __initdata = { + &palmte_lcd_device, +}; + static struct omap_usb_config palmte_usb_config __initdata = { .register_dev = 1, .hmc_mode = 0, @@ -55,7 +64,6 @@ static struct omap_mmc_config palmte_mmc_config __initdata = { }; static struct omap_lcd_config palmte_lcd_config __initdata = { - .panel_name = "palmte", .ctrl_name = "internal", }; @@ -69,6 +77,8 @@ static void __init omap_generic_init(void) { omap_board_config = palmte_config; omap_board_config_size = ARRAY_SIZE(palmte_config); + + platform_add_devices(devices, ARRAY_SIZE(devices)); } static void __init omap_generic_map_io(void) diff --git a/arch/arm/mach-omap1/board-perseus2.c b/arch/arm/mach-omap1/board-perseus2.c index 3913a3cc0ce..64b45d8ae35 100644 --- a/arch/arm/mach-omap1/board-perseus2.c +++ b/arch/arm/mach-omap1/board-perseus2.c @@ -16,7 +16,9 @@ #include #include #include +#include #include +#include #include #include @@ -28,9 +30,44 @@ #include #include #include +#include #include #include +static int p2_keymap[] = { + KEY(0,0,KEY_UP), + KEY(0,1,KEY_RIGHT), + KEY(0,2,KEY_LEFT), + KEY(0,3,KEY_DOWN), + KEY(0,4,KEY_CENTER), + KEY(0,5,KEY_0_5), + KEY(1,0,KEY_SOFT2), + KEY(1,1,KEY_SEND), + KEY(1,2,KEY_END), + KEY(1,3,KEY_VOLUMEDOWN), + KEY(1,4,KEY_VOLUMEUP), + KEY(1,5,KEY_RECORD), + KEY(2,0,KEY_SOFT1), + KEY(2,1,KEY_3), + KEY(2,2,KEY_6), + KEY(2,3,KEY_9), + KEY(2,4,KEY_SHARP), + KEY(2,5,KEY_2_5), + KEY(3,0,KEY_BACK), + KEY(3,1,KEY_2), + KEY(3,2,KEY_5), + KEY(3,3,KEY_8), + KEY(3,4,KEY_0), + KEY(3,5,KEY_HEADSETHOOK), + KEY(4,0,KEY_HOME), + KEY(4,1,KEY_1), + KEY(4,2,KEY_4), + KEY(4,3,KEY_7), + KEY(4,4,KEY_STAR), + KEY(4,5,KEY_POWER), + 0 +}; + static struct resource smc91x_resources[] = { [0] = { .start = H2P2_DBG_FPGA_ETHR_START, /* Physical */ @@ -44,7 +81,7 @@ static struct resource smc91x_resources[] = { }, }; -static struct mtd_partition p2_partitions[] = { +static struct mtd_partition nor_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ { .name = "bootloader", @@ -75,27 +112,47 @@ static struct mtd_partition p2_partitions[] = { }, }; -static struct flash_platform_data p2_flash_data = { +static struct flash_platform_data nor_data = { .map_name = "cfi_probe", .width = 2, - .parts = p2_partitions, - .nr_parts = ARRAY_SIZE(p2_partitions), + .parts = nor_partitions, + .nr_parts = ARRAY_SIZE(nor_partitions), }; -static struct resource p2_flash_resource = { +static struct resource nor_resource = { .start = OMAP_CS0_PHYS, .end = OMAP_CS0_PHYS + SZ_32M - 1, .flags = IORESOURCE_MEM, }; -static struct platform_device p2_flash_device = { +static struct platform_device nor_device = { .name = "omapflash", .id = 0, .dev = { - .platform_data = &p2_flash_data, + .platform_data = &nor_data, + }, + .num_resources = 1, + .resource = &nor_resource, +}; + +static struct nand_platform_data nand_data = { + .options = NAND_SAMSUNG_LP_OPTIONS, +}; + +static struct resource nand_resource = { + .start = OMAP_CS3_PHYS, + .end = OMAP_CS3_PHYS + SZ_4K - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device nand_device = { + .name = "omapnand", + .id = 0, + .dev = { + .platform_data = &nand_data, }, .num_resources = 1, - .resource = &p2_flash_resource, + .resource = &nand_resource, }; static struct platform_device smc91x_device = { @@ -105,17 +162,55 @@ static struct platform_device smc91x_device = { .resource = smc91x_resources, }; +static struct resource kp_resources[] = { + [0] = { + .start = INT_730_MPUIO_KEYPAD, + .end = INT_730_MPUIO_KEYPAD, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct omap_kp_platform_data kp_data = { + .rows = 8, + .cols = 8, + .keymap = p2_keymap, +}; + +static struct platform_device kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &kp_data, + }, + .num_resources = ARRAY_SIZE(kp_resources), + .resource = kp_resources, +}; + +static struct platform_device lcd_device = { + .name = "lcd_p2", + .id = -1, +}; + static struct platform_device *devices[] __initdata = { - &p2_flash_device, + &nor_device, + &nand_device, &smc91x_device, + &kp_device, + &lcd_device, }; +#define P2_NAND_RB_GPIO_PIN 62 + +static int nand_dev_ready(struct nand_platform_data *data) +{ + return omap_get_gpio_datain(P2_NAND_RB_GPIO_PIN); +} + static struct omap_uart_config perseus2_uart_config __initdata = { .enabled_uarts = ((1 << 0) | (1 << 1)), }; static struct omap_lcd_config perseus2_lcd_config __initdata = { - .panel_name = "p2", .ctrl_name = "internal", }; @@ -126,7 +221,13 @@ static struct omap_board_config_kernel perseus2_config[] = { static void __init omap_perseus2_init(void) { - (void) platform_add_devices(devices, ARRAY_SIZE(devices)); + if (!(omap_request_gpio(P2_NAND_RB_GPIO_PIN))) + nand_data.dev_ready = nand_dev_ready; + + omap_cfg_reg(L3_1610_FLASH_CS2B_OE); + omap_cfg_reg(M8_1610_FLASH_CS2B_WE); + + platform_add_devices(devices, ARRAY_SIZE(devices)); omap_board_config = perseus2_config; omap_board_config_size = ARRAY_SIZE(perseus2_config); diff --git a/arch/arm/mach-omap1/board-voiceblue.c b/arch/arm/mach-omap1/board-voiceblue.c index bfd5fdd1a87..447a586eb33 100644 --- a/arch/arm/mach-omap1/board-voiceblue.c +++ b/arch/arm/mach-omap1/board-voiceblue.c @@ -155,9 +155,9 @@ static struct omap_uart_config voiceblue_uart_config __initdata = { }; static struct omap_board_config_kernel voiceblue_config[] = { - { OMAP_TAG_USB, &voiceblue_usb_config }, - { OMAP_TAG_MMC, &voiceblue_mmc_config }, - { OMAP_TAG_UART, &voiceblue_uart_config }, + { OMAP_TAG_USB, &voiceblue_usb_config }, + { OMAP_TAG_MMC, &voiceblue_mmc_config }, + { OMAP_TAG_UART, &voiceblue_uart_config }, }; static void __init voiceblue_init_irq(void) diff --git a/arch/arm/mach-omap1/clock.c b/arch/arm/mach-omap1/clock.c index 75110ba1042..619db18144e 100644 --- a/arch/arm/mach-omap1/clock.c +++ b/arch/arm/mach-omap1/clock.c @@ -345,7 +345,7 @@ static unsigned calc_ext_dsor(unsigned long rate) */ for (dsor = 2; dsor < 96; ++dsor) { if ((dsor & 1) && dsor > 8) - continue; + continue; if (rate >= 96000000 / dsor) break; } @@ -687,6 +687,11 @@ int __init omap1_clk_init(void) clk_register(*clkp); continue; } + + if (((*clkp)->flags &CLOCK_IN_OMAP310) && cpu_is_omap310()) { + clk_register(*clkp); + continue; + } } info = omap_get_config(OMAP_TAG_CLOCK, struct omap_clock_config); @@ -784,7 +789,7 @@ int __init omap1_clk_init(void) clk_enable(&armxor_ck.clk); clk_enable(&armtim_ck.clk); /* This should be done by timer code */ - if (cpu_is_omap1510()) + if (cpu_is_omap15xx()) clk_enable(&arm_gpio_ck); return 0; diff --git a/arch/arm/mach-omap1/clock.h b/arch/arm/mach-omap1/clock.h index 4f18d1b9444..b7c68819c4e 100644 --- a/arch/arm/mach-omap1/clock.h +++ b/arch/arm/mach-omap1/clock.h @@ -151,7 +151,7 @@ static struct clk ck_ref = { .name = "ck_ref", .rate = 12000000, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - ALWAYS_ENABLED, + CLOCK_IN_OMAP310 | ALWAYS_ENABLED, .enable = &omap1_clk_enable_generic, .disable = &omap1_clk_disable_generic, }; @@ -160,7 +160,7 @@ static struct clk ck_dpll1 = { .name = "ck_dpll1", .parent = &ck_ref, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_PROPAGATES | ALWAYS_ENABLED, + CLOCK_IN_OMAP310 | RATE_PROPAGATES | ALWAYS_ENABLED, .enable = &omap1_clk_enable_generic, .disable = &omap1_clk_disable_generic, }; @@ -183,7 +183,8 @@ static struct clk arm_ck = { .name = "arm_ck", .parent = &ck_dpll1, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_CKCTL | RATE_PROPAGATES | ALWAYS_ENABLED, + CLOCK_IN_OMAP310 | RATE_CKCTL | RATE_PROPAGATES | + ALWAYS_ENABLED, .rate_offset = CKCTL_ARMDIV_OFFSET, .recalc = &omap1_ckctl_recalc, .enable = &omap1_clk_enable_generic, @@ -195,7 +196,8 @@ static struct arm_idlect1_clk armper_ck = { .name = "armper_ck", .parent = &ck_dpll1, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_CKCTL | CLOCK_IDLE_CONTROL, + CLOCK_IN_OMAP310 | RATE_CKCTL | + CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_PERCK, .rate_offset = CKCTL_PERDIV_OFFSET, @@ -209,7 +211,7 @@ static struct arm_idlect1_clk armper_ck = { static struct clk arm_gpio_ck = { .name = "arm_gpio_ck", .parent = &ck_dpll1, - .flags = CLOCK_IN_OMAP1510, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_GPIOCK, .recalc = &followparent_recalc, @@ -222,7 +224,7 @@ static struct arm_idlect1_clk armxor_ck = { .name = "armxor_ck", .parent = &ck_ref, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IDLE_CONTROL, + CLOCK_IN_OMAP310 | CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_XORPCK, .recalc = &followparent_recalc, @@ -237,7 +239,7 @@ static struct arm_idlect1_clk armtim_ck = { .name = "armtim_ck", .parent = &ck_ref, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IDLE_CONTROL, + CLOCK_IN_OMAP310 | CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_TIMCK, .recalc = &followparent_recalc, @@ -252,7 +254,7 @@ static struct arm_idlect1_clk armwdt_ck = { .name = "armwdt_ck", .parent = &ck_ref, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IDLE_CONTROL, + CLOCK_IN_OMAP310 | CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_WDTCK, .recalc = &omap1_watchdog_recalc, @@ -344,9 +346,9 @@ static struct arm_idlect1_clk tc_ck = { .name = "tc_ck", .parent = &ck_dpll1, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IN_OMAP730 | RATE_CKCTL | - RATE_PROPAGATES | ALWAYS_ENABLED | - CLOCK_IDLE_CONTROL, + CLOCK_IN_OMAP730 | CLOCK_IN_OMAP310 | + RATE_CKCTL | RATE_PROPAGATES | + ALWAYS_ENABLED | CLOCK_IDLE_CONTROL, .rate_offset = CKCTL_TCDIV_OFFSET, .recalc = &omap1_ckctl_recalc, .enable = &omap1_clk_enable_generic, @@ -358,7 +360,8 @@ static struct arm_idlect1_clk tc_ck = { static struct clk arminth_ck1510 = { .name = "arminth_ck", .parent = &tc_ck.clk, - .flags = CLOCK_IN_OMAP1510 | ALWAYS_ENABLED, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | + ALWAYS_ENABLED, .recalc = &followparent_recalc, /* Note: On 1510 the frequency follows TC_CK * @@ -372,7 +375,8 @@ static struct clk tipb_ck = { /* No-idle controlled by "tc_ck" */ .name = "tibp_ck", .parent = &tc_ck.clk, - .flags = CLOCK_IN_OMAP1510 | ALWAYS_ENABLED, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | + ALWAYS_ENABLED, .recalc = &followparent_recalc, .enable = &omap1_clk_enable_generic, .disable = &omap1_clk_disable_generic, @@ -417,7 +421,7 @@ static struct clk dma_ck = { .name = "dma_ck", .parent = &tc_ck.clk, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - ALWAYS_ENABLED, + CLOCK_IN_OMAP310 | ALWAYS_ENABLED, .recalc = &followparent_recalc, .enable = &omap1_clk_enable_generic, .disable = &omap1_clk_disable_generic, @@ -437,7 +441,7 @@ static struct arm_idlect1_clk api_ck = { .name = "api_ck", .parent = &tc_ck.clk, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - CLOCK_IDLE_CONTROL, + CLOCK_IN_OMAP310 | CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_APICK, .recalc = &followparent_recalc, @@ -451,7 +455,8 @@ static struct arm_idlect1_clk lb_ck = { .clk = { .name = "lb_ck", .parent = &tc_ck.clk, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IDLE_CONTROL, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | + CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_LBCK, .recalc = &followparent_recalc, @@ -495,8 +500,8 @@ static struct arm_idlect1_clk lcd_ck_1510 = { .clk = { .name = "lcd_ck", .parent = &ck_dpll1, - .flags = CLOCK_IN_OMAP1510 | RATE_CKCTL | - CLOCK_IDLE_CONTROL, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | + RATE_CKCTL | CLOCK_IDLE_CONTROL, .enable_reg = (void __iomem *)ARM_IDLECT2, .enable_bit = EN_LCDCK, .rate_offset = CKCTL_LCDDIV_OFFSET, @@ -512,8 +517,9 @@ static struct clk uart1_1510 = { /* Direct from ULPD, no real parent */ .parent = &armper_ck.clk, .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | ENABLE_REG_32BIT | - ALWAYS_ENABLED | CLOCK_NO_IDLE_PARENT, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | + ENABLE_REG_32BIT | ALWAYS_ENABLED | + CLOCK_NO_IDLE_PARENT, .enable_reg = (void __iomem *)MOD_CONF_CTRL_0, .enable_bit = 29, /* Chooses between 12MHz and 48MHz */ .set_rate = &omap1_set_uart_rate, @@ -544,8 +550,8 @@ static struct clk uart2_ck = { .parent = &armper_ck.clk, .rate = 12000000, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - ENABLE_REG_32BIT | ALWAYS_ENABLED | - CLOCK_NO_IDLE_PARENT, + CLOCK_IN_OMAP310 | ENABLE_REG_32BIT | + ALWAYS_ENABLED | CLOCK_NO_IDLE_PARENT, .enable_reg = (void __iomem *)MOD_CONF_CTRL_0, .enable_bit = 30, /* Chooses between 12MHz and 48MHz */ .set_rate = &omap1_set_uart_rate, @@ -559,8 +565,9 @@ static struct clk uart3_1510 = { /* Direct from ULPD, no real parent */ .parent = &armper_ck.clk, .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | ENABLE_REG_32BIT | - ALWAYS_ENABLED | CLOCK_NO_IDLE_PARENT, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | + ENABLE_REG_32BIT | ALWAYS_ENABLED | + CLOCK_NO_IDLE_PARENT, .enable_reg = (void __iomem *)MOD_CONF_CTRL_0, .enable_bit = 31, /* Chooses between 12MHz and 48MHz */ .set_rate = &omap1_set_uart_rate, @@ -590,7 +597,7 @@ static struct clk usb_clko = { /* 6 MHz output on W4_USB_CLKO */ /* Direct from ULPD, no parent */ .rate = 6000000, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_FIXED | ENABLE_REG_32BIT, + CLOCK_IN_OMAP310 | RATE_FIXED | ENABLE_REG_32BIT, .enable_reg = (void __iomem *)ULPD_CLOCK_CTRL, .enable_bit = USB_MCLK_EN_BIT, .enable = &omap1_clk_enable_generic, @@ -601,7 +608,7 @@ static struct clk usb_hhc_ck1510 = { .name = "usb_hhc_ck", /* Direct from ULPD, no parent */ .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */ - .flags = CLOCK_IN_OMAP1510 | + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | RATE_FIXED | ENABLE_REG_32BIT, .enable_reg = (void __iomem *)MOD_CONF_CTRL_0, .enable_bit = USB_HOST_HHC_UHOST_EN, @@ -637,7 +644,9 @@ static struct clk mclk_1510 = { .name = "mclk", /* Direct from ULPD, no parent. May be enabled by ext hardware. */ .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | RATE_FIXED, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | RATE_FIXED, + .enable_reg = (void __iomem *)SOFT_REQ_REG, + .enable_bit = 6, .enable = &omap1_clk_enable_generic, .disable = &omap1_clk_disable_generic, }; @@ -659,7 +668,7 @@ static struct clk bclk_1510 = { .name = "bclk", /* Direct from ULPD, no parent. May be enabled by ext hardware. */ .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | RATE_FIXED, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | RATE_FIXED, .enable = &omap1_clk_enable_generic, .disable = &omap1_clk_disable_generic, }; @@ -678,12 +687,14 @@ static struct clk bclk_16xx = { }; static struct clk mmc1_ck = { - .name = "mmc1_ck", + .name = "mmc_ck", + .id = 1, /* Functional clock is direct from ULPD, interface clock is ARMPER */ .parent = &armper_ck.clk, .rate = 48000000, .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - RATE_FIXED | ENABLE_REG_32BIT | CLOCK_NO_IDLE_PARENT, + CLOCK_IN_OMAP310 | RATE_FIXED | ENABLE_REG_32BIT | + CLOCK_NO_IDLE_PARENT, .enable_reg = (void __iomem *)MOD_CONF_CTRL_0, .enable_bit = 23, .enable = &omap1_clk_enable_generic, @@ -691,7 +702,8 @@ static struct clk mmc1_ck = { }; static struct clk mmc2_ck = { - .name = "mmc2_ck", + .name = "mmc_ck", + .id = 2, /* Functional clock is direct from ULPD, interface clock is ARMPER */ .parent = &armper_ck.clk, .rate = 48000000, @@ -706,7 +718,7 @@ static struct clk mmc2_ck = { static struct clk virtual_ck_mpu = { .name = "mpu", .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | - VIRTUAL_CLOCK | ALWAYS_ENABLED, + CLOCK_IN_OMAP310 | VIRTUAL_CLOCK | ALWAYS_ENABLED, .parent = &arm_ck, /* Is smarter alias for */ .recalc = &followparent_recalc, .set_rate = &omap1_select_table_rate, @@ -715,6 +727,20 @@ static struct clk virtual_ck_mpu = { .disable = &omap1_clk_disable_generic, }; +/* virtual functional clock domain for I2C. Just for making sure that ARMXOR_CK +remains active during MPU idle whenever this is enabled */ +static struct clk i2c_fck = { + .name = "i2c_fck", + .id = 1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + VIRTUAL_CLOCK | CLOCK_NO_IDLE_PARENT | + ALWAYS_ENABLED, + .parent = &armxor_ck.clk, + .recalc = &followparent_recalc, + .enable = &omap1_clk_enable_generic, + .disable = &omap1_clk_disable_generic, +}; + static struct clk * onchip_clks[] = { /* non-ULPD clocks */ &ck_ref, @@ -763,6 +789,7 @@ static struct clk * onchip_clks[] = { &mmc2_ck, /* Virtual clocks */ &virtual_ck_mpu, + &i2c_fck, }; #endif diff --git a/arch/arm/mach-omap1/devices.c b/arch/arm/mach-omap1/devices.c index ecbc47514ad..876c38da14f 100644 --- a/arch/arm/mach-omap1/devices.c +++ b/arch/arm/mach-omap1/devices.c @@ -99,6 +99,45 @@ static void omap_init_rtc(void) static inline void omap_init_rtc(void) {} #endif +#if defined(CONFIG_OMAP_STI) + +#define OMAP1_STI_BASE IO_ADDRESS(0xfffea000) +#define OMAP1_STI_CHANNEL_BASE (OMAP1_STI_BASE + 0x400) + +static struct resource sti_resources[] = { + { + .start = OMAP1_STI_BASE, + .end = OMAP1_STI_BASE + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = OMAP1_STI_CHANNEL_BASE, + .end = OMAP1_STI_CHANNEL_BASE + SZ_1K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = INT_1610_STI, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device sti_device = { + .name = "sti", + .id = -1, + .dev = { + .release = omap_nop_release, + }, + .num_resources = ARRAY_SIZE(sti_resources), + .resource = sti_resources, +}; + +static inline void omap_init_sti(void) +{ + platform_device_register(&sti_device); +} +#else +static inline void omap_init_sti(void) {} +#endif /*-------------------------------------------------------------------------*/ @@ -129,6 +168,7 @@ static int __init omap1_init_devices(void) */ omap_init_irda(); omap_init_rtc(); + omap_init_sti(); return 0; } diff --git a/arch/arm/mach-omap1/io.c b/arch/arm/mach-omap1/io.c index 82d556be79c..be3a2a4ee2b 100644 --- a/arch/arm/mach-omap1/io.c +++ b/arch/arm/mach-omap1/io.c @@ -18,6 +18,7 @@ #include #include #include +#include extern int omap1_clk_init(void); extern void omap_check_revision(void); @@ -110,7 +111,7 @@ void __init omap1_map_common_io(void) } #endif #ifdef CONFIG_ARCH_OMAP15XX - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { iotable_init(omap1510_io_desc, ARRAY_SIZE(omap1510_io_desc)); } #endif @@ -121,6 +122,7 @@ void __init omap1_map_common_io(void) #endif omap_sram_init(); + omapfb_reserve_mem(); } /* diff --git a/arch/arm/mach-omap1/irq.c b/arch/arm/mach-omap1/irq.c index ed65a7d2e94..a0431c00fa8 100644 --- a/arch/arm/mach-omap1/irq.c +++ b/arch/arm/mach-omap1/irq.c @@ -60,7 +60,7 @@ struct omap_irq_bank { unsigned long wake_enable; }; -static unsigned int irq_bank_count = 0; +static unsigned int irq_bank_count; static struct omap_irq_bank *irq_banks; static inline unsigned int irq_bank_readl(int bank, int offset) @@ -142,28 +142,28 @@ static void omap_irq_set_cfg(int irq, int fiq, int priority, int trigger) #ifdef CONFIG_ARCH_OMAP730 static struct omap_irq_bank omap730_irq_banks[] = { - { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3f8e22f }, - { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb9c1f2 }, + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3f8e22f }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb9c1f2 }, { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0x800040f3 }, }; #endif #ifdef CONFIG_ARCH_OMAP15XX static struct omap_irq_bank omap1510_irq_banks[] = { - { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3febfff }, - { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xffbfffed }, + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3febfff }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xffbfffed }, }; static struct omap_irq_bank omap310_irq_banks[] = { - { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3faefc3 }, - { .base_reg = OMAP_IH2_BASE, .trigger_map = 0x65b3c061 }, + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3faefc3 }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0x65b3c061 }, }; #endif #if defined(CONFIG_ARCH_OMAP16XX) static struct omap_irq_bank omap1610_irq_banks[] = { - { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3fefe8f }, - { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb7c1fd }, + { .base_reg = OMAP_IH1_BASE, .trigger_map = 0xb3fefe8f }, + { .base_reg = OMAP_IH2_BASE, .trigger_map = 0xfdb7c1fd }, { .base_reg = OMAP_IH2_BASE + 0x100, .trigger_map = 0xffffb7ff }, { .base_reg = OMAP_IH2_BASE + 0x200, .trigger_map = 0xffffffff }, }; diff --git a/arch/arm/mach-omap1/mux.c b/arch/arm/mach-omap1/mux.c index d4b8d624e74..10fe0b3efca 100644 --- a/arch/arm/mach-omap1/mux.c +++ b/arch/arm/mach-omap1/mux.c @@ -35,16 +35,20 @@ #ifdef CONFIG_ARCH_OMAP730 struct pin_config __initdata_or_module omap730_pins[] = { -MUX_CFG_730("E2_730_KBR0", 12, 21, 0, 0, 20, 1, NA, 0, 0) -MUX_CFG_730("J7_730_KBR1", 12, 25, 0, 0, 24, 1, NA, 0, 0) -MUX_CFG_730("E1_730_KBR2", 12, 29, 0, 0, 28, 1, NA, 0, 0) -MUX_CFG_730("F3_730_KBR3", 13, 1, 0, 0, 0, 1, NA, 0, 0) -MUX_CFG_730("D2_730_KBR4", 13, 5, 0, 0, 4, 1, NA, 0, 0) -MUX_CFG_730("C2_730_KBC0", 13, 9, 0, 0, 8, 1, NA, 0, 0) -MUX_CFG_730("D3_730_KBC1", 13, 13, 0, 0, 12, 1, NA, 0, 0) -MUX_CFG_730("E4_730_KBC2", 13, 17, 0, 0, 16, 1, NA, 0, 0) -MUX_CFG_730("F4_730_KBC3", 13, 21, 0, 0, 20, 1, NA, 0, 0) -MUX_CFG_730("E3_730_KBC4", 13, 25, 0, 0, 24, 1, NA, 0, 0) +MUX_CFG_730("E2_730_KBR0", 12, 21, 0, 20, 1, 0) +MUX_CFG_730("J7_730_KBR1", 12, 25, 0, 24, 1, 0) +MUX_CFG_730("E1_730_KBR2", 12, 29, 0, 28, 1, 0) +MUX_CFG_730("F3_730_KBR3", 13, 1, 0, 0, 1, 0) +MUX_CFG_730("D2_730_KBR4", 13, 5, 0, 4, 1, 0) +MUX_CFG_730("C2_730_KBC0", 13, 9, 0, 8, 1, 0) +MUX_CFG_730("D3_730_KBC1", 13, 13, 0, 12, 1, 0) +MUX_CFG_730("E4_730_KBC2", 13, 17, 0, 16, 1, 0) +MUX_CFG_730("F4_730_KBC3", 13, 21, 0, 20, 1, 0) +MUX_CFG_730("E3_730_KBC4", 13, 25, 0, 24, 1, 0) + +MUX_CFG_730("AA17_730_USB_DM", 2, 21, 0, 20, 0, 0) +MUX_CFG_730("W16_730_USB_PU_EN", 2, 25, 0, 24, 0, 0) +MUX_CFG_730("W17_730_USB_VBUSI", 2, 29, 0, 28, 0, 0) }; #endif @@ -73,8 +77,8 @@ MUX_CFG("UART3_BCLK", A, 0, 0, 2, 6, 0, NA, 0, 0) MUX_CFG("Y15_1610_UART3_RTS", A, 0, 1, 2, 6, 0, NA, 0, 0) /* PWT & PWL, conflicts with UART3 */ -MUX_CFG("PWT", 6, 0, 2, 0, 30, 0, NA, 0, 0) -MUX_CFG("PWL", 6, 3, 1, 0, 31, 1, NA, 0, 0) +MUX_CFG("PWT", 6, 0, 2, 0, 30, 0, NA, 0, 0) +MUX_CFG("PWL", 6, 3, 1, 0, 31, 1, NA, 0, 0) /* USB internal master generic */ MUX_CFG("R18_USB_VBUS", 7, 9, 2, 1, 11, 0, NA, 0, 1) @@ -151,7 +155,7 @@ MUX_CFG("MCBSP3_CLKX", 9, 3, 1, 1, 29, 0, NA, 0, 1) /* Misc ballouts */ MUX_CFG("BALLOUT_V8_ARMIO3", B, 18, 0, 2, 25, 1, NA, 0, 1) -MUX_CFG("N20_HDQ", 6, 18, 1, 1, 4, 0, 1, 4, 0) +MUX_CFG("N20_HDQ", 6, 18, 1, 1, 4, 0, 1, 4, 0) /* OMAP-1610 MMC2 */ MUX_CFG("W8_1610_MMC2_DAT0", B, 21, 6, 2, 23, 1, 2, 1, 1) diff --git a/arch/arm/mach-omap1/pm.c b/arch/arm/mach-omap1/pm.c new file mode 100644 index 00000000000..be0d6b7b6e9 --- /dev/null +++ b/arch/arm/mach-omap1/pm.c @@ -0,0 +1,779 @@ +/* + * linux/arch/arm/mach-omap1/pm.c + * + * OMAP Power Management Routines + * + * Original code for the SA11x0: + * Copyright (c) 2001 Cliff Brake + * + * Modified for the PXA250 by Nicolas Pitre: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * Modified for the OMAP1510 by David Singleton: + * Copyright (c) 2002 Monta Vista Software, Inc. + * + * Cleanup 2004 for OMAP1510/1610 by Dirk Behme + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static unsigned int arm_sleep_save[ARM_SLEEP_SAVE_SIZE]; +static unsigned short dsp_sleep_save[DSP_SLEEP_SAVE_SIZE]; +static unsigned short ulpd_sleep_save[ULPD_SLEEP_SAVE_SIZE]; +static unsigned int mpui730_sleep_save[MPUI730_SLEEP_SAVE_SIZE]; +static unsigned int mpui1510_sleep_save[MPUI1510_SLEEP_SAVE_SIZE]; +static unsigned int mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_SIZE]; + +static unsigned short enable_dyn_sleep = 1; + +static ssize_t omap_pm_sleep_while_idle_show(struct subsystem * subsys, char *buf) +{ + return sprintf(buf, "%hu\n", enable_dyn_sleep); +} + +static ssize_t omap_pm_sleep_while_idle_store(struct subsystem * subsys, + const char * buf, + size_t n) +{ + unsigned short value; + if (sscanf(buf, "%hu", &value) != 1 || + (value != 0 && value != 1)) { + printk(KERN_ERR "idle_sleep_store: Invalid value\n"); + return -EINVAL; + } + enable_dyn_sleep = value; + return n; +} + +static struct subsys_attribute sleep_while_idle_attr = { + .attr = { + .name = __stringify(sleep_while_idle), + .mode = 0644, + }, + .show = omap_pm_sleep_while_idle_show, + .store = omap_pm_sleep_while_idle_store, +}; + +extern struct subsystem power_subsys; +static void (*omap_sram_idle)(void) = NULL; +static void (*omap_sram_suspend)(unsigned long r0, unsigned long r1) = NULL; + +/* + * Let's power down on idle, but only if we are really + * idle, because once we start down the path of + * going idle we continue to do idle even if we get + * a clock tick interrupt . . + */ +void omap_pm_idle(void) +{ + extern __u32 arm_idlect1_mask; + __u32 use_idlect1 = arm_idlect1_mask; +#ifndef CONFIG_OMAP_MPU_TIMER + int do_sleep; +#endif + + local_irq_disable(); + local_fiq_disable(); + if (need_resched()) { + local_fiq_enable(); + local_irq_enable(); + return; + } + + /* + * Since an interrupt may set up a timer, we don't want to + * reprogram the hardware timer with interrupts enabled. + * Re-enable interrupts only after returning from idle. + */ + timer_dyn_reprogram(); + +#ifdef CONFIG_OMAP_MPU_TIMER +#warning Enable 32kHz OS timer in order to allow sleep states in idle + use_idlect1 = use_idlect1 & ~(1 << 9); +#else + + do_sleep = 0; + while (enable_dyn_sleep) { + +#ifdef CONFIG_CBUS_TAHVO_USB + extern int vbus_active; + /* Clock requirements? */ + if (vbus_active) + break; +#endif + do_sleep = 1; + break; + } + +#ifdef CONFIG_OMAP_DM_TIMER + use_idlect1 = omap_dm_timer_modify_idlect_mask(use_idlect1); +#endif + + if (omap_dma_running()) { + use_idlect1 &= ~(1 << 6); + if (omap_lcd_dma_ext_running()) + use_idlect1 &= ~(1 << 12); + } + + /* We should be able to remove the do_sleep variable and multiple + * tests above as soon as drivers, timer and DMA code have been fixed. + * Even the sleep block count should become obsolete. */ + if ((use_idlect1 != ~0) || !do_sleep) { + + __u32 saved_idlect1 = omap_readl(ARM_IDLECT1); + if (cpu_is_omap15xx()) + use_idlect1 &= OMAP1510_BIG_SLEEP_REQUEST; + else + use_idlect1 &= OMAP1610_IDLECT1_SLEEP_VAL; + omap_writel(use_idlect1, ARM_IDLECT1); + __asm__ volatile ("mcr p15, 0, r0, c7, c0, 4"); + omap_writel(saved_idlect1, ARM_IDLECT1); + + local_fiq_enable(); + local_irq_enable(); + return; + } + omap_sram_suspend(omap_readl(ARM_IDLECT1), + omap_readl(ARM_IDLECT2)); +#endif + + local_fiq_enable(); + local_irq_enable(); +} + +/* + * Configuration of the wakeup event is board specific. For the + * moment we put it into this helper function. Later it may move + * to board specific files. + */ +static void omap_pm_wakeup_setup(void) +{ + u32 level1_wake = 0; + u32 level2_wake = OMAP_IRQ_BIT(INT_UART2); + + /* + * Turn off all interrupts except GPIO bank 1, L1-2nd level cascade, + * and the L2 wakeup interrupts: keypad and UART2. Note that the + * drivers must still separately call omap_set_gpio_wakeup() to + * wake up to a GPIO interrupt. + */ + if (cpu_is_omap730()) + level1_wake = OMAP_IRQ_BIT(INT_730_GPIO_BANK1) | + OMAP_IRQ_BIT(INT_730_IH2_IRQ); + else if (cpu_is_omap15xx()) + level1_wake = OMAP_IRQ_BIT(INT_GPIO_BANK1) | + OMAP_IRQ_BIT(INT_1510_IH2_IRQ); + else if (cpu_is_omap16xx()) + level1_wake = OMAP_IRQ_BIT(INT_GPIO_BANK1) | + OMAP_IRQ_BIT(INT_1610_IH2_IRQ); + + omap_writel(~level1_wake, OMAP_IH1_MIR); + + if (cpu_is_omap730()) { + omap_writel(~level2_wake, OMAP_IH2_0_MIR); + omap_writel(~(OMAP_IRQ_BIT(INT_730_WAKE_UP_REQ) | + OMAP_IRQ_BIT(INT_730_MPUIO_KEYPAD)), + OMAP_IH2_1_MIR); + } else if (cpu_is_omap15xx()) { + level2_wake |= OMAP_IRQ_BIT(INT_KEYBOARD); + omap_writel(~level2_wake, OMAP_IH2_MIR); + } else if (cpu_is_omap16xx()) { + level2_wake |= OMAP_IRQ_BIT(INT_KEYBOARD); + omap_writel(~level2_wake, OMAP_IH2_0_MIR); + + /* INT_1610_WAKE_UP_REQ is needed for GPIO wakeup... */ + omap_writel(~OMAP_IRQ_BIT(INT_1610_WAKE_UP_REQ), + OMAP_IH2_1_MIR); + omap_writel(~0x0, OMAP_IH2_2_MIR); + omap_writel(~0x0, OMAP_IH2_3_MIR); + } + + /* New IRQ agreement, recalculate in cascade order */ + omap_writel(1, OMAP_IH2_CONTROL); + omap_writel(1, OMAP_IH1_CONTROL); +} + +#define EN_DSPCK 13 /* ARM_CKCTL */ +#define EN_APICK 6 /* ARM_IDLECT2 */ +#define DSP_EN 1 /* ARM_RSTCT1 */ + +void omap_pm_suspend(void) +{ + unsigned long arg0 = 0, arg1 = 0; + + printk("PM: OMAP%x is trying to enter deep sleep...\n", system_rev); + + omap_serial_wake_trigger(1); + + if (machine_is_omap_osk()) { + /* Stop LED1 (D9) blink */ + tps65010_set_led(LED1, OFF); + } + + omap_writew(0xffff, ULPD_SOFT_DISABLE_REQ_REG); + + /* + * Step 1: turn off interrupts (FIXME: NOTE: already disabled) + */ + + local_irq_disable(); + local_fiq_disable(); + + /* + * Step 2: save registers + * + * The omap is a strange/beautiful device. The caches, memory + * and register state are preserved across power saves. + * We have to save and restore very little register state to + * idle the omap. + * + * Save interrupt, MPUI, ARM and UPLD control registers. + */ + + if (cpu_is_omap730()) { + MPUI730_SAVE(OMAP_IH1_MIR); + MPUI730_SAVE(OMAP_IH2_0_MIR); + MPUI730_SAVE(OMAP_IH2_1_MIR); + MPUI730_SAVE(MPUI_CTRL); + MPUI730_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI730_SAVE(MPUI_DSP_API_CONFIG); + MPUI730_SAVE(EMIFS_CONFIG); + MPUI730_SAVE(EMIFF_SDRAM_CONFIG); + + } else if (cpu_is_omap15xx()) { + MPUI1510_SAVE(OMAP_IH1_MIR); + MPUI1510_SAVE(OMAP_IH2_MIR); + MPUI1510_SAVE(MPUI_CTRL); + MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1510_SAVE(MPUI_DSP_API_CONFIG); + MPUI1510_SAVE(EMIFS_CONFIG); + MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); + } else if (cpu_is_omap16xx()) { + MPUI1610_SAVE(OMAP_IH1_MIR); + MPUI1610_SAVE(OMAP_IH2_0_MIR); + MPUI1610_SAVE(OMAP_IH2_1_MIR); + MPUI1610_SAVE(OMAP_IH2_2_MIR); + MPUI1610_SAVE(OMAP_IH2_3_MIR); + MPUI1610_SAVE(MPUI_CTRL); + MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1610_SAVE(MPUI_DSP_API_CONFIG); + MPUI1610_SAVE(EMIFS_CONFIG); + MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); + } + + ARM_SAVE(ARM_CKCTL); + ARM_SAVE(ARM_IDLECT1); + ARM_SAVE(ARM_IDLECT2); + if (!(cpu_is_omap15xx())) + ARM_SAVE(ARM_IDLECT3); + ARM_SAVE(ARM_EWUPCT); + ARM_SAVE(ARM_RSTCT1); + ARM_SAVE(ARM_RSTCT2); + ARM_SAVE(ARM_SYSST); + ULPD_SAVE(ULPD_CLOCK_CTRL); + ULPD_SAVE(ULPD_STATUS_REQ); + + /* (Step 3 removed - we now allow deep sleep by default) */ + + /* + * Step 4: OMAP DSP Shutdown + */ + + /* stop DSP */ + omap_writew(omap_readw(ARM_RSTCT1) & ~(1 << DSP_EN), ARM_RSTCT1); + + /* shut down dsp_ck */ + omap_writew(omap_readw(ARM_CKCTL) & ~(1 << EN_DSPCK), ARM_CKCTL); + + /* temporarily enabling api_ck to access DSP registers */ + omap_writew(omap_readw(ARM_IDLECT2) | 1 << EN_APICK, ARM_IDLECT2); + + /* save DSP registers */ + DSP_SAVE(DSP_IDLECT2); + + /* Stop all DSP domain clocks */ + __raw_writew(0, DSP_IDLECT2); + + /* + * Step 5: Wakeup Event Setup + */ + + omap_pm_wakeup_setup(); + + /* + * Step 6: ARM and Traffic controller shutdown + */ + + /* disable ARM watchdog */ + omap_writel(0x00F5, OMAP_WDT_TIMER_MODE); + omap_writel(0x00A0, OMAP_WDT_TIMER_MODE); + + /* + * Step 6b: ARM and Traffic controller shutdown + * + * Step 6 continues here. Prepare jump to power management + * assembly code in internal SRAM. + * + * Since the omap_cpu_suspend routine has been copied to + * SRAM, we'll do an indirect procedure call to it and pass the + * contents of arm_idlect1 and arm_idlect2 so it can restore + * them when it wakes up and it will return. + */ + + arg0 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT1]; + arg1 = arm_sleep_save[ARM_SLEEP_SAVE_ARM_IDLECT2]; + + /* + * Step 6c: ARM and Traffic controller shutdown + * + * Jump to assembly code. The processor will stay there + * until wake up. + */ + omap_sram_suspend(arg0, arg1); + + /* + * If we are here, processor is woken up! + */ + + /* + * Restore DSP clocks + */ + + /* again temporarily enabling api_ck to access DSP registers */ + omap_writew(omap_readw(ARM_IDLECT2) | 1 << EN_APICK, ARM_IDLECT2); + + /* Restore DSP domain clocks */ + DSP_RESTORE(DSP_IDLECT2); + + /* + * Restore ARM state, except ARM_IDLECT1/2 which omap_cpu_suspend did + */ + + if (!(cpu_is_omap15xx())) + ARM_RESTORE(ARM_IDLECT3); + ARM_RESTORE(ARM_CKCTL); + ARM_RESTORE(ARM_EWUPCT); + ARM_RESTORE(ARM_RSTCT1); + ARM_RESTORE(ARM_RSTCT2); + ARM_RESTORE(ARM_SYSST); + ULPD_RESTORE(ULPD_CLOCK_CTRL); + ULPD_RESTORE(ULPD_STATUS_REQ); + + if (cpu_is_omap730()) { + MPUI730_RESTORE(EMIFS_CONFIG); + MPUI730_RESTORE(EMIFF_SDRAM_CONFIG); + MPUI730_RESTORE(OMAP_IH1_MIR); + MPUI730_RESTORE(OMAP_IH2_0_MIR); + MPUI730_RESTORE(OMAP_IH2_1_MIR); + } else if (cpu_is_omap15xx()) { + MPUI1510_RESTORE(MPUI_CTRL); + MPUI1510_RESTORE(MPUI_DSP_BOOT_CONFIG); + MPUI1510_RESTORE(MPUI_DSP_API_CONFIG); + MPUI1510_RESTORE(EMIFS_CONFIG); + MPUI1510_RESTORE(EMIFF_SDRAM_CONFIG); + MPUI1510_RESTORE(OMAP_IH1_MIR); + MPUI1510_RESTORE(OMAP_IH2_MIR); + } else if (cpu_is_omap16xx()) { + MPUI1610_RESTORE(MPUI_CTRL); + MPUI1610_RESTORE(MPUI_DSP_BOOT_CONFIG); + MPUI1610_RESTORE(MPUI_DSP_API_CONFIG); + MPUI1610_RESTORE(EMIFS_CONFIG); + MPUI1610_RESTORE(EMIFF_SDRAM_CONFIG); + + MPUI1610_RESTORE(OMAP_IH1_MIR); + MPUI1610_RESTORE(OMAP_IH2_0_MIR); + MPUI1610_RESTORE(OMAP_IH2_1_MIR); + MPUI1610_RESTORE(OMAP_IH2_2_MIR); + MPUI1610_RESTORE(OMAP_IH2_3_MIR); + } + + omap_writew(0, ULPD_SOFT_DISABLE_REQ_REG); + + /* + * Reenable interrupts + */ + + local_irq_enable(); + local_fiq_enable(); + + omap_serial_wake_trigger(0); + + printk("PM: OMAP%x is re-starting from deep sleep...\n", system_rev); + + if (machine_is_omap_osk()) { + /* Let LED1 (D9) blink again */ + tps65010_set_led(LED1, BLINK); + } +} + +#if defined(DEBUG) && defined(CONFIG_PROC_FS) +static int g_read_completed; + +/* + * Read system PM registers for debugging + */ +static int omap_pm_read_proc( + char *page_buffer, + char **my_first_byte, + off_t virtual_start, + int length, + int *eof, + void *data) +{ + int my_buffer_offset = 0; + char * const my_base = page_buffer; + + ARM_SAVE(ARM_CKCTL); + ARM_SAVE(ARM_IDLECT1); + ARM_SAVE(ARM_IDLECT2); + if (!(cpu_is_omap15xx())) + ARM_SAVE(ARM_IDLECT3); + ARM_SAVE(ARM_EWUPCT); + ARM_SAVE(ARM_RSTCT1); + ARM_SAVE(ARM_RSTCT2); + ARM_SAVE(ARM_SYSST); + + ULPD_SAVE(ULPD_IT_STATUS); + ULPD_SAVE(ULPD_CLOCK_CTRL); + ULPD_SAVE(ULPD_SOFT_REQ); + ULPD_SAVE(ULPD_STATUS_REQ); + ULPD_SAVE(ULPD_DPLL_CTRL); + ULPD_SAVE(ULPD_POWER_CTRL); + + if (cpu_is_omap730()) { + MPUI730_SAVE(MPUI_CTRL); + MPUI730_SAVE(MPUI_DSP_STATUS); + MPUI730_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI730_SAVE(MPUI_DSP_API_CONFIG); + MPUI730_SAVE(EMIFF_SDRAM_CONFIG); + MPUI730_SAVE(EMIFS_CONFIG); + } else if (cpu_is_omap15xx()) { + MPUI1510_SAVE(MPUI_CTRL); + MPUI1510_SAVE(MPUI_DSP_STATUS); + MPUI1510_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1510_SAVE(MPUI_DSP_API_CONFIG); + MPUI1510_SAVE(EMIFF_SDRAM_CONFIG); + MPUI1510_SAVE(EMIFS_CONFIG); + } else if (cpu_is_omap16xx()) { + MPUI1610_SAVE(MPUI_CTRL); + MPUI1610_SAVE(MPUI_DSP_STATUS); + MPUI1610_SAVE(MPUI_DSP_BOOT_CONFIG); + MPUI1610_SAVE(MPUI_DSP_API_CONFIG); + MPUI1610_SAVE(EMIFF_SDRAM_CONFIG); + MPUI1610_SAVE(EMIFS_CONFIG); + } + + if (virtual_start == 0) { + g_read_completed = 0; + + my_buffer_offset += sprintf(my_base + my_buffer_offset, + "ARM_CKCTL_REG: 0x%-8x \n" + "ARM_IDLECT1_REG: 0x%-8x \n" + "ARM_IDLECT2_REG: 0x%-8x \n" + "ARM_IDLECT3_REG: 0x%-8x \n" + "ARM_EWUPCT_REG: 0x%-8x \n" + "ARM_RSTCT1_REG: 0x%-8x \n" + "ARM_RSTCT2_REG: 0x%-8x \n" + "ARM_SYSST_REG: 0x%-8x \n" + "ULPD_IT_STATUS_REG: 0x%-4x \n" + "ULPD_CLOCK_CTRL_REG: 0x%-4x \n" + "ULPD_SOFT_REQ_REG: 0x%-4x \n" + "ULPD_DPLL_CTRL_REG: 0x%-4x \n" + "ULPD_STATUS_REQ_REG: 0x%-4x \n" + "ULPD_POWER_CTRL_REG: 0x%-4x \n", + ARM_SHOW(ARM_CKCTL), + ARM_SHOW(ARM_IDLECT1), + ARM_SHOW(ARM_IDLECT2), + ARM_SHOW(ARM_IDLECT3), + ARM_SHOW(ARM_EWUPCT), + ARM_SHOW(ARM_RSTCT1), + ARM_SHOW(ARM_RSTCT2), + ARM_SHOW(ARM_SYSST), + ULPD_SHOW(ULPD_IT_STATUS), + ULPD_SHOW(ULPD_CLOCK_CTRL), + ULPD_SHOW(ULPD_SOFT_REQ), + ULPD_SHOW(ULPD_DPLL_CTRL), + ULPD_SHOW(ULPD_STATUS_REQ), + ULPD_SHOW(ULPD_POWER_CTRL)); + + if (cpu_is_omap730()) { + my_buffer_offset += sprintf(my_base + my_buffer_offset, + "MPUI730_CTRL_REG 0x%-8x \n" + "MPUI730_DSP_STATUS_REG: 0x%-8x \n" + "MPUI730_DSP_BOOT_CONFIG_REG: 0x%-8x \n" + "MPUI730_DSP_API_CONFIG_REG: 0x%-8x \n" + "MPUI730_SDRAM_CONFIG_REG: 0x%-8x \n" + "MPUI730_EMIFS_CONFIG_REG: 0x%-8x \n", + MPUI730_SHOW(MPUI_CTRL), + MPUI730_SHOW(MPUI_DSP_STATUS), + MPUI730_SHOW(MPUI_DSP_BOOT_CONFIG), + MPUI730_SHOW(MPUI_DSP_API_CONFIG), + MPUI730_SHOW(EMIFF_SDRAM_CONFIG), + MPUI730_SHOW(EMIFS_CONFIG)); + } else if (cpu_is_omap15xx()) { + my_buffer_offset += sprintf(my_base + my_buffer_offset, + "MPUI1510_CTRL_REG 0x%-8x \n" + "MPUI1510_DSP_STATUS_REG: 0x%-8x \n" + "MPUI1510_DSP_BOOT_CONFIG_REG: 0x%-8x \n" + "MPUI1510_DSP_API_CONFIG_REG: 0x%-8x \n" + "MPUI1510_SDRAM_CONFIG_REG: 0x%-8x \n" + "MPUI1510_EMIFS_CONFIG_REG: 0x%-8x \n", + MPUI1510_SHOW(MPUI_CTRL), + MPUI1510_SHOW(MPUI_DSP_STATUS), + MPUI1510_SHOW(MPUI_DSP_BOOT_CONFIG), + MPUI1510_SHOW(MPUI_DSP_API_CONFIG), + MPUI1510_SHOW(EMIFF_SDRAM_CONFIG), + MPUI1510_SHOW(EMIFS_CONFIG)); + } else if (cpu_is_omap16xx()) { + my_buffer_offset += sprintf(my_base + my_buffer_offset, + "MPUI1610_CTRL_REG 0x%-8x \n" + "MPUI1610_DSP_STATUS_REG: 0x%-8x \n" + "MPUI1610_DSP_BOOT_CONFIG_REG: 0x%-8x \n" + "MPUI1610_DSP_API_CONFIG_REG: 0x%-8x \n" + "MPUI1610_SDRAM_CONFIG_REG: 0x%-8x \n" + "MPUI1610_EMIFS_CONFIG_REG: 0x%-8x \n", + MPUI1610_SHOW(MPUI_CTRL), + MPUI1610_SHOW(MPUI_DSP_STATUS), + MPUI1610_SHOW(MPUI_DSP_BOOT_CONFIG), + MPUI1610_SHOW(MPUI_DSP_API_CONFIG), + MPUI1610_SHOW(EMIFF_SDRAM_CONFIG), + MPUI1610_SHOW(EMIFS_CONFIG)); + } + + g_read_completed++; + } else if (g_read_completed >= 1) { + *eof = 1; + return 0; + } + g_read_completed++; + + *my_first_byte = page_buffer; + return my_buffer_offset; +} + +static void omap_pm_init_proc(void) +{ + struct proc_dir_entry *entry; + + entry = create_proc_read_entry("driver/omap_pm", + S_IWUSR | S_IRUGO, NULL, + omap_pm_read_proc, NULL); +} + +#endif /* DEBUG && CONFIG_PROC_FS */ + +static void (*saved_idle)(void) = NULL; + +/* + * omap_pm_prepare - Do preliminary suspend work. + * @state: suspend state we're entering. + * + */ +static int omap_pm_prepare(suspend_state_t state) +{ + int error = 0; + + /* We cannot sleep in idle until we have resumed */ + saved_idle = pm_idle; + pm_idle = NULL; + + switch (state) + { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + break; + + case PM_SUSPEND_DISK: + return -ENOTSUPP; + + default: + return -EINVAL; + } + + return error; +} + + +/* + * omap_pm_enter - Actually enter a sleep state. + * @state: State we're entering. + * + */ + +static int omap_pm_enter(suspend_state_t state) +{ + switch (state) + { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + omap_pm_suspend(); + break; + + case PM_SUSPEND_DISK: + return -ENOTSUPP; + + default: + return -EINVAL; + } + + return 0; +} + + +/** + * omap_pm_finish - Finish up suspend sequence. + * @state: State we're coming out of. + * + * This is called after we wake back up (or if entering the sleep state + * failed). + */ + +static int omap_pm_finish(suspend_state_t state) +{ + pm_idle = saved_idle; + return 0; +} + + +static irqreturn_t omap_wakeup_interrupt(int irq, void * dev, + struct pt_regs * regs) +{ + return IRQ_HANDLED; +} + +static struct irqaction omap_wakeup_irq = { + .name = "peripheral wakeup", + .flags = SA_INTERRUPT, + .handler = omap_wakeup_interrupt +}; + + + +static struct pm_ops omap_pm_ops ={ + .pm_disk_mode = 0, + .prepare = omap_pm_prepare, + .enter = omap_pm_enter, + .finish = omap_pm_finish, +}; + +static int __init omap_pm_init(void) +{ + printk("Power Management for TI OMAP.\n"); + + /* + * We copy the assembler sleep/wakeup routines to SRAM. + * These routines need to be in SRAM as that's the only + * memory the MPU can see when it wakes up. + */ + if (cpu_is_omap730()) { + omap_sram_idle = omap_sram_push(omap730_idle_loop_suspend, + omap730_idle_loop_suspend_sz); + omap_sram_suspend = omap_sram_push(omap730_cpu_suspend, + omap730_cpu_suspend_sz); + } else if (cpu_is_omap15xx()) { + omap_sram_idle = omap_sram_push(omap1510_idle_loop_suspend, + omap1510_idle_loop_suspend_sz); + omap_sram_suspend = omap_sram_push(omap1510_cpu_suspend, + omap1510_cpu_suspend_sz); + } else if (cpu_is_omap16xx()) { + omap_sram_idle = omap_sram_push(omap1610_idle_loop_suspend, + omap1610_idle_loop_suspend_sz); + omap_sram_suspend = omap_sram_push(omap1610_cpu_suspend, + omap1610_cpu_suspend_sz); + } + + if (omap_sram_idle == NULL || omap_sram_suspend == NULL) { + printk(KERN_ERR "PM not initialized: Missing SRAM support\n"); + return -ENODEV; + } + + pm_idle = omap_pm_idle; + + if (cpu_is_omap730()) + setup_irq(INT_730_WAKE_UP_REQ, &omap_wakeup_irq); + else if (cpu_is_omap16xx()) + setup_irq(INT_1610_WAKE_UP_REQ, &omap_wakeup_irq); + +#if 0 + /* --- BEGIN BOARD-DEPENDENT CODE --- */ + /* Sleepx mask direction */ + omap_writew((omap_readw(0xfffb5008) & ~2), 0xfffb5008); + /* Unmask sleepx signal */ + omap_writew((omap_readw(0xfffb5004) & ~2), 0xfffb5004); + /* --- END BOARD-DEPENDENT CODE --- */ +#endif + + /* Program new power ramp-up time + * (0 for most boards since we don't lower voltage when in deep sleep) + */ + omap_writew(ULPD_SETUP_ANALOG_CELL_3_VAL, ULPD_SETUP_ANALOG_CELL_3); + + /* Setup ULPD POWER_CTRL_REG - enter deep sleep whenever possible */ + omap_writew(ULPD_POWER_CTRL_REG_VAL, ULPD_POWER_CTRL); + + /* Configure IDLECT3 */ + if (cpu_is_omap730()) + omap_writel(OMAP730_IDLECT3_VAL, OMAP730_IDLECT3); + else if (cpu_is_omap16xx()) + omap_writel(OMAP1610_IDLECT3_VAL, OMAP1610_IDLECT3); + + pm_set_ops(&omap_pm_ops); + +#if defined(DEBUG) && defined(CONFIG_PROC_FS) + omap_pm_init_proc(); +#endif + + subsys_create_file(&power_subsys, &sleep_while_idle_attr); + + if (cpu_is_omap16xx()) { + /* configure LOW_PWR pin */ + omap_cfg_reg(T20_1610_LOW_PWR); + } + + return 0; +} +__initcall(omap_pm_init); diff --git a/arch/arm/mach-omap1/serial.c b/arch/arm/mach-omap1/serial.c index e924e0c6a4c..9b4cd698bec 100644 --- a/arch/arm/mach-omap1/serial.c +++ b/arch/arm/mach-omap1/serial.c @@ -30,9 +30,9 @@ #include #endif -static struct clk * uart1_ck = NULL; -static struct clk * uart2_ck = NULL; -static struct clk * uart3_ck = NULL; +static struct clk * uart1_ck; +static struct clk * uart2_ck; +static struct clk * uart3_ck; static inline unsigned int omap_serial_in(struct plat_serial8250_port *up, int offset) diff --git a/arch/arm/plat-omap/sleep.S b/arch/arm/mach-omap1/sleep.S similarity index 93% rename from arch/arm/plat-omap/sleep.S rename to arch/arm/mach-omap1/sleep.S index 4cd7d292f85..e58295e2d3b 100644 --- a/arch/arm/plat-omap/sleep.S +++ b/arch/arm/mach-omap1/sleep.S @@ -1,5 +1,5 @@ /* - * linux/arch/arm/plat-omap/sleep.S + * linux/arch/arm/mach-omap1/sleep.S * * Low-level OMAP730/1510/1610 sleep/wakeUp support * @@ -383,60 +383,133 @@ ENTRY(omap1610_cpu_suspend) mcr p15, 0, r0, c7, c10, 4 nop - @ load base address of Traffic Controller + @ Load base address of Traffic Controller mov r6, #TCMIF_ASM_BASE & 0xff000000 orr r6, r6, #TCMIF_ASM_BASE & 0x00ff0000 orr r6, r6, #TCMIF_ASM_BASE & 0x0000ff00 - @ prepare to put SDRAM into self-refresh manually + @ Prepare to put SDRAM into self-refresh manually ldr r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] orr r9, r7, #SELF_REFRESH_MODE & 0xff000000 orr r9, r9, #SELF_REFRESH_MODE & 0x000000ff str r9, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] - @ prepare to put EMIFS to Sleep + @ Prepare to put EMIFS to Sleep ldr r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] orr r9, r8, #IDLE_EMIFS_REQUEST & 0xff str r9, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] - @ load base address of ARM_IDLECT1 and ARM_IDLECT2 + @ Load base address of ARM_IDLECT1 and ARM_IDLECT2 mov r4, #CLKGEN_REG_ASM_BASE & 0xff000000 orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x00ff0000 orr r4, r4, #CLKGEN_REG_ASM_BASE & 0x0000ff00 - @ turn off clock domains - @ do not disable PERCK (0x04) + @ Turn off clock domains + @ Do not disable PERCK (0x04) mov r5, #OMAP1610_IDLECT2_SLEEP_VAL & 0xff orr r5, r5, #OMAP1610_IDLECT2_SLEEP_VAL & 0xff00 strh r5, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] - @ request ARM idle + @ Request ARM idle mov r3, #OMAP1610_IDLECT1_SLEEP_VAL & 0xff orr r3, r3, #OMAP1610_IDLECT1_SLEEP_VAL & 0xff00 strh r3, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] - @ disable instruction cache - mrc p15, 0, r9, c1, c0, 0 - bic r2, r9, #0x1000 - mcr p15, 0, r2, c1, c0, 0 - nop - /* * Let's wait for the next wake up event to wake us up. r0 can't be * used here because r0 holds ARM_IDLECT1 */ mov r2, #0 mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt + + @ Errata (HEL3SU467, section 1.4.4) specifies nop-instructions + @ according to this formula: + @ 2 + (4*DPLL_MULT)/DPLL_DIV/ARMDIV + @ Max DPLL_MULT = 18 + @ DPLL_DIV = 1 + @ ARMDIV = 1 + @ => 74 nop-instructions + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @10 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @20 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @30 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @40 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @50 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @60 + nop + nop + nop + nop + nop + nop + nop + nop + nop + nop @70 + nop + nop + nop + nop @74 /* * omap1610_cpu_suspend()'s resume point. * * It will just start executing here, so we'll restore stuff from the * stack. */ - @ re-enable Icache - mcr p15, 0, r9, c1, c0, 0 - - @ reset the ARM_IDLECT1 and ARM_IDLECT2. + @ Restore the ARM_IDLECT1 and ARM_IDLECT2. strh r1, [r4, #ARM_IDLECT2_ASM_OFFSET & 0xff] strh r0, [r4, #ARM_IDLECT1_ASM_OFFSET & 0xff] @@ -444,7 +517,7 @@ ENTRY(omap1610_cpu_suspend) str r7, [r6, #EMIFF_SDRAM_CONFIG_ASM_OFFSET & 0xff] str r8, [r6, #EMIFS_CONFIG_ASM_OFFSET & 0xff] - @ restore regs and return + @ Restore regs and return ldmfd sp!, {r0 - r12, pc} ENTRY(omap1610_cpu_suspend_sz) diff --git a/arch/arm/mach-omap1/time.c b/arch/arm/mach-omap1/time.c index cdbf4d7620c..a85fe6066bc 100644 --- a/arch/arm/mach-omap1/time.c +++ b/arch/arm/mach-omap1/time.c @@ -51,8 +51,6 @@ struct sys_timer omap_timer; -#ifdef CONFIG_OMAP_MPU_TIMER - /* * --------------------------------------------------------------------------- * MPU timer @@ -222,195 +220,6 @@ unsigned long long sched_clock(void) return cycles_2_ns(ticks64); } -#endif /* CONFIG_OMAP_MPU_TIMER */ - -#ifdef CONFIG_OMAP_32K_TIMER - -#ifdef CONFIG_ARCH_OMAP15XX -#error OMAP 32KHz timer does not currently work on 15XX! -#endif - -/* - * --------------------------------------------------------------------------- - * 32KHz OS timer - * - * This currently works only on 16xx, as 1510 does not have the continuous - * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track - * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer - * on 1510 would be possible, but the timer would not be as accurate as - * with the 32KHz synchronized timer. - * --------------------------------------------------------------------------- - */ -#define OMAP_32K_TIMER_BASE 0xfffb9000 -#define OMAP_32K_TIMER_CR 0x08 -#define OMAP_32K_TIMER_TVR 0x00 -#define OMAP_32K_TIMER_TCR 0x04 - -#define OMAP_32K_TICKS_PER_HZ (32768 / HZ) - -/* - * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 - * so with HZ = 100, TVR = 327.68. - */ -#define OMAP_32K_TIMER_TICK_PERIOD ((32768 / HZ) - 1) -#define TIMER_32K_SYNCHRONIZED 0xfffbc410 - -#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ - (((nr_jiffies) * (clock_rate)) / HZ) - -static inline void omap_32k_timer_write(int val, int reg) -{ - omap_writew(val, reg + OMAP_32K_TIMER_BASE); -} - -static inline unsigned long omap_32k_timer_read(int reg) -{ - return omap_readl(reg + OMAP_32K_TIMER_BASE) & 0xffffff; -} - -/* - * The 32KHz synchronized timer is an additional timer on 16xx. - * It is always running. - */ -static inline unsigned long omap_32k_sync_timer_read(void) -{ - return omap_readl(TIMER_32K_SYNCHRONIZED); -} - -static inline void omap_32k_timer_start(unsigned long load_val) -{ - omap_32k_timer_write(load_val, OMAP_32K_TIMER_TVR); - omap_32k_timer_write(0x0f, OMAP_32K_TIMER_CR); -} - -static inline void omap_32k_timer_stop(void) -{ - omap_32k_timer_write(0x0, OMAP_32K_TIMER_CR); -} - -/* - * Rounds down to nearest usec. Note that this will overflow for larger values. - */ -static inline unsigned long omap_32k_ticks_to_usecs(unsigned long ticks_32k) -{ - return (ticks_32k * 5*5*5*5*5*5) >> 9; -} - -/* - * Rounds down to nearest nsec. - */ -static inline unsigned long long -omap_32k_ticks_to_nsecs(unsigned long ticks_32k) -{ - return (unsigned long long) ticks_32k * 1000 * 5*5*5*5*5*5 >> 9; -} - -static unsigned long omap_32k_last_tick = 0; - -/* - * Returns elapsed usecs since last 32k timer interrupt - */ -static unsigned long omap_32k_timer_gettimeoffset(void) -{ - unsigned long now = omap_32k_sync_timer_read(); - return omap_32k_ticks_to_usecs(now - omap_32k_last_tick); -} - -/* - * Returns current time from boot in nsecs. It's OK for this to wrap - * around for now, as it's just a relative time stamp. - */ -unsigned long long sched_clock(void) -{ - return omap_32k_ticks_to_nsecs(omap_32k_sync_timer_read()); -} - -/* - * Timer interrupt for 32KHz timer. When dynamic tick is enabled, this - * function is also called from other interrupts to remove latency - * issues with dynamic tick. In the dynamic tick case, we need to lock - * with irqsave. - */ -static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id, - struct pt_regs *regs) -{ - unsigned long flags; - unsigned long now; - - write_seqlock_irqsave(&xtime_lock, flags); - now = omap_32k_sync_timer_read(); - - while (now - omap_32k_last_tick >= OMAP_32K_TICKS_PER_HZ) { - omap_32k_last_tick += OMAP_32K_TICKS_PER_HZ; - timer_tick(regs); - } - - /* Restart timer so we don't drift off due to modulo or dynamic tick. - * By default we program the next timer to be continuous to avoid - * latencies during high system load. During dynamic tick operation the - * continuous timer can be overridden from pm_idle to be longer. - */ - omap_32k_timer_start(omap_32k_last_tick + OMAP_32K_TICKS_PER_HZ - now); - write_sequnlock_irqrestore(&xtime_lock, flags); - - return IRQ_HANDLED; -} - -#ifdef CONFIG_NO_IDLE_HZ -/* - * Programs the next timer interrupt needed. Called when dynamic tick is - * enabled, and to reprogram the ticks to skip from pm_idle. Note that - * we can keep the timer continuous, and don't need to set it to run in - * one-shot mode. This is because the timer will get reprogrammed again - * after next interrupt. - */ -void omap_32k_timer_reprogram(unsigned long next_tick) -{ - omap_32k_timer_start(JIFFIES_TO_HW_TICKS(next_tick, 32768) + 1); -} - -static struct irqaction omap_32k_timer_irq; -extern struct timer_update_handler timer_update; - -static int omap_32k_timer_enable_dyn_tick(void) -{ - /* No need to reprogram timer, just use the next interrupt */ - return 0; -} - -static int omap_32k_timer_disable_dyn_tick(void) -{ - omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); - return 0; -} - -static struct dyn_tick_timer omap_dyn_tick_timer = { - .enable = omap_32k_timer_enable_dyn_tick, - .disable = omap_32k_timer_disable_dyn_tick, - .reprogram = omap_32k_timer_reprogram, - .handler = omap_32k_timer_interrupt, -}; -#endif /* CONFIG_NO_IDLE_HZ */ - -static struct irqaction omap_32k_timer_irq = { - .name = "32KHz timer", - .flags = SA_INTERRUPT | SA_TIMER, - .handler = omap_32k_timer_interrupt, -}; - -static __init void omap_init_32k_timer(void) -{ - -#ifdef CONFIG_NO_IDLE_HZ - omap_timer.dyn_tick = &omap_dyn_tick_timer; -#endif - - setup_irq(INT_OS_TIMER, &omap_32k_timer_irq); - omap_timer.offset = omap_32k_timer_gettimeoffset; - omap_32k_last_tick = omap_32k_sync_timer_read(); - omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); -} -#endif /* CONFIG_OMAP_32K_TIMER */ /* * --------------------------------------------------------------------------- @@ -419,13 +228,7 @@ static __init void omap_init_32k_timer(void) */ static void __init omap_timer_init(void) { -#if defined(CONFIG_OMAP_MPU_TIMER) omap_init_mpu_timer(); -#elif defined(CONFIG_OMAP_32K_TIMER) - omap_init_32k_timer(); -#else -#error No system timer selected in Kconfig! -#endif } struct sys_timer omap_timer = { diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig index 578880943cf..ab144c3f734 100644 --- a/arch/arm/mach-omap2/Kconfig +++ b/arch/arm/mach-omap2/Kconfig @@ -19,4 +19,8 @@ config MACH_OMAP_GENERIC config MACH_OMAP_H4 bool "OMAP 2420 H4 board" depends on ARCH_OMAP2 && ARCH_OMAP24XX + select GPIOEXPANDER_OMAP +config MACH_OMAP_APOLLON + bool "OMAP 2420 Apollon board" + depends on ARCH_OMAP2 && ARCH_OMAP24XX diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 42041166435..111eaa64258 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -3,11 +3,15 @@ # # Common support -obj-y := irq.o id.o io.o sram-fn.o clock.o mux.o devices.o serial.o +obj-y := irq.o id.o io.o sram-fn.o memory.o prcm.o clock.o mux.o devices.o serial.o obj-$(CONFIG_OMAP_MPU_TIMER) += timer-gp.o +# Power Management +obj-$(CONFIG_PM) += pm.o sleep.o + # Specific board support obj-$(CONFIG_MACH_OMAP_GENERIC) += board-generic.o obj-$(CONFIG_MACH_OMAP_H4) += board-h4.o +obj-$(CONFIG_MACH_OMAP_APOLLON) += board-apollon.o diff --git a/arch/arm/mach-omap2/board-apollon.c b/arch/arm/mach-omap2/board-apollon.c new file mode 100644 index 00000000000..6c6ba172cdf --- /dev/null +++ b/arch/arm/mach-omap2/board-apollon.c @@ -0,0 +1,285 @@ +/* + * linux/arch/arm/mach-omap/omap2/board-apollon.c + * + * Copyright (C) 2005,2006 Samsung Electronics + * Author: Kyungmin Park + * + * Modified from mach-omap/omap2/board-h4.c + * + * Code for apollon OMAP2 board. Should work on many OMAP2 systems where + * the bootloader passes the board-specific data to the kernel. + * Do not put any board specific code to this file; create a new machine + * type if you need custom low-level initializations. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include "prcm-regs.h" + +/* LED & Switch macros */ +#define LED0_GPIO13 13 +#define LED1_GPIO14 14 +#define LED2_GPIO15 15 +#define SW_ENTER_GPIO16 16 +#define SW_UP_GPIO17 17 +#define SW_DOWN_GPIO58 58 + +static struct mtd_partition apollon_partitions[] = { + { + .name = "X-Loader + U-Boot", + .offset = 0, + .size = SZ_128K, + .mask_flags = MTD_WRITEABLE, + }, + { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = SZ_128K, + }, + { + .name = "kernel", + .offset = MTDPART_OFS_APPEND, + .size = SZ_2M, + }, + { + .name = "rootfs", + .offset = MTDPART_OFS_APPEND, + .size = SZ_16M, + }, + { + .name = "filesystem00", + .offset = MTDPART_OFS_APPEND, + .size = SZ_32M, + }, + { + .name = "filesystem01", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct flash_platform_data apollon_flash_data = { + .parts = apollon_partitions, + .nr_parts = ARRAY_SIZE(apollon_partitions), +}; + +static struct resource apollon_flash_resource = { + .start = APOLLON_CS0_BASE, + .end = APOLLON_CS0_BASE + SZ_128K, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device apollon_onenand_device = { + .name = "onenand", + .id = -1, + .dev = { + .platform_data = &apollon_flash_data, + }, + .num_resources = ARRAY_SIZE(&apollon_flash_resource), + .resource = &apollon_flash_resource, +}; + +static struct resource apollon_smc91x_resources[] = { + [0] = { + .start = APOLLON_ETHR_START, /* Physical */ + .end = APOLLON_ETHR_START + 0xf, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = OMAP_GPIO_IRQ(APOLLON_ETHR_GPIO_IRQ), + .end = OMAP_GPIO_IRQ(APOLLON_ETHR_GPIO_IRQ), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device apollon_smc91x_device = { + .name = "smc91x", + .id = -1, + .num_resources = ARRAY_SIZE(apollon_smc91x_resources), + .resource = apollon_smc91x_resources, +}; + +static struct platform_device apollon_lcd_device = { + .name = "apollon_lcd", + .id = -1, +}; + +static struct platform_device *apollon_devices[] __initdata = { + &apollon_onenand_device, + &apollon_smc91x_device, + &apollon_lcd_device, +}; + +static inline void __init apollon_init_smc91x(void) +{ + /* Make sure CS1 timings are correct */ + GPMC_CONFIG1_1 = 0x00011203; + GPMC_CONFIG2_1 = 0x001f1f01; + GPMC_CONFIG3_1 = 0x00080803; + GPMC_CONFIG4_1 = 0x1c091c09; + GPMC_CONFIG5_1 = 0x041f1f1f; + GPMC_CONFIG6_1 = 0x000004c4; + GPMC_CONFIG7_1 = 0x00000f40 | (APOLLON_CS1_BASE >> 24); + udelay(100); + + omap_cfg_reg(W4__24XX_GPIO74); + if (omap_request_gpio(APOLLON_ETHR_GPIO_IRQ) < 0) { + printk(KERN_ERR "Failed to request GPIO%d for smc91x IRQ\n", + APOLLON_ETHR_GPIO_IRQ); + return; + } + omap_set_gpio_direction(APOLLON_ETHR_GPIO_IRQ, 1); +} + +static void __init omap_apollon_init_irq(void) +{ + omap2_init_common_hw(); + omap_init_irq(); + omap_gpio_init(); + apollon_init_smc91x(); +} + +static struct omap_uart_config apollon_uart_config __initdata = { + .enabled_uarts = (1 << 0) | (0 << 1) | (0 << 2), +}; + +static struct omap_mmc_config apollon_mmc_config __initdata = { + .mmc [0] = { + .enabled = 0, + .wire4 = 0, + .wp_pin = -1, + .power_pin = -1, + .switch_pin = -1, + }, +}; + +static struct omap_lcd_config apollon_lcd_config __initdata = { + .ctrl_name = "internal", +}; + +static struct omap_board_config_kernel apollon_config[] = { + { OMAP_TAG_UART, &apollon_uart_config }, + { OMAP_TAG_MMC, &apollon_mmc_config }, + { OMAP_TAG_LCD, &apollon_lcd_config }, +}; + +static void __init apollon_led_init(void) +{ + /* LED0 - AA10 */ + omap_cfg_reg(AA10_242X_GPIO13); + omap_request_gpio(LED0_GPIO13); + omap_set_gpio_direction(LED0_GPIO13, 0); + omap_set_gpio_dataout(LED0_GPIO13, 0); + /* LED1 - AA6 */ + omap_cfg_reg(AA6_242X_GPIO14); + omap_request_gpio(LED1_GPIO14); + omap_set_gpio_direction(LED1_GPIO14, 0); + omap_set_gpio_dataout(LED1_GPIO14, 0); + /* LED2 - AA4 */ + omap_cfg_reg(AA4_242X_GPIO15); + omap_request_gpio(LED2_GPIO15); + omap_set_gpio_direction(LED2_GPIO15, 0); + omap_set_gpio_dataout(LED2_GPIO15, 0); +} + +static irqreturn_t apollon_sw_interrupt(int irq, void *ignored, struct pt_regs *regs) +{ + static unsigned int led0, led1, led2; + + if (irq == OMAP_GPIO_IRQ(SW_ENTER_GPIO16)) + omap_set_gpio_dataout(LED0_GPIO13, led0 ^= 1); + else if (irq == OMAP_GPIO_IRQ(SW_UP_GPIO17)) + omap_set_gpio_dataout(LED1_GPIO14, led1 ^= 1); + else if (irq == OMAP_GPIO_IRQ(SW_DOWN_GPIO58)) + omap_set_gpio_dataout(LED2_GPIO15, led2 ^= 1); + + return IRQ_HANDLED; +} + +static void __init apollon_sw_init(void) +{ + /* Enter SW - Y11 */ + omap_cfg_reg(Y11_242X_GPIO16); + omap_request_gpio(SW_ENTER_GPIO16); + omap_set_gpio_direction(SW_ENTER_GPIO16, 1); + /* Up SW - AA12 */ + omap_cfg_reg(AA12_242X_GPIO17); + omap_request_gpio(SW_UP_GPIO17); + omap_set_gpio_direction(SW_UP_GPIO17, 1); + /* Down SW - AA8 */ + omap_cfg_reg(AA8_242X_GPIO58); + omap_request_gpio(SW_DOWN_GPIO58); + omap_set_gpio_direction(SW_DOWN_GPIO58, 1); + + set_irq_type(OMAP_GPIO_IRQ(SW_ENTER_GPIO16), IRQT_RISING); + if (request_irq(OMAP_GPIO_IRQ(SW_ENTER_GPIO16), &apollon_sw_interrupt, + SA_SHIRQ, "enter sw", + &apollon_sw_interrupt)) + return; + set_irq_type(OMAP_GPIO_IRQ(SW_UP_GPIO17), IRQT_RISING); + if (request_irq(OMAP_GPIO_IRQ(SW_UP_GPIO17), &apollon_sw_interrupt, + SA_SHIRQ, "up sw", + &apollon_sw_interrupt)) + return; + set_irq_type(OMAP_GPIO_IRQ(SW_DOWN_GPIO58), IRQT_RISING); + if (request_irq(OMAP_GPIO_IRQ(SW_DOWN_GPIO58), &apollon_sw_interrupt, + SA_SHIRQ, "down sw", + &apollon_sw_interrupt)) + return; +} + +static void __init omap_apollon_init(void) +{ + apollon_led_init(); + apollon_sw_init(); + + /* REVISIT: where's the correct place */ + omap_cfg_reg(W19_24XX_SYS_NIRQ); + + /* + * Make sure the serial ports are muxed on at this point. + * You have to mux them off in device drivers later on + * if not needed. + */ + platform_add_devices(apollon_devices, ARRAY_SIZE(apollon_devices)); + omap_board_config = apollon_config; + omap_board_config_size = ARRAY_SIZE(apollon_config); + omap_serial_init(); +} + +static void __init omap_apollon_map_io(void) +{ + omap2_map_common_io(); +} + +MACHINE_START(OMAP_APOLLON, "OMAP24xx Apollon") + /* Maintainer: Kyungmin Park */ + .phys_io = 0x48000000, + .io_pg_offst = ((0xd8000000) >> 18) & 0xfffc, + .boot_params = 0x80000100, + .map_io = omap_apollon_map_io, + .init_irq = omap_apollon_init_irq, + .init_machine = omap_apollon_init, + .timer = &omap_timer, +MACHINE_END diff --git a/arch/arm/mach-omap2/board-h4.c b/arch/arm/mach-omap2/board-h4.c index a300d634d8a..4933fce766c 100644 --- a/arch/arm/mach-omap2/board-h4.c +++ b/arch/arm/mach-omap2/board-h4.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include @@ -25,15 +27,57 @@ #include #include +#include #include #include +#include #include #include -#include +#include +#include +#include +#include "prcm-regs.h" #include #include +static unsigned int row_gpios[6] = { 88, 89, 124, 11, 6, 96 }; +static unsigned int col_gpios[7] = { 90, 91, 100, 36, 12, 97, 98 }; + +static int h4_keymap[] = { + KEY(0, 0, KEY_LEFT), + KEY(0, 1, KEY_RIGHT), + KEY(0, 2, KEY_A), + KEY(0, 3, KEY_B), + KEY(0, 4, KEY_C), + KEY(1, 0, KEY_DOWN), + KEY(1, 1, KEY_UP), + KEY(1, 2, KEY_E), + KEY(1, 3, KEY_F), + KEY(1, 4, KEY_G), + KEY(2, 0, KEY_ENTER), + KEY(2, 1, KEY_I), + KEY(2, 2, KEY_J), + KEY(2, 3, KEY_K), + KEY(2, 4, KEY_3), + KEY(3, 0, KEY_M), + KEY(3, 1, KEY_N), + KEY(3, 2, KEY_O), + KEY(3, 3, KEY_P), + KEY(3, 4, KEY_Q), + KEY(4, 0, KEY_R), + KEY(4, 1, KEY_4), + KEY(4, 2, KEY_T), + KEY(4, 3, KEY_U), + KEY(4, 4, KEY_ENTER), + KEY(5, 0, KEY_V), + KEY(5, 1, KEY_W), + KEY(5, 2, KEY_L), + KEY(5, 3, KEY_S), + KEY(5, 4, KEY_ENTER), + 0 +}; + static struct mtd_partition h4_partitions[] = { /* bootloader (U-Boot, etc) in first sector */ { @@ -108,9 +152,123 @@ static struct platform_device h4_smc91x_device = { .resource = h4_smc91x_resources, }; +/* Select between the IrDA and aGPS module + */ +static int h4_select_irda(struct device *dev, int state) +{ + unsigned char expa; + int err = 0; + + if ((err = read_gpio_expa(&expa, 0x21))) { + printk(KERN_ERR "Error reading from I/O expander\n"); + return err; + } + + /* 'P6' enable/disable IRDA_TX and IRDA_RX */ + if (state & IR_SEL) { /* IrDa */ + if ((err = write_gpio_expa(expa | 0x01, 0x21))) { + printk(KERN_ERR "Error writing to I/O expander\n"); + return err; + } + } else { + if ((err = write_gpio_expa(expa & ~0x01, 0x21))) { + printk(KERN_ERR "Error writing to I/O expander\n"); + return err; + } + } + return err; +} + +static void set_trans_mode(void *data) +{ + int *mode = data; + unsigned char expa; + int err = 0; + + if ((err = read_gpio_expa(&expa, 0x20)) != 0) { + printk(KERN_ERR "Error reading from I/O expander\n"); + } + + expa &= ~0x01; + + if (!(*mode & IR_SIRMODE)) { /* MIR/FIR */ + expa |= 0x01; + } + + if ((err = write_gpio_expa(expa, 0x20)) != 0) { + printk(KERN_ERR "Error writing to I/O expander\n"); + } +} + +static int h4_transceiver_mode(struct device *dev, int mode) +{ + struct omap_irda_config *irda_config = dev->platform_data; + + cancel_delayed_work(&irda_config->gpio_expa); + PREPARE_WORK(&irda_config->gpio_expa, set_trans_mode, &mode); + schedule_work(&irda_config->gpio_expa); + + return 0; +} + +static struct omap_irda_config h4_irda_data = { + .transceiver_cap = IR_SIRMODE | IR_MIRMODE | IR_FIRMODE, + .transceiver_mode = h4_transceiver_mode, + .select_irda = h4_select_irda, + .rx_channel = OMAP24XX_DMA_UART3_RX, + .tx_channel = OMAP24XX_DMA_UART3_TX, + .dest_start = OMAP_UART3_BASE, + .src_start = OMAP_UART3_BASE, + .tx_trigger = OMAP24XX_DMA_UART3_TX, + .rx_trigger = OMAP24XX_DMA_UART3_RX, +}; + +static struct resource h4_irda_resources[] = { + [0] = { + .start = INT_24XX_UART3_IRQ, + .end = INT_24XX_UART3_IRQ, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device h4_irda_device = { + .name = "omapirda", + .id = -1, + .dev = { + .platform_data = &h4_irda_data, + }, + .num_resources = 1, + .resource = h4_irda_resources, +}; + +static struct omap_kp_platform_data h4_kp_data = { + .rows = 6, + .cols = 7, + .keymap = h4_keymap, + .rep = 1, + .row_gpios = row_gpios, + .col_gpios = col_gpios, +}; + +static struct platform_device h4_kp_device = { + .name = "omap-keypad", + .id = -1, + .dev = { + .platform_data = &h4_kp_data, + }, +}; + +static struct platform_device h4_lcd_device = { + .name = "lcd_h4", + .id = -1, +}; + static struct platform_device *h4_devices[] __initdata = { &h4_smc91x_device, &h4_flash_device, + &h4_irda_device, + &h4_kp_device, + &h4_lcd_device, }; static inline void __init h4_init_smc91x(void) @@ -157,7 +315,6 @@ static struct omap_mmc_config h4_mmc_config __initdata = { }; static struct omap_lcd_config h4_lcd_config __initdata = { - .panel_name = "h4", .ctrl_name = "internal", }; @@ -174,6 +331,19 @@ static void __init omap_h4_init(void) * You have to mux them off in device drivers later on * if not needed. */ +#if defined(CONFIG_OMAP_IR) || defined(CONFIG_OMAP_IR_MODULE) + omap_cfg_reg(K15_24XX_UART3_TX); + omap_cfg_reg(K14_24XX_UART3_RX); +#endif + +#if defined(CONFIG_KEYBOARD_OMAP) || defined(CONFIG_KEYBOARD_OMAP_MODULE) + if (omap_has_menelaus()) { + row_gpios[5] = 0; + col_gpios[2] = 15; + col_gpios[6] = 18; + } +#endif + platform_add_devices(h4_devices, ARRAY_SIZE(h4_devices)); omap_board_config = h4_config; omap_board_config_size = ARRAY_SIZE(h4_config); diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c index 180f675c906..72eb4bf571a 100644 --- a/arch/arm/mach-omap2/clock.c +++ b/arch/arm/mach-omap2/clock.c @@ -28,14 +28,14 @@ #include #include -#include +#include "prcm-regs.h" +#include "memory.h" #include "clock.h" //#define DOWN_VARIABLE_DPLL 1 /* Experimental */ static struct prcm_config *curr_prcm_set; -static struct memory_timings mem_timings; static u32 curr_perf_level = PRCM_FULL_SPEED; /*------------------------------------------------------------------------- @@ -54,11 +54,13 @@ static void omap2_sys_clk_recalc(struct clk * clk) static u32 omap2_get_dpll_rate(struct clk * tclk) { - int dpll_clk, dpll_mult, dpll_div, amult; + long long dpll_clk; + int dpll_mult, dpll_div, amult; dpll_mult = (CM_CLKSEL1_PLL >> 12) & 0x03ff; /* 10 bits */ dpll_div = (CM_CLKSEL1_PLL >> 8) & 0x0f; /* 4 bits */ - dpll_clk = (tclk->parent->rate * dpll_mult) / (dpll_div + 1); + dpll_clk = (long long)tclk->parent->rate * dpll_mult; + do_div(dpll_clk, dpll_div + 1); amult = CM_CLKSEL2_PLL & 0x3; dpll_clk *= amult; @@ -385,75 +387,23 @@ static u32 omap2_dll_force_needed(void) return 0; } -static void omap2_init_memory_params(u32 force_lock_to_unlock_mode) -{ - unsigned long dll_cnt; - u32 fast_dll = 0; - - mem_timings.m_type = !((SDRC_MR_0 & 0x3) == 0x1); /* DDR = 1, SDR = 0 */ - - /* 2422 es2.05 and beyond has a single SIP DDR instead of 2 like others. - * In the case of 2422, its ok to use CS1 instead of CS0. - */ - -#if 0 /* FIXME: Enable after 24xx cpu detection works */ - ctype = get_cpu_type(); - if (cpu_is_omap2422()) - mem_timings.base_cs = 1; - else -#endif - mem_timings.base_cs = 0; - - if (mem_timings.m_type != M_DDR) - return; - - /* With DDR we need to determine the low frequency DLL value */ - if (((mem_timings.fast_dll_ctrl & (1 << 2)) == M_LOCK_CTRL)) - mem_timings.dll_mode = M_UNLOCK; - else - mem_timings.dll_mode = M_LOCK; - - if (mem_timings.base_cs == 0) { - fast_dll = SDRC_DLLA_CTRL; - dll_cnt = SDRC_DLLA_STATUS & 0xff00; - } else { - fast_dll = SDRC_DLLB_CTRL; - dll_cnt = SDRC_DLLB_STATUS & 0xff00; - } - if (force_lock_to_unlock_mode) { - fast_dll &= ~0xff00; - fast_dll |= dll_cnt; /* Current lock mode */ - } - mem_timings.fast_dll_ctrl = fast_dll; - - /* No disruptions, DDR will be offline & C-ABI not followed */ - omap2_sram_ddr_init(&mem_timings.slow_dll_ctrl, - mem_timings.fast_dll_ctrl, - mem_timings.base_cs, - force_lock_to_unlock_mode); - mem_timings.slow_dll_ctrl &= 0xff00; /* Keep lock value */ - - /* Turn status into unlock ctrl */ - mem_timings.slow_dll_ctrl |= - ((mem_timings.fast_dll_ctrl & 0xF) | (1 << 2)); - - /* 90 degree phase for anything below 133Mhz */ - mem_timings.slow_dll_ctrl |= (1 << 1); -} - static u32 omap2_reprogram_sdrc(u32 level, u32 force) { + u32 slow_dll_ctrl, fast_dll_ctrl, m_type; u32 prev = curr_perf_level, flags; if ((curr_perf_level == level) && !force) return prev; + m_type = omap2_memory_get_type(); + slow_dll_ctrl = omap2_memory_get_slow_dll_ctrl(); + fast_dll_ctrl = omap2_memory_get_fast_dll_ctrl(); + if (level == PRCM_HALF_SPEED) { local_irq_save(flags); PRCM_VOLTSETUP = 0xffff; omap2_sram_reprogram_sdrc(PRCM_HALF_SPEED, - mem_timings.slow_dll_ctrl, - mem_timings.m_type); + slow_dll_ctrl, m_type); curr_perf_level = PRCM_HALF_SPEED; local_irq_restore(flags); } @@ -461,8 +411,7 @@ static u32 omap2_reprogram_sdrc(u32 level, u32 force) local_irq_save(flags); PRCM_VOLTSETUP = 0xffff; omap2_sram_reprogram_sdrc(PRCM_FULL_SPEED, - mem_timings.fast_dll_ctrl, - mem_timings.m_type); + fast_dll_ctrl, m_type); curr_perf_level = PRCM_FULL_SPEED; local_irq_restore(flags); } @@ -650,7 +599,7 @@ static u32 omap2_get_clksel(u32 *div_sel, u32 *field_mask, case 13: /* dss2 */ mask = 0x1; break; case 25: /* usb */ - mask = 0xf; break; + mask = 0x7; break; } } diff --git a/arch/arm/mach-omap2/clock.h b/arch/arm/mach-omap2/clock.h index 6cab20b1d3c..6c78d471fab 100644 --- a/arch/arm/mach-omap2/clock.h +++ b/arch/arm/mach-omap2/clock.h @@ -33,20 +33,6 @@ static u32 omap2_clksel_get_divisor(struct clk *clk); #define RATE_IN_242X (1 << 0) #define RATE_IN_243X (1 << 1) -/* Memory timings */ -#define M_DDR 1 -#define M_LOCK_CTRL (1 << 2) -#define M_UNLOCK 0 -#define M_LOCK 1 - -struct memory_timings { - u32 m_type; /* ddr = 1, sdr = 0 */ - u32 dll_mode; /* use lock mode = 1, unlock mode = 0 */ - u32 slow_dll_ctrl; /* unlock mode, dll value for slow speed */ - u32 fast_dll_ctrl; /* unlock mode, dll value for fast speed */ - u32 base_cs; /* base chip select to use for calculations */ -}; - /* Key dividers which make up a PRCM set. Ratio's for a PRCM are mandated. * xtal_speed, dpll_speed, mpu_speed, CM_CLKSEL_MPU,CM_CLKSEL_DSP * CM_CLKSEL_GFX, CM_CLKSEL1_CORE, CM_CLKSEL1_PLL CM_CLKSEL2_PLL, CM_CLKSEL_MDM @@ -731,6 +717,16 @@ static struct clk sys_clkout2 = { .recalc = &omap2_clksel_recalc, }; +static struct clk emul_ck = { + .name = "emul_ck", + .parent = &func_54m_ck, + .flags = CLOCK_IN_OMAP242X, + .enable_reg = (void __iomem *)&PRCM_CLKEMUL_CTRL, + .enable_bit = 0, + .recalc = &omap2_propagate_rate, + +}; + /* * MPU clock domain * Clocks: @@ -1702,7 +1698,8 @@ static struct clk hdq_fck = { }; static struct clk i2c2_ick = { - .name = "i2c2_ick", + .name = "i2c_ick", + .id = 2, .parent = &l4_ck, .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, .enable_reg = (void __iomem *)&CM_ICLKEN1_CORE, @@ -1711,7 +1708,8 @@ static struct clk i2c2_ick = { }; static struct clk i2c2_fck = { - .name = "i2c2_fck", + .name = "i2c_fck", + .id = 2, .parent = &func_12m_ck, .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, .enable_reg = (void __iomem *)&CM_FCLKEN1_CORE, @@ -1729,7 +1727,8 @@ static struct clk i2chs2_fck = { }; static struct clk i2c1_ick = { - .name = "i2c1_ick", + .name = "i2c_ick", + .id = 1, .parent = &l4_ck, .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, .enable_reg = (void __iomem *)&CM_ICLKEN1_CORE, @@ -1738,7 +1737,8 @@ static struct clk i2c1_ick = { }; static struct clk i2c1_fck = { - .name = "i2c1_fck", + .name = "i2c_fck", + .id = 1, .parent = &func_12m_ck, .flags = CLOCK_IN_OMAP242X | CLOCK_IN_OMAP243X, .enable_reg = (void __iomem *)&CM_FCLKEN1_CORE, @@ -1971,6 +1971,7 @@ static struct clk *onchip_clks[] = { &wdt1_osc_ck, &sys_clkout, &sys_clkout2, + &emul_ck, /* mpu domain clocks */ &mpu_ck, /* dsp domain clocks */ diff --git a/arch/arm/mach-omap2/devices.c b/arch/arm/mach-omap2/devices.c index 7181edb8935..def9e5370ed 100644 --- a/arch/arm/mach-omap2/devices.c +++ b/arch/arm/mach-omap2/devices.c @@ -74,6 +74,47 @@ static void omap_init_i2c(void) {} #endif +#if defined(CONFIG_OMAP_STI) + +#define OMAP2_STI_BASE IO_ADDRESS(0x48068000) +#define OMAP2_STI_CHANNEL_BASE 0x54000000 +#define OMAP2_STI_IRQ 4 + +static struct resource sti_resources[] = { + { + .start = OMAP2_STI_BASE, + .end = OMAP2_STI_BASE + 0x7ff, + .flags = IORESOURCE_MEM, + }, + { + .start = OMAP2_STI_CHANNEL_BASE, + .end = OMAP2_STI_CHANNEL_BASE + SZ_64K - 1, + .flags = IORESOURCE_MEM, + }, + { + .start = OMAP2_STI_IRQ, + .flags = IORESOURCE_IRQ, + } +}; + +static struct platform_device sti_device = { + .name = "sti", + .id = -1, + .dev = { + .release = omap_nop_release, + }, + .num_resources = ARRAY_SIZE(sti_resources), + .resource = sti_resources, +}; + +static inline void omap_init_sti(void) +{ + platform_device_register(&sti_device); +} +#else +static inline void omap_init_sti(void) {} +#endif + /*-------------------------------------------------------------------------*/ static int __init omap2_init_devices(void) @@ -82,6 +123,7 @@ static int __init omap2_init_devices(void) * in alphabetical order so they're easier to sort through. */ omap_init_i2c(); + omap_init_sti(); return 0; } diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c index 8ea67bf196a..7d5711611f2 100644 --- a/arch/arm/mach-omap2/io.c +++ b/arch/arm/mach-omap2/io.c @@ -16,9 +16,13 @@ #include #include -#include +#include #include + +#include + #include +#include extern void omap_sram_init(void); extern int omap2_clk_init(void); @@ -43,11 +47,24 @@ static struct map_desc omap2_io_desc[] __initdata = { } }; -void __init omap_map_common_io(void) +void __init omap2_map_common_io(void) { iotable_init(omap2_io_desc, ARRAY_SIZE(omap2_io_desc)); + + /* Normally devicemaps_init() would flush caches and tlb after + * mdesc->map_io(), but we must also do it here because of the CPU + * revision check below. + */ + local_flush_tlb_all(); + flush_cache_all(); + omap2_check_revision(); omap_sram_init(); + omapfb_reserve_mem(); +} + +void __init omap2_init_common_hw(void) +{ omap2_mux_init(); omap2_clk_init(); } diff --git a/arch/arm/mach-omap2/memory.c b/arch/arm/mach-omap2/memory.c new file mode 100644 index 00000000000..1d925d69fc3 --- /dev/null +++ b/arch/arm/mach-omap2/memory.c @@ -0,0 +1,102 @@ +/* + * linux/arch/arm/mach-omap2/memory.c + * + * Memory timing related functions for OMAP24XX + * + * Copyright (C) 2005 Texas Instruments Inc. + * Richard Woodruff + * + * Copyright (C) 2005 Nokia Corporation + * Tony Lindgren + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "prcm-regs.h" +#include "memory.h" + +static struct memory_timings mem_timings; + +u32 omap2_memory_get_slow_dll_ctrl(void) +{ + return mem_timings.slow_dll_ctrl; +} + +u32 omap2_memory_get_fast_dll_ctrl(void) +{ + return mem_timings.fast_dll_ctrl; +} + +u32 omap2_memory_get_type(void) +{ + return mem_timings.m_type; +} + +void omap2_init_memory_params(u32 force_lock_to_unlock_mode) +{ + unsigned long dll_cnt; + u32 fast_dll = 0; + + mem_timings.m_type = !((SDRC_MR_0 & 0x3) == 0x1); /* DDR = 1, SDR = 0 */ + + /* 2422 es2.05 and beyond has a single SIP DDR instead of 2 like others. + * In the case of 2422, its ok to use CS1 instead of CS0. + */ + if (cpu_is_omap2422()) + mem_timings.base_cs = 1; + else + mem_timings.base_cs = 0; + + if (mem_timings.m_type != M_DDR) + return; + + /* With DDR we need to determine the low frequency DLL value */ + if (((mem_timings.fast_dll_ctrl & (1 << 2)) == M_LOCK_CTRL)) + mem_timings.dll_mode = M_UNLOCK; + else + mem_timings.dll_mode = M_LOCK; + + if (mem_timings.base_cs == 0) { + fast_dll = SDRC_DLLA_CTRL; + dll_cnt = SDRC_DLLA_STATUS & 0xff00; + } else { + fast_dll = SDRC_DLLB_CTRL; + dll_cnt = SDRC_DLLB_STATUS & 0xff00; + } + if (force_lock_to_unlock_mode) { + fast_dll &= ~0xff00; + fast_dll |= dll_cnt; /* Current lock mode */ + } + /* set fast timings with DLL filter disabled */ + mem_timings.fast_dll_ctrl = (fast_dll | (3 << 8)); + + /* No disruptions, DDR will be offline & C-ABI not followed */ + omap2_sram_ddr_init(&mem_timings.slow_dll_ctrl, + mem_timings.fast_dll_ctrl, + mem_timings.base_cs, + force_lock_to_unlock_mode); + mem_timings.slow_dll_ctrl &= 0xff00; /* Keep lock value */ + + /* Turn status into unlock ctrl */ + mem_timings.slow_dll_ctrl |= + ((mem_timings.fast_dll_ctrl & 0xF) | (1 << 2)); + + /* 90 degree phase for anything below 133Mhz + disable DLL filter */ + mem_timings.slow_dll_ctrl |= ((1 << 1) | (3 << 8)); +} diff --git a/arch/arm/mach-omap2/memory.h b/arch/arm/mach-omap2/memory.h new file mode 100644 index 00000000000..d212eea83a0 --- /dev/null +++ b/arch/arm/mach-omap2/memory.h @@ -0,0 +1,34 @@ +/* + * linux/arch/arm/mach-omap2/memory.h + * + * Interface for memory timing related functions for OMAP24XX + * + * Copyright (C) 2005 Texas Instruments Inc. + * Richard Woodruff + * + * Copyright (C) 2005 Nokia Corporation + * Tony Lindgren + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* Memory timings */ +#define M_DDR 1 +#define M_LOCK_CTRL (1 << 2) +#define M_UNLOCK 0 +#define M_LOCK 1 + +struct memory_timings { + u32 m_type; /* ddr = 1, sdr = 0 */ + u32 dll_mode; /* use lock mode = 1, unlock mode = 0 */ + u32 slow_dll_ctrl; /* unlock mode, dll value for slow speed */ + u32 fast_dll_ctrl; /* unlock mode, dll value for fast speed */ + u32 base_cs; /* base chip select to use for calculations */ +}; + +extern void omap2_init_memory_params(u32 force_lock_to_unlock_mode); +extern u32 omap2_memory_get_slow_dll_ctrl(void); +extern u32 omap2_memory_get_fast_dll_ctrl(void); +extern u32 omap2_memory_get_type(void); diff --git a/arch/arm/mach-omap2/mux.c b/arch/arm/mach-omap2/mux.c index ea4654815dd..1197dc38c20 100644 --- a/arch/arm/mach-omap2/mux.c +++ b/arch/arm/mach-omap2/mux.c @@ -50,9 +50,54 @@ MUX_CFG_24XX("H19_24XX_I2C2_SDA", 0x114, 0, 0, 0, 1) /* Menelaus interrupt */ MUX_CFG_24XX("W19_24XX_SYS_NIRQ", 0x12c, 0, 1, 1, 1) +/* 24xx clocks */ +MUX_CFG_24XX("W14_24XX_SYS_CLKOUT", 0x137, 0, 1, 1, 1) + +/* 24xx McBSP */ +MUX_CFG_24XX("Y15_24XX_MCBSP2_CLKX", 0x124, 1, 1, 0, 1) +MUX_CFG_24XX("R14_24XX_MCBSP2_FSX", 0x125, 1, 1, 0, 1) +MUX_CFG_24XX("W15_24XX_MCBSP2_DR", 0x126, 1, 1, 0, 1) +MUX_CFG_24XX("V15_24XX_MCBSP2_DX", 0x127, 1, 1, 0, 1) + /* 24xx GPIO */ +MUX_CFG_24XX("M21_242X_GPIO11", 0x0c9, 3, 1, 1, 1) +MUX_CFG_24XX("AA10_242X_GPIO13", 0x0e5, 3, 0, 0, 1) +MUX_CFG_24XX("AA6_242X_GPIO14", 0x0e6, 3, 0, 0, 1) +MUX_CFG_24XX("AA4_242X_GPIO15", 0x0e7, 3, 0, 0, 1) +MUX_CFG_24XX("Y11_242X_GPIO16", 0x0e8, 3, 0, 0, 1) +MUX_CFG_24XX("AA12_242X_GPIO17", 0x0e9, 3, 0, 0, 1) +MUX_CFG_24XX("AA8_242X_GPIO58", 0x0ea, 3, 0, 0, 1) MUX_CFG_24XX("Y20_24XX_GPIO60", 0x12c, 3, 0, 0, 1) +MUX_CFG_24XX("W4__24XX_GPIO74", 0x0f2, 3, 0, 0, 1) MUX_CFG_24XX("M15_24XX_GPIO92", 0x10a, 3, 0, 0, 1) +MUX_CFG_24XX("V14_24XX_GPIO117", 0x128, 3, 1, 0, 1) + +/* TSC IRQ */ +MUX_CFG_24XX("P20_24XX_TSC_IRQ", 0x108, 0, 0, 0, 1) + +/* UART3 */ +MUX_CFG_24XX("K15_24XX_UART3_TX", 0x118, 0, 0, 0, 1) +MUX_CFG_24XX("K14_24XX_UART3_RX", 0x119, 0, 0, 0, 1) + +/* Keypad GPIO*/ +MUX_CFG_24XX("T19_24XX_KBR0", 0x106, 3, 1, 1, 1) +MUX_CFG_24XX("R19_24XX_KBR1", 0x107, 3, 1, 1, 1) +MUX_CFG_24XX("V18_24XX_KBR2", 0x139, 3, 1, 1, 1) +MUX_CFG_24XX("M21_24XX_KBR3", 0xc9, 3, 1, 1, 1) +MUX_CFG_24XX("E5__24XX_KBR4", 0x138, 3, 1, 1, 1) +MUX_CFG_24XX("M18_24XX_KBR5", 0x10e, 3, 1, 1, 1) +MUX_CFG_24XX("R20_24XX_KBC0", 0x108, 3, 0, 0, 1) +MUX_CFG_24XX("M14_24XX_KBC1", 0x109, 3, 0, 0, 1) +MUX_CFG_24XX("H19_24XX_KBC2", 0x114, 3, 0, 0, 1) +MUX_CFG_24XX("V17_24XX_KBC3", 0x135, 3, 0, 0, 1) +MUX_CFG_24XX("P21_24XX_KBC4", 0xca, 3, 0, 0, 1) +MUX_CFG_24XX("L14_24XX_KBC5", 0x10f, 3, 0, 0, 1) +MUX_CFG_24XX("N19_24XX_KBC6", 0x110, 3, 0, 0, 1) + +/* 24xx Menelaus Keypad GPIO */ +MUX_CFG_24XX("B3__24XX_KBR5", 0x30, 3, 1, 1, 1) +MUX_CFG_24XX("AA4_24XX_KBC2", 0xe7, 3, 0, 0, 1) +MUX_CFG_24XX("B13_24XX_KBC6", 0x110, 3, 0, 0, 1) }; diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c new file mode 100644 index 00000000000..562168fa2b1 --- /dev/null +++ b/arch/arm/mach-omap2/pm.c @@ -0,0 +1,149 @@ +/* + * linux/arch/arm/mach-omap2/pm.c + * + * OMAP2 Power Management Routines + * + * Copyright (C) 2006 Nokia Corporation + * Tony Lindgren + * + * Copyright (C) 2005 Texas Instruments, Inc. + * Richard Woodruff + * + * Based on pm.c for omap1 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static struct clk *vclk; +static void (*omap2_sram_idle)(void); +static void (*omap2_sram_suspend)(int dllctrl, int cpu_rev); +static void (*saved_idle)(void); + +void omap2_pm_idle(void) +{ + local_irq_disable(); + local_fiq_disable(); + if (need_resched()) { + local_fiq_enable(); + local_irq_enable(); + return; + } + + /* + * Since an interrupt may set up a timer, we don't want to + * reprogram the hardware timer with interrupts enabled. + * Re-enable interrupts only after returning from idle. + */ + timer_dyn_reprogram(); + + omap2_sram_idle(); + local_fiq_enable(); + local_irq_enable(); +} + +static int omap2_pm_prepare(suspend_state_t state) +{ + int error = 0; + + /* We cannot sleep in idle until we have resumed */ + saved_idle = pm_idle; + pm_idle = NULL; + + switch (state) + { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + break; + + case PM_SUSPEND_DISK: + return -ENOTSUPP; + + default: + return -EINVAL; + } + + return error; +} + +static int omap2_pm_enter(suspend_state_t state) +{ + switch (state) + { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + /* FIXME: Add suspend */ + break; + + case PM_SUSPEND_DISK: + return -ENOTSUPP; + + default: + return -EINVAL; + } + + return 0; +} + +static int omap2_pm_finish(suspend_state_t state) +{ + pm_idle = saved_idle; + return 0; +} + +static struct pm_ops omap_pm_ops = { + .pm_disk_mode = 0, + .prepare = omap2_pm_prepare, + .enter = omap2_pm_enter, + .finish = omap2_pm_finish, +}; + +int __init omap2_pm_init(void) +{ + printk("Power Management for TI OMAP.\n"); + + vclk = clk_get(NULL, "virt_prcm_set"); + if (IS_ERR(vclk)) { + printk(KERN_ERR "Could not get PM vclk\n"); + return -ENODEV; + } + + /* + * We copy the assembler sleep/wakeup routines to SRAM. + * These routines need to be in SRAM as that's the only + * memory the MPU can see when it wakes up. + */ + omap2_sram_idle = omap_sram_push(omap24xx_idle_loop_suspend, + omap24xx_idle_loop_suspend_sz); + + omap2_sram_suspend = omap_sram_push(omap24xx_cpu_suspend, + omap24xx_cpu_suspend_sz); + + pm_set_ops(&omap_pm_ops); + pm_idle = omap2_pm_idle; + + return 0; +} + +__initcall(omap2_pm_init); diff --git a/arch/arm/mach-omap2/prcm.h b/arch/arm/mach-omap2/prcm-regs.h similarity index 70% rename from arch/arm/mach-omap2/prcm.h rename to arch/arm/mach-omap2/prcm-regs.h index 2eb89b936c8..22ac7be4f78 100644 --- a/arch/arm/mach-omap2/prcm.h +++ b/arch/arm/mach-omap2/prcm-regs.h @@ -1,5 +1,7 @@ /* - * prcm.h - Access definations for use in OMAP24XX clock and power management + * linux/arch/arm/mach-omap2/prcm-reg.h + * + * OMAP24XX Power Reset and Clock Management (PRCM) registers * * Copyright (C) 2005 Texas Instruments, Inc. * @@ -18,8 +20,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#ifndef __ASM_ARM_ARCH_DPM_PRCM_H -#define __ASM_ARM_ARCH_DPM_PRCM_H +#ifndef __ARCH_ARM_MACH_OMAP2_PRCM_H +#define __ARCH_ARM_MACH_OMAP2_PRCM_H /* SET_PERFORMANCE_LEVEL PARAMETERS */ #define PRCM_HALF_SPEED 1 @@ -159,54 +161,63 @@ #define CM_FCLKEN_MDM PRCM_REG32(0xC00) #define CM_ICLKEN_MDM PRCM_REG32(0xC10) #define CM_IDLEST_MDM PRCM_REG32(0xC20) +#define CM_AUTOIDLE_MDM PRCM_REG32(0xC30) #define CM_CLKSEL_MDM PRCM_REG32(0xC40) - -/* FIXME: Move to header for 2430 */ -#define DISP_BASE (OMAP24XX_L4_IO_BASE+0x50000) +#define CM_CLKSTCTRL_MDM PRCM_REG32(0xC48) +#define RM_RSTCTRL_MDM PRCM_REG32(0xC50) +#define RM_RSTST_MDM PRCM_REG32(0xC58) +#define PM_WKEN_MDM PRCM_REG32(0xCA0) +#define PM_WKST_MDM PRCM_REG32(0xCB0) +#define PM_WKDEP_MDM PRCM_REG32(0xCC8) +#define PM_PWSTCTRL_MDM PRCM_REG32(0xCE0) +#define PM_PWSTST_MDM PRCM_REG32(0xCE4) + +#define OMAP24XX_L4_IO_BASE 0x48000000 + +#define DISP_BASE (OMAP24XX_L4_IO_BASE + 0x50000) #define DISP_REG32(offset) __REG32(DISP_BASE + (offset)) -#define GPMC_BASE (OMAP24XX_GPMC_BASE) -#define GPMC_REG32(offset) __REG32(GPMC_BASE + (offset)) +#define OMAP24XX_GPMC_BASE (L3_24XX_BASE + 0xa000) +#define GPMC_REG32(offset) __REG32(OMAP24XX_GPMC_BASE + (offset)) -#define GPT1_BASE (OMAP24XX_GPT1) +/* FIXME: Move these to timer code */ +#define GPT1_BASE (0x48028000) #define GPT1_REG32(offset) __REG32(GPT1_BASE + (offset)) /* Misc sysconfig */ #define DISPC_SYSCONFIG DISP_REG32(0x410) -#define SPI_BASE (OMAP24XX_L4_IO_BASE+0x98000) +#define SPI_BASE (OMAP24XX_L4_IO_BASE + 0x98000) #define MCSPI1_SYSCONFIG __REG32(SPI_BASE + 0x10) -#define MCSPI2_SYSCONFIG __REG32(SPI_BASE+0x2000 + 0x10) - -//#define DSP_MMU_SYSCONFIG 0x5A000010 -#define CAMERA_MMU_SYSCONFIG __REG32(DISP_BASE+0x2C10) -//#define IVA_MMU_SYSCONFIG 0x5D000010 -//#define DSP_DMA_SYSCONFIG 0x00FCC02C -#define CAMERA_DMA_SYSCONFIG __REG32(DISP_BASE+0x282C) -#define SYSTEM_DMA_SYSCONFIG __REG32(DISP_BASE+0x602C) +#define MCSPI2_SYSCONFIG __REG32(SPI_BASE + 0x2000 + 0x10) +#define MCSPI3_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE + 0xb8010) + +#define CAMERA_MMU_SYSCONFIG __REG32(DISP_BASE + 0x2C10) +#define CAMERA_DMA_SYSCONFIG __REG32(DISP_BASE + 0x282C) +#define SYSTEM_DMA_SYSCONFIG __REG32(DISP_BASE + 0x602C) #define GPMC_SYSCONFIG GPMC_REG32(0x010) -#define MAILBOXES_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE+0x94010) -#define UART1_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE+0x6A054) -#define UART2_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE+0x6C054) -#define UART3_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE+0x6E054) -//#define IVA_SYSCONFIG 0x5C060010 -#define SDRC_SYSCONFIG __REG32(OMAP24XX_SDRC_BASE+0x10) -#define SMS_SYSCONFIG __REG32(OMAP24XX_SMS_BASE+0x10) -#define SSI_SYSCONFIG __REG32(DISP_BASE+0x8010) -//#define VLYNQ_SYSCONFIG 0x67FFFE10 +#define MAILBOXES_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE + 0x94010) +#define UART1_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE + 0x6A054) +#define UART2_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE + 0x6C054) +#define UART3_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE + 0x6E054) +#define SDRC_SYSCONFIG __REG32(OMAP24XX_SDRC_BASE + 0x10) +#define OMAP24XX_SMS_BASE (L3_24XX_BASE + 0x8000) +#define SMS_SYSCONFIG __REG32(OMAP24XX_SMS_BASE + 0x10) +#define SSI_SYSCONFIG __REG32(DISP_BASE + 0x8010) /* rkw - good cannidates for PM_ to start what nm was trying */ -#define OMAP24XX_GPT2 (OMAP24XX_L4_IO_BASE+0x2A000) -#define OMAP24XX_GPT3 (OMAP24XX_L4_IO_BASE+0x78000) -#define OMAP24XX_GPT4 (OMAP24XX_L4_IO_BASE+0x7A000) -#define OMAP24XX_GPT5 (OMAP24XX_L4_IO_BASE+0x7C000) -#define OMAP24XX_GPT6 (OMAP24XX_L4_IO_BASE+0x7E000) -#define OMAP24XX_GPT7 (OMAP24XX_L4_IO_BASE+0x80000) -#define OMAP24XX_GPT8 (OMAP24XX_L4_IO_BASE+0x82000) -#define OMAP24XX_GPT9 (OMAP24XX_L4_IO_BASE+0x84000) -#define OMAP24XX_GPT10 (OMAP24XX_L4_IO_BASE+0x86000) -#define OMAP24XX_GPT11 (OMAP24XX_L4_IO_BASE+0x88000) -#define OMAP24XX_GPT12 (OMAP24XX_L4_IO_BASE+0x8A000) - +#define OMAP24XX_GPT2 (OMAP24XX_L4_IO_BASE + 0x2A000) +#define OMAP24XX_GPT3 (OMAP24XX_L4_IO_BASE + 0x78000) +#define OMAP24XX_GPT4 (OMAP24XX_L4_IO_BASE + 0x7A000) +#define OMAP24XX_GPT5 (OMAP24XX_L4_IO_BASE + 0x7C000) +#define OMAP24XX_GPT6 (OMAP24XX_L4_IO_BASE + 0x7E000) +#define OMAP24XX_GPT7 (OMAP24XX_L4_IO_BASE + 0x80000) +#define OMAP24XX_GPT8 (OMAP24XX_L4_IO_BASE + 0x82000) +#define OMAP24XX_GPT9 (OMAP24XX_L4_IO_BASE + 0x84000) +#define OMAP24XX_GPT10 (OMAP24XX_L4_IO_BASE + 0x86000) +#define OMAP24XX_GPT11 (OMAP24XX_L4_IO_BASE + 0x88000) +#define OMAP24XX_GPT12 (OMAP24XX_L4_IO_BASE + 0x8A000) + +/* FIXME: Move these to timer code */ #define GPTIMER1_SYSCONFIG GPT1_REG32(0x010) #define GPTIMER2_SYSCONFIG __REG32(OMAP24XX_GPT2 + 0x10) #define GPTIMER3_SYSCONFIG __REG32(OMAP24XX_GPT3 + 0x10) @@ -220,12 +231,18 @@ #define GPTIMER11_SYSCONFIG __REG32(OMAP24XX_GPT11 + 0x10) #define GPTIMER12_SYSCONFIG __REG32(OMAP24XX_GPT12 + 0x10) -#define GPIOX_BASE(X) (OMAP24XX_GPIO_BASE+(0x2000*((X)-1))) +/* FIXME: Move these to gpio code */ +#define OMAP24XX_GPIO_BASE 0x48018000 +#define GPIOX_BASE(X) (OMAP24XX_GPIO_BASE + (0x2000 * ((X) - 1))) + +#define GPIO1_SYSCONFIG __REG32((GPIOX_BASE(1) + 0x10)) +#define GPIO2_SYSCONFIG __REG32((GPIOX_BASE(2) + 0x10)) +#define GPIO3_SYSCONFIG __REG32((GPIOX_BASE(3) + 0x10)) +#define GPIO4_SYSCONFIG __REG32((GPIOX_BASE(4) + 0x10)) -#define GPIO1_SYSCONFIG __REG32((GPIOX_BASE(1)+0x10)) -#define GPIO2_SYSCONFIG __REG32((GPIOX_BASE(2)+0x10)) -#define GPIO3_SYSCONFIG __REG32((GPIOX_BASE(3)+0x10)) -#define GPIO4_SYSCONFIG __REG32((GPIOX_BASE(4)+0x10)) +#if defined(CONFIG_ARCH_OMAP243X) +#define GPIO5_SYSCONFIG __REG32((OMAP24XX_GPIO5_BASE + 0x10)) +#endif /* GP TIMER 1 */ #define GPTIMER1_TISTAT GPT1_REG32(0x014) @@ -243,15 +260,15 @@ #define GPTIMER1_TCAR2 GPT1_REG32(0x044) /* rkw -- base fix up please... */ -#define GPTIMER3_TISR __REG32(OMAP24XX_L4_IO_BASE+0x78018) +#define GPTIMER3_TISR __REG32(OMAP24XX_L4_IO_BASE + 0x78018) /* SDRC */ -#define SDRC_DLLA_CTRL __REG32(OMAP24XX_SDRC_BASE+0x060) -#define SDRC_DLLA_STATUS __REG32(OMAP24XX_SDRC_BASE+0x064) -#define SDRC_DLLB_CTRL __REG32(OMAP24XX_SDRC_BASE+0x068) -#define SDRC_DLLB_STATUS __REG32(OMAP24XX_SDRC_BASE+0x06C) -#define SDRC_POWER __REG32(OMAP24XX_SDRC_BASE+0x070) -#define SDRC_MR_0 __REG32(OMAP24XX_SDRC_BASE+0x084) +#define SDRC_DLLA_CTRL __REG32(OMAP24XX_SDRC_BASE + 0x060) +#define SDRC_DLLA_STATUS __REG32(OMAP24XX_SDRC_BASE + 0x064) +#define SDRC_DLLB_CTRL __REG32(OMAP24XX_SDRC_BASE + 0x068) +#define SDRC_DLLB_STATUS __REG32(OMAP24XX_SDRC_BASE + 0x06C) +#define SDRC_POWER __REG32(OMAP24XX_SDRC_BASE + 0x070) +#define SDRC_MR_0 __REG32(OMAP24XX_SDRC_BASE + 0x084) /* GPIO 1 */ #define GPIO1_BASE GPIOX_BASE(1) @@ -278,6 +295,8 @@ #define GPIO2_DATAIN GPIO2_REG32(0x038) #define GPIO2_OE GPIO2_REG32(0x034) #define GPIO2_DATAOUT GPIO2_REG32(0x03C) +#define GPIO2_DEBOUNCENABLE GPIO2_REG32(0x050) +#define GPIO2_DEBOUNCINGTIME GPIO2_REG32(0x054) /* GPIO 3 */ #define GPIO3_BASE GPIOX_BASE(3) @@ -294,6 +313,8 @@ #define GPIO3_DATAOUT GPIO3_REG32(0x03C) #define GPIO3_DEBOUNCENABLE GPIO3_REG32(0x050) #define GPIO3_DEBOUNCINGTIME GPIO3_REG32(0x054) +#define GPIO3_DEBOUNCENABLE GPIO3_REG32(0x050) +#define GPIO3_DEBOUNCINGTIME GPIO3_REG32(0x054) /* GPIO 4 */ #define GPIO4_BASE GPIOX_BASE(4) @@ -311,10 +332,26 @@ #define GPIO4_DEBOUNCENABLE GPIO4_REG32(0x050) #define GPIO4_DEBOUNCINGTIME GPIO4_REG32(0x054) +#if defined(CONFIG_ARCH_OMAP243X) +/* GPIO 5 */ +#define GPIO5_REG32(offset) __REG32((OMAP24XX_GPIO5_BASE + (offset))) +#define GPIO5_IRQENABLE1 GPIO5_REG32(0x01C) +#define GPIO5_IRQSTATUS1 GPIO5_REG32(0x018) +#define GPIO5_IRQENABLE2 GPIO5_REG32(0x02C) +#define GPIO5_IRQSTATUS2 GPIO5_REG32(0x028) +#define GPIO5_WAKEUPENABLE GPIO5_REG32(0x020) +#define GPIO5_RISINGDETECT GPIO5_REG32(0x048) +#define GPIO5_FALLINGDETECT GPIO5_REG32(0x04C) +#define GPIO5_DATAIN GPIO5_REG32(0x038) +#define GPIO5_OE GPIO5_REG32(0x034) +#define GPIO5_DATAOUT GPIO5_REG32(0x03C) +#define GPIO5_DEBOUNCENABLE GPIO5_REG32(0x050) +#define GPIO5_DEBOUNCINGTIME GPIO5_REG32(0x054) +#endif /* IO CONFIG */ -#define CONTROL_BASE (OMAP24XX_CTRL_BASE) -#define CONTROL_REG32(offset) __REG32(CONTROL_BASE + (offset)) +#define OMAP24XX_CTRL_BASE (L4_24XX_BASE) +#define CONTROL_REG32(offset) __REG32(OMAP24XX_CTRL_BASE + (offset)) #define CONTROL_PADCONF_SPI1_NCS2 CONTROL_REG32(0x104) #define CONTROL_PADCONF_SYS_XTALOUT CONTROL_REG32(0x134) @@ -322,15 +359,18 @@ #define CONTROL_PADCONF_MCBSP1_DX CONTROL_REG32(0x10C) #define CONTROL_PADCONF_GPMC_NCS4 CONTROL_REG32(0x090) #define CONTROL_PADCONF_DSS_D5 CONTROL_REG32(0x0B8) -#define CONTROL_PADCONF_DSS_D9 CONTROL_REG32(0x0BC) +#define CONTROL_PADCONF_DSS_D9 CONTROL_REG32(0x0BC) /* 2420 */ #define CONTROL_PADCONF_DSS_D13 CONTROL_REG32(0x0C0) #define CONTROL_PADCONF_DSS_VSYNC CONTROL_REG32(0x0CC) +#define CONTROL_PADCONF_SYS_NIRQW0 CONTROL_REG32(0x0BC) /* 2430 */ +#define CONTROL_PADCONF_SSI1_FLAG_TX CONTROL_REG32(0x108) /* 2430 */ /* CONTROL */ #define CONTROL_DEVCONF CONTROL_REG32(0x274) +#define CONTROL_DEVCONF1 CONTROL_REG32(0x2E8) /* INTERRUPT CONTROLLER */ -#define INTC_BASE (OMAP24XX_L4_IO_BASE+0xfe000) +#define INTC_BASE ((L4_24XX_BASE) + 0xfe000) #define INTC_REG32(offset) __REG32(INTC_BASE + (offset)) #define INTC1_U_BASE INTC_REG32(0x000) @@ -348,10 +388,12 @@ #define INTC_ISR_CLEAR2 INTC_REG32(0x0D4) #define INTC_SIR_IRQ INTC_REG32(0x040) #define INTC_CONTROL INTC_REG32(0x048) -#define INTC_ILR11 INTC_REG32(0x12C) +#define INTC_ILR11 INTC_REG32(0x12C) /* PRCM on MPU PIC */ +#define INTC_ILR30 INTC_REG32(0x178) +#define INTC_ILR31 INTC_REG32(0x17C) #define INTC_ILR32 INTC_REG32(0x180) -#define INTC_ILR37 INTC_REG32(0x194) -#define INTC_SYSCONFIG INTC_REG32(0x010) +#define INTC_ILR37 INTC_REG32(0x194) /* GPIO4 on MPU PIC */ +#define INTC_SYSCONFIG INTC_REG32(0x010) /* GPT1 on MPU PIC */ /* RAM FIREWALL */ #define RAMFW_BASE (0x68005000) @@ -373,6 +415,24 @@ #define GPMC_CONFIG6_0 GPMC_REG32(0x074) #define GPMC_CONFIG7_0 GPMC_REG32(0x078) +/* GPMC CS1 */ +#define GPMC_CONFIG1_1 GPMC_REG32(0x090) +#define GPMC_CONFIG2_1 GPMC_REG32(0x094) +#define GPMC_CONFIG3_1 GPMC_REG32(0x098) +#define GPMC_CONFIG4_1 GPMC_REG32(0x09C) +#define GPMC_CONFIG5_1 GPMC_REG32(0x0a0) +#define GPMC_CONFIG6_1 GPMC_REG32(0x0a4) +#define GPMC_CONFIG7_1 GPMC_REG32(0x0a8) + +/* GPMC CS3 */ +#define GPMC_CONFIG1_3 GPMC_REG32(0x0F0) +#define GPMC_CONFIG2_3 GPMC_REG32(0x0F4) +#define GPMC_CONFIG3_3 GPMC_REG32(0x0F8) +#define GPMC_CONFIG4_3 GPMC_REG32(0x0FC) +#define GPMC_CONFIG5_3 GPMC_REG32(0x100) +#define GPMC_CONFIG6_3 GPMC_REG32(0x104) +#define GPMC_CONFIG7_3 GPMC_REG32(0x108) + /* DSS */ #define DSS_CONTROL DISP_REG32(0x040) #define DISPC_CONTROL DISP_REG32(0x440) @@ -405,11 +465,15 @@ #define DISPC_DATA_CYCLE2 DISP_REG32(0x5D8) #define DISPC_DATA_CYCLE3 DISP_REG32(0x5DC) -/* Wake up define for board */ -#define GPIO97 (1 << 1) -#define GPIO88 (1 << 24) +/* HSUSB Suspend */ +#define HSUSB_CTRL __REG8(0x480AC001) +#define USBOTG_POWER __REG32(0x480AC000) + +/* HS MMC */ +#define MMCHS1_SYSCONFIG __REG32(0x4809C010) +#define MMCHS2_SYSCONFIG __REG32(0x480b4010) -#endif /* __ASSEMBLER__ */ +#endif /* __ASSEMBLER__ */ #endif diff --git a/arch/arm/mach-omap2/prcm.c b/arch/arm/mach-omap2/prcm.c new file mode 100644 index 00000000000..8893479dc7e --- /dev/null +++ b/arch/arm/mach-omap2/prcm.c @@ -0,0 +1,40 @@ +/* + * linux/arch/arm/mach-omap2/prcm.c + * + * OMAP 24xx Power Reset and Clock Management (PRCM) functions + * + * Copyright (C) 2005 Nokia Corporation + * + * Written by Tony Lindgren + * + * Some pieces of code Copyright (C) 2005 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 version 2 as + * published by the Free Software Foundation. + */ +#include +#include +#include +#include + +#include "prcm-regs.h" + +u32 omap_prcm_get_reset_sources(void) +{ + return RM_RSTST_WKUP & 0x7f; +} +EXPORT_SYMBOL(omap_prcm_get_reset_sources); + +/* Resets clock rates and reboots the system. Only called from system.h */ +void omap_prcm_arch_reset(char mode) +{ + u32 rate; + struct clk *vclk, *sclk; + + vclk = clk_get(NULL, "virt_prcm_set"); + sclk = clk_get(NULL, "sys_ck"); + rate = clk_get_rate(sclk); + clk_set_rate(vclk, rate); /* go to bypass for OMAP limitation */ + RM_RSTCTRL_WKUP |= 2; +} diff --git a/arch/arm/mach-omap2/sleep.S b/arch/arm/mach-omap2/sleep.S new file mode 100644 index 00000000000..00299cbeb91 --- /dev/null +++ b/arch/arm/mach-omap2/sleep.S @@ -0,0 +1,144 @@ +/* + * linux/arch/arm/mach-omap2/sleep.S + * + * (C) Copyright 2004 + * Texas Instruments, + * Richard Woodruff + * + * 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 + +#define A_32KSYNC_CR_V IO_ADDRESS(OMAP_TIMER32K_BASE+0x10) +#define A_PRCM_VOLTCTRL_V IO_ADDRESS(OMAP24XX_PRCM_BASE+0x50) +#define A_PRCM_CLKCFG_CTRL_V IO_ADDRESS(OMAP24XX_PRCM_BASE+0x80) +#define A_CM_CLKEN_PLL_V IO_ADDRESS(OMAP24XX_PRCM_BASE+0x500) +#define A_CM_IDLEST_CKGEN_V IO_ADDRESS(OMAP24XX_PRCM_BASE+0x520) +#define A_CM_CLKSEL1_PLL_V IO_ADDRESS(OMAP24XX_PRCM_BASE+0x540) +#define A_CM_CLKSEL2_PLL_V IO_ADDRESS(OMAP24XX_PRCM_BASE+0x544) + +#define A_SDRC_DLLA_CTRL_V IO_ADDRESS(OMAP24XX_SDRC_BASE+0x60) +#define A_SDRC_POWER_V IO_ADDRESS(OMAP24XX_SDRC_BASE+0x70) +#define A_SDRC_RFR_CTRL_V IO_ADDRESS(OMAP24XX_SDRC_BASE+0xA4) +#define A_SDRC0_V (0xC0000000) +#define A_SDRC_MANUAL_V IO_ADDRESS(OMAP24XX_SDRC_BASE+0xA8) + + .text + +/* + * Forces OMAP into idle state + * + * omap24xx_idle_loop_suspend() - This bit of code just executes the WFI + * for normal idles. + * + * Note: This code get's copied to internal SRAM at boot. When the OMAP + * wakes up it continues execution at the point it went to sleep. + */ +ENTRY(omap24xx_idle_loop_suspend) + stmfd sp!, {r0, lr} @ save registers on stack + mov r0, #0 @ clear for mcr setup + mcr p15, 0, r0, c7, c0, 4 @ wait for interrupt + ldmfd sp!, {r0, pc} @ restore regs and return + +ENTRY(omap24xx_idle_loop_suspend_sz) + .word . - omap24xx_idle_loop_suspend + +/* + * omap242x_cpu_suspend() - Forces OMAP into deep sleep state by completing + * SDRC shutdown then ARM shutdown. Upon wake MPU is back on so just restore + * SDRC. + * + * Input: + * R0 : DLL ctrl value pre-Sleep + * R1 : Processor+Revision + * 2420: 0x21 = 242xES1, 0x26 = 242xES2.2 + * 2430: 0x31 = 2430ES1, 0x32 = 2430ES2 + * + * The if the DPLL is going to AutoIdle. It seems like the DPLL may be back on + * when we get called, but the DLL probably isn't. We will wait a bit more in + * case the DPLL isn't quite there yet. The code will wait on DLL for DDR even + * if in unlocked mode. + * + * For less than 242x-ES2.2 upon wake from a sleep mode where the external + * oscillator was stopped, a timing bug exists where a non-stabilized 12MHz + * clock can pass into the PRCM can cause problems at DSP and IVA. + * To work around this the code will switch to the 32kHz source prior to sleep. + * Post sleep we will shift back to using the DPLL. Apparently, + * CM_IDLEST_CLKGEN does not reflect the full clock change so you need to wait + * 3x12MHz + 3x32kHz clocks for a full switch. + * + * The DLL load value is not kept in RETENTION or OFF. It needs to be restored + * at wake + */ +ENTRY(omap24xx_cpu_suspend) + stmfd sp!, {r0 - r12, lr} @ save registers on stack + mov r3, #0x0 @ clear for mrc call + mcr p15, 0, r3, c7, c10, 4 @ memory barrier, hope SDR/DDR finished + nop + nop + ldr r3, A_SDRC_POWER @ addr of sdrc power + ldr r4, [r3] @ value of sdrc power + orr r4, r4, #0x40 @ enable self refresh on idle req + mov r5, #0x2000 @ set delay (DPLL relock + DLL relock) + str r4, [r3] @ make it so + mov r2, #0 + nop + mcr p15, 0, r2, c7, c0, 4 @ wait for interrupt + nop +loop: + subs r5, r5, #0x1 @ awake, wait just a bit + bne loop + + /* The DPLL has on before we take the DDR out of self refresh */ + bic r4, r4, #0x40 @ now clear self refresh bit. + str r4, [r3] @ put vlaue back. + ldr r4, A_SDRC0 @ make a clock happen + ldr r4, [r4] + nop @ start auto refresh only after clk ok + movs r0, r0 @ see if DDR or SDR + ldrne r1, A_SDRC_DLLA_CTRL_S @ get addr of DLL ctrl + strne r0, [r1] @ rewrite DLLA to force DLL reload + addne r1, r1, #0x8 @ move to DLLB + strne r0, [r1] @ rewrite DLLB to force DLL reload + + mov r5, #0x1000 +loop2: + subs r5, r5, #0x1 + bne loop2 + /* resume*/ + ldmfd sp!, {r0 - r12, pc} @ restore regs and return + +A_SDRC_POWER: + .word A_SDRC_POWER_V +A_SDRC0: + .word A_SDRC0_V +A_CM_CLKSEL2_PLL_S: + .word A_CM_CLKSEL2_PLL_V +A_CM_CLKEN_PLL: + .word A_CM_CLKEN_PLL_V +A_SDRC_DLLA_CTRL_S: + .word A_SDRC_DLLA_CTRL_V +A_SDRC_MANUAL_S: + .word A_SDRC_MANUAL_V + +ENTRY(omap24xx_cpu_suspend_sz) + .word . - omap24xx_cpu_suspend + diff --git a/arch/arm/mach-omap2/sram-fn.S b/arch/arm/mach-omap2/sram-fn.S index 2a869e20334..d261e4ff4d9 100644 --- a/arch/arm/mach-omap2/sram-fn.S +++ b/arch/arm/mach-omap2/sram-fn.S @@ -1,5 +1,5 @@ /* - * linux/arch/arm/mach-omap1/sram.S + * linux/arch/arm/mach-omap2/sram.S * * Omap2 specific functions that need to be run in internal SRAM * @@ -28,7 +28,7 @@ #include #include -#include +#include "prcm-regs.h" #define TIMER_32KSYNCT_CR_V IO_ADDRESS(OMAP24XX_32KSYNCT_BASE + 0x010) diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index 0887bb2a255..28ca5ad4376 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -31,6 +31,40 @@ config OMAP_RESET_CLOCKS probably do not want this option enabled until your device drivers work properly. +config OMAP_BOOT_TAG + bool "OMAP bootloader information passing" + depends on ARCH_OMAP + default n + help + Say Y, if you have a bootloader which passes information + about your board and its peripheral configuration. + +config OMAP_BOOT_REASON + bool "Support for boot reason" + depends on OMAP_BOOT_TAG + default n + help + Say Y, if you want to have a procfs entry for reading the boot + reason in user-space. + +config OMAP_COMPONENT_VERSION + bool "Support for component version display" + depends on OMAP_BOOT_TAG && PROC_FS + default n + help + Say Y, if you want to have a procfs entry for reading component + versions (supplied by the bootloader) in user-space. + +config OMAP_GPIO_SWITCH + bool "GPIO switch support" + depends on OMAP_BOOT_TAG + default n + help + Say Y, if you want to have support for input layer reporting + of GPIO switches (e.g. cover switches). Your bootloader has to + provide information about the switches to the kernel via the + ATAG_BOARD mechanism. + config OMAP_MUX bool "OMAP multiplexing support" depends on ARCH_OMAP @@ -57,6 +91,17 @@ config OMAP_MUX_WARNINGS to change the pin multiplexing setup. When there are no warnings printed, it's safe to deselect OMAP_MUX for your product. +config OMAP_STI + bool "STI/XTI support" + depends on ARCH_OMAP16XX || ARCH_OMAP24XX + default n + +config OMAP_STI_CONSOLE + bool "STI console support" + depends on OMAP_STI + help + This enables a console driver by way of STI/XTI. + choice prompt "System timer" default OMAP_MPU_TIMER @@ -70,13 +115,13 @@ config OMAP_MPU_TIMER config OMAP_32K_TIMER bool "Use 32KHz timer" - depends on ARCH_OMAP16XX + depends on ARCH_OMAP16XX || ARCH_OMAP24XX help Select this option if you want to enable the OMAP 32KHz timer. This timer saves power compared to the OMAP_MPU_TIMER, and has support for no tick during idle. The 32KHz timer provides less intra-tick resolution than OMAP_MPU_TIMER. The 32KHz timer is - currently only available for OMAP-16xx. + currently only available for OMAP16XX and 24XX. endchoice diff --git a/arch/arm/plat-omap/Makefile b/arch/arm/plat-omap/Makefile index 9ccf1943fc9..34ba8fdd9cc 100644 --- a/arch/arm/plat-omap/Makefile +++ b/arch/arm/plat-omap/Makefile @@ -3,17 +3,24 @@ # # Common support -obj-y := common.o sram.o sram-fn.o clock.o devices.o dma.o mux.o gpio.o mcbsp.o usb.o +obj-y := common.o sram.o sram-fn.o clock.o devices.o dma.o mux.o gpio.o mcbsp.o usb.o fb.o obj-m := obj-n := obj- := +obj-$(CONFIG_OMAP_32K_TIMER) += timer32k.o + # OCPI interconnect support for 1710, 1610 and 5912 obj-$(CONFIG_ARCH_OMAP16XX) += ocpi.o -# Power Management -obj-$(CONFIG_PM) += pm.o sleep.o +# STI support +obj-$(CONFIG_OMAP_STI) += sti/ obj-$(CONFIG_CPU_FREQ) += cpu-omap.o obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o +obj-$(CONFIG_OMAP_BOOT_REASON) += bootreason.o +obj-$(CONFIG_OMAP_COMPONENT_VERSION) += component-version.o +obj-$(CONFIG_OMAP_GPIO_SWITCH) += gpio-switch.o +# DSP subsystem +obj-y += dsp/ diff --git a/arch/arm/plat-omap/bootreason.c b/arch/arm/plat-omap/bootreason.c new file mode 100644 index 00000000000..32d6c9fb534 --- /dev/null +++ b/arch/arm/plat-omap/bootreason.c @@ -0,0 +1,80 @@ +/* + * linux/arch/arm/plat-omap/bootreason.c + * + * OMAP Bootreason passing + * + * Copyright (c) 2004 Nokia + * + * Written by David Weinehall + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include + +static char boot_reason[16]; + +static int omap_bootreason_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = 0; + + len += sprintf(page + len, "%s\n", boot_reason); + + *start = page + off; + + if (len > off) + len -= off; + else + len = 0; + + return len < count ? len : count; +} + +static int __init bootreason_init(void) +{ + const struct omap_boot_reason_config *cfg; + int reason_valid = 0; + + cfg = omap_get_config(OMAP_TAG_BOOT_REASON, struct omap_boot_reason_config); + if (cfg != NULL) { + strncpy(boot_reason, cfg->reason_str, sizeof(cfg->reason_str)); + boot_reason[sizeof(cfg->reason_str)] = 0; + reason_valid = 1; + } else { + /* Read the boot reason from the OMAP registers */ + } + + if (!reason_valid) + return -ENOENT; + + printk(KERN_INFO "Bootup reason: %s\n", boot_reason); + + if (!create_proc_read_entry("bootreason", S_IRUGO, NULL, + omap_bootreason_read_proc, NULL)) + return -ENOMEM; + + return 0; +} + +late_initcall(bootreason_init); diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c index 3c2bfc0efda..29071ad3421 100644 --- a/arch/arm/plat-omap/clock.c +++ b/arch/arm/plat-omap/clock.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -37,17 +38,37 @@ static struct clk_functions *arch_clock; * Standard clock functions defined in include/linux/clk.h *-------------------------------------------------------------------------*/ +/* + * Returns a clock. Note that we first try to use device id on the bus + * and clock name. If this fails, we try to use clock name only. + */ struct clk * clk_get(struct device *dev, const char *id) { struct clk *p, *clk = ERR_PTR(-ENOENT); + int idno; + + if (dev == NULL || dev->bus != &platform_bus_type) + idno = -1; + else + idno = to_platform_device(dev)->id; mutex_lock(&clocks_mutex); + list_for_each_entry(p, &clocks, node) { - if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { + if (p->id == idno && + strcmp(id, p->name) == 0 && try_module_get(p->owner)) { clk = p; break; } } + + list_for_each_entry(p, &clocks, node) { + if (strcmp(id, p->name) == 0 && try_module_get(p->owner)) { + clk = p; + break; + } + } + mutex_unlock(&clocks_mutex); return clk; @@ -59,6 +80,9 @@ int clk_enable(struct clk *clk) unsigned long flags; int ret = 0; + if (clk == NULL || IS_ERR(clk)) + return -EINVAL; + spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_enable) ret = arch_clock->clk_enable(clk); @@ -72,6 +96,9 @@ void clk_disable(struct clk *clk) { unsigned long flags; + if (clk == NULL || IS_ERR(clk)) + return; + spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_disable) arch_clock->clk_disable(clk); @@ -84,6 +111,9 @@ int clk_get_usecount(struct clk *clk) unsigned long flags; int ret = 0; + if (clk == NULL || IS_ERR(clk)) + return 0; + spin_lock_irqsave(&clockfw_lock, flags); ret = clk->usecount; spin_unlock_irqrestore(&clockfw_lock, flags); @@ -97,6 +127,9 @@ unsigned long clk_get_rate(struct clk *clk) unsigned long flags; unsigned long ret = 0; + if (clk == NULL || IS_ERR(clk)) + return 0; + spin_lock_irqsave(&clockfw_lock, flags); ret = clk->rate; spin_unlock_irqrestore(&clockfw_lock, flags); @@ -121,6 +154,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate) unsigned long flags; long ret = 0; + if (clk == NULL || IS_ERR(clk)) + return ret; + spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_round_rate) ret = arch_clock->clk_round_rate(clk, rate); @@ -133,7 +169,10 @@ EXPORT_SYMBOL(clk_round_rate); int clk_set_rate(struct clk *clk, unsigned long rate) { unsigned long flags; - int ret = 0; + int ret = -EINVAL; + + if (clk == NULL || IS_ERR(clk)) + return ret; spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_set_rate) @@ -147,7 +186,10 @@ EXPORT_SYMBOL(clk_set_rate); int clk_set_parent(struct clk *clk, struct clk *parent) { unsigned long flags; - int ret = 0; + int ret = -EINVAL; + + if (clk == NULL || IS_ERR(clk) || parent == NULL || IS_ERR(parent)) + return ret; spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_set_parent) @@ -163,6 +205,9 @@ struct clk *clk_get_parent(struct clk *clk) unsigned long flags; struct clk * ret = NULL; + if (clk == NULL || IS_ERR(clk)) + return ret; + spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_get_parent) ret = arch_clock->clk_get_parent(clk); @@ -199,6 +244,9 @@ __setup("mpurate=", omap_clk_setup); /* Used for clocks that always have same value as the parent clock */ void followparent_recalc(struct clk *clk) { + if (clk == NULL || IS_ERR(clk)) + return; + clk->rate = clk->parent->rate; } @@ -207,6 +255,9 @@ void propagate_rate(struct clk * tclk) { struct clk *clkp; + if (tclk == NULL || IS_ERR(tclk)) + return; + list_for_each_entry(clkp, &clocks, node) { if (likely(clkp->parent != tclk)) continue; @@ -217,6 +268,9 @@ void propagate_rate(struct clk * tclk) int clk_register(struct clk *clk) { + if (clk == NULL || IS_ERR(clk)) + return -EINVAL; + mutex_lock(&clocks_mutex); list_add(&clk->node, &clocks); if (clk->init) @@ -229,6 +283,9 @@ EXPORT_SYMBOL(clk_register); void clk_unregister(struct clk *clk) { + if (clk == NULL || IS_ERR(clk)) + return; + mutex_lock(&clocks_mutex); list_del(&clk->node); mutex_unlock(&clocks_mutex); @@ -239,6 +296,9 @@ void clk_deny_idle(struct clk *clk) { unsigned long flags; + if (clk == NULL || IS_ERR(clk)) + return; + spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_deny_idle) arch_clock->clk_deny_idle(clk); @@ -250,6 +310,9 @@ void clk_allow_idle(struct clk *clk) { unsigned long flags; + if (clk == NULL || IS_ERR(clk)) + return; + spin_lock_irqsave(&clockfw_lock, flags); if (arch_clock->clk_allow_idle) arch_clock->clk_allow_idle(clk); @@ -270,3 +333,65 @@ int __init clk_init(struct clk_functions * custom_clocks) return 0; } + +#ifdef CONFIG_PROC_FS +#include +#include + +static void *omap_ck_start(struct seq_file *m, loff_t *pos) +{ + return *pos < 1 ? (void *)1 : NULL; +} + +static void *omap_ck_next(struct seq_file *m, void *v, loff_t *pos) +{ + ++*pos; + return NULL; +} + +static void omap_ck_stop(struct seq_file *m, void *v) +{ +} + +int omap_ck_show(struct seq_file *m, void *v) +{ + struct clk *cp; + + list_for_each_entry(cp, &clocks, node) + seq_printf(m,"%s %ld %d\n", cp->name, cp->rate, cp->usecount); + + return 0; +} + +static struct seq_operations omap_ck_op = { + .start = omap_ck_start, + .next = omap_ck_next, + .stop = omap_ck_stop, + .show = omap_ck_show +}; + +static int omap_ck_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &omap_ck_op); +} + +static struct file_operations proc_omap_ck_operations = { + .open = omap_ck_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +int __init omap_ck_init(void) +{ + struct proc_dir_entry *entry; + + entry = create_proc_entry("omap_clocks", 0, NULL); + if (entry) + entry->proc_fops = &proc_omap_ck_operations; + return 0; + +} +__initcall(omap_ck_init); +#endif /* CONFIG_DEBUG_PROC_FS */ + diff --git a/arch/arm/plat-omap/common.c b/arch/arm/plat-omap/common.c index adffc5a859e..0625df51dce 100644 --- a/arch/arm/plat-omap/common.c +++ b/arch/arm/plat-omap/common.c @@ -41,6 +41,26 @@ int omap_bootloader_tag_len; struct omap_board_config_kernel *omap_board_config; int omap_board_config_size; +#ifdef CONFIG_OMAP_BOOT_TAG + +static int __init parse_tag_omap(const struct tag *tag) +{ + u32 size = tag->hdr.size - (sizeof(tag->hdr) >> 2); + + size <<= 2; + if (size > sizeof(omap_bootloader_tag)) + return -1; + + memcpy(omap_bootloader_tag, tag->u.omap.data, size); + omap_bootloader_tag_len = size; + + return 0; +} + +__tagtable(ATAG_BOARD, parse_tag_omap); + +#endif + static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out) { struct omap_board_config_kernel *kinfo = NULL; diff --git a/arch/arm/plat-omap/common.h b/arch/arm/plat-omap/common.h new file mode 100644 index 00000000000..e69de29bb2d diff --git a/arch/arm/plat-omap/component-version.c b/arch/arm/plat-omap/component-version.c new file mode 100644 index 00000000000..a9fe63d5e72 --- /dev/null +++ b/arch/arm/plat-omap/component-version.c @@ -0,0 +1,65 @@ +/* + * linux/arch/arm/plat-omap/component-version.c + * + * Copyright (C) 2005 Nokia Corporation + * Written by Juha Yrjölä + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +static int component_version_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len, i; + const struct omap_version_config *ver; + char *p; + + i = 0; + p = page; + while ((ver = omap_get_nr_config(OMAP_TAG_VERSION_STR, + struct omap_version_config, i)) != NULL) { + p += sprintf(p, "%-12s%s\n", ver->component, ver->version); + i++; + } + + len = (p - page) - off; + if (len < 0) + len = 0; + + *eof = (len <= count) ? 1 : 0; + *start = page + off; + + return len; +} + +static int __init component_version_init(void) +{ + if (omap_get_config(OMAP_TAG_VERSION_STR, struct omap_version_config) == NULL) + return -ENODEV; + if (!create_proc_read_entry("component_version", S_IRUGO, NULL, + component_version_read_proc, NULL)) + return -ENOMEM; + + return 0; +} + +static void __exit component_version_exit(void) +{ + remove_proc_entry("component_version", NULL); +} + +late_initcall(component_version_init); +module_exit(component_version_exit); + +MODULE_AUTHOR("Juha Yrjölä "); +MODULE_DESCRIPTION("Component version driver"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c index 9dcce904b60..079b67deac0 100644 --- a/arch/arm/plat-omap/devices.c +++ b/arch/arm/plat-omap/devices.c @@ -24,6 +24,7 @@ #include #include #include +#include void omap_nop_release(struct device *dev) @@ -97,6 +98,62 @@ static void omap_init_i2c(void) static inline void omap_init_i2c(void) {} #endif +/*-------------------------------------------------------------------------*/ +#if defined(CONFIG_KEYBOARD_OMAP) || defined(CONFIG_KEYBOARD_OMAP_MODULE) + +static void omap_init_kp(void) +{ + if (machine_is_omap_h2() || machine_is_omap_h3()) { + omap_cfg_reg(F18_1610_KBC0); + omap_cfg_reg(D20_1610_KBC1); + omap_cfg_reg(D19_1610_KBC2); + omap_cfg_reg(E18_1610_KBC3); + omap_cfg_reg(C21_1610_KBC4); + + omap_cfg_reg(G18_1610_KBR0); + omap_cfg_reg(F19_1610_KBR1); + omap_cfg_reg(H14_1610_KBR2); + omap_cfg_reg(E20_1610_KBR3); + omap_cfg_reg(E19_1610_KBR4); + omap_cfg_reg(N19_1610_KBR5); + } else if (machine_is_omap_perseus2()) { + omap_cfg_reg(E2_730_KBR0); + omap_cfg_reg(J7_730_KBR1); + omap_cfg_reg(E1_730_KBR2); + omap_cfg_reg(F3_730_KBR3); + omap_cfg_reg(D2_730_KBR4); + + omap_cfg_reg(C2_730_KBC0); + omap_cfg_reg(D3_730_KBC1); + omap_cfg_reg(E4_730_KBC2); + omap_cfg_reg(F4_730_KBC3); + omap_cfg_reg(E3_730_KBC4); + } else if (machine_is_omap_h4()) { + omap_cfg_reg(T19_24XX_KBR0); + omap_cfg_reg(R19_24XX_KBR1); + omap_cfg_reg(V18_24XX_KBR2); + omap_cfg_reg(M21_24XX_KBR3); + omap_cfg_reg(E5__24XX_KBR4); + if (omap_has_menelaus()) { + omap_cfg_reg(B3__24XX_KBR5); + omap_cfg_reg(AA4_24XX_KBC2); + omap_cfg_reg(B13_24XX_KBC6); + } else { + omap_cfg_reg(M18_24XX_KBR5); + omap_cfg_reg(H19_24XX_KBC2); + omap_cfg_reg(N19_24XX_KBC6); + } + omap_cfg_reg(R20_24XX_KBC0); + omap_cfg_reg(M14_24XX_KBC1); + omap_cfg_reg(V17_24XX_KBC3); + omap_cfg_reg(P21_24XX_KBC4); + omap_cfg_reg(L14_24XX_KBC5); + } +} +#else +static inline void omap_init_kp(void) {} +#endif + /*-------------------------------------------------------------------------*/ #if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) @@ -240,6 +297,55 @@ static void __init omap_init_mmc(void) static inline void omap_init_mmc(void) {} #endif +/*-------------------------------------------------------------------------*/ + +/* Numbering for the SPI-capable controllers when used for SPI: + * spi = 1 + * uwire = 2 + * mmc1..2 = 3..4 + * mcbsp1..3 = 5..7 + */ + +#if defined(CONFIG_SPI_OMAP_UWIRE) || defined(CONFIG_SPI_OMAP_UWIRE_MODULE) + +#define OMAP_UWIRE_BASE 0xfffb3000 + +static struct resource uwire_resources[] = { + { + .start = OMAP_UWIRE_BASE, + .end = OMAP_UWIRE_BASE + 0x20, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device omap_uwire_device = { + .name = "omap_uwire", + .id = -1, + .dev = { + .release = omap_nop_release, + }, + .num_resources = ARRAY_SIZE(uwire_resources), + .resource = uwire_resources, +}; + +static void omap_init_uwire(void) +{ + /* FIXME define and use a boot tag; not all boards will be hooking + * up devices to the microwire controller, and multi-board configs + * mean that CONFIG_SPI_OMAP_UWIRE may be configured anyway... + */ + + /* board-specific code must configure chipselects (only a few + * are normally used) and SCLK/SDI/SDO (each has two choices). + */ + (void) platform_device_register(&omap_uwire_device); +} +#else +static inline void omap_init_uwire(void) {} +#endif + +/*-------------------------------------------------------------------------*/ + #if defined(CONFIG_OMAP_WATCHDOG) || defined(CONFIG_OMAP_WATCHDOG_MODULE) #ifdef CONFIG_ARCH_OMAP24XX @@ -310,40 +416,6 @@ static void omap_init_rng(void) static inline void omap_init_rng(void) {} #endif -#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) - -static struct omap_lcd_config omap_fb_conf; - -static u64 omap_fb_dma_mask = ~(u32)0; - -static struct platform_device omap_fb_device = { - .name = "omapfb", - .id = -1, - .dev = { - .release = omap_nop_release, - .dma_mask = &omap_fb_dma_mask, - .coherent_dma_mask = ~(u32)0, - .platform_data = &omap_fb_conf, - }, - .num_resources = 0, -}; - -static inline void omap_init_fb(void) -{ - const struct omap_lcd_config *conf; - - conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config); - if (conf != NULL) - omap_fb_conf = *conf; - platform_device_register(&omap_fb_device); -} - -#else - -static inline void omap_init_fb(void) {} - -#endif - /* * This gets called after board-specific INIT_MACHINE, and initializes most * on-chip peripherals accessible on this board (except for few like USB): @@ -369,9 +441,10 @@ static int __init omap_init_devices(void) /* please keep these calls, and their implementations above, * in alphabetical order so they're easier to sort through. */ - omap_init_fb(); omap_init_i2c(); + omap_init_kp(); omap_init_mmc(); + omap_init_uwire(); omap_init_wdt(); omap_init_rng(); diff --git a/arch/arm/plat-omap/dma.c b/arch/arm/plat-omap/dma.c index a4e5ac77f6d..5dac4230360 100644 --- a/arch/arm/plat-omap/dma.c +++ b/arch/arm/plat-omap/dma.c @@ -1258,6 +1258,11 @@ void omap_stop_lcd_dma(void) omap_writew(w, OMAP1610_DMA_LCD_CTRL); } +int omap_lcd_dma_ext_running(void) +{ + return lcd_dma.ext_ctrl && lcd_dma.active; +} + /*----------------------------------------------------------------------------*/ static int __init omap_init_dma(void) @@ -1389,6 +1394,7 @@ EXPORT_SYMBOL(omap_free_lcd_dma); EXPORT_SYMBOL(omap_enable_lcd_dma); EXPORT_SYMBOL(omap_setup_lcd_dma); EXPORT_SYMBOL(omap_stop_lcd_dma); +EXPORT_SYMBOL(omap_lcd_dma_ext_running); EXPORT_SYMBOL(omap_set_lcd_dma_b1); EXPORT_SYMBOL(omap_set_lcd_dma_single_transfer); EXPORT_SYMBOL(omap_set_lcd_dma_ext_controller); diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c index 38d7ebf8792..eba3cb52ad8 100644 --- a/arch/arm/plat-omap/dmtimer.c +++ b/arch/arm/plat-omap/dmtimer.c @@ -97,6 +97,32 @@ int omap_dm_timers_active(void) } +/** + * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR + * @inputmask: current value of idlect mask + */ +__u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask) +{ + int n; + + /* If ARMXOR cannot be idled this function call is unnecessary */ + if (!(inputmask & (1 << 1))) + return inputmask; + + /* If any active timer is using ARMXOR return modified mask */ + for (n = 0; dm_timers[n].base; ++n) + if (omap_dm_timer_read_reg(&dm_timers[n], OMAP_TIMER_CTRL_REG)& + OMAP_TIMER_CTRL_ST) { + if (((omap_readl(MOD_CONF_CTRL_1)>>(n*2)) & 0x03) == 0) + inputmask &= ~(1 << 1); + else + inputmask &= ~(1 << 2); + } + + return inputmask; +} + + void omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) { int n = (timer - dm_timers) << 1; diff --git a/arch/arm/plat-omap/dsp/Kconfig b/arch/arm/plat-omap/dsp/Kconfig new file mode 100644 index 00000000000..47f06b21e2e --- /dev/null +++ b/arch/arm/plat-omap/dsp/Kconfig @@ -0,0 +1,29 @@ + +config OMAP_DSP + tristate "OMAP DSP driver (DSP Gateway)" + depends on ARCH_OMAP15XX || ARCH_OMAP16XX + help + This enables OMAP DSP driver, DSP Gateway. + +config OMAP_DSP_MBCMD_VERBOSE + bool "Mailbox Command Verbose LOG" + depends on OMAP_DSP + help + This enables kernel log output in the Mailbox command exchanges + in the DSP Gateway driver. + +config OMAP_DSP_TASK_MULTIOPEN + bool "DSP Task Multiopen Capability" + depends on OMAP_DSP + help + This enables DSP tasks to be opened by multiple times at a time. + Otherwise, they can be opened only once at a time. + +config OMAP_DSP_FBEXPORT + bool "Framebuffer export to DSP" + depends on OMAP_DSP + help + This enables to map the frame buffer to DSP. + By doing this, DSP can access the frame buffer directly without + bothering ARM. + diff --git a/arch/arm/plat-omap/dsp/Makefile b/arch/arm/plat-omap/dsp/Makefile new file mode 100644 index 00000000000..c7d86f358b5 --- /dev/null +++ b/arch/arm/plat-omap/dsp/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the OMAP DSP driver. +# + +# The target object and module list name. + +obj-y := dsp_common.o + +obj-$(CONFIG_OMAP_DSP) += dsp.o + +# Declare multi-part drivers + +dsp-objs := dsp_core.o ipbuf.o mblog.o task.o \ + dsp_ctl_core.o dsp_ctl.o taskwatch.o error.o dsp_mem.o \ + uaccess_dsp.o diff --git a/arch/arm/plat-omap/dsp/dsp.h b/arch/arm/plat-omap/dsp/dsp.h new file mode 100644 index 00000000000..0a4876630f7 --- /dev/null +++ b/arch/arm/plat-omap/dsp/dsp.h @@ -0,0 +1,226 @@ +/* + * linux/arch/arm/mach-omap/dsp/dsp.h + * + * Header for OMAP DSP driver + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2005/06/09: DSP Gateway version 3.3 + */ + +#include "hardware_dsp.h" +#include "dsp_common.h" + +#define OLD_BINARY_SUPPORT y + +#ifdef OLD_BINARY_SUPPORT +#define MBREV_3_0 0x0017 +#define MBREV_3_2 0x0018 +#endif + +#define DSP_INIT_PAGE 0xfff000 +/* idle program will be placed at IDLEPG_BASE. */ +#define IDLEPG_BASE 0xfffe00 +#define IDLEPG_SIZE 0x100 + +/* timeout value for DSP response */ +#define DSP_TIMEOUT (10 * HZ) + +enum dsp_mem_type_e { + MEM_TYPE_CROSSING = -1, + MEM_TYPE_NONE = 0, + MEM_TYPE_DARAM, + MEM_TYPE_SARAM, + MEM_TYPE_EXTERN, +}; + +enum arm_dsp_dir { + DIR_A2D, + DIR_D2A, +}; + +/* + * INT_D2A_MB value definition + * INT_DSP_MAILBOX1: use Mailbox 1 (INT 10) for DSP->ARM mailbox + * INT_DSP_MAILBOX2: use Mailbox 2 (INT 11) for DSP->ARM mailbox + */ +#define INT_D2A_MB1 INT_DSP_MAILBOX1 + +/* keep 2 entries for OMAP_DSP_TID_FREE and OMAP_DSP_TID_ANON */ +#define TASKDEV_MAX 254 + +#define MKLONG(uw,lw) (((unsigned long)(uw)) << 16 | (lw)) +#define MKVIRT(uw,lw) dspword_to_virt(MKLONG((uw), (lw))); +#define MBCMD(nm) OMAP_DSP_MBCMD_##nm + +struct sync_seq { + unsigned short da_dsp; + unsigned short da_arm; + unsigned short ad_dsp; + unsigned short ad_arm; +}; + +struct mem_sync_struct { + struct sync_seq *DARAM; + struct sync_seq *SARAM; + struct sync_seq *SDRAM; +}; + +/* struct mbcmd and struct mbcmd_hw must be compatible */ +struct mbcmd { + unsigned short cmd_l:8; + unsigned short cmd_h:7; + unsigned short seq:1; + unsigned short data; +}; + +struct mbcmd_hw { + unsigned short cmd; + unsigned short data; +}; + +#define mbcmd_set(mb, h, l, d) \ + do { \ + (mb).cmd_h = (h); \ + (mb).cmd_l = (l); \ + (mb).data = (d); \ + } while(0) + +struct mb_exarg { + unsigned char tid; + int argc; + unsigned short *argv; +}; + +extern void dsp_mb_start(void); +extern void dsp_mb_stop(void); +extern int dsp_mb_config(void *p); +extern int sync_with_dsp(unsigned short *syncwd, unsigned short tid, + int try_cnt); +extern int __mbcmd_send(struct mbcmd *mb); +extern int __dsp_mbcmd_send(struct mbcmd *mb, struct mb_exarg *arg, + int recovery_flag); +#define dsp_mbcmd_send(mb) __dsp_mbcmd_send(mb, NULL, 0) +#define dsp_mbcmd_send_exarg(mb, arg) __dsp_mbcmd_send(mb, arg, 0) +extern int __dsp_mbcmd_send_and_wait(struct mbcmd *mb, struct mb_exarg *arg, + wait_queue_head_t *q); +#define dsp_mbcmd_send_and_wait(mb, q) \ + __dsp_mbcmd_send_and_wait(mb, NULL, q) +#define dsp_mbcmd_send_and_wait_exarg(mb, arg, q) \ + __dsp_mbcmd_send_and_wait(mb, arg, q) +int __dsp_mbsend(unsigned char cmdh, unsigned char cmdl, unsigned short data, + int recovery_flag); +#define dsp_mbsend(cmdh, cmdl, data) \ + __dsp_mbsend(cmdh, cmdl, data, 0) +#define dsp_mbsend_recovery(cmdh, cmdl, data) \ + __dsp_mbsend(cmdh, cmdl, data, 1) + +extern void ipbuf_start(void); +extern void ipbuf_stop(void); +extern int ipbuf_config(unsigned short ln, unsigned short lsz, void *base); +extern int ipbuf_sys_config(void *p, enum arm_dsp_dir dir); +extern int ipbuf_p_validate(void *p, enum arm_dsp_dir dir); +extern unsigned short get_free_ipbuf(unsigned char tid); +extern void unuse_ipbuf_nowait(unsigned short bid); +extern void unuse_ipbuf(unsigned short bid); +extern void release_ipbuf(unsigned short bid); +extern void balance_ipbuf(void); + +#define release_ipbuf_pvt(ipbuf_pvt) \ + do { \ + (ipbuf_pvt)->s = OMAP_DSP_TID_FREE; \ + } while(0) + +extern int mbx_revision; + +extern int dsp_is_ready(void); +extern int dspuncfg(void); +extern void dsp_runlevel(unsigned char level); +extern int dsp_suspend(void); +extern int dsp_resume(void); + +extern int dsp_task_config_all(unsigned char n); +extern void dsp_task_unconfig_all(void); +extern unsigned char dsp_task_count(void); +extern int dsp_taskmod_busy(void); +extern int dsp_mkdev(char *name); +extern int dsp_rmdev(char *name); +extern int dsp_tadd(unsigned char minor, unsigned long adr); +extern int dsp_tdel(unsigned char minor); +extern int dsp_tkill(unsigned char minor); +extern long taskdev_state_stale(unsigned char minor); +extern int dsp_dbg_config(short *buf, unsigned short sz, unsigned short lsz); +extern void dsp_dbg_stop(void); + +extern int ipbuf_is_held(unsigned char tid, unsigned short bid); + +extern void dsp_mem_sync_inc(void); +extern int dsp_mem_sync_config(struct mem_sync_struct *sync); +extern enum dsp_mem_type_e dsp_mem_type(void *vadr, size_t len); +extern int dsp_address_validate(void *p, size_t len, char *fmt, ...); +extern int dsp_mem_enable(void *adr); +extern void dsp_mem_disable(void *adr); +extern void dsp_mem_usecount_clear(void); +extern void exmap_use(void *vadr, size_t len); +extern void exmap_unuse(void *vadr, size_t len); +extern unsigned long dsp_virt_to_phys(void *vadr, size_t *len); +extern void dsp_mem_start(void); +extern void dsp_mem_stop(void); + +extern void dsp_twch_start(void); +extern void dsp_twch_stop(void); +extern void dsp_twch_touch(void); + +extern void dsp_err_start(void); +extern void dsp_err_stop(void); +extern void dsp_err_mmu_set(unsigned long adr); +extern void dsp_err_mmu_clear(void); +extern int dsp_err_mmu_isset(void); +extern void dsp_err_wdt_clear(void); +extern int dsp_err_wdt_isset(void); + +enum cmd_l_type { + CMD_L_TYPE_NULL, + CMD_L_TYPE_TID, + CMD_L_TYPE_SUBCMD, +}; + +struct cmdinfo { + char *name; + enum cmd_l_type cmd_l_type; + void (*handler)(struct mbcmd *mb); +}; + +extern const struct cmdinfo *cmdinfo[]; + +#define cmd_name(mb) (cmdinfo[(mb).cmd_h]->name) +extern char *subcmd_name(struct mbcmd *mb); + +extern void mblog_add(struct mbcmd *mb, enum arm_dsp_dir dir); +#ifdef CONFIG_OMAP_DSP_MBCMD_VERBOSE +extern void mblog_printcmd(struct mbcmd *mb, enum arm_dsp_dir dir); +#else /* CONFIG_OMAP_DSP_MBCMD_VERBOSE */ +#define mblog_printcmd(mb, dir) do {} while(0) +#endif /* CONFIG_OMAP_DSP_MBCMD_VERBOSE */ + +#ifdef CONFIG_PROC_FS +extern struct proc_dir_entry *procdir_dsp; +#endif /* CONFIG_PROC_FS */ + +extern struct platform_device dsp_device; diff --git a/arch/arm/plat-omap/dsp/dsp_common.c b/arch/arm/plat-omap/dsp/dsp_common.c new file mode 100644 index 00000000000..9c701567f1f --- /dev/null +++ b/arch/arm/plat-omap/dsp/dsp_common.c @@ -0,0 +1,545 @@ +/* + * linux/arch/arm/mach-omap/dsp/dsp_common.c + * + * OMAP DSP driver static part + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2005/06/13: DSP Gateway version 3.3 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dsp_common.h" + +struct clk *dsp_ck_handle; +struct clk *api_ck_handle; +unsigned long dspmem_base, dspmem_size, + daram_base, daram_size, + saram_base, saram_size; + +struct cpustat { + struct semaphore sem; + enum e_cpustat stat; + enum e_cpustat req; + unsigned short icrmask; + struct { + int mpui; + int mem; + int mem_delayed; + } usecount; + int (*mem_req_cb)(void); + void (*mem_rel_cb)(void); +}; +struct cpustat cpustat = { + .sem = __SEMAPHORE_INIT(cpustat.sem, 1), + .stat = CPUSTAT_RESET, + .icrmask = 0xffff, +}; + +int dsp_set_rstvect(unsigned long adr) +{ + unsigned long *dst_adr; + + if (adr >= DSPSPACE_SIZE) + return -EINVAL; + + dst_adr = dspbyte_to_virt(DSP_BOOT_ADR_DIRECT); + /* word swap */ + *dst_adr = ((adr & 0xffff) << 16) | (adr >> 16); + /* fill 8 bytes! */ + *(dst_adr+1) = 0; + /* direct boot */ + omap_writew(MPUI_DSP_BOOT_CONFIG_DIRECT, MPUI_DSP_BOOT_CONFIG); + + return 0; +} + +static void simple_load_code(unsigned char *src_c, unsigned short *dst, int len) +{ + int i; + unsigned short *src = (unsigned short *)src_c; + int len_w; + + /* len must be multiple of 2. */ + if (len & 1) + BUG(); + + len_w = len / 2; + for (i = 0; i < len_w; i++) { + /* byte swap copy */ + *dst = ((*src & 0x00ff) << 8) | + ((*src & 0xff00) >> 8); + src++; + dst++; + } +} + +/* program size must be multiple of 2 */ +#define GBL_IDLE_TEXT_SIZE 52 +#define GBL_IDLE_TEXT_INIT { \ + /* SAM */ \ + 0x3c, 0x4a, /* 0x3c4a: MOV 0x4, AR2 */ \ + 0xf4, 0x41, 0xfc, 0xff, /* 0xf441fcff: AND 0xfcff, *AR2 */ \ + /* disable WDT */ \ + 0x76, 0x34, 0x04, 0xb8, /* 0x763404b8: MOV 0x3404, AR3 */ \ + 0xfb, 0x61, 0x00, 0xf5, /* 0xfb6100f5: MOV 0x00f5, *AR3 */ \ + 0x9a, /* 0x9a: PORT */ \ + 0xfb, 0x61, 0x00, 0xa0, /* 0xfb6100a0: MOV 0x00a0, *AR3 */ \ + 0x9a, /* 0x9a: PORT */ \ + /* *IER0 = 0, *IER1 = 0 */ \ + 0x3c, 0x0b, /* 0x3c0b: MOV 0x0, AR3 */ \ + 0xe6, 0x61, 0x00, /* 0xe66100: MOV 0, *AR3 */ \ + 0x76, 0x00, 0x45, 0xb8, /* 0x76004508: MOV 0x45, AR3 */ \ + 0xe6, 0x61, 0x00, /* 0xe66100: MOV 0, *AR3 */ \ + /* *ICR = 0xffff */ \ + 0x3c, 0x1b, /* 0x3c1b: MOV 0x1, AR3 */ \ + 0xfb, 0x61, 0xff, 0xff, /* 0xfb61ffff: MOV 0xffff, *AR3 */ \ + 0x9a, /* 0x9a: PORT */ \ + /* HOM */ \ + 0xf5, 0x41, 0x03, 0x00, /* 0xf5410300: OR 0x0300, *AR2 */ \ + /* idle and loop forever */ \ + 0x7a, 0x00, 0x00, 0x0c, /* 0x7a00000c: IDLE */ \ + 0x4a, 0x7a, /* 0x4a7a: B -6 (infinite loop) */ \ + 0x20, 0x20, 0x20, /* 0x20: NOP */ \ +} + +/* program size must be multiple of 2 */ +#define CPU_IDLE_TEXT_SIZE 48 +#define CPU_IDLE_TEXT_INIT(icrh, icrl) { \ + /* SAM */ \ + 0x3c, 0x4b, /* 0x3c4b: MOV 0x4, AR3 */ \ + 0xf4, 0x61, 0xfc, 0xff, /* 0xf461fcff: AND 0xfcff, *AR3 */ \ + /* disable WDT */ \ + 0x76, 0x34, 0x04, 0xb8, /* 0x763404b8: MOV 0x3404, AR3 */ \ + 0xfb, 0x61, 0x00, 0xf5, /* 0xfb6100f5: MOV 0x00f5, *AR3 */ \ + 0x9a, /* 0x9a: PORT */ \ + 0xfb, 0x61, 0x00, 0xa0, /* 0xfb6100a0: MOV 0x00a0, *AR3 */ \ + 0x9a, /* 0x9a: PORT */ \ + /* *IER0 = 0, *IER1 = 0 */ \ + 0x3c, 0x0b, /* 0x3c0b: MOV 0x0, AR3 */ \ + 0xe6, 0x61, 0x00, /* 0xe66100: MOV 0, *AR3 */ \ + 0x76, 0x00, 0x45, 0xb8, /* 0x76004508: MOV 0x45, AR3 */ \ + 0xe6, 0x61, 0x00, /* 0xe66100: MOV 0, *AR3 */ \ + /* set ICR = icr */ \ + 0x3c, 0x1b, /* 0x3c1b: MOV AR3 0x1 */ \ + 0xfb, 0x61, (icrh), (icrl), /* 0xfb61****: MOV *AR3, icr */ \ + 0x9a, /* 0x9a: PORT */ \ + /* idle and loop forever */ \ + 0x7a, 0x00, 0x00, 0x0c, /* 0x7a00000c: IDLE */ \ + 0x4a, 0x7a, /* 0x4a7a: B -6 (infinite loop) */ \ + 0x20, 0x20, 0x20 /* 0x20: nop */ \ +} + +/* + * idle_boot base: + * Initialized with DSP_BOOT_ADR_MPUI (=0x010000). + * This value is used before DSP Gateway driver is initialized. + * DSP Gateway driver will overwrite this value with other value, + * to avoid confliction with the user program. + */ +static unsigned long idle_boot_base = DSP_BOOT_ADR_MPUI; + +static void dsp_gbl_idle(void) +{ + unsigned char idle_text[GBL_IDLE_TEXT_SIZE] = GBL_IDLE_TEXT_INIT; + + __dsp_reset(); + clk_enable(api_ck_handle); + +#if 0 + omap_writew(MPUI_DSP_BOOT_CONFIG_IDLE, MPUI_DSP_BOOT_CONFIG); +#endif + simple_load_code(idle_text, dspbyte_to_virt(idle_boot_base), + GBL_IDLE_TEXT_SIZE); + if (idle_boot_base == DSP_BOOT_ADR_MPUI) + omap_writew(MPUI_DSP_BOOT_CONFIG_MPUI, MPUI_DSP_BOOT_CONFIG); + else + dsp_set_rstvect(idle_boot_base); + + __dsp_run(); + udelay(100); /* to make things stable */ + clk_disable(api_ck_handle); +} + +static void dsp_cpu_idle(void) +{ + unsigned short icr_tmp; + unsigned char icrh, icrl; + + __dsp_reset(); + clk_enable(api_ck_handle); + + /* + * icr settings: + * DMA should not sleep for DARAM/SARAM access + * DPLL should not sleep while any other domain is active + */ + icr_tmp = cpustat.icrmask & ~(DSPREG_ICR_DMA_IDLE_DOMAIN | + DSPREG_ICR_DPLL_IDLE_DOMAIN); + icrh = icr_tmp >> 8; + icrl = icr_tmp & 0xff; + { + unsigned char idle_text[CPU_IDLE_TEXT_SIZE] = CPU_IDLE_TEXT_INIT(icrh, icrl); + simple_load_code(idle_text, dspbyte_to_virt(idle_boot_base), + CPU_IDLE_TEXT_SIZE); + } + if (idle_boot_base == DSP_BOOT_ADR_MPUI) + omap_writew(MPUI_DSP_BOOT_CONFIG_MPUI, MPUI_DSP_BOOT_CONFIG); + else + dsp_set_rstvect(idle_boot_base); + __dsp_run(); + udelay(100); /* to make things stable */ + clk_disable(api_ck_handle); +} + +void dsp_set_idle_boot_base(unsigned long adr, size_t size) +{ + if (adr == idle_boot_base) + return; + idle_boot_base = adr; + if ((size < GBL_IDLE_TEXT_SIZE) || + (size < CPU_IDLE_TEXT_SIZE)) { + printk(KERN_ERR + "omapdsp: size for idle program is not enough!\n"); + BUG(); + } + + /* restart idle program with new base address */ + if (cpustat.stat == CPUSTAT_GBL_IDLE) + dsp_gbl_idle(); + if (cpustat.stat == CPUSTAT_CPU_IDLE) + dsp_cpu_idle(); +} + +static int init_done; + +static int __init omap_dsp_init(void) +{ + dspmem_size = 0; +#ifdef CONFIG_ARCH_OMAP15XX + if (cpu_is_omap1510()) { + dspmem_base = OMAP1510_DSP_BASE; + dspmem_size = OMAP1510_DSP_SIZE; + daram_base = OMAP1510_DARAM_BASE; + daram_size = OMAP1510_DARAM_SIZE; + saram_base = OMAP1510_SARAM_BASE; + saram_size = OMAP1510_SARAM_SIZE; + } +#endif +#ifdef CONFIG_ARCH_OMAP16XX + if (cpu_is_omap16xx()) { + dspmem_base = OMAP16XX_DSP_BASE; + dspmem_size = OMAP16XX_DSP_SIZE; + daram_base = OMAP16XX_DARAM_BASE; + daram_size = OMAP16XX_DARAM_SIZE; + saram_base = OMAP16XX_SARAM_BASE; + saram_size = OMAP16XX_SARAM_SIZE; + } +#endif + if (dspmem_size == 0) { + printk(KERN_ERR "omapdsp: unsupported omap architecture.\n"); + return -ENODEV; + } + + dsp_ck_handle = clk_get(0, "dsp_ck"); + if (IS_ERR(dsp_ck_handle)) { + printk(KERN_ERR "omapdsp: could not acquire dsp_ck handle.\n"); + return PTR_ERR(dsp_ck_handle); + } + + api_ck_handle = clk_get(0, "api_ck"); + if (IS_ERR(api_ck_handle)) { + printk(KERN_ERR "omapdsp: could not acquire api_ck handle.\n"); + return PTR_ERR(api_ck_handle); + } + + /* This is needed for McBSP init, released in late_initcall */ + clk_enable(api_ck_handle); + + __dsp_enable(); + mpui_byteswap_off(); + mpui_wordswap_on(); + tc_wordswap(); + + init_done = 1; + printk(KERN_INFO "omap_dsp_init() done\n"); + return 0; +} + +static int dsp_late_init(void) +{ + clk_disable(api_ck_handle); + return 0; +} +late_initcall(dsp_late_init); + +static void dsp_cpustat_update(void) +{ + if (!init_done) + omap_dsp_init(); + + if (cpustat.req == CPUSTAT_RUN) { + if (cpustat.stat < CPUSTAT_RUN) { + __dsp_reset(); + clk_enable(api_ck_handle); + udelay(10); + __dsp_run(); + cpustat.stat = CPUSTAT_RUN; + enable_irq(INT_DSP_MMU); + } + return; + } + + /* cpustat.stat < CPUSTAT_RUN */ + + if (cpustat.stat == CPUSTAT_RUN) { + disable_irq(INT_DSP_MMU); + clk_disable(api_ck_handle); + } + + /* + * (1) when ARM wants DARAM access, MPUI should be SAM and + * DSP needs to be on. + * (2) if any bits of icr is masked, we can not enter global idle. + */ + if ((cpustat.req == CPUSTAT_CPU_IDLE) || + (cpustat.usecount.mem > 0) || + (cpustat.usecount.mem_delayed > 0) || + ((cpustat.usecount.mpui > 0) && (cpustat.icrmask != 0xffff))) { + if (cpustat.stat != CPUSTAT_CPU_IDLE) { + dsp_cpu_idle(); + cpustat.stat = CPUSTAT_CPU_IDLE; + } + return; + } + + /* + * when ARM only needs MPUI access, MPUI can be HOM and + * DSP can be idling. + */ + if ((cpustat.req == CPUSTAT_GBL_IDLE) || + (cpustat.usecount.mpui > 0)) { + if (cpustat.stat != CPUSTAT_GBL_IDLE) { + dsp_gbl_idle(); + cpustat.stat = CPUSTAT_GBL_IDLE; + } + return; + } + + /* + * no user, no request + */ + if (cpustat.stat != CPUSTAT_RESET) { + __dsp_reset(); + cpustat.stat = CPUSTAT_RESET; + } +} + +void dsp_cpustat_request(enum e_cpustat req) +{ + down(&cpustat.sem); + cpustat.req = req; + dsp_cpustat_update(); + up(&cpustat.sem); +} + +enum e_cpustat dsp_cpustat_get_stat(void) +{ + return cpustat.stat; +} + +unsigned short dsp_cpustat_get_icrmask(void) +{ + return cpustat.icrmask; +} + +void dsp_cpustat_set_icrmask(unsigned short mask) +{ + down(&cpustat.sem); + cpustat.icrmask = mask; + dsp_cpustat_update(); + up(&cpustat.sem); +} + +void omap_dsp_request_mpui(void) +{ + down(&cpustat.sem); + if (cpustat.usecount.mpui++ == 0) + dsp_cpustat_update(); + up(&cpustat.sem); +} + +void omap_dsp_release_mpui(void) +{ + down(&cpustat.sem); + if (cpustat.usecount.mpui-- == 0) { + printk(KERN_ERR + "omapdsp: unbalanced mpui request/release detected.\n" + " cpustat.usecount.mpui is going to be " + "less than zero! ... fixed to be zero.\n"); + cpustat.usecount.mpui = 0; + } + if (cpustat.usecount.mpui == 0) + dsp_cpustat_update(); + up(&cpustat.sem); +} + +int omap_dsp_request_mem(void) +{ + int ret = 0; + + down(&cpustat.sem); + if ((cpustat.usecount.mem++ == 0) && + (cpustat.usecount.mem_delayed == 0)) { + if (cpustat.mem_req_cb) { + if ((ret = cpustat.mem_req_cb()) < 0) { + cpustat.usecount.mem--; + goto out; + } + } + dsp_cpustat_update(); + } +out: + up(&cpustat.sem); + + return ret; +} + +/* + * release_mem will be delayed. + */ +static void do_release_mem(void) { + down(&cpustat.sem); + cpustat.usecount.mem_delayed = 0; + if (cpustat.usecount.mem == 0) { + dsp_cpustat_update(); + if (cpustat.mem_rel_cb) + cpustat.mem_rel_cb(); + } + up(&cpustat.sem); +} + +static DECLARE_WORK(mem_rel_work, (void (*)(void *))do_release_mem, NULL); + +int omap_dsp_release_mem(void) +{ + down(&cpustat.sem); + + /* cancel previous release work */ + cancel_delayed_work(&mem_rel_work); + cpustat.usecount.mem_delayed = 0; + + if (cpustat.usecount.mem-- == 0) { + printk(KERN_ERR + "omapdsp: unbalanced memory request/release detected.\n" + " cpustat.usecount.mem is going to be " + "less than zero! ... fixed to be zero.\n"); + cpustat.usecount.mem = 0; + } + if (cpustat.usecount.mem == 0) { + cpustat.usecount.mem_delayed = 1; + schedule_delayed_work(&mem_rel_work, HZ); + } + + up(&cpustat.sem); + + return 0; +} + +void dsp_register_mem_cb(int (*req_cb)(void), void (*rel_cb)(void)) +{ + down(&cpustat.sem); + + cpustat.mem_req_cb = req_cb; + cpustat.mem_rel_cb = rel_cb; + + /* + * This function must be called while mem is enabled! + */ + BUG_ON(cpustat.usecount.mem == 0); + + up(&cpustat.sem); +} + +void dsp_unregister_mem_cb(void) +{ + down(&cpustat.sem); + cpustat.mem_req_cb = NULL; + cpustat.mem_rel_cb = NULL; + up(&cpustat.sem); +} + +/* + * Audio power control function prototypes and defaults + * (To be overridden with board specific functions) + */ +static void generic_audio_pwr_up_request(int stage) +{ + printk(KERN_ERR "audio power-up request function is not defined.\n"); +} + +void (*omap_dsp_audio_pwr_up_request)(int stage) = generic_audio_pwr_up_request; +EXPORT_SYMBOL(omap_dsp_audio_pwr_up_request); + +static void generic_audio_pwr_down_request(int stage) +{ + printk(KERN_ERR "audio power-down request function is not defined.\n"); +} + +void (*omap_dsp_audio_pwr_down_request)(int stage) = generic_audio_pwr_down_request; +EXPORT_SYMBOL(omap_dsp_audio_pwr_down_request); + +arch_initcall(omap_dsp_init); + +EXPORT_SYMBOL(omap_dsp_request_mpui); +EXPORT_SYMBOL(omap_dsp_release_mpui); +EXPORT_SYMBOL(omap_dsp_request_mem); +EXPORT_SYMBOL(omap_dsp_release_mem); + +#ifdef CONFIG_OMAP_DSP_MODULE +EXPORT_SYMBOL(dsp_ck_handle); +EXPORT_SYMBOL(api_ck_handle); +EXPORT_SYMBOL(dspmem_base); +EXPORT_SYMBOL(dspmem_size); +EXPORT_SYMBOL(daram_base); +EXPORT_SYMBOL(daram_size); +EXPORT_SYMBOL(saram_base); +EXPORT_SYMBOL(saram_size); +EXPORT_SYMBOL(dsp_set_rstvect); +EXPORT_SYMBOL(dsp_set_idle_boot_base); +EXPORT_SYMBOL(dsp_cpustat_request); +EXPORT_SYMBOL(dsp_cpustat_get_stat); +EXPORT_SYMBOL(dsp_cpustat_get_icrmask); +EXPORT_SYMBOL(dsp_cpustat_set_icrmask); +EXPORT_SYMBOL(dsp_register_mem_cb); +EXPORT_SYMBOL(dsp_unregister_mem_cb); + +EXPORT_SYMBOL(__cpu_flush_kern_tlb_range); +#endif diff --git a/arch/arm/plat-omap/dsp/dsp_common.h b/arch/arm/plat-omap/dsp/dsp_common.h new file mode 100644 index 00000000000..36034bd8ae4 --- /dev/null +++ b/arch/arm/plat-omap/dsp/dsp_common.h @@ -0,0 +1,132 @@ +/* + * linux/arch/arm/mach-omap/dsp/dsp_common.h + * + * Header for OMAP DSP driver static part + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2005/06/13: DSP Gateway version 3.3 + */ + +#include "hardware_dsp.h" + +#define DSPSPACE_SIZE 0x1000000 + +#define omap_set_bit_regw(b,r) \ + do { omap_writew(omap_readw(r) | (b), (r)); } while(0) +#define omap_clr_bit_regw(b,r) \ + do { omap_writew(omap_readw(r) & ~(b), (r)); } while(0) +#define omap_set_bit_regl(b,r) \ + do { omap_writel(omap_readl(r) | (b), (r)); } while(0) +#define omap_clr_bit_regl(b,r) \ + do { omap_writel(omap_readl(r) & ~(b), (r)); } while(0) + +#define dspword_to_virt(dw) ((void *)(dspmem_base + ((dw) << 1))) +#define dspbyte_to_virt(db) ((void *)(dspmem_base + (db))) +#define virt_to_dspword(va) (((unsigned long)(va) - dspmem_base) >> 1) +#define virt_to_dspbyte(va) ((unsigned long)(va) - dspmem_base) +#define is_dsp_internal_mem(va) \ + (((unsigned long)(va) >= dspmem_base) && \ + ((unsigned long)(va) < dspmem_base + dspmem_size)) +#define is_dspbyte_internal_mem(db) ((db) < dspmem_size) +#define is_dspword_internal_mem(dw) (((dw) << 1) < dspmem_size) + +/* + * MPUI byteswap/wordswap on/off + * default setting: wordswap = all, byteswap = APIMEM only + */ +#define mpui_wordswap_on() \ + do { \ + omap_writel( \ + (omap_readl(MPUI_CTRL) & ~MPUI_CTRL_WORDSWAP_MASK) | \ + MPUI_CTRL_WORDSWAP_ALL, MPUI_CTRL); \ + } while(0) + +#define mpui_wordswap_off() \ + do { \ + omap_writel( \ + (omap_readl(MPUI_CTRL) & ~MPUI_CTRL_WORDSWAP_MASK) | \ + MPUI_CTRL_WORDSWAP_NONE, MPUI_CTRL); \ + } while(0) + +#define mpui_byteswap_on() \ + do { \ + omap_writel( \ + (omap_readl(MPUI_CTRL) & ~MPUI_CTRL_BYTESWAP_MASK) | \ + MPUI_CTRL_BYTESWAP_API, MPUI_CTRL); \ + } while(0) + +#define mpui_byteswap_off() \ + do { \ + omap_writel( \ + (omap_readl(MPUI_CTRL) & ~MPUI_CTRL_BYTESWAP_MASK) | \ + MPUI_CTRL_BYTESWAP_NONE, MPUI_CTRL); \ + } while(0) + +/* + * TC wordswap on / off + */ +#define tc_wordswap() \ + do { \ + omap_writel(TC_ENDIANISM_SWAP_WORD | TC_ENDIANISM_EN, \ + TC_ENDIANISM); \ + } while(0) + +#define tc_noswap() \ + do { \ + omap_writel(omap_readl(TC_ENDIANISM) & ~TC_ENDIANISM_EN, \ + TC_ENDIANISM); \ + } while(0) + +/* + * enable priority registers, EMIF, MPUI control logic + */ +#define __dsp_enable() omap_set_bit_regw(ARM_RSTCT1_DSP_RST, ARM_RSTCT1) +#define __dsp_disable() omap_clr_bit_regw(ARM_RSTCT1_DSP_RST, ARM_RSTCT1) +#define __dsp_run() omap_set_bit_regw(ARM_RSTCT1_DSP_EN, ARM_RSTCT1) +#define __dsp_reset() omap_clr_bit_regw(ARM_RSTCT1_DSP_EN, ARM_RSTCT1) + +extern struct clk *dsp_ck_handle; +extern struct clk *api_ck_handle; +extern unsigned long dspmem_base, dspmem_size, + daram_base, daram_size, + saram_base, saram_size; + +enum e_cpustat { + CPUSTAT_RESET = 0, + CPUSTAT_GBL_IDLE = 1, + CPUSTAT_CPU_IDLE = 2, + CPUSTAT_RUN = 3 +}; + +#define cpustat_name(stat) \ + ((stat == CPUSTAT_RESET) ? "RESET" :\ + (stat == CPUSTAT_GBL_IDLE) ? "GBL_IDLE" :\ + (stat == CPUSTAT_CPU_IDLE) ? "CPU_IDLE" :\ + (stat == CPUSTAT_RUN) ? "RUN" :\ + "unknown") + +int dsp_set_rstvect(unsigned long adr); +void dsp_set_idle_boot_base(unsigned long adr, size_t size); +void dsp_cpustat_request(enum e_cpustat req); +enum e_cpustat dsp_cpustat_get_stat(void); +unsigned short dsp_cpustat_get_icrmask(void); +void dsp_cpustat_set_icrmask(unsigned short mask); +void dsp_register_mem_cb(int (*req_cb)(void), void (*rel_cb)(void)); +void dsp_unregister_mem_cb(void); diff --git a/arch/arm/plat-omap/dsp/dsp_core.c b/arch/arm/plat-omap/dsp/dsp_core.c new file mode 100644 index 00000000000..42565774988 --- /dev/null +++ b/arch/arm/plat-omap/dsp/dsp_core.c @@ -0,0 +1,814 @@ +/* + * linux/arch/arm/mach-omap/dsp/dsp_core.c + * + * OMAP DSP driver + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2005/06/07: DSP Gateway version 3.3 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hardware_dsp.h" +#include "dsp.h" +#include "ipbuf.h" + + +MODULE_AUTHOR("Toshihiro Kobayashi "); +MODULE_DESCRIPTION("OMAP DSP driver module"); +MODULE_LICENSE("GPL"); + +enum mbseq_check_level { + MBSEQ_CHECK_NONE, /* no check */ + MBSEQ_CHECK_VERBOSE, /* discard the illegal command and + error report */ + MBSEQ_CHECK_SILENT, /* discard the illegal command */ +}; + +static enum mbseq_check_level mbseq_check_level = MBSEQ_CHECK_VERBOSE; + +static int mbx1_valid; +static struct sync_seq *mbseq; +static unsigned short mbseq_expect_tmp; +static unsigned short *mbseq_expect = &mbseq_expect_tmp; + +/* + * mailbox commands + */ +extern void mbx1_wdsnd(struct mbcmd *mb); +extern void mbx1_wdreq(struct mbcmd *mb); +extern void mbx1_bksnd(struct mbcmd *mb); +extern void mbx1_bkreq(struct mbcmd *mb); +extern void mbx1_bkyld(struct mbcmd *mb); +extern void mbx1_bksndp(struct mbcmd *mb); +extern void mbx1_bkreqp(struct mbcmd *mb); +extern void mbx1_tctl(struct mbcmd *mb); +extern void mbx1_poll(struct mbcmd *mb); +#ifdef OLD_BINARY_SUPPORT +/* v3.3 obsolete */ +extern void mbx1_wdt(struct mbcmd *mb); +#endif +extern void mbx1_suspend(struct mbcmd *mb); +static void mbx1_kfunc(struct mbcmd *mb); +extern void mbx1_tcfg(struct mbcmd *mb); +extern void mbx1_tadd(struct mbcmd *mb); +extern void mbx1_tdel(struct mbcmd *mb); +extern void mbx1_dspcfg(struct mbcmd *mb); +extern void mbx1_regrw(struct mbcmd *mb); +extern void mbx1_getvar(struct mbcmd *mb); +extern void mbx1_err(struct mbcmd *mb); +extern void mbx1_dbg(struct mbcmd *mb); + +static const struct cmdinfo + cif_null = { "Unknown", CMD_L_TYPE_NULL, NULL }, + cif_wdsnd = { "WDSND", CMD_L_TYPE_TID, mbx1_wdsnd }, + cif_wdreq = { "WDREQ", CMD_L_TYPE_TID, mbx1_wdreq }, + cif_bksnd = { "BKSND", CMD_L_TYPE_TID, mbx1_bksnd }, + cif_bkreq = { "BKREQ", CMD_L_TYPE_TID, mbx1_bkreq }, + cif_bkyld = { "BKYLD", CMD_L_TYPE_NULL, mbx1_bkyld }, + cif_bksndp = { "BKSNDP", CMD_L_TYPE_TID, mbx1_bksndp }, + cif_bkreqp = { "BKREQP", CMD_L_TYPE_TID, mbx1_bkreqp }, + cif_tctl = { "TCTL", CMD_L_TYPE_TID, mbx1_tctl }, + cif_poll = { "POLL", CMD_L_TYPE_NULL, mbx1_poll }, +#ifdef OLD_BINARY_SUPPORT + /* v3.3 obsolete */ + cif_wdt = { "WDT", CMD_L_TYPE_NULL, mbx1_wdt }, +#endif + cif_runlevel = { "RUNLEVEL", CMD_L_TYPE_SUBCMD, NULL }, + cif_pm = { "PM", CMD_L_TYPE_SUBCMD, NULL }, + cif_suspend = { "SUSPEND", CMD_L_TYPE_NULL, mbx1_suspend }, + cif_kfunc = { "KFUNC", CMD_L_TYPE_SUBCMD, mbx1_kfunc }, + cif_tcfg = { "TCFG", CMD_L_TYPE_TID, mbx1_tcfg }, + cif_tadd = { "TADD", CMD_L_TYPE_TID, mbx1_tadd }, + cif_tdel = { "TDEL", CMD_L_TYPE_TID, mbx1_tdel }, + cif_tstop = { "TSTOP", CMD_L_TYPE_TID, NULL }, + cif_dspcfg = { "DSPCFG", CMD_L_TYPE_SUBCMD, mbx1_dspcfg }, + cif_regrw = { "REGRW", CMD_L_TYPE_SUBCMD, mbx1_regrw }, + cif_getvar = { "GETVAR", CMD_L_TYPE_SUBCMD, mbx1_getvar }, + cif_setvar = { "SETVAR", CMD_L_TYPE_SUBCMD, NULL }, + cif_err = { "ERR", CMD_L_TYPE_SUBCMD, mbx1_err }, + cif_dbg = { "DBG", CMD_L_TYPE_NULL, mbx1_dbg }; + +const struct cmdinfo *cmdinfo[128] = { +/*00*/ &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, +/*10*/ &cif_wdsnd, &cif_wdreq, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, +/*20*/ &cif_bksnd, &cif_bkreq, &cif_null, &cif_bkyld, + &cif_bksndp, &cif_bkreqp, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, +/*30*/ &cif_tctl, &cif_null, &cif_poll, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, +/*40*/ &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, +#ifdef OLD_BINARY_SUPPORT + /* v3.3 obsolete */ +/*50*/ &cif_wdt, &cif_runlevel, &cif_pm, &cif_suspend, +#else +/*50*/ &cif_null, &cif_runlevel, &cif_pm, &cif_suspend, +#endif + &cif_kfunc, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, +/*60*/ &cif_tcfg, &cif_null, &cif_tadd, &cif_tdel, + &cif_null, &cif_tstop, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null, +/*70*/ &cif_dspcfg, &cif_null, &cif_regrw, &cif_null, + &cif_getvar, &cif_setvar, &cif_null, &cif_null, + &cif_err, &cif_dbg, &cif_null, &cif_null, + &cif_null, &cif_null, &cif_null, &cif_null +}; + +int sync_with_dsp(unsigned short *syncwd, unsigned short tid, int try_cnt) +{ + int try; + + if (*(volatile unsigned short *)syncwd == tid) + return 0; + + for (try = 0; try < try_cnt; try++) { + udelay(1); + if (*(volatile unsigned short *)syncwd == tid) { + /* success! */ + printk(KERN_INFO + "omapdsp: sync_with_dsp(): try = %d\n", try); + return 0; + } + } + + /* fail! */ + return -1; +} + +static __inline__ int mbsync_irq_save(unsigned long *flags, int try_cnt) +{ + int cnt; + + local_irq_save(*flags); + if (omap_readw(MAILBOX_ARM2DSP1_Flag) == 0) + return 0; + /* + * mailbox is busy. wait for some usecs... + */ + local_irq_restore(*flags); + for (cnt = 0; cnt < try_cnt; cnt++) { + udelay(1); + local_irq_save(*flags); + if (omap_readw(MAILBOX_ARM2DSP1_Flag) == 0) /* success! */ + return 0; + local_irq_restore(*flags); + } + + /* fail! */ + return -1; +} + +#ifdef CONFIG_OMAP_DSP_MBCMD_VERBOSE +#define print_mb_busy_abort(mb) \ + printk(KERN_DEBUG \ + "mbx: mailbox is busy. %s is aborting.\n", cmd_name(*mb)) +#define print_mb_mmu_abort(mb) \ + printk(KERN_DEBUG \ + "mbx: mmu interrupt is set. %s is aborting.\n", cmd_name(*mb)) +#else /* CONFIG_OMAP_DSP_MBCMD_VERBOSE */ +#define print_mb_busy_abort(mb) do {} while(0) +#define print_mb_mmu_abort(mb) do {} while(0) +#endif /* !CONFIG_OMAP_DSP_MBCMD_VERBOSE */ + +int __mbcmd_send(struct mbcmd *mb) +{ + struct mbcmd_hw *mb_hw = (struct mbcmd_hw *)mb; + unsigned long flags; + + /* + * DSP mailbox interrupt latency must be less than 1ms. + */ + if (mbsync_irq_save(&flags, 1000) < 0) { + print_mb_busy_abort(mb); + return -1; + } + + if (mbseq) { + mb->seq = mbseq->ad_arm; + mbseq->ad_arm++; + } else + mb->seq = 0; + mblog_add(mb, DIR_A2D); + mblog_printcmd(mb, DIR_A2D); + + omap_writew(mb_hw->data, MAILBOX_ARM2DSP1); + omap_writew(mb_hw->cmd, MAILBOX_ARM2DSP1b); + + local_irq_restore(flags); + return 0; +} + +/* + * __dsp_mbcmd_send(): mailbox dispatcher + */ +int __dsp_mbcmd_send(struct mbcmd *mb, struct mb_exarg *arg, int recovery_flag) +{ + static DECLARE_MUTEX(mbsend_sem); + int ret = 0; + + /* + * while MMU fault is set, + * only recovery command can be executed + */ + if (dsp_err_mmu_isset() && !recovery_flag) { + print_mb_mmu_abort(mb); + return -1; + } + + if (down_interruptible(&mbsend_sem) < 0) + return -1; + + if (arg) { /* we have extra argument */ + int i; + + /* + * even if ipbuf_sys_ad is in DSP internal memory, + * dsp_mem_enable() never cause to call PM mailbox command + * because in that case DSP memory should be always enabled. + * (see ipbuf_sys_hold_mem_active in ipbuf.c) + * + * Therefore, we can call this function here safely. + */ + dsp_mem_enable(ipbuf_sys_ad); + if (sync_with_dsp(&ipbuf_sys_ad->s, OMAP_DSP_TID_FREE, 10) < 0) { + printk(KERN_ERR "omapdsp: ipbuf_sys_ad is busy.\n"); + dsp_mem_disable(ipbuf_sys_ad); + ret = -EBUSY; + goto out; + } + for (i = 0; i < arg->argc; i++) { + ipbuf_sys_ad->d[i] = arg->argv[i]; + } + ipbuf_sys_ad->s = arg->tid; + dsp_mem_disable(ipbuf_sys_ad); + } + + ret = __mbcmd_send(mb); + +out: + up(&mbsend_sem); + return ret; +} + +int __dsp_mbcmd_send_and_wait(struct mbcmd *mb, struct mb_exarg *arg, + wait_queue_head_t *q) +{ + long current_state; + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(q, &wait); + current_state = current->state; + set_current_state(TASK_INTERRUPTIBLE); + if (dsp_mbcmd_send_exarg(mb, arg) < 0) { + set_current_state(current_state); + remove_wait_queue(q, &wait); + return -1; + } + schedule_timeout(DSP_TIMEOUT); + set_current_state(current_state); + remove_wait_queue(q, &wait); + + return 0; +} + +int __dsp_mbsend(unsigned char cmdh, unsigned char cmdl, unsigned short data, + int recovery_flag) +{ + struct mbcmd mb; + + mbcmd_set(mb, cmdh, cmdl, data); + return __dsp_mbcmd_send(&mb, NULL, recovery_flag); +} + +static int mbsync_hold_mem_active; + +void dsp_mb_start(void) +{ + mbx1_valid = 1; /* start interpreting */ + mbseq_expect_tmp = 0; +} + +void dsp_mb_stop(void) +{ + mbx1_valid = 0; /* stop interpreting */ + if (mbsync_hold_mem_active) { + dsp_mem_disable((void *)daram_base); + mbsync_hold_mem_active = 0; + } + mbseq = NULL; + mbseq_expect = &mbseq_expect_tmp; +} + +int dsp_mb_config(void *p) +{ + unsigned long flags; + + if (dsp_address_validate(p, sizeof(struct sync_seq), "mbseq") < 0) + return -1; + if (dsp_mem_type(p, sizeof(struct sync_seq)) != MEM_TYPE_EXTERN) { + printk(KERN_WARNING + "omapdsp: mbseq is placed in DSP internal memory.\n" + " It will prevent DSP from idling.\n"); + mbsync_hold_mem_active = 1; + /* + * dsp_mem_enable() never fails because + * it has been already enabled in dspcfg process and + * this will just increment the usecount. + */ + dsp_mem_enable((void *)daram_base); + } + + local_irq_save(flags); + mbseq = p; + mbseq->da_arm = mbseq_expect_tmp; + mbseq_expect = &mbseq->da_arm; + local_irq_restore(flags); + + return 0; +} + +/* + * mbq: mailbox queue + */ +#define MBQ_DEPTH 16 +struct mbq { + struct mbcmd mb[MBQ_DEPTH]; + int rp, wp, full; +} mbq = { + .rp = 0, + .wp = 0, +}; + +#define mbq_inc(p) do { if (++(p) == MBQ_DEPTH) (p) = 0; } while(0) + +/* + * workqueue for mbx1 + */ +static void do_mbx1(void) +{ + int empty = 0; + + disable_irq(INT_D2A_MB1); + if ((mbq.rp == mbq.wp) && !mbq.full) + empty = 1; + enable_irq(INT_D2A_MB1); + + while (!empty) { + struct mbcmd *mb; + + mb = &mbq.mb[mbq.rp]; + + mblog_add(mb, DIR_D2A); + mblog_printcmd(mb, DIR_D2A); + + /* + * call handler for each command + */ + if (cmdinfo[mb->cmd_h]->handler) + cmdinfo[mb->cmd_h]->handler(mb); + else if (cmdinfo[mb->cmd_h] != &cif_null) + printk(KERN_ERR "mbx: %s is not allowed from DSP.\n", + cmd_name(*mb)); + else + printk(KERN_ERR + "mbx: Unrecognized command: " + "cmd=0x%04x, data=0x%04x\n", + ((struct mbcmd_hw *)mb)->cmd & 0x7fff, mb->data); + + disable_irq(INT_D2A_MB1); + mbq_inc(mbq.rp); + if (mbq.rp == mbq.wp) + empty = 1; + /* if mbq has been full, now we have a room. */ + if (mbq.full) { + mbq.full = 0; + enable_irq(INT_D2A_MB1); + } + enable_irq(INT_D2A_MB1); + } +} + +static DECLARE_WORK(mbx1_work, (void (*)(void *))do_mbx1, NULL); + +/* + * kernel function dispatcher + */ +extern void mbx1_fbctl_upd(void); +extern void mbx1_fbctl_disable(void); + +static void mbx1_kfunc_fbctl(unsigned short data) +{ + switch (data) { + case OMAP_DSP_MBCMD_FBCTL_UPD: + mbx1_fbctl_upd(); + break; + case OMAP_DSP_MBCMD_FBCTL_DISABLE: + mbx1_fbctl_disable(); + break; + default: + printk(KERN_ERR + "mailbox: Unknown FBCTL from DSP: 0x%04x\n", data); + } +} + +static void mbx1_kfunc_audio_pwr(unsigned short data) +{ + struct mbcmd mb; + + switch (data) { + case OMAP_DSP_MBCMD_AUDIO_PWR_UP: + omap_dsp_audio_pwr_up_request(0); + /* send back ack */ + mbcmd_set(mb, MBCMD(KFUNC), OMAP_DSP_MBCMD_KFUNC_AUDIO_PWR, + OMAP_DSP_MBCMD_AUDIO_PWR_UP); + dsp_mbcmd_send(&mb); + break; + case OMAP_DSP_MBCMD_AUDIO_PWR_DOWN1: + omap_dsp_audio_pwr_down_request(1); + break; + case OMAP_DSP_MBCMD_AUDIO_PWR_DOWN2: + omap_dsp_audio_pwr_down_request(2); + break; + default: + printk(KERN_ERR + "mailbox: Unknown AUDIO_PWR from DSP: 0x%04x\n", data); + } +} + +static void mbx1_kfunc(struct mbcmd *mb) +{ + switch (mb->cmd_l) { + case OMAP_DSP_MBCMD_KFUNC_FBCTL: + mbx1_kfunc_fbctl(mb->data); + break; + case OMAP_DSP_MBCMD_KFUNC_AUDIO_PWR: + mbx1_kfunc_audio_pwr(mb->data); + break; + default: + printk(KERN_ERR + "mailbox: Unknown kfunc from DSP: 0x%02x\n", mb->cmd_l); + } +} + +/* + * mailbox interrupt handler + */ +static irqreturn_t mbx1_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + union { + struct mbcmd sw; + struct mbcmd_hw hw; + } *mb = (void *)&mbq.mb[mbq.wp]; + +#if (INT_D2A_MB1 == INT_DSP_MAILBOX1) + mb->hw.data = omap_readw(MAILBOX_DSP2ARM1); + mb->hw.cmd = omap_readw(MAILBOX_DSP2ARM1b); +#elif (INT_D2A_MB1 == INT_DSP_MAILBOX2) + mb->hw.data = omap_readw(MAILBOX_DSP2ARM2); + mb->hw.cmd = omap_readw(MAILBOX_DSP2ARM2b); +#endif + + /* if mbx1 has not been validated yet, discard. */ + if (!mbx1_valid) + return IRQ_HANDLED; + + if (mb->sw.seq != (*mbseq_expect & 1)) { + switch (mbseq_check_level) { + case MBSEQ_CHECK_NONE: + break; + case MBSEQ_CHECK_VERBOSE: + printk(KERN_INFO + "mbx: illegal seq bit!!! ignoring this command." + " (%04x:%04x)\n", mb->hw.cmd, mb->hw.data); + return IRQ_HANDLED; + case MBSEQ_CHECK_SILENT: + return IRQ_HANDLED; + } + } + + (*mbseq_expect)++; + + mbq_inc(mbq.wp); + if (mbq.wp == mbq.rp) { /* mbq is full */ + mbq.full = 1; + disable_irq(INT_D2A_MB1); + } + schedule_work(&mbx1_work); + + return IRQ_HANDLED; +} + +static irqreturn_t mbx2_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned short cmd, data; + +#if (INT_D2A_MB1 == INT_DSP_MAILBOX1) + data = omap_readw(MAILBOX_DSP2ARM2); + cmd = omap_readw(MAILBOX_DSP2ARM2b); +#elif (INT_D2A_MB1 == INT_DSP_MAILBOX2) + data = omap_readw(MAILBOX_DSP2ARM1); + cmd = omap_readw(MAILBOX_DSP2ARM1b); +#endif + printk(KERN_DEBUG + "mailbox2 interrupt! cmd=%04x, data=%04x\n", cmd, data); + + return IRQ_HANDLED; +} + +#if 0 +static void mpuio_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + printk(KERN_INFO "MPUIO interrupt!\n"); +} +#endif + +#ifdef CONFIG_PROC_FS +struct proc_dir_entry *procdir_dsp = NULL; + +static void dsp_create_procdir_dsp(void) +{ + procdir_dsp = proc_mkdir("dsp", 0); + if (procdir_dsp == NULL) { + printk(KERN_ERR + "omapdsp: failed to register proc directory: dsp\n"); + } +} + +static void dsp_remove_procdir_dsp(void) +{ + procdir_dsp = NULL; + remove_proc_entry("dsp", 0); +} +#else /* CONFIG_PROC_FS */ +#define dsp_create_procdir_dsp() do { } while (0) +#define dsp_remove_procdir_dsp() do { } while (0) +#endif /* CONFIG_PROC_FS */ + +extern irqreturn_t dsp_mmu_interrupt(int irq, void *dev_id, + struct pt_regs *regs); + +extern int dsp_ctl_core_init(void); +extern void dsp_ctl_core_exit(void); +extern void dsp_ctl_init(void); +extern void dsp_ctl_exit(void); +extern int dsp_mem_init(void); +extern void dsp_mem_exit(void); +extern void mblog_init(void); +extern void mblog_exit(void); +extern int dsp_taskmod_init(void); +extern void dsp_taskmod_exit(void); + +/* + * device functions + */ +static void dsp_dev_release(struct device *dev) +{ +} + +/* + * driver functions + */ +#if (INT_D2A_MB1 == INT_DSP_MAILBOX1) +# define INT_D2A_MB2 INT_DSP_MAILBOX2 +#elif(INT_D2A_MB1 == INT_DSP_MAILBOX2) /* swap MB1 and MB2 */ +# define INT_D2A_MB2 INT_DSP_MAILBOX1 +#endif + +static int __init dsp_drv_probe(struct platform_device *pdev) +{ + int ret; + + printk(KERN_INFO "OMAP DSP driver initialization\n"); + + //__dsp_enable(); // XXX + + dsp_create_procdir_dsp(); + + if ((ret = dsp_ctl_core_init()) < 0) + goto fail1; + if ((ret = dsp_mem_init()) < 0) + goto fail2; + dsp_ctl_init(); + mblog_init(); + if ((ret = dsp_taskmod_init()) < 0) + goto fail3; + + /* + * mailbox interrupt handlers registration + */ + ret = request_irq(INT_D2A_MB1, mbx1_interrupt, SA_INTERRUPT, "dsp", + &pdev->dev); + if (ret) { + printk(KERN_ERR + "failed to register mailbox1 interrupt: %d\n", ret); + goto fail4; + } + + ret = request_irq(INT_D2A_MB2, mbx2_interrupt, SA_INTERRUPT, "dsp", + &pdev->dev); + if (ret) { + printk(KERN_ERR + "failed to register mailbox2 interrupt: %d\n", ret); + goto fail5; + } + + ret = request_irq(INT_DSP_MMU, dsp_mmu_interrupt, SA_INTERRUPT, "dsp", + &pdev->dev); + if (ret) { + printk(KERN_ERR + "failed to register DSP MMU interrupt: %d\n", ret); + goto fail6; + } + + /* MMU interrupt is not enabled until DSP runs */ + disable_irq(INT_DSP_MMU); + +#if 0 + ret = request_irq(INT_MPUIO, mpuio_interrupt, SA_INTERRUPT, "dsp", dev); + if (ret) { + printk(KERN_ERR + "failed to register MPUIO interrupt: %d\n", ret); + goto fail7; + } +#endif + + return 0; + +fail6: + free_irq(INT_D2A_MB2, &pdev->dev); +fail5: + free_irq(INT_D2A_MB1, &pdev->dev); +fail4: + dsp_taskmod_exit(); +fail3: + mblog_exit(); + dsp_ctl_exit(); + dsp_mem_exit(); +fail2: + dsp_ctl_core_exit(); +fail1: + dsp_remove_procdir_dsp(); + + //__dsp_disable(); // XXX + return ret; +} + +static int dsp_drv_remove(struct platform_device *pdev) +{ + dsp_cpustat_request(CPUSTAT_RESET); + +#if 0 + free_irq(INT_MPUIO, dev); +#endif + free_irq(INT_DSP_MMU, &pdev->dev); + free_irq(INT_D2A_MB2, &pdev->dev); + free_irq(INT_D2A_MB1, &pdev->dev); + + /* recover disable_depth */ + enable_irq(INT_DSP_MMU); + + dspuncfg(); + dsp_taskmod_exit(); + mblog_exit(); + dsp_ctl_exit(); + dsp_mem_exit(); + + dsp_ctl_core_exit(); + dsp_remove_procdir_dsp(); + + //__dsp_disable(); // XXX + + return 0; +} + +#ifdef CONFIG_PM +static int dsp_drv_suspend(struct platform_device *pdev, pm_message_t state) +{ + dsp_suspend(); + + return 0; +} + +static int dsp_drv_resume(struct platform_device *pdev) +{ + dsp_resume(); + + return 0; +} +#else +#define dsp_drv_suspend NULL +#define dsp_drv_resume NULL +#endif /* CONFIG_PM */ + +static struct resource dsp_resources[] = { + { + .start = INT_DSP_MAILBOX1, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_DSP_MAILBOX2, + .flags = IORESOURCE_IRQ, + }, + { + .start = INT_DSP_MMU, + .flags = IORESOURCE_IRQ, + }, +}; + +struct platform_device dsp_device = { + .name = "dsp", + .id = -1, + .dev = { + .release = dsp_dev_release, + }, + .num_resources = ARRAY_SIZE(&dsp_resources), + .resource = dsp_resources, +}; + +static struct platform_driver dsp_driver = { + .probe = dsp_drv_probe, + .remove = dsp_drv_remove, + .suspend = dsp_drv_suspend, + .resume = dsp_drv_resume, + .driver = { + .name = "dsp", + }, +}; + +static int __init omap_dsp_mod_init(void) +{ + int ret; + + ret = platform_device_register(&dsp_device); + if (ret) { + printk(KERN_ERR "failed to register the DSP device: %d\n", ret); + goto fail1; + } + + ret = platform_driver_register(&dsp_driver); + if (ret) { + printk(KERN_ERR "failed to register the DSP driver: %d\n", ret); + goto fail2; + } + + return 0; + +fail2: + platform_device_unregister(&dsp_device); +fail1: + return -ENODEV; +} + +static void __exit omap_dsp_mod_exit(void) +{ + platform_driver_unregister(&dsp_driver); + platform_device_unregister(&dsp_device); +} + +module_init(omap_dsp_mod_init); +module_exit(omap_dsp_mod_exit); diff --git a/arch/arm/plat-omap/dsp/dsp_ctl.c b/arch/arm/plat-omap/dsp/dsp_ctl.c new file mode 100644 index 00000000000..fbe2f5ca699 --- /dev/null +++ b/arch/arm/plat-omap/dsp/dsp_ctl.c @@ -0,0 +1,1015 @@ +/* + * linux/arch/arm/mach-omap/dsp/dsp_ctl.c + * + * OMAP DSP control device driver + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2005/06/09: DSP Gateway version 3.3 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "hardware_dsp.h" +#include "dsp.h" +#include "ipbuf.h" + +static ssize_t loadinfo_show(struct device *dev, struct device_attribute *attr, + char *buf); +static struct device_attribute dev_attr_loadinfo = __ATTR_RO(loadinfo); +extern struct device_attribute dev_attr_ipbuf; + +static enum cfgstat { + CFG_ERR, + CFG_READY, + CFG_SUSPEND +} cfgstat; +int mbx_revision; +static DECLARE_WAIT_QUEUE_HEAD(ioctl_wait_q); +static unsigned short ioctl_wait_cmd; +static DECLARE_MUTEX(ioctl_sem); + +static unsigned char n_stask; + +/* + * control functions + */ +static short varread_val[5]; /* maximum */ + +static int dsp_regread(unsigned short cmd_l, unsigned short adr, + unsigned short *val) +{ + struct mbcmd mb; + int ret = 0; + + if (down_interruptible(&ioctl_sem)) + return -ERESTARTSYS; + + ioctl_wait_cmd = MBCMD(REGRW); + mbcmd_set(mb, MBCMD(REGRW), cmd_l, adr); + dsp_mbcmd_send_and_wait(&mb, &ioctl_wait_q); + + if (ioctl_wait_cmd != 0) { + printk(KERN_ERR "omapdsp: register read error!\n"); + ret = -EINVAL; + goto up_out; + } + + *val = varread_val[0]; + +up_out: + up(&ioctl_sem); + return ret; +} + +static int dsp_regwrite(unsigned short cmd_l, unsigned short adr, + unsigned short val) +{ + struct mbcmd mb; + struct mb_exarg arg = { + .tid = OMAP_DSP_TID_ANON, + .argc = 1, + .argv = &val, + }; + + mbcmd_set(mb, MBCMD(REGRW), cmd_l, adr); + dsp_mbcmd_send_exarg(&mb, &arg); + return 0; +} + +static int dsp_getvar(unsigned char varid, unsigned short *val, int sz) +{ + struct mbcmd mb; + int ret = 0; + + if (down_interruptible(&ioctl_sem)) + return -ERESTARTSYS; + + ioctl_wait_cmd = MBCMD(GETVAR); + mbcmd_set(mb, MBCMD(GETVAR), varid, 0); + dsp_mbcmd_send_and_wait(&mb, &ioctl_wait_q); + + if (ioctl_wait_cmd != 0) { + printk(KERN_ERR "omapdsp: variable read error!\n"); + ret = -EINVAL; + goto up_out; + } + + memcpy(val, varread_val, sz * sizeof(short)); + +up_out: + up(&ioctl_sem); + return ret; +} + +static int dsp_setvar(unsigned char varid, unsigned short val) +{ + dsp_mbsend(MBCMD(SETVAR), varid, val); + return 0; +} + +static int dspcfg(void) +{ + struct mbcmd mb; + int ret = 0; + + if (down_interruptible(&ioctl_sem)) + return -ERESTARTSYS; + + if (cfgstat != CFG_ERR) { + printk(KERN_ERR + "omapdsp: DSP has been already configured. " + "do unconfig!\n"); + ret = -EBUSY; + goto up_out; + } + + /* for safety */ + dsp_mem_usecount_clear(); + + /* + * DSPCFG command and dsp_mem_start() must be called + * while internal mem is on. + */ + dsp_mem_enable((void *)dspmem_base); + + dsp_mb_start(); + dsp_twch_start(); + dsp_mem_start(); + dsp_err_start(); + + mbx_revision = -1; + ioctl_wait_cmd = MBCMD(DSPCFG); + mbcmd_set(mb, MBCMD(DSPCFG), OMAP_DSP_MBCMD_DSPCFG_REQ, 0); + dsp_mbcmd_send_and_wait(&mb, &ioctl_wait_q); + + if (ioctl_wait_cmd != 0) { + printk(KERN_ERR "omapdsp: configuration error!\n"); + ret = -EINVAL; + cfgstat = CFG_ERR; + goto up_out; + } + +#ifdef OLD_BINARY_SUPPORT + /* + * MBREV 3.2 or earlier doesn't assume DMA domain is on + * when DSPCFG command is sent + */ + if ((mbx_revision == MBREV_3_0) || + (mbx_revision == MBREV_3_2)) { + ret = dsp_mbsend(MBCMD(PM), OMAP_DSP_MBCMD_PM_ENABLE, + DSPREG_ICR_DMA_IDLE_DOMAIN); + } +#endif + + if ((ret = dsp_task_config_all(n_stask)) < 0) { + up(&ioctl_sem); + dspuncfg(); + dsp_mem_disable((void *)dspmem_base); + return -EINVAL; + } + + cfgstat = CFG_READY; + + /* send parameter */ + if ((ret = dsp_setvar(OMAP_DSP_MBCMD_VARID_ICRMASK, + dsp_cpustat_get_icrmask())) < 0) + goto up_out; + + /* create runtime sysfs entries */ + device_create_file(&dsp_device.dev, &dev_attr_loadinfo); + device_create_file(&dsp_device.dev, &dev_attr_ipbuf); + +up_out: + dsp_mem_disable((void *)dspmem_base); + up(&ioctl_sem); + return ret; +} + +int dspuncfg(void) +{ + if (dsp_taskmod_busy()) { + printk(KERN_WARNING "omapdsp: tasks are busy.\n"); + return -EBUSY; + } + + if (down_interruptible(&ioctl_sem)) + return -ERESTARTSYS; + + /* FIXME: lock task module */ + + /* remove runtime sysfs entries */ + device_remove_file(&dsp_device.dev, &dev_attr_loadinfo); + device_remove_file(&dsp_device.dev, &dev_attr_ipbuf); + + dsp_mb_stop(); + dsp_twch_stop(); + dsp_mem_stop(); + dsp_err_stop(); + dsp_dbg_stop(); + dsp_task_unconfig_all(); + ipbuf_stop(); + cfgstat = CFG_ERR; + + up(&ioctl_sem); + return 0; +} + +int dsp_is_ready(void) +{ + return (cfgstat == CFG_READY) ? 1 : 0; +} + +/* + * polls all tasks + */ +int dsp_poll(void) +{ + struct mbcmd mb; + int ret = 0; + + if (down_interruptible(&ioctl_sem)) + return -ERESTARTSYS; + + ioctl_wait_cmd = MBCMD(POLL); + mbcmd_set(mb, MBCMD(POLL), 0, 0); + dsp_mbcmd_send_and_wait(&mb, &ioctl_wait_q); + + if (ioctl_wait_cmd != 0) { + printk(KERN_ERR "omapdsp: poll error!\n"); + ret = -EINVAL; + goto up_out; + } + +up_out: + up(&ioctl_sem); + return ret; +} + +void dsp_runlevel(unsigned char level) +{ + if (level == OMAP_DSP_MBCMD_RUNLEVEL_RECOVERY) + dsp_mbsend_recovery(MBCMD(RUNLEVEL), level, 0); + else + dsp_mbsend(MBCMD(RUNLEVEL), level, 0); +} + +static enum cfgstat cfgstat_save_suspend; + +/* + * suspend / resume callbacks + * DSP is not reset within this code, but done in omap_pm_suspend. + * so if these functions are called as OMAP_DSP_IOCTL_SUSPEND, + * DSP should be reset / unreset out of these functions. + */ +int dsp_suspend(void) +{ + struct mbcmd mb; + int ret = 0; + + if (cfgstat == CFG_SUSPEND) { + printk(KERN_ERR "omapdsp: DSP is already in suspend state.\n"); + return -EINVAL; + } + + if (down_interruptible(&ioctl_sem)) + return -ERESTARTSYS; + + cfgstat_save_suspend = cfgstat; + if (!dsp_is_ready()) { + if (dsp_cpustat_get_stat() == CPUSTAT_RUN) { + printk(KERN_WARNING + "omapdsp: illegal operation: trying suspend DSP " + "while it is running but has not configured " + "yet.\n" + " Resetting DSP...\n"); + } + goto transition; + } + + ioctl_wait_cmd = MBCMD(SUSPEND); + mbcmd_set(mb, MBCMD(SUSPEND), 0, 0); + dsp_mbcmd_send_and_wait(&mb, &ioctl_wait_q); + + if (ioctl_wait_cmd != 0) { + printk(KERN_ERR "omapdsp: DSP suspend error!\n"); + ret = -EINVAL; + goto up_out; + } + + udelay(100); +transition: + cfgstat = CFG_SUSPEND; +up_out: + up(&ioctl_sem); + return ret; +} + +int dsp_resume(void) +{ + if (cfgstat != CFG_SUSPEND) { + printk(KERN_ERR "omapdsp: DSP is not in suspend state.\n"); + return -EINVAL; + } + + cfgstat = cfgstat_save_suspend; + return 0; +} + +static void dsp_fbctl_enable(void) +{ + dsp_mbsend(MBCMD(KFUNC), OMAP_DSP_MBCMD_KFUNC_FBCTL, + OMAP_DSP_MBCMD_FBCTL_ENABLE); +} + +static int dsp_fbctl_disable(void) +{ + int ret = 0; + struct mbcmd mb; + + if (down_interruptible(&ioctl_sem)) + return -ERESTARTSYS; + + ioctl_wait_cmd = MBCMD(KFUNC); + mbcmd_set(mb, MBCMD(KFUNC), OMAP_DSP_MBCMD_KFUNC_FBCTL, + OMAP_DSP_MBCMD_FBCTL_DISABLE); + dsp_mbcmd_send_and_wait(&mb, &ioctl_wait_q); + if (ioctl_wait_cmd != 0) { + printk(KERN_ERR "omapdsp: fb disable error!\n"); + ret = -EINVAL; + } + up(&ioctl_sem); + + return ret; +} + +/* + * DSP control device file operations + */ +static int dsp_ctl_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret = 0; + + switch (cmd) { + /* + * command level 1: commands which don't need lock + */ + case OMAP_DSP_IOCTL_RUN: + dsp_cpustat_request(CPUSTAT_RUN); + break; + + case OMAP_DSP_IOCTL_RESET: + dsp_cpustat_request(CPUSTAT_RESET); + break; + + case OMAP_DSP_IOCTL_SETRSTVECT: + ret = dsp_set_rstvect((unsigned long)arg); + break; + + case OMAP_DSP_IOCTL_CPU_IDLE: + dsp_cpustat_request(CPUSTAT_CPU_IDLE); + break; + + case OMAP_DSP_IOCTL_GBL_IDLE: + dsp_cpustat_request(CPUSTAT_GBL_IDLE); + break; + + case OMAP_DSP_IOCTL_MPUI_WORDSWAP_ON: + mpui_wordswap_on(); + break; + + case OMAP_DSP_IOCTL_MPUI_WORDSWAP_OFF: + mpui_wordswap_off(); + break; + + case OMAP_DSP_IOCTL_MPUI_BYTESWAP_ON: + mpui_byteswap_on(); + break; + + case OMAP_DSP_IOCTL_MPUI_BYTESWAP_OFF: + mpui_byteswap_off(); + break; + + case OMAP_DSP_IOCTL_MBSEND: + { + struct omap_dsp_mailbox_cmd u_cmd; + struct mbcmd_hw mb; + if (copy_from_user(&u_cmd, (void *)arg, sizeof(u_cmd))) + return -EFAULT; + mb.cmd = u_cmd.cmd; + mb.data = u_cmd.data; + ret = dsp_mbcmd_send((struct mbcmd *)&mb); + break; + } + + case OMAP_DSP_IOCTL_SETVAR: + { + struct omap_dsp_varinfo var; + if (copy_from_user(&var, (void *)arg, sizeof(var))) + return -EFAULT; + ret = dsp_setvar(var.varid, var.val[0]); + break; + } + + case OMAP_DSP_IOCTL_RUNLEVEL: + dsp_runlevel(arg); + break; + + case OMAP_DSP_IOCTL_FBEN: + dsp_fbctl_enable(); + return 0; + + /* + * command level 2: commands which need lock + */ + case OMAP_DSP_IOCTL_DSPCFG: + ret = dspcfg(); + break; + + case OMAP_DSP_IOCTL_DSPUNCFG: + ret = dspuncfg(); + break; + + case OMAP_DSP_IOCTL_TASKCNT: + ret = dsp_task_count(); + break; + + case OMAP_DSP_IOCTL_POLL: + ret = dsp_poll(); + break; + + case OMAP_DSP_IOCTL_FBDIS: + ret = dsp_fbctl_disable(); + break; + + /* + * FIXME: cpu status control for suspend - resume + */ + case OMAP_DSP_IOCTL_SUSPEND: + if ((ret = dsp_suspend()) < 0) + break; + dsp_cpustat_request(CPUSTAT_RESET); + break; + + case OMAP_DSP_IOCTL_RESUME: + if ((ret = dsp_resume()) < 0) + break; + dsp_cpustat_request(CPUSTAT_RUN); + break; + + case OMAP_DSP_IOCTL_REGMEMR: + { + struct omap_dsp_reginfo *u_reg = (void *)arg; + unsigned short adr, val; + + if (copy_from_user(&adr, &u_reg->adr, sizeof(short))) + return -EFAULT; + if ((ret = dsp_regread(OMAP_DSP_MBCMD_REGRW_MEMR, + adr, &val)) < 0) + return ret; + if (copy_to_user(&u_reg->val, &val, sizeof(short))) + return -EFAULT; + break; + } + + case OMAP_DSP_IOCTL_REGMEMW: + { + struct omap_dsp_reginfo reg; + + if (copy_from_user(®, (void *)arg, sizeof(reg))) + return -EFAULT; + ret = dsp_regwrite(OMAP_DSP_MBCMD_REGRW_MEMW, + reg.adr, reg.val); + break; + } + + case OMAP_DSP_IOCTL_REGIOR: + { + struct omap_dsp_reginfo *u_reg = (void *)arg; + unsigned short adr, val; + + if (copy_from_user(&adr, &u_reg->adr, sizeof(short))) + return -EFAULT; + if ((ret = dsp_regread(OMAP_DSP_MBCMD_REGRW_IOR, + adr, &val)) < 0) + return ret; + if (copy_to_user(&u_reg->val, &val, sizeof(short))) + return -EFAULT; + break; + } + + case OMAP_DSP_IOCTL_REGIOW: + { + struct omap_dsp_reginfo reg; + + if (copy_from_user(®, (void *)arg, sizeof(reg))) + return -EFAULT; + ret = dsp_regwrite(OMAP_DSP_MBCMD_REGRW_IOW, + reg.adr, reg.val); + break; + } + + case OMAP_DSP_IOCTL_GETVAR: + { + struct omap_dsp_varinfo *u_var = (void *)arg; + unsigned char varid; + unsigned short val[5]; /* maximum */ + int argc; + + if (copy_from_user(&varid, &u_var->varid, sizeof(char))) + return -EFAULT; + switch (varid) { + case OMAP_DSP_MBCMD_VARID_ICRMASK: + argc = 1; + break; + case OMAP_DSP_MBCMD_VARID_LOADINFO: + argc = 5; + break; + default: + return -EINVAL; + } + if ((ret = dsp_getvar(varid, val, argc)) < 0) + return ret; + if (copy_to_user(&u_var->val, val, sizeof(short) * argc)) + return -EFAULT; + break; + } + + default: + return -ENOIOCTLCMD; + } + + return ret; +} + +/* + * functions called from mailbox1 interrupt routine + */ +void mbx1_suspend(struct mbcmd *mb) +{ + if (!waitqueue_active(&ioctl_wait_q) || + (ioctl_wait_cmd != MBCMD(SUSPEND))) { + printk(KERN_WARNING + "mbx: SUSPEND command received, " + "but nobody is waiting for it...\n"); + return; + } + + ioctl_wait_cmd = 0; + wake_up_interruptible(&ioctl_wait_q); +} + +void mbx1_dspcfg(struct mbcmd *mb) +{ + unsigned char last = mb->cmd_l & 0x80; + unsigned char cfgcmd = mb->cmd_l & 0x7f; + static unsigned long tmp_ipb_adr; + + /* mailbox protocol check */ + if (cfgcmd == OMAP_DSP_MBCMD_DSPCFG_PROTREV) { + if (!waitqueue_active(&ioctl_wait_q) || + (ioctl_wait_cmd != MBCMD(DSPCFG))) { + printk(KERN_WARNING + "mbx: DSPCFG command received, " + "but nobody is waiting for it...\n"); + return; + } + + mbx_revision = mb->data; + if (mbx_revision == OMAP_DSP_MBPROT_REVISION) + return; +#ifdef OLD_BINARY_SUPPORT + else if ((mbx_revision == MBREV_3_0) || + (mbx_revision == MBREV_3_2)) { + printk(KERN_WARNING + "mbx: ***** old DSP binary *****\n" + " Please update your DSP application.\n"); + return; + } +#endif + else { + printk(KERN_ERR + "mbx: protocol revision check error!\n" + " expected=0x%04x, received=0x%04x\n", + OMAP_DSP_MBPROT_REVISION, mb->data); + mbx_revision = -1; + goto abort1; + } + } + + /* + * following commands are accepted only after + * revision check has been passed. + */ + if (!mbx_revision < 0) { + printk(KERN_INFO + "mbx: DSPCFG command received, " + "but revision check has not been passed.\n"); + return; + } + + if (!waitqueue_active(&ioctl_wait_q) || + (ioctl_wait_cmd != MBCMD(DSPCFG))) { + printk(KERN_WARNING + "mbx: DSPCFG command received, " + "but nobody is waiting for it...\n"); + return; + } + + switch (cfgcmd) { + case OMAP_DSP_MBCMD_DSPCFG_SYSADRH: + tmp_ipb_adr = (unsigned long)mb->data << 16; + break; + + case OMAP_DSP_MBCMD_DSPCFG_SYSADRL: + tmp_ipb_adr |= mb->data; + break; + + case OMAP_DSP_MBCMD_DSPCFG_ABORT: + goto abort1; + + default: + printk(KERN_ERR + "mbx: Unknown CFG command: cmd_l=0x%02x, data=0x%04x\n", + mb->cmd_l, mb->data); + return; + } + + if (last) { + void *badr; + unsigned short bln; + unsigned short bsz; + volatile unsigned short *buf; + void *ipb_sys_da, *ipb_sys_ad; + void *mbseq; + short *dbg_buf; + unsigned short dbg_buf_sz, dbg_line_sz; + struct mem_sync_struct mem_sync, *mem_syncp; + + ipb_sys_da = dspword_to_virt(tmp_ipb_adr); + if (ipbuf_sys_config(ipb_sys_da, DIR_D2A) < 0) + goto abort1; + + if (sync_with_dsp(&ipbuf_sys_da->s, OMAP_DSP_TID_ANON, 10) < 0) { + printk(KERN_ERR "mbx: DSPCFG - IPBUF sync failed!\n"); + goto abort1; + } + /* + * read configuration data on system IPBUF + * we must read with 16bit-access + */ +#ifdef OLD_BINARY_SUPPORT + if (mbx_revision == OMAP_DSP_MBPROT_REVISION) { +#endif + buf = ipbuf_sys_da->d; + n_stask = buf[0]; + bln = buf[1]; + bsz = buf[2]; + badr = MKVIRT(buf[3], buf[4]); + /* ipb_sys_da = MKVIRT(buf[5], buf[6]); */ + ipb_sys_ad = MKVIRT(buf[7], buf[8]); + mbseq = MKVIRT(buf[9], buf[10]); + dbg_buf = MKVIRT(buf[11], buf[12]); + dbg_buf_sz = buf[13]; + dbg_line_sz = buf[14]; + mem_sync.DARAM = MKVIRT(buf[15], buf[16]); + mem_sync.SARAM = MKVIRT(buf[17], buf[18]); + mem_sync.SDRAM = MKVIRT(buf[19], buf[20]); + mem_syncp = &mem_sync; +#ifdef OLD_BINARY_SUPPORT + } else if (mbx_revision == MBREV_3_2) { + buf = ipbuf_sys_da->d; + n_stask = buf[0]; + bln = buf[1]; + bsz = buf[2]; + badr = MKVIRT(buf[3], buf[4]); + /* ipb_sys_da = MKVIRT(buf[5], buf[6]); */ + ipb_sys_ad = MKVIRT(buf[7], buf[8]); + mbseq = MKVIRT(buf[9], buf[10]); + dbg_buf = NULL; + dbg_buf_sz = 0; + dbg_line_sz = 0; + mem_syncp = NULL; + } else if (mbx_revision == MBREV_3_0) { + buf = ipbuf_sys_da->d; + n_stask = buf[0]; + bln = buf[1]; + bsz = buf[2]; + badr = MKVIRT(buf[3], buf[4]); + /* bkeep = buf[5]; */ + /* ipb_sys_da = MKVIRT(buf[6], buf[7]); */ + ipb_sys_ad = MKVIRT(buf[8], buf[9]); + mbseq = MKVIRT(buf[10], buf[11]); + dbg_buf = NULL; + dbg_buf_sz = 0; + dbg_line_sz = 0; + mem_syncp = NULL; + } else /* should not occur */ + goto abort1; +#endif /* OLD_BINARY_SUPPORT */ + + release_ipbuf_pvt(ipbuf_sys_da); + + /* + * following configurations need to be done before + * waking up the dspcfg initiator process. + */ + if (ipbuf_sys_config(ipb_sys_ad, DIR_A2D) < 0) + goto abort1; + if (ipbuf_config(bln, bsz, badr) < 0) + goto abort1; + if (dsp_mb_config(mbseq) < 0) + goto abort2; + if (dsp_dbg_config(dbg_buf, dbg_buf_sz, dbg_line_sz) < 0) + goto abort2; + if (dsp_mem_sync_config(mem_syncp) < 0) + goto abort2; + + ioctl_wait_cmd = 0; + wake_up_interruptible(&ioctl_wait_q); + } + return; + +abort2: + ipbuf_stop(); +abort1: + wake_up_interruptible(&ioctl_wait_q); + return; +} + +void mbx1_poll(struct mbcmd *mb) +{ + if (!waitqueue_active(&ioctl_wait_q) || + (ioctl_wait_cmd != MBCMD(POLL))) { + printk(KERN_WARNING + "mbx: POLL command received, " + "but nobody is waiting for it...\n"); + return; + } + + ioctl_wait_cmd = 0; + wake_up_interruptible(&ioctl_wait_q); +} + +void mbx1_regrw(struct mbcmd *mb) +{ + if (!waitqueue_active(&ioctl_wait_q) || + (ioctl_wait_cmd != MBCMD(REGRW))) { + printk(KERN_WARNING + "mbx: REGRW command received, " + "but nobody is waiting for it...\n"); + return; + } + + switch (mb->cmd_l) { + case OMAP_DSP_MBCMD_REGRW_DATA: + ioctl_wait_cmd = 0; + varread_val[0] = mb->data; + wake_up_interruptible(&ioctl_wait_q); + return; + + default: + printk(KERN_ERR + "mbx: Illegal REGRW command: " + "cmd_l=0x%02x, data=0x%04x\n", mb->cmd_l, mb->data); + return; + } +} + +void mbx1_getvar(struct mbcmd *mb) +{ + unsigned char varid = mb->cmd_l; + int i; + volatile unsigned short *buf; + + if (!waitqueue_active(&ioctl_wait_q) || + (ioctl_wait_cmd != MBCMD(GETVAR))) { + printk(KERN_WARNING + "mbx: GETVAR command received, " + "but nobody is waiting for it...\n"); + return; + } + + ioctl_wait_cmd = 0; + switch (varid) { + case OMAP_DSP_MBCMD_VARID_ICRMASK: + varread_val[0] = mb->data; + break; + case OMAP_DSP_MBCMD_VARID_LOADINFO: + { + if (sync_with_dsp(&ipbuf_sys_da->s, OMAP_DSP_TID_ANON, 10) < 0) { + printk(KERN_ERR + "mbx: GETVAR - IPBUF sync failed!\n"); + return; + } + /* need word access. do not use memcpy. */ + buf = ipbuf_sys_da->d; + for (i = 0; i < 5; i++) { + varread_val[i] = buf[i]; + } + release_ipbuf_pvt(ipbuf_sys_da); + break; + } + } + wake_up_interruptible(&ioctl_wait_q); + + return; +} + +/* + * sysfs files + */ +static ssize_t ifver_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int len = 0; + + /* + * I/F VERSION descriptions: + * + * 3.2: sysfs / udev support + * KMEM_RESERVE / KMEM_RELEASE ioctls for mem device + * 3.3: added following ioctls + * OMAP_DSP_IOCTL_GBL_IDLE + * OMAP_DSP_IOCTL_CPU_IDLE (instead of OMAP_DSP_IOCTL_IDLE) + * OMAP_DSP_IOCTL_POLL + */ + + /* + * print all supporting I/F VERSIONs, like followings. + * + * len += sprintf(buf, "3.2\n"); + * len += sprintf(buf, "3.3\n"); + */ + len += sprintf(buf + len, "3.2\n"); + len += sprintf(buf + len, "3.3\n"); + + return len; +} + +static struct device_attribute dev_attr_ifver = __ATTR_RO(ifver); + +static ssize_t cpustat_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\n", cpustat_name(dsp_cpustat_get_stat())); +} + +static struct device_attribute dev_attr_cpustat = __ATTR_RO(cpustat); + +static ssize_t icrmask_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "0x%04x\n", dsp_cpustat_get_icrmask()); +} + +static ssize_t icrmask_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned short mask; + int ret; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + mask = simple_strtol(buf, NULL, 16); + dsp_cpustat_set_icrmask(mask); + + if (dsp_is_ready()) { + ret = dsp_setvar(OMAP_DSP_MBCMD_VARID_ICRMASK, mask); + if (ret < 0) + return ret; + } + + return strlen(buf); +} + +static struct device_attribute dev_attr_icrmask = + __ATTR(icrmask, S_IWUSR | S_IRUGO, icrmask_show, icrmask_store); + +static ssize_t loadinfo_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int len; + int ret; + static unsigned short val[5]; + + if ((ret = dsp_getvar(OMAP_DSP_MBCMD_VARID_LOADINFO, val, 5)) < 0) + return ret; + + /* load info value range is 0(free) - 10000(busy) */ + len = sprintf(buf, + "DSP load info:\n" + " 10ms average = %3d.%02d%%\n" + " 1sec average = %3d.%02d%% busiest 10ms = %3d.%02d%%\n" + " 1min average = %3d.%02d%% busiest 1s = %3d.%02d%%\n", + val[0]/100, val[0]%100, + val[1]/100, val[1]%100, val[2]/100, val[2]%100, + val[3]/100, val[3]%100, val[4]/100, val[4]%100); + return len; +} + +/* + * This is declared at the top of this file. + * + * static struct device_attribute dev_attr_loadinfo = __ATTR_RO(loadinfo); + */ + +void mbx1_fbctl_disable(void) +{ + if (!waitqueue_active(&ioctl_wait_q) || + (ioctl_wait_cmd != MBCMD(KFUNC))) { + printk(KERN_WARNING + "mbx: KFUNC:FBCTL command received, " + "but nobody is waiting for it...\n"); + return; + } + ioctl_wait_cmd = 0; + wake_up_interruptible(&ioctl_wait_q); +} + +#ifdef CONFIG_PROC_FS +/* for backward compatibility */ +static int version_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + /* + * This entry is read by 3.1 tools only, so leave it as is. + * 3.2 and later will read from sysfs file. + */ + return sprintf(page, "3.1\n"); +} + +static void __init dsp_ctl_create_proc(void) +{ + struct proc_dir_entry *ent; + + /* version */ + ent = create_proc_read_entry("version", 0, procdir_dsp, + version_read_proc, NULL); + if (ent == NULL) { + printk(KERN_ERR + "omapdsp: failed to register proc device: version\n"); + } +} + +static void dsp_ctl_remove_proc(void) +{ + remove_proc_entry("version", procdir_dsp); +} +#endif /* CONFIG_PROC_FS */ + +struct file_operations dsp_ctl_fops = { + .owner = THIS_MODULE, + .ioctl = dsp_ctl_ioctl, +}; + +void __init dsp_ctl_init(void) +{ + device_create_file(&dsp_device.dev, &dev_attr_ifver); + device_create_file(&dsp_device.dev, &dev_attr_cpustat); + device_create_file(&dsp_device.dev, &dev_attr_icrmask); +#ifdef CONFIG_PROC_FS + dsp_ctl_create_proc(); +#endif +} + +void dsp_ctl_exit(void) +{ + device_remove_file(&dsp_device.dev, &dev_attr_ifver); + device_remove_file(&dsp_device.dev, &dev_attr_cpustat); + device_remove_file(&dsp_device.dev, &dev_attr_icrmask); +#ifdef CONFIG_PROC_FS + dsp_ctl_remove_proc(); +#endif +} diff --git a/arch/arm/plat-omap/dsp/dsp_ctl_core.c b/arch/arm/plat-omap/dsp/dsp_ctl_core.c new file mode 100644 index 00000000000..8207441096b --- /dev/null +++ b/arch/arm/plat-omap/dsp/dsp_ctl_core.c @@ -0,0 +1,123 @@ +/* + * linux/arch/arm/mach-omap/dsp/dsp_ctl_core.c + * + * OMAP DSP control devices core driver + * + * Copyright (C) 2004,2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2005/07/26: DSP Gateway version 3.3 + */ + +#include +#include +#include +#include +#include +#include +#include "hardware_dsp.h" + +#define CTL_MINOR 0 +#define MEM_MINOR 1 +#define TWCH_MINOR 2 +#define ERR_MINOR 3 + +static struct class *dsp_ctl_class; +extern struct file_operations dsp_ctl_fops, + dsp_mem_fops, + dsp_twch_fops, + dsp_err_fops; + +static int dsp_ctl_core_open(struct inode *inode, struct file *file) +{ + switch (iminor(inode)) { + case CTL_MINOR: + file->f_op = &dsp_ctl_fops; + break; + case MEM_MINOR: + file->f_op = &dsp_mem_fops; + break; + case TWCH_MINOR: + file->f_op = &dsp_twch_fops; + break; + case ERR_MINOR: + file->f_op = &dsp_err_fops; + break; + default: + return -ENXIO; + } + if (file->f_op && file->f_op->open) + return file->f_op->open(inode, file); + return 0; +} + +static struct file_operations dsp_ctl_core_fops = { + .owner = THIS_MODULE, + .open = dsp_ctl_core_open, +}; + +static const struct dev_list { + unsigned int minor; + char *devname; + umode_t mode; +} dev_list[] = { + {CTL_MINOR, "dspctl", S_IRUSR | S_IWUSR}, + {MEM_MINOR, "dspmem", S_IRUSR | S_IWUSR | S_IRGRP}, + {TWCH_MINOR, "dsptwch", S_IRUSR | S_IWUSR | S_IRGRP}, + {ERR_MINOR, "dsperr", S_IRUSR | S_IRGRP}, +}; + +int __init dsp_ctl_core_init(void) +{ + int retval; + int i; + struct class_device *cdev; + + retval = register_chrdev(OMAP_DSP_CTL_MAJOR, "dspctl", + &dsp_ctl_core_fops); + if (retval < 0) { + printk(KERN_ERR + "omapdsp: failed to register dspctl device: %d\n", + retval); + return retval; + } + + dsp_ctl_class = class_create(THIS_MODULE, "dspctl"); + for (i = 0; i < ARRAY_SIZE(dev_list); i++) { + cdev = class_device_create(dsp_ctl_class, NULL, + MKDEV(OMAP_DSP_CTL_MAJOR, + dev_list[i].minor), + NULL, dev_list[i].devname); + } + + return 0; +} + +void dsp_ctl_core_exit(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(dev_list); i++) { + class_device_destroy(dsp_ctl_class, + MKDEV(OMAP_DSP_CTL_MAJOR, + dev_list[i].minor)); + } + class_destroy(dsp_ctl_class); + + unregister_chrdev(OMAP_DSP_CTL_MAJOR, "dspctl"); +} diff --git a/arch/arm/plat-omap/dsp/dsp_mem.c b/arch/arm/plat-omap/dsp/dsp_mem.c new file mode 100644 index 00000000000..109d9732c2d --- /dev/null +++ b/arch/arm/plat-omap/dsp/dsp_mem.c @@ -0,0 +1,1941 @@ +/* + * linux/arch/arm/mach-omap/dsp/dsp_mem.c + * + * OMAP DSP memory driver + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * Conversion to mempool API and ARM MMU section mapping + * by Paul Mundt + * + * 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 + * + * Toshihiro Kobayashi + * 2005/06/09: DSP Gateway version 3.3 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "uaccess_dsp.h" +#include "ipbuf.h" +#include "dsp.h" + +#define SZ_1MB 0x100000 +#define SZ_64KB 0x10000 +#define SZ_4KB 0x1000 +#define SZ_1KB 0x400 +#define is_aligned(adr,align) (!((adr)&((align)-1))) +#define ORDER_1MB (20 - PAGE_SHIFT) +#define ORDER_64KB (16 - PAGE_SHIFT) +#define ORDER_4KB (12 - PAGE_SHIFT) + +#define PGDIR_MASK (~(PGDIR_SIZE-1)) +#define PGDIR_ALIGN(addr) (((addr)+PGDIR_SIZE-1)&(PGDIR_MASK)) + +#define dsp_mmu_enable() \ + do { \ + omap_writew(DSPMMU_CNTL_MMU_EN | DSPMMU_CNTL_RESET_SW, \ + DSPMMU_CNTL); \ + } while(0) +#define dsp_mmu_disable() \ + do { omap_writew(0, DSPMMU_CNTL); } while(0) +#define dsp_mmu_flush() \ + do { \ + omap_writew(DSPMMU_FLUSH_ENTRY_FLUSH_ENTRY, \ + DSPMMU_FLUSH_ENTRY); \ + } while(0) +#define __dsp_mmu_gflush() \ + do { omap_writew(DSPMMU_GFLUSH_GFLUSH, DSPMMU_GFLUSH); } while(0) +#define __dsp_mmu_itack() \ + do { omap_writew(DSPMMU_IT_ACK_IT_ACK, DSPMMU_IT_ACK); } while(0) + +#define EMIF_PRIO_LB_MASK 0x0000f000 +#define EMIF_PRIO_LB_SHIFT 12 +#define EMIF_PRIO_DMA_MASK 0x00000f00 +#define EMIF_PRIO_DMA_SHIFT 8 +#define EMIF_PRIO_DSP_MASK 0x00000070 +#define EMIF_PRIO_DSP_SHIFT 4 +#define EMIF_PRIO_MPU_MASK 0x00000007 +#define EMIF_PRIO_MPU_SHIFT 0 +#define set_emiff_dma_prio(prio) \ + do { \ + omap_writel((omap_readl(OMAP_TC_OCPT1_PRIOR) & \ + ~EMIF_PRIO_DMA_MASK) | \ + ((prio) << EMIF_PRIO_DMA_SHIFT), \ + OMAP_TC_OCPT1_PRIOR); \ + } while(0) + +enum exmap_type { + EXMAP_TYPE_MEM, + EXMAP_TYPE_FB +}; + +struct exmap_tbl { + unsigned int valid:1; + unsigned int cntnu:1; /* grouping */ + int usecount; /* reference count by mmap */ + enum exmap_type type; + void *buf; /* virtual address of the buffer, + * i.e. 0xc0000000 - */ + void *vadr; /* DSP shadow space, + * i.e. 0xe0000000 - 0xe0ffffff */ + unsigned int order; +}; +#define DSPMMU_TLB_LINES 32 +static struct exmap_tbl exmap_tbl[DSPMMU_TLB_LINES]; +static DECLARE_RWSEM(exmap_sem); + +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL +static struct omapfb_notifier_block *omapfb_nb; +static int omapfb_ready; +#endif + +static int dsp_exunmap(unsigned long dspadr); + +static void *dspvect_page; +static unsigned long dsp_fault_adr; +static struct mem_sync_struct mem_sync; + +static void *mempool_alloc_from_pool(mempool_t *pool, + unsigned int __nocast gfp_mask) +{ + spin_lock_irq(&pool->lock); + if (likely(pool->curr_nr)) { + void *element = pool->elements[--pool->curr_nr]; + spin_unlock_irq(&pool->lock); + return element; + } + + spin_unlock_irq(&pool->lock); + return mempool_alloc(pool, gfp_mask); +} + +static __inline__ unsigned long lineup_offset(unsigned long adr, + unsigned long ref, + unsigned long mask) +{ + unsigned long newadr; + + newadr = (adr & ~mask) | (ref & mask); + if (newadr < adr) + newadr += mask + 1; + return newadr; +} + +void dsp_mem_sync_inc(void) +{ + /* + * FIXME: dsp_mem_enable()!!! + */ + if (mem_sync.DARAM) + mem_sync.DARAM->ad_arm++; + if (mem_sync.SARAM) + mem_sync.SARAM->ad_arm++; + if (mem_sync.SDRAM) + mem_sync.SDRAM->ad_arm++; +} + +/* + * dsp_mem_sync_config() is called from mbx1 workqueue + */ +int dsp_mem_sync_config(struct mem_sync_struct *sync) +{ + size_t sync_seq_sz = sizeof(struct sync_seq); + +#ifdef OLD_BINARY_SUPPORT + if (sync == NULL) { + memset(&mem_sync, 0, sizeof(struct mem_sync_struct)); + return 0; + } +#endif + if ((dsp_mem_type(sync->DARAM, sync_seq_sz) != MEM_TYPE_DARAM) || + (dsp_mem_type(sync->SARAM, sync_seq_sz) != MEM_TYPE_SARAM) || + (dsp_mem_type(sync->SDRAM, sync_seq_sz) != MEM_TYPE_EXTERN)) { + printk(KERN_ERR + "omapdsp: mem_sync address validation failure!\n" + " mem_sync.DARAM = 0x%p,\n" + " mem_sync.SARAM = 0x%p,\n" + " mem_sync.SDRAM = 0x%p,\n", + sync->DARAM, sync->SARAM, sync->SDRAM); + return -1; + } + memcpy(&mem_sync, sync, sizeof(struct mem_sync_struct)); + return 0; +} + +static mempool_t *kmem_pool_1M; +static mempool_t *kmem_pool_64K; + +static void *dsp_pool_alloc(unsigned int __nocast gfp, void *order) +{ + return (void *)__get_dma_pages(gfp, (unsigned int)order); +} + +static void dsp_pool_free(void *buf, void *order) +{ + free_pages((unsigned long)buf, (unsigned int)order); +} + +static void dsp_kmem_release(void) +{ + if (kmem_pool_64K) { + mempool_destroy(kmem_pool_64K); + kmem_pool_64K = NULL; + } + + if (kmem_pool_1M) { + mempool_destroy(kmem_pool_1M); + kmem_pool_1M = NULL; + } +} + +static int dsp_kmem_reserve(unsigned long size) +{ + unsigned long len = size; + + /* alignment check */ + if (!is_aligned(size, SZ_64KB)) { + printk(KERN_ERR + "omapdsp: size(0x%lx) is not multiple of 64KB.\n", size); + return -EINVAL; + } + if (size > DSPSPACE_SIZE) { + printk(KERN_ERR + "omapdsp: size(0x%lx) is larger than DSP memory space " + "size (0x%x.\n", size, DSPSPACE_SIZE); + return -EINVAL; + } + + if (size >= SZ_1MB) { + int nr = size >> 20; + + if (likely(!kmem_pool_1M)) + kmem_pool_1M = mempool_create(nr, + dsp_pool_alloc, + dsp_pool_free, + (void *)ORDER_1MB); + else + mempool_resize(kmem_pool_1M, kmem_pool_1M->min_nr + nr, + GFP_KERNEL); + + size &= ~(0xf << 20); + } + + if (size >= SZ_64KB) { + int nr = size >> 16; + + if (likely(!kmem_pool_64K)) + kmem_pool_64K = mempool_create(nr, + dsp_pool_alloc, + dsp_pool_free, + (void *)ORDER_64KB); + else + mempool_resize(kmem_pool_64K, + kmem_pool_64K->min_nr + nr, GFP_KERNEL); + + size &= ~(0xf << 16); + } + + if (size) + len -= size; + + return len; +} + +static void dsp_mem_free_pages(unsigned long buf, unsigned int order) +{ + struct page *page, *ps, *pe; + + ps = virt_to_page(buf); + pe = virt_to_page(buf + (1 << (PAGE_SHIFT + order))); + + for (page = ps; page < pe; page++) + ClearPageReserved(page); + + if (buf) { + if ((order == ORDER_64KB) && likely(kmem_pool_64K)) + mempool_free((void *)buf, kmem_pool_64K); + else if ((order == ORDER_1MB) && likely(kmem_pool_1M)) + mempool_free((void *)buf, kmem_pool_1M); + else + free_pages(buf, order); + } +} + +static inline void +exmap_alloc_pte(unsigned long virt, unsigned long phys, pgprot_t prot) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + pte_t *pte; + + pgd = pgd_offset_k(virt); + pud = pud_offset(pgd, virt); + pmd = pmd_offset(pud, virt); + + if (pmd_none(*pmd)) { + pte = pte_alloc_one_kernel(&init_mm, 0); + if (!pte) + return; + + /* note: two PMDs will be set */ + pmd_populate_kernel(&init_mm, pmd, pte); + } + + pte = pte_offset_kernel(pmd, virt); + set_pte(pte, pfn_pte(phys >> PAGE_SHIFT, prot)); +} + +static inline int +exmap_alloc_sect(unsigned long virt, unsigned long phys, int prot) +{ + pgd_t *pgd; + pud_t *pud; + pmd_t *pmd; + + pgd = pgd_offset_k(virt); + pud = pud_alloc(&init_mm, pgd, virt); + pmd = pmd_alloc(&init_mm, pud, virt); + + if (virt & (1 << 20)) + pmd++; + + if (!pmd_none(*pmd)) + /* No good, fall back on smaller mappings. */ + return -EINVAL; + + *pmd = __pmd(phys | prot); + flush_pmd_entry(pmd); + + return 0; +} + +/* + * ARM MMU operations + */ +static int exmap_set_armmmu(unsigned long virt, unsigned long phys, + unsigned long size) +{ + long off; + pgprot_t prot_pte; + int prot_sect; + + printk(KERN_DEBUG + "omapdsp: mapping in ARM MMU, v=0x%08lx, p=0x%08lx, sz=0x%lx\n", + virt, phys, size); + + prot_pte = __pgprot(L_PTE_PRESENT | L_PTE_YOUNG | + L_PTE_DIRTY | L_PTE_WRITE); + + prot_sect = PMD_TYPE_SECT | PMD_SECT_UNCACHED | + PMD_SECT_AP_WRITE | PMD_DOMAIN(DOMAIN_IO); + + if (cpu_architecture() <= CPU_ARCH_ARMv5) + prot_sect |= PMD_BIT4; + + off = phys - virt; + + while ((virt & 0xfffff || (virt + off) & 0xfffff) && size >= PAGE_SIZE) { + exmap_alloc_pte(virt, virt + off, prot_pte); + + virt += PAGE_SIZE; + size -= PAGE_SIZE; + } + + /* XXX: Not yet.. confuses dspfb -- PFM. */ +#if 0 + while (size >= (PGDIR_SIZE / 2)) { + if (exmap_alloc_sect(virt, virt + off, prot_sect) < 0) + break; + + virt += (PGDIR_SIZE / 2); + size -= (PGDIR_SIZE / 2); + } +#endif + + while (size >= PAGE_SIZE) { + exmap_alloc_pte(virt, virt + off, prot_pte); + + virt += PAGE_SIZE; + size -= PAGE_SIZE; + } + + BUG_ON(size); + + return 0; +} + +static inline void +exmap_clear_pte_range(pmd_t *pmd, unsigned long addr, unsigned long end) +{ + pte_t *pte; + + pte = pte_offset_map(pmd, addr); + do { + if (pte_none(*pte)) + continue; + + pte_clear(&init_mm, addr, pte); + } while (pte++, addr += PAGE_SIZE, addr != end); + + pte_unmap(pte - 1); +} + +static inline void +exmap_clear_pmd_range(pud_t *pud, unsigned long addr, unsigned long end) +{ + pmd_t *pmd; + unsigned long next; + + pmd = pmd_offset(pud, addr); + do { + next = pmd_addr_end(addr, end); + + if (addr & (1 << 20)) + pmd++; + + if ((pmd_val(*pmd) & PMD_TYPE_MASK) == PMD_TYPE_SECT) { + *pmd = __pmd(0); + clean_pmd_entry(pmd); + continue; + } + + if (pmd_none_or_clear_bad(pmd)) + continue; + + exmap_clear_pte_range(pmd, addr, next); + } while (pmd++, addr = next, addr != end); +} + +static inline void +exmap_clear_pud_range(pgd_t *pgd, unsigned long addr, unsigned long end) +{ + pud_t *pud; + unsigned long next; + + pud = pud_offset(pgd, addr); + do { + next = pud_addr_end(addr, end); + if (pud_none_or_clear_bad(pud)) + continue; + + exmap_clear_pmd_range(pud, addr, next); + } while (pud++, addr = next, addr != end); +} + +static void exmap_clear_armmmu(unsigned long virt, unsigned long size) +{ + unsigned long next, end; + pgd_t *pgd; + + printk(KERN_DEBUG + "omapdsp: unmapping in ARM MMU, v=0x%08lx, sz=0x%lx\n", + virt, size); + + pgd = pgd_offset_k(virt); + end = virt + size; + do { + next = pgd_addr_end(virt, end); + if (pgd_none_or_clear_bad(pgd)) + continue; + + exmap_clear_pud_range(pgd, virt, next); + } while (pgd++, virt = next, virt != end); +} + +static int exmap_valid(void *vadr, size_t len) +{ + /* exmap_sem should be held before calling this function */ + int i; + +start: + for (i = 0; i < DSPMMU_TLB_LINES; i++) { + void *mapadr; + unsigned long mapsize; + struct exmap_tbl *ent = &exmap_tbl[i]; + + if (!ent->valid) + continue; + mapadr = (void *)ent->vadr; + mapsize = 1 << (ent->order + PAGE_SHIFT); + if ((vadr >= mapadr) && (vadr < mapadr + mapsize)) { + if (vadr + len <= mapadr + mapsize) { + /* this map covers whole address. */ + return 1; + } else { + /* + * this map covers partially. + * check rest portion. + */ + len -= mapadr + mapsize - vadr; + vadr = mapadr + mapsize; + goto start; + } + } + } + + return 0; +} + +enum dsp_mem_type_e dsp_mem_type(void *vadr, size_t len) +{ + void *ds = (void *)daram_base; + void *de = (void *)daram_base + daram_size; + void *ss = (void *)saram_base; + void *se = (void *)saram_base + saram_size; + int ret; + + if ((vadr >= ds) && (vadr < de)) { + if (vadr + len > de) + return MEM_TYPE_CROSSING; + else + return MEM_TYPE_DARAM; + } else if ((vadr >= ss) && (vadr < se)) { + if (vadr + len > se) + return MEM_TYPE_CROSSING; + else + return MEM_TYPE_SARAM; + } else { + down_read(&exmap_sem); + if (exmap_valid(vadr, len)) + ret = MEM_TYPE_EXTERN; + else + ret = MEM_TYPE_NONE; + up_read(&exmap_sem); + return ret; + } +} + +int dsp_address_validate(void *p, size_t len, char *fmt, ...) +{ + if (dsp_mem_type(p, len) <= 0) { + if (fmt != NULL) { + char s[64]; + va_list args; + + va_start(args, fmt); + vsprintf(s, fmt, args); + va_end(args); + printk(KERN_ERR + "omapdsp: %s address(0x%p) and size(0x%x) is " + "not valid!\n" + " (crossing different type of memories, or \n" + " external memory space where no " + "actual memory is mapped)\n", + s, p, len); + } + return -1; + } + + return 0; +} + +/* + * exmap_use(), unuse(): + * when the mapped area is exported to user space with mmap, + * the usecount is incremented. + * while the usecount > 0, that area can't be released. + */ +void exmap_use(void *vadr, size_t len) +{ + int i; + + down_write(&exmap_sem); + for (i = 0; i < DSPMMU_TLB_LINES; i++) { + void *mapadr; + unsigned long mapsize; + struct exmap_tbl *ent = &exmap_tbl[i]; + + if (!ent->valid) + continue; + mapadr = (void *)ent->vadr; + mapsize = 1 << (ent->order + PAGE_SHIFT); + if ((vadr + len > mapadr) && (vadr < mapadr + mapsize)) { + ent->usecount++; + } + } + up_write(&exmap_sem); +} + +void exmap_unuse(void *vadr, size_t len) +{ + int i; + + down_write(&exmap_sem); + for (i = 0; i < DSPMMU_TLB_LINES; i++) { + void *mapadr; + unsigned long mapsize; + struct exmap_tbl *ent = &exmap_tbl[i]; + + if (!ent->valid) + continue; + mapadr = (void *)ent->vadr; + mapsize = 1 << (ent->order + PAGE_SHIFT); + if ((vadr + len > mapadr) && (vadr < mapadr + mapsize)) { + ent->usecount--; + } + } + up_write(&exmap_sem); +} + +/* + * dsp_virt_to_phys() + * returns physical address, and sets len to valid length + */ +unsigned long dsp_virt_to_phys(void *vadr, size_t *len) +{ + int i; + + if (is_dsp_internal_mem(vadr)) { + /* DSRAM or SARAM */ + *len = dspmem_base + dspmem_size - (unsigned long)vadr; + return (unsigned long)vadr; + } + + /* EXRAM */ + for (i = 0; i < DSPMMU_TLB_LINES; i++) { + void *mapadr; + unsigned long mapsize; + struct exmap_tbl *ent = &exmap_tbl[i]; + + if (!ent->valid) + continue; + mapadr = (void *)ent->vadr; + mapsize = 1 << (ent->order + PAGE_SHIFT); + if ((vadr >= mapadr) && (vadr < mapadr + mapsize)) { + *len = mapadr + mapsize - vadr; + return __pa(ent->buf) + vadr - mapadr; + } + } + + /* valid mapping not found */ + return 0; +} + +/* + * DSP MMU operations + */ +static __inline__ unsigned short get_cam_l_va_mask(unsigned short slst) +{ + switch (slst) { + case DSPMMU_CAM_L_SLST_1MB: + return DSPMMU_CAM_L_VA_TAG_L1_MASK | + DSPMMU_CAM_L_VA_TAG_L2_MASK_1MB; + case DSPMMU_CAM_L_SLST_64KB: + return DSPMMU_CAM_L_VA_TAG_L1_MASK | + DSPMMU_CAM_L_VA_TAG_L2_MASK_64KB; + case DSPMMU_CAM_L_SLST_4KB: + return DSPMMU_CAM_L_VA_TAG_L1_MASK | + DSPMMU_CAM_L_VA_TAG_L2_MASK_4KB; + case DSPMMU_CAM_L_SLST_1KB: + return DSPMMU_CAM_L_VA_TAG_L1_MASK | + DSPMMU_CAM_L_VA_TAG_L2_MASK_1KB; + } + return 0; +} + +static __inline__ void get_tlb_lock(int *base, int *victim) +{ + unsigned short lock = omap_readw(DSPMMU_LOCK); + if (base != NULL) + *base = (lock & DSPMMU_LOCK_BASE_MASK) + >> DSPMMU_LOCK_BASE_SHIFT; + if (victim != NULL) + *victim = (lock & DSPMMU_LOCK_VICTIM_MASK) + >> DSPMMU_LOCK_VICTIM_SHIFT; +} + +static __inline__ void set_tlb_lock(int base, int victim) +{ + omap_writew((base << DSPMMU_LOCK_BASE_SHIFT) | + (victim << DSPMMU_LOCK_VICTIM_SHIFT), DSPMMU_LOCK); +} + +static __inline__ void __read_tlb(unsigned short lbase, unsigned short victim, + unsigned short *cam_h, unsigned short *cam_l, + unsigned short *ram_h, unsigned short *ram_l) +{ + /* set victim */ + set_tlb_lock(lbase, victim); + + /* read a TLB entry */ + omap_writew(DSPMMU_LD_TLB_RD, DSPMMU_LD_TLB); + + if (cam_h != NULL) + *cam_h = omap_readw(DSPMMU_READ_CAM_H); + if (cam_l != NULL) + *cam_l = omap_readw(DSPMMU_READ_CAM_L); + if (ram_h != NULL) + *ram_h = omap_readw(DSPMMU_READ_RAM_H); + if (ram_l != NULL) + *ram_l = omap_readw(DSPMMU_READ_RAM_L); +} + +static __inline__ void __load_tlb(unsigned short cam_h, unsigned short cam_l, + unsigned short ram_h, unsigned short ram_l) +{ + omap_writew(cam_h, DSPMMU_CAM_H); + omap_writew(cam_l, DSPMMU_CAM_L); + omap_writew(ram_h, DSPMMU_RAM_H); + omap_writew(ram_l, DSPMMU_RAM_L); + + /* flush the entry */ + dsp_mmu_flush(); + + /* load a TLB entry */ + omap_writew(DSPMMU_LD_TLB_LD, DSPMMU_LD_TLB); +} + +static int dsp_mmu_load_tlb(unsigned long vadr, unsigned long padr, + unsigned short slst, unsigned short prsvd, + unsigned short ap) +{ + int lbase, victim; + unsigned short cam_l_va_mask; + + clk_enable(dsp_ck_handle); + + get_tlb_lock(&lbase, NULL); + for (victim = 0; victim < lbase; victim++) { + unsigned short cam_l; + + /* read a TLB entry */ + __read_tlb(lbase, victim, NULL, &cam_l, NULL, NULL); + if (!(cam_l & DSPMMU_CAM_L_V)) + goto found_victim; + } + set_tlb_lock(lbase, victim); + +found_victim: + /* The last (31st) entry cannot be locked? */ + if (victim == 31) { + printk(KERN_ERR "omapdsp: TLB is full.\n"); + return -EBUSY; + } + + cam_l_va_mask = get_cam_l_va_mask(slst); + if (vadr & + ~(DSPMMU_CAM_H_VA_TAG_H_MASK << 22 | + (unsigned long)cam_l_va_mask << 6)) { + printk(KERN_ERR + "omapdsp: mapping vadr (0x%06lx) is not " + "aligned boundary\n", vadr); + return -EINVAL; + } + + __load_tlb(vadr >> 22, (vadr >> 6 & cam_l_va_mask) | prsvd | slst, + padr >> 16, (padr & DSPMMU_RAM_L_RAM_LSB_MASK) | ap); + + /* update lock base */ + if (victim == lbase) + lbase++; + set_tlb_lock(lbase, lbase); + + clk_disable(dsp_ck_handle); + return 0; +} + +static int dsp_mmu_clear_tlb(unsigned long vadr) +{ + int lbase; + int i; + int max_valid = 0; + + clk_enable(dsp_ck_handle); + + get_tlb_lock(&lbase, NULL); + for (i = 0; i < lbase; i++) { + unsigned short cam_h, cam_l; + unsigned short cam_l_va_mask, cam_vld, slst; + unsigned long cam_va; + + /* read a TLB entry */ + __read_tlb(lbase, i, &cam_h, &cam_l, NULL, NULL); + + cam_vld = cam_l & DSPMMU_CAM_L_V; + if (!cam_vld) + continue; + + slst = cam_l & DSPMMU_CAM_L_SLST_MASK; + cam_l_va_mask = get_cam_l_va_mask(slst); + cam_va = (unsigned long)(cam_h & DSPMMU_CAM_H_VA_TAG_H_MASK) << 22 | + (unsigned long)(cam_l & cam_l_va_mask) << 6; + + if (cam_va == vadr) + /* flush the entry */ + dsp_mmu_flush(); + else + max_valid = i; + } + + /* set new lock base */ + set_tlb_lock(max_valid+1, max_valid+1); + + clk_disable(dsp_ck_handle); + return 0; +} + +static void dsp_mmu_gflush(void) +{ + clk_enable(dsp_ck_handle); + + __dsp_mmu_gflush(); + set_tlb_lock(1, 1); + + clk_disable(dsp_ck_handle); +} + +/* + * dsp_exmap() + * + * OMAP_DSP_MEM_IOCTL_EXMAP ioctl calls this function with padr=0. + * In this case, the buffer for DSP is allocated in this routine, + * then it is mapped. + * On the other hand, for example - frame buffer sharing, calls + * this function with padr set. It means some known address space + * pointed with padr is going to be shared with DSP. + */ +static int dsp_exmap(unsigned long dspadr, unsigned long padr, + unsigned long size, enum exmap_type type) +{ + unsigned short slst; + void *buf; + unsigned int order = 0; + unsigned long unit; + unsigned int cntnu = 0; + unsigned long _dspadr = dspadr; + unsigned long _padr = padr; + void *_vadr = dspbyte_to_virt(dspadr); + unsigned long _size = size; + struct exmap_tbl *exmap_ent; + int status; + int i; + +#define MINIMUM_PAGESZ SZ_4KB + /* + * alignment check + */ + if (!is_aligned(size, MINIMUM_PAGESZ)) { + printk(KERN_ERR + "omapdsp: size(0x%lx) is not multiple of 4KB.\n", size); + return -EINVAL; + } + if (!is_aligned(dspadr, MINIMUM_PAGESZ)) { + printk(KERN_ERR + "omapdsp: DSP address(0x%lx) is not aligned.\n", dspadr); + return -EINVAL; + } + if (!is_aligned(padr, MINIMUM_PAGESZ)) { + printk(KERN_ERR + "omapdsp: physical address(0x%lx) is not aligned.\n", + padr); + return -EINVAL; + } + + /* address validity check */ + if ((dspadr < dspmem_size) || + (dspadr >= DSPSPACE_SIZE) || + ((dspadr + size > DSP_INIT_PAGE) && + (dspadr < DSP_INIT_PAGE + PAGE_SIZE))) { + printk(KERN_ERR + "omapdsp: illegal address/size for dsp_exmap().\n"); + return -EINVAL; + } + + down_write(&exmap_sem); + + /* overlap check */ + for (i = 0; i < DSPMMU_TLB_LINES; i++) { + unsigned long mapsize; + struct exmap_tbl *tmp_ent = &exmap_tbl[i]; + + if (!tmp_ent->valid) + continue; + mapsize = 1 << (tmp_ent->order + PAGE_SHIFT); + if ((_vadr + size > tmp_ent->vadr) && + (_vadr < tmp_ent->vadr + mapsize)) { + printk(KERN_ERR "omapdsp: exmap page overlap!\n"); + up_write(&exmap_sem); + return -EINVAL; + } + } + +start: + buf = NULL; + /* Are there any free TLB lines? */ + for (i = 0; i < DSPMMU_TLB_LINES; i++) { + if (!exmap_tbl[i].valid) + goto found_free; + } + printk(KERN_ERR "omapdsp: DSP TLB is full.\n"); + status = -EBUSY; + goto fail; + +found_free: + exmap_ent = &exmap_tbl[i]; + + if ((_size >= SZ_1MB) && + (is_aligned(_padr, SZ_1MB) || (padr == 0)) && + is_aligned(_dspadr, SZ_1MB)) { + unit = SZ_1MB; + slst = DSPMMU_CAM_L_SLST_1MB; + } else if ((_size >= SZ_64KB) && + (is_aligned(_padr, SZ_64KB) || (padr == 0)) && + is_aligned(_dspadr, SZ_64KB)) { + unit = SZ_64KB; + slst = DSPMMU_CAM_L_SLST_64KB; + } else { + unit = SZ_4KB; + slst = DSPMMU_CAM_L_SLST_4KB; + } + + order = get_order(unit); + + /* buffer allocation */ + if (type == EXMAP_TYPE_MEM) { + struct page *page, *ps, *pe; + + if ((order == ORDER_1MB) && likely(kmem_pool_1M)) + buf = mempool_alloc_from_pool(kmem_pool_1M, GFP_KERNEL); + else if ((order == ORDER_64KB) && likely(kmem_pool_64K)) + buf = mempool_alloc_from_pool(kmem_pool_64K,GFP_KERNEL); + else { + buf = (void *)__get_dma_pages(GFP_KERNEL, order); + if (buf == NULL) { + status = -ENOMEM; + goto fail; + } + } + + /* mark the pages as reserved; this is needed for mmap */ + ps = virt_to_page(buf); + pe = virt_to_page(buf + unit); + + for (page = ps; page < pe; page++) + SetPageReserved(page); + + _padr = __pa(buf); + } + + /* + * mapping for ARM MMU: + * we should not access to the allocated memory through 'buf' + * since this area should not be cashed. + */ + status = exmap_set_armmmu((unsigned long)_vadr, _padr, unit); + if (status < 0) + goto fail; + + /* loading DSP TLB entry */ + status = dsp_mmu_load_tlb(_dspadr, _padr, slst, 0, DSPMMU_RAM_L_AP_FA); + if (status < 0) { + exmap_clear_armmmu((unsigned long)_vadr, unit); + goto fail; + } + + exmap_ent->buf = buf; + exmap_ent->vadr = _vadr; + exmap_ent->order = order; + exmap_ent->valid = 1; + exmap_ent->cntnu = cntnu; + exmap_ent->type = type; + exmap_ent->usecount = 0; + + if ((_size -= unit) == 0) { /* normal completion */ + up_write(&exmap_sem); + return size; + } + + _dspadr += unit; + _vadr += unit; + _padr = padr ? _padr + unit : 0; + cntnu = 1; + goto start; + +fail: + up_write(&exmap_sem); + if (buf) + dsp_mem_free_pages((unsigned long)buf, order); + dsp_exunmap(dspadr); + return status; +} + +static unsigned long unmap_free_arm(struct exmap_tbl *ent) +{ + unsigned long size; + + /* clearing ARM MMU */ + size = 1 << (ent->order + PAGE_SHIFT); + exmap_clear_armmmu((unsigned long)ent->vadr, size); + + /* freeing allocated memory */ + if (ent->type == EXMAP_TYPE_MEM) { + dsp_mem_free_pages((unsigned long)ent->buf, ent->order); + printk(KERN_DEBUG + "omapdsp: freeing 0x%lx bytes @ adr 0x%8p\n", + size, ent->buf); + } +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL + else if (ent->type == EXMAP_TYPE_FB) { + int status; + if (omapfb_nb) { + status = omapfb_unregister_client(omapfb_nb); + if (!status) + printk("omapfb_unregister_client(): " + "success\n"); + else + printk("omapfb_runegister_client(): " + "failure(%d)\n", status); + kfree(omapfb_nb); + omapfb_nb = NULL; + omapfb_ready = 0; + } + } +#endif + + return size; +} + +static int dsp_exunmap(unsigned long dspadr) +{ + void *vadr; + unsigned long size; + int total = 0; + struct exmap_tbl *ent; + int idx; + + vadr = dspbyte_to_virt(dspadr); + down_write(&exmap_sem); + for (idx = 0; idx < DSPMMU_TLB_LINES; idx++) { + ent = &exmap_tbl[idx]; + if (!ent->valid) + continue; + if (ent->vadr == vadr) + goto found_map; + } + up_write(&exmap_sem); + printk(KERN_WARNING + "omapdsp: address %06lx not found in exmap_tbl.\n", dspadr); + return -EINVAL; + +found_map: + if (ent->usecount > 0) { + printk(KERN_ERR + "omapdsp: exmap reference count is not 0.\n" + " idx=%d, vadr=%p, order=%d, usecount=%d\n", + idx, ent->vadr, ent->order, ent->usecount); + up_write(&exmap_sem); + return -EINVAL; + } + /* clearing DSP TLB entry */ + dsp_mmu_clear_tlb(dspadr); + + /* clear ARM MMU and free buffer */ + size = unmap_free_arm(ent); + ent->valid = 0; + total += size; + + /* we don't free PTEs */ + + /* flush TLB */ + flush_tlb_kernel_range((unsigned long)vadr, (unsigned long)vadr + size); + + /* check if next mapping is in same group */ + if (++idx == DSPMMU_TLB_LINES) + goto up_out; /* normal completion */ + ent = &exmap_tbl[idx]; + if (!ent->valid || !ent->cntnu) + goto up_out; /* normal completion */ + + dspadr += size; + vadr += size; + if (ent->vadr == vadr) + goto found_map; /* continue */ + + printk(KERN_ERR + "omapdsp: illegal exmap_tbl grouping!\n" + "expected vadr = %p, exmap_tbl[%d].vadr = %p\n", + vadr, idx, ent->vadr); + up_write(&exmap_sem); + return -EINVAL; + +up_out: + up_write(&exmap_sem); + return total; +} + +static void exmap_flush(void) +{ + struct exmap_tbl *ent; + int i; + + down_write(&exmap_sem); + + /* clearing DSP TLB entry */ + dsp_mmu_gflush(); + + /* exmap_tbl[0] should be preserved */ + for (i = 1; i < DSPMMU_TLB_LINES; i++) { + ent = &exmap_tbl[i]; + if (ent->valid) { + unmap_free_arm(ent); + ent->valid = 0; + } + } + + /* flush TLB */ + flush_tlb_kernel_range(dspmem_base + dspmem_size, + dspmem_base + DSPSPACE_SIZE); + up_write(&exmap_sem); +} + +#ifdef CONFIG_OMAP_DSP_FBEXPORT +#ifndef CONFIG_FB +#error You configured OMAP_DSP_FBEXPORT, but FB was not configured! +#endif /* CONFIG_FB */ + +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL +static int omapfb_notifier_cb(struct omapfb_notifier_block *omapfb_nb, + unsigned long event, struct omapfb_device *fbdev) +{ + /* XXX */ + printk("omapfb_notifier_cb(): event = %s\n", + (event == OMAPFB_EVENT_READY) ? "READY" : + (event == OMAPFB_EVENT_DISABLED) ? "DISABLED" : "Unknown"); + if (event == OMAPFB_EVENT_READY) + omapfb_ready = 1; + else if (event == OMAPFB_EVENT_DISABLED) + omapfb_ready = 0; + return 0; +} +#endif + +static int dsp_fbexport(unsigned long *dspadr) +{ + unsigned long dspadr_actual; + unsigned long padr_sys, padr, fbsz_sys, fbsz; + int cnt; +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL + int status; +#endif + + printk(KERN_DEBUG "omapdsp: frame buffer export\n"); + +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL + if (omapfb_nb) { + printk(KERN_WARNING + "omapdsp: frame buffer has been exported already!\n"); + return -EBUSY; + } +#endif + + if (num_registered_fb == 0) { + printk(KERN_INFO "omapdsp: frame buffer not registered.\n"); + return -EINVAL; + } + if (num_registered_fb != 1) { + printk(KERN_INFO + "omapdsp: %d frame buffers found. we use first one.\n", + num_registered_fb); + } + padr_sys = registered_fb[0]->fix.smem_start; + fbsz_sys = registered_fb[0]->fix.smem_len; + if (fbsz_sys == 0) { + printk(KERN_ERR + "omapdsp: framebuffer doesn't seem to be configured " + "correctly! (size=0)\n"); + return -EINVAL; + } + + /* + * align padr and fbsz to 4kB boundary + * (should be noted to the user afterwards!) + */ + padr = padr_sys & ~(SZ_4KB-1); + fbsz = (fbsz_sys + padr_sys - padr + SZ_4KB-1) & ~(SZ_4KB-1); + + /* line up dspadr offset with padr */ + dspadr_actual = + (fbsz > SZ_1MB) ? lineup_offset(*dspadr, padr, SZ_1MB-1) : + (fbsz > SZ_64KB) ? lineup_offset(*dspadr, padr, SZ_64KB-1) : + /* (fbsz > SZ_4KB) ? */ *dspadr; + if (dspadr_actual != *dspadr) + printk(KERN_DEBUG + "omapdsp: actual dspadr for FBEXPORT = %08lx\n", + dspadr_actual); + *dspadr = dspadr_actual; + + cnt = dsp_exmap(dspadr_actual, padr, fbsz, EXMAP_TYPE_FB); + if (cnt < 0) { + printk(KERN_ERR "omapdsp: exmap failure.\n"); + return cnt; + } + + if ((padr != padr_sys) || (fbsz != fbsz_sys)) { + printk(KERN_WARNING +" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n" +" !! screen base address or size is not aligned in 4kB: !!\n" +" !! actual screen adr = %08lx, size = %08lx !!\n" +" !! exporting adr = %08lx, size = %08lx !!\n" +" !! Make sure that the framebuffer is allocated with 4kB-order! !!\n" +" !! Otherwise DSP can corrupt the kernel memory. !!\n" +" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n", + padr_sys, fbsz_sys, padr, fbsz); + } + + /* increase the DMA priority */ + set_emiff_dma_prio(15); + +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL + omapfb_nb = kmalloc(sizeof(struct omapfb_notifier_block), GFP_KERNEL); + if (omapfb_nb == NULL) { + printk(KERN_ERR + "omapdsp: failed to allocate memory for omapfb_nb!\n"); + dsp_exunmap(dspadr_actual); + return -ENOMEM; + } + status = omapfb_register_client(omapfb_nb, omapfb_notifier_cb, NULL); + if (!status) + printk("omapfb_register_client(): success\n"); + else + printk("omapfb_register_client(): failure(%d)\n", status); +#endif + + return cnt; +} + +#else /* CONFIG_OMAP_DSP_FBEXPORT */ + +static int dsp_fbexport(unsigned long *dspadr) +{ + printk(KERN_ERR "omapdsp: FBEXPORT function is not enabled.\n"); + return -EINVAL; +} + +#endif /* CONFIG_OMAP_DSP_FBEXPORT */ + +static int dsp_mmu_itack(void) +{ + unsigned long dspadr; + + printk(KERN_INFO "omapdsp: sending DSP MMU interrupt ack.\n"); + if (!dsp_err_mmu_isset()) { + printk(KERN_ERR "omapdsp: DSP MMU error has not been set.\n"); + return -EINVAL; + } + dspadr = dsp_fault_adr & ~(SZ_4K-1); + dsp_exmap(dspadr, 0, SZ_4K, EXMAP_TYPE_MEM); /* FIXME: reserve TLB entry for this */ + printk(KERN_INFO "omapdsp: falling into recovery runlevel...\n"); + dsp_runlevel(OMAP_DSP_MBCMD_RUNLEVEL_RECOVERY); + __dsp_mmu_itack(); + udelay(100); + dsp_exunmap(dspadr); + dsp_err_mmu_clear(); + return 0; +} + +static void dsp_mmu_init(void) +{ + unsigned long phys; + void *virt; + + clk_enable(dsp_ck_handle); + down_write(&exmap_sem); + + dsp_mmu_disable(); /* clear all */ + udelay(100); + dsp_mmu_enable(); + + /* mapping for ARM MMU */ + phys = __pa(dspvect_page); + virt = dspbyte_to_virt(DSP_INIT_PAGE); /* 0xe0fff000 */ + exmap_set_armmmu((unsigned long)virt, phys, PAGE_SIZE); + exmap_tbl[0].buf = dspvect_page; + exmap_tbl[0].vadr = virt; + exmap_tbl[0].usecount = 0; + exmap_tbl[0].order = 0; + exmap_tbl[0].valid = 1; + exmap_tbl[0].cntnu = 0; + + /* DSP TLB initialization */ + set_tlb_lock(0, 0); + /* preserved, full access */ + dsp_mmu_load_tlb(DSP_INIT_PAGE, phys, DSPMMU_CAM_L_SLST_4KB, + DSPMMU_CAM_L_P, DSPMMU_RAM_L_AP_FA); + up_write(&exmap_sem); + clk_disable(dsp_ck_handle); +} + +static void dsp_mmu_shutdown(void) +{ + exmap_flush(); + dsp_mmu_disable(); /* clear all */ +} + +/* + * intmem_enable() / disable(): + * if the address is in DSP internal memories, + * we send PM mailbox commands so that DSP DMA domain won't go in idle + * when ARM is accessing to those memories. + */ +static int intmem_enable(void) +{ + int ret = 0; + + if (dsp_is_ready()) + ret = dsp_mbsend(MBCMD(PM), OMAP_DSP_MBCMD_PM_ENABLE, + DSPREG_ICR_DMA_IDLE_DOMAIN); + + return ret; +} + +static void intmem_disable(void) { + if (dsp_is_ready()) + dsp_mbsend(MBCMD(PM), OMAP_DSP_MBCMD_PM_DISABLE, + DSPREG_ICR_DMA_IDLE_DOMAIN); +} + +/* + * dsp_mem_enable() / disable() + */ +int intmem_usecount; + +int dsp_mem_enable(void *adr) +{ + int ret = 0; + + if (is_dsp_internal_mem(adr)) { + if (intmem_usecount++ == 0) + ret = omap_dsp_request_mem(); + } else + down_read(&exmap_sem); + + return ret; +} + +void dsp_mem_disable(void *adr) +{ + if (is_dsp_internal_mem(adr)) { + if (--intmem_usecount == 0) + omap_dsp_release_mem(); + } else + up_read(&exmap_sem); +} + +/* for safety */ +void dsp_mem_usecount_clear(void) +{ + if (intmem_usecount != 0) { + printk(KERN_WARNING + "omapdsp: unbalanced memory request/release detected.\n" + " intmem_usecount is not zero at where " + "it should be! ... fixed to be zero.\n"); + intmem_usecount = 0; + omap_dsp_release_mem(); + } +} + +/* + * dsp_mem file operations + */ +static loff_t dsp_mem_lseek(struct file *file, loff_t offset, int orig) +{ + loff_t ret; + + mutex_lock(&file->f_dentry->d_inode->i_mutex); + switch (orig) { + case 0: + file->f_pos = offset; + ret = file->f_pos; + break; + case 1: + file->f_pos += offset; + ret = file->f_pos; + break; + default: + ret = -EINVAL; + } + mutex_unlock(&file->f_dentry->d_inode->i_mutex); + return ret; +} + +static ssize_t intmem_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + unsigned long p = *ppos; + void *vadr = dspbyte_to_virt(p); + ssize_t size = dspmem_size; + ssize_t read; + + if (p >= size) + return 0; + clk_enable(api_ck_handle); + read = count; + if (count > size - p) + read = size - p; + if (copy_to_user(buf, vadr, read)) { + read = -EFAULT; + goto out; + } + *ppos += read; +out: + clk_disable(api_ck_handle); + return read; +} + +static ssize_t exmem_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + unsigned long p = *ppos; + void *vadr = dspbyte_to_virt(p); + + if (!exmap_valid(vadr, count)) { + printk(KERN_ERR + "omapdsp: DSP address %08lx / size %08x " + "is not valid!\n", p, count); + return -EFAULT; + } + if (count > DSPSPACE_SIZE - p) + count = DSPSPACE_SIZE - p; + if (copy_to_user(buf, vadr, count)) + return -EFAULT; + *ppos += count; + + return count; +} + +static ssize_t dsp_mem_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + int ret; + void *vadr = dspbyte_to_virt(*(unsigned long *)ppos); + + if (dsp_mem_enable(vadr) < 0) + return -EBUSY; + if (is_dspbyte_internal_mem(*ppos)) + ret = intmem_read(file, buf, count, ppos); + else + ret = exmem_read(file, buf, count, ppos); + dsp_mem_disable(vadr); + + return ret; +} + +static ssize_t intmem_write(struct file *file, const char *buf, size_t count, + loff_t *ppos) +{ + unsigned long p = *ppos; + void *vadr = dspbyte_to_virt(p); + ssize_t size = dspmem_size; + ssize_t written; + + if (p >= size) + return 0; + clk_enable(api_ck_handle); + written = count; + if (count > size - p) + written = size - p; + if (copy_from_user(vadr, buf, written)) { + written = -EFAULT; + goto out; + } + *ppos += written; +out: + clk_disable(api_ck_handle); + return written; +} + +static ssize_t exmem_write(struct file *file, const char *buf, size_t count, + loff_t *ppos) +{ + unsigned long p = *ppos; + void *vadr = dspbyte_to_virt(p); + + if (!exmap_valid(vadr, count)) { + printk(KERN_ERR + "omapdsp: DSP address %08lx / size %08x " + "is not valid!\n", p, count); + return -EFAULT; + } + if (count > DSPSPACE_SIZE - p) + count = DSPSPACE_SIZE - p; + if (copy_from_user(vadr, buf, count)) + return -EFAULT; + *ppos += count; + + return count; +} + +static ssize_t dsp_mem_write(struct file *file, const char *buf, size_t count, + loff_t *ppos) +{ + int ret; + void *vadr = dspbyte_to_virt(*(unsigned long *)ppos); + + if (dsp_mem_enable(vadr) < 0) + return -EBUSY; + if (is_dspbyte_internal_mem(*ppos)) + ret = intmem_write(file, buf, count, ppos); + else + ret = exmem_write(file, buf, count, ppos); + dsp_mem_disable(vadr); + + return ret; +} + +static int dsp_mem_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case OMAP_DSP_MEM_IOCTL_MMUINIT: + dsp_mmu_init(); + return 0; + + case OMAP_DSP_MEM_IOCTL_EXMAP: + { + struct omap_dsp_mapinfo mapinfo; + if (copy_from_user(&mapinfo, (void *)arg, + sizeof(mapinfo))) + return -EFAULT; + return dsp_exmap(mapinfo.dspadr, 0, mapinfo.size, + EXMAP_TYPE_MEM); + } + + case OMAP_DSP_MEM_IOCTL_EXUNMAP: + return dsp_exunmap((unsigned long)arg); + + case OMAP_DSP_MEM_IOCTL_EXMAP_FLUSH: + exmap_flush(); + return 0; + + case OMAP_DSP_MEM_IOCTL_FBEXPORT: + { + unsigned long dspadr; + int ret; + if (copy_from_user(&dspadr, (void *)arg, sizeof(long))) + return -EFAULT; + ret = dsp_fbexport(&dspadr); + if (copy_to_user((void *)arg, &dspadr, sizeof(long))) + return -EFAULT; + return ret; + } + + case OMAP_DSP_MEM_IOCTL_MMUITACK: + return dsp_mmu_itack(); + + case OMAP_DSP_MEM_IOCTL_KMEM_RESERVE: + { + unsigned long size; + if (copy_from_user(&size, (void *)arg, sizeof(long))) + return -EFAULT; + return dsp_kmem_reserve(size); + } + + case OMAP_DSP_MEM_IOCTL_KMEM_RELEASE: + dsp_kmem_release(); + return 0; + + default: + return -ENOIOCTLCMD; + } +} + +static int dsp_mem_mmap(struct file *file, struct vm_area_struct *vma) +{ + /* + * FIXME + */ + return -ENOSYS; +} + +static int dsp_mem_open(struct inode *inode, struct file *file) +{ + if (!capable(CAP_SYS_RAWIO)) + return -EPERM; + + return 0; +} + +static int dsp_mem_release(struct inode *inode, struct file *file) +{ + return 0; +} + +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL +/* + * fb update functions: + * fbupd_response() is executed by the workqueue. + * fbupd_cb() is called when fb update is done, in interrupt context. + * mbx1_fbupd() is called when KFUNC:FBCTL:UPD is received from DSP. + */ +static void fbupd_response(void *arg) +{ + int status; + + status = dsp_mbsend(MBCMD(KFUNC), OMAP_DSP_MBCMD_KFUNC_FBCTL, + OMAP_DSP_MBCMD_FBCTL_UPD); + if (status < 0) { + /* FIXME: DSP is busy !! */ + printk(KERN_ERR + "omapdsp: DSP is busy when trying to send FBCTL:UPD " + "response!\n"); + } +} + +static DECLARE_WORK(fbupd_response_work, (void (*)(void *))fbupd_response, + NULL); + +static void fbupd_cb(void *arg) +{ + schedule_work(&fbupd_response_work); +} + +void mbx1_fbctl_upd(void) +{ + struct omapfb_update_window win; + volatile unsigned short *buf = ipbuf_sys_da->d; + + /* FIXME: try count sometimes exceeds 1000. */ + if (sync_with_dsp(&ipbuf_sys_da->s, OMAP_DSP_TID_ANON, 5000) < 0) { + printk(KERN_ERR "mbx: FBCTL:UPD - IPBUF sync failed!\n"); + return; + } + win.x = buf[0]; + win.y = buf[1]; + win.width = buf[2]; + win.height = buf[3]; + win.format = buf[4]; + release_ipbuf_pvt(ipbuf_sys_da); + + if (!omapfb_ready) { + printk(KERN_WARNING + "omapdsp: fbupd() called while HWA742 is not ready!\n"); + return; + } + //printk("calling omapfb_update_window_async()\n"); + omapfb_update_window_async(&win, fbupd_cb, NULL); +} + +#else /* CONFIG_FB_OMAP_LCDC_EXTERNAL */ + +void mbx1_fbctl_upd(void) +{ +} +#endif /* CONFIG_FB_OMAP_LCDC_EXTERNAL */ + +/* + * sysfs files + */ +static ssize_t mmu_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int len; + int lbase, victim; + int i; + + clk_enable(dsp_ck_handle); + down_read(&exmap_sem); + + get_tlb_lock(&lbase, &victim); + + len = sprintf(buf, "p: preserved, v: valid\n" + "ety cam_va ram_pa sz ap\n"); + /* 00: p v 0x300000 0x10171800 64KB FA */ + for (i = 0; i < 32; i++) { + unsigned short cam_h, cam_l, ram_h, ram_l; + unsigned short cam_l_va_mask, prsvd, cam_vld, slst; + unsigned long cam_va; + unsigned short ram_l_ap; + unsigned long ram_pa; + char *pgsz_str, *ap_str; + + /* read a TLB entry */ + __read_tlb(lbase, i, &cam_h, &cam_l, &ram_h, &ram_l); + + slst = cam_l & DSPMMU_CAM_L_SLST_MASK; + cam_l_va_mask = get_cam_l_va_mask(slst); + pgsz_str = (slst == DSPMMU_CAM_L_SLST_1MB) ? " 1MB": + (slst == DSPMMU_CAM_L_SLST_64KB)? "64KB": + (slst == DSPMMU_CAM_L_SLST_4KB) ? " 4KB": + " 1KB"; + prsvd = cam_l & DSPMMU_CAM_L_P; + cam_vld = cam_l & DSPMMU_CAM_L_V; + ram_l_ap = ram_l & DSPMMU_RAM_L_AP_MASK; + ap_str = (ram_l_ap == DSPMMU_RAM_L_AP_RO) ? "RO": + (ram_l_ap == DSPMMU_RAM_L_AP_FA) ? "FA": + "NA"; + cam_va = (unsigned long)(cam_h & DSPMMU_CAM_H_VA_TAG_H_MASK) << 22 | + (unsigned long)(cam_l & cam_l_va_mask) << 6; + ram_pa = (unsigned long)ram_h << 16 | + (ram_l & DSPMMU_RAM_L_RAM_LSB_MASK); + + if (i == lbase) + len += sprintf(buf + len, "lock base = %d\n", lbase); + if (i == victim) + len += sprintf(buf + len, "victim = %d\n", victim); + /* 00: p v 0x300000 0x10171800 64KB FA */ + len += sprintf(buf + len, + "%02d: %c %c 0x%06lx 0x%08lx %s %s\n", + i, + prsvd ? 'p' : ' ', + cam_vld ? 'v' : ' ', + cam_va, ram_pa, pgsz_str, ap_str); + } + + /* restore victim entry */ + set_tlb_lock(lbase, victim); + + up_read(&exmap_sem); + clk_disable(dsp_ck_handle); + return len; +} + +static struct device_attribute dev_attr_mmu = __ATTR_RO(mmu); + +static ssize_t exmap_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int len; + int i; + + down_read(&exmap_sem); + len = sprintf(buf, "v: valid, c: cntnu\n" + "ety vadr buf od uc\n"); + /* 00: v c 0xe0300000 0xc0171800 0 */ + for (i = 0; i < DSPMMU_TLB_LINES; i++) { + struct exmap_tbl *ent = &exmap_tbl[i]; + /* 00: v c 0xe0300000 0xc0171800 0 */ + len += sprintf(buf + len, "%02d: %c %c 0x%8p 0x%8p %2d %2d\n", + i, + ent->valid ? 'v' : ' ', + ent->cntnu ? 'c' : ' ', + ent->vadr, ent->buf, ent->order, ent->usecount); + } + + up_read(&exmap_sem); + return len; +} + +static struct device_attribute dev_attr_exmap = __ATTR_RO(exmap); + +static ssize_t kmem_pool_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int nr_1M, nr_64K, total; + + nr_1M = kmem_pool_1M->min_nr; + nr_64K = kmem_pool_64K->min_nr; + total = nr_1M * SZ_1MB + nr_64K * SZ_64KB; + + return sprintf(buf, "0x%x %d %d\n", total, nr_1M, nr_64K); +} + +static struct device_attribute dev_attr_kmem_pool = __ATTR_RO(kmem_pool); + +/* + * DSP MMU interrupt handler + */ + +/* + * MMU fault mask: + * We ignore prefetch err. + */ +#define MMUFAULT_MASK \ + (DSPMMU_FAULT_ST_PERM |\ + DSPMMU_FAULT_ST_TLB_MISS |\ + DSPMMU_FAULT_ST_TRANS) +irqreturn_t dsp_mmu_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned short status; + unsigned short adh, adl; + unsigned short dp; + + status = omap_readw(DSPMMU_FAULT_ST); + adh = omap_readw(DSPMMU_FAULT_AD_H); + adl = omap_readw(DSPMMU_FAULT_AD_L); + dp = adh & DSPMMU_FAULT_AD_H_DP; + dsp_fault_adr = MKLONG(adh & DSPMMU_FAULT_AD_H_ADR_MASK, adl); + /* if the fault is masked, nothing to do */ + if ((status & MMUFAULT_MASK) == 0) { + printk(KERN_DEBUG "DSP MMU interrupt, but ignoring.\n"); + /* + * note: in OMAP1710, + * when CACHE + DMA domain gets out of idle in DSP, + * MMU interrupt occurs but DSPMMU_FAULT_ST is not set. + * in this case, we just ignore the interrupt. + */ + if (status) { + printk(KERN_DEBUG "%s%s%s%s\n", + (status & DSPMMU_FAULT_ST_PREF)? + " (prefetch err)" : "", + (status & DSPMMU_FAULT_ST_PERM)? + " (permission fault)" : "", + (status & DSPMMU_FAULT_ST_TLB_MISS)? + " (TLB miss)" : "", + (status & DSPMMU_FAULT_ST_TRANS) ? + " (translation fault)": ""); + printk(KERN_DEBUG + "fault address = %s: 0x%06lx\n", + dp ? "DATA" : "PROGRAM", + dsp_fault_adr); + } + return IRQ_HANDLED; + } + + printk(KERN_INFO "DSP MMU interrupt!\n"); + printk(KERN_INFO "%s%s%s%s\n", + (status & DSPMMU_FAULT_ST_PREF)? + (MMUFAULT_MASK & DSPMMU_FAULT_ST_PREF)? + " prefetch err": + " (prefetch err)": + "", + (status & DSPMMU_FAULT_ST_PERM)? + (MMUFAULT_MASK & DSPMMU_FAULT_ST_PERM)? + " permission fault": + " (permission fault)": + "", + (status & DSPMMU_FAULT_ST_TLB_MISS)? + (MMUFAULT_MASK & DSPMMU_FAULT_ST_TLB_MISS)? + " TLB miss": + " (TLB miss)": + "", + (status & DSPMMU_FAULT_ST_TRANS)? + (MMUFAULT_MASK & DSPMMU_FAULT_ST_TRANS)? + " translation fault": + " (translation fault)": + ""); + printk(KERN_INFO "fault address = %s: 0x%06lx\n", + dp ? "DATA" : "PROGRAM", + dsp_fault_adr); + + if (dsp_is_ready()) { + /* + * If we call dsp_exmap() here, + * "kernel BUG at slab.c" occurs. + */ + /* FIXME */ + dsp_err_mmu_set(dsp_fault_adr); + } else { + disable_irq(INT_DSP_MMU); + __dsp_mmu_itack(); + printk(KERN_INFO "Resetting DSP...\n"); + dsp_cpustat_request(CPUSTAT_RESET); + enable_irq(INT_DSP_MMU); + /* + * if we enable followings, semaphore lock should be avoided. + * + printk(KERN_INFO "Flushing DSP MMU...\n"); + exmap_flush(); + dsp_mmu_init(); + */ + } + + return IRQ_HANDLED; +} + +/* + * + */ +struct file_operations dsp_mem_fops = { + .owner = THIS_MODULE, + .llseek = dsp_mem_lseek, + .read = dsp_mem_read, + .write = dsp_mem_write, + .ioctl = dsp_mem_ioctl, + .mmap = dsp_mem_mmap, + .open = dsp_mem_open, + .release = dsp_mem_release, +}; + +void dsp_mem_start(void) +{ + dsp_register_mem_cb(intmem_enable, intmem_disable); +} + +void dsp_mem_stop(void) +{ + memset(&mem_sync, 0, sizeof(struct mem_sync_struct)); + dsp_unregister_mem_cb(); +} + +int __init dsp_mem_init(void) +{ + int i; + + for (i = 0; i < DSPMMU_TLB_LINES; i++) { + exmap_tbl[i].valid = 0; + } + + dspvect_page = (void *)__get_dma_pages(GFP_KERNEL, 0); + if (dspvect_page == NULL) { + printk(KERN_ERR + "omapdsp: failed to allocate memory " + "for dsp vector table\n"); + return -ENOMEM; + } + dsp_mmu_init(); + dsp_set_idle_boot_base(IDLEPG_BASE, IDLEPG_SIZE); + + device_create_file(&dsp_device.dev, &dev_attr_mmu); + device_create_file(&dsp_device.dev, &dev_attr_exmap); + device_create_file(&dsp_device.dev, &dev_attr_kmem_pool); + + return 0; +} + +void dsp_mem_exit(void) +{ + dsp_mmu_shutdown(); + dsp_kmem_release(); + + if (dspvect_page != NULL) { + unsigned long virt; + + down_read(&exmap_sem); + + virt = (unsigned long)dspbyte_to_virt(DSP_INIT_PAGE); + flush_tlb_kernel_range(virt, virt + PAGE_SIZE); + free_page((unsigned long)dspvect_page); + dspvect_page = NULL; + + up_read(&exmap_sem); + } + + device_remove_file(&dsp_device.dev, &dev_attr_mmu); + device_remove_file(&dsp_device.dev, &dev_attr_exmap); + device_remove_file(&dsp_device.dev, &dev_attr_kmem_pool); +} diff --git a/arch/arm/plat-omap/dsp/error.c b/arch/arm/plat-omap/dsp/error.c new file mode 100644 index 00000000000..dd7d7c4594b --- /dev/null +++ b/arch/arm/plat-omap/dsp/error.c @@ -0,0 +1,206 @@ +/* + * linux/arch/arm/mach-omap/dsp/error.c + * + * OMAP DSP error detection I/F device driver + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2005/03/11: DSP Gateway version 3.3 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dsp.h" + +static DECLARE_WAIT_QUEUE_HEAD(err_wait_q); +static unsigned long errcode; +static int errcnt; +static unsigned short wdtval; /* FIXME: read through ioctl */ +static unsigned long mmu_fadr; /* FIXME: read through ioctl */ + +/* + * DSP error detection device file operations + */ +static ssize_t dsp_err_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + unsigned long flags; + int status; + + if (count < 4) + return 0; + + if (errcnt == 0) { + long current_state; + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&err_wait_q, &wait); + current_state = current->state; + set_current_state(TASK_INTERRUPTIBLE); + if (errcnt == 0) /* last check */ + schedule(); + set_current_state(current_state); + remove_wait_queue(&err_wait_q, &wait); + if (signal_pending(current)) + return -EINTR; + } + + local_irq_save(flags); + status = copy_to_user(buf, &errcode, 4); + if (status) { + local_irq_restore(flags); + return -EFAULT; + } + errcnt = 0; + local_irq_restore(flags); + + return 4; +} + +static unsigned int dsp_err_poll(struct file *file, poll_table *wait) +{ + unsigned int mask = 0; + + poll_wait(file, &err_wait_q, wait); + if (errcnt != 0) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +struct file_operations dsp_err_fops = { + .owner = THIS_MODULE, + .poll = dsp_err_poll, + .read = dsp_err_read, +}; + +/* + * DSP MMU + */ +void dsp_err_mmu_set(unsigned long adr) +{ + disable_irq(INT_DSP_MMU); + errcode |= OMAP_DSP_ERRDT_MMU; + errcnt++; + mmu_fadr = adr; + wake_up_interruptible(&err_wait_q); +} + +void dsp_err_mmu_clear(void) +{ + errcode &= ~OMAP_DSP_ERRDT_MMU; + enable_irq(INT_DSP_MMU); +} + +int dsp_err_mmu_isset(void) +{ + return (errcode & OMAP_DSP_ERRDT_MMU) ? 1 : 0; +} + +/* + * WDT + */ +void dsp_err_wdt_clear(void) +{ + errcode &= ~OMAP_DSP_ERRDT_WDT; +} + +int dsp_err_wdt_isset(void) +{ + return (errcode & OMAP_DSP_ERRDT_WDT) ? 1 : 0; +} + +/* + * functions called from mailbox1 interrupt routine + */ +static void mbx1_err_wdt(unsigned short data) +{ + errcode |= OMAP_DSP_ERRDT_WDT; + errcnt++; + wdtval = data; + wake_up_interruptible(&err_wait_q); +} + +#ifdef OLD_BINARY_SUPPORT +/* v3.3 obsolete */ +void mbx1_wdt(struct mbcmd *mb) +{ + mbx1_err_wdt(mb->data); +} +#endif + +extern void mbx1_err_ipbfull(void); +extern void mbx1_err_fatal(unsigned char tid); + +void mbx1_err(struct mbcmd *mb) +{ + unsigned char eid = mb->cmd_l; + char *eidnm = subcmd_name(mb); + unsigned char tid; + + if (eidnm) { + printk(KERN_WARNING + "mbx: ERR from DSP (%s): 0x%04x\n", eidnm, mb->data); + } else { + printk(KERN_WARNING + "mbx: ERR from DSP (unknown EID=%02x): %04x\n", + eid, mb->data); + } + + switch (eid) { + case OMAP_DSP_EID_IPBFULL: + mbx1_err_ipbfull(); + break; + + case OMAP_DSP_EID_FATAL: + tid = mb->data & 0x00ff; + mbx1_err_fatal(tid); + break; + + case OMAP_DSP_EID_WDT: + mbx1_err_wdt(mb->data); + break; + } +} + +/* + * + */ +void dsp_err_start(void) +{ + errcnt = 0; + if (dsp_err_wdt_isset()) + dsp_err_wdt_clear(); + if (dsp_err_mmu_isset()) + dsp_err_mmu_clear(); +} + +void dsp_err_stop(void) +{ + wake_up_interruptible(&err_wait_q); +} diff --git a/arch/arm/plat-omap/dsp/fifo.h b/arch/arm/plat-omap/dsp/fifo.h new file mode 100644 index 00000000000..46fc33ccc80 --- /dev/null +++ b/arch/arm/plat-omap/dsp/fifo.h @@ -0,0 +1,179 @@ +/* + * linux/arch/arm/mach-omap/dsp/fifo.h + * + * FIFO buffer operators + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2005/02/24: DSP Gateway version 3.3 + */ + +struct fifo_struct { + spinlock_t lock; + char *buf; + size_t sz; + size_t cnt; + unsigned int wp; +}; + +static inline int alloc_fifo(struct fifo_struct *fifo, size_t sz) +{ + if ((fifo->buf = kmalloc(sz, GFP_KERNEL)) == NULL) { + fifo->sz = 0; + return -ENOMEM; + } + fifo->sz = sz; + fifo->cnt = 0; + fifo->wp = 0; + return 0; +} + +static inline int init_fifo(struct fifo_struct *fifo, size_t sz) +{ + spin_lock_init(&fifo->lock); + return alloc_fifo(fifo, sz); +} + +static inline void free_fifo(struct fifo_struct *fifo) +{ + spin_lock(&fifo->lock); + if (fifo->buf == NULL) + return; + + kfree(fifo->buf); + fifo->buf = NULL; + fifo->sz = 0; + spin_unlock(&fifo->lock); +} + +static inline void flush_fifo(struct fifo_struct *fifo) +{ + spin_lock(&fifo->lock); + fifo->cnt = 0; + fifo->wp = 0; + spin_unlock(&fifo->lock); +} + +#define fifo_empty(fifo) ((fifo)->cnt == 0) + +static inline int realloc_fifo(struct fifo_struct *fifo, size_t sz) +{ + int ret = sz; + + spin_lock(&fifo->lock); + if (!fifo_empty(fifo)) { + ret = -EBUSY; + goto out; + } + + /* free */ + if (fifo->buf) + kfree(fifo->buf); + + /* alloc */ + if ((fifo->buf = kmalloc(sz, GFP_KERNEL)) == NULL) { + fifo->sz = 0; + ret = -ENOMEM; + goto out; + } + fifo->sz = sz; + fifo->cnt = 0; + fifo->wp = 0; + +out: + spin_unlock(&fifo->lock); + return ret; +} + +static inline void write_word_to_fifo(struct fifo_struct *fifo, + unsigned short word) +{ + spin_lock(&fifo->lock); + *(unsigned short *)&fifo->buf[fifo->wp] = word; + if ((fifo->wp += 2) == fifo->sz) + fifo->wp = 0; + if ((fifo->cnt += 2) > fifo->sz) + fifo->cnt = fifo->sz; + spin_unlock(&fifo->lock); +} + +/* + * (before) + * + * [*******----------*************] + * ^wp + * <----------------------------> sz = 30 + * <-----> <-----------> cnt = 20 + * + * (read: count=16) + * <-> <-----------> count = 16 + * <-----------> cnt1 = 13 + * ^rp + * + * (after) + * [---****-----------------------] + * ^wp + */ +static inline ssize_t copy_to_user_fm_fifo(char *dst, struct fifo_struct *fifo, + size_t count) +{ + int rp; + ssize_t ret; + + /* fifo size can be zero */ + if (fifo->sz == 0) + return 0; + + spin_lock(&fifo->lock); + if (count > fifo->cnt) + count = fifo->cnt; + + if ((rp = fifo->wp - fifo->cnt) >= 0) { + /* valid area is straight */ + if (copy_to_user(dst, &fifo->buf[rp], count)) { + ret = -EFAULT; + goto out; + } + } else { + int cnt1 = -rp; + rp += fifo->sz; + if (cnt1 >= count) { + /* requested area is straight */ + if (copy_to_user(dst, &fifo->buf[rp], count)) { + ret = -EFAULT; + goto out; + } + } else { + if (copy_to_user(dst, &fifo->buf[rp], cnt1)) { + ret = -EFAULT; + goto out; + } + if (copy_to_user(dst+cnt1, fifo->buf, count-cnt1)) { + ret = -EFAULT; + goto out; + } + } + } + fifo->cnt -= count; + ret = count; + +out: + spin_unlock(&fifo->lock); + return ret; +} diff --git a/arch/arm/plat-omap/dsp/hardware_dsp.h b/arch/arm/plat-omap/dsp/hardware_dsp.h new file mode 100644 index 00000000000..1308e530543 --- /dev/null +++ b/arch/arm/plat-omap/dsp/hardware_dsp.h @@ -0,0 +1,210 @@ +/* + * linux/arch/arm/mach-omap/dsp/hardware_dsp.h + * + * Register bit definitions for DSP driver + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2005/05/30: DSP Gateway version 3.3 + */ + +#ifndef __OMAP_DSP_HARDWARE_DSP_H +#define __OMAP_DSP_HARDWARE_DSP_H + +#ifdef CONFIG_ARCH_OMAP15XX +#define OMAP1510_DARAM_BASE 0xe0000000 +#define OMAP1510_DARAM_SIZE 0x10000 +#define OMAP1510_SARAM_BASE 0xe0010000 +#define OMAP1510_SARAM_SIZE 0x18000 +#endif +#ifdef CONFIG_ARCH_OMAP16XX +#define OMAP16XX_DARAM_BASE 0xe0000000 +#define OMAP16XX_DARAM_SIZE 0x10000 +#define OMAP16XX_SARAM_BASE 0xe0010000 +#define OMAP16XX_SARAM_SIZE 0x18000 +#endif + +/* + * MAJOR device number: !! allocated arbitrary !! + */ +#define OMAP_DSP_CTL_MAJOR 96 +#define OMAP_DSP_TASK_MAJOR 97 + +/* + * Reset Control + */ +#define ARM_RSTCT1_SW_RST 0x0008 +#define ARM_RSTCT1_DSP_RST 0x0004 +#define ARM_RSTCT1_DSP_EN 0x0002 +#define ARM_RSTCT1_ARM_RST 0x0001 + +/* + * MPUI + */ +#define MPUI_CTRL_WORDSWAP_MASK 0x00600000 +#define MPUI_CTRL_WORDSWAP_ALL 0x00000000 +#define MPUI_CTRL_WORDSWAP_NONAPI 0x00200000 +#define MPUI_CTRL_WORDSWAP_API 0x00400000 +#define MPUI_CTRL_WORDSWAP_NONE 0x00600000 +#define MPUI_CTRL_AP_MASK 0x001c0000 +#define MPUI_CTRL_AP_MDH 0x00000000 +#define MPUI_CTRL_AP_MHD 0x00040000 +#define MPUI_CTRL_AP_DMH 0x00080000 +#define MPUI_CTRL_AP_HMD 0x000c0000 +#define MPUI_CTRL_AP_DHM 0x00100000 +#define MPUI_CTRL_AP_HDM 0x00140000 +#define MPUI_CTRL_BYTESWAP_MASK 0x00030000 +#define MPUI_CTRL_BYTESWAP_NONE 0x00000000 +#define MPUI_CTRL_BYTESWAP_NONAPI 0x00010000 +#define MPUI_CTRL_BYTESWAP_ALL 0x00020000 +#define MPUI_CTRL_BYTESWAP_API 0x00030000 +#define MPUI_CTRL_TIMEOUT_MASK 0x0000ff00 +#define MPUI_CTRL_APIF_HNSTB_DIV_MASK 0x000000f0 +#define MPUI_CTRL_S_NABORT_GL 0x00000008 +#define MPUI_CTRL_S_NABORT_32BIT 0x00000004 +#define MPUI_CTRL_EN_TIMEOUT 0x00000002 +#define MPUI_CTRL_HF_MCUCLK 0x00000001 +#define MPUI_DSP_BOOT_CONFIG_DIRECT 0x00000000 +#define MPUI_DSP_BOOT_CONFIG_PSD_DIRECT 0x00000001 +#define MPUI_DSP_BOOT_CONFIG_IDLE 0x00000002 +#define MPUI_DSP_BOOT_CONFIG_DL16 0x00000003 +#define MPUI_DSP_BOOT_CONFIG_DL32 0x00000004 +#define MPUI_DSP_BOOT_CONFIG_MPUI 0x00000005 +#define MPUI_DSP_BOOT_CONFIG_INTERNAL 0x00000006 + +/* + * DSP boot mode + * direct: 0xffff00 + * pseudo direct: 0x080000 + * MPUI: branch 0x010000 + * internel: branch 0x024000 + */ +#define DSP_BOOT_ADR_DIRECT 0xffff00 +#define DSP_BOOT_ADR_PSD_DIRECT 0x080000 +#define DSP_BOOT_ADR_MPUI 0x010000 +#define DSP_BOOT_ADR_INTERNAL 0x024000 + +/* + * TC + */ +#define TC_ENDIANISM_SWAP 0x00000002 +#define TC_ENDIANISM_SWAP_WORD 0x00000002 +#define TC_ENDIANISM_SWAP_BYTE 0x00000000 +#define TC_ENDIANISM_EN 0x00000001 + +/* + * DSP MMU + */ +#define DSPMMU_BASE (0xfffed200) +#define DSPMMU_PREFETCH (DSPMMU_BASE + 0x00) +#define DSPMMU_WALKING_ST (DSPMMU_BASE + 0x04) +#define DSPMMU_CNTL (DSPMMU_BASE + 0x08) +#define DSPMMU_FAULT_AD_H (DSPMMU_BASE + 0x0c) +#define DSPMMU_FAULT_AD_L (DSPMMU_BASE + 0x10) +#define DSPMMU_FAULT_ST (DSPMMU_BASE + 0x14) +#define DSPMMU_IT_ACK (DSPMMU_BASE + 0x18) +#define DSPMMU_TTB_H (DSPMMU_BASE + 0x1c) +#define DSPMMU_TTB_L (DSPMMU_BASE + 0x20) +#define DSPMMU_LOCK (DSPMMU_BASE + 0x24) +#define DSPMMU_LD_TLB (DSPMMU_BASE + 0x28) +#define DSPMMU_CAM_H (DSPMMU_BASE + 0x2c) +#define DSPMMU_CAM_L (DSPMMU_BASE + 0x30) +#define DSPMMU_RAM_H (DSPMMU_BASE + 0x34) +#define DSPMMU_RAM_L (DSPMMU_BASE + 0x38) +#define DSPMMU_GFLUSH (DSPMMU_BASE + 0x3c) +#define DSPMMU_FLUSH_ENTRY (DSPMMU_BASE + 0x40) +#define DSPMMU_READ_CAM_H (DSPMMU_BASE + 0x44) +#define DSPMMU_READ_CAM_L (DSPMMU_BASE + 0x48) +#define DSPMMU_READ_RAM_H (DSPMMU_BASE + 0x4c) +#define DSPMMU_READ_RAM_L (DSPMMU_BASE + 0x50) + +#define DSPMMU_CNTL_BURST_16MNGT_EN 0x0020 +#define DSPMMU_CNTL_WTL_EN 0x0004 +#define DSPMMU_CNTL_MMU_EN 0x0002 +#define DSPMMU_CNTL_RESET_SW 0x0001 + +#define DSPMMU_FAULT_AD_H_DP 0x0100 +#define DSPMMU_FAULT_AD_H_ADR_MASK 0x00ff + +#define DSPMMU_FAULT_ST_PREF 0x0008 +#define DSPMMU_FAULT_ST_PERM 0x0004 +#define DSPMMU_FAULT_ST_TLB_MISS 0x0002 +#define DSPMMU_FAULT_ST_TRANS 0x0001 + +#define DSPMMU_IT_ACK_IT_ACK 0x0001 + +#define DSPMMU_LOCK_BASE_MASK 0xfc00 +#define DSPMMU_LOCK_BASE_SHIFT 10 +#define DSPMMU_LOCK_VICTIM_MASK 0x03f0 +#define DSPMMU_LOCK_VICTIM_SHIFT 4 + +#define DSPMMU_CAM_H_VA_TAG_H_MASK 0x0003 + +#define DSPMMU_CAM_L_VA_TAG_L1_MASK 0xc000 +#define DSPMMU_CAM_L_VA_TAG_L2_MASK_1MB 0x0000 +#define DSPMMU_CAM_L_VA_TAG_L2_MASK_64KB 0x3c00 +#define DSPMMU_CAM_L_VA_TAG_L2_MASK_4KB 0x3fc0 +#define DSPMMU_CAM_L_VA_TAG_L2_MASK_1KB 0x3ff0 +#define DSPMMU_CAM_L_P 0x0008 +#define DSPMMU_CAM_L_V 0x0004 +#define DSPMMU_CAM_L_SLST_MASK 0x0003 +#define DSPMMU_CAM_L_SLST_1MB 0x0000 +#define DSPMMU_CAM_L_SLST_64KB 0x0001 +#define DSPMMU_CAM_L_SLST_4KB 0x0002 +#define DSPMMU_CAM_L_SLST_1KB 0x0003 + +#define DSPMMU_RAM_L_RAM_LSB_MASK 0xfc00 +#define DSPMMU_RAM_L_AP_MASK 0x0300 +#define DSPMMU_RAM_L_AP_NA 0x0000 +#define DSPMMU_RAM_L_AP_RO 0x0200 +#define DSPMMU_RAM_L_AP_FA 0x0300 + +#define DSPMMU_GFLUSH_GFLUSH 0x0001 + +#define DSPMMU_FLUSH_ENTRY_FLUSH_ENTRY 0x0001 + +#define DSPMMU_LD_TLB_RD 0x0002 +#define DSPMMU_LD_TLB_LD 0x0001 + +/* + * Mailbox + */ +#define MAILBOX_BASE (0xfffcf000) +#define MAILBOX_ARM2DSP1 (MAILBOX_BASE + 0x00) +#define MAILBOX_ARM2DSP1b (MAILBOX_BASE + 0x04) +#define MAILBOX_DSP2ARM1 (MAILBOX_BASE + 0x08) +#define MAILBOX_DSP2ARM1b (MAILBOX_BASE + 0x0c) +#define MAILBOX_DSP2ARM2 (MAILBOX_BASE + 0x10) +#define MAILBOX_DSP2ARM2b (MAILBOX_BASE + 0x14) +#define MAILBOX_ARM2DSP1_Flag (MAILBOX_BASE + 0x18) +#define MAILBOX_DSP2ARM1_Flag (MAILBOX_BASE + 0x1c) +#define MAILBOX_DSP2ARM2_Flag (MAILBOX_BASE + 0x20) + +/* + * DSP ICR + */ +#define DSPREG_ICR_RESERVED_BITS 0xffc0 +#define DSPREG_ICR_EMIF_IDLE_DOMAIN 0x0020 +#define DSPREG_ICR_DPLL_IDLE_DOMAIN 0x0010 +#define DSPREG_ICR_PER_IDLE_DOMAIN 0x0008 +#define DSPREG_ICR_CACHE_IDLE_DOMAIN 0x0004 +#define DSPREG_ICR_DMA_IDLE_DOMAIN 0x0002 +#define DSPREG_ICR_CPU_IDLE_DOMAIN 0x0001 + +#endif /* __OMAP_DSP_HARDWARE_DSP_H */ diff --git a/arch/arm/plat-omap/dsp/ipbuf.c b/arch/arm/plat-omap/dsp/ipbuf.c new file mode 100644 index 00000000000..ebb482b05f0 --- /dev/null +++ b/arch/arm/plat-omap/dsp/ipbuf.c @@ -0,0 +1,348 @@ +/* + * linux/arch/arm/mach-omap/dsp/ipbuf.c + * + * IPBUF handler + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2005/06/06: DSP Gateway version 3.3 + */ + +#include +#include +#include +#include +#include +#include +#include +#include "dsp.h" +#include "ipbuf.h" + +struct ipbuf **ipbuf; +struct ipbcfg ipbcfg; +struct ipbuf_sys *ipbuf_sys_da, *ipbuf_sys_ad; +static struct ipblink ipb_free = IPBLINK_INIT; +static int ipbuf_sys_hold_mem_active; + +void ipbuf_stop(void) +{ + int i; + + spin_lock(&ipb_free.lock); + INIT_IPBLINK(&ipb_free); + spin_unlock(&ipb_free.lock); + + ipbcfg.ln = 0; + if (ipbuf) { + kfree(ipbuf); + ipbuf = NULL; + } + for (i = 0; i < ipbuf_sys_hold_mem_active; i++) { + dsp_mem_disable((void *)daram_base); + } + ipbuf_sys_hold_mem_active = 0; +} + +int ipbuf_config(unsigned short ln, unsigned short lsz, void *base) +{ + unsigned long lsz_byte = ((unsigned long)lsz) << 1; + size_t size; + int ret = 0; + int i; + + /* + * global IPBUF + */ + if (((unsigned long)base) & 0x3) { + printk(KERN_ERR + "omapdsp: global ipbuf address(0x%p) is not " + "32-bit aligned!\n", base); + return -EINVAL; + } + size = lsz_byte * ln; + if (dsp_address_validate(base, size, "global ipbuf") < 0) + return -EINVAL; + + ipbuf = kmalloc(sizeof(void *) * ln, GFP_KERNEL); + if (ipbuf == NULL) { + printk(KERN_ERR + "omapdsp: memory allocation for ipbuf failed.\n"); + return -ENOMEM; + } + for (i = 0; i < ln; i++) { + void *top, *btm; + + top = base + (sizeof(struct ipbuf) + lsz_byte) * i; + btm = base + (sizeof(struct ipbuf) + lsz_byte) * (i+1) - 1; + ipbuf[i] = (struct ipbuf *)top; + if (((unsigned long)top & 0xfffe0000) != + ((unsigned long)btm & 0xfffe0000)) { + /* + * an ipbuf line should not cross + * 64k-word boundary. + */ + printk(KERN_ERR + "omapdsp: ipbuf[%d] crosses 64k-word boundary!\n" + " @0x%p, size=0x%08lx\n", i, top, lsz_byte); + ret = -EINVAL; + goto free_out; + } + } + ipbcfg.ln = ln; + ipbcfg.lsz = lsz; + ipbcfg.base = base; + ipbcfg.bsycnt = ln; /* DSP holds all ipbufs initially. */ + ipbcfg.cnt_full = 0; + + printk(KERN_INFO + "omapdsp: IPBUF configuration\n" + " %d words * %d lines at 0x%p.\n", + ipbcfg.lsz, ipbcfg.ln, ipbcfg.base); + + return ret; + +free_out: + kfree(ipbuf); + ipbuf = NULL; + return ret; +} + +int ipbuf_sys_config(void *p, enum arm_dsp_dir dir) +{ + char *dir_str = (dir == DIR_D2A) ? "D2A" : "A2D"; + + if (((unsigned long)p) & 0x3) { + printk(KERN_ERR + "omapdsp: system ipbuf(%s) address(0x%p) is " + "not 32-bit aligned!\n", dir_str, p); + return -1; + } + if (dsp_address_validate(p, sizeof(struct ipbuf_sys), + "system ipbuf(%s)", dir_str) < 0) + return -1; + if (dsp_mem_type(p, sizeof(struct ipbuf_sys)) != MEM_TYPE_EXTERN) { + printk(KERN_WARNING + "omapdsp: system ipbuf(%s) is placed in" + " DSP internal memory.\n" + " It will prevent DSP from idling.\n", dir_str); + ipbuf_sys_hold_mem_active++; + /* + * dsp_mem_enable() never fails because + * it has been already enabled in dspcfg process and + * this will just increment the usecount. + */ + dsp_mem_enable((void *)daram_base); + } + + if (dir == DIR_D2A) + ipbuf_sys_da = p; + else + ipbuf_sys_ad = p; + + return 0; +} + +int ipbuf_p_validate(void *p, enum arm_dsp_dir dir) +{ + char *dir_str = (dir == DIR_D2A) ? "D2A" : "A2D"; + + if (((unsigned long)p) & 0x3) { + printk(KERN_ERR + "omapdsp: private ipbuf(%s) address(0x%p) is " + "not 32-bit aligned!\n", dir_str, p); + return -1; + } + return dsp_address_validate(p, sizeof(struct ipbuf_p), + "private ipbuf(%s)", dir_str); +} + +/* + * Global IPBUF operations + */ +unsigned short get_free_ipbuf(unsigned char tid) +{ + unsigned short bid; + + if (dsp_mem_enable_ipbuf() < 0) + return OMAP_DSP_BID_NULL; + + spin_lock(&ipb_free.lock); + + if (ipblink_empty(&ipb_free)) { + /* FIXME: wait on queue when not available. */ + bid = OMAP_DSP_BID_NULL; + goto out; + } + bid = ipb_free.top; + ipbuf[bid]->la = tid; /* lock */ + ipblink_del_top(&ipb_free, ipbuf); +out: + spin_unlock(&ipb_free.lock); + dsp_mem_disable_ipbuf(); + + return bid; +} + +void release_ipbuf(unsigned short bid) +{ + if (ipbuf[bid]->la == OMAP_DSP_TID_FREE) { + printk(KERN_WARNING + "omapdsp: attempt to release unlocked IPBUF[%d].\n", + bid); + /* + * FIXME: re-calc bsycnt + */ + return; + } + ipbuf[bid]->la = OMAP_DSP_TID_FREE; + ipbuf[bid]->sa = OMAP_DSP_TID_FREE; + spin_lock(&ipb_free.lock); + ipblink_add_tail(&ipb_free, bid, ipbuf); + spin_unlock(&ipb_free.lock); +} + +static int try_yld(unsigned short bid) +{ + int status; + + ipbuf[bid]->sa = OMAP_DSP_TID_ANON; + status = dsp_mbsend(MBCMD(BKYLD), 0, bid); + if (status < 0) { + /* DSP is busy and ARM keeps this line. */ + release_ipbuf(bid); + return status; + } + + ipb_bsycnt_inc(&ipbcfg); + return 0; +} + +/* + * balancing ipbuf lines with DSP + */ +static void do_balance_ipbuf(void) +{ + while (ipbcfg.bsycnt <= ipbcfg.ln / 4) { + unsigned short bid; + + bid = get_free_ipbuf(OMAP_DSP_TID_ANON); + if (bid == OMAP_DSP_BID_NULL) + return; + if (try_yld(bid) < 0) + return; + } +} + +static DECLARE_WORK(balance_ipbuf_work, (void (*)(void *))do_balance_ipbuf, + NULL); + +void balance_ipbuf(void) +{ + schedule_work(&balance_ipbuf_work); +} + +/* for process context */ +void unuse_ipbuf(unsigned short bid) +{ + if (ipbcfg.bsycnt > ipbcfg.ln / 4) { + /* we don't have enough IPBUF lines. let's keep it. */ + release_ipbuf(bid); + } else { + /* we have enough IPBUF lines. let's return this line to DSP. */ + ipbuf[bid]->la = OMAP_DSP_TID_ANON; + try_yld(bid); + balance_ipbuf(); + } +} + +/* for interrupt context */ +void unuse_ipbuf_nowait(unsigned short bid) +{ + release_ipbuf(bid); + balance_ipbuf(); +} + +/* + * functions called from mailbox1 interrupt routine + */ + +void mbx1_err_ipbfull(void) +{ + ipbcfg.cnt_full++; +} + +/* + * sysfs files + */ +static ssize_t ipbuf_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int len = 0; + unsigned short bid; + + for (bid = 0; bid < ipbcfg.ln; bid++) { + unsigned short la = ipbuf[bid]->la; + unsigned short ld = ipbuf[bid]->ld; + unsigned short c = ipbuf[bid]->c; + + if (len > PAGE_SIZE - 100) { + len += sprintf(buf + len, "out of buffer.\n"); + goto finish; + } + + len += sprintf(buf + len, "ipbuf[%d]: adr = 0x%p\n", + bid, ipbuf[bid]); + if (la == OMAP_DSP_TID_FREE) { + len += sprintf(buf + len, + " DSPtask[%d]->Linux " + "(already read and now free for Linux)\n", + ld); + } else if (ld == OMAP_DSP_TID_FREE) { + len += sprintf(buf + len, + " Linux->DSPtask[%d] " + "(already read and now free for DSP)\n", + la); + } else if (ipbuf_is_held(ld, bid)) { + len += sprintf(buf + len, + " DSPtask[%d]->Linux " + "(waiting to be read)\n" + " count = %d\n", ld, c); + } else { + len += sprintf(buf + len, + " Linux->DSPtask[%d] " + "(waiting to be read)\n" + " count = %d\n", la, c); + } + } + + len += sprintf(buf + len, "\nFree IPBUF link: "); + spin_lock(&ipb_free.lock); + ipblink_for_each(bid, &ipb_free, ipbuf) { + len += sprintf(buf + len, "%d ", bid); + } + spin_unlock(&ipb_free.lock); + len += sprintf(buf + len, "\n"); + len += sprintf(buf + len, "IPBFULL error count: %ld\n", + ipbcfg.cnt_full); + +finish: + return len; +} + +struct device_attribute dev_attr_ipbuf = __ATTR_RO(ipbuf); diff --git a/arch/arm/plat-omap/dsp/ipbuf.h b/arch/arm/plat-omap/dsp/ipbuf.h new file mode 100644 index 00000000000..77826f443d2 --- /dev/null +++ b/arch/arm/plat-omap/dsp/ipbuf.h @@ -0,0 +1,134 @@ +/* + * linux/arch/arm/mach-omap/dsp/ipbuf.h + * + * Header for IPBUF + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2005/05/17: DSP Gateway version 3.3 + */ + +struct ipbuf { + unsigned short c; /* count */ + unsigned short next; /* link */ + unsigned short la; /* lock owner (ARM side) */ + unsigned short sa; /* sync word (ARM->DSP) */ + unsigned short ld; /* lock owner (DSP side) */ + unsigned short sd; /* sync word (DSP->ARM) */ + unsigned char d[0]; /* data */ +}; + +struct ipbuf_p { + unsigned short c; /* count */ + unsigned short s; /* sync word */ + unsigned short al; /* data address lower */ + unsigned short ah; /* data address upper */ +}; + +struct ipbuf_sys { + unsigned short s; /* sync word */ + unsigned short d[31]; /* data */ +}; + +struct ipbcfg { + unsigned short ln; + unsigned short lsz; + void *base; + unsigned short bsycnt; + unsigned long cnt_full; /* count of IPBFULL error */ +}; + +extern struct ipbuf **ipbuf; +extern struct ipbcfg ipbcfg; +extern struct ipbuf_sys *ipbuf_sys_da, *ipbuf_sys_ad; + +#define ipb_bsycnt_inc(ipbcfg) \ + do { \ + disable_irq(INT_D2A_MB1); \ + (ipbcfg)->bsycnt++; \ + enable_irq(INT_D2A_MB1); \ + } while(0) + +#define ipb_bsycnt_dec(ipbcfg) \ + do { \ + disable_irq(INT_D2A_MB1); \ + (ipbcfg)->bsycnt--; \ + enable_irq(INT_D2A_MB1); \ + } while(0) + +#define dsp_mem_enable_ipbuf() dsp_mem_enable(ipbcfg.base) +#define dsp_mem_disable_ipbuf() dsp_mem_disable(ipbcfg.base) + +struct ipblink { + spinlock_t lock; + unsigned short top; + unsigned short tail; +}; + +#define IPBLINK_INIT { \ + .lock = SPIN_LOCK_UNLOCKED, \ + .top = OMAP_DSP_BID_NULL, \ + .tail = OMAP_DSP_BID_NULL, \ + } + +#define INIT_IPBLINK(link) \ + do { \ + spin_lock_init(&(link)->lock); \ + (link)->top = OMAP_DSP_BID_NULL; \ + (link)->tail = OMAP_DSP_BID_NULL; \ + } while(0) + +#define ipblink_empty(link) ((link)->top == OMAP_DSP_BID_NULL) + +static __inline__ void ipblink_del_top(struct ipblink *link, + struct ipbuf **ipbuf) +{ + struct ipbuf *bufp = ipbuf[link->top]; + + if ((link->top = bufp->next) == OMAP_DSP_BID_NULL) + link->tail = OMAP_DSP_BID_NULL; + else + bufp->next = OMAP_DSP_BID_NULL; +} + +static __inline__ void ipblink_add_tail(struct ipblink *link, + unsigned short bid, + struct ipbuf **ipbuf) +{ + if (ipblink_empty(link)) + link->top = bid; + else + ipbuf[link->tail]->next = bid; + link->tail = bid; +} + +static __inline__ void ipblink_add_pvt(struct ipblink *link) +{ + link->top = OMAP_DSP_BID_PVT; + link->tail = OMAP_DSP_BID_PVT; +} + +static __inline__ void ipblink_del_pvt(struct ipblink *link) +{ + link->top = OMAP_DSP_BID_NULL; + link->tail = OMAP_DSP_BID_NULL; +} + +#define ipblink_for_each(bid, link, ipbuf) \ + for (bid = (link)->top; bid != OMAP_DSP_BID_NULL; bid = ipbuf[bid]->next) diff --git a/arch/arm/plat-omap/dsp/mblog.c b/arch/arm/plat-omap/dsp/mblog.c new file mode 100644 index 00000000000..32989766020 --- /dev/null +++ b/arch/arm/plat-omap/dsp/mblog.c @@ -0,0 +1,276 @@ +/* + * linux/arch/arm/mach-omap/dsp/mblog.c + * + * OMAP DSP driver Mailbox log module + * + * Copyright (C) 2003-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2005/05/18: DSP Gateway version 3.3 + */ + +#include +#include +#include +#include +#include +#include +#include "dsp.h" + +#define RLCMD(nm) OMAP_DSP_MBCMD_RUNLEVEL_##nm +#define KFUNCCMD(nm) OMAP_DSP_MBCMD_KFUNC_##nm +#define PMCMD(nm) OMAP_DSP_MBCMD_PM_##nm +#define CFGCMD(nm) OMAP_DSP_MBCMD_DSPCFG_##nm +#define REGCMD(nm) OMAP_DSP_MBCMD_REGRW_##nm +#define VICMD(nm) OMAP_DSP_MBCMD_VARID_##nm +#define EID(nm) OMAP_DSP_EID_##nm + +char *subcmd_name(struct mbcmd *mb) +{ + unsigned char cmd_h = mb->cmd_h; + unsigned char cmd_l = mb->cmd_l; + char *s; + + switch (cmd_h) { + case MBCMD(RUNLEVEL): + s = (cmd_l == RLCMD(USER)) ? "USER": + (cmd_l == RLCMD(SUPER)) ? "SUPER": + (cmd_l == RLCMD(RECOVERY)) ? "RECOVERY": + NULL; + break; + case MBCMD(PM): + s = (cmd_l == PMCMD(DISABLE)) ? "DISABLE": + (cmd_l == PMCMD(ENABLE)) ? "ENABLE": + NULL; + break; + case MBCMD(KFUNC): + s = (cmd_l == KFUNCCMD(FBCTL)) ? "FBCTL": + (cmd_l == KFUNCCMD(AUDIO_PWR)) ? "AUDIO_PWR": + NULL; + break; + case MBCMD(DSPCFG): + { + unsigned char cfgc = cmd_l & 0x7f; + s = (cfgc == CFGCMD(REQ)) ? "REQ": + (cfgc == CFGCMD(SYSADRH)) ? "SYSADRH": + (cfgc == CFGCMD(SYSADRL)) ? "SYSADRL": + (cfgc == CFGCMD(ABORT)) ? "ABORT": + (cfgc == CFGCMD(PROTREV)) ? "PROTREV": + NULL; + break; + } + case MBCMD(REGRW): + s = (cmd_l == REGCMD(MEMR)) ? "MEMR": + (cmd_l == REGCMD(MEMW)) ? "MEMW": + (cmd_l == REGCMD(IOR)) ? "IOR": + (cmd_l == REGCMD(IOW)) ? "IOW": + (cmd_l == REGCMD(DATA)) ? "DATA": + NULL; + break; + case MBCMD(GETVAR): + case MBCMD(SETVAR): + s = (cmd_l == VICMD(ICRMASK)) ? "ICRMASK": + (cmd_l == VICMD(LOADINFO)) ? "LOADINFO": + NULL; + break; + case MBCMD(ERR): + s = (cmd_l == EID(BADTID)) ? "BADTID": + (cmd_l == EID(BADTCN)) ? "BADTCN": + (cmd_l == EID(BADBID)) ? "BADBID": + (cmd_l == EID(BADCNT)) ? "BADCNT": + (cmd_l == EID(NOTLOCKED)) ? "NOTLOCKED": + (cmd_l == EID(STVBUF)) ? "STVBUF": + (cmd_l == EID(BADADR)) ? "BADADR": + (cmd_l == EID(BADTCTL)) ? "BADTCTL": + (cmd_l == EID(BADPARAM)) ? "BADPARAM": + (cmd_l == EID(FATAL)) ? "FATAL": + (cmd_l == EID(WDT)) ? "WDT": + (cmd_l == EID(NOMEM)) ? "NOMEM": + (cmd_l == EID(NORES)) ? "NORES": + (cmd_l == EID(IPBFULL)) ? "IPBFULL": + (cmd_l == EID(TASKNOTRDY)) ? "TASKNOTRDY": + (cmd_l == EID(TASKBSY)) ? "TASKBSY": + (cmd_l == EID(TASKERR)) ? "TASKERR": + (cmd_l == EID(BADCFGTYP)) ? "BADCFGTYP": + (cmd_l == EID(DEBUG)) ? "DEBUG": + (cmd_l == EID(BADSEQ)) ? "BADSEQ": + (cmd_l == EID(BADCMD)) ? "BADCMD": + NULL; + break; + default: + s = NULL; + } + + return s; +} + +/* output of show() method should fit to PAGE_SIZE */ +#define MBLOG_DEPTH 64 + +struct mblogent { + unsigned long jiffies; + unsigned short cmd; + unsigned short data; + enum arm_dsp_dir dir; +}; + +static struct { + spinlock_t lock; + int wp; + unsigned long cnt, cnt_ad, cnt_da; + struct mblogent ent[MBLOG_DEPTH]; +} mblog; + +void mblog_add(struct mbcmd *mb, enum arm_dsp_dir dir) +{ + struct mbcmd_hw *mb_hw = (struct mbcmd_hw *)mb; + struct mblogent *ent; + + spin_lock(&mblog.lock); + ent = &mblog.ent[mblog.wp]; + ent->jiffies = jiffies; + ent->cmd = mb_hw->cmd; + ent->data = mb_hw->data; + ent->dir = dir; + if (mblog.cnt < 0xffffffff) + mblog.cnt++; + switch (dir) { + case DIR_A2D: + if (mblog.cnt_ad < 0xffffffff) + mblog.cnt_ad++; + break; + case DIR_D2A: + if (mblog.cnt_da < 0xffffffff) + mblog.cnt_da++; + break; + } + if (++mblog.wp == MBLOG_DEPTH) + mblog.wp = 0; + spin_unlock(&mblog.lock); +} + +/* + * sysfs file + */ +static ssize_t mblog_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + int len = 0; + int wp; + int i; + + spin_lock(&mblog.lock); + + wp = mblog.wp; + len += sprintf(buf + len, + "log count:%ld / ARM->DSP:%ld, DSP->ARM:%ld\n", + mblog.cnt, mblog.cnt_ad, mblog.cnt_da); + if (mblog.cnt == 0) + goto done; + + len += sprintf(buf + len, " ARM -> DSP ARM <- DSP\n"); + len += sprintf(buf + len, "jiffies q cmd data q cmd data\n"); + i = (mblog.cnt >= MBLOG_DEPTH) ? wp : 0; + do { + struct mblogent *ent = &mblog.ent[i]; + union { + struct mbcmd sw; + struct mbcmd_hw hw; + } mb = { + .hw.cmd = ent->cmd, + .hw.data = ent->data + }; + char *subname; + const struct cmdinfo *ci = cmdinfo[mb.sw.cmd_h]; + + len += sprintf(buf + len, + (ent->dir == DIR_A2D) ? + "%08lx %d %04x %04x ": + "%08lx %d %04x %04x ", + ent->jiffies, mb.sw.seq, ent->cmd, ent->data); + switch (ci->cmd_l_type) { + case CMD_L_TYPE_SUBCMD: + if ((subname = subcmd_name(&mb.sw)) == NULL) + subname = "Unknown"; + len += sprintf(buf + len, "%s:%s\n", + ci->name, subname); + break; + case CMD_L_TYPE_TID: + len += sprintf(buf + len, "%s:task %d\n", + ci->name, mb.sw.cmd_l); + break; + case CMD_L_TYPE_NULL: + len += sprintf(buf + len, "%s\n", ci->name); + break; + } + + if (++i == MBLOG_DEPTH) + i = 0; + } while (i != wp); + +done: + spin_unlock(&mblog.lock); + + return len; +} + +static struct device_attribute dev_attr_mblog = __ATTR_RO(mblog); + +#ifdef CONFIG_OMAP_DSP_MBCMD_VERBOSE +void mblog_printcmd(struct mbcmd *mb, enum arm_dsp_dir dir) +{ + const struct cmdinfo *ci = cmdinfo[mb->cmd_h]; + char *dir_str; + char *subname; + + dir_str = (dir == DIR_A2D) ? "sending" : "receiving"; + switch (ci->cmd_l_type) { + case CMD_L_TYPE_SUBCMD: + if ((subname = subcmd_name(mb)) == NULL) + subname = "Unknown"; + printk(KERN_DEBUG + "mbx: %s seq=%d, cmd=%02x:%02x(%s:%s), data=%04x\n", + dir_str, mb->seq, mb->cmd_h, mb->cmd_l, + ci->name, subname, mb->data); + break; + case CMD_L_TYPE_TID: + printk(KERN_DEBUG + "mbx: %s seq=%d, cmd=%02x:%02x(%s:task %d), data=%04x\n", + dir_str, mb->seq, mb->cmd_h, mb->cmd_l, + ci->name, mb->cmd_l, mb->data); + break; + case CMD_L_TYPE_NULL: + printk(KERN_DEBUG + "mbx: %s seq=%d, cmd=%02x:%02x(%s), data=%04x\n", + dir_str, mb->seq, mb->cmd_h, mb->cmd_l, + ci->name, mb->data); + break; + } +} +#endif /* CONFIG_OMAP_DSP_MBCMD_VERBOSE */ + +void __init mblog_init(void) +{ + spin_lock_init(&mblog.lock); + device_create_file(&dsp_device.dev, &dev_attr_mblog); +} + +void mblog_exit(void) +{ + device_remove_file(&dsp_device.dev, &dev_attr_mblog); +} diff --git a/arch/arm/plat-omap/dsp/proclist.h b/arch/arm/plat-omap/dsp/proclist.h new file mode 100644 index 00000000000..7265ea0fd4f --- /dev/null +++ b/arch/arm/plat-omap/dsp/proclist.h @@ -0,0 +1,97 @@ +/* + * linux/arch/arm/mach-omap/dsp/proclist.h + * + * Linux task list handler + * + * Copyright (C) 2004,2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2004/11/22: DSP Gateway version 3.3 + */ + +struct proc_list { + struct list_head list_head; + pid_t pid; + unsigned int cnt; +}; + +static __inline__ void proc_list_add(struct list_head *list, + struct task_struct *tsk) +{ + struct proc_list *pl; + struct proc_list *new; + + list_for_each_entry(pl, list, list_head) { + if (pl->pid == tsk->pid) { + /* + * this process has opened DSP devices multi time + */ + pl->cnt++; + return; + } + } + + new = kmalloc(sizeof(struct proc_list), GFP_KERNEL); + new->pid = tsk->pid; + new->cnt = 1; + list_add_tail(&new->list_head, list); +} + +static __inline__ void proc_list_del(struct list_head *list, + struct task_struct *tsk) +{ + struct proc_list *pl, *next; + + list_for_each_entry(pl, list, list_head) { + if (pl->pid == tsk->pid) { + if (--pl->cnt == 0) { + list_del(&pl->list_head); + kfree(pl); + } + return; + } + } + + /* + * correspinding pid wasn't found in the list + * -- this means the caller of proc_list_del is different from + * the proc_list_add's caller. in this case, the parent is + * cleaning up the context of a killed child. + * let's delete exiting task from the list. + */ + /* need to lock tasklist_lock before calling find_task_by_pid_type. */ + read_lock(&tasklist_lock); + list_for_each_entry_safe(pl, next, list, list_head) { + if (find_task_by_pid_type(PIDTYPE_PID, pl->pid) == NULL) { + list_del(&pl->list_head); + kfree(pl); + } + } + read_unlock(&tasklist_lock); +} + +static __inline__ void proc_list_flush(struct list_head *list) +{ + struct proc_list *pl; + + while (!list_empty(list)) { + pl = list_entry(list->next, struct proc_list, list_head); + list_del(&pl->list_head); + kfree(pl); + } +} diff --git a/arch/arm/plat-omap/dsp/task.c b/arch/arm/plat-omap/dsp/task.c new file mode 100644 index 00000000000..1abf4dff82c --- /dev/null +++ b/arch/arm/plat-omap/dsp/task.c @@ -0,0 +1,2822 @@ +/* + * linux/arch/arm/mach-omap/dsp/task.c + * + * OMAP DSP task device driver + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * mmap function by Hiroo Ishikawa + * + * 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 + * + * 2005/07/26: DSP Gateway version 3.3 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "uaccess_dsp.h" +#include "dsp.h" +#include "ipbuf.h" +#include "fifo.h" +#include "proclist.h" + +#define is_aligned(adr,align) (!((adr)&((align)-1))) + +/* + * taskdev.state: device state machine + * NOTASK: task is not attached. + * ATTACHED: task is attached. + * GARBAGE: task is detached. waiting for all processes to close this device. + * ADDREQ: requesting for tadd + * DELREQ: requesting for tdel. no process is opening this device. + * ADDFAIL: tadd failed. + * ADDING: tadd in process. + * DELING: tdel in process. + * KILLING: tkill in process. + */ +#define devstate_name(stat) (\ + ((stat) & OMAP_DSP_DEVSTATE_NOTASK) ? "NOTASK" :\ + ((stat) & OMAP_DSP_DEVSTATE_ATTACHED) ? "ATTACHED" :\ + ((stat) & OMAP_DSP_DEVSTATE_GARBAGE) ? "GARBAGE" :\ + ((stat) & OMAP_DSP_DEVSTATE_INVALID) ? "INVALID" :\ + ((stat) & OMAP_DSP_DEVSTATE_ADDREQ) ? "ADDREQ" :\ + ((stat) & OMAP_DSP_DEVSTATE_DELREQ) ? "DELREQ" :\ + ((stat) & OMAP_DSP_DEVSTATE_ADDFAIL) ? "ADDFAIL" :\ + ((stat) & OMAP_DSP_DEVSTATE_ADDING) ? "ADDING" :\ + ((stat) & OMAP_DSP_DEVSTATE_DELING) ? "DELING" :\ + ((stat) & OMAP_DSP_DEVSTATE_KILLING) ? "KILLING" :\ + "unknown") + +struct taskdev { + struct bus_type *bus; +// struct device_driver *driver; + struct device dev; /* Generic device interface */ + + long state; + spinlock_t state_lock; + wait_queue_head_t state_wait_q; + unsigned int usecount; + char name[OMAP_DSP_TNM_LEN]; + struct file_operations fops; + struct list_head proc_list; + struct dsptask *task; + + /* read stuff */ + wait_queue_head_t read_wait_q; + struct semaphore read_sem; + + /* write stuff */ + wait_queue_head_t write_wait_q; + struct semaphore write_sem; + + /* ioctl stuff */ + wait_queue_head_t ioctl_wait_q; + struct semaphore ioctl_sem; + + /* device lock */ + struct semaphore lock_sem; + pid_t lock_pid; +}; + +#define to_taskdev(n) container_of(n, struct taskdev, dev) + +struct rcvdt_bk_struct { + struct ipblink link; + unsigned int rp; + struct ipbuf_p *ipbuf_pvt_r; +}; + +struct dsptask { + enum { + TASK_STATE_ERR = 0, + TASK_STATE_READY, + TASK_STATE_CFGREQ + } state; + unsigned char tid; + char name[OMAP_DSP_TNM_LEN]; + unsigned short ttyp; + struct taskdev *dev; + + /* read stuff */ + union { + struct fifo_struct fifo; /* for active word */ + struct rcvdt_bk_struct bk; + } rcvdt; + + /* write stuff */ + size_t wsz; + spinlock_t wsz_lock; + struct ipbuf_p *ipbuf_pvt_w; /* for private block */ + + /* tctl stuff */ + int tctl_stat; + + /* mmap stuff */ + void *map_base; + size_t map_length; +}; + +#define sndtyp_acv(ttyp) ((ttyp) & OMAP_DSP_TTYP_ASND) +#define sndtyp_psv(ttyp) (!((ttyp) & OMAP_DSP_TTYP_ASND)) +#define sndtyp_bk(ttyp) ((ttyp) & OMAP_DSP_TTYP_BKDM) +#define sndtyp_wd(ttyp) (!((ttyp) & OMAP_DSP_TTYP_BKDM)) +#define sndtyp_pvt(ttyp) ((ttyp) & OMAP_DSP_TTYP_PVDM) +#define sndtyp_gbl(ttyp) (!((ttyp) & OMAP_DSP_TTYP_PVDM)) +#define rcvtyp_acv(ttyp) ((ttyp) & OMAP_DSP_TTYP_ARCV) +#define rcvtyp_psv(ttyp) (!((ttyp) & OMAP_DSP_TTYP_ARCV)) +#define rcvtyp_bk(ttyp) ((ttyp) & OMAP_DSP_TTYP_BKMD) +#define rcvtyp_wd(ttyp) (!((ttyp) & OMAP_DSP_TTYP_BKMD)) +#define rcvtyp_pvt(ttyp) ((ttyp) & OMAP_DSP_TTYP_PVMD) +#define rcvtyp_gbl(ttyp) (!((ttyp) & OMAP_DSP_TTYP_PVMD)) + +static int dsp_rmdev_minor(unsigned char minor); +static int taskdev_init(struct taskdev *dev, char *name, unsigned char minor); +static void taskdev_delete(unsigned char minor); +static void taskdev_attach_task(struct taskdev *dev, struct dsptask *task); +static void taskdev_detach_task(struct taskdev *dev); +static int dsp_tdel_bh(unsigned char minor, unsigned short type); + +static ssize_t devname_show(struct device *d, struct device_attribute *attr, + char *buf); +static ssize_t devstate_show(struct device *d, struct device_attribute *attr, + char *buf); +static ssize_t proc_list_show(struct device *d, struct device_attribute *attr, + char *buf); +static ssize_t taskname_show(struct device *d, struct device_attribute *attr, + char *buf); +static ssize_t ttyp_show(struct device *d, struct device_attribute *attr, + char *buf); +static ssize_t fifosz_show(struct device *d, struct device_attribute *attr, + char *buf); +static int fifosz_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t count); +static ssize_t fifocnt_show(struct device *d, struct device_attribute *attr, + char *buf); +static ssize_t ipblink_show(struct device *d, struct device_attribute *attr, + char *buf); +static ssize_t wsz_show(struct device *d, struct device_attribute *attr, + char *buf); +static ssize_t mmap_show(struct device *d, struct device_attribute *attr, + char *buf); + +static struct device_attribute dev_attr_devname = __ATTR_RO(devname); +static struct device_attribute dev_attr_devstate = __ATTR_RO(devstate); +static struct device_attribute dev_attr_proc_list = __ATTR_RO(proc_list); +static struct device_attribute dev_attr_fifosz = + __ATTR(fifosz, S_IWUGO | S_IRUGO, fifosz_show, fifosz_store); +static struct device_attribute dev_attr_fifocnt = __ATTR_RO(fifocnt); +static struct device_attribute dev_attr_taskname = __ATTR_RO(taskname); +static struct device_attribute dev_attr_ttyp = __ATTR_RO(ttyp); +static struct device_attribute dev_attr_ipblink = __ATTR_RO(ipblink); +static struct device_attribute dev_attr_wsz = __ATTR_RO(wsz); +static struct device_attribute dev_attr_mmap = __ATTR_RO(mmap); + +static struct bus_type dsptask_bus = { + .name = "dsptask", +}; + +static struct class *dsp_task_class; +static struct taskdev *taskdev[TASKDEV_MAX]; +static struct dsptask *dsptask[TASKDEV_MAX]; +static DECLARE_MUTEX(cfg_sem); +static unsigned short cfg_cmd; +static unsigned char cfg_tid; +static DECLARE_WAIT_QUEUE_HEAD(cfg_wait_q); +static unsigned char n_task; +static void *heap; + +#define devstate_lock(dev, devstate) devstate_lock_timeout(dev, devstate, 0) + +/* + * devstate_lock_timeout(): + * when called with timeout > 0, dev->state can be diffeent from what you want. + */ +static int devstate_lock_timeout(struct taskdev *dev, long devstate, + int timeout) +{ + DECLARE_WAITQUEUE(wait, current); + long current_state = current->state; + int ret = 0; + + spin_lock(&dev->state_lock); + add_wait_queue(&dev->state_wait_q, &wait); + while (!(dev->state & devstate)) { + set_current_state(TASK_INTERRUPTIBLE); + spin_unlock(&dev->state_lock); + if (timeout) { + if ((timeout = schedule_timeout(timeout)) == 0) { + /* timeout */ + spin_lock(&dev->state_lock); + break; + } + } + else + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + spin_lock(&dev->state_lock); + } + remove_wait_queue(&dev->state_wait_q, &wait); + set_current_state(current_state); + return ret; +} + +static __inline__ void devstate_unlock(struct taskdev *dev) +{ + spin_unlock(&dev->state_lock); +} + +static __inline__ int down_tasksem_interruptible(struct taskdev *dev, + struct semaphore *sem) +{ + int ret; + + if (dev->lock_pid == current->pid) { + /* this process has lock */ + ret = down_interruptible(sem); + } else { + if ((ret = down_interruptible(&dev->lock_sem)) != 0) + return ret; + ret = down_interruptible(sem); + up(&dev->lock_sem); + } + return ret; +} + +static void proclist_send_sigbus(struct list_head *list) +{ + siginfo_t info; + struct proc_list *pl; + struct task_struct *tsk; + + info.si_signo = SIGBUS; + info.si_errno = 0; + info.si_code = SI_KERNEL; + info._sifields._sigfault._addr = NULL; + + /* need to lock tasklist_lock before calling find_task_by_pid_type. */ + read_lock(&tasklist_lock); + list_for_each_entry(pl, list, list_head) { + if ((tsk = find_task_by_pid_type(PIDTYPE_PID, pl->pid)) != NULL) + send_sig_info(SIGBUS, &info, tsk); + } + read_unlock(&tasklist_lock); +} + +static int dsp_task_flush_buf(struct dsptask *task) +{ + unsigned short ttyp = task->ttyp; + + if (sndtyp_wd(ttyp)) { + /* word receiving */ + flush_fifo(&task->rcvdt.fifo); + } else { + /* block receiving */ + struct rcvdt_bk_struct *rcvdt = &task->rcvdt.bk; + + spin_lock(&rcvdt->link.lock); + if (sndtyp_gbl(ttyp)) { + /* global IPBUF */ + while (!ipblink_empty(&rcvdt->link)) { + unsigned short bid = rcvdt->link.top; + ipblink_del_top(&rcvdt->link, ipbuf); + unuse_ipbuf(bid); + } + } else { + /* private IPBUF */ + if (!ipblink_empty(&rcvdt->link)) { + ipblink_del_pvt(&rcvdt->link); + release_ipbuf_pvt(rcvdt->ipbuf_pvt_r); + } + } + spin_unlock(&rcvdt->link.lock); + } + + return 0; +} + +static int dsp_task_set_fifosz(struct dsptask *task, unsigned long sz) +{ + unsigned short ttyp = task->ttyp; + int stat; + + if (!(sndtyp_wd(ttyp) && sndtyp_acv(ttyp))) { + printk(KERN_ERR + "omapdsp: buffer size can be changed only for " + "active word sending task.\n"); + return -EINVAL; + } + if ((sz == 0) || (sz & 1)) { + printk(KERN_ERR "omapdsp: illegal buffer size! (%ld)\n" + "it must be even and non-zero value.\n", sz); + return -EINVAL; + } + + stat = realloc_fifo(&task->rcvdt.fifo, sz); + if (stat == -EBUSY) { + printk(KERN_ERR "omapdsp: buffer is not empty!\n"); + return stat; + } else if (stat < 0) { + printk(KERN_ERR + "omapdsp: unable to change receive buffer size. " + "(%ld bytes for %s)\n", sz, task->name); + return stat; + } + + return 0; +} + +static int taskdev_lock(struct taskdev *dev) +{ + if (down_interruptible(&dev->lock_sem)) + return -ERESTARTSYS; + dev->lock_pid = current->pid; + return 0; +} + +static int taskdev_unlock(struct taskdev *dev) +{ + if (dev->lock_pid != current->pid) { + printk(KERN_ERR + "omapdsp: an illegal process attempted to " + "unlock the dsptask lock!\n"); + return -EINVAL; + } + dev->lock_pid = 0; + up(&dev->lock_sem); + return 0; +} + +static int dsp_task_config(struct dsptask *task, unsigned char tid) +{ + unsigned short ttyp; + struct mbcmd mb; + int ret; + + task->tid = tid; + dsptask[tid] = task; + + /* TCFG request */ + task->state = TASK_STATE_CFGREQ; + if (down_interruptible(&cfg_sem)) { + ret = -ERESTARTSYS; + goto fail_out; + } + cfg_cmd = MBCMD(TCFG); + mbcmd_set(mb, MBCMD(TCFG), tid, 0); + dsp_mbcmd_send_and_wait(&mb, &cfg_wait_q); + cfg_cmd = 0; + up(&cfg_sem); + + if (task->state != TASK_STATE_READY) { + printk(KERN_ERR "omapdsp: task %d configuration error!\n", tid); + ret = -EINVAL; + goto fail_out; + } + + if (strlen(task->name) <= 1) + sprintf(task->name, "%d", tid); + printk(KERN_INFO "omapdsp: task %d: name %s\n", tid, task->name); + + ttyp = task->ttyp; + + /* + * task info sanity check + */ + + /* task type check */ + if (rcvtyp_psv(ttyp) && rcvtyp_pvt(ttyp)) { + printk(KERN_ERR "omapdsp: illegal task type(0x%04x), tid=%d\n", + tid, ttyp); + ret = -EINVAL; + goto fail_out; + } + + /* private buffer address check */ + if (sndtyp_pvt(ttyp) && + (ipbuf_p_validate(task->rcvdt.bk.ipbuf_pvt_r, DIR_D2A) < 0)) { + ret = -EINVAL; + goto fail_out; + } + if (rcvtyp_pvt(ttyp) && + (ipbuf_p_validate(task->ipbuf_pvt_w, DIR_A2D) < 0)) { + ret = -EINVAL; + goto fail_out; + } + + /* mmap buffer configuration check */ + if ((task->map_length > 0) && + ((!is_aligned((unsigned long)task->map_base, PAGE_SIZE)) || + (!is_aligned(task->map_length, PAGE_SIZE)) || + (dsp_mem_type(task->map_base, task->map_length) != MEM_TYPE_EXTERN))) { + printk(KERN_ERR + "omapdsp: illegal mmap buffer address(0x%p) or " + "length(0x%x).\n" + " It needs to be page-aligned and located at " + "external memory.\n", + task->map_base, task->map_length); + ret = -EINVAL; + goto fail_out; + } + + /* + * initialization + */ + + /* read initialization */ + if (sndtyp_wd(ttyp)) { + /* word */ + size_t fifosz; + + fifosz = sndtyp_psv(ttyp) ? 2 : /* passive */ + 32; /* active */ + if (init_fifo(&task->rcvdt.fifo, fifosz) < 0) { + printk(KERN_ERR + "omapdsp: unable to allocate receive buffer. " + "(%d bytes for %s)\n", fifosz, task->name); + ret = -ENOMEM; + goto fail_out; + } + } else { + /* block */ + INIT_IPBLINK(&task->rcvdt.bk.link); + task->rcvdt.bk.rp = 0; + } + + /* write initialization */ + spin_lock_init(&task->wsz_lock); + task->wsz = rcvtyp_acv(ttyp) ? 0 : /* active */ + rcvtyp_wd(ttyp) ? 2 : /* passive word */ + ipbcfg.lsz*2; /* passive block */ + + return 0; + +fail_out: + dsptask[tid] = NULL; + return ret; +} + +static void dsp_task_init(struct dsptask *task) +{ + dsp_mbsend(MBCMD(TCTL), task->tid, OMAP_DSP_MBCMD_TCTL_TINIT); +} + +int dsp_task_config_all(unsigned char n) +{ + int i, ret; + struct taskdev *devheap; + struct dsptask *taskheap; + size_t devheapsz, taskheapsz; + + memset(taskdev, 0, sizeof(void *) * TASKDEV_MAX); + memset(dsptask, 0, sizeof(void *) * TASKDEV_MAX); + + n_task = n; + printk(KERN_INFO "omapdsp: found %d task(s)\n", n_task); + if (n_task == 0) + return 0; + + /* + * reducing kmalloc! + */ + devheapsz = sizeof(struct taskdev) * n_task; + taskheapsz = sizeof(struct dsptask) * n_task; + heap = kmalloc(devheapsz + taskheapsz, GFP_KERNEL); + if (heap == NULL) { + n_task = 0; + return -ENOMEM; + } + memset(heap, 0, devheapsz + taskheapsz); + devheap = heap; + taskheap = heap + devheapsz; + + for (i = 0; i < n_task; i++) { + struct taskdev *dev = &devheap[i]; + struct dsptask *task = &taskheap[i]; + + if ((ret = dsp_task_config(task, i)) < 0) + return ret; + if ((ret = taskdev_init(dev, task->name, i)) < 0) + return ret; + taskdev_attach_task(dev, task); + dsp_task_init(task); + printk(KERN_INFO "omapdsp: taskdev %s enabled.\n", dev->name); + } + + return 0; +} + +static void dsp_task_unconfig(struct dsptask *task) +{ + unsigned char tid = task->tid; + + preempt_disable(); + dsp_task_flush_buf(task); + if (sndtyp_wd(task->ttyp) && (task->state == TASK_STATE_READY)) + free_fifo(&task->rcvdt.fifo); + dsptask[tid] = NULL; + preempt_enable(); +} + +void dsp_task_unconfig_all(void) +{ + unsigned char minor; + unsigned char tid; + struct dsptask *task; + + for (minor = 0; minor < n_task; minor++) { + /* + * taskdev[minor] can be NULL in case of + * configuration failure + */ + if (taskdev[minor]) + taskdev_delete(minor); + } + for (; minor < TASKDEV_MAX; minor++) { + if (taskdev[minor]) + dsp_rmdev_minor(minor); + } + + for (tid = 0; tid < n_task; tid++) { + /* + * dsptask[tid] can be NULL in case of + * configuration failure + */ + task = dsptask[tid]; + if (task) + dsp_task_unconfig(task); + } + for (; tid < TASKDEV_MAX; tid++) { + task = dsptask[tid]; + if (task) { + /* + * on-demand tasks should be deleted in + * rmdev_minor(), but just in case. + */ + dsp_task_unconfig(task); + kfree(task); + } + } + + if (heap) { + kfree(heap); + heap = NULL; + } + + n_task = 0; +} + +static struct device_driver dsptask_driver = { + .name = "dsptask", + .bus = &dsptask_bus, +}; + +unsigned char dsp_task_count(void) +{ + return n_task; +} + +int dsp_taskmod_busy(void) +{ + struct taskdev *dev; + unsigned char minor; + + for (minor = 0; minor < TASKDEV_MAX; minor++) { + dev = taskdev[minor]; + if (dev == NULL) + continue; + if (dev->usecount > 0) { + printk("dsp_taskmod_busy(): %s: usecount=%d\n", + dev->name, dev->usecount); + return 1; + } +/* + if ((dev->state & (OMAP_DSP_DEVSTATE_ADDREQ | + OMAP_DSP_DEVSTATE_DELREQ)) { +*/ + if (dev->state & OMAP_DSP_DEVSTATE_ADDREQ) { + printk("dsp_taskmod_busy(): %s is in %s\n", + dev->name, devstate_name(dev->state)); + return 1; + } + } + return 0; +} + +/* + * DSP task device file operations + */ +static ssize_t dsp_task_read_wd_acv(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + struct taskdev *dev = taskdev[minor]; + int have_devstate_lock = 0; + int ret = 0; + + if (count == 0) { + return 0; + } else if (count & 0x1) { + printk(KERN_ERR + "omapdsp: odd count is illegal for DSP task device.\n"); + return -EINVAL; + } + + if (down_tasksem_interruptible(dev, &dev->read_sem)) + return -ERESTARTSYS; + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + have_devstate_lock = 1; + + if (fifo_empty(&dev->task->rcvdt.fifo)) { + long current_state = current->state; + DECLARE_WAITQUEUE(wait, current); + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&dev->read_wait_q, &wait); + if (fifo_empty(&dev->task->rcvdt.fifo)) { /* last check */ + devstate_unlock(dev); + have_devstate_lock = 0; + schedule(); + } + set_current_state(current_state); + remove_wait_queue(&dev->read_wait_q, &wait); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + goto up_out; + } + if (!have_devstate_lock) { + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + have_devstate_lock = 1; + } + if (fifo_empty(&dev->task->rcvdt.fifo)) /* should not occur */ + goto up_out; + } + + ret = copy_to_user_fm_fifo(buf, &dev->task->rcvdt.fifo, count); + +up_out: + if (have_devstate_lock) + devstate_unlock(dev); + up(&dev->read_sem); + return ret; +} + +static ssize_t dsp_task_read_bk_acv(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + struct taskdev *dev = taskdev[minor]; + struct rcvdt_bk_struct *rcvdt; + int have_devstate_lock = 0; + ssize_t ret = 0; + + if (count == 0) { + return 0; + } else if (count & 0x1) { + printk(KERN_ERR + "omapdsp: odd count is illegal for DSP task device.\n"); + return -EINVAL; + } else if ((int)buf & 0x1) { + printk(KERN_ERR + "omapdsp: buf should be word aligned for " + "dsp_task_read().\n"); + return -EINVAL; + } + + if (down_tasksem_interruptible(dev, &dev->read_sem)) + return -ERESTARTSYS; + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + have_devstate_lock = 1; + + if (ipblink_empty(&dev->task->rcvdt.bk.link)) { + long current_state; + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&dev->read_wait_q, &wait); + current_state = current->state; + set_current_state(TASK_INTERRUPTIBLE); + if (ipblink_empty(&dev->task->rcvdt.bk.link)) { /* last check */ + devstate_unlock(dev); + have_devstate_lock = 0; + schedule(); + } + set_current_state(current_state); + remove_wait_queue(&dev->read_wait_q, &wait); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + goto up_out; + } + if (!have_devstate_lock) { + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + have_devstate_lock = 1; + } + /* signal or 0-byte send from DSP */ + if (ipblink_empty(&dev->task->rcvdt.bk.link)) + goto up_out; + } + + rcvdt = &dev->task->rcvdt.bk; + /* copy from delayed IPBUF */ + if (sndtyp_pvt(dev->task->ttyp)) { + /* private */ + if (!ipblink_empty(&rcvdt->link)) { + struct ipbuf_p *ipbp = rcvdt->ipbuf_pvt_r; + unsigned char *base, *src; + size_t bkcnt; + + if (dsp_mem_enable(ipbp) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + base = MKVIRT(ipbp->ah, ipbp->al); + bkcnt = ((unsigned long)ipbp->c) * 2 - rcvdt->rp; + if (dsp_address_validate(base, bkcnt, + "task %s read buffer", + dev->task->name) < 0) { + ret = -ERESTARTSYS; + goto pv_out1; + } + if (dsp_mem_enable(base) < 0) { + ret = -ERESTARTSYS; + goto pv_out1; + } + src = base + rcvdt->rp; + if (bkcnt > count) { + if (copy_to_user_dsp(buf, src, count)) { + ret = -EFAULT; + goto pv_out2; + } + ret = count; + rcvdt->rp += count; + } else { + if (copy_to_user_dsp(buf, src, bkcnt)) { + ret = -EFAULT; + goto pv_out2; + } + ret = bkcnt; + spin_lock(&rcvdt->link.lock); + ipblink_del_pvt(&rcvdt->link); + spin_unlock(&rcvdt->link.lock); + release_ipbuf_pvt(ipbp); + rcvdt->rp = 0; + } +pv_out2: + dsp_mem_disable(src); +pv_out1: + dsp_mem_disable(ipbp); + } + } else { + /* global */ + if (dsp_mem_enable_ipbuf() < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + while (!ipblink_empty(&rcvdt->link)) { + unsigned char *src; + size_t bkcnt; + unsigned short bid = rcvdt->link.top; + struct ipbuf *ipbp = ipbuf[bid]; + + src = ipbp->d + rcvdt->rp; + bkcnt = ((unsigned long)ipbp->c) * 2 - rcvdt->rp; + if (bkcnt > count) { + if (copy_to_user_dsp(buf, src, count)) { + ret = -EFAULT; + goto gb_out; + } + ret += count; + rcvdt->rp += count; + break; + } else { + if (copy_to_user_dsp(buf, src, bkcnt)) { + ret = -EFAULT; + goto gb_out; + } + ret += bkcnt; + buf += bkcnt; + count -= bkcnt; + spin_lock(&rcvdt->link.lock); + ipblink_del_top(&rcvdt->link, ipbuf); + spin_unlock(&rcvdt->link.lock); + unuse_ipbuf(bid); + rcvdt->rp = 0; + } + } +gb_out: + dsp_mem_disable_ipbuf(); + } + +up_out: + if (have_devstate_lock) + devstate_unlock(dev); + up(&dev->read_sem); + return ret; +} + +static ssize_t dsp_task_read_wd_psv(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + struct taskdev *dev = taskdev[minor]; + struct mbcmd mb; + unsigned char tid; + int ret = 0; + + if (count == 0) { + return 0; + } else if (count & 0x1) { + printk(KERN_ERR + "omapdsp: odd count is illegal for DSP task device.\n"); + return -EINVAL; + } else { + /* force! */ + count = 2; + } + + if (down_tasksem_interruptible(dev, &dev->read_sem)) + return -ERESTARTSYS; + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + tid = dev->task->tid; + devstate_unlock(dev); + + mbcmd_set(mb, MBCMD(WDREQ), tid, 0); + dsp_mbcmd_send_and_wait(&mb, &dev->read_wait_q); + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + goto up_out; + } + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + if (fifo_empty(&dev->task->rcvdt.fifo)) /* should not occur */ + goto unlock_out; + + ret = copy_to_user_fm_fifo(buf, &dev->task->rcvdt.fifo, count); + +unlock_out: + devstate_unlock(dev); +up_out: + up(&dev->read_sem); + return ret; +} + +static ssize_t dsp_task_read_bk_psv(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + struct taskdev *dev = taskdev[minor]; + struct rcvdt_bk_struct *rcvdt; + struct mbcmd mb; + unsigned char tid; + int ret = 0; + + if (count == 0) { + return 0; + } else if (count & 0x1) { + printk(KERN_ERR + "omapdsp: odd count is illegal for DSP task device.\n"); + return -EINVAL; + } else if ((int)buf & 0x1) { + printk(KERN_ERR + "omapdsp: buf should be word aligned for " + "dsp_task_read().\n"); + return -EINVAL; + } + + if (down_tasksem_interruptible(dev, &dev->read_sem)) + return -ERESTARTSYS; + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + tid = dev->task->tid; + devstate_unlock(dev); + + mbcmd_set(mb, MBCMD(BKREQ), tid, count/2); + dsp_mbcmd_send_and_wait(&mb, &dev->read_wait_q); + + if (signal_pending(current)) { + ret = -ERESTARTSYS; + goto up_out; + } + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + rcvdt = &dev->task->rcvdt.bk; + /* signal or 0-byte send from DSP */ + if (ipblink_empty(&rcvdt->link)) + goto unlock_out; + + /* + * We will not receive more than requested count. + */ + if (sndtyp_pvt(dev->task->ttyp)) { + /* private */ + struct ipbuf_p *ipbp = rcvdt->ipbuf_pvt_r; + size_t rcvcnt; + void *src; + + if (dsp_mem_enable(ipbp) < 0) { + ret = -ERESTARTSYS; + goto unlock_out; + } + src = MKVIRT(ipbp->ah, ipbp->al); + rcvcnt = ((unsigned long)ipbp->c) * 2; + if (dsp_address_validate(src, rcvcnt, "task %s read buffer", + dev->task->name) < 0) { + ret = -ERESTARTSYS; + goto pv_out1; + } + if (dsp_mem_enable(src) < 0) { + ret = -ERESTARTSYS; + goto pv_out1; + } + if (count > rcvcnt) + count = rcvcnt; + if (copy_to_user_dsp(buf, src, count)) { + ret = -EFAULT; + goto pv_out2; + } + spin_lock(&rcvdt->link.lock); + ipblink_del_pvt(&rcvdt->link); + spin_unlock(&rcvdt->link.lock); + release_ipbuf_pvt(ipbp); + ret = count; +pv_out2: + dsp_mem_disable(src); +pv_out1: + dsp_mem_disable(ipbp); + } else { + /* global */ + unsigned short bid = rcvdt->link.top; + struct ipbuf *ipbp = ipbuf[bid]; + size_t rcvcnt; + + if (dsp_mem_enable_ipbuf() < 0) { + ret = -ERESTARTSYS; + goto unlock_out; + } + rcvcnt = ((unsigned long)ipbp->c) * 2; + if (count > rcvcnt) + count = rcvcnt; + if (copy_to_user_dsp(buf, ipbp->d, count)) { + ret = -EFAULT; + goto gb_out; + } + spin_lock(&rcvdt->link.lock); + ipblink_del_top(&rcvdt->link, ipbuf); + spin_unlock(&rcvdt->link.lock); + unuse_ipbuf(bid); + ret = count; +gb_out: + dsp_mem_disable_ipbuf(); + } + +unlock_out: + devstate_unlock(dev); +up_out: + up(&dev->read_sem); + return ret; +} + +static ssize_t dsp_task_write_wd(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + struct taskdev *dev = taskdev[minor]; + unsigned short wd; + int have_devstate_lock = 0; + int ret = 0; + + if (count == 0) { + return 0; + } else if (count & 0x1) { + printk(KERN_ERR + "omapdsp: odd count is illegal for DSP task device.\n"); + return -EINVAL; + } else { + /* force! */ + count = 2; + } + + if (down_tasksem_interruptible(dev, &dev->write_sem)) + return -ERESTARTSYS; + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + have_devstate_lock = 1; + + if (dev->task->wsz == 0) { + long current_state; + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&dev->write_wait_q, &wait); + current_state = current->state; + set_current_state(TASK_INTERRUPTIBLE); + if (dev->task->wsz == 0) { /* last check */ + devstate_unlock(dev); + have_devstate_lock = 0; + schedule(); + } + set_current_state(current_state); + remove_wait_queue(&dev->write_wait_q, &wait); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + goto up_out; + } + if (!have_devstate_lock) { + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + have_devstate_lock = 1; + } + if (dev->task->wsz == 0) /* should not occur */ + goto up_out; + } + + if (copy_from_user(&wd, buf, count)) { + ret = -EFAULT; + goto up_out; + } + + spin_lock(&dev->task->wsz_lock); + if (dsp_mbsend(MBCMD(WDSND), dev->task->tid, wd) < 0) { + spin_unlock(&dev->task->wsz_lock); + goto up_out; + } + ret = count; + if (rcvtyp_acv(dev->task->ttyp)) + dev->task->wsz = 0; + spin_unlock(&dev->task->wsz_lock); + +up_out: + if (have_devstate_lock) + devstate_unlock(dev); + up(&dev->write_sem); + return ret; +} + +static ssize_t dsp_task_write_bk(struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + struct taskdev *dev = taskdev[minor]; + int have_devstate_lock = 0; + int ret = 0; + + if (count == 0) { + return 0; + } else if (count & 0x1) { + printk(KERN_ERR + "omapdsp: odd count is illegal for DSP task device.\n"); + return -EINVAL; + } else if ((int)buf & 0x1) { + printk(KERN_ERR + "omapdsp: buf should be word aligned for " + "dsp_task_write().\n"); + return -EINVAL; + } + + if (down_tasksem_interruptible(dev, &dev->write_sem)) + return -ERESTARTSYS; + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + have_devstate_lock = 1; + + if (dev->task->wsz == 0) { + long current_state; + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&dev->write_wait_q, &wait); + current_state = current->state; + set_current_state(TASK_INTERRUPTIBLE); + if (dev->task->wsz == 0) { /* last check */ + devstate_unlock(dev); + have_devstate_lock = 0; + schedule(); + } + set_current_state(current_state); + remove_wait_queue(&dev->write_wait_q, &wait); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + goto up_out; + } + if (!have_devstate_lock) { + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + have_devstate_lock = 1; + } + if (dev->task->wsz == 0) /* should not occur */ + goto up_out; + } + + if (count > dev->task->wsz) + count = dev->task->wsz; + + if (rcvtyp_pvt(dev->task->ttyp)) { + /* private */ + struct ipbuf_p *ipbp = dev->task->ipbuf_pvt_w; + unsigned char *dst; + + if (dsp_mem_enable(ipbp) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + dst = MKVIRT(ipbp->ah, ipbp->al); + if (dsp_address_validate(dst, count, "task %s write buffer", + dev->task->name) < 0) { + ret = -ERESTARTSYS; + goto pv_out1; + } + if (dsp_mem_enable(dst) < 0) { + ret = -ERESTARTSYS; + goto pv_out1; + } + if (copy_from_user_dsp(dst, buf, count)) { + ret = -EFAULT; + goto pv_out2; + } + ipbp->c = count/2; + ipbp->s = dev->task->tid; + spin_lock(&dev->task->wsz_lock); + if (dsp_mbsend(MBCMD(BKSNDP), dev->task->tid, 0) == 0) { + if (rcvtyp_acv(dev->task->ttyp)) + dev->task->wsz = 0; + ret = count; + } + spin_unlock(&dev->task->wsz_lock); +pv_out2: + dsp_mem_disable(dst); +pv_out1: + dsp_mem_disable(ipbp); + } else { + /* global */ + struct ipbuf *ipbp; + unsigned short bid; + + if (dsp_mem_enable_ipbuf() < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + bid = get_free_ipbuf(dev->task->tid); + if (bid == OMAP_DSP_BID_NULL) + goto gb_out; + ipbp = ipbuf[bid]; + if (copy_from_user_dsp(ipbp->d, buf, count)) { + release_ipbuf(bid); + ret = -EFAULT; + goto gb_out; + } + ipbp->c = count/2; + ipbp->sa = dev->task->tid; + spin_lock(&dev->task->wsz_lock); + if (dsp_mbsend(MBCMD(BKSND), dev->task->tid, bid) == 0) { + if (rcvtyp_acv(dev->task->ttyp)) + dev->task->wsz = 0; + ret = count; + ipb_bsycnt_inc(&ipbcfg); + } else + release_ipbuf(bid); + spin_unlock(&dev->task->wsz_lock); +gb_out: + dsp_mem_disable_ipbuf(); + } + +up_out: + if (have_devstate_lock) + devstate_unlock(dev); + up(&dev->write_sem); + return ret; +} + +static unsigned int dsp_task_poll(struct file * file, poll_table * wait) +{ + unsigned int minor = MINOR(file->f_dentry->d_inode->i_rdev); + struct taskdev *dev = taskdev[minor]; + struct dsptask *task = dev->task; + unsigned int mask = 0; + + poll_wait(file, &dev->read_wait_q, wait); + poll_wait(file, &dev->write_wait_q, wait); + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) + return 0; + if (sndtyp_psv(task->ttyp) || + (sndtyp_wd(task->ttyp) && !fifo_empty(&task->rcvdt.fifo)) || + (sndtyp_bk(task->ttyp) && !ipblink_empty(&task->rcvdt.bk.link))) + mask |= POLLIN | POLLRDNORM; + if (task->wsz) + mask |= POLLOUT | POLLWRNORM; + devstate_unlock(dev); + + return mask; +} + +static int dsp_task_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct taskdev *dev = taskdev[minor]; + struct mbcmd mb; + unsigned char tid; + struct mb_exarg mbarg, *mbargp; + int mbargc; + unsigned short mbargv[1]; + int interactive; + int ret; + + /* LOCK / UNLOCK operations */ + switch (cmd) { + case OMAP_DSP_TASK_IOCTL_LOCK: + return taskdev_lock(dev); + case OMAP_DSP_TASK_IOCTL_UNLOCK: + return taskdev_unlock(dev); + } + + /* + * actually only interractive commands need to lock + * the semaphore, but here all commands do it for simplicity. + */ + if (down_tasksem_interruptible(dev, &dev->ioctl_sem)) + return -ERESTARTSYS; + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + + if ((cmd >= 0x0080) && (cmd < 0x0100)) { + /* + * 0x0080 - 0x00ff + * reserved for backward compatibility + * user-defined TCTL commands: no arg, non-interactive + */ + printk(KERN_WARNING "omapdsp: " + "TCTL commands in 0x0080 - 0x0100 are obsolete.\n" + "they won't be supported in the future.\n"); + mbargc = 0; + interactive = 0; + } else if (cmd < 0x8000) { + /* + * 0x0000 - 0x7fff (except 0x0080 - 0x00ff) + * system reserved TCTL commands + */ + switch (cmd) { + case OMAP_DSP_MBCMD_TCTL_TEN: + case OMAP_DSP_MBCMD_TCTL_TDIS: + mbargc = 0; + interactive = 0; + break; + default: + ret = -ENOIOCTLCMD; + goto unlock_out; + } + } + /* + * 0x8000 - 0xffff + * user-defined TCTL commands + */ + else if (cmd < 0x8100) { + /* 0x8000-0x80ff: no arg, non-interactive */ + mbargc = 0; + interactive = 0; + } else if (cmd < 0x8200) { + /* 0x8100-0x81ff: 1 arg, non-interactive */ + mbargc = 1; + mbargv[0] = arg & 0xffff; + interactive = 0; + } else if (cmd < 0x9000) { + /* 0x8200-0x8fff: reserved */ + ret = -ENOIOCTLCMD; + goto unlock_out; + } else if (cmd < 0x9100) { + /* 0x9000-0x90ff: no arg, interactive */ + mbargc = 0; + interactive = 1; + } else if (cmd < 0x9200) { + /* 0x9100-0x91ff: 1 arg, interactive */ + mbargc = 1; + mbargv[0] = arg & 0xffff; + interactive = 1; + } else if (cmd < 0x10000) { + /* 0x9200-0xffff: reserved */ + ret = -ENOIOCTLCMD; + goto unlock_out; + } else { + /* + * 0x10000 - + * non TCTL ioctls + */ + switch (cmd) { + case OMAP_DSP_TASK_IOCTL_BFLSH: + ret = dsp_task_flush_buf(dev->task); + break; + case OMAP_DSP_TASK_IOCTL_SETBSZ: + ret = dsp_task_set_fifosz(dev->task, arg); + break; + case OMAP_DSP_TASK_IOCTL_GETNAME: + ret = 0; + if (copy_to_user((void *)arg, dev->name, + strlen(dev->name) + 1)) + ret = -EFAULT; + break; + default: + ret = -ENOIOCTLCMD; + } + goto unlock_out; + } + + /* + * issue TCTL + */ + tid = dev->task->tid; + mbcmd_set(mb, MBCMD(TCTL), tid, cmd); + if (mbargc > 0) { + mbarg.argc = mbargc; + mbarg.tid = tid; + mbarg.argv = mbargv; + mbargp = &mbarg; + } else + mbargp = NULL; + + if (interactive) { + dev->task->tctl_stat = -ERESTARTSYS; + devstate_unlock(dev); + + dsp_mbcmd_send_and_wait_exarg(&mb, mbargp, &dev->ioctl_wait_q); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + goto up_out; + } + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) { + ret = -ERESTARTSYS; + goto up_out; + } + ret = dev->task->tctl_stat; + if (ret < 0) { + printk(KERN_ERR "omapdsp: TCTL not responding.\n"); + goto unlock_out; + } + } else { + dsp_mbcmd_send_exarg(&mb, mbargp); + ret = 0; + } + +unlock_out: + devstate_unlock(dev); +up_out: + up(&dev->ioctl_sem); + return ret; +} + +static void dsp_task_mmap_open(struct vm_area_struct *vma) +{ + struct taskdev *dev = (struct taskdev *)vma->vm_private_data; + struct dsptask *task; + size_t len = vma->vm_end - vma->vm_start; + + BUG_ON(!(dev->state & OMAP_DSP_DEVSTATE_ATTACHED)); + task = dev->task; + exmap_use(task->map_base, len); +} + +static void dsp_task_mmap_close(struct vm_area_struct *vma) +{ + struct taskdev *dev = (struct taskdev *)vma->vm_private_data; + struct dsptask *task; + size_t len = vma->vm_end - vma->vm_start; + + BUG_ON(!(dev->state & OMAP_DSP_DEVSTATE_ATTACHED)); + task = dev->task; + exmap_unuse(task->map_base, len); +} + +/** + * On demand page allocation is not allowed. The mapping area is defined by + * corresponding DSP tasks. + */ +static struct page *dsp_task_mmap_nopage(struct vm_area_struct *vma, + unsigned long address, int *type) +{ + return NOPAGE_SIGBUS; +} + +static struct vm_operations_struct dsp_task_vm_ops = { + .open = dsp_task_mmap_open, + .close = dsp_task_mmap_close, + .nopage = dsp_task_mmap_nopage, +}; + +static int dsp_task_mmap(struct file *filp, struct vm_area_struct *vma) +{ + void *tmp_vadr; + unsigned long tmp_padr, tmp_vmadr, off; + size_t req_len, tmp_len; + unsigned int minor = MINOR(filp->f_dentry->d_inode->i_rdev); + struct taskdev *dev = taskdev[minor]; + struct dsptask *task; + int ret = 0; + + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED) < 0) + return -ERESTARTSYS; + task = dev->task; + + /* + * Don't swap this area out + * Don't dump this area to a core file + */ + vma->vm_flags |= VM_RESERVED | VM_IO; + + /* Do not cache this area */ + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + req_len = vma->vm_end - vma->vm_start; + off = vma->vm_pgoff << PAGE_SHIFT; + tmp_vmadr = vma->vm_start; + tmp_vadr = task->map_base + off; + do { + tmp_padr = dsp_virt_to_phys(tmp_vadr, &tmp_len); + if (tmp_padr == 0) { + printk(KERN_ERR + "omapdsp: task %s: illegal address " + "for mmap: %p", task->name, tmp_vadr); + /* partial mapping will be cleared in upper layer */ + ret = -EINVAL; + goto unlock_out; + } + if (tmp_len > req_len) + tmp_len = req_len; + + printk(KERN_DEBUG + "omapdsp: mmap info: " + "vmadr = %08lx, padr = %08lx, len = %x\n", + tmp_vmadr, tmp_padr, tmp_len); + if (remap_pfn_range(vma, tmp_vmadr, tmp_padr >> PAGE_SHIFT, + tmp_len, vma->vm_page_prot) != 0) { + printk(KERN_ERR + "omapdsp: task %s: remap_page_range() failed.\n", + task->name); + /* partial mapping will be cleared in upper layer */ + ret = -EINVAL; + goto unlock_out; + } + + req_len -= tmp_len; + tmp_vmadr += tmp_len; + tmp_vadr += tmp_len; + } while (req_len); + + vma->vm_ops = &dsp_task_vm_ops; + vma->vm_private_data = dev; + exmap_use(task->map_base, vma->vm_end - vma->vm_start); + +unlock_out: + devstate_unlock(dev); + return ret; +} + +static int dsp_task_open(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct taskdev *dev; + int ret = 0; + + if ((minor >= TASKDEV_MAX) || ((dev = taskdev[minor]) == NULL)) + return -ENODEV; + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_NOTASK | + OMAP_DSP_DEVSTATE_ATTACHED) < 0) + return -ERESTARTSYS; +#ifndef CONFIG_OMAP_DSP_TASK_MULTIOPEN + if (dev->usecount > 0) { + ret = -EBUSY; + goto unlock_out; + } +#endif + + if (dev->state & OMAP_DSP_DEVSTATE_NOTASK) { + dev->state = OMAP_DSP_DEVSTATE_ADDREQ; + /* wake up twch daemon for tadd */ + dsp_twch_touch(); + devstate_unlock(dev); + if (devstate_lock(dev, OMAP_DSP_DEVSTATE_ATTACHED | + OMAP_DSP_DEVSTATE_ADDFAIL) < 0) { + spin_lock(&dev->state_lock); + if (dev->state & OMAP_DSP_DEVSTATE_ADDREQ) + dev->state = OMAP_DSP_DEVSTATE_NOTASK; + spin_unlock(&dev->state_lock); + return -ERESTARTSYS; + } + if (dev->state & OMAP_DSP_DEVSTATE_ADDFAIL) { + printk(KERN_ERR "omapdsp: task attach failed for %s!\n", + dev->name); + ret = -EBUSY; + dev->state = OMAP_DSP_DEVSTATE_NOTASK; + wake_up_interruptible_all(&dev->state_wait_q); + goto unlock_out; + } + } + + /* state_lock covers usecount, proc_list as well. */ + dev->usecount++; + proc_list_add(&dev->proc_list, current); + file->f_op = &dev->fops; + devstate_unlock(dev); + + return 0; + +unlock_out: + devstate_unlock(dev); + return ret; +} + +static int dsp_task_release(struct inode *inode, struct file *file) +{ + unsigned int minor = MINOR(inode->i_rdev); + struct taskdev *dev = taskdev[minor]; + + /* state_lock covers usecount, proc_list as well. */ + spin_lock(&dev->state_lock); + + /* state can be ATTACHED, KILLING or GARBAGE here. */ + switch (dev->state & OMAP_DSP_DEVSTATE_STATE_MASK) { + + case OMAP_DSP_DEVSTATE_KILLING: + dev->usecount--; + break; + + case OMAP_DSP_DEVSTATE_GARBAGE: + if(--dev->usecount == 0) { + dev->state = OMAP_DSP_DEVSTATE_NOTASK; + wake_up_interruptible_all(&dev->state_wait_q); + } + break; + + case OMAP_DSP_DEVSTATE_ATTACHED: + if (dev->lock_pid == current->pid) + taskdev_unlock(dev); + proc_list_del(&dev->proc_list, current); + if (--dev->usecount == 0) { + if (minor >= n_task) { /* dynamic task */ + dev->state = OMAP_DSP_DEVSTATE_DELREQ; + /* wake up twch daemon for tdel */ + dsp_twch_touch(); + } + } + break; + + } + + spin_unlock(&dev->state_lock); + return 0; +} + +/* + * mkdev / rmdev + */ +int dsp_mkdev(char *name) +{ + struct taskdev *dev; + int status; + unsigned char minor; + + if (!dsp_is_ready()) { + printk(KERN_ERR "omapdsp: dsp has not been configured.\n"); + return -EINVAL; + } + for (minor = n_task; minor < TASKDEV_MAX; minor++) { + if (taskdev[minor] == NULL) + goto do_make; + } + printk(KERN_ERR "omapdsp: Too many task devices.\n"); + return -EBUSY; + +do_make: + if ((dev = kmalloc(sizeof(struct taskdev), GFP_KERNEL)) == NULL) + return -ENOMEM; + memset(dev, 0, sizeof(struct taskdev)); + if ((status = taskdev_init(dev, name, minor)) < 0) { + kfree(dev); + return status; + } + return minor; +} + +int dsp_rmdev(char *name) +{ + unsigned char minor; + int ret; + + if (!dsp_is_ready()) { + printk(KERN_ERR "omapdsp: dsp has not been configured.\n"); + return -EINVAL; + } + for (minor = n_task; minor < TASKDEV_MAX; minor++) { + if (taskdev[minor] && !strcmp(taskdev[minor]->name, name)) { + if ((ret = dsp_rmdev_minor(minor)) < 0) + return ret; + return minor; + } + } + return -EINVAL; +} + +static int dsp_rmdev_minor(unsigned char minor) +{ + struct taskdev *dev = taskdev[minor]; + + spin_lock(&dev->state_lock); + + switch (dev->state & OMAP_DSP_DEVSTATE_STATE_MASK) { + + case OMAP_DSP_DEVSTATE_NOTASK: + /* fine */ + break; + + case OMAP_DSP_DEVSTATE_ATTACHED: + /* task is working. kill it. */ + dev->state = OMAP_DSP_DEVSTATE_KILLING; + proclist_send_sigbus(&dev->proc_list); + spin_unlock(&dev->state_lock); + dsp_tdel_bh(minor, OMAP_DSP_MBCMD_TDEL_KILL); + goto invalidate; + + case OMAP_DSP_DEVSTATE_ADDREQ: + /* open() is waiting. drain it. */ + dev->state = OMAP_DSP_DEVSTATE_ADDFAIL; + wake_up_interruptible_all(&dev->state_wait_q); + break; + + case OMAP_DSP_DEVSTATE_DELREQ: + /* nobody is waiting. */ + dev->state = OMAP_DSP_DEVSTATE_NOTASK; + wake_up_interruptible_all(&dev->state_wait_q); + break; + + case OMAP_DSP_DEVSTATE_ADDING: + case OMAP_DSP_DEVSTATE_DELING: + case OMAP_DSP_DEVSTATE_KILLING: + case OMAP_DSP_DEVSTATE_GARBAGE: + case OMAP_DSP_DEVSTATE_ADDFAIL: + /* transient state. wait for a moment. */ + break; + + } + + spin_unlock(&dev->state_lock); + +invalidate: + /* wait for some time and hope the state is settled */ + devstate_lock_timeout(dev, OMAP_DSP_DEVSTATE_NOTASK, HZ); + if (!(dev->state & OMAP_DSP_DEVSTATE_NOTASK)) { + printk(KERN_WARNING + "omapdsp: illegal device state (%s) on rmdev %s.\n", + devstate_name(dev->state), dev->name); + } + dev->state = OMAP_DSP_DEVSTATE_INVALID; + devstate_unlock(dev); + + taskdev_delete(minor); + kfree(dev); + + return 0; +} + +struct file_operations dsp_task_fops = { + .owner = THIS_MODULE, + .poll = dsp_task_poll, + .ioctl = dsp_task_ioctl, + .open = dsp_task_open, + .release = dsp_task_release, +}; + +static void dsptask_dev_release(struct device *dev) +{ +} + +static int taskdev_init(struct taskdev *dev, char *name, unsigned char minor) +{ + struct class_device *cdev; + + taskdev[minor] = dev; + + INIT_LIST_HEAD(&dev->proc_list); + init_waitqueue_head(&dev->read_wait_q); + init_waitqueue_head(&dev->write_wait_q); + init_waitqueue_head(&dev->ioctl_wait_q); + init_MUTEX(&dev->read_sem); + init_MUTEX(&dev->write_sem); + init_MUTEX(&dev->ioctl_sem); + init_MUTEX(&dev->lock_sem); + dev->lock_pid = 0; + + strncpy(dev->name, name, OMAP_DSP_TNM_LEN); + dev->name[OMAP_DSP_TNM_LEN-1] = '\0'; + dev->state = (minor < n_task) ? OMAP_DSP_DEVSTATE_ATTACHED : + OMAP_DSP_DEVSTATE_NOTASK; + dev->usecount = 0; + memcpy(&dev->fops, &dsp_task_fops, sizeof(struct file_operations)); + + dev->dev.parent = &dsp_device.dev; + dev->dev.bus = &dsptask_bus; + sprintf(dev->dev.bus_id, "dsptask%d", minor); + dev->dev.release = dsptask_dev_release; + device_register(&dev->dev); + device_create_file(&dev->dev, &dev_attr_devname); + device_create_file(&dev->dev, &dev_attr_devstate); + device_create_file(&dev->dev, &dev_attr_proc_list); + cdev = class_device_create(dsp_task_class, NULL, + MKDEV(OMAP_DSP_TASK_MAJOR, minor), + NULL, "dsptask%d", minor); + + init_waitqueue_head(&dev->state_wait_q); + spin_lock_init(&dev->state_lock); + + return 0; +} + +static void taskdev_delete(unsigned char minor) +{ + struct taskdev *dev = taskdev[minor]; + + if (!dev) + return; + device_remove_file(&dev->dev, &dev_attr_devname); + device_remove_file(&dev->dev, &dev_attr_devstate); + device_remove_file(&dev->dev, &dev_attr_proc_list); + class_device_destroy(dsp_task_class, MKDEV(OMAP_DSP_TASK_MAJOR, minor)); + device_unregister(&dev->dev); + proc_list_flush(&dev->proc_list); + taskdev[minor] = NULL; +} + +static void taskdev_attach_task(struct taskdev *dev, struct dsptask *task) +{ + unsigned short ttyp = task->ttyp; + + dev->task = task; + task->dev = dev; + dev->fops.read = + sndtyp_acv(ttyp) ? + sndtyp_wd(ttyp) ? dsp_task_read_wd_acv: + /* sndtyp_bk */ dsp_task_read_bk_acv: + /* sndtyp_psv */ + sndtyp_wd(ttyp) ? dsp_task_read_wd_psv: + /* sndtyp_bk */ dsp_task_read_bk_psv; + dev->fops.write = + rcvtyp_wd(ttyp) ? dsp_task_write_wd: + /* rcvbyp_bk */ dsp_task_write_bk; + if (task->map_length) + dev->fops.mmap = dsp_task_mmap; + + device_create_file(&dev->dev, &dev_attr_taskname); + device_create_file(&dev->dev, &dev_attr_ttyp); + if (sndtyp_wd(ttyp)) { + device_create_file(&dev->dev, &dev_attr_fifosz); + device_create_file(&dev->dev, &dev_attr_fifocnt); + } else + device_create_file(&dev->dev, &dev_attr_ipblink); + device_create_file(&dev->dev, &dev_attr_wsz); + if (task->map_length) + device_create_file(&dev->dev, &dev_attr_mmap); +} + +static void taskdev_detach_task(struct taskdev *dev) +{ + unsigned short ttyp = dev->task->ttyp; + + device_remove_file(&dev->dev, &dev_attr_taskname); + device_remove_file(&dev->dev, &dev_attr_ttyp); + if (sndtyp_wd(ttyp)) { + device_remove_file(&dev->dev, &dev_attr_fifosz); + device_remove_file(&dev->dev, &dev_attr_fifocnt); + } else + device_remove_file(&dev->dev, &dev_attr_ipblink); + device_remove_file(&dev->dev, &dev_attr_wsz); + if (dev->task->map_length) + device_remove_file(&dev->dev, &dev_attr_mmap); + + if (dev->task) { + dev->task = NULL; + dev->fops.read = NULL; + dev->fops.write = NULL; + printk(KERN_INFO "omapdsp: taskdev %s disabled.\n", dev->name); + } +} + +/* + * tadd / tdel / tkill + */ +int dsp_tadd(unsigned char minor, unsigned long adr) +{ + struct taskdev *dev; + struct dsptask *task; + struct mbcmd mb; + struct mb_exarg arg; + unsigned char tid, tid_response; + unsigned short argv[2]; + int ret = minor; + + if ((minor >= TASKDEV_MAX) || ((dev = taskdev[minor]) == NULL)) { + printk(KERN_ERR + "omapdsp: no task device with minor %d\n", minor); + return -EINVAL; + } + + spin_lock(&dev->state_lock); + if (!(dev->state & OMAP_DSP_DEVSTATE_ADDREQ)) { + printk(KERN_ERR + "omapdsp: taskdev %s is not requesting for tadd. " + "(state is %s)\n", dev->name, devstate_name(dev->state)); + spin_unlock(&dev->state_lock); + return -EINVAL; + } + dev->state = OMAP_DSP_DEVSTATE_ADDING; + spin_unlock(&dev->state_lock); + + if (adr == OMAP_DSP_TADD_ABORTADR) { + /* aborting tadd intentionally */ + printk(KERN_INFO "omapdsp: tadd address is ABORTADR.\n"); + goto fail_out; + } + if (adr >= DSPSPACE_SIZE) { + printk(KERN_ERR + "omapdsp: illegal address 0x%08lx for tadd\n", adr); + ret = -EINVAL; + goto fail_out; + } + + adr >>= 1; /* word address */ + argv[0] = adr >> 16; /* addrh */ + argv[1] = adr & 0xffff; /* addrl */ + + if (down_interruptible(&cfg_sem)) { + ret = -ERESTARTSYS; + goto fail_out; + } + cfg_tid = OMAP_DSP_TID_ANON; + cfg_cmd = MBCMD(TADD); + mbcmd_set(mb, MBCMD(TADD), 0, 0); + arg.tid = OMAP_DSP_TID_ANON; + arg.argc = 2; + arg.argv = argv; + + dsp_mem_sync_inc(); + dsp_mbcmd_send_and_wait_exarg(&mb, &arg, &cfg_wait_q); + + tid = cfg_tid; + cfg_tid = OMAP_DSP_TID_ANON; + cfg_cmd = 0; + up(&cfg_sem); + + if (tid == OMAP_DSP_TID_ANON) { + printk(KERN_ERR "omapdsp: tadd failed!\n"); + ret = -EINVAL; + goto fail_out; + } + if ((tid < n_task) || dsptask[tid]) { + printk(KERN_ERR "omapdsp: illegal tid (%d)!\n", tid); + ret = -EINVAL; + goto fail_out; + } + if ((task = kmalloc(sizeof(struct dsptask), GFP_KERNEL)) == NULL) { + ret = -ENOMEM; + goto del_out; + } + memset(task, 0, sizeof(struct dsptask)); + + if ((ret = dsp_task_config(task, tid)) < 0) + goto free_out; + taskdev_attach_task(dev, task); + + if (strcmp(dev->name, task->name)) { + printk(KERN_ERR + "omapdsp: task name (%s) doesn't match with " + "device name (%s).\n", task->name, dev->name); + ret = -EINVAL; + goto free_out; + } + + dsp_task_init(task); + printk(KERN_INFO "omapdsp: taskdev %s enabled.\n", dev->name); + dev->state = OMAP_DSP_DEVSTATE_ATTACHED; + wake_up_interruptible_all(&dev->state_wait_q); + return minor; + +free_out: + kfree(task); + +del_out: + printk(KERN_ERR "omapdsp: deleting the task...\n"); + + dev->state = OMAP_DSP_DEVSTATE_DELING; + + if (down_interruptible(&cfg_sem)) { + printk(KERN_ERR "omapdsp: aborting tdel process. " + "DSP side could be corrupted.\n"); + goto fail_out; + } + cfg_tid = OMAP_DSP_TID_ANON; + cfg_cmd = MBCMD(TDEL); + mbcmd_set(mb, MBCMD(TDEL), tid, OMAP_DSP_MBCMD_TDEL_KILL); + dsp_mbcmd_send_and_wait(&mb, &cfg_wait_q); + tid_response = cfg_tid; + cfg_tid = OMAP_DSP_TID_ANON; + cfg_cmd = 0; + up(&cfg_sem); + + if (tid_response != tid) + printk(KERN_ERR "omapdsp: tdel failed. " + "DSP side could be corrupted.\n"); + +fail_out: + dev->state = OMAP_DSP_DEVSTATE_ADDFAIL; + wake_up_interruptible_all(&dev->state_wait_q); + return ret; +} + +int dsp_tdel(unsigned char minor) +{ + struct taskdev *dev; + + if ((minor >= TASKDEV_MAX) || ((dev = taskdev[minor]) == NULL)) { + printk(KERN_ERR + "omapdsp: no task device with minor %d\n", minor); + return -EINVAL; + } + spin_lock(&dev->state_lock); + if (!(dev->state & OMAP_DSP_DEVSTATE_DELREQ)) { + printk(KERN_ERR + "omapdsp: taskdev %s is not requesting for tdel. " + "(state is %s)\n", dev->name, devstate_name(dev->state)); + spin_unlock(&dev->state_lock); + return -EINVAL; + } + dev->state = OMAP_DSP_DEVSTATE_DELING; + spin_unlock(&dev->state_lock); + + return dsp_tdel_bh(minor, OMAP_DSP_MBCMD_TDEL_SAFE); +} + +int dsp_tkill(unsigned char minor) +{ + struct taskdev *dev; + + if ((minor >= TASKDEV_MAX) || ((dev = taskdev[minor]) == NULL)) { + printk(KERN_ERR + "omapdsp: no task device with minor %d\n", minor); + return -EINVAL; + } + spin_lock(&dev->state_lock); + if (!(dev->state & OMAP_DSP_DEVSTATE_ATTACHED)) { + printk(KERN_ERR + "omapdsp: task has not been attached for taskdev %s\n", + dev->name); + spin_unlock(&dev->state_lock); + return -EINVAL; + } + dev->state = OMAP_DSP_DEVSTATE_KILLING; + proclist_send_sigbus(&dev->proc_list); + spin_unlock(&dev->state_lock); + + return dsp_tdel_bh(minor, OMAP_DSP_MBCMD_TDEL_KILL); +} + +static int dsp_tdel_bh(unsigned char minor, unsigned short type) +{ + struct taskdev *dev = taskdev[minor]; + struct dsptask *task; + struct mbcmd mb; + unsigned char tid, tid_response; + int ret = minor; + + task = dev->task; + tid = task->tid; + if (down_interruptible(&cfg_sem)) { + if (type == OMAP_DSP_MBCMD_TDEL_SAFE) { + dev->state = OMAP_DSP_DEVSTATE_DELREQ; + return -ERESTARTSYS; + } else { + tid_response = OMAP_DSP_TID_ANON; + ret = -ERESTARTSYS; + goto detach_out; + } + } + cfg_tid = OMAP_DSP_TID_ANON; + cfg_cmd = MBCMD(TDEL); + mbcmd_set(mb, MBCMD(TDEL), tid, type); + dsp_mbcmd_send_and_wait(&mb, &cfg_wait_q); + tid_response = cfg_tid; + cfg_tid = OMAP_DSP_TID_ANON; + cfg_cmd = 0; + up(&cfg_sem); + +detach_out: + taskdev_detach_task(dev); + dsp_task_unconfig(task); + kfree(task); + + if (tid_response != tid) { + printk(KERN_ERR "omapdsp: %s failed!\n", + (type == OMAP_DSP_MBCMD_TDEL_SAFE) ? "tdel" : "tkill"); + ret = -EINVAL; + } + spin_lock(&dev->state_lock); + dev->state = (dev->usecount > 0) ? OMAP_DSP_DEVSTATE_GARBAGE : + OMAP_DSP_DEVSTATE_NOTASK; + wake_up_interruptible_all(&dev->state_wait_q); + spin_unlock(&dev->state_lock); + + return ret; +} + +/* + * state inquiry + */ +long taskdev_state_stale(unsigned char minor) +{ + if (taskdev[minor]) { + long state = taskdev[minor]->state; + taskdev[minor]->state |= OMAP_DSP_DEVSTATE_STALE; + return state; + } else + return OMAP_DSP_DEVSTATE_NOTASK; +} + +/* + * functions called from mailbox1 interrupt routine + */ +void mbx1_wdsnd(struct mbcmd *mb) +{ + unsigned char tid = mb->cmd_l; + struct dsptask *task = dsptask[tid]; + + if ((tid >= TASKDEV_MAX) || (task == NULL)) { + printk(KERN_ERR "mbx: WDSND with illegal tid! %d\n", tid); + return; + } + if (sndtyp_bk(task->ttyp)) { + printk(KERN_ERR + "mbx: WDSND from block sending task! (task%d)\n", tid); + return; + } + if (sndtyp_psv(task->ttyp) && + !waitqueue_active(&task->dev->read_wait_q)) { + printk(KERN_WARNING + "mbx: WDSND from passive sending task (task%d) " + "without request!\n", tid); + return; + } + + write_word_to_fifo(&task->rcvdt.fifo, mb->data); + wake_up_interruptible(&task->dev->read_wait_q); +} + +void mbx1_wdreq(struct mbcmd *mb) +{ + unsigned char tid = mb->cmd_l; + struct dsptask *task = dsptask[tid]; + + if ((tid >= TASKDEV_MAX) || (task == NULL)) { + printk(KERN_ERR "mbx: WDREQ with illegal tid! %d\n", tid); + return; + } + if (rcvtyp_psv(task->ttyp)) { + printk(KERN_ERR + "mbx: WDREQ from passive receiving task! (task%d)\n", + tid); + return; + } + + spin_lock(&task->wsz_lock); + task->wsz = 2; + spin_unlock(&task->wsz_lock); + wake_up_interruptible(&task->dev->write_wait_q); +} + +void mbx1_bksnd(struct mbcmd *mb) +{ + unsigned char tid = mb->cmd_l; + unsigned short bid = mb->data; + struct dsptask *task = dsptask[tid]; + unsigned short cnt; + + if (bid >= ipbcfg.ln) { + printk(KERN_ERR "mbx: BKSND with illegal bid! %d\n", bid); + return; + } + ipb_bsycnt_dec(&ipbcfg); + if ((tid >= TASKDEV_MAX) || (task == NULL)) { + printk(KERN_ERR "mbx: BKSND with illegal tid! %d\n", tid); + goto unuse_ipbuf_out; + } + if (sndtyp_wd(task->ttyp)) { + printk(KERN_ERR + "mbx: BKSND from word sending task! (task%d)\n", tid); + goto unuse_ipbuf_out; + } + if (sndtyp_pvt(task->ttyp)) { + printk(KERN_ERR + "mbx: BKSND from private sending task! (task%d)\n", tid); + goto unuse_ipbuf_out; + } + if (sync_with_dsp(&ipbuf[bid]->sd, tid, 10) < 0) { + printk(KERN_ERR "mbx: BKSND - IPBUF sync failed!\n"); + return; + } + + /* should be done in DSP, but just in case. */ + ipbuf[bid]->next = OMAP_DSP_BID_NULL; + + cnt = ipbuf[bid]->c; + if (cnt > ipbcfg.lsz) { + printk(KERN_ERR "mbx: BKSND cnt(%d) > ipbuf line size(%d)!\n", + cnt, ipbcfg.lsz); + goto unuse_ipbuf_out; + } + + if (cnt == 0) { + /* 0-byte send from DSP */ + unuse_ipbuf_nowait(bid); + goto done; + } + spin_lock(&task->rcvdt.bk.link.lock); + ipblink_add_tail(&task->rcvdt.bk.link, bid, ipbuf); + spin_unlock(&task->rcvdt.bk.link.lock); + /* we keep coming bid and return alternative line to DSP. */ + balance_ipbuf(); + +done: + wake_up_interruptible(&task->dev->read_wait_q); + return; + +unuse_ipbuf_out: + unuse_ipbuf_nowait(bid); + return; +} + +void mbx1_bkreq(struct mbcmd *mb) +{ + unsigned char tid = mb->cmd_l; + unsigned short cnt = mb->data; + struct dsptask *task = dsptask[tid]; + + if ((tid >= TASKDEV_MAX) || (task == NULL)) { + printk(KERN_ERR "mbx: BKREQ with illegal tid! %d\n", tid); + return; + } + if (rcvtyp_wd(task->ttyp)) { + printk(KERN_ERR + "mbx: BKREQ from word receiving task! (task%d)\n", tid); + return; + } + if (rcvtyp_pvt(task->ttyp)) { + printk(KERN_ERR + "mbx: BKREQ from private receiving task! (task%d)\n", + tid); + return; + } + if (rcvtyp_psv(task->ttyp)) { + printk(KERN_ERR + "mbx: BKREQ from passive receiving task! (task%d)\n", + tid); + return; + } + + spin_lock(&task->wsz_lock); + task->wsz = cnt*2; + spin_unlock(&task->wsz_lock); + wake_up_interruptible(&task->dev->write_wait_q); +} + +void mbx1_bkyld(struct mbcmd *mb) +{ + unsigned short bid = mb->data; + + if (bid >= ipbcfg.ln) { + printk(KERN_ERR "mbx: BKYLD with illegal bid! %d\n", bid); + return; + } + + /* should be done in DSP, but just in case. */ + ipbuf[bid]->next = OMAP_DSP_BID_NULL; + + /* we don't need to sync with DSP */ + ipb_bsycnt_dec(&ipbcfg); + release_ipbuf(bid); +} + +void mbx1_bksndp(struct mbcmd *mb) +{ + unsigned char tid = mb->cmd_l; + struct dsptask *task = dsptask[tid]; + struct rcvdt_bk_struct *rcvdt = &task->rcvdt.bk; + struct ipbuf_p *ipbp = rcvdt->ipbuf_pvt_r; + + if ((tid >= TASKDEV_MAX) || (task == NULL)) { + printk(KERN_ERR "mbx: BKSNDP with illegal tid! %d\n", tid); + return; + } + if (sndtyp_wd(task->ttyp)) { + printk(KERN_ERR + "mbx: BKSNDP from word sending task! (task%d)\n", tid); + return; + } + if (sndtyp_gbl(task->ttyp)) { + printk(KERN_ERR + "mbx: BKSNDP from non-private sending task! (task%d)\n", + tid); + return; + } + + /* + * we should not have delayed block at this point + * because read() routine releases the lock of the buffer and + * until then DSP can't send next data. + */ + + if (sync_with_dsp(&ipbp->s, tid, 10) < 0) { + printk(KERN_ERR "mbx: BKSNDP - IPBUF sync failed!\n"); + return; + } + printk(KERN_DEBUG "mbx: ipbuf_pvt_r->a = 0x%08lx\n", + MKLONG(ipbp->ah, ipbp->al)); + spin_lock(&rcvdt->link.lock); + ipblink_add_pvt(&rcvdt->link); + spin_unlock(&rcvdt->link.lock); + wake_up_interruptible(&task->dev->read_wait_q); +} + +void mbx1_bkreqp(struct mbcmd *mb) +{ + unsigned char tid = mb->cmd_l; + struct dsptask *task = dsptask[tid]; + struct ipbuf_p *ipbp = task->ipbuf_pvt_w; + + if ((tid >= TASKDEV_MAX) || (task == NULL)) { + printk(KERN_ERR "mbx: BKREQP with illegal tid! %d\n", tid); + return; + } + if (rcvtyp_wd(task->ttyp)) { + printk(KERN_ERR + "mbx: BKREQP from word receiving task! (task%d)\n", tid); + return; + } + if (rcvtyp_gbl(task->ttyp)) { + printk(KERN_ERR + "mbx: BKREQP from non-private receiving task! (task%d)\n", tid); + return; + } + if (rcvtyp_psv(task->ttyp)) { + printk(KERN_ERR + "mbx: BKREQP from passive receiving task! (task%d)\n", tid); + return; + } + + if (sync_with_dsp(&ipbp->s, OMAP_DSP_TID_FREE, 10) < 0) { + printk(KERN_ERR "mbx: BKREQP - IPBUF sync failed!\n"); + return; + } + printk(KERN_DEBUG "mbx: ipbuf_pvt_w->a = 0x%08lx\n", + MKLONG(ipbp->ah, ipbp->al)); + spin_lock(&task->wsz_lock); + task->wsz = ipbp->c*2; + spin_unlock(&task->wsz_lock); + wake_up_interruptible(&task->dev->write_wait_q); +} + +void mbx1_tctl(struct mbcmd *mb) +{ + unsigned char tid = mb->cmd_l; + struct dsptask *task = dsptask[tid]; + + if ((tid >= TASKDEV_MAX) || (task == NULL)) { + printk(KERN_ERR "mbx: TCTL with illegal tid! %d\n", tid); + return; + } + + if (!waitqueue_active(&task->dev->ioctl_wait_q)) { + printk(KERN_WARNING "mbx: unexpected TCTL from DSP!\n"); + return; + } + + task->tctl_stat = mb->data; + wake_up_interruptible(&task->dev->ioctl_wait_q); +} + +void mbx1_tcfg(struct mbcmd *mb) +{ + unsigned char tid = mb->cmd_l; + struct dsptask *task = dsptask[tid]; + unsigned short *tnm; + volatile unsigned short *buf; + int i; + + if ((tid >= TASKDEV_MAX) || (task == NULL)) { + printk(KERN_ERR "mbx: TCFG with illegal tid! %d\n", tid); + return; + } + if ((task->state != TASK_STATE_CFGREQ) || (cfg_cmd != MBCMD(TCFG))) { + printk(KERN_WARNING "mbx: unexpected TCFG from DSP!\n"); + return; + } + + if (sync_with_dsp(&ipbuf_sys_da->s, tid, 10) < 0) { + printk(KERN_ERR "mbx: TCFG - IPBUF sync failed!\n"); + goto out; + } + + /* + * read configuration data on system IPBUF + */ + buf = ipbuf_sys_da->d; + task->ttyp = buf[0]; + task->rcvdt.bk.ipbuf_pvt_r = MKVIRT(buf[1], buf[2]); + task->ipbuf_pvt_w = MKVIRT(buf[3], buf[4]); + task->map_base = MKVIRT(buf[5], buf[6]); + task->map_length = MKLONG(buf[7], buf[8]) << 1; /* word -> byte */ + tnm = MKVIRT(buf[9], buf[10]); + release_ipbuf_pvt(ipbuf_sys_da); + + /* + * copy task name string + */ + if (dsp_address_validate(tnm, OMAP_DSP_TNM_LEN, "task name buffer") <0) { + task->name[0] = '\0'; + goto out; + } + + for (i = 0; i < OMAP_DSP_TNM_LEN-1; i++) { + /* avoiding byte access */ + unsigned short tmp = tnm[i]; + task->name[i] = tmp & 0x00ff; + if (!tmp) + break; + } + task->name[OMAP_DSP_TNM_LEN-1] = '\0'; + + task->state = TASK_STATE_READY; +out: + wake_up_interruptible(&cfg_wait_q); +} + +void mbx1_tadd(struct mbcmd *mb) +{ + unsigned char tid = mb->cmd_l; + + if ((!waitqueue_active(&cfg_wait_q)) || (cfg_cmd != MBCMD(TADD))) { + printk(KERN_WARNING "mbx: unexpected TADD from DSP!\n"); + return; + } + cfg_tid = tid; + wake_up_interruptible(&cfg_wait_q); +} + +void mbx1_tdel(struct mbcmd *mb) +{ + unsigned char tid = mb->cmd_l; + + if ((!waitqueue_active(&cfg_wait_q)) || (cfg_cmd != MBCMD(TDEL))) { + printk(KERN_WARNING "mbx: unexpected TDEL from DSP!\n"); + return; + } + cfg_tid = tid; + wake_up_interruptible(&cfg_wait_q); +} + +void mbx1_err_fatal(unsigned char tid) +{ + struct dsptask *task = dsptask[tid]; + + if ((tid >= TASKDEV_MAX) || (task == NULL)) { + printk(KERN_ERR "mbx: FATAL ERR with illegal tid! %d\n", tid); + return; + } + + spin_lock(&task->dev->state_lock); + proclist_send_sigbus(&task->dev->proc_list); + spin_unlock(&task->dev->state_lock); +} + +static short *dbg_buf; +static unsigned short dbg_buf_sz, dbg_line_sz; +static int dbg_rp; + +int dsp_dbg_config(short *buf, unsigned short sz, unsigned short lsz) +{ +#ifdef OLD_BINARY_SUPPORT + if ((mbx_revision == MBREV_3_0) || (mbx_revision == MBREV_3_2)) { + dbg_buf = NULL; + dbg_buf_sz = 0; + dbg_line_sz = 0; + dbg_rp = 0; + return 0; + } +#endif + + if (dsp_address_validate(buf, sz, "debug buffer") < 0) + return -1; + + if (lsz > sz) { + printk(KERN_ERR + "omapdsp: dbg_buf lsz (%d) is greater than its " + "buffer size (%d)\n", lsz, sz); + return -1; + } + + dbg_buf = buf; + dbg_buf_sz = sz; + dbg_line_sz = lsz; + dbg_rp = 0; + + return 0; +} + +void dsp_dbg_stop(void) +{ + dbg_buf = NULL; +} + +#ifdef OLD_BINARY_SUPPORT +static void mbx1_dbg_old(struct mbcmd *mb); +#endif + +void mbx1_dbg(struct mbcmd *mb) +{ + unsigned char tid = mb->cmd_l; + int cnt = mb->data; + char s[80], *s_end = &s[79], *p; + unsigned short *src; + int i; + +#ifdef OLD_BINARY_SUPPORT + if ((mbx_revision == MBREV_3_0) || (mbx_revision == MBREV_3_2)) { + mbx1_dbg_old(mb); + return; + } +#endif + + if (((tid >= TASKDEV_MAX) || (dsptask[tid] == NULL)) && + (tid != OMAP_DSP_TID_ANON)) { + printk(KERN_ERR "mbx: DBG with illegal tid! %d\n", tid); + return; + } + if (dbg_buf == NULL) { + printk(KERN_ERR "mbx: DBG command received, but " + "dbg_buf has not been configured yet.\n"); + return; + } + + if (dsp_mem_enable(dbg_buf) < 0) + return; + + src = &dbg_buf[dbg_rp]; + p = s; + for (i = 0; i < cnt; i++) { + unsigned short tmp; + /* + * Be carefull that dbg_buf should not be read with + * 1-byte access since it might be placed in DARAM/SARAM + * and it can cause unexpected byteswap. + * For example, + * *(p++) = *(src++) & 0xff; + * causes 1-byte access! + */ + tmp = *src++; + *(p++) = tmp & 0xff; + if (*(p-1) == '\n') { + *p = '\0'; + printk(KERN_INFO "%s", s); + p = s; + continue; + } + if (p == s_end) { + *p = '\0'; + printk(KERN_INFO "%s\n", s); + p = s; + continue; + } + } + if (p > s) { + *p = '\0'; + printk(KERN_INFO "%s\n", s); + } + if ((dbg_rp += cnt + 1) > dbg_buf_sz - dbg_line_sz) + dbg_rp = 0; + + dsp_mem_disable(dbg_buf); +} + +#ifdef OLD_BINARY_SUPPORT +static void mbx1_dbg_old(struct mbcmd *mb) +{ + unsigned char tid = mb->cmd_l; + char s[80], *s_end = &s[79], *p; + unsigned short *src; + volatile unsigned short *buf; + int cnt; + int i; + + if (((tid >= TASKDEV_MAX) || (dsptask[tid] == NULL)) && + (tid != OMAP_DSP_TID_ANON)) { + printk(KERN_ERR "mbx: DBG with illegal tid! %d\n", tid); + return; + } + if (sync_with_dsp(&ipbuf_sys_da->s, tid, 10) < 0) { + printk(KERN_ERR "mbx: DBG - IPBUF sync failed!\n"); + return; + } + buf = ipbuf_sys_da->d; + cnt = buf[0]; + src = MKVIRT(buf[1], buf[2]); + if (dsp_address_validate(src, cnt, "dbg buffer") < 0) + return; + + if (dsp_mem_enable(src) < 0) + return; + + p = s; + for (i = 0; i < cnt; i++) { + unsigned short tmp; + /* + * Be carefull that ipbuf should not be read with + * 1-byte access since it might be placed in DARAM/SARAM + * and it can cause unexpected byteswap. + * For example, + * *(p++) = *(src++) & 0xff; + * causes 1-byte access! + */ + tmp = *src++; + *(p++) = tmp & 0xff; + if (*(p-1) == '\n') { + *p = '\0'; + printk(KERN_INFO "%s", s); + p = s; + continue; + } + if (p == s_end) { + *p = '\0'; + printk(KERN_INFO "%s\n", s); + p = s; + continue; + } + } + if (p > s) { + *p = '\0'; + printk(KERN_INFO "%s\n", s); + } + + release_ipbuf_pvt(ipbuf_sys_da); + dsp_mem_disable(src); +} +#endif /* OLD_BINARY_SUPPORT */ + +/* + * sysfs files + */ +static ssize_t devname_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct taskdev *dev = to_taskdev(d); + return sprintf(buf, "%s\n", dev->name); +} + +static ssize_t devstate_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct taskdev *dev = to_taskdev(d); + return sprintf(buf, "%s\n", devstate_name(dev->state)); +} + +static ssize_t proc_list_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct taskdev *dev; + struct proc_list *pl; + int len = 0; + + dev = to_taskdev(d); + spin_lock(&dev->state_lock); + list_for_each_entry(pl, &dev->proc_list, list_head) { + len += sprintf(buf + len, "%d\n", pl->pid); + } + spin_unlock(&dev->state_lock); + + return len; +} + +static ssize_t taskname_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct taskdev *dev = to_taskdev(d); + int len; + + len = sprintf(buf, "%s\n", dev->task->name); + + return len; +} + +static ssize_t ttyp_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + unsigned short ttyp = to_taskdev(d)->task->ttyp; + int len = 0; + + len += sprintf(buf + len, "0x%04x\n", ttyp); + len += sprintf(buf + len, "%s %s send\n", + (sndtyp_acv(ttyp)) ? "active" : + "passive", + (sndtyp_wd(ttyp)) ? "word" : + (sndtyp_pvt(ttyp)) ? "private block" : + "global block"); + len += sprintf(buf + len, "%s %s receive\n", + (rcvtyp_acv(ttyp)) ? "active" : + "passive", + (rcvtyp_wd(ttyp)) ? "word" : + (rcvtyp_pvt(ttyp)) ? "private block" : + "global block"); + + return len; +} + +static ssize_t fifosz_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct fifo_struct *fifo = &to_taskdev(d)->task->rcvdt.fifo; + return sprintf(buf, "%d\n", fifo->sz); +} + +static int fifosz_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dsptask *task = to_taskdev(d)->task; + unsigned long fifosz; + int ret; + + fifosz = simple_strtol(buf, NULL, 10); + ret = dsp_task_set_fifosz(task, fifosz); + + return (ret < 0) ? ret : strlen(buf); +} + +static ssize_t fifocnt_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct fifo_struct *fifo = &to_taskdev(d)->task->rcvdt.fifo; + return sprintf(buf, "%d\n", fifo->cnt); +} + +static __inline__ char *bid_name(unsigned short bid) +{ + static char s[6]; + + switch (bid) { + case OMAP_DSP_BID_NULL: + return "NULL"; + case OMAP_DSP_BID_PVT: + return "PRIVATE"; + default: + sprintf(s, "%d", bid); + return s; + } +} + +static ssize_t ipblink_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct rcvdt_bk_struct *rcvdt = &to_taskdev(d)->task->rcvdt.bk; + int len; + + spin_lock(&rcvdt->link.lock); + len = sprintf(buf, "top %s\ntail %s\n", + bid_name(rcvdt->link.top), bid_name(rcvdt->link.tail)); + spin_unlock(&rcvdt->link.lock); + + return len; +} + +static ssize_t wsz_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + return sprintf(buf, "%d\n", to_taskdev(d)->task->wsz); +} + +static ssize_t mmap_show(struct device *d, struct device_attribute *attr, + char *buf) +{ + struct dsptask *task = to_taskdev(d)->task; + return sprintf(buf, "0x%p 0x%x\n", task->map_base, task->map_length); +} + +/* + * called from ipbuf_read_proc() + */ +int ipbuf_is_held(unsigned char tid, unsigned short bid) +{ + struct dsptask *task = dsptask[tid]; + unsigned short b; + int ret = 0; + + if (task == NULL) + return 0; + + spin_lock(&task->rcvdt.bk.link.lock); + ipblink_for_each(b, &task->rcvdt.bk.link, ipbuf) { + if (b == bid) { /* found */ + ret = 1; + break; + } + } + spin_unlock(&task->rcvdt.bk.link.lock); + + return ret; +} + +int __init dsp_taskmod_init(void) +{ + int retval; + + memset(taskdev, 0, sizeof(void *) * TASKDEV_MAX); + memset(dsptask, 0, sizeof(void *) * TASKDEV_MAX); + + retval = register_chrdev(OMAP_DSP_TASK_MAJOR, "dsptask", + &dsp_task_fops); + if (retval < 0) { + printk(KERN_ERR + "omapdsp: failed to register task device: %d\n", retval); + return retval; + } + + bus_register(&dsptask_bus); + retval = driver_register(&dsptask_driver); + if (retval) { + printk(KERN_ERR + "omapdsp: failed to register DSP task driver: %d\n", + retval); + bus_unregister(&dsptask_bus); + unregister_chrdev(OMAP_DSP_TASK_MAJOR, "dsptask"); + return -EINVAL; + } + dsp_task_class = class_create(THIS_MODULE, "dsptask"); + + return 0; +} + +void dsp_taskmod_exit(void) +{ + class_destroy(dsp_task_class); + driver_unregister(&dsptask_driver); + bus_unregister(&dsptask_bus); + unregister_chrdev(OMAP_DSP_TASK_MAJOR, "dsptask"); +} diff --git a/arch/arm/plat-omap/dsp/taskwatch.c b/arch/arm/plat-omap/dsp/taskwatch.c new file mode 100644 index 00000000000..b09c59d51d8 --- /dev/null +++ b/arch/arm/plat-omap/dsp/taskwatch.c @@ -0,0 +1,188 @@ +/* + * linux/arch/arm/mach-omap/dsp/taskwatch.c + * + * OMAP DSP task watch device driver + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 200%/05/16: DSP Gateway version 3.3 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dsp.h" + +static DECLARE_WAIT_QUEUE_HEAD(read_wait_q); +static unsigned int change_cnt; + +void dsp_twch_touch(void) +{ + change_cnt++; + wake_up_interruptible(&read_wait_q); +} + +/* + * @count: represents the device counts of the user's interst + */ +static ssize_t dsp_twch_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + long taskstat[TASKDEV_MAX]; + int devcount = count / sizeof(long); + int i; + + if (!dsp_is_ready()) { + printk(KERN_ERR "omapdsp: dsp has not been configured.\n"); + return -EINVAL; + } + + if (change_cnt == 0) { + long current_state; + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&read_wait_q, &wait); + current_state = current->state; + set_current_state(TASK_INTERRUPTIBLE); + if (change_cnt == 0) /* last check */ + schedule(); + set_current_state(current_state); + remove_wait_queue(&read_wait_q, &wait); + + /* unconfigured while waiting ;-( */ + if (dsp_is_ready()) + return -EINVAL; + } + + if (devcount > TASKDEV_MAX) + devcount = TASKDEV_MAX; + + count = devcount * sizeof(long); + change_cnt = 0; + for (i = 0; i < devcount; i++) { + /* + * once the device state is read, the 'STALE' bit will be set + * so that the Dynamic Loader can distinguish the new request + * from the old one. + */ + taskstat[i] = taskdev_state_stale(i); + } + + if (copy_to_user(buf, taskstat, count)) + return -EFAULT; + + return count; +} + +static unsigned int dsp_twch_poll(struct file *file, poll_table *wait) +{ + unsigned int mask = 0; + + poll_wait(file, &read_wait_q, wait); + if (change_cnt) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static int dsp_twch_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + static DECLARE_MUTEX(ioctl_sem); + int ret; + + if (down_interruptible(&ioctl_sem)) + return -ERESTARTSYS; + + switch (cmd) { + case OMAP_DSP_TWCH_IOCTL_MKDEV: + { + char name[OMAP_DSP_TNM_LEN]; + if (copy_from_user(name, (void *)arg, OMAP_DSP_TNM_LEN)) { + ret = -EFAULT; + goto up_out; + } + name[OMAP_DSP_TNM_LEN-1] = '\0'; + ret = dsp_mkdev(name); + break; + } + + case OMAP_DSP_TWCH_IOCTL_RMDEV: + { + char name[OMAP_DSP_TNM_LEN]; + if (copy_from_user(name, (void *)arg, OMAP_DSP_TNM_LEN)) { + ret = -EFAULT; + goto up_out; + } + name[OMAP_DSP_TNM_LEN-1] = '\0'; + ret = dsp_rmdev(name); + break; + } + + case OMAP_DSP_TWCH_IOCTL_TADD: + { + struct omap_dsp_taddinfo ti; + if (copy_from_user(&ti, (void *)arg, sizeof(ti))) { + ret = -EFAULT; + goto up_out; + } + ret = dsp_tadd(ti.minor, ti.taskadr); + break; + } + + case OMAP_DSP_TWCH_IOCTL_TDEL: + ret = dsp_tdel(arg); + break; + + case OMAP_DSP_TWCH_IOCTL_TKILL: + ret = dsp_tkill(arg); + break; + + default: + ret = -ENOIOCTLCMD; + } + +up_out: + up(&ioctl_sem); + return ret; +} + +struct file_operations dsp_twch_fops = { + .owner = THIS_MODULE, + .read = dsp_twch_read, + .poll = dsp_twch_poll, + .ioctl = dsp_twch_ioctl, +}; + +void dsp_twch_start(void) +{ + change_cnt = 1; /* first read will not wait */ +} + +void dsp_twch_stop(void) +{ + wake_up_interruptible(&read_wait_q); +} diff --git a/arch/arm/plat-omap/dsp/uaccess_dsp.S b/arch/arm/plat-omap/dsp/uaccess_dsp.S new file mode 100644 index 00000000000..3257b705064 --- /dev/null +++ b/arch/arm/plat-omap/dsp/uaccess_dsp.S @@ -0,0 +1,80 @@ +/* + * linux/arch/arm/mach-omap/dsp/uaccess_dsp.S + * + * user memory access functions for DSP driver + * + * Copyright (C) 2004,2005 Nokia Corporation + * + * Written by Toshihiro Kobayashi + * + * 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 + * + * 2004/06/29: DSP Gateway version 3.3 + */ + +#include +#include + + .text + +/* Prototype: int __arch_copy_to_user_dsp_2b(void *to, const char *from) + * Purpose : copy 2 bytes to user memory from kernel(DSP) memory + * escaping from unexpected byte swap using __arch_copy_to_user() + * in OMAP architecture. + * Params : to - user memory + * : from - kernel(DSP) memory + * Returns : success = 0, failure = 2 + */ + +ENTRY(__arch_copy_to_user_dsp_2b) + stmfd sp!, {r4, lr} + ldrb r3, [r1], #1 + ldrb r4, [r1], #1 +USER( strbt r4, [r0], #1) @ May fault +USER( strbt r3, [r0], #1) @ May fault + mov r0, #0 + LOADREGS(fd,sp!,{r4, pc}) + + .section .fixup,"ax" + .align 0 +9001: mov r0, #2 + LOADREGS(fd,sp!, {r4, pc}) + .previous + +/* Prototype: unsigned long __arch_copy_from_user_dsp_2b(void *to, const void *from); + * Purpose : copy 2 bytes from user memory to kernel(DSP) memory + * escaping from unexpected byte swap using __arch_copy_to_user() + * in OMAP architecture. + * Params : to - kernel (DSP) memory + * : from - user memory + * Returns : success = 0, failure = 2 + */ + +ENTRY(__arch_copy_from_user_dsp_2b) + stmfd sp!, {r4, lr} +USER( ldrbt r3, [r1], #1) @ May fault +USER( ldrbt r4, [r1], #1) @ May fault + strb r4, [r0], #1 + strb r3, [r0], #1 + mov r0, #0 + LOADREGS(fd,sp!,{r4, pc}) + + .section .fixup,"ax" + .align 0 +9001: mov r3, #0 + strh r3, [r0], #2 + mov r0, #2 + LOADREGS(fd,sp!, {r4, pc}) + .previous diff --git a/arch/arm/plat-omap/dsp/uaccess_dsp.h b/arch/arm/plat-omap/dsp/uaccess_dsp.h new file mode 100644 index 00000000000..2217a1042e5 --- /dev/null +++ b/arch/arm/plat-omap/dsp/uaccess_dsp.h @@ -0,0 +1,186 @@ +/* + * linux/arch/arm/mach-omap/dsp/uaccess_dsp.h + * + * Header for user access functions for DSP driver + * + * Copyright (C) 2002-2005 Nokia Corporation + * + * Modified from linux/include/asm-arm/uaccess.h + * by Toshihiro Kobayashi + * + * 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 + * + * 2004/06/29: DSP Gateway version 3.3 + */ + +#ifndef _OMAP_DSP_UACCESS_DSP_H +#define _OMAP_DSP_UACCESS_DSP_H + +#include + +#define HAVE_ASM_COPY_FROM_USER_DSP_2B + +#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B +extern unsigned long __arch_copy_from_user_dsp_2b(void *to, + const void __user *from); +extern unsigned long __arch_copy_to_user_dsp_2b(void __user *to, + const void *from); +#endif + +extern unsigned long dspmem_base, dspmem_size; +#define is_dsp_internal_mem(va) \ + (((unsigned long)(va) >= dspmem_base) && \ + ((unsigned long)(va) < dspmem_base + dspmem_size)) + + +#ifndef HAVE_ASM_COPY_FROM_USER_DSP_2B +static __inline__ unsigned long copy_from_user_dsp_2b(void *to, + const void *from) +{ + unsigned short tmp; + + if (__arch_copy_from_user(&tmp, from, 2)) + return 2; + /* expecting compiler to generate "strh" instruction */ + *((unsigned short *)to) = tmp; + return 0; +} +#endif + +/* + * @n must be multiple of 2 + */ +static __inline__ unsigned long copy_from_user_dsp(void *to, const void *from, + unsigned long n) +{ + if (access_ok(VERIFY_READ, from, n)) { + if ((is_dsp_internal_mem(to)) && + (((unsigned long)to & 2) || (n & 2))) { + /* + * DARAM/SARAM with odd word alignment + */ + unsigned long n4; + unsigned long last_n; + + /* dest not aligned -- copy 2 bytes */ + if (((unsigned long)to & 2) && (n >= 2)) { +#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B + if (__arch_copy_from_user_dsp_2b(to, from)) +#else + if (copy_from_user_dsp_2b(to, from)) +#endif + return n; + to += 2; + from += 2; + n -= 2; + } + /* middle 4*n bytes */ + last_n = n & 2; + n4 = n - last_n; + if ((n = __arch_copy_from_user(to, from, n4)) != 0) + return n + last_n; + /* last 2 bytes */ + if (last_n) { + to += n4; + from += n4; +#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B + if (__arch_copy_from_user_dsp_2b(to, from)) +#else + if (copy_from_user_dsp_2b(to, from)) +#endif + return 2; + n = 0; + } + } else { + /* + * DARAM/SARAM with 4-byte alignment or + * external memory + */ + n = __arch_copy_from_user(to, from, n); + } + } + else /* security hole - plug it */ + memzero(to, n); + return n; +} + +#ifndef HAVE_ASM_COPY_FROM_USER_DSP_2B +static __inline__ unsigned long copy_to_user_dsp_2b(void *to, const void *from) +{ + /* expecting compiler to generate "strh" instruction */ + unsigned short tmp = *(unsigned short *)from; + + return __arch_copy_to_user(to, &tmp, 2); +} +#endif + +/* + * @n must be multiple of 2 + */ +static __inline__ unsigned long copy_to_user_dsp(void *to, const void *from, + unsigned long n) +{ + if (access_ok(VERIFY_WRITE, to, n)) { + if ((is_dsp_internal_mem(from)) && + (((unsigned long)to & 2) || (n & 2))) { + /* + * DARAM/SARAM with odd word alignment + */ + unsigned long n4; + unsigned long last_n; + + /* dest not aligned -- copy 2 bytes */ + if (((unsigned long)to & 2) && (n >= 2)) { +#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B + if (__arch_copy_to_user_dsp_2b(to, from)) +#else + if (copy_to_user_dsp_2b(to, from)) +#endif + return n; + to += 2; + from += 2; + n -= 2; + } + /* middle 4*n bytes */ + last_n = n & 2; + n4 = n - last_n; + if ((n = __arch_copy_to_user(to, from, n4)) != 0) + return n + last_n; + /* last 2 bytes */ + if (last_n) { + to += n4; + from += n4; +#ifdef HAVE_ASM_COPY_FROM_USER_DSP_2B + if (__arch_copy_to_user_dsp_2b(to, from)) +#else + if (copy_to_user_dsp_2b(to, from)) +#endif + return 2; + n = 0; + } + } else { + /* + * DARAM/SARAM with 4-byte alignment or + * external memory + */ + n = __arch_copy_to_user(to, from, n); + } + } + return n; +} + +#undef is_dsp_internal_mem + +#endif /* _OMAP_DSP_UACCESS_DSP_H */ diff --git a/arch/arm/plat-omap/fb.c b/arch/arm/plat-omap/fb.c new file mode 100644 index 00000000000..6dd5292df07 --- /dev/null +++ b/arch/arm/plat-omap/fb.c @@ -0,0 +1,84 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) + +static struct omapfb_platform_data omapfb_config; + +static u64 omap_fb_dma_mask = ~(u32)0; + +/* in devices.c */ +extern void omap_nop_release(struct device *dev); + +static struct platform_device omap_fb_device = { + .name = "omapfb", + .id = -1, + .dev = { + .release = omap_nop_release, + .dma_mask = &omap_fb_dma_mask, + .coherent_dma_mask = ~(u32)0, + .platform_data = &omapfb_config, + }, + .num_resources = 0, +}; + +/* called from map_io */ +void omapfb_reserve_mem(void) +{ + const struct omap_fbmem_config *fbmem_conf; + + omapfb_config.fbmem.fb_sram_start = omap_fb_sram_start; + omapfb_config.fbmem.fb_sram_size = omap_fb_sram_size; + + fbmem_conf = omap_get_config(OMAP_TAG_FBMEM, struct omap_fbmem_config); + + if (fbmem_conf != NULL) { + /* indicate that the bootloader already initialized the + * fb device, so we'll skip that part in the fb driver + */ + omapfb_config.fbmem.fb_sdram_start = fbmem_conf->fb_sdram_start; + omapfb_config.fbmem.fb_sdram_size = fbmem_conf->fb_sdram_size; + if (fbmem_conf->fb_sdram_size) { + pr_info("Reserving %u bytes SDRAM for frame buffer\n", + fbmem_conf->fb_sdram_size); + reserve_bootmem(fbmem_conf->fb_sdram_start, + fbmem_conf->fb_sdram_size); + } + } +} + +static inline int omap_init_fb(void) +{ + const struct omap_lcd_config *conf; + + conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config); + if (conf == NULL) + return 0; + + omapfb_config.lcd = *conf; + + return platform_device_register(&omap_fb_device); +} + +arch_initcall(omap_init_fb); + +#else + +void omapfb_reserve_mem(void) {} + +#endif + + diff --git a/arch/arm/plat-omap/gpio-switch.c b/arch/arm/plat-omap/gpio-switch.c new file mode 100644 index 00000000000..b5d0543d5e1 --- /dev/null +++ b/arch/arm/plat-omap/gpio-switch.c @@ -0,0 +1,333 @@ +/* + * linux/arch/arm/plat-omap/gpio-switch.c + * + * Copyright (C) 2004, 2005 Nokia Corporation + * Written by Juha Yrjölä + * and Paul Mundt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct gpio_switch { + char name[14]; + u16 gpio; + int flags; + int type; + int state; + + struct work_struct work; + struct timer_list timer; + struct platform_device pdev; + + struct list_head node; +}; + +static LIST_HEAD(gpio_switches); +static struct platform_device *gpio_sw_platform_dev; +static struct device_driver gpio_sw_driver; + +static const char *cover_str[2] = { "open", "closed" }; +static const char *connection_str[2] = { "disconnected", "connected" }; + +/* + * GPIO switch state poll delay in ms + */ +#define OMAP_GPIO_SW_POLL_DELAY 10 + +static void print_sw_state(struct gpio_switch *sw, int state) +{ + const char **str; + + switch (sw->type) { + case OMAP_GPIO_SWITCH_TYPE_COVER: + str = cover_str; + break; + case OMAP_GPIO_SWITCH_TYPE_CONNECTION: + str = connection_str; + break; + default: + str = NULL; + } + if (str != NULL) + printk(KERN_INFO "%s (GPIO %d) is now %s\n", sw->name, sw->gpio, str[state]); +} + +static int gpio_sw_get_state(struct gpio_switch *sw) +{ + int state; + + state = omap_get_gpio_datain(sw->gpio); + if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED) + state = !state; + + return state; +} + +static ssize_t gpio_sw_store(struct device *dev, + struct device_attribute *attr, + const char *buf, + size_t count) +{ + struct gpio_switch *sw = dev_get_drvdata(dev); + int enable = (int)simple_strtoul(buf, NULL, 10); + omap_set_gpio_dataout(sw->gpio, enable); + return count; +} + +#define gpio_sw_switch_attr(name) \ +static ssize_t gpio_sw_show_##name(struct device *dev, \ + struct device_attribute *attr, \ + char *buf) \ +{ \ + struct gpio_switch *sw = dev_get_drvdata(dev); \ + return sprintf(buf, "%s\n", name##_str[gpio_sw_get_state(sw)]); \ +} \ +static DEVICE_ATTR(name##_switch, S_IRUGO | S_IWUSR, \ + gpio_sw_show_##name, gpio_sw_store) + +gpio_sw_switch_attr(cover); +gpio_sw_switch_attr(connection); + +static irqreturn_t gpio_sw_irq_handler(int irq, void *arg, struct pt_regs *regs) +{ + struct gpio_switch *sw = arg; + + mod_timer(&sw->timer, jiffies + OMAP_GPIO_SW_POLL_DELAY / (1000 / HZ)); + + return IRQ_HANDLED; +} + +static void gpio_sw_timer(unsigned long arg) +{ + struct gpio_switch *sw = (struct gpio_switch *)arg; + + schedule_work(&sw->work); +} + +static void gpio_sw_handler(void *data) +{ + struct gpio_switch *sw = data; + int state = gpio_sw_get_state(sw); + + if (sw->state == state) + return; + + if (sw->type == OMAP_GPIO_SWITCH_TYPE_CONNECTION) + kobject_uevent(&sw->pdev.dev.kobj, KOBJ_CHANGE); + else + kobject_uevent(&sw->pdev.dev.kobj, KOBJ_CHANGE); + sw->state = state; + if (omap_get_gpio_datain(sw->gpio)) + set_irq_type(OMAP_GPIO_IRQ(sw->gpio), IRQT_FALLING); + else + set_irq_type(OMAP_GPIO_IRQ(sw->gpio), IRQT_RISING); + print_sw_state(sw, state); +} + +static int __init new_switch(struct gpio_switch *sw) +{ + int r, direction, trigger; + + sw->pdev.name = sw->name; + sw->pdev.id = -1; + + sw->pdev.dev.parent = &gpio_sw_platform_dev->dev; + sw->pdev.dev.driver = &gpio_sw_driver; + + r = platform_device_register(&sw->pdev); + if (r) + return r; + + dev_set_drvdata(&sw->pdev.dev, sw); + + r = omap_request_gpio(sw->gpio); + if (r < 0) { + platform_device_unregister(&sw->pdev); + return r; + } + + /* input: 1, output: 0 */ + direction = !(sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT); + omap_set_gpio_direction(sw->gpio, direction); + + switch (sw->type) { + case OMAP_GPIO_SWITCH_TYPE_COVER: + device_create_file(&sw->pdev.dev, &dev_attr_cover_switch); + break; + case OMAP_GPIO_SWITCH_TYPE_CONNECTION: + device_create_file(&sw->pdev.dev, &dev_attr_connection_switch); + break; + } + + list_add(&sw->node, &gpio_switches); + + if (!direction) + return 0; + + if (omap_get_gpio_datain(sw->gpio)) + trigger = SA_TRIGGER_FALLING; + else + trigger = SA_TRIGGER_RISING; + r = request_irq(OMAP_GPIO_IRQ(sw->gpio), gpio_sw_irq_handler, + SA_SHIRQ | trigger, sw->name, sw); + if (r < 0) { + printk(KERN_ERR "gpio-switch: request_irq() failed " + "for GPIO %d\n", sw->gpio); + platform_device_unregister(&sw->pdev); + omap_free_gpio(sw->gpio); + return r; + } + + INIT_WORK(&sw->work, gpio_sw_handler, sw); + init_timer(&sw->timer); + + sw->timer.function = gpio_sw_timer; + sw->timer.data = (unsigned long)sw; + + return 0; +} + +static int __init add_atag_switches(void) +{ + const struct omap_gpio_switch_config *cfg; + struct gpio_switch *sw; + int i, r; + + for (i = 0; ; i++) { + cfg = omap_get_nr_config(OMAP_TAG_GPIO_SWITCH, + struct omap_gpio_switch_config, i); + if (cfg == NULL) + break; + sw = kmalloc(sizeof(*sw), GFP_KERNEL); + if (sw == NULL) { + printk(KERN_ERR "gpio-switch: kmalloc failed\n"); + return -ENOMEM; + } + memset(sw, 0, sizeof(*sw)); + strncpy(sw->name, cfg->name, sizeof(cfg->name)); + sw->gpio = cfg->gpio; + sw->flags = cfg->flags; + sw->type = cfg->type; + sw->state = gpio_sw_get_state(sw); + if ((r = new_switch(sw)) < 0) { + kfree(sw); + return r; + } + } + return 0; +} + +static void gpio_sw_cleanup(void) +{ + struct gpio_switch *sw = NULL, *old = NULL; + + list_for_each_entry(sw, &gpio_switches, node) { + kfree(old); + + flush_scheduled_work(); + del_timer_sync(&sw->timer); + + free_irq(OMAP_GPIO_IRQ(sw->gpio), sw); + + if (sw->type == OMAP_GPIO_SWITCH_TYPE_CONNECTION) + device_remove_file(&sw->pdev.dev, + &dev_attr_connection_switch); + else + device_remove_file(&sw->pdev.dev, + &dev_attr_cover_switch); + + platform_device_unregister(&sw->pdev); + omap_free_gpio(sw->gpio); + old = sw; + } + + kfree(sw); +} + +static void __init report_initial_state(void) +{ + struct gpio_switch *sw; + + list_for_each_entry(sw, &gpio_switches, node) { + int state; + + state = omap_get_gpio_datain(sw->gpio); + if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED) + state = !state; + print_sw_state(sw, state); + } +} + +static void gpio_sw_shutdown(struct device *dev) +{ +} + +static struct device_driver gpio_sw_driver = { + .name = "gpio-switch", + .bus = &platform_bus_type, + .shutdown = gpio_sw_shutdown, +}; + +static int __init gpio_sw_init(void) +{ + int r; + + printk(KERN_INFO "OMAP GPIO switch handler initializing\n"); + + r = driver_register(&gpio_sw_driver); + if (r) + return r; + + gpio_sw_platform_dev = platform_device_register_simple("gpio-switch", + -1, NULL, 0); + if (IS_ERR(gpio_sw_platform_dev)) { + driver_unregister(&gpio_sw_driver); + return PTR_ERR(gpio_sw_platform_dev); + } + + r = add_atag_switches(); + if (r < 0) { + platform_device_unregister(gpio_sw_platform_dev); + driver_unregister(&gpio_sw_driver); + gpio_sw_cleanup(); + return r; + } + + report_initial_state(); + + return 0; +} + +static void __exit gpio_sw_exit(void) +{ + gpio_sw_cleanup(); + platform_device_unregister(gpio_sw_platform_dev); + driver_unregister(&gpio_sw_driver); +} + +#ifndef MODULE +late_initcall(gpio_sw_init); +#else +module_init(gpio_sw_init); +#endif +module_exit(gpio_sw_exit); + +MODULE_AUTHOR("Juha Yrjölä , Paul Mundt method == METHOD_GPIO_24XX) isr_reg = bank->base + OMAP24XX_GPIO_IRQSTATUS1; #endif - while(1) { - isr = __raw_readl(isr_reg); - _enable_gpio_irqbank(bank, isr, 0); - _clear_gpio_irqbank(bank, isr); - _enable_gpio_irqbank(bank, isr, 1); - desc->chip->unmask(irq); + u32 isr_saved, level_mask = 0; + + isr_saved = isr = __raw_readl(isr_reg); + + if (cpu_is_omap15xx() && (bank->method == METHOD_MPUIO)) + isr &= 0x0000ffff; + + if (cpu_is_omap24xx()) + level_mask = + __raw_readl(bank->base + + OMAP24XX_GPIO_LEVELDETECT0) | + __raw_readl(bank->base + + OMAP24XX_GPIO_LEVELDETECT1); + + /* clear edge sensitive interrupts before handler(s) are + called so that we don't miss any interrupt occurred while + executing them */ + _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 0); + _clear_gpio_irqbank(bank, isr_saved & ~level_mask); + _enable_gpio_irqbank(bank, isr_saved & ~level_mask, 1); + + /* if there is only edge sensitive GPIO pin interrupts + configured, we could unmask GPIO bank interrupt immediately */ + if (!level_mask) + desc->chip->unmask(irq); if (!isr) break; @@ -774,6 +796,20 @@ static void gpio_irq_handler(unsigned int irq, struct irqdesc *desc, d = irq_desc + gpio_irq; desc_handle_irq(gpio_irq, d, regs); } + + if (cpu_is_omap24xx()) { + /* clear level sensitive interrupts after handler(s) */ + _enable_gpio_irqbank(bank, isr_saved & level_mask, 0); + _clear_gpio_irqbank(bank, isr_saved & level_mask); + _enable_gpio_irqbank(bank, isr_saved & level_mask, 1); + } + + /* if bank has any level sensitive GPIO pin interrupt + configured, we must unmask the bank interrupt only after + handler(s) are executed in order to avoid spurious bank + interrupt */ + if (level_mask) + desc->chip->unmask(irq); } } @@ -848,7 +884,7 @@ static int __init _omap_gpio_init(void) initialized = 1; - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { gpio_ick = clk_get(NULL, "arm_gpio_ck"); if (IS_ERR(gpio_ick)) printk("Could not get arm_gpio_ck\n"); @@ -869,7 +905,7 @@ static int __init _omap_gpio_init(void) } #ifdef CONFIG_ARCH_OMAP15XX - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { printk(KERN_INFO "OMAP1510 GPIO hardware\n"); gpio_bank_count = 2; gpio_bank = gpio_bank_1510; diff --git a/arch/arm/plat-omap/mcbsp.c b/arch/arm/plat-omap/mcbsp.c index 1cd2cace7e1..d87f21cc3c0 100644 --- a/arch/arm/plat-omap/mcbsp.c +++ b/arch/arm/plat-omap/mcbsp.c @@ -34,7 +34,7 @@ #ifdef CONFIG_MCBSP_DEBUG #define DBG(x...) printk(x) #else -#define DBG(x...) do { } while (0) +#define DBG(x...) do { } while (0) #endif struct omap_mcbsp { @@ -44,6 +44,7 @@ struct omap_mcbsp { omap_mcbsp_word_length rx_word_length; omap_mcbsp_word_length tx_word_length; + omap_mcbsp_io_type_t io_type; /* IRQ or poll */ /* IRQ based TX/RX */ int rx_irq; int tx_irq; @@ -64,10 +65,19 @@ struct omap_mcbsp { }; static struct omap_mcbsp mcbsp[OMAP_MAX_MCBSP_COUNT]; +#ifdef CONFIG_ARCH_OMAP1 static struct clk *mcbsp_dsp_ck = 0; static struct clk *mcbsp_api_ck = 0; static struct clk *mcbsp_dspxor_ck = 0; - +#endif +#ifdef CONFIG_ARCH_OMAP2 +static struct clk *mcbsp1_ick = 0; +static struct clk *mcbsp1_fck = 0; +static struct clk *mcbsp2_ick = 0; +static struct clk *mcbsp2_fck = 0; +static struct clk *sys_ck = 0; +static struct clk *sys_clkout = 0; +#endif static void omap_mcbsp_dump_reg(u8 id) { @@ -88,7 +98,6 @@ static void omap_mcbsp_dump_reg(u8 id) DBG("***********************\n"); } - static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id, struct pt_regs *regs) { struct omap_mcbsp * mcbsp_tx = (struct omap_mcbsp *)(dev_id); @@ -109,7 +118,6 @@ static irqreturn_t omap_mcbsp_rx_irq_handler(int irq, void *dev_id, struct pt_re return IRQ_HANDLED; } - static void omap_mcbsp_tx_dma_callback(int lch, u16 ch_status, void *data) { struct omap_mcbsp * mcbsp_dma_tx = (struct omap_mcbsp *)(data); @@ -176,7 +184,7 @@ static int omap_mcbsp_check(unsigned int id) return 0; } - if (cpu_is_omap1510() || cpu_is_omap16xx()) { + if (cpu_is_omap15xx() || cpu_is_omap16xx() || cpu_is_omap24xx()) { if (id > OMAP_MAX_MCBSP_COUNT) { printk(KERN_ERR "OMAP-McBSP: McBSP%d doesn't exist\n", id + 1); return -1; @@ -187,9 +195,11 @@ static int omap_mcbsp_check(unsigned int id) return -1; } +#ifdef CONFIG_ARCH_OMAP1 static void omap_mcbsp_dsp_request(void) { - if (cpu_is_omap1510() || cpu_is_omap16xx()) { + if (cpu_is_omap15xx() || cpu_is_omap16xx()) { + omap_dsp_request_mem(); clk_enable(mcbsp_dsp_ck); clk_enable(mcbsp_api_ck); @@ -207,12 +217,50 @@ static void omap_mcbsp_dsp_request(void) static void omap_mcbsp_dsp_free(void) { - if (cpu_is_omap1510() || cpu_is_omap16xx()) { + if (cpu_is_omap15xx() || cpu_is_omap16xx()) { + omap_dsp_release_mem(); clk_disable(mcbsp_dspxor_ck); clk_disable(mcbsp_dsp_ck); clk_disable(mcbsp_api_ck); } } +#endif + +#ifdef CONFIG_ARCH_OMAP2 +static void omap2_mcbsp2_mux_setup(void) +{ + omap_cfg_reg(Y15_24XX_MCBSP2_CLKX); + omap_cfg_reg(R14_24XX_MCBSP2_FSX); + omap_cfg_reg(W15_24XX_MCBSP2_DR); + omap_cfg_reg(V15_24XX_MCBSP2_DX); + omap_cfg_reg(V14_24XX_GPIO117); + omap_cfg_reg(W14_24XX_SYS_CLKOUT); +} +#endif + +/* + * We can choose between IRQ based or polled IO. + * This needs to be called before omap_mcbsp_request(). + */ +int omap_mcbsp_set_io_type(unsigned int id, omap_mcbsp_io_type_t io_type) +{ + if (omap_mcbsp_check(id) < 0) + return -EINVAL; + + spin_lock(&mcbsp[id].lock); + + if (!mcbsp[id].free) { + printk (KERN_ERR "OMAP-McBSP: McBSP%d is currently in use\n", id + 1); + spin_unlock(&mcbsp[id].lock); + return -EINVAL; + } + + mcbsp[id].io_type = io_type; + + spin_unlock(&mcbsp[id].lock); + + return 0; +} int omap_mcbsp_request(unsigned int id) { @@ -221,12 +269,26 @@ int omap_mcbsp_request(unsigned int id) if (omap_mcbsp_check(id) < 0) return -EINVAL; +#ifdef CONFIG_ARCH_OMAP1 /* * On 1510, 1610 and 1710, McBSP1 and McBSP3 * are DSP public peripherals. */ if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) omap_mcbsp_dsp_request(); +#endif + +#ifdef CONFIG_ARCH_OMAP2 + if (cpu_is_omap24xx()) { + if (id == OMAP_MCBSP1) { + clk_enable(mcbsp1_ick); + clk_enable(mcbsp1_fck); + } else { + clk_enable(mcbsp2_ick); + clk_enable(mcbsp2_fck); + } + } +#endif spin_lock(&mcbsp[id].lock); if (!mcbsp[id].free) { @@ -238,30 +300,33 @@ int omap_mcbsp_request(unsigned int id) mcbsp[id].free = 0; spin_unlock(&mcbsp[id].lock); - /* We need to get IRQs here */ - err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0, - "McBSP", - (void *) (&mcbsp[id])); - if (err != 0) { - printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n", - mcbsp[id].tx_irq, mcbsp[id].id); - return err; - } - - init_completion(&(mcbsp[id].tx_irq_completion)); - - - err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0, - "McBSP", - (void *) (&mcbsp[id])); - if (err != 0) { - printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n", - mcbsp[id].rx_irq, mcbsp[id].id); - free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); - return err; + if (mcbsp[id].io_type == OMAP_MCBSP_IRQ_IO) { + /* We need to get IRQs here */ + err = request_irq(mcbsp[id].tx_irq, omap_mcbsp_tx_irq_handler, 0, + "McBSP", + (void *) (&mcbsp[id])); + if (err != 0) { + printk(KERN_ERR "OMAP-McBSP: Unable to request TX IRQ %d for McBSP%d\n", + mcbsp[id].tx_irq, mcbsp[id].id); + return err; + } + + init_completion(&(mcbsp[id].tx_irq_completion)); + + + err = request_irq(mcbsp[id].rx_irq, omap_mcbsp_rx_irq_handler, 0, + "McBSP", + (void *) (&mcbsp[id])); + if (err != 0) { + printk(KERN_ERR "OMAP-McBSP: Unable to request RX IRQ %d for McBSP%d\n", + mcbsp[id].rx_irq, mcbsp[id].id); + free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); + return err; + } + + init_completion(&(mcbsp[id].rx_irq_completion)); } - - init_completion(&(mcbsp[id].rx_irq_completion)); + return 0; } @@ -271,8 +336,24 @@ void omap_mcbsp_free(unsigned int id) if (omap_mcbsp_check(id) < 0) return; - if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) - omap_mcbsp_dsp_free(); +#ifdef CONFIG_ARCH_OMAP1 + if (cpu_class_is_omap1()) { + if (id == OMAP_MCBSP1 || id == OMAP_MCBSP3) + omap_mcbsp_dsp_free(); + } +#endif + +#ifdef CONFIG_ARCH_OMAP2 + if (cpu_is_omap24xx()) { + if (id == OMAP_MCBSP1) { + clk_disable(mcbsp1_ick); + clk_disable(mcbsp1_fck); + } else { + clk_disable(mcbsp2_ick); + clk_disable(mcbsp2_fck); + } + } +#endif spin_lock(&mcbsp[id].lock); if (mcbsp[id].free) { @@ -283,10 +364,12 @@ void omap_mcbsp_free(unsigned int id) mcbsp[id].free = 1; spin_unlock(&mcbsp[id].lock); - - /* Free IRQs */ - free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id])); - free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); + + if (mcbsp[id].io_type == OMAP_MCBSP_IRQ_IO) { + /* Free IRQs */ + free_irq(mcbsp[id].rx_irq, (void *) (&mcbsp[id])); + free_irq(mcbsp[id].tx_irq, (void *) (&mcbsp[id])); + } } /* @@ -461,6 +544,115 @@ u32 omap_mcbsp_recv_word(unsigned int id) } +int omap_mcbsp_spi_master_xmit_word_poll(unsigned int id, u32 word) +{ + u32 io_base = mcbsp[id].io_base; + omap_mcbsp_word_length tx_word_length = mcbsp[id].tx_word_length; + omap_mcbsp_word_length rx_word_length = mcbsp[id].rx_word_length; + u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0; + + if (tx_word_length != rx_word_length) + return -EINVAL; + + /* First we wait for the transmitter to be ready */ + spcr2 = OMAP_MCBSP_READ(io_base, SPCR2); + while (!(spcr2 & XRDY)) { + spcr2 = OMAP_MCBSP_READ(io_base, SPCR2); + if (attempts++ > 1000) { + /* We must reset the transmitter */ + OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 & (~XRST)); + udelay(10); + OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 | XRST); + udelay(10); + printk("McBSP transmitter not ready\n"); + return -EAGAIN; + } + } + + /* Now we can push the data */ + if (tx_word_length > OMAP_MCBSP_WORD_16) + OMAP_MCBSP_WRITE(io_base, DXR2, word >> 16); + OMAP_MCBSP_WRITE(io_base, DXR1, word & 0xffff); + + /* We wait for the receiver to be ready */ + spcr1 = OMAP_MCBSP_READ(io_base, SPCR1); + while (!(spcr1 & RRDY)) { + spcr1 = OMAP_MCBSP_READ(io_base, SPCR1); + if (attempts++ > 1000) { + /* We must reset the receiver */ + OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 & (~RRST)); + udelay(10); + OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 | RRST); + udelay(10); + printk("McBSP receiver not ready\n"); + return -EAGAIN; + } + } + + /* Receiver is ready, let's read the dummy data */ + if (rx_word_length > OMAP_MCBSP_WORD_16) + word_msb = OMAP_MCBSP_READ(io_base, DRR2); + word_lsb = OMAP_MCBSP_READ(io_base, DRR1); + + return 0; +} + +int omap_mcbsp_spi_master_recv_word_poll(unsigned int id, u32 * word) +{ + u32 io_base = mcbsp[id].io_base, clock_word = 0; + omap_mcbsp_word_length tx_word_length = mcbsp[id].tx_word_length; + omap_mcbsp_word_length rx_word_length = mcbsp[id].rx_word_length; + u16 spcr2, spcr1, attempts = 0, word_lsb, word_msb = 0; + + if (tx_word_length != rx_word_length) + return -EINVAL; + + /* First we wait for the transmitter to be ready */ + spcr2 = OMAP_MCBSP_READ(io_base, SPCR2); + while (!(spcr2 & XRDY)) { + spcr2 = OMAP_MCBSP_READ(io_base, SPCR2); + if (attempts++ > 1000) { + /* We must reset the transmitter */ + OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 & (~XRST)); + udelay(10); + OMAP_MCBSP_WRITE(io_base, SPCR2, spcr2 | XRST); + udelay(10); + printk("McBSP transmitter not ready\n"); + return -EAGAIN; + } + } + + /* We first need to enable the bus clock */ + if (tx_word_length > OMAP_MCBSP_WORD_16) + OMAP_MCBSP_WRITE(io_base, DXR2, clock_word >> 16); + OMAP_MCBSP_WRITE(io_base, DXR1, clock_word & 0xffff); + + /* We wait for the receiver to be ready */ + spcr1 = OMAP_MCBSP_READ(io_base, SPCR1); + while (!(spcr1 & RRDY)) { + spcr1 = OMAP_MCBSP_READ(io_base, SPCR1); + if (attempts++ > 1000) { + /* We must reset the receiver */ + OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 & (~RRST)); + udelay(10); + OMAP_MCBSP_WRITE(io_base, SPCR1, spcr1 | RRST); + udelay(10); + printk("McBSP receiver not ready\n"); + return -EAGAIN; + } + } + + /* Receiver is ready, there is something for us */ + if (rx_word_length > OMAP_MCBSP_WORD_16) + word_msb = OMAP_MCBSP_READ(io_base, DRR2); + word_lsb = OMAP_MCBSP_READ(io_base, DRR1); + + word[0] = (word_lsb | (word_msb << 16)); + + return 0; +} + + /* * Simple DMA based buffer rx/tx routines. * Nothing fancy, just a single buffer tx/rx through DMA. @@ -471,6 +663,9 @@ u32 omap_mcbsp_recv_word(unsigned int id) int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) { int dma_tx_ch; + int src_port = 0; + int dest_port = 0; + int sync_dev = 0; if (omap_mcbsp_check(id) < 0) return -EINVAL; @@ -487,20 +682,27 @@ int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int leng init_completion(&(mcbsp[id].tx_dma_completion)); + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_TIPB; + dest_port = OMAP_DMA_PORT_EMIFF; + } + if (cpu_is_omap24xx()) + sync_dev = mcbsp[id].dma_tx_sync; + omap_set_dma_transfer_params(mcbsp[id].dma_tx_lch, OMAP_DMA_DATA_TYPE_S16, length >> 1, 1, OMAP_DMA_SYNC_ELEMENT, - 0, 0); + sync_dev, 0); omap_set_dma_dest_params(mcbsp[id].dma_tx_lch, - OMAP_DMA_PORT_TIPB, + src_port, OMAP_DMA_AMODE_CONSTANT, mcbsp[id].io_base + OMAP_MCBSP_REG_DXR1, 0, 0); omap_set_dma_src_params(mcbsp[id].dma_tx_lch, - OMAP_DMA_PORT_EMIFF, + dest_port, OMAP_DMA_AMODE_POST_INC, buffer, 0, 0); @@ -514,6 +716,9 @@ int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int leng int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int length) { int dma_rx_ch; + int src_port = 0; + int dest_port = 0; + int sync_dev = 0; if (omap_mcbsp_check(id) < 0) return -EINVAL; @@ -530,20 +735,27 @@ int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int leng init_completion(&(mcbsp[id].rx_dma_completion)); + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_TIPB; + dest_port = OMAP_DMA_PORT_EMIFF; + } + if (cpu_is_omap24xx()) + sync_dev = mcbsp[id].dma_rx_sync; + omap_set_dma_transfer_params(mcbsp[id].dma_rx_lch, OMAP_DMA_DATA_TYPE_S16, length >> 1, 1, OMAP_DMA_SYNC_ELEMENT, - 0, 0); + sync_dev, 0); omap_set_dma_src_params(mcbsp[id].dma_rx_lch, - OMAP_DMA_PORT_TIPB, + src_port, OMAP_DMA_AMODE_CONSTANT, mcbsp[id].io_base + OMAP_MCBSP_REG_DRR1, 0, 0); omap_set_dma_dest_params(mcbsp[id].dma_rx_lch, - OMAP_DMA_PORT_EMIFF, + dest_port, OMAP_DMA_AMODE_POST_INC, buffer, 0, 0); @@ -688,6 +900,23 @@ static const struct omap_mcbsp_info mcbsp_1610[] = { }; #endif +#if defined(CONFIG_ARCH_OMAP24XX) +static const struct omap_mcbsp_info mcbsp_24xx[] = { + [0] = { .virt_base = IO_ADDRESS(OMAP24XX_MCBSP1_BASE), + .dma_rx_sync = OMAP24XX_DMA_MCBSP1_RX, + .dma_tx_sync = OMAP24XX_DMA_MCBSP1_TX, + .rx_irq = INT_24XX_MCBSP1_IRQ_RX, + .tx_irq = INT_24XX_MCBSP1_IRQ_TX, + }, + [1] = { .virt_base = IO_ADDRESS(OMAP24XX_MCBSP2_BASE), + .dma_rx_sync = OMAP24XX_DMA_MCBSP2_RX, + .dma_tx_sync = OMAP24XX_DMA_MCBSP2_TX, + .rx_irq = INT_24XX_MCBSP2_IRQ_RX, + .tx_irq = INT_24XX_MCBSP2_IRQ_TX, + }, +}; +#endif + static int __init omap_mcbsp_init(void) { int mcbsp_count = 0, i; @@ -695,6 +924,7 @@ static int __init omap_mcbsp_init(void) printk("Initializing OMAP McBSP system\n"); +#ifdef CONFIG_ARCH_OMAP1 mcbsp_dsp_ck = clk_get(0, "dsp_ck"); if (IS_ERR(mcbsp_dsp_ck)) { printk(KERN_ERR "mcbsp: could not acquire dsp_ck handle.\n"); @@ -710,6 +940,29 @@ static int __init omap_mcbsp_init(void) printk(KERN_ERR "mcbsp: could not acquire dspxor_ck handle.\n"); return PTR_ERR(mcbsp_dspxor_ck); } +#endif +#ifdef CONFIG_ARCH_OMAP2 + mcbsp1_ick = clk_get(0, "mcbsp1_ick"); + if (IS_ERR(mcbsp1_ick)) { + printk(KERN_ERR "mcbsp: could not acquire mcbsp1_ick handle.\n"); + return PTR_ERR(mcbsp1_ick); + } + mcbsp1_fck = clk_get(0, "mcbsp1_fck"); + if (IS_ERR(mcbsp1_fck)) { + printk(KERN_ERR "mcbsp: could not acquire mcbsp1_fck handle.\n"); + return PTR_ERR(mcbsp1_fck); + } + mcbsp2_ick = clk_get(0, "mcbsp2_ick"); + if (IS_ERR(mcbsp2_ick)) { + printk(KERN_ERR "mcbsp: could not acquire mcbsp2_ick handle.\n"); + return PTR_ERR(mcbsp2_ick); + } + mcbsp2_fck = clk_get(0, "mcbsp2_fck"); + if (IS_ERR(mcbsp2_fck)) { + printk(KERN_ERR "mcbsp: could not acquire mcbsp2_fck handle.\n"); + return PTR_ERR(mcbsp2_fck); + } +#endif #ifdef CONFIG_ARCH_OMAP730 if (cpu_is_omap730()) { @@ -718,7 +971,7 @@ static int __init omap_mcbsp_init(void) } #endif #ifdef CONFIG_ARCH_OMAP15XX - if (cpu_is_omap1510()) { + if (cpu_is_omap15xx()) { mcbsp_info = mcbsp_1510; mcbsp_count = ARRAY_SIZE(mcbsp_1510); } @@ -728,6 +981,19 @@ static int __init omap_mcbsp_init(void) mcbsp_info = mcbsp_1610; mcbsp_count = ARRAY_SIZE(mcbsp_1610); } +#endif +#if defined(CONFIG_ARCH_OMAP24XX) + if (cpu_is_omap24xx()) { + mcbsp_info = mcbsp_24xx; + mcbsp_count = ARRAY_SIZE(mcbsp_24xx); + + /* REVISIT: where's the right place? */ + omap2_mcbsp2_mux_setup(); + sys_ck = clk_get(0, "sys_ck"); + sys_clkout = clk_get(0, "sys_clkout"); + clk_set_parent(sys_clkout, sys_ck); + clk_enable(sys_clkout); + } #endif for (i = 0; i < OMAP_MAX_MCBSP_COUNT ; i++) { if (i >= mcbsp_count) { @@ -741,6 +1007,7 @@ static int __init omap_mcbsp_init(void) mcbsp[i].dma_rx_lch = -1; mcbsp[i].io_base = mcbsp_info[i].virt_base; + mcbsp[i].io_type = OMAP_MCBSP_IRQ_IO; /* Default I/O is IRQ based */ mcbsp[i].tx_irq = mcbsp_info[i].tx_irq; mcbsp[i].rx_irq = mcbsp_info[i].rx_irq; mcbsp[i].dma_rx_sync = mcbsp_info[i].dma_rx_sync; @@ -751,11 +1018,11 @@ static int __init omap_mcbsp_init(void) return 0; } - arch_initcall(omap_mcbsp_init); EXPORT_SYMBOL(omap_mcbsp_config); EXPORT_SYMBOL(omap_mcbsp_request); +EXPORT_SYMBOL(omap_mcbsp_set_io_type); EXPORT_SYMBOL(omap_mcbsp_free); EXPORT_SYMBOL(omap_mcbsp_start); EXPORT_SYMBOL(omap_mcbsp_stop); @@ -763,4 +1030,6 @@ EXPORT_SYMBOL(omap_mcbsp_xmit_word); EXPORT_SYMBOL(omap_mcbsp_recv_word); EXPORT_SYMBOL(omap_mcbsp_xmit_buffer); EXPORT_SYMBOL(omap_mcbsp_recv_buffer); +EXPORT_SYMBOL(omap_mcbsp_spi_master_xmit_word_poll); +EXPORT_SYMBOL(omap_mcbsp_spi_master_recv_word_poll); EXPORT_SYMBOL(omap_mcbsp_set_spi_mode); diff --git a/arch/arm/plat-omap/ocpi.c b/arch/arm/plat-omap/ocpi.c index 5cc6775c789..37792d43738 100644 --- a/arch/arm/plat-omap/ocpi.c +++ b/arch/arm/plat-omap/ocpi.c @@ -62,9 +62,6 @@ int ocpi_enable(void) if (!cpu_is_omap16xx()) return -ENODEV; - /* Make sure there's clock for OCPI */ - clk_enable(ocpi_ck); - /* Enable access for OHCI in OCPI */ val = omap_readl(OCPI_PROT); val &= ~0xff; diff --git a/arch/arm/plat-omap/pm.c b/arch/arm/plat-omap/pm.c index 093efd786f2..1a24e2c1071 100644 --- a/arch/arm/plat-omap/pm.c +++ b/arch/arm/plat-omap/pm.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include diff --git a/arch/arm/plat-omap/sram.c b/arch/arm/plat-omap/sram.c index ee82763b02b..b7bf09b1b41 100644 --- a/arch/arm/plat-omap/sram.c +++ b/arch/arm/plat-omap/sram.c @@ -16,24 +16,94 @@ #include #include -#include #include #include #include +#include + #include +#include #define OMAP1_SRAM_PA 0x20000000 #define OMAP1_SRAM_VA 0xd0000000 #define OMAP2_SRAM_PA 0x40200000 +#define OMAP2_SRAM_PUB_PA 0x4020f800 #define OMAP2_SRAM_VA 0xd0000000 +#define OMAP2_SRAM_PUB_VA 0xd0000800 +#if defined(CONFIG_ARCH_OMAP24XX) +#define SRAM_BOOTLOADER_SZ 0x00 +#else #define SRAM_BOOTLOADER_SZ 0x80 +#endif + +#define VA_REQINFOPERM0 IO_ADDRESS(0x68005048) +#define VA_READPERM0 IO_ADDRESS(0x68005050) +#define VA_WRITEPERM0 IO_ADDRESS(0x68005058) +#define VA_CONTROL_STAT IO_ADDRESS(0x480002F8) +#define GP_DEVICE 0x300 +#define TYPE_MASK 0x700 + +#define ROUND_DOWN(value,boundary) ((value) & (~((boundary)-1))) static unsigned long omap_sram_base; static unsigned long omap_sram_size; static unsigned long omap_sram_ceil; +unsigned long omap_fb_sram_start; +unsigned long omap_fb_sram_size; + +/* Depending on the target RAMFS firewall setup, the public usable amount of + * SRAM varies. The default accessable size for all device types is 2k. A GP + * device allows ARM11 but not other initators for full size. This + * functionality seems ok until some nice security API happens. + */ +static int is_sram_locked(void) +{ + int type = 0; + + if (cpu_is_omap242x()) + type = __raw_readl(VA_CONTROL_STAT) & TYPE_MASK; + + if (type == GP_DEVICE) { + /* RAMFW: R/W access to all initators for all qualifier sets */ + if (cpu_is_omap242x()) { + __raw_writel(0xFF, VA_REQINFOPERM0); /* all q-vects */ + __raw_writel(0xCFDE, VA_READPERM0); /* all i-read */ + __raw_writel(0xCFDE, VA_WRITEPERM0); /* all i-write */ + } + return 0; + } else + return 1; /* assume locked with no PPA or security driver */ +} + +void get_fb_sram_conf(unsigned long start_avail, unsigned size_avail, + unsigned long *start, unsigned long *size) +{ + const struct omap_fbmem_config *fbmem_conf; + + fbmem_conf = omap_get_config(OMAP_TAG_FBMEM, struct omap_fbmem_config); + if (fbmem_conf != NULL) { + *start = fbmem_conf->fb_sram_start; + *size = fbmem_conf->fb_sram_size; + } else { + *size = 0; + *start = 0; + } + + if (*size && ( + *start < start_avail || + *start + *size > start_avail + size_avail)) { + printk(KERN_ERR "invalid FB SRAM configuration\n"); + *start = start_avail; + *size = size_avail; + } + + if (*size) + pr_info("Reserving %lu bytes SRAM for frame buffer\n", *size); +} + /* * The amount of SRAM depends on the core type. * Note that we cannot try to test for SRAM here because writes @@ -42,26 +112,45 @@ static unsigned long omap_sram_ceil; */ void __init omap_detect_sram(void) { - if (!cpu_is_omap24xx()) + unsigned long sram_start; + + if (cpu_is_omap24xx()) { + if (is_sram_locked()) { + omap_sram_base = OMAP2_SRAM_PUB_VA; + sram_start = OMAP2_SRAM_PUB_PA; + omap_sram_size = 0x800; /* 2K */ + } else { + omap_sram_base = OMAP2_SRAM_VA; + sram_start = OMAP2_SRAM_PA; + if (cpu_is_omap242x()) + omap_sram_size = 0xa0000; /* 640K */ + else if (cpu_is_omap243x()) + omap_sram_size = 0x10000; /* 64K */ + } + } else { omap_sram_base = OMAP1_SRAM_VA; - else - omap_sram_base = OMAP2_SRAM_VA; - - if (cpu_is_omap730()) - omap_sram_size = 0x32000; /* 200K */ - else if (cpu_is_omap15xx()) - omap_sram_size = 0x30000; /* 192K */ - else if (cpu_is_omap1610() || cpu_is_omap1621() || cpu_is_omap1710()) - omap_sram_size = 0x4000; /* 16K */ - else if (cpu_is_omap1611()) - omap_sram_size = 0x3e800; /* 250K */ - else if (cpu_is_omap2420()) - omap_sram_size = 0xa0014; /* 640K */ - else { - printk(KERN_ERR "Could not detect SRAM size\n"); - omap_sram_size = 0x4000; + sram_start = OMAP1_SRAM_PA; + + if (cpu_is_omap730()) + omap_sram_size = 0x32000; /* 200K */ + else if (cpu_is_omap15xx()) + omap_sram_size = 0x30000; /* 192K */ + else if (cpu_is_omap1610() || cpu_is_omap1621() || + cpu_is_omap1710()) + omap_sram_size = 0x4000; /* 16K */ + else if (cpu_is_omap1611()) + omap_sram_size = 0x3e800; /* 250K */ + else { + printk(KERN_ERR "Could not detect SRAM size\n"); + omap_sram_size = 0x4000; + } } - + get_fb_sram_conf(sram_start + SRAM_BOOTLOADER_SZ, + omap_sram_size - SRAM_BOOTLOADER_SZ, + &omap_fb_sram_start, &omap_fb_sram_size); + if (omap_fb_sram_size) + omap_sram_size -= sram_start + omap_sram_size - + omap_fb_sram_start; omap_sram_ceil = omap_sram_base + omap_sram_size; } @@ -80,12 +169,20 @@ static struct map_desc omap_sram_io_desc[] __initdata = { */ void __init omap_map_sram(void) { + unsigned long base; + if (omap_sram_size == 0) return; if (cpu_is_omap24xx()) { omap_sram_io_desc[0].virtual = OMAP2_SRAM_VA; - omap_sram_io_desc[0].pfn = __phys_to_pfn(OMAP2_SRAM_PA); + + if (is_sram_locked()) + base = OMAP2_SRAM_PUB_PA; + else + base = OMAP2_SRAM_PA; + base = ROUND_DOWN(base, PAGE_SIZE); + omap_sram_io_desc[0].pfn = __phys_to_pfn(base); } omap_sram_io_desc[0].length = (omap_sram_size + PAGE_SIZE-1)/PAGE_SIZE; @@ -93,7 +190,8 @@ void __init omap_map_sram(void) iotable_init(omap_sram_io_desc, ARRAY_SIZE(omap_sram_io_desc)); printk(KERN_INFO "SRAM: Mapped pa 0x%08lx to va 0x%08lx size: 0x%lx\n", - omap_sram_io_desc[0].pfn, omap_sram_io_desc[0].virtual, + __pfn_to_phys(omap_sram_io_desc[0].pfn), + omap_sram_io_desc[0].virtual, omap_sram_io_desc[0].length); /* @@ -118,8 +216,9 @@ void * omap_sram_push(void * start, unsigned long size) printk(KERN_ERR "Not enough space in SRAM\n"); return NULL; } + omap_sram_ceil -= size; - omap_sram_ceil &= ~0x3; + omap_sram_ceil = ROUND_DOWN(omap_sram_ceil, sizeof(void *)); memcpy((void *)omap_sram_ceil, start, size); return (void *)omap_sram_ceil; diff --git a/arch/arm/plat-omap/sti/Makefile b/arch/arm/plat-omap/sti/Makefile new file mode 100644 index 00000000000..6ad9bb37e51 --- /dev/null +++ b/arch/arm/plat-omap/sti/Makefile @@ -0,0 +1,4 @@ +obj-y += sti.o sti-fifo.o + +obj-$(CONFIG_OMAP_STI_CONSOLE) += sti-console.o +obj-$(CONFIG_NET) += sti-netlink.o diff --git a/arch/arm/plat-omap/sti/sti-console.c b/arch/arm/plat-omap/sti/sti-console.c new file mode 100644 index 00000000000..2a2554fbad6 --- /dev/null +++ b/arch/arm/plat-omap/sti/sti-console.c @@ -0,0 +1,185 @@ +/* + * Console support for OMAP STI/XTI + * + * Copyright (C) 2004, 2005, 2006 Nokia Corporation + * Written by: Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "sticon" + +static struct tty_driver *tty_driver; +static DEFINE_SPINLOCK(sti_console_lock); +static unsigned int sti_console_channel; +static int sti_line_done = -1; + +/* + * Write a string to any channel (including terminating NULL) + * Returns number of characters written. + */ +static int sti_channel_puts(const char *string, unsigned int channel, int len) +{ + int count = 0; + + /* + * sti_line_done is needed to determine when we have reached the + * end of the line. write() has a tendency to hand us small + * strings which otherwise end up creating newlines.. we need to + * keep the channel open and in append mode until the line has + * been terminated. + */ + if (sti_line_done != 0) { +#ifdef __LITTLE_ENDIAN + sti_channel_writeb(0xc3, channel); +#else + sti_channel_writeb(0xc0, channel); +#endif + xchg(&sti_line_done, 0); + } + + while (*string && count != len) { + char c = *string++; + + count++; + + if (c == '\n') { + xchg(&sti_line_done, 1); + sti_channel_writeb(0, channel); + break; + } else + sti_channel_writeb(c, channel); + } + + if (sti_line_done) + sti_channel_flush(channel); + + return count; +} + +static int sti_tty_open(struct tty_struct *tty, struct file *filp) +{ + return 0; +} + +static int sti_tty_write(struct tty_struct *tty, + const unsigned char *buf, int len) +{ + unsigned long flags; + int bytes; + + spin_lock_irqsave(&sti_console_lock, flags); + bytes = sti_channel_puts(buf, sti_console_channel, len); + spin_unlock_irqrestore(&sti_console_lock, flags); + + return bytes; +} + +static int sti_tty_write_room(struct tty_struct *tty) +{ + return 0x100000; +} + +static int sti_tty_chars_in_buffer(struct tty_struct *tty) +{ + return 0; +} + +static struct tty_operations sti_tty_ops = { + .open = sti_tty_open, + .write = sti_tty_write, + .write_room = sti_tty_write_room, + .chars_in_buffer = sti_tty_chars_in_buffer, +}; + +static void sti_console_write(struct console *c, const char *s, unsigned n) +{ + unsigned long flags; + + spin_lock_irqsave(&sti_console_lock, flags); + sti_channel_puts(s, sti_console_channel, n); + spin_unlock_irqrestore(&sti_console_lock, flags); +} + +static struct tty_driver *sti_console_device(struct console *c, int *index) +{ + *index = c->index; + return tty_driver; +} + +static int sti_console_setup(struct console *c, char *opts) +{ + return 0; +} + +static struct console sti_console = { + .name = DRV_NAME, + .write = sti_console_write, + .device = sti_console_device, + .setup = sti_console_setup, + .flags = CON_PRINTBUFFER | CON_ENABLED, + .index = -1, +}; + +static int __init sti_console_init(void) +{ + const struct omap_sti_console_config *info; + + info = omap_get_config(OMAP_TAG_STI_CONSOLE, + struct omap_sti_console_config); + if (info && info->enable) { + add_preferred_console(DRV_NAME, 0, NULL); + + sti_console_channel = info->channel; + } + + register_console(&sti_console); + return 0; +} +__initcall(sti_console_init); + +static int __init sti_tty_init(void) +{ + struct tty_driver *tty; + int ret; + + tty = alloc_tty_driver(1); + if (!tty) + return -ENOMEM; + + tty->name = DRV_NAME; + tty->driver_name = DRV_NAME; + tty->major = 0; /* dynamic major */ + tty->minor_start = 0; + tty->type = TTY_DRIVER_TYPE_SYSTEM; + tty->subtype = SYSTEM_TYPE_SYSCONS; + tty->init_termios = tty_std_termios; + + tty_set_operations(tty, &sti_tty_ops); + + ret = tty_register_driver(tty); + if (ret) { + put_tty_driver(tty); + return ret; + } + + tty_driver = tty; + return 0; +} +late_initcall(sti_tty_init); + +module_param(sti_console_channel, uint, 0); +MODULE_PARM_DESC(sti_console_channel, "STI console channel (default 32)"); +MODULE_AUTHOR("Paul Mundt"); +MODULE_DESCRIPTION("OMAP STI console support"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-omap/sti/sti-fifo.c b/arch/arm/plat-omap/sti/sti-fifo.c new file mode 100644 index 00000000000..4069d9b2d39 --- /dev/null +++ b/arch/arm/plat-omap/sti/sti-fifo.c @@ -0,0 +1,117 @@ +/* + * STI RX FIFO Support + * + * Copyright (C) 2005, 2006 Nokia Corporation + * Written by: Paul Mundt and + * Roman Tereshonkov + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include + +#define STI_READ_BUFFER_SIZE 1024 +#define sti_buf_pos(pos) ((sti_crb->bufpos + (pos)) % \ + STI_READ_BUFFER_SIZE) + +static struct sti_cycle_buffer { + int bufpos; + int datalen; + unsigned char *buf; +} *sti_crb; + +/** + * sti_read_packet - STI read packet (read an entire STI packet) + * @buf: Buffer to store the packet. + * @maxsize: Maximum size requested. + * + * This reads in a single completed STI packet from the RX FIFOs and + * places it in @buf for further processing. + * + * The return value is < 0 on error, and >= 0 for the number of bytes + * actually read. As per the STI specification, we require a 0xC1 to + * indicate the end of the packet, and we don't return the packet until + * we've read the entire thing in. + * + * Due to the size of the FIFOs, it's unrealistic to constantly drain + * this for 1 or 2 bytes at a time, so we assemble it here and return + * the whole thing. + */ +int sti_read_packet(unsigned char *buf, int maxsize) +{ + unsigned int pos; + + if (unlikely(!buf)) + return -EINVAL; + if (!sti_crb->datalen) + return 0; + + pos = sti_buf_pos(sti_crb->datalen - 1); + /* End of packet */ + if (sti_crb->buf[pos] == 0xC1) { + int i; + + for (i = 0; i < sti_crb->datalen && i < maxsize; i++) { + pos = sti_buf_pos(i); + buf[i] = sti_crb->buf[pos]; + } + + sti_crb->bufpos = sti_buf_pos(i); + sti_crb->datalen -= i; + + return i; + } + + return 0; +} +EXPORT_SYMBOL(sti_read_packet); + +static void sti_fifo_irq(unsigned long arg) +{ + /* If there is data read it */ + while (!(sti_readl(STI_RX_STATUS) & STI_RXFIFO_EMPTY)) { + unsigned int pos = sti_buf_pos(sti_crb->datalen); + + sti_crb->buf[pos] = sti_readl(STI_RX_DR); + sti_crb->datalen++; + } + + sti_ack_irq(STI_RX_INT); +} + +static int __init sti_fifo_init(void) +{ + unsigned int size; + int ret; + + size = sizeof(struct sti_cycle_buffer) + STI_READ_BUFFER_SIZE; + sti_crb = kmalloc(size, GFP_KERNEL); + if (!sti_crb) + return -ENOMEM; + + sti_crb->bufpos = sti_crb->datalen = 0; + sti_crb->buf = (unsigned char *)(sti_crb + sizeof(*sti_crb)); + + ret = sti_request_irq(STI_RX_INT, sti_fifo_irq, 0); + if (ret != 0) + kfree(sti_crb); + + return ret; +} + +static void __exit sti_fifo_exit(void) +{ + sti_free_irq(STI_RX_INT); + kfree(sti_crb); +} + +module_init(sti_fifo_init); +module_exit(sti_fifo_exit); + +MODULE_AUTHOR("Paul Mundt, Roman Tereshonkov"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-omap/sti/sti-netlink.c b/arch/arm/plat-omap/sti/sti-netlink.c new file mode 100644 index 00000000000..45b8085b94b --- /dev/null +++ b/arch/arm/plat-omap/sti/sti-netlink.c @@ -0,0 +1,157 @@ +/* + * OMAP STI/XTI communications interface via netlink socket. + * + * Copyright (C) 2004, 2005 Nokia Corporation + * Written by: Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include + +static struct sock *sti_sock; +static DECLARE_MUTEX(sti_netlink_sem); + +enum { + STI_READ, + STI_WRITE, +}; + +static int sti_netlink_read(int pid, int seq, void *payload, int size) +{ + struct sk_buff *skb; + struct nlmsghdr *nlh; + int ret, len = NLMSG_SPACE(size); + unsigned char *tail; + + skb = alloc_skb(len, GFP_KERNEL); + if (!skb) + return -ENOMEM; + + tail = skb->tail; + nlh = NLMSG_PUT(skb, pid, seq, STI_READ, + len - (sizeof(struct nlmsghdr))); + nlh->nlmsg_flags = 0; + memcpy(NLMSG_DATA(nlh), payload, size); + nlh->nlmsg_len = skb->tail - tail; + + ret = netlink_unicast(sti_sock, skb, pid, MSG_DONTWAIT); + if (ret > 0) + ret = 0; + + return ret; + +nlmsg_failure: + if (skb) + kfree_skb(skb); + + return -EINVAL; +} + +/* + * We abuse nlmsg_type and nlmsg_flags for our purposes. + * + * The ID is encoded into the upper 8 bits of the nlmsg_type, while the + * channel number is encoded into the upper 8 bits of the nlmsg_flags. + */ +static int sti_netlink_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh) +{ + void *data; + u8 chan, id; + int size, ret = 0, len = 0; + + data = NLMSG_DATA(nlh); + chan = (nlh->nlmsg_flags >> 8) & 0xff; + id = (nlh->nlmsg_type >> 8) & 0xff; + size = (int)(nlh->nlmsg_len - ((char *)data - (char *)nlh)); + + switch (nlh->nlmsg_type & 0xff) { + case STI_WRITE: + sti_channel_write_trace(size, id, data, chan); + break; + case STI_READ: + data = kmalloc(size, GFP_KERNEL); + if (!data) + return -ENOMEM; + memset(data, 0, size); + + len = sti_read_packet(data, size); + ret = sti_netlink_read(NETLINK_CB(skb).pid, nlh->nlmsg_seq, + data, len); + kfree(data); + break; + default: + return -ENOTTY; + } + + return ret; +} + +static int sti_netlink_receive_skb(struct sk_buff *skb) +{ + while (skb->len >= NLMSG_SPACE(0)) { + struct nlmsghdr *nlh; + u32 rlen; + int ret; + + nlh = (struct nlmsghdr *)skb->data; + if (nlh->nlmsg_len < sizeof(struct nlmsghdr) || + skb->len < nlh->nlmsg_len) + break; + + rlen = NLMSG_ALIGN(nlh->nlmsg_len); + if (rlen > skb->len) + rlen = skb->len; + + ret = sti_netlink_receive_msg(skb, nlh); + if (ret) + netlink_ack(skb, nlh, -ret); + else if (nlh->nlmsg_flags & NLM_F_ACK) + netlink_ack(skb, nlh, 0); + + skb_pull(skb, rlen); + } + + return 0; +} + +static void sti_netlink_receive(struct sock *sk, int len) +{ + struct sk_buff *skb; + + if (down_trylock(&sti_netlink_sem)) + return; + + while ((skb = skb_dequeue(&sk->sk_receive_queue))) + if (sti_netlink_receive_skb(skb) && skb->len) + skb_queue_head(&sk->sk_receive_queue, skb); + else + kfree_skb(skb); + + up(&sti_netlink_sem); +} + +static int __init sti_netlink_init(void) +{ + sti_sock = netlink_kernel_create(NETLINK_USERSOCK, 0, + sti_netlink_receive, THIS_MODULE); + if (!sti_sock) { + printk(KERN_ERR "STI: Failed to create netlink socket\n"); + return -ENODEV; + } + + return 0; +} + +module_init(sti_netlink_init); + +MODULE_AUTHOR("Paul Mundt"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("STI netlink-driven communications interface"); diff --git a/arch/arm/plat-omap/sti/sti.c b/arch/arm/plat-omap/sti/sti.c new file mode 100644 index 00000000000..7b04e3899e8 --- /dev/null +++ b/arch/arm/plat-omap/sti/sti.c @@ -0,0 +1,433 @@ +/* + * Support functions for OMAP STI/XTI (Serial Tracing Interface) + * + * Copyright (C) 2004, 2005, 2006 Nokia Corporation + * Written by: Paul Mundt + * + * STI initialization code and channel handling + * from Juha Yrjölä . + * + * XTI initialization + * from Roman Tereshonkov . + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct clk *sti_ck; +unsigned long sti_base, sti_channel_base; +static unsigned long sti_kern_mask = STIEn; +static unsigned long sti_irq_mask = STI_IRQSTATUS_MASK; +static DEFINE_SPINLOCK(sti_lock); + +static struct sti_irqdesc { + irqreturn_t (*func)(unsigned long); + unsigned long data; +} ____cacheline_aligned sti_irq_desc[STI_NR_IRQS]; + +void sti_channel_write_trace(int len, int id, void *data, unsigned int channel) +{ + const u8 *tpntr = data; + + sti_channel_writeb(id, channel); + + if (cpu_is_omap16xx()) + /* Check u32 boundary */ + if (!((u32)data & (STI_PERCHANNEL_SIZE - 1)) && + (len >= STI_PERCHANNEL_SIZE)) { + const u32 *asrc = data; + + do { + sti_channel_writel(cpu_to_be32(*asrc++), + channel); + len -= STI_PERCHANNEL_SIZE; + } while (len >= STI_PERCHANNEL_SIZE); + + tpntr = (const u8 *)asrc; + } + + while (len--) + sti_channel_writeb(*tpntr++, channel); + + sti_channel_flush(channel); +} +EXPORT_SYMBOL(sti_channel_write_trace); + +void sti_enable_irq(unsigned int id) +{ + spin_lock_irq(&sti_lock); + sti_writel(1 << id, STI_IRQSETEN); + spin_unlock_irq(&sti_lock); +} +EXPORT_SYMBOL(sti_enable_irq); + +void sti_disable_irq(unsigned int id) +{ + spin_lock_irq(&sti_lock); + + if (cpu_is_omap16xx()) + sti_writel(1 << id, STI_IRQCLREN); + else if (cpu_is_omap24xx()) + sti_writel(sti_readl(STI_IRQSETEN) & ~(1 << id), STI_IRQSETEN); + else + BUG(); + + spin_unlock_irq(&sti_lock); +} +EXPORT_SYMBOL(sti_disable_irq); + +void sti_ack_irq(unsigned int id) +{ + /* Even though the clear state is 0, we have to write 1 to clear */ + sti_writel(1 << id, STI_IRQSTATUS); +} +EXPORT_SYMBOL(sti_ack_irq); + +int sti_request_irq(unsigned int irq, void *handler, unsigned long arg) +{ + struct sti_irqdesc *desc; + + if (unlikely(!handler || irq > STI_NR_IRQS)) + return -EINVAL; + + desc = sti_irq_desc + irq; + if (unlikely(desc->func)) { + printk(KERN_WARNING "STI: Attempting to request in-use IRQ " + "%d, consider fixing your code..\n", irq); + return -EBUSY; + } + + desc->func = handler; + desc->data = arg; + + sti_enable_irq(irq); + return 0; +} +EXPORT_SYMBOL(sti_request_irq); + +void sti_free_irq(unsigned int irq) +{ + struct sti_irqdesc *desc = sti_irq_desc + irq; + + if (unlikely(irq > STI_NR_IRQS)) + return; + + sti_disable_irq(irq); + + desc->func = NULL; + desc->data = 0; +} +EXPORT_SYMBOL(sti_free_irq); + +/* + * This is a bit heavy, so normally we would defer this to a tasklet. + * Unfortunately tasklets are too slow for the RX FIFO interrupt (and + * possibly some others), so we just do the irqdesc walking here. + */ +static irqreturn_t sti_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int ret = IRQ_NONE; + u16 status; + int i; + + status = sti_readl(STI_IRQSTATUS) & sti_irq_mask; + + for (i = 0; status; i++) { + struct sti_irqdesc *desc = sti_irq_desc + i; + u16 id = 1 << i; + + if (!(status & id)) + continue; + + if (likely(desc && desc->func)) + ret |= desc->func(desc->data); + if (unlikely(ret == IRQ_NONE)) { + printk("STI: spurious interrupt (id %d)\n", id); + sti_disable_irq(i); + sti_ack_irq(i); + ret = IRQ_HANDLED; + } + + status &= ~id; + } + + return IRQ_RETVAL(ret); +} + +static void omap_sti_reset(void) +{ + int i; + + /* Reset STI module */ + sti_writel(0x02, STI_SYSCONFIG); + + /* Wait a while for the STI module to complete its reset */ + for (i = 0; i < 10000; i++) + if (sti_readl(STI_SYSSTATUS) & 1) + break; +} + +static int __init sti_init(void) +{ + char buf[64]; + int i; + + if (cpu_is_omap16xx()) { + /* Release ARM Rhea buses peripherals enable */ + sti_writel(sti_readl(ARM_RSTCT2) | 0x0001, ARM_RSTCT2); + + /* Enable TC1_CK (functional clock) */ + sti_ck = clk_get(NULL, "tc1_ck"); + } else if (cpu_is_omap24xx()) + /* Enable emulation tools clock */ + sti_ck = clk_get(NULL, "emul_ck"); + + if (IS_ERR(sti_ck)) + return PTR_ERR(sti_ck); + + clk_enable(sti_ck); + + /* Reset STI module */ + omap_sti_reset(); + + /* Enable STI */ + sti_trace_enable(MPUCmdEn); + + /* Change to custom serial protocol */ + sti_writel(0x01, STI_SERIAL_CFG); + + /* Set STI clock control register to normal mode */ + sti_writel(0x00, STI_CLK_CTRL); + + i = sti_readl(STI_REVISION); + snprintf(buf, sizeof(buf), "OMAP STI support loaded (HW v%u.%u)\n", + (i >> 4) & 0x0f, i & 0x0f); + printk(KERN_INFO "%s", buf); + + sti_channel_write_trace(strlen(buf), 0xc3, buf, 239); + + return 0; +} + +static void sti_exit(void) +{ + u32 tmp; + + /* + * This should have already been done by reset, but we switch off + * STI entirely just for added sanity.. + */ + tmp = sti_readl(STI_ER); + tmp &= ~STIEn; + sti_writel(tmp, STI_ER); + + clk_disable(sti_ck); + clk_put(sti_ck); +} + +static void __sti_trace_enable(int event) +{ + u32 tmp; + + tmp = sti_readl(STI_ER); + tmp |= sti_kern_mask | event; + sti_writel(tmp, STI_ER); +} + +int sti_trace_enable(int event) +{ + spin_lock_irq(&sti_lock); + sti_kern_mask |= event; + __sti_trace_enable(event); + spin_unlock_irq(&sti_lock); + + return 0; +} +EXPORT_SYMBOL(sti_trace_enable); + +static void __sti_trace_disable(int event) +{ + u32 tmp; + + tmp = sti_readl(STI_DR); + + if (cpu_is_omap16xx()) { + tmp |= event; + tmp &= ~sti_kern_mask; + } else if (cpu_is_omap24xx()) { + tmp &= ~event; + tmp |= sti_kern_mask; + } else + BUG(); + + sti_writel(tmp, STI_DR); +} + +void sti_trace_disable(int event) +{ + spin_lock_irq(&sti_lock); + sti_kern_mask &= ~event; + __sti_trace_disable(event); + spin_unlock_irq(&sti_lock); +} +EXPORT_SYMBOL(sti_trace_disable); + +static ssize_t +sti_trace_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%08lx\n", sti_readl(STI_ER)); +} + +static ssize_t +sti_trace_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int evt = simple_strtoul(buf, NULL, 0); + int mask = ~evt; + + spin_lock_irq(&sti_lock); + __sti_trace_disable(mask); + __sti_trace_enable(evt); + spin_unlock_irq(&sti_lock); + + return count; +} +static DEVICE_ATTR(trace, S_IRUGO | S_IWUSR, sti_trace_show, sti_trace_store); + +static ssize_t +sti_imask_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "0x%04lx\n", sti_irq_mask); +} + +static ssize_t +sti_imask_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + spin_lock_irq(&sti_lock); + sti_irq_mask = simple_strtoul(buf, NULL, 0); + spin_unlock_irq(&sti_lock); + + return count; +} +static DEVICE_ATTR(imask, S_IRUGO | S_IWUSR, sti_imask_show, sti_imask_store); + +static int __devinit sti_probe(struct platform_device *pdev) +{ + struct resource *res, *cres; + int ret; + + if (pdev->num_resources != 3) { + dev_err(&pdev->dev, "invalid number of resources: %d\n", + pdev->num_resources); + return -ENODEV; + } + + /* STI base */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (unlikely(!res)) { + dev_err(&pdev->dev, "invalid mem resource\n"); + return -ENODEV; + } + + /* Channel base */ + cres = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (unlikely(!cres)) { + dev_err(&pdev->dev, "invalid channel mem resource\n"); + return -ENODEV; + } + + ret = device_create_file(&pdev->dev, &dev_attr_trace); + if (unlikely(ret != 0)) + return ret; + + ret = device_create_file(&pdev->dev, &dev_attr_imask); + if (unlikely(ret != 0)) + goto err; + + sti_base = res->start; + + /* + * OMAP 16xx keeps channels in a relatively sane location, + * whereas 24xx maps them much further out, and so they must be + * remapped. + */ + if (cpu_is_omap16xx()) + sti_channel_base = cres->start; + else if (cpu_is_omap24xx()) { + unsigned int size = cres->end - cres->start; + + sti_channel_base = (unsigned long)ioremap(cres->start, size); + if (unlikely(!sti_channel_base)) { + ret = -ENODEV; + goto err_badremap; + } + } + + ret = request_irq(platform_get_irq(pdev, 0), sti_interrupt, + SA_INTERRUPT, "sti", NULL); + if (unlikely(ret != 0)) + goto err_badirq; + + return sti_init(); + +err_badirq: + iounmap((void *)sti_channel_base); +err_badremap: + device_remove_file(&pdev->dev, &dev_attr_imask); +err: + device_remove_file(&pdev->dev, &dev_attr_trace); + + return ret; +} + +static int __devexit sti_remove(struct platform_device *pdev) +{ + unsigned int irq = platform_get_irq(pdev, 0); + + if (cpu_is_omap24xx()) + iounmap((void *)sti_channel_base); + + device_remove_file(&pdev->dev, &dev_attr_trace); + device_remove_file(&pdev->dev, &dev_attr_imask); + free_irq(irq, NULL); + sti_exit(); + + return 0; +} + +static struct platform_driver sti_driver = { + .probe = sti_probe, + .remove = __devexit_p(sti_remove), + .driver = { + .name = "sti", + .owner = THIS_MODULE, + }, +}; + +static int __init sti_module_init(void) +{ + return platform_driver_register(&sti_driver); +} + +static void __exit sti_module_exit(void) +{ + platform_driver_unregister(&sti_driver); +} +subsys_initcall(sti_module_init); +module_exit(sti_module_exit); + +MODULE_AUTHOR("Paul Mundt, Juha Yrjölä, Roman Tereshonkov"); +MODULE_LICENSE("GPL"); diff --git a/arch/arm/plat-omap/timer32k.c b/arch/arm/plat-omap/timer32k.c new file mode 100644 index 00000000000..6ef5468c31b --- /dev/null +++ b/arch/arm/plat-omap/timer32k.c @@ -0,0 +1,325 @@ +/* + * linux/arch/arm/plat-omap/timer32k.c + * + * OMAP 32K Timer + * + * Copyright (C) 2004 - 2005 Nokia Corporation + * Partial timer rewrite and additional dynamic tick timer support by + * Tony Lindgen and + * Tuukka Tikkanen + * + * MPU timer code based on the older MPU timer code for OMAP + * Copyright (C) 2000 RidgeRun, Inc. + * Author: Greg Lonnon + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct sys_timer omap_timer; + +/* + * --------------------------------------------------------------------------- + * 32KHz OS timer + * + * This currently works only on 16xx, as 1510 does not have the continuous + * 32KHz synchronous timer. The 32KHz synchronous timer is used to keep track + * of time in addition to the 32KHz OS timer. Using only the 32KHz OS timer + * on 1510 would be possible, but the timer would not be as accurate as + * with the 32KHz synchronized timer. + * --------------------------------------------------------------------------- + */ + +#if defined(CONFIG_ARCH_OMAP16XX) +#define TIMER_32K_SYNCHRONIZED 0xfffbc410 +#elif defined(CONFIG_ARCH_OMAP24XX) +#define TIMER_32K_SYNCHRONIZED 0x48004010 +#else +#error OMAP 32KHz timer does not currently work on 15XX! +#endif + +/* 16xx specific defines */ +#define OMAP1_32K_TIMER_BASE 0xfffb9000 +#define OMAP1_32K_TIMER_CR 0x08 +#define OMAP1_32K_TIMER_TVR 0x00 +#define OMAP1_32K_TIMER_TCR 0x04 + +/* 24xx specific defines */ +#define OMAP2_GP_TIMER_BASE 0x48028000 +#define CM_CLKSEL_WKUP 0x48008440 +#define GP_TIMER_TIDR 0x00 +#define GP_TIMER_TISR 0x18 +#define GP_TIMER_TIER 0x1c +#define GP_TIMER_TCLR 0x24 +#define GP_TIMER_TCRR 0x28 +#define GP_TIMER_TLDR 0x2c +#define GP_TIMER_TTGR 0x30 +#define GP_TIMER_TSICR 0x40 + +#define OMAP_32K_TICKS_PER_HZ (32768 / HZ) + +/* + * TRM says 1 / HZ = ( TVR + 1) / 32768, so TRV = (32768 / HZ) - 1 + * so with HZ = 128, TVR = 255. + */ +#define OMAP_32K_TIMER_TICK_PERIOD ((32768 / HZ) - 1) + +#define JIFFIES_TO_HW_TICKS(nr_jiffies, clock_rate) \ + (((nr_jiffies) * (clock_rate)) / HZ) + +static inline void omap_32k_timer_write(int val, int reg) +{ + if (cpu_class_is_omap1()) + omap_writew(val, OMAP1_32K_TIMER_BASE + reg); + + if (cpu_is_omap24xx()) + omap_writel(val, OMAP2_GP_TIMER_BASE + reg); +} + +static inline unsigned long omap_32k_timer_read(int reg) +{ + if (cpu_class_is_omap1()) + return omap_readl(OMAP1_32K_TIMER_BASE + reg) & 0xffffff; + + if (cpu_is_omap24xx()) + return omap_readl(OMAP2_GP_TIMER_BASE + reg); +} + +/* + * The 32KHz synchronized timer is an additional timer on 16xx. + * It is always running. + */ +static inline unsigned long omap_32k_sync_timer_read(void) +{ + return omap_readl(TIMER_32K_SYNCHRONIZED); +} + +static inline void omap_32k_timer_start(unsigned long load_val) +{ + if (cpu_class_is_omap1()) { + omap_32k_timer_write(load_val, OMAP1_32K_TIMER_TVR); + omap_32k_timer_write(0x0f, OMAP1_32K_TIMER_CR); + } + + if (cpu_is_omap24xx()) { + omap_32k_timer_write(0xffffffff - load_val, GP_TIMER_TCRR); + omap_32k_timer_write((1 << 1), GP_TIMER_TIER); + omap_32k_timer_write((1 << 1) | 1, GP_TIMER_TCLR); + } +} + +static inline void omap_32k_timer_stop(void) +{ + if (cpu_class_is_omap1()) + omap_32k_timer_write(0x0, OMAP1_32K_TIMER_CR); + + if (cpu_is_omap24xx()) + omap_32k_timer_write(0x0, GP_TIMER_TCLR); +} + +/* + * Rounds down to nearest usec. Note that this will overflow for larger values. + */ +static inline unsigned long omap_32k_ticks_to_usecs(unsigned long ticks_32k) +{ + return (ticks_32k * 5*5*5*5*5*5) >> 9; +} + +/* + * Rounds down to nearest nsec. + */ +static inline unsigned long long +omap_32k_ticks_to_nsecs(unsigned long ticks_32k) +{ + return (unsigned long long) ticks_32k * 1000 * 5*5*5*5*5*5 >> 9; +} + +static unsigned long omap_32k_last_tick = 0; + +/* + * Returns elapsed usecs since last 32k timer interrupt + */ +static unsigned long omap_32k_timer_gettimeoffset(void) +{ + unsigned long now = omap_32k_sync_timer_read(); + return omap_32k_ticks_to_usecs(now - omap_32k_last_tick); +} + +/* + * Returns current time from boot in nsecs. It's OK for this to wrap + * around for now, as it's just a relative time stamp. + */ +unsigned long long sched_clock(void) +{ + return omap_32k_ticks_to_nsecs(omap_32k_sync_timer_read()); +} + +/* + * Timer interrupt for 32KHz timer. When dynamic tick is enabled, this + * function is also called from other interrupts to remove latency + * issues with dynamic tick. In the dynamic tick case, we need to lock + * with irqsave. + */ +static irqreturn_t omap_32k_timer_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + unsigned long flags; + unsigned long now; + + write_seqlock_irqsave(&xtime_lock, flags); + + if (cpu_is_omap24xx()) { + u32 status = omap_32k_timer_read(GP_TIMER_TISR); + omap_32k_timer_write(status, GP_TIMER_TISR); + } + + now = omap_32k_sync_timer_read(); + + while (now - omap_32k_last_tick >= OMAP_32K_TICKS_PER_HZ) { + omap_32k_last_tick += OMAP_32K_TICKS_PER_HZ; + timer_tick(regs); + } + + /* Restart timer so we don't drift off due to modulo or dynamic tick. + * By default we program the next timer to be continuous to avoid + * latencies during high system load. During dynamic tick operation the + * continuous timer can be overridden from pm_idle to be longer. + */ + omap_32k_timer_start(omap_32k_last_tick + OMAP_32K_TICKS_PER_HZ - now); + write_sequnlock_irqrestore(&xtime_lock, flags); + + return IRQ_HANDLED; +} + +#ifdef CONFIG_NO_IDLE_HZ +/* + * Programs the next timer interrupt needed. Called when dynamic tick is + * enabled, and to reprogram the ticks to skip from pm_idle. Note that + * we can keep the timer continuous, and don't need to set it to run in + * one-shot mode. This is because the timer will get reprogrammed again + * after next interrupt. + */ +void omap_32k_timer_reprogram(unsigned long next_tick) +{ + omap_32k_timer_start(JIFFIES_TO_HW_TICKS(next_tick, 32768) + 1); +} + +static struct irqaction omap_32k_timer_irq; +extern struct timer_update_handler timer_update; + +static int omap_32k_timer_enable_dyn_tick(void) +{ + /* No need to reprogram timer, just use the next interrupt */ + return 0; +} + +static int omap_32k_timer_disable_dyn_tick(void) +{ + omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); + return 0; +} + +static struct dyn_tick_timer omap_dyn_tick_timer = { + .enable = omap_32k_timer_enable_dyn_tick, + .disable = omap_32k_timer_disable_dyn_tick, + .reprogram = omap_32k_timer_reprogram, + .handler = omap_32k_timer_interrupt, +}; +#endif /* CONFIG_NO_IDLE_HZ */ + +static struct irqaction omap_32k_timer_irq = { + .name = "32KHz timer", + .flags = SA_INTERRUPT | SA_TIMER, + .handler = omap_32k_timer_interrupt, +}; + +static struct clk * gpt1_ick; +static struct clk * gpt1_fck; + +static __init void omap_init_32k_timer(void) +{ +#ifdef CONFIG_NO_IDLE_HZ + omap_timer.dyn_tick = &omap_dyn_tick_timer; +#endif + + if (cpu_class_is_omap1()) + setup_irq(INT_OS_TIMER, &omap_32k_timer_irq); + if (cpu_is_omap24xx()) + setup_irq(37, &omap_32k_timer_irq); + omap_timer.offset = omap_32k_timer_gettimeoffset; + omap_32k_last_tick = omap_32k_sync_timer_read(); + + /* REVISIT: Check 24xx TIOCP_CFG settings after idle works */ + if (cpu_is_omap24xx()) { + omap_32k_timer_write(0, GP_TIMER_TCLR); + omap_writel(0, CM_CLKSEL_WKUP); /* 32KHz clock source */ + + gpt1_ick = clk_get(NULL, "gpt1_ick"); + if (IS_ERR(gpt1_ick)) + printk(KERN_ERR "Could not get gpt1_ick\n"); + else + clk_enable(gpt1_ick); + + gpt1_fck = clk_get(NULL, "gpt1_fck"); + if (IS_ERR(gpt1_fck)) + printk(KERN_ERR "Could not get gpt1_fck\n"); + else + clk_enable(gpt1_fck); + + mdelay(100); /* Wait for clocks to stabilize */ + + omap_32k_timer_write(0x7, GP_TIMER_TISR); + } + + omap_32k_timer_start(OMAP_32K_TIMER_TICK_PERIOD); +} + +/* + * --------------------------------------------------------------------------- + * Timer initialization + * --------------------------------------------------------------------------- + */ +static void __init omap_timer_init(void) +{ + omap_init_32k_timer(); +} + +struct sys_timer omap_timer = { + .init = omap_timer_init, + .offset = NULL, /* Initialized later */ +}; diff --git a/drivers/Kconfig b/drivers/Kconfig index bddf431bbb7..f5e28dfd821 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -70,4 +70,6 @@ source "drivers/sn/Kconfig" source "drivers/edac/Kconfig" +source "drivers/ssi/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 5c69b86db62..4a13ac995ca 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -30,7 +30,9 @@ obj-$(CONFIG_FB_INTEL) += video/intelfb/ obj-$(CONFIG_SERIO) += input/serio/ obj-y += serial/ obj-$(CONFIG_PARPORT) += parport/ -obj-y += base/ block/ misc/ mfd/ net/ media/ +obj-y += base/ block/ misc/ mfd/ net/ +obj-$(CONFIG_I2C) += i2c/ +obj-y += media/ ssi/ cbus/ obj-$(CONFIG_NUBUS) += nubus/ obj-$(CONFIG_ATM) += atm/ obj-$(CONFIG_PPC_PMAC) += macintosh/ @@ -56,7 +58,6 @@ obj-$(CONFIG_USB_GADGET) += usb/gadget/ obj-$(CONFIG_GAMEPORT) += input/gameport/ obj-$(CONFIG_INPUT) += input/ obj-$(CONFIG_I2O) += message/ -obj-$(CONFIG_I2C) += i2c/ obj-$(CONFIG_W1) += w1/ obj-$(CONFIG_HWMON) += hwmon/ obj-$(CONFIG_PHONE) += telephony/ diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig index b9fbe6e7f9a..9abcd6206ac 100644 --- a/drivers/bluetooth/Kconfig +++ b/drivers/bluetooth/Kconfig @@ -147,6 +147,17 @@ config BT_HCIBTUART Say Y here to compile support for HCI UART devices into the kernel or say M to compile it as module (btuart_cs). +config BT_HCIBRF6150 + tristate "HCI TI BRF6150 driver with H4 extensions" + depends on BT && ARCH_OMAP + help + Bluetooth HCI driver for TI BRF6150 with H4 extensions. + This driver provides support for BRF6150 Bluetooth chip + with vendor-specific H4 extensions. + + Say Y here to compile support for TI BRF6150 devices into the + kernel or say M to compile it as module (brf6150). + config BT_HCIVHCI tristate "HCI VHCI (Virtual HCI device) driver" help diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile index 08c10e178e0..18aa038db13 100644 --- a/drivers/bluetooth/Makefile +++ b/drivers/bluetooth/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_BT_HCIDTL1) += dtl1_cs.o obj-$(CONFIG_BT_HCIBT3C) += bt3c_cs.o obj-$(CONFIG_BT_HCIBLUECARD) += bluecard_cs.o obj-$(CONFIG_BT_HCIBTUART) += btuart_cs.o +obj-$(CONFIG_BT_HCIBRF6150) += brf6150.o hci_uart-y := hci_ldisc.o hci_uart-$(CONFIG_BT_HCIUART_H4) += hci_h4.o diff --git a/drivers/bluetooth/brf6150.c b/drivers/bluetooth/brf6150.c new file mode 100644 index 00000000000..3e0faa1262b --- /dev/null +++ b/drivers/bluetooth/brf6150.c @@ -0,0 +1,1042 @@ +/* + * linux/drivers/bluetooth/brf6150/brf6150.c + * + * Copyright (C) 2005 Nokia Corporation + * Written by Ville Tervo + * + * 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 +#include + +#include +#include +#include + +#include "brf6150.h" + +#if 0 +#define NBT_DBG(fmt, arg...) printk("%s: " fmt "" , __FUNCTION__ , ## arg) +#else +#define NBT_DBG(...) +#endif + +#if 0 +#define NBT_DBG_FW(fmt, arg...) printk("%s: " fmt "" , __FUNCTION__ , ## arg) +#else +#define NBT_DBG_FW(...) +#endif + +#if 0 +#define NBT_DBG_POWER(fmt, arg...) printk("%s: " fmt "" , __FUNCTION__ , ## arg) +#else +#define NBT_DBG_POWER(...) +#endif + +#if 0 +#define NBT_DBG_TRANSFER(fmt, arg...) printk("%s: " fmt "" , __FUNCTION__ , ## arg) +#else +#define NBT_DBG_TRANSFER(...) +#endif + +#if 0 +#define NBT_DBG_TRANSFER_NF(fmt, arg...) printk(fmt "" , ## arg) +#else +#define NBT_DBG_TRANSFER_NF(...) +#endif + +#define PM_TIMEOUT (2000) + +static void brf6150_device_release(struct device *dev); +static struct brf6150_info *exit_info; + +static struct platform_device brf6150_device = { + .name = BT_DEVICE, + .id = -1, + .num_resources = 0, + .dev = { + .release = brf6150_device_release, + } +}; + +static struct device_driver brf6150_driver = { + .name = BT_DRIVER, + .bus = &platform_bus_type, +}; + +static inline void brf6150_outb(struct brf6150_info *info, unsigned int offset, u8 val) +{ + outb(val, info->uart_base + (offset << 2)); +} + +static inline u8 brf6150_inb(struct brf6150_info *info, unsigned int offset) +{ + return inb(info->uart_base + (offset << 2)); +} + +static void brf6150_set_rts(struct brf6150_info *info, int active) +{ + u8 b; + + b = brf6150_inb(info, UART_MCR); + if (active) + b |= UART_MCR_RTS; + else + b &= ~UART_MCR_RTS; + brf6150_outb(info, UART_MCR, b); +} + +static void brf6150_wait_for_cts(struct brf6150_info *info, int active, + int timeout_ms) +{ + int okay; + unsigned long timeout; + + okay = 0; + timeout = jiffies + msecs_to_jiffies(timeout_ms); + for (;;) { + int state; + + state = brf6150_inb(info, UART_MSR) & UART_MSR_CTS; + if (active) { + if (state) + break; + } else { + if (!state) + break; + } + if (jiffies > timeout) + break; + } +} + +static inline void brf6150_set_auto_ctsrts(struct brf6150_info *info, int on) +{ + u8 lcr, b; + + lcr = brf6150_inb(info, UART_LCR); + brf6150_outb(info, UART_LCR, 0xbf); + b = brf6150_inb(info, UART_EFR); + if (on) + b |= UART_EFR_CTS | UART_EFR_RTS; + else + b &= ~(UART_EFR_CTS | UART_EFR_RTS); + brf6150_outb(info, UART_EFR, b); + brf6150_outb(info, UART_LCR, lcr); +} + +static inline void brf6150_enable_pm_rx(struct brf6150_info *info) +{ + if (info->pm_enabled) { + info->rx_pm_enabled = 1; + } +} + +static inline void brf6150_disable_pm_rx(struct brf6150_info *info) +{ + if (info->pm_enabled) { + info->rx_pm_enabled = 0; + } +} + +static void brf6150_enable_pm_tx(struct brf6150_info *info) +{ + if (info->pm_enabled) { + mod_timer(&info->pm_timer, jiffies + msecs_to_jiffies(PM_TIMEOUT)); + info->tx_pm_enabled = 1; + } +} + +static void brf6150_disable_pm_tx(struct brf6150_info *info) +{ + if (info->pm_enabled) { + info->tx_pm_enabled = 0; + omap_set_gpio_dataout(info->btinfo->bt_wakeup_gpio, 1); + } + if (omap_get_gpio_datain(info->btinfo->host_wakeup_gpio)) + tasklet_schedule(&info->tx_task); +} + +static void brf6150_pm_timer(unsigned long data) +{ + struct brf6150_info *info; + + info = (struct brf6150_info *)data; + if (info->tx_pm_enabled && info->rx_pm_enabled && !test_bit(HCI_INQUIRY, &info->hdev->flags)) + omap_set_gpio_dataout(info->btinfo->bt_wakeup_gpio, 0); + else + mod_timer(&info->pm_timer, jiffies + msecs_to_jiffies(PM_TIMEOUT)); +} + +static int brf6150_change_speed(struct brf6150_info *info, unsigned long speed) +{ + unsigned int divisor; + u8 lcr, mdr1; + + NBT_DBG("Setting speed %lu\n", speed); + + if (speed >= 460800) { + divisor = UART_CLOCK / 13 / speed; + mdr1 = 3; + } else { + divisor = UART_CLOCK / 16 / speed; + mdr1 = 0; + } + + brf6150_outb(info, UART_OMAP_MDR1, 7); /* Make sure UART mode is disabled */ + lcr = brf6150_inb(info, UART_LCR); + brf6150_outb(info, UART_LCR, UART_LCR_DLAB); /* Set DLAB */ + brf6150_outb(info, UART_DLL, divisor & 0xff); /* Set speed */ + brf6150_outb(info, UART_DLM, divisor >> 8); + brf6150_outb(info, UART_LCR, lcr); + brf6150_outb(info, UART_OMAP_MDR1, mdr1); /* Make sure UART mode is enabled */ + + return 0; +} + +/* Firmware handling */ +static int brf6150_open_firmware(struct brf6150_info *info) +{ + int err; + + info->fw_pos = 0; + err = request_firmware(&info->fw_entry, "brf6150fw.bin", &brf6150_device.dev); + + return err; +} + +static struct sk_buff *brf6150_read_fw_cmd(struct brf6150_info *info, int how) +{ + struct sk_buff *skb; + unsigned int cmd_len; + + if (info->fw_pos >= info->fw_entry->size) { + return NULL; + } + + cmd_len = info->fw_entry->data[info->fw_pos++]; + if (!cmd_len) + return NULL; + + if (info->fw_pos + cmd_len > info->fw_entry->size) { + printk(KERN_WARNING "Corrupted firmware image\n"); + return NULL; + } + + skb = bt_skb_alloc(cmd_len, how); + if (!skb) { + printk(KERN_WARNING "Cannot reserve memory for buffer\n"); + return NULL; + } + memcpy(skb_put(skb, cmd_len), &info->fw_entry->data[info->fw_pos], cmd_len); + + info->fw_pos += cmd_len; + + return skb; +} + +static int brf6150_close_firmware(struct brf6150_info *info) +{ + release_firmware(info->fw_entry); + return 0; +} + +static int brf6150_send_alive_packet(struct brf6150_info *info) +{ + struct sk_buff *skb; + + NBT_DBG("Sending alive packet\n"); + skb = brf6150_read_fw_cmd(info, GFP_ATOMIC); + if (!skb) { + printk(KERN_WARNING "Cannot read alive command"); + return -1; + } + + clk_enable(info->uart_ck); + skb_queue_tail(&info->txq, skb); + tasklet_schedule(&info->tx_task); + + NBT_DBG("Alive packet sent\n"); + return 0; +} + +static void brf6150_alive_packet(struct brf6150_info *info, struct sk_buff *skb) +{ + NBT_DBG("Received alive packet\n"); + if (skb->data[1] == 0xCC) { + complete(&info->init_completion); + } + + kfree_skb(skb); +} + +static int brf6150_send_negotiation(struct brf6150_info *info) +{ + struct sk_buff *skb; + NBT_DBG("Sending negotiation..\n"); + + brf6150_change_speed(info, INIT_SPEED); + + skb = brf6150_read_fw_cmd(info, GFP_KERNEL); + + if (!skb) { + printk(KERN_WARNING "Cannot read negoatiation message"); + return -1; + } + + clk_enable(info->uart_ck); + skb_queue_tail(&info->txq, skb); + tasklet_schedule(&info->tx_task); + + + NBT_DBG("Negotiation sent\n"); + return 0; +} + +static void brf6150_negotiation_packet(struct brf6150_info *info, + struct sk_buff *skb) +{ + if (skb->data[1] == 0x20) { + /* Change to operational settings */ + brf6150_set_rts(info, 0); + brf6150_wait_for_cts(info, 0, 100); + brf6150_change_speed(info, MAX_BAUD_RATE); + brf6150_set_rts(info, 1); + brf6150_wait_for_cts(info, 1, 100); + brf6150_set_auto_ctsrts(info, 1); + brf6150_send_alive_packet(info); + } else { + printk(KERN_WARNING "Could not negotiate brf6150 settings\n"); + } + kfree_skb(skb); +} + +static int brf6150_get_hdr_len(u8 pkt_type) +{ + long retval; + + switch (pkt_type) { + case H4_EVT_PKT: + retval = HCI_EVENT_HDR_SIZE; + break; + case H4_ACL_PKT: + retval = HCI_ACL_HDR_SIZE; + break; + case H4_SCO_PKT: + retval = HCI_SCO_HDR_SIZE; + break; + case H4_NEG_PKT: + retval = 9; + break; + case H4_ALIVE_PKT: + retval = 3; + break; + default: + printk(KERN_ERR "brf6150: Unknown H4 packet"); + retval = -1; + break; + } + + return retval; +} + +static unsigned int brf6150_get_data_len(struct brf6150_info *info, + struct sk_buff *skb) +{ + long retval = -1; + struct hci_event_hdr *evt_hdr; + struct hci_acl_hdr *acl_hdr; + struct hci_sco_hdr *sco_hdr; + + switch (bt_cb(skb)->pkt_type) { + case H4_EVT_PKT: + evt_hdr = (struct hci_event_hdr *)skb->data; + retval = evt_hdr->plen; + break; + case H4_ACL_PKT: + acl_hdr = (struct hci_acl_hdr *)skb->data; + retval = le16_to_cpu(acl_hdr->dlen); + break; + case H4_SCO_PKT: + sco_hdr = (struct hci_sco_hdr *)skb->data; + retval = sco_hdr->dlen; + break; + case H4_NEG_PKT: + retval = 0; + break; + case H4_ALIVE_PKT: + retval = 0; + break; + } + + return retval; +} + +static void brf6150_parse_fw_event(struct brf6150_info *info) +{ + struct hci_fw_event *ev; + + if (bt_cb(info->rx_skb)->pkt_type != H4_EVT_PKT) { + printk(KERN_WARNING "Got non event fw packet.\n"); + info->fw_error = 1; + return; + } + + ev = (struct hci_fw_event *)info->rx_skb->data; + if (ev->hev.evt != HCI_EV_CMD_COMPLETE) { + printk(KERN_WARNING "Got non cmd complete fw event\n"); + info->fw_error = 1; + return; + } + + if (ev->status != 0) { + printk(KERN_WARNING "Got error status from fw command\n"); + info->fw_error = 1; + return; + } + + complete(&info->fw_completion); +} + +static inline void brf6150_recv_frame(struct brf6150_info *info, + struct sk_buff *skb) +{ + if (unlikely(!test_bit(HCI_RUNNING, &info->hdev->flags))) { + NBT_DBG("fw_event\n"); + brf6150_parse_fw_event(info); + kfree_skb(skb); + } else { + hci_recv_frame(skb); + if (!(brf6150_inb(info, UART_LSR) & UART_LSR_DR)) + brf6150_enable_pm_rx(info); + NBT_DBG("Frame sent to upper layer\n"); + } + +} + +static inline void brf6150_rx(struct brf6150_info *info) +{ + u8 byte; + + NBT_DBG_TRANSFER("rx_tasklet woke up\ndata "); + + while (brf6150_inb(info, UART_LSR) & UART_LSR_DR) { + if (info->rx_skb == NULL) { + info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC); + if (!info->rx_skb) { + printk(KERN_WARNING "brf6150: Can't allocate memory for new packet\n"); + return; + } + info->rx_state = WAIT_FOR_PKT_TYPE; + info->rx_skb->dev = (void *)info->hdev; + brf6150_disable_pm_rx(info); + clk_enable(info->uart_ck); + } + + byte = brf6150_inb(info, UART_RX); + if (info->garbage_bytes) { + info->garbage_bytes--; + info->hdev->stat.err_rx++; + continue; + } + info->hdev->stat.byte_rx++; + NBT_DBG_TRANSFER_NF("0x%.2x ", byte); + switch (info->rx_state) { + case WAIT_FOR_PKT_TYPE: + bt_cb(info->rx_skb)->pkt_type = byte; + info->rx_count = brf6150_get_hdr_len(byte); + if (info->rx_count >= 0) { + info->rx_state = WAIT_FOR_HEADER; + } else { + info->hdev->stat.err_rx++; + kfree_skb(info->rx_skb); + info->rx_skb = NULL; + clk_disable(info->uart_ck); + } + break; + case WAIT_FOR_HEADER: + info->rx_count--; + *skb_put(info->rx_skb, 1) = byte; + if (info->rx_count == 0) { + info->rx_count = brf6150_get_data_len(info, info->rx_skb); + if (info->rx_count > skb_tailroom(info->rx_skb)) { + printk(KERN_WARNING "brf6150: Frame is %ld bytes too long.\n", + info->rx_count - skb_tailroom(info->rx_skb)); + info->rx_skb = NULL; + info->garbage_bytes = info->rx_count - skb_tailroom(info->rx_skb); + clk_disable(info->uart_ck); + break; + } + info->rx_state = WAIT_FOR_DATA; + if (bt_cb(info->rx_skb)->pkt_type == H4_NEG_PKT) { + brf6150_negotiation_packet(info, info->rx_skb); + info->rx_skb = NULL; + clk_disable(info->uart_ck); + return; + } + if (bt_cb(info->rx_skb)->pkt_type == H4_ALIVE_PKT) { + brf6150_alive_packet(info, info->rx_skb); + info->rx_skb = NULL; + clk_disable(info->uart_ck); + return; + } + } + break; + case WAIT_FOR_DATA: + info->rx_count--; + *skb_put(info->rx_skb, 1) = byte; + if (info->rx_count == 0) { + brf6150_recv_frame(info, info->rx_skb); + info->rx_skb = NULL; + clk_disable(info->uart_ck); + } + break; + default: + WARN_ON(1); + break; + } + } + + NBT_DBG_TRANSFER_NF("\n"); +} + +static void brf6150_tx_tasklet(unsigned long data) +{ + unsigned int sent = 0; + unsigned long flags; + struct sk_buff *skb; + struct brf6150_info *info = (struct brf6150_info *)data; + + NBT_DBG_TRANSFER("tx_tasklet woke up\n data "); + + skb = skb_dequeue(&info->txq); + if (!skb) { + /* No data in buffer */ + brf6150_enable_pm_tx(info); + return; + } + + /* Copy data to tx fifo */ + while (!(brf6150_inb(info, UART_OMAP_SSR) & UART_OMAP_SSR_TXFULL) && + (sent < skb->len)) { + NBT_DBG_TRANSFER_NF("0x%.2x ", skb->data[sent]); + brf6150_outb(info, UART_TX, skb->data[sent]); + sent++; + } + + info->hdev->stat.byte_tx += sent; + NBT_DBG_TRANSFER_NF("\n"); + if (skb->len == sent) { + kfree_skb(skb); + clk_disable(info->uart_ck); + } else { + skb_pull(skb, sent); + skb_queue_head(&info->txq, skb); + } + + spin_lock_irqsave(&info->lock, flags); + brf6150_outb(info, UART_IER, brf6150_inb(info, UART_IER) | UART_IER_THRI); + spin_unlock_irqrestore(&info->lock, flags); +} + +static irqreturn_t brf6150_interrupt(int irq, void *data, struct pt_regs *regs) +{ + struct brf6150_info *info = (struct brf6150_info *)data; + u8 iir, msr; + int ret; + unsigned long flags; + + ret = IRQ_NONE; + + clk_enable(info->uart_ck); + iir = brf6150_inb(info, UART_IIR); + if (iir & UART_IIR_NO_INT) { + printk("Interrupt but no reason irq 0x%.2x\n", iir); + clk_disable(info->uart_ck); + return IRQ_HANDLED; + } + + NBT_DBG("In interrupt handler iir 0x%.2x\n", iir); + + iir &= UART_IIR_ID; + + if (iir == UART_IIR_MSI) { + msr = brf6150_inb(info, UART_MSR); + ret = IRQ_HANDLED; + } + if (iir == UART_IIR_RLSI) { + brf6150_inb(info, UART_RX); + brf6150_inb(info, UART_LSR); + ret = IRQ_HANDLED; + } + + if (iir == UART_IIR_RDI) { + brf6150_rx(info); + ret = IRQ_HANDLED; + } + + if (iir == UART_IIR_THRI) { + spin_lock_irqsave(&info->lock, flags); + brf6150_outb(info, UART_IER, brf6150_inb(info, UART_IER) & ~UART_IER_THRI); + spin_unlock_irqrestore(&info->lock, flags); + tasklet_schedule(&info->tx_task); + ret = IRQ_HANDLED; + } + + clk_disable(info->uart_ck); + return ret; +} + +static irqreturn_t brf6150_wakeup_interrupt(int irq, void *dev_inst, + struct pt_regs *regs) +{ + struct brf6150_info *info = dev_inst; + int should_wakeup; + unsigned long flags; + + spin_lock_irqsave(&info->lock, flags); + should_wakeup = omap_get_gpio_datain(info->btinfo->host_wakeup_gpio); + NBT_DBG_POWER("gpio interrupt %d\n", should_wakeup); + if (should_wakeup) { + clk_enable(info->uart_ck); + brf6150_set_auto_ctsrts(info, 1); + brf6150_rx(info); + tasklet_schedule(&info->tx_task); + } else { + brf6150_set_auto_ctsrts(info, 0); + brf6150_set_rts(info, 0); + clk_disable(info->uart_ck); + } + + spin_unlock_irqrestore(&info->lock, flags); + return IRQ_HANDLED; +} + +static int brf6150_init_uart(struct brf6150_info *info) +{ + int count = 0; + + /* Reset the UART */ + brf6150_outb(info, UART_OMAP_SYSC, UART_SYSC_OMAP_RESET); + while (!(brf6150_inb(info, UART_OMAP_SYSS) & UART_SYSS_RESETDONE)) { + if (count++ > 100) { + printk(KERN_ERR "brf6150: UART reset timeout\n"); + return -1; + } + udelay(1); + } + + /* Enable and setup FIFO */ + brf6150_outb(info, UART_LCR, UART_LCR_WLEN8); + brf6150_outb(info, UART_OMAP_MDR1, 0x00); /* Make sure UART mode is enabled */ + brf6150_outb(info, UART_OMAP_SCR, 0x00); + brf6150_outb(info, UART_EFR, brf6150_inb(info, UART_EFR) | UART_EFR_ECB); + brf6150_outb(info, UART_MCR, brf6150_inb(info, UART_MCR) | UART_MCR_TCRTLR); + brf6150_outb(info, UART_TI752_TLR, 0xff); + brf6150_outb(info, UART_TI752_TCR, 0x1f); + brf6150_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + brf6150_outb(info, UART_IER, UART_IER_RDI); + + return 0; +} + +static int brf6150_reset(struct brf6150_info *info) +{ + omap_set_gpio_dataout(info->btinfo->bt_wakeup_gpio, 0); + omap_set_gpio_dataout(info->btinfo->reset_gpio, 0); + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(msecs_to_jiffies(10)); + omap_set_gpio_dataout(info->btinfo->bt_wakeup_gpio, 1); + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(msecs_to_jiffies(100)); + omap_set_gpio_dataout(info->btinfo->reset_gpio, 1); + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(msecs_to_jiffies(100)); + + return 0; +} + +static int brf6150_send_firmware(struct brf6150_info *info) +{ + struct sk_buff *skb; + + init_completion(&info->fw_completion); + info->fw_error = 0; + + while ((skb = brf6150_read_fw_cmd(info, GFP_KERNEL)) != NULL) { + clk_enable(info->uart_ck); + skb_queue_tail(&info->txq, skb); + tasklet_schedule(&info->tx_task); + + if (!wait_for_completion_timeout(&info->fw_completion, HZ)) { + return -1; + } + + if (info->fw_error) { + return -1; + } + } + NBT_DBG_FW("Firmware sent\n"); + + return 0; + +} + +/* hci callback functions */ +static int brf6150_hci_flush(struct hci_dev *hdev) +{ + struct brf6150_info *info; + info = hdev->driver_data; + + skb_queue_purge(&info->txq); + + return 0; +} + +static int brf6150_hci_open(struct hci_dev *hdev) +{ + struct brf6150_info *info; + int err; + + info = hdev->driver_data; + + if (test_bit(HCI_RUNNING, &hdev->flags)) + return 0; + + if (brf6150_open_firmware(info) < 0) { + printk("Cannot open firmware\n"); + return -1; + } + + info->rx_state = WAIT_FOR_PKT_TYPE; + info->rx_count = 0; + info->garbage_bytes = 0; + info->rx_skb = NULL; + info->pm_enabled = 0; + set_irq_type(OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio), IRQT_NOEDGE); + init_completion(&info->fw_completion); + + clk_enable(info->uart_ck); + + brf6150_init_uart(info); + brf6150_set_auto_ctsrts(info, 0); + brf6150_set_rts(info, 0); + brf6150_reset(info); + brf6150_wait_for_cts(info, 1, 10); + brf6150_set_rts(info, 1); + if (brf6150_send_negotiation(info)) { + brf6150_close_firmware(info); + return -1; + } + + if (!wait_for_completion_interruptible_timeout(&info->init_completion, HZ)) { + brf6150_close_firmware(info); + clk_disable(info->uart_ck); + clear_bit(HCI_RUNNING, &hdev->flags); + return -1; + } + brf6150_set_auto_ctsrts(info, 1); + + err = brf6150_send_firmware(info); + brf6150_close_firmware(info); + if (err < 0) + printk(KERN_ERR "brf6150: Sending firmware failed. Bluetooth won't work properly\n"); + + set_irq_type(OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio), IRQT_BOTHEDGE); + info->pm_enabled = 1; + set_bit(HCI_RUNNING, &hdev->flags); + return 0; +} + +static int brf6150_hci_close(struct hci_dev *hdev) +{ + struct brf6150_info *info = hdev->driver_data; + if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags)) + return 0; + + brf6150_hci_flush(hdev); + clk_disable(info->uart_ck); + del_timer_sync(&info->pm_timer); + omap_set_gpio_dataout(info->btinfo->bt_wakeup_gpio, 0); + set_irq_type(OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio), IRQT_NOEDGE); + + return 0; +} + +static void brf6150_hci_destruct(struct hci_dev *hdev) +{ +} + +static int brf6150_hci_send_frame(struct sk_buff *skb) +{ + struct brf6150_info *info; + struct hci_dev *hdev = (struct hci_dev *)skb->dev; + + if (!hdev) { + printk(KERN_WARNING "brf6150: Frame for unknown device\n"); + return -ENODEV; + } + + if (!test_bit(HCI_RUNNING, &hdev->flags)) { + printk(KERN_WARNING "brf6150: Frame for non-running device\n"); + return -EIO; + } + + info = hdev->driver_data; + + switch (bt_cb(skb)->pkt_type) { + case HCI_COMMAND_PKT: + hdev->stat.cmd_tx++; + break; + case HCI_ACLDATA_PKT: + hdev->stat.acl_tx++; + break; + case HCI_SCODATA_PKT: + hdev->stat.sco_tx++; + break; + }; + + /* Push frame type to skb */ + clk_enable(info->uart_ck); + *skb_push(skb, 1) = bt_cb(skb)->pkt_type; + skb_queue_tail(&info->txq, skb); + + brf6150_disable_pm_tx(info); + + return 0; +} + +static int brf6150_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg) +{ + return -ENOIOCTLCMD; +} + +static void brf6150_device_release(struct device *dev) +{ +} + +static int brf6150_register_hdev(struct brf6150_info *info) +{ + struct hci_dev *hdev; + + /* Initialize and register HCI device */ + + hdev = hci_alloc_dev(); + if (!hdev) { + printk(KERN_WARNING "brf6150: Can't allocate memory for device\n"); + return -ENOMEM; + } + info->hdev = hdev; + + hdev->type = HCI_UART; + hdev->driver_data = info; + + hdev->open = brf6150_hci_open; + hdev->close = brf6150_hci_close; + hdev->destruct = brf6150_hci_destruct; + hdev->flush = brf6150_hci_flush; + hdev->send = brf6150_hci_send_frame; + hdev->destruct = brf6150_hci_destruct; + hdev->ioctl = brf6150_hci_ioctl; + + hdev->owner = THIS_MODULE; + + if (hci_register_dev(hdev) < 0) { + printk(KERN_WARNING "brf6150: Can't register HCI device %s.\n", hdev->name); + return -ENODEV; + } + + return 0; +} + +static int __init brf6150_init(void) +{ + struct brf6150_info *info; + int irq, err; + + info = kmalloc(sizeof(struct brf6150_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + memset(info, 0, sizeof(struct brf6150_info)); + + brf6150_device.dev.driver_data = info; + init_completion(&info->init_completion); + init_completion(&info->fw_completion); + info->pm_enabled = 0; + info->rx_pm_enabled = 0; + info->tx_pm_enabled = 0; + info->garbage_bytes = 0; + tasklet_init(&info->tx_task, brf6150_tx_tasklet, (unsigned long)info); + spin_lock_init(&info->lock); + skb_queue_head_init(&info->txq); + init_timer(&info->pm_timer); + info->pm_timer.function = brf6150_pm_timer; + info->pm_timer.data = (unsigned long)info; + exit_info = NULL; + + info->btinfo = omap_get_config(OMAP_TAG_NOKIA_BT, struct omap_bluetooth_config); + if (info->btinfo == NULL) + return -1; + + NBT_DBG("RESET gpio: %d\n", info->btinfo->reset_gpio); + NBT_DBG("BTWU gpio: %d\n", info->btinfo->bt_wakeup_gpio); + NBT_DBG("HOSTWU gpio: %d\n", info->btinfo->host_wakeup_gpio); + NBT_DBG("Uart: %d\n", info->btinfo->bt_uart); + NBT_DBG("sysclk: %d\n", info->btinfo->bt_sysclk); + + err = omap_request_gpio(info->btinfo->reset_gpio); + if (err < 0) + { + printk(KERN_WARNING "Cannot get GPIO line %d", + info->btinfo->reset_gpio); + kfree(info); + return err; + } + + err = omap_request_gpio(info->btinfo->bt_wakeup_gpio); + if (err < 0) + { + printk(KERN_WARNING "Cannot get GPIO line 0x%d", + info->btinfo->bt_wakeup_gpio); + omap_free_gpio(info->btinfo->reset_gpio); + kfree(info); + return err; + } + + err = omap_request_gpio(info->btinfo->host_wakeup_gpio); + if (err < 0) + { + printk(KERN_WARNING "Cannot get GPIO line %d", + info->btinfo->host_wakeup_gpio); + omap_free_gpio(info->btinfo->reset_gpio); + omap_free_gpio(info->btinfo->bt_wakeup_gpio); + kfree(info); + return err; + } + + omap_set_gpio_direction(info->btinfo->reset_gpio, 0); + omap_set_gpio_direction(info->btinfo->bt_wakeup_gpio, 0); + omap_set_gpio_direction(info->btinfo->host_wakeup_gpio, 1); + set_irq_type(OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio), IRQT_NOEDGE); + + switch (info->btinfo->bt_uart) { + case 1: + irq = INT_UART1; + info->uart_ck = clk_get(NULL, "uart1_ck"); + info->uart_base = io_p2v((unsigned long)OMAP_UART1_BASE); + break; + case 2: + irq = INT_UART2; + info->uart_ck = clk_get(NULL, "uart2_ck"); + info->uart_base = io_p2v((unsigned long)OMAP_UART2_BASE); + break; + case 3: + irq = INT_UART3; + info->uart_ck = clk_get(NULL, "uart3_ck"); + info->uart_base = io_p2v((unsigned long)OMAP_UART3_BASE); + break; + default: + printk(KERN_ERR "No uart defined\n"); + goto cleanup; + } + + info->irq = irq; + err = request_irq(irq, brf6150_interrupt, 0, "brf6150", (void *)info); + if (err < 0) { + printk(KERN_ERR "brf6150: unable to get IRQ %d\n", irq); + goto cleanup; + } + + err = request_irq(OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio), + brf6150_wakeup_interrupt, 0, "brf6150_wkup", (void *)info); + if (err < 0) { + printk(KERN_ERR "brf6150: unable to get wakeup IRQ %d\n", + OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio)); + free_irq(irq, (void *)info); + goto cleanup; + } + + /* Register with LDM */ + if (platform_device_register(&brf6150_device)) { + printk(KERN_ERR "failed to register brf6150 device\n"); + err = -ENODEV; + goto cleanup_irq; + } + /* Register the driver with LDM */ + if (driver_register(&brf6150_driver)) { + printk(KERN_WARNING "failed to register brf6150 driver\n"); + platform_device_unregister(&brf6150_device); + err = -ENODEV; + goto cleanup_irq; + } + + if (brf6150_register_hdev(info) < 0) { + printk(KERN_WARNING "failed to register brf6150 hci device\n"); + platform_device_unregister(&brf6150_device); + driver_unregister(&brf6150_driver); + goto cleanup_irq; + } + + exit_info = info; + return 0; + +cleanup_irq: + free_irq(irq, (void *)info); + free_irq(OMAP_GPIO_IRQ(info->btinfo->host_wakeup_gpio), (void *)info); +cleanup: + omap_free_gpio(info->btinfo->reset_gpio); + omap_free_gpio(info->btinfo->bt_wakeup_gpio); + omap_free_gpio(info->btinfo->host_wakeup_gpio); + kfree(info); + + return err; +} + +static void __exit brf6150_exit(void) +{ + brf6150_hci_close(exit_info->hdev); + hci_free_dev(exit_info->hdev); + omap_free_gpio(exit_info->btinfo->reset_gpio); + omap_free_gpio(exit_info->btinfo->bt_wakeup_gpio); + omap_free_gpio(exit_info->btinfo->host_wakeup_gpio); + free_irq(exit_info->irq, (void *)exit_info); + free_irq(OMAP_GPIO_IRQ(exit_info->btinfo->host_wakeup_gpio), (void *)exit_info); + kfree(exit_info); +} + +module_init(brf6150_init); +module_exit(brf6150_exit); + +MODULE_DESCRIPTION("brf6150 hci driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ville Tervo "); diff --git a/drivers/bluetooth/brf6150.h b/drivers/bluetooth/brf6150.h new file mode 100644 index 00000000000..334a72e5908 --- /dev/null +++ b/drivers/bluetooth/brf6150.h @@ -0,0 +1,91 @@ +/* + * linux/drivers/bluetooth/brf6150/brf6150.h + * + * Copyright (C) 2005 Nokia Corporation + * Written by Ville Tervo + * + * 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 + +#ifndef __DRIVERS_BLUETOOTH_BRF6150_H +#define __DRIVERS_BLUETOOTH_BRF6150_H + +#define UART_SYSC_OMAP_RESET 0x02 +#define UART_SYSS_RESETDONE 0x01 +#define UART_OMAP_SCR_EMPTY_THR 0x08 +#define UART_OMAP_SCR_WAKEUP 0x10 +#define UART_OMAP_SSR_WAKEUP 0x02 +#define UART_OMAP_SSR_TXFULL 0x01 + +struct brf6150_info { + struct hci_dev *hdev; + spinlock_t lock; + + struct clk *uart_ck; + unsigned long uart_base; + unsigned int irq; + + struct sk_buff_head txq; + struct sk_buff *rx_skb; + const struct omap_bluetooth_config *btinfo; + const struct firmware *fw_entry; + int fw_pos; + int fw_error; + struct completion fw_completion; + struct completion init_completion; + struct tasklet_struct tx_task; + long rx_count; + unsigned long garbage_bytes; + unsigned long rx_state; + int pm_enabled; + int rx_pm_enabled; + int tx_pm_enabled; + struct timer_list pm_timer; +}; + +#define BT_DEVICE "nokia_btuart" +#define BT_DRIVER "nokia_btuart" + +#define MAX_BAUD_RATE 921600 +#define UART_CLOCK 48000000 +#define BT_INIT_DIVIDER 320 +#define BT_BAUDRATE_DIVIDER 384000000 +#define BT_SYSCLK_DIV 1000 +#define INIT_SPEED 120000 + +#define H4_TYPE_SIZE 1 + +/* H4+ packet types */ +#define H4_CMD_PKT 0x01 +#define H4_ACL_PKT 0x02 +#define H4_SCO_PKT 0x03 +#define H4_EVT_PKT 0x04 +#define H4_NEG_PKT 0x06 +#define H4_ALIVE_PKT 0x07 + +/* TX states */ +#define WAIT_FOR_PKT_TYPE 1 +#define WAIT_FOR_HEADER 2 +#define WAIT_FOR_DATA 3 + +struct hci_fw_event { + struct hci_event_hdr hev; + struct hci_ev_cmd_complete cmd; + __u8 status; +} __attribute__ ((packed)); + +#endif /* __DRIVERS_BLUETOOTH_BRF6150_H */ diff --git a/drivers/cbus/Kconfig b/drivers/cbus/Kconfig new file mode 100644 index 00000000000..008d193ccba --- /dev/null +++ b/drivers/cbus/Kconfig @@ -0,0 +1,81 @@ +# +# CBUS device configuration +# + +menu "CBUS support" + +config CBUS + depends on ARCH_OMAP + bool "CBUS support on OMAP" + ---help--- + CBUS is a proprietary serial protocol by Nokia. It is mainly + used for accessing Energy Management auxiliary chips. + + If you want CBUS support, you should say Y here. + +config CBUS_TAHVO + depends on CBUS + bool "Support for Tahvo" + ---help--- + Tahvo is a mixed signal ASIC with some system features + + If you want Tahvo support, you should say Y here. + +config CBUS_TAHVO_USER + depends on CBUS_TAHVO + bool "Support for Tahvo user space functions" + ---help--- + If you want support for Tahvo's user space read/write etc. functions, + you should say Y here. + +config CBUS_TAHVO_USB + depends on CBUS_TAHVO && USB + tristate "Support for Tahvo USB transceiver" + ---help--- + If you want Tahvo support for USB transceiver, say Y or M here. + +config CBUS_TAHVO_USB_HOST_BY_DEFAULT + depends on CBUS_TAHVO_USB && USB_OTG + boolean "Device in USB host mode by default" + ---help--- + Say Y here, if you want the device to enter USB host mode + by default on bootup. + +config CBUS_RETU + depends on CBUS + bool "Support for Retu" + ---help--- + Retu is a mixed signal ASIC with some system features + + If you want Retu support, you should say Y here. + +config CBUS_RETU_USER + depends on CBUS_RETU + bool "Support for Retu user space functions" + ---help--- + If you want support for Retu's user space read/write etc. functions, + you should say Y here. + +config CBUS_RETU_POWERBUTTON + depends on CBUS_RETU + bool "Support for Retu power button" + ---help--- + The power button on Nokia 770 is connected to the Retu ASIC. + + If you want support for the Retu power button, you should say Y here. + +config CBUS_RETU_RTC + depends on CBUS_RETU && SYSFS + tristate "Support for Retu pseudo-RTC" + ---help--- + Say Y here if you want support for the device that alleges to be an + RTC in Retu. This will expose a sysfs interface for it. + +config CBUS_RETU_WDT + depends on CBUS_RETU && SYSFS + tristate "Support for Retu watchdog timer" + ---help--- + Say Y here if you want support for the watchdog in Retu. This will + expose a sysfs interface to grok it. + +endmenu diff --git a/drivers/cbus/Makefile b/drivers/cbus/Makefile new file mode 100644 index 00000000000..6390139fb96 --- /dev/null +++ b/drivers/cbus/Makefile @@ -0,0 +1,13 @@ +# +# Makefile for CBUS. +# + +obj-$(CONFIG_CBUS) += cbus.o +obj-$(CONFIG_CBUS_TAHVO) += tahvo.o +obj-$(CONFIG_CBUS_RETU) += retu.o +obj-$(CONFIG_CBUS_TAHVO_USB) += tahvo-usb.o +obj-$(CONFIG_CBUS_RETU_POWERBUTTON) += retu-pwrbutton.o +obj-$(CONFIG_CBUS_RETU_RTC) += retu-rtc.o +obj-$(CONFIG_CBUS_RETU_WDT) += retu-wdt.o +obj-$(CONFIG_CBUS_TAHVO_USER) += tahvo-user.o +obj-$(CONFIG_CBUS_RETU_USER) += retu-user.o diff --git a/drivers/cbus/cbus.c b/drivers/cbus/cbus.c new file mode 100644 index 00000000000..eff38be5088 --- /dev/null +++ b/drivers/cbus/cbus.c @@ -0,0 +1,293 @@ +/* + * drivers/cbus/cbus.c + * + * Support functions for CBUS serial protocol + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Written by Juha Yrjölä , + * David Weinehall , and + * Mikko Ylinen + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 "cbus.h" + +struct cbus_host *cbus_host = NULL; + +#ifdef CONFIG_ARCH_OMAP1 +/* We use our own MPUIO functions to get closer to 1MHz bus speed */ + +static inline void cbus_set_gpio_direction(u32 base, int mpuio, int is_input) +{ + u16 w; + + mpuio &= 0x0f; + w = __raw_readw(base + OMAP_MPUIO_IO_CNTL); + if (is_input) + w |= 1 << mpuio; + else + w &= ~(1 << mpuio); + __raw_writew(w, base + OMAP_MPUIO_IO_CNTL); + +} + +static inline void cbus_set_gpio_dataout(u32 base, int mpuio, int enable) +{ + u16 w; + + mpuio &= 0x0f; + w = __raw_readw(base + OMAP_MPUIO_OUTPUT); + if (enable) + w |= 1 << mpuio; + else + w &= ~(1 << mpuio); + __raw_writew(w, base + OMAP_MPUIO_OUTPUT); +} + +static inline int cbus_get_gpio_datain(u32 base, int mpuio) +{ + mpuio &= 0x0f; + + return (__raw_readw(base + OMAP_MPUIO_INPUT_LATCH) & (1 << mpuio)) != 0; +} + +static void cbus_send_bit(struct cbus_host *host, u32 base, int bit, + int set_to_input) +{ + cbus_set_gpio_dataout(base, host->dat_gpio, bit ? 1 : 0); + cbus_set_gpio_dataout(base, host->clk_gpio, 1); + + /* The data bit is read on the rising edge of CLK */ + if (set_to_input) + cbus_set_gpio_direction(base, host->dat_gpio, 1); + + cbus_set_gpio_dataout(base, host->clk_gpio, 0); +} + +static u8 cbus_receive_bit(struct cbus_host *host, u32 base) +{ + u8 ret; + + cbus_set_gpio_dataout(base, host->clk_gpio, 1); + ret = cbus_get_gpio_datain(base, host->dat_gpio); + cbus_set_gpio_dataout(base, host->clk_gpio, 0); + + return ret; +} + +#else + +#define cbus_set_gpio_direction(base, gpio, is_input) omap_set_gpio_direction(gpio, is_input) +#define cbus_set_gpio_dataout(base, gpio, enable) omap_set_gpio_dataout(gpio, enable) +#define cbus_get_gpio_datain(base, int, gpio) omap_get_gpio_datain(gpio) + +static void _cbus_send_bit(struct cbus_host *host, int bit, int set_to_input) +{ + omap_set_gpio_dataout(host->dat_gpio, bit ? 1 : 0); + omap_set_gpio_dataout(host->clk_gpio, 1); + + /* The data bit is read on the rising edge of CLK */ + if (set_to_input) + omap_set_gpio_direction(host->dat_gpio, 1); + + omap_set_gpio_dataout(host->clk_gpio, 0); +} + +static u8 _cbus_receive_bit(struct cbus_host *host) +{ + u8 ret; + + omap_set_gpio_dataout(host->clk_gpio, 1); + ret = omap_get_gpio_datain(host->dat_gpio); + omap_set_gpio_dataout(host->clk_gpio, 0); + + return ret; +} + +#define cbus_send_bit(host, base, bit, set_to_input) _cbus_send_bit(host, bit, set_to_input) +#define cbus_receive_bit(host, base) _cbus_receive_bit(host) + +#endif + +static int cbus_transfer(struct cbus_host *host, int dev, int reg, int data) +{ + int i; + int is_read = 0; + unsigned long flags; + u32 base; + +#ifdef CONFIG_ARCH_OMAP1 + base = (u32) io_p2v(OMAP_MPUIO_BASE); +#else + base = 0; +#endif + + if (data < 0) + is_read = 1; + + /* We don't want interrupts disturbing our transfer */ + spin_lock_irqsave(&host->lock, flags); + + /* Reset state and start of transfer, SEL stays down during transfer */ + cbus_set_gpio_dataout(base, host->sel_gpio, 0); + + /* Set the DAT pin to output */ + cbus_set_gpio_direction(base, host->dat_gpio, 0); + + /* Send the device address */ + for (i = 3; i > 0; i--) + cbus_send_bit(host, base, dev & (1 << (i - 1)), 0); + + /* Send the rw flag */ + cbus_send_bit(host, base, is_read, 0); + + /* Send the register address */ + for (i = 5; i > 0; i--) { + int set_to_input = 0; + + if (is_read && i == 1) + set_to_input = 1; + + cbus_send_bit(host, base, reg & (1 << (i - 1)), set_to_input); + } + + if (!is_read) { + for (i = 16; i > 0; i--) + cbus_send_bit(host, base, data & (1 << (i - 1)), 0); + } else { + cbus_set_gpio_dataout(base, host->clk_gpio, 1); + data = 0; + + for (i = 16; i > 0; i--) { + u8 bit = cbus_receive_bit(host, base); + + if (bit) + data |= 1 << (i - 1); + } + } + + /* Indicate end of transfer, SEL goes up until next transfer */ + cbus_set_gpio_dataout(base, host->sel_gpio, 1); + cbus_set_gpio_dataout(base, host->clk_gpio, 1); + cbus_set_gpio_dataout(base, host->clk_gpio, 0); + + spin_unlock_irqrestore(&host->lock, flags); + + return is_read ? data : 0; +} + +/* + * Read a given register from the device + */ +int cbus_read_reg(struct cbus_host *host, int dev, int reg) +{ + return cbus_host ? cbus_transfer(host, dev, reg, -1) : -ENODEV; +} + +/* + * Write to a given register of the device + */ +int cbus_write_reg(struct cbus_host *host, int dev, int reg, u16 val) +{ + return cbus_host ? cbus_transfer(host, dev, reg, (int)val) : -ENODEV; +} + +int __init cbus_bus_init(void) +{ + const struct omap_cbus_config * cbus_config; + struct cbus_host *chost; + int ret; + + chost = kmalloc(sizeof (*chost), GFP_KERNEL); + if (chost == NULL) + return -ENOMEM; + + memset(chost, 0, sizeof (*chost)); + + spin_lock_init(&chost->lock); + + cbus_config = omap_get_config(OMAP_TAG_CBUS, struct omap_cbus_config); + + if (cbus_config == NULL) { + printk(KERN_ERR "cbus: Unable to retrieve config data\n"); + return -ENODATA; + } + + chost->clk_gpio = cbus_config->clk_gpio; + chost->dat_gpio = cbus_config->dat_gpio; + chost->sel_gpio = cbus_config->sel_gpio; + +#ifdef CONFIG_ARCH_OMAP1 + if (!OMAP_GPIO_IS_MPUIO(chost->clk_gpio) || + !OMAP_GPIO_IS_MPUIO(chost->dat_gpio) || + !OMAP_GPIO_IS_MPUIO(chost->sel_gpio)) { + printk(KERN_ERR "cbus: Only MPUIO pins supported\n"); + ret = -ENODEV; + goto exit1; + } +#endif + + if ((ret = omap_request_gpio(chost->clk_gpio)) < 0) + goto exit1; + + if ((ret = omap_request_gpio(chost->dat_gpio)) < 0) + goto exit2; + + if ((ret = omap_request_gpio(chost->sel_gpio)) < 0) + goto exit3; + + omap_set_gpio_dataout(chost->clk_gpio, 0); + omap_set_gpio_dataout(chost->sel_gpio, 1); + + omap_set_gpio_direction(chost->clk_gpio, 0); + omap_set_gpio_direction(chost->dat_gpio, 1); + omap_set_gpio_direction(chost->sel_gpio, 0); + + omap_set_gpio_dataout(chost->clk_gpio, 1); + omap_set_gpio_dataout(chost->clk_gpio, 0); + + cbus_host = chost; + + return 0; +exit3: + omap_free_gpio(chost->dat_gpio); +exit2: + omap_free_gpio(chost->clk_gpio); +exit1: + kfree(chost); + return ret; +} + +subsys_initcall(cbus_bus_init); + +EXPORT_SYMBOL(cbus_host); +EXPORT_SYMBOL(cbus_read_reg); +EXPORT_SYMBOL(cbus_write_reg); + +MODULE_DESCRIPTION("CBUS serial protocol"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Juha Yrjölä, David Weinehall, and Mikko Ylinen"); diff --git a/drivers/cbus/cbus.h b/drivers/cbus/cbus.h new file mode 100644 index 00000000000..957224cf77b --- /dev/null +++ b/drivers/cbus/cbus.h @@ -0,0 +1,36 @@ +/* + * drivers/cbus/cbus.h + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Written by Juha Yrjölä and + * David Weinehall + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 __DRIVERS_CBUS_CBUS_H +#define __DRIVERS_CBUS_CBUS_H + +struct cbus_host { + int clk_gpio, dat_gpio, sel_gpio; + spinlock_t lock; +}; + +extern struct cbus_host *cbus_host; + +extern int cbus_read_reg(struct cbus_host *host, int dev, int reg); +extern int cbus_write_reg(struct cbus_host *host, int dev, int reg, u16 val); + +#endif /* __DRIVERS_CBUS_CBUS_H */ diff --git a/drivers/cbus/retu-pwrbutton.c b/drivers/cbus/retu-pwrbutton.c new file mode 100644 index 00000000000..da8c14c73b2 --- /dev/null +++ b/drivers/cbus/retu-pwrbutton.c @@ -0,0 +1,120 @@ +/** + * drivers/cbus/retu-pwrbutton.c + * + * Driver for sending retu power button event to input-layer + * + * Copyright (C) 2004 Nokia Corporation + * + * Written by Ari Saastamoinen + * + * Contact Juha Yrjölä + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 "retu.h" + +#define RETU_STATUS_PWRONX (1 << 5) + +#define PWRBTN_DELAY 20 +#define PWRBTN_UP 0 +#define PWRBTN_PRESSED 1 + +static int pwrbtn_state; +static struct input_dev *pwrbtn_dev; +static struct timer_list pwrbtn_timer; + +static void retubutton_timer_func(unsigned long arg) +{ + int state; + + if (retu_read_reg(RETU_REG_STATUS) & RETU_STATUS_PWRONX) + state = PWRBTN_UP; + else + state = PWRBTN_PRESSED; + + if (pwrbtn_state != state) { + input_report_key(pwrbtn_dev, KEY_POWER, state); + pwrbtn_state = state; + } +} + +/** + * Interrupt function is called whenever power button key is pressed + * or released. + */ +static void retubutton_irq(unsigned long arg) +{ + retu_ack_irq(RETU_INT_PWR); + mod_timer(&pwrbtn_timer, jiffies + msecs_to_jiffies(PWRBTN_DELAY)); +} + +/** + * Init function. + * Allocates interrupt for power button and registers itself to input layer. + */ +static int __init retubutton_init(void) +{ + int irq; + + printk(KERN_INFO "Retu power button driver initialized\n"); + irq = RETU_INT_PWR; + + init_timer(&pwrbtn_timer); + pwrbtn_timer.function = retubutton_timer_func; + + if (retu_request_irq(irq, &retubutton_irq, 0, "PwrOnX") < 0) { + printk(KERN_ERR "%s@%s: Cannot allocate irq\n", + __FUNCTION__, __FILE__); + return -EBUSY; + } + + pwrbtn_dev = input_allocate_device(); + if (!pwrbtn_dev) + return -ENOMEM; + + pwrbtn_dev->evbit[0] = BIT(EV_KEY); + pwrbtn_dev->keybit[LONG(KEY_POWER)] = BIT(KEY_POWER); + pwrbtn_dev->name = "retu-pwrbutton"; + + input_register_device(pwrbtn_dev); + + return 0; +} + +/** + * Cleanup function which is called when driver is unloaded + */ +static void __exit retubutton_exit(void) +{ + retu_free_irq(RETU_INT_PWR); + del_timer_sync(&pwrbtn_timer); + input_unregister_device(pwrbtn_dev); +} + +module_init(retubutton_init); +module_exit(retubutton_exit); + +MODULE_DESCRIPTION("Retu Power Button"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ari Saastamoinen"); diff --git a/drivers/cbus/retu-rtc.c b/drivers/cbus/retu-rtc.c new file mode 100644 index 00000000000..6471e7ed0c3 --- /dev/null +++ b/drivers/cbus/retu-rtc.c @@ -0,0 +1,476 @@ +/** + * drivers/cbus/retu-rtc.c + * + * Support for Retu RTC + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Written by Paul Mundt and + * Igor Stoppa + * + * The Retu RTC is essentially a partial read-only RTC that gives us Retu's + * idea of what time actually is. It's left as a userspace excercise to map + * this back to time in the real world and ensure that calibration settings + * are sane to compensate for any horrible drift (on account of not being able + * to set the clock to anything). + * + * Days are semi-writeable. Namely, Retu will only track 255 days for us + * consecutively, after which the counter is explicitly stuck at 255 until + * someone comes along and clears it with a write. In the event that no one + * comes along and clears it, we no longer have any idea what day it is. + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 "cbus.h" +#include "retu.h" + +static struct semaphore retu_rtc_sem; +static u16 retu_rtc_alarm_expired; +static u16 retu_rtc_reset_occurred; + +static DECLARE_COMPLETION(retu_rtc_exited); +static DECLARE_COMPLETION(retu_rtc_sync); + +static void retu_rtc_barrier(void); + +static void retu_rtc_device_release(struct device *dev) +{ + complete(&retu_rtc_exited); +} + +static ssize_t retu_rtc_time_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u16 dsr, hmr, dsr2; + + down(&retu_rtc_sem); + + do { + u16 dummy; + + /* + * Not being in_interrupt() for a retu rtc IRQ, we need to + * read twice for consistency.. + */ + dummy = retu_read_reg(RETU_REG_RTCDSR); + dsr = retu_read_reg(RETU_REG_RTCDSR); + + dummy = retu_read_reg(RETU_REG_RTCHMR); + hmr = retu_read_reg(RETU_REG_RTCHMR); + + dummy = retu_read_reg(RETU_REG_RTCDSR); + dsr2 = retu_read_reg(RETU_REG_RTCDSR); + } while ((dsr != dsr2)); + + up(&retu_rtc_sem); + + /* + * Format a 32-bit date-string for userspace + * + * days | hours | minutes | seconds + * + * 8 bits for each. + * + * This mostly sucks because days and seconds are tracked in RTCDSR + * while hours and minutes are tracked in RTCHMR. And yes, there + * really are no words that can describe an 8 bit day register (or + * rather, none that will be reprinted here). + */ + return sprintf(buf, "0x%08x\n", (((dsr >> 8) & 0xff) << 24) | + (((hmr >> 8) & 0x1f) << 16) | + ((hmr & 0x3f) << 8) | (dsr & 0x3f)); +} + +static ssize_t retu_rtc_time_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + down(&retu_rtc_sem); + /* + * Writing anything to the day counter forces it to 0 + * The seconds counter would be cleared by resetting the minutes counter, + * however this won't happen, since we are using the hh:mm counters as + * a set of free running counters and the day counter as a multiple + * overflow holder. + */ + + /* Reset day counter, but keep Temperature Shutdown state */ + retu_write_reg(RETU_REG_RTCDSR, + retu_read_reg(RETU_REG_RTCDSR) & (1 << 6)); + + up(&retu_rtc_sem); + + return count; +} + +static DEVICE_ATTR(time, S_IRUGO | S_IWUSR, retu_rtc_time_show, + retu_rtc_time_store); + + +static ssize_t retu_rtc_reset_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + /* + * Returns the status of the rtc + * + * 0: no reset has occurred or the status has been cleared + * 1: a reset has occurred + * + * RTC needs to be reset only when both main battery + * _AND_ backup battery are discharged + */ + return sprintf(buf, "%u\n", retu_rtc_reset_occurred); +} + +static void retu_rtc_do_reset(void) +{ + u16 ccr1; + + ccr1 = retu_read_reg(RETU_REG_CC1); + /* RTC in reset */ + retu_write_reg(RETU_REG_CC1, ccr1 | 0x0001); + /* RTC in normal operating mode */ + retu_write_reg(RETU_REG_CC1, ccr1 & ~0x0001); + + retu_rtc_barrier(); + /* Disable alarm and RTC WD */ + retu_write_reg(RETU_REG_RTCHMAR, 0x7f3f); + /* Set Calibration register to default value */ + retu_write_reg(RETU_REG_RTCCALR, 0x00c0); + + retu_rtc_alarm_expired = 0; + retu_rtc_reset_occurred = 1; +} + +static ssize_t retu_rtc_reset_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned choice; + + if(sscanf(buf, "%u", &choice) != 1) + return count; + down(&retu_rtc_sem); + if (choice == 0) + retu_rtc_reset_occurred = 0; + else if (choice == 1) + retu_rtc_do_reset(); + up(&retu_rtc_sem); + return count; +} + +static DEVICE_ATTR(reset, S_IRUGO | S_IWUSR, retu_rtc_reset_show, + retu_rtc_reset_store); + +static ssize_t retu_rtc_alarm_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u16 chmar; + ssize_t retval; + + down(&retu_rtc_sem); + /* + * Format a 16-bit date-string for userspace + * + * hours | minutes + * 8 bits for each. + */ + chmar = retu_read_reg(RETU_REG_RTCHMAR); + /* No shifting needed, only masking unrelated bits */ + retval = sprintf(buf, "0x%04x\n", chmar & 0x1f3f); + up(&retu_rtc_sem); + + return retval; +} + +static ssize_t retu_rtc_alarm_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + u16 chmar; + unsigned alrm; + unsigned hours; + unsigned minutes; + + down(&retu_rtc_sem); + + if(sscanf(buf, "%x", &alrm) != 1) + return count; + hours = (alrm >> 8) & 0x001f; + minutes = (alrm >> 0) & 0x003f; + if ((hours < 24 && minutes < 60) || (hours == 24 && minutes == 60)) { + /* + * OK, the time format for the alarm is valid (including the + * disabling values) + */ + /* Keeps the RTC watchdog status */ + chmar = retu_read_reg(RETU_REG_RTCHMAR) & 0x6000; + chmar |= alrm & 0x1f3f; /* Stores the requested alarm */ + retu_rtc_barrier(); + retu_write_reg(RETU_REG_RTCHMAR, chmar); + /* If the alarm is being disabled */ + if (hours == 24 && minutes == 60) { + /* disable the interrupt */ + retu_disable_irq(RETU_INT_RTCA); + retu_rtc_alarm_expired = 0; + } else + /* enable the interrupt */ + retu_enable_irq(RETU_INT_RTCA); + } + up(&retu_rtc_sem); + + return count; +} + +static DEVICE_ATTR(alarm, S_IRUGO | S_IWUSR, retu_rtc_alarm_show, + retu_rtc_alarm_store); + +static ssize_t retu_rtc_alarm_expired_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + ssize_t retval; + + retval = sprintf(buf, "%u\n", retu_rtc_alarm_expired); + + return retval; +} + +static ssize_t retu_rtc_alarm_expired_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + retu_rtc_alarm_expired = 0; + + return count; +} + +static DEVICE_ATTR(alarm_expired, S_IRUGO | S_IWUSR, retu_rtc_alarm_expired_show, + retu_rtc_alarm_expired_store); + + +static ssize_t retu_rtc_cal_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u16 rtccalr1; + + down(&retu_rtc_sem); + rtccalr1 = retu_read_reg(RETU_REG_RTCCALR); + up(&retu_rtc_sem); + + /* + * Shows the status of the Calibration Register. + * + * Default, after power loss: 0x0000 + * Default, for R&D: 0x00C0 + * Default, for factory: 0x00?? + * + */ + return sprintf(buf, "0x%04x\n", rtccalr1 & 0x00ff); +} + +static ssize_t retu_rtc_cal_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned calibration_value; + + if (sscanf(buf, "%x", &calibration_value) != 1) + return count; + + down(&retu_rtc_sem); + retu_rtc_barrier(); + retu_write_reg(RETU_REG_RTCCALR, calibration_value & 0x00ff); + up(&retu_rtc_sem); + + return count; +} + +static DEVICE_ATTR(cal, S_IRUGO | S_IWUSR, retu_rtc_cal_show, + retu_rtc_cal_store); + +static struct device_driver retu_rtc_driver; + +static void retu_rtca_disable(void) +{ + retu_disable_irq(RETU_INT_RTCA); + retu_rtc_alarm_expired = 1; + retu_rtc_barrier(); + retu_write_reg(RETU_REG_RTCHMAR, (24 << 8) | 60); +} + +static void retu_rtca_expired(void *data) +{ + retu_rtca_disable(); + kobject_uevent(&retu_rtc_driver.kobj, KOBJ_CHANGE); +} + +DECLARE_WORK(retu_rtca_work, retu_rtca_expired, NULL); + +/* + * RTCHMR RTCHMAR RTCCAL must be accessed within 0.9 s since the seconds + * interrupt has been signaled in the IDR register + */ +static void retu_rtcs_interrupt(unsigned long unused) +{ + retu_ack_irq(RETU_INT_RTCS); + complete(&retu_rtc_sync); +} + +static void retu_rtca_interrupt(unsigned long unused) +{ + retu_ack_irq(RETU_INT_RTCA); + schedule_work(&retu_rtca_work); +} + +static int retu_rtc_init_irq(void) +{ + int ret; + + ret = retu_request_irq(RETU_INT_RTCS, retu_rtcs_interrupt, 0, "RTCS"); + if (ret != 0) + return ret; + /* + * We will take care of enabling and disabling the interrupt + * elsewhere, so leave it off by default.. + */ + retu_disable_irq(RETU_INT_RTCS); + + ret = retu_request_irq(RETU_INT_RTCA, retu_rtca_interrupt, 0, "RTCA"); + if (ret != 0) { + retu_free_irq(RETU_INT_RTCS); + return ret; + } + retu_disable_irq(RETU_INT_RTCA); + + return 0; +} + + +static int __devinit retu_rtc_probe(struct device *dev) +{ + int r; + + retu_rtc_alarm_expired = retu_read_reg(RETU_REG_IDR) & + (0x1 << RETU_INT_RTCA); + + if ((r = retu_rtc_init_irq()) != 0) + return r; + + init_MUTEX(&retu_rtc_sem); + + /* If the calibration register is zero, we've probably lost + * power */ + if (retu_read_reg(RETU_REG_RTCCALR) & 0x00ff) + retu_rtc_reset_occurred = 0; + else + retu_rtc_do_reset(); + + if ((r = device_create_file(dev, &dev_attr_time)) != 0) + return r; + else if ((r = device_create_file(dev, &dev_attr_reset)) != 0) + goto err_unregister_time; + else if ((r = device_create_file(dev, &dev_attr_alarm)) != 0) + goto err_unregister_reset; + else if ((r = device_create_file(dev, &dev_attr_alarm_expired)) != 0) + goto err_unregister_alarm; + else if ((r = device_create_file(dev, &dev_attr_cal)) != 0) + goto err_unregister_alarm_expired; + else + return r; + +err_unregister_alarm_expired: + device_remove_file(dev, &dev_attr_alarm_expired); +err_unregister_alarm: + device_remove_file(dev, &dev_attr_alarm); +err_unregister_reset: + device_remove_file(dev, &dev_attr_reset); +err_unregister_time: + device_remove_file(dev, &dev_attr_time); + return r; +} + +static int __devexit retu_rtc_remove(struct device *dev) +{ + retu_disable_irq(RETU_INT_RTCS); + retu_free_irq(RETU_INT_RTCS); + retu_free_irq(RETU_INT_RTCA); + device_remove_file(dev, &dev_attr_cal); + device_remove_file(dev, &dev_attr_alarm_expired); + device_remove_file(dev, &dev_attr_alarm); + device_remove_file(dev, &dev_attr_reset); + device_remove_file(dev, &dev_attr_time); + return 0; +} + +static struct device_driver retu_rtc_driver = { + .name = "retu-rtc", + .bus = &platform_bus_type, + .probe = retu_rtc_probe, + .remove = __devexit_p(retu_rtc_remove), +}; + +static struct platform_device retu_rtc_device = { + .name = "retu-rtc", + .id = -1, + .dev = { + .release = retu_rtc_device_release, + }, +}; + +/* This function provides syncronization with the RTCS interrupt handler */ +static void retu_rtc_barrier(void) +{ + init_completion(&retu_rtc_sync); + retu_ack_irq(RETU_INT_RTCS); + retu_enable_irq(RETU_INT_RTCS); + wait_for_completion(&retu_rtc_sync); + retu_disable_irq(RETU_INT_RTCS); +} + +static int __init retu_rtc_init(void) +{ + int ret; + + init_completion(&retu_rtc_exited); + + if ((ret = driver_register(&retu_rtc_driver)) != 0) + return ret; + + if ((ret = platform_device_register(&retu_rtc_device)) != 0) + goto err_unregister_driver; + + return 0; + +err_unregister_driver: + driver_unregister(&retu_rtc_driver); + return ret; +} + +static void __exit retu_rtc_exit(void) +{ + platform_device_unregister(&retu_rtc_device); + driver_unregister(&retu_rtc_driver); + + wait_for_completion(&retu_rtc_exited); +} + +module_init(retu_rtc_init); +module_exit(retu_rtc_exit); + +MODULE_DESCRIPTION("Retu RTC"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Paul Mundt and Igor Stoppa"); diff --git a/drivers/cbus/retu-user.c b/drivers/cbus/retu-user.c new file mode 100644 index 00000000000..69d1be966de --- /dev/null +++ b/drivers/cbus/retu-user.c @@ -0,0 +1,415 @@ +/** + * drivers/cbus/retu-user.c + * + * Retu user space interface functions + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Written by Mikko Ylinen + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 "retu.h" + +#include "user_retu_tahvo.h" + +/* Maximum size of IRQ node buffer/pool */ +#define RETU_MAX_IRQ_BUF_LEN 16 + +#define PFX "retu-user: " + +/* Bitmap for marking the interrupt sources as having the handlers */ +static u32 retu_irq_bits; + +/* For allowing only one user process to subscribe to the retu interrupts */ +static struct file *retu_irq_subscr = NULL; + +/* For poll and IRQ passing */ +struct retu_irq { + u32 id; + struct list_head node; +}; + +static spinlock_t retu_irqs_lock; +static struct retu_irq *retu_irq_block; +static LIST_HEAD(retu_irqs); +static LIST_HEAD(retu_irqs_reserve); + +/* Wait queue - used when user wants to read the device */ +DECLARE_WAIT_QUEUE_HEAD(retu_user_waitqueue); + +/* Semaphore to protect irq subscription sequence */ +static struct semaphore retu_sem; + +/* This array specifies RETU register types (read/write/toggle) */ +static const u8 retu_access_bits[] = { + 1, + 4, + 3, + 3, + 1, + 3, + 3, + 0, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 4, + 4, + 3, + 0, + 0, + 0, + 0, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3 +}; + +/* + * The handler for all RETU interrupts. + * + * arg is the interrupt source in RETU. + */ +static void retu_user_irq_handler(unsigned long arg) +{ + struct retu_irq *irq; + + retu_ack_irq(arg); + + spin_lock(&retu_irqs_lock); + if (list_empty(&retu_irqs_reserve)) { + spin_unlock(&retu_irqs_lock); + return; + } + irq = list_entry((&retu_irqs_reserve)->next, struct retu_irq, node); + irq->id = arg; + list_move_tail(&irq->node, &retu_irqs); + spin_unlock(&retu_irqs_lock); + + /* wake up waiting thread */ + wake_up(&retu_user_waitqueue); +} + +/* + * This routine sets up the interrupt handler and marks an interrupt source + * in RETU as a candidate for signal delivery to the user process. + */ +static int retu_user_subscribe_to_irq(int id, struct file *filp) +{ + int ret; + + down(&retu_sem); + if ((retu_irq_subscr != NULL) && (retu_irq_subscr != filp)) { + up(&retu_sem); + return -EBUSY; + } + /* Store the file pointer of the first user process registering IRQs */ + retu_irq_subscr = filp; + up(&retu_sem); + + if (retu_irq_bits & (1 << id)) + return 0; + + ret = retu_request_irq(id, retu_user_irq_handler, id, ""); + if (ret < 0) + return ret; + + /* Mark that this interrupt has a handler */ + retu_irq_bits |= 1 << id; + + return 0; +} + +/* + * Unregisters all RETU interrupt handlers. + */ +static void retu_unreg_irq_handlers(void) +{ + int id; + + if (!retu_irq_bits) + return; + + for (id = 0; id < MAX_RETU_IRQ_HANDLERS; id++) + if (retu_irq_bits & (1 << id)) + retu_free_irq(id); + + retu_irq_bits = 0; +} + +/* + * Write to RETU register. + * Returns 0 upon success, a negative error value otherwise. + */ +static int retu_user_write_with_mask(u32 field, u16 value) +{ + u32 mask; + u32 reg; + u_short tmp; + unsigned long flags; + + mask = MASK(field); + reg = REG(field); + + /* Detect bad mask and reg */ + if (mask == 0 || reg > RETU_REG_MAX || + retu_access_bits[reg] == READ_ONLY) { + printk(KERN_ERR PFX "invalid arguments (reg=%#x, mask=%#x)\n", + reg, mask); + return -EINVAL; + } + + /* Justify value according to mask */ + while (!(mask & 1)) { + value = value << 1; + mask = mask >> 1; + } + + spin_lock_irqsave(&retu_lock, flags); + if (retu_access_bits[reg] == TOGGLE) { + /* No need to detect previous content of register */ + tmp = 0; + } else { + /* Read current value of register */ + tmp = retu_read_reg(reg); + } + + /* Generate new value */ + tmp = (tmp & ~MASK(field)) | (value & MASK(field)); + /* Write data to RETU */ + retu_write_reg(reg, tmp); + spin_unlock_irqrestore(&retu_lock, flags); + + return 0; +} + +/* + * Read RETU register. + */ +static u32 retu_user_read_with_mask(u32 field) +{ + u_short value; + u32 mask, reg; + + mask = MASK(field); + reg = REG(field); + + /* Detect bad mask and reg */ + if (mask == 0 || reg > RETU_REG_MAX) { + printk(KERN_ERR PFX "invalid arguments (reg=%#x, mask=%#x)\n", + reg, mask); + return -EINVAL; + } + + /* Read the register */ + value = retu_read_reg(reg) & mask; + + /* Right justify value */ + while (!(mask & 1)) { + value = value >> 1; + mask = mask >> 1; + } + + return value; +} + +/* + * Close device + */ +static int retu_close(struct inode *inode, struct file *filp) +{ + /* Unregister all interrupts that have been registered */ + if (retu_irq_subscr == filp) { + retu_unreg_irq_handlers(); + retu_irq_subscr = NULL; + } + + return 0; +} + +/* + * Device control (ioctl) + */ +static int retu_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct retu_tahvo_write_parms par; + + switch (cmd) { + case URT_IOCT_IRQ_SUBSCR: + return retu_user_subscribe_to_irq(arg, filp); + case RETU_IOCH_READ: + return retu_user_read_with_mask(arg); + case RETU_IOCX_WRITE: + copy_from_user(&par, (void __user *) arg, sizeof(par)); + par.result = retu_user_write_with_mask(par.field, par.value); + copy_to_user((void __user *) arg, &par, sizeof(par)); + break; + case RETU_IOCH_ADC_READ: + return retu_read_adc(arg); + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/* + * Read from device + */ +static ssize_t retu_read(struct file *filp, char *buf, size_t count, + loff_t * offp) +{ + struct retu_irq *irq; + + u32 nr, i; + + /* read not permitted if neither filp nor anyone has registered IRQs */ + if (retu_irq_subscr != filp) + return -EPERM; + + if ((count < sizeof(u32)) || ((count % sizeof(u32)) != 0)) + return -EINVAL; + + nr = count / sizeof(u32); + + for (i = 0; i < nr; i++) { + unsigned long flags; + u32 irq_id; + int ret; + + ret = wait_event_interruptible(retu_user_waitqueue, + !list_empty(&retu_irqs)); + if (ret < 0) + return ret; + + spin_lock_irqsave(&retu_irqs_lock, flags); + irq = list_entry((&retu_irqs)->next, struct retu_irq, node); + irq_id = irq->id; + list_move(&irq->node, &retu_irqs_reserve); + spin_unlock_irqrestore(&retu_irqs_lock, flags); + + copy_to_user(buf + i * sizeof(irq_id), &irq_id, sizeof(irq_id)); + + } + + return count; +} + +/* + * Poll method + */ +static unsigned retu_poll(struct file *filp, struct poll_table_struct *pt) +{ + if (!list_empty(&retu_irqs)) + return POLLIN; + + poll_wait(filp, &retu_user_waitqueue, pt); + + if (!list_empty(&retu_irqs)) + return POLLIN; + else + return 0; +} + +static struct file_operations retu_user_fileops = { + .owner = THIS_MODULE, + .ioctl = retu_ioctl, + .read = retu_read, + .release = retu_close, + .poll = retu_poll +}; + +static struct miscdevice retu_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "retu", + .fops = &retu_user_fileops +}; + +/* + * Initialization + * + * @return 0 if successful, error value otherwise. + */ +int retu_user_init(void) +{ + struct retu_irq *irq; + int res, i; + + irq = kmalloc(sizeof(*irq) * RETU_MAX_IRQ_BUF_LEN, GFP_KERNEL); + if (irq == NULL) { + printk(KERN_ERR PFX "kmalloc failed\n"); + return -ENOMEM; + } + memset(irq, 0, sizeof(*irq) * RETU_MAX_IRQ_BUF_LEN); + for (i = 0; i < RETU_MAX_IRQ_BUF_LEN; i++) + list_add(&irq[i].node, &retu_irqs_reserve); + + retu_irq_block = irq; + + spin_lock_init(&retu_irqs_lock); + sema_init(&retu_sem, 1); + + /* Request a misc device */ + res = misc_register(&retu_device); + if (res < 0) { + printk(KERN_ERR PFX "unable to register misc device for %s\n", + retu_device.name); + kfree(irq); + return res; + } + + return 0; +} + +/* + * Cleanup. + */ +void retu_user_cleanup(void) +{ + /* Unregister our misc device */ + misc_deregister(&retu_device); + /* Unregister and disable all RETU interrupts used by this module */ + retu_unreg_irq_handlers(); + kfree(retu_irq_block); +} + +MODULE_DESCRIPTION("Retu ASIC user space functions"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mikko Ylinen"); diff --git a/drivers/cbus/retu-wdt.c b/drivers/cbus/retu-wdt.c new file mode 100644 index 00000000000..97399070877 --- /dev/null +++ b/drivers/cbus/retu-wdt.c @@ -0,0 +1,199 @@ +/** + * drivers/cbus/retu-wdt.c + * + * Driver for Retu watchdog + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Written by Amit Kucheria + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 "cbus.h" +#include "retu.h" + +/* Watchdog timeout in seconds */ +#define RETU_WDT_MIN_TIMER 0 +#define RETU_WDT_DEFAULT_TIMER 32 +#define RETU_WDT_MAX_TIMER 63 + +static struct completion retu_wdt_completion; +static DECLARE_MUTEX(retu_wdt_mutex); /* Avoid simultaneous writes to watchdog register */ + +static unsigned int period_val = RETU_WDT_DEFAULT_TIMER; /* Current period of watchdog */ +static int counter_param = RETU_WDT_MAX_TIMER; + +static int retu_modify_counter(unsigned int new) +{ + int ret = 0; + + if (new < RETU_WDT_MIN_TIMER || new > RETU_WDT_MAX_TIMER) + return -EINVAL; + + down_interruptible(&retu_wdt_mutex); + + period_val = new; + retu_write_reg(RETU_REG_WATCHDOG, (u16)period_val); + + up(&retu_wdt_mutex); + return ret; +} + +static ssize_t retu_wdt_period_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + /* Show current max counter */ + return sprintf(buf, "%u\n", (u16)period_val); +} + +static ssize_t retu_wdt_period_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned int new_period; + int ret; + + if (sscanf(buf, "%u", &new_period) != 1) { + printk(KERN_ALERT "retu_wdt_period_store: Invalid input\n"); + return -EINVAL; + } + + ret = retu_modify_counter(new_period); + if (ret < 0) + return ret; + + return strnlen(buf, count); +} + +static ssize_t retu_wdt_counter_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u16 counter; + + /* Show current value in watchdog counter */ + counter = retu_read_reg(RETU_REG_WATCHDOG); + + /* Only the 5 LSB are important */ + return snprintf(buf, PAGE_SIZE, "%u\n", (counter & 0x3F)); +} + +static DEVICE_ATTR(period, S_IRUGO | S_IWUSR, retu_wdt_period_show, \ + retu_wdt_period_store); +static DEVICE_ATTR(counter, S_IRUGO, retu_wdt_counter_show, NULL); + +static int __devinit retu_wdt_probe(struct device *dev) +{ + int ret; + + ret = device_create_file(dev, &dev_attr_period); + if (ret) { + printk(KERN_ERR "retu_wdt_probe: Error creating sys device file: period\n"); + return ret; + } + + ret = device_create_file(dev, &dev_attr_counter); + if (ret) { + device_remove_file(dev, &dev_attr_period); + printk(KERN_ERR "retu_wdt_probe: Error creating sys device file: counter\n"); + } + + return ret; +} + +static int __devexit retu_wdt_remove(struct device *dev) +{ + device_remove_file(dev, &dev_attr_period); + device_remove_file(dev, &dev_attr_counter); + return 0; +} + +static void retu_wdt_device_release(struct device *dev) +{ + complete(&retu_wdt_completion); +} + +static struct platform_device retu_wdt_device = { + .name = "retu-watchdog", + .id = -1, + .dev = { + .release = retu_wdt_device_release, + }, +}; + +static struct device_driver retu_wdt_driver = { + .name = "retu-watchdog", + .bus = &platform_bus_type, + .probe = retu_wdt_probe, + .remove = __devexit_p(retu_wdt_remove), +}; + +static int __init retu_wdt_init(void) +{ + int ret; + + init_completion(&retu_wdt_completion); + + ret = driver_register(&retu_wdt_driver); + if (ret) + return ret; + + ret = platform_device_register(&retu_wdt_device); + if (ret) + goto exit1; + + /* passed as module parameter? */ + ret = retu_modify_counter(counter_param); + if (ret == -EINVAL) { + ret = retu_modify_counter(RETU_WDT_DEFAULT_TIMER); + printk(KERN_INFO + "retu_wdt_init: Intializing to default value\n"); + } + + printk(KERN_INFO "Retu watchdog driver initialized\n"); + return ret; + +exit1: + driver_unregister(&retu_wdt_driver); + wait_for_completion(&retu_wdt_completion); + + return ret; +} + +static void __exit retu_wdt_exit(void) +{ + platform_device_unregister(&retu_wdt_device); + driver_unregister(&retu_wdt_driver); + + wait_for_completion(&retu_wdt_completion); +} + +module_init(retu_wdt_init); +module_exit(retu_wdt_exit); +module_param(counter_param, int, 0); + +MODULE_DESCRIPTION("Retu WatchDog"); +MODULE_AUTHOR("Amit Kucheria"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/cbus/retu.c b/drivers/cbus/retu.c new file mode 100644 index 00000000000..87ab216c4ae --- /dev/null +++ b/drivers/cbus/retu.c @@ -0,0 +1,441 @@ +/** + * drivers/cbus/retu.c + * + * Support functions for Retu ASIC + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Written by Juha Yrjölä , + * David Weinehall , and + * Mikko Ylinen + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 "cbus.h" +#include "retu.h" + +#define RETU_ID 0x01 +#define PFX "retu: " + +static int retu_initialized; +static int retu_irq_pin; +static int retu_is_vilma; + +static struct tasklet_struct retu_tasklet; +spinlock_t retu_lock = SPIN_LOCK_UNLOCKED; + +static struct completion device_release; + +struct retu_irq_handler_desc { + int (*func)(unsigned long); + unsigned long arg; + char name[8]; +}; + +static struct retu_irq_handler_desc retu_irq_handlers[MAX_RETU_IRQ_HANDLERS]; + +/** + * retu_read_reg - Read a value from a register in Retu + * @reg: the register to read from + * + * This function returns the contents of the specified register + */ +int retu_read_reg(int reg) +{ + BUG_ON(!retu_initialized); + return cbus_read_reg(cbus_host, RETU_ID, reg); +} + +/** + * retu_write_reg - Write a value to a register in Retu + * @reg: the register to write to + * @reg: the value to write to the register + * + * This function writes a value to the specified register + */ +void retu_write_reg(int reg, u16 val) +{ + BUG_ON(!retu_initialized); + cbus_write_reg(cbus_host, RETU_ID, reg, val); +} + +#define ADC_MAX_CHAN_NUMBER 13 + +int retu_read_adc(int channel) +{ + unsigned long flags; + int res; + + if (channel < 0 || channel > ADC_MAX_CHAN_NUMBER) + return -EINVAL; + + spin_lock_irqsave(&retu_lock, flags); + /* Select the channel and read result */ + retu_write_reg(RETU_REG_ADCR, channel << 10); + res = retu_read_reg(RETU_REG_ADCR) & 0x3ff; + /* Unlock retu */ + spin_unlock_irqrestore(&retu_lock, flags); + + return res; +} + + +static u16 retu_disable_bogus_irqs(u16 mask) +{ + int i; + + for (i = 0; i < MAX_RETU_IRQ_HANDLERS; i++) { + if (mask & (1 << i)) + continue; + if (retu_irq_handlers[i].func != NULL) + continue; + /* an IRQ was enabled but we don't have a handler for it */ + printk(KERN_INFO PFX "disabling bogus IRQ %d\n", i); + mask |= (1 << i); + } + return mask; +} + +/* + * Disable given RETU interrupt + */ +void retu_disable_irq(int id) +{ + unsigned long flags; + u16 mask; + + spin_lock_irqsave(&retu_lock, flags); + mask = retu_read_reg(RETU_REG_IMR); + mask |= 1 << id; + mask = retu_disable_bogus_irqs(mask); + retu_write_reg(RETU_REG_IMR, mask); + spin_unlock_irqrestore(&retu_lock, flags); +} + +/* + * Enable given RETU interrupt + */ +void retu_enable_irq(int id) +{ + unsigned long flags; + u16 mask; + + if (id == 3) { + printk("Enabling Retu IRQ %d\n", id); + dump_stack(); + } + spin_lock_irqsave(&retu_lock, flags); + mask = retu_read_reg(RETU_REG_IMR); + mask &= ~(1 << id); + mask = retu_disable_bogus_irqs(mask); + retu_write_reg(RETU_REG_IMR, mask); + spin_unlock_irqrestore(&retu_lock, flags); +} + +/* + * Acknowledge given RETU interrupt + */ +void retu_ack_irq(int id) +{ + retu_write_reg(RETU_REG_IDR, 1 << id); +} + +/* + * RETU interrupt handler. Only schedules the tasklet. + */ +static irqreturn_t retu_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + tasklet_schedule(&retu_tasklet); + return IRQ_HANDLED; +} + +/* + * Tasklet handler + */ +static void retu_tasklet_handler(unsigned long data) +{ + struct retu_irq_handler_desc *hnd; + u16 id; + u16 im; + int i; + + for (;;) { + id = retu_read_reg(RETU_REG_IDR); + im = ~retu_read_reg(RETU_REG_IMR); + id &= im; + + if (!id) + break; + + for (i = 0; id != 0; i++, id >>= 1) { + if (!(id & 1)) + continue; + hnd = &retu_irq_handlers[i]; + if (hnd->func == NULL) { + /* Spurious retu interrupt - disable and ack it */ + printk(KERN_INFO "Spurious Retu interrupt " + "(id %d)\n", i); + retu_disable_irq(i); + retu_ack_irq(i); + continue; + } + hnd->func(hnd->arg); + /* + * Don't acknowledge the interrupt here + * It must be done explicitly + */ + } + } +} + +/* + * Register the handler for a given RETU interrupt source. + */ +int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name) +{ + struct retu_irq_handler_desc *hnd; + + if (irq_handler == NULL || id >= MAX_RETU_IRQ_HANDLERS || + name == NULL) { + printk(KERN_ERR PFX "Invalid arguments to %s\n", + __FUNCTION__); + return -EINVAL; + } + hnd = &retu_irq_handlers[id]; + if (hnd->func != NULL) { + printk(KERN_ERR PFX "IRQ %d already reserved\n", id); + return -EBUSY; + } + printk(KERN_INFO PFX "Registering interrupt %d for device %s\n", + id, name); + hnd->func = irq_handler; + hnd->arg = arg; + strlcpy(hnd->name, name, sizeof(hnd->name)); + + retu_ack_irq(id); + retu_enable_irq(id); + + return 0; +} + +/* + * Unregister the handler for a given RETU interrupt source. + */ +void retu_free_irq(int id) +{ + struct retu_irq_handler_desc *hnd; + + if (id >= MAX_RETU_IRQ_HANDLERS) { + printk(KERN_ERR PFX "Invalid argument to %s\n", + __FUNCTION__); + return; + } + hnd = &retu_irq_handlers[id]; + if (hnd->func == NULL) { + printk(KERN_ERR PFX "IRQ %d already freed\n", id); + return; + } + + retu_disable_irq(id); + hnd->func = NULL; +} + +/** + * retu_power_off - Shut down power to system + * + * This function puts the system in power off state + */ +static void retu_power_off(void) +{ + /* Ignore power button state */ + retu_write_reg(RETU_REG_CC1, retu_read_reg(RETU_REG_CC1) | 2); + /* Expire watchdog immediately */ + retu_write_reg(RETU_REG_WATCHDOG, 0); + /* Wait for poweroff*/ + for (;;); +} + +/** + * retu_probe - Probe for Retu ASIC + * @dev: the Retu device + * + * Probe for the Retu ASIC and allocate memory + * for its device-struct if found + */ +static int __devinit retu_probe(struct device *dev) +{ + const struct omap_em_asic_bb5_config * em_asic_config; + int rev, ret; + + /* Prepare tasklet */ + tasklet_init(&retu_tasklet, retu_tasklet_handler, 0); + + em_asic_config = omap_get_config(OMAP_TAG_EM_ASIC_BB5, + struct omap_em_asic_bb5_config); + if (em_asic_config == NULL) { + printk(KERN_ERR PFX "Unable to retrieve config data\n"); + return -ENODATA; + } + + retu_irq_pin = em_asic_config->retu_irq_gpio; + + if ((ret = omap_request_gpio(retu_irq_pin)) < 0) { + printk(KERN_ERR PFX "Unable to reserve IRQ GPIO\n"); + return ret; + } + + /* Set the pin as input */ + omap_set_gpio_direction(retu_irq_pin, 1); + + /* Rising edge triggers the IRQ */ + set_irq_type(OMAP_GPIO_IRQ(retu_irq_pin), IRQT_RISING); + + retu_initialized = 1; + + rev = retu_read_reg(RETU_REG_ASICR) & 0xff; + if (rev & (1 << 7)) + retu_is_vilma = 1; + + printk(KERN_INFO "%s v%d.%d found\n", retu_is_vilma ? "Vilma" : "Retu", + (rev >> 4) & 0x07, rev & 0x0f); + + /* Mask all RETU interrupts */ + retu_write_reg(RETU_REG_IMR, 0xffff); + + ret = request_irq(OMAP_GPIO_IRQ(retu_irq_pin), retu_irq_handler, 0, + "retu", 0); + + if (ret < 0) { + printk(KERN_ERR PFX "Unable to register IRQ handler\n"); + omap_free_gpio(retu_irq_pin); + return ret; + } + + /* Register power off function */ + pm_power_off = retu_power_off; + +#ifdef CONFIG_CBUS_RETU_USER + /* Initialize user-space interface */ + if (retu_user_init() < 0) { + printk(KERN_ERR "Unable to initialize driver\n"); + free_irq(OMAP_GPIO_IRQ(retu_irq_pin), 0); + omap_free_gpio(retu_irq_pin); + return ret; + } +#endif + + return 0; +} + +static int retu_remove(struct device *dev) +{ +#ifdef CONFIG_CBUS_RETU_USER + retu_user_cleanup(); +#endif + /* Mask all RETU interrupts */ + retu_write_reg(RETU_REG_IMR, 0xffff); + free_irq(OMAP_GPIO_IRQ(retu_irq_pin), 0); + omap_free_gpio(retu_irq_pin); + tasklet_kill(&retu_tasklet); + + return 0; +} + +static void retu_device_release(struct device *dev) +{ + complete(&device_release); +} + +static struct device_driver retu_driver = { + .name = "retu", + .bus = &platform_bus_type, + .probe = retu_probe, + .remove = retu_remove, +}; + +static struct platform_device retu_device = { + .name = "retu", + .id = -1, + .dev = { + .release = retu_device_release, + } +}; + +/** + * retu_init - initialise Retu driver + * + * Initialise the Retu driver and return 0 if everything worked ok + */ +static int __init retu_init(void) +{ + int ret = 0; + + printk(KERN_INFO "Retu/Vilma driver initialising\n"); + + init_completion(&device_release); + + if ((ret = driver_register(&retu_driver)) < 0) + return ret; + + if ((ret = platform_device_register(&retu_device)) < 0) { + driver_unregister(&retu_driver); + return ret; + } + return 0; +} + +/* + * Cleanup + */ +static void __exit retu_exit(void) +{ + platform_device_unregister(&retu_device); + driver_unregister(&retu_driver); + wait_for_completion(&device_release); +} + +EXPORT_SYMBOL(retu_request_irq); +EXPORT_SYMBOL(retu_free_irq); +EXPORT_SYMBOL(retu_enable_irq); +EXPORT_SYMBOL(retu_disable_irq); +EXPORT_SYMBOL(retu_ack_irq); +EXPORT_SYMBOL(retu_read_reg); +EXPORT_SYMBOL(retu_write_reg); + +subsys_initcall(retu_init); +module_exit(retu_exit); + +MODULE_DESCRIPTION("Retu ASIC control"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Juha Yrjölä, David Weinehall, and Mikko Ylinen"); diff --git a/drivers/cbus/retu.h b/drivers/cbus/retu.h new file mode 100644 index 00000000000..db20afc13d2 --- /dev/null +++ b/drivers/cbus/retu.h @@ -0,0 +1,72 @@ +/** + * drivers/cbus/retu.h + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Written by Juha Yrjölä and + * David Weinehall + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 __DRIVERS_CBUS_RETU_H +#define __DRIVERS_CBUS_RETU_H + +#include + +/* Registers */ +#define RETU_REG_ASICR 0x00 /* ASIC ID & revision */ +#define RETU_REG_IDR 0x01 /* Interrupt ID */ +#define RETU_REG_IMR 0x02 /* Interrupt mask */ +#define RETU_REG_RTCDSR 0x03 /* RTC seconds register */ +#define RETU_REG_RTCHMR 0x04 /* RTC hours and minutes register */ +#define RETU_REG_RTCHMAR 0x05 /* RTC hours and minutes alarm and time set register */ +#define RETU_REG_RTCCALR 0x06 /* RTC calibration register */ +#define RETU_REG_ADCR 0x08 /* ADC result */ +#define RETU_REG_CC1 0x0d /* Common control register 1 */ +#define RETU_REG_CC2 0x0e /* Common control register 2 */ +#define RETU_REG_CTRL_CLR 0x0f /* Regulator clear register */ +#define RETU_REG_CTRL_SET 0x10 /* Regulator set register */ +#define RETU_REG_STATUS 0x16 /* Status register */ +#define RETU_REG_WATCHDOG 0x17 /* Watchdog register */ +#define RETU_REG_MAX 0x1f + +/* Interrupt sources */ +#define RETU_INT_PWR 0 +#define RETU_INT_CHAR 1 +#define RETU_INT_RTCS 2 +#define RETU_INT_RTCM 3 +#define RETU_INT_RTCD 4 +#define RETU_INT_RTCA 5 +#define RETU_INT_ADCS 8 + +#define MAX_RETU_IRQ_HANDLERS 16 + +int retu_read_reg(int reg); +void retu_write_reg(int reg, u16 val); +int retu_read_adc(int channel); +int retu_request_irq(int id, void *irq_handler, unsigned long arg, char *name); +void retu_free_irq(int id); +void retu_enable_irq(int id); +void retu_disable_irq(int id); +void retu_ack_irq(int id); + +#ifdef CONFIG_CBUS_RETU_USER +int retu_user_init(void); +void retu_user_cleanup(void); +#endif + +extern spinlock_t retu_lock; + +#endif /* __DRIVERS_CBUS_RETU_H */ diff --git a/drivers/cbus/tahvo-usb.c b/drivers/cbus/tahvo-usb.c new file mode 100644 index 00000000000..a002fb71585 --- /dev/null +++ b/drivers/cbus/tahvo-usb.c @@ -0,0 +1,676 @@ +/** + * drivers/cbus/tahvo-usb.c + * + * Tahvo USB transeiver + * + * Copyright (C) 2005 Nokia Corporation + * + * Written by Juha Yrjölä , + * Tony Lindgren , and + * Timo Teräs + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 "cbus.h" +#include "tahvo.h" + +#define DRIVER_NAME "tahvo-usb" + +#define USBR_SLAVE_CONTROL (1 << 8) +#define USBR_VPPVIO_SW (1 << 7) +#define USBR_SPEED (1 << 6) +#define USBR_REGOUT (1 << 5) +#define USBR_MASTER_SW2 (1 << 4) +#define USBR_MASTER_SW1 (1 << 3) +#define USBR_SLAVE_SW (1 << 2) +#define USBR_NSUSPEND (1 << 1) +#define USBR_SEMODE (1 << 0) + +/* bits in OTG_CTRL_REG */ + +/* Bits that are controlled by OMAP OTG and are read-only */ +#define OTG_CTRL_OMAP_MASK (OTG_PULLDOWN|OTG_PULLUP|OTG_DRV_VBUS|\ + OTG_PD_VBUS|OTG_PU_VBUS|OTG_PU_ID) +/* Bits that are controlled by transceiver */ +#define OTG_CTRL_XCVR_MASK (OTG_ASESSVLD|OTG_BSESSEND|\ + OTG_BSESSVLD|OTG_VBUSVLD|OTG_ID) +/* Bits that are controlled by system */ +#define OTG_CTRL_SYS_MASK (OTG_A_BUSREQ|OTG_A_SETB_HNPEN|OTG_B_BUSREQ|\ + OTG_B_HNPEN|OTG_BUSDROP) + +#if defined(CONFIG_USB_OHCI_HCD) && !defined(CONFIG_USB_OTG) +#error tahvo-otg.c does not work with OCHI yet! +#endif + +#define TAHVO_MODE_HOST 0 +#define TAHVO_MODE_PERIPHERAL 1 + +#ifdef CONFIG_USB_OTG +#define TAHVO_MODE(tu) (tu)->tahvo_mode +#elif defined(CONFIG_USB_GADGET_OMAP) +#define TAHVO_MODE(tu) TAHVO_MODE_PERIPHERAL +#else +#define TAHVO_MODE(tu) TAHVO_MODE_HOST +#endif + +extern int ohci_omap_host_enable(struct usb_bus *host, int enable); + +struct tahvo_usb { + struct platform_device *pt_dev; + struct otg_transceiver otg; + int vbus_state; + struct work_struct irq_work; + struct semaphore serialize; +#ifdef CONFIG_USB_OTG + int tahvo_mode; +#endif +}; +static struct platform_device tahvo_usb_device; + +/* + * --------------------------------------------------------------------------- + * OTG related functions + * + * These shoud be separated into omap-otg.c driver module, as they are used + * by various transceivers. These functions are needed in the UDC-only case + * as well. These functions are copied from GPL isp1301_omap.c + * --------------------------------------------------------------------------- + */ +static struct platform_device *tahvo_otg_dev; + +static irqreturn_t omap_otg_irq(int irq, void *arg, struct pt_regs *regs) +{ + struct platform_device *otg_dev = (struct platform_device *) arg; + struct tahvo_usb *tu = (struct tahvo_usb *) otg_dev->dev.driver_data; + u16 otg_irq; + + otg_irq = OTG_IRQ_SRC_REG; + if (otg_irq & OPRT_CHG) { + OTG_IRQ_SRC_REG = OPRT_CHG; + } else if (otg_irq & B_SRP_TMROUT) { + OTG_IRQ_SRC_REG = B_SRP_TMROUT; + } else if (otg_irq & B_HNP_FAIL) { + OTG_IRQ_SRC_REG = B_HNP_FAIL; + } else if (otg_irq & A_SRP_DETECT) { + OTG_IRQ_SRC_REG = A_SRP_DETECT; + } else if (otg_irq & A_REQ_TMROUT) { + OTG_IRQ_SRC_REG = A_REQ_TMROUT; + } else if (otg_irq & A_VBUS_ERR) { + OTG_IRQ_SRC_REG = A_VBUS_ERR; + } else if (otg_irq & DRIVER_SWITCH) { + + if ((!(OTG_CTRL_REG & OTG_DRIVER_SEL)) && + tu->otg.host && tu->otg.state == OTG_STATE_A_HOST) { + /* role is host */ + usb_bus_start_enum(tu->otg.host, + tu->otg.host->otg_port); + } + + OTG_IRQ_SRC_REG = DRIVER_SWITCH; + } else + return IRQ_NONE; + + return IRQ_HANDLED; + +} + +static int omap_otg_init(void) +{ + +#ifdef CONFIG_USB_OTG + if (!tahvo_otg_dev) { + printk("tahvo-usb: no tahvo_otg_dev\n"); + return -ENODEV; + } +#endif + + /* some of these values are board-specific... */ + OTG_SYSCON_2_REG |= OTG_EN + /* for B-device: */ + | SRP_GPDATA /* 9msec Bdev D+ pulse */ + | SRP_GPDVBUS /* discharge after VBUS pulse */ + // | (3 << 24) /* 2msec VBUS pulse */ + /* for A-device: */ + | (0 << 20) /* 200ms nominal A_WAIT_VRISE timer */ + | SRP_DPW /* detect 167+ns SRP pulses */ + | SRP_DATA | SRP_VBUS; /* accept both kinds of SRP pulse */ + + OTG_IRQ_EN_REG = DRIVER_SWITCH | OPRT_CHG + | B_SRP_TMROUT | B_HNP_FAIL + | A_VBUS_ERR | A_SRP_DETECT | A_REQ_TMROUT; + OTG_SYSCON_2_REG |= OTG_EN; + + return 0; +} + +static int omap_otg_probe(struct device *dev) +{ + int ret; + + tahvo_otg_dev = to_platform_device(dev); + ret = omap_otg_init(); + if (ret != 0) { + printk(KERN_ERR "tahvo-usb: omap_otg_init failed\n"); + return ret; + } + + return request_irq(tahvo_otg_dev->resource[1].start, + omap_otg_irq, SA_INTERRUPT, DRIVER_NAME, + &tahvo_usb_device); +} + +static int omap_otg_remove(struct device *dev) +{ + tahvo_otg_dev = NULL; + free_irq(tahvo_otg_dev->resource[1].start, &tahvo_usb_device); + return 0; +} + +struct device_driver omap_otg_driver = { + .name = "omap_otg", + .bus = &platform_bus_type, + .probe = omap_otg_probe, + .remove = omap_otg_remove, +}; + +/* + * --------------------------------------------------------------------------- + * Tahvo related functions + * These are Nokia proprietary code, except for the OTG register settings, + * which are copied from isp1301.c + * --------------------------------------------------------------------------- + */ +static ssize_t vbus_state_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tahvo_usb *tu = (struct tahvo_usb*) device->driver_data; + return sprintf(buf, "%d\n", tu->vbus_state); +} +static DEVICE_ATTR(vbus_state, 0444, vbus_state_show, NULL); + +int vbus_active = 0; + +static void check_vbus_state(struct tahvo_usb *tu) +{ + int reg, prev_state; + + reg = tahvo_read_reg(TAHVO_REG_IDSR); + if (reg & 0x01) { + vbus_active = 1; + switch (tu->otg.state) { + case OTG_STATE_B_IDLE: + /* Enable the gadget driver */ + if (tu->otg.gadget) + usb_gadget_vbus_connect(tu->otg.gadget); + /* Set B-session valid and not B-sessio ended to indicate + * Vbus to be ok. */ + OTG_CTRL_REG = (OTG_CTRL_REG & ~OTG_BSESSEND) | OTG_BSESSVLD; + + tu->otg.state = OTG_STATE_B_PERIPHERAL; + break; + case OTG_STATE_A_IDLE: + /* Session is now valid assuming the USB hub is driving Vbus */ + tu->otg.state = OTG_STATE_A_HOST; + if (tu->otg.host) + ohci_omap_host_enable(tu->otg.host, 1); + break; + default: + break; + } + printk("USB cable connected\n"); + } else { + vbus_active = 0; + switch (tu->otg.state) { + case OTG_STATE_B_PERIPHERAL: + OTG_CTRL_REG = (OTG_CTRL_REG & ~OTG_BSESSVLD) | OTG_BSESSEND; + if (tu->otg.gadget) + usb_gadget_vbus_disconnect(tu->otg.gadget); + tu->otg.state = OTG_STATE_B_IDLE; + break; + case OTG_STATE_A_HOST: + tu->otg.state = OTG_STATE_A_IDLE; + if (tu->otg.host) + ohci_omap_host_enable(tu->otg.host, 0); + + break; + default: + break; + } + printk("USB cable disconnected\n"); + } + + prev_state = tu->vbus_state; + tu->vbus_state = reg & 0x01; + if (prev_state != tu->vbus_state) + kobject_uevent(&tu->pt_dev->dev.kobj, KOBJ_CHANGE); +} + +static void tahvo_usb_become_host(struct tahvo_usb *tu) +{ + /* Clear system and transceiver controlled bits + * also mark the A-session is always valid */ + omap_otg_init(); + OTG_CTRL_REG = (OTG_CTRL_REG & ~(OTG_CTRL_XCVR_MASK|OTG_CTRL_SYS_MASK)) + | OTG_ASESSVLD; + + /* Power up the transceiver in USB host mode */ + tahvo_write_reg(TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND | + USBR_MASTER_SW2 | USBR_MASTER_SW1); + tu->otg.state = OTG_STATE_A_IDLE; + + check_vbus_state(tu); +} + +static void tahvo_usb_stop_host(struct tahvo_usb *tu) +{ + if (tu->otg.host) + ohci_omap_host_enable(tu->otg.host, 0); + tu->otg.state = OTG_STATE_A_IDLE; +} + +static void tahvo_usb_become_peripheral(struct tahvo_usb *tu) +{ + /* Clear system and transceiver controlled bits + * and enable ID to mark peripheral mode and + * BSESSEND to mark no Vbus */ + omap_otg_init(); + OTG_CTRL_REG = (OTG_CTRL_REG & ~(OTG_CTRL_XCVR_MASK|OTG_CTRL_SYS_MASK)) + | OTG_ID | OTG_BSESSEND; + + /* Power up transceiver and set it in USB perhiperal mode */ + tahvo_write_reg(TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT | USBR_NSUSPEND | USBR_SLAVE_SW); + tu->otg.state = OTG_STATE_B_IDLE; + + check_vbus_state(tu); +} + +static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu) +{ + OTG_CTRL_REG = (OTG_CTRL_REG & ~OTG_BSESSVLD) | OTG_BSESSEND; + if (tu->otg.gadget) + usb_gadget_vbus_disconnect(tu->otg.gadget); + tu->otg.state = OTG_STATE_B_IDLE; + +} + +static void tahvo_usb_power_off(struct tahvo_usb *tu) +{ + int id; + + /* Disable gadget controller if any */ + if (tu->otg.gadget) + usb_gadget_vbus_disconnect(tu->otg.gadget); + + if (tu->otg.host) + ohci_omap_host_enable(tu->otg.host, 0); + + /* Disable OTG and interrupts */ + if (TAHVO_MODE(tu) == TAHVO_MODE_PERIPHERAL) + id = OTG_ID; + else id = 0; + OTG_CTRL_REG = (OTG_CTRL_REG & ~(OTG_CTRL_XCVR_MASK|OTG_CTRL_SYS_MASK)) | id; + OTG_IRQ_EN_REG = 0; +#if 0 + OTG_SYSCON_2_REG &= ~OTG_EN; +#endif + + /* Power off transceiver */ + tahvo_write_reg(TAHVO_REG_USBR, 0); + tu->otg.state = OTG_STATE_UNDEFINED; +} + + +static int tahvo_usb_set_power(struct otg_transceiver *dev, unsigned mA) +{ + if (dev->state == OTG_STATE_B_PERIPHERAL) { + /* REVISIT: Can Tahvo charge battery from VBUS? */ + } + return 0; +} + +static int tahvo_usb_set_suspend(struct otg_transceiver *dev, int suspend) +{ + u16 w; + + w = tahvo_read_reg(TAHVO_REG_USBR); + if (suspend) + w &= ~USBR_NSUSPEND; + else + w |= USBR_NSUSPEND; + tahvo_write_reg(TAHVO_REG_USBR, w); + + return 0; +} + +static int tahvo_usb_start_srp(struct otg_transceiver *dev) +{ + struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, otg); + u32 otg_ctrl; + + if (!dev || tu->otg.state != OTG_STATE_B_IDLE) + return -ENODEV; + + otg_ctrl = OTG_CTRL_REG; + if (!(otg_ctrl & OTG_BSESSEND)) + return -EINVAL; + + otg_ctrl |= OTG_B_BUSREQ; + otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_SYS_MASK; + OTG_CTRL_REG = otg_ctrl; + tu->otg.state = OTG_STATE_B_SRP_INIT; + + pr_debug("otg: SRP, %s ... %06x\n", state_name(tu), OTG_CTRL_REG); + + return 0; +} + +static int tahvo_usb_start_hnp(struct otg_transceiver *dev) +{ +#ifdef CONFIG_USB_OTG + /* REVISIT: Add this for OTG */ +#endif + return -EINVAL; +} + +static int tahvo_usb_set_host(struct otg_transceiver *otg, struct usb_bus *host) +{ + struct tahvo_usb *tu = container_of(otg, struct tahvo_usb, otg); + + if (!otg) + return -ENODEV; + +#if defined(CONFIG_USB_OTG) || !defined(CONFIG_USB_GADGET_OMAP) + + down(&tu->serialize); + + if (!host) { + if (TAHVO_MODE(tu) == TAHVO_MODE_HOST) + tahvo_usb_power_off(tu); + tu->otg.host = 0; + up(&tu->serialize); + return 0; + } + + if (TAHVO_MODE(tu) == TAHVO_MODE_HOST) { + tu->otg.host = 0; + tahvo_usb_become_host(tu); + } else + ohci_omap_host_enable(host, 0); + + tu->otg.host = host; + + up(&tu->serialize); +#else + /* No host mode configured, so do not allow host controlled to be set */ + return -EINVAL; +#endif + + return 0; +} + +static int tahvo_usb_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget) +{ + struct tahvo_usb *tu = container_of(otg, struct tahvo_usb, otg); + + if (!otg) + return -ENODEV; + +#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_GADGET_OMAP) + + down(&tu->serialize); + + if (!gadget) { + if (TAHVO_MODE(tu) == TAHVO_MODE_PERIPHERAL) + tahvo_usb_power_off(tu); + tu->otg.gadget = 0; + up(&tu->serialize); + return 0; + } + + tu->otg.gadget = gadget; + if (TAHVO_MODE(tu) == TAHVO_MODE_PERIPHERAL) + tahvo_usb_become_peripheral(tu); + + up(&tu->serialize); +#else + /* No gadget mode configured, so do not allow host controlled to be set */ + return -EINVAL; +#endif + + return 0; +} + +static void tahvo_usb_irq_work(void *data) +{ + struct tahvo_usb *tu = (struct tahvo_usb *)data; + + down(&tu->serialize); + check_vbus_state(tu); + up(&tu->serialize); +} + +static void tahvo_usb_vbus_interrupt(unsigned long arg) +{ + struct tahvo_usb *tu = (struct tahvo_usb *) arg; + + tahvo_ack_irq(TAHVO_INT_VBUSON); + /* Seems we need this to acknowledge the interrupt */ + tahvo_read_reg(TAHVO_REG_IDSR); + schedule_work(&tu->irq_work); +} + +#ifdef CONFIG_USB_OTG +static ssize_t otg_mode_show(struct device *device, + struct device_attribute *attr, char *buf) +{ + struct tahvo_usb *tu = (struct tahvo_usb*) device->driver_data; + switch (tu->tahvo_mode) { + case TAHVO_MODE_HOST: + return sprintf(buf, "host\n"); + case TAHVO_MODE_PERIPHERAL: + return sprintf(buf, "peripheral\n"); + } + return sprintf(buf, "unknown\n"); +} + +static ssize_t otg_mode_store(struct device *device, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct tahvo_usb *tu = (struct tahvo_usb*) device->driver_data; + int r; + + r = strlen(buf); + down(&tu->serialize); + if (strncmp(buf, "host", 4) == 0) { + if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL) + tahvo_usb_stop_peripheral(tu); + tu->tahvo_mode = TAHVO_MODE_HOST; + if (tu->otg.host) { + printk(KERN_INFO "Selected HOST mode: host controller present.\n"); + tahvo_usb_become_host(tu); + } else { + printk(KERN_INFO "Selected HOST mode: no host controller, powering off.\n"); + tahvo_usb_power_off(tu); + } + } else if (strncmp(buf, "peripheral", 10) == 0) { + if (tu->tahvo_mode == TAHVO_MODE_HOST) + tahvo_usb_stop_host(tu); + tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; + if (tu->otg.gadget) { + printk(KERN_INFO "Selected PERIPHERAL mode: gadget driver present.\n"); + tahvo_usb_become_peripheral(tu); + } else { + printk(KERN_INFO "Selected PERIPHERAL mode: no gadget driver, powering off.\n"); + tahvo_usb_power_off(tu); + } + } else + r = -EINVAL; + + up(&tu->serialize); + return r; +} + +static DEVICE_ATTR(otg_mode, 0644, otg_mode_show, otg_mode_store); +#endif + +static int tahvo_usb_probe(struct device *dev) +{ + struct tahvo_usb *tu; + int ret; + + /* Create driver data */ + tu = kmalloc(sizeof(*tu), GFP_KERNEL); + if (!tu) + return -ENOMEM; + memset(tu, 0, sizeof(*tu)); + tu->pt_dev = container_of(dev, struct platform_device, dev); +#ifdef CONFIG_USB_OTG + /* Default mode */ +#ifdef CONFIG_CBUS_TAHVO_USB_HOST_BY_DEFAULT + tu->tahvo_mode = TAHVO_MODE_HOST; +#else + tu->tahvo_mode = TAHVO_MODE_PERIPHERAL; +#endif +#endif + + INIT_WORK(&tu->irq_work, tahvo_usb_irq_work, tu); + init_MUTEX(&tu->serialize); + + /* Set initial state, so that we generate kevents only on + * state changes */ + tu->vbus_state = tahvo_read_reg(TAHVO_REG_IDSR) & 0x01; + + /* We cannot enable interrupt until omap_udc is initialized */ + ret = tahvo_request_irq(TAHVO_INT_VBUSON, tahvo_usb_vbus_interrupt, + (unsigned long) tu, "vbus_interrupt"); + if (ret != 0) { + kfree(tu); + printk(KERN_ERR "Could not register Tahvo interrupt for VBUS\n"); + return ret; + } + + /* Attributes */ + device_create_file(dev, &dev_attr_vbus_state); +#ifdef CONFIG_USB_OTG + device_create_file(dev, &dev_attr_otg_mode); +#endif + + /* Create OTG interface */ + tahvo_usb_power_off(tu); + tu->otg.state = OTG_STATE_UNDEFINED; + tu->otg.label = DRIVER_NAME; + tu->otg.set_host = tahvo_usb_set_host; + tu->otg.set_peripheral = tahvo_usb_set_peripheral; + tu->otg.set_power = tahvo_usb_set_power; + tu->otg.set_suspend = tahvo_usb_set_suspend; + tu->otg.start_srp = tahvo_usb_start_srp; + tu->otg.start_hnp = tahvo_usb_start_hnp; + + ret = otg_set_transceiver(&tu->otg); + if (ret < 0) { + printk(KERN_ERR "Cannot register USB transceiver\n"); + kfree(tu); + tahvo_free_irq(TAHVO_INT_VBUSON); + return ret; + } + + dev->driver_data = tu; + + /* Act upon current vbus state once at startup. A vbus state irq may or + * may not be generated in addition to this. */ + schedule_work(&tu->irq_work); + return 0; +} + +static int tahvo_usb_remove(struct device *dev) +{ + tahvo_free_irq(TAHVO_INT_VBUSON); + flush_scheduled_work(); + otg_set_transceiver(0); + device_remove_file(dev, &dev_attr_vbus_state); +#ifdef CONFIG_USB_OTG + device_remove_file(dev, &dev_attr_otg_mode); +#endif + return 0; +} + +static struct device_driver tahvo_usb_driver = { + .name = "tahvo-usb", + .bus = &platform_bus_type, + .probe = tahvo_usb_probe, + .remove = tahvo_usb_remove, +}; + +static struct platform_device tahvo_usb_device = { + .name = "tahvo-usb", + .id = -1, +}; + +static int __init tahvo_usb_init(void) +{ + int ret = 0; + + printk(KERN_INFO "Tahvo USB transceiver driver initializing\n"); + ret = driver_register(&tahvo_usb_driver); + if (ret) + return ret; + ret = platform_device_register(&tahvo_usb_device); + if (ret < 0) { + driver_unregister(&tahvo_usb_driver); + return ret; + } + ret = driver_register(&omap_otg_driver); + if (ret) { + platform_device_unregister(&tahvo_usb_device); + driver_unregister(&tahvo_usb_driver); + return ret; + } + return 0; +} + +subsys_initcall(tahvo_usb_init); + +static void __exit tahvo_usb_exit(void) +{ + driver_unregister(&omap_otg_driver); + platform_device_unregister(&tahvo_usb_device); + driver_unregister(&tahvo_usb_driver); +} +module_exit(tahvo_usb_exit); + +MODULE_DESCRIPTION("Tahvo USB OTG Transceiver Driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs"); diff --git a/drivers/cbus/tahvo-user.c b/drivers/cbus/tahvo-user.c new file mode 100644 index 00000000000..7e6f7707025 --- /dev/null +++ b/drivers/cbus/tahvo-user.c @@ -0,0 +1,396 @@ +/** + * drivers/cbus/tahvo-user.c + * + * Tahvo user space interface functions + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Written by Mikko Ylinen + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 "tahvo.h" + +#include "user_retu_tahvo.h" + +/* Maximum size of IRQ node buffer/pool */ +#define TAHVO_MAX_IRQ_BUF_LEN 16 + +#define PFX "tahvo-user: " + +/* Bitmap for marking the interrupt sources as having the handlers */ +static u32 tahvo_irq_bits; + +/* For allowing only one user process to subscribe to the tahvo interrupts */ +static struct file *tahvo_irq_subscr = NULL; + +/* For poll and IRQ passing */ +struct tahvo_irq { + u32 id; + struct list_head node; +}; + +static spinlock_t tahvo_irqs_lock; +static struct tahvo_irq *tahvo_irq_block; +static LIST_HEAD(tahvo_irqs); +static LIST_HEAD(tahvo_irqs_reserve); + +/* Wait queue - used when user wants to read the device */ +DECLARE_WAIT_QUEUE_HEAD(tahvo_user_waitqueue); + +/* Semaphore to protect irq subscription sequence */ +static struct semaphore tahvo_sem; + +/* This array specifies TAHVO register types (read/write/toggle) */ +static const u8 tahvo_access_bits[] = { + 1, + 4, + 1, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 3, + 1 +}; + +/* + * The handler for all TAHVO interrupts. + * + * arg is the interrupt source in TAHVO. + */ +static void tahvo_user_irq_handler(unsigned long arg) +{ + struct tahvo_irq *irq; + + /* user has to re-enable the interrupt once ready + * for receiving them again */ + tahvo_disable_irq(arg); + tahvo_ack_irq(arg); + + spin_lock(&tahvo_irqs_lock); + if (list_empty(&tahvo_irqs_reserve)) { + spin_unlock(&tahvo_irqs_lock); + return; + } + irq = list_entry((&tahvo_irqs_reserve)->next, struct tahvo_irq, node); + irq->id = arg; + list_move_tail(&irq->node, &tahvo_irqs); + spin_unlock(&tahvo_irqs_lock); + + /* wake up waiting thread */ + wake_up(&tahvo_user_waitqueue); +} + +/* + * This routine sets up the interrupt handler and marks an interrupt source + * in TAHVO as a candidate for signal delivery to the user process. + */ +static int tahvo_user_subscribe_to_irq(int id, struct file *filp) +{ + int ret; + + down(&tahvo_sem); + if ((tahvo_irq_subscr != NULL) && (tahvo_irq_subscr != filp)) { + up(&tahvo_sem); + return -EBUSY; + } + /* Store the file pointer of the first user process registering IRQs */ + tahvo_irq_subscr = filp; + up(&tahvo_sem); + + if (tahvo_irq_bits & (1 << id)) + return 0; + + ret = tahvo_request_irq(id, tahvo_user_irq_handler, id, ""); + if (ret < 0) + return ret; + + /* Mark that this interrupt has a handler */ + tahvo_irq_bits |= 1 << id; + + return 0; +} + +/* + * Unregister all TAHVO interrupt handlers + */ +static void tahvo_unreg_irq_handlers(void) +{ + int id; + + if (!tahvo_irq_bits) + return; + + for (id = 0; id < MAX_TAHVO_IRQ_HANDLERS; id++) + if (tahvo_irq_bits & (1 << id)) + tahvo_free_irq(id); + + tahvo_irq_bits = 0; +} + +/* + * Write to TAHVO register. + * Returns 0 upon success, a negative error value otherwise. + */ +static int tahvo_user_write_with_mask(u32 field, u16 value) +{ + u32 mask; + u32 reg; + u_short tmp; + unsigned long flags; + + mask = MASK(field); + reg = REG(field); + + /* Detect bad mask and reg */ + if (mask == 0 || reg > TAHVO_REG_MAX || + tahvo_access_bits[reg] == READ_ONLY) { + printk(KERN_ERR PFX "invalid arguments (reg=%#x, mask=%#x)\n", + reg, mask); + return -EINVAL; + } + + /* Justify value according to mask */ + while (!(mask & 1)) { + value = value << 1; + mask = mask >> 1; + } + + spin_lock_irqsave(&tahvo_lock, flags); + if (tahvo_access_bits[reg] == TOGGLE) { + /* No need to detect previous content of register */ + tmp = 0; + } else { + /* Read current value of register */ + tmp = tahvo_read_reg(reg); + } + /* Generate a new value */ + tmp = (tmp & ~MASK(field)) | (value & MASK(field)); + /* Write data to TAHVO */ + tahvo_write_reg(reg, tmp); + spin_unlock_irqrestore(&tahvo_lock, flags); + + return 0; +} + +/* + * Read TAHVO register. + */ +static u32 tahvo_user_read_with_mask(u32 field) +{ + u_short value; + u32 mask, reg; + + mask = MASK(field); + reg = REG(field); + + /* Detect bad mask and reg */ + if (mask == 0 || reg > TAHVO_REG_MAX) { + printk(KERN_ERR PFX "invalid arguments (reg=%#x, mask=%#x)\n", + reg, mask); + return -EINVAL; + } + + /* Read the register */ + value = tahvo_read_reg(reg) & mask; + + /* Right justify value */ + while (!(mask & 1)) { + value = value >> 1; + mask = mask >> 1; + } + + return value; +} + +/* + * Close device + */ +static int tahvo_close(struct inode *inode, struct file *filp) +{ + /* Unregister all interrupts that have been registered */ + if (tahvo_irq_subscr == filp) { + tahvo_unreg_irq_handlers(); + tahvo_irq_subscr = NULL; + } + + return 0; +} + +/* + * Device control (ioctl) + */ +static int tahvo_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + struct retu_tahvo_write_parms par; + + switch (cmd) { + case URT_IOCT_IRQ_SUBSCR: + return tahvo_user_subscribe_to_irq(arg, filp); + case TAHVO_IOCH_READ: + return tahvo_user_read_with_mask(arg); + case TAHVO_IOCX_WRITE: + copy_from_user(&par, (void __user *) arg, sizeof(par)); + par.result = tahvo_user_write_with_mask(par.field, par.value); + copy_to_user((void __user *) arg, &par, sizeof(par)); + break; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +/* + * Read from device + */ +static ssize_t tahvo_read(struct file *filp, char *buf, size_t count, + loff_t * offp) +{ + struct tahvo_irq *irq; + + u32 nr, i; + + /* read not permitted if neither filp nor anyone has registered IRQs */ + if (tahvo_irq_subscr != filp) + return -EPERM; + + if ((count < sizeof(u32)) || ((count % sizeof(u32)) != 0)) + return -EINVAL; + + nr = count / sizeof(u32); + + for (i = 0; i < nr; i++) { + unsigned long flags; + u32 irq_id; + int ret; + + ret = wait_event_interruptible(tahvo_user_waitqueue, + !list_empty(&tahvo_irqs)); + if (ret < 0) + return ret; + + spin_lock_irqsave(&tahvo_irqs_lock, flags); + irq = list_entry((&tahvo_irqs)->next, struct tahvo_irq, node); + irq_id = irq->id; + list_move(&irq->node, &tahvo_irqs_reserve); + spin_unlock_irqrestore(&tahvo_irqs_lock, flags); + + copy_to_user(buf + i * sizeof(irq_id), &irq_id, sizeof(irq_id)); + } + + return count; +} + +/* + * Poll method + */ +static unsigned tahvo_poll(struct file *filp, struct poll_table_struct *pt) +{ + if (!list_empty(&tahvo_irqs)) + return POLLIN; + + poll_wait(filp, &tahvo_user_waitqueue, pt); + + if (!list_empty(&tahvo_irqs)) + return POLLIN; + else + return 0; +} + +static struct file_operations tahvo_user_fileops = { + .owner = THIS_MODULE, + .ioctl = tahvo_ioctl, + .read = tahvo_read, + .release = tahvo_close, + .poll = tahvo_poll +}; + +static struct miscdevice tahvo_device = { + .minor = MISC_DYNAMIC_MINOR, + .name = "tahvo", + .fops = &tahvo_user_fileops +}; + +/* + * Initialization + * + * @return 0 if successful, error value otherwise. + */ +int tahvo_user_init(void) +{ + struct tahvo_irq *irq; + int res, i; + + irq = kmalloc(sizeof(*irq) * TAHVO_MAX_IRQ_BUF_LEN, GFP_KERNEL); + if (irq == NULL) { + printk(KERN_ERR PFX "kmalloc failed\n"); + return -ENOMEM; + } + memset(irq, 0, sizeof(*irq) * TAHVO_MAX_IRQ_BUF_LEN); + for (i = 0; i < TAHVO_MAX_IRQ_BUF_LEN; i++) + list_add(&irq[i].node, &tahvo_irqs_reserve); + + tahvo_irq_block = irq; + + spin_lock_init(&tahvo_irqs_lock); + sema_init(&tahvo_sem, 1); + + /* Request a misc device */ + res = misc_register(&tahvo_device); + if (res < 0) { + printk(KERN_ERR PFX "unable to register misc device for %s\n", + tahvo_device.name); + kfree(irq); + return res; + } + + return 0; +} + +/* + * Cleanup. + */ +void tahvo_user_cleanup(void) +{ + /* Unregister our misc device */ + misc_deregister(&tahvo_device); + /* Unregister and disable all TAHVO interrupts */ + tahvo_unreg_irq_handlers(); + kfree(tahvo_irq_block); +} + +MODULE_DESCRIPTION("Tahvo ASIC user space functions"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mikko Ylinen"); diff --git a/drivers/cbus/tahvo.c b/drivers/cbus/tahvo.c new file mode 100644 index 00000000000..6693a74e303 --- /dev/null +++ b/drivers/cbus/tahvo.c @@ -0,0 +1,421 @@ +/** + * drivers/cbus/tahvo.c + * + * Support functions for Tahvo ASIC + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Written by Juha Yrjölä , + * David Weinehall , and + * Mikko Ylinen + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 "cbus.h" +#include "tahvo.h" + +#define TAHVO_ID 0x02 +#define PFX "tahvo: " + +static int tahvo_initialized; +static int tahvo_irq_pin; +static int tahvo_is_betty; + +static struct tasklet_struct tahvo_tasklet; +spinlock_t tahvo_lock = SPIN_LOCK_UNLOCKED; + +static struct completion device_release; + +struct tahvo_irq_handler_desc { + int (*func)(unsigned long); + unsigned long arg; + char name[8]; +}; + +static struct tahvo_irq_handler_desc tahvo_irq_handlers[MAX_TAHVO_IRQ_HANDLERS]; + +/** + * tahvo_read_reg - Read a value from a register in Tahvo + * @reg: the register to read from + * + * This function returns the contents of the specified register + */ +int tahvo_read_reg(int reg) +{ + BUG_ON(!tahvo_initialized); + return cbus_read_reg(cbus_host, TAHVO_ID, reg); +} + +/** + * tahvo_write_reg - Write a value to a register in Tahvo + * @reg: the register to write to + * @reg: the value to write to the register + * + * This function writes a value to the specified register + */ +void tahvo_write_reg(int reg, u16 val) +{ + BUG_ON(!tahvo_initialized); + cbus_write_reg(cbus_host, TAHVO_ID, reg, val); +} + +/* + * Disable given TAHVO interrupt + */ +void tahvo_disable_irq(int id) +{ + unsigned long flags; + u16 mask; + + spin_lock_irqsave(&tahvo_lock, flags); + mask = tahvo_read_reg(TAHVO_REG_IMR); + mask |= 1 << id; + tahvo_write_reg(TAHVO_REG_IMR, mask); + spin_unlock_irqrestore(&tahvo_lock, flags); +} + +/* + * Enable given TAHVO interrupt + */ +void tahvo_enable_irq(int id) +{ + unsigned long flags; + u16 mask; + + spin_lock_irqsave(&tahvo_lock, flags); + mask = tahvo_read_reg(TAHVO_REG_IMR); + mask &= ~(1 << id); + tahvo_write_reg(TAHVO_REG_IMR, mask); + spin_unlock_irqrestore(&tahvo_lock, flags); +} + +/* + * Acknowledge given TAHVO interrupt + */ +void tahvo_ack_irq(int id) +{ + tahvo_write_reg(TAHVO_REG_IDR, 1 << id); +} + +static int tahvo_7bit_backlight; + +int tahvo_get_backlight_level(void) +{ + int mask; + + if (tahvo_7bit_backlight) + mask = 0x7f; + else + mask = 0x0f; + return tahvo_read_reg(TAHVO_REG_LEDPWMR) & mask; +} + +int tahvo_get_max_backlight_level(void) +{ + if (tahvo_7bit_backlight) + return 0x7f; + else + return 0x0f; +} + +void tahvo_set_backlight_level(int level) +{ + int max_level; + + max_level = tahvo_get_max_backlight_level(); + if (level > max_level) + level = max_level; + tahvo_write_reg(TAHVO_REG_LEDPWMR, level); +} + +/* + * TAHVO interrupt handler. Only schedules the tasklet. + */ +static irqreturn_t tahvo_irq_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + tasklet_schedule(&tahvo_tasklet); + return IRQ_HANDLED; +} + +/* + * Tasklet handler + */ +static void tahvo_tasklet_handler(unsigned long data) +{ + struct tahvo_irq_handler_desc *hnd; + u16 id; + u16 im; + int i; + + for (;;) { + id = tahvo_read_reg(TAHVO_REG_IDR); + im = ~tahvo_read_reg(TAHVO_REG_IMR); + id &= im; + + if (!id) + break; + + for (i = 0; id != 0; i++, id >>= 1) { + if (!(id & 1)) + continue; + hnd = &tahvo_irq_handlers[i]; + if (hnd->func == NULL) { + /* Spurious tahvo interrupt - just ack it */ + printk(KERN_INFO "Spurious Tahvo interrupt " + "(id %d)\n", i); + tahvo_disable_irq(i); + tahvo_ack_irq(i); + continue; + } + hnd->func(hnd->arg); + /* + * Don't acknowledge the interrupt here + * It must be done explicitly + */ + } + } +} + +/* + * Register the handler for a given TAHVO interrupt source. + */ +int tahvo_request_irq(int id, void *irq_handler, unsigned long arg, char *name) +{ + struct tahvo_irq_handler_desc *hnd; + + if (irq_handler == NULL || id >= MAX_TAHVO_IRQ_HANDLERS || + name == NULL) { + printk(KERN_ERR PFX "Invalid arguments to %s\n", + __FUNCTION__); + return -EINVAL; + } + hnd = &tahvo_irq_handlers[id]; + if (hnd->func != NULL) { + printk(KERN_ERR PFX "IRQ %d already reserved\n", id); + return -EBUSY; + } + printk(KERN_INFO PFX "Registering interrupt %d for device %s\n", + id, name); + hnd->func = irq_handler; + hnd->arg = arg; + strlcpy(hnd->name, name, sizeof(hnd->name)); + + tahvo_ack_irq(id); + tahvo_enable_irq(id); + + return 0; +} + +/* + * Unregister the handler for a given TAHVO interrupt source. + */ +void tahvo_free_irq(int id) +{ + struct tahvo_irq_handler_desc *hnd; + + if (id >= MAX_TAHVO_IRQ_HANDLERS) { + printk(KERN_ERR PFX "Invalid argument to %s\n", + __FUNCTION__); + return; + } + hnd = &tahvo_irq_handlers[id]; + if (hnd->func == NULL) { + printk(KERN_ERR PFX "IRQ %d already freed\n", id); + return; + } + + tahvo_disable_irq(id); + hnd->func = NULL; +} + +/** + * tahvo_probe - Probe for Tahvo ASIC + * @dev: the Tahvo device + * + * Probe for the Tahvo ASIC and allocate memory + * for its device-struct if found + */ +static int __devinit tahvo_probe(struct device *dev) +{ + const struct omap_em_asic_bb5_config * em_asic_config; + int rev, id, ret; + + /* Prepare tasklet */ + tasklet_init(&tahvo_tasklet, tahvo_tasklet_handler, 0); + + em_asic_config = omap_get_config(OMAP_TAG_EM_ASIC_BB5, + struct omap_em_asic_bb5_config); + if (em_asic_config == NULL) { + printk(KERN_ERR PFX "Unable to retrieve config data\n"); + return -ENODATA; + } + + tahvo_initialized = 1; + + rev = tahvo_read_reg(TAHVO_REG_ASICR); + + id = (rev >> 8) & 0xff; + if (id == 0x03) { + if ((rev & 0xff) >= 0x50) + tahvo_7bit_backlight = 1; + } else if (id == 0x0b) { + tahvo_is_betty = 1; + tahvo_7bit_backlight = 1; + } else { + printk(KERN_ERR "Tahvo/Betty chip not found"); + return -ENODEV; + } + + printk(KERN_INFO "%s v%d.%d found\n", tahvo_is_betty ? "Betty" : "Tahvo", + (rev >> 4) & 0x0f, rev & 0x0f); + + tahvo_irq_pin = em_asic_config->tahvo_irq_gpio; + + if ((ret = omap_request_gpio(tahvo_irq_pin)) < 0) { + printk(KERN_ERR PFX "Unable to reserve IRQ GPIO\n"); + return ret; + } + + /* Set the pin as input */ + omap_set_gpio_direction(tahvo_irq_pin, 1); + + /* Rising edge triggers the IRQ */ + set_irq_type(OMAP_GPIO_IRQ(tahvo_irq_pin), IRQT_RISING); + + /* Mask all TAHVO interrupts */ + tahvo_write_reg(TAHVO_REG_IMR, 0xffff); + + ret = request_irq(OMAP_GPIO_IRQ(tahvo_irq_pin), tahvo_irq_handler, 0, + "tahvo", 0); + if (ret < 0) { + printk(KERN_ERR PFX "Unable to register IRQ handler\n"); + omap_free_gpio(tahvo_irq_pin); + return ret; + } +#ifdef CONFIG_CBUS_TAHVO_USER + /* Initialize user-space interface */ + if (tahvo_user_init() < 0) { + printk(KERN_ERR "Unable to initialize driver\n"); + free_irq(OMAP_GPIO_IRQ(tahvo_irq_pin), 0); + omap_free_gpio(tahvo_irq_pin); + return ret; + } +#endif + return 0; +} + +static int tahvo_remove(struct device *dev) +{ +#ifdef CONFIG_CBUS_TAHVO_USER + tahvo_user_cleanup(); +#endif + /* Mask all TAHVO interrupts */ + tahvo_write_reg(TAHVO_REG_IMR, 0xffff); + free_irq(OMAP_GPIO_IRQ(tahvo_irq_pin), 0); + omap_free_gpio(tahvo_irq_pin); + tasklet_kill(&tahvo_tasklet); + + return 0; +} + +static void tahvo_device_release(struct device *dev) +{ + complete(&device_release); +} + +static struct device_driver tahvo_driver = { + .name = "tahvo", + .bus = &platform_bus_type, + .probe = tahvo_probe, + .remove = tahvo_remove, +}; + +static struct platform_device tahvo_device = { + .name = "tahvo", + .id = -1, + .dev = { + .release = tahvo_device_release, + } +}; + +/** + * tahvo_init - initialise Tahvo driver + * + * Initialise the Tahvo driver and return 0 if everything worked ok + */ +static int __init tahvo_init(void) +{ + int ret = 0; + + printk(KERN_INFO "Tahvo/Betty driver initialising\n"); + + init_completion(&device_release); + + if ((ret = driver_register(&tahvo_driver)) < 0) + return ret; + + if ((ret = platform_device_register(&tahvo_device)) < 0) { + driver_unregister(&tahvo_driver); + return ret; + } + return 0; +} + +/* + * Cleanup + */ +static void __exit tahvo_exit(void) +{ + platform_device_unregister(&tahvo_device); + driver_unregister(&tahvo_driver); + wait_for_completion(&device_release); +} + +EXPORT_SYMBOL(tahvo_request_irq); +EXPORT_SYMBOL(tahvo_free_irq); +EXPORT_SYMBOL(tahvo_enable_irq); +EXPORT_SYMBOL(tahvo_disable_irq); +EXPORT_SYMBOL(tahvo_ack_irq); +EXPORT_SYMBOL(tahvo_read_reg); +EXPORT_SYMBOL(tahvo_write_reg); +EXPORT_SYMBOL(tahvo_get_backlight_level); +EXPORT_SYMBOL(tahvo_get_max_backlight_level); +EXPORT_SYMBOL(tahvo_set_backlight_level); + +subsys_initcall(tahvo_init); +module_exit(tahvo_exit); + +MODULE_DESCRIPTION("Tahvo ASIC control"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Juha Yrjölä, David Weinehall, and Mikko Ylinen"); diff --git a/drivers/cbus/tahvo.h b/drivers/cbus/tahvo.h new file mode 100644 index 00000000000..3cde3662333 --- /dev/null +++ b/drivers/cbus/tahvo.h @@ -0,0 +1,60 @@ +/* + * drivers/cbus/tahvo.h + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Written by Juha Yrjölä and + * David Weinehall + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 __DRIVERS_CBUS_TAHVO_H +#define __DRIVERS_CBUS_TAHVO_H + +#include + +/* Registers */ +#define TAHVO_REG_ASICR 0x00 /* ASIC ID & revision */ +#define TAHVO_REG_IDR 0x01 /* Interrupt ID */ +#define TAHVO_REG_IDSR 0x02 /* Interrupt status */ +#define TAHVO_REG_IMR 0x03 /* Interrupt mask */ +#define TAHVO_REG_LEDPWMR 0x05 /* LED PWM */ +#define TAHVO_REG_USBR 0x06 /* USB control */ +#define TAHVO_REG_MAX 0x0d + +/* Interrupt sources */ +#define TAHVO_INT_VBUSON 0 + +#define MAX_TAHVO_IRQ_HANDLERS 8 + +int tahvo_read_reg(int reg); +void tahvo_write_reg(int reg, u16 val); +int tahvo_request_irq(int id, void *irq_handler, unsigned long arg, char *name); +void tahvo_free_irq(int id); +void tahvo_enable_irq(int id); +void tahvo_disable_irq(int id); +void tahvo_ack_irq(int id); +int tahvo_get_backlight_level(void); +int tahvo_get_max_backlight_level(void); +void tahvo_set_backlight_level(int level); + +#ifdef CONFIG_CBUS_TAHVO_USER +int tahvo_user_init(void); +void tahvo_user_cleanup(void); +#endif + +extern spinlock_t tahvo_lock; + +#endif /* __DRIVERS_CBUS_TAHVO_H */ diff --git a/drivers/cbus/user_retu_tahvo.h b/drivers/cbus/user_retu_tahvo.h new file mode 100644 index 00000000000..a5c219026c5 --- /dev/null +++ b/drivers/cbus/user_retu_tahvo.h @@ -0,0 +1,75 @@ +/** + * drivers/cbus/user_retu_tahvo.h + * + * Copyright (C) 2004, 2005 Nokia Corporation + * + * Written by Mikko Ylinen + * + * Definitions and types used by both retu-user and tahvo-user. + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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 _USER_RETU_TAHVO_H +#define _USER_RETU_TAHVO_H + +/* Chip IDs */ +#define CHIP_RETU 1 +#define CHIP_TAHVO 2 + +/* Register access type bits */ +#define READ_ONLY 1 +#define WRITE_ONLY 2 +#define READ_WRITE 3 +#define TOGGLE 4 + +#define MASK(field) ((u16)(field & 0xFFFF)) +#define REG(field) ((u16)((field >> 16) & 0x3F)) + +/*** IOCTL definitions. These should be kept in sync with user space **********/ + +#define URT_IOC_MAGIC '`' + +/* + * IOCTL function naming conventions: + * ================================== + * 0 -- No argument and return value + * S -- Set through a pointer + * T -- Tell directly with the argument value + * G -- Reply by setting through a pointer + * Q -- response is on the return value + * X -- S and G atomically + * H -- T and Q atomically + */ + +/* General */ +#define URT_IOCT_IRQ_SUBSCR _IO(URT_IOC_MAGIC, 0) + +/* RETU */ +#define RETU_IOCH_READ _IO(URT_IOC_MAGIC, 1) +#define RETU_IOCX_WRITE _IO(URT_IOC_MAGIC, 2) +#define RETU_IOCH_ADC_READ _IO(URT_IOC_MAGIC, 3) + +/* TAHVO */ +#define TAHVO_IOCH_READ _IO(URT_IOC_MAGIC, 4) +#define TAHVO_IOCX_WRITE _IO(URT_IOC_MAGIC, 5) + +/* This structure is used for writing RETU/TAHVO registers */ +struct retu_tahvo_write_parms { + u32 field; + u16 value; + u8 result; +}; + +#endif diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 05ba410682a..94982e0e41a 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -667,6 +667,16 @@ config HW_RANDOM If unsure, say N. +config OMAP_RNG + tristate "OMAP Random Number Generator support" + depends on ARCH_OMAP16XX || ARCH_OMAP24XX + ---help--- + This driver provides kernel-side support for the Random Number + Generator hardware found on OMAP16xx and OMAP24xx multimedia + processors. + + If unsure, say N. + config NVRAM tristate "/dev/nvram support" depends on ATARI || X86 || ARM || GENERIC_NVRAM @@ -769,6 +779,11 @@ config GEN_RTC_X config EFI_RTC bool "EFI Real Time Clock Services" depends on IA64 +config OMAP_RTC + bool "TI OMAP Real Time Clock" + depends on ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 + help + Support for TI OMAP RTC config DS1302 tristate "DS1302 RTC support" diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 503dd901d40..2441f119bba 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o obj-$(CONFIG_DS1302) += ds1302.o obj-$(CONFIG_S3C2410_RTC) += s3c2410-rtc.o obj-$(CONFIG_RTC_VR41XX) += vr41xx_rtc.o +obj-$(CONFIG_OMAP_RTC)+= omap-rtc.o ifeq ($(CONFIG_GENERIC_NVRAM),y) obj-$(CONFIG_NVRAM) += generic_nvram.o else @@ -75,6 +76,7 @@ obj-$(CONFIG_TOSHIBA) += toshiba.o obj-$(CONFIG_I8K) += i8k.o obj-$(CONFIG_DS1620) += ds1620.o obj-$(CONFIG_HW_RANDOM) += hw_random.o +obj-$(CONFIG_OMAP_RNG) += omap-rng.o obj-$(CONFIG_FTAPE) += ftape/ obj-$(CONFIG_COBALT_LCD) += lcd.o obj-$(CONFIG_PPDEV) += ppdev.o diff --git a/drivers/char/omap-rng.c b/drivers/char/omap-rng.c new file mode 100644 index 00000000000..cc3290ef70d --- /dev/null +++ b/drivers/char/omap-rng.c @@ -0,0 +1,98 @@ +/* + * drivers/char/omap-rng.c + * + * Copyright (C) 2005 Nokia Corporation + * Author: Juha Yrjölä + * + * OMAP16xx and OMAP24xx Random Number Generator driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include + +#include + +#if defined (CONFIG_ARCH_OMAP16XX) +#define RNG_BASE 0xfffe5000 +#endif +#if defined (CONFIG_ARCH_OMAP24XX) +#define RNG_BASE 0x480A0000 +#endif + +#define RNG_OUT_REG 0x00 /* Output register */ +#define RNG_STAT_REG 0x04 /* Status register + [0] = STAT_BUSY */ +#define RNG_ALARM_REG 0x24 /* Alarm register + [7:0] = ALARM_COUNTER */ +#define RNG_CONFIG_REG 0x28 /* Configuration register + [11:6] = RESET_COUNT + [5:3] = RING2_DELAY + [2:0] = RING1_DELAY */ +#define RNG_REV_REG 0x3c /* Revision register + [7:0] = REV_NB */ +#define RNG_MASK_REG 0x40 /* Mask and reset register + [2] = IT_EN + [1] = SOFTRESET + [0] = AUTOIDLE */ +#define RNG_SYSSTATUS 0x44 /* System status + [0] = RESETDONE */ + +#define ENTROPY_WORD_COUNT 128 + +static u32 rng_base = io_p2v(RNG_BASE); + +static struct clk *rng_ick = NULL; + +static u32 rng_read_reg(int reg) +{ + return __raw_readl(rng_base + reg); +} + +static void rng_write_reg(int reg, u32 val) +{ + __raw_writel(val, rng_base + reg); +} + +static void rng_feed_entropy(int count) +{ + u32 l; + + while (count--) { + while (rng_read_reg(RNG_STAT_REG)); + l = rng_read_reg(RNG_OUT_REG); + add_input_randomness(0, 0, l); + } +} + +static int __init rng_init(void) +{ + if (!cpu_is_omap16xx() && !cpu_is_omap24xx()) + return -ENODEV; + + if (cpu_is_omap24xx()) { + rng_ick = clk_get(NULL, "rng_ick"); + if (IS_ERR(rng_ick)) { + printk(KERN_ERR "omap-rng.c: Could not get rng_ick\n"); + return PTR_ERR(rng_ick); + } + clk_enable(rng_ick); + } + + printk("OMAP Random Number Generator ver. %02x\n", + rng_read_reg(RNG_REV_REG)); + rng_write_reg(RNG_MASK_REG, 0x00000001); + rng_feed_entropy(ENTROPY_WORD_COUNT); + rng_write_reg(RNG_MASK_REG, 0x00000000); + printk("%d words of entropy generated\n", ENTROPY_WORD_COUNT); + + return 0; +} +late_initcall(rng_init); diff --git a/drivers/char/omap-rtc.c b/drivers/char/omap-rtc.c new file mode 100644 index 00000000000..d0e0014b705 --- /dev/null +++ b/drivers/char/omap-rtc.c @@ -0,0 +1,789 @@ +/* + * TI OMAP Real Time Clock interface for Linux + * + * Copyright (C) 2003 MontaVista Software, Inc. + * Author: George G. Davis or + * + * Initially based on linux-2.4.20/drivers/char/rtc.c + * Copyright (C) 1996 Paul Gortmaker + * + * This driver allows use of the real time clock (built into + * nearly all computers) from user space. It exports the /dev/rtc + * interface supporting various ioctl() and also the + * /proc/driver/rtc pseudo-file for status information. + * + * The ioctls can be used to set the interrupt behaviour from the + * RTC via IRQs. Then the /dev/rtc interface can be used to make + * use of RTC interrupts, be they time update or alarm based. + * + * The /dev/rtc interface will block on reads until an interrupt + * has been received. If a RTC interrupt has already happened, + * it will output an unsigned long and then block. The output value + * contains the interrupt status in the low byte and the number of + * interrupts since the last read in the remaining high bytes. The + * /dev/rtc interface can also be used with the select(2) call. + * + * 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. + * + * Based on other minimal char device drivers, like Alan's + * watchdog, Ted's random, etc. etc. + * + * Change Log : + * v1.0 Initial version based on rtc.c v1.10e + * Added support for 2.6 kernel, + * - changed the return value of the interrupt handler + */ + +/* + * Note that *all* calls to CMOS_READ and CMOS_WRITE are done with + * interrupts disabled. + * REVISIT: Elaborate on OMAP1510 TRM 15uS BUSY access rule. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "omap-rtc.h" + +extern spinlock_t rtc_lock; + +static int omap_rtc_alarm = NO_IRQ; +static int omap_rtc_timer = NO_IRQ; + + +/* OMAP RTC register access macros: */ + +#define CMOS_READ(addr) omap_readb(addr) +#define CMOS_WRITE(val, addr) omap_writeb(val, addr) + +static struct fasync_struct *rtc_async_queue; + +static DECLARE_WAIT_QUEUE_HEAD(rtc_wait); + +static void get_rtc_time (struct rtc_time *rtc_tm); +static void get_rtc_alm_time (struct rtc_time *alm_tm); + +static void set_rtc_irq_bit(unsigned char bit); +static void mask_rtc_irq_bit(unsigned char bit); + +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + +/* + * Bits in rtc_status. (7 bits of room for future expansion) + */ + +#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */ + +/* + * REVISIT: fix this comment: + * rtc_status is never changed by rtc_interrupt, and ioctl/open/close is + * protected by the big kernel lock. + */ +static unsigned long rtc_status = 0; /* bitmapped status byte. */ +static unsigned long rtc_irq_data = 0; /* our output to the world */ + +/* + * If this driver ever becomes modularised, it will be really nice + * to make the epoch retain its value across module reload... + */ + +static unsigned long epoch = 1900; /* year corresponding to 0x00 */ + +static const unsigned char days_in_mo[] = +{0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +/* + * A very tiny interrupt handler. It runs with SA_INTERRUPT set. + */ + +static irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + /* + * Either an alarm interrupt or update complete interrupt. + * We store the status in the low byte and the number of + * interrupts received since the last read in the remainder + * of rtc_irq_data. + */ + + spin_lock (&rtc_lock); + + rtc_irq_data += 0x100; + rtc_irq_data &= ~0xff; + rtc_irq_data |= CMOS_READ(OMAP_RTC_STATUS_REG); + + if (rtc_irq_data & OMAP_RTC_STATUS_ALARM) + CMOS_WRITE(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG); + + spin_unlock (&rtc_lock); + + /* Now do the rest of the actions */ + wake_up_interruptible(&rtc_wait); + + kill_fasync (&rtc_async_queue, SIGIO, POLL_IN); + return IRQ_HANDLED; +} + +/* + * Now all the various file operations that we export. + */ + +static ssize_t rtc_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t retval; + + if (count < sizeof(unsigned long)) + return -EINVAL; + + add_wait_queue(&rtc_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + for (;;) { + spin_lock_irq (&rtc_lock); + data = rtc_irq_data; + if (data != 0) { + rtc_irq_data = 0; + break; + } + spin_unlock_irq (&rtc_lock); + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + goto out; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + goto out; + } + schedule(); + } + + spin_unlock_irq (&rtc_lock); + retval = put_user(data, (unsigned long __user *)buf); + if (!retval) + retval = sizeof(unsigned long); + out: + set_current_state(TASK_RUNNING); + remove_wait_queue(&rtc_wait, &wait); + + return retval; +} + +/* convert from userspace struct to hardware BCD-encoded version, + * or return error code + */ +static int utm2bcd(struct rtc_time __user *arg, struct rtc_time *tm) +{ + unsigned char leap_yr; + + if (copy_from_user(tm, arg, sizeof(struct rtc_time))) + return -EFAULT; + + tm->tm_year += 1900; + tm->tm_mon++; + + if (tm->tm_year < 1970) + return -EINVAL; + + leap_yr = (!(tm->tm_year % 4) && (tm->tm_year % 100)) + || !(tm->tm_year % 400); + + if ((tm->tm_mon > 12) || (tm->tm_mday == 0)) + return -EINVAL; + + if (tm->tm_mday > (days_in_mo[tm->tm_mon] + ((tm->tm_mon == 2) && leap_yr))) + return -EINVAL; + + if ((tm->tm_hour >= 24) || (tm->tm_min >= 60) || (tm->tm_sec >= 60)) + return -EINVAL; + + if ((tm->tm_year -= epoch) > 255) /* They are unsigned */ + return -EINVAL; + + if (tm->tm_year > 169) + return -EINVAL; + + if (tm->tm_year >= 100) + tm->tm_year -= 100; + + BIN_TO_BCD(tm->tm_sec); + BIN_TO_BCD(tm->tm_min); + BIN_TO_BCD(tm->tm_hour); + BIN_TO_BCD(tm->tm_mday); + BIN_TO_BCD(tm->tm_mon); + BIN_TO_BCD(tm->tm_year); + + return 0; +} + +static int rtc_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct rtc_time wtime; + int status = 0; + u8 save_control; + + switch (cmd) { + case RTC_AIE_OFF: /* Mask alarm int. enab. bit */ + mask_rtc_irq_bit(OMAP_RTC_INTERRUPTS_IT_ALARM); + break; + case RTC_AIE_ON: /* Allow alarm interrupts. */ + set_rtc_irq_bit(OMAP_RTC_INTERRUPTS_IT_ALARM); + break; + case RTC_UIE_OFF: /* Mask ints from RTC updates. */ + mask_rtc_irq_bit(OMAP_RTC_INTERRUPTS_IT_TIMER); + break; + case RTC_UIE_ON: /* Allow ints for RTC updates. */ + set_rtc_irq_bit(OMAP_RTC_INTERRUPTS_IT_TIMER); + break; + case RTC_ALM_READ: /* Read the present alarm time */ + /* + * This returns a struct rtc_time. Reading >= 0xc0 + * means "don't care" or "match all". Only the tm_hour, + * tm_min, and tm_sec values are filled in. + */ + memset(&wtime, 0, sizeof(struct rtc_time)); + get_rtc_alm_time(&wtime); + goto return_wtime; + case RTC_ALM_SET: /* Store a time into the alarm */ + status = utm2bcd((void __user *)arg, &wtime); + if (status != 0) + return status; + + spin_lock_irq(&rtc_lock); + CMOS_WRITE(wtime.tm_year, OMAP_RTC_ALARM_YEARS_REG); + CMOS_WRITE(wtime.tm_mon, OMAP_RTC_ALARM_MONTHS_REG); + CMOS_WRITE(wtime.tm_mday, OMAP_RTC_ALARM_DAYS_REG); + CMOS_WRITE(wtime.tm_hour, OMAP_RTC_ALARM_HOURS_REG); + CMOS_WRITE(wtime.tm_min, OMAP_RTC_ALARM_MINUTES_REG); + CMOS_WRITE(wtime.tm_sec, OMAP_RTC_ALARM_SECONDS_REG); + spin_unlock_irq(&rtc_lock); + + break; + case RTC_RD_TIME: /* Read the time/date from RTC */ + memset(&wtime, 0, sizeof(struct rtc_time)); + get_rtc_time(&wtime); + goto return_wtime; + case RTC_SET_TIME: /* Set the RTC */ + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + status = utm2bcd((void __user *)arg, &wtime); + if (status != 0) + return status; + + spin_lock_irq(&rtc_lock); + save_control = CMOS_READ(OMAP_RTC_CTRL_REG); + CMOS_WRITE((save_control & ~OMAP_RTC_CTRL_STOP), + OMAP_RTC_CTRL_REG); + CMOS_WRITE(wtime.tm_year, OMAP_RTC_YEARS_REG); + CMOS_WRITE(wtime.tm_mon, OMAP_RTC_MONTHS_REG); + CMOS_WRITE(wtime.tm_mday, OMAP_RTC_DAYS_REG); + CMOS_WRITE(wtime.tm_hour, OMAP_RTC_HOURS_REG); + CMOS_WRITE(wtime.tm_min, OMAP_RTC_MINUTES_REG); + CMOS_WRITE(wtime.tm_sec, OMAP_RTC_SECONDS_REG); + CMOS_WRITE((save_control | OMAP_RTC_CTRL_STOP), + OMAP_RTC_CTRL_REG); + spin_unlock_irq(&rtc_lock); + + break; + case RTC_EPOCH_READ: /* Read the epoch. */ + status = put_user (epoch, (unsigned long __user *)arg); + break; + case RTC_EPOCH_SET: /* Set the epoch. */ + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + /* + * There were no RTC clocks before 1900. + */ + if (arg < 1900) + status = -EINVAL; + else + epoch = arg; + break; + default: + status = -ENOTTY; + } + return status; + +return_wtime: + return copy_to_user((void __user *)arg, &wtime, sizeof wtime) + ? -EFAULT + : 0; +} + +/* + * We enforce only one user at a time here with the open/close. + * Also clear the previous interrupt data on an open, and clean + * up things on a close. + */ + +/* We use rtc_lock to protect against concurrent opens. So the BKL is not + * needed here. Or anywhere else in this driver. */ +static int rtc_open(struct inode *inode, struct file *file) +{ + spin_lock_irq (&rtc_lock); + + if (rtc_status & RTC_IS_OPEN) + goto out_busy; + + rtc_status |= RTC_IS_OPEN; + + rtc_irq_data = 0; + spin_unlock_irq (&rtc_lock); + return 0; + +out_busy: + spin_unlock_irq (&rtc_lock); + return -EBUSY; +} + +static int rtc_fasync(int fd, struct file *filp, int on) +{ + return fasync_helper (fd, filp, on, &rtc_async_queue); +} + +static int rtc_release(struct inode *inode, struct file *file) +{ + unsigned char tmp; + + /* + * Turn off all interrupts once the device is no longer + * in use, and clear the data. + */ + + spin_lock_irq(&rtc_lock); + tmp = CMOS_READ(OMAP_RTC_INTERRUPTS_REG); + tmp &= ~OMAP_RTC_INTERRUPTS_IT_ALARM; + tmp &= ~OMAP_RTC_INTERRUPTS_IT_TIMER; + CMOS_WRITE(tmp, OMAP_RTC_INTERRUPTS_REG); + spin_unlock_irq(&rtc_lock); + + if (file->f_flags & FASYNC) { + rtc_fasync (-1, file, 0); + } + + spin_lock_irq (&rtc_lock); + rtc_irq_data = 0; + spin_unlock_irq (&rtc_lock); + + /* No need for locking -- nobody else can do anything until this rmw + * is committed, and we don't implement timer support in omap-rtc. + */ + rtc_status &= ~RTC_IS_OPEN; + return 0; +} + +/* Called without the kernel lock - fine */ +static unsigned int rtc_poll(struct file *file, poll_table *wait) +{ + unsigned long l; + + poll_wait(file, &rtc_wait, wait); + + spin_lock_irq (&rtc_lock); + l = rtc_irq_data; + spin_unlock_irq (&rtc_lock); + + if (l != 0) + return POLLIN | POLLRDNORM; + return 0; +} + +/* + * The various file operations we support. + */ + +static struct file_operations rtc_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = rtc_read, + .poll = rtc_poll, + .ioctl = rtc_ioctl, + .open = rtc_open, + .release = rtc_release, + .fasync = rtc_fasync, +}; + +static struct miscdevice rtc_dev = { + .minor = RTC_MINOR, + .name = "rtc", + .fops = &rtc_fops, +}; + +static int __init omap_rtc_probe(struct platform_device *pdev) +{ + struct resource *res, *mem; + + /* find the IRQs */ + + omap_rtc_timer = platform_get_irq(pdev, 0); + if (omap_rtc_timer <= 0) { + dev_err(&pdev->dev, "no irq for rtc timer\n"); + return -ENOENT; + } + + omap_rtc_alarm = platform_get_irq(pdev, 1); + if (omap_rtc_alarm <= 0) { + dev_err(&pdev->dev, "no irq for alarm\n"); + return -ENOENT; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) + mem = request_mem_region(res->start, + res->end - res->start + 1, + pdev->name); + else + mem = NULL; + if (!mem) { + pr_debug("%s: RTC registers at %x are not free.\n", + pdev->name, OMAP_RTC_BASE); + return -EBUSY; + } + platform_set_drvdata(pdev, mem); + + if (CMOS_READ(OMAP_RTC_STATUS_REG) & OMAP_RTC_STATUS_POWER_UP) { + pr_info("%s: RTC power up reset detected.\n", + pdev->name); + /* Clear OMAP_RTC_STATUS_POWER_UP */ + CMOS_WRITE(OMAP_RTC_STATUS_POWER_UP, OMAP_RTC_STATUS_REG); + } + + if (CMOS_READ(OMAP_RTC_STATUS_REG) & OMAP_RTC_STATUS_ALARM) { + pr_debug("%s: Clearing RTC ALARM interrupt.\n", + pdev->name); + /* Clear OMAP_RTC_STATUS_ALARM */ + CMOS_WRITE(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG); + } + + if (request_irq(omap_rtc_timer, rtc_interrupt, SA_INTERRUPT, + pdev->name, NULL)) { + pr_debug("%s: RTC timer interrupt IRQ%d is not free.\n", + pdev->name, omap_rtc_timer); + goto fail; + } + + if (request_irq(omap_rtc_alarm, rtc_interrupt, SA_INTERRUPT, + pdev->name, NULL)) { + pr_debug("%s: RTC alarm interrupt IRQ%d is not free.\n", + pdev->name, omap_rtc_alarm); + free_irq(omap_rtc_timer, NULL); + goto fail; + } + + /* On boards with split power, RTC_ON_NOFF resets all but the RTC */ + if (!(CMOS_READ(OMAP_RTC_CTRL_REG) & OMAP_RTC_CTRL_STOP)) { + pr_info("%s: Enabling RTC.\n", pdev->name); + CMOS_WRITE(OMAP_RTC_CTRL_STOP, OMAP_RTC_CTRL_REG); + } else + pr_info("%s: RTC already running.\n", pdev->name); + + spin_lock_init(&rtc_lock); + misc_register(&rtc_dev); + create_proc_read_entry("driver/rtc", 0, NULL, rtc_read_proc, NULL); + + return 0; + +fail: + release_resource(mem); + return -EIO; +} + +static int omap_rtc_remove(struct platform_device *pdev) +{ + free_irq (omap_rtc_timer, NULL); + free_irq (omap_rtc_alarm, NULL); + + remove_proc_entry ("driver/rtc", NULL); + misc_deregister(&rtc_dev); + + release_resource(platform_get_drvdata(pdev)); + return 0; +} + +/* + * Info exported via "/proc/driver/rtc". + */ + +static int rtc_proc_output (char *buf) +{ +#define YN(value) ((value) ? "yes" : "no") + char *p; + struct rtc_time tm; + + p = buf; + + get_rtc_time(&tm); + + /* + * There is no way to tell if the luser has the RTC set for local + * time or for Universal Standard Time (GMT). Probably local though. + */ + p += sprintf(p, + "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04lu\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, epoch); + + get_rtc_alm_time(&tm); + + /* + * We implicitly assume 24hr mode here. Alarm values >= 0xc0 will + * match any value for that particular field. Values that are + * greater than a valid time, but less than 0xc0 shouldn't appear. + */ + p += sprintf(p, + "alarm_time\t: %02d:%02d:%02d\n" + "alarm_date\t: %04d-%02d-%02d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday); + + p += sprintf(p, + "BCD\t\t: %s\n" + "24hr\t\t: %s\n" + "alarm_IRQ\t: %s\n" + "update_IRQ\t: %s\n" + "update_rate\t: %ud\n", + YN(1), + YN(1), + YN(CMOS_READ(OMAP_RTC_INTERRUPTS_REG) & + OMAP_RTC_INTERRUPTS_IT_ALARM), + YN(CMOS_READ(OMAP_RTC_INTERRUPTS_REG) & + OMAP_RTC_INTERRUPTS_IT_TIMER), + CMOS_READ(OMAP_RTC_INTERRUPTS_REG) & 3 /* REVISIT */); + + return p - buf; +#undef YN +} + +static int rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = rtc_proc_output (page); + + if (len <= off+count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + return len; +} + +/* + * Returns true if a clock update is in progress + */ +static inline unsigned char rtc_is_updating(void) +{ + unsigned char uip; + + spin_lock_irq(&rtc_lock); + uip = (CMOS_READ(OMAP_RTC_STATUS_REG) & OMAP_RTC_STATUS_BUSY); + spin_unlock_irq(&rtc_lock); + return uip; +} + +static void bcd2tm(struct rtc_time *tm) +{ + BCD_TO_BIN(tm->tm_sec); + BCD_TO_BIN(tm->tm_min); + BCD_TO_BIN(tm->tm_hour); + BCD_TO_BIN(tm->tm_mday); + BCD_TO_BIN(tm->tm_mon); + BCD_TO_BIN(tm->tm_year); + + /* + * Account for differences between how the RTC uses the values + * and how they are defined in a struct rtc_time; + */ + if ((tm->tm_year += (epoch - 1900)) <= 69) + tm->tm_year += 100; + + tm->tm_mon--; +} + + +static void get_rtc_time(struct rtc_time *rtc_tm) +{ + unsigned char ctrl; + + /* REVISIT: Fix this comment!!! + * read RTC once any update in progress is done. The update + * can take just over 2ms. We wait 10 to 20ms. There is no need to + * to poll-wait (up to 1s - eeccch) for the falling edge of OMAP_RTC_STATUS_BUSY. + * If you need to know *exactly* when a second has started, enable + * periodic update complete interrupts, (via ioctl) and then + * immediately read /dev/rtc which will block until you get the IRQ. + * Once the read clears, read the RTC time (again via ioctl). Easy. + */ + +#if 0 /* REVISIT: This need to do as the TRM says. */ + unsigned long uip_watchdog = jiffies; + if (rtc_is_updating() != 0) + while (jiffies - uip_watchdog < 2*HZ/100) { + barrier(); + cpu_relax(); + } +#endif + + /* + * Only the values that we read from the RTC are set. We leave + * tm_wday, tm_yday and tm_isdst untouched. Even though the + * RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated + * by the RTC when initially set to a non-zero value. + */ + spin_lock_irq(&rtc_lock); + rtc_tm->tm_sec = CMOS_READ(OMAP_RTC_SECONDS_REG); + rtc_tm->tm_min = CMOS_READ(OMAP_RTC_MINUTES_REG); + rtc_tm->tm_hour = CMOS_READ(OMAP_RTC_HOURS_REG); + rtc_tm->tm_mday = CMOS_READ(OMAP_RTC_DAYS_REG); + rtc_tm->tm_mon = CMOS_READ(OMAP_RTC_MONTHS_REG); + rtc_tm->tm_year = CMOS_READ(OMAP_RTC_YEARS_REG); + ctrl = CMOS_READ(OMAP_RTC_CTRL_REG); + spin_unlock_irq(&rtc_lock); + + bcd2tm(rtc_tm); +} + +static void get_rtc_alm_time(struct rtc_time *alm_tm) +{ + unsigned char ctrl; + + spin_lock_irq(&rtc_lock); + alm_tm->tm_sec = CMOS_READ(OMAP_RTC_ALARM_SECONDS_REG); + alm_tm->tm_min = CMOS_READ(OMAP_RTC_ALARM_MINUTES_REG); + alm_tm->tm_hour = CMOS_READ(OMAP_RTC_ALARM_HOURS_REG); + alm_tm->tm_mday = CMOS_READ(OMAP_RTC_ALARM_DAYS_REG); + alm_tm->tm_mon = CMOS_READ(OMAP_RTC_ALARM_MONTHS_REG); + alm_tm->tm_year = CMOS_READ(OMAP_RTC_ALARM_YEARS_REG); + ctrl = CMOS_READ(OMAP_RTC_CTRL_REG); + spin_unlock_irq(&rtc_lock); + + bcd2tm(alm_tm); +} + +/* + * Used to disable/enable UIE and AIE interrupts. + */ + +static void mask_rtc_irq_bit(unsigned char bit) +{ + unsigned char val; + + spin_lock_irq(&rtc_lock); + val = CMOS_READ(OMAP_RTC_INTERRUPTS_REG); + val &= ~bit; + CMOS_WRITE(val, OMAP_RTC_INTERRUPTS_REG); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); +} + +static void set_rtc_irq_bit(unsigned char bit) +{ + unsigned char val; + + spin_lock_irq(&rtc_lock); + val = CMOS_READ(OMAP_RTC_INTERRUPTS_REG); + val |= bit; + CMOS_WRITE(val, OMAP_RTC_INTERRUPTS_REG); + rtc_irq_data = 0; + spin_unlock_irq(&rtc_lock); +} + +#ifdef CONFIG_PM +static struct timespec rtc_delta; + +static int omap_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct rtc_time rtc_tm; + struct timespec time; + + time.tv_nsec = 0; + get_rtc_time(&rtc_tm); + rtc_tm_to_time(&rtc_tm, &time.tv_sec); + + save_time_delta(&rtc_delta, &time); + + return 0; +} + +static int omap_rtc_resume(struct platform_device *pdev) +{ + struct rtc_time rtc_tm; + struct timespec time; + + time.tv_nsec = 0; + get_rtc_time(&rtc_tm); + rtc_tm_to_time(&rtc_tm, &time.tv_sec); + + restore_time_delta(&rtc_delta, &time); + + return 0; +} +#else +#define omap_rtc_suspend NULL +#define omap_rtc_resume NULL +#endif + +static struct platform_driver omap_rtc_driver = { + .probe = omap_rtc_probe, + .remove = omap_rtc_remove, + .suspend = omap_rtc_suspend, + .resume = omap_rtc_resume, + .driver = { + .name = "omap_rtc", + .owner = THIS_MODULE, + }, +}; + +static char __initdata banner[] = KERN_INFO "OMAP RTC Driver\n"; + +static int __init rtc_init(void) +{ + printk(banner); + return platform_driver_register(&omap_rtc_driver); +} + +static void __exit rtc_exit(void) +{ + platform_driver_unregister(&omap_rtc_driver); +} + +module_init(rtc_init); +module_exit(rtc_exit); + +MODULE_AUTHOR("George G. Davis (and others)"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(RTC_MINOR); diff --git a/drivers/char/omap-rtc.h b/drivers/char/omap-rtc.h new file mode 100644 index 00000000000..d8acb56cd59 --- /dev/null +++ b/drivers/char/omap-rtc.h @@ -0,0 +1,59 @@ +/* + * TI OMAP Real Time Clock header file + * + * Copyright (C) 2003 TI + * + * 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. + * + */ + +#define OMAP_RTC_BASE 0xfffb4800 +#define OMAP_RTC_SIZE 128 + +#define OMAP_RTC_VIRT_BASE IO_ADDRESS(OMAP_RTC_BASE) + +/* + * Real-Time Clock + */ + +#define OMAP_RTC_SECONDS_REG (OMAP_RTC_BASE + 0x00) +#define OMAP_RTC_MINUTES_REG (OMAP_RTC_BASE + 0x04) +#define OMAP_RTC_HOURS_REG (OMAP_RTC_BASE + 0x08) +#define OMAP_RTC_DAYS_REG (OMAP_RTC_BASE + 0x0C) +#define OMAP_RTC_MONTHS_REG (OMAP_RTC_BASE + 0x10) +#define OMAP_RTC_YEARS_REG (OMAP_RTC_BASE + 0x14) +#define OMAP_RTC_WEEKS_REG (OMAP_RTC_BASE + 0x18) +#define OMAP_RTC_ALARM_SECONDS_REG (OMAP_RTC_BASE + 0x20) +#define OMAP_RTC_ALARM_MINUTES_REG (OMAP_RTC_BASE + 0x24) +#define OMAP_RTC_ALARM_HOURS_REG (OMAP_RTC_BASE + 0x28) +#define OMAP_RTC_ALARM_DAYS_REG (OMAP_RTC_BASE + 0x2c) +#define OMAP_RTC_ALARM_MONTHS_REG (OMAP_RTC_BASE + 0x30) +#define OMAP_RTC_ALARM_YEARS_REG (OMAP_RTC_BASE + 0x34) +#define OMAP_RTC_CTRL_REG (OMAP_RTC_BASE + 0x40) +#define OMAP_RTC_STATUS_REG (OMAP_RTC_BASE + 0x44) +#define OMAP_RTC_INTERRUPTS_REG (OMAP_RTC_BASE + 0x48) +#define OMAP_RTC_COMP_LSB_REG (OMAP_RTC_BASE + 0x4c) +#define OMAP_RTC_COMP_MSB_REG (OMAP_RTC_BASE + 0x50) + +/* RTC Control Register bit fields: */ + +#define OMAP_RTC_CTRL_STOP (1<<0) + +/* RTC Status Register bit fields: */ + +#define OMAP_RTC_STATUS_POWER_UP (1<<7) +#define OMAP_RTC_STATUS_ALARM (1<<6) +#define OMAP_RTC_STATUS_1D_EVENT (1<<5) +#define OMAP_RTC_STATUS_1H_EVENT (1<<4) +#define OMAP_RTC_STATUS_1M_EVENT (1<<3) +#define OMAP_RTC_STATUS_1S_EVENT (1<<2) +#define OMAP_RTC_STATUS_RUN (1<<1) +#define OMAP_RTC_STATUS_BUSY (1<<0) + +/* RTC Interrupt Register bit fields: */ + +#define OMAP_RTC_INTERRUPTS_IT_ALARM (1<<3) +#define OMAP_RTC_INTERRUPTS_IT_TIMER (1<<2) diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig index c0dfcf273f0..3b6ce7f45fd 100644 --- a/drivers/char/watchdog/Kconfig +++ b/drivers/char/watchdog/Kconfig @@ -649,4 +649,11 @@ config USBPCWATCHDOG Most people will say N. +config OMAP_WATCHDOG + tristate "OMAP Watchdog" + depends on WATCHDOG && (ARCH_OMAP16XX || ARCH_OMAP24XX) + help + Support for TI OMAP1610/OMAP1710/OMAP2420 watchdog. Say 'Y' here to enable the + OMAP1610/OMAP1710 watchdog timer. + endmenu diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile index 36c0b282b8b..5aba6d72a25 100644 --- a/drivers/char/watchdog/Makefile +++ b/drivers/char/watchdog/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_21285_WATCHDOG) += wdt285.o obj-$(CONFIG_977_WATCHDOG) += wdt977.o obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o +obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o diff --git a/drivers/char/watchdog/omap_wdt.c b/drivers/char/watchdog/omap_wdt.c new file mode 100644 index 00000000000..3580de5b633 --- /dev/null +++ b/drivers/char/watchdog/omap_wdt.c @@ -0,0 +1,384 @@ +/* + * linux/drivers/char/omap_wdt.c + * + * Watchdog driver for the TI OMAP 16xx & 24xx 32KHz (non-secure) watchdog + * + * Author: MontaVista Software, Inc. + * or + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under the + * terms of the GNU General Public License version 2. This program is + * licensed "as is" without any warranty of any kind, whether express + * or implied. + * + * History: + * + * 20030527: George G. Davis + * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c + * (c) Copyright 2000 Oleg Drokin + * Based on SoftDog driver by Alan Cox + * + * Copyright (c) 2004 Texas Instruments. + * 1. Modified to support OMAP1610 32-KHz watchdog timer + * 2. Ported to 2.6 kernel + * + * Copyright (c) 2005 David Brownell + * Use the driver model and standard identifiers; handle bigger timeouts. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include "omap_wdt.h" + +static unsigned timer_margin; +module_param(timer_margin, uint, 0); +MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)"); + +static int omap_wdt_users; +static struct clk *armwdt_ck = NULL; +static struct clk *mpu_wdt_ick = NULL; +static struct clk *mpu_wdt_fck = NULL; + +static unsigned int wdt_trgr_pattern = 0x1234; + +static void omap_wdt_ping(void) +{ + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) ; /* wait for posted write to complete */ + wdt_trgr_pattern = ~wdt_trgr_pattern; + omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR)); + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08) ; /* wait for posted write to complete */ + /* reloaded WCRR from WLDR */ +} + +static void omap_wdt_enable(void) +{ + /* Sequence to enable the watchdog */ + omap_writel(0xBBBB, OMAP_WATCHDOG_SPR); + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) ; + omap_writel(0x4444, OMAP_WATCHDOG_SPR); + while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10) ; +} + +static void omap_wdt_disable(void) +{ + /* sequence required to disable watchdog */ + omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) ; + omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10) ; +} + +static void omap_wdt_adjust_timeout(unsigned new_timeout) +{ + if (new_timeout < TIMER_MARGIN_MIN) + new_timeout = TIMER_MARGIN_DEFAULT; + if (new_timeout > TIMER_MARGIN_MAX) + new_timeout = TIMER_MARGIN_MAX; + timer_margin = new_timeout; +} + +static void omap_wdt_set_timeout(void) +{ + u32 pre_margin = GET_WLDR_VAL(timer_margin); + + /* just count up at 32 KHz */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) + continue; + omap_writel(pre_margin, OMAP_WATCHDOG_LDR); + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04) + continue; +} + +/* + * Allow only one task to hold it open + */ + +static int omap_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users)) + return -EBUSY; + + if (cpu_is_omap16xx()) { + clk_enable(armwdt_ck); /* Enable the clock */ + } + + if (cpu_is_omap24xx()) { + clk_enable(mpu_wdt_ick); /* Enable the interface clock */ + clk_enable(mpu_wdt_fck); /* Enable the functional clock */ + } + + /* initialize prescaler */ + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) + continue; + omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL); + while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01) + continue; + + omap_wdt_set_timeout(); + omap_wdt_enable(); + return 0; +} + +static int omap_wdt_release(struct inode *inode, struct file *file) +{ + /* + * Shut off the timer unless NOWAYOUT is defined. + */ +#ifndef CONFIG_WATCHDOG_NOWAYOUT + omap_wdt_disable(); + + if (cpu_is_omap16xx()) { + clk_disable(armwdt_ck); /* Disable the clock */ + clk_put(armwdt_ck); + armwdt_ck = NULL; + } + + if (cpu_is_omap24xx()) { + clk_disable(mpu_wdt_ick); /* Disable the clock */ + clk_disable(mpu_wdt_fck); /* Disable the clock */ + clk_put(mpu_wdt_ick); + clk_put(mpu_wdt_fck); + mpu_wdt_ick = NULL; + mpu_wdt_fck = NULL; + } +#else + printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n"); +#endif + omap_wdt_users = 0; + return 0; +} + +static ssize_t +omap_wdt_write(struct file *file, const char __user * data, + size_t len, loff_t * ppos) +{ + /* Refresh LOAD_TIME. */ + if (len) + omap_wdt_ping(); + return len; +} + +static int +omap_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int new_margin; + static struct watchdog_info ident = { + .identity = "OMAP Watchdog", + .options = WDIOF_SETTIMEOUT, + .firmware_version = 0, + }; + + switch (cmd) { + default: + return -ENOIOCTLCMD; + case WDIOC_GETSUPPORT: + return copy_to_user((struct watchdog_info __user *)arg, &ident, + sizeof(ident)); + case WDIOC_GETSTATUS: + return put_user(0, (int __user *)arg); + case WDIOC_GETBOOTSTATUS: + if (cpu_is_omap16xx()) + return put_user(omap_readw(ARM_SYSST), + (int __user *)arg); + if (cpu_is_omap24xx()) + return put_user(omap_prcm_get_reset_sources(), + (int __user *)arg); + case WDIOC_KEEPALIVE: + omap_wdt_ping(); + return 0; + case WDIOC_SETTIMEOUT: + if (get_user(new_margin, (int __user *)arg)) + return -EFAULT; + omap_wdt_adjust_timeout(new_margin); + + omap_wdt_disable(); + omap_wdt_set_timeout(); + omap_wdt_enable(); + + omap_wdt_ping(); + /* Fall */ + case WDIOC_GETTIMEOUT: + return put_user(timer_margin, (int __user *)arg); + } +} + +static struct file_operations omap_wdt_fops = { + .owner = THIS_MODULE, + .write = omap_wdt_write, + .ioctl = omap_wdt_ioctl, + .open = omap_wdt_open, + .release = omap_wdt_release, +}; + +static struct miscdevice omap_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &omap_wdt_fops +}; + +static int __init omap_wdt_probe(struct platform_device *pdev) +{ + struct resource *res, *mem; + int ret; + + /* reserve static register mappings */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENOENT; + + mem = request_mem_region(res->start, res->end - res->start + 1, + pdev->name); + if (mem == NULL) + return -EBUSY; + + platform_set_drvdata(pdev, mem); + + omap_wdt_users = 0; + + if (cpu_is_omap16xx()) { + armwdt_ck = clk_get(&pdev->dev, "armwdt_ck"); + if (IS_ERR(armwdt_ck)) { + ret = PTR_ERR(armwdt_ck); + armwdt_ck = NULL; + goto fail; + } + } + + if (cpu_is_omap24xx()) { + mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick"); + if (IS_ERR(mpu_wdt_ick)) { + ret = PTR_ERR(mpu_wdt_ick); + mpu_wdt_ick = NULL; + goto fail; + } + mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck"); + if (IS_ERR(mpu_wdt_fck)) { + ret = PTR_ERR(mpu_wdt_fck); + mpu_wdt_fck = NULL; + goto fail; + } + } + + omap_wdt_disable(); + omap_wdt_adjust_timeout(timer_margin); + + omap_wdt_miscdev.dev = &pdev->dev; + ret = misc_register(&omap_wdt_miscdev); + if (ret) + goto fail; + + pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin); + + /* autogate OCP interface clock */ + omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG); + return 0; + +fail: + if (armwdt_ck) + clk_put(armwdt_ck); + if (mpu_wdt_ick) + clk_put(mpu_wdt_ick); + if (mpu_wdt_fck) + clk_put(mpu_wdt_fck); + release_resource(mem); + return ret; +} + +static void omap_wdt_shutdown(struct platform_device *pdev) +{ + omap_wdt_disable(); +} + +static int omap_wdt_remove(struct platform_device *pdev) +{ + struct resource *mem = platform_get_drvdata(pdev); + misc_deregister(&omap_wdt_miscdev); + release_resource(mem); + if (armwdt_ck) + clk_put(armwdt_ck); + if (mpu_wdt_ick) + clk_put(mpu_wdt_ick); + if (mpu_wdt_fck) + clk_put(mpu_wdt_fck); + return 0; +} + +#ifdef CONFIG_PM + +/* REVISIT ... not clear this is the best way to handle system suspend; and + * it's very inappropriate for selective device suspend (e.g. suspending this + * through sysfs rather than by stopping the watchdog daemon). Also, this + * may not play well enough with NOWAYOUT... + */ + +static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state) +{ + if (omap_wdt_users) + omap_wdt_disable(); + return 0; +} + +static int omap_wdt_resume(struct platform_device *pdev) +{ + if (omap_wdt_users) { + omap_wdt_enable(); + omap_wdt_ping(); + } + return 0; +} + +#else +#define omap_wdt_suspend NULL +#define omap_wdt_resume NULL +#endif + +static struct platform_driver omap_wdt_driver = { + .probe = omap_wdt_probe, + .remove = omap_wdt_remove, + .shutdown = omap_wdt_shutdown, + .suspend = omap_wdt_suspend, + .resume = omap_wdt_resume, + .driver = { + .owner = THIS_MODULE, + .name = "omap_wdt", + }, +}; + +static int __init omap_wdt_init(void) +{ + return platform_driver_register(&omap_wdt_driver); +} + +static void __exit omap_wdt_exit(void) +{ + platform_driver_unregister(&omap_wdt_driver); +} + +module_init(omap_wdt_init); +module_exit(omap_wdt_exit); + +MODULE_AUTHOR("George G. Davis"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); diff --git a/drivers/char/watchdog/omap_wdt.h b/drivers/char/watchdog/omap_wdt.h new file mode 100644 index 00000000000..52a532a5114 --- /dev/null +++ b/drivers/char/watchdog/omap_wdt.h @@ -0,0 +1,64 @@ +/* + * linux/drivers/char/watchdog/omap_wdt.h + * + * BRIEF MODULE DESCRIPTION + * OMAP Watchdog timer register definitions + * + * Copyright (C) 2004 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; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _OMAP_WATCHDOG_H +#define _OMAP_WATCHDOG_H + +#define OMAP1610_WATCHDOG_BASE 0xfffeb000 +#define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */ + +#ifdef CONFIG_ARCH_OMAP24XX +#define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE +#else +#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE +#define RM_RSTST_WKUP 0 +#endif + +#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00) +#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10) +#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14) +#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24) +#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28) +#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c) +#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30) +#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34) +#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48) + +/* Using the prescaler, the OMAP watchdog could go for many + * months before firing. These limits work without scaling, + * with the 60 second default assumed by most tools and docs. + */ +#define TIMER_MARGIN_MAX (24 * 60 * 60) /* 1 day */ +#define TIMER_MARGIN_DEFAULT 60 /* 60 secs */ +#define TIMER_MARGIN_MIN 1 + +#define PTV 0 /* prescale */ +#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1< and Imre Deak + * Copyright (C) 2005 Nokia Corporation + * + * Cleaned up by Juha Yrjölä + * + * ---------------------------------------------------------------------------- + * This file was highly leveraged from i2c-elektor.c: + * + * Copyright 1995-97 Simon G. Vogl + * 1998-99 Hans Berglund + * + * With some changes from Kyösti Mälkki and even + * Frodo Looijaard + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +// #define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* ----- global defines ----------------------------------------------- */ +static const char driver_name[] = "i2c_omap"; + +#define MODULE_NAME "OMAP I2C" +#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000)) /* timeout waiting for the controller to respond */ + +#define DEFAULT_OWN 1 /* default own I2C address */ +#define MAX_MESSAGES 65536 /* max number of messages */ + +#define OMAP_I2C_REV_REG 0x00 +#define OMAP_I2C_IE_REG 0x04 +#define OMAP_I2C_STAT_REG 0x08 +#define OMAP_I2C_IV_REG 0x0c +#define OMAP_I2C_SYSS_REG 0x10 +#define OMAP_I2C_BUF_REG 0x14 +#define OMAP_I2C_CNT_REG 0x18 +#define OMAP_I2C_DATA_REG 0x1c +#define OMAP_I2C_SYSC_REG 0x20 +#define OMAP_I2C_CON_REG 0x24 +#define OMAP_I2C_OA_REG 0x28 +#define OMAP_I2C_SA_REG 0x2c +#define OMAP_I2C_PSC_REG 0x30 +#define OMAP_I2C_SCLL_REG 0x34 +#define OMAP_I2C_SCLH_REG 0x38 +#define OMAP_I2C_SYSTEST_REG 0x3c + +/* I2C Interrupt Enable Register (OMAP_I2C_IE): */ +#define OMAP_I2C_IE_XRDY (1 << 4) /* TX data ready int enable */ +#define OMAP_I2C_IE_RRDY (1 << 3) /* RX data ready int enable */ +#define OMAP_I2C_IE_ARDY (1 << 2) /* Access ready int enable */ +#define OMAP_I2C_IE_NACK (1 << 1) /* No ack interrupt enable */ +#define OMAP_I2C_IE_AL (1 << 0) /* Arbitration lost int ena */ + +/* I2C Status Register (OMAP_I2C_STAT): */ +#define OMAP_I2C_STAT_SBD (1 << 15) /* Single byte data */ +#define OMAP_I2C_STAT_BB (1 << 12) /* Bus busy */ +#define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */ +#define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */ +#define OMAP_I2C_STAT_AAS (1 << 9) /* Address as slave */ +#define OMAP_I2C_STAT_AD0 (1 << 8) /* Address zero */ +#define OMAP_I2C_STAT_XRDY (1 << 4) /* Transmit data ready */ +#define OMAP_I2C_STAT_RRDY (1 << 3) /* Receive data ready */ +#define OMAP_I2C_STAT_ARDY (1 << 2) /* Register access ready */ +#define OMAP_I2C_STAT_NACK (1 << 1) /* No ack interrupt enable */ +#define OMAP_I2C_STAT_AL (1 << 0) /* Arbitration lost int ena */ + +/* I2C Buffer Configuration Register (OMAP_I2C_BUF): */ +#define OMAP_I2C_BUF_RDMA_EN (1 << 15) /* RX DMA channel enable */ +#define OMAP_I2C_BUF_XDMA_EN (1 << 7) /* TX DMA channel enable */ + +/* I2C Configuration Register (OMAP_I2C_CON): */ +#define OMAP_I2C_CON_EN (1 << 15) /* I2C module enable */ +#define OMAP_I2C_CON_BE (1 << 14) /* Big endian mode */ +#define OMAP_I2C_CON_STB (1 << 11) /* Start byte mode (master) */ +#define OMAP_I2C_CON_MST (1 << 10) /* Master/slave mode */ +#define OMAP_I2C_CON_TRX (1 << 9) /* TX/RX mode (master only) */ +#define OMAP_I2C_CON_XA (1 << 8) /* Expand address */ +#define OMAP_I2C_CON_RM (1 << 2) /* Repeat mode (master only) */ +#define OMAP_I2C_CON_STP (1 << 1) /* Stop cond (master only) */ +#define OMAP_I2C_CON_STT (1 << 0) /* Start condition (master) */ + +/* I2C System Test Register (OMAP_I2C_SYSTEST): */ +#define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */ +#define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */ +#define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */ +#define OMAP_I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */ +#define OMAP_I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense in */ +#define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */ +#define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */ +#define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */ + +/* I2C System Status register (OMAP_I2C_SYSS): */ +#define OMAP_I2C_SYSS_RDONE 1 /* Reset Done */ + +/* I2C System Configuration Register (OMAP_I2C_SYSC): */ +#define OMAP_I2C_SYSC_SRST (1 << 1) /* Soft Reset */ + +/* REVISIT: Use platform_data instead of module parameters */ +static int clock = 100; /* Default: Fast Mode = 400 KHz, Standard = 100 KHz */ +module_param(clock, int, 0); +MODULE_PARM_DESC(clock, "Set I2C clock in kHz: 100 or 400 (Fast Mode)"); + +static int own; +module_param(own, int, 0); +MODULE_PARM_DESC(own, "Address of OMAP I2C master (0 for default == 1)"); + +struct omap_i2c_dev { + struct device *dev; + void __iomem *base; /* virtual */ + int irq; + struct clk *iclk; /* Interface clock */ + struct clk *fclk; /* Functional clock */ + struct completion cmd_complete; + u16 cmd_err; + u8 *buf; + size_t buf_len; + struct i2c_adapter adapter; + unsigned rev1:1; + u8 own_address; +}; + +static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev, + int reg, u16 val) +{ + __raw_writew(val, i2c_dev->base + reg); +} + +static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg) +{ + return __raw_readw(i2c_dev->base + reg); +} + +static int omap_i2c_get_clocks(struct omap_i2c_dev *dev) +{ + if (cpu_is_omap24xx()) { + dev->iclk = clk_get(dev->dev, "i2c_ick"); + if (IS_ERR(dev->iclk)) { + return -ENODEV; + } + dev->fclk = clk_get(dev->dev, "i2c_fck"); + if (IS_ERR(dev->fclk)) { + clk_put(dev->fclk); + return -ENODEV; + } + } + + if (cpu_class_is_omap1()) { + dev->fclk = clk_get(dev->dev, "i2c_fck"); + if (IS_ERR(dev->fclk)) + return -ENODEV; + } + + return 0; +} + +static void omap_i2c_put_clocks(struct omap_i2c_dev *dev) +{ + clk_put(dev->fclk); + dev->fclk = NULL; + if (dev->iclk != NULL) { + clk_put(dev->iclk); + dev->iclk = NULL; + } +} + +static void omap_i2c_enable_clocks(struct omap_i2c_dev *dev) +{ + if (dev->iclk != NULL) + clk_enable(dev->iclk); + clk_enable(dev->fclk); +} + +static void omap_i2c_disable_clocks(struct omap_i2c_dev *dev) +{ + if (dev->iclk != NULL) + clk_disable(dev->iclk); + clk_disable(dev->fclk); +} + +static void omap_i2c_reset(struct omap_i2c_dev *dev) +{ + u16 psc; + unsigned long fclk_rate; + + if (!dev->rev1) { + omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, OMAP_I2C_SYSC_SRST); + /* For some reason we need to set the EN bit before the + * reset done bit gets set. */ + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); + while (!(omap_i2c_read_reg(dev, OMAP_I2C_SYSS_REG) & 0x01)); + } + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); + + if (cpu_class_is_omap1()) { + struct clk *armxor_ck; + unsigned long armxor_rate; + + armxor_ck = clk_get(NULL, "armxor_ck"); + if (IS_ERR(armxor_ck)) { + printk(KERN_WARNING "i2c: Could not get armxor_ck\n"); + armxor_rate = 12000000; + } else { + armxor_rate = clk_get_rate(armxor_ck); + clk_put(armxor_ck); + } + + if (armxor_rate > 16000000) + psc = (armxor_rate + 8000000) / 12000000; + else + psc = 0; + + fclk_rate = armxor_rate; + } else if (cpu_class_is_omap2()) { + fclk_rate = 12000000; + psc = 0; + } + + /* Setup clock prescaler to obtain approx 12MHz I2C module clock: */ + omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, psc); + + /* Program desired operating rate */ + fclk_rate /= (psc + 1) * 1000; + if (psc > 2) + psc = 2; + + omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG, + fclk_rate / (clock * 2) - 7 + psc); + omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG, + fclk_rate / (clock * 2) - 7 + psc); + + /* Set Own Address: */ + omap_i2c_write_reg(dev, OMAP_I2C_OA_REG, dev->own_address); + + /* Take the I2C module out of reset: */ + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN); + + /* Enable interrupts */ + omap_i2c_write_reg(dev, OMAP_I2C_IE_REG, + (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY | + OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK | + OMAP_I2C_IE_AL)); +} + +/* + * Waiting on Bus Busy + */ +static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev) +{ + unsigned long timeout; + + timeout = jiffies + OMAP_I2C_TIMEOUT; + while (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) { + if (time_after(jiffies, timeout)) { + dev_warn(dev->dev, "timeout waiting for bus ready\n"); + return -ETIMEDOUT; + } + msleep(1); + } + + return 0; +} + +/* + * Low level master read/write transaction. + */ +static int omap_i2c_xfer_msg(struct i2c_adapter *adap, + struct i2c_msg *msg, int stop) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + int r; + u16 w; + u8 zero_byte = 0; + + dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n", + msg->addr, msg->len, msg->flags, stop); + + omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr); + + /* Sigh, seems we can't do zero length transactions. Thus, we + * can't probe for devices w/o actually sending/receiving at least + * a single byte. So we'll set count to 1 for the zero length + * transaction case and hope we don't cause grief for some + * arbitrary device due to random byte write/read during + * probes. + */ + /* REVISIT: Could the STB bit of I2C_CON be used with probing? */ + if (msg->len == 0) { + dev->buf = &zero_byte; + dev->buf_len = 1; + } else { + dev->buf = msg->buf; + dev->buf_len = msg->len; + } + omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len); + + init_completion(&dev->cmd_complete); + dev->cmd_err = 0; + + w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT; + if (msg->flags & I2C_M_TEN) + w |= OMAP_I2C_CON_XA; + if (!(msg->flags & I2C_M_RD)) + w |= OMAP_I2C_CON_TRX; + if (stop) + w |= OMAP_I2C_CON_STP; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); + + r = wait_for_completion_interruptible_timeout(&dev->cmd_complete, + OMAP_I2C_TIMEOUT); + dev->buf_len = 0; + if (r < 0) + return r; + if (r == 0) { + dev_err(dev->dev, "controller timed out\n"); + omap_i2c_reset(dev); + return -ETIMEDOUT; + } + + if (likely(!dev->cmd_err)) + return 0; + + /* We have an error */ + if (dev->cmd_err & OMAP_I2C_STAT_NACK) { + if (msg->flags & I2C_M_IGNORE_NAK) + return 0; + if (stop) { + u16 w; + + w = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG); + w |= OMAP_I2C_CON_STP; + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w); + } + return -EREMOTEIO; + } + if (dev->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR | + OMAP_I2C_STAT_XUDF)) + omap_i2c_reset(dev); + return -EIO; +} + + +/* + * Prepare controller for a transaction and call omap_i2c_xfer_msg + * to do the work during IRQ processing. + */ +static int +omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct omap_i2c_dev *dev = i2c_get_adapdata(adap); + int i; + int r = 0; + + if (num < 1 || num > MAX_MESSAGES) + return -EINVAL; + + /* Check for valid parameters in messages */ + for (i = 0; i < num; i++) + if (msgs[i].buf == NULL) + return -EINVAL; + + omap_i2c_enable_clocks(dev); + + /* REVISIT: initialize and use adap->retries */ + if ((r = omap_i2c_wait_for_bb(dev)) < 0) + goto out; + + for (i = 0; i < num; i++) { + dev_dbg(dev->dev, "msg: %d, addr: 0x%04x, len: %d, flags: 0x%x\n", + i, msgs[i].addr, msgs[i].len, msgs[i].flags); + r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1))); + if (r != 0) + break; + } + + if (r == 0) + r = num; +out: + omap_i2c_disable_clocks(dev); + return r; +} + +static u32 +omap_i2c_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +static inline void +omap_i2c_complete_cmd(struct omap_i2c_dev *dev, u16 err) +{ + dev->cmd_err |= err; + complete(&dev->cmd_complete); +} + +static inline void +omap_i2c_ack_stat(struct omap_i2c_dev *dev, u16 stat) +{ + omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat); +} + +#ifdef CONFIG_ARCH_OMAP15XX +static irqreturn_t +omap_i2c_rev1_isr(int this_irq, void *dev_id, struct pt_regs *regs) +{ + struct omap_i2c_dev *dev = dev_id; + u16 iv, w; + + iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG); + switch (iv) { + case 0x00: /* None */ + break; + case 0x01: /* Arbitration lost */ + dev_err(dev->dev, "Arbitration lost\n"); + omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_AL); + break; + case 0x02: /* No acknowledgement */ + omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_NACK); + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_STP); + break; + case 0x03: /* Register access ready */ + omap_i2c_complete_cmd(dev, 0); + break; + case 0x04: /* Receive data ready */ + if (dev->buf_len) { + w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG); + *dev->buf++ = w; + dev->buf_len--; + if (dev->buf_len) { + *dev->buf++ = w >> 8; + dev->buf_len--; + } + } else + dev_err(dev->dev, "RRDY IRQ while no data requested\n"); + break; + case 0x05: /* Transmit data ready */ + if (dev->buf_len) { + w = *dev->buf++; + dev->buf_len--; + if (dev->buf_len) { + w |= *dev->buf++ << 8; + dev->buf_len--; + } + omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); + } else + dev_err(dev->dev, "XRDY IRQ while no data to send\n"); + break; + default: + return IRQ_NONE; + } + + return IRQ_HANDLED; +} +#endif + +static irqreturn_t +omap_i2c_isr(int this_irq, void *dev_id, struct pt_regs *regs) +{ + struct omap_i2c_dev *dev = dev_id; + u16 bits; + u16 stat, w; + int count = 0; + + bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG); + while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) { + dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat); + if (count++ == 100) { + dev_warn(dev->dev, "Too much work in one IRQ\n"); + break; + } + + omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat); + + if (stat & OMAP_I2C_STAT_ARDY) { + omap_i2c_complete_cmd(dev, 0); + continue; + } + if (stat & OMAP_I2C_STAT_RRDY) { + w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG); + if (dev->buf_len) { + *dev->buf++ = w; + dev->buf_len--; + if (dev->buf_len) { + *dev->buf++ = w >> 8; + dev->buf_len--; + } + } else + dev_err(dev->dev, "RRDY IRQ while no data requested\n"); + omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RRDY); + continue; + } + if (stat & OMAP_I2C_STAT_XRDY) { + int bail_out = 0; + + w = 0; + if (dev->buf_len) { + w = *dev->buf++; + dev->buf_len--; + if (dev->buf_len) { + w |= *dev->buf++ << 8; + dev->buf_len--; + } + } else + dev_err(dev->dev, "XRDY IRQ while no data to send\n"); +#if 0 + if (!(stat & OMAP_I2C_STAT_BB)) { + dev_warn(dev->dev, "XRDY while bus not busy\n"); + bail_out = 1; + } +#endif + omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w); + omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XRDY); + if (bail_out) + omap_i2c_complete_cmd(dev, 1 << 15); + continue; + } + if (stat & OMAP_I2C_STAT_ROVR) { + dev_err(dev->dev, "Receive overrun\n"); + dev->cmd_err |= OMAP_I2C_STAT_ROVR; + } + if (stat & OMAP_I2C_STAT_XUDF) { + dev_err(dev->dev, "Transmit overflow\n"); + dev->cmd_err |= OMAP_I2C_STAT_XUDF; + } + if (stat & OMAP_I2C_STAT_NACK) { + omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_NACK); + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_STP); + } + if (stat & OMAP_I2C_STAT_AL) { + dev_err(dev->dev, "Arbitration lost\n"); + omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_AL); + } + } + + return count ? IRQ_HANDLED : IRQ_NONE; +} + +static struct i2c_algorithm omap_i2c_algo = { + .master_xfer = omap_i2c_xfer, + .functionality = omap_i2c_func, +}; + +static int +omap_i2c_probe(struct platform_device *pdev) +{ + struct omap_i2c_dev *dev; + struct i2c_adapter *adap; + struct resource *mem, *irq; + int r; + + /* NOTE: driver uses the static register mapping */ + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mem) { + dev_err(&pdev->dev, "no mem resource?\n"); + return -ENODEV; + } + irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!irq) { + dev_err(&pdev->dev, "no irq resource?\n"); + return -ENODEV; + } + + r = (int) request_mem_region(mem->start, (mem->end - mem->start) + 1, + driver_name); + if (!r) { + dev_err(&pdev->dev, "I2C region already claimed\n"); + return -EBUSY; + } + + if (clock > 200) + clock = 400; /* Fast mode */ + else + clock = 100; /* Standard mode */ + + dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL); + if (!dev) { + r = -ENOMEM; + goto do_release_region; + } + + /* FIXME: Get own address from platform_data */ + if (own >= 1 && own < 0x7f) + dev->own_address = own; + else + own = DEFAULT_OWN; + + dev->dev = &pdev->dev; + dev->irq = irq->start; + dev->base = (void __iomem *) IO_ADDRESS(mem->start); + platform_set_drvdata(pdev, dev); + + if ((r = omap_i2c_get_clocks(dev)) != 0) + goto do_free_mem; + + omap_i2c_enable_clocks(dev); + +#ifdef CONFIG_ARCH_OMAP15XX + dev->rev1 = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) < 0x20; +#endif + + /* reset ASAP, clearing any IRQs */ + omap_i2c_reset(dev); + +#ifdef CONFIG_ARCH_OMAP15XX + r = request_irq(dev->irq, dev->rev1 ? omap_i2c_rev1_isr : omap_i2c_isr, + 0, driver_name, dev); +#else + r = request_irq(dev->irq, omap_i2c_isr, 0, driver_name, dev); +#endif + if (r) { + dev_err(dev->dev, "failure requesting irq %i\n", dev->irq); + goto do_unuse_clocks; + } + r = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff; + dev_info(dev->dev, "bus %d rev%d.%d at %d kHz\n", + pdev->id - 1, r >> 4, r & 0xf, clock); + + adap = &dev->adapter; + i2c_set_adapdata(adap, dev); + adap->owner = THIS_MODULE; + adap->class = I2C_CLASS_HWMON; + strncpy(adap->name, "OMAP I2C adapter", sizeof(adap->name)); + adap->algo = &omap_i2c_algo; + adap->dev.parent = &pdev->dev; + + /* i2c device drivers may be active on return from add_adapter() */ + r = i2c_add_adapter(adap); + if (r) { + dev_err(dev->dev, "failure adding adapter\n"); + goto do_free_irq; + } + + omap_i2c_disable_clocks(dev); + + return 0; + +do_free_irq: + free_irq(dev->irq, dev); +do_unuse_clocks: + omap_i2c_disable_clocks(dev); + omap_i2c_put_clocks(dev); +do_free_mem: + kfree(dev); +do_release_region: + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); + release_mem_region(mem->start, (mem->end - mem->start) + 1); + + return r; +} + +static int +omap_i2c_remove(struct platform_device *pdev) +{ + struct omap_i2c_dev *dev = platform_get_drvdata(pdev); + struct resource *mem; + + free_irq(dev->irq, dev); + i2c_del_adapter(&dev->adapter); + omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0); + omap_i2c_put_clocks(dev); + kfree(dev); + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(mem->start, (mem->end - mem->start) + 1); + return 0; +} + +static struct platform_driver omap_i2c_driver = { + .probe = omap_i2c_probe, + .remove = omap_i2c_remove, + .driver = { + .name = (char *)driver_name, + }, +}; + +/* I2C may be needed to bring up other drivers */ +static int __init +omap_i2c_init_driver(void) +{ + return platform_driver_register(&omap_i2c_driver); +} +subsys_initcall(omap_i2c_init_driver); + +static void __exit omap_i2c_exit_driver(void) +{ + platform_driver_unregister(&omap_i2c_driver); +} +module_exit(omap_i2c_exit_driver); + +MODULE_AUTHOR("MontaVista Software, Inc. (and others)"); +MODULE_DESCRIPTION("TI OMAP I2C bus adapter"); +MODULE_LICENSE("GPL"); diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig index f9fae28f561..946cc210e80 100644 --- a/drivers/i2c/chips/Kconfig +++ b/drivers/i2c/chips/Kconfig @@ -102,6 +102,26 @@ config TPS65010 This driver can also be built as a module. If so, the module will be called tps65010. +config SENSORS_TLV320AIC23 + tristate "Texas Instruments TLV320AIC23 Codec" + depends on I2C && I2C_OMAP + help + If you say yes here you get support for the I2C control + interface for Texas Instruments TLV320AIC23 audio codec. + +config GPIOEXPANDER_OMAP + bool "GPIO Expander PCF8574PWR for OMAP" + depends on I2C && (ARCH_OMAP16XX || ARCH_OMAP24XX) + help + If you say yes here you get support for I/O expander calls + to configure IrDA, Camera and audio devices. + +config MENELAUS + bool "Menelaus PM chip" + depends on I2C=y && ARCH_OMAP24XX + help + Say yes here if you have Menelaus chip on your board + config SENSORS_M41T00 tristate "ST M41T00 RTC chip" depends on I2C && PPC32 diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile index 46178b57b1f..316b67d9a73 100644 --- a/drivers/i2c/chips/Makefile +++ b/drivers/i2c/chips/Makefile @@ -13,6 +13,9 @@ obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o obj-$(CONFIG_SENSORS_RTC8564) += rtc8564.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o obj-$(CONFIG_TPS65010) += tps65010.o +obj-$(CONFIG_SENSORS_TLV320AIC23) += tlv320aic23.o +obj-$(CONFIG_GPIOEXPANDER_OMAP) += gpio_expander_omap.o +obj-$(CONFIG_MENELAUS) += menelaus.o obj-$(CONFIG_RTC_X1205_I2C) += x1205.o ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) diff --git a/drivers/i2c/chips/gpio_expander_omap.c b/drivers/i2c/chips/gpio_expander_omap.c new file mode 100644 index 00000000000..d63a6ee77cd --- /dev/null +++ b/drivers/i2c/chips/gpio_expander_omap.c @@ -0,0 +1,71 @@ +/* + * drivers/i2c/chips/gpio_expander_omap.c + * + * Copyright (C) 2004 Texas Instruments Inc + * Author: + * + * gpio expander is used to configure IrDA, camera and audio devices on omap 1710 processor. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include + +int read_gpio_expa(u8 * val, int addr); +int write_gpio_expa(u8 val, int addr); + +int write_gpio_expa(u8 val, int addr) +{ + struct i2c_adapter *adap; + int err; + struct i2c_msg msg[1]; + unsigned char data[1]; + + adap = i2c_get_adapter(0); + if (!adap) + return -ENODEV; + msg->addr = addr; /* I2C address of GPIO EXPA */ + msg->flags = 0; + msg->len = 1; + msg->buf = data; + data[0] = val; + err = i2c_transfer(adap, msg, 1); + if (err >= 0) + return 0; + return err; +} + +/* Read from I/O EXPANDER on the H3 board. + * The IO expanders need an independent I2C client driver. + */ + +int read_gpio_expa(u8 * val, int addr) +{ + struct i2c_adapter *adap; + int err; + struct i2c_msg msg[1]; + unsigned char data[1]; + + adap = i2c_get_adapter(0); + if (!adap) + return -ENODEV; + msg->addr = addr; /* I2C address of GPIO EXPA */ + msg->flags = I2C_M_RD; + msg->len = 2; + msg->buf = data; + err = i2c_transfer(adap, msg, 1); + *val = data[0]; + + if (err >= 0) + return 0; + return err; +} + +EXPORT_SYMBOL(read_gpio_expa); +EXPORT_SYMBOL(write_gpio_expa); + diff --git a/drivers/i2c/chips/isp1301_omap.c b/drivers/i2c/chips/isp1301_omap.c index 1251c7fc18d..8e5f37c6bb6 100644 --- a/drivers/i2c/chips/isp1301_omap.c +++ b/drivers/i2c/chips/isp1301_omap.c @@ -36,7 +36,11 @@ #include #include +#include + +#include #include +#include #ifndef DEBUG @@ -45,7 +49,7 @@ #define DRIVER_VERSION "24 August 2004" -#define DRIVER_NAME (isp1301_driver.name) +#define DRIVER_NAME (isp1301_driver.driver.name) MODULE_DESCRIPTION("ISP1301 USB OTG Transceiver Driver"); MODULE_LICENSE("GPL"); @@ -56,6 +60,7 @@ struct isp1301 { void (*i2c_release)(struct device *dev); int irq; + int irq_type; u32 last_otg_ctrl; unsigned working:1; @@ -91,14 +96,11 @@ struct isp1301 { /*-------------------------------------------------------------------------*/ -#ifdef CONFIG_MACH_OMAP_H2 +#if defined(CONFIG_MACH_OMAP_H2) || \ + defined(CONFIG_MACH_OMAP_H3) /* board-specific PM hooks */ -#include -#include -#include - #if defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE) @@ -129,17 +131,30 @@ static void enable_vbus_source(struct isp1301 *isp) } -/* products will deliver OTG messages with LEDs, GUI, etc */ -static inline void notresponding(struct isp1301 *isp) +#else + +static void enable_vbus_draw(struct isp1301 *isp, unsigned mA) { - printk(KERN_NOTICE "OTG device not responding.\n"); + pr_debug("%s UNIMPL\n", __FUNCTION__); } +static void enable_vbus_source(struct isp1301 *isp) +{ + pr_debug("%s UNIMPL\n", __FUNCTION__); +} #endif /*-------------------------------------------------------------------------*/ +/* products will deliver OTG messages with LEDs, GUI, etc */ +static inline void notresponding(struct isp1301 *isp) +{ + printk(KERN_NOTICE "OTG device not responding.\n"); +} + +/*-------------------------------------------------------------------------*/ + /* only two addresses possible */ #define ISP_BASE 0x2c static unsigned short normal_i2c[] = { @@ -515,6 +530,7 @@ static inline void check_state(struct isp1301 *isp, const char *tag) { } static void update_otg1(struct isp1301 *isp, u8 int_src) { u32 otg_ctrl; + u8 int_id; otg_ctrl = OTG_CTRL_REG & OTG_CTRL_MASK @@ -528,7 +544,10 @@ static void update_otg1(struct isp1301 *isp, u8 int_src) } if (int_src & INTR_VBUS_VLD) otg_ctrl |= OTG_VBUSVLD; - if (int_src & INTR_ID_GND) { /* default-A */ + + int_id = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE); + + if (int_id & INTR_ID_GND) { /* default-A */ if (isp->otg.state == OTG_STATE_B_IDLE || isp->otg.state == OTG_STATE_UNDEFINED) { a_idle(isp, "init"); @@ -1083,7 +1102,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat) /* update the OTG controller state to match the isp1301; may * trigger OPRT_CHG irqs for changes going to the isp1301. */ - update_otg1(isp, isp_stat); + update_otg1(isp, stat); // pass the actual interrupt latch status update_otg2(isp, isp_bstat); check_state(isp, __FUNCTION__); #endif @@ -1224,6 +1243,9 @@ static int isp1301_detach_client(struct i2c_client *i2c) if (machine_is_omap_h2()) omap_free_gpio(2); + if (machine_is_omap_h3()) + omap_free_gpio(14); + isp->timer.data = 0; set_bit(WORK_STOP, &isp->todo); del_timer_sync(&isp->timer); @@ -1302,7 +1324,7 @@ isp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host) power_up(isp); - if (machine_is_omap_h2()) + if (machine_is_omap_h2() || machine_is_omap_h3()) isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); dev_info(&isp->client.dev, "A-Host sessions ok\n"); @@ -1365,13 +1387,13 @@ isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget) power_up(isp); isp->otg.state = OTG_STATE_B_IDLE; - if (machine_is_omap_h2()) + if (machine_is_omap_h2() || machine_is_omap_h3()) isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0); isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING, - INTR_SESS_VLD); + INTR_SESS_VLD | INTR_VBUS_VLD); isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING, - INTR_VBUS_VLD); + INTR_VBUS_VLD | INTR_SESS_VLD); dev_info(&isp->client.dev, "B-Peripheral sessions ok\n"); dump_regs(isp, __FUNCTION__); @@ -1448,6 +1470,10 @@ isp1301_start_hnp(struct otg_transceiver *dev) * So do this part as early as possible... */ switch (isp->otg.state) { + case OTG_STATE_B_PERIPHERAL: + isp->otg.state = OTG_STATE_B_WAIT_ACON; + isp1301_defer_work(isp, WORK_UPDATE_ISP); + break; case OTG_STATE_B_HOST: isp->otg.state = OTG_STATE_B_PERIPHERAL; /* caller will suspend next */ @@ -1501,6 +1527,7 @@ static int isp1301_probe(struct i2c_adapter *bus, int address, int kind) isp->timer.data = (unsigned long) isp; isp->irq = -1; + isp->irq_type = 0; isp->client.addr = address; i2c_set_clientdata(&isp->client, isp); isp->client.adapter = bus; @@ -1563,23 +1590,34 @@ fail1: } #endif - if (machine_is_omap_h2()) { + if (machine_is_omap_h2() || machine_is_omap_h3()) { /* full speed signaling by default */ isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SPEED_REG); isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_SPD_SUSP_CTRL); + } + if (machine_is_omap_h2()) { /* IRQ wired at M14 */ omap_cfg_reg(M14_1510_GPIO2); isp->irq = OMAP_GPIO_IRQ(2); omap_request_gpio(2); omap_set_gpio_direction(2, 1); - omap_set_gpio_edge_ctrl(2, OMAP_GPIO_FALLING_EDGE); + isp->irq_type = SA_TRIGGER_FALLING; + } + + if (machine_is_omap_h3()) { + /* IRQ wired at N21 */ + omap_cfg_reg(N21_1710_GPIO14); + isp->irq = OMAP_GPIO_IRQ(14); + omap_request_gpio(14); + omap_set_gpio_direction(14, 1); + isp->irq_type = SA_TRIGGER_FALLING; } status = request_irq(isp->irq, isp1301_irq, - SA_SAMPLE_RANDOM, DRIVER_NAME, isp); + isp->irq_type, DRIVER_NAME, isp); if (status < 0) { dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n", isp->irq, status); diff --git a/drivers/i2c/chips/menelaus.c b/drivers/i2c/chips/menelaus.c new file mode 100644 index 00000000000..3fc037483c0 --- /dev/null +++ b/drivers/i2c/chips/menelaus.c @@ -0,0 +1,487 @@ +/* + * drivers/i2c/chips/menelaus.c + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * Some parts based tps65010.c: + * Copyright (C) 2004 Texas Instruments and + * Copyright (C) 2004-2005 David Brownell + * + * Some parts based on tlv320aic24.c: + * Copyright (C) by Kai Svahn + * + * Changes for interrupt handling and clean-up by + * Tony Lindgren and Imre Deak + * Copyright (C) 2005 Nokia Corporation + * + * 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 + +#define DRIVER_NAME "menelaus" + +#define pr_err(fmt, arg...) printk(KERN_ERR DRIVER_NAME ": ", ## arg); + +#define MENELAUS_I2C_ADDRESS 0x72 + +#define MENELAUS_REV 0x01 +#define MENELAUS_VCORE_CTRL1 0x02 +#define MENELAUS_VCORE_CTRL2 0x03 +#define MENELAUS_VCORE_CTRL3 0x04 +#define MENELAUS_VCORE_CTRL4 0x05 +#define MENELAUS_VCORE_CTRL5 0x06 +#define MENELAUS_DCDC_CTRL1 0x07 +#define MENELAUS_DCDC_CTRL2 0x08 +#define MENELAUS_DCDC_CTRL3 0x09 +#define MENELAUS_LDO_CTRL1 0x0A +#define MENELAUS_LDO_CTRL2 0x0B +#define MENELAUS_LDO_CTRL3 0x0C +#define MENELAUS_LDO_CTRL4 0x0D +#define MENELAUS_LDO_CTRL5 0x0E +#define MENELAUS_LDO_CTRL6 0x0F +#define MENELAUS_LDO_CTRL7 0x10 +#define MENELAUS_LDO_CTRL8 0x11 +#define MENELAUS_SLEEP_CTRL1 0x12 +#define MENELAUS_SLEEP_CTRL2 0x13 +#define MENELAUS_DEVICE_OFF 0x14 +#define MENELAUS_OSC_CTRL 0x15 +#define MENELAUS_DETECT_CTRL 0x16 +#define MENELAUS_INT_MASK1 0x17 +#define MENELAUS_INT_MASK2 0x18 +#define MENELAUS_INT_STATUS1 0x19 +#define MENELAUS_INT_STATUS2 0x1A +#define MENELAUS_INT_ACK1 0x1B +#define MENELAUS_INT_ACK2 0x1C +#define MENELAUS_GPIO_CTRL 0x1D +#define MENELAUS_GPIO_IN 0x1E +#define MENELAUS_GPIO_OUT 0x1F +#define MENELAUS_BBSMS 0x20 +#define MENELAUS_RTC_CTRL 0x21 +#define MENELAUS_RTC_UPDATE 0x22 +#define MENELAUS_RTC_SEC 0x23 +#define MENELAUS_RTC_MIN 0x24 +#define MENELAUS_RTC_HR 0x25 +#define MENELAUS_RTC_DAY 0x26 +#define MENELAUS_RTC_MON 0x27 +#define MENELAUS_RTC_YR 0x28 +#define MENELAUS_RTC_WKDAY 0x29 +#define MENELAUS_RTC_AL_SEC 0x2A +#define MENELAUS_RTC_AL_MIN 0x2B +#define MENELAUS_RTC_AL_HR 0x2C +#define MENELAUS_RTC_AL_DAY 0x2D +#define MENELAUS_RTC_AL_MON 0x2E +#define MENELAUS_RTC_AL_YR 0x2F +#define MENELAUS_RTC_COMP_MSB 0x30 +#define MENELAUS_RTC_COMP_LSB 0x31 +#define MENELAUS_S1_PULL_EN 0x32 +#define MENELAUS_S1_PULL_DIR 0x33 +#define MENELAUS_S2_PULL_EN 0x34 +#define MENELAUS_S2_PULL_DIR 0x35 +#define MENELAUS_MCT_CTRL1 0x36 +#define MENELAUS_MCT_CTRL2 0x37 +#define MENELAUS_MCT_CTRL3 0x38 +#define MENELAUS_MCT_PIN_ST 0x39 +#define MENELAUS_DEBOUNCE1 0x3A + +#define IH_MENELAUS_IRQS 12 +#define MENELAUS_MMC_S1CD_IRQ 0 /* MMC slot 1 card change */ +#define MENELAUS_MMC_S2CD_IRQ 1 /* MMC slot 2 card change */ +#define MENELAUS_MMC_S1D1_IRQ 2 /* MMC DAT1 low in slot 1 */ +#define MENELAUS_MMC_S2D1_IRQ 3 /* MMC DAT1 low in slot 2 */ +#define MENELAUS_LOWBAT_IRQ 4 /* Low battery */ +#define MENELAUS_HOTDIE_IRQ 5 /* Hot die detect */ +#define MENELAUS_UVLO_IRQ 6 /* UVLO detect */ +#define MENELAUS_TSHUT_IRQ 7 /* Thermal shutdown */ +#define MENELAUS_RTCTMR_IRQ 8 /* RTC timer */ +#define MENELAUS_RTCALM_IRQ 9 /* RTC alarm */ +#define MENELAUS_RTCERR_IRQ 10 /* RTC error */ +#define MENELAUS_PSHBTN_IRQ 11 /* Push button */ +#define MENELAUS_RESERVED12_IRQ 12 /* Reserved */ +#define MENELAUS_RESERVED13_IRQ 13 /* Reserved */ +#define MENELAUS_RESERVED14_IRQ 14 /* Reserved */ +#define MENELAUS_RESERVED15_IRQ 15 /* Reserved */ + +static void menelaus_work(void * _menelaus); + +/* Initialized by menelaus_init */ +static unsigned short normal_i2c[] = { MENELAUS_I2C_ADDRESS, I2C_CLIENT_END }; + +I2C_CLIENT_INSMOD; + +struct menelaus_chip { + unsigned long initialized; + struct semaphore lock; + struct i2c_client client; + struct work_struct work; + int irq; + void *handlers[16]; + void (*mmc_callback)(unsigned long data, u8 mask); + unsigned long mmc_callback_data; +}; + +static struct menelaus_chip menelaus; + +static void menelaus_write(u8 value, u8 reg) +{ + if (i2c_smbus_write_byte_data(&menelaus.client, reg, value) < 0) + pr_err("write error"); +} + +static u8 menelaus_read(u8 reg) +{ + int val = i2c_smbus_read_byte_data(&menelaus.client, reg); + + if (val < 0) { + pr_err("read error"); + return 0; + } + + return val; +} + +static void menelaus_enable_irq(int irq) +{ + if (irq > 7) + menelaus_write(menelaus_read(MENELAUS_INT_MASK2) & + ~(1 << (irq - 8)), MENELAUS_INT_MASK2); + else + menelaus_write(menelaus_read(MENELAUS_INT_MASK1) & + ~(1 << irq), MENELAUS_INT_MASK1); +} + +static void menelaus_disable_irq(int irq) +{ + if (irq > 7) + menelaus_write(menelaus_read(MENELAUS_INT_MASK2) + | (1 << (irq - 8)), MENELAUS_INT_MASK2); + else + menelaus_write(menelaus_read(MENELAUS_INT_MASK1) + | (1 << irq), MENELAUS_INT_MASK1); +} + +static void menelaus_ack_irq(int irq) +{ + if (irq > 7) + menelaus_write(1 << (irq - 8), MENELAUS_INT_ACK2); + else + menelaus_write(1 << irq, MENELAUS_INT_ACK1); +} + +/* Adds a handler for an interrupt. Does not run in interrupt context */ +static int menelaus_add_irq_work(int irq, void * handler) +{ + down(&menelaus.lock); + menelaus.handlers[irq] = handler; + menelaus_enable_irq(irq); + up(&menelaus.lock); + + return 0; +} + +/* Removes handler for an interrupt */ +static void menelaus_remove_irq_work(int irq) +{ + down(&menelaus.lock); + menelaus_disable_irq(irq); + menelaus.handlers[irq] = NULL; + up(&menelaus.lock); +} + +/*-----------------------------------------------------------------------*/ + +/* + * Toggles the MMC slots between open-drain and push-pull mode. + * We always set both slots the same way. + */ +void menelaus_mmc_opendrain(int enable) +{ + unsigned char reg = menelaus_read(MENELAUS_MCT_CTRL1); + + if (enable) + reg |= (0x3 << 2); + else + reg &= ~(0x3 << 2); + + menelaus_write(reg, MENELAUS_MCT_CTRL1); +} +EXPORT_SYMBOL(menelaus_mmc_opendrain); + +/* + * Gets scheduled when a card detect interrupt happens. Note that in some cases + * this line is wired to card cover switch rather than the card detect switch + * in each slot. In this case the cards are not seen by menelaus. + * FIXME: Add handling for D1 too + */ +static int menelaus_mmc_cd_work(struct menelaus_chip *menelaus) +{ + unsigned char reg; + unsigned char card_mask = 0; + + reg = menelaus_read(MENELAUS_MCT_PIN_ST); + + if (!(reg & 0x1)) + card_mask |= (1 << 0); + + if (!(reg & 0x2)) + card_mask |= (1 << 1); + + if (menelaus->mmc_callback) + menelaus->mmc_callback(menelaus->mmc_callback_data, card_mask); + + return 0; +} + +/* Initializes MMC slots */ +void menelaus_mmc_register(void (*callback)(unsigned long data, u8 card_mask), unsigned long data) +{ + int reg; + + /* DCDC3 to 3V */ + reg = menelaus_read(MENELAUS_DCDC_CTRL1); + reg |= 0x6 << 4; + menelaus_write(reg, MENELAUS_DCDC_CTRL1); + + reg = menelaus_read(MENELAUS_DCDC_CTRL3); + reg |= 0x6; + menelaus_write(reg, MENELAUS_DCDC_CTRL3); + + /* Enable both slots, do not set auto shutdown */ + reg = menelaus_read(MENELAUS_MCT_CTRL3); + reg |= 0x3; + menelaus_write(reg, MENELAUS_MCT_CTRL3); + + /* Enable card detect for both slots, slot 2 powered from DCDC3 */ + reg = menelaus_read(MENELAUS_MCT_CTRL2); + reg |= 0xf0; + menelaus_write(reg, MENELAUS_MCT_CTRL2); + + /* Set both slots in open-drain mode, card detect normally closed */ + reg = menelaus_read(MENELAUS_MCT_CTRL1); + reg |= 0xfc; + menelaus_write(reg, MENELAUS_MCT_CTRL1); + + /* Set MMC voltage */ + reg = menelaus_read(MENELAUS_LDO_CTRL7); + reg |= 0x03; + menelaus_write(reg, MENELAUS_LDO_CTRL7); + + menelaus.mmc_callback_data = data; + menelaus.mmc_callback = callback; + + menelaus_add_irq_work(MENELAUS_MMC_S1CD_IRQ, menelaus_mmc_cd_work); + menelaus_add_irq_work(MENELAUS_MMC_S2CD_IRQ, menelaus_mmc_cd_work); + menelaus_add_irq_work(MENELAUS_MMC_S1D1_IRQ, menelaus_mmc_cd_work); + menelaus_add_irq_work(MENELAUS_MMC_S2D1_IRQ, menelaus_mmc_cd_work); +} +EXPORT_SYMBOL(menelaus_mmc_register); + +void menelaus_mmc_remove(void) +{ + menelaus_remove_irq_work(MENELAUS_MMC_S1CD_IRQ); + menelaus_remove_irq_work(MENELAUS_MMC_S2CD_IRQ); + menelaus_remove_irq_work(MENELAUS_MMC_S1D1_IRQ); + menelaus_remove_irq_work(MENELAUS_MMC_S2D1_IRQ); + + menelaus.mmc_callback = NULL; + menelaus.mmc_callback_data = 0; + + /* FIXME: Shutdown MMC components of Menelaus */ +} +EXPORT_SYMBOL(menelaus_mmc_remove); + +/*-----------------------------------------------------------------------*/ + +/* Handles Menelaus interrupts. Does not run in interrupt context */ +static void menelaus_work(void * _menelaus) +{ + struct menelaus_chip *menelaus = _menelaus; + int (*handler)(struct menelaus_chip *menelaus); + + while (1) { + int i; + unsigned char isr; + + isr = menelaus_read(MENELAUS_INT_STATUS1) | + (menelaus_read(MENELAUS_INT_STATUS2) << 8); + + if (!isr) + break; + + for (i = 0; i < IH_MENELAUS_IRQS; i++) { + if (isr & (1 << i)) { + down(&menelaus->lock); + menelaus_disable_irq(i); + menelaus_ack_irq(i); + if (menelaus->handlers[i]) { + handler = menelaus->handlers[i]; + handler(menelaus); + } + menelaus_enable_irq(i); + up(&menelaus->lock); + } + } + } + enable_irq(menelaus->irq); +} + +/* + * We cannot use I2C in interrupt context, so we just schedule work. + */ +static irqreturn_t menelaus_irq(int irq, void *_menelaus, struct pt_regs *regs) +{ + struct menelaus_chip *menelaus = _menelaus; + + disable_irq_nosync(irq); + (void)schedule_work(&menelaus->work); + + return IRQ_HANDLED; +} + +static struct i2c_driver menelaus_i2c_driver; + +static int menelaus_probe(struct i2c_adapter *adapter, int address, int kind) +{ + struct i2c_client *c; + int rev = 0; + int err = 0, i; + + if (test_and_set_bit(0, &menelaus.initialized)) + return -EBUSY; + + c = &menelaus.client; + strncpy(c->name, DRIVER_NAME, sizeof(c->name)); + c->addr = address; + c->adapter = adapter; + c->driver = &menelaus_i2c_driver; + c->flags = 0; + + if ((err = i2c_attach_client(c)) < 0) { + pr_err("couldn't attach\n"); + goto fail1; + } + + /* If a true probe check the device */ + if (kind < 0 && (rev = menelaus_read(MENELAUS_REV)) < 0) { + pr_err("device not found"); + err = -ENODEV; + goto fail2; + } + + /* Most likely Menelaus interrupt is at SYS_NIRQ */ + omap_cfg_reg(W19_24XX_SYS_NIRQ); + menelaus.irq = INT_24XX_SYS_NIRQ; + + /* Disable all menelaus interrupts */ + for (i = 0; i < 16; i++) { + menelaus_ack_irq(i); + menelaus_disable_irq(i); + } + + err = request_irq(menelaus.irq, menelaus_irq, SA_INTERRUPT, + DRIVER_NAME, &menelaus); + if (err) + printk(KERN_ERR "Could not get Menelaus IRQ\n"); + + init_MUTEX(&menelaus.lock); + INIT_WORK(&menelaus.work, menelaus_work, &menelaus); + + if (kind < 0) + pr_info("Menelaus rev %d.%d\n", rev >> 4, rev & 0x0f); + + return 0; + +fail2: + i2c_detach_client(c); +fail1: + clear_bit(0, &menelaus.initialized); + return err; +} + +static int menelaus_remove(struct i2c_client *client) +{ + int err; + + free_irq(menelaus.irq, &menelaus); + + if ((err = i2c_detach_client(client))) { + pr_err("client deregistration failed\n"); + return err; + } + + clear_bit(0, &menelaus.initialized); + + return 0; +} + +/*-----------------------------------------------------------------------*/ + +static int menelaus_scan_bus(struct i2c_adapter *bus) +{ + if (!i2c_check_functionality(bus, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE)) { + pr_err("invalid i2c bus functionality\n"); + return -EINVAL; + } + + return i2c_probe(bus, &addr_data, menelaus_probe); +} + +static struct i2c_driver menelaus_i2c_driver = { + .driver { + .name = DRIVER_NAME, + }, + .id = I2C_DRIVERID_MISC, /*FIXME:accroding to i2c-ids.h */ + .class = I2C_CLASS_HWMON, + .attach_adapter = menelaus_scan_bus, + .detach_client = menelaus_remove, +}; + +static int __init menelaus_init(void) +{ + int res; + + if ((res = i2c_add_driver(&menelaus_i2c_driver)) < 0) { + pr_err("driver registration failed\n"); + return res; + } + + return 0; +} + +static void __exit menelaus_exit(void) +{ + if (i2c_del_driver(&menelaus_i2c_driver) < 0) + pr_err("driver remove failed\n"); + + /* FIXME: Shutdown menelaus parts that can be shut down */ +} + +MODULE_AUTHOR("Texas Instruments, Inc."); +MODULE_DESCRIPTION("I2C interface for Menelaus."); +MODULE_LICENSE("GPL"); + +module_init(menelaus_init); +module_exit(menelaus_exit); diff --git a/drivers/i2c/chips/tlv320aic23.c b/drivers/i2c/chips/tlv320aic23.c new file mode 100644 index 00000000000..b62ea5318c9 --- /dev/null +++ b/drivers/i2c/chips/tlv320aic23.c @@ -0,0 +1,669 @@ +/* + * Texas Instrumens TLV320AIC23 audio codec's i2c interface. + * + * Copyright (c) by Kai Svahn + * Copyright (c) by Jussi Laako + * + * 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 + +#define TLV320AIC23_VERSION "1.8" +#define TLV320AIC23_DATE "10-Feb-2006" +#define MAX_VOL 100 +#define MIN_VOL 0 +#define MAX_GAIN 100 +#define MIN_GAIN 0 +#define OUTPUT_VOLUME_MIN LHV_MIN +#define OUTPUT_VOLUME_MAX LHV_MAX +#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN) +#define INPUT_VOLUME_MIN LIV_MIN +#define INPUT_VOLUME_MAX LIV_MAX +#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN) + +/* I2C Addresses to scan */ +static unsigned short normal_i2c[] = { TLV320AIC23ID1, TLV320AIC23ID2, \ + I2C_CLIENT_END }; +/*static unsigned short normal_i2c_range[] = { I2C_CLIENT_END };*/ + +/* This makes all addr_data:s */ +I2C_CLIENT_INSMOD; + +static struct i2c_driver tlv320aic23_driver; +static struct i2c_client *new_client; +static int selftest; + +static struct tlv320aic23_info { + u16 volume_reg_left; + u16 volume_reg_right; + u16 input_gain_reg_left; + u16 input_gain_reg_right; + u16 power; /* For POWER_DOWN_CONTROL_ADDR */ + u16 mask; /* For ANALOG_AUDIO_CONTROL_ADDR */ + int mic_loopback; + int mic_enable; + int sta; + int power_down; + int initialization; +} tlv320aic23_info_l; + +static int _tlv320aic23_write_value(struct i2c_client *client, u8 reg, u16 value) +{ + u8 val, wreg; + + /* TLV320AIC23 has 7 bit address and 9 bits of data + * so we need to switch one data bit into reg and rest + * of data into val + */ + + wreg = (reg << 1); + val = (0x01 & (value >> 8)); + wreg = (wreg | val); + val = (0x00ff & value); + + return i2c_smbus_write_byte_data(client, wreg, val); +} + +int tlv320aic23_write_value(u8 reg, u16 value) +{ + static struct i2c_client *client; + client = new_client; + _tlv320aic23_write_value(client, reg, value); + + return 0; +} + +static int tlv320aic23_detect_client(struct i2c_adapter *adapter, int address, + int kind) +{ + int err = 0; + const char *client_name = "TLV320AIC23 Audio Codec"; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE)) { + printk(KERN_WARNING "%s functinality check failed\n", + client_name); + return err; + } + + if (!(new_client = kmalloc(sizeof(struct i2c_client), + GFP_KERNEL))) { + err = -ENOMEM; + printk(KERN_WARNING "Couldn't allocate memory for %s\n", + client_name); + return err; + } + + memset(new_client, 0x00, sizeof(struct i2c_client)); + new_client->addr = address; + new_client->adapter = adapter; + new_client->driver = &tlv320aic23_driver; + new_client->flags = 0; + strlcpy(new_client->name, client_name, I2C_NAME_SIZE); + + if ((err = i2c_attach_client(new_client))) { + printk(KERN_WARNING "Couldn't attach %s\n", client_name); + kfree(new_client); + return err; + } + return 0; +} + +static int tlv320aic23_detach_client(struct i2c_client *client) +{ + int err; + + if ((err = i2c_detach_client(client))) { + printk("tlv320aic23.o: Client deregistration failed, \ + client not detached.\n"); + return err; + } + kfree(client); + return 0; +} + +static int tlv320aic23_attach_adapter(struct i2c_adapter *adapter) +{ + int res; + + res = i2c_probe(adapter, &addr_data, &tlv320aic23_detect_client); + return res; +} + +static struct i2c_driver tlv320aic23_driver = { + .driver { + .name = "OMAP+TLV320AIC23 codec", + /*.flags = I2C_DF_NOTIFY,*/ + }, + .id = I2C_DRIVERID_MISC, /* Experimental ID */ + .attach_adapter = tlv320aic23_attach_adapter, + .detach_client = tlv320aic23_detach_client, +}; + +/* + * Configures the McBSP3 which is used to send clock to the AIC23 codec. + * The input clock rate from DSP is 12MHz. + * The DSP clock must be on before this is called. + */ +static int omap_mcbsp3_tlv320aic23_clock_init(void) +{ + u16 w; + + /* enable 12MHz clock to mcbsp 1 & 3 */ + __raw_writew(__raw_readw(DSP_IDLECT2) | (1<<1), DSP_IDLECT2); + __raw_writew(__raw_readw(DSP_RSTCT2) | 1 | 1<<1, DSP_RSTCT2); + + /* disable sample rate generator */ + OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SPCR1, 0x0000); + OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SPCR2, 0x0000); + + /* pin control register */ + OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, PCR0,(CLKXM | CLKXP | CLKRP)); + + /* configure srg to send 12MHz pulse from dsp peripheral clock */ + OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SRGR1, 0x0000); + OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SRGR2, CLKSM); + + /* enable sample rate generator */ + w = OMAP_MCBSP_READ(OMAP1610_MCBSP3_BASE, SPCR2); + OMAP_MCBSP_WRITE(OMAP1610_MCBSP3_BASE, SPCR2, (w | FREE | GRST)); + printk("Clock enabled to MCBSP1 & 3 \n"); + + return 0; +} + +static void update_volume_left(int volume) +{ + u16 val = 0; + val = ((volume * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN; + tlv320aic23_write_value(LEFT_CHANNEL_VOLUME_ADDR, val); + tlv320aic23_info_l.volume_reg_left = volume; +} + +static void update_volume_right(int volume) +{ + u16 val = 0; + val = ((volume * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN; + tlv320aic23_write_value(RIGHT_CHANNEL_VOLUME_ADDR, val); + tlv320aic23_info_l.volume_reg_right = volume; +} + +static void set_mic(int mic_en) +{ + u16 dg_ctrl; + + if (mic_en) { + tlv320aic23_info_l.power = OSC_OFF | LINE_OFF; + dg_ctrl = ADCHP_ON; + tlv320aic23_info_l.mask &= ~MICM_MUTED; + tlv320aic23_info_l.mask |= MICB_20DB; /* STE_ENABLED */ + } else { + tlv320aic23_info_l.power = + OSC_OFF | ADC_OFF | MIC_OFF | LINE_OFF; + dg_ctrl = 0x00; + tlv320aic23_info_l.mask = + DAC_SELECTED | INSEL_MIC | MICM_MUTED; + } + tlv320aic23_write_value(POWER_DOWN_CONTROL_ADDR, + tlv320aic23_info_l.power); + tlv320aic23_write_value(DIGITAL_AUDIO_CONTROL_ADDR, dg_ctrl); + tlv320aic23_write_value(ANALOG_AUDIO_CONTROL_ADDR, + tlv320aic23_info_l.mask); + tlv320aic23_info_l.mic_enable = mic_en; + + printk(KERN_INFO "tlv320aic23 mic state: %i\n", mic_en); +} + +static void tlv320aic23_init_power(void) +{ + tlv320aic23_write_value(RESET_CONTROL_ADDR, 0x00); + + if (tlv320aic23_info_l.initialization == 0) { + tlv320aic23_write_value(LEFT_CHANNEL_VOLUME_ADDR, LHV_MIN); + tlv320aic23_write_value(RIGHT_CHANNEL_VOLUME_ADDR, LHV_MIN); + } + else { + update_volume_left(tlv320aic23_info_l.volume_reg_left); + update_volume_right(tlv320aic23_info_l.volume_reg_right); + } + + tlv320aic23_info_l.mask = DAC_SELECTED | INSEL_MIC | MICM_MUTED; + tlv320aic23_write_value(ANALOG_AUDIO_CONTROL_ADDR, + tlv320aic23_info_l.mask); + tlv320aic23_write_value(DIGITAL_AUDIO_CONTROL_ADDR, 0x00); + tlv320aic23_write_value(DIGITAL_AUDIO_FORMAT_ADDR, LRP_ON | FOR_DSP); + tlv320aic23_write_value(SAMPLE_RATE_CONTROL_ADDR, USB_CLK_ON); + tlv320aic23_write_value(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON); + tlv320aic23_info_l.power = OSC_OFF | ADC_OFF | MIC_OFF | LINE_OFF; + tlv320aic23_write_value(POWER_DOWN_CONTROL_ADDR, + tlv320aic23_info_l.power); + + /* enable mic input */ + if (tlv320aic23_info_l.mic_enable) + set_mic(tlv320aic23_info_l.mic_enable); + + printk(KERN_INFO "tlv320aic23_init_power() done\n"); +} + +void tlv320aic23_power_down(void) +{ + printk("tlv320aic23 powering down\n"); + tlv320aic23_write_value(POWER_DOWN_CONTROL_ADDR, 0xff); + tlv320aic23_info_l.power_down = 1; +} + +void tlv320aic23_power_up(void) +{ + printk("tlv320aic23 powering up\n"); + tlv320aic23_init_power(); + tlv320aic23_info_l.power_down = 0; +} + +/*----------------------------------------------------------------------*/ +/* sysfs initializations */ +/*----------------------------------------------------------------------*/ + +static ssize_t store_volume_left(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + signed volume; + + sscanf(buf, "%i", &volume); + + if (volume < MIN_VOL) { + tlv320aic23_power_down(); + return count; + } else if (volume > MIN_VOL && tlv320aic23_info_l.power_down) { + tlv320aic23_info_l.volume_reg_left = volume; + tlv320aic23_power_up(); + return count; + } + if (volume > MAX_VOL) + volume = MAX_VOL; + + update_volume_left(volume); + return count; +} + +static ssize_t show_volume_left(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", tlv320aic23_info_l.volume_reg_left); +} + +static DEVICE_ATTR(volume_left, S_IRUGO | S_IWUGO, + show_volume_left, store_volume_left); + +static ssize_t store_volume_right(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + signed volume; + + sscanf(buf, "%i", &volume); + if (volume < MIN_VOL) { + tlv320aic23_power_down(); + return count; + } else if (volume > MIN_VOL && tlv320aic23_info_l.power_down) { + tlv320aic23_info_l.volume_reg_right = volume; + tlv320aic23_power_up(); + return count; + } + if (volume > MAX_VOL) + volume = MAX_VOL; + + update_volume_right(volume); + return count; +} + +static ssize_t show_volume_right(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", tlv320aic23_info_l.volume_reg_right); +} + +static DEVICE_ATTR(volume_right, S_IRUGO | S_IWUGO, + show_volume_right, store_volume_right); + +static ssize_t store_gain_left(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u16 val = 0; + unsigned gain; + + sscanf(buf, "%u", &gain); + if (gain > MAX_VOL) + gain = MAX_VOL; + + val = ((gain * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN; + tlv320aic23_write_value(LEFT_LINE_VOLUME_ADDR, val); + tlv320aic23_info_l.input_gain_reg_left = gain; + + return count; +} + +static ssize_t show_gain_left(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", tlv320aic23_info_l.input_gain_reg_left); +} + +static DEVICE_ATTR(gain_left, S_IRUGO | S_IWUSR, show_gain_left, + store_gain_left); + +static ssize_t store_gain_right(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + u16 val = 0; + unsigned gain; + + sscanf(buf, "%u", &gain); + if (gain > MAX_VOL) + gain = MAX_VOL; + + val = ((gain * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN; + tlv320aic23_write_value(RIGHT_LINE_VOLUME_ADDR, val); + tlv320aic23_info_l.input_gain_reg_right = gain; + + return count; +} + +static ssize_t show_gain_right(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", tlv320aic23_info_l.input_gain_reg_right); +} + +static DEVICE_ATTR(gain_right, S_IRUGO | S_IWUSR, show_gain_right, + store_gain_right); + +static ssize_t store_mic_loopback(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int mic; + + sscanf(buf, "%i", &mic); + if (mic > 0) { + tlv320aic23_write_value(POWER_DOWN_CONTROL_ADDR, \ + OSC_OFF | ADC_OFF | LINE_OFF); + tlv320aic23_info_l.mask = STE_ENABLED | DAC_SELECTED \ + | INSEL_MIC | MICB_20DB; + tlv320aic23_write_value(ANALOG_AUDIO_CONTROL_ADDR, + tlv320aic23_info_l.mask); + mic = 1; + } + else { + tlv320aic23_write_value(POWER_DOWN_CONTROL_ADDR, \ + OSC_OFF | ADC_OFF | MIC_OFF | LINE_OFF); + mic = 0; + } + tlv320aic23_info_l.mic_loopback = mic; + + return count; +} + +static ssize_t show_mic_loopback(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%i\n", tlv320aic23_info_l.mic_loopback); +} + +static DEVICE_ATTR(mic_loopback, S_IRUGO | S_IWUSR, + show_mic_loopback, store_mic_loopback); + +static ssize_t store_st_attenuation(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + unsigned sta; + u16 tmp; + + sscanf(buf, "%u", &sta); + if (sta > 3) + sta = 3; + + tmp = tlv320aic23_info_l.mask; + tmp &= 0x3f; + + tlv320aic23_info_l.mask = tmp | STA_REG(sta); + tlv320aic23_write_value(ANALOG_AUDIO_CONTROL_ADDR, + tlv320aic23_info_l.mask); + tlv320aic23_info_l.sta = sta; + + return count; +} + +static ssize_t show_st_attenuation(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%i\n", tlv320aic23_info_l.sta); +} + +static DEVICE_ATTR(st_attenuation, S_IRUGO | S_IWUSR, + show_st_attenuation, store_st_attenuation); + +static ssize_t store_mic_enable(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int mic; + + sscanf(buf, "%i", &mic); + set_mic(mic); + + return count; +} + +static ssize_t show_mic_enable(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%i\n", tlv320aic23_info_l.mic_enable); +} + +static DEVICE_ATTR(mic_enable, S_IRUGO | S_IWUSR, + show_mic_enable, store_mic_enable); + +static ssize_t show_audio_selftest(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%i\n", selftest); +} + +static DEVICE_ATTR(audio_selftest, S_IRUGO | S_IWUSR, + show_audio_selftest, NULL); + +static int audio_i2c_probe(struct platform_device *dev) +{ + int r; + + if ((r = device_create_file(&dev->dev, &dev_attr_volume_left)) != 0) + return r; + else if ((r = device_create_file(&dev->dev, + &dev_attr_volume_right)) != 0) + goto err_volume_left; + else if ((r = device_create_file(&dev->dev, + &dev_attr_gain_right)) != 0) + goto err_volume_right; + else if ((r = device_create_file(&dev->dev, + &dev_attr_gain_left)) != 0) + goto err_gain_right; + else if ((r = device_create_file(&dev->dev, + &dev_attr_mic_loopback)) != 0) + goto err_gain_left; + else if ((r = device_create_file(&dev->dev, + &dev_attr_mic_enable)) != 0) + goto err_mic_loopback; + else if ((r = device_create_file(&dev->dev, + &dev_attr_st_attenuation)) != 0) + goto err_mic_enable; + else if ((r = device_create_file(&dev->dev, + &dev_attr_audio_selftest)) != 0) + goto err_st_attenuation; + else + return r; + +err_st_attenuation: + device_remove_file(&dev->dev, &dev_attr_st_attenuation); +err_mic_enable: + device_remove_file(&dev->dev, &dev_attr_mic_enable); +err_mic_loopback: + device_remove_file(&dev->dev, &dev_attr_mic_loopback); +err_gain_left: + device_remove_file(&dev->dev, &dev_attr_gain_left); +err_gain_right: + device_remove_file(&dev->dev, &dev_attr_gain_right); +err_volume_right: + device_remove_file(&dev->dev, &dev_attr_volume_right); +err_volume_left: + device_remove_file(&dev->dev, &dev_attr_volume_left); + + return r; +} + +static int audio_i2c_remove(struct platform_device *dev) +{ + device_remove_file(&dev->dev, &dev_attr_st_attenuation); + device_remove_file(&dev->dev, &dev_attr_mic_enable); + device_remove_file(&dev->dev, &dev_attr_mic_loopback); + device_remove_file(&dev->dev, &dev_attr_gain_left); + device_remove_file(&dev->dev, &dev_attr_gain_right); + device_remove_file(&dev->dev, &dev_attr_volume_right); + device_remove_file(&dev->dev, &dev_attr_volume_left); + + return 0; +} + +/*----------------------------------------------------------------*/ +/* PM functions */ +/*----------------------------------------------------------------*/ + +static void audio_i2c_shutdown(struct platform_device *dev) +{ + /* Let's mute the codec before powering off to prevent + * glitch in the sound + */ + tlv320aic23_write_value(LEFT_CHANNEL_VOLUME_ADDR, LHV_MIN); + tlv320aic23_write_value(RIGHT_CHANNEL_VOLUME_ADDR, LHV_MIN); + tlv320aic23_power_down(); +} + +static int audio_i2c_suspend(struct platform_device *dev, pm_message_t state) +{ + /* Let's mute the codec before powering off to prevent + * glitch in the sound + */ + tlv320aic23_write_value(LEFT_CHANNEL_VOLUME_ADDR, LHV_MIN); + tlv320aic23_write_value(RIGHT_CHANNEL_VOLUME_ADDR, LHV_MIN); + tlv320aic23_power_down(); + + return 0; +} + +static int audio_i2c_resume(struct platform_device *dev) +{ + tlv320aic23_power_up(); + + return 0; +} + +static struct platform_driver audio_i2c_driver = { + .driver { + .owner = THIS_MODULE, + .name = "audio-i2c", + }, + .shutdown = audio_i2c_shutdown, + .probe = audio_i2c_probe, + .remove = audio_i2c_remove, + .suspend = audio_i2c_suspend, + .resume = audio_i2c_resume, +}; + +static struct platform_device audio_i2c_device = { + .name = "audio-i2c", + .id = -1, +}; + +/*----------------------------------------------------------------*/ + +static int __init tlv320aic23_init(void) +{ + selftest = 0; + tlv320aic23_info_l.initialization = 0; + + if (i2c_add_driver(&tlv320aic23_driver)) { + printk("tlv320aic23 i2c: Driver registration failed, \ + module not inserted.\n"); + selftest= -ENODEV; + return selftest; + } + + if (platform_driver_register(&audio_i2c_driver)) { + printk(KERN_WARNING "Failed to register audio i2c driver\n"); + selftest = -ENODEV; + return selftest; + } + + if (platform_device_register(&audio_i2c_device)) { + printk(KERN_WARNING "Failed to register audio i2c device\n"); + platform_driver_unregister(&audio_i2c_driver); + selftest = -ENODEV; + return selftest; + } + omap_mcbsp3_tlv320aic23_clock_init(); + tlv320aic23_power_up(); + tlv320aic23_info_l.initialization = 1; + printk("TLV320AIC23 I2C version %s (%s)\n", + TLV320AIC23_VERSION, TLV320AIC23_DATE); + + return selftest; +} + +static void __exit tlv320aic23_exit(void) +{ + int res; + + tlv320aic23_power_down(); + if ((res = i2c_del_driver(&tlv320aic23_driver))) + printk("tlv320aic23 i2c: Driver remove failed, module not removed.\n"); + + platform_device_unregister(&audio_i2c_device); + platform_driver_unregister(&audio_i2c_driver); +} + +MODULE_AUTHOR("Kai Svahn "); +MODULE_DESCRIPTION("I2C interface for TLV320AIC23 codec."); +MODULE_LICENSE("GPL"); + +module_init(tlv320aic23_init) +module_exit(tlv320aic23_exit) + +EXPORT_SYMBOL(tlv320aic23_write_value); +EXPORT_SYMBOL(tlv320aic23_power_up); +EXPORT_SYMBOL(tlv320aic23_power_down); diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c index 1af3dfbb808..ba4a0acf279 100644 --- a/drivers/i2c/chips/tps65010.c +++ b/drivers/i2c/chips/tps65010.c @@ -18,6 +18,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#undef DEBUG #include #include @@ -43,13 +44,16 @@ /*-------------------------------------------------------------------------*/ #define DRIVER_VERSION "2 May 2005" -#define DRIVER_NAME (tps65010_driver.name) +#define DRIVER_NAME (tps65010_driver.driver.name) MODULE_DESCRIPTION("TPS6501x Power Management Driver"); MODULE_LICENSE("GPL"); -static unsigned short normal_i2c[] = { 0x48, /* 0x49, */ I2C_CLIENT_END }; -static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +/* only two addresses possible */ +#define TPS_BASE 0x48 +static unsigned short normal_i2c[] = { + TPS_BASE, + I2C_CLIENT_END }; I2C_CLIENT_INSMOD; @@ -83,6 +87,7 @@ struct tps65010 { struct i2c_client client; struct semaphore lock; int irq; + int irq_type; struct work_struct work; struct dentry *file; unsigned charging:1; @@ -97,7 +102,7 @@ struct tps65010 { u8 chgstatus, regstatus, chgconf; u8 nmask1, nmask2; - /* not currently tracking GPIO state */ + /* plus four GPIOs, probably used to switch power */ }; #define POWER_POLL_DELAY msecs_to_jiffies(800) @@ -130,7 +135,7 @@ static void dbg_regstat(char *buf, size_t len, u8 regstatus) (regstatus & TPS_REG_COVER) ? " uncover" : "", (regstatus & TPS_REG_UVLO) ? " UVLO" : "", (regstatus & TPS_REG_NO_CHG) ? " NO_CHG" : "", - (regstatus & TPS_REG_PG_LD02) ? " ld02_bad" : "", + (regstatus & TPS_REG_PG_LD02) ? " ld01_bad" : "", (regstatus & TPS_REG_PG_LD01) ? " ld01_bad" : "", (regstatus & TPS_REG_PG_MAIN) ? " main_bad" : "", (regstatus & TPS_REG_PG_CORE) ? " core_bad" : ""); @@ -138,7 +143,7 @@ static void dbg_regstat(char *buf, size_t len, u8 regstatus) static void dbg_chgconf(int por, char *buf, size_t len, u8 chgconfig) { - const char *hibit; + char *hibit; if (por) hibit = (chgconfig & TPS_CHARGE_POR) @@ -290,7 +295,7 @@ static int dbg_show(struct seq_file *s, void *_) seq_printf(s, "defgpio %02x mask3 %02x\n", value, v2); for (i = 0; i < 4; i++) { - if (value & (1 << (4 + i))) + if (value & (1 << (4 +i))) seq_printf(s, " gpio%d-out %s\n", i + 1, (value & (1 << i)) ? "low" : "hi "); else @@ -476,7 +481,7 @@ static int __exit tps65010_detach_client(struct i2c_client *client) debugfs_remove(tps->file); if (i2c_detach_client(client) == 0) kfree(tps); - the_tps = NULL; + the_tps = 0; return 0; } @@ -494,7 +499,6 @@ tps65010_probe(struct i2c_adapter *bus, int address, int kind) { struct tps65010 *tps; int status; - unsigned long irqflags; if (the_tps) { dev_dbg(&bus->dev, "only one %s for now\n", DRIVER_NAME); @@ -509,6 +513,7 @@ tps65010_probe(struct i2c_adapter *bus, int address, int kind) INIT_WORK(&tps->work, tps65010_work, tps); tps->irq = -1; tps->client.addr = address; + i2c_set_clientdata(&tps->client, tps); tps->client.adapter = bus; tps->client.driver = &tps65010_driver; strlcpy(tps->client.name, DRIVER_NAME, I2C_NAME_SIZE); @@ -517,18 +522,21 @@ tps65010_probe(struct i2c_adapter *bus, int address, int kind) if (status < 0) { dev_dbg(&bus->dev, "can't attach %s to device %d, err %d\n", DRIVER_NAME, address, status); - goto fail1; +fail1: + kfree(tps); + return 0; } + tps->irq_type = 0; + #ifdef CONFIG_ARM - irqflags = SA_SAMPLE_RANDOM | SA_TRIGGER_LOW; if (machine_is_omap_h2()) { tps->model = TPS65010; omap_cfg_reg(W4_GPIO58); tps->irq = OMAP_GPIO_IRQ(58); omap_request_gpio(58); omap_set_gpio_direction(58, 1); - irqflags |= SA_TRIGGER_FALLING; + tps->irq_type = SA_TRIGGER_FALLING; } if (machine_is_omap_osk()) { tps->model = TPS65010; @@ -536,20 +544,18 @@ tps65010_probe(struct i2c_adapter *bus, int address, int kind) tps->irq = OMAP_GPIO_IRQ(OMAP_MPUIO(1)); omap_request_gpio(OMAP_MPUIO(1)); omap_set_gpio_direction(OMAP_MPUIO(1), 1); - irqflags |= SA_TRIGGER_FALLING; + tps->irq_type = SA_TRIGGER_FALLING; } if (machine_is_omap_h3()) { tps->model = TPS65013; // FIXME set up this board's IRQ ... } -#else - irqflags = SA_SAMPLE_RANDOM; #endif if (tps->irq > 0) { status = request_irq(tps->irq, tps65010_irq, - irqflags, DRIVER_NAME, tps); + tps->irq_type, DRIVER_NAME, tps); if (status < 0) { dev_dbg(&tps->client.dev, "can't get IRQ %d, err %d\n", tps->irq, status); @@ -625,9 +631,6 @@ tps65010_probe(struct i2c_adapter *bus, int address, int kind) tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL, tps, DEBUG_FOPS); return 0; -fail1: - kfree(tps); - return 0; } static int __init tps65010_scan_bus(struct i2c_adapter *bus) @@ -639,8 +642,9 @@ static int __init tps65010_scan_bus(struct i2c_adapter *bus) static struct i2c_driver tps65010_driver = { .driver = { - .name = "tps65010", + .name = "tps65010", }, + .id = 888, /* FIXME assign "official" value */ .attach_adapter = tps65010_scan_bus, .detach_client = __exit_p(tps65010_detach_client), }; @@ -694,14 +698,14 @@ int tps65010_set_gpio_out_value(unsigned gpio, unsigned value) return -ENODEV; if ((gpio < GPIO1) || (gpio > GPIO4)) return -EINVAL; - + down(&the_tps->lock); defgpio = i2c_smbus_read_byte_data(&the_tps->client, TPS_DEFGPIO); /* Configure GPIO for output */ defgpio |= 1 << (gpio + 3); - + /* Writing 1 forces a logic 0 on that GPIO and vice versa */ switch (value) { case LOW: @@ -712,14 +716,14 @@ int tps65010_set_gpio_out_value(unsigned gpio, unsigned value) defgpio &= ~(1 << (gpio - 1)); /* set GPIO high by writing 0 */ break; } - + status = i2c_smbus_write_byte_data(&the_tps->client, TPS_DEFGPIO, defgpio); pr_debug("%s: gpio%dout = %s, defgpio 0x%02x\n", DRIVER_NAME, gpio, value ? "high" : "low", i2c_smbus_read_byte_data(&the_tps->client, TPS_DEFGPIO)); - + up(&the_tps->lock); return status; } @@ -738,7 +742,7 @@ int tps65010_set_led(unsigned led, unsigned mode) if (!the_tps) return -ENODEV; - if (led == LED1) + if(led == LED1) offs = 0; else { offs = 2; @@ -747,14 +751,12 @@ int tps65010_set_led(unsigned led, unsigned mode) down(&the_tps->lock); - pr_debug("%s: led%i_on 0x%02x\n", DRIVER_NAME, led, - i2c_smbus_read_byte_data(&the_tps->client, - TPS_LED1_ON + offs)); - - pr_debug("%s: led%i_per 0x%02x\n", DRIVER_NAME, led, - i2c_smbus_read_byte_data(&the_tps->client, - TPS_LED1_PER + offs)); + dev_dbg (&the_tps->client.dev, "led%i_on 0x%02x\n", led, + i2c_smbus_read_byte_data(&the_tps->client, TPS_LED1_ON + offs)); + dev_dbg (&the_tps->client.dev, "led%i_per 0x%02x\n", led, + i2c_smbus_read_byte_data(&the_tps->client, TPS_LED1_PER + offs)); + switch (mode) { case OFF: led_on = 1 << 7; @@ -769,7 +771,7 @@ int tps65010_set_led(unsigned led, unsigned mode) led_per = 0x08 | (1 << 7); break; default: - printk(KERN_ERR "%s: Wrong mode parameter for set_led()\n", + printk(KERN_ERR "%s: Wrong mode parameter for tps65010_set_led()\n", DRIVER_NAME); up(&the_tps->lock); return -EINVAL; @@ -779,28 +781,27 @@ int tps65010_set_led(unsigned led, unsigned mode) TPS_LED1_ON + offs, led_on); if (status != 0) { - printk(KERN_ERR "%s: Failed to write led%i_on register\n", + printk(KERN_ERR "%s: Failed to write led%i_on register\n", DRIVER_NAME, led); up(&the_tps->lock); return status; - } + } - pr_debug("%s: led%i_on 0x%02x\n", DRIVER_NAME, led, + dev_dbg (&the_tps->client.dev, "led%i_on 0x%02x\n", led, i2c_smbus_read_byte_data(&the_tps->client, TPS_LED1_ON + offs)); status = i2c_smbus_write_byte_data(&the_tps->client, TPS_LED1_PER + offs, led_per); if (status != 0) { - printk(KERN_ERR "%s: Failed to write led%i_per register\n", + printk(KERN_ERR "%s: Failed to write led%i_per register\n", DRIVER_NAME, led); up(&the_tps->lock); return status; } - pr_debug("%s: led%i_per 0x%02x\n", DRIVER_NAME, led, - i2c_smbus_read_byte_data(&the_tps->client, - TPS_LED1_PER + offs)); + dev_dbg (&the_tps->client.dev, "led%i_per 0x%02x\n", led, + i2c_smbus_read_byte_data(&the_tps->client, TPS_LED1_PER + offs)); up(&the_tps->lock); @@ -855,7 +856,7 @@ int tps65010_set_low_pwr(unsigned mode) i2c_smbus_read_byte_data(&the_tps->client, TPS_VDCDC1)); vdcdc1 = i2c_smbus_read_byte_data(&the_tps->client, TPS_VDCDC1); - + switch (mode) { case OFF: vdcdc1 &= ~TPS_ENABLE_LP; /* disable ENABLE_LP bit */ @@ -870,8 +871,8 @@ int tps65010_set_low_pwr(unsigned mode) TPS_VDCDC1, vdcdc1); if (status != 0) - printk(KERN_ERR "%s: Failed to write vdcdc1 register\n", - DRIVER_NAME); + printk(KERN_ERR "%s: Failed to write vdcdc1 register\n", + DRIVER_NAME); else pr_debug("%s: vdcdc1 0x%02x\n", DRIVER_NAME, i2c_smbus_read_byte_data(&the_tps->client, TPS_VDCDC1)); @@ -896,15 +897,15 @@ int tps65010_config_vregs1(unsigned value) down(&the_tps->lock); - pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME, - i2c_smbus_read_byte_data(&the_tps->client, TPS_VREGS1)); + pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME, + i2c_smbus_read_byte_data(&the_tps->client, TPS_VREGS1)); status = i2c_smbus_write_byte_data(&the_tps->client, TPS_VREGS1, value); if (status != 0) - printk(KERN_ERR "%s: Failed to write vregs1 register\n", - DRIVER_NAME); + printk(KERN_ERR "%s: Failed to write vregs1 register\n", + DRIVER_NAME); else pr_debug("%s: vregs1 0x%02x\n", DRIVER_NAME, i2c_smbus_read_byte_data(&the_tps->client, TPS_VREGS1)); @@ -1006,7 +1007,7 @@ static int __init tps_init(void) msleep(10); } -#ifdef CONFIG_ARM +#if defined(CONFIG_ARM) if (machine_is_omap_osk()) { // FIXME: More should be placed in the initialization code @@ -1046,8 +1047,8 @@ static int __init tps_init(void) } else if (machine_is_omap_h3()) { /* gpio4 for SD, gpio3 for VDD_DSP */ #ifdef CONFIG_PM - /* Enable LOW_PWR */ - tps65013_set_low_pwr(ON); + /* FIXME: Enable LOW_PWR hangs H3 */ + //tps65013_set_low_pwr(ON); #endif } #endif diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index 3b0ac3b43c5..1286eac3dfd 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -183,4 +183,24 @@ config KEYBOARD_HIL This driver implements support for HIL-keyboards attached to your machine, so normally you should say Y here. + +config KEYBOARD_OMAP + tristate "TI OMAP keypad support" + depends on (ARCH_OMAP1 || ARCH_OMAP2) + help + Say Y here if you want to use the OMAP keypad. + + To compile this driver as a module, choose M here: the + module will be called omap-keypad. + +config OMAP_PS2 + tristate "TI OMAP Innovator 1510 PS/2 keyboard & mouse support" + depends on ARCH_OMAP15XX && MACH_OMAP_INNOVATOR + help + Say Y here if you want to use the OMAP Innovator 1510 PS/2 + keyboard and mouse. + + To compile this driver as a module, choose M here: the + module will be called innovator_ps2. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index 2708167ba17..545268a62e8 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -15,4 +15,5 @@ obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o - +obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o +obj-$(CONFIG_OMAP_PS2) += innovator_ps2.o diff --git a/drivers/input/keyboard/innovator_ps2.c b/drivers/input/keyboard/innovator_ps2.c new file mode 100644 index 00000000000..c0889dcd5b9 --- /dev/null +++ b/drivers/input/keyboard/innovator_ps2.c @@ -0,0 +1,1291 @@ +/* + * drivers/char/innovator_ps2.c + * + * Basic PS/2 keyboard/mouse driver for the Juno® USAR HID controller + * present on the TI Innovator/OMAP1510 Break-out-board. + * + * + * Author: MontaVista Software, Inc. + * or + * + * + * 2003 (c) MontaVista Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + * + * + * REFERENCES: + * + * 1. Technical Reference Manual + * Juno® 01 + * Multi-function ICs family + * UR8HC007-001 HID & Power management controller + * Document Number: DOC8-007-001-TR-075 + * Date: February 2002 + * Copyright ©1998-2002 Semtech Corporation + * http://www.semtech.com/pdf/doc8-007-001-tr.pdf + * + * 2. Juno® 01 UR8HC007-001 Data Sheet + * Extremely Low-power Input Device and Power Management IC + * Copyright ©1998-2002 Semtech Corporation + * DOC8-007-001-DS-112 + * http://www.semtech.com/pdf/doc8-007-001-ds.pdf + * + * + * HISTORY: + * + * 20030626: George G. Davis + * Initially based on the following RidgeRun DSPlinux Version 1.6 files: + * linux-2.4.15-rmk1-dsplinux/arch/arm/dsplinux/hid/omap1510_hid.c + * linux-2.4.15-rmk1-dsplinux/arch/arm/dsplinux/hid/omap1510_hid.h + * linux-2.4.15-rmk1-dsplinux/arch/arm/dsplinux/hid/omap1510_ps2.c + * linux-2.4.15-rmk1-dsplinux/arch/arm/dsplinux/hid/omap1510_spi.c + * All original files above are + * Copyright (C) 2001 RidgeRun, Inc. + * Author: Alex McMains + * + * 20040812: Thiago Radicchi + * Cleanup of old code from 2.4 driver and some debug code. + * Minor changes in interrupt handling code. + * + * NOTES: + * + * 1. This driver does not provide support for setting keyboard/mouse + * configuration parameters. Both devices are managed directly by + * the Juno UR8HC007-001 on behalf of the host. This minimises the + * amount of host processing required to manage HID events and state + * changes, e.g. both keyboard and mouse devices are hot pluggable + * with no host intervention required. However, we cannot customise + * keyboard/mouse settings in this case. So we live with the defaults + * as setup by the Juno UR8HC007-001 whatever they may be. + * 2. Keyboard auto repeat does not work. See 1 above. : ) + * + * + * TODO: + * + * 1. Complete DPM/LDM stubs and test. + * 2. Add SPI error handling support, i.e. resend, etc.,. + * 3. Determine why innovator_hid_interrupt() is called for every + * invocation of Innovator FPGA IRQ demux. It appears that the + * missed Innovator ethernet workaround may be to blame. However, + * it does not adversely affect operation of this driver since we + * check for assertion of ATN prior to servicing the interrupt. If + * ATN is negated, we bug out right away. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#undef INNOVATOR_KEYB_DEBUG +#ifdef INNOVATOR_KEYB_DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG "%s:%d: " format , \ + __FUNCTION__ , __LINE__ , ## arg) +#define entry() printk(KERN_DEBUG "%s:%d: Entry\n" , __FUNCTION__ , __LINE__) +#define exit() printk(KERN_DEBUG "%s:%d: Exit\n" , __FUNCTION__ , __LINE__) +#define dump_packet(p, n) \ + { \ + int i; \ + printk(KERN_DEBUG "%s:%d: %08x:" , \ + __FUNCTION__ , __LINE__ , (int) p); \ + for (i = 0; i < n; i += 1) { \ + printk(" %02x", (int) p[i]); \ + } \ + printk("\n"); \ + } +#else +#define dbg(format, arg...) do {} while (0) +#define entry() do {} while (0) +#define exit() do {} while (0) +#define dump_packet(p, n) do {} while (0) +#endif + + +#define PFX "innovator_ps2" +#define err(format, arg...) printk(KERN_ERR PFX ": " format , ## arg) +#define info(format, arg...) printk(KERN_INFO PFX ": " format , ## arg) +#define warn(format, arg...) printk(KERN_WARNING PFX ": " format , ## arg) + + +/****************************************************************************/ + +/* + * Synchronous communications timing parameters (Reference [1] pg 7-7) + */ + +#define tMSA 5000 /* -/5ms _SS to _ATN (master transfer) */ +#define tMAC 100 /* 100us/5ms _ATN to first clock pulse (master + transfer) */ +#define tMIB 150 /* 150us/5ms Beginning of byte transfer to beginning + of next byte transfer */ +#define tSIB 150 /* 150us/5ms Beginning of byte transfer to beginning + of next byte transfer */ +#define tMSP 100 /* -/100us Last clock pulse of packet to _SS + de-assertion */ +#define tMNSA 100 /* -/100us _SS de-assertion to _ATN de-assertion */ +#define tMNEXT 120 /* 120uS/- _ATN release to _SS re-assertion + (master transfer) */ +#define tSAS 5000 /* -/5ms _ATN to _SS (slave transfer) */ +#define tSSC 100 /* 100us/5ms _SS to first clock pulse (slave + transfer) */ +#define tSNA 100 /* -/100us Last clock pulse of packet to _ATN + de-assertion */ +#define tSNAS 100 /* -/100us _ATN release to _SS de-assertion */ +#define tSNEXT 120 /* 120us/- _SS release to _ATN re-assertion + (slave transfer) */ +#define tSCK 4 /* 4us/- Clock period */ +#define tSLOW 2 /* 2us/- Clock LOW period */ +#define tHOLD 200 /* 200ns/- Master data hold time */ +#define tSETUP 100 /* 100ns/- Master data setup Time */ +#define tSSETUP 500 /* -/500ns Slave data setup time from clock + falling edge */ + + +/* + * Protocol Headers (Reference [1], pg. 5-1): + */ + + +/* Protocols used in commands issued by the host: */ +#define SIMPLE 0x80 /* Simple commands + * Common for both host and controller + * protocol headers. + */ +#define WRITE_REGISTER_BIT 0x81 /* Write register bit */ +#define READ_REGISTER_BIT 0x82 /* Read register bit */ +#define WRITE_REGISTER 0x83 /* Write register */ +#define READ_REGISTER 0x84 /* Read register */ +#define WRITE_BLOCK 0x85 /* Write block */ +#define READ_BLOCK 0x86 /* Read block */ + + +/* Protocols used in responses, reports and alerts issued by the controller: */ +#define REPORT_REGISTER_BIT 0x81 /* Report register bit & event alerts */ +#define REPORT_REGISTER 0x83 /* Report register */ +#define REPORT_BLOCK 0x85 /* Report block */ +#define POINTING_REPORT 0x87 /* Pointing device data report */ +#define KEYBOARD_REPORT 0x88 /* Keyboard device data report */ + + +/* Simple Commands (Reference [1], pg 5-3): */ +#define INITIALIZE 0x00 /* Forces the recipient to enter the + * known default power-on state. + */ +#define INITIALIZATION_COMPLETE 0x01 /* Issued as a hand-shake response only + * to the "Initialize" command. + */ +#define RESEND_REQUEST 0x05 /* Issued upon error in the reception + * of a package. The recipient resends + * the last transmitted packet. + */ + +/* Register offsets (Reference [1], pg 6-1 thru 6-9): */ + +#define REG_PM_COMM 0 +#define REG_PM_STATUS 1 +#define REG_PAGENO 255 + +/* Power management bits ((Reference [1], pg 6-10): */ + +#define SUS_STATE 0x2 /* in REG_PM_COMM */ + +/* Miscellaneous constants: */ + +#define X_MSB_SHIFT (8-4) +#define X_MSB_MASK (3<<4) +#define Y_MSB_SHIFT (8-6) +#define Y_MSB_MASK (3<<6) + + +#define JUNO_BLOCK_SIZE 32 +#define JUNO_BUFFER_SIZE 256 + + +/* + * Errors: + */ + +#define E_BAD_HEADER 1 +#define E_BAD_LRC 2 +#define E_ZERO_BYTES 3 +#define E_BAD_VALUE 4 +#define E_BAD_MODE 5 +#define E_REPORT_MODE 6 +#define E_BAD_ACK 7 +#define E_BAD_DEVICE_ID 8 +#define E_PKT_SZ 9 + + +/* + * Host/Controller Command/Response Formats: + */ + +typedef struct _simple_t { + u8 header; + u8 cmd_code; + u8 LRC; +} __attribute__ ((packed)) simple_t; + +typedef struct _write_bit_t { + u8 header; + u8 offset; + u8 value_bit; + u8 LRC; +} __attribute__ ((packed)) write_bit_t; + +typedef struct _read_bit_t { + u8 header; + u8 offset; + u8 bit; + u8 LRC; +} __attribute__ ((packed)) read_bit_t; + +typedef struct _write_reg_t { + u8 header; + u8 offset; + u8 value; + u8 LRC; +} __attribute__ ((packed)) write_reg_t; + +typedef struct _read_reg_t { + u8 header; + u8 offset; + u8 LRC; +} __attribute__ ((packed)) read_reg_t; + +typedef struct _write_block_t { + u8 header; + u8 offset; + u8 length; + u8 block[JUNO_BLOCK_SIZE + 1]; /* Hack: LRC is last element of block[] */ +} __attribute__ ((packed)) write_block_t; + +typedef struct _read_block_t { + u8 header; + u8 offset; + u8 length; + u8 LRC; +} __attribute__ ((packed)) read_block_t; + +typedef struct _report_bit_t { + u8 header; + u8 offset; + u8 value_bit; + u8 LRC; +} __attribute__ ((packed)) report_bit_t; + +typedef struct _report_reg_t { + u8 header; + u8 offset; + u8 value; + u8 LRC; +} __attribute__ ((packed)) report_reg_t; + +typedef struct _report_block_t { + u8 header; + u8 offset; + u8 length; + u8 block[32]; + u8 LRC; +} __attribute__ ((packed)) report_block_t; + +typedef struct _mse_report_t { + u8 header; + u8 buttons; + u8 Xdisplacement; + u8 Ydisplacement; + u8 Zdisplacement; + u8 LRC; +} __attribute__ ((packed)) mse_report_t; + +typedef struct _kdb_report_t { + u8 header; + u8 keynum; /* up > 0x80, down < 0x7E, all keys up 0x00 */ + u8 LRC; +} __attribute__ ((packed)) kdb_report_t; + + +static u8 buffer[JUNO_BUFFER_SIZE]; +static u8 block[JUNO_BLOCK_SIZE]; + +static void do_hid_tasklet(unsigned long); +DECLARE_TASKLET(hid_tasklet, do_hid_tasklet, 0); +static struct innovator_hid_dev *hid; +static spinlock_t innovator_fpga_hid_lock = SPIN_LOCK_UNLOCKED; +static atomic_t innovator_fpga_hid_busy = ATOMIC_INIT(0); + +struct innovator_hid_dev { + struct input_dev mouse, keyboard; + int open; + int irq_enabled; +}; + +/****************************************************************************/ + +/* + * Low-level TI Innovator/OMAP1510 FPGA HID SPI interface helper functions: + */ + +static u8 +innovator_fpga_hid_rd(void) +{ + u8 val = inb(INNOVATOR_FPGA_HID_SPI); + return val; +} + +static void +innovator_fpga_hid_wr(u8 val) +{ + outb(val, INNOVATOR_FPGA_HID_SPI); +} + +static void +innovator_fpga_hid_frob(u8 mask, u8 val) +{ + unsigned long flags; + local_irq_save(flags); + innovator_fpga_hid_wr((innovator_fpga_hid_rd() & ~mask) | val); + local_irq_restore(flags); +} + +static void +innovator_fpga_hid_set_bits(u8 x) +{ + innovator_fpga_hid_frob(x, x); +} + +static void +innovator_fpga_hid_clear_bits(u8 x) +{ + innovator_fpga_hid_frob(x, 0); +} + +static void +SS(int value) +{ + innovator_fpga_hid_frob(OMAP1510_FPGA_HID_nSS, value ? OMAP1510_FPGA_HID_nSS : 0); +} + +static void +SCLK(int value) +{ + innovator_fpga_hid_frob(OMAP1510_FPGA_HID_SCLK, value ? OMAP1510_FPGA_HID_SCLK : 0); +} + +static void +MOSI(int value) +{ + innovator_fpga_hid_frob(OMAP1510_FPGA_HID_MOSI, value ? OMAP1510_FPGA_HID_MOSI : 0); +} + +static u8 +MISO(void) +{ + return ((innovator_fpga_hid_rd() & OMAP1510_FPGA_HID_MISO) ? 1 : 0); +} + +static u8 +ATN(void) +{ + return ((innovator_fpga_hid_rd() & OMAP1510_FPGA_HID_ATN) ? 1 : 0); +} + +static int +wait_for_ATN(int assert, int timeout) +{ + do { + if (ATN() == assert) + return 0; + udelay(1); + } while (timeout -= 1); + return -1; +} + +static u8 +innovator_fpga_hid_xfer_byte(u8 xbyte) +{ + int i; + u8 rbyte; + + for (rbyte = 0, i = 7; i >= 0; i -= 1) { + SCLK(0); + MOSI((xbyte >> i) & 1); + udelay(tSLOW); + SCLK(1); + rbyte = (rbyte << 1) | MISO(); + udelay(tSLOW); + } + + return rbyte; +} + +static void +innovator_fpga_hid_reset(void) +{ + innovator_fpga_hid_wr(OMAP1510_FPGA_HID_SCLK | OMAP1510_FPGA_HID_MOSI); + mdelay(1); + innovator_fpga_hid_set_bits(OMAP1510_FPGA_HID_RESETn); +} + + +/***************************************************************************** + + Refer to Reference [1], Chapter 7 / Low-level communications, Serial + Peripheral Interface (SPI) implementation Host (master) packet + transmission timing, pg. 7-3, for timing and implementation details + for spi_xmt(). + + *****************************************************************************/ + +int +spi_xmt(u8 * p, u8 n) +{ + unsigned long flags; + + dump_packet(p, n); + local_irq_save(flags); + disable_irq(OMAP1510_INT_FPGA_ATN); + + if (ATN()) { + /* Oops, we have a collision. */ + enable_irq(OMAP1510_INT_FPGA_ATN); + local_irq_restore(flags); + dbg("Protocol error: ATN is asserted\n"); + return -EAGAIN; + } + + SS(1); + + if (wait_for_ATN(1, tMSA) < 0) { + SS(0); + enable_irq(OMAP1510_INT_FPGA_ATN); + local_irq_restore(flags); + dbg("timeout waiting for ATN assertion\n"); + return -EREMOTEIO; + } + + udelay(tMAC); + + while (n--) { + innovator_fpga_hid_xfer_byte(*p++); + if (n) { + udelay(tMIB - 8 * tSCK); + } + } + + MOSI(1); /* Set MOSI to idle high. */ + + /* NOTE: The data sheet does not specify a minimum delay + * here. But innovator_fpga_hid_xfer_byte() gives us a half-clock + * delay (tSLOW) after the last bit is sent. So I'm happy with + * that. + */ + + SS(0); + + if (wait_for_ATN(0, tMNSA) < 0) { + enable_irq(OMAP1510_INT_FPGA_ATN); + local_irq_restore(flags); + dbg("timeout waiting for ATN negation\n"); + return -EREMOTEIO; + } + + udelay(tMNEXT); + enable_irq(OMAP1510_INT_FPGA_ATN); + local_irq_restore(flags); + return 0; +} + + +/***************************************************************************** + + Refer to Reference [1], Chapter 7 / Low-level communications, Serial + Peripheral Interface (SPI) implementation, Slave packet transmission + timing, pg. 7-5, for timing and implementation details for spi_rcv(). + + *****************************************************************************/ + +int +spi_rcv(u8 * p, int len) +{ + unsigned long flags; + int ret = 0; + + if (len > 256) { + /* Limit packet size to something reasonable */ + return -1; + } + + local_irq_save(flags); + + if (wait_for_ATN(1, tMSA) < 0) { + local_irq_restore(flags); + dbg("Protocol error: ATN is not asserted\n"); + return -EREMOTEIO; + } + + SS(1); + + udelay(tSSC); + + while (ATN()) { + if (ret >= len) { + err("over run error\n"); + ret = -1; + break; + } + p[ret++] = innovator_fpga_hid_xfer_byte(0xff); + udelay(tSNA); /* Wait long enough to detect negation of ATN + * after last clock pulse of packet. + * + * NOTE: Normally, we need a minimum delay of + * tSIB between the start of one byte + * and the start of the next. However, + * we also need to wait long enough + * for the USAR to negate ATN before + * starting the next byte. So we use + * max(tSIB - 8 * tSCK, tSNA) here to + * satisfy both constraints. + */ + } + + SS(0); /* NOTE: The data sheet does not specify a minimum delay + * here. But innovator_fpga_hid_xfer_byte() gives us a + * half-clock delay (tSLOW) after the last bit is sent. So + * I'm happy with that (rather than no delay at all : ). + */ + + + udelay(tSNEXT); /* This isn't quite right. Assertion of ATN after + * negation of SS is an USAR timing constraint. + * What we need here is a spec for the minimum + * delay from SS negation to SS assertion. But + * for now, just use this brain dead delay. + */ + + local_irq_restore(flags); + + if (ret > 0) { + dump_packet(p, ret); + } + + return ret; +} + + +/***************************************************************************** + Calculate Host/Controller Command/Response Longitudinal Redundancy Check (LRC) + + The algorithm implemented in calculate_LRC() below is taken directly from + the reference [1], Chapter 7 / Low-level communications, LRC (Longitudinal + Redundancy Check), pg 5-10. + + *****************************************************************************/ + +static u8 +calculate_LRC(u8 * p, int n) +{ + u8 LRC; + int i; + + /* + * Init the LRC using the first two message bytes. + */ + LRC = p[0] ^ p[1]; + + /* + * Update the LRC using the remainder of the p. + */ + for (i = 2; i < n; i++) + LRC ^= p[i]; + + /* + * If the MSB is set then clear the MSB and change the next + * most significant bit + */ + if (LRC & 0x80) + LRC ^= 0xC0; + + return LRC; +} + + +/* + * Controller response helper functions: + */ + +static inline int +report_mouse(mse_report_t * p, int n) +{ + if (p->header != POINTING_REPORT) + return -E_BAD_HEADER; + + if (n != sizeof(mse_report_t)) + return -E_PKT_SZ; + + return (p->LRC != calculate_LRC((u8 *) p, sizeof(mse_report_t) - 1)) ? + -E_BAD_LRC : POINTING_REPORT; +} + +static inline int +report_keyboard(kdb_report_t * p, int n) +{ + if (p->header != KEYBOARD_REPORT) + return -E_BAD_HEADER; + + if (n != sizeof(kdb_report_t)) + return -E_PKT_SZ; + + return (p->LRC != calculate_LRC((u8 *) p, sizeof(kdb_report_t) - 1)) ? + -E_BAD_LRC : KEYBOARD_REPORT; +} + + +/* + * Miscellaneous helper functions: + */ + +static inline int +report_type(u8 * type) +{ + /* check the header to find out what kind of report it is */ + if ((*type) == KEYBOARD_REPORT) + return KEYBOARD_REPORT; + else if ((*type) == POINTING_REPORT) + return POINTING_REPORT; + else + return -E_BAD_HEADER; +} + +static inline int +report_async(void * p, int n) +{ + int ret; + + if ((ret = spi_rcv((u8 *) p, n)) < 0) + return ret; + + if (report_type((u8 *) p) == POINTING_REPORT) + ret = report_mouse((mse_report_t *) p, ret); + else if (report_type((u8 *) p) == KEYBOARD_REPORT) + ret = report_keyboard((kdb_report_t *) p, ret); + + return ret; +} + +static int +verify_init(u8 * p) +{ + return (((simple_t *)p)->cmd_code == 0x01) ? 0 : -1; +} + + +/* + * Host command helper functions: + */ + +#if 0 +/* REVISIT/TODO: Wrapper for command/response with resend handing. */ +static int +spi_xfer(u8 * optr, u8 osz, u8 * iptr, u8 isz) +{ + static u8 buf[256]; + int ret; + int xretries = 3; + + do { + if (optr != NULL && osz) { + do { + ret = spi_xmt((u8 *) optr, osz); + } while (ret < 0); + } + + ret = spi_rcv((u8 *) buf, 256); + + if (ret == -EREMOTEIO) { + if (iptr == NULL) { + break; + } + } + } while (xretries--); + + return ret; +} +#endif + +/* REVISIT: Enable these when/if additional Juno features are required. */ +static inline int +simple(u8 cmd) +{ + static simple_t p; + int ret; + + p.header = SIMPLE; + p.cmd_code = cmd; + p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1); + + if ((ret = spi_xmt((u8 *) & p, sizeof(p))) < 0) + return ret; + + if ((ret = spi_rcv((u8 *) & p, sizeof(p))) < 0) + return ret; + + if (ret == 0) + return -E_ZERO_BYTES; + + if (ret != sizeof(p)) + return -E_PKT_SZ; + + if (p.header != SIMPLE) + return -E_BAD_HEADER; + + if (p.LRC != calculate_LRC((u8 *) & p, sizeof(p) - 1)) + return -E_BAD_LRC; + + /* REVISIT: Need to check or return response code here? */ +} + +static inline int +write_bit(u8 offset, u8 bit, u8 value) +{ + static write_bit_t p; + + p.header = WRITE_REGISTER_BIT; + p.offset = offset; + p.value_bit = (bit << 1) | (value & 1); + p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1); + + return spi_xmt((u8 *) & p, sizeof(p)); +} + +static inline int +read_bit(u8 offset, u8 bit, u8 * data) +{ + static read_bit_t p; + static report_bit_t q; + int ret; + + p.header = READ_REGISTER_BIT; + p.offset = offset; + p.bit = bit; + p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1); + + if ((ret = spi_xmt((u8 *) & p, sizeof(p))) < 0) + return ret; + + if ((ret = spi_rcv((u8 *) & q, sizeof(q))) < 0) + return ret; + + if (ret == 0) + return -E_ZERO_BYTES; + + if (ret != sizeof(q)) + return -E_PKT_SZ; + + if (q.header != REPORT_REGISTER_BIT) + return -E_BAD_HEADER; + + if (q.LRC != calculate_LRC((u8 *) & q, sizeof(q) - 1)) + return -E_BAD_LRC; + + *data = q.value_bit; + + return 0; +} + +static inline int +write_reg(u8 offset, u8 value) +{ + static write_reg_t p; + + p.header = WRITE_REGISTER; + p.offset = offset; + p.value = value; + p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1); + + return spi_xmt((u8 *) & p, sizeof(p)); +} + +static inline int +read_reg(u8 offset, u8 * data) +{ + static read_reg_t p; + static report_reg_t q; + int ret; + + p.header = READ_REGISTER; + p.offset = offset; + p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1); + + if ((ret = spi_xmt((u8 *) & p, sizeof(p))) < 0) + return ret; + + if ((ret = spi_rcv((u8 *) & q, sizeof(q))) < 0) + return ret; + + if (ret == 0) + return -E_ZERO_BYTES; + + if (ret != sizeof(q)) + return -E_PKT_SZ; + + if (q.header != REPORT_REGISTER) + return -E_BAD_HEADER; + + if (q.LRC != calculate_LRC((u8 *) & q, sizeof(q) - 1)) + return -E_BAD_LRC; + + *data = q.value; + + return 0; +} + +static inline int +write_block(u8 offset, u8 length, u8 * block) +{ + static write_block_t p; + + p.header = WRITE_BLOCK; + p.offset = offset; + p.length = length; + memcpy(&p.block, block, length); + p.block[length] = calculate_LRC((u8 *) & p, 3 + length); + + return spi_xmt((u8 *) & p, 4 + length); +} + +static inline int +read_block(u8 offset, u8 length, u8 * buf) +{ + static read_block_t p; + static report_block_t q; + int ret; + + p.header = READ_BLOCK; + p.offset = offset; + p.length = length; + p.LRC = calculate_LRC((u8 *) & p, sizeof(p) - 1); + + if ((ret = spi_xmt((u8 *) & p, sizeof(p))) < 0) + return ret; + + if ((ret = spi_rcv((u8 *) & q, sizeof(q))) < 0) + return ret; + + if (ret == 0) + return -E_ZERO_BYTES; + + if (ret != sizeof(4 + q.length)) + return -E_PKT_SZ; + + if (q.header != REPORT_BLOCK) + return -E_BAD_HEADER; + + if (q.block[q.length] != calculate_LRC((u8 *) & q, 3 + q.length)) + return -E_BAD_LRC; + + if (length != q.length) + return -E_PKT_SZ; + + memcpy(buf, &q.block, length); + + return 0; +} + +#ifdef INNOVATOR_KEYB_DEBUG +static void +ctrl_dump_regs(void) +{ + int i; + int n; + + for (i = 0; i < 256; i += 8) { + read_block(i, 16, buffer); + mdelay(1); + } +} +#endif + +/*****************************************************************************/ + +static void +process_pointing_report(struct innovator_hid_dev *hid, u8 * buffer) +{ + static int prev_x, prev_y, prev_btn; + int x, y, btn; + + if (buffer[1] & (1 << 3)) { + /* relative pointing device report */ + x = buffer[2]; + y = buffer[3]; + + /* check the sign and convert from 2's complement if negative */ + if (buffer[1] & (1<<4)) + x = ~(-x) - 255; + + /* input driver wants -y */ + if (buffer[1] & (1<<5)) + y = -(~(-y) - 255); + else + y = -y; + + input_report_key(&hid->mouse, + BTN_LEFT, buffer[1] & (1<<0)); + input_report_key(&hid->mouse, + BTN_RIGHT, buffer[1] & (1<<1)); + input_report_key(&hid->mouse, + BTN_MIDDLE, buffer[1] & (1<<2)); + input_report_rel(&hid->mouse, REL_X, x); + input_report_rel(&hid->mouse, REL_Y, y); + } else { + /* REVISIT: Does this work? */ + /* absolute pointing device report */ + x = buffer[2] + ((buffer[1] & X_MSB_MASK) << X_MSB_SHIFT); + y = buffer[3] + ((buffer[1] & Y_MSB_MASK) << Y_MSB_SHIFT); + btn = buffer[1] & (1<<0); + + if ((prev_x == x) && (prev_y == y) + && (prev_btn == btn)) + return; + + input_report_key(&hid->mouse, BTN_LEFT, btn); + input_report_abs(&hid->mouse, ABS_X, x); + input_report_abs(&hid->mouse, ABS_Y, y); + prev_x = x; + prev_y = y; + prev_btn = btn; + } + input_sync(&hid->mouse); + dbg("HID X: %d Y: %d Functions: %x\n", x, y, buffer[1]); +} + +/* + * Reference [1], Appendix A, Semtech standard PS/2 key number definitions, + * pgs. A-1 through A-3. The following table lists standard PS/2 key numbers + * used by the Juno® 01 keyboard manager. + * + * NOTES: + * 1. The following table indices are E0 codes which require special handling: + * 53..62, 77..78, 94, 96, 100, 102..104, 108..110 + * 2. The following table indices are E1 codes which require special handling: + * 101 + */ + +static unsigned char usar2scancode[128] = { + 0x00, 0x29, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x2b, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, + 0x1c, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, + 0x33, 0x34, 0x35, 0x39, 0x01, 0x52, 0x53, 0x4b, + 0x47, 0x4f, 0x48, 0x50, 0x49, 0x51, 0x4d, 0x37, + 0x4e, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47, + 0x48, 0x49, 0x52, 0x53, 0x4a, 0x1c, 0x35, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, + 0x44, 0x57, 0x58, 0x2a, 0x36, 0x38, 0x38, 0x1d, + 0x1d, 0x3a, 0x45, 0x46, 0x2a, 0x1d, 0x5b, 0x5c, + 0x5d, 0xff, 0x00, 0x00, 0x5e, 0x5f, 0x63, 0x70, + 0x7b, 0x79, 0x7d, 0x73, 0x5b, 0x5c, 0x5d, 0x63, + 0x65, 0x66, 0x68, 0x69, 0x6b, 0x56, 0x54, 0x00 +}; + +/* + * The following are bit masks used to encode E0 scan codes which + * require special handling. However, scan codes 100 and 101 are + * excludable here since they each require unique multi-byte scan + * code translations and are therefore dealt with individually via + * handle_print_scr() and handle_pause() respectively below. + */ + +static unsigned long int e0_codes1 = 0x030003ff; /* scan codes 53..84 */ +static unsigned long int e0_codes2 = 0x038e0a00; /* scan codes 85..116 */ + +static void +handle_print_scr(int up) +{ + if (up) { + input_report_key(&hid->keyboard, 0xe0, 1); + input_report_key(&hid->keyboard, 0xb7, 1); + input_report_key(&hid->keyboard, 0xe0, 1); + input_report_key(&hid->keyboard, 0xaa, 1); + } else { + input_report_key(&hid->keyboard, 0xe0, 0); + input_report_key(&hid->keyboard, 0x2a, 0); + input_report_key(&hid->keyboard, 0xe0, 0); + input_report_key(&hid->keyboard, 0x37, 0); + } +} + +static void +handle_pause(void) +{ + input_report_key(&hid->keyboard, 0xe1, 0); + input_report_key(&hid->keyboard, 0x1d, 0); + input_report_key(&hid->keyboard, 0x45, 0); + input_report_key(&hid->keyboard, 0xe1, 0); + input_report_key(&hid->keyboard, 0x9d, 0); + input_report_key(&hid->keyboard, 0xc5, 0); +} + +static void +process_keyboard_report(struct innovator_hid_dev *hid, u8 * buffer) +{ + unsigned char ch = buffer[1] & 0x7f; + int up = buffer[1] & 0x80 ? 1 : 0; + int is_e0 = 0; + + if ((ch == 106) || (ch == 107)) + return; /* no code */ + + if (ch == 100) { + handle_print_scr(up); + return; + } + + if (ch == 101) { + handle_pause(); + return; + } + + if ((ch >= 53) && (ch <= 84)) { + /* first block of e0 codes */ + is_e0 = e0_codes1 & (1 << (ch - 53)); + } else if ((ch >= 85) && (ch <= 116)) { + /* second block of e0 codes */ + is_e0 = e0_codes2 & (1 << (ch - 85)); + } + + if (is_e0) { + input_report_key(&hid->keyboard, 0xe0, !up); + } + input_report_key(&hid->keyboard, usar2scancode[ch], !up); + input_sync(&hid->keyboard); +} + +static irqreturn_t +innovator_hid_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + if (ATN()) { + disable_irq(OMAP1510_INT_FPGA_ATN); + tasklet_schedule(&hid_tasklet); + } + return IRQ_HANDLED; +} + +static void +do_hid_tasklet(unsigned long unused) +{ + int ret; + if ((ret = report_async(buffer, 256)) == -1) { + dbg("Error: Bad Juno return value: %d\n", ret); + } else if (ret == KEYBOARD_REPORT) { + process_keyboard_report(hid, buffer); + } else if (ret == POINTING_REPORT) { + process_pointing_report(hid, buffer); + } else { + dbg("ERROR: bad report\n"); + } + enable_irq(OMAP1510_INT_FPGA_ATN); +} + +static int +innovator_hid_open(struct input_dev *dev) +{ + if (hid->open++) + return 0; + + if (request_irq(OMAP1510_INT_FPGA_ATN, (void *) innovator_hid_interrupt, + SA_INTERRUPT, PFX, hid) < 0) + return -EINVAL; + + return 0; +} + +static void +innovator_hid_close(struct input_dev *dev) +{ + if (!--hid->open) + return; + + if (hid == NULL) + return; + + kfree(hid); +} + +static int innovator_ps2_remove(struct device *dev) +{ + return 0; +} + +static void innovator_ps2_device_release(struct device *dev) +{ + /* Nothing */ +} + +static int innovator_ps2_suspend(struct device *dev, pm_message_t state) +{ + u8 pmcomm; + + /* + * Set SUS_STATE in REG_PM_COMM (Page 0 R0). This will cause + * PM_MOD bits of REG_PM_STATUS to show suspended state, + * but the SUS_STAT bit of REG_PM_STATUS will continue to + * reflect the state of the _HSUS pin. + */ + + if (write_reg(REG_PAGENO, 0) < 0) + printk("ps2 suspend: write_reg REG_PAGENO error\n"); + + if (read_reg(REG_PM_COMM, &pmcomm) < 0) + printk("ps2 suspend: read_reg REG_PM_COMM error\n"); + + if (write_reg(REG_PM_COMM, pmcomm | SUS_STATE) < 0) + printk("ps2 suspend: write_reg REG_PM_COMM error\n"); + + return 0; +} + +static int innovator_ps2_resume(struct device *dev) +{ + u8 pmcomm; + + /* + * Clear SUS_STATE from REG_PM_COMM (Page 0 R0). + */ + + if (write_reg(REG_PAGENO, 0) < 0) + printk("ps2 resume: write_reg REG_PAGENO error\n"); + + if (read_reg(REG_PM_COMM, &pmcomm) < 0) + printk("ps2 resume: read_reg REG_PM_COMM error\n"); + + if (write_reg(REG_PM_COMM, pmcomm & ~SUS_STATE) < 0) + printk("ps2 resume: write_reg REG_PM_COMM error\n"); + + return 0; +} + +static struct device_driver innovator_ps2_driver = { + .name = "innovator_ps2", + .bus = &platform_bus_type, + .remove = innovator_ps2_remove, + .suspend = innovator_ps2_suspend, + .resume = innovator_ps2_resume, +}; + +static struct platform_device innovator_ps2_device = { + .name = "ps2", + .id = -1, + .dev = { + .driver = &innovator_ps2_driver, + .release = innovator_ps2_device_release, + }, +}; + +static int __init +innovator_kbd_init(void) +{ + int i; + info("Innovator PS/2 keyboard/mouse driver v1.0\n"); + + innovator_fpga_hid_reset(); + + if ((hid = kmalloc(sizeof(struct innovator_hid_dev), + GFP_KERNEL)) == NULL) { + warn("unable to allocate space for HID device\n"); + return -ENOMEM; + } + + /* setup the mouse */ + memset(hid, 0, sizeof(struct innovator_hid_dev)); + hid->mouse.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + hid->mouse.keybit[LONG(BTN_MOUSE)] = + BIT(BTN_LEFT) | BIT(BTN_RIGHT) | + BIT(BTN_MIDDLE) | BIT(BTN_TOUCH); + hid->mouse.relbit[0] = BIT(REL_X) | BIT(REL_Y); + hid->mouse.private = hid; + hid->mouse.open = innovator_hid_open; + hid->mouse.close = innovator_hid_close; + hid->mouse.name = "innovator_mouse"; + hid->mouse.id.bustype = 0; + hid->mouse.id.vendor = 0; + hid->mouse.id.product = 0; + hid->mouse.id.version = 0; + hid->keyboard.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + init_input_dev(&hid->keyboard); + hid->keyboard.keycodesize = sizeof(unsigned char); + hid->keyboard.keycodemax = ARRAY_SIZE(usar2scancode); + for(i = 0; i < 128; i++) + set_bit(usar2scancode[i], hid->keyboard.keybit); + hid->keyboard.private = hid; + hid->keyboard.open = innovator_hid_open; + hid->keyboard.close = innovator_hid_close; + hid->keyboard.name = "innovator_keyboard"; + hid->keyboard.id.bustype = 0; + hid->keyboard.id.vendor = 0; + hid->keyboard.id.product = 0; + hid->keyboard.id.version = 0; + input_register_device(&hid->mouse); + input_register_device(&hid->keyboard); + innovator_hid_open(&hid->mouse); + innovator_hid_open(&hid->keyboard); + + if (driver_register(&innovator_ps2_driver) != 0) + printk(KERN_ERR "Driver register failed for innovator_ps2\n"); + + if (platform_device_register(&innovator_ps2_device) != 0) { + printk(KERN_ERR "Device register failed for ps2\n"); + driver_unregister(&innovator_ps2_driver); + } + +#ifdef INNOVATOR_KEYB_DEBUG + ctrl_dump_regs(); +#endif + return 0; +} + +static void __exit +innovator_kbd_exit(void) +{ + input_unregister_device(&hid->mouse); + input_unregister_device(&hid->keyboard); + free_irq(OMAP1510_INT_FPGA_ATN, hid); + if (hid != NULL) + kfree(hid); + driver_unregister(&innovator_ps2_driver); + platform_device_unregister(&innovator_ps2_device); + return; +} + +module_init(innovator_kbd_init); +module_exit(innovator_kbd_exit); + +MODULE_AUTHOR("George G. Davis "); +MODULE_DESCRIPTION("Innovator PS/2 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c new file mode 100644 index 00000000000..e6057faf5d7 --- /dev/null +++ b/drivers/input/keyboard/omap-keypad.c @@ -0,0 +1,451 @@ +/* + * linux/drivers/char/omap-keypad.c + * + * OMAP Keypad Driver + * + * Copyright (C) 2003 Nokia Corporation + * Written by Timo Teräs + * + * Added support for H2 & H3 Keypad + * Copyright (C) 2004 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; 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 +#include + +#undef NEW_BOARD_LEARNING_MODE + +static void omap_kp_tasklet(unsigned long); +static void omap_kp_timer(unsigned long); + +static unsigned char keypad_state[8]; +static DECLARE_MUTEX(kp_enable_mutex); +static int kp_enable = 1; +static int kp_cur_group = -1; + +struct omap_kp { + struct input_dev *input; + struct timer_list timer; + unsigned int irq; + unsigned int rows; + unsigned int cols; +}; + +DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0); + +static int *keymap; +static unsigned int *row_gpios; +static unsigned int *col_gpios; + +#ifdef CONFIG_ARCH_OMAP2 +static void set_col_gpio_val(struct omap_kp *omap_kp, u8 value) +{ + int col; + for (col = 0; col < omap_kp->cols; col++) { + if (value & (1 << col)) + omap_set_gpio_dataout(col_gpios[col], 1); + else + omap_set_gpio_dataout(col_gpios[col], 0); + } +} + +static u8 get_row_gpio_val(struct omap_kp *omap_kp) +{ + int row; + u8 value = 0; + + for (row = 0; row < omap_kp->rows; row++) { + if (omap_get_gpio_datain(row_gpios[row])) + value |= (1 << row); + } + return value; +} +#else +#define set_col_gpio_val(x, y) do {} while (0) +#define get_row_gpio_val(x) 0 +#endif + +static irqreturn_t omap_kp_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct omap_kp *omap_kp = dev_id; + + /* disable keyboard interrupt and schedule for handling */ + if (cpu_is_omap24xx()) { + int i; + for (i = 0; i < omap_kp->rows; i++) + disable_irq(OMAP_GPIO_IRQ(row_gpios[i])); + } else { + /* disable keyboard interrupt and schedule for handling */ + omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + } + + tasklet_schedule(&kp_tasklet); + + return IRQ_HANDLED; +} + +static void omap_kp_timer(unsigned long data) +{ + tasklet_schedule(&kp_tasklet); +} + +static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state) +{ + int col = 0; + + /* read the keypad status */ + if (cpu_is_omap24xx()) { + int i; + for (i = 0; i < omap_kp->rows; i++) + disable_irq(OMAP_GPIO_IRQ(row_gpios[i])); + } else { + /* disable keyboard interrupt and schedule for handling */ + omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + } + if (!cpu_is_omap24xx()) { + /* read the keypad status */ + omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC); + for (col = 0; col < omap_kp->cols; col++) { + omap_writew(~(1 << col) & 0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC); + + if (machine_is_omap_osk() || machine_is_omap_h2() || machine_is_omap_h3()) { + udelay(9); + } else { + udelay(4); + } + state[col] = ~omap_readw(OMAP_MPUIO_BASE + OMAP_MPUIO_KBR_LATCH) & 0xff; + } + omap_writew(0x00, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC); + udelay(2); + } else { + /* read the keypad status */ + for (col = 0; col < omap_kp->cols; col++) { + set_col_gpio_val(omap_kp, ~(1 << col)); + state[col] = ~(get_row_gpio_val(omap_kp)) & 0x3f; + } + set_col_gpio_val(omap_kp, 0); + } +} + +static inline int omap_kp_find_key(int col, int row) +{ + int i, key; + + key = KEY(col, row, 0); + for (i = 0; keymap[i] != 0; i++) + if ((keymap[i] & 0xff000000) == key) + return keymap[i] & 0x00ffffff; + return -1; +} + +static void omap_kp_tasklet(unsigned long data) +{ + struct omap_kp *omap_kp_data = (struct omap_kp *) data; + unsigned char new_state[8], changed, key_down = 0; + int col, row; + int spurious = 0; + + /* check for any changes */ + omap_kp_scan_keypad(omap_kp_data, new_state); + + /* check for changes and print those */ + for (col = 0; col < omap_kp_data->cols; col++) { + changed = new_state[col] ^ keypad_state[col]; + key_down |= new_state[col]; + if (changed == 0) + continue; + + for (row = 0; row < omap_kp_data->rows; row++) { + int key; + if (!(changed & (1 << row))) + continue; +#ifdef NEW_BOARD_LEARNING_MODE + printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col, row, (new_state[col] & (1 << row)) ? "pressed" : "released"); +#else + key = omap_kp_find_key(col, row); + if (key < 0) { + printk(KERN_WARNING "omap-keypad: Spurious key event %d-%d\n", + col, row); + /* We scan again after a couple of seconds */ + spurious = 1; + continue; + } + + if (!(kp_cur_group == (key & GROUP_MASK) || + kp_cur_group == -1)) + continue; + + kp_cur_group = key & GROUP_MASK; + input_report_key(omap_kp_data->input, key & ~GROUP_MASK, + !!(new_state[col] & (1 << row))); +#endif + } + } + memcpy(keypad_state, new_state, sizeof(keypad_state)); + + if (key_down) { + int delay = HZ / 20; + /* some key is pressed - keep irq disabled and use timer + * to poll the keypad */ + if (spurious) + delay = 2 * HZ; + mod_timer(&omap_kp_data->timer, jiffies + delay); + } else { + /* enable interrupts */ + if (cpu_is_omap24xx()) { + int i; + for (i = 0; i < omap_kp_data->rows; i++) + enable_irq(OMAP_GPIO_IRQ(row_gpios[i])); + } else { + omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + kp_cur_group = -1; + } + } +} + +static ssize_t omap_kp_enable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", kp_enable); +} + +static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int state; + + if (sscanf(buf, "%u", &state) != 1) + return -EINVAL; + + if ((state != 1) && (state != 0)) + return -EINVAL; + + down(&kp_enable_mutex); + if (state != kp_enable) { + if (state) + enable_irq(INT_KEYBOARD); + else + disable_irq(INT_KEYBOARD); + kp_enable = state; + } + up(&kp_enable_mutex); + + return strnlen(buf, count); +} + +static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, omap_kp_enable_show, omap_kp_enable_store); + +#ifdef CONFIG_PM +static int omap_kp_suspend(struct platform_device *dev, pm_message_t state) +{ + /* Nothing yet */ + + return 0; +} + +static int omap_kp_resume(struct platform_device *dev) +{ + /* Nothing yet */ + + return 0; +} +#else +#define omap_kp_suspend NULL +#define omap_kp_resume NULL +#endif + +static int __init omap_kp_probe(struct platform_device *pdev) +{ + struct omap_kp *omap_kp; + struct input_dev *input_dev; + struct omap_kp_platform_data *pdata = pdev->dev.platform_data; + int i; + + if (!pdata->rows || !pdata->cols || !pdata->keymap) { + printk(KERN_ERR "No rows, cols or keymap from pdata\n"); + return -EINVAL; + } + + omap_kp = kzalloc(sizeof(struct omap_kp), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!omap_kp || !input_dev) { + kfree(omap_kp); + input_free_device(input_dev); + return -ENOMEM; + } + + platform_set_drvdata(pdev, omap_kp); + + omap_kp->input = input_dev; + + /* Disable the interrupt for the MPUIO keyboard */ + if (!cpu_is_omap24xx()) + omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + + keymap = pdata->keymap; + + if (pdata->rep) + set_bit(EV_REP, input_dev->evbit); + + if (pdata->row_gpios && pdata->col_gpios) { + row_gpios = pdata->row_gpios; + col_gpios = pdata->col_gpios; + } + + omap_kp->rows = pdata->rows; + omap_kp->cols = pdata->cols; + + if (cpu_is_omap24xx()) { + /* Cols: outputs */ + for (i = 0; i < omap_kp->cols; i++) { + if (omap_request_gpio(col_gpios[i]) < 0) { + printk(KERN_ERR "Failed to request" + "GPIO%d for keypad\n", + col_gpios[i]); + return -EINVAL; + } + omap_set_gpio_direction(col_gpios[i], 0); + } + /* Rows: inputs */ + for (i = 0; i < omap_kp->rows; i++) { + if (omap_request_gpio(row_gpios[i]) < 0) { + printk(KERN_ERR "Failed to request" + "GPIO%d for keypad\n", + row_gpios[i]); + return -EINVAL; + } + omap_set_gpio_direction(row_gpios[i], 1); + } + } + + init_timer(&omap_kp->timer); + omap_kp->timer.function = omap_kp_timer; + omap_kp->timer.data = (unsigned long) omap_kp; + + /* get the irq and init timer*/ + tasklet_enable(&kp_tasklet); + kp_tasklet.data = (unsigned long) omap_kp; + + omap_kp->irq = platform_get_irq(pdev, 0); + if (omap_kp->irq) { + if (request_irq(omap_kp->irq, omap_kp_interrupt, 0, + "omap-keypad", omap_kp) < 0) + return -EINVAL; + } + + device_create_file(&pdev->dev, &dev_attr_enable); + + /* setup input device */ + set_bit(EV_KEY, input_dev->evbit); + for (i = 0; keymap[i] != 0; i++) + set_bit(keymap[i] & KEY_MAX, input_dev->keybit); + input_dev->name = "omap-keypad"; + input_dev->cdev.dev = &pdev->dev; + input_dev->private = omap_kp; + input_register_device(omap_kp->input); + + if (machine_is_omap_h2() || machine_is_omap_h3() || + machine_is_omap_perseus2()) { + omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING); + } + /* scan current status and enable interrupt */ + omap_kp_scan_keypad(omap_kp, keypad_state); + if (!cpu_is_omap24xx()) { + omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + } else { + for (i = 0; i < omap_kp->rows; i++) { + if (request_irq(OMAP_GPIO_IRQ(row_gpios[i]), omap_kp_interrupt, + SA_TRIGGER_FALLING, "omap-keypad", omap_kp) < 0) + return -EINVAL; + } + } + + return 0; +} + +static int omap_kp_remove(struct platform_device *pdev) +{ + struct omap_kp *omap_kp = platform_get_drvdata(pdev); + + /* disable keypad interrupt handling */ + tasklet_disable(&kp_tasklet); + if (cpu_is_omap24xx()) { + int i; + for (i = 0; i < omap_kp->cols; i++) + omap_free_gpio(col_gpios[i]); + for (i = 0; i < omap_kp->rows; i++) { + omap_free_gpio(row_gpios[i]); + free_irq(OMAP_GPIO_IRQ(row_gpios[i]), 0); + } + } else { + omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT); + free_irq(omap_kp->irq, 0); + } + + del_timer_sync(&omap_kp->timer); + + /* unregister everything */ + input_unregister_device(omap_kp->input); + + kfree(omap_kp); + + return 0; +} + +static struct platform_driver omap_kp_driver = { + .probe = omap_kp_probe, + .remove = omap_kp_remove, + .suspend = omap_kp_suspend, + .resume = omap_kp_resume, + .driver = { + .name = "omap-keypad", + }, +}; + +static int __devinit omap_kp_init(void) +{ + printk(KERN_INFO "OMAP Keypad Driver\n"); + return platform_driver_register(&omap_kp_driver); +} + +static void __exit omap_kp_exit(void) +{ + platform_driver_unregister(&omap_kp_driver); +} + +module_init(omap_kp_init); +module_exit(omap_kp_exit); + +MODULE_AUTHOR("Timo Teräs"); +MODULE_DESCRIPTION("OMAP Keypad Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index b1b14f8d4dd..63721960572 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -109,3 +109,18 @@ config TOUCHSCREEN_HP600 module will be called hp680_ts_input. endif +config TOUCHSCREEN_OMAP + tristate "OMAP touchscreen input driver" + depends on INPUT && INPUT_TOUCHSCREEN && ARCH_OMAP + select OMAP_TSC2101 + help + Say Y here if you have an OMAP based board with touchscreen + attached to it, e.g. OMAP Innovator, OSK, H2 or H3 + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called omap_ts. + + + diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index 5e5557c4312..b5866f932e2 100644 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -1,5 +1,5 @@ # -# Makefile for the mouse drivers. +# Makefile for the touchscreen input drivers. # # Each configuration option enables a list of files. @@ -12,3 +12,4 @@ obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o +obj-$(CONFIG_TOUCHSCREEN_OMAP) += omap/ diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index 8c12a974b41..6da83482f5b 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c @@ -71,11 +71,18 @@ struct ads7846 { u16 vref_delay_usecs; u16 x_plate_ohms; - u8 read_x, read_y, read_z1, read_z2; + u8 read_x, read_y, read_z1, read_z2, pwrdown; + u16 dummy; /* for the pwrdown read */ struct ts_event tc; - struct spi_transfer xfer[8]; - struct spi_message msg; + struct spi_transfer xfer[10]; + struct spi_message msg[5]; + int msg_idx; + int read_cnt; + int last_read; + + u16 debounce_max; + u16 debounce_tol; spinlock_t lock; struct timer_list timer; /* P: lock */ @@ -124,7 +131,9 @@ struct ads7846 { #define READ_Y (READ_12BIT_DFR(y) | ADS_PD10_ADC_ON) #define READ_Z1 (READ_12BIT_DFR(z1) | ADS_PD10_ADC_ON) #define READ_Z2 (READ_12BIT_DFR(z2) | ADS_PD10_ADC_ON) -#define READ_X (READ_12BIT_DFR(x) | ADS_PD10_PDOWN) /* LAST */ + +#define READ_X (READ_12BIT_DFR(x) | ADS_PD10_ADC_ON) +#define PWRDOWN (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) /* LAST */ /* single-ended samples need to first power up reference voltage; * we leave both ADC and VREF powered @@ -163,7 +172,7 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) if (!req) return -ENOMEM; - INIT_LIST_HEAD(&req->msg.transfers); + spi_message_init(&req->msg); /* activate reference, so it has time to settle; */ req->ref_on = REF_ON; @@ -232,6 +241,21 @@ SHOW(temp1) SHOW(vaux) SHOW(vbatt) +static int is_pen_down(struct device *dev) +{ + struct ads7846 *ts = dev_get_drvdata(dev); + + return ts->pendown; +} + +static ssize_t ads7846_pen_down_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%u\n", is_pen_down(dev)); +} + +static DEVICE_ATTR(pen_down, S_IRUGO, ads7846_pen_down_show, NULL); + /*--------------------------------------------------------------------------*/ /* @@ -254,10 +278,10 @@ static void ads7846_rx(void *ads) /* adjust: 12 bit samples (left aligned), built from * two 8 bit values writen msb-first. */ - x = be16_to_cpu(ts->tc.x) >> 4; - y = be16_to_cpu(ts->tc.y) >> 4; - z1 = be16_to_cpu(ts->tc.z1) >> 4; - z2 = be16_to_cpu(ts->tc.z2) >> 4; + x = ts->tc.x >> 3; + y = ts->tc.y >> 3; + z1 = ts->tc.z1 >> 3; + z2 = ts->tc.z2 >> 3; /* range filtering */ if (x == MAX_12BIT) @@ -325,31 +349,71 @@ static void ads7846_rx(void *ads) spin_unlock_irqrestore(&ts->lock, flags); } +static void ads7846_debounce(void *ads) +{ + struct ads7846 *ts = ads; + struct spi_message *m; + struct spi_transfer *t; + u16 val; + int status; + + m = &ts->msg[ts->msg_idx]; + t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + val = (*(u16 *)t->rx_buf) >> 3; + + if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol && + ts->read_cnt < ts->debounce_max)) { + /* Repeat it, if this was the first read or the read wasn't + * consistent enough */ + ts->read_cnt++; + ts->last_read = val; + } else { + /* Go for the next read */ + ts->msg_idx++; + ts->read_cnt = 0; + m++; + } + status = spi_async(ts->spi, m); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", + status); +} + static void ads7846_timer(unsigned long handle) { struct ads7846 *ts = (void *)handle; int status = 0; - unsigned long flags; + + ts->msg_idx = 0; + status = spi_async(ts->spi, &ts->msg[0]); + if (status) + dev_err(&ts->spi->dev, "spi_async --> %d\n", status); +} + +static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) +{ + struct ads7846 *ts = handle; + unsigned long flags; + int r = IRQ_HANDLED; spin_lock_irqsave(&ts->lock, flags); - if (!ts->pending) { - ts->pending = 1; + if (ts->irq_disabled) + r = IRQ_HANDLED; + else { if (!ts->irq_disabled) { ts->irq_disabled = 1; + /* The following has at the moment no effect whatsoever + * on OMAP, that's why we maintain the disabled + * state ourselves */ disable_irq(ts->spi->irq); } - status = spi_async(ts->spi, &ts->msg); - if (status) - dev_err(&ts->spi->dev, "spi_async --> %d\n", - status); + if (!ts->pending) { + ts->pending = 1; + mod_timer(&ts->timer, jiffies); + } } spin_unlock_irqrestore(&ts->lock, flags); -} - -static irqreturn_t ads7846_irq(int irq, void *handle, struct pt_regs *regs) -{ - ads7846_timer((unsigned long) handle); - return IRQ_HANDLED; + return r; } /*--------------------------------------------------------------------------*/ @@ -379,7 +443,7 @@ ads7846_suspend(struct spi_device *spi, pm_message_t message) while (ts->pendown || ts->pending) { spin_unlock_irqrestore(&ts->lock, flags); - udelay(10); + msleep(1); spin_lock_irqsave(&ts->lock, flags); } } @@ -407,6 +471,7 @@ static int __devinit ads7846_probe(struct spi_device *spi) struct ads7846 *ts; struct input_dev *input_dev; struct ads7846_platform_data *pdata = spi->dev.platform_data; + struct spi_message *m; struct spi_transfer *x; int err; @@ -453,6 +518,8 @@ static int __devinit ads7846_probe(struct spi_device *spi) ts->model = pdata->model ? : 7846; ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; + ts->debounce_max = pdata->debounce_max ? : 1; + ts->debounce_tol = pdata->debounce_tol ? : 10; snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id); @@ -476,60 +543,98 @@ static int __devinit ads7846_probe(struct spi_device *spi) /* set up the transfers to read touchscreen state; this assumes we * use formula #2 for pressure, not #3. */ - INIT_LIST_HEAD(&ts->msg.transfers); + m = &ts->msg[0]; x = ts->xfer; + spi_message_init(m); + /* y- still on; turn on only y+ (and ADC) */ ts->read_y = READ_Y; x->tx_buf = &ts->read_y; x->len = 1; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); x++; x->rx_buf = &ts->tc.y; x->len = 2; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); + + m->complete = ads7846_debounce; + m->context = ts; + + m++; + spi_message_init(m); + + /* turn y- off, x+ on, then leave in lowpower */ + x++; + ts->read_x = READ_X; + x->tx_buf = &ts->read_x; + x->len = 1; + spi_message_add_tail(x, m); + + x++; + x->rx_buf = &ts->tc.x; + x->len = 2; + spi_message_add_tail(x, m); + + m->complete = ads7846_debounce; + m->context = ts; /* turn y+ off, x- on; we'll use formula #2 */ if (ts->model == 7846) { + m++; + spi_message_init(m); + x++; ts->read_z1 = READ_Z1; x->tx_buf = &ts->read_z1; x->len = 1; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); x++; x->rx_buf = &ts->tc.z1; x->len = 2; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); + + m->complete = ads7846_debounce; + m->context = ts; + + m++; + spi_message_init(m); x++; ts->read_z2 = READ_Z2; x->tx_buf = &ts->read_z2; x->len = 1; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); x++; x->rx_buf = &ts->tc.z2; x->len = 2; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); + + m->complete = ads7846_debounce; + m->context = ts; } - /* turn y- off, x+ on, then leave in lowpower */ + /* power down */ + m++; + spi_message_init(m); + x++; - ts->read_x = READ_X; - x->tx_buf = &ts->read_x; + ts->pwrdown = PWRDOWN; + x->tx_buf = &ts->pwrdown; x->len = 1; - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); x++; - x->rx_buf = &ts->tc.x; + x->rx_buf = &ts->dummy; x->len = 2; CS_CHANGE(*x); - spi_message_add_tail(x, &ts->msg); + spi_message_add_tail(x, m); - ts->msg.complete = ads7846_rx; - ts->msg.context = ts; + m->complete = ads7846_rx; + m->context = ts; if (request_irq(spi->irq, ads7846_irq, SA_SAMPLE_RANDOM | SA_TRIGGER_FALLING, @@ -558,6 +663,8 @@ static int __devinit ads7846_probe(struct spi_device *spi) device_create_file(&spi->dev, &dev_attr_vbatt); device_create_file(&spi->dev, &dev_attr_vaux); + device_create_file(&spi->dev, &dev_attr_pen_down); + err = input_register_device(input_dev); if (err) goto err_free_irq; @@ -581,6 +688,8 @@ static int __devexit ads7846_remove(struct spi_device *spi) if (ts->irq_disabled) enable_irq(ts->spi->irq); + device_remove_file(&spi->dev, &dev_attr_pen_down); + if (ts->model == 7846) { device_remove_file(&spi->dev, &dev_attr_temp0); device_remove_file(&spi->dev, &dev_attr_temp1); diff --git a/drivers/input/touchscreen/omap/Makefile b/drivers/input/touchscreen/omap/Makefile new file mode 100644 index 00000000000..687c068fd76 --- /dev/null +++ b/drivers/input/touchscreen/omap/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for the OMAP touchscreen input driver +# + +obj-$(CONFIG_TOUCHSCREEN_OMAP) += omapts.o + +objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_H2) += ts_hx.o +objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_H3) += ts_hx.o +objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += ts_inn1510.o +objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_OSK) += ts_osk.o + +omapts-objs := omap_ts.o $(objs-yy) diff --git a/drivers/input/touchscreen/omap/ads7846.h b/drivers/input/touchscreen/omap/ads7846.h new file mode 100644 index 00000000000..ae4347a8f68 --- /dev/null +++ b/drivers/input/touchscreen/omap/ads7846.h @@ -0,0 +1,53 @@ +/* + * ads7846.h - header file for ADS7846 touchscreen controller + * + * Copyright 2002 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or source@mvista.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; 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 __ADS7846_H +#define __ADS7846_H + +// ADS7846 Control Byte bit defines +#define ADS7846_S (1<<7) +#define ADS7846_ADDR_BIT 4 +#define ADS7846_ADDR_MASK (0x7< + * + * Assembled using driver code copyright the companies above. + * + * 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 + * History: + * 12/12/2004 Srinath Modified and intergrated code for H2 and H3 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +//#define DEBUG + +#include "omap_ts.h" + +#define OMAP_TS_NAME "omap_ts" + +static struct ts_device *__initdata ts_devs[] = { +#if defined(CONFIG_MACH_OMAP_H2) || defined(CONFIG_MACH_OMAP_H3) + &hx_ts, +#endif +#ifdef CONFIG_MACH_OMAP_OSK + &osk_ts, +#endif +#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX) + &innovator1510_ts, +#endif +}; + +static struct omap_ts_t ts_omap; + +static int omap_ts_read(void) +{ + u16 data[4] = { 0, 0, 0, 0 }; + + ts_omap.dev->read(data); + + input_report_abs(ts_omap.inputdevice, ABS_X, data[0]); + input_report_abs(ts_omap.inputdevice, ABS_Y, data[1]); + input_report_abs(ts_omap.inputdevice, ABS_PRESSURE, data[2]); + input_sync(ts_omap.inputdevice); + + DEBUG_TS("omap_ts_read: read x=%d,y=%d,p=%d\n", data[0], data[1], + data[2]); + + return 0; +} + +static void omap_ts_timer(unsigned long data) +{ + unsigned long flags; + + spin_lock_irqsave(&ts_omap.lock, flags); + + if (!ts_omap.dev->penup()) { + if (!ts_omap.touched) { + DEBUG_TS("omap_ts_timer: pen down\n"); + input_report_key(ts_omap.inputdevice, BTN_TOUCH, 1); + } + ts_omap.touched = 1; + omap_ts_read(); + ts_omap.ts_timer.expires = jiffies + HZ / 100; + add_timer(&(ts_omap.ts_timer)); + } else { + if (ts_omap.touched) { + DEBUG_TS("omap_ts_timer: pen up\n"); + ts_omap.touched = 0; + input_report_abs(ts_omap.inputdevice, ABS_X, 0); + input_report_abs(ts_omap.inputdevice, ABS_Y, 0); + input_report_abs(ts_omap.inputdevice, ABS_PRESSURE, + 0); + input_sync(ts_omap.inputdevice); + input_report_key(ts_omap.inputdevice, BTN_TOUCH, 0); + } + if (!ts_omap.irq_enabled) { + ts_omap.irq_enabled = 1; + enable_irq(ts_omap.irq); + } + } + + spin_unlock_irqrestore(&ts_omap.lock, flags); +} + +static irqreturn_t omap_ts_handler(int irq, void *dev_id, struct pt_regs *regs) +{ + spin_lock(&ts_omap.lock); + + if (ts_omap.irq_enabled) { + ts_omap.irq_enabled = 0; + disable_irq(irq); + } + // restart acquire + mod_timer(&ts_omap.ts_timer, jiffies + HZ / 100); + + spin_unlock(&ts_omap.lock); + + return IRQ_HANDLED; +} + +static int __init omap_ts_probe(struct platform_device *pdev) +{ + int i; + int status = -ENODEV; + + memset(&ts_omap, 0, sizeof(ts_omap)); + + ts_omap.inputdevice = input_allocate_device(); + if (!ts_omap.inputdevice) { + return -ENOMEM; + } + + spin_lock_init(&ts_omap.lock); + + for (i = 0; i < ARRAY_SIZE(ts_devs); i++) { + if (!ts_devs[i] || !ts_devs[i]->probe) + continue; + status = ts_devs[i]->probe(&ts_omap); + if (status == 0) { + ts_omap.dev = ts_devs[i]; + break; + } + } + + if (status != 0) { + input_free_device(ts_omap.inputdevice); + return status; + } + + // Init acquisition timer function + init_timer(&ts_omap.ts_timer); + ts_omap.ts_timer.function = omap_ts_timer; + + /* request irq */ + if (ts_omap.irq != -1) { + if (request_irq(ts_omap.irq, omap_ts_handler, + SA_SAMPLE_RANDOM | ts_omap.irq_type, + OMAP_TS_NAME, &ts_omap)) { + printk(KERN_ERR + "omap_ts.c: Could not allocate touchscreen IRQ!\n"); + ts_omap.irq = -1; + ts_omap.dev->remove(); + input_free_device(ts_omap.inputdevice); + return -EINVAL; + } + ts_omap.irq_enabled = 1; + } else { + printk(KERN_ERR "omap_ts.c: No touchscreen IRQ assigned!\n"); + ts_omap.dev->remove(); + input_free_device(ts_omap.inputdevice); + return -EINVAL; + } + + ts_omap.inputdevice->name = OMAP_TS_NAME; + ts_omap.inputdevice->dev = &pdev->dev; + ts_omap.inputdevice->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + ts_omap.inputdevice->keybit[LONG(BTN_TOUCH)] |= BIT(BTN_TOUCH); + ts_omap.inputdevice->absbit[0] = + BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); + input_register_device(ts_omap.inputdevice); + + ts_omap.dev->enable(); + + printk("OMAP touchscreen driver initialized\n"); + + return 0; +} + +static int omap_ts_remove(struct platform_device *pdev) +{ + ts_omap.dev->disable(); + input_unregister_device(ts_omap.inputdevice); + if (ts_omap.irq != -1) + free_irq(ts_omap.irq, &ts_omap); + + ts_omap.dev->remove(); + + return 0; +} + +static int omap_ts_suspend(struct platform_device *pdev, pm_message_t state) +{ + ts_omap.dev->disable(); + return 0; +} + +static int omap_ts_resume(struct platform_device *pdev) +{ + ts_omap.dev->enable(); + return 0; +} + +static void omap_ts_device_release(struct device *dev) +{ + /* Nothing */ +} +static struct platform_driver omap_ts_driver = { + .probe = omap_ts_probe, + .remove = omap_ts_remove, + .suspend = omap_ts_suspend, + .resume = omap_ts_resume, + .driver = { + .name = OMAP_TS_NAME, + }, +}; + +static struct platform_device omap_ts_device = { + .name = OMAP_TS_NAME, + .id = -1, + .dev = { + .release = omap_ts_device_release, + }, +}; + +static int __init omap_ts_init(void) +{ + int ret; + + ret = platform_device_register(&omap_ts_device); + if (ret != 0) + return -ENODEV; + + ret = platform_driver_register(&omap_ts_driver); + if (ret != 0) { + platform_device_unregister(&omap_ts_device); + return -ENODEV; + } + + return 0; +} + +static void __exit omap_ts_exit(void) +{ + platform_driver_unregister(&omap_ts_driver); + platform_device_unregister(&omap_ts_device); +} + +module_init(omap_ts_init); +module_exit(omap_ts_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/input/touchscreen/omap/omap_ts.h b/drivers/input/touchscreen/omap/omap_ts.h new file mode 100644 index 00000000000..6efb7dbb526 --- /dev/null +++ b/drivers/input/touchscreen/omap/omap_ts.h @@ -0,0 +1,59 @@ +/* + * omap_ts.h - header file for OMAP touchscreen support + * + * Copyright (c) 2002 MontaVista Software Inc. + * Copyright (c) 2004 Texas Instruments, Inc. + * + * Assembled using driver code copyright the companies above. + * + * 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 __OMAP_TS_H +#define __OMAP_TS_H + +#ifdef DEBUG +#define DEBUG_TS(fmt...) printk(fmt) +#else +#define DEBUG_TS(fmt...) do { } while (0) +#endif + +struct omap_ts_t; + +struct ts_device { + int (*probe) (struct omap_ts_t *); + void (*read) (u16 *); + void (*enable) (void); + void (*disable) (void); + void (*remove) (void); + int (*penup) (void); +}; + +struct omap_ts_t{ + struct input_dev * inputdevice; + struct timer_list ts_timer; // Timer for triggering acquisitions + int touched; + int irq; + int irq_type; + int irq_enabled; + struct ts_device *dev; + spinlock_t lock; +}; + +extern struct ts_device hx_ts; +extern struct ts_device osk_ts; +extern struct ts_device innovator1510_ts; + +#endif /* __OMAP_TS_H */ diff --git a/drivers/input/touchscreen/omap/ts_hx.c b/drivers/input/touchscreen/omap/ts_hx.c new file mode 100644 index 00000000000..f148f19765e --- /dev/null +++ b/drivers/input/touchscreen/omap/ts_hx.c @@ -0,0 +1,184 @@ +/* + * input/touchscreen/omap/ts_hx.c + * touchscreen support for OMAP H3 and H2 boards + * + * Copyright (c) 2002 MontaVista Software Inc. + * Copyright (c) 2004 Texas Instruments, Inc. + * + * Assembled using driver code copyright the companies above. + * + * 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 + * + * History: + * 9/12/2004 Srinath Modified and integrated H2 and H3 code + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "../drivers/ssi/omap-tsc2101.h" +#include "omap_ts.h" + +#define H2_GPIO_NUM 4 +#define H3_GPIO_NUM 48 + +#define OMAP_TSC2101_XRES 500 +#define TOUCHSCREEN_DATA_REGISTERS_PAGE 0x0 +#define TOUCHSCREEN_CONTROL_REGISTERS_PAGE 0x1 +#define OMAP_TSC2101_READ_MAX 0x4 +#define TSC2101_GETSTATUS(ret) (((ret) >> 11) & 0x1) +#define TSC2101_MASKVAL 0xFFF +#define TSC2101_PRESSUREVAL(x) ((x) << 12) + +static int hx_ts_penup(void); +static int hx_ts_probe(struct omap_ts_t *ts); +static void hx_ts_read(u16 * data); +static void hx_ts_enable(void); +static void hx_ts_disable(void); +#ifdef MODULE +static void hx_ts_remove(void); +#endif + +struct ts_device hx_ts = { + .probe = hx_ts_probe, + .read = hx_ts_read, + .enable = hx_ts_enable, + .disable = hx_ts_disable, + .remove = __exit_p(hx_ts_remove), + .penup = hx_ts_penup, +}; + +static int hx_ts_penup(void) +{ + int ret = 0; + /* Read the status register */ + ret = omap_tsc2101_read(TOUCHSCREEN_CONTROL_REGISTERS_PAGE, + TSC2101_TS_STATUS); + /* Check for availability of data in status register */ + ret = TSC2101_GETSTATUS(ret); + return !ret; + +} + +static int __init hx_ts_probe(struct omap_ts_t *ts) +{ + unsigned gpio; + + if (machine_is_omap_h2()) { + gpio = H2_GPIO_NUM; + omap_cfg_reg(P20_1610_GPIO4); + } else if (machine_is_omap_h3()) { + gpio = H3_GPIO_NUM; + omap_cfg_reg(W19_1610_GPIO48); + } else + return -ENODEV; + + ts->irq = OMAP_GPIO_IRQ(gpio); + if (omap_request_gpio(gpio) != 0) { + printk(KERN_ERR "hX_ts_init.c: Could not reserve GPIO!\n"); + return -EINVAL; + }; + + omap_set_gpio_direction(gpio, 1); + ts->irq_type = SA_TRIGGER_FALLING; + return 0; +} + +static void hx_ts_read(u16 * values) +{ + s32 t, p = 0; + int i; + + /* Read X, Y, Z1 and Z2 */ + omap_tsc2101_reads(TOUCHSCREEN_DATA_REGISTERS_PAGE, TSC2101_TS_X, + values, OMAP_TSC2101_READ_MAX); + + for (i = 0; i < OMAP_TSC2101_READ_MAX; i++) + values[i] &= TSC2101_MASKVAL; + + /* Calculate Pressure */ + if (values[TSC2101_TS_Z1] != 0) { + t = ((OMAP_TSC2101_XRES * values[TSC2101_TS_X]) * + (values[TSC2101_TS_Z2] - values[TSC2101_TS_Z1])); + p = t / (u32) (TSC2101_PRESSUREVAL(values[TSC2101_TS_Z1])); + if (p < 0) + p = 0; + } + + values[TSC2101_TS_Z1] = p; +} + +static void hx_ts_enable(void) +{ + int ret = omap_tsc2101_enable(); + if (ret) { + printk(KERN_ERR "FAILED TO INITIALIZE TSC CODEC\n"); + return; + } + + /* PINTDAV is data available only */ + omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE, + TSC2101_TS_STATUS, TSC2101_DATA_AVAILABLE); + /* disable buffer mode */ + omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE, + TSC2101_TS_BUFFER_CTRL, TSC2101_BUFFERMODE_DISABLE); + /* use internal reference, 100 usec power-up delay, + * * power down between conversions, 1.25V internal reference */ + omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE, + TSC2101_TS_REF_CTRL, TSC2101_REF_POWERUP); + /* enable touch detection, 84usec precharge time, 32 usec sense time */ + omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE, + TSC2101_TS_CONFIG_CTRL, TSC2101_ENABLE_TOUCHDETECT); + /* 3 msec conversion delays */ + omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE, + TSC2101_TS_PROG_DELAY, TSC2101_PRG_DELAY); + /* + * TSC2101-controlled conversions + * 12-bit samples + * continuous X,Y,Z1,Z2 scan mode + * average (mean) 4 samples per coordinate + * 1 MHz internal conversion clock + * 500 usec panel voltage stabilization delay + */ + omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE, + TSC2101_TS_ADC_CTRL, TSC2101_ADC_CONTROL); + + return; + +} + +static void hx_ts_disable(void) +{ + /* stop conversions and power down */ + omap_tsc2101_write(TOUCHSCREEN_CONTROL_REGISTERS_PAGE, + TSC2101_TS_ADC_CTRL, TSC2101_ADC_POWERDOWN); + omap_tsc2101_disable(); +} + +#ifdef MODULE +static void __exit hx_ts_remove(void) +{ + if (machine_is_omap_h2()) + omap_free_gpio(H2_GPIO_NUM); + else if (machine_is_omap_h3()) + omap_free_gpio(H3_GPIO_NUM); +} +#endif diff --git a/drivers/input/touchscreen/omap/ts_inn1510.c b/drivers/input/touchscreen/omap/ts_inn1510.c new file mode 100644 index 00000000000..c181e1dc5d5 --- /dev/null +++ b/drivers/input/touchscreen/omap/ts_inn1510.c @@ -0,0 +1,219 @@ +/* + * ts_inn1510.c - touchscreen support for OMAP1510 Innovator board + * + * Copyright 2002 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or source@mvista.com + * + * The touchscreen hardware on the Innovator consists of an FPGA + * register which is bit-banged to generate SPI-like transactions + * to an ADS7846 touch screen controller. + * + * 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 "omap_ts.h" +#include "ads7846.h" + +// The Touch Screen Register on Innovator FPGA +#define FPGA_TS_BCLK (1<<0) +#define FPGA_TS_BDIN (1<<1) +#define FPGA_TS_BCS (1<<2) +#define FPGA_TS_BBUSY (1<<3) +#define FPGA_TS_BOUT (1<<4) +#define FPGA_TS_BPENUP (1<<5) + +#define X_PLATE_OHMS 419 +#define Y_PLATE_OHMS 486 + +static int inn1510_ts_penup(void); +static int inn1510_ts_probe(struct omap_ts_t *ts); +static void inn1510_ts_read(u16 * data); +static void inn1510_ts_enable(void); +static void inn1510_ts_disable(void); +#ifdef MODULE +static void inn1510_ts_remove(void); +#endif + +struct ts_device innovator1510_ts = { + .probe = inn1510_ts_probe, + .read = inn1510_ts_read, + .enable = inn1510_ts_enable, + .disable = inn1510_ts_disable, + .remove = __exit_p(inn1510_ts_remove), + .penup = inn1510_ts_penup, +}; + +static inline u8 fpga_ts_read(void) +{ + return fpga_read(OMAP1510_FPGA_TOUCHSCREEN); +} + +static inline void fpga_ts_write(u8 val) +{ + fpga_write(val, OMAP1510_FPGA_TOUCHSCREEN); +} + +static inline void fpga_ts_set_bits(u8 mask) +{ + fpga_ts_write(fpga_ts_read() | mask); +} + +static inline void fpga_ts_clear_bits(u8 mask) +{ + fpga_ts_write(fpga_ts_read() & ~mask); +} + +static inline void CS_H(void) +{ + // EPLD inverts active low signals. + fpga_ts_clear_bits(FPGA_TS_BCS); +} + +static inline void CS_L(void) +{ + fpga_ts_set_bits(FPGA_TS_BCS); +} + +static inline void SCLK_L(void) +{ + fpga_ts_clear_bits(FPGA_TS_BCLK); +} + +static inline void SCLK_H(void) +{ + fpga_ts_set_bits(FPGA_TS_BCLK); +} + +static inline void SDI_L(void) +{ + fpga_ts_clear_bits(FPGA_TS_BDIN); +} + +static inline void SDI_H(void) +{ + fpga_ts_set_bits(FPGA_TS_BDIN); +} + +static inline int BUSY(void) +{ + return (((fpga_ts_read() & FPGA_TS_BBUSY) == 0) ? 1 : 0) ; +} + +static inline u8 DOUT(void) +{ + return ((fpga_ts_read() & FPGA_TS_BOUT) ? 1 : 0) ; +} + +static u16 ads7846_do(u8 cmd) +{ + int i; + u16 val=0; + + SCLK_L() ; + SDI_L(); + CS_L() ; // enable the chip select + + // send the command to the ADS7846 + for (i=0; i<8; i++ ) { + if (cmd & 0x80) + SDI_H(); + else + SDI_L(); // prepare the data on line sdi OR din + + SCLK_H() ; // clk in the data + cmd <<= 1 ; + SCLK_L() ; + } + + SDI_L(); + while (BUSY()) + ; + + // now read returned data + for (i=0 ; i<16 ; i++ ) { + SCLK_L() ; + + if (i < 12) { + val <<= 1 ; + val |= DOUT(); + } + SCLK_H() ; + } + + SCLK_L() ; + CS_H() ; // disable the chip select + + return val; +} + +static int inn1510_ts_penup(void) +{ + return ((fpga_ts_read() & FPGA_TS_BPENUP) ? 0 : 1) ; +} + +static int __init inn1510_ts_probe(struct omap_ts_t *ts) +{ + if (!cpu_is_omap15xx() || !machine_is_omap_innovator()) + return -ENODEV; + + ts->irq = OMAP1510_INT_FPGA_TS; + ts->irq_type = 0; + + return 0; +} + +static void inn1510_ts_read(u16 *data) +{ + unsigned int Rt = 0; + + data[0] = ads7846_do(MEASURE_12BIT_X); + data[1] = ads7846_do(MEASURE_12BIT_Y); + data[2] = ads7846_do(MEASURE_12BIT_Z1); + data[3] = ads7846_do(MEASURE_12BIT_Z2); + + // Calculate touch pressure resistance + if (data[2]) { + Rt = (X_PLATE_OHMS * (u32)data[0] * + ((u32)data[3] - (u32)data[2])) / (u32)data[2]; + + Rt = (Rt + 2048) >> 12; // round up to nearest ohm + } + + data[2] = Rt; +} + +static void inn1510_ts_enable(void) +{ + +} + +static void inn1510_ts_disable(void) +{ + +} + +#ifdef MODULE +static void __exit inn1510_ts_remove(void) +{ + /* Nothing to do here */ +} +#endif diff --git a/drivers/input/touchscreen/omap/ts_osk.c b/drivers/input/touchscreen/omap/ts_osk.c new file mode 100644 index 00000000000..9531fcdb95a --- /dev/null +++ b/drivers/input/touchscreen/omap/ts_osk.c @@ -0,0 +1,158 @@ +/* + * ts_osk.c - touchscreen support for OMAP OSK board + * + * Copyright 2002 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or source@mvista.com + * + * The touchscreen hardware on the OSK uses OMAP5912 uWire interface, + * GPIO4 (/PENIRQ) and GPIO6 (BUSY) to connect to an ADS7846 + * touch screen controller. GPIO6 doesn't seem to be necessary here. + * + * 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 "../drivers/ssi/omap-uwire.h" + +#include "omap_ts.h" +#include "ads7846.h" + +// /PENIRQ on GPIO4 on OSK +#define PEN_IRQ OMAP_GPIO_IRQ(4) + +// ADS7846 is on OSK uWire CS 0 +#define ADS7846_UWIRE_CS 0 +#define UWIRE_LEAVE_CS 1 + +#define X_PLATE_OHMS 419 +#define Y_PLATE_OHMS 486 + +static int osk_ts_penup(void); +static int osk_ts_probe(struct omap_ts_t *ts); +static void osk_ts_read(u16 * data); +static void osk_ts_enable(void); +static void osk_ts_disable(void); +#ifdef MODULE +static void osk_ts_remove(void); +#endif + +struct ts_device osk_ts = { + .probe = osk_ts_probe, + .read = osk_ts_read, + .enable = osk_ts_enable, + .disable = osk_ts_disable, + .remove = __exit_p(osk_ts_remove), + .penup = osk_ts_penup, +}; + +static u16 ads7846_do(u8 cmd) +{ + u16 val = 0; + + // send the command to the ADS7846, leave CS active after this + omap_uwire_data_transfer(ADS7846_UWIRE_CS, cmd, 8, 0, NULL, UWIRE_LEAVE_CS); + + // now read returned data + omap_uwire_data_transfer(ADS7846_UWIRE_CS, 0, 0, 16, &val, !UWIRE_LEAVE_CS); + + return val; +} + +static int osk_ts_penup(void) +{ + return (omap_get_gpio_datain(4)); +} + +static int __init osk_ts_probe(struct omap_ts_t *ts) +{ +#ifdef CONFIG_OMAP_OSK_MISTRAL + if (!machine_is_omap_osk()) + return -ENODEV; + + /* Configure GPIO4 (pin M17 ZDY) as /PENIRQ interrupt input */ + omap_cfg_reg(P20_1610_GPIO4); + omap_request_gpio(4); + omap_set_gpio_direction(4, 1); + ts->irq_type = SA_TRIGGER_FALLING; + + ts->irq = PEN_IRQ; + + /* Configure uWire interface. ADS7846 is on CS0 */ + omap_uwire_configure_mode(ADS7846_UWIRE_CS, UWIRE_READ_RISING_EDGE | + UWIRE_WRITE_RISING_EDGE | + UWIRE_CS_ACTIVE_LOW | + UWIRE_FREQ_DIV_2); + + /* FIXME verify there's really a Mistral board: + * see if the AD7846 chip responds. + */ + + /* NOTE: no VREF; must ignore the temp, VBAT, and AUX sensors */ + return 0; +#else + return -ENODEV; +#endif +} + +static void osk_ts_read(u16 *data) +{ + unsigned int Rt = 0; + + data[0] = ads7846_do(MEASURE_12BIT_X); + data[1] = ads7846_do(MEASURE_12BIT_Y); + data[2] = ads7846_do(MEASURE_12BIT_Z1); + data[3] = ads7846_do(MEASURE_12BIT_Z2); + + // Calculate touch pressure resistance + if (data[2]) { + Rt = (X_PLATE_OHMS * (u32)data[0] * + ((u32)data[3] - (u32)data[2])) / (u32)data[2]; + + Rt = (Rt + 2048) >> 12; // round up to nearest ohm + } + + /* + * Raw OSK touchscreen data values are between ~4000 and + * ~60000. This seems to be to large for calibration + * systems (e.g. tslib). Make the values smaller. + */ + data[0] = data[0] >> 4; + data[1] = data[1] >> 4; + + data[2] = Rt; +} + +static void osk_ts_enable(void) +{ + +} + +static void osk_ts_disable(void) +{ + +} + +#ifdef MODULE +static void __exit osk_ts_remove(void) +{ + omap_free_gpio(4); +} +#endif diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index d82c8a30ba4..f33a35d207a 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -353,4 +353,6 @@ config VIDEO_DECODER Say Y here to compile drivers for SAA7115, SAA7127 and CX25840 video decoders. +source drivers/media/video/omap/Kconfig + endmenu diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index faf728366c4..93cfb54d2bb 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -60,6 +60,7 @@ obj-$(CONFIG_VIDEO_BTCX) += btcx-risc.o obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o +obj-$(CONFIG_VIDEO_OMAP_CAMERA) += omap/ obj-$(CONFIG_VIDEO_DECODER) += saa7115.o cx25840/ saa7127.o diff --git a/drivers/media/video/omap/Kconfig b/drivers/media/video/omap/Kconfig new file mode 100644 index 00000000000..809193b9be0 --- /dev/null +++ b/drivers/media/video/omap/Kconfig @@ -0,0 +1,12 @@ +config VIDEO_OMAP_CAMERA + tristate "OMAP Camera support (EXPERIMENTAL)" + select VIDEO_BUF + depends on VIDEO_DEV && (ARCH_OMAP16XX || ARCH_OMAP24XX) + help + V4L2 camera driver support for OMAP1/2 based boards. + +config VIDEO_CAMERA_SENSOR_OV9640 + tristate "OV9640 sensor support" + depends on VIDEO_OMAP_CAMERA + help + OmniVision 9640 camera sensor support diff --git a/drivers/media/video/omap/Makefile b/drivers/media/video/omap/Makefile new file mode 100644 index 00000000000..c4c5a81b0d4 --- /dev/null +++ b/drivers/media/video/omap/Makefile @@ -0,0 +1,13 @@ +# Makefile for OMAP1/2 camera driver + +obj-$(CONFIG_VIDEO_OMAP_CAMERA) += omapcamera.o +obj-$(CONFIG_VIDEO_CAMERA_SENSOR_OV9640) += sensor_ov9640.o + +objs-yy := camera_core.o + +objs-y$(CONFIG_ARCH_OMAP16XX) += omap16xxcam.o +objs-y$(CONFIG_MACH_OMAP_H3) += h3_sensor_power.o + +omapcamera-objs := $(objs-yy) + +EXTRA_CFLAGS = -I$(src)/.. diff --git a/drivers/media/video/omap/camera_core.c b/drivers/media/video/omap/camera_core.c new file mode 100644 index 00000000000..12c1fd1735f --- /dev/null +++ b/drivers/media/video/omap/camera_core.c @@ -0,0 +1,1188 @@ +/* + * drivers/media/video/omap/camera_core.c + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * Video-for-Linux (Version 2) camera capture driver for + * the OMAP H2 and H3 camera controller. + * + * Adapted from omap24xx driver written by Andy Lowe (source@mvista.com) + * Copyright (C) 2003-2004 MontaVista Software, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * 27/03/05 Vladimir Barinov - Added support for power management + */ + +#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 "sensor_if.h" +#include "camera_hw_if.h" +#include "camera_core.h" + +struct camera_device *camera_dev; +extern struct camera_sensor camera_sensor_if; +extern struct camera_hardware camera_hardware_if; + +static void camera_core_sgdma_process(struct camera_device *cam); + +/* module parameters */ +static int video_nr = -1; /* video device minor (-1 ==> auto assign) */ + +/* Maximum amount of memory to use for capture buffers. + * Default is 4800KB, enough to double-buffer SXGA. + */ +static int capture_mem = 1280*960*2*2; + +/*Size of video overlay framebuffer. This determines the maximum image size + *that can be previewed. Default is 600KB, enough for sxga. + */ +static int overlay_mem = 640*480*2; + + +/* DMA completion routine for the scatter-gather DMA fragments. */ +/* This function is called when a scatter DMA fragment is completed */ +static void +camera_core_callback_sgdma(void *arg1, void *arg2) +{ + struct camera_device *cam = (struct camera_device *)arg1; + int sgslot = (int)arg2; + + struct sgdma_state *sgdma; + + spin_lock(&cam->sg_lock); + sgdma = cam->sgdma + sgslot; + if (!sgdma->queued_sglist) + { + spin_unlock(&cam->sg_lock); + printk(KERN_ERR CAM_NAME ": SGDMA completed when none queued\n"); + return; + } + if (!--sgdma->queued_sglist) { + /* queue for this sglist is empty so check whether transfer + ** of the frame has been completed */ + if (sgdma->next_sglist == sgdma->sglen) { + dma_callback_t callback = sgdma->callback; + void *arg = sgdma->arg; + /* all done with this sglist */ + cam->free_sgdma++; + if (callback) { + spin_unlock(&cam->sg_lock); + (*callback)(cam, arg); + camera_core_sgdma_process(cam); + return; + } + } + } + spin_unlock(&cam->sg_lock); + camera_core_sgdma_process(cam); + + return; +} + +static void +camera_core_sgdma_init(struct camera_device *cam) +{ + int sg; + + /* Initialize the underlying camera DMA */ + cam->cam_hardware->init_dma(cam->hardware_data); + spin_lock_init(&cam->sg_lock); + + cam->free_sgdma = NUM_SG_DMA; + cam->next_sgdma = 0; + for (sg = 0; sg < NUM_SG_DMA; sg++) { + cam->sgdma[sg].sglen = 0; + cam->sgdma[sg].next_sglist = 0; + cam->sgdma[sg].queued_sglist = 0; + cam->sgdma[sg].csr = 0; + cam->sgdma[sg].callback = NULL; + cam->sgdma[sg].arg = NULL; + } +} + +/* + * Process the scatter-gather DMA queue by starting queued transfers + * This function is called to program the dma to start the transfer of an image. + */ +static void +camera_core_sgdma_process(struct camera_device *cam) +{ + unsigned long irqflags; + int queued_sgdma, sgslot; + struct sgdma_state *sgdma; + const struct scatterlist *sglist; + + spin_lock_irqsave(&cam->sg_lock, irqflags); + if (1 == cam->in_use) { + spin_unlock_irqrestore(&cam->sg_lock, irqflags); + return; + } + cam->in_use = 1; + spin_unlock_irqrestore(&cam->sg_lock, irqflags); + + queued_sgdma = NUM_SG_DMA - cam->free_sgdma; + sgslot = (cam->next_sgdma + cam->free_sgdma) % (NUM_SG_DMA); + while (queued_sgdma > 0) { + sgdma = cam->sgdma + sgslot; + while (sgdma->next_sglist < sgdma->sglen) { + sglist = sgdma->sglist + sgdma->next_sglist; + if (cam->cam_hardware->start_dma(sgdma, camera_core_callback_sgdma, + (void *)cam, (void *)sgslot, cam->hardware_data)) { + /* dma start failed */ + cam->in_use = 0; + return; + } + else { + /* dma start successful */ + sgdma->next_sglist ++; + sgdma->queued_sglist ++; + } + } + queued_sgdma-- ; + sgslot = (sgslot + 1) % (NUM_SG_DMA); + } + + cam->in_use = 0; +} + +/* Queue a scatter-gather DMA transfer from the camera to memory. + * Returns zero if the transfer was successfully queued, or + * non-zero if all of the scatter-gather slots are already in use. + */ +static int +camera_core_sgdma_queue(struct camera_device *cam, + const struct scatterlist *sglist, int sglen, dma_callback_t callback, + void *arg) +{ + unsigned long irqflags; + struct sgdma_state *sgdma; + + if ((sglen < 0) || ((sglen > 0) & !sglist)) + return -EINVAL; + + spin_lock_irqsave(&cam->sg_lock, irqflags); + + if (!cam->free_sgdma) { + spin_unlock_irqrestore(&cam->sg_lock, irqflags); + return -EBUSY; + } + + sgdma = cam->sgdma + cam->next_sgdma; + + sgdma->sglist = sglist; + sgdma->sglen = sglen; + sgdma->next_sglist = 0; + sgdma->queued_sglist = 0; + sgdma->csr = 0; + sgdma->callback = callback; + sgdma->arg = arg; + + cam->next_sgdma = (cam->next_sgdma + 1) % (NUM_SG_DMA); + cam->free_sgdma--; + + spin_unlock_irqrestore(&cam->sg_lock, irqflags); + + camera_core_sgdma_process(cam); + + return 0; +} + + +/* -------------------overlay routines ------------------------------*/ +/* callback routine for overlay DMA completion. We just start another DMA + * transfer unless overlay has been turned off + */ + +static void +camera_core_overlay_callback(void *arg1, void *arg) +{ + struct camera_device *cam = (struct camera_device *)arg1; + int err; + unsigned long irqflags; + int i, j; + int count, index; + unsigned char *fb_buf = phys_to_virt((unsigned long)camera_dev->fbuf.base); + + spin_lock_irqsave(&cam->overlay_lock, irqflags); + + if (!cam->previewing || cam->overlay_cnt == 0) { + spin_unlock_irqrestore(&cam->overlay_lock, irqflags); + return; + } + + --cam->overlay_cnt; + sg_dma_address(&cam->overlay_sglist) = cam->overlay_base_phys; + sg_dma_len(&cam->overlay_sglist) = cam->pix.sizeimage; + + count = 0; + j = ((cam->pix.width - 1) * cam->fbuf.fmt.bytesperline); + for (i = 0 ; i < cam->pix.sizeimage; i += cam->pix.bytesperline) { + for (index = 0; index < cam->pix.bytesperline; index++) { + fb_buf[j] = *(((unsigned char *) cam->overlay_base) + + i + index); + index++; + fb_buf[j + 1] = *(((unsigned char *) cam->overlay_base) + i + index); + j = j - cam->fbuf.fmt.bytesperline; + } + count += 2; + j = ((cam->pix.width - 1) * cam->fbuf.fmt.bytesperline) + count; + } + + while (cam->overlay_cnt < 2) { + err = camera_core_sgdma_queue(cam, &cam->overlay_sglist, 1, + camera_core_overlay_callback, NULL); + if (err) + break; + ++cam->overlay_cnt; + } + + spin_unlock_irqrestore(&cam->overlay_lock, irqflags); + +} + + +static void +camera_core_start_overlay(struct camera_device *cam) +{ + int err; + unsigned long irqflags; + + if (!cam->previewing) + return; + + spin_lock_irqsave(&cam->overlay_lock, irqflags); + + sg_dma_address(&cam->overlay_sglist) = cam->overlay_base_phys; + sg_dma_len(&cam->overlay_sglist)= cam->pix.sizeimage; + while (cam->overlay_cnt < 2) { + err = camera_core_sgdma_queue(cam, &cam->overlay_sglist, 1, + camera_core_overlay_callback, NULL); + if (err) + break; + ++cam->overlay_cnt; + } + + spin_unlock_irqrestore(&cam->overlay_lock, irqflags); +} + +/* ------------------ videobuf_queue_ops ---------------------------------------- */ + +/* This routine is called from interrupt context when a scatter-gather DMA + * transfer of a videobuf_buffer completes. + */ +static void +camera_core_vbq_complete(void *arg1, void *arg) +{ + struct camera_device *cam = (struct camera_device *)arg1; + struct videobuf_buffer *vb = (struct videobuf_buffer *)arg; + + spin_lock(&cam->vbq_lock); + + do_gettimeofday(&vb->ts); + vb->field_count = cam->field_count; + cam->field_count += 2; + vb->state = STATE_DONE; + + wake_up(&vb->done); + + spin_unlock(&cam->vbq_lock); +} + +static void +camera_core_vbq_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + videobuf_waiton(vb, 0, 0); + videobuf_dma_pci_unmap(NULL, &vb->dma); + videobuf_dma_free(&vb->dma); + + vb->state = STATE_NEEDS_INIT; +} + +/* Limit the number of available kernel image capture buffers based on the + * number requested, the currently selected image size, and the maximum + * amount of memory permitted for kernel capture buffers. + */ +static int +camera_core_vbq_setup(struct videobuf_queue *q, unsigned int *cnt, unsigned int *size) +{ + struct camera_device *cam = q->priv_data; + + if (*cnt <= 0) + *cnt = VIDEO_MAX_FRAME; /* supply a default number of buffers */ + + if (*cnt > VIDEO_MAX_FRAME) + *cnt = VIDEO_MAX_FRAME; + + spin_lock(&cam->img_lock); + *size = cam->pix.sizeimage; + spin_unlock(&cam->img_lock); + + while (*size * *cnt > capture_mem) + (*cnt)--; + + return 0; +} + +static int +camera_core_vbq_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct camera_device *cam = q->priv_data; + int err = 0; + + spin_lock(&cam->img_lock); + if (cam->pix.sizeimage > vb->bsize) { + spin_unlock(&cam->img_lock); + return -EINVAL; + } + vb->size = cam->pix.sizeimage; + vb->width = cam->pix.width; + vb->height = cam->pix.height; + vb->field = field; + spin_unlock(&cam->img_lock); + + if (vb->state == STATE_NEEDS_INIT) + err = videobuf_iolock(NULL, vb, NULL); + + if (!err) + vb->state = STATE_PREPARED; + else + camera_core_vbq_release (q, vb); + + return err; +} + +static void +camera_core_vbq_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct camera_device *cam = q->priv_data; + enum videobuf_state state = vb->state; + int err; + + vb->state = STATE_QUEUED; + err = camera_core_sgdma_queue(cam, vb->dma.sglist, vb->dma.sglen, + camera_core_vbq_complete, vb); + if (err) { + /* Oops. We're not supposed to get any errors here. The only + * way we could get an error is if we ran out of scatter-gather + * DMA slots, but we are supposed to have at least as many + * scatter-gather DMA slots as video buffers so that can't + * happen. + */ + printk(KERN_DEBUG CAM_NAME + ": Failed to queue a video buffer for SGDMA\n"); + vb->state = state; + } +} + +/* ------------------ videobuf_queue_ops ---------------------------------------- */ + +static int +camera_core_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + void *arg) +{ + struct camera_fh *fh = file->private_data; + struct camera_device *cam = fh->cam; + int err; + + switch (cmd) { + case VIDIOC_ENUMINPUT: + { + /* default handler assumes 1 video input (the camera) */ + struct v4l2_input *input = (struct v4l2_input *)arg; + int index = input->index; + + memset(input, 0, sizeof(*input)); + input->index = index; + + if (index > 0) + return -EINVAL; + + strlcpy(input->name, "camera", sizeof(input->name)); + input->type = V4L2_INPUT_TYPE_CAMERA; + + return 0; + } + + case VIDIOC_G_INPUT: + { + unsigned int *input = arg; + *input = 0; + + return 0; + } + + case VIDIOC_S_INPUT: + { + unsigned int *input = arg; + + if (*input > 0) + return -EINVAL; + + return 0; + } + + case VIDIOC_ENUM_FMT: + { + struct v4l2_fmtdesc *fmt = arg; + return cam->cam_sensor->enum_pixformat(fmt, cam->sensor_data); + } + + case VIDIOC_TRY_FMT: + { + struct v4l2_format *fmt = arg; + return cam->cam_sensor->try_format(&fmt->fmt.pix, cam->sensor_data); + + } + + case VIDIOC_G_FMT: + { + struct v4l2_format *fmt = arg; + + /* get the current format */ + memset(&fmt->fmt.pix, 0, sizeof (fmt->fmt.pix)); + fmt->fmt.pix = cam->pix; + + return 0; + } + + case VIDIOC_S_FMT: + { + struct v4l2_format *fmt = arg; + unsigned int temp_sizeimage = 0; + + temp_sizeimage = cam->pix.sizeimage; + cam->cam_sensor->try_format(&fmt->fmt.pix, cam->sensor_data); + cam->pix = fmt->fmt.pix; + + cam->xclk = cam->cam_sensor->calc_xclk(&cam->pix, + &cam->nominal_timeperframe, cam->sensor_data); + cam->cparm.timeperframe = cam->nominal_timeperframe; + cam->xclk = cam->cam_hardware->set_xclk(cam->xclk, cam->hardware_data); + return cam->cam_sensor->configure(&cam->pix, cam->xclk, + &cam->cparm.timeperframe, cam->sensor_data); + } + + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *qc = arg; + return cam->cam_sensor->query_control(qc, cam->sensor_data); + } + + case VIDIOC_G_CTRL: + { + struct v4l2_control *vc = arg; + return cam->cam_sensor->get_control(vc, cam->sensor_data); + } + + case VIDIOC_S_CTRL: + { + struct v4l2_control *vc = arg; + return cam->cam_sensor->set_control(vc, cam->sensor_data); + } + + case VIDIOC_QUERYCAP: + { + struct v4l2_capability *cap = + (struct v4l2_capability *) arg; + + memset(cap, 0, sizeof(*cap)); + strlcpy(cap->driver, CAM_NAME, sizeof(cap->driver)); + strlcpy(cap->card, cam->vfd->name, sizeof(cap->card)); + cap->bus_info[0] = '\0'; + cap->version = KERNEL_VERSION(0, 0, 0); + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_OVERLAY | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + return 0; + } + + case VIDIOC_G_FBUF: /* Get the frame buffer parameters */ + { + struct v4l2_framebuffer *fbuf = + (struct v4l2_framebuffer *) arg; + + spin_lock(&cam->img_lock); + *fbuf = cam->fbuf; + spin_unlock(&cam->img_lock); + return 0; + } + + case VIDIOC_S_FBUF: /* set the frame buffer parameters */ + { + struct v4l2_framebuffer *fbuf = + (struct v4l2_framebuffer *) arg; + + spin_lock(&cam->img_lock); + if (cam->previewing) { + spin_unlock(&cam->img_lock); + return -EBUSY; + } + cam->fbuf.base = fbuf->base; + cam->fbuf.fmt = fbuf->fmt; + + spin_unlock(&cam->img_lock); + return 0; + } + + case VIDIOC_OVERLAY: + { + int enable = *((int *) arg); + + /* + * check whether the capture format and + ** the display format matches + * return failure if they are different + */ + if (cam->pix.pixelformat != cam->fbuf.fmt.pixelformat) + { + return -EINVAL; + } + + /* If the camera image size is greater + ** than LCD size return failure */ + if ((cam->pix.width > cam->fbuf.fmt.height) || + (cam->pix.height > cam->fbuf.fmt.width)) + { + return -EINVAL; + } + + if (!cam->previewing && enable) + { + cam->previewing = fh; + cam->overlay_cnt = 0; + camera_core_start_overlay(cam); + } + else if (!enable) + { + cam->previewing = NULL; + } + + return 0; + } + + case VIDIOC_REQBUFS: + return videobuf_reqbufs(&fh->vbq, arg); + + case VIDIOC_QUERYBUF: + return videobuf_querybuf(&fh->vbq, arg); + + case VIDIOC_QBUF: + return videobuf_qbuf(&fh->vbq, arg); + + case VIDIOC_DQBUF: + return videobuf_dqbuf(&fh->vbq, arg, + file->f_flags & O_NONBLOCK); + + case VIDIOC_STREAMON: + { + spin_lock(&cam->img_lock); + + if (cam->streaming || cam->reading) { + spin_unlock(&cam->img_lock); + return -EBUSY; + } + else { + cam->streaming = fh; + /* FIXME: start camera interface */ + } + + spin_unlock(&cam->img_lock); + + return videobuf_streamon(&fh->vbq); + } + case VIDIOC_STREAMOFF: + { + err = videobuf_streamoff(&fh->vbq); + if (err < 0) + return err; + + spin_lock(&cam->img_lock); + if (cam->streaming == fh) { + cam->streaming = NULL; + /* FIXME: stop camera interface */ + } + spin_unlock(&cam->img_lock); + return 0; + } + case VIDIOC_ENUMSTD: + case VIDIOC_G_STD: + case VIDIOC_S_STD: + case VIDIOC_QUERYSTD: + { + /* Digital cameras don't have an analog video standard, + * so we don't need to implement these ioctls. + */ + return -EINVAL; + } + case VIDIOC_G_AUDIO: + case VIDIOC_S_AUDIO: + case VIDIOC_G_AUDOUT: + case VIDIOC_S_AUDOUT: + { + /* we don't have any audio inputs or outputs */ + return -EINVAL; + } + + case VIDIOC_G_JPEGCOMP: + case VIDIOC_S_JPEGCOMP: + { + /* JPEG compression is not supported */ + return -EINVAL; + } + + case VIDIOC_G_TUNER: + case VIDIOC_S_TUNER: + case VIDIOC_G_MODULATOR: + case VIDIOC_S_MODULATOR: + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: + { + /* we don't have a tuner or modulator */ + return -EINVAL; + } + + case VIDIOC_ENUMOUTPUT: + case VIDIOC_G_OUTPUT: + case VIDIOC_S_OUTPUT: + { + /* we don't have any video outputs */ + return -EINVAL; + } + + default: + { + /* unrecognized ioctl */ + return -ENOIOCTLCMD; + } + } + return 0; +} + +/* + * file operations + */ + +static unsigned +int camera_core_poll(struct file *file, struct poll_table_struct *wait) +{ + return -EINVAL; +} + +/* ------------------------------------------------------------ */ +/* callback routine for read DMA completion. We just start another DMA + * transfer unless overlay has been turned off + */ +static void +camera_core_capture_callback(void *arg1, void *arg) +{ + struct camera_device *cam = (struct camera_device *)arg1; + int err; + unsigned long irqflags; + static int done = 0; + + spin_lock_irqsave(&cam->capture_lock, irqflags); + if (!cam->reading) + { + done = 0; + cam->capture_started = 0; + spin_unlock_irqrestore(&cam->capture_lock, irqflags); + return; + } + + if (done < 14) { + ++done; + sg_dma_address(&cam->capture_sglist) = cam->capture_base_phys; + sg_dma_len(&cam->capture_sglist) = cam->pix.sizeimage; + err = camera_core_sgdma_queue(cam, &cam->capture_sglist, 1, + camera_core_capture_callback, NULL); + } else { + cam->capture_completed = 1; + if (cam->reading) + { + /* Wake up any process which are waiting for the + ** DMA to complete */ + wake_up_interruptible(&camera_dev->new_video_frame); + sg_dma_address(&cam->capture_sglist) = cam->capture_base_phys; + sg_dma_len(&cam->capture_sglist) = cam->pix.sizeimage; + err = camera_core_sgdma_queue(cam, &cam->capture_sglist, 1, + camera_core_capture_callback, NULL); + } + } + + spin_unlock_irqrestore(&cam->capture_lock, irqflags); +} + + +static ssize_t +camera_core_read(struct file *file, char *data, size_t count, loff_t *ppos) +{ + struct camera_fh *fh = file->private_data; + struct camera_device *cam = fh->cam; + int err; + unsigned long irqflags; + long timeout; +#if 0 /* use video_buf to do capture */ + int i; + for (i = 0; i < 14; i++) + videobuf_read_one(file, &fh->vbq, data, count, ppos); + i = videobuf_read_one(file, &fh->vbq, data, count, ppos); + return i; +#endif + + if (!cam->capture_base) { + cam->capture_base = (unsigned long)dma_alloc_coherent(NULL, + cam->pix.sizeimage, + (dma_addr_t *) &cam->capture_base_phys, + GFP_KERNEL | GFP_DMA); + } + if (!cam->capture_base) { + printk(KERN_ERR CAM_NAME + ": cannot allocate capture buffer\n"); + return 0; + } + + spin_lock_irqsave(&cam->capture_lock, irqflags); + cam->reading = fh; + cam->capture_started = 1; + sg_dma_address(&cam->capture_sglist) = cam->capture_base_phys; + sg_dma_len(&cam->capture_sglist)= cam->pix.sizeimage; + spin_unlock_irqrestore(&cam->capture_lock, irqflags); + + err = camera_core_sgdma_queue(cam, &cam->capture_sglist, 1, + camera_core_capture_callback, NULL); + + /* Wait till DMA is completed */ + timeout = HZ * 10; + cam->capture_completed = 0; + while (cam->capture_completed == 0) { + timeout = interruptible_sleep_on_timeout + (&cam->new_video_frame, timeout); + if (timeout == 0) { + printk(KERN_ERR CAM_NAME ": timeout waiting video frame\n"); + return -EIO; /* time out */ + } + } + /* copy the data to the user buffer */ + err = copy_to_user(data, (void *)cam->capture_base, cam->pix.sizeimage); + return (cam->pix.sizeimage - err); + +} + +static int +camera_core_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct camera_fh *fh = file->private_data; + + return videobuf_mmap_mapper(&fh->vbq, vma); +} + +static int +camera_core_ioctl(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + + return video_usercopy(inode, file, cmd, arg, camera_core_do_ioctl); +} + +static int +camera_core_release(struct inode *inode, struct file *file) +{ + struct camera_fh *fh = file->private_data; + struct camera_device *cam = fh->cam; + + file->private_data = NULL; + kfree(fh); + + spin_lock(&cam->img_lock); + if (cam->previewing == fh) { + cam->previewing = NULL; + } + if (cam->streaming == fh) { + cam->streaming = NULL; + } + if (cam->reading == fh) { + cam->reading = NULL; + } + spin_unlock(&cam->img_lock); + + camera_dev->cam_hardware->finish_dma(cam->hardware_data); + + if (cam->capture_base) { + dma_free_coherent(NULL, cam->pix.sizeimage, + (void *)cam->capture_base, + cam->capture_base_phys); + cam->capture_base = 0; + cam->capture_base_phys = 0; + } + if (fh->vbq.read_buf) { + camera_core_vbq_release(&fh->vbq, fh->vbq.read_buf); + kfree(fh->vbq.read_buf); + } + + cam->cam_hardware->close(cam->hardware_data); + cam->active = 0; + return 0; +} + +static int +camera_core_open(struct inode *inode, struct file *file) +{ + int minor = iminor(inode); + struct camera_device *cam = camera_dev; + struct camera_fh *fh; + + if (!cam || !cam->vfd || (cam->vfd->minor != minor)) + return -ENODEV; + + /* allocate per-filehandle data */ + fh = kmalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + file->private_data = fh; + fh->cam = cam; + fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + spin_lock(&cam->img_lock); + if (cam->active == 1) { + printk (KERN_ERR CAM_NAME ": Camera device Active\n"); + spin_unlock(&cam->img_lock); + return -EPERM; + } + cam->active = 1; + spin_unlock(&cam->img_lock); + + videobuf_queue_init(&fh->vbq, &cam->vbq_ops, NULL, &cam->vbq_lock, + fh->type, V4L2_FIELD_NONE, sizeof(struct videobuf_buffer), fh); + + cam->capture_completed = 0; + cam->capture_started = 0; + + if (cam->cam_hardware->open(cam->hardware_data)) + { + printk (KERN_ERR CAM_NAME ": Camera IF configuration failed\n"); + cam->active = 0; + return -ENODEV; + } + + cam->xclk = cam->cam_hardware->set_xclk(cam->xclk, cam->hardware_data); + /* program the sensor for the capture format and rate */ + if (cam->cam_sensor->configure(&cam->pix, cam->xclk, + &cam->cparm.timeperframe, cam->sensor_data)) + { + printk (KERN_ERR CAM_NAME ": Camera sensor configuration failed\n"); + cam->cam_hardware->close(cam->hardware_data); + cam->active = 0; + return -ENODEV; + } + + return 0; +} + +#ifdef CONFIG_PM +static int camera_core_suspend(struct device *dev, pm_message_t state) +{ + struct camera_device *cam = dev_get_drvdata(dev); + int ret = 0; + + spin_lock(&cam->img_lock); + if (cam->active) { + cam->cam_hardware->close(cam->hardware_data); + } + cam->cam_sensor->power_off(cam->sensor_data); + spin_unlock(&cam->img_lock); + return ret; +} + +static int camera_core_resume(struct device *dev) +{ + struct camera_device *cam = dev_get_drvdata(dev); + int ret = 0; + + spin_lock(&cam->img_lock); + cam->cam_sensor->power_on(cam->sensor_data); + if (cam->active) { + cam->capture_completed = 1; + cam->cam_hardware->open(cam->hardware_data); + cam->cam_hardware->set_xclk(cam->xclk, cam->hardware_data); + + cam->cam_sensor->configure(&cam->pix, cam->xclk, + &cam->cparm.timeperframe, + cam->sensor_data); + camera_core_sgdma_process(cam); + } + spin_unlock(&cam->img_lock); + + return ret; +} +#endif /* CONFIG_PM */ + +static struct file_operations camera_core_fops = +{ + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = camera_core_read, + .poll = camera_core_poll, + .ioctl = camera_core_ioctl, + .mmap = camera_core_mmap, + .open = camera_core_open, + .release = camera_core_release, +}; + +static struct device_driver camera_core_driver = { + .name = CAM_NAME, + .bus = &platform_bus_type, + .probe = NULL, + .remove = NULL, +#ifdef CONFIG_PM + .suspend = camera_core_suspend, + .resume = camera_core_resume, +#endif + .shutdown = NULL, +}; + +static struct platform_device camera_core_device = { + .name = CAM_NAME, + .dev = { + .release = NULL, + }, + .id = 0, +}; + +void +camera_core_cleanup(void) +{ + struct camera_device *cam = camera_dev; + struct video_device *vfd; + + if (!cam) + return; + + vfd = cam->vfd; + if (vfd) { + if (vfd->minor == -1) { + /* The device never got registered, so release the + ** video_device struct directly + */ + video_device_release(vfd); + } else { + /* The unregister function will release the video_device + ** struct as well as unregistering it. + */ + video_unregister_device(vfd); + driver_unregister(&camera_core_driver); + platform_device_unregister(&camera_core_device); + } + cam->vfd = NULL; + } + if (cam->overlay_base) { + dma_free_coherent(NULL, cam->overlay_size, + (void *)cam->overlay_base, + cam->overlay_base_phys); + cam->overlay_base = 0; + } + cam->overlay_base_phys = 0; + + cam->cam_sensor->cleanup(cam->sensor_data); + cam->cam_hardware->cleanup(cam->hardware_data); + kfree(cam); + camera_dev = NULL; + + return; +} + + +int __init +camera_core_init(void) +{ + struct camera_device *cam; + struct video_device *vfd; + + cam = kzalloc(sizeof(struct camera_device), GFP_KERNEL); + if (!cam) { + printk(KERN_ERR CAM_NAME ": could not allocate memory\n"); + goto init_error; + } + + /* Save the pointer to camera device in a global variable */ + camera_dev = cam; + + /* initialize the video_device struct */ + vfd = cam->vfd = video_device_alloc(); + if (!vfd) { + printk(KERN_ERR CAM_NAME + ": could not allocate video device struct\n"); + goto init_error; + } + + vfd->release = video_device_release; + + strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name)); + vfd->type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | VID_TYPE_CHROMAKEY; + + /* need to register for a VID_HARDWARE_* ID in videodev.h */ + vfd->hardware = 0; + vfd->fops = &camera_core_fops; + video_set_drvdata(vfd, cam); + vfd->minor = -1; + + /* initialize the videobuf queue ops */ + cam->vbq_ops.buf_setup = camera_core_vbq_setup; + cam->vbq_ops.buf_prepare = camera_core_vbq_prepare; + cam->vbq_ops.buf_queue = camera_core_vbq_queue; + cam->vbq_ops.buf_release = camera_core_vbq_release; + + /* initilize the overlay interface */ + cam->overlay_size = overlay_mem; + if (cam->overlay_size > 0) + { + cam->overlay_base = (unsigned long) dma_alloc_coherent(NULL, + cam->overlay_size, + (dma_addr_t *) &cam->overlay_base_phys, + GFP_KERNEL | GFP_DMA); + if (!cam->overlay_base) { + printk(KERN_ERR CAM_NAME + ": cannot allocate overlay framebuffer\n"); + goto init_error; + } + } + memset((void*)cam->overlay_base, 0, cam->overlay_size); + spin_lock_init(&cam->overlay_lock); + spin_lock_init(&cam->capture_lock); + + /*Initialise the pointer to the sensor interface and camera interface */ + cam->cam_sensor = &camera_sensor_if; + cam->cam_hardware = &camera_hardware_if; + + /* initialize the camera interface */ + cam->hardware_data = cam->cam_hardware->init(); + if (!cam->hardware_data) { + printk(KERN_ERR CAM_NAME ": cannot initialize interface hardware\n"); + goto init_error; + } + + /* initialize the spinlock used to serialize access to the image + * parameters + */ + spin_lock_init(&cam->img_lock); + + /* initialize the streaming capture parameters */ + cam->cparm.capability = V4L2_CAP_TIMEPERFRAME; + cam->cparm.readbuffers = 1; + + /* Enable the xclk output. The sensor may (and does, in the case of + * the OV9640) require an xclk input in order for its initialization + * routine to work. + */ + cam->xclk = 21000000; /* choose an arbitrary xclk frequency */ + cam->xclk = cam->cam_hardware->set_xclk(cam->xclk, cam->hardware_data); + + /* initialize the sensor and define a default capture format cam->pix */ + cam->sensor_data = cam->cam_sensor->init(&cam->pix); + if (!cam->sensor_data) { + cam->cam_hardware->disable(cam->hardware_data); + printk(KERN_ERR CAM_NAME ": cannot initialize sensor\n"); + goto init_error; + } + + printk(KERN_INFO CAM_NAME ": %s interface with %s sensor\n", + cam->cam_hardware->name, cam->cam_sensor->name); + + /* select an arbitrary default capture frame rate of 15fps */ + cam->nominal_timeperframe.numerator = 1; + cam->nominal_timeperframe.denominator = 15; + + /* calculate xclk based on the default capture format and default + * frame rate + */ + cam->xclk = cam->cam_sensor->calc_xclk(&cam->pix, + &cam->nominal_timeperframe, cam->sensor_data); + cam->cparm.timeperframe = cam->nominal_timeperframe; + + /* initialise the wait queue */ + init_waitqueue_head(&cam->new_video_frame); + + /* Initialise the DMA structures */ + camera_core_sgdma_init(cam); + + /* Disable the Camera after detection */ + cam->cam_hardware->disable(cam->hardware_data); + + dev_set_drvdata(&camera_core_device.dev, (void *)cam); + if (platform_device_register(&camera_core_device) < 0) { + printk(KERN_ERR CAM_NAME + ": could not register platform_device\n"); + goto init_error; + } + + if (driver_register(&camera_core_driver) < 0) { + printk(KERN_ERR CAM_NAME + ": could not register driver\n"); + platform_device_unregister(&camera_core_device); + goto init_error; + } + if (video_register_device(vfd, VFL_TYPE_GRABBER, video_nr) < 0) { + printk(KERN_ERR CAM_NAME + ": could not register Video for Linux device\n"); + platform_device_unregister(&camera_core_device); + driver_unregister(&camera_core_driver); + goto init_error; + } + + printk(KERN_INFO CAM_NAME + ": registered device video%d [v4l2]\n", vfd->minor); + return 0; + +init_error: + camera_core_cleanup(); + return -ENODEV; +} + +MODULE_AUTHOR("Texas Instruments."); +MODULE_DESCRIPTION("OMAP Video for Linux camera driver"); +MODULE_LICENSE("GPL"); +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr, + "Minor number for video device (-1 ==> auto assign)"); +module_param(capture_mem, int, 0); +MODULE_PARM_DESC(capture_mem, + "Maximum amount of memory for capture buffers (default 4800KB)"); + +module_init(camera_core_init); +module_exit(camera_core_cleanup); + + diff --git a/drivers/media/video/omap/camera_core.h b/drivers/media/video/omap/camera_core.h new file mode 100644 index 00000000000..9ab336fef2f --- /dev/null +++ b/drivers/media/video/omap/camera_core.h @@ -0,0 +1,155 @@ +/* + * drivers/media/video/omap/camera_core.h + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef CAMERA_CORE__H +#define CAMERA_CORE__H + +struct camera_fh; + +#include +#include + +struct camera_device; +typedef void (*dma_callback_t)(void *arg1, void *arg2); + +struct sgdma_state { + const struct scatterlist *sglist; + int sglen; /* number of sglist entries */ + int next_sglist; /* index of next sglist entry to process */ + int queued_sglist; /* number of sglist entries queued for DMA */ + unsigned long csr; /* DMA return code */ + dma_callback_t callback; + void *arg; +}; + +/* NUM_SG_DMA is the number of scatter-gather DMA transfers that can be queued. + */ +#define NUM_SG_DMA VIDEO_MAX_FRAME+2 + +/* per-device data structure */ +struct camera_device { + struct device dev; + struct video_device *vfd; + + spinlock_t overlay_lock; /* spinlock for overlay DMA counter */ + int overlay_cnt; /* count of queued overlay DMA xfers */ + struct scatterlist overlay_sglist; + unsigned long overlay_base_phys; + unsigned long overlay_base; + unsigned long overlay_size; + + spinlock_t vbq_lock; /* spinlock for videobuf queues */ + struct videobuf_queue_ops vbq_ops; /* videobuf queue operations */ + unsigned long field_count; /* field counter for videobuf_buffer */ + + /* scatter-gather DMA management */ + spinlock_t sg_lock; + int free_sgdma; /* number of free sg dma slots */ + int next_sgdma; /* index of next sg dma slot to use */ + struct sgdma_state sgdma[NUM_SG_DMA]; + char in_use; + + /* The img_lock is used to serialize access to the image parameters for + * overlay and capture. Need to use spin_lock_irq when writing to the + * reading, streaming, and previewing parameters. A regular spin_lock + * will suffice for all other cases. + */ + spinlock_t img_lock; + + /* We allow reading from at most one filehandle at a time. + * non-NULL means reading is in progress. + */ + struct camera_fh *reading; + /* We allow streaming from at most one filehandle at a time. + * non-NULL means streaming is in progress. + */ + struct camera_fh *streaming; + /* We allow previewing from at most one filehandle at a time. + * non-NULL means previewing is in progress. + */ + struct camera_fh *previewing; + + /* capture parameters (frame rate, number of buffers) */ + struct v4l2_captureparm cparm; + + /* This is the frame period actually requested by the user. */ + struct v4l2_fract nominal_timeperframe; + + /* frequency (in Hz) of camera interface xclk output */ + unsigned long xclk; + + /* Pointer to the sensor interface ops */ + struct camera_sensor *cam_sensor; + void *sensor_data; + + /* Pointer to the camera interface hardware ops */ + struct camera_hardware *cam_hardware; + void *hardware_data; + + /* pix defines the size and pixel format of the image captured by the + * sensor. This also defines the size of the framebuffers. The + * same pool of framebuffers is used for video capture and video + * overlay. These parameters are set/queried by the + * VIDIOC_S_FMT/VIDIOC_G_FMT ioctls with a CAPTURE buffer type. + */ + struct v4l2_pix_format pix; + + /* crop defines the size and offset of the video overlay source window + * within the framebuffer. These parameters are set/queried by the + * VIDIOC_S_CROP/VIDIOC_G_CROP ioctls with an OVERLAY buffer type. + * The cropping rectangle allows a subset of the captured image to be + * previewed. It only affects the portion of the image previewed, not + * captured; the entire camera image is always captured. + */ + struct v4l2_rect crop; + + /* win defines the size and offset of the video overlay target window + * within the video display. These parameters are set/queried by the + * VIDIOC_S_FMT/VIDIOC_G_FMT ioctls with an OVERLAY buffer type. + */ + struct v4l2_window win; + + /* fbuf reflects the size of the video display. It is queried with the + * VIDIOC_G_FBUF ioctl. The size of the video display cannot be + * changed with the VIDIOC_S_FBUF ioctl. + */ + struct v4l2_framebuffer fbuf; + + /* end of generic stuff, the above should be common to all omaps */ + + /* note, 2420 uses videobuf to do caprure, it is more memory efficient + we need 1710 and 2420 do capture in the same way */ + /* Variables to store the capture state */ + /* Wait till DMA is completed */ + wait_queue_head_t new_video_frame; + char capture_completed; + char capture_started; + spinlock_t capture_lock; + struct scatterlist capture_sglist; + unsigned long capture_base; + unsigned long capture_base_phys; + + char active; +}; + +/* per-filehandle data structure */ +struct camera_fh { + struct camera_device *cam; + enum v4l2_buf_type type; + struct videobuf_queue vbq; +}; + +#define CAM_NAME "omap-camera" + +#endif /* CAMERA_CORE__H */ diff --git a/drivers/media/video/omap/camera_hw_if.h b/drivers/media/video/omap/camera_hw_if.h new file mode 100644 index 00000000000..2c941b5b2a7 --- /dev/null +++ b/drivers/media/video/omap/camera_hw_if.h @@ -0,0 +1,48 @@ +/* + * drivers/media/video/omap/camera_hw_if.h + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * Camera interface to OMAP camera capture drivers + * Camera interface hardware driver should implement this interface + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef OMAP_CAMERA_HW_IF_H +#define OMAP_CAMERA_HW_IF_H + +#define LEN_HW_IF_NAME 31 + +struct sgdma_state; + +struct camera_hardware { + unsigned int version; //version of camera driver module + char name[LEN_HW_IF_NAME + 1]; + + void *(*init)(void); + int (*cleanup)(void *); + + int (*open)(void *); /* acquire h/w resources (irq,DMA), etc. */ + int (*close)(void *); /* free h/w resources, stop i/f */ + + int (*enable)(void *); + int (*disable)(void *); + + int (*abort)(void *); + + int (*set_xclk)(int, void *); + + int (*init_dma)(void *); + int (*start_dma)(struct sgdma_state *, void (*)(void *arg1, void *arg2), + void *, void *, void *); + int (*finish_dma)(void *); +}; + +#endif /* OMAP_CAMERA_HW_IF_H */ diff --git a/drivers/media/video/omap/h3_sensor_power.c b/drivers/media/video/omap/h3_sensor_power.c new file mode 100644 index 00000000000..ae76688ae2a --- /dev/null +++ b/drivers/media/video/omap/h3_sensor_power.c @@ -0,0 +1,63 @@ +/* + * drivers/media/video/omap/h3_sensor_power.c + * + * H3 sensor powerup/down functions. + * + * Author: Andy Lowe (source@mvista.com) + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2004 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include + +#include + +int h3_sensor_powerup(void); +int h3_sensor_powerdown(void); + +int +h3_sensor_powerup(void) +{ + unsigned char expa; + int err; + + /* read the current state of GPIO EXPA output */ + if (( err = read_gpio_expa(&expa, 0x27))) { + printk(KERN_ERR "Error reading GPIO EXPA \n"); + return err; + } + /* set GPIO EXPA P7 CAMERA_MOD_EN to power-up sensor */ + if ((err = write_gpio_expa(expa | 0x80, 0x27))) { + printk(KERN_ERR "Error writing to GPIO EXPA \n"); + return err; + } + return 0; +} + +int +h3_sensor_powerdown(void) +{ + unsigned char expa; + int err; + + /* read the current state of GPIO EXPA output */ + if (( err = read_gpio_expa(&expa, 0x27))) { + printk(KERN_ERR "Error reading GPIO EXPA \n"); + return err; + } + /* clear GPIO EXPA P7 CAMERA_MOD_EN to power-up sensor */ + if ((err = write_gpio_expa(expa & ~0x80, 0x27))) { + printk(KERN_ERR "Error writing to GPIO EXPA \n"); + return err; + } + return 0; +} + +EXPORT_SYMBOL(h3_sensor_powerup); +EXPORT_SYMBOL(h3_sensor_powerdown); diff --git a/drivers/media/video/omap/h3sensorpower.h b/drivers/media/video/omap/h3sensorpower.h new file mode 100644 index 00000000000..8587729cd71 --- /dev/null +++ b/drivers/media/video/omap/h3sensorpower.h @@ -0,0 +1,17 @@ +/* + * drivers/media/video/omap/h3sensorpower.h + * + * Copyright (C) 2005 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef H3SENSORPOWER_H +#define H3SENSORPOWER_H + +int h3_sensor_powerup(void); +int h3_sensor_powerdown(void); + +#endif /*H3SENSORPOWER_H*/ diff --git a/drivers/media/video/omap/omap16xxcam.c b/drivers/media/video/omap/omap16xxcam.c new file mode 100644 index 00000000000..74198af2a46 --- /dev/null +++ b/drivers/media/video/omap/omap16xxcam.c @@ -0,0 +1,583 @@ +/* + * drivers/media/video/omap/omap16xxcam.c + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * Video-for-Linux (Version 2) camera capture driver for + * the OMAP H2 and H3 camera controller. + * + * leverage some code from CEE distribution + * Copyright (C) 2003-2004 MontaVista Software, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "omap16xxcam.h" +#include "camera_hw_if.h" +#include "camera_core.h" + +#define CONF_CAMERAIF_RESET_R 5 +#define EN_PER 0 + +/* NUM_CAMDMA_CHANNELS is the number of logical channels used for + * DMA data transfer. + */ +#define NUM_CAMDMA_CHANNELS 2 + +typedef struct { + unsigned int ctrlclock; /* 00 */ + unsigned int it_status; /* 04 */ + unsigned int mode; /* 08 */ + unsigned int status; /* 0C */ + unsigned int camdata; /* 10 */ + unsigned int gpio; /* 14 */ + unsigned int peak_counter; /* 18 */ +} camera_regs_t; + +struct camdma_state { + dma_callback_t callback; + void *arg1; + void *arg2; +}; + +struct omap16xxcam { + camera_regs_t *camera_regs; + unsigned long iobase_phys; + + /* frequncy (in Hz) of camera interface functional clock (ocp_clk) */ + unsigned long ocp_clk; + + /* dma related stuff */ + spinlock_t dma_lock; + int free_dmach; + int next_dmach; + struct camdma_state camdma[NUM_CAMDMA_CHANNELS]; + int dma_channel_number1; + int dma_channel_number2; + + wait_queue_head_t vsync_wait; + + int new; +}; +static struct omap16xxcam hardware_data; + +static int omap16xxcam_set_xclk(int, void *); +static void omap16xx_cam_dma_link_callback(int, unsigned short, void *); + +/* Clears the camera data FIFO by setting RAZ_FIFO bit in MODE configuration + register. */ +static void +omap16xx_cam_clear_fifo(struct omap16xxcam *data) +{ + data->camera_regs->mode |= RAZ_FIFO; + udelay(10); + data->camera_regs->mode &= ~RAZ_FIFO; +} + +static void +omap16xx_cam_reset(struct omap16xxcam *data, int yes) +{ + if (machine_is_omap_h3()) + data->camera_regs->gpio = yes ? 0 : 1; + else + data->camera_regs->gpio = yes ? 1 : 0; +} + +static void +omap16xx_cam_init(void) +{ + /* + * FIXME - Use mux API's instead of directly writing in to MUX registers + */ + omap_writel(omap_readl(FUNC_MUX_CTRL_4) & ~(0x1ff << 21), FUNC_MUX_CTRL_4); + omap_writel(0, FUNC_MUX_CTRL_5); + omap_writel(omap_readl(PULL_DWN_CTRL_0) & ~(0x1FFF << 17), PULL_DWN_CTRL_0); + omap_writel(omap_readl(PU_PD_SEL_0) & ~(0x1FFF << 17), PU_PD_SEL_0); + + omap_writel(0xeaef, COMP_MODE_CTRL_0); + omap_writel(omap_readl(OMAP1610_RESET_CONTROL) & ~(1 << CONF_CAMERAIF_RESET_R), + OMAP1610_RESET_CONTROL); + omap_writel(omap_readl(OMAP1610_RESET_CONTROL) | (1 << CONF_CAMERAIF_RESET_R), + OMAP1610_RESET_CONTROL); + + /* Enable peripheral reset */ + omap_writew(omap_readw(ARM_RSTCT2) | (1 << EN_PER), ARM_RSTCT2); + + /* enable peripheral clock */ + if (machine_is_omap_h3()) + clk_enable(clk_get(0, "tc2_ck")); + else { + clk_enable(clk_get(0, "armper_ck")); + clk_enable(clk_get(0, "armxor_ck")); + } +} + +static void +omap16xx_cam_waitfor_syncedge(struct omap16xxcam *data, u32 edge_mask) +{ + data->camera_regs->mode = (FIFO_TRIGGER_LVL << THRESHOLD_BIT) | edge_mask; + do { + interruptible_sleep_on(&data->vsync_wait); + } while (signal_pending(current)); +} + +static void +omap16xx_cam_configure_dma(struct omap16xxcam *data) +{ + + data->camera_regs->mode = (FIFO_TRIGGER_LVL << THRESHOLD_BIT) + | EN_DMA | EN_FIFO_FULL; + data->camera_regs->ctrlclock |= LCLK_EN; +} + +/* acquire h/w resources DMA */ +static int +omap16xx_cam_link_open(struct omap16xxcam *data) +{ + int ret; + + /* Acquire first dma channel */ + if ((ret = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, + "camera dma 1", omap16xx_cam_dma_link_callback, + (void *)data, &data->dma_channel_number1))) { + return ret; + } + /* Acquire second dma channel */ + if ((ret = omap_request_dma(OMAP_DMA_CAMERA_IF_RX, + "camera dma 2", omap16xx_cam_dma_link_callback, + (void *)data, &data->dma_channel_number2))) { + printk ("No DMA available for camera\n"); + return ret; + } + data->next_dmach = data->dma_channel_number1; + OMAP_DMA_CLNK_CTRL_REG(data->dma_channel_number1) = + data->dma_channel_number2; + OMAP_DMA_CLNK_CTRL_REG(data->dma_channel_number2) = + data->dma_channel_number1; + + return 0; +} + +/* free h/w resources, stop i/f */ +static int +omap16xx_cam_link_close(struct omap16xxcam *data) +{ + /* free dma channels */ + omap_stop_dma(data->dma_channel_number1); + omap_stop_dma(data->dma_channel_number2); + + omap_free_dma (data->dma_channel_number1); + omap_free_dma (data->dma_channel_number2); + + return 0; +} + +/* dma callback routine. */ +static void +omap16xx_cam_dma_link_callback(int lch, unsigned short ch_status, void *data) +{ + int count; + void *arg1, *arg2; + struct sgdma_state *sgdma = sgdma; + struct omap16xxcam *cam = (struct omap16xxcam *)data; + dma_callback_t callback; + + spin_lock(&cam->dma_lock); + if (cam->free_dmach == 2) + { + printk("callback all CHANNELS WERE IDLE \n"); + spin_unlock(&cam->dma_lock); + return; + } + if (cam->free_dmach == 0) { + lch = cam->next_dmach; + } else { + lch = cam->next_dmach == cam->dma_channel_number1 ? + cam->dma_channel_number2 : cam->dma_channel_number1; + } + + while (cam->free_dmach < 2) + { + if (OMAP_DMA_CCR_REG(lch) & (1 << 7)) + break; + + count = (lch == cam->dma_channel_number2) ? 1 : 0; + + callback = cam->camdma[count].callback; + arg1 = cam->camdma[count].arg1; + arg2 = cam->camdma[count].arg2; + cam->free_dmach++; + + spin_unlock(&cam->dma_lock); + callback(arg1, arg2); + spin_lock(&cam->dma_lock); + + lch = (lch == cam->dma_channel_number2) ? cam->dma_channel_number1 : + cam->dma_channel_number2; + } + spin_unlock(&cam->dma_lock); + +} + +static irqreturn_t +omap16xx_cam_isr(int irq, void *client_data, struct pt_regs *regs) +{ + struct omap16xxcam *data = (struct omap16xxcam *)client_data; + unsigned int itstat = data->camera_regs->it_status; + + /* VSYNC UP interrupt, start filling FIFO and enabling DMA */ + if (itstat & V_UP) { + data->camera_regs->mode &= ~EN_V_UP; + omap16xx_cam_clear_fifo(data); + omap16xx_cam_configure_dma(data); + omap_start_dma(data->next_dmach); + wake_up_interruptible(&data->vsync_wait); + } + + if (itstat & V_DOWN) { + data->camera_regs->mode &= ~EN_V_DOWN; + wake_up_interruptible(&data->vsync_wait); + } + + if (itstat & H_UP) + printk("H_UP\n"); + + if (itstat & H_DOWN) + printk("H_DOWN\n"); + + if (itstat & FIFO_FULL) { + omap16xx_cam_clear_fifo(data); + printk("FIFO_FULL\n"); + } + + if (itstat & DATA_XFER) + printk("DATA_TRANS\n"); + + return IRQ_HANDLED; +} + +/* ------------- below are interface functions ----------------- */ +/* ------------- these functions are named omap16xxcam_ -- */ +static int +omap16xxcam_init_dma(void *priv) +{ + int ch; + struct omap16xxcam *data = (struct omap16xxcam *) priv; + + data->free_dmach = 2; + for (ch = 0; ch < 2; ++ch) { + data->camdma[ch].callback = NULL; + data->camdma[ch].arg1 = NULL; + data->camdma[ch].arg2 = NULL; + } + + return 0; +} + +/* start the dma of chains */ +static int +omap16xxcam_start_dma(struct sgdma_state *sgdma, + dma_callback_t callback, void *arg1, void *arg2, void *priv) +{ + struct omap16xxcam *data = (struct omap16xxcam *) priv; + struct scatterlist *sglist; + unsigned long irqflags; + int dmach; + int prev_dmach; + int count; + + spin_lock_irqsave(&data->dma_lock, irqflags); + sglist = (struct scatterlist *)(sgdma->sglist + sgdma->next_sglist); + + if (!data->free_dmach) { + spin_unlock_irqrestore(&data->dma_lock, irqflags); + return -EBUSY; + } + dmach = data->next_dmach; + count = (dmach == data->dma_channel_number2) ? 1:0; + data->camdma[count].callback = callback; + data->camdma[count].arg1 = arg1; + data->camdma[count].arg2 = arg2; + + if (machine_is_omap_h3()) + omap_set_dma_src_params(dmach, OMAP_DMA_PORT_OCP_T1, + OMAP_DMA_AMODE_CONSTANT, CAM_CAMDATA_REG, + 0, 0); + else + omap_set_dma_src_params(dmach, OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, CAM_CAMDATA_REG, + 0, 0); + + omap_set_dma_dest_params(dmach, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, sg_dma_address(sglist), + 0, 0); + + omap_set_dma_transfer_params(dmach, OMAP_DMA_DATA_TYPE_S32, + FIFO_TRIGGER_LVL, + sg_dma_len(sglist)/(4 * FIFO_TRIGGER_LVL), + OMAP_DMA_SYNC_FRAME, + 0, 0); + + OMAP_DMA_CLNK_CTRL_REG(dmach) &= ~( 1<< 15); + + prev_dmach = (dmach == data->dma_channel_number2) ? + data->dma_channel_number1 : data->dma_channel_number2; + + if (data->new) { + data->new = 0; + omap16xx_cam_waitfor_syncedge(data, EN_V_UP); + } else { + if (OMAP_DMA_CCR_REG(prev_dmach) & (1 << 7)) + OMAP_DMA_CLNK_CTRL_REG(prev_dmach) |= (1 << 15); + else { + /* no transfer is in progress */ + omap_start_dma(dmach); + } + } + + data->next_dmach = prev_dmach; + data->free_dmach--; + spin_unlock_irqrestore(&data->dma_lock, irqflags); + return 0; +} +int static +omap16xxcam_finish_dma(void *priv) +{ + struct omap16xxcam *data = (struct omap16xxcam *) priv; + + while (data->free_dmach < 2) + mdelay(1); + + return 0; +} + + +/* Enables the camera. Takes camera out of reset. Enables the clocks. */ +static int +omap16xxcam_enable(void *priv) +{ + struct omap16xxcam *data = (struct omap16xxcam *) priv; + + omap16xx_cam_reset(data, 1); + + /* give clock to camera_module */ + data->camera_regs->mode = (FIFO_TRIGGER_LVL << THRESHOLD_BIT); + data->camera_regs->ctrlclock = MCLK_EN | CAMEXCLK_EN; + + omap16xx_cam_clear_fifo(data); + + /* wait for camera to settle down */ + mdelay(5); + + return 0; +} + +/* Disables all the camera clocks. Put the camera interface in reset. */ +static int +omap16xxcam_disable(void *priv) +{ + struct omap16xxcam *data = (struct omap16xxcam *) priv; + + omap16xx_cam_clear_fifo(data); + + data->camera_regs->ctrlclock = 0x00000000; + data->camera_regs->mode = 0x00000000; + + omap16xx_cam_reset(data, 0); + + return 0; +} + +/* Abort the data transfer */ +static int +omap16xxcam_abort(void *priv) +{ + return omap16xxcam_disable(priv); +} + +static int +omap16xxcam_set_xclk(int xclk, void *priv) +{ + struct omap16xxcam *data = (struct omap16xxcam *) priv; + int xclk_val; + int divisor = 1; + divisor = data->ocp_clk/xclk; + if ( divisor * xclk < data->ocp_clk) + ++divisor; + + switch (divisor) { + case 1: + case 2: + xclk_val = FOSCMOD_TC2_CK2; + break; + case 3: + xclk_val = FOSCMOD_TC2_CK3; + break; + case 4: + case 5: + case 6: + case 7: + xclk_val = FOSCMOD_TC2_CK4; + break; + case 8: + case 9: + xclk_val = FOSCMOD_TC2_CK8; + break; + case 10: + case 11: + xclk_val = FOSCMOD_TC2_CK10; + break; + case 12: + case 13: + case 14: + case 15: + xclk_val = FOSCMOD_TC2_CK12; + break; + case 16: + xclk_val = FOSCMOD_TC2_CK16; + break; + default: + xclk_val = FOSCMOD_TC2_CK16; + } + + /* follow the protocol to change the XCLK clock */ + data->camera_regs->ctrlclock &= ~CAMEXCLK_EN; + data->camera_regs->ctrlclock |= xclk_val; + data->camera_regs->ctrlclock |= CAMEXCLK_EN; + + return (data->ocp_clk/divisor); +} + +static int +omap16xxcam_open(void *priv) +{ + struct omap16xxcam *data = (struct omap16xxcam *) priv; + int ret; + + if ((ret = request_irq(INT_CAMERA, omap16xx_cam_isr, SA_INTERRUPT, + "camera", data))) { + printk("FAILED to aquire irq\n"); + return ret; + } + + data->new = 1; + omap16xxcam_enable(data); + omap16xxcam_init_dma(data); + + return omap16xx_cam_link_open(data); +} + +static int +omap16xxcam_close(void *priv) +{ + struct omap16xxcam *data = (struct omap16xxcam *) priv; + + omap16xxcam_disable(priv); + + free_irq(INT_CAMERA, data); + + return omap16xx_cam_link_close(data); +} + +static int +omap16xxcam_cleanup(void *priv) +{ + struct omap16xxcam *data = (struct omap16xxcam *) priv; + + omap16xxcam_disable(data); + if (machine_is_omap_h3()) { + if (data->camera_regs) { + iounmap((void *)data->camera_regs); + data->camera_regs= NULL; + } + } + + if (data->iobase_phys) { + release_mem_region(data->iobase_phys, CAMERA_IOSIZE); + data->iobase_phys = 0; + } + + return 0; +} + +/* Initialise the OMAP camera interface */ +static void * +omap16xxcam_init(void) +{ + unsigned long cam_iobase; + + if (!request_region(CAMERA_BASE, CAMERA_IOSIZE, "OAMP16xx Camera")) { + printk ("OMAP16XX Parallel Camera Interface is already in use\n"); + return NULL; + } + + if (machine_is_omap_h3()) { + cam_iobase = (unsigned long) ioremap (CAMERA_BASE, CAMERA_IOSIZE); + if (!cam_iobase) { + printk("CANNOT MAP CAMERA REGISTER\n"); + return NULL; + } + } + else + cam_iobase = io_p2v(CAMERA_BASE); + + /* Set the base address of the camera registers */ + hardware_data.camera_regs = (camera_regs_t *)cam_iobase; + hardware_data.iobase_phys = (unsigned long) CAMERA_BASE; + /* get the input clock value to camera interface and store it */ + if (machine_is_omap_h3()) + hardware_data.ocp_clk = clk_get_rate(clk_get(0, "tc_ck")); + else + hardware_data.ocp_clk = clk_get_rate(clk_get(0, "mpuper_ck")); + + /* Init the camera IF */ + omap16xx_cam_init(); + /* enable it. This is needed for sensor detection */ + omap16xxcam_enable((void*)&hardware_data); + /* Init dma data */ + spin_lock_init(&hardware_data.dma_lock); + + init_waitqueue_head(&hardware_data.vsync_wait); + return (void*)&hardware_data; +} + +struct camera_hardware camera_hardware_if = { + .version = 0x01, + .name = "OMAP16xx Camera Parallel", + .init = omap16xxcam_init, + .cleanup = omap16xxcam_cleanup, + .open = omap16xxcam_open, + .close = omap16xxcam_close, + .enable = omap16xxcam_enable, + .disable = omap16xxcam_disable, + .abort = omap16xxcam_abort, + .set_xclk = omap16xxcam_set_xclk, + .init_dma = omap16xxcam_init_dma, + .start_dma = omap16xxcam_start_dma, + .finish_dma = omap16xxcam_finish_dma, +}; + diff --git a/drivers/media/video/omap/omap16xxcam.h b/drivers/media/video/omap/omap16xxcam.h new file mode 100644 index 00000000000..14de5b77314 --- /dev/null +++ b/drivers/media/video/omap/omap16xxcam.h @@ -0,0 +1,106 @@ +/* + * drivers/media/video/omap/omap16xxcam.h + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef OMAP_16XX_CAM_H +#define OMAP_16XX_CAM_H + +#define DMA_ELEM_SIZE 4 +#define FIFO_TRIGGER_LVL (32) + +/* + * --------------------------------------------------------------------------- + * OMAP1610 Camera Interface + * --------------------------------------------------------------------------- + */ + +#ifdef CONFIG_MACH_OMAP_H3 +#define CAMERA_BASE (0x2007d800) +#else +#define CAMERA_BASE (IO_PHYS + 0x6800) +#endif + +#define CAM_CTRLCLOCK_REG (CAMERA_BASE + 0x00) +#define CAM_IT_STATUS_REG (CAMERA_BASE + 0x04) +#define CAM_MODE_REG (CAMERA_BASE + 0x08) +#define CAM_STATUS_REG (CAMERA_BASE + 0x0C) +#define CAM_CAMDATA_REG (CAMERA_BASE + 0x10) +#define CAM_GPIO_REG (CAMERA_BASE + 0x14) +#define CAM_PEAK_CTR_REG (CAMERA_BASE + 0x18) +#define CAMERA_IOSIZE 0x1C + +/* CTRLCLOCK bit shifts */ +#define FOSCMOD_BIT 0 +#define FOSCMOD_MASK (0x7 << FOSCMOD_BIT) +#define FOSCMOD_12MHz 0x0 +#define FOSCMOD_6MHz 0x2 +#define FOSCMOD_9_6MHz 0x4 +#define FOSCMOD_24MHz 0x5 +#define FOSCMOD_8MHz 0x6 +#define FOSCMOD_TC2_CK2 0x3 +#define FOSCMOD_TC2_CK3 0x1 +#define FOSCMOD_TC2_CK4 0x5 +#define FOSCMOD_TC2_CK8 0x0 +#define FOSCMOD_TC2_CK10 0x4 +#define FOSCMOD_TC2_CK12 0x6 +#define FOSCMOD_TC2_CK16 0x2 +#define POLCLK (1<<3) +#define CAMEXCLK_EN (1<<4) +#define MCLK_EN (1<<5) +#define DPLL_EN (1<<6) +#define LCLK_EN (1<<7) + +/* IT_STATUS bit shifts */ +#define V_UP (1<<0) +#define V_DOWN (1<<1) +#define H_UP (1<<2) +#define H_DOWN (1<<3) +#define FIFO_FULL (1<<4) +#define DATA_XFER (1<<5) + +/* MODE bit shifts */ +#define CAMOSC (1<<0) +#define IMGSIZE_BIT 1 +#define IMGSIZE_MASK (0x3 << IMGSIZE_BIT) +#define IMGSIZE_CIF (0x0 << IMGSIZE_BIT) /* 352x288 */ +#define IMGSIZE_QCIF (0x1 << IMGSIZE_BIT) /* 176x144 */ +#define IMGSIZE_VGA (0x2 << IMGSIZE_BIT) /* 640x480 */ +#define IMGSIZE_QVGA (0x3 << IMGSIZE_BIT) /* 320x240 */ +#define ORDERCAMD (1<<3) +#define EN_V_UP (1<<4) +#define EN_V_DOWN (1<<5) +#define EN_H_UP (1<<6) +#define EN_H_DOWN (1<<7) +#define EN_DMA (1<<8) +#define THRESHOLD (1<<9) +#define THRESHOLD_BIT 9 +#define THRESHOLD_MASK (0x7f<<9) +#define EN_NIRQ (1<<16) +#define EN_FIFO_FULL (1<<17) +#define RAZ_FIFO (1<<18) + +/* STATUS bit shifts */ +#define VSTATUS (1<<0) +#define HSTATUS (1<<1) + +/* GPIO bit shifts */ +#define CAM_RST (1<<0) + + +#define XCLK_6MHZ 6000000 +#define XCLK_8MHZ 8000000 +#define XCLK_9_6MHZ 9000000 +#define XCLK_12MHZ 12000000 +#define XCLK_24MHZ 24000000 + +#endif /* OMAP_16XX_CAM_H */ diff --git a/drivers/media/video/omap/ov9640.h b/drivers/media/video/omap/ov9640.h new file mode 100644 index 00000000000..4cdba05f72a --- /dev/null +++ b/drivers/media/video/omap/ov9640.h @@ -0,0 +1,179 @@ +/* + * drivers/media/video/omap/ov9640.h + * + * Register definitions for the OmniVision OV9640 CameraChip. + * + * Author: Andy Lowe (source@mvista.com) + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2004 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#ifndef OV9640_H +#define OV9640_H + +/* The OV9640 I2C sensor chip has a fixed slave address of 0x30. */ +#ifdef CONFIG_OMAP24XX_VIRTIO +#define OV9640_I2C_ADDR 0x60 +#else +#define OV9640_I2C_ADDR 0x30 +#endif + +/* define register offsets for the OV9640 sensor chip */ +#define OV9640_GAIN 0x00 +#define OV9640_BLUE 0x01 +#define OV9640_RED 0x02 +#define OV9640_VREF 0x03 +#define OV9640_COM1 0x04 +#define OV9640_BAVE 0x05 +#define OV9640_GEAVE 0x06 +#define OV9640_RAVE 0x08 +#define OV9640_COM2 0x09 +#define OV9640_PID 0x0A +#define OV9640_VER 0x0B +#define OV9640_COM3 0x0C +#define OV9640_COM4 0x0D +#define OV9640_COM5 0x0E +#define OV9640_COM6 0x0F +#define OV9640_AECH 0x10 +#define OV9640_CLKRC 0x11 +#define OV9640_COM7 0x12 +#define OV9640_COM8 0x13 +#define OV9640_COM9 0x14 +#define OV9640_COM10 0x15 +#define OV9640_HSTRT 0x17 +#define OV9640_HSTOP 0x18 +#define OV9640_VSTRT 0x19 +#define OV9640_VSTOP 0x1A +#define OV9640_PSHFT 0x1B +#define OV9640_MIDH 0x1C +#define OV9640_MIDL 0x1D +#define OV9640_MVFP 0x1E +#define OV9640_LAEC 0x1F +#define OV9640_BOS 0x20 +#define OV9640_GBOS 0x21 +#define OV9640_GROS 0x22 +#define OV9640_ROS 0x23 +#define OV9640_AEW 0x24 +#define OV9640_AEB 0x25 +#define OV9640_VPT 0x26 +#define OV9640_BBIAS 0x27 +#define OV9640_GBBIAS 0x28 +#define OV9640_EXHCH 0x2A +#define OV9640_EXHCL 0x2B +#define OV9640_RBIAS 0x2C +#define OV9640_ADVFL 0x2D +#define OV9640_ADVFH 0x2E +#define OV9640_YAVE 0x2F +#define OV9640_HSYST 0x30 +#define OV9640_HSYEN 0x31 +#define OV9640_HREF 0x32 +#define OV9640_CHLF 0x33 +#define OV9640_ARBLM 0x34 +#define OV9640_ADC 0x37 +#define OV9640_ACOM 0x38 +#define OV9640_OFON 0x39 +#define OV9640_TSLB 0x3A +#define OV9640_COM11 0x3B +#define OV9640_COM12 0x3C +#define OV9640_COM13 0x3D +#define OV9640_COM14 0x3E +#define OV9640_EDGE 0x3F +#define OV9640_COM15 0x40 +#define OV9640_COM16 0x41 +#define OV9640_COM17 0x42 +#define OV9640_MTX1 0x4F +#define OV9640_MTX2 0x50 +#define OV9640_MTX3 0x51 +#define OV9640_MTX4 0x52 +#define OV9640_MTX5 0x53 +#define OV9640_MTX6 0x54 +#define OV9640_MTX7 0x55 +#define OV9640_MTX8 0x56 +#define OV9640_MTX9 0x57 +#define OV9640_MTXS 0x58 +#define OV9640_LCC1 0x62 +#define OV9640_LCC2 0x63 +#define OV9640_LCC3 0x64 +#define OV9640_LCC4 0x65 +#define OV9640_LCC5 0x66 +#define OV9640_MANU 0x67 +#define OV9640_MANV 0x68 +#define OV9640_HV 0x69 +#define OV9640_MBD 0x6A +#define OV9640_DBLV 0x6B +#define OV9640_GSP1 0x6C +#define OV9640_GSP2 0x6D +#define OV9640_GSP3 0x6E +#define OV9640_GSP4 0x6F +#define OV9640_GSP5 0x70 +#define OV9640_GSP6 0x71 +#define OV9640_GSP7 0x72 +#define OV9640_GSP8 0x73 +#define OV9640_GSP9 0x74 +#define OV9640_GSP10 0x75 +#define OV9640_GSP11 0x76 +#define OV9640_GSP12 0x77 +#define OV9640_GSP13 0x78 +#define OV9640_GSP14 0x79 +#define OV9640_GSP15 0x7A +#define OV9640_GSP16 0x7B +#define OV9640_GST1 0x7C +#define OV9640_GST2 0x7D +#define OV9640_GST3 0x7E +#define OV9640_GST4 0x7F +#define OV9640_GST5 0x80 +#define OV9640_GST6 0x81 +#define OV9640_GST7 0x82 +#define OV9640_GST8 0x83 +#define OV9640_GST9 0x84 +#define OV9640_GST10 0x85 +#define OV9640_GST11 0x86 +#define OV9640_GST12 0x87 +#define OV9640_GST13 0x88 +#define OV9640_GST14 0x89 +#define OV9640_GST15 0x8A + +#define OV9640_NUM_REGS (OV9640_GST15 + 1) + +#define OV9640_PID_MAGIC 0x96 /* high byte of product ID number */ +#define OV9640_VER_REV2 0x48 /* low byte of product ID number */ +#define OV9640_VER_REV3 0x49 /* low byte of product ID number */ +#define OV9640_MIDH_MAGIC 0x7F /* high byte of mfg ID */ +#define OV9640_MIDL_MAGIC 0xA2 /* low byte of mfg ID */ + +/* define a structure for ov9640 register initialization values */ +struct ov9640_reg { + unsigned char reg; + unsigned char val; +}; + +enum image_size { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; +enum pixel_format { YUV, RGB565, RGB555 }; +#define NUM_IMAGE_SIZES 7 +#define NUM_PIXEL_FORMATS 3 + +struct capture_size { + unsigned long width; + unsigned long height; +}; + +/* Array of image sizes supported by OV9640. These must be ordered from + * smallest image size to largest. + */ +const static struct capture_size ov9640_sizes[] = { + { 88, 72 }, /* QQCIF */ + { 160, 120 }, /* QQVGA */ + { 176, 144 }, /* QCIF */ + { 320, 240 }, /* QVGA */ + { 352, 288 }, /* CIF */ + { 640, 480 }, /* VGA */ + { 1280, 960 }, /* SXGA */ +}; + +#endif /* ifndef OV9640_H */ + diff --git a/drivers/media/video/omap/sensor_if.h b/drivers/media/video/omap/sensor_if.h new file mode 100644 index 00000000000..984ba5553d6 --- /dev/null +++ b/drivers/media/video/omap/sensor_if.h @@ -0,0 +1,49 @@ + +/* + * drivers/media/video/omap/sensor_if.h + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * Sensor interface to OMAP camera capture drivers + * Sensor driver should implement this interface + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef OMAP_SENSOR_IF_H +#define OMAP_SENSOR_IF_H + +#define LEN_SENSOR_NAME 31 + +struct camera_sensor { + unsigned int version; + char name[LEN_SENSOR_NAME + 1]; + + void *(*init)(struct v4l2_pix_format *); + int (*cleanup)(void *); + + int (*power_on)(void *); + int (*power_off)(void *); + + int (*enum_pixformat)(struct v4l2_fmtdesc *, void *); + int (*try_format) (struct v4l2_pix_format *, void *); + + unsigned long (*calc_xclk) (struct v4l2_pix_format *, + struct v4l2_fract *, void *); + + int (*configure) (struct v4l2_pix_format *, unsigned long, + struct v4l2_fract *, void *); + + int (*query_control) (struct v4l2_queryctrl *, void *); + int (*get_control) (struct v4l2_control *, void *); + int (*set_control) (struct v4l2_control *, void *); + +}; + +#endif diff --git a/drivers/media/video/omap/sensor_ov9640.c b/drivers/media/video/omap/sensor_ov9640.c new file mode 100644 index 00000000000..788f41d008a --- /dev/null +++ b/drivers/media/video/omap/sensor_ov9640.c @@ -0,0 +1,1167 @@ + +/* + * drivers/media/video/omap/sensor_ov9640.c + * + * Ov9640 Sensor driver for OMAP camera sensor interface + * + * Author: Andy Lowe (source@mvista.com) + * + * Copyright (C) 2004 MontaVista Software, Inc. + * Copyright (C) 2004 Texas Instruments. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + */ + +#include +#include +#include +#include +#include +#include + +#include "sensor_if.h" +#include "ov9640.h" +#include "h3sensorpower.h" + +#define CAMERA_OV9640 +#ifdef CAMERA_OV9640 + +struct ov9640_sensor { + /* I2C parameters */ + struct i2c_client client; + struct i2c_driver driver; + int ver; /* OV9640 version */ +}; + +static struct ov9640_sensor ov9640; + +/* list of image formats supported by OV9640 sensor */ +const static struct v4l2_fmtdesc ov9640_formats[] = { + { + /* Note: V4L2 defines RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 r4 r3 r2 r1 r0 b4 b3 b2 b1 b0 g5 g4 g3 + * + * We interpret RGB565 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 b4 b3 b2 b1 b0 r4 r3 r2 r1 r0 g5 g4 g3 + */ + .description = "RGB565, le", + .pixelformat = V4L2_PIX_FMT_RGB565, + },{ + /* Note: V4L2 defines RGB565X as: + * + * Byte 0 Byte 1 + * b4 b3 b2 b1 b0 g5 g4 g3 g2 g1 g0 r4 r3 r2 r1 r0 + * + * We interpret RGB565X as: + * + * Byte 0 Byte 1 + * r4 r3 r2 r1 r0 g5 g4 g3 g2 g1 g0 b4 b3 b2 b1 b0 + */ + .description = "RGB565, be", + .pixelformat = V4L2_PIX_FMT_RGB565X, + }, + { + .description = "YUYV (YUV 4:2:2), packed", + .pixelformat = V4L2_PIX_FMT_YUYV, + },{ + .description = "UYVY, packed", + .pixelformat = V4L2_PIX_FMT_UYVY, + }, + { + /* Note: V4L2 defines RGB555 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 r4 r3 r2 r1 r0 x b4 b3 b2 b1 b0 g4 g3 + * + * We interpret RGB555 as: + * + * Byte 0 Byte 1 + * g2 g1 g0 b4 b3 b2 b1 b0 x r4 r3 r2 r1 r0 g4 g3 + */ + .description = "RGB555, le", + .pixelformat = V4L2_PIX_FMT_RGB555, + },{ + /* Note: V4L2 defines RGB555X as: + * + * Byte 0 Byte 1 + * x b4 b3 b2 b1 b0 g4 g3 g2 g1 g0 r4 r3 r2 r1 r0 + * + * We interpret RGB555X as: + * + * Byte 0 Byte 1 + * x r4 r3 r2 r1 r0 g4 g3 g2 g1 g0 b4 b3 b2 b1 b0 + */ + .description = "RGB555, be", + .pixelformat = V4L2_PIX_FMT_RGB555X, + } +}; + +#define NUM_CAPTURE_FORMATS (sizeof(ov9640_formats)/sizeof(ov9640_formats[0])) +#define NUM_OVERLAY_FORMATS 2 + +/* register initialization tables for OV9640 */ + +#define OV9640_REG_TERM 0xFF /* terminating list entry for reg */ +#define OV9640_VAL_TERM 0xFF /* terminating list entry for val */ + +/* common OV9640 register initialization for all image sizes, pixel formats, + * and frame rates + */ +const static struct ov9640_reg ov9640_common[] = { + { 0x12, 0x80 }, { 0x11, 0x80 }, { 0x13, 0x88 }, /* COM7, CLKRC, COM8 */ + { 0x01, 0x58 }, { 0x02, 0x24 }, { 0x04, 0x00 }, /* BLUE, RED, COM1 */ + { 0x0E, 0x81 }, { 0x0F, 0x4F }, { 0x14, 0xcA }, /* COM5, COM6, COM9 */ + { 0x16, 0x02 }, { 0x1B, 0x01 }, { 0x24, 0x70 }, /* ?, PSHFT, AEW */ + { 0x25, 0x68 }, { 0x26, 0xD3 }, { 0x27, 0x90 }, /* AEB, VPT, BBIAS */ + { 0x2A, 0x00 }, { 0x2B, 0x00 }, { 0x32, 0x24 }, /* EXHCH, EXHCL, HREF */ + { 0x33, 0x02 }, { 0x37, 0x02 }, { 0x38, 0x13 }, /* CHLF, ADC, ACOM */ + { 0x39, 0xF0 }, { 0x3A, 0x00 }, { 0x3B, 0x01 }, /* OFON, TSLB, COM11 */ + { 0x3D, 0x90 }, { 0x3E, 0x02 }, { 0x3F, 0xF2 }, /* COM13, COM14, EDGE */ + { 0x41, 0x02 }, { 0x42, 0xC8 }, /* COM16, COM17 */ + { 0x43, 0xF0 }, { 0x44, 0x10 }, { 0x45, 0x6C }, /* ?, ?, ? */ + { 0x46, 0x6C }, { 0x47, 0x44 }, { 0x48, 0x44 }, /* ?, ?, ? */ + { 0x49, 0x03 }, { 0x59, 0x49 }, { 0x5A, 0x94 }, /* ?, ?, ? */ + { 0x5B, 0x46 }, { 0x5C, 0x84 }, { 0x5D, 0x5C }, /* ?, ?, ? */ + { 0x5E, 0x08 }, { 0x5F, 0x00 }, { 0x60, 0x14 }, /* ?, ?, ? */ + { 0x61, 0xCE }, /* ? */ + { 0x62, 0x70 }, { 0x63, 0x00 }, { 0x64, 0x04 }, /* LCC1, LCC2, LCC3 */ + { 0x65, 0x00 }, { 0x66, 0x00 }, /* LCC4, LCC5 */ + { 0x69, 0x00 }, { 0x6A, 0x3E }, { 0x6B, 0x3F }, /* HV, MBD, DBLV */ + { 0x6C, 0x40 }, { 0x6D, 0x30 }, { 0x6E, 0x4B }, /* GSP1, GSP2, GSP3 */ + { 0x6F, 0x60 }, { 0x70, 0x70 }, { 0x71, 0x70 }, /* GSP4, GSP5, GSP6 */ + { 0x72, 0x70 }, { 0x73, 0x70 }, { 0x74, 0x60 }, /* GSP7, GSP8, GSP9 */ + { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, /* GSP10,GSP11,GSP12 */ + { 0x78, 0x3A }, { 0x79, 0x2E }, { 0x7A, 0x28 }, /* GSP13,GSP14,GSP15 */ + { 0x7B, 0x22 }, { 0x7C, 0x04 }, { 0x7D, 0x07 }, /* GSP16,GST1, GST2 */ + { 0x7E, 0x10 }, { 0x7F, 0x28 }, { 0x80, 0x36 }, /* GST3, GST4, GST5 */ + { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, /* GST6, GST7, GST8 */ + { 0x84, 0x6C }, { 0x85, 0x78 }, { 0x86, 0x8C }, /* GST9, GST10,GST11 */ + { 0x87, 0x9E }, { 0x88, 0xBB }, { 0x89, 0xD2 }, /* GST12,GST13,GST14 */ + { 0x8A, 0xE6 }, { 0x13, 0xaF }, { 0x15, 0x02 }, /* GST15, COM8 */ + { 0x22, 0x8a }, /* GROS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + +/* OV9640 register configuration for all combinations of pixel format and + * image size + */ + /* YUV (YCbCr) QQCIF */ +const static struct ov9640_reg qqcif_yuv[] = { + { 0x12, 0x08 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */ + { 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x0F }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* YUV (YCbCr) QQVGA */ +const static struct ov9640_reg qqvga_yuv[] = { + { 0x12, 0x10 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */ + { 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x0F }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* YUV (YCbCr) QCIF */ +const static struct ov9640_reg qcif_yuv[] = { + { 0x12, 0x08 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x0F }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* YUV (YCbCr) QVGA */ +const static struct ov9640_reg qvga_yuv[] = { + { 0x12, 0x10 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x0F }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* YUV (YCbCr) CIF */ +const static struct ov9640_reg cif_yuv[] = { + { 0x12, 0x20 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x0F }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* YUV (YCbCr) VGA */ +const static struct ov9640_reg vga_yuv[] = { + { 0x12, 0x40 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x0F }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* YUV (YCbCr) SXGA */ +const static struct ov9640_reg sxga_yuv[] = { + { 0x12, 0x00 }, { 0x3C, 0x46 }, { 0x40, 0xC0 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x50 }, { 0x50, 0x43 }, { 0x51, 0x0D }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x19 }, { 0x53, 0x4C }, { 0x54, 0x65 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x40 }, { 0x56, 0x40 }, { 0x57, 0x40 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x0F }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB565 QQCIF */ +const static struct ov9640_reg qqcif_565[] = { + { 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */ + { 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB565 QQVGA */ +const static struct ov9640_reg qqvga_565[] = { + { 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */ + { 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB565 QCIF */ +const static struct ov9640_reg qcif_565[] = { + { 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB565 QVGA */ +const static struct ov9640_reg qvga_565[] = { + { 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB565 CIF */ +const static struct ov9640_reg cif_565[] = { + { 0x12, 0x24 }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB565 VGA */ +const static struct ov9640_reg vga_565[] = { + { 0x12, 0x44 }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB565 SXGA */ +const static struct ov9640_reg sxga_565[] = { + { 0x12, 0x04 }, { 0x3C, 0x40 }, { 0x40, 0x10 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB555 QQCIF */ +const static struct ov9640_reg qqcif_555[] = { + { 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */ + { 0x04, 0x24 }, { 0x0C, 0x00 }, { 0x0D, 0x40 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB555 QQVGA */ +const static struct ov9640_reg qqvga_555[] = { + { 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */ + { 0x04, 0x24 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB555 QCIF */ +const static struct ov9640_reg qcif_555[] = { + { 0x12, 0x0C }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB555 QVGA */ +const static struct ov9640_reg qvga_555[] = { + { 0x12, 0x14 }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB555 CIF */ +const static struct ov9640_reg cif_555[] = { + { 0x12, 0x24 }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB555 VGA */ +const static struct ov9640_reg vga_555[] = { + { 0x12, 0x44 }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x04 }, { 0x0D, 0xC0 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + /* RGB555 SXGA */ +const static struct ov9640_reg sxga_555[] = { + { 0x12, 0x04 }, { 0x3C, 0x40 }, { 0x40, 0x30 }, /* COM7, COM12, COM15 */ + { 0x04, 0x00 }, { 0x0C, 0x00 }, { 0x0D, 0x40 }, /* COM1, COM3, COM4 */ + { 0x4F, 0x71 }, { 0x50, 0x3E }, { 0x51, 0x0C }, /* MTX1, MTX2, MTX3 */ + { 0x52, 0x33 }, { 0x53, 0x72 }, { 0x54, 0x00 }, /* MTX4, MTX5, MTX6 */ + { 0x55, 0x2B }, { 0x56, 0x66 }, { 0x57, 0xD2 }, /* MTX7, MTX8, MTX9 */ + { 0x58, 0x65 }, /* MTXS */ + { OV9640_REG_TERM, OV9640_VAL_TERM } +}; + + +#define DEF_GAIN 31 +#define DEF_AUTOGAIN 1 +#define DEF_EXPOSURE 154 +#define DEF_AEC 1 +#define DEF_FREEZE_AGCAEC 0 +#define DEF_BLUE 153 +#define DEF_RED (255 - DEF_BLUE) +#define DEF_AWB 1 +#define DEF_HFLIP 0 +#define DEF_VFLIP 0 + +/* Our own specific controls */ +#define V4L2_CID_FREEZE_AGCAEC V4L2_CID_PRIVATE_BASE+0 +#define V4L2_CID_AUTOEXPOSURE V4L2_CID_PRIVATE_BASE+1 +#define V4L2_CID_LAST_PRIV V4L2_CID_AUTOEXPOSURE + +/* Video controls */ +static struct vcontrol { + struct v4l2_queryctrl qc; + int current_value; + u8 reg; + u8 mask; + u8 start_bit; +} control[] = { + { { V4L2_CID_GAIN, V4L2_CTRL_TYPE_INTEGER, "Gain", 0, 63, 1, + DEF_GAIN }, + 0, OV9640_GAIN, 0x3f, 0 }, + { { V4L2_CID_AUTOGAIN, V4L2_CTRL_TYPE_BOOLEAN, "Auto Gain", 0, 1, 0, + DEF_AUTOGAIN }, + 0, OV9640_COM8, 0x04, 2 }, + { { V4L2_CID_EXPOSURE, V4L2_CTRL_TYPE_INTEGER, "Exposure", 0, 255, 1, + DEF_EXPOSURE }, + 0, OV9640_AECH, 0xff, 0 }, + { { V4L2_CID_AUTOEXPOSURE, V4L2_CTRL_TYPE_BOOLEAN, "Auto Exposure", 0, 1, 0, + DEF_AEC }, + 0, OV9640_COM8, 0x01, 0 }, + { { V4L2_CID_FREEZE_AGCAEC, V4L2_CTRL_TYPE_BOOLEAN, "Freeze AGC/AEC", 0,1,0, + DEF_FREEZE_AGCAEC }, + 0, OV9640_COM9, 0x01, 0 }, + { { V4L2_CID_RED_BALANCE, V4L2_CTRL_TYPE_INTEGER, "Red Balance", 0, 255, 1, + DEF_RED }, + 0, OV9640_RED, 0xff, 0 }, + { { V4L2_CID_BLUE_BALANCE, V4L2_CTRL_TYPE_INTEGER, "Blue Balance", 0, 255, 1, + DEF_BLUE }, + 0, OV9640_BLUE, 0xff, 0 }, + { { V4L2_CID_AUTO_WHITE_BALANCE, V4L2_CTRL_TYPE_BOOLEAN, "Auto White Balance", 0,1,0, + DEF_AWB }, + 0, OV9640_COM8, 0x02, 1 }, + { { V4L2_CID_HFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Mirror Image", 0, 1, 0, + DEF_HFLIP }, + 0, OV9640_MVFP, 0x20, 5 }, + { { V4L2_CID_VFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Vertical Flip", 0, 1, 0, + DEF_VFLIP }, + 0, OV9640_MVFP, 0x10, 4 }, +}; + +#define NUM_CONTROLS (sizeof(control)/sizeof(control[0])) + +const static struct ov9640_reg * + ov9640_reg_init[NUM_PIXEL_FORMATS][NUM_IMAGE_SIZES] = +{ + { qqcif_yuv, qqvga_yuv, qcif_yuv, qvga_yuv, cif_yuv, vga_yuv, sxga_yuv }, + { qqcif_565, qqvga_565, qcif_565, qvga_565, cif_565, vga_565, sxga_565 }, + { qqcif_555, qqvga_555, qcif_555, qvga_555, cif_555, vga_555, sxga_555 }, +}; + + +/* + * Read a value from a register in an OV9640 sensor device. The value is + * returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int +ov9640_read_reg(struct i2c_client *client, u8 reg, u8 *val) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[1]; + + if (!client->adapter) + return -ENODEV; + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 1; + msg->buf = data; + *data = reg; + err = i2c_transfer(client->adapter, msg, 1); + if (err >= 0) { + msg->flags = I2C_M_RD; + err = i2c_transfer(client->adapter, msg, 1); + } + if (err >= 0) { + *val = *data; + return 0; + } + return err; +} + +/* Write a value to a register in an OV9640 sensor device. + * Returns zero if successful, or non-zero otherwise. + */ +static int +ov9640_write_reg(struct i2c_client *client, u8 reg, u8 val) +{ + int err; + struct i2c_msg msg[1]; + unsigned char data[2]; + + if (!client->adapter) + return -ENODEV; + + msg->addr = client->addr; + msg->flags = 0; + msg->len = 2; + msg->buf = data; + data[0] = reg; + data[1] = val; + err = i2c_transfer(client->adapter, msg, 1); + if (err >= 0) + return 0; + return err; +} + +static int +ov9640_write_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask) +{ + u8 oldval, newval; + int rc; + + if (mask == 0xff) { + newval = *val; + } else { + /* need to do read - modify - write */ + if ((rc = ov9640_read_reg(client, reg, &oldval))) + return rc; + oldval &= (~mask); /* Clear the masked bits */ + *val &= mask; /* Enforce mask on value */ + newval = oldval | *val; /* Set the desired bits */ + } + + /* write the new value to the register */ + if ((rc = ov9640_write_reg(client, reg, newval))) + return rc; + + if ((rc = ov9640_read_reg(client, reg, &newval))) + return rc; + + *val = newval & mask; + return 0; +} + +static int +ov9640_read_reg_mask(struct i2c_client *client, u8 reg, u8 *val, u8 mask) +{ + int rc; + + if ((rc = ov9640_read_reg(client, reg, val))) + return rc; + (*val) &= mask; + + return 0; +} + +/* Initialize a list of OV9640 registers. + * The list of registers is terminated by the pair of values + * { OV9640_REG_TERM, OV9640_VAL_TERM }. + * Returns zero if successful, or non-zero otherwise. + */ +static int +ov9640_write_regs(struct i2c_client *client, const struct ov9640_reg reglist[]) +{ + int err; + const struct ov9640_reg *next = reglist; + + while (!((next->reg == OV9640_REG_TERM) + && (next->val == OV9640_VAL_TERM))) + { + err = ov9640_write_reg(client, next->reg, next->val); + udelay(100); + if (err) + return err; + next++; + } + return 0; +} + +/* Returns the index of the requested ID from the control structure array */ +static int +find_vctrl(int id) +{ + int i; + + if (id < V4L2_CID_BASE) + return -EDOM; + + for (i = NUM_CONTROLS - 1; i >= 0; i--) + if (control[i].qc.id == id) + break; + if (i < 0) + i = -EINVAL; + return i; +} + +/* Calculate the internal clock divisor (value of the CLKRC register) of the + * OV9640 given the image size, the frequency (in Hz) of its XCLK input and a + * desired frame period (in seconds). The frame period 'fper' is expressed as + * a fraction. The frame period is an input/output parameter. + * Returns the value of the OV9640 CLKRC register that will yield the frame + * period returned in 'fper' at the specified xclk frequency. The + * returned period will be as close to the requested period as possible. + */ +static unsigned char +ov9640_clkrc(enum image_size isize, unsigned long xclk, struct v4l2_fract *fper) +{ + unsigned long fpm, fpm_max; /* frames per minute */ + unsigned long divisor; + const unsigned long divisor_max = 64; + const static unsigned long clks_per_frame[] = + { 200000, 200000, 200000, 200000, 400000, 800000, 3200000 }; + + if (fper->numerator > 0) + fpm = (fper->denominator*60)/fper->numerator; + else + fpm = 0xffffffff; + fpm_max = (xclk*60)/clks_per_frame[isize]; + if (fpm_max == 0) + fpm_max = 1; + if (fpm > fpm_max) + fpm = fpm_max; + if (fpm == 0) + fpm = 1; + divisor = fpm_max/fpm; + if (divisor > divisor_max) + divisor = divisor_max; + fper->numerator = divisor*60; + fper->denominator = fpm_max; + + /* try to reduce the fraction */ + while (!(fper->denominator % 5) && !(fper->numerator % 5)) { + fper->numerator /= 5; + fper->denominator /= 5; + } + while (!(fper->denominator % 3) && !(fper->numerator % 3)) { + fper->numerator /= 3; + fper->denominator /= 3; + } + while (!(fper->denominator % 2) && !(fper->numerator % 2)) { + fper->numerator /= 2; + fper->denominator /= 2; + } + if (fper->numerator < fper->denominator) { + if (!(fper->denominator % fper->numerator)) { + fper->denominator /= fper->numerator; + fper->numerator = 1; + } + } + else { + if (!(fper->numerator % fper->denominator)) { + fper->numerator /= fper->denominator; + fper->denominator = 1; + } + } + + /* we set bit 7 in CLKRC to enable the digital PLL */ + return (0x80 | (divisor - 1)); +} + +/* Configure the OV9640 for a specified image size, pixel format, and frame + * period. xclk is the frequency (in Hz) of the xclk input to the OV9640. + * fper is the frame period (in seconds) expressed as a fraction. + * Returns zero if successful, or non-zero otherwise. + * The actual frame period is returned in fper. + */ +static int +ov9640_configure(struct i2c_client *client, + enum image_size isize, + enum pixel_format pfmt, + unsigned long xclk, + struct v4l2_fract *fper) +{ + int err; + unsigned char clkrc; + + /* common register initialization */ + err = ov9640_write_regs(client, ov9640_common); + if (err) + return err; + + /* configure image size and pixel format */ + err = ov9640_write_regs(client, ov9640_reg_init[pfmt][isize]); + if (err) + return err; + + /* configure frame rate */ + clkrc = ov9640_clkrc(isize, xclk, fper); + err = ov9640_write_reg(client, OV9640_CLKRC, clkrc); + if (err) + return err; + + return 0; +} + +static int +ov9640_powerup(void) +{ + int err; + + if (machine_is_omap_h2()) + return 0; + + if (machine_is_omap_h3()) { + err = h3_sensor_powerup(); + if (err) + return err; + } + + return 0; +} +static int +ov9640_powerdown(void) +{ + int err; + + if (machine_is_omap_h2()) + return 0; + + if (machine_is_omap_h3()) { + err = h3_sensor_powerdown(); + if (err) + return err; + } + + return 0; +} + +static int +ov9640sensor_power_on(void *priv) +{ + return ov9640_powerup(); +} + +static int +ov9640sensor_power_off(void *priv) +{ + return ov9640_powerdown(); +} + +/* Detect if an OV9640 is present, and if so which revision. + * A device is considered to be detected if the manufacturer ID (MIDH and MIDL) + * and the product ID (PID) registers match the expected values. + * Any value of the version ID (VER) register is accepted. + * Here are the version numbers we know about: + * 0x48 --> OV9640 Revision 1 or OV9640 Revision 2 + * 0x49 --> OV9640 Revision 3 + * Returns a negative error number if no device is detected, or the + * non-negative value of the version ID register if a device is detected. + */ +static int +ov9640_detect(struct i2c_client *client) +{ + u8 midh, midl, pid, ver; + + if (!client) + return -ENODEV; + + if (ov9640_read_reg(client, OV9640_MIDH, &midh)) + return -ENODEV; + if (ov9640_read_reg(client, OV9640_MIDL, &midl)) + return -ENODEV; + if (ov9640_read_reg(client, OV9640_PID, &pid)) + return -ENODEV; + if (ov9640_read_reg(client, OV9640_VER, &ver)) + return -ENODEV; + + if ((midh != OV9640_MIDH_MAGIC) + || (midl != OV9640_MIDL_MAGIC) + || (pid != OV9640_PID_MAGIC)) + { + /* We didn't read the values we expected, so + * this must not be an OV9640. + */ + return -ENODEV; + } + return ver; +} + +/* This function registers an I2C client via i2c_attach_client() for an OV9640 + * sensor device. If 'probe' is non-zero, then the I2C client is only + * registered if the device can be detected. If 'probe' is zero, then no + * device detection is attempted and the I2C client is always registered. + * Returns zero if an I2C client is successfully registered, or non-zero + * otherwise. + */ +static int +ov9640_i2c_attach_client(struct i2c_adapter *adap, int addr, int probe) +{ + struct ov9640_sensor *sensor = &ov9640; + struct i2c_client *client = &sensor->client; + int err; + + if (client->adapter) + return -EBUSY; /* our client is already attached */ + + client->addr = addr; + client->driver = &sensor->driver; + client->adapter = adap; + + err = i2c_attach_client(client); + if (err) { + client->adapter = NULL; + return err; + } + + if (probe) { + err = ov9640_detect(client); + if (err < 0) { + i2c_detach_client(client); + client->adapter = NULL; + return err; + } + sensor->ver = err; + } + return 0; +} + +/* This function is called by i2c_del_adapter() and i2c_del_driver() + * if the adapter or driver with which this I2C client is associated is + * removed. This function unregisters the client via i2c_detach_client(). + * Returns zero if the client is successfully detached, or non-zero + * otherwise. + */ +static int +ov9640_i2c_detach_client(struct i2c_client *client) +{ + int err; + + if (!client->adapter) + return -ENODEV; /* our client isn't attached */ + + err = i2c_detach_client(client); + client->adapter = NULL; + + return err; +} + +/* This function will be called for each registered I2C bus adapter when our + * I2C driver is registered via i2c_add_driver(). It will also be called + * whenever a new I2C adapter is registered after our I2C driver is registered. + * This function probes the specified I2C bus adapter to determine if an + * OV9640 sensor device is present. If a device is detected, an I2C client + * is registered for it via ov9640_i2c_attach_client(). Note that we can't use + * the standard i2c_probe() function to look for the sensor because the OMAP + * I2C controller doesn't support probing. + * Returns zero if an OV9640 device is detected and an I2C client successfully + * registered for it, or non-zero otherwise. + */ +static int +ov9640_i2c_probe_adapter(struct i2c_adapter *adap) +{ + return ov9640_i2c_attach_client(adap, OV9640_I2C_ADDR, 1); +} + +/* Find the best match for a requested image capture size. The best match + * is chosen as the nearest match that has the same number or fewer pixels + * as the requested size, or the smallest image size if the requested size + * has fewer pixels than the smallest image. + */ +static enum image_size +ov9640_find_size(unsigned int width, unsigned int height) +{ + enum image_size isize; + unsigned long pixels = width*height; + + for (isize = QQCIF; isize < SXGA; isize++) { + if (ov9640_sizes[isize + 1].height* + ov9640_sizes[isize + 1].width > pixels) + { + return isize; + } + } + return SXGA; +} + +/* following are sensor interface functions implemented by + * OV9640 sensor driver. + */ +static int +ov9640sensor_query_control(struct v4l2_queryctrl *qc, void *priv) +{ + int i; + + i = find_vctrl (qc->id); + if (i == -EINVAL) { + qc->flags = V4L2_CTRL_FLAG_DISABLED; + return 0; + } + if (i < 0) + return -EINVAL; + + *qc = control[i].qc; + return 0; +} + +static int +ov9640sensor_get_control(struct v4l2_control *vc, void *priv) +{ + struct ov9640_sensor *sensor = (struct ov9640_sensor *) priv; + struct i2c_client *client = &sensor->client; + int i, val; + struct vcontrol * lvc; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + + lvc = &control[i]; + if (ov9640_read_reg_mask(client, lvc->reg, (u8 *)&val, lvc->mask)) + return -EIO; + + val = val >> lvc->start_bit; + if (val >= 0) { + vc->value = lvc->current_value = val; + return 0; + } else + return val; +} + +static int +ov9640sensor_set_control(struct v4l2_control *vc, void *priv) +{ + struct ov9640_sensor *sensor = (struct ov9640_sensor *) priv; + struct i2c_client *client = &sensor->client; + struct vcontrol *lvc; + int val = vc->value; + int i; + + i = find_vctrl(vc->id); + if (i < 0) + return -EINVAL; + + lvc = &control[i]; + val = val << lvc->start_bit; + if (ov9640_write_reg_mask(client, lvc->reg, (u8 *)&val, (u8)lvc->mask)) + return -EIO; + + val = val>> lvc->start_bit; + if (val >= 0) { + lvc->current_value = val; + return 0; + } else + return val; +} + +/* Implement the VIDIOC_ENUM_FMT ioctl for the CAPTURE buffer type. + */ +static int +ov9640sensor_enum_pixformat(struct v4l2_fmtdesc *fmt, void *priv) +{ + int index = fmt->index; + enum v4l2_buf_type type = fmt->type; + + memset(fmt, 0, sizeof(*fmt)); + fmt->index = index; + fmt->type = type; + + switch (fmt->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (index >= NUM_CAPTURE_FORMATS) + return -EINVAL; + break; + + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + if (index >= NUM_OVERLAY_FORMATS) + return -EINVAL; + break; + + default: + return -EINVAL; + } + + fmt->flags = ov9640_formats[index].flags; + strlcpy(fmt->description, ov9640_formats[index].description, sizeof(fmt->description)); + fmt->pixelformat = ov9640_formats[index].pixelformat; + + return 0; +} + +/* Implement the VIDIOC_TRY_FMT ioctl for the CAPTURE buffer type. This + * ioctl is used to negotiate the image capture size and pixel format + * without actually making it take effect. + */ +static int +ov9640sensor_try_format(struct v4l2_pix_format *pix, void *priv) +{ + enum image_size isize; + int ifmt; + + isize = ov9640_find_size(pix->width, pix->height); + pix->width = ov9640_sizes[isize].width; + pix->height = ov9640_sizes[isize].height; + for (ifmt = 0; ifmt < NUM_CAPTURE_FORMATS; ifmt++) { + if (pix->pixelformat == ov9640_formats[ifmt].pixelformat) + break; + } + if (ifmt == NUM_CAPTURE_FORMATS) + ifmt = 0; + pix->pixelformat = ov9640_formats[ifmt].pixelformat; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = pix->width*2; + pix->sizeimage = pix->bytesperline*pix->height; + pix->priv = 0; + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + default: + pix->colorspace = V4L2_COLORSPACE_JPEG; + break; + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_RGB555X: + pix->colorspace = V4L2_COLORSPACE_SRGB; + break; + } + return 0; +} + +/* Given the image capture format in pix, the nominal frame period in + * timeperframe, calculate the required xclk frequency + * The nominal xclk input frequency of the OV9640 is 24MHz, maximum + * frequency is 48MHz, and minimum frequency is 10MHz. + */ +static unsigned long +ov9640sensor_calc_xclk(struct v4l2_pix_format *pix, + struct v4l2_fract *timeperframe, void *priv) +{ + unsigned long tgt_xclk; /* target xclk */ + unsigned long tgt_fpm; /* target frames per minute */ + enum image_size isize; + + /* We use arbitrary rules to select the xclk frequency. If the + * capture size is VGA and the frame rate is greater than 900 + * frames per minute, or if the capture size is SXGA and the + * frame rate is greater than 450 frames per minutes, then the + * xclk frequency will be set to 48MHz. Otherwise, the xclk + * frequency will be set to 24MHz. If the mclk frequency is such that + * the target xclk frequency is not achievable, then xclk will be set + * as close as to the target as possible. + */ + if ((timeperframe->numerator == 0) + || (timeperframe->denominator == 0)) + { + /* supply a default nominal_timeperframe of 15 fps */ + timeperframe->numerator = 1; + timeperframe->denominator = 15; + } + tgt_fpm = (timeperframe->denominator*60) + / timeperframe->numerator; + tgt_xclk = 24000000; + isize = ov9640_find_size(pix->width, pix->height); + switch (isize) { + case SXGA: + if (tgt_fpm > 450) + tgt_xclk = 48000000; + break; + case VGA: + if (tgt_fpm > 900) + tgt_xclk = 48000000; + break; + default: + break; + } + return tgt_xclk; +} + +/* Given a capture format in pix, the frame period in timeperframe, and + * the xclk frequency, set the capture format of the OV9640 sensor. + * The actual frame period will be returned in timeperframe. + */ +static int +ov9640sensor_configure(struct v4l2_pix_format *pix, unsigned long xclk, + struct v4l2_fract *timeperframe, void *priv) +{ + struct ov9640_sensor *sensor = (struct ov9640_sensor *) priv; + enum pixel_format pfmt = YUV; + + switch (pix->pixelformat) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + pfmt = RGB565; + break; + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_RGB555X: + pfmt = RGB555; + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + default: + pfmt = YUV; + } + + return ov9640_configure(&sensor->client, + ov9640_find_size(pix->width, pix->height), + pfmt, xclk, timeperframe); +} + +/* Prepare for the driver to exit. + * Balances ov9640sensor_init(). + * This function must de-initialize the sensor and its associated data + * structures. + */ +static int +ov9640sensor_cleanup(void *priv) +{ + struct ov9640_sensor *sensor = (struct ov9640_sensor *) priv; + + if (sensor) { + i2c_del_driver(&sensor->driver); + ov9640_powerdown(); + } + return 0; +} + +/* Initialize the OV9640 sensor. + * This routine allocates and initializes the data structure for the sensor, + * powers up the sensor, registers the I2C driver, and sets a default image + * capture format in pix. The capture format is not actually programmed + * into the OV9640 sensor by this routine. + * This function must return a non-NULL value to indicate that + * initialization is successful. + */ +static void * +ov9640sensor_init(struct v4l2_pix_format *pix) +{ + struct ov9640_sensor *sensor = &ov9640; + struct i2c_driver *driver = &sensor->driver; + int err; + + memset(sensor, 0, sizeof(*sensor)); + + /* power-up the sensor */ + if (ov9640_powerup()) + return NULL; + + strlcpy(driver->driver.name, "OV9640 I2C driver", sizeof(driver->driver.name)); + driver->id = I2C_DRIVERID_MISC; + driver->attach_adapter = ov9640_i2c_probe_adapter; + driver->detach_client = ov9640_i2c_detach_client; + + err = i2c_add_driver(driver); + if (err) { + printk(KERN_ERR "Failed to register OV9640 I2C client.\n"); + return NULL; + } + if (!sensor->client.adapter) { + printk(KERN_WARNING + "Failed to detect OV9640 sensor chip.\n"); + return NULL; + } + else { + printk(KERN_INFO + "OV9640 sensor chip version 0x%02x detected\n", sensor->ver); + } + + /* Make the default capture format QCIF RGB565 */ + pix->width = ov9640_sizes[QCIF].width; + pix->height = ov9640_sizes[QCIF].height; + pix->pixelformat = V4L2_PIX_FMT_RGB565; + ov9640sensor_try_format(pix, NULL); + + return (void *)sensor; +} + +struct camera_sensor camera_sensor_if = { + .version = 0x01, + .name = "OV9640", + .init = ov9640sensor_init, + .cleanup = ov9640sensor_cleanup, + .enum_pixformat = ov9640sensor_enum_pixformat, + .try_format = ov9640sensor_try_format, + .calc_xclk = ov9640sensor_calc_xclk, + .configure = ov9640sensor_configure, + .query_control = ov9640sensor_query_control, + .get_control = ov9640sensor_get_control, + .set_control = ov9640sensor_set_control, + .power_on = ov9640sensor_power_on, + .power_off = ov9640sensor_power_off, +}; + +void print_ov9640_regs(void *priv) +{ + struct ov9640_sensor *sensor = (struct ov9640_sensor *) priv; + u8 reg, val; + for (reg=0x00; reg <=0x8A; reg++) + if (ov9640_read_reg(&sensor->client,reg,&val)) + printk("error reading %x\n", reg); + else + printk("reg %x = %x\n", reg, val); +} + +#endif /* ifdef CAMERA_OV9640 */ diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 5d397b7a549..da8ce352773 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -29,6 +29,23 @@ config MMC_BLOCK mount the filesystem. Almost everyone wishing MMC support should say Y or M here. +config MMC_BLOCK_BROKEN_RFD + boolean "Write work-around for incompatible cards" + depends on MMC_BLOCK + default n + help + Say y here if your MMC card fails write operations. Some cards + lie about being ready to receive data while they actually are not. + +config MMC_BULKTRANSFER + bool "Multi-block writes (EXPERIMENTAL)" + depends on MMC_BLOCK != n && EXPERIMENTAL + default n + help + By default all writes are done one sector at a time. Enable + this option to transfer as large blocks as the host supports. + The transfer speed is in most cases doubled. + config MMC_ARMMMCI tristate "ARM AMBA Multimedia Card Interface support" depends on ARM_AMBA && MMC @@ -49,6 +66,17 @@ config MMC_PXA If unsure, say N. +config MMC_OMAP + tristate "TI OMAP Multimedia Card Interface support" + depends on ARCH_OMAP && MMC + select TPS65010 if MACH_OMAP_H2 + help + This selects the TI OMAP Multimedia card Interface. + If you have an OMAP board with a Multimedia Card slot, + say Y or M here. + + If unsure, say N. + config MMC_WBSD tristate "Winbond W83L51xD SD/MMC Card Interface support" depends on MMC && ISA_DMA_API diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index e351e71146e..27cbef71ffc 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -19,5 +19,6 @@ obj-$(CONFIG_MMC_ARMMMCI) += mmci.o obj-$(CONFIG_MMC_PXA) += pxamci.o obj-$(CONFIG_MMC_WBSD) += wbsd.o obj-$(CONFIG_MMC_AU1X) += au1xmmc.o +obj-$(CONFIG_MMC_OMAP) += omap.o mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 1888060c5e0..09aeeba61b7 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -705,6 +705,7 @@ static void mmc_power_up(struct mmc_host *host) int bit = fls(host->ocr_avail) - 1; host->ios.vdd = bit; + host->ios.clock = host->f_min; host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN; host->ios.chip_select = MMC_CS_DONTCARE; host->ios.power_mode = MMC_POWER_UP; @@ -713,7 +714,6 @@ static void mmc_power_up(struct mmc_host *host) mmc_delay(1); - host->ios.clock = host->f_min; host->ios.power_mode = MMC_POWER_ON; host->ops->set_ios(host, &host->ios); @@ -747,7 +747,7 @@ static int mmc_send_op_cond(struct mmc_host *host, u32 ocr, u32 *rocr) if (cmd.resp[0] & MMC_CARD_BUSY || ocr == 0) break; - + mmc_delay(1); err = MMC_ERR_TIMEOUT; mmc_delay(10); @@ -1089,6 +1089,14 @@ static void mmc_setup(struct mmc_host *host) host->ios.bus_mode = MMC_BUSMODE_PUSHPULL; host->ops->set_ios(host, &host->ios); + /* + * Some already detectd cards get confused in the card identification + * mode and futher commands can fail. Doing an extra status inquiry + * after the identification mode seems to get cards back to their + * senses. + */ + mmc_check_cards(host); + mmc_read_csds(host); if (host->mode == MMC_MODE_SD) diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c new file mode 100644 index 00000000000..822808d9205 --- /dev/null +++ b/drivers/mmc/omap.c @@ -0,0 +1,1487 @@ +/* + * linux/drivers/media/mmc/omap.c + * + * Copyright (C) 2004 Nokia Corporation + * Written by Tuukka Tikkanen and Juha Yrjölä + * Misc hacks here and there by Tony Lindgren + * Other hacks (DMA, SD, etc) by David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include + +// #define CONFIG_MMC_DEBUG +#ifdef CONFIG_MMC_DEBUG +#define DEBUG /* for dev_dbg(), pr_debug(), etc */ +#endif + +#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 "omap.h" + +#define DRIVER_NAME "mmci-omap" + +#ifdef CONFIG_MMC_DEBUG +#define DBG(x...) pr_debug(x) +//#define DBG(x...) printk(x) +#else +#define DBG(x...) do { } while (0) +#endif + +/* Specifies how often in millisecs to poll for card status changes + * when the cover switch is open */ +#define OMAP_MMC_SWITCH_POLL_DELAY 500 + +static int mmc_omap_enable_poll = 1; + +struct mmc_omap_host { + int initialized; + int suspended; + struct mmc_request * mrq; + struct mmc_command * cmd; + struct mmc_data * data; + struct mmc_host * mmc; + struct device * dev; + unsigned char id; /* 16xx chips have 2 MMC blocks */ + struct clk * iclk; + struct clk * fclk; + void __iomem *base; + int irq; + unsigned char bus_mode; + unsigned char hw_bus_mode; + + unsigned int sg_len; + int sg_idx; + u16 * buffer; + u32 buffer_bytes_left; + u32 total_bytes_left; + struct timer_list xfer_timer; + + unsigned use_dma:1; + unsigned brs_received:1, dma_done:1; + unsigned dma_is_read:1; + unsigned dma_in_use:1; + int dma_ch; + spinlock_t dma_lock; + struct timer_list dma_timer; + unsigned dma_len; + + short power_pin; + short wp_pin; + + int switch_pin; + struct work_struct switch_work; + struct timer_list switch_timer; + int switch_last_state; +}; + +static inline int +mmc_omap_cover_is_open(struct mmc_omap_host *host) +{ + if (host->switch_pin < 0) + return 0; + return omap_get_gpio_datain(host->switch_pin); +} + +static ssize_t +mmc_omap_show_cover_switch(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct mmc_omap_host *host = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", mmc_omap_cover_is_open(host) ? "open" : "closed"); +} + +static DEVICE_ATTR(cover_switch, S_IRUGO, mmc_omap_show_cover_switch, NULL); + +static ssize_t +mmc_omap_show_enable_poll(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return snprintf(buf, PAGE_SIZE, "%d\n", mmc_omap_enable_poll); +} + +static ssize_t +mmc_omap_store_enable_poll(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t size) +{ + int enable_poll; + + if (sscanf(buf, "%10d", &enable_poll) != 1) + return -EINVAL; + + if (enable_poll != mmc_omap_enable_poll) { + struct mmc_omap_host *host = dev_get_drvdata(dev); + + mmc_omap_enable_poll = enable_poll; + if (enable_poll && host->switch_pin >= 0) + schedule_work(&host->switch_work); + } + return size; +} + +static DEVICE_ATTR(enable_poll, 0664, + mmc_omap_show_enable_poll, mmc_omap_store_enable_poll); + +static void +mmc_omap_start_command(struct mmc_omap_host *host, struct mmc_command *cmd) +{ + u32 cmdreg; + u32 resptype; + u32 cmdtype; + + pr_debug("MMC%d: CMD%d, argument 0x%08x%s%s%s%s\n", + host->id, cmd->opcode, cmd->arg, + (cmd->flags & MMC_RSP_SHORT) ? ", 32-bit response" : "", + (cmd->flags & MMC_RSP_LONG) ? ", 128-bit response" : "", + (cmd->flags & MMC_RSP_CRC) ? ", CRC" : "", + (cmd->flags & MMC_RSP_BUSY) ? ", busy notification" : ""); + + host->cmd = cmd; + + resptype = 0; + cmdtype = 0; + + /* + * On 24xx we may have external MMC transceiver on Menelaus. + * In that case we need to manually toggle between open-drain + * and push-pull states. + */ + if (omap_has_menelaus() && (host->bus_mode != host->hw_bus_mode)) { + if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) + menelaus_mmc_opendrain(1); + else + menelaus_mmc_opendrain(0); + host->hw_bus_mode = host->bus_mode; + } + + if (!(cmd->flags & MMC_RSP_PRESENT)) + resptype = 0; /* Resp 0 */ + + if (cmd->flags & MMC_RSP_136) + resptype = 2; /* Resp 2 */ + else { + if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) + resptype = 3; /* Resp 3 */ + else + resptype = 1; /* Resp 1, Resp 1b */ + } + + /* Protocol layer does not provide command type, but our hardware + * needs it! + * any data transfer means adtc type (but that information is not + * in command structure, so we flagged it into host struct.) + * However, telling bc, bcr and ac apart based on response is + * not foolproof: + * CMD0 = bc = resp0 CMD15 = ac = resp0 + * CMD2 = bcr = resp2 CMD10 = ac = resp2 + * + * Resolve to best guess with some exception testing: + * resp0 -> bc, except CMD15 = ac + * rest are ac, except if opendrain + */ + if (host->data) { + cmdtype = OMAP_MMC_CMDTYPE_ADTC; + } else if (resptype == 0 && cmd->opcode != 15) { + cmdtype = OMAP_MMC_CMDTYPE_BC; + } else if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) { + cmdtype = OMAP_MMC_CMDTYPE_BCR; + } else { + cmdtype = OMAP_MMC_CMDTYPE_AC; + } + + cmdreg = cmd->opcode | (resptype << 8) | (cmdtype << 12); + + if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) + cmdreg |= 1 << 6; + + if (cmd->flags & MMC_RSP_BUSY) + cmdreg |= 1 << 11; + + if (host->data && !(host->data->flags & MMC_DATA_WRITE)) + cmdreg |= 1 << 15; + + clk_enable(host->fclk); + + OMAP_MMC_WRITE(host->base, CTO, 200); + OMAP_MMC_WRITE(host->base, ARGL, cmd->arg & 0xffff); + OMAP_MMC_WRITE(host->base, ARGH, cmd->arg >> 16); + OMAP_MMC_WRITE(host->base, IE, + OMAP_MMC_STAT_A_EMPTY | OMAP_MMC_STAT_A_FULL | + OMAP_MMC_STAT_CMD_CRC | OMAP_MMC_STAT_CMD_TOUT | + OMAP_MMC_STAT_DATA_CRC | OMAP_MMC_STAT_DATA_TOUT | + OMAP_MMC_STAT_END_OF_CMD | OMAP_MMC_STAT_CARD_ERR | + OMAP_MMC_STAT_END_OF_DATA); + OMAP_MMC_WRITE(host->base, CMD, cmdreg); +} + +static void +mmc_omap_xfer_done(struct mmc_omap_host *host, struct mmc_data *data) +{ + del_timer_sync(&host->xfer_timer); + + if (host->dma_in_use) { + enum dma_data_direction dma_data_dir; + + BUG_ON(host->dma_ch < 0); + if (data->error != MMC_ERR_NONE) + omap_stop_dma(host->dma_ch); + /* Release DMA channel lazily */ + mod_timer(&host->dma_timer, jiffies + HZ); + if (data->flags & MMC_DATA_WRITE) + dma_data_dir = DMA_TO_DEVICE; + else + dma_data_dir = DMA_FROM_DEVICE; + dma_unmap_sg(mmc_dev(host->mmc), data->sg, host->sg_len, + dma_data_dir); + } + host->data = NULL; + host->sg_len = 0; + clk_disable(host->fclk); + + /* NOTE: MMC layer will sometimes poll-wait CMD13 next, issuing + * dozens of requests until the card finishes writing data. + * It'd be cheaper to just wait till an EOFB interrupt arrives... + */ + + if (!data->stop) { + host->mrq = NULL; + mmc_request_done(host->mmc, data->mrq); + return; + } + + mmc_omap_start_command(host, data->stop); +} + +static void +mmc_omap_end_of_data(struct mmc_omap_host *host, struct mmc_data *data) +{ + unsigned long flags; + int done; + + if (!host->dma_in_use) { + mmc_omap_xfer_done(host, data); + return; + } + done = 0; + spin_lock_irqsave(&host->dma_lock, flags); + if (host->dma_done) + done = 1; + else + host->brs_received = 1; + spin_unlock_irqrestore(&host->dma_lock, flags); + if (done) + mmc_omap_xfer_done(host, data); +} + +static void +mmc_omap_dma_timer(unsigned long data) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) data; + + DBG("MMC%d: Freeing DMA channel %d\n", host->id, host->dma_ch); + BUG_ON(host->dma_ch < 0); + omap_free_dma(host->dma_ch); + host->dma_ch = -1; +} + +static void +mmc_omap_dma_done(struct mmc_omap_host *host, struct mmc_data *data) +{ + unsigned long flags; + int done; + + done = 0; + spin_lock_irqsave(&host->dma_lock, flags); + if (host->brs_received) + done = 1; + else + host->dma_done = 1; + spin_unlock_irqrestore(&host->dma_lock, flags); + if (done) + mmc_omap_xfer_done(host, data); +} + +static void +mmc_omap_cmd_done(struct mmc_omap_host *host, struct mmc_command *cmd, int card_ready) +{ + host->cmd = NULL; + + if (cmd->flags & MMC_RSP_136) { + /* Response type 2 */ + cmd->resp[3] = + OMAP_MMC_READ(host->base, RSP0) | + (OMAP_MMC_READ(host->base, RSP1) << 16); + cmd->resp[2] = + OMAP_MMC_READ(host->base, RSP2) | + (OMAP_MMC_READ(host->base, RSP3) << 16); + cmd->resp[1] = + OMAP_MMC_READ(host->base, RSP4) | + (OMAP_MMC_READ(host->base, RSP5) << 16); + cmd->resp[0] = + OMAP_MMC_READ(host->base, RSP6) | + (OMAP_MMC_READ(host->base, RSP7) << 16); + DBG("MMC%d: Response %08x %08x %08x %08x\n", host->id, + cmd->resp[0], cmd->resp[1], + cmd->resp[2], cmd->resp[3]); + } else { + /* Response types 1, 1b, 3, 4, 5, 6 */ + cmd->resp[0] = + OMAP_MMC_READ(host->base, RSP6) | + (OMAP_MMC_READ(host->base, RSP7) << 16); + DBG("MMC%d: Response %08x\n", host->id, cmd->resp[0]); + if (card_ready) { + pr_debug("MMC%d: Faking card ready based on EOFB\n", host->id); + cmd->resp[0] |= R1_READY_FOR_DATA; + } + } + + if (host->data == NULL || cmd->error != MMC_ERR_NONE) { + DBG("MMC%d: End request, err %x\n", host->id, cmd->error); + if (host->data != NULL) + del_timer_sync(&host->xfer_timer); + host->mrq = NULL; + clk_disable(host->fclk); + mmc_request_done(host->mmc, cmd->mrq); + } +} + +static void +mmc_omap_xfer_timeout(unsigned long data) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) data; + + printk(KERN_ERR "MMC%d: Data xfer timeout\n", host->id); + if (host->data != NULL) { + host->data->error |= MMC_ERR_TIMEOUT; + /* Perform a pseudo-reset of the MMC core logic, since + * the controller seems to get really stuck */ + OMAP_MMC_WRITE(host->base, CON, OMAP_MMC_READ(host->base, CON) & ~(1 << 11)); + OMAP_MMC_WRITE(host->base, CON, OMAP_MMC_READ(host->base, CON) | (1 << 11)); + mmc_omap_xfer_done(host, host->data); + } +} + +/* PIO only */ +static void +mmc_omap_sg_to_buf(struct mmc_omap_host *host) +{ + struct scatterlist *sg; + + sg = host->data->sg + host->sg_idx; + host->buffer_bytes_left = sg->length; + host->buffer = page_address(sg->page) + sg->offset; + if (host->buffer_bytes_left > host->total_bytes_left) + host->buffer_bytes_left = host->total_bytes_left; +} + +/* PIO only */ +static void +mmc_omap_xfer_data(struct mmc_omap_host *host, int write) +{ + int n; + void __iomem *reg; + u16 *p; + + if (host->buffer_bytes_left == 0) { + host->sg_idx++; + BUG_ON(host->sg_idx == host->sg_len); + mmc_omap_sg_to_buf(host); + } + n = 64; + if (n > host->buffer_bytes_left) + n = host->buffer_bytes_left; + host->buffer_bytes_left -= n; + host->total_bytes_left -= n; + host->data->bytes_xfered += n; + + /* Optimize the loop a bit by calculating the register only + * once */ + reg = host->base + OMAP_MMC_REG_DATA; + p = host->buffer; + n /= 2; + if (write) { + while (n--) + __raw_writew(*p++, reg); + } else { + while (n-- > 0) + *p++ = __raw_readw(reg); + } + host->buffer = p; +} + +static inline void mmc_omap_report_irq(u16 status) +{ + static const char *mmc_omap_status_bits[] = { + "EOC", "CD", "CB", "BRS", "EOFB", "DTO", "DCRC", "CTO", + "CCRC", "CRW", "AF", "AE", "OCRB", "CIRQ", "CERR" + }; + int i, c = 0; + + for (i = 0; i < ARRAY_SIZE(mmc_omap_status_bits); i++) + if (status & (1 << i)) { + if (c) + printk(" "); + printk("%s", mmc_omap_status_bits[i]); + c++; + } +} + +static irqreturn_t mmc_omap_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mmc_omap_host * host = (struct mmc_omap_host *)dev_id; + u16 status; + int end_command; + int end_transfer; + int card_ready; + int transfer_error; + + if (host->cmd == NULL && host->data == NULL) { + status = OMAP_MMC_READ(host->base, STAT); + printk(KERN_INFO "MMC%d: Spurious interrupt 0x%04x\n", host->id, status); + if (status != 0) { + OMAP_MMC_WRITE(host->base, STAT, status); + OMAP_MMC_WRITE(host->base, IE, 0); + } + return IRQ_HANDLED; + } + + end_command = 0; + end_transfer = 0; + card_ready = 0; + transfer_error = 0; + + while ((status = OMAP_MMC_READ(host->base, STAT)) != 0) { + OMAP_MMC_WRITE(host->base, STAT, status); // Reset status bits +#ifdef CONFIG_MMC_DEBUG + printk(KERN_DEBUG "\tMMC IRQ %04x (CMD %d): ", status, + host->cmd != NULL ? host->cmd->opcode : -1); + mmc_omap_report_irq(status); + printk("\n"); +#endif + if (host->total_bytes_left) { + if ((status & OMAP_MMC_STAT_A_FULL) || + (status & OMAP_MMC_STAT_END_OF_DATA)) + mmc_omap_xfer_data(host, 0); + if (status & OMAP_MMC_STAT_A_EMPTY) + mmc_omap_xfer_data(host, 1); + } + + if (status & OMAP_MMC_STAT_END_OF_DATA) { + // Block sent/received + end_transfer = 1; + } + + if (status & OMAP_MMC_STAT_DATA_TOUT) { + // Data timeout + printk(KERN_DEBUG "MMC%d: Data timeout\n", host->id); + if (host->data) { + host->data->error |= MMC_ERR_TIMEOUT; + transfer_error = 1; + } + } + + if (status & OMAP_MMC_STAT_DATA_CRC) { + // Data CRC error + if (host->data) { + host->data->error |= MMC_ERR_BADCRC; + printk(KERN_DEBUG "MMC%d: Data CRC error, bytes left %d\n", + host->id, host->total_bytes_left); + transfer_error = 1; + } else { + printk(KERN_DEBUG "MMC%d: Data CRC error\n", + host->id); + } + } + + if (status & OMAP_MMC_STAT_CMD_TOUT) { + /* Timeouts are routine with some commands */ + if (host->cmd) { + if (host->cmd->opcode != MMC_ALL_SEND_CID && + host->cmd->opcode != MMC_SEND_OP_COND && + host->cmd->opcode != MMC_APP_CMD && + !mmc_omap_cover_is_open(host)) + printk(KERN_ERR "MMC%d: Command timeout, CMD%d\n", + host->id, host->cmd->opcode); + host->cmd->error |= MMC_ERR_TIMEOUT; + end_command = 1; + } + } + + if (status & OMAP_MMC_STAT_CMD_CRC) { + // Command CRC error + if (host->cmd) { + printk(KERN_ERR "MMC%d: Command CRC error (CMD%d, arg 0x%08x)\n", + host->id, host->cmd->opcode, + host->cmd->arg); + host->cmd->error |= MMC_ERR_BADCRC; + end_command = 1; + } else + printk(KERN_ERR "MMC%d: Command CRC error without cmd?\n", host->id); + } + + if (status & OMAP_MMC_STAT_OCR_BUSY) { + /* OCR Busy ... happens a lot */ + if (host->cmd && host->cmd->opcode != MMC_SEND_OP_COND + && host->cmd->opcode != MMC_SET_RELATIVE_ADDR) { + DBG("MMC%d: OCR busy error, CMD%d\n", + host->id, host->cmd->opcode); + } + } + + if (status & OMAP_MMC_STAT_CARD_ERR) { + if (host->cmd && host->cmd->opcode == MMC_STOP_TRANSMISSION) { + u32 response = OMAP_MMC_READ(host->base, RSP6) + | (OMAP_MMC_READ(host->base, RSP7) << 16); + /* STOP sometimes sets must-ignore bits */ + if (!(response & (R1_CC_ERROR + | R1_ILLEGAL_COMMAND + | R1_COM_CRC_ERROR))) { + end_command = 1; + continue; + } + } + + // Card status error + printk(KERN_DEBUG "MMC%d: Card status error (CMD%d)\n", + host->id, host->cmd->opcode); + if (host->cmd) { + host->cmd->error |= MMC_ERR_FAILED; + end_command = 1; + } + if (host->data) { + host->data->error |= MMC_ERR_FAILED; + transfer_error = 1; + } + } + + /* + * NOTE: On 1610 the END_OF_CMD may come too early when + * starting a write + */ + if ((status & OMAP_MMC_STAT_END_OF_CMD) && + (!(status & OMAP_MMC_STAT_A_EMPTY))) { + // End of command phase + end_command = 1; + } + /* + * Some cards produce EOFB interrupt and never + * raise R1_READY_FOR_DATA bit after that. + * To avoid infinite card status polling loop, + * we must fake that bit to MMC layer. + */ + if ((status & OMAP_MMC_STAT_END_OF_CMD) && + (status & OMAP_MMC_STAT_END_BUSY)) { + card_ready = 1; + } + } + + if (end_command) { + mmc_omap_cmd_done(host, host->cmd, card_ready); + } + if (transfer_error) + mmc_omap_xfer_done(host, host->data); + else if (end_transfer) + mmc_omap_end_of_data(host, host->data); + + return IRQ_HANDLED; +} + +static irqreturn_t mmc_omap_switch_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) dev_id; + int cover_open, detect_now; + + cover_open = mmc_omap_cover_is_open(host); + DBG("MMC%d cover is now %s\n", host->id, + cover_open ? "open" : "closed"); + set_irq_type(OMAP_GPIO_IRQ(host->switch_pin), 0); + detect_now = 0; + if (host->switch_last_state != cover_open) { + /* If the cover was just opened and a card is inserted, + * we want to inform user-space about the event as soon as + * possible */ + if (cover_open) { + struct mmc_card *card; + + list_for_each_entry(card, &host->mmc->cards, node) + if (mmc_card_present(card)) + detect_now = 1; + } + } + if (detect_now) + schedule_work(&host->switch_work); + else { + /* Delay the switch work a little bit to get rid of the GPIO + * line bounces */ + mod_timer(&host->switch_timer, + jiffies + msecs_to_jiffies(OMAP_MMC_SWITCH_POLL_DELAY) / 2); + } + + return IRQ_HANDLED; +} + +static void mmc_omap_switch_timer(unsigned long arg) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) arg; + + schedule_work(&host->switch_work); +} + +/* FIXME: Handle card insertion and removal properly. Maybe use a mask + * for MMC state? */ +static void mmc_omap_switch_callback(unsigned long data, u8 mmc_mask) +{ + if (machine_is_omap_h4()) { + if (mmc_mask & 0x1) + printk("XXX card in slot 1\n"); + if (mmc_mask & 0x2) + printk("XXX card in slot 2\n"); + } else { + /* Assume card detect connected to cover switch */ + if (mmc_mask & 0x2) + printk("XXX cover open\n"); + else + printk("XXX cover closed\n"); + } +} + +static void mmc_omap_switch_handler(void *data) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) data; + struct mmc_card *card; + static int complained = 0; + int cards = 0, cover_open; + + if (host->switch_pin == -1) + return; + set_irq_type(OMAP_GPIO_IRQ(host->switch_pin), IRQT_RISING | IRQT_FALLING); + cover_open = mmc_omap_cover_is_open(host); + if (cover_open != host->switch_last_state) { + kobject_uevent(&host->dev->kobj, KOBJ_CHANGE); + host->switch_last_state = cover_open; + } + DBG("MMC cover switch handler started\n"); + mmc_detect_change(host->mmc, 0); + list_for_each_entry(card, &host->mmc->cards, node) { + if (mmc_card_present(card)) + cards++; + } + DBG("MMC%d: %d card(s) present\n", host->id, cards); + if (cover_open) { + if (!complained) { + printk(KERN_INFO "MMC%d: cover is open\n", host->id); + complained = 1; + } + if (cover_open && (cards || mmc_omap_enable_poll)) + mod_timer(&host->switch_timer, jiffies + + msecs_to_jiffies(OMAP_MMC_SWITCH_POLL_DELAY)); + } else { + complained = 0; + } +} + +/* prepare to transfer the next segment of a scatterlist */ +static void +mmc_omap_prepare_dma(struct mmc_omap_host *host, struct mmc_data *data) +{ + int dma_ch = host->dma_ch; + unsigned long data_addr; + u16 buf, frame; + u32 count; + struct scatterlist *sg = &data->sg[host->sg_idx]; + int src_port = 0; + int dst_port = 0; + int sync_dev = 0; + + data_addr = (unsigned long)io_v2p((void __force *) host->base) + OMAP_MMC_REG_DATA; + frame = 1 << data->blksz_bits; + count = (u32)sg_dma_len(sg); + + /* the MMC layer is confused about single block writes... */ + if ((data->blocks == 1) && (count > (1 << data->blksz_bits))) { + pr_debug("patch bogus single block length! %d > %d\n", + count, frame); + count = frame; + } + host->dma_len = count; + + /* FIFO is 16x2 bytes on 15xx, and 32x2 bytes on 16xx and 24xx. + * Use 16 or 32 word frames when the blocksize is at least that large. + * Blocksize is usually 512 bytes; but not for some SD reads. + */ + if (cpu_is_omap15xx() && frame > 32) + frame = 32; + else if (frame > 64) + frame = 64; + count /= frame; + frame >>= 1; + + if (!(data->flags & MMC_DATA_WRITE)) { + buf = 0x800f | ((frame - 1) << 8); + + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_TIPB; + dst_port = OMAP_DMA_PORT_EMIFF; + } + if (cpu_is_omap24xx()) + sync_dev = OMAP24XX_DMA_MMC1_RX; + + omap_set_dma_src_params(dma_ch, src_port, + OMAP_DMA_AMODE_CONSTANT, + data_addr, 0, 0); + omap_set_dma_dest_params(dma_ch, dst_port, + OMAP_DMA_AMODE_POST_INC, + sg_dma_address(sg), 0, 0); + omap_set_dma_dest_data_pack(dma_ch, 1); + omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); + } else { + buf = 0x0f80 | ((frame - 1) << 0); + + if (cpu_class_is_omap1()) { + src_port = OMAP_DMA_PORT_EMIFF; + dst_port = OMAP_DMA_PORT_TIPB; + } + if (cpu_is_omap24xx()) + sync_dev = OMAP24XX_DMA_MMC1_TX; + + omap_set_dma_dest_params(dma_ch, dst_port, + OMAP_DMA_AMODE_CONSTANT, + data_addr, 0, 0); + omap_set_dma_src_params(dma_ch, src_port, + OMAP_DMA_AMODE_POST_INC, + sg_dma_address(sg), 0, 0); + omap_set_dma_src_data_pack(dma_ch, 1); + omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); + } + + /* Max limit for DMA frame count is 0xffff */ + if (unlikely(count > 0xffff)) + BUG(); + + OMAP_MMC_WRITE(host->base, BUF, buf); + omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S16, + frame, count, OMAP_DMA_SYNC_FRAME, + sync_dev, 0); +} + +/* a scatterlist segment completed */ +static void mmc_omap_dma_cb(int lch, u16 ch_status, void *data) +{ + struct mmc_omap_host *host = (struct mmc_omap_host *) data; + struct mmc_data *mmcdat = host->data; + + if (unlikely(host->dma_ch < 0)) { + printk(KERN_ERR "MMC%d: DMA callback while DMA not enabled\n", + host->id); + return; + } + /* FIXME: We really should do something to _handle_ the errors */ + if (ch_status & OMAP_DMA_TOUT_IRQ) { + printk(KERN_ERR "MMC%d: DMA timeout\n", host->id); + return; + } + if (ch_status & OMAP_DMA_DROP_IRQ) { + printk(KERN_ERR "MMC%d: DMA sync error\n", host->id); + return; + } + if (!(ch_status & OMAP_DMA_BLOCK_IRQ)) { + /* REVISIT we should be able to avoid getting IRQs with + * just SYNC status ... + */ + if ((ch_status & ~OMAP1_DMA_SYNC_IRQ)) + pr_debug("MMC%d: DMA channel status: %04x\n", + host->id, ch_status); + return; + } + mmcdat->bytes_xfered += host->dma_len; + + pr_debug("\tMMC DMA %d bytes CB %04x (%d segments to go), %p\n", + host->dma_len, ch_status, + host->sg_len - host->sg_idx - 1, host->data); + + host->sg_idx++; + if (host->sg_idx < host->sg_len) { + mmc_omap_prepare_dma(host, host->data); + omap_start_dma(host->dma_ch); + } else + mmc_omap_dma_done(host, host->data); +} + +static int mmc_omap_get_dma_channel(struct mmc_omap_host *host, struct mmc_data *data) +{ + const char *dev_name; + int sync_dev, dma_ch, is_read, r; + + is_read = !(data->flags & MMC_DATA_WRITE); + del_timer_sync(&host->dma_timer); + if (host->dma_ch >= 0) { + if (is_read == host->dma_is_read) + return 0; + omap_free_dma(host->dma_ch); + host->dma_ch = -1; + } + + if (is_read) { + if (host->id == 1) { + sync_dev = OMAP_DMA_MMC_RX; + dev_name = "MMC1 read"; + } else { + sync_dev = OMAP_DMA_MMC2_RX; + dev_name = "MMC2 read"; + } + } else { + if (host->id == 1) { + sync_dev = OMAP_DMA_MMC_TX; + dev_name = "MMC1 write"; + } else { + sync_dev = OMAP_DMA_MMC2_TX; + dev_name = "MMC2 write"; + } + } + r = omap_request_dma(sync_dev, dev_name, mmc_omap_dma_cb, + host, &dma_ch); + if (r != 0) { + printk("MMC%d: omap_request_dma() failed with %d\n", + host->id, r); + return r; + } + host->dma_ch = dma_ch; + host->dma_is_read = is_read; + + return 0; +} + +static inline void set_cmd_timeout(struct mmc_omap_host *host, struct mmc_request *req) +{ + u16 reg; + + reg = OMAP_MMC_READ(host->base, SDIO); + reg &= ~(1 << 5); + OMAP_MMC_WRITE(host->base, SDIO, reg); + /* Set maximum timeout */ + OMAP_MMC_WRITE(host->base, CTO, 0xff); +} + +static inline void set_data_timeout(struct mmc_omap_host *host, struct mmc_request *req) +{ + int timeout; + u16 reg; + + /* Convert ns to clock cycles by assuming 20MHz frequency + * 1 cycle at 20MHz = 500 ns + */ + timeout = req->data->timeout_clks + req->data->timeout_ns / 500; + + /* Some cards require more time to do at least the first read operation */ + timeout = timeout << 4; + + /* Check if we need to use timeout multiplier register */ + reg = OMAP_MMC_READ(host->base, SDIO); + if (timeout > 0xffff) { + reg |= (1 << 5); + timeout /= 1024; + } else + reg &= ~(1 << 5); + OMAP_MMC_WRITE(host->base, SDIO, reg); + OMAP_MMC_WRITE(host->base, DTO, timeout); +} + +static void +mmc_omap_prepare_data(struct mmc_omap_host *host, struct mmc_request *req) +{ + struct mmc_data *data = req->data; + int i, use_dma, block_size; + unsigned sg_len; + + host->data = data; + if (data == NULL) { + OMAP_MMC_WRITE(host->base, BLEN, 0); + OMAP_MMC_WRITE(host->base, NBLK, 0); + OMAP_MMC_WRITE(host->base, BUF, 0); + host->dma_in_use = 0; + set_cmd_timeout(host, req); + return; + } + + + block_size = 1 << data->blksz_bits; + + OMAP_MMC_WRITE(host->base, NBLK, data->blocks - 1); + OMAP_MMC_WRITE(host->base, BLEN, block_size - 1); + set_data_timeout(host, req); + + /* cope with calling layer confusion; it issues "single + * block" writes using multi-block scatterlists. + */ + sg_len = (data->blocks == 1) ? 1 : data->sg_len; + + /* Only do DMA for entire blocks */ + use_dma = host->use_dma; + if (use_dma) { + for (i = 0; i < sg_len; i++) { + if ((data->sg[i].length % block_size) != 0) { + use_dma = 0; + break; + } + } + } + + host->sg_idx = 0; + if (use_dma) { + if (mmc_omap_get_dma_channel(host, data) == 0) { + enum dma_data_direction dma_data_dir; + + if (data->flags & MMC_DATA_WRITE) + dma_data_dir = DMA_TO_DEVICE; + else + dma_data_dir = DMA_FROM_DEVICE; + + host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, + sg_len, dma_data_dir); + host->total_bytes_left = 0; + mmc_omap_prepare_dma(host, req->data); + host->brs_received = 0; + host->dma_done = 0; + host->dma_in_use = 1; + } else + use_dma = 0; + } + + /* Revert to PIO? */ + if (!use_dma) { + OMAP_MMC_WRITE(host->base, BUF, 0x1f1f); + host->total_bytes_left = data->blocks * block_size; + host->sg_len = sg_len; + mmc_omap_sg_to_buf(host); + host->dma_in_use = 0; + } + mod_timer(&host->xfer_timer, jiffies + msecs_to_jiffies(500)); + + pr_debug("MMC%d: %s %s %s, DTO %d cycles + %d ns, " + "%d blocks of %d bytes, %d segments\n", + host->id, use_dma ? "DMA" : "PIO", + (data->flags & MMC_DATA_STREAM) ? "stream" : "block", + (data->flags & MMC_DATA_WRITE) ? "write" : "read", + data->timeout_clks, data->timeout_ns, data->blocks, + block_size, host->sg_len); +} + +static inline int is_broken_card(struct mmc_card *card) +{ + int i; + struct mmc_cid *c = &card->cid; + static const struct broken_card_cid { + unsigned int manfid; + char prod_name[8]; + unsigned char hwrev; + unsigned char fwrev; + } broken_cards[] = { + { 0x00150000, "\x30\x30\x30\x30\x30\x30\x15\x00", 0x06, 0x03 }, + }; + + for (i = 0; i < sizeof(broken_cards)/sizeof(broken_cards[0]); i++) { + const struct broken_card_cid *b = broken_cards + i; + + if (b->manfid != c->manfid) + continue; + if (memcmp(b->prod_name, c->prod_name, sizeof(b->prod_name)) != 0) + continue; + if (b->hwrev != c->hwrev || b->fwrev != c->fwrev) + continue; + return 1; + } + return 0; +} + +static void mmc_omap_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + + WARN_ON(host->mrq != NULL); + + host->mrq = req; + + /* Some cards (vendor left unnamed to protect the guilty) seem to + * require this delay after power-up. Otherwise we'll get mysterious + * data timeouts. */ + if (req->cmd->opcode == MMC_SEND_CSD) { + struct mmc_card *card; + int broken_present = 0; + + list_for_each_entry(card, &mmc->cards, node) { + if (is_broken_card(card)) { + broken_present = 1; + break; + } + } + if (broken_present) { + static int complained = 0; + + if (!complained) { + printk(KERN_WARNING "MMC%d: Broken card workaround enabled\n", + host->id); + complained = 1; + } + if (in_interrupt()) { + /* This is nasty */ + printk(KERN_ERR "Sleeping in IRQ handler, FIXME please!\n"); + dump_stack(); + mdelay(100); + } else { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(100 * HZ / 1000); + } + } + } + + /* only touch fifo AFTER the controller readies it */ + mmc_omap_prepare_data(host, req); + mmc_omap_start_command(host, req->cmd); + if (host->dma_in_use) + omap_start_dma(host->dma_ch); +} + +static void innovator_fpga_socket_power(int on) +{ +#if defined(CONFIG_MACH_OMAP_INNOVATOR) && defined(CONFIG_ARCH_OMAP15XX) + + if (on) { + fpga_write(fpga_read(OMAP1510_FPGA_POWER) | (1 << 3), + OMAP1510_FPGA_POWER); + } else { + fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~(1 << 3), + OMAP1510_FPGA_POWER); + } +#endif +} + +/* + * Turn the socket power on/off. Innovator uses FPGA, most boards + * probably use GPIO. + */ +static void mmc_omap_power(struct mmc_omap_host *host, int on) +{ + if (on) { + if (machine_is_omap_innovator()) + innovator_fpga_socket_power(1); + else if (machine_is_omap_h2()) + tps65010_set_gpio_out_value(GPIO3, HIGH); + else if (machine_is_omap_h3()) + /* GPIO 4 of TPS65010 sends SD_EN signal */ + tps65010_set_gpio_out_value(GPIO4, HIGH); + else if (cpu_is_omap24xx()) { + u16 reg = OMAP_MMC_READ(host->base, CON); + OMAP_MMC_WRITE(host->base, CON, reg | (1 << 11)); + } else + if (host->power_pin >= 0) + omap_set_gpio_dataout(host->power_pin, 1); + } else { + if (machine_is_omap_innovator()) + innovator_fpga_socket_power(0); + else if (machine_is_omap_h2()) + tps65010_set_gpio_out_value(GPIO3, LOW); + else if (machine_is_omap_h3()) + tps65010_set_gpio_out_value(GPIO4, LOW); + else if (cpu_is_omap24xx()) { + u16 reg = OMAP_MMC_READ(host->base, CON); + OMAP_MMC_WRITE(host->base, CON, reg & ~(1 << 11)); + } else + if (host->power_pin >= 0) + omap_set_gpio_dataout(host->power_pin, 0); + } +} + +static void mmc_omap_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + int dsor; + int realclock, i; + + DBG("MMC%d: set_ios: clock %dHz busmode %d powermode %d Vdd %d.%02d\n", + host->id, ios->clock, ios->bus_mode, ios->power_mode, + ios->vdd / 100, ios->vdd % 100); + + if (ios->power_mode == MMC_POWER_UP && ios->clock < 400000) + realclock = 400000; /* Fix for broken stack */ + else + realclock = ios->clock; + + if (ios->clock == 0) + dsor = 0; + else { + int func_clk_rate = clk_get_rate(host->fclk); + + dsor = func_clk_rate / realclock; + if (dsor < 1) + dsor = 1; + + if (func_clk_rate / dsor > realclock) + dsor++; + + if (dsor > 250) + dsor = 250; + dsor++; + + if (ios->bus_width == MMC_BUS_WIDTH_4) + dsor |= 1 << 15; + } + + switch (ios->power_mode) { + case MMC_POWER_OFF: + mmc_omap_power(host, 0); + break; + case MMC_POWER_UP: + case MMC_POWER_ON: + mmc_omap_power(host, 1); + dsor |= 1<<11; + break; + } + + host->bus_mode = ios->bus_mode; + if (omap_has_menelaus()) { + if (host->bus_mode == MMC_BUSMODE_OPENDRAIN) + menelaus_mmc_opendrain(1); + else + menelaus_mmc_opendrain(0); + } + host->hw_bus_mode = host->bus_mode; + + clk_enable(host->fclk); + + /* On insanely high arm_per frequencies something sometimes + * goes somehow out of sync, and the POW bit is not being set, + * which results in the while loop below getting stuck. + * Writing to the CON register twice seems to do the trick. */ + for (i = 0; i < 2; i++) + OMAP_MMC_WRITE(host->base, CON, dsor); + if (ios->power_mode == MMC_POWER_UP) { + /* Wait a little while for the power regulator to + * settle */ + msleep(1); + /* Send clock cycles, poll completion */ + OMAP_MMC_WRITE(host->base, IE, 0); + OMAP_MMC_WRITE(host->base, STAT, 0xffff); + OMAP_MMC_WRITE(host->base, CMD, 1<<7); + while (0 == (OMAP_MMC_READ(host->base, STAT) & 1)); + OMAP_MMC_WRITE(host->base, STAT, 1); + } + clk_disable(host->fclk); +} + +static int mmc_omap_get_ro(struct mmc_host *mmc) +{ + struct mmc_omap_host *host = mmc_priv(mmc); + + return host->wp_pin && omap_get_gpio_datain(host->wp_pin); +} + +static struct mmc_host_ops mmc_omap_ops = { + .request = mmc_omap_request, + .set_ios = mmc_omap_set_ios, + .get_ro = mmc_omap_get_ro, +}; + +static int __init mmc_omap_probe(struct platform_device *pdev) +{ + struct omap_mmc_conf *minfo = pdev->dev.platform_data; + struct mmc_host *mmc; + struct mmc_omap_host *host = NULL; + int ret = 0; + + if (pdev->resource[0].flags != IORESOURCE_MEM + || pdev->resource[1].flags != IORESOURCE_IRQ) { + printk(KERN_ERR "mmc_omap_probe: invalid resource type\n"); + return -ENODEV; + } + + if (!request_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1, + pdev->name)) { + dev_dbg(&pdev->dev, "request_mem_region failed\n"); + return -EBUSY; + } + + mmc = mmc_alloc_host(sizeof(struct mmc_omap_host), &pdev->dev); + if (!mmc) { + ret = -ENOMEM; + goto out; + } + + host = mmc_priv(mmc); + host->mmc = mmc; + + spin_lock_init(&host->dma_lock); + init_timer(&host->dma_timer); + host->dma_timer.function = mmc_omap_dma_timer; + host->dma_timer.data = (unsigned long) host; + + init_timer(&host->xfer_timer); + host->xfer_timer.function = mmc_omap_xfer_timeout; + host->xfer_timer.data = (unsigned long) host; + + host->id = pdev->id; + + if (cpu_is_omap24xx()) { + host->iclk = clk_get(&pdev->dev, "mmc_ick"); + if (IS_ERR(host->iclk)) + goto out; + clk_enable(host->iclk); + } + + if (!cpu_is_omap24xx()) + host->fclk = clk_get(&pdev->dev, "mmc_ck"); + else + host->fclk = clk_get(&pdev->dev, "mmc_fck"); + + if (IS_ERR(host->fclk)) { + ret = PTR_ERR(host->fclk); + goto out; + } + + /* REVISIT: + * Also, use minfo->cover to decide how to manage + * the card detect sensing. + */ + host->power_pin = minfo->power_pin; + host->switch_pin = minfo->switch_pin; + host->wp_pin = minfo->wp_pin; + host->use_dma = 1; + host->dma_ch = -1; + + host->irq = pdev->resource[1].start; + host->base = (void __iomem *)pdev->resource[0].start; + + if (minfo->wire4) + mmc->caps |= MMC_CAP_4_BIT_DATA; + + mmc->ops = &mmc_omap_ops; + mmc->f_min = 400000; + mmc->f_max = 24000000; + mmc->ocr_avail = MMC_VDD_33_34; + + /* Use scatterlist DMA to reduce per-transfer costs. + * NOTE max_seg_size assumption that small blocks aren't + * normally used (except e.g. for reading SD registers). + */ + mmc->max_phys_segs = 32; + mmc->max_hw_segs = 32; + mmc->max_sectors = 120; /* NBLK max 11-bits, OMAP also limited by DMA */ + mmc->max_seg_size = mmc->max_sectors * 512; + + if (host->power_pin >= 0) { + if ((ret = omap_request_gpio(host->power_pin)) != 0) { + printk(KERN_ERR "MMC%d: Unable to get GPIO pin for MMC power\n", + host->id); + goto out; + } + omap_set_gpio_direction(host->power_pin, 0); + } + + ret = request_irq(host->irq, mmc_omap_irq, 0, DRIVER_NAME, host); + if (ret) + goto out; + + host->dev = &pdev->dev; + platform_set_drvdata(pdev, host); + + mmc_add_host(mmc); + + if (host->switch_pin >= 0) { + INIT_WORK(&host->switch_work, mmc_omap_switch_handler, host); + init_timer(&host->switch_timer); + host->switch_timer.function = mmc_omap_switch_timer; + host->switch_timer.data = (unsigned long) host; + if (omap_request_gpio(host->switch_pin) != 0) { + printk(KERN_WARNING "MMC%d: Unable to get GPIO pin for MMC cover switch\n", + host->id); + host->switch_pin = -1; + goto no_switch; + } + + omap_set_gpio_direction(host->switch_pin, 1); + ret = request_irq(OMAP_GPIO_IRQ(host->switch_pin), + mmc_omap_switch_irq, + SA_TRIGGER_RISING | SA_TRIGGER_FALLING, + DRIVER_NAME, host); + if (ret) { + printk(KERN_WARNING "MMC%d: Unable to get IRQ for MMC cover switch\n", + host->id); + omap_free_gpio(host->switch_pin); + host->switch_pin = -1; + goto no_switch; + } + ret = device_create_file(&pdev->dev, &dev_attr_cover_switch); + if (ret == 0) { + ret = device_create_file(&pdev->dev, &dev_attr_enable_poll); + if (ret != 0) + device_remove_file(&pdev->dev, &dev_attr_cover_switch); + } + if (ret) { + printk(KERN_WARNING "MMC%d: Unable to create sysfs attributes\n", + host->id); + free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); + omap_free_gpio(host->switch_pin); + host->switch_pin = -1; + goto no_switch; + } + host->switch_last_state = mmc_omap_cover_is_open(host); + if (mmc_omap_enable_poll && mmc_omap_cover_is_open(host)) + schedule_work(&host->switch_work); + } + + if (omap_has_menelaus()) + menelaus_mmc_register(mmc_omap_switch_callback, + (unsigned long)&host); + +no_switch: + return 0; + +out: + /* FIXME: Free other resources too. */ + if (host) { + if (host->iclk && !IS_ERR(host->iclk)) + clk_put(host->iclk); + if (host->fclk && !IS_ERR(host->fclk)) + clk_put(host->fclk); + mmc_free_host(host->mmc); + } + return ret; +} + +static int mmc_omap_remove(struct platform_device *pdev) +{ + struct mmc_omap_host *host = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + if (host) { + mmc_remove_host(host->mmc); + free_irq(host->irq, host); + mmc_omap_power(host, 0); + + if (host->power_pin >= 0) + omap_free_gpio(host->power_pin); + if (host->switch_pin >= 0) { + device_remove_file(&pdev->dev, &dev_attr_enable_poll); + device_remove_file(&pdev->dev, &dev_attr_cover_switch); + free_irq(OMAP_GPIO_IRQ(host->switch_pin), host); + omap_free_gpio(host->switch_pin); + host->switch_pin = -1; + del_timer_sync(&host->switch_timer); + flush_scheduled_work(); + } + if (host->iclk && !IS_ERR(host->iclk)) + clk_put(host->iclk); + if (host->fclk && !IS_ERR(host->fclk)) + clk_put(host->fclk); + mmc_free_host(host->mmc); + } + + if (omap_has_menelaus()) + menelaus_mmc_remove(); + + release_mem_region(pdev->resource[0].start, + pdev->resource[0].end - pdev->resource[0].start + 1); + + return 0; +} + +#ifdef CONFIG_PM +static int mmc_omap_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + int ret = 0; + struct mmc_omap_host *host = platform_get_drvdata(pdev); + + if (host && host->suspended) + return 0; + + if (!irqs_disabled()) + return -EAGAIN; + + if (host) { + ret = mmc_suspend_host(host->mmc, mesg); + if (ret == 0) + host->suspended = 1; + } + return ret; +} + +static int mmc_omap_resume(struct platform_device *pdev) +{ + int ret = 0; + struct mmc_omap_host *host = platform_get_drvdata(pdev); + + if (host && !host->suspended) + return 0; + + if (host) { + ret = mmc_resume_host(host->mmc); + if (ret == 0) + host->suspended = 0; + } + + return ret; +} +#else +#define mmc_omap_suspend NULL +#define mmc_omap_resume NULL +#endif + +static struct platform_driver mmc_omap_driver = { + .probe = mmc_omap_probe, + .remove = mmc_omap_remove, + .suspend = mmc_omap_suspend, + .resume = mmc_omap_resume, + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init mmc_omap_init(void) +{ + return platform_driver_register(&mmc_omap_driver); +} + +static void __exit mmc_omap_exit(void) +{ + platform_driver_unregister(&mmc_omap_driver); +} + +module_init(mmc_omap_init); +module_exit(mmc_omap_exit); + +MODULE_DESCRIPTION("OMAP Multimedia Card driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS(DRIVER_NAME); +MODULE_AUTHOR("Juha Yrjölä"); diff --git a/drivers/mmc/omap.h b/drivers/mmc/omap.h new file mode 100644 index 00000000000..c954d355a5e --- /dev/null +++ b/drivers/mmc/omap.h @@ -0,0 +1,55 @@ +#ifndef DRIVERS_MEDIA_MMC_OMAP_H +#define DRIVERS_MEDIA_MMC_OMAP_H + +#define OMAP_MMC_REG_CMD 0x00 +#define OMAP_MMC_REG_ARGL 0x04 +#define OMAP_MMC_REG_ARGH 0x08 +#define OMAP_MMC_REG_CON 0x0c +#define OMAP_MMC_REG_STAT 0x10 +#define OMAP_MMC_REG_IE 0x14 +#define OMAP_MMC_REG_CTO 0x18 +#define OMAP_MMC_REG_DTO 0x1c +#define OMAP_MMC_REG_DATA 0x20 +#define OMAP_MMC_REG_BLEN 0x24 +#define OMAP_MMC_REG_NBLK 0x28 +#define OMAP_MMC_REG_BUF 0x2c +#define OMAP_MMC_REG_SDIO 0x34 +#define OMAP_MMC_REG_REV 0x3c +#define OMAP_MMC_REG_RSP0 0x40 +#define OMAP_MMC_REG_RSP1 0x44 +#define OMAP_MMC_REG_RSP2 0x48 +#define OMAP_MMC_REG_RSP3 0x4c +#define OMAP_MMC_REG_RSP4 0x50 +#define OMAP_MMC_REG_RSP5 0x54 +#define OMAP_MMC_REG_RSP6 0x58 +#define OMAP_MMC_REG_RSP7 0x5c +#define OMAP_MMC_REG_IOSR 0x60 +#define OMAP_MMC_REG_SYSC 0x64 +#define OMAP_MMC_REG_SYSS 0x68 + +#define OMAP_MMC_STAT_CARD_ERR (1 << 14) +#define OMAP_MMC_STAT_CARD_IRQ (1 << 13) +#define OMAP_MMC_STAT_OCR_BUSY (1 << 12) +#define OMAP_MMC_STAT_A_EMPTY (1 << 11) +#define OMAP_MMC_STAT_A_FULL (1 << 10) +#define OMAP_MMC_STAT_CMD_CRC (1 << 8) +#define OMAP_MMC_STAT_CMD_TOUT (1 << 7) +#define OMAP_MMC_STAT_DATA_CRC (1 << 6) +#define OMAP_MMC_STAT_DATA_TOUT (1 << 5) +#define OMAP_MMC_STAT_END_BUSY (1 << 4) +#define OMAP_MMC_STAT_END_OF_DATA (1 << 3) +#define OMAP_MMC_STAT_CARD_BUSY (1 << 2) +#define OMAP_MMC_STAT_END_OF_CMD (1 << 0) + +#define OMAP_MMC_READ(base, reg) __raw_readw((base) + OMAP_MMC_REG_##reg) +#define OMAP_MMC_WRITE(base, reg, val) __raw_writew((val), (base) + OMAP_MMC_REG_##reg) + +/* + * Command types + */ +#define OMAP_MMC_CMDTYPE_BC 0 +#define OMAP_MMC_CMDTYPE_BCR 1 +#define OMAP_MMC_CMDTYPE_AC 2 +#define OMAP_MMC_CMDTYPE_ADTC 3 + +#endif diff --git a/drivers/mtd/maps/omap_nor.c b/drivers/mtd/maps/omap_nor.c index 418afffb2d8..6614e9721f9 100644 --- a/drivers/mtd/maps/omap_nor.c +++ b/drivers/mtd/maps/omap_nor.c @@ -61,12 +61,14 @@ static void omap_set_vpp(struct map_info *map, int enable) { static int count; - if (enable) { - if (count++ == 0) - OMAP_EMIFS_CONFIG_REG |= OMAP_EMIFS_CONFIG_WP; - } else { - if (count && (--count == 0)) - OMAP_EMIFS_CONFIG_REG &= ~OMAP_EMIFS_CONFIG_WP; + if (!cpu_is_omap24xx()) { + if (enable) { + if (count++ == 0) + OMAP_EMIFS_CONFIG_REG |= OMAP_EMIFS_CONFIG_WP; + } else { + if (count && (--count == 0)) + OMAP_EMIFS_CONFIG_REG &= ~OMAP_EMIFS_CONFIG_WP; + } } } @@ -135,11 +137,12 @@ out_free_info: static int __devexit omapflash_remove(struct platform_device *pdev) { struct omapflash_info *info = platform_get_drvdata(pdev); + struct flash_platform_data *pdata = pdev->dev.platform_data; platform_set_drvdata(pdev, NULL); if (info) { - if (info->parts) { + if (info->parts || (pdata && pdata->parts)) { del_mtd_partitions(info->mtd); kfree(info->parts); } else diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 1fc4c134d93..f4c4e8e9a3b 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -49,6 +49,12 @@ config MTD_NAND_SPIA help If you had to ask, you don't have one. Say 'N'. +config MTD_NAND_OMAP + tristate "NAND Flash device on OMAP H3/H2/P2 boards" + depends on ARM && ARCH_OMAP1 && MTD_NAND && (MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_PERSEUS2) + help + Support for NAND flash on Texas Instruments H3/H2/P2 platforms. + config MTD_NAND_TOTO tristate "NAND Flash device on TOTO board" depends on ARCH_OMAP && MTD_NAND @@ -190,5 +196,12 @@ config MTD_NAND_DISKONCHIP_BBTWRITE help The simulator may simulate verious NAND flash chips for the MTD nand layer. - + +config MTD_NAND_OMAP_HW + bool "OMAP HW NAND Flash controller support" + depends on ARM && ARCH_OMAP16XX && MTD_NAND + + help + Driver for TI OMAP16xx hardware NAND flash controller. + endmenu diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 41742026a52..dc2929f996b 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -18,5 +18,7 @@ obj-$(CONFIG_MTD_NAND_H1900) += h1910.o obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o +obj-$(CONFIG_MTD_NAND_OMAP) += omap-nand-flash.o +obj-$(CONFIG_MTD_NAND_OMAP_HW) += omap-hw.o nand-objs = nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/omap-hw.c b/drivers/mtd/nand/omap-hw.c new file mode 100644 index 00000000000..d0e7e829e71 --- /dev/null +++ b/drivers/mtd/nand/omap-hw.c @@ -0,0 +1,871 @@ +/* + * drivers/mtd/nand/omap-hw.c + * + * This is the MTD driver for OMAP1710 internal HW NAND controller. + * + * Copyright (C) 2004-2006 Nokia Corporation + * + * Author: Jarkko Lavinen and + * Juha Yrjölä + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation. + * + * 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; see the file COPYING. If not, write to the Free Software + * Foundation, 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 + +#define NAND_BASE 0xfffbcc00 +#define NND_REVISION 0x00 +#define NND_ACCESS 0x04 +#define NND_ADDR_SRC 0x08 +#define NND_CTRL 0x10 +#define NND_MASK 0x14 +#define NND_STATUS 0x18 +#define NND_READY 0x1c +#define NND_COMMAND 0x20 +#define NND_COMMAND_SEC 0x24 +#define NND_ECC_SELECT 0x28 +#define NND_ECC_START 0x2c +#define NND_ECC_9 0x4c +#define NND_RESET 0x50 +#define NND_FIFO 0x54 +#define NND_FIFOCTRL 0x58 +#define NND_PSC_CLK 0x5c +#define NND_SYSTEST 0x60 +#define NND_SYSCFG 0x64 +#define NND_SYSSTATUS 0x68 +#define NND_FIFOTEST1 0x6c +#define NND_FIFOTEST2 0x70 +#define NND_FIFOTEST3 0x74 +#define NND_FIFOTEST4 0x78 +#define NND_PSC1_CLK 0x8c +#define NND_PSC2_CLK 0x90 + + +#define NND_CMD_READ1_LOWER 0x00 +#define NND_CMD_WRITE1_LOWER 0x00 +#define NND_CMD_READ1_UPPER 0x01 +#define NND_CMD_WRITE1_UPPER 0x01 +#define NND_CMD_PROGRAM_END 0x10 +#define NND_CMD_READ2_SPARE 0x50 +#define NND_CMD_WRITE2_SPARE 0x50 +#define NND_CMD_ERASE 0x60 +#define NND_CMD_STATUS 0x70 +#define NND_CMD_PROGRAM 0x80 +#define NND_CMD_READ_ID 0x90 +#define NND_CMD_ERASE_END 0xD0 +#define NND_CMD_RESET 0xFF + + +#define NAND_Ecc_P1e (1 << 0) +#define NAND_Ecc_P2e (1 << 1) +#define NAND_Ecc_P4e (1 << 2) +#define NAND_Ecc_P8e (1 << 3) +#define NAND_Ecc_P16e (1 << 4) +#define NAND_Ecc_P32e (1 << 5) +#define NAND_Ecc_P64e (1 << 6) +#define NAND_Ecc_P128e (1 << 7) +#define NAND_Ecc_P256e (1 << 8) +#define NAND_Ecc_P512e (1 << 9) +#define NAND_Ecc_P1024e (1 << 10) +#define NAND_Ecc_P2048e (1 << 11) + +#define NAND_Ecc_P1o (1 << 16) +#define NAND_Ecc_P2o (1 << 17) +#define NAND_Ecc_P4o (1 << 18) +#define NAND_Ecc_P8o (1 << 19) +#define NAND_Ecc_P16o (1 << 20) +#define NAND_Ecc_P32o (1 << 21) +#define NAND_Ecc_P64o (1 << 22) +#define NAND_Ecc_P128o (1 << 23) +#define NAND_Ecc_P256o (1 << 24) +#define NAND_Ecc_P512o (1 << 25) +#define NAND_Ecc_P1024o (1 << 26) +#define NAND_Ecc_P2048o (1 << 27) + +#define TF(value) (value ? 1 : 0) + +#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0 ) +#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1 ) +#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2 ) +#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3 ) +#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4 ) +#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5 ) +#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6 ) +#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7 ) + +#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0 ) +#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1 ) +#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2 ) +#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3 ) +#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4 ) +#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5 ) +#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6 ) +#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7 ) + +#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0 ) +#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1 ) +#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2 ) +#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3 ) +#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4 ) +#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5 ) +#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6 ) +#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7 ) + +#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0 ) +#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1 ) +#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2 ) +#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3 ) +#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4 ) +#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5 ) +#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6 ) +#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7 ) + +#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0 ) +#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1 ) + +extern struct nand_oobinfo jffs2_oobinfo; + +/* + * MTD structure for OMAP board + */ +static struct mtd_info *omap_mtd; +static struct clk *omap_nand_clk; +static int omap_nand_dma_ch; +static struct completion omap_nand_dma_comp; +static unsigned long omap_nand_base = io_p2v(NAND_BASE); + +static inline u32 nand_read_reg(int idx) +{ + return __raw_readl(omap_nand_base + idx); +} + +static inline void nand_write_reg(int idx, u32 val) +{ + __raw_writel(val, omap_nand_base + idx); +} + +static inline u8 nand_read_reg8(int idx) +{ + return __raw_readb(omap_nand_base + idx); +} + +static inline void nand_write_reg8(int idx, u8 val) +{ + __raw_writeb(val, omap_nand_base + idx); +} + +static void omap_nand_select_chip(struct mtd_info *mtd, int chip) +{ + u32 l; + + switch(chip) { + case -1: + l = nand_read_reg(NND_CTRL); + l |= (1 << 8) | (1 << 10) | (1 << 12) | (1 << 14); + nand_write_reg(NND_CTRL, l); + break; + case 0: + /* Also CS1, CS2, CS4 would be available */ + l = nand_read_reg(NND_CTRL); + l &= ~(1 << 8); + nand_write_reg(NND_CTRL, l); + break; + default: + BUG(); + } +} + +static void nand_dma_cb(int lch, u16 ch_status, void *data) +{ + complete((struct completion *) data); +} + +static void omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, + unsigned int u32_count, int is_write) +{ + const int block_size = 16; + unsigned int block_count, len; + int dma_ch; + unsigned long fifo_reg, timeout, jiffies_before, jiffies_spent; + static unsigned long max_jiffies = 0; + + dma_ch = omap_nand_dma_ch; + block_count = u32_count * 4 / block_size; + nand_write_reg(NND_STATUS, 0x0f); + nand_write_reg(NND_FIFOCTRL, (block_size << 24) | block_count); + fifo_reg = NAND_BASE + NND_FIFO; + if (is_write) { + omap_set_dma_dest_params(dma_ch, OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, fifo_reg, + 0, 0); + omap_set_dma_src_params(dma_ch, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, + virt_to_phys(addr), + 0, 0); +// omap_set_dma_src_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_4); + /* Set POSTWRITE bit */ + nand_write_reg(NND_CTRL, nand_read_reg(NND_CTRL) | (1 << 16)); + } else { + omap_set_dma_src_params(dma_ch, OMAP_DMA_PORT_TIPB, + OMAP_DMA_AMODE_CONSTANT, fifo_reg, + 0, 0); + omap_set_dma_dest_params(dma_ch, OMAP_DMA_PORT_EMIFF, + OMAP_DMA_AMODE_POST_INC, + virt_to_phys(addr), + 0, 0); +// omap_set_dma_dest_burst_mode(dma_ch, OMAP_DMA_DATA_BURST_8); + /* Set PREFETCH bit */ + nand_write_reg(NND_CTRL, nand_read_reg(NND_CTRL) | (1 << 17)); + } + omap_set_dma_transfer_params(dma_ch, OMAP_DMA_DATA_TYPE_S32, block_size / 4, + block_count, OMAP_DMA_SYNC_FRAME, + 0, 0); + init_completion(&omap_nand_dma_comp); + + len = u32_count << 2; + consistent_sync(addr, len, DMA_TO_DEVICE); + omap_start_dma(dma_ch); + jiffies_before = jiffies; + timeout = wait_for_completion_timeout(&omap_nand_dma_comp, + msecs_to_jiffies(1000)); + jiffies_spent = (unsigned long)((long)jiffies - (long)jiffies_before); + if (jiffies_spent > max_jiffies) + max_jiffies = jiffies_spent; + + if (timeout == 0) { + printk(KERN_WARNING "omap-hw-nand: DMA timeout after %u ms, max. seen latency %u ms\n", + jiffies_to_msecs(jiffies_spent), + jiffies_to_msecs(max_jiffies)); + if (OMAP_DMA_CCR_REG(dma_ch) & (1 << 7)) { + /* If the DMA transfer is still running, something + * is really wrong. */ + printk(KERN_ERR "omap-hw-nand: DMA transfer still running. Not good.\n"); + printk(KERN_INFO "DMA ch %d: CCR %04x, CSR %04x, CCDEN_L %04x\n", + dma_ch, omap_readw(OMAP_DMA_CCR_REG(dma_ch)), omap_readw(OMAP_DMA_CSR_REG(dma_ch)), + omap_readw(OMAP_DMA_BASE + 0x40 * (dma_ch) + 0x34)); + } + } + if (!is_write) + consistent_sync(addr, len, DMA_FROM_DEVICE); + + nand_write_reg(NND_CTRL, nand_read_reg(NND_CTRL) & ~((1 << 16) | (1 << 17))); +} + +static void fifo_read(u32 *out, unsigned int len) +{ + const int block_size = 16; + unsigned long status_reg, fifo_reg; + int c; + + status_reg = omap_nand_base + NND_STATUS; + fifo_reg = omap_nand_base + NND_FIFO; + len = len * 4 / block_size; + nand_write_reg(NND_FIFOCTRL, (block_size << 24) | len); + nand_write_reg(NND_STATUS, 0x0f); + nand_write_reg(NND_CTRL, nand_read_reg(NND_CTRL) | (1 << 17)); + c = block_size / 4; + while (len--) { + int i; + + while ((__raw_readl(status_reg) & (1 << 2)) == 0); + __raw_writel(0x0f, status_reg); + for (i = 0; i < c; i++) { + u32 l = __raw_readl(fifo_reg); + *out++ = l; + } + } + nand_write_reg(NND_CTRL, nand_read_reg(NND_CTRL) & ~(1 << 17)); + nand_write_reg(NND_STATUS, 0x0f); +} + +static void omap_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + unsigned long access_reg; + + if (likely(((unsigned long) buf & 3) == 0 && (len & 3) == 0)) { + int u32_count = len >> 2; + u32 *dest = (u32 *) buf; + /* If the transfer is big enough and the length divisible by + * 16, we try to use DMA transfer, or FIFO copy in case of + * DMA failure (e.g. all channels busy) */ + if (u32_count > 64 && (u32_count & 3) == 0) { + if (omap_nand_dma_ch >= 0) { + omap_nand_dma_transfer(mtd, buf, u32_count, 0); + return; + } + /* In case of an error, fallback to FIFO copy */ + fifo_read((u32 *) buf, u32_count); + return; + } + access_reg = omap_nand_base + NND_ACCESS; + /* Small buffers we just read directly */ + while (u32_count--) + *dest++ = __raw_readl(access_reg); + } else { + /* If we're not word-aligned, we use byte copy */ + access_reg = omap_nand_base + NND_ACCESS; + while (len--) + *buf++ = __raw_readb(access_reg); + } +} + +static void omap_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + if (likely(((unsigned long) buf & 3) == 0 && (len & 3) == 0)) { + const u32 *src = (const u32 *) buf; + + len >>= 2; +#if 0 + /* If the transfer is big enough and length divisible by 16, + * we try to use DMA transfer. */ + if (len > 256 / 4 && (len & 3) == 0) { + if (omap_nand_dma_transfer(mtd, (void *) buf, len, 1) == 0) + return; + /* In case of an error, fallback to CPU copy */ + } +#endif + while (len--) + nand_write_reg(NND_ACCESS, *src++); + } else { + while (len--) + nand_write_reg8(NND_ACCESS, *buf++); + } +} + +static int omap_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) +{ + if (likely(((unsigned long) buf & 3) == 0 && (len & 3) == 0)) { + const u32 *dest = (const u32 *) buf; + len >>= 2; + while (len--) + if (*dest++ != nand_read_reg(NND_ACCESS)) + return -EFAULT; + } else { + while (len--) + if (*buf++ != nand_read_reg8(NND_ACCESS)) + return -EFAULT; + } + return 0; +} + +static u_char omap_nand_read_byte(struct mtd_info *mtd) +{ + return nand_read_reg8(NND_ACCESS); +} + +static void omap_nand_write_byte(struct mtd_info *mtd, u_char byte) +{ + nand_write_reg8(NND_ACCESS, byte); +} + +static int omap_nand_dev_ready(struct mtd_info *mtd) +{ + u32 l; + + l = nand_read_reg(NND_READY); + return l & 0x01; +} + +static int nand_write_command(u8 cmd, u32 addr, int addr_valid) +{ + if (addr_valid) { + nand_write_reg(NND_ADDR_SRC, addr); + nand_write_reg8(NND_COMMAND, cmd); + } else { + nand_write_reg(NND_ADDR_SRC, 0); + nand_write_reg8(NND_COMMAND_SEC, cmd); + } + while (!omap_nand_dev_ready(NULL)); + return 0; +} + +/* + * Send command to NAND device + */ +static void omap_nand_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + struct nand_chip *this = mtd->priv; + + /* + * Write out the command to the device. + */ + if (command == NAND_CMD_SEQIN) { + int readcmd; + + if (column >= mtd->oobblock) { + /* OOB area */ + column -= mtd->oobblock; + readcmd = NAND_CMD_READOOB; + } else if (column < 256) { + /* First 256 bytes --> READ0 */ + readcmd = NAND_CMD_READ0; + } else { + column -= 256; + readcmd = NAND_CMD_READ1; + } + nand_write_command(readcmd, 0, 0); + } + switch (command) { + case NAND_CMD_RESET: + case NAND_CMD_PAGEPROG: + case NAND_CMD_STATUS: + case NAND_CMD_ERASE2: + nand_write_command(command, 0, 0); + break; + case NAND_CMD_ERASE1: + nand_write_command(command, ((page_addr & 0xFFFFFF00) << 1) | (page_addr & 0XFF), 1); + break; + default: + nand_write_command(command, (page_addr << this->page_shift) | column, 1); + } +} + +static void omap_nand_command_lp(struct mtd_info *mtd, unsigned command, int column, int page_addr) +{ + struct nand_chip *this = mtd->priv; + + if (command == NAND_CMD_READOOB) { + column += mtd->oobblock; + command = NAND_CMD_READ0; + } + switch (command) { + case NAND_CMD_RESET: + case NAND_CMD_PAGEPROG: + case NAND_CMD_STATUS: + case NAND_CMD_ERASE2: + nand_write_command(command, 0, 0); + break; + case NAND_CMD_ERASE1: + nand_write_command(command, page_addr << this->page_shift >> 11, 1); + break; + default: + nand_write_command(command, (page_addr << 16) | column, 1); + } + if (command == NAND_CMD_READ0) + nand_write_command(NAND_CMD_READSTART, 0, 0); +} + +/* + * Generate non-inverted ECC bytes. + * + * Using noninverted ECC can be considered ugly since writing a blank + * page ie. padding will clear the ECC bytes. This is no problem as long + * nobody is trying to write data on the seemingly unused page. + * + * Reading an erased page will produce an ECC mismatch between + * generated and read ECC bytes that has to be dealt with separately. + */ +static int omap_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ + u32 l; + int reg; + int n; + struct nand_chip *this = mtd->priv; + + if (this->eccmode == NAND_ECC_HW12_2048) + n = 4; + else + n = 1; + reg = NND_ECC_START; + while (n--) { + l = nand_read_reg(reg); + *ecc_code++ = l; // P128e, ..., P1e + *ecc_code++ = l >> 16; // P128o, ..., P1o + // P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e + *ecc_code++ = ((l >> 8) & 0x0f) | ((l >> 20) & 0xf0); + reg += 4; + } + return 0; +} + +/* + * This function will generate true ECC value, which can be used + * when correcting data read from NAND flash memory core + */ +static void gen_true_ecc(u8 *ecc_buf) +{ + u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) | ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8); + + ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) | P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp) ); + ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) | P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp)); + ecc_buf[2] = ~( P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) | P1e(tmp) | P2048o(tmp) | P2048e(tmp)); +} + +/* + * This function compares two ECC's and indicates if there is an error. + * If the error can be corrected it will be corrected to the buffer + */ +static int omap_nand_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ + u8 *ecc_data2, /* read from register */ + u8 *page_data) +{ + uint i; + u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8]; + u8 comp0_bit[8], comp1_bit[8], comp2_bit[8]; + u8 ecc_bit[24]; + u8 ecc_sum = 0; + u8 find_bit = 0; + uint find_byte = 0; + int isEccFF; + + isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF); + + gen_true_ecc(ecc_data1); + gen_true_ecc(ecc_data2); + + for (i = 0; i <= 2; i++) { + *(ecc_data1 + i) = ~(*(ecc_data1 + i)); + *(ecc_data2 + i) = ~(*(ecc_data2 + i)); + } + + for (i = 0; i < 8; i++) { + tmp0_bit[i] = *ecc_data1 % 2; + *ecc_data1 = *ecc_data1 / 2; + } + + for (i = 0; i < 8; i++) { + tmp1_bit[i] = *(ecc_data1 + 1) % 2; + *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2; + } + + for (i = 0; i < 8; i++) { + tmp2_bit[i] = *(ecc_data1 + 2) % 2; + *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2; + } + + for (i = 0; i < 8; i++) { + comp0_bit[i] = *ecc_data2 % 2; + *ecc_data2 = *ecc_data2 / 2; + } + + for (i = 0; i < 8; i++) { + comp1_bit[i] = *(ecc_data2 + 1) % 2; + *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2; + } + + for (i = 0; i < 8; i++) { + comp2_bit[i] = *(ecc_data2 + 2) % 2; + *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2; + } + + for (i = 0; i< 6; i++ ) + ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2]; + + for (i = 0; i < 8; i++) + ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i]; + + for (i = 0; i < 8; i++) + ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i]; + + ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0]; + ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1]; + + for (i = 0; i < 24; i++) + ecc_sum += ecc_bit[i]; + + switch (ecc_sum) { + case 0: + /* Not reached because this function is not called if + ECC values are equal */ + return 0; + + case 1: + /* Uncorrectable error */ + DEBUG (MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n"); + return -1; + + case 12: + /* Correctable error */ + find_byte = (ecc_bit[23] << 8) + + (ecc_bit[21] << 7) + + (ecc_bit[19] << 6) + + (ecc_bit[17] << 5) + + (ecc_bit[15] << 4) + + (ecc_bit[13] << 3) + + (ecc_bit[11] << 2) + + (ecc_bit[9] << 1) + + ecc_bit[7]; + + find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1]; + + DEBUG (MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at offset: %d, bit: %d\n", find_byte, find_bit); + + page_data[find_byte] ^= (1 << find_bit); + + return 0; + default: + if (isEccFF) { + if (ecc_data2[0] == 0 && ecc_data2[1] == 0 && ecc_data2[2] == 0) + return 0; + } + DEBUG (MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n"); + return -1; + } +} + +static int omap_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) +{ + struct nand_chip *this; + int block_count = 0, i, r; + + this = mtd->priv; + if (this->eccmode == NAND_ECC_HW12_2048) + block_count = 4; + else + block_count = 1; + for (i = 0; i < block_count; i++) { + if (memcmp(read_ecc, calc_ecc, 3) != 0) { + r = omap_nand_compare_ecc(read_ecc, calc_ecc, dat); + if (r < 0) + return r; + } + read_ecc += 3; + calc_ecc += 3; + dat += 512; + } + return 0; +} + +static void omap_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + nand_write_reg(NND_RESET, 0x01); +} + +#ifdef CONFIG_MTD_CMDLINE_PARTS + +extern int mtdpart_setup(char *); + +static int __init add_dynamic_parts(struct mtd_info *mtd) +{ + static const char *part_parsers[] = { "cmdlinepart", NULL }; + struct mtd_partition *parts; + const struct omap_flash_part_config *cfg; + char *part_str = NULL; + size_t part_str_len; + int c; + + cfg = omap_get_var_config(OMAP_TAG_FLASH_PART, &part_str_len); + if (cfg != NULL) { + part_str = kmalloc(part_str_len + 1, GFP_KERNEL); + if (part_str == NULL) + return -ENOMEM; + memcpy(part_str, cfg->part_table, part_str_len); + part_str[part_str_len] = '\0'; + mtdpart_setup(part_str); + } + c = parse_mtd_partitions(omap_mtd, part_parsers, &parts, 0); + if (part_str != NULL) { + mtdpart_setup(NULL); + kfree(part_str); + } + if (c <= 0) + return -1; + + add_mtd_partitions(mtd, parts, c); + + return 0; +} + +#else + +static inline int add_dynamic_parts(struct mtd_info *mtd) +{ + return -1; +} + +#endif + +static inline int calc_psc(int ns, int cycle_ps) +{ + return (ns * 1000 + (cycle_ps - 1)) / cycle_ps; +} + +static void set_psc_regs(int psc_ns, int psc1_ns, int psc2_ns) +{ + int psc[3], i; + unsigned long rate, ps; + + rate = clk_get_rate(omap_nand_clk); + ps = 1000000000 / (rate / 1000); + psc[0] = calc_psc(psc_ns, ps); + psc[1] = calc_psc(psc1_ns, ps); + psc[2] = calc_psc(psc2_ns, ps); + for (i = 0; i < 3; i++) { + if (psc[i] < 2) + psc[i] = 2; + else if (psc[i] > 256) + psc[i] = 256; + } + nand_write_reg(NND_PSC_CLK, psc[0] - 1); + nand_write_reg(NND_PSC1_CLK, psc[1] - 1); + nand_write_reg(NND_PSC2_CLK, psc[2] - 1); + printk(KERN_INFO "omap-hw-nand: using PSC values %d, %d, %d\n", psc[0], psc[1], psc[2]); +} + +/* + * Main initialization routine + */ +static int __init omap_nand_init(void) +{ + struct nand_chip *this; + int err = 0; + u32 l; + + omap_nand_clk = clk_get(NULL, "armper_ck"); + BUG_ON(omap_nand_clk == NULL); + clk_enable(omap_nand_clk); + + l = nand_read_reg(NND_REVISION); + printk(KERN_INFO "omap-hw-nand: OMAP NAND Controller rev. %d.%d\n", l>>4, l & 0xf); + + /* Reset the NAND Controller */ + nand_write_reg(NND_SYSCFG, 0x02); + while ((nand_read_reg(NND_SYSSTATUS) & 0x01) == 0); + + /* No Prefetch, no postwrite, write prot & enable pairs disabled, + addres counter set to send 4 byte addresses to flash, + A8 is set not to be sent to flash (erase addre needs formatting), + choose little endian, enable 512 byte ECC logic, + */ + nand_write_reg(NND_CTRL, 0xFF01); + + /* Allocate memory for MTD device structure and private data */ + omap_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); + if (!omap_mtd) { + printk(KERN_WARNING "omap-hw-nand: Unable to allocate OMAP NAND MTD device structure.\n"); + err = -ENOMEM; + goto free_clock; + } +#if 1 + err = omap_request_dma(OMAP_DMA_NAND, "NAND", nand_dma_cb, + &omap_nand_dma_comp, &omap_nand_dma_ch); + if (err < 0) { + printk(KERN_WARNING "omap-hw-nand: Unable to reserve DMA channel\n"); + omap_nand_dma_ch = -1; + } +#else + omap_nand_dma_ch = -1; +#endif + /* Get pointer to private data */ + this = (struct nand_chip *) (&omap_mtd[1]); + + /* Initialize structures */ + memset((char *) omap_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + omap_mtd->priv = this; + omap_mtd->name = "omap-nand"; + + this->options = NAND_SKIP_BBTSCAN; + + /* Used from chip select and nand_command() */ + this->read_byte = omap_nand_read_byte; + this->write_byte = omap_nand_write_byte; + + this->select_chip = omap_nand_select_chip; + this->dev_ready = omap_nand_dev_ready; + this->chip_delay = 0; + this->eccmode = NAND_ECC_HW3_512; + this->cmdfunc = omap_nand_command; + this->write_buf = omap_nand_write_buf; + this->read_buf = omap_nand_read_buf; + this->verify_buf = omap_nand_verify_buf; + this->calculate_ecc = omap_nand_calculate_ecc; + this->correct_data = omap_nand_correct_data; + this->enable_hwecc = omap_nand_enable_hwecc; + + nand_write_reg(NND_SYSCFG, 0x1); /* Enable auto idle */ + nand_write_reg(NND_PSC_CLK, 10); + /* Scan to find existance of the device */ + if (nand_scan(omap_mtd, 1)) { + err = -ENXIO; + goto out_mtd; + } + + set_psc_regs(25, 15, 35); + if (this->page_shift == 11) { + this->cmdfunc = omap_nand_command_lp; + l = nand_read_reg(NND_CTRL); + l |= 1 << 4; /* Set the A8 bit in CTRL reg */ + nand_write_reg(NND_CTRL, l); + this->eccmode = NAND_ECC_HW12_2048; + this->eccsteps = 1; + this->eccsize = 2048; + this->eccbytes = 12; + omap_mtd->eccsize = 2048; + nand_write_reg(NND_ECC_SELECT, 6); + } + + /* We have to do bbt scanning ourselves */ + if (this->scan_bbt (omap_mtd)) { + err = -ENXIO; + goto out_mtd; + } + + err = add_dynamic_parts(omap_mtd); + if (err < 0) { + printk(KERN_ERR "omap-hw-nand: no partitions defined\n"); + err = -ENODEV; + nand_release(omap_mtd); + goto out_mtd; + } + /* init completed */ + return 0; +out_mtd: + if (omap_nand_dma_ch >= 0) + omap_free_dma(omap_nand_dma_ch); + kfree(omap_mtd); +free_clock: + clk_put(omap_nand_clk); + return err; +} + +module_init(omap_nand_init); + +/* + * Clean up routine + */ +static void __exit omap_nand_cleanup (void) +{ + clk_disable(omap_nand_clk); + clk_put(omap_nand_clk); + nand_release(omap_mtd); + kfree(omap_mtd); +} + +module_exit(omap_nand_cleanup); + diff --git a/drivers/mtd/nand/omap-nand-flash.c b/drivers/mtd/nand/omap-nand-flash.c new file mode 100644 index 00000000000..35a34088f99 --- /dev/null +++ b/drivers/mtd/nand/omap-nand-flash.c @@ -0,0 +1,182 @@ +/* + * drivers/mtd/nand/omap-nand-flash.c + * + * Copyright (c) 2004 Texas Instruments, Jian Zhang + * Copyright (c) 2004 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#define DRIVER_NAME "omapnand" + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + +struct omap_nand_info { + struct nand_platform_data *pdata; + struct mtd_partition *parts; + struct mtd_info mtd; + struct nand_chip nand; +}; + +/* + * hardware specific access to control-lines + * NOTE: boards may use different bits for these!! + */ +#define MASK_CLE 0x02 +#define MASK_ALE 0x04 +static void omap_nand_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nand_chip *this = mtd->priv; + unsigned long IO_ADDR_W = (unsigned long) this->IO_ADDR_W; + + switch (cmd) { + case NAND_CTL_SETCLE: IO_ADDR_W |= MASK_CLE; break; + case NAND_CTL_CLRCLE: IO_ADDR_W &= ~MASK_CLE; break; + case NAND_CTL_SETALE: IO_ADDR_W |= MASK_ALE; break; + case NAND_CTL_CLRALE: IO_ADDR_W &= ~MASK_ALE; break; + } + this->IO_ADDR_W = (void __iomem *) IO_ADDR_W; +} + +static int omap_nand_dev_ready(struct mtd_info *mtd) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, mtd); + + return info->pdata->dev_ready(info->pdata); +} + +static int __devinit omap_nand_probe(struct platform_device *pdev) +{ + struct omap_nand_info *info; + struct nand_platform_data *pdata = pdev->dev.platform_data; + struct resource *res = pdev->resource; + unsigned long size = res->end - res->start + 1; + int err; + + info = kmalloc(sizeof(struct omap_nand_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + memset(info, 0, sizeof(struct omap_nand_info)); + + if (!request_mem_region(res->start, size, pdev->dev.driver->name)) { + err = -EBUSY; + goto out_free_info; + } + + info->nand.IO_ADDR_R = ioremap(res->start, size); + if (!info->nand.IO_ADDR_R) { + err = -ENOMEM; + goto out_release_mem_region; + } + info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; + info->nand.hwcontrol = omap_nand_hwcontrol; + info->nand.eccmode = NAND_ECC_SOFT; + info->nand.options = pdata->options; + if (pdata->dev_ready) + info->nand.dev_ready = omap_nand_dev_ready; + else + info->nand.chip_delay = 20; + + info->mtd.name = pdev->dev.bus_id; + info->mtd.priv = &info->nand; + + info->pdata = pdata; + + /* DIP switches on H2 and some other boards change between 8 and 16 bit + * bus widths for flash. Try the other width if the first try fails. + */ + if (nand_scan(&info->mtd, 1)) { + info->nand.options ^= NAND_BUSWIDTH_16; + if (nand_scan(&info->mtd, 1)) { + err = -ENXIO; + goto out_iounmap; + } + } + info->mtd.owner = THIS_MODULE; + +#ifdef CONFIG_MTD_PARTITIONS + err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); + if (err > 0) + add_mtd_partitions(&info->mtd, info->parts, err); + else if (err < 0 && pdata->parts) + add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); + else +#endif + add_mtd_device(&info->mtd); + + platform_set_drvdata(pdev, info); + + return 0; + +out_iounmap: + iounmap(info->nand.IO_ADDR_R); +out_release_mem_region: + release_mem_region(res->start, size); +out_free_info: + kfree(info); + + return err; +} + +static int omap_nand_remove(struct platform_device *pdev) +{ + struct omap_nand_info *info = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + /* Release NAND device, its internal structures and partitions */ + nand_release(&info->mtd); + iounmap(info->nand.IO_ADDR_R); + kfree(info); + return 0; +} + +static struct platform_driver omap_nand_driver = { + .probe = omap_nand_probe, + .remove = omap_nand_remove, + .driver = { + .name = DRIVER_NAME, + }, +}; +MODULE_ALIAS(DRIVER_NAME); + +static int __init omap_nand_init(void) +{ + return platform_driver_register(&omap_nand_driver); +} + +static void __exit omap_nand_exit(void) +{ + platform_driver_unregister(&omap_nand_driver); +} + +module_init(omap_nand_init); +module_exit(omap_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jian Zhang (and others)"); +MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards"); + diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig index 7a081346f07..19a64449587 100644 --- a/drivers/net/irda/Kconfig +++ b/drivers/net/irda/Kconfig @@ -340,6 +340,13 @@ config TOSHIBA_FIR To compile it as a module, choose M here: the module will be called donauboe. +config OMAP_IR + tristate "OMAP IrDA(SIR/MIR/FIR)" + depends on IRDA && (ARCH_OMAP1 || ARCH_OMAP2) + select GPIOEXPANDER_OMAP if (MACH_OMAP_H3 || MACH_OMAP_H4) + help + Say Y here if you want to build support for the OMAP IR. + config AU1000_FIR tristate "Alchemy Au1000 SIR/FIR" depends on MIPS_AU1000 && IRDA diff --git a/drivers/net/irda/Makefile b/drivers/net/irda/Makefile index 72cbfdc9cfc..df619ebdc49 100644 --- a/drivers/net/irda/Makefile +++ b/drivers/net/irda/Makefile @@ -43,6 +43,8 @@ obj-$(CONFIG_OLD_BELKIN_DONGLE) += old_belkin-sir.o obj-$(CONFIG_MCP2120_DONGLE) += mcp2120-sir.o obj-$(CONFIG_ACT200L_DONGLE) += act200l-sir.o obj-$(CONFIG_MA600_DONGLE) += ma600-sir.o +obj-$(CONFIG_OMAP_IR) += omap-ir.o + # The SIR helper module sir-dev-objs := sir_dev.o sir_dongle.o sir_kthread.o diff --git a/drivers/net/irda/omap-ir.c b/drivers/net/irda/omap-ir.c new file mode 100644 index 00000000000..c2b87811b5b --- /dev/null +++ b/drivers/net/irda/omap-ir.c @@ -0,0 +1,903 @@ +/* + * BRIEF MODULE DESCRIPTION + * + * Infra-red driver for the OMAP1610-H2 and OMAP1710-H3 Platforms + * (SIR/MIR/FIR modes) + * (based on omap-sir.c) + * + * Copyright 2003 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * Copyright 2004 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; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + Modifications: + Feb 2004, Texas Instruments + - Ported to 2.6 kernel (Feb 2004). + * + Apr 2004, Texas Instruments + - Added support for H3 (Apr 2004). + Nov 2004, Texas Instruments + - Added support for Power Management. + + */ + +#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 + +#define UART3_EFR_EN (1 << 4) +#define UART3_MCR_EN_TCR_TLR (1 << 6) + +#define UART3_LCR_WL_8 (3 << 0) +#define UART3_LCR_SP2 (1 << 2) +#define UART3_LCR_DIVEN (1 << 7) + +#define UART3_FCR_FIFO_EN (1 << 0) +#define UART3_FCR_FIFO_RX (1 << 1) +#define UART3_FCR_FIFO_TX (1 << 2) +#define UART3_FCR_FIFO_DMA1 (1 << 3) +#define UART3_FCR_FIFO_TX_TRIG16 (1 << 4) +#define UART3_FCR_FIFO_RX_TRIG16 (1 << 6) +#define UART3_FCR_CONFIG UART3_FCR_FIFO_EN | UART3_FCR_FIFO_RX | \ + UART3_FCR_FIFO_TX | UART3_FCR_FIFO_DMA1 | \ + UART3_FCR_FIFO_TX_TRIG16 | \ + UART3_FCR_FIFO_RX_TRIG16 + +#define UART3_SCR_TX_TRIG1 (1 << 6) +#define UART3_SCR_RX_TRIG1 (1 << 7) + +#define UART3_MDR1_RESET (0x07) +#define UART3_MDR1_SIR (1 << 0) +#define UART3_MDR1_MIR (4 << 0) +#define UART3_MDR1_FIR (5 << 0) +#define UART3_MDR1_SIP_AUTO (1 << 6) + +#define UART3_MDR2_TRIG1 (0 << 1) +#define UART3_MDR2_IRTX_UNDERRUN (1 << 0) + +#define UART3_ACERG_TX_UNDERRUN_DIS (1 << 4) +#define UART3_ACERG_SD_MODE_LOW (1 << 6) +#define UART3_ACERG_DIS_IR_RX (1 << 5) + +#define UART3_IER_EOF (1 << 5) +#define UART3_IER_CTS (1 << 7) + +#define UART3_IIR_TX_STATUS (1 << 5) +#define UART3_IIR_EOF (0x80) + +#define IS_FIR(si) ((si)->speed >= 4000000) +#define IRDA_FRAME_SIZE_LIMIT 4096 + +static int rx_state = 0; /* RX state for IOCTL */ + +struct omap_irda { + unsigned char open; + int speed; /* Current IrDA speed */ + int newspeed; + + struct net_device_stats stats; + struct irlap_cb *irlap; + struct qos_info qos; + + int rx_dma_channel; + int tx_dma_channel; + + dma_addr_t rx_buf_dma_phys; /* Physical address of RX DMA buffer */ + dma_addr_t tx_buf_dma_phys; /* Physical address of TX DMA buffer */ + + void *rx_buf_dma_virt; /* Virtual address of RX DMA buffer */ + void *tx_buf_dma_virt; /* Virtual address of TX DMA buffer */ + + struct device *dev; + struct omap_irda_config *pdata; +}; + +static void inline uart_reg_out(int idx, u8 val) +{ + omap_writeb(val, idx); +} + +static u8 inline uart_reg_in(int idx) +{ + u8 b = omap_readb(idx); + return b; +} + +/* forward declarations */ +extern void irda_device_setup(struct net_device *dev); +extern void omap_stop_dma(int lch); +static int omap_irda_set_speed(struct net_device *dev, int speed); + +static void omap_irda_start_rx_dma(struct omap_irda *si) +{ + /* Configure DMA */ + omap_set_dma_src_params(si->rx_dma_channel, 0x3, 0x0, + si->pdata->src_start, + 0, 0); + + omap_enable_dma_irq(si->rx_dma_channel, 0x01); + + omap_set_dma_dest_params(si->rx_dma_channel, 0x0, 0x1, + si->rx_buf_dma_phys, + 0, 0); + + omap_set_dma_transfer_params(si->rx_dma_channel, 0x0, + IRDA_FRAME_SIZE_LIMIT, 0x1, + 0x0, si->pdata->rx_trigger, 0); + + omap_start_dma(si->rx_dma_channel); +} + +static void omap_start_tx_dma(struct omap_irda *si, int size) +{ + /* Configure DMA */ + omap_set_dma_dest_params(si->tx_dma_channel, 0x03, 0x0, + si->pdata->dest_start, 0, 0); + + omap_enable_dma_irq(si->tx_dma_channel, 0x01); + + omap_set_dma_src_params(si->tx_dma_channel, 0x0, 0x1, + si->tx_buf_dma_phys, + 0, 0); + + omap_set_dma_transfer_params(si->tx_dma_channel, 0x0, size, 0x1, + 0x0, si->pdata->tx_trigger, 0); + + /* Start DMA */ + omap_start_dma(si->tx_dma_channel); +} + +/* DMA RX callback - normally, we should not go here, + * it calls only if something is going wrong + */ +static void omap_irda_rx_dma_callback(int lch, u16 ch_status, void *data) +{ + struct net_device *dev = data; + struct omap_irda *si = dev->priv; + + printk(KERN_ERR "RX Transfer error or very big frame\n"); + + /* Clear interrupts */ + uart_reg_in(UART3_IIR); + + si->stats.rx_frame_errors++; + + uart_reg_in(UART3_RESUME); + + /* Re-init RX DMA */ + omap_irda_start_rx_dma(si); +} + +/* DMA TX callback - calling when frame transfer has been finished */ +static void omap_irda_tx_dma_callback(int lch, u16 ch_status, void *data) +{ + struct net_device *dev = data; + struct omap_irda *si = dev->priv; + + /*Stop DMA controller */ + omap_stop_dma(si->tx_dma_channel); +} + +/* + * Set the IrDA communications speed. + * Interrupt have to be disabled here. + */ +static int omap_irda_startup(struct net_device *dev) +{ + struct omap_irda *si = dev->priv; + + + /* FIXME: use clk_* apis for UART3 clock*/ + /* Enable UART3 clock and set UART3 to IrDA mode */ + if (machine_is_omap_h2() || machine_is_omap_h3()) + omap_writel(omap_readl(MOD_CONF_CTRL_0) | (1 << 31) | (1 << 15), + MOD_CONF_CTRL_0); + + /* Only for H2? + */ + if (si->pdata->transceiver_mode && machine_is_omap_h2()) { + /* Is it select_irda on H2 ? */ + omap_writel(omap_readl(FUNC_MUX_CTRL_A) | 7, + FUNC_MUX_CTRL_A); + si->pdata->transceiver_mode(si->dev, IR_SIRMODE); + } + + uart_reg_out(UART3_MDR1, UART3_MDR1_RESET); /* Reset mode */ + + /* Clear DLH and DLL */ + uart_reg_out(UART3_LCR, UART3_LCR_DIVEN); + + uart_reg_out(UART3_DLL, 0); + uart_reg_out(UART3_DLH, 0); + uart_reg_out(UART3_LCR, 0xbf); /* FIXME: Add #define */ + + uart_reg_out(UART3_EFR, UART3_EFR_EN); + uart_reg_out(UART3_LCR, UART3_LCR_DIVEN); + + /* Enable access to UART3_TLR and UART3_TCR registers */ + uart_reg_out(UART3_MCR, UART3_MCR_EN_TCR_TLR); + + uart_reg_out(UART3_SCR, 0); + /* Set Rx trigger to 1 and Tx trigger to 1 */ + uart_reg_out(UART3_TLR, 0); + + /* Set LCR to 8 bits and 1 stop bit */ + uart_reg_out(UART3_LCR, 0x03); + + /* Clear RX and TX FIFO and enable FIFO */ + /* Use DMA Req for transfers */ + uart_reg_out(UART3_FCR, UART3_FCR_CONFIG); + + uart_reg_out(UART3_MCR, 0); + + uart_reg_out(UART3_SCR, UART3_SCR_TX_TRIG1 | + UART3_SCR_RX_TRIG1); + + /* Enable UART3 SIR Mode,(Frame-length method to end frames) */ + uart_reg_out(UART3_MDR1, UART3_MDR1_SIR); + + /* Set Status FIFO trig to 1 */ + uart_reg_out(UART3_MDR2, 0); + + /* Enables RXIR input */ + /* and disable TX underrun */ + /* SEND_SIP pulse */ + uart_reg_out(UART3_ACREG, UART3_ACERG_SD_MODE_LOW | + UART3_ACERG_TX_UNDERRUN_DIS); + + /* Enable EOF Interrupt only */ + uart_reg_out(UART3_IER, UART3_IER_CTS | UART3_IER_EOF); + + /* Set Maximum Received Frame size to 2048 bytes */ + uart_reg_out(UART3_RXFLL, 0x00); + uart_reg_out(UART3_RXFLH, 0x08); + + uart_reg_in(UART3_RESUME); + + return 0; +} + +static int omap_irda_shutdown(struct omap_irda *si) +{ + unsigned long flags; + + local_irq_save(flags); + + /* Disable all UART3 Interrupts */ + uart_reg_out(UART3_IER, 0); + + /* Disable UART3 and disable baud rate generator */ + uart_reg_out(UART3_MDR1, UART3_MDR1_RESET); + + /* set SD_MODE pin to high and Disable RX IR */ + uart_reg_out(UART3_ACREG, (UART3_ACERG_DIS_IR_RX | + ~(UART3_ACERG_SD_MODE_LOW))); + + /* Clear DLH and DLL */ + uart_reg_out(UART3_LCR, UART3_LCR_DIVEN); + uart_reg_out(UART3_DLL, 0); + uart_reg_out(UART3_DLH, 0); + + local_irq_restore(flags); + + return 0; +} + +static irqreturn_t +omap_irda_irq(int irq, void *dev_id, struct pt_regs *hw_regs) +{ + struct net_device *dev = dev_id; + struct omap_irda *si = dev->priv; + struct sk_buff *skb; + + u8 status; + int w = 0; + + /* Clear EOF interrupt */ + status = uart_reg_in(UART3_IIR); + + if (status & UART3_IIR_TX_STATUS) { + u8 mdr2 = uart_reg_in(UART3_MDR2); + if (mdr2 & UART3_MDR2_IRTX_UNDERRUN) + printk(KERN_ERR "IrDA Buffer underrun error\n"); + + si->stats.tx_packets++; + + if (si->newspeed) { + omap_irda_set_speed(dev, si->newspeed); + si->newspeed = 0; + } + + netif_wake_queue(dev); + if (!(status & UART3_IIR_EOF)) + return IRQ_HANDLED; + } + + /* Stop DMA and if there are no errors, send frame to upper layer */ + omap_stop_dma(si->rx_dma_channel); + + status = uart_reg_in(UART3_SFLSR); /* Take a frame status */ + + if (status != 0) { /* Bad frame? */ + si->stats.rx_frame_errors++; + uart_reg_in(UART3_RESUME); + } else { + /* We got a frame! */ + skb = alloc_skb(IRDA_FRAME_SIZE_LIMIT, GFP_ATOMIC); + + if (!skb) { + printk(KERN_ERR "omap_sir: out of memory for RX SKB\n"); + return IRQ_HANDLED; + } + /* + * Align any IP headers that may be contained + * within the frame. + */ + + skb_reserve(skb, 1); + + w = OMAP_DMA_CDAC_REG(si->rx_dma_channel); + + if (cpu_is_omap16xx()) + w -= OMAP1_DMA_CDSA_L_REG(si->rx_dma_channel); + if (cpu_is_omap24xx()) + w -= OMAP2_DMA_CDSA_REG(si->rx_dma_channel); + + if (!IS_FIR(si)) { + /* Copy DMA buffer to skb */ + memcpy(skb_put(skb, w - 2), si->rx_buf_dma_virt, w - 2); + } else { + /* Copy DMA buffer to skb */ + memcpy(skb_put(skb, w - 4), si->rx_buf_dma_virt, w - 4); + } + + skb->dev = dev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + si->stats.rx_packets++; + si->stats.rx_bytes += skb->len; + netif_receive_skb(skb); /* Send data to upper level */ + } + + /* Re-init RX DMA */ + omap_irda_start_rx_dma(si); + + dev->last_rx = jiffies; + + return IRQ_HANDLED; +} + +static int omap_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct omap_irda *si = dev->priv; + int speed = irda_get_next_speed(skb); + int mtt = irda_get_mtt(skb); + int xbofs = irda_get_next_xbofs(skb); + + + /* + * Does this packet contain a request to change the interface + * speed? If so, remember it until we complete the transmission + * of this frame. + */ + if (speed != si->speed && speed != -1) + si->newspeed = speed; + + if (xbofs) /* Set number of addtional BOFS */ + uart_reg_out(UART3_EBLR, xbofs + 1); + + /* + * If this is an empty frame, we can bypass a lot. + */ + if (skb->len == 0) { + if (si->newspeed) { + si->newspeed = 0; + omap_irda_set_speed(dev, speed); + } + dev_kfree_skb(skb); + return 0; + } + + netif_stop_queue(dev); + + /* Copy skb data to DMA buffer */ + memcpy(si->tx_buf_dma_virt, skb->data, skb->len); + + /* Copy skb data to DMA buffer */ + si->stats.tx_bytes += skb->len; + + /* Set frame length */ + uart_reg_out(UART3_TXFLL, (skb->len & 0xff)); + uart_reg_out(UART3_TXFLH, (skb->len >> 8)); + + if (mtt > 1000) + mdelay(mtt / 1000); + else + udelay(mtt); + + /* Start TX DMA transfer */ + omap_start_tx_dma(si, skb->len); + + /* We can free skb now because it's already in DMA buffer */ + dev_kfree_skb(skb); + + dev->trans_start = jiffies; + + return 0; +} + +static int +omap_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) +{ + struct if_irda_req *rq = (struct if_irda_req *)ifreq; + struct omap_irda *si = dev->priv; + int ret = -EOPNOTSUPP; + + + switch (cmd) { + case SIOCSBANDWIDTH: + if (capable(CAP_NET_ADMIN)) { + /* + * We are unable to set the speed if the + * device is not running. + */ + if (si->open) + ret = omap_irda_set_speed(dev, + rq->ifr_baudrate); + else { + printk(KERN_ERR "omap_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n"); + ret = 0; + } + } + break; + + case SIOCSMEDIABUSY: + ret = -EPERM; + if (capable(CAP_NET_ADMIN)) { + irda_device_set_media_busy(dev, TRUE); + ret = 0; + } + break; + + case SIOCGRECEIVING: + rq->ifr_receiving = rx_state; + break; + + default: + break; + } + + return ret; +} + +static struct net_device_stats *omap_irda_stats(struct net_device *dev) +{ + struct omap_irda *si = dev->priv; + return &si->stats; +} + +static int omap_irda_start(struct net_device *dev) +{ + struct omap_irda *si = dev->priv; + int err; + + si->speed = 9600; + + err = request_irq(dev->irq, omap_irda_irq, 0, dev->name, dev); + if (err) + goto err_irq; + + /* + * The interrupt must remain disabled for now. + */ + disable_irq(dev->irq); + + /* Request DMA channels for IrDA hardware */ + if (omap_request_dma(si->pdata->rx_channel, "IrDA Rx DMA", + (void *)omap_irda_rx_dma_callback, + dev, &(si->rx_dma_channel))) { + printk(KERN_ERR "Failed to request IrDA Rx DMA\n"); + goto err_irq; + } + + if (omap_request_dma(si->pdata->tx_channel, "IrDA Tx DMA", + (void *)omap_irda_tx_dma_callback, + dev, &(si->tx_dma_channel))) { + printk(KERN_ERR "Failed to request IrDA Tx DMA\n"); + goto err_irq; + } + + /* Allocate TX and RX buffers for DMA channels */ + si->rx_buf_dma_virt = + dma_alloc_coherent(NULL, IRDA_FRAME_SIZE_LIMIT, + &(si->rx_buf_dma_phys), + GFP_KERNEL); + + if (!si->rx_buf_dma_virt) { + printk(KERN_ERR "Unable to allocate memory for rx_buf_dma\n"); + goto err_irq; + } + + si->tx_buf_dma_virt = + dma_alloc_coherent(NULL, IRDA_FRAME_SIZE_LIMIT, + &(si->tx_buf_dma_phys), + GFP_KERNEL); + + if (!si->tx_buf_dma_virt) { + printk(KERN_ERR "Unable to allocate memory for tx_buf_dma\n"); + goto err_mem1; + } + + /* + * Setup the serial port for the specified config. + */ + if (si->pdata->select_irda) + si->pdata->select_irda(si->dev, IR_SEL); + + err = omap_irda_startup(dev); + + if (err) + goto err_startup; + + omap_irda_set_speed(dev, si->speed = 9600); + + /* + * Open a new IrLAP layer instance. + */ + si->irlap = irlap_open(dev, &si->qos, "omap_sir"); + + err = -ENOMEM; + if (!si->irlap) + goto err_irlap; + + /* Now enable the interrupt and start the queue */ + si->open = 1; + + /* Start RX DMA */ + omap_irda_start_rx_dma(si); + + enable_irq(dev->irq); + netif_start_queue(dev); + + return 0; + +err_irlap: + si->open = 0; + omap_irda_shutdown(si); +err_startup: + dma_free_coherent(NULL, IRDA_FRAME_SIZE_LIMIT, + si->tx_buf_dma_virt, si->tx_buf_dma_phys); +err_mem1: + dma_free_coherent(NULL, IRDA_FRAME_SIZE_LIMIT, + si->rx_buf_dma_virt, si->rx_buf_dma_phys); +err_irq: + free_irq(dev->irq, dev); + return err; +} + +static int omap_irda_stop(struct net_device *dev) +{ + struct omap_irda *si = dev->priv; + + disable_irq(dev->irq); + + netif_stop_queue(dev); + + omap_free_dma(si->rx_dma_channel); + omap_free_dma(si->tx_dma_channel); + + if (si->rx_buf_dma_virt) + dma_free_coherent(NULL, IRDA_FRAME_SIZE_LIMIT, + si->rx_buf_dma_virt, si->rx_buf_dma_phys); + if (si->tx_buf_dma_virt) + dma_free_coherent(NULL, IRDA_FRAME_SIZE_LIMIT, + si->tx_buf_dma_virt, si->tx_buf_dma_phys); + + omap_irda_shutdown(si); + + /* Stop IrLAP */ + if (si->irlap) { + irlap_close(si->irlap); + si->irlap = NULL; + } + + si->open = 0; + + /* + * Free resources + */ + free_irq(dev->irq, dev); + + return 0; +} + +static int omap_irda_set_speed(struct net_device *dev, int speed) +{ + struct omap_irda *si = dev->priv; + int divisor; + unsigned long flags; + + /* Set IrDA speed */ + if (speed <= 115200) { + + local_irq_save(flags); + + /* SIR mode */ + if (si->pdata->transceiver_mode) + si->pdata->transceiver_mode(si->dev, IR_SIRMODE); + + /* Set SIR mode */ + uart_reg_out(UART3_MDR1, 1); + uart_reg_out(UART3_EBLR, 1); + + divisor = 48000000 / (16 * speed); /* Base clock 48 MHz */ + + uart_reg_out(UART3_LCR, UART3_LCR_DIVEN); + uart_reg_out(UART3_DLL, (divisor & 0xff)); + uart_reg_out(UART3_DLH, (divisor >> 8)); + uart_reg_out(UART3_LCR, 0x03); + + uart_reg_out(UART3_MCR, 0); + + local_irq_restore(flags); + } else if (speed <= 1152000) { + + local_irq_save(flags); + + /* Set MIR mode, auto SIP */ + uart_reg_out(UART3_MDR1, UART3_MDR1_MIR | + UART3_MDR1_SIP_AUTO); + + uart_reg_out(UART3_EBLR, 2); + + divisor = 48000000 / (41 * speed); /* Base clock 48 MHz */ + + uart_reg_out(UART3_LCR, UART3_LCR_DIVEN); + uart_reg_out(UART3_DLL, (divisor & 0xff)); + uart_reg_out(UART3_DLH, (divisor >> 8)); + uart_reg_out(UART3_LCR, 0x03); + + if (si->pdata->transceiver_mode) + si->pdata->transceiver_mode(si->dev, IR_MIRMODE); + + local_irq_restore(flags); + } else { + local_irq_save(flags); + + /* FIR mode */ + uart_reg_out(UART3_MDR1, UART3_MDR1_FIR | + UART3_MDR1_SIP_AUTO); + + if (si->pdata->transceiver_mode) + si->pdata->transceiver_mode(si->dev, IR_FIRMODE); + + local_irq_restore(flags); + } + + si->speed = speed; + + return 0; +} + +#ifdef CONFIG_PM +/* + * Suspend the IrDA interface. + */ +static int omap_irda_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct omap_irda *si = dev->priv; + + if (!dev) + return 0; + + if (si->open) { + /* + * Stop the transmit queue + */ + netif_device_detach(dev); + disable_irq(dev->irq); + omap_irda_shutdown(si); + } + return 0; +} + +/* + * Resume the IrDA interface. + */ +static int omap_irda_resume(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + struct omap_irda *si= dev->priv; + + if (!dev) + return 0; + + if (si->open) { + /* + * If we missed a speed change, initialise at the new speed + * directly. It is debatable whether this is actually + * required, but in the interests of continuing from where + * we left off it is desireable. The converse argument is + * that we should re-negotiate at 9600 baud again. + */ + if (si->newspeed) { + si->speed = si->newspeed; + si->newspeed = 0; + } + + omap_irda_startup(dev); + omap_irda_set_speed(dev, si->speed); + enable_irq(dev->irq); + + /* + * This automatically wakes up the queue + */ + netif_device_attach(dev); + } + + return 0; +} +#else +#define omap_irda_suspend NULL +#define omap_irda_resume NULL +#endif + +static int omap_irda_probe(struct platform_device *pdev) +{ + struct net_device *dev; + struct omap_irda *si; + struct omap_irda_config *pdata = pdev->dev.platform_data; + unsigned int baudrate_mask; + int err = 0; + int irq = NO_IRQ; + + if (!pdata) { + printk(KERN_ERR "IrDA Platform data not supplied\n"); + return -ENOENT; + } + + if (!pdata->rx_channel || !pdata->tx_channel) { + printk(KERN_ERR "IrDA invalid rx/tx channel value\n"); + return -ENOENT; + } + + irq = platform_get_irq(pdev, 0); + if (irq <= 0) { + printk(KERN_WARNING "no irq for IrDA\n"); + return -ENOENT; + } + + dev = alloc_irdadev(sizeof(struct omap_irda)); + if (!dev) + goto err_mem_1; + + + si = dev->priv; + si->dev = &pdev->dev; + si->pdata = pdata; + + dev->hard_start_xmit = omap_irda_hard_xmit; + dev->open = omap_irda_start; + dev->stop = omap_irda_stop; + dev->do_ioctl = omap_irda_ioctl; + dev->get_stats = omap_irda_stats; + dev->irq = irq; + + irda_init_max_qos_capabilies(&si->qos); + + baudrate_mask = 0; + if (si->pdata->transceiver_cap & IR_SIRMODE) + baudrate_mask |= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200; + if (si->pdata->transceiver_cap & IR_MIRMODE) + baudrate_mask |= IR_57600 | IR_1152000; + if (si->pdata->transceiver_cap & IR_FIRMODE) + baudrate_mask |= IR_4000000 << 8; + + si->qos.baud_rate.bits &= baudrate_mask; + si->qos.min_turn_time.bits = 7; + + irda_qos_bits_to_value(&si->qos); + + /* Any better way to avoid this? No. */ + if (machine_is_omap_h3() || machine_is_omap_h4()) + INIT_WORK(&si->pdata->gpio_expa, NULL, NULL); + + err = register_netdev(dev); + if (!err) + platform_set_drvdata(pdev, dev); + else + free_netdev(dev); + +err_mem_1: + return err; +} + +static int omap_irda_remove(struct platform_device *pdev) +{ + struct net_device *dev = platform_get_drvdata(pdev); + + if (pdev) { + unregister_netdev(dev); + free_netdev(dev); + } + return 0; +} + +static struct platform_driver omapir_driver = { + .probe = omap_irda_probe, + .remove = omap_irda_remove, + .suspend = omap_irda_suspend, + .resume = omap_irda_resume, + .driver = { + .name = "omapirda", + }, +}; + +static char __initdata banner[] = KERN_INFO "OMAP IrDA driver initializing\n"; + +static int __init omap_irda_init(void) +{ + printk(banner); + return platform_driver_register(&omapir_driver); +} + +static void __exit omap_irda_exit(void) +{ + platform_driver_unregister(&omapir_driver); +} + +module_init(omap_irda_init); +module_exit(omap_irda_exit); + +MODULE_AUTHOR("MontaVista"); +MODULE_DESCRIPTION("OMAP IrDA Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/net/smc91x.c b/drivers/net/smc91x.c index 7ec08127c9d..193fd83ed8c 100644 --- a/drivers/net/smc91x.c +++ b/drivers/net/smc91x.c @@ -500,6 +500,11 @@ static inline void smc_rcv(struct net_device *dev) dev->name, packet_number, status, packet_len, packet_len); + if (unlikely(packet_len == 0 && !(status & RS_ERRORS))) { + printk(KERN_ERR "%s: bad memory timings: rxlen %u status %x\n", + dev->name, packet_len, status); + status |= RS_TOOSHORT; + } back: if (unlikely(packet_len < 6 || status & RS_ERRORS)) { if (status & RS_TOOLONG && packet_len <= (1514 + 4 + 6)) { diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h index e0efd1964e7..a91a8a03ddc 100644 --- a/drivers/net/smc91x.h +++ b/drivers/net/smc91x.h @@ -188,6 +188,7 @@ SMC_outw(u16 val, void __iomem *ioaddr, int reg) #define SMC_IRQ_FLAGS (( \ machine_is_omap_h2() \ || machine_is_omap_h3() \ + || machine_is_omap_h4() \ || (machine_is_omap_innovator() && !cpu_is_omap1510()) \ ) ? SA_TRIGGER_FALLING : SA_TRIGGER_RISING) diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c index 47b5ade95bd..dfb18c82fdf 100644 --- a/drivers/pcmcia/omap_cf.c +++ b/drivers/pcmcia/omap_cf.c @@ -121,7 +121,8 @@ static int omap_cf_get_status(struct pcmcia_socket *s, u_int *sp) *sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD; cf = container_of(s, struct omap_cf_socket, socket); - s->irq.AssignedIRQ = cf->irq; + s->irq.AssignedIRQ = 0; + s->pci_irq = cf->irq; } else *sp = 0; return 0; @@ -320,7 +321,6 @@ static int __devexit omap_cf_remove(struct device *dev) struct omap_cf_socket *cf = dev_get_drvdata(dev); cf->active = 0; - pcmcia_unregister_socket(&cf->socket); del_timer_sync(&cf->timer); iounmap((void __iomem *) cf->socket.io_offset); release_mem_region(cf->phys_cf, SZ_8K); diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c index 7aca22c9976..8b1fc5aa303 100644 --- a/drivers/serial/8250.c +++ b/drivers/serial/8250.c @@ -1342,7 +1342,8 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id, struct pt_regs *r DEBUG_INTR("end.\n"); - return IRQ_RETVAL(handled); + //return IRQ_RETVAL(handled); + return IRQ_HANDLED; /* FIXME: iir status not ready on 1510 */ } /* @@ -1855,6 +1856,17 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios, serial_outp(up, UART_EFR, efr); } +#ifdef CONFIG_ARCH_OMAP15XX + /* Workaround to enable 115200 baud on OMAP1510 internal ports */ + if (cpu_is_omap1510() && is_omap_port((unsigned int)up->port.membase)) { + if (baud == 115200) { + quot = 1; + serial_out(up, UART_OMAP_OSC_12M_SEL, 1); + } else + serial_out(up, UART_OMAP_OSC_12M_SEL, 0); + } +#endif + if (up->capabilities & UART_NATSEMI) { /* Switch to bank 2 not bank 1, to avoid resetting EXCR2 */ serial_outp(up, UART_LCR, 0xe0); @@ -1879,6 +1891,19 @@ serial8250_set_termios(struct uart_port *port, struct termios *termios, /* emulated UARTs (Lucent Venus 167x) need two steps */ serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO); } + + /* Note that we need to set ECB to access write water mark + * bits. First allow FCR tx fifo write, then set fcr with + * possible TX fifo settings. */ + if (uart_config[up->port.type].flags & UART_CAP_EFR) { + serial_outp(up, UART_LCR, 0xbf); /* Access EFR */ + serial_outp(up, UART_EFR, UART_EFR_ECB); + serial_outp(up, UART_LCR, 0x0); /* Access FCR */ + serial_outp(up, UART_FCR, fcr); + serial_outp(up, UART_LCR, 0xbf); /* Access EFR */ + serial_outp(up, UART_EFR, 0); + serial_outp(up, UART_LCR, cval); /* Access FCR */ + } else serial_outp(up, UART_FCR, fcr); /* set fcr */ } serial8250_set_mctrl(&up->port, up->port.mctrl); @@ -1905,6 +1930,11 @@ static int serial8250_request_std_resource(struct uart_8250_port *up) unsigned int size = 8 << up->port.regshift; int ret = 0; +#ifdef CONFIG_ARCH_OMAP + if (is_omap_port((unsigned int)up->port.membase)) + size = 0x16 << up->port.regshift; +#endif + switch (up->port.iotype) { case UPIO_MEM: if (!up->port.mapbase) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 7a75faeb052..6ca4eec3be5 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -75,6 +75,29 @@ config SPI_BUTTERFLY inexpensive battery powered microcontroller evaluation board. This same cable can be used to flash new firmware. +config SPI_BUTTERFLY + tristate "Parallel port adapter for AVR Butterfly (DEVELOPMENT)" + depends on SPI_MASTER && PARPORT && EXPERIMENTAL + select SPI_BITBANG + help + This uses a custom parallel port cable to connect to an AVR + Butterfly , an + inexpensive battery powered microcontroller evaluation board. + This same cable can be used to flash new firmware. + +config SPI_OMAP_UWIRE + tristate "OMAP MicroWire" + depends on SPI_MASTER && ARCH_OMAP + select SPI_BITBANG + help + This hooks up to the MicroWire controller on OMAP chips. + +config SPI_OMAP24XX + bool "McSPI driver for OMAP24xx" + depends on SPI_MASTER && ARCH_OMAP24XX + help + SPI master controller for OMAP24xx McSPI modules. + # # Add new SPI master controllers in alphabetical order above this line # diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c2c87e845ab..ba27054f8b4 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -13,6 +13,8 @@ obj-$(CONFIG_SPI_MASTER) += spi.o # SPI master controller drivers (bus) obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o +obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o +obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o # ... add above this line ... # SPI protocol drivers (device/link on bus) diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c new file mode 100644 index 00000000000..5042fc80a0e --- /dev/null +++ b/drivers/spi/omap2_mcspi.c @@ -0,0 +1,505 @@ +/* + * OMAP2 McSPI controller driver + * + * Copyright (C) 2005, 2006 Nokia Corporation + * Author: Samuel Ortiz and + * Juha Yrjölä + * + * 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 + +#define OMAP2_MCSPI_MAX_FREQ 48000000 + +#define OMAP2_MCSPI_REVISION 0x00 +#define OMAP2_MCSPI_SYSCONFIG 0x10 +#define OMAP2_MCSPI_SYSSTATUS 0x14 +#define OMAP2_MCSPI_IRQSTATUS 0x18 +#define OMAP2_MCSPI_IRQENABLE 0x1c +#define OMAP2_MCSPI_WAKEUPENABLE 0x20 +#define OMAP2_MCSPI_SYST 0x24 +#define OMAP2_MCSPI_MODULCTRL 0x28 +#define OMAP2_MCSPI_CHCONF0 0x2c +#define OMAP2_MCSPI_CHSTAT0 0x30 +#define OMAP2_MCSPI_CHCTRL0 0x34 +#define OMAP2_MCSPI_TX0 0x38 +#define OMAP2_MCSPI_RX0 0x3c + +#define OMAP2_MCSPI_SYSCONFIG_SOFTRESET (1 << 1) + +#define OMAP2_MCSPI_SYSSTATUS_RESETDONE (1 << 0) + +#define OMAP2_MCSPI_MODULCTRL_SINGLE (1 << 0) +#define OMAP2_MCSPI_MODULCTRL_MS (1 << 2) +#define OMAP2_MCSPI_MODULCTRL_STEST (1 << 3) + +#define OMAP2_MCSPI_CHCONF_PHA (1 << 0) +#define OMAP2_MCSPI_CHCONF_POL (1 << 1) +#define OMAP2_MCSPI_CHCONF_CLKD_MASK (0x0f << 2) +#define OMAP2_MCSPI_CHCONF_EPOL (1 << 6) +#define OMAP2_MCSPI_CHCONF_WL_MASK (0x1f << 7) +#define OMAP2_MCSPI_CHCONF_TRM_RX_ONLY (0x01 << 12) +#define OMAP2_MCSPI_CHCONF_TRM_TX_ONLY (0x02 << 12) +#define OMAP2_MCSPI_CHCONF_TRM_MASK (0x03 << 12) +#define OMAP2_MCSPI_CHCONF_DPE0 (1 << 16) +#define OMAP2_MCSPI_CHCONF_DPE1 (1 << 17) +#define OMAP2_MCSPI_CHCONF_IS (1 << 18) +#define OMAP2_MCSPI_CHCONF_TURBO (1 << 19) +#define OMAP2_MCSPI_CHCONF_FORCE (1 << 20) + + +#define OMAP2_MCSPI_CHSTAT_RXS (1 << 0) +#define OMAP2_MCSPI_CHSTAT_TXS (1 << 1) +#define OMAP2_MCSPI_CHSTAT_EOT (1 << 2) + +#define OMAP2_MCSPI_CHCTRL_EN (1 << 0) + +struct omap2_mcspi { + struct tasklet_struct tasklet; + spinlock_t lock; + struct list_head msg_queue; + struct spi_master *master; +}; + +struct omap2_mcspi_cs { + u8 transmit_mode; +}; + +#define MOD_REG_BIT(val, mask, set) do { \ + if (set) \ + val |= mask; \ + else \ + val &= ~mask; \ +} while(0) + + +#define MASTER_PDATA(master) (struct omap2_mcspi_platform_config *)((master)->cdev.dev->platform_data) + +static inline void mcspi_write_reg(const struct spi_master *master, + int idx, u32 val) +{ + struct omap2_mcspi_platform_config *pdata = MASTER_PDATA(master); + + __raw_writel(val, pdata->base + idx); +} + +static inline u32 mcspi_read_reg(const struct spi_master *master, + int idx) +{ + struct omap2_mcspi_platform_config *pdata = MASTER_PDATA(master); + + return __raw_readl(pdata->base + idx); +} + +static inline void mcspi_write_cs_reg(const struct spi_device *spi, + int idx, u32 val) +{ + struct omap2_mcspi_platform_config *pdata = MASTER_PDATA(spi->master); + + __raw_writel(val, pdata->base + spi->chip_select * 0x14 + idx); +} + +static inline u32 mcspi_read_cs_reg(const struct spi_device *spi, + int idx) +{ + struct omap2_mcspi_platform_config *pdata = MASTER_PDATA(spi->master); + + return __raw_readl(pdata->base + spi->chip_select * 0x14 + idx); +} + +static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) +{ + u32 l; + + l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0); + MOD_REG_BIT(l, OMAP2_MCSPI_CHCTRL_EN, enable); + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, l); +} + +static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) +{ + u32 l; + + l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active); + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); +} + +static void omap2_mcspi_set_master_mode(struct spi_device *spi, int single_channel) +{ + u32 l; + + /* Need reset when switching from slave mode */ + l = mcspi_read_reg(spi->master, OMAP2_MCSPI_MODULCTRL); + MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_STEST, 0); + MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0); + MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, single_channel); + mcspi_write_reg(spi->master, OMAP2_MCSPI_MODULCTRL, l); +} + +static void omap2_mcspi_txrx(struct spi_device *spi, struct spi_transfer *xfer) +{ + unsigned int count, c; + u32 l; + unsigned long base, tx_reg, rx_reg, chstat_reg; + + count = xfer->len; + c = count; + + l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + l &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; + if (xfer->tx_buf == NULL) + l |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; + else if (xfer->rx_buf == NULL) + l |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); + + omap2_mcspi_set_enable(spi, 1); + + /* We store the pre-calculated register addresses on stack to speed + * up the transfer loop. */ + base = (MASTER_PDATA(spi->master))->base + spi->chip_select * 0x14; + tx_reg = base + OMAP2_MCSPI_TX0; + rx_reg = base + OMAP2_MCSPI_RX0; + chstat_reg = base + OMAP2_MCSPI_CHSTAT0; + + /* RX_ONLY mode needs dummy data in TX reg */ + if (xfer->tx_buf == NULL) + __raw_writel(0, tx_reg); + + if (spi->bits_per_word <= 8) { + u8 *rx; + const u8 *tx; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + + while (c--) { + if (tx != NULL) { + while (!(__raw_readl(chstat_reg) & OMAP2_MCSPI_CHSTAT_TXS)); + __raw_writel(*tx, tx_reg); + } + if (rx != NULL) { + while (!(__raw_readl(chstat_reg) & OMAP2_MCSPI_CHSTAT_RXS)); + if (c == 0 && tx == NULL) + omap2_mcspi_set_enable(spi, 0); + *rx++ = __raw_readl(rx_reg); + } + } + } else if (spi->bits_per_word <= 16) { + u16 *rx; + const u16 *tx; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + c >>= 1; + while (c--) { + if (tx != NULL) { + while (!(__raw_readl(chstat_reg) & OMAP2_MCSPI_CHSTAT_TXS)); + __raw_writel(*tx++, tx_reg); + } + if (rx != NULL) { + while (!(__raw_readl(chstat_reg) & OMAP2_MCSPI_CHSTAT_RXS)); + if (c == 0 && tx == NULL) + omap2_mcspi_set_enable(spi, 0); + *rx++ = __raw_readl(rx_reg); + } + } + } else if (spi->bits_per_word <= 32) { + u32 *rx; + const u32 *tx; + + rx = xfer->rx_buf; + tx = xfer->tx_buf; + c >>= 2; + while (c--) { + if (tx != NULL) { + while (!(__raw_readl(chstat_reg) & OMAP2_MCSPI_CHSTAT_TXS)); + __raw_writel(*tx++, tx_reg); + } + if (rx != NULL) { + while (!(__raw_readl(chstat_reg) & OMAP2_MCSPI_CHSTAT_RXS)); + if (c == 0 && tx == NULL) + omap2_mcspi_set_enable(spi, 0); + *rx++ = __raw_readl(rx_reg); + } + } + } + + if (xfer->tx_buf != NULL) { + while (!(__raw_readl(chstat_reg) & OMAP2_MCSPI_CHSTAT_TXS)); + while (!(__raw_readl(chstat_reg) & OMAP2_MCSPI_CHSTAT_EOT)); + omap2_mcspi_set_enable(spi, 0); + } +} + +static int omap2_mcspi_setup(struct spi_device *spi) +{ + struct omap2_mcspi_cs *cs = spi->controller_state; + struct omap2_mcspi_device_config *conf; + u32 l = 0, div = 0; + u8 word_len = spi->bits_per_word; + + if (!word_len) + return 0; + + if (!cs) { + cs = kzalloc(sizeof *cs, SLAB_KERNEL); + if (!cs) + return -ENOMEM; + spi->controller_state = cs; + } + + if (spi->bits_per_word > 32) + return -EINVAL; + + conf = (struct omap2_mcspi_device_config *) spi->controller_data; + + if (conf->single_channel == 1) + omap2_mcspi_set_master_mode(spi, 1); + else + omap2_mcspi_set_master_mode(spi, 0); + + if (spi->max_speed_hz) { + while (div <= 15 && (OMAP2_MCSPI_MAX_FREQ / (1 << div)) > spi->max_speed_hz) + div++; + } else + div = 15; + + if (spi->chip_select > 3 || + word_len < 4 || word_len > 32 || + div > 15) { + dev_err(&spi->dev, "Invalid McSPI channel setting\n"); + return -EINVAL; + } + + l = mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0); + l &= ~OMAP2_MCSPI_CHCONF_IS; + l &= ~OMAP2_MCSPI_CHCONF_DPE1; + l |= OMAP2_MCSPI_CHCONF_DPE0; + l &= ~OMAP2_MCSPI_CHCONF_WL_MASK; + l |= (word_len - 1) << 7; + if (!(spi->mode & SPI_CS_HIGH)) + l |= OMAP2_MCSPI_CHCONF_EPOL; + l &= ~OMAP2_MCSPI_CHCONF_CLKD_MASK; + l |= div << 2; + if (spi->mode & SPI_CPOL) + l |= OMAP2_MCSPI_CHCONF_POL; + if (spi->mode & SPI_CPHA) + l |= OMAP2_MCSPI_CHCONF_PHA; + mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, l); + + return 0; +} + +static void omap2_mcspi_cleanup(const struct spi_device *spi) +{ + if (spi->controller_state != NULL) + kfree(spi->controller_state); +} + + +static void omap2_mcspi_work(unsigned long arg) +{ + struct omap2_mcspi *mcspi = (struct omap2_mcspi *) arg; + unsigned long flags; + + spin_lock_irqsave(&mcspi->lock, flags); + while (!list_empty(&mcspi->msg_queue)) { + struct spi_message *m; + struct spi_device *spi; + struct spi_transfer *t = NULL; + int cs_active; + struct omap2_mcspi_device_config *conf; + struct omap2_mcspi_cs *cs; + int status = 0; + + m = container_of(mcspi->msg_queue.next, struct spi_message, + queue); + + list_del_init(&m->queue); + spin_unlock_irqrestore(&mcspi->lock, flags); + + spi = m->spi; + conf = (struct omap2_mcspi_device_config *) spi->controller_data; + cs = (struct omap2_mcspi_cs *) spi->controller_state; + + omap2_mcspi_force_cs(spi, 1); + cs_active = 1; + + list_for_each_entry(t, &m->transfers, transfer_list) { + if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) { + status = -EINVAL; + break; + } + + if (!cs_active) { + omap2_mcspi_force_cs(spi, 1); + cs_active = 1; + } + + omap2_mcspi_txrx(spi, t); + + if (t->cs_change) { + omap2_mcspi_force_cs(spi, 0); + cs_active = 0; + } + } + + if (cs_active) + omap2_mcspi_force_cs(spi, 0); + + m->status = status; + m->complete(m->context); + + spin_lock_irqsave(&mcspi->lock, flags); + } + spin_unlock_irqrestore(&mcspi->lock, flags); +} + +static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m) +{ + struct omap2_mcspi *mcspi; + unsigned long flags; + + m->actual_length = 0; + m->status = 0; + + mcspi = class_get_devdata(&spi->master->cdev); + + spin_lock_irqsave(&mcspi->lock, flags); + list_add_tail(&m->queue, &mcspi->msg_queue); + spin_unlock_irqrestore(&mcspi->lock, flags); + + tasklet_hi_schedule(&mcspi->tasklet); + + return 0; +} + +static int __devinit omap2_mcspi_reset(struct spi_master *master) +{ +#if 0 + mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG, + OMAP2_MCSPI_SYSCONFIG_SOFTRESET); + while (!(mcspi_read_reg(master, OMAP2_MCSPI_SYSSTATUS) & OMAP2_MCSPI_SYSSTATUS_RESETDONE)); +#else + return 0; +#endif +} + +static int __devinit omap2_mcspi_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct omap2_mcspi_platform_config *pdata = pdev->dev.platform_data; + struct omap2_mcspi *mcspi; + int status = 0; + + if (!pdata) + return -EINVAL; + + master = spi_alloc_master(&pdev->dev, sizeof *mcspi); + if (!master) { + dev_err(&pdev->dev, "master allocation failed\n"); + return -ENOMEM; + } + + if (pdev->id != -1) + master->bus_num = pdev->id; + + master->setup = omap2_mcspi_setup; + master->transfer = omap2_mcspi_transfer; + master->cleanup = omap2_mcspi_cleanup; + master->num_chipselect = pdata->num_cs; + + if (class_device_get(&master->cdev) == NULL) { + dev_err(&pdev->dev, "no master->cdev"); + status = -ENOMEM; + goto err0; + } + + dev_set_drvdata(&pdev->dev, master); + + mcspi = class_get_devdata(&master->cdev); + mcspi->master = master; + + tasklet_init(&mcspi->tasklet, omap2_mcspi_work, (unsigned long) mcspi); + + spin_lock_init(&mcspi->lock); + INIT_LIST_HEAD(&mcspi->msg_queue); + + if (omap2_mcspi_reset(master) < 0) + goto err1; + + status = spi_register_master(master); + if (status < 0) + goto err1; + + return status; + + err1: + class_device_put(&master->cdev); + err0: + return status; +} + +static int __devexit omap2_mcspi_remove(struct platform_device *pdev) +{ + struct spi_master *master; + + master = dev_get_drvdata(&pdev->dev); + + spi_unregister_master(master); + + return 0; +} + +struct platform_driver omap2_mcspi_driver = { + .driver = { + .name = "omap2_mcspi", + .owner = THIS_MODULE, + }, + .probe = omap2_mcspi_probe, + .remove = __devexit_p(omap2_mcspi_remove), +}; +EXPORT_SYMBOL_GPL(omap2_mcspi_driver); + + +static int __init omap2_mcspi_init(void) +{ + printk(KERN_INFO "OMAP24xx McSPI driver initializing\n"); + return platform_driver_register(&omap2_mcspi_driver); +} +subsys_initcall(omap2_mcspi_init); + +static void __exit omap2_mcspi_exit(void) +{ + platform_driver_unregister(&omap2_mcspi_driver); +} +module_exit(omap2_mcspi_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/spi/omap_uwire.c b/drivers/spi/omap_uwire.c new file mode 100644 index 00000000000..8d2e4949eb4 --- /dev/null +++ b/drivers/spi/omap_uwire.c @@ -0,0 +1,515 @@ +/* + * omap_uwire.c -- MicroWire interface driver for OMAP + * + * Copyright 2003 MontaVista Software Inc. + * + * Ported to 2.6 OMAP uwire interface. + * Copyright (C) 2004 Texas Instruments. + * + * Generalization patches by Juha Yrjölä + * + * Copyright (C) 2005 David Brownell (ported to 2.6 SPI 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; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include /* OMAP730_IO_CONF registers */ + + +/* FIXME address is now a platform device resource, + * and irqs should show there too... + */ +#define UWIRE_BASE_PHYS 0xFFFB3000 +#define UWIRE_BASE ((void *__iomem)IO_ADDRESS(UWIRE_BASE_PHYS)) + +/* uWire Registers: */ +#define UWIRE_IO_SIZE 0x20 +#define UWIRE_TDR 0x00 +#define UWIRE_RDR 0x00 +#define UWIRE_CSR 0x01 +#define UWIRE_SR1 0x02 +#define UWIRE_SR2 0x03 +#define UWIRE_SR3 0x04 +#define UWIRE_SR4 0x05 +#define UWIRE_SR5 0x06 + +/* CSR bits */ +#define RDRB (1 << 15) +#define CSRB (1 << 14) +#define START (1 << 13) +#define CS_CMD (1 << 12) + +/* SR1 or SR2 bits */ +#define UWIRE_READ_FALLING_EDGE 0x0000 +#define UWIRE_READ_RISING_EDGE 0x0001 +#define UWIRE_WRITE_FALLING_EDGE 0x0000 +#define UWIRE_WRITE_RISING_EDGE 0x0002 +#define UWIRE_CS_ACTIVE_LOW 0x0000 +#define UWIRE_CS_ACTIVE_HIGH 0x0004 +#define UWIRE_FREQ_DIV_2 0x0000 +#define UWIRE_FREQ_DIV_4 0x0008 +#define UWIRE_FREQ_DIV_8 0x0010 +#define UWIRE_CHK_READY 0x0020 +#define UWIRE_CLK_INVERTED 0x0040 + + +struct uwire_spi { + struct spi_bitbang bitbang; + struct clk *ck; +}; + +/* REVISIT compile time constant for idx_shift? */ +static unsigned int uwire_idx_shift; + +static inline void uwire_write_reg(int idx, u16 val) +{ + __raw_writew(val, UWIRE_BASE + (idx << uwire_idx_shift)); +} + +static inline u16 uwire_read_reg(int idx) +{ + return __raw_readw(UWIRE_BASE + (idx << uwire_idx_shift)); +} + +static inline void omap_uwire_configure_mode(u8 cs, unsigned long flags) +{ + u16 w, val = 0; + int shift, reg; + + if (flags & UWIRE_CLK_INVERTED) + val ^= 0x03; + val = flags & 0x3f; + if (cs & 1) + shift = 6; + else + shift = 0; + if (cs <= 1) + reg = UWIRE_SR1; + else + reg = UWIRE_SR2; + + w = uwire_read_reg(reg); + w &= ~(0x3f << shift); + w |= val << shift; + uwire_write_reg(reg, w); +} + +static int wait_uwire_csr_flag(u16 mask, u16 val, int might_not_catch) +{ + u16 w; + int c = 0; + unsigned long max_jiffies = jiffies + HZ; + + for (;;) { + w = uwire_read_reg(UWIRE_CSR); + if ((w & mask) == val) + break; + if (time_after(jiffies, max_jiffies)) { + printk(KERN_ERR "%s: timeout. reg=%#06x " + "mask=%#06x val=%#06x\n", + __FUNCTION__, w, mask, val); + return -1; + } + c++; + if (might_not_catch && c > 64) + break; + } + return 0; +} + +static void uwire_chipselect(struct spi_device *spi, int value) +{ + u16 w; + int old_cs; + + BUG_ON(wait_uwire_csr_flag(CSRB, 0, 0)); + + w = uwire_read_reg(UWIRE_CSR); + old_cs = (w >> 10) & 0x03; + if (value == BITBANG_CS_INACTIVE || old_cs != spi->chip_select) { + /* Deselect this CS, or the previous CS */ + w &= ~CS_CMD; + uwire_write_reg(UWIRE_CSR, w); + } + /* activate specfied chipselect */ + if (value == BITBANG_CS_ACTIVE) { + /* invert clock? */ + if (spi->mode & SPI_CPOL) + uwire_write_reg(UWIRE_SR4, 1); + else + uwire_write_reg(UWIRE_SR4, 0); + + w = spi->chip_select << 10; + w |= CS_CMD; + uwire_write_reg(UWIRE_CSR, w); + } +} + +static int uwire_txrx(struct spi_device *spi, struct spi_transfer *t) +{ + unsigned len = t->len; + unsigned bits = spi->bits_per_word; + unsigned bytes; + u16 val, w; + int status = 0;; + + if (!t->tx_buf && !t->rx_buf) + return 0; + + /* Microwire doesn't read and write concurrently */ + if (t->tx_buf && t->rx_buf) + return -EPERM; + + w = spi->chip_select << 10; + w |= CS_CMD; + + if (t->tx_buf) { + const u8 *buf = t->tx_buf; + + /* NOTE: DMA could be used for TX transfers */ + + /* write one or two bytes at a time */ + while (len >= 1) { + /* tx is msb-aligned */ + val = *buf++; + if (len > 1 && (!bits || bits > 8)) { + if (!bits) + bits = 16; + bytes = 2; + val |= *buf++ << 8; + } else { + if (!bits || bits > 8) + bits = 8; + bytes = 1; + } + val <<= 16 - bits; + +#ifdef VERBOSE + pr_debug("%s: write-%d =%04x\n", + spi->dev.bus_id, bits, val); +#endif + uwire_write_reg(UWIRE_TDR, val); + + /* start write */ + val = START | w | (bits << 5); + if (wait_uwire_csr_flag(CSRB, 0, 0)) + goto eio; + + uwire_write_reg(UWIRE_CSR, val); + len -= bytes; + + /* Wait till write actually starts. + * This is needed with MPU clock 60+ MHz. + * REVISIT: we may not have time to catch it... + */ + if (wait_uwire_csr_flag(CSRB, CSRB, 1)) + goto eio; + + status += bytes; + } + + /* REVISIT: save this for later to get more i/o overlap */ + if (wait_uwire_csr_flag(CSRB, 0, 0)) + goto eio; + + } else if (t->rx_buf) { + u8 *buf = t->rx_buf; + + /* read one or two bytes at a time */ + while (len) { + if (len > 1 && (!bits || bits > 8)) { + if (!bits) + bits = 16; + bytes = 2; + } else { + if (!bits || bits > 8) + bits = 8; + bytes = 1; + } + + /* start read */ + val = START | w | (bits << 0); + uwire_write_reg(UWIRE_CSR, val); + len -= bytes; + + /* Wait till read actually starts */ + (void) wait_uwire_csr_flag(CSRB, CSRB, 1); + + if (wait_uwire_csr_flag(RDRB | CSRB, + RDRB, 0)) + goto eio; + + /* rx is lsb-aligned */ + val = uwire_read_reg(UWIRE_RDR); + val &= (1 << bits) - 1; + *buf++ = (u8) val; + if (bytes == 2) + *buf++ = val >> 8; + status += bytes; +#ifdef VERBOSE + pr_debug("%s: read-%d =%04x\n", + spi->dev.bus_id, bits, val); +#endif + + } + } + return status; +eio: + return -EIO; +} + +static int uwire_setup(struct spi_device *spi) +{ + struct uwire_spi *uwire; + unsigned flags = 0; + unsigned long rate; + int div1_idx; + int div1; + int div2; + u16 w; + int status; + + uwire = spi_master_get_devdata(spi->master); + + if (spi->chip_select > 3) { + pr_debug("%s: cs%d?\n", spi->dev.bus_id, spi->chip_select); + status = -ENODEV; + goto done; + } + + if (spi->bits_per_word > 16) { + pr_debug("%s: wordsize %d?\n", spi->dev.bus_id, + spi->bits_per_word); + status = -ENODEV; + goto done; + } + + /* mode 0..3, clock inverted separately; + * standard nCS signaling; + * don't treat DI=high as "not ready" + */ + if (spi->mode & SPI_CS_HIGH) + flags |= UWIRE_CS_ACTIVE_HIGH; + + if (spi->mode & SPI_CPOL) + flags |= UWIRE_CLK_INVERTED; + + switch (spi->mode & (SPI_CPOL | SPI_CPHA)) { + case SPI_MODE_0: + case SPI_MODE_3: + flags |= UWIRE_WRITE_FALLING_EDGE | UWIRE_READ_FALLING_EDGE; + break; + case SPI_MODE_1: + case SPI_MODE_2: + flags |= UWIRE_WRITE_FALLING_EDGE | UWIRE_READ_RISING_EDGE; + break; + } + + /* assume it's already enabled */ + rate = clk_get_rate(uwire->ck); + + /* F_INT = mpu_per_clk / DIV1 */ + for (div1_idx = 0; div1_idx < 4; div1_idx++) { + switch (div1_idx) { + case 0: + div1 = 2; + break; + case 1: + div1 = 4; + break; + case 2: + div1 = 7; + break; + default: + case 3: + div1 = 10; + break; + } + div2 = (rate / div1 + spi->max_speed_hz - 1) / + spi->max_speed_hz; + if (div2 <= 8) + break; + } + if (div1_idx == 4) { + pr_debug("%s: lowest clock %ld, need %d\n", + spi->dev.bus_id, rate / 10 / 8, spi->max_speed_hz); + status = -EDOM; + goto done; + } + + w = uwire_read_reg(UWIRE_SR3); + w &= ~(0x03 << 1); + w |= div1_idx << 1; + uwire_write_reg(UWIRE_SR3, w); + + rate /= div1; + + switch (div2) { + case 0: + case 1: + case 2: + flags |= UWIRE_FREQ_DIV_2; + rate /= 2; + break; + case 3: + case 4: + flags |= UWIRE_FREQ_DIV_4; + rate /= 4; + break; + case 5: + case 6: + case 7: + case 8: + flags |= UWIRE_FREQ_DIV_8; + rate /= 8; + break; + } + omap_uwire_configure_mode(spi->chip_select, flags); + pr_debug("%s: uwire flags %02x, armper %lu KHz, SCK %lu KHz\n", + __FUNCTION__, flags, + clk_get_rate(uwire->ck) / 1000, + rate / 1000); + status = 0; +done: + return status; +} + +static void uwire_off(struct uwire_spi *uwire) +{ + uwire_write_reg(UWIRE_SR3, 0); + clk_disable(uwire->ck); + clk_put(uwire->ck); + spi_master_put(uwire->bitbang.master); +} + +static int uwire_probe(struct platform_device *pdev) +{ + struct spi_master *master; + struct uwire_spi *uwire; + int status; + + master = spi_alloc_master(&pdev->dev, sizeof *uwire); + if (!master) + return -ENODEV; + + uwire = spi_master_get_devdata(master); + dev_set_drvdata(&pdev->dev, uwire); + + uwire->ck = clk_get(&pdev->dev, "armper_ck"); + if (!uwire->ck || IS_ERR(uwire->ck)) { + dev_dbg(&pdev->dev, "no mpu_per_clk ?\n"); + spi_master_put(master); + return -ENODEV; + } + clk_enable(uwire->ck); + + if (cpu_is_omap730()) + uwire_idx_shift = 1; + else + uwire_idx_shift = 2; + + uwire_write_reg(UWIRE_SR3, 1); + + master->bus_num = 2; /* "official" */ + master->num_chipselect = 4; + master->setup = uwire_setup; + + uwire->bitbang.master = master; + uwire->bitbang.chipselect = uwire_chipselect; + uwire->bitbang.txrx_bufs = uwire_txrx; + + status = spi_bitbang_start(&uwire->bitbang); + if (status < 0) + uwire_off(uwire); + return status; +} + +static int uwire_remove(struct platform_device *pdev) +{ + struct uwire_spi *uwire = dev_get_drvdata(&pdev->dev); + int status; + + // FIXME remove all child devices, somewhere ... + + status = spi_bitbang_stop(&uwire->bitbang); + uwire_off(uwire); + return status; +} + +static struct platform_driver uwire_driver = { + .driver = { + .name = "omap_uwire", + .bus = &platform_bus_type, + .owner = THIS_MODULE, + }, + .probe = uwire_probe, + .remove = uwire_remove, + // suspend ... unuse ck + // resume ... use ck +}; + +static int __init omap_uwire_init(void) +{ + /* FIXME move these into the relevant board init code. also, include + * H3 support; it uses tsc2101 like H2 (on a different chipselect). + */ + + if (machine_is_omap_h2()) { + /* defaults: W21 SDO, U18 SDI, V19 SCL */ + omap_cfg_reg(N14_1610_UWIRE_CS0); + omap_cfg_reg(N15_1610_UWIRE_CS1); + } + if (machine_is_omap_perseus2()) { + /* configure pins: MPU_UW_nSCS1, MPU_UW_SDO, MPU_UW_SCLK */ + int val = omap_readl(OMAP730_IO_CONF_9) & ~0x00EEE000; + omap_writel(val | 0x00AAA000, OMAP730_IO_CONF_9); + } + + return platform_driver_register(&uwire_driver); +} + +static void __exit omap_uwire_exit(void) +{ + platform_driver_unregister(&uwire_driver); +} + +subsys_initcall(omap_uwire_init); +module_exit(omap_uwire_exit); + +MODULE_LICENSE("GPL"); diff --git a/drivers/ssi/Kconfig b/drivers/ssi/Kconfig new file mode 100644 index 00000000000..e84f9d2a3c9 --- /dev/null +++ b/drivers/ssi/Kconfig @@ -0,0 +1,18 @@ +menu "Synchronous Serial Interfaces (SSI)" + +config OMAP_UWIRE + depends on ARCH_OMAP1 + tristate "MicroWire support on OMAP" + ---help--- + Say Y here if you want support for the MicroWire interface + on an OMAP processor. + +config OMAP_TSC2101 + depends on ARCH_OMAP1 || ARCH_OMAP24XX + tristate "TSC2101 codec support for Touchscreen and audio" + select OMAP_UWIRE if MACH_OMAP_H3 || MACH_OMAP_H2 + select GPIOEXPANDER_OMAP if MACH_OMAP_H3 + ---help--- + Say Y here if you want support for the TSC2101 codec. It is + needed for touchscreen and audio on OMAP1610 and 1710. +endmenu diff --git a/drivers/ssi/Makefile b/drivers/ssi/Makefile new file mode 100644 index 00000000000..c63cf2b2e44 --- /dev/null +++ b/drivers/ssi/Makefile @@ -0,0 +1,6 @@ +# +# Makefile for the SSI drivers +# + +obj-$(CONFIG_OMAP_UWIRE) += omap-uwire.o +obj-$(CONFIG_OMAP_TSC2101) += omap-tsc2101.o diff --git a/drivers/ssi/omap-tsc2101.c b/drivers/ssi/omap-tsc2101.c new file mode 100644 index 00000000000..5dfe994001a --- /dev/null +++ b/drivers/ssi/omap-tsc2101.c @@ -0,0 +1,247 @@ +/* + * linux/drivers/ssi/omap-tsc2101.c + * + * TSC2101 codec interface driver for the OMAP platform + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * + * 2004/11/07 Nishanth Menon - Modified for common hooks for Audio and Touchscreen + */ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "omap-tsc2101.h" + +#if CONFIG_ARCH_OMAP16XX +#include <../drivers/ssi/omap-uwire.h> +#else +#error "Unsupported configuration" +#endif + +#define SPIO 1 + +static int count; +static spinlock_t tsc2101_lock = SPIN_LOCK_UNLOCKED; +static struct clk * tsc2101_mclk_ck; + +static int omap_tsc2101_configure(void); + +/* FIXME: add driver model usage to powerdown the tsc2101 on suspend */ +/* Clock -Hard coding for the time being */ +#define CLK_SOFT_REQ_REG_BASE (0xFFFE0800+0x34) +#define SOFT_COM_MCK0_REQ_MASK (0x1<<6) + +int omap_tsc2101_enable(void) +{ + int ret = 0; + + spin_lock(&tsc2101_lock); + if (count++ == 0) { + int ret = 0; + /* set the Mux to provide MCLK to TSC2101 */ + if (machine_is_omap_h3()) { + ret = omap_cfg_reg(V5_1710_MCLK_ON); + } else { + if (machine_is_omap_h2()) { + ret = omap_cfg_reg(R10_1610_MCLK_ON); + } + } + + /* Get the MCLK */ + tsc2101_mclk_ck = clk_get(NULL, "mclk"); + if (NULL == tsc2101_mclk_ck) { + printk(KERN_ERR "Unable to get the clock MCLK!!!\n");; + ret = -EPERM; + goto done; + } + if (clk_set_rate(tsc2101_mclk_ck, 12000000)) { + printk(KERN_ERR "Unable to set rate to the MCLK!!!\n");; + ret = -EPERM; + goto done; + } + clk_enable(tsc2101_mclk_ck); + + ret = omap_tsc2101_configure(); + + /* Lock the module */ + if (!ret && !try_module_get(THIS_MODULE)) { + printk(KERN_CRIT "Failed to get TSC module\n"); + ret = -ESTALE; + } + } + +done: + spin_unlock(&tsc2101_lock); + return ret; +} + +void omap_tsc2101_disable(void) +{ + spin_lock(&tsc2101_lock); + if (--count == 0) { + int ret = 0; + /* Remove the Mux to Stop MCLK to TSC2101 */ + if (machine_is_omap_h3()) { + ret = omap_cfg_reg(V5_1710_MCLK_OFF); + } else { + if (machine_is_omap_h2()) { + ret = omap_cfg_reg(R10_1610_MCLK_OFF); + } + } + + /* Release the MCLK */ + clk_disable(tsc2101_mclk_ck); + clk_put(tsc2101_mclk_ck); + tsc2101_mclk_ck = NULL; + + module_put(THIS_MODULE); + } + spin_unlock(&tsc2101_lock); +} + +void omap_tsc2101_write(int page, u8 address, u16 data) +{ + + int ret = 0; + + if (machine_is_omap_h2()) { + ret = + omap_uwire_data_transfer(1, + (((page) << 11) | (address << 5)), + 16, 0, NULL, 1); + if (ret) { + printk(KERN_ERR + "uwire-write returned error for address %x\n", + address); + return; + } + ret = omap_uwire_data_transfer(1, data, 16, 0, NULL, 0); + if (ret) { + printk(KERN_ERR + "uwire-write returned error for address %x\n", + address); + return; + } + } + if (machine_is_omap_h3()) { + + ret = + omap_uwire_data_transfer(0, ((page << 11) | (address << 5)), + 16, 0, NULL, 1); + if (ret) { + printk(KERN_ERR + "uwire-write returned error for address %x\n", + address); + return; + } + ret = omap_uwire_data_transfer(0, data, 16, 0, NULL, 0); + if (ret) { + printk(KERN_ERR + "uwire-write returned error for address %x\n", + address); + return; + } + } + +} + +void omap_tsc2101_reads(int page, u8 startaddress, u16 * data, int numregs) +{ + int cs = 0, i; + if (machine_is_omap_h2()) { + cs = 1; + } + if (machine_is_omap_h3()) { + cs = 0; + } + (void)omap_uwire_data_transfer(cs, (0x8000 | (page << 11) + | (startaddress << 5)), + 16, 0, NULL, 1); + for (i = 0; i < (numregs - 1); i++, data++) { + omap_uwire_data_transfer(cs, 0, 0, 16, data, 1); + } + omap_uwire_data_transfer(cs, 0, 0, 16, data, 0); +} + +u16 omap_tsc2101_read(int page, u8 address) +{ + u16 ret; + omap_tsc2101_reads(page, address, &ret, 1); + return ret; +} + +/* FIXME: adapt clock divisors for uwire to current ARM xor clock rate */ +static int omap_tsc2101_configure(void) +{ + unsigned long uwire_flags = 0; + +#ifdef CONFIG_MACH_OMAP_H3 + int err = 0; + u8 ioExpanderVal = 0; + + if ((err = read_gpio_expa(&ioExpanderVal, 0x24))) { + printk(" Error reading from I/O EXPANDER \n"); + return err; + } + ioExpanderVal |= 0x8; + + if ((err = write_gpio_expa(ioExpanderVal, 0x24))) { + printk(KERN_ERR ": Error writing to I/O EXPANDER \n"); + return err; + } +#endif + + if (machine_is_omap_h2()) { + uwire_flags = UWIRE_READ_RISING_EDGE | UWIRE_WRITE_RISING_EDGE; + omap_cfg_reg(N15_1610_UWIRE_CS1); + omap_uwire_configure_mode(1, uwire_flags); + } + if (machine_is_omap_h3()) { + uwire_flags = UWIRE_READ_RISING_EDGE | UWIRE_WRITE_RISING_EDGE; + omap_cfg_reg(N14_1610_UWIRE_CS0); + omap_uwire_configure_mode(0, uwire_flags); + } + + /* Configure MCLK enable */ + omap_writel(omap_readl(PU_PD_SEL_2) | (1 << 22), PU_PD_SEL_2); + + return 0; +} + +EXPORT_SYMBOL(omap_tsc2101_enable); +EXPORT_SYMBOL(omap_tsc2101_read); +EXPORT_SYMBOL(omap_tsc2101_reads); +EXPORT_SYMBOL(omap_tsc2101_write); +EXPORT_SYMBOL(omap_tsc2101_disable); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION + ("Glue audio driver for the TI OMAP1610/OMAP1710 TSC2101 codec."); +MODULE_LICENSE("GPL"); diff --git a/drivers/ssi/omap-tsc2101.h b/drivers/ssi/omap-tsc2101.h new file mode 100644 index 00000000000..f6b40b8f446 --- /dev/null +++ b/drivers/ssi/omap-tsc2101.h @@ -0,0 +1,32 @@ +/* + * linux/drivers/ssi/omap-tsc2101.h + * + * TSC2101 codec interface driver for the OMAP platform + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * + * 2004/11/07 Nishanth Menon - Provided common hooks for Audio and Touchscreen + */ + +#ifndef __OMAP_TSC2101_H +#define __OMAP_TSC2101_H + +extern u16 omap_tsc2101_read(int page, u8 address); +extern void omap_tsc2101_reads(int page, u8 startaddress, u16 * data, + int numregs); +extern void omap_tsc2101_write(int page, u8 address, u16 data); + +extern void omap_tsc2101_disable(void); +extern int omap_tsc2101_enable(void); + +#endif diff --git a/drivers/ssi/omap-uwire.c b/drivers/ssi/omap-uwire.c new file mode 100644 index 00000000000..61b3ca16257 --- /dev/null +++ b/drivers/ssi/omap-uwire.c @@ -0,0 +1,233 @@ +/* + * BRIEF MODULE DESCRIPTION + * + * uWire interface driver for the OMAP Platform + * + * Copyright 2003 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * source@mvista.com + * + * Ported to 2.6 uwire interface. + * Copyright (C) 2004 Texas Instruments. + * + * Generalization patches by Juha Yrjölä + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include /* OMAP730_IO_CONF registers */ + +#include "omap-uwire.h" + +/* uWire Registers: */ +#define UWIRE_BASE 0xFFFB3000 +#define UWIRE_IO_SIZE 0x20 +#define UWIRE_TDR 0x00 +#define UWIRE_RDR 0x00 +#define UWIRE_CSR 0x01 +#define UWIRE_SR1 0x02 +#define UWIRE_SR2 0x03 +#define UWIRE_SR3 0x04 +#define UWIRE_SR4 0x05 +#define UWIRE_SR5 0x06 + +static unsigned short uwire_flags[4]; +static unsigned long uwire_base = io_p2v(UWIRE_BASE); +static spinlock_t uwire_lock; +static unsigned int uwire_idx_shift; + +static inline void uwire_write_reg(int idx, u16 val) +{ + __raw_writew(val, uwire_base + (idx << uwire_idx_shift)); +} + +static inline u16 uwire_read_reg(int idx) +{ + return __raw_readw(uwire_base + (idx << uwire_idx_shift)); +} + +void omap_uwire_configure_mode(int cs, unsigned long flags) +{ + u16 w, val = 0; + int shift, reg; + + BUG_ON(cs > 3); + + val = flags & 0x3f; + if (flags & UWIRE_CLK_INVERTED) + val ^= 0x03; + if (cs & 1) + shift = 6; + else + shift = 0; + if (cs <= 1) + reg = UWIRE_SR1; + else + reg = UWIRE_SR2; + spin_lock(&uwire_lock); + w = uwire_read_reg(reg); + w &= ~(0x3f << shift); + w |= val << shift; + uwire_write_reg(reg, w); + spin_unlock(&uwire_lock); + + uwire_flags[cs] = flags; +} + +static int wait_uwire_csr_flag(u16 mask, u16 val, int might_not_catch) +{ + u16 w; + int c = 0; + unsigned long max_jiffies = jiffies + HZ; + + for (;;) { + w = uwire_read_reg(UWIRE_CSR); + if ((w & mask) == val) + break; + if (time_after(jiffies, max_jiffies)) { + printk(KERN_ERR "%s: timeout. reg=%#06x mask=%#06x val=%#06x\n", + __FUNCTION__, w, mask, val); + return -1; + } + c++; + if (might_not_catch && c > 64) + break; + } + return 0; +} + +int omap_uwire_data_transfer(int cs, u16 tx_data, int tx_size, int rx_size, + u16 *rx_buf, int leave_cs_active) +{ + u16 ret = -1, w; + u16 mask; + + BUG_ON(cs > 3); + BUG_ON(rx_size && !rx_buf); + + spin_lock(&uwire_lock); + + if (wait_uwire_csr_flag(1 << 14, 0, 0)) + goto exit; + + if (uwire_flags[cs] & UWIRE_CLK_INVERTED) + uwire_write_reg(UWIRE_SR4, 1); + else + uwire_write_reg(UWIRE_SR4, 0); + + w = cs << 10; + w |= 1 << 12; /* CS_CMD : activate CS */ + uwire_write_reg(UWIRE_CSR, w); + + /* Shift data to 16bit MSb and place it in TX register. */ + uwire_write_reg(UWIRE_TDR, tx_data << (16 - tx_size)); + + if (wait_uwire_csr_flag(1 << 14, 0, 0)) + goto exit; + + w = rx_size | (tx_size << 5) | (cs << 10); + w |= (1 << 12) | (1 << 13); + /* Start uWire read/write */ + uwire_write_reg(UWIRE_CSR, w); + + /* Wait till read/write actually starts. + * This is needed at high (>=60MHz) MPU frequencies + * REVISIT: But occasionally we won't have time to catch it + */ + if (wait_uwire_csr_flag(1 << 14, 1 << 14, 1)) + goto exit; + + /* Wait for both transfers to be completed */ + mask = 1 << 14; /* CSRB : reg busy */ + w = 0; + if (rx_size) { + mask |= 1 << 15; /* RDRB : reg busy */ + w |= 1 << 15; + } + + if (wait_uwire_csr_flag(mask, w, 0)) + goto exit; + + if (rx_size) + *rx_buf = uwire_read_reg(UWIRE_RDR); + + if (!leave_cs_active) + uwire_write_reg(UWIRE_CSR, cs << 10); + + ret = 0; + +exit: + spin_unlock(&uwire_lock); + return ret; +} + +static int __init omap_uwire_init(void) +{ + spin_lock_init(&uwire_lock); + if (cpu_is_omap730()) + uwire_idx_shift = 1; + else + uwire_idx_shift = 2; + + uwire_write_reg(UWIRE_SR3, 1); + if (machine_is_omap_h2()) { + /* defaults: W21 SDO, U18 SDI, V19 SCL */ + omap_cfg_reg(N14_1610_UWIRE_CS0); + omap_cfg_reg(N15_1610_UWIRE_CS1); + } + if (machine_is_omap_osk()) { + /* this is the standard expansion connector usage, with + * the other chipselect pins for MPUIO2 and MPUIO4. + */ + omap_cfg_reg(N14_1610_UWIRE_CS0); + omap_cfg_reg(P15_1610_UWIRE_CS3); + } + if (machine_is_omap_perseus2()) { + /* configure pins: MPU_UW_nSCS1, MPU_UW_SDO, MPU_UW_SCLK */ + int val = omap_readl(OMAP730_IO_CONF_9) & ~0x00EEE000; + omap_writel(val | 0x00AAA000, OMAP730_IO_CONF_9); + } + return 0; +} + +static void __exit omap_uwire_exit(void) +{ +} + +subsys_initcall(omap_uwire_init); +module_exit(omap_uwire_exit); + +EXPORT_SYMBOL(omap_uwire_configure_mode); +EXPORT_SYMBOL(omap_uwire_data_transfer); + +MODULE_LICENSE("GPL"); diff --git a/drivers/ssi/omap-uwire.h b/drivers/ssi/omap-uwire.h new file mode 100644 index 00000000000..a0d24bc4add --- /dev/null +++ b/drivers/ssi/omap-uwire.h @@ -0,0 +1,26 @@ +#ifndef __ARCH_OMAP_UWIRE_H +#define __ARCH_OMAP_UWIRE_H + +#define UWIRE_READ_FALLING_EDGE 0x0000 +#define UWIRE_READ_RISING_EDGE 0x0001 +#define UWIRE_WRITE_FALLING_EDGE 0x0000 +#define UWIRE_WRITE_RISING_EDGE 0x0002 +#define UWIRE_CS_ACTIVE_LOW 0x0000 +#define UWIRE_CS_ACTIVE_HIGH 0x0004 +#define UWIRE_FREQ_DIV_2 0x0000 +#define UWIRE_FREQ_DIV_4 0x0008 +#define UWIRE_FREQ_DIV_8 0x0010 +#define UWIRE_CHK_READY 0x0020 +#define UWIRE_CLK_INVERTED 0x0040 + +/* + * uWire for OMAP declarations + */ +extern void omap_uwire_configure_mode(int cs, unsigned long flags); + +/* NOTE: Make sure you don't call this from an interrupt handler! */ +extern int omap_uwire_data_transfer(int cs, u16 tx_data, int tx_size, + int rx_size, u16 *rx_buf, + int leave_cs_active); + +#endif diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index ff075a53c8d..75e15b1ae9a 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -157,7 +157,7 @@ config USB_LH7A40X config USB_GADGET_OMAP boolean "OMAP USB Device Controller" - depends on ARCH_OMAP + depends on ARCH_OMAP1 select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 help Many Texas Instruments OMAP processors have flexible full diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c index a8972d7c97b..f2b065fd28b 100644 --- a/drivers/usb/gadget/omap_udc.c +++ b/drivers/usb/gadget/omap_udc.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -544,9 +545,9 @@ static inline dma_addr_t dma_csac(unsigned lch) /* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is * read before the DMA controller finished disabling the channel. */ - csac = omap_readw(OMAP_DMA_CSAC(lch)); + csac = OMAP_DMA_CSAC_REG(lch); if (csac == 0) - csac = omap_readw(OMAP_DMA_CSAC(lch)); + csac = OMAP_DMA_CSAC_REG(lch); return csac; } @@ -557,9 +558,9 @@ static inline dma_addr_t dma_cdac(unsigned lch) /* omap 3.2/3.3 erratum: sometimes 0 is returned if CSAC/CDAC is * read before the DMA controller finished disabling the channel. */ - cdac = omap_readw(OMAP_DMA_CDAC(lch)); + cdac = OMAP_DMA_CDAC_REG(lch); if (cdac == 0) - cdac = omap_readw(OMAP_DMA_CDAC(lch)); + cdac = OMAP_DMA_CDAC_REG(lch); return cdac; } @@ -584,7 +585,7 @@ static u16 dma_src_len(struct omap_ep *ep, dma_addr_t start) } #define DMA_DEST_LAST(x) (cpu_is_omap15xx() \ - ? omap_readw(OMAP_DMA_CSAC(x)) /* really: CPC */ \ + ? OMAP_DMA_CSAC_REG(x) /* really: CPC */ \ : dma_cdac(x)) static u16 dma_dest_len(struct omap_ep *ep, dma_addr_t start) @@ -622,17 +623,19 @@ static void next_in_dma(struct omap_ep *ep, struct omap_req *req) || (cpu_is_omap15xx() && length < ep->maxpacket)) { txdma_ctrl = UDC_TXN_EOT | length; omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S8, - length, 1, sync_mode); + length, 1, sync_mode, 0, 0); } else { length = min(length / ep->maxpacket, (unsigned) UDC_TXN_TSC + 1); txdma_ctrl = length; omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, - ep->ep.maxpacket >> 1, length, sync_mode); + ep->ep.maxpacket >> 1, length, sync_mode, + 0, 0); length *= ep->maxpacket; } omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_EMIFF, - OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual); + OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, + 0, 0); omap_start_dma(ep->lch); ep->dma_counter = dma_csac(ep->lch); @@ -677,9 +680,11 @@ static void next_out_dma(struct omap_ep *ep, struct omap_req *req) req->dma_bytes = packets * ep->ep.maxpacket; omap_set_dma_transfer_params(ep->lch, OMAP_DMA_DATA_TYPE_S16, ep->ep.maxpacket >> 1, packets, - OMAP_DMA_SYNC_ELEMENT); + OMAP_DMA_SYNC_ELEMENT, + 0, 0); omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_EMIFF, - OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual); + OMAP_DMA_AMODE_POST_INC, req->req.dma + req->req.actual, + 0, 0); ep->dma_counter = DMA_DEST_LAST(ep->lch); UDC_RXDMA_REG(ep->dma_channel) = UDC_RXN_STOP | (packets - 1); @@ -822,7 +827,8 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) omap_set_dma_dest_params(ep->lch, OMAP_DMA_PORT_TIPB, OMAP_DMA_AMODE_CONSTANT, - (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG)); + (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG), + 0, 0); } } else { status = omap_request_dma(OMAP_DMA_USB_W2FC_RX0 - 1 + channel, @@ -833,7 +839,8 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) omap_set_dma_src_params(ep->lch, OMAP_DMA_PORT_TIPB, OMAP_DMA_AMODE_CONSTANT, - (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG)); + (unsigned long) io_v2p((u32)&UDC_DATA_DMA_REG), + 0, 0); /* EMIFF */ omap_set_dma_dest_burst_mode(ep->lch, OMAP_DMA_DATA_BURST_4); @@ -848,7 +855,7 @@ static void dma_channel_claim(struct omap_ep *ep, unsigned channel) /* channel type P: hw synch (fifo) */ if (!cpu_is_omap15xx()) - omap_writew(2, OMAP_DMA_LCH_CTRL(ep->lch)); + OMAP1_DMA_LCH_CTRL_REG(ep->lch) = 2; } just_restart: @@ -895,7 +902,7 @@ static void dma_channel_release(struct omap_ep *ep) else req = NULL; - active = ((1 << 7) & omap_readl(OMAP_DMA_CCR(ep->lch))) != 0; + active = ((1 << 7) & OMAP_DMA_CCR_REG(ep->lch)) != 0; DBG("%s release %s %cxdma%d %p\n", ep->ep.name, active ? "active" : "idle", @@ -1300,6 +1307,23 @@ static void pullup_disable(struct omap_udc *udc) UDC_SYSCON1_REG &= ~UDC_PULLUP_EN; } +static struct omap_udc *udc; + +static void omap_udc_enable_clock(int enable) +{ + if (udc == NULL || udc->dc_clk == NULL || udc->hhc_clk == NULL) + return; + + if (enable) { + clk_enable(udc->dc_clk); + clk_enable(udc->hhc_clk); + udelay(100); + } else { + clk_disable(udc->hhc_clk); + clk_disable(udc->dc_clk); + } +} + /* * Called by whatever detects VBUS sessions: external transceiver * driver, or maybe GPIO0 VBUS IRQ. May request 48 MHz clock. @@ -1320,10 +1344,22 @@ static int omap_vbus_session(struct usb_gadget *gadget, int is_active) else FUNC_MUX_CTRL_0_REG &= ~VBUS_CTRL_1510; } + if (udc->dc_clk != NULL && is_active) { + if (!udc->clk_requested) { + omap_udc_enable_clock(1); + udc->clk_requested = 1; + } + } if (can_pullup(udc)) pullup_enable(udc); else pullup_disable(udc); + if (udc->dc_clk != NULL && !is_active) { + if (udc->clk_requested) { + omap_udc_enable_clock(0); + udc->clk_requested = 0; + } + } spin_unlock_irqrestore(&udc->lock, flags); return 0; } @@ -1869,7 +1905,7 @@ static void pio_out_timer(unsigned long _ep) spin_lock_irqsave(&ep->udc->lock, flags); if (!list_empty(&ep->queue) && ep->ackwait) { - use_ep(ep, 0); + use_ep(ep, UDC_EP_SEL); stat_flg = UDC_STAT_FLG_REG; if ((stat_flg & UDC_ACK) && (!(stat_flg & UDC_FIFO_EN) @@ -1879,12 +1915,14 @@ static void pio_out_timer(unsigned long _ep) VDBG("%s: lose, %04x\n", ep->ep.name, stat_flg); req = container_of(ep->queue.next, struct omap_req, queue); - UDC_EP_NUM_REG = ep->bEndpointAddress | UDC_EP_SEL; (void) read_fifo(ep, req); UDC_EP_NUM_REG = ep->bEndpointAddress; UDC_CTRL_REG = UDC_SET_FIFO_EN; ep->ackwait = 1 + ep->double_buf; } + else { + deselect_ep(); + } } mod_timer(&ep->timer, PIO_OUT_TIMEOUT); spin_unlock_irqrestore(&ep->udc->lock, flags); @@ -2033,7 +2071,6 @@ omap_udc_iso_irq(int irq, void *_dev, struct pt_regs *r) /*-------------------------------------------------------------------------*/ -static struct omap_udc *udc; int usb_gadget_register_driver (struct usb_gadget_driver *driver) { @@ -2076,6 +2113,9 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) udc->gadget.dev.driver = &driver->driver; spin_unlock_irqrestore(&udc->lock, flags); + if (udc->dc_clk != NULL) + omap_udc_enable_clock(1); + status = driver->bind (&udc->gadget); if (status) { DBG("bind to %s --> %d\n", driver->driver.name, status); @@ -2111,6 +2151,8 @@ int usb_gadget_register_driver (struct usb_gadget_driver *driver) omap_vbus_session(&udc->gadget, 1); done: + if (udc->dc_clk != NULL) + omap_udc_enable_clock(0); return status; } EXPORT_SYMBOL(usb_gadget_register_driver); @@ -2125,6 +2167,9 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) if (!driver || driver != udc->driver) return -EINVAL; + if (udc->dc_clk != NULL) + omap_udc_enable_clock(1); + if (machine_is_omap_innovator() || machine_is_omap_osk()) omap_vbus_session(&udc->gadget, 0); @@ -2141,6 +2186,8 @@ int usb_gadget_unregister_driver (struct usb_gadget_driver *driver) udc->gadget.dev.driver = NULL; udc->driver = NULL; + if (udc->dc_clk != NULL) + omap_udc_enable_clock(0); DBG("unregistered driver '%s'\n", driver->driver.name); return status; } @@ -2714,6 +2761,8 @@ static int __init omap_udc_probe(struct platform_device *pdev) struct otg_transceiver *xceiv = NULL; const char *type = NULL; struct omap_usb_config *config = pdev->dev.platform_data; + struct clk *dc_clk; + struct clk *hhc_clk; /* NOTE: "knows" the order of the resources! */ if (!request_mem_region(pdev->resource[0].start, @@ -2723,6 +2772,16 @@ static int __init omap_udc_probe(struct platform_device *pdev) return -EBUSY; } + if (cpu_is_omap16xx()) { + dc_clk = clk_get(&pdev->dev, "usb_dc_ck"); + hhc_clk = clk_get(&pdev->dev, "usb_hhc_ck"); + BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk)); + /* can't use omap_udc_enable_clock yet */ + clk_enable(dc_clk); + clk_enable(hhc_clk); + udelay(100); + } + INFO("OMAP UDC rev %d.%d%s\n", UDC_REV_REG >> 4, UDC_REV_REG & 0xf, config->otg ? ", Mini-AB" : ""); @@ -2845,6 +2904,12 @@ bad_on_1710: goto cleanup3; } #endif + if (cpu_is_omap16xx()) { + udc->dc_clk = dc_clk; + udc->hhc_clk = hhc_clk; + clk_disable(hhc_clk); + clk_disable(dc_clk); + } create_proc_file(); device_add(&udc->gadget.dev); @@ -2865,8 +2930,17 @@ cleanup1: cleanup0: if (xceiv) put_device(xceiv->dev); + + if (cpu_is_omap16xx()) { + clk_disable(hhc_clk); + clk_disable(dc_clk); + clk_put(hhc_clk); + clk_put(dc_clk); + } + release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1); + return status; } @@ -2894,6 +2968,13 @@ static int __exit omap_udc_remove(struct platform_device *pdev) free_irq(pdev->resource[2].start, udc); free_irq(pdev->resource[1].start, udc); + if (udc->dc_clk) { + if (udc->clk_requested) + omap_udc_enable_clock(0); + clk_put(udc->hhc_clk); + clk_put(udc->dc_clk); + } + release_mem_region(pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start + 1); diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/omap_udc.h index 652ee462734..1dc398bb9ab 100644 --- a/drivers/usb/gadget/omap_udc.h +++ b/drivers/usb/gadget/omap_udc.h @@ -175,6 +175,9 @@ struct omap_udc { unsigned ep0_reset_config:1; unsigned ep0_setup:1; struct completion *done; + struct clk *dc_clk; + struct clk *hhc_clk; + unsigned clk_requested:1; }; /*-------------------------------------------------------------------------*/ diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c index 3785b3f7df1..d9207d86207 100644 --- a/drivers/usb/host/ohci-omap.c +++ b/drivers/usb/host/ohci-omap.c @@ -66,15 +66,20 @@ extern int usb_disabled(void); extern int ocpi_enable(void); static struct clk *usb_host_ck; +static struct clk *usb_dc_ck; +static int host_enabled; +static int host_initialized; static void omap_ohci_clock_power(int on) { if (on) { + clk_enable(usb_dc_ck); clk_enable(usb_host_ck); /* guesstimate for T5 == 1x 32K clock + APLL lock time */ udelay(100); } else { clk_disable(usb_host_ck); + clk_disable(usb_dc_ck); } } @@ -103,6 +108,7 @@ static int omap_ohci_transceiver_power(int on) return 0; } +#ifdef CONFIG_ARCH_OMAP15XX /* * OMAP-1510 specific Local Bus clock on/off */ @@ -152,6 +158,10 @@ static int omap_1510_local_bus_init(void) return 0; } +#else +#define omap_1510_local_bus_power(x) {} +#define omap_1510_local_bus_init() {} +#endif #ifdef CONFIG_USB_OTG @@ -275,6 +285,43 @@ void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *); /* always called with process context; sleeping is OK */ +int ohci_omap_host_enable(struct usb_bus *host, int enable) +{ + struct usb_hcd *hcd; + struct ohci_hcd *ohci; + int retval; + + if (host_enabled == enable) + return 0; + + host_enabled = enable; + + if (!host_initialized) + return 0; + + hcd = (struct usb_hcd *)host->hcpriv; + ohci = hcd_to_ohci(hcd); + if (enable) { + omap_ohci_clock_power(1); + if ((retval = ohci_init(ohci)) < 0) { + dev_err(hcd->self.controller, "init error %d\n", + retval); + return retval; + } + if ((retval = hcd->driver->start(hcd)) < 0) { + dev_err(hcd->self.controller, "startup error %d\n", + retval); + return retval; + } + } else { + usb_disconnect(&hcd->self.root_hub); + hcd->driver->stop(hcd); + omap_ohci_clock_power(0); + } + + return 0; +} + /** * usb_hcd_omap_probe - initialize OMAP-based HCDs * Context: !in_interrupt() @@ -306,6 +353,13 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, if (IS_ERR(usb_host_ck)) return PTR_ERR(usb_host_ck); + usb_dc_ck = clk_get(0, "usb_dc_ck"); + if (IS_ERR(usb_dc_ck)) { + clk_put(usb_host_ck); + return PTR_ERR(usb_dc_ck); + } + + hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id); if (!hcd) { retval = -ENOMEM; @@ -325,20 +379,32 @@ int usb_hcd_omap_probe (const struct hc_driver *driver, ohci = hcd_to_ohci(hcd); ohci_hcd_init(ohci); + host_initialized = 0; + host_enabled = 1; + retval = omap_start_hc(ohci, pdev); if (retval < 0) goto err2; retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), SA_INTERRUPT); - if (retval == 0) - return retval; + if (retval) + goto err3; + + host_initialized = 1; + + if (!host_enabled) + omap_ohci_clock_power(0); + + return 0; +err3: omap_stop_hc(pdev); err2: release_mem_region(hcd->rsrc_start, hcd->rsrc_len); err1: usb_put_hcd(hcd); err0: + clk_put(usb_dc_ck); clk_put(usb_host_ck); return retval; } @@ -364,18 +430,21 @@ void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev) omap_stop_hc(pdev); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); usb_put_hcd(hcd); + clk_put(usb_dc_ck); clk_put(usb_host_ck); } /*-------------------------------------------------------------------------*/ -static int __devinit +static int ohci_omap_start (struct usb_hcd *hcd) { struct omap_usb_config *config; struct ohci_hcd *ohci = hcd_to_ohci (hcd); int ret; + if (!host_enabled) + return 0; config = hcd->self.controller->platform_data; if (config->otg || config->rwc) writel(OHCI_CTRL_RWC, &ohci->regs->control); @@ -467,7 +536,7 @@ static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message) omap_ohci_clock_power(0); ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED; - dev->power.power_state = PMSG_SUSPEND; + dev->dev.power.power_state = PMSG_SUSPEND; return 0; } @@ -480,8 +549,8 @@ static int ohci_omap_resume(struct platform_device *dev) ohci->next_statechange = jiffies; omap_ohci_clock_power(1); - dev->power.power_state = PMSG_ON; - usb_hcd_resume_root_hub(dev_get_drvdata(dev)); + dev->dev.power.power_state = PMSG_ON; + usb_hcd_resume_root_hub(platform_get_drvdata(dev)); return 0; } diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index f5079c78ba4..47cc5020314 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig @@ -1426,6 +1426,8 @@ config FB_S3C2410_DEBUG Turn on debugging messages. Note that you can set/unset at run time through sysfs +source "drivers/video/omap/Kconfig" + config FB_VIRTUAL tristate "Virtual Frame Buffer support (ONLY FOR TESTING!)" depends on FB diff --git a/drivers/video/Makefile b/drivers/video/Makefile index aa434e725c0..4528b65b42e 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile @@ -99,6 +99,7 @@ obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o obj-$(CONFIG_FB_VESA) += vesafb.o obj-$(CONFIG_FB_VGA16) += vga16fb.o vgastate.o obj-$(CONFIG_FB_OF) += offb.o +obj-$(CONFIG_FB_OMAP) += omap/ cfbcopyarea.o cfbfillrect.o cfbimgblt.o # the test framebuffer is last obj-$(CONFIG_FB_VIRTUAL) += vfb.o diff --git a/drivers/video/omap/Kconfig b/drivers/video/omap/Kconfig new file mode 100644 index 00000000000..6411c95095c --- /dev/null +++ b/drivers/video/omap/Kconfig @@ -0,0 +1,55 @@ +config FB_OMAP + tristate "OMAP frame buffer support (EXPERIMENTAL)" + depends on FB + help + Frame buffer driver for OMAP based boards. + +config FB_OMAP_LCDC_EXTERNAL + bool "External LCD controller support" + depends on FB_OMAP + help + Say Y here, if you want to have support for boards with an + external LCD controller connected to the SoSSI/RFBI interface. + +config FB_OMAP_LCDC_HWA742 + bool "Epson HWA742 LCD controller support" + depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL + help + Say Y here if you want to have support for the external + Epson HWA742 LCD controller. + +config FB_OMAP_MANUAL_UPDATE + bool "Default to manual update mode" + depends on FB_OMAP && FB_OMAP_LCDC_EXTERNAL + help + Say Y here, if your user-space applications are capable of + notifying the frame buffer driver when a change has occured in + the frame buffer content and thus a reload of the image data to + the external frame buffer is required. If unsure, say N. + +config FB_OMAP_LCD_LPH8923 + bool "Philips LPH8923 LCD support" + depends on FB_OMAP + help + Say Y here if you want to have support for the Philips + LPH8923 LCD. + +config FB_OMAP_BOOTLOADER_INIT + bool "Check bootloader initializaion" + depends on FB_OMAP + help + Say Y here if you want to enable checking if the bootloader has + already initialized the display controller. In this case the + driver will skip the initialization. + +config FB_OMAP_DMA_TUNE + bool "Set DMA SDRAM access priority high" + depends on FB_OMAP && ARCH_OMAP1 + help + On systems in which video memory is in system memory + (SDRAM) this will speed up graphics DMA operations. + If you have such a system and want to use rotation + answer yes. Answer no if you have a dedicated video + memory, or don't use any of the accelerated features. + + diff --git a/drivers/video/omap/Makefile b/drivers/video/omap/Makefile new file mode 100644 index 00000000000..725ea29c671 --- /dev/null +++ b/drivers/video/omap/Makefile @@ -0,0 +1,30 @@ +# +# Makefile for the new OMAP framebuffer device driver +# + +obj-$(CONFIG_FB_OMAP) += omapfb.o + +objs-yy := omapfb_main.o + +objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o +objs-y$(CONFIG_ARCH_OMAP2) += dispc.o + +objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o +objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o + +objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o + +objs-y$(CONFIG_MACH_OMAP_H4) += lcd_h4.o +objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o +objs-y$(CONFIG_MACH_OMAP_H2) += lcd_h2.o +objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o +objs-$(CONFIG_ARCH_OMAP16XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1610.o +objs-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o +objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o +objs-y$(CONFIG_MACH_OMAP_PERSEUS2) += lcd_p2.o +objs-y$(CONFIG_MACH_OMAP_APOLLON) += lcd_apollon.o + +objs-y$(CONFIG_FB_OMAP_LCD_LPH8923) += lcd_lph8923.o + +omapfb-objs := $(objs-yy) + diff --git a/drivers/video/omap/debug.h b/drivers/video/omap/debug.h new file mode 100644 index 00000000000..a280412aeb9 --- /dev/null +++ b/drivers/video/omap/debug.h @@ -0,0 +1,44 @@ +/* + * File: drivers/video/omap_new/debug.c + * + * Debug support for the omapfb driver + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak + * + * 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 __OMAPFB_DEBUG_H +#define __OMAPFB_DEBUG_H + +#ifdef OMAPFB_DBG + +#define DBGPRINT(level, fmt, ...) if (level <= OMAPFB_DBG) do { \ + printk(KERN_DEBUG "%s: "fmt, \ + __FUNCTION__, ## __VA_ARGS__); \ + } while (0) +#define DBGENTER(level) DBGPRINT(level, "Enter\n") +#define DBGLEAVE(level) DBGPRINT(level, "Leave\n") + +#else /* OMAPFB_DBG */ + +#define DBGPRINT(level, format, ...) +#define DBGENTER(level) +#define DBGLEAVE(level) + +#endif /* OMAPFB_DBG */ + +#endif /* __OMAPFB_DEBUG_H */ diff --git a/drivers/video/omap/dispc.c b/drivers/video/omap/dispc.c new file mode 100644 index 00000000000..3185282887f --- /dev/null +++ b/drivers/video/omap/dispc.c @@ -0,0 +1,1190 @@ +/* + * File: drivers/video/omap/omap2/dispc.c + * + * OMAP2 display controller support + * + * Copyright (C) 2005 Nokia Corporation + * Author: Imre Deak + * + * 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 "dispc.h" + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +#define MODULE_NAME "omapfb-dispc" + +#define DISPC_BASE 0x48050400 + +/* DISPC common */ +#define DISPC_REVISION 0x0000 +#define DISPC_SYSCONFIG 0x0010 +#define DISPC_SYSSTATUS 0x0014 +#define DISPC_IRQSTATUS 0x0018 +#define DISPC_IRQENABLE 0x001C +#define DISPC_CONTROL 0x0040 +#define DISPC_CONFIG 0x0044 +#define DISPC_CAPABLE 0x0048 +#define DISPC_DEFAULT_COLOR0 0x004C +#define DISPC_DEFAULT_COLOR1 0x0050 +#define DISPC_TRANS_COLOR0 0x0054 +#define DISPC_TRANS_COLOR1 0x0058 +#define DISPC_LINE_STATUS 0x005C +#define DISPC_LINE_NUMBER 0x0060 +#define DISPC_TIMING_H 0x0064 +#define DISPC_TIMING_V 0x0068 +#define DISPC_POL_FREQ 0x006C +#define DISPC_DIVISOR 0x0070 +#define DISPC_SIZE_DIG 0x0078 +#define DISPC_SIZE_LCD 0x007C + +#define DISPC_DATA_CYCLE1 0x01D4 +#define DISPC_DATA_CYCLE2 0x01D8 +#define DISPC_DATA_CYCLE3 0x01DC + +/* DISPC GFX plane */ +#define DISPC_GFX_BA0 0x0080 +#define DISPC_GFX_BA1 0x0084 +#define DISPC_GFX_POSITION 0x0088 +#define DISPC_GFX_SIZE 0x008C +#define DISPC_GFX_ATTRIBUTES 0x00A0 +#define DISPC_GFX_FIFO_THRESHOLD 0x00A4 +#define DISPC_GFX_FIFO_SIZE_STATUS 0x00A8 +#define DISPC_GFX_ROW_INC 0x00AC +#define DISPC_GFX_PIXEL_INC 0x00B0 +#define DISPC_GFX_WINDOW_SKIP 0x00B4 +#define DISPC_GFX_TABLE_BA 0x00B8 + +/* DISPC Video plane 1/2 */ +#define DISPC_VID1_BASE 0x00BC +#define DISPC_VID2_BASE 0x014C + +/* Offsets into DISPC_VID1/2_BASE */ +#define DISPC_VID_BA0 0x0000 +#define DISPC_VID_BA1 0x0004 +#define DISPC_VID_POSITION 0x0008 +#define DISPC_VID_SIZE 0x000C +#define DISPC_VID_ATTRIBUTES 0x0010 +#define DISPC_VID_FIFO_THRESHOLD 0x0014 +#define DISPC_VID_FIFO_SIZE_STATUS 0x0018 +#define DISPC_VID_ROW_INC 0x001C +#define DISPC_VID_PIXEL_INC 0x0020 +#define DISPC_VID_FIR 0x0024 +#define DISPC_VID_PICTURE_SIZE 0x0028 +#define DISPC_VID_ACCU0 0x002C +#define DISPC_VID_ACCU1 0x0030 + +/* 8 elements in 8 byte increments */ +#define DISPC_VID_FIR_COEF_H0 0x0034 +/* 8 elements in 8 byte increments */ +#define DISPC_VID_FIR_COEF_HV0 0x0038 +/* 5 elements in 4 byte increments */ +#define DISPC_VID_CONV_COEF0 0x0074 + +#define DISPC_IRQ_FRAMEMASK 0x0001 +#define DISPC_IRQ_VSYNC 0x0002 +#define DISPC_IRQ_EVSYNC_EVEN 0x0004 +#define DISPC_IRQ_EVSYNC_ODD 0x0008 +#define DISPC_IRQ_ACBIAS_COUNT_STAT 0x0010 +#define DISPC_IRQ_PROG_LINE_NUM 0x0020 +#define DISPC_IRQ_GFX_FIFO_UNDERFLOW 0x0040 +#define DISPC_IRQ_GFX_END_WIN 0x0080 +#define DISPC_IRQ_PAL_GAMMA_MASK 0x0100 +#define DISPC_IRQ_OCP_ERR 0x0200 +#define DISPC_IRQ_VID1_FIFO_UNDERFLOW 0x0400 +#define DISPC_IRQ_VID1_END_WIN 0x0800 +#define DISPC_IRQ_VID2_FIFO_UNDERFLOW 0x1000 +#define DISPC_IRQ_VID2_END_WIN 0x2000 +#define DISPC_IRQ_SYNC_LOST 0x4000 + +#define DISPC_IRQ_MASK_ALL 0x7fff + +#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ + DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ + DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ + DISPC_IRQ_SYNC_LOST) + +#define RFBI_CONTROL 0x48050040 + +#define MAX_PALETTE_SIZE (256 * 16) + +#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) + +#define FLD_MASK(pos, len) (((1 << len) - 1) << pos) + +#define MOD_REG_FLD(reg, mask, val) \ + dispc_write_reg((reg), (dispc_read_reg(reg) & ~(mask)) | (val)); + +static struct { + u32 base; + + dma_addr_t fb_sram_paddr; + u32 fb_sram_size; + int fb_sram_lines; + + dma_addr_t fb_sdram_paddr; + void *fb_sdram_vaddr; + u32 fb_sdram_size; + int fb_sdram_lines; + + dma_addr_t palette_paddr; + void *palette_vaddr; + + void *fb_kern_vaddr; + + int multiplane_head; + + int ext_mode; + int fbmem_allocated; + + unsigned long enabled_irqs; + void (*irq_callback)(void *); + void *irq_callback_data; + struct completion frame_done; + + struct clk *dss_ick, *dss1_fck; + struct clk *dss_54m_fck; + + enum omapfb_update_mode update_mode; + struct omapfb_device *fbdev; +} dispc; + +static void inline dispc_write_reg(int idx, u32 val) +{ + __raw_writel(val, dispc.base + idx); +} + +static u32 inline dispc_read_reg(int idx) +{ + u32 l = __raw_readl(dispc.base + idx); + return l; +} + +/* Select RFBI or bypass mode */ +static void enable_rfbi_mode(int enable) +{ + u32 l; + + l = dispc_read_reg(DISPC_CONTROL); + /* Enable RFBI, GPIO0/1 */ + l &= ~((1 << 11) | (1 << 15) | (1 << 16)); + l |= enable ? (1 << 11) : 0; + /* RFBI En: GPIO0/1=10 RFBI Dis: GPIO0/1=11 */ + l |= 1 << 15; + l |= enable ? 0 : (1 << 16); + dispc_write_reg(DISPC_CONTROL, l); + + /* Set bypass mode in RFBI module */ + l = __raw_readl(io_p2v(RFBI_CONTROL)); + l |= enable ? 0 : (1 << 1); + __raw_writel(l, io_p2v(RFBI_CONTROL)); +} + +static void set_lcd_data_lines(int data_lines) +{ + u32 l; + int code = 0; + + switch (data_lines) { + case 12: + code = 0; + break; + case 16: + code = 1; + break; + case 18: + code = 2; + break; + case 24: + code = 3; + break; + default: + BUG(); + } + + l = dispc_read_reg(DISPC_CONTROL); + l &= ~(0x03 << 8); + l |= code << 8; + dispc_write_reg(DISPC_CONTROL, l); +} + +static void set_load_mode(int mode) +{ + BUG_ON(mode & ~(DISPC_LOAD_CLUT_ONLY | DISPC_LOAD_FRAME_ONLY | + DISPC_LOAD_CLUT_ONCE_FRAME)); + MOD_REG_FLD(DISPC_CONFIG, 0x03 << 1, mode << 1); +} + +void omap_dispc_set_lcd_size(int x, int y) +{ + BUG_ON((x > (1 << 11)) || (y > (1 << 11))); + MOD_REG_FLD(DISPC_SIZE_LCD, FLD_MASK(16, 11) | FLD_MASK(0, 11), + ((y - 1) << 16) | (x - 1)); +} +EXPORT_SYMBOL(omap_dispc_set_lcd_size); + +void omap_dispc_set_digit_size(int x, int y) +{ + BUG_ON((x > (1 << 11)) || (y > (1 << 11))); + MOD_REG_FLD(DISPC_SIZE_DIG, FLD_MASK(16, 11) | FLD_MASK(0, 11), + ((y - 1) << 16) | (x - 1)); +} +EXPORT_SYMBOL(omap_dispc_set_digit_size); + +static void setup_plane_fifo(int plane) +{ + const u32 ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, + DISPC_VID1_BASE + DISPC_VID_FIFO_THRESHOLD, + DISPC_VID2_BASE + DISPC_VID_FIFO_THRESHOLD }; + const u32 fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS, + DISPC_VID1_BASE + DISPC_VID_FIFO_SIZE_STATUS, + DISPC_VID2_BASE + DISPC_VID_FIFO_SIZE_STATUS }; + + u32 l; + + BUG_ON(plane > 2); + + l = dispc_read_reg(fsz_reg[plane]); + l &= FLD_MASK(0, 9); + /* HIGH=3/4 LOW=1/4 */ + MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 9) | FLD_MASK(0, 9), + ((l * 3 / 4) << 16) | (l / 4)); +} + +void omap_dispc_enable_lcd_out(int enable) +{ + MOD_REG_FLD(DISPC_CONTROL, 1, enable ? 1 : 0); +} +EXPORT_SYMBOL(omap_dispc_enable_lcd_out); + +void omap_dispc_enable_digit_out(int enable) +{ + MOD_REG_FLD(DISPC_CONTROL, 1 << 1, enable ? 1 << 1 : 0); +} +EXPORT_SYMBOL(omap_dispc_enable_digit_out); + +static inline int _setup_plane(int plane, int channel_out, + u32 paddr, int screen_width, + int pos_x, int pos_y, int width, int height, + int color_mode) +{ + const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES, + DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES, + DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES }; + const u32 ba_reg[] = { DISPC_GFX_BA0, DISPC_VID1_BASE + DISPC_VID_BA0, + DISPC_VID2_BASE + DISPC_VID_BA0 }; + const u32 ps_reg[] = { DISPC_GFX_POSITION, + DISPC_VID1_BASE + DISPC_VID_POSITION, + DISPC_VID2_BASE + DISPC_VID_POSITION }; + const u32 sz_reg[] = { DISPC_GFX_SIZE, DISPC_VID1_BASE + DISPC_VID_SIZE, + DISPC_VID2_BASE + DISPC_VID_SIZE }; + const u32 ri_reg[] = { DISPC_GFX_ROW_INC, + DISPC_VID1_BASE + DISPC_VID_ROW_INC, + DISPC_VID2_BASE + DISPC_VID_ROW_INC }; + int chout_shift, burst_shift; + int chout_val; + int color_code; + int bpp; + u32 l; + + DBGPRINT(2, "plane %d channel %d paddr %u scr_width %d pos_x %d pos_y %d " + "width %d height %d color_mode %d\n", + plane, channel_out, paddr, screen_width, pos_x, pos_y, + width, height, color_mode); + + switch (plane) { + case OMAPFB_PLANE_GFX: + burst_shift = 6; + chout_shift = 8; + break; + case OMAPFB_PLANE_VID1: + case OMAPFB_PLANE_VID2: + burst_shift = 14; + chout_shift = 16; + break; + default: + return -EINVAL; + } + + switch (channel_out) { + case OMAPFB_CHANNEL_OUT_LCD: + chout_val = 0; + break; + case OMAPFB_CHANNEL_OUT_DIGIT: + chout_val = 1; + break; + default: + return -EINVAL; + } + + switch (color_mode) { + case OMAPFB_COLOR_RGB565: + color_code = DISPC_RGB_16_BPP; + bpp = 16; + break; + case OMAPFB_COLOR_YUV422: + if (plane != 0) + return -EINVAL; + color_code = DISPC_UYVY_422; + bpp = 16; + break; + case OMAPFB_COLOR_YUV420: + if (plane != 0) + return -EINVAL; + color_code = DISPC_YUV2_422; + bpp = 12; + break; + default: + return -EINVAL; + } + + l = dispc_read_reg(at_reg[plane]); + + l &= ~(0x0f << 1); + l |= color_code << 1; + + l &= ~(0x03 << burst_shift); + l |= DISPC_BURST_8x32 << burst_shift; + + l &= ~(1 << chout_shift); + l |= chout_val << chout_shift; + + dispc_write_reg(at_reg[plane], l); + + dispc_write_reg(ba_reg[plane], paddr); + MOD_REG_FLD(ps_reg[plane], + FLD_MASK(16, 11) | FLD_MASK(0, 11), (pos_y << 16) | pos_x); + + MOD_REG_FLD(sz_reg[plane], FLD_MASK(16, 11) | FLD_MASK(0, 11), + ((height - 1) << 16) | (width - 1)); + + dispc_write_reg(ri_reg[plane], (screen_width - width) * bpp / 8 + 1); + + return height * screen_width * bpp / 8; +} + +static int omap_dispc_setup_plane(int plane, int channel_out, + unsigned long offset, int screen_width, + int pos_x, int pos_y, int width, int height, + int color_mode) +{ + int yspan; + u32 paddr; + int mp_head = -1; + int r; + + if (offset > dispc.fb_sram_size + dispc.fb_sdram_size) + return -EINVAL; + + if (offset < dispc.fb_sram_size) { + paddr = dispc.fb_sram_paddr + offset; + yspan = min_t(int, height, dispc.fb_sram_lines); + if (yspan) { + if ((r = _setup_plane(plane, channel_out, paddr, + screen_width, pos_x, pos_y, + width, height, color_mode)) < 0) + return r; + offset += r; + height -= yspan; + pos_y += yspan; + mp_head = plane; + if (++plane > 2) + plane = OMAPFB_PLANE_GFX; + } + } + if (height) { + offset -= dispc.fb_sram_size; + paddr = dispc.fb_sdram_paddr + offset; + yspan = min_t(int, height, dispc.fb_sdram_lines); + if (yspan) { + if ((r = _setup_plane(plane, channel_out, paddr, + screen_width, pos_x, pos_y, + width, height, color_mode)) < 0) + return r; + if (mp_head != -1) + dispc.multiplane_head = mp_head; + } + } + + return 0; +} + +static int omap_dispc_enable_plane(int plane, int enable) +{ + const u32 at_reg[] = { DISPC_GFX_ATTRIBUTES, + DISPC_VID1_BASE + DISPC_VID_ATTRIBUTES, + DISPC_VID2_BASE + DISPC_VID_ATTRIBUTES }; + DBGENTER(2); + + if ((unsigned int)plane > 2) + return -EINVAL; + MOD_REG_FLD(at_reg[plane], 1, enable ? 1 : 0); + if (plane == dispc.multiplane_head) { + if (++plane > 2) + plane = OMAPFB_PLANE_GFX; + MOD_REG_FLD(at_reg[plane], 1, enable ? 1 : 0); + } + + return 0; +} + +static int omap_dispc_set_color_key(struct omapfb_color_key *ck) +{ + u32 df_reg, tr_reg; + int shift, val; + + switch (ck->channel_out) { + case OMAPFB_CHANNEL_OUT_LCD: + df_reg = DISPC_DEFAULT_COLOR0; + tr_reg = DISPC_TRANS_COLOR0; + shift = 10; + break; + case OMAPFB_CHANNEL_OUT_DIGIT: + df_reg = DISPC_DEFAULT_COLOR1; + tr_reg = DISPC_TRANS_COLOR1; + shift = 12; + break; + default: + return -EINVAL; + } + switch (ck->key_type) { + case OMAPFB_COLOR_KEY_DISABLED: + val = 0; + break; + case OMAPFB_COLOR_KEY_GFX_DST: + val = 1; + break; + case OMAPFB_COLOR_KEY_VID_SRC: + val = 3; + break; + default: + return -EINVAL; + } + MOD_REG_FLD(DISPC_CONFIG, FLD_MASK(shift, 2), val << shift); + + if (val != 0) + dispc_write_reg(tr_reg, ck->trans_key); + dispc_write_reg(df_reg, ck->background); + + return 0; +} + +static void load_palette(void) +{ +} + +static int omap_dispc_set_update_mode(enum omapfb_update_mode mode) +{ + int r = 0; + + DBGENTER(1); + + if (mode != dispc.update_mode) { + switch (mode) { + case OMAPFB_AUTO_UPDATE: + case OMAPFB_MANUAL_UPDATE: + omap_dispc_enable_lcd_out(1); + dispc.update_mode = mode; + break; + case OMAPFB_UPDATE_DISABLED: + init_completion(&dispc.frame_done); + omap_dispc_enable_lcd_out(0); + if (!wait_for_completion_timeout(&dispc.frame_done, + msecs_to_jiffies(500))) { + pr_err("timeout waiting for FRAME DONE\n"); + } + dispc.update_mode = mode; + break; + default: + r = -EINVAL; + } + } + + DBGLEAVE(1); + + return r; +} + +static enum omapfb_update_mode omap_dispc_get_update_mode(void) +{ + return dispc.update_mode; +} + +static void calc_ck_div(int is_tft, int pck, int *lck_div, int *pck_div) +{ + unsigned long fck, lck; + + *lck_div = 1; + pck = max(1, pck); + fck = clk_get_rate(dispc.dss1_fck); + lck = fck; + *pck_div = (lck + pck - 1) / pck; + if (is_tft) + *pck_div = max(2, *pck_div); + else + *pck_div = max(3, *pck_div); + if (*pck_div > 255) { + *pck_div = 255; + lck = pck * *pck_div; + *lck_div = fck / lck; + BUG_ON(*lck_div < 1); + if (*lck_div > 255) { + *lck_div = 255; + printk(KERN_WARNING + MODULE_NAME ": pixclock %d kHz too low.\n", + pck / 1000); + } + } +} + +static void set_lcd_tft_mode(int enable) +{ + u32 mask; + + mask = 1 << 3; + MOD_REG_FLD(DISPC_CONTROL, mask, enable ? mask : 0); +} + +static void set_lcd_timings(void) +{ + u32 l; + int lck_div, pck_div; + struct lcd_panel *panel = dispc.fbdev->panel; + int is_tft = panel->config & OMAP_LCDC_PANEL_TFT; + unsigned long fck; + + DBGENTER(1); + + l = dispc_read_reg(DISPC_TIMING_H); + l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8)); + l |= ( max(1, (min(64, panel->hsw))) - 1 ) << 0; + l |= ( max(1, (min(256, panel->hfp))) - 1 ) << 8; + l |= ( max(1, (min(256, panel->hbp))) - 1 ) << 20; + dispc_write_reg(DISPC_TIMING_H, l); + + l = dispc_read_reg(DISPC_TIMING_V); + l &= ~(FLD_MASK(0, 6) | FLD_MASK(8, 8) | FLD_MASK(20, 8)); + l |= ( max(1, (min(64, panel->vsw))) - 1 ) << 0; + l |= ( max(0, (min(255, panel->vfp))) - 0 ) << 8; + l |= ( max(0, (min(255, panel->vbp))) - 0 ) << 20; + dispc_write_reg(DISPC_TIMING_V, l); + + l = dispc_read_reg(DISPC_POL_FREQ); + l &= ~FLD_MASK(12, 6); + l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 12; + l |= panel->acb & 0xff; + dispc_write_reg(DISPC_POL_FREQ, l); + + calc_ck_div(is_tft, panel->pixel_clock * 1000, &lck_div, &pck_div); + + l = dispc_read_reg(DISPC_DIVISOR); + l &= ~(FLD_MASK(16, 8) | FLD_MASK(0, 8)); + l |= (lck_div << 16) | (pck_div << 0); + dispc_write_reg(DISPC_DIVISOR, l); + + /* update panel info with the exact clock */ + fck = clk_get_rate(dispc.dss1_fck); + panel->pixel_clock = fck / lck_div / pck_div / 1000; +} + +int omap_dispc_request_irq(void (*callback)(void *data), void *data) +{ + int r = 0; + + BUG_ON(callback == NULL); + + if (dispc.irq_callback) + r = -EBUSY; + else { + dispc.irq_callback = callback; + dispc.irq_callback_data = data; + } + + return r; +} +EXPORT_SYMBOL(omap_dispc_request_irq); + +void omap_dispc_enable_irqs(int irq_mask) +{ + dispc.enabled_irqs = irq_mask; + irq_mask |= DISPC_IRQ_MASK_ERROR; + MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask); +} +EXPORT_SYMBOL(omap_dispc_enable_irqs); + +void omap_dispc_disable_irqs(int irq_mask) +{ + dispc.enabled_irqs &= ~irq_mask; + irq_mask &= ~DISPC_IRQ_MASK_ERROR; + MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask); +} +EXPORT_SYMBOL(omap_dispc_disable_irqs); + +void omap_dispc_free_irq(void) +{ + omap_dispc_disable_irqs(DISPC_IRQ_MASK_ALL); + dispc.irq_callback = NULL; + dispc.irq_callback_data = NULL; +} +EXPORT_SYMBOL(omap_dispc_free_irq); + +static irqreturn_t omap_dispc_irq_handler(int irq, void *dev, struct pt_regs *regs) +{ + u32 stat = dispc_read_reg(DISPC_IRQSTATUS); + static int jabber; + + DBGENTER(2); + + if (stat & DISPC_IRQ_FRAMEMASK) + complete(&dispc.frame_done); + + if (stat & DISPC_IRQ_MASK_ERROR) { + if (jabber++ < 5) { + pr_err("irq error status %04x\n", stat & 0x7fff); + } else { + pr_err("disable irq\n"); + dispc_write_reg(DISPC_IRQENABLE, 0); + } + } + + if ((stat & dispc.enabled_irqs) && dispc.irq_callback) + dispc.irq_callback(dispc.irq_callback_data); + + dispc_write_reg(DISPC_IRQSTATUS, stat); + + return IRQ_HANDLED; +} + +static int get_dss_clocks(void) +{ + if (IS_ERR((dispc.dss_ick = clk_get(dispc.fbdev->dev, "dss_ick")))) { + pr_err("can't get dss_ick"); + return PTR_ERR(dispc.dss_ick); + } + + if (IS_ERR((dispc.dss1_fck = clk_get(dispc.fbdev->dev, "dss1_fck")))) { + pr_err("can't get dss1_fck"); + clk_put(dispc.dss_ick); + return PTR_ERR(dispc.dss1_fck); + } + + if (IS_ERR((dispc.dss_54m_fck = + clk_get(dispc.fbdev->dev, "dss_54m_fck")))) { + pr_err("can't get dss_54m_fck"); + clk_put(dispc.dss_ick); + clk_put(dispc.dss1_fck); + return PTR_ERR(dispc.dss_54m_fck); + } + + return 0; +} + +static void put_dss_clocks(void) +{ + clk_put(dispc.dss_54m_fck); + clk_put(dispc.dss1_fck); + clk_put(dispc.dss_ick); +} + +static void enable_lcd_clocks(int enable) +{ + if (enable) { + clk_enable(dispc.dss_ick); + clk_enable(dispc.dss1_fck); + } else { + clk_disable(dispc.dss1_fck); + clk_disable(dispc.dss_ick); + } +} + +static void enable_digit_clocks(int enable) +{ + if (enable) + clk_enable(dispc.dss_54m_fck); + else + clk_disable(dispc.dss_54m_fck); +} + +static void omap_dispc_suspend(void) +{ + DBGENTER(1); + + if (dispc.update_mode == OMAPFB_AUTO_UPDATE) { + init_completion(&dispc.frame_done); + omap_dispc_enable_lcd_out(0); + if (!wait_for_completion_timeout(&dispc.frame_done, + msecs_to_jiffies(500))) { + pr_err("timeout waiting for FRAME DONE\n"); + } + enable_lcd_clocks(0); + } + + DBGLEAVE(1); +} + +static void omap_dispc_resume(void) +{ + DBGENTER(1); + + if (dispc.update_mode == OMAPFB_AUTO_UPDATE) { + enable_lcd_clocks(1); + set_lcd_timings(); + load_palette(); + omap_dispc_enable_lcd_out(1); + } + + DBGLEAVE(1); +} + + +static int omap_dispc_update_window(struct omapfb_update_window *win, + void (*complete_callback)(void *arg), + void *complete_callback_data) +{ + return dispc.update_mode == OMAPFB_UPDATE_DISABLED ? -ENODEV : 0; +} + +/* Greatest common divisor */ +static int calc_gcd(int a, int b) +{ + int tmp; + + if (a < b) { + tmp = a; + a = b; + b = tmp; + } + for (;;) { + tmp = a % b; + if (tmp != 0) { + a = b; + b = tmp; + } else + break; + } + + return b; +} + +/* Least common multiple */ +static int calc_lcm(int a, int b) +{ + return a * b / calc_gcd(a, b); +} + +static void omap_dispc_get_vram_layout(unsigned long *size, void **virt, + dma_addr_t *phys) +{ + *size = dispc.fb_sram_size + dispc.fb_sdram_size; + *virt = dispc.fb_kern_vaddr; + /* Physical vram might not be contiguous. No one except own mmap + * should use this addr. + */ + *phys = 0; +} + +static int omap_dispc_mmap_user(struct vm_area_struct *vma) +{ + unsigned long len; + unsigned long offset; + unsigned long vaddr; + int r; + + DBGPRINT(1, "vm_pgoff 0x%08lx vm_start 0x%08lx vm_end 0x%08lx\n", + vma->vm_pgoff, vma->vm_start, vma->vm_end); + + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) + return -EINVAL; + offset = vma->vm_pgoff << PAGE_SHIFT; + + if (offset >= dispc.fb_sram_size + dispc.fb_sdram_size) + return -EINVAL; + + len = vma->vm_end - vma->vm_start; + if (offset + len > dispc.fb_sram_size + dispc.fb_sdram_size) + return -EINVAL; + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + vma->vm_flags |= VM_PFNMAP; + + vaddr = vma->vm_start; + + if (dispc.fb_sram_size) { + if (offset < dispc.fb_sram_size) { + int chunk = min_t(int, dispc.fb_sram_size - offset, len); + DBGPRINT(1, "map sram va %08lx pa %08lx size %d\n", + vaddr, dispc.fb_sram_paddr + offset, chunk); + r = io_remap_pfn_range(vma, vaddr, + (dispc.fb_sram_paddr + + offset) >> PAGE_SHIFT, chunk, + vma->vm_page_prot); + if (r == -EINVAL) + return r; + if (r < 0) + return -EAGAIN; + + vaddr += chunk; + len -= chunk; + offset = 0; + } else + offset -= dispc.fb_sram_size; + } + + if (len) { + DBGPRINT(1, "map sdram va %08lx pa %08lx size %ld\n", + vaddr, dispc.fb_sdram_paddr + offset, len); + BUG_ON(offset > dispc.fb_sdram_size || + offset + len > dispc.fb_sdram_size || + vma->vm_end - vaddr != len); + r = io_remap_pfn_range(vma, vaddr, (dispc.fb_sdram_paddr + + offset) >> PAGE_SHIFT, len, + vma->vm_page_prot); + /* no teardown of the previous mapping here. + * do_mmap_pgoff will take core of that. */ + if (r == -EINVAL) + return r; + if (r < 0) + return -EAGAIN; + } + + return 0; +} + +static int mmap_kern(void) +{ + struct vm_struct *kvma; + struct vm_area_struct vma; + pgprot_t pgprot; + unsigned long vaddr; + + DBGENTER(1); + + kvma = get_vm_area(dispc.fb_sram_size + dispc.fb_sdram_size, VM_IOREMAP); + if (kvma == NULL) { + pr_err("can't get kernel vm area\n"); + return -ENOMEM; + } + vma.vm_mm = &init_mm; + + dispc.fb_kern_vaddr = kvma->addr; + vaddr = (unsigned long)kvma->addr; + + pgprot = pgprot_writecombine(pgprot_kernel); + if (dispc.fb_sram_size) { + vma.vm_start = vaddr; + vma.vm_end = vaddr + dispc.fb_sram_size; + if (io_remap_pfn_range(&vma, vaddr, + dispc.fb_sram_paddr >> PAGE_SHIFT, + dispc.fb_sram_size, pgprot) < 0) { + pr_err("kernel mmap for FBMEM failed\n"); + return -EAGAIN; + } + vaddr += dispc.fb_sram_size; + } + if (dispc.fb_sdram_size) { + vma.vm_start = vaddr; + vma.vm_end = vaddr + dispc.fb_sdram_size; + if (io_remap_pfn_range(&vma, vaddr, + dispc.fb_sdram_paddr >> PAGE_SHIFT, + dispc.fb_sdram_size, pgprot) < 0) { + pr_err("kernel mmap for FBMEM failed\n"); + return -EAGAIN; + } + } + + DBGLEAVE(1); + + return 0; +} + +static void unmap_kern(void) +{ + vunmap(dispc.fb_kern_vaddr); +} + +static int alloc_palette_ram(void) +{ + dispc.palette_vaddr = dma_alloc_writecombine(dispc.fbdev->dev, + MAX_PALETTE_SIZE, &dispc.palette_paddr, GFP_KERNEL); + if (dispc.palette_vaddr == NULL) { + pr_err("failed to alloc palette memory\n"); + return -ENOMEM; + } + + return 0; +} + +static void free_palette_ram(void) +{ + dma_free_writecombine(dispc.fbdev->dev, MAX_PALETTE_SIZE, + dispc.palette_vaddr, dispc.palette_paddr); +} + +static int alloc_fbmem(int req_size) +{ + int frame_size; + struct lcd_panel *panel = dispc.fbdev->panel; + + frame_size = PAGE_ALIGN(panel->x_res * panel->bpp / 8 * panel->y_res); + if (req_size > frame_size) + frame_size = req_size; + dispc.fb_sdram_size = PAGE_ALIGN(MAX_PALETTE_SIZE) + frame_size; + dispc.fb_kern_vaddr = dma_alloc_writecombine(dispc.fbdev->dev, + dispc.fb_sdram_size, &dispc.fb_sdram_paddr, GFP_KERNEL); + + if (dispc.fb_kern_vaddr == 0) { + pr_err("unable to allocate fb DMA memory\n"); + return -ENOMEM; + } + + return 0; +} + +static void free_fbmem(void) +{ + dma_free_writecombine(dispc.fbdev->dev, dispc.fb_sdram_size, + dispc.fb_kern_vaddr, dispc.fb_sdram_paddr); +} + +static int setup_fbmem(int req_size) +{ + struct lcd_panel *panel = dispc.fbdev->panel; + struct omapfb_platform_data *conf; + unsigned long size_align; + int line_size; + int frame_size; + int lines; + int r; + + conf = dispc.fbdev->dev->platform_data; + + if (conf->fbmem.fb_sram_size + conf->fbmem.fb_sdram_size == 0) { + if ((r = alloc_fbmem(req_size)) < 0) + return r; + dispc.fbmem_allocated = 1; + dispc.fb_sdram_lines = panel->y_res; + return 0; + } + + dispc.fb_sram_paddr = conf->fbmem.fb_sram_start; + dispc.fb_sram_size = conf->fbmem.fb_sram_size; + dispc.fb_sdram_paddr = conf->fbmem.fb_sdram_start; + dispc.fb_sdram_size = conf->fbmem.fb_sdram_size; + + line_size = panel->x_res * panel->bpp / 8; + frame_size = PAGE_ALIGN(line_size * panel->y_res); + + size_align = calc_lcm(line_size, PAGE_SIZE); + + if (dispc.fb_sram_size + dispc.fb_sdram_size < frame_size || + (dispc.fb_sdram_size && (dispc.fb_sram_size % size_align))) { + pr_err("Invalid FB memory configuration\n"); + return -EINVAL; + } + + if (dispc.fb_sram_size + dispc.fb_sdram_size < req_size) { + pr_err("%d vram was requested, but only %u is available\n", + req_size, dispc.fb_sram_size + dispc.fb_sdram_size); + } + + lines = dispc.fb_sram_size / line_size; + lines = min_t(int, panel->y_res, lines); + dispc.fb_sram_lines = lines; + lines = panel->y_res - lines; + dispc.fb_sdram_lines = lines; + + if ((r = mmap_kern()) < 0) + return r; + + DBGPRINT(1, "fb_sram %08x size %08x fb_sdram %08x size %08x\n", + dispc.fb_sram_paddr, dispc.fb_sram_size, + dispc.fb_sdram_paddr, dispc.fb_sdram_size); + + return 0; +} + +static void cleanup_fbmem(void) +{ + if (dispc.fbmem_allocated) + free_fbmem(); + else + unmap_kern(); +} + +static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode, + int req_vram_size) +{ + int r; + u32 l; + struct lcd_panel *panel = fbdev->panel; + int tmo = 10000; + int skip_init = 0; + + DBGENTER(1); + + memset(&dispc, 0, sizeof(dispc)); + + dispc.base = io_p2v(DISPC_BASE); + dispc.fbdev = fbdev; + dispc.ext_mode = ext_mode; + + dispc.multiplane_head = -1; + + if (fbdev->dev->platform_data == NULL) { + pr_err("missing FB configuration\n"); + return -ENOENT; + } + + init_completion(&dispc.frame_done); + + if ((r = get_dss_clocks()) < 0) + return r; + + enable_lcd_clocks(1); + + l = dispc_read_reg(DISPC_REVISION); + pr_info(MODULE_NAME ": version %d.%d\n", l >> 4 & 0x0f, l & 0x0f); + +#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT + l = dispc_read_reg(DISPC_CONTROL); + /* LCD enabled ? */ + if (l & 1) { + pr_info(MODULE_NAME ": skipping hardware initialization\n"); + skip_init = 1; + } +#endif + + if (!skip_init) { + /* Reset monitoring works only w/ the 54M clk */ + enable_digit_clocks(1); + + /* Soft reset */ + MOD_REG_FLD(DISPC_SYSCONFIG, 1 << 1, 1 << 1); + + while (!(dispc_read_reg(DISPC_SYSSTATUS) & 1)) { + if (!--tmo) { + pr_err("soft reset failed\n"); + r = -ENODEV; + enable_digit_clocks(0); + goto fail1; + } + } + + enable_digit_clocks(0); + } + + l = dispc_read_reg(DISPC_IRQSTATUS); + dispc_write_reg(l, DISPC_IRQSTATUS); + + /* Enable those that we handle always */ + omap_dispc_enable_irqs(DISPC_IRQ_FRAMEMASK); + + if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler, + 0, MODULE_NAME, NULL)) < 0) { + pr_err("can't get DSS IRQ\n"); + goto fail1; + } + + /* L3 firewall setting: enable access to OCM RAM */ + __raw_writel(0x402000b0, io_p2v(0x680050a0)); + + if ((r = alloc_palette_ram()) < 0) + goto fail2; + + if ((r = setup_fbmem(req_vram_size)) < 0) + goto fail3; + + if (!skip_init) { + memset(dispc.fb_kern_vaddr, 0, + dispc.fb_sram_size + dispc.fb_sdram_size); + + /* Set logic clock to fck, pixel clock to fck/2 for now */ + MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(16, 8), 1 << 16); + MOD_REG_FLD(DISPC_DIVISOR, FLD_MASK(0, 8), 2 << 0); + + setup_plane_fifo(0); + setup_plane_fifo(1); + setup_plane_fifo(2); + + set_lcd_tft_mode(panel->config & OMAP_LCDC_PANEL_TFT); + set_lcd_data_lines(panel->data_lines); + set_load_mode(DISPC_LOAD_FRAME_ONLY); + + if (!ext_mode) { + omap_dispc_set_lcd_size(panel->x_res, panel->y_res); + set_lcd_timings(); + } + enable_rfbi_mode(ext_mode); + } + + return 0; +fail3: + free_palette_ram(); +fail2: + free_irq(INT_24XX_DSS_IRQ, NULL); +fail1: + enable_lcd_clocks(0); + put_dss_clocks(); + + return r; +} + +static void omap_dispc_cleanup(void) +{ + cleanup_fbmem(); + free_palette_ram(); + free_irq(INT_24XX_DSS_IRQ, NULL); + enable_lcd_clocks(0); + put_dss_clocks(); +} + +static unsigned long omap_dispc_get_caps(void) +{ + return 0; +} + +struct lcd_ctrl omap2_int_ctrl = { + .name = "internal", + .init = omap_dispc_init, + .cleanup = omap_dispc_cleanup, + .get_vram_layout = omap_dispc_get_vram_layout, + .mmap = omap_dispc_mmap_user, + .get_caps = omap_dispc_get_caps, + .set_update_mode = omap_dispc_set_update_mode, + .get_update_mode = omap_dispc_get_update_mode, + .update_window = omap_dispc_update_window, + .suspend = omap_dispc_suspend, + .resume = omap_dispc_resume, + .setup_plane = omap_dispc_setup_plane, + .enable_plane = omap_dispc_enable_plane, + .set_color_key = omap_dispc_set_color_key, +}; + +MODULE_DESCRIPTION("TI OMAP LCDC controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap/dispc.h b/drivers/video/omap/dispc.h new file mode 100644 index 00000000000..eb1512b56ce --- /dev/null +++ b/drivers/video/omap/dispc.h @@ -0,0 +1,43 @@ +#ifndef _DISPC_H +#define _DISPC_H + +#include + +#define DISPC_PLANE_GFX 0 +#define DISPC_PLANE_VID1 1 +#define DISPC_PLANE_VID2 2 + +#define DISPC_RGB_1_BPP 0x00 +#define DISPC_RGB_2_BPP 0x01 +#define DISPC_RGB_4_BPP 0x02 +#define DISPC_RGB_8_BPP 0x03 +#define DISPC_RGB_12_BPP 0x04 +#define DISPC_RGB_16_BPP 0x06 +#define DISPC_RGB_24_BPP 0x08 +#define DISPC_RGB_24_BPP_UNPACK_32 0x09 +#define DISPC_YUV2_422 0x0a +#define DISPC_UYVY_422 0x0b + +#define DISPC_BURST_4x32 0 +#define DISPC_BURST_8x32 1 +#define DISPC_BURST_16x32 2 + +#define DISPC_LOAD_CLUT_AND_FRAME 0x00 +#define DISPC_LOAD_CLUT_ONLY 0x01 +#define DISPC_LOAD_FRAME_ONLY 0x02 +#define DISPC_LOAD_CLUT_ONCE_FRAME 0x03 + +#define DISPC_TFT_DATA_LINES_12 0 +#define DISPC_TFT_DATA_LINES_16 1 +#define DISPC_TFT_DATA_LINES_18 2 +#define DISPC_TFT_DATA_LINES_24 3 + +extern void omap_dispc_set_lcd_size(int width, int height); + +extern void omap_dispc_enable_lcd_out(int enable); +extern void omap_dispc_enable_digit_out(int enable); + +extern int omap_dispc_request_irq(void (*callback)(void *data), void *data); +extern void omap_dispc_free_irq(void); + +#endif diff --git a/drivers/video/omap/hwa742.c b/drivers/video/omap/hwa742.c new file mode 100644 index 00000000000..f466c4b5b2b --- /dev/null +++ b/drivers/video/omap/hwa742.c @@ -0,0 +1,895 @@ +/* + * File: drivers/video/omap/hwa742.c + * + * Epson HWA742 LCD controller driver + * + * Copyright (C) 2004-2005 Nokia Corporation + * Authors: Juha Yrjölä + * Imre Deak + * YUV support: Jussi Laako + * + * 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 + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +#define MODULE_NAME "omapfb-hwa742" + +#define HWA742_REV_CODE_REG 0x0 +#define HWA742_CONFIG_REG 0x2 +#define HWA742_PLL_DIV_REG 0x4 +#define HWA742_PLL_0_REG 0x6 +#define HWA742_PLL_1_REG 0x8 +#define HWA742_PLL_2_REG 0xa +#define HWA742_PLL_3_REG 0xc +#define HWA742_PLL_4_REG 0xe +#define HWA742_CLK_SRC_REG 0x12 +#define HWA742_PANEL_TYPE_REG 0x14 +#define HWA742_H_DISP_REG 0x16 +#define HWA742_H_NDP_REG 0x18 +#define HWA742_V_DISP_1_REG 0x1a +#define HWA742_V_DISP_2_REG 0x1c +#define HWA742_V_NDP_REG 0x1e +#define HWA742_HS_W_REG 0x20 +#define HWA742_HP_S_REG 0x22 +#define HWA742_VS_W_REG 0x24 +#define HWA742_VP_S_REG 0x26 +#define HWA742_PCLK_POL_REG 0x28 +#define HWA742_INPUT_MODE_REG 0x2a +#define HWA742_TRANSL_MODE_REG1 0x2e +#define HWA742_DISP_MODE_REG 0x34 +#define HWA742_WINDOW_TYPE 0x36 +#define HWA742_WINDOW_X_START_0 0x38 +#define HWA742_WINDOW_X_START_1 0x3a +#define HWA742_WINDOW_Y_START_0 0x3c +#define HWA742_WINDOW_Y_START_1 0x3e +#define HWA742_WINDOW_X_END_0 0x40 +#define HWA742_WINDOW_X_END_1 0x42 +#define HWA742_WINDOW_Y_END_0 0x44 +#define HWA742_WINDOW_Y_END_1 0x46 +#define HWA742_MEMORY_WRITE_LSB 0x48 +#define HWA742_MEMORY_WRITE_MSB 0x49 +#define HWA742_MEMORY_READ_0 0x4a +#define HWA742_MEMORY_READ_1 0x4c +#define HWA742_MEMORY_READ_2 0x4e +#define HWA742_POWER_SAVE 0x56 +#define HWA742_NDP_CTRL 0x58 + +#define HWA742_AUTO_UPDATE_TIME (HZ / 20) + +#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) + +/* Reserve 4 request slots for requests in irq context */ +#define REQ_POOL_SIZE 24 +#define IRQ_REQ_POOL_SIZE 4 + +struct update_param { + int x, y, width, height; + int color_mode; + int flags; +}; + +#define REQ_FROM_IRQ_POOL 0x01 + +#define REQ_COMPLETE 0 +#define REQ_PENDING 1 + +struct hwa742_request { + struct list_head entry; + unsigned int flags; + + int (*handler)(struct hwa742_request *req); + void (*complete)(void *data); + void *complete_data; + + union { + struct update_param update; + struct completion *sync; + } par; +}; + +struct hwa742_struct { + enum omapfb_update_mode update_mode; + enum omapfb_update_mode update_mode_before_suspend; + + struct timer_list auto_update_timer; + int stop_auto_update; + struct omapfb_update_window auto_update_window; + + struct hwa742_request req_pool[REQ_POOL_SIZE]; + struct list_head pending_req_list; + struct list_head free_req_list; + struct semaphore req_sema; + spinlock_t req_lock; + + struct clk *sys_ck; + struct extif_timings reg_timings, lut_timings; + + int prev_color_mode; + int prev_flags; + int window_type; + + u32 max_transmit_size; + u32 extif_clk_period; + + struct omapfb_device *fbdev; + struct lcd_ctrl_extif *extif; + struct lcd_ctrl *int_ctrl; +} hwa742; + +static u8 hwa742_read_reg(u8 reg) +{ + u8 data; + + hwa742.extif->set_bits_per_cycle(8); + hwa742.extif->write_command(®, 1); + hwa742.extif->read_data(&data, 1); + + return data; +} + +static void hwa742_write_reg(u8 reg, u8 data) +{ + hwa742.extif->set_bits_per_cycle(8); + hwa742.extif->write_command(®, 1); + hwa742.extif->write_data(&data, 1); +} + +static void set_window_regs(int x_start, int y_start, int x_end, int y_end) +{ + u8 tmp[8]; + u8 cmd; + + x_end--; + y_end--; + tmp[0] = x_start; + tmp[1] = x_start >> 8; + tmp[2] = y_start; + tmp[3] = y_start >> 8; + tmp[4] = x_end; + tmp[5] = x_end >> 8; + tmp[6] = y_end; + tmp[7] = y_end >> 8; + + hwa742.extif->set_bits_per_cycle(8); + cmd = HWA742_WINDOW_X_START_0; + + hwa742.extif->write_command(&cmd, 1); + + hwa742.extif->write_data(tmp, 8); +} + +static void set_format_regs(int conv, int transl, int flags) +{ + if (flags & OMAPFB_FORMAT_FLAG_DOUBLE) { + hwa742.window_type = ((hwa742.window_type & 0xfc) | 0x01); + DBGPRINT(2, "hwa742: enabled pixel doubling\n"); + } else { + hwa742.window_type = (hwa742.window_type & 0xfc); + DBGPRINT(2, "hwa742: disabled pixel doubling\n"); + } + + hwa742_write_reg(HWA742_INPUT_MODE_REG, conv); + hwa742_write_reg(HWA742_TRANSL_MODE_REG1, transl); + hwa742_write_reg(HWA742_WINDOW_TYPE, hwa742.window_type); +} + +static inline struct hwa742_request *alloc_req(void) +{ + unsigned long flags; + struct hwa742_request *req; + int req_flags = 0; + + if (!in_interrupt()) + down(&hwa742.req_sema); + else + req_flags = REQ_FROM_IRQ_POOL; + + spin_lock_irqsave(&hwa742.req_lock, flags); + BUG_ON(list_empty(&hwa742.free_req_list)); + req = list_entry(hwa742.free_req_list.next, + struct hwa742_request, entry); + list_del(&req->entry); + spin_unlock_irqrestore(&hwa742.req_lock, flags); + + INIT_LIST_HEAD(&req->entry); + req->flags = req_flags; + + return req; +} + +static inline void free_req(struct hwa742_request *req) +{ + unsigned long flags; + + spin_lock_irqsave(&hwa742.req_lock, flags); + + list_del(&req->entry); + list_add(&req->entry, &hwa742.free_req_list); + if (!(req->flags & REQ_FROM_IRQ_POOL)) + up(&hwa742.req_sema); + + spin_unlock_irqrestore(&hwa742.req_lock, flags); +} + +static void process_pending_requests(void) +{ + unsigned long flags; + + DBGENTER(2); + + spin_lock_irqsave(&hwa742.req_lock, flags); + + while (!list_empty(&hwa742.pending_req_list)) { + struct hwa742_request *req; + void (*complete)(void *); + void *complete_data; + + req = list_entry(hwa742.pending_req_list.next, + struct hwa742_request, entry); + spin_unlock_irqrestore(&hwa742.req_lock, flags); + + if (req->handler(req) == REQ_PENDING) + return; + + complete = req->complete; + complete_data = req->complete_data; + free_req(req); + + if (complete) + complete(complete_data); + + spin_lock_irqsave(&hwa742.req_lock, flags); + } + + spin_unlock_irqrestore(&hwa742.req_lock, flags); + + DBGLEAVE(2); +} + +static void submit_req_list(struct list_head *head) +{ + unsigned long flags; + int process = 1; + + DBGENTER(2); + + spin_lock_irqsave(&hwa742.req_lock, flags); + if (likely(!list_empty(&hwa742.pending_req_list))) + process = 0; + list_splice_init(head, hwa742.pending_req_list.prev); + spin_unlock_irqrestore(&hwa742.req_lock, flags); + + if (process) + process_pending_requests(); + + DBGLEAVE(2); +} + +static void request_complete(void *data) +{ + struct hwa742_request *req = (struct hwa742_request *)data; + void (*complete)(void *); + void *complete_data; + + complete = req->complete; + complete_data = req->complete_data; + + free_req(req); + + if (complete) + complete(complete_data); + + process_pending_requests(); +} + +static int send_frame_handler(struct hwa742_request *req) +{ + struct update_param *par = &req->par.update; + int x = par->x; + int y = par->y; + int w = par->width; + int h = par->height; + int bpp; + int conv, transl; + unsigned long offset; + int color_mode = par->color_mode; + int flags = par->flags; + int scr_width = 800; + + DBGPRINT(2, "x %d y %d w %d h %d scr_width %d color_mode %d flags %d\n", + x, y, w, h, scr_width, color_mode, flags); + + switch (color_mode) { + case OMAPFB_COLOR_YUV422: + bpp = 16; + conv = 0x08; + transl = 0x25; + break; + case OMAPFB_COLOR_YUV420: + bpp = 12; + conv = 0x09; + transl = 0x25; + break; + case OMAPFB_COLOR_RGB565: + bpp = 16; + conv = 0x01; + transl = 0x05; + break; + default: + return -EINVAL; + } + + if (hwa742.prev_flags != flags || + hwa742.prev_color_mode != color_mode) { + set_format_regs(conv, transl, flags); + hwa742.prev_color_mode = color_mode; + hwa742.prev_flags = flags; + } + + set_window_regs(x, y, x + w, y + h); + + offset = (scr_width * y + x) * bpp / 8; + + hwa742.int_ctrl->setup_plane(OMAPFB_PLANE_GFX, + OMAPFB_CHANNEL_OUT_LCD, offset, scr_width, 0, 0, w, h, + color_mode); + + hwa742.extif->set_bits_per_cycle(16); + + hwa742.int_ctrl->enable_plane(OMAPFB_PLANE_GFX, 1); + hwa742.extif->transfer_area(w, h, request_complete, req); + + return REQ_PENDING; +} + +static void send_frame_complete(void *data) +{ + hwa742.int_ctrl->enable_plane(OMAPFB_PLANE_GFX, 0); +} + +#define ADD_PREQ(_x, _y, _w, _h) do { \ + req = alloc_req(); \ + req->handler = send_frame_handler; \ + req->complete = send_frame_complete; \ + req->par.update.x = _x; \ + req->par.update.y = _y; \ + req->par.update.width = _w; \ + req->par.update.height = _h; \ + req->par.update.color_mode = color_mode;\ + req->par.update.flags = flags; \ + list_add_tail(&req->entry, req_head); \ +} while(0) + +static void create_req_list(struct omapfb_update_window *win, + struct list_head *req_head) +{ + struct hwa742_request *req; + int x = win->x; + int y = win->y; + int width = win->width; + int height = win->height; + int color_mode; + int flags; + + flags = win->format & OMAPFB_FORMAT_FLAG_DOUBLE; + color_mode = win->format & OMAPFB_FORMAT_MASK; + + if (x & 1) { + ADD_PREQ(x, y, 1, height); + width--; + x++; + } + if (width & ~1) { + unsigned int xspan = width & ~1; + unsigned int ystart = y; + unsigned int yspan = height; + + if (xspan * height * 2 > hwa742.max_transmit_size) { + yspan = hwa742.max_transmit_size / (xspan * 2); + ADD_PREQ(x, ystart, xspan, yspan); + ystart += yspan; + yspan = height - yspan; + } + + ADD_PREQ(x, ystart, xspan, yspan); + x += xspan; + width -= xspan; + } + if (width) + ADD_PREQ(x, y, 1, height); +} + +static void auto_update_complete(void *data) +{ + DBGENTER(2); + + if (!hwa742.stop_auto_update) + mod_timer(&hwa742.auto_update_timer, + jiffies + HWA742_AUTO_UPDATE_TIME); + + DBGLEAVE(2); +} + +static void hwa742_update_window_auto(unsigned long arg) +{ + LIST_HEAD(req_list); + struct hwa742_request *last; + + DBGENTER(2); + + create_req_list(&hwa742.auto_update_window, &req_list); + last = list_entry(req_list.prev, struct hwa742_request, entry); + + last->complete = auto_update_complete; + last->complete_data = NULL; + + submit_req_list(&req_list); + + DBGLEAVE(2); +} + +int hwa742_update_window_async(struct omapfb_update_window *win, + void (*complete_callback)(void *arg), + void *complete_callback_data) +{ + LIST_HEAD(req_list); + struct hwa742_request *last; + int r = 0; + + DBGENTER(2); + + if (hwa742.update_mode != OMAPFB_MANUAL_UPDATE) { + DBGPRINT(1, "invalid update mode\n"); + r = -EINVAL; + goto out; + } + if (unlikely(win->format & ~(0x03 | OMAPFB_FORMAT_FLAG_DOUBLE))) { + DBGPRINT(1, "invalid window flag"); + r = -EINVAL; + goto out; + } + + create_req_list(win, &req_list); + last = list_entry(req_list.prev, struct hwa742_request, entry); + + last->complete = complete_callback; + last->complete_data = (void *)complete_callback_data; + + submit_req_list(&req_list); + +out: + DBGLEAVE(2); + return r; +} +EXPORT_SYMBOL(hwa742_update_window_async); + +static int hwa742_setup_plane(int plane, int channel_out, + unsigned long offset, int screen_width, + int pos_x, int pos_y, int width, int height, + int color_mode) +{ + if (plane != OMAPFB_PLANE_GFX || + channel_out != OMAPFB_CHANNEL_OUT_LCD) + return -EINVAL; + + return 0; +} + +static int hwa742_enable_plane(int plane, int enable) +{ + if (plane != 0) + return -EINVAL; + + hwa742.int_ctrl->enable_plane(plane, enable); + + return 0; +} + +static int sync_handler(struct hwa742_request *req) +{ + complete(req->par.sync); + return REQ_COMPLETE; +} + +static void hwa742_sync(void) +{ + LIST_HEAD(req_list); + struct hwa742_request *req; + struct completion comp; + + DBGENTER(2); + + req = alloc_req(); + + req->handler = sync_handler; + req->complete = NULL; + init_completion(&comp); + req->par.sync = ∁ + + list_add(&req->entry, &req_list); + submit_req_list(&req_list); + + wait_for_completion(&comp); + + DBGLEAVE(2); +} + +static void hwa742_bind_client(struct omapfb_notifier_block *nb) +{ + DBGPRINT(1, "update_mode %d\n", hwa742.update_mode); + if (hwa742.update_mode == OMAPFB_MANUAL_UPDATE) { + omapfb_notify_clients(hwa742.fbdev, OMAPFB_EVENT_READY); + } +} + +static int hwa742_set_update_mode(enum omapfb_update_mode mode) +{ + int r = 0; + + DBGENTER(1); + + if (mode != OMAPFB_MANUAL_UPDATE && mode != OMAPFB_AUTO_UPDATE && + mode != OMAPFB_UPDATE_DISABLED) { + r = -EINVAL; + goto out; + } + + if (mode == hwa742.update_mode) + goto out; + + printk(KERN_INFO "hwa742: setting update mode to %s\n", + mode == OMAPFB_UPDATE_DISABLED ? "disabled" : + (mode == OMAPFB_AUTO_UPDATE ? "auto" : "manual")); + + switch (hwa742.update_mode) { + case OMAPFB_MANUAL_UPDATE: + omapfb_notify_clients(hwa742.fbdev, OMAPFB_EVENT_DISABLED); + break; + case OMAPFB_AUTO_UPDATE: + hwa742.stop_auto_update = 1; + del_timer_sync(&hwa742.auto_update_timer); + break; + case OMAPFB_UPDATE_DISABLED: + break; + } + + hwa742.update_mode = mode; + hwa742_sync(); + hwa742.stop_auto_update = 0; + + switch (mode) { + case OMAPFB_MANUAL_UPDATE: + omapfb_notify_clients(hwa742.fbdev, OMAPFB_EVENT_READY); + break; + case OMAPFB_AUTO_UPDATE: + hwa742_update_window_auto(0); + break; + case OMAPFB_UPDATE_DISABLED: + break; + } +out: + + DBGLEAVE(1); + return r; +} + +static enum omapfb_update_mode hwa742_get_update_mode(void) +{ + return hwa742.update_mode; +} + +static unsigned long round_to_extif_ticks(unsigned long ps, int div) +{ + int bus_tick = hwa742.extif_clk_period * div; + return (ps + bus_tick - 1) / bus_tick * bus_tick; +} + +static int calc_reg_timing(unsigned long sysclk, int div) +{ + struct extif_timings *t; + unsigned long systim; + + /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns, + * AccessTime 2 ns + 12.2 ns (regs), + * WEOffTime = WEOnTime + 1 ns, + * REOffTime = REOnTime + 16 ns (regs), + * CSOffTime = REOffTime + 1 ns + * ReadCycle = 2ns + 2*SYSCLK (regs), + * WriteCycle = 2*SYSCLK + 2 ns, + * CSPulseWidth = 10 ns */ + systim = 1000000000 / (sysclk / 1000); + DBGPRINT(1, "HWA742 systim %lu ps extif_clk_period %u ps" + "extif_clk_div %d\n", systim, hwa742.extif_clk_period, div); + + t = &hwa742.reg_timings; + memset(t, 0, sizeof(*t)); + t->clk_div = div; + t->cs_on_time = 0; + t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->access_time = round_to_extif_ticks(t->re_on_time + 12200, div); + t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div); + t->re_off_time = round_to_extif_ticks(t->re_on_time + 16000, div); + t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div); + t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); + if (t->we_cycle_time < t->we_off_time) + t->we_cycle_time = t->we_off_time; + t->re_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); + if (t->re_cycle_time < t->re_off_time) + t->re_cycle_time = t->re_off_time; + t->cs_pulse_width = 0; + + DBGPRINT(1, "[reg]cson %d csoff %d reon %d reoff %d\n", + t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); + DBGPRINT(1, "[reg]weon %d weoff %d recyc %d wecyc %d\n", + t->we_on_time, t->we_off_time, t->re_cycle_time, + t->we_cycle_time); + DBGPRINT(1, "[reg]rdaccess %d cspulse %d\n", + t->access_time, t->cs_pulse_width); + + return hwa742.extif->convert_timings(t); +} + +static int calc_lut_timing(unsigned long sysclk, int div) +{ + struct extif_timings *t; + unsigned long systim; + + /* CSOnTime 0, WEOnTime 2 ns, REOnTime 2 ns, + * AccessTime 2 ns + 4 * SYSCLK + 26 (lut), + * WEOffTime = WEOnTime + 1 ns, + * REOffTime = REOnTime + 4*SYSCLK + 26 ns (lut), + * CSOffTime = REOffTime + 1 ns + * ReadCycle = 2ns + 4*SYSCLK + 26 ns (lut), + * WriteCycle = 2*SYSCLK + 2 ns, + * CSPulseWidth = 10 ns + */ + systim = 1000000000 / (sysclk / 1000); + DBGPRINT(1, "HWA742 systim %lu ps extif_clk_period %u ps" + "extif_clk_div %d\n", systim, hwa742.extif_clk_period, div); + + t = &hwa742.lut_timings; + memset(t, 0, sizeof(*t)); + + t->clk_div = div; + + t->cs_on_time = 0; + t->we_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->re_on_time = round_to_extif_ticks(t->cs_on_time + 2000, div); + t->access_time = round_to_extif_ticks(t->re_on_time + 4 * systim + + 26000, div); + t->we_off_time = round_to_extif_ticks(t->we_on_time + 1000, div); + t->re_off_time = round_to_extif_ticks(t->re_on_time + 4 * systim + + 26000, div); + t->cs_off_time = round_to_extif_ticks(t->re_off_time + 1000, div); + t->we_cycle_time = round_to_extif_ticks(2 * systim + 2000, div); + if (t->we_cycle_time < t->we_off_time) + t->we_cycle_time = t->we_off_time; + t->re_cycle_time = round_to_extif_ticks(2000 + 4 * systim + 26000, div); + if (t->re_cycle_time < t->re_off_time) + t->re_cycle_time = t->re_off_time; + t->cs_pulse_width = 0; + + DBGPRINT(1, "[lut]cson %d csoff %d reon %d reoff %d\n", + t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); + DBGPRINT(1, "[lut]weon %d weoff %d recyc %d wecyc %d\n", + t->we_on_time, t->we_off_time, t->re_cycle_time, + t->we_cycle_time); + DBGPRINT(1, "[lut]rdaccess %d cspulse %d\n", + t->access_time, t->cs_pulse_width); + + return hwa742.extif->convert_timings(t); +} + +static int calc_extif_timings(unsigned long sysclk) +{ + int max_clk_div; + int div; + + hwa742.extif->get_clk_info(&hwa742.extif_clk_period, &max_clk_div); + for (div = 1; div < max_clk_div; div++) { + if (calc_reg_timing(sysclk, div) == 0) + break; + } + if (div == max_clk_div) + goto err; + + for (div = 1; div < max_clk_div; div++) { + if (calc_lut_timing(sysclk, div) == 0) + break; + } + + if (div < max_clk_div) + return 0; + +err: + pr_err("can't setup timings\n"); + return -1; +} + +static unsigned long hwa742_get_caps(void) +{ + return OMAPFB_CAPS_MANUAL_UPDATE; +} + +static void hwa742_suspend(void) +{ + hwa742.update_mode_before_suspend = hwa742.update_mode; + hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED); + /* Enable sleep mode */ + hwa742_write_reg(HWA742_POWER_SAVE, 1 << 1); + clk_disable(hwa742.sys_ck); +} + +static void hwa742_resume(void) +{ + if (clk_enable(hwa742.sys_ck) != 0) + pr_err("failed to enable SYS clock\n"); + /* Disable sleep mode */ + hwa742_write_reg(HWA742_POWER_SAVE, 0); + while (1) { + /* Loop until PLL output is stabilized */ + if (hwa742_read_reg(HWA742_PLL_DIV_REG) & (1 << 7)) + break; + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(msecs_to_jiffies(5)); + } + hwa742_set_update_mode(hwa742.update_mode_before_suspend); +} + +struct lcd_ctrl hwa742_ctrl; + +static int hwa742_init(struct omapfb_device *fbdev, int ext_mode, int req_vram_size) +{ + int r = 0, i; + u8 rev, conf; + unsigned long sysfreq; + int div, nd; + + DBGENTER(1); + + hwa742.sys_ck = clk_get(0, "bclk"); + if (IS_ERR(hwa742.sys_ck)) { + pr_err("can't get SYS clock\n"); + return PTR_ERR(hwa742.sys_ck); + } + + if ((r = clk_enable(hwa742.sys_ck)) != 0) { + pr_err("can't enable SYS clock\n"); + clk_put(hwa742.sys_ck); + return r; + } + + BUG_ON(!fbdev->ext_if || !fbdev->int_ctrl); + + hwa742.fbdev = fbdev; + hwa742.extif = fbdev->ext_if; + hwa742.int_ctrl = fbdev->int_ctrl; + + spin_lock_init(&hwa742.req_lock); + + if ((r = hwa742.int_ctrl->init(fbdev, 1, req_vram_size)) < 0) + goto err1; + + if ((r = hwa742.extif->init()) < 0) + goto err2; + + hwa742_ctrl.get_vram_layout = hwa742.int_ctrl->get_vram_layout; + hwa742_ctrl.mmap = hwa742.int_ctrl->mmap; + + sysfreq = clk_get_rate(hwa742.sys_ck); + if ((r = calc_extif_timings(sysfreq)) < 0) + goto err3; + hwa742.extif->set_timings(&hwa742.reg_timings); + + div = (hwa742_read_reg(HWA742_PLL_DIV_REG) & 0x3f) + 1; + + nd = (hwa742_read_reg(HWA742_PLL_4_REG) & 0x7f) + 1; + + if ((r = calc_extif_timings(sysfreq / div * nd)) < 0) + goto err3; + hwa742.extif->set_timings(&hwa742.reg_timings); + + rev = hwa742_read_reg(HWA742_REV_CODE_REG); + if ((rev & 0xfc) != 0x80) { + pr_err("invalid revision %02x\n", rev); + r = -ENODEV; + goto err3; + } + + conf = hwa742_read_reg(HWA742_CONFIG_REG); + pr_info(MODULE_NAME ": Epson HWA742 LCD controller rev. %d " + "initialized (CNF pins %x)\n", rev & 0x03, conf & 0x07); + + if (!(hwa742_read_reg(HWA742_PLL_DIV_REG) & 0x80)) { + pr_err("controller not initialized by the bootloader\n"); + r = -ENODEV; + goto err2; + } + + hwa742.max_transmit_size = hwa742.extif->max_transmit_size; + + hwa742.update_mode = OMAPFB_UPDATE_DISABLED; + + hwa742.auto_update_window.x = 0; + hwa742.auto_update_window.y = 0; + hwa742.auto_update_window.width = fbdev->panel->x_res; + hwa742.auto_update_window.height = fbdev->panel->y_res; + hwa742.auto_update_window.format = 0; + + init_timer(&hwa742.auto_update_timer); + hwa742.auto_update_timer.function = hwa742_update_window_auto; + hwa742.auto_update_timer.data = 0; + + hwa742.prev_color_mode = -1; + hwa742.prev_flags = 0; + + hwa742.fbdev = fbdev; + + INIT_LIST_HEAD(&hwa742.free_req_list); + INIT_LIST_HEAD(&hwa742.pending_req_list); + for (i = 0; i < ARRAY_SIZE(hwa742.req_pool); i++) + list_add(&hwa742.req_pool[i].entry, &hwa742.free_req_list); + BUG_ON(i <= IRQ_REQ_POOL_SIZE); + sema_init(&hwa742.req_sema, i - IRQ_REQ_POOL_SIZE); + + return 0; +err3: + hwa742.extif->cleanup(); +err2: + hwa742.int_ctrl->cleanup(); +err1: + clk_disable(hwa742.sys_ck); + clk_put(hwa742.sys_ck); + return r; +} + +static void hwa742_cleanup(void) +{ + hwa742_set_update_mode(OMAPFB_UPDATE_DISABLED); + hwa742.extif->cleanup(); + hwa742.int_ctrl->cleanup(); + clk_disable(hwa742.sys_ck); + clk_put(hwa742.sys_ck); +} + +struct lcd_ctrl hwa742_ctrl = { + .name = "hwa742", + .init = hwa742_init, + .cleanup = hwa742_cleanup, + .bind_client = hwa742_bind_client, + .get_caps = hwa742_get_caps, + .set_update_mode = hwa742_set_update_mode, + .get_update_mode = hwa742_get_update_mode, + .setup_plane = hwa742_setup_plane, + .enable_plane = hwa742_enable_plane, + .update_window = hwa742_update_window_async, + .sync = hwa742_sync, + .suspend = hwa742_suspend, + .resume = hwa742_resume, +}; + diff --git a/drivers/video/omap/lcd_apollon.c b/drivers/video/omap/lcd_apollon.c new file mode 100644 index 00000000000..30cec3e856f --- /dev/null +++ b/drivers/video/omap/lcd_apollon.c @@ -0,0 +1,157 @@ +/* + * File: drivers/video/omap/lcd_apollon.c + * + * LCD panel support for the Samsung OMAP2 Apollon board + * + * Copyright (C) 2005,2006 Samsung Electronics + * Author: Kyungmin Park + * + * Derived from drivers/video/omap/lcd-h4.c + * + * 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 + +/* #define OMAPFB_DBG 1 */ + +/* #define USE_35INCH_LCD 1 */ + +#include "debug.h" + +static int apollon_panel_init(struct omapfb_device *fbdev) +{ + DBGENTER(1); + DBGLEAVE(1); + return 0; +} + +static void apollon_panel_cleanup(void) +{ + DBGENTER(1); + DBGLEAVE(1); +} + +static int apollon_panel_enable(void) +{ + + DBGENTER(1); + + /* configure LCD PWR_EN */ + omap_cfg_reg(M21_242X_GPIO11); + + DBGLEAVE(1); + return 0; +} + +static void apollon_panel_disable(void) +{ + DBGENTER(1); + DBGLEAVE(1); +} + +static unsigned long apollon_panel_get_caps(void) +{ + return 0; +} + +struct lcd_panel apollon_panel = { + .name = "apollon", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC, + + .bpp = 16, + .data_lines = 18, +#ifdef USE_35INCH_LCD + .x_res = 240, + .y_res = 320, + .hsw = 2, + .hfp = 3, + .hbp = 9, + .vsw = 4, + .vfp = 3, + .vbp = 5, +#else + .x_res = 480, + .y_res = 272, + .hsw = 41, + .hfp = 2, + .hbp = 2, + .vsw = 10, + .vfp = 2, + .vbp = 2, +#endif + .pixel_clock = 6250, + + .init = apollon_panel_init, + .cleanup = apollon_panel_cleanup, + .enable = apollon_panel_enable, + .disable = apollon_panel_disable, + .get_caps = apollon_panel_get_caps, +}; + +static int apollon_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&apollon_panel); + return 0; +} + +static int apollon_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int apollon_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int apollon_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver apollon_panel_driver = { + .probe = apollon_panel_probe, + .remove = apollon_panel_remove, + .suspend = apollon_panel_suspend, + .resume = apollon_panel_resume, + .driver = { + .name = "apollon_lcd", + .owner = THIS_MODULE, + }, +}; + +static int __init apollon_panel_drv_init(void) +{ + return platform_driver_register(&apollon_panel_driver); +} + +static void __exit apollon_panel_drv_exit(void) +{ + platform_driver_unregister(&apollon_panel_driver); +} + +module_init(apollon_panel_drv_init); +module_exit(apollon_panel_drv_exit); diff --git a/drivers/video/omap/lcd_h2.c b/drivers/video/omap/lcd_h2.c new file mode 100644 index 00000000000..128ba247c4a --- /dev/null +++ b/drivers/video/omap/lcd_h2.c @@ -0,0 +1,183 @@ +/* + * File: drivers/video/omap/lcd-h2.c + * + * LCD panel support for the TI OMAP H2 board + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak + * + * 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 + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" +#include "../drivers/ssi/omap-uwire.h" + +#define MODULE_NAME "omapfb-lcd_h2" +#define TSC2101_UWIRE_CS 1 + +#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) + +static int tsc2101_write_reg(int page, int reg, u16 data) +{ + u16 cmd; + int r; + + DBGENTER(1); + + cmd = ((page & 3) << 11) | ((reg & 0x3f) << 5); + if (omap_uwire_data_transfer(TSC2101_UWIRE_CS, cmd, 16, 0, NULL, 1)) + r = -1; + else + r = omap_uwire_data_transfer(TSC2101_UWIRE_CS, data, 16, 0, + NULL, 0); + + DBGLEAVE(1); + return r; +} + +static int h2_panel_init(struct omapfb_device *fbdev) +{ + unsigned long uwire_flags; + DBGENTER(1); + + /* Configure N15 pin to be uWire CS1 */ + omap_cfg_reg(N15_1610_UWIRE_CS1); + uwire_flags = UWIRE_READ_RISING_EDGE | UWIRE_WRITE_RISING_EDGE; + uwire_flags |= UWIRE_FREQ_DIV_8; + omap_uwire_configure_mode(TSC2101_UWIRE_CS, uwire_flags); + + DBGLEAVE(1); + return 0; +} + +static void h2_panel_cleanup(void) +{ + DBGENTER(1); + DBGLEAVE(1); +} + +static int h2_panel_enable(void) +{ + int r; + + DBGENTER(1); + + /* Assert LCD_EN, BKLIGHT_EN pins on LCD panel + * page2, GPIO config reg, GPIO(0,1) to out and asserted + */ + r = tsc2101_write_reg(2, 0x23, 0xCC00) ? -1 : 0; + + DBGLEAVE(1); + return r; +} + +static void h2_panel_disable(void) +{ + DBGENTER(1); + + /* Deassert LCD_EN and BKLIGHT_EN pins on LCD panel + * page2, GPIO config reg, GPIO(0,1) to out and deasserted + */ + if (tsc2101_write_reg(2, 0x23, 0x8800)) + pr_err("failed to disable LCD panel\n"); + + DBGLEAVE(1); +} + +static unsigned long h2_panel_get_caps(void) +{ + return 0; +} + +struct lcd_panel h2_panel = { + .name = "h2", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 16, + .x_res = 240, + .y_res = 320, + .pixel_clock = 5000, + .hsw = 12, + .hfp = 12, + .hbp = 46, + .vsw = 1, + .vfp = 1, + .vbp = 0, + + .init = h2_panel_init, + .cleanup = h2_panel_cleanup, + .enable = h2_panel_enable, + .disable = h2_panel_disable, + .get_caps = h2_panel_get_caps, +}; + +static int h2_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&h2_panel); + return 0; +} + +static int h2_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int h2_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int h2_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver h2_panel_driver = { + .probe = h2_panel_probe, + .remove = h2_panel_remove, + .suspend = h2_panel_suspend, + .resume = h2_panel_resume, + .driver = { + .name = "lcd_h2", + .owner = THIS_MODULE, + }, +}; + +static int h2_panel_drv_init(void) +{ + return platform_driver_register(&h2_panel_driver); +} + +static void h2_panel_drv_cleanup(void) +{ + platform_driver_unregister(&h2_panel_driver); +} + +module_init(h2_panel_drv_init); +module_exit(h2_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_h3.c b/drivers/video/omap/lcd_h3.c new file mode 100644 index 00000000000..21c9e3ee8bb --- /dev/null +++ b/drivers/video/omap/lcd_h3.c @@ -0,0 +1,162 @@ +/* + * File: drivers/video/omap/lcd-h3.c + * + * LCD panel support for the TI OMAP H3 board + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak + * + * 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 + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +#define MODULE_NAME "omapfb-lcd_h3" + +#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) + +static int h3_panel_init(struct omapfb_device *fbdev) +{ + DBGENTER(1); + DBGLEAVE(1); + return 0; +} + +static void h3_panel_cleanup(void) +{ + DBGENTER(1); + DBGLEAVE(1); +} + +static int h3_panel_enable(void) +{ + int r = 0; + + DBGENTER(1); + + /* GPIO1 and GPIO2 of TPS65010 send LCD_ENBKL and LCD_ENVDD signals */ + r = tps65010_set_gpio_out_value(GPIO1, HIGH); + if (!r) + r = tps65010_set_gpio_out_value(GPIO2, HIGH); + if (r) + pr_err("Unable to turn on LCD panel\n"); + + DBGLEAVE(1); + return r; +} + +static void h3_panel_disable(void) +{ + int r = 0; + + DBGENTER(1); + + /* GPIO1 and GPIO2 of TPS65010 send LCD_ENBKL and LCD_ENVDD signals */ + r = tps65010_set_gpio_out_value(GPIO1, LOW); + if (!r) + tps65010_set_gpio_out_value(GPIO2, LOW); + if (r) + pr_err("Unable to turn off LCD panel\n"); + + DBGLEAVE(1); +} + +static unsigned long h3_panel_get_caps(void) +{ + return 0; +} + +struct lcd_panel h3_panel = { + .name = "h3", + .config = OMAP_LCDC_PANEL_TFT, + + .data_lines = 16, + .bpp = 16, + .x_res = 240, + .y_res = 320, + .pixel_clock = 12000, + .hsw = 12, + .hfp = 14, + .hbp = 72 - 12, + .vsw = 1, + .vfp = 1, + .vbp = 0, + .pcd = 0, + + .init = h3_panel_init, + .cleanup = h3_panel_cleanup, + .enable = h3_panel_enable, + .disable = h3_panel_disable, + .get_caps = h3_panel_get_caps, +}; + +static int h3_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&h3_panel); + return 0; +} + +static int h3_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int h3_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int h3_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver h3_panel_driver = { + .probe = h3_panel_probe, + .remove = h3_panel_remove, + .suspend = h3_panel_suspend, + .resume = h3_panel_resume, + .driver = { + .name = "lcd_h3", + .owner = THIS_MODULE, + }, +}; + +static int h3_panel_drv_init(void) +{ + return platform_driver_register(&h3_panel_driver); +} + +static void h3_panel_drv_cleanup(void) +{ + platform_driver_unregister(&h3_panel_driver); +} + +module_init(h3_panel_drv_init); +module_exit(h3_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_h4.c b/drivers/video/omap/lcd_h4.c new file mode 100644 index 00000000000..ab7782a2fac --- /dev/null +++ b/drivers/video/omap/lcd_h4.c @@ -0,0 +1,136 @@ +/* + * File: drivers/video/omap/lcd-h4.c + * + * LCD panel support for the TI OMAP H4 board + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak + * + * 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 + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +static int h4_panel_init(struct omapfb_device *fbdev) +{ + DBGENTER(1); + DBGLEAVE(1); + return 0; +} + +static void h4_panel_cleanup(void) +{ + DBGENTER(1); + DBGLEAVE(1); +} + +static int h4_panel_enable(void) +{ + + DBGENTER(1); + DBGLEAVE(1); + return 0; +} + +static void h4_panel_disable(void) +{ + DBGENTER(1); + DBGLEAVE(1); +} + +static unsigned long h4_panel_get_caps(void) +{ + return 0; +} + +struct lcd_panel h4_panel = { + .name = "h4", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 16, + .x_res = 240, + .y_res = 320, + .pixel_clock = 6250, + .hsw = 15, + .hfp = 15, + .hbp = 60, + .vsw = 1, + .vfp = 1, + .vbp = 1, + + .init = h4_panel_init, + .cleanup = h4_panel_cleanup, + .enable = h4_panel_enable, + .disable = h4_panel_disable, + .get_caps = h4_panel_get_caps, +}; + +static int h4_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&h4_panel); + return 0; +} + +static int h4_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int h4_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int h4_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver h4_panel_driver = { + .probe = h4_panel_probe, + .remove = h4_panel_remove, + .suspend = h4_panel_suspend, + .resume = h4_panel_resume, + .driver = { + .name = "lcd_h4", + .owner = THIS_MODULE, + }, +}; + +static int h4_panel_drv_init(void) +{ + return platform_driver_register(&h4_panel_driver); +} + +static void h4_panel_drv_cleanup(void) +{ + platform_driver_unregister(&h4_panel_driver); +} + +module_init(h4_panel_drv_init); +module_exit(h4_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_inn1510.c b/drivers/video/omap/lcd_inn1510.c new file mode 100644 index 00000000000..31988d1b574 --- /dev/null +++ b/drivers/video/omap/lcd_inn1510.c @@ -0,0 +1,145 @@ +/* + * File: drivers/video/omap/lcd-inn1510.c + * + * LCD panel support for the TI OMAP1510 Innovator board + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak + * + * 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 + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +static int innovator1510_panel_init(struct omapfb_device *fbdev) +{ + DBGENTER(1); + DBGLEAVE(1); + return 0; +} + +static void innovator1510_panel_cleanup(void) +{ + DBGENTER(1); + DBGLEAVE(1); +} + +static int innovator1510_panel_enable(void) +{ + DBGENTER(1); + + fpga_write(0x7, OMAP1510_FPGA_LCD_PANEL_CONTROL); + + DBGLEAVE(1); + return 0; +} + +static void innovator1510_panel_disable(void) +{ + DBGENTER(1); + + fpga_write(0x0, OMAP1510_FPGA_LCD_PANEL_CONTROL); + + DBGLEAVE(1); +} + +static unsigned long innovator1510_panel_get_caps(void) +{ + return 0; +} + +struct lcd_panel innovator1510_panel = { + .name = "inn1510", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 16, + .x_res = 240, + .y_res = 320, + .pixel_clock = 12500, + .hsw = 40, + .hfp = 40, + .hbp = 72, + .vsw = 1, + .vfp = 1, + .vbp = 0, + .pcd = 12, + + .init = innovator1510_panel_init, + .cleanup = innovator1510_panel_cleanup, + .enable = innovator1510_panel_enable, + .disable = innovator1510_panel_disable, + .get_caps = innovator1510_panel_get_caps, +}; + +static int innovator1510_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&innovator1510_panel); + return 0; +} + +static int innovator1510_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int innovator1510_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int innovator1510_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver innovator1510_panel_driver = { + .probe = innovator1510_panel_probe, + .remove = innovator1510_panel_remove, + .suspend = innovator1510_panel_suspend, + .resume = innovator1510_panel_resume, + .driver = { + .name = "lcd_inn1510", + .owner = THIS_MODULE, + }, +}; + +static int innovator1510_panel_drv_init(void) +{ + return platform_driver_register(&innovator1510_panel_driver); +} + +static void innovator1510_panel_drv_cleanup(void) +{ + platform_driver_unregister(&innovator1510_panel_driver); +} + +module_init(innovator1510_panel_drv_init); +module_exit(innovator1510_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_inn1610.c b/drivers/video/omap/lcd_inn1610.c new file mode 100644 index 00000000000..c6593b4c22c --- /dev/null +++ b/drivers/video/omap/lcd_inn1610.c @@ -0,0 +1,173 @@ +/* + * File: drivers/video/omap/lcd-inn1610.c + * + * LCD panel support for the TI OMAP1610 Innovator board + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak + * + * 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 + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +#define MODULE_NAME "omapfb-lcd_h3" + +#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) + +static int innovator1610_panel_init(struct omapfb_device *fbdev) +{ + int r = 0; + + DBGENTER(1); + + if (omap_request_gpio(14)) { + pr_err("can't request GPIO 14\n"); + r = -1; + goto exit; + } + if (omap_request_gpio(15)) { + pr_err("can't request GPIO 15\n"); + omap_free_gpio(14); + r = -1; + goto exit; + } + /* configure GPIO(14, 15) as outputs */ + omap_set_gpio_direction(14, 0); + omap_set_gpio_direction(15, 0); +exit: + DBGLEAVE(1); + return r; +} + +static void innovator1610_panel_cleanup(void) +{ + DBGENTER(1); + + omap_free_gpio(15); + omap_free_gpio(14); + + DBGLEAVE(1); +} + +static int innovator1610_panel_enable(void) +{ + DBGENTER(1); + + /* set GPIO14 and GPIO15 high */ + omap_set_gpio_dataout(14, 1); + omap_set_gpio_dataout(15, 1); + + DBGLEAVE(1); + return 0; +} + +static void innovator1610_panel_disable(void) +{ + DBGENTER(1); + + /* set GPIO13, GPIO14 and GPIO15 low */ + omap_set_gpio_dataout(14, 0); + omap_set_gpio_dataout(15, 0); + + DBGLEAVE(1); +} + +static unsigned long innovator1610_panel_get_caps(void) +{ + return 0; +} + +struct lcd_panel innovator1610_panel = { + .name = "inn1610", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 16, + .x_res = 320, + .y_res = 240, + .pixel_clock = 12500, + .hsw = 40, + .hfp = 40, + .hbp = 72, + .vsw = 1, + .vfp = 1, + .vbp = 0, + .pcd = 12, + + .init = innovator1610_panel_init, + .cleanup = innovator1610_panel_cleanup, + .enable = innovator1610_panel_enable, + .disable = innovator1610_panel_disable, + .get_caps = innovator1610_panel_get_caps, +}; + +static int innovator1610_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&innovator1610_panel); + return 0; +} + +static int innovator1610_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int innovator1610_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int innovator1610_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver innovator1610_panel_driver = { + .probe = innovator1610_panel_probe, + .remove = innovator1610_panel_remove, + .suspend = innovator1610_panel_suspend, + .resume = innovator1610_panel_resume, + .driver = { + .name = "lcd_inn1610", + .owner = THIS_MODULE, + }, +}; + +static int innovator1610_panel_drv_init(void) +{ + return platform_driver_register(&innovator1610_panel_driver); +} + +static void innovator1610_panel_drv_cleanup(void) +{ + platform_driver_unregister(&innovator1610_panel_driver); +} + +module_init(innovator1610_panel_drv_init); +module_exit(innovator1610_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_lph8923.c b/drivers/video/omap/lcd_lph8923.c new file mode 100644 index 00000000000..69f5f184335 --- /dev/null +++ b/drivers/video/omap/lcd_lph8923.c @@ -0,0 +1,471 @@ +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include "../../cbus/tahvo.h" + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +#define LPH8923_MODULE_NAME "lcd_lph8923" + +#define LPH8923_VER_BUGGY 1 +#define LPH8923_VER_NON_BUGGY 3 + +struct { + int enabled; + int version; + + u8 display_id[3]; + unsigned int saved_bklight_level; + unsigned long hw_guard_end; /* next value of jiffies + when we can issue the + next sleep in/out command */ + unsigned long hw_guard_wait; /* max guard time in jiffies */ + struct omapfb_device *fbdev; + struct spi_device *spi; +} lph8923; + +#define LPH8923_CMD_READ_DISP_ID 0x04 +#define LPH8923_CMD_READ_RED 0x06 +#define LPH8923_CMD_READ_GREEN 0x07 +#define LPH8923_CMD_READ_BLUE 0x08 +#define LPH8923_CMD_READ_DISP_STATUS 0x09 +#define LPH8923_CMD_SLEEP_IN 0x10 +#define LPH8923_CMD_SLEEP_OUT 0x11 +#define LPH8923_CMD_DISP_OFF 0x28 +#define LPH8923_CMD_DISP_ON 0x29 + +static struct lcd_panel lph8923_panel; + +#define LPH8923_SPEED_HZ 12000000 + +static int lph8923_spi_probe(struct spi_device *spi) +{ + DBGENTER(1); + + spi->dev.power.power_state = PMSG_ON; + spi->mode = SPI_MODE_0; + spi->bits_per_word = 9; + spi_setup(spi); + + DBGPRINT(1, "spi %p\n", spi); + lph8923.spi = spi; + + omapfb_register_panel(&lph8923_panel); + + return 0; +} + +static int lph8923_spi_remove(struct spi_device *spi) +{ + DBGENTER(1); + + lph8923.spi = NULL; + + return 0; +} + +static struct spi_driver lph8923_spi_driver = { + .driver = { + .name = LPH8923_MODULE_NAME, + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + .probe = lph8923_spi_probe, + .remove = __devexit_p(lph8923_spi_remove), +}; + +static int lph8923_drv_init(void) +{ + spi_register_driver(&lph8923_spi_driver); + + return 0; +} +module_init(lph8923_drv_init); + +static void lph8923_drv_cleanup(void) +{ + spi_unregister_driver(&lph8923_spi_driver); +} +module_exit(lph8923_drv_cleanup); + +static void set_spi_data_width(int width) +{ + if (lph8923.spi->bits_per_word != width) { + lph8923.spi->bits_per_word = width; + spi_setup(lph8923.spi); + } +} + +static void lph8923_read(int cmd, u8 *buf, int len) +{ + struct spi_message m; + struct spi_transfer t; + u16 w; + + BUG_ON(lph8923.spi == NULL); + + spi_message_init(&m); + m.spi = lph8923.spi; + + if (len > 1) { + cmd <<= 1; + set_spi_data_width(10); + } else + set_spi_data_width(9); + + w = cmd; + t.cs_change = len ? 1 : 0; + t.tx_buf = &w; + t.rx_buf = NULL; + t.len = 2; + + spi_message_add_tail(&t, &m); + + spi_sync(m.spi, &m); + + if (!len) + return; + + spi_message_init(&m); + m.spi = lph8923.spi; + + t.cs_change = 0; + t.tx_buf = NULL; + t.rx_buf = buf; + t.len = len; + + set_spi_data_width(8); + + spi_message_add_tail(&t, &m); + + spi_sync(m.spi, &m); +} + +static void lph8923_write(int cmd, const u8 *buf, int len) +{ + struct spi_message m; + struct spi_transfer t; + u16 w; + int i; + + BUG_ON(lph8923.spi == NULL); + + spi_message_init(&m); + m.spi = lph8923.spi; + set_spi_data_width(9); + + t.cs_change = 0; + w = cmd; + t.tx_buf = &w; + t.rx_buf = NULL; + t.len = 2; + + spi_message_add_tail(&t, &m); + spi_sync(m.spi, &m); + + if (!len) + return; + + t.tx_buf = &w; + for (i = 0; i < len; i++) { + spi_message_init(&m); + m.spi = lph8923.spi; + spi_message_add_tail(&t, &m); + w = buf[i] | (1 << 8); + spi_sync(m.spi, &m); + } +} + +static inline void lph8923_cmd(int cmd) +{ + lph8923_write(cmd, NULL, 0); +} + +struct cmd_data { + u8 cmd; + u8 len; + const u8 *data; +} __attribute__ ((packed));; + +static const struct cmd_data init_cmds_buggy_lph8923[] = { + { 0xb0, 1, "\x08" }, + { 0xb1, 2, "\x0b\x1c" }, + { 0xb2, 4, "\x00\x00\x00\x00" }, + { 0xb3, 4, "\x00\x00\x00\x00" }, + { 0xb4, 1, "\x87" }, + { 0xb5, 4, "\x37\x07\x37\x07" }, + { 0xb6, 2, "\x64\x24" }, + { 0xb7, 1, "\x90" }, + { 0xb8, 3, "\x10\x11\x20" }, + { 0xb9, 2, "\x31\x02" }, + { 0xba, 3, "\x04\xa3\x9d" }, + { 0xbb, 4, "\x15\xb2\x8c\x00" }, + { 0xc2, 3, "\x02\x00\x00" }, +}; + +static const struct cmd_data init_cmds_non_buggy_lph8923[] = { + { 0xc2, 3, "\x02\x00\x00" }, +}; + +static inline void lph8923_set_16bit_mode(void) +{ + lph8923_write(0x3a, "\x50", 1); +} + +static void lph8923_send_init_string(void) +{ + int c; + const struct cmd_data *cd; + + switch (lph8923.version) { + case LPH8923_VER_BUGGY: + c = sizeof(init_cmds_buggy_lph8923)/sizeof(init_cmds_buggy_lph8923[0]); + cd = init_cmds_buggy_lph8923; + break; + case LPH8923_VER_NON_BUGGY: + default: + c = sizeof(init_cmds_non_buggy_lph8923)/sizeof(init_cmds_non_buggy_lph8923[0]); + cd = init_cmds_non_buggy_lph8923; + break; + } + while (c--) { + lph8923_write(cd->cmd, cd->data, cd->len); + cd++; + } + lph8923_set_16bit_mode(); +} + +static void hw_guard_start(int guard_msec) +{ + lph8923.hw_guard_wait = msecs_to_jiffies(guard_msec); + lph8923.hw_guard_end = jiffies + lph8923.hw_guard_wait; +} + +static void hw_guard_wait(void) +{ + unsigned long wait = lph8923.hw_guard_end - jiffies; + + if ((long)wait > 0 && wait <= lph8923.hw_guard_wait) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(wait); + } +} + +static void lph8923_set_sleep_mode(int on) +{ + int cmd, sleep_time = 5; + + if (on) + cmd = LPH8923_CMD_SLEEP_IN; + else + cmd = LPH8923_CMD_SLEEP_OUT; + hw_guard_wait(); + lph8923_cmd(cmd); + hw_guard_start(120); + /* When we enable the panel, it seems we _have_ to sleep + * 120 ms before sending the init string */ + if (!on) + sleep_time = 120; + msleep(sleep_time); +} + +static void lph8923_set_display_state(int enabled) +{ + int cmd = enabled ? LPH8923_CMD_DISP_ON : LPH8923_CMD_DISP_OFF; + + lph8923_cmd(cmd); +} + +static void lph8923_detect(void) +{ + lph8923_read(LPH8923_CMD_READ_DISP_ID, lph8923.display_id, 3); + printk(KERN_INFO "Moscow display id: %02x%02x%02x\n", + lph8923.display_id[0], lph8923.display_id[1], + lph8923.display_id[2]); + + if (lph8923.display_id[0] == 0x45) { + lph8923.version = LPH8923_VER_NON_BUGGY; + printk(KERN_INFO "Non-buggy Moscow detected\n"); + return; + } else { + lph8923.version = LPH8923_VER_BUGGY; + printk(KERN_INFO "Buggy Moscow detected\n"); + } +} + +static int lph8923_enabled(void) +{ + u32 disp_status; + int enabled; + + lph8923_read(LPH8923_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4); + disp_status = __be32_to_cpu(disp_status); + enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); + DBGPRINT(1, ": panel %senabled by bootloader (status 0x%04x)\n", + enabled ? "" : "not ", disp_status); + return enabled; +} + +static int lph8923_panel_init(struct omapfb_device *fbdev) +{ + lph8923.fbdev = fbdev; + + lph8923.enabled = 1; + lph8923_detect(); + if (lph8923.version == LPH8923_VER_NON_BUGGY) + lph8923.enabled = lph8923_enabled(); + else + /* We can't be sure, but assume the bootloader + * enabled it already */ + lph8923.enabled = 1; + + return 0; +} + +static void lph8923_panel_cleanup(void) +{ +} + +static int lph8923_panel_set_bklight_level(unsigned int level) +{ + if (level > tahvo_get_max_backlight_level()) + return -EINVAL; + if (!lph8923.enabled) { + lph8923.saved_bklight_level = level; + return 0; + } + tahvo_set_backlight_level(level); + + return 0; +} + +static unsigned int lph8923_panel_get_bklight_level(void) +{ + return tahvo_get_backlight_level(); +} + +static unsigned int lph8923_panel_get_bklight_max(void) +{ + return tahvo_get_max_backlight_level(); +} + +static int lph8923_panel_enable(void) +{ + if (lph8923.enabled) + return 0; + + lph8923_set_sleep_mode(0); + lph8923.enabled = 1; + lph8923_send_init_string(); + lph8923_set_display_state(1); + lph8923_panel_set_bklight_level(lph8923.saved_bklight_level); + + return 0; +} + +static void lph8923_panel_disable(void) +{ + if (!lph8923.enabled) + return; + lph8923.saved_bklight_level = lph8923_panel_get_bklight_level(); + lph8923_panel_set_bklight_level(0); + lph8923_set_display_state(0); + lph8923_set_sleep_mode(1); + lph8923.enabled = 0; +} + +static unsigned long lph8923_panel_get_caps(void) +{ + return OMAPFB_CAPS_SET_BACKLIGHT; +} + +static u16 read_first_pixel(void) +{ + u8 b; + u16 pixel; + + lph8923_read(LPH8923_CMD_READ_RED, &b, 1); + pixel = (b >> 1) << 11; + lph8923_read(LPH8923_CMD_READ_GREEN, &b, 1); + pixel |= b << 5; + lph8923_read(LPH8923_CMD_READ_BLUE, &b, 1); + pixel |= (b >> 1); + + return pixel; +} + +static int lph8923_panel_test(int test_num) +{ + static const u16 test_values[4] = { + 0x0000, 0xffff, 0xaaaa, 0x5555, + }; + int i; + + if (test_num != LCD_LPH8923_TEST_RGB_LINES) + return LCD_LPH8923_TEST_INVALID; + + for (i = 0; i < ARRAY_SIZE(test_values); i++) { + int delay; + unsigned long tmo; + + omapfb_write_first_pixel(lph8923.fbdev, test_values[i]); + tmo = jiffies + msecs_to_jiffies(100); + delay = msecs_to_jiffies(25); + while (1) { + u16 pixel; + + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(delay); + pixel = read_first_pixel(); + if (pixel == test_values[i]) + break; + if (time_after(jiffies, tmo)) { + printk(KERN_ERR "Moscow RGB I/F test failed: " + "expecting %04x, got %04x\n", + test_values[i], pixel); + return LCD_LPH8923_TEST_FAILED; + } + delay = msecs_to_jiffies(10); + } + } + + return 0; +} + +static struct lcd_panel lph8923_panel = { + .name = "lph8923", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 16, + .x_res = 800, + .y_res = 480, + .pixel_clock = 21940, + .hsw = 50, + .hfp = 20, + .hbp = 15, + .vsw = 2, + .vfp = 1, + .vbp = 3, + + .init = lph8923_panel_init, + .cleanup = lph8923_panel_cleanup, + .enable = lph8923_panel_enable, + .disable = lph8923_panel_disable, + .get_caps = lph8923_panel_get_caps, + .set_bklight_level= lph8923_panel_set_bklight_level, + .get_bklight_level= lph8923_panel_get_bklight_level, + .get_bklight_max= lph8923_panel_get_bklight_max, + .run_test = lph8923_panel_test, +}; diff --git a/drivers/video/omap/lcd_osk.c b/drivers/video/omap/lcd_osk.c new file mode 100644 index 00000000000..5751ec6d325 --- /dev/null +++ b/drivers/video/omap/lcd_osk.c @@ -0,0 +1,165 @@ +/* + * File: drivers/video/omap/lcd-osk.c + * + * LCD panel support for the TI OMAP OSK board + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak + * Adapted for OSK by + * + * 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 + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +static int osk_panel_init(struct omapfb_device *fbdev) +{ + DBGENTER(1); + DBGLEAVE(1); + return 0; +} + +static void osk_panel_cleanup(void) +{ + DBGENTER(1); + DBGLEAVE(1); +} + +static int osk_panel_enable(void) +{ + DBGENTER(1); + + /* configure PWL pin */ + omap_cfg_reg(PWL); + + /* Enable PWL unit */ + omap_writeb(0x01, OMAP16XX_PWL_CLK_ENABLE); + + /* Set PWL level */ + omap_writeb(0xFF, OMAP16XX_PWL_ENABLE); + + /* configure GPIO2 as output */ + omap_set_gpio_direction(2, 0); + + /* set GPIO2 high */ + omap_set_gpio_dataout(2, 1); + + DBGLEAVE(1); + return 0; +} + +static void osk_panel_disable(void) +{ + DBGENTER(1); + + /* Set PWL level to zero */ + omap_writeb(0x00, OMAP16XX_PWL_ENABLE); + + /* Disable PWL unit */ + omap_writeb(0x00, OMAP16XX_PWL_CLK_ENABLE); + + /* set GPIO2 low */ + omap_set_gpio_dataout(2, 0); + + DBGLEAVE(1); +} + +static unsigned long osk_panel_get_caps(void) +{ + return 0; +} + +struct lcd_panel osk_panel = { + .name = "osk", + .config = OMAP_LCDC_PANEL_TFT, + + .bpp = 16, + .data_lines = 16, + .x_res = 240, + .y_res = 320, + .pixel_clock = 12500, + .hsw = 40, + .hfp = 40, + .hbp = 72, + .vsw = 1, + .vfp = 1, + .vbp = 0, + .pcd = 12, + + .init = osk_panel_init, + .cleanup = osk_panel_cleanup, + .enable = osk_panel_enable, + .disable = osk_panel_disable, + .get_caps = osk_panel_get_caps, +}; + +static int osk_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&osk_panel); + return 0; +} + +static int osk_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int osk_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int osk_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver osk_panel_driver = { + .probe = osk_panel_probe, + .remove = osk_panel_remove, + .suspend = osk_panel_suspend, + .resume = osk_panel_resume, + .driver = { + .name = "lcd_osk", + .owner = THIS_MODULE, + }, +}; + +static int osk_panel_drv_init(void) +{ + return platform_driver_register(&osk_panel_driver); +} + +static void osk_panel_drv_cleanup(void) +{ + platform_driver_unregister(&osk_panel_driver); +} + +module_init(osk_panel_drv_init); +module_exit(osk_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_p2.c b/drivers/video/omap/lcd_p2.c new file mode 100644 index 00000000000..3f03dca42a6 --- /dev/null +++ b/drivers/video/omap/lcd_p2.c @@ -0,0 +1,357 @@ +/* + * File: drivers/video/omap/lcd-p2.c + * + * LCD panel support for the TI OMAP P2 board + * + * Authors: + * jekyll + * B Jp + * Brian Swetland + * + * 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 + +/* + * File: epson-md-tft.h + * + * This file contains definitions for Epsons MD-TF LCD Module + * + * Copyright (C) 2004 MPC-Data Limited (http://www.mpc-data.co.uk) + * Author: Dave Peverley + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please report all bugs and problems to the author. + * + */ + +/* LCD uWire commands & params + * All values from Epson + */ +#define LCD_DISON 0xAF +#define LCD_DISOFF 0xAE +#define LCD_DISNOR 0xA6 +#define LCD_DISINV 0xA7 +#define LCD_DISCTL 0xCA +#define LCD_GCP64 0xCB +#define LCD_GCP16 0xCC +#define LCD_GSSET 0xCD +#define LCD_SLPIN 0x95 +#define LCD_SLPOUT 0x94 +#define LCD_SD_PSET 0x75 +#define LCD_MD_PSET 0x76 +#define LCD_SD_CSET 0x15 +#define LCD_MD_CSET 0x16 +#define LCD_DATCTL 0xBC +#define LCD_RAMWR 0x5C +#define LCD_RAMRD 0x5D +#define LCD_PTLIN 0xA8 +#define LCD_PTLOUT 0xA9 +#define LCD_ASCSET 0xAA +#define LCD_SCSTART 0xAB +#define LCD_VOLCTL 0xC6 +#define LCD_NOP 0x25 +#define LCD_OSCISEL 0x7 +#define LCD_3500KSET 0xD1 +#define LCD_3500KEND 0xD2 +#define LCD_14MSET 0xD3 +#define LCD_14MEND 0xD4 + +#define INIT_3500KSET 0x45 +#define INIT_14MSET 0x4B +#define INIT_DATCTL 0x08 /* 6.6.6 bits for D-Sample */ + +#define INIT_OSCISEL 0x05 + +#define INIT_VOLCTL 0x77 /* Nominel "volume" */ + +#define INIT_VOLCTL_Ton 0x98 /* Activate power-IC timer */ +#define INIT_GSSET 0x00 + +const unsigned short INIT_DISCTL[11] = +{ + 0xDE, 0x01, 0x64, 0x00, 0x1B, 0xF4, 0x00, 0xDC, 0x00, 0x02, 0x00 +}; + +const unsigned short INIT_GCP64[126] = +{ + 0x3B,0x00,0x42,0x00,0x4A,0x00,0x51,0x00, + 0x58,0x00,0x5F,0x00,0x66,0x00,0x6E,0x00, + 0x75,0x00,0x7C,0x00,0x83,0x00,0x8A,0x00, + 0x92,0x00,0x99,0x00,0xA0,0x00,0xA7,0x00, + 0xAE,0x00,0xB6,0x00,0xBD,0x00,0xC4,0x00, + 0xCB,0x00,0xD2,0x00,0xDA,0x00,0xE1,0x00, + 0xE8,0x00,0xEF,0x00,0xF6,0x00,0xFE,0x00, + 0x05,0x01,0x0C,0x01,0x13,0x01,0x1A,0x01, + 0x22,0x01,0x29,0x01,0x30,0x01,0x37,0x01, + 0x3E,0x01,0x46,0x01,0x4D,0x01,0x54,0x01, + 0x5B,0x01,0x62,0x01,0x6A,0x01,0x71,0x01, + 0x78,0x01,0x7F,0x01,0x86,0x01,0x8E,0x01, + 0x95,0x01,0x9C,0x01,0xA3,0x01,0xAA,0x01, + 0xB2,0x01,0xB9,0x01,0xC0,0x01,0xC7,0x01, + 0xCE,0x01,0xD6,0x01,0xDD,0x01,0xE4,0x01, + 0xEB,0x01,0xF2,0x01,0xFA,0x01 +}; + +const unsigned short INIT_GCP16[15] = +{ + 0x1A,0x31,0x48,0x54,0x5F,0x67,0x70,0x76,0x7C,0x80,0x83,0x84,0x85,0x87,0x96 +}; + +const unsigned short INIT_MD_PSET[4] = { 0, 0, 219, 0 }; +const unsigned short INIT_MD_CSET[4] = { 2, 0, 177, 0 }; + +const unsigned short INIT_SD_PSET[4] = { 0x00, 0x01, 0x00, 0x01 }; +const unsigned short INIT_SD_CSET[4] = { 0x00, 0x02, 0x00, 0x02 }; + +const unsigned short INIT_ASCSET[7] = { 0x00, 0x00, 0xDB, 0x00, 0xDC, 0x00, 0x01 }; +const unsigned short INIT_SCSTART[2] = { 0x00, 0x00 }; + +/* ----- end of epson_md_tft.h ----- */ + + +#include "debug.h" +#include "../drivers/ssi/omap-uwire.h" + +#define LCD_UWIRE_CS 0 + +static int p2_panel_init(struct omapfb_device *fbdev) +{ + DBGENTER(1); + DBGLEAVE(1); + return 0; +} + +static void p2_panel_cleanup(void) +{ + DBGENTER(1); + DBGLEAVE(1); +} + +static int p2_panel_enable(void) +{ + int i; + unsigned long value; + DBGENTER(1); + + /* thwack the reset line */ + omap_set_gpio_direction(19, 0); + omap_set_gpio_dataout(19, 0); + mdelay(2); + omap_set_gpio_dataout(19, 1); + + /* bits 31:28 -> 0 LCD_PXL_15 .. 12 */ + value = omap_readl(OMAP730_IO_CONF_3) & 0x0FFFFFFF; + omap_writel(value, OMAP730_IO_CONF_3); + + /* bits 19:0 -> 0 LCD_VSYNC, AC, PXL_0, PCLK, HSYNC, + ** PXL_9..1, PXL_10, PXL_11 + */ + value = omap_readl(OMAP730_IO_CONF_4) & 0xFFF00000; + omap_writel(value, OMAP730_IO_CONF_4); + + omap_uwire_configure_mode(0,16); + + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISOFF, 9, 0,NULL,1); + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SLPIN, 9, 0,NULL,1); + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISNOR, 9, 0,NULL,1); + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_GSSET, 9, 0,NULL,1); + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_GSSET | 0x100), 9, 0,NULL,1); + + /* DISCTL */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISCTL, 9, 0,NULL,1); + for (i = 0; i < (sizeof(INIT_DISCTL)/sizeof(unsigned short)); i++) + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_DISCTL[i] | 0x100), 9, 0,NULL,1); + + /* GCP64 */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_GCP64, 9, 0,NULL,1); + for (i = 0; i < (sizeof(INIT_GCP64)/sizeof(unsigned short)); i++) + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_GCP64[i] | 0x100), 9, 0,NULL,1); + + /* GCP16 */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_GCP16, 9, 0,NULL,1); + for (i = 0; i < (sizeof(INIT_GCP16)/sizeof(unsigned short)); i++) + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_GCP16[i] | 0x100), 9, 0,NULL,1); + + /* MD_CSET */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_MD_CSET, 9, 0,NULL,1); + for (i = 0; i < (sizeof(INIT_MD_CSET)/sizeof(unsigned short)); i++) + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_MD_CSET[i] | 0x100), 9, 0,NULL,1); + + /* MD_PSET */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_MD_PSET, 9, 0,NULL,1); + for (i = 0; i < (sizeof(INIT_MD_PSET)/sizeof(unsigned short)); i++) + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_MD_PSET[i] | 0x100), 9, 0,NULL,1); + + /* SD_CSET */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SD_CSET, 9, 0,NULL,1); + for (i = 0; i < (sizeof(INIT_SD_CSET)/sizeof(unsigned short)); i++) + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_SD_CSET[i] | 0x100), 9, 0,NULL,1); + + /* SD_PSET */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SD_PSET, 9, 0,NULL,1); + for (i = 0; i < (sizeof(INIT_SD_PSET)/sizeof(unsigned short)); i++) + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_SD_PSET[i] | 0x100), 9, 0,NULL,1); + + /* DATCTL */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DATCTL, 9, 0,NULL,1); + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_DATCTL | 0x100), 9, 0,NULL,1); + + /* OSSISEL = d'5 */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_OSCISEL, 9, 0,NULL,1); + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_OSCISEL | 0x100), 9, 0,NULL,1); + + /* 14MSET = d'74 */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_14MSET, 9, 0,NULL,1); + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_14MSET | 0x100), 9, 0,NULL,1); + + /* 14MEND */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_14MEND, 9, 0,NULL,1); + + /* 3500KSET = d'69 */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_3500KSET, 9, 0,NULL,1); + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_3500KSET | 0x100), 9, 0,NULL,1); + + /* 3500KEND */ + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_3500KEND, 9, 0,NULL,1); + + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_SLPOUT, 9, 0,NULL,1); + + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_VOLCTL, 9, 0,NULL,1); + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_VOLCTL_Ton | 0x100), 9, 0,NULL,1); + + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_VOLCTL, 9, 0,NULL,1); + + omap_uwire_data_transfer(LCD_UWIRE_CS, (INIT_VOLCTL | 0x100), 9, 0,NULL,1); + + omap_uwire_data_transfer(LCD_UWIRE_CS, LCD_DISON, 9, 0,NULL,1); + + /* enable backlight */ + omap_set_gpio_direction(134, 0); + omap_set_gpio_dataout(134, 1); + + DBGLEAVE(1); + return 0; +} + +static void p2_panel_disable(void) +{ + DBGENTER(1); + DBGLEAVE(1); +} + +static unsigned long p2_panel_get_caps(void) +{ + return 0; +} + +struct lcd_panel p2_panel = { + .name = "p2", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_PIX_CLOCK, + + .bpp = 16, + .data_lines = 16, + .x_res = 176, + .y_res = 220, + .pixel_clock = 12500, + .hsw = 5, + .hfp = 1, + .hbp = 1, + .vsw = 2, + .vfp = 12, + .vbp = 1, + + .init = p2_panel_init, + .cleanup = p2_panel_cleanup, + .enable = p2_panel_enable, + .disable = p2_panel_disable, + .get_caps = p2_panel_get_caps, +}; + +static int p2_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&p2_panel); + return 0; +} + +static int p2_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int p2_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int p2_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver p2_panel_driver = { + .probe = p2_panel_probe, + .remove = p2_panel_remove, + .suspend = p2_panel_suspend, + .resume = p2_panel_resume, + .driver = { + .name = "lcd_p2", + .owner = THIS_MODULE, + }, +}; + +static int p2_panel_drv_init(void) +{ + return platform_driver_register(&p2_panel_driver); +} + +static void p2_panel_drv_cleanup(void) +{ + platform_driver_unregister(&p2_panel_driver); +} + +module_init(p2_panel_drv_init); +module_exit(p2_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcd_palmte.c b/drivers/video/omap/lcd_palmte.c new file mode 100644 index 00000000000..5cf3851f4e2 --- /dev/null +++ b/drivers/video/omap/lcd_palmte.c @@ -0,0 +1,141 @@ +/* + * File: drivers/video/omap/lcd_palmte.c + * + * LCD panel support for the Palm Tungsten E + * + * Original version : Romain Goyet + * Current version : Laurent Gonzalez + * + * 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 + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +static int palmte_panel_init(struct lcd_panel *panel) +{ + DBGENTER(1); + DBGLEAVE(1); + return 0; +} + +static void palmte_panel_cleanup(struct lcd_panel *panel) +{ + DBGENTER(1); + DBGLEAVE(1); +} + +static int palmte_panel_enable(struct lcd_panel *panel) +{ + DBGENTER(1); + DBGLEAVE(1); + return 0; +} + +static void palmte_panel_disable(struct lcd_panel *panel) +{ + DBGENTER(1); + DBGLEAVE(1); +} + +static unsigned long palmte_panel_get_caps(struct lcd_panel *panel) +{ + return 0; +} + +struct lcd_panel palmte_panel = { + .name = "palmte", + .config = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC | + OMAP_LCDC_INV_HSYNC | OMAP_LCDC_HSVS_RISING_EDGE | + OMAP_LCDC_HSVS_OPPOSITE, + + .data_lines = 16, + .bpp = 8, + .pixel_clock = 12000, + .x_res = 320, + .y_res = 320, + .hsw = 4, + .hfp = 8, + .hbp = 28, + .vsw = 1, + .vfp = 8, + .vbp = 7, + .pcd = 0, + + .init = palmte_panel_init, + .cleanup = palmte_panel_cleanup, + .enable = palmte_panel_enable, + .disable = palmte_panel_disable, + .get_caps = palmte_panel_get_caps, +}; + +static int palmte_panel_probe(struct platform_device *pdev) +{ + DBGENTER(1); + omapfb_register_panel(&palmte_panel); + return 0; +} + +static int palmte_panel_remove(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +static int palmte_panel_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + DBGENTER(1); + return 0; +} + +static int palmte_panel_resume(struct platform_device *pdev) +{ + DBGENTER(1); + return 0; +} + +struct platform_driver palmte_panel_driver = { + .probe = palmte_panel_probe, + .remove = palmte_panel_remove, + .suspend = palmte_panel_suspend, + .resume = palmte_panel_resume, + .driver = { + .name = "lcd_palmte", + .owner = THIS_MODULE, + }, +}; + +static int palmte_panel_drv_init(void) +{ + return platform_driver_register(&palmte_panel_driver); +} + +static void palmte_panel_drv_cleanup(void) +{ + platform_driver_unregister(&palmte_panel_driver); +} + +module_init(palmte_panel_drv_init); +module_exit(palmte_panel_drv_cleanup); + diff --git a/drivers/video/omap/lcdc.c b/drivers/video/omap/lcdc.c new file mode 100644 index 00000000000..bfe94784cbf --- /dev/null +++ b/drivers/video/omap/lcdc.c @@ -0,0 +1,919 @@ +/* + * File: drivers/video/omap/omap1/lcdc.c + * + * OMAP1 internal LCD controller + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak + * + * 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 + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +#define MODULE_NAME "omapfb-lcdc" + +#define OMAP_LCDC_BASE 0xfffec000 +#define OMAP_LCDC_SIZE 256 +#define OMAP_LCDC_IRQ INT_LCD_CTRL + +#define OMAP_LCDC_CONTROL (OMAP_LCDC_BASE + 0x00) +#define OMAP_LCDC_TIMING0 (OMAP_LCDC_BASE + 0x04) +#define OMAP_LCDC_TIMING1 (OMAP_LCDC_BASE + 0x08) +#define OMAP_LCDC_TIMING2 (OMAP_LCDC_BASE + 0x0c) +#define OMAP_LCDC_STATUS (OMAP_LCDC_BASE + 0x10) +#define OMAP_LCDC_SUBPANEL (OMAP_LCDC_BASE + 0x14) +#define OMAP_LCDC_LINE_INT (OMAP_LCDC_BASE + 0x18) +#define OMAP_LCDC_DISPLAY_STATUS (OMAP_LCDC_BASE + 0x1c) + +#define OMAP_LCDC_STAT_DONE (1 << 0) +#define OMAP_LCDC_STAT_VSYNC (1 << 1) +#define OMAP_LCDC_STAT_SYNC_LOST (1 << 2) +#define OMAP_LCDC_STAT_ABC (1 << 3) +#define OMAP_LCDC_STAT_LINE_INT (1 << 4) +#define OMAP_LCDC_STAT_FUF (1 << 5) +#define OMAP_LCDC_STAT_LOADED_PALETTE (1 << 6) + +#define OMAP_LCDC_CTRL_LCD_EN (1 << 0) +#define OMAP_LCDC_CTRL_LCD_TFT (1 << 7) +#define OMAP_LCDC_CTRL_LINE_IRQ_CLR_SEL (1 << 10) + +#define OMAP_LCDC_IRQ_VSYNC (1 << 2) +#define OMAP_LCDC_IRQ_DONE (1 << 3) +#define OMAP_LCDC_IRQ_LOADED_PALETTE (1 << 4) +#define OMAP_LCDC_IRQ_LINE_NIRQ (1 << 5) +#define OMAP_LCDC_IRQ_LINE (1 << 6) +#define OMAP_LCDC_IRQ_MASK (((1 << 5) - 1) << 2) + +#define MAX_PALETTE_SIZE PAGE_SIZE + +#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) + +enum lcdc_load_mode { + OMAP_LCDC_LOAD_PALETTE, + OMAP_LCDC_LOAD_FRAME, + OMAP_LCDC_LOAD_PALETTE_AND_FRAME +}; + +static struct omap_lcd_controller { + enum omapfb_update_mode update_mode; + int ext_mode; + + unsigned long frame_offset; + int screen_width; + int xres; + int yres; + + enum omapfb_color_format color_mode; + int bpp; + void *palette_virt; + dma_addr_t palette_phys; + int palette_code; + int palette_size; + + unsigned int irq_mask; + struct completion last_frame_complete; + struct completion palette_load_complete; + struct clk *lcd_ck; + struct omapfb_device *fbdev; + + void (*dma_callback)(void *data); + void *dma_callback_data; + + int fbmem_allocated; + dma_addr_t vram_phys; + void *vram_virt; + unsigned long vram_size; +} omap_lcdc; + +static void inline enable_irqs(int mask) +{ + omap_lcdc.irq_mask |= mask; +} + +static void inline disable_irqs(int mask) +{ + omap_lcdc.irq_mask &= ~mask; +} + +static void set_load_mode(enum lcdc_load_mode mode) +{ + u32 l; + + l = omap_readl(OMAP_LCDC_CONTROL); + l &= ~(3 << 20); + switch (mode) { + case OMAP_LCDC_LOAD_PALETTE: + l |= 1 << 20; + break; + case OMAP_LCDC_LOAD_FRAME: + l |= 2 << 20; + break; + case OMAP_LCDC_LOAD_PALETTE_AND_FRAME: + break; + default: + BUG(); + } + omap_writel(l, OMAP_LCDC_CONTROL); +} + +static void enable_controller(void) +{ + u32 l; + + l = omap_readl(OMAP_LCDC_CONTROL); + l |= OMAP_LCDC_CTRL_LCD_EN; + l &= ~OMAP_LCDC_IRQ_MASK; + l |= omap_lcdc.irq_mask | OMAP_LCDC_IRQ_DONE; /* enabled IRQs */ + omap_writel(l, OMAP_LCDC_CONTROL); +} + +static void disable_controller_async(void) +{ + u32 l; + u32 mask; + + l = omap_readl(OMAP_LCDC_CONTROL); + mask = OMAP_LCDC_CTRL_LCD_EN | OMAP_LCDC_IRQ_MASK; + /* Preserve the DONE mask, since we still want to get the + * final DONE irq. It will be disabled in the IRQ handler. + */ + mask &= ~OMAP_LCDC_IRQ_DONE; + l &= ~mask; + omap_writel(l, OMAP_LCDC_CONTROL); +} + +static void disable_controller(void) +{ + init_completion(&omap_lcdc.last_frame_complete); + disable_controller_async(); + if (!wait_for_completion_timeout(&omap_lcdc.last_frame_complete, + msecs_to_jiffies(500))) + pr_err("timeout waiting for FRAME DONE\n"); +} + +static void reset_controller(u32 status) +{ + static unsigned long reset_count = 0; + static unsigned long last_jiffies = 0; + + disable_controller_async(); + reset_count++; + if (reset_count == 1 || time_after(jiffies, last_jiffies + HZ)) { + pr_err("resetting (status %#010x,reset count %lu)\n", + status, reset_count); + last_jiffies = jiffies; + } + if (reset_count < 100) { + enable_controller(); + } else { + reset_count = 0; + pr_err("too many reset attempts, giving up.\n"); + } +} + +/* Configure the LCD DMA according to the current mode specified by parameters + * in omap_lcdc.fbdev and fbdev->var. + */ +static void setup_lcd_dma(void) +{ + static const int dma_elem_type[] = { + 0, + OMAP_DMA_DATA_TYPE_S8, + OMAP_DMA_DATA_TYPE_S16, + 0, + OMAP_DMA_DATA_TYPE_S32, + }; + struct fb_var_screeninfo *var = &omap_lcdc.fbdev->fb_info->var; + unsigned long src; + int esize, xelem, yelem; + + src = omap_lcdc.vram_phys + omap_lcdc.frame_offset; + + switch (var->rotate) { + case 0: + if (omap_lcdc.fbdev->mirror || (src & 3) || + omap_lcdc.color_mode == OMAPFB_COLOR_YUV420 || + (omap_lcdc.xres & 1)) + esize = 2; + else + esize = 4; + xelem = omap_lcdc.xres * omap_lcdc.bpp / 8 / esize; + yelem = omap_lcdc.yres; + break; + case 90: + case 180: + case 270: + if (cpu_is_omap15xx()) { + BUG(); + } + esize = 2; + xelem = omap_lcdc.yres * omap_lcdc.bpp / 16; + yelem = omap_lcdc.xres; + break; + default: + BUG(); + return; + } + DBGPRINT(2, "setup_dma: src %#010lx esize %d xelem %d yelem %d\n", + src, esize, xelem, yelem); + omap_set_lcd_dma_b1(src, xelem, yelem, dma_elem_type[esize]); + if (!cpu_is_omap15xx()) { + int bpp = omap_lcdc.bpp; + + /* YUV support is only for external mode when we have the + * YUV window embedded in a 16bpp frame buffer. + */ + if (omap_lcdc.color_mode == OMAPFB_COLOR_YUV420) + bpp = 16; + /* Set virtual xres elem size */ + omap_set_lcd_dma_b1_vxres( + omap_lcdc.screen_width * bpp / 8 / esize); + /* Setup transformations */ + omap_set_lcd_dma_b1_rotation(var->rotate); + omap_set_lcd_dma_b1_mirror(omap_lcdc.fbdev->mirror); + } + omap_setup_lcd_dma(); +} + +static irqreturn_t lcdc_irq_handler(int irq, void *dev_id, struct pt_regs *fp) +{ + u32 status; + + status = omap_readl(OMAP_LCDC_STATUS); + + if (status & (OMAP_LCDC_STAT_FUF | OMAP_LCDC_STAT_SYNC_LOST)) + reset_controller(status); + else { + if (status & OMAP_LCDC_STAT_DONE) { + u32 l; + + /* Disable IRQ_DONE. The status bit will be cleared + * only when the controller is reenabled and we don't + * want to get more interrupts. + */ + l = omap_readl(OMAP_LCDC_CONTROL); + l &= ~OMAP_LCDC_IRQ_DONE; + omap_writel(l, OMAP_LCDC_CONTROL); + complete(&omap_lcdc.last_frame_complete); + } + if (status & OMAP_LCDC_STAT_LOADED_PALETTE) { + disable_controller_async(); + complete(&omap_lcdc.palette_load_complete); + } + } + + /* Clear these interrupt status bits. + * Sync_lost, FUF bits were cleared by disabling the LCD controller + * LOADED_PALETTE can be cleared this way only in palette only + * load mode. In other load modes it's cleared by disabling the + * controller. + */ + status &= ~(OMAP_LCDC_STAT_VSYNC | + OMAP_LCDC_STAT_LOADED_PALETTE | + OMAP_LCDC_STAT_ABC | + OMAP_LCDC_STAT_LINE_INT); + omap_writel(status, OMAP_LCDC_STATUS); + return IRQ_HANDLED; +} + +/* Change to a new video mode. We defer this to a later time to avoid any + * flicker and not to mess up the current LCD DMA context. For this we disable + * the LCD controler, which will generate a DONE irq after the last frame has + * been transferred. Then it'll be safe to reconfigure both the LCD controller + * as well as the LCD DMA. + */ +static int omap_lcdc_setup_plane(int plane, int channel_out, + unsigned long offset, int screen_width, + int pos_x, int pos_y, int width, int height, + int color_mode) +{ + struct fb_var_screeninfo *var = &omap_lcdc.fbdev->fb_info->var; + struct lcd_panel *panel = omap_lcdc.fbdev->panel; + int rot_x, rot_y; + + DBGENTER(2); + + if (var->rotate == 0) { + rot_x = panel->x_res; + rot_y = panel->y_res; + } else { + rot_x = panel->y_res; + rot_y = panel->x_res; + } + if (plane != 0 || channel_out != 0 || pos_x != 0 || pos_y != 0 || + width > rot_x || height > rot_y) { + DBGPRINT(1, "invalid plane params plane %d pos_x %d " + "pos_y %d w %d h %d\n", plane, pos_x, pos_y, + width, height); + return -EINVAL; + } + + omap_lcdc.frame_offset = offset; + omap_lcdc.xres = width; + omap_lcdc.yres = height; + omap_lcdc.screen_width = screen_width; + omap_lcdc.color_mode = color_mode; + + switch (color_mode) { + case OMAPFB_COLOR_CLUT_8BPP: + omap_lcdc.bpp = 8; + omap_lcdc.palette_code = 0x3000; + omap_lcdc.palette_size = 512; + break; + case OMAPFB_COLOR_RGB565: + omap_lcdc.bpp = 16; + omap_lcdc.palette_code = 0x4000; + omap_lcdc.palette_size = 32; + break; + case OMAPFB_COLOR_YUV420: + if (omap_lcdc.ext_mode) { + omap_lcdc.bpp = 12; + break; + } + /* fallthrough */ + case OMAPFB_COLOR_YUV422: + if (omap_lcdc.ext_mode) { + omap_lcdc.bpp = 16; + break; + } + /* fallthrough */ + default: + /* FIXME: other BPPs. + * bpp1: code 0, size 256 + * bpp2: code 0x1000 size 256 + * bpp4: code 0x2000 size 256 + * bpp12: code 0x4000 size 32 + */ + DBGPRINT(1, "invalid color mode %d\n", color_mode); + return -1; + } + + if (omap_lcdc.ext_mode) { + setup_lcd_dma(); + return 0; + } + + if (omap_lcdc.update_mode == OMAPFB_AUTO_UPDATE) { + disable_controller(); + omap_stop_lcd_dma(); + setup_lcd_dma(); + enable_controller(); + } + + DBGLEAVE(2); + + return 0; +} + +static int omap_lcdc_enable_plane(int plane, int enable) +{ + DBGPRINT(2, "plane %d enable %d update_mode %d ext_mode %d\n", + plane, enable, omap_lcdc.update_mode, + omap_lcdc.ext_mode); + if (plane != OMAPFB_PLANE_GFX) + return -EINVAL; + + return 0; +} + +/* Configure the LCD DMA for a palette load operation and do the palette + * downloading synchronously. We don't use the frame+palette load mode of + * the controller, since the palette can always be downloaded seperately. + */ +static void load_palette(void) +{ + u16 *palette; + + DBGENTER(1); + + palette = (u16 *)omap_lcdc.palette_virt; + + *(u16 *)palette &= 0x0fff; + *(u16 *)palette |= omap_lcdc.palette_code; + + omap_set_lcd_dma_b1(omap_lcdc.palette_phys, + omap_lcdc.palette_size / 4 + 1, 1, OMAP_DMA_DATA_TYPE_S32); + + omap_set_lcd_dma_single_transfer(1); + omap_setup_lcd_dma(); + + init_completion(&omap_lcdc.palette_load_complete); + enable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE); + set_load_mode(OMAP_LCDC_LOAD_PALETTE); + enable_controller(); + if (!wait_for_completion_timeout(&omap_lcdc.palette_load_complete, + msecs_to_jiffies(500))) + pr_err("timeout waiting for FRAME DONE\n"); + /* The controller gets disabled in the irq handler */ + disable_irqs(OMAP_LCDC_IRQ_LOADED_PALETTE); + omap_stop_lcd_dma(); + + omap_set_lcd_dma_single_transfer(omap_lcdc.ext_mode); + + DBGLEAVE(1); +} + +/* Used only in internal controller mode */ +static int omap_lcdc_setcolreg(u_int regno, u16 red, u16 green, u16 blue, + u16 transp, int update_hw_pal) +{ + u16 *palette; + + if (omap_lcdc.color_mode != OMAPFB_COLOR_CLUT_8BPP || regno > 255) + return -EINVAL; + + palette = (u16 *)omap_lcdc.palette_virt; + + palette[regno] &= ~0x0fff; + palette[regno] |= ((red >> 12) << 8) | ((green >> 12) << 4 ) | + (blue >> 12); + + if (update_hw_pal) { + disable_controller(); + omap_stop_lcd_dma(); + load_palette(); + setup_lcd_dma(); + set_load_mode(OMAP_LCDC_LOAD_FRAME); + enable_controller(); + } + + return 0; +} + +static void calc_ck_div(int is_tft, int pck, int *pck_div) +{ + unsigned long lck; + + pck = max(1, pck); + lck = clk_get_rate(omap_lcdc.lcd_ck); + *pck_div = (lck + pck - 1) / pck; + if (is_tft) + *pck_div = max(2, *pck_div); + else + *pck_div = max(3, *pck_div); + if (*pck_div > 255) { + /* FIXME: try to adjust logic clock divider as well */ + *pck_div = 255; + printk(KERN_WARNING MODULE_NAME ": pixclock %d kHz too low.\n", + pck / 1000); + } +} + +static void inline setup_regs(void) +{ + u32 l; + struct lcd_panel *panel = omap_lcdc.fbdev->panel; + int is_tft = panel->config & OMAP_LCDC_PANEL_TFT; + unsigned long lck; + int pcd; + + l = omap_readl(OMAP_LCDC_CONTROL); + l &= ~OMAP_LCDC_CTRL_LCD_TFT; + l |= is_tft ? OMAP_LCDC_CTRL_LCD_TFT : 0; +#ifdef CONFIG_MACH_OMAP_PALMTE +/* FIXME:if (machine_is_omap_palmte()) { */ + /* PalmTE uses alternate TFT setting in 8BPP mode */ + l |= (is_tft && panel->bpp == 8) ? 0x810000 : 0; +/* } */ +#endif + omap_writel(l, OMAP_LCDC_CONTROL); + + l = omap_readl(OMAP_LCDC_TIMING2); + l &= ~(((1 << 6) - 1) << 20); + l |= (panel->config & OMAP_LCDC_SIGNAL_MASK) << 20; + omap_writel(l, OMAP_LCDC_TIMING2); + + l = panel->x_res - 1; + l |= (panel->hsw - 1) << 10; + l |= (panel->hfp - 1) << 16; + l |= (panel->hbp - 1) << 24; + omap_writel(l, OMAP_LCDC_TIMING0); + + l = panel->y_res - 1; + l |= (panel->vsw - 1) << 10; + l |= panel->vfp << 16; + l |= panel->vbp << 24; + omap_writel(l, OMAP_LCDC_TIMING1); + + l = omap_readl(OMAP_LCDC_TIMING2); + l &= ~0xff; + + lck = clk_get_rate(omap_lcdc.lcd_ck); + + if (!panel->pcd) + calc_ck_div(is_tft, panel->pixel_clock * 1000, &pcd); + else { + printk(KERN_WARNING + MODULE_NAME ": Pixel clock divider value is obsolete.\n" + MODULE_NAME ": Try to set pixel_clock to %lu and pcd to 0 " + "in drivers/video/omap/lcd_%s.c and submit a patch.\n", + lck / panel->pcd / 1000, panel->name); + + pcd = panel->pcd; + } + l |= pcd & 0xff; + l |= panel->acb << 8; + omap_writel(l, OMAP_LCDC_TIMING2); + + /* update panel info with the exact clock */ + panel->pixel_clock = lck / pcd / 1000; +} + +/* Configure the LCD controller, download the color palette and start a looped + * DMA transfer of the frame image data. Called only in internal + * controller mode. + */ +static int omap_lcdc_set_update_mode(enum omapfb_update_mode mode) +{ + int r = 0; + + DBGENTER(1); + + if (mode != omap_lcdc.update_mode) { + switch (mode) { + case OMAPFB_AUTO_UPDATE: + setup_regs(); + load_palette(); + + /* Setup and start LCD DMA */ + setup_lcd_dma(); + + set_load_mode(OMAP_LCDC_LOAD_FRAME); + enable_irqs(OMAP_LCDC_IRQ_DONE); + /* This will start the actual DMA transfer */ + enable_controller(); + omap_lcdc.update_mode = mode; + break; + case OMAPFB_UPDATE_DISABLED: + disable_controller(); + omap_stop_lcd_dma(); + omap_lcdc.update_mode = mode; + break; + default: + r = -EINVAL; + } + } + + DBGLEAVE(1); + return r; +} + +static enum omapfb_update_mode omap_lcdc_get_update_mode(void) +{ + return omap_lcdc.update_mode; +} + +/* PM code called only in internal controller mode */ +static void omap_lcdc_suspend(void) +{ + if (omap_lcdc.update_mode == OMAPFB_AUTO_UPDATE) { + disable_controller(); + omap_stop_lcd_dma(); + } +} + +static void omap_lcdc_resume(void) +{ + if (omap_lcdc.update_mode == OMAPFB_AUTO_UPDATE) { + setup_regs(); + load_palette(); + setup_lcd_dma(); + set_load_mode(OMAP_LCDC_LOAD_FRAME); + enable_irqs(OMAP_LCDC_IRQ_DONE); + enable_controller(); + } +} + +static unsigned long omap_lcdc_get_caps(void) +{ + return 0; +} + +static void omap_lcdc_get_vram_layout(unsigned long *size, void **virt, + dma_addr_t *phys) +{ + *size = omap_lcdc.vram_size; + *virt = (u8 *)omap_lcdc.vram_virt; + *phys = omap_lcdc.vram_phys; +} + +int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data) +{ + BUG_ON(callback == NULL); + + if (omap_lcdc.dma_callback) + return -EBUSY; + else { + omap_lcdc.dma_callback = callback; + omap_lcdc.dma_callback_data = data; + } + return 0; +} +EXPORT_SYMBOL(omap_lcdc_set_dma_callback); + +void omap_lcdc_free_dma_callback(void) +{ + omap_lcdc.dma_callback = NULL; +} +EXPORT_SYMBOL(omap_lcdc_free_dma_callback); + +static void lcdc_dma_handler(u16 status, void *data) +{ + DBGENTER(2); + if (omap_lcdc.dma_callback) + omap_lcdc.dma_callback(omap_lcdc.dma_callback_data); +} + +static int mmap_kern(void) +{ + struct vm_struct *kvma; + struct vm_area_struct vma; + pgprot_t pgprot; + unsigned long vaddr; + + DBGENTER(1); + + kvma = get_vm_area(omap_lcdc.vram_size, VM_IOREMAP); + if (kvma == NULL) { + pr_err("can't get kernel vm area\n"); + return -ENOMEM; + } + vma.vm_mm = &init_mm; + + vaddr = (unsigned long)kvma->addr; + vma.vm_start = vaddr; + vma.vm_end = vaddr + omap_lcdc.vram_size; + + pgprot = pgprot_writecombine(pgprot_kernel); + if (io_remap_pfn_range(&vma, vaddr, + omap_lcdc.vram_phys >> PAGE_SHIFT, + omap_lcdc.vram_size, pgprot) < 0) { + pr_err("kernel mmap for FB memory failed\n"); + return -EAGAIN; + } + + omap_lcdc.vram_virt = (void *)vaddr; + + DBGLEAVE(1); + + return 0; +} + +static void unmap_kern(void) +{ + vunmap(omap_lcdc.vram_virt); +} + +static int alloc_palette_ram(void) +{ + omap_lcdc.palette_virt = dma_alloc_writecombine(omap_lcdc.fbdev->dev, + MAX_PALETTE_SIZE, &omap_lcdc.palette_phys, GFP_KERNEL); + if (omap_lcdc.palette_virt == NULL) { + pr_err("failed to alloc palette memory\n"); + return -ENOMEM; + } + memset(omap_lcdc.palette_virt, 0, MAX_PALETTE_SIZE); + + return 0; +} + +static void free_palette_ram(void) +{ + dma_free_writecombine(omap_lcdc.fbdev->dev, MAX_PALETTE_SIZE, + omap_lcdc.palette_virt, omap_lcdc.palette_phys); +} + +static int alloc_fbmem(int req_size) +{ + int frame_size; + struct lcd_panel *panel = omap_lcdc.fbdev->panel; + + frame_size = PAGE_ALIGN(panel->x_res * panel->bpp / 8 * panel->y_res); + if (req_size > frame_size) + frame_size = req_size; + omap_lcdc.vram_size = frame_size; + omap_lcdc.vram_virt = dma_alloc_writecombine(omap_lcdc.fbdev->dev, + omap_lcdc.vram_size, &omap_lcdc.vram_phys, GFP_KERNEL); + + if (omap_lcdc.vram_virt == NULL) { + pr_err("unable to allocate FB DMA memory\n"); + return -ENOMEM; + } + + memset(omap_lcdc.vram_virt, 0, omap_lcdc.vram_size); + + return 0; +} + +static void free_fbmem(void) +{ + dma_free_writecombine(omap_lcdc.fbdev->dev, omap_lcdc.vram_size, + omap_lcdc.vram_virt, omap_lcdc.vram_phys); +} + +static int setup_fbmem(int req_size) +{ + struct lcd_panel *panel = omap_lcdc.fbdev->panel; + struct omapfb_platform_data *conf; + int frame_size; + int r; + + conf = omap_lcdc.fbdev->dev->platform_data; + + if (conf->fbmem.fb_sram_size) { + pr_err("can't use FB SRAM in OMAP1\n"); + return -EINVAL; + } + + if (conf->fbmem.fb_sdram_size == 0) { + omap_lcdc.fbmem_allocated = 1; + if ((r = alloc_fbmem(req_size)) < 0) + return r; + return 0; + } + + frame_size = PAGE_ALIGN(panel->x_res * panel->bpp / 8 * panel->y_res); + + if (conf->fbmem.fb_sdram_size < frame_size) { + pr_err("invalid FB memory configuration\n"); + return -EINVAL; + } + + if (conf->fbmem.fb_sdram_size < req_size) { + pr_err("%d vram was requested, but only %u is available\n", + req_size, conf->fbmem.fb_sdram_size); + } + + omap_lcdc.vram_phys = conf->fbmem.fb_sdram_start; + omap_lcdc.vram_size = conf->fbmem.fb_sdram_size; + + if ((r = mmap_kern()) < 0) + return r; + + DBGPRINT(1, "vram at %08x size %08lx mapped to 0x%p\n", + omap_lcdc.vram_phys, omap_lcdc.vram_size, omap_lcdc.vram_virt); + + return 0; +} + +static void cleanup_fbmem(void) +{ + if (omap_lcdc.fbmem_allocated) + free_fbmem(); + else + unmap_kern(); +} + +static int omap_lcdc_init(struct omapfb_device *fbdev, int ext_mode, + int req_vram_size) +{ + int r; + u32 l; + int rate; + struct clk *tc_ck; + + DBGENTER(1); + + omap_lcdc.irq_mask = 0; + + omap_lcdc.fbdev = fbdev; + omap_lcdc.ext_mode = ext_mode; + + pr_info(MODULE_NAME ": init\n"); + + l = 0; + omap_writel(l, OMAP_LCDC_CONTROL); + + /* FIXME: + * According to errata some platforms have a clock rate limitiation + */ + omap_lcdc.lcd_ck = clk_get(NULL, "lcd_ck"); + if (IS_ERR(omap_lcdc.lcd_ck)) { + pr_err("unable to access LCD clock\n"); + r = PTR_ERR(omap_lcdc.lcd_ck); + goto fail0; + } + + tc_ck = clk_get(NULL, "tc_ck"); + if (IS_ERR(tc_ck)) { + pr_err("unable to access TC clock\n"); + r = PTR_ERR(tc_ck); + goto fail1; + } + + rate = clk_get_rate(tc_ck); + clk_put(tc_ck); + + if (machine_is_omap_h3()) + rate /= 3; + r = clk_set_rate(omap_lcdc.lcd_ck, rate); + if (r) { + pr_err("failed to adjust LCD rate\n"); + goto fail1; + } + clk_enable(omap_lcdc.lcd_ck); + + r = request_irq(OMAP_LCDC_IRQ, lcdc_irq_handler, 0, "omap-lcdc", + omap_lcdc.fbdev); + if (r) { + pr_err("unable to get IRQ\n"); + goto fail2; + } + + r = omap_request_lcd_dma(lcdc_dma_handler, NULL); + if (r) { + pr_err("unable to get LCD DMA\n"); + goto fail3; + } + + omap_set_lcd_dma_single_transfer(ext_mode); + omap_set_lcd_dma_ext_controller(ext_mode); + + if (!ext_mode) + if ((r = alloc_palette_ram()) < 0) + goto fail4; + + req_vram_size = 1024 * 1024; + if ((r = setup_fbmem(req_vram_size)) < 0) + goto fail5; + + + DBGLEAVE(1); + return 0; +fail5: + if (!ext_mode) + free_palette_ram(); +fail4: + omap_free_lcd_dma(); +fail3: + free_irq(OMAP_LCDC_IRQ, omap_lcdc.fbdev); +fail2: + clk_disable(omap_lcdc.lcd_ck); +fail1: + clk_put(omap_lcdc.lcd_ck); +fail0: + DBGLEAVE(1); + return r; +} + +static void omap_lcdc_cleanup(void) +{ + if (!omap_lcdc.ext_mode) + free_palette_ram(); + cleanup_fbmem(); + omap_free_lcd_dma(); + free_irq(OMAP_LCDC_IRQ, omap_lcdc.fbdev); + clk_disable(omap_lcdc.lcd_ck); + clk_put(omap_lcdc.lcd_ck); +} + +struct lcd_ctrl omap1_int_ctrl = { + .name = "internal", + .init = omap_lcdc_init, + .cleanup = omap_lcdc_cleanup, + .get_vram_layout = omap_lcdc_get_vram_layout, + .get_caps = omap_lcdc_get_caps, + .set_update_mode = omap_lcdc_set_update_mode, + .get_update_mode = omap_lcdc_get_update_mode, + .update_window = NULL, + .suspend = omap_lcdc_suspend, + .resume = omap_lcdc_resume, + .setup_plane = omap_lcdc_setup_plane, + .enable_plane = omap_lcdc_enable_plane, + .setcolreg = omap_lcdc_setcolreg, +}; + +MODULE_DESCRIPTION("TI OMAP LCDC controller"); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap/lcdc.h b/drivers/video/omap/lcdc.h new file mode 100644 index 00000000000..adb731e5314 --- /dev/null +++ b/drivers/video/omap/lcdc.h @@ -0,0 +1,7 @@ +#ifndef LCDC_H +#define LCDC_H + +int omap_lcdc_set_dma_callback(void (*callback)(void *data), void *data); +void omap_lcdc_free_dma_callback(void); + +#endif diff --git a/drivers/video/omap/omapfb_main.c b/drivers/video/omap/omapfb_main.c new file mode 100644 index 00000000000..d32ba58ddd7 --- /dev/null +++ b/drivers/video/omap/omapfb_main.c @@ -0,0 +1,1562 @@ +/* + * File: drivers/video/omap/omapfb_main.c + * + * Framebuffer driver for TI OMAP boards + * + * Copyright (C) 2004 Nokia Corporation + * Author: Imre Deak + * + * Acknowledgements: + * Alex McMains - Original driver + * Juha Yrjola - Original driver and improvements + * Dirk Behme - changes for 2.6 kernel API + * Texas Instruments - H3 support + * + * 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 + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +#define OMAPFB_DRIVER "omapfb" +#define MODULE_NAME "omapfb" + +#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) + +static unsigned int def_accel; +static unsigned long def_vram; +static unsigned long def_vxres; +static unsigned long def_vyres; +static unsigned int def_rotate; +static unsigned int def_mirror; + +#ifdef CONFIG_FB_OMAP_MANUAL_UPDATE +static int manual_update = 1; +#else +static int manual_update; +#endif + +static struct platform_device *fbdev_pdev; +static struct lcd_panel *fbdev_panel; +static struct omapfb_device *omapfb_dev; + +static struct caps_table_struct { + unsigned long flag; + const char *name; +} omapfb_caps_table[] = { + { OMAPFB_CAPS_MANUAL_UPDATE, "manual update" }, + { OMAPFB_CAPS_SET_BACKLIGHT, "backlight setting" }, +}; + +/* + * --------------------------------------------------------------------------- + * LCD panel + * --------------------------------------------------------------------------- + */ +extern struct lcd_ctrl omap1_int_ctrl; +extern struct lcd_ctrl omap2_int_ctrl; +extern struct lcd_ctrl hwa742_ctrl; +extern struct lcd_ctrl blizzard_ctrl; + +static struct lcd_ctrl *ctrls[] = { +#ifdef CONFIG_ARCH_OMAP1 + &omap1_int_ctrl, +#else + &omap2_int_ctrl, +#endif + +#ifdef CONFIG_FB_OMAP_LCDC_HWA742 + &hwa742_ctrl, +#endif +}; + +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL +#ifdef CONFIG_ARCH_OMAP1 +extern struct lcd_ctrl_extif sossi_extif; +#else +extern struct lcd_ctrl_extif rfbi_extif; +#endif +#endif + +static void omapfb_rqueue_lock(struct omapfb_device *fbdev) +{ + down(&fbdev->rqueue_sema); +} + +static void omapfb_rqueue_unlock(struct omapfb_device *fbdev) +{ + up(&fbdev->rqueue_sema); +} + +/* + * --------------------------------------------------------------------------- + * LCD controller and LCD DMA + * --------------------------------------------------------------------------- + */ +/* Lookup table to map elem size to elem type. */ +static const int dma_elem_type[] = { + 0, + OMAP_DMA_DATA_TYPE_S8, + OMAP_DMA_DATA_TYPE_S16, + 0, + OMAP_DMA_DATA_TYPE_S32, +}; + +/* Allocate resources needed for LCD controller and LCD DMA operations. Video + * memory is allocated from system memory according to the virtual display + * size, except if a bigger memory size is specified explicitly as a kernel + * parameter. + */ +static int ctrl_init(struct omapfb_device *fbdev) +{ + int r; + + DBGENTER(1); + + r = fbdev->ctrl->init(fbdev, 0, def_vram); + if (r < 0) { + pr_err("controller initialization failed\n"); + goto exit; + } + + fbdev->ctrl->get_vram_layout(&fbdev->vram_size, &fbdev->vram_virt_base, + &fbdev->vram_phys_base); + + DBGPRINT(1, "vram_phys %08x vram_virt %p vram_size=%lu\n", + fbdev->vram_phys_base, fbdev->vram_virt_base, + fbdev->vram_size); + + DBGLEAVE(1); + return 0; +exit: + DBGLEAVE(1); + return r; +} + +static void ctrl_cleanup(struct omapfb_device *fbdev) +{ + fbdev->ctrl->cleanup(); +} + +static int ctrl_change_mode(struct omapfb_device *fbdev) +{ + int r; + unsigned long offset; + struct fb_var_screeninfo *var = &fbdev->fb_info->var; + + DBGPRINT(1, "xoffset %d yoffset %d line_length %d bits_per_pixel %d\n", + var->xoffset, var->yoffset, fbdev->fb_info->fix.line_length, + var->bits_per_pixel); + offset = var->yoffset * fbdev->fb_info->fix.line_length + + var->xoffset * var->bits_per_pixel / 8; + r = fbdev->ctrl->setup_plane(OMAPFB_PLANE_GFX, OMAPFB_CHANNEL_OUT_LCD, + offset, var->xres_virtual, 0, 0, var->xres, + var->yres, fbdev->color_mode); + DBGLEAVE(1); + + return r; +} + +/* + * --------------------------------------------------------------------------- + * fbdev framework callbacks and the ioctl interface + * --------------------------------------------------------------------------- + */ +/* Called each time the omapfb device is opened */ +static int omapfb_open(struct fb_info *info, int user) +{ + DBGENTER(1); + DBGLEAVE(1); + return 0; +} + +static void omapfb_sync(struct fb_info *info); + +/* Called when the omapfb device is closed. We make sure that any pending + * gfx DMA operations are ended, before we return. */ +static int omapfb_release(struct fb_info *info, int user) +{ + DBGENTER(1); + + omapfb_sync(info); + + DBGLEAVE(1); + return 0; +} + +/* Store a single color palette entry into a pseudo palette or the hardware + * palette if one is available. For now we support only 16bpp and thus store + * the entry only to the pseudo palette. + */ +static int _setcolreg(struct fb_info *info, u_int regno, u_int red, u_int green, + u_int blue, u_int transp, int update_hw_pal) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)info->par; + int r = 0; + + switch (fbdev->color_mode) { + case OMAPFB_COLOR_YUV422: + case OMAPFB_COLOR_YUV420: + r = -EINVAL; + break; + case OMAPFB_COLOR_CLUT_8BPP: + case OMAPFB_COLOR_CLUT_4BPP: + case OMAPFB_COLOR_CLUT_2BPP: + case OMAPFB_COLOR_CLUT_1BPP: + if (fbdev->ctrl->setcolreg) + r = fbdev->ctrl->setcolreg(regno, red, green, blue, + transp, update_hw_pal); + /* Fallthrough */ + case OMAPFB_COLOR_RGB565: + if (r != 0) + break; + + if (regno < 0) { + r = -EINVAL; + break; + } + + if (regno < 16) { + u16 pal; + pal = ((red >> 11) << 11) | ((green >> 10) << 5) | + (blue >> 11); + ((u32 *)(info->pseudo_palette))[regno] = pal; + } + break; + default: + BUG(); + } + return r; +} + +static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, + u_int transp, struct fb_info *info) +{ + int r = 0; + + DBGENTER(2); + + _setcolreg(info, regno, red, green, blue, transp, 1); + + DBGLEAVE(2); + + return r; +} + +static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info) +{ + int count, index, r; + u16 *red, *green, *blue, *transp; + u16 trans = 0xffff; + + red = cmap->red; + green = cmap->green; + blue = cmap->blue; + transp = cmap->transp; + index = cmap->start; + + for (count = 0; count < cmap->len; count++) { + if (transp) + trans = *transp++; + r = _setcolreg(info, index++, *red++, *green++, *blue++, trans, + count == cmap->len - 1); + if (r != 0) + return r; + } + + return 0; +} + +static int omapfb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + struct omapfb_device *fbdev = info->par; + int r; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->mmap(vma); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +static void omapfb_update_full_screen(struct omapfb_device *fbdev); + +static int omapfb_blank(int blank, struct fb_info *fbi) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par; + int do_update = 0; + int r = 0; + + DBGENTER(1); + + omapfb_rqueue_lock(fbdev); + switch (blank) { + case VESA_NO_BLANKING: + if (fbdev->state == OMAPFB_SUSPENDED) { + if (fbdev->ctrl->resume) + fbdev->ctrl->resume(); + fbdev->panel->enable(); + fbdev->state = OMAPFB_ACTIVE; + if (fbdev->ctrl->get_update_mode() == + OMAPFB_MANUAL_UPDATE) + do_update = 1; + } + break; + case VESA_POWERDOWN: + if (fbdev->state == OMAPFB_ACTIVE) { + fbdev->panel->disable(); + if (fbdev->ctrl->suspend) + fbdev->ctrl->suspend(); + fbdev->state = OMAPFB_SUSPENDED; + } + break; + default: + r = -EINVAL; + } + omapfb_rqueue_unlock(fbdev); + + if (do_update) + omapfb_update_full_screen(fbdev); + + DBGLEAVE(1); + return r; +} + +static void omapfb_sync(struct fb_info *fbi) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par; + + omapfb_rqueue_lock(fbdev); + if (fbdev->ctrl->sync) + fbdev->ctrl->sync(); + omapfb_rqueue_unlock(fbdev); +} + +/* Set fb_info.fix fields and also updates fbdev. + * When calling this fb_info.var must be set up already. + */ +static void set_fb_fix(struct omapfb_device *fbdev) +{ + struct fb_info *fbi = fbdev->fb_info; + struct fb_fix_screeninfo *fix = &fbi->fix; + struct fb_var_screeninfo *var = &fbi->var; + + strncpy(fix->id, OMAPFB_DRIVER, sizeof(fix->id)); + fix->type = FB_TYPE_PACKED_PIXELS; + switch (var->bits_per_pixel) { + case 16: + fix->visual = FB_VISUAL_TRUECOLOR; + break; + case 1: + case 2: + case 4: + case 8: + fix->visual = FB_VISUAL_PSEUDOCOLOR; + break; + } + fix->accel = FB_ACCEL_OMAP1610; + fix->line_length = var->xres_virtual * var->bits_per_pixel / 8; + fix->smem_len = fbdev->vram_size; + fix->smem_start = fbdev->vram_phys_base; +} + +/* Check the values in var against our capabilities and in case of out of + * bound values try to adjust them. + */ +static int set_fb_var(struct omapfb_device *fbdev, + struct fb_var_screeninfo *var) +{ + int bpp; + unsigned long max_frame_size; + unsigned long line_size; + struct lcd_panel *panel = fbdev->panel; + + bpp = var->bits_per_pixel = panel->bpp; + + switch (bpp) { + case 16: + fbdev->color_mode = OMAPFB_COLOR_RGB565; + break; + case 8: + fbdev->color_mode = OMAPFB_COLOR_CLUT_8BPP; + break; + default: + /* FIXME: other BPPs not yet supported */ + return -EINVAL; + } + + switch (var->rotate) { + case 0: + case 180: + var->xres = fbdev->panel->x_res; + var->yres = fbdev->panel->y_res; + break; + case 90: + case 270: + var->xres = fbdev->panel->y_res; + var->yres = fbdev->panel->x_res; + break; + default: + return -EINVAL; + } + if (var->xres_virtual < var->xres) + var->xres_virtual = var->xres; + if (var->yres_virtual < var->yres) + var->yres_virtual = var->yres; + max_frame_size = fbdev->vram_size; + line_size = var->xres_virtual * bpp / 8; + if (line_size * var->yres_virtual > max_frame_size) { + /* Try to keep yres_virtual first */ + line_size = max_frame_size / var->yres_virtual; + var->xres_virtual = line_size * 8 / bpp; + if (var->xres_virtual < var->xres) { + /* Still doesn't fit. Shrink yres_virtual too */ + var->xres_virtual = var->xres; + line_size = var->xres * bpp / 8; + var->yres_virtual = max_frame_size / line_size; + } + } + if (var->xres + var->xoffset > var->xres_virtual) + var->xoffset = var->xres_virtual - var->xres; + if (var->yres + var->yoffset > var->yres_virtual) + var->yoffset = var->yres_virtual - var->yres; + line_size = var->xres * bpp / 8; + + var->red.offset = 11; var->red.length = 5; var->red.msb_right = 0; + var->green.offset= 5; var->green.length = 6; var->green.msb_right = 0; + var->blue.offset = 0; var->blue.length = 5; var->blue.msb_right = 0; + + var->height = -1; + var->width = -1; + var->grayscale = 0; + var->nonstd = 0; + + /* pixclock in ps, the rest in pixclock */ + var->pixclock = 10000000 / (panel->pixel_clock / 100); + var->left_margin = panel->hfp; + var->right_margin = panel->hbp; + var->upper_margin = panel->vfp; + var->lower_margin = panel->vbp; + var->hsync_len = panel->hsw; + var->vsync_len = panel->vsw; + + /* TODO: get these from panel->config */ + var->vmode = FB_VMODE_NONINTERLACED; + var->sync = 0; + + return 0; +} + +static struct fb_var_screeninfo new_var; + +/* Set rotation (0, 90, 180, 270 degree), and switch to the new mode. */ +static void omapfb_rotate(struct fb_info *fbi, int rotate) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par; + + DBGENTER(1); + + if (cpu_is_omap1510() && rotate != fbdev->fb_info->var.rotate) { + memcpy(&new_var, &fbi->var, sizeof(new_var)); + new_var.rotate = rotate; + if (set_fb_var(fbdev, &new_var) == 0 && + memcmp(&new_var, &fbi->var, sizeof(new_var))) { + memcpy(&fbi->var, &new_var, sizeof(new_var)); + ctrl_change_mode(fbdev); + } + } + + DBGLEAVE(1); +} + +/* Set new x,y offsets in the virtual display for the visible area and switch + * to the new mode. + */ +static int omapfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *fbi) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par; + int r = 0; + + DBGENTER(1); + + if (var->xoffset != fbi->var.xoffset || + var->yoffset != fbi->var.yoffset) { + memcpy(&new_var, &fbi->var, sizeof(new_var)); + new_var.xoffset = var->xoffset; + new_var.yoffset = var->yoffset; + if (set_fb_var(fbdev, &new_var)) + r = -EINVAL; + else { + memcpy(&fbi->var, &new_var, sizeof(new_var)); + ctrl_change_mode(fbdev); + } + } + + DBGLEAVE(1); + return r; +} + +/* Set mirror to vertical axis and switch to the new mode. */ +static int omapfb_mirror(struct omapfb_device *fbdev, int mirror) +{ + int r = 0; + + DBGENTER(1); + + mirror = mirror ? 1 : 0; + if (cpu_is_omap1510()) + r = -EINVAL; + else if (mirror != fbdev->mirror) { + fbdev->mirror = mirror; + r = ctrl_change_mode(fbdev); + } + + DBGLEAVE(1); + return r; +} + +/* Check values in var, try to adjust them in case of out of bound values if + * possible, or return error. + */ +static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par; + int r; + + DBGENTER(1); + + r = set_fb_var(fbdev, var); + + DBGLEAVE(1); + return r; +} + +/* Switch to a new mode. The parameters for it has been check already by + * omapfb_check_var. + */ +static int omapfb_set_par(struct fb_info *fbi) +{ + int r; + struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par; + + DBGENTER(1); + + set_fb_fix(fbdev); + r = ctrl_change_mode(fbdev); + + DBGLEAVE(1); + return r; +} + +int omapfb_update_window_async(struct omapfb_update_window *win, + void (*callback)(void *), + void *callback_data) +{ + struct omapfb_device *fbdev = omapfb_dev; + struct fb_var_screeninfo *var; + + DBGENTER(2); + if (fbdev == NULL) { + DBGPRINT(1, "no fbdev\n"); + return -ENODEV; + } + + var = &fbdev->fb_info->var; + + if (win->x >= var->xres || win->y >= var->yres) { + DBGPRINT(1, "invalid x %d, y %d\n", win->x, win->y); + return -EINVAL; + } + + if (!fbdev->ctrl->update_window || + fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE) { + DBGPRINT(1, "invalid update mode\n"); + return -ENODEV; + } + + if (win->x + win->width >= var->xres) + win->width = var->xres - win->x; + if (win->y + win->height >= var->yres) + win->height = var->yres - win->y; + if (!win->width || !win->height) { + DBGPRINT(1, "zero size window\n"); + return 0; + } + + return fbdev->ctrl->update_window(win, callback, callback_data); +} +EXPORT_SYMBOL(omapfb_update_window_async); + +static int omapfb_update_win(struct omapfb_device *fbdev, + struct omapfb_update_window *win) +{ + int ret; + + omapfb_rqueue_lock(fbdev); + ret = omapfb_update_window_async(win, NULL, 0); + omapfb_rqueue_unlock(fbdev); + + return ret; +} + +static void omapfb_update_full_screen(struct omapfb_device *fbdev) +{ + struct omapfb_update_window win; + + win.x = 0; + win.y = 0; + win.width = fbdev->panel->x_res; + win.height = fbdev->panel->y_res; + win.format = 0; + + omapfb_rqueue_lock(fbdev); + fbdev->ctrl->update_window(&win, NULL, 0); + omapfb_rqueue_unlock(fbdev); +} + +static int omapfb_setup_plane(struct omapfb_device *fbdev, + struct omapfb_setup_plane *sp) +{ + int r; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->setup_plane(sp->plane, sp->channel_out, sp->offset, + sp->width, sp->pos_x, sp->pos_y, sp->width, + sp->height, sp->color_mode); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +static int omapfb_enable_plane(struct omapfb_device *fbdev, int plane, + int enable) +{ + int r; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->enable_plane(plane, enable); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +static int omapfb_set_color_key(struct omapfb_device *fbdev, + struct omapfb_color_key *ck) +{ + int r; + + if (!fbdev->ctrl->set_color_key) + return -ENODEV; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->set_color_key(ck); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +static struct notifier_block *omapfb_client_list; + +int omapfb_register_client(struct omapfb_notifier_block *omapfb_nb, + omapfb_notifier_callback_t callback, + void *callback_data) +{ + int r; + + DBGENTER(1); + + omapfb_nb->nb.notifier_call = (int (*)(struct notifier_block *, + unsigned long, void *))callback; + omapfb_nb->data = callback_data; + r = notifier_chain_register(&omapfb_client_list, &omapfb_nb->nb); + if (r) + return r; + if (omapfb_dev != NULL && + omapfb_dev->ctrl && omapfb_dev->ctrl->bind_client) { + omapfb_dev->ctrl->bind_client(omapfb_nb); + } + + return 0; +} +EXPORT_SYMBOL(omapfb_register_client); + +int omapfb_unregister_client(struct omapfb_notifier_block *omapfb_nb) +{ + return notifier_chain_unregister(&omapfb_client_list, + &omapfb_nb->nb); +} +EXPORT_SYMBOL(omapfb_unregister_client); + +void omapfb_notify_clients(struct omapfb_device *fbdev, unsigned long event) +{ + DBGENTER(1); + notifier_call_chain(&omapfb_client_list, event, fbdev); +} +EXPORT_SYMBOL(omapfb_notify_clients); + +static int omapfb_set_update_mode(struct omapfb_device *fbdev, + enum omapfb_update_mode mode) +{ + int r; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->set_update_mode(mode); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +static enum omapfb_update_mode omapfb_get_update_mode(struct omapfb_device *fbdev) +{ + int r; + + omapfb_rqueue_lock(fbdev); + r = fbdev->ctrl->get_update_mode(); + omapfb_rqueue_unlock(fbdev); + + return r; +} + +static unsigned long omapfb_get_caps(struct fb_info *fbi) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par; + unsigned long caps; + + caps = 0; + caps |= fbdev->panel->get_caps(); + caps |= fbdev->ctrl->get_caps(); + return caps; +} + +/* For lcd testing */ +void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval) +{ + omapfb_rqueue_lock(fbdev); + *(u16 *)fbdev->vram_virt_base = pixval; + if (fbdev->ctrl->get_update_mode() == OMAPFB_MANUAL_UPDATE) { + struct omapfb_update_window win; + + win.x = 0; + win.y = 0; + win.width = 1; + win.height = 1; + win.format = 0; + fbdev->ctrl->update_window(&win, NULL, 0); + } + omapfb_rqueue_unlock(fbdev); +} +EXPORT_SYMBOL(omapfb_write_first_pixel); + +/* Ioctl interface. Part of the kernel mode frame buffer API is duplicated + * here to be accessible by user mode code. In addition transparent copy + * graphics transformations, frame flipping support is provided through this + * interface. + */ +static int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, + unsigned long arg) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)fbi->par; + struct fb_ops *ops = fbi->fbops; + union { + struct omapfb_update_window update_window; + struct omapfb_setup_plane setup_plane; + struct omapfb_enable_plane enable_plane; + struct omapfb_color_key color_key; + enum omapfb_update_mode update_mode; + unsigned long caps; + unsigned int mirror; + } p; + int r = 0; + + BUG_ON(!ops); + DBGPRINT(2, "cmd=%010x\n", cmd); + switch (cmd) + { + case OMAPFB_MIRROR: + if (get_user(p.mirror, (int __user *)arg)) + r = -EFAULT; + else + omapfb_mirror(fbdev, p.mirror); + break; + case OMAPFB_SYNC_GFX: + omapfb_sync(fbi); + break; + case OMAPFB_VSYNC: + break; + case OMAPFB_SET_UPDATE_MODE: + if (get_user(p.update_mode, (int __user *)arg)) + r = -EFAULT; + else + r = omapfb_set_update_mode(fbdev, p.update_mode); + break; + case OMAPFB_GET_UPDATE_MODE: + p.update_mode = omapfb_get_update_mode(fbdev); + if (put_user(p.update_mode, + (enum omapfb_update_mode __user *)arg)) + r = -EFAULT; + break; + case OMAPFB_UPDATE_WINDOW_OLD: + if (copy_from_user(&p.update_window, (void __user *)arg, + sizeof(struct omapfb_update_window_old))) + r = -EFAULT; + else { + p.update_window.format = 0; + r = omapfb_update_win(fbdev, &p.update_window); + } + break; + case OMAPFB_UPDATE_WINDOW: + if (copy_from_user(&p.update_window, (void __user *)arg, + sizeof(p.update_window))) + r = -EFAULT; + else + r = omapfb_update_win(fbdev, &p.update_window); + break; + case OMAPFB_SETUP_PLANE: + if (copy_from_user(&p.setup_plane, (void __user *)arg, + sizeof(p.setup_plane))) + r = -EFAULT; + else + r = omapfb_setup_plane(fbdev, &p.setup_plane); + break; + case OMAPFB_ENABLE_PLANE: + if (copy_from_user(&p.enable_plane, (void __user *)arg, + sizeof(p.enable_plane))) + r = -EFAULT; + else + r = omapfb_enable_plane(fbdev, + p.enable_plane.plane, p.enable_plane.enable); + break; + case OMAPFB_SET_COLOR_KEY: + if (copy_from_user(&p.color_key, (void __user *)arg, + sizeof(p.color_key))) + r = -EFAULT; + else + r = omapfb_set_color_key(fbdev, &p.color_key); + break; + case OMAPFB_GET_CAPS: + p.caps = omapfb_get_caps(fbi); + if (put_user(p.caps, (unsigned long __user *)arg)) + r = -EFAULT; + break; + case OMAPFB_LCD_TEST: + { + int test_num; + + if (get_user(test_num, (int __user *)arg)) { + r = -EFAULT; + break; + } + if (!fbdev->panel->run_test) { + r = -EINVAL; + break; + } + r = fbdev->panel->run_test(test_num); + break; + } + case OMAPFB_CTRL_TEST: + { + int test_num; + + if (get_user(test_num, (int __user *)arg)) { + r = -EFAULT; + break; + } + if (!fbdev->ctrl->run_test) { + r = -EINVAL; + break; + } + r = fbdev->ctrl->run_test(test_num); + break; + } + default: + r = -EINVAL; + } + + DBGLEAVE(2); + return r; +} + +/* Callback table for the frame buffer framework. Some of these pointers + * will be changed according to the current setting of fb_info->accel_flags. + */ +static struct fb_ops omapfb_ops = { + .owner = THIS_MODULE, + .fb_open = omapfb_open, + .fb_release = omapfb_release, + .fb_setcolreg = omapfb_setcolreg, + .fb_setcmap = omapfb_setcmap, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_blank = omapfb_blank, + .fb_ioctl = omapfb_ioctl, + .fb_check_var = omapfb_check_var, + .fb_set_par = omapfb_set_par, + .fb_rotate = omapfb_rotate, + .fb_pan_display = omapfb_pan_display, +}; + +/* + * --------------------------------------------------------------------------- + * Sysfs interface + * --------------------------------------------------------------------------- + */ +/* omapfbX sysfs entries */ +static ssize_t omapfb_show_caps_num(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + + return snprintf(buf, PAGE_SIZE, "%#010lx\n", + omapfb_get_caps(fbdev->fb_info)); +} + +static ssize_t omapfb_show_caps_text(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + int pos = 0; + int i; + unsigned long caps; + + caps = omapfb_get_caps(fbdev->fb_info); + for (i = 0; i < ARRAY_SIZE(omapfb_caps_table) && pos < PAGE_SIZE; i++) { + if (omapfb_caps_table[i].flag & caps) { + pos += snprintf(&buf[pos], PAGE_SIZE - pos, "%s\n", + omapfb_caps_table[i].name); + } + } + return min((int)PAGE_SIZE, pos); +} + +static DEVICE_ATTR(caps_num, 0444, omapfb_show_caps_num, NULL); +static DEVICE_ATTR(caps_text, 0444, omapfb_show_caps_text, NULL); + +/* panel sysfs entries */ +static ssize_t omapfb_show_panel_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + + return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->panel->name); +} + +static ssize_t omapfb_show_bklight_level(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + int r; + + if (fbdev->panel->get_bklight_level) { + r = snprintf(buf, PAGE_SIZE, "%d\n", + fbdev->panel->get_bklight_level()); + } else + r = -ENODEV; + return r; +} + +static ssize_t omapfb_store_bklight_level(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + int r; + + if (fbdev->panel->set_bklight_level) { + unsigned int level; + + if (sscanf(buf, "%10d", &level) == 1) { + r = fbdev->panel->set_bklight_level(level); + } else + r = -EINVAL; + } else + r = -ENODEV; + return r ? r : size; +} + +static ssize_t omapfb_show_bklight_max(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + int r; + + if (fbdev->panel->get_bklight_level) { + r = snprintf(buf, PAGE_SIZE, "%d\n", + fbdev->panel->get_bklight_max()); + } else + r = -ENODEV; + return r; +} + +static struct device_attribute dev_attr_panel_name = + __ATTR(name, 0444, omapfb_show_panel_name, NULL); +static DEVICE_ATTR(backlight_level, 0664, + omapfb_show_bklight_level, omapfb_store_bklight_level); +static DEVICE_ATTR(backlight_max, 0444, omapfb_show_bklight_max, NULL); + +static struct attribute *panel_attrs[] = { + &dev_attr_panel_name.attr, + &dev_attr_backlight_level.attr, + &dev_attr_backlight_max.attr, + NULL, +}; + +static struct attribute_group panel_attr_grp = { + .name = "panel", + .attrs = panel_attrs, +}; + +/* ctrl sysfs entries */ +static ssize_t omapfb_show_ctrl_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct omapfb_device *fbdev = (struct omapfb_device *)dev->driver_data; + + return snprintf(buf, PAGE_SIZE, "%s\n", fbdev->ctrl->name); +} + +static struct device_attribute dev_attr_ctrl_name = + __ATTR(name, 0444, omapfb_show_ctrl_name, NULL); + +static struct attribute *ctrl_attrs[] = { + &dev_attr_ctrl_name.attr, + NULL, +}; + +static struct attribute_group ctrl_attr_grp = { + .name = "ctrl", + .attrs = ctrl_attrs, +}; + +static int omapfb_register_sysfs(struct omapfb_device *fbdev) +{ + int r; + + if ((r = device_create_file(fbdev->dev, &dev_attr_caps_num))) + goto fail0; + + if ((r = device_create_file(fbdev->dev, &dev_attr_caps_text))) + goto fail1; + + if ((r = sysfs_create_group(&fbdev->dev->kobj, &panel_attr_grp))) + goto fail2; + + if ((r = sysfs_create_group(&fbdev->dev->kobj, &ctrl_attr_grp))) + goto fail3; + + return 0; +fail3: + sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp); +fail2: + device_remove_file(fbdev->dev, &dev_attr_caps_text); +fail1: + device_remove_file(fbdev->dev, &dev_attr_caps_num); +fail0: + pr_err("unable to register sysfs interface\n"); + return r; +} + +static void omapfb_unregister_sysfs(struct omapfb_device *fbdev) +{ + sysfs_remove_group(&fbdev->dev->kobj, &ctrl_attr_grp); + sysfs_remove_group(&fbdev->dev->kobj, &panel_attr_grp); + device_remove_file(fbdev->dev, &dev_attr_caps_num); + device_remove_file(fbdev->dev, &dev_attr_caps_text); +} + +/* + * --------------------------------------------------------------------------- + * LDM callbacks + * --------------------------------------------------------------------------- + */ +/* Initialize system fb_info object and set the default video mode. + * The frame buffer memory already allocated by lcddma_init + */ +static int fbinfo_init(struct omapfb_device *fbdev) +{ + struct fb_info *info = fbdev->fb_info; + struct fb_var_screeninfo *var = &info->var; + int r = 0; + + DBGENTER(1); + + BUG_ON(!fbdev->vram_virt_base); + + info->fbops = &omapfb_ops; + info->flags = FBINFO_FLAG_DEFAULT; + info->screen_base = (char __iomem *)fbdev->vram_virt_base; + + info->pseudo_palette = fbdev->pseudo_palette; + + var->accel_flags = def_accel ? FB_ACCELF_TEXT : 0; + var->xres_virtual = def_vxres; + var->yres_virtual = def_vyres; + var->rotate = def_rotate; + + fbdev->mirror = def_mirror; + + set_fb_var(fbdev, var); + set_fb_fix(fbdev); + + r = fb_alloc_cmap(&info->cmap, 16, 0); + if (r != 0) + pr_err("unable to allocate color map memory\n"); + + DBGLEAVE(1); + return r; +} + +/* Release the fb_info object */ +static void fbinfo_cleanup(struct omapfb_device *fbdev) +{ + DBGENTER(1); + + fb_dealloc_cmap(&fbdev->fb_info->cmap); + + DBGLEAVE(1); +} + +/* Free driver resources. Can be called to rollback an aborted initialization + * sequence. + */ +static void omapfb_free_resources(struct omapfb_device *fbdev, int state) +{ + switch (state) { + case OMAPFB_ACTIVE: + unregister_framebuffer(fbdev->fb_info); + case 7: + omapfb_unregister_sysfs(fbdev); + case 6: + fbdev->panel->disable(); + case 5: + omapfb_set_update_mode(fbdev, OMAPFB_UPDATE_DISABLED); + case 4: + fbinfo_cleanup(fbdev); + case 3: + ctrl_cleanup(fbdev); + case 2: + fbdev->panel->cleanup(); + case 1: + dev_set_drvdata(fbdev->dev, NULL); + framebuffer_release(fbdev->fb_info); + case 0: + /* nothing to free */ + break; + default: + BUG(); + } +} + +static int omapfb_find_ctrl(struct omapfb_device *fbdev) +{ + struct omapfb_platform_data *conf; + char name[17]; + int i; + + conf = (struct omapfb_platform_data *)fbdev->dev->platform_data; + + fbdev->ctrl = NULL; + if (conf == NULL) { + DBGPRINT(1, "omap_lcd_config not found\n"); + return -1; + } + + strncpy(name, conf->lcd.ctrl_name, sizeof(name) - 1); + name[sizeof(name) - 1] = '\0'; + + if (strcmp(name, "internal") == 0) { + fbdev->ctrl = fbdev->int_ctrl; + return 0; + } + + for (i = 0; i < ARRAY_SIZE(ctrls); i++) { + DBGPRINT(1, "ctrl %s\n", ctrls[i]->name); + if (strcmp(ctrls[i]->name, name) == 0) { + fbdev->ctrl = ctrls[i]; + break; + } + } + + if (fbdev->ctrl == NULL) { + DBGPRINT(1, "ctrl %s not supported\n", name); + return -1; + } + + return 0; +} + +static void check_required_callbacks(struct omapfb_device *fbdev) +{ +#define _C(x) (fbdev->ctrl->x != NULL) +#define _P(x) (fbdev->panel->x != NULL) + BUG_ON(fbdev->ctrl == NULL || fbdev->panel == NULL); + BUG_ON(!(_C(init) && _C(cleanup) && _C(get_caps) && + _C(set_update_mode) && _C(setup_plane) && _C(enable_plane) && + _P(init) && _P(cleanup) && _P(enable) && _P(disable) && + _P(get_caps))); +#undef _P +#undef _C +} + +/* Called by LDM binding to probe and attach a new device. + * Initialization sequence: + * 1. allocate system fb_info structure + * select panel type according to machine type + * 2. init LCD panel + * 3. init LCD controller and LCD DMA + * 4. init system fb_info structure + * 5. init gfx DMA + * 6. enable LCD panel + * start LCD frame transfer + * 7. register system fb_info structure + */ +static int omapfb_do_probe(struct platform_device *pdev, struct lcd_panel *panel) +{ + struct omapfb_device *fbdev = NULL; + struct fb_info *fbi; + int init_state; + unsigned long phz, hhz, vhz; + int r = 0; + + DBGENTER(1); + + init_state = 0; + + if (pdev->num_resources != 0) { + pr_err("probed for an unknown device\n"); + r = -ENODEV; + goto cleanup; + } + + fbi = framebuffer_alloc(sizeof(struct omapfb_device), &pdev->dev); + if (fbi == NULL) { + pr_err("unable to allocate memory for device info\n"); + r = -ENOMEM; + goto cleanup; + } + init_state++; + + fbdev = (struct omapfb_device *)fbi->par; + fbdev->fb_info = fbi; + fbdev->dev = &pdev->dev; + fbdev->panel = panel; + platform_set_drvdata(pdev, fbdev); + + init_MUTEX(&fbdev->rqueue_sema); + +#ifdef CONFIG_ARCH_OMAP1 + fbdev->int_ctrl = &omap1_int_ctrl; +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL + fbdev->ext_if = &sossi_extif; +#endif +#else /* OMAP2 */ + fbdev->int_ctrl = &omap2_int_ctrl; +#ifdef CONFIG_FB_OMAP_LCDC_EXTERNAL + fbdev->ext_if = &rfbi_extif; +#endif +#endif + if (omapfb_find_ctrl(fbdev) < 0) { + pr_err("LCD controller not found, board not supported\n"); + r = -ENODEV; + goto cleanup; + } + + pr_info(MODULE_NAME ": configured for panel %s\n", fbdev->panel->name); + + r = fbdev->panel->init(fbdev); + if (r) + goto cleanup; + init_state++; + + r = ctrl_init(fbdev); + if (r) + goto cleanup; + init_state++; + + /* We depend on doing this after ctrl_init, since it can redefine + * member functions. + */ + if (fbdev->ctrl->mmap) + omapfb_ops.fb_mmap = omapfb_mmap; + + check_required_callbacks(fbdev); + + r = fbinfo_init(fbdev); + if (r) + goto cleanup; + init_state++; + +#ifdef CONFIG_FB_OMAP_DMA_TUNE + /* Set DMA priority for EMIFF access to highest */ + omap_set_dma_priority(OMAP_DMA_PORT_EMIFF, 15); +#endif + + r = ctrl_change_mode(fbdev); + if (r) { + pr_err("mode setting failed\n"); + goto cleanup; + } + + if (!manual_update) + omapfb_enable_plane(fbdev, OMAPFB_PLANE_GFX, 1); + + omapfb_set_update_mode(fbdev, manual_update ? + OMAPFB_MANUAL_UPDATE : OMAPFB_AUTO_UPDATE); + init_state++; + + r = fbdev->panel->enable(); + if (r) + goto cleanup; + init_state++; + + r = omapfb_register_sysfs(fbdev); + if (r) + goto cleanup; + init_state++; + + r = register_framebuffer(fbdev->fb_info); + if (r != 0) { + pr_err("register_framebuffer failed\n"); + goto cleanup; + } + + fbdev->state = OMAPFB_ACTIVE; + + panel = fbdev->panel; + phz = panel->pixel_clock * 1000; + hhz = phz * 10 / (panel->hfp + panel->x_res + panel->hbp + panel->hsw); + vhz = hhz / (panel->vfp + panel->y_res + panel->vbp + panel->vsw); + + omapfb_dev = fbdev; + + pr_info(MODULE_NAME ": initialized vram=%lu " + "pixclock %lu kHz hfreq %lu.%lu kHz vfreq %lu.%lu Hz\n", + fbdev->vram_size, + phz / 1000, hhz / 10000, hhz % 10, vhz / 10, vhz % 10); + + DBGLEAVE(1); + return 0; + +cleanup: + omapfb_free_resources(fbdev, init_state); + + DBGLEAVE(1); + return r; +} + +static int omapfb_probe(struct platform_device *pdev) +{ + BUG_ON(fbdev_pdev != NULL); + + DBGENTER(1); + fbdev_pdev = pdev; + if (fbdev_panel != NULL) + omapfb_do_probe(fbdev_pdev, fbdev_panel); + return 0; +} + +void omapfb_register_panel(struct lcd_panel *panel) +{ + BUG_ON(fbdev_panel != NULL); + + DBGENTER(1); + fbdev_panel = panel; + if (fbdev_pdev != NULL) + omapfb_do_probe(fbdev_pdev, fbdev_panel); +} + +/* Called when the device is being detached from the driver */ +static int omapfb_remove(struct platform_device *pdev) +{ + struct omapfb_device *fbdev = platform_get_drvdata(pdev); + enum omapfb_state saved_state = fbdev->state; + + DBGENTER(1); + /* FIXME: wait till completion of pending events */ + + fbdev->state = OMAPFB_DISABLED; + omapfb_free_resources(fbdev, saved_state); + + DBGLEAVE(1); + return 0; +} + +/* PM suspend */ +static int omapfb_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct omapfb_device *fbdev = platform_get_drvdata(pdev); + + DBGENTER(1); + + omapfb_blank(VESA_POWERDOWN, fbdev->fb_info); + + DBGLEAVE(1); + + return 0; +} + +/* PM resume */ +static int omapfb_resume(struct platform_device *pdev) +{ + struct omapfb_device *fbdev = platform_get_drvdata(pdev); + + DBGENTER(1); + + omapfb_blank(VESA_NO_BLANKING, fbdev->fb_info); + + DBGLEAVE(1); + return 0; +} + +static struct platform_driver omapfb_driver = { + .probe = omapfb_probe, + .remove = omapfb_remove, + .suspend = omapfb_suspend, + .resume = omapfb_resume, + .driver = { + .name = OMAPFB_DRIVER, + .owner = THIS_MODULE, + }, +}; + +#ifndef MODULE + +/* Process kernel command line parameters */ +static int __init omapfb_setup(char *options) +{ + char *this_opt = NULL; + int r = 0; + + DBGENTER(1); + + if (!options || !*options) + goto exit; + + while (!r && (this_opt = strsep(&options, ",")) != NULL) { + if (!strncmp(this_opt, "accel", 5)) + def_accel = 1; + else if (!strncmp(this_opt, "vram:", 5)) { + char *suffix; + def_vram = (simple_strtoul(this_opt + 5, &suffix, 0)); + switch (suffix[0]) { + case '\0': + break; + case 'm': + case 'M': + def_vram *= 1024; + /* Fall through */ + case 'k': + case 'K': + def_vram *= 1024; + break; + default: + pr_err("invalid vram suffix\n"); + r = -1; + } + } + else if (!strncmp(this_opt, "vxres:", 6)) + def_vxres = simple_strtoul(this_opt + 6, NULL, 0); + else if (!strncmp(this_opt, "vyres:", 6)) + def_vyres = simple_strtoul(this_opt + 6, NULL, 0); + else if (!strncmp(this_opt, "rotate:", 7)) + def_rotate = (simple_strtoul(this_opt + 7, NULL, 0)); + else if (!strncmp(this_opt, "mirror:", 7)) + def_mirror = (simple_strtoul(this_opt + 7, NULL, 0)); + else if (!strncmp(this_opt, "manual_update", 13)) + manual_update = 1; + else { + pr_err("invalid option\n"); + r = -1; + } + } +exit: + DBGLEAVE(1); + return r; +} + +#endif + +/* Register both the driver and the device */ +static int __init omapfb_init(void) +{ + int r = 0; + + DBGENTER(1); + +#ifndef MODULE + { + char *option; + + if (fb_get_options("omapfb", &option)) { + r = -ENODEV; + goto exit; + } + omapfb_setup(option); + } +#endif + /* Register the driver with LDM */ + if (platform_driver_register(&omapfb_driver)) { + pr_err("failed to register omapfb driver\n"); + r = -ENODEV; + goto exit; + } + +exit: + DBGLEAVE(1); + return r; +} + +static void __exit omapfb_cleanup(void) +{ + DBGENTER(1); + + platform_driver_unregister(&omapfb_driver); + + DBGLEAVE(1); +} + +module_param_named(accel, def_accel, uint, 0664); +module_param_named(vram, def_vram, ulong, 0664); +module_param_named(vxres, def_vxres, long, 0664); +module_param_named(vyres, def_vyres, long, 0664); +module_param_named(rotate, def_rotate, uint, 0664); +module_param_named(mirror, def_mirror, uint, 0664); +module_param_named(manual_update, manual_update, bool, 0664); + +module_init(omapfb_init); +module_exit(omapfb_cleanup); + +MODULE_DESCRIPTION("TI OMAP framebuffer driver"); +MODULE_AUTHOR("Imre Deak "); +MODULE_LICENSE("GPL"); diff --git a/drivers/video/omap/rfbi.c b/drivers/video/omap/rfbi.c new file mode 100644 index 00000000000..82415f50a69 --- /dev/null +++ b/drivers/video/omap/rfbi.c @@ -0,0 +1,411 @@ +/* + * File: drivers/video/omap/omap2/rfbi.c + * + * OMAP2 Remote Frame Buffer Interface support + * + * Copyright (C) 2005 Nokia Corporation + * Author: Juha Yrjölä + * Imre Deak + * + * 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 "dispc.h" + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +#define MODULE_NAME "omapfb-rfbi" + +#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) + +#define RFBI_BASE 0x48050800 +#define RFBI_REVISION 0x0000 +#define RFBI_SYSCONFIG 0x0010 +#define RFBI_SYSSTATUS 0x0014 +#define RFBI_CONTROL 0x0040 +#define RFBI_PIXEL_CNT 0x0044 +#define RFBI_LINE_NUMBER 0x0048 +#define RFBI_CMD 0x004c +#define RFBI_PARAM 0x0050 +#define RFBI_DATA 0x0054 +#define RFBI_READ 0x0058 +#define RFBI_STATUS 0x005c +#define RFBI_CONFIG0 0x0060 +#define RFBI_ONOFF_TIME0 0x0064 +#define RFBI_CYCLE_TIME0 0x0068 +#define RFBI_DATA_CYCLE1_0 0x006c +#define RFBI_DATA_CYCLE2_0 0x0070 +#define RFBI_DATA_CYCLE3_0 0x0074 +#define RFBI_VSYNC_WIDTH 0x0090 +#define RFBI_HSYNC_WIDTH 0x0094 + +#define DISPC_BASE 0x48050400 +#define DISPC_CONTROL 0x0040 + +static struct { + u32 base; + void (*lcdc_callback)(void *data); + void *lcdc_callback_data; + unsigned long l4_khz; + int bits_per_cycle; +} rfbi; + +struct lcd_ctrl_extif rfbi_extif; + +static inline void rfbi_write_reg(int idx, u32 val) +{ + __raw_writel(val, rfbi.base + idx); +} + +static inline u32 rfbi_read_reg(int idx) +{ + return __raw_readl(rfbi.base + idx); +} + +#ifdef OMAPFB_DBG +static void rfbi_print_timings(void) +{ + u32 l; + u32 time; + + l = rfbi_read_reg(RFBI_CONFIG0); + time = 1000000000 / rfbi.l4_khz; + if (l & (1 << 4)) + time *= 2; + + DBGPRINT(1, "Tick time %u ps\n", time); + l = rfbi_read_reg(RFBI_ONOFF_TIME0); + DBGPRINT(1, "CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, " + "REONTIME %d, REOFFTIME %d\n", + l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f, + (l >> 20) & 0x0f, (l >> 24) & 0x3f); + l = rfbi_read_reg(RFBI_CYCLE_TIME0); + DBGPRINT(1, "WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, " + "ACCESSTIME %d\n", + (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, (l >> 22) & 0x3f); +} +#else +static void rfbi_print_timings(void) {} +#endif + +static void rfbi_set_timings(const struct extif_timings *t) +{ + u32 l; + + BUG_ON(!t->converted); + + rfbi_write_reg(RFBI_ONOFF_TIME0, t->tim[0]); + rfbi_write_reg(RFBI_CYCLE_TIME0, t->tim[1]); + + l = rfbi_read_reg(RFBI_CONFIG0); + l &= ~(1 << 4); + l |= (t->tim[2] ? 1 : 0) << 4; + rfbi_write_reg(RFBI_CONFIG0, l); + + rfbi_print_timings(); +} + +static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) +{ + *clk_period = 1000000000 / rfbi.l4_khz; + *max_clk_div = 2; +} + +static int ps_to_rfbi_ticks(int time, int div) +{ + unsigned long tick_ps; + int ret; + + /* Calculate in picosecs to yield more exact results */ + tick_ps = 1000000000 / (rfbi.l4_khz) * div; + + ret = (time + tick_ps - 1) / tick_ps; + + return ret; +} + +static int rfbi_convert_timings(struct extif_timings *t) +{ + u32 l; + int reon, reoff, weon, weoff, cson, csoff, cs_pulse; + int actim, recyc, wecyc; + int div = t->clk_div; + + if (div <= 0 || div > 2) + return -1; + + /* Make sure that after conversion it still holds that: + * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff, + * csoff > cson, csoff >= max(weoff, reoff), actim > reon + */ + weon = ps_to_rfbi_ticks(t->we_on_time, div); + weoff = ps_to_rfbi_ticks(t->we_off_time, div); + if (weoff <= weon) + weoff = weon + 1; + if (weon > 0x0f) + return -1; + if (weoff > 0x3f) + return -1; + + reon = ps_to_rfbi_ticks(t->re_on_time, div); + reoff = ps_to_rfbi_ticks(t->re_off_time, div); + if (reoff <= reon) + reoff = reon + 1; + if (reon > 0x0f) + return -1; + if (reoff > 0x3f) + return -1; + + cson = ps_to_rfbi_ticks(t->cs_on_time, div); + csoff = ps_to_rfbi_ticks(t->cs_off_time, div); + if (csoff <= cson) + csoff = cson + 1; + if (csoff < max(weoff, reoff)) + csoff = max(weoff, reoff); + if (cson > 0x0f) + return -1; + if (csoff > 0x3f) + return -1; + + l = cson; + l |= csoff << 4; + l |= weon << 10; + l |= weoff << 14; + l |= reon << 20; + l |= reoff << 24; + + t->tim[0] = l; + + actim = ps_to_rfbi_ticks(t->access_time, div); + if (actim <= reon) + actim = reon + 1; + if (actim > 0x3f) + return -1; + + wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div); + if (wecyc < weoff) + wecyc = weoff; + if (wecyc > 0x3f) + return -1; + + recyc = ps_to_rfbi_ticks(t->re_cycle_time, div); + if (recyc < reoff) + recyc = reoff; + if (recyc > 0x3f) + return -1; + + cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div); + if (cs_pulse > 0x3f) + return -1; + + l = wecyc; + l |= recyc << 6; + l |= cs_pulse << 12; + l |= actim << 22; + + t->tim[1] = l; + + t->tim[2] = div - 1; + + t->converted = 1; + + return 0; +} + +static void rfbi_write_command(const void *buf, unsigned int len) +{ + if (rfbi.bits_per_cycle == 16) { + const u16 *w = buf; + BUG_ON(len & 1); + for (; len; len -= 2) + rfbi_write_reg(RFBI_CMD, *w++); + } else { + const u8 *b = buf; + BUG_ON(rfbi.bits_per_cycle != 8); + for (; len; len--) + rfbi_write_reg(RFBI_CMD, *b++); + } +} + +static void rfbi_read_data(void *buf, unsigned int len) +{ + if (rfbi.bits_per_cycle == 16) { + u16 *w = buf; + BUG_ON(len & ~1); + for (; len; len -= 2) { + rfbi_write_reg(RFBI_READ, 0); + *w++ = rfbi_read_reg(RFBI_READ); + } + } else { + u8 *b = buf; + BUG_ON(rfbi.bits_per_cycle != 8); + for (; len; len--) { + rfbi_write_reg(RFBI_READ, 0); + *b++ = rfbi_read_reg(RFBI_READ); + } + } +} + +static void rfbi_write_data(const void *buf, unsigned int len) +{ + if (rfbi.bits_per_cycle == 16) { + const u16 *w = buf; + BUG_ON(len & 1); + for (; len; len -= 2) + rfbi_write_reg(RFBI_PARAM, *w++); + } else { + const u8 *b = buf; + BUG_ON(rfbi.bits_per_cycle != 8); + for (; len; len--) + rfbi_write_reg(RFBI_PARAM, *b++); + } +} + +static void rfbi_transfer_area(int width, int height, + void (callback)(void * data), void *data) +{ + u32 w; + + BUG_ON(callback == NULL); + + omap_dispc_set_lcd_size(width, height); + + rfbi.lcdc_callback = callback; + rfbi.lcdc_callback_data = data; + + rfbi_write_reg(RFBI_PIXEL_CNT, width * height); + + w = rfbi_read_reg(RFBI_CONTROL); + /* Enable, Internal trigger */ + rfbi_write_reg(RFBI_CONTROL, w | (1 << 0) | (1 << 4)); + + omap_dispc_enable_lcd_out(1); +} + +static inline void _stop_transfer(void) +{ + u32 w; + + w = rfbi_read_reg(RFBI_CONTROL); + rfbi_write_reg(RFBI_CONTROL, w & ~(1 << 0)); +} + +static void rfbi_dma_callback(void *data) +{ + _stop_transfer(); + rfbi.lcdc_callback(rfbi.lcdc_callback_data); +} + +static void rfbi_set_bits_per_cycle(int bpc) +{ + u32 l; + + l = rfbi_read_reg(RFBI_CONFIG0); + l &= ~(0x03 << 0); + switch (bpc) + { + case 8: + break; + case 16: + l |= 3; + break; + default: + BUG(); + } + rfbi_write_reg(RFBI_CONFIG0, l); + rfbi.bits_per_cycle = bpc; +} + +static int rfbi_init(void) +{ + u32 l; + int r; + struct clk *dss_ick; + + rfbi.base = io_p2v(RFBI_BASE); + + l = rfbi_read_reg(RFBI_REVISION); + pr_info(MODULE_NAME ": version %d.%d\n", (l >> 4) & 0x0f, l & 0x0f); + + dss_ick = clk_get(NULL, "dss_ick"); + if (IS_ERR(dss_ick)) { + pr_err("can't get dss_ick\n"); + return PTR_ERR(dss_ick); + } + + rfbi.l4_khz = clk_get_rate(dss_ick) / 1000; + clk_put(dss_ick); + + /* Reset */ + rfbi_write_reg(RFBI_SYSCONFIG, 1 << 1); + while (!(rfbi_read_reg(RFBI_SYSSTATUS) & (1 << 0))); + + l = rfbi_read_reg(RFBI_SYSCONFIG); + /* Enable autoidle and smart-idle */ + l |= (1 << 0) | (2 << 3); + rfbi_write_reg(RFBI_SYSCONFIG, l); + + /* 16-bit interface, ITE trigger mode, 16-bit data */ + l = (0x03 << 0) | (0x00 << 2) | (0x01 << 5) | (0x02 << 7); + l |= (0 << 9) | (1 << 20) | (1 << 21); + rfbi_write_reg(RFBI_CONFIG0, l); + + rfbi_write_reg(RFBI_DATA_CYCLE1_0, 0x00000010); + + l = rfbi_read_reg(RFBI_CONTROL); + /* Select CS0, clear bypass mode */ + l = (0x01 << 2); + rfbi_write_reg(RFBI_CONTROL, l); + + if ((r = omap_dispc_request_irq(rfbi_dma_callback, NULL)) < 0) { + pr_err("can't get DISPC irq\n"); + return r; + } + + return 0; +} + +static void rfbi_cleanup(void) +{ + omap_dispc_free_irq(); +} + +struct lcd_ctrl_extif rfbi_extif = { + .init = rfbi_init, + .cleanup = rfbi_cleanup, + .get_clk_info = rfbi_get_clk_info, + .set_bits_per_cycle = rfbi_set_bits_per_cycle, + .convert_timings = rfbi_convert_timings, + .set_timings = rfbi_set_timings, + .write_command = rfbi_write_command, + .read_data = rfbi_read_data, + .write_data = rfbi_write_data, + .transfer_area = rfbi_transfer_area, + .max_transmit_size = (u32)~0, +}; + diff --git a/drivers/video/omap/sossi.c b/drivers/video/omap/sossi.c new file mode 100644 index 00000000000..986f639f588 --- /dev/null +++ b/drivers/video/omap/sossi.c @@ -0,0 +1,529 @@ +/* + * File: drivers/video/omap/omap1/sossi.c + * + * OMAP1 Special OptimiSed Screen Interface support + * + * Copyright (C) 2004-2005 Nokia Corporation + * Author: Juha Yrjölä + * + * 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 "lcdc.h" + +/* #define OMAPFB_DBG 1 */ + +#include "debug.h" + +#define MODULE_NAME "omapfb-sossi" + +#define OMAP_SOSSI_BASE 0xfffbac00 +#define SOSSI_ID_REG 0x00 +#define SOSSI_INIT1_REG 0x04 +#define SOSSI_INIT2_REG 0x08 +#define SOSSI_INIT3_REG 0x0c +#define SOSSI_FIFO_REG 0x10 +#define SOSSI_REOTABLE_REG 0x14 +#define SOSSI_TEARING_REG 0x18 +#define SOSSI_INIT1B_REG 0x1c +#define SOSSI_FIFOB_REG 0x20 + +#define DMA_GSCR 0xfffedc04 +#define DMA_LCD_CCR 0xfffee3c2 +#define DMA_LCD_CTRL 0xfffee3c4 +#define DMA_LCD_LCH_CTRL 0xfffee3ea + +#define RD_ACCESS 0 +#define WR_ACCESS 1 + +#define SOSSI_MAX_XMIT_BYTES (512 * 1024) + +#define pr_err(fmt, args...) printk(KERN_ERR MODULE_NAME ": " fmt, ## args) + +static struct sossi { + int base; + unsigned long dpll_khz; + int bus_pick_width; + void (*lcdc_callback)(void *data); + void *lcdc_callback_data; + /* timing for read and write access */ + int clk_div; + u8 clk_tw0[2]; + u8 clk_tw1[2]; + /* if last_access is the same as current we don't have to change + * the timings + */ + int last_access; +} sossi; + +struct lcd_ctrl_extif sossi_extif; + +static inline u32 sossi_read_reg(int reg) +{ + return readl(sossi.base + reg); +} + +static inline u16 sossi_read_reg16(int reg) +{ + return readw(sossi.base + reg); +} + +static inline u8 sossi_read_reg8(int reg) +{ + return readb(sossi.base + reg); +} + +static inline void sossi_write_reg(int reg, u32 value) +{ + writel(value, sossi.base + reg); +} + +static inline void sossi_write_reg16(int reg, u16 value) +{ + writew(value, sossi.base + reg); +} + +static inline void sossi_write_reg8(int reg, u8 value) +{ + writeb(value, sossi.base + reg); +} + +static void sossi_set_bits(int reg, u32 bits) +{ + sossi_write_reg(reg, sossi_read_reg(reg) | bits); +} + +static void sossi_clear_bits(int reg, u32 bits) +{ + sossi_write_reg(reg, sossi_read_reg(reg) & ~bits); +} + +#define MOD_CONF_CTRL_1 0xfffe1110 +#define CONF_SOSSI_RESET_R (1 << 23) +#define CONF_MOD_SOSSI_CLK_EN_R (1 << 16) + +static void sossi_dma_callback(void *data); + +static int sossi_init(void) +{ + u32 l, k; + struct clk *dpll_clk; + int r; + + sossi.base = IO_ADDRESS(OMAP_SOSSI_BASE); + + dpll_clk = clk_get(NULL, "ck_dpll1"); + if (IS_ERR(dpll_clk)) { + pr_err("can't get dpll1 clock\n"); + return PTR_ERR(dpll_clk); + } + + sossi.dpll_khz = clk_get_rate(dpll_clk) / 1000; + clk_put(dpll_clk); + + sossi_extif.max_transmit_size = SOSSI_MAX_XMIT_BYTES; + + /* Reset and enable the SoSSI module */ + l = omap_readl(MOD_CONF_CTRL_1); + l |= CONF_SOSSI_RESET_R; + omap_writel(l, MOD_CONF_CTRL_1); + l &= ~CONF_SOSSI_RESET_R; + omap_writel(l, MOD_CONF_CTRL_1); + + l |= CONF_MOD_SOSSI_CLK_EN_R; + omap_writel(l, MOD_CONF_CTRL_1); + + omap_writel(omap_readl(ARM_IDLECT2) | (1 << 11), ARM_IDLECT2); + omap_writel(omap_readl(ARM_IDLECT1) | (1 << 6), ARM_IDLECT1); + + l = sossi_read_reg(SOSSI_INIT2_REG); + /* Enable and reset the SoSSI block */ + l |= (1 << 0) | (1 << 1); + sossi_write_reg(SOSSI_INIT2_REG, l); + /* Take SoSSI out of reset */ + l &= ~(1 << 1); + sossi_write_reg(SOSSI_INIT2_REG, l); + + sossi_write_reg(SOSSI_ID_REG, 0); + l = sossi_read_reg(SOSSI_ID_REG); + k = sossi_read_reg(SOSSI_ID_REG); + + if (l != 0x55555555 || k != 0xaaaaaaaa) { + pr_err("Invalid SoSSI sync pattern: %08x, %08x\n", l, k); + return -ENODEV; + } + + if ((r = omap_lcdc_set_dma_callback(sossi_dma_callback, NULL)) < 0) { + pr_err("can't get LCDC IRQ\n"); + return r; + } + + l = sossi_read_reg(SOSSI_ID_REG); /* Component code */ + l = sossi_read_reg(SOSSI_ID_REG); + pr_info(KERN_INFO MODULE_NAME ": version %d.%d initialized\n", + l >> 16, l & 0xffff); + + l = sossi_read_reg(SOSSI_INIT1_REG); + l |= (1 << 19); /* DMA_MODE */ + l &= ~(1 << 31); /* REORDERING */ + sossi_write_reg(SOSSI_INIT1_REG, l); + + return 0; +} + +static void sossi_cleanup(void) +{ + omap_lcdc_free_dma_callback(); +} + +#define KHZ_TO_PS(x) (1000000000 / (x)) + +static void sossi_get_clk_info(u32 *clk_period, u32 *max_clk_div) +{ + *clk_period = KHZ_TO_PS(sossi.dpll_khz); + *max_clk_div = 8; +} + +static u32 ps_to_sossi_ticks(u32 ps, int div) +{ + u32 clk_period = KHZ_TO_PS(sossi.dpll_khz) * div; + return (clk_period + ps - 1) / clk_period; +} + +static int calc_rd_timings(struct extif_timings *t) +{ + u32 tw0, tw1; + int reon, reoff, recyc, actim; + int div = t->clk_div; + + /* Make sure that after conversion it still holds that: + * reoff > reon, recyc >= reoff, actim > reon + */ + reon = ps_to_sossi_ticks(t->re_on_time, div); + /* reon will be exactly one sossi tick */ + if (reon > 1) + return -1; + + reoff = ps_to_sossi_ticks(t->re_off_time, div); + + if (reoff <= reon) + reoff = reon + 1; + + tw0 = reoff - reon; + if (tw0 > 0x10) + return -1; + + recyc = ps_to_sossi_ticks(t->re_cycle_time, div); + if (recyc <= reoff) + recyc = reoff + 1; + + tw1 = recyc - reoff; + if (tw1 > 0x40) + return -1; + + actim = ps_to_sossi_ticks(t->access_time, div); + if (actim < reoff) + actim++; + /* access time (data hold time) will be exactly one sossi + * tick + */ + if (actim - reoff > 1) + return -1; + + t->tim[0] = tw0 - 1; + t->tim[1] = tw1 - 1; + + return 0; +} + +static int calc_wr_timings(struct extif_timings *t) +{ + u32 tw0, tw1; + int weon, weoff, wecyc; + int div = t->clk_div; + + /* Make sure that after conversion it still holds that: + * weoff > weon, wecyc >= weoff + */ + weon = ps_to_sossi_ticks(t->we_on_time, div); + /* weon will be exactly one sossi tick */ + if (weon > 1) + return -1; + + weoff = ps_to_sossi_ticks(t->we_off_time, div); + if (weoff <= weon) + weoff = weon + 1; + tw0 = weoff - weon; + if (tw0 > 0x10) + return -1; + + wecyc = ps_to_sossi_ticks(t->we_cycle_time, div); + if (wecyc <= weoff) + wecyc = weoff + 1; + + tw1 = wecyc - weoff; + if (tw1 > 0x40) + return -1; + + t->tim[2] = tw0 - 1; + t->tim[3] = tw1 - 1; + + return 0; +} + +static int sossi_convert_timings(struct extif_timings *t) +{ + int r = 0; + int div = t->clk_div; + + t->converted = 0; + + if (div <= 0 || div > 8) + return -1; + + /* no CS on SOSSI, so ignore cson, csoff, cs_pulsewidth */ + if ((r = calc_rd_timings(t)) < 0) + return r; + + if ((r = calc_wr_timings(t)) < 0) + return r; + + t->tim[4] = div - 1; + + t->converted = 1; + + return 0; +} + +static void sossi_set_timings(const struct extif_timings *t) +{ + BUG_ON(!t->converted); + + sossi.clk_tw0[RD_ACCESS] = t->tim[0]; + sossi.clk_tw1[RD_ACCESS] = t->tim[1]; + + sossi.clk_tw0[WR_ACCESS] = t->tim[2]; + sossi.clk_tw1[WR_ACCESS] = t->tim[3]; + + sossi.clk_div = t->tim[4]; +} + +static void _set_timing(int div, int tw0, int tw1) +{ + u32 l; + + DBGPRINT(2, "Using TW0 = %d, TW1 = %d, div = %d\n", + tw0 + 1, tw1 + 1, div + 1); + + l = omap_readl(MOD_CONF_CTRL_1); + l &= ~(7 << 17); + l |= div << 17; + omap_writel(l, MOD_CONF_CTRL_1); + + l = sossi_read_reg(SOSSI_INIT1_REG); + l &= ~((0x0f << 20) | (0x3f << 24)); + l |= (tw0 << 20) | (tw1 << 24); + sossi_write_reg(SOSSI_INIT1_REG, l); +} + +static inline void set_timing(int access) +{ + if (access != sossi.last_access) { + sossi.last_access = access; + _set_timing(sossi.clk_div, + sossi.clk_tw0[access], sossi.clk_tw1[access]); + } +} + +static void sossi_set_bits_per_cycle(int bpc) +{ + u32 l; + int bus_pick_count, bus_pick_width; + + DBGPRINT(2, "bits_per_cycle %d\n", bpc); + /* We set explicitly the the bus_pick_count as well, although + * with remapping/reordering disabled it will be calculated by HW + * as (32 / bus_pick_width). + */ + switch (bpc) { + case 8: + bus_pick_count = 4; + bus_pick_width = 8; + break; + case 16: + bus_pick_count = 2; + bus_pick_width = 16; + break; + default: + BUG(); + return; + } + l = sossi_read_reg(SOSSI_INIT3_REG); + sossi.bus_pick_width = bus_pick_width; + l &= ~0x3ff; + l |= ((bus_pick_count - 1) << 5) | ((bus_pick_width - 1) & 0x1f); + sossi_write_reg(SOSSI_INIT3_REG, l); +} + +static void sossi_start_transfer(void) +{ + /* WE */ + sossi_clear_bits(SOSSI_INIT2_REG, 1 << 4); + /* CS active low */ + sossi_clear_bits(SOSSI_INIT1_REG, 1 << 30); + /* FIXME: locking? */ +} + +static void sossi_stop_transfer(void) +{ + /* WE */ + sossi_set_bits(SOSSI_INIT2_REG, 1 << 4); + /* CS active low */ + sossi_set_bits(SOSSI_INIT1_REG, 1 << 30); + /* FIXME: locking? */ +} + +static void wait_end_of_write(void) +{ + /* Before reading we must check if some writings are going on */ + while (!(sossi_read_reg(SOSSI_INIT2_REG) & (1 << 3))); +} + +static void send_data(const void *data, unsigned int len) +{ + while (len >= 4) { + sossi_write_reg(SOSSI_FIFO_REG, *(const u32 *) data); + len -= 4; + data += 4; + } + while (len >= 2) { + sossi_write_reg16(SOSSI_FIFO_REG, *(const u16 *) data); + len -= 2; + data += 2; + } + while (len) { + sossi_write_reg8(SOSSI_FIFO_REG, *(const u8 *) data); + len--; + data++; + } +} + +static void set_cycles(unsigned int len) +{ + unsigned long nr_cycles = len / (sossi.bus_pick_width / 8); + + BUG_ON((nr_cycles - 1) & ~0x3ffff); + + sossi_clear_bits(SOSSI_INIT1_REG, 0x3ffff); + sossi_set_bits(SOSSI_INIT1_REG, (nr_cycles - 1) & 0x3ffff); +} + +static void sossi_write_command(const void *data, unsigned int len) +{ + set_timing(WR_ACCESS); + /* CMD#/DATA */ + sossi_clear_bits(SOSSI_INIT1_REG, 1 << 18); + set_cycles(len); + sossi_start_transfer(); + send_data(data, len); + sossi_stop_transfer(); + wait_end_of_write(); +} + +static void sossi_write_data(const void *data, unsigned int len) +{ + set_timing(WR_ACCESS); + /* CMD#/DATA */ + sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); + set_cycles(len); + sossi_start_transfer(); + send_data(data, len); + sossi_stop_transfer(); + wait_end_of_write(); +} + +static void sossi_transfer_area(int width, int height, + void (callback)(void *data), void *data) +{ + BUG_ON(callback == NULL); + + sossi.lcdc_callback = callback; + sossi.lcdc_callback_data = data; + + set_timing(WR_ACCESS); + /* CMD#/DATA */ + sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); + set_cycles(width * height * sossi.bus_pick_width / 8); + + DBGPRINT(2, "SOSSI_INIT1_REG %08x\n", sossi_read_reg(SOSSI_INIT1_REG)); + + sossi_start_transfer(); + omap_enable_lcd_dma(); +} + +static void sossi_dma_callback(void *data) +{ + omap_stop_lcd_dma(); + sossi_stop_transfer(); + sossi.lcdc_callback(sossi.lcdc_callback_data); +} + +static void sossi_read_data(void *data, unsigned int len) +{ + set_timing(RD_ACCESS); + /* CMD#/DATA */ + sossi_set_bits(SOSSI_INIT1_REG, 1 << 18); + set_cycles(len); + sossi_start_transfer(); + while (len >= 4) { + *(u32 *) data = sossi_read_reg(SOSSI_FIFO_REG); + len -= 4; + data += 4; + } + while (len >= 2) { + *(u16 *) data = sossi_read_reg16(SOSSI_FIFO_REG); + len -= 2; + data += 2; + } + while (len) { + *(u8 *) data = sossi_read_reg8(SOSSI_FIFO_REG); + len--; + data++; + } + sossi_stop_transfer(); +} + +struct lcd_ctrl_extif sossi_extif = { + .init = sossi_init, + .cleanup = sossi_cleanup, + .get_clk_info = sossi_get_clk_info, + .convert_timings = sossi_convert_timings, + .set_timings = sossi_set_timings, + .set_bits_per_cycle = sossi_set_bits_per_cycle, + .write_command = sossi_write_command, + .read_data = sossi_read_data, + .write_data = sossi_write_data, + .transfer_area = sossi_transfer_area, +}; diff --git a/include/asm-arm/.gitignore b/include/asm-arm/.gitignore new file mode 100644 index 00000000000..e1bf4707aa7 --- /dev/null +++ b/include/asm-arm/.gitignore @@ -0,0 +1,3 @@ +arch +asm-offsets.h +mach-types.h diff --git a/include/asm-arm/arch-omap/aic23.h b/include/asm-arm/arch-omap/aic23.h index 590bac25b7c..6513065941d 100644 --- a/include/asm-arm/arch-omap/aic23.h +++ b/include/asm-arm/arch-omap/aic23.h @@ -57,6 +57,7 @@ #define LHV_MIN 0x0000 // Analog audio path control register +#define STA_REG(x) ((x)<<6) #define STE_ENABLED 0x0020 #define DAC_SELECTED 0x0010 #define BYPASS_ON 0x0008 @@ -109,4 +110,7 @@ #define TLV320AIC23ID1 (0x1a) // cs low #define TLV320AIC23ID2 (0x1b) // cs high +void tlv320aic23_power_up(void); +void tlv320aic23_power_down(void); + #endif /* __ASM_ARCH_AIC23_H */ diff --git a/include/asm-arm/arch-omap/board-apollon.h b/include/asm-arm/arch-omap/board-apollon.h new file mode 100644 index 00000000000..de0c5b792c5 --- /dev/null +++ b/include/asm-arm/arch-omap/board-apollon.h @@ -0,0 +1,45 @@ +/* + * linux/include/asm-arm/arch-omap/board-apollon.h + * + * Hardware definitions for Samsung OMAP24XX Apollon board. + * + * Initial creation by Kyungmin Park + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ASM_ARCH_OMAP_APOLLON_H +#define __ASM_ARCH_OMAP_APOLLON_H + +/* Placeholder for APOLLON specific defines */ +/* GPMC CS0 */ +#define APOLLON_CS0_BASE 0x00000000 +/* GPMC CS1 */ +#define APOLLON_CS1_BASE 0x08000000 +#define APOLLON_ETHR_START (APOLLON_CS1_BASE + 0x300) +#define APOLLON_ETHR_GPIO_IRQ 74 +/* GPMC CS2 - reserved for OneNAND */ +#define APOLLON_CS2_BASE 0x10000000 +/* GPMC CS3 - reserved for NOR or NAND */ +#define APOLLON_CS3_BASE 0x18000000 + +#endif /* __ASM_ARCH_OMAP_APOLLON_H */ + diff --git a/include/asm-arm/arch-omap/board-h2.h b/include/asm-arm/arch-omap/board-h2.h index 39ca5a31aee..b2888ef9e9b 100644 --- a/include/asm-arm/arch-omap/board-h2.h +++ b/include/asm-arm/arch-omap/board-h2.h @@ -34,9 +34,5 @@ /* At OMAP1610 Innovator the Ethernet is directly connected to CS1 */ #define OMAP1610_ETHR_START 0x04000300 -/* Samsung NAND flash at CS2B or CS3(NAND Boot) */ -#define OMAP_NAND_FLASH_START1 0x0A000000 /* CS2B */ -#define OMAP_NAND_FLASH_START2 0x0C000000 /* CS3 */ - #endif /* __ASM_ARCH_OMAP_H2_H */ diff --git a/include/asm-arm/arch-omap/board-h3.h b/include/asm-arm/arch-omap/board-h3.h index 1b12c1dcc2f..761ea0a1789 100644 --- a/include/asm-arm/arch-omap/board-h3.h +++ b/include/asm-arm/arch-omap/board-h3.h @@ -30,10 +30,6 @@ /* In OMAP1710 H3 the Ethernet is directly connected to CS1 */ #define OMAP1710_ETHR_START 0x04000300 -/* Samsung NAND flash at CS2B or CS3(NAND Boot) */ -#define OMAP_NAND_FLASH_START1 0x0A000000 /* CS2B */ -#define OMAP_NAND_FLASH_START2 0x0C000000 /* CS3 */ - #define MAXIRQNUM (IH_BOARD_BASE) #define MAXFIQNUM MAXIRQNUM #define MAXSWINUM MAXIRQNUM diff --git a/include/asm-arm/arch-omap/board-h4.h b/include/asm-arm/arch-omap/board-h4.h index 33ea29a4165..7ef664bc9e3 100644 --- a/include/asm-arm/arch-omap/board-h4.h +++ b/include/asm-arm/arch-omap/board-h4.h @@ -33,12 +33,6 @@ /* GPMC CS1 */ #define OMAP24XX_ETHR_START 0x08000300 #define OMAP24XX_ETHR_GPIO_IRQ 92 - -#define H4_CS0_BASE 0x04000000 - -#define H4_CS0_BASE 0x04000000 - -#define H4_CS0_BASE 0x04000000 - +#define H4_CS0_BASE 0x04000000 #endif /* __ASM_ARCH_OMAP_H4_H */ diff --git a/include/asm-arm/arch-omap/board-netstar.h b/include/asm-arm/arch-omap/board-netstar.h deleted file mode 100644 index 77cc0fb54d5..00000000000 --- a/include/asm-arm/arch-omap/board-netstar.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2004 2N Telekomunikace, Ladislav Michl - * - * Hardware definitions for OMAP5910 based NetStar board. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#ifndef __ASM_ARCH_NETSTAR_H -#define __ASM_ARCH_NETSTAR_H - -#include - -#define OMAP_NAND_FLASH_START1 OMAP_CS1_PHYS + (1 << 23) -#define OMAP_NAND_FLASH_START2 OMAP_CS1_PHYS + (2 << 23) - -#endif /* __ASM_ARCH_NETSTAR_H */ diff --git a/include/asm-arm/arch-omap/board-nokia.h b/include/asm-arm/arch-omap/board-nokia.h new file mode 100644 index 00000000000..72deea20349 --- /dev/null +++ b/include/asm-arm/arch-omap/board-nokia.h @@ -0,0 +1,54 @@ +/* + * linux/include/asm-arm/arch-omap/board-nokia.h + * + * Information structures for Nokia-specific board config data + * + * Copyright (C) 2005 Nokia Corporation + */ + +#ifndef _OMAP_BOARD_NOKIA_H +#define _OMAP_BOARD_NOKIA_H + +#include + +#define OMAP_TAG_NOKIA_BT 0x4e01 +#define OMAP_TAG_WLAN_CX3110X 0x4e02 +#define OMAP_TAG_CBUS 0x4e03 +#define OMAP_TAG_EM_ASIC_BB5 0x4e04 + + +#define BT_CHIP_CSR 1 +#define BT_CHIP_TI 2 + +#define BT_SYSCLK_12 1 +#define BT_SYSCLK_38_4 2 + +struct omap_bluetooth_config { + u8 chip_type; + u8 bt_wakeup_gpio; + u8 host_wakeup_gpio; + u8 reset_gpio; + u8 bt_uart; + u8 bd_addr[6]; + u8 bt_sysclk; +}; + +struct omap_wlan_cx3110x_config { + u8 chip_type; + s16 power_gpio; + s16 irq_gpio; + s16 spi_cs_gpio; +}; + +struct omap_cbus_config { + s16 clk_gpio; + s16 dat_gpio; + s16 sel_gpio; +}; + +struct omap_em_asic_bb5_config { + s16 retu_irq_gpio; + s16 tahvo_irq_gpio; +}; + +#endif diff --git a/include/asm-arm/arch-omap/board-perseus2.h b/include/asm-arm/arch-omap/board-perseus2.h index 691e52a52b4..eb74420cb43 100644 --- a/include/asm-arm/arch-omap/board-perseus2.h +++ b/include/asm-arm/arch-omap/board-perseus2.h @@ -42,8 +42,4 @@ #define NR_IRQS (MAXIRQNUM + 1) -/* Samsung NAND flash at CS2B or CS3(NAND Boot) */ -#define OMAP_NAND_FLASH_START1 0x0A000000 /* CS2B */ -#define OMAP_NAND_FLASH_START2 0x0C000000 /* CS3 */ - #endif diff --git a/include/asm-arm/arch-omap/board.h b/include/asm-arm/arch-omap/board.h index a0040cd8663..6d6240a4681 100644 --- a/include/asm-arm/arch-omap/board.h +++ b/include/asm-arm/arch-omap/board.h @@ -21,9 +21,12 @@ #define OMAP_TAG_LCD 0x4f05 #define OMAP_TAG_GPIO_SWITCH 0x4f06 #define OMAP_TAG_UART 0x4f07 +#define OMAP_TAG_FBMEM 0x4f08 +#define OMAP_TAG_STI_CONSOLE 0x4f09 #define OMAP_TAG_BOOT_REASON 0x4f80 #define OMAP_TAG_FLASH_PART 0x4f81 +#define OMAP_TAG_VERSION_STR 0x4f82 struct omap_clock_config { /* 0 for 12 MHz, 1 for 13 MHz and 2 for 19.2 MHz */ @@ -54,6 +57,11 @@ struct omap_serial_console_config { u32 console_speed; }; +struct omap_sti_console_config { + unsigned enable:1; + u8 channel; +}; + struct omap_usb_config { /* Configure drivers according to the connectors on your board: * - "A" connector (rectagular) @@ -87,6 +95,13 @@ struct omap_lcd_config { char ctrl_name[16]; }; +struct omap_fbmem_config { + u32 fb_sram_start; + u32 fb_sram_size; + u32 fb_sdram_start; + u32 fb_sdram_size; +}; + /* Cover: * high -> closed * low -> open @@ -106,6 +121,12 @@ struct omap_gpio_switch_config { int key_code:24; /* Linux key code */ }; +struct omap_uart_config { + /* Bit field of UARTs present; bit 0 --> UART1 */ + unsigned int enabled_uarts; +}; + + struct omap_flash_part_config { char part_table[0]; }; @@ -114,11 +135,14 @@ struct omap_boot_reason_config { char reason_str[12]; }; -struct omap_uart_config { - /* Bit field of UARTs present; bit 0 --> UART1 */ - unsigned int enabled_uarts; +struct omap_version_config { + char component[12]; + char version[12]; }; + +#include + struct omap_board_config_entry { u16 tag; u16 len; diff --git a/include/asm-arm/arch-omap/clock.h b/include/asm-arm/arch-omap/clock.h index 46a0402696d..3c4eb9fbe48 100644 --- a/include/asm-arm/arch-omap/clock.h +++ b/include/asm-arm/arch-omap/clock.h @@ -19,6 +19,7 @@ struct clk { struct list_head node; struct module *owner; const char *name; + int id; struct clk *parent; unsigned long rate; __u32 flags; @@ -57,6 +58,7 @@ extern void propagate_rate(struct clk *clk); extern void followparent_recalc(struct clk * clk); extern void clk_allow_idle(struct clk *clk); extern void clk_deny_idle(struct clk *clk); +extern int clk_get_usecount(struct clk *clk); /* Clock flags */ #define RATE_CKCTL (1 << 0) /* Main fixed ratio clocks */ @@ -80,10 +82,11 @@ extern void clk_deny_idle(struct clk *clk); #define CM_PLL_SEL1 (1 << 18) #define CM_PLL_SEL2 (1 << 19) #define CM_SYSCLKOUT_SEL1 (1 << 20) -#define CLOCK_IN_OMAP730 (1 << 21) -#define CLOCK_IN_OMAP1510 (1 << 22) -#define CLOCK_IN_OMAP16XX (1 << 23) -#define CLOCK_IN_OMAP242X (1 << 24) -#define CLOCK_IN_OMAP243X (1 << 25) +#define CLOCK_IN_OMAP310 (1 << 21) +#define CLOCK_IN_OMAP730 (1 << 22) +#define CLOCK_IN_OMAP1510 (1 << 23) +#define CLOCK_IN_OMAP16XX (1 << 24) +#define CLOCK_IN_OMAP242X (1 << 25) +#define CLOCK_IN_OMAP243X (1 << 26) #endif diff --git a/include/asm-arm/arch-omap/dma.h b/include/asm-arm/arch-omap/dma.h index d4e73efcb81..ca1202312a4 100644 --- a/include/asm-arm/arch-omap/dma.h +++ b/include/asm-arm/arch-omap/dma.h @@ -404,6 +404,7 @@ extern void omap_free_lcd_dma(void); extern void omap_setup_lcd_dma(void); extern void omap_enable_lcd_dma(void); extern void omap_stop_lcd_dma(void); +extern int omap_lcd_dma_ext_running(void); extern void omap_set_lcd_dma_ext_controller(int external); extern void omap_set_lcd_dma_single_transfer(int single); extern void omap_set_lcd_dma_b1(unsigned long addr, u16 fb_xres, u16 fb_yres, diff --git a/include/asm-arm/arch-omap/dmtimer.h b/include/asm-arm/arch-omap/dmtimer.h index 11772c792f3..e6522e6a383 100644 --- a/include/asm-arm/arch-omap/dmtimer.h +++ b/include/asm-arm/arch-omap/dmtimer.h @@ -88,5 +88,6 @@ unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer); void omap_dm_timer_reset_counter(struct omap_dm_timer *timer); int omap_dm_timers_active(void); +u32 omap_dm_timer_modify_idlect_mask(u32 inputmask); #endif /* __ASM_ARCH_TIMER_H */ diff --git a/include/asm-arm/arch-omap/dsp.h b/include/asm-arm/arch-omap/dsp.h index 57bf4f39ca5..06dad83dd41 100644 --- a/include/asm-arm/arch-omap/dsp.h +++ b/include/asm-arm/arch-omap/dsp.h @@ -181,10 +181,16 @@ struct omap_dsp_varinfo { #define OMAP_DSP_MBCMD_PM_ENABLE 0x01 #define OMAP_DSP_MBCMD_KFUNC_FBCTL 0x00 +#define OMAP_DSP_MBCMD_KFUNC_AUDIO_PWR 0x01 +#define OMAP_DSP_MBCMD_FBCTL_UPD 0x0000 #define OMAP_DSP_MBCMD_FBCTL_ENABLE 0x0002 #define OMAP_DSP_MBCMD_FBCTL_DISABLE 0x0003 +#define OMAP_DSP_MBCMD_AUDIO_PWR_UP 0x0000 +#define OMAP_DSP_MBCMD_AUDIO_PWR_DOWN1 0x0001 +#define OMAP_DSP_MBCMD_AUDIO_PWR_DOWN2 0x0002 + #define OMAP_DSP_MBCMD_TDEL_SAFE 0x0000 #define OMAP_DSP_MBCMD_TDEL_KILL 0x0001 diff --git a/include/asm-arm/arch-omap/dsp_common.h b/include/asm-arm/arch-omap/dsp_common.h index 4fcce694405..16a459dfa71 100644 --- a/include/asm-arm/arch-omap/dsp_common.h +++ b/include/asm-arm/arch-omap/dsp_common.h @@ -27,11 +27,12 @@ #ifndef ASM_ARCH_DSP_COMMON_H #define ASM_ARCH_DSP_COMMON_H -void omap_dsp_pm_suspend(void); -void omap_dsp_pm_resume(void); -void omap_dsp_request_mpui(void); -void omap_dsp_release_mpui(void); -int omap_dsp_request_mem(void); -int omap_dsp_release_mem(void); +extern void omap_dsp_request_mpui(void); +extern void omap_dsp_release_mpui(void); +extern int omap_dsp_request_mem(void); +extern int omap_dsp_release_mem(void); + +extern void (*omap_dsp_audio_pwr_up_request)(int stage); +extern void (*omap_dsp_audio_pwr_down_request)(int stage); #endif /* ASM_ARCH_DSP_COMMON_H */ diff --git a/include/asm-arm/arch-omap/gpioexpander.h b/include/asm-arm/arch-omap/gpioexpander.h new file mode 100644 index 00000000000..7a43b0a912e --- /dev/null +++ b/include/asm-arm/arch-omap/gpioexpander.h @@ -0,0 +1,24 @@ +/* + * linux/include/asm-arm/arch-omap/gpioexpander.h + * + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef __ASM_ARCH_OMAP_GPIOEXPANDER_H +#define __ASM_ARCH_OMAP_GPIOEXPANDER_H + +/* Function Prototypes for GPIO Expander functions */ + +int read_gpio_expa(u8 *, int); +int write_gpio_expa(u8 , int); + +#endif /* __ASM_ARCH_OMAP_GPIOEXPANDER_H */ diff --git a/include/asm-arm/arch-omap/hardware.h b/include/asm-arm/arch-omap/hardware.h index 5406b875c42..7909b729826 100644 --- a/include/asm-arm/arch-omap/hardware.h +++ b/include/asm-arm/arch-omap/hardware.h @@ -306,6 +306,10 @@ #include "board-h4.h" #endif +#ifdef CONFIG_MACH_OMAP_APOLLON +#include "board-apollon.h" +#endif + #ifdef CONFIG_MACH_OMAP_OSK #include "board-osk.h" #endif @@ -314,10 +318,6 @@ #include "board-voiceblue.h" #endif -#ifdef CONFIG_MACH_NETSTAR -#include "board-netstar.h" -#endif - #endif /* !__ASSEMBLER__ */ #endif /* __ASM_ARCH_OMAP_HARDWARE_H */ diff --git a/include/asm-arm/arch-omap/irda.h b/include/asm-arm/arch-omap/irda.h new file mode 100644 index 00000000000..805ae3575e4 --- /dev/null +++ b/include/asm-arm/arch-omap/irda.h @@ -0,0 +1,36 @@ +/* + * linux/include/asm-arm/arch-omap/irda.h + * + * Copyright (C) 2005-2006 Komal Shah + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef ASMARM_ARCH_IRDA_H +#define ASMARM_ARCH_IRDA_H + +/* board specific transceiver capabilities */ + +#define IR_SEL 1 /* Selects IrDA */ +#define IR_SIRMODE 2 +#define IR_FIRMODE 4 +#define IR_MIRMODE 8 + +struct omap_irda_config { + int transceiver_cap; + int (*transceiver_mode)(struct device *dev, int mode); + int (*select_irda)(struct device *dev, int state); + /* Very specific to the needs of some platforms (h3,h4) + * having calls which can sleep in irda_set_speed. + */ + struct work_struct gpio_expa; + int rx_channel; + int tx_channel; + unsigned long dest_start; + unsigned long src_start; + int tx_trigger; + int rx_trigger; +}; + +#endif diff --git a/include/asm-arm/arch-omap/irqs.h b/include/asm-arm/arch-omap/irqs.h index 4ffce1d7775..42098d99f30 100644 --- a/include/asm-arm/arch-omap/irqs.h +++ b/include/asm-arm/arch-omap/irqs.h @@ -242,6 +242,11 @@ #define INT_24XX_GPIO_BANK2 30 #define INT_24XX_GPIO_BANK3 31 #define INT_24XX_GPIO_BANK4 32 +#define INT_24XX_MCBSP1_IRQ_TX 59 +#define INT_24XX_MCBSP1_IRQ_RX 60 +#define INT_24XX_MCBSP2_IRQ_TX 62 +#define INT_24XX_MCBSP2_IRQ_RX 63 +#define INT_24XX_UART3_IRQ 74 /* Max. 128 level 2 IRQs (OMAP1610), 192 GPIOs (OMAP730) and * 16 MPUIO lines */ diff --git a/include/asm-arm/arch-omap/keypad.h b/include/asm-arm/arch-omap/keypad.h new file mode 100644 index 00000000000..8a023a984ac --- /dev/null +++ b/include/asm-arm/arch-omap/keypad.h @@ -0,0 +1,36 @@ +/* + * linux/include/asm-arm/arch-omap/keypad.h + * + * Copyright (C) 2006 Komal Shah + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#ifndef ASMARM_ARCH_KEYPAD_H +#define ASMARM_ARCH_KEYPAD_H + +struct omap_kp_platform_data { + int rows; + int cols; + int *keymap; + unsigned int rep:1; + /* specific to OMAP242x*/ + unsigned int *row_gpios; + unsigned int *col_gpios; +}; + +/* Group (0..3) -- when multiple keys are pressed, only the + * keys pressed in the same group are considered as pressed. This is + * in order to workaround certain crappy HW designs that produce ghost + * keypresses. */ +#define GROUP_0 (0 << 16) +#define GROUP_1 (1 << 16) +#define GROUP_2 (2 << 16) +#define GROUP_3 (3 << 16) +#define GROUP_MASK GROUP_3 + +#define KEY(col, row, val) (((col) << 28) | ((row) << 24) | (val)) + +#endif + diff --git a/include/asm-arm/arch-omap/lcd_lph8923.h b/include/asm-arm/arch-omap/lcd_lph8923.h new file mode 100644 index 00000000000..004e67e22ca --- /dev/null +++ b/include/asm-arm/arch-omap/lcd_lph8923.h @@ -0,0 +1,14 @@ +#ifndef __LCD_LPH8923_H +#define __LCD_LPH8923_H + +enum lcd_lph8923_test_num { + LCD_LPH8923_TEST_RGB_LINES, +}; + +enum lcd_lph8923_test_result { + LCD_LPH8923_TEST_SUCCESS, + LCD_LPH8923_TEST_INVALID, + LCD_LPH8923_TEST_FAILED, +}; + +#endif diff --git a/include/asm-arm/arch-omap/mcbsp.h b/include/asm-arm/arch-omap/mcbsp.h index e79d98ab2ab..ed0dde4f721 100644 --- a/include/asm-arm/arch-omap/mcbsp.h +++ b/include/asm-arm/arch-omap/mcbsp.h @@ -37,6 +37,11 @@ #define OMAP1610_MCBSP2_BASE 0xfffb1000 #define OMAP1610_MCBSP3_BASE 0xe1017000 +#define OMAP24XX_MCBSP1_BASE 0x48074000 +#define OMAP24XX_MCBSP2_BASE 0x48076000 + +#if defined(CONFIG_ARCH_OMAP15XX) || defined(CONFIG_ARCH_OMAP16XX) || defined(CONFIG_ARCH_OMAP730) + #define OMAP_MCBSP_REG_DRR2 0x00 #define OMAP_MCBSP_REG_DRR1 0x02 #define OMAP_MCBSP_REG_DXR2 0x04 @@ -71,9 +76,62 @@ #define OMAP_MAX_MCBSP_COUNT 3 +#define AUDIO_MCBSP_DATAWRITE (OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DXR1) +#define AUDIO_MCBSP_DATAREAD (OMAP1510_MCBSP1_BASE + OMAP_MCBSP_REG_DRR1) + +#define AUDIO_MCBSP OMAP_MCBSP1 +#define AUDIO_DMA_TX OMAP_DMA_MCBSP1_TX +#define AUDIO_DMA_RX OMAP_DMA_MCBSP1_RX + +#elif defined(CONFIG_ARCH_OMAP24XX) + +#define OMAP_MCBSP_REG_DRR2 0x00 +#define OMAP_MCBSP_REG_DRR1 0x04 +#define OMAP_MCBSP_REG_DXR2 0x08 +#define OMAP_MCBSP_REG_DXR1 0x0C +#define OMAP_MCBSP_REG_SPCR2 0x10 +#define OMAP_MCBSP_REG_SPCR1 0x14 +#define OMAP_MCBSP_REG_RCR2 0x18 +#define OMAP_MCBSP_REG_RCR1 0x1C +#define OMAP_MCBSP_REG_XCR2 0x20 +#define OMAP_MCBSP_REG_XCR1 0x24 +#define OMAP_MCBSP_REG_SRGR2 0x28 +#define OMAP_MCBSP_REG_SRGR1 0x2C +#define OMAP_MCBSP_REG_MCR2 0x30 +#define OMAP_MCBSP_REG_MCR1 0x34 +#define OMAP_MCBSP_REG_RCERA 0x38 +#define OMAP_MCBSP_REG_RCERB 0x3C +#define OMAP_MCBSP_REG_XCERA 0x40 +#define OMAP_MCBSP_REG_XCERB 0x44 +#define OMAP_MCBSP_REG_PCR0 0x48 +#define OMAP_MCBSP_REG_RCERC 0x4C +#define OMAP_MCBSP_REG_RCERD 0x50 +#define OMAP_MCBSP_REG_XCERC 0x54 +#define OMAP_MCBSP_REG_XCERD 0x58 +#define OMAP_MCBSP_REG_RCERE 0x5C +#define OMAP_MCBSP_REG_RCERF 0x60 +#define OMAP_MCBSP_REG_XCERE 0x64 +#define OMAP_MCBSP_REG_XCERF 0x68 +#define OMAP_MCBSP_REG_RCERG 0x6C +#define OMAP_MCBSP_REG_RCERH 0x70 +#define OMAP_MCBSP_REG_XCERG 0x74 +#define OMAP_MCBSP_REG_XCERH 0x78 + +#define OMAP_MAX_MCBSP_COUNT 2 + +#define AUDIO_MCBSP_DATAWRITE (OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DXR1) +#define AUDIO_MCBSP_DATAREAD (OMAP24XX_MCBSP2_BASE + OMAP_MCBSP_REG_DRR1) + +#define AUDIO_MCBSP OMAP_MCBSP2 +#define AUDIO_DMA_TX OMAP24XX_DMA_MCBSP2_TX +#define AUDIO_DMA_RX OMAP24XX_DMA_MCBSP2_RX + +#endif + #define OMAP_MCBSP_READ(base, reg) __raw_readw((base) + OMAP_MCBSP_REG_##reg) #define OMAP_MCBSP_WRITE(base, reg, val) __raw_writew((val), (base) + OMAP_MCBSP_REG_##reg) + /************************** McBSP SPCR1 bit definitions ***********************/ #define RRST 0x0001 #define RRDY 0x0002 @@ -195,6 +253,10 @@ typedef enum { OMAP_MCBSP3, } omap_mcbsp_id; +typedef int __bitwise omap_mcbsp_io_type_t; +#define OMAP_MCBSP_IRQ_IO ((__force omap_mcbsp_io_type_t) 1) +#define OMAP_MCBSP_POLL_IO ((__force omap_mcbsp_io_type_t) 2) + typedef enum { OMAP_MCBSP_WORD_8 = 0, OMAP_MCBSP_WORD_12, @@ -246,6 +308,9 @@ u32 omap_mcbsp_recv_word(unsigned int id); int omap_mcbsp_xmit_buffer(unsigned int id, dma_addr_t buffer, unsigned int length); int omap_mcbsp_recv_buffer(unsigned int id, dma_addr_t buffer, unsigned int length); +int omap_mcbsp_spi_master_xmit_word_poll(unsigned int id, u32 word); +int omap_mcbsp_spi_master_recv_word_poll(unsigned int id, u32 * word); + /* SPI specific API */ void omap_mcbsp_set_spi_mode(unsigned int id, const struct omap_mcbsp_spi_cfg * spi_cfg); diff --git a/include/asm-arm/arch-omap/mcspi.h b/include/asm-arm/arch-omap/mcspi.h new file mode 100644 index 00000000000..9e7f40a88e1 --- /dev/null +++ b/include/asm-arm/arch-omap/mcspi.h @@ -0,0 +1,16 @@ +#ifndef _OMAP2_MCSPI_H +#define _OMAP2_MCSPI_H + +struct omap2_mcspi_platform_config { + unsigned long base; + unsigned short num_cs; +}; + +struct omap2_mcspi_device_config { + unsigned turbo_mode:1; + + /* Do we want one channel enabled at the same time? */ + unsigned single_channel:1; +}; + +#endif diff --git a/include/asm-arm/arch-omap/menelaus.h b/include/asm-arm/arch-omap/menelaus.h index 46be8b8d634..88cd4c87f0d 100644 --- a/include/asm-arm/arch-omap/menelaus.h +++ b/include/asm-arm/arch-omap/menelaus.h @@ -7,7 +7,7 @@ #ifndef __ASM_ARCH_MENELAUS_H #define __ASM_ARCH_MENELAUS_H -extern void menelaus_mmc_register(void (*callback)(u8 card_mask), +extern void menelaus_mmc_register(void (*callback)(unsigned long data, u8 card_mask), unsigned long data); extern void menelaus_mmc_remove(void); extern void menelaus_mmc_opendrain(int enable); diff --git a/include/asm-arm/arch-omap/mux.h b/include/asm-arm/arch-omap/mux.h index 13415a9aab0..28fd67542dd 100644 --- a/include/asm-arm/arch-omap/mux.h +++ b/include/asm-arm/arch-omap/mux.h @@ -112,14 +112,13 @@ * as mux config */ #define MUX_CFG_730(desc, mux_reg, mode_offset, mode, \ - pull_reg, pull_bit, pull_status, \ - pu_pd_reg, pu_pd_status, debug_status)\ + pull_bit, pull_status, debug_status)\ { \ .name = desc, \ .debug = debug_status, \ MUX_REG_730(mux_reg, mode_offset, mode) \ PULL_REG_730(mux_reg, pull_bit, pull_status) \ - PU_PD_REG(pu_pd_reg, pu_pd_status) \ + PU_PD_REG(NA, 0) \ }, #define MUX_CFG_24XX(desc, reg_offset, mode, \ @@ -172,6 +171,11 @@ enum omap730_index { E4_730_KBC2, F4_730_KBC3, E3_730_KBC4, + + /* USB */ + AA17_730_USB_DM, + W16_730_USB_PU_EN, + W17_730_USB_VBUSI, }; enum omap1xxx_index { @@ -403,9 +407,53 @@ enum omap24xx_index { /* 24xx Menelaus interrupt */ W19_24XX_SYS_NIRQ, + /* 24xx clock */ + W14_24XX_SYS_CLKOUT, + + /* 242X McBSP */ + Y15_24XX_MCBSP2_CLKX, + R14_24XX_MCBSP2_FSX, + W15_24XX_MCBSP2_DR, + V15_24XX_MCBSP2_DX, + /* 24xx GPIO */ + M21_242X_GPIO11, + AA10_242X_GPIO13, + AA6_242X_GPIO14, + AA4_242X_GPIO15, + Y11_242X_GPIO16, + AA12_242X_GPIO17, + AA8_242X_GPIO58, Y20_24XX_GPIO60, + W4__24XX_GPIO74, M15_24XX_GPIO92, + V14_24XX_GPIO117, + + P20_24XX_TSC_IRQ, + + /* UART3 */ + K15_24XX_UART3_TX, + K14_24XX_UART3_RX, + + /* Keypad GPIO*/ + T19_24XX_KBR0, + R19_24XX_KBR1, + V18_24XX_KBR2, + M21_24XX_KBR3, + E5__24XX_KBR4, + M18_24XX_KBR5, + R20_24XX_KBC0, + M14_24XX_KBC1, + H19_24XX_KBC2, + V17_24XX_KBC3, + P21_24XX_KBC4, + L14_24XX_KBC5, + N19_24XX_KBC6, + + /* 24xx Menelaus Keypad GPIO */ + B3__24XX_KBR5, + AA4_24XX_KBC2, + B13_24XX_KBC6, }; #ifdef CONFIG_OMAP_MUX diff --git a/include/asm-arm/arch-omap/omapfb.h b/include/asm-arm/arch-omap/omapfb.h index 4ba2622cc14..7eade7be175 100644 --- a/include/asm-arm/arch-omap/omapfb.h +++ b/include/asm-arm/arch-omap/omapfb.h @@ -34,9 +34,10 @@ #define OMAPFB_MIRROR OMAP_IOW(31, int) #define OMAPFB_SYNC_GFX OMAP_IO(37) #define OMAPFB_VSYNC OMAP_IO(38) -#define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, enum omapfb_update_mode) +#define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, int) +#define OMAPFB_UPDATE_WINDOW_OLD OMAP_IOW(41, struct omapfb_update_window_old) #define OMAPFB_GET_CAPS OMAP_IOR(42, unsigned long) -#define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, enum omapfb_update_mode) +#define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, int) #define OMAPFB_LCD_TEST OMAP_IOW(45, int) #define OMAPFB_CTRL_TEST OMAP_IOW(46, int) #define OMAPFB_UPDATE_WINDOW OMAP_IOW(47, struct omapfb_update_window) @@ -66,9 +67,14 @@ enum omapfb_color_format { }; struct omapfb_update_window { - u32 x, y; - u32 width, height; - u32 format; + __u32 x, y; + __u32 width, height; + __u32 format; +}; + +struct omapfb_update_window_old { + __u32 x, y; + __u32 width, height; }; enum omapfb_plane { @@ -83,17 +89,17 @@ enum omapfb_channel_out { }; struct omapfb_setup_plane { - u8 plane; - u8 channel_out; - u32 offset; - u32 pos_x, pos_y; - u32 width, height; - u32 color_mode; + __u8 plane; + __u8 channel_out; + __u32 offset; + __u32 pos_x, pos_y; + __u32 width, height; + __u32 color_mode; }; struct omapfb_enable_plane { - u8 plane; - u8 enable; + __u8 plane; + __u8 enable; }; enum omapfb_color_key_type { @@ -103,10 +109,10 @@ enum omapfb_color_key_type { }; struct omapfb_color_key { - u8 channel_out; - u32 background; - u32 trans_key; - u8 key_type; + __u8 channel_out; + __u32 background; + __u32 trans_key; + __u8 key_type; }; enum omapfb_update_mode { @@ -121,6 +127,8 @@ enum omapfb_update_mode { #include #include +#include + #define OMAP_LCDC_INV_VSYNC 0x0001 #define OMAP_LCDC_INV_HSYNC 0x0002 #define OMAP_LCDC_INV_PIX_CLOCK 0x0004 @@ -184,19 +192,38 @@ struct extif_timings { int re_cycle_time; int cs_pulse_width; int access_time; + + int clk_div; + + u32 tim[5]; /* set by extif->convert_timings */ + + int converted; }; struct lcd_ctrl_extif { int (*init) (void); void (*cleanup) (void); + void (*get_clk_info) (u32 *clk_period, u32 *max_clk_div); + int (*convert_timings) (struct extif_timings *timings); void (*set_timings) (const struct extif_timings *timings); - void (*write_command) (u32 cmd); - u32 (*read_data) (void); - void (*write_data) (u32 data); + void (*set_bits_per_cycle)(int bpc); + void (*write_command) (const void *buf, unsigned int len); + void (*read_data) (void *buf, unsigned int len); + void (*write_data) (const void *buf, unsigned int len); void (*transfer_area) (int width, int height, void (callback)(void * data), void *data); + unsigned long max_transmit_size; }; +struct omapfb_notifier_block { + struct notifier_block nb; + void *data; +}; + +typedef int (*omapfb_notifier_callback_t)(struct omapfb_notifier_block *, + unsigned long event, + struct omapfb_device *fbdev); + struct lcd_ctrl { const char *name; void *data; @@ -204,9 +231,11 @@ struct lcd_ctrl { int (*init) (struct omapfb_device *fbdev, int ext_mode, int req_vram_size); void (*cleanup) (void); + void (*bind_client) (struct omapfb_notifier_block *nb); void (*get_vram_layout)(unsigned long *size, void **virt_base, dma_addr_t *phys_base); + int (*mmap) (struct vm_area_struct *vma); unsigned long (*get_caps) (void); int (*set_update_mode)(enum omapfb_update_mode mode); enum omapfb_update_mode (*get_update_mode)(void); @@ -261,12 +290,13 @@ struct omapfb_device { struct device *dev; }; -extern struct lcd_panel h3_panel; -extern struct lcd_panel h2_panel; -extern struct lcd_panel p2_panel; -extern struct lcd_panel osk_panel; -extern struct lcd_panel innovator1610_panel; -extern struct lcd_panel innovator1510_panel; +struct omapfb_platform_data { + struct omap_lcd_config lcd; + struct omap_fbmem_config fbmem; +}; + +#define OMAPFB_EVENT_READY 1 +#define OMAPFB_EVENT_DISABLED 2 #ifdef CONFIG_ARCH_OMAP1 extern struct lcd_ctrl omap1_lcd_ctrl; @@ -274,7 +304,20 @@ extern struct lcd_ctrl omap1_lcd_ctrl; extern struct lcd_ctrl omap2_disp_ctrl; #endif +extern void omapfb_register_panel(struct lcd_panel *panel); extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval); +extern void omapfb_notify_clients(struct omapfb_device *fbdev, + unsigned long event); +extern int omapfb_register_client(struct omapfb_notifier_block *nb, + omapfb_notifier_callback_t callback, + void *callback_data); +extern int omapfb_unregister_client(struct omapfb_notifier_block *nb); +extern int omapfb_update_window_async(struct omapfb_update_window *win, + void (*callback)(void *), + void *callback_data); + +/* in arch/arm/plat-omap/devices.c */ +extern void omapfb_reserve_mem(void); #endif /* __KERNEL__ */ diff --git a/include/asm-arm/arch-omap/pm.h b/include/asm-arm/arch-omap/pm.h index 7c790425e36..05b003f3a94 100644 --- a/include/asm-arm/arch-omap/pm.h +++ b/include/asm-arm/arch-omap/pm.h @@ -49,7 +49,7 @@ /* * ---------------------------------------------------------------------------- - * Powermanagement bitmasks + * Power management bitmasks * ---------------------------------------------------------------------------- */ #define IDLE_WAIT_CYCLES 0x00000fff @@ -112,32 +112,59 @@ #endif #ifndef __ASSEMBLER__ + +#include + +extern void prevent_idle_sleep(void); +extern void allow_idle_sleep(void); + +/** + * clk_deny_idle - Prevents the clock from being idled during MPU idle + * @clk: clock signal handle + */ +void clk_deny_idle(struct clk *clk); + +/** + * clk_allow_idle - Counters previous clk_deny_idle + * @clk: clock signal handle + */ +void clk_deny_idle(struct clk *clk); + extern void omap_pm_idle(void); extern void omap_pm_suspend(void); extern void omap730_cpu_suspend(unsigned short, unsigned short); extern void omap1510_cpu_suspend(unsigned short, unsigned short); extern void omap1610_cpu_suspend(unsigned short, unsigned short); +extern void omap24xx_cpu_suspend(u32 dll_ctrl, u32 cpu_revision); extern void omap730_idle_loop_suspend(void); extern void omap1510_idle_loop_suspend(void); extern void omap1610_idle_loop_suspend(void); +extern void omap24xx_idle_loop_suspend(void); + +extern unsigned int omap730_cpu_suspend_sz; +extern unsigned int omap1510_cpu_suspend_sz; +extern unsigned int omap1610_cpu_suspend_sz; +extern unsigned int omap24xx_cpu_suspend_sz; +extern unsigned int omap730_idle_loop_suspend_sz; +extern unsigned int omap1510_idle_loop_suspend_sz; +extern unsigned int omap1610_idle_loop_suspend_sz; +extern unsigned int omap24xx_idle_loop_suspend_sz; #ifdef CONFIG_OMAP_SERIAL_WAKE extern void omap_serial_wake_trigger(int enable); #else +#define omap_serial_wakeup_init() {} #define omap_serial_wake_trigger(x) {} #endif /* CONFIG_OMAP_SERIAL_WAKE */ -extern unsigned int omap730_cpu_suspend_sz; -extern unsigned int omap730_idle_loop_suspend_sz; -extern unsigned int omap1510_cpu_suspend_sz; -extern unsigned int omap1510_idle_loop_suspend_sz; -extern unsigned int omap1610_cpu_suspend_sz; -extern unsigned int omap1610_idle_loop_suspend_sz; - #define ARM_SAVE(x) arm_sleep_save[ARM_SLEEP_SAVE_##x] = omap_readl(x) #define ARM_RESTORE(x) omap_writel((arm_sleep_save[ARM_SLEEP_SAVE_##x]), (x)) #define ARM_SHOW(x) arm_sleep_save[ARM_SLEEP_SAVE_##x] +#define DSP_SAVE(x) dsp_sleep_save[DSP_SLEEP_SAVE_##x] = __raw_readw(x) +#define DSP_RESTORE(x) __raw_writew((dsp_sleep_save[DSP_SLEEP_SAVE_##x]), (x)) +#define DSP_SHOW(x) dsp_sleep_save[DSP_SLEEP_SAVE_##x] + #define ULPD_SAVE(x) ulpd_sleep_save[ULPD_SLEEP_SAVE_##x] = omap_readw(x) #define ULPD_RESTORE(x) omap_writew((ulpd_sleep_save[ULPD_SLEEP_SAVE_##x]), (x)) #define ULPD_SHOW(x) ulpd_sleep_save[ULPD_SLEEP_SAVE_##x] @@ -154,6 +181,10 @@ extern unsigned int omap1610_idle_loop_suspend_sz; #define MPUI1610_RESTORE(x) omap_writel((mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_##x]), (x)) #define MPUI1610_SHOW(x) mpui1610_sleep_save[MPUI1610_SLEEP_SAVE_##x] +#define OMAP24XX_SAVE(x) omap24xx_sleep_save[OMAP24XX_SLEEP_SAVE_##x] = x +#define OMAP24XX_RESTORE(x) x = omap24xx_sleep_save[OMAP24XX_SLEEP_SAVE_##x] +#define OMAP24XX_SHOW(x) omap24xx_sleep_save[OMAP24XX_SLEEP_SAVE_##x] + /* * List of global OMAP registers to preserve. * More ones like CP and general purpose register values are preserved @@ -176,6 +207,15 @@ enum arm_save_state { ARM_SLEEP_SAVE_SIZE }; +enum dsp_save_state { + DSP_SLEEP_SAVE_START = 0, + /* + * DSP registers 16 bits + */ + DSP_SLEEP_SAVE_DSP_IDLECT2, + DSP_SLEEP_SAVE_SIZE +}; + enum ulpd_save_state { ULPD_SLEEP_SAVE_START = 0, /* @@ -254,5 +294,30 @@ enum mpui1610_save_state { #endif }; +enum omap24xx_save_state { + OMAP24XX_SLEEP_SAVE_START = 0, + OMAP24XX_SLEEP_SAVE_INTC_MIR0, + OMAP24XX_SLEEP_SAVE_INTC_MIR1, + OMAP24XX_SLEEP_SAVE_INTC_MIR2, + OMAP24XX_SLEEP_SAVE_CM_FCLKEN1_CORE, + OMAP24XX_SLEEP_SAVE_CM_FCLKEN2_CORE, + OMAP24XX_SLEEP_SAVE_CM_ICLKEN1_CORE, + OMAP24XX_SLEEP_SAVE_CM_ICLKEN2_CORE, + OMAP24XX_SLEEP_SAVE_CM_ICLKEN4_CORE, + OMAP24XX_SLEEP_SAVE_GPIO1_IRQENABLE1, + OMAP24XX_SLEEP_SAVE_GPIO2_IRQENABLE1, + OMAP24XX_SLEEP_SAVE_GPIO3_IRQENABLE1, + OMAP24XX_SLEEP_SAVE_GPIO4_IRQENABLE1, + OMAP24XX_SLEEP_SAVE_GPIO3_OE, + OMAP24XX_SLEEP_SAVE_GPIO4_OE, + OMAP24XX_SLEEP_SAVE_GPIO3_RISINGDETECT, + OMAP24XX_SLEEP_SAVE_GPIO3_FALLINGDETECT, + OMAP24XX_SLEEP_SAVE_CONTROL_PADCONF_SPI1_NCS2, + OMAP24XX_SLEEP_SAVE_CONTROL_PADCONF_MCBSP1_DX, + OMAP24XX_SLEEP_SAVE_CONTROL_PADCONF_SSI1_FLAG_TX, + OMAP24XX_SLEEP_SAVE_CONTROL_PADCONF_SYS_NIRQW0, + OMAP24XX_SLEEP_SAVE_SIZE +}; + #endif /* ASSEMBLER */ #endif /* __ASM_ARCH_OMAP_PM_H */ diff --git a/include/asm-arm/arch-omap/prcm.h b/include/asm-arm/arch-omap/prcm.h index 7b48a5cbb15..7bcaf94bde9 100644 --- a/include/asm-arm/arch-omap/prcm.h +++ b/include/asm-arm/arch-omap/prcm.h @@ -1,5 +1,7 @@ /* - * prcm.h - Access definations for use in OMAP24XX clock and power management + * linux/include/asm-arm/arch-omap/prcm.h + * + * Access definations for use in OMAP24XX clock and power management * * Copyright (C) 2005 Texas Instruments, Inc. * @@ -21,405 +23,7 @@ #ifndef __ASM_ARM_ARCH_DPM_PRCM_H #define __ASM_ARM_ARCH_DPM_PRCM_H -/* SET_PERFORMANCE_LEVEL PARAMETERS */ -#define PRCM_HALF_SPEED 1 -#define PRCM_FULL_SPEED 2 - -#ifndef __ASSEMBLER__ - -#define PRCM_REG32(offset) __REG32(OMAP24XX_PRCM_BASE + (offset)) - -#define PRCM_REVISION PRCM_REG32(0x000) -#define PRCM_SYSCONFIG PRCM_REG32(0x010) -#define PRCM_IRQSTATUS_MPU PRCM_REG32(0x018) -#define PRCM_IRQENABLE_MPU PRCM_REG32(0x01C) -#define PRCM_VOLTCTRL PRCM_REG32(0x050) -#define PRCM_VOLTST PRCM_REG32(0x054) -#define PRCM_CLKSRC_CTRL PRCM_REG32(0x060) -#define PRCM_CLKOUT_CTRL PRCM_REG32(0x070) -#define PRCM_CLKEMUL_CTRL PRCM_REG32(0x078) -#define PRCM_CLKCFG_CTRL PRCM_REG32(0x080) -#define PRCM_CLKCFG_STATUS PRCM_REG32(0x084) -#define PRCM_VOLTSETUP PRCM_REG32(0x090) -#define PRCM_CLKSSETUP PRCM_REG32(0x094) -#define PRCM_POLCTRL PRCM_REG32(0x098) - -/* GENERAL PURPOSE */ -#define GENERAL_PURPOSE1 PRCM_REG32(0x0B0) -#define GENERAL_PURPOSE2 PRCM_REG32(0x0B4) -#define GENERAL_PURPOSE3 PRCM_REG32(0x0B8) -#define GENERAL_PURPOSE4 PRCM_REG32(0x0BC) -#define GENERAL_PURPOSE5 PRCM_REG32(0x0C0) -#define GENERAL_PURPOSE6 PRCM_REG32(0x0C4) -#define GENERAL_PURPOSE7 PRCM_REG32(0x0C8) -#define GENERAL_PURPOSE8 PRCM_REG32(0x0CC) -#define GENERAL_PURPOSE9 PRCM_REG32(0x0D0) -#define GENERAL_PURPOSE10 PRCM_REG32(0x0D4) -#define GENERAL_PURPOSE11 PRCM_REG32(0x0D8) -#define GENERAL_PURPOSE12 PRCM_REG32(0x0DC) -#define GENERAL_PURPOSE13 PRCM_REG32(0x0E0) -#define GENERAL_PURPOSE14 PRCM_REG32(0x0E4) -#define GENERAL_PURPOSE15 PRCM_REG32(0x0E8) -#define GENERAL_PURPOSE16 PRCM_REG32(0x0EC) -#define GENERAL_PURPOSE17 PRCM_REG32(0x0F0) -#define GENERAL_PURPOSE18 PRCM_REG32(0x0F4) -#define GENERAL_PURPOSE19 PRCM_REG32(0x0F8) -#define GENERAL_PURPOSE20 PRCM_REG32(0x0FC) - -/* MPU */ -#define CM_CLKSEL_MPU PRCM_REG32(0x140) -#define CM_CLKSTCTRL_MPU PRCM_REG32(0x148) -#define RM_RSTST_MPU PRCM_REG32(0x158) -#define PM_WKDEP_MPU PRCM_REG32(0x1C8) -#define PM_EVGENCTRL_MPU PRCM_REG32(0x1D4) -#define PM_EVEGENONTIM_MPU PRCM_REG32(0x1D8) -#define PM_EVEGENOFFTIM_MPU PRCM_REG32(0x1DC) -#define PM_PWSTCTRL_MPU PRCM_REG32(0x1E0) -#define PM_PWSTST_MPU PRCM_REG32(0x1E4) - -/* CORE */ -#define CM_FCLKEN1_CORE PRCM_REG32(0x200) -#define CM_FCLKEN2_CORE PRCM_REG32(0x204) -#define CM_FCLKEN3_CORE PRCM_REG32(0x208) -#define CM_ICLKEN1_CORE PRCM_REG32(0x210) -#define CM_ICLKEN2_CORE PRCM_REG32(0x214) -#define CM_ICLKEN3_CORE PRCM_REG32(0x218) -#define CM_ICLKEN4_CORE PRCM_REG32(0x21C) -#define CM_IDLEST1_CORE PRCM_REG32(0x220) -#define CM_IDLEST2_CORE PRCM_REG32(0x224) -#define CM_IDLEST3_CORE PRCM_REG32(0x228) -#define CM_IDLEST4_CORE PRCM_REG32(0x22C) -#define CM_AUTOIDLE1_CORE PRCM_REG32(0x230) -#define CM_AUTOIDLE2_CORE PRCM_REG32(0x234) -#define CM_AUTOIDLE3_CORE PRCM_REG32(0x238) -#define CM_AUTOIDLE4_CORE PRCM_REG32(0x23C) -#define CM_CLKSEL1_CORE PRCM_REG32(0x240) -#define CM_CLKSEL2_CORE PRCM_REG32(0x244) -#define CM_CLKSTCTRL_CORE PRCM_REG32(0x248) -#define PM_WKEN1_CORE PRCM_REG32(0x2A0) -#define PM_WKEN2_CORE PRCM_REG32(0x2A4) -#define PM_WKST1_CORE PRCM_REG32(0x2B0) -#define PM_WKST2_CORE PRCM_REG32(0x2B4) -#define PM_WKDEP_CORE PRCM_REG32(0x2C8) -#define PM_PWSTCTRL_CORE PRCM_REG32(0x2E0) -#define PM_PWSTST_CORE PRCM_REG32(0x2E4) - -/* GFX */ -#define CM_FCLKEN_GFX PRCM_REG32(0x300) -#define CM_ICLKEN_GFX PRCM_REG32(0x310) -#define CM_IDLEST_GFX PRCM_REG32(0x320) -#define CM_CLKSEL_GFX PRCM_REG32(0x340) -#define CM_CLKSTCTRL_GFX PRCM_REG32(0x348) -#define RM_RSTCTRL_GFX PRCM_REG32(0x350) -#define RM_RSTST_GFX PRCM_REG32(0x358) -#define PM_WKDEP_GFX PRCM_REG32(0x3C8) -#define PM_PWSTCTRL_GFX PRCM_REG32(0x3E0) -#define PM_PWSTST_GFX PRCM_REG32(0x3E4) - -/* WAKE-UP */ -#define CM_FCLKEN_WKUP PRCM_REG32(0x400) -#define CM_ICLKEN_WKUP PRCM_REG32(0x410) -#define CM_IDLEST_WKUP PRCM_REG32(0x420) -#define CM_AUTOIDLE_WKUP PRCM_REG32(0x430) -#define CM_CLKSEL_WKUP PRCM_REG32(0x440) -#define RM_RSTCTRL_WKUP PRCM_REG32(0x450) -#define RM_RSTTIME_WKUP PRCM_REG32(0x454) -#define RM_RSTST_WKUP PRCM_REG32(0x458) -#define PM_WKEN_WKUP PRCM_REG32(0x4A0) -#define PM_WKST_WKUP PRCM_REG32(0x4B0) - -/* CLOCKS */ -#define CM_CLKEN_PLL PRCM_REG32(0x500) -#define CM_IDLEST_CKGEN PRCM_REG32(0x520) -#define CM_AUTOIDLE_PLL PRCM_REG32(0x530) -#define CM_CLKSEL1_PLL PRCM_REG32(0x540) -#define CM_CLKSEL2_PLL PRCM_REG32(0x544) - -/* DSP */ -#define CM_FCLKEN_DSP PRCM_REG32(0x800) -#define CM_ICLKEN_DSP PRCM_REG32(0x810) -#define CM_IDLEST_DSP PRCM_REG32(0x820) -#define CM_AUTOIDLE_DSP PRCM_REG32(0x830) -#define CM_CLKSEL_DSP PRCM_REG32(0x840) -#define CM_CLKSTCTRL_DSP PRCM_REG32(0x848) -#define RM_RSTCTRL_DSP PRCM_REG32(0x850) -#define RM_RSTST_DSP PRCM_REG32(0x858) -#define PM_WKEN_DSP PRCM_REG32(0x8A0) -#define PM_WKDEP_DSP PRCM_REG32(0x8C8) -#define PM_PWSTCTRL_DSP PRCM_REG32(0x8E0) -#define PM_PWSTST_DSP PRCM_REG32(0x8E4) -#define PRCM_IRQSTATUS_DSP PRCM_REG32(0x8F0) -#define PRCM_IRQENABLE_DSP PRCM_REG32(0x8F4) - -/* IVA */ -#define PRCM_IRQSTATUS_IVA PRCM_REG32(0x8F8) -#define PRCM_IRQENABLE_IVA PRCM_REG32(0x8FC) - -/* Modem on 2430 */ -#define CM_FCLKEN_MDM PRCM_REG32(0xC00) -#define CM_ICLKEN_MDM PRCM_REG32(0xC10) -#define CM_IDLEST_MDM PRCM_REG32(0xC20) -#define CM_CLKSEL_MDM PRCM_REG32(0xC40) - -/* FIXME: Move to header for 2430 */ -#define DISP_BASE (OMAP24XX_L4_IO_BASE+0x50000) -#define DISP_REG32(offset) __REG32(DISP_BASE + (offset)) - -#define OMAP24XX_GPMC_BASE (L3_24XX_BASE + 0xa000) -#define GPMC_BASE (OMAP24XX_GPMC_BASE) -#define GPMC_REG32(offset) __REG32(GPMC_BASE + (offset)) - -#define GPT1_BASE (OMAP24XX_GPT1) -#define GPT1_REG32(offset) __REG32(GPT1_BASE + (offset)) - -/* Misc sysconfig */ -#define DISPC_SYSCONFIG DISP_REG32(0x410) -#define SPI_BASE (OMAP24XX_L4_IO_BASE+0x98000) -#define MCSPI1_SYSCONFIG __REG32(SPI_BASE + 0x10) -#define MCSPI2_SYSCONFIG __REG32(SPI_BASE+0x2000 + 0x10) - -//#define DSP_MMU_SYSCONFIG 0x5A000010 -#define CAMERA_MMU_SYSCONFIG __REG32(DISP_BASE+0x2C10) -//#define IVA_MMU_SYSCONFIG 0x5D000010 -//#define DSP_DMA_SYSCONFIG 0x00FCC02C -#define CAMERA_DMA_SYSCONFIG __REG32(DISP_BASE+0x282C) -#define SYSTEM_DMA_SYSCONFIG __REG32(DISP_BASE+0x602C) -#define GPMC_SYSCONFIG GPMC_REG32(0x010) -#define MAILBOXES_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE+0x94010) -#define UART1_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE+0x6A054) -#define UART2_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE+0x6C054) -#define UART3_SYSCONFIG __REG32(OMAP24XX_L4_IO_BASE+0x6E054) -//#define IVA_SYSCONFIG 0x5C060010 -#define SDRC_SYSCONFIG __REG32(OMAP24XX_SDRC_BASE+0x10) -#define SMS_SYSCONFIG __REG32(OMAP24XX_SMS_BASE+0x10) -#define SSI_SYSCONFIG __REG32(DISP_BASE+0x8010) -//#define VLYNQ_SYSCONFIG 0x67FFFE10 - -/* rkw - good cannidates for PM_ to start what nm was trying */ -#define OMAP24XX_GPT2 (OMAP24XX_L4_IO_BASE+0x2A000) -#define OMAP24XX_GPT3 (OMAP24XX_L4_IO_BASE+0x78000) -#define OMAP24XX_GPT4 (OMAP24XX_L4_IO_BASE+0x7A000) -#define OMAP24XX_GPT5 (OMAP24XX_L4_IO_BASE+0x7C000) -#define OMAP24XX_GPT6 (OMAP24XX_L4_IO_BASE+0x7E000) -#define OMAP24XX_GPT7 (OMAP24XX_L4_IO_BASE+0x80000) -#define OMAP24XX_GPT8 (OMAP24XX_L4_IO_BASE+0x82000) -#define OMAP24XX_GPT9 (OMAP24XX_L4_IO_BASE+0x84000) -#define OMAP24XX_GPT10 (OMAP24XX_L4_IO_BASE+0x86000) -#define OMAP24XX_GPT11 (OMAP24XX_L4_IO_BASE+0x88000) -#define OMAP24XX_GPT12 (OMAP24XX_L4_IO_BASE+0x8A000) - -#define GPTIMER1_SYSCONFIG GPT1_REG32(0x010) -#define GPTIMER2_SYSCONFIG __REG32(OMAP24XX_GPT2 + 0x10) -#define GPTIMER3_SYSCONFIG __REG32(OMAP24XX_GPT3 + 0x10) -#define GPTIMER4_SYSCONFIG __REG32(OMAP24XX_GPT4 + 0x10) -#define GPTIMER5_SYSCONFIG __REG32(OMAP24XX_GPT5 + 0x10) -#define GPTIMER6_SYSCONFIG __REG32(OMAP24XX_GPT6 + 0x10) -#define GPTIMER7_SYSCONFIG __REG32(OMAP24XX_GPT7 + 0x10) -#define GPTIMER8_SYSCONFIG __REG32(OMAP24XX_GPT8 + 0x10) -#define GPTIMER9_SYSCONFIG __REG32(OMAP24XX_GPT9 + 0x10) -#define GPTIMER10_SYSCONFIG __REG32(OMAP24XX_GPT10 + 0x10) -#define GPTIMER11_SYSCONFIG __REG32(OMAP24XX_GPT11 + 0x10) -#define GPTIMER12_SYSCONFIG __REG32(OMAP24XX_GPT12 + 0x10) - -#define GPIOX_BASE(X) (OMAP24XX_GPIO_BASE+(0x2000*((X)-1))) - -#define GPIO1_SYSCONFIG __REG32((GPIOX_BASE(1)+0x10)) -#define GPIO2_SYSCONFIG __REG32((GPIOX_BASE(2)+0x10)) -#define GPIO3_SYSCONFIG __REG32((GPIOX_BASE(3)+0x10)) -#define GPIO4_SYSCONFIG __REG32((GPIOX_BASE(4)+0x10)) - -/* GP TIMER 1 */ -#define GPTIMER1_TISTAT GPT1_REG32(0x014) -#define GPTIMER1_TISR GPT1_REG32(0x018) -#define GPTIMER1_TIER GPT1_REG32(0x01C) -#define GPTIMER1_TWER GPT1_REG32(0x020) -#define GPTIMER1_TCLR GPT1_REG32(0x024) -#define GPTIMER1_TCRR GPT1_REG32(0x028) -#define GPTIMER1_TLDR GPT1_REG32(0x02C) -#define GPTIMER1_TTGR GPT1_REG32(0x030) -#define GPTIMER1_TWPS GPT1_REG32(0x034) -#define GPTIMER1_TMAR GPT1_REG32(0x038) -#define GPTIMER1_TCAR1 GPT1_REG32(0x03C) -#define GPTIMER1_TSICR GPT1_REG32(0x040) -#define GPTIMER1_TCAR2 GPT1_REG32(0x044) - -/* rkw -- base fix up please... */ -#define GPTIMER3_TISR __REG32(OMAP24XX_L4_IO_BASE+0x78018) - -/* SDRC */ -#define SDRC_DLLA_CTRL __REG32(OMAP24XX_SDRC_BASE+0x060) -#define SDRC_DLLA_STATUS __REG32(OMAP24XX_SDRC_BASE+0x064) -#define SDRC_DLLB_CTRL __REG32(OMAP24XX_SDRC_BASE+0x068) -#define SDRC_DLLB_STATUS __REG32(OMAP24XX_SDRC_BASE+0x06C) -#define SDRC_POWER __REG32(OMAP24XX_SDRC_BASE+0x070) -#define SDRC_MR_0 __REG32(OMAP24XX_SDRC_BASE+0x084) - -/* GPIO 1 */ -#define GPIO1_BASE GPIOX_BASE(1) -#define GPIO1_REG32(offset) __REG32(GPIO1_BASE + (offset)) -#define GPIO1_IRQENABLE1 GPIO1_REG32(0x01C) -#define GPIO1_IRQSTATUS1 GPIO1_REG32(0x018) -#define GPIO1_IRQENABLE2 GPIO1_REG32(0x02C) -#define GPIO1_IRQSTATUS2 GPIO1_REG32(0x028) -#define GPIO1_WAKEUPENABLE GPIO1_REG32(0x020) -#define GPIO1_RISINGDETECT GPIO1_REG32(0x048) -#define GPIO1_DATAIN GPIO1_REG32(0x038) -#define GPIO1_OE GPIO1_REG32(0x034) -#define GPIO1_DATAOUT GPIO1_REG32(0x03C) - -/* GPIO2 */ -#define GPIO2_BASE GPIOX_BASE(2) -#define GPIO2_REG32(offset) __REG32(GPIO2_BASE + (offset)) -#define GPIO2_IRQENABLE1 GPIO2_REG32(0x01C) -#define GPIO2_IRQSTATUS1 GPIO2_REG32(0x018) -#define GPIO2_IRQENABLE2 GPIO2_REG32(0x02C) -#define GPIO2_IRQSTATUS2 GPIO2_REG32(0x028) -#define GPIO2_WAKEUPENABLE GPIO2_REG32(0x020) -#define GPIO2_RISINGDETECT GPIO2_REG32(0x048) -#define GPIO2_DATAIN GPIO2_REG32(0x038) -#define GPIO2_OE GPIO2_REG32(0x034) -#define GPIO2_DATAOUT GPIO2_REG32(0x03C) - -/* GPIO 3 */ -#define GPIO3_BASE GPIOX_BASE(3) -#define GPIO3_REG32(offset) __REG32(GPIO3_BASE + (offset)) -#define GPIO3_IRQENABLE1 GPIO3_REG32(0x01C) -#define GPIO3_IRQSTATUS1 GPIO3_REG32(0x018) -#define GPIO3_IRQENABLE2 GPIO3_REG32(0x02C) -#define GPIO3_IRQSTATUS2 GPIO3_REG32(0x028) -#define GPIO3_WAKEUPENABLE GPIO3_REG32(0x020) -#define GPIO3_RISINGDETECT GPIO3_REG32(0x048) -#define GPIO3_FALLINGDETECT GPIO3_REG32(0x04C) -#define GPIO3_DATAIN GPIO3_REG32(0x038) -#define GPIO3_OE GPIO3_REG32(0x034) -#define GPIO3_DATAOUT GPIO3_REG32(0x03C) -#define GPIO3_DEBOUNCENABLE GPIO3_REG32(0x050) -#define GPIO3_DEBOUNCINGTIME GPIO3_REG32(0x054) - -/* GPIO 4 */ -#define GPIO4_BASE GPIOX_BASE(4) -#define GPIO4_REG32(offset) __REG32(GPIO4_BASE + (offset)) -#define GPIO4_IRQENABLE1 GPIO4_REG32(0x01C) -#define GPIO4_IRQSTATUS1 GPIO4_REG32(0x018) -#define GPIO4_IRQENABLE2 GPIO4_REG32(0x02C) -#define GPIO4_IRQSTATUS2 GPIO4_REG32(0x028) -#define GPIO4_WAKEUPENABLE GPIO4_REG32(0x020) -#define GPIO4_RISINGDETECT GPIO4_REG32(0x048) -#define GPIO4_FALLINGDETECT GPIO4_REG32(0x04C) -#define GPIO4_DATAIN GPIO4_REG32(0x038) -#define GPIO4_OE GPIO4_REG32(0x034) -#define GPIO4_DATAOUT GPIO4_REG32(0x03C) -#define GPIO4_DEBOUNCENABLE GPIO4_REG32(0x050) -#define GPIO4_DEBOUNCINGTIME GPIO4_REG32(0x054) - - -/* IO CONFIG */ -#define CONTROL_BASE (OMAP24XX_CTRL_BASE) -#define CONTROL_REG32(offset) __REG32(CONTROL_BASE + (offset)) - -#define CONTROL_PADCONF_SPI1_NCS2 CONTROL_REG32(0x104) -#define CONTROL_PADCONF_SYS_XTALOUT CONTROL_REG32(0x134) -#define CONTROL_PADCONF_UART1_RX CONTROL_REG32(0x0C8) -#define CONTROL_PADCONF_MCBSP1_DX CONTROL_REG32(0x10C) -#define CONTROL_PADCONF_GPMC_NCS4 CONTROL_REG32(0x090) -#define CONTROL_PADCONF_DSS_D5 CONTROL_REG32(0x0B8) -#define CONTROL_PADCONF_DSS_D9 CONTROL_REG32(0x0BC) -#define CONTROL_PADCONF_DSS_D13 CONTROL_REG32(0x0C0) -#define CONTROL_PADCONF_DSS_VSYNC CONTROL_REG32(0x0CC) - -/* CONTROL */ -#define CONTROL_DEVCONF CONTROL_REG32(0x274) - -/* INTERRUPT CONTROLLER */ -#define INTC_BASE (OMAP24XX_L4_IO_BASE+0xfe000) -#define INTC_REG32(offset) __REG32(INTC_BASE + (offset)) - -#define INTC1_U_BASE INTC_REG32(0x000) -#define INTC_MIR0 INTC_REG32(0x084) -#define INTC_MIR_SET0 INTC_REG32(0x08C) -#define INTC_MIR_CLEAR0 INTC_REG32(0x088) -#define INTC_ISR_CLEAR0 INTC_REG32(0x094) -#define INTC_MIR1 INTC_REG32(0x0A4) -#define INTC_MIR_SET1 INTC_REG32(0x0AC) -#define INTC_MIR_CLEAR1 INTC_REG32(0x0A8) -#define INTC_ISR_CLEAR1 INTC_REG32(0x0B4) -#define INTC_MIR2 INTC_REG32(0x0C4) -#define INTC_MIR_SET2 INTC_REG32(0x0CC) -#define INTC_MIR_CLEAR2 INTC_REG32(0x0C8) -#define INTC_ISR_CLEAR2 INTC_REG32(0x0D4) -#define INTC_SIR_IRQ INTC_REG32(0x040) -#define INTC_CONTROL INTC_REG32(0x048) -#define INTC_ILR11 INTC_REG32(0x12C) -#define INTC_ILR32 INTC_REG32(0x180) -#define INTC_ILR37 INTC_REG32(0x194) -#define INTC_SYSCONFIG INTC_REG32(0x010) - -/* RAM FIREWALL */ -#define RAMFW_BASE (0x68005000) -#define RAMFW_REG32(offset) __REG32(RAMFW_BASE + (offset)) - -#define RAMFW_REQINFOPERM0 RAMFW_REG32(0x048) -#define RAMFW_READPERM0 RAMFW_REG32(0x050) -#define RAMFW_WRITEPERM0 RAMFW_REG32(0x058) - -/* GPMC CS1 FPGA ON USER INTERFACE MODULE */ -//#define DEBUG_BOARD_LED_REGISTER 0x04000014 - -/* GPMC CS0 */ -#define GPMC_CONFIG1_0 GPMC_REG32(0x060) -#define GPMC_CONFIG2_0 GPMC_REG32(0x064) -#define GPMC_CONFIG3_0 GPMC_REG32(0x068) -#define GPMC_CONFIG4_0 GPMC_REG32(0x06C) -#define GPMC_CONFIG5_0 GPMC_REG32(0x070) -#define GPMC_CONFIG6_0 GPMC_REG32(0x074) -#define GPMC_CONFIG7_0 GPMC_REG32(0x078) - -/* GPMC CS1 */ -#define GPMC_CONFIG1_1 GPMC_REG32(0x090) -#define GPMC_CONFIG2_1 GPMC_REG32(0x094) -#define GPMC_CONFIG3_1 GPMC_REG32(0x098) -#define GPMC_CONFIG4_1 GPMC_REG32(0x09C) -#define GPMC_CONFIG5_1 GPMC_REG32(0x0a0) -#define GPMC_CONFIG6_1 GPMC_REG32(0x0a4) -#define GPMC_CONFIG7_1 GPMC_REG32(0x0a8) - -/* DSS */ -#define DSS_CONTROL DISP_REG32(0x040) -#define DISPC_CONTROL DISP_REG32(0x440) -#define DISPC_SYSSTATUS DISP_REG32(0x414) -#define DISPC_IRQSTATUS DISP_REG32(0x418) -#define DISPC_IRQENABLE DISP_REG32(0x41C) -#define DISPC_CONFIG DISP_REG32(0x444) -#define DISPC_DEFAULT_COLOR0 DISP_REG32(0x44C) -#define DISPC_DEFAULT_COLOR1 DISP_REG32(0x450) -#define DISPC_TRANS_COLOR0 DISP_REG32(0x454) -#define DISPC_TRANS_COLOR1 DISP_REG32(0x458) -#define DISPC_LINE_NUMBER DISP_REG32(0x460) -#define DISPC_TIMING_H DISP_REG32(0x464) -#define DISPC_TIMING_V DISP_REG32(0x468) -#define DISPC_POL_FREQ DISP_REG32(0x46C) -#define DISPC_DIVISOR DISP_REG32(0x470) -#define DISPC_SIZE_DIG DISP_REG32(0x478) -#define DISPC_SIZE_LCD DISP_REG32(0x47C) -#define DISPC_GFX_BA0 DISP_REG32(0x480) -#define DISPC_GFX_BA1 DISP_REG32(0x484) -#define DISPC_GFX_POSITION DISP_REG32(0x488) -#define DISPC_GFX_SIZE DISP_REG32(0x48C) -#define DISPC_GFX_ATTRIBUTES DISP_REG32(0x4A0) -#define DISPC_GFX_FIFO_THRESHOLD DISP_REG32(0x4A4) -#define DISPC_GFX_ROW_INC DISP_REG32(0x4AC) -#define DISPC_GFX_PIXEL_INC DISP_REG32(0x4B0) -#define DISPC_GFX_WINDOW_SKIP DISP_REG32(0x4B4) -#define DISPC_GFX_TABLE_BA DISP_REG32(0x4B8) -#define DISPC_DATA_CYCLE1 DISP_REG32(0x5D4) -#define DISPC_DATA_CYCLE2 DISP_REG32(0x5D8) -#define DISPC_DATA_CYCLE3 DISP_REG32(0x5DC) - -/* Wake up define for board */ -#define GPIO97 (1 << 1) -#define GPIO88 (1 << 24) - -#endif /* __ASSEMBLER__ */ +u32 omap_prcm_get_reset_sources(void); #endif diff --git a/include/asm-arm/arch-omap/sram.h b/include/asm-arm/arch-omap/sram.h index e72ccbf0fe0..6fc0dd57b7c 100644 --- a/include/asm-arm/arch-omap/sram.h +++ b/include/asm-arm/arch-omap/sram.h @@ -20,6 +20,8 @@ extern void omap2_sram_reprogram_sdrc(u32 perf_level, u32 dll_val, u32 mem_type); extern u32 omap2_set_prcm(u32 dpll_ctrl_val, u32 sdrc_rfr_val, int bypass); +extern unsigned long omap_fb_sram_start; +extern unsigned long omap_fb_sram_size; /* Do not use these */ extern void sram_reprogram_clock(u32 ckctl, u32 dpllctl); diff --git a/include/asm-arm/arch-omap/sti.h b/include/asm-arm/arch-omap/sti.h new file mode 100644 index 00000000000..e5a383d813b --- /dev/null +++ b/include/asm-arm/arch-omap/sti.h @@ -0,0 +1,161 @@ +#ifndef __ASM_ARCH_OMAP_STI_H +#define __ASM_ARCH_OMAP_STI_H + +#include + +/* + * STI/XTI + */ +#define STI_REVISION 0x00 +#define STI_SYSCONFIG 0x10 +#define STI_SYSSTATUS 0x14 +#define STI_IRQSTATUS 0x18 +#define STI_IRQSETEN 0x1c + +#if defined(CONFIG_ARCH_OMAP1) +#define STI_IRQCLREN 0x20 +#define STI_ER 0x24 +#define STI_DR 0x28 +#define STI_RX_DR 0x2c +#define STI_RX_STATUS 0x30 +#define STI_CLK_CTRL 0x34 +#define STI_IOBOTT0 0x4c +#define STI_IOTOP0 0x50 +#define STI_IOBOTT1 0x54 +#define STI_IOTOP1 0x58 +#define STI_SERIAL_CFG 0x60 + +#define STI_OCPT2_MATCH_INT 0 +#define STI_OCPT1_MATCH_INT 1 +#define STI_EMIFS_MATCH_INT 2 +#define STI_EMIFF_MATCH_INT 3 +#define STI_IO_MATCH_INT 4 +#define STI_RX_INT 5 +#define STI_DUMP_REQUEST_INT 6 +#define STI_DUMP_UNDERRUN_INT 7 +#define STI_WAKEUP_INT 9 + +#define STI_NR_IRQS 10 + +#define STI_IRQSTATUS_MASK 0x2ff +#define STI_PERCHANNEL_SIZE 4 + +#define STI_RXFIFO_EMPTY (1 << 0) + +/* + * We use the following enums to retain consistency with the STI "functional" + * specification. + */ + +/* STI_ER */ +enum { + UnlockStatMatch = (1 << 2), /* Unlock status match event regs */ + IOMPUStr1En1 = (1 << 3), /* MPU IO match, strobe 1, window 1 */ + IOMPUStr0En1 = (1 << 4), /* MPU IO match, strobe 0, window 1 */ + IOMPUStr1En0 = (1 << 5), /* MPU IO match, strobe 1, window 0 */ + IOMPUStr0En0 = (1 << 6), /* MPU IO match, strobe 0, window 0 */ + IODSPStr1En1 = (1 << 7), /* DSP IO match, strobe 1, window 1 */ + IODSPStr0En1 = (1 << 8), /* DSP IO match, strobe 0, window 1 */ + IODSPStr1En0 = (1 << 9), /* DSP IO match, strobe 1, window 0 */ + IODSPStr0En0 = (1 << 10), /* DSP IO match, strobe 0, window 0 */ + MemMatchEn = (1 << 11), /* Memory matched event */ + DSPCmdEn = (1 << 12), /* DSP command write */ + MPUCmdEn = (1 << 13), /* MPU command write */ + MemDumpEn = (1 << 14), /* System memory dump */ + STIEn = (1 << 15), /* Global trace enable */ +}; +#elif defined(CONFIG_ARCH_OMAP2) + +/* XTI interrupt bits */ +enum { + STI_WAKEUP_INT = 0, + STI_ETB_THRESHOLD_INT, + STI_RX_INT, + STI_DUMP_REQUEST_INT, + STI_NR_IRQS, +}; + +/* XTI_TRACESELECT */ +enum { + CmdTimeStampEn = (1 << 0), /* Command write timestamps */ + WinTimeStampEn = (1 << 1), /* Window match timestamps */ + WinMatchEn = (1 << 2), /* Window match trace */ + DSPCmdEn = (1 << 3), /* DSP command write */ + MPUCmdEn = (1 << 4), /* MPU command write */ + MemDumpEn0 = (1 << 5), /* System memory dump */ + MemDumpEn1 = (1 << 6), + MemDumpEn2 = (1 << 7), + ExtTriggerEn = (1 << 8), /* External trace trigger */ + STIEn = (1 << 9), /* System trace enable */ +}; + +#define STI_IRQSTATUS_MASK 0x0f +#define STI_PERCHANNEL_SIZE 64 + +/* XTI registers */ +#define XTI_SYSSTATUS 0x14 +#define XTI_TRACESELECT 0x24 +#define XTI_RXDATA 0x28 +#define XTI_SCLKCRTL 0x2c +#define XTI_SCONFIG 0x30 + +/* STI Compatability */ +#define STI_RX_STATUS XTI_SYSSTATUS +#define STI_IRQCLREN STI_IRQSETEN +#define STI_ER XTI_TRACESELECT +#define STI_DR XTI_TRACESELECT +#define STI_RX_DR XTI_RXDATA +#define STI_CLK_CTRL XTI_SCLKCRTL +#define STI_SERIAL_CFG XTI_SCONFIG + +#define STI_RXFIFO_EMPTY (1 << 8) + +#endif + +/* arch/arm/plat-omap/sti/sti.c */ +extern unsigned long sti_base, sti_channel_base; + +int sti_request_irq(unsigned int irq, void *handler, unsigned long arg); +void sti_free_irq(unsigned int irq); +void sti_enable_irq(unsigned int irq); +void sti_disable_irq(unsigned int irq); +void sti_ack_irq(unsigned int irq); + +int sti_trace_enable(int event); +void sti_trace_disable(int event); + +void sti_channel_write_trace(int len, int id, void *data, unsigned int channel); + +/* arch/arm/plat-omap/sti/sti-fifo.c */ +int sti_read_packet(unsigned char *buf, int maxsize); + +static inline unsigned long sti_readl(unsigned long reg) +{ + return __raw_readl(sti_base + reg); +} + +static inline void sti_writel(unsigned long data, unsigned long reg) +{ + __raw_writel(data, sti_base + reg); +} + +#define to_channel_address(channel) \ + (sti_channel_base + STI_PERCHANNEL_SIZE * (channel)) + +static inline void sti_channel_writeb(unsigned char data, unsigned int channel) +{ + __raw_writeb(data, to_channel_address(channel)); +} + +static inline void sti_channel_writel(unsigned long data, unsigned int channel) +{ + __raw_writel(data, to_channel_address(channel)); +} + +#define STI_TRACE_CONTROL_CHANNEL 253 + +static inline void sti_channel_flush(unsigned int channel) +{ + sti_channel_writeb(channel, STI_TRACE_CONTROL_CHANNEL); +} +#endif /* __ASM_ARCH_OMAP_STI_H */ diff --git a/include/asm-arm/arch-omap/system.h b/include/asm-arm/arch-omap/system.h index 6724a81bd10..67970d1a202 100644 --- a/include/asm-arm/arch-omap/system.h +++ b/include/asm-arm/arch-omap/system.h @@ -9,12 +9,13 @@ #include #include -#include #ifndef CONFIG_MACH_VOICEBLUE #define voiceblue_reset() do {} while (0) #endif +extern void omap_prcm_arch_reset(char mode); + static inline void arch_idle(void) { cpu_do_idle(); @@ -38,24 +39,12 @@ static inline void omap1_arch_reset(char mode) omap_writew(1, ARM_RSTCT1); } -static inline void omap2_arch_reset(char mode) -{ - u32 rate; - struct clk *vclk, *sclk; - - vclk = clk_get(NULL, "virt_prcm_set"); - sclk = clk_get(NULL, "sys_ck"); - rate = clk_get_rate(sclk); - clk_set_rate(vclk, rate); /* go to bypass for OMAP limitation */ - RM_RSTCTRL_WKUP |= 2; -} - static inline void arch_reset(char mode) { if (!cpu_is_omap24xx()) omap1_arch_reset(mode); else - omap2_arch_reset(mode); + omap_prcm_arch_reset(mode); } #endif diff --git a/include/asm-arm/dma-mapping.h b/include/asm-arm/dma-mapping.h index e3e8541ee63..18ca0c5dd8a 100644 --- a/include/asm-arm/dma-mapping.h +++ b/include/asm-arm/dma-mapping.h @@ -5,6 +5,7 @@ #include #include /* need struct page */ +#include #include diff --git a/include/asm-arm/hardware/tsc2101.h b/include/asm-arm/hardware/tsc2101.h new file mode 100644 index 00000000000..449045841f6 --- /dev/null +++ b/include/asm-arm/hardware/tsc2101.h @@ -0,0 +1,300 @@ +/* + * + * TI TSC2101 Audio CODEC and TS control registers definition + * + * + * Copyright 2003 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * source@mvista.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; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ASM_HARDWARE_TSC2101_H +#define __ASM_HARDWARE_TSC2101_H + +/* Page 0 Touch Screen Data Registers */ +#define TSC2101_TS_X (0x00) +#define TSC2101_TS_Y (0x01) +#define TSC2101_TS_Z1 (0x02) +#define TSC2101_TS_Z2 (0x03) +#define TSC2101_TS_BAT (0x05) +#define TSC2101_TS_AUX1 (0x07) +#define TSC2101_TS_AUX2 (0x08) +#define TSC2101_TS_TEMP1 (0x09) +#define TSC2101_TS_TEMP2 (0x0A) + +/* Page 1 Touch Screen Control registers */ +#define TSC2101_TS_ADC_CTRL (0x00) +#define TSC2101_TS_STATUS (0x01) +#define TSC2101_TS_BUFFER_CTRL (0x02) +#define TSC2101_TS_REF_CTRL (0x03) +#define TSC2101_TS_RESET_CTRL (0x04) +#define TSC2101_TS_CONFIG_CTRL (0x05) +#define TSC2101_TS_TEMP_MAX_THRESHOLD (0x06) +#define TSC2101_TS_TEMP_MIN_THRESHOLD (0x07) +#define TSC2101_TS_AUX1_MAX_THRESHOLD (0x08) +#define TSC2101_TS_AUX1_MIN_THRESHOLD (0x09) +#define TSC2101_TS_AUX2_MAX_THRESHOLD (0x0A) +#define TSC2101_TS_AUX2_MIN_THRESHOLD (0x0B) +#define TSC2101_TS_MEASURE_CONFIG (0x0C) +#define TSC2101_TS_PROG_DELAY (0x0D) + +/* Page 2 Audio codec Control registers */ +#define TSC2101_AUDIO_CTRL_1 (0x00) +#define TSC2101_HEADSET_GAIN_CTRL (0x01) +#define TSC2101_DAC_GAIN_CTRL (0x02) +#define TSC2101_MIXER_PGA_CTRL (0x03) +#define TSC2101_AUDIO_CTRL_2 (0x04) +#define TSC2101_CODEC_POWER_CTRL (0x05) +#define TSC2101_AUDIO_CTRL_3 (0x06) +#define TSC2101_LCH_BASS_BOOST_N0 (0x07) +#define TSC2101_LCH_BASS_BOOST_N1 (0x08) +#define TSC2101_LCH_BASS_BOOST_N2 (0x09) +#define TSC2101_LCH_BASS_BOOST_N3 (0x0A) +#define TSC2101_LCH_BASS_BOOST_N4 (0x0B) +#define TSC2101_LCH_BASS_BOOST_N5 (0x0C) +#define TSC2101_LCH_BASS_BOOST_D1 (0x0D) +#define TSC2101_LCH_BASS_BOOST_D2 (0x0E) +#define TSC2101_LCH_BASS_BOOST_D4 (0x0F) +#define TSC2101_LCH_BASS_BOOST_D5 (0x10) +#define TSC2101_RCH_BASS_BOOST_N0 (0x11) +#define TSC2101_RCH_BASS_BOOST_N1 (0x12) +#define TSC2101_RCH_BASS_BOOST_N2 (0x13) +#define TSC2101_RCH_BASS_BOOST_N3 (0x14) +#define TSC2101_RCH_BASS_BOOST_N4 (0x15) +#define TSC2101_RCH_BASS_BOOST_N5 (0x16) +#define TSC2101_RCH_BASS_BOOST_D1 (0x17) +#define TSC2101_RCH_BASS_BOOST_D2 (0x18) +#define TSC2101_RCH_BASS_BOOST_D4 (0x19) +#define TSC2101_RCH_BASS_BOOST_D5 (0x1A) +#define TSC2101_PLL_PROG_1 (0x1B) +#define TSC2101_PLL_PROG_2 (0x1C) +#define TSC2101_AUDIO_CTRL_4 (0x1D) +#define TSC2101_HANDSET_GAIN_CTRL (0x1E) +#define TSC2101_BUZZER_GAIN_CTRL (0x1F) +#define TSC2101_AUDIO_CTRL_5 (0x20) +#define TSC2101_AUDIO_CTRL_6 (0x21) +#define TSC2101_AUDIO_CTRL_7 (0x22) +#define TSC2101_GPIO_CTRL (0x23) +#define TSC2101_AGC_CTRL (0x24) +#define TSC2101_POWERDOWN_STS (0x25) +#define TSC2101_MIC_AGC_CONTROL (0x26) +#define TSC2101_CELL_AGC_CONTROL (0x27) + +/* Bit field definitions for TS Control */ +#define TSC2101_DATA_AVAILABLE 0x4000 +#define TSC2101_BUFFERMODE_DISABLE 0x0 +#define TSC2101_REF_POWERUP 0x16 +#define TSC2101_ENABLE_TOUCHDETECT 0x08 +#define TSC2101_PRG_DELAY 0x0900 +#define TSC2101_ADC_CONTROL 0x8874 +#define TSC2101_ADC_POWERDOWN 0x4000 + +/* Bit position */ +#define TSC2101_BIT(ARG) ((0x01)<<(ARG)) + +/* Field masks for Audio Control 1 */ +#define AC1_ADCHPF(ARG) (((ARG) & 0x03) << 14) +#define AC1_WLEN(ARG) (((ARG) & 0x03) << 10) +#define AC1_DATFM(ARG) (((ARG) & 0x03) << 8) +#define AC1_DACFS(ARG) (((ARG) & 0x07) << 3) +#define AC1_ADCFS(ARG) (((ARG) & 0x07)) + +/* Field masks for TSC2101_HEADSET_GAIN_CTRL */ +#define HGC_ADMUT_HED TSC2101_BIT(15) +#define HGC_ADPGA_HED(ARG) (((ARG) & 0x7F) << 8) +#define HGC_AGCTG_HED(ARG) (((ARG) & 0x07) << 5) +#define HGC_AGCTC_HED(ARG) (((ARG) & 0x0F) << 1) +#define HGC_AGCEN_HED (0x01) + +/* Field masks for TSC2101_DAC_GAIN_CTRL */ +#define DGC_DALMU TSC2101_BIT(15) +#define DGC_DALVL(ARG) (((ARG) & 0x7F) << 8) +#define DGC_DARMU TSC2101_BIT(7) +#define DGC_DARVL(ARG) (((ARG) & 0x7F)) + +/* Field masks for TSC2101_MIXER_PGA_CTRL */ +#define MPC_ASTMU TSC2101_BIT(15) +#define MPC_ASTG(ARG) (((ARG) & 0x7F) << 8) +#define MPC_MICSEL(ARG) (((ARG) & 0x07) << 5) +#define MPC_MICADC TSC2101_BIT(4) +#define MPC_CPADC TSC2101_BIT(3) +#define MPC_ASTGF (0x01) + +/* Field formats for TSC2101_AUDIO_CTRL_2 */ +#define AC2_KCLEN TSC2101_BIT(15) +#define AC2_KCLAC(ARG) (((ARG) & 0x07) << 12) +#define AC2_APGASS TSC2101_BIT(11) +#define AC2_KCLFRQ(ARG) (((ARG) & 0x07) << 8) +#define AC2_KCLLN(ARG) (((ARG) & 0x0F) << 4) +#define AC2_DLGAF TSC2101_BIT(3) +#define AC2_DRGAF TSC2101_BIT(2) +#define AC2_DASTC TSC2101_BIT(1) +#define AC2_ADGAF (0x01) + +/* Field masks for TSC2101_CODEC_POWER_CTRL */ +#define CPC_MBIAS_HND TSC2101_BIT(15) +#define CPC_MBIAS_HED TSC2101_BIT(14) +#define CPC_ASTPWD TSC2101_BIT(13) +#define CPC_SP1PWDN TSC2101_BIT(12) +#define CPC_SP2PWDN TSC2101_BIT(11) +#define CPC_DAPWDN TSC2101_BIT(10) +#define CPC_ADPWDN TSC2101_BIT(9) +#define CPC_VGPWDN TSC2101_BIT(8) +#define CPC_COPWDN TSC2101_BIT(7) +#define CPC_LSPWDN TSC2101_BIT(6) +#define CPC_ADPWDF TSC2101_BIT(5) +#define CPC_LDAPWDF TSC2101_BIT(4) +#define CPC_RDAPWDF TSC2101_BIT(3) +#define CPC_ASTPWF TSC2101_BIT(2) +#define CPC_BASSBC TSC2101_BIT(1) +#define CPC_DEEMPF (0x01) + +/* Field masks for TSC2101_AUDIO_CTRL_3 */ +#define AC3_DMSVOL(ARG) (((ARG) & 0x03) << 14) +#define AC3_REFFS TSC2101_BIT(13) +#define AC3_DAXFM TSC2101_BIT(12) +#define AC3_SLVMS TSC2101_BIT(11) +#define AC3_ADCOVF TSC2101_BIT(8) +#define AC3_DALOVF TSC2101_BIT(7) +#define AC3_DAROVF TSC2101_BIT(6) +#define AC3_CLPST TSC2101_BIT(3) +#define AC3_REVID(ARG) (((ARG) & 0x07)) + +/* Field masks for TSC2101_PLL_PROG_1 */ +#define PLL1_PLLSEL TSC2101_BIT(15) +#define PLL1_QVAL(ARG) (((ARG) & 0x0F) << 11) +#define PLL1_PVAL(ARG) (((ARG) & 0x07) << 8) +#define PLL1_I_VAL(ARG) (((ARG) & 0x3F) << 2) + +/* Field masks of TSC2101_PLL_PROG_2 */ +#define PLL2_D_VAL(ARG) (((ARG) & 0x3FFF) << 2) + +/* Field masks for TSC2101_AUDIO_CTRL_4 */ +#define AC4_ADSTPD TSC2101_BIT(15) +#define AC4_DASTPD TSC2101_BIT(14) +#define AC4_ASSTPD TSC2101_BIT(13) +#define AC4_CISTPD TSC2101_BIT(12) +#define AC4_BISTPD TSC2101_BIT(11) +#define AC4_AGCHYS(ARG) (((ARG) & 0x03) << 9) +#define AC4_MB_HED(ARG) (((ARG) & 0x03) << 7) +#define AC4_MB_HND TSC2101_BIT(6) +#define AC4_SCPFL TSC2101_BIT(1) + +/* Field masks settings for TSC2101_HANDSET_GAIN_CTRL */ +#define HNGC_ADMUT_HND TSC2101_BIT(15) +#define HNGC_ADPGA_HND(ARG) (((ARG) & 0x7F) << 8) +#define HNGC_AGCTG_HND(ARG) (((ARG) & 0x07) << 5) +#define HNGC_AGCTC_HND(ARG) (((ARG) & 0x0F) << 1) +#define HNGC_AGCEN_HND (0x01) + +/* Field masks settings for TSC2101_BUZZER_GAIN_CTRL */ +#define BGC_MUT_CP TSC2101_BIT(15) +#define BGC_CPGA(ARG) (((ARG) & 0x7F) << 8) +#define BGC_CPGF TSC2101_BIT(7) +#define BGC_MUT_BU TSC2101_BIT(6) +#define BGC_BPGA(ARG) (((ARG) & 0x0F) << 2) +#define BGC_BUGF TSC2101_BIT(1) + +/* Field masks settings for TSC2101_AUDIO_CTRL_5 */ +#define AC5_DIFFIN TSC2101_BIT(15) +#define AC5_DAC2SPK1(ARG) (((ARG) & 0x03) << 13) +#define AC5_AST2SPK1 TSC2101_BIT(12) +#define AC5_BUZ2SPK1 TSC2101_BIT(11) +#define AC5_KCL2SPK1 TSC2101_BIT(10) +#define AC5_CPI2SPK1 TSC2101_BIT(9) +#define AC5_DAC2SPK2(ARG) (((ARG) & 0x03) << 7) +#define AC5_AST2SPK2 TSC2101_BIT(6) +#define AC5_BUZ2SPK2 TSC2101_BIT(5) +#define AC5_KCL2SPK2 TSC2101_BIT(4) +#define AC5_CPI2SPK2 TSC2101_BIT(3) +#define AC5_MUTSPK1 TSC2101_BIT(2) +#define AC5_MUTSPK2 TSC2101_BIT(1) +#define AC5_HDSCPTC (0x01) + +/* Field masks settings for TSC2101_AUDIO_CTRL_6 */ +#define AC6_SPL2LSK TSC2101_BIT(15) +#define AC6_AST2LSK TSC2101_BIT(14) +#define AC6_BUZ2LSK TSC2101_BIT(13) +#define AC6_KCL2LSK TSC2101_BIT(12) +#define AC6_CPI2LSK TSC2101_BIT(11) +#define AC6_MIC2CPO TSC2101_BIT(10) +#define AC6_SPL2CPO TSC2101_BIT(9) +#define AC6_SPR2CPO TSC2101_BIT(8) +#define AC6_MUTLSPK TSC2101_BIT(7) +#define AC6_MUTSPK2 TSC2101_BIT(6) +#define AC6_LDSCPTC TSC2101_BIT(5) +#define AC6_VGNDSCPTC TSC2101_BIT(4) +#define AC6_CAPINTF TSC2101_BIT(3) + +/* Field masks settings for TSC2101_AUDIO_CTRL_7 */ +#define AC7_DETECT TSC2101_BIT(15) +#define AC7_HESTYPE(ARG) (((ARG) & 0x03) << 13) +#define AC7_HDDETFL TSC2101_BIT(12) +#define AC7_BDETFL TSC2101_BIT(11) +#define AC7_HDDEBNPG(ARG) (((ARG) & 0x03) << 9) +#define AC7_BDEBNPG(ARG) (((ARG) & 0x03) << 6) +#define AC7_DGPIO2 TSC2101_BIT(4) +#define AC7_DGPIO1 TSC2101_BIT(3) +#define AC7_CLKGPIO2 TSC2101_BIT(2) +#define AC7_ADWSF(ARG) (((ARG) & 0x03)) + +/* Field masks settings for TSC2101_GPIO_CTRL */ +#define GC_GPO2EN TSC2101_BIT(15) +#define GC_GPO2SG TSC2101_BIT(14) +#define GC_GPI2EN TSC2101_BIT(13) +#define GC_GPI2SGF TSC2101_BIT(12) +#define GC_GPO1EN TSC2101_BIT(11) +#define GC_GPO1SG TSC2101_BIT(10) +#define GC_GPI1EN TSC2101_BIT(9) +#define GC_GPI1SGF TSC2101_BIT(8) + +/* Field masks for TSC2101_AGC_CTRL */ +#define AC_AGCNF_CELL TSC2101_BIT(14) +#define AC_AGCNL(ARG) (((ARG) & 0x07) << 11) +#define AC_AGCHYS_CELL(ARG) (((ARG) & 0x03) << 9) +#define AC_CLPST_CELL TSC2101_BIT(8) +#define AC_AGCTG_CELL(ARG) (((ARG) & 0x07) << 5) +#define AC_AGCTC_CELL(ARG) (((ARG) & 0x0F) << 1) +#define AC_AGCEN_CELL (0x01) + +/* Field masks for TSC2101_POWERDOWN_STS */ +#define PS_SPK1FL TSC2101_BIT(15) +#define PS_SPK2FL TSC2101_BIT(14) +#define PS_HNDFL TSC2101_BIT(13) +#define PS_VGNDFL TSC2101_BIT(12) +#define PS_LSPKFL TSC2101_BIT(11) +#define PS_CELLFL TSC2101_BIT(10) +#define PS_PSEQ TSC2101_BIT(5) +#define PS_PSTIME TSC2101_BIT(4) + +/* Field masks for Register Mic AGC Control */ +#define MAC_MMPGA(ARG) (((ARG) & 0x7F) << 9) +#define MAC_MDEBNS(ARG) (((ARG) & 0x07) << 6) +#define MAC_MDEBSN(ARG) (((ARG) & 0x07) << 3) + +/* Field masks for Register Cellphone AGC Control */ +#define CAC_CMPGA(ARG) (((ARG) & 0x7F) << 9) +#define CAC_CDEBNS(ARG) (((ARG) & 0x07) << 6) +#define CAC_CDEBSN(ARG) (((ARG) & 0x07) << 3) + +#endif /* __ASM_HARDWARE_TSC2101_H */ diff --git a/include/asm-arm/mach/flash.h b/include/asm-arm/mach/flash.h index 05b029ef637..664b708d257 100644 --- a/include/asm-arm/mach/flash.h +++ b/include/asm-arm/mach/flash.h @@ -36,4 +36,18 @@ struct flash_platform_data { unsigned int nr_parts; }; +/** + * struct nand_platform_data - platform data describing NAND flash banks + * @dev_ready: tests if the NAND flash is ready (READY signal is high) + * @options: bitmask for nand_chip.options + * @parts: optional array of mtd_partitions for static partitioning + * @nr_parts: number of mtd_partitions for static partitoning + */ +struct nand_platform_data { + int (*dev_ready)(struct nand_platform_data *data); + unsigned int options; + struct mtd_partition *parts; + unsigned int nr_parts; +}; + #endif diff --git a/include/asm-arm/setup.h b/include/asm-arm/setup.h index ea3ed246523..b0d181e7c0f 100644 --- a/include/asm-arm/setup.h +++ b/include/asm-arm/setup.h @@ -134,6 +134,13 @@ struct tag_acorn { u8 adfsdrives; }; +/* TI OMAP specific information */ +#define ATAG_BOARD 0x414f4d50 + +struct tag_omap { + u8 data[0]; +}; + /* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */ #define ATAG_MEMCLK 0x41000402 @@ -159,6 +166,11 @@ struct tag { */ struct tag_acorn acorn; + /* + * OMAP specific + */ + struct tag_omap omap; + /* * DC21285 specific */ diff --git a/include/linux/fb.h b/include/linux/fb.h index 2cb19e6503a..e23032c8217 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -109,6 +109,7 @@ #define FB_ACCEL_NV_40 46 /* nVidia Arch 40 */ #define FB_ACCEL_XGI_VOLARI_V 47 /* XGI Volari V3XT, V5, V8 */ #define FB_ACCEL_XGI_VOLARI_Z 48 /* XGI Volari Z7 */ +#define FB_ACCEL_OMAP1610 49 /* TI OMAP16xx */ #define FB_ACCEL_NEOMAGIC_NM2070 90 /* NeoMagic NM2070 */ #define FB_ACCEL_NEOMAGIC_NM2090 91 /* NeoMagic NM2090 */ #define FB_ACCEL_NEOMAGIC_NM2093 92 /* NeoMagic NM2093 */ diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h index 474c8f4f5d4..d1e86965eb7 100644 --- a/include/linux/i2c-id.h +++ b/include/linux/i2c-id.h @@ -109,6 +109,8 @@ #define I2C_DRIVERID_UPD64031A 79 /* upd64031a video processor */ #define I2C_DRIVERID_SAA717X 80 /* saa717x video encoder */ +#define I2C_DRIVERID_MISC 99 /* Whatever until sorted out */ + #define I2C_DRIVERID_I2CDEV 900 #define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */ #define I2C_DRIVERID_ALERT 903 /* SMBus Alert Responder Client */ diff --git a/include/linux/input.h b/include/linux/input.h index 6d4cc3c110d..84898732ea1 100644 --- a/include/linux/input.h +++ b/include/linux/input.h @@ -334,10 +334,22 @@ struct input_absinfo { #define KEY_BRIGHTNESSUP 225 #define KEY_MEDIA 226 -#define KEY_SWITCHVIDEOMODE 227 -#define KEY_KBDILLUMTOGGLE 228 -#define KEY_KBDILLUMDOWN 229 -#define KEY_KBDILLUMUP 230 + +/*Zeus: these keys are defined for OMAP730 Perseus2*/ +#define KEY_STAR 227 +#define KEY_SHARP 228 +#define KEY_SOFT1 229 +#define KEY_SOFT2 230 +#define KEY_SEND 231 +#define KEY_CENTER 232 +#define KEY_HEADSETHOOK 233 +#define KEY_0_5 234 +#define KEY_2_5 235 + +#define KEY_SWITCHVIDEOMODE 236 +#define KEY_KBDILLUMTOGGLE 237 +#define KEY_KBDILLUMDOWN 238 +#define KEY_KBDILLUMUP 239 #define KEY_SEND 231 #define KEY_REPLY 232 diff --git a/include/linux/netfilter_ipv4/ipt_IDLETIMER.h b/include/linux/netfilter_ipv4/ipt_IDLETIMER.h new file mode 100644 index 00000000000..89993e27448 --- /dev/null +++ b/include/linux/netfilter_ipv4/ipt_IDLETIMER.h @@ -0,0 +1,22 @@ +/* + * linux/include/linux/netfilter_ipv4/ipt_IDLETIMER.h + * + * Header file for IP tables timer target module. + * + * Copyright (C) 2004 Nokia Corporation + * Written by Timo Teräs + * + * 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. + */ + +#ifndef _IPT_TIMER_H +#define _IPT_TIMER_H + +struct ipt_idletimer_info { + unsigned int timeout; +}; + +#endif diff --git a/include/linux/spi/ads7846.h b/include/linux/spi/ads7846.h index 72261e0f2ac..3f766495125 100644 --- a/include/linux/spi/ads7846.h +++ b/include/linux/spi/ads7846.h @@ -14,5 +14,8 @@ struct ads7846_platform_data { u16 x_min, x_max; u16 y_min, y_max; u16 pressure_min, pressure_max; + + u16 debounce_max; /* max number of readings per sample */ + u16 debounce_tol; /* tolerance used for filtering */ }; diff --git a/init/do_mounts.c b/init/do_mounts.c index b27c1106440..d63da86037b 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -286,6 +286,7 @@ void __init mount_block_root(char *name, int flags) char *fs_names = __getname(); char *p; char b[BDEVNAME_SIZE]; + int i = 0; get_fs_names(fs_names); retry: @@ -300,6 +301,14 @@ retry: case -EINVAL: continue; } + + printk("VFS: No root yet, retrying to mount root on %s (%s)\n", + root_device_name, __bdevname(ROOT_DEV, b)); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(10 * HZ); + if (i++ < 5) + goto retry; + /* * Allow the user to distinguish between failed sys_open * and bad superblock on root device. diff --git a/kernel/printk.c b/kernel/printk.c index 13ced0f7828..2389f21c9f7 100644 --- a/kernel/printk.c +++ b/kernel/printk.c @@ -36,6 +36,10 @@ #define __LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT) +#ifdef CONFIG_DEBUG_LL +extern void printascii(char *); +#endif + /* printk's without a loglevel use this.. */ #define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */ @@ -547,6 +551,10 @@ asmlinkage int vprintk(const char *fmt, va_list args) /* Emit the output into the temporary buffer */ printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args); +#ifdef CONFIG_DEBUG_LL + printascii(printk_buf); +#endif + /* * Copy the output into log_buf. If the caller didn't provide * appropriate log level tags, we insert them here diff --git a/kernel/timer.c b/kernel/timer.c index 2410c18dbeb..3fc403131ad 100644 --- a/kernel/timer.c +++ b/kernel/timer.c @@ -488,8 +488,7 @@ unsigned long next_timer_interrupt(void) tvec_base_t *base; struct list_head *list; struct timer_list *nte; - unsigned long expires; - unsigned long hr_expires = MAX_JIFFY_OFFSET; + unsigned long expires, hr_expires = MAX_JIFFY_OFFSET; ktime_t hr_delta; tvec_t *varray[4]; int i, j; @@ -941,8 +940,6 @@ static inline void update_times(void) void do_timer(struct pt_regs *regs) { jiffies_64++; - /* prevent loading jiffies before storing new jiffies_64 value. */ - barrier(); update_times(); softlockup_tick(regs); } @@ -1354,8 +1351,8 @@ void __init init_timers(void) #ifdef CONFIG_TIME_INTERPOLATION -struct time_interpolator *time_interpolator __read_mostly; -static struct time_interpolator *time_interpolator_list __read_mostly; +struct time_interpolator *time_interpolator; +static struct time_interpolator *time_interpolator_list; static DEFINE_SPINLOCK(time_interpolator_lock); static inline u64 time_interpolator_get_cycles(unsigned int src) @@ -1369,10 +1366,10 @@ static inline u64 time_interpolator_get_cycles(unsigned int src) return x(); case TIME_SOURCE_MMIO64 : - return readq_relaxed((void __iomem *)time_interpolator->addr); + return readq((void __iomem *) time_interpolator->addr); case TIME_SOURCE_MMIO32 : - return readl_relaxed((void __iomem *)time_interpolator->addr); + return readl((void __iomem *) time_interpolator->addr); default: return get_cycles(); } diff --git a/net/ipv4/netfilter/Kconfig b/net/ipv4/netfilter/Kconfig index db783036e4d..3626b1b00f8 100644 --- a/net/ipv4/netfilter/Kconfig +++ b/net/ipv4/netfilter/Kconfig @@ -447,6 +447,16 @@ config IP_NF_TARGET_SAME To compile it as a module, choose M here. If unsure, say N. +config IP_NF_TARGET_IDLETIMER + tristate "IDLETIMER target support" + depends on IP_NF_IPTABLES + help + This option adds a `IDLETIMER' target. Each matching packet resets + the timer associated with input and/or output interfaces. Timer + expiry causes kobject uevent. Idle timer can be read via sysfs. + + To compile it as a module, choose M here. If unsure, say N. + config IP_NF_NAT_SNMP_BASIC tristate "Basic SNMP-ALG support (EXPERIMENTAL)" depends on EXPERIMENTAL && IP_NF_NAT diff --git a/net/ipv4/netfilter/Makefile b/net/ipv4/netfilter/Makefile index e5c5b3202f0..b8e450acd3a 100644 --- a/net/ipv4/netfilter/Makefile +++ b/net/ipv4/netfilter/Makefile @@ -68,6 +68,7 @@ obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o obj-$(CONFIG_IP_NF_TARGET_SAME) += ipt_SAME.o +obj-$(CONFIG_IP_NF_TARGET_IDLETIMER) += ipt_IDLETIMER.o obj-$(CONFIG_IP_NF_NAT_SNMP_BASIC) += ip_nat_snmp_basic.o obj-$(CONFIG_IP_NF_TARGET_LOG) += ipt_LOG.o obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o diff --git a/net/ipv4/netfilter/ipt_IDLETIMER.c b/net/ipv4/netfilter/ipt_IDLETIMER.c new file mode 100644 index 00000000000..9a11466c3d1 --- /dev/null +++ b/net/ipv4/netfilter/ipt_IDLETIMER.c @@ -0,0 +1,285 @@ +/* + * linux/net/ipv4/netfilter/ipt_IDLETIMER.c + * + * Netfilter module to trigger a timer when packet matches. + * After timer expires a kevent will be sent. + * + * Copyright (C) 2004 Nokia Corporation + * Written by Timo Teräs + * + * 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 +#include +#include +#include +#include +#include + +#if 0 +#define DEBUGP(format, args...) printk("%s:%s:" format, \ + __FILE__, __FUNCTION__ , ## args) +#else +#define DEBUGP(format, args...) +#endif + +/* + * Internal timer management. + */ +static ssize_t utimer_attr_show(struct class_device *, char *buf); +static ssize_t utimer_attr_store(struct class_device *, + const char *buf, size_t count); + +struct utimer_t { + char name[IFNAMSIZ]; + struct list_head entry; + struct timer_list timer; +}; + +static LIST_HEAD(active_utimer_head); +static DEFINE_SPINLOCK(list_lock); +static CLASS_DEVICE_ATTR(idletimer, 0644, utimer_attr_show, utimer_attr_store); + +static void utimer_delete(struct utimer_t *timer) +{ + DEBUGP("Deleting timer '%s'\n", timer->name); + + list_del(&timer->entry); + del_timer_sync(&timer->timer); + kfree(timer); +} + +static void utimer_expired(unsigned long data) +{ + struct utimer_t *timer = (struct utimer_t *) data; + struct net_device *netdev; + + DEBUGP("Timer '%s' expired\n", timer->name); + netdev = dev_get_by_name(timer->name); + + spin_lock_bh(&list_lock); + utimer_delete(timer); + spin_unlock_bh(&list_lock); + + if (netdev != NULL) { + kobject_uevent(&netdev->class_dev.kobj, + KOBJ_CHANGE); + dev_put(netdev); + } +} + +static struct utimer_t *utimer_create(const char *name) +{ + struct utimer_t *timer; + + timer = kmalloc(sizeof(struct utimer_t), GFP_ATOMIC); + if (timer == NULL) + return NULL; + + list_add(&timer->entry, &active_utimer_head); + strlcpy(timer->name, name, sizeof(timer->name)); + + init_timer(&timer->timer); + timer->timer.function = utimer_expired; + timer->timer.data = (unsigned long) timer; + + DEBUGP("Created timer '%s'\n", timer->name); + + return timer; +} + +static struct utimer_t *__utimer_find(const char *name) +{ + struct utimer_t *entry; + + list_for_each_entry(entry, &active_utimer_head, entry) { + if (strcmp(name, entry->name) == 0) { + return entry; + } + } + + return NULL; +} + +static void utimer_modify(const char *name, + unsigned long expires) +{ + struct utimer_t *timer; + + DEBUGP("Modifying timer '%s'\n", name); + spin_lock_bh(&list_lock); + timer = __utimer_find(name); + if (timer == NULL) + timer = utimer_create(name); + mod_timer(&timer->timer, expires); + spin_unlock_bh(&list_lock); +} + +static ssize_t utimer_attr_show(struct class_device *dev, char *buf) +{ + struct utimer_t *timer; + unsigned long expires = 0; + + spin_lock_bh(&list_lock); + timer = __utimer_find(dev->class_id); + if (timer) + expires = timer->timer.expires; + spin_unlock_bh(&list_lock); + + if (expires) + return sprintf(buf, "%lu\n", (expires-jiffies) / HZ); + + return sprintf(buf, "0\n"); +} + +static ssize_t utimer_attr_store(struct class_device *dev, + const char *buf, size_t count) +{ + int expires; + + if (sscanf(buf, "%d", &expires) == 1) { + if (expires > 0) + utimer_modify(dev->class_id, + jiffies+HZ*(unsigned long)expires); + } + + return count; +} + +static int utimer_notifier_call(struct notifier_block *this, + unsigned long event, void *ptr) +{ + struct net_device *dev = ptr; + + switch (event) { + case NETDEV_UP: + DEBUGP("NETDEV_UP: %s\n", dev->name); + class_device_create_file(&dev->class_dev, + &class_device_attr_idletimer); + break; + case NETDEV_DOWN: + DEBUGP("NETDEV_DOWN: %s\n", dev->name); + class_device_remove_file(&dev->class_dev, + &class_device_attr_idletimer); + break; + } + + return NOTIFY_DONE; +} + +static struct notifier_block utimer_notifier_block = { + .notifier_call = utimer_notifier_call, +}; + + +static int utimer_init(void) +{ + return register_netdevice_notifier(&utimer_notifier_block); +} + +static void utimer_fini(void) +{ + struct utimer_t *entry, *next; + struct net_device *dev; + + list_for_each_entry_safe(entry, next, &active_utimer_head, entry) + utimer_delete(entry); + + rtnl_lock(); + unregister_netdevice_notifier(&utimer_notifier_block); + for (dev = dev_base; dev; dev = dev->next) + utimer_notifier_call(&utimer_notifier_block, + NETDEV_DOWN, dev); + rtnl_unlock(); +} + +/* + * The actual iptables plugin. + */ +static unsigned int ipt_idletimer_target(struct sk_buff **pskb, + const struct net_device *in, + const struct net_device *out, + unsigned int hooknum, + const void *targinfo, + void *userinfo) +{ + struct ipt_idletimer_info *target = (struct ipt_idletimer_info*) targinfo; + unsigned long expires; + + expires = jiffies + HZ*target->timeout; + + if (in != NULL) + utimer_modify(in->name, expires); + + if (out != NULL) + utimer_modify(out->name, expires); + + return IPT_CONTINUE; +} + +static int ipt_idletimer_checkentry(const char *tablename, + const void *e, + void *targinfo, + unsigned int targinfosize, + unsigned int hookmask) +{ + struct ipt_idletimer_info *info = + (struct ipt_idletimer_info *) targinfo; + + if (targinfosize != IPT_ALIGN(sizeof(struct ipt_idletimer_info))) { + DEBUGP("targinfosize %u != 0\n", targinfosize); + return 0; + } + + if (info->timeout == 0) { + DEBUGP("timeout value is zero\n"); + return 0; + } + + return 1; +} + +static struct ipt_target ipt_idletimer = { + .name = "IDLETIMER", + .target = ipt_idletimer_target, + .checkentry = ipt_idletimer_checkentry, + .me = THIS_MODULE, +}; + +static int __init init(void) +{ + int ret; + + ret = utimer_init(); + if (ret) + return ret; + + if (ipt_register_target(&ipt_idletimer)) { + utimer_fini(); + return -EINVAL; + } + + return 0; +} + +static void __exit fini(void) +{ + ipt_unregister_target(&ipt_idletimer); + utimer_fini(); +} + +module_init(init); +module_exit(fini); + +MODULE_AUTHOR("Timo Teräs "); +MODULE_DESCRIPTION("iptables idletimer target module"); +MODULE_LICENSE("GPL"); diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig index 2e4a5e0d16d..cf071547a13 100644 --- a/sound/arm/Kconfig +++ b/sound/arm/Kconfig @@ -33,4 +33,16 @@ config SND_PXA2XX_AC97 Say Y or M if you want to support any AC97 codec attached to the PXA2xx AC97 interface. +config SND_OMAP_AIC23 + tristate "OMAP AIC23 alsa driver (osk5912)" + depends on ARCH_OMAP && SND + select SND_PCM + select SENSORS_TLV320AIC23 + help + Say Y here if you have a OSK platform board + and want to use its AIC23 audio chip. + + To compile this driver as a module, choose M here: the module + will be called snd-omap-aic23. + endmenu diff --git a/sound/arm/Makefile b/sound/arm/Makefile index 4ef6dd00c6e..019a9f03c2c 100644 --- a/sound/arm/Makefile +++ b/sound/arm/Makefile @@ -13,3 +13,6 @@ snd-pxa2xx-pcm-objs := pxa2xx-pcm.o obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o snd-pxa2xx-ac97-objs := pxa2xx-ac97.o + +obj-$(CONFIG_SND_OMAP_AIC23) += snd-omap-aic23.o +snd-omap-aic23-objs := omap-aic23.o omap-alsa-dma.o omap-alsa-mixer.o diff --git a/sound/arm/omap-aic23.c b/sound/arm/omap-aic23.c new file mode 100644 index 00000000000..c92e24534f4 --- /dev/null +++ b/sound/arm/omap-aic23.c @@ -0,0 +1,929 @@ +/* + * sound/arm/omap-aic23.c + * + * Alsa Driver for AIC23 codec on OSK5912 platform board + * + * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil + * Written by Daniel Petrini, David Cohen, Anderson Briglia + * {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br + * + * Based on sa11xx-uda1341.c, + * Copyright (C) 2002 Tomas Kasparek + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: + * + * 2005-07-29 INdT Kernel Team - Alsa driver for omap osk. Creation of new + * file omap-aic23.c + * + * 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_PM +#include +#endif + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "omap-alsa-dma.h" +#include "omap-aic23.h" + +#undef DEBUG + +#ifdef DEBUG +#define ADEBUG() printk("XXX Alsa debug f:%s, l:%d\n", __FUNCTION__, __LINE__) +#else +#define ADEBUG() /* nop */ +#endif + +/* Define to set the AIC23 as the master w.r.t McBSP */ +#define AIC23_MASTER + +/* + * AUDIO related MACROS + */ +#define DEFAULT_BITPERSAMPLE 16 +#define AUDIO_RATE_DEFAULT 44100 +#define AUDIO_MCBSP OMAP_MCBSP1 +#define NUMBER_SAMPLE_RATES_SUPPORTED 10 + + +MODULE_AUTHOR("Daniel Petrini, David Cohen, Anderson Briglia - INdT"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("OMAP AIC23 driver for ALSA"); +MODULE_SUPPORTED_DEVICE("{{AIC23,OMAP AIC23}}"); +MODULE_ALIAS("omap_mcbsp.1"); + +static char *id = NULL; +MODULE_PARM_DESC(id, "OMAP OSK ALSA Driver for AIC23 chip."); + +static struct snd_card_omap_aic23 *omap_aic23 = NULL; + +static struct clk *aic23_mclk = 0; + +struct sample_rate_rate_reg_info { + u8 control; /* SR3, SR2, SR1, SR0 and BOSR */ + u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */ +}; + +/* + * DAC USB-mode sampling rates (MCLK = 12 MHz) + * The rates and rate_reg_into MUST be in the same order + */ +static unsigned int rates[] = { + 4000, 8000, 16000, 22050, + 24000, 32000, 44100, + 48000, 88200, 96000, +}; +static const struct sample_rate_rate_reg_info + rate_reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = { + {0x06, 1}, /* 4000 */ + {0x06, 0}, /* 8000 */ + {0x0C, 1}, /* 16000 */ + {0x11, 1}, /* 22050 */ + {0x00, 1}, /* 24000 */ + {0x0C, 0}, /* 32000 */ + {0x11, 0}, /* 44100 */ + {0x00, 0}, /* 48000 */ + {0x1F, 0}, /* 88200 */ + {0x0E, 0}, /* 96000 */ +}; + +/* + * mcbsp configuration structure + */ +static struct omap_mcbsp_reg_cfg initial_config_mcbsp = { + .spcr2 = FREE | FRST | GRST | XRST | XINTM(3), + .spcr1 = RINTM(3) | RRST, + .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) | + RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(0), + .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16), + .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) | + XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(0) | XFIG, + .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16), + .srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1), + .srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1), +#ifndef AIC23_MASTER + /* configure McBSP to be the I2S master */ + .pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP, +#else + /* configure McBSP to be the I2S slave */ + .pcr0 = CLKXP | CLKRP, +#endif /* AIC23_MASTER */ +}; + +static snd_pcm_hw_constraint_list_t hw_constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +/* + * HW interface start and stop helper functions + */ +static int audio_ifc_start(void) +{ + omap_mcbsp_start(AUDIO_MCBSP); + return 0; +} + +static int audio_ifc_stop(void) +{ + omap_mcbsp_stop(AUDIO_MCBSP); + return 0; +} + +/* + * Codec/mcbsp init and configuration section + * codec dependent code. + */ + +/* + * Sample rate changing + */ +static void omap_aic23_set_samplerate(struct snd_card_omap_aic23 + *omap_aic23, long rate) +{ + u8 count = 0; + u16 data = 0; + + /* Fix the rate if it has a wrong value */ + if (rate >= 96000) + rate = 96000; + else if (rate >= 88200) + rate = 88200; + else if (rate >= 48000) + rate = 48000; + else if (rate >= 44100) + rate = 44100; + else if (rate >= 32000) + rate = 32000; + else if (rate >= 24000) + rate = 24000; + else if (rate >= 22050) + rate = 22050; + else if (rate >= 16000) + rate = 16000; + else if (rate >= 8000) + rate = 8000; + else + rate = 4000; + + /* Search for the right sample rate */ + /* Verify what happens if the rate is not supported + * now it goes to 96Khz */ + while ((rates[count] != rate) && + (count < (NUMBER_SAMPLE_RATES_SUPPORTED - 1))) { + count++; + } + + data = (rate_reg_info[count].divider << CLKIN_SHIFT) | + (rate_reg_info[count].control << BOSR_SHIFT) | USB_CLK_ON; + + audio_aic23_write(SAMPLE_RATE_CONTROL_ADDR, data); + + omap_aic23->samplerate = rate; +} + +static inline void aic23_configure(void) +{ + /* Reset codec */ + audio_aic23_write(RESET_CONTROL_ADDR, 0); + + /* Initialize the AIC23 internal state */ + + /* Analog audio path control, DAC selected, delete INSEL_MIC for line in */ + audio_aic23_write(ANALOG_AUDIO_CONTROL_ADDR, DEFAULT_ANALOG_AUDIO_CONTROL); + + /* Digital audio path control, de-emphasis control 44.1kHz */ + audio_aic23_write(DIGITAL_AUDIO_CONTROL_ADDR, DEEMP_44K); + + /* Digital audio interface, master/slave mode, I2S, 16 bit */ +#ifdef AIC23_MASTER + audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, + MS_MASTER | IWL_16 | FOR_DSP); +#else + audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, IWL_16 | FOR_DSP); +#endif + + /* Enable digital interface */ + audio_aic23_write(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON); + +} + +static void omap_aic23_audio_init(struct snd_card_omap_aic23 *omap_aic23) +{ + /* Setup DMA stuff */ + omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].id = "Alsa AIC23 out"; + omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].stream_id = + SNDRV_PCM_STREAM_PLAYBACK; + omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].dma_dev = + OMAP_DMA_MCBSP1_TX; + omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].hw_start = + audio_ifc_start; + omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK].hw_stop = + audio_ifc_stop; + + omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].id = "Alsa AIC23 in"; + omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].stream_id = + SNDRV_PCM_STREAM_CAPTURE; + omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].dma_dev = + OMAP_DMA_MCBSP1_RX; + omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].hw_start = + audio_ifc_start; + omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE].hw_stop = + audio_ifc_stop; + + /* configuring the McBSP */ + omap_mcbsp_request(AUDIO_MCBSP); + + /* if configured, then stop mcbsp */ + omap_mcbsp_stop(AUDIO_MCBSP); + + omap_mcbsp_config(AUDIO_MCBSP, &initial_config_mcbsp); + omap_mcbsp_start(AUDIO_MCBSP); + aic23_configure(); +} + +/* + * DMA functions + * Depends on omap-aic23-dma.c functions and (omap) dma.c + * + */ +#define DMA_BUF_SIZE 1024 * 8 + +static int audio_dma_request(struct audio_stream *s, + void (*callback) (void *)) +{ + int err; + + err = omap_request_sound_dma(s->dma_dev, s->id, s, &s->lch); + if (err < 0) + printk(KERN_ERR "unable to grab audio dma 0x%x\n", + s->dma_dev); + return err; +} + +static int audio_dma_free(struct audio_stream *s) +{ + int err = 0; + + err = omap_free_sound_dma(s, &s->lch); + if (err < 0) + printk(KERN_ERR "Unable to free audio dma channels!\n"); + return err; +} + +/* + * This function should calculate the current position of the dma in the + * buffer. It will help alsa middle layer to continue update the buffer. + * Its correctness is crucial for good functioning. + */ +static u_int audio_get_dma_pos(struct audio_stream *s) +{ + snd_pcm_substream_t *substream = s->stream; + snd_pcm_runtime_t *runtime = substream->runtime; + unsigned int offset; + unsigned long flags; + dma_addr_t count; + ADEBUG(); + + /* this must be called w/ interrupts locked as requested in dma.c */ + spin_lock_irqsave(&s->dma_lock, flags); + + /* For the current period let's see where we are */ + count = omap_get_dma_src_addr_counter(s->lch[s->dma_q_head]); + + spin_unlock_irqrestore(&s->dma_lock, flags); + + /* Now, the position related to the end of that period */ + offset = bytes_to_frames(runtime, s->offset) - bytes_to_frames(runtime, count); + + if (offset >= runtime->buffer_size || offset < 0) + offset = 0; + + return offset; +} + +/* + * this stops the dma and clears the dma ptrs + */ +static void audio_stop_dma(struct audio_stream *s) +{ + unsigned long flags; + ADEBUG(); + + spin_lock_irqsave(&s->dma_lock, flags); + s->active = 0; + s->period = 0; + s->periods = 0; + + /* this stops the dma channel and clears the buffer ptrs */ + omap_audio_stop_dma(s); + + omap_clear_sound_dma(s); + + spin_unlock_irqrestore(&s->dma_lock, flags); +} + +/* + * Main dma routine, requests dma according where you are in main alsa buffer + */ +static void audio_process_dma(struct audio_stream *s) +{ + snd_pcm_substream_t *substream = s->stream; + snd_pcm_runtime_t *runtime; + unsigned int dma_size; + unsigned int offset; + int ret; + + runtime = substream->runtime; + if (s->active) { + dma_size = frames_to_bytes(runtime, runtime->period_size); + offset = dma_size * s->period; + snd_assert(dma_size <= DMA_BUF_SIZE,); + ret = + omap_start_sound_dma(s, + (dma_addr_t) runtime->dma_area + + offset, dma_size); + if (ret) { + printk(KERN_ERR + "audio_process_dma: cannot queue DMA buffer (%i)\n", + ret); + return; + } + + s->period++; + s->period %= runtime->periods; + s->periods++; + s->offset = offset; + } +} + +/* + * This is called when dma IRQ occurs at the end of each transmited block + */ +void audio_dma_callback(void *data) +{ + struct audio_stream *s = data; + + /* + * If we are getting a callback for an active stream then we inform + * the PCM middle layer we've finished a period + */ + if (s->active) + snd_pcm_period_elapsed(s->stream); + + spin_lock(&s->dma_lock); + if (s->periods > 0) { + s->periods--; + } + audio_process_dma(s); + spin_unlock(&s->dma_lock); +} + + +/* + * Alsa section + * PCM settings and callbacks + */ + +static int snd_omap_aic23_trigger(snd_pcm_substream_t * substream, int cmd) +{ + struct snd_card_omap_aic23 *chip = + snd_pcm_substream_chip(substream); + int stream_id = substream->pstr->stream; + struct audio_stream *s = &chip->s[stream_id]; + int err = 0; + ADEBUG(); + + /* note local interrupts are already disabled in the midlevel code */ + spin_lock(&s->dma_lock); + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* requested stream startup */ + s->active = 1; + audio_process_dma(s); + break; + case SNDRV_PCM_TRIGGER_STOP: + /* requested stream shutdown */ + audio_stop_dma(s); + break; + default: + err = -EINVAL; + break; + } + spin_unlock(&s->dma_lock); + + return err; +} + +static int snd_omap_aic23_prepare(snd_pcm_substream_t * substream) +{ + struct snd_card_omap_aic23 *chip = + snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + struct audio_stream *s = &chip->s[substream->pstr->stream]; + + /* set requested samplerate */ + omap_aic23_set_samplerate(chip, runtime->rate); + + s->period = 0; + s->periods = 0; + + return 0; +} + +static snd_pcm_uframes_t snd_omap_aic23_pointer(snd_pcm_substream_t * + substream) +{ + struct snd_card_omap_aic23 *chip = + snd_pcm_substream_chip(substream); + + return audio_get_dma_pos(&chip->s[substream->pstr->stream]); +} + +/* Hardware capabilities */ + +static snd_pcm_hardware_t snd_omap_aic23_capture = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_KNOT), + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8 * 1024, + .periods_min = 16, + .periods_max = 255, + .fifo_size = 0, +}; + +static snd_pcm_hardware_t snd_omap_aic23_playback = { + .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID), + .formats = (SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | + SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_32000 | + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | + SNDRV_PCM_RATE_KNOT), + .rate_min = 8000, + .rate_max = 96000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 128 * 1024, + .period_bytes_min = 32, + .period_bytes_max = 8 * 1024, + .periods_min = 16, + .periods_max = 255, + .fifo_size = 0, +}; + +static int snd_card_omap_aic23_open(snd_pcm_substream_t * substream) +{ + struct snd_card_omap_aic23 *chip = + snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + int stream_id = substream->pstr->stream; + int err; + ADEBUG(); + + chip->s[stream_id].stream = substream; + + omap_aic23_clock_on(); + + if (stream_id == SNDRV_PCM_STREAM_PLAYBACK) + runtime->hw = snd_omap_aic23_playback; + else + runtime->hw = snd_omap_aic23_capture; + if ((err = + snd_pcm_hw_constraint_integer(runtime, + SNDRV_PCM_HW_PARAM_PERIODS)) < + 0) + return err; + if ((err = + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &hw_constraints_rates)) < 0) + return err; + + return 0; +} + +static int snd_card_omap_aic23_close(snd_pcm_substream_t * substream) +{ + struct snd_card_omap_aic23 *chip = + snd_pcm_substream_chip(substream); + ADEBUG(); + + omap_aic23_clock_off(); + chip->s[substream->pstr->stream].stream = NULL; + + return 0; +} + +/* HW params & free */ + +static int snd_omap_aic23_hw_params(snd_pcm_substream_t * substream, + snd_pcm_hw_params_t * hw_params) +{ + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +} + +static int snd_omap_aic23_hw_free(snd_pcm_substream_t * substream) +{ + return snd_pcm_lib_free_pages(substream); +} + +/* pcm operations */ + +static snd_pcm_ops_t snd_card_omap_aic23_playback_ops = { + .open = snd_card_omap_aic23_open, + .close = snd_card_omap_aic23_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_omap_aic23_hw_params, + .hw_free = snd_omap_aic23_hw_free, + .prepare = snd_omap_aic23_prepare, + .trigger = snd_omap_aic23_trigger, + .pointer = snd_omap_aic23_pointer, +}; + +static snd_pcm_ops_t snd_card_omap_aic23_capture_ops = { + .open = snd_card_omap_aic23_open, + .close = snd_card_omap_aic23_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_omap_aic23_hw_params, + .hw_free = snd_omap_aic23_hw_free, + .prepare = snd_omap_aic23_prepare, + .trigger = snd_omap_aic23_trigger, + .pointer = snd_omap_aic23_pointer, +}; + +/* + * Alsa init and exit section + * + * Inits pcm alsa structures, allocate the alsa buffer, suspend, resume + */ +static int __init snd_card_omap_aic23_pcm(struct snd_card_omap_aic23 + *omap_aic23, int device) +{ + snd_pcm_t *pcm; + int err; + ADEBUG(); + + if ((err = + snd_pcm_new(omap_aic23->card, "AIC23 PCM", device, 1, 1, + &pcm)) < 0) + return err; + + /* sets up initial buffer with continuous allocation */ + snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data + (GFP_KERNEL), + 128 * 1024, 128 * 1024); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &snd_card_omap_aic23_playback_ops); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_card_omap_aic23_capture_ops); + pcm->private_data = omap_aic23; + pcm->info_flags = 0; + strcpy(pcm->name, "omap aic23 pcm"); + + omap_aic23_audio_init(omap_aic23); + + /* setup DMA controller */ + audio_dma_request(&omap_aic23->s[SNDRV_PCM_STREAM_PLAYBACK], + audio_dma_callback); + audio_dma_request(&omap_aic23->s[SNDRV_PCM_STREAM_CAPTURE], + audio_dma_callback); + + omap_aic23->pcm = pcm; + + return 0; +} + + +#ifdef CONFIG_PM + +static int snd_omap_aic23_suspend(snd_card_t * card, pm_message_t state) +{ + struct snd_card_omap_aic23 *chip = card->private_data; + ADEBUG(); + + if (chip->card->power_state != SNDRV_CTL_POWER_D3hot) { + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); + snd_pcm_suspend_all(chip->pcm); + /* Mutes and turn clock off */ + omap_aic23_clock_off(); + snd_omap_suspend_mixer(); + } + + return 0; +} + +/* + * Prepare hardware for resume + */ +static int snd_omap_aic23_resume(snd_card_t * card) +{ + struct snd_card_omap_aic23 *chip = card->private_data; + ADEBUG(); + + if (chip->card->power_state != SNDRV_CTL_POWER_D0) { + snd_power_change_state(chip->card, SNDRV_CTL_POWER_D0); + omap_aic23_clock_on(); + snd_omap_resume_mixer(); + } + + return 0; +} + +/* + * Driver suspend/resume - calls alsa functions. Some hints from aaci.c + */ +static int omap_aic23_suspend(struct platform_device *pdev, pm_message_t state) +{ + snd_card_t *card = platform_get_drvdata(pdev); + + if (card->power_state != SNDRV_CTL_POWER_D3hot) { + snd_omap_aic23_suspend(card, PMSG_SUSPEND); + } + return 0; +} + +static int omap_aic23_resume(struct platform_device *pdev) +{ + snd_card_t *card = platform_get_drvdata(pdev); + + if (card->power_state != SNDRV_CTL_POWER_D0) { + snd_omap_aic23_resume(card); + } + return 0; +} + +#else +#define snd_omap_aic23_suspend NULL +#define snd_omap_aic23_resume NULL +#define omap_aic23_suspend NULL +#define omap_aic23_resume NULL + +#endif /* CONFIG_PM */ + +/* + */ +void snd_omap_aic23_free(snd_card_t * card) +{ + struct snd_card_omap_aic23 *chip = card->private_data; + ADEBUG(); + + /* + * Turn off codec after it is done. + * Can't do it immediately, since it may still have + * buffered data. + */ + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(2); + + omap_mcbsp_stop(AUDIO_MCBSP); + omap_mcbsp_free(AUDIO_MCBSP); + + audio_aic23_write(RESET_CONTROL_ADDR, 0); + audio_aic23_write(POWER_DOWN_CONTROL_ADDR, 0xff); + + audio_dma_free(&chip->s[SNDRV_PCM_STREAM_PLAYBACK]); + audio_dma_free(&chip->s[SNDRV_PCM_STREAM_CAPTURE]); +} + +/* + * Omap MCBSP clock configuration + * + * Here we have some functions that allows clock to be enabled and + * disabled only when needed. Besides doing clock configuration + * it allows turn on/turn off audio when necessary. + */ +#define CODEC_CLOCK 12000000 +#define AUDIO_RATE_DEFAULT 44100 + +/* + * Do clock framework mclk search + */ +static __init void omap_aic23_clock_setup(void) +{ + aic23_mclk = clk_get(0, "mclk"); +} + +/* + * Do some sanity check, set clock rate, starts it and + * turn codec audio on + */ +int omap_aic23_clock_on(void) +{ + if (clk_get_usecount(aic23_mclk) > 0) { + /* MCLK is already in use */ + printk(KERN_WARNING + "MCLK in use at %d Hz. We change it to %d Hz\n", + (uint) clk_get_rate(aic23_mclk), + CODEC_CLOCK); + } + + if (clk_set_rate(aic23_mclk, CODEC_CLOCK)) { + printk(KERN_ERR + "Cannot set MCLK for AIC23 CODEC\n"); + return -ECANCELED; + } + + clk_enable(aic23_mclk); + + printk(KERN_DEBUG + "MCLK = %d [%d], usecount = %d\n", + (uint) clk_get_rate(aic23_mclk), CODEC_CLOCK, + clk_get_usecount(aic23_mclk)); + + /* Now turn the audio on */ + audio_aic23_write(POWER_DOWN_CONTROL_ADDR, + ~DEVICE_POWER_OFF & ~OUT_OFF & ~DAC_OFF & + ~ADC_OFF & ~MIC_OFF & ~LINE_OFF); + + return 0; +} +/* + * Do some sanity check, turn clock off and then turn + * codec audio off + */ +int omap_aic23_clock_off(void) +{ + if (clk_get_usecount(aic23_mclk) > 0) { + if (clk_get_rate(aic23_mclk) != CODEC_CLOCK) { + printk(KERN_WARNING + "MCLK for audio should be %d Hz. But is %d Hz\n", + (uint) clk_get_rate(aic23_mclk), + CODEC_CLOCK); + } + + clk_disable(aic23_mclk); + } + + audio_aic23_write(POWER_DOWN_CONTROL_ADDR, + DEVICE_POWER_OFF | OUT_OFF | DAC_OFF | + ADC_OFF | MIC_OFF | LINE_OFF); + return 0; +} + +/* module init & exit */ + +/* + * Inits alsa soudcard structure + */ +static int __init snd_omap_aic23_probe(struct platform_device *pdev) +{ + int err = 0; + snd_card_t *card; + ADEBUG(); + + /* gets clock from clock framework */ + omap_aic23_clock_setup(); + + /* register the soundcard */ + card = snd_card_new(-1, id, THIS_MODULE, sizeof(omap_aic23)); + if (card == NULL) + return -ENOMEM; + + omap_aic23 = kcalloc(1, sizeof(*omap_aic23), GFP_KERNEL); + if (omap_aic23 == NULL) + return -ENOMEM; + + card->private_data = (void *) omap_aic23; + card->private_free = snd_omap_aic23_free; + + omap_aic23->card = card; + omap_aic23->samplerate = AUDIO_RATE_DEFAULT; + + spin_lock_init(&omap_aic23->s[0].dma_lock); + spin_lock_init(&omap_aic23->s[1].dma_lock); + + /* mixer */ + if ((err = snd_omap_mixer(omap_aic23)) < 0) + goto nodev; + + /* PCM */ + if ((err = snd_card_omap_aic23_pcm(omap_aic23, 0)) < 0) + goto nodev; + + strcpy(card->driver, "AIC23"); + strcpy(card->shortname, "OSK AIC23"); + sprintf(card->longname, "OMAP OSK with AIC23"); + + snd_omap_init_mixer(); + + snd_card_set_dev(card, &pdev->dev); + + if ((err = snd_card_register(card)) == 0) { + printk(KERN_INFO "OSK audio support initialized\n"); + platform_set_drvdata(pdev, card); + return 0; + } + +nodev: + snd_omap_aic23_free(card); + + return err; +} + +static int snd_omap_aic23_remove(struct platform_device *pdev) +{ + snd_card_t *card = platform_get_drvdata(pdev); + struct snd_card_omap_aic23 *chip = card->private_data; + + snd_card_free(card); + + omap_aic23 = NULL; + card->private_data = NULL; + kfree(chip); + + platform_set_drvdata(pdev, NULL); + + return 0; + +} + +static struct platform_driver omap_alsa_driver = { + .probe = snd_omap_aic23_probe, + .remove = snd_omap_aic23_remove, + .suspend = omap_aic23_suspend, + .resume = omap_aic23_resume, + .driver = { + .name = "omap_mcbsp", + }, +}; + +static int __init omap_aic23_init(void) +{ + int err; + ADEBUG(); + + err = platform_driver_register(&omap_alsa_driver); + + return err; +} + +static void __exit omap_aic23_exit(void) +{ + ADEBUG(); + + platform_driver_unregister(&omap_alsa_driver); +} + +module_init(omap_aic23_init); +module_exit(omap_aic23_exit); diff --git a/sound/arm/omap-aic23.h b/sound/arm/omap-aic23.h new file mode 100644 index 00000000000..c8ab42c2935 --- /dev/null +++ b/sound/arm/omap-aic23.h @@ -0,0 +1,130 @@ +/* + * sound/arm/omap-aic23.h + * + * Alsa Driver for AIC23 codec on OSK5912 platform board + * + * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil + * Written by Daniel Petrini, David Cohen, Anderson Briglia + * {daniel.petrini, david.cohen, anderson.briglia}@indt.org.br + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History + * ------- + * + * 2005/07/25 INdT-10LE Kernel Team - Alsa driver for omap osk, + * original version based in sa1100 driver + * and omap oss driver. + * + * 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu + */ + +#ifndef __OMAP_AIC23_H +#define __OMAP_AIC23_H + +#include +#include +#include +#include + +#define DEFAULT_OUTPUT_VOLUME 0x60 +#define DEFAULT_INPUT_VOLUME 0x00 /* 0 ==> mute line in */ + +#define OUTPUT_VOLUME_MIN LHV_MIN +#define OUTPUT_VOLUME_MAX LHV_MAX +#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN) +#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MAX + +#define INPUT_VOLUME_MIN LIV_MIN +#define INPUT_VOLUME_MAX LIV_MAX +#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN) +#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX + +#define SIDETONE_MASK 0x1c0 +#define SIDETONE_0 0x100 +#define SIDETONE_6 0x000 +#define SIDETONE_9 0x040 +#define SIDETONE_12 0x080 +#define SIDETONE_18 0x0c0 + +#define DEFAULT_ANALOG_AUDIO_CONTROL DAC_SELECTED | STE_ENABLED | BYPASS_ON | INSEL_MIC | MICB_20DB + +/* + * Buffer management for alsa and dma + */ +struct audio_stream { + char *id; /* identification string */ + int stream_id; /* numeric identification */ + int dma_dev; /* dma number of that device */ + int *lch; /* Chain of channels this stream is linked to */ + char started; /* to store if the chain was started or not */ + int dma_q_head; /* DMA Channel Q Head */ + int dma_q_tail; /* DMA Channel Q Tail */ + char dma_q_count; /* DMA Channel Q Count */ + int active:1; /* we are using this stream for transfer now */ + int period; /* current transfer period */ + int periods; /* current count of periods registerd in the DMA engine */ + spinlock_t dma_lock; /* for locking in DMA operations */ + snd_pcm_substream_t *stream; /* the pcm stream */ + unsigned linked:1; /* dma channels linked */ + int offset; /* store start position of the last period in the alsa buffer */ + int (*hw_start)(void); /* interface to start HW interface, e.g. McBSP */ + int (*hw_stop)(void); /* interface to stop HW interface, e.g. McBSP */ +}; + +/* + * Alsa card structure for aic23 + */ +struct snd_card_omap_aic23 { + snd_card_t *card; + snd_pcm_t *pcm; + long samplerate; + struct audio_stream s[2]; /* playback & capture */ +}; + +/*********** Function Prototypes *************************/ + +void audio_dma_callback(void *); +int snd_omap_mixer(struct snd_card_omap_aic23 *); +void snd_omap_init_mixer(void); +/* Clock functions */ +int omap_aic23_clock_on(void); +int omap_aic23_clock_off(void); + +#ifdef CONFIG_PM +void snd_omap_suspend_mixer(void); +void snd_omap_resume_mixer(void); +#endif + +/* Codec AIC23 */ +#if defined(CONFIG_SENSORS_TLV320AIC23) || defined (CONFIG_SENSORS_TLV320AIC23_MODULE) + +extern int tlv320aic23_write_value(u8 reg, u16 value); + +/* TLV320AIC23 is a write only device */ +static __inline__ void audio_aic23_write(u8 address, u16 data) +{ + tlv320aic23_write_value(address, data); +} + +#endif /* CONFIG_SENSORS_TLV320AIC23 */ + +#endif diff --git a/sound/arm/omap-alsa-dma.c b/sound/arm/omap-alsa-dma.c new file mode 100644 index 00000000000..8530acf7694 --- /dev/null +++ b/sound/arm/omap-alsa-dma.c @@ -0,0 +1,456 @@ +/* + * sound/arm/omap-alsa-dma.c + * + * Common audio DMA handling for the OMAP processors + * + * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * Copyright (C) 2000, 2001 Nicolas Pitre + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * + * 2004-06-07 Sriram Kannan - Created new file from omap_audio_dma_intfc.c. This file + * will contain only the DMA interface and buffer handling of OMAP + * audio driver. + * + * 2004-06-22 Sriram Kannan - removed legacy code (auto-init). Self-linking of DMA logical channel. + * + * 2004-08-12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms + * + * 2004-11-01 Nishanth Menon - 16xx platform code base modified to support multi channel chaining. + * + * 2004-12-15 Nishanth Menon - Improved 16xx platform channel logic introduced - tasklets, queue handling updated + * + * 2005-07-19 INdT Kernel Team - Alsa port. Creation of new file omap-alsa-dma.c based in + * omap-audio-dma-intfc.c oss file. Support for aic23 codec. + * Removal of buffer handling (Alsa does that), modifications + * in dma handling and port to alsa structures. + * + * 2005-12-18 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "omap-alsa-dma.h" + +#include + +#include "omap-aic23.h" + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS) +#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__) +#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n) +#else + +#define DPRINTK( x... ) +#define FN_IN +#define FN_OUT(x) +#endif + +#define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS); + +/* Channel Queue Handling macros + * tail always points to the current free entry + * Head always points to the current entry being used + * end is either head or tail + */ + +#define AUDIO_QUEUE_INIT(s) s->dma_q_head = s->dma_q_tail = s->dma_q_count = 0; +#define AUDIO_QUEUE_FULL(s) (nr_linked_channels == s->dma_q_count) +#define AUDIO_QUEUE_LAST(s) (1 == s->dma_q_count) +#define AUDIO_QUEUE_EMPTY(s) (0 == s->dma_q_count) +#define __AUDIO_INCREMENT_QUEUE(end) ((end)=((end)+1) % nr_linked_channels) +#define AUDIO_INCREMENT_HEAD(s) __AUDIO_INCREMENT_QUEUE(s->dma_q_head); s->dma_q_count--; +#define AUDIO_INCREMENT_TAIL(s) __AUDIO_INCREMENT_QUEUE(s->dma_q_tail); s->dma_q_count++; + +/* DMA buffer fragmentation sizes */ +#define MAX_DMA_SIZE 0x1000000 /* todo: sync with alsa */ +//#define CUT_DMA_SIZE 0x1000 +/* TODO: To be moved to more appropriate location */ +#define DCSR_ERROR 0x3 +#define DCSR_END_BLOCK (1 << 5) +#define DCSR_SYNC_SET (1 << 6) + +#define DCCR_FS (1 << 5) +#define DCCR_PRIO (1 << 6) +#define DCCR_EN (1 << 7) +#define DCCR_AI (1 << 8) +#define DCCR_REPEAT (1 << 9) +/* if 0 the channel works in 3.1 compatible mode*/ +#define DCCR_N31COMP (1 << 10) +#define DCCR_EP (1 << 11) +#define DCCR_SRC_AMODE_BIT 12 +#define DCCR_SRC_AMODE_MASK (0x3<<12) +#define DCCR_DST_AMODE_BIT 14 +#define DCCR_DST_AMODE_MASK (0x3<<14) +#define AMODE_CONST 0x0 +#define AMODE_POST_INC 0x1 +#define AMODE_SINGLE_INDEX 0x2 +#define AMODE_DOUBLE_INDEX 0x3 + +/**************************** DATA STRUCTURES *****************************************/ + +static spinlock_t dma_list_lock = SPIN_LOCK_UNLOCKED; + +static char nr_linked_channels = 1; + +/*********************************** MODULE SPECIFIC FUNCTIONS ***********************/ + +static void sound_dma_irq_handler(int lch, u16 ch_status, void *data); +static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr, + u_int dma_size); +static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr, + u_int dma_size); +static int audio_start_dma_chain(struct audio_stream * s); + +/*************************************************************************************** + * + * DMA channel requests + * + **************************************************************************************/ +static void omap_sound_dma_link_lch(void *data) +{ + + struct audio_stream *s = (struct audio_stream *) data; + int *chan = s->lch; + int i; + + FN_IN; + if (s->linked) { + FN_OUT(1); + return; + } + for (i = 0; i < nr_linked_channels; i++) { + int cur_chan = chan[i]; + int nex_chan = + ((nr_linked_channels - 1 == + i) ? chan[0] : chan[i + 1]); + omap_dma_link_lch(cur_chan, nex_chan); + } + s->linked = 1; + FN_OUT(0); +} + +int omap_request_sound_dma(int device_id, const char *device_name, + void *data, int **channels) +{ + int i, err = 0; + int *chan = NULL; + FN_IN; + if (unlikely((NULL == channels) || (NULL == device_name))) { + BUG(); + return -EPERM; + } + /* Try allocate memory for the num channels */ + *channels = + (int *) kmalloc(sizeof(int) * nr_linked_channels, GFP_KERNEL); + chan = *channels; + if (NULL == chan) { + ERR("No Memory for channel allocs!\n"); + FN_OUT(-ENOMEM); + return -ENOMEM; + } + spin_lock(&dma_list_lock); + for (i = 0; i < nr_linked_channels; i++) { + err = + omap_request_dma(device_id, device_name, + sound_dma_irq_handler, data, + &chan[i]); + + /* Handle Failure condition here */ + if (err < 0) { + int j; + for (j = 0; j < i; j++) { + omap_free_dma(chan[j]); + } + spin_unlock(&dma_list_lock); + kfree(chan); + *channels = NULL; + ERR("Error in requesting channel %d=0x%x\n", i, + err); + FN_OUT(err); + return err; + } + } + + /* Chain the channels together */ + if (!cpu_is_omap1510()) + omap_sound_dma_link_lch(data); + + spin_unlock(&dma_list_lock); + FN_OUT(0); + return 0; +} + +/*************************************************************************************** + * + * DMA channel requests Freeing + * + **************************************************************************************/ +static void omap_sound_dma_unlink_lch(void *data) +{ + struct audio_stream *s = (struct audio_stream *) data; + int *chan = s->lch; + int i; + + FN_IN; + if (!s->linked) { + FN_OUT(1); + return; + } + for (i = 0; i < nr_linked_channels; i++) { + int cur_chan = chan[i]; + int nex_chan = + ((nr_linked_channels - 1 == + i) ? chan[0] : chan[i + 1]); + omap_dma_unlink_lch(cur_chan, nex_chan); + } + s->linked = 0; + FN_OUT(0); +} + +int omap_free_sound_dma(void *data, int **channels) +{ + + int i; + int *chan = NULL; + FN_IN; + if (unlikely(NULL == channels)) { + BUG(); + return -EPERM; + } + if (unlikely(NULL == *channels)) { + BUG(); + return -EPERM; + } + chan = (*channels); + + if (!cpu_is_omap1510()) + omap_sound_dma_unlink_lch(data); + for (i = 0; i < nr_linked_channels; i++) { + int cur_chan = chan[i]; + omap_stop_dma(cur_chan); + omap_free_dma(cur_chan); + } + kfree(*channels); + *channels = NULL; + FN_OUT(0); + return 0; +} + +/*************************************************************************************** + * + * Stop all the DMA channels of the stream + * + **************************************************************************************/ +void omap_audio_stop_dma(struct audio_stream *s) +{ + int *chan = s->lch; + int i; + FN_IN; + if (unlikely(NULL == chan)) { + BUG(); + return; + } + for (i = 0; i < nr_linked_channels; i++) { + int cur_chan = chan[i]; + omap_stop_dma(cur_chan); + } + s->started = 0; + FN_OUT(0); + return; +} +/*************************************************************************************** + * + * Clear any pending transfers + * + **************************************************************************************/ +void omap_clear_sound_dma(struct audio_stream * s) +{ + FN_IN; + omap_clear_dma(s->lch[s->dma_q_head]); + FN_OUT(0); + return; +} + +/*********************************** MODULE FUNCTIONS DEFINTIONS ***********************/ + +#ifdef OMAP1610_MCBSP1_BASE +#undef OMAP1610_MCBSP1_BASE +#endif +#define OMAP1610_MCBSP1_BASE 0xE1011000 + +/*************************************************************************************** + * + * DMA related functions + * + **************************************************************************************/ +static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr, + u_int dma_size) +{ + int dt = 0x1; /* data type 16 */ + int cen = 32; /* Stereo */ + int cfn = dma_size / (2 * cen); + FN_IN; + omap_set_dma_dest_params(channel, 0x05, 0x00, + (OMAP1610_MCBSP1_BASE + 0x806), + 0, 0); + omap_set_dma_src_params(channel, 0x00, 0x01, dma_ptr, + 0, 0); + omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0); + FN_OUT(0); + return 0; +} + +static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr, + u_int dma_size) +{ + int dt = 0x1; /* data type 16 */ + int cen = 32; /* stereo */ + + int cfn = dma_size / (2 * cen); + FN_IN; + omap_set_dma_src_params(channel, 0x05, 0x00, + (OMAP1610_MCBSP1_BASE + 0x802), + 0, 0); + omap_set_dma_dest_params(channel, 0x00, 0x01, dma_ptr, 0, 0); + omap_set_dma_transfer_params(channel, dt, cen, cfn, 0x00, 0, 0); + FN_OUT(0); + return 0; +} + +static int audio_start_dma_chain(struct audio_stream *s) +{ + int channel = s->lch[s->dma_q_head]; + FN_IN; + if (!s->started) { + s->hw_stop(); /* stops McBSP Interface */ + omap_start_dma(channel); + s->started = 1; + s->hw_start(); /* start McBSP interface */ + } + /* else the dma itself will progress forward with out our help */ + FN_OUT(0); + return 0; +} + +/* Start DMA - + * Do the initial set of work to initialize all the channels as required. + * We shall then initate a transfer + */ +int omap_start_sound_dma(struct audio_stream *s, dma_addr_t dma_ptr, + u_int dma_size) +{ + int ret = -EPERM; + + FN_IN; + + if (unlikely(dma_size > MAX_DMA_SIZE)) { + ERR("DmaSoundDma: Start: overflowed %d-%d\n", dma_size, + MAX_DMA_SIZE); + return -EOVERFLOW; + } + //if (AUDIO_QUEUE_FULL(s)) { + // ret = -2; + // goto sound_out; + //} + + if (s->stream_id == SNDRV_PCM_STREAM_PLAYBACK) { + /*playback */ + ret = + audio_set_dma_params_play(s->lch[s->dma_q_tail], + dma_ptr, dma_size); + } else { + ret = + audio_set_dma_params_capture(s->lch[s->dma_q_tail], + dma_ptr, dma_size); + } + if (ret != 0) { + ret = -3; /* indicate queue full */ + goto sound_out; + } + AUDIO_INCREMENT_TAIL(s); + ret = audio_start_dma_chain(s); + if (ret) { + ERR("dma start failed"); + } + sound_out: + FN_OUT(ret); + return ret; + +} + +/* + * ISRs have to be short and smart.. + * Here we call alsa handling, after some error checking + */ +static void sound_dma_irq_handler(int sound_curr_lch, u16 ch_status, + void *data) +{ + int dma_status = ch_status; + struct audio_stream *s = (struct audio_stream *) data; + FN_IN; + + /* + * some register checkings + */ + DPRINTK("lch=%d,status=0x%x, dma_status=%d, data=%p\n", + sound_curr_lch, ch_status, dma_status, data); + + if (dma_status & (DCSR_ERROR)) { + OMAP_DMA_CCR_REG(sound_curr_lch) &= ~DCCR_EN; + ERR("DCSR_ERROR!\n"); + FN_OUT(-1); + return; + } + + if (ch_status & DCSR_END_BLOCK) + audio_dma_callback(s); + FN_OUT(0); + return; +} + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION + ("Common DMA handling for Audio driver on OMAP processors"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(omap_start_sound_dma); +EXPORT_SYMBOL(omap_clear_sound_dma); +EXPORT_SYMBOL(omap_request_sound_dma); +EXPORT_SYMBOL(omap_free_sound_dma); +EXPORT_SYMBOL(omap_audio_stop_dma); diff --git a/sound/arm/omap-alsa-dma.h b/sound/arm/omap-alsa-dma.h new file mode 100644 index 00000000000..2ac7650abe3 --- /dev/null +++ b/sound/arm/omap-alsa-dma.h @@ -0,0 +1,59 @@ +/* + * linux/sound/arm/omap-alsa-dma.h + * + * Common audio DMA handling for the OMAP processors + * + * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * Copyright (C) 2000, 2001 Nicolas Pitre + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * + * + * 2004/08/12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms + * + * 2005/07/25 INdT Kernel Team - Renamed to omap-alsa-dma.h. Ported to Alsa. + */ + +#ifndef __OMAP_AUDIO_ALSA_DMA_H +#define __OMAP_AUDIO_ALSA_DMA_H + +/************************** INCLUDES *************************************/ + +#include "omap-aic23.h" + +/************************** GLOBAL MACROS *************************************/ + +/* Provide the Macro interfaces common across platforms */ +#define DMA_REQUEST(e,s, cb) {e=omap_request_sound_dma(s->dma_dev, s->id, s, &s->lch);} +#define DMA_FREE(s) omap_free_sound_dma(s, &s->lch) +#define DMA_CLEAR(s) omap_clear_sound_dma(s) + +/************************** GLOBAL DATA STRUCTURES *********************************/ + +typedef void (*dma_callback_t) (int lch, u16 ch_status, void *data); + +/**************** ARCH SPECIFIC FUNCIONS *******************************************/ + +void omap_clear_sound_dma(struct audio_stream * s); + +int omap_request_sound_dma(int device_id, const char *device_name, + void *data, int **channels); +int omap_free_sound_dma(void *data, int **channels); + +int omap_start_sound_dma(struct audio_stream *s, dma_addr_t dma_ptr, + u_int dma_size); + +void omap_audio_stop_dma(struct audio_stream *s); + +#endif diff --git a/sound/arm/omap-alsa-mixer.c b/sound/arm/omap-alsa-mixer.c new file mode 100644 index 00000000000..742e5b627c4 --- /dev/null +++ b/sound/arm/omap-alsa-mixer.c @@ -0,0 +1,496 @@ +/* + * sound/arm/omap-alsa-mixer.c + * + * Alsa Driver Mixer for generic codecs for omap boards + * + * Copyright (C) 2005 Instituto Nokia de Tecnologia - INdT - Manaus Brazil + * Written by David Cohen, Daniel Petrini + * {david.cohen, daniel.petrini}@indt.org.br + * + * Based on es1688_lib.c, + * Copyright (c) by Jaroslav Kysela + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: + * + * 2005-08-02 INdT Kernel Team - Alsa mixer driver for omap osk. Creation of new + * file omap-alsa-mixer.c. Initial version + * with aic23 codec for osk5912 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "omap-aic23.h" +#include +#include + +MODULE_AUTHOR("David Cohen, Daniel Petrini - INdT"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("OMAP Alsa mixer driver for ALSA"); + +/* + * Codec dependent region + */ + +/* Codec AIC23 */ +#if defined(CONFIG_SENSORS_TLV320AIC23) || defined (CONFIG_SENSORS_TLV320AIC23_MODULE) + +extern __inline__ void audio_aic23_write(u8, u16); + +#define MIXER_NAME "Mixer AIC23" +#define SND_OMAP_WRITE(reg, val) audio_aic23_write(reg, val) + +#endif + +/* Callback Functions */ +#define OMAP_BOOL(xname, xindex, reg, reg_index, mask, invert) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_omap_info_bool, \ + .get = snd_omap_get_bool, \ + .put = snd_omap_put_bool, \ + .private_value = reg | (reg_index << 8) | (invert << 10) | (mask << 12) \ +} + +#define OMAP_MUX(xname, reg, reg_index, mask) \ +{ \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .info = snd_omap_info_mux, \ + .get = snd_omap_get_mux, \ + .put = snd_omap_put_mux, \ + .private_value = reg | (reg_index << 8) | (mask << 10) \ +} + +#define OMAP_SINGLE(xname, xindex, reg, reg_index, reg_val, mask) \ +{\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_omap_info_single, \ + .get = snd_omap_get_single, \ + .put = snd_omap_put_single, \ + .private_value = reg | (reg_val << 8) | (reg_index << 16) | (mask << 18) \ +} + +#define OMAP_DOUBLE(xname, xindex, left_reg, right_reg, reg_index, mask) \ +{\ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .index = xindex, \ + .info = snd_omap_info_double, \ + .get = snd_omap_get_double, \ + .put = snd_omap_put_double, \ + .private_value = left_reg | (right_reg << 8) | (reg_index << 16) | (mask << 18) \ +} + +/* Local Registers */ +enum snd_device_index { + PCM_INDEX = 0, + LINE_INDEX, + AAC_INDEX, /* Analog Audio Control: reg = l_reg */ +}; + +struct { + u16 l_reg; + u16 r_reg; + u8 sw; +} omap_regs[3]; + +#ifdef CONFIG_PM +struct { + u16 l_reg; + u16 r_reg; + u8 sw; +} omap_pm_regs[3]; +#endif + +u16 snd_sidetone[6] = { + SIDETONE_18, + SIDETONE_12, + SIDETONE_9, + SIDETONE_6, + SIDETONE_0, + 0 +}; + +/* Begin Bool Functions */ + +static int snd_omap_info_bool(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +static int snd_omap_get_bool(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + int mic_index = (kcontrol->private_value >> 8) & 0x03; + u16 mask = (kcontrol->private_value >> 12) & 0xff; + int invert = (kcontrol->private_value >> 10) & 0x03; + + if (invert) + ucontrol->value.integer.value[0] = (omap_regs[mic_index].l_reg & mask) ? 0 : 1; + else + ucontrol->value.integer.value[0] = (omap_regs[mic_index].l_reg & mask) ? 1 : 0; + + return 0; +} + +static int snd_omap_put_bool(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + int mic_index = (kcontrol->private_value >> 8) & 0x03; + u16 mask = (kcontrol->private_value >> 12) & 0xff; + u16 reg = kcontrol->private_value & 0xff; + int invert = (kcontrol->private_value >> 10) & 0x03; + + int changed = 1; + + if (ucontrol->value.integer.value[0]) /* XOR */ + if (invert) + omap_regs[mic_index].l_reg &= ~mask; + else + omap_regs[mic_index].l_reg |= mask; + else + if (invert) + omap_regs[mic_index].l_reg |= mask; + else + omap_regs[mic_index].l_reg &= ~mask; + + SND_OMAP_WRITE(reg, omap_regs[mic_index].l_reg); + + return changed; +} + +/* End Bool Functions */ + +/* Begin Mux Functions */ + +static int snd_omap_info_mux(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + /* Mic = 0 + * Line = 1 */ + static char *texts[2] = { "Mic", "Line" }; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = 2; + + if (uinfo->value.enumerated.item > 1) + uinfo->value.enumerated.item = 1; + + strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); + + return 0; +} + +static int snd_omap_get_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + u16 mask = (kcontrol->private_value >> 10) & 0xff; + int mux_index = (kcontrol->private_value >> 8) & 0x03; + + ucontrol->value.enumerated.item[0] = (omap_regs[mux_index].l_reg & mask) ? 0 /* Mic */ : 1 /* Line */; + + return 0; +} + +static int snd_omap_put_mux(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + u16 reg = kcontrol->private_value & 0xff; + u16 mask = (kcontrol->private_value >> 10) & 0xff; + int mux_index = (kcontrol->private_value >> 8) & 0x03; + + int changed = 1; + + if (!ucontrol->value.integer.value[0]) + omap_regs[mux_index].l_reg |= mask; /* AIC23: Mic */ + else + omap_regs[mux_index].l_reg &= ~mask; /* AIC23: Line */ + + SND_OMAP_WRITE(reg, omap_regs[mux_index].l_reg); + + return changed; +} + +/* End Mux Functions */ + +/* Begin Single Functions */ + +static int snd_omap_info_single(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + int mask = (kcontrol->private_value >> 18) & 0xff; + int reg_val = (kcontrol->private_value >> 8) & 0xff; + + uinfo->type = mask ? SNDRV_CTL_ELEM_TYPE_INTEGER : SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = reg_val-1; + + return 0; +} + +static int snd_omap_get_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + u16 reg_val = (kcontrol->private_value >> 8) & 0xff; + + ucontrol->value.integer.value[0] = snd_sidetone[reg_val]; + + return 0; +} + +static int snd_omap_put_single(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + u16 reg_index = (kcontrol->private_value >> 16) & 0x03; + u16 mask = (kcontrol->private_value >> 18) & 0x1ff; + u16 reg = kcontrol->private_value & 0xff; + u16 reg_val = (kcontrol->private_value >> 8) & 0xff; + + int changed = 0; + + /* Volume */ + if ((omap_regs[reg_index].l_reg != (ucontrol->value.integer.value[0] & mask))) + { + changed = 1; + + omap_regs[reg_index].l_reg &= ~mask; + omap_regs[reg_index].l_reg |= snd_sidetone[ucontrol->value.integer.value[0]]; + + snd_sidetone[reg_val] = ucontrol->value.integer.value[0]; + SND_OMAP_WRITE(reg, omap_regs[reg_index].l_reg); + } + else + changed = 0; + + return changed; +} + +/* End Single Functions */ + +/* Begin Double Functions */ + +static int snd_omap_info_double(snd_kcontrol_t *kcontrol, snd_ctl_elem_info_t * uinfo) +{ + /* mask == 0 : Switch + * mask != 0 : Volume */ + int mask = (kcontrol->private_value >> 18) & 0xff; + + uinfo->type = mask ? SNDRV_CTL_ELEM_TYPE_INTEGER : SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = mask ? 2 : 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask ? mask : 1; + + return 0; +} + +static int snd_omap_get_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + /* mask == 0 : Switch + * mask != 0 : Volume */ + int mask = (kcontrol->private_value >> 18) & 0xff; + int vol_index = (kcontrol->private_value >> 16) & 0x03; + + if (!mask) + /* Switch */ + ucontrol->value.integer.value[0] = omap_regs[vol_index].sw; + else + { + /* Volume */ + ucontrol->value.integer.value[0] = omap_regs[vol_index].l_reg; + ucontrol->value.integer.value[1] = omap_regs[vol_index].r_reg; + } + + return 0; +} + +static int snd_omap_put_double(snd_kcontrol_t * kcontrol, snd_ctl_elem_value_t * ucontrol) +{ + /* mask == 0 : Switch + * mask != 0 : Volume */ + int vol_index = (kcontrol->private_value >> 16) & 0x03; + int mask = (kcontrol->private_value >> 18) & 0xff; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + + int changed = 0; + + if (!mask) + { + /* Switch */ + if (!ucontrol->value.integer.value[0]) + { + SND_OMAP_WRITE(left_reg, 0x00); + SND_OMAP_WRITE(right_reg, 0x00); + } + else + { + SND_OMAP_WRITE(left_reg, omap_regs[vol_index].l_reg); + SND_OMAP_WRITE(right_reg, omap_regs[vol_index].r_reg); + } + changed = 1; + omap_regs[vol_index].sw = ucontrol->value.integer.value[0]; + } + else + { + /* Volume */ + if ((omap_regs[vol_index].l_reg != (ucontrol->value.integer.value[0] & mask)) || + (omap_regs[vol_index].r_reg != (ucontrol->value.integer.value[1] & mask))) + { + changed = 1; + + omap_regs[vol_index].l_reg &= ~mask; + omap_regs[vol_index].r_reg &= ~mask; + omap_regs[vol_index].l_reg |= (ucontrol->value.integer.value[0] & mask); + omap_regs[vol_index].r_reg |= (ucontrol->value.integer.value[1] & mask); + if (omap_regs[vol_index].sw) + { + /* write to registers only if sw is actived */ + SND_OMAP_WRITE(left_reg, omap_regs[vol_index].l_reg); + SND_OMAP_WRITE(right_reg, omap_regs[vol_index].r_reg); + } + } + else + changed = 0; + } + + return changed; +} + +/* End Double Functions */ + +static snd_kcontrol_new_t snd_omap_controls[] = { + OMAP_DOUBLE("PCM Playback Switch", 0, LEFT_CHANNEL_VOLUME_ADDR, RIGHT_CHANNEL_VOLUME_ADDR, + PCM_INDEX, 0x00), + OMAP_DOUBLE("PCM Playback Volume", 0, LEFT_CHANNEL_VOLUME_ADDR, RIGHT_CHANNEL_VOLUME_ADDR, + PCM_INDEX, OUTPUT_VOLUME_MASK), + OMAP_BOOL("Line Playback Switch", 0, ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, BYPASS_ON, 0), + OMAP_DOUBLE("Line Capture Switch", 0, LEFT_LINE_VOLUME_ADDR, RIGHT_LINE_VOLUME_ADDR, + LINE_INDEX, 0x00), + OMAP_DOUBLE("Line Capture Volume", 0, LEFT_LINE_VOLUME_ADDR, RIGHT_LINE_VOLUME_ADDR, + LINE_INDEX, INPUT_VOLUME_MASK), + OMAP_BOOL("Mic Playback Switch", 0, ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, STE_ENABLED, 0), + OMAP_SINGLE("Mic Playback Volume", 0, ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, 5, SIDETONE_MASK), + OMAP_BOOL("Mic Capture Switch", 0, ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, MICM_MUTED, 1), + OMAP_BOOL("Mic Booster Playback Switch", 0, ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, MICB_20DB, 0), + OMAP_MUX("Capture Source", ANALOG_AUDIO_CONTROL_ADDR, AAC_INDEX, INSEL_MIC), +}; + +void snd_omap_init_mixer(void) +{ + u16 vol_reg; + + /* Line's default values */ + omap_regs[LINE_INDEX].l_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK; + omap_regs[LINE_INDEX].r_reg = DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK; + omap_regs[LINE_INDEX].sw = 0; + SND_OMAP_WRITE(LEFT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK); + SND_OMAP_WRITE(RIGHT_LINE_VOLUME_ADDR, DEFAULT_INPUT_VOLUME & INPUT_VOLUME_MASK); + + /* Analog Audio Control's default values */ + omap_regs[AAC_INDEX].l_reg = DEFAULT_ANALOG_AUDIO_CONTROL; + + /* Headphone's default values */ + vol_reg = LZC_ON; + vol_reg &= ~OUTPUT_VOLUME_MASK; + vol_reg |= DEFAULT_OUTPUT_VOLUME; + omap_regs[PCM_INDEX].l_reg = DEFAULT_OUTPUT_VOLUME; + omap_regs[PCM_INDEX].r_reg = DEFAULT_OUTPUT_VOLUME; + omap_regs[PCM_INDEX].sw = 1; + SND_OMAP_WRITE(LEFT_CHANNEL_VOLUME_ADDR, vol_reg); + SND_OMAP_WRITE(RIGHT_CHANNEL_VOLUME_ADDR, vol_reg); +} + +#ifdef CONFIG_PM + +void snd_omap_suspend_mixer(void) +{ + /* Saves current values to wake-up correctly */ + omap_pm_regs[LINE_INDEX].l_reg = omap_regs[LINE_INDEX].l_reg; + omap_pm_regs[LINE_INDEX].r_reg = omap_regs[LINE_INDEX].l_reg; + omap_pm_regs[LINE_INDEX].sw = omap_regs[LINE_INDEX].sw; + + omap_pm_regs[AAC_INDEX].l_reg = omap_regs[AAC_INDEX].l_reg; + + omap_pm_regs[PCM_INDEX].l_reg = omap_regs[PCM_INDEX].l_reg; + omap_pm_regs[PCM_INDEX].r_reg = omap_regs[PCM_INDEX].r_reg; + omap_pm_regs[PCM_INDEX].sw = omap_regs[PCM_INDEX].sw; +} + +void snd_omap_resume_mixer(void) +{ + /* Line's saved values */ + omap_regs[LINE_INDEX].l_reg = omap_pm_regs[LINE_INDEX].l_reg; + omap_regs[LINE_INDEX].r_reg = omap_pm_regs[LINE_INDEX].l_reg; + omap_regs[LINE_INDEX].sw = omap_pm_regs[LINE_INDEX].sw; + SND_OMAP_WRITE(LEFT_LINE_VOLUME_ADDR, omap_pm_regs[LINE_INDEX].l_reg); + SND_OMAP_WRITE(RIGHT_LINE_VOLUME_ADDR, omap_pm_regs[LINE_INDEX].l_reg); + + /* Analog Audio Control's saved values */ + omap_regs[AAC_INDEX].l_reg = omap_pm_regs[AAC_INDEX].l_reg; + SND_OMAP_WRITE(ANALOG_AUDIO_CONTROL_ADDR, omap_regs[AAC_INDEX].l_reg); + + /* Headphone's saved values */ + omap_regs[PCM_INDEX].l_reg = omap_pm_regs[PCM_INDEX].l_reg; + omap_regs[PCM_INDEX].r_reg = omap_pm_regs[PCM_INDEX].r_reg; + omap_regs[PCM_INDEX].sw = omap_pm_regs[PCM_INDEX].sw; + SND_OMAP_WRITE(LEFT_CHANNEL_VOLUME_ADDR, omap_pm_regs[PCM_INDEX].l_reg); + SND_OMAP_WRITE(RIGHT_CHANNEL_VOLUME_ADDR, omap_pm_regs[PCM_INDEX].r_reg); +} +#endif + +int snd_omap_mixer(struct snd_card_omap_aic23 *chip) +{ + snd_card_t *card; + unsigned int idx; + int err; + + snd_assert(chip != NULL && chip->card != NULL, return -EINVAL); + + card = chip->card; + + strcpy(card->mixername, MIXER_NAME); + + /* Registering alsa mixer controls */ + for (idx = 0; idx < ARRAY_SIZE(snd_omap_controls); idx++) + if ((err = snd_ctl_add(card, snd_ctl_new1(&snd_omap_controls[idx], chip))) < 0) + return err; + + return 0; +} + diff --git a/sound/oss/Kconfig b/sound/oss/Kconfig index 88e52dc84c0..448f50f54e7 100644 --- a/sound/oss/Kconfig +++ b/sound/oss/Kconfig @@ -5,6 +5,35 @@ # # Prompt user for primary drivers. +config SOUND_OMAP + tristate "OMAP Sound Driver" + depends on SOUND_PRIME!=n && SOUND && ARCH_OMAP + ---help--- + OMAP Audio driver + +config SOUND_OMAP_TSC2101 + tristate "TSC2101 Stereo Codec" + depends on SOUND_OMAP && ( MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4 || MACH_OMAP_APOLLON) + select OMAP_TSC2101 if ( MACH_OMAP_H2 || MACH_OMAP_H3 ) + select OMAP_UWIRE if ARCH_OMAP1 + ---help--- + Tsc2101 Audio Codec Driver for OMAP will be enabled. + Will also Enable the following: + case OMAP1: + 1. uWire Driver based on Platform + 2. TSC2101 Glue driver + case OMAP2: + 1. McSPI Driver based on Platform + 2. TSC2101 Glue driver + +config SOUND_OMAP_AIC23 + tristate "AIC23 Stereo Codec" + depends on SOUND_OMAP && ( MACH_OMAP_INNOVATOR || MACH_OMAP_OSK ) + select SENSORS_TLV320AIC23 if ARCH_OMAP + ---help--- + AIC23 Audio Codec Driver for OMAP will be enabled. + Additionally, AIC23 I2C support is enabled. + config OBSOLETE_OSS_DRIVER bool "Obsolete OSS drivers" depends on SOUND_PRIME diff --git a/sound/oss/Makefile b/sound/oss/Makefile index 9bf3ee544d8..17ca39dfe00 100644 --- a/sound/oss/Makefile +++ b/sound/oss/Makefile @@ -8,6 +8,10 @@ obj-$(CONFIG_SOUND_OSS) += sound.o obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o +obj-$(CONFIG_SOUND_OMAP) += omap-audio-dma-intfc.o omap-audio.o +obj-$(CONFIG_SOUND_OMAP_TSC2101)+= omap-audio-tsc2101.o +obj-$(CONFIG_SOUND_OMAP_AIC23) += omap-audio-aic23.o + # Please leave it as is, cause the link order is significant ! obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o diff --git a/sound/oss/omap-audio-aic23.c b/sound/oss/omap-audio-aic23.c new file mode 100644 index 00000000000..35bd2b3e782 --- /dev/null +++ b/sound/oss/omap-audio-aic23.c @@ -0,0 +1,752 @@ +/* + * linux/sound/oss/omap-audio-aic23.c + * + * Glue audio driver for TI TLV320AIC23 codec + * + * Copyright (c) 2000 Nicolas Pitre + * Copyright (C) 2001, Steve Johnson + * Copyright (C) 2004 Texas Instruments, Inc. + * Copyright (C) 2005 Dirk Behme + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "omap-audio.h" +#include "omap-audio-dma-intfc.h" + +#ifdef CONFIG_PROC_FS +#include +#define PROC_START_FILE "driver/aic23-audio-start" +#define PROC_STOP_FILE "driver/aic23-audio-stop" +#endif + +//#define DEBUG + +#ifdef DEBUG +#define DPRINTK(ARGS...) printk("<%s>: ",__FUNCTION__);printk(ARGS) +#else +#define DPRINTK( x... ) +#endif + +#define CODEC_NAME "AIC23" + +#if CONFIG_MACH_OMAP_OSK +#define PLATFORM_NAME "OMAP OSK" +#elif CONFIG_MACH_OMAP_INNOVATOR +#define PLATFORM_NAME "OMAP INNOVATOR" +#else +#error "Unsupported plattform" +#endif + +/* Define to set the AIC23 as the master w.r.t McBSP */ +#define AIC23_MASTER + +#define CODEC_CLOCK 12000000 + +/* + * AUDIO related MACROS + */ +#define DEFAULT_BITPERSAMPLE 16 +#define AUDIO_RATE_DEFAULT 44100 + +/* Select the McBSP For Audio */ +#define AUDIO_MCBSP OMAP_MCBSP1 + +#define REC_MASK (SOUND_MASK_LINE | SOUND_MASK_MIC) +#define DEV_MASK (REC_MASK | SOUND_MASK_VOLUME) + +#define SET_VOLUME 1 +#define SET_LINE 2 + +#define DEFAULT_OUTPUT_VOLUME 93 +#define DEFAULT_INPUT_VOLUME 0 /* 0 ==> mute line in */ + +#define OUTPUT_VOLUME_MIN LHV_MIN +#define OUTPUT_VOLUME_MAX LHV_MAX +#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MAX - OUTPUT_VOLUME_MIN) +#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MAX + +#define INPUT_VOLUME_MIN LIV_MIN +#define INPUT_VOLUME_MAX LIV_MAX +#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN) +#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX + +#define NUMBER_SAMPLE_RATES_SUPPORTED 9 + +/* + * HW interface start and stop helper functions + */ +static int audio_ifc_start(void) +{ + omap_mcbsp_start(AUDIO_MCBSP); + return 0; +} + +static int audio_ifc_stop(void) +{ + omap_mcbsp_stop(AUDIO_MCBSP); + return 0; +} + +static audio_stream_t output_stream = { + .id = "AIC23 out", + .dma_dev = OMAP_DMA_MCBSP1_TX, + .input_or_output = FMODE_WRITE, + .hw_start = audio_ifc_start, + .hw_stop = audio_ifc_stop +}; + +static audio_stream_t input_stream = { + .id = "AIC23 in", + .dma_dev = OMAP_DMA_MCBSP1_RX, + .input_or_output = FMODE_READ, + .hw_start = audio_ifc_start, + .hw_stop = audio_ifc_stop +}; + +static struct clk *aic23_mclk = 0; + +static int audio_dev_id, mixer_dev_id; + +static struct aic23_local_info { + u8 volume; + u16 volume_reg; + u8 line; + u8 mic; + u16 input_volume_reg; + int mod_cnt; +} aic23_local; + +struct sample_rate_reg_info { + u32 sample_rate; + u8 control; /* SR3, SR2, SR1, SR0 and BOSR */ + u8 divider; /* if 0 CLKIN = MCLK, if 1 CLKIN = MCLK/2 */ +}; + +/* To Store the default sample rate */ +static long audio_samplerate = AUDIO_RATE_DEFAULT; + +/* DAC USB-mode sampling rates (MCLK = 12 MHz) */ +static const struct sample_rate_reg_info +reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = { + {96000, 0x0E, 0}, + {88200, 0x1F, 0}, + {48000, 0x00, 0}, + {44100, 0x11, 0}, + {32000, 0x0C, 0}, + {24000, 0x00, 1}, + {16000, 0x0C, 1}, + { 8000, 0x06, 0}, + { 4000, 0x06, 1}, +}; + +static struct omap_mcbsp_reg_cfg initial_config = { + .spcr2 = FREE | FRST | GRST | XRST | XINTM(3), + .spcr1 = RINTM(3) | RRST, + .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) | + RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(0), + .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16), + .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) | + XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(0) | XFIG, + .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16), + .srgr1 = FWID(DEFAULT_BITPERSAMPLE - 1), + .srgr2 = GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1), +#ifndef AIC23_MASTER + /* configure McBSP to be the I2S master */ + .pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP, +#else + /* configure McBSP to be the I2S slave */ + .pcr0 = CLKXP | CLKRP, +#endif /* AIC23_MASTER */ +}; + +static void omap_aic23_initialize(void *dummy); +static void omap_aic23_shutdown(void *dummy); +static int omap_aic23_ioctl(struct inode *inode, struct file *file, + uint cmd, ulong arg); +static int omap_aic23_probe(void); +#ifdef MODULE +static void omap_aic23_remove(void); +#endif +static int omap_aic23_suspend(void); +static int omap_aic23_resume(void); +static inline void aic23_configure(void); +static int mixer_open(struct inode *inode, struct file *file); +static int mixer_release(struct inode *inode, struct file *file); +static int mixer_ioctl(struct inode *inode, struct file *file, uint cmd, + ulong arg); + +#ifdef CONFIG_PROC_FS +static int codec_start(char *buf, char **start, off_t offset, int count, + int *eof, void *data); +static int codec_stop(char *buf, char **start, off_t offset, int count, + int *eof, void *data); +#endif + + +/* File Op structure for mixer */ +static struct file_operations omap_mixer_fops = { + .open = mixer_open, + .release = mixer_release, + .ioctl = mixer_ioctl, + .owner = THIS_MODULE +}; + +/* To store characteristic info regarding the codec for the audio driver */ +static audio_state_t aic23_state = { + .output_stream = &output_stream, + .input_stream = &input_stream, +/* .need_tx_for_rx = 1, //Once the Full Duplex works */ + .need_tx_for_rx = 0, + .hw_init = omap_aic23_initialize, + .hw_shutdown = omap_aic23_shutdown, + .client_ioctl = omap_aic23_ioctl, + .hw_probe = omap_aic23_probe, + .hw_remove = __exit_p(omap_aic23_remove), + .hw_suspend = omap_aic23_suspend, + .hw_resume = omap_aic23_resume, + .sem = __SEMAPHORE_INIT(aic23_state.sem, 1), +}; + +/* This will be defined in the audio.h */ +static struct file_operations *omap_audio_fops; + +extern int tlv320aic23_write_value(u8 reg, u16 value); + +/* TLV320AIC23 is a write only device */ +static __inline__ void audio_aic23_write(u8 address, u16 data) +{ + tlv320aic23_write_value(address, data); +} + +static int aic23_update(int flag, int val) +{ + u16 volume; + + /* Ignore separate left/right channel for now, + even the codec does support it. */ + val &= 0xff; + + if (val < 0 || val > 100) { + printk(KERN_ERR "Trying a bad volume value(%d)!\n",val); + return -EPERM; + } + + switch (flag) { + case SET_VOLUME: + // Convert 0 -> 100 volume to 0x00 (LHV_MIN) -> 0x7f (LHV_MAX) + // volume range + volume = ((val * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MIN; + + // R/LHV[6:0] 1111111 (+6dB) to 0000000 (-73dB) in 1db steps, + // default 1111001 (0dB) + aic23_local.volume_reg &= ~OUTPUT_VOLUME_MASK; + aic23_local.volume_reg |= volume; + audio_aic23_write(LEFT_CHANNEL_VOLUME_ADDR, aic23_local.volume_reg); + audio_aic23_write(RIGHT_CHANNEL_VOLUME_ADDR, aic23_local.volume_reg); + break; + + case SET_LINE: + // Convert 0 -> 100 volume to 0x0 (LIV_MIN) -> 0x1f (LIV_MAX) + // volume range + volume = ((val * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN; + + // R/LIV[4:0] 11111 (+12dB) to 00000 (-34.5dB) in 1.5dB steps, + // default 10111 (0dB) + aic23_local.input_volume_reg &= ~INPUT_VOLUME_MASK; + aic23_local.input_volume_reg |= volume; + audio_aic23_write(LEFT_LINE_VOLUME_ADDR, aic23_local.input_volume_reg); + audio_aic23_write(RIGHT_LINE_VOLUME_ADDR, aic23_local.input_volume_reg); + break; + } + return 0; +} + +static int mixer_open(struct inode *inode, struct file *file) +{ + /* Any mixer specific initialization */ + + return 0; +} + +static int mixer_release(struct inode *inode, struct file *file) +{ + /* Any mixer specific Un-initialization */ + + return 0; +} + +static int +mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + int val; + int ret = 0; + int nr = _IOC_NR(cmd); + + /* + * We only accept mixer (type 'M') ioctls. + */ + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + DPRINTK(" 0x%08x\n", cmd); + + if (cmd == SOUND_MIXER_INFO) { + struct mixer_info mi; + + strncpy(mi.id, "AIC23", sizeof(mi.id)); + strncpy(mi.name, "TI AIC23", sizeof(mi.name)); + mi.modify_counter = aic23_local.mod_cnt; + return copy_to_user((void *)arg, &mi, sizeof(mi)); + } + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + ret = get_user(val, (int *)arg); + if (ret) + goto out; + + + switch (nr) { + case SOUND_MIXER_VOLUME: + aic23_local.volume = val; + aic23_local.mod_cnt++; + ret = aic23_update(SET_VOLUME, val); + break; + + case SOUND_MIXER_LINE: + aic23_local.line = val; + aic23_local.mod_cnt++; + ret = aic23_update(SET_LINE, val); + break; + + case SOUND_MIXER_MIC: + aic23_local.mic = val; + aic23_local.mod_cnt++; + ret = aic23_update(SET_LINE, val); + break; + + case SOUND_MIXER_RECSRC: + break; + + default: + ret = -EINVAL; + } + } + + if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { + ret = 0; + + switch (nr) { + case SOUND_MIXER_VOLUME: + val = aic23_local.volume; + break; + case SOUND_MIXER_LINE: + val = aic23_local.line; + break; + case SOUND_MIXER_MIC: + val = aic23_local.mic; + break; + case SOUND_MIXER_RECSRC: + val = REC_MASK; + break; + case SOUND_MIXER_RECMASK: + val = REC_MASK; + break; + case SOUND_MIXER_DEVMASK: + val = DEV_MASK; + break; + case SOUND_MIXER_CAPS: + val = 0; + break; + case SOUND_MIXER_STEREODEVS: + val = 0; + break; + default: + val = 0; + ret = -EINVAL; + break; + } + + if (ret == 0) + ret = put_user(val, (int *)arg); + } +out: + return ret; + +} + +int omap_set_samplerate(long sample_rate) +{ + u8 count = 0; + u16 data = 0; + /* wait for any frame to complete */ + udelay(125); + + /* Search for the right sample rate */ + while ((reg_info[count].sample_rate != sample_rate) && + (count < NUMBER_SAMPLE_RATES_SUPPORTED)) { + count++; + } + if (count == NUMBER_SAMPLE_RATES_SUPPORTED) { + printk(KERN_ERR "Invalid Sample Rate %d requested\n", + (int)sample_rate); + return -EPERM; + } + + if (machine_is_omap_innovator()) { + /* set the CODEC clock input source to 12.000MHz */ + fpga_write(fpga_read(OMAP1510_FPGA_POWER) & ~0x01, + OMAP1510_FPGA_POWER); + } + + data = (reg_info[count].divider << CLKIN_SHIFT) | + (reg_info[count].control << BOSR_SHIFT) | USB_CLK_ON; + + audio_aic23_write(SAMPLE_RATE_CONTROL_ADDR, data); + + audio_samplerate = sample_rate; + +#ifndef AIC23_MASTER + { + int clkgdv = 0; + /* + Set Sample Rate at McBSP + + Formula : + Codec System Clock = CODEC_CLOCK, or half if clock_divider = 1; + clkgdv = ((Codec System Clock / (SampleRate * BitsPerSample * 2)) - 1); + + FWID = BitsPerSample - 1; + FPER = (BitsPerSample * 2) - 1; + */ + if (reg_info[count].divider) + clkgdv = CODEC_CLOCK / 2; + else + clkgdv = CODEC_CLOCK; + + clkgdv = (clkgdv / (sample_rate * DEFAULT_BITPERSAMPLE * 2)) - 1; + + initial_config.srgr1 = (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv)); + + initial_config.srgr2 = + (CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1)); + + omap_mcbsp_config(AUDIO_MCBSP, &initial_config); + } +#endif /* AIC23_MASTER */ + + return 0; +} + +static void omap_aic23_initialize(void *dummy) +{ + DPRINTK("entry\n"); + + /* initialize with default sample rate */ + audio_samplerate = AUDIO_RATE_DEFAULT; + + omap_mcbsp_request(AUDIO_MCBSP); + + /* if configured, then stop mcbsp */ + omap_mcbsp_stop(AUDIO_MCBSP); + + omap_mcbsp_config(AUDIO_MCBSP, &initial_config); + omap_mcbsp_start(AUDIO_MCBSP); + aic23_configure(); + + DPRINTK("exit\n"); +} + +static void omap_aic23_shutdown(void *dummy) +{ + /* + Turn off codec after it is done. + Can't do it immediately, since it may still have + buffered data. + + Wait 20ms (arbitrary value) and then turn it off. + */ + + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(2); + + omap_mcbsp_stop(AUDIO_MCBSP); + omap_mcbsp_free(AUDIO_MCBSP); + + audio_aic23_write(RESET_CONTROL_ADDR, 0); + audio_aic23_write(POWER_DOWN_CONTROL_ADDR, 0xff); +} + +static inline void aic23_configure() +{ + /* Reset codec */ + audio_aic23_write(RESET_CONTROL_ADDR, 0); + + /* Initialize the AIC23 internal state */ + + /* Left/Right line input volume control */ + aic23_local.line = DEFAULT_INPUT_VOLUME; + aic23_local.mic = DEFAULT_INPUT_VOLUME; + aic23_update(SET_LINE, DEFAULT_INPUT_VOLUME); + + /* Left/Right headphone channel volume control */ + /* Zero-cross detect on */ + aic23_local.volume_reg = LZC_ON; + aic23_update(SET_VOLUME, aic23_local.volume); + + /* Analog audio path control, DAC selected, delete INSEL_MIC for line in */ + audio_aic23_write(ANALOG_AUDIO_CONTROL_ADDR, DAC_SELECTED | INSEL_MIC); + + /* Digital audio path control, de-emphasis control 44.1kHz */ + audio_aic23_write(DIGITAL_AUDIO_CONTROL_ADDR, DEEMP_44K); + + /* Power control, everything is on */ + audio_aic23_write(POWER_DOWN_CONTROL_ADDR, 0); + + /* Digital audio interface, master/slave mode, I2S, 16 bit */ +#ifdef AIC23_MASTER + audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, MS_MASTER | IWL_16 | FOR_DSP); +#else + audio_aic23_write(DIGITAL_AUDIO_FORMAT_ADDR, IWL_16 | FOR_DSP); +#endif /* AIC23_MASTER */ + + /* Enable digital interface */ + audio_aic23_write(DIGITAL_INTERFACE_ACT_ADDR, ACT_ON); + + /* clock configuration */ + omap_set_samplerate(audio_samplerate); +} + +static int +omap_aic23_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + long val; + int ret = 0; + + DPRINTK(" 0x%08x\n", cmd); + + /* + * These are platform dependent ioctls which are not handled by the + * generic omap-audio module. + */ + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *)arg); + if (ret) + return ret; + /* the AIC23 is stereo only */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *)arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* the AIC23 is stereo only */ + return put_user(2, (long *)arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *)arg); + if (ret) + break; + ret = omap_set_samplerate(val); + if (ret) + break; + /* fall through */ + + case SOUND_PCM_READ_RATE: + return put_user(audio_samplerate, (long *)arg); + + case SOUND_PCM_READ_BITS: + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* we can do 16-bit only */ + return put_user(AFMT_S16_LE, (long *)arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + + return ret; +} + +static int omap_aic23_probe(void) +{ + /* Get the fops from audio oss driver */ + if (!(omap_audio_fops = audio_get_fops())) { + printk(KERN_ERR "Unable to get the file operations for AIC23 OSS driver\n"); + audio_unregister_codec(&aic23_state); + return -EPERM; + } + + aic23_local.volume = DEFAULT_OUTPUT_VOLUME; + + /* register devices */ + audio_dev_id = register_sound_dsp(omap_audio_fops, -1); + mixer_dev_id = register_sound_mixer(&omap_mixer_fops, -1); + +#ifdef CONFIG_PROC_FS + create_proc_read_entry(PROC_START_FILE, 0 /* default mode */ , + NULL /* parent dir */ , + codec_start, NULL /* client data */ ); + + create_proc_read_entry(PROC_STOP_FILE, 0 /* default mode */ , + NULL /* parent dir */ , + codec_stop, NULL /* client data */ ); +#endif + + /* Announcement Time */ + printk(KERN_INFO PLATFORM_NAME " " CODEC_NAME + " audio support initialized\n"); + return 0; +} + +#ifdef MODULE +static void __exit omap_aic23_remove(void) +{ + /* Un-Register the codec with the audio driver */ + unregister_sound_dsp(audio_dev_id); + unregister_sound_mixer(mixer_dev_id); + +#ifdef CONFIG_PROC_FS + remove_proc_entry(PROC_START_FILE, NULL); + remove_proc_entry(PROC_STOP_FILE, NULL); +#endif +} +#endif /* MODULE */ + +static int omap_aic23_suspend(void) +{ + /* Empty for the moment */ + return 0; +} + +static int omap_aic23_resume(void) +{ + /* Empty for the moment */ + return 0; +} + +static int __init audio_aic23_init(void) +{ + + int err = 0; + + if (machine_is_omap_h2() || machine_is_omap_h3()) + return -ENODEV; + + if (machine_is_omap_osk()) { + /* Set MCLK to be clock input for AIC23 */ + aic23_mclk = clk_get(0, "mclk"); + + if(clk_get_rate( aic23_mclk) != CODEC_CLOCK){ + /* MCLK ist not at CODEC_CLOCK */ + if( clk_get_usecount(aic23_mclk) > 0 ){ + /* MCLK is already in use */ + printk(KERN_WARNING "MCLK in use at %d Hz. We change it to %d Hz\n", + (uint)clk_get_rate( aic23_mclk), CODEC_CLOCK); + } + if( clk_set_rate( aic23_mclk, CODEC_CLOCK ) ){ + printk(KERN_ERR "Cannot set MCLK for AIC23 CODEC\n"); + return -ECANCELED; + } + } + + clk_enable( aic23_mclk ); + + DPRINTK("MCLK = %d [%d], usecount = %d\n",(uint)clk_get_rate( aic23_mclk ), + CODEC_CLOCK, clk_get_usecount( aic23_mclk)); + } + + if (machine_is_omap_innovator()) { + u8 fpga; + /* + Turn on chip select for CODEC (shared with touchscreen). + Don't turn it back off, in case touch screen needs it. + */ + fpga = fpga_read(OMAP1510_FPGA_TOUCHSCREEN); + fpga |= 0x4; + fpga_write(fpga, OMAP1510_FPGA_TOUCHSCREEN); + } + + /* register the codec with the audio driver */ + if ((err = audio_register_codec(&aic23_state))) { + printk(KERN_ERR + "Failed to register AIC23 driver with Audio OSS Driver\n"); + } + + return err; +} + +static void __exit audio_aic23_exit(void) +{ + (void)audio_unregister_codec(&aic23_state); + return; +} + +#ifdef CONFIG_PROC_FS +static int codec_start(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + void *foo = NULL; + + omap_aic23_initialize(foo); + + printk("AIC23 codec initialization done.\n"); + return 0; +} +static int codec_stop(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + void *foo = NULL; + + omap_aic23_shutdown(foo); + + printk("AIC23 codec shutdown.\n"); + return 0; +} +#endif /* CONFIG_PROC_FS */ + +module_init(audio_aic23_init); +module_exit(audio_aic23_exit); + +MODULE_AUTHOR("Dirk Behme "); +MODULE_DESCRIPTION("Glue audio driver for the TI AIC23 codec."); +MODULE_LICENSE("GPL"); diff --git a/sound/oss/omap-audio-dma-intfc.c b/sound/oss/omap-audio-dma-intfc.c new file mode 100644 index 00000000000..7bcc3fa499d --- /dev/null +++ b/sound/oss/omap-audio-dma-intfc.c @@ -0,0 +1,987 @@ +/* + * linux/sound/oss/omap-audio-dma-intfc.c + * + * Common audio DMA handling for the OMAP processors + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * Copyright (C) 2000, 2001 Nicolas Pitre + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * + * 2004-06-07 Sriram Kannan - Created new file from omap_audio_dma_intfc.c. This file + * will contain only the DMA interface and buffer handling of OMAP + * audio driver. + * + * 2004-06-22 Sriram Kannan - removed legacy code (auto-init). Self-linking of DMA logical channel. + * + * 2004-08-12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms + * + * 2004-11-01 Nishanth Menon - 16xx platform code base modified to support multi channel chaining. + * + * 2004-12-15 Nishanth Menon - Improved 16xx platform channel logic introduced - tasklets, queue handling updated + * + * 2005-12-10 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include "omap-audio-dma-intfc.h" + +#include + +#include "omap-audio.h" + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS) +#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__) +#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n) +#else + +#define DPRINTK( x... ) +#define FN_IN +#define FN_OUT(x) +#endif + +#define ERR(ARGS...) printk(KERN_ERR "{%s}-ERROR: ", __FUNCTION__);printk(ARGS); + +#define AUDIO_NAME "omap-audio" +#define AUDIO_NBFRAGS_DEFAULT 8 +#define AUDIO_FRAGSIZE_DEFAULT 8192 + +#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref) + +#define SPIN_ADDR (dma_addr_t)0 +#define SPIN_SIZE 2048 + +/* Channel Queue Handling macros + * tail always points to the current free entry + * Head always points to the current entry being used + * end is either head or tail + */ + +#define AUDIO_QUEUE_INIT(s) s->dma_q_head = s->dma_q_tail = s->dma_q_count = 0; +#define AUDIO_QUEUE_FULL(s) (nr_linked_channels == s->dma_q_count) +#define AUDIO_QUEUE_LAST(s) (1 == s->dma_q_count) +#define AUDIO_QUEUE_EMPTY(s) (0 == s->dma_q_count) +#define __AUDIO_INCREMENT_QUEUE(end) ((end)=((end)+1) % nr_linked_channels) +#define AUDIO_INCREMENT_HEAD(s) __AUDIO_INCREMENT_QUEUE(s->dma_q_head); s->dma_q_count--; +#define AUDIO_INCREMENT_TAIL(s) __AUDIO_INCREMENT_QUEUE(s->dma_q_tail); s->dma_q_count++; + +/* DMA buffer fragmentation sizes */ +#define MAX_DMA_SIZE 0x1000000 +#define CUT_DMA_SIZE 0x1000 +/* TODO: To be moved to more appropriate location */ +#define DCSR_ERROR 0x3 +#define DCSR_SYNC_SET (1 << 6) + +#define DCCR_FS (1 << 5) +#define DCCR_PRIO (1 << 6) +#define DCCR_EN (1 << 7) +#define DCCR_AI (1 << 8) +#define DCCR_REPEAT (1 << 9) +/* if 0 the channel works in 3.1 compatible mode*/ +#define DCCR_N31COMP (1 << 10) +#define DCCR_EP (1 << 11) +#define DCCR_SRC_AMODE_BIT 12 +#define DCCR_SRC_AMODE_MASK (0x3<<12) +#define DCCR_DST_AMODE_BIT 14 +#define DCCR_DST_AMODE_MASK (0x3<<14) +#define AMODE_CONST 0x0 +#define AMODE_POST_INC 0x1 +#define AMODE_SINGLE_INDEX 0x2 +#define AMODE_DOUBLE_INDEX 0x3 + +/**************************** DATA STRUCTURES *****************************************/ + +static spinlock_t dma_list_lock = SPIN_LOCK_UNLOCKED; + +struct audio_isr_work_item { + int current_lch; + u16 ch_status; + audio_stream_t *s; +}; + +static char work_item_running = 0; +static char nr_linked_channels = 1; +static struct audio_isr_work_item work1, work2; + + +/*********************************** MODULE SPECIFIC FUNCTIONS PROTOTYPES *************/ + +static void audio_dsr_handler(unsigned long); +static DECLARE_TASKLET(audio_isr_work1, audio_dsr_handler, + (unsigned long)&work1); +static DECLARE_TASKLET(audio_isr_work2, audio_dsr_handler, + (unsigned long)&work2); + +static void sound_dma_irq_handler(int lch, u16 ch_status, void *data); +static void audio_dma_callback(int lch, u16 ch_status, void *data); +static int omap_start_sound_dma(audio_stream_t * s, dma_addr_t dma_ptr, + u_int size); +static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr, + u_int dma_size); +static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr, + u_int dma_size); +static int audio_start_dma_chain(audio_stream_t * s); + +/*********************************** GLOBAL FUNCTIONS DEFINTIONS ***********************/ + +/*************************************************************************************** + * + * Buffer creation/destruction + * + **************************************************************************************/ +int audio_setup_buf(audio_stream_t * s) +{ + int frag; + int dmasize = 0; + char *dmabuf = NULL; + dma_addr_t dmaphys = 0; + FN_IN; + if (s->buffers) { + FN_OUT(1); + return -EBUSY; + } + s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL); + if (!s->buffers) + goto err; + memset(s->buffers, 0, sizeof(audio_buf_t) * s->nbfrags); + for (frag = 0; frag < s->nbfrags; frag++) { + audio_buf_t *b = &s->buffers[frag]; + /* + * Let's allocate non-cached memory for DMA buffers. + * We try to allocate all memory at once. + * If this fails (a common reason is memory fragmentation), + * then we allocate more smaller buffers. + */ + if (!dmasize) { + dmasize = (s->nbfrags - frag) * s->fragsize; + do { + dmabuf = + dma_alloc_coherent(NULL, dmasize, &dmaphys, + 0); + if (!dmabuf) + dmasize -= s->fragsize; + } + while (!dmabuf && dmasize); + if (!dmabuf) + goto err; + b->master = dmasize; + memzero(dmabuf, dmasize); + } + b->data = dmabuf; + b->dma_addr = dmaphys; + dmabuf += s->fragsize; + dmaphys += s->fragsize; + dmasize -= s->fragsize; + } + s->usr_head = s->dma_head = s->dma_tail = 0; + AUDIO_QUEUE_INIT(s); + s->started = 0; + s->bytecount = 0; + s->fragcount = 0; + init_completion(&s->wfc); + s->wfc.done = s->nbfrags; + FN_OUT(0); + return 0; + err: + audio_discard_buf(s); + FN_OUT(1); + return -ENOMEM; +} + +void audio_discard_buf(audio_stream_t * s) +{ + FN_IN; + /* ensure DMA isn't using those buffers */ + audio_reset(s); + if (s->buffers) { + int frag; + for (frag = 0; frag < s->nbfrags; frag++) { + if (!s->buffers[frag].master) + continue; + dma_free_coherent(NULL, + s->buffers[frag].master, + s->buffers[frag].data, + s->buffers[frag].dma_addr); + } + kfree(s->buffers); + s->buffers = NULL; + } + FN_OUT(0); +} + +/*************************************************************************************** + * + * DMA channel requests + * + **************************************************************************************/ +static void omap_sound_dma_link_lch(void *data) +{ + audio_stream_t *s = (audio_stream_t *) data; + int *chan = s->lch; + int i; + + FN_IN; + if (s->linked) { + FN_OUT(1); + return; + } + for (i = 0; i < nr_linked_channels; i++) { + int cur_chan = chan[i]; + int nex_chan = + ((nr_linked_channels - 1 == + i) ? chan[0] : chan[i + 1]); + omap_dma_link_lch(cur_chan, nex_chan); + } + s->linked = 1; + FN_OUT(0); +} + +int +omap_request_sound_dma(int device_id, const char *device_name, void *data, + int **channels) +{ + int i, err = 0; + int *chan = NULL; + FN_IN; + if (unlikely((NULL == channels) || (NULL == device_name))) { + BUG(); + return -EPERM; + } + /* Try allocate memory for the num channels */ + *channels = + (int *)kmalloc(sizeof(int) * nr_linked_channels, + GFP_KERNEL); + chan = *channels; + if (NULL == chan) { + ERR("No Memory for channel allocs!\n"); + FN_OUT(-ENOMEM); + return -ENOMEM; + } + spin_lock(&dma_list_lock); + for (i = 0; i < nr_linked_channels; i++) { + err = + omap_request_dma(device_id, device_name, + sound_dma_irq_handler, data, &chan[i]); + /* Handle Failure condition here */ + if (err < 0) { + int j; + for (j = 0; j < i; j++) { + omap_free_dma(chan[j]); + } + spin_unlock(&dma_list_lock); + kfree(chan); + *channels = NULL; + ERR("Error in requesting channel %d=0x%x\n", i, err); + FN_OUT(err); + return err; + } + } + + /* Chain the channels together */ + if (!cpu_is_omap1510()) + omap_sound_dma_link_lch(data); + + spin_unlock(&dma_list_lock); + FN_OUT(0); + return 0; +} + +/*************************************************************************************** + * + * DMA channel requests Freeing + * + **************************************************************************************/ +static void omap_sound_dma_unlink_lch(void *data) +{ + audio_stream_t *s = (audio_stream_t *) data; + int *chan = s->lch; + int i; + + FN_IN; + if (!s->linked) { + FN_OUT(1); + return; + } + for (i = 0; i < nr_linked_channels; i++) { + int cur_chan = chan[i]; + int nex_chan = + ((nr_linked_channels - 1 == + i) ? chan[0] : chan[i + 1]); + omap_dma_unlink_lch(cur_chan, nex_chan); + } + s->linked = 0; + FN_OUT(0); +} + +int omap_free_sound_dma(void *data, int **channels) +{ + int i; + int *chan = NULL; + FN_IN; + if (unlikely(NULL == channels)) { + BUG(); + return -EPERM; + } + if (unlikely(NULL == *channels)) { + BUG(); + return -EPERM; + } + chan = (*channels); + + if (!cpu_is_omap1510()) + omap_sound_dma_unlink_lch(data); + for (i = 0; i < nr_linked_channels; i++) { + int cur_chan = chan[i]; + omap_stop_dma(cur_chan); + omap_free_dma(cur_chan); + } + kfree(*channels); + *channels = NULL; + FN_OUT(0); + return 0; +} + +/*************************************************************************************** + * + * Process DMA requests - This will end up starting the transfer. Proper fragments of + * Transfers will be initiated. + * + **************************************************************************************/ +int audio_process_dma(audio_stream_t * s) +{ + int ret = 0; + unsigned long flags; + FN_IN; + + /* Dont let the ISR over ride touching the in_use flag */ + local_irq_save(flags); + if (1 == s->in_use) { + local_irq_restore(flags); + ERR("Called again while In Use\n"); + return 0; + } + s->in_use = 1; + local_irq_restore(flags); + + if (s->stopped) + goto spin; + + if (s->dma_spinref > 0 && s->pending_frags) { + s->dma_spinref = 0; + DMA_CLEAR(s); + } + while (s->pending_frags) { + audio_buf_t *b = &s->buffers[s->dma_head]; + u_int dma_size = s->fragsize - b->offset; + if (dma_size > MAX_DMA_SIZE) + dma_size = CUT_DMA_SIZE; + ret = + omap_start_sound_dma(s, b->dma_addr + b->offset, dma_size); + if (ret) { + goto process_out; + } + b->dma_ref++; + b->offset += dma_size; + if (b->offset >= s->fragsize) { + s->pending_frags--; + if (++s->dma_head >= s->nbfrags) + s->dma_head = 0; + } + } + spin: + if (s->spin_idle) { + int spincnt = 0; + ERR("we are spinning\n"); + while (omap_start_sound_dma(s, SPIN_ADDR, SPIN_SIZE) == 0) + spincnt++; + /* + * Note: if there is still a data buffer being + * processed then the ref count is negative. This + * allows for the DMA termination to be accounted in + * the proper order. Of course dma_spinref can't be + * greater than 0 if dma_ref is not 0 since we kill + * the spinning above as soon as there is real data to process. + */ + if (s->buffers && s->buffers[s->dma_tail].dma_ref) + spincnt = -spincnt; + s->dma_spinref += spincnt; + } + + process_out: + s->in_use = 0; + + FN_OUT(ret); + return ret; +} + +/*************************************************************************************** + * + * Prime Rx - Since the recieve buffer has no time limit as to when it would arrive, + * we need to prime it + * + **************************************************************************************/ +void audio_prime_rx(audio_state_t * state) +{ + audio_stream_t *is = state->input_stream; + + FN_IN; + if (state->need_tx_for_rx) { + /* + * With some codecs like the Philips UDA1341 we must ensure + * there is an output stream at any time while recording since + * this is how the UDA1341 gets its clock from the SA1100. + * So while there is no playback data to send, the output DMA + * will spin with all zeroes. We use the cache flush special + * area for that. + */ + state->output_stream->spin_idle = 1; + audio_process_dma(state->output_stream); + } + is->pending_frags = is->nbfrags; + init_completion(&is->wfc); + is->wfc.done = 0; + + is->active = 1; + audio_process_dma(is); + + FN_OUT(0); + return; +} + +/*************************************************************************************** + * + * set the fragment size + * + **************************************************************************************/ +int audio_set_fragments(audio_stream_t * s, int val) +{ + FN_IN; + if (s->active) + return -EBUSY; + if (s->buffers) + audio_discard_buf(s); + s->nbfrags = (val >> 16) & 0x7FFF; + val &= 0xFFFF; + if (val < 4) + val = 4; + if (val > 15) + val = 15; + s->fragsize = 1 << val; + if (s->nbfrags < 2) + s->nbfrags = 2; + if (s->nbfrags * s->fragsize > 128 * 1024) + s->nbfrags = 128 * 1024 / s->fragsize; + FN_OUT(0); + if (audio_setup_buf(s)) + return -ENOMEM; + return val | (s->nbfrags << 16); + +} + +/*************************************************************************************** + * + * Sync up the buffers before we shutdown, else under-run errors will happen + * + **************************************************************************************/ +int audio_sync(struct file *file) +{ + audio_state_t *state = file->private_data; + audio_stream_t *s = state->output_stream; + audio_buf_t *b; + u_int shiftval = 0; + unsigned long flags; + + DECLARE_WAITQUEUE(wait, current); + + FN_IN; + + if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped) { + FN_OUT(1); + return 0; + } + + /* + * Send current buffer if it contains data. Be sure to send + * a full sample count. + */ + b = &s->buffers[s->usr_head]; + if (b->offset &= ~3) { + /* Wait for a buffer to become free */ + if (wait_for_completion_interruptible(&s->wfc)) + return 0; + /* + * HACK ALERT ! + * To avoid increased complexity in the rest of the code + * where full fragment sizes are assumed, we cheat a little + * with the start pointer here and don't forget to restore + * it later. + */ + + /* As this is a last frag we need only one dma channel + * to complete. So it's need to unlink dma channels + * to avoid empty dma work. + */ + if (!cpu_is_omap1510() && AUDIO_QUEUE_EMPTY(s)) + omap_sound_dma_unlink_lch(s); + + shiftval = s->fragsize - b->offset; + b->offset = shiftval; + b->dma_addr -= shiftval; + b->data -= shiftval; + local_irq_save(flags); + s->bytecount -= shiftval; + if (++s->usr_head >= s->nbfrags) + s->usr_head = 0; + + s->pending_frags++; + audio_process_dma(s); + local_irq_restore(flags); + } + + /* Let's wait for all buffers to complete */ + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&s->wq, &wait); + while ((s->pending_frags || (s->wfc.done < s->nbfrags)) + && !signal_pending(current)) { + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&s->wq, &wait); + + /* undo the pointer hack above */ + if (shiftval) { + local_irq_save(flags); + b->dma_addr += shiftval; + b->data += shiftval; + /* ensure sane DMA code behavior if not yet processed */ + if (b->offset != 0) + b->offset = s->fragsize; + local_irq_restore(flags); + } + + FN_OUT(0); + return 0; +} + +/*************************************************************************************** + * + * Stop all the DMA channels of the stream + * + **************************************************************************************/ +void audio_stop_dma(audio_stream_t * s) +{ + int *chan = s->lch; + int i; + FN_IN; + if (unlikely(NULL == chan)) { + BUG(); + return; + } + for (i = 0; i < nr_linked_channels; i++) { + int cur_chan = chan[i]; + omap_stop_dma(cur_chan); + } + s->started = 0; + FN_OUT(0); + return; +} + +/*************************************************************************************** + * + * Get the dma posn + * + **************************************************************************************/ +u_int audio_get_dma_pos(audio_stream_t * s) +{ + audio_buf_t *b = &s->buffers[s->dma_tail]; + u_int offset; + + FN_IN; + if (b->dma_ref) { + offset = omap_get_dma_src_pos(s->lch[s->dma_q_head]) - b->dma_addr; + if (offset >= s->fragsize) + offset = s->fragsize - 4; + } else if (s->pending_frags) { + offset = b->offset; + } else { + offset = 0; + } + FN_OUT(offset); + return offset; +} + +/*************************************************************************************** + * + * Reset the audio buffers + * + **************************************************************************************/ +void audio_reset(audio_stream_t * s) +{ + FN_IN; + if (s->buffers) { + audio_stop_dma(s); + s->buffers[s->dma_head].offset = 0; + s->buffers[s->usr_head].offset = 0; + s->usr_head = s->dma_head; + s->pending_frags = 0; + init_completion(&s->wfc); + s->wfc.done = s->nbfrags; + } + s->active = 0; + s->stopped = 0; + s->started = 0; + FN_OUT(0); + return; +} + +/*************************************************************************************** + * + * Clear any pending transfers + * + **************************************************************************************/ +void omap_clear_sound_dma(audio_stream_t * s) +{ + FN_IN; + omap_clear_dma(s->lch[s->dma_q_head]); + FN_OUT(0); + return; +} + +/*************************************************************************************** + * + * DMA related functions + * + **************************************************************************************/ +static int audio_set_dma_params_play(int channel, dma_addr_t dma_ptr, + u_int dma_size) +{ + int dt = 0x1; /* data type 16 */ + int cen = 32; /* Stereo */ + int cfn = dma_size / (2 * cen); + unsigned long dest_start; + int dest_port = 0; + int sync_dev = 0; + + FN_IN; + + if (cpu_is_omap1510() || cpu_is_omap16xx()) { + dest_start = AUDIO_MCBSP_DATAWRITE; + dest_port = OMAP_DMA_PORT_MPUI; + } + if (cpu_is_omap24xx()) { + dest_start = AUDIO_MCBSP_DATAWRITE; + sync_dev = AUDIO_DMA_TX; + } + + omap_set_dma_dest_params(channel, dest_port, OMAP_DMA_AMODE_CONSTANT, dest_start, 0, 0); + omap_set_dma_src_params(channel, 0, OMAP_DMA_AMODE_POST_INC, dma_ptr, 0, 0); + omap_set_dma_transfer_params(channel, dt, cen, cfn, OMAP_DMA_SYNC_ELEMENT, sync_dev, 0); + + FN_OUT(0); + return 0; +} + +static int audio_set_dma_params_capture(int channel, dma_addr_t dma_ptr, + u_int dma_size) +{ + int dt = 0x1; /* data type 16 */ + int cen = 16; /* mono */ + int cfn = dma_size / (2 * cen); + unsigned long src_start; + int src_port = 0; + int sync_dev = 0; + int src_sync = 0; + + FN_IN; + + if (cpu_is_omap1510() || cpu_is_omap16xx()) { + src_start = AUDIO_MCBSP_DATAREAD; + src_port = OMAP_DMA_PORT_MPUI; + } + if (cpu_is_omap24xx()) { + src_start = AUDIO_MCBSP_DATAREAD; + sync_dev = AUDIO_DMA_RX; + src_sync = 1; + } + + omap_set_dma_src_params(channel, src_port, OMAP_DMA_AMODE_CONSTANT, src_start, 0, 0); + omap_set_dma_dest_params(channel, 0, OMAP_DMA_AMODE_POST_INC, dma_ptr, 0, 0); + omap_set_dma_transfer_params(channel, dt, cen, cfn, OMAP_DMA_SYNC_ELEMENT, sync_dev, src_sync); + + FN_OUT(0); + return 0; +} + +static int audio_start_dma_chain(audio_stream_t * s) +{ + int channel = s->lch[s->dma_q_head]; + FN_IN; + if (!s->started) { + s->hw_stop(); /* stops McBSP Interface */ + omap_start_dma(channel); + s->started = 1; + s->hw_start(); /* start McBSP interface */ + } + /* else the dma itself will progress forward with out our help */ + FN_OUT(0); + return 0; +} + +/* Start DMA - + * Do the initial set of work to initialize all the channels as required. + * We shall then initate a transfer + */ +static int omap_start_sound_dma(audio_stream_t * s, dma_addr_t dma_ptr, + u_int dma_size) +{ + int ret = -EPERM; + + FN_IN; + if (unlikely(dma_size > MAX_DMA_SIZE)) { + ERR("DmaSoundDma: Start: overflowed %d-%d\n", dma_size, + MAX_DMA_SIZE); + return -EOVERFLOW; + } + + if (AUDIO_QUEUE_FULL(s)) { + ret = -2; + goto sound_out; + } + + if (s->input_or_output == FMODE_WRITE) + /*playback */ + { + ret = + audio_set_dma_params_play(s->lch[s->dma_q_tail], dma_ptr, + dma_size); + } else { + ret = + audio_set_dma_params_capture(s->lch[s->dma_q_tail], dma_ptr, + dma_size); + } + if (ret != 0) { + ret = -2; /* indicate queue full */ + goto sound_out; + } + AUDIO_INCREMENT_TAIL(s); + ret = audio_start_dma_chain(s); + if (ret) { + ERR("dma start failed"); + } + sound_out: + FN_OUT(ret); + return ret; + +} + +/*************************************************************************************** + * + * ISR related functions + * + **************************************************************************************/ +/* The work item handler */ +static void audio_dsr_handler(unsigned long inData) +{ + void *data = (void *)inData; + struct audio_isr_work_item *work = data; + audio_stream_t *s = (work->s); + int sound_curr_lch = work->current_lch; + u16 ch_status = work->ch_status; + + FN_IN; + DPRINTK("lch=%d,status=0x%x, data=%p as=%p\n", sound_curr_lch, + ch_status, data, s); + if (AUDIO_QUEUE_EMPTY(s)) { + ERR("Interrupt(%d) for empty queue(h=%d, T=%d)???\n", + sound_curr_lch, s->dma_q_head, s->dma_q_tail); + ERR("nbfrag=%d,pendfrags=%d,USR-H=%d, QH-%d QT-%d\n", + s->nbfrags, s->pending_frags, s->usr_head, s->dma_head, + s->dma_tail); + FN_OUT(-1); + return; + } + + AUDIO_INCREMENT_HEAD(s); /* Empty the queue */ + + /* Try to fill again */ + audio_dma_callback(sound_curr_lch, ch_status, s); + FN_OUT(0); + +} + +/* Macro to trace the IRQ calls - checks for multi-channel irqs */ +//#define IRQ_TRACE +#ifdef IRQ_TRACE +#define MAX_UP 10 +static char xyz[MAX_UP] = { 0 }; +static int h = 0; +#endif + +/* ISRs have to be short and smart.. So we transfer every heavy duty stuff to the + * work item + */ +static void sound_dma_irq_handler(int sound_curr_lch, u16 ch_status, void *data) +{ + int dma_status = ch_status; + audio_stream_t *s = (audio_stream_t *) data; + FN_IN; +#ifdef IRQ_TRACE + xyz[h++] = '0' + sound_curr_lch; + if (h == MAX_UP - 1) { + printk("%s-", xyz); + h = 0; + } +#endif + DPRINTK("lch=%d,status=0x%x, dma_status=%d, data=%p\n", sound_curr_lch, + ch_status, dma_status, data); + + if (dma_status & (DCSR_ERROR)) { + if (cpu_is_omap1510() || cpu_is_omap16xx()) + OMAP_DMA_CCR_REG(sound_curr_lch) &= ~DCCR_EN; + ERR("DCSR_ERROR!\n"); + FN_OUT(-1); + return; + } + + if (AUDIO_QUEUE_LAST(s)) + audio_stop_dma(s); + + /* Start the work item - we ping pong the work items */ + if (!work_item_running) { + work1.current_lch = sound_curr_lch; + work1.ch_status = ch_status; + work1.s = s; + /* schedule tasklet 1 */ + tasklet_schedule(&audio_isr_work1); + work_item_running = 1; + } else { + work2.current_lch = sound_curr_lch; + work2.ch_status = ch_status; + work2.s = s; + /* schedule tasklet 2 */ + tasklet_schedule(&audio_isr_work2); + work_item_running = 0; + } + FN_OUT(0); + return; +} + +/* The call back that handles buffer stuff */ +static void audio_dma_callback(int lch, u16 ch_status, void *data) +{ + audio_stream_t *s = data; + audio_buf_t *b = &s->buffers[s->dma_tail]; + FN_IN; + + if (s->dma_spinref > 0) { + s->dma_spinref--; + } else if (!s->buffers) { + printk(KERN_CRIT + "omap_audio: received DMA IRQ for non existent buffers!\n"); + return; + } else if (b->dma_ref && --b->dma_ref == 0 && b->offset >= s->fragsize) { + /* This fragment is done */ + b->offset = 0; + s->bytecount += s->fragsize; + s->fragcount++; + s->dma_spinref = -s->dma_spinref; + + if (++s->dma_tail >= s->nbfrags) + s->dma_tail = 0; + + if (!s->mapped) + complete(&s->wfc); + else + s->pending_frags++; + + wake_up(&s->wq); + } + + audio_process_dma(s); + + FN_OUT(0); + return; +} + +/********************************************************************************* + * + * audio_get_dma_callback(): return the dma interface call back function + * + *********************************************************************************/ +dma_callback_t audio_get_dma_callback(void) +{ + FN_IN; + FN_OUT(0); + return audio_dma_callback; +} + +static int __init audio_dma_init(void) +{ + if (!cpu_is_omap1510()) + nr_linked_channels = 2; + + return 0; +} + +static void __exit audio_dma_exit(void) +{ + /* Nothing */ +} + +module_init(audio_dma_init); +module_exit(audio_dma_exit); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("Common DMA handling for Audio driver on OMAP processors"); +MODULE_LICENSE("GPL"); + +EXPORT_SYMBOL(omap_clear_sound_dma); +EXPORT_SYMBOL(omap_request_sound_dma); +EXPORT_SYMBOL(omap_free_sound_dma); + +EXPORT_SYMBOL(audio_get_dma_callback); +EXPORT_SYMBOL(audio_setup_buf); +EXPORT_SYMBOL(audio_process_dma); +EXPORT_SYMBOL(audio_prime_rx); +EXPORT_SYMBOL(audio_set_fragments); +EXPORT_SYMBOL(audio_sync); +EXPORT_SYMBOL(audio_stop_dma); +EXPORT_SYMBOL(audio_get_dma_pos); +EXPORT_SYMBOL(audio_reset); +EXPORT_SYMBOL(audio_discard_buf); diff --git a/sound/oss/omap-audio-dma-intfc.h b/sound/oss/omap-audio-dma-intfc.h new file mode 100644 index 00000000000..65aa528f21c --- /dev/null +++ b/sound/oss/omap-audio-dma-intfc.h @@ -0,0 +1,63 @@ +/* + * linux/sound/oss/omap-audio-dma-intfc.h + * + * Common audio DMA handling for the OMAP processors + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * Copyright (C) 2000, 2001 Nicolas Pitre + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * + * 2004/08/12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms + */ + +#ifndef __OMAP_AUDIO_DMA_INTFC_H +#define __OMAP_AUDIO_DMA_INTFC_H + +/************************** INCLUDES *************************************/ + +/* Requires omap-audio.h */ +#include "omap-audio.h" + +/************************** GLOBAL MACROS *************************************/ + +/* Provide the Macro interfaces common across platforms */ +#define DMA_REQUEST(e,s, cb) {e=omap_request_sound_dma(s->dma_dev, s->id, s, &s->lch);} +#define DMA_FREE(s) omap_free_sound_dma(s, &s->lch) +#define DMA_CLEAR(s) omap_clear_sound_dma(s) + +/************************** GLOBAL DATA STRUCTURES *********************************/ + +typedef void (*dma_callback_t) (int lch, u16 ch_status, void *data); + +/************************** GLOBAL FUNCTIONS ***************************************/ + +dma_callback_t audio_get_dma_callback(void); +int audio_setup_buf(audio_stream_t * s); +int audio_process_dma(audio_stream_t * s); +void audio_prime_rx(audio_state_t * state); +int audio_set_fragments(audio_stream_t * s, int val); +int audio_sync(struct file *file); +void audio_stop_dma(audio_stream_t * s); +u_int audio_get_dma_pos(audio_stream_t * s); +void audio_reset(audio_stream_t * s); +void audio_discard_buf(audio_stream_t * s); + +/**************** ARCH SPECIFIC FUNCIONS *******************************************/ + +void omap_clear_sound_dma(audio_stream_t * s); + +int omap_request_sound_dma(int device_id, const char *device_name, void *data, + int **channels); +int omap_free_sound_dma(void *data, int **channels); + +#endif /* #ifndef __OMAP_AUDIO_DMA_INTFC_H */ diff --git a/sound/oss/omap-audio-tsc2101.c b/sound/oss/omap-audio-tsc2101.c new file mode 100644 index 00000000000..13dc7aff1ab --- /dev/null +++ b/sound/oss/omap-audio-tsc2101.c @@ -0,0 +1,1237 @@ +/* + * linux/sound/oss/omap-audio-tsc2101.c + * + * Glue driver for TSC2101 for OMAP processors + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * ------- + * 2004-08-12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms. + * 2004-09-14 Sriram Kannan - Added /proc support for asynchronous starting/stopping the codec + * (without affecting the normal driver flow). + * 2004-11-04 Nishanth Menon - Support for power management + * 2004-11-07 Nishanth Menon - Support for Common TSC access b/w Touchscreen and audio drivers + */ + +/***************************** INCLUDES ************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "omap-audio.h" +#include "omap-audio-dma-intfc.h" +#include +#ifdef CONFIG_ARCH_OMAP16XX +#include <../drivers/ssi/omap-uwire.h> +#include +#elif defined(CONFIG_ARCH_OMAP24XX) +#else +#error "Unsupported configuration" +#endif + +#include +#include <../drivers/ssi/omap-tsc2101.h> + +/***************************** MACROS ************************************/ + +#define PROC_SUPPORT + +#ifdef PROC_SUPPORT +#include +#define PROC_START_FILE "driver/tsc2101-audio-start" +#define PROC_STOP_FILE "driver/tsc2101-audio-stop" +#endif + +#define CODEC_NAME "TSC2101" + +#ifdef CONFIG_ARCH_OMAP16XX +#define PLATFORM_NAME "OMAP16XX" +#elif defined(CONFIG_ARCH_OMAP24XX) +#define PLATFORM_NAME "OMAP2" +#endif + +/* Define to set the tsc as the master w.r.t McBSP */ +#define TSC_MASTER + +/* + * AUDIO related MACROS + */ +#define DEFAULT_BITPERSAMPLE 16 +#define AUDIO_RATE_DEFAULT 44100 +#define PAGE2_AUDIO_CODEC_REGISTERS (2) +#define LEAVE_CS 0x80 + +/* Select the McBSP For Audio */ +/* 16XX is MCBSP1 and 24XX is MCBSP2*/ +/* see include/asm-arm/arch-omap/mcbsp.h */ +#ifndef AUDIO_MCBSP +#error "UnSupported Configuration" +#endif + +#define REC_MASK (SOUND_MASK_LINE | SOUND_MASK_MIC) +#define DEV_MASK (REC_MASK | SOUND_MASK_VOLUME) + +#define SET_VOLUME 1 +#define SET_LINE 2 +#define SET_MIC 3 +#define SET_RECSRC 4 + +#define DEFAULT_VOLUME 93 +#define DEFAULT_INPUT_VOLUME 20 /* An minimal volume */ + +/* Tsc Audio Specific */ +#define NUMBER_SAMPLE_RATES_SUPPORTED 16 +#define OUTPUT_VOLUME_MIN 0x7F +#define OUTPUT_VOLUME_MAX 0x32 +#define OUTPUT_VOLUME_RANGE (OUTPUT_VOLUME_MIN - OUTPUT_VOLUME_MAX) +#define OUTPUT_VOLUME_MASK OUTPUT_VOLUME_MIN +#define DEFAULT_VOLUME_LEVEL OUTPUT_VOLUME_MAX + +/* use input vol of 75 for 0dB gain */ +#define INPUT_VOLUME_MIN 0x0 +#define INPUT_VOLUME_MAX 0x7D +#define INPUT_VOLUME_RANGE (INPUT_VOLUME_MAX - INPUT_VOLUME_MIN) +#define INPUT_VOLUME_MASK INPUT_VOLUME_MAX + +/*********** Debug Macros ********/ +/* To Generate a rather shrill tone -test the entire path */ +//#define TONE_GEN +/* To Generate a tone for each keyclick - test the tsc,spi paths*/ +//#define TEST_KEYCLICK +/* To dump the tsc registers for debug */ +//#define TSC_DUMP_REGISTERS + +#ifdef DPRINTK +#undef DPRINTK +#endif +#undef DEBUG + +//#define DEBUG +#ifdef DEBUG +#define DPRINTK(ARGS...) printk(KERN_INFO "<%s>: ",__FUNCTION__);printk(ARGS) +#define FN_IN printk(KERN_INFO "[%s]: start\n", __FUNCTION__) +#define FN_OUT(n) printk(KERN_INFO "[%s]: end(%u)\n",__FUNCTION__, n) +#else +#define DPRINTK( x... ) +#define FN_IN +#define FN_OUT(n) +#endif + +/***************************** Data Structures **********************************/ + +static int audio_ifc_start(void) +{ + omap_mcbsp_start(AUDIO_MCBSP); + return 0; +} + +static int audio_ifc_stop(void) +{ + omap_mcbsp_stop(AUDIO_MCBSP); + return 0; +} + +static audio_stream_t output_stream = { + .id = "TSC2101 out", + .dma_dev = AUDIO_DMA_TX, + .input_or_output = FMODE_WRITE, + .hw_start = audio_ifc_start, + .hw_stop = audio_ifc_stop, +}; + +static audio_stream_t input_stream = { + .id = "TSC2101 in", + .dma_dev = AUDIO_DMA_RX, + .input_or_output = FMODE_READ, + .hw_start = audio_ifc_start, + .hw_stop = audio_ifc_stop, +}; + +static int audio_dev_id, mixer_dev_id; + +typedef struct { + u8 volume; + u8 line; + u8 mic; + int recsrc; + int mod_cnt; +} tsc2101_local_info; + +static tsc2101_local_info tsc2101_local = { + volume: DEFAULT_VOLUME, + line: DEFAULT_INPUT_VOLUME, + mic: DEFAULT_INPUT_VOLUME, + recsrc: SOUND_MASK_LINE, + mod_cnt: 0 +}; + +struct sample_rate_reg_info { + u16 sample_rate; + u8 divisor; + u8 fs_44kHz; /* if 0 48 khz, if 1 44.1 khz fsref */ +}; + +/* To Store the default sample rate */ +static long audio_samplerate = AUDIO_RATE_DEFAULT; + +static const struct sample_rate_reg_info + reg_info[NUMBER_SAMPLE_RATES_SUPPORTED] = { + /* Div 1 */ + {48000, 0, 0}, + {44100, 0, 1}, + /* Div 1.5 */ + {32000, 1, 0}, + {29400, 1, 1}, + /* Div 2 */ + {24000, 2, 0}, + {22050, 2, 1}, + /* Div 3 */ + {16000, 3, 0}, + {14700, 3, 1}, + /* Div 4 */ + {12000, 4, 0}, + {11025, 4, 1}, + /* Div 5 */ + {9600, 5, 0}, + {8820, 5, 1}, + /* Div 5.5 */ + {8727, 6, 0}, + {8018, 6, 1}, + /* Div 6 */ + {8000, 7, 0}, + {7350, 7, 1}, +}; + +static struct omap_mcbsp_reg_cfg initial_config = { + .spcr2 = FREE | FRST | GRST | XRST | XINTM(3), + .spcr1 = RINTM(3) | RRST, + .rcr2 = RPHASE | RFRLEN2(OMAP_MCBSP_WORD_8) | + RWDLEN2(OMAP_MCBSP_WORD_16) | RDATDLY(1), + .rcr1 = RFRLEN1(OMAP_MCBSP_WORD_8) | RWDLEN1(OMAP_MCBSP_WORD_16), + .xcr2 = XPHASE | XFRLEN2(OMAP_MCBSP_WORD_8) | + XWDLEN2(OMAP_MCBSP_WORD_16) | XDATDLY(1) | XFIG, + .xcr1 = XFRLEN1(OMAP_MCBSP_WORD_8) | XWDLEN1(OMAP_MCBSP_WORD_16), + .srgr1 = FWID(15), + .srgr2 = GSYNC | CLKSP | FSGM | FPER(31), + + /* platform specific initialization */ +#ifdef CONFIG_MACH_OMAP_H2 + .pcr0 = CLKXM | CLKRM | FSXP | FSRP | CLKXP | CLKRP, +#elif defined(CONFIG_MACH_OMAP_H3) || defined(CONFIG_MACH_OMAP_H4) || defined(CONFIG_MACH_OMAP_APOLLON) + +#ifndef TSC_MASTER + .pcr0 = FSXM | FSRM | CLKXM | CLKRM | CLKXP | CLKRP, +#else + .pcr0 = CLKRM | SCLKME | FSXP | FSRP | CLKXP | CLKRP, +#endif /* tsc Master defs */ + +#endif /* platform specific inits */ +}; + +/***************************** MODULES SPECIFIC FUNCTION PROTOTYPES ********************/ + +static void omap_tsc2101_initialize(void *dummy); + +static void omap_tsc2101_shutdown(void *dummy); + +static int omap_tsc2101_ioctl(struct inode *inode, struct file *file, + uint cmd, ulong arg); + +static int omap_tsc2101_probe(void); + +static void omap_tsc2101_remove(void); + +static int omap_tsc2101_suspend(void); + +static int omap_tsc2101_resume(void); + +static void tsc2101_configure(void); + +static int mixer_open(struct inode *inode, struct file *file); + +static int mixer_release(struct inode *inode, struct file *file); + +static int mixer_ioctl(struct inode *inode, struct file *file, uint cmd, + ulong arg); + +#ifdef TEST_KEYCLICK +void tsc2101_testkeyclick(void); +#endif + +#ifdef TONE_GEN +void toneGen(void); +#endif + +#ifdef TSC_DUMP_REGISTERS +static void tsc2101_dumpRegisters(void); +#endif + +#ifdef PROC_SUPPORT +static int codec_start(char *buf, char **start, off_t offset, int count, + int *eof, void *data); + +static int codec_stop(char *buf, char **start, off_t offset, int count, + int *eof, void *data); + +static void tsc2101_start(void); +#endif + +/******************** DATA STRUCTURES USING FUNCTION POINTERS **************************/ + +/* File Op structure for mixer */ +static struct file_operations omap_mixer_fops = { + .open = mixer_open, + .release = mixer_release, + .ioctl = mixer_ioctl, + .owner = THIS_MODULE +}; + +/* To store characteristic info regarding the codec for the audio driver */ +static audio_state_t tsc2101_state = { + .output_stream = &output_stream, + .input_stream = &input_stream, +/* .need_tx_for_rx = 1, //Once the Full Duplex works */ + .need_tx_for_rx = 0, + .hw_init = omap_tsc2101_initialize, + .hw_shutdown = omap_tsc2101_shutdown, + .client_ioctl = omap_tsc2101_ioctl, + .hw_probe = omap_tsc2101_probe, + .hw_remove = omap_tsc2101_remove, + .hw_suspend = omap_tsc2101_suspend, + .hw_resume = omap_tsc2101_resume, + .sem = __SEMAPHORE_INIT(tsc2101_state.sem, 1), +}; + +/* This will be defined in the Audio.h */ +static struct file_operations *omap_audio_fops; + +/***************************** MODULES SPECIFIC FUNCTIONs *******************************/ + +/********************************************************************************* + * + * Simplified write for tsc Audio + * + *********************************************************************************/ +static __inline__ void audio_tsc2101_write(u8 address, u16 data) +{ + omap_tsc2101_write(PAGE2_AUDIO_CODEC_REGISTERS, address, data); +} + +/********************************************************************************* + * + * Simplified read for tsc Audio + * + *********************************************************************************/ +static __inline__ u16 audio_tsc2101_read(u8 address) +{ + return (omap_tsc2101_read(PAGE2_AUDIO_CODEC_REGISTERS, address)); +} + +/********************************************************************************* + * + * tsc2101_update() + * Volume Adj etc + * + ********************************************************************************/ +static int tsc2101_update(int flag, int val) +{ + u16 volume; + u16 data; + + FN_IN; + switch (flag) { + case SET_VOLUME: + if (val < 0 || val > 100) { + printk(KERN_ERR "Trying a bad volume value(%d)!\n", val); + return -EPERM; + } + /* Convert 0 -> 100 volume to 0x7F(min) -> y(max) volume range */ + volume = + ((val * OUTPUT_VOLUME_RANGE) / 100) + OUTPUT_VOLUME_MAX; + /* invert the value for getting the proper range 0 min and 100 max */ + volume = OUTPUT_VOLUME_MIN - volume; + data = audio_tsc2101_read(TSC2101_DAC_GAIN_CTRL); + data &= + ~(DGC_DALVL(OUTPUT_VOLUME_MIN) | + DGC_DARVL(OUTPUT_VOLUME_MIN)); + data |= DGC_DALVL(volume) | DGC_DARVL(volume); + audio_tsc2101_write(TSC2101_DAC_GAIN_CTRL, data); + data = audio_tsc2101_read(TSC2101_DAC_GAIN_CTRL); + + break; + + case SET_LINE: + if (val < 0 || val > 100) { + printk(KERN_ERR "Trying a bad volume value(%d)!\n", val); + return -EPERM; + } + /* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range */ + /* NOTE: 0 is minimum volume and not mute */ + volume = ((val * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN; + /* Handset Input not muted, AGC for Handset In off */ + audio_tsc2101_write(TSC2101_HEADSET_GAIN_CTRL, + HGC_ADPGA_HED(volume)); + break; + + case SET_MIC: + if (val < 0 || val > 100) { + printk(KERN_ERR "Trying a bad volume value(%d)!\n", val); + return -EPERM; + } + /* Convert 0 -> 100 volume to 0x0(min) -> 0x7D(max) volume range */ + /* NOTE: 0 is minimum volume and not mute */ + volume = ((val * INPUT_VOLUME_RANGE) / 100) + INPUT_VOLUME_MIN; + /* Handset Input not muted, AGC for Handset In off */ + audio_tsc2101_write(TSC2101_HANDSET_GAIN_CTRL, + HNGC_ADPGA_HND(volume)); + break; + + case SET_RECSRC: + /* + * If more than one recording device selected, + * disable the device that is currently in use. + */ + if (hweight32(val) > 1) + val &= ~tsc2101_local.recsrc; + + data = audio_tsc2101_read(TSC2101_MIXER_PGA_CTRL); + data &= ~MPC_MICSEL(7); /* clear all MICSEL bits */ + + if (val == SOUND_MASK_MIC) { + data |= MPC_MICSEL(1); + audio_tsc2101_write(TSC2101_MIXER_PGA_CTRL, data); + } + else if (val == SOUND_MASK_LINE) { + data |= MPC_MICSEL(0); + audio_tsc2101_write(TSC2101_MIXER_PGA_CTRL, data); + } + else { + printk(KERN_WARNING "omap1610-tsc2101: Wrong RECSRC" + " value specified\n"); + return -EINVAL; + } + tsc2101_local.recsrc = val; + break; + default: + printk(KERN_WARNING "omap1610-tsc2101: Wrong tsc2101_update " + "flag specified\n"); + break; + } + + FN_OUT(0); + return 0; +} + +/********************************************************************************* + * + * mixer_open() + * + ********************************************************************************/ +static int mixer_open(struct inode *inode, struct file *file) +{ + /* Any mixer specific initialization */ + + /* Initalize the tsc2101 */ + omap_tsc2101_enable(); + + return 0; +} + +/********************************************************************************* + * + * mixer_release() + * + ********************************************************************************/ +static int mixer_release(struct inode *inode, struct file *file) +{ + /* Any mixer specific Un-initialization */ + omap_tsc2101_disable(); + + return 0; +} + +/********************************************************************************* + * + * mixer_ioctl() + * + ********************************************************************************/ +static int +mixer_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + int val; + int gain; + int ret = 0; + int nr = _IOC_NR(cmd); + + /* + * We only accept mixer (type 'M') ioctls. + */ + FN_IN; + if (_IOC_TYPE(cmd) != 'M') + return -EINVAL; + + DPRINTK(" 0x%08x\n", cmd); + + if (cmd == SOUND_MIXER_INFO) { + struct mixer_info mi; + + strncpy(mi.id, "TSC2101", sizeof(mi.id)); + strncpy(mi.name, "TI TSC2101", sizeof(mi.name)); + mi.modify_counter = tsc2101_local.mod_cnt; + FN_OUT(1); + return copy_to_user((void __user *)arg, &mi, sizeof(mi)); + } + + if (_IOC_DIR(cmd) & _IOC_WRITE) { + ret = get_user(val, (int __user *)arg); + if (ret) + goto out; + + /* Ignore separate left/right channel for now, + * even the codec does support it. + */ + gain = val & 255; + + switch (nr) { + case SOUND_MIXER_VOLUME: + tsc2101_local.volume = val; + tsc2101_local.mod_cnt++; + ret = tsc2101_update(SET_VOLUME, gain); + break; + + case SOUND_MIXER_LINE: + tsc2101_local.line = val; + tsc2101_local.mod_cnt++; + ret = tsc2101_update(SET_LINE, gain); + break; + + case SOUND_MIXER_MIC: + tsc2101_local.mic = val; + tsc2101_local.mod_cnt++; + ret = tsc2101_update(SET_MIC, gain); + break; + + case SOUND_MIXER_RECSRC: + if ((val & SOUND_MASK_LINE) || + (val & SOUND_MASK_MIC)) { + if (tsc2101_local.recsrc != val) { + tsc2101_local.mod_cnt++; + tsc2101_update(SET_RECSRC, val); + } + } + else { + ret = -EINVAL; + } + break; + + default: + ret = -EINVAL; + } + } + + if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) { + ret = 0; + + switch (nr) { + case SOUND_MIXER_VOLUME: + val = tsc2101_local.volume; + val = (tsc2101_local.volume << 8) | + tsc2101_local.volume; + break; + case SOUND_MIXER_LINE: + val = (tsc2101_local.line << 8) | + tsc2101_local.line; + break; + case SOUND_MIXER_MIC: + val = (tsc2101_local.mic << 8) | + tsc2101_local.mic; + break; + case SOUND_MIXER_RECSRC: + val = tsc2101_local.recsrc; + break; + case SOUND_MIXER_RECMASK: + val = REC_MASK; + break; + case SOUND_MIXER_DEVMASK: + val = DEV_MASK; + break; + case SOUND_MIXER_CAPS: + val = 0; + break; + case SOUND_MIXER_STEREODEVS: + val = SOUND_MASK_VOLUME; + break; + default: + val = 0; + printk(KERN_WARNING "omap1610-tsc2101: unknown mixer " + "read ioctl flag specified\n"); + ret = -EINVAL; + break; + } + + if (ret == 0) + ret = put_user(val, (int __user *)arg); + } + out: + FN_OUT(0); + return ret; + +} + +/********************************************************************************* + * + * omap_set_samplerate() + * + ********************************************************************************/ +static int omap_set_samplerate(long sample_rate) +{ + u8 count = 0; + u16 data = 0; + int clkgdv = 0; + /* wait for any frame to complete */ + udelay(125); + + /* Search for the right sample rate */ + while ((reg_info[count].sample_rate != sample_rate) && + (count < NUMBER_SAMPLE_RATES_SUPPORTED)) { + count++; + } + if (count == NUMBER_SAMPLE_RATES_SUPPORTED) { + printk(KERN_ERR "Invalid Sample Rate %d requested\n", + (int)sample_rate); + return -EPERM; + } + + /* Set AC1 */ + data = audio_tsc2101_read(TSC2101_AUDIO_CTRL_1); + /*Clear prev settings */ + data &= ~(AC1_DACFS(0x07) | AC1_ADCFS(0x07)); + data |= + AC1_DACFS(reg_info[count].divisor) | AC1_ADCFS(reg_info[count]. + divisor); + audio_tsc2101_write(TSC2101_AUDIO_CTRL_1, data); + + /* Set the AC3 */ + data = audio_tsc2101_read(TSC2101_AUDIO_CTRL_3); + /*Clear prev settings */ + data &= ~(AC3_REFFS | AC3_SLVMS); + data |= (reg_info[count].fs_44kHz) ? AC3_REFFS : 0; +#ifdef TSC_MASTER + data |= AC3_SLVMS; +#endif /* #ifdef TSC_MASTER */ + audio_tsc2101_write(TSC2101_AUDIO_CTRL_3, data); + + /* program the PLLs */ + if (reg_info[count].fs_44kHz) { + /* 44.1 khz - 12 MHz Mclk */ + audio_tsc2101_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL | PLL1_PVAL(1) | PLL1_I_VAL(7)); /* PVAL 1; I_VAL 7 */ + audio_tsc2101_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x1490)); /* D_VAL 5264 */ + } else { + /* 48 khz - 12 Mhz Mclk */ + audio_tsc2101_write(TSC2101_PLL_PROG_1, PLL1_PLLSEL | PLL1_PVAL(1) | PLL1_I_VAL(8)); /* PVAL 1; I_VAL 8 */ + audio_tsc2101_write(TSC2101_PLL_PROG_2, PLL2_D_VAL(0x780)); /* D_VAL 1920 */ + } + + audio_samplerate = sample_rate; + + /* Set the sample rate */ +#ifndef TSC_MASTER + clkgdv = + DEFAULT_MCBSP_CLOCK / (sample_rate * + (DEFAULT_BITPERSAMPLE * 2 - 1)); + if (clkgdv) + initial_config.srgr1 = + (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv)); + else + return (1); + + /* Stereo Mode */ + initial_config.srgr2 = + (CLKSM | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1)); +#else + initial_config.srgr1 = + (FWID(DEFAULT_BITPERSAMPLE - 1) | CLKGDV(clkgdv)); + initial_config.srgr2 = + ((GSYNC | CLKSP | FSGM | FPER(DEFAULT_BITPERSAMPLE * 2 - 1))); + +#endif /* end of #ifdef TSC_MASTER */ + omap_mcbsp_config(AUDIO_MCBSP, &initial_config); + + return 0; +} + +/********************************************************************************* + * + * omap_tsc2101_initialize() [hw_init() ] + * + ********************************************************************************/ +static void omap_tsc2101_initialize(void *dummy) +{ + + DPRINTK("omap_tsc2101_initialize entry\n"); + + /* initialize with default sample rate */ + audio_samplerate = AUDIO_RATE_DEFAULT; + + omap_mcbsp_request(AUDIO_MCBSP); + + /* if configured, then stop mcbsp */ + omap_mcbsp_stop(AUDIO_MCBSP); + + omap_tsc2101_enable(); + + omap_mcbsp_config(AUDIO_MCBSP, &initial_config); + omap_mcbsp_start(AUDIO_MCBSP); + tsc2101_configure(); + +#ifdef TEST_KEYCLICK + tsc2101_testkeyclick(); +#endif + +#ifdef TONE_GEN + toneGen(); +#endif + + DPRINTK("omap_tsc2101_initialize exit\n"); +} + +/********************************************************************************* + * + * omap_tsc2101_shutdown() [hw_shutdown() ] + * + ********************************************************************************/ +static void omap_tsc2101_shutdown(void *dummy) +{ + /* + Turn off codec after it is done. + Can't do it immediately, since it may still have + buffered data. + + Wait 20ms (arbitrary value) and then turn it off. + */ + + FN_IN; + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(2); + + omap_mcbsp_stop(AUDIO_MCBSP); + omap_mcbsp_free(AUDIO_MCBSP); + + audio_tsc2101_write(TSC2101_CODEC_POWER_CTRL, + ~(CPC_SP1PWDN | CPC_SP2PWDN | CPC_BASSBC)); + + omap_tsc2101_disable(); + + FN_OUT(0); +} + +/********************************************************************************* + * + * tsc2101_configure + * + ********************************************************************************/ +static void tsc2101_configure(void) +{ + FN_IN; + + audio_tsc2101_write(TSC2101_CODEC_POWER_CTRL, 0x0000); + + /*Mute Analog Sidetone */ + /*Select MIC_INHED input for headset */ + /*Cell Phone In not connected */ + audio_tsc2101_write(TSC2101_MIXER_PGA_CTRL, + MPC_ASTMU | MPC_ASTG(0x40) | MPC_MICADC); + + /* Set record source */ + tsc2101_update(SET_RECSRC, tsc2101_local.recsrc); + + /* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */ + /* 1dB AGC hysteresis */ + /* MICes bias 2V */ + audio_tsc2101_write(TSC2101_AUDIO_CTRL_4, AC4_MB_HED(0)); + + /* Set codec output volume */ + audio_tsc2101_write(TSC2101_DAC_GAIN_CTRL, 0x0000); + + /* DAC left and right routed to SPK2 */ + /* SPK1/2 unmuted */ + audio_tsc2101_write(TSC2101_AUDIO_CTRL_5, + AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 | + AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 | + AC5_HDSCPTC); + + /* OUT8P/N muted, CPOUT muted */ + + audio_tsc2101_write(TSC2101_AUDIO_CTRL_6, + AC6_MUTLSPK | AC6_MUTSPK2 | AC6_LDSCPTC | + AC6_VGNDSCPTC); + + /* Headset/Hook switch detect disabled */ + audio_tsc2101_write(TSC2101_AUDIO_CTRL_7, 0x0000); + + /* Left line input volume control */ + tsc2101_update(SET_LINE, tsc2101_local.line); + + /* mic input volume control */ + tsc2101_update(SET_MIC, tsc2101_local.mic); + + /* Left/Right headphone channel volume control */ + /* Zero-cross detect on */ + tsc2101_update(SET_VOLUME, tsc2101_local.volume); + + /* clock configuration */ + omap_set_samplerate(audio_samplerate); + +#ifdef TSC_DUMP_REGISTERS + tsc2101_dumpRegisters(); +#endif + + FN_OUT(0); +} + +#ifdef PROC_SUPPORT +static void tsc2101_start(void) +{ + FN_IN; + + audio_tsc2101_write(TSC2101_CODEC_POWER_CTRL, 0x0000); + + /*Mute Analog Sidetone */ + /*Select MIC_INHED input for headset */ + /*Cell Phone In not connected */ + audio_tsc2101_write(TSC2101_MIXER_PGA_CTRL, + MPC_ASTMU | MPC_ASTG(0x40) | MPC_MICADC); + + /* Set record source */ + tsc2101_update(SET_RECSRC, tsc2101_local.recsrc); + + /* ADC, DAC, Analog Sidetone, cellphone, buzzer softstepping enabled */ + /* 1dB AGC hysteresis */ + /* MICes bias 2V */ + audio_tsc2101_write(TSC2101_AUDIO_CTRL_4, AC4_MB_HED(0)); + + /* Set codec output volume */ + audio_tsc2101_write(TSC2101_DAC_GAIN_CTRL, 0x0000); + + /* DAC left and right routed to SPK2 */ + /* SPK1/2 unmuted */ + audio_tsc2101_write(TSC2101_AUDIO_CTRL_5, + AC5_DAC2SPK1(3) | AC5_AST2SPK1 | AC5_KCL2SPK1 | + AC5_DAC2SPK2(3) | AC5_AST2SPK2 | AC5_KCL2SPK2 | + AC5_HDSCPTC); + + /* OUT8P/N muted, CPOUT muted */ + + audio_tsc2101_write(TSC2101_AUDIO_CTRL_6, + AC6_MUTLSPK | AC6_MUTSPK2 | AC6_LDSCPTC | + AC6_VGNDSCPTC); + + /* Headset/Hook switch detect disabled */ + audio_tsc2101_write(TSC2101_AUDIO_CTRL_7, 0x0000); + + /* Left line input volume control */ + tsc2101_update(SET_LINE, tsc2101_local.line); + + /* mic input volume control */ + tsc2101_update(SET_MIC, tsc2101_local.mic); + + /* Left/Right headphone channel volume control */ + /* Zero-cross detect on */ + tsc2101_update(SET_VOLUME, tsc2101_local.volume); + + FN_OUT(0); + +} +#endif + +/****************************************************************************************** + * + * All generic ioctl's are handled by audio_ioctl() [File: omap-audio.c]. This + * routine handles some platform specific ioctl's + * + ******************************************************************************************/ +static int +omap_tsc2101_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + long val; + int ret = 0; + + DPRINTK(" 0x%08x\n", cmd); + + /* + * These are platform dependent ioctls which are not handled by the + * generic omap-audio module. + */ + switch (cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int __user *)arg); + if (ret) + return ret; + /* the AIC23 is stereo only */ + ret = (val == 0) ? -EINVAL : 1; + FN_OUT(1); + return put_user(ret, (int __user *)arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* the AIC23 is stereo only */ + FN_OUT(2); + return put_user(2, (long __user *)arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long __user *)arg); + if (ret) + break; + ret = omap_set_samplerate(val); + if (ret) + break; + /* fall through */ + + case SOUND_PCM_READ_RATE: + FN_OUT(3); + return put_user(audio_samplerate, (long __user *)arg); + + case SOUND_PCM_READ_BITS: + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* we can do 16-bit only */ + FN_OUT(4); + return put_user(AFMT_S16_LE, (long __user *)arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + FN_OUT(5); + return mixer_ioctl(inode, file, cmd, arg); + } + + FN_OUT(0); + return ret; +} + +/********************************************************************************* + * + * module_probe for TSC2101 + * + ********************************************************************************/ +static int omap_tsc2101_probe(void) +{ + FN_IN; + + /* Get the fops from audio oss driver */ + if (!(omap_audio_fops = audio_get_fops())) { + printk(KERN_ERR "Unable to Get the FOPs of Audio OSS driver\n"); + audio_unregister_codec(&tsc2101_state); + return -EPERM; + } + + /* register devices */ + audio_dev_id = register_sound_dsp(omap_audio_fops, -1); + mixer_dev_id = register_sound_mixer(&omap_mixer_fops, -1); + +#ifdef PROC_SUPPORT + create_proc_read_entry(PROC_START_FILE, 0 /* default mode */ , + NULL /* parent dir */ , + codec_start, NULL /* client data */ ); + + create_proc_read_entry(PROC_STOP_FILE, 0 /* default mode */ , + NULL /* parent dir */ , + codec_stop, NULL /* client data */ ); +#endif + + /* Announcement Time */ + printk(KERN_INFO PLATFORM_NAME " " CODEC_NAME + " Audio support initialized\n"); + + FN_OUT(0); + return 0; +} + +/********************************************************************************* + * + * Module Remove for TSC2101 + * + ********************************************************************************/ +static void omap_tsc2101_remove(void) +{ + FN_IN; + /* Un-Register the codec with the audio driver */ + unregister_sound_dsp(audio_dev_id); + unregister_sound_mixer(mixer_dev_id); + +#ifdef PROC_SUPPORT + remove_proc_entry(PROC_START_FILE, NULL); + remove_proc_entry(PROC_STOP_FILE, NULL); +#endif + FN_OUT(0); + +} + +/********************************************************************************* + * + * Module Suspend for TSC2101 + * + ********************************************************************************/ +static int omap_tsc2101_suspend(void) +{ + + FN_OUT(0); + return 0; +} + +/********************************************************************************* + * + * Module Resume for TSC2101 + * + ********************************************************************************/ +static int omap_tsc2101_resume(void) +{ + + FN_OUT(0); + return 0; +} + +/********************************************************************************* + * + * module_init for TSC2101 + * + ********************************************************************************/ +static int __init audio_tsc2101_init(void) +{ + + int err = 0; + FN_IN; + + if (machine_is_omap_osk() || machine_is_omap_innovator()) + return -ENODEV; + + /* register the codec with the audio driver */ + if ((err = audio_register_codec(&tsc2101_state))) { + printk(KERN_ERR + "Failed to register TSC driver with Audio OSS Driver\n"); + } + FN_OUT(err); + return err; +} + +/********************************************************************************* + * + * module_exit for TSC2101 + * + ********************************************************************************/ +static void __exit audio_tsc2101_exit(void) +{ + + FN_IN; + (void)audio_unregister_codec(&tsc2101_state); + FN_OUT(0); + return; +} + +/**************************** DEBUG FUNCTIONS ***********************************/ + +/********************************************************************************* + * TEST_KEYCLICK: + * This is a test to generate various keyclick sound on tsc. + * verifies if the tsc and the spi interfaces are operational. + * + ********************************************************************************/ +#ifdef TEST_KEYCLICK +void tsc2101_testkeyclick(void) +{ + u8 freq = 0; + u16 old_reg_val, reg_val; + u32 uDummyVal = 0; + u32 uTryVal = 0; + + old_reg_val = audio_tsc2101_read(TSC2101_AUDIO_CTRL_2); + + /* Keyclick active, max amplitude and longest key click len(32 period) */ + printk(KERN_INFO " TESTING KEYCLICK\n Listen carefully NOW....\n"); + printk(KERN_INFO " OLD REG VAL=0x%x\n", old_reg_val); + /* try all frequencies */ + for (; freq < 8; freq++) { + /* Keyclick active, max amplitude and longest key click len(32 period) */ + reg_val = old_reg_val | AC2_KCLAC(0x7) | AC2_KCLLN(0xF); + uDummyVal = 0; + uTryVal = 0; + printk(KERN_INFO "\n\nTrying frequency %d reg val= 0x%x\n", + freq, reg_val | AC2_KCLFRQ(freq) | AC2_KCLEN); + audio_tsc2101_write(TSC2101_AUDIO_CTRL_2, + reg_val | AC2_KCLFRQ(freq) | AC2_KCLEN); + printk("DONE. Wait 10 ms ...\n"); + /* wait till the kclk bit is auto cleared! time out also to be considered. */ + while (audio_tsc2101_read(TSC2101_AUDIO_CTRL_2) & AC2_KCLEN) { + udelay(3); + uTryVal++; + if (uTryVal > 2000) { + printk(KERN_ERR + "KEYCLICK TIMED OUT! freq val=%d, POSSIBLE ERROR!\n", + freq); + printk(KERN_INFO + "uTryVal == %d: Read back new reg val= 0x%x\n", + uTryVal, + audio_tsc2101_read + (TSC2101_AUDIO_CTRL_2)); + /* clear */ + audio_tsc2101_write(TSC2101_AUDIO_CTRL_2, 0x00); + break; + } + } + } + /* put the old value back */ + audio_tsc2101_write(TSC2101_AUDIO_CTRL_2, old_reg_val); + printk(KERN_INFO " KEYCLICK TEST COMPLETE\n"); + +} /* End of tsc2101_testkeyclick */ + +#endif /* TEST_KEYCLICK */ + +/********************************************************************************* + * TONEGEN: + * This is a test to generate a rather unpleasant sound.. + * verifies if the mcbsp is active (requires MCBSP_DIRECT_RW to be active on McBSP) + * + ********************************************************************************/ +#ifdef TONE_GEN +/* Generates a shrill tone */ +u16 tone[] = { + 0x0ce4, 0x0ce4, 0x1985, 0x1985, 0x25A1, 0x25A1, 0x30FD, 0x30FE, + 0x3B56, 0x3B55, 0x447A, 0x447A, 0x4C3B, 0x4C3C, 0x526D, 0x526C, + 0x56F1, 0x56F1, 0x59B1, 0x59B1, 0x5A9E, 0x5A9D, 0x59B1, 0x59B2, + 0x56F3, 0x56F2, 0x526D, 0x526D, 0x4C3B, 0x4C3B, 0x447C, 0x447C, + 0x3B5A, 0x3B59, 0x30FE, 0x30FE, 0x25A5, 0x25A6, 0x1989, 0x198A, + 0x0CE5, 0x0CE3, 0x0000, 0x0000, 0xF31C, 0xF31C, 0xE677, 0xE676, + 0xDA5B, 0xDA5B, 0xCF03, 0xCF03, 0xC4AA, 0xC4AA, 0xBB83, 0xBB83, + 0xB3C5, 0xB3C5, 0xAD94, 0xAD94, 0xA90D, 0xA90E, 0xA64F, 0xA64E, + 0xA562, 0xA563, 0xA64F, 0xA64F, 0xA910, 0xA90F, 0xAD93, 0xAD94, + 0xB3C4, 0xB3C4, 0xBB87, 0xBB86, 0xC4AB, 0xC4AB, 0xCF03, 0xCF03, + 0xDA5B, 0xDA5A, 0xE67B, 0xE67B, 0xF31B, 0xF3AC, 0x0000, 0x0000, + 0x0CE4, 0x0CE4, 0x1985, 0x1985, 0x25A1, 0x25A1, 0x30FD, 0x30FE, + 0x3B56, 0x3B55, 0x447A, 0x447A, 0x4C3B, 0x4C3C, 0x526D, 0x526C, + 0x56F1, 0x56F1, 0x59B1, 0x59B1, 0x5A9E, 0x5A9D, 0x59B1, 0x59B2, + 0x56F3, 0x56F2, 0x526D, 0x526D, 0x4C3B, 0x4C3B, 0x447C, 0x447C, + 0x3B5A, 0x3B59, 0x30FE, 0x30FE, 0x25A5, 0x25A6, 0x1989, 0x198A, + 0x0CE5, 0x0CE3, 0x0000, 0x0000, 0xF31C, 0xF31C, 0xE677, 0xE676, + 0xDA5B, 0xDA5B, 0xCF03, 0xCF03, 0xC4AA, 0xC4AA, 0xBB83, 0xBB83, + 0xB3C5, 0xB3C5, 0xAD94, 0xAD94, 0xA90D, 0xA90E, 0xA64F, 0xA64E, + 0xA562, 0xA563, 0xA64F, 0xA64F, 0xA910, 0xA90F, 0xAD93, 0xAD94, + 0xB3C4, 0xB3C4, 0xBB87, 0xBB86, 0xC4AB, 0xC4AB, 0xCF03, 0xCF03, + 0xDA5B, 0xDA5A, 0xE67B, 0xE67B, 0xF31B, 0xF3AC, 0x0000, 0x0000, + 0x0CE4, 0x0CE4, 0x1985, 0x1985, 0x25A1, 0x25A1, 0x30FD, 0x30FE, + 0x3B56, 0x3B55, 0x447A, 0x447A, 0x4C3B, 0x4C3C, 0x526D, 0x526C, + 0x56F1, 0x56F1, 0x59B1, 0x59B1, 0x5A9E, 0x5A9D, 0x59B1, 0x59B2, + 0x56F3, 0x56F2, 0x526D, 0x526D, 0x4C3B, 0x4C3B, 0x447C, 0x447C, + 0x3B5A, 0x3B59, 0x30FE, 0x30FE, 0x25A5, 0x25A6, 0x1989, 0x198A, + 0x0CE5, 0x0CE3, 0x0000, 0x0000, 0xF31C, 0xF31C, 0xE677, 0xE676, + 0xDA5B, 0xDA5B, 0xCF03, 0xCF03, 0xC4AA, 0xC4AA, 0xBB83, 0xBB83, + 0xB3C5, 0xB3C5, 0xAD94, 0xAD94, 0xA90D, 0xA90E, 0xA64F, 0xA64E, + 0xA562, 0xA563, 0xA64F, 0xA64F, 0xA910, 0xA90F, 0xAD93, 0xAD94, + 0xB3C4, 0xB3C4, 0xBB87, 0xBB86, 0xC4AB, 0xC4AB, 0xCF03, 0xCF03, + 0xDA5B, 0xDA5A, 0xE67B, 0xE67B, 0xF31B, 0xF3AC, 0x0000, 0x0000 +}; + +void toneGen(void) +{ + int count = 0; + int ret = 0; + printk(KERN_INFO "TONE GEN TEST :"); + + for (count = 0; count < 5000; count++) { + int bytes; + for (bytes = 0; bytes < sizeof(tone) / 2; bytes++) { + ret = omap_mcbsp_pollwrite(AUDIO_MCBSP, tone[bytes]); + if (ret == -1) { + /* retry */ + bytes--; + } else if (ret == -2) { + printk(KERN_INFO "ERROR:bytes=%d\n", bytes); + return; + } + } + } + printk(KERN_INFO "SUCCESS\n"); +} + +#endif /* End of TONE_GEN */ + +/********************************************************************************* + * + * TSC_DUMP_REGISTERS: + * This will dump the entire register set of Page 2 tsc2101. + * Useful for major goof ups + * + ********************************************************************************/ +#ifdef TSC_DUMP_REGISTERS +static void tsc2101_dumpRegisters(void) +{ + int i = 0; + u16 data = 0; + printk("TSC 2101 Register dump for Page 2 \n"); + for (i = 0; i < 0x27; i++) { + data = audio_tsc2101_read(i); + printk(KERN_INFO "Register[%x]=0x%04x\n", i, data); + + } +} +#endif /* End of #ifdef TSC_DUMP_REGISTERS */ + +#ifdef PROC_SUPPORT +static int codec_start(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + omap_tsc2101_enable(); + tsc2101_start(); + printk("Codec initialization done.\n"); + return 0; +} +static int codec_stop(char *buf, char **start, off_t offset, int count, + int *eof, void *data) +{ + + omap_tsc2101_disable(); + audio_tsc2101_write(TSC2101_CODEC_POWER_CTRL, + ~(CPC_SP1PWDN | CPC_SP2PWDN | CPC_BASSBC)); + printk("Codec shutdown.\n"); + return 0; +} +#endif + +/********************************************************************************* + * + * Other misc management, registration etc + * + ********************************************************************************/ +module_init(audio_tsc2101_init); +module_exit(audio_tsc2101_exit); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION + ("Glue audio driver for the TI OMAP1610/OMAP1710 TSC2101 codec."); +MODULE_LICENSE("GPL"); diff --git a/sound/oss/omap-audio.c b/sound/oss/omap-audio.c new file mode 100644 index 00000000000..38857007e70 --- /dev/null +++ b/sound/oss/omap-audio.c @@ -0,0 +1,1163 @@ +/* + * linux/sound/oss/omap-audio.c + * + * Common audio handling for the OMAP processors + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * Copyright (C) 2000, 2001 Nicolas Pitre + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History: + * + * 2004/08/12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms + * + * 2004-11-01 Nishanth Menon - modified to support 16xx and 17xx + * platform multi channel chaining. + * + * 2004-11-04 Nishanth Menon - Added support for power management + * + * 2004-12-17 Nishanth Menon - Provided proper module handling support + */ + +/***************************** INCLUDES ************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "omap-audio-dma-intfc.h" +#include "omap-audio.h" + +/***************************** MACROS ************************************/ + +#undef DEBUG +//#define DEBUG +#ifdef DEBUG +#define DPRINTK printk +#define FN_IN printk("[omap_audio.c:[%s] start\n", __FUNCTION__) +#define FN_OUT(n) printk("[omap_audio.c:[%s] end(%d)\n", __FUNCTION__ , n) +#else +#define DPRINTK( x... ) +#define FN_IN +#define FN_OUT(x) +#endif + +#define OMAP_AUDIO_NAME "omap-audio" +#define AUDIO_NBFRAGS_DEFAULT 8 +#define AUDIO_FRAGSIZE_DEFAULT 8192 + +/* HACK ALERT!: These values will bave to be tuned as this is a trade off b/w + * Sampling Rate vs buffer size and delay we are prepared to do before giving up + */ +#define MAX_QUEUE_FULL_RETRIES 1000000 +#define QUEUE_WAIT_TIME 10 + +#define AUDIO_ACTIVE(state) ((state)->rd_ref || (state)->wr_ref) + +#define SPIN_ADDR (dma_addr_t)0 +#define SPIN_SIZE 2048 + +/***************************** MODULES SPECIFIC FUNCTION PROTOTYPES ********************/ + +static int audio_write(struct file *file, const char __user *buffer, + size_t count, loff_t * ppos); + +static int audio_read(struct file *file, char __user *buffer, size_t count, + loff_t * ppos); + +static int audio_mmap(struct file *file, struct vm_area_struct *vma); + +static unsigned int audio_poll(struct file *file, + struct poll_table_struct *wait); + +static loff_t audio_llseek(struct file *file, loff_t offset, int origin); + +static int audio_ioctl(struct inode *inode, struct file *file, uint cmd, + ulong arg); + +static int audio_open(struct inode *inode, struct file *file); + +static int audio_release(struct inode *inode, struct file *file); + +static int audio_probe(struct platform_device *pdev); + +static int audio_remove(struct platform_device *pdev); + +static void audio_shutdown(struct platform_device *pdev); + +static int audio_suspend(struct platform_device *pdev, pm_message_t mesg); + +static int audio_resume(struct platform_device *pdev); + +static void audio_free(struct device *dev); + +/***************************** Data Structures **********************************/ + +/* + * The function pointer set to be registered by the codec. + */ +static audio_state_t audio_state = { NULL }; + +/* DMA Call back function */ +static dma_callback_t audio_dma_callback = NULL; + +/* File Ops structure */ +static struct file_operations omap_audio_fops = { + .open = audio_open, + .release = audio_release, + .write = audio_write, + .read = audio_read, + .mmap = audio_mmap, + .poll = audio_poll, + .ioctl = audio_ioctl, + .llseek = audio_llseek, + .owner = THIS_MODULE +}; + +/* Driver information */ +static struct platform_driver omap_audio_driver = { + .probe = audio_probe, + .remove = audio_remove, + .suspend = audio_suspend, + .shutdown = audio_shutdown, + .resume = audio_resume, + .driver = { + .name = OMAP_AUDIO_NAME, + }, +}; + +/* Device Information */ +static struct platform_device omap_audio_device = { + .name = OMAP_AUDIO_NAME, + .dev = { + .driver_data = &audio_state, + .release = audio_free, + }, + .id = 0, +}; + +/***************************** GLOBAL FUNCTIONs **********************************/ + +/* Power Management Functions for Linux Device Model */ +/* DEBUG PUPOSES ONLY! */ +#ifdef CONFIG_PM +//#undef CONFIG_PM +#endif + +#ifdef CONFIG_PM +/********************************************************************************* + * + * audio_ldm_suspend(): Suspend operation + * + *********************************************************************************/ +static int audio_ldm_suspend(void *data) +{ + audio_state_t *state = data; + + FN_IN; + + /* + * Reject the suspend request if we are already actively transmitting data + * Rationale: We dont want to be suspended while in the middle of a call! + */ + if (AUDIO_ACTIVE(state) && state->hw_init) { + printk(KERN_ERR "Audio device Active, Cannot Suspend"); + return -EPERM; +#if 0 + /* NOTE: + * This Piece of code is commented out in hope + * That one day we would need to suspend the device while + * audio operations are in progress and resume the operations + * once the resume is done. + * This is just a sample implementation of how it could be done. + * Currently NOT SUPPORTED + */ + audio_stream_t *is = state->input_stream; + audio_stream_t *os = state->output_stream; + int stopstate; + if (is && is->buffers) { + printk("IS Suspend\n"); + stopstate = is->stopped; + audio_stop_dma(is); + DMA_CLEAR(is); + is->dma_spinref = 0; + is->stopped = stopstate; + } + if (os && os->buffers) { + printk("OS Suspend\n"); + stopstate = os->stopped; + audio_stop_dma(os); + DMA_CLEAR(os); + os->dma_spinref = 0; + os->stopped = stopstate; + } +#endif + } + + FN_OUT(0); + return 0; +} + +/********************************************************************************* + * + * audio_ldm_resume(): Resume Operations + * + *********************************************************************************/ +static int audio_ldm_resume(void *data) +{ + audio_state_t *state = data; + + FN_IN; + if (AUDIO_ACTIVE(state) && state->hw_init) { + /* Should never occur - since we never suspend with active state */ + BUG(); + return -EPERM; +#if 0 + /* NOTE: + * This Piece of code is commented out in hope + * That one day we would need to suspend the device while + * audio operations are in progress and resume the operations + * once the resume is done. + * This is just a sample implementation of how it could be done. + * Currently NOT SUPPORTED + */ + audio_stream_t *is = state->input_stream; + audio_stream_t *os = state->output_stream; + if (os && os->buffers) { + printk("OS Resume\n"); + audio_reset(os); + audio_process_dma(os); + } + if (is && is->buffers) { + printk("IS Resume\n"); + audio_reset(is); + audio_process_dma(is); + } +#endif + } + FN_OUT(0); + return 0; +} +#endif /* End of #ifdef CONFIG_PM */ + +/********************************************************************************* + * + * audio_free(): The Audio driver release function + * This is a dummy function required by the platform driver + * + *********************************************************************************/ +static void audio_free(struct device *dev) +{ + /* Nothing to Release! */ +} + +/********************************************************************************* + * + * audio_probe(): The Audio driver probe function + * WARNING!!!! : It is expected that the codec would have registered with us by now + * + *********************************************************************************/ +static int audio_probe(struct platform_device *pdev) +{ + int ret; + FN_IN; + if (!audio_state.hw_probe) { + printk(KERN_ERR "Probe Function Not Registered\n"); + return -ENODEV; + } + ret = audio_state.hw_probe(); + FN_OUT(ret); + return ret; +} + +/********************************************************************************* + * + * audio_remove() Function to handle removal operations + * + *********************************************************************************/ +static int audio_remove(struct platform_device *pdev) +{ + FN_IN; + if (audio_state.hw_remove) { + audio_state.hw_remove(); + } + FN_OUT(0); + return 0; +} + +/********************************************************************************* + * + * audio_shutdown(): Function to handle shutdown operations + * + *********************************************************************************/ +static void audio_shutdown(struct platform_device *pdev) +{ + FN_IN; + if (audio_state.hw_cleanup) { + audio_state.hw_cleanup(); + } + FN_OUT(0); + return; +} + +/********************************************************************************* + * + * audio_suspend(): Function to handle suspend operations + * + *********************************************************************************/ +static int audio_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + int ret = 0; + +#ifdef CONFIG_PM + void *data = pdev->dev.driver_data; + FN_IN; + if (audio_state.hw_suspend) { + ret = audio_ldm_suspend(data); + if (ret == 0) + ret = audio_state.hw_suspend(); + } + if (ret) { + printk(KERN_INFO "Audio Suspend Failed \n"); + } else { + printk(KERN_INFO "Audio Suspend Success \n"); + } +#endif /* CONFIG_PM */ + + FN_OUT(ret); + return ret; +} + +/********************************************************************************* + * + * audio_resume(): Function to handle resume operations + * + *********************************************************************************/ +static int audio_resume(struct platform_device *pdev) +{ + int ret = 0; + +#ifdef CONFIG_PM + void *data = pdev->dev.driver_data; + FN_IN; + if (audio_state.hw_resume) { + ret = audio_ldm_resume(data); + if (ret == 0) + ret = audio_state.hw_resume(); + } + if (ret) { + printk(KERN_INFO " Audio Resume Failed \n"); + } else { + printk(KERN_INFO " Audio Resume Success \n"); + } +#endif /* CONFIG_PM */ + + FN_OUT(ret); + return ret; +} + +/********************************************************************************* + * + * audio_get_fops(): Return the fops required to get the function pointers of + * OMAP Audio Driver + * + *********************************************************************************/ +struct file_operations *audio_get_fops(void) +{ + FN_IN; + FN_OUT(0); + return &omap_audio_fops; +} + +/********************************************************************************* + * + * audio_register_codec(): Register a Codec fn points using this function + * WARNING!!!!! : Codecs should ensure that they do so! no sanity checks + * during runtime is done due to obvious performance + * penalties. + * + *********************************************************************************/ +int audio_register_codec(audio_state_t * codec_state) +{ + int ret; + FN_IN; + + /* We dont handle multiple codecs now */ + if (audio_state.hw_init) { + printk(KERN_ERR " Codec Already registered\n"); + return -EPERM; + } + + /* Grab the dma Callback */ + audio_dma_callback = audio_get_dma_callback(); + if (!audio_dma_callback) { + printk(KERN_ERR "Unable to get call back function\n"); + return -EPERM; + } + + /* Sanity checks */ + if (!codec_state) { + printk(KERN_ERR "NULL ARGUMENT!\n"); + return -EPERM; + } + + if (!codec_state->hw_probe || !codec_state->hw_init + || !codec_state->hw_shutdown || !codec_state->client_ioctl) { + printk(KERN_ERR + "Required Fn Entry point Missing probe=%p init=%p,down=%p,ioctl=%p!\n", + codec_state->hw_probe, codec_state->hw_init, + codec_state->hw_shutdown, codec_state->client_ioctl); + return -EPERM; + } + + memcpy(&audio_state, codec_state, sizeof(audio_state_t)); + sema_init(&audio_state.sem, 1); + + ret = platform_device_register(&omap_audio_device); + if (ret != 0) { + printk(KERN_ERR "Platform dev_register failed =%d\n", ret); + ret = -ENODEV; + goto register_out; + } + + ret = platform_driver_register(&omap_audio_driver); + if (ret != 0) { + printk(KERN_ERR "Device Register failed =%d\n", ret); + ret = -ENODEV; + platform_device_unregister(&omap_audio_device); + goto register_out; + } + + register_out: + + FN_OUT(ret); + return ret; +} + +/********************************************************************************* + * + * audio_unregister_codec(): Un-Register a Codec using this function + * + *********************************************************************************/ +int audio_unregister_codec(audio_state_t * codec_state) +{ + FN_IN; + + /* We dont handle multiple codecs now */ + if (!audio_state.hw_init) { + printk(KERN_ERR " No Codec registered\n"); + return -EPERM; + } + /* Security check */ + if (audio_state.hw_init != codec_state->hw_init) { + printk(KERN_ERR + " Attempt to unregister codec which was not registered with us\n"); + return -EPERM; + } + + platform_driver_unregister(&omap_audio_driver); + platform_device_unregister(&omap_audio_device); + + memset(&audio_state, 0, sizeof(audio_state_t)); + + FN_OUT(0); + return 0; +} + +/***************************** MODULES SPECIFIC FUNCTION *************************/ + +/********************************************************************************* + * + * audio_write(): Exposed to write() call + * + *********************************************************************************/ +static int +audio_write(struct file *file, const char __user *buffer, + size_t count, loff_t * ppos) +{ + const char __user *buffer0 = buffer; + audio_state_t *state = file->private_data; + audio_stream_t *s = state->output_stream; + int chunksize, ret = 0; + + DPRINTK("audio_write: count=%d\n", count); + if (*ppos != file->f_pos) { + printk("FPOS not ppos ppos=0x%x fpos =0x%x\n", (u32) * ppos, + (u32) file->f_pos); + return -ESPIPE; + } + if (s->mapped) { + printk("s already mapped\n"); + return -ENXIO; + } + if (!s->buffers && audio_setup_buf(s)) { + printk("NO MEMORY\n"); + return -ENOMEM; + } + + while (count > 0) { + audio_buf_t *b = &s->buffers[s->usr_head]; + + /* Wait for a buffer to become free */ + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (!s->wfc.done) + break; + } + ret = -ERESTARTSYS; + if (wait_for_completion_interruptible(&s->wfc)) + break; + + /* Feed the current buffer */ + chunksize = s->fragsize - b->offset; + if (chunksize > count) + chunksize = count; + DPRINTK("write %d to %d\n", chunksize, s->usr_head); + if (copy_from_user(b->data + b->offset, buffer, chunksize)) { + printk(KERN_ERR "Audio: CopyFrom User failed \n"); + complete(&s->wfc); + return -EFAULT; + } + + buffer += chunksize; + count -= chunksize; + b->offset += chunksize; + + if (b->offset < s->fragsize) { + complete(&s->wfc); + break; + } + + /* Update pointers and send current fragment to DMA */ + b->offset = 0; + if (++s->usr_head >= s->nbfrags) + s->usr_head = 0; + /* Add the num of frags pending */ + s->pending_frags++; + s->active = 1; + + audio_process_dma(s); + + } + + if ((buffer - buffer0)) + ret = buffer - buffer0; + DPRINTK("audio_write: return=%d\n", ret); + return ret; +} + +/********************************************************************************* + * + * audio_read(): Exposed as read() function + * + *********************************************************************************/ +static int +audio_read(struct file *file, char __user *buffer, size_t count, loff_t * ppos) +{ + char __user *buffer0 = buffer; + audio_state_t *state = file->private_data; + audio_stream_t *s = state->input_stream; + int chunksize, ret = 0; + unsigned long flags; + + DPRINTK("audio_read: count=%d\n", count); + + if (*ppos != file->f_pos) { + printk("AudioRead - FPOS not ppos ppos=0x%x fpos =0x%x\n", + (u32) * ppos, (u32) file->f_pos); + return -ESPIPE; + } + if (s->mapped) { + printk("AudioRead - s already mapped\n"); + return -ENXIO; + } + + if (!s->active) { + if (!s->buffers && audio_setup_buf(s)) { + printk("AudioRead - No Memory\n"); + return -ENOMEM; + } + audio_prime_rx(state); + } + + while (count > 0) { + audio_buf_t *b = &s->buffers[s->usr_head]; + + /* Wait for a buffer to become full */ + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (!s->wfc.done) + break; + } + ret = -ERESTARTSYS; + if (wait_for_completion_interruptible(&s->wfc)) + break; + + /* Grab data from the current buffer */ + chunksize = s->fragsize - b->offset; + if (chunksize > count) + chunksize = count; + DPRINTK("read %d from %d\n", chunksize, s->usr_head); + if (copy_to_user(buffer, b->data + b->offset, chunksize)) { + complete(&s->wfc); + return -EFAULT; + } + buffer += chunksize; + count -= chunksize; + b->offset += chunksize; + if (b->offset < s->fragsize) { + complete(&s->wfc); + break; + } + + /* Update pointers and return current fragment to DMA */ + local_irq_save(flags); + b->offset = 0; + if (++s->usr_head >= s->nbfrags) + s->usr_head = 0; + + s->pending_frags++; + local_irq_restore(flags); + audio_process_dma(s); + + } + + if ((buffer - buffer0)) + ret = buffer - buffer0; + DPRINTK("audio_read: return=%d\n", ret); + return ret; +} + +/********************************************************************************* + * + * audio_mmap(): Exposed as mmap Function + * !!WARNING: Still under development + * + *********************************************************************************/ +static int audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + audio_state_t *state = file->private_data; + audio_stream_t *s; + unsigned long size, vma_addr; + int i, ret; + + FN_IN; + if (vma->vm_pgoff != 0) + return -EINVAL; + + if (vma->vm_flags & VM_WRITE) { + if (!state->wr_ref) + return -EINVAL;; + s = state->output_stream; + } else if (vma->vm_flags & VM_READ) { + if (!state->rd_ref) + return -EINVAL; + s = state->input_stream; + } else + return -EINVAL; + + if (s->mapped) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size != s->fragsize * s->nbfrags) + return -EINVAL; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + vma_addr = vma->vm_start; + for (i = 0; i < s->nbfrags; i++) { + audio_buf_t *buf = &s->buffers[i]; + if (!buf->master) + continue; + ret = + remap_pfn_range(vma, vma_addr, buf->dma_addr >> PAGE_SHIFT, + buf->master, vma->vm_page_prot); + if (ret) + return ret; + vma_addr += buf->master; + } + s->mapped = 1; + + FN_OUT(0); + return 0; +} + +/********************************************************************************* + * + * audio_poll(): Exposed as poll function + * + *********************************************************************************/ +static unsigned int +audio_poll(struct file *file, struct poll_table_struct *wait) +{ + audio_state_t *state = file->private_data; + audio_stream_t *is = state->input_stream; + audio_stream_t *os = state->output_stream; + unsigned int mask = 0; + + DPRINTK("audio_poll(): mode=%s%s\n", + (file->f_mode & FMODE_READ) ? "r" : "", + (file->f_mode & FMODE_WRITE) ? "w" : ""); + + if (file->f_mode & FMODE_READ) { + /* Start audio input if not already active */ + if (!is->active) { + if (!is->buffers && audio_setup_buf(is)) + return -ENOMEM; + audio_prime_rx(state); + } + poll_wait(file, &is->wq, wait); + } + + if (file->f_mode & FMODE_WRITE) { + if (!os->buffers && audio_setup_buf(os)) + return -ENOMEM; + poll_wait(file, &os->wq, wait); + } + + if (file->f_mode & FMODE_READ) + if ((is->mapped && is->bytecount > 0) || + (!is->mapped && is->wfc.done > 0)) + mask |= POLLIN | POLLRDNORM; + + if (file->f_mode & FMODE_WRITE) + if ((os->mapped && os->bytecount > 0) || + (!os->mapped && os->wfc.done > 0)) + mask |= POLLOUT | POLLWRNORM; + + DPRINTK("audio_poll() returned mask of %s%s\n", + (mask & POLLIN) ? "r" : "", (mask & POLLOUT) ? "w" : ""); + + FN_OUT(mask); + return mask; +} + +/********************************************************************************* + * + * audio_llseek(): Exposed as lseek() function. + * + *********************************************************************************/ +static loff_t audio_llseek(struct file *file, loff_t offset, int origin) +{ + FN_IN; + FN_OUT(0); + return -ESPIPE; +} + +/********************************************************************************* + * + * audio_ioctl(): Handles generic ioctls. If there is a request for something this + * fn cannot handle, its then given to client specific ioctl routine, that will take + * up platform specific requests + * + *********************************************************************************/ +static int +audio_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) +{ + audio_state_t *state = file->private_data; + audio_stream_t *os = state->output_stream; + audio_stream_t *is = state->input_stream; + long val; + + DPRINTK(__FILE__ " audio_ioctl 0x%08x\n", cmd); + + /* dispatch based on command */ + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int __user *)arg); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(os->fragsize, (int __user *)arg); + else + return put_user(is->fragsize, (int __user *)arg); + + case SNDCTL_DSP_GETCAPS: + val = DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP; + if (is && os) + val |= DSP_CAP_DUPLEX; + FN_OUT(1); + return put_user(val, (int __user *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (long __user *)arg)) { + FN_OUT(2); + return -EFAULT; + } + if (file->f_mode & FMODE_READ) { + int ret = audio_set_fragments(is, val); + if (ret < 0) { + FN_OUT(3); + return ret; + } + ret = put_user(ret, (int __user *)arg); + if (ret) { + FN_OUT(4); + return ret; + } + } + if (file->f_mode & FMODE_WRITE) { + int ret = audio_set_fragments(os, val); + if (ret < 0) { + FN_OUT(5); + return ret; + } + ret = put_user(ret, (int __user *)arg); + if (ret) { + FN_OUT(6); + return ret; + } + } + FN_OUT(7); + return 0; + + case SNDCTL_DSP_SYNC: + FN_OUT(8); + return audio_sync(file); + + case SNDCTL_DSP_SETDUPLEX: + FN_OUT(9); + return 0; + + case SNDCTL_DSP_POST: + FN_OUT(10); + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && is->active && !is->stopped) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && os->active && !os->stopped) + val |= PCM_ENABLE_OUTPUT; + FN_OUT(11); + return put_user(val, (int __user *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int __user *)arg)) { + FN_OUT(12); + return -EFAULT; + } + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + unsigned long flags; + if (!is->active) { + if (!is->buffers && audio_setup_buf(is)) { + FN_OUT(13); + return -ENOMEM; + } + audio_prime_rx(state); + } + local_irq_save(flags); + is->stopped = 0; + local_irq_restore(flags); + audio_process_dma(is); + + } else { + audio_stop_dma(is); + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + unsigned long flags; + if (!os->buffers && audio_setup_buf(os)) { + FN_OUT(14); + return -ENOMEM; + } + local_irq_save(flags); + if (os->mapped && !os->pending_frags) { + os->pending_frags = os->nbfrags; + init_completion(&os->wfc); + os->wfc.done = 0; + os->active = 1; + } + os->stopped = 0; + local_irq_restore(flags); + audio_process_dma(os); + + } else { + audio_stop_dma(os); + } + } + FN_OUT(15); + return 0; + + case SNDCTL_DSP_GETOPTR: + case SNDCTL_DSP_GETIPTR: + { + count_info inf = { 0, }; + audio_stream_t *s = + (cmd == SNDCTL_DSP_GETOPTR) ? os : is; + int bytecount, offset; + unsigned long flags; + + if ((s == is && !(file->f_mode & FMODE_READ)) || + (s == os && !(file->f_mode & FMODE_WRITE))) { + FN_OUT(16); + return -EINVAL; + } + if (s->active) { + local_irq_save(flags); + offset = audio_get_dma_pos(s); + inf.ptr = s->dma_tail * s->fragsize + offset; + bytecount = s->bytecount + offset; + s->bytecount = -offset; + inf.blocks = s->fragcount; + s->fragcount = 0; + local_irq_restore(flags); + if (bytecount < 0) + bytecount = 0; + inf.bytes = bytecount; + } + FN_OUT(17); + return copy_to_user((void __user *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_GETOSPACE: + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info inf = { 0, }; + audio_stream_t *s = + (cmd == SNDCTL_DSP_GETOSPACE) ? os : is; + + if ((s == is && !(file->f_mode & FMODE_READ)) || + (s == os && !(file->f_mode & FMODE_WRITE))) { + FN_OUT(18); + return -EINVAL; + } + if (!s->buffers && audio_setup_buf(s)) { + FN_OUT(19); + return -ENOMEM; + } + inf.bytes = s->wfc.done * s->fragsize; + + inf.fragments = inf.bytes / s->fragsize; + inf.fragsize = s->fragsize; + inf.fragstotal = s->nbfrags; + FN_OUT(20); + return copy_to_user((void __user *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + FN_OUT(21); + return 0; + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_READ) { + audio_reset(is); + if (state->need_tx_for_rx) { + unsigned long flags; + local_irq_save(flags); + os->spin_idle = 0; + local_irq_restore(flags); + } + } + if (file->f_mode & FMODE_WRITE) { + audio_reset(os); + } + FN_OUT(22); + return 0; + + default: + /* + * Let the client of this module handle the + * non generic ioctls + */ + FN_OUT(23); + return state->client_ioctl(inode, file, cmd, arg); + } + + FN_OUT(0); + return 0; +} + +/********************************************************************************* + * + * audio_open(): Exposed as open() function + * + *********************************************************************************/ +static int audio_open(struct inode *inode, struct file *file) +{ + audio_state_t *state = (&audio_state); + audio_stream_t *os = state->output_stream; + audio_stream_t *is = state->input_stream; + int err, need_tx_dma; + static unsigned char tsc2101_init_flag = 0; + + FN_IN; + + /* Lock the module */ + if (!try_module_get(THIS_MODULE)) { + printk(KERN_CRIT "Failed to get module\n"); + return -ESTALE; + } + /* Lock the codec module */ + if (!try_module_get(state->owner)) { + printk(KERN_CRIT "Failed to get codec module\n"); + module_put(THIS_MODULE); + return -ESTALE; + } + + down(&state->sem); + + /* access control */ + err = -ENODEV; + if ((file->f_mode & FMODE_WRITE) && !os) + goto out; + if ((file->f_mode & FMODE_READ) && !is) + goto out; + err = -EBUSY; + if ((file->f_mode & FMODE_WRITE) && state->wr_ref) + goto out; + if ((file->f_mode & FMODE_READ) && state->rd_ref) + goto out; + err = -EINVAL; + if ((file->f_mode & FMODE_READ) && state->need_tx_for_rx && !os) + goto out; + + /* request DMA channels */ + need_tx_dma = ((file->f_mode & FMODE_WRITE) || + ((file->f_mode & FMODE_READ) && state->need_tx_for_rx)); + if (state->wr_ref || (state->rd_ref && state->need_tx_for_rx)) + need_tx_dma = 0; + if (need_tx_dma) { + DMA_REQUEST(err, os, audio_dma_callback); + if (err < 0) + goto out; + } + if (file->f_mode & FMODE_READ) { + DMA_REQUEST(err, is, audio_dma_callback); + if (err < 0) { + if (need_tx_dma) + DMA_FREE(os); + goto out; + } + } + + /* now complete initialisation */ + if (!AUDIO_ACTIVE(state)) { + if (state->hw_init && !tsc2101_init_flag) { + state->hw_init(state->data); + tsc2101_init_flag = 0; + + } + + } + + if ((file->f_mode & FMODE_WRITE)) { + state->wr_ref = 1; + audio_reset(os); + os->fragsize = AUDIO_FRAGSIZE_DEFAULT; + os->nbfrags = AUDIO_NBFRAGS_DEFAULT; + os->mapped = 0; + init_waitqueue_head(&os->wq); + } + + if (file->f_mode & FMODE_READ) { + state->rd_ref = 1; + audio_reset(is); + is->fragsize = AUDIO_FRAGSIZE_DEFAULT; + is->nbfrags = AUDIO_NBFRAGS_DEFAULT; + is->mapped = 0; + init_waitqueue_head(&is->wq); + } + + file->private_data = state; + err = 0; + + out: + up(&state->sem); + if (err) { + module_put(state->owner); + module_put(THIS_MODULE); + } + FN_OUT(err); + return err; +} + +/********************************************************************************* + * + * audio_release(): Exposed as release function() + * + *********************************************************************************/ +static int audio_release(struct inode *inode, struct file *file) +{ + audio_state_t *state = file->private_data; + audio_stream_t *os = state->output_stream; + audio_stream_t *is = state->input_stream; + + FN_IN; + + down(&state->sem); + + if (file->f_mode & FMODE_READ) { + audio_discard_buf(is); + DMA_FREE(is); + is->dma_spinref = 0; + if (state->need_tx_for_rx) { + os->spin_idle = 0; + if (!state->wr_ref) { + DMA_FREE(os); + os->dma_spinref = 0; + } + } + state->rd_ref = 0; + } + + if (file->f_mode & FMODE_WRITE) { + audio_sync(file); + audio_discard_buf(os); + if (!state->need_tx_for_rx || !state->rd_ref) { + DMA_FREE(os); + os->dma_spinref = 0; + } + state->wr_ref = 0; + } + + if (!AUDIO_ACTIVE(state)) { + if (state->hw_shutdown) + state->hw_shutdown(state->data); + } + + up(&state->sem); + + module_put(state->owner); + module_put(THIS_MODULE); + + FN_OUT(0); + return 0; +} + +EXPORT_SYMBOL(audio_register_codec); +EXPORT_SYMBOL(audio_unregister_codec); +EXPORT_SYMBOL(audio_get_fops); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("Common audio handling for OMAP processors"); +MODULE_LICENSE("GPL"); diff --git a/sound/oss/omap-audio.h b/sound/oss/omap-audio.h new file mode 100644 index 00000000000..58eff3130c3 --- /dev/null +++ b/sound/oss/omap-audio.h @@ -0,0 +1,125 @@ +/* + * linux/sound/oss/omap-audio.h + * + * Common audio handling for the OMAP processors + * + * Copyright (C) 2004 Texas Instruments, Inc. + * + * Copyright (C) 2000, 2001 Nicolas Pitre + * + * This package is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * History + * ------- + * 2004/08/12 Nishanth Menon - Modified to integrate Audio requirements on 1610,1710 platforms + * + * 2004/04/04 Nishanth menon - Added hooks for power management + * + * 2005/12/10 Dirk Behme - Added L/R Channel Interchange fix as proposed by Ajaya Babu + */ + +#ifndef __OMAP_AUDIO_H +#define __OMAP_AUDIO_H + +/* Requires dma.h */ +#include + +/* + * Buffer Management + */ +typedef struct { + int offset; /* current offset */ + char *data; /* points to actual buffer */ + dma_addr_t dma_addr; /* physical buffer address */ + int dma_ref; /* DMA refcount */ + int master; /* owner for buffer allocation, contain size when true */ +} audio_buf_t; + +/* + * Structure describing the data stream related information + */ +typedef struct { + char *id; /* identification string */ + audio_buf_t *buffers; /* pointer to audio buffer structures */ + u_int usr_head; /* user fragment index */ + u_int dma_head; /* DMA fragment index to go */ + u_int dma_tail; /* DMA fragment index to complete */ + u_int fragsize; /* fragment i.e. buffer size */ + u_int nbfrags; /* nbr of fragments i.e. buffers */ + u_int pending_frags; /* Fragments sent to DMA */ + int dma_dev; /* device identifier for DMA */ + +#ifdef OMAP_DMA_CHAINING_SUPPORT + lch_chain *dma_chain; + dma_regs_t *dma_regs; /* points to our DMA registers */ +#else + char started; /* to store if the chain was started or not */ + int dma_q_head; /* DMA Channel Q Head */ + int dma_q_tail; /* DMA Channel Q Tail */ + char dma_q_count; /* DMA Channel Q Count */ + char in_use; /* Is this is use? */ + int *lch; /* Chain of channels this stream is linked to */ +#endif + int input_or_output; /* Direction of this data stream */ + int bytecount; /* nbr of processed bytes */ + int fragcount; /* nbr of fragment transitions */ + struct completion wfc; /* wait for "nbfrags" fragment completion */ + wait_queue_head_t wq; /* for poll */ + int dma_spinref; /* DMA is spinning */ + unsigned mapped:1; /* mmap()'ed buffers */ + unsigned active:1; /* actually in progress */ + unsigned stopped:1; /* might be active but stopped */ + unsigned spin_idle:1; /* have DMA spin on zeros when idle */ + unsigned linked:1; /* dma channels linked */ + int (*hw_start)(void); /* interface to start HW interface, e.g. McBSP */ + int (*hw_stop)(void); /* interface to stop HW interface, e.g. McBSP */ +} audio_stream_t; + +/* + * State structure for one instance + */ +typedef struct { + struct module *owner; /* Codec module ID */ + audio_stream_t *output_stream; + audio_stream_t *input_stream; + unsigned rd_ref:1; /* open reference for recording */ + unsigned wr_ref:1; /* open reference for playback */ + unsigned need_tx_for_rx:1; /* if data must be sent while receiving */ + void *data; + void (*hw_init) (void *); + void (*hw_shutdown) (void *); + int (*client_ioctl) (struct inode *, struct file *, uint, ulong); + int (*hw_probe) (void); + void (*hw_remove) (void); + void (*hw_cleanup) (void); + int (*hw_suspend) (void); + int (*hw_resume) (void); + struct pm_dev *pm_dev; + struct semaphore sem; /* to protect against races in attach() */ +} audio_state_t; + +#ifdef AUDIO_PM +void audio_ldm_suspend(void *data); + +void audio_ldm_resume(void *data); + +#endif + +/* Register a Codec using this function */ +extern int audio_register_codec(audio_state_t * codec_state); +/* Un-Register a Codec using this function */ +extern int audio_unregister_codec(audio_state_t * codec_state); +/* Function to provide fops of omap audio driver */ +extern struct file_operations *audio_get_fops(void); +/* Function to initialize the device info for audio driver */ +extern int audio_dev_init(void); +/* Function to un-initialize the device info for audio driver */ +void audio_dev_uninit(void); + +#endif /* End of #ifndef __OMAP_AUDIO_H */