]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Extra omap code in linux-omap tree
authorTony Lindgren <tony@atomide.com>
Tue, 7 Apr 2009 23:22:17 +0000 (16:22 -0700)
committerTony Lindgren <tony@atomide.com>
Tue, 7 Apr 2009 23:26:33 +0000 (16:26 -0700)
This patch contains all the uncategorized patches not in
mainline. These patches will be reworked for mainline or
removed.

Signed-off-by: Tony Lindgren <tony@atomide.com>
203 files changed:
Documentation/arm/OMAP/README [new file with mode: 0644]
Documentation/arm/OMAP/gpio [new file with mode: 0644]
arch/arm/Kconfig
arch/arm/boot/compressed/Makefile
arch/arm/boot/compressed/head-omap.S [new file with mode: 0644]
arch/arm/include/asm/setup.h
arch/arm/kernel/head.S
arch/arm/mach-omap1/Kconfig
arch/arm/mach-omap1/Makefile
arch/arm/mach-omap1/board-h2-mmc.c
arch/arm/mach-omap1/board-h3-mmc.c
arch/arm/mach-omap1/board-h3.c
arch/arm/mach-omap1/board-nokia770.c
arch/arm/mach-omap2/Kconfig
arch/arm/mach-omap2/Makefile
arch/arm/mach-omap2/board-2430sdp-flash.c [new file with mode: 0644]
arch/arm/mach-omap2/board-2430sdp.c
arch/arm/mach-omap2/board-3430sdp-flash.c [new file with mode: 0644]
arch/arm/mach-omap2/board-3430sdp.c
arch/arm/mach-omap2/board-apollon-keys.c [new file with mode: 0644]
arch/arm/mach-omap2/board-apollon-mmc.c [new file with mode: 0644]
arch/arm/mach-omap2/board-apollon.c
arch/arm/mach-omap2/board-generic.c
arch/arm/mach-omap2/board-h4.c
arch/arm/mach-omap2/board-ldp.c
arch/arm/mach-omap2/board-n800-bt.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800-camera.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800-dsp.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800-flash.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800-mmc.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800-usb.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800.c [new file with mode: 0644]
arch/arm/mach-omap2/board-n800.h [new file with mode: 0644]
arch/arm/mach-omap2/board-n810.c [new file with mode: 0644]
arch/arm/mach-omap2/board-omap2evm.c [new file with mode: 0644]
arch/arm/mach-omap2/board-omap3beagle.c
arch/arm/mach-omap2/board-omap3evm-flash.c [new file with mode: 0644]
arch/arm/mach-omap2/board-omap3evm.c [new file with mode: 0644]
arch/arm/mach-omap2/board-omap3pandora.c
arch/arm/mach-omap2/board-overo.c
arch/arm/mach-omap2/board-rx51-flash.c [new file with mode: 0644]
arch/arm/mach-omap2/board-rx51-peripherals.c
arch/arm/mach-omap2/board-rx51-sdram.c [new file with mode: 0644]
arch/arm/mach-omap2/board-rx51-video.c [new file with mode: 0644]
arch/arm/mach-omap2/board-rx51.c
arch/arm/mach-omap2/devices.c
arch/arm/mach-omap2/id.c
arch/arm/mach-omap2/irq.c
arch/arm/mach-omap2/mmc-twl4030.c
arch/arm/mach-omap2/mmc-twl4030.h
arch/arm/mach-omap2/sdram-micron-mt46h32m32lf-6.h [new file with mode: 0644]
arch/arm/mach-omap2/sdram-qimonda-hyb18m512160af-6.h [new file with mode: 0644]
arch/arm/mach-omap2/twl4030-generic-scripts.c [new file with mode: 0644]
arch/arm/mach-omap2/twl4030-generic-scripts.h [new file with mode: 0644]
arch/arm/mach-omap2/usb-ehci.c [new file with mode: 0644]
arch/arm/mach-omap2/usb-musb.c
arch/arm/mach-omap2/usb-tusb6010.c
arch/arm/plat-omap/Kconfig
arch/arm/plat-omap/Makefile
arch/arm/plat-omap/bootreason.c [new file with mode: 0644]
arch/arm/plat-omap/common.c
arch/arm/plat-omap/component-version.c [new file with mode: 0644]
arch/arm/plat-omap/dma.c
arch/arm/plat-omap/dmtimer.c
arch/arm/plat-omap/fb.c
arch/arm/plat-omap/gpio-switch.c [new file with mode: 0644]
arch/arm/plat-omap/gpio.c
arch/arm/plat-omap/include/mach/board-nokia.h [new file with mode: 0644]
arch/arm/plat-omap/include/mach/board-rx51.h [new file with mode: 0644]
arch/arm/plat-omap/include/mach/board.h
arch/arm/plat-omap/include/mach/dma.h
arch/arm/plat-omap/include/mach/eac.h [deleted file]
arch/arm/plat-omap/include/mach/gpio-switch.h
arch/arm/plat-omap/include/mach/gpioexpander.h [deleted file]
arch/arm/plat-omap/include/mach/gpmc.h
arch/arm/plat-omap/include/mach/irqs.h
arch/arm/plat-omap/include/mach/keypad.h
arch/arm/plat-omap/include/mach/mmc.h
arch/arm/plat-omap/include/mach/sti.h [new file with mode: 0644]
arch/arm/plat-omap/include/mach/usb.h
arch/arm/plat-omap/include/mach/vmalloc.h
arch/arm/plat-omap/sram.c
drivers/Makefile
drivers/bluetooth/Kconfig
drivers/bluetooth/Makefile
drivers/bluetooth/brf6150.c [new file with mode: 0644]
drivers/bluetooth/brf6150.h [new file with mode: 0644]
drivers/bluetooth/hci_h4p/Makefile [new file with mode: 0644]
drivers/bluetooth/hci_h4p/core.c [new file with mode: 0644]
drivers/bluetooth/hci_h4p/fw-csr.c [new file with mode: 0644]
drivers/bluetooth/hci_h4p/fw-ti.c [new file with mode: 0644]
drivers/bluetooth/hci_h4p/fw.c [new file with mode: 0644]
drivers/bluetooth/hci_h4p/hci_h4p.h [new file with mode: 0644]
drivers/bluetooth/hci_h4p/sysfs.c [new file with mode: 0644]
drivers/bluetooth/hci_h4p/uart.c [new file with mode: 0644]
drivers/char/hw_random/omap-rng.c
drivers/crypto/Kconfig
drivers/crypto/Makefile
drivers/crypto/omap-sha1-md5.c [new file with mode: 0644]
drivers/hwmon/Kconfig
drivers/hwmon/Makefile
drivers/hwmon/omap34xx_temp.c [new file with mode: 0644]
drivers/hwmon/tsc210x_sensors.c [new file with mode: 0644]
drivers/i2c/Kconfig
drivers/i2c/busses/i2c-omap.c
drivers/i2c/chips/Kconfig
drivers/i2c/chips/Makefile
drivers/i2c/chips/tsl2563.c [new file with mode: 0644]
drivers/i2c/chips/twl4030-madc.c [new file with mode: 0644]
drivers/i2c/chips/twl4030-poweroff.c [new file with mode: 0644]
drivers/input/keyboard/Kconfig
drivers/input/keyboard/Makefile
drivers/input/keyboard/innovator_ps2.c [new file with mode: 0644]
drivers/input/keyboard/lm8323.c [new file with mode: 0644]
drivers/input/keyboard/omap-keypad.c
drivers/input/keyboard/tsc2301_kp.c [new file with mode: 0644]
drivers/input/keyboard/twl4030_keypad.c [new file with mode: 0644]
drivers/input/misc/Kconfig
drivers/input/misc/Makefile
drivers/input/misc/twl4030-pwrbutton.c [new file with mode: 0644]
drivers/input/touchscreen/Kconfig
drivers/input/touchscreen/Makefile
drivers/input/touchscreen/ads7846.c
drivers/input/touchscreen/tsc2005.c [new file with mode: 0644]
drivers/input/touchscreen/tsc210x_ts.c [new file with mode: 0644]
drivers/input/touchscreen/tsc2301_ts.c [new file with mode: 0644]
drivers/leds/Kconfig
drivers/leds/Makefile
drivers/leds/leds-lp5521.c [new file with mode: 0644]
drivers/leds/leds-omap-pwm.c [new file with mode: 0644]
drivers/leds/leds-omap.c [new file with mode: 0644]
drivers/mfd/Kconfig
drivers/mfd/Makefile
drivers/mfd/twl4030-core.c
drivers/mfd/twl4030-power.c [new file with mode: 0644]
drivers/misc/Kconfig
drivers/misc/Makefile
drivers/misc/sti/Makefile [new file with mode: 0644]
drivers/misc/sti/sdti.c [new file with mode: 0644]
drivers/misc/sti/sti-console.c [new file with mode: 0644]
drivers/misc/sti/sti-fifo.c [new file with mode: 0644]
drivers/misc/sti/sti-netlink.c [new file with mode: 0644]
drivers/misc/sti/sti.c [new file with mode: 0644]
drivers/mmc/host/Kconfig
drivers/mmc/host/omap.c
drivers/mmc/host/omap_hsmmc.c
drivers/mtd/cmdlinepart.c
drivers/mtd/maps/Kconfig
drivers/mtd/maps/Makefile
drivers/mtd/maps/omap_nor.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/omap-hw.c [new file with mode: 0644]
drivers/mtd/nand/omap-nand-flash.c [new file with mode: 0644]
drivers/mtd/nand/omap2.c [new file with mode: 0644]
drivers/net/irda/Kconfig
drivers/net/irda/Makefile
drivers/net/irda/omap-ir.c [new file with mode: 0644]
drivers/net/smc91x.c
drivers/power/Kconfig
drivers/power/Makefile
drivers/power/twl4030_bci_battery.c [new file with mode: 0644]
drivers/serial/8250.c
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/omap2_mcspi.c
drivers/spi/tsc210x.c [new file with mode: 0644]
drivers/spi/tsc2301-core.c [new file with mode: 0644]
drivers/video/omap/Kconfig
drivers/video/omap/Makefile
drivers/video/omap/blizzard.c
drivers/video/omap/dispc.c
drivers/video/omap/dispc.h
drivers/video/omap/lcd_2430sdp.c [new file with mode: 0644]
drivers/video/omap/lcd_ams_delta.c [new file with mode: 0644]
drivers/video/omap/lcd_apollon.c [new file with mode: 0644]
drivers/video/omap/lcd_ldp.c [new file with mode: 0644]
drivers/video/omap/lcd_mipid.c [new file with mode: 0644]
drivers/video/omap/lcd_omap2evm.c [new file with mode: 0644]
drivers/video/omap/lcd_omap3beagle.c [new file with mode: 0644]
drivers/video/omap/lcd_omap3evm.c [new file with mode: 0644]
drivers/video/omap/lcd_overo.c [new file with mode: 0644]
drivers/video/omap/lcd_p2.c [new file with mode: 0644]
drivers/video/omap/omapfb_main.c
drivers/video/omap/rfbi.c
drivers/video/omap/sossi.c
include/asm-arm/.gitignore
include/asm-arm/hardware/tsc2101.h [new file with mode: 0644]
include/linux/connector.h
include/linux/i2c/lm8323.h [new file with mode: 0644]
include/linux/i2c/lp5521.h [new file with mode: 0644]
include/linux/i2c/menelaus.h [new file with mode: 0644]
include/linux/i2c/twl4030-madc.h [new file with mode: 0644]
include/linux/i2c/twl4030.h
include/linux/netfilter_ipv4/ipt_IDLETIMER.h [new file with mode: 0644]
include/linux/serial_reg.h
include/linux/spi/ads7846.h
include/linux/spi/tsc2005.h [new file with mode: 0644]
include/linux/spi/tsc210x.h [new file with mode: 0644]
include/linux/spi/tsc2301.h [new file with mode: 0644]
net/ipv4/netfilter/Kconfig
net/ipv4/netfilter/Makefile
net/ipv4/netfilter/ipt_IDLETIMER.c [new file with mode: 0644]

diff --git a/Documentation/arm/OMAP/README b/Documentation/arm/OMAP/README
new file mode 100644 (file)
index 0000000..038d863
--- /dev/null
@@ -0,0 +1,376 @@
+
+                  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://www.denx.de/en/Software/GIT
+
+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 OMAP GIT tree, or by
+applying patches. The OMAP GIT tree has the most up to date sources
+and is the recommended one.
+
+- Using GIT and cloning OMAP GIT tree please follow the README at:
+
+http://www.muru.com/linux/omap/README_OMAP_GIT
+
+Hint: If you are sitting behind a firewall and have to use a proxy for 
+internet access, you can access GIT 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 GIT, 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 GIT or by patch, you
+should look into arch/arm/configs/ 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).
+
+Please refer http://www.muru.com/linux/omap/ to get latest information on the
+list of boards supported.
+
+/* 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[1/2]/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[1/2]/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[1/2]/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[1/2]/. 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[1/2]/io.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[1/2]/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[1/2]/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
+- Power Management
+- 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.
+
+------------------------------------------------------------------
+Last modified 15. March 2006
+The OMAP Linux Kernel Team
+Dirk Behme <dirk.behme@de.bosch.com>
diff --git a/Documentation/arm/OMAP/gpio b/Documentation/arm/OMAP/gpio
new file mode 100644 (file)
index 0000000..8a011ad
--- /dev/null
@@ -0,0 +1,285 @@
+
+                         OMAP GPIO API's HowTo
+                         =====================
+
+This document is a short summary how to use OMAP Linux GPIO API. It is
+mainly focussed on OMAP5912 OSK, but should fit with extensions (more
+or less GPIOs) to other OMAP processors as well.
+
+If anything is missing, is wrong, needs extension or update, please send
+update to Linux-omap-open-source@linux.omap.com.
+
+       *************************************************************
+
+       NOTICE:  these OMAP-specific interfaces are deprecated/obsolete.
+
+       See Documentation/gpio.txt for information on the standard
+       cross-platform GPIO interface.  All new code should use those
+       calls instead of the ones described here.
+
+       The only exception to that policy is the omap_cfg_reg() call,
+       which isn't a GPIO-specific interface; it configures how chip
+       functions are multiplexed to pins, with GPIO being only one
+       of those functions.
+
+       *************************************************************
+
+I. GPIO Modules/Banks
+---------------------
+
+OMAP5912 OSK has 64 GPIOs (general purpose IO pins). These are organized
+in four modules (banks) with 16 pins each. OMAP GPIO API doesn't distinguish
+between modules and numbers the pins from 0 - 63:
+
+A) GPIO MODULE/BANK 0 - PIN  0-15
+B) GPIO MODULE/BANK 1 - PIN 16-31
+C) GPIO MODULE/BANK 2 - PIN 32-47
+D) GPIO MODULE/BANK 3 - PIN 48-63
+
+See
+
+http://www-s.ti.com/sc/psheets/spru767a/spru767a.pdf
+
+for more details.
+
+II. GPIO API's
+--------------
+
+A) Include
+
+#include <asm/arch/gpio.h>
+
+B) omap_cfg_reg(xxxx);
+
+Description: Configure pin mux.
+
+Parameter: Pin to be configured for GPIO.
+
+Note: This function may only be necessary for some GPIO pins. Because OMAP
+      chip itself has less real hardware pins than necessary to use all
+      its functionality at the same time, some pins share different
+      functions (called pin multiplexing, short pin mux). E.g. one pin may
+      be used for serial interface *or* GPIO. Check if this is the case for
+      the GPIO you want to use and if you have to configure the pin mux.
+
+C) omap_request_gpio(int gpio)
+
+Description: Request GPIO to be used.
+
+Parameter: int gpio - GPIO PIN (Pin 0-63)
+
+Note: Using this function, you dont have to worry about banks/modules where
+      the gpio pin is.
+
+D) omap_set_gpio_direction(int gpio, int is_input)
+
+Description: This function is responsible for setting the gpio pin direction
+             (input or output).
+
+Parameter: int gpio - GPIO PIN (Pin 0-63)
+           int is_input - pin direction (0 = output, 1 = input)
+
+E) omap_set_gpio_dataout(int gpio, int enable)
+
+Description: This function is responsible for writing to a pin.
+
+Parameter: int gpio - GPIO PIN (Pin 0-63)
+           int enable - pin value (0 or 1)
+
+F) omap_get_gpio_datain(int gpio)
+
+Description: This function is responsible for reading pin values.
+
+Parameter: int gpio - GPIO PIN (Pin 0-63)
+
+G) omap_free_gpio(int gpio)
+
+Description: This function is responsible for freeing the pin used.
+
+Parameter: int gpio - GPIO PIN (Pin 0-63)
+
+H) OMAP_GPIO_IRQ(int gpio)
+
+Description: Returns the Interrupt number for the specified gpio pin.
+
+Parameter: int gpio - GPIO PIN (Pin 0-63)
+
+I) set_irq_type(unsigned int irq, unsigned int type)
+
+Description: This function is responsible for setting the type of interrupt
+             (RISING or FALLING).
+
+Parameter: unsigned int irq - The interrupt number for the gpio pin.
+           unsigned int type - (IRQT_RISING = rising, IRQT_FALLING= falling)
+
+
+III. Example
+------------
+
+1) Writing to gpio pin#3 a value 1 and reading the value of gpio pin#3.
+
+#include <asm/arch/gpio.h>
+
+int ret;                       /* Return value */
+
+omap_request_gpio(3);          /* Request for gpio pin */
+omap_set_gpio_direction(3,0);
+omap_set_set_dataout(3,1);     /* Writing a 1 to gpio pin # 3: */
+ret = omap_get_datain(3);      /* Reading the value of pin # 3 */
+printk("value of pin # 3 = %d\n",ret);
+omap_free_gpio(3);             /* Freeing gpio pin # 3 */
+
+2) Interrupt input by gpio pin#3
+
+#include <asm/arch/gpio.h>
+
+omap_request_gpio(3);         /* Request for gpio pin */
+omap_set_gpio_direction(3,0);
+set_irq_type(OMAP_GPIO_IRQ(3),IRQT_RISING); /* Setting up pin for interrupt */
+request_irq(OMAP_GPIO_IRQ(3), (void *)&my_int_handler, SA_SHIRQ,....);
+
+...                         /* Do stuff, handle interrupts in my_int_handler */
+
+free_irq(OMAP_GPIO_IRQ(3),&id); /*  Freeing interrupt and gpio pin */
+omap_free_gpio(3);
+
+------------------------------------------------------------------
+Last modified 14. August 2006
+The OMAP Linux Kernel Team
+Arnold <abo_gwapo@yahoo.com>
+Dirk Behme <dirk.behme@gmail.com>
+
+                         OMAP GPIO API's HowTo
+                         =====================
+
+This document is a short summary how to use OMAP Linux GPIO API. It is
+mainly focussed on OMAP5912 OSK, but should fit with extensions (more
+or less GPIOs) to other OMAP processors as well.
+
+If anything is missing, is wrong, needs extension or update, please send
+update to Linux-omap-open-source@linux.omap.com.
+
+I. GPIO Modules/Banks
+---------------------
+
+OMAP5912 OSK has 64 GPIOs (general purpose IO pins). These are organized
+in four modules (banks) with 16 pins each. OMAP GPIO API doesn't distinguish
+between modules and numbers the pins from 0 - 63:
+
+A) GPIO MODULE/BANK 0 - PIN  0-15
+B) GPIO MODULE/BANK 1 - PIN 16-31
+C) GPIO MODULE/BANK 2 - PIN 32-47
+D) GPIO MODULE/BANK 3 - PIN 48-63
+
+See
+
+http://www-s.ti.com/sc/psheets/spru767a/spru767a.pdf
+
+for more details.
+
+II. GPIO API's
+--------------
+
+A) Include
+
+#include <asm/arch/gpio.h>
+
+B) omap_cfg_reg(xxxx);
+
+Description: Configure pin mux.
+
+Parameter: Pin to be configured for GPIO.
+
+Note: This function may only be necessary for some GPIO pins. Because OMAP
+      chip itself has less real hardware pins than necessary to use all
+      its functionality at the same time, some pins share different
+      functions (called pin multiplexing, short pin mux). E.g. one pin may
+      be used for serial interface *or* GPIO. Check if this is the case for
+      the GPIO you want to use and if you have to configure the pin mux.
+
+C) omap_request_gpio(int gpio)
+
+Description: Request GPIO to be used.
+
+Parameter: int gpio - GPIO PIN (Pin 0-63)
+
+Note: Using this function, you dont have to worry about banks/modules where
+      the gpio pin is.
+
+D) omap_set_gpio_direction(int gpio, int is_input)
+
+Description: This function is responsible for setting the gpio pin direction
+             (input or output).
+
+Parameter: int gpio - GPIO PIN (Pin 0-63)
+           int is_input - pin direction (0 = output, 1 = input)
+
+E) omap_set_gpio_dataout(int gpio, int enable)
+
+Description: This function is responsible for writing to a pin.
+
+Parameter: int gpio - GPIO PIN (Pin 0-63)
+           int enable - pin value (0 or 1)
+
+F) omap_get_gpio_datain(int gpio)
+
+Description: This function is responsible for reading pin values.
+
+Parameter: int gpio - GPIO PIN (Pin 0-63)
+
+G) omap_free_gpio(int gpio)
+
+Description: This function is responsible for freeing the pin used.
+
+Parameter: int gpio - GPIO PIN (Pin 0-63)
+
+H) OMAP_GPIO_IRQ(int gpio)
+
+Description: Returns the Interrupt number for the specified gpio pin.
+
+Parameter: int gpio - GPIO PIN (Pin 0-63)
+
+I) set_irq_type(unsigned int irq, unsigned int type)
+
+Description: This function is responsible for setting the type of interrupt
+             (RISING or FALLING).
+
+Parameter: unsigned int irq - The interrupt number for the gpio pin.
+           unsigned int type - (IRQT_RISING = rising, IRQT_FALLING= falling)
+
+
+III. Example
+------------
+
+1) Writing to gpio pin#3 a value 1 and reading the value of gpio pin#3.
+
+#include <asm/arch/gpio.h>
+
+int ret;                       /* Return value */
+
+omap_request_gpio(3);          /* Request for gpio pin */
+omap_set_gpio_direction(3,0);
+omap_set_set_dataout(3,1);     /* Writing a 1 to gpio pin # 3: */
+ret = omap_get_datain(3);      /* Reading the value of pin # 3 */
+printk("value of pin # 3 = %d\n",ret);
+omap_free_gpio(3);             /* Freeing gpio pin # 3 */
+
+2) Interrupt input by gpio pin#3
+
+#include <asm/arch/gpio.h>
+
+omap_request_gpio(3);         /* Request for gpio pin */
+omap_set_gpio_direction(3,0);
+set_irq_type(OMAP_GPIO_IRQ(3),IRQT_RISING); /* Setting up pin for interrupt */
+request_irq(OMAP_GPIO_IRQ(3), (void *)&my_int_handler, SA_SHIRQ,....);
+
+...                         /* Do stuff, handle interrupts in my_int_handler */
+
+free_irq(OMAP_GPIO_IRQ(3),&id); /*  Freeing interrupt and gpio pin */
+omap_free_gpio(3);
+
+------------------------------------------------------------------
+Last modified 14. August 2006
+The OMAP Linux Kernel Team
+Arnold <abo_gwapo@yahoo.com>
+Dirk Behme <dirk.behme@gmail.com>
index e02b893fb9097f4fd97508a10ebc7b9e5c05f8ef..fd5663bf4b35d2dd2258e30c34fcf09c394a8b22 100644 (file)
@@ -1378,6 +1378,10 @@ source "drivers/uio/Kconfig"
 
 source "drivers/staging/Kconfig"
 
 
 source "drivers/staging/Kconfig"
 
+if ARCH_OMAP
+source "drivers/cbus/Kconfig"
+endif
+
 endmenu
 
 source "fs/Kconfig"
 endmenu
 
 source "fs/Kconfig"
index fbe5eef1f6c9c031b2763e1ba9bb734de941f91b..98d589a59b58ad2f513eb9420f19ac2e6585f53d 100644 (file)
@@ -40,6 +40,10 @@ ifeq ($(CONFIG_PXA_SHARPSL),y)
 OBJS           += head-sharpsl.o
 endif
 
 OBJS           += head-sharpsl.o
 endif
 
+ifeq ($(CONFIG_MACH_OMAP_PERSEUS2),y)
+OBJS           += head-omap.o
+endif
+
 ifeq ($(CONFIG_CPU_BIG_ENDIAN),y)
 ifeq ($(CONFIG_CPU_CP15),y)
 OBJS           += big-endian.o
 ifeq ($(CONFIG_CPU_BIG_ENDIAN),y)
 ifeq ($(CONFIG_CPU_CP15),y)
 OBJS           += big-endian.o
diff --git a/arch/arm/boot/compressed/head-omap.S b/arch/arm/boot/compressed/head-omap.S
new file mode 100644 (file)
index 0000000..ba3ecca
--- /dev/null
@@ -0,0 +1,18 @@
+/*
+ * linux/arch/arm/boot/compressed/head-omap.S
+ *
+ * OMAP specific tweaks.  This is merged into head.S by the linker.
+ *
+ */
+
+#include <linux/linkage.h>
+#include <asm/mach-types.h>
+
+               .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
index ee1304f22f944c3104d28d44c1b0eeb05957eb22..2e0b9e04f12e85176aeb38b84baf3a6e84a08b60 100644 (file)
@@ -136,6 +136,13 @@ struct tag_acorn {
        __u8 adfsdrives;
 };
 
        __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
 
 /* footbridge memory clock, see arch/arm/mach-footbridge/arch.c */
 #define ATAG_MEMCLK    0x41000402
 
@@ -161,6 +168,11 @@ struct tag {
                 */
                struct tag_acorn        acorn;
 
                 */
                struct tag_acorn        acorn;
 
+               /*
+                * OMAP specific
+                 */
+                struct tag_omap         omap;
+
                /*
                 * DC21285 specific
                 */
                /*
                 * DC21285 specific
                 */
index 21e17dc94cb56757f6f25f05b61ba4c2d6e095ca..1bc918cbdb189989878981ae375657b31f0e9b8e 100644 (file)
@@ -282,7 +282,7 @@ __create_page_tables:
        .endif
        str     r6, [r0]
 
        .endif
        str     r6, [r0]
 
-#ifdef CONFIG_DEBUG_LL
+#if defined(CONFIG_DEBUG_LL) || defined(CONFIG_DEBUG_SPINLOCK)
        ldr     r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
        /*
         * Map in IO space for serial debugging.
        ldr     r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags
        /*
         * Map in IO space for serial debugging.
index cd8de89c5fadca3f92e8a95099bd38272a025cf5..55ecc01ea206b2dd0d286f7e0879f9a24695bf1b 100644 (file)
@@ -46,7 +46,6 @@ config MACH_OMAP_H2
 config MACH_OMAP_H3
        bool "TI H3 Support"
        depends on ARCH_OMAP1 && ARCH_OMAP16XX
 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.
        help
          TI OMAP 1710 H3 board support. Say Y here if you have such
          a board.
index 1bda8f5d754666550c24d3881cf0b7c59185848e..6867cd3ad0b4a7c9c391256afe760328541ccc0e 100644 (file)
@@ -13,6 +13,10 @@ obj-$(CONFIG_OMAP_32K_TIMER) += timer32k.o
 # Power Management
 obj-$(CONFIG_PM) += pm.o sleep.o
 
 # Power Management
 obj-$(CONFIG_PM) += pm.o sleep.o
 
+# DSP
+obj-$(CONFIG_OMAP_MBOX_FWK)    += mailbox_mach.o
+mailbox_mach-objs              := mailbox.o
+
 led-y := leds.o
 
 # Specific board support
 led-y := leds.o
 
 # Specific board support
index 44d4a966bed9b3aa4c6ea06efa678396f641ae52..46098f546824853499151687cb1f7bd46fc45a83 100644 (file)
 static int mmc_set_power(struct device *dev, int slot, int power_on,
                                int vdd)
 {
 static int mmc_set_power(struct device *dev, int slot, int power_on,
                                int vdd)
 {
-       if (power_on)
-               gpio_direction_output(H2_TPS_GPIO_MMC_PWR_EN, 1);
-       else
-               gpio_direction_output(H2_TPS_GPIO_MMC_PWR_EN, 0);
-
+       gpio_set_value(H2_TPS_GPIO_MMC_PWR_EN, power_on);
        return 0;
 }
 
 static int mmc_late_init(struct device *dev)
 {
        return 0;
 }
 
 static int mmc_late_init(struct device *dev)
 {
-       int ret;
-
-       ret = gpio_request(H2_TPS_GPIO_MMC_PWR_EN, "MMC power");
+       int ret = gpio_request(H2_TPS_GPIO_MMC_PWR_EN, "MMC power");
        if (ret < 0)
                return ret;
 
        if (ret < 0)
                return ret;
 
@@ -47,7 +41,7 @@ static int mmc_late_init(struct device *dev)
        return ret;
 }
 
        return ret;
 }
 
-static void mmc_shutdown(struct device *dev)
+static void mmc_cleanup(struct device *dev)
 {
        gpio_free(H2_TPS_GPIO_MMC_PWR_EN);
 }
 {
        gpio_free(H2_TPS_GPIO_MMC_PWR_EN);
 }
@@ -60,7 +54,7 @@ static void mmc_shutdown(struct device *dev)
 static struct omap_mmc_platform_data mmc1_data = {
        .nr_slots                       = 1,
        .init                           = mmc_late_init,
 static struct omap_mmc_platform_data mmc1_data = {
        .nr_slots                       = 1,
        .init                           = mmc_late_init,
-       .shutdown                       = mmc_shutdown,
+       .cleanup                        = mmc_cleanup,
        .dma_mask                       = 0xffffffff,
        .slots[0]       = {
                .set_power              = mmc_set_power,
        .dma_mask                       = 0xffffffff,
        .slots[0]       = {
                .set_power              = mmc_set_power,
index 0d8a3c195e2e01872efd43394e333dea2f7aa43d..5e8877ce35e09718f811578ffbcf93ee166bdf2c 100644 (file)
 static int mmc_set_power(struct device *dev, int slot, int power_on,
                                int vdd)
 {
 static int mmc_set_power(struct device *dev, int slot, int power_on,
                                int vdd)
 {
-       if (power_on)
-               gpio_direction_output(H3_TPS_GPIO_MMC_PWR_EN, 1);
-       else
-               gpio_direction_output(H3_TPS_GPIO_MMC_PWR_EN, 0);
-
+       gpio_set_value(H3_TPS_GPIO_MMC_PWR_EN, power_on);
        return 0;
 }
 
        return 0;
 }
 
index 4695965114c4e67e86456439fa4435149e0cf374..f597968733b4f2baa980419519edade5e0203961 100644 (file)
 #include <asm/mach/flash.h>
 #include <asm/mach/map.h>
 
 #include <asm/mach/flash.h>
 #include <asm/mach/map.h>
 
-#include <mach/gpioexpander.h>
 #include <mach/irqs.h>
 #include <mach/mux.h>
 #include <mach/tc.h>
 #include <mach/nand.h>
 #include <mach/irqs.h>
 #include <mach/mux.h>
 #include <mach/tc.h>
 #include <mach/nand.h>
-#include <mach/irda.h>
 #include <mach/usb.h>
 #include <mach/keypad.h>
 #include <mach/dma.h>
 #include <mach/usb.h>
 #include <mach/keypad.h>
 #include <mach/dma.h>
@@ -276,104 +274,6 @@ static struct platform_device h3_kp_device = {
        .resource       = 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(struct work_struct *work)
-{
-       struct omap_irda_config *irda_config =
-               container_of(work, struct omap_irda_config, gpio_expa.work);
-       int mode = irda_config->mode;
-       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;
-
-       irda_config->mode = mode;
-       cancel_delayed_work(&irda_config->gpio_expa);
-       PREPARE_DELAYED_WORK(&irda_config->gpio_expa, set_trans_mode);
-       schedule_delayed_work(&irda_config->gpio_expa, 0);
-
-       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 u64 irda_dmamask = 0xffffffff;
-
-static struct platform_device h3_irda_device = {
-       .name           = "omapirda",
-       .id             = 0,
-       .dev            = {
-               .platform_data  = &h3_irda_data,
-               .dma_mask       = &irda_dmamask,
-       },
-       .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 h3_lcd_device = {
        .name           = "lcd_h3",
        .id             = -1,
@@ -395,7 +295,6 @@ static struct platform_device *devices[] __initdata = {
        &nand_device,
         &smc91x_device,
        &intlat_device,
        &nand_device,
         &smc91x_device,
        &intlat_device,
-       &h3_irda_device,
        &h3_kp_device,
        &h3_lcd_device,
 };
        &h3_kp_device,
        &h3_lcd_device,
 };
index 7bc7a3cb9c510459953bb45012f9c70653e55a6c..8780ca66639832b0fe54749aae4a21ab103eeb62 100644 (file)
@@ -35,6 +35,7 @@
 #include <mach/omapfb.h>
 #include <mach/lcd_mipid.h>
 #include <mach/mmc.h>
 #include <mach/omapfb.h>
 #include <mach/lcd_mipid.h>
 #include <mach/mmc.h>
+#include <mach/usb.h>
 
 #define ADS7846_PENDOWN_GPIO   15
 
 
 #define ADS7846_PENDOWN_GPIO   15
 
@@ -181,11 +182,7 @@ static struct omap_usb_config nokia770_usb_config __initdata = {
 static int nokia770_mmc_set_power(struct device *dev, int slot, int power_on,
                                int vdd)
 {
 static int nokia770_mmc_set_power(struct device *dev, int slot, int power_on,
                                int vdd)
 {
-       if (power_on)
-               gpio_set_value(NOKIA770_GPIO_MMC_POWER, 1);
-       else
-               gpio_set_value(NOKIA770_GPIO_MMC_POWER, 0);
-
+       gpio_set_value(NOKIA770_GPIO_MMC_POWER, power_on);
        return 0;
 }
 
        return 0;
 }
 
index 64ab386a65c775ff7553d24b65ab8c4928d64210..3a2214cb37aba18fb5f4964223d79693902b1029 100644 (file)
@@ -10,10 +10,13 @@ config ARCH_OMAP2420
        depends on ARCH_OMAP24XX
        select OMAP_DM_TIMER
        select ARCH_OMAP_OTG
        depends on ARCH_OMAP24XX
        select OMAP_DM_TIMER
        select ARCH_OMAP_OTG
+       select CPU_V6
 
 config ARCH_OMAP2430
        bool "OMAP2430 support"
        depends on ARCH_OMAP24XX
 
 config ARCH_OMAP2430
        bool "OMAP2430 support"
        depends on ARCH_OMAP24XX
+       select ARCH_OMAP_OTG
+       select CPU_V6
 
 config ARCH_OMAP34XX
        bool "OMAP34xx Based System"
 
 config ARCH_OMAP34XX
        bool "OMAP34xx Based System"
@@ -23,6 +26,7 @@ config ARCH_OMAP3430
        bool "OMAP3430 support"
        depends on ARCH_OMAP3 && ARCH_OMAP34XX
        select ARCH_OMAP_OTG
        bool "OMAP3430 support"
        depends on ARCH_OMAP3 && ARCH_OMAP34XX
        select ARCH_OMAP_OTG
+       select CPU_V7
 
 comment "OMAP Board Type"
        depends on ARCH_OMAP2 || ARCH_OMAP3
 
 comment "OMAP Board Type"
        depends on ARCH_OMAP2 || ARCH_OMAP3
@@ -31,25 +35,98 @@ config MACH_OMAP_GENERIC
        bool "Generic OMAP board"
        depends on ARCH_OMAP2 && ARCH_OMAP24XX
 
        bool "Generic OMAP board"
        depends on ARCH_OMAP2 && ARCH_OMAP24XX
 
+config MACH_NOKIA_N800
+       bool "Nokia N800"
+       depends on ARCH_OMAP2420
+       select VIDEO_TCM825X if VIDEO_OMAP2 && VIDEO_HELPER_CHIPS_AUTO
+       select CBUS if VIDEO_TCM825X
+       select CBUS_RETU if VIDEO_TCM825X
+       select MENELAUS if VIDEO_TCM825X
+       select OMAP_GPIO_SWITCH
+
+config MACH_NOKIA_N810
+       bool "Nokia N810"
+       depends on MACH_NOKIA_N800
+
+config MACH_NOKIA_N810_WIMAX
+       bool "Nokia N810 WiMAX"
+       depends on MACH_NOKIA_N800
+       select MACH_NOKIA_N810
+
+config MACH_NOKIA_RX51
+       bool "Nokia RX-51 board"
+       depends on ARCH_OMAP3 && ARCH_OMAP34XX
+
+config MACH_OMAP2_TUSB6010
+       bool
+       depends on ARCH_OMAP2 && ARCH_OMAP2420
+       default y if MACH_NOKIA_N800
+
 config MACH_OMAP_H4
        bool "OMAP 2420 H4 board"
 config MACH_OMAP_H4
        bool "OMAP 2420 H4 board"
-       depends on ARCH_OMAP2 && ARCH_OMAP24XX
+       depends on ARCH_OMAP2 && ARCH_OMAP2420
        select OMAP_DEBUG_DEVICES
 
        select OMAP_DEBUG_DEVICES
 
+config MACH_OMAP_H4_TUSB
+       bool "TUSB 6010 EVM board"
+       depends on MACH_OMAP_H4
+       select MACH_OMAP2_TUSB6010
+       help
+         Set this if you've got a TUSB6010 high speed USB board.
+         You may need to consult the schematics for your revisions
+         of the Menelaus and TUSB boards, and make changes to be
+         sure this is set up properly for your board stack.
+
+         Be sure to select OTG mode operation, not host-only or
+         peripheral-only.
+
+config MACH_OMAP_H4_OTG
+       bool "Use USB OTG connector, not device connector (S1.10)"
+       depends on MACH_OMAP_H4
+       help
+         Set this if you've set S1.10 (on the mainboard) to use the
+         Mini-AB (OTG) connector and OTG transceiver with the USB0
+         port, instead of the Mini-B ("download") connector with its
+         non-OTG transceiver.
+
+         Note that the "download" connector can be used to bootstrap
+         the system from the OMAP mask ROM.  Also, since this is a
+         development platform, you can also force the OTG port into
+         a non-OTG operational mode.
+
+config MACH_OMAP2_H4_USB1
+       bool "Use USB1 port, not UART2 (S3.3)"
+       depends on MACH_OMAP_H4
+       help
+         Set this if you've set SW3.3 (on the CPU card) so that the
+         expansion connectors receive USB1 signals instead of UART2.
+
 config MACH_OMAP_APOLLON
        bool "OMAP 2420 Apollon board"
 config MACH_OMAP_APOLLON
        bool "OMAP 2420 Apollon board"
-       depends on ARCH_OMAP2 && ARCH_OMAP24XX
+       depends on ARCH_OMAP2 && ARCH_OMAP2420
 
 config MACH_OMAP_2430SDP
        bool "OMAP 2430 SDP board"
 
 config MACH_OMAP_2430SDP
        bool "OMAP 2430 SDP board"
+       depends on ARCH_OMAP2 && ARCH_OMAP2430
+
+config MACH_OMAP_LDP
+       bool "OMAP3 LDP board"
+       depends on ARCH_OMAP3 && ARCH_OMAP34XX
+
+config MACH_OMAP2EVM 
+       bool "OMAP 2530 EVM board"
        depends on ARCH_OMAP2 && ARCH_OMAP24XX
 
        depends on ARCH_OMAP2 && ARCH_OMAP24XX
 
-config MACH_OMAP3_BEAGLE
-       bool "OMAP3 BEAGLE board"
+config MACH_OMAP_3430SDP
+       bool "OMAP 3430 SDP board"
        depends on ARCH_OMAP3 && ARCH_OMAP34XX
 
        depends on ARCH_OMAP3 && ARCH_OMAP34XX
 
-config MACH_OMAP_LDP
-       bool "OMAP3 LDP board"
+config MACH_OMAP3EVM
+       bool "OMAP 3530 EVM board"
+       depends on ARCH_OMAP3 && ARCH_OMAP34XX
+
+config MACH_OMAP3_BEAGLE
+       bool "OMAP3 BEAGLE board"
        depends on ARCH_OMAP3 && ARCH_OMAP34XX
 
 config MACH_OVERO
        depends on ARCH_OMAP3 && ARCH_OMAP34XX
 
 config MACH_OVERO
index c49d9bfa3abde7694e261115e40424f586f1152f..76acefa0c765384648fb44e29797a8c6d50be087 100644 (file)
@@ -28,28 +28,59 @@ endif
 obj-$(CONFIG_ARCH_OMAP2)               += clock24xx.o
 obj-$(CONFIG_ARCH_OMAP3)               += clock34xx.o
 
 obj-$(CONFIG_ARCH_OMAP2)               += clock24xx.o
 obj-$(CONFIG_ARCH_OMAP3)               += clock34xx.o
 
+# DSP
+obj-$(CONFIG_OMAP_MBOX_FWK)    += mailbox_mach.o
+mailbox_mach-objs              := mailbox.o
+
 # Specific board support
 obj-$(CONFIG_MACH_OMAP_GENERIC)                += board-generic.o
 obj-$(CONFIG_MACH_OMAP_H4)             += board-h4.o
 obj-$(CONFIG_MACH_OMAP_2430SDP)                += board-2430sdp.o \
 # Specific board support
 obj-$(CONFIG_MACH_OMAP_GENERIC)                += board-generic.o
 obj-$(CONFIG_MACH_OMAP_H4)             += board-h4.o
 obj-$(CONFIG_MACH_OMAP_2430SDP)                += board-2430sdp.o \
+                                          board-2430sdp-flash.o \
                                           mmc-twl4030.o
                                           mmc-twl4030.o
-obj-$(CONFIG_MACH_OMAP_APOLLON)                += board-apollon.o
-obj-$(CONFIG_MACH_OMAP3_BEAGLE)                += board-omap3beagle.o \
-                                          mmc-twl4030.o
-obj-$(CONFIG_MACH_OMAP_LDP)            += board-ldp.o \
-                                          mmc-twl4030.o
-obj-$(CONFIG_MACH_OVERO)               += board-overo.o \
-                                          mmc-twl4030.o
-obj-$(CONFIG_MACH_OMAP3_PANDORA)       += board-omap3pandora.o \
+obj-$(CONFIG_MACH_OMAP2EVM)            += board-omap2evm.o \
                                           mmc-twl4030.o
 obj-$(CONFIG_MACH_OMAP_3430SDP)                += board-3430sdp.o \
                                           mmc-twl4030.o
 obj-$(CONFIG_MACH_OMAP_3430SDP)                += board-3430sdp.o \
+                                          mmc-twl4030.o \
+                                          board-3430sdp-flash.o
+obj-$(CONFIG_MACH_OMAP3EVM)            += board-omap3evm.o \
+                                          mmc-twl4030.o \
+                                          board-omap3evm-flash.o \
+                                          twl4030-generic-scripts.o
+obj-$(CONFIG_MACH_OMAP3_BEAGLE)                += board-omap3beagle.o \
+                                          mmc-twl4030.o \
+                                          twl4030-generic-scripts.o
+obj-$(CONFIG_MACH_OMAP_LDP)            += board-ldp.o \
                                           mmc-twl4030.o
                                           mmc-twl4030.o
-
+obj-$(CONFIG_MACH_OMAP_APOLLON)                += board-apollon.o \
+                                          board-apollon-mmc.o  \
+                                          board-apollon-keys.o
+obj-$(CONFIG_MACH_NOKIA_N800)          += board-n800.o board-n800-flash.o \
+                                          board-n800-mmc.o board-n800-bt.o \
+                                          board-n800-usb.o \
+                                          board-n800-dsp.o \
+                                          board-n800-camera.o
+obj-$(CONFIG_MACH_NOKIA_N810)          += board-n810.o
 obj-$(CONFIG_MACH_NOKIA_RX51)          += board-rx51.o \
 obj-$(CONFIG_MACH_NOKIA_RX51)          += board-rx51.o \
+                                          board-n800-flash.o \
+                                          board-rx51-flash.o \
+                                          board-rx51-sdram.o \
+                                          board-rx51-video.o \
                                           board-rx51-peripherals.o \
                                           mmc-twl4030.o
 
                                           board-rx51-peripherals.o \
                                           mmc-twl4030.o
 
+obj-$(CONFIG_MACH_OVERO)               += board-overo.o \
+                                          mmc-twl4030.o \
+                                          twl4030-generic-scripts.o
+obj-$(CONFIG_MACH_OMAP3_PANDORA)       += board-omap3pandora.o \
+                                          mmc-twl4030.o
+
 # Platform specific device init code
 # Platform specific device init code
-ifeq ($(CONFIG_USB_MUSB_SOC),y)
-obj-y                                  += usb-musb.o
+obj-$(CONFIG_USB_MUSB_SOC)             += usb-musb.o
+obj-$(CONFIG_MACH_OMAP2_TUSB6010)      += usb-tusb6010.o
+
+ifneq ($(CONFIG_USB_EHCI_HCD),)
+       obj-y                           += usb-ehci.o
 endif
 endif
+
+
diff --git a/arch/arm/mach-omap2/board-2430sdp-flash.c b/arch/arm/mach-omap2/board-2430sdp-flash.c
new file mode 100644 (file)
index 0000000..57d3b55
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * linux/arch/arm/mach-omap2/board-2430sdp-flash.c
+ *
+ * Copyright (C) 2007 MontaVista Software, Inc. <source@mvista.com>
+ * Author: Kevin Hilman
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <asm/mach/flash.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/onenand_regs.h>
+
+#include <asm/io.h>
+#include <mach/onenand.h>
+#include <mach/board.h>
+#include <mach/gpmc.h>
+#include <mach/nand.h>
+
+#define ONENAND_MAP 0x20000000
+#define GPMC_OFF_CONFIG1_0 0x60
+
+enum fstype {
+       NAND = 0,
+       NOR,
+       ONENAND,
+       UNKNOWN = -1
+};
+
+static enum fstype flash_type = NAND;
+
+static struct mtd_partition nand_partitions[] = {
+       {
+               .name           = "X-Loader",
+               .offset         = 0,
+               .size           = 4*(64*2048),  /* 0-3 blks reserved.
+                                                  Mandated by ROM code */
+               .mask_flags     = MTD_WRITEABLE /* force read-only */
+       },
+       {
+               .name           = "U-Boot",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           =  4*(64*2048),
+               .mask_flags     = MTD_WRITEABLE /* force read-only */
+       },
+       {
+               .name           = "U-Boot Environment",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 2*(64*2048),
+       },
+       {
+               .name           = "Kernel",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 32*(64*2048),         /* 4*1M */
+       },
+       {
+               .name           = "File System",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = MTDPART_SIZ_FULL,
+       },
+};
+static struct omap_nand_platform_data sdp_nand_data = {
+       .parts          = nand_partitions,
+       .nr_parts       = ARRAY_SIZE(nand_partitions),
+       .dma_channel    = -1,   /* disable DMA in OMAP OneNAND driver */
+};
+
+static struct platform_device sdp_nand_device = {
+       .name           = "omap2-nand",
+       .id             = -1,
+       .dev = {
+               .platform_data = &sdp_nand_data,
+       },
+};
+
+static struct mtd_partition onenand_partitions[] = {
+       {
+               .name           = "(OneNAND)X-Loader",
+               .offset         = 0,
+               .size           = 4*(64*2048),  /* 0-3 blks reserved.
+                                                  Mandated by ROM code */
+               .mask_flags     = MTD_WRITEABLE /* force read-only */
+       },
+       {
+               .name           = "(OneNAND)U-Boot",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           =  2*(64*2048),
+               .mask_flags     = MTD_WRITEABLE /* force read-only */
+       },
+       {
+               .name           = "(OneNAND)U-Boot Environment",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 1*(64*2048),
+       },
+       {
+               .name           = "(OneNAND)Kernel",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 4*(64*2048),
+       },
+       {
+               .name           = "(OneNAND)File System",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = MTDPART_SIZ_FULL,
+       },
+};
+
+static struct omap_onenand_platform_data sdp_onenand_data = {
+       .parts          = onenand_partitions,
+       .nr_parts       = ARRAY_SIZE(onenand_partitions),
+       .dma_channel    = -1,   /* disable DMA in OMAP OneNAND driver */
+};
+
+static struct platform_device sdp_onenand_device = {
+       .name           = "omap2-onenand",
+       .id             = -1,
+       .dev = {
+               .platform_data = &sdp_onenand_data,
+       },
+};
+
+void __init sdp2430_flash_init(void)
+{
+       void __iomem *gpmc_base_add, *gpmc_cs_base_add;
+       unsigned char cs = 0;
+
+       gpmc_base_add = (__force void __iomem *)OMAP243X_GPMC_VIRT;
+       while (cs < GPMC_CS_NUM) {
+               int ret = 0;
+
+               /* Each GPMC set for a single CS is at offset 0x30 */
+               gpmc_cs_base_add = (gpmc_base_add + GPMC_OFF_CONFIG1_0 +
+                                   (cs*0x30));
+
+               /* xloader/Uboot would have programmed the NAND/oneNAND
+                * base address for us This is a ugly hack. The proper
+                * way of doing this is to pass the setup of u-boot up
+                * to kernel using kernel params - something on the
+                * lines of machineID. Check if Nand/oneNAND is
+                * configured */
+               ret = __raw_readl(gpmc_cs_base_add + GPMC_CS_CONFIG1);
+               if ((ret & 0xC00) == (0x800)) {
+                       /* Found it!! */
+                       printk(KERN_INFO "NAND: Found NAND on CS %d \n", cs);
+                       flash_type = NAND;
+                       break;
+               }
+               ret = __raw_readl(gpmc_cs_base_add + GPMC_CS_CONFIG7);
+               if ((ret & 0x3F) == (ONENAND_MAP >> 24)) {
+                       /* Found it!! */
+                       flash_type = ONENAND;
+                       break;
+               }
+               cs++;
+       }
+       if (cs >= GPMC_CS_NUM) {
+               printk(KERN_INFO "MTD: Unable to find MTD configuration in "
+                                "GPMC   - not registering.\n");
+               return;
+       }
+
+       if (flash_type == NAND) {
+               sdp_nand_data.cs               = cs;
+               sdp_nand_data.gpmc_cs_baseaddr = gpmc_cs_base_add;
+               sdp_nand_data.gpmc_baseaddr    = gpmc_base_add;
+
+               if (platform_device_register(&sdp_nand_device) < 0) {
+                       printk(KERN_ERR "Unable to register NAND device\n");
+                       return;
+               }
+       }
+
+       if (flash_type == ONENAND) {
+               sdp_onenand_data.cs = cs;
+
+               if (platform_device_register(&sdp_onenand_device) < 0) {
+                       printk(KERN_ERR "Unable to register OneNAND device\n");
+                       return;
+               }
+       }
+}
index 22143651037e3b62c526f128c282e1132deac9fd..899e6e3402dd2051484d4f6aef7afaf57b7a80ad 100644 (file)
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/delay.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/delay.h>
+#include <linux/input.h>
 #include <linux/i2c/twl4030.h>
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/i2c/twl4030.h>
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/io.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
 #include <mach/gpio.h>
 #include <mach/mux.h>
 #include <mach/board.h>
 #include <mach/gpio.h>
 #include <mach/mux.h>
 #include <mach/board.h>
+#include <mach/usb.h>
 #include <mach/common.h>
 #include <mach/gpmc.h>
 #include <mach/common.h>
 #include <mach/gpmc.h>
-#include <mach/usb.h>
+#include <mach/mcspi.h>
+
 
 #include "mmc-twl4030.h"
 
 #define SDP2430_CS0_BASE       0x04000000
 #define        SDP2430_FLASH_CS        0
 #define        SDP2430_SMC91X_CS       5
 
 #include "mmc-twl4030.h"
 
 #define SDP2430_CS0_BASE       0x04000000
 #define        SDP2430_FLASH_CS        0
 #define        SDP2430_SMC91X_CS       5
-
 #define SDP2430_ETHR_GPIO_IRQ          149
 
 #define SDP2430_ETHR_GPIO_IRQ          149
 
+/* GPIO used for TSC2046 (touchscreen)
+ *
+ * Also note that the tsc2046 is the same silicon as the ads7846, so
+ * that driver is used for the touchscreen. */
+#define TS_GPIO                 24
+
+#define TWL4030_MSECURE_GPIO   118
+#define SECONDARY_LCD_GPIO     147
+
+extern void sdp2430_flash_init(void);
+
 static struct mtd_partition sdp2430_partitions[] = {
        /* bootloader (U-Boot, etc) in first sector */
        {
 static struct mtd_partition sdp2430_partitions[] = {
        /* bootloader (U-Boot, etc) in first sector */
        {
@@ -112,6 +127,11 @@ static struct resource sdp2430_smc91x_resources[] = {
        },
 };
 
        },
 };
 
+static struct platform_device sdp2430_lcd_device = {
+       .name           = "sdp2430_lcd",
+       .id             = -1,
+};
+
 static struct platform_device sdp2430_smc91x_device = {
        .name           = "smc91x",
        .id             = -1,
 static struct platform_device sdp2430_smc91x_device = {
        .name           = "smc91x",
        .id             = -1,
@@ -119,9 +139,125 @@ static struct platform_device sdp2430_smc91x_device = {
        .resource       = sdp2430_smc91x_resources,
 };
 
        .resource       = sdp2430_smc91x_resources,
 };
 
+/*
+ * Key mapping for 2430 SDP board
+ */
+
+static int sdp2430_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_D),
+       KEY(5, 0, KEY_V),
+       KEY(5, 1, KEY_W),
+       KEY(5, 2, KEY_L),
+       KEY(5, 3, KEY_S),
+       KEY(5, 4, KEY_H),
+       0
+};
+
+static struct twl4030_keypad_data sdp2430_kp_data = {
+       .rows           = 5,
+       .cols           = 6,
+       .keymap         = sdp2430_keymap,
+       .keymapsize     = ARRAY_SIZE(sdp2430_keymap),
+       .rep            = 1,
+};
+
+static int __init msecure_init(void)
+{
+       int ret = 0;
+
+#ifdef CONFIG_RTC_DRV_TWL4030
+       ret = gpio_request(TWL4030_MSECURE_GPIO, "msecure");
+       if (ret < 0) {
+               printk(KERN_ERR "msecure_init: can't reserve GPIO:%d !\n",
+                       TWL4030_MSECURE_GPIO);
+               goto out;
+       }
+       /*
+        * TWL4030 will be in secure mode if msecure line from OMAP is low.
+        * Make msecure line high in order to change the TWL4030 RTC time
+        * and calender registers.
+        */
+       gpio_direction_output(TWL4030_MSECURE_GPIO, 1);
+out:
+#endif
+
+       return ret;
+}
+
 static struct platform_device *sdp2430_devices[] __initdata = {
        &sdp2430_smc91x_device,
        &sdp2430_flash_device,
 static struct platform_device *sdp2430_devices[] __initdata = {
        &sdp2430_smc91x_device,
        &sdp2430_flash_device,
+       &sdp2430_lcd_device,
+};
+
+static void ads7846_dev_init(void)
+{
+       if (gpio_request(TS_GPIO, "ads7846 irq") < 0)
+               printk(KERN_ERR "can't get ads746 pen down GPIO\n");
+
+       gpio_direction_input(TS_GPIO);
+
+       omap_set_gpio_debounce(TS_GPIO, 1);
+       omap_set_gpio_debounce_time(TS_GPIO, 0xa);
+}
+
+static int ads7846_get_pendown_state(void)
+{
+       return !gpio_get_value(TS_GPIO);
+}
+
+static struct ads7846_platform_data tsc2046_config __initdata = {
+       .get_pendown_state = ads7846_get_pendown_state,
+       .keep_vref_on      = 1,
+};
+
+static struct omap2_mcspi_device_config tsc2046_mcspi_config = {
+       .turbo_mode     = 0,
+       .single_channel = 0,  /* 0: slave, 1: master */
+};
+
+static struct omap_lcd_config sdp2430_lcd_config __initdata = {
+       .ctrl_name      = "internal",
+};
+
+static struct spi_board_info sdp2430_spi_board_info[] __initdata = {
+       [0] = {
+               /*
+                * TSC2046 operates at a max freqency of 2MHz, so
+                * operate slightly below at 1.5MHz
+                */
+               .modalias        = "ads7846",
+               .bus_num         = 1,
+               .chip_select     = 0,
+               .max_speed_hz    = 1500000,
+               .controller_data = &tsc2046_mcspi_config,
+               .irq             = OMAP_GPIO_IRQ(TS_GPIO),
+               .platform_data   = &tsc2046_config,
+       },
 };
 
 static inline void __init sdp2430_init_smc91x(void)
 };
 
 static inline void __init sdp2430_init_smc91x(void)
@@ -199,8 +335,16 @@ static struct omap_uart_config sdp2430_uart_config __initdata = {
        .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
 };
 
        .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
 };
 
-static struct omap_board_config_kernel sdp2430_config[] = {
+static
+struct omap_serial_console_config sdp2430_serial_console_config __initdata = {
+       .console_uart = 1,
+       .console_speed = 115200,
+};
+
+static struct omap_board_config_kernel sdp2430_config[] __initdata = {
        {OMAP_TAG_UART, &sdp2430_uart_config},
        {OMAP_TAG_UART, &sdp2430_uart_config},
+       {OMAP_TAG_LCD, &sdp2430_lcd_config},
+       {OMAP_TAG_SERIAL_CONSOLE, &sdp2430_serial_console_config},
 };
 
 
 };
 
 
@@ -210,12 +354,23 @@ static struct twl4030_gpio_platform_data sdp2430_gpio_data = {
        .irq_end        = TWL4030_GPIO_IRQ_END,
 };
 
        .irq_end        = TWL4030_GPIO_IRQ_END,
 };
 
+static struct twl4030_usb_data sdp2430_usb_data = {
+       .usb_mode       = T2_USB_MODE_ULPI,
+};
+
+static struct twl4030_madc_platform_data sdp2430_madc_data = {
+       .irq_line       = 1,
+};
+
 static struct twl4030_platform_data sdp2430_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
 
        /* platform_data for children goes here */
        .gpio           = &sdp2430_gpio_data,
 static struct twl4030_platform_data sdp2430_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
 
        /* platform_data for children goes here */
        .gpio           = &sdp2430_gpio_data,
+       .madc           = &sdp2430_madc_data,
+       .keypad         = &sdp2430_kp_data,
+       .usb            = &sdp2430_usb_data,
 };
 
 static struct i2c_board_info __initdata sdp2430_i2c_boardinfo[] = {
 };
 
 static struct i2c_board_info __initdata sdp2430_i2c_boardinfo[] = {
@@ -254,8 +409,19 @@ static void __init omap_2430sdp_init(void)
        omap_board_config = sdp2430_config;
        omap_board_config_size = ARRAY_SIZE(sdp2430_config);
        omap_serial_init();
        omap_board_config = sdp2430_config;
        omap_board_config_size = ARRAY_SIZE(sdp2430_config);
        omap_serial_init();
-       twl4030_mmc_init(mmc);
+
+       msecure_init();
+
+       sdp2430_flash_init();
        usb_musb_init();
        usb_musb_init();
+
+       spi_register_board_info(sdp2430_spi_board_info,
+                               ARRAY_SIZE(sdp2430_spi_board_info));
+       ads7846_dev_init();
+       twl4030_mmc_init(mmc);
+
+       /* turn off secondary LCD backlight */
+       gpio_direction_output(SECONDARY_LCD_GPIO, 0);
 }
 
 static void __init omap_2430sdp_map_io(void)
 }
 
 static void __init omap_2430sdp_map_io(void)
diff --git a/arch/arm/mach-omap2/board-3430sdp-flash.c b/arch/arm/mach-omap2/board-3430sdp-flash.c
new file mode 100644 (file)
index 0000000..f0e25a4
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * linux/arch/arm/mach-omap2/board-3430sdp-flash.c
+ *
+ * Copyright (c) 2007 Texas Instruments
+ *
+ * Modified from mach-omap2/board-2430sdp-flash.c
+ * Author: Rohit Choraria <rohitkc@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/onenand_regs.h>
+#include <linux/types.h>
+#include <linux/io.h>
+
+#include <asm/mach/flash.h>
+#include <mach/onenand.h>
+#include <mach/board.h>
+#include <mach/gpmc.h>
+#include <mach/nand.h>
+
+#define NAND_BLOCK_SIZE                SZ_128K
+
+/* NAND */
+/* IMPORTANT NOTE ON MAPPING
+ * 3430SDP - 34XX
+ * ----------
+ * NOR always on 0x04000000 for SDPV1
+ * NOR always on 0x10000000 for SDPV2
+ * MPDB always on 0x08000000
+ * NAND always on 0x0C000000
+ * OneNand Mapped to 0x20000000
+ * Boot Mode(NAND/NOR). The other on CS1
+ */
+#define FLASH_BASE_SDPV1       0x04000000 /* NOR flash (64 Meg aligned) */
+#define FLASH_BASE_SDPV2       0x10000000 /* NOR flash (256 Meg aligned) */
+#define DEBUG_BASE             0x08000000 /* debug board */
+#define NAND_BASE              0x0C000000 /* NAND flash */
+#define ONENAND_MAP            0x20000000 /* OneNand flash */
+
+/* various memory sizes */
+#define FLASH_SIZE_SDPV1       SZ_64M
+#define FLASH_SIZE_SDPV2       SZ_128M
+
+static struct mtd_partition sdp_nor_partitions[] = {
+       /* bootloader (U-Boot, etc) in first sector */
+       {
+               .name           = "Bootloader-NOR",
+               .offset         = 0,
+               .size           = SZ_256K,
+               .mask_flags     = MTD_WRITEABLE, /* force read-only */
+       },
+       /* bootloader params in the next sector */
+       {
+               .name           = "Params-NOR",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = SZ_256K,
+               .mask_flags     = 0,
+       },
+       /* kernel */
+       {
+               .name           = "Kernel-NOR",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = SZ_2M,
+               .mask_flags     = 0
+       },
+       /* file system */
+       {
+               .name           = "Filesystem-NOR",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = MTDPART_SIZ_FULL,
+               .mask_flags     = 0
+       }
+};
+
+static struct flash_platform_data sdp_nor_data = {
+       .map_name       = "cfi_probe",
+       .width          = 2,
+       .parts          = sdp_nor_partitions,
+       .nr_parts       = ARRAY_SIZE(sdp_nor_partitions),
+};
+
+static struct resource sdp_nor_resource = {
+       .start          = 0,
+       .end            = 0,
+       .flags          = IORESOURCE_MEM,
+};
+
+static struct platform_device sdp_nor_device = {
+       .name           = "omapflash",
+       .id             = 0,
+       .dev            = {
+                       .platform_data = &sdp_nor_data,
+       },
+       .num_resources  = 1,
+       .resource       = &sdp_nor_resource,
+};
+
+static int sdp_onenand_setup(void __iomem *, int freq);
+
+static struct mtd_partition sdp_onenand_partitions[] = {
+       {
+               .name           = "X-Loader-OneNAND",
+               .offset         = 0,
+               .size           = 4 * (64 * 2048),
+               .mask_flags     = MTD_WRITEABLE  /* force read-only */
+       },
+       {
+               .name           = "U-Boot-OneNAND",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 2 * (64 * 2048),
+               .mask_flags     = MTD_WRITEABLE  /* force read-only */
+       },
+       {
+               .name           = "U-Boot Environment-OneNAND",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 1 * (64 * 2048),
+       },
+       {
+               .name           = "Kernel-OneNAND",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 16 * (64 * 2048),
+       },
+       {
+               .name           = "File System-OneNAND",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = MTDPART_SIZ_FULL,
+       },
+};
+
+static struct omap_onenand_platform_data sdp_onenand_data = {
+       .parts          = sdp_onenand_partitions,
+       .nr_parts       = ARRAY_SIZE(sdp_onenand_partitions),
+       .onenand_setup  = sdp_onenand_setup,
+       .dma_channel    = -1,   /* disable DMA in OMAP OneNAND driver */
+};
+
+static struct platform_device sdp_onenand_device = {
+       .name           = "omap2-onenand",
+       .id             = -1,
+       .dev = {
+               .platform_data = &sdp_onenand_data,
+       },
+};
+
+/*
+ * sdp_onenand_setup - The function configures the onenand flash.
+ * @onenand_base: Onenand base address
+ *
+ * @return int:        Currently always returning zero.
+ */
+static int sdp_onenand_setup(void __iomem *onenand_base, int freq)
+{
+       /* Onenand setup does nothing at present */
+       return 0;
+}
+
+static struct mtd_partition sdp_nand_partitions[] = {
+       /* All the partition sizes are listed in terms of NAND block size */
+       {
+               .name           = "X-Loader-NAND",
+               .offset         = 0,
+               .size           = 4 * NAND_BLOCK_SIZE,
+               .mask_flags     = MTD_WRITEABLE,        /* force read-only */
+       },
+       {
+               .name           = "U-Boot-NAND",
+               .offset         = MTDPART_OFS_APPEND,   /* Offset = 0x80000 */
+               .size           = 4 * NAND_BLOCK_SIZE,
+               .mask_flags     = MTD_WRITEABLE,        /* force read-only */
+       },
+       {
+               .name           = "Boot Env-NAND",
+               .offset         = MTDPART_OFS_APPEND,   /* Offset = 0x100000 */
+               .size           = 2 * NAND_BLOCK_SIZE,
+       },
+       {
+               .name           = "Kernel-NAND",
+               .offset         = MTDPART_OFS_APPEND,   /* Offset = 0x140000 */
+               .size           = 32 * NAND_BLOCK_SIZE,
+       },
+       {
+               .name           = "File System - NAND",
+               .size           = MTDPART_SIZ_FULL,
+               .offset         = MTDPART_OFS_APPEND,   /* Offset = 0x540000 */
+       },
+};
+
+static struct omap_nand_platform_data sdp_nand_data = {
+       .parts          = sdp_nand_partitions,
+       .nr_parts       = ARRAY_SIZE(sdp_nand_partitions),
+       .nand_setup     = NULL,
+       .dma_channel    = -1,           /* disable DMA in OMAP NAND driver */
+       .dev_ready      = NULL,
+};
+
+static struct resource sdp_nand_resource = {
+       .flags          = IORESOURCE_MEM,
+};
+
+static struct platform_device sdp_nand_device = {
+       .name           = "omap2-nand",
+       .id             = 0,
+       .dev            = {
+       .platform_data  = &sdp_nand_data,
+       },
+       .num_resources  = 1,
+       .resource       = &sdp_nand_resource,
+};
+
+
+/**
+ * sdp3430_flash_init - Identify devices connected to GPMC and register.
+ *
+ * @return - void.
+ */
+void __init sdp3430_flash_init(void)
+{
+       u8              cs = 0;
+       u8              nandcs = GPMC_CS_NUM + 1;
+       u8              onenandcs = GPMC_CS_NUM + 1;
+       unsigned long   gpmc_base_add;
+
+       gpmc_base_add   = OMAP34XX_GPMC_VIRT;
+
+       /* Configure start address and size of NOR device */
+       if (omap_rev() > OMAP3430_REV_ES1_0) {
+               sdp_nor_resource.start  = FLASH_BASE_SDPV2;
+               sdp_nor_resource.end    = FLASH_BASE_SDPV2
+                                               + FLASH_SIZE_SDPV2 - 1;
+       } else {
+               sdp_nor_resource.start  = FLASH_BASE_SDPV1;
+               sdp_nor_resource.end    = FLASH_BASE_SDPV1
+                                               + FLASH_SIZE_SDPV1 - 1;
+       }
+
+       if (platform_device_register(&sdp_nor_device) < 0)
+               printk(KERN_ERR "Unable to register NOR device\n");
+
+       while (cs < GPMC_CS_NUM) {
+               u32 ret = 0;
+               ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
+
+               /*
+               * xloader/Uboot would have programmed the NAND/oneNAND
+               * base address for us This is a ugly hack. The proper
+               * way of doing this is to pass the setup of u-boot up
+               * to kernel using kernel params - something on the
+               * lines of machineID. Check if oneNAND is configured
+               */
+               if ((ret & 0xC00) == 0x800) {
+                       /* Found it!! */
+                       if (nandcs > GPMC_CS_NUM)
+                               nandcs = cs;
+               } else {
+                       ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
+                       if ((ret & 0x3F) == (ONENAND_MAP >> 24))
+                       onenandcs = cs;
+               }
+               cs++;
+       }
+       if ((nandcs > GPMC_CS_NUM) && (onenandcs > GPMC_CS_NUM)) {
+               printk(KERN_INFO "NAND/OneNAND: Unable to find configuration "
+                               " in GPMC\n ");
+               return;
+       }
+
+       if (nandcs < GPMC_CS_NUM) {
+               sdp_nand_data.cs        = nandcs;
+               sdp_nand_data.gpmc_cs_baseaddr   = (void *)(gpmc_base_add +
+                                       GPMC_CS0_BASE + nandcs*GPMC_CS_SIZE);
+               sdp_nand_data.gpmc_baseaddr     = (void *) (gpmc_base_add);
+
+               if (platform_device_register(&sdp_nand_device) < 0)
+                       printk(KERN_ERR "Unable to register NAND device\n");
+       }
+
+       if (onenandcs < GPMC_CS_NUM) {
+               sdp_onenand_data.cs = onenandcs;
+               if (platform_device_register(&sdp_onenand_device) < 0)
+                       printk(KERN_ERR "Unable to register OneNAND device\n");
+       }
+}
index ed9274972122e782fcc1d94b2570f82f2b0733fb..03acac79adfc730cb697d1d4dcff29312c48fc2d 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/input.h>
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/input.h>
+#include <linux/err.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
 #include <linux/i2c/twl4030.h>
 #include <linux/regulator/machine.h>
 #include <linux/io.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
 #include <linux/i2c/twl4030.h>
 #include <linux/regulator/machine.h>
 #include <linux/io.h>
-#include <linux/gpio.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
@@ -30,6 +30,7 @@
 #include <asm/mach/map.h>
 
 #include <mach/mcspi.h>
 #include <asm/mach/map.h>
 
 #include <mach/mcspi.h>
+#include <mach/gpio.h>
 #include <mach/mux.h>
 #include <mach/board.h>
 #include <mach/usb.h>
 #include <mach/mux.h>
 #include <mach/board.h>
 #include <mach/usb.h>
 #include <mach/gpmc.h>
 
 #include <mach/control.h>
 #include <mach/gpmc.h>
 
 #include <mach/control.h>
-#include <mach/keypad.h>
 
 
+#include "sdram-qimonda-hyb18m512160af-6.h"
 #include "mmc-twl4030.h"
 
 #define CONFIG_DISABLE_HFCLK 1
 
 #include "mmc-twl4030.h"
 
 #define CONFIG_DISABLE_HFCLK 1
 
+#define SDP3430_ETHR_START             DEBUG_BASE
 #define SDP3430_ETHR_GPIO_IRQ_SDPV1    29
 #define SDP3430_ETHR_GPIO_IRQ_SDPV2    6
 #define SDP3430_SMC91X_CS              3
 #define SDP3430_ETHR_GPIO_IRQ_SDPV1    29
 #define SDP3430_ETHR_GPIO_IRQ_SDPV2    6
 #define SDP3430_SMC91X_CS              3
 #define ENABLE_VAUX3_DEDICATED 0x03
 #define ENABLE_VAUX3_DEV_GRP   0x20
 
 #define ENABLE_VAUX3_DEDICATED 0x03
 #define ENABLE_VAUX3_DEV_GRP   0x20
 
+#define ENABLE_VAUX3_DEDICATED 0x03
+#define ENABLE_VAUX3_DEV_GRP   0x20
+
 #define TWL4030_MSECURE_GPIO 22
 
 #define TWL4030_MSECURE_GPIO 22
 
+extern void sdp3430_flash_init(void);
+
 static struct resource sdp3430_smc91x_resources[] = {
        [0] = {
                .flags  = IORESOURCE_MEM,
 static struct resource sdp3430_smc91x_resources[] = {
        [0] = {
                .flags  = IORESOURCE_MEM,
@@ -118,6 +125,42 @@ static struct twl4030_keypad_data sdp3430_kp_data = {
 
 static int ts_gpio;    /* Needed for ads7846_get_pendown_state */
 
 
 static int ts_gpio;    /* Needed for ads7846_get_pendown_state */
 
+static int __init msecure_init(void)
+{
+       int ret = 0;
+
+#ifdef CONFIG_RTC_DRV_TWL4030
+       /* 3430ES2.0 doesn't have msecure/gpio-22 line connected to T2 */
+       if (omap_type() == OMAP2_DEVICE_TYPE_GP &&
+                       omap_rev() < OMAP3430_REV_ES2_0) {
+               void __iomem *msecure_pad_config_reg = omap_ctrl_base_get() +
+                       0xA3C;
+               int mux_mask = 0x04;
+               u16 tmp;
+
+               ret = gpio_request(TWL4030_MSECURE_GPIO, "msecure");
+               if (ret < 0) {
+                       printk(KERN_ERR "msecure_init: can't"
+                               "reserve GPIO:%d !\n", TWL4030_MSECURE_GPIO);
+                       goto out;
+               }
+               /*
+                * TWL4030 will be in secure mode if msecure line from OMAP
+                * is low. Make msecure line high in order to change the
+                * TWL4030 RTC time and calender registers.
+                */
+               tmp = __raw_readw(msecure_pad_config_reg);
+               tmp &= 0xF8; /* To enable mux mode 03/04 = GPIO_RTC */
+               tmp |= mux_mask;/* To enable mux mode 03/04 = GPIO_RTC */
+               __raw_writew(tmp, msecure_pad_config_reg);
+
+               gpio_direction_output(TWL4030_MSECURE_GPIO, 1);
+       }
+out:
+#endif
+       return ret;
+}
+
 /**
  * @brief ads7846_dev_init : Requests & sets GPIO line for pen-irq
  *
 /**
  * @brief ads7846_dev_init : Requests & sets GPIO line for pen-irq
  *
@@ -141,9 +184,42 @@ static int ads7846_get_pendown_state(void)
        return !gpio_get_value(ts_gpio);
 }
 
        return !gpio_get_value(ts_gpio);
 }
 
+/*
+ * This enable(1)/disable(0) the voltage for TS: uses twl4030 calls
+ */
+static int ads7846_vaux_control(int vaux_cntrl)
+{
+       int ret = 0;
+
+       /* FIXME use regulator calls */
+
+#ifdef CONFIG_TWL4030_CORE
+       /* check for return value of ldo_use: if success it returns 0 */
+       if (vaux_cntrl == VAUX_ENABLE) {
+               if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+                       ENABLE_VAUX3_DEDICATED, TWL4030_VAUX3_DEDICATED))
+                       return -EIO;
+               if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+                       ENABLE_VAUX3_DEV_GRP, TWL4030_VAUX3_DEV_GRP))
+                       return -EIO;
+       } else if (vaux_cntrl == VAUX_DISABLE) {
+               if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+                       0x00, TWL4030_VAUX3_DEDICATED))
+                       return -EIO;
+               if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+                       0x00, TWL4030_VAUX3_DEV_GRP))
+                       return -EIO;
+       }
+#else
+       ret = -EIO;
+#endif
+       return ret;
+}
+
 static struct ads7846_platform_data tsc2046_config __initdata = {
        .get_pendown_state      = ads7846_get_pendown_state,
        .keep_vref_on           = 1,
 static struct ads7846_platform_data tsc2046_config __initdata = {
        .get_pendown_state      = ads7846_get_pendown_state,
        .keep_vref_on           = 1,
+       .vaux_control           = ads7846_vaux_control,
 };
 
 
 };
 
 
@@ -222,7 +298,7 @@ static inline void __init sdp3430_init_smc91x(void)
 
 static void __init omap_3430sdp_init_irq(void)
 {
 
 static void __init omap_3430sdp_init_irq(void)
 {
-       omap2_init_common_hw(NULL);
+       omap2_init_common_hw(hyb18m512160af6_sdrc_params);
        omap_init_irq();
        omap_gpio_init();
        sdp3430_init_smc91x();
        omap_init_irq();
        omap_gpio_init();
        sdp3430_init_smc91x();
@@ -331,6 +407,87 @@ static struct twl4030_madc_platform_data sdp3430_madc_data = {
        .irq_line       = 1,
 };
 
        .irq_line       = 1,
 };
 
+
+static struct twl4030_ins __initdata sleep_on_seq[] = {
+/*
+ * Turn off VDD1 and VDD2.
+ */
+       {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_OFF), 4},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_OFF), 2},
+#ifdef CONFIG_DISABLE_HFCLK
+/*
+ * And also turn off the OMAP3 PLLs and the sysclk output.
+ */
+       {MSG_SINGULAR(DEV_GRP_P1, 0x7, RES_STATE_OFF), 3},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x19, RES_STATE_OFF), 3},
+#endif
+};
+
+static struct twl4030_script sleep_on_script __initdata = {
+       .script = sleep_on_seq,
+       .size   = ARRAY_SIZE(sleep_on_seq),
+       .flags  = TRITON_SLEEP_SCRIPT,
+};
+
+static struct twl4030_ins wakeup_seq[] __initdata = {
+#ifndef CONFIG_DISABLE_HFCLK
+/*
+ * Wakeup VDD1 and VDD2.
+ */
+       {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_ACTIVE), 4},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_ACTIVE), 2},
+#else
+/*
+ * Reenable the OMAP3 PLLs.
+ * Wakeup VDD1 and VDD2.
+ * Reenable sysclk output.
+ */
+       {MSG_SINGULAR(DEV_GRP_P1, 0x7, RES_STATE_ACTIVE), 0x30},
+       {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_ACTIVE), 0x30},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_ACTIVE), 0x37},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x19, RES_STATE_ACTIVE), 3},
+#endif /* #ifndef CONFIG_DISABLE_HFCLK */
+};
+
+static struct twl4030_script wakeup_script __initdata = {
+       .script = wakeup_seq,
+       .size   = ARRAY_SIZE(wakeup_seq),
+       .flags  = TRITON_WAKEUP12_SCRIPT | TRITON_WAKEUP3_SCRIPT,
+};
+
+static struct twl4030_ins wrst_seq[] __initdata = {
+/*
+ * Reset twl4030.
+ * Reset VDD1 regulator.
+ * Reset VDD2 regulator.
+ * Reset VPLL1 regulator.
+ * Enable sysclk output.
+ * Reenable twl4030.
+ */
+       {MSG_SINGULAR(DEV_GRP_NULL, 0x1b, RES_STATE_OFF), 2},
+       {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_WRST), 15},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_WRST), 15},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x7, RES_STATE_WRST), 0x60},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x19, RES_STATE_ACTIVE), 2},
+       {MSG_SINGULAR(DEV_GRP_NULL, 0x1b, RES_STATE_ACTIVE), 2},
+};
+static struct twl4030_script wrst_script __initdata = {
+       .script = wrst_seq,
+       .size   = ARRAY_SIZE(wakeup_seq),
+       .flags  = TRITON_WRST_SCRIPT,
+};
+
+static struct twl4030_script *twl4030_scripts[] __initdata = {
+       &sleep_on_script,
+       &wakeup_script,
+       &wrst_script,
+};
+
+static struct twl4030_power_data sdp3430_t2scripts_data __initdata = {
+       .scripts        = twl4030_scripts,
+       .size           = ARRAY_SIZE(twl4030_scripts),
+};
+
 /*
  * Apply all the fixed voltages since most versions of U-Boot
  * don't bother with that initialization.
 /*
  * Apply all the fixed voltages since most versions of U-Boot
  * don't bother with that initialization.
@@ -472,6 +629,7 @@ static struct twl4030_platform_data sdp3430_twldata = {
        .gpio           = &sdp3430_gpio_data,
        .madc           = &sdp3430_madc_data,
        .keypad         = &sdp3430_kp_data,
        .gpio           = &sdp3430_gpio_data,
        .madc           = &sdp3430_madc_data,
        .keypad         = &sdp3430_kp_data,
+       .power          = &sdp3430_t2scripts_data,
        .usb            = &sdp3430_usb_data,
 
        .vaux1          = &sdp3430_vaux1,
        .usb            = &sdp3430_usb_data,
 
        .vaux1          = &sdp3430_vaux1,
@@ -520,8 +678,11 @@ static void __init omap_3430sdp_init(void)
        spi_register_board_info(sdp3430_spi_board_info,
                                ARRAY_SIZE(sdp3430_spi_board_info));
        ads7846_dev_init();
        spi_register_board_info(sdp3430_spi_board_info,
                                ARRAY_SIZE(sdp3430_spi_board_info));
        ads7846_dev_init();
+       sdp3430_flash_init();
+       msecure_init();
        omap_serial_init();
        usb_musb_init();
        omap_serial_init();
        usb_musb_init();
+       usb_ehci_init();
 }
 
 static void __init omap_3430sdp_map_io(void)
 }
 
 static void __init omap_3430sdp_map_io(void)
diff --git a/arch/arm/mach-omap2/board-apollon-keys.c b/arch/arm/mach-omap2/board-apollon-keys.c
new file mode 100644 (file)
index 0000000..10329c0
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * linux/arch/arm/mach-omap2/board-apollon-keys.c
+ *
+ * Copyright (C) 2007 Samsung Electronics
+ * Author: Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/gpio_keys.h>
+
+#include <mach/gpio.h>
+#include <mach/mux.h>
+
+#define SW_ENTER_GPIO16                16
+#define SW_UP_GPIO17           17
+#define SW_DOWN_GPIO58         58
+#define SW_LEFT_GPIO95         95
+#define SW_RIGHT_GPIO96                96
+#define SW_ESC_GPIO97          97
+
+extern int apollon_plus(void);
+
+static struct gpio_keys_button apollon_gpio_keys_buttons[] = {
+       [0] = {
+               .code           = KEY_ENTER,
+               .gpio           = SW_ENTER_GPIO16,
+               .desc           = "enter sw",
+       },
+       [1] = {
+               .code           = KEY_UP,
+               .gpio           = SW_UP_GPIO17,
+               .desc           = "up sw",
+       },
+       [2] = {
+               .code           = KEY_DOWN,
+               .gpio           = SW_DOWN_GPIO58,
+               .desc           = "down sw",
+       },
+       [3] = {
+               .code           = KEY_LEFT,
+               .gpio           = SW_LEFT_GPIO95,
+               .desc           = "left sw",
+       },
+       [4] = {
+               .code           = KEY_RIGHT,
+               .gpio           = SW_RIGHT_GPIO96,
+               .desc           = "right sw",
+       },
+       [5] = {
+               .code           = KEY_ESC,
+               .gpio           = SW_ESC_GPIO97,
+               .desc           = "esc sw",
+       },
+};
+
+static struct gpio_keys_platform_data apollon_gpio_keys = {
+       .buttons                = apollon_gpio_keys_buttons,
+       .nbuttons               = ARRAY_SIZE(apollon_gpio_keys_buttons),
+};
+
+static struct platform_device apollon_gpio_keys_device = {
+       .name                   = "gpio-keys",
+       .id                     = -1,
+       .dev                    = {
+               .platform_data  = &apollon_gpio_keys,
+       },
+};
+
+static void __init apollon_sw_init(void)
+{
+       /* Enter SW - Y11 */
+       omap_cfg_reg(Y11_242X_GPIO16);
+       /* Up SW - AA12 */
+       omap_cfg_reg(AA12_242X_GPIO17);
+       /* Down SW - AA8 */
+       omap_cfg_reg(AA8_242X_GPIO58);
+
+       if (apollon_plus()) {
+               /* Left SW - P18 */
+               omap_cfg_reg(P18_24XX_GPIO95);
+               /* Right SW - M18 */
+               omap_cfg_reg(M18_24XX_GPIO96);
+               /* Esc SW - L14 */
+               omap_cfg_reg(L14_24XX_GPIO97);
+       } else
+               apollon_gpio_keys.nbuttons = 3;
+}
+
+static int __init omap_apollon_keys_init(void)
+{
+       apollon_sw_init();
+
+       return platform_device_register(&apollon_gpio_keys_device);
+}
+
+arch_initcall(omap_apollon_keys_init);
diff --git a/arch/arm/mach-omap2/board-apollon-mmc.c b/arch/arm/mach-omap2/board-apollon-mmc.c
new file mode 100644 (file)
index 0000000..3197741
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * linux/arch/arm/mach-omap2/board-apollon-mmc.c
+ *
+ * Copyright (C) 2005-2007 Samsung Electronics
+ * Author: Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#include <mach/gpio.h>
+#include <mach/mmc.h>
+
+#ifdef CONFIG_MMC_OMAP
+
+static struct device *mmc_device;
+
+static int apollon_mmc_set_power(struct device *dev, int slot, int power_on,
+                                       int vdd)
+{
+#ifdef CONFIG_MMC_DEBUG
+       dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1,
+               power_on ? "on" : "off", vdd);
+#endif
+       if (slot != 0) {
+               dev_err(dev, "No such slot %d\n", slot + 1);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int apollon_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode)
+{
+#ifdef CONFIG_MMC_DEBUG
+       dev_dbg(dev, "Set slot %d bus_mode %s\n", slot + 1,
+               bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull");
+#endif
+       if (slot != 0) {
+               dev_err(dev, "No such slot %d\n", slot + 1);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int apollon_mmc_late_init(struct device *dev)
+{
+       mmc_device = dev;
+
+       return 0;
+}
+
+static void apollon_mmc_cleanup(struct device *dev)
+{
+}
+
+/*
+ * Note: If you want to detect card feature, please assign GPIO 37
+ */
+static struct omap_mmc_platform_data mmc1_data = {
+       .nr_slots                       = 1,
+       .init                           = apollon_mmc_late_init,
+       .cleanup                        = apollon_mmc_cleanup,
+       .dma_mask                       = 0xffffffff,
+       .slots[0]       = {
+               .wires                  = 4,
+
+               /*
+                * Use internal loop-back in MMC/SDIO Module Input Clock
+                * selection
+                */
+               .internal_clock         = 1,
+
+               .set_power              = apollon_mmc_set_power,
+               .set_bus_mode           = apollon_mmc_set_bus_mode,
+               .ocr_mask               = MMC_VDD_30_31 | MMC_VDD_31_32 |
+                                         MMC_VDD_32_33 | MMC_VDD_33_34,
+               .name                   = "mmcblk",
+       },
+};
+
+static struct omap_mmc_platform_data *mmc_data[OMAP24XX_NR_MMC];
+
+void __init apollon_mmc_init(void)
+{
+       mmc_data[0] = &mmc1_data;
+       omap2_init_mmc(mmc_data, OMAP24XX_NR_MMC);
+}
+
+#else  /* !CONFIG_MMC_OMAP */
+
+void __init apollon_mmc_init(void)
+{
+}
+
+#endif /* CONFIG_MMC_OMAP */
index 06dfba888b0ca408b9691e1623774f366e1569d3..936cb49bdd0c0156b78476a8977c4824284761e8 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * linux/arch/arm/mach-omap2/board-apollon.c
  *
 /*
  * linux/arch/arm/mach-omap2/board-apollon.c
  *
- * Copyright (C) 2005,2006 Samsung Electronics
+ * Copyright (C) 2005-2008 Samsung Electronics
  * Author: Kyungmin Park <kyungmin.park@samsung.com>
  *
  * Author: Kyungmin Park <kyungmin.park@samsung.com>
  *
- * Modified from mach-omap/omap2/board-h4.c
+ * Modified from mach-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.
  *
  * Code for apollon OMAP2 board. Should work on many OMAP2 systems where
  * the bootloader passes the board-specific data to the kernel.
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/onenand.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/onenand.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/leds.h>
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/leds.h>
 #include <linux/err.h>
 #include <linux/clk.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc210x.h>
 
 
+#include <asm/io.h>
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <mach/board.h>
 #include <mach/common.h>
 #include <mach/gpmc.h>
 #include <mach/board.h>
 #include <mach/common.h>
 #include <mach/gpmc.h>
-#include <mach/control.h>
 
 /* LED & Switch macros */
 #define LED0_GPIO13            13
 #define LED1_GPIO14            14
 #define LED2_GPIO15            15
 
 /* 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
+#define LED3_GPIO92            92
+#define LED4_GPIO93            93
 
 #define APOLLON_FLASH_CS       0
 #define APOLLON_ETH_CS         1
 
 #define APOLLON_FLASH_CS       0
 #define APOLLON_ETH_CS         1
+#define APOLLON_NOR_CS         3
 #define APOLLON_ETHR_GPIO_IRQ  74
 
 #define APOLLON_ETHR_GPIO_IRQ  74
 
+#define APOLLON_ONENAND_CS2_ADDRESS    (0x00000e40 | (0x10000000 >> 24))
+#define APOLLON_EXT_CS3_ADDRESS                (0x00000c40 | (0x18000000 >> 24))
+
+extern apollon_mmc_init(void);
+
+int apollon_plus(void)
+{
+       /* The apollon plus has IDCODE revision 5 */
+       return omap_rev() & 0xc0;
+}
+
 static struct mtd_partition apollon_partitions[] = {
        {
                .name           = "X-Loader + U-Boot",
 static struct mtd_partition apollon_partitions[] = {
        {
                .name           = "X-Loader + U-Boot",
@@ -108,6 +123,63 @@ static struct platform_device apollon_onenand_device = {
        .resource       = apollon_flash_resource,
 };
 
        .resource       = apollon_flash_resource,
 };
 
+static struct mtd_partition apollon_nor_partitions[] = {
+       {
+               .name           = "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_4M - SZ_256K,
+       },
+       {
+               .name           = "application",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = SZ_8M + SZ_2M,
+       },
+       {
+               .name           = "reserved",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = MTDPART_SIZ_FULL,
+       },
+};
+
+static struct flash_platform_data apollon_nor_data = {
+       .map_name               = "cfi_probe",
+       .width          = 2,
+       .parts          = apollon_nor_partitions,
+       .nr_parts       = ARRAY_SIZE(apollon_nor_partitions),
+};
+
+static struct resource apollon_nor_resource[] = {
+       [0] = {
+               .flags  = IORESOURCE_MEM,
+       }
+};
+
+static struct platform_device apollon_nor_device = {
+       .name           = "omapflash",
+       .id             = -1,
+       .dev            = {
+               .platform_data  = &apollon_nor_data,
+       },
+       .num_resources  = ARRAY_SIZE(apollon_nor_resource),
+       .resource       = apollon_nor_resource,
+};
+
 static void __init apollon_flash_init(void)
 {
        unsigned long base;
 static void __init apollon_flash_init(void)
 {
        unsigned long base;
@@ -118,6 +190,13 @@ static void __init apollon_flash_init(void)
        }
        apollon_flash_resource[0].start = base;
        apollon_flash_resource[0].end   = base + SZ_128K - 1;
        }
        apollon_flash_resource[0].start = base;
        apollon_flash_resource[0].end   = base + SZ_128K - 1;
+
+       if (gpmc_cs_request(APOLLON_NOR_CS, SZ_32M, &base) < 0) {
+               printk(KERN_ERR "Cannot request NOR GPMC CS\n");
+               return;
+       }
+       apollon_nor_resource[0].start = base;
+       apollon_nor_resource[0].end   = base + SZ_32M - 1;
 }
 
 static struct resource apollon_smc91x_resources[] = {
 }
 
 static struct resource apollon_smc91x_resources[] = {
@@ -143,34 +222,37 @@ static struct platform_device apollon_lcd_device = {
        .id             = -1,
 };
 
        .id             = -1,
 };
 
-static struct omap_led_config apollon_led_config[] = {
+static struct gpio_led apollon_led_config[] = {
        {
        {
-               .cdev   = {
-                       .name   = "apollon:led0",
-               },
-               .gpio   = LED0_GPIO13,
+               .name                   = "d2",
+               .gpio                   = LED0_GPIO13,
+               .default_trigger        = "heartbeat",
        },
        {
        },
        {
-               .cdev   = {
-                       .name   = "apollon:led1",
-               },
-               .gpio   = LED1_GPIO14,
+               .name                   = "d3",
+               .gpio                   = LED1_GPIO14,
        },
        {
        },
        {
-               .cdev   = {
-                       .name   = "apollon:led2",
-               },
-               .gpio   = LED2_GPIO15,
+               .name                   = "d4",
+               .gpio                   = LED2_GPIO15,
+       },
+       {
+               .name                   = "d5",
+               .gpio                   = LED3_GPIO92,
+       },
+       {
+               .name                   = "d6",
+               .gpio                   = LED4_GPIO93,
        },
 };
 
        },
 };
 
-static struct omap_led_platform_data apollon_led_data = {
-       .nr_leds        = ARRAY_SIZE(apollon_led_config),
+static struct gpio_led_platform_data apollon_led_data = {
+       .num_leds       = ARRAY_SIZE(apollon_led_config),
        .leds           = apollon_led_config,
 };
 
 static struct platform_device apollon_led_device = {
        .leds           = apollon_led_config,
 };
 
 static struct platform_device apollon_led_device = {
-       .name           = "omap-led",
+       .name           = "leds-gpio",
        .id             = -1,
        .dev            = {
                .platform_data  = &apollon_led_data,
        .id             = -1,
        .dev            = {
                .platform_data  = &apollon_led_data,
@@ -179,6 +261,7 @@ static struct platform_device apollon_led_device = {
 
 static struct platform_device *apollon_devices[] __initdata = {
        &apollon_onenand_device,
 
 static struct platform_device *apollon_devices[] __initdata = {
        &apollon_onenand_device,
+       &apollon_nor_device,
        &apollon_smc91x_device,
        &apollon_lcd_device,
        &apollon_led_device,
        &apollon_smc91x_device,
        &apollon_lcd_device,
        &apollon_led_device,
@@ -187,7 +270,6 @@ static struct platform_device *apollon_devices[] __initdata = {
 static inline void __init apollon_init_smc91x(void)
 {
        unsigned long base;
 static inline void __init apollon_init_smc91x(void)
 {
        unsigned long base;
-
        unsigned int rate;
        struct clk *gpmc_fck;
        int eth_cs;
        unsigned int rate;
        struct clk *gpmc_fck;
        int eth_cs;
@@ -226,7 +308,7 @@ static inline void __init apollon_init_smc91x(void)
                gpmc_cs_write_reg(eth_cs, GPMC_CS_CONFIG6, 0x000003C2);
        }
 
                gpmc_cs_write_reg(eth_cs, GPMC_CS_CONFIG6, 0x000003C2);
        }
 
-       if (gpmc_cs_request(APOLLON_ETH_CS, SZ_16M, &base) < 0) {
+       if (gpmc_cs_request(eth_cs, SZ_16M, &base) < 0) {
                printk(KERN_ERR "Failed to request GPMC CS for smc91x\n");
                goto out;
        }
                printk(KERN_ERR "Failed to request GPMC CS for smc91x\n");
                goto out;
        }
@@ -238,7 +320,7 @@ static inline void __init apollon_init_smc91x(void)
        if (gpio_request(APOLLON_ETHR_GPIO_IRQ, "SMC91x irq") < 0) {
                printk(KERN_ERR "Failed to request GPIO%d for smc91x IRQ\n",
                        APOLLON_ETHR_GPIO_IRQ);
        if (gpio_request(APOLLON_ETHR_GPIO_IRQ, "SMC91x irq") < 0) {
                printk(KERN_ERR "Failed to request GPIO%d for smc91x IRQ\n",
                        APOLLON_ETHR_GPIO_IRQ);
-               gpmc_cs_free(APOLLON_ETH_CS);
+               gpmc_cs_free(eth_cs);
                goto out;
        }
        gpio_direction_input(APOLLON_ETHR_GPIO_IRQ);
                goto out;
        }
        gpio_direction_input(APOLLON_ETHR_GPIO_IRQ);
@@ -256,6 +338,24 @@ static void __init omap_apollon_init_irq(void)
        apollon_init_smc91x();
 }
 
        apollon_init_smc91x();
 }
 
+static struct tsc210x_config tsc_platform_data = {
+       .use_internal           = 1,
+       .monitor                = TSC_TEMP,
+       .mclk                   = "sys_clkout",
+};
+
+static struct spi_board_info apollon_spi_board_info[] = {
+       [0] = {
+               .modalias       = "tsc2101",
+               .irq            = OMAP_GPIO_IRQ(85),
+               .bus_num        = 1,
+               .chip_select    = 0,
+               .mode           = SPI_MODE_1,
+               .max_speed_hz   = 6000000,
+               .platform_data  = &tsc_platform_data,
+       },
+};
+
 static struct omap_uart_config apollon_uart_config __initdata = {
        .enabled_uarts = (1 << 0) | (0 << 1) | (0 << 2),
 };
 static struct omap_uart_config apollon_uart_config __initdata = {
        .enabled_uarts = (1 << 0) | (0 << 1) | (0 << 2),
 };
@@ -271,7 +371,7 @@ static struct omap_lcd_config apollon_lcd_config __initdata = {
        .ctrl_name      = "internal",
 };
 
        .ctrl_name      = "internal",
 };
 
-static struct omap_board_config_kernel apollon_config[] = {
+static struct omap_board_config_kernel apollon_config[] __initdata = {
        { OMAP_TAG_UART,        &apollon_uart_config },
        { OMAP_TAG_LCD,         &apollon_lcd_config },
 };
        { OMAP_TAG_UART,        &apollon_uart_config },
        { OMAP_TAG_LCD,         &apollon_lcd_config },
 };
@@ -280,16 +380,18 @@ static void __init apollon_led_init(void)
 {
        /* LED0 - AA10 */
        omap_cfg_reg(AA10_242X_GPIO13);
 {
        /* LED0 - AA10 */
        omap_cfg_reg(AA10_242X_GPIO13);
-       gpio_request(LED0_GPIO13, "LED0");
-       gpio_direction_output(LED0_GPIO13, 0);
        /* LED1  - AA6 */
        omap_cfg_reg(AA6_242X_GPIO14);
        /* LED1  - AA6 */
        omap_cfg_reg(AA6_242X_GPIO14);
-       gpio_request(LED1_GPIO14, "LED1");
-       gpio_direction_output(LED1_GPIO14, 0);
        /* LED2  - AA4 */
        omap_cfg_reg(AA4_242X_GPIO15);
        /* LED2  - AA4 */
        omap_cfg_reg(AA4_242X_GPIO15);
-       gpio_request(LED2_GPIO15, "LED2");
-       gpio_direction_output(LED2_GPIO15, 0);
+
+       if (apollon_plus()) {
+               /* LED3 - M15 */
+               omap_cfg_reg(M15_24XX_GPIO92);
+               /* LED4 - P20 */
+               omap_cfg_reg(P20_24XX_GPIO93);
+       } else
+               apollon_led_data.num_leds = 3;
 }
 
 static void __init apollon_usb_init(void)
 }
 
 static void __init apollon_usb_init(void)
@@ -302,21 +404,101 @@ static void __init apollon_usb_init(void)
        omap_usb_init(&apollon_usb_config);
 }
 
        omap_usb_init(&apollon_usb_config);
 }
 
-static void __init omap_apollon_init(void)
+static void __init apollon_tsc_init(void)
+{
+       /* TSC2101 */
+       omap_cfg_reg(N15_24XX_GPIO85);
+       gpio_request(85, "tsc2101 irq");
+       gpio_direction_input(85);
+
+       omap_cfg_reg(W14_24XX_SYS_CLKOUT);      /* mclk */
+}
+
+static void __init apollon_cs_init(void)
 {
 {
-       u32 v;
+       unsigned long base;
+       unsigned int rate;
+       struct clk *l3ck;
+       u32 value;
+       int cs, sync = 0;
+
+       l3ck = clk_get(NULL, "core_l3_ck");
+       if (IS_ERR(l3ck))
+               rate = 100000000;
+       else
+               rate = clk_get_rate(l3ck);
+
+       /* CS2: OneNAND */
+       cs = 2;
+       value = gpmc_cs_read_reg(0, GPMC_CS_CONFIG1);
+       gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, value);
+       value = gpmc_cs_read_reg(0, GPMC_CS_CONFIG2);
+       gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, value);
+       value = gpmc_cs_read_reg(0, GPMC_CS_CONFIG3);
+       gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, value);
+       value = gpmc_cs_read_reg(0, GPMC_CS_CONFIG4);
+       gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, value);
+       value = gpmc_cs_read_reg(0, GPMC_CS_CONFIG5);
+       gpmc_cs_write_reg(cs, GPMC_CS_CONFIG5, value);
+       value = gpmc_cs_read_reg(0, GPMC_CS_CONFIG6);
+       gpmc_cs_write_reg(cs, GPMC_CS_CONFIG6, value);
+
+       gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, APOLLON_ONENAND_CS2_ADDRESS);
+
+       /* CS3: External NOR */
+       cs = APOLLON_NOR_CS;
+       if (rate >= 160000000) {
+               sync = 1;
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, 0xe5011211);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, 0x00090b01);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, 0x00020201);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, 0x09030b03);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG5, 0x010a0a0c);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG6, 0x00000000);
+       } else if (rate >= 130000000) {
+               /* Not yet know ... Use the async values */
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, 0x00021201);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, 0x00121601);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, 0x00040401);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, 0x12061605);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG5, 0x01151317);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG6, 0x00000000);
+       } else {/* rate = 100000000 */
+               sync = 1;
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, 0xe1001202);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, 0x00151501);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, 0x00050501);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, 0x0e070e07);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG5, 0x01131F1F);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG6, 0x00000000);
+       }
+
+       gpmc_cs_write_reg(cs, GPMC_CS_CONFIG7, APOLLON_EXT_CS3_ADDRESS);
+
+       if (gpmc_cs_request(cs, SZ_32M, &base) < 0) {
+               printk(KERN_ERR "Failed to request GPMC CS for external\n");
+               return;
+       }
 
 
+       /* Synchronous mode */
+       if (sync) {
+               void __iomem *addr = ioremap(base, SZ_32M);
+               writew(0xaa, addr + 0xaaa);
+               writew(0x55, addr + 0x554);
+               writew(0xc0, addr + 0x24aaa);
+               iounmap(addr);
+       }
+
+       gpmc_cs_free(cs);
+}
+
+static void __init omap_apollon_init(void)
+{
+       apollon_cs_init();
        apollon_led_init();
        apollon_flash_init();
        apollon_usb_init();
        apollon_led_init();
        apollon_flash_init();
        apollon_usb_init();
-
-       /* REVISIT: where's the correct place */
-       omap_cfg_reg(W19_24XX_SYS_NIRQ);
-
-       /* Use Interal loop-back in MMC/SDIO Module Input Clock selection */
-       v = omap_ctrl_readl(OMAP2_CONTROL_DEVCONF0);
-       v |= (1 << 24);
-       omap_ctrl_writel(v, OMAP2_CONTROL_DEVCONF0);
+       apollon_tsc_init();
 
        /*
         * Make sure the serial ports are muxed on at this point.
 
        /*
         * Make sure the serial ports are muxed on at this point.
@@ -327,6 +509,13 @@ static void __init omap_apollon_init(void)
        omap_board_config = apollon_config;
        omap_board_config_size = ARRAY_SIZE(apollon_config);
        omap_serial_init();
        omap_board_config = apollon_config;
        omap_board_config_size = ARRAY_SIZE(apollon_config);
        omap_serial_init();
+       omap_register_i2c_bus(1, 100, NULL, 0);
+       omap_register_i2c_bus(2, 100, NULL, 0);
+
+       spi_register_board_info(apollon_spi_board_info,
+                               ARRAY_SIZE(apollon_spi_board_info));
+
+       apollon_mmc_init();
 }
 
 static void __init omap_apollon_map_io(void)
 }
 
 static void __init omap_apollon_map_io(void)
index 3492162a65c383907bb093702b0c9921ff1b8ac2..0e353b3611f373503dbb6674e1ce15d85d606125 100644 (file)
@@ -41,7 +41,7 @@ static struct omap_uart_config generic_uart_config __initdata = {
        .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
 };
 
        .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
 };
 
-static struct omap_board_config_kernel generic_config[] = {
+static struct omap_board_config_kernel generic_config[] __initdata = {
        { OMAP_TAG_UART,        &generic_uart_config },
 };
 
        { OMAP_TAG_UART,        &generic_uart_config },
 };
 
@@ -50,6 +50,8 @@ static void __init omap_generic_init(void)
        omap_board_config = generic_config;
        omap_board_config_size = ARRAY_SIZE(generic_config);
        omap_serial_init();
        omap_board_config = generic_config;
        omap_board_config_size = ARRAY_SIZE(generic_config);
        omap_serial_init();
+       omap_register_i2c_bus(1, 100, NULL, 0);
+       omap_register_i2c_bus(2, 100, NULL, 0);
 }
 
 static void __init omap_generic_map_io(void)
 }
 
 static void __init omap_generic_map_io(void)
index a0267a9ab466e9638b9bd74c1e045d2e1321c5de..cbca22a0199a27de88333a054db3c4cc54e2d67f 100644 (file)
 #include <linux/mtd/partitions.h>
 #include <linux/delay.h>
 #include <linux/workqueue.h>
 #include <linux/mtd/partitions.h>
 #include <linux/delay.h>
 #include <linux/workqueue.h>
-#include <linux/i2c.h>
-#include <linux/i2c/at24.h>
 #include <linux/input.h>
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/input.h>
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/gpio.h>
+#include <linux/leds.h>
+
+#include <linux/i2c/at24.h>
+#include <linux/i2c/menelaus.h>
+#include <linux/i2c/pcf857x.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc210x.h>
+
+#include <media/v4l2-int-device.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
 #include <asm/mach/flash.h>
 
 #include <mach/control.h>
 #include <asm/mach/flash.h>
 
 #include <mach/control.h>
-#include <mach/gpio.h>
-#include <mach/gpioexpander.h>
 #include <mach/mux.h>
 #include <mach/usb.h>
 #include <mach/irda.h>
 #include <mach/board.h>
 #include <mach/common.h>
 #include <mach/keypad.h>
 #include <mach/mux.h>
 #include <mach/usb.h>
 #include <mach/irda.h>
 #include <mach/board.h>
 #include <mach/common.h>
 #include <mach/keypad.h>
-#include <mach/menelaus.h>
 #include <mach/dma.h>
 #include <mach/gpmc.h>
 
 #include <mach/dma.h>
 #include <mach/gpmc.h>
 
 
 #define H4_ETHR_GPIO_IRQ               92
 
 
 #define H4_ETHR_GPIO_IRQ               92
 
+/* FPGA on debug board has 32 GPIOs:  16 dedicated to leds,
+ * 8 outputs on a header, and 6 inputs from a DIP switch.
+ */
+#define H4_DEBUG_GPIO_BASE             OMAP_MAX_GPIO_LINES
+#      define H4_DEBUG_GPIO_SW3_1      (H4_DEBUG_GPIO_BASE + 24)
+#      define H4_DEBUG_GPIO_SW3_2      (H4_DEBUG_GPIO_BASE + 25)
+#      define H4_DEBUG_GPIO_SW3_3      (H4_DEBUG_GPIO_BASE + 26)
+#      define H4_DEBUG_GPIO_SW3_4      (H4_DEBUG_GPIO_BASE + 27)
+#      define H4_DEBUG_GPIO_SW3_5      (H4_DEBUG_GPIO_BASE + 28)
+#      define H4_DEBUG_GPIO_SW3_8      (H4_DEBUG_GPIO_BASE + 29)
+
+/* H4 baseboard has 3 PCF8574 (8 bit) I2C GPIO expanders */
+#define H4_U191_GPIO_BASE              (H4_DEBUG_GPIO_BASE + 32)
+#      define H4_GPIO_IRDA_FIRSEL      (H4_U191_GPIO_BASE + 0)
+#      define H4_GPIO_MODEM_MOD_EN     (H4_U191_GPIO_BASE + 1)
+#      define H4_GPIO_WLAN_MOD_EN      (H4_U191_GPIO_BASE + 2)
+#      define H4_GPIO_CAM_MODULE_EN    (H4_U191_GPIO_BASE + 3)
+#      define H4_GPIO_HANDSET_EN       (H4_U191_GPIO_BASE + 4)
+#      define H4_GPIO_LCD_ENBKL        (H4_U191_GPIO_BASE + 5)
+#      define H4_GPIO_AUDIO_ENVDD      (H4_U191_GPIO_BASE + 6)
+#      define H4_GPIO_LCD_ENVDD        (H4_U191_GPIO_BASE + 7)
+
+#define H4_U192_GPIO_BASE              (H4_U191_GPIO_BASE + 8)
+#      define H4_GPIO_IRDA_AGPSn       (H4_U192_GPIO_BASE + 0)
+#      define H4_GPIO_AGPS_PWREN       (H4_U192_GPIO_BASE + 1)
+#      define H4_GPIO_AGPS_RSTn        (H4_U192_GPIO_BASE + 2)
+#      define H4_GPIO_AGPS_SLEEP       (H4_U192_GPIO_BASE + 3)
+#      define H4_GPIO_AGPS_PA_XMT      (H4_U192_GPIO_BASE + 4)
+#      define H4_GPIO_MODEM_SPR2       (H4_U192_GPIO_BASE + 5)
+#      define H4_GPIO_MODEM_SPR1       (H4_U192_GPIO_BASE + 6)
+#      define H4_GPIO_BT_ACLK_ENn      (H4_U192_GPIO_BASE + 7)
+
+#define H4_U193_GPIO_BASE              (H4_U192_GPIO_BASE + 8)
+#      define H4_GPIO_SPR0             (H4_U193_GPIO_BASE + 0)
+#      define H4_GPIO_SPR1             (H4_U193_GPIO_BASE + 1)
+#      define H4_GPIO_WLAN_SHUTDOWN    (H4_U193_GPIO_BASE + 2)
+#      define H4_GPIO_WLAN_RESET       (H4_U193_GPIO_BASE + 3)
+#      define H4_GPIO_WLAN_CLK_ENn     (H4_U193_GPIO_BASE + 4)
+       /* 5, 6 not connected */
+#      define H4_GPIO_CAM_RST          (H4_U193_GPIO_BASE + 7)
+
 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 unsigned int row_gpios[6] = { 88, 89, 124, 11, 6, 96 };
 static unsigned int col_gpios[7] = { 90, 91, 100, 36, 12, 97, 98 };
 
@@ -138,31 +186,16 @@ static struct platform_device h4_flash_device = {
        .resource       = &h4_flash_resource,
 };
 
        .resource       = &h4_flash_resource,
 };
 
-/* Select between the IrDA and aGPS module
- */
+#if defined(CONFIG_OMAP_IR) || defined(CONFIG_OMAP_IR_MODULE)
+
+/* Select between the IrDA and aGPS module */
 static int h4_select_irda(struct device *dev, int state)
 {
 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;
-       }
+       /* U192.P0 = high for IRDA; else AGPS */
+       gpio_set_value_cansleep(H4_GPIO_IRDA_AGPSn, state & IR_SEL);
 
 
-       /* '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;
+       /* NOTE:  UART3 can also hook up to a DB9 or to GSM ... */
+       return 0;
 }
 
 static void set_trans_mode(struct work_struct *work)
 }
 
 static void set_trans_mode(struct work_struct *work)
@@ -170,22 +203,9 @@ static void set_trans_mode(struct work_struct *work)
        struct omap_irda_config *irda_config =
                container_of(work, struct omap_irda_config, gpio_expa.work);
        int mode = irda_config->mode;
        struct omap_irda_config *irda_config =
                container_of(work, struct omap_irda_config, gpio_expa.work);
        int mode = irda_config->mode;
-       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");
-       }
+       /* U191.P0 = low for SIR; else MIR/FIR */
+       gpio_set_value_cansleep(H4_GPIO_IRDA_FIRSEL, !(mode & IR_SIRMODE));
 }
 
 static int h4_transceiver_mode(struct device *dev, int mode)
 }
 
 static int h4_transceiver_mode(struct device *dev, int mode)
@@ -199,6 +219,10 @@ static int h4_transceiver_mode(struct device *dev, int mode)
 
        return 0;
 }
 
        return 0;
 }
+#else
+static int h4_select_irda(struct device *dev, int state) { return 0; }
+static int h4_transceiver_mode(struct device *dev, int mode) { return 0; }
+#endif
 
 static struct omap_irda_config h4_irda_data = {
        .transceiver_cap        = IR_SIRMODE | IR_MIRMODE | IR_FIRMODE,
 
 static struct omap_irda_config h4_irda_data = {
        .transceiver_cap        = IR_SIRMODE | IR_MIRMODE | IR_FIRMODE,
@@ -269,6 +293,8 @@ static u32 get_sysboot_value(void)
                 OMAP2_SYSBOOT_1_MASK | OMAP2_SYSBOOT_0_MASK));
 }
 
                 OMAP2_SYSBOOT_1_MASK | OMAP2_SYSBOOT_0_MASK));
 }
 
+/* FIXME: This function should be moved to some other file, gpmc.c? */
+
 /* H4-2420's always used muxed mode, H4-2422's always use non-muxed
  *
  * Note: OMAP-GIT doesn't correctly do is_cpu_omap2422 and is_cpu_omap2423
 /* H4-2420's always used muxed mode, H4-2422's always use non-muxed
  *
  * Note: OMAP-GIT doesn't correctly do is_cpu_omap2422 and is_cpu_omap2423
@@ -372,7 +398,11 @@ static void __init omap_h4_init_irq(void)
 }
 
 static struct omap_uart_config h4_uart_config __initdata = {
 }
 
 static struct omap_uart_config h4_uart_config __initdata = {
+#ifdef CONFIG_MACH_OMAP2_H4_USB1
+       .enabled_uarts = ((1 << 0) | (1 << 1)),
+#else
        .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
        .enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
+#endif
 };
 
 static struct omap_lcd_config h4_lcd_config __initdata = {
 };
 
 static struct omap_lcd_config h4_lcd_config __initdata = {
@@ -412,21 +442,309 @@ static struct omap_usb_config h4_usb_config __initdata = {
 #endif
 };
 
 #endif
 };
 
-static struct omap_board_config_kernel h4_config[] = {
+/* ----------------------------------------------------------------------- */
+
+static struct tsc210x_config tsc_platform_data = {
+       .use_internal           = 1,
+       .monitor                = TSC_VBAT | TSC_TEMP,
+       /* REVISIT temp calibration data -- board-specific; from EEPROM? */
+       .mclk                   = "sys_clkout",
+};
+
+static struct spi_board_info h4_spi_board_info[] __initdata = {
+       {
+               .modalias       = "tsc2101",
+               .bus_num        = 1,
+               .chip_select    = 0,
+               .mode           = SPI_MODE_1,
+               .irq            = OMAP_GPIO_IRQ(93),
+               .max_speed_hz   = 16000000,
+               .platform_data  = &tsc_platform_data,
+       },
+
+       /* nCS1 -- to lcd board, but unused
+        * nCS2 -- to WLAN/miniPCI
+        */
+};
+
+/* ----------------------------------------------------------------------- */
+
+static struct omap_board_config_kernel h4_config[] __initdata = {
        { OMAP_TAG_UART,        &h4_uart_config },
        { OMAP_TAG_LCD,         &h4_lcd_config },
 };
 
        { OMAP_TAG_UART,        &h4_uart_config },
        { OMAP_TAG_LCD,         &h4_lcd_config },
 };
 
+#ifdef CONFIG_MACH_OMAP_H4_TUSB
+
+#include <linux/usb/musb.h>
+
+static struct musb_hdrc_platform_data tusb_data = {
+       .mode           = MUSB_OTG,
+       .min_power      = 25,   /* x2 = 50 mA drawn from VBUS as peripheral */
+
+       /* 1.8V supplied by Menelaus, other voltages supplied by VBAT;
+        * so no switching.
+        */
+};
+
+static void __init tusb_evm_setup(void)
+{
+       static char     announce[] __initdata =
+                               KERN_INFO "TUSB 6010 EVM\n";
+       int             irq;
+       unsigned        dmachan = 0;
+
+       /* There are at least 32 different combinations of boards that
+        * are loosely called "H4", with a 2420 ... different OMAP chip
+        * revisions (with pin mux changes for DMAREQ, GPMC errata, etc),
+        * modifications of the CPU board, mainboard, EVM, TUSB etc.
+        * Plus omap2422, omap2423, etc.
+        *
+        * So you might need to tweak this setup to make the TUSB EVM
+        * behave on your particular setup ...
+        */
+
+       /* Already set up:  GPMC AD[0..15], CLK, nOE, nWE, nADV_ALE */
+       omap_cfg_reg(E2_GPMC_NCS2);
+       omap_cfg_reg(L2_GPMC_NCS7);
+       omap_cfg_reg(M1_GPMC_WAIT2);
+
+       switch ((omap_rev() >> 8) & 0x0f) {
+       case 0:         /* ES 1.0 */
+       case 1:         /* ES 2.0 */
+               /* Assume early board revision without optional ES2.0
+                * rework to swap J15 & AA10 so DMAREQ0 works
+                */
+               omap_cfg_reg(AA10_242X_GPIO13);
+               irq = 13;
+               /* omap_cfg_reg(J15_24XX_DMAREQ0); */
+               break;
+       default:
+               /* Later Menelaus boards can support all 6 DMA request
+                * lines, at the price of boot flash A23-A26.
+                */
+               omap_cfg_reg(J15_24XX_GPIO99);
+               irq = 99;
+               dmachan = (1 << 1) | (1 << 0);
+#if !(defined(CONFIG_MTD_OMAP_NOR) || defined(CONFIG_MTD_OMAP_NOR_MODULE))
+               dmachan |= (1 << 5) | (1 << 4) (1 << 3) | (1 << 2);
+#endif
+               break;
+       }
+
+       if (tusb6010_setup_interface(&tusb_data,
+                       TUSB6010_REFCLK_24, /* waitpin */ 2,
+                       /* async cs */ 2, /* sync cs */ 7,
+                       irq, dmachan) == 0)
+               printk(announce);
+}
+
+#endif
+
+#if defined(CONFIG_VIDEO_OV9640) || defined(CONFIG_VIDEO_OV9640_MODULE)
+/*
+ * 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, 0x8F }, /* COM7, CLKRC, COM8 */
+       { 0x01, 0x80 }, { 0x02, 0x80 }, { 0x04, 0x00 }, /* BLUE, RED, COM1 */
+       { 0x0E, 0x81 }, { 0x0F, 0x4F }, { 0x14, 0x4A }, /* 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, 0x8F }, { 0x00, 0x7F }, /* GST15, COM8 */
+       { OV9640_REG_TERM, OV9640_VAL_TERM }
+};
+
+static int ov9640_sensor_power_set(int power)
+{
+       /* power up the sensor? */
+       gpio_set_value_cansleep(H4_GPIO_CAM_MODULE_EN, power);
+
+       /* take it out of reset if it's not powered */
+       gpio_direction_output(H4_GPIO_CAM_RST, !power);
+
+       return 0;
+}
+
+static struct v4l2_ifparm ifparm = {
+       .if_type = V4L2_IF_TYPE_BT656,
+       .u = {
+               .bt656 = {
+                        .frame_start_on_rising_vs = 1,
+                        .nobt_vs_inv = 1,
+                        .mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT,
+                        .clock_min = OV9640_XCLK_MIN,
+                        .clock_max = OV9640_XCLK_MAX,
+                },
+       },
+};
+
+static int ov9640_ifparm(struct v4l2_ifparm *p)
+{
+       *p = ifparm;
+
+       return 0;
+}
+
+static struct ov9640_platform_data h4_ov9640_platform_data = {
+       .power_set      = ov9640_sensor_power_set,
+       .default_regs   = ov9640_common,
+       .ifparm         = ov9640_ifparm,
+};
+
+#endif
+
+/* leave LCD powered off unless it will be used */
+#if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE)
+#define LCD_ENABLED            true
+#else
+#define LCD_ENABLED            false
+#endif
+
+static struct gpio_led backlight_leds[] = {
+       {
+               .name                   = "lcd_h4",
+               .default_trigger        = "backlight",
+               .gpio                   = H4_GPIO_LCD_ENBKL,
+       },
+       { },
+};
+
+static struct gpio_led_platform_data backlight_led_data = {
+       .num_leds               = 1,
+       .leds                   = backlight_leds,
+};
+
+static struct platform_device h4_backlight_device = {
+       .name                   = "leds-gpio",
+       .id                     = 0,
+       .dev.platform_data      = &backlight_led_data,
+};
+
+static int
+u191_setup(struct i2c_client *client, int gpio, unsigned ngpio, void *context)
+{
+       /* P0 = IRDA control, FIR/MIR vs SIR */
+       gpio_request(H4_GPIO_IRDA_FIRSEL, "irda_firsel");
+       gpio_direction_output(H4_GPIO_IRDA_FIRSEL, false);
+
+       /* P3 = camera sensor module PWDN */
+       gpio_request(H4_GPIO_CAM_MODULE_EN, "camera_en");
+       gpio_direction_output(H4_GPIO_CAM_MODULE_EN, false);
+
+       /* P7 = LCD_ENVDD ... controls power to LCD (including backlight)
+        * P5 = LCD_ENBKL ... switches backlight
+        */
+       gpio_request(H4_GPIO_LCD_ENVDD, "lcd_power");
+       gpio_direction_output(H4_GPIO_LCD_ENVDD, LCD_ENABLED);
+       if (LCD_ENABLED) {
+               h4_backlight_device.dev.parent = &client->dev;
+               platform_device_register(&h4_backlight_device);
+       }
+
+       /* P6 = AUDIO_ENVDD ... switch power to microphone */
+       gpio_request(H4_GPIO_AUDIO_ENVDD, "audio_power");
+       gpio_direction_output(H4_GPIO_AUDIO_ENVDD, true);
+
+       return 0;
+}
+
+
+static struct pcf857x_platform_data u191_platform_data = {
+       .gpio_base      = H4_U191_GPIO_BASE,
+       .setup          = u191_setup,
+};
+
+static int
+u192_setup(struct i2c_client *client, int gpio, unsigned ngpio, void *context)
+{
+       gpio_request(H4_GPIO_IRDA_AGPSn, "irda/agps");
+       gpio_direction_output(H4_GPIO_IRDA_AGPSn, false);
+
+       return 0;
+}
+
+static struct pcf857x_platform_data u192_platform_data = {
+       .gpio_base      = H4_U192_GPIO_BASE,
+       .setup          = u192_setup,
+};
+
+static int
+u193_setup(struct i2c_client *client, int gpio, unsigned ngpio, void *context)
+{
+       /* reset sensor */
+       gpio_request(H4_GPIO_CAM_RST, "camera_rst");
+       gpio_direction_output(H4_GPIO_CAM_RST, true);
+
+       return 0;
+}
+
+static struct pcf857x_platform_data u193_platform_data = {
+       .gpio_base      = H4_U193_GPIO_BASE,
+       .setup          = u193_setup,
+};
+
 static struct at24_platform_data m24c01 = {
        .byte_len       = SZ_1K / 8,
        .page_size      = 16,
 };
 
 static struct i2c_board_info __initdata h4_i2c_board_info[] = {
 static struct at24_platform_data m24c01 = {
        .byte_len       = SZ_1K / 8,
        .page_size      = 16,
 };
 
 static struct i2c_board_info __initdata h4_i2c_board_info[] = {
+       {       /* U191 gpios */
+               I2C_BOARD_INFO("pcf8574", 0x20),
+               .platform_data  = &u191_platform_data,
+       },
+       {       /* U192 gpios */
+               I2C_BOARD_INFO("pcf8574", 0x21),
+               .platform_data  = &u192_platform_data,
+       },
+       {       /* U193 gpios */
+               I2C_BOARD_INFO("pcf8574", 0x22),
+               .platform_data  = &u193_platform_data,
+       },
+       {
+               I2C_BOARD_INFO("rv5c387a", 0x32),
+               /* no IRQ wired to OMAP; nINTB goes to AGPS */
+       },
+       {
+               I2C_BOARD_INFO("menelaus", 0x72),
+               .irq = INT_24XX_SYS_NIRQ,
+       },
        {
                I2C_BOARD_INFO("isp1301_omap", 0x2d),
                .irq            = OMAP_GPIO_IRQ(125),
        },
        {
                I2C_BOARD_INFO("isp1301_omap", 0x2d),
                .irq            = OMAP_GPIO_IRQ(125),
        },
+#if defined(CONFIG_VIDEO_OV9640) || defined(CONFIG_VIDEO_OV9640_MODULE)
+       {
+               I2C_BOARD_INFO("ov9640", 0x30),
+               .platform_data = &h4_ov9640_platform_data,
+       },
+#endif
        {       /* EEPROM on mainboard */
                I2C_BOARD_INFO("24c01", 0x52),
                .platform_data  = &m24c01,
        {       /* EEPROM on mainboard */
                I2C_BOARD_INFO("24c01", 0x52),
                .platform_data  = &m24c01,
@@ -457,14 +775,54 @@ static void __init omap_h4_init(void)
        }
 #endif
 
        }
 #endif
 
-       i2c_register_board_info(1, h4_i2c_board_info,
-                       ARRAY_SIZE(h4_i2c_board_info));
+#ifdef CONFIG_MACH_OMAP2_H4_USB1
+       /* S3.3 controls whether these pins are for UART2 or USB1 */
+       omap_cfg_reg(N14_24XX_USB1_SE0);
+       omap_cfg_reg(P15_24XX_USB1_DAT);
+       omap_cfg_reg(W20_24XX_USB1_TXEN);
+       omap_cfg_reg(V19_24XX_USB1_RCV);
+#endif
+
+       /* Menelaus interrupt */
+       omap_cfg_reg(W19_24XX_SYS_NIRQ);
 
        platform_add_devices(h4_devices, ARRAY_SIZE(h4_devices));
        omap_board_config = h4_config;
        omap_board_config_size = ARRAY_SIZE(h4_config);
 
        platform_add_devices(h4_devices, ARRAY_SIZE(h4_devices));
        omap_board_config = h4_config;
        omap_board_config_size = ARRAY_SIZE(h4_config);
-       omap_usb_init(&h4_usb_config);
        omap_serial_init();
        omap_serial_init();
+       omap_usb_init(&h4_usb_config);
+       omap_register_i2c_bus(1, 100, h4_i2c_board_info,
+                             ARRAY_SIZE(h4_i2c_board_info));
+
+       /* smc91x, debug leds, ps/2, extra uarts */
+       h4_init_debug();
+
+#ifdef CONFIG_MACH_OMAP_H4_TUSB
+       tusb_evm_setup();
+#endif
+
+       /* defaults seem ok for:
+        * omap_cfg_reg(U18_24XX_SPI1_SCK);
+        * omap_cfg_reg(V20_24XX_SPI1_MOSI);
+        * omap_cfg_reg(T18_24XX_SPI1_MISO);
+        * omap_cfg_reg(U19_24XX_SPI1_NCS0);
+        */
+
+       /* TSC2101 */
+       omap_cfg_reg(P20_24XX_GPIO93);
+       gpio_request(93, "tsc_irq");
+       gpio_direction_input(93);
+
+       omap_cfg_reg(W14_24XX_SYS_CLKOUT);      /* mclk */
+       /* defaults seem ok for:
+        * omap_cfg_reg(Y15_EAC_AC_SCLK);       // bclk
+        * omap_cfg_reg(R14_EAC_AC_FS);
+        * omap_cfg_reg(V15_EAC_AC_DOUT);
+        * omap_cfg_reg(W15_EAC_AC_DIN);
+        */
+
+       spi_register_board_info(h4_spi_board_info,
+                               ARRAY_SIZE(h4_spi_board_info));
 }
 
 static void __init omap_h4_map_io(void)
 }
 
 static void __init omap_h4_map_io(void)
index da57b0fcda14c5c2ca0ac92e1da076f7176763ee..804fd8e7e974d0eeb40d492a15db89a7f1585147 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/input.h>
 #include <linux/platform_device.h>
 #include <linux/delay.h>
 #include <linux/input.h>
+#include <linux/gpio_keys.h>
 #include <linux/workqueue.h>
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
 #include <linux/i2c/twl4030.h>
 #include <linux/workqueue.h>
 #include <linux/err.h>
 #include <linux/clk.h>
 #include <linux/spi/spi.h>
 #include <linux/spi/ads7846.h>
 #include <linux/i2c/twl4030.h>
-#include <linux/io.h>
 #include <linux/smsc911x.h>
 
 #include <mach/hardware.h>
 #include <linux/smsc911x.h>
 
 #include <mach/hardware.h>
 #include <mach/board.h>
 #include <mach/common.h>
 #include <mach/gpmc.h>
 #include <mach/board.h>
 #include <mach/common.h>
 #include <mach/gpmc.h>
+#include <mach/usb.h>
 
 
+#include <asm/io.h>
 #include <asm/delay.h>
 #include <mach/control.h>
 #include <asm/delay.h>
 #include <mach/control.h>
-#include <mach/usb.h>
 
 #include "mmc-twl4030.h"
 
 
 #include "mmc-twl4030.h"
 
 #define LDP_SMSC911X_GPIO      152
 #define DEBUG_BASE             0x08000000
 #define LDP_ETHR_START         DEBUG_BASE
 #define LDP_SMSC911X_GPIO      152
 #define DEBUG_BASE             0x08000000
 #define LDP_ETHR_START         DEBUG_BASE
+#define ENABLE_VAUX1_DEDICATED 0x03
+#define ENABLE_VAUX1_DEV_GRP   0x20
+
+#define TWL4030_MSECURE_GPIO   22
 
 static struct resource ldp_smsc911x_resources[] = {
        [0] = {
 
 static struct resource ldp_smsc911x_resources[] = {
        [0] = {
@@ -77,8 +82,244 @@ static struct platform_device ldp_smsc911x_device = {
        },
 };
 
        },
 };
 
+static int ldp_twl4030_keymap[] = {
+       KEY(0, 0, KEY_1),
+       KEY(1, 0, KEY_2),
+       KEY(2, 0, KEY_3),
+       KEY(0, 1, KEY_4),
+       KEY(1, 1, KEY_5),
+       KEY(2, 1, KEY_6),
+       KEY(3, 1, KEY_F5),
+       KEY(0, 2, KEY_7),
+       KEY(1, 2, KEY_8),
+       KEY(2, 2, KEY_9),
+       KEY(3, 2, KEY_F6),
+       KEY(0, 3, KEY_F7),
+       KEY(1, 3, KEY_0),
+       KEY(2, 3, KEY_F8),
+       PERSISTENT_KEY(4, 5),
+       KEY(4, 4, KEY_VOLUMEUP),
+       KEY(5, 5, KEY_VOLUMEDOWN),
+       0
+};
+
+static struct twl4030_keypad_data ldp_kp_twl4030_data = {
+       .rows           = 6,
+       .cols           = 6,
+       .keymap         = ldp_twl4030_keymap,
+       .keymapsize     = ARRAY_SIZE(ldp_twl4030_keymap),
+       .rep            = 1,
+};
+
+static struct gpio_keys_button ldp_gpio_keys_buttons[] = {
+       [0] = {
+               .code                   = KEY_ENTER,
+               .gpio                   = 101,
+               .desc                   = "enter sw",
+               .active_low             = 1,
+               .debounce_interval      = 30,
+       },
+       [1] = {
+               .code                   = KEY_F1,
+               .gpio                   = 102,
+               .desc                   = "func 1",
+               .active_low             = 1,
+               .debounce_interval      = 30,
+       },
+       [2] = {
+               .code                   = KEY_F2,
+               .gpio                   = 103,
+               .desc                   = "func 2",
+               .active_low             = 1,
+               .debounce_interval      = 30,
+       },
+       [3] = {
+               .code                   = KEY_F3,
+               .gpio                   = 104,
+               .desc                   = "func 3",
+               .active_low             = 1,
+               .debounce_interval      = 30,
+       },
+       [4] = {
+               .code                   = KEY_F4,
+               .gpio                   = 105,
+               .desc                   = "func 4",
+               .active_low             = 1,
+               .debounce_interval      = 30,
+       },
+       [5] = {
+               .code                   = KEY_LEFT,
+               .gpio                   = 106,
+               .desc                   = "left sw",
+               .active_low             = 1,
+               .debounce_interval      = 30,
+       },
+       [6] = {
+               .code                   = KEY_RIGHT,
+               .gpio                   = 107,
+               .desc                   = "right sw",
+               .active_low             = 1,
+               .debounce_interval      = 30,
+       },
+       [7] = {
+               .code                   = KEY_UP,
+               .gpio                   = 108,
+               .desc                   = "up sw",
+               .active_low             = 1,
+               .debounce_interval      = 30,
+       },
+       [8] = {
+               .code                   = KEY_DOWN,
+               .gpio                   = 109,
+               .desc                   = "down sw",
+               .active_low             = 1,
+               .debounce_interval      = 30,
+       },
+};
+
+static struct gpio_keys_platform_data ldp_gpio_keys = {
+       .buttons                = ldp_gpio_keys_buttons,
+       .nbuttons               = ARRAY_SIZE(ldp_gpio_keys_buttons),
+       .rep                    = 1,
+};
+
+static struct platform_device ldp_gpio_keys_device = {
+       .name           = "gpio-keys",
+       .id             = -1,
+       .dev            = {
+               .platform_data  = &ldp_gpio_keys,
+       },
+};
+
+static int ts_gpio;
+
+static int __init msecure_init(void)
+{
+       int ret = 0;
+
+#ifdef CONFIG_RTC_DRV_TWL4030
+       /* 3430ES2.0 doesn't have msecure/gpio-22 line connected to T2 */
+       if (omap_type() == OMAP2_DEVICE_TYPE_GP &&
+                       omap_rev() < OMAP3430_REV_ES2_0) {
+               void __iomem *msecure_pad_config_reg =
+                       omap_ctrl_base_get() + 0xA3C;
+               int mux_mask = 0x04;
+               u16 tmp;
+
+               ret = gpio_request(TWL4030_MSECURE_GPIO, "msecure");
+               if (ret < 0) {
+                       printk(KERN_ERR "msecure_init: can't"
+                               "reserve GPIO:%d !\n", TWL4030_MSECURE_GPIO);
+                       goto out;
+               }
+               /*
+                * TWL4030 will be in secure mode if msecure line from OMAP
+                * is low. Make msecure line high in order to change the
+                * TWL4030 RTC time and calender registers.
+                */
+
+               tmp = __raw_readw(msecure_pad_config_reg);
+               tmp &= 0xF8;    /* To enable mux mode 03/04 = GPIO_RTC */
+               tmp |= mux_mask;/* To enable mux mode 03/04 = GPIO_RTC */
+               __raw_writew(tmp, msecure_pad_config_reg);
+
+               gpio_direction_output(TWL4030_MSECURE_GPIO, 1);
+       }
+out:
+#endif
+       return ret;
+}
+
+/**
+ * @brief ads7846_dev_init : Requests & sets GPIO line for pen-irq
+ *
+ * @return - void. If request gpio fails then Flag KERN_ERR.
+ */
+static void ads7846_dev_init(void)
+{
+       if (gpio_request(ts_gpio, "ads7846 irq") < 0) {
+               printk(KERN_ERR "can't get ads746 pen down GPIO\n");
+               return;
+       }
+
+       gpio_direction_input(ts_gpio);
+
+       omap_set_gpio_debounce(ts_gpio, 1);
+       omap_set_gpio_debounce_time(ts_gpio, 0xa);
+}
+
+static int ads7846_get_pendown_state(void)
+{
+       return !gpio_get_value(ts_gpio);
+}
+
+/*
+ * This enable(1)/disable(0) the voltage for TS: uses twl4030 calls
+ */
+static int ads7846_vaux_control(int vaux_cntrl)
+{
+       int ret = 0;
+
+#ifdef CONFIG_TWL4030_CORE
+       /* check for return value of ldo_use: if success it returns 0 */
+       if (vaux_cntrl == VAUX_ENABLE) {
+               if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+                       ENABLE_VAUX1_DEDICATED, TWL4030_VAUX1_DEDICATED))
+                       return -EIO;
+               if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+                       ENABLE_VAUX1_DEV_GRP, TWL4030_VAUX1_DEV_GRP))
+                       return -EIO;
+       } else if (vaux_cntrl == VAUX_DISABLE) {
+               if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+                       0x00, TWL4030_VAUX1_DEDICATED))
+                       return -EIO;
+               if (ret != twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+                       0x00, TWL4030_VAUX1_DEV_GRP))
+                       return -EIO;
+       }
+#else
+       ret = -EIO;
+#endif
+       return ret;
+}
+
+static struct ads7846_platform_data tsc2046_config __initdata = {
+       .get_pendown_state      = ads7846_get_pendown_state,
+       .keep_vref_on           = 1,
+       .vaux_control           = ads7846_vaux_control,
+};
+
+
+static struct omap2_mcspi_device_config tsc2046_mcspi_config = {
+       .turbo_mode     = 0,
+       .single_channel = 1,    /* 0: slave, 1: master */
+};
+
+static struct spi_board_info ldp_spi_board_info[] __initdata = {
+       [0] = {
+               /*
+                * TSC2046 operates at a max freqency of 2MHz, so
+                * operate slightly below at 1.5MHz
+                */
+               .modalias               = "ads7846",
+               .bus_num                = 1,
+               .chip_select            = 0,
+               .max_speed_hz           = 1500000,
+               .controller_data        = &tsc2046_mcspi_config,
+               .irq                    = 0,
+               .platform_data          = &tsc2046_config,
+       },
+};
+
+static struct platform_device ldp_lcd_device = {
+       .name           = "ldp_lcd",
+       .id             = -1,
+};
+
 static struct platform_device *ldp_devices[] __initdata = {
        &ldp_smsc911x_device,
 static struct platform_device *ldp_devices[] __initdata = {
        &ldp_smsc911x_device,
+       &ldp_lcd_device,
+       &ldp_gpio_keys_device,
 };
 
 static inline void __init ldp_init_smsc911x(void)
 };
 
 static inline void __init ldp_init_smsc911x(void)
@@ -110,6 +351,7 @@ static inline void __init ldp_init_smsc911x(void)
        gpio_direction_input(eth_gpio);
 }
 
        gpio_direction_input(eth_gpio);
 }
 
+
 static void __init omap_ldp_init_irq(void)
 {
        omap2_init_common_hw(NULL);
 static void __init omap_ldp_init_irq(void)
 {
        omap2_init_common_hw(NULL);
@@ -122,8 +364,114 @@ static struct omap_uart_config ldp_uart_config __initdata = {
        .enabled_uarts  = ((1 << 0) | (1 << 1) | (1 << 2)),
 };
 
        .enabled_uarts  = ((1 << 0) | (1 << 1) | (1 << 2)),
 };
 
+static struct omap_lcd_config ldp_lcd_config __initdata = {
+       .ctrl_name      = "internal",
+};
+
 static struct omap_board_config_kernel ldp_config[] __initdata = {
        { OMAP_TAG_UART,        &ldp_uart_config },
 static struct omap_board_config_kernel ldp_config[] __initdata = {
        { OMAP_TAG_UART,        &ldp_uart_config },
+       { OMAP_TAG_LCD,         &ldp_lcd_config },
+};
+
+static int ldp_batt_table[] = {
+/* 0 C*/
+30800, 29500, 28300, 27100,
+26000, 24900, 23900, 22900, 22000, 21100, 20300, 19400, 18700, 17900,
+17200, 16500, 15900, 15300, 14700, 14100, 13600, 13100, 12600, 12100,
+11600, 11200, 10800, 10400, 10000, 9630,   9280,   8950,   8620,   8310,
+8020,   7730,   7460,   7200,   6950,   6710,   6470,   6250,   6040,   5830,
+5640,   5450,   5260,   5090,   4920,   4760,   4600,   4450,   4310,   4170,
+4040,   3910,   3790,   3670,   3550
+};
+
+static struct twl4030_ins __initdata sleep_on_seq[] = {
+/*
+ * Turn off VDD1 and VDD2.
+ */
+       {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_OFF), 4},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_OFF), 2},
+#ifdef CONFIG_DISABLE_HFCLK
+/*
+ * And also turn off the OMAP3 PLLs and the sysclk output.
+ */
+       {MSG_SINGULAR(DEV_GRP_P1, 0x7, RES_STATE_OFF), 3},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x19, RES_STATE_OFF), 3},
+#endif
+};
+
+static struct twl4030_script sleep_on_script __initdata = {
+       .script = sleep_on_seq,
+       .size   = ARRAY_SIZE(sleep_on_seq),
+       .flags  = TRITON_SLEEP_SCRIPT,
+};
+
+static struct twl4030_ins wakeup_seq[] __initdata = {
+#ifndef CONFIG_DISABLE_HFCLK
+/*
+ * Wakeup VDD1 and VDD2.
+ */
+       {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_ACTIVE), 4},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_ACTIVE), 2},
+#else
+/*
+ * Reenable the OMAP3 PLLs.
+ * Wakeup VDD1 and VDD2.
+ * Reenable sysclk output.
+ */
+       {MSG_SINGULAR(DEV_GRP_P1, 0x7, RES_STATE_ACTIVE), 0x30},
+       {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_ACTIVE), 0x30},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_ACTIVE), 0x37},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x19, RES_STATE_ACTIVE), 3},
+#endif /* #ifndef CONFIG_DISABLE_HFCLK */
+};
+
+static struct twl4030_script wakeup_script __initdata = {
+       .script = wakeup_seq,
+       .size   = ARRAY_SIZE(wakeup_seq),
+       .flags  = TRITON_WAKEUP12_SCRIPT | TRITON_WAKEUP3_SCRIPT,
+};
+
+static struct twl4030_ins wrst_seq[] __initdata = {
+/*
+ * Reset twl4030.
+ * Reset VDD1 regulator.
+ * Reset VDD2 regulator.
+ * Reset VPLL1 regulator.
+ * Enable sysclk output.
+ * Reenable twl4030.
+ */
+       {MSG_SINGULAR(DEV_GRP_NULL, 0x1b, RES_STATE_OFF), 2},
+       {MSG_SINGULAR(DEV_GRP_P1, 0xf, RES_STATE_WRST), 15},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x10, RES_STATE_WRST), 15},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x7, RES_STATE_WRST), 0x60},
+       {MSG_SINGULAR(DEV_GRP_P1, 0x19, RES_STATE_ACTIVE), 2},
+       {MSG_SINGULAR(DEV_GRP_NULL, 0x1b, RES_STATE_ACTIVE), 2},
+};
+
+static struct twl4030_script wrst_script __initdata = {
+       .script = wrst_seq,
+       .size   = ARRAY_SIZE(wakeup_seq),
+       .flags  = TRITON_WRST_SCRIPT,
+};
+
+static struct twl4030_script *twl4030_scripts[] __initdata = {
+       &sleep_on_script,
+       &wakeup_script,
+       &wrst_script,
+};
+
+static struct twl4030_power_data sdp3430_t2scripts_data __initdata = {
+       .scripts        = twl4030_scripts,
+       .size           = ARRAY_SIZE(twl4030_scripts),
+};
+
+static struct twl4030_bci_platform_data ldp_bci_data = {
+       .battery_tmp_tbl        = ldp_batt_table,
+       .tblsize                = ARRAY_SIZE(ldp_batt_table),
+};
+
+static struct twl4030_usb_data ldp_usb_data = {
+       .usb_mode       = T2_USB_MODE_ULPI,
 };
 
 static struct twl4030_gpio_platform_data ldp_gpio_data = {
 };
 
 static struct twl4030_gpio_platform_data ldp_gpio_data = {
@@ -132,12 +480,21 @@ static struct twl4030_gpio_platform_data ldp_gpio_data = {
        .irq_end        = TWL4030_GPIO_IRQ_END,
 };
 
        .irq_end        = TWL4030_GPIO_IRQ_END,
 };
 
+static struct twl4030_madc_platform_data ldp_madc_data = {
+       .irq_line       = 1,
+};
+
 static struct twl4030_platform_data ldp_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
 
        /* platform_data for children goes here */
 static struct twl4030_platform_data ldp_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
 
        /* platform_data for children goes here */
+       .bci            = &ldp_bci_data,
+       .madc           = &ldp_madc_data,
+       .usb            = &ldp_usb_data,
+       .power          = &sdp3430_t2scripts_data,
        .gpio           = &ldp_gpio_data,
        .gpio           = &ldp_gpio_data,
+       .keypad         = &ldp_kp_twl4030_data,
 };
 
 static struct i2c_board_info __initdata ldp_i2c_boardinfo[] = {
 };
 
 static struct i2c_board_info __initdata ldp_i2c_boardinfo[] = {
@@ -174,9 +531,15 @@ static void __init omap_ldp_init(void)
        platform_add_devices(ldp_devices, ARRAY_SIZE(ldp_devices));
        omap_board_config = ldp_config;
        omap_board_config_size = ARRAY_SIZE(ldp_config);
        platform_add_devices(ldp_devices, ARRAY_SIZE(ldp_devices));
        omap_board_config = ldp_config;
        omap_board_config_size = ARRAY_SIZE(ldp_config);
+       ts_gpio = 54;
+       ldp_spi_board_info[0].irq = gpio_to_irq(ts_gpio);
+       spi_register_board_info(ldp_spi_board_info,
+                               ARRAY_SIZE(ldp_spi_board_info));
+       msecure_init();
+       ads7846_dev_init();
        omap_serial_init();
        omap_serial_init();
-       twl4030_mmc_init(mmc);
        usb_musb_init();
        usb_musb_init();
+       twl4030_mmc_init(mmc);
 }
 
 static void __init omap_ldp_map_io(void)
 }
 
 static void __init omap_ldp_map_io(void)
diff --git a/arch/arm/mach-omap2/board-n800-bt.c b/arch/arm/mach-omap2/board-n800-bt.c
new file mode 100644 (file)
index 0000000..da3a7bb
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Nokia N800 platform-specific data for Bluetooth
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <mach/board.h>
+#include <mach/board-nokia.h>
+
+static struct platform_device n800_bt_device = {
+       .name           = "hci_h4p",
+       .id             = -1,
+       .num_resources  = 0,
+};
+
+void __init n800_bt_init(void)
+{
+       const struct omap_bluetooth_config *bt_config;
+
+       bt_config = (void *) omap_get_config(OMAP_TAG_NOKIA_BT,
+                                            struct omap_bluetooth_config);
+       n800_bt_device.dev.platform_data = (void *) bt_config;
+       if (platform_device_register(&n800_bt_device) < 0)
+               BUG();
+}
+
diff --git a/arch/arm/mach-omap2/board-n800-camera.c b/arch/arm/mach-omap2/board-n800-camera.c
new file mode 100644 (file)
index 0000000..3959128
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * arch/arm/mach-omap2/board-n800-camera.c
+ *
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Contact: Sakari Ailus <sakari.ailus@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/videodev2.h>
+#include <linux/gpio.h>
+#include <linux/i2c/menelaus.h>
+
+#include <media/v4l2-int-device.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/board.h>
+
+#include <../drivers/cbus/retu.h>
+#include <../drivers/media/video/tcm825x.h>
+
+#include "board-n800.h"
+
+#if defined (CONFIG_VIDEO_TCM825X) || defined (CONFIG_VIDEO_TCM825X_MODULE)
+
+#define OMAP24XX_CAMERA_JAM_HACK
+
+#ifdef OMAP24XX_CAMERA_JAM_HACK
+/*
+ * We don't need to check every pixel to assume that the frame is
+ * corrupt and the sensor is jammed. CHECK_X and CHECK_Y are the
+ * number of u32s to check per line / row, plus there are two lines in
+ * the bottom of the frame.
+ */
+#define CHECK_X 8
+#define CHECK_Y 6
+/*
+ * Start checking after this many frames since resetting the sensor.
+ * Sometimes the first frame(s) is(/are) black which could trigger
+ * unwanted reset(s).
+ */
+#define JAM_CHECK_AFTER 3
+/*
+ * If the sensor is quickly brought into bright conditions from dark,
+ * it may temporarily be saturated, leaving out the normal background
+ * noise. This many saturated frames may go through before the sensor
+ * is considered jammed.
+ */
+#define SATURATED_MAX 30
+#endif
+
+#define N800_CAM_SENSOR_RESET_GPIO     53
+
+static int sensor_okay;
+#ifdef OMAP24XX_CAMERA_JAM_HACK
+static int frames_after_reset;
+static int saturated_count;
+#endif
+
+const static struct tcm825x_reg tcm825x_regs_n800[] = {
+       /* initial settings for 2.5 V */
+       {0x00, 0x03}, {0x03, 0x29}, {0xaa, 0x2a}, {0xc0, 0x2b},
+       {0x10, 0x2c}, {0x4c, 0x2d}, {0x9c, 0x3f},
+
+       /* main settings */
+       {0x00, 0x00}, {0x30, 0x01}, {0x0e, 0x02}, /* initial */
+       {0x0f, 0x04}, {0x02, 0x05}, {0x0d, 0x06}, {0xc0, 0x07},
+       {0x38, 0x08}, {0x50, 0x09}, {0x80, 0x0a}, {0x40, 0x0b},
+       {0x40, 0x0c}, {0x00, 0x0d}, {0x04, 0x0e}, {0x04, 0x0f},
+       {0x22, 0x10}, {0x96, 0x11}, {0xf0, 0x12}, {0x08, 0x13},
+       {0x08, 0x14}, {0x30, 0x15}, {0x30, 0x16}, {0x01, 0x17},
+       {0x40, 0x18}, {0x87, 0x19}, {0x2b, 0x1a}, {0x84, 0x1b},
+       {0x52, 0x1c}, {0x44, 0x1d}, {0x68, 0x1e}, {0x00, 0x1f},
+       {0x00, 0x20}, {0x01, 0x21}, {0x27, 0x22}, {0x40, 0x23},
+       {0x27, 0x24}, {0x5f, 0x25}, {0x00, 0x26}, {0x16, 0x27},
+       {0x23, 0x28}, /* initial */ /* initial */ /* initial */
+       /* initial */ /* initial */ {0x00, 0x2e}, {0x00, 0x2f},
+       {0x00, 0x30}, {0x00, 0x31}, {0x00, 0x32}, {0x00, 0x33},
+       {0x00, 0x34}, {0x00, 0x35}, {0x00, 0x36}, {0x00, 0x37},
+       {0x00, 0x38}, {0x8c, 0x39}, {0xc8, 0x3A}, {0x80, 0x3b},
+       {0x00, 0x3c}, {0x17, 0x3d}, {0x85, 0x3e}, /* initial */
+       {0xa0, 0x40}, {0x00, 0x41}, {0x00, 0x42}, {0x00, 0x43},
+       {0x08, 0x44}, {0x12, 0x45}, {0x00, 0x46}, {0x20, 0x47},
+       {0x30, 0x48}, {0x18, 0x49}, {0x20, 0x4a}, {0x4d, 0x4b},
+       {0x0c, 0x4c}, {0xe0, 0x4d}, {0x20, 0x4e}, {0x89, 0x4f},
+       {0x21, 0x50}, {0x80, 0x51}, {0x02, 0x52}, {0x00, 0x53},
+       {0x30, 0x54}, {0x90, 0x55}, {0x40, 0x56}, {0x06, 0x57},
+       {0x0f, 0x58}, {0x23, 0x59}, {0x08, 0x5A}, {0x04, 0x5b},
+       {0x08, 0x5c}, {0x08, 0x5d}, {0x08, 0x5e}, {0x08, 0x5f},
+       {TCM825X_VAL_TERM, TCM825X_REG_TERM}
+};
+
+const static struct tcm825x_reg tcm825x_regs_n810[] = {
+       /* initial settings for 2.5 V */
+       {0x00, 0x03}, {0x03, 0x29}, {0xaa, 0x2a}, {0xc0, 0x2b},
+       {0x10, 0x2c}, {0x4c, 0x2d}, {0x9c, 0x3f},
+
+       /* main settings */
+       {0x00, 0x00}, {0x30, 0x01}, {0x0e, 0x02}, /* initial */
+       {0xcf, 0x04}, {0x02, 0x05}, {0x0d, 0x06}, {0xc0, 0x07},
+       {0x38, 0x08}, {0x50, 0x09}, {0x80, 0x0a}, {0x40, 0x0b},
+       {0x40, 0x0c}, {0x00, 0x0d}, {0x04, 0x0e}, {0x04, 0x0f},
+       {0x22, 0x10}, {0x96, 0x11}, {0xf0, 0x12}, {0x08, 0x13},
+       {0x08, 0x14}, {0x30, 0x15}, {0x30, 0x16}, {0x01, 0x17},
+       {0x40, 0x18}, {0x87, 0x19}, {0x2b, 0x1a}, {0x84, 0x1b},
+       {0x52, 0x1c}, {0x44, 0x1d}, {0x68, 0x1e}, {0x00, 0x1f},
+       {0x00, 0x20}, {0x01, 0x21}, {0x27, 0x22}, {0x40, 0x23},
+       {0x27, 0x24}, {0x5f, 0x25}, {0x00, 0x26}, {0x16, 0x27},
+       {0x23, 0x28}, /* initial */ /* initial */ /* initial */
+       /* initial */ /* initial */ {0x00, 0x2e}, {0x00, 0x2f},
+       {0x00, 0x30}, {0x00, 0x31}, {0x00, 0x32}, {0x00, 0x33},
+       {0x00, 0x34}, {0x00, 0x35}, {0x00, 0x36}, {0x00, 0x37},
+       {0x00, 0x38}, {0x8c, 0x39}, {0xc8, 0x3A}, {0x80, 0x3b},
+       {0x00, 0x3c}, {0x17, 0x3d}, {0x85, 0x3e}, /* initial */
+       {0xa0, 0x40}, {0x00, 0x41}, {0x00, 0x42}, {0x00, 0x43},
+       {0x08, 0x44}, {0x12, 0x45}, {0x00, 0x46}, {0x20, 0x47},
+       {0x30, 0x48}, {0x18, 0x49}, {0x20, 0x4a}, {0x4d, 0x4b},
+       {0x0c, 0x4c}, {0xe0, 0x4d}, {0x20, 0x4e}, {0x89, 0x4f},
+       {0x21, 0x50}, {0x80, 0x51}, {0x02, 0x52}, {0x00, 0x53},
+       {0x30, 0x54}, {0x90, 0x55}, {0x40, 0x56}, {0x06, 0x57},
+       {0x0f, 0x58}, {0x23, 0x59}, {0x08, 0x5A}, {0x04, 0x5b},
+       {0x08, 0x5c}, {0x08, 0x5d}, {0x08, 0x5e}, {0x08, 0x5f},
+       {TCM825X_VAL_TERM, TCM825X_REG_TERM}
+};
+
+static int tcm825x_is_okay(void)
+{
+       return sensor_okay;
+}
+
+/*
+ * VSIM1       --> CAM_IOVDD   --> IOVDD (1.8 V)
+ */
+static int tcm825x_power_on(void)
+{
+       int ret;
+
+       /* Set VMEM to 1.5V and VIO to 2.5V */
+       ret = menelaus_set_vmem(1500);
+       if (ret < 0) {
+               /* Try once more, it seems the sensor power up causes
+                * some problems on the I2C bus. */
+               ret = menelaus_set_vmem(1500);
+               if (ret < 0)
+                       return ret;
+       }
+       msleep(1);
+
+       ret = menelaus_set_vio(2500);
+       if (ret < 0)
+               return ret;
+
+       /* Set VSim1 on */
+       retu_write_reg(RETU_REG_CTRL_SET, 0x0080);
+       msleep(1);
+
+       gpio_set_value(N800_CAM_SENSOR_RESET_GPIO, 1);
+       msleep(1);
+
+       saturated_count = 0;
+       frames_after_reset = 0;
+
+       return 0;
+}
+
+static int tcm825x_power_off(void)
+{
+       int ret;
+
+       gpio_set_value(N800_CAM_SENSOR_RESET_GPIO, 0);
+       msleep(1);
+
+       /* Set VSim1 off */
+       retu_write_reg(RETU_REG_CTRL_CLR, 0x0080);
+       msleep(1);
+
+       /* Set VIO_MODE to off */
+       ret = menelaus_set_vio(0);
+       if (ret < 0)
+               return ret;
+       msleep(1);
+
+       /* Set VMEM_MODE to off */
+       ret = menelaus_set_vmem(0);
+       if (ret < 0)
+               return ret;
+       msleep(1);
+
+       return 0;
+}
+
+static int tcm825x_power_set(int power)
+{
+       BUG_ON(!sensor_okay);
+
+       if (power)
+               return tcm825x_power_on();
+       else
+               return tcm825x_power_off();
+}
+
+static const struct tcm825x_reg *tcm825x_default_regs(void)
+{
+       if (machine_is_nokia_n810())
+               return tcm825x_regs_n810;
+
+       return tcm825x_regs_n800;
+}
+
+#ifdef OMAP24XX_CAMERA_JAM_HACK
+/*
+ * Check for jammed sensor, in which case all horizontal lines are
+ * equal. Handle also case where sensor could be saturated awhile in
+ * case of rapid increase of brightness.
+ */
+static int tcm825x_needs_reset(struct v4l2_int_device *s, void *buf,
+                              struct v4l2_pix_format *pix)
+{
+       int i, j;
+       uint32_t xor, xor2;
+       uint32_t offset;
+       uint32_t dx_offset;
+       uint32_t saturated_pattern;
+       int is_saturated = 1;
+
+       switch (pix->pixelformat) {
+       default:
+       case V4L2_PIX_FMT_RGB565:
+               saturated_pattern = 0xffffffff; /* guess */
+               break;
+       case V4L2_PIX_FMT_UYVY:
+               saturated_pattern = 0xe080e080;
+               break;
+       }
+
+       /* This won't work for height under 2 at all. */
+       if (pix->height < 2)
+               return 0;
+       /* Check that there is enough image data. */
+       if (pix->width * TCM825X_BYTES_PER_PIXEL < sizeof(uint32_t))
+               return 0;
+       /*
+        * Don't check for jamming immediately. Sometimes frames
+        * immediately after reset are black.
+        */
+       if (frames_after_reset < JAM_CHECK_AFTER) {
+               frames_after_reset++;
+               return 0;
+       }
+
+       dx_offset = ((pix->width - sizeof(uint32_t) / TCM825X_BYTES_PER_PIXEL)
+                    * TCM825X_BYTES_PER_PIXEL) / (CHECK_X - 1);
+       dx_offset = dx_offset - dx_offset % TCM825X_BYTES_PER_PIXEL;
+       /*
+        * Check two lines in the bottom first. They're unlikely to be
+        * saturated and quick to check.
+        */
+       offset = (pix->height - 2) * pix->bytesperline;
+       xor = xor2 = 0;
+       for (j = 0; j < CHECK_X; j++) {
+               uint32_t *val = buf + offset;
+               uint32_t *val2 = buf + offset + pix->bytesperline;
+               xor ^= *val;
+               if (*val != saturated_pattern)
+                       is_saturated = 0;
+               xor2 ^= *val2;
+               if (xor2 != xor) {
+                       saturated_count = 0;
+                       return 0;
+               }
+               offset += dx_offset;
+       }
+       /* Check the rest of the picture. */
+       offset = 0;
+       for (i = 0; i < CHECK_Y; i++) {
+               uint32_t offset2 = offset;
+               xor2 = 0;
+               for (j = 0; j < CHECK_X; j++) {
+                       uint32_t *val = buf + offset2;
+                       xor2 ^= *val;
+                       offset2 += dx_offset;
+               }
+               if (xor2 != xor) {
+                       saturated_count = 0;
+                       return 0;
+               }
+               offset += pix->bytesperline * ((pix->height - 2) / CHECK_Y);
+       }
+
+       if (is_saturated && saturated_count++ < SATURATED_MAX)
+               return 0;
+
+       return -EIO;
+}
+#else
+static int tcm825x_needs_reset(struct v4l2_int_device *s, void *buf,
+                              struct v4l2_pix_format *pix)
+{
+       return 0;
+}
+#endif
+
+static const struct v4l2_ifparm ifparm = {
+       .if_type = V4L2_IF_TYPE_BT656,
+       .u = {
+               .bt656 = {
+                        .frame_start_on_rising_vs = 1,
+                        .latch_clk_inv = 1,
+                        .mode = V4L2_IF_TYPE_BT656_MODE_NOBT_8BIT,
+                        .clock_min = TCM825X_XCLK_MIN,
+                        .clock_max = TCM825X_XCLK_MAX,
+                },
+       },
+};
+
+static int tcm825x_ifparm(struct v4l2_ifparm *p)
+{
+       *p = ifparm;
+
+       return 0;
+}
+
+static int tcm825x_is_upside_down(void)
+{
+       return machine_is_nokia_n810();
+}
+
+const struct tcm825x_platform_data n800_tcm825x_platform_data = {
+       .is_okay        = tcm825x_is_okay,
+       .power_set      = tcm825x_power_set,
+       .default_regs   = tcm825x_default_regs,
+       .needs_reset    = tcm825x_needs_reset,
+       .ifparm         = tcm825x_ifparm,
+       .is_upside_down = tcm825x_is_upside_down,
+};
+
+void __init n800_cam_init(void)
+{
+       int r;
+
+       r = gpio_request(N800_CAM_SENSOR_RESET_GPIO, "TCM825x reset");
+       if (r < 0) {
+               printk(KERN_WARNING "%s: failed to request gpio\n",
+                       __func__);
+               return;
+       }
+
+       gpio_direction_output(N800_CAM_SENSOR_RESET_GPIO, 0);
+
+       sensor_okay = 1;
+}
+
+#else
+void __init n800_cam_init(void)
+{
+}
+
+#endif
diff --git a/arch/arm/mach-omap2/board-n800-dsp.c b/arch/arm/mach-omap2/board-n800-dsp.c
new file mode 100644 (file)
index 0000000..5f3f0d6
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * linux/arch/arm/mach-omap2/board-n800-dsp.c
+ *
+ * Copyright (C) 2006 Nokia Corporation.
+ *
+ * Contact: Hiroshi DOYU <Hiroshi.DOYU@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/list.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <mach/clock.h>
+#include <mach/board.h>
+#include <mach/dsp_common.h>
+
+#if    defined(CONFIG_OMAP_DSP)
+
+/*
+ * dsp peripheral device: AUDIO
+ */
+static struct dsp_kfunc_device n800_audio_device = {
+       .name    = "audio",
+       .type    = DSP_KFUNC_DEV_TYPE_AUDIO,
+       .enable  = n800_audio_enable,
+       .disable = n800_audio_disable,
+};
+
+/*
+ * dsp peripheral device: TIMER
+ */
+static int dsp_timer_probe(struct dsp_kfunc_device *kdev, int stage)
+{
+       char clockname[20];
+
+       strcpy(clockname, kdev->name);
+       strcat(clockname, "_fck");
+
+       kdev->fck = clk_get(NULL, clockname);
+       if (IS_ERR(kdev->fck)) {
+               printk(KERN_ERR "couldn't acquire %s\n", clockname);
+               return PTR_ERR(kdev->fck);
+       }
+       pr_debug("%s probed successfully\n", clockname);
+
+       strcpy(clockname, kdev->name);
+       strcat(clockname, "_ick");
+       kdev->ick = clk_get(NULL, clockname);
+       if (IS_ERR(kdev->ick)) {
+               printk(KERN_ERR "couldn't acquire %s\n", clockname);
+               goto fail;
+       }
+       pr_debug("%s probed successfully\n", clockname);
+
+       return 0;
+ fail:
+       clk_put(kdev->fck);
+
+       return PTR_ERR(kdev->ick);
+}
+
+static int dsp_timer_remove(struct dsp_kfunc_device *kdev, int stage)
+{
+       clk_put(kdev->ick);
+       clk_put(kdev->fck);
+       pr_debug("%s removed successfully\n", kdev->name);
+       return 0;
+}
+
+static int dsp_timer_enable(struct dsp_kfunc_device *kdev, int stage)
+{
+       pr_debug("%s enabled(%d)\n", kdev->name, stage);
+
+       spin_lock(&kdev->lock);
+
+       if (kdev->enabled)
+               goto out;
+       kdev->enabled = 1;
+
+       clk_enable(kdev->fck);
+       clk_enable(kdev->ick);
+ out:
+       spin_unlock(&kdev->lock);
+
+       return 0;
+}
+
+static int dsp_timer_disable(struct dsp_kfunc_device *kdev, int stage)
+{
+       pr_debug("%s disabled(%d)\n", kdev->name, stage);
+
+       spin_lock(&kdev->lock);
+
+       if (kdev->enabled == 0)
+               goto out;
+       kdev->enabled = 0;
+
+       clk_disable(kdev->ick);
+       clk_disable(kdev->fck);
+ out:
+       spin_unlock(&kdev->lock);
+
+       return 0;
+}
+
+static struct dsp_kfunc_device n800_timer_device = {
+       .name    = "gpt5",
+       .type    = DSP_KFUNC_DEV_TYPE_COMMON,
+       .probe   = dsp_timer_probe,
+       .remove  = dsp_timer_remove,
+       .enable  = dsp_timer_enable,
+       .disable = dsp_timer_disable,
+};
+
+static struct dsp_kfunc_device *n800_kfunc_dev[] = {
+       &n800_audio_device,
+       &n800_timer_device,
+};
+
+void __init n800_dsp_init(void)
+{
+       int i, ret;
+       struct dsp_kfunc_device **p = n800_kfunc_dev;
+
+       for (i = 0; i < ARRAY_SIZE(n800_kfunc_dev); i++) {
+               ret = dsp_kfunc_device_register(p[i]);
+               if (ret) {
+                       printk(KERN_ERR
+                              "KFUNC device registration failed: %s\n",
+                              p[i]->name);
+               }
+       }
+}
+
+#else
+void __init n800_dsp_init(void) { }
+#endif /* CONFIG_OMAP_DSP */
diff --git a/arch/arm/mach-omap2/board-n800-flash.c b/arch/arm/mach-omap2/board-n800-flash.c
new file mode 100644 (file)
index 0000000..52aaf76
--- /dev/null
@@ -0,0 +1,349 @@
+/*
+ * linux/arch/arm/mach-omap2/board-n800-flash.c
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Juha Yrjola
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <asm/mach/flash.h>
+#include <linux/mtd/onenand_regs.h>
+
+#include <asm/io.h>
+#include <mach/onenand.h>
+#include <mach/board.h>
+#include <mach/gpmc.h>
+
+struct mtd_partition n800_partitions[ONENAND_MAX_PARTITIONS];
+
+int n800_onenand_setup(void __iomem *, int freq);
+
+static struct omap_onenand_platform_data n800_onenand_data = {
+       .cs = 0,
+       .parts = n800_partitions,
+       .nr_parts = 0, /* filled later */
+       .onenand_setup = n800_onenand_setup,
+};
+
+static struct platform_device n800_onenand_device = {
+       .name           = "omap2-onenand",
+       .id             = -1,
+       .dev = {
+               .platform_data = &n800_onenand_data,
+       },
+};
+
+static int omap2_onenand_set_async_mode(int cs, void __iomem *onenand_base)
+{
+       struct gpmc_timings t;
+
+       const int t_cer = 15;
+       const int t_avdp = 12;
+       const int t_aavdh = 7;
+       const int t_ce = 76;
+       const int t_aa = 76;
+       const int t_oe = 20;
+       const int t_cez = 20; /* max of t_cez, t_oez */
+       const int t_ds = 30;
+       const int t_wpl = 40;
+       const int t_wph = 30;
+
+       memset(&t, 0, sizeof(t));
+       t.sync_clk = 0;
+       t.cs_on = 0;
+       t.adv_on = 0;
+
+       /* Read */
+       t.adv_rd_off = gpmc_round_ns_to_ticks(max_t(int, t_avdp, t_cer));
+       t.oe_on  = t.adv_rd_off + gpmc_round_ns_to_ticks(t_aavdh);
+       t.access = t.adv_on + gpmc_round_ns_to_ticks(t_aa);
+       t.access = max_t(int, t.access, t.cs_on + gpmc_round_ns_to_ticks(t_ce));
+       t.access = max_t(int, t.access, t.oe_on + gpmc_round_ns_to_ticks(t_oe));
+       t.oe_off = t.access + gpmc_round_ns_to_ticks(1);
+       t.cs_rd_off = t.oe_off;
+       t.rd_cycle  = t.cs_rd_off + gpmc_round_ns_to_ticks(t_cez);
+
+       /* Write */
+       t.adv_wr_off = t.adv_rd_off;
+       t.we_on  = t.oe_on;
+       if (cpu_is_omap34xx()) {
+               t.wr_data_mux_bus = t.we_on;
+               t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds);
+       }
+       t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl);
+       t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph);
+       t.wr_cycle  = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez);
+
+       /* Configure GPMC for asynchronous read */
+       gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1,
+                         GPMC_CONFIG1_DEVICESIZE_16 |
+                         GPMC_CONFIG1_MUXADDDATA);
+
+       return gpmc_cs_set_timings(cs, &t);
+}
+
+static unsigned short omap2_onenand_readw(void __iomem *addr)
+{
+       return readw(addr);
+}
+
+static void omap2_onenand_writew(unsigned short value, void __iomem *addr)
+{
+       writew(value, addr);
+}
+
+static void set_onenand_cfg(void __iomem *onenand_base, int latency,
+                           int sync_write, int hf)
+{
+       u32 reg;
+
+       reg = omap2_onenand_readw(onenand_base + ONENAND_REG_SYS_CFG1);
+       reg &= ~((0x7 << ONENAND_SYS_CFG1_BRL_SHIFT) | (0x7 << 9));
+       reg |=  (latency << ONENAND_SYS_CFG1_BRL_SHIFT) |
+               ONENAND_SYS_CFG1_SYNC_READ |
+               ONENAND_SYS_CFG1_BL_16;
+       if (sync_write)
+               reg |= ONENAND_SYS_CFG1_SYNC_WRITE;
+       else
+               reg &= ~ONENAND_SYS_CFG1_SYNC_WRITE;
+       if (hf)
+               reg |= ONENAND_SYS_CFG1_HF;
+       else
+               reg &= ~ONENAND_SYS_CFG1_HF;
+       omap2_onenand_writew(reg, onenand_base + ONENAND_REG_SYS_CFG1);
+}
+
+static int omap2_onenand_set_sync_mode(int cs, void __iomem *onenand_base,
+                                      int freq)
+{
+       struct gpmc_timings t;
+       const int t_cer  = 15;
+       const int t_avdp = 12;
+       const int t_cez  = 20; /* max of t_cez, t_oez */
+       const int t_ds   = 30;
+       const int t_wpl  = 40;
+       const int t_wph  = 30;
+       int min_gpmc_clk_period, t_ces, t_avds, t_avdh, t_ach, t_aavdh, t_rdyo;
+       int tick_ns, div, fclk_offset_ns, fclk_offset, gpmc_clk_ns, latency;
+       int err, ticks_cez, sync_write = 0, first_time = 0, hf = 0;
+       u32 reg;
+
+       if (!freq) {
+               /* Very first call freq is not known */
+               err = omap2_onenand_set_async_mode(cs, onenand_base);
+               if (err)
+                       return err;
+               reg = omap2_onenand_readw(onenand_base +
+                                         ONENAND_REG_VERSION_ID);
+               switch ((reg >> 4) & 0xf) {
+               case 0:
+                       freq = 40;
+                       break;
+               case 1:
+                       freq = 54;
+                       break;
+               case 2:
+                       freq = 66;
+                       break;
+               case 3:
+                       freq = 83;
+                       break;
+               case 4:
+                       freq = 104;
+                       break;
+               default:
+                       freq = 54;
+                       break;
+               }
+               first_time = 1;
+       }
+
+       switch (freq) {
+       case 83:
+               min_gpmc_clk_period = 12; /* 83 MHz */
+               t_ces   = 5;
+               t_avds  = 4;
+               t_avdh  = 2;
+               t_ach   = 6;
+               t_aavdh = 6;
+               t_rdyo  = 9;
+               if (cpu_is_omap34xx())
+                       sync_write = 1;
+               break;
+       case 66:
+               min_gpmc_clk_period = 15; /* 66 MHz */
+               t_ces   = 6;
+               t_avds  = 5;
+               t_avdh  = 2;
+               t_ach   = 6;
+               t_aavdh = 6;
+               t_rdyo  = 11;
+               if (cpu_is_omap34xx())
+                       sync_write = 1;
+               break;
+       default:
+               min_gpmc_clk_period = 18; /* 54 MHz */
+               t_ces   = 7;
+               t_avds  = 7;
+               t_avdh  = 7;
+               t_ach   = 9;
+               t_aavdh = 7;
+               t_rdyo  = 15;
+               break;
+       }
+
+       tick_ns = gpmc_ticks_to_ns(1);
+       div = gpmc_cs_calc_divider(cs, min_gpmc_clk_period);
+       gpmc_clk_ns = gpmc_ticks_to_ns(div);
+       if (gpmc_clk_ns < 15) /* >66Mhz */
+               hf = 1;
+       if (hf)
+               latency = 6;
+       else if (gpmc_clk_ns >= 25) /* 40 MHz*/
+               latency = 3;
+       else
+               latency = 4;
+
+       if (first_time)
+               set_onenand_cfg(onenand_base, latency, sync_write, hf);
+
+       if (div == 1) {
+               reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2);
+               reg |= (1 << 7);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, reg);
+               reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG3);
+               reg |= (1 << 7);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, reg);
+               reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG4);
+               reg |= (1 << 7);
+               reg |= (1 << 23);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, reg);
+       } else {
+               reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG2);
+               reg &= ~(1 << 7);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG2, reg);
+               reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG3);
+               reg &= ~(1 << 7);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG3, reg);
+               reg = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG4);
+               reg &= ~(1 << 7);
+               reg &= ~(1 << 23);
+               gpmc_cs_write_reg(cs, GPMC_CS_CONFIG4, reg);
+       }
+
+       /* Set synchronous read timings */
+       memset(&t, 0, sizeof(t));
+       t.sync_clk = min_gpmc_clk_period;
+       t.cs_on = 0;
+       t.adv_on = 0;
+       fclk_offset_ns = gpmc_round_ns_to_ticks(max_t(int, t_ces, t_avds));
+       fclk_offset = gpmc_ns_to_ticks(fclk_offset_ns);
+       t.page_burst_access = gpmc_clk_ns;
+
+       /* Read */
+       t.adv_rd_off = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_avdh));
+       t.oe_on = gpmc_ticks_to_ns(fclk_offset + gpmc_ns_to_ticks(t_ach));
+       t.access = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div);
+       t.oe_off = t.access + gpmc_round_ns_to_ticks(1);
+       t.cs_rd_off = t.oe_off;
+       ticks_cez = ((gpmc_ns_to_ticks(t_cez) + div - 1) / div) * div;
+       t.rd_cycle = gpmc_ticks_to_ns(fclk_offset + (latency + 1) * div +
+                    ticks_cez);
+
+       /* Write */
+       if (sync_write) {
+               t.adv_wr_off = t.adv_rd_off;
+               t.we_on  = 0;
+               t.we_off = t.cs_rd_off;
+               t.cs_wr_off = t.cs_rd_off;
+               t.wr_cycle  = t.rd_cycle;
+               if (cpu_is_omap34xx()) {
+                       t.wr_data_mux_bus = gpmc_ticks_to_ns(fclk_offset +
+                                       gpmc_ns_to_ticks(min_gpmc_clk_period +
+                                       t_rdyo));
+                       t.wr_access = t.access;
+               }
+       } else {
+               t.adv_wr_off = gpmc_round_ns_to_ticks(max_t(int, t_avdp, t_cer));
+               t.we_on  = t.adv_wr_off + gpmc_round_ns_to_ticks(t_aavdh);
+               t.we_off = t.we_on + gpmc_round_ns_to_ticks(t_wpl);
+               t.cs_wr_off = t.we_off + gpmc_round_ns_to_ticks(t_wph);
+               t.wr_cycle  = t.cs_wr_off + gpmc_round_ns_to_ticks(t_cez);
+               if (cpu_is_omap34xx()) {
+                       t.wr_data_mux_bus = t.we_on;
+                       t.wr_access = t.we_on + gpmc_round_ns_to_ticks(t_ds);
+               }
+       }
+
+       /* Configure GPMC for synchronous read */
+       gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1,
+                         GPMC_CONFIG1_WRAPBURST_SUPP |
+                         GPMC_CONFIG1_READMULTIPLE_SUPP |
+                         GPMC_CONFIG1_READTYPE_SYNC |
+                         (sync_write ? GPMC_CONFIG1_WRITEMULTIPLE_SUPP : 0) |
+                         (sync_write ? GPMC_CONFIG1_WRITETYPE_SYNC : 0) |
+                         GPMC_CONFIG1_CLKACTIVATIONTIME(fclk_offset) |
+                         GPMC_CONFIG1_PAGE_LEN(2) |
+                         (cpu_is_omap34xx() ? 0 :
+                               (GPMC_CONFIG1_WAIT_READ_MON |
+                                GPMC_CONFIG1_WAIT_PIN_SEL(0))) |
+                         GPMC_CONFIG1_DEVICESIZE_16 |
+                         GPMC_CONFIG1_DEVICETYPE_NOR |
+                         GPMC_CONFIG1_MUXADDDATA);
+
+       err = gpmc_cs_set_timings(cs, &t);
+       if (err)
+               return err;
+
+       set_onenand_cfg(onenand_base, latency, sync_write, hf);
+
+       return 0;
+}
+
+int n800_onenand_setup(void __iomem *onenand_base, int freq)
+{
+       struct omap_onenand_platform_data *datap = &n800_onenand_data;
+       struct device *dev = &n800_onenand_device.dev;
+
+       /* Set sync timings in GPMC */
+       if (omap2_onenand_set_sync_mode(datap->cs, onenand_base, freq) < 0) {
+               dev_err(dev, "Unable to set synchronous mode\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+void __init n800_flash_init(void)
+{
+       const struct omap_partition_config *part;
+       int i = 0;
+
+       n800_onenand_data.gpio_irq = cpu_is_omap34xx() ? 65 : 26;
+
+       while ((part = omap_get_nr_config(OMAP_TAG_PARTITION,
+                               struct omap_partition_config, i)) != NULL) {
+               struct mtd_partition *mpart;
+
+               mpart = n800_partitions + i;
+               mpart->name = (char *) part->name;
+               mpart->size = part->size;
+               mpart->offset = part->offset;
+               mpart->mask_flags = part->mask_flags;
+               i++;
+               if (i == ARRAY_SIZE(n800_partitions)) {
+                       printk(KERN_ERR "Too many partitions supplied\n");
+                       return;
+               }
+       }
+       n800_onenand_data.nr_parts = i;
+       if (platform_device_register(&n800_onenand_device) < 0) {
+               printk(KERN_ERR "Unable to register OneNAND device\n");
+               return;
+       }
+}
diff --git a/arch/arm/mach-omap2/board-n800-mmc.c b/arch/arm/mach-omap2/board-n800-mmc.c
new file mode 100644 (file)
index 0000000..b2376a9
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * linux/arch/arm/mach-omap2/board-n800-mmc.c
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Juha Yrjola
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/i2c/menelaus.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/mmc.h>
+
+#if defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE)
+
+static const int slot_switch_gpio = 96;
+
+static const int n810_slot2_pw_vddf = 23;
+static const int n810_slot2_pw_vdd = 9;
+
+static int slot1_cover_open;
+static int slot2_cover_open;
+static struct device *mmc_device;
+
+/*
+ * VMMC   --> slot 1 (N800 & N810)
+ * VDCDC3_APE, VMCS2_APE --> slot 2 on N800
+ * GPIO96 --> Menelaus GPIO2
+ * GPIO23 --> controls slot2 VSD    (N810 only)
+ * GPIO9  --> controls slot2 VIO_SD (N810 only)
+ */
+
+static int n800_mmc_switch_slot(struct device *dev, int slot)
+{
+#ifdef CONFIG_MMC_DEBUG
+       dev_dbg(dev, "Choose slot %d\n", slot + 1);
+#endif
+       gpio_set_value(slot_switch_gpio, slot);
+       return 0;
+}
+
+static int n800_mmc_set_power_menelaus(struct device *dev, int slot,
+                                       int power_on, int vdd)
+{
+       int mV;
+
+#ifdef CONFIG_MMC_DEBUG
+       dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1,
+               power_on ? "on" : "off", vdd);
+#endif
+       if (slot == 0) {
+               if (!power_on)
+                       return menelaus_set_vmmc(0);
+               switch (1 << vdd) {
+               case MMC_VDD_33_34:
+               case MMC_VDD_32_33:
+               case MMC_VDD_31_32:
+                       mV = 3100;
+                       break;
+               case MMC_VDD_30_31:
+                       mV = 3000;
+                       break;
+               case MMC_VDD_28_29:
+                       mV = 2800;
+                       break;
+               case MMC_VDD_165_195:
+                       mV = 1850;
+                       break;
+               default:
+                       BUG();
+               }
+               return menelaus_set_vmmc(mV);
+       } else {
+               if (!power_on)
+                       return menelaus_set_vdcdc(3, 0);
+               switch (1 << vdd) {
+               case MMC_VDD_33_34:
+               case MMC_VDD_32_33:
+                       mV = 3300;
+                       break;
+               case MMC_VDD_30_31:
+               case MMC_VDD_29_30:
+                       mV = 3000;
+                       break;
+               case MMC_VDD_28_29:
+               case MMC_VDD_27_28:
+                       mV = 2800;
+                       break;
+               case MMC_VDD_24_25:
+               case MMC_VDD_23_24:
+                       mV = 2400;
+                       break;
+               case MMC_VDD_22_23:
+               case MMC_VDD_21_22:
+                       mV = 2200;
+                       break;
+               case MMC_VDD_20_21:
+                       mV = 2000;
+                       break;
+               case MMC_VDD_165_195:
+                       mV = 1800;
+                       break;
+               default:
+                       BUG();
+               }
+               return menelaus_set_vdcdc(3, mV);
+       }
+       return 0;
+}
+
+static void nokia_mmc_set_power_internal(struct device *dev,
+                                        int power_on)
+{
+       dev_dbg(dev, "Set internal slot power %s\n",
+               power_on ? "on" : "off");
+
+       if (power_on) {
+               gpio_set_value(n810_slot2_pw_vddf, 1);
+               udelay(30);
+               gpio_set_value(n810_slot2_pw_vdd, 1);
+               udelay(100);
+       } else {
+               gpio_set_value(n810_slot2_pw_vdd, 0);
+               msleep(50);
+               gpio_set_value(n810_slot2_pw_vddf, 0);
+               msleep(50);
+       }
+}
+
+static int n800_mmc_set_power(struct device *dev, int slot, int power_on,
+                             int vdd)
+{
+       if (machine_is_nokia_n800() || slot == 0)
+               return n800_mmc_set_power_menelaus(dev, slot, power_on, vdd);
+
+       nokia_mmc_set_power_internal(dev, power_on);
+
+       return 0;
+}
+
+static int n800_mmc_set_bus_mode(struct device *dev, int slot, int bus_mode)
+{
+       int r;
+
+       dev_dbg(dev, "Set slot %d bus mode %s\n", slot + 1,
+               bus_mode == MMC_BUSMODE_OPENDRAIN ? "open-drain" : "push-pull");
+       BUG_ON(slot != 0 && slot != 1);
+       slot++;
+       switch (bus_mode) {
+       case MMC_BUSMODE_OPENDRAIN:
+               r = menelaus_set_mmc_opendrain(slot, 1);
+               break;
+       case MMC_BUSMODE_PUSHPULL:
+               r = menelaus_set_mmc_opendrain(slot, 0);
+               break;
+       default:
+               BUG();
+       }
+       if (r != 0 && printk_ratelimit())
+               dev_err(dev, "MMC: unable to set bus mode for slot %d\n",
+                       slot);
+       return r;
+}
+
+static int n800_mmc_get_cover_state(struct device *dev, int slot)
+{
+       slot++;
+       BUG_ON(slot != 1 && slot != 2);
+       if (slot == 1)
+               return slot1_cover_open;
+       else
+               return slot2_cover_open;
+}
+
+static void n800_mmc_callback(void *data, u8 card_mask)
+{
+       int bit, *openp, index;
+
+       if (machine_is_nokia_n800()) {
+               bit = 1 << 1;
+               openp = &slot2_cover_open;
+               index = 1;
+       } else {
+               bit = 1;
+               openp = &slot1_cover_open;
+               index = 0;
+       }
+
+       if (card_mask & bit)
+               *openp = 1;
+       else
+               *openp = 0;
+
+       omap_mmc_notify_cover_event(mmc_device, index, *openp);
+}
+
+void n800_mmc_slot1_cover_handler(void *arg, int closed_state)
+{
+       if (mmc_device == NULL)
+               return;
+
+       slot1_cover_open = !closed_state;
+       omap_mmc_notify_cover_event(mmc_device, 0, closed_state);
+}
+
+static int n800_mmc_late_init(struct device *dev)
+{
+       int r, bit, *openp;
+       int vs2sel;
+
+       mmc_device = dev;
+
+       r = menelaus_set_slot_sel(1);
+       if (r < 0)
+               return r;
+
+       if (machine_is_nokia_n800())
+               vs2sel = 0;
+       else
+               vs2sel = 2;
+
+       r = menelaus_set_mmc_slot(2, 0, vs2sel, 1);
+       if (r < 0)
+               return r;
+
+       n800_mmc_set_power(dev, 0, MMC_POWER_ON, 16); /* MMC_VDD_28_29 */
+       n800_mmc_set_power(dev, 1, MMC_POWER_ON, 16);
+
+       r = menelaus_set_mmc_slot(1, 1, 0, 1);
+       if (r < 0)
+               return r;
+       r = menelaus_set_mmc_slot(2, 1, vs2sel, 1);
+       if (r < 0)
+               return r;
+
+       r = menelaus_get_slot_pin_states();
+       if (r < 0)
+               return r;
+
+       if (machine_is_nokia_n800()) {
+               bit = 1 << 1;
+               openp = &slot2_cover_open;
+       } else {
+               bit = 1;
+               openp = &slot1_cover_open;
+               slot2_cover_open = 0;
+       }
+
+       /* All slot pin bits seem to be inversed until first swith change */
+       if (r == 0xf || r == (0xf & ~bit))
+               r = ~r;
+
+       if (r & bit)
+               *openp = 1;
+       else
+               *openp = 0;
+
+       r = menelaus_register_mmc_callback(n800_mmc_callback, NULL);
+
+       return r;
+}
+
+static void n800_mmc_shutdown(struct device *dev)
+{
+       int vs2sel;
+
+       if (machine_is_nokia_n800())
+               vs2sel = 0;
+       else
+               vs2sel = 2;
+
+       menelaus_set_mmc_slot(1, 0, 0, 0);
+       menelaus_set_mmc_slot(2, 0, vs2sel, 0);
+}
+
+static void n800_mmc_cleanup(struct device *dev)
+{
+       menelaus_unregister_mmc_callback();
+
+       gpio_free(slot_switch_gpio);
+
+       if (machine_is_nokia_n810()) {
+               gpio_free(n810_slot2_pw_vddf);
+               gpio_free(n810_slot2_pw_vdd);
+       }
+}
+
+/*
+ * MMC controller1 has two slots that are multiplexed via I2C.
+ * MMC controller2 is not in use.
+ */
+static struct omap_mmc_platform_data mmc1_data = {
+       .nr_slots               = 2,
+       .switch_slot            = n800_mmc_switch_slot,
+       .init                   = n800_mmc_late_init,
+       .cleanup                = n800_mmc_cleanup,
+       .shutdown               = n800_mmc_shutdown,
+       .max_freq               = 24000000,
+       .dma_mask               = 0xffffffff,
+       .slots[0] = {
+               .wires          = 4,
+               .set_power      = n800_mmc_set_power,
+               .set_bus_mode   = n800_mmc_set_bus_mode,
+               .get_cover_state= n800_mmc_get_cover_state,
+               .ocr_mask       = MMC_VDD_165_195 | MMC_VDD_30_31 |
+                                 MMC_VDD_32_33   | MMC_VDD_33_34,
+               .name           = "internal",
+       },
+       .slots[1] = {
+               .set_power      = n800_mmc_set_power,
+               .set_bus_mode   = n800_mmc_set_bus_mode,
+               .get_cover_state= n800_mmc_get_cover_state,
+               .ocr_mask       = MMC_VDD_165_195 | MMC_VDD_20_21 |
+                                 MMC_VDD_21_22 | MMC_VDD_22_23 | MMC_VDD_23_24 |
+                                 MMC_VDD_24_25 | MMC_VDD_27_28 | MMC_VDD_28_29 |
+                                 MMC_VDD_29_30 | MMC_VDD_30_31 | MMC_VDD_32_33 |
+                                 MMC_VDD_33_34,
+               .name           = "external",
+       },
+};
+
+static struct omap_mmc_platform_data *mmc_data[OMAP24XX_NR_MMC];
+
+void __init n800_mmc_init(void)
+
+{
+       if (machine_is_nokia_n810()) {
+               mmc1_data.slots[0].name = "external";
+
+               /*
+                * Some Samsung Movinand chips do not like open-ended
+                * multi-block reads and fall to braind-dead state
+                * while doing so. Reducing the number of blocks in
+                * the transfer or delays in clock disable do not help
+                */
+               mmc1_data.slots[1].name = "internal";
+               mmc1_data.slots[1].ban_openended = 1;
+       }
+
+       if (gpio_request(slot_switch_gpio, "MMC slot switch") < 0)
+               BUG();
+       gpio_direction_output(slot_switch_gpio, 0);
+
+       if (machine_is_nokia_n810()) {
+               if (gpio_request(n810_slot2_pw_vddf, "MMC slot 2 Vddf") < 0)
+                       BUG();
+               gpio_direction_output(n810_slot2_pw_vddf, 0);
+
+               if (gpio_request(n810_slot2_pw_vdd, "MMC slot 2 Vdd") < 0)
+                       BUG();
+               gpio_direction_output(n810_slot2_pw_vdd, 0);
+       }
+
+       mmc_data[0] = &mmc1_data;
+       omap2_init_mmc(mmc_data, OMAP24XX_NR_MMC);
+}
+#else
+
+void __init n800_mmc_init(void)
+{
+}
+
+void n800_mmc_slot1_cover_handler(void *arg, int state)
+{
+}
+
+#endif
diff --git a/arch/arm/mach-omap2/board-n800-usb.c b/arch/arm/mach-omap2/board-n800-usb.c
new file mode 100644 (file)
index 0000000..8250014
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * linux/arch/arm/mach-omap2/board-n800-usb.c
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Juha Yrjola
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/usb/musb.h>
+#include <mach/gpmc.h>
+#include <mach/pm.h>
+
+#define TUSB_ASYNC_CS          1
+#define TUSB_SYNC_CS           4
+#define GPIO_TUSB_INT          58
+#define GPIO_TUSB_ENABLE       0
+
+static int tusb_set_power(int state);
+static int tusb_set_clock(struct clk *osc_ck, int state);
+
+#if    defined(CONFIG_USB_MUSB_OTG)
+#      define BOARD_MODE       MUSB_OTG
+#elif  defined(CONFIG_USB_MUSB_PERIPHERAL)
+#      define BOARD_MODE       MUSB_PERIPHERAL
+#else  /* defined(CONFIG_USB_MUSB_HOST) */
+#      define BOARD_MODE       MUSB_HOST
+#endif
+
+static struct musb_hdrc_eps_bits musb_eps[] = {
+       {       "ep1_tx", 5,    },
+       {       "ep1_rx", 5,    },
+       {       "ep2_tx", 5,    },
+       {       "ep2_rx", 5,    },
+       {       "ep3_tx", 3,    },
+       {       "ep3_rx", 3,    },
+       {       "ep4_tx", 3,    },
+       {       "ep4_rx", 3,    },
+       {       "ep5_tx", 2,    },
+       {       "ep5_rx", 2,    },
+       {       "ep6_tx", 2,    },
+       {       "ep6_rx", 2,    },
+       {       "ep7_tx", 2,    },
+       {       "ep7_rx", 2,    },
+       {       "ep8_tx", 2,    },
+       {       "ep8_rx", 2,    },
+       {       "ep9_tx", 2,    },
+       {       "ep9_rx", 2,    },
+       {       "ep10_tx", 2,   },
+       {       "ep10_rx", 2,   },
+       {       "ep11_tx", 2,   },
+       {       "ep11_rx", 2,   },
+       {       "ep12_tx", 2,   },
+       {       "ep12_rx", 2,   },
+       {       "ep13_tx", 2,   },
+       {       "ep13_rx", 2,   },
+       {       "ep14_tx", 2,   },
+       {       "ep14_rx", 2,   },
+       {       "ep15_tx", 2,   },
+       {       "ep15_rx", 2,   },
+};
+
+static struct musb_hdrc_config musb_config = {
+       .multipoint     = 1,
+       .dyn_fifo       = 1,
+       .soft_con       = 1,
+       .dma            = 1,
+       .num_eps        = 16,
+       .dma_channels   = 7,
+       .ram_bits       = 12,
+       .eps_bits       = musb_eps,
+};
+
+static struct musb_hdrc_platform_data tusb_data = {
+       .mode           = BOARD_MODE,
+       .set_power      = tusb_set_power,
+       .set_clock      = tusb_set_clock,
+       .min_power      = 25,   /* x2 = 50 mA drawn from VBUS as peripheral */
+       .power          = 100,  /* Max 100 mA VBUS for host mode */
+       .clock          = "osc_ck",
+       .config         = &musb_config,
+};
+
+/*
+ * Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and
+ * 1.5 V voltage regulators of PM companion chip. Companion chip will then
+ * provide then PGOOD signal to TUSB6010 which will release it from reset.
+ */
+static int tusb_set_power(int state)
+{
+       int i, retval = 0;
+
+       if (state) {
+               gpio_set_value(GPIO_TUSB_ENABLE, 1);
+               msleep(1);
+
+               /* Wait until TUSB6010 pulls INT pin down */
+               i = 100;
+               while (i && gpio_get_value(GPIO_TUSB_INT)) {
+                       msleep(1);
+                       i--;
+               }
+
+               if (!i) {
+                       printk(KERN_ERR "tusb: powerup failed\n");
+                       retval = -ENODEV;
+               }
+       } else {
+               gpio_set_value(GPIO_TUSB_ENABLE, 0);
+               msleep(10);
+       }
+
+       return retval;
+}
+
+static int             osc_ck_on;
+
+static int tusb_set_clock(struct clk *osc_ck, int state)
+{
+       if (state) {
+               if (osc_ck_on > 0)
+                       return -ENODEV;
+
+               //omap2_block_sleep();
+               clk_enable(osc_ck);
+               osc_ck_on = 1;
+       } else {
+               if (osc_ck_on == 0)
+                       return -ENODEV;
+
+               clk_disable(osc_ck);
+               osc_ck_on = 0;
+               //omap2_allow_sleep();
+       }
+
+       return 0;
+}
+
+void __init n800_usb_init(void)
+{
+       int ret = 0;
+       static char     announce[] __initdata = KERN_INFO "TUSB 6010\n";
+
+       /* PM companion chip power control pin */
+       ret = gpio_request(GPIO_TUSB_ENABLE, "TUSB6010 enable");
+       if (ret != 0) {
+               printk(KERN_ERR "Could not get TUSB power GPIO%i\n",
+                      GPIO_TUSB_ENABLE);
+               return;
+       }
+       gpio_direction_output(GPIO_TUSB_ENABLE, 0);
+
+       tusb_set_power(0);
+
+       ret = tusb6010_setup_interface(&tusb_data, TUSB6010_REFCLK_19, 2,
+                                       TUSB_ASYNC_CS, TUSB_SYNC_CS,
+                                       GPIO_TUSB_INT, 0x3f);
+       if (ret != 0)
+               goto err;
+
+       printk(announce);
+
+       return;
+
+err:
+       gpio_free(GPIO_TUSB_ENABLE);
+}
diff --git a/arch/arm/mach-omap2/board-n800.c b/arch/arm/mach-omap2/board-n800.c
new file mode 100644 (file)
index 0000000..f1552f0
--- /dev/null
@@ -0,0 +1,750 @@
+/*
+ * linux/arch/arm/mach-omap2/board-n800.c
+ *
+ * Copyright (C) 2005-2007 Nokia Corporation
+ * Author: Juha Yrjola <juha.yrjola@nokia.com>
+ *
+ * Modified from mach-omap2/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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc2301.h>
+#include <linux/spi/tsc2005.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
+#include <linux/i2c/lm8323.h>
+#include <linux/i2c/menelaus.h>
+#include <linux/i2c/lp5521.h>
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <mach/gpio.h>
+#include <mach/usb.h>
+#include <mach/board.h>
+#include <mach/common.h>
+#include <mach/mcspi.h>
+#include <mach/lcd_mipid.h>
+#include <mach/clock.h>
+#include <mach/gpio-switch.h>
+#include <mach/omapfb.h>
+#include <mach/blizzard.h>
+#include <mach/board-nokia.h>
+
+#include <../drivers/cbus/tahvo.h>
+#include <../drivers/media/video/tcm825x.h>
+
+#define N800_BLIZZARD_POWERDOWN_GPIO   15
+#define N800_STI_GPIO                  62
+#define N800_KEYB_IRQ_GPIO             109
+#define N800_DAV_IRQ_GPIO              103
+#define N800_TSC2301_RESET_GPIO                118
+
+#ifdef CONFIG_MACH_NOKIA_N810
+static s16 rx44_keymap[LM8323_KEYMAP_SIZE] = {
+       [0x01] = KEY_Q,
+       [0x02] = KEY_K,
+       [0x03] = KEY_O,
+       [0x04] = KEY_P,
+       [0x05] = KEY_BACKSPACE,
+       [0x06] = KEY_A,
+       [0x07] = KEY_S,
+       [0x08] = KEY_D,
+       [0x09] = KEY_F,
+       [0x0a] = KEY_G,
+       [0x0b] = KEY_H,
+       [0x0c] = KEY_J,
+
+       [0x11] = KEY_W,
+       [0x12] = KEY_F4,
+       [0x13] = KEY_L,
+       [0x14] = KEY_APOSTROPHE,
+       [0x16] = KEY_Z,
+       [0x17] = KEY_X,
+       [0x18] = KEY_C,
+       [0x19] = KEY_V,
+       [0x1a] = KEY_B,
+       [0x1b] = KEY_N,
+       [0x1c] = KEY_LEFTSHIFT, /* Actually, this is both shift keys */
+       [0x1f] = KEY_F7,
+
+       [0x21] = KEY_E,
+       [0x22] = KEY_SEMICOLON,
+       [0x23] = KEY_MINUS,
+       [0x24] = KEY_EQUAL,
+       [0x2b] = KEY_FN,
+       [0x2c] = KEY_M,
+       [0x2f] = KEY_F8,
+
+       [0x31] = KEY_R,
+       [0x32] = KEY_RIGHTCTRL,
+       [0x34] = KEY_SPACE,
+       [0x35] = KEY_COMMA,
+       [0x37] = KEY_UP,
+       [0x3c] = KEY_COMPOSE,
+       [0x3f] = KEY_F6,
+
+       [0x41] = KEY_T,
+       [0x44] = KEY_DOT,
+       [0x46] = KEY_RIGHT,
+       [0x4f] = KEY_F5,
+       [0x51] = KEY_Y,
+       [0x53] = KEY_DOWN,
+       [0x55] = KEY_ENTER,
+       [0x5f] = KEY_ESC,
+
+       [0x61] = KEY_U,
+       [0x64] = KEY_LEFT,
+
+       [0x71] = KEY_I,
+       [0x75] = KEY_KPENTER,
+};
+
+static struct lm8323_platform_data lm8323_pdata = {
+       .repeat         = 0, /* Repeat is handled in userspace for now. */
+       .keymap         = rx44_keymap,
+       .size_x         = 8,
+       .size_y         = 8,
+       .debounce_time  = 12,
+       .active_time    = 500,
+
+       .name           = "Internal keyboard",
+       .pwm1_name      = "n810::keyboard",
+       .pwm2_name      = "n810::cover",
+};
+#endif
+
+void __init nokia_n800_init_irq(void)
+{
+       omap2_init_common_hw(NULL);
+       omap_init_irq();
+       omap_gpio_init();
+
+#ifdef CONFIG_OMAP_STI
+       if (gpio_request(N800_STI_GPIO, "STI") < 0) {
+               printk(KERN_ERR "Failed to request GPIO %d for STI\n",
+                      N800_STI_GPIO);
+               return;
+       }
+
+       gpio_direction_output(N800_STI_GPIO, 0);
+#endif
+}
+
+#if defined(CONFIG_MENELAUS) && defined(CONFIG_SENSORS_TMP105)
+
+static int n800_tmp105_set_power(int enable)
+{
+       return menelaus_set_vaux(enable ? 2800 : 0);
+}
+
+#else
+
+#define n800_tmp105_set_power NULL
+
+#endif
+
+static struct omap_uart_config n800_uart_config __initdata = {
+       .enabled_uarts = (1 << 0) | (1 << 2),
+};
+
+#include "../../../drivers/cbus/retu.h"
+
+static struct omap_fbmem_config n800_fbmem0_config __initdata = {
+       .size = 752 * 1024,
+};
+
+static struct omap_fbmem_config n800_fbmem1_config __initdata = {
+       .size = 752 * 1024,
+};
+
+static struct omap_fbmem_config n800_fbmem2_config __initdata = {
+       .size = 752 * 1024,
+};
+
+static struct omap_tmp105_config n800_tmp105_config __initdata = {
+       .tmp105_irq_pin = 125,
+       .set_power = n800_tmp105_set_power,
+};
+
+static void mipid_shutdown(struct mipid_platform_data *pdata)
+{
+       if (pdata->nreset_gpio != -1) {
+               pr_info("shutdown LCD\n");
+               gpio_set_value(pdata->nreset_gpio, 0);
+               msleep(120);
+       }
+}
+
+static struct mipid_platform_data n800_mipid_platform_data = {
+       .shutdown = mipid_shutdown,
+};
+
+static void __init mipid_dev_init(void)
+{
+       const struct omap_lcd_config *conf;
+
+       conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
+       if (conf != NULL) {
+               n800_mipid_platform_data.nreset_gpio = conf->nreset_gpio;
+               n800_mipid_platform_data.data_lines = conf->data_lines;
+       }
+}
+
+static struct {
+       struct clk *sys_ck;
+} blizzard;
+
+static int blizzard_get_clocks(void)
+{
+       blizzard.sys_ck = clk_get(0, "osc_ck");
+       if (IS_ERR(blizzard.sys_ck)) {
+               printk(KERN_ERR "can't get Blizzard clock\n");
+               return PTR_ERR(blizzard.sys_ck);
+       }
+       return 0;
+}
+
+static unsigned long blizzard_get_clock_rate(struct device *dev)
+{
+       return clk_get_rate(blizzard.sys_ck);
+}
+
+static void blizzard_enable_clocks(int enable)
+{
+       if (enable)
+               clk_enable(blizzard.sys_ck);
+       else
+               clk_disable(blizzard.sys_ck);
+}
+
+static void blizzard_power_up(struct device *dev)
+{
+       /* Vcore to 1.475V */
+       tahvo_set_clear_reg_bits(0x07, 0, 0xf);
+       msleep(10);
+
+       blizzard_enable_clocks(1);
+       gpio_set_value(N800_BLIZZARD_POWERDOWN_GPIO, 1);
+}
+
+static void blizzard_power_down(struct device *dev)
+{
+       gpio_set_value(N800_BLIZZARD_POWERDOWN_GPIO, 0);
+       blizzard_enable_clocks(0);
+
+       /* Vcore to 1.005V */
+       tahvo_set_clear_reg_bits(0x07, 0xf, 0);
+}
+
+static struct blizzard_platform_data n800_blizzard_data = {
+       .power_up       = blizzard_power_up,
+       .power_down     = blizzard_power_down,
+       .get_clock_rate = blizzard_get_clock_rate,
+       .te_connected   = 1,
+};
+
+static void __init blizzard_dev_init(void)
+{
+       int r;
+
+       r = gpio_request(N800_BLIZZARD_POWERDOWN_GPIO, "Blizzard pd");
+       if (r < 0)
+               return;
+       gpio_direction_output(N800_BLIZZARD_POWERDOWN_GPIO, 1);
+
+       blizzard_get_clocks();
+       omapfb_set_ctrl_platform_data(&n800_blizzard_data);
+}
+
+static struct omap_board_config_kernel n800_config[] __initdata = {
+       { OMAP_TAG_UART,                        &n800_uart_config },
+       { OMAP_TAG_FBMEM,                       &n800_fbmem0_config },
+       { OMAP_TAG_FBMEM,                       &n800_fbmem1_config },
+       { OMAP_TAG_FBMEM,                       &n800_fbmem2_config },
+       { OMAP_TAG_TMP105,                      &n800_tmp105_config },
+};
+
+static struct tsc2301_platform_data tsc2301_config = {
+       .reset_gpio     = N800_TSC2301_RESET_GPIO,
+       .keymap = {
+               -1,             /* Event for bit 0 */
+               KEY_UP,         /* Event for bit 1 (up) */
+               KEY_F5,         /* Event for bit 2 (home) */
+               -1,             /* Event for bit 3 */
+               KEY_LEFT,       /* Event for bit 4 (left) */
+               KEY_ENTER,      /* Event for bit 5 (enter) */
+               KEY_RIGHT,      /* Event for bit 6 (right) */
+               -1,             /* Event for bit 7 */
+               KEY_ESC,        /* Event for bit 8 (cycle) */
+               KEY_DOWN,       /* Event for bit 9 (down) */
+               KEY_F4,         /* Event for bit 10 (menu) */
+               -1,             /* Event for bit 11 */
+               KEY_F8,         /* Event for bit 12 (Zoom-) */
+               KEY_F6,         /* Event for bit 13 (FS) */
+               KEY_F7,         /* Event for bit 14 (Zoom+) */
+               -1,             /* Event for bit 15 */
+       },
+       .kp_rep         = 0,
+       .keyb_name      = "Internal keypad",
+};
+
+static void tsc2301_dev_init(void)
+{
+       int r;
+       int gpio = N800_KEYB_IRQ_GPIO;
+
+       r = gpio_request(gpio, "tsc2301 KBD IRQ");
+       if (r >= 0) {
+               gpio_direction_input(gpio);
+               tsc2301_config.keyb_int = gpio_to_irq(gpio);
+       } else {
+               printk(KERN_ERR "unable to get KBD GPIO");
+       }
+
+       gpio = N800_DAV_IRQ_GPIO;
+       r = gpio_request(gpio, "tsc2301 DAV IRQ");
+       if (r >= 0) {
+               gpio_direction_input(gpio);
+               tsc2301_config.dav_int = gpio_to_irq(gpio);
+       } else {
+               printk(KERN_ERR "unable to get DAV GPIO");
+       }
+}
+
+static int __init tea5761_dev_init(void)
+{
+       const struct omap_tea5761_config *info;
+       int enable_gpio = 0;
+
+       info = omap_get_config(OMAP_TAG_TEA5761, struct omap_tea5761_config);
+       if (info)
+               enable_gpio = info->enable_gpio;
+
+       if (enable_gpio) {
+               pr_debug("Enabling tea5761 at GPIO %d\n",
+                        enable_gpio);
+
+               if (gpio_request(enable_gpio, "TEA5761 enable") < 0) {
+                       printk(KERN_ERR "Can't request GPIO %d\n",
+                              enable_gpio);
+                       return -ENODEV;
+               }
+
+               gpio_direction_output(enable_gpio, 0);
+               udelay(50);
+               gpio_set_value(enable_gpio, 1);
+       }
+
+       return 0;
+}
+
+static struct omap2_mcspi_device_config tsc2301_mcspi_config = {
+       .turbo_mode     = 0,
+       .single_channel = 1,
+};
+
+static struct omap2_mcspi_device_config mipid_mcspi_config = {
+       .turbo_mode     = 0,
+       .single_channel = 1,
+};
+
+static struct omap2_mcspi_device_config cx3110x_mcspi_config = {
+       .turbo_mode     = 0,
+       .single_channel = 1,
+};
+
+#ifdef CONFIG_TOUCHSCREEN_TSC2005
+static struct tsc2005_platform_data tsc2005_config = {
+       .reset_gpio = 94,
+       .dav_gpio = 106
+};
+
+static struct omap2_mcspi_device_config tsc2005_mcspi_config = {
+       .turbo_mode     = 0,
+       .single_channel = 1,
+};
+#endif
+
+static struct spi_board_info n800_spi_board_info[] __initdata = {
+       {
+               .modalias       = "lcd_mipid",
+               .bus_num        = 1,
+               .chip_select    = 1,
+               .max_speed_hz   = 4000000,
+               .controller_data= &mipid_mcspi_config,
+               .platform_data  = &n800_mipid_platform_data,
+       }, {
+               .modalias       = "cx3110x",
+               .bus_num        = 2,
+               .chip_select    = 0,
+               .max_speed_hz   = 48000000,
+               .controller_data= &cx3110x_mcspi_config,
+       },
+       {
+               .modalias       = "tsc2301",
+               .bus_num        = 1,
+               .chip_select    = 0,
+               .max_speed_hz   = 6000000,
+               .controller_data= &tsc2301_mcspi_config,
+               .platform_data  = &tsc2301_config,
+       },
+};
+
+static struct spi_board_info n810_spi_board_info[] __initdata = {
+       {
+               .modalias        = "lcd_mipid",
+               .bus_num         = 1,
+               .chip_select     = 1,
+               .max_speed_hz    = 4000000,
+               .controller_data = &mipid_mcspi_config,
+               .platform_data   = &n800_mipid_platform_data,
+       },
+       {
+               .modalias        = "cx3110x",
+               .bus_num         = 2,
+               .chip_select     = 0,
+               .max_speed_hz    = 48000000,
+               .controller_data = &cx3110x_mcspi_config,
+       },
+       {
+               .modalias        = "tsc2005",
+               .bus_num         = 1,
+               .chip_select     = 0,
+               .max_speed_hz    = 6000000,
+               .controller_data = &tsc2005_mcspi_config,
+               .platform_data   = &tsc2005_config,
+       },
+};
+
+static void __init tsc2005_set_config(void)
+{
+       const struct omap_lcd_config *conf;
+
+       conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
+       if (conf != NULL) {
+#ifdef CONFIG_TOUCHSCREEN_TSC2005
+               if (strcmp(conf->panel_name, "lph8923") == 0) {
+                       tsc2005_config.ts_x_plate_ohm = 180;
+                       tsc2005_config.ts_hw_avg = 0;
+                       tsc2005_config.ts_ignore_last = 0;
+                       tsc2005_config.ts_touch_pressure = 1500;
+                       tsc2005_config.ts_stab_time = 100;
+                       tsc2005_config.ts_pressure_max = 2048;
+                       tsc2005_config.ts_pressure_fudge = 2;
+                       tsc2005_config.ts_x_max = 4096;
+                       tsc2005_config.ts_x_fudge = 4;
+                       tsc2005_config.ts_y_max = 4096;
+                       tsc2005_config.ts_y_fudge = 7;
+               } else if (strcmp(conf->panel_name, "ls041y3") == 0) {
+                       tsc2005_config.ts_x_plate_ohm = 280;
+                       tsc2005_config.ts_hw_avg = 0;
+                       tsc2005_config.ts_ignore_last = 0;
+                       tsc2005_config.ts_touch_pressure = 1500;
+                       tsc2005_config.ts_stab_time = 1000;
+                       tsc2005_config.ts_pressure_max = 2048;
+                       tsc2005_config.ts_pressure_fudge = 2;
+                       tsc2005_config.ts_x_max = 4096;
+                       tsc2005_config.ts_x_fudge = 4;
+                       tsc2005_config.ts_y_max = 4096;
+                       tsc2005_config.ts_y_fudge = 7;
+               } else {
+                       printk(KERN_ERR "Unknown panel type, set default "
+                              "touchscreen configuration\n");
+                       tsc2005_config.ts_x_plate_ohm = 200;
+                       tsc2005_config.ts_stab_time = 100;
+               }
+#endif
+       }
+}
+
+#if defined(CONFIG_CBUS_RETU) && defined(CONFIG_LEDS_OMAP_PWM)
+
+void retu_keypad_led_set_power(struct omap_pwm_led_platform_data *self,
+                              int on_off)
+{
+       if (on_off) {
+               retu_write_reg(RETU_REG_CTRL_SET, 1 << 6);
+               msleep(2);
+               retu_write_reg(RETU_REG_CTRL_SET, 1 << 3);
+       } else {
+               retu_write_reg(RETU_REG_CTRL_CLR, (1 << 6) | (1 << 3));
+       }
+}
+
+static struct omap_pwm_led_platform_data n800_keypad_led_data = {
+       .name = "keypad",
+       .intensity_timer = 10,
+       .blink_timer = 9,
+       .set_power = retu_keypad_led_set_power,
+};
+
+static struct platform_device n800_keypad_led_device = {
+       .name           = "omap_pwm_led",
+       .id             = -1,
+       .dev            = {
+               .platform_data = &n800_keypad_led_data,
+       },
+};
+#endif
+
+#if defined(CONFIG_TOUCHSCREEN_TSC2301)
+static void __init n800_ts_set_config(void)
+{
+       const struct omap_lcd_config *conf;
+
+       conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
+       if (conf != NULL) {
+               if (strcmp(conf->panel_name, "lph8923") == 0) {
+                       tsc2301_config.ts_x_plate_ohm   = 180;
+                       tsc2301_config.ts_hw_avg        = 8;
+                       tsc2301_config.ts_max_pressure  = 2048;
+                       tsc2301_config.ts_touch_pressure = 400;
+                       tsc2301_config.ts_stab_time     = 100;
+                       tsc2301_config.ts_pressure_fudge = 2;
+                       tsc2301_config.ts_x_max         = 4096;
+                       tsc2301_config.ts_x_fudge       = 4;
+                       tsc2301_config.ts_y_max         = 4096;
+                       tsc2301_config.ts_y_fudge       = 7;
+               } else if (strcmp(conf->panel_name, "ls041y3") == 0) {
+                       tsc2301_config.ts_x_plate_ohm   = 280;
+                       tsc2301_config.ts_hw_avg        = 8;
+                       tsc2301_config.ts_touch_pressure = 400;
+                       tsc2301_config.ts_max_pressure  = 2048;
+                       tsc2301_config.ts_stab_time     = 1000;
+                       tsc2301_config.ts_pressure_fudge = 2;
+                       tsc2301_config.ts_x_max         = 4096;
+                       tsc2301_config.ts_x_fudge       = 4;
+                       tsc2301_config.ts_y_max         = 4096;
+                       tsc2301_config.ts_y_fudge       = 7;
+               } else {
+                       printk(KERN_ERR "Unknown panel type, set default "
+                              "touchscreen configuration\n");
+                       tsc2301_config.ts_x_plate_ohm   = 200;
+                       tsc2301_config.ts_stab_time     = 100;
+               }
+       }
+}
+#else
+static inline void n800_ts_set_config(void)
+{
+}
+#endif
+
+static struct omap_gpio_switch n800_gpio_switches[] __initdata = {
+       {
+               .name                   = "bat_cover",
+               .gpio                   = -1,
+               .debounce_rising        = 100,
+               .debounce_falling       = 0,
+               .notify                 = n800_mmc_slot1_cover_handler,
+               .notify_data            = NULL,
+       }, {
+               .name                   = "headphone",
+               .gpio                   = -1,
+               .debounce_rising        = 200,
+               .debounce_falling       = 200,
+       }, {
+               .name                   = "cam_act",
+               .gpio                   = -1,
+               .debounce_rising        = 200,
+               .debounce_falling       = 200,
+       }, {
+               .name                   = "cam_turn",
+               .gpio                   = -1,
+               .debounce_rising        = 100,
+               .debounce_falling       = 100,
+       },
+};
+
+#if defined(CONFIG_CBUS_RETU_HEADSET)
+static struct platform_device retu_headset_device = {
+       .name   = "retu-headset",
+       .id     = -1,
+};
+#endif
+
+static struct platform_device *n800_devices[] __initdata = {
+#if defined(CONFIG_CBUS_RETU) && defined(CONFIG_LEDS_OMAP_PWM)
+       &n800_keypad_led_device,
+#endif
+#if defined(CONFIG_CBUS_RETU_HEADSET)
+       &retu_headset_device,
+#endif
+};
+
+#ifdef CONFIG_MENELAUS
+static int n800_auto_sleep_regulators(void)
+{
+       u32 val;
+       int ret;
+
+       val = EN_VPLL_SLEEP | EN_VMMC_SLEEP    \
+               | EN_VAUX_SLEEP | EN_VIO_SLEEP \
+               | EN_VMEM_SLEEP | EN_DC3_SLEEP \
+               | EN_VC_SLEEP | EN_DC2_SLEEP;
+
+       ret = menelaus_set_regulator_sleep(1, val);
+       if (ret < 0) {
+               printk(KERN_ERR "Could not set regulators to sleep on "
+                       "menelaus: %u\n", ret);
+               return ret;
+       }
+       return 0;
+}
+
+static int n800_auto_voltage_scale(void)
+{
+       int ret;
+
+       ret = menelaus_set_vcore_hw(1400, 1050);
+       if (ret < 0) {
+               printk(KERN_ERR "Could not set VCORE voltage on "
+                       "menelaus: %u\n", ret);
+               return ret;
+       }
+       return 0;
+}
+
+static int n800_menelaus_init(struct device *dev)
+{
+       int ret;
+
+       ret = n800_auto_voltage_scale();
+       if (ret < 0)
+               return ret;
+       ret = n800_auto_sleep_regulators();
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+static struct menelaus_platform_data n800_menelaus_platform_data = {
+       .late_init = n800_menelaus_init,
+};
+#endif
+
+static struct i2c_board_info __initdata n800_i2c_board_info_1[] = {
+       {
+               I2C_BOARD_INFO("menelaus", 0x72),
+               .irq = INT_24XX_SYS_NIRQ,
+               .platform_data = &n800_menelaus_platform_data,
+       },
+};
+
+static struct lp5521_platform_data n810_lp5521_platform_data = {
+       .mode           = LP5521_MODE_DIRECT_CONTROL,
+       .label          = "n810",
+       .red_present    = true,
+       .green_present  = true,
+       .blue_present   = true,
+};
+
+extern struct tcm825x_platform_data n800_tcm825x_platform_data;
+
+static struct i2c_board_info __initdata_or_module n8x0_i2c_board_info_2[] = {
+       {
+               I2C_BOARD_INFO(TCM825X_NAME, TCM825X_I2C_ADDR),
+#if defined (CONFIG_VIDEO_TCM825X) || defined (CONFIG_VIDEO_TCM825X_MODULE)
+               .platform_data = &n800_tcm825x_platform_data,
+#endif
+       },
+};
+
+
+static struct i2c_board_info __initdata_or_module n800_i2c_board_info_2[] = {
+       {
+               I2C_BOARD_INFO("tea5761", 0x10),
+       },
+};
+
+static struct i2c_board_info __initdata_or_module n810_i2c_board_info_2[] = {
+       {
+               I2C_BOARD_INFO("lm8323", 0x45),
+               .irq            = OMAP_GPIO_IRQ(109),
+               .platform_data  = &lm8323_pdata,
+       },
+       {
+               I2C_BOARD_INFO("tsl2563", 0x29),
+       },
+       {
+               I2C_BOARD_INFO("lp5521", 0x32),
+               .platform_data = &n810_lp5521_platform_data,
+       },
+};
+
+void __init nokia_n800_common_init(void)
+{
+       platform_add_devices(n800_devices, ARRAY_SIZE(n800_devices));
+
+       n800_flash_init();
+       n800_mmc_init();
+       n800_bt_init();
+       n800_dsp_init();
+       n800_usb_init();
+       n800_cam_init();
+       if (machine_is_nokia_n800())
+               spi_register_board_info(n800_spi_board_info,
+                               ARRAY_SIZE(n800_spi_board_info));
+       if (machine_is_nokia_n810()) {
+               tsc2005_set_config();
+               spi_register_board_info(n810_spi_board_info,
+                               ARRAY_SIZE(n810_spi_board_info));
+       }
+       omap_serial_init();
+       omap_register_i2c_bus(1, 400, n800_i2c_board_info_1,
+                             ARRAY_SIZE(n800_i2c_board_info_1));
+       omap_register_i2c_bus(2, 400, n8x0_i2c_board_info_2,
+                             ARRAY_SIZE(n8x0_i2c_board_info_2));
+       if (machine_is_nokia_n800())
+               i2c_register_board_info(2, n800_i2c_board_info_2,
+                       ARRAY_SIZE(n800_i2c_board_info_2));
+       if (machine_is_nokia_n810())
+               i2c_register_board_info(2, n810_i2c_board_info_2,
+                       ARRAY_SIZE(n810_i2c_board_info_2));
+               
+       mipid_dev_init();
+       blizzard_dev_init();
+}
+
+static void __init nokia_n800_init(void)
+{
+       nokia_n800_common_init();
+
+       n800_ts_set_config();
+       tsc2301_dev_init();
+       tea5761_dev_init();
+       omap_register_gpio_switches(n800_gpio_switches,
+                                   ARRAY_SIZE(n800_gpio_switches));
+}
+
+void __init nokia_n800_map_io(void)
+{
+       omap_board_config = n800_config;
+       omap_board_config_size = ARRAY_SIZE(n800_config);
+
+       omap2_set_globals_242x();
+       omap2_map_common_io();
+}
+
+MACHINE_START(NOKIA_N800, "Nokia N800")
+       .phys_io        = 0x48000000,
+       .io_pg_offst    = ((0xd8000000) >> 18) & 0xfffc,
+       .boot_params    = 0x80000100,
+       .map_io         = nokia_n800_map_io,
+       .init_irq       = nokia_n800_init_irq,
+       .init_machine   = nokia_n800_init,
+       .timer          = &omap_timer,
+MACHINE_END
diff --git a/arch/arm/mach-omap2/board-n800.h b/arch/arm/mach-omap2/board-n800.h
new file mode 100644 (file)
index 0000000..e71dae4
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * linux/arch/arm/mach-omap2/board-n800.h
+ *
+ * Copyright (C) 2005-2007 Nokia Corporation
+ * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ *
+ * Modified from mach-omap2/board-n800.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.
+ */
+
+#ifndef __ARCH_ARM_MACH_OMAP2_BOARD_N800_H
+#define __ARCH_ARM_MACH_OMAP2_BOARD_N800_H
+
+void __init nokia_n800_common_init(void);
+void __init nokia_n800_map_io(void);
+void __init nokia_n800_init_irq(void);
+
+extern const struct tcm825x_platform_data n800_tcm825x_platform_data;
+
+#endif
diff --git a/arch/arm/mach-omap2/board-n810.c b/arch/arm/mach-omap2/board-n810.c
new file mode 100644 (file)
index 0000000..7984d43
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * linux/arch/arm/mach-omap2/board-n810.c
+ *
+ * Copyright (C) 2007 Nokia
+ * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c/lm8323.h>
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/board.h>
+#include <mach/common.h>
+
+#include "board-n800.h"
+
+static void __init nokia_n810_init(void)
+{
+       nokia_n800_common_init();
+}
+
+MACHINE_START(NOKIA_N810, "Nokia N810")
+       .phys_io        = 0x48000000,
+       .io_pg_offst    = ((0xd8000000) >> 18) & 0xfffc,
+       .boot_params    = 0x80000100,
+       .map_io         = nokia_n800_map_io,
+       .init_irq       = nokia_n800_init_irq,
+       .init_machine   = nokia_n810_init,
+       .timer          = &omap_timer,
+MACHINE_END
+
+MACHINE_START(NOKIA_N810_WIMAX, "Nokia N810 WiMAX")
+       .phys_io        = 0x48000000,
+       .io_pg_offst    = ((0xd8000000) >> 18) & 0xfffc,
+       .boot_params    = 0x80000100,
+       .map_io         = nokia_n800_map_io,
+       .init_irq       = nokia_n800_init_irq,
+       .init_machine   = nokia_n810_init,
+       .timer          = &omap_timer,
+MACHINE_END
diff --git a/arch/arm/mach-omap2/board-omap2evm.c b/arch/arm/mach-omap2/board-omap2evm.c
new file mode 100644 (file)
index 0000000..86477cc
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * linux/arch/arm/mach-omap2/board-omap2evm.c
+ *
+ * Copyright (C) 2008 Mistral Solutions Pvt Ltd
+ *
+ * Modified from mach-omap2/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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/input.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/flash.h>
+
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/common.h>
+#include <mach/mmc.h>
+#include <mach/gpmc.h>
+#include <mach/nand.h>
+#include <mach/mcspi.h>
+#include <mach/mux.h>
+
+#include "mmc-twl4030.h"
+
+#define OMAP2EVM_ETHR_START            0x2c000000
+#define OMAP2EVM_ETHR_SIZE             1024
+#define OMAP2EVM_ETHR_GPIO_IRQ         149
+#define        OMAP2_EVM_TS_GPIO               85
+
+#define GPMC_OFF_CONFIG1_0             0x60
+
+static struct mtd_partition omap2evm_nand_partitions[] = {
+       {
+               .name           = "X-Loader",
+               .offset         = 0,
+               .size           = 1 * (64 * 2048),
+               .mask_flags     = MTD_WRITEABLE,        /* force read-only */
+       },
+       {
+               .name           = "U-Boot",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 3 * (64 * 2048),
+               .mask_flags     = MTD_WRITEABLE,        /* force read-only */
+       },
+       {
+               .name           = "U-Boot Environment",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 1 * (64 * 2048),
+        },
+       {
+               .name           = "Kernel",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 16 * (64 * 2048),     /* 2MB */
+       },
+       {
+               .name           = "Ramdisk",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 32 * (64 * 2048),     /* 4MB */
+       },
+       {
+               .name           = "Filesystem",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = MTDPART_SIZ_FULL,
+       }
+};
+
+static struct omap_nand_platform_data omap2evm_nand_data = {
+       .parts          = omap2evm_nand_partitions,
+       .nr_parts       = ARRAY_SIZE(omap2evm_nand_partitions),
+       .dma_channel    = -1,   /* disable DMA in OMAP NAND driver */
+};
+
+static struct resource omap2evm_nand_resource = {
+       .flags          = IORESOURCE_MEM,
+};
+
+static struct platform_device omap2evm_nand_device = {
+       .name           = "omap2-nand",
+       .id             = -1,
+       .dev            = {
+               .platform_data  = &omap2evm_nand_data,
+       },
+       .num_resources  = 1,
+       .resource       = &omap2evm_nand_resource,
+};
+
+void __init omap2evm_flash_init(void)
+{
+       void __iomem *gpmc_base_add, *gpmc_cs_base_add;
+       unsigned char cs = 0;
+
+       gpmc_base_add = (__force void __iomem *)OMAP243X_GPMC_VIRT;
+       while (cs < GPMC_CS_NUM) {
+               int ret = 0;
+
+               /* Each GPMC set for a single CS is at offset 0x30 */
+               gpmc_cs_base_add = (gpmc_base_add + GPMC_OFF_CONFIG1_0 +
+                                   (cs * 0x30));
+
+               /* xloader/Uboot would have programmed the NAND
+                * base address for us This is a ugly hack. The proper
+                * way of doing this is to pass the setup of u-boot up
+                * to kernel using kernel params - something on the
+                * lines of machineID. Check if Nand is
+                * configured */
+               ret = __raw_readl(gpmc_cs_base_add + GPMC_CS_CONFIG1);
+               if ((ret & 0xC00) == (0x800)) {
+                       /* Found it!! */
+                       printk(KERN_INFO "NAND: Found NAND on CS %d \n", cs);
+                       break;
+               }
+               cs++;
+       }
+       if (cs >= GPMC_CS_NUM) {
+               printk(KERN_INFO "MTD: Unable to find MTD configuration in "
+                                "GPMC   - not registering.\n");
+               return;
+       }
+
+       omap2evm_nand_data.cs                   = cs;
+       omap2evm_nand_data.gpmc_cs_baseaddr     = gpmc_cs_base_add;
+       omap2evm_nand_data.gpmc_baseaddr        = gpmc_base_add;
+
+       if (platform_device_register(&omap2evm_nand_device) < 0) {
+               printk(KERN_ERR "Unable to register NAND device\n");
+               return;
+       }
+}
+
+static struct resource omap2evm_smc911x_resources[] = {
+       [0] =   {
+               .start  = OMAP2EVM_ETHR_START,
+               .end    = (OMAP2EVM_ETHR_START + OMAP2EVM_ETHR_SIZE - 1),
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] =   {
+               .start  = OMAP_GPIO_IRQ(OMAP2EVM_ETHR_GPIO_IRQ),
+               .end    = OMAP_GPIO_IRQ(OMAP2EVM_ETHR_GPIO_IRQ),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device omap2evm_smc911x_device = {
+       .name       = "smc911x",
+       .id     = -1,
+       .num_resources  = ARRAY_SIZE(omap2evm_smc911x_resources),
+       .resource   = &omap2evm_smc911x_resources [0],
+};
+
+static inline void __init omap2evm_init_smc911x(void)
+{
+       int gpio = OMAP2EVM_ETHR_GPIO_IRQ;
+       int ret;
+
+       ret = gpio_request(gpio, "smc911x IRQ");
+       if (ret < 0) {
+               printk(KERN_ERR "Failed to request GPIO %d for smc911x IRQ\n",
+                               gpio);
+               return;
+       }
+       gpio_direction_input(gpio);
+
+}
+
+static struct platform_device omap2_evm_lcd_device = {
+       .name           = "omap2evm_lcd",
+       .id             = -1,
+};
+
+static struct omap_lcd_config omap2_evm_lcd_config __initdata = {
+       .ctrl_name      = "internal",
+};
+
+static void ads7846_dev_init(void)
+{
+       int gpio = OMAP2_EVM_TS_GPIO;
+       int ret;
+
+       ret = gpio_request(gpio, "ads7846_pen_down");
+       if (ret < 0) {
+               printk(KERN_ERR "Failed to request GPIO %d for ads7846 pen down IRQ\n",
+                               gpio);
+               return;
+       }
+
+       gpio_direction_input(gpio);
+
+       /*Setting the MUX */
+       omap_cfg_reg(Y18_2430_MCSPI1_CLK);
+       omap_cfg_reg(AD15_2430_MCSPI1_SIMO);
+       omap_cfg_reg(AE17_2430_MCSPI1_SOMI);
+       omap_cfg_reg(U1_2430_MCSPI1_CS0);
+
+       omap_cfg_reg(AF19_2430_GPIO_85);
+
+}
+
+static int ads7846_get_pendown_state(void)
+{
+       return !gpio_get_value(OMAP2_EVM_TS_GPIO);
+}
+
+struct ads7846_platform_data ads7846_config = {
+       .x_max                  = 0x0fff,
+       .y_max                  = 0x0fff,
+       .x_plate_ohms           = 180,
+       .pressure_max           = 255,
+       .debounce_max           = 10,
+       .debounce_tol           = 3,
+       .debounce_rep           = 1,
+       .get_pendown_state      = ads7846_get_pendown_state,
+       .keep_vref_on           = 1,
+       .settle_delay_usecs     = 150,
+};
+
+static struct omap2_mcspi_device_config ads7846_mcspi_config = {
+       .turbo_mode     = 0,
+       .single_channel = 1,  /* 0: slave, 1: master */
+};
+
+struct spi_board_info omap2evm_spi_board_info[] = {
+       [0] = {
+               .modalias               = "ads7846",
+               .bus_num                = 1,
+               .chip_select            = 0,
+               .max_speed_hz           = 1500000,
+               .controller_data        = &ads7846_mcspi_config,
+               .irq                    = OMAP_GPIO_IRQ(OMAP2_EVM_TS_GPIO),
+               .platform_data          = &ads7846_config,
+       },
+};
+
+
+static int omap2evm_keymap[] = {
+       KEY(0, 0, KEY_LEFT),
+       KEY(0, 1, KEY_RIGHT),
+       KEY(0, 2, KEY_A),
+       KEY(0, 3, KEY_B),
+       KEY(1, 0, KEY_DOWN),
+       KEY(1, 1, KEY_UP),
+       KEY(1, 2, KEY_E),
+       KEY(1, 3, KEY_F),
+       KEY(2, 0, KEY_ENTER),
+       KEY(2, 1, KEY_I),
+       KEY(2, 2, KEY_J),
+       KEY(2, 3, KEY_K),
+       KEY(3, 0, KEY_M),
+       KEY(3, 1, KEY_N),
+       KEY(3, 2, KEY_O),
+       KEY(3, 3, KEY_P)
+};
+
+static struct twl4030_keypad_data omap2evm_kp_data = {
+       .rows           = 4,
+       .cols           = 4,
+       .keymap         = omap2evm_keymap,
+       .keymapsize     = ARRAY_SIZE(omap2evm_keymap),
+       .rep            = 1,
+};
+
+static void __init omap2_evm_init_irq(void)
+{
+       omap2_init_common_hw(NULL);
+       omap_init_irq();
+       omap_gpio_init();
+       omap2evm_init_smc911x();
+}
+
+static struct omap_uart_config omap2_evm_uart_config __initdata = {
+       .enabled_uarts  = ((1 << 0) | (1 << 1) | (1 << 2)),
+};
+
+static struct omap_board_config_kernel omap2_evm_config[] __initdata = {
+       { OMAP_TAG_UART,        &omap2_evm_uart_config },
+       { OMAP_TAG_LCD,         &omap2_evm_lcd_config },
+};
+
+static struct twl4030_gpio_platform_data omap2evm_gpio_data = {
+       .gpio_base      = OMAP_MAX_GPIO_LINES,
+       .irq_base       = TWL4030_GPIO_IRQ_BASE,
+       .irq_end        = TWL4030_GPIO_IRQ_END,
+};
+
+static struct twl4030_usb_data omap2evm_usb_data = {
+       .usb_mode       = T2_USB_MODE_ULPI,
+};
+
+static struct twl4030_madc_platform_data omap2evm_madc_data = {
+       .irq_line       = 1,
+};
+
+static struct twl4030_platform_data omap2evm_twldata = {
+       .irq_base       = TWL4030_IRQ_BASE,
+       .irq_end        = TWL4030_IRQ_END,
+
+       /* platform_data for children goes here */
+       .keypad         = &omap2evm_kp_data,
+       .madc           = &omap2evm_madc_data,
+       .usb            = &omap2evm_usb_data,
+       .gpio           = &omap2evm_gpio_data,
+};
+
+static struct i2c_board_info __initdata omap2evm_i2c_boardinfo[] = {
+       {
+               I2C_BOARD_INFO("twl4030", 0x48),
+               .flags = I2C_CLIENT_WAKE,
+               .irq = INT_24XX_SYS_NIRQ,
+               .platform_data = &omap2evm_twldata,
+       },
+};
+
+static int __init omap2_evm_i2c_init(void)
+{
+       omap_register_i2c_bus(1, 400, NULL, 0);
+       omap_register_i2c_bus(2, 2600, omap2evm_i2c_boardinfo,
+                       ARRAY_SIZE(omap2evm_i2c_boardinfo));
+       return 0;
+}
+
+static struct platform_device *omap2_evm_devices[] __initdata = {
+       &omap2_evm_lcd_device,
+       &omap2evm_smc911x_device,
+};
+
+static struct twl4030_hsmmc_info mmc[] __initdata = {
+       {
+               .mmc            = 1,
+               .wires          = 4,
+               .gpio_cd        = -EINVAL,
+               .gpio_wp        = -EINVAL,
+       },
+       {}      /* Terminator */
+};
+
+static void __init omap2_evm_init(void)
+{
+       omap2_evm_i2c_init();
+
+       platform_add_devices(omap2_evm_devices, ARRAY_SIZE(omap2_evm_devices));
+       omap_board_config = omap2_evm_config;
+       omap_board_config_size = ARRAY_SIZE(omap2_evm_config);
+       spi_register_board_info(omap2evm_spi_board_info,
+                               ARRAY_SIZE(omap2evm_spi_board_info));
+       omap_serial_init();
+       twl4030_mmc_init(mmc);
+       omap2evm_flash_init();
+       ads7846_dev_init();
+}
+
+static void __init omap2_evm_map_io(void)
+{
+       omap2_set_globals_243x();
+       omap2_map_common_io();
+}
+
+MACHINE_START(OMAP2EVM, "OMAP2EVM Board")
+       /* Maintainer:  Arun KS <arunks@mistralsolutions.com> */
+       .phys_io        = 0x48000000,
+       .io_pg_offst    = ((0xd8000000) >> 18) & 0xfffc,
+       .boot_params    = 0x80000100,
+       .map_io         = omap2_evm_map_io,
+       .init_irq       = omap2_evm_init_irq,
+       .init_machine   = omap2_evm_init,
+       .timer          = &omap_timer,
+MACHINE_END
index 744740ae1b9c0424c842afe2ebba88e20f3e1d61..346351e0fe8689fa79abb23fcb371a39605e6ef2 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/nand.h>
 
 #include <linux/mtd/partitions.h>
 #include <linux/mtd/nand.h>
 
+#include <linux/regulator/machine.h>
 #include <linux/i2c/twl4030.h>
 
 #include <mach/hardware.h>
 #include <linux/i2c/twl4030.h>
 
 #include <mach/hardware.h>
 #include <asm/mach/flash.h>
 
 #include <mach/board.h>
 #include <asm/mach/flash.h>
 
 #include <mach/board.h>
+#include <mach/usb.h>
 #include <mach/common.h>
 #include <mach/gpmc.h>
 #include <mach/nand.h>
 #include <mach/mux.h>
 #include <mach/common.h>
 #include <mach/gpmc.h>
 #include <mach/nand.h>
 #include <mach/mux.h>
-#include <mach/usb.h>
 
 
+#include "twl4030-generic-scripts.h"
 #include "mmc-twl4030.h"
 
 #include "mmc-twl4030.h"
 
+
 #define GPMC_CS0_BASE  0x60
 #define GPMC_CS_SIZE   0x30
 
 #define GPMC_CS0_BASE  0x60
 #define GPMC_CS_SIZE   0x30
 
@@ -104,10 +107,16 @@ static struct platform_device omap3beagle_nand_device = {
        .resource       = &omap3beagle_nand_resource,
 };
 
        .resource       = &omap3beagle_nand_resource,
 };
 
+#include "sdram-micron-mt46h32m32lf-6.h"
+
 static struct omap_uart_config omap3_beagle_uart_config __initdata = {
        .enabled_uarts  = ((1 << 0) | (1 << 1) | (1 << 2)),
 };
 
 static struct omap_uart_config omap3_beagle_uart_config __initdata = {
        .enabled_uarts  = ((1 << 0) | (1 << 1) | (1 << 2)),
 };
 
+static struct twl4030_usb_data beagle_usb_data = {
+       .usb_mode       = T2_USB_MODE_ULPI,
+};
+
 static struct twl4030_hsmmc_info mmc[] = {
        {
                .mmc            = 1,
 static struct twl4030_hsmmc_info mmc[] = {
        {
                .mmc            = 1,
@@ -117,6 +126,14 @@ static struct twl4030_hsmmc_info mmc[] = {
        {}      /* Terminator */
 };
 
        {}      /* Terminator */
 };
 
+static struct regulator_consumer_supply beagle_vmmc1_supply = {
+       .supply                 = "vmmc",
+};
+
+static struct regulator_consumer_supply beagle_vsim_supply = {
+       .supply                 = "vmmc_aux",
+};
+
 static struct gpio_led gpio_leds[];
 
 static int beagle_twl_gpio_setup(struct device *dev,
 static struct gpio_led gpio_leds[];
 
 static int beagle_twl_gpio_setup(struct device *dev,
@@ -127,6 +144,10 @@ static int beagle_twl_gpio_setup(struct device *dev,
        mmc[0].gpio_cd = gpio + 0;
        twl4030_mmc_init(mmc);
 
        mmc[0].gpio_cd = gpio + 0;
        twl4030_mmc_init(mmc);
 
+       /* link regulators to MMC adapters */
+       beagle_vmmc1_supply.dev = mmc[0].dev;
+       beagle_vsim_supply.dev = mmc[0].dev;
+
        /* REVISIT: need ehci-omap hooks for external VBUS
         * power switch and overcurrent detect
         */
        /* REVISIT: need ehci-omap hooks for external VBUS
         * power switch and overcurrent detect
         */
@@ -155,12 +176,114 @@ static struct twl4030_gpio_platform_data beagle_gpio_data = {
        .setup          = beagle_twl_gpio_setup,
 };
 
        .setup          = beagle_twl_gpio_setup,
 };
 
+static struct platform_device omap3_beagle_lcd_device = {
+       .name           = "omap3beagle_lcd",
+       .id             = -1,
+};
+
+static struct regulator_consumer_supply beagle_vdac_supply = {
+       .supply         = "vdac",
+       .dev            = &omap3_beagle_lcd_device.dev,
+};
+
+static struct regulator_consumer_supply beagle_vdvi_supply = {
+       .supply         = "vdvi",
+       .dev            = &omap3_beagle_lcd_device.dev,
+};
+
+/* VMMC1 for MMC1 pins CMD, CLK, DAT0..DAT3 (20 mA, plus card == max 220 mA) */
+static struct regulator_init_data beagle_vmmc1 = {
+       .constraints = {
+               .min_uV                 = 1850000,
+               .max_uV                 = 3150000,
+               .valid_modes_mask       = REGULATOR_MODE_NORMAL
+                                       | REGULATOR_MODE_STANDBY,
+               .valid_ops_mask         = REGULATOR_CHANGE_VOLTAGE
+                                       | REGULATOR_CHANGE_MODE
+                                       | REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies  = 1,
+       .consumer_supplies      = &beagle_vmmc1_supply,
+};
+
+/* VSIM for MMC1 pins DAT4..DAT7 (2 mA, plus card == max 50 mA) */
+static struct regulator_init_data beagle_vsim = {
+       .constraints = {
+               .min_uV                 = 1800000,
+               .max_uV                 = 3000000,
+               .valid_modes_mask       = REGULATOR_MODE_NORMAL
+                                       | REGULATOR_MODE_STANDBY,
+               .valid_ops_mask         = REGULATOR_CHANGE_VOLTAGE
+                                       | REGULATOR_CHANGE_MODE
+                                       | REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies  = 1,
+       .consumer_supplies      = &beagle_vsim_supply,
+};
+
+/* VDAC for DSS driving S-Video (8 mA unloaded, max 65 mA) */
+static struct regulator_init_data beagle_vdac = {
+       .constraints = {
+               .min_uV                 = 1800000,
+               .max_uV                 = 1800000,
+               .valid_modes_mask       = REGULATOR_MODE_NORMAL
+                                       | REGULATOR_MODE_STANDBY,
+               .valid_ops_mask         = REGULATOR_CHANGE_MODE
+                                       | REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies  = 1,
+       .consumer_supplies      = &beagle_vdac_supply,
+};
+
+/* VPLL2 for digital video outputs */
+static struct regulator_init_data beagle_vpll2 = {
+       .constraints = {
+               .name                   = "VDVI",
+               .min_uV                 = 1800000,
+               .max_uV                 = 1800000,
+               .valid_modes_mask       = REGULATOR_MODE_NORMAL
+                                       | REGULATOR_MODE_STANDBY,
+               .valid_ops_mask         = REGULATOR_CHANGE_MODE
+                                       | REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies  = 1,
+       .consumer_supplies      = &beagle_vdvi_supply,
+};
+
+static const struct twl4030_resconfig beagle_resconfig[] = {
+       /* disable regulators that u-boot left enabled; the
+        * devices' drivers should be managing these.
+        */
+       { .resource = RES_VAUX3, },     /* not even connected! */
+       { .resource = RES_VMMC1, },
+       { .resource = RES_VSIM, },
+       { .resource = RES_VPLL2, },
+       { .resource = RES_VDAC, },
+       { .resource = RES_VUSB_1V5, },
+       { .resource = RES_VUSB_1V8, },
+       { .resource = RES_VUSB_3V1, },
+       { 0, },
+};
+
+static struct twl4030_power_data beagle_power_data = {
+       .resource_config        = beagle_resconfig,
+       /* REVISIT can't use GENERIC3430_T2SCRIPTS_DATA;
+        * among other things, it makes reboot fail.
+        */
+};
+
 static struct twl4030_platform_data beagle_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
 
        /* platform_data for children goes here */
 static struct twl4030_platform_data beagle_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
 
        /* platform_data for children goes here */
+       .usb            = &beagle_usb_data,
        .gpio           = &beagle_gpio_data,
        .gpio           = &beagle_gpio_data,
+       .power          = &beagle_power_data,
+       .vmmc1          = &beagle_vmmc1,
+       .vsim           = &beagle_vsim,
+       .vdac           = &beagle_vdac,
+       .vpll2          = &beagle_vpll2,
 };
 
 static struct i2c_board_info __initdata beagle_i2c_boardinfo[] = {
 };
 
 static struct i2c_board_info __initdata beagle_i2c_boardinfo[] = {
@@ -184,16 +307,11 @@ static int __init omap3_beagle_i2c_init(void)
 
 static void __init omap3_beagle_init_irq(void)
 {
 
 static void __init omap3_beagle_init_irq(void)
 {
-       omap2_init_common_hw(NULL);
+       omap2_init_common_hw(mt46h32m32lf6_sdrc_params);
        omap_init_irq();
        omap_gpio_init();
 }
 
        omap_init_irq();
        omap_gpio_init();
 }
 
-static struct platform_device omap3_beagle_lcd_device = {
-       .name           = "omap3beagle_lcd",
-       .id             = -1,
-};
-
 static struct omap_lcd_config omap3_beagle_lcd_config __initdata = {
        .ctrl_name      = "internal",
 };
 static struct omap_lcd_config omap3_beagle_lcd_config __initdata = {
        .ctrl_name      = "internal",
 };
@@ -315,6 +433,7 @@ static void __init omap3_beagle_init(void)
        gpio_direction_output(170, true);
 
        usb_musb_init();
        gpio_direction_output(170, true);
 
        usb_musb_init();
+       usb_ehci_init();
        omap3beagle_flash_init();
 }
 
        omap3beagle_flash_init();
 }
 
diff --git a/arch/arm/mach-omap2/board-omap3evm-flash.c b/arch/arm/mach-omap2/board-omap3evm-flash.c
new file mode 100644 (file)
index 0000000..b58b7c6
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * board-omap3evm-flash.c
+ *
+ * Copyright (c) 2008 Texas Instruments,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/onenand_regs.h>
+#include <linux/types.h>
+#include <linux/io.h>
+
+#include <asm/mach/flash.h>
+#include <mach/onenand.h>
+#include <mach/board.h>
+#include <mach/gpmc.h>
+#include <mach/nand.h>
+
+#define ONENAND_MAP            0x20000000
+
+static int omap3evm_onenand_setup(void __iomem *, int freq);
+
+static struct mtd_partition omap3evm_onenand_partitions[] = {
+       {
+               .name           = "xloader-onenand",
+               .offset         = 0,
+               .size           = 4*(64*2048),
+               .mask_flags     = MTD_WRITEABLE
+       },
+       {
+               .name           = "uboot-onenand",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           =  15*(64*2048),
+               .mask_flags     = MTD_WRITEABLE
+       },
+       {
+               .name           = "params-onenand",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 1*(64*2048),
+       },
+       {
+               .name           = "linux-onenand",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 40*(64*2048),
+       },
+       {
+               .name           = "jffs2-onenand",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = MTDPART_SIZ_FULL,
+       },
+};
+
+static struct omap_onenand_platform_data omap3evm_onenand_data = {
+       .parts = omap3evm_onenand_partitions,
+       .nr_parts = ARRAY_SIZE(omap3evm_onenand_partitions),
+       .onenand_setup = omap3evm_onenand_setup,
+       .dma_channel    = -1,   /* disable DMA in OMAP OneNAND driver */
+};
+
+static struct platform_device omap3evm_onenand_device = {
+       .name           = "omap2-onenand",
+       .id             = -1,
+       .dev = {
+               .platform_data = &omap3evm_onenand_data,
+       },
+};
+
+static struct mtd_partition omap3evm_nand_partitions[] = {
+       /* All the partition sizes are listed in terms of NAND block size */
+       {
+               .name           = "xloader-nand",
+               .offset         = 0,
+               .size           = 4*(128 * 1024),
+               .mask_flags     = MTD_WRITEABLE
+       },
+       {
+               .name           = "uboot-nand",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 14*(128 * 1024),
+               .mask_flags     = MTD_WRITEABLE
+       },
+       {
+               .name           = "params-nand",
+
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 2*(128 * 1024)
+       },
+       {
+               .name           = "linux-nand",
+               .offset         = MTDPART_OFS_APPEND,
+               .size           = 40*(128 * 1024)
+       },
+       {
+               .name           = "jffs2-nand",
+               .size           = MTDPART_SIZ_FULL,
+               .offset         = MTDPART_OFS_APPEND,
+       },
+};
+
+static struct omap_nand_platform_data omap3evm_nand_data = {
+       .parts          = omap3evm_nand_partitions,
+       .nr_parts       = ARRAY_SIZE(omap3evm_nand_partitions),
+       .nand_setup     = NULL,
+       .dma_channel    = -1,           /* disable DMA in OMAP NAND driver */
+       .dev_ready      = NULL,
+};
+
+static struct resource omap3evm_nand_resource = {
+       .flags          = IORESOURCE_MEM,
+};
+
+static struct platform_device omap3evm_nand_device = {
+       .name           = "omap2-nand",
+       .id             = 0,
+       .dev            = {
+               .platform_data  = &omap3evm_nand_data,
+       },
+       .num_resources  = 1,
+       .resource       = &omap3evm_nand_resource,
+};
+
+/*
+ *      omap3evm_onenand_setup - Set the onenand sync mode
+ *      @onenand_base:  The onenand base address in GPMC memory map
+ *
+ */
+
+static int omap3evm_onenand_setup(void __iomem *onenand_base, int freq)
+{
+       /* nothing is required to be setup for onenand as of now */
+       return 0;
+}
+
+void __init omap3evm_flash_init(void)
+{
+       u8              cs = 0;
+       u8              onenandcs = GPMC_CS_NUM + 1, nandcs = GPMC_CS_NUM + 1;
+       u32             gpmc_base_add = OMAP34XX_GPMC_VIRT;
+
+       while (cs < GPMC_CS_NUM) {
+               u32 ret = 0;
+               ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
+
+               /*
+               * xloader/Uboot would have programmed the NAND/oneNAND
+               * base address for us This is a ugly hack. The proper
+               * way of doing this is to pass the setup of u-boot up
+               * to kernel using kernel params - something on the
+               * lines of machineID. Check if NAND/oneNAND is configured
+               */
+               if ((ret & 0xC00) == 0x800) {
+                       /* Found it!! */
+                       if (nandcs > GPMC_CS_NUM)
+                               nandcs = cs;
+               } else {
+                       ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG7);
+                       if ((ret & 0x3F) == (ONENAND_MAP >> 24))
+                               onenandcs = cs;
+               }
+               cs++;
+       }
+       if ((nandcs > GPMC_CS_NUM) && (onenandcs > GPMC_CS_NUM)) {
+               printk(KERN_INFO "NAND/OneNAND: Unable to find configuration "
+                               " in GPMC\n ");
+               return;
+       }
+
+       if (nandcs < GPMC_CS_NUM) {
+               omap3evm_nand_data.cs   = nandcs;
+               omap3evm_nand_data.gpmc_cs_baseaddr = (void *)(gpmc_base_add +
+                                       GPMC_CS0_BASE + nandcs*GPMC_CS_SIZE);
+               omap3evm_nand_data.gpmc_baseaddr   = (void *) (gpmc_base_add);
+
+               if (platform_device_register(&omap3evm_nand_device) < 0) {
+                       printk(KERN_ERR "Unable to register NAND device\n");
+               }
+       }
+
+       if (onenandcs < GPMC_CS_NUM) {
+               omap3evm_onenand_data.cs = onenandcs;
+               if (platform_device_register(&omap3evm_onenand_device) < 0)
+                       printk(KERN_ERR "Unable to register OneNAND device\n");
+       }
+}
+
diff --git a/arch/arm/mach-omap2/board-omap3evm.c b/arch/arm/mach-omap2/board-omap3evm.c
new file mode 100644 (file)
index 0000000..6eb3c52
--- /dev/null
@@ -0,0 +1,333 @@
+/*
+ * linux/arch/arm/mach-omap2/board-omap3evm.c
+ *
+ * Copyright (C) 2008 Texas Instruments
+ *
+ * Modified from mach-omap2/board-3430sdp.c
+ *
+ * Initial code: Syed Mohammed Khasim
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/ads7846.h>
+#include <linux/i2c/twl4030.h>
+
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/gpio.h>
+#include <mach/board.h>
+#include <mach/mux.h>
+#include <mach/usb.h>
+#include <mach/common.h>
+#include <mach/mcspi.h>
+
+#include "sdram-micron-mt46h32m32lf-6.h"
+#include "twl4030-generic-scripts.h"
+#include "mmc-twl4030.h"
+
+#define OMAP3_EVM_TS_GPIO      175
+
+#define OMAP3EVM_ETHR_START    0x2c000000
+#define OMAP3EVM_ETHR_SIZE     1024
+#define OMAP3EVM_ETHR_GPIO_IRQ 176
+#define OMAP3EVM_SMC911X_CS    5
+
+extern void omap3evm_flash_init(void);
+
+static struct resource omap3evm_smc911x_resources[] = {
+       [0] =   {
+               .start  = OMAP3EVM_ETHR_START,
+               .end    = (OMAP3EVM_ETHR_START + OMAP3EVM_ETHR_SIZE - 1),
+               .flags  = IORESOURCE_MEM,
+       },
+       [1] =   {
+               .start  = OMAP_GPIO_IRQ(OMAP3EVM_ETHR_GPIO_IRQ),
+               .end    = OMAP_GPIO_IRQ(OMAP3EVM_ETHR_GPIO_IRQ),
+               .flags  = IORESOURCE_IRQ,
+       },
+};
+
+static struct platform_device omap3evm_smc911x_device = {
+       .name           = "smc911x",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(omap3evm_smc911x_resources),
+       .resource       = &omap3evm_smc911x_resources [0],
+};
+
+static inline void __init omap3evm_init_smc911x(void)
+{
+       int eth_cs;
+       struct clk *l3ck;
+       unsigned int rate;
+
+       eth_cs = OMAP3EVM_SMC911X_CS;
+
+       l3ck = clk_get(NULL, "l3_ck");
+       if (IS_ERR(l3ck))
+               rate = 100000000;
+       else
+               rate = clk_get_rate(l3ck);
+
+       if (gpio_request(OMAP3EVM_ETHR_GPIO_IRQ, "SMC911x irq") < 0) {
+               printk(KERN_ERR "Failed to request GPIO%d for smc911x IRQ\n",
+                       OMAP3EVM_ETHR_GPIO_IRQ);
+               return;
+       }
+
+       gpio_direction_input(OMAP3EVM_ETHR_GPIO_IRQ);
+}
+
+static struct omap_uart_config omap3_evm_uart_config __initdata = {
+       .enabled_uarts  = ((1 << 0) | (1 << 1) | (1 << 2)),
+};
+
+static struct twl4030_hsmmc_info mmc[] = {
+       {
+               .mmc            = 1,
+               .wires          = 4,
+               .gpio_cd        = -EINVAL,
+               .gpio_wp        = 63,
+       },
+       {}      /* Terminator */
+};
+
+static struct gpio_led gpio_leds[] = {
+       {
+               .name                   = "omap3evm::ledb",
+               /* normally not visible (board underside) */
+               .default_trigger        = "default-on",
+               .gpio                   = -EINVAL,      /* gets replaced */
+               .active_low             = true,
+       },
+};
+
+static struct gpio_led_platform_data gpio_led_info = {
+       .leds           = gpio_leds,
+       .num_leds       = ARRAY_SIZE(gpio_leds),
+};
+
+static struct platform_device leds_gpio = {
+       .name   = "leds-gpio",
+       .id     = -1,
+       .dev    = {
+               .platform_data  = &gpio_led_info,
+       },
+};
+
+
+static int omap3evm_twl_gpio_setup(struct device *dev,
+               unsigned gpio, unsigned ngpio)
+{
+       /* gpio + 0 is "mmc0_cd" (input/IRQ) */
+       omap_cfg_reg(L8_34XX_GPIO63);
+       mmc[0].gpio_cd = gpio + 0;
+       twl4030_mmc_init(mmc);
+
+       /* Most GPIOs are for USB OTG.  Some are mostly sent to
+        * the P2 connector; notably LEDA for the LCD backlight.
+        */
+
+       /* TWL4030_GPIO_MAX + 1 == ledB (out, active low LED) */
+       gpio_leds[2].gpio = gpio + TWL4030_GPIO_MAX + 1;
+
+       platform_device_register(&leds_gpio);
+
+       return 0;
+}
+
+static struct twl4030_gpio_platform_data omap3evm_gpio_data = {
+       .gpio_base      = OMAP_MAX_GPIO_LINES,
+       .irq_base       = TWL4030_GPIO_IRQ_BASE,
+       .irq_end        = TWL4030_GPIO_IRQ_END,
+       .use_leds       = true,
+       .setup          = omap3evm_twl_gpio_setup,
+};
+
+static struct twl4030_usb_data omap3evm_usb_data = {
+       .usb_mode       = T2_USB_MODE_ULPI,
+};
+
+static int omap3evm_keymap[] = {
+       KEY(0, 0, KEY_LEFT),
+       KEY(0, 1, KEY_RIGHT),
+       KEY(0, 2, KEY_A),
+       KEY(0, 3, KEY_B),
+       KEY(1, 0, KEY_DOWN),
+       KEY(1, 1, KEY_UP),
+       KEY(1, 2, KEY_E),
+       KEY(1, 3, KEY_F),
+       KEY(2, 0, KEY_ENTER),
+       KEY(2, 1, KEY_I),
+       KEY(2, 2, KEY_J),
+       KEY(2, 3, KEY_K),
+       KEY(3, 0, KEY_M),
+       KEY(3, 1, KEY_N),
+       KEY(3, 2, KEY_O),
+       KEY(3, 3, KEY_P)
+};
+
+static struct twl4030_keypad_data omap3evm_kp_data = {
+       .rows           = 4,
+       .cols           = 4,
+       .keymap         = omap3evm_keymap,
+       .keymapsize     = ARRAY_SIZE(omap3evm_keymap),
+       .rep            = 1,
+};
+
+static struct twl4030_madc_platform_data omap3evm_madc_data = {
+       .irq_line       = 1,
+};
+
+static struct twl4030_platform_data omap3evm_twldata = {
+       .irq_base       = TWL4030_IRQ_BASE,
+       .irq_end        = TWL4030_IRQ_END,
+
+       /* platform_data for children goes here */
+       .keypad         = &omap3evm_kp_data,
+       .madc           = &omap3evm_madc_data,
+       .usb            = &omap3evm_usb_data,
+       .power          = GENERIC3430_T2SCRIPTS_DATA,
+       .gpio           = &omap3evm_gpio_data,
+};
+
+static struct i2c_board_info __initdata omap3evm_i2c_boardinfo[] = {
+       {
+               I2C_BOARD_INFO("twl4030", 0x48),
+               .flags = I2C_CLIENT_WAKE,
+               .irq = INT_34XX_SYS_NIRQ,
+               .platform_data = &omap3evm_twldata,
+       },
+};
+
+static int __init omap3_evm_i2c_init(void)
+{
+       omap_register_i2c_bus(1, 2600, omap3evm_i2c_boardinfo,
+                       ARRAY_SIZE(omap3evm_i2c_boardinfo));
+       omap_register_i2c_bus(2, 400, NULL, 0);
+       omap_register_i2c_bus(3, 400, NULL, 0);
+       return 0;
+}
+
+static struct platform_device omap3_evm_lcd_device = {
+       .name           = "omap3evm_lcd",
+       .id             = -1,
+};
+
+static struct omap_lcd_config omap3_evm_lcd_config __initdata = {
+       .ctrl_name      = "internal",
+};
+
+static void ads7846_dev_init(void)
+{
+       if (gpio_request(OMAP3_EVM_TS_GPIO, "ADS7846 pendown") < 0)
+               printk(KERN_ERR "can't get ads7846 pen down GPIO\n");
+
+       gpio_direction_input(OMAP3_EVM_TS_GPIO);
+
+       omap_set_gpio_debounce(OMAP3_EVM_TS_GPIO, 1);
+       omap_set_gpio_debounce_time(OMAP3_EVM_TS_GPIO, 0xa);
+}
+
+static int ads7846_get_pendown_state(void)
+{
+       return !gpio_get_value(OMAP3_EVM_TS_GPIO);
+}
+
+struct ads7846_platform_data ads7846_config = {
+       .x_max                  = 0x0fff,
+       .y_max                  = 0x0fff,
+       .x_plate_ohms           = 180,
+       .pressure_max           = 255,
+       .debounce_max           = 10,
+       .debounce_tol           = 3,
+       .debounce_rep           = 1,
+       .get_pendown_state      = ads7846_get_pendown_state,
+       .keep_vref_on           = 1,
+       .settle_delay_usecs     = 150,
+};
+
+static struct omap2_mcspi_device_config ads7846_mcspi_config = {
+       .turbo_mode     = 0,
+       .single_channel = 1,  /* 0: slave, 1: master */
+};
+
+struct spi_board_info omap3evm_spi_board_info[] = {
+       [0] = {
+               .modalias               = "ads7846",
+               .bus_num                = 1,
+               .chip_select            = 0,
+               .max_speed_hz           = 1500000,
+               .controller_data        = &ads7846_mcspi_config,
+               .irq                    = OMAP_GPIO_IRQ(OMAP3_EVM_TS_GPIO),
+               .platform_data          = &ads7846_config,
+       },
+};
+
+static void __init omap3_evm_init_irq(void)
+{
+       omap2_init_common_hw(mt46h32m32lf6_sdrc_params);
+       omap_init_irq();
+       omap_gpio_init();
+       omap3evm_init_smc911x();
+}
+
+static struct omap_board_config_kernel omap3_evm_config[] __initdata = {
+       { OMAP_TAG_UART,        &omap3_evm_uart_config },
+       { OMAP_TAG_LCD,         &omap3_evm_lcd_config },
+};
+
+static struct platform_device *omap3_evm_devices[] __initdata = {
+       &omap3_evm_lcd_device,
+       &omap3evm_smc911x_device,
+};
+
+static void __init omap3_evm_init(void)
+{
+       omap3_evm_i2c_init();
+
+       platform_add_devices(omap3_evm_devices, ARRAY_SIZE(omap3_evm_devices));
+       omap_board_config = omap3_evm_config;
+       omap_board_config_size = ARRAY_SIZE(omap3_evm_config);
+
+       spi_register_board_info(omap3evm_spi_board_info,
+                               ARRAY_SIZE(omap3evm_spi_board_info));
+
+       omap_serial_init();
+       usb_musb_init();
+       usb_ehci_init();
+       omap3evm_flash_init();
+       ads7846_dev_init();
+}
+
+static void __init omap3_evm_map_io(void)
+{
+       omap2_set_globals_343x();
+       omap2_map_common_io();
+}
+
+MACHINE_START(OMAP3EVM, "OMAP3 EVM")
+       /* Maintainer: Syed Mohammed Khasim - Texas Instruments */
+       .phys_io        = 0x48000000,
+       .io_pg_offst    = ((0xd8000000) >> 18) & 0xfffc,
+       .boot_params    = 0x80000100,
+       .map_io         = omap3_evm_map_io,
+       .init_irq       = omap3_evm_init_irq,
+       .init_machine   = omap3_evm_init,
+       .timer          = &omap_timer,
+MACHINE_END
index 402f09c6cf1011da8ccd291b62decf5606b6f402..c67f62ff8850976a9da45820f6ed19493323b0ae 100644 (file)
  *
  */
 
  *
  */
 
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
 #include <linux/init.h>
 #include <linux/init.h>
+#include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 
 #include <linux/spi/ads7846.h>
 #include <linux/i2c/twl4030.h>
 
 #include <linux/spi/ads7846.h>
 #include <linux/i2c/twl4030.h>
 
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
 #include <asm/mach-types.h>
 #include <asm/mach/arch.h>
+#include <asm/mach/flash.h>
 #include <asm/mach/map.h>
 
 #include <mach/board.h>
 #include <mach/common.h>
 #include <mach/gpio.h>
 #include <asm/mach/map.h>
 
 #include <mach/board.h>
 #include <mach/common.h>
 #include <mach/gpio.h>
+#include <mach/gpmc.h>
 #include <mach/hardware.h>
 #include <mach/hardware.h>
-#include <mach/mcspi.h>
+#include <mach/nand.h>
 #include <mach/usb.h>
 #include <mach/usb.h>
+#include <mach/mcspi.h>
 
 
+#include "sdram-micron-mt46h32m32lf-6.h"
 #include "mmc-twl4030.h"
 
 #include "mmc-twl4030.h"
 
+
+#define NAND_BLOCK_SIZE                        SZ_128K
+#define GPMC_CS0_BASE                  0x60
+#define GPMC_CS_SIZE                   0x30
+
 #define OMAP3_PANDORA_TS_GPIO          94
 
 #define OMAP3_PANDORA_TS_GPIO          94
 
+static struct mtd_partition omap3pandora_nand_partitions[] = {
+       {
+               .name           = "xloader",
+               .offset         = 0,                    /* Offset = 0x00000 */
+               .size           = 4 * NAND_BLOCK_SIZE,
+               .mask_flags     = MTD_WRITEABLE
+       }, {
+               .name           = "uboot",
+               .offset         = MTDPART_OFS_APPEND,   /* Offset = 0x80000 */
+               .size           = 14 * NAND_BLOCK_SIZE,
+       }, {
+               .name           = "uboot environment",
+               .offset         = MTDPART_OFS_APPEND,   /* Offset = 0x240000 */
+               .size           = 2 * NAND_BLOCK_SIZE,
+       }, {
+               .name           = "linux",
+               .offset         = MTDPART_OFS_APPEND,   /* Offset = 0x280000 */
+               .size           = 32 * NAND_BLOCK_SIZE,
+       }, {
+               .name           = "rootfs",
+               .offset         = MTDPART_OFS_APPEND,   /* Offset = 0x680000 */
+               .size           = MTDPART_SIZ_FULL,
+       },
+};
+
+static struct omap_nand_platform_data omap3pandora_nand_data = {
+       .parts          = omap3pandora_nand_partitions,
+       .nr_parts       = ARRAY_SIZE(omap3pandora_nand_partitions),
+       .dma_channel    = -1,   /* disable DMA in OMAP NAND driver */
+};
+
+static struct resource omap3pandora_nand_resource[] = {
+       {
+               .flags          = IORESOURCE_MEM,
+       },
+};
+
+static struct platform_device omap3pandora_nand_device = {
+       .name           = "omap2-nand",
+       .id             = -1,
+       .dev            = {
+               .platform_data  = &omap3pandora_nand_data,
+       },
+       .num_resources  = ARRAY_SIZE(omap3pandora_nand_resource),
+       .resource       = omap3pandora_nand_resource,
+};
+
+static void __init omap3pandora_flash_init(void)
+{
+       u8 cs = 0;
+       u8 nandcs = GPMC_CS_NUM + 1;
+
+       u32 gpmc_base_add = OMAP34XX_GPMC_VIRT;
+
+       /* find out the chip-select on which NAND exists */
+       while (cs < GPMC_CS_NUM) {
+               u32 ret = 0;
+               ret = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1);
+
+               if ((ret & 0xC00) == 0x800) {
+                       printk(KERN_INFO "Found NAND on CS%d\n", cs);
+                       if (nandcs > GPMC_CS_NUM)
+                               nandcs = cs;
+               }
+               cs++;
+       }
+
+       if (nandcs > GPMC_CS_NUM) {
+               printk(KERN_INFO "NAND: Unable to find configuration "
+                                "in GPMC\n ");
+               return;
+       }
+
+       if (nandcs < GPMC_CS_NUM) {
+               omap3pandora_nand_data.cs = nandcs;
+               omap3pandora_nand_data.gpmc_cs_baseaddr = (void *)
+                       (gpmc_base_add + GPMC_CS0_BASE + nandcs * GPMC_CS_SIZE);
+               omap3pandora_nand_data.gpmc_baseaddr = (void *) (gpmc_base_add);
+
+               printk(KERN_INFO "Registering NAND on CS%d\n", nandcs);
+               if (platform_device_register(&omap3pandora_nand_device) < 0)
+                       printk(KERN_ERR "Unable to register NAND device\n");
+       }
+}
+
 static struct twl4030_hsmmc_info omap3pandora_mmc[] = {
        {
                .mmc            = 1,
 static struct twl4030_hsmmc_info omap3pandora_mmc[] = {
        {
                .mmc            = 1,
@@ -118,7 +220,7 @@ static int __init omap3pandora_i2c_init(void)
 
 static void __init omap3pandora_init_irq(void)
 {
 
 static void __init omap3pandora_init_irq(void)
 {
-       omap2_init_common_hw(NULL);
+       omap2_init_common_hw(mt46h32m32lf6_sdrc_params);
        omap_init_irq();
        omap_gpio_init();
 }
        omap_init_irq();
        omap_gpio_init();
 }
@@ -200,8 +302,10 @@ static void __init omap3pandora_init(void)
        omap_serial_init();
        spi_register_board_info(omap3pandora_spi_board_info,
                        ARRAY_SIZE(omap3pandora_spi_board_info));
        omap_serial_init();
        spi_register_board_info(omap3pandora_spi_board_info,
                        ARRAY_SIZE(omap3pandora_spi_board_info));
-       omap3pandora_ads7846_init();
        usb_musb_init();
        usb_musb_init();
+       usb_ehci_init();
+       omap3pandora_flash_init();
+       omap3pandora_ads7846_init();
 }
 
 static void __init omap3pandora_map_io(void)
 }
 
 static void __init omap3pandora_map_io(void)
index b1f23bea863fec71ff972b5c9aec8952222f9fc4..f357de9681c7308d43af80837f70c2c608a108e7 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 #include <linux/i2c/twl4030.h>
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 #include <linux/i2c/twl4030.h>
+#include <linux/regulator/machine.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
@@ -45,6 +46,8 @@
 #include <mach/nand.h>
 #include <mach/usb.h>
 
 #include <mach/nand.h>
 #include <mach/usb.h>
 
+#include "sdram-micron-mt46h32m32lf-6.h"
+#include "twl4030-generic-scripts.h"
 #include "mmc-twl4030.h"
 
 #define OVERO_GPIO_BT_XGATE    15
 #include "mmc-twl4030.h"
 
 #define OVERO_GPIO_BT_XGATE    15
@@ -57,8 +60,8 @@
 #define GPMC_CS0_BASE  0x60
 #define GPMC_CS_SIZE   0x30
 
 #define GPMC_CS0_BASE  0x60
 #define GPMC_CS_SIZE   0x30
 
-#define OVERO_SMSC911X_CS      5
-#define OVERO_SMSC911X_GPIO    176
+#define OVERO_SMSC911X_CS      5
+#define OVERO_SMSC911X_GPIO    176
 
 #if defined(CONFIG_TOUCHSCREEN_ADS7846) || \
        defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
 
 #if defined(CONFIG_TOUCHSCREEN_ADS7846) || \
        defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE)
@@ -180,6 +183,8 @@ static inline void __init overo_init_smsc911x(void)
 static inline void __init overo_init_smsc911x(void) { return; }
 #endif
 
 static inline void __init overo_init_smsc911x(void) { return; }
 #endif
 
+
+
 static struct mtd_partition overo_nand_partitions[] = {
        {
                .name           = "xloader",
 static struct mtd_partition overo_nand_partitions[] = {
        {
                .name           = "xloader",
@@ -271,21 +276,77 @@ static struct omap_uart_config overo_uart_config __initdata = {
        .enabled_uarts  = ((1 << 0) | (1 << 1) | (1 << 2)),
 };
 
        .enabled_uarts  = ((1 << 0) | (1 << 1) | (1 << 2)),
 };
 
+static struct twl4030_hsmmc_info mmc[] = {
+       {
+               .mmc            = 1,
+               .wires          = 4,
+               .gpio_cd        = -EINVAL,
+               .gpio_wp        = -EINVAL,
+       },
+       {
+               .mmc            = 2,
+               .wires          = 4,
+               .gpio_cd        = -EINVAL,
+               .gpio_wp        = -EINVAL,
+               .transceiver    = true,
+               .ocr_mask       = 0x00100000,   /* 3.3V */
+       },
+       {}      /* Terminator */
+};
+
+static struct regulator_consumer_supply overo_vmmc1_supply = {
+       .supply                 = "vmmc",
+};
+
+static int overo_twl_gpio_setup(struct device *dev,
+               unsigned gpio, unsigned ngpio)
+{
+       twl4030_mmc_init(mmc);
+
+       overo_vmmc1_supply.dev = mmc[0].dev;
+
+       return 0;
+}
+
 static struct twl4030_gpio_platform_data overo_gpio_data = {
        .gpio_base      = OMAP_MAX_GPIO_LINES,
        .irq_base       = TWL4030_GPIO_IRQ_BASE,
        .irq_end        = TWL4030_GPIO_IRQ_END,
 static struct twl4030_gpio_platform_data overo_gpio_data = {
        .gpio_base      = OMAP_MAX_GPIO_LINES,
        .irq_base       = TWL4030_GPIO_IRQ_BASE,
        .irq_end        = TWL4030_GPIO_IRQ_END,
+       .setup          = overo_twl_gpio_setup,
 };
 
 };
 
+static struct twl4030_usb_data overo_usb_data = {
+       .usb_mode       = T2_USB_MODE_ULPI,
+};
+
+static struct regulator_init_data overo_vmmc1 = {
+       .constraints = {
+               .min_uV                 = 1850000,
+               .max_uV                 = 3150000,
+               .valid_modes_mask       = REGULATOR_MODE_NORMAL
+                                       | REGULATOR_MODE_STANDBY,
+               .valid_ops_mask         = REGULATOR_CHANGE_VOLTAGE
+                                       | REGULATOR_CHANGE_MODE
+                                       | REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies  = 1,
+       .consumer_supplies      = &overo_vmmc1_supply,
+};
+
+/* mmc2 (WLAN) and Bluetooth don't use twl4030 regulators */
+
 static struct twl4030_platform_data overo_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
        .gpio           = &overo_gpio_data,
 static struct twl4030_platform_data overo_twldata = {
        .irq_base       = TWL4030_IRQ_BASE,
        .irq_end        = TWL4030_IRQ_END,
        .gpio           = &overo_gpio_data,
+       .usb            = &overo_usb_data,
+       .power          = GENERIC3430_T2SCRIPTS_DATA,
+       .vmmc1          = &overo_vmmc1,
 };
 
 static struct i2c_board_info __initdata overo_i2c_boardinfo[] = {
        {
 };
 
 static struct i2c_board_info __initdata overo_i2c_boardinfo[] = {
        {
-               I2C_BOARD_INFO("twl4030", 0x48),
+               I2C_BOARD_INFO("tps65950", 0x48),
                .flags = I2C_CLIENT_WAKE,
                .irq = INT_34XX_SYS_NIRQ,
                .platform_data = &overo_twldata,
                .flags = I2C_CLIENT_WAKE,
                .irq = INT_34XX_SYS_NIRQ,
                .platform_data = &overo_twldata,
@@ -303,7 +364,7 @@ static int __init overo_i2c_init(void)
 
 static void __init overo_init_irq(void)
 {
 
 static void __init overo_init_irq(void)
 {
-       omap2_init_common_hw(NULL);
+       omap2_init_common_hw(mt46h32m32lf6_sdrc_params);
        omap_init_irq();
        omap_gpio_init();
 }
        omap_init_irq();
        omap_gpio_init();
 }
@@ -326,23 +387,6 @@ static struct platform_device *overo_devices[] __initdata = {
        &overo_lcd_device,
 };
 
        &overo_lcd_device,
 };
 
-static struct twl4030_hsmmc_info mmc[] __initdata = {
-       {
-               .mmc            = 1,
-               .wires          = 4,
-               .gpio_cd        = -EINVAL,
-               .gpio_wp        = -EINVAL,
-       },
-       {
-               .mmc            = 2,
-               .wires          = 4,
-               .gpio_cd        = -EINVAL,
-               .gpio_wp        = -EINVAL,
-               .transceiver    = true,
-       },
-       {}      /* Terminator */
-};
-
 static void __init overo_init(void)
 {
        overo_i2c_init();
 static void __init overo_init(void)
 {
        overo_i2c_init();
@@ -350,9 +394,10 @@ static void __init overo_init(void)
        omap_board_config = overo_config;
        omap_board_config_size = ARRAY_SIZE(overo_config);
        omap_serial_init();
        omap_board_config = overo_config;
        omap_board_config_size = ARRAY_SIZE(overo_config);
        omap_serial_init();
-       twl4030_mmc_init(mmc);
-       overo_flash_init();
        usb_musb_init();
        usb_musb_init();
+       usb_ehci_init();
+       overo_flash_init();
+       overo_init_smsc911x();
        overo_ads7846_init();
        overo_init_smsc911x();
 
        overo_ads7846_init();
        overo_init_smsc911x();
 
diff --git a/arch/arm/mach-omap2/board-rx51-flash.c b/arch/arm/mach-omap2/board-rx51-flash.c
new file mode 100644 (file)
index 0000000..f3b7eaf
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * linux/arch/arm/mach-omap2/board-rx51-flash.c
+ *
+ * Copyright (C) 2008 Nokia
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <mach/board-rx51.h>
+
+extern void __init n800_flash_init(void);
+
+void __init rx51_flash_init(void)
+{
+       n800_flash_init();
+}
+
index a7381729645cf5c4deeaf23e895c21ee1306b613..22183aff1669fafb32d5f6a0dd5da8c42c208997 100644 (file)
 #include <linux/platform_device.h>
 #include <linux/input.h>
 #include <linux/spi/spi.h>
 #include <linux/platform_device.h>
 #include <linux/input.h>
 #include <linux/spi/spi.h>
+#include <linux/spi/tsc2005.h>
 #include <linux/i2c.h>
 #include <linux/i2c/twl4030.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/regulator/machine.h>
 #include <linux/i2c.h>
 #include <linux/i2c/twl4030.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/regulator/machine.h>
-#include <linux/gpio.h>
 
 #include <mach/mcspi.h>
 
 #include <mach/mcspi.h>
+#include <mach/gpio.h>
 #include <mach/mux.h>
 #include <mach/board.h>
 #include <mach/common.h>
 #include <mach/dma.h>
 #include <mach/gpmc.h>
 #include <mach/mux.h>
 #include <mach/board.h>
 #include <mach/common.h>
 #include <mach/dma.h>
 #include <mach/gpmc.h>
-#include <mach/keypad.h>
 
 #include "mmc-twl4030.h"
 
 
 #include "mmc-twl4030.h"
 
+#define SYSTEM_REV_B_USES_VAUX3 0x1699
+#define SYSTEM_REV_S_USES_VAUX3 0x8
 
 #define SMC91X_CS                      1
 #define SMC91X_GPIO_IRQ                        54
 #define SMC91X_GPIO_RESET              164
 #define SMC91X_GPIO_PWRDWN             86
 
 
 #define SMC91X_CS                      1
 #define SMC91X_GPIO_IRQ                        54
 #define SMC91X_GPIO_RESET              164
 #define SMC91X_GPIO_PWRDWN             86
 
+#define RX51_TSC2005_RESET_GPIO                104
+#define RX51_TSC2005_IRQ_GPIO          100
+
 static struct resource rx51_smc91x_resources[] = {
        [0] = {
                .flags          = IORESOURCE_MEM,
 static struct resource rx51_smc91x_resources[] = {
        [0] = {
                .flags          = IORESOURCE_MEM,
@@ -52,6 +57,38 @@ static struct platform_device rx51_smc91x_device = {
        .resource       = rx51_smc91x_resources,
 };
 
        .resource       = rx51_smc91x_resources,
 };
 
+static struct tsc2005_platform_data tsc2005_config = {
+       .reset_gpio             = RX51_TSC2005_RESET_GPIO, /* not used */
+
+       .ts_x_plate_ohm         = 280,
+       .ts_hw_avg              = 0,
+       .ts_touch_pressure      = 1500,
+       .ts_stab_time           = 1000,
+       .ts_pressure_max        = 2048,
+       .ts_pressure_fudge      = 2,
+       .ts_x_max               = 4096,
+       .ts_x_fudge             = 4,
+       .ts_y_max               = 4096,
+       .ts_y_fudge             = 7,
+};
+
+static struct omap2_mcspi_device_config tsc2005_mcspi_config = {
+       .turbo_mode     = 0,
+       .single_channel = 1,
+};
+
+static struct spi_board_info rx51_peripherals_spi_board_info[] = {
+       [0] = {
+               .modalias               = "tsc2005",
+               .bus_num                = 1,
+               .chip_select            = 0,
+               .irq                    = OMAP_GPIO_IRQ(RX51_TSC2005_IRQ_GPIO),
+               .max_speed_hz           = 6000000,
+               .controller_data        = &tsc2005_mcspi_config,
+               .platform_data          = &tsc2005_config,
+       },
+};
+
 static int rx51_keymap[] = {
        KEY(0, 0, KEY_Q),
        KEY(0, 1, KEY_W),
 static int rx51_keymap[] = {
        KEY(0, 0, KEY_Q),
        KEY(0, 1, KEY_W),
@@ -199,6 +236,18 @@ free1:
        printk(KERN_ERR "Could not initialize smc91x\n");
 }
 
        printk(KERN_ERR "Could not initialize smc91x\n");
 }
 
+static void __init rx51_init_tsc2005(void)
+{
+       int r;
+
+       r = gpio_request(RX51_TSC2005_IRQ_GPIO, "tsc2005 DAV IRQ");
+       if (r >= 0) {
+               gpio_direction_input(RX51_TSC2005_IRQ_GPIO);
+       } else {
+               printk(KERN_ERR "unable to get DAV GPIO");
+       }
+}
+
 static struct twl4030_madc_platform_data rx51_madc_data = {
        .irq_line               = 1,
 };
 static struct twl4030_madc_platform_data rx51_madc_data = {
        .irq_line               = 1,
 };
@@ -259,7 +308,7 @@ static struct regulator_init_data rx51_vaux2 = {
 };
 
 /* VAUX3 - adds more power to VIO_18 rail */
 };
 
 /* VAUX3 - adds more power to VIO_18 rail */
-static struct regulator_init_data rx51_vaux3 = {
+static struct regulator_init_data rx51_vaux3_cam = {
        .constraints = {
                .name                   = "VCAM_DIG_18",
                .min_uV                 = 1800000,
        .constraints = {
                .name                   = "VCAM_DIG_18",
                .min_uV                 = 1800000,
@@ -272,6 +321,22 @@ static struct regulator_init_data rx51_vaux3 = {
        },
 };
 
        },
 };
 
+static struct regulator_init_data rx51_vaux3_mmc = {
+       .constraints = {
+               .name                   = "VMMC2_30",
+               .min_uV                 = 2800000,
+               .max_uV                 = 3000000,
+               .apply_uV               = true,
+               .valid_modes_mask       = REGULATOR_MODE_NORMAL
+                                       | REGULATOR_MODE_STANDBY,
+               .valid_ops_mask         = REGULATOR_CHANGE_VOLTAGE
+                                       | REGULATOR_CHANGE_MODE
+                                       | REGULATOR_CHANGE_STATUS,
+       },
+       .num_consumer_supplies  = 1,
+       .consumer_supplies      = &rx51_vmmc2_supply,
+};
+
 static struct regulator_init_data rx51_vaux4 = {
        .constraints = {
                .name                   = "VCAM_ANA_28",
 static struct regulator_init_data rx51_vaux4 = {
        .constraints = {
                .name                   = "VCAM_ANA_28",
@@ -382,10 +447,8 @@ static struct twl4030_platform_data rx51_twldata = {
 
        .vaux1                  = &rx51_vaux1,
        .vaux2                  = &rx51_vaux2,
 
        .vaux1                  = &rx51_vaux1,
        .vaux2                  = &rx51_vaux2,
-       .vaux3                  = &rx51_vaux3,
        .vaux4                  = &rx51_vaux4,
        .vmmc1                  = &rx51_vmmc1,
        .vaux4                  = &rx51_vaux4,
        .vmmc1                  = &rx51_vmmc1,
-       .vmmc2                  = &rx51_vmmc2,
        .vsim                   = &rx51_vsim,
        .vdac                   = &rx51_vdac,
 };
        .vsim                   = &rx51_vsim,
        .vdac                   = &rx51_vdac,
 };
@@ -401,6 +464,13 @@ static struct i2c_board_info __initdata rx51_peripherals_i2c_board_info_1[] = {
 
 static int __init rx51_i2c_init(void)
 {
 
 static int __init rx51_i2c_init(void)
 {
+       if ((system_rev >= SYSTEM_REV_S_USES_VAUX3 && system_rev < 0x100) ||
+           system_rev >= SYSTEM_REV_B_USES_VAUX3)
+               rx51_twldata.vaux3 = &rx51_vaux3_mmc;
+       else {
+               rx51_twldata.vaux3 = &rx51_vaux3_cam;
+               rx51_twldata.vmmc2 = &rx51_vmmc2;
+       }
        omap_register_i2c_bus(1, 2600, rx51_peripherals_i2c_board_info_1,
                        ARRAY_SIZE(rx51_peripherals_i2c_board_info_1));
        omap_register_i2c_bus(2, 100, NULL, 0);
        omap_register_i2c_bus(1, 2600, rx51_peripherals_i2c_board_info_1,
                        ARRAY_SIZE(rx51_peripherals_i2c_board_info_1));
        omap_register_i2c_bus(2, 100, NULL, 0);
@@ -413,7 +483,10 @@ void __init rx51_peripherals_init(void)
 {
        platform_add_devices(rx51_peripherals_devices,
                                ARRAY_SIZE(rx51_peripherals_devices));
 {
        platform_add_devices(rx51_peripherals_devices,
                                ARRAY_SIZE(rx51_peripherals_devices));
+       spi_register_board_info(rx51_peripherals_spi_board_info,
+                               ARRAY_SIZE(rx51_peripherals_spi_board_info));
        rx51_i2c_init();
        rx51_init_smc91x();
        rx51_i2c_init();
        rx51_init_smc91x();
+       rx51_init_tsc2005();
 }
 
 }
 
diff --git a/arch/arm/mach-omap2/board-rx51-sdram.c b/arch/arm/mach-omap2/board-rx51-sdram.c
new file mode 100644 (file)
index 0000000..32b5da4
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * SDRC register values for the Samsung K4X1G323PC
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ *
+ * Original code by Juha Yrjölä <juha.yrjola@solidboot.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <mach/io.h>
+#include <mach/common.h>
+#include <mach/clock.h>
+#include <mach/sdrc.h>
+
+
+/* In picoseconds, except for tREF */
+struct sdram_timings {
+       u32 casl;
+       u32 tDAL;
+       u32 tDPL;
+       u32 tRRD;
+       u32 tRCD;
+       u32 tRP;
+       u32 tRAS;
+       u32 tRC;
+       u32 tRFC;
+       u32 tXSR;
+
+       u32 tREF; /* in ms */
+};
+
+struct sdram_info {
+       u8 row_lines;
+};
+
+
+struct omap_sdrc_params rx51_sdrc_params[2];
+
+static const struct sdram_timings rx51_timings[] = {
+       {
+               .casl = 3,
+               .tDAL = 15000 + 18000,
+               .tDPL = 15000,
+               .tRRD = 12000,
+               .tRCD = 18000,
+               .tRP = 18000,
+               .tRAS = 42000,
+               .tRC = 66000,
+               .tRFC = 97500,
+               .tXSR = 120000,
+
+               .tREF = 64,
+       },
+};
+
+static const struct sdram_info rx51_info = {
+       .row_lines = 13,
+};
+
+#define CM_BASE                    0x48004000
+
+#define CM_CLKSEL_CORE      0x0a40
+#define CM_CLKSEL1_PLL      0x0d40
+
+#define PRM_CLKSEL          0x48306d40
+#define PRM_CLKSRC_CTRL     0x48307270
+
+static u32 cm_base = CM_BASE;
+
+static inline u32 cm_read_reg(int idx)
+{
+       return *(u32 *)OMAP2_IO_ADDRESS(cm_base + idx);
+}
+
+static const unsigned long sys_clk_rate_table[] = {
+       12000, 13000, 19200, 26000, 38400, 16800
+};
+
+static unsigned long get_sys_clk_rate(void)
+{
+       unsigned long rate;
+
+       rate = sys_clk_rate_table[*(u32 *)OMAP2_IO_ADDRESS(PRM_CLKSEL) & 0x07];
+       if (((*(u32 *)OMAP2_IO_ADDRESS(PRM_CLKSRC_CTRL) >> 6) & 0x03) == 0x02)
+               rate /= 2;
+       return rate;
+}
+
+static unsigned long get_core_rate(void)
+{
+       unsigned long rate;
+       u32 l;
+
+       l = cm_read_reg(CM_CLKSEL1_PLL);
+       rate = get_sys_clk_rate();
+       rate *= ((l >> 16) & 0x7ff);
+       rate /= ((l >> 8) & 0x7f) + 1;
+       rate /= (l >> 27) & 0x1f;
+
+       return rate;
+}
+
+static unsigned long get_l3_rate(void)
+{
+       u32 l;
+
+       l = cm_read_reg(CM_CLKSEL_CORE);
+       return get_core_rate() / (l & 0x03);
+}
+
+
+
+static unsigned long sdrc_get_fclk_period(void)
+{
+       /* In picoseconds */
+       return 1000000000 / get_l3_rate();
+}
+
+static unsigned int sdrc_ps_to_ticks(unsigned int time_ps)
+{
+       unsigned long tick_ps;
+
+       /* Calculate in picosecs to yield more exact results */
+       tick_ps = sdrc_get_fclk_period();
+
+       return (time_ps + tick_ps - 1) / tick_ps;
+}
+#undef DEBUG
+#ifdef DEBUG
+static int set_sdrc_timing_regval(u32 *regval, int st_bit, int end_bit,
+                              int time, const char *name)
+#else
+static int set_sdrc_timing_regval(u32 *regval, int st_bit, int end_bit,
+                              int time)
+#endif
+{
+       int ticks, mask, nr_bits;
+
+       if (time == 0)
+               ticks = 0;
+       else
+               ticks = sdrc_ps_to_ticks(time);
+       nr_bits = end_bit - st_bit + 1;
+       if (ticks >= 1 << nr_bits)
+               return -1;
+       mask = (1 << nr_bits) - 1;
+       *regval &= ~(mask << st_bit);
+       *regval |= ticks << st_bit;
+#ifdef DEBUG
+       printk("SDRC %s: %i ticks %i ns\n", name, ticks,
+                       (unsigned int)sdrc_get_fclk_period() * ticks / 1000);
+#endif
+
+       return 0;
+}
+
+#ifdef DEBUG
+#define SDRC_SET_ONE(reg, st, end, field) \
+       if (set_sdrc_timing_regval((reg), (st), (end), rx51_timings->field, #field) < 0) \
+               err = -1
+#else
+#define SDRC_SET_ONE(reg, st, end, field) \
+       if (set_sdrc_timing_regval((reg), (st), (end), rx51_timings->field) < 0) \
+               err = -1
+#endif
+
+struct omap_sdrc_params *rx51_get_sdram_timings(void)
+{
+       u32 ticks_per_ms;
+       u32 rfr, l;
+       u32 actim_ctrla, actim_ctrlb;
+       u32 rfr_ctrl;
+       int err = 0;
+
+       SDRC_SET_ONE(&actim_ctrla,  0,  4, tDAL);
+       SDRC_SET_ONE(&actim_ctrla,  6,  8, tDPL);
+       SDRC_SET_ONE(&actim_ctrla,  9, 11, tRRD);
+       SDRC_SET_ONE(&actim_ctrla, 12, 14, tRCD);
+       SDRC_SET_ONE(&actim_ctrla, 15, 17, tRP);
+       SDRC_SET_ONE(&actim_ctrla, 18, 21, tRAS);
+       SDRC_SET_ONE(&actim_ctrla, 22, 26, tRC);
+       SDRC_SET_ONE(&actim_ctrla, 27, 31, tRFC);
+
+       SDRC_SET_ONE(&actim_ctrlb,  0,  7, tXSR);
+
+       ticks_per_ms = sdrc_ps_to_ticks(1000000000);
+       rfr = rx51_timings[0].tREF * ticks_per_ms / (1 << rx51_info.row_lines);
+       if (rfr > 65535 + 50)
+               rfr = 65535;
+       else
+               rfr -= 50;
+
+       l = rfr << 8;
+       rfr_ctrl = l | 0x3; /* autorefresh, reload counter with 8xARCV */
+
+       rx51_sdrc_params[0].rate = 133333333;
+       rx51_sdrc_params[0].actim_ctrla = actim_ctrla;
+       rx51_sdrc_params[0].actim_ctrlb = actim_ctrlb;
+       rx51_sdrc_params[0].rfr_ctrl = rfr_ctrl;
+       rx51_sdrc_params[0].mr = 0x32;
+
+       rx51_sdrc_params[1].rate = 0;
+
+       if (err < 0)
+               return NULL;
+
+       return &rx51_sdrc_params[0];
+}
+
diff --git a/arch/arm/mach-omap2/board-rx51-video.c b/arch/arm/mach-omap2/board-rx51-video.c
new file mode 100644 (file)
index 0000000..6ebdc82
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * linux/arch/arm/mach-omap2/board-rx51-video.c
+ *
+ * Copyright (C) 2008 Nokia
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+
+#include <mach/lcd_mipid.h>
+#include <mach/mcspi.h>
+#include <mach/gpio.h>
+#include <mach/board.h>
+
+
+static struct omap2_mcspi_device_config mipid_mcspi_config = {
+       .turbo_mode     = 0,
+       .single_channel = 1,
+};
+
+static struct platform_device rx51_lcd_device = {
+       .name           = "lcd_mipid",
+       .id             = -1,
+};
+
+static void mipid_shutdown(struct mipid_platform_data *pdata)
+{
+       if (pdata->nreset_gpio != -1) {
+               pr_info("shutdown LCD\n");
+               gpio_direction_output(pdata->nreset_gpio, 0);
+               msleep(120);
+       }
+}
+
+static struct mipid_platform_data rx51_mipid_platform_data = {
+       .shutdown = mipid_shutdown,
+};
+
+static void __init mipid_dev_init(void)
+{
+       const struct omap_lcd_config *conf;
+
+       conf = omap_get_config(OMAP_TAG_LCD, struct omap_lcd_config);
+       if (conf != NULL) {
+               rx51_mipid_platform_data.nreset_gpio = conf->nreset_gpio;
+               rx51_mipid_platform_data.data_lines = conf->data_lines;
+       }
+}
+
+static struct spi_board_info rx51_video_spi_board_info[] = {
+       [0] = {
+               .modalias               = "lcd_mipid",
+               .bus_num                = 1,
+               .chip_select            = 2,
+               .max_speed_hz           = 6000000,
+               .controller_data        = &mipid_mcspi_config,
+               .platform_data          = &rx51_mipid_platform_data,
+       },
+};
+
+static struct platform_device *rx51_video_devices[] = {
+       &rx51_lcd_device,
+};
+
+void __init rx51_video_init(void)
+{
+       platform_add_devices(rx51_video_devices, ARRAY_SIZE(rx51_video_devices));
+       spi_register_board_info(rx51_video_spi_board_info,
+                       ARRAY_SIZE(rx51_video_spi_board_info));
+       mipid_dev_init();
+}
+
index 3a0daac6c8396740e10eca560514989c902596a6..768f507a569fb822e9d59cc7da6d43581d1eb298 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/delay.h>
-#include <linux/gpio.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
@@ -24,6 +23,7 @@
 #include <asm/mach/map.h>
 
 #include <mach/mcspi.h>
 #include <asm/mach/map.h>
 
 #include <mach/mcspi.h>
+#include <mach/gpio.h>
 #include <mach/mux.h>
 #include <mach/board.h>
 #include <mach/common.h>
 #include <mach/mux.h>
 #include <mach/board.h>
 #include <mach/common.h>
@@ -31,6 +31,7 @@
 #include <mach/dma.h>
 #include <mach/gpmc.h>
 #include <mach/usb.h>
 #include <mach/dma.h>
 #include <mach/gpmc.h>
 #include <mach/usb.h>
+#include <mach/board-rx51.h>
 
 static struct omap_uart_config rx51_uart_config = {
        .enabled_uarts  = ((1 << 0) | (1 << 1) | (1 << 2)),
 
 static struct omap_uart_config rx51_uart_config = {
        .enabled_uarts  = ((1 << 0) | (1 << 1) | (1 << 2)),
@@ -62,12 +63,14 @@ static struct omap_board_config_kernel rx51_config[] = {
 
 static void __init rx51_init_irq(void)
 {
 
 static void __init rx51_init_irq(void)
 {
-       omap2_init_common_hw(NULL);
+       omap2_init_common_hw(rx51_get_sdram_timings());
        omap_init_irq();
        omap_gpio_init();
 }
 
        omap_init_irq();
        omap_gpio_init();
 }
 
+extern void __init rx51_flash_init(void);
 extern void __init rx51_peripherals_init(void);
 extern void __init rx51_peripherals_init(void);
+extern void __init rx51_video_init(void);
 
 static void __init rx51_init(void)
 {
 
 static void __init rx51_init(void)
 {
@@ -75,7 +78,9 @@ static void __init rx51_init(void)
        omap_board_config_size = ARRAY_SIZE(rx51_config);
        omap_serial_init();
        usb_musb_init();
        omap_board_config_size = ARRAY_SIZE(rx51_config);
        omap_serial_init();
        usb_musb_init();
+       rx51_flash_init();
        rx51_peripherals_init();
        rx51_peripherals_init();
+       rx51_video_init();
 }
 
 static void __init rx51_map_io(void)
 }
 
 static void __init rx51_map_io(void)
index d6b4b2f8722fac7cbafa7a01b6367f5862802bc0..894cc355818afacdb0119796cffd76d793972075 100644 (file)
@@ -25,7 +25,6 @@
 #include <mach/board.h>
 #include <mach/mux.h>
 #include <mach/gpio.h>
 #include <mach/board.h>
 #include <mach/mux.h>
 #include <mach/gpio.h>
-#include <mach/eac.h>
 #include <mach/mmc.h>
 
 #if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE)
 #include <mach/mmc.h>
 
 #if defined(CONFIG_VIDEO_OMAP2) || defined(CONFIG_VIDEO_OMAP2_MODULE)
@@ -355,10 +354,12 @@ static void omap_init_mcspi(void)
        platform_device_register(&omap2_mcspi1);
        platform_device_register(&omap2_mcspi2);
 #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3)
        platform_device_register(&omap2_mcspi1);
        platform_device_register(&omap2_mcspi2);
 #if defined(CONFIG_ARCH_OMAP2430) || defined(CONFIG_ARCH_OMAP3)
-       platform_device_register(&omap2_mcspi3);
+       if (cpu_is_omap2430() || cpu_is_omap343x())
+               platform_device_register(&omap2_mcspi3);
 #endif
 #ifdef CONFIG_ARCH_OMAP3
 #endif
 #ifdef CONFIG_ARCH_OMAP3
-       platform_device_register(&omap2_mcspi4);
+       if (cpu_is_omap343x())
+               platform_device_register(&omap2_mcspi4);
 #endif
 }
 
 #endif
 }
 
@@ -366,38 +367,6 @@ static void omap_init_mcspi(void)
 static inline void omap_init_mcspi(void) {}
 #endif
 
 static inline void omap_init_mcspi(void) {}
 #endif
 
-#ifdef CONFIG_SND_OMAP24XX_EAC
-
-#define OMAP2_EAC_BASE                 0x48090000
-
-static struct resource omap2_eac_resources[] = {
-       {
-               .start          = OMAP2_EAC_BASE,
-               .end            = OMAP2_EAC_BASE + 0x109,
-               .flags          = IORESOURCE_MEM,
-       },
-};
-
-static struct platform_device omap2_eac_device = {
-       .name           = "omap24xx-eac",
-       .id             = -1,
-       .num_resources  = ARRAY_SIZE(omap2_eac_resources),
-       .resource       = omap2_eac_resources,
-       .dev = {
-               .platform_data = NULL,
-       },
-};
-
-void omap_init_eac(struct eac_platform_data *pdata)
-{
-       omap2_eac_device.dev.platform_data = pdata;
-       platform_device_register(&omap2_eac_device);
-}
-
-#else
-void omap_init_eac(struct eac_platform_data *pdata) {}
-#endif
-
 #ifdef CONFIG_OMAP_SHA1_MD5
 static struct resource sha1_md5_resources[] = {
        {
 #ifdef CONFIG_OMAP_SHA1_MD5
 static struct resource sha1_md5_resources[] = {
        {
index 34b5914e0f8b18d094d45a31e9ce166af215b410..32ceaa6582408e36e96ba968e0cbe116d15a220f 100644 (file)
@@ -45,6 +45,28 @@ int omap_chip_is(struct omap_chip_id oci)
 }
 EXPORT_SYMBOL(omap_chip_is);
 
 }
 EXPORT_SYMBOL(omap_chip_is);
 
+int omap_type(void)
+{
+       u32 val = 0;
+
+       if (cpu_is_omap24xx()) {
+               val = omap_ctrl_readl(OMAP24XX_CONTROL_STATUS);
+       } else if (cpu_is_omap34xx()) {
+               val = omap_ctrl_readl(OMAP343X_CONTROL_STATUS);
+       } else {
+               pr_err("Cannot detect omap type!\n");
+               goto out;
+       }
+
+       val &= OMAP2_DEVICETYPE_MASK;
+       val >>= 8;
+
+out:
+       return val;
+}
+EXPORT_SYMBOL(omap_type);
+
+
 /*----------------------------------------------------------------------------*/
 
 #define OMAP_TAP_IDCODE                0x0204
 /*----------------------------------------------------------------------------*/
 
 #define OMAP_TAP_IDCODE                0x0204
index 9ba20d985dda252f5a38bb54f478d543454e4599..998c5c45587ea31ac1c527aaff399c717907c3a4 100644 (file)
@@ -73,9 +73,9 @@ static int omap_check_spurious(unsigned int irq)
        u32 sir, spurious;
 
        sir = intc_bank_read_reg(&irq_banks[0], INTC_SIR);
        u32 sir, spurious;
 
        sir = intc_bank_read_reg(&irq_banks[0], INTC_SIR);
-       spurious = sir >> 6;
+       spurious = sir >> 7;
 
 
-       if (spurious > 1) {
+       if (spurious) {
                printk(KERN_WARNING "Spurious irq %i: 0x%08x, please flush "
                                        "posted write for irq %i\n",
                                        irq, sir, previous_irq);
                printk(KERN_WARNING "Spurious irq %i: 0x%08x, please flush "
                                        "posted write for irq %i\n",
                                        irq, sir, previous_irq);
index dc40b3e72206080164b1c1f5b17def1f4d211688..46d6e9bcadfa5c495a9c9ab026b0a260bf922cb1 100644 (file)
@@ -16,8 +16,8 @@
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
-#include <linux/i2c/twl4030.h>
-#include <linux/regulator/machine.h>
+#include <linux/mmc/host.h>
+#include <linux/regulator/consumer.h>
 
 #include <mach/hardware.h>
 #include <mach/control.h>
 
 #include <mach/hardware.h>
 #include <mach/control.h>
 
 #include "mmc-twl4030.h"
 
 
 #include "mmc-twl4030.h"
 
-#if defined(CONFIG_TWL4030_CORE) && \
-       (defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE))
 
 
-#define LDO_CLR                        0x00
-#define VSEL_S2_CLR            0x40
-
-#define VMMC1_DEV_GRP          0x27
-#define VMMC1_CLR              0x00
-#define VMMC1_315V             0x03
-#define VMMC1_300V             0x02
-#define VMMC1_285V             0x01
-#define VMMC1_185V             0x00
-#define VMMC1_DEDICATED                0x2A
-
-#define VMMC2_DEV_GRP          0x2B
-#define VMMC2_CLR              0x40
-#define VMMC2_315V             0x0c
-#define VMMC2_300V             0x0b
-#define VMMC2_285V             0x0a
-#define VMMC2_280V             0x09
-#define VMMC2_260V             0x08
-#define VMMC2_185V             0x06
-#define VMMC2_DEDICATED                0x2E
-
-#define VMMC_DEV_GRP_P1                0x20
+#if defined(CONFIG_REGULATOR) && \
+       (defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE))
 
 static u16 control_pbias_offset;
 static u16 control_devconf1_offset;
 
 static u16 control_pbias_offset;
 static u16 control_devconf1_offset;
@@ -59,19 +37,16 @@ static u16 control_devconf1_offset;
 
 static struct twl_mmc_controller {
        struct omap_mmc_platform_data   *mmc;
 
 static struct twl_mmc_controller {
        struct omap_mmc_platform_data   *mmc;
-       u8              twl_vmmc_dev_grp;
-       u8              twl_mmc_dedicated;
-       char            name[HSMMC_NAME_LEN + 1];
-} hsmmc[OMAP34XX_NR_MMC] = {
-       {
-               .twl_vmmc_dev_grp               = VMMC1_DEV_GRP,
-               .twl_mmc_dedicated              = VMMC1_DEDICATED,
-       },
-       {
-               .twl_vmmc_dev_grp               = VMMC2_DEV_GRP,
-               .twl_mmc_dedicated              = VMMC2_DEDICATED,
-       },
-};
+       /* Vcc == configured supply
+        * Vcc_alt == optional
+        *   -  MMC1, supply for DAT4..DAT7
+        *   -  MMC2/MMC2, external level shifter voltage supply, for
+        *      chip (SDIO, eMMC, etc) or transceiver (MMC2 only)
+        */
+       struct regulator                *vcc;
+       struct regulator                *vcc_aux;
+       char                            name[HSMMC_NAME_LEN + 1];
+} hsmmc[OMAP34XX_NR_MMC];
 
 static int twl_mmc_card_detect(int irq)
 {
 
 static int twl_mmc_card_detect(int irq)
 {
@@ -117,16 +92,60 @@ static int twl_mmc_late_init(struct device *dev)
        int ret = 0;
        int i;
 
        int ret = 0;
        int i;
 
-       ret = gpio_request(mmc->slots[0].switch_pin, "mmc_cd");
-       if (ret)
-               goto done;
-       ret = gpio_direction_input(mmc->slots[0].switch_pin);
-       if (ret)
-               goto err;
+       /* MMC/SD/SDIO doesn't require a card detect switch */
+       if (gpio_is_valid(mmc->slots[0].switch_pin)) {
+               ret = gpio_request(mmc->slots[0].switch_pin, "mmc_cd");
+               if (ret)
+                       goto done;
+               ret = gpio_direction_input(mmc->slots[0].switch_pin);
+               if (ret)
+                       goto err;
+       }
 
 
+       /* require at least main regulator */
        for (i = 0; i < ARRAY_SIZE(hsmmc); i++) {
                if (hsmmc[i].name == mmc->slots[0].name) {
        for (i = 0; i < ARRAY_SIZE(hsmmc); i++) {
                if (hsmmc[i].name == mmc->slots[0].name) {
+                       struct regulator *reg;
+
                        hsmmc[i].mmc = mmc;
                        hsmmc[i].mmc = mmc;
+
+                       reg = regulator_get(dev, "vmmc");
+                       if (IS_ERR(reg)) {
+                               dev_dbg(dev, "vmmc regulator missing\n");
+                               /* HACK: until fixed.c regulator is usable,
+                                * we don't require a main regulator
+                                * for MMC2 or MMC3
+                                */
+                               if (i != 0)
+                                       break;
+                               ret = PTR_ERR(reg);
+                               goto err;
+                       }
+                       hsmmc[i].vcc = reg;
+                       mmc->slots[0].ocr_mask = mmc_regulator_get_ocrmask(reg);
+
+                       /* allow an aux regulator */
+                       reg = regulator_get(dev, "vmmc_aux");
+                       hsmmc[i].vcc_aux = IS_ERR(reg) ? NULL : reg;
+
+                       /* UGLY HACK:  workaround regulator framework bugs.
+                        * When the bootloader leaves a supply active, it's
+                        * initialized with zero usecount ... and we can't
+                        * disable it without first disabling it.  Until the
+                        * framework is fixed, we need a workaround like this
+                        * (which is safe for MMC, but not in general).
+                        */
+                       if (regulator_is_enabled(hsmmc[i].vcc) > 0) {
+                               regulator_enable(hsmmc[i].vcc);
+                               regulator_disable(hsmmc[i].vcc);
+                       }
+                       if (hsmmc[i].vcc_aux) {
+                               if (regulator_is_enabled(reg) > 0) {
+                                       regulator_enable(reg);
+                                       regulator_disable(reg);
+                               }
+                       }
+
                        break;
                }
        }
                        break;
                }
        }
@@ -173,96 +192,6 @@ static int twl_mmc_resume(struct device *dev, int slot)
 #define twl_mmc_resume NULL
 #endif
 
 #define twl_mmc_resume NULL
 #endif
 
-/*
- * Sets the MMC voltage in twl4030
- */
-
-#define MMC1_OCR       (MMC_VDD_165_195 \
-               |MMC_VDD_28_29|MMC_VDD_29_30|MMC_VDD_30_31|MMC_VDD_31_32)
-#define MMC2_OCR       (MMC_VDD_165_195 \
-               |MMC_VDD_25_26|MMC_VDD_26_27|MMC_VDD_27_28 \
-               |MMC_VDD_28_29|MMC_VDD_29_30|MMC_VDD_30_31|MMC_VDD_31_32)
-
-static int twl_mmc_set_voltage(struct twl_mmc_controller *c, int vdd)
-{
-       int ret;
-       u8 vmmc = 0, dev_grp_val;
-
-       if (!vdd)
-               goto doit;
-
-       if (c->twl_vmmc_dev_grp == VMMC1_DEV_GRP) {
-               /* VMMC1:  max 220 mA.  And for 8-bit mode,
-                * VSIM:  max 50 mA
-                */
-               switch (1 << vdd) {
-               case MMC_VDD_165_195:
-                       vmmc = VMMC1_185V;
-                       /* and VSIM_180V */
-                       break;
-               case MMC_VDD_28_29:
-                       vmmc = VMMC1_285V;
-                       /* and VSIM_280V */
-                       break;
-               case MMC_VDD_29_30:
-               case MMC_VDD_30_31:
-                       vmmc = VMMC1_300V;
-                       /* and VSIM_300V */
-                       break;
-               case MMC_VDD_31_32:
-                       vmmc = VMMC1_315V;
-                       /* error if VSIM needed */
-                       break;
-               default:
-                       return -EINVAL;
-               }
-       } else if (c->twl_vmmc_dev_grp == VMMC2_DEV_GRP) {
-               /* VMMC2:  max 100 mA */
-               switch (1 << vdd) {
-               case MMC_VDD_165_195:
-                       vmmc = VMMC2_185V;
-                       break;
-               case MMC_VDD_25_26:
-               case MMC_VDD_26_27:
-                       vmmc = VMMC2_260V;
-                       break;
-               case MMC_VDD_27_28:
-                       vmmc = VMMC2_280V;
-                       break;
-               case MMC_VDD_28_29:
-                       vmmc = VMMC2_285V;
-                       break;
-               case MMC_VDD_29_30:
-               case MMC_VDD_30_31:
-                       vmmc = VMMC2_300V;
-                       break;
-               case MMC_VDD_31_32:
-                       vmmc = VMMC2_315V;
-                       break;
-               default:
-                       return -EINVAL;
-               }
-       } else {
-               return -EINVAL;
-       }
-
-doit:
-       if (vdd)
-               dev_grp_val = VMMC_DEV_GRP_P1;  /* Power up */
-       else
-               dev_grp_val = LDO_CLR;          /* Power down */
-
-       ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
-                                       dev_grp_val, c->twl_vmmc_dev_grp);
-       if (ret || !vdd)
-               return ret;
-
-       ret = twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
-                                       vmmc, c->twl_mmc_dedicated);
-
-       return ret;
-}
-
 static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
                                int vdd)
 {
 static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
                                int vdd)
 {
@@ -273,11 +202,13 @@ static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
 
        /*
         * Assume we power both OMAP VMMC1 (for CMD, CLK, DAT0..3) and the
 
        /*
         * Assume we power both OMAP VMMC1 (for CMD, CLK, DAT0..3) and the
-        * card using the same TWL VMMC1 supply (hsmmc[0]); OMAP has both
+        * card with Vcc regulator (from twl4030 or whatever).  OMAP has both
         * 1.8V and 3.0V modes, controlled by the PBIAS register.
         *
         * In 8-bit modes, OMAP VMMC1A (for DAT4..7) needs a supply, which
         * is most naturally TWL VSIM; those pins also use PBIAS.
         * 1.8V and 3.0V modes, controlled by the PBIAS register.
         *
         * In 8-bit modes, OMAP VMMC1A (for DAT4..7) needs a supply, which
         * is most naturally TWL VSIM; those pins also use PBIAS.
+        *
+        * FIXME handle VMMC1A as needed ...
         */
        if (power_on) {
                if (cpu_is_omap2430()) {
         */
        if (power_on) {
                if (cpu_is_omap2430()) {
@@ -300,7 +231,7 @@ static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
                reg &= ~OMAP2_PBIASLITEPWRDNZ0;
                omap_ctrl_writel(reg, control_pbias_offset);
 
                reg &= ~OMAP2_PBIASLITEPWRDNZ0;
                omap_ctrl_writel(reg, control_pbias_offset);
 
-               ret = twl_mmc_set_voltage(c, vdd);
+               ret = mmc_regulator_set_ocr(c->vcc, vdd);
 
                /* 100ms delay required for PBIAS configuration */
                msleep(100);
 
                /* 100ms delay required for PBIAS configuration */
                msleep(100);
@@ -316,7 +247,7 @@ static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
                reg &= ~OMAP2_PBIASLITEPWRDNZ0;
                omap_ctrl_writel(reg, control_pbias_offset);
 
                reg &= ~OMAP2_PBIASLITEPWRDNZ0;
                omap_ctrl_writel(reg, control_pbias_offset);
 
-               ret = twl_mmc_set_voltage(c, 0);
+               ret = mmc_regulator_set_ocr(c->vcc, 0);
 
                /* 100ms delay required for PBIAS configuration */
                msleep(100);
 
                /* 100ms delay required for PBIAS configuration */
                msleep(100);
@@ -329,19 +260,33 @@ static int twl_mmc1_set_power(struct device *dev, int slot, int power_on,
        return ret;
 }
 
        return ret;
 }
 
-static int twl_mmc2_set_power(struct device *dev, int slot, int power_on, int vdd)
+static int twl_mmc23_set_power(struct device *dev, int slot, int power_on, int vdd)
 {
 {
-       int ret;
+       int ret = 0;
        struct twl_mmc_controller *c = &hsmmc[1];
        struct omap_mmc_platform_data *mmc = dev->platform_data;
 
        struct twl_mmc_controller *c = &hsmmc[1];
        struct omap_mmc_platform_data *mmc = dev->platform_data;
 
+       /* If we don't see a Vcc regulator, assume it's a fixed
+        * voltage always-on regulator.
+        */
+       if (!c->vcc)
+               return 0;
+
        /*
        /*
-        * Assume TWL VMMC2 (hsmmc[1]) is used only to power the card ... OMAP
+        * Assume Vcc regulator is used only to power the card ... OMAP
         * VDDS is used to power the pins, optionally with a transceiver to
         * support cards using voltages other than VDDS (1.8V nominal).  When a
         * transceiver is used, DAT3..7 are muxed as transceiver control pins.
         * VDDS is used to power the pins, optionally with a transceiver to
         * support cards using voltages other than VDDS (1.8V nominal).  When a
         * transceiver is used, DAT3..7 are muxed as transceiver control pins.
+        *
+        * In some cases this regulator won't support enable/disable;
+        * e.g. it's a fixed rail for a WLAN chip.
+        *
+        * In other cases vcc_aux switches interface power.  Example, for
+        * eMMC cards it represents VccQ.  Sometimes transceivers or SDIO
+        * chips/cards need an interface voltage rail too.
         */
        if (power_on) {
         */
        if (power_on) {
+               /* only MMC2 supports a CLKIN */
                if (mmc->slots[0].internal_clock) {
                        u32 reg;
 
                if (mmc->slots[0].internal_clock) {
                        u32 reg;
 
@@ -349,24 +294,23 @@ static int twl_mmc2_set_power(struct device *dev, int slot, int power_on, int vd
                        reg |= OMAP2_MMCSDIO2ADPCLKISEL;
                        omap_ctrl_writel(reg, control_devconf1_offset);
                }
                        reg |= OMAP2_MMCSDIO2ADPCLKISEL;
                        omap_ctrl_writel(reg, control_devconf1_offset);
                }
-               ret = twl_mmc_set_voltage(c, vdd);
+               ret = mmc_regulator_set_ocr(c->vcc, vdd);
+               /* enable interface voltage rail, if needed */
+               if (ret == 0 && c->vcc_aux) {
+                       ret = regulator_enable(c->vcc_aux);
+                       if (ret < 0)
+                               ret = mmc_regulator_set_ocr(c->vcc, 0);
+               }
        } else {
        } else {
-               ret = twl_mmc_set_voltage(c, 0);
+               if (c->vcc_aux && (ret = regulator_is_enabled(c->vcc_aux)) > 0)
+                       ret = regulator_disable(c->vcc_aux);
+               if (ret == 0)
+                       ret = mmc_regulator_set_ocr(c->vcc, 0);
        }
 
        return ret;
 }
 
        }
 
        return ret;
 }
 
-static int twl_mmc3_set_power(struct device *dev, int slot, int power_on,
-               int vdd)
-{
-       /*
-        * Assume MMC3 has self-powered device connected, for example on-board
-        * chip with external power source.
-        */
-       return 0;
-}
-
 static struct omap_mmc_platform_data *hsmmc_data[OMAP34XX_NR_MMC] __initdata;
 
 void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
 static struct omap_mmc_platform_data *hsmmc_data[OMAP34XX_NR_MMC] __initdata;
 
 void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
@@ -405,17 +349,16 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
                if (c->name)
                        strncpy(twl->name, c->name, HSMMC_NAME_LEN);
                else
                if (c->name)
                        strncpy(twl->name, c->name, HSMMC_NAME_LEN);
                else
-                       snprintf(twl->name, ARRAY_SIZE(twl->name),
-                               "mmc%islot%i", c->mmc, 1);
+                       sprintf(twl->name, "mmc%islot%i", c->mmc, 1);
                mmc->slots[0].name = twl->name;
                mmc->nr_slots = 1;
                mmc->slots[0].wires = c->wires;
                mmc->slots[0].internal_clock = !c->ext_clock;
                mmc->dma_mask = 0xffffffff;
                mmc->slots[0].name = twl->name;
                mmc->nr_slots = 1;
                mmc->slots[0].wires = c->wires;
                mmc->slots[0].internal_clock = !c->ext_clock;
                mmc->dma_mask = 0xffffffff;
+               mmc->init = twl_mmc_late_init;
 
 
-               /* note: twl4030 card detect GPIOs normally switch VMMCx ... */
+               /* note: twl4030 card detect GPIOs can disable VMMCx ... */
                if (gpio_is_valid(c->gpio_cd)) {
                if (gpio_is_valid(c->gpio_cd)) {
-                       mmc->init = twl_mmc_late_init;
                        mmc->cleanup = twl_mmc_cleanup;
                        mmc->suspend = twl_mmc_suspend;
                        mmc->resume = twl_mmc_resume;
                        mmc->cleanup = twl_mmc_cleanup;
                        mmc->suspend = twl_mmc_suspend;
                        mmc->resume = twl_mmc_resume;
@@ -439,26 +382,28 @@ void __init twl4030_mmc_init(struct twl4030_hsmmc_info *controllers)
                } else
                        mmc->slots[0].gpio_wp = -EINVAL;
 
                } else
                        mmc->slots[0].gpio_wp = -EINVAL;
 
-               /* NOTE:  we assume OMAP's MMC1 and MMC2 use
-                * the TWL4030's VMMC1 and VMMC2, respectively;
-                * and that MMC3 device has it's own power source.
+               /* NOTE:  MMC slots should have a Vcc regulator set up.
+                * This may be from a TWL4030-family chip, another
+                * controllable regulator, or a fixed supply.
+                *
+                * temporary HACK: ocr_mask instead of fixed supply
                 */
                 */
+               mmc->slots[0].ocr_mask = c->ocr_mask;
 
                switch (c->mmc) {
                case 1:
 
                switch (c->mmc) {
                case 1:
+                       /* on-chip level shifting via PBIAS0/PBIAS1 */
                        mmc->slots[0].set_power = twl_mmc1_set_power;
                        mmc->slots[0].set_power = twl_mmc1_set_power;
-                       mmc->slots[0].ocr_mask = MMC1_OCR;
                        break;
                case 2:
                        break;
                case 2:
-                       mmc->slots[0].set_power = twl_mmc2_set_power;
-                       if (c->transceiver)
-                               mmc->slots[0].ocr_mask = MMC2_OCR;
-                       else
-                               mmc->slots[0].ocr_mask = MMC_VDD_165_195;
-                       break;
+                       if (c->ext_clock)
+                               c->transceiver = 1;
+                       if (c->transceiver && c->wires > 4)
+                               c->wires = 4;
+                       /* FALLTHROUGH */
                case 3:
                case 3:
-                       mmc->slots[0].set_power = twl_mmc3_set_power;
-                       mmc->slots[0].ocr_mask = MMC_VDD_165_195;
+                       /* off-chip level shifting, or none */
+                       mmc->slots[0].set_power = twl_mmc23_set_power;
                        break;
                default:
                        pr_err("MMC%d configuration not supported!\n", c->mmc);
                        break;
                default:
                        pr_err("MMC%d configuration not supported!\n", c->mmc);
index ea59e8624290a1d8c1fb5b2f002a69beebd0cd2b..3807c45c9a6ca4d8dc0a4534da1276af8a069ce3 100644 (file)
@@ -16,9 +16,10 @@ struct twl4030_hsmmc_info {
        int     gpio_wp;        /* or -EINVAL */
        char    *name;          /* or NULL for default */
        struct device *dev;     /* returned: pointer to mmc adapter */
        int     gpio_wp;        /* or -EINVAL */
        char    *name;          /* or NULL for default */
        struct device *dev;     /* returned: pointer to mmc adapter */
+       int     ocr_mask;       /* temporary HACK */
 };
 
 };
 
-#if    defined(CONFIG_TWL4030_CORE) && \
+#if defined(CONFIG_REGULATOR) && \
        (defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \
         defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE))
 
        (defined(CONFIG_MMC_OMAP) || defined(CONFIG_MMC_OMAP_MODULE) || \
         defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE))
 
diff --git a/arch/arm/mach-omap2/sdram-micron-mt46h32m32lf-6.h b/arch/arm/mach-omap2/sdram-micron-mt46h32m32lf-6.h
new file mode 100644 (file)
index 0000000..ef35415
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * SDRC register values for the Micron MT46H32M32LF-6
+ *
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Paul Walmsley
+ *
+ * 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 ARCH_ARM_MACH_OMAP2_SDRAM_MICRON_MT46H32M32LF
+#define ARCH_ARM_MACH_OMAP2_SDRAM_MICRON_MT46H32M32LF
+
+#include <mach/sdrc.h>
+
+/* Micron MT46H32M32LF-6 */
+/* XXX Using ARE = 0x1 (no autorefresh burst) -- can this be changed? */
+static struct omap_sdrc_params mt46h32m32lf6_sdrc_params[] = {
+       [0] = {
+               .rate        = 165941176,
+               .actim_ctrla = 0x9a9db4c6,
+               .actim_ctrlb = 0x00011217,
+               .rfr_ctrl    = 0x0004dc01,
+               .mr          = 0x00000032,
+       },
+       [1] = {
+               .rate        = 133333333,
+               .actim_ctrla = 0x7a19b485,
+               .actim_ctrlb = 0x00011213,
+               .rfr_ctrl    = 0x0003de01,
+               .mr          = 0x00000032,
+       },
+       [2] = {
+               .rate        = 82970588,
+               .actim_ctrla = 0x51512283,
+               .actim_ctrlb = 0x0001120c,
+               .rfr_ctrl    = 0x00025501,
+               .mr          = 0x00000032,
+       },
+       [3] = {
+               .rate        = 66666666,
+               .actim_ctrla = 0x410d2243,
+               .actim_ctrlb = 0x0001120a,
+               .rfr_ctrl    = 0x0001d601,
+               .mr          = 0x00000032,
+       },
+       [4] = {
+               .rate        = 0
+       },
+};
+
+#endif
diff --git a/arch/arm/mach-omap2/sdram-qimonda-hyb18m512160af-6.h b/arch/arm/mach-omap2/sdram-qimonda-hyb18m512160af-6.h
new file mode 100644 (file)
index 0000000..74a92c8
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * SDRC register values for the Qimonda HYB18M512160AF-6
+ *
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Paul Walmsley
+ *
+ * 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 ARCH_ARM_MACH_OMAP2_SDRAM_QIMONDA_HYB18M512160AF6
+#define ARCH_ARM_MACH_OMAP2_SDRAM_QIMONDA_HYB18M512160AF6
+
+#include <mach/sdrc.h>
+
+/* Qimonda HYB18M512160AF-6 */
+/* XXX Using ARE = 0x1 (no autorefresh burst) -- can this be changed? */
+static struct omap_sdrc_params hyb18m512160af6_sdrc_params[] = {
+       [0] = {
+               .rate        = 165941176,
+               .actim_ctrla = 0x629db4c6,
+               .actim_ctrlb = 0x00012214,
+               .rfr_ctrl    = 0x0004dc01,
+               .mr          = 0x00000032,
+       },
+       [1] = {
+               .rate        = 133333333,
+               .actim_ctrla = 0x5219b485,
+               .actim_ctrlb = 0x00012210,
+               .rfr_ctrl    = 0x0003de01,
+               .mr          = 0x00000032,
+       },
+       [2] = {
+               .rate        = 82970588,
+               .actim_ctrla = 0x31512283,
+               .actim_ctrlb = 0x0001220a,
+               .rfr_ctrl    = 0x00025501,
+               .mr          = 0x00000022,
+       },
+       [3] = {
+               .rate        = 66666666,
+               .actim_ctrla = 0x290d2243,
+               .actim_ctrlb = 0x00012208,
+               .rfr_ctrl    = 0x0001d601,
+               .mr          = 0x00000022,
+       },
+       [4] = {
+               .rate        = 0
+       },
+};
+
+#endif
diff --git a/arch/arm/mach-omap2/twl4030-generic-scripts.c b/arch/arm/mach-omap2/twl4030-generic-scripts.c
new file mode 100644 (file)
index 0000000..4293752
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * arch/arm/mach-omap2/twl4030-generic-scripts.c
+ *
+ * Generic power control scripts for TWL4030
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2006 Texas Instruments, Inc
+ *
+ * Written by  Kalle Jokiniemi
+ *             Peter De Schrijver <peter.de-schrijver@nokia.com>
+ *
+ * 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
+ */
+
+#ifdef CONFIG_TWL4030_POWER
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/i2c/twl4030.h>
+
+/*
+ * This script instructs twl4030 to first put the Reset and Control (RC)
+ * resources to sleep and then all the other resources.
+ */
+
+static struct twl4030_ins sleep_on_seq[] __initdata = {
+       {MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_RC, RES_TYPE_ALL, RES_TYPE2_R0,
+                       RES_STATE_SLEEP), 4},
+       {MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_ALL, RES_TYPE2_R0,
+                       RES_STATE_SLEEP), 4},
+};
+
+static struct twl4030_script sleep_on_script __initdata = {
+       .script = sleep_on_seq,
+       .size   = ARRAY_SIZE(sleep_on_seq),
+       .flags  = TRITON_SLEEP_SCRIPT,
+};
+
+/*
+ * This script instructs twl4030 to first enable CLKEN, then wakeup the
+ * regulators and then all other resources.
+ */
+
+static struct twl4030_ins wakeup_seq[] __initdata = {
+       {MSG_SINGULAR(DEV_GRP_NULL, 0x17, RES_STATE_ACTIVE), 0x30},
+       {MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_PP_PR, RES_TYPE_ALL, RES_TYPE2_R0,
+                       RES_STATE_ACTIVE), 0x37},
+       {MSG_BROADCAST(DEV_GRP_NULL, RES_GRP_ALL, RES_TYPE_ALL, RES_TYPE2_R0,
+                       RES_STATE_ACTIVE), 0x2},
+};
+
+static struct twl4030_script wakeup_script __initdata = {
+       .script = wakeup_seq,
+       .size   = ARRAY_SIZE(wakeup_seq),
+       .flags  = TRITON_WAKEUP12_SCRIPT | TRITON_WAKEUP3_SCRIPT,
+};
+
+static struct twl4030_script *twl4030_scripts[] __initdata = {
+       &sleep_on_script,
+       &wakeup_script,
+};
+
+struct twl4030_power_data generic3430_t2scripts_data __initdata = {
+       .scripts        = twl4030_scripts,
+       .size           = ARRAY_SIZE(twl4030_scripts),
+};
+
+
+#endif /* CONFIG_TWL4030_POWER */
diff --git a/arch/arm/mach-omap2/twl4030-generic-scripts.h b/arch/arm/mach-omap2/twl4030-generic-scripts.h
new file mode 100644 (file)
index 0000000..fb69c2e
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __TWL4030_GENERIC_SCRIPTS_H
+#define __TWL4030_GENERIC_SCRIPTS_H
+
+#include <linux/i2c/twl4030.h>
+
+#ifdef CONFIG_TWL4030_POWER
+extern struct twl4030_power_data generic3430_t2scripts_data;
+#define GENERIC3430_T2SCRIPTS_DATA &generic3430_t2scripts_data
+#else
+#define GENERIC3430_T2SCRIPTS_DATA NULL
+#endif /* CONFIG_TWL4030_POWER */
+
+#endif
diff --git a/arch/arm/mach-omap2/usb-ehci.c b/arch/arm/mach-omap2/usb-ehci.c
new file mode 100644 (file)
index 0000000..23fe857
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * linux/arch/arm/mach-omap2/usb-ehci.c
+ *
+ * This file will contain the board specific details for the
+ * Synopsys EHCI host controller on OMAP3430
+ *
+ * Copyright (C) 2007 Texas Instruments
+ * Author: Vikram Pandita <vikram.pandita@ti.com>
+ *
+ * Generalization by:
+ * Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <asm/io.h>
+#include <mach/mux.h>
+
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/pm.h>
+#include <mach/usb.h>
+
+static struct resource ehci_resources[] = {
+       [0] = {
+               .start   = OMAP34XX_HSUSB_HOST_BASE + 0x800,
+               .end     = OMAP34XX_HSUSB_HOST_BASE + 0x800 + SZ_1K - 1,
+               .flags   = IORESOURCE_MEM,
+       },
+       [1] = {         /* general IRQ */
+               .start   = INT_34XX_EHCI_IRQ,
+               .flags   = IORESOURCE_IRQ,
+       }
+};
+
+static u64 ehci_dmamask = ~(u32)0;
+static struct platform_device ehci_device = {
+       .name           = "ehci-omap",
+       .id             = 0,
+       .dev = {
+               .dma_mask               = &ehci_dmamask,
+               .coherent_dma_mask      = 0xffffffff,
+               .platform_data          = NULL,
+       },
+       .num_resources  = ARRAY_SIZE(ehci_resources),
+       .resource       = ehci_resources,
+};
+
+/* MUX settings for EHCI pins */
+/*
+ * setup_ehci_io_mux - initialize IO pad mux for USBHOST
+ */
+static void setup_ehci_io_mux(void)
+{
+#ifdef CONFIG_OMAP_EHCI_PHY_MODE
+       /* PHY mode of operation for board: 750-2083-001
+        * ISP1504 connected to Port1 and Port2
+        * Do Func Mux setting for 12-pin ULPI PHY mode
+        */
+
+       /* Port1 */
+       omap_cfg_reg(Y9_3430_USB1HS_PHY_STP);
+       omap_cfg_reg(Y8_3430_USB1HS_PHY_CLK);
+       omap_cfg_reg(AA14_3430_USB1HS_PHY_DIR);
+       omap_cfg_reg(AA11_3430_USB1HS_PHY_NXT);
+       omap_cfg_reg(W13_3430_USB1HS_PHY_DATA0);
+       omap_cfg_reg(W12_3430_USB1HS_PHY_DATA1);
+       omap_cfg_reg(W11_3430_USB1HS_PHY_DATA2);
+       omap_cfg_reg(Y11_3430_USB1HS_PHY_DATA3);
+       omap_cfg_reg(W9_3430_USB1HS_PHY_DATA4);
+       omap_cfg_reg(Y12_3430_USB1HS_PHY_DATA5);
+       omap_cfg_reg(W8_3430_USB1HS_PHY_DATA6);
+       omap_cfg_reg(Y13_3430_USB1HS_PHY_DATA7);
+
+       /* Port2 */
+       omap_cfg_reg(AA10_3430_USB2HS_PHY_STP);
+       omap_cfg_reg(AA8_3430_USB2HS_PHY_CLK);
+       omap_cfg_reg(AA9_3430_USB2HS_PHY_DIR);
+       omap_cfg_reg(AB11_3430_USB2HS_PHY_NXT);
+       omap_cfg_reg(AB10_3430_USB2HS_PHY_DATA0);
+       omap_cfg_reg(AB9_3430_USB2HS_PHY_DATA1);
+       omap_cfg_reg(W3_3430_USB2HS_PHY_DATA2);
+       omap_cfg_reg(T4_3430_USB2HS_PHY_DATA3);
+       omap_cfg_reg(T3_3430_USB2HS_PHY_DATA4);
+       omap_cfg_reg(R3_3430_USB2HS_PHY_DATA5);
+       omap_cfg_reg(R4_3430_USB2HS_PHY_DATA6);
+       omap_cfg_reg(T2_3430_USB2HS_PHY_DATA7);
+
+#else
+       /* Set Func mux for :
+        * TLL mode of operation
+        * 12-pin ULPI SDR TLL mode for Port1/2/3
+        */
+
+       /* Port1 */
+       omap_cfg_reg(Y9_3430_USB1HS_TLL_STP);
+       omap_cfg_reg(Y8_3430_USB1HS_TLL_CLK);
+       omap_cfg_reg(AA14_3430_USB1HS_TLL_DIR);
+       omap_cfg_reg(AA11_3430_USB1HS_TLL_NXT);
+       omap_cfg_reg(W13_3430_USB1HS_TLL_DATA0);
+       omap_cfg_reg(W12_3430_USB1HS_TLL_DATA1);
+       omap_cfg_reg(W11_3430_USB1HS_TLL_DATA2);
+       omap_cfg_reg(Y11_3430_USB1HS_TLL_DATA3);
+       omap_cfg_reg(W9_3430_USB1HS_TLL_DATA4);
+       omap_cfg_reg(Y12_3430_USB1HS_TLL_DATA5);
+       omap_cfg_reg(W8_3430_USB1HS_TLL_DATA6);
+       omap_cfg_reg(Y13_3430_USB1HS_TLL_DATA7);
+
+       /* Port2 */
+       omap_cfg_reg(AA10_3430_USB2HS_TLL_STP);
+       omap_cfg_reg(AA8_3430_USB2HS_TLL_CLK);
+       omap_cfg_reg(AA9_3430_USB2HS_TLL_DIR);
+       omap_cfg_reg(AB11_3430_USB2HS_TLL_NXT);
+       omap_cfg_reg(AB10_3430_USB2HS_TLL_DATA0);
+       omap_cfg_reg(AB9_3430_USB2HS_TLL_DATA1);
+       omap_cfg_reg(W3_3430_USB2HS_TLL_DATA2);
+       omap_cfg_reg(T4_3430_USB2HS_TLL_DATA3);
+       omap_cfg_reg(T3_3430_USB2HS_TLL_DATA4);
+       omap_cfg_reg(R3_3430_USB2HS_TLL_DATA5);
+       omap_cfg_reg(R4_3430_USB2HS_TLL_DATA6);
+       omap_cfg_reg(T2_3430_USB2HS_TLL_DATA7);
+
+       /* Port3 */
+       omap_cfg_reg(AB3_3430_USB3HS_TLL_STP);
+       omap_cfg_reg(AA6_3430_USB3HS_TLL_CLK);
+       omap_cfg_reg(AA3_3430_USB3HS_TLL_DIR);
+       omap_cfg_reg(Y3_3430_USB3HS_TLL_NXT);
+       omap_cfg_reg(AA5_3430_USB3HS_TLL_DATA0);
+       omap_cfg_reg(Y4_3430_USB3HS_TLL_DATA1);
+       omap_cfg_reg(Y5_3430_USB3HS_TLL_DATA2);
+       omap_cfg_reg(W5_3430_USB3HS_TLL_DATA3);
+       omap_cfg_reg(AB12_3430_USB3HS_TLL_DATA4);
+       omap_cfg_reg(AB13_3430_USB3HS_TLL_DATA5);
+       omap_cfg_reg(AA13_3430_USB3HS_TLL_DATA6);
+       omap_cfg_reg(AA12_3430_USB3HS_TLL_DATA7);
+#endif /* CONFIG_OMAP_EHCI_PHY_MODE */
+
+       return;
+}
+
+void __init usb_ehci_init(void)
+{
+       /* Setup Pin IO MUX for EHCI */
+       if (cpu_is_omap34xx())
+               setup_ehci_io_mux();
+
+       if (platform_device_register(&ehci_device) < 0) {
+               printk(KERN_ERR "Unable to register HS-USB (EHCI) device\n");
+               return;
+       }
+}
+
+
index fc74e913c415523dcf3965468b6b374b98c9654f..927c2d91a036c15ec7d741bb1d822bc1be8f770b 100644 (file)
@@ -161,17 +161,15 @@ static struct platform_device nop_xceiv_device = {
 
 void __init usb_musb_init(void)
 {
 
 void __init usb_musb_init(void)
 {
-       if (cpu_is_omap243x())
+       if (cpu_is_omap243x()) {
                musb_resources[0].start = OMAP243X_HS_BASE;
                musb_resources[0].start = OMAP243X_HS_BASE;
-       else
+               musb_plat.clock = "usbhs_ick";
+       } else {
                musb_resources[0].start = OMAP34XX_HSUSB_OTG_BASE;
                musb_resources[0].start = OMAP34XX_HSUSB_OTG_BASE;
-       musb_resources[0].end = musb_resources[0].start + SZ_8K - 1;
+               musb_plat.clock = "hsotgusb_ick";
+       }
 
 
-       /*
-        * REVISIT: This line can be removed once all the platforms using
-        * musb_core.c have been converted to use use clkdev.
-        */
-       musb_plat.clock = "ick";
+       musb_resources[0].end = musb_resources[0].start + SZ_8K - 1;
 
 #ifdef CONFIG_NOP_USB_XCEIV
        if (platform_device_register(&nop_xceiv_device) < 0) {
 
 #ifdef CONFIG_NOP_USB_XCEIV
        if (platform_device_register(&nop_xceiv_device) < 0) {
index 15e509013def69cda9cf2ff4d431e70868d676c0..59c1d57e507f1c96ef9e451c863937a6cc670d2f 100644 (file)
@@ -175,8 +175,6 @@ static int tusb_set_sync_mode(unsigned sysclk_ps, unsigned fclk_ps)
        return gpmc_cs_set_timings(sync_cs, &t);
 }
 
        return gpmc_cs_set_timings(sync_cs, &t);
 }
 
-extern unsigned long gpmc_get_fclk_period(void);
-
 /* tusb driver calls this when it changes the chip's clocking */
 int tusb6010_platform_retime(unsigned is_refclk)
 {
 /* tusb driver calls this when it changes the chip's clocking */
 int tusb6010_platform_retime(unsigned is_refclk)
 {
@@ -187,7 +185,7 @@ int tusb6010_platform_retime(unsigned is_refclk)
        unsigned        sysclk_ps;
        int             status;
 
        unsigned        sysclk_ps;
        int             status;
 
-       if (!refclk_psec)
+       if (!refclk_psec || sysclk_ps == 0)
                return -ENODEV;
 
        sysclk_ps = is_refclk ? refclk_psec : TUSB6010_OSCCLK_60;
                return -ENODEV;
 
        sysclk_ps = is_refclk ? refclk_psec : TUSB6010_OSCCLK_60;
index 9dd68fafb374b54c273ebe2e7034be042e993563..b37fc101e9f581bed9ea5feff8b1e4dd0009e76a 100644 (file)
@@ -73,6 +73,39 @@ config OMAP_RESET_CLOCKS
          probably do not want this option enabled until your
          device drivers work properly.
 
          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"
+        default n
+        help
+          Say Y, if you want to have support for reporting of GPIO
+          switches (e.g. cover switches) via sysfs. Your bootloader has
+          to provide information about the switches to the kernel via the
+          ATAG_BOARD mechanism if they're not defined by the board config.
+
 config OMAP_MUX
        bool "OMAP multiplexing support"
         depends on ARCH_OMAP
 config OMAP_MUX
        bool "OMAP multiplexing support"
         depends on ARCH_OMAP
index 04a100cfb8e5fcfe67057938f7472114150c04b1..3ebc09ed0f6c8ea466796026b2c3a757586e49f0 100644 (file)
@@ -16,6 +16,9 @@ obj-$(CONFIG_OMAP_MCBSP) += mcbsp.o
 
 obj-$(CONFIG_CPU_FREQ) += cpu-omap.o
 obj-$(CONFIG_OMAP_DM_TIMER) += dmtimer.o
 
 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
 obj-$(CONFIG_OMAP_DEBUG_DEVICES) += debug-devices.o
 obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o
 i2c-omap-$(CONFIG_I2C_OMAP) := i2c.o
 obj-$(CONFIG_OMAP_DEBUG_DEVICES) += debug-devices.o
 obj-$(CONFIG_OMAP_DEBUG_LEDS) += debug-leds.o
 i2c-omap-$(CONFIG_I2C_OMAP) := i2c.o
diff --git a/arch/arm/plat-omap/bootreason.c b/arch/arm/plat-omap/bootreason.c
new file mode 100644 (file)
index 0000000..d527b1b
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * linux/arch/arm/plat-omap/bootreason.c
+ *
+ * OMAP Bootreason passing
+ *
+ * Copyright (c) 2004 Nokia
+ *
+ * Written by David Weinehall <david.weinehall@nokia.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.
+ */
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
+#include <mach/board.h>
+
+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);
index d1797147732f217ce4906f5388e55956b7f4f62c..28666126fd3b9c661fdf611068e5b37e9a074625 100644 (file)
 
 #define NO_LENGTH_CHECK 0xffffffff
 
 
 #define NO_LENGTH_CHECK 0xffffffff
 
-unsigned char omap_bootloader_tag[512];
+unsigned char omap_bootloader_tag[1024];
 int omap_bootloader_tag_len;
 
 struct omap_board_config_kernel *omap_board_config;
 int omap_board_config_size;
 
 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;
 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/component-version.c b/arch/arm/plat-omap/component-version.c
new file mode 100644 (file)
index 0000000..3c9d523
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ *  linux/arch/arm/plat-omap/component-version.c
+ *
+ *  Copyright (C) 2005 Nokia Corporation
+ *  Written by Juha Yrjölä <juha.yrjola@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/proc_fs.h>
+#include <mach/board.h>
+#include <mach/board-nokia.h>
+
+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ä <juha.yrjola@nokia.com>");
+MODULE_DESCRIPTION("Component version driver");
+MODULE_LICENSE("GPL");
index 21cc0142b97ad25293706acbb027fc32aa34d28b..609301860c50c6544d9cf795b4e069dbb785e428 100644 (file)
@@ -738,7 +738,7 @@ int omap_request_dma(int dev_id, const char *dev_name,
                 * id.
                 */
                dma_write(dev_id | (1 << 10), CCR(free_ch));
                 * id.
                 */
                dma_write(dev_id | (1 << 10), CCR(free_ch));
-       } else if (cpu_is_omap7xx() || cpu_is_omap15xx()) {
+       } else if (cpu_is_omap730() || cpu_is_omap15xx()) {
                dma_write(dev_id, CCR(free_ch));
        }
 
                dma_write(dev_id, CCR(free_ch));
        }
 
@@ -760,19 +760,12 @@ void omap_free_dma(int lch)
 {
        unsigned long flags;
 
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&dma_chan_lock, flags);
        if (dma_chan[lch].dev_id == -1) {
                pr_err("omap_dma: trying to free unallocated DMA channel %d\n",
                       lch);
        if (dma_chan[lch].dev_id == -1) {
                pr_err("omap_dma: trying to free unallocated DMA channel %d\n",
                       lch);
-               spin_unlock_irqrestore(&dma_chan_lock, flags);
                return;
        }
 
                return;
        }
 
-       dma_chan[lch].dev_id = -1;
-       dma_chan[lch].next_lch = -1;
-       dma_chan[lch].callback = NULL;
-       spin_unlock_irqrestore(&dma_chan_lock, flags);
-
        if (cpu_class_is_omap1()) {
                /* Disable all DMA interrupts for the channel. */
                dma_write(0, CICR(lch));
        if (cpu_class_is_omap1()) {
                /* Disable all DMA interrupts for the channel. */
                dma_write(0, CICR(lch));
@@ -798,6 +791,12 @@ void omap_free_dma(int lch)
                dma_write(0, CCR(lch));
                omap_clear_dma(lch);
        }
                dma_write(0, CCR(lch));
                omap_clear_dma(lch);
        }
+
+       spin_lock_irqsave(&dma_chan_lock, flags);
+       dma_chan[lch].dev_id = -1;
+       dma_chan[lch].next_lch = -1;
+       dma_chan[lch].callback = NULL;
+       spin_unlock_irqrestore(&dma_chan_lock, flags);
 }
 EXPORT_SYMBOL(omap_free_dma);
 
 }
 EXPORT_SYMBOL(omap_free_dma);
 
@@ -2346,7 +2345,7 @@ static int __init omap_init_dma(void)
                printk(KERN_INFO "DMA support for OMAP15xx initialized\n");
                dma_chan_count = 9;
                enable_1510_mode = 1;
                printk(KERN_INFO "DMA support for OMAP15xx initialized\n");
                dma_chan_count = 9;
                enable_1510_mode = 1;
-       } else if (cpu_is_omap16xx() || cpu_is_omap7xx()) {
+       } else if (cpu_is_omap16xx() || cpu_is_omap730()) {
                printk(KERN_INFO "OMAP DMA hardware version %d\n",
                       dma_read(HW_ID));
                printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n",
                printk(KERN_INFO "OMAP DMA hardware version %d\n",
                       dma_read(HW_ID));
                printk(KERN_INFO "DMA capabilities: %08x:%08x:%04x:%04x:%04x\n",
@@ -2424,6 +2423,19 @@ static int __init omap_init_dma(void)
        if (cpu_class_is_omap2())
                setup_irq(INT_24XX_SDMA_IRQ0, &omap24xx_dma_irq);
 
        if (cpu_class_is_omap2())
                setup_irq(INT_24XX_SDMA_IRQ0, &omap24xx_dma_irq);
 
+       /* Enable smartidle idlemodes and autoidle */
+       if (cpu_is_omap34xx()) {
+               u32 v = dma_read(OCP_SYSCONFIG);
+               v &= ~(DMA_SYSCONFIG_MIDLEMODE_MASK |
+                               DMA_SYSCONFIG_SIDLEMODE_MASK |
+                               DMA_SYSCONFIG_AUTOIDLE);
+               v |= (DMA_SYSCONFIG_MIDLEMODE(DMA_IDLEMODE_SMARTIDLE) |
+                       DMA_SYSCONFIG_SIDLEMODE(DMA_IDLEMODE_SMARTIDLE) |
+                       DMA_SYSCONFIG_AUTOIDLE);
+               dma_write(v , OCP_SYSCONFIG);
+       }
+
+
        /* FIXME: Update LCD DMA to work on 24xx */
        if (cpu_class_is_omap1()) {
                r = request_irq(INT_DMA_LCD, lcd_dma_irq_handler, 0,
        /* FIXME: Update LCD DMA to work on 24xx */
        if (cpu_class_is_omap1()) {
                r = request_irq(INT_DMA_LCD, lcd_dma_irq_handler, 0,
index bfd47570cc9142e2d0d2b2d0e471233f4a925979..a05205c12f7b3d39c58f5eba6561107558f34a09 100644 (file)
@@ -238,7 +238,7 @@ static struct omap_dm_timer omap3_dm_timers[] = {
        { .phys_base = 0x49040000, .irq = INT_24XX_GPTIMER9 },
        { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 },
        { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 },
        { .phys_base = 0x49040000, .irq = INT_24XX_GPTIMER9 },
        { .phys_base = 0x48086000, .irq = INT_24XX_GPTIMER10 },
        { .phys_base = 0x48088000, .irq = INT_24XX_GPTIMER11 },
-       { .phys_base = 0x48304000, .irq = INT_24XX_GPTIMER12 },
+       { .phys_base = 0x48304000, .irq = INT_34XX_GPT12_IRQ },
 };
 
 static const char *omap3_dm_source_names[] __initdata = {
 };
 
 static const char *omap3_dm_source_names[] __initdata = {
@@ -321,11 +321,9 @@ static void omap_dm_timer_reset(struct omap_dm_timer *timer)
        l |= 0x2 << 8;   /* Set clock activity to perserve f-clock on idle */
 
        /*
        l |= 0x2 << 8;   /* Set clock activity to perserve f-clock on idle */
 
        /*
-        * Enable wake-up only for GPT1 on OMAP2 CPUs.
-        * FIXME: All timers should have wake-up enabled and clear
-        * PRCM status.
+        * Enable wake-up on OMAP2 CPUs.
         */
         */
-       if (cpu_class_is_omap2() && (timer == &dm_timers[0]))
+       if (cpu_class_is_omap2())
                l |= 1 << 2;
        omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, l);
 
                l |= 1 << 2;
        omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_REG, l);
 
index ce6b4baeedec416aa48d64f17872e799079cd328..3746222bed10ff52f6002556c21cfca486e2babf 100644 (file)
@@ -206,9 +206,10 @@ void __init omapfb_reserve_sdram(void)
                        config_invalid = 1;
                        return;
                }
                        config_invalid = 1;
                        return;
                }
-               if (rg.paddr)
+               if (rg.paddr) {
                        reserve_bootmem(rg.paddr, rg.size, BOOTMEM_DEFAULT);
                        reserve_bootmem(rg.paddr, rg.size, BOOTMEM_DEFAULT);
-               reserved += rg.size;
+                       reserved += rg.size;
+               }
                omapfb_config.mem_desc.region[i] = rg;
                configured_regions++;
        }
                omapfb_config.mem_desc.region[i] = rg;
                configured_regions++;
        }
diff --git a/arch/arm/plat-omap/gpio-switch.c b/arch/arm/plat-omap/gpio-switch.c
new file mode 100644 (file)
index 0000000..9053ea0
--- /dev/null
@@ -0,0 +1,558 @@
+/*
+ *  linux/arch/arm/plat-omap/gpio-switch.c
+ *
+ *  Copyright (C) 2004-2006 Nokia Corporation
+ *  Written by Juha Yrjölä <juha.yrjola@nokia.com>
+ *         and Paul Mundt <paul.mundt@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/timer.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <mach/hardware.h>
+#include <mach/irqs.h>
+#include <mach/mux.h>
+#include <mach/board.h>
+#include <mach/gpio-switch.h>
+
+struct gpio_switch {
+       char            name[14];
+       u16             gpio;
+       unsigned        flags:4;
+       unsigned        type:4;
+       unsigned        state:1;
+       unsigned        both_edges:1;
+
+       u16             debounce_rising;
+       u16             debounce_falling;
+
+       void (* notify)(void *data, int state);
+       void *notify_data;
+
+       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 platform_driver gpio_sw_driver;
+
+static const struct omap_gpio_switch *board_gpio_sw_table;
+static int board_gpio_sw_count;
+
+static const char *cover_str[2] = { "open", "closed" };
+static const char *connection_str[2] = { "disconnected", "connected" };
+static const char *activity_str[2] = { "inactive", "active" };
+
+/*
+ * GPIO switch state default debounce delay in ms
+ */
+#define OMAP_GPIO_SW_DEFAULT_DEBOUNCE          10
+
+static const char **get_sw_str(struct gpio_switch *sw)
+{
+       switch (sw->type) {
+       case OMAP_GPIO_SWITCH_TYPE_COVER:
+               return cover_str;
+       case OMAP_GPIO_SWITCH_TYPE_CONNECTION:
+               return connection_str;
+       case OMAP_GPIO_SWITCH_TYPE_ACTIVITY:
+               return activity_str;
+       default:
+               BUG();
+               return NULL;
+       }
+}
+
+static const char *get_sw_type(struct gpio_switch *sw)
+{
+       switch (sw->type) {
+       case OMAP_GPIO_SWITCH_TYPE_COVER:
+               return "cover";
+       case OMAP_GPIO_SWITCH_TYPE_CONNECTION:
+               return "connection";
+       case OMAP_GPIO_SWITCH_TYPE_ACTIVITY:
+               return "activity";
+       default:
+               BUG();
+               return NULL;
+       }
+}
+
+static void print_sw_state(struct gpio_switch *sw, int state)
+{
+       const char **str;
+
+       str = get_sw_str(sw);
+       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 = gpio_get_value(sw->gpio);
+       if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
+               state = !state;
+
+       return state;
+}
+
+static ssize_t gpio_sw_state_store(struct device *dev,
+                                  struct device_attribute *attr,
+                                  const char *buf,
+                                  size_t count)
+{
+       struct gpio_switch *sw = dev_get_drvdata(dev);
+       const char **str;
+       char state[16];
+       int enable;
+
+       if (!(sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT))
+               return -EPERM;
+
+       if (sscanf(buf, "%15s", state) != 1)
+               return -EINVAL;
+
+       str = get_sw_str(sw);
+       if (strcmp(state, str[0]) == 0)
+               sw->state = enable = 0;
+       else if (strcmp(state, str[1]) == 0)
+               sw->state = enable = 1;
+       else
+               return -EINVAL;
+
+       if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
+               enable = !enable;
+       gpio_set_value(sw->gpio, enable);
+
+       return count;
+}
+
+static ssize_t gpio_sw_state_show(struct device *dev,
+                                 struct device_attribute *attr,
+                                 char *buf)
+{
+       struct gpio_switch *sw = dev_get_drvdata(dev);
+       const char **str;
+
+       str = get_sw_str(sw);
+       return sprintf(buf, "%s\n", str[sw->state]);
+}
+
+static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, gpio_sw_state_show,
+                  gpio_sw_state_store);
+
+static ssize_t gpio_sw_type_show(struct device *dev,
+                                struct device_attribute *attr,
+                                char *buf)
+{
+       struct gpio_switch *sw = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%s\n", get_sw_type(sw));
+}
+
+static DEVICE_ATTR(type, S_IRUGO, gpio_sw_type_show, NULL);
+
+static ssize_t gpio_sw_direction_show(struct device *dev,
+                                     struct device_attribute *attr,
+                                     char *buf)
+{
+       struct gpio_switch *sw = dev_get_drvdata(dev);
+       int is_output;
+
+       is_output = sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT;
+       return sprintf(buf, "%s\n", is_output ? "output" : "input");
+}
+
+static DEVICE_ATTR(direction, S_IRUGO, gpio_sw_direction_show, NULL);
+
+
+static irqreturn_t gpio_sw_irq_handler(int irq, void *arg)
+{
+       struct gpio_switch *sw = arg;
+       unsigned long timeout;
+       int state;
+
+       if (!sw->both_edges) {
+               if (gpio_get_value(sw->gpio))
+                       set_irq_type(gpio_to_irq(sw->gpio), IRQ_TYPE_EDGE_FALLING);
+               else
+                       set_irq_type(gpio_to_irq(sw->gpio), IRQ_TYPE_EDGE_RISING);
+       }
+
+       state = gpio_sw_get_state(sw);
+       if (sw->state == state)
+               return IRQ_HANDLED;
+
+       if (state)
+               timeout = sw->debounce_rising;
+       else
+               timeout = sw->debounce_falling;
+       if (!timeout)
+               schedule_work(&sw->work);
+       else
+               mod_timer(&sw->timer, jiffies + msecs_to_jiffies(timeout));
+
+       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(struct work_struct *work)
+{
+       struct gpio_switch *sw = container_of(work, struct gpio_switch, work);
+       int state;
+
+       state = gpio_sw_get_state(sw);
+       if (sw->state == state)
+               return;
+
+       sw->state = state;
+       if (sw->notify != NULL)
+               sw->notify(sw->notify_data, state);
+       sysfs_notify(&sw->pdev.dev.kobj, NULL, "state");
+       print_sw_state(sw, state);
+}
+
+static int __init can_do_both_edges(struct gpio_switch *sw)
+{
+       if (!cpu_class_is_omap1())
+               return 1;
+       if (OMAP_GPIO_IS_MPUIO(sw->gpio))
+               return 0;
+       else
+               return 1;
+}
+
+static void gpio_sw_release(struct device *dev)
+{
+}
+
+static int __init new_switch(struct gpio_switch *sw)
+{
+       int r, direction, trigger;
+
+       switch (sw->type) {
+       case OMAP_GPIO_SWITCH_TYPE_COVER:
+       case OMAP_GPIO_SWITCH_TYPE_CONNECTION:
+       case OMAP_GPIO_SWITCH_TYPE_ACTIVITY:
+               break;
+       default:
+               printk(KERN_ERR "invalid GPIO switch type: %d\n", sw->type);
+               return -EINVAL;
+       }
+
+       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.driver;
+       sw->pdev.dev.release = gpio_sw_release;
+
+       r = platform_device_register(&sw->pdev);
+       if (r) {
+               printk(KERN_ERR "gpio-switch: platform device registration "
+                      "failed for %s", sw->name);
+               return r;
+       }
+       dev_set_drvdata(&sw->pdev.dev, sw);
+
+       r = gpio_request(sw->gpio, sw->name);
+       if (r < 0) {
+               platform_device_unregister(&sw->pdev);
+               return r;
+       }
+
+       /* input: 1, output: 0 */
+       direction = !(sw->flags & OMAP_GPIO_SWITCH_FLAG_OUTPUT);
+       if (direction) {
+               gpio_direction_input(sw->gpio);
+               sw->state = gpio_sw_get_state(sw);
+       } else {
+               int state = sw->state = !!(sw->flags &
+                       OMAP_GPIO_SWITCH_FLAG_OUTPUT_INIT_ACTIVE);
+
+               if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
+                       state = !state;
+               gpio_direction_output(sw->gpio, state);
+       }
+
+       r = 0;
+       r |= device_create_file(&sw->pdev.dev, &dev_attr_state);
+       r |= device_create_file(&sw->pdev.dev, &dev_attr_type);
+       r |= device_create_file(&sw->pdev.dev, &dev_attr_direction);
+       if (r)
+               printk(KERN_ERR "gpio-switch: attribute file creation "
+                      "failed for %s\n", sw->name);
+
+       if (!direction)
+               return 0;
+
+       if (can_do_both_edges(sw)) {
+               trigger = IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING;
+               sw->both_edges = 1;
+       } else {
+               if (gpio_get_value(sw->gpio))
+                       trigger = IRQF_TRIGGER_FALLING;
+               else
+                       trigger = IRQF_TRIGGER_RISING;
+       }
+       r = request_irq(gpio_to_irq(sw->gpio), gpio_sw_irq_handler,
+                       IRQF_SHARED | 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);
+               gpio_free(sw->gpio);
+               return r;
+       }
+
+       INIT_WORK(&sw->work, gpio_sw_handler);
+       init_timer(&sw->timer);
+
+       sw->timer.function = gpio_sw_timer;
+       sw->timer.data = (unsigned long)sw;
+
+       list_add(&sw->node, &gpio_switches);
+
+       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 = kzalloc(sizeof(*sw), GFP_KERNEL);
+               if (sw == NULL) {
+                       printk(KERN_ERR "gpio-switch: kmalloc failed\n");
+                       return -ENOMEM;
+               }
+               strncpy(sw->name, cfg->name, sizeof(cfg->name));
+               sw->gpio = cfg->gpio;
+               sw->flags = cfg->flags;
+               sw->type = cfg->type;
+               sw->debounce_rising = OMAP_GPIO_SW_DEFAULT_DEBOUNCE;
+               sw->debounce_falling = OMAP_GPIO_SW_DEFAULT_DEBOUNCE;
+               if ((r = new_switch(sw)) < 0) {
+                       kfree(sw);
+                       return r;
+               }
+       }
+       return 0;
+}
+
+static struct gpio_switch * __init find_switch(int gpio, const char *name)
+{
+       struct gpio_switch *sw;
+
+       list_for_each_entry(sw, &gpio_switches, node) {
+               if ((gpio < 0 || sw->gpio != gpio) &&
+                   (name == NULL || strcmp(sw->name, name) != 0))
+                       continue;
+
+               if (gpio < 0 || name == NULL)
+                       goto no_check;
+
+               if (strcmp(sw->name, name) != 0)
+                       printk("gpio-switch: name mismatch for %d (%s, %s)\n",
+                              gpio, name, sw->name);
+               else if (sw->gpio != gpio)
+                       printk("gpio-switch: GPIO mismatch for %s (%d, %d)\n",
+                              name, gpio, sw->gpio);
+no_check:
+               return sw;
+       }
+       return NULL;
+}
+
+static int __init add_board_switches(void)
+{
+       int i;
+
+       for (i = 0; i < board_gpio_sw_count; i++) {
+               const struct omap_gpio_switch *cfg;
+               struct gpio_switch *sw;
+               int r;
+
+               cfg = board_gpio_sw_table + i;
+               if (strlen(cfg->name) > sizeof(sw->name) - 1)
+                       return -EINVAL;
+               /* Check whether we only update an existing switch
+                * or add a new switch. */
+               sw = find_switch(cfg->gpio, cfg->name);
+               if (sw != NULL) {
+                       sw->debounce_rising = cfg->debounce_rising;
+                       sw->debounce_falling = cfg->debounce_falling;
+                       sw->notify = cfg->notify;
+                       sw->notify_data = cfg->notify_data;
+                       continue;
+               } else {
+                       if (cfg->gpio < 0 || cfg->name == NULL) {
+                               printk("gpio-switch: required switch not "
+                                      "found (%d, %s)\n", cfg->gpio,
+                                      cfg->name);
+                               continue;
+                       }
+               }
+               sw = kzalloc(sizeof(*sw), GFP_KERNEL);
+               if (sw == NULL) {
+                       printk(KERN_ERR "gpio-switch: kmalloc failed\n");
+                       return -ENOMEM;
+               }
+               strlcpy(sw->name, cfg->name, sizeof(sw->name));
+               sw->gpio = cfg->gpio;
+               sw->flags = cfg->flags;
+               sw->type = cfg->type;
+               sw->debounce_rising = cfg->debounce_rising;
+               sw->debounce_falling = cfg->debounce_falling;
+               sw->notify = cfg->notify;
+               sw->notify_data = cfg->notify_data;
+               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) {
+               if (old != NULL)
+                       kfree(old);
+               flush_scheduled_work();
+               del_timer_sync(&sw->timer);
+
+               free_irq(gpio_to_irq(sw->gpio), sw);
+
+               device_remove_file(&sw->pdev.dev, &dev_attr_state);
+               device_remove_file(&sw->pdev.dev, &dev_attr_type);
+               device_remove_file(&sw->pdev.dev, &dev_attr_direction);
+
+               platform_device_unregister(&sw->pdev);
+               gpio_free(sw->gpio);
+               old = sw;
+       }
+       kfree(old);
+}
+
+static void __init report_initial_state(void)
+{
+       struct gpio_switch *sw;
+
+       list_for_each_entry(sw, &gpio_switches, node) {
+               int state;
+
+               state = gpio_get_value(sw->gpio);
+               if (sw->flags & OMAP_GPIO_SWITCH_FLAG_INVERTED)
+                       state = !state;
+               if (sw->notify != NULL)
+                       sw->notify(sw->notify_data, state);
+               print_sw_state(sw, state);
+       }
+}
+
+static int gpio_sw_remove(struct platform_device *dev)
+{
+       return 0;
+}
+
+static struct platform_driver gpio_sw_driver = {
+       .remove         = gpio_sw_remove,
+       .driver         = {
+               .name   = "gpio-switch",
+       },
+};
+
+void __init omap_register_gpio_switches(const struct omap_gpio_switch *tbl,
+                                       int count)
+{
+       BUG_ON(board_gpio_sw_table != NULL);
+
+       board_gpio_sw_table = tbl;
+       board_gpio_sw_count = count;
+}
+
+static int __init gpio_sw_init(void)
+{
+       int r;
+
+       printk(KERN_INFO "OMAP GPIO switch handler initializing\n");
+
+       r = platform_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)) {
+               r = PTR_ERR(gpio_sw_platform_dev);
+               goto err1;
+       }
+
+       r = add_atag_switches();
+       if (r < 0)
+               goto err2;
+
+       r = add_board_switches();
+       if (r < 0)
+               goto err2;
+
+       report_initial_state();
+
+       return 0;
+err2:
+       gpio_sw_cleanup();
+       platform_device_unregister(gpio_sw_platform_dev);
+err1:
+       platform_driver_unregister(&gpio_sw_driver);
+       return r;
+}
+
+static void __exit gpio_sw_exit(void)
+{
+       gpio_sw_cleanup();
+       platform_device_unregister(gpio_sw_platform_dev);
+       platform_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ä <juha.yrjola@nokia.com>, Paul Mundt <paul.mundt@nokia.com");
+MODULE_DESCRIPTION("GPIO switch driver");
+MODULE_LICENSE("GPL");
index d3fa41e3d8c5a79235eb687dc4dc9ae92f330b77..17d7afe42b83efce507a8112af98d4de8280d02a 100644 (file)
@@ -758,8 +758,12 @@ static void _clear_gpio_irqbank(struct gpio_bank *bank, int gpio_mask)
 
        /* Workaround for clearing DSP GPIO interrupts to allow retention */
 #if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
 
        /* Workaround for clearing DSP GPIO interrupts to allow retention */
 #if defined(CONFIG_ARCH_OMAP24XX) || defined(CONFIG_ARCH_OMAP34XX)
+       reg = bank->base + OMAP24XX_GPIO_IRQSTATUS2;
        if (cpu_is_omap24xx() || cpu_is_omap34xx())
        if (cpu_is_omap24xx() || cpu_is_omap34xx())
-               __raw_writel(gpio_mask, bank->base + OMAP24XX_GPIO_IRQSTATUS2);
+               __raw_writel(gpio_mask, reg);
+
+       /* Flush posted write for the irq status to avoid spurious interrupts */
+       __raw_readl(reg);
 #endif
 }
 
 #endif
 }
 
@@ -921,13 +925,10 @@ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
        case METHOD_MPUIO:
        case METHOD_GPIO_1610:
                spin_lock_irqsave(&bank->lock, flags);
        case METHOD_MPUIO:
        case METHOD_GPIO_1610:
                spin_lock_irqsave(&bank->lock, flags);
-               if (enable) {
+               if (enable)
                        bank->suspend_wakeup |= (1 << gpio);
                        bank->suspend_wakeup |= (1 << gpio);
-                       enable_irq_wake(bank->irq);
-               } else {
-                       disable_irq_wake(bank->irq);
+               else
                        bank->suspend_wakeup &= ~(1 << gpio);
                        bank->suspend_wakeup &= ~(1 << gpio);
-               }
                spin_unlock_irqrestore(&bank->lock, flags);
                return 0;
 #endif
                spin_unlock_irqrestore(&bank->lock, flags);
                return 0;
 #endif
@@ -940,13 +941,10 @@ static int _set_gpio_wakeup(struct gpio_bank *bank, int gpio, int enable)
                        return -EINVAL;
                }
                spin_lock_irqsave(&bank->lock, flags);
                        return -EINVAL;
                }
                spin_lock_irqsave(&bank->lock, flags);
-               if (enable) {
+               if (enable)
                        bank->suspend_wakeup |= (1 << gpio);
                        bank->suspend_wakeup |= (1 << gpio);
-                       enable_irq_wake(bank->irq);
-               } else {
-                       disable_irq_wake(bank->irq);
+               else
                        bank->suspend_wakeup &= ~(1 << gpio);
                        bank->suspend_wakeup &= ~(1 << gpio);
-               }
                spin_unlock_irqrestore(&bank->lock, flags);
                return 0;
 #endif
                spin_unlock_irqrestore(&bank->lock, flags);
                return 0;
 #endif
diff --git a/arch/arm/plat-omap/include/mach/board-nokia.h b/arch/arm/plat-omap/include/mach/board-nokia.h
new file mode 100644 (file)
index 0000000..198d761
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ *  arch/arm/plat-omap/include/mach/board-nokia.h
+ *
+ *  Information structures for Nokia-specific board config data
+ *
+ *  Copyright (C) 2005 Nokia Corporation
+ */
+
+#ifndef __ASM_ARCH_OMAP_NOKIA_H
+#define __ASM_ARCH_OMAP_NOKIA_H
+
+#include <linux/types.h>
+
+struct tsc2301_platform_data;
+struct dsp_kfunc_device;
+extern void n800_bt_init(void);
+extern void n800_dsp_init(void);
+extern void n800_flash_init(void);
+extern void n800_mmc_init(void);
+extern void n800_pm_init(void);
+extern void n800_usb_init(void);
+extern void n800_cam_init(void);
+extern void n800_audio_init(struct tsc2301_platform_data *);
+extern int n800_audio_enable(struct dsp_kfunc_device *kdev, int stage);
+extern int n800_audio_disable(struct dsp_kfunc_device *kdev, int stage);
+extern void n800_mmc_slot1_cover_handler(void *arg, int state);
+
+#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/arch/arm/plat-omap/include/mach/board-rx51.h b/arch/arm/plat-omap/include/mach/board-rx51.h
new file mode 100644 (file)
index 0000000..7b32872
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * linux/include/asm-arm/arch-omap/board-rx51.h
+ *
+ * Copyright (C) 2007 Nokia
+ *
+ * Hardware definitions for Nokia RX-51
+ * based on board-3430sdp.h
+ *
+ *
+ * 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_BOARD_RX51_H
+#define __ASM_ARCH_OMAP_BOARD_RX51_H
+
+#include <mach/board-nokia.h>
+
+#ifdef CONFIG_USB_MUSB_SOC
+extern void rx51_usb_init(void);
+#else
+static inline void rx51_usb_init(void) { }
+#endif
+
+extern void n800_bt_init(void);
+
+struct omap_sdrc_params *rx51_get_sdram_timings(void);
+
+#endif /*  __ASM_ARCH_OMAP_BOARD_RX51_H */
+
index 50ea79a0efa258b807b793c6cf58733fcb1b236c..ae8c2db22eb51786222be2a93ff2e31eb39c5b33 100644 (file)
 #define OMAP_TAG_FBMEM         0x4f08
 #define OMAP_TAG_STI_CONSOLE   0x4f09
 #define OMAP_TAG_CAMERA_SENSOR 0x4f0a
 #define OMAP_TAG_FBMEM         0x4f08
 #define OMAP_TAG_STI_CONSOLE   0x4f09
 #define OMAP_TAG_CAMERA_SENSOR 0x4f0a
+#define OMAP_TAG_PARTITION      0x4f0b
+#define OMAP_TAG_TEA5761       0x4f10
+#define OMAP_TAG_TMP105                0x4f11
 
 #define OMAP_TAG_BOOT_REASON    0x4f80
 
 #define OMAP_TAG_BOOT_REASON    0x4f80
-#define OMAP_TAG_FLASH_PART    0x4f81
+#define OMAP_TAG_FLASH_PART_STR        0x4f81
 #define OMAP_TAG_VERSION_STR   0x4f82
 
 struct omap_clock_config {
 #define OMAP_TAG_VERSION_STR   0x4f82
 
 struct omap_clock_config {
@@ -43,12 +46,6 @@ struct omap_sti_console_config {
        u8 channel;
 };
 
        u8 channel;
 };
 
-struct omap_camera_sensor_config {
-       u16 reset_gpio;
-       int (*power_on)(void * data);
-       int (*power_off)(void * data);
-};
-
 struct omap_usb_config {
        /* Configure drivers according to the connectors on your board:
         *  - "A" connector (rectagular)
 struct omap_usb_config {
        /* Configure drivers according to the connectors on your board:
         *  - "A" connector (rectagular)
@@ -108,9 +105,9 @@ struct omap_pwm_led_platform_data {
 struct omap_gpio_switch_config {
        char name[12];
        u16 gpio;
 struct omap_gpio_switch_config {
        char name[12];
        u16 gpio;
-       int flags:4;
-       int type:4;
-       int key_code:24; /* Linux key code */
+       u8 flags:4;
+       u8 type:4;
+       unsigned int key_code:24; /* Linux key code */
 };
 
 struct omap_uart_config {
 };
 
 struct omap_uart_config {
@@ -118,8 +115,25 @@ struct omap_uart_config {
        unsigned int enabled_uarts;
 };
 
        unsigned int enabled_uarts;
 };
 
+struct omap_tea5761_config {
+       u16 enable_gpio;
+};
+
+/* This cannot be passed from the bootloader */
+struct omap_tmp105_config {
+       u16 tmp105_irq_pin;
+       int (* set_power)(int enable);
+};
+
+struct omap_partition_config {
+       char name[16];
+       unsigned int size;
+       unsigned int offset;
+       /* same as in include/linux/mtd/partitions.h */
+       unsigned int mask_flags;
+};
 
 
-struct omap_flash_part_config {
+struct omap_flash_part_str_config {
        char part_table[0];
 };
 
        char part_table[0];
 };
 
index 54fe9665b1826f674b761685d443f54c48567864..224b0770ec2dcd41e461aadf49df797731153f30 100644 (file)
 #define DMA_THREAD_FIFO_25             (0x02 << 14)
 #define DMA_THREAD_FIFO_50             (0x03 << 14)
 
 #define DMA_THREAD_FIFO_25             (0x02 << 14)
 #define DMA_THREAD_FIFO_50             (0x03 << 14)
 
+/* DMA4_OCP_SYSCONFIG bits */
+#define DMA_SYSCONFIG_MIDLEMODE_MASK           (3 << 12)
+#define DMA_SYSCONFIG_CLOCKACTIVITY_MASK       (3 << 8)
+#define DMA_SYSCONFIG_EMUFREE                  (1 << 5)
+#define DMA_SYSCONFIG_SIDLEMODE_MASK           (3 << 3)
+#define DMA_SYSCONFIG_SOFTRESET                        (1 << 2)
+#define DMA_SYSCONFIG_AUTOIDLE                 (1 << 0)
+
+#define DMA_SYSCONFIG_MIDLEMODE(n)             ((n) << 12)
+#define DMA_SYSCONFIG_SIDLEMODE(n)             ((n) << 3)
+
+#define DMA_IDLEMODE_SMARTIDLE                 0x2
+#define DMA_IDLEMODE_NO_IDLE                   0x1
+#define DMA_IDLEMODE_FORCE_IDLE                        0x0
+
 /* Chaining modes*/
 #ifndef CONFIG_ARCH_OMAP1
 #define OMAP_DMA_STATIC_CHAIN          0x1
 /* Chaining modes*/
 #ifndef CONFIG_ARCH_OMAP1
 #define OMAP_DMA_STATIC_CHAIN          0x1
diff --git a/arch/arm/plat-omap/include/mach/eac.h b/arch/arm/plat-omap/include/mach/eac.h
deleted file mode 100644 (file)
index 9e62cf0..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * arch/arm/plat-omap/include/mach2/eac.h
- *
- * Defines for Enhanced Audio Controller
- *
- * Contact: Jarkko Nikula <jarkko.nikula@nokia.com>
- *
- * Copyright (C) 2006 Nokia Corporation
- * Copyright (C) 2004 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.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#ifndef __ASM_ARM_ARCH_OMAP2_EAC_H
-#define __ASM_ARM_ARCH_OMAP2_EAC_H
-
-#include <mach/io.h>
-#include <mach/hardware.h>
-#include <asm/irq.h>
-
-#include <sound/core.h>
-
-/* master codec clock source */
-#define EAC_MCLK_EXT_MASK      0x100
-enum eac_mclk_src {
-       EAC_MCLK_INT_11290000, /* internal 96 MHz / 8.5 = 11.29 Mhz */
-       EAC_MCLK_EXT_11289600 = EAC_MCLK_EXT_MASK,
-       EAC_MCLK_EXT_12288000,
-       EAC_MCLK_EXT_2x11289600,
-       EAC_MCLK_EXT_2x12288000,
-};
-
-/* codec port interface mode */
-enum eac_codec_mode {
-       EAC_CODEC_PCM,
-       EAC_CODEC_AC97,
-       EAC_CODEC_I2S_MASTER, /* codec port, I.e. EAC is the master */
-       EAC_CODEC_I2S_SLAVE,
-};
-
-/* configuration structure for I2S mode */
-struct eac_i2s_conf {
-       /* if enabled, then first data slot (left channel) is signaled as
-        * positive level of frame sync EAC.AC_FS */
-       unsigned        polarity_changed_mode:1;
-       /* if enabled, then serial data starts one clock cycle after the
-        * of EAC.AC_FS for first audio slot */
-       unsigned        sync_delay_enable:1;
-};
-
-/* configuration structure for EAC codec port */
-struct eac_codec {
-       enum eac_mclk_src       mclk_src;
-
-       enum eac_codec_mode     codec_mode;
-       union {
-               struct eac_i2s_conf     i2s;
-       } codec_conf;
-
-       int             default_rate; /* audio sampling rate */
-
-       int             (* set_power)(void *private_data, int dac, int adc);
-       int             (* register_controls)(void *private_data,
-                                             struct snd_card *card);
-       const char      *short_name;
-
-       void            *private_data;
-};
-
-/* structure for passing platform dependent data to the EAC driver */
-struct eac_platform_data {
-        int    (* init)(struct device *eac_dev);
-       void    (* cleanup)(struct device *eac_dev);
-       /* these callbacks are used to configure & control external MCLK
-        * source. NULL if not used */
-       int     (* enable_ext_clocks)(struct device *eac_dev);
-       void    (* disable_ext_clocks)(struct device *eac_dev);
-};
-
-extern void omap_init_eac(struct eac_platform_data *pdata);
-
-extern int eac_register_codec(struct device *eac_dev, struct eac_codec *codec);
-extern void eac_unregister_codec(struct device *eac_dev);
-
-extern int eac_set_mode(struct device *eac_dev, int play, int rec);
-
-#endif /* __ASM_ARM_ARCH_OMAP2_EAC_H */
index 10da0e07c0cf8998b926a3680b042936e77e5129..2096780660937ceef27eb24e0b437ab32af88bc1 100644 (file)
  *     low  -> inactive
  *
  */
  *     low  -> inactive
  *
  */
-#define OMAP_GPIO_SWITCH_TYPE_COVER            0x0000
-#define OMAP_GPIO_SWITCH_TYPE_CONNECTION       0x0001
-#define OMAP_GPIO_SWITCH_TYPE_ACTIVITY         0x0002
-#define OMAP_GPIO_SWITCH_FLAG_INVERTED         0x0001
-#define OMAP_GPIO_SWITCH_FLAG_OUTPUT           0x0002
+#define OMAP_GPIO_SWITCH_TYPE_COVER                    0x0000
+#define OMAP_GPIO_SWITCH_TYPE_CONNECTION               0x0001
+#define OMAP_GPIO_SWITCH_TYPE_ACTIVITY                 0x0002
+#define OMAP_GPIO_SWITCH_FLAG_INVERTED                 0x0001
+#define OMAP_GPIO_SWITCH_FLAG_OUTPUT                   0x0002
+#define OMAP_GPIO_SWITCH_FLAG_OUTPUT_INIT_ACTIVE       0x0004
 
 struct omap_gpio_switch {
        const char *name;
 
 struct omap_gpio_switch {
        const char *name;
@@ -48,7 +49,11 @@ struct omap_gpio_switch {
 };
 
 /* Call at init time only */
 };
 
 /* Call at init time only */
+#ifdef CONFIG_OMAP_GPIO_SWITCH
 extern void omap_register_gpio_switches(const struct omap_gpio_switch *tbl,
                                        int count);
 extern void omap_register_gpio_switches(const struct omap_gpio_switch *tbl,
                                        int count);
+#else
+#define omap_register_gpio_switches(tbl, count)        do { } while (0)
+#endif
 
 #endif
 
 #endif
diff --git a/arch/arm/plat-omap/include/mach/gpioexpander.h b/arch/arm/plat-omap/include/mach/gpioexpander.h
deleted file mode 100644 (file)
index 90444a0..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * arch/arm/plat-omap/include/mach/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 */
-
-#ifdef CONFIG_GPIOEXPANDER_OMAP
-int read_gpio_expa(u8 *, int);
-int write_gpio_expa(u8 , int);
-#else
-static inline int read_gpio_expa(u8 *val, int addr)
-{
-       return 0;
-}
-static inline int write_gpio_expa(u8 val, int addr)
-{
-       return 0;
-}
-#endif
-
-#endif /* __ASM_ARCH_OMAP_GPIOEXPANDER_H */
index 921b16532ff55c4e28301cb5bcce975de546a70b..b0b2edf8c550615372c50f538da9aaf3c49a26a3 100644 (file)
 #define GPMC_CS_NAND_ADDRESS   0x20
 #define GPMC_CS_NAND_DATA      0x24
 
 #define GPMC_CS_NAND_ADDRESS   0x20
 #define GPMC_CS_NAND_DATA      0x24
 
+/*
+ * The following gpmc registers are being used by
+ * nand driver and hence is defined here.
+ * TBD: Move them to gpmc.c by providing appropriate
+ *  methods to read and write into these registers
+ */
+#define GPMC_IRQSTATUS         0x18
 #define GPMC_CONFIG            0x50
 #define GPMC_STATUS            0x54
 #define GPMC_CONFIG            0x50
 #define GPMC_STATUS            0x54
+#define GPMC_CS0_BASE           0x60
+#define GPMC_CS_SIZE            0x30
+
 
 #define GPMC_CONFIG1_WRAPBURST_SUPP     (1 << 31)
 #define GPMC_CONFIG1_READMULTIPLE_SUPP  (1 << 30)
 
 #define GPMC_CONFIG1_WRAPBURST_SUPP     (1 << 31)
 #define GPMC_CONFIG1_READMULTIPLE_SUPP  (1 << 30)
index 7f57ee66f364dd0ad65092f58d62066c95a3604d..9499a0520e0f5554de292d4cef2e757fd0b4d22e 100644 (file)
 #define INT_34XX_MMC3_IRQ      94
 #define INT_34XX_GPT12_IRQ     95
 
 #define INT_34XX_MMC3_IRQ      94
 #define INT_34XX_GPT12_IRQ     95
 
-#define        INT_34XX_BENCH_MPU_EMUL 3
-
 /* Max. 128 level 2 IRQs (OMAP1610), 192 GPIOs (OMAP730/850) and
  * 16 MPUIO lines */
 #define OMAP_MAX_GPIO_LINES    192
 /* Max. 128 level 2 IRQs (OMAP1610), 192 GPIOs (OMAP730/850) and
  * 16 MPUIO lines */
 #define OMAP_MAX_GPIO_LINES    192
 
 #ifndef __ASSEMBLY__
 extern void omap_init_irq(void);
 
 #ifndef __ASSEMBLY__
 extern void omap_init_irq(void);
+extern int omap_irq_pending(void);
 #endif
 
 #include <mach/hardware.h>
 #endif
 
 #include <mach/hardware.h>
index 232923aaf61d34ed0f66daf5af54d1fad7c73a13..b7f270a6782188a471469a366c7ea86eac45ccbb 100644 (file)
@@ -14,6 +14,7 @@ struct omap_kp_platform_data {
        int rows;
        int cols;
        int *keymap;
        int rows;
        int cols;
        int *keymap;
+       int irq;
        unsigned int keymapsize;
        unsigned int rep:1;
        unsigned long delay;
        unsigned int keymapsize;
        unsigned int rep:1;
        unsigned long delay;
@@ -33,7 +34,12 @@ struct omap_kp_platform_data {
 #define GROUP_3                (3 << 16)
 #define GROUP_MASK     GROUP_3
 
 #define GROUP_3                (3 << 16)
 #define GROUP_MASK     GROUP_3
 
+#define ROWCOL_MASK            0xFF000000
+#define KEY_PERSISTENT         0x00800000
+#define KEYNUM_MASK            0x00EFFFFF
 #define KEY(col, row, val) (((col) << 28) | ((row) << 24) | (val))
 #define KEY(col, row, val) (((col) << 28) | ((row) << 24) | (val))
+#define PERSISTENT_KEY(col, row) (((col) << 28) | ((row) << 24) | \
+                                               KEY_PERSISTENT)
 
 #endif
 
 
 #endif
 
index 4435bd434e179b995adfbec2944ac24436aa6119..81d5b36534b340680ffc36fde156e9cafbb70e2d 100644 (file)
@@ -79,7 +79,6 @@ struct omap_mmc_platform_data {
 
                /* use the internal clock */
                unsigned internal_clock:1;
 
                /* use the internal clock */
                unsigned internal_clock:1;
-               s16 power_pin;
 
                int switch_pin;                 /* gpio (card detect) */
                int gpio_wp;                    /* gpio (write protect) */
 
                int switch_pin;                 /* gpio (card detect) */
                int gpio_wp;                    /* gpio (write protect) */
diff --git a/arch/arm/plat-omap/include/mach/sti.h b/arch/arm/plat-omap/include/mach/sti.h
new file mode 100644 (file)
index 0000000..af43917
--- /dev/null
@@ -0,0 +1,172 @@
+#ifndef __ASM_ARCH_OMAP_STI_H
+#define __ASM_ARCH_OMAP_STI_H
+
+#include <asm/io.h>
+
+/*
+ * STI/SDTI
+ */
+#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_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 */
+};
+
+#define STI_PERCHANNEL_SIZE    4
+
+#define to_channel_address(channel) \
+       (sti_channel_base + STI_PERCHANNEL_SIZE * (channel))
+
+#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)
+
+#define to_channel_address(channel) \
+       (sti_channel_base + STI_PERCHANNEL_SIZE * (channel))
+
+#elif defined(CONFIG_ARCH_OMAP3)
+
+#define STI_PERCHANNEL_SIZE    0x1000
+#define to_channel_address(channel) \
+       (sti_channel_base + STI_PERCHANNEL_SIZE * (channel) + 0x800)
+
+#endif
+
+/* arch/arm/plat-omap/sti/sti.c */
+extern void __iomem *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);
+}
+
+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 */
index 69f0ceed500bd174467e3b1cd7e579d160c9cc9e..f6d334ffaae85987e6e74b9576463a89a1bde821 100644 (file)
@@ -33,9 +33,17 @@ extern void usb_musb_init(void);
 static inline void usb_musb_init(void)
 {
 }
 static inline void usb_musb_init(void)
 {
 }
-#endif
+#endif /* !OMAP1 && !MUSB */
 
 
-#endif
+#if defined(CONFIG_USB_EHCI_HCD) || defined(CONFIG_USB_EHCI_HCD_MODULE)
+extern void usb_ehci_init(void);
+#else
+static inline void usb_ehci_init(void)
+{
+}
+#endif /* !OMAP1 && !EHCI */
+
+#endif /* !OMAP1 */
 
 void omap_usb_init(struct omap_usb_config *pdata);
 
 
 void omap_usb_init(struct omap_usb_config *pdata);
 
index dc104cd96197ce63e983cbd4139830bbe36ce29b..b97dfafeebda6179a5ef652fb0e8070a2ef4b3ae 100644 (file)
@@ -17,5 +17,5 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
-#define VMALLOC_END      (PAGE_OFFSET + 0x10000000)
+#define VMALLOC_END      (PAGE_OFFSET + 0x18000000)
 
 
index fa5297d643d3f545d181551bdee853a2a0938251..e1e637f222c6673fb102c588eb0ef6616ba36462 100644 (file)
@@ -38,8 +38,8 @@
 #define OMAP1_SRAM_VA          VMALLOC_END
 #define OMAP2_SRAM_PA          0x40200000
 #define OMAP2_SRAM_PUB_PA      0x4020f800
 #define OMAP1_SRAM_VA          VMALLOC_END
 #define OMAP2_SRAM_PA          0x40200000
 #define OMAP2_SRAM_PUB_PA      0x4020f800
-#define OMAP2_SRAM_VA          VMALLOC_END
-#define OMAP2_SRAM_PUB_VA      (VMALLOC_END + 0x800)
+#define OMAP2_SRAM_VA          0xe3000000
+#define OMAP2_SRAM_PUB_VA      (OMAP2_SRAM_VA + 0x800)
 #define OMAP3_SRAM_PA           0x40200000
 #define OMAP3_SRAM_VA           0xd7000000
 #define OMAP3_SRAM_PUB_PA       0x40208000
 #define OMAP3_SRAM_PA           0x40200000
 #define OMAP3_SRAM_VA           0xd7000000
 #define OMAP3_SRAM_PUB_PA       0x40208000
@@ -359,14 +359,14 @@ static u32 (*_omap3_sram_configure_core_dpll)(u32 sdrc_rfr_ctrl,
                                              u32 m2);
 u32 omap3_configure_core_dpll(u32 sdrc_rfr_ctrl, u32 sdrc_actim_ctrla,
                              u32 sdrc_actim_ctrlb, u32 m2)
                                              u32 m2);
 u32 omap3_configure_core_dpll(u32 sdrc_rfr_ctrl, u32 sdrc_actim_ctrla,
                              u32 sdrc_actim_ctrlb, u32 m2)
-{
+ {
        if (!_omap3_sram_configure_core_dpll)
                omap_sram_error();
 
        return _omap3_sram_configure_core_dpll(sdrc_rfr_ctrl,
                                               sdrc_actim_ctrla,
                                               sdrc_actim_ctrlb, m2);
        if (!_omap3_sram_configure_core_dpll)
                omap_sram_error();
 
        return _omap3_sram_configure_core_dpll(sdrc_rfr_ctrl,
                                               sdrc_actim_ctrla,
                                               sdrc_actim_ctrlb, m2);
-}
+ }
 
 /* REVISIT: Should this be same as omap34xx_sram_init() after off-idle? */
 void restore_sram_functions(void)
 
 /* REVISIT: Should this be same as omap34xx_sram_init() after off-idle? */
 void restore_sram_functions(void)
@@ -378,7 +378,7 @@ void restore_sram_functions(void)
                               omap3_sram_configure_core_dpll_sz);
 }
 
                               omap3_sram_configure_core_dpll_sz);
 }
 
-int __init omap34xx_sram_init(void)
+int __init omap3_sram_init(void)
 {
        _omap3_sram_configure_core_dpll =
                omap_sram_push(omap3_sram_configure_core_dpll,
 {
        _omap3_sram_configure_core_dpll =
                omap_sram_push(omap3_sram_configure_core_dpll,
@@ -387,7 +387,7 @@ int __init omap34xx_sram_init(void)
        return 0;
 }
 #else
        return 0;
 }
 #else
-static inline int omap34xx_sram_init(void)
+static inline int omap3_sram_init(void)
 {
        return 0;
 }
 {
        return 0;
 }
@@ -405,7 +405,7 @@ int __init omap_sram_init(void)
        else if (cpu_is_omap2430())
                omap243x_sram_init();
        else if (cpu_is_omap34xx())
        else if (cpu_is_omap2430())
                omap243x_sram_init();
        else if (cpu_is_omap34xx())
-               omap34xx_sram_init();
+               omap3_sram_init();
 
        return 0;
 }
 
        return 0;
 }
index 1ba9e471ca456c335d5eccb31ca55ca6d60d799d..625f69d8ecdc7f618e96e06efd0eba2edaaf03a3 100644 (file)
@@ -36,9 +36,13 @@ obj-$(CONFIG_FB_INTEL)          += video/intelfb/
 
 obj-y                          += cbus/
 
 
 obj-y                          += cbus/
 
+# we also need input/serio early so serio bus is initialized by the time
+# serial drivers start registering their serio ports
+obj-$(CONFIG_SERIO)            += input/serio/
 obj-y                          += serial/
 obj-$(CONFIG_PARPORT)          += parport/
 obj-y                          += base/ block/ misc/ mfd/ media/
 obj-y                          += serial/
 obj-$(CONFIG_PARPORT)          += parport/
 obj-y                          += base/ block/ misc/ mfd/ media/
+obj-y                          += i2c/
 obj-$(CONFIG_NUBUS)            += nubus/
 obj-y                          += macintosh/
 obj-$(CONFIG_IDE)              += ide/
 obj-$(CONFIG_NUBUS)            += nubus/
 obj-y                          += macintosh/
 obj-$(CONFIG_IDE)              += ide/
@@ -68,12 +72,10 @@ obj-$(CONFIG_USB)           += usb/
 obj-$(CONFIG_USB_MUSB_HDRC)    += usb/musb/
 obj-$(CONFIG_PCI)              += usb/
 obj-$(CONFIG_USB_GADGET)       += usb/gadget/
 obj-$(CONFIG_USB_MUSB_HDRC)    += usb/musb/
 obj-$(CONFIG_PCI)              += usb/
 obj-$(CONFIG_USB_GADGET)       += usb/gadget/
-obj-$(CONFIG_SERIO)            += input/serio/
 obj-$(CONFIG_GAMEPORT)         += input/gameport/
 obj-$(CONFIG_INPUT)            += input/
 obj-$(CONFIG_I2O)              += message/
 obj-$(CONFIG_RTC_LIB)          += rtc/
 obj-$(CONFIG_GAMEPORT)         += input/gameport/
 obj-$(CONFIG_INPUT)            += input/
 obj-$(CONFIG_I2O)              += message/
 obj-$(CONFIG_RTC_LIB)          += rtc/
-obj-y                          += i2c/
 obj-$(CONFIG_W1)               += w1/
 obj-$(CONFIG_POWER_SUPPLY)     += power/
 obj-$(CONFIG_HWMON)            += hwmon/
 obj-$(CONFIG_W1)               += w1/
 obj-$(CONFIG_POWER_SUPPLY)     += power/
 obj-$(CONFIG_HWMON)            += hwmon/
index 1164837bb781470730768c37f466a17f091095d3..68eff964e53b56ffbda97526f0d0344f2a9adf5e 100644 (file)
@@ -161,6 +161,27 @@ 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).
 
          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_HCIH4P
+       tristate "HCI driver with H4 Nokia extensions"
+       depends on BT && ARCH_OMAP 
+       help 
+         Bluetooth HCI driver with H4 extensions.  This driver provides
+         support for H4+ Bluetooth chip with vendor-specific H4 extensions.
+
+         Say Y here to compile support for h4 extended devices into the kernel
+         or say M to compile it as module (hci_h4p).
+
 config BT_HCIVHCI
        tristate "HCI VHCI (Virtual HCI device) driver"
        help
 config BT_HCIVHCI
        tristate "HCI VHCI (Virtual HCI device) driver"
        help
index 16930f93d1ca4db6ffaf08df3e2f5fb4132ea076..9aebb460a34777ed9e942824d3b338c3104fe512 100644 (file)
@@ -11,6 +11,8 @@ 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_HCIBT3C)       += bt3c_cs.o
 obj-$(CONFIG_BT_HCIBLUECARD)   += bluecard_cs.o
 obj-$(CONFIG_BT_HCIBTUART)     += btuart_cs.o
+obj-$(CONFIG_BT_HCIBRF6150)    += brf6150.o
+obj-$(CONFIG_BT_HCIH4P)                += hci_h4p/
 
 obj-$(CONFIG_BT_HCIBTUSB)      += btusb.o
 obj-$(CONFIG_BT_HCIBTSDIO)     += btsdio.o
 
 obj-$(CONFIG_BT_HCIBTUSB)      += btusb.o
 obj-$(CONFIG_BT_HCIBTSDIO)     += btsdio.o
diff --git a/drivers/bluetooth/brf6150.c b/drivers/bluetooth/brf6150.c
new file mode 100644 (file)
index 0000000..211fa5e
--- /dev/null
@@ -0,0 +1,1051 @@
+/*
+ *  linux/drivers/bluetooth/brf6150/brf6150.c
+ *
+ *  Copyright (C) 2005 Nokia Corporation
+ *  Written by Ville Tervo <ville.tervo@nokia.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
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/serial_reg.h>
+#include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include <linux/irq.h>
+#include <linux/timer.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+
+#include <mach/hardware.h>
+#include <mach/board.h>
+#include <mach/irqs.h>
+#include <mach/board-nokia.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#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;
+               gpio_set_value(info->btinfo->bt_wakeup_gpio, 1);
+       }
+       if (gpio_get_value(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))
+               gpio_set_value(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 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 brf6150_info *info = dev_inst;
+       int should_wakeup;
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock, flags);
+       should_wakeup = gpio_get_value(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)
+{
+       gpio_set_value(info->btinfo->bt_wakeup_gpio, 0);
+       gpio_set_value(info->btinfo->reset_gpio, 0);
+       current->state = TASK_UNINTERRUPTIBLE;
+       schedule_timeout(msecs_to_jiffies(10));
+       gpio_set_value(info->btinfo->bt_wakeup_gpio, 1);
+       current->state = TASK_UNINTERRUPTIBLE;
+       schedule_timeout(msecs_to_jiffies(100));
+       gpio_set_value(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(gpio_to_irq(info->btinfo->host_wakeup_gpio), IRQ_TYPE_NONE);
+       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(gpio_to_irq(info->btinfo->host_wakeup_gpio), IRQ_TYPE_EDGE_BOTH);
+       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);
+       gpio_set_value(info->btinfo->bt_wakeup_gpio, 0);
+       set_irq_type(gpio_to_irq(info->btinfo->host_wakeup_gpio), IRQ_TYPE_NONE);
+
+       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 = gpio_request(info->btinfo->reset_gpio, "BT reset");
+       if (err < 0)
+       {
+               printk(KERN_WARNING "Cannot get GPIO line %d", 
+                      info->btinfo->reset_gpio);
+               kfree(info);
+               return err;
+       }
+
+       err = gpio_request(info->btinfo->bt_wakeup_gpio, "BT wakeup");
+       if (err < 0)
+       {
+               printk(KERN_WARNING "Cannot get GPIO line 0x%d",
+                      info->btinfo->bt_wakeup_gpio);
+               gpio_free(info->btinfo->reset_gpio);
+               kfree(info);
+               return err;
+       }
+
+       err = gpio_request(info->btinfo->host_wakeup_gpio, "BT host wakeup");
+       if (err < 0)
+       {
+               printk(KERN_WARNING "Cannot get GPIO line %d",
+                      info->btinfo->host_wakeup_gpio);
+               gpio_free(info->btinfo->reset_gpio);
+               gpio_free(info->btinfo->bt_wakeup_gpio);
+               kfree(info);
+               return err;
+       }
+
+       gpio_direction_output(info->btinfo->reset_gpio, 0);
+       gpio_direction_output(info->btinfo->bt_wakeup_gpio, 0);
+       gpio_direction_input(info->btinfo->host_wakeup_gpio);
+       set_irq_type(gpio_to_irq(info->btinfo->host_wakeup_gpio), IRQ_TYPE_NONE);
+
+       switch (info->btinfo->bt_uart) {
+       case 1:
+               irq = INT_UART1;
+               info->uart_ck = clk_get(NULL, "uart1_ck");
+               /* FIXME: Use platform_get_resource for the port */
+               info->uart_base = ioremap(OMAP_UART1_BASE, 0x16);
+               if (!info->uart_base)
+                       goto cleanup;
+               break;
+       case 2:
+               irq = INT_UART2;
+               info->uart_ck = clk_get(NULL, "uart2_ck");
+               /* FIXME: Use platform_get_resource for the port */
+               info->uart_base = ioremap(OMAP_UART2_BASE, 0x16);
+               if (!info->uart_base)
+                       goto cleanup;
+               break;
+       case 3:
+               irq = INT_UART3;
+               info->uart_ck = clk_get(NULL, "uart3_ck");
+               /* FIXME: Use platform_get_resource for the port */
+               info->uart_base = ioremap(OMAP_UART3_BASE, 0x16);
+               if (!info->uart_base)
+                       goto cleanup;
+               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(gpio_to_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",
+                               gpio_to_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(gpio_to_irq(info->btinfo->host_wakeup_gpio), (void *)info);
+cleanup:
+       gpio_free(info->btinfo->reset_gpio);
+       gpio_free(info->btinfo->bt_wakeup_gpio);
+       gpio_free(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);
+       gpio_free(exit_info->btinfo->reset_gpio);
+       gpio_free(exit_info->btinfo->bt_wakeup_gpio);
+       gpio_free(exit_info->btinfo->host_wakeup_gpio);
+       free_irq(exit_info->irq, (void *)exit_info);
+       free_irq(gpio_to_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 <ville.tervo@nokia.com>");
diff --git a/drivers/bluetooth/brf6150.h b/drivers/bluetooth/brf6150.h
new file mode 100644 (file)
index 0000000..e8b2988
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ *  linux/drivers/bluetooth/brf6150/brf6150.h
+ *
+ *  Copyright (C) 2005 Nokia Corporation
+ *  Written by Ville Tervo <ville.tervo@nokia.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
+ */
+
+#include <mach/board.h>
+
+#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/bluetooth/hci_h4p/Makefile b/drivers/bluetooth/hci_h4p/Makefile
new file mode 100644 (file)
index 0000000..07608a4
--- /dev/null
@@ -0,0 +1,7 @@
+#
+# Makefile for the Linux Bluetooth HCI device drivers.
+#
+
+obj-$(CONFIG_BT_HCIH4P)                += hci_h4p.o
+
+hci_h4p-objs := core.o fw.o uart.o sysfs.o fw-ti.o fw-csr.o
diff --git a/drivers/bluetooth/hci_h4p/core.c b/drivers/bluetooth/hci_h4p/core.c
new file mode 100644 (file)
index 0000000..a5b76ad
--- /dev/null
@@ -0,0 +1,1013 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation.
+ *
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/spinlock.h>
+#include <linux/serial_reg.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+
+#include <mach/hardware.h>
+#include <mach/board.h>
+#include <mach/irqs.h>
+#include <mach/pm.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#include "hci_h4p.h"
+
+#define PM_TIMEOUT 200
+
+/* This should be used in function that cannot release clocks */
+static void hci_h4p_set_clk(struct hci_h4p_info *info, int *clock, int enable)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->clocks_lock, flags);
+       if (enable && !*clock) {
+               NBT_DBG_POWER("Enabling %p\n", clock);
+               clk_enable(info->uart_fclk);
+#ifdef CONFIG_ARCH_OMAP2
+               if (cpu_is_omap24xx()) {
+                       clk_enable(info->uart_iclk);
+                       omap2_block_sleep();
+               }
+#endif
+       }
+       if (!enable && *clock) {
+               NBT_DBG_POWER("Disabling %p\n", clock);
+               clk_disable(info->uart_fclk);
+#ifdef CONFIG_ARCH_OMAP2
+               if (cpu_is_omap24xx()) {
+                       clk_disable(info->uart_iclk);
+                       omap2_allow_sleep();
+               }
+#endif
+       }
+
+       *clock = enable;
+       spin_unlock_irqrestore(&info->clocks_lock, flags);
+}
+
+/* Power management functions */
+static void hci_h4p_disable_tx(struct hci_h4p_info *info)
+{
+       NBT_DBG_POWER("\n");
+
+       if (!info->pm_enabled)
+               return;
+
+       mod_timer(&info->tx_pm_timer, jiffies + msecs_to_jiffies(PM_TIMEOUT));
+}
+
+static void hci_h4p_enable_tx(struct hci_h4p_info *info)
+{
+       NBT_DBG_POWER("\n");
+
+       if (!info->pm_enabled)
+               return;
+
+       del_timer_sync(&info->tx_pm_timer);
+       if (info->tx_pm_enabled) {
+               info->tx_pm_enabled = 0;
+               hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+               gpio_set_value(info->bt_wakeup_gpio, 1);
+       }
+}
+
+static void hci_h4p_tx_pm_timer(unsigned long data)
+{
+       struct hci_h4p_info *info;
+
+       NBT_DBG_POWER("\n");
+
+       info = (struct hci_h4p_info *)data;
+
+       if (hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT) {
+               gpio_set_value(info->bt_wakeup_gpio, 0);
+               hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+               info->tx_pm_enabled = 1;
+       }
+       else {
+               mod_timer(&info->tx_pm_timer, jiffies + msecs_to_jiffies(PM_TIMEOUT));
+       }
+}
+
+static void hci_h4p_disable_rx(struct hci_h4p_info *info)
+{
+       if (!info->pm_enabled)
+               return;
+
+       mod_timer(&info->rx_pm_timer, jiffies + msecs_to_jiffies(PM_TIMEOUT));
+}
+
+static void hci_h4p_enable_rx(struct hci_h4p_info *info)
+{
+       unsigned long flags;
+
+       if (!info->pm_enabled)
+               return;
+
+       del_timer_sync(&info->rx_pm_timer);
+       spin_lock_irqsave(&info->lock, flags);
+       if (info->rx_pm_enabled) {
+               hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+               hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) | UART_IER_RDI);
+               __hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
+               info->rx_pm_enabled = 0;
+       }
+       spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static void hci_h4p_rx_pm_timer(unsigned long data)
+{
+       unsigned long flags;
+       struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+
+       spin_lock_irqsave(&info->lock, flags);
+       if (!(hci_h4p_inb(info, UART_LSR) & UART_LSR_DR)) {
+               __hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
+               hci_h4p_set_rts(info, 0);
+               hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) & ~UART_IER_RDI);
+               hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+               info->rx_pm_enabled = 1;
+       }
+       else {
+               mod_timer(&info->rx_pm_timer, jiffies + msecs_to_jiffies(PM_TIMEOUT));
+       }
+       spin_unlock_irqrestore(&info->lock, flags);
+}
+
+/* Negotiation functions */
+int hci_h4p_send_alive_packet(struct hci_h4p_info *info)
+{
+       NBT_DBG("Sending alive packet\n");
+
+       if (!info->alive_cmd_skb)
+               return -EINVAL;
+
+       /* Keep reference to buffer so we can reuse it */
+       info->alive_cmd_skb = skb_get(info->alive_cmd_skb);
+
+       skb_queue_tail(&info->txq, info->alive_cmd_skb);
+       tasklet_schedule(&info->tx_task);
+
+       NBT_DBG("Alive packet sent\n");
+
+       return 0;
+}
+
+static void hci_h4p_alive_packet(struct hci_h4p_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 hci_h4p_send_negotiation(struct hci_h4p_info *info, struct sk_buff *skb)
+{
+       NBT_DBG("Sending negotiation..\n");
+
+       hci_h4p_change_speed(info, INIT_SPEED);
+
+       info->init_error = 0;
+       init_completion(&info->init_completion);
+       skb_queue_tail(&info->txq, skb);
+       tasklet_schedule(&info->tx_task);
+
+       if (!wait_for_completion_interruptible_timeout(&info->init_completion,
+                               msecs_to_jiffies(1000))) 
+               return -ETIMEDOUT;
+
+       NBT_DBG("Negotiation sent\n");
+       return info->init_error;
+}
+
+static void hci_h4p_negotiation_packet(struct hci_h4p_info *info,
+                                      struct sk_buff *skb)
+{
+       int err = 0;
+
+       if (skb->data[1] == 0x20) {
+               /* Change to operational settings */
+               hci_h4p_set_rts(info, 0);
+
+               err = hci_h4p_wait_for_cts(info, 0, 100);
+               if (err < 0)
+                       goto neg_ret;
+
+               hci_h4p_change_speed(info, MAX_BAUD_RATE);
+
+               err = hci_h4p_wait_for_cts(info, 1, 100);
+               if (err < 0)
+                       goto neg_ret;
+
+               hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_CTS | UART_EFR_RTS);
+
+               err = hci_h4p_send_alive_packet(info);
+               if (err < 0)
+                       goto neg_ret;
+       } else {
+               dev_err(info->dev, "Could not negotiate hci_h4p settings\n");
+               err = -EINVAL;
+               goto neg_ret;
+       }
+
+       kfree_skb(skb);
+       return;
+
+neg_ret:
+       info->init_error = err;
+       complete(&info->init_completion);
+       kfree_skb(skb);
+}
+
+/* H4 packet handling functions */
+static int hci_h4p_get_hdr_len(struct hci_h4p_info *info, 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 = 11;
+               break;
+       case H4_ALIVE_PKT:
+               retval = 3;
+               break;
+       default:
+               dev_err(info->dev, "Unknown H4 packet type 0x%.2x\n", pkt_type);
+               retval = -1;
+               break;
+       }
+
+       return retval;
+}
+
+static unsigned int hci_h4p_get_data_len(struct hci_h4p_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 inline void hci_h4p_recv_frame(struct hci_h4p_info *info,
+                                     struct sk_buff *skb)
+{
+
+       if (unlikely(!test_bit(HCI_RUNNING, &info->hdev->flags))) {
+               NBT_DBG("fw_event\n");
+               hci_h4p_parse_fw_event(info, skb);
+       } else {
+               hci_recv_frame(skb);
+               NBT_DBG("Frame sent to upper layer\n");
+       }
+}
+
+static void hci_h4p_rx_tasklet(unsigned long data)
+{
+       u8 byte;
+       unsigned long flags;
+       struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+
+       NBT_DBG("tasklet woke up\n");
+       NBT_DBG_TRANSFER("rx_tasklet woke up\ndata ");
+
+       while (hci_h4p_inb(info, UART_LSR) & UART_LSR_DR) {
+               byte = hci_h4p_inb(info, UART_RX);
+               if (info->garbage_bytes) {
+                       info->garbage_bytes--;
+                       continue;
+               }
+               if (info->rx_skb == NULL) {
+                       info->rx_skb = bt_skb_alloc(HCI_MAX_FRAME_SIZE, GFP_ATOMIC | GFP_DMA);
+                       if (!info->rx_skb) {
+                               dev_err(info->dev, "Can't allocate memory for new packet\n");
+                               goto finish_task;
+                       }
+                       info->rx_state = WAIT_FOR_PKT_TYPE;
+                       info->rx_skb->dev = (void *)info->hdev;
+               }
+               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 = hci_h4p_get_hdr_len(info, byte);
+                       if (info->rx_count < 0) {
+                               info->hdev->stat.err_rx++;
+                               kfree_skb(info->rx_skb);
+                               info->rx_skb = NULL;
+                       } else {
+                               info->rx_state = WAIT_FOR_HEADER;
+                       }
+                       break;
+               case WAIT_FOR_HEADER:
+                       info->rx_count--;
+                       *skb_put(info->rx_skb, 1) = byte;
+                       if (info->rx_count == 0) {
+                               info->rx_count = hci_h4p_get_data_len(info, info->rx_skb);
+                               if (info->rx_count > skb_tailroom(info->rx_skb)) {
+                                       dev_err(info->dev, "Frame is %ld bytes too long.\n",
+                                              info->rx_count - skb_tailroom(info->rx_skb));
+                                       kfree_skb(info->rx_skb);
+                                       info->rx_skb = NULL;
+                                       info->garbage_bytes = info->rx_count - skb_tailroom(info->rx_skb);
+                                       break;
+                               }
+                               info->rx_state = WAIT_FOR_DATA;
+
+                               if (bt_cb(info->rx_skb)->pkt_type == H4_NEG_PKT) {
+                                       hci_h4p_negotiation_packet(info, info->rx_skb);
+                                       info->rx_skb = NULL;
+                                       info->rx_state = WAIT_FOR_PKT_TYPE;
+                                       goto finish_task;
+                               }
+                               if (bt_cb(info->rx_skb)->pkt_type == H4_ALIVE_PKT) {
+                                       hci_h4p_alive_packet(info, info->rx_skb);
+                                       info->rx_skb = NULL;
+                                       info->rx_state = WAIT_FOR_PKT_TYPE;
+                                       goto finish_task;
+                               }
+                       }
+                       break;
+               case WAIT_FOR_DATA:
+                       info->rx_count--;
+                       *skb_put(info->rx_skb, 1) = byte;
+                       if (info->rx_count == 0) {
+                               /* H4+ devices should allways send word aligned packets */
+                               if (!(info->rx_skb->len % 2)) {
+                                       info->garbage_bytes++;
+                               }
+                               hci_h4p_recv_frame(info, info->rx_skb);
+                               info->rx_skb = NULL;
+                       }
+                       break;
+               default:
+                       WARN_ON(1);
+                       break;
+               }
+       }
+
+finish_task:
+       spin_lock_irqsave(&info->lock, flags);
+       hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) | UART_IER_RDI);
+       spin_unlock_irqrestore(&info->lock, flags);
+
+       NBT_DBG_TRANSFER_NF("\n");
+       NBT_DBG("rx_ended\n");
+}
+
+static void hci_h4p_tx_tasklet(unsigned long data)
+{
+       unsigned int sent = 0;
+       unsigned long flags;
+       struct sk_buff *skb;
+       struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+
+       NBT_DBG("tasklet woke up\n");
+       NBT_DBG_TRANSFER("tx_tasklet woke up\n data ");
+
+       skb = skb_dequeue(&info->txq);
+       if (!skb) {
+               /* No data in buffer */
+               NBT_DBG("skb ready\n");
+               hci_h4p_disable_tx(info);
+               return;
+       }
+
+       /* Copy data to tx fifo */
+       while (!(hci_h4p_inb(info, UART_OMAP_SSR) & UART_OMAP_SSR_TXFULL) &&
+              (sent < skb->len)) {
+               NBT_DBG_TRANSFER_NF("0x%.2x ", skb->data[sent]);
+               hci_h4p_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);
+       } else {
+               skb_pull(skb, sent);
+               skb_queue_head(&info->txq, skb);
+       }
+
+       spin_lock_irqsave(&info->lock, flags);
+       hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) | UART_IER_THRI);
+       spin_unlock_irqrestore(&info->lock, flags);
+}
+
+static irqreturn_t hci_h4p_interrupt(int irq, void *data)
+{
+       struct hci_h4p_info *info = (struct hci_h4p_info *)data;
+       u8 iir, msr;
+       int ret;
+       unsigned long flags;
+
+       ret = IRQ_NONE;
+
+       iir = hci_h4p_inb(info, UART_IIR);
+       if (iir & UART_IIR_NO_INT) {
+               dev_err(info->dev, "Interrupt but no reason irq 0x%.2x\n", iir);
+               return IRQ_HANDLED;
+       }
+
+       NBT_DBG("In interrupt handler iir 0x%.2x\n", iir);
+
+       iir &= UART_IIR_ID;
+
+       if (iir == UART_IIR_MSI) {
+               msr = hci_h4p_inb(info, UART_MSR);
+               ret = IRQ_HANDLED;
+       }
+       if (iir == UART_IIR_RLSI) {
+               hci_h4p_inb(info, UART_RX);
+               hci_h4p_inb(info, UART_LSR);
+               ret = IRQ_HANDLED;
+       }
+
+       if (iir == UART_IIR_RDI) {
+               spin_lock_irqsave(&info->lock, flags);
+               hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) & ~UART_IER_RDI);
+               spin_unlock_irqrestore(&info->lock, flags);
+               tasklet_schedule(&info->rx_task);
+               ret = IRQ_HANDLED;
+       }
+
+       if (iir == UART_IIR_THRI) {
+               spin_lock_irqsave(&info->lock, flags);
+               hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) & ~UART_IER_THRI);
+               spin_unlock_irqrestore(&info->lock, flags);
+               tasklet_schedule(&info->tx_task);
+               ret = IRQ_HANDLED;
+       }
+
+       return ret;
+}
+
+static irqreturn_t hci_h4p_wakeup_interrupt(int irq, void *dev_inst)
+{
+       struct hci_h4p_info *info = dev_inst;
+       int should_wakeup;
+       struct hci_dev *hdev;
+
+       if (!info->hdev)
+               return IRQ_HANDLED;
+
+       hdev = info->hdev;
+
+       if (!test_bit(HCI_RUNNING, &hdev->flags))
+               return IRQ_HANDLED;
+
+       should_wakeup = gpio_get_value(info->host_wakeup_gpio);
+       NBT_DBG_POWER("gpio interrupt %d\n", should_wakeup);
+       if (should_wakeup) {
+               hci_h4p_enable_rx(info);
+       } else {
+               hci_h4p_disable_rx(info);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int hci_h4p_reset(struct hci_h4p_info *info)
+{
+       int err;
+
+       hci_h4p_init_uart(info);
+       hci_h4p_set_rts(info, 0);
+
+       gpio_set_value(info->reset_gpio, 0);
+       msleep(100);
+       gpio_set_value(info->bt_wakeup_gpio, 1);
+       gpio_set_value(info->reset_gpio, 1);
+       msleep(100);
+
+       err = hci_h4p_wait_for_cts(info, 1, 10);
+       if (err < 0) {
+               dev_err(info->dev, "No cts from bt chip\n");
+               return err;
+       }
+
+       hci_h4p_set_rts(info, 1);
+
+       return 0;
+}
+
+/* hci callback functions */
+static int hci_h4p_hci_flush(struct hci_dev *hdev)
+{
+       struct hci_h4p_info *info;
+       info = hdev->driver_data;
+
+       skb_queue_purge(&info->txq);
+
+       return 0;
+}
+
+static int hci_h4p_hci_open(struct hci_dev *hdev)
+{
+       struct hci_h4p_info *info;
+       int err;
+       struct sk_buff *neg_cmd_skb;
+       struct sk_buff_head fw_queue;
+
+       info = hdev->driver_data;
+
+       if (test_bit(HCI_RUNNING, &hdev->flags))
+               return 0;
+
+       skb_queue_head_init(&fw_queue);
+       err = hci_h4p_read_fw(info, &fw_queue);
+       if (err < 0) {
+               dev_err(info->dev, "Cannot read firmware\n");
+               return err;
+       }
+       neg_cmd_skb = skb_dequeue(&fw_queue);
+       if (!neg_cmd_skb) {
+               err = -EPROTO;
+               goto err_clean;
+       }
+       info->alive_cmd_skb = skb_dequeue(&fw_queue);
+       if (!info->alive_cmd_skb) {
+               err = -EPROTO;
+               goto err_clean;
+       }
+
+       hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+       hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+
+       tasklet_enable(&info->tx_task);
+       tasklet_enable(&info->rx_task);
+       info->rx_state = WAIT_FOR_PKT_TYPE;
+       info->rx_count = 0;
+       info->garbage_bytes = 0;
+       info->rx_skb = NULL;
+       info->pm_enabled = 0;
+       init_completion(&info->fw_completion);
+
+       err = hci_h4p_reset(info);
+       if (err < 0)
+               goto err_clean;
+
+       err = hci_h4p_send_negotiation(info, neg_cmd_skb);
+       neg_cmd_skb = NULL;
+       if (err < 0)
+               goto err_clean;
+
+       err = hci_h4p_send_fw(info, &fw_queue);
+       if (err < 0) {
+               dev_err(info->dev, "Sending firmware failed.\n");
+               goto err_clean;
+       }
+
+       kfree_skb(info->alive_cmd_skb);
+       info->alive_cmd_skb = NULL;
+       info->pm_enabled = 1;
+       info->tx_pm_enabled = 1;
+       info->rx_pm_enabled = 0;
+       set_bit(HCI_RUNNING, &hdev->flags);
+
+       NBT_DBG("hci up and running\n");
+       return 0;
+
+err_clean:
+       hci_h4p_hci_flush(hdev);
+       tasklet_disable(&info->tx_task);
+       tasklet_disable(&info->rx_task);
+       hci_h4p_reset_uart(info);
+       hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+       hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+       gpio_set_value(info->reset_gpio, 0);
+       gpio_set_value(info->bt_wakeup_gpio, 0);
+       skb_queue_purge(&fw_queue);
+       kfree_skb(neg_cmd_skb);
+       neg_cmd_skb = NULL;
+       kfree_skb(info->alive_cmd_skb);
+       info->alive_cmd_skb = NULL;
+       kfree_skb(info->rx_skb);
+
+       return err;
+}
+
+static int hci_h4p_hci_close(struct hci_dev *hdev)
+{
+       struct hci_h4p_info *info = hdev->driver_data;
+
+       if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
+               return 0;
+
+       hci_h4p_hci_flush(hdev);
+       del_timer_sync(&info->tx_pm_timer);
+       del_timer_sync(&info->rx_pm_timer);
+       tasklet_disable(&info->tx_task);
+       tasklet_disable(&info->rx_task);
+       hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+       hci_h4p_set_clk(info, &info->rx_clocks_en, 1);
+       hci_h4p_reset_uart(info);
+       hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+       hci_h4p_set_clk(info, &info->rx_clocks_en, 0);
+       gpio_set_value(info->reset_gpio, 0);
+       gpio_set_value(info->bt_wakeup_gpio, 0);
+       kfree_skb(info->rx_skb);
+
+       return 0;
+}
+
+static void hci_h4p_hci_destruct(struct hci_dev *hdev)
+{
+}
+
+static int hci_h4p_hci_send_frame(struct sk_buff *skb)
+{
+       struct hci_h4p_info *info;
+       struct hci_dev *hdev = (struct hci_dev *)skb->dev;
+       int err = 0;
+
+       if (!hdev) {
+               printk(KERN_WARNING "hci_h4p: Frame for unknown device\n");
+               return -ENODEV;
+       }
+
+       NBT_DBG("dev %p, skb %p\n", hdev, skb);
+
+       info = hdev->driver_data;
+
+       if (!test_bit(HCI_RUNNING, &hdev->flags)) {
+               dev_warn(info->dev, "Frame for non-running device\n");
+               return -EIO;
+       }
+
+       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 */
+       *skb_push(skb, 1) = (bt_cb(skb)->pkt_type);
+       /* We should allways send word aligned data to h4+ devices */
+       if (skb->len % 2) {
+               err = skb_pad(skb, 1);
+       }
+       if (err)
+               return err;
+
+       hci_h4p_enable_tx(info);
+       skb_queue_tail(&info->txq, skb);
+       tasklet_schedule(&info->tx_task);
+
+       return 0;
+}
+
+static int hci_h4p_hci_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
+{
+       return -ENOIOCTLCMD;
+}
+
+static int hci_h4p_register_hdev(struct hci_h4p_info *info)
+{
+       struct hci_dev *hdev;
+
+       /* Initialize and register HCI device */
+
+       hdev = hci_alloc_dev();
+       if (!hdev) {
+               dev_err(info->dev, "Can't allocate memory for device\n");
+               return -ENOMEM;
+       }
+       info->hdev = hdev;
+
+       hdev->type = HCI_UART;
+       hdev->driver_data = info;
+
+       hdev->open = hci_h4p_hci_open;
+       hdev->close = hci_h4p_hci_close;
+       hdev->flush = hci_h4p_hci_flush;
+       hdev->send = hci_h4p_hci_send_frame;
+       hdev->destruct = hci_h4p_hci_destruct;
+       hdev->ioctl = hci_h4p_hci_ioctl;
+
+       hdev->owner = THIS_MODULE;
+
+       if (hci_register_dev(hdev) < 0) {
+               dev_err(info->dev, "hci_h4p: Can't register HCI device %s.\n", hdev->name);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+static int hci_h4p_probe(struct platform_device *pdev)
+{
+       struct omap_bluetooth_config *bt_config;
+       struct hci_h4p_info *info;
+       int irq, err;
+
+       dev_info(&pdev->dev, "Registering HCI H4P device\n");
+       info = kzalloc(sizeof(struct hci_h4p_info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       info->dev = &pdev->dev;
+       info->pm_enabled = 0;
+       info->tx_pm_enabled = 0;
+       info->rx_pm_enabled = 0;
+       info->garbage_bytes = 0;
+       info->tx_clocks_en = 0;
+       info->rx_clocks_en = 0;
+       tasklet_init(&info->tx_task, hci_h4p_tx_tasklet, (unsigned long)info);
+       tasklet_init(&info->rx_task, hci_h4p_rx_tasklet, (unsigned long)info);
+       /* hci_h4p_hci_open assumes that tasklet is disabled in startup */
+       tasklet_disable(&info->tx_task);
+       tasklet_disable(&info->rx_task);
+       spin_lock_init(&info->lock);
+       spin_lock_init(&info->clocks_lock);
+       skb_queue_head_init(&info->txq);
+       init_timer(&info->tx_pm_timer);
+       info->tx_pm_timer.function = hci_h4p_tx_pm_timer;
+       info->tx_pm_timer.data = (unsigned long)info;
+       init_timer(&info->rx_pm_timer);
+       info->rx_pm_timer.function = hci_h4p_rx_pm_timer;
+       info->rx_pm_timer.data = (unsigned long)info;
+
+       if (pdev->dev.platform_data == NULL) {
+               dev_err(&pdev->dev, "Could not get Bluetooth config data\n");
+               return -ENODATA;
+       }
+
+       bt_config = pdev->dev.platform_data;
+       info->chip_type = bt_config->chip_type;
+       info->bt_wakeup_gpio = bt_config->bt_wakeup_gpio;
+       info->host_wakeup_gpio = bt_config->host_wakeup_gpio;
+       info->reset_gpio = bt_config->reset_gpio;
+       info->bt_sysclk = bt_config->bt_sysclk;
+
+       NBT_DBG("RESET gpio: %d\n", info->reset_gpio);
+       NBT_DBG("BTWU gpio: %d\n", info->bt_wakeup_gpio);
+       NBT_DBG("HOSTWU gpio: %d\n", info->host_wakeup_gpio);
+       NBT_DBG("Uart: %d\n", bt_config->bt_uart);
+       NBT_DBG("sysclk: %d\n", info->bt_sysclk);
+
+       err = gpio_request(info->reset_gpio, "BT reset");
+       if (err < 0) {
+               dev_err(&pdev->dev, "Cannot get GPIO line %d\n",
+                       info->reset_gpio);
+               kfree(info);
+               goto cleanup;
+       }
+
+       err = gpio_request(info->bt_wakeup_gpio, "BT wakeup");
+       if (err < 0)
+       {
+               dev_err(info->dev, "Cannot get GPIO line 0x%d",
+                       info->bt_wakeup_gpio);
+               gpio_free(info->reset_gpio);
+               kfree(info);
+               goto cleanup;
+       }
+
+       err = gpio_request(info->host_wakeup_gpio, "BT host wakeup");
+       if (err < 0)
+       {
+               dev_err(info->dev, "Cannot get GPIO line %d",
+                      info->host_wakeup_gpio);
+               gpio_free(info->reset_gpio);
+               gpio_free(info->bt_wakeup_gpio);
+               kfree(info);
+               goto cleanup;
+       }
+
+       gpio_direction_output(info->reset_gpio, 0);
+       gpio_direction_output(info->bt_wakeup_gpio, 0);
+       gpio_direction_input(info->host_wakeup_gpio);
+
+       switch (bt_config->bt_uart) {
+       case 1:
+               if (cpu_is_omap16xx()) {
+                       irq = INT_UART1;
+                       info->uart_fclk = clk_get(NULL, "uart1_ck");
+               } else if (cpu_is_omap24xx()) {
+                       irq = INT_24XX_UART1_IRQ;
+                       info->uart_iclk = clk_get(NULL, "uart1_ick");
+                       info->uart_fclk = clk_get(NULL, "uart1_fck");
+               }
+               /* FIXME: Use platform_get_resource for the port */
+               info->uart_base = ioremap(OMAP_UART1_BASE, 0x16);
+               if (!info->uart_base)
+                       goto cleanup;
+               break;
+       case 2:
+               if (cpu_is_omap16xx()) {
+                       irq = INT_UART2;
+                       info->uart_fclk = clk_get(NULL, "uart2_ck");
+               } else {
+                       irq = INT_24XX_UART2_IRQ;
+                       info->uart_iclk = clk_get(NULL, "uart2_ick");
+                       info->uart_fclk = clk_get(NULL, "uart2_fck");
+               }
+               /* FIXME: Use platform_get_resource for the port */
+               info->uart_base = ioremap(OMAP_UART2_BASE, 0x16);
+               if (!info->uart_base)
+                       goto cleanup;
+               break;
+       case 3:
+               if (cpu_is_omap16xx()) {
+                       irq = INT_UART3;
+                       info->uart_fclk = clk_get(NULL, "uart3_ck");
+               } else {
+                       irq = INT_24XX_UART3_IRQ;
+                       info->uart_iclk = clk_get(NULL, "uart3_ick");
+                       info->uart_fclk = clk_get(NULL, "uart3_fck");
+               }
+               /* FIXME: Use platform_get_resource for the port */
+               info->uart_base = ioremap(OMAP_UART3_BASE, 0x16);
+               if (!info->uart_base)
+                       goto cleanup;
+               break;
+       default:
+               dev_err(info->dev, "No uart defined\n");
+               goto cleanup;
+       }
+
+       info->irq = irq;
+       err = request_irq(irq, hci_h4p_interrupt, 0, "hci_h4p", (void *)info);
+       if (err < 0) {
+               dev_err(info->dev, "hci_h4p: unable to get IRQ %d\n", irq);
+               goto cleanup;
+       }
+
+       err = request_irq(gpio_to_irq(info->host_wakeup_gpio),
+                         hci_h4p_wakeup_interrupt,
+                               IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+                         "hci_h4p_wkup", (void *)info);
+       if (err < 0) {
+               dev_err(info->dev, "hci_h4p: unable to get wakeup IRQ %d\n",
+                         gpio_to_irq(info->host_wakeup_gpio));
+               free_irq(irq, (void *)info);
+               goto cleanup;
+       }
+
+       hci_h4p_set_clk(info, &info->tx_clocks_en, 1);
+       hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_CTS | UART_EFR_RTS);
+       err = hci_h4p_init_uart(info);
+       if (err < 0)
+               goto cleanup_irq;
+       err = hci_h4p_reset(info);
+       if (err < 0)
+               goto cleanup_irq;
+       err = hci_h4p_wait_for_cts(info, 1, 10);
+       if (err < 0)
+               goto cleanup_irq;
+       hci_h4p_set_clk(info, &info->tx_clocks_en, 0);
+
+       platform_set_drvdata(pdev, info);
+       err = hci_h4p_sysfs_create_files(info->dev);
+       if (err < 0)
+               goto cleanup_irq;
+
+       if (hci_h4p_register_hdev(info) < 0) {
+               dev_err(info->dev, "failed to register hci_h4p hci device\n");
+               goto cleanup_irq;
+       }
+       gpio_set_value(info->reset_gpio, 0);
+
+       return 0;
+
+cleanup_irq:
+       free_irq(irq, (void *)info);
+       free_irq(gpio_to_irq(info->host_wakeup_gpio), (void *)info);
+cleanup:
+       gpio_set_value(info->reset_gpio, 0);
+       gpio_free(info->reset_gpio);
+       gpio_free(info->bt_wakeup_gpio);
+       gpio_free(info->host_wakeup_gpio);
+       kfree(info);
+
+       return err;
+
+}
+
+static int hci_h4p_remove(struct platform_device *dev)
+{
+       struct hci_h4p_info *info;
+
+       info = platform_get_drvdata(dev);
+
+       hci_h4p_hci_close(info->hdev);
+       free_irq(gpio_to_irq(info->host_wakeup_gpio), (void *) info);
+       hci_free_dev(info->hdev);
+       gpio_free(info->reset_gpio);
+       gpio_free(info->bt_wakeup_gpio);
+       gpio_free(info->host_wakeup_gpio);
+       free_irq(info->irq, (void *) info);
+       kfree(info);
+
+       return 0;
+}
+
+static struct platform_driver hci_h4p_driver = {
+       .probe          = hci_h4p_probe,
+       .remove         = hci_h4p_remove,
+       .driver         = {
+               .name   = "hci_h4p",
+       },
+};
+
+static int __init hci_h4p_init(void)
+{
+       int err = 0;
+
+       /* Register the driver with LDM */
+       err = platform_driver_register(&hci_h4p_driver);
+       if (err < 0)
+               printk(KERN_WARNING "failed to register hci_h4p driver\n");
+
+       return err;
+}
+
+static void __exit hci_h4p_exit(void)
+{
+       platform_driver_unregister(&hci_h4p_driver);
+}
+
+module_init(hci_h4p_init);
+module_exit(hci_h4p_exit);
+
+MODULE_DESCRIPTION("h4 driver with nokia extensions");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ville Tervo");
diff --git a/drivers/bluetooth/hci_h4p/fw-csr.c b/drivers/bluetooth/hci_h4p/fw-csr.c
new file mode 100644 (file)
index 0000000..209aab2
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation.
+ *
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/serial_reg.h>
+
+#include "hci_h4p.h"
+
+void hci_h4p_bc4_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
+{
+       /* Check if this is fw packet */
+       if (skb->data[0] != 0xff) {
+               hci_recv_frame(skb);
+               return;
+       }
+
+       if (skb->data[11] || skb->data[12]) {
+               dev_err(info->dev, "Firmware sending command failed\n");
+               info->fw_error = -EPROTO;
+       }
+
+       kfree_skb(skb);
+       complete(&info->fw_completion);
+}
+
+int hci_h4p_bc4_send_fw(struct hci_h4p_info *info,
+                       struct sk_buff_head *fw_queue)
+{
+       struct sk_buff *skb;
+       unsigned int offset;
+       int retries, count, i;
+
+       info->fw_error = 0;
+
+       NBT_DBG_FW("Sending firmware\n");
+       skb = skb_dequeue(fw_queue);
+
+       if (!skb)
+               return -ENOMSG;
+
+       /* Check if this is bd_address packet */
+       if (skb->data[15] == 0x01 && skb->data[16] == 0x00) {
+               offset = 21;
+               skb->data[offset + 1] = 0x00;
+               skb->data[offset + 5] = 0x00;
+               skb->data[offset + 7] = info->bdaddr[0];
+               skb->data[offset + 6] = info->bdaddr[1];
+               skb->data[offset + 4] = info->bdaddr[2];
+               skb->data[offset + 0] = info->bdaddr[3];
+               skb->data[offset + 3] = info->bdaddr[4];
+               skb->data[offset + 2] = info->bdaddr[5];
+       }
+
+       for (i = 0; i < 6; i++) {
+               if (info->bdaddr[i] != 0x00)
+                       break;
+       }
+
+       if (i > 5) {
+               dev_info(info->dev, "Valid bluetooth address not found.\n");
+               kfree_skb(skb);
+               return -ENODEV;
+       }
+
+       for (count = 1; ; count++) {
+               NBT_DBG_FW("Sending firmware command %d\n", count);
+               init_completion(&info->fw_completion);
+               skb_queue_tail(&info->txq, skb);
+               tasklet_schedule(&info->tx_task);
+
+               skb = skb_dequeue(fw_queue);
+               if (!skb)
+                       break;
+
+               if (!wait_for_completion_timeout(&info->fw_completion,
+                                                msecs_to_jiffies(1000))) {
+                       dev_err(info->dev, "No reply to fw command\n");
+                       return -ETIMEDOUT;
+               }
+
+               if (info->fw_error) {
+                       dev_err(info->dev, "FW error\n");
+                       return -EPROTO;
+               }
+       };
+
+       /* Wait for chip warm reset */
+       retries = 100;
+       while ((!skb_queue_empty(&info->txq) ||
+              !(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT)) &&
+              retries--) {
+               msleep(10);
+       }
+       if (!retries) {
+               dev_err(info->dev, "Transmitter not empty\n");
+               return -ETIMEDOUT;
+       }
+
+       hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
+
+       if (hci_h4p_wait_for_cts(info, 1, 100)) {
+               dev_err(info->dev, "cts didn't go down after final speed change\n");
+               return -ETIMEDOUT;
+       }
+
+       retries = 100;
+       do {
+               init_completion(&info->init_completion);
+               hci_h4p_send_alive_packet(info);
+               retries--;
+       } while (!wait_for_completion_timeout(&info->init_completion, 100) &&
+                retries > 0);
+
+       if (!retries) {
+               dev_err(info->dev, "No alive reply after speed change\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
diff --git a/drivers/bluetooth/hci_h4p/fw-ti.c b/drivers/bluetooth/hci_h4p/fw-ti.c
new file mode 100644 (file)
index 0000000..b98b579
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation.
+ *
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+
+#include "hci_h4p.h"
+
+void hci_h4p_brf6150_parse_fw_event(struct hci_h4p_info *info,
+                                   struct sk_buff *skb)
+{
+       struct hci_fw_event *ev;
+       int err = 0;
+
+       if (bt_cb(skb)->pkt_type != H4_EVT_PKT) {
+               dev_err(info->dev, "Got non event fw packet.\n");
+               err = -EPROTO;
+               goto ret;
+       }
+
+       ev = (struct hci_fw_event *)skb->data;
+       if (ev->hev.evt != HCI_EV_CMD_COMPLETE) {
+               dev_err(info->dev, "Got non cmd complete fw event\n");
+               err = -EPROTO;
+               goto ret;
+       }
+
+       if (ev->status != 0) {
+               dev_err(info->dev, "Got error status from fw command\n");
+               err = -EPROTO;
+               goto ret;
+       }
+
+ret:
+       info->fw_error = err;
+       complete(&info->fw_completion);
+}
+
+int hci_h4p_brf6150_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
+{
+       struct sk_buff *skb;
+       int err = 0;
+
+       info->fw_error = 0;
+
+       while ((skb = skb_dequeue(fw_queue)) != NULL) {
+               /* We should allways send word aligned data to h4+ devices */
+               if (skb->len % 2) {
+                       err = skb_pad(skb, 1);
+               }
+               if (err)
+                       return err;
+
+               init_completion(&info->fw_completion);
+               skb_queue_tail(&info->txq, skb);
+               tasklet_schedule(&info->tx_task);
+
+               if (!wait_for_completion_timeout(&info->fw_completion, HZ)) {
+                       dev_err(info->dev, "Timeout while sending brf6150 fw\n");
+                       return -ETIMEDOUT;
+               }
+
+               if (info->fw_error) {
+                       dev_err(info->dev, "There was fw_error while sending bfr6150 fw\n");
+                       return -EPROTO;
+               }
+       }
+       NBT_DBG_FW("Firmware sent\n");
+
+       return 0;
+}
diff --git a/drivers/bluetooth/hci_h4p/fw.c b/drivers/bluetooth/hci_h4p/fw.c
new file mode 100644 (file)
index 0000000..792b6b7
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation.
+ *
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/skbuff.h>
+#include <linux/firmware.h>
+#include <linux/clk.h>
+
+#include <net/bluetooth/bluetooth.h>
+
+#include "hci_h4p.h"
+
+static int fw_pos;
+
+/* Firmware handling */
+static int hci_h4p_open_firmware(struct hci_h4p_info *info,
+                                const struct firmware **fw_entry)
+{
+       int err;
+
+       fw_pos = 0;
+       NBT_DBG_FW("Opening %d firmware\n", info->chip_type);
+       switch (info->chip_type) {
+       case BT_CHIP_TI:
+               err = request_firmware(fw_entry, "brf6150fw.bin", info->dev);
+               break;
+       case BT_CHIP_CSR:
+               err = request_firmware(fw_entry, "bc4fw.bin", info->dev);
+               break;
+       default:
+               dev_err(info->dev, "Invalid chip type\n");
+               *fw_entry = NULL;
+               err = -EINVAL;
+       }
+
+       return err;
+}
+
+static void hci_h4p_close_firmware(const struct firmware *fw_entry)
+{
+       release_firmware(fw_entry);
+}
+
+/* Read fw. Return length of the command. If no more commands in
+ * fw 0 is returned. In error case return value is negative.
+ */
+static int hci_h4p_read_fw_cmd(struct hci_h4p_info *info, struct sk_buff **skb,
+                              const struct firmware *fw_entry, int how)
+{
+       unsigned int cmd_len;
+
+       if (fw_pos >= fw_entry->size) {
+               return 0;
+       }
+
+       cmd_len = fw_entry->data[fw_pos++];
+       if (!cmd_len)
+               return 0;
+
+       if (fw_pos + cmd_len > fw_entry->size) {
+               dev_err(info->dev, "Corrupted firmware image\n");
+               return -EMSGSIZE;
+       }
+
+       *skb = bt_skb_alloc(cmd_len, how);
+       if (!*skb) {
+               dev_err(info->dev, "Cannot reserve memory for buffer\n");
+               return -ENOMEM;
+       }
+       memcpy(skb_put(*skb, cmd_len), &fw_entry->data[fw_pos], cmd_len);
+
+       fw_pos += cmd_len;
+
+       return (*skb)->len;
+}
+
+int hci_h4p_read_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
+{
+       const struct firmware *fw_entry = NULL;
+       struct sk_buff *skb = NULL;
+       int err;
+
+       err = hci_h4p_open_firmware(info, &fw_entry);
+       if (err < 0 || !fw_entry)
+               goto err_clean;
+
+       while ((err = hci_h4p_read_fw_cmd(info, &skb, fw_entry, GFP_KERNEL))) {
+               if (err < 0 || !skb)
+                       goto err_clean;
+
+               skb_queue_tail(fw_queue, skb);
+       }
+
+err_clean:
+       hci_h4p_close_firmware(fw_entry);
+       return err;
+}
+
+int hci_h4p_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
+{
+       int err;
+
+       switch(info->chip_type) {
+       case BT_CHIP_CSR:
+               err = hci_h4p_bc4_send_fw(info, fw_queue);
+               break;
+       case BT_CHIP_TI:
+               err = hci_h4p_brf6150_send_fw(info, fw_queue);
+               break;
+       default:
+               dev_err(info->dev, "Don't know how to send firmware\n");
+               err = -EINVAL;
+       }
+
+       return err;
+}
+
+void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
+{
+       switch (info->chip_type) {
+       case BT_CHIP_CSR:
+               hci_h4p_bc4_parse_fw_event(info, skb);
+               break;
+       case BT_CHIP_TI:
+               hci_h4p_brf6150_parse_fw_event(info, skb);
+               break;
+       default:
+               dev_err(info->dev, "Don't know how to parse fw event\n");
+               info->fw_error = -EINVAL;
+       }
+
+       return;
+}
diff --git a/drivers/bluetooth/hci_h4p/hci_h4p.h b/drivers/bluetooth/hci_h4p/hci_h4p.h
new file mode 100644 (file)
index 0000000..85e9116
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation.
+ *
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <mach/board.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+#include <net/bluetooth/hci.h>
+
+#ifndef __DRIVERS_BLUETOOTH_HCI_H4P_H
+#define __DRIVERS_BLUETOOTH_HCI_H4P_H
+
+#define UART_SYSC_OMAP_RESET   0x03
+#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
+
+#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
+
+#if 0
+#define NBT_DBG_DMA(fmt, arg...)  printk("%s: " fmt "" , __FUNCTION__ , ## arg)
+#else
+#define NBT_DBG_DMA(...)
+#endif
+
+struct hci_h4p_info {
+       struct hci_dev *hdev;
+       spinlock_t lock;
+
+       void __iomem *uart_base;
+       unsigned long uart_phys_base;
+       int irq;
+       struct device *dev;
+       u8 bdaddr[6];
+       u8 chip_type;
+       u8 bt_wakeup_gpio;
+       u8 host_wakeup_gpio;
+       u8 reset_gpio;
+       u8 bt_sysclk;
+
+
+       struct sk_buff_head fw_queue;
+       struct sk_buff *alive_cmd_skb;
+       struct completion init_completion;
+       struct completion fw_completion;
+       int fw_error;
+       int init_error;
+
+       struct sk_buff_head txq;
+       struct tasklet_struct tx_task;
+
+       struct sk_buff *rx_skb;
+       long rx_count;
+       unsigned long rx_state;
+       unsigned long garbage_bytes;
+       struct tasklet_struct rx_task;
+
+       int pm_enabled;
+       int tx_pm_enabled;
+       int rx_pm_enabled;
+       struct timer_list tx_pm_timer;
+       struct timer_list rx_pm_timer;
+
+       int tx_clocks_en;
+       int rx_clocks_en;
+       spinlock_t clocks_lock;
+       struct clk *uart_iclk;
+       struct clk *uart_fclk;
+};
+
+#define MAX_BAUD_RATE          921600
+#define BC4_MAX_BAUD_RATE      3692300
+#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));
+
+struct hci_bc4_set_bdaddr {
+       u8 type;
+       struct hci_command_hdr cmd_hdr;
+} __attribute__ ((packed));
+
+int hci_h4p_send_alive_packet(struct hci_h4p_info *info);
+
+void hci_h4p_bc4_parse_fw_event(struct hci_h4p_info *info,
+                               struct sk_buff *skb);
+int hci_h4p_bc4_send_fw(struct hci_h4p_info *info,
+                       struct sk_buff_head *fw_queue);
+
+void hci_h4p_brf6150_parse_fw_event(struct hci_h4p_info *info,
+                                   struct sk_buff *skb);
+int hci_h4p_brf6150_send_fw(struct hci_h4p_info *info,
+                           struct sk_buff_head *fw_queue);
+
+int hci_h4p_read_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue);
+int hci_h4p_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue);
+void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb);
+
+int hci_h4p_sysfs_create_files(struct device *dev);
+
+void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val);
+u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset);
+void hci_h4p_set_rts(struct hci_h4p_info *info, int active);
+int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active, int timeout_ms);
+void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
+void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
+void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed);
+int hci_h4p_reset_uart(struct hci_h4p_info *info);
+int hci_h4p_init_uart(struct hci_h4p_info *info);
+
+#endif /* __DRIVERS_BLUETOOTH_HCI_H4P_H */
diff --git a/drivers/bluetooth/hci_h4p/sysfs.c b/drivers/bluetooth/hci_h4p/sysfs.c
new file mode 100644 (file)
index 0000000..687df69
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation.
+ *
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+
+#include "hci_h4p.h"
+
+#ifdef CONFIG_SYSFS
+
+static ssize_t hci_h4p_store_bdaddr(struct device *dev, struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct hci_h4p_info *info = (struct hci_h4p_info*)dev_get_drvdata(dev);
+       unsigned int bdaddr[6];
+       int ret, i;
+
+       ret = sscanf(buf, "%2x:%2x:%2x:%2x:%2x:%2x\n",
+                       &bdaddr[0], &bdaddr[1], &bdaddr[2],
+                       &bdaddr[3], &bdaddr[4], &bdaddr[5]);
+
+       if (ret != 6) {
+               return -EINVAL;
+       }
+
+       for (i = 0; i < 6; i++)
+               info->bdaddr[i] = bdaddr[i] & 0xff;
+
+       return count;
+}
+
+static ssize_t hci_h4p_show_bdaddr(struct device *dev, struct device_attribute *attr,
+                                  char *buf)
+{
+       struct hci_h4p_info *info = (struct hci_h4p_info*)dev_get_drvdata(dev);
+
+       return sprintf(buf, "%.2x:%.2x:%.2x:%.2x:%.2x:%.2x\n",
+                      info->bdaddr[0],
+                      info->bdaddr[1],
+                      info->bdaddr[2],
+                      info->bdaddr[3],
+                      info->bdaddr[4],
+                      info->bdaddr[5]);
+}
+
+static DEVICE_ATTR(bdaddr, S_IRUGO | S_IWUSR, hci_h4p_show_bdaddr, hci_h4p_store_bdaddr);
+int hci_h4p_sysfs_create_files(struct device *dev)
+{
+       return device_create_file(dev, &dev_attr_bdaddr);
+}
+
+#endif
diff --git a/drivers/bluetooth/hci_h4p/uart.c b/drivers/bluetooth/hci_h4p/uart.c
new file mode 100644 (file)
index 0000000..1b75a42
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * This file is part of hci_h4p bluetooth driver
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation.
+ *
+ * Contact: Ville Tervo <ville.tervo@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/serial_reg.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+
+#include "hci_h4p.h"
+
+inline void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val)
+{
+       outb(val, info->uart_base + (offset << 2));
+}
+
+inline u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset)
+{
+       return inb(info->uart_base + (offset << 2));
+}
+
+void hci_h4p_set_rts(struct hci_h4p_info *info, int active)
+{
+       u8 b;
+
+       b = hci_h4p_inb(info, UART_MCR);
+       if (active)
+               b |= UART_MCR_RTS;
+       else
+               b &= ~UART_MCR_RTS;
+       hci_h4p_outb(info, UART_MCR, b);
+}
+
+int hci_h4p_wait_for_cts(struct hci_h4p_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 = hci_h4p_inb(info, UART_MSR) & UART_MSR_CTS;
+               if (active) {
+                       if (state)
+                               return 0;
+               } else {
+                       if (!state)
+                               return 0;
+               }
+               if (time_after(jiffies, timeout))
+                       return -ETIMEDOUT;
+       }
+}
+
+void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which)
+{
+       u8 lcr, b;
+
+       lcr = hci_h4p_inb(info, UART_LCR);
+       hci_h4p_outb(info, UART_LCR, 0xbf);
+       b = hci_h4p_inb(info, UART_EFR);
+       if (on)
+               b |= which;
+       else
+               b &= ~which;
+       hci_h4p_outb(info, UART_EFR, b);
+       hci_h4p_outb(info, UART_LCR, lcr);
+}
+
+void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&info->lock, flags);
+       __hci_h4p_set_auto_ctsrts(info, on, which);
+       spin_unlock_irqrestore(&info->lock, flags);
+}
+
+void hci_h4p_change_speed(struct hci_h4p_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;
+       }
+
+       hci_h4p_outb(info, UART_OMAP_MDR1, 7); /* Make sure UART mode is disabled */
+       lcr = hci_h4p_inb(info, UART_LCR);
+       hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);     /* Set DLAB */
+       hci_h4p_outb(info, UART_DLL, divisor & 0xff);    /* Set speed */
+       hci_h4p_outb(info, UART_DLM, divisor >> 8);
+       hci_h4p_outb(info, UART_LCR, lcr);
+       hci_h4p_outb(info, UART_OMAP_MDR1, mdr1); /* Make sure UART mode is enabled */
+}
+
+int hci_h4p_reset_uart(struct hci_h4p_info *info)
+{
+       int count = 0;
+
+       /* Reset the  UART */
+       hci_h4p_outb(info, UART_OMAP_SYSC, UART_SYSC_OMAP_RESET);
+       while (!(hci_h4p_inb(info, UART_OMAP_SYSS) & UART_SYSS_RESETDONE)) {
+               if (count++ > 100) {
+                       dev_err(info->dev, "hci_h4p: UART reset timeout\n");
+                       return -ENODEV;
+               }
+               udelay(1);
+       }
+
+       return 0;
+}
+
+int hci_h4p_init_uart(struct hci_h4p_info *info)
+{
+       int err;
+
+       err = hci_h4p_reset_uart(info);
+       if (err < 0)
+               return err;
+
+       /* Enable and setup FIFO */
+       hci_h4p_outb(info, UART_LCR, UART_LCR_WLEN8);
+       hci_h4p_outb(info, UART_OMAP_MDR1, 0x00); /* Make sure UART mode is enabled */
+       hci_h4p_outb(info, UART_OMAP_SCR, 0x80);
+       hci_h4p_outb(info, UART_EFR, UART_EFR_ECB);
+       hci_h4p_outb(info, UART_MCR, UART_MCR_TCRTLR);
+       hci_h4p_outb(info, UART_TI752_TLR, 0x1f);
+       hci_h4p_outb(info, UART_TI752_TCR, 0xef);
+       hci_h4p_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR |
+                    UART_FCR_CLEAR_XMIT | UART_FCR_R_TRIG_00);
+       hci_h4p_outb(info, UART_IER, UART_IER_RDI);
+
+       return 0;
+}
index 538313f9e7ac6f8b45731b6ee49be2e8af0e0f8a..de201faa9df71640998b83500f07e190f5e1b97f 100644 (file)
@@ -205,7 +205,6 @@ static struct platform_driver omap_rng_driver = {
                .name           = "omap_rng",
                .owner          = THIS_MODULE,
        },
                .name           = "omap_rng",
                .owner          = THIS_MODULE,
        },
-       .probe          = omap_rng_probe,
        .remove         = __exit_p(omap_rng_remove),
        .suspend        = omap_rng_suspend,
        .resume         = omap_rng_resume
        .remove         = __exit_p(omap_rng_remove),
        .suspend        = omap_rng_suspend,
        .resume         = omap_rng_resume
@@ -216,7 +215,7 @@ static int __init omap_rng_init(void)
        if (!cpu_is_omap16xx() && !cpu_is_omap24xx())
                return -ENODEV;
 
        if (!cpu_is_omap16xx() && !cpu_is_omap24xx())
                return -ENODEV;
 
-       return platform_driver_register(&omap_rng_driver);
+       return platform_driver_probe(&omap_rng_driver, omap_rng_probe);
 }
 
 static void __exit omap_rng_exit(void)
 }
 
 static void __exit omap_rng_exit(void)
index 01afd758072f8242f550fc70b30b9c124aa8fa2b..f43cdd664258410b708541aa8d8383efcccfbd21 100644 (file)
@@ -83,6 +83,13 @@ config ZCRYPT_MONOLITHIC
          that contains all parts of the crypto device driver (ap bus,
          request router and all the card drivers).
 
          that contains all parts of the crypto device driver (ap bus,
          request router and all the card drivers).
 
+config OMAP_SHA1_MD5
+       tristate "Support for OMAP SHA1/MD5 hw engine"
+       depends on ARCH_OMAP24XX && CRYPTO_SHA1 && CRYPTO_MD5
+       help
+         OMAP processors have SHA1/MD5 module accelerator. Select this if you
+         want to use the OMAP module for SHA1/MD5 algorithms.
+
 config CRYPTO_SHA1_S390
        tristate "SHA1 digest algorithm"
        depends on S390
 config CRYPTO_SHA1_S390
        tristate "SHA1 digest algorithm"
        depends on S390
index 9bf4a2bc88461efdcbaca356a39728d84a9ca420..e6095f0f1778211b9bfa8434914898a90cd3b7dd 100644 (file)
@@ -1,6 +1,7 @@
 obj-$(CONFIG_CRYPTO_DEV_PADLOCK_AES) += padlock-aes.o
 obj-$(CONFIG_CRYPTO_DEV_PADLOCK_SHA) += padlock-sha.o
 obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o
 obj-$(CONFIG_CRYPTO_DEV_PADLOCK_AES) += padlock-aes.o
 obj-$(CONFIG_CRYPTO_DEV_PADLOCK_SHA) += padlock-sha.o
 obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o
+obj-$(CONFIG_OMAP_SHA1_MD5) += omap-sha1-md5.o
 obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
 obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
 obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
 obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o
 obj-$(CONFIG_CRYPTO_DEV_TALITOS) += talitos.o
 obj-$(CONFIG_CRYPTO_DEV_IXP4XX) += ixp4xx_crypto.o
diff --git a/drivers/crypto/omap-sha1-md5.c b/drivers/crypto/omap-sha1-md5.c
new file mode 100644 (file)
index 0000000..6d77703
--- /dev/null
@@ -0,0 +1,575 @@
+/*
+ * Cryptographic API.
+ *
+ * Support for OMAP SHA1/MD5 HW acceleration.
+ *
+ * Copyright (c) 2007 Instituto Nokia de Tecnologia - INdT
+ * Author: David Cohen <david.cohen@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 version 2 as published
+ * by the Free Software Foundation.
+ *
+ * This driver is based on padlock-sha.c driver.
+ */
+
+#include <asm/arch-omap/irqs.h>
+#include <crypto/algapi.h>
+#include <crypto/sha.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/cryptohash.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/scatterlist.h>
+
+#define SHA_REG_DIGEST(x)              (0x00 + ((x) * 0x04))
+#define SHA_REG_DIN(x)                 (0x1C + ((x) * 0x04))
+
+#define SHA1_MD5_BLOCK_SIZE            SHA1_BLOCK_SIZE
+#define MD5_DIGEST_SIZE                        16
+
+#define SHA_REG_DIGCNT                 0x14
+
+#define SHA_REG_CTRL                   0x18
+#define SHA_REG_CTRL_LENGTH            (0xFFFFFFFF << 5)
+#define SHA_REG_CTRL_CLOSE_HASH                (1 << 4)
+#define SHA_REG_CTRL_ALGO_CONST                (1 << 3)
+#define SHA_REG_CTRL_ALGO              (1 << 2)
+#define SHA_REG_CTRL_INPUT_READY       (1 << 1)
+#define SHA_REG_CTRL_OUTPUT_READY      (1 << 0)
+
+#define SHA_REG_REV                    0x5C
+#define SHA_REG_REV_MAJOR              0xF0
+#define SHA_REG_REV_MINOR              0x0F
+
+#define SHA_REG_MASK                   0x60
+#define SHA_REG_MASK_DMA_EN            (1 << 3)
+#define SHA_REG_MASK_IT_EN             (1 << 2)
+#define SHA_REG_MASK_SOFTRESET         (1 << 1)
+#define SHA_REG_AUTOIDLE               (1 << 0)
+
+#define SHA_REG_SYSSTATUS              0x64
+#define SHA_REG_SYSSTATUS_RESETDONE    (1 << 0)
+
+#define DRIVER_NAME                    "OMAP SHA1/MD5"
+
+struct omap_sha1_md5_ctx {
+       unsigned int            type_algo;
+       unsigned int            bufcnt;
+       unsigned int            digcnt;
+       int                     algo_const;
+       int                     bypass;
+       int                     digsize;
+       u8                      hash[SHA1_DIGEST_SIZE];
+       u8                      buffer[SHA1_BLOCK_SIZE];
+       struct                  hash_desc fallback;
+};
+
+struct omap_sha1_md5_dev {
+       unsigned long           base_address;
+       int                     irq;
+       int                     digready;
+       struct clk              *sha1_ick;
+       struct omap_sha1_md5_ctx
+                               *hw_ctx;
+       struct device           *dev;
+       wait_queue_head_t       wq;
+};
+
+static struct omap_sha1_md5_dev *sha1_md5_data;
+
+#define SHA_REG_IOADDR(d, x) (void *)IO_ADDRESS((d)->base_address + (x))
+
+static u32 omap_sha1_md5_read(struct omap_sha1_md5_dev *data, u32 offset)
+{
+       return __raw_readl(SHA_REG_IOADDR(data, offset));
+}
+
+static void omap_sha1_md5_write(struct omap_sha1_md5_dev *data,
+                                       u32 value, u32 offset)
+{
+       __raw_writel(value, SHA_REG_IOADDR(data, offset));
+}
+
+static void omap_sha1_md5_write_mask(struct omap_sha1_md5_dev *data,
+                                       u32 value, u32 mask, u32 address)
+{
+       u32 val;
+
+       val = omap_sha1_md5_read(data, address);
+       val &= ~mask;
+       val |= value;
+       omap_sha1_md5_write(data, val, address);
+}
+
+static inline void omap_sha1_md5_enable_clk(struct crypto_tfm *tfm)
+{
+       struct omap_sha1_md5_dev *data = sha1_md5_data;
+
+       clk_enable(data->sha1_ick);
+}
+
+static inline void omap_sha1_md5_disable_clk(struct crypto_tfm *tfm)
+{
+       struct omap_sha1_md5_dev *data = sha1_md5_data;
+
+       clk_disable(data->sha1_ick);
+}
+
+static void omap_sha1_md5_copy_hash(struct crypto_tfm *tfm)
+{
+       struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct omap_sha1_md5_dev *data = sha1_md5_data;
+
+       u32 *hash = (u32 *)ctx->hash;
+
+       if (ctx->type_algo) {
+               /* SHA1 results are in big endian */
+               hash[0] = be32_to_cpu(
+                               omap_sha1_md5_read(data, SHA_REG_DIGEST(0)));
+               hash[1] = be32_to_cpu(
+                               omap_sha1_md5_read(data, SHA_REG_DIGEST(1)));
+               hash[2] = be32_to_cpu(
+                               omap_sha1_md5_read(data, SHA_REG_DIGEST(2)));
+               hash[3] = be32_to_cpu(
+                               omap_sha1_md5_read(data, SHA_REG_DIGEST(3)));
+               hash[4] = be32_to_cpu(
+                               omap_sha1_md5_read(data, SHA_REG_DIGEST(4)));
+       } else {
+               /* MD5 results are in little endian */
+               hash[0] = le32_to_cpu(
+                               omap_sha1_md5_read(data, SHA_REG_DIGEST(0)));
+               hash[1] = le32_to_cpu(
+                               omap_sha1_md5_read(data, SHA_REG_DIGEST(1)));
+               hash[2] = le32_to_cpu(
+                               omap_sha1_md5_read(data, SHA_REG_DIGEST(2)));
+               hash[3] = le32_to_cpu(
+                               omap_sha1_md5_read(data, SHA_REG_DIGEST(3)));
+       }
+}
+
+static void omap_sha1_md5_bypass(struct crypto_tfm *tfm,
+                               u8 *data, unsigned int length)
+{
+       struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       if (unlikely(!ctx->bypass))
+               return;
+
+       if (ctx->bypass == 1) {
+               crypto_hash_init(&ctx->fallback);
+               ctx->bypass++;
+       }
+
+       if (length) {
+               struct scatterlist sg;
+
+               sg_set_buf(&sg, data, length);
+               crypto_hash_update(&ctx->fallback, &sg, sg.length);
+       }
+}
+
+static void omap_sha1_md5_digest_buffer(struct crypto_tfm *tfm,
+                               u8 *buf, unsigned int len, int close_hash)
+{
+       struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct omap_sha1_md5_dev *data = sha1_md5_data;
+       unsigned int algo_const = 0;
+       int c;
+       u32 *buffer = (u32 *)buf;
+
+       if (unlikely(ctx->bypass)) {
+               omap_sha1_md5_bypass(tfm, buf, len);
+               return;
+       }
+
+       if (unlikely(ctx->algo_const)) {
+               algo_const = SHA_REG_CTRL_ALGO_CONST;
+               ctx->algo_const = 0;
+       } else
+               omap_sha1_md5_write(data, ctx->digcnt, SHA_REG_DIGCNT);
+
+       if (unlikely(close_hash))
+               close_hash = SHA_REG_CTRL_CLOSE_HASH;
+
+       /* Setting ALGO_CONST only for the first iteration
+        * and CLOSE_HASH only for the last one. */
+       omap_sha1_md5_write_mask(data,
+                       ctx->type_algo | algo_const | close_hash | (len << 5),
+                       SHA_REG_CTRL_ALGO_CONST | SHA_REG_CTRL_CLOSE_HASH |
+                       SHA_REG_CTRL_ALGO | SHA_REG_CTRL_LENGTH,
+                       SHA_REG_CTRL);
+
+       ctx->digcnt += len;
+       while (!(omap_sha1_md5_read(data, SHA_REG_CTRL)
+               & SHA_REG_CTRL_INPUT_READY));
+
+       if (len % 4)
+               len = (len/4) + 1;
+       else
+               len /= 4;
+       for (c = 0; c < len; c++)
+               omap_sha1_md5_write(data, buffer[c], SHA_REG_DIN(c));
+}
+
+static void omap_sha1_md5_append_buffer(struct crypto_tfm *tfm,
+                               const uint8_t *data, unsigned int length)
+{
+       struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       BUG_ON((ctx->bufcnt + length) > SHA1_MD5_BLOCK_SIZE);
+
+       memcpy(&ctx->buffer[ctx->bufcnt], data, length);
+       ctx->bufcnt += length;
+}
+
+static void omap_sha1_md5_dia_update(struct crypto_tfm *tfm,
+                               const uint8_t *data, unsigned int length)
+{
+       struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       /* We need to save the last buffer <= 64 to digest it with
+        * CLOSE_HASH = 1 */
+       if (ctx->bufcnt && ((ctx->bufcnt + length) > SHA1_MD5_BLOCK_SIZE)) {
+               unsigned int c = SHA1_MD5_BLOCK_SIZE - ctx->bufcnt;
+
+               omap_sha1_md5_append_buffer(tfm, data, c);
+               data += c;
+               length -= c;
+               if (length) {
+                       ctx->bufcnt = 0;
+                       omap_sha1_md5_digest_buffer(tfm, ctx->buffer,
+                                       SHA1_MD5_BLOCK_SIZE, 0);
+               }
+       }
+
+       while (length > SHA1_MD5_BLOCK_SIZE) {
+               /* Revisit: use DMA here */
+               omap_sha1_md5_digest_buffer(tfm, (u8 *)data,
+                               SHA1_MD5_BLOCK_SIZE, 0);
+               length -= SHA1_MD5_BLOCK_SIZE;
+               data += SHA1_MD5_BLOCK_SIZE;
+       }
+
+       if (length)
+               omap_sha1_md5_append_buffer(tfm, data, length);
+}
+
+static void omap_sha1_md5_start_reset(struct crypto_tfm *tfm)
+{
+       struct omap_sha1_md5_dev *data = sha1_md5_data;
+
+       omap_sha1_md5_write_mask(data, SHA_REG_MASK_SOFTRESET,
+                       SHA_REG_MASK_SOFTRESET, SHA_REG_MASK);
+}
+
+static void omap_sha1_md5_wait_reset(struct crypto_tfm *tfm)
+{
+       struct omap_sha1_md5_dev *data = sha1_md5_data;
+
+       while (!(omap_sha1_md5_read(data, SHA_REG_SYSSTATUS)
+                       & SHA_REG_SYSSTATUS_RESETDONE));
+}
+
+static void omap_sha1_md5_dia_init(struct crypto_tfm *tfm)
+{
+       struct omap_sha1_md5_dev *data = sha1_md5_data;
+       struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       if (unlikely(data->hw_ctx))
+               ctx->bypass = 1;
+       else {
+               data->hw_ctx = ctx;
+               ctx->bypass = 0;
+               omap_sha1_md5_enable_clk(tfm);
+               omap_sha1_md5_start_reset(tfm);
+               data->digready = 0;
+       }
+
+       if (ctx->bypass) {
+               omap_sha1_md5_bypass(tfm, NULL, 0);
+               return;
+       }
+
+       ctx->algo_const = 1;
+       ctx->bufcnt = 0;
+       ctx->digcnt = 0;
+
+       omap_sha1_md5_wait_reset(tfm);
+       omap_sha1_md5_write_mask(data, SHA_REG_MASK_IT_EN,
+               SHA_REG_MASK_DMA_EN | SHA_REG_MASK_IT_EN, SHA_REG_MASK);
+}
+
+static void omap_sha1_md5_dia_final(struct crypto_tfm *tfm, uint8_t *out)
+{
+       struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct omap_sha1_md5_dev *data = sha1_md5_data;
+       int digsize = ctx->digsize;
+
+       /* The buffer should be >= 9 */
+       if (((ctx->digcnt + ctx->bufcnt) < 9) && !ctx->bypass)
+               ctx->bypass = 1;
+
+       omap_sha1_md5_digest_buffer(tfm, ctx->buffer, ctx->bufcnt, 1);
+
+       if (unlikely(ctx->bypass)) {
+               crypto_hash_final(&ctx->fallback, out);
+               ctx->bypass = 0;
+               goto bypass;
+       } else
+               data->digready = 1;
+
+       wait_event_interruptible(data->wq, (data->digready == 2));
+       omap_sha1_md5_copy_hash(tfm);
+
+       memcpy(out, ctx->hash, digsize);
+
+bypass:
+       if (data->hw_ctx == ctx) {
+               omap_sha1_md5_disable_clk(tfm);
+               data->hw_ctx = NULL;
+       }
+}
+
+static irqreturn_t omap_sha1_md5_irq(int irq, void *dev_id)
+{
+       struct omap_sha1_md5_dev *data = dev_id;
+
+       omap_sha1_md5_write_mask(data, SHA_REG_CTRL_OUTPUT_READY,
+                       SHA_REG_CTRL_OUTPUT_READY, SHA_REG_CTRL);
+
+       if (likely(!data->digready))
+               return IRQ_HANDLED;
+
+       if (data->hw_ctx == NULL) {
+               dev_err(data->dev, "unknown interrupt.\n");
+               return IRQ_HANDLED;
+       }
+
+       data->digready = 2;
+       wake_up_interruptible(&data->wq);
+
+       return IRQ_HANDLED;
+}
+
+static int omap_sha1_md5_cra_init(struct crypto_tfm *tfm)
+{
+       struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
+       struct omap_sha1_md5_dev *data = sha1_md5_data;
+       const char *fallback_driver_name = tfm->__crt_alg->cra_name;
+       struct crypto_hash *fallback_tfm;
+
+       /* Allocate a fallback and abort if it failed. */
+       fallback_tfm = crypto_alloc_hash(fallback_driver_name, 0,
+                                        CRYPTO_ALG_ASYNC |
+                                        CRYPTO_ALG_NEED_FALLBACK);
+       if (IS_ERR(fallback_tfm)) {
+               dev_err(data->dev, "fallback driver '%s' could not be"
+                               "loaded.\n", fallback_driver_name);
+               return PTR_ERR(fallback_tfm);
+       }
+
+       ctx->fallback.tfm = fallback_tfm;
+
+       return 0;
+}
+
+static int omap_sha1_cra_init(struct crypto_tfm *tfm)
+{
+       struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       ctx->type_algo = SHA_REG_CTRL_ALGO;
+       ctx->digsize = SHA1_DIGEST_SIZE;
+
+       return omap_sha1_md5_cra_init(tfm);
+}
+
+static int omap_md5_cra_init(struct crypto_tfm *tfm)
+{
+       struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       ctx->type_algo = 0;
+       ctx->digsize = MD5_DIGEST_SIZE;
+
+       return omap_sha1_md5_cra_init(tfm);
+}
+
+static void omap_sha1_md5_cra_exit(struct crypto_tfm *tfm)
+{
+       struct omap_sha1_md5_ctx *ctx = crypto_tfm_ctx(tfm);
+
+       crypto_free_hash(ctx->fallback.tfm);
+       ctx->fallback.tfm = NULL;
+}
+
+static struct crypto_alg omap_sha1_alg = {
+       .cra_name               =       "sha1",
+       .cra_driver_name        =       "omap-sha1",
+       .cra_flags              =       CRYPTO_ALG_TYPE_DIGEST |
+                                       CRYPTO_ALG_NEED_FALLBACK,
+       .cra_blocksize          =       SHA1_MD5_BLOCK_SIZE,
+       .cra_ctxsize            =       sizeof(struct omap_sha1_md5_ctx),
+       .cra_module             =       THIS_MODULE,
+       .cra_list               =       LIST_HEAD_INIT(omap_sha1_alg.cra_list),
+       .cra_init               =       omap_sha1_cra_init,
+       .cra_exit               =       omap_sha1_md5_cra_exit,
+       .cra_u                  =       {
+               .digest = {
+                       .dia_digestsize =       SHA1_DIGEST_SIZE,
+                       .dia_init       =       omap_sha1_md5_dia_init,
+                       .dia_update     =       omap_sha1_md5_dia_update,
+                       .dia_final      =       omap_sha1_md5_dia_final,
+               }
+       }
+};
+
+static struct crypto_alg omap_md5_alg = {
+       .cra_name               =       "md5",
+       .cra_driver_name        =       "omap-md5",
+       .cra_flags              =       CRYPTO_ALG_TYPE_DIGEST |
+                                       CRYPTO_ALG_NEED_FALLBACK,
+       .cra_blocksize          =       SHA1_MD5_BLOCK_SIZE,
+       .cra_ctxsize            =       sizeof(struct omap_sha1_md5_ctx),
+       .cra_module             =       THIS_MODULE,
+       .cra_list               =       LIST_HEAD_INIT(omap_md5_alg.cra_list),
+       .cra_init               =       omap_md5_cra_init,
+       .cra_exit               =       omap_sha1_md5_cra_exit,
+       .cra_u                  =       {
+               .digest = {
+                       .dia_digestsize =       MD5_DIGEST_SIZE,
+                       .dia_init       =       omap_sha1_md5_dia_init,
+                       .dia_update     =       omap_sha1_md5_dia_update,
+                       .dia_final      =       omap_sha1_md5_dia_final,
+               }
+       }
+};
+
+static int omap_sha1_md5_probe(struct platform_device *pdev)
+{
+       struct omap_sha1_md5_dev *data;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       int rc;
+
+       rc = crypto_register_alg(&omap_sha1_alg);
+       if (rc)
+               goto sha1_err;
+       rc = crypto_register_alg(&omap_md5_alg);
+       if (rc)
+               goto md5_err;
+
+       data = kzalloc(sizeof(struct omap_sha1_md5_dev), GFP_KERNEL);
+       if (data == NULL) {
+               dev_err(dev, "unable to alloc data struct.\n");
+               goto data_err;
+       }
+       platform_set_drvdata(pdev, data);
+       data->dev = dev;
+
+       /* Get the base address */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "invalid resource type\n");
+               rc = -ENODEV;
+               goto res_err;
+       }
+       data->base_address = res->start;
+
+       /* Set the private data */
+       sha1_md5_data = data;
+
+       /* Get the IRQ */
+       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!res) {
+               dev_err(dev, "invalid resource type\n");
+               rc = -ENODEV;
+               goto res_err;
+       }
+       data->irq = res->start;
+
+       rc = request_irq(res->start, omap_sha1_md5_irq,
+                       IRQF_TRIGGER_LOW, DRIVER_NAME, data);
+       if (rc) {
+               dev_err(dev, "unable to request irq.\n");
+               goto res_err;
+       }
+
+       /* Initializing the clock */
+       data->sha1_ick = clk_get(0, "sha_ick");
+       if (!data->sha1_ick) {
+               dev_err(dev, "clock intialization failed.\n");
+               rc = -ENODEV;
+               goto clk_err;
+       }
+
+       init_waitqueue_head(&data->wq);
+
+       dev_info(dev, "hw accel on OMAP rev %u.%u\n",
+               (omap_sha1_md5_read(data, SHA_REG_REV) & SHA_REG_REV_MAJOR)>>4,
+               omap_sha1_md5_read(data, SHA_REG_REV) & SHA_REG_REV_MINOR);
+
+       return 0;
+
+clk_err:
+       free_irq(data->irq, data);
+res_err:
+       kfree(data);
+data_err:
+       crypto_unregister_alg(&omap_md5_alg);
+md5_err:
+       crypto_unregister_alg(&omap_sha1_alg);
+sha1_err:
+       dev_err(dev, "initialization failed.\n");
+       return rc;
+}
+
+static int omap_sha1_md5_remove(struct platform_device *pdev)
+{
+       struct omap_sha1_md5_dev *data = platform_get_drvdata(pdev);
+
+       free_irq(data->irq, data);
+       kfree(data);
+       crypto_unregister_alg(&omap_sha1_alg);
+       crypto_unregister_alg(&omap_md5_alg);
+
+       return 0;
+}
+
+static struct platform_driver omap_sha1_md5_driver = {
+       .probe  = omap_sha1_md5_probe,
+       .remove = omap_sha1_md5_remove,
+       .driver = {
+               .name   = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init omap_sha1_md5_init(void)
+{
+       int ret;
+
+       ret = platform_driver_register(&omap_sha1_md5_driver);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static void __exit omap_sha1_md5_exit(void)
+{
+       platform_driver_unregister(&omap_sha1_md5_driver);
+}
+
+module_init(omap_sha1_md5_init);
+module_exit(omap_sha1_md5_exit);
+
+MODULE_DESCRIPTION("OMAP SHA1/MD5 hw acceleration support.");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Cohen");
index 0e8a9185f676fa6ade83113b983a7e6430e52739..1d3374ae7eba898d251359e6cf6657228dc60575 100644 (file)
@@ -995,6 +995,22 @@ config SENSORS_APPLESMC
          Say Y here if you have an applicable laptop and want to experience
          the awesome power of applesmc.
 
          Say Y here if you have an applicable laptop and want to experience
          the awesome power of applesmc.
 
+config SENSORS_TSC210X
+       tristate "TI TSC210x battery & temperature sensors"
+       depends on HWMON && SPI_MASTER
+       select SPI_TSC210X
+       help
+         Say Y if your board has a TSC210x chip and you want to
+         have its battery state, auxiliary input and/or temperature
+         sensors exported through hwmon.
+
+         This driver can also be built as a module.  In this case
+         the module will be called tsc210x_sensors.
+
+config SENSORS_OMAP34XX
+       tristate "TI OMAP34xx internal temperature sensor"
+       depends on ARCH_OMAP3 && HIGH_RES_TIMERS
+
 config HWMON_DEBUG_CHIP
        bool "Hardware Monitoring Chip debugging messages"
        default n
 config HWMON_DEBUG_CHIP
        bool "Hardware Monitoring Chip debugging messages"
        default n
index 1d3757837b4ff5091f4ae2d6728616f9ec7aee1a..023f5719abdc088ff3b4c03c64f64a4ff982d528 100644 (file)
@@ -86,7 +86,9 @@ obj-$(CONFIG_SENSORS_VT1211)  += vt1211.o
 obj-$(CONFIG_SENSORS_VT8231)   += vt8231.o
 obj-$(CONFIG_SENSORS_W83627EHF)        += w83627ehf.o
 obj-$(CONFIG_SENSORS_W83L785TS)        += w83l785ts.o
 obj-$(CONFIG_SENSORS_VT8231)   += vt8231.o
 obj-$(CONFIG_SENSORS_W83627EHF)        += w83627ehf.o
 obj-$(CONFIG_SENSORS_W83L785TS)        += w83l785ts.o
+obj-$(CONFIG_SENSORS_TSC210X)  += tsc210x_sensors.o
 obj-$(CONFIG_SENSORS_W83L786NG)        += w83l786ng.o
 obj-$(CONFIG_SENSORS_W83L786NG)        += w83l786ng.o
+obj-$(CONFIG_SENSORS_OMAP34XX)  += omap34xx_temp.o
 
 ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG
 
 ifeq ($(CONFIG_HWMON_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/hwmon/omap34xx_temp.c b/drivers/hwmon/omap34xx_temp.c
new file mode 100644 (file)
index 0000000..18e0764
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * omap34xx_temp.c - Linux kernel module for OMAP34xx hardware monitoring
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Written by Peter De Schrijver <peter.de-schrijver@nokia.com>
+ *
+ * Inspired by k8temp.c
+ *
+ * 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 <linux/clk.h>
+#include <linux/hrtimer.h>
+#include <linux/module.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <mach/omap34xx.h>
+#include <mach/control.h>
+
+#define TEMP_SENSOR_SOC BIT(8)
+#define TEMP_SENSOR_EOCZ BIT(7)
+
+/* minimum delay for EOCZ rise after SOC rise is
+ * 11 cycles of the 32.768Khz clock */
+#define EOCZ_MIN_RISING_DELAY (11 * 30518)
+
+/* maximum delay for EOCZ rise after SOC rise is
+ * 14 cycles of the 32.768Khz clock */
+#define EOCZ_MAX_RISING_DELAY (14 * 30518)
+
+/* minimum delay for EOCZ falling is
+ * 36 cycles of the 32.768Khz clock */
+#define EOCZ_MIN_FALLING_DELAY (36 * 30518)
+
+/* maximum delay for EOCZ falling is
+ * 40 cycles of the 32.768Khz clock */
+#define EOCZ_MAX_FALLING_DELAY (40 * 30518)
+
+struct omap34xx_data {
+       struct device *hwmon_dev;
+       struct clk *clk_32k;
+       struct mutex update_lock;
+       const char *name;
+       char valid;
+       unsigned long last_updated;
+       u32 temp;
+};
+
+static struct platform_device omap34xx_temp_device = {
+       .name   = "omap34xx_temp",
+       .id     = -1,
+};
+
+static int adc_to_temp[] = {
+       -40, -40, -40, -40, -40, -39, -38, -36, -34, -32, -31, -29, -28, -26,
+       -25, -24, -22, -21, -19, -18, -17, -15, -14, -12, -11, -9, -8, -7, -5,
+       -4, -2, -1, 0, 1, 3, 4, 5, 7, 8, 10, 11, 13, 14, 15, 17, 18, 20, 21,
+       22, 24, 25, 27, 28, 30, 31, 32, 34, 35, 37, 38, 39, 41, 42, 44, 45,
+       47, 48, 49, 51, 52, 53, 55, 56, 58, 59, 60, 62, 63, 65, 66, 67, 69,
+       70, 72, 73, 74, 76, 77, 79, 80, 81, 83, 84, 85, 87, 88, 89, 91, 92,
+       94, 95, 96, 98, 99, 100, 102, 103, 105, 106, 107, 109, 110, 111, 113,
+       114, 116, 117, 118, 120, 121, 122, 124, 124, 125, 125, 125, 125, 125};
+
+static inline u32 wait_for_eocz(int min_delay, int max_delay, u32 level)
+{
+       struct timespec timeout;
+       ktime_t expire;
+       u32 temp_sensor_reg;
+
+       level &= 1;
+       level *= TEMP_SENSOR_EOCZ;
+
+       expire = ktime_add_ns(ktime_get(), max_delay);
+       timeout = ns_to_timespec(min_delay);
+       hrtimer_nanosleep(&timeout, NULL, HRTIMER_MODE_REL, CLOCK_MONOTONIC);
+       do {
+               temp_sensor_reg = omap_ctrl_readl(OMAP343X_CONTROL_TEMP_SENSOR);
+               if ((temp_sensor_reg & TEMP_SENSOR_EOCZ) == level)
+                       break;
+       } while (ktime_us_delta(expire, ktime_get()) > 0);
+
+       return (temp_sensor_reg & TEMP_SENSOR_EOCZ) == level;
+}
+
+static void omap34xx_update(struct omap34xx_data *data)
+{
+       u32 temp_sensor_reg;
+
+       mutex_lock(&data->update_lock);
+
+       if (!data->valid
+           || time_after(jiffies, data->last_updated + HZ)) {
+
+               clk_enable(data->clk_32k);
+
+               temp_sensor_reg = omap_ctrl_readl(OMAP343X_CONTROL_TEMP_SENSOR);
+               temp_sensor_reg |= TEMP_SENSOR_SOC;
+               omap_ctrl_writel(temp_sensor_reg, OMAP343X_CONTROL_TEMP_SENSOR);
+
+               if (!wait_for_eocz(EOCZ_MIN_RISING_DELAY,
+                                       EOCZ_MAX_RISING_DELAY, 1))
+                       goto err;
+
+               temp_sensor_reg = omap_ctrl_readl(OMAP343X_CONTROL_TEMP_SENSOR);
+               temp_sensor_reg &= ~TEMP_SENSOR_SOC;
+               omap_ctrl_writel(temp_sensor_reg, OMAP343X_CONTROL_TEMP_SENSOR);
+
+               if (!wait_for_eocz(EOCZ_MIN_FALLING_DELAY,
+                                       EOCZ_MAX_FALLING_DELAY, 0))
+                       goto err;
+
+               data->temp = omap_ctrl_readl(OMAP343X_CONTROL_TEMP_SENSOR) &
+                                               ((1<<7) - 1);
+               data->last_updated = jiffies;
+               data->valid = 1;
+
+err:
+               clk_disable(data->clk_32k);
+       }
+
+       mutex_unlock(&data->update_lock);
+}
+
+static ssize_t show_name(struct device *dev,
+                       struct device_attribute *devattr, char *buf)
+{
+       struct omap34xx_data *data = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%s\n", data->name);
+}
+
+static ssize_t show_temp_raw(struct device *dev,
+                        struct device_attribute *devattr, char *buf)
+{
+       struct omap34xx_data *data = dev_get_drvdata(dev);
+
+       omap34xx_update(data);
+
+       return sprintf(buf, "%d\n", data->temp);
+}
+
+static ssize_t show_temp(struct device *dev,
+                        struct device_attribute *devattr, char *buf)
+{
+       struct omap34xx_data *data = dev_get_drvdata(dev);
+
+       omap34xx_update(data);
+
+       return sprintf(buf, "%d\n", adc_to_temp[data->temp]);
+}
+
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_input_raw, S_IRUGO, show_temp_raw,
+                               NULL, 0, 0);
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static int __devinit omap34xx_temp_probe(void)
+{
+       int err;
+       struct omap34xx_data *data;
+
+       err = platform_device_register(&omap34xx_temp_device);
+       if (err) {
+               printk(KERN_ERR
+                       "Unable to register omap34xx temperature device\n");
+               goto exit;
+       }
+
+       data = kzalloc(sizeof(struct omap34xx_data), GFP_KERNEL);
+       if (!data) {
+               err = -ENOMEM;
+               goto exit_platform;
+       }
+
+       dev_set_drvdata(&omap34xx_temp_device.dev, data);
+       mutex_init(&data->update_lock);
+       data->name = "omap34xx_temp";
+
+       data->clk_32k = clk_get(&omap34xx_temp_device.dev, "ts_fck");
+       if (IS_ERR(data->clk_32k)) {
+               err = PTR_ERR(data->clk_32k);
+               goto exit_free;
+       }
+
+       err = device_create_file(&omap34xx_temp_device.dev,
+                                &sensor_dev_attr_temp1_input.dev_attr);
+       if (err)
+               goto clock_free;
+
+       err = device_create_file(&omap34xx_temp_device.dev,
+                                &sensor_dev_attr_temp1_input_raw.dev_attr);
+       if (err)
+               goto exit_remove;
+
+       err = device_create_file(&omap34xx_temp_device.dev, &dev_attr_name);
+       if (err)
+               goto exit_remove_raw;
+
+       data->hwmon_dev = hwmon_device_register(&omap34xx_temp_device.dev);
+
+       if (IS_ERR(data->hwmon_dev)) {
+               err = PTR_ERR(data->hwmon_dev);
+               goto exit_remove_all;
+       }
+
+       return 0;
+
+exit_remove_all:
+       device_remove_file(&omap34xx_temp_device.dev,
+                          &dev_attr_name);
+exit_remove_raw:
+       device_remove_file(&omap34xx_temp_device.dev,
+                          &sensor_dev_attr_temp1_input_raw.dev_attr);
+exit_remove:
+       device_remove_file(&omap34xx_temp_device.dev,
+                          &sensor_dev_attr_temp1_input.dev_attr);
+clock_free:
+       clk_put(data->clk_32k);
+
+exit_free:
+       kfree(data);
+exit_platform:
+       platform_device_unregister(&omap34xx_temp_device);
+exit:
+       return err;
+}
+
+static int __init omap34xx_temp_init(void)
+{
+       return omap34xx_temp_probe();
+}
+
+static void __exit omap34xx_temp_exit(void)
+{
+       struct omap34xx_data *data =
+                       dev_get_drvdata(&omap34xx_temp_device.dev);
+
+       clk_put(data->clk_32k);
+       hwmon_device_unregister(data->hwmon_dev);
+       device_remove_file(&omap34xx_temp_device.dev,
+                          &sensor_dev_attr_temp1_input.dev_attr);
+       device_remove_file(&omap34xx_temp_device.dev, &dev_attr_name);
+       kfree(data);
+       platform_device_unregister(&omap34xx_temp_device);
+}
+
+MODULE_AUTHOR("Peter De Schrijver");
+MODULE_DESCRIPTION("Omap34xx temperature sensor");
+MODULE_LICENSE("GPL");
+
+module_init(omap34xx_temp_init)
+module_exit(omap34xx_temp_exit)
+
diff --git a/drivers/hwmon/tsc210x_sensors.c b/drivers/hwmon/tsc210x_sensors.c
new file mode 100644 (file)
index 0000000..daac126
--- /dev/null
@@ -0,0 +1,296 @@
+/*
+ * tsc210x_sensors.c - hwmon interface to TI TSC210x sensors
+ *
+ * Copyright (c) 2005-2007 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This package 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 package 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 package; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/autoconf.h>
+#ifdef CONFIG_APM
+# include <linux/apm-emulation.h>
+#endif
+
+#include <linux/spi/tsc210x.h>
+
+
+/*
+ * TI TSC210x chips include an ADC that's shared between various
+ * sensors (temperature, battery, vAUX, etc) and the touchscreen.
+ * This driver packages access to the non-touchscreen sensors
+ * available on a given board.
+ */
+
+struct tsc210x_hwmon {
+       int bat[2], aux[2], temp[2];
+
+       struct device *dev;
+       struct tsc210x_config *pdata;
+#ifdef CONFIG_APM
+       /* prevent APM from colliding with normal hwmon accessors */
+       spinlock_t apm_lock;
+#endif
+};
+
+#ifdef CONFIG_APM
+# define apm_lock(h)   spin_lock(&(h)->apm_lock)
+# define apm_unlock(h) spin_unlock(&(h)->apm_lock)
+#else
+# define apm_lock(h)   do { } while (0)
+# define apm_unlock(h) do { } while (0)
+#endif
+
+static void tsc210x_ports(void *context, int bat[], int aux[])
+{
+       struct tsc210x_hwmon    *hwmon = context;
+
+       apm_lock(hwmon);
+
+       /* FIXME for tsc2101 and tsc2111, battery voltage is:
+        *      VBAT = (5 * VREF * (bat[x])) / (2 ^ bits)
+        * For tsc2100 and tsc2102, use "6" not "5"; that formula ignores
+        * an external 100-300 Ohm resistor making the right value be just
+        * a bit over 5 (or 6).
+        *
+        * FIXME the vAUX measurements need scaling too, but in that case
+        * there's no *internal* voltage divider so just scale to VREF.
+        *
+        *  --> This code needs to know VREF, the VBAT multiplier, and
+        *      the precision.  For now, assume VREF 1.25V and 12 bits.
+        *      When an external reference is used, it normally won't
+        *      match the 1.25V (or 2.5V) values supported internally...
+        *
+        *  --> Output units should become milliVolts; currently they are
+        *      dimensionless...
+        */
+       hwmon->bat[0] = bat[0];
+       hwmon->bat[1] = bat[1];
+
+       hwmon->aux[0] = aux[0];
+       hwmon->aux[1] = aux[1];
+
+       apm_unlock(hwmon);
+}
+
+/* FIXME temp sensors also need scaling so values are milliVolts...
+ * temperature (given calibration data) should be millidegrees C.
+ */
+
+static void tsc210x_temp1(void *context, int temp)
+{
+       struct tsc210x_hwmon    *hwmon = context;
+
+       apm_lock(hwmon);
+       hwmon->temp[0] = temp;
+       apm_unlock(hwmon);
+}
+
+static void tsc210x_temp2(void *context, int temp)
+{
+       struct tsc210x_hwmon    *hwmon = context;
+
+       apm_lock(hwmon);
+       hwmon->temp[1] = temp;
+       apm_unlock(hwmon);
+}
+
+#define TSC210X_INPUT(devname, field)  \
+static ssize_t tsc_show_ ## devname(struct device *dev,        \
+               struct device_attribute *devattr, char *buf)    \
+{      \
+       struct tsc210x_hwmon *hwmon = dev_get_drvdata(dev);     \
+       return sprintf(buf, "%i\n", hwmon->field);      \
+}      \
+static DEVICE_ATTR(devname ## _input, S_IRUGO, tsc_show_ ## devname, NULL);
+
+TSC210X_INPUT(in0, bat[0])
+TSC210X_INPUT(in1, bat[1])
+TSC210X_INPUT(in2, aux[0])
+TSC210X_INPUT(in3, aux[1])
+TSC210X_INPUT(in4, temp[0])
+TSC210X_INPUT(in5, temp[1])
+
+static ssize_t tsc_show_temp1(struct device *dev,
+               struct device_attribute *devattr, char *buf)
+{
+       struct tsc210x_hwmon *hwmon = dev_get_drvdata(dev);
+       int t1 = hwmon->temp[0];
+       int t2 = hwmon->temp[1];
+       int diff;
+       int value;
+
+       /*
+        * Use method #2 (differential) to calculate current temperature.
+        * The difference between TEMP2 and TEMP1 input values is
+        * multiplied by a constant to obtain current temperature.
+        * To find this constant we use the values measured at 25 C as
+        * thermometer calibration data.
+        *
+        * 298150 is 25 degrees Celcius represented in Kelvins and
+        * multiplied by 1000 for fixed point precision (273.15 + 25).
+        * 273150 is zero degrees Celcius.
+        */
+       diff = hwmon->pdata->temp_at25c[1] - hwmon->pdata->temp_at25c[0];
+       value = (t2 - t1) * 298150 / diff;      /* This is in Kelvins now */
+
+       value -= 273150;                        /* Celcius millidegree */
+       return sprintf(buf, "%i\n", value);
+}
+static DEVICE_ATTR(temp1_input, S_IRUGO, tsc_show_temp1, NULL);
+
+#ifdef CONFIG_APM
+static struct tsc210x_hwmon *apm_hwmon;
+
+static void tsc210x_get_power_status(struct apm_power_info *info)
+{
+       struct tsc210x_hwmon *hwmon = apm_hwmon;
+
+       apm_lock(hwmon);
+       hwmon->pdata->apm_report(info, hwmon->bat);
+       apm_unlock(hwmon);
+}
+#endif
+
+static int tsc210x_hwmon_probe(struct platform_device *pdev)
+{
+       struct tsc210x_hwmon *hwmon;
+       struct tsc210x_config *pdata = pdev->dev.platform_data;
+       int status = 0;
+
+       hwmon = (struct tsc210x_hwmon *)
+               kzalloc(sizeof(struct tsc210x_hwmon), GFP_KERNEL);
+       if (!hwmon) {
+               dev_dbg(&pdev->dev, "allocation failed\n");
+               return -ENOMEM;
+       }
+
+       hwmon->dev = hwmon_device_register(&pdev->dev);
+       if (IS_ERR(hwmon->dev)) {
+               kfree(hwmon);
+               dev_dbg(&pdev->dev, "registration failed\n");
+               return PTR_ERR(hwmon->dev);
+       }
+
+       hwmon->pdata = pdata;
+
+#ifdef CONFIG_APM
+       spin_lock_init(&hwmon->apm_lock);
+
+       if (pdata->apm_report) {
+               apm_hwmon = hwmon;
+               apm_get_power_status = tsc210x_get_power_status;
+       }
+#endif
+
+       platform_set_drvdata(pdev, hwmon);
+
+       if (pdata->monitor & (TSC_BAT1 | TSC_BAT2 | TSC_AUX1 | TSC_AUX2))
+               status |= tsc210x_ports_cb(pdev->dev.parent,
+                               tsc210x_ports, hwmon);
+       if (pdata->monitor & TSC_TEMP) {
+               status |= tsc210x_temp1_cb(pdev->dev.parent,
+                               tsc210x_temp1, hwmon);
+               status |= tsc210x_temp2_cb(pdev->dev.parent,
+                               tsc210x_temp2, hwmon);
+       }
+
+       if (status) {
+               tsc210x_ports_cb(pdev->dev.parent, NULL, NULL);
+               tsc210x_temp1_cb(pdev->dev.parent, NULL, NULL);
+               tsc210x_temp2_cb(pdev->dev.parent, NULL, NULL);
+               platform_set_drvdata(pdev, NULL);
+#ifdef CONFIG_APM
+               if (pdata->apm_report)
+                       apm_get_power_status = 0;
+#endif
+               hwmon_device_unregister(hwmon->dev);
+               kfree(hwmon);
+               return status;
+       }
+
+       if (pdata->monitor & TSC_BAT1)
+               status |= device_create_file(&pdev->dev, &dev_attr_in0_input);
+       if (pdata->monitor & TSC_BAT2)
+               status |= device_create_file(&pdev->dev, &dev_attr_in1_input);
+       if (pdata->monitor & TSC_AUX1)
+               status |= device_create_file(&pdev->dev, &dev_attr_in2_input);
+       if (pdata->monitor & TSC_AUX2)
+               status |= device_create_file(&pdev->dev, &dev_attr_in3_input);
+       if (pdata->monitor & TSC_TEMP) {
+               status |= device_create_file(&pdev->dev, &dev_attr_in4_input);
+               status |= device_create_file(&pdev->dev, &dev_attr_in5_input);
+
+               if ((pdata->temp_at25c[1] - pdata->temp_at25c[0]) == 0)
+                       dev_warn(&pdev->dev, "No temp calibration data.\n");
+               else
+                       status |= device_create_file(&pdev->dev,
+                                               &dev_attr_temp1_input);
+       }
+       if (status)     /* Not fatal */
+               dev_dbg(&pdev->dev, "Creating one or more "
+                               "attribute files failed\n");
+
+       return 0;
+}
+
+static int __exit tsc210x_hwmon_remove(struct platform_device *pdev)
+{
+       struct tsc210x_hwmon *dev = platform_get_drvdata(pdev);
+
+       tsc210x_ports_cb(pdev->dev.parent, NULL, NULL);
+       tsc210x_temp1_cb(pdev->dev.parent, NULL, NULL);
+       tsc210x_temp2_cb(pdev->dev.parent, NULL, NULL);
+       platform_set_drvdata(pdev, NULL);
+#ifdef CONFIG_APM
+       if (dev->pdata->apm_report)
+               apm_get_power_status = 0;
+#endif
+       hwmon_device_unregister(dev->dev);
+       kfree(dev);
+       return 0;
+}
+
+static struct platform_driver tsc210x_hwmon_driver = {
+       .probe          = tsc210x_hwmon_probe,
+       .remove         = __exit_p(tsc210x_hwmon_remove),
+       /* Nothing to do on suspend/resume */
+       .driver         = {
+               .name   = "tsc210x-hwmon",
+       },
+};
+
+static int __init tsc210x_hwmon_init(void)
+{
+       /* can't use driver_probe() here since the parent device
+        * gets registered "late"
+        */
+       return platform_driver_register(&tsc210x_hwmon_driver);
+}
+module_init(tsc210x_hwmon_init);
+
+static void __exit tsc210x_hwmon_exit(void)
+{
+       platform_driver_unregister(&tsc210x_hwmon_driver);
+}
+module_exit(tsc210x_hwmon_exit);
+
+MODULE_AUTHOR("Andrzej Zaborowski");
+MODULE_DESCRIPTION("hwmon driver for TI TSC210x-connected sensors.");
+MODULE_LICENSE("GPL");
index 711ca08ab7762ab19f6516c9a5ba24bedcf54c4e..f1e6aa7b93bbb9ae8e9e57d8e6eed9f1d8f94193 100644 (file)
@@ -5,6 +5,7 @@
 menuconfig I2C
        tristate "I2C support"
        depends on HAS_IOMEM
 menuconfig I2C
        tristate "I2C support"
        depends on HAS_IOMEM
+       default y if MACH_OMAP_H3 || MACH_OMAP_OSK
        ---help---
          I2C (pronounce: I-square-C) is a slow serial bus protocol used in
          many micro controller applications and developed by Philips.  SMBus,
        ---help---
          I2C (pronounce: I-square-C) is a slow serial bus protocol used in
          many micro controller applications and developed by Philips.  SMBus,
index ece0125a1ee520710f1e4ac18cd7b8eea83f0422..9919c086ff5d8188edb034ec2b578bf8bce64932 100644 (file)
@@ -672,8 +672,9 @@ omap_i2c_isr(int this_irq, void *dev_id)
                                if (stat & OMAP_I2C_STAT_RRDY)
                                        num_bytes = dev->fifo_size;
                                else
                                if (stat & OMAP_I2C_STAT_RRDY)
                                        num_bytes = dev->fifo_size;
                                else
-                                       num_bytes = omap_i2c_read_reg(dev,
-                                                       OMAP_I2C_BUFSTAT_REG);
+                                       num_bytes = (omap_i2c_read_reg(dev,
+                                                       OMAP_I2C_BUFSTAT_REG)
+                                                       >> 8) & 0x3F;
                        }
                        while (num_bytes) {
                                num_bytes--;
                        }
                        while (num_bytes) {
                                num_bytes--;
@@ -711,8 +712,9 @@ omap_i2c_isr(int this_irq, void *dev_id)
                                if (stat & OMAP_I2C_STAT_XRDY)
                                        num_bytes = dev->fifo_size;
                                else
                                if (stat & OMAP_I2C_STAT_XRDY)
                                        num_bytes = dev->fifo_size;
                                else
-                                       num_bytes = omap_i2c_read_reg(dev,
-                                                       OMAP_I2C_BUFSTAT_REG);
+                                       num_bytes = (omap_i2c_read_reg(dev,
+                                                       OMAP_I2C_BUFSTAT_REG))
+                                                       & 0x3F;
                        }
                        while (num_bytes) {
                                num_bytes--;
                        }
                        while (num_bytes) {
                                num_bytes--;
index 8f8c81eb0aee750006a7949ed530d10a4257503c..a01f40778ae61d57bd3c61cb0122e57d8c58ce39 100644 (file)
@@ -64,6 +64,31 @@ config SENSORS_PCA9539
          This driver is deprecated and will be dropped soon. Use
          drivers/gpio/pca953x.c instead.
 
          This driver is deprecated and will be dropped soon. Use
          drivers/gpio/pca953x.c instead.
 
+config TWL4030_MADC
+       tristate "TWL4030 MADC Driver"
+       depends on TWL4030_CORE
+       help
+         The TWL4030 Monitoring ADC driver enables the host
+         processor to monitor analog signals using analog-to-digital
+         conversions on the input source. TWL4030 MADC provides the
+         following features:
+          - Single 10-bit ADC with successive approximation register (SAR) conversion;
+          - Analog multiplexer for 16 inputs;
+          - Seven (of the 16) inputs are freely available;
+          - Battery voltage monitoring;
+          - Concurrent conversion request management;
+          - Interrupt signal to Primary Interrupt Handler;
+          - Averaging feature;
+          - Selective enable/disable of the averaging feature.
+
+         Say 'y' here to statically link this module into the kernel or 'm'
+         to build it as a dinamically loadable module. The module will be
+         called twl4030-madc.ko
+
+config TWL4030_POWEROFF
+       tristate "TWL4030 device poweroff"
+       depends on TWL4030_CORE
+
 config SENSORS_MAX6875
        tristate "Maxim MAX6875 Power supply supervisor"
        depends on EXPERIMENTAL
 config SENSORS_MAX6875
        tristate "Maxim MAX6875 Power supply supervisor"
        depends on EXPERIMENTAL
@@ -89,4 +114,24 @@ config SENSORS_TSL2550
          This driver can also be built as a module.  If so, the module
          will be called tsl2550.
 
          This driver can also be built as a module.  If so, the module
          will be called tsl2550.
 
+config SENSORS_TSL2563
+       tristate "Taos TSL2563 ambient light sensor"
+       depends on I2C && HWMON
+       help
+         If you say yes here you get support for the Taos TSL2563
+         ambient light sensor.
+
+         This driver can also be built as a module.  If so, the module
+         will be called tsl2563.
+
+config MENELAUS
+       bool "TWL92330/Menelaus PM chip"
+       depends on I2C=y && ARCH_OMAP24XX
+       help
+         If you say yes here you get support for the Texas Instruments
+         TWL92330/Menelaus Power Management chip. This include voltage
+         regulators, Dual slot memory card tranceivers, real-time clock
+         and other features that are often used in portable devices like
+         cell phones and PDAs.
+
 endmenu
 endmenu
index 55a37603718332dcceab0ef5c90f38a6cb054a8e..0f8a24539a9854a6d3534c40f7074283a2ad7c62 100644 (file)
@@ -16,8 +16,10 @@ obj-$(CONFIG_SENSORS_PCA9539)        += pca9539.o
 obj-$(CONFIG_SENSORS_PCF8574)  += pcf8574.o
 obj-$(CONFIG_PCF8575)          += pcf8575.o
 obj-$(CONFIG_SENSORS_TSL2550)  += tsl2550.o
 obj-$(CONFIG_SENSORS_PCF8574)  += pcf8574.o
 obj-$(CONFIG_PCF8575)          += pcf8575.o
 obj-$(CONFIG_SENSORS_TSL2550)  += tsl2550.o
+obj-$(CONFIG_TWL4030_POWEROFF) += twl4030-poweroff.o
+obj-$(CONFIG_TWL4030_MADC)     += twl4030-madc.o
+obj-$(CONFIG_RTC_X1205_I2C)    += x1205.o
 
 ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG
 endif
 
 ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
 EXTRA_CFLAGS += -DDEBUG
 endif
-
diff --git a/drivers/i2c/chips/tsl2563.c b/drivers/i2c/chips/tsl2563.c
new file mode 100644 (file)
index 0000000..e05b880
--- /dev/null
@@ -0,0 +1,739 @@
+/*
+ * drivers/i2c/chips/tsl2563.c
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Written by Timo O. Karjalainen <timo.o.karjalainen@nokia.com>
+ * Contact: Mathias Nyman <mathias.nyman@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/hwmon.h>
+#include <linux/err.h>
+#include <mach/board.h>
+
+#define DRIVER_NAME  "tsl2563"
+
+/* Use this many bits for fraction part. */
+#define ADC_FRAC_BITS          (14)
+
+/* Given number of 1/10000's in ADC_FRAC_BITS precision. */
+#define FRAC10K(f)             (((f) * (1L << (ADC_FRAC_BITS))) / (10000))
+
+/* Bits used for fraction in calibration coefficients.*/
+#define CALIB_FRAC_BITS                (10)
+/* 0.5 in CALIB_FRAC_BITS precision */
+#define CALIB_FRAC_HALF                (1 << (CALIB_FRAC_BITS - 1))
+/* Make a fraction from a number n that was multiplied with b. */
+#define CALIB_FRAC(n, b)       (((n) << CALIB_FRAC_BITS) / (b))
+/* Decimal 10^(digits in sysfs presentation) */
+#define CALIB_BASE_SYSFS       (1000)
+
+#define TSL2563_CMD            (0x80)
+#define TSL2563_CLEARINT       (0x40)
+
+#define TSL2563_REG_CTRL       (0x00)
+#define TSL2563_REG_TIMING     (0x01)
+#define TSL2563_REG_LOWLOW     (0x02) /* data0 low threshold, 2 bytes */
+#define TSL2563_REG_LOWHIGH    (0x03)
+#define TSL2563_REG_HIGHLOW    (0x04) /* data0 high threshold, 2 bytes */
+#define TSL2563_REG_HIGHHIGH   (0x05)
+#define TSL2563_REG_INT                (0x06)
+#define TSL2563_REG_ID         (0x0a)
+#define TSL2563_REG_DATA0LOW   (0x0c) /* broadband sensor value, 2 bytes */
+#define TSL2563_REG_DATA0HIGH  (0x0d)
+#define TSL2563_REG_DATA1LOW   (0x0e) /* infrared sensor value, 2 bytes */
+#define TSL2563_REG_DATA1HIGH  (0x0f)
+
+#define TSL2563_CMD_POWER_ON   (0x03)
+#define TSL2563_CMD_POWER_OFF  (0x00)
+#define TSL2563_CTRL_POWER_MASK        (0x03)
+
+#define TSL2563_TIMING_13MS    (0x00)
+#define TSL2563_TIMING_100MS   (0x01)
+#define TSL2563_TIMING_400MS   (0x02)
+#define TSL2563_TIMING_MASK    (0x03)
+#define TSL2563_TIMING_GAIN16  (0x10)
+#define TSL2563_TIMING_GAIN1   (0x00)
+
+#define TSL2563_INT_DISBLED    (0x00)
+#define TSL2563_INT_LEVEL      (0x10)
+#define TSL2563_INT_PERSIST(n) ((n) & 0x0F)
+
+struct tsl2563_gainlevel_coeff {
+       u8 gaintime;
+       u16 min;
+       u16 max;
+};
+
+static struct tsl2563_gainlevel_coeff tsl2563_gainlevel_table[] = {
+       {
+               .gaintime       = TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN16,
+               .min            = 0,
+               .max            = 65534,
+       }, {
+               .gaintime       = TSL2563_TIMING_400MS | TSL2563_TIMING_GAIN1,
+               .min            = 2048,
+               .max            = 65534,
+       }, {
+               .gaintime       = TSL2563_TIMING_100MS | TSL2563_TIMING_GAIN1,
+               .min            = 4095,
+               .max            = 37177,
+       }, {
+               .gaintime       = TSL2563_TIMING_13MS | TSL2563_TIMING_GAIN1,
+               .min            = 3000,
+               .max            = 65535,
+       },
+};
+
+struct tsl2563_chip {
+       struct mutex            lock;
+       struct i2c_client       *client;
+       struct device           *hwmon_dev;
+
+       /* Remember state for suspend and resume functions */
+       pm_message_t            state;
+
+       struct tsl2563_gainlevel_coeff *gainlevel;
+
+       /* Thresholds are in lux */
+       u16                     low_thres;
+       u16                     high_thres;
+       u8                      intr;
+
+       /* Calibration coefficients */
+       u32                     calib0;
+       u32                     calib1;
+
+       /* Cache current values, to be returned while suspended */
+       u32                     data0;
+       u32                     data1;
+};
+
+static int tsl2563_write(struct i2c_client *client, u8 reg, u8 value)
+{
+       int ret;
+       u8 buf[2];
+
+       buf[0] = TSL2563_CMD | reg;
+       buf[1] = value;
+
+       ret = i2c_master_send(client, buf, sizeof(buf));
+       return (ret == sizeof(buf)) ? 0 : ret;
+}
+
+static int tsl2563_read(struct i2c_client *client, u8 reg, void *buf, int len)
+{
+       int ret;
+       u8 cmd = TSL2563_CMD | reg;
+
+       ret = i2c_master_send(client, &cmd, sizeof(cmd));
+       if (ret != sizeof(cmd))
+               return ret;
+
+       return i2c_master_recv(client, buf, len);
+}
+
+static int tsl2563_set_power(struct tsl2563_chip *chip, int on)
+{
+       struct i2c_client *client = chip->client;
+       u8 cmd;
+
+       cmd = on ? TSL2563_CMD_POWER_ON : TSL2563_CMD_POWER_OFF;
+       return tsl2563_write(client, TSL2563_REG_CTRL, cmd);
+}
+
+/*
+ * Return value is 0 for off, 1 for on, or a negative error
+ * code if reading failed.
+ */
+static int tsl2563_get_power(struct tsl2563_chip *chip)
+{
+       struct i2c_client *client = chip->client;
+       int ret;
+       u8 val;
+
+       ret = tsl2563_read(client, TSL2563_REG_CTRL, &val, sizeof(val));
+       if (ret != sizeof(val))
+               return ret;
+
+       return (val & TSL2563_CTRL_POWER_MASK) == TSL2563_CMD_POWER_ON;
+}
+
+static int tsl2563_configure(struct tsl2563_chip *chip)
+{
+       struct i2c_client *client = chip->client;
+       int ret;
+
+       ret = tsl2563_write(client, TSL2563_REG_TIMING,
+                       chip->gainlevel->gaintime);
+       if (ret)
+               goto out;
+
+       ret = tsl2563_write(client, TSL2563_REG_INT, chip->intr);
+
+out:
+       return ret;
+}
+
+static int tsl2563_detect(struct tsl2563_chip *chip)
+{
+       int ret;
+
+       ret = tsl2563_set_power(chip, 1);
+       if (ret)
+               return ret;
+
+       ret = tsl2563_get_power(chip);
+       if (ret < 0)
+               return ret;
+
+       return ret ? 0 : -ENODEV;
+}
+
+static int tsl2563_read_id(struct tsl2563_chip *chip, u8 *id)
+{
+       struct i2c_client *client = chip->client;
+       int ret;
+
+       ret = tsl2563_read(client, TSL2563_REG_ID, id, sizeof(*id));
+       if (ret != sizeof(*id))
+               return ret;
+
+       return 0;
+}
+
+/*
+ * "Normalized" ADC value is one obtained with 400ms of integration time and
+ * 16x gain. This function returns the number of bits of shift needed to
+ * convert between normalized values and HW values obtained using given
+ * timing and gain settings.
+ */
+static int adc_shiftbits(u8 timing)
+{
+       int shift = 0;
+
+       switch (timing & TSL2563_TIMING_MASK) {
+       case TSL2563_TIMING_13MS:
+               shift += 5;
+               break;
+       case TSL2563_TIMING_100MS:
+               shift += 2;
+               break;
+       case TSL2563_TIMING_400MS:
+               /* no-op */
+               break;
+       }
+
+       if (!(timing & TSL2563_TIMING_GAIN16))
+               shift += 4;
+
+       return shift;
+}
+
+/* Convert a HW ADC value to normalized scale. */
+static u32 normalize_adc(u16 adc, u8 timing)
+{
+       return adc << adc_shiftbits(timing);
+}
+
+static void tsl2563_wait_adc(struct tsl2563_chip *chip)
+{
+       unsigned int delay;
+
+       switch (chip->gainlevel->gaintime & TSL2563_TIMING_MASK) {
+       case TSL2563_TIMING_13MS:
+               delay = 14;
+               break;
+       case TSL2563_TIMING_100MS:
+               delay = 101;
+               break;
+       default:
+               delay = 402;
+       }
+       /*
+        * TODO: Make sure that we wait at least required delay but why we
+        * have to extend it one tick more?
+        */
+       schedule_timeout_interruptible(msecs_to_jiffies(delay) + 2);
+}
+
+static int tsl2563_adjust_gainlevel(struct tsl2563_chip *chip, u16 adc)
+{
+       struct i2c_client *client = chip->client;
+
+       if (adc > chip->gainlevel->max || adc < chip->gainlevel->min) {
+
+               (adc > chip->gainlevel->max) ?
+                       chip->gainlevel++ : chip->gainlevel--;
+
+               tsl2563_write(client, TSL2563_REG_TIMING,
+                             chip->gainlevel->gaintime);
+
+               tsl2563_wait_adc(chip);
+               tsl2563_wait_adc(chip);
+
+               return 1;
+       } else
+               return 0;
+}
+
+static int tsl2563_get_adc(struct tsl2563_chip *chip)
+{
+       struct i2c_client *client = chip->client;
+       u8 buf0[2], buf1[2];
+       u16 adc0, adc1;
+       int retry = 1;
+       int ret = 0;
+
+       if (chip->state.event != PM_EVENT_ON)
+               goto out;
+
+       while (retry) {
+               ret = tsl2563_read(client,
+                                  TSL2563_REG_DATA0LOW | TSL2563_CLEARINT,
+                                  buf0, sizeof(buf0));
+               if (ret != sizeof(buf0))
+                       goto out;
+
+               ret = tsl2563_read(client, TSL2563_REG_DATA1LOW,
+                                  buf1, sizeof(buf1));
+               if (ret != sizeof(buf1))
+                       goto out;
+
+               adc0 = (buf0[1] << 8) + buf0[0];
+               adc1 = (buf1[1] << 8) + buf1[0];
+
+               retry = tsl2563_adjust_gainlevel(chip, adc0);
+       }
+
+       chip->data0 = normalize_adc(adc0, chip->gainlevel->gaintime);
+       chip->data1 = normalize_adc(adc1, chip->gainlevel->gaintime);
+
+       ret = 0;
+out:
+       return ret;
+}
+
+static inline int calib_to_sysfs(u32 calib)
+{
+       return (int) (((calib * CALIB_BASE_SYSFS) +
+                      CALIB_FRAC_HALF) >> CALIB_FRAC_BITS);
+}
+
+static inline u32 calib_from_sysfs(int value)
+{
+       return (((u32) value) << CALIB_FRAC_BITS) / CALIB_BASE_SYSFS;
+}
+
+/*
+ * Conversions between lux and ADC values.
+ *
+ * The basic formula is lux = c0 * adc0 - c1 * adc1, where c0 and c1 are
+ * appropriate constants. Different constants are needed for different
+ * kinds of light, determined by the ratio adc1/adc0 (basically the ratio
+ * of the intensities in infrared and visible wavelengths). lux_table below
+ * lists the upper threshold of the adc1/adc0 ratio and the corresponding
+ * constants.
+ */
+
+struct tsl2563_lux_coeff {
+       unsigned long ch_ratio;
+       unsigned long ch0_coeff;
+       unsigned long ch1_coeff;
+};
+
+static const struct tsl2563_lux_coeff lux_table[] = {
+       {
+               .ch_ratio       = FRAC10K(1300),
+               .ch0_coeff      = FRAC10K(315),
+               .ch1_coeff      = FRAC10K(262),
+       }, {
+               .ch_ratio       = FRAC10K(2600),
+               .ch0_coeff      = FRAC10K(337),
+               .ch1_coeff      = FRAC10K(430),
+       }, {
+               .ch_ratio       = FRAC10K(3900),
+               .ch0_coeff      = FRAC10K(363),
+               .ch1_coeff      = FRAC10K(529),
+       }, {
+               .ch_ratio       = FRAC10K(5200),
+               .ch0_coeff      = FRAC10K(392),
+               .ch1_coeff      = FRAC10K(605),
+       }, {
+               .ch_ratio       = FRAC10K(6500),
+               .ch0_coeff      = FRAC10K(229),
+               .ch1_coeff      = FRAC10K(291),
+       }, {
+               .ch_ratio       = FRAC10K(8000),
+               .ch0_coeff      = FRAC10K(157),
+               .ch1_coeff      = FRAC10K(180),
+       }, {
+               .ch_ratio       = FRAC10K(13000),
+               .ch0_coeff      = FRAC10K(34),
+               .ch1_coeff      = FRAC10K(26),
+       }, {
+               .ch_ratio       = ULONG_MAX,
+               .ch0_coeff      = 0,
+               .ch1_coeff      = 0,
+       },
+};
+
+/*
+ * Convert normalized, scaled ADC values to lux.
+ */
+static unsigned int adc_to_lux(u32 adc0, u32 adc1)
+{
+       const struct tsl2563_lux_coeff *lp = lux_table;
+       unsigned long ratio, lux, ch0 = adc0, ch1 = adc1;
+
+       ratio = ch0 ? ((ch1 << ADC_FRAC_BITS) / ch0) : ULONG_MAX;
+
+       while (lp->ch_ratio < ratio)
+               lp++;
+
+       lux = ch0 * lp->ch0_coeff - ch1 * lp->ch1_coeff;
+
+       return (unsigned int) (lux >> ADC_FRAC_BITS);
+}
+
+/*--------------------------------------------------------------*/
+/*                      Sysfs interface                         */
+/*--------------------------------------------------------------*/
+
+static ssize_t tsl2563_adc0_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct tsl2563_chip *chip = dev_get_drvdata(dev);
+       int ret;
+
+       mutex_lock(&chip->lock);
+
+       ret = tsl2563_get_adc(chip);
+       if (ret)
+               return ret;
+
+       ret = snprintf(buf, PAGE_SIZE, "%d\n", chip->data0);
+       mutex_unlock(&chip->lock);
+
+       return ret;
+}
+
+static ssize_t tsl2563_adc1_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct tsl2563_chip *chip = dev_get_drvdata(dev);
+       int ret;
+
+       mutex_lock(&chip->lock);
+
+       ret = tsl2563_get_adc(chip);
+       if (ret)
+               return ret;
+
+       ret = snprintf(buf, PAGE_SIZE, "%d\n", chip->data1);
+       mutex_unlock(&chip->lock);
+
+       return ret;
+}
+
+/* Apply calibration coefficient to ADC count. */
+static u32 calib_adc(u32 adc, u32 calib)
+{
+       unsigned long scaled = adc;
+
+       scaled *= calib;
+       scaled >>= CALIB_FRAC_BITS;
+
+       return (u32) scaled;
+}
+
+static ssize_t tsl2563_lux_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct tsl2563_chip *chip = dev_get_drvdata(dev);
+       u32 calib0, calib1;
+       int ret;
+
+       mutex_lock(&chip->lock);
+
+       ret = tsl2563_get_adc(chip);
+       if (ret)
+               goto out;
+
+       calib0 = calib_adc(chip->data0, chip->calib0);
+       calib1 = calib_adc(chip->data1, chip->calib1);
+
+       ret = snprintf(buf, PAGE_SIZE, "%d\n", adc_to_lux(calib0, calib1));
+
+out:
+       mutex_unlock(&chip->lock);
+       return ret;
+}
+
+static ssize_t format_calib(char *buf, int len, u32 calib)
+{
+       return snprintf(buf, PAGE_SIZE, "%d\n", calib_to_sysfs(calib));
+}
+
+static ssize_t tsl2563_calib0_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct tsl2563_chip *chip = dev_get_drvdata(dev);
+       int ret;
+
+       mutex_lock(&chip->lock);
+       ret = format_calib(buf, PAGE_SIZE, chip->calib0);
+       mutex_unlock(&chip->lock);
+       return ret;
+}
+
+static ssize_t tsl2563_calib1_show(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct tsl2563_chip *chip = dev_get_drvdata(dev);
+       int ret;
+
+       mutex_lock(&chip->lock);
+       ret = format_calib(buf, PAGE_SIZE, chip->calib1);
+       mutex_unlock(&chip->lock);
+       return ret;
+}
+
+static int do_calib_store(struct device *dev, const char *buf, size_t len,
+                         int ch)
+{
+       struct tsl2563_chip *chip = dev_get_drvdata(dev);
+       int value;
+       u32 calib;
+
+       if (1 != sscanf(buf, "%d", &value))
+               return -EINVAL;
+
+       calib = calib_from_sysfs(value);
+
+       if (ch)
+               chip->calib1 = calib;
+       else
+               chip->calib0 = calib;
+
+       return len;
+}
+
+static ssize_t tsl2563_calib0_store(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t len)
+{
+       return do_calib_store(dev, buf, len, 0);
+}
+
+static ssize_t tsl2563_calib1_store(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t len)
+{
+       return do_calib_store(dev, buf, len, 1);
+}
+
+static DEVICE_ATTR(adc0, S_IRUGO, tsl2563_adc0_show, NULL);
+static DEVICE_ATTR(adc1, S_IRUGO, tsl2563_adc1_show, NULL);
+static DEVICE_ATTR(lux, S_IRUGO, tsl2563_lux_show, NULL);
+static DEVICE_ATTR(calib0, S_IRUGO | S_IWUSR,
+                  tsl2563_calib0_show, tsl2563_calib0_store);
+static DEVICE_ATTR(calib1, S_IRUGO | S_IWUSR,
+                  tsl2563_calib1_show, tsl2563_calib1_store);
+
+static struct attribute *tsl2563_attributes[] = {
+       &dev_attr_adc0.attr,
+       &dev_attr_adc1.attr,
+       &dev_attr_lux.attr,
+       &dev_attr_calib0.attr,
+       &dev_attr_calib1.attr,
+       NULL
+};
+
+static const struct attribute_group tsl2563_group = {
+       .attrs = tsl2563_attributes,
+};
+
+static int tsl2563_register_sysfs(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+
+       return sysfs_create_group(&dev->kobj, &tsl2563_group);
+}
+
+static void tsl2563_unregister_sysfs(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+
+       sysfs_remove_group(&dev->kobj, &tsl2563_group);
+}
+
+/*--------------------------------------------------------------*/
+/*                      Probe, Attach, Remove                   */
+/*--------------------------------------------------------------*/
+static struct i2c_driver tsl2563_i2c_driver;
+
+static int tsl2563_probe(struct i2c_client *client,
+               const struct i2c_device_id *device_id)
+{
+       struct tsl2563_chip *chip;
+       int err = 0;
+       u8 id;
+
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, chip);
+       chip->client = client;
+
+       err = tsl2563_detect(chip);
+       if (err) {
+               dev_err(&client->dev, "device not found, error %d \n", -err);
+               goto fail1;
+       }
+
+       err = tsl2563_read_id(chip, &id);
+       if (err)
+               goto fail1;
+
+       mutex_init(&chip->lock);
+
+       /* Default values used until userspace says otherwise */
+       chip->low_thres = 0x0;
+       chip->high_thres = 0xffff;
+       chip->gainlevel = tsl2563_gainlevel_table;
+       chip->intr = TSL2563_INT_PERSIST(4);
+       chip->calib0 = calib_from_sysfs(CALIB_BASE_SYSFS);
+       chip->calib1 = calib_from_sysfs(CALIB_BASE_SYSFS);
+
+       dev_info(&client->dev, "model %d, rev. %d\n", id >> 4, id & 0x0f);
+
+       err = tsl2563_configure(chip);
+       if (err)
+               goto fail1;
+
+       chip->hwmon_dev = hwmon_device_register(&client->dev);
+       if (IS_ERR(chip->hwmon_dev))
+               goto fail1;
+
+       err = tsl2563_register_sysfs(client);
+       if (err) {
+               dev_err(&client->dev, "sysfs registration failed, %d\n", err);
+               goto fail2;
+       }
+
+       return 0;
+fail2:
+       hwmon_device_unregister(chip->hwmon_dev);
+fail1:
+       kfree(chip);
+       return err;
+}
+
+static int tsl2563_remove(struct i2c_client *client)
+{
+       struct tsl2563_chip *chip = i2c_get_clientdata(client);
+
+       tsl2563_unregister_sysfs(client);
+       hwmon_device_unregister(chip->hwmon_dev);
+
+       kfree(chip);
+       return 0;
+}
+
+static int tsl2563_suspend(struct i2c_client *client, pm_message_t state)
+{
+       struct tsl2563_chip *chip = i2c_get_clientdata(client);
+       int ret;
+
+       mutex_lock(&chip->lock);
+
+       ret = tsl2563_set_power(chip, 0);
+       if (ret)
+               goto out;
+
+       chip->state = state;
+
+out:
+       mutex_unlock(&chip->lock);
+       return ret;
+}
+
+static int tsl2563_resume(struct i2c_client *client)
+{
+       struct tsl2563_chip *chip = i2c_get_clientdata(client);
+       int ret;
+
+       mutex_lock(&chip->lock);
+
+       ret = tsl2563_set_power(chip, 1);
+       if (ret)
+               goto out;
+
+       ret = tsl2563_configure(chip);
+       if (ret)
+               goto out;
+
+       chip->state.event = PM_EVENT_ON;
+
+out:
+       mutex_unlock(&chip->lock);
+       return ret;
+}
+
+static const struct i2c_device_id tsl2563_id[] = {
+       { DRIVER_NAME, 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, tsl2563_id);
+
+static struct i2c_driver tsl2563_i2c_driver = {
+       .driver = {
+               .name    = DRIVER_NAME,
+       },
+       .suspend        = tsl2563_suspend,
+       .resume         = tsl2563_resume,
+       .probe          = tsl2563_probe,
+       .remove         = __devexit_p(tsl2563_remove),
+       .id_table       = tsl2563_id,
+};
+
+static int __init tsl2563_init(void)
+{
+       return i2c_add_driver(&tsl2563_i2c_driver);
+}
+
+static void __exit tsl2563_exit(void)
+{
+       i2c_del_driver(&tsl2563_i2c_driver);
+}
+
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("tsl2563 light sensor driver");
+MODULE_LICENSE("GPL");
+
+module_init(tsl2563_init);
+module_exit(tsl2563_exit);
diff --git a/drivers/i2c/chips/twl4030-madc.c b/drivers/i2c/chips/twl4030-madc.c
new file mode 100644 (file)
index 0000000..d3e0a7f
--- /dev/null
@@ -0,0 +1,528 @@
+/*
+ * drivers/i2c/chips/twl4030-madc.c
+ *
+ * TWL4030 MADC module driver
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Mikko Ylinen <mikko.k.ylinen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/i2c/twl4030-madc.h>
+
+#include <asm/uaccess.h>
+
+#define TWL4030_MADC_PFX       "twl4030-madc: "
+
+struct twl4030_madc_data {
+       struct device           *dev;
+       struct mutex            lock;
+       struct work_struct      ws;
+       struct twl4030_madc_request     requests[TWL4030_MADC_NUM_METHODS];
+       int imr;
+       int isr;
+};
+
+static struct twl4030_madc_data *the_madc;
+
+static
+const struct twl4030_madc_conversion_method twl4030_conversion_methods[] = {
+       [TWL4030_MADC_RT] = {
+               .sel    = TWL4030_MADC_RTSELECT_LSB,
+               .avg    = TWL4030_MADC_RTAVERAGE_LSB,
+               .rbase  = TWL4030_MADC_RTCH0_LSB,
+       },
+       [TWL4030_MADC_SW1] = {
+               .sel    = TWL4030_MADC_SW1SELECT_LSB,
+               .avg    = TWL4030_MADC_SW1AVERAGE_LSB,
+               .rbase  = TWL4030_MADC_GPCH0_LSB,
+               .ctrl   = TWL4030_MADC_CTRL_SW1,
+       },
+       [TWL4030_MADC_SW2] = {
+               .sel    = TWL4030_MADC_SW2SELECT_LSB,
+               .avg    = TWL4030_MADC_SW2AVERAGE_LSB,
+               .rbase  = TWL4030_MADC_GPCH0_LSB,
+               .ctrl   = TWL4030_MADC_CTRL_SW2,
+       },
+};
+
+static int twl4030_madc_read(struct twl4030_madc_data *madc, u8 reg)
+{
+       int ret;
+       u8 val;
+
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MADC, &val, reg);
+       if (ret) {
+               dev_dbg(madc->dev, "unable to read register 0x%X\n", reg);
+               return ret;
+       }
+
+       return val;
+}
+
+static void twl4030_madc_write(struct twl4030_madc_data *madc, u8 reg, u8 val)
+{
+       int ret;
+
+       ret = twl4030_i2c_write_u8(TWL4030_MODULE_MADC, val, reg);
+       if (ret)
+               dev_err(madc->dev, "unable to write register 0x%X\n", reg);
+}
+
+static int twl4030_madc_channel_raw_read(struct twl4030_madc_data *madc, u8 reg)
+{
+       u8 msb, lsb;
+
+       /* For each ADC channel, we have MSB and LSB register pair. MSB address
+        * is always LSB address+1. reg parameter is the addr of LSB register */
+       msb = twl4030_madc_read(madc, reg + 1);
+       lsb = twl4030_madc_read(madc, reg);
+
+       return (int)(((msb << 8) | lsb) >> 6);
+}
+
+static int twl4030_madc_read_channels(struct twl4030_madc_data *madc,
+               u8 reg_base, u16 channels, int *buf)
+{
+       int count = 0;
+       u8 reg, i;
+
+       if (unlikely(!buf))
+               return 0;
+
+       for (i = 0; i < TWL4030_MADC_MAX_CHANNELS; i++) {
+               if (channels & (1<<i)) {
+                       reg = reg_base + 2*i;
+                       buf[i] = twl4030_madc_channel_raw_read(madc, reg);
+                       count++;
+               }
+       }
+       return count;
+}
+
+static void twl4030_madc_enable_irq(struct twl4030_madc_data *madc, int id)
+{
+       u8 val;
+
+       val = twl4030_madc_read(madc, madc->imr);
+       val &= ~(1 << id);
+       twl4030_madc_write(madc, madc->imr, val);
+}
+
+static void twl4030_madc_disable_irq(struct twl4030_madc_data *madc, int id)
+{
+       u8 val;
+
+       val = twl4030_madc_read(madc, madc->imr);
+       val |= (1 << id);
+       twl4030_madc_write(madc, madc->imr, val);
+}
+
+static irqreturn_t twl4030_madc_irq_handler(int irq, void *_madc)
+{
+       struct twl4030_madc_data *madc = _madc;
+       u8 isr_val, imr_val;
+       int i;
+
+#ifdef CONFIG_LOCKDEP
+       /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
+        * we don't want and can't tolerate.  Although it might be
+        * friendlier not to borrow this thread context...
+        */
+       local_irq_enable();
+#endif
+
+       /* Use COR to ack interrupts since we have no shared IRQs in ISRx */
+       isr_val = twl4030_madc_read(madc, madc->isr);
+       imr_val = twl4030_madc_read(madc, madc->imr);
+
+       isr_val &= ~imr_val;
+
+       for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
+
+               if (!(isr_val & (1<<i)))
+                       continue;
+
+               twl4030_madc_disable_irq(madc, i);
+               madc->requests[i].result_pending = 1;
+       }
+
+       schedule_work(&madc->ws);
+
+       return IRQ_HANDLED;
+}
+
+static void twl4030_madc_work(struct work_struct *ws)
+{
+       const struct twl4030_madc_conversion_method *method;
+       struct twl4030_madc_data *madc;
+       struct twl4030_madc_request *r;
+       int len, i;
+
+       madc = container_of(ws, struct twl4030_madc_data, ws);
+       mutex_lock(&madc->lock);
+
+       for (i = 0; i < TWL4030_MADC_NUM_METHODS; i++) {
+
+               r = &madc->requests[i];
+
+               /* No pending results for this method, move to next one */
+               if (!r->result_pending)
+                       continue;
+
+               method = &twl4030_conversion_methods[r->method];
+
+               /* Read results */
+               len = twl4030_madc_read_channels(madc, method->rbase,
+                                                r->channels, r->rbuf);
+
+               /* Return results to caller */
+               if (r->func_cb != NULL) {
+                       r->func_cb(len, r->channels, r->rbuf);
+                       r->func_cb = NULL;
+               }
+
+               /* Free request */
+               r->result_pending = 0;
+               r->active         = 0;
+       }
+
+       mutex_unlock(&madc->lock);
+}
+
+static int twl4030_madc_set_irq(struct twl4030_madc_data *madc,
+               struct twl4030_madc_request *req)
+{
+       struct twl4030_madc_request *p;
+
+       p = &madc->requests[req->method];
+
+       memcpy(p, req, sizeof *req);
+
+       twl4030_madc_enable_irq(madc, req->method);
+
+       return 0;
+}
+
+static inline void twl4030_madc_start_conversion(struct twl4030_madc_data *madc,
+               int conv_method)
+{
+       const struct twl4030_madc_conversion_method *method;
+
+       method = &twl4030_conversion_methods[conv_method];
+
+       switch (conv_method) {
+       case TWL4030_MADC_SW1:
+       case TWL4030_MADC_SW2:
+               twl4030_madc_write(madc, method->ctrl, TWL4030_MADC_SW_START);
+               break;
+       case TWL4030_MADC_RT:
+       default:
+               break;
+       }
+}
+
+static int twl4030_madc_wait_conversion_ready(
+               struct twl4030_madc_data *madc,
+               unsigned int timeout_ms, u8 status_reg)
+{
+       unsigned long timeout;
+
+       timeout = jiffies + msecs_to_jiffies(timeout_ms);
+       do {
+               u8 reg;
+
+               reg = twl4030_madc_read(madc, status_reg);
+               if (!(reg & TWL4030_MADC_BUSY) && (reg & TWL4030_MADC_EOC_SW))
+                       return 0;
+       } while (!time_after(jiffies, timeout));
+
+       return -EAGAIN;
+}
+
+int twl4030_madc_conversion(struct twl4030_madc_request *req)
+{
+       const struct twl4030_madc_conversion_method *method;
+       u8 ch_msb, ch_lsb;
+       int ret;
+
+       if (unlikely(!req))
+               return -EINVAL;
+
+       mutex_lock(&the_madc->lock);
+
+       /* Do we have a conversion request ongoing */
+       if (the_madc->requests[req->method].active) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       ch_msb = (req->channels >> 8) & 0xff;
+       ch_lsb = req->channels & 0xff;
+
+       method = &twl4030_conversion_methods[req->method];
+
+       /* Select channels to be converted */
+       twl4030_madc_write(the_madc, method->sel + 1, ch_msb);
+       twl4030_madc_write(the_madc, method->sel, ch_lsb);
+
+       /* Select averaging for all channels if do_avg is set */
+       if (req->do_avg) {
+               twl4030_madc_write(the_madc, method->avg + 1, ch_msb);
+               twl4030_madc_write(the_madc, method->avg, ch_lsb);
+       }
+
+       if ((req->type == TWL4030_MADC_IRQ_ONESHOT) && (req->func_cb != NULL)) {
+               twl4030_madc_set_irq(the_madc, req);
+               twl4030_madc_start_conversion(the_madc, req->method);
+               the_madc->requests[req->method].active = 1;
+               ret = 0;
+               goto out;
+       }
+
+       /* With RT method we should not be here anymore */
+       if (req->method == TWL4030_MADC_RT) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       twl4030_madc_start_conversion(the_madc, req->method);
+       the_madc->requests[req->method].active = 1;
+
+       /* Wait until conversion is ready (ctrl register returns EOC) */
+       ret = twl4030_madc_wait_conversion_ready(the_madc, 5, method->ctrl);
+       if (ret) {
+               dev_dbg(the_madc->dev, "conversion timeout!\n");
+               the_madc->requests[req->method].active = 0;
+               goto out;
+       }
+
+       ret = twl4030_madc_read_channels(the_madc, method->rbase, req->channels,
+                                        req->rbuf);
+
+       the_madc->requests[req->method].active = 0;
+
+out:
+       mutex_unlock(&the_madc->lock);
+
+       return ret;
+}
+EXPORT_SYMBOL(twl4030_madc_conversion);
+
+static int twl4030_madc_set_current_generator(struct twl4030_madc_data *madc,
+               int chan, int on)
+{
+       int ret;
+       u8 regval;
+
+       /* Current generator is only available for ADCIN0 and ADCIN1. NB:
+        * ADCIN1 current generator only works when AC or VBUS is present */
+       if (chan > 1)
+               return EINVAL;
+
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
+                                 &regval, TWL4030_BCI_BCICTL1);
+       if (on)
+               regval |= (chan) ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN;
+       else
+               regval &= (chan) ? ~TWL4030_BCI_ITHEN : ~TWL4030_BCI_TYPEN;
+       ret = twl4030_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
+                                  regval, TWL4030_BCI_BCICTL1);
+
+       return ret;
+}
+
+static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on)
+{
+       u8 regval;
+
+       regval = twl4030_madc_read(madc, TWL4030_MADC_CTRL1);
+       if (on)
+               regval |= TWL4030_MADC_MADCON;
+       else
+               regval &= ~TWL4030_MADC_MADCON;
+       twl4030_madc_write(madc, TWL4030_MADC_CTRL1, regval);
+
+       return 0;
+}
+
+static long twl4030_madc_ioctl(struct file *filp, unsigned int cmd,
+                              unsigned long arg)
+{
+       struct twl4030_madc_user_parms par;
+       int val, ret;
+
+       ret = copy_from_user(&par, (void __user *) arg, sizeof(par));
+       if (ret) {
+               dev_dbg(the_madc->dev, "copy_from_user: %d\n", ret);
+               return -EACCES;
+       }
+
+       switch (cmd) {
+       case TWL4030_MADC_IOCX_ADC_RAW_READ: {
+               struct twl4030_madc_request req;
+               if (par.channel >= TWL4030_MADC_MAX_CHANNELS)
+                       return -EINVAL;
+
+               req.channels = (1 << par.channel);
+               req.do_avg      = par.average;
+               req.method      = TWL4030_MADC_SW1;
+               req.func_cb     = NULL;
+
+               val = twl4030_madc_conversion(&req);
+               if (val <= 0) {
+                       par.status = -1;
+               } else {
+                       par.status = 0;
+                       par.result = (u16)req.rbuf[par.channel];
+               }
+               break;
+                                            }
+       default:
+               return -EINVAL;
+       }
+
+       ret = copy_to_user((void __user *) arg, &par, sizeof(par));
+       if (ret) {
+               dev_dbg(the_madc->dev, "copy_to_user: %d\n", ret);
+               return -EACCES;
+       }
+
+       return 0;
+}
+
+static struct file_operations twl4030_madc_fileops = {
+       .owner = THIS_MODULE,
+       .unlocked_ioctl = twl4030_madc_ioctl
+};
+
+static struct miscdevice twl4030_madc_device = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = "twl4030-madc",
+       .fops = &twl4030_madc_fileops
+};
+
+static int __init twl4030_madc_probe(struct platform_device *pdev)
+{
+       struct twl4030_madc_data *madc;
+       struct twl4030_madc_platform_data *pdata = pdev->dev.platform_data;
+       int ret;
+       u8 regval;
+
+       madc = kzalloc(sizeof *madc, GFP_KERNEL);
+       if (!madc)
+               return -ENOMEM;
+
+       if (!pdata) {
+               dev_dbg(&pdev->dev, "platform_data not available\n");
+               ret = -EINVAL;
+               goto err_pdata;
+       }
+
+       madc->imr = (pdata->irq_line == 1) ? TWL4030_MADC_IMR1 : TWL4030_MADC_IMR2;
+       madc->isr = (pdata->irq_line == 1) ? TWL4030_MADC_ISR1 : TWL4030_MADC_ISR2;
+
+       ret = misc_register(&twl4030_madc_device);
+       if (ret) {
+               dev_dbg(&pdev->dev, "could not register misc_device\n");
+               goto err_misc;
+       }
+       twl4030_madc_set_power(madc, 1);
+       twl4030_madc_set_current_generator(madc, 0, 1);
+
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
+                                 &regval, TWL4030_BCI_BCICTL1);
+
+       regval |= TWL4030_BCI_MESBAT;
+
+       ret = twl4030_i2c_write_u8(TWL4030_MODULE_MAIN_CHARGE,
+                                  regval, TWL4030_BCI_BCICTL1);
+
+       ret = request_irq(platform_get_irq(pdev, 0), twl4030_madc_irq_handler,
+                         0, "twl4030_madc", madc);
+       if (ret) {
+               dev_dbg(&pdev->dev, "could not request irq\n");
+               goto err_irq;
+       }
+
+       platform_set_drvdata(pdev, madc);
+       mutex_init(&madc->lock);
+       INIT_WORK(&madc->ws, twl4030_madc_work);
+
+       the_madc = madc;
+
+       return 0;
+
+err_irq:
+       misc_deregister(&twl4030_madc_device);
+
+err_misc:
+err_pdata:
+       kfree(madc);
+
+       return ret;
+}
+
+static int __exit twl4030_madc_remove(struct platform_device *pdev)
+{
+       struct twl4030_madc_data *madc = platform_get_drvdata(pdev);
+
+       twl4030_madc_set_power(madc, 0);
+       twl4030_madc_set_current_generator(madc, 0, 0);
+       free_irq(platform_get_irq(pdev, 0), madc);
+       cancel_work_sync(&madc->ws);
+       misc_deregister(&twl4030_madc_device);
+
+       return 0;
+}
+
+static struct platform_driver twl4030_madc_driver = {
+       .probe          = twl4030_madc_probe,
+       .remove         = __exit_p(twl4030_madc_remove),
+       .driver         = {
+               .name   = "twl4030_madc",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init twl4030_madc_init(void)
+{
+       return platform_driver_register(&twl4030_madc_driver);
+}
+module_init(twl4030_madc_init);
+
+static void __exit twl4030_madc_exit(void)
+{
+       platform_driver_unregister(&twl4030_madc_driver);
+}
+module_exit(twl4030_madc_exit);
+
+MODULE_ALIAS("platform:twl4030-madc");
+MODULE_AUTHOR("Nokia Corporation");
+MODULE_DESCRIPTION("twl4030 ADC driver");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/i2c/chips/twl4030-poweroff.c b/drivers/i2c/chips/twl4030-poweroff.c
new file mode 100644 (file)
index 0000000..0ebab0b
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * linux/drivers/i2c/chips/twl4030_poweroff.c
+ *
+ * Power off device
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Written by Peter De Schrijver <peter.de-schrijver@nokia.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/pm.h>
+#include <linux/i2c/twl4030.h>
+
+#define PWR_P1_SW_EVENTS       0x10
+#define PWR_DEVOFF     (1<<0)
+
+static void twl4030_poweroff(void)
+{
+       u8 val;
+       int err;
+
+       err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &val,
+                                 PWR_P1_SW_EVENTS);
+       if (err) {
+               printk(KERN_WARNING "I2C error %d while reading TWL4030"
+                                       "PM_MASTER P1_SW_EVENTS\n", err);
+               return ;
+       }
+
+       val |= PWR_DEVOFF;
+
+       err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, val,
+                                  PWR_P1_SW_EVENTS);
+
+       if (err) {
+               printk(KERN_WARNING "I2C error %d while writing TWL4030"
+                                       "PM_MASTER P1_SW_EVENTS\n", err);
+               return ;
+       }
+
+       return;
+}
+
+static int __init twl4030_poweroff_init(void)
+{
+       pm_power_off = twl4030_poweroff;
+
+       return 0;
+}
+
+static void __exit twl4030_poweroff_exit(void)
+{
+       pm_power_off = NULL;
+}
+
+module_init(twl4030_poweroff_init);
+module_exit(twl4030_poweroff_exit);
+
+MODULE_ALIAS("i2c:twl4030-poweroff");
+MODULE_DESCRIPTION("Triton2 device power off");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter De Schrijver");
index ea2638b4198226dfc7d2d69c87a387eea1df1df6..0c20376a2ed466e714f0e77a798a0a175e84e44f 100644 (file)
@@ -259,6 +259,41 @@ config KEYBOARD_OMAP
          To compile this driver as a module, choose M here: the
          module will be called omap-keypad.
 
          To compile this driver as a module, choose M here: the
          module will be called omap-keypad.
 
+config KEYBOARD_TWL4030
+       tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
+       depends on TWL4030_CORE
+       help
+         Say Y here if your board use the keypad controller on
+         TWL4030 family chips.  It's safe to say enable this
+         even on boards that don't use the keypad controller.
+
+         To compile this driver as a module, choose M here: the
+         module will be called twl4030_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.
+
+config KEYBOARD_TSC2301
+       tristate "TSC2301 keypad support"
+       depends on SPI_TSC2301
+       help
+         Say Y here for if you are using the keypad features of TSC2301.
+
+config KEYBOARD_LM8323
+       tristate "LM8323 keypad chip"
+       depends on I2C
+       depends on LEDS
+       help
+         If you say yes here you get support for the National Semiconductor
+         LM8323 keypad controller.
+
 config KEYBOARD_PXA27x
        tristate "PXA27x/PXA3xx keypad support"
        depends on PXA27x || PXA3xx
 config KEYBOARD_PXA27x
        tristate "PXA27x/PXA3xx keypad support"
        depends on PXA27x || PXA3xx
index 36351e1190f9e2db8d49da0ac1b6a941c8b547d1..9f68f208895a9ae935b53772b5904a1631fa1a2d 100644 (file)
@@ -19,6 +19,10 @@ obj-$(CONFIG_KEYBOARD_TOSA)          += tosakbd.o
 obj-$(CONFIG_KEYBOARD_HIL)             += hil_kbd.o
 obj-$(CONFIG_KEYBOARD_HIL_OLD)         += hilkbd.o
 obj-$(CONFIG_KEYBOARD_OMAP)            += omap-keypad.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
+obj-$(CONFIG_KEYBOARD_TSC2301)         += tsc2301_kp.o
+obj-$(CONFIG_KEYBOARD_LM8323)          += lm8323.o
+obj-$(CONFIG_KEYBOARD_TWL4030)         += twl4030_keypad.o
 obj-$(CONFIG_KEYBOARD_PXA27x)          += pxa27x_keypad.o
 obj-$(CONFIG_KEYBOARD_PXA930_ROTARY)   += pxa930_rotary.o
 obj-$(CONFIG_KEYBOARD_AAED2000)                += aaed2000_kbd.o
 obj-$(CONFIG_KEYBOARD_PXA27x)          += pxa27x_keypad.o
 obj-$(CONFIG_KEYBOARD_PXA930_ROTARY)   += pxa930_rotary.o
 obj-$(CONFIG_KEYBOARD_AAED2000)                += aaed2000_kbd.o
diff --git a/drivers/input/keyboard/innovator_ps2.c b/drivers/input/keyboard/innovator_ps2.c
new file mode 100644 (file)
index 0000000..5f88839
--- /dev/null
@@ -0,0 +1,1279 @@
+/*
+ * 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.
+ *         <gdavis@mvista.com> or <source@mvista.com>
+ *
+ *
+ * 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 <gdavis@mvista.com>
+ *      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 <aam@ridgerun.com>
+ *
+ * 20040812: Thiago Radicchi <trr@dcc.ufmg.br>
+ *      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 <linux/version.h>
+#include <linux/stddef.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/ptrace.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/poll.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/platform_device.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/arch/fpga.h>
+
+#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 void do_hid_tasklet(unsigned long);
+DECLARE_TASKLET(hid_tasklet, do_hid_tasklet, 0);
+static struct innovator_hid_dev *hid;
+
+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
+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;
+}
+
+/*
+ * 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;
+       hid->keyboard = input_allocate_device();
+       hid->mouse = input_allocate_device();
+
+       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;
+       hid->keyboard = input_allocate_device();
+       hid->mouse = input_allocate_device();
+
+       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,
+                       IRQF_DISABLED, 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 = 0;
+
+       /*
+        * 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 = 0;
+
+       /*
+        * 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 = input_allocate_device();
+       hid->mouse->evbit[0] = BIT(EV_KEY) | BIT(EV_REL);
+       hid->mouse->keybit[BIT_WORD(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 = input_allocate_device();
+       hid->keyboard->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+       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 <gdavis@mvista.com>");
+MODULE_DESCRIPTION("Innovator PS/2 Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/lm8323.c b/drivers/input/keyboard/lm8323.c
new file mode 100644 (file)
index 0000000..a95ea03
--- /dev/null
@@ -0,0 +1,928 @@
+/*
+ * drivers/i2c/chips/lm8323.c
+ *
+ * Copyright (C) 2007-2009 Nokia Corporation
+ *
+ * Written by Daniel Stone <daniel.stone@nokia.com>
+ *            Timo O. Karjalainen <timo.o.karjalainen@nokia.com>
+ *
+ * Updated by Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License only).
+ *
+ * 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 <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/sched.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+#include <linux/leds.h>
+#include <linux/i2c/lm8323.h>
+
+/* Commands to send to the chip. */
+#define LM8323_CMD_READ_ID             0x80 /* Read chip ID. */
+#define LM8323_CMD_WRITE_CFG           0x81 /* Set configuration item. */
+#define LM8323_CMD_READ_INT            0x82 /* Get interrupt status. */
+#define LM8323_CMD_RESET               0x83 /* Reset, same as external one */
+#define LM8323_CMD_WRITE_PORT_SEL      0x85 /* Set GPIO in/out. */
+#define LM8323_CMD_WRITE_PORT_STATE    0x86 /* Set GPIO pullup. */
+#define LM8323_CMD_READ_PORT_SEL       0x87 /* Get GPIO in/out. */
+#define LM8323_CMD_READ_PORT_STATE     0x88 /* Get GPIO pullup. */
+#define LM8323_CMD_READ_FIFO           0x89 /* Read byte from FIFO. */
+#define LM8323_CMD_RPT_READ_FIFO       0x8a /* Read FIFO (no increment). */
+#define LM8323_CMD_SET_ACTIVE          0x8b /* Set active time. */
+#define LM8323_CMD_READ_ERR            0x8c /* Get error status. */
+#define LM8323_CMD_READ_ROTATOR                0x8e /* Read rotator status. */
+#define LM8323_CMD_SET_DEBOUNCE                0x8f /* Set debouncing time. */
+#define LM8323_CMD_SET_KEY_SIZE                0x90 /* Set keypad size. */
+#define LM8323_CMD_READ_KEY_SIZE       0x91 /* Get keypad size. */
+#define LM8323_CMD_READ_CFG            0x92 /* Get configuration item. */
+#define LM8323_CMD_WRITE_CLOCK         0x93 /* Set clock config. */
+#define LM8323_CMD_READ_CLOCK          0x94 /* Get clock config. */
+#define LM8323_CMD_PWM_WRITE           0x95 /* Write PWM script. */
+#define LM8323_CMD_START_PWM           0x96 /* Start PWM engine. */
+#define LM8323_CMD_STOP_PWM            0x97 /* Stop PWM engine. */
+
+/* Interrupt status. */
+#define INT_KEYPAD                     0x01 /* Key event. */
+#define INT_ROTATOR                    0x02 /* Rotator event. */
+#define INT_ERROR                      0x08 /* Error: use CMD_READ_ERR. */
+#define INT_NOINIT                     0x10 /* Lost configuration. */
+#define INT_PWM1                       0x20 /* PWM1 stopped. */
+#define INT_PWM2                       0x40 /* PWM2 stopped. */
+#define INT_PWM3                       0x80 /* PWM3 stopped. */
+
+/* Errors (signalled by INT_ERROR, read with CMD_READ_ERR). */
+#define ERR_BADPAR                     0x01 /* Bad parameter. */
+#define ERR_CMDUNK                     0x02 /* Unknown command. */
+#define ERR_KEYOVR                     0x04 /* Too many keys pressed. */
+#define ERR_FIFOOVER                   0x40 /* FIFO overflow. */
+
+/* Configuration keys (CMD_{WRITE,READ}_CFG). */
+#define CFG_MUX1SEL                    0x01 /* Select MUX1_OUT input. */
+#define CFG_MUX1EN                     0x02 /* Enable MUX1_OUT. */
+#define CFG_MUX2SEL                    0x04 /* Select MUX2_OUT input. */
+#define CFG_MUX2EN                     0x08 /* Enable MUX2_OUT. */
+#define CFG_PSIZE                      0x20 /* Package size (must be 0). */
+#define CFG_ROTEN                      0x40 /* Enable rotator. */
+
+/* Clock settings (CMD_{WRITE,READ}_CLOCK). */
+#define CLK_RCPWM_INTERNAL             0x00
+#define CLK_RCPWM_EXTERNAL             0x03
+#define CLK_SLOWCLKEN                  0x08 /* Enable 32.768kHz clock. */
+#define CLK_SLOWCLKOUT                 0x40 /* Enable slow pulse output. */
+
+/* The possible addresses corresponding to CONFIG1 and CONFIG2 pin wirings. */
+#define LM8323_I2C_ADDR00              (0x84 >> 1)     /* 1000 010x */
+#define LM8323_I2C_ADDR01              (0x86 >> 1)     /* 1000 011x */
+#define LM8323_I2C_ADDR10              (0x88 >> 1)     /* 1000 100x */
+#define LM8323_I2C_ADDR11              (0x8A >> 1)     /* 1000 101x */
+
+/* Key event fifo length */
+#define LM8323_FIFO_LEN                        15
+
+/* Commands for PWM engine; feed in with PWM_WRITE. */
+/* Load ramp counter from duty cycle field (range 0 - 0xff). */
+#define PWM_SET(v)                     (0x4000 | ((v) & 0xff))
+/* Go to start of script. */
+#define PWM_GOTOSTART                  0x0000
+/*
+ * Stop engine (generates interrupt).  If reset is 1, clear the program
+ * counter, else leave it.
+ */
+#define PWM_END(reset)                 (0xc000 | (!!(reset) << 11))
+/*
+ * Ramp.  If s is 1, divide clock by 512, else divide clock by 16.
+ * Take t clock scales (up to 63) per step, for n steps (up to 126).
+ * If u is set, ramp up, else ramp down.
+ */
+#define PWM_RAMP(s, t, n, u)           ((!!(s) << 14) | ((t) & 0x3f) << 8 | \
+                                        ((n) & 0x7f) | ((u) ? 0 : 0x80))
+/*
+ * Loop (i.e. jump back to pos) for a given number of iterations (up to 63).
+ * If cnt is zero, execute until PWM_END is encountered.
+ */
+#define PWM_LOOP(cnt, pos)             (0xa000 | (((cnt) & 0x3f) << 7) | \
+                                        ((pos) & 0x3f))
+/*
+ * Wait for trigger.  Argument is a mask of channels, shifted by the channel
+ * number, e.g. 0xa for channels 3 and 1.  Note that channels are numbered
+ * from 1, not 0.
+ */
+#define PWM_WAIT_TRIG(chans)           (0xe000 | (((chans) & 0x7) << 6))
+/* Send trigger.  Argument is same as PWM_WAIT_TRIG. */
+#define PWM_SEND_TRIG(chans)           (0xe000 | ((chans) & 0x7))
+
+struct lm8323_pwm {
+       int                     id;
+       int                     enabled;
+       int                     fade_time;
+       int                     brightness;
+       int                     desired_brightness;
+       int                     running;
+       /* pwm lock */
+       struct mutex            lock;
+       struct work_struct      work;
+       struct led_classdev     cdev;
+};
+
+struct lm8323_chip {
+       /* device lock */
+       struct mutex            lock;
+       struct i2c_client       *client;
+       struct work_struct      work;
+       struct input_dev        *idev;
+       unsigned                kp_enabled:1;
+       unsigned                pm_suspend:1;
+       unsigned                keys_down;
+       char                    phys[32];
+       s16                     keymap[LM8323_KEYMAP_SIZE];
+       int                     size_x;
+       int                     size_y;
+       int                     debounce_time;
+       int                     active_time;
+       struct lm8323_pwm       pwm1;
+       struct lm8323_pwm       pwm2;
+       struct lm8323_pwm       pwm3;
+};
+
+#define client_to_lm8323(c)    container_of(c, struct lm8323_chip, client)
+#define dev_to_lm8323(d)       container_of(d, struct lm8323_chip, client->dev)
+#define work_to_lm8323(w)      container_of(w, struct lm8323_chip, work)
+#define cdev_to_pwm(c)         container_of(c, struct lm8323_pwm, cdev)
+#define work_to_pwm(w)         container_of(w, struct lm8323_pwm, work)
+
+static struct lm8323_chip *pwm_to_lm8323(struct lm8323_pwm *pwm)
+{
+       switch (pwm->id) {
+       case 1:
+               return container_of(pwm, struct lm8323_chip, pwm1);
+       case 2:
+               return container_of(pwm, struct lm8323_chip, pwm2);
+       case 3:
+               return container_of(pwm, struct lm8323_chip, pwm3);
+       default:
+               return NULL;
+       }
+}
+
+#define LM8323_MAX_DATA 8
+
+/*
+ * To write, we just access the chip's address in write mode, and dump the
+ * command and data out on the bus.  The command byte and data are taken as
+ * sequential u8s out of varargs, to a maximum of LM8323_MAX_DATA.
+ */
+static int lm8323_write(struct lm8323_chip *lm, int len, ...)
+{
+       int ret, i;
+       va_list ap;
+       u8 data[LM8323_MAX_DATA];
+
+       va_start(ap, len);
+
+       if (unlikely(len > LM8323_MAX_DATA)) {
+               dev_err(&lm->client->dev, "tried to send %d bytes\n", len);
+               va_end(ap);
+               return 0;
+       }
+
+       for (i = 0; i < len; i++)
+               data[i] = va_arg(ap, int);
+
+       va_end(ap);
+
+       /*
+        * If the host is asleep while we send the data, we can get a NACK
+        * back while it wakes up, so try again, once.
+        */
+       ret = i2c_master_send(lm->client, data, len);
+       if (unlikely(ret == -EREMOTEIO))
+               ret = i2c_master_send(lm->client, data, len);
+       if (unlikely(ret != len))
+               dev_err(&lm->client->dev, "sent %d bytes of %d total\n",
+                       len, ret);
+
+       return ret;
+}
+
+/*
+ * To read, we first send the command byte to the chip and end the transaction,
+ * then access the chip in read mode, at which point it will send the data.
+ */
+static int lm8323_read(struct lm8323_chip *lm, u8 cmd, u8 *buf, int len)
+{
+       int ret;
+
+       /*
+        * If the host is asleep while we send the byte, we can get a NACK
+        * back while it wakes up, so try again, once.
+        */
+       ret = i2c_master_send(lm->client, &cmd, 1);
+       if (unlikely(ret == -EREMOTEIO))
+               ret = i2c_master_send(lm->client, &cmd, 1);
+       if (unlikely(ret != 1)) {
+               dev_err(&lm->client->dev, "sending read cmd 0x%02x failed\n",
+                       cmd);
+               return 0;
+       }
+
+       ret = i2c_master_recv(lm->client, buf, len);
+       if (unlikely(ret != len))
+               dev_err(&lm->client->dev, "wanted %d bytes, got %d\n",
+                       len, ret);
+
+       return ret;
+}
+
+/*
+ * Set the chip active time (idle time before it enters halt).
+ */
+static void lm8323_set_active_time(struct lm8323_chip *lm, int time)
+{
+       lm8323_write(lm, 2, LM8323_CMD_SET_ACTIVE, time >> 2);
+}
+
+/*
+ * The signals are AT-style: the low 7 bits are the keycode, and the top
+ * bit indicates the state (1 for down, 0 for up).
+ */
+static inline u8 lm8323_whichkey(u8 event)
+{
+       return event & 0x7f;
+}
+
+static inline int lm8323_ispress(u8 event)
+{
+       return (event & 0x80) ? 1 : 0;
+}
+
+static void process_keys(struct lm8323_chip *lm)
+{
+       u8 event;
+       u8 key_fifo[LM8323_FIFO_LEN + 1];
+       int old_keys_down = lm->keys_down;
+       int ret;
+       int i = 0;
+
+       /*
+        * Read all key events from the FIFO at once. Next READ_FIFO clears the
+        * FIFO even if we didn't read all events previously.
+        */
+       ret = lm8323_read(lm, LM8323_CMD_READ_FIFO, key_fifo, LM8323_FIFO_LEN);
+
+       if (ret < 0) {
+               dev_err(&lm->client->dev, "Failed reading fifo \n");
+               return;
+       }
+       key_fifo[ret] = 0;
+
+       while ((event = key_fifo[i])) {
+               u8 key = lm8323_whichkey(event);
+               int isdown = lm8323_ispress(event);
+               s16 keycode = lm->keymap[key];
+
+               if (likely(keycode > 0)) {
+                       dev_vdbg(&lm->client->dev, "key 0x%02x %s\n", key,
+                             isdown ? "down" : "up");
+                       if (likely(lm->kp_enabled)) {
+                               input_report_key(lm->idev, keycode, isdown);
+                               input_sync(lm->idev);
+                       }
+                       if (isdown)
+                               lm->keys_down++;
+                       else
+                               lm->keys_down--;
+               } else {
+                       dev_err(&lm->client->dev, "keycode 0x%02x not mapped "
+                               "to any key\n", key);
+               }
+               i++;
+       }
+
+       /*
+        * Errata: We need to ensure that the chip never enters halt mode
+        * during a keypress, so set active time to 0.  When it's released,
+        * we can enter halt again, so set the active time back to normal.
+        */
+       if (!old_keys_down && lm->keys_down)
+               lm8323_set_active_time(lm, 0);
+       if (old_keys_down && !lm->keys_down)
+               lm8323_set_active_time(lm, lm->active_time);
+}
+
+static void lm8323_process_error(struct lm8323_chip *lm)
+{
+       u8 error;
+
+       if (lm8323_read(lm, LM8323_CMD_READ_ERR, &error, 1) == 1) {
+               if (error & ERR_FIFOOVER)
+                       dev_vdbg(&lm->client->dev, "fifo overflow!\n");
+               if (error & ERR_KEYOVR)
+                       dev_vdbg(&lm->client->dev,
+                                       "more than two keys pressed\n");
+               if (error & ERR_CMDUNK)
+                       dev_vdbg(&lm->client->dev,
+                                       "unknown command submitted\n");
+               if (error & ERR_BADPAR)
+                       dev_vdbg(&lm->client->dev, "bad command parameter\n");
+       }
+}
+
+static void lm8323_reset(struct lm8323_chip *lm)
+{
+       /* The docs say we must pass 0xAA as the data byte. */
+       lm8323_write(lm, 2, LM8323_CMD_RESET, 0xAA);
+}
+
+static int lm8323_configure(struct lm8323_chip *lm)
+{
+       int keysize = (lm->size_x << 4) | lm->size_y;
+       int clock = (CLK_SLOWCLKEN | CLK_RCPWM_EXTERNAL);
+       int debounce = lm->debounce_time >> 2;
+       int active = lm->active_time >> 2;
+
+       /*
+        * Active time must be greater than the debounce time: if it's
+        * a close-run thing, give ourselves a 12ms buffer.
+        */
+       if (debounce >= active)
+               active = debounce + 3;
+
+       lm8323_write(lm, 2, LM8323_CMD_WRITE_CFG, 0);
+       lm8323_write(lm, 2, LM8323_CMD_WRITE_CLOCK, clock);
+       lm8323_write(lm, 2, LM8323_CMD_SET_KEY_SIZE, keysize);
+       lm8323_set_active_time(lm, lm->active_time);
+       lm8323_write(lm, 2, LM8323_CMD_SET_DEBOUNCE, debounce);
+       lm8323_write(lm, 3, LM8323_CMD_WRITE_PORT_STATE, 0xff, 0xff);
+       lm8323_write(lm, 3, LM8323_CMD_WRITE_PORT_SEL, 0, 0);
+
+       /*
+        * Not much we can do about errors at this point, so just hope
+        * for the best.
+        */
+
+       return 0;
+}
+
+static void pwm_done(struct lm8323_pwm *pwm)
+{
+       mutex_lock(&pwm->lock);
+       pwm->running = 0;
+       if (pwm->desired_brightness != pwm->brightness)
+               schedule_work(&pwm->work);
+       mutex_unlock(&pwm->lock);
+}
+
+/*
+ * Bottom half: handle the interrupt by posting key events, or dealing with
+ * errors appropriately.
+ */
+static void lm8323_work(struct work_struct *work)
+{
+       struct lm8323_chip *lm = work_to_lm8323(work);
+       u8 ints;
+
+       mutex_lock(&lm->lock);
+
+       while ((lm8323_read(lm, LM8323_CMD_READ_INT, &ints, 1) == 1) && ints) {
+               if (likely(ints & INT_KEYPAD))
+                       process_keys(lm);
+               if (ints & INT_ROTATOR) {
+                       /* We don't currently support the rotator. */
+                       dev_vdbg(&lm->client->dev, "rotator fired\n");
+               }
+               if (ints & INT_ERROR) {
+                       dev_vdbg(&lm->client->dev, "error!\n");
+                       lm8323_process_error(lm);
+               }
+               if (ints & INT_NOINIT) {
+                       dev_err(&lm->client->dev, "chip lost config; "
+                                                 "reinitialising\n");
+                       lm8323_configure(lm);
+               }
+               if (ints & INT_PWM1) {
+                       dev_vdbg(&lm->client->dev, "pwm1 engine completed\n");
+                       pwm_done(&lm->pwm1);
+               }
+               if (ints & INT_PWM2) {
+                       dev_vdbg(&lm->client->dev, "pwm2 engine completed\n");
+                       pwm_done(&lm->pwm2);
+               }
+               if (ints & INT_PWM3) {
+                       dev_vdbg(&lm->client->dev, "pwm3 engine completed\n");
+                       pwm_done(&lm->pwm3);
+               }
+       }
+
+       mutex_unlock(&lm->lock);
+}
+
+/*
+ * We cannot use I2C in interrupt context, so we just schedule work.
+ */
+static irqreturn_t lm8323_irq(int irq, void *data)
+{
+       struct lm8323_chip *lm = data;
+
+       schedule_work(&lm->work);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Read the chip ID.
+ */
+static int lm8323_read_id(struct lm8323_chip *lm, u8 *buf)
+{
+       int bytes;
+
+       bytes = lm8323_read(lm, LM8323_CMD_READ_ID, buf, 2);
+       if (unlikely(bytes != 2))
+               return -EIO;
+
+       return 0;
+}
+
+static void lm8323_write_pwm_one(struct lm8323_pwm *pwm, int pos, u16 cmd)
+{
+       struct lm8323_chip *lm = pwm_to_lm8323(pwm);
+
+       lm8323_write(lm, 4, LM8323_CMD_PWM_WRITE, (pos << 2) | pwm->id,
+                    (cmd & 0xff00) >> 8, cmd & 0x00ff);
+}
+
+/*
+ * Write a script into a given PWM engine, concluding with PWM_END.
+ * If 'kill' is nonzero, the engine will be shut down at the end
+ * of the script, producing a zero output. Otherwise the engine
+ * will be kept running at the final PWM level indefinitely.
+ */
+static void lm8323_write_pwm(struct lm8323_pwm *pwm, int kill,
+                            int len, const u16 *cmds)
+{
+       struct lm8323_chip *lm = pwm_to_lm8323(pwm);
+       int i;
+
+       for (i = 0; i < len; i++)
+               lm8323_write_pwm_one(pwm, i, cmds[i]);
+
+       lm8323_write_pwm_one(pwm, i++, PWM_END(kill));
+       lm8323_write(lm, 2, LM8323_CMD_START_PWM, pwm->id);
+       pwm->running = 1;
+}
+
+static void lm8323_pwm_work(struct work_struct *work)
+{
+       struct lm8323_pwm *pwm = work_to_pwm(work);
+       int div512, perstep, steps, hz, up, kill;
+       u16 pwm_cmds[3];
+       int num_cmds = 0;
+
+       mutex_lock(&pwm->lock);
+
+       /*
+        * Do nothing if we're already at the requested level,
+        * or previous setting is not yet complete. In the latter
+        * case we will be called again when the previous PWM script
+        * finishes.
+        */
+       if (pwm->running || pwm->desired_brightness == pwm->brightness) {
+               mutex_unlock(&pwm->lock);
+               return;
+       }
+
+       kill = (pwm->desired_brightness == 0);
+       up = (pwm->desired_brightness > pwm->brightness);
+       steps = abs(pwm->desired_brightness - pwm->brightness);
+
+       /*
+        * Convert time (in ms) into a divisor (512 or 16 on a refclk of
+        * 32768Hz), and number of ticks per step.
+        */
+       if ((pwm->fade_time / steps) > (32768 / 512)) {
+               div512 = 1;
+               hz = 32768 / 512;
+       } else {
+               div512 = 0;
+               hz = 32768 / 16;
+       }
+
+       perstep = (hz * pwm->fade_time) / (steps * 1000);
+
+       if (perstep == 0)
+               perstep = 1;
+       else if (perstep > 63)
+               perstep = 63;
+
+       while (steps) {
+               int s;
+
+               s = min(126, steps);
+               pwm_cmds[num_cmds++] = PWM_RAMP(div512, perstep, s, up);
+               steps -= s;
+       }
+
+       lm8323_write_pwm(pwm, kill, num_cmds, pwm_cmds);
+
+       pwm->brightness = pwm->desired_brightness;
+       mutex_unlock(&pwm->lock);
+}
+
+static void lm8323_pwm_set_brightness(struct led_classdev *led_cdev,
+                                     enum led_brightness brightness)
+{
+       struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
+       struct lm8323_chip *lm = pwm_to_lm8323(pwm);
+
+       mutex_lock(&pwm->lock);
+       pwm->desired_brightness = brightness;
+       mutex_unlock(&pwm->lock);
+
+       if (in_interrupt()) {
+               schedule_work(&pwm->work);
+       } else {
+               /*
+                * Schedule PWM work as usual unless we are going into suspend
+                */
+               mutex_lock(&lm->lock);
+               if (likely(!lm->pm_suspend))
+                       schedule_work(&pwm->work);
+               else
+                       lm8323_pwm_work(&pwm->work);
+               mutex_unlock(&lm->lock);
+       }
+}
+
+static ssize_t lm8323_pwm_show_time(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
+
+       return sprintf(buf, "%d\n", pwm->fade_time);
+}
+
+static ssize_t lm8323_pwm_store_time(struct device *dev,
+               struct device_attribute *attr, const char *buf, size_t len)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct lm8323_pwm *pwm = cdev_to_pwm(led_cdev);
+       int ret;
+       int time;
+
+       ret = strict_strtoul(buf, 10, &time);
+       /* Numbers only, please. */
+       if (ret)
+               return -EINVAL;
+
+       pwm->fade_time = time;
+
+       return strlen(buf);
+}
+static DEVICE_ATTR(time, 0644, lm8323_pwm_show_time, lm8323_pwm_store_time);
+
+static int init_pwm(struct lm8323_chip *lm, int id, struct device *dev,
+                   const char *name)
+{
+       struct lm8323_pwm *pwm = NULL;
+
+       BUG_ON(id > 3);
+
+       switch (id) {
+       case 1:
+               pwm = &lm->pwm1;
+               break;
+       case 2:
+               pwm = &lm->pwm2;
+               break;
+       case 3:
+               pwm = &lm->pwm3;
+               break;
+       }
+
+       pwm->id = id;
+       pwm->fade_time = 0;
+       pwm->brightness = 0;
+       pwm->desired_brightness = 0;
+       pwm->running = 0;
+       mutex_init(&pwm->lock);
+       if (name) {
+               pwm->cdev.name = name;
+               pwm->cdev.brightness_set = lm8323_pwm_set_brightness;
+               if (led_classdev_register(dev, &pwm->cdev) < 0) {
+                       dev_err(dev, "couldn't register PWM %d\n", id);
+                       return -1;
+               }
+               if (device_create_file(pwm->cdev.dev,
+                                            &dev_attr_time) < 0) {
+                       dev_err(dev, "couldn't register time attribute\n");
+                       led_classdev_unregister(&pwm->cdev);
+                       return -1;
+               }
+               INIT_WORK(&pwm->work, lm8323_pwm_work);
+               pwm->enabled = 1;
+       } else {
+               pwm->enabled = 0;
+       }
+
+       return 0;
+}
+
+static struct i2c_driver lm8323_i2c_driver;
+
+static ssize_t lm8323_show_disable(struct device *dev,
+                                  struct device_attribute *attr, char *buf)
+{
+       struct lm8323_chip *lm = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", !lm->kp_enabled);
+}
+
+static ssize_t lm8323_set_disable(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+       struct lm8323_chip *lm = dev_get_drvdata(dev);
+       int ret;
+       int i;
+
+       ret = strict_strtoul(buf, 10, &i);
+
+       mutex_lock(&lm->lock);
+       lm->kp_enabled = !i;
+       mutex_unlock(&lm->lock);
+
+       return count;
+}
+static DEVICE_ATTR(disable_kp, 0644, lm8323_show_disable, lm8323_set_disable);
+
+static int lm8323_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct lm8323_platform_data *pdata;
+       struct input_dev *idev;
+       struct lm8323_chip *lm;
+       int i, err = 0;
+       unsigned long tmo;
+       u8 data[2];
+
+       lm = kzalloc(sizeof *lm, GFP_KERNEL);
+       if (!lm)
+               return -ENOMEM;
+
+       i2c_set_clientdata(client, lm);
+       lm->client = client;
+       pdata = client->dev.platform_data;
+       if (!pdata || !pdata->size_x || !pdata->size_y) {
+               dev_err(&client->dev, "missing platform_data\n");
+               err = -EINVAL;
+               goto fail2;
+       }
+
+       lm->size_x = pdata->size_x;
+       if (lm->size_x > 8) {
+               dev_err(&client->dev, "invalid x size %d specified\n",
+                               lm->size_x);
+               err = -EINVAL;
+               goto fail2;
+       }
+
+       lm->size_y = pdata->size_y;
+       if (lm->size_y > 12) {
+               dev_err(&client->dev, "invalid y size %d specified\n",
+                               lm->size_y);
+               err = -EINVAL;
+               goto fail2;
+       }
+
+       dev_vdbg(&client->dev, "Keypad size: %d x %d\n",
+                       lm->size_x, lm->size_y);
+
+       lm->debounce_time = pdata->debounce_time;
+       lm->active_time = pdata->active_time;
+
+       lm8323_reset(lm);
+
+       /* Nothing's set up to service the IRQ yet, so just spin for max.
+        * 100ms until we can configure. */
+       tmo = jiffies + msecs_to_jiffies(100);
+       while (lm8323_read(lm, LM8323_CMD_READ_INT, data, 1) == 1) {
+               if (data[0] & INT_NOINIT)
+                       break;
+
+               if (time_after(jiffies, tmo)) {
+                       dev_err(&client->dev,
+                                       "timeout waiting for initialisation\n");
+                       break;
+               }
+
+               msleep(1);
+       }
+       lm8323_configure(lm);
+
+       /* If a true probe check the device */
+       if (lm8323_read_id(lm, data) != 0) {
+               dev_err(&client->dev, "device not found\n");
+               err = -ENODEV;
+               goto fail2;
+       }
+
+       if (init_pwm(lm, 1, &client->dev, pdata->pwm1_name) < 0)
+               goto fail3;
+       if (init_pwm(lm, 2, &client->dev, pdata->pwm2_name) < 0)
+               goto fail4;
+       if (init_pwm(lm, 3, &client->dev, pdata->pwm3_name) < 0)
+               goto fail5;
+
+       mutex_init(&lm->lock);
+       INIT_WORK(&lm->work, lm8323_work);
+
+       err = request_irq(client->irq, lm8323_irq,
+                         IRQF_TRIGGER_FALLING | IRQF_DISABLED,
+                         "lm8323", lm);
+       if (err) {
+               dev_err(&client->dev, "could not get IRQ %d\n", client->irq);
+               goto fail6;
+       }
+
+       device_init_wakeup(&client->dev, 1);
+       enable_irq_wake(client->irq);
+
+       lm->kp_enabled = 1;
+       err = device_create_file(&client->dev, &dev_attr_disable_kp);
+       if (err < 0)
+               goto fail7;
+
+       idev = input_allocate_device();
+       if (!idev) {
+               err = -ENOMEM;
+               goto fail8;
+       }
+
+       if (pdata->name)
+               idev->name = pdata->name;
+       else
+               idev->name = "LM8323 keypad";
+       snprintf(lm->phys, sizeof(lm->phys), "%s/input-kp", dev_name(&client->dev));
+       idev->phys = lm->phys;
+
+       lm->keys_down = 0;
+       idev->evbit[0] = BIT(EV_KEY);
+       for (i = 0; i < LM8323_KEYMAP_SIZE; i++) {
+               if (pdata->keymap[i] > 0)
+                       __set_bit(pdata->keymap[i], idev->keybit);
+
+               lm->keymap[i] = pdata->keymap[i];
+       }
+
+       if (pdata->repeat)
+               __set_bit(EV_REP, idev->evbit);
+
+       lm->idev = idev;
+       err = input_register_device(idev);
+       if (err) {
+               dev_dbg(&client->dev, "error registering input device\n");
+               goto fail8;
+       }
+
+       return 0;
+
+fail8:
+       device_remove_file(&client->dev, &dev_attr_disable_kp);
+fail7:
+       free_irq(client->irq, lm);
+fail6:
+       if (lm->pwm3.enabled)
+               led_classdev_unregister(&lm->pwm3.cdev);
+fail5:
+       if (lm->pwm2.enabled)
+               led_classdev_unregister(&lm->pwm2.cdev);
+fail4:
+       if (lm->pwm1.enabled)
+               led_classdev_unregister(&lm->pwm1.cdev);
+fail3:
+fail2:
+       kfree(lm);
+       return err;
+}
+
+static int lm8323_remove(struct i2c_client *client)
+{
+       struct lm8323_chip *lm = i2c_get_clientdata(client);
+
+       disable_irq_wake(client->irq);
+       free_irq(client->irq, lm);
+       cancel_work_sync(&lm->work);
+       input_unregister_device(lm->idev);
+       device_remove_file(&lm->client->dev, &dev_attr_disable_kp);
+       if (lm->pwm3.enabled)
+               led_classdev_unregister(&lm->pwm3.cdev);
+       if (lm->pwm2.enabled)
+               led_classdev_unregister(&lm->pwm2.cdev);
+       if (lm->pwm1.enabled)
+               led_classdev_unregister(&lm->pwm1.cdev);
+       kfree(lm);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+/*
+ * We don't need to explicitly suspend the chip, as it already switches off
+ * when there's no activity.
+ */
+static int lm8323_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+       struct lm8323_chip *lm = i2c_get_clientdata(client);
+
+       set_irq_wake(client->irq, 0);
+       disable_irq(client->irq);
+
+       mutex_lock(&lm->lock);
+       lm->pm_suspend = 1;
+       mutex_unlock(&lm->lock);
+
+       if (lm->pwm1.enabled)
+               led_classdev_suspend(&lm->pwm1.cdev);
+       if (lm->pwm2.enabled)
+               led_classdev_suspend(&lm->pwm2.cdev);
+       if (lm->pwm3.enabled)
+               led_classdev_suspend(&lm->pwm3.cdev);
+
+       return 0;
+}
+
+static int lm8323_resume(struct i2c_client *client)
+{
+       struct lm8323_chip *lm = i2c_get_clientdata(client);
+
+       mutex_lock(&lm->lock);
+       lm->pm_suspend = 0;
+       mutex_unlock(&lm->lock);
+
+       if (lm->pwm1.enabled)
+               led_classdev_resume(&lm->pwm1.cdev);
+       if (lm->pwm2.enabled)
+               led_classdev_resume(&lm->pwm2.cdev);
+       if (lm->pwm3.enabled)
+               led_classdev_resume(&lm->pwm3.cdev);
+
+       enable_irq(client->irq);
+       set_irq_wake(client->irq, 1);
+
+       return 0;
+}
+#else
+#define lm8323_suspend NULL
+#define lm8323_resume  NULL
+#endif
+
+static const struct i2c_device_id lm8323_id[] = {
+       { "lm8323", 0 },
+       { }
+};
+
+static struct i2c_driver lm8323_i2c_driver = {
+       .driver = {
+               .name    = "lm8323",
+       },
+       .probe          = lm8323_probe,
+       .remove         = lm8323_remove,
+       .suspend        = lm8323_suspend,
+       .resume         = lm8323_resume,
+       .id_table       = lm8323_id,
+};
+MODULE_DEVICE_TABLE(i2c, lm8323_id);
+
+static int __init lm8323_init(void)
+{
+       return i2c_add_driver(&lm8323_i2c_driver);
+}
+module_init(lm8323_init);
+
+static void __exit lm8323_exit(void)
+{
+       i2c_del_driver(&lm8323_i2c_driver);
+}
+module_exit(lm8323_exit);
+
+MODULE_AUTHOR("Timo O. Karjalainen <timo.o.karjalainen@nokia.com>");
+MODULE_AUTHOR("Daniel Stone");
+MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");
+MODULE_DESCRIPTION("LM8323 keypad driver");
+MODULE_LICENSE("GPL");
+
index 058fa8b02c21ee1b939085c024c23884e2f6ca1d..98da278a1cc46ee1f5c8413d7b7b936ada3304bb 100644 (file)
 #include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
 #include <linux/delay.h>
 #include <linux/platform_device.h>
 #include <linux/mutex.h>
+#include <linux/spinlock.h>
 #include <linux/errno.h>
 #include <linux/errno.h>
+#include <linux/i2c/menelaus.h>
 #include <mach/gpio.h>
 #include <mach/keypad.h>
 #include <mach/gpio.h>
 #include <mach/keypad.h>
-#include <mach/menelaus.h>
 #include <asm/irq.h>
 #include <mach/hardware.h>
 #include <asm/io.h>
 #include <asm/irq.h>
 #include <mach/hardware.h>
 #include <asm/io.h>
+#include <asm/mach-types.h>
 #include <mach/mux.h>
 
 #undef NEW_BOARD_LEARNING_MODE
 #include <mach/mux.h>
 
 #undef NEW_BOARD_LEARNING_MODE
@@ -60,6 +62,8 @@ struct omap_kp {
        unsigned int cols;
        unsigned long delay;
        unsigned int debounce;
        unsigned int cols;
        unsigned long delay;
        unsigned int debounce;
+       int suspended;
+       spinlock_t suspend_lock;
 };
 
 static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
 };
 
 static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
@@ -96,6 +100,14 @@ static u8 get_row_gpio_val(struct omap_kp *omap_kp)
 static irqreturn_t omap_kp_interrupt(int irq, void *dev_id)
 {
        struct omap_kp *omap_kp = dev_id;
 static irqreturn_t omap_kp_interrupt(int irq, void *dev_id)
 {
        struct omap_kp *omap_kp = dev_id;
+       unsigned long flags;
+
+       spin_lock_irqsave(&omap_kp->suspend_lock, flags);
+       if (omap_kp->suspended) {
+               spin_unlock_irqrestore(&omap_kp->suspend_lock, flags);
+               return IRQ_HANDLED;
+       }
+       spin_unlock_irqrestore(&omap_kp->suspend_lock, flags);
 
        /* disable keyboard interrupt and schedule for handling */
        if (cpu_is_omap24xx()) {
 
        /* disable keyboard interrupt and schedule for handling */
        if (cpu_is_omap24xx()) {
@@ -263,15 +275,29 @@ static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, omap_kp_enable_show, omap_kp_enabl
 #ifdef CONFIG_PM
 static int omap_kp_suspend(struct platform_device *dev, pm_message_t state)
 {
 #ifdef CONFIG_PM
 static int omap_kp_suspend(struct platform_device *dev, pm_message_t state)
 {
-       /* Nothing yet */
+       struct omap_kp *omap_kp = platform_get_drvdata(dev);
+       unsigned long flags;
+       spin_lock_irqsave(&omap_kp->suspend_lock, flags);
+
+       /*
+        * Re-enable the interrupt in case it has been masked by the
+        * handler and a key is still pressed.  We need the interrupt
+        * to wake us up from suspended.
+        */
+       if (cpu_class_is_omap1())
+               omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+       omap_kp->suspended = 1;
 
 
+       spin_unlock_irqrestore(&omap_kp->suspend_lock, flags);
        return 0;
 }
 
 static int omap_kp_resume(struct platform_device *dev)
 {
        return 0;
 }
 
 static int omap_kp_resume(struct platform_device *dev)
 {
-       /* Nothing yet */
+       struct omap_kp *omap_kp = platform_get_drvdata(dev);
 
 
+       omap_kp->suspended = 0;
        return 0;
 }
 #else
        return 0;
 }
 #else
@@ -284,7 +310,7 @@ static int __devinit 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;
        struct omap_kp *omap_kp;
        struct input_dev *input_dev;
        struct omap_kp_platform_data *pdata =  pdev->dev.platform_data;
-       int i, col_idx, row_idx, irq_idx, ret;
+       int i, col_idx = 0, row_idx = 0, irq_idx, ret;
 
        if (!pdata->rows || !pdata->cols || !pdata->keymap) {
                printk(KERN_ERR "No rows, cols or keymap from pdata\n");
 
        if (!pdata->rows || !pdata->cols || !pdata->keymap) {
                printk(KERN_ERR "No rows, cols or keymap from pdata\n");
@@ -301,7 +327,9 @@ static int __devinit omap_kp_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, omap_kp);
 
 
        platform_set_drvdata(pdev, omap_kp);
 
+       spin_lock_init(&omap_kp->suspend_lock);
        omap_kp->input = input_dev;
        omap_kp->input = input_dev;
+       omap_kp->suspended = 0;
 
        /* Disable the interrupt for the MPUIO keyboard */
        if (!cpu_is_omap24xx())
 
        /* Disable the interrupt for the MPUIO keyboard */
        if (!cpu_is_omap24xx())
diff --git a/drivers/input/keyboard/tsc2301_kp.c b/drivers/input/keyboard/tsc2301_kp.c
new file mode 100644 (file)
index 0000000..0f2cb7f
--- /dev/null
@@ -0,0 +1,475 @@
+/*
+ * TSC2301 keypad driver
+ *
+ * Copyright (C) 2005-2006 Nokia Corporation
+ *
+ * Written by Jarkko Oikarinen
+ * Rewritten by Juha Yrjola <juha.yrjola@nokia.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
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+
+#include <linux/spi/tsc2301.h>
+
+#define TSC2301_KEYBOARD_PRODUCT_ID      0x0051
+#define TSC2301_KEYBOARD_PRODUCT_VERSION 0x0001
+#define TSC2301_DEBOUNCE_TIME_2MS        0x0000
+#define TSC2301_DEBOUNCE_TIME_10MS       0x0800
+#define TSC2301_DEBOUNCE_TIME_20MS       0x1000
+#define TSC2301_DEBOUNCE_TIME_50MS       0x1800
+#define TSC2301_DEBOUNCE_TIME_60MS       0x2000
+#define TSC2301_DEBOUNCE_TIME_80MS       0x2800
+#define TSC2301_DEBOUNCE_TIME_100MS      0x3000
+#define TSC2301_DEBOUNCE_TIME_120MS      0x3800
+
+#define TSC2301_DEBOUNCE_TIME          TSC2301_DEBOUNCE_TIME_20MS
+
+#define TSC2301_RELEASE_TIMEOUT                50
+
+struct tsc2301_kp {
+       struct input_dev        *idev;
+       char                    phys[32];
+       spinlock_t              lock;
+       struct mutex            mutex;
+       struct timer_list       timer;
+       u16                     keys_pressed;
+       unsigned                pending:1;
+       unsigned                user_disabled:1;
+       unsigned                disable_depth;
+
+       struct spi_transfer     read_xfer[4];
+       struct spi_message      read_msg;
+
+       u16                     data;
+       u16                     mask;
+
+       int                     irq;
+       s16                     keymap[16];
+};
+
+static inline int tsc2301_kp_disabled(struct tsc2301 *tsc)
+{
+       return tsc->kp->disable_depth != 0;
+}
+
+static void tsc2301_kp_send_key_events(struct tsc2301 *tsc,
+                                      u16 prev_state,
+                                      u16 new_state)
+{
+       struct tsc2301_kp *kp = tsc->kp;
+       u16 common, released, pressed;
+       int i;
+
+       common = prev_state & new_state;
+       released = common ^ prev_state;
+       pressed = common ^ new_state;
+       if (!released && !pressed)
+               return;
+       for (i = 0; i < 16 && (released || pressed); i++) {
+               if (released & 1) {
+                       dev_dbg(&tsc->spi->dev, "key %d released\n", i);
+                       input_report_key(kp->idev, kp->keymap[i], 0);
+               }
+               released >>= 1;
+               if (pressed & 1) {
+                       dev_dbg(&tsc->spi->dev, "key %d pressed\n", i);
+                       input_report_key(kp->idev, kp->keymap[i], 1);
+               }
+               pressed >>= 1;
+       }
+       input_sync(kp->idev);
+}
+
+static inline void _filter_out(struct tsc2301 *tsc, u16 prev_state,
+                              u16 *new_state, int row1, int row2, u8 rect_pat)
+{
+       u16 mask;
+
+       mask = (rect_pat << (row1 * 4)) | (rect_pat << (row2 * 4));
+       mask &= ~prev_state;
+       *new_state &= ~mask;
+       dev_dbg(&tsc->spi->dev, "filtering ghost keys %02x\n", mask);
+}
+
+static void tsc2301_filter_ghost_keys(struct tsc2301 *tsc, u16 prev_state,
+                                     u16 *new_state)
+{
+       int row1, row2;
+       u16 key_map;
+       u16 row1_map;
+       static const u8 rect_pat[] = {
+               0x3, 0x5, 0x9, 0x6, 0xa, 0xc, 0,
+       };
+
+       key_map = *new_state;
+       for (row1 = 0; row1 < 4; row1++) {
+               row1_map = (key_map >> (row1 * 4)) & 0xf;
+               if (!row1_map)
+                       continue;
+               for (row2 = row1 + 1; row2 < 4; row2++) {
+                       u16 rect_map = (key_map >> (row2 * 4)) & 0xf;
+                       const u8 *rp;
+
+                       rect_map &= row1_map;
+                       if (!rect_map)
+                               continue;
+                       for (rp = rect_pat; *rp; rp++)
+                               if ((rect_map & *rp) == *rp)
+                                       _filter_out(tsc, prev_state, new_state,
+                                                   row1, row2, *rp);
+               }
+       }
+}
+
+static void tsc2301_kp_timer(unsigned long arg)
+{
+       struct tsc2301 *tsc = (void *) arg;
+       struct tsc2301_kp *kp = tsc->kp;
+       unsigned long flags;
+
+       tsc2301_kp_send_key_events(tsc, kp->keys_pressed, 0);
+       spin_lock_irqsave(&kp->lock, flags);
+       kp->keys_pressed = 0;
+       spin_unlock_irqrestore(&kp->lock, flags);
+}
+
+static void tsc2301_kp_rx(void *arg)
+{
+       struct tsc2301 *tsc = arg;
+       struct tsc2301_kp *kp = tsc->kp;
+       unsigned long flags;
+       u16 kp_data;
+
+       kp_data = kp->data;
+       dev_dbg(&tsc->spi->dev, "KP data %04x\n", kp_data);
+
+       tsc2301_filter_ghost_keys(tsc, kp->keys_pressed, &kp_data);
+       tsc2301_kp_send_key_events(tsc, kp->keys_pressed, kp_data);
+       spin_lock_irqsave(&kp->lock, flags);
+       kp->keys_pressed = kp_data;
+       kp->pending = 0;
+       spin_unlock_irqrestore(&kp->lock, flags);
+}
+
+static irqreturn_t tsc2301_kp_irq_handler(int irq, void *dev_id)
+{
+       struct tsc2301 *tsc = dev_id;
+       struct tsc2301_kp *kp = tsc->kp;
+       unsigned long flags;
+       int r;
+
+       spin_lock_irqsave(&kp->lock, flags);
+       if (tsc2301_kp_disabled(tsc)) {
+               spin_unlock_irqrestore(&kp->lock, flags);
+               return IRQ_HANDLED;
+       }
+       kp->pending = 1;
+       spin_unlock_irqrestore(&kp->lock, flags);
+       mod_timer(&kp->timer,
+                jiffies + msecs_to_jiffies(TSC2301_RELEASE_TIMEOUT));
+       r = spi_async(tsc->spi, &tsc->kp->read_msg);
+       if (r)
+               dev_err(&tsc->spi->dev, "kp: spi_async() failed");
+       return IRQ_HANDLED;
+}
+
+static void tsc2301_kp_start_scan(struct tsc2301 *tsc)
+{
+       tsc2301_write_reg(tsc, TSC2301_REG_KPMASK, tsc->kp->mask);
+       tsc2301_write_reg(tsc, TSC2301_REG_KEY, TSC2301_DEBOUNCE_TIME);
+}
+
+static void tsc2301_kp_stop_scan(struct tsc2301 *tsc)
+{
+       tsc2301_write_reg(tsc, TSC2301_REG_KEY, 1 << 14);
+}
+
+/* Must be called with the mutex held */
+static void tsc2301_kp_enable(struct tsc2301 *tsc)
+{
+       struct tsc2301_kp *kp = tsc->kp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&kp->lock, flags);
+       BUG_ON(!tsc2301_kp_disabled(tsc));
+       if (--kp->disable_depth != 0) {
+               spin_unlock_irqrestore(&kp->lock, flags);
+               return;
+       }
+       spin_unlock_irqrestore(&kp->lock, flags);
+
+       set_irq_type(kp->irq, IRQ_TYPE_EDGE_FALLING);
+       tsc2301_kp_start_scan(tsc);
+       enable_irq(kp->irq);
+}
+
+/* Must be called with the mutex held */
+static int tsc2301_kp_disable(struct tsc2301 *tsc, int release_keys)
+{
+       struct tsc2301_kp *kp = tsc->kp;
+       unsigned long flags;
+
+       spin_lock_irqsave(&kp->lock, flags);
+       if (kp->disable_depth++ != 0) {
+               spin_unlock_irqrestore(&kp->lock, flags);
+               goto out;
+       }
+       disable_irq_nosync(kp->irq);
+       set_irq_type(kp->irq, IRQ_TYPE_NONE);
+       spin_unlock_irqrestore(&kp->lock, flags);
+
+       while (kp->pending) {
+               msleep(1);
+       }
+
+       tsc2301_kp_stop_scan(tsc);
+out:
+       if (!release_keys)
+               del_timer(&kp->timer); /* let timeout release keys */
+
+       return 0;
+}
+
+/* The following workaround is needed for a HW bug triggered by the
+ * following:
+ * 1. keep any key pressed
+ * 2. disable keypad
+ * 3. release all keys
+ * 4. reenable keypad
+ * 5. disable touch screen controller
+ *
+ * After this the keypad scanner will get stuck in busy state and won't
+ * report any interrupts for further keypresses. One way to recover is to
+ * restart the keypad scanner whenever we enable / disable the
+ * touchscreen controller.
+ */
+void tsc2301_kp_restart(struct tsc2301 *tsc)
+{
+       if (!tsc2301_kp_disabled(tsc)) {
+               tsc2301_kp_start_scan(tsc);
+       }
+}
+
+static ssize_t tsc2301_kp_disable_show(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
+{
+       struct tsc2301          *tsc = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", tsc2301_kp_disabled(tsc) ? 1 : 0);
+}
+
+static ssize_t tsc2301_kp_disable_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       struct tsc2301          *tsc = dev_get_drvdata(dev);
+       struct tsc2301_kp       *kp = tsc->kp;
+       char *endp;
+       int i;
+
+       i = simple_strtoul(buf, &endp, 10);
+       i = i ? 1 : 0;
+
+       mutex_lock(&kp->mutex);
+       if (i == kp->user_disabled) {
+               mutex_unlock(&kp->mutex);
+               return count;
+       }
+       kp->user_disabled = i;
+
+       if (i)
+               tsc2301_kp_disable(tsc, 1);
+       else
+               tsc2301_kp_enable(tsc);
+       mutex_unlock(&kp->mutex);
+
+       return count;
+}
+
+static DEVICE_ATTR(disable_kp, 0664, tsc2301_kp_disable_show,
+                  tsc2301_kp_disable_store);
+
+static const u16 tsc2301_kp_read_data = 0x8000 | TSC2301_REG_KPDATA;
+
+static void tsc2301_kp_setup_spi_xfer(struct tsc2301 *tsc)
+{
+       struct tsc2301_kp *kp = tsc->kp;
+       struct spi_message *m = &kp->read_msg;
+       struct spi_transfer *x = &kp->read_xfer[0];
+
+       spi_message_init(&kp->read_msg);
+
+       x->tx_buf = &tsc2301_kp_read_data;
+       x->len = 2;
+       spi_message_add_tail(x, m);
+       x++;
+
+       x->rx_buf = &kp->data;
+       x->len = 2;
+       spi_message_add_tail(x, m);
+
+       m->complete = tsc2301_kp_rx;
+       m->context = tsc;
+}
+
+#ifdef CONFIG_PM
+int tsc2301_kp_suspend(struct tsc2301 *tsc)
+{
+       struct tsc2301_kp *kp = tsc->kp;
+
+       mutex_lock(&kp->mutex);
+       tsc2301_kp_disable(tsc, 1);
+       mutex_unlock(&kp->mutex);
+       return 0;
+}
+
+void tsc2301_kp_resume(struct tsc2301 *tsc)
+{
+       struct tsc2301_kp *kp = tsc->kp;
+
+       mutex_lock(&kp->mutex);
+       tsc2301_kp_enable(tsc);
+       mutex_unlock(&kp->mutex);
+}
+#endif
+
+int __devinit tsc2301_kp_init(struct tsc2301 *tsc,
+                             struct tsc2301_platform_data *pdata)
+{
+       struct input_dev *idev;
+       struct tsc2301_kp *kp;
+       int r, i;
+       u16 mask;
+
+       if (pdata->keyb_int < 0) {
+               dev_err(&tsc->spi->dev, "need kbirq");
+               return -EINVAL;
+       }
+
+       kp = kzalloc(sizeof(*kp), GFP_KERNEL);
+       if (kp == NULL)
+               return -ENOMEM;
+       tsc->kp = kp;
+
+       kp->irq = pdata->keyb_int;
+       spin_lock_init(&kp->lock);
+       mutex_init(&kp->mutex);
+
+       init_timer(&kp->timer);
+       kp->timer.data = (unsigned long) tsc;
+       kp->timer.function = tsc2301_kp_timer;
+
+       idev = input_allocate_device();
+       if (idev == NULL) {
+               r = -ENOMEM;
+               goto err1;
+       }
+       if (pdata->keyb_name)
+               idev->name = pdata->keyb_name;
+       else
+               idev->name = "TSC2301 keypad";
+       snprintf(kp->phys, sizeof(kp->phys), "%s/input-kp", dev_name(&tsc->spi->dev));
+       idev->phys = kp->phys;
+
+       mask = 0;
+       idev->evbit[0] = BIT(EV_KEY);
+       for (i = 0; i < 16; i++) {
+               if (pdata->keymap[i] > 0) {
+                       set_bit(pdata->keymap[i], idev->keybit);
+                       kp->keymap[i] = pdata->keymap[i];
+               } else {
+                       kp->keymap[i] = -1;
+                       mask |= 1 << i;
+               }
+       }
+
+       if (pdata->kp_rep)
+               set_bit(EV_REP, idev->evbit);
+
+       kp->idev = idev;
+
+       tsc2301_kp_setup_spi_xfer(tsc);
+
+       r = device_create_file(&tsc->spi->dev, &dev_attr_disable_kp);
+       if (r < 0)
+               goto err2;
+
+       tsc2301_kp_start_scan(tsc);
+
+       /* IRQ mode 0 is faulty, it can cause the KBIRQ to get stuck.
+        * Mode 2 deasserts the IRQ at:
+        * - HW or SW reset
+        * - Setting SCS flag in REG_KEY register
+        * - Releasing all keys
+        * - Reading the REG_KPDATA
+        */
+       tsc2301_write_kbc(tsc, 2);
+
+       tsc2301_write_reg(tsc, TSC2301_REG_KPMASK, mask);
+       kp->mask = mask;
+
+       set_irq_type(kp->irq, IRQ_TYPE_EDGE_FALLING);
+
+       r = request_irq(kp->irq, tsc2301_kp_irq_handler, IRQF_SAMPLE_RANDOM,
+                       "tsc2301-kp", tsc);
+       if (r < 0) {
+               dev_err(&tsc->spi->dev, "unable to get kbirq IRQ");
+               goto err3;
+       }
+       set_irq_wake(kp->irq, 1);
+
+       /* We need to read the register once..? */
+       tsc2301_read_reg(tsc, TSC2301_REG_KPDATA);
+
+       r = input_register_device(idev);
+       if (r < 0) {
+               dev_err(&tsc->spi->dev, "can't register keypad device\n");
+               goto err4;
+       }
+
+       return 0;
+
+err4:
+       free_irq(kp->irq, tsc);
+err3:
+       tsc2301_kp_stop_scan(tsc);
+       device_remove_file(&tsc->spi->dev, &dev_attr_disable_kp);
+err2:
+       input_free_device(kp->idev);
+err1:
+       kfree(kp);
+       return r;
+}
+
+void __devexit tsc2301_kp_exit(struct tsc2301 *tsc)
+{
+       struct tsc2301_kp *kp = tsc->kp;
+
+       tsc2301_kp_disable(tsc, 1);
+       input_unregister_device(kp->idev);
+       free_irq(kp->irq, tsc);
+       device_remove_file(&tsc->spi->dev, &dev_attr_disable_kp);
+
+       kfree(kp);
+}
diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
new file mode 100644 (file)
index 0000000..8bfd650
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * twl4030_keypad.c - driver for 8x8 keypad controller in twl4030 chips
+ *
+ * Copyright (C) 2007 Texas Instruments, Inc.
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * Code re-written for 2430SDP by:
+ * Syed Mohammed Khasim <x0khasim@ti.com>
+ *
+ * Initial Code:
+ * Manjunatha G K <manjugk@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+
+
+/*
+ * The TWL4030 family chips include a keypad controller that supports
+ * up to an 8x8 switch matrix.  The controller can issue system wakeup
+ * events, since it uses only the always-on 32KiHz oscillator, and has
+ * an internal state machine that decodes pressed keys, including
+ * multi-key combinations.
+ *
+ * This driver lets boards define what keycodes they wish to report for
+ * which scancodes, as part of the "struct twl4030_keypad_data" used in
+ * the probe() routine.
+ *
+ * See the TPS65950 documentation; that's the general availability
+ * version of the TWL5030 second generation part.
+ */
+#define MAX_ROWS               8       /* TWL4030 hard limit */
+
+struct twl4030_keypad {
+       unsigned        *keymap;
+       unsigned int    keymapsize;
+       u16             kp_state[MAX_ROWS];
+       unsigned        n_rows;
+       unsigned        n_cols;
+       unsigned        irq;
+
+       struct device   *dbg_dev;
+       struct input_dev *input;
+};
+
+#define ROWCOL_MASK    KEY(0xf, 0xf, 0)
+#define KEYNUM_MASK    ~PERSISTENT_KEY(0xf, 0xf)
+
+/*----------------------------------------------------------------------*/
+
+/* arbitrary prescaler value 0..7 */
+#define PTV_PRESCALER                  4
+
+/* Register Offsets */
+#define KEYP_CTRL                      0x00
+#define KEYP_DEB                       0x01
+#define KEYP_LONG_KEY                  0x02
+#define KEYP_LK_PTV                    0x03
+#define KEYP_TIMEOUT_L                 0x04
+#define KEYP_TIMEOUT_H                 0x05
+#define KEYP_KBC                       0x06
+#define KEYP_KBR                       0x07
+#define KEYP_SMS                       0x08
+#define KEYP_FULL_CODE_7_0             0x09    /* row 0 column status */
+#define KEYP_FULL_CODE_15_8            0x0a    /* ... row 1 ... */
+#define KEYP_FULL_CODE_23_16           0x0b
+#define KEYP_FULL_CODE_31_24           0x0c
+#define KEYP_FULL_CODE_39_32           0x0d
+#define KEYP_FULL_CODE_47_40           0x0e
+#define KEYP_FULL_CODE_55_48           0x0f
+#define KEYP_FULL_CODE_63_56           0x10
+#define KEYP_ISR1                      0x11
+#define KEYP_IMR1                      0x12
+#define KEYP_ISR2                      0x13
+#define KEYP_IMR2                      0x14
+#define KEYP_SIR                       0x15
+#define KEYP_EDR                       0x16    /* edge triggers */
+#define KEYP_SIH_CTRL                  0x17
+
+/* KEYP_CTRL_REG Fields */
+#define KEYP_CTRL_SOFT_NRST            BIT(0)
+#define KEYP_CTRL_SOFTMODEN            BIT(1)
+#define KEYP_CTRL_LK_EN                        BIT(2)
+#define KEYP_CTRL_TOE_EN               BIT(3)
+#define KEYP_CTRL_TOLE_EN              BIT(4)
+#define KEYP_CTRL_RP_EN                        BIT(5)
+#define KEYP_CTRL_KBD_ON               BIT(6)
+
+/* KEYP_DEB, KEYP_LONG_KEY, KEYP_TIMEOUT_x*/
+#define KEYP_PERIOD_US(t, prescale)    ((t) / (31 << (prescale + 1)) - 1)
+
+/* KEYP_LK_PTV_REG Fields */
+#define KEYP_LK_PTV_PTV_SHIFT          5
+
+/* KEYP_{IMR,ISR,SIR} Fields */
+#define KEYP_IMR1_MIS                  BIT(3)
+#define KEYP_IMR1_TO                   BIT(2)
+#define KEYP_IMR1_LK                   BIT(1)
+#define KEYP_IMR1_KP                   BIT(0)
+
+/* KEYP_EDR Fields */
+#define KEYP_EDR_KP_FALLING            0x01
+#define KEYP_EDR_KP_RISING             0x02
+#define KEYP_EDR_KP_BOTH               0x03
+#define KEYP_EDR_LK_FALLING            0x04
+#define KEYP_EDR_LK_RISING             0x08
+#define KEYP_EDR_TO_FALLING            0x10
+#define KEYP_EDR_TO_RISING             0x20
+#define KEYP_EDR_MIS_FALLING           0x40
+#define KEYP_EDR_MIS_RISING            0x80
+
+
+/*----------------------------------------------------------------------*/
+
+static int twl4030_kpread(struct twl4030_keypad *kp,
+               u8 *data, u32 reg, u8 num_bytes)
+{
+       int ret;
+
+       ret = twl4030_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
+       if (ret < 0) {
+               dev_warn(kp->dbg_dev,
+                       "Couldn't read TWL4030: %X - ret %d[%x]\n",
+                        reg, ret, ret);
+               return ret;
+       }
+       return ret;
+}
+
+static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
+{
+       int ret;
+
+       ret = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
+       if (ret < 0) {
+               dev_warn(kp->dbg_dev,
+                       "Could not write TWL4030: %X - ret %d[%x]\n",
+                        reg, ret, ret);
+               return ret;
+       }
+       return ret;
+}
+
+static int twl4030_find_key(struct twl4030_keypad *kp, int col, int row)
+{
+       int i, rc;
+
+       rc = KEY(col, row, 0);
+       for (i = 0; i < kp->keymapsize; i++)
+               if ((kp->keymap[i] & ROWCOL_MASK) == rc)
+                       return kp->keymap[i] & (KEYNUM_MASK | KEY_PERSISTENT);
+
+       return -EINVAL;
+}
+
+static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
+{
+       /* If all bits in a row are active for all coloumns then
+        * we have that row line connected to gnd. Mark this
+        * key on as if it was on matrix position n_cols (ie
+        * one higher than the size of the matrix).
+        */
+       if (col == 0xFF)
+               return 1 << kp->n_cols;
+       else
+               return col & ((1 << kp->n_cols) - 1);
+}
+
+static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state)
+{
+       u8 new_state[MAX_ROWS];
+       int row;
+       int ret = twl4030_kpread(kp,
+                                new_state, KEYP_FULL_CODE_7_0, kp->n_rows);
+       if (ret >= 0) {
+               for (row = 0; row < kp->n_rows; row++)
+                       state[row] = twl4030_col_xlate(kp, new_state[row]);
+       }
+       return ret;
+}
+
+static int twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state)
+{
+       int i;
+       u16 check = 0;
+
+       for (i = 0; i < kp->n_rows; i++) {
+               u16 col = key_state[i];
+
+               if ((col & check) && hweight16(col) > 1)
+                       return 1;
+               check |= col;
+       }
+
+       return 0;
+}
+
+static void twl4030_kp_scan(struct twl4030_keypad *kp, int release_all)
+{
+       u16 new_state[MAX_ROWS];
+       int col, row;
+
+       if (release_all)
+               memset(new_state, 0, sizeof(new_state));
+       else {
+               /* check for any changes */
+               int ret = twl4030_read_kp_matrix_state(kp, new_state);
+
+               if (ret < 0)    /* panic ... */
+                       return;
+               if (twl4030_is_in_ghost_state(kp, new_state))
+                       return;
+       }
+
+       /* check for changes and print those */
+       for (row = 0; row < kp->n_rows; row++) {
+               int changed = new_state[row] ^ kp->kp_state[row];
+
+               if (!changed)
+                       continue;
+
+               for (col = 0; col < kp->n_cols; col++) {
+                       int key;
+
+                       if (!(changed & (1 << col)))
+                               continue;
+
+                       dev_dbg(kp->dbg_dev, "key [%d:%d] %s\n", row, col,
+                               (new_state[row] & (1 << col)) ?
+                               "press" : "release");
+
+                       key = twl4030_find_key(kp, col, row);
+                       if (key < 0)
+                               dev_warn(kp->dbg_dev,
+                                       "Spurious key event %d-%d\n",
+                                        col, row);
+                       else if (key & KEY_PERSISTENT)
+                               continue;
+                       else
+                               input_report_key(kp->input, key,
+                                                new_state[row] & (1 << col));
+               }
+               kp->kp_state[row] = new_state[row];
+       }
+       input_sync(kp->input);
+}
+
+/*
+ * Keypad interrupt handler
+ */
+static irqreturn_t do_kp_irq(int irq, void *_kp)
+{
+       struct twl4030_keypad *kp = _kp;
+       u8 reg;
+       int ret;
+
+#ifdef CONFIG_LOCKDEP
+       /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
+        * we don't want and can't tolerate.  Although it might be
+        * friendlier not to borrow this thread context...
+        */
+       local_irq_enable();
+#endif
+
+       /* Read & Clear TWL4030 pending interrupt */
+       ret = twl4030_kpread(kp, &reg, KEYP_ISR1, 1);
+
+       /* Release all keys if I2C has gone bad or
+        * the KEYP has gone to idle state */
+       if ((ret >= 0) && (reg & KEYP_IMR1_KP))
+               twl4030_kp_scan(kp, 0);
+       else
+               twl4030_kp_scan(kp, 1);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Registers keypad device with input subsystem
+ * and configures TWL4030 keypad registers
+ */
+static int __devinit twl4030_kp_probe(struct platform_device *pdev)
+{
+       u8 reg;
+       int i;
+       int ret = 0;
+       struct twl4030_keypad *kp;
+       struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
+
+       if (!pdata || !pdata->rows || !pdata->cols || !pdata->keymap
+                       || pdata->rows > 8 || pdata->cols > 8) {
+               dev_err(&pdev->dev, "Invalid platform_data\n");
+               return -EINVAL;
+       }
+
+       kp = kzalloc(sizeof(*kp), GFP_KERNEL);
+       if (!kp)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, kp);
+
+       /* Get the debug Device */
+       kp->dbg_dev = &pdev->dev;
+
+       kp->input = input_allocate_device();
+       if (!kp->input) {
+               kfree(kp);
+               return -ENOMEM;
+       }
+
+       kp->keymap = pdata->keymap;
+       kp->keymapsize = pdata->keymapsize;
+       kp->n_rows = pdata->rows;
+       kp->n_cols = pdata->cols;
+       kp->irq = platform_get_irq(pdev, 0);
+
+       /* setup input device */
+       __set_bit(EV_KEY, kp->input->evbit);
+
+       /* Enable auto repeat feature of Linux input subsystem */
+       if (pdata->rep)
+               __set_bit(EV_REP, kp->input->evbit);
+
+       for (i = 0; i < kp->keymapsize; i++)
+               __set_bit(kp->keymap[i] & KEYNUM_MASK,
+                               kp->input->keybit);
+
+       kp->input->name         = "TWL4030 Keypad";
+       kp->input->phys         = "twl4030_keypad/input0";
+       kp->input->dev.parent   = &pdev->dev;
+
+       kp->input->id.bustype   = BUS_HOST;
+       kp->input->id.vendor    = 0x0001;
+       kp->input->id.product   = 0x0001;
+       kp->input->id.version   = 0x0003;
+
+       kp->input->keycode      = kp->keymap;
+       kp->input->keycodesize  = sizeof(unsigned int);
+       kp->input->keycodemax   = kp->keymapsize;
+
+       ret = input_register_device(kp->input);
+       if (ret < 0) {
+               dev_err(kp->dbg_dev,
+                       "Unable to register twl4030 keypad device\n");
+               goto err2;
+       }
+
+       /* Enable controller, with hardware decoding but not autorepeat */
+       reg = KEYP_CTRL_SOFT_NRST | KEYP_CTRL_SOFTMODEN
+               | KEYP_CTRL_TOE_EN | KEYP_CTRL_KBD_ON;
+       ret = twl4030_kpwrite_u8(kp, reg, KEYP_CTRL);
+       if (ret < 0)
+               goto err3;
+
+       /* NOTE:  we could use sih_setup() here to package keypad
+        * event sources as four different IRQs ... but we don't.
+        */
+
+       /* Enable TO rising and KP rising and falling edge detection */
+       reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING;
+       ret = twl4030_kpwrite_u8(kp, reg, KEYP_EDR);
+       if (ret < 0)
+               goto err3;
+
+       /* Set PTV prescaler Field */
+       reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT);
+       ret = twl4030_kpwrite_u8(kp, reg, KEYP_LK_PTV);
+       if (ret < 0)
+               goto err3;
+
+       /* Set key debounce time to 20 ms */
+       i = KEYP_PERIOD_US(20000, PTV_PRESCALER);
+       ret = twl4030_kpwrite_u8(kp, i, KEYP_DEB);
+       if (ret < 0)
+               goto err3;
+
+       /* Set timeout period to 100 ms */
+       i = KEYP_PERIOD_US(200000, PTV_PRESCALER);
+       ret = twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L);
+       if (ret < 0)
+               goto err3;
+       ret = twl4030_kpwrite_u8(kp, (i >> 8), KEYP_TIMEOUT_H);
+       if (ret < 0)
+               goto err3;
+
+       /* Enable Clear-on-Read; disable remembering events that fire
+        * after the IRQ but before our handler acks (reads) them,
+        */
+       reg = TWL4030_SIH_CTRL_COR_MASK | TWL4030_SIH_CTRL_PENDDIS_MASK;
+       ret = twl4030_kpwrite_u8(kp, reg, KEYP_SIH_CTRL);
+       if (ret < 0)
+               goto err3;
+
+       /* initialize key state; irqs update it from here on */
+       ret = twl4030_read_kp_matrix_state(kp, kp->kp_state);
+       if (ret < 0)
+               goto err3;
+
+       /*
+        * This ISR will always execute in kernel thread context because of
+        * the need to access the TWL4030 over the I2C bus.
+        *
+        * NOTE:  we assume this host is wired to TWL4040 INT1, not INT2 ...
+        */
+       ret = request_irq(kp->irq, do_kp_irq, 0, pdev->name, kp);
+       if (ret < 0) {
+               dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
+                       kp->irq);
+               goto err3;
+       } else {
+               /* Enable KP and TO interrupts now. */
+               reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
+               ret = twl4030_kpwrite_u8(kp, reg, KEYP_IMR1);
+               if (ret < 0)
+                       goto err5;
+       }
+
+       return ret;
+err5:
+       /* mask all events - we don't care about the result */
+       (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
+       free_irq(kp->irq, NULL);
+err3:
+       input_unregister_device(kp->input);
+       kp->input = NULL;
+err2:
+       input_free_device(kp->input);
+       kfree(kp);
+       return -ENODEV;
+}
+
+static int __devexit twl4030_kp_remove(struct platform_device *pdev)
+{
+       struct twl4030_keypad *kp = platform_get_drvdata(pdev);
+
+       free_irq(kp->irq, kp);
+       input_unregister_device(kp->input);
+       kfree(kp);
+
+       return 0;
+}
+
+/*
+ * NOTE: twl4030 are multi-function devices connected via I2C.
+ * So this device is a child of an I2C parent, thus it needs to
+ * support unplug/replug (which most platform devices don't).
+ */
+
+MODULE_ALIAS("platform:twl4030_keypad");
+
+static struct platform_driver twl4030_kp_driver = {
+       .probe          = twl4030_kp_probe,
+       .remove         = __devexit_p(twl4030_kp_remove),
+       .driver         = {
+               .name   = "twl4030_keypad",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init twl4030_kp_init(void)
+{
+       return platform_driver_register(&twl4030_kp_driver);
+}
+module_init(twl4030_kp_init);
+
+static void __exit twl4030_kp_exit(void)
+{
+       platform_driver_unregister(&twl4030_kp_driver);
+}
+module_exit(twl4030_kp_exit);
+
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("TWL4030 Keypad Driver");
+MODULE_LICENSE("GPL");
index 67e5553f699a1053224b233e1f2fb85b778471ec..6fa9e3847c3679a041a08ea6bb94e2cb14a0a7db 100644 (file)
@@ -193,6 +193,16 @@ config INPUT_CM109
          To compile this driver as a module, choose M here: the module will be
          called cm109.
 
          To compile this driver as a module, choose M here: the module will be
          called cm109.
 
+config INPUT_TWL4030_PWRBUTTON
+       tristate "TWL4030 Power button Driver"
+       depends on TWL4030_CORE
+       help
+         Say Y here if you want to enable power key reporting via the
+         TWL4030 family of chips.
+
+         To compile this driver as a module, choose M here. The module will
+         be called twl4030_pwrbutton.
+
 config INPUT_UINPUT
        tristate "User level driver support"
        help
 config INPUT_UINPUT
        tristate "User level driver support"
        help
index bb62e6efacf3476d78c66fd0c9a575c465f2a99a..2fabcdba827d2d50ccab87419483a4fc7d6882f5 100644 (file)
@@ -19,6 +19,7 @@ obj-$(CONFIG_INPUT_YEALINK)           += yealink.o
 obj-$(CONFIG_INPUT_CM109)              += cm109.o
 obj-$(CONFIG_HP_SDC_RTC)               += hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_UINPUT)             += uinput.o
 obj-$(CONFIG_INPUT_CM109)              += cm109.o
 obj-$(CONFIG_HP_SDC_RTC)               += hp_sdc_rtc.o
 obj-$(CONFIG_INPUT_UINPUT)             += uinput.o
+obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON)  += twl4030-pwrbutton.o
 obj-$(CONFIG_INPUT_APANEL)             += apanel.o
 obj-$(CONFIG_INPUT_SGI_BTNS)           += sgi_btns.o
 obj-$(CONFIG_INPUT_PCF50633_PMU)       += pcf50633-input.o
 obj-$(CONFIG_INPUT_APANEL)             += apanel.o
 obj-$(CONFIG_INPUT_SGI_BTNS)           += sgi_btns.o
 obj-$(CONFIG_INPUT_PCF50633_PMU)       += pcf50633-input.o
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c
new file mode 100644 (file)
index 0000000..7150830
--- /dev/null
@@ -0,0 +1,146 @@
+/**
+ * twl4030-pwrbutton.c - TWL4030 Power Button Input Driver
+ *
+ * Copyright (C) 2008-2009 Nokia Corporation
+ *
+ * Written by Peter De Schrijver <peter.de-schrijver@nokia.com>
+ * Several fixes by Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+
+#define PWR_PWRON_IRQ (1 << 0)
+
+#define STS_HW_CONDITIONS 0xf
+
+static irqreturn_t powerbutton_irq(int irq, void *_pwr)
+{
+       struct input_dev *pwr = _pwr;
+       int err;
+       u8 value;
+
+#ifdef CONFIG_LOCKDEP
+       /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
+        * we don't want and can't tolerate since this is a threaded
+        * IRQ and can sleep due to the i2c reads it has to issue.
+        * Although it might be friendlier not to borrow this thread
+        * context...
+        */
+       local_irq_enable();
+#endif
+
+       err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value,
+                                 STS_HW_CONDITIONS);
+       if (!err)  {
+               input_report_key(pwr, KEY_POWER, value & PWR_PWRON_IRQ);
+               input_sync(pwr);
+       } else {
+               dev_err(pwr->dev.parent, "twl4030: i2c error %d while reading"
+                       " TWL4030 PM_MASTER STS_HW_CONDITIONS register\n", err);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int __devinit twl4030_pwrbutton_probe(struct platform_device *pdev)
+{
+       struct input_dev *pwr;
+       int irq = platform_get_irq(pdev, 0);
+       int err;
+
+       pwr = input_allocate_device();
+       if (!pwr) {
+               dev_dbg(&pdev->dev, "Can't allocate power button\n");
+               err = -ENOMEM;
+               goto out;
+       }
+
+       err = request_irq(irq, powerbutton_irq,
+                       IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
+                       "twl4030_pwrbutton", pwr);
+       if (err < 0) {
+               dev_dbg(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err);
+               goto free_input_dev;
+       }
+
+       pwr->evbit[0] = BIT_MASK(EV_KEY);
+       pwr->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER);
+       pwr->name = "twl4030_pwrbutton";
+       pwr->phys = "twl4030_pwrbutton/input0";
+       pwr->dev.parent = &pdev->dev;
+       platform_set_drvdata(pdev, pwr);
+
+       err = input_register_device(pwr);
+       if (err) {
+               dev_dbg(&pdev->dev, "Can't register power button: %d\n", err);
+               goto free_irq_and_out;
+       }
+
+       return 0;
+
+free_irq_and_out:
+       free_irq(irq, NULL);
+free_input_dev:
+       input_free_device(pwr);
+out:
+       return err;
+}
+
+static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev)
+{
+       struct input_dev *pwr = platform_get_drvdata(pdev);
+       int irq = platform_get_irq(pdev, 0);
+
+       free_irq(irq, pwr);
+       input_unregister_device(pwr);
+
+       return 0;
+}
+
+struct platform_driver twl4030_pwrbutton_driver = {
+       .probe          = twl4030_pwrbutton_probe,
+       .remove         = __devexit_p(twl4030_pwrbutton_remove),
+       .driver         = {
+               .name   = "twl4030_pwrbutton",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init twl4030_pwrbutton_init(void)
+{
+       return platform_driver_register(&twl4030_pwrbutton_driver);
+}
+module_init(twl4030_pwrbutton_init);
+
+static void __exit twl4030_pwrbutton_exit(void)
+{
+       platform_driver_unregister(&twl4030_pwrbutton_driver);
+}
+module_exit(twl4030_pwrbutton_exit);
+
+MODULE_ALIAS("platform:twl4030_pwrbutton");
+MODULE_DESCRIPTION("Triton2 Power Button");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Peter De Schrijver <peter.de-schrijver@nokia.com>");
+MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");
+
index bb6486a8c070f599430020681c395e9e59300eb9..5bd3bfac9c0dc40b4f5677ad082c1db9d774f855 100644 (file)
@@ -242,6 +242,32 @@ config TOUCHSCREEN_ATMEL_TSADCC
          To compile this driver as a module, choose M here: the
          module will be called atmel_tsadcc.
 
          To compile this driver as a module, choose M here: the
          module will be called atmel_tsadcc.
 
+config TOUCHSCREEN_TSC2005
+       tristate "TSC2005 touchscreen support"
+       depends on SPI_MASTER
+       help
+         Say Y here for if you are using the touchscreen features of TSC2005.
+
+config TOUCHSCREEN_TSC210X
+       tristate "TI TSC210x based touchscreens"
+       depends on SPI_MASTER
+       select SPI_TSC210X
+       help
+         Say Y here if you have a touchscreen interface using a
+         TI TSC210x controller, and your board-specific initialisation
+         code includes that in its table of SPI devices.
+
+         If unsure, say N (but it's safe to say "Y").
+
+         To compile this driver as a module, choose M here: the
+         module will be called tsc210x_ts.
+
+config TOUCHSCREEN_TSC2301
+       tristate "TSC2301 touchscreen support"
+       depends on SPI_TSC2301
+       help
+         Say Y here for if you are using the touchscreen features of TSC2301.
+
 config TOUCHSCREEN_UCB1400
        tristate "Philips UCB1400 touchscreen"
        depends on AC97_BUS
 config TOUCHSCREEN_UCB1400
        tristate "Philips UCB1400 touchscreen"
        depends on AC97_BUS
index d3375aff46fe14303ed44f44e686213485aafb20..a2530b83ff74655ebf780f46cd2ca13a560806b9 100644 (file)
@@ -25,11 +25,11 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT)  += penmount.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)   += touchit213.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)   += touchright.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)     += touchwin.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213)   += touchit213.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT)   += touchright.o
 obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN)     += touchwin.o
-obj-$(CONFIG_TOUCHSCREEN_TSC2007)      += tsc2007.o
 obj-$(CONFIG_TOUCHSCREEN_UCB1400)      += ucb1400_ts.o
 obj-$(CONFIG_TOUCHSCREEN_UCB1400)      += ucb1400_ts.o
-obj-$(CONFIG_TOUCHSCREEN_WACOM_W8001)  += wacom_w8001.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2005)      += tsc2005.o
+obj-$(CONFIG_TOUCHSCREEN_TSC210X)      += tsc210x_ts.o
+obj-$(CONFIG_TOUCHSCREEN_TSC2301)      += tsc2301_ts.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX)       += wm97xx-ts.o
 obj-$(CONFIG_TOUCHSCREEN_WM97XX)       += wm97xx-ts.o
-obj-$(CONFIG_TOUCHSCREEN_DA9034)       += da9034-ts.o
 wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
 wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
 wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
 wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o
 wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o
 wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o
index 056ac77e2cf0ed6fe5624d9f62f35bbab2d57697..c5ecce1842fea53539e828c2bfd67d9fc5583cc3 100644 (file)
@@ -878,6 +878,15 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                return -ENODEV;
        }
 
                return -ENODEV;
        }
 
+       /* enable voltage */
+       if (pdata->vaux_control != NULL) {
+               err = pdata->vaux_control(VAUX_ENABLE);
+               if (err != 0) {
+                       dev_dbg(&spi->dev, "TS vaux enable failed\n");
+                       return err;
+               }
+       }
+
        /* don't exceed max specified sample rate */
        if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) {
                dev_dbg(&spi->dev, "f(sample) %d KHz?\n",
        /* don't exceed max specified sample rate */
        if (spi->max_speed_hz > (125000 * SAMPLE_BITS)) {
                dev_dbg(&spi->dev, "f(sample) %d KHz?\n",
diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c
new file mode 100644 (file)
index 0000000..ec4c9e7
--- /dev/null
@@ -0,0 +1,728 @@
+/*
+ * TSC2005 touchscreen driver
+ *
+ * Copyright (C) 2006-2008 Nokia Corporation
+ *
+ * Author: Lauri Leukkunen <lauri.leukkunen@nokia.com>
+ * based on TSC2301 driver by Klaus K. Pedersen <klaus.k.pedersen@nokia.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
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+
+#include <linux/spi/tsc2005.h>
+
+/**
+ * The touchscreen interface operates as follows:
+ *
+ * Initialize:
+ *    Request access to GPIO103 (DAV)
+ *    tsc2005_dav_irq_handler will trigger when DAV line goes down
+ *
+ *  1) Pen is pressed against touchscreeen
+ *  2) TSC2005 performs AD conversion
+ *  3) After the conversion is done TSC2005 drives DAV line down
+ *  4) GPIO IRQ is received and tsc2005_dav_irq_handler is called
+ *  5) tsc2005_ts_irq_handler queues up an spi transfer to fetch
+ *     the x, y, z1, z2 values
+ *  6) tsc2005_ts_rx() reports coordinates to input layer and
+ *     sets up tsc2005_ts_timer() to be called after TSC2005_TS_SCAN_TIME
+ *  7)  When the penup_timer expires, there have not been DAV interrupts
+ *     during the last 20ms which means the pen has been lifted.
+ */
+
+#define TSC2005_VDD_LOWER_27
+
+#ifdef TSC2005_VDD_LOWER_27
+#define TSC2005_HZ     (10000000)
+#else
+#define TSC2005_HZ     (25000000)
+#endif
+
+#define TSC2005_CMD    (0x80)
+#define TSC2005_REG    (0x00)
+
+#define TSC2005_CMD_STOP       (1)
+#define TSC2005_CMD_10BIT      (0 << 2)
+#define TSC2005_CMD_12BIT      (1 << 2)
+
+#define TSC2005_CMD_SCAN_XYZZ  (0 << 3)
+#define TSC2005_CMD_SCAN_XY    (1 << 3)
+#define TSC2005_CMD_SCAN_X     (2 << 3)
+#define TSC2005_CMD_SCAN_Y     (3 << 3)
+#define TSC2005_CMD_SCAN_ZZ    (4 << 3)
+#define TSC2005_CMD_AUX_SINGLE (5 << 3)
+#define TSC2005_CMD_TEMP1      (6 << 3)
+#define TSC2005_CMD_TEMP2      (7 << 3)
+#define TSC2005_CMD_AUX_CONT   (8 << 3)
+#define TSC2005_CMD_TEST_X_CONN        (9 << 3)
+#define TSC2005_CMD_TEST_Y_CONN        (10 << 3)
+/* command 11 reserved */
+#define TSC2005_CMD_TEST_SHORT (12 << 3)
+#define TSC2005_CMD_DRIVE_XX   (13 << 3)
+#define TSC2005_CMD_DRIVE_YY   (14 << 3)
+#define TSC2005_CMD_DRIVE_YX   (15 << 3)
+
+#define TSC2005_REG_X          (0 << 3)
+#define TSC2005_REG_Y          (1 << 3)
+#define TSC2005_REG_Z1         (2 << 3)
+#define TSC2005_REG_Z2         (3 << 3)
+#define TSC2005_REG_AUX                (4 << 3)
+#define TSC2005_REG_TEMP1      (5 << 3)
+#define TSC2005_REG_TEMP2      (6 << 3)
+#define TSC2005_REG_STATUS     (7 << 3)
+#define TSC2005_REG_AUX_HIGH   (8 << 3)
+#define TSC2005_REG_AUX_LOW    (9 << 3)
+#define TSC2005_REG_TEMP_HIGH  (10 << 3)
+#define TSC2005_REG_TEMP_LOW   (11 << 3)
+#define TSC2005_REG_CFR0       (12 << 3)
+#define TSC2005_REG_CFR1       (13 << 3)
+#define TSC2005_REG_CFR2       (14 << 3)
+#define TSC2005_REG_FUNCTION   (15 << 3)
+
+#define TSC2005_REG_PND0       (1 << 1)
+#define TSC2005_REG_READ       (0x01)
+#define TSC2005_REG_WRITE      (0x00)
+
+
+#define TSC2005_CFR0_LONGSAMPLING      (1)
+#define TSC2005_CFR0_DETECTINWAIT      (1 << 1)
+#define TSC2005_CFR0_SENSETIME_32US    (0)
+#define TSC2005_CFR0_SENSETIME_96US    (1 << 2)
+#define TSC2005_CFR0_SENSETIME_544US   (1 << 3)
+#define TSC2005_CFR0_SENSETIME_2080US  (1 << 4)
+#define TSC2005_CFR0_SENSETIME_2656US  (0x001C)
+#define TSC2005_CFR0_PRECHARGE_20US    (0x0000)
+#define TSC2005_CFR0_PRECHARGE_84US    (0x0020)
+#define TSC2005_CFR0_PRECHARGE_276US   (0x0040)
+#define TSC2005_CFR0_PRECHARGE_1044US  (0x0080)
+#define TSC2005_CFR0_PRECHARGE_1364US  (0x00E0)
+#define TSC2005_CFR0_STABTIME_0US      (0x0000)
+#define TSC2005_CFR0_STABTIME_100US    (0x0100)
+#define TSC2005_CFR0_STABTIME_500US    (0x0200)
+#define TSC2005_CFR0_STABTIME_1MS      (0x0300)
+#define TSC2005_CFR0_STABTIME_5MS      (0x0400)
+#define TSC2005_CFR0_STABTIME_100MS    (0x0700)
+#define TSC2005_CFR0_CLOCK_4MHZ                (0x0000)
+#define TSC2005_CFR0_CLOCK_2MHZ                (0x0800)
+#define TSC2005_CFR0_CLOCK_1MHZ                (0x1000)
+#define TSC2005_CFR0_RESOLUTION12      (0x2000)
+#define TSC2005_CFR0_STATUS            (0x4000)
+#define TSC2005_CFR0_PENMODE           (0x8000)
+
+#define TSC2005_CFR0_INITVALUE (TSC2005_CFR0_STABTIME_1MS  |   \
+                                TSC2005_CFR0_CLOCK_1MHZ    |   \
+                                TSC2005_CFR0_RESOLUTION12  |   \
+                                TSC2005_CFR0_PRECHARGE_276US | \
+                                TSC2005_CFR0_PENMODE)
+
+#define TSC2005_CFR1_BATCHDELAY_0MS    (0x0000)
+#define TSC2005_CFR1_BATCHDELAY_1MS    (0x0001)
+#define TSC2005_CFR1_BATCHDELAY_2MS    (0x0002)
+#define TSC2005_CFR1_BATCHDELAY_4MS    (0x0003)
+#define TSC2005_CFR1_BATCHDELAY_10MS   (0x0004)
+#define TSC2005_CFR1_BATCHDELAY_20MS   (0x0005)
+#define TSC2005_CFR1_BATCHDELAY_40MS   (0x0006)
+#define TSC2005_CFR1_BATCHDELAY_100MS  (0x0007)
+
+#define TSC2005_CFR1_INITVALUE (TSC2005_CFR1_BATCHDELAY_2MS)
+
+#define TSC2005_CFR2_MAVE_TEMP (0x0001)
+#define TSC2005_CFR2_MAVE_AUX  (0x0002)
+#define TSC2005_CFR2_MAVE_Z    (0x0004)
+#define TSC2005_CFR2_MAVE_Y    (0x0008)
+#define TSC2005_CFR2_MAVE_X    (0x0010)
+#define TSC2005_CFR2_AVG_1     (0x0000)
+#define TSC2005_CFR2_AVG_3     (0x0400)
+#define TSC2005_CFR2_AVG_7     (0x0800)
+#define TSC2005_CFR2_MEDIUM_1  (0x0000)
+#define TSC2005_CFR2_MEDIUM_3  (0x1000)
+#define TSC2005_CFR2_MEDIUM_7  (0x2000)
+#define TSC2005_CFR2_MEDIUM_15 (0x3000)
+
+#define TSC2005_CFR2_IRQ_DAV   (0x4000)
+#define TSC2005_CFR2_IRQ_PEN   (0x8000)
+#define TSC2005_CFR2_IRQ_PENDAV        (0x0000)
+
+#define TSC2005_CFR2_INITVALUE (TSC2005_CFR2_IRQ_DAV   |       \
+                                TSC2005_CFR2_MAVE_X    |       \
+                                TSC2005_CFR2_MAVE_Y    |       \
+                                TSC2005_CFR2_MAVE_Z    |       \
+                                TSC2005_CFR2_MEDIUM_15 |       \
+                                TSC2005_CFR2_AVG_7)
+
+#define MAX_12BIT                                      ((1 << 12) - 1)
+#define TS_SAMPLES                                     4
+#define TS_RECT_SIZE                                   8
+#define TSC2005_TS_PENUP_TIME                          20
+
+static const u32 tsc2005_read_reg[] = {
+       (TSC2005_REG | TSC2005_REG_X | TSC2005_REG_READ) << 16,
+       (TSC2005_REG | TSC2005_REG_Y | TSC2005_REG_READ) << 16,
+       (TSC2005_REG | TSC2005_REG_Z1 | TSC2005_REG_READ) << 16,
+       (TSC2005_REG | TSC2005_REG_Z2 | TSC2005_REG_READ) << 16,
+};
+#define NUM_READ_REGS  (sizeof(tsc2005_read_reg)/sizeof(tsc2005_read_reg[0]))
+
+struct tsc2005 {
+       struct spi_device       *spi;
+
+       struct input_dev        *idev;
+       char                    phys[32];
+       struct timer_list       penup_timer;
+       spinlock_t              lock;
+       struct mutex            mutex;
+
+       struct spi_message      read_msg;
+       struct spi_transfer     read_xfer[NUM_READ_REGS];
+       u32                     data[NUM_READ_REGS];
+
+       /* previous x,y,z */
+       int                     x;
+       int                     y;
+       int                     p;
+       /* average accumulators for each component */
+       int                     sample_cnt;
+       int                     avg_x;
+       int                     avg_y;
+       int                     avg_z1;
+       int                     avg_z2;
+       /* configuration */
+       int                     x_plate_ohm;
+       int                     hw_avg_max;
+       int                     stab_time;
+       int                     p_max;
+       int                     touch_pressure;
+       int                     irq;
+       s16                     dav_gpio;
+       /* status */
+       u8                      sample_sent;
+       u8                      pen_down;
+       u8                      disabled;
+       u8                      disable_depth;
+       u8                      spi_active;
+};
+
+static void tsc2005_cmd(struct tsc2005 *ts, u8 cmd)
+{
+       u16 data = TSC2005_CMD | TSC2005_CMD_12BIT | cmd;
+       struct spi_message msg;
+       struct spi_transfer xfer = { 0 };
+
+       xfer.tx_buf = &data;
+       xfer.rx_buf = NULL;
+       xfer.len = 1;
+       xfer.bits_per_word = 8;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfer, &msg);
+       spi_sync(ts->spi, &msg);
+}
+
+static void tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value)
+{
+       u32 tx;
+       struct spi_message msg;
+       struct spi_transfer xfer = { 0 };
+
+       tx = (TSC2005_REG | reg | TSC2005_REG_PND0 |
+              TSC2005_REG_WRITE) << 16;
+       tx |= value;
+
+       xfer.tx_buf = &tx;
+       xfer.rx_buf = NULL;
+       xfer.len = 4;
+       xfer.bits_per_word = 24;
+
+       spi_message_init(&msg);
+       spi_message_add_tail(&xfer, &msg);
+       spi_sync(ts->spi, &msg);
+}
+
+static void tsc2005_ts_update_pen_state(struct tsc2005 *ts,
+                                       int x, int y, int pressure)
+{
+       if (pressure) {
+               input_report_abs(ts->idev, ABS_X, x);
+               input_report_abs(ts->idev, ABS_Y, y);
+               input_report_abs(ts->idev, ABS_PRESSURE, pressure);
+               if (!ts->pen_down) {
+                       input_report_key(ts->idev, BTN_TOUCH, 1);
+                       ts->pen_down = 1;
+               }
+       } else {
+               input_report_abs(ts->idev, ABS_PRESSURE, 0);
+               if (ts->pen_down) {
+                       input_report_key(ts->idev, BTN_TOUCH, 0);
+                       ts->pen_down = 0;
+               }
+       }
+
+       input_sync(ts->idev);
+}
+
+/*
+ * This function is called by the SPI framework after the coordinates
+ * have been read from TSC2005
+ */
+static void tsc2005_ts_rx(void *arg)
+{
+       struct tsc2005 *ts = arg;
+       unsigned long flags;
+       int inside_rect, pressure_limit;
+       int x, y, z1, z2, pressure;
+
+       spin_lock_irqsave(&ts->lock, flags);
+
+       x = ts->data[0];
+       y = ts->data[1];
+       z1 = ts->data[2];
+       z2 = ts->data[3];
+
+       /* validate pressure and position */
+       if (x > MAX_12BIT || y > MAX_12BIT)
+               goto out;
+
+       /* skip coords if the pressure-components are out of range */
+       if (z1 < 100 || z2 > 4000)
+               goto out;
+
+       /* don't run average on the "pen down" event */
+       if (ts->sample_sent) {
+               ts->avg_x += x;
+               ts->avg_y += y;
+               ts->avg_z1 += z1;
+               ts->avg_z2 += z2;
+
+               if (++ts->sample_cnt < TS_SAMPLES)
+                       goto out;
+
+               x = ts->avg_x / TS_SAMPLES;
+               y = ts->avg_y / TS_SAMPLES;
+               z1 = ts->avg_z1 / TS_SAMPLES;
+               z2 = ts->avg_z2 / TS_SAMPLES;
+       }
+
+       ts->sample_cnt = 0;
+       ts->avg_x = 0;
+       ts->avg_y = 0;
+       ts->avg_z1 = 0;
+       ts->avg_z2 = 0;
+
+       if (z1) {
+               pressure = x * (z2 - z1) / z1;
+               pressure = pressure * ts->x_plate_ohm / 4096;
+       } else
+               goto out;
+
+       pressure_limit = ts->sample_sent? ts->p_max: ts->touch_pressure;
+       if (pressure > pressure_limit)
+               goto out;
+
+       /* discard the event if it still is within the previous rect - unless
+        * if the pressure is harder, but then use previous x,y position */
+       inside_rect = (ts->sample_sent &&
+               x > (int)ts->x - TS_RECT_SIZE &&
+               x < (int)ts->x + TS_RECT_SIZE &&
+               y > (int)ts->y - TS_RECT_SIZE &&
+               y < (int)ts->y + TS_RECT_SIZE);
+       if (inside_rect)
+               x = ts->x, y = ts->y;
+
+       if (!inside_rect || pressure < ts->p) {
+               tsc2005_ts_update_pen_state(ts, x, y, pressure);
+               ts->sample_sent = 1;
+               ts->x = x;
+               ts->y = y;
+               ts->p = pressure;
+       }
+out:
+       ts->spi_active = 0;
+       spin_unlock_irqrestore(&ts->lock, flags);
+
+       /* kick pen up timer - to make sure it expires again(!) */
+       if (ts->sample_sent)
+               mod_timer(&ts->penup_timer,
+                         jiffies + msecs_to_jiffies(TSC2005_TS_PENUP_TIME));
+}
+
+static void tsc2005_ts_penup_timer_handler(unsigned long data)
+{
+       struct tsc2005 *ts = (struct tsc2005 *)data;
+
+       if (ts->sample_sent) {
+               tsc2005_ts_update_pen_state(ts, 0, 0, 0);
+               ts->sample_sent = 0;
+       }
+}
+
+/*
+ * This interrupt is called when pen is down and coordinates are
+ * available. That is indicated by a falling edge on DAV line.
+ */
+static irqreturn_t tsc2005_ts_irq_handler(int irq, void *dev_id)
+{
+       struct tsc2005 *ts = dev_id;
+       int r;
+
+       if (ts->spi_active)
+               return IRQ_HANDLED;
+
+       ts->spi_active = 1;
+       r = spi_async(ts->spi, &ts->read_msg);
+       if (r)
+               dev_err(&ts->spi->dev, "ts: spi_async() failed");
+
+       /* kick pen up timer */
+       mod_timer(&ts->penup_timer,
+                 jiffies + msecs_to_jiffies(TSC2005_TS_PENUP_TIME));
+
+       return IRQ_HANDLED;
+}
+
+static void tsc2005_ts_setup_spi_xfer(struct tsc2005 *ts)
+{
+       struct spi_message *m = &ts->read_msg;
+       struct spi_transfer *x = &ts->read_xfer[0];
+       int i;
+
+       spi_message_init(m);
+
+       for (i = 0; i < NUM_READ_REGS; i++, x++) {
+               x->tx_buf = &tsc2005_read_reg[i];
+               x->rx_buf = &ts->data[i];
+               x->len = 4;
+               x->bits_per_word = 24;
+               x->cs_change = i < (NUM_READ_REGS - 1);
+               spi_message_add_tail(x, m);
+       }
+
+       m->complete = tsc2005_ts_rx;
+       m->context = ts;
+}
+
+static ssize_t tsc2005_ts_pen_down_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct tsc2005 *tsc = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", tsc->pen_down);
+}
+
+static DEVICE_ATTR(pen_down, S_IRUGO, tsc2005_ts_pen_down_show, NULL);
+
+static int tsc2005_configure(struct tsc2005 *tsc, int flags)
+{
+       tsc2005_write(tsc, TSC2005_REG_CFR0, TSC2005_CFR0_INITVALUE);
+       tsc2005_write(tsc, TSC2005_REG_CFR1, TSC2005_CFR1_INITVALUE);
+       tsc2005_write(tsc, TSC2005_REG_CFR2, TSC2005_CFR2_INITVALUE);
+       tsc2005_cmd(tsc, flags);
+
+       return 0;
+}
+
+static void tsc2005_start_scan(struct tsc2005 *tsc)
+{
+       tsc2005_configure(tsc, TSC2005_CMD_SCAN_XYZZ);
+}
+
+static void tsc2005_stop_scan(struct tsc2005 *tsc)
+{
+       tsc2005_cmd(tsc, TSC2005_CMD_STOP);
+}
+
+/* Must be called with mutex held */
+static void tsc2005_disable(struct tsc2005 *ts)
+{
+       if (ts->disable_depth++ != 0)
+               return;
+
+       disable_irq(ts->irq);
+
+       /* wait until penup timer expire normally */
+       do {
+               msleep(4);
+       } while (ts->sample_sent);
+
+       tsc2005_stop_scan(ts);
+}
+
+static void tsc2005_enable(struct tsc2005 *ts)
+{
+       if (--ts->disable_depth != 0)
+               return;
+
+       enable_irq(ts->irq);
+
+       tsc2005_start_scan(ts);
+}
+
+static ssize_t tsc2005_disable_show(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       struct tsc2005 *ts = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t tsc2005_disable_store(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       struct tsc2005          *tsc = dev_get_drvdata(dev);
+       unsigned long res;
+       int i;
+
+       i = strict_strtoul(buf, 10, &res);
+       i = i ? 1 : 0;
+
+       mutex_lock(&tsc->mutex);
+       if (i == tsc->disabled)
+               goto out;
+       tsc->disabled = i;
+
+       if (i)
+               tsc2005_disable(tsc);
+       else
+               tsc2005_enable(tsc);
+out:
+       mutex_unlock(&tsc->mutex);
+       return count;
+}
+
+static DEVICE_ATTR(disable_ts, 0664, tsc2005_disable_show,
+                  tsc2005_disable_store);
+
+
+static int __devinit tsc2005_ts_init(struct tsc2005 *ts,
+                                    struct tsc2005_platform_data *pdata)
+{
+       struct input_dev *idev;
+       int dav_gpio, r;
+       int x_max, y_max;
+       int x_fudge, y_fudge, p_fudge;
+
+       if (pdata->dav_gpio < 0) {
+               dev_err(&ts->spi->dev, "need DAV GPIO");
+               return -EINVAL;
+       }
+       dav_gpio = pdata->dav_gpio;
+       ts->dav_gpio = dav_gpio;
+       dev_dbg(&ts->spi->dev, "TSC2005: DAV GPIO = %d\n", dav_gpio);
+
+       r = gpio_request(dav_gpio, "TSC2005 dav");
+       if (r < 0) {
+               dev_err(&ts->spi->dev, "unable to get DAV GPIO");
+               goto err1;
+       }
+       gpio_direction_input(dav_gpio);
+       ts->irq = gpio_to_irq(dav_gpio);
+       dev_dbg(&ts->spi->dev, "TSC2005: DAV IRQ = %d\n", ts->irq);
+
+       init_timer(&ts->penup_timer);
+       setup_timer(&ts->penup_timer, tsc2005_ts_penup_timer_handler,
+                       (unsigned long)ts);
+
+       spin_lock_init(&ts->lock);
+       mutex_init(&ts->mutex);
+
+       ts->x_plate_ohm         = pdata->ts_x_plate_ohm ? : 280;
+       ts->hw_avg_max          = pdata->ts_hw_avg;
+       ts->stab_time           = pdata->ts_stab_time;
+       x_max                   = pdata->ts_x_max ? : 4096;
+       x_fudge                 = pdata->ts_x_fudge ? : 4;
+       y_max                   = pdata->ts_y_max ? : 4096;
+       y_fudge                 = pdata->ts_y_fudge ? : 8;
+       ts->p_max               = pdata->ts_pressure_max ? : MAX_12BIT;
+       ts->touch_pressure      = pdata->ts_touch_pressure ? : ts->p_max;
+       p_fudge                 = pdata->ts_pressure_fudge ? : 2;
+
+       idev = input_allocate_device();
+       if (idev == NULL) {
+               r = -ENOMEM;
+               goto err2;
+       }
+
+       idev->name = "TSC2005 touchscreen";
+       snprintf(ts->phys, sizeof(ts->phys), "%s/input-ts",
+                dev_name(&ts->spi->dev));
+       idev->phys = ts->phys;
+
+       idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+       idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+       ts->idev = idev;
+
+       tsc2005_ts_setup_spi_xfer(ts);
+
+       input_set_abs_params(idev, ABS_X, 0, x_max, x_fudge, 0);
+       input_set_abs_params(idev, ABS_Y, 0, y_max, y_fudge, 0);
+       input_set_abs_params(idev, ABS_PRESSURE, 0, ts->p_max, p_fudge, 0);
+
+       tsc2005_start_scan(ts);
+
+       r = request_irq(ts->irq, tsc2005_ts_irq_handler,
+                       IRQF_TRIGGER_FALLING | IRQF_DISABLED |
+                       IRQF_SAMPLE_RANDOM, "tsc2005", ts);
+       if (r < 0) {
+               dev_err(&ts->spi->dev, "unable to get DAV IRQ");
+               goto err3;
+       }
+
+       set_irq_wake(ts->irq, 1);
+
+       r = input_register_device(idev);
+       if (r < 0) {
+               dev_err(&ts->spi->dev, "can't register touchscreen device\n");
+               goto err4;
+       }
+
+       /* We can tolerate these failing */
+       if (device_create_file(&ts->spi->dev, &dev_attr_pen_down));
+       if (device_create_file(&ts->spi->dev, &dev_attr_disable_ts));
+
+       return 0;
+err4:
+       free_irq(ts->irq, ts);
+err3:
+       tsc2005_stop_scan(ts);
+       input_free_device(idev);
+err2:
+       gpio_free(dav_gpio);
+err1:
+       return r;
+}
+
+static int __devinit tsc2005_probe(struct spi_device *spi)
+{
+       struct tsc2005                  *tsc;
+       struct tsc2005_platform_data    *pdata = spi->dev.platform_data;
+       int r;
+
+       if (!pdata) {
+               dev_dbg(&spi->dev, "no platform data?\n");
+               return -ENODEV;
+       }
+
+       tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
+       if (tsc == NULL)
+               return -ENOMEM;
+
+       dev_set_drvdata(&spi->dev, tsc);
+       tsc->spi = spi;
+       spi->dev.power.power_state = PMSG_ON;
+
+       spi->mode = SPI_MODE_0;
+       spi->bits_per_word = 8;
+       /* The max speed might've been defined by the board-specific
+        * struct */
+       if (!spi->max_speed_hz)
+               spi->max_speed_hz = TSC2005_HZ;
+
+       spi_setup(spi);
+
+       r = tsc2005_ts_init(tsc, pdata);
+       if (r)
+               goto err1;
+
+       return 0;
+
+err1:
+       kfree(tsc);
+       return r;
+}
+
+static int __devexit tsc2005_remove(struct spi_device *spi)
+{
+       struct tsc2005 *ts = dev_get_drvdata(&spi->dev);
+
+       mutex_lock(&ts->mutex);
+       tsc2005_disable(ts);
+       mutex_unlock(&ts->mutex);
+
+       device_remove_file(&ts->spi->dev, &dev_attr_disable_ts);
+       device_remove_file(&ts->spi->dev, &dev_attr_pen_down);
+
+       free_irq(ts->irq, ts);
+       input_unregister_device(ts->idev);
+
+       gpio_free(ts->dav_gpio);
+       kfree(ts);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int tsc2005_suspend(struct spi_device *spi, pm_message_t mesg)
+{
+       struct tsc2005 *ts = dev_get_drvdata(&spi->dev);
+
+       mutex_lock(&ts->mutex);
+       tsc2005_disable(ts);
+       mutex_unlock(&ts->mutex);
+
+       return 0;
+}
+
+static int tsc2005_resume(struct spi_device *spi)
+{
+       struct tsc2005 *ts = dev_get_drvdata(&spi->dev);
+
+       mutex_lock(&ts->mutex);
+       tsc2005_enable(ts);
+       mutex_unlock(&ts->mutex);
+
+       return 0;
+}
+#endif
+
+static struct spi_driver tsc2005_driver = {
+       .driver = {
+               .name = "tsc2005",
+               .owner = THIS_MODULE,
+       },
+#ifdef CONFIG_PM
+       .suspend = tsc2005_suspend,
+       .resume = tsc2005_resume,
+#endif
+       .probe = tsc2005_probe,
+       .remove = __devexit_p(tsc2005_remove),
+};
+
+static int __init tsc2005_init(void)
+{
+       printk(KERN_INFO "TSC2005 driver initializing\n");
+
+       return spi_register_driver(&tsc2005_driver);
+}
+module_init(tsc2005_init);
+
+static void __exit tsc2005_exit(void)
+{
+       spi_unregister_driver(&tsc2005_driver);
+}
+module_exit(tsc2005_exit);
+
+MODULE_AUTHOR("Lauri Leukkunen <lauri.leukkunen@nokia.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:tsc2005");
diff --git a/drivers/input/touchscreen/tsc210x_ts.c b/drivers/input/touchscreen/tsc210x_ts.c
new file mode 100644 (file)
index 0000000..5828b6d
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * tsc210x_ts.c - touchscreen input device for TI TSC210x chips
+ *
+ * Copyright (c) 2006-2007 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This package 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 package 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 package; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <linux/spi/tsc210x.h>
+
+
+/*
+ * The sensor ADC on tsc210x chips is most often used with the smart
+ * touchscreen controller.   Those controllers can be made to improve
+ * sample quality directly by multi-sampling and by taking the mean or
+ * median of various numbers of samples.  They also take X, Y, and
+ * pressure measurements automatically, so this driver has relatively
+ * little to do.
+ *
+ * There are a few chips in this family that don't have quite the same
+ * touchscreen interface, e.g. no "median" mode.
+ */
+
+static void tsc210x_touch(void *context, int touching)
+{
+       struct input_dev *dev = context;
+
+       if (!touching) {
+               input_report_abs(dev, ABS_X, 0);
+               input_report_abs(dev, ABS_Y, 0);
+               input_report_abs(dev, ABS_PRESSURE, 0);
+               input_sync(dev);
+       }
+
+       input_report_key(dev, BTN_TOUCH, touching);
+}
+
+static void tsc210x_coords(void *context, int x, int y, int z1, int z2)
+{
+       struct input_dev *dev = context;
+       int p;
+
+       /* Calculate the touch resistance a la equation #1 */
+       if (z1 != 0)
+               p = x * (z2 - z1) / (z1 << 4);
+       else
+               p = 1;
+
+       input_report_abs(dev, ABS_X, x);
+       input_report_abs(dev, ABS_Y, y);
+       input_report_abs(dev, ABS_PRESSURE, p);
+       input_sync(dev);
+}
+
+static int tsc210x_ts_probe(struct platform_device *pdev)
+{
+       int status;
+       struct input_dev *dev;
+
+       dev = input_allocate_device();
+       if (!dev)
+               return -ENOMEM;
+
+       status = tsc210x_touch_cb(pdev->dev.parent, tsc210x_touch, dev);
+       if (status) {
+               input_free_device(dev);
+               return status;
+       }
+
+       status = tsc210x_coords_cb(pdev->dev.parent, tsc210x_coords, dev);
+       if (status) {
+               tsc210x_touch_cb(pdev->dev.parent, NULL, NULL);
+               input_free_device(dev);
+               return status;
+       }
+
+       dev->name = "TSC210x Touchscreen";
+       dev->dev.parent = &pdev->dev;
+       dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+       dev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
+       dev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+       dev->phys = "tsc210x/input0";
+       dev->id.bustype = BUS_HOST;
+       dev->id.vendor = 0x0001;
+       dev->id.product = 0x2100;
+       dev->id.version = 0x0001;
+
+       status = input_register_device(dev);
+       if (status) {
+               tsc210x_coords_cb(pdev->dev.parent, NULL, NULL);
+               tsc210x_touch_cb(pdev->dev.parent, NULL, NULL);
+               input_free_device(dev);
+               return status;
+       }
+
+       platform_set_drvdata(pdev, dev);
+       printk(KERN_INFO "TSC210x touchscreen initialised\n");
+       return 0;
+}
+
+static int __exit tsc210x_ts_remove(struct platform_device *pdev)
+{
+       struct input_dev *dev = platform_get_drvdata(pdev);
+
+       tsc210x_touch_cb(pdev->dev.parent, NULL, NULL);
+       tsc210x_coords_cb(pdev->dev.parent, NULL, NULL);
+       platform_set_drvdata(pdev, NULL);
+       input_unregister_device(dev);
+       input_free_device(dev);
+
+       return 0;
+}
+
+static struct platform_driver tsc210x_ts_driver = {
+       .probe          = tsc210x_ts_probe,
+       .remove         = __exit_p(tsc210x_ts_remove),
+       /* Nothing to do on suspend/resume */
+       .driver         = {
+               .name   = "tsc210x-ts",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init tsc210x_ts_init(void)
+{
+       /* can't use driver_probe() here since the parent device
+        * gets registered "late"
+        */
+       return platform_driver_register(&tsc210x_ts_driver);
+}
+module_init(tsc210x_ts_init);
+
+static void __exit tsc210x_ts_exit(void)
+{
+       platform_driver_unregister(&tsc210x_ts_driver);
+}
+module_exit(tsc210x_ts_exit);
+
+MODULE_AUTHOR("Andrzej Zaborowski");
+MODULE_DESCRIPTION("Touchscreen input driver for TI TSC2101/2102.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/tsc2301_ts.c b/drivers/input/touchscreen/tsc2301_ts.c
new file mode 100644 (file)
index 0000000..a157b48
--- /dev/null
@@ -0,0 +1,676 @@
+/*
+ * TSC2301 touchscreen driver
+ *
+ * Copyright (C) 2005-2008 Nokia Corporation
+ *
+ * Written by Jarkko Oikarinen, Imre Deak and Juha Yrjola
+ *
+ * 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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+
+#include <linux/spi/tsc2301.h>
+
+/**
+ * The touchscreen interface operates as follows:
+ *
+ * Initialize:
+ *    Request access to GPIO103 (DAV)
+ *    tsc2301_ts_irq_handler will trigger when DAV line goes down
+ *
+ *  1) Pen is pressed against touchscreeen
+ *  2) TSC2301 performs AD conversion
+ *  3) After the conversion is done TSC2301 drives DAV line down
+ *  4) GPIO IRQ is received and tsc2301_ts_irq_handler is called
+ *  5) tsc2301_ts_irq_handler queues up an spi transfer to fetch
+ *     the x, y, z1, z2 values
+ *  6) SPI framework calls tsc2301_ts_rx after the coordinates are read
+ *  7) When the penup_timer expires, there have not been DAV interrupts
+ *     during the last 20ms which means the pen has been lifted.
+ */
+
+
+#define TSC2301_TOUCHSCREEN_PRODUCT_ID                 0x0052
+#define TSC2301_TOUCHSCREEN_PRODUCT_VERSION            0x0001
+
+#define TSC2301_TS_PENUP_TIME                          20
+
+#define TSC2301_ADCREG_CONVERSION_CTRL_BY_TSC2301      0x8000
+#define TSC2301_ADCREG_CONVERSION_CTRL_BY_HOST         0x0000
+
+#define TSC2301_ADCREG_FUNCTION_NONE                   0x0000
+#define TSC2301_ADCREG_FUNCTION_XY                     0x0400
+#define TSC2301_ADCREG_FUNCTION_XYZ                    0x0800
+#define TSC2301_ADCREG_FUNCTION_X                      0x0C00
+#define TSC2301_ADCREG_FUNCTION_Y                      0x1000
+#define TSC2301_ADCREG_FUNCTION_Z                      0x1400
+#define TSC2301_ADCREG_FUNCTION_DAT1                   0x1800
+#define TSC2301_ADCREG_FUNCTION_DAT2                   0x1C00
+#define TSC2301_ADCREG_FUNCTION_AUX1                   0x2000
+#define TSC2301_ADCREG_FUNCTION_AUX2                   0x2400
+#define TSC2301_ADCREG_FUNCTION_TEMP                   0x2800
+
+#define TSC2301_ADCREG_RESOLUTION_8BIT                 0x0100
+#define TSC2301_ADCREG_RESOLUTION_10BIT                        0x0200
+#define TSC2301_ADCREG_RESOLUTION_12BIT                        0x0300
+
+#define TSC2301_ADCREG_AVERAGING_NONE                  0x0000
+#define TSC2301_ADCREG_AVERAGING_4AVG                  0x0040
+#define TSC2301_ADCREG_AVERAGING_8AVG                  0x0080
+#define TSC2301_ADCREG_AVERAGING_16AVG                 0x00C0
+
+#define TSC2301_ADCREG_CLOCK_8MHZ                      0x0000
+#define TSC2301_ADCREG_CLOCK_4MHZ                      0x0010
+#define TSC2301_ADCREG_CLOCK_2MHZ                      0x0020
+#define TSC2301_ADCREG_CLOCK_1MHZ                      0x0030
+
+#define TSC2301_ADCREG_VOLTAGE_STAB_0US                        0x0000
+#define TSC2301_ADCREG_VOLTAGE_STAB_100US              0x0002
+#define TSC2301_ADCREG_VOLTAGE_STAB_500US              0x0004
+#define TSC2301_ADCREG_VOLTAGE_STAB_1MS                        0x0006
+#define TSC2301_ADCREG_VOLTAGE_STAB_5MS                        0x0008
+#define TSC2301_ADCREG_VOLTAGE_STAB_10MS               0x000A
+#define TSC2301_ADCREG_VOLTAGE_STAB_50MS               0x000C
+#define TSC2301_ADCREG_VOLTAGE_STAB_100MS              0x000E
+
+#define TSC2301_ADCREG_STOP_CONVERSION                 0x4000
+
+#define MAX_12BIT                                      ((1 << 12) - 1)
+
+#define TS_RECT_SIZE                                   8
+#define TSF_MIN_Z1                                     100
+#define TSF_MAX_Z2                                     4000
+
+#define TSF_SAMPLES                                    4
+
+struct ts_filter {
+       int                     sample_cnt;
+
+       int                     avg_x;
+       int                     avg_y;
+       int                     avg_z1;
+       int                     avg_z2;
+};
+
+struct ts_coords {
+       u16                     x;
+       u16                     y;
+       u16                     z1;
+       u16                     z2;
+};
+
+struct tsc2301_ts {
+       struct input_dev        *idev;
+       char                    phys[32];
+       struct timer_list       penup_timer;
+       struct mutex            mutex;
+
+       struct spi_transfer     read_xfer[2];
+       struct spi_message      read_msg;
+       struct ts_coords        *coords;
+
+       struct ts_filter        filter;
+
+       int                     hw_avg_max;
+       u16                     x;
+       u16                     y;
+       u16                     p;
+
+       u16                     x_plate_ohm;
+       int                     stab_time;
+       int                     max_pressure;
+       int                     touch_pressure;
+
+       u8                      event_sent;
+       u8                      pen_down;
+       u8                      disabled;
+       u8                      disable_depth;
+
+       int                     hw_flags;
+       int                     irq;
+};
+
+
+static const u16 tsc2301_ts_read_data = 0x8000 | TSC2301_REG_X;
+
+static int tsc2301_ts_check_config(struct tsc2301_ts *ts, int *hw_flags)
+{
+       int flags;
+
+       flags = 0;
+       switch (ts->hw_avg_max) {
+       case 0:
+               flags |= TSC2301_ADCREG_AVERAGING_NONE;
+               break;
+       case 4:
+               flags |= TSC2301_ADCREG_AVERAGING_4AVG;
+               break;
+       case 8:
+               flags |= TSC2301_ADCREG_AVERAGING_8AVG;
+               break;
+       case 16:
+               flags |= TSC2301_ADCREG_AVERAGING_16AVG;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (ts->stab_time) {
+       case 0:
+               flags |= TSC2301_ADCREG_VOLTAGE_STAB_0US;
+               break;
+       case 100:
+               flags |= TSC2301_ADCREG_VOLTAGE_STAB_100US;
+               break;
+       case 500:
+               flags |= TSC2301_ADCREG_VOLTAGE_STAB_500US;
+               break;
+       case 1000:
+               flags |= TSC2301_ADCREG_VOLTAGE_STAB_1MS;
+               break;
+       case 5000:
+               flags |= TSC2301_ADCREG_VOLTAGE_STAB_5MS;
+               break;
+       case 10000:
+               flags |= TSC2301_ADCREG_VOLTAGE_STAB_10MS;
+               break;
+       case 50000:
+               flags |= TSC2301_ADCREG_VOLTAGE_STAB_50MS;
+               break;
+       case 100000:
+               flags |= TSC2301_ADCREG_VOLTAGE_STAB_100MS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       *hw_flags = flags;
+       return 0;
+}
+
+/*
+ * This odd three-time initialization is to work around a bug in TSC2301.
+ * See TSC2301 errata for details.
+ */
+static int tsc2301_ts_configure(struct tsc2301 *tsc, int flags)
+{
+       struct spi_transfer xfer[5];
+       struct spi_transfer *x;
+       struct spi_message m;
+       int i;
+       u16 val1, val2, val3;
+       u16 data[10];
+
+       val1 = TSC2301_ADCREG_CONVERSION_CTRL_BY_HOST |
+               TSC2301_ADCREG_STOP_CONVERSION |
+               TSC2301_ADCREG_FUNCTION_NONE |
+               TSC2301_ADCREG_RESOLUTION_12BIT |
+               TSC2301_ADCREG_AVERAGING_NONE |
+               TSC2301_ADCREG_CLOCK_2MHZ |
+               TSC2301_ADCREG_VOLTAGE_STAB_100MS;
+
+       val2 = TSC2301_ADCREG_CONVERSION_CTRL_BY_HOST |
+               TSC2301_ADCREG_FUNCTION_XYZ |
+               TSC2301_ADCREG_RESOLUTION_12BIT |
+               TSC2301_ADCREG_AVERAGING_16AVG |
+               TSC2301_ADCREG_CLOCK_1MHZ |
+               TSC2301_ADCREG_VOLTAGE_STAB_100MS;
+
+       /* Averaging and voltage stabilization settings in flags */
+       val3 = TSC2301_ADCREG_CONVERSION_CTRL_BY_TSC2301 |
+               TSC2301_ADCREG_FUNCTION_XYZ |
+               TSC2301_ADCREG_RESOLUTION_12BIT |
+               TSC2301_ADCREG_CLOCK_2MHZ |
+               flags;
+
+       /* Now we prepare the command for transferring */
+       data[0] = TSC2301_REG_ADC;
+       data[1] = val1;
+       data[2] = TSC2301_REG_ADC;
+       data[3] = val2;
+       data[4] = TSC2301_REG_ADC;
+       data[5] = val3;
+       data[6] = TSC2301_REG_REF;
+       data[7] = 1 << 4 | 1 << 2 | 1; /* intref, 100uS settl, 2.5V ref */
+       data[8] = TSC2301_REG_CONFIG;
+       data[9] = 3 << 3 | 2 << 0; /* 340uS pre-chrg, 544us delay */
+
+       spi_message_init(&m);
+       m.spi = tsc->spi;
+
+       memset(xfer, 0, sizeof(xfer));
+       x = &xfer[0];
+
+       for (i = 0; i < 10; i += 2) {
+               x->tx_buf = &data[i];
+               x->len = 4;
+               if (i != 8)
+                       x->cs_change = 1;
+               spi_message_add_tail(x, &m);
+               x++;
+       }
+       spi_sync(m.spi, &m);
+
+       return 0;
+}
+
+static void tsc2301_ts_start_scan(struct tsc2301 *tsc)
+{
+       tsc2301_ts_configure(tsc, tsc->ts->hw_flags);
+       tsc2301_kp_restart(tsc);
+}
+
+static void tsc2301_ts_stop_scan(struct tsc2301 *tsc)
+{
+       tsc2301_write_reg(tsc, TSC2301_REG_ADC, TSC2301_ADCREG_STOP_CONVERSION);
+       tsc2301_kp_restart(tsc);
+}
+
+static void update_pen_state(struct tsc2301_ts *ts, int x, int y, int pressure)
+{
+       if (pressure) {
+               input_report_abs(ts->idev, ABS_X, x);
+               input_report_abs(ts->idev, ABS_Y, y);
+               input_report_abs(ts->idev, ABS_PRESSURE, pressure);
+               if (!ts->pen_down)
+                       input_report_key(ts->idev, BTN_TOUCH, 1);
+               ts->pen_down = 1;
+       } else {
+               input_report_abs(ts->idev, ABS_PRESSURE, 0);
+               if (ts->pen_down)
+                       input_report_key(ts->idev, BTN_TOUCH, 0);
+               ts->pen_down = 0;
+       }
+
+       input_sync(ts->idev);
+
+#ifdef VERBOSE
+       dev_dbg(&tsc->spi->dev, "x %4d y %4d p %4d\n", x, y, pressure);
+#endif
+}
+
+static int filter(struct tsc2301_ts *ts, int x, int y, int z1, int z2)
+{
+       int inside_rect, pressure_limit, Rt;
+       struct ts_filter *tsf = &ts->filter;
+
+       /* validate pressure and position */
+       if (x > MAX_12BIT || y > MAX_12BIT)
+               return 0;
+
+       /* skip coords if the pressure-components are out of range */
+       if (z1 < TSF_MIN_Z1 || z2 > TSF_MAX_Z2)
+               return 0;
+
+       /* Use the x,y,z1,z2 directly on the first "pen down" event */
+       if (ts->event_sent) {
+               tsf->avg_x  += x;
+               tsf->avg_y  += y;
+               tsf->avg_z1 += z1;
+               tsf->avg_z2 += z2;
+
+               if (++tsf->sample_cnt < TSF_SAMPLES)
+                       return 0;
+               x = tsf->avg_x / TSF_SAMPLES;
+               y = tsf->avg_y / TSF_SAMPLES;
+               z1 = tsf->avg_z1 / TSF_SAMPLES;
+               z2 = tsf->avg_z2 / TSF_SAMPLES;
+       }
+       tsf->sample_cnt = 0;
+       tsf->avg_x  = 0;
+       tsf->avg_y  = 0;
+       tsf->avg_z1 = 0;
+       tsf->avg_z2 = 0;
+
+       pressure_limit = ts->event_sent? ts->max_pressure: ts->touch_pressure;
+
+       /* z1 is always at least 100: */
+       Rt = x * (z2 - z1) / z1;
+       Rt = Rt * ts->x_plate_ohm / 4096;
+       if (Rt > pressure_limit)
+               return 0;
+
+       /* discard the event if it still is within the previous rect - unless
+        * if the pressure is harder, but then use previous x,y position */
+       inside_rect = (
+           x > (int)ts->x - TS_RECT_SIZE && x < (int)ts->x + TS_RECT_SIZE &&
+           y > (int)ts->y - TS_RECT_SIZE && y < (int)ts->y + TS_RECT_SIZE);
+
+       if (!ts->event_sent || !inside_rect) {
+               ts->x = x;
+               ts->y = y;
+               ts->p = Rt;
+               return 1;
+       } else if (Rt < ts->p) {
+               ts->p = Rt;
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * This procedure is called by the SPI framework after the coordinates
+ * have been read from TSC2301
+ */
+static void tsc2301_ts_rx(void *arg)
+{
+       struct tsc2301 *tsc = arg;
+       struct tsc2301_ts *ts = tsc->ts;
+       int send_event;
+       int x, y, z1, z2;
+
+       x  = ts->coords->x;
+       y  = ts->coords->y;
+       z1 = ts->coords->z1;
+       z2 = ts->coords->z2;
+
+       send_event = filter(ts, x, y, z1, z2);
+       if (send_event) {
+               update_pen_state(ts, ts->x, ts->y, ts->p);
+               ts->event_sent = 1;
+       }
+
+       mod_timer(&ts->penup_timer,
+                 jiffies + msecs_to_jiffies(TSC2301_TS_PENUP_TIME));
+}
+
+/*
+ * Timer is called TSC2301_TS_PENUP_TIME after pen is up
+ */
+static void tsc2301_ts_timer_handler(unsigned long data)
+{
+       struct tsc2301 *tsc = (struct tsc2301 *)data;
+       struct tsc2301_ts *ts = tsc->ts;
+
+       if (ts->event_sent) {
+               ts->event_sent = 0;
+               update_pen_state(ts, 0, 0, 0);
+       }
+}
+
+/*
+ * This interrupt is called when pen is down and coordinates are
+ * available. That is indicated by a falling edge on DEV line.
+ */
+static irqreturn_t tsc2301_ts_irq_handler(int irq, void *dev_id)
+{
+       struct tsc2301 *tsc = dev_id;
+       struct tsc2301_ts *ts = tsc->ts;
+       int r;
+
+       r = spi_async(tsc->spi, &ts->read_msg);
+       if (r)
+               dev_err(&tsc->spi->dev, "ts: spi_async() failed");
+
+       mod_timer(&ts->penup_timer,
+                 jiffies + msecs_to_jiffies(TSC2301_TS_PENUP_TIME));
+
+       return IRQ_HANDLED;
+}
+
+static void tsc2301_ts_disable(struct tsc2301 *tsc)
+{
+       struct tsc2301_ts *ts = tsc->ts;
+
+       if (ts->disable_depth++ != 0)
+               return;
+
+       disable_irq(ts->irq);
+
+       /* wait until penup timer expire normally */
+       do {
+               msleep(1);
+       } while (ts->event_sent);
+
+       tsc2301_ts_stop_scan(tsc);
+}
+
+static void tsc2301_ts_enable(struct tsc2301 *tsc)
+{
+       struct tsc2301_ts *ts = tsc->ts;
+
+       if (--ts->disable_depth != 0)
+               return;
+
+       enable_irq(ts->irq);
+
+       tsc2301_ts_start_scan(tsc);
+}
+
+#ifdef CONFIG_PM
+int tsc2301_ts_suspend(struct tsc2301 *tsc)
+{
+       struct tsc2301_ts *ts = tsc->ts;
+
+       mutex_lock(&ts->mutex);
+       tsc2301_ts_disable(tsc);
+       mutex_unlock(&ts->mutex);
+
+       return 0;
+}
+
+void tsc2301_ts_resume(struct tsc2301 *tsc)
+{
+       struct tsc2301_ts *ts = tsc->ts;
+
+       mutex_lock(&ts->mutex);
+       tsc2301_ts_enable(tsc);
+       mutex_unlock(&ts->mutex);
+}
+#endif
+
+static void tsc2301_ts_setup_spi_xfer(struct tsc2301 *tsc)
+{
+       struct tsc2301_ts *ts = tsc->ts;
+       struct spi_message *m = &ts->read_msg;
+       struct spi_transfer *x = &ts->read_xfer[0];
+
+       spi_message_init(m);
+
+       x->tx_buf = &tsc2301_ts_read_data;
+       x->len = 2;
+       spi_message_add_tail(x, m);
+
+       x++;
+       x->rx_buf = ts->coords;
+       x->len = 8;
+       spi_message_add_tail(x, m);
+
+       m->complete = tsc2301_ts_rx;
+       m->context = tsc;
+}
+
+static ssize_t tsc2301_ts_pen_down_show(struct device *dev,
+                                       struct device_attribute *attr,
+                                       char *buf)
+{
+       struct tsc2301 *tsc = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%u\n", tsc->ts->pen_down);
+}
+
+static DEVICE_ATTR(pen_down, S_IRUGO, tsc2301_ts_pen_down_show, NULL);
+
+static ssize_t tsc2301_ts_disable_show(struct device *dev,
+                                      struct device_attribute *attr, char *buf)
+{
+       struct tsc2301          *tsc = dev_get_drvdata(dev);
+       struct tsc2301_ts       *ts = tsc->ts;
+
+       return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t tsc2301_ts_disable_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t count)
+{
+       struct tsc2301          *tsc = dev_get_drvdata(dev);
+       struct tsc2301_ts       *ts = tsc->ts;
+       char *endp;
+       int i;
+
+       i = simple_strtoul(buf, &endp, 10);
+       i = i ? 1 : 0;
+       mutex_lock(&ts->mutex);
+       if (i == ts->disabled) goto out;
+       ts->disabled = i;
+
+       if (i)
+               tsc2301_ts_disable(tsc);
+       else
+               tsc2301_ts_enable(tsc);
+out:
+       mutex_unlock(&ts->mutex);
+       return count;
+}
+
+static DEVICE_ATTR(disable_ts, 0664, tsc2301_ts_disable_show,
+                  tsc2301_ts_disable_store);
+
+int __devinit tsc2301_ts_init(struct tsc2301 *tsc,
+                             struct tsc2301_platform_data *pdata)
+{
+       struct tsc2301_ts *ts;
+       struct input_dev *idev;
+       int r;
+       int x_max, y_max;
+       int x_fudge, y_fudge, p_fudge;
+
+       if (pdata->dav_int <= 0) {
+               dev_err(&tsc->spi->dev, "need DAV IRQ");
+               return -EINVAL;
+       }
+
+       ts = kzalloc(sizeof(*ts), GFP_KERNEL);
+       if (ts == NULL)
+               return -ENOMEM;
+       tsc->ts = ts;
+
+       ts->coords = kzalloc(sizeof(*ts->coords), GFP_KERNEL);
+       if (ts->coords == NULL) {
+               kfree(ts);
+               return -ENOMEM;
+       }
+
+       ts->irq = pdata->dav_int;
+
+       init_timer(&ts->penup_timer);
+       setup_timer(&ts->penup_timer, tsc2301_ts_timer_handler,
+                       (unsigned long)tsc);
+
+       mutex_init(&ts->mutex);
+
+       ts->x_plate_ohm = pdata->ts_x_plate_ohm ? : 280;
+       ts->hw_avg_max  = pdata->ts_hw_avg;
+       ts->max_pressure = pdata->ts_max_pressure ? : MAX_12BIT;
+       ts->touch_pressure = pdata->ts_touch_pressure ? : ts->max_pressure;
+       ts->stab_time   = pdata->ts_stab_time;
+
+       x_max           = pdata->ts_x_max ? : 4096;
+       y_max           = pdata->ts_y_max ? : 4096;
+       x_fudge         = pdata->ts_x_fudge ? : 4;
+       y_fudge         = pdata->ts_y_fudge ? : 8;
+       p_fudge         = pdata->ts_pressure_fudge ? : 2;
+
+       if ((r = tsc2301_ts_check_config(ts, &ts->hw_flags))) {
+               dev_err(&tsc->spi->dev, "invalid configuration\n");
+               goto err2;
+       }
+
+       idev = input_allocate_device();
+       if (idev == NULL) {
+               r = -ENOMEM;
+               goto err2;
+       }
+       idev->name = "TSC2301 touchscreen";
+       snprintf(ts->phys, sizeof(ts->phys),
+                "%s/input-ts", dev_name(&tsc->spi->dev));
+       idev->phys = ts->phys;
+       idev->dev.parent = &tsc->spi->dev;
+
+       idev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
+       idev->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE);
+       ts->idev = idev;
+
+       tsc2301_ts_setup_spi_xfer(tsc);
+
+       /* These parameters should perhaps be configurable? */
+       input_set_abs_params(idev, ABS_X, 0, x_max, x_fudge, 0);
+       input_set_abs_params(idev, ABS_Y, 0, y_max, y_fudge, 0);
+       input_set_abs_params(idev, ABS_PRESSURE, 0, ts->max_pressure,
+                            p_fudge, 0);
+
+       tsc2301_ts_start_scan(tsc);
+
+       r = request_irq(ts->irq, tsc2301_ts_irq_handler,
+                       IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_FALLING,
+                       "tsc2301-ts", tsc);
+       if (r < 0) {
+               dev_err(&tsc->spi->dev, "unable to get DAV IRQ");
+               goto err3;
+       }
+       set_irq_wake(ts->irq, 1);
+
+       if (device_create_file(&tsc->spi->dev, &dev_attr_pen_down) < 0)
+               goto err4;
+       if (device_create_file(&tsc->spi->dev, &dev_attr_disable_ts) < 0)
+               goto err5;
+
+       r = input_register_device(idev);
+       if (r < 0) {
+               dev_err(&tsc->spi->dev, "can't register touchscreen device\n");
+               goto err6;
+       }
+
+       return 0;
+err6:
+       device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
+err5:
+       device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
+err4:
+       free_irq(ts->irq, tsc);
+err3:
+       tsc2301_ts_stop_scan(tsc);
+       input_free_device(idev);
+err2:
+       kfree(ts->coords);
+       kfree(ts);
+       return r;
+}
+
+void __devexit tsc2301_ts_exit(struct tsc2301 *tsc)
+{
+       struct tsc2301_ts *ts = tsc->ts;
+
+       tsc2301_ts_disable(tsc);
+
+       device_remove_file(&tsc->spi->dev, &dev_attr_disable_ts);
+       device_remove_file(&tsc->spi->dev, &dev_attr_pen_down);
+
+       free_irq(ts->irq, tsc);
+       input_unregister_device(ts->idev);
+
+       kfree(ts->coords);
+       kfree(ts);
+}
+MODULE_AUTHOR("Jarkko Oikarinen <jarkko.oikarinen@nokia.com>");
+MODULE_LICENSE("GPL");
index 9b60b6b684d9cd7c5a62c843d827e6071491a51f..788f48cf921a6c2cb319bdffd3ac4a5dae629e03 100644 (file)
@@ -70,11 +70,26 @@ config LEDS_WRAP
        help
          This option enables support for the PCEngines WRAP programmable LEDs.
 
        help
          This option enables support for the PCEngines WRAP programmable LEDs.
 
-config LEDS_ALIX2
-       tristate "LED Support for ALIX.2 and ALIX.3 series"
-       depends on LEDS_CLASS && X86 && EXPERIMENTAL
+config LEDS_OMAP_DEBUG
+       boolean "LED Support for OMAP debug board LEDs"
+       depends on LEDS_CLASS=y && ARCH_OMAP
        help
        help
-         This option enables support for the PCEngines ALIX.2 and ALIX.3 LEDs.
+         Enables support for the LEDs on the debug board used with OMAP
+         reference boards like H2/H3/H4 and Perseus2.  Up to six of these
+         may be claimed by the original ARM debug LED API.
+
+config LEDS_OMAP
+       tristate "LED Support for OMAP GPIO LEDs"
+       depends on LEDS_CLASS && ARCH_OMAP
+       help
+         This option enables support for the LEDs on OMAP processors.
+
+config LEDS_OMAP_PWM
+       tristate "LED Support for OMAP PWM-controlled LEDs"
+       depends on LEDS_CLASS && ARCH_OMAP && OMAP_DM_TIMER
+       help
+         This options enables support for LEDs connected to GPIO lines
+         controlled by a PWM timer on OMAP CPUs.
 
 config LEDS_H1940
        tristate "LED Support for iPAQ H1940 device"
 
 config LEDS_H1940
        tristate "LED Support for iPAQ H1940 device"
@@ -90,7 +105,7 @@ config LEDS_COBALT_QUBE
 
 config LEDS_COBALT_RAQ
        bool "LED Support for the Cobalt Raq series"
 
 config LEDS_COBALT_RAQ
        bool "LED Support for the Cobalt Raq series"
-       depends on LEDS_CLASS=y && MIPS_COBALT
+       depends on LEDS_CLASS && MIPS_COBALT
        select LEDS_TRIGGERS
        help
          This option enables support for the Cobalt Raq series LEDs.
        select LEDS_TRIGGERS
        help
          This option enables support for the Cobalt Raq series LEDs.
@@ -155,6 +170,16 @@ config LEDS_LP5521
          This driver can be built as a module by choosing 'M'. The module
          will be called leds-lp5521.
 
          This driver can be built as a module by choosing 'M'. The module
          will be called leds-lp5521.
 
+config LEDS_LP5521
+       tristate "LED Support for the LP5521 LEDs"
+       depends on LEDS_CLASS && I2C
+       help
+         If you say 'Y' here you get support for the National Semiconductor
+         LP5521 LED driver used in n8x0 boards.
+
+         This driver can be built as a module by choosing 'M'. The module
+         will be called leds-lp5521.
+
 config LEDS_CLEVO_MAIL
        tristate "Mail LED on Clevo notebook"
        depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI
 config LEDS_CLEVO_MAIL
        tristate "Mail LED on Clevo notebook"
        depends on LEDS_CLASS && X86 && SERIO_I8042 && DMI
@@ -193,13 +218,6 @@ config LEDS_PCA955X
          LED driver chips accessed via the I2C bus.  Supported
          devices include PCA9550, PCA9551, PCA9552, and PCA9553.
 
          LED driver chips accessed via the I2C bus.  Supported
          devices include PCA9550, PCA9551, PCA9552, and PCA9553.
 
-config LEDS_WM8350
-       tristate "LED Support for WM8350 AudioPlus PMIC"
-       depends on LEDS_CLASS && MFD_WM8350
-       help
-         This option enables support for LEDs driven by the Wolfson
-         Microelectronics WM8350 AudioPlus PMIC.
-
 config LEDS_DA903X
        tristate "LED Support for DA9030/DA9034 PMIC"
        depends on LEDS_CLASS && PMIC_DA903X
 config LEDS_DA903X
        tristate "LED Support for DA9030/DA9034 PMIC"
        depends on LEDS_CLASS && PMIC_DA903X
index 2d41c4dcf92ff9a8db515aac17aee9dc9482db96..ee79340b3fa827a5c0d594bf485df941d475a85e 100644 (file)
@@ -13,13 +13,15 @@ obj-$(CONFIG_LEDS_S3C24XX)          += leds-s3c24xx.o
 obj-$(CONFIG_LEDS_AMS_DELTA)           += leds-ams-delta.o
 obj-$(CONFIG_LEDS_NET48XX)             += leds-net48xx.o
 obj-$(CONFIG_LEDS_WRAP)                        += leds-wrap.o
 obj-$(CONFIG_LEDS_AMS_DELTA)           += leds-ams-delta.o
 obj-$(CONFIG_LEDS_NET48XX)             += leds-net48xx.o
 obj-$(CONFIG_LEDS_WRAP)                        += leds-wrap.o
-obj-$(CONFIG_LEDS_ALIX2)               += leds-alix2.o
+obj-$(CONFIG_LEDS_OMAP)                        += leds-omap.o
+obj-$(CONFIG_LEDS_OMAP_PWM)            += leds-omap-pwm.o
 obj-$(CONFIG_LEDS_H1940)               += leds-h1940.o
 obj-$(CONFIG_LEDS_COBALT_QUBE)         += leds-cobalt-qube.o
 obj-$(CONFIG_LEDS_COBALT_RAQ)          += leds-cobalt-raq.o
 obj-$(CONFIG_LEDS_SUNFIRE)             += leds-sunfire.o
 obj-$(CONFIG_LEDS_PCA9532)             += leds-pca9532.o
 obj-$(CONFIG_LEDS_GPIO)                        += leds-gpio.o
 obj-$(CONFIG_LEDS_H1940)               += leds-h1940.o
 obj-$(CONFIG_LEDS_COBALT_QUBE)         += leds-cobalt-qube.o
 obj-$(CONFIG_LEDS_COBALT_RAQ)          += leds-cobalt-raq.o
 obj-$(CONFIG_LEDS_SUNFIRE)             += leds-sunfire.o
 obj-$(CONFIG_LEDS_PCA9532)             += leds-pca9532.o
 obj-$(CONFIG_LEDS_GPIO)                        += leds-gpio.o
+obj-$(CONFIG_LEDS_LP5521)              += leds-lp5521.o
 obj-$(CONFIG_LEDS_CLEVO_MAIL)          += leds-clevo-mail.o
 obj-$(CONFIG_LEDS_HP6XX)               += leds-hp6xx.o
 obj-$(CONFIG_LEDS_FSG)                 += leds-fsg.o
 obj-$(CONFIG_LEDS_CLEVO_MAIL)          += leds-clevo-mail.o
 obj-$(CONFIG_LEDS_HP6XX)               += leds-hp6xx.o
 obj-$(CONFIG_LEDS_FSG)                 += leds-fsg.o
diff --git a/drivers/leds/leds-lp5521.c b/drivers/leds/leds-lp5521.c
new file mode 100644 (file)
index 0000000..3e1ca31
--- /dev/null
@@ -0,0 +1,724 @@
+/*
+ * lp5521.c - LP5521 LED Driver
+ *
+ * Copyright (C) 2007 Nokia Corporation
+ *
+ * Written by Mathias Nyman <mathias.nyman@nokia.com>
+ * Updated by Felipe Balbi <felipe.balbi@nokia.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
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/leds.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/i2c/lp5521.h>
+
+#define LP5521_DRIVER_NAME             "lp5521"
+
+#define LP5521_REG_R_PWM               0x02
+#define LP5521_REG_B_PWM               0x04
+#define LP5521_REG_ENABLE              0x00
+#define LP5521_REG_OP_MODE             0x01
+#define LP5521_REG_G_PWM               0x03
+#define LP5521_REG_R_CNTRL             0x05
+#define LP5521_REG_G_CNTRL             0x06
+#define LP5521_REG_B_CNTRL             0x07
+#define LP5521_REG_MISC                        0x08
+#define LP5521_REG_R_CHANNEL_PC                0x09
+#define LP5521_REG_G_CHANNEL_PC                0x0a
+#define LP5521_REG_B_CHANNEL_PC                0x0b
+#define LP5521_REG_STATUS              0x0c
+#define LP5521_REG_RESET               0x0d
+#define LP5521_REG_GPO                 0x0e
+#define LP5521_REG_R_PROG_MEM          0x10
+#define LP5521_REG_G_PROG_MEM          0x30
+#define LP5521_REG_B_PROG_MEM          0x50
+
+#define LP5521_CURRENT_1m5             0x0f
+#define LP5521_CURRENT_3m1             0x1f
+#define LP5521_CURRENT_4m7             0x2f
+#define LP5521_CURRENT_6m3             0x3f
+#define LP5521_CURRENT_7m9             0x4f
+#define LP5521_CURRENT_9m5             0x5f
+#define LP5521_CURRENT_11m1            0x6f
+#define LP5521_CURRENT_12m7            0x7f
+#define LP5521_CURRENT_14m3            0x8f
+#define LP5521_CURRENT_15m9            0x9f
+#define LP5521_CURRENT_17m5            0xaf
+#define LP5521_CURRENT_19m1            0xbf
+#define LP5521_CURRENT_20m7            0xcf
+#define LP5521_CURRENT_22m3            0xdf
+#define LP5521_CURRENT_23m9            0xef
+#define LP5521_CURRENT_25m5            0xff
+
+#define LP5521_PROGRAM_LENGTH          32      /* in bytes */
+
+struct lp5521_chip {
+       /* device lock */
+       struct mutex            lock;
+       struct i2c_client       *client;
+
+       struct work_struct      red_work;
+       struct work_struct      green_work;
+       struct work_struct      blue_work;
+
+       struct led_classdev     ledr;
+       struct led_classdev     ledg;
+       struct led_classdev     ledb;
+
+       enum lp5521_mode        mode;
+
+       int                     red;
+       int                     green;
+       int                     blue;
+};
+
+static int lp5521_set_mode(struct lp5521_chip *chip, enum lp5521_mode mode);
+
+static inline int lp5521_write(struct i2c_client *client, u8 reg, u8 value)
+{
+       return i2c_smbus_write_byte_data(client, reg, value);
+}
+
+static inline int lp5521_read(struct i2c_client *client, u8 reg)
+{
+       return i2c_smbus_read_byte_data(client, reg);
+}
+
+static int lp5521_configure(struct i2c_client *client)
+{
+       int ret = 0;
+
+       /* Enable chip and set light to logarithmic mode*/
+       ret |= lp5521_write(client, LP5521_REG_ENABLE, 0xc0);
+
+       /* setting all color pwms to direct control mode */
+       ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x3f);
+
+       /* setting current to 4.7 mA for all channels */
+       ret |= lp5521_write(client, LP5521_REG_R_CNTRL, LP5521_CURRENT_4m7);
+       ret |= lp5521_write(client, LP5521_REG_G_CNTRL, LP5521_CURRENT_4m7);
+       ret |= lp5521_write(client, LP5521_REG_B_CNTRL, LP5521_CURRENT_4m7);
+
+       /* Enable auto-powersave, set charge pump to auto, red to battery */
+       ret |= lp5521_write(client, LP5521_REG_MISC, 0x3c);
+
+       /* initialize all channels pwm to zero */
+       ret |= lp5521_write(client, LP5521_REG_R_PWM, 0);
+       ret |= lp5521_write(client, LP5521_REG_G_PWM, 0);
+       ret |= lp5521_write(client, LP5521_REG_B_PWM, 0);
+
+       /* Not much can be done about errors at this point */
+       return ret;
+}
+
+static int lp5521_load_program(struct lp5521_chip *chip, u8 *pattern)
+{
+       struct i2c_client *client = chip->client;
+       int ret = 0;
+
+       /* Enter load program mode for all led channels */
+       ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x15); /* 0001 0101 */
+       if (ret)
+               return ret;
+
+       if (chip->red)
+               ret |= i2c_smbus_write_i2c_block_data(client,
+                               LP5521_REG_R_PROG_MEM,
+                               LP5521_PROGRAM_LENGTH,
+                               pattern);
+       if (chip->green)
+               ret |= i2c_smbus_write_i2c_block_data(client,
+                               LP5521_REG_G_PROG_MEM,
+                               LP5521_PROGRAM_LENGTH,
+                               pattern);
+       if (chip->blue)
+               ret |= i2c_smbus_write_i2c_block_data(client,
+                               LP5521_REG_B_PROG_MEM,
+                               LP5521_PROGRAM_LENGTH,
+                               pattern);
+
+       return ret;
+}
+
+static int lp5521_run_program(struct lp5521_chip *chip)
+{
+       struct i2c_client *client = chip->client;
+       int reg;
+       u8 mask = 0xc0;
+       u8 exec_state = 0;
+
+       reg = lp5521_read(client, LP5521_REG_ENABLE);
+       if (reg < 0)
+               return reg;
+
+       reg &= mask;
+
+       /* set all active channels exec state to countinous run*/
+       exec_state |= (chip->red << 5);
+       exec_state |= (chip->green << 3);
+       exec_state |= (chip->blue << 1);
+
+       reg |= exec_state;
+
+       if (lp5521_write(client, LP5521_REG_ENABLE, reg))
+               dev_dbg(&client->dev, "failed writing to register %02x\n",
+                               LP5521_REG_ENABLE);
+
+       /* set op-mode to run for active channels, disabled for others */
+       if (lp5521_write(client, LP5521_REG_OP_MODE, exec_state))
+               dev_dbg(&client->dev, "failed writing to register %02x\n",
+                               LP5521_REG_OP_MODE);
+
+       return 0;
+}
+
+/*--------------------------------------------------------------*/
+/*                     Sysfs interface                         */
+/*--------------------------------------------------------------*/
+
+static ssize_t show_active_channels(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct lp5521_chip *chip = dev_get_drvdata(dev);
+       char channels[4];
+       int pos = 0;
+
+       if (chip->red)
+               pos += sprintf(channels + pos, "r");
+       if (chip->green)
+               pos += sprintf(channels + pos, "g");
+       if (chip->blue)
+               pos += sprintf(channels + pos, "b");
+
+       channels[pos] = '\0';
+
+       return sprintf(buf, "%s\n", channels);
+}
+
+static ssize_t store_active_channels(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t len)
+{
+       struct lp5521_chip *chip = dev_get_drvdata(dev);
+
+       chip->red = 0;
+       chip->green = 0;
+       chip->blue = 0;
+
+       if (strchr(buf, 'r') != NULL)
+               chip->red = 1;
+       if (strchr(buf, 'b') != NULL)
+               chip->blue = 1;
+       if (strchr(buf, 'g') != NULL)
+               chip->green = 1;
+
+       return len;
+}
+
+static ssize_t show_color(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       int r, g, b;
+
+       r = lp5521_read(client, LP5521_REG_R_PWM);
+       g = lp5521_read(client, LP5521_REG_G_PWM);
+       b = lp5521_read(client, LP5521_REG_B_PWM);
+
+       if (r < 0 || g < 0 || b < 0)
+               return -EINVAL;
+
+       return sprintf(buf, "%.2x:%.2x:%.2x\n", r, g, b);
+}
+
+static ssize_t store_color(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t len)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct lp5521_chip *chip = i2c_get_clientdata(client);
+       int ret;
+       unsigned r, g, b;
+
+
+       ret = sscanf(buf, "%2x:%2x:%2x", &r, &g, &b);
+       if (ret != 3)
+               return  -EINVAL;
+
+       mutex_lock(&chip->lock);
+
+       ret = lp5521_write(client, LP5521_REG_R_PWM, (u8)r);
+       ret = lp5521_write(client, LP5521_REG_G_PWM, (u8)g);
+       ret = lp5521_write(client, LP5521_REG_B_PWM, (u8)b);
+
+       mutex_unlock(&chip->lock);
+
+       return len;
+}
+
+static ssize_t store_load(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t len)
+{
+       struct lp5521_chip *chip = dev_get_drvdata(dev);
+       int  ret, nrchars, offset = 0, i = 0;
+       char c[3];
+       unsigned cmd;
+       u8 pattern[LP5521_PROGRAM_LENGTH] = {0};
+
+       while ((offset < len - 1) && (i < LP5521_PROGRAM_LENGTH)) {
+
+               /* separate sscanfs because length is working only for %s */
+               ret = sscanf(buf + offset, "%2s%n ", c, &nrchars);
+               ret = sscanf(c, "%2x", &cmd);
+               if (ret != 1)
+                       goto fail;
+               pattern[i] = (u8)cmd;
+
+               offset += nrchars;
+               i++;
+       }
+
+       /* pattern commands are always two bytes long */
+       if (i % 2)
+               goto fail;
+
+       mutex_lock(&chip->lock);
+
+       ret = lp5521_load_program(chip, pattern);
+       mutex_unlock(&chip->lock);
+
+       if (ret) {
+               dev_err(dev, "lp5521 failed loading pattern\n");
+               return ret;
+       }
+
+       return len;
+fail:
+       dev_err(dev, "lp5521 wrong pattern format\n");
+       return -EINVAL;
+}
+
+static ssize_t show_mode(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct lp5521_chip *chip = dev_get_drvdata(dev);
+       char *mode;
+
+       mutex_lock(&chip->lock);
+       switch (chip->mode) {
+       case LP5521_MODE_RUN:
+               mode = "run";
+               break;
+       case LP5521_MODE_LOAD:
+               mode = "load";
+               break;
+       case LP5521_MODE_DIRECT_CONTROL:
+               mode = "direct";
+               break;
+       default:
+               mode = "undefined";
+       }
+       mutex_unlock(&chip->lock);
+
+       return sprintf(buf, "%s\n", mode);
+}
+
+static ssize_t store_mode(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t len)
+{
+       struct lp5521_chip *chip = dev_get_drvdata(dev);
+
+       mutex_lock(&chip->lock);
+
+       if (sysfs_streq(buf, "run"))
+               lp5521_set_mode(chip, LP5521_MODE_RUN);
+       else if (sysfs_streq(buf, "load"))
+               lp5521_set_mode(chip, LP5521_MODE_LOAD);
+       else if (sysfs_streq(buf, "direct"))
+               lp5521_set_mode(chip, LP5521_MODE_DIRECT_CONTROL);
+       else
+               len = -EINVAL;
+
+       mutex_unlock(&chip->lock);
+
+       return len;
+}
+
+static ssize_t show_current(struct device *dev,
+               struct device_attribute *attr,
+               char *buf)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       int r, g, b;
+
+       r = lp5521_read(client, LP5521_REG_R_CNTRL);
+       g = lp5521_read(client, LP5521_REG_G_CNTRL);
+       b = lp5521_read(client, LP5521_REG_B_CNTRL);
+
+       if (r < 0 || g < 0 || b < 0)
+               return -EINVAL;
+
+       r >>= 4;
+       g >>= 4;
+       b >>= 4;
+
+       return sprintf(buf, "%x %x %x\n", r, g, b);
+}
+
+static ssize_t store_current(struct device *dev,
+               struct device_attribute *attr,
+               const char *buf, size_t len)
+{
+       struct lp5521_chip *chip = dev_get_drvdata(dev);
+       struct i2c_client *client = chip->client;
+       int ret;
+       unsigned curr;
+
+       ret = sscanf(buf, "%1x", &curr);
+       if (ret != 1)
+               return  -EINVAL;
+
+       /* current level is determined by the 4 upper bits, rest is ones */
+       curr = (curr << 4) | 0x0f;
+
+       mutex_lock(&chip->lock);
+
+       ret |= lp5521_write(client, LP5521_REG_R_CNTRL, (u8)curr);
+       ret |= lp5521_write(client, LP5521_REG_G_CNTRL, (u8)curr);
+       ret |= lp5521_write(client, LP5521_REG_B_CNTRL, (u8)curr);
+
+       mutex_unlock(&chip->lock);
+
+       return len;
+}
+
+static DEVICE_ATTR(color, S_IRUGO | S_IWUGO, show_color, store_color);
+static DEVICE_ATTR(load, S_IWUGO, NULL, store_load);
+static DEVICE_ATTR(mode, S_IRUGO | S_IWUGO, show_mode, store_mode);
+static DEVICE_ATTR(active_channels, S_IRUGO | S_IWUGO,
+               show_active_channels, store_active_channels);
+static DEVICE_ATTR(led_current, S_IRUGO | S_IWUGO, show_current, store_current);
+
+static int lp5521_register_sysfs(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+       int ret;
+
+       ret = device_create_file(dev, &dev_attr_color);
+       if (ret)
+               goto fail1;
+       ret = device_create_file(dev, &dev_attr_load);
+       if (ret)
+               goto fail2;
+       ret = device_create_file(dev, &dev_attr_active_channels);
+       if (ret)
+               goto fail3;
+       ret = device_create_file(dev, &dev_attr_mode);
+       if (ret)
+               goto fail4;
+       ret = device_create_file(dev, &dev_attr_led_current);
+       if (ret)
+               goto fail5;
+
+       return 0;
+
+fail5:
+       device_remove_file(dev, &dev_attr_mode);
+fail4:
+       device_remove_file(dev, &dev_attr_active_channels);
+fail3:
+       device_remove_file(dev, &dev_attr_load);
+fail2:
+       device_remove_file(dev, &dev_attr_color);
+fail1:
+       return ret;
+}
+
+static void lp5521_unregister_sysfs(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+
+       device_remove_file(dev, &dev_attr_led_current);
+       device_remove_file(dev, &dev_attr_mode);
+       device_remove_file(dev, &dev_attr_active_channels);
+       device_remove_file(dev, &dev_attr_color);
+       device_remove_file(dev, &dev_attr_load);
+}
+
+/*--------------------------------------------------------------*/
+/*                     Set chip operating mode                 */
+/*--------------------------------------------------------------*/
+
+static int lp5521_set_mode(struct lp5521_chip *chip, enum lp5521_mode mode)
+{
+       struct i2c_client *client = chip->client ;
+       int ret = 0;
+
+       /* if in that mode already do nothing, except for run */
+       if (chip->mode == mode && mode != LP5521_MODE_RUN)
+               return 0;
+
+       switch (mode) {
+       case LP5521_MODE_RUN:
+               ret = lp5521_run_program(chip);
+               break;
+       case LP5521_MODE_LOAD:
+               ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x15);
+               break;
+       case LP5521_MODE_DIRECT_CONTROL:
+               ret |= lp5521_write(client, LP5521_REG_OP_MODE, 0x3F);
+               break;
+       default:
+               dev_dbg(&client->dev, "unsupported mode %d\n", mode);
+       }
+
+       chip->mode = mode;
+
+       return ret;
+}
+
+static void lp5521_red_work(struct work_struct *work)
+{
+       struct lp5521_chip *chip = container_of(work, struct lp5521_chip, red_work);
+       int ret;
+
+       ret = lp5521_configure(chip->client);
+       if (ret) {
+               dev_dbg(&chip->client->dev, "could not configure lp5521, %d\n",
+                               ret);
+               return;
+       }
+
+       ret = lp5521_write(chip->client, LP5521_REG_R_PWM, chip->red);
+       if (ret)
+               dev_dbg(&chip->client->dev, "could not set brightness, %d\n",
+                               ret);
+}
+
+static void lp5521_red_set(struct led_classdev *led,
+               enum led_brightness value)
+{
+       struct lp5521_chip *chip = container_of(led, struct lp5521_chip, ledr);
+
+       chip->red = value;
+       schedule_work(&chip->red_work);
+}
+
+static void lp5521_green_work(struct work_struct *work)
+{
+       struct lp5521_chip *chip = container_of(work, struct lp5521_chip, green_work);
+       int ret;
+
+       ret = lp5521_configure(chip->client);
+       if (ret) {
+               dev_dbg(&chip->client->dev, "could not configure lp5521, %d\n",
+                               ret);
+               return;
+       }
+
+       ret = lp5521_write(chip->client, LP5521_REG_G_PWM, chip->green);
+       if (ret)
+               dev_dbg(&chip->client->dev, "could not set brightness, %d\n",
+                               ret);
+}
+
+static void lp5521_green_set(struct led_classdev *led,
+               enum led_brightness value)
+{
+       struct lp5521_chip *chip = container_of(led, struct lp5521_chip, ledg);
+
+       chip->green = value;
+       schedule_work(&chip->green_work);
+}
+
+static void lp5521_blue_work(struct work_struct *work)
+{
+       struct lp5521_chip *chip = container_of(work, struct lp5521_chip, blue_work);
+       int ret;
+
+       ret = lp5521_configure(chip->client);
+       if (ret) {
+               dev_dbg(&chip->client->dev, "could not configure lp5521, %d\n",
+                               ret);
+               return;
+       }
+
+       ret = lp5521_write(chip->client, LP5521_REG_B_PWM, chip->blue);
+       if (ret)
+               dev_dbg(&chip->client->dev, "could not set brightness, %d\n",
+                               ret);
+}
+
+static void lp5521_blue_set(struct led_classdev *led,
+               enum led_brightness value)
+{
+       struct lp5521_chip *chip = container_of(led, struct lp5521_chip, ledb);
+
+       chip->blue = value;
+       schedule_work(&chip->blue_work);
+}
+
+/*--------------------------------------------------------------*/
+/*                     Probe, Attach, Remove                   */
+/*--------------------------------------------------------------*/
+
+static int __init lp5521_probe(struct i2c_client *client,
+               const struct i2c_device_id *id)
+{
+       struct lp5521_platform_data *pdata = client->dev.platform_data;
+       struct lp5521_chip *chip;
+       char name[16];
+       int ret = 0;
+
+       if (!pdata) {
+               dev_err(&client->dev, "platform_data is missing\n");
+               return -EINVAL;
+       }
+
+       chip = kzalloc(sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
+
+       chip->client = client;
+       strncpy(client->name, LP5521_DRIVER_NAME, I2C_NAME_SIZE);
+       i2c_set_clientdata(client, chip);
+
+       mutex_init(&chip->lock);
+
+       INIT_WORK(&chip->red_work, lp5521_red_work);
+       INIT_WORK(&chip->green_work, lp5521_green_work);
+       INIT_WORK(&chip->blue_work, lp5521_blue_work);
+
+       ret = lp5521_configure(client);
+       if (ret < 0) {
+               dev_err(&client->dev, "lp5521 error configuring chip \n");
+               goto fail1;
+       }
+
+       /* Set default values */
+       chip->mode      = pdata->mode;
+       chip->red       = pdata->red_present;
+       chip->green     = pdata->green_present;
+       chip->blue      = pdata->blue_present;
+
+       chip->ledr.brightness_set = lp5521_red_set;
+       chip->ledr.default_trigger = NULL;
+       snprintf(name, sizeof(name), "%s::red", pdata->label);
+       chip->ledr.name = name;
+       ret = led_classdev_register(&client->dev, &chip->ledr);
+       if (ret < 0) {
+               dev_dbg(&client->dev, "failed to register led %s, %d\n",
+                               chip->ledb.name, ret);
+               goto fail1;
+       }
+
+       chip->ledg.brightness_set = lp5521_green_set;
+       chip->ledg.default_trigger = NULL;
+       snprintf(name, sizeof(name), "%s::green", pdata->label);
+       chip->ledg.name = name;
+       ret = led_classdev_register(&client->dev, &chip->ledg);
+       if (ret < 0) {
+               dev_dbg(&client->dev, "failed to register led %s, %d\n",
+                               chip->ledb.name, ret);
+               goto fail2;
+       }
+
+       chip->ledb.brightness_set = lp5521_blue_set;
+       chip->ledb.default_trigger = NULL;
+       snprintf(name, sizeof(name), "%s::blue", pdata->label);
+       chip->ledb.name = name;
+       ret = led_classdev_register(&client->dev, &chip->ledb);
+       if (ret < 0) {
+               dev_dbg(&client->dev, "failed to register led %s, %d\n", chip->ledb.name, ret);
+               goto fail3;
+       }
+
+       ret = lp5521_register_sysfs(client);
+       if (ret) {
+               dev_err(&client->dev, "lp5521 registering sysfs failed \n");
+               goto fail4;
+       }
+
+       return 0;
+
+fail4:
+       led_classdev_unregister(&chip->ledb);
+fail3:
+       led_classdev_unregister(&chip->ledg);
+fail2:
+       led_classdev_unregister(&chip->ledr);
+fail1:
+       i2c_set_clientdata(client, NULL);
+       kfree(chip);
+
+       return ret;
+}
+
+static int __exit lp5521_remove(struct i2c_client *client)
+{
+       struct lp5521_chip *chip = i2c_get_clientdata(client);
+
+       lp5521_unregister_sysfs(client);
+       i2c_set_clientdata(client, NULL);
+
+       led_classdev_unregister(&chip->ledb);
+       led_classdev_unregister(&chip->ledg);
+       led_classdev_unregister(&chip->ledr);
+
+       kfree(chip);
+
+       return 0;
+}
+
+static const struct i2c_device_id lp5521_id[] = {
+       { LP5521_DRIVER_NAME, 0},
+       { },
+};
+MODULE_DEVICE_TABLE(i2c, lp5521_id);
+
+static struct i2c_driver lp5521_driver = {
+       .driver         = {
+               .name   = LP5521_DRIVER_NAME,
+       },
+       .probe          = lp5521_probe,
+       .remove         = __exit_p(lp5521_remove),
+       .id_table       = lp5521_id,
+};
+
+static int __init lp5521_init(void)
+{
+       return i2c_add_driver(&lp5521_driver);
+}
+module_init(lp5521_init);
+
+static void __exit lp5521_exit(void)
+{
+       i2c_del_driver(&lp5521_driver);
+}
+module_exit(lp5521_exit);
+
+MODULE_AUTHOR("Mathias Nyman <mathias.nyman@nokia.com>");
+MODULE_DESCRIPTION("lp5521 LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-omap-pwm.c b/drivers/leds/leds-omap-pwm.c
new file mode 100644 (file)
index 0000000..57eb383
--- /dev/null
@@ -0,0 +1,376 @@
+/* drivers/leds/leds-omap_pwm.c
+ *
+ * Driver to blink LEDs using OMAP PWM timers
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Timo Teras
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/ctype.h>
+#include <linux/sched.h>
+#include <asm/delay.h>
+#include <mach/board.h>
+#include <mach/dmtimer.h>
+
+struct omap_pwm_led {
+       struct led_classdev cdev;
+       struct work_struct work;
+       struct omap_pwm_led_platform_data *pdata;
+       struct omap_dm_timer *intensity_timer;
+       struct omap_dm_timer *blink_timer;
+       int powered;
+       unsigned int on_period, off_period;
+       enum led_brightness brightness;
+};
+
+static inline struct omap_pwm_led *pdev_to_omap_pwm_led(struct platform_device *pdev)
+{
+       return platform_get_drvdata(pdev);
+}
+
+static inline struct omap_pwm_led *cdev_to_omap_pwm_led(struct led_classdev *led_cdev)
+{
+       return container_of(led_cdev, struct omap_pwm_led, cdev);
+}
+
+static inline struct omap_pwm_led *work_to_omap_pwm_led(struct work_struct *work)
+{
+       return container_of(work, struct omap_pwm_led, work);
+}
+
+static void omap_pwm_led_set_blink(struct omap_pwm_led *led)
+{
+       if (!led->powered)
+               return;
+
+       if (led->on_period != 0 && led->off_period != 0) {
+               unsigned long load_reg, cmp_reg;
+
+               load_reg = 32768 * (led->on_period + led->off_period) / 1000;
+               cmp_reg = 32768 * led->on_period / 1000;
+
+               omap_dm_timer_stop(led->blink_timer);
+               omap_dm_timer_set_load(led->blink_timer, 1, -load_reg);
+               omap_dm_timer_set_match(led->blink_timer, 1, -cmp_reg);
+               omap_dm_timer_set_pwm(led->blink_timer, 1, 1,
+                                     OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+               omap_dm_timer_write_counter(led->blink_timer, -2);
+               omap_dm_timer_start(led->blink_timer);
+       } else {
+               omap_dm_timer_set_pwm(led->blink_timer, 1, 1,
+                                     OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+               omap_dm_timer_stop(led->blink_timer);
+       }
+}
+
+static void omap_pwm_led_power_on(struct omap_pwm_led *led)
+{
+       if (led->powered)
+               return;
+       led->powered = 1;
+
+       /* Select clock */
+       omap_dm_timer_enable(led->intensity_timer);
+       omap_dm_timer_set_source(led->intensity_timer, OMAP_TIMER_SRC_32_KHZ);
+
+       /* Turn voltage on */
+       if (led->pdata->set_power != NULL)
+               led->pdata->set_power(led->pdata, 1);
+
+       /* Enable PWM timers */
+       if (led->blink_timer != NULL) {
+               omap_dm_timer_enable(led->blink_timer);
+               omap_dm_timer_set_source(led->blink_timer,
+                                        OMAP_TIMER_SRC_32_KHZ);
+               omap_pwm_led_set_blink(led);
+       }
+
+       omap_dm_timer_set_load(led->intensity_timer, 1, 0xffffff00);
+}
+
+static void omap_pwm_led_power_off(struct omap_pwm_led *led)
+{
+       if (!led->powered)
+               return;
+       led->powered = 0;
+
+       /* Everything off */
+       omap_dm_timer_stop(led->intensity_timer);
+       omap_dm_timer_disable(led->intensity_timer);
+
+       if (led->blink_timer != NULL) {
+               omap_dm_timer_stop(led->blink_timer);
+               omap_dm_timer_disable(led->blink_timer);
+       }
+
+       if (led->pdata->set_power != NULL)
+               led->pdata->set_power(led->pdata, 0);
+}
+
+static void omap_pwm_led_set_pwm_cycle(struct omap_pwm_led *led, int cycle)
+{
+       int n;
+
+       if (cycle == 0)
+               n = 0xff;
+       else    n = cycle - 1;
+
+       if (cycle == LED_FULL) {
+               omap_dm_timer_set_pwm(led->intensity_timer, 1, 1,
+                                     OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+               omap_dm_timer_stop(led->intensity_timer);
+       } else {
+               omap_dm_timer_set_pwm(led->intensity_timer, 0, 1,
+                                     OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+               omap_dm_timer_set_match(led->intensity_timer, 1,
+                                       (0xffffff00) | cycle);
+               omap_dm_timer_start(led->intensity_timer);
+       }
+}
+
+static void omap_pwm_led_set(struct led_classdev *led_cdev,
+                            enum led_brightness value)
+{
+       struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+
+       led->brightness = value;
+       schedule_work(&led->work);
+}
+
+static void omap_pwm_led_work(struct work_struct *work)
+{
+       struct omap_pwm_led *led = work_to_omap_pwm_led(work);
+
+       if (led->brightness != LED_OFF) {
+               omap_pwm_led_power_on(led);
+               omap_pwm_led_set_pwm_cycle(led, led->brightness);
+       } else {
+               omap_pwm_led_power_off(led);
+       }
+}
+
+static ssize_t omap_pwm_led_on_period_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+
+       return sprintf(buf, "%u\n", led->on_period) + 1;
+}
+
+static ssize_t omap_pwm_led_on_period_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+       int ret = -EINVAL;
+       unsigned long val;
+       char *after;
+       size_t count;
+
+       val = simple_strtoul(buf, &after, 10);
+       count = after - buf;
+       if (*after && isspace(*after))
+               count++;
+
+       if (count == size) {
+               led->on_period = val;
+               omap_pwm_led_set_blink(led);
+               ret = count;
+       }
+
+       return ret;
+}
+
+static ssize_t omap_pwm_led_off_period_show(struct device *dev,
+                               struct device_attribute *attr, char *buf)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+
+       return sprintf(buf, "%u\n", led->off_period) + 1;
+}
+
+static ssize_t omap_pwm_led_off_period_store(struct device *dev,
+                                       struct device_attribute *attr,
+                                       const char *buf, size_t size)
+{
+       struct led_classdev *led_cdev = dev_get_drvdata(dev);
+       struct omap_pwm_led *led = cdev_to_omap_pwm_led(led_cdev);
+       int ret = -EINVAL;
+       unsigned long val;
+       char *after;
+       size_t count;
+
+       val = simple_strtoul(buf, &after, 10);
+       count = after - buf;
+       if (*after && isspace(*after))
+               count++;
+
+       if (count == size) {
+               led->off_period = val;
+               omap_pwm_led_set_blink(led);
+               ret = count;
+       }
+
+       return ret;
+}
+
+static DEVICE_ATTR(on_period, 0644, omap_pwm_led_on_period_show,
+                               omap_pwm_led_on_period_store);
+static DEVICE_ATTR(off_period, 0644, omap_pwm_led_off_period_show,
+                               omap_pwm_led_off_period_store);
+
+static int omap_pwm_led_probe(struct platform_device *pdev)
+{
+       struct omap_pwm_led_platform_data *pdata = pdev->dev.platform_data;
+       struct omap_pwm_led *led;
+       int ret;
+
+       led = kzalloc(sizeof(struct omap_pwm_led), GFP_KERNEL);
+       if (led == NULL) {
+               dev_err(&pdev->dev, "No memory for device\n");
+               return -ENOMEM;
+       }
+
+       platform_set_drvdata(pdev, led);
+       led->cdev.brightness_set = omap_pwm_led_set;
+       led->cdev.default_trigger = NULL;
+       led->cdev.name = pdata->name;
+       led->pdata = pdata;
+       led->brightness = LED_OFF;
+       INIT_WORK(&led->work, omap_pwm_led_work);
+
+       dev_info(&pdev->dev, "OMAP PWM LED (%s) at GP timer %d/%d\n",
+                pdata->name, pdata->intensity_timer, pdata->blink_timer);
+
+       /* register our new led device */
+       ret = led_classdev_register(&pdev->dev, &led->cdev);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "led_classdev_register failed\n");
+               goto error_classdev;
+       }
+
+       /* get related dm timers */
+       led->intensity_timer = omap_dm_timer_request_specific(pdata->intensity_timer);
+       if (led->intensity_timer == NULL) {
+               dev_err(&pdev->dev, "failed to request intensity pwm timer\n");
+               ret = -ENODEV;
+               goto error_intensity;
+       }
+       omap_dm_timer_disable(led->intensity_timer);
+
+       if (pdata->blink_timer != 0) {
+               led->blink_timer = omap_dm_timer_request_specific(pdata->blink_timer);
+               if (led->blink_timer == NULL) {
+                       dev_err(&pdev->dev, "failed to request blinking pwm timer\n");
+                       ret = -ENODEV;
+                       goto error_blink1;
+               }
+               omap_dm_timer_disable(led->blink_timer);
+
+               ret = device_create_file(led->cdev.dev,
+                                              &dev_attr_on_period);
+               if(ret)
+                       goto error_blink2;
+
+               ret = device_create_file(led->cdev.dev,
+                                       &dev_attr_off_period);
+               if(ret)
+                       goto error_blink3;
+
+       }
+
+       return 0;
+
+error_blink3:
+       device_remove_file(led->cdev.dev,
+                                &dev_attr_on_period);
+error_blink2:
+       dev_err(&pdev->dev, "failed to create device file(s)\n");
+error_blink1:
+       omap_dm_timer_free(led->intensity_timer);
+error_intensity:
+       led_classdev_unregister(&led->cdev);
+error_classdev:
+       kfree(led);
+       return ret;
+}
+
+static int omap_pwm_led_remove(struct platform_device *pdev)
+{
+       struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
+
+       device_remove_file(led->cdev.dev,
+                                &dev_attr_on_period);
+       device_remove_file(led->cdev.dev,
+                                &dev_attr_off_period);
+       led_classdev_unregister(&led->cdev);
+
+       omap_pwm_led_set(&led->cdev, LED_OFF);
+       if (led->blink_timer != NULL)
+               omap_dm_timer_free(led->blink_timer);
+       omap_dm_timer_free(led->intensity_timer);
+       kfree(led);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int omap_pwm_led_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
+
+       led_classdev_suspend(&led->cdev);
+       return 0;
+}
+
+static int omap_pwm_led_resume(struct platform_device *pdev)
+{
+       struct omap_pwm_led *led = pdev_to_omap_pwm_led(pdev);
+
+       led_classdev_resume(&led->cdev);
+       return 0;
+}
+#else
+#define omap_pwm_led_suspend NULL
+#define omap_pwm_led_resume NULL
+#endif
+
+static struct platform_driver omap_pwm_led_driver = {
+       .probe          = omap_pwm_led_probe,
+       .remove         = omap_pwm_led_remove,
+       .suspend        = omap_pwm_led_suspend,
+       .resume         = omap_pwm_led_resume,
+       .driver         = {
+               .name           = "omap_pwm_led",
+               .owner          = THIS_MODULE,
+       },
+};
+
+static int __init omap_pwm_led_init(void)
+{
+       return platform_driver_register(&omap_pwm_led_driver);
+}
+
+static void __exit omap_pwm_led_exit(void)
+{
+       platform_driver_unregister(&omap_pwm_led_driver);
+}
+
+module_init(omap_pwm_led_init);
+module_exit(omap_pwm_led_exit);
+
+MODULE_AUTHOR("Timo Teras");
+MODULE_DESCRIPTION("OMAP PWM LED driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/leds/leds-omap.c b/drivers/leds/leds-omap.c
new file mode 100644 (file)
index 0000000..e936403
--- /dev/null
@@ -0,0 +1,129 @@
+/* drivers/leds/leds-omap.c
+ *
+ * (C) 2006 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * OMAP - LEDs GPIO 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 <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+
+#include <mach/hardware.h>
+#include <mach/led.h>
+
+/* our context */
+
+static void omap_set_led_gpio(struct led_classdev *led_cdev,
+                           enum led_brightness value)
+{
+       struct omap_led_config *led_dev;
+
+       led_dev = container_of(led_cdev, struct omap_led_config, cdev);
+       gpio_set_value(led_dev->gpio, value);
+}
+
+static int omap_led_probe(struct platform_device *dev)
+{
+       struct omap_led_platform_data *pdata = dev->dev.platform_data;
+       struct omap_led_config *leds = pdata->leds;
+       int i, ret = 0;
+
+       for (i = 0; ret >= 0 && i < pdata->nr_leds; i++) {
+               ret = gpio_request(leds[i].gpio, leds[i].cdev.name);
+               if (ret < 0)
+                       break;
+               gpio_direction_output(leds[i].gpio, 0);
+               if (!leds[i].cdev.brightness_set)
+                       leds[i].cdev.brightness_set = omap_set_led_gpio;
+
+               ret = led_classdev_register(&dev->dev, &leds[i].cdev);
+       }
+
+       if (ret < 0 && i > 1) {
+               for (i = i - 2; i >= 0; i--) {
+                       led_classdev_unregister(&leds[i].cdev);
+                       gpio_free(leds[i].gpio);
+               }
+       }
+
+       return ret;
+}
+
+static int omap_led_remove(struct platform_device *dev)
+{
+       struct omap_led_platform_data *pdata = dev->dev.platform_data;
+       struct omap_led_config *leds = pdata->leds;
+       int i;
+
+       for (i = 0; i < pdata->nr_leds; i++) {
+               led_classdev_unregister(&leds[i].cdev);
+               gpio_free(leds[i].gpio);
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int omap_led_suspend(struct platform_device *dev, pm_message_t state)
+{
+       struct omap_led_platform_data *pdata = dev->dev.platform_data;
+       struct omap_led_config *leds = pdata->leds;
+       int i;
+
+       for (i = 0; i < pdata->nr_leds; i++)
+               led_classdev_suspend(&leds[i].cdev);
+
+       return 0;
+}
+
+static int omap_led_resume(struct platform_device *dev)
+{
+       struct omap_led_platform_data *pdata = dev->dev.platform_data;
+       struct omap_led_config *leds = pdata->leds;
+       int i;
+
+       for (i = 0; i < pdata->nr_leds; i++)
+               led_classdev_resume(&leds[i].cdev);
+
+       return 0;
+}
+#else
+#define omap_led_suspend       NULL
+#define omap_led_resume                NULL
+#endif
+
+static struct platform_driver omap_led_driver = {
+       .probe          = omap_led_probe,
+       .remove         = omap_led_remove,
+       .suspend        = omap_led_suspend,
+       .resume         = omap_led_resume,
+       .driver         = {
+               .name           = "omap-led",
+               .owner          = THIS_MODULE,
+       },
+};
+
+static int __init omap_led_init(void)
+{
+       return platform_driver_register(&omap_led_driver);
+}
+
+static void __exit omap_led_exit(void)
+{
+       platform_driver_unregister(&omap_led_driver);
+}
+
+module_init(omap_led_init);
+module_exit(omap_led_exit);
+
+MODULE_AUTHOR("Kyungmin Park<kyungmin.park@samsung.com>");
+MODULE_DESCRIPTION("OMAP LED driver");
+MODULE_LICENSE("GPL");
index ee3927ab11e053a06bd68f019075ffbcc7d66077..502c1165058f8c90bae973f245dfde0e76260032 100644 (file)
@@ -107,6 +107,19 @@ config TWL4030_CORE
          high speed USB OTG transceiver, an audio codec (on most
          versions) and many other features.
 
          high speed USB OTG transceiver, an audio codec (on most
          versions) and many other features.
 
+config TWL4030_POWER
+       bool "Support power resources on TWL4030 family chips"
+       depends on TWL4030_CORE
+       help
+         Say yes here if you want to use the power resources on the
+         TWL4030 family chips.  Most of these resources are regulators,
+         which have a separate driver; some are control signals, such
+         as clock request handshaking.
+
+         This driver uses board-specific data to initialize the resources
+         and load scripts controling which resources are switched off/on
+         or reset when a sleep, wakeup or warm reset event occurs.
+
 config MFD_TMIO
        bool
        default n
 config MFD_TMIO
        bool
        default n
index 3afb5192e4dadf31c640c2574101950f2068f0ac..44a0053d61008d55770f4eb0eeed8e293b619e97 100644 (file)
@@ -23,6 +23,7 @@ obj-$(CONFIG_TPS65010)                += tps65010.o
 obj-$(CONFIG_MENELAUS)         += menelaus.o
 
 obj-$(CONFIG_TWL4030_CORE)     += twl4030-core.o twl4030-irq.o
 obj-$(CONFIG_MENELAUS)         += menelaus.o
 
 obj-$(CONFIG_TWL4030_CORE)     += twl4030-core.o twl4030-irq.o
+obj-$(CONFIG_TWL4030_POWER)    += twl4030-power.o
 
 obj-$(CONFIG_MFD_CORE)         += mfd-core.o
 
 
 obj-$(CONFIG_MFD_CORE)         += mfd-core.o
 
index ec90e953adced9f0d2b3f3d4abe878d6b3511da9..769b34bd48e445880ac0920423d9b73eabaf4cb7 100644 (file)
 #define twl_has_madc() false
 #endif
 
 #define twl_has_madc() false
 #endif
 
+#ifdef CONFIG_TWL4030_POWER
+#define twl_has_power()        true
+#else
+#define twl_has_power()        false
+#endif
+
 #if defined(CONFIG_RTC_DRV_TWL4030) || defined(CONFIG_RTC_DRV_TWL4030_MODULE)
 #define twl_has_rtc()  true
 #else
 #if defined(CONFIG_RTC_DRV_TWL4030) || defined(CONFIG_RTC_DRV_TWL4030_MODULE)
 #define twl_has_rtc()  true
 #else
 #define twl_has_usb()  false
 #endif
 
 #define twl_has_usb()  false
 #endif
 
+#if defined(CONFIG_INPUT_TWL4030_PWRBUTTON) \
+       || defined(CONFIG_INPUT_TWL4030_PWBUTTON_MODULE)
+#define twl_has_pwrbutton()    true
+#else
+#define twl_has_pwrbutton()    false
+#endif
 
 /* Triton Core internal information (BEGIN) */
 
 
 /* Triton Core internal information (BEGIN) */
 
@@ -225,6 +237,8 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
        { 3, TWL4030_BASEADD_SECURED_REG },
 };
 
        { 3, TWL4030_BASEADD_SECURED_REG },
 };
 
+extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
+
 /*----------------------------------------------------------------------*/
 
 /* Exported Functions */
 /*----------------------------------------------------------------------*/
 
 /* Exported Functions */
@@ -526,6 +540,13 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
                usb_transceiver = child;
        }
 
                usb_transceiver = child;
        }
 
+       if (twl_has_pwrbutton()) {
+               child = add_child(1, "twl4030_pwrbutton",
+                               NULL, 0, true, pdata->irq_base + 8 + 0, 0);
+               if (IS_ERR(child))
+                       return PTR_ERR(child);
+       }
+
        if (twl_has_regulator()) {
                /*
                child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
        if (twl_has_regulator()) {
                /*
                child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1);
@@ -776,6 +797,10 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
        /* setup clock framework */
        clocks_init(&client->dev);
 
        /* setup clock framework */
        clocks_init(&client->dev);
 
+       /* load power event scripts */
+       if (twl_has_power() && pdata->power)
+               twl4030_power_init(pdata->power);
+
        /* Maybe init the T2 Interrupt subsystem */
        if (client->irq
                        && pdata->irq_base
        /* Maybe init the T2 Interrupt subsystem */
        if (client->irq
                        && pdata->irq_base
diff --git a/drivers/mfd/twl4030-power.c b/drivers/mfd/twl4030-power.c
new file mode 100644 (file)
index 0000000..9dc493b
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * linux/drivers/i2c/chips/twl4030-power.c
+ *
+ * Handle TWL4030 Power initialization
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2006 Texas Instruments, Inc
+ *
+ * Written by  Kalle Jokiniemi
+ *             Peter De Schrijver <peter.de-schrijver@nokia.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/pm.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach-types.h>
+
+static u8 triton_next_free_address = 0x2b;
+
+#define PWR_P1_SW_EVENTS       0x10
+#define PWR_DEVOFF     (1<<0)
+
+#define PHY_TO_OFF_PM_MASTER(p)                (p - 0x36)
+#define PHY_TO_OFF_PM_RECEIVER(p)      (p - 0x5b)
+
+#define NUM_OF_RESOURCES       28
+
+/* resource - hfclk */
+#define R_HFCLKOUT_DEV_GRP     PHY_TO_OFF_PM_RECEIVER(0xe6)
+
+/* PM events */
+#define R_P1_SW_EVENTS         PHY_TO_OFF_PM_MASTER(0x46)
+#define R_P2_SW_EVENTS         PHY_TO_OFF_PM_MASTER(0x47)
+#define R_P3_SW_EVENTS         PHY_TO_OFF_PM_MASTER(0x48)
+#define R_CFG_P1_TRANSITION    PHY_TO_OFF_PM_MASTER(0x36)
+#define R_CFG_P2_TRANSITION    PHY_TO_OFF_PM_MASTER(0x37)
+#define R_CFG_P3_TRANSITION    PHY_TO_OFF_PM_MASTER(0x38)
+
+#define LVL_WAKEUP     0x08
+
+#define ENABLE_WARMRESET (1<<4)
+
+#define END_OF_SCRIPT          0x3f
+
+#define R_SEQ_ADD_A2S          PHY_TO_OFF_PM_MASTER(0x55)
+#define R_SEQ_ADD_SA12         PHY_TO_OFF_PM_MASTER(0x56)
+#define        R_SEQ_ADD_S2A3          PHY_TO_OFF_PM_MASTER(0x57)
+#define        R_SEQ_ADD_WARM          PHY_TO_OFF_PM_MASTER(0x58)
+#define R_MEMORY_ADDRESS       PHY_TO_OFF_PM_MASTER(0x59)
+#define R_MEMORY_DATA          PHY_TO_OFF_PM_MASTER(0x5a)
+
+#define R_PROTECT_KEY          0x0E
+#define KEY_1                  0xC0
+#define KEY_2                  0x0C
+
+/* resource configuration registers */
+
+#define DEVGROUP_OFFSET                0
+#define TYPE_OFFSET            1
+
+static u8 res_config_addrs[] = {
+       [RES_VAUX1]     = 0x17,
+       [RES_VAUX2]     = 0x1b,
+       [RES_VAUX3]     = 0x1f,
+       [RES_VAUX4]     = 0x23,
+       [RES_VMMC1]     = 0x27,
+       [RES_VMMC2]     = 0x2b,
+       [RES_VPLL1]     = 0x2f,
+       [RES_VPLL2]     = 0x33,
+       [RES_VSIM]      = 0x37,
+       [RES_VDAC]      = 0x3b,
+       [RES_VINTANA1]  = 0x3f,
+       [RES_VINTANA2]  = 0x43,
+       [RES_VINTDIG]   = 0x47,
+       [RES_VIO]       = 0x4b,
+       [RES_VDD1]      = 0x55,
+       [RES_VDD2]      = 0x63,
+       [RES_VUSB_1V5]  = 0x71,
+       [RES_VUSB_1V8]  = 0x74,
+       [RES_VUSB_3V1]  = 0x77,
+       [RES_VUSBCP]    = 0x7a,
+       [RES_REGEN]     = 0x7f,
+       [RES_NRES_PWRON] = 0x82,
+       [RES_CLKEN]     = 0x85,
+       [RES_SYSEN]     = 0x88,
+       [RES_HFCLKOUT]  = 0x8b,
+       [RES_32KCLKOUT] = 0x8e,
+       [RES_RESET]     = 0x91,
+       [RES_Main_Ref]  = 0x94,
+};
+
+static int __init twl4030_write_script_byte(u8 address, u8 byte)
+{
+       int err;
+
+       err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+                                       R_MEMORY_ADDRESS);
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, byte,
+                                       R_MEMORY_DATA);
+
+       return err;
+}
+
+static int __init twl4030_write_script_ins(u8 address, u16 pmb_message,
+                                               u8 delay, u8 next)
+{
+       int err = 0;
+
+       address *= 4;
+       err |= twl4030_write_script_byte(address++, pmb_message >> 8);
+       err |= twl4030_write_script_byte(address++, pmb_message & 0xff);
+       err |= twl4030_write_script_byte(address++, delay);
+       err |= twl4030_write_script_byte(address++, next);
+
+       return err;
+}
+
+static int __init twl4030_write_script(u8 address, struct twl4030_ins *script,
+                                       int len)
+{
+       int err = 0;
+
+       for (; len; len--, address++, script++) {
+               if (len == 1)
+                       err |= twl4030_write_script_ins(address,
+                                                       script->pmb_message,
+                                                       script->delay,
+                                                       END_OF_SCRIPT);
+               else
+                       err |= twl4030_write_script_ins(address,
+                                                       script->pmb_message,
+                                                       script->delay,
+                                                       address + 1);
+       }
+
+       return err;
+}
+
+static int __init config_wakeup3_sequence(u8 address)
+{
+
+       int err = 0;
+
+       /* Set SLEEP to ACTIVE SEQ address for P3 */
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+                                 R_SEQ_ADD_S2A3);
+
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, LVL_WAKEUP,
+                                       R_P3_SW_EVENTS);
+       if (err)
+               printk(KERN_ERR "TWL4030 wakeup sequence for P3" \
+                               "config error\n");
+
+       return err;
+}
+
+static int __init config_wakeup12_sequence(u8 address)
+{
+       int err = 0;
+
+       /* Set SLEEP to ACTIVE SEQ address for P1 and P2 */
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+                                 R_SEQ_ADD_SA12);
+
+       /* P1/P2/P3 LVL_WAKEUP should be on LEVEL */
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, LVL_WAKEUP,
+                                       R_P1_SW_EVENTS);
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, LVL_WAKEUP,
+                                       R_P2_SW_EVENTS);
+
+       if (machine_is_omap_3430sdp() || machine_is_omap_ldp()) {
+               u8 data;
+               /* Disabling AC charger effect on sleep-active transitions */
+               err |= twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &data,
+                                               R_CFG_P1_TRANSITION);
+               data &= ~(1<<1);
+               err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, data ,
+                                               R_CFG_P1_TRANSITION);
+       }
+
+       if (err)
+               printk(KERN_ERR "TWL4030 wakeup sequence for P1 and P2" \
+                               "config error\n");
+
+       return err;
+}
+
+static int __init config_sleep_sequence(u8 address)
+{
+       int err = 0;
+
+       /*
+        * CLKREQ is pulled high on the 2430SDP, therefore, we need to take
+        * it out of the HFCLKOUT DEV_GRP for P1 else HFCLKOUT can't be stopped.
+        */
+
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+                                 0x20, R_HFCLKOUT_DEV_GRP);
+
+       /* Set ACTIVE to SLEEP SEQ address in T2 memory*/
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+                                 R_SEQ_ADD_A2S);
+
+       if (err)
+               printk(KERN_ERR "TWL4030 sleep sequence config error\n");
+
+       return err;
+}
+
+static int __init config_warmreset_sequence(u8 address)
+{
+
+       int err = 0;
+       u8 rd_data;
+
+       /* Set WARM RESET SEQ address for P1 */
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, address,
+                                       R_SEQ_ADD_WARM);
+
+       /* P1/P2/P3 enable WARMRESET */
+       err |= twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
+                                       R_P1_SW_EVENTS);
+       rd_data |= ENABLE_WARMRESET;
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
+                                       R_P1_SW_EVENTS);
+
+       err |= twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
+                                       R_P2_SW_EVENTS);
+       rd_data |= ENABLE_WARMRESET;
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
+                                       R_P2_SW_EVENTS);
+
+       err |= twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &rd_data,
+                                       R_P3_SW_EVENTS);
+       rd_data |= ENABLE_WARMRESET;
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, rd_data,
+                                       R_P3_SW_EVENTS);
+
+       if (err)
+               printk(KERN_ERR
+                       "TWL4030 warmreset seq config error\n");
+       return err;
+}
+
+void twl4030_configure_resource(struct twl4030_resconfig *rconfig)
+{
+       int rconfig_addr;
+       u8 type;
+
+       if (rconfig->resource > NUM_OF_RESOURCES) {
+               printk(KERN_ERR
+                       "TWL4030 Resource %d does not exist\n",
+                       rconfig->resource);
+               return;
+       }
+
+       rconfig_addr = res_config_addrs[rconfig->resource];
+
+       /* Set resource group */
+
+       if (rconfig->devgroup >= 0)
+               twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+                                       rconfig->devgroup << 5,
+                                       rconfig_addr + DEVGROUP_OFFSET);
+
+       /* Set resource types */
+
+       if (twl4030_i2c_read_u8(TWL4030_MODULE_PM_RECEIVER,
+                                       &type,
+                                       rconfig_addr + TYPE_OFFSET) < 0) {
+               printk(KERN_ERR
+                       "TWL4030 Resource %d type could not read\n",
+                       rconfig->resource);
+               return;
+       }
+
+       if (rconfig->type >= 0) {
+               type &= ~7;
+               type |= rconfig->type;
+       }
+
+       if (rconfig->type2 >= 0) {
+               type &= ~(3 << 3);
+               type |= rconfig->type2 << 3;
+       }
+
+       twl4030_i2c_write_u8(TWL4030_MODULE_PM_RECEIVER,
+                               type, rconfig_addr + TYPE_OFFSET);
+
+}
+
+static int __init load_triton_script(struct twl4030_script *tscript)
+{
+       u8 address = triton_next_free_address;
+       int err;
+
+       err = twl4030_write_script(address, tscript->script, tscript->size);
+       if (err)
+               return err;
+
+       triton_next_free_address += tscript->size;
+
+       if (tscript->flags & TRITON_WRST_SCRIPT)
+               err |= config_warmreset_sequence(address);
+
+       if (tscript->flags & TRITON_WAKEUP12_SCRIPT)
+               err |= config_wakeup12_sequence(address);
+
+       if (tscript->flags & TRITON_WAKEUP3_SCRIPT)
+               err |= config_wakeup3_sequence(address);
+
+       if (tscript->flags & TRITON_SLEEP_SCRIPT)
+               err |= config_sleep_sequence(address);
+
+       return err;
+}
+
+void __init twl4030_power_init(struct twl4030_power_data *triton2_scripts)
+{
+       int err = 0;
+       int i;
+       struct twl4030_resconfig *resconfig;
+
+       err = twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_1,
+                               R_PROTECT_KEY);
+       err |= twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, KEY_2,
+                               R_PROTECT_KEY);
+       if (err)
+               printk(KERN_ERR
+                       "TWL4030 Unable to unlock registers\n");
+
+       for (i = 0; i < triton2_scripts->size; i++) {
+               err = load_triton_script(triton2_scripts->scripts[i]);
+               if (err)
+                       break;
+       }
+
+       resconfig = triton2_scripts->resource_config;
+       if (resconfig) {
+               while (resconfig->resource) {
+                       twl4030_configure_resource(resconfig);
+                       resconfig++;
+               }
+       }
+
+       if (twl4030_i2c_write_u8(TWL4030_MODULE_PM_MASTER, 0, R_PROTECT_KEY))
+               printk(KERN_ERR
+                       "TWL4030 Unable to relock registers\n");
+}
index 6d1ac180f6ee8013b212ff06bf6dca6ff475b1cb..b1b2696c9c2bba0c2f5d340d9ef4f98c9daf0f81 100644 (file)
@@ -150,6 +150,20 @@ config ATMEL_SSC
 
          If unsure, say N.
 
 
          If unsure, say N.
 
+config OMAP_STI
+       bool "Serial Trace Interface support"
+       depends on ARCH_OMAP16XX || ARCH_OMAP24XX || ARCH_OMAP34XX
+       default n
+       help
+         Serial Trace Interface. The protocols suported for OMAP1/2/3 are
+         STI/CSTI/XTIv2 correspondingly.
+
+config OMAP_STI_CONSOLE
+       bool "STI console support"
+       depends on OMAP_STI
+       help
+         This enables a console driver by way of STI/XTI.
+
 config ENCLOSURE_SERVICES
        tristate "Enclosure Services"
        default n
 config ENCLOSURE_SERVICES
        tristate "Enclosure Services"
        default n
index 7871f05dcb9b8d29804dbe7e68743c01369937da..8143c4d157a522c4357cba37fe3a26b84d69a75e 100644 (file)
@@ -13,6 +13,7 @@ obj-$(CONFIG_TIFM_CORE)               += tifm_core.o
 obj-$(CONFIG_TIFM_7XX1)        += tifm_7xx1.o
 obj-$(CONFIG_PHANTOM)          += phantom.o
 obj-$(CONFIG_SGI_IOC4)         += ioc4.o
 obj-$(CONFIG_TIFM_7XX1)        += tifm_7xx1.o
 obj-$(CONFIG_PHANTOM)          += phantom.o
 obj-$(CONFIG_SGI_IOC4)         += ioc4.o
+obj-$(CONFIG_OMAP_STI)         += sti/
 obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
 obj-$(CONFIG_KGDB_TESTS)       += kgdbts.o
 obj-$(CONFIG_SGI_XP)           += sgi-xp/
 obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
 obj-$(CONFIG_KGDB_TESTS)       += kgdbts.o
 obj-$(CONFIG_SGI_XP)           += sgi-xp/
diff --git a/drivers/misc/sti/Makefile b/drivers/misc/sti/Makefile
new file mode 100644 (file)
index 0000000..43574d1
--- /dev/null
@@ -0,0 +1,8 @@
+ifeq ($(CONFIG_ARCH_OMAP3),y)
+obj-$(CONFIG_OMAP_STI) += sdti.o
+else
+obj-$(CONFIG_OMAP_STI) += sti.o sti-fifo.o
+endif
+
+obj-$(CONFIG_NET)      += sti-netlink.o
+obj-$(CONFIG_OMAP_STI_CONSOLE) += sti-console.o
diff --git a/drivers/misc/sti/sdti.c b/drivers/misc/sti/sdti.c
new file mode 100644 (file)
index 0000000..31780de
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Support functions for OMAP3 SDTI (Serial Debug Tracing Interface)
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Written by: Roman Tereshonkov <roman.tereshonkov@nokia.com>
+ *
+ * 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 <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <mach/sti.h>
+#include <asm/byteorder.h>
+#include <asm/io.h>
+
+#define SDTI_REVISION          0x000
+#define SDTI_SYSCONFIG         0x010
+#define SDTI_SYSSTATUS         0x014
+#define SDTI_WINCTRL           0x024
+#define SDTI_SCONFIG           0x028
+#define SDTI_TESTCTRL          0x02C
+#define SDTI_LOCK_ACCESS       0xFB0
+
+#define CPU1_TRACE_EN          0x01
+#define CPU2_TRACE_EN          0x02
+
+#define SDTI_SYSCONFIG_SOFTRESET       (1 << 1)
+#define SDTI_SYSCONFIG_AUTOIDLE                (1 << 0)
+
+static struct clk *sdti_fck, *sdti_ick;
+void __iomem *sti_base, *sti_channel_base;
+static DEFINE_SPINLOCK(sdti_lock);
+static int sdti_initialized;
+
+void sti_channel_write_trace(int len, int id, void *data,
+                               unsigned int channel)
+{
+       const u8 *tpntr = data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&sdti_lock, flags);
+
+       if (unlikely(!sdti_initialized))
+               goto skip;
+
+       sti_channel_writeb(id, channel);
+       while (len--)
+               sti_channel_writeb(*tpntr++, channel);
+       sti_channel_flush(channel);
+ skip:
+       spin_unlock_irqrestore(&sdti_lock, flags);
+}
+EXPORT_SYMBOL(sti_channel_write_trace);
+
+static void omap_sdti_reset(void)
+{
+       int i;
+
+       sti_writel(SDTI_SYSCONFIG_SOFTRESET, SDTI_SYSCONFIG);
+
+       for (i = 0; i < 10000; i++)
+               if (sti_readl(SDTI_SYSSTATUS) & 1)
+                       break;
+       if (i == 10000)
+               printk(KERN_WARNING "XTI: no real reset\n");
+}
+
+static int __init omap_sdti_init(void)
+{
+       char buf[64];
+       int i, ret = 0;
+
+       sdti_fck = clk_get(NULL, "pclk_fck");
+       if (IS_ERR(sdti_fck)) {
+               printk(KERN_ERR "Cannot get clk pclk_fck\n");
+               ret = PTR_ERR(sdti_fck);
+               goto err0;
+       }
+       sdti_ick = clk_get(NULL, "pclkx2_fck");
+       if (IS_ERR(sdti_ick)) {
+               printk(KERN_ERR "Cannot get clk pclkx2_fck\n");
+               ret = PTR_ERR(sdti_ick);
+               goto err1;
+       }
+       ret = clk_enable(sdti_fck);
+       if (ret) {
+               printk(KERN_ERR "Cannot enable sdti_fck\n");
+               goto err2;
+       }
+       ret = clk_enable(sdti_ick);
+       if (ret) {
+               printk(KERN_ERR "Cannot enable sdti_ick\n");
+               goto err3;
+       }
+
+       omap_sdti_reset();
+       sti_writel(0xC5ACCE55, SDTI_LOCK_ACCESS);
+
+       /* Autoidle */
+       sti_writel(SDTI_SYSCONFIG_AUTOIDLE, SDTI_SYSCONFIG);
+
+       /* Claim SDTI */
+       sti_writel(1 << 30, SDTI_WINCTRL);
+       i = sti_readl(SDTI_WINCTRL);
+       if (!(i & (1 << 30)))
+               printk(KERN_WARNING "SDTI: cannot claim SDTI\n");
+
+       /* 4 bits dual, fclk/3 */
+       sti_writel(0x43, SDTI_SCONFIG);
+
+       /* CPU2 trace enable */
+       sti_writel(i | CPU2_TRACE_EN, SDTI_WINCTRL);
+       i = sti_readl(SDTI_WINCTRL);
+
+       /* Enable SDTI */
+       sti_writel((1 << 31) | (i & 0x3FFFFFFF), SDTI_WINCTRL);
+
+       spin_lock_irq(&sdti_lock);
+       sdti_initialized = 1;
+       spin_unlock_irq(&sdti_lock);
+
+       i = sti_readl(SDTI_REVISION);
+       snprintf(buf, sizeof(buf), "OMAP SDTI 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 ret;
+
+err3:
+       clk_disable(sdti_fck);
+err2:
+       clk_put(sdti_ick);
+err1:
+       clk_put(sdti_fck);
+err0:
+       return ret;
+}
+
+static void omap_sdti_exit(void)
+{
+       sti_writel(0, SDTI_WINCTRL);
+       clk_disable(sdti_fck);
+       clk_disable(sdti_ick);
+       clk_put(sdti_fck);
+       clk_put(sdti_ick);
+}
+
+static int __devinit omap_sdti_probe(struct platform_device *pdev)
+{
+       struct resource *res, *cres;
+       unsigned int size;
+
+       if (pdev->num_resources != 2) {
+               dev_err(&pdev->dev, "invalid number of resources: %d\n",
+                       pdev->num_resources);
+               return -ENODEV;
+       }
+
+       /* SDTI 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;
+       }
+
+       size = res->end - res->start;
+       sti_base = ioremap(res->start, size);
+       if (unlikely(!sti_base))
+               return -ENODEV;
+
+       size = cres->end - cres->start;
+       sti_channel_base = ioremap(cres->start, size);
+       if (unlikely(!sti_channel_base)) {
+               iounmap(sti_base);
+               return -ENODEV;
+       }
+
+       return omap_sdti_init();
+}
+
+static int __devexit omap_sdti_remove(struct platform_device *pdev)
+{
+       iounmap(sti_channel_base);
+       iounmap(sti_base);
+       omap_sdti_exit();
+
+       return 0;
+}
+
+static struct platform_driver omap_sdti_driver = {
+       .probe          = omap_sdti_probe,
+       .remove         = __devexit_p(omap_sdti_remove),
+       .driver         = {
+               .name   = "sti",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init omap_sdti_module_init(void)
+{
+       return platform_driver_register(&omap_sdti_driver);
+}
+
+static void __exit omap_sdti_module_exit(void)
+{
+       platform_driver_unregister(&omap_sdti_driver);
+}
+subsys_initcall(omap_sdti_module_init);
+module_exit(omap_sdti_module_exit);
+
+MODULE_AUTHOR("Roman Tereshonkov");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/sti/sti-console.c b/drivers/misc/sti/sti-console.c
new file mode 100644 (file)
index 0000000..2062d23
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Console support for OMAP STI/XTI
+ *
+ * Copyright (C) 2004, 2005, 2006 Nokia Corporation
+ * Written by: Paul Mundt <paul.mundt@nokia.com>
+ *
+ * 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 <linux/console.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <mach/sti.h>
+#include <mach/board.h>
+
+#define DRV_NAME "sticon"
+
+static struct tty_driver *tty_driver;
+static DEFINE_SPINLOCK(sti_console_lock);
+static unsigned int sti_console_channel = -1;
+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;
+       }
+
+       if (unlikely(sti_console_channel == -1))
+               return -EINVAL;
+
+       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");
+MODULE_AUTHOR("Paul Mundt");
+MODULE_DESCRIPTION("OMAP STI console support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/sti/sti-fifo.c b/drivers/misc/sti/sti-fifo.c
new file mode 100644 (file)
index 0000000..1ea5b18
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * STI RX FIFO Support
+ *
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ * Written by:  Paul Mundt <paul.mundt@nokia.com> and
+ *             Roman Tereshonkov <roman.tereshonkov@nokia.com>
+ *
+ * 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 <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <mach/sti.h>
+
+#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/drivers/misc/sti/sti-netlink.c b/drivers/misc/sti/sti-netlink.c
new file mode 100644 (file)
index 0000000..dbd6a03
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * OMAP STI/XTI communications interface via netlink socket.
+ *
+ * Copyright (C) 2004, 2005, 2006 Nokia Corporation
+ * Written by: Paul Mundt <paul.mundt@nokia.com>
+ *
+ * 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 <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/netlink.h>
+#include <linux/socket.h>
+#include <linux/skbuff.h>
+#include <linux/mutex.h>
+#include <net/sock.h>
+#include <mach/sti.h>
+
+static struct sock *sti_sock;
+static DEFINE_MUTEX(sti_netlink_mutex);
+
+enum {
+       STI_READ,
+       STI_WRITE,
+};
+
+#if defined(CONFIG_ARCH_OMAP1) || defined(CONFIG_ARCH_OMAP2)
+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;
+}
+#endif
+
+/*
+ * 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;
+       int 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;
+#if defined(CONFIG_ARCH_OMAP1) || defined(CONFIG_ARCH_OMAP2)
+       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;
+#endif
+       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 sk_buff *skb)
+{
+       if (!mutex_trylock(&sti_netlink_mutex))
+               return;
+
+       sti_netlink_receive_skb(skb);
+       mutex_unlock(&sti_netlink_mutex);
+}
+
+static int __init sti_netlink_init(void)
+{
+       sti_sock = netlink_kernel_create(&init_net, NETLINK_USERSOCK, 0,
+                                        sti_netlink_receive, NULL,
+                                        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/drivers/misc/sti/sti.c b/drivers/misc/sti/sti.c
new file mode 100644 (file)
index 0000000..518298b
--- /dev/null
@@ -0,0 +1,430 @@
+/*
+ * Support functions for OMAP STI/XTI (Serial Tracing Interface)
+ *
+ * Copyright (C) 2004, 2005, 2006 Nokia Corporation
+ * Written by: Paul Mundt <paul.mundt@nokia.com>
+ *
+ * STI initialization code and channel handling
+ * from Juha Yrjölä <juha.yrjola@nokia.com>.
+ *
+ * XTI initialization
+ * from Roman Tereshonkov <roman.tereshonkov@nokia.com>.
+ *
+ * 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 <linux/init.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <mach/sti.h>
+#include <asm/byteorder.h>
+
+static struct clk *sti_ck;
+void __iomem *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)
+{
+       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;
+       unsigned int size;
+       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;
+
+       size = res->end - res->start + 1;
+       sti_base = ioremap(res->start, size);
+       if (!sti_base) {
+               ret = -ENOMEM;
+               goto err_badremap;
+       }
+
+       size = cres->end - cres->start + 1;
+       sti_channel_base = ioremap(cres->start, size);
+       if (!sti_channel_base) {
+               iounmap(sti_base);
+               ret = -ENOMEM;
+               goto err_badremap;
+       }
+
+       ret = request_irq(platform_get_irq(pdev, 0), sti_interrupt,
+                         IRQF_DISABLED, "sti", NULL);
+       if (unlikely(ret != 0))
+               goto err_badirq;
+
+       return sti_init();
+
+err_badirq:
+       iounmap(sti_channel_base);
+       iounmap(sti_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);
+
+       iounmap(sti_channel_base);
+       iounmap(sti_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");
index b4cf691f3f64ec9cdbbc2792d32ae35bbf37bbfe..5e7ef789c828735754e25275a60f54ac9c3d9a86 100644 (file)
@@ -85,8 +85,9 @@ config MMC_SDHCI_OF
 
 config MMC_OMAP
        tristate "TI OMAP Multimedia Card Interface support"
 
 config MMC_OMAP
        tristate "TI OMAP Multimedia Card Interface support"
-       depends on ARCH_OMAP
+       depends on ARCH_OMAP1 || (ARCH_OMAP2 && ARCH_OMAP2420)
        select TPS65010 if MACH_OMAP_H2
        select TPS65010 if MACH_OMAP_H2
+       select OMAP_GPIO_SWITCH if MACH_NOKIA_N800
        help
          This selects the TI OMAP Multimedia card Interface.
          If you have an OMAP board with a Multimedia Card slot,
        help
          This selects the TI OMAP Multimedia card Interface.
          If you have an OMAP board with a Multimedia Card slot,
index 5570849188cc806032cab4791ab3ce10706a51f2..bfa25c01c8722f17684520b1ab3adfee9bdef193 100644 (file)
@@ -157,8 +157,6 @@ struct mmc_omap_host {
        struct timer_list       dma_timer;
        unsigned                dma_len;
 
        struct timer_list       dma_timer;
        unsigned                dma_len;
 
-       short                   power_pin;
-
        struct mmc_omap_slot    *slots[OMAP_MMC_MAX_SLOTS];
        struct mmc_omap_slot    *current_slot;
        spinlock_t              slot_lock;
        struct mmc_omap_slot    *slots[OMAP_MMC_MAX_SLOTS];
        struct mmc_omap_slot    *current_slot;
        spinlock_t              slot_lock;
index d183be6f2a5f7ab369ef8b60c5cfd25815db110f..63a06266828edb21fda7fefe8172b704bcb1a236 100644 (file)
@@ -489,6 +489,7 @@ static irqreturn_t mmc_omap_irq(int irq, void *dev_id)
        }
 
        OMAP_HSMMC_WRITE(host->base, STAT, status);
        }
 
        OMAP_HSMMC_WRITE(host->base, STAT, status);
+       OMAP_HSMMC_READ(host->base, STAT); /* flush posted write */
 
        if (end_cmd || (status & CC))
                mmc_omap_cmd_done(host, host->cmd);
 
        if (end_cmd || (status & CC))
                mmc_omap_cmd_done(host, host->cmd);
@@ -1070,7 +1071,6 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
        mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
        mmc->max_seg_size = mmc->max_req_size;
 
        mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
        mmc->max_seg_size = mmc->max_req_size;
 
-       mmc->ocr_avail = mmc_slot(host).ocr_mask;
        mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
 
        if (pdata->slots[host->slot_id].wires >= 8)
        mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
 
        if (pdata->slots[host->slot_id].wires >= 8)
@@ -1107,13 +1107,14 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
                goto err_irq;
        }
 
                goto err_irq;
        }
 
+       /* initialize power supplies, gpios, etc */
        if (pdata->init != NULL) {
                if (pdata->init(&pdev->dev) != 0) {
        if (pdata->init != NULL) {
                if (pdata->init(&pdev->dev) != 0) {
-                       dev_dbg(mmc_dev(host->mmc),
-                               "Unable to configure MMC IRQs\n");
+                       dev_dbg(mmc_dev(host->mmc), "late init error\n");
                        goto err_irq_cd_init;
                }
        }
                        goto err_irq_cd_init;
                }
        }
+       mmc->ocr_avail = mmc_slot(host).ocr_mask;
 
        /* Request IRQ for card detect */
        if ((mmc_slot(host).card_detect_irq)) {
 
        /* Request IRQ for card detect */
        if ((mmc_slot(host).card_detect_irq)) {
index 5011fa73f91886302135ea08904908c2c6f89461..6b172a4f88627607912887178e9b176161e9dc2a 100644 (file)
@@ -354,7 +354,7 @@ static int parse_cmdline_partitions(struct mtd_info *master,
  *
  * This function needs to be visible for bootloaders.
  */
  *
  * This function needs to be visible for bootloaders.
  */
-static int mtdpart_setup(char *s)
+int mtdpart_setup(char *s)
 {
        cmdline = s;
        return 1;
 {
        cmdline = s;
        return 1;
index 82923bd2d9c5444166c53552a409f27ebb646e50..5d590fe58d735a34d1d31b5f46e1b29bf922547b 100644 (file)
@@ -445,6 +445,13 @@ config MTD_CEIVA
          PhotoMax Digital Picture Frame.
          If you have such a device, say 'Y'.
 
          PhotoMax Digital Picture Frame.
          If you have such a device, say 'Y'.
 
+config MTD_NOR_TOTO
+       tristate "NOR Flash device on TOTO board"
+       depends on ARCH_OMAP && OMAP_TOTO
+       help
+         This enables access to the NOR flash on the Texas Instruments
+         TOTO board.
+
 config MTD_H720X
        tristate "Hynix evaluation board mappings"
        depends on MTD_CFI && ( ARCH_H7201 || ARCH_H7202 )
 config MTD_H720X
        tristate "Hynix evaluation board mappings"
        depends on MTD_CFI && ( ARCH_H7201 || ARCH_H7202 )
index 2dbc1bec848808559f4bc52995eda5ee06fb8dad..a39401735ba6ae7f6cd6980d763e340e1e73abbe 100644 (file)
@@ -52,6 +52,7 @@ obj-$(CONFIG_MTD_NETtel)      += nettel.o
 obj-$(CONFIG_MTD_SCB2_FLASH)   += scb2_flash.o
 obj-$(CONFIG_MTD_H720X)                += h720x-flash.o
 obj-$(CONFIG_MTD_SBC8240)      += sbc8240.o
 obj-$(CONFIG_MTD_SCB2_FLASH)   += scb2_flash.o
 obj-$(CONFIG_MTD_H720X)                += h720x-flash.o
 obj-$(CONFIG_MTD_SBC8240)      += sbc8240.o
+obj-$(CONFIG_MTD_NOR_TOTO)     += omap-toto-flash.o
 obj-$(CONFIG_MTD_IXP4XX)       += ixp4xx.o
 obj-$(CONFIG_MTD_IXP2000)      += ixp2000.o
 obj-$(CONFIG_MTD_WRSBC8260)    += wr_sbc82xx_flash.o
 obj-$(CONFIG_MTD_IXP4XX)       += ixp4xx.o
 obj-$(CONFIG_MTD_IXP2000)      += ixp2000.o
 obj-$(CONFIG_MTD_WRSBC8260)    += wr_sbc82xx_flash.o
index a24478102b118920e1dfd423fa082fa432576345..8cc92958e5e9a025c6895ea0a2566d4f3e20c510 100644 (file)
@@ -144,11 +144,12 @@ out_free_info:
 static int __exit omapflash_remove(struct platform_device *pdev)
 {
        struct omapflash_info *info = platform_get_drvdata(pdev);
 static int __exit 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) {
 
        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
                        del_mtd_partitions(info->mtd);
                        kfree(info->parts);
                } else
index 890936d0275ed21b957e2a46a9af52764a1fe28b..80b691b67b5ef49819c0105760baf40895b3368e 100644 (file)
@@ -74,6 +74,31 @@ config MTD_NAND_AMS_DELTA
        help
          Support for NAND flash on Amstrad E3 (Delta).
 
        help
          Support for NAND flash on Amstrad E3 (Delta).
 
+config MTD_NAND_OMAP2
+       tristate "NAND Flash device on OMAP2 and OMAP3"
+       depends on ARM && MTD_NAND && (ARCH_OMAP2 || ARCH_OMAP3)
+       help
+          Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms.
+
+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_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.
+
+config MTD_NAND_TOTO
+       tristate "NAND Flash device on TOTO board"
+       depends on ARCH_OMAP && BROKEN
+       help
+         Support for NAND flash on Texas Instruments Toto platform.
+
 config MTD_NAND_TS7250
        tristate "NAND Flash device on TS-7250 board"
        depends on MACH_TS72XX
 config MTD_NAND_TS7250
        tristate "NAND Flash device on TS-7250 board"
        depends on MACH_TS72XX
index d33860ac42c396a4f500d6498daf9559787b50c7..9bf5b1da2a4c2eab08c5454488cc4f5f5da3141a 100644 (file)
@@ -8,6 +8,7 @@ obj-$(CONFIG_MTD_NAND_IDS)              += nand_ids.o
 obj-$(CONFIG_MTD_NAND_CAFE)            += cafe_nand.o
 obj-$(CONFIG_MTD_NAND_SPIA)            += spia.o
 obj-$(CONFIG_MTD_NAND_AMS_DELTA)       += ams-delta.o
 obj-$(CONFIG_MTD_NAND_CAFE)            += cafe_nand.o
 obj-$(CONFIG_MTD_NAND_SPIA)            += spia.o
 obj-$(CONFIG_MTD_NAND_AMS_DELTA)       += ams-delta.o
+obj-$(CONFIG_MTD_NAND_TOTO)            += toto.o
 obj-$(CONFIG_MTD_NAND_AUTCPU12)                += autcpu12.o
 obj-$(CONFIG_MTD_NAND_EDB7312)         += edb7312.o
 obj-$(CONFIG_MTD_NAND_AU1550)          += au1550nd.o
 obj-$(CONFIG_MTD_NAND_AUTCPU12)                += autcpu12.o
 obj-$(CONFIG_MTD_NAND_EDB7312)         += edb7312.o
 obj-$(CONFIG_MTD_NAND_AU1550)          += au1550nd.o
@@ -25,6 +26,9 @@ obj-$(CONFIG_MTD_NAND_CS553X)         += cs553x_nand.o
 obj-$(CONFIG_MTD_NAND_NDFC)            += ndfc.o
 obj-$(CONFIG_MTD_NAND_ATMEL)           += atmel_nand.o
 obj-$(CONFIG_MTD_NAND_GPIO)            += gpio.o
 obj-$(CONFIG_MTD_NAND_NDFC)            += ndfc.o
 obj-$(CONFIG_MTD_NAND_ATMEL)           += atmel_nand.o
 obj-$(CONFIG_MTD_NAND_GPIO)            += gpio.o
+obj-$(CONFIG_MTD_NAND_OMAP)            += omap-nand-flash.o
+obj-$(CONFIG_MTD_NAND_OMAP2)           += omap2.o
+obj-$(CONFIG_MTD_NAND_OMAP_HW)         += omap-hw.o
 obj-$(CONFIG_MTD_NAND_CM_X270)         += cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)   += excite_nandflash.o
 obj-$(CONFIG_MTD_NAND_PXA3xx)          += pxa3xx_nand.o
 obj-$(CONFIG_MTD_NAND_CM_X270)         += cmx270_nand.o
 obj-$(CONFIG_MTD_NAND_BASLER_EXCITE)   += excite_nandflash.o
 obj-$(CONFIG_MTD_NAND_PXA3xx)          += pxa3xx_nand.o
diff --git a/drivers/mtd/nand/omap-hw.c b/drivers/mtd/nand/omap-hw.c
new file mode 100644 (file)
index 0000000..7eb9894
--- /dev/null
@@ -0,0 +1,859 @@
+/*
+ *  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 <jarkko.lavinen@nokia.com> and
+ *          Juha Yrjölä <juha.yrjola@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+
+#include <mach/board.h>
+#include <mach/dma.h>
+
+#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 = OMAP1_IO_ADDRESS(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;
+       dma_cache_maint(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 (!is_write)
+               dma_cache_maint(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 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->writesize) {
+                       /* OOB area */
+                       column -= mtd->writesize;
+                       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->writesize;
+               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;
+
+       /* Ex NAND_ECC_HW12_2048 */
+       if ((this->ecc.mode == NAND_ECC_HW) && (this->ecc.size  == 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;
+       /* Ex NAND_ECC_HW12_2048 */
+       if ((this->ecc.mode == NAND_ECC_HW) && (this->ecc.size  == 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_str_config *cfg;
+       char *part_str = NULL;
+       size_t part_str_len;
+       int c;
+
+       cfg = omap_get_var_config(OMAP_TAG_FLASH_PART_STR, &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->select_chip   = omap_nand_select_chip;
+       this->dev_ready     = omap_nand_dev_ready;
+       this->chip_delay    = 0;
+       this->ecc.mode      = NAND_ECC_HW;
+       this->ecc.bytes     = 3;
+       this->ecc.size      = 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->ecc.calculate = omap_nand_calculate_ecc;
+       this->ecc.correct   = omap_nand_correct_data;
+       this->ecc.hwctl     = 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->ecc.mode = NAND_ECC_HW;
+               this->ecc.steps = 1;
+               this->ecc.size = 2048;
+               this->ecc.bytes = 12;
+               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 (file)
index 0000000..5263b19
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * drivers/mtd/nand/omap-nand-flash.c
+ *
+ * Copyright (c) 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
+ * 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 <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/flash.h>
+#include <mach/tc.h>
+
+#include <mach/nand.h>
+
+#define        DRIVER_NAME     "omapnand"
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+struct omap_nand_info {
+       struct omap_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!!
+ *
+ *     ctrl:
+ *     NAND_NCE: bit 0 - don't care
+ *     NAND_CLE: bit 1 -> bit 1  (0x0002)
+ *     NAND_ALE: bit 2 -> bit 2  (0x0004)
+ */
+
+static void omap_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+       struct nand_chip *chip = mtd->priv;
+       unsigned long mask;
+
+       if (cmd == NAND_CMD_NONE)
+               return;
+
+       mask = (ctrl & NAND_CLE) ? 0x02 : 0;
+       if (ctrl & NAND_ALE)
+               mask |= 0x04;
+       writeb(cmd, (unsigned long)chip->IO_ADDR_W | mask);
+}
+
+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 omap_nand_platform_data  *pdata = pdev->dev.platform_data;
+       struct resource                 *res = pdev->resource;
+       unsigned long                   size = res->end - res->start + 1;
+       int                             err;
+
+       info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       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.cmd_ctrl = omap_nand_hwcontrol;
+       info->nand.ecc.mode = 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 = dev_name(&pdev->dev);
+       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 <jzhang@ti.com> (and others)");
+MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
+
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c
new file mode 100644 (file)
index 0000000..516da8f
--- /dev/null
@@ -0,0 +1,755 @@
+/*
+ * drivers/mtd/nand/omap2.c
+ *
+ * Copyright (c) 2004 Texas Instruments, Jian Zhang <jzhang@ti.com>
+ * Copyright (c) 2004 Micron Technology Inc.
+ * 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 <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+
+#include <asm/dma.h>
+
+#include <mach/gpmc.h>
+#include <mach/nand.h>
+
+#define GPMC_IRQ_STATUS                0x18
+#define GPMC_ECC_CONFIG                0x1F4
+#define GPMC_ECC_CONTROL       0x1F8
+#define GPMC_ECC_SIZE_CONFIG   0x1FC
+#define GPMC_ECC1_RESULT       0x200
+
+#define        DRIVER_NAME     "omap2-nand"
+#define        NAND_IO_SIZE    SZ_4K
+
+#define        NAND_WP_ON      1
+#define        NAND_WP_OFF     0
+#define NAND_WP_BIT    0x00000010
+#define WR_RD_PIN_MONITORING   0x00600000
+
+#define        GPMC_BUF_FULL   0x00000001
+#define        GPMC_BUF_EMPTY  0x00000000
+
+#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)
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+struct omap_nand_info {
+       struct nand_hw_control          controller;
+       struct omap_nand_platform_data  *pdata;
+       struct mtd_info                 mtd;
+       struct mtd_partition            *parts;
+       struct nand_chip                nand;
+       struct platform_device          *pdev;
+
+       int                             gpmc_cs;
+       unsigned long                   phys_base;
+       void __iomem                    *gpmc_cs_baseaddr;
+       void __iomem                    *gpmc_baseaddr;
+};
+
+/*
+ * omap_nand_wp - This function enable or disable the Write Protect feature on
+ * NAND device
+ * @mtd: MTD device structure
+ * @mode: WP ON/OFF
+ */
+static void omap_nand_wp(struct mtd_info *mtd, int mode)
+{
+       struct omap_nand_info *info = container_of(mtd,
+                                               struct omap_nand_info, mtd);
+
+       unsigned long config = __raw_readl(info->gpmc_baseaddr + GPMC_CONFIG);
+
+       if (mode)
+               config &= ~(NAND_WP_BIT);       /* WP is ON */
+       else
+               config |= (NAND_WP_BIT);        /* WP is OFF */
+
+       __raw_writel(config, (info->gpmc_baseaddr + GPMC_CONFIG));
+}
+
+/*
+ * hardware specific access to control-lines
+ * NOTE: boards may use different bits for these!!
+ *
+ * ctrl:
+ * NAND_NCE: bit 0 - don't care
+ * NAND_CLE: bit 1 -> Command Latch
+ * NAND_ALE: bit 2 -> Address Latch
+ */
+static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
+{
+       struct omap_nand_info *info = container_of(mtd,
+                                       struct omap_nand_info, mtd);
+       switch (ctrl) {
+       case NAND_CTRL_CHANGE | NAND_CTRL_CLE:
+               info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_COMMAND;
+               info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_DATA;
+               break;
+
+       case NAND_CTRL_CHANGE | NAND_CTRL_ALE:
+               info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_ADDRESS;
+               info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_DATA;
+               break;
+
+       case NAND_CTRL_CHANGE | NAND_NCE:
+               info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_DATA;
+               info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_DATA;
+               break;
+       }
+
+       if (cmd != NAND_CMD_NONE)
+               __raw_writeb(cmd, info->nand.IO_ADDR_W);
+}
+
+/*
+ * omap_read_buf16 - read data from NAND controller into buffer
+ * @mtd: MTD device structure
+ * @buf: buffer to store date
+ * @len: number of bytes to read
+ */
+static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
+{
+       struct nand_chip *nand = mtd->priv;
+
+       __raw_readsw(nand->IO_ADDR_R, buf, len / 2);
+}
+
+/*
+ * omap_write_buf16 - write buffer to NAND controller
+ * @mtd: MTD device structure
+ * @buf: data buffer
+ * @len: number of bytes to write
+ */
+static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len)
+{
+       struct omap_nand_info *info = container_of(mtd,
+                                               struct omap_nand_info, mtd);
+       u16 *p = (u16 *) buf;
+
+       /* FIXME try bursts of writesw() or DMA ... */
+       len >>= 1;
+
+       while (len--) {
+               writew(*p++, info->nand.IO_ADDR_W);
+
+               while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr +
+                                               GPMC_STATUS) & GPMC_BUF_FULL));
+       }
+}
+/*
+ * omap_verify_buf - Verify chip data against buffer
+ * @mtd: MTD device structure
+ * @buf: buffer containing the data to compare
+ * @len: number of bytes to compare
+ */
+static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len)
+{
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       u16 *p = (u16 *) buf;
+
+       len >>= 1;
+
+       while (len--) {
+
+               if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R)))
+                       return -EFAULT;
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_MTD_NAND_OMAP_HWECC
+/*
+ * omap_hwecc_init-Initialize the Hardware ECC for NAND flash in GPMC controller
+ * @mtd: MTD device structure
+ */
+static void omap_hwecc_init(struct mtd_info *mtd)
+{
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       register struct nand_chip *chip = mtd->priv;
+       unsigned long val = 0x0;
+
+       /* Read from ECC Control Register */
+       val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+       /* Clear all ECC | Enable Reg1 */
+       val = ((0x00000001<<8) | 0x00000001);
+       __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+
+       /* Read from ECC Size Config Register */
+       val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
+       /* ECCSIZE1=512 | Select eccResultsize[0-3] */
+       val = ((((chip->ecc.size >> 1) - 1) << 22) | (0x0000000F));
+       __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG);
+}
+
+/*
+ * gen_true_ecc - This function will generate true ECC value, which can be used
+ * when correcting data read from NAND flash memory core
+ * @ecc_buf: buffer to store ecc code
+ */
+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));
+}
+
+/*
+ * omap_compare_ecc - 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
+ * @ecc_data1:  ecc code from nand spare area
+ * @ecc_data2:  ecc code from hardware register obtained from hardware ecc
+ * @page_data:  page data
+ */
+static int omap_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 11:
+               /* UN-Correctable error */
+               DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\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;
+       }
+}
+
+/*
+ * omap_correct_data - Compares the ecc read from nand spare area with ECC
+ * registers values and corrects one bit error if it has occured
+ * @mtd: MTD device structure
+ * @dat: page data
+ * @read_ecc: ecc read from nand flash
+ * @calc_ecc: ecc read from ECC registers
+ */
+static int omap_correct_data(struct mtd_info *mtd, u_char * dat,
+                               u_char * read_ecc, u_char * calc_ecc)
+{
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       int blockCnt = 0, i = 0, ret = 0;
+
+       /* Ex NAND_ECC_HW12_2048 */
+       if ((info->nand.ecc.mode == NAND_ECC_HW) &&
+                       (info->nand.ecc.size  == 2048))
+               blockCnt = 4;
+       else
+               blockCnt = 1;
+
+       for (i = 0; i < blockCnt; i++) {
+               if (memcmp(read_ecc, calc_ecc, 3) != 0) {
+                       ret = omap_compare_ecc(read_ecc, calc_ecc, dat);
+                       if (ret < 0) return ret;
+               }
+               read_ecc += 3;
+               calc_ecc += 3;
+               dat      += 512;
+       }
+       return 0;
+}
+
+/*
+ * omap_calcuate_ecc - 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.
+ * @mtd: MTD device structure
+ * @dat: The pointer to data on which ecc is computed
+ * @ecc_code: The ecc_code buffer
+ */
+static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+                               u_char *ecc_code)
+{
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       unsigned long val = 0x0;
+       unsigned long reg;
+
+       /* Start Reading from HW ECC1_Result = 0x200 */
+       reg = (unsigned long)(info->gpmc_baseaddr + GPMC_ECC1_RESULT);
+       val = __raw_readl(reg);
+       *ecc_code++ = val;          /* P128e, ..., P1e */
+       *ecc_code++ = val >> 16;    /* P128o, ..., P1o */
+       /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */
+       *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0);
+       reg += 4;
+
+       return 0;
+}
+
+/*
+ * omap_enable_hwecc - This function enables the hardware ecc functionality
+ * @mtd: MTD device structure
+ * @mode: Read/Write mode
+ */
+static void omap_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       register struct nand_chip *chip = mtd->priv;
+       unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0;
+       unsigned long val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONFIG);
+
+       switch (mode) {
+       case NAND_ECC_READ    :
+               __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+               /* (ECC 16 or 8 bit col) | ( CS  )  | ECC Enable */
+               val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
+               break;
+       case NAND_ECC_READSYN :
+                __raw_writel(0x100, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+               /* (ECC 16 or 8 bit col) | ( CS  )  | ECC Enable */
+               val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
+               break;
+       case NAND_ECC_WRITE   :
+               __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL);
+               /* (ECC 16 or 8 bit col) | ( CS  )  | ECC Enable */
+               val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1);
+               break;
+       default:
+               DEBUG(MTD_DEBUG_LEVEL0, "Error: Unrecognized Mode[%d]!\n",
+                                       mode);
+               break;
+       }
+
+       __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONFIG);
+}
+#endif
+
+/*
+ * omap_wait - Wait function is called during Program and erase
+ * operations and the way it is called from MTD layer, we should wait
+ * till the NAND chip is ready after the programming/erase operation
+ * has completed.
+ * @mtd: MTD device structure
+ * @chip: NAND Chip structure
+ */
+static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+       register struct nand_chip *this = mtd->priv;
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       int status = 0;
+
+       this->IO_ADDR_W = (void *) info->gpmc_cs_baseaddr +
+                                               GPMC_CS_NAND_COMMAND;
+       this->IO_ADDR_R = (void *) info->gpmc_cs_baseaddr + GPMC_CS_NAND_DATA;
+
+       while (!(status & 0x40)) {
+                __raw_writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W);
+               status = __raw_readb(this->IO_ADDR_R);
+       }
+       return status;
+}
+
+/*
+ * omap_dev_ready - calls the platform specific dev_ready function
+ * @mtd: MTD device structure
+ */
+static int omap_dev_ready(struct mtd_info *mtd)
+{
+       struct omap_nand_info *info = container_of(mtd, struct omap_nand_info,
+                                                       mtd);
+       unsigned int val = __raw_readl(info->gpmc_baseaddr + GPMC_IRQ_STATUS);
+
+       if ((val & 0x100) == 0x100) {
+               /* Clear IRQ Interrupt */
+               val |= 0x100;
+               val &= ~(0x0);
+               __raw_writel(val, info->gpmc_baseaddr + GPMC_IRQ_STATUS);
+       } else {
+               unsigned int cnt = 0;
+               while (cnt++ < 0x1FF) {
+                       if  ((val & 0x100) == 0x100)
+                               return 0;
+                       val = __raw_readl(info->gpmc_baseaddr +
+                                                       GPMC_IRQ_STATUS);
+               }
+       }
+
+       return 1;
+}
+
+static int __devinit omap_nand_probe(struct platform_device *pdev)
+{
+       struct omap_nand_info           *info;
+       struct omap_nand_platform_data  *pdata;
+       int                             err;
+       unsigned long                   val;
+
+
+       pdata = pdev->dev.platform_data;
+       if (pdata == NULL) {
+               dev_err(&pdev->dev, "platform data missing\n");
+               return -ENODEV;
+       }
+
+       info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL);
+       if (!info) return -ENOMEM;
+
+       platform_set_drvdata(pdev, info);
+
+       spin_lock_init(&info->controller.lock);
+       init_waitqueue_head(&info->controller.wq);
+
+       info->pdev = pdev;
+
+       info->gpmc_cs           = pdata->cs;
+       info->gpmc_baseaddr     = pdata->gpmc_baseaddr;
+       info->gpmc_cs_baseaddr  = pdata->gpmc_cs_baseaddr;
+
+       info->mtd.priv          = &info->nand;
+       info->mtd.name          = dev_name(&pdev->dev);
+       info->mtd.owner         = THIS_MODULE;
+
+       err = gpmc_cs_request(info->gpmc_cs, NAND_IO_SIZE, &info->phys_base);
+       if (err < 0) {
+               dev_err(&pdev->dev, "Cannot request GPMC CS\n");
+               goto out_free_info;
+       }
+
+       /* Enable RD PIN Monitoring Reg */
+       if (pdata->dev_ready) {
+               val  = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1);
+               val |= WR_RD_PIN_MONITORING;
+               gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG1, val);
+       }
+
+       val  = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG7);
+       val &= ~(0xf << 8);
+       val |=  (0xc & 0xf) << 8;
+       gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG7, val);
+
+       /* NAND write protect off */
+       omap_nand_wp(&info->mtd, NAND_WP_OFF);
+
+       if (!request_mem_region(info->phys_base, NAND_IO_SIZE,
+                               pdev->dev.driver->name)) {
+               err = -EBUSY;
+               goto out_free_cs;
+       }
+
+       info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE);
+       if (!info->nand.IO_ADDR_R) {
+               err = -ENOMEM;
+               goto out_release_mem_region;
+       }
+       info->nand.controller = &info->controller;
+
+       info->nand.IO_ADDR_W = info->nand.IO_ADDR_R;
+       info->nand.cmd_ctrl  = omap_hwcontrol;
+
+       /* REVISIT:  only supports 16-bit NAND flash */
+
+       info->nand.read_buf   = omap_read_buf16;
+       info->nand.write_buf  = omap_write_buf16;
+       info->nand.verify_buf = omap_verify_buf;
+
+       /*
+       * If RDY/BSY line is connected to OMAP then use the omap ready funcrtion
+       * and the generic nand_wait function which reads the status register
+       * after monitoring the RDY/BSY line.Otherwise use a standard chip delay
+       * which is slightly more than tR (AC Timing) of the NAND device and read
+       * status register until you get a failure or success
+       */
+       if (pdata->dev_ready) {
+               info->nand.dev_ready = omap_dev_ready;
+               info->nand.chip_delay = 0;
+       } else {
+               info->nand.waitfunc = omap_wait;
+               info->nand.chip_delay = 50;
+       }
+
+       info->nand.options  |= NAND_SKIP_BBTSCAN;
+       if ((gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1) & 0x3000)
+                                                               == 0x1000)
+               info->nand.options  |= NAND_BUSWIDTH_16;
+
+#ifdef CONFIG_MTD_NAND_OMAP_HWECC
+       info->nand.ecc.bytes            = 3;
+       info->nand.ecc.size             = 512;
+       info->nand.ecc.calculate        = omap_calculate_ecc;
+       info->nand.ecc.hwctl            = omap_enable_hwecc;
+       info->nand.ecc.correct          = omap_correct_data;
+       info->nand.ecc.mode             = NAND_ECC_HW;
+
+       /* init HW ECC */
+       omap_hwecc_init(&info->mtd);
+#else
+       info->nand.ecc.mode = NAND_ECC_SOFT;
+#endif
+
+       /* DIP switches on some 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_release_mem_region;
+               }
+       }
+
+#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 (pdata->parts)
+               add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
+       else
+#endif
+               add_mtd_device(&info->mtd);
+
+       platform_set_drvdata(pdev, &info->mtd);
+
+       return 0;
+
+out_release_mem_region:
+       release_mem_region(info->phys_base, NAND_IO_SIZE);
+out_free_cs:
+       gpmc_cs_free(info->gpmc_cs);
+out_free_info:
+       kfree(info);
+
+       return err;
+}
+
+static int omap_nand_remove(struct platform_device *pdev)
+{
+       struct mtd_info *mtd = platform_get_drvdata(pdev);
+       struct omap_nand_info *info = mtd->priv;
+
+       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->mtd);
+       return 0;
+}
+
+static struct platform_driver omap_nand_driver = {
+       .probe          = omap_nand_probe,
+       .remove         = omap_nand_remove,
+       .driver         = {
+               .name   = DRIVER_NAME,
+               .owner  = THIS_MODULE,
+       },
+};
+MODULE_ALIAS(DRIVER_NAME);
+
+static int __init omap_nand_init(void)
+{
+       printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME);
+       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_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards");
index e6317557a531e342d516f02ba8daa8bf360b2753..58d0c811afdfd2a1f87dd48da17e3e215596c0b1 100644 (file)
@@ -342,5 +342,15 @@ config MCS_FIR
          To compile it as a module, choose M here: the module will be called
          mcs7780.
 
          To compile it as a module, choose M here: the module will be called
          mcs7780.
 
+config OMAP_IR
+       tristate "OMAP IrDA(SIR/MIR/FIR)"
+       depends on IRDA && ARCH_OMAP
+       select GPIOEXPANDER_OMAP if MACH_OMAP_H3
+        help
+         Say Y here if you want to build support for the Texas Instruments
+         OMAP IrDA device driver, which supports SIR/MIR/FIR. This driver
+         relies on platform specific helper routines so available capabilities
+         may vary from one OMAP target to another.
+
 endmenu
 
 endmenu
 
index 5d20fde32a246fd41081594e6559239b60c4bf23..49e6f062e05d649d112ecc2e535ccc34095b2902 100644 (file)
@@ -18,6 +18,7 @@ obj-$(CONFIG_VLSI_FIR)                += vlsi_ir.o
 obj-$(CONFIG_VIA_FIR)          += via-ircc.o
 obj-$(CONFIG_PXA_FICP)         += pxaficp_ir.o
 obj-$(CONFIG_MCS_FIR)          += mcs7780.o
 obj-$(CONFIG_VIA_FIR)          += via-ircc.o
 obj-$(CONFIG_PXA_FICP)         += pxaficp_ir.o
 obj-$(CONFIG_MCS_FIR)          += mcs7780.o
+obj-$(CONFIG_OMAP_IR)          += omap-ir.o
 obj-$(CONFIG_AU1000_FIR)       += au1k_ir.o
 # SIR drivers
 obj-$(CONFIG_IRTTY_SIR)                += irtty-sir.o  sir-dev.o
 obj-$(CONFIG_AU1000_FIR)       += au1k_ir.o
 # SIR drivers
 obj-$(CONFIG_IRTTY_SIR)                += irtty-sir.o  sir-dev.o
diff --git a/drivers/net/irda/omap-ir.c b/drivers/net/irda/omap-ir.c
new file mode 100644 (file)
index 0000000..59401b7
--- /dev/null
@@ -0,0 +1,901 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *
+ *     Infra-red driver for the OMAP1610-H2 and OMAP1710-H3 and H4 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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/slab.h>
+#include <linux/rtnetlink.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/dma-mapping.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+
+#include <net/irda/irda.h>
+#include <net/irda/irmod.h>
+#include <net/irda/wrapper.h>
+#include <net/irda/irda_device.h>
+
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <mach/hardware.h>
+#include <asm/serial.h>
+#include <asm/mach-types.h>
+#include <mach/dma.h>
+#include <mach/mux.h>
+#include <mach/gpio.h>
+#include <mach/irda.h>
+
+#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(omap_ir)                ((omap_ir)->speed >= 4000000)
+
+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 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 *omap_ir)
+{
+       /* Configure DMA */
+       omap_set_dma_src_params(omap_ir->rx_dma_channel, 0x3, 0x0,
+                               omap_ir->pdata->src_start,
+                               0, 0);
+
+       omap_enable_dma_irq(omap_ir->rx_dma_channel, 0x01);
+
+       omap_set_dma_dest_params(omap_ir->rx_dma_channel, 0x0, 0x1,
+                               omap_ir->rx_buf_dma_phys,
+                               0, 0);
+
+       omap_set_dma_transfer_params(omap_ir->rx_dma_channel, 0x0,
+                               IRDA_SKB_MAX_MTU, 0x1,
+                               0x0, omap_ir->pdata->rx_trigger, 0);
+
+       omap_start_dma(omap_ir->rx_dma_channel);
+}
+
+static void omap_start_tx_dma(struct omap_irda *omap_ir, int size)
+{
+       /* Configure DMA */
+       omap_set_dma_dest_params(omap_ir->tx_dma_channel, 0x03, 0x0,
+                               omap_ir->pdata->dest_start, 0, 0);
+
+       omap_enable_dma_irq(omap_ir->tx_dma_channel, 0x01);
+
+       omap_set_dma_src_params(omap_ir->tx_dma_channel, 0x0, 0x1,
+                               omap_ir->tx_buf_dma_phys,
+                               0, 0);
+
+       omap_set_dma_transfer_params(omap_ir->tx_dma_channel, 0x0, size, 0x1,
+                               0x0, omap_ir->pdata->tx_trigger, 0);
+
+       /* Start DMA */
+       omap_start_dma(omap_ir->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 *omap_ir = netdev_priv(dev);
+
+       printk(KERN_ERR "RX Transfer error or very big frame\n");
+
+       /* Clear interrupts */
+       uart_reg_in(UART3_IIR);
+
+       omap_ir->stats.rx_frame_errors++;
+
+       uart_reg_in(UART3_RESUME);
+
+       /* Re-init RX DMA */
+       omap_irda_start_rx_dma(omap_ir);
+}
+
+/* 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 *omap_ir = netdev_priv(dev);
+
+       /*Stop DMA controller */
+       omap_stop_dma(omap_ir->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 *omap_ir = netdev_priv(dev);
+
+       /* 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 (omap_ir->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);
+               omap_ir->pdata->transceiver_mode(omap_ir->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 *omap_ir)
+{
+       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 net_device *dev = dev_id;
+       struct omap_irda *omap_ir = netdev_priv(dev);
+       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");
+
+               omap_ir->stats.tx_packets++;
+
+               if (omap_ir->newspeed) {
+                       omap_irda_set_speed(dev, omap_ir->newspeed);
+                       omap_ir->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(omap_ir->rx_dma_channel);
+
+       status = uart_reg_in(UART3_SFLSR);      /* Take a frame status */
+
+       if (status != 0) {      /* Bad frame? */
+               omap_ir->stats.rx_frame_errors++;
+               uart_reg_in(UART3_RESUME);
+       } else {
+               /* We got a frame! */
+               skb = dev_alloc_skb(IRDA_SKB_MAX_MTU);
+
+               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_get_dma_dst_pos(omap_ir->rx_dma_channel) -
+                                               omap_ir->rx_buf_dma_phys;
+
+               if (!IS_FIR(omap_ir))
+                       /* Copy DMA buffer to skb */
+                       memcpy(skb_put(skb, w - 2), omap_ir->rx_buf_dma_virt,
+                                       w - 2);
+               else
+                       /* Copy DMA buffer to skb */
+                       memcpy(skb_put(skb, w - 4), omap_ir->rx_buf_dma_virt,
+                                       w - 4);
+
+               skb->dev = dev;
+               skb_reset_mac_header(skb);
+               skb->protocol = htons(ETH_P_IRDA);
+               omap_ir->stats.rx_packets++;
+               omap_ir->stats.rx_bytes += skb->len;
+               netif_receive_skb(skb); /* Send data to upper level */
+       }
+
+       /* Re-init RX DMA */
+       omap_irda_start_rx_dma(omap_ir);
+
+       dev->last_rx = jiffies;
+
+       return IRQ_HANDLED;
+}
+
+static int omap_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       struct omap_irda *omap_ir = netdev_priv(dev);
+       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 != omap_ir->speed && speed != -1)
+               omap_ir->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 (omap_ir->newspeed) {
+                       omap_ir->newspeed = 0;
+                       omap_irda_set_speed(dev, speed);
+               }
+               dev_kfree_skb(skb);
+               return 0;
+       }
+
+       netif_stop_queue(dev);
+
+       /* Copy skb data to DMA buffer */
+       skb_copy_from_linear_data(skb, omap_ir->tx_buf_dma_virt, skb->len);
+
+       /* Copy skb data to DMA buffer */
+       omap_ir->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(omap_ir, 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 *omap_ir = netdev_priv(dev);
+       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 (omap_ir->open)
+                               ret = omap_irda_set_speed(dev,
+                                               rq->ifr_baudrate);
+                       else {
+                               printk(KERN_ERR "omap_ir: 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 = 0;
+               break;
+
+       default:
+               break;
+       }
+
+       return ret;
+}
+
+static struct net_device_stats *omap_irda_stats(struct net_device *dev)
+{
+       struct omap_irda *omap_ir = netdev_priv(dev);
+       return &omap_ir->stats;
+}
+
+static int omap_irda_start(struct net_device *dev)
+{
+       struct omap_irda *omap_ir = netdev_priv(dev);
+       int err;
+
+       omap_ir->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(omap_ir->pdata->rx_channel, "IrDA Rx DMA",
+                       (void *)omap_irda_rx_dma_callback,
+                       dev, &(omap_ir->rx_dma_channel))) {
+               printk(KERN_ERR "Failed to request IrDA Rx DMA\n");
+               goto err_irq;
+       }
+
+       if (omap_request_dma(omap_ir->pdata->tx_channel, "IrDA Tx DMA",
+                       (void *)omap_irda_tx_dma_callback,
+                       dev, &(omap_ir->tx_dma_channel))) {
+               printk(KERN_ERR "Failed to request IrDA Tx DMA\n");
+               goto err_irq;
+       }
+
+       /* Allocate TX and RX buffers for DMA channels */
+       omap_ir->rx_buf_dma_virt =
+               dma_alloc_coherent(NULL, IRDA_SKB_MAX_MTU,
+                               &(omap_ir->rx_buf_dma_phys),
+                               GFP_KERNEL);
+
+       if (!omap_ir->rx_buf_dma_virt) {
+               printk(KERN_ERR "Unable to allocate memory for rx_buf_dma\n");
+               goto err_irq;
+       }
+
+       omap_ir->tx_buf_dma_virt =
+               dma_alloc_coherent(NULL, IRDA_SIR_MAX_FRAME,
+                               &(omap_ir->tx_buf_dma_phys),
+                               GFP_KERNEL);
+
+       if (!omap_ir->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 (omap_ir->pdata->select_irda)
+               omap_ir->pdata->select_irda(omap_ir->dev, IR_SEL);
+
+       err = omap_irda_startup(dev);
+
+       if (err)
+               goto err_startup;
+
+       omap_irda_set_speed(dev, omap_ir->speed = 9600);
+
+       /*
+        * Open a new IrLAP layer instance.
+        */
+       omap_ir->irlap = irlap_open(dev, &omap_ir->qos, "omap_sir");
+
+       err = -ENOMEM;
+       if (!omap_ir->irlap)
+               goto err_irlap;
+
+       /* Now enable the interrupt and start the queue */
+       omap_ir->open = 1;
+
+       /* Start RX DMA */
+       omap_irda_start_rx_dma(omap_ir);
+
+       enable_irq(dev->irq);
+       netif_start_queue(dev);
+
+       return 0;
+
+err_irlap:
+       omap_ir->open = 0;
+       omap_irda_shutdown(omap_ir);
+err_startup:
+       dma_free_coherent(NULL, IRDA_SIR_MAX_FRAME,
+                       omap_ir->tx_buf_dma_virt, omap_ir->tx_buf_dma_phys);
+err_mem1:
+       dma_free_coherent(NULL, IRDA_SKB_MAX_MTU,
+                       omap_ir->rx_buf_dma_virt, omap_ir->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 *omap_ir = netdev_priv(dev);
+
+       disable_irq(dev->irq);
+
+       netif_stop_queue(dev);
+
+       omap_free_dma(omap_ir->rx_dma_channel);
+       omap_free_dma(omap_ir->tx_dma_channel);
+
+       if (omap_ir->rx_buf_dma_virt)
+               dma_free_coherent(NULL, IRDA_SKB_MAX_MTU,
+                               omap_ir->rx_buf_dma_virt,
+                               omap_ir->rx_buf_dma_phys);
+       if (omap_ir->tx_buf_dma_virt)
+               dma_free_coherent(NULL, IRDA_SIR_MAX_FRAME,
+                               omap_ir->tx_buf_dma_virt,
+                               omap_ir->tx_buf_dma_phys);
+
+       omap_irda_shutdown(omap_ir);
+
+       /* Stop IrLAP */
+       if (omap_ir->irlap) {
+               irlap_close(omap_ir->irlap);
+               omap_ir->irlap = NULL;
+       }
+
+       omap_ir->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 *omap_ir = netdev_priv(dev);
+       int divisor;
+       unsigned long flags;
+
+       /* Set IrDA speed */
+       if (speed <= 115200) {
+
+               local_irq_save(flags);
+
+               /* SIR mode */
+               if (omap_ir->pdata->transceiver_mode)
+                       omap_ir->pdata->transceiver_mode(omap_ir->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 (omap_ir->pdata->transceiver_mode)
+                       omap_ir->pdata->transceiver_mode(omap_ir->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 (omap_ir->pdata->transceiver_mode)
+                       omap_ir->pdata->transceiver_mode(omap_ir->dev,
+                                                       IR_FIRMODE);
+
+               local_irq_restore(flags);
+       }
+
+       omap_ir->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 *omap_ir = netdev_priv(dev);
+
+       if (!dev)
+               return 0;
+
+       if (omap_ir->open) {
+               /*
+                * Stop the transmit queue
+                */
+               netif_device_detach(dev);
+               disable_irq(dev->irq);
+               omap_irda_shutdown(omap_ir);
+       }
+       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 *omap_ir= netdev_priv(dev);
+
+       if (!dev)
+               return 0;
+
+       if (omap_ir->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 (omap_ir->newspeed) {
+                       omap_ir->speed = omap_ir->newspeed;
+                       omap_ir->newspeed = 0;
+               }
+
+               omap_irda_startup(dev);
+               omap_irda_set_speed(dev, omap_ir->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 *omap_ir;
+       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;
+
+
+       omap_ir = netdev_priv(dev);
+       omap_ir->dev = &pdev->dev;
+       omap_ir->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(&omap_ir->qos);
+
+       baudrate_mask = 0;
+       if (omap_ir->pdata->transceiver_cap & IR_SIRMODE)
+               baudrate_mask |= IR_9600|IR_19200|IR_38400|IR_57600|IR_115200;
+       if (omap_ir->pdata->transceiver_cap & IR_MIRMODE)
+               baudrate_mask |= IR_57600 | IR_1152000;
+       if (omap_ir->pdata->transceiver_cap & IR_FIRMODE)
+               baudrate_mask |= IR_4000000 << 8;
+
+       omap_ir->qos.baud_rate.bits &= baudrate_mask;
+       omap_ir->qos.min_turn_time.bits = 7;
+
+       irda_qos_bits_to_value(&omap_ir->qos);
+
+       /* Any better way to avoid this? No. */
+       if (machine_is_omap_h3() || machine_is_omap_h4())
+               INIT_DELAYED_WORK(&omap_ir->pdata->gpio_expa, 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");
+
index fdcbaf8dfa739618a5a8f25bc27f6c24222e8004..734bf1e82c4fb2b3a6e3867181fdb5fffd3136c4 100644 (file)
@@ -421,6 +421,11 @@ static inline void  smc_rcv(struct net_device *dev)
                dev->name, packet_number, status,
                packet_len, packet_len);
 
                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)) {
        back:
        if (unlikely(packet_len < 6 || status & RS_ERRORS)) {
                if (status & RS_TOOLONG && packet_len <= (1514 + 4 + 6)) {
index 33da1127992aca4c36cfa04b1dfbb7a354edf38d..aad68dbd486c0ebae7b258956892e8bac1b4ebef 100644 (file)
@@ -63,6 +63,13 @@ config BATTERY_TOSA
          Say Y to enable support for the battery on the Sharp Zaurus
          SL-6000 (tosa) models.
 
          Say Y to enable support for the battery on the Sharp Zaurus
          SL-6000 (tosa) models.
 
+config TWL4030_BCI_BATTERY
+       tristate "OMAP TWL4030 BCI Battery driver"
+       depends on TWL4030_CORE && TWL4030_MADC
+       help
+         Support for OMAP TWL4030 BCI Battery driver.
+         This driver can give support for TWL4030 Battery Charge Interface.
+
 config BATTERY_WM97XX
        bool "WM97xx generic battery driver"
        depends on TOUCHSCREEN_WM97XX=y
 config BATTERY_WM97XX
        bool "WM97xx generic battery driver"
        depends on TOUCHSCREEN_WM97XX=y
index 2fcf41d13e5cae530c79478578485fe59493cb8b..7e688e752bed86dc279da06b875f84a1a58a04f9 100644 (file)
@@ -21,6 +21,8 @@ obj-$(CONFIG_WM8350_POWER)    += wm8350_power.o
 obj-$(CONFIG_BATTERY_DS2760)   += ds2760_battery.o
 obj-$(CONFIG_BATTERY_PMU)      += pmu_battery.o
 obj-$(CONFIG_BATTERY_OLPC)     += olpc_battery.o
 obj-$(CONFIG_BATTERY_DS2760)   += ds2760_battery.o
 obj-$(CONFIG_BATTERY_PMU)      += pmu_battery.o
 obj-$(CONFIG_BATTERY_OLPC)     += olpc_battery.o
+obj-$(CONFIG_BATTERY_BQ27x00)   += bq27x00_battery.o
+obj-$(CONFIG_TWL4030_BCI_BATTERY)      += twl4030_bci_battery.o
 obj-$(CONFIG_BATTERY_TOSA)     += tosa_battery.o
 obj-$(CONFIG_BATTERY_WM97XX)   += wm97xx_battery.o
 obj-$(CONFIG_BATTERY_BQ27x00)  += bq27x00_battery.o
 obj-$(CONFIG_BATTERY_TOSA)     += tosa_battery.o
 obj-$(CONFIG_BATTERY_WM97XX)   += wm97xx_battery.o
 obj-$(CONFIG_BATTERY_BQ27x00)  += bq27x00_battery.o
diff --git a/drivers/power/twl4030_bci_battery.c b/drivers/power/twl4030_bci_battery.c
new file mode 100644 (file)
index 0000000..ddba62b
--- /dev/null
@@ -0,0 +1,1097 @@
+/*
+ * linux/drivers/power/twl4030_bci_battery.c
+ *
+ * OMAP2430/3430 BCI battery driver for Linux
+ *
+ * Copyright (C) 2008 Texas Instruments, Inc.
+ * Author: 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.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+#include <linux/power_supply.h>
+#include <linux/i2c/twl4030-madc.h>
+
+#define T2_BATTERY_VOLT                0x04
+#define T2_BATTERY_TEMP                0x06
+#define T2_BATTERY_CUR         0x08
+
+/* charger constants */
+#define NO_PW_CONN             0
+#define AC_PW_CONN             0x01
+#define USB_PW_CONN            0x02
+
+/* TWL4030_MODULE_USB */
+#define REG_POWER_CTRL         0x0AC
+#define OTG_EN                 0x020
+#define REG_PHY_CLK_CTRL       0x0FE
+#define REG_PHY_CLK_CTRL_STS   0x0FF
+#define PHY_DPLL_CLK           0x01
+
+#define REG_BCICTL1            0x023
+#define REG_BCICTL2            0x024
+#define CGAIN                  0x020
+#define ITHEN                  0x010
+#define ITHSENS                        0x007
+
+/* Boot BCI flag bits */
+#define BCIAUTOWEN             0x020
+#define CONFIG_DONE            0x010
+#define BCIAUTOUSB             0x002
+#define BCIAUTOAC              0x001
+#define BCIMSTAT_MASK          0x03F
+
+/* Boot BCI register */
+#define REG_BOOT_BCI           0x007
+#define REG_CTRL1              0x00
+#define REG_SW1SELECT_MSB      0x07
+#define SW1_CH9_SEL            0x02
+#define REG_CTRL_SW1           0x012
+#define SW1_TRIGGER            0x020
+#define EOC_SW1                        0x002
+#define REG_GPCH9              0x049
+#define REG_STS_HW_CONDITIONS  0x0F
+#define STS_VBUS               0x080
+#define STS_CHG                        0x02
+#define REG_BCIMSTATEC         0x02
+#define REG_BCIMFSTS4          0x010
+#define REG_BCIMFSTS2          0x00E
+#define REG_BCIMFSTS3          0x00F
+#define REG_BCIMFSTS1          0x001
+#define USBFASTMCHG            0x004
+#define BATSTSPCHG             0x004
+#define BATSTSMCHG             0x040
+#define VBATOV4                        0x020
+#define VBATOV3                        0x010
+#define VBATOV2                        0x008
+#define VBATOV1                        0x004
+#define REG_BB_CFG             0x012
+#define BBCHEN                 0x010
+
+/* Power supply charge interrupt */
+#define REG_PWR_ISR1           0x00
+#define REG_PWR_IMR1           0x01
+#define REG_PWR_EDR1           0x05
+#define REG_PWR_SIH_CTRL       0x007
+
+#define USB_PRES               0x004
+#define CHG_PRES               0x002
+
+#define USB_PRES_RISING                0x020
+#define USB_PRES_FALLING       0x010
+#define CHG_PRES_RISING                0x008
+#define CHG_PRES_FALLING       0x004
+#define AC_STATEC              0x20
+#define COR                    0x004
+
+/* interrupt status registers */
+#define REG_BCIISR1A           0x0
+#define REG_BCIISR2A           0x01
+
+/* Interrupt flags bits BCIISR1 */
+#define BATSTS_ISR1            0x080
+#define VBATLVL_ISR1           0x001
+
+/* Interrupt mask registers for int1*/
+#define REG_BCIIMR1A           0x002
+#define REG_BCIIMR2A           0x003
+
+ /* Interrupt masks for BCIIMR1 */
+#define BATSTS_IMR1            0x080
+#define VBATLVL_IMR1           0x001
+
+/* Interrupt edge detection register */
+#define REG_BCIEDR1            0x00A
+#define REG_BCIEDR2            0x00B
+#define REG_BCIEDR3            0x00C
+
+/* BCIEDR2 */
+#define        BATSTS_EDRRISIN         0x080
+#define BATSTS_EDRFALLING      0x040
+
+/* BCIEDR3 */
+#define        VBATLVL_EDRRISIN        0x02
+
+/* Step size and prescaler ratio */
+#define TEMP_STEP_SIZE         147
+#define TEMP_PSR_R             100
+
+#define VOLT_STEP_SIZE         588
+#define VOLT_PSR_R             100
+
+#define CURR_STEP_SIZE         147
+#define CURR_PSR_R1            44
+#define CURR_PSR_R2            80
+
+#define BK_VOLT_STEP_SIZE      441
+#define BK_VOLT_PSR_R          100
+
+#define ENABLE         1
+#define DISABLE                1
+
+/* Ptr to thermistor table */
+int *therm_tbl;
+
+struct twl4030_bci_device_info {
+       struct device           *dev;
+
+       unsigned long           update_time;
+       int                     voltage_uV;
+       int                     bk_voltage_uV;
+       int                     current_uA;
+       int                     temp_C;
+       int                     charge_rsoc;
+       int                     charge_status;
+
+       struct power_supply     bat;
+       struct power_supply     bk_bat;
+       struct delayed_work     twl4030_bci_monitor_work;
+       struct delayed_work     twl4030_bk_bci_monitor_work;
+};
+
+static int usb_charger_flag;
+static int LVL_1, LVL_2, LVL_3, LVL_4;
+
+static int read_bci_val(u8 reg_1);
+static inline int clear_n_set(u8 mod_no, u8 clear, u8 set, u8 reg);
+static int twl4030charger_presence(void);
+
+/*
+ * Report and clear the charger presence event.
+ */
+static inline int twl4030charger_presence_evt(void)
+{
+       int ret;
+       u8 chg_sts, set = 0, clear = 0;
+
+       /* read charger power supply status */
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &chg_sts,
+               REG_STS_HW_CONDITIONS);
+       if (ret)
+               return IRQ_NONE;
+
+       if (chg_sts & STS_CHG) { /* If the AC charger have been connected */
+               /* configuring falling edge detection for CHG_PRES */
+               set = CHG_PRES_FALLING;
+               clear = CHG_PRES_RISING;
+       } else { /* If the AC charger have been disconnected */
+               /* configuring rising edge detection for CHG_PRES */
+               set = CHG_PRES_RISING;
+               clear = CHG_PRES_FALLING;
+       }
+
+       /* Update the interrupt edge detection register */
+       clear_n_set(TWL4030_MODULE_INT, clear, set, REG_PWR_EDR1);
+
+       return 0;
+}
+
+/*
+ * Interrupt service routine
+ *
+ * Attends to TWL 4030 power module interruptions events, specifically
+ * USB_PRES (USB charger presence) CHG_PRES (AC charger presence) events
+ *
+ */
+static irqreturn_t twl4030charger_interrupt(int irq, void *_di)
+{
+       struct twl4030_bci_device_info *di = _di;
+
+#ifdef CONFIG_LOCKDEP
+       /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
+        * we don't want and can't tolerate.  Although it might be
+        * friendlier not to borrow this thread context...
+        */
+       local_irq_enable();
+#endif
+
+       twl4030charger_presence_evt();
+       power_supply_changed(&di->bat);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * This function handles the twl4030 battery presence interrupt
+ */
+static int twl4030battery_presence_evt(void)
+{
+       int ret;
+       u8 batstsmchg, batstspchg;
+
+       /* check for the battery presence in main charge*/
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
+                       &batstsmchg, REG_BCIMFSTS3);
+       if (ret)
+               return ret;
+
+       /* check for the battery presence in precharge */
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_PRECHARGE,
+                       &batstspchg, REG_BCIMFSTS1);
+       if (ret)
+               return ret;
+
+       /*
+        * REVISIT: Physically inserting/removing the batt
+        * does not seem to generate an int on 3430ES2 SDP.
+        */
+       if ((batstspchg & BATSTSPCHG) || (batstsmchg & BATSTSMCHG)) {
+               /* In case of the battery insertion event */
+               ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, BATSTS_EDRRISIN,
+                       BATSTS_EDRFALLING, REG_BCIEDR2);
+               if (ret)
+                       return ret;
+       } else {
+               /* In case of the battery removal event */
+               ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, BATSTS_EDRFALLING,
+                       BATSTS_EDRRISIN, REG_BCIEDR2);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * This function handles the twl4030 battery voltage level interrupt.
+ */
+static int twl4030battery_level_evt(void)
+{
+       int ret;
+       u8 mfst;
+
+       /* checking for threshold event */
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
+                       &mfst, REG_BCIMFSTS2);
+       if (ret)
+               return ret;
+
+       /* REVISIT could use a bitmap */
+       if (mfst & VBATOV4) {
+               LVL_4 = 1;
+               LVL_3 = 0;
+               LVL_2 = 0;
+               LVL_1 = 0;
+       } else if (mfst & VBATOV3) {
+               LVL_4 = 0;
+               LVL_3 = 1;
+               LVL_2 = 0;
+               LVL_1 = 0;
+       } else if (mfst & VBATOV2) {
+               LVL_4 = 0;
+               LVL_3 = 0;
+               LVL_2 = 1;
+               LVL_1 = 0;
+       } else {
+               LVL_4 = 0;
+               LVL_3 = 0;
+               LVL_2 = 0;
+               LVL_1 = 1;
+       }
+
+       return 0;
+}
+
+/*
+ * Interrupt service routine
+ *
+ * Attends to BCI interruptions events,
+ * specifically BATSTS (battery connection and removal)
+ * VBATOV (main battery voltage threshold) events
+ *
+ */
+static irqreturn_t twl4030battery_interrupt(int irq, void *_di)
+{
+       u8 isr1a_val, isr2a_val, clear_2a, clear_1a;
+       int ret;
+
+#ifdef CONFIG_LOCKDEP
+       /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
+        * we don't want and can't tolerate.  Although it might be
+        * friendlier not to borrow this thread context...
+        */
+       local_irq_enable();
+#endif
+
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &isr1a_val,
+                               REG_BCIISR1A);
+       if (ret)
+               return IRQ_NONE;
+
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_INTERRUPTS, &isr2a_val,
+                               REG_BCIISR2A);
+       if (ret)
+               return IRQ_NONE;
+
+       clear_2a = (isr2a_val & VBATLVL_ISR1) ? (VBATLVL_ISR1) : 0;
+       clear_1a = (isr1a_val & BATSTS_ISR1) ? (BATSTS_ISR1) : 0;
+
+       /* cleaning BCI interrupt status flags */
+       ret = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS,
+                       clear_1a , REG_BCIISR1A);
+       if (ret)
+               return IRQ_NONE;
+
+       ret = twl4030_i2c_write_u8(TWL4030_MODULE_INTERRUPTS,
+                       clear_2a , REG_BCIISR2A);
+       if (ret)
+               return IRQ_NONE;
+
+       /* battery connetion or removal event */
+       if (isr1a_val & BATSTS_ISR1)
+               twl4030battery_presence_evt();
+       /* battery voltage threshold event*/
+       else if (isr2a_val & VBATLVL_ISR1)
+               twl4030battery_level_evt();
+       else
+               return IRQ_NONE;
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * Enable/Disable hardware battery level event notifications.
+ */
+static int twl4030battery_hw_level_en(int enable)
+{
+       int ret;
+
+       if (enable) {
+               /* unmask VBATOV interrupt for INT1 */
+               ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, VBATLVL_IMR1,
+                       0, REG_BCIIMR2A);
+               if (ret)
+                       return ret;
+
+               /* configuring interrupt edge detection for VBATOv */
+               ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0,
+                       VBATLVL_EDRRISIN, REG_BCIEDR3);
+               if (ret)
+                       return ret;
+       } else {
+               /* mask VBATOV interrupt for INT1 */
+               ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0,
+                       VBATLVL_IMR1, REG_BCIIMR2A);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Enable/disable hardware battery presence event notifications.
+ */
+static int twl4030battery_hw_presence_en(int enable)
+{
+       int ret;
+
+       if (enable) {
+               /* unmask BATSTS interrupt for INT1 */
+               ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, BATSTS_IMR1,
+                       0, REG_BCIIMR1A);
+               if (ret)
+                       return ret;
+
+               /* configuring interrupt edge for BATSTS */
+               ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0,
+                       BATSTS_EDRRISIN | BATSTS_EDRFALLING, REG_BCIEDR2);
+               if (ret)
+                       return ret;
+       } else {
+               /* mask BATSTS interrupt for INT1 */
+               ret = clear_n_set(TWL4030_MODULE_INTERRUPTS, 0,
+                       BATSTS_IMR1, REG_BCIIMR1A);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Enable/Disable AC Charge funtionality.
+ */
+static int twl4030charger_ac_en(int enable)
+{
+       int ret;
+
+       if (enable) {
+               /* forcing the field BCIAUTOAC (BOOT_BCI[0) to 1 */
+               ret = clear_n_set(TWL4030_MODULE_PM_MASTER, 0,
+                       (CONFIG_DONE | BCIAUTOWEN | BCIAUTOAC),
+                       REG_BOOT_BCI);
+               if (ret)
+                       return ret;
+       } else {
+               /* forcing the field BCIAUTOAC (BOOT_BCI[0) to 0*/
+               ret = clear_n_set(TWL4030_MODULE_PM_MASTER, BCIAUTOAC,
+                       (CONFIG_DONE | BCIAUTOWEN),
+                       REG_BOOT_BCI);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Enable/Disable USB Charge funtionality.
+ */
+int twl4030charger_usb_en(int enable)
+{
+       u8 value;
+       int ret;
+       unsigned long timeout;
+
+       if (enable) {
+               /* Check for USB charger conneted */
+               ret = twl4030charger_presence();
+               if (ret < 0)
+                       return ret;
+
+               if (!(ret & USB_PW_CONN))
+                       return -ENXIO;
+
+               /* forcing the field BCIAUTOUSB (BOOT_BCI[1]) to 1 */
+               ret = clear_n_set(TWL4030_MODULE_PM_MASTER, 0,
+                       (CONFIG_DONE | BCIAUTOWEN | BCIAUTOUSB),
+                       REG_BOOT_BCI);
+               if (ret)
+                       return ret;
+
+               ret = clear_n_set(TWL4030_MODULE_USB, 0, PHY_DPLL_CLK,
+                       REG_PHY_CLK_CTRL);
+               if (ret)
+                       return ret;
+
+               value = 0;
+               timeout = jiffies + msecs_to_jiffies(50);
+
+               while ((!(value & PHY_DPLL_CLK)) &&
+                       time_before(jiffies, timeout)) {
+                       udelay(10);
+                       ret = twl4030_i2c_read_u8(TWL4030_MODULE_USB, &value,
+                               REG_PHY_CLK_CTRL_STS);
+                       if (ret)
+                               return ret;
+               }
+
+               /* OTG_EN (POWER_CTRL[5]) to 1 */
+               ret = clear_n_set(TWL4030_MODULE_USB, 0, OTG_EN,
+                       REG_POWER_CTRL);
+               if (ret)
+                       return ret;
+
+               mdelay(50);
+
+               /* forcing USBFASTMCHG(BCIMFSTS4[2]) to 1 */
+               ret = clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0,
+                       USBFASTMCHG, REG_BCIMFSTS4);
+               if (ret)
+                       return ret;
+       } else {
+               twl4030charger_presence();
+               ret = clear_n_set(TWL4030_MODULE_PM_MASTER, BCIAUTOUSB,
+                       (CONFIG_DONE | BCIAUTOWEN), REG_BOOT_BCI);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Return battery temperature
+ * Or < 0 on failure.
+ */
+static int twl4030battery_temperature(void)
+{
+       u8 val;
+       int temp, curr, volt, res, ret;
+
+       /* Getting and calculating the thermistor voltage */
+       ret = read_bci_val(T2_BATTERY_TEMP);
+       if (ret < 0)
+               return ret;
+
+       volt = (ret * TEMP_STEP_SIZE) / TEMP_PSR_R;
+
+       /* Getting and calculating the supply current in micro ampers */
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val,
+                REG_BCICTL2);
+       if (ret)
+               return 0;
+
+       curr = ((val & ITHSENS) + 1) * 10;
+
+       /* Getting and calculating the thermistor resistance in ohms*/
+       res = volt * 1000 / curr;
+
+       /*calculating temperature*/
+       for (temp = 58; temp >= 0; temp--) {
+               int actual = therm_tbl[temp];
+               if ((actual - res) >= 0)
+                       break;
+       }
+
+       /* Negative temperature */
+       if (temp < 3) {
+               if (temp == 2)
+                       temp = -1;
+               else if (temp == 1)
+                       temp = -2;
+               else
+                       temp = -3;
+       }
+
+       return temp + 1;
+}
+
+/*
+ * Return battery voltage
+ * Or < 0 on failure.
+ */
+static int twl4030battery_voltage(void)
+{
+       int volt = read_bci_val(T2_BATTERY_VOLT);
+
+       return (volt * VOLT_STEP_SIZE) / VOLT_PSR_R;
+}
+
+/*
+ * Return the battery current
+ * Or < 0 on failure.
+ */
+static int twl4030battery_current(void)
+{
+       int ret, curr = read_bci_val(T2_BATTERY_CUR);
+       u8 val;
+
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val,
+               REG_BCICTL1);
+       if (ret)
+               return ret;
+
+       if (val & CGAIN) /* slope of 0.44 mV/mA */
+               return (curr * CURR_STEP_SIZE) / CURR_PSR_R1;
+       else /* slope of 0.88 mV/mA */
+               return (curr * CURR_STEP_SIZE) / CURR_PSR_R2;
+}
+
+/*
+ * Return the battery backup voltage
+ * Or < 0 on failure.
+ */
+static int twl4030backupbatt_voltage(void)
+{
+       struct twl4030_madc_request req;
+       int temp;
+
+       req.channels = (1 << 9);
+       req.do_avg = 0;
+       req.method = TWL4030_MADC_SW1;
+       req.active = 0;
+       req.func_cb = NULL;
+       twl4030_madc_conversion(&req);
+       temp = (u16)req.rbuf[9];
+
+       return  (temp * BK_VOLT_STEP_SIZE) / BK_VOLT_PSR_R;
+}
+
+/*
+ * Returns an integer value, that means,
+ * NO_PW_CONN  no power supply is connected
+ * AC_PW_CONN  if the AC power supply is connected
+ * USB_PW_CONN  if the USB power supply is connected
+ * AC_PW_CONN + USB_PW_CONN if USB and AC power supplies are both connected
+ *
+ * Or < 0 on failure.
+ */
+static int twl4030charger_presence(void)
+{
+       int ret;
+       u8 hwsts;
+
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &hwsts,
+               REG_STS_HW_CONDITIONS);
+       if (ret) {
+               pr_err("twl4030_bci: error reading STS_HW_CONDITIONS\n");
+               return ret;
+       }
+
+       ret = (hwsts & STS_CHG) ? AC_PW_CONN : NO_PW_CONN;
+       ret += (hwsts & STS_VBUS) ? USB_PW_CONN : NO_PW_CONN;
+
+       if (ret & USB_PW_CONN)
+               usb_charger_flag = 1;
+       else
+               usb_charger_flag = 0;
+
+       return ret;
+
+}
+
+/*
+ * Returns the main charge FSM status
+ * Or < 0 on failure.
+ */
+static int twl4030bci_status(void)
+{
+       int ret;
+       u8 status;
+
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE,
+               &status, REG_BCIMSTATEC);
+       if (ret) {
+               pr_err("twl4030_bci: error reading BCIMSTATEC\n");
+               return ret;
+       }
+
+       return (int) (status & BCIMSTAT_MASK);
+}
+
+static int read_bci_val(u8 reg)
+{
+       int ret, temp;
+       u8 val;
+
+       /* reading MSB */
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val,
+               reg + 1);
+       if (ret)
+               return ret;
+
+       temp = ((int)(val & 0x03)) << 8;
+
+       /* reading LSB */
+       ret = twl4030_i2c_read_u8(TWL4030_MODULE_MAIN_CHARGE, &val,
+               reg);
+       if (ret)
+               return ret;
+
+       return temp | val;
+}
+
+/*
+ * Settup the twl4030 BCI module to enable backup
+ * battery charging.
+ */
+static int twl4030backupbatt_voltage_setup(void)
+{
+       int ret;
+
+       /* Starting backup batery charge */
+       ret = clear_n_set(TWL4030_MODULE_PM_RECEIVER, 0, BBCHEN,
+               REG_BB_CFG);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+/*
+ * Settup the twl4030 BCI module to measure battery
+ * temperature
+ */
+static int twl4030battery_temp_setup(void)
+{
+       int ret;
+
+       /* Enabling thermistor current */
+       ret = clear_n_set(TWL4030_MODULE_MAIN_CHARGE, 0, ITHEN,
+               REG_BCICTL1);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+/*
+ * Sets and clears bits on an given register on a given module
+ */
+static inline int clear_n_set(u8 mod_no, u8 clear, u8 set, u8 reg)
+{
+       int ret;
+       u8 val = 0;
+
+       /* Gets the initial register value */
+       ret = twl4030_i2c_read_u8(mod_no, &val, reg);
+       if (ret)
+               return ret;
+
+       /* Clearing all those bits to clear */
+       val &= ~(clear);
+
+       /* Setting all those bits to set */
+       val |= set;
+
+       /* Update the register */
+       ret = twl4030_i2c_write_u8(mod_no, val, reg);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static enum power_supply_property twl4030_bci_battery_props[] = {
+       POWER_SUPPLY_PROP_STATUS,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+       POWER_SUPPLY_PROP_CAPACITY,
+       POWER_SUPPLY_PROP_TEMP,
+};
+
+static enum power_supply_property twl4030_bk_bci_battery_props[] = {
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+};
+
+static void
+twl4030_bk_bci_battery_read_status(struct twl4030_bci_device_info *di)
+{
+       di->bk_voltage_uV = twl4030backupbatt_voltage();
+}
+
+static void twl4030_bk_bci_battery_work(struct work_struct *work)
+{
+       struct twl4030_bci_device_info *di = container_of(work,
+               struct twl4030_bci_device_info,
+               twl4030_bk_bci_monitor_work.work);
+
+       twl4030_bk_bci_battery_read_status(di);
+       schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 500);
+}
+
+static void twl4030_bci_battery_read_status(struct twl4030_bci_device_info *di)
+{
+       di->temp_C = twl4030battery_temperature();
+       di->voltage_uV = twl4030battery_voltage();
+       di->current_uA = twl4030battery_current();
+}
+
+static void
+twl4030_bci_battery_update_status(struct twl4030_bci_device_info *di)
+{
+       twl4030_bci_battery_read_status(di);
+       di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+       if (power_supply_am_i_supplied(&di->bat))
+               di->charge_status = POWER_SUPPLY_STATUS_CHARGING;
+       else
+               di->charge_status = POWER_SUPPLY_STATUS_DISCHARGING;
+}
+
+static void twl4030_bci_battery_work(struct work_struct *work)
+{
+       struct twl4030_bci_device_info *di = container_of(work,
+               struct twl4030_bci_device_info, twl4030_bci_monitor_work.work);
+
+       twl4030_bci_battery_update_status(di);
+       schedule_delayed_work(&di->twl4030_bci_monitor_work, 100);
+}
+
+
+#define to_twl4030_bci_device_info(x) container_of((x), \
+                       struct twl4030_bci_device_info, bat);
+
+static void twl4030_bci_battery_external_power_changed(struct power_supply *psy)
+{
+       struct twl4030_bci_device_info *di = to_twl4030_bci_device_info(psy);
+
+       cancel_delayed_work(&di->twl4030_bci_monitor_work);
+       schedule_delayed_work(&di->twl4030_bci_monitor_work, 0);
+}
+
+#define to_twl4030_bk_bci_device_info(x) container_of((x), \
+               struct twl4030_bci_device_info, bk_bat);
+
+static int twl4030_bk_bci_battery_get_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       union power_supply_propval *val)
+{
+       struct twl4030_bci_device_info *di = to_twl4030_bk_bci_device_info(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = di->bk_voltage_uV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int twl4030_bci_battery_get_property(struct power_supply *psy,
+                                       enum power_supply_property psp,
+                                       union power_supply_propval *val)
+{
+       struct twl4030_bci_device_info *di;
+       int status = 0;
+
+       di = to_twl4030_bci_device_info(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_STATUS:
+               val->intval = di->charge_status;
+               return 0;
+       default:
+               break;
+       }
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               val->intval = di->voltage_uV;
+               break;
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               val->intval = di->current_uA;
+               break;
+       case POWER_SUPPLY_PROP_TEMP:
+               val->intval = di->temp_C;
+               break;
+       case POWER_SUPPLY_PROP_ONLINE:
+               status = twl4030bci_status();
+               if ((status & AC_STATEC) == AC_STATEC)
+                       val->intval = POWER_SUPPLY_TYPE_MAINS;
+               else if (usb_charger_flag)
+                       val->intval = POWER_SUPPLY_TYPE_USB;
+               else
+                       val->intval = 0;
+               break;
+       case POWER_SUPPLY_PROP_CAPACITY:
+               /*
+                * need to get the correct percentage value per the
+                * battery characteristics. Approx values for now.
+                */
+               if (di->voltage_uV < 2894 || LVL_1) {
+                       val->intval = 5;
+                       LVL_1 = 0;
+               } else if ((di->voltage_uV < 3451 && di->voltage_uV > 2894)
+                       || LVL_2) {
+                       val->intval = 20;
+                       LVL_2 = 0;
+               } else if ((di->voltage_uV < 3902 && di->voltage_uV > 3451)
+                       || LVL_3) {
+                       val->intval = 50;
+                       LVL_3 = 0;
+               } else if ((di->voltage_uV < 3949 && di->voltage_uV > 3902)
+                       || LVL_4) {
+                       val->intval = 75;
+                       LVL_4 = 0;
+               } else if (di->voltage_uV > 3949)
+                       val->intval = 90;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static char *twl4030_bci_supplied_to[] = {
+       "twl4030_bci_battery",
+};
+
+static int __init twl4030_bci_battery_probe(struct platform_device *pdev)
+{
+       struct twl4030_bci_platform_data *pdata = pdev->dev.platform_data;
+       struct twl4030_bci_device_info *di;
+       int irq;
+       int ret;
+
+       therm_tbl = pdata->battery_tmp_tbl;
+
+       di = kzalloc(sizeof(*di), GFP_KERNEL);
+       if (!di)
+               return -ENOMEM;
+
+       di->dev = &pdev->dev;
+       di->bat.name = "twl4030_bci_battery";
+       di->bat.supplied_to = twl4030_bci_supplied_to;
+       di->bat.num_supplicants = ARRAY_SIZE(twl4030_bci_supplied_to);
+       di->bat.type = POWER_SUPPLY_TYPE_BATTERY;
+       di->bat.properties = twl4030_bci_battery_props;
+       di->bat.num_properties = ARRAY_SIZE(twl4030_bci_battery_props);
+       di->bat.get_property = twl4030_bci_battery_get_property;
+       di->bat.external_power_changed =
+                       twl4030_bci_battery_external_power_changed;
+
+       di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+       di->bk_bat.name = "twl4030_bci_bk_battery";
+       di->bk_bat.type = POWER_SUPPLY_TYPE_BATTERY;
+       di->bk_bat.properties = twl4030_bk_bci_battery_props;
+       di->bk_bat.num_properties = ARRAY_SIZE(twl4030_bk_bci_battery_props);
+       di->bk_bat.get_property = twl4030_bk_bci_battery_get_property;
+       di->bk_bat.external_power_changed = NULL;
+
+       twl4030charger_ac_en(ENABLE);
+       twl4030charger_usb_en(ENABLE);
+       twl4030battery_hw_level_en(ENABLE);
+       twl4030battery_hw_presence_en(ENABLE);
+
+       platform_set_drvdata(pdev, di);
+
+       /* settings for temperature sensing */
+       ret = twl4030battery_temp_setup();
+       if (ret)
+               goto temp_setup_fail;
+
+       /* enabling GPCH09 for read back battery voltage */
+       ret = twl4030backupbatt_voltage_setup();
+       if (ret)
+               goto voltage_setup_fail;
+
+       /* REVISIT do we need to request both IRQs ?? */
+
+       /* request BCI interruption */
+       irq = platform_get_irq(pdev, 1);
+       ret = request_irq(irq, twl4030battery_interrupt,
+               0, pdev->name, NULL);
+       if (ret) {
+               dev_dbg(&pdev->dev, "could not request irq %d, status %d\n",
+                       irq, ret);
+               goto batt_irq_fail;
+       }
+
+       /* request Power interruption */
+       irq = platform_get_irq(pdev, 0);
+       ret = request_irq(irq, twl4030charger_interrupt,
+               0, pdev->name, di);
+
+       if (ret) {
+               dev_dbg(&pdev->dev, "could not request irq %d, status %d\n",
+                       irq, ret);
+               goto chg_irq_fail;
+       }
+
+       ret = power_supply_register(&pdev->dev, &di->bat);
+       if (ret) {
+               dev_dbg(&pdev->dev, "failed to register main battery\n");
+               goto batt_failed;
+       }
+
+       INIT_DELAYED_WORK_DEFERRABLE(&di->twl4030_bci_monitor_work,
+                               twl4030_bci_battery_work);
+       schedule_delayed_work(&di->twl4030_bci_monitor_work, 0);
+
+       ret = power_supply_register(&pdev->dev, &di->bk_bat);
+       if (ret) {
+               dev_dbg(&pdev->dev, "failed to register backup battery\n");
+               goto bk_batt_failed;
+       }
+
+       INIT_DELAYED_WORK_DEFERRABLE(&di->twl4030_bk_bci_monitor_work,
+                               twl4030_bk_bci_battery_work);
+       schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 500);
+
+       return 0;
+
+bk_batt_failed:
+       power_supply_unregister(&di->bat);
+batt_failed:
+       free_irq(irq, di);
+chg_irq_fail:
+       irq = platform_get_irq(pdev, 1);
+       free_irq(irq, NULL);
+batt_irq_fail:
+voltage_setup_fail:
+temp_setup_fail:
+       twl4030charger_ac_en(DISABLE);
+       twl4030charger_usb_en(DISABLE);
+       twl4030battery_hw_level_en(DISABLE);
+       twl4030battery_hw_presence_en(DISABLE);
+       kfree(di);
+
+       return ret;
+}
+
+static int __exit twl4030_bci_battery_remove(struct platform_device *pdev)
+{
+       struct twl4030_bci_device_info *di = platform_get_drvdata(pdev);
+       int irq;
+
+       twl4030charger_ac_en(DISABLE);
+       twl4030charger_usb_en(DISABLE);
+       twl4030battery_hw_level_en(DISABLE);
+       twl4030battery_hw_presence_en(DISABLE);
+
+       irq = platform_get_irq(pdev, 0);
+       free_irq(irq, di);
+
+       irq = platform_get_irq(pdev, 1);
+       free_irq(irq, NULL);
+
+       flush_scheduled_work();
+       power_supply_unregister(&di->bat);
+       power_supply_unregister(&di->bk_bat);
+       platform_set_drvdata(pdev, NULL);
+       kfree(di);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int twl4030_bci_battery_suspend(struct platform_device *pdev,
+       pm_message_t state)
+{
+       struct twl4030_bci_device_info *di = platform_get_drvdata(pdev);
+
+       di->charge_status = POWER_SUPPLY_STATUS_UNKNOWN;
+       cancel_delayed_work(&di->twl4030_bci_monitor_work);
+       cancel_delayed_work(&di->twl4030_bk_bci_monitor_work);
+       return 0;
+}
+
+static int twl4030_bci_battery_resume(struct platform_device *pdev)
+{
+       struct twl4030_bci_device_info *di = platform_get_drvdata(pdev);
+
+       schedule_delayed_work(&di->twl4030_bci_monitor_work, 0);
+       schedule_delayed_work(&di->twl4030_bk_bci_monitor_work, 50);
+       return 0;
+}
+#else
+#define twl4030_bci_battery_suspend    NULL
+#define twl4030_bci_battery_resume     NULL
+#endif /* CONFIG_PM */
+
+static struct platform_driver twl4030_bci_battery_driver = {
+       .probe          = twl4030_bci_battery_probe,
+       .remove         = __exit_p(twl4030_bci_battery_remove),
+       .suspend        = twl4030_bci_battery_suspend,
+       .resume         = twl4030_bci_battery_resume,
+       .driver         = {
+               .name   = "twl4030_bci",
+       },
+};
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:twl4030_bci");
+MODULE_AUTHOR("Texas Instruments Inc");
+
+static int __init twl4030_battery_init(void)
+{
+       return platform_driver_register(&twl4030_bci_battery_driver);
+}
+module_init(twl4030_battery_init);
+
+static void __exit twl4030_battery_exit(void)
+{
+       platform_driver_unregister(&twl4030_bci_battery_driver);
+}
+module_exit(twl4030_battery_exit);
+
index b4b39811b44544cc76a4c71e00c5c02567a765c2..67877b1be0dd818e08b634d3d3c1d715603889c5 100644 (file)
@@ -1596,7 +1596,11 @@ static irqreturn_t serial8250_interrupt(int irq, void *dev_id)
 
        DEBUG_INTR("end.\n");
 
 
        DEBUG_INTR("end.\n");
 
+#ifdef CONFIG_ARCH_OMAP15XX
+       return IRQ_HANDLED;     /* FIXME: iir status not ready on 1510 */
+#else
        return IRQ_RETVAL(handled);
        return IRQ_RETVAL(handled);
+#endif
 }
 
 /*
 }
 
 /*
@@ -2387,6 +2391,19 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
                        /* emulated UARTs (Lucent Venus 167x) need two steps */
                        serial_outp(up, UART_FCR, UART_FCR_ENABLE_FIFO);
                }
                        /* 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);
                serial_outp(up, UART_FCR, fcr);         /* set fcr */
        }
        serial8250_set_mctrl(&up->port, up->port.mctrl);
index 83a185d52961e5075cd5330de7f770fc4eb28e79..8bc6d1147217ca74a8b02795bb661494ebdcf4e9 100644 (file)
@@ -230,6 +230,47 @@ config SPI_XILINX
 #
 comment "SPI Protocol Masters"
 
 #
 comment "SPI Protocol Masters"
 
+config SPI_TSC210X
+       depends on SPI_MASTER && EXPERIMENTAL
+       tristate "TI TSC210x (TSC2101/TSC2102) support"
+       help
+         Say Y here if you want support for the TSC210x chips.  Some
+         boards use these for touchscreen and audio support.
+
+         These are members of a family of highly integrated PDA analog
+         interface circuit.  They include a 12-bit ADC used for battery,
+         temperature, touchscreen, and other sensors.  They also have
+         an audio DAC and amplifier, and in some models an audio ADC.
+         The audio support is highly chip-specific, but most of the
+         sensor support works the same.
+
+         Note that the device has to be present in the board's SPI
+         devices table for this driver to load.  This driver doesn't
+         automatically enable touchscreen, sensors or audio
+         functionality - enable these in their respective menus.
+
+config SPI_TSC2301
+       tristate "TSC2301 driver"
+       depends on SPI_MASTER
+       help
+         Say Y here if you have a TSC2301 chip connected to an SPI
+         bus on your board.
+
+         The TSC2301 is a highly integrated PDA analog interface circuit.
+         It contains a complete 12-bit A/D resistive touch screen
+         converter (ADC) including drivers, touch pressure measurement
+         capability, keypad controller, and 8-bit D/A converter (DAC) output
+         for LCD contrast control.
+
+         To compile this driver as a module, choose M here: the
+         module will be called tsc2301.
+
+config SPI_TSC2301_AUDIO
+       boolean "TSC2301 audio support"
+       depends on SPI_TSC2301 && SND
+       help
+         Say Y here for if you are using the audio features of TSC2301.
+
 config SPI_SPIDEV
        tristate "User mode SPI device driver support"
        depends on EXPERIMENTAL
 config SPI_SPIDEV
        tristate "User mode SPI device driver support"
        depends on EXPERIMENTAL
index 5d0451936d8625eebd4f70dd59f65ab69c2db333..2b8af98787376f6f5edf164dc8a6aae6d47a209b 100644 (file)
@@ -35,6 +35,9 @@ obj-$(CONFIG_SPI_SH_SCI)              += spi_sh_sci.o
 # SPI protocol drivers (device/link on bus)
 obj-$(CONFIG_SPI_SPIDEV)       += spidev.o
 obj-$(CONFIG_SPI_TLE62X0)      += tle62x0.o
 # SPI protocol drivers (device/link on bus)
 obj-$(CONFIG_SPI_SPIDEV)       += spidev.o
 obj-$(CONFIG_SPI_TLE62X0)      += tle62x0.o
+obj-$(CONFIG_SPI_TSC210X)      += tsc210x.o
+obj-$(CONFIG_SPI_TSC2301)      += tsc2301.o
+tsc2301-objs                   := tsc2301-core.o
 #      ... add above this line ...
 
 # SPI slave controller drivers (upstream link)
 #      ... add above this line ...
 
 # SPI slave controller drivers (upstream link)
index d6d0c5d241ce1bcbcd9b68bc15caacbccb5f5be8..f2b4d329e1aa5f1c9e496597cd052da2c09e56f0 100644 (file)
 
 /* per-register bitmasks: */
 
 
 /* per-register bitmasks: */
 
-#define OMAP2_MCSPI_SYSCONFIG_AUTOIDLE (1 << 0)
-#define OMAP2_MCSPI_SYSCONFIG_SOFTRESET        (1 << 1)
+#define OMAP2_MCSPI_SYSCONFIG_SMARTIDLE        BIT(4)
+#define OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP        BIT(2)
+#define OMAP2_MCSPI_SYSCONFIG_AUTOIDLE BIT(0)
+#define OMAP2_MCSPI_SYSCONFIG_SOFTRESET        BIT(1)
 
 
-#define OMAP2_MCSPI_SYSSTATUS_RESETDONE        (1 << 0)
+#define OMAP2_MCSPI_SYSSTATUS_RESETDONE        BIT(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_MODULCTRL_SINGLE   BIT(0)
+#define OMAP2_MCSPI_MODULCTRL_MS       BIT(2)
+#define OMAP2_MCSPI_MODULCTRL_STEST    BIT(3)
 
 
-#define OMAP2_MCSPI_CHCONF_PHA         (1 << 0)
-#define OMAP2_MCSPI_CHCONF_POL         (1 << 1)
+#define OMAP2_MCSPI_CHCONF_PHA         BIT(0)
+#define OMAP2_MCSPI_CHCONF_POL         BIT(1)
 #define OMAP2_MCSPI_CHCONF_CLKD_MASK   (0x0f << 2)
 #define OMAP2_MCSPI_CHCONF_CLKD_MASK   (0x0f << 2)
-#define OMAP2_MCSPI_CHCONF_EPOL                (1 << 6)
+#define OMAP2_MCSPI_CHCONF_EPOL                BIT(6)
 #define OMAP2_MCSPI_CHCONF_WL_MASK     (0x1f << 7)
 #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_RX_ONLY BIT(12)
+#define OMAP2_MCSPI_CHCONF_TRM_TX_ONLY BIT(13)
 #define OMAP2_MCSPI_CHCONF_TRM_MASK    (0x03 << 12)
 #define OMAP2_MCSPI_CHCONF_TRM_MASK    (0x03 << 12)
-#define OMAP2_MCSPI_CHCONF_DMAW                (1 << 14)
-#define OMAP2_MCSPI_CHCONF_DMAR                (1 << 15)
-#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_CHCONF_DMAW                BIT(14)
+#define OMAP2_MCSPI_CHCONF_DMAR                BIT(15)
+#define OMAP2_MCSPI_CHCONF_DPE0                BIT(16)
+#define OMAP2_MCSPI_CHCONF_DPE1                BIT(17)
+#define OMAP2_MCSPI_CHCONF_IS          BIT(18)
+#define OMAP2_MCSPI_CHCONF_TURBO       BIT(19)
+#define OMAP2_MCSPI_CHCONF_FORCE       BIT(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_CHSTAT_RXS         BIT(0)
+#define OMAP2_MCSPI_CHSTAT_TXS         BIT(1)
+#define OMAP2_MCSPI_CHSTAT_EOT         BIT(2)
 
 
-#define OMAP2_MCSPI_CHCTRL_EN          (1 << 0)
+#define OMAP2_MCSPI_CHCTRL_EN          BIT(0)
 
 
+#define OMAP2_MCSPI_WAKEUPENABLE_WKEN  BIT(0)
 
 /* We have 2 DMA channels per CS, one for RX and one for TX */
 struct omap2_mcspi_dma {
 
 /* We have 2 DMA channels per CS, one for RX and one for TX */
 struct omap2_mcspi_dma {
@@ -884,8 +887,12 @@ static int __init omap2_mcspi_reset(struct omap2_mcspi *mcspi)
        } while (!(tmp & OMAP2_MCSPI_SYSSTATUS_RESETDONE));
 
        mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG,
        } while (!(tmp & OMAP2_MCSPI_SYSSTATUS_RESETDONE));
 
        mcspi_write_reg(master, OMAP2_MCSPI_SYSCONFIG,
-                       /* (3 << 8) | (2 << 3) | */
-                       OMAP2_MCSPI_SYSCONFIG_AUTOIDLE);
+                       OMAP2_MCSPI_SYSCONFIG_AUTOIDLE |
+                       OMAP2_MCSPI_SYSCONFIG_ENAWAKEUP |
+                       OMAP2_MCSPI_SYSCONFIG_SMARTIDLE);
+
+       mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE,
+                       OMAP2_MCSPI_WAKEUPENABLE_WKEN);
 
        omap2_mcspi_set_master_mode(master);
 
 
        omap2_mcspi_set_master_mode(master);
 
diff --git a/drivers/spi/tsc210x.c b/drivers/spi/tsc210x.c
new file mode 100644 (file)
index 0000000..1d2ac94
--- /dev/null
@@ -0,0 +1,1262 @@
+/*
+ * tsc210x.c - TSC2101/2102/... driver core
+ *
+ * Copyright (c) 2005-2007 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This package 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 package 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 package; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/suspend.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <linux/autoconf.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc210x.h>
+
+
+/* NOTE:  It should be straightforward to make this driver framework handle
+ * tsc2100 and tsc2111 chips, and maybe others too.  The main differences
+ * are in the audio codec capabilities, but there are also some differences
+ * in how the various sensors (including touchscreen) are handled.
+ */
+
+/* Bit field definitions for chip registers */
+
+/* Scan X, Y, Z1, Z2, chip controlled, 12-bit, 16 samples, 500 usec */
+#define TSC210X_ADC_TS_CONTROL         0x8bf4
+/* Scan BAT1, BAT2, AUX1, AUX2, 12-bit, 16 samples, 500 usec */
+#define TSC210X_ADC_SCAN_CONTROL       0x2ff4
+/* Scan TEMP1, 12-bit, 16 samples, 500 usec */
+#define TSC210X_ADC_T1_CONTROL         0x2bf4
+/* Scan TEMP2, 12-bit, 16 samples, 500 usec */
+#define TSC210X_ADC_T2_CONTROL         0x33f4
+/* PINT/DAV acts as DAV */
+#define TSC210X_ADC_DAV                        0x4000
+/* Internal reference, 100 usec delay, 1.25 V reference */
+#define TSC210X_ADC_INT_REF            0x0016
+/* External reference, 100 usec delay, 1.25 V reference */
+#define TSC210X_ADC_EXT_REF            0x0002
+/* 84 usec precharge time, 32 usec sense time */
+#define TSC210X_CONFIG_TIMES           0x0008
+/* The reset sequence */
+#define TSC210X_RESET                  0xbb00
+/* Pen Status bit */
+#define TSC210X_ADC_PSTCM              (1 << 15)
+/* A/D Status bit */
+#define TSC210X_ADC_ADST               (1 << 14)
+/* (At least) One of X, Y, Z1, Z2 contains data */
+#define TSC210X_TS_DAV                 0x0780
+/* (At least) One of BAT1, BAT2, AUX1, AUX2 contains data */
+#define TSC210X_PS_DAV                 0x0078
+/* TEMP1 contains data */
+#define TSC210X_T1_DAV                 0x0004
+/* TEMP2 contains data */
+#define TSC210X_T2_DAV                 0x0002
+#define TSC2101_DAC_ON                 0x0000
+#define TSC2101_DAC_OFF                        0xe7fc
+#define TSC2102_DAC_ON                 0x3ba0
+#define TSC2102_DAC_OFF                        0xafa0
+#define TSC210X_FS44K                  (1 << 13)
+#define TSC210X_PLL1_OFF               0x0000
+#define TSC210X_PLL1_44K               0x811c
+#define TSC210X_PLL1_48K               0x8120
+#define TSC210X_PLL2_44K               (5462 << 2)
+#define TSC210X_PLL2_48K               (1920 << 2)
+#define TSC210X_SLVMS                  (1 << 11)
+#define TSC210X_DEEMPF                 (1 << 0)
+#define TSC2102_BASSBC                 (1 << 1)
+#define TSC210X_KEYCLICK_OFF           0x0000
+
+#define CS_CHANGE(val)                 0
+
+struct tsc210x_spi_req {
+       struct spi_device *dev;
+       u16 command;
+       u16 data;
+       struct spi_message message;
+};
+
+struct tsc210x_dev {
+       enum tsc_type {
+               tsc2101,
+               tsc2102,
+       } kind;
+       struct tsc210x_config *pdata;
+       struct clk *bclk_ck;
+
+       struct workqueue_struct *queue;
+       struct delayed_work ts_worker;          /* Poll-wait for PEN UP */
+       struct delayed_work sensor_worker;      /* Scan the ADC inputs */
+       struct mutex queue_lock;
+       struct completion data_avail;
+
+       tsc210x_touch_t touch_cb;
+       void *touch_cb_ctx;
+
+       tsc210x_coords_t coords_cb;
+       void *coords_cb_ctx;
+
+       tsc210x_ports_t ports_cb;
+       void *ports_cb_ctx;
+
+       tsc210x_temp_t temp1_cb;
+       void *temp2_cb_ctx;
+
+       tsc210x_temp_t temp2_cb;
+       void *temp1_cb_ctx;
+
+       struct spi_device *spi;
+       struct spi_transfer *transfers;
+       struct tsc210x_spi_req req_adc;
+       struct tsc210x_spi_req req_status;
+       struct tsc210x_spi_req req_mode;
+       struct tsc210x_spi_req req_stop;
+
+       int pendown;
+       int flushing;                   /* Queue flush in progress */
+       u16 status;
+       u16 adc_data[4];
+       int bat[2], aux[2], temp[2];
+};
+
+static struct {
+       unsigned int ts_msecs;          /* Interval for .ts_timer */
+       unsigned int mode_msecs;        /* Interval for .mode_timer */
+} settings;
+
+module_param_named(touch_check_msecs, settings.ts_msecs, uint, 0);
+MODULE_PARM_DESC(touch_check_msecs, "Pen-up polling interval in msecs");
+
+module_param_named(sensor_scan_msecs, settings.mode_msecs, uint, 0);
+MODULE_PARM_DESC(sensor_scan_msecs, "Temperature & battery scan interval");
+
+int tsc210x_write_sync(struct tsc210x_dev *dev,
+               int page, u8 address, u16 data)
+{
+       static struct tsc210x_spi_req req;
+       static struct spi_transfer transfer[2];
+       int ret;
+
+       spi_message_init(&req.message);
+
+       /* Address */
+       req.command = (page << 11) | (address << 5);
+       transfer[0].tx_buf = &req.command;
+       transfer[0].rx_buf = NULL;
+       transfer[0].len = 2;
+       spi_message_add_tail(&transfer[0], &req.message);
+
+       /* Data */
+       transfer[1].tx_buf = &data;
+       transfer[1].rx_buf = NULL;
+       transfer[1].len = 2;
+       transfer[1].cs_change = CS_CHANGE(1);
+       spi_message_add_tail(&transfer[1], &req.message);
+
+       ret = spi_sync(dev->spi, &req.message);
+       if (!ret && req.message.status)
+               ret = req.message.status;
+       if (ret)
+               dev_dbg(&dev->spi->dev, "write_sync --> %d\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL(tsc210x_write_sync);
+
+int tsc210x_reads_sync(struct tsc210x_dev *dev,
+               int page, u8 startaddress, u16 *data, int numregs)
+{
+       static struct tsc210x_spi_req req;
+       static struct spi_transfer transfer[6];
+       int ret, i, j;
+
+       if (numregs + 1 > ARRAY_SIZE(transfer))
+               return -EINVAL;
+
+       spi_message_init(&req.message);
+       i = 0;
+       j = 0;
+
+       /* Address */
+       req.command = 0x8000 | (page << 11) | (startaddress << 5);
+       transfer[i].tx_buf = &req.command;
+       transfer[i].rx_buf = NULL;
+       transfer[i].len = 2;
+       spi_message_add_tail(&transfer[i ++], &req.message);
+
+       /* Data */
+       while (j < numregs) {
+               transfer[i].tx_buf = NULL;
+               transfer[i].rx_buf = &data[j ++];
+               transfer[i].len = 2;
+               transfer[i].cs_change = CS_CHANGE(j == numregs);
+               spi_message_add_tail(&transfer[i ++], &req.message);
+       }
+
+       ret = spi_sync(dev->spi, &req.message);
+       if (!ret && req.message.status)
+               ret = req.message.status;
+       if (ret)
+               dev_dbg(&dev->spi->dev, "reads_sync --> %d\n", ret);
+
+       return ret;
+}
+EXPORT_SYMBOL(tsc210x_reads_sync);
+
+int tsc210x_read_sync(struct tsc210x_dev *dev, int page, u8 address)
+{
+       u16 ret;
+       int status;
+
+       status = tsc210x_reads_sync(dev, page, address, &ret, 1);
+       return status ? : ret;
+}
+EXPORT_SYMBOL(tsc210x_read_sync);
+
+
+static void tsc210x_submit_async(struct tsc210x_spi_req *spi)
+{
+       int ret;
+
+       ret = spi_async(spi->dev, &spi->message);
+       if (ret)
+               dev_dbg(&spi->dev->dev, "%s: error %i in SPI request\n",
+                               __FUNCTION__, ret);
+}
+
+static void tsc210x_request_alloc(struct tsc210x_dev *dev,
+               struct tsc210x_spi_req *spi, int direction,
+               int page, u8 startaddress, int numregs, u16 *data,
+               void (*complete)(struct tsc210x_dev *context),
+               struct spi_transfer **transfer)
+{
+       spi->dev = dev->spi;
+
+       if (direction == 1)     /* Write */
+               numregs = 2;
+       else                    /* Read */
+               numregs += 1;
+
+       spi_message_init(&spi->message);
+       spi->message.complete = (void (*)(void *)) complete;
+       spi->message.context = dev;
+
+       /* Address */
+       spi->command = (page << 11) | (startaddress << 5);
+       if (direction != 1)
+               spi->command |= 1 << 15;
+
+       (*transfer)->tx_buf = &spi->command;
+       (*transfer)->rx_buf = NULL;
+       (*transfer)->len = 2;
+       spi_message_add_tail((*transfer) ++, &spi->message);
+
+       /* Data */
+       while (-- numregs) {
+               if (direction == 1)
+                       (*transfer)->tx_buf = &spi->data;
+               else
+                       (*transfer)->rx_buf = data++;
+               (*transfer)->len = 2;
+               (*transfer)->cs_change = CS_CHANGE(numregs != 1);
+               spi_message_add_tail((*transfer) ++, &spi->message);
+       }
+}
+
+#define tsc210x_cb_register_func(cb, cb_t)     \
+int tsc210x_ ## cb(struct device *dev, cb_t handler, void *context)    \
+{      \
+       struct tsc210x_dev *tsc = dev_get_drvdata(dev); \
+       \
+       /* Lock the module */   \
+       if (handler && !tsc->cb)        \
+               if (!try_module_get(THIS_MODULE)) {     \
+                       dev_err(dev, "Failed to get TSC module\n");     \
+               }       \
+       if (!handler && tsc->cb)        \
+               module_put(THIS_MODULE);        \
+       \
+       tsc->cb = handler;      \
+       tsc->cb ## _ctx = context;      \
+       return 0;       \
+} \
+EXPORT_SYMBOL(tsc210x_ ## cb);
+
+tsc210x_cb_register_func(touch_cb, tsc210x_touch_t)
+tsc210x_cb_register_func(coords_cb, tsc210x_coords_t)
+tsc210x_cb_register_func(ports_cb, tsc210x_ports_t)
+tsc210x_cb_register_func(temp1_cb, tsc210x_temp_t)
+tsc210x_cb_register_func(temp2_cb, tsc210x_temp_t)
+
+#ifdef DEBUG
+static void tsc210x_print_dav(struct tsc210x_dev *dev)
+{
+       int status = tsc210x_read_sync(dev, TSC210X_TS_STATUS_CTRL);
+
+       if (status < 0) {
+               dev_dbg(&dev->spi->dev, "status %d\n", status);
+               return;
+       }
+
+       if (!(status & 0x0fff))
+               return;
+
+       dev_dbg(&dev->spi->dev, "data in %s%s%s%s%s%s%s%s%s%s%s\n",
+               (status & 0x0400) ? " X" : "",
+               (status & 0x0200) ? " Y" : "",
+               (status & 0x0100) ? " Z1" : "",
+               (status & 0x0080) ? " Z2" : "",
+               (status & 0x0040) ? " BAT1" : "",
+               (status & 0x0020) ? " BAT2" : "",
+               (status & 0x0010) ? " AUX1" : "",
+               (status & 0x0008) ? " AUX2" : "",
+               (status & 0x0004) ? " TEMP1" : "",
+               (status & 0x0002) ? " TEMP2" : "",
+               (status & 0x0001) ? " KP" : "");
+}
+#endif
+
+static void tsc210x_complete_dummy(struct tsc210x_dev *dev)
+{
+}
+
+static inline void tsc210x_touchscreen_mode(struct tsc210x_dev *dev)
+{
+       /* Scan X, Y, Z1, Z2, chip controlled, 12-bit, 16 samples, 500 usec */
+       dev->req_mode.data = TSC210X_ADC_TS_CONTROL;
+       tsc210x_submit_async(&dev->req_mode);
+}
+
+static inline void tsc210x_portscan_mode(struct tsc210x_dev *dev)
+{
+       /* Scan BAT1, BAT2, AUX1, AUX2, 12-bit, 16 samples, 500 usec */
+       dev->req_mode.data = TSC210X_ADC_SCAN_CONTROL;
+       tsc210x_submit_async(&dev->req_mode);
+}
+
+static inline void tsc210x_temp1_mode(struct tsc210x_dev *dev)
+{
+       /* Scan TEMP1, 12-bit, 16 samples, 500 usec */
+       dev->req_mode.data = TSC210X_ADC_T1_CONTROL;
+       tsc210x_submit_async(&dev->req_mode);
+}
+
+static inline void tsc210x_temp2_mode(struct tsc210x_dev *dev)
+{
+       /* Scan TEMP2, 12-bit, 16 samples, 500 usec */
+       dev->req_mode.data = TSC210X_ADC_T2_CONTROL;
+       tsc210x_submit_async(&dev->req_mode);
+}
+
+/* Abort current conversion if any */
+static void tsc210x_new_mode(struct tsc210x_dev *dev)
+{
+       dev->req_stop.data = TSC210X_ADC_ADST;
+       tsc210x_submit_async(&dev->req_stop);
+}
+
+static void tsc210x_queue_scan(struct tsc210x_dev *dev)
+{
+       if (dev->pdata->monitor)
+               if (!queue_delayed_work(dev->queue,
+                               &dev->sensor_worker,
+                               msecs_to_jiffies(settings.mode_msecs)))
+                       dev_err(&dev->spi->dev,
+                                       "%s: can't queue measurements\n",
+                                       __FUNCTION__);
+}
+
+static void tsc210x_queue_penup(struct tsc210x_dev *dev)
+{
+       if (!queue_delayed_work(dev->queue,
+                       &dev->ts_worker,
+                       msecs_to_jiffies(settings.ts_msecs)))
+               dev_err(&dev->spi->dev,
+                               "%s: can't queue pen-up poll\n",
+                               __FUNCTION__);
+}
+
+static void tsc210x_status_report(struct tsc210x_dev *dev)
+{
+       /*
+        * Read all converted data from corresponding registers
+        * so that the ADC can move on to a new conversion.
+        */
+       if (dev->status & TSC210X_TS_DAV) {
+               if (!dev->pendown && !dev->flushing) {
+                       dev->pendown = 1;
+                       if (dev->touch_cb)
+                               dev->touch_cb(dev->touch_cb_ctx, 1);
+
+                       tsc210x_queue_penup(dev);
+               }
+
+               tsc210x_submit_async(&dev->req_adc);
+       }
+
+       if (dev->status & (TSC210X_PS_DAV | TSC210X_T1_DAV | TSC210X_T2_DAV))
+               complete(&dev->data_avail);
+}
+
+static void tsc210x_data_report(struct tsc210x_dev *dev)
+{
+       u16 adc_data[4];
+
+       if (dev->status & TSC210X_PS_DAV) {
+               tsc210x_reads_sync(dev, TSC210X_TS_BAT1, adc_data, 4);
+               /* NOTE: reads_sync() could fail */
+
+               dev->bat[0] = adc_data[0];
+               dev->bat[1] = adc_data[1];
+               dev->aux[0] = adc_data[2];
+               dev->aux[1] = adc_data[3];
+               if (dev->ports_cb)
+                       dev->ports_cb(dev->ports_cb_ctx, dev->bat, dev->aux);
+       }
+
+       if (dev->status & TSC210X_T1_DAV) {
+               dev->temp[0] = tsc210x_read_sync(dev, TSC210X_TS_TEMP1);
+
+               if (dev->temp[0] >= 0 && dev->temp1_cb)
+                       dev->temp1_cb(dev->temp1_cb_ctx, dev->temp[0]);
+       }
+
+       if (dev->status & TSC210X_T2_DAV) {
+               dev->temp[1] = tsc210x_read_sync(dev, TSC210X_TS_TEMP2);
+
+               if (dev->temp[1] >= 0 && dev->temp2_cb)
+                       dev->temp2_cb(dev->temp2_cb_ctx, dev->temp[1]);
+       }
+}
+
+static void tsc210x_coords_report(struct tsc210x_dev *dev)
+{
+       if (dev->coords_cb)
+               dev->coords_cb(dev->coords_cb_ctx,
+                               dev->adc_data[0], dev->adc_data[1],
+                               dev->adc_data[2], dev->adc_data[3]);
+}
+
+/*
+ * There are at least three ways to check for pen-up:
+ *     - the PINT/DAV pin state,
+ *     - reading PSTCM bit in ADC Control register (D15, offset 0x00),
+ *     - reading ADST bit in ADC Control register (D14, offset 0x00),
+ *             ADC idle would indicate no screen touch.
+ * Unfortunately none of them seems to be 100% accurate and you will
+ * find they are totally inconsistent, i.e. you get to see any arbitrary
+ * combination of values in these three bits.  So we will busy-wait
+ * for a moment when the latter two indicate a pen-up, using a queue,
+ * before we report a pen-up.
+ */
+static void tsc210x_pressure(struct work_struct *work)
+{
+       struct tsc210x_dev *dev =
+               container_of(work, struct tsc210x_dev, ts_worker.work);
+       int adc_status;
+
+       WARN_ON(!dev->pendown);
+
+       adc_status = tsc210x_read_sync(dev, TSC210X_TS_ADC_CTRL);
+       if (adc_status < 0) {
+               dev_dbg(&dev->spi->dev, "pressure, err %d\n", adc_status);
+               return;
+       }
+
+       if ((adc_status & TSC210X_ADC_PSTCM) != 0
+                       || !(adc_status & TSC210X_ADC_ADST))
+               tsc210x_queue_penup(dev);
+       else {
+               dev->pendown = 0;
+               if (dev->touch_cb)
+                       dev->touch_cb(dev->touch_cb_ctx, 0);
+       }
+}
+
+static void tsc210x_wait_data(struct tsc210x_dev *dev)
+{
+       wait_for_completion(&dev->data_avail);
+
+       tsc210x_data_report(dev);
+}
+
+static void tsc210x_input_scan(struct work_struct *work)
+{
+       struct tsc210x_dev *dev = (struct tsc210x_dev *)
+               container_of(work, struct tsc210x_dev, sensor_worker.work);
+
+       tsc210x_new_mode(dev);
+
+       if (dev->pdata->monitor &
+                       (TSC_BAT1 | TSC_BAT2 | TSC_AUX1 | TSC_AUX2)) {
+               tsc210x_portscan_mode(dev);
+               tsc210x_wait_data(dev);
+       }
+
+       if (dev->pdata->monitor & TSC_TEMP) {
+               tsc210x_temp1_mode(dev);
+               tsc210x_wait_data(dev);
+
+               tsc210x_temp2_mode(dev);
+               tsc210x_wait_data(dev);
+       }
+
+       tsc210x_touchscreen_mode(dev);
+
+       mutex_lock(&dev->queue_lock);
+       if (!dev->flushing)
+               tsc210x_queue_scan(dev);
+       mutex_unlock(&dev->queue_lock);
+}
+
+/* ADC has finished a new conversion for us.  */
+static irqreturn_t tsc210x_handler(int irq, void *dev_id)
+{
+       struct tsc210x_dev *dev = (struct tsc210x_dev *) dev_id;
+
+       /* See what data became available.  */
+       tsc210x_submit_async(&dev->req_status);
+
+       return IRQ_HANDLED;
+}
+
+#if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE)
+
+/*
+ * FIXME the audio support shouldn't be included in upstream patches
+ * until it's ready.  They might be better as utility functions linked
+ * with a chip-specific tsc21xx audio module ... e.g. chips with input
+ * channels need more, as will ones with multiple output channels and
+ * so on.  Each of these functions should probably return a fault code,
+ * and will need to be exported so the sound drier can be modular.
+ */
+
+/*
+ * Volume level values should be in the range [0, 127].
+ * Higher values mean lower volume.
+ */
+void tsc210x_set_dac_volume(struct device *dev, u8 left_ch, u8 right_ch)
+{
+       struct tsc210x_dev *tsc = dev_get_drvdata(dev);
+       int val;
+
+       if (tsc->kind == tsc2102) {
+               /* All 0's or all 1's */
+               if (left_ch == 0x00 || left_ch == 0x7f)
+                       left_ch ^= 0x7f;
+               if (right_ch == 0x00 || right_ch == 0x7f)
+                       right_ch ^= 0x7f;
+       }
+
+       val = tsc210x_read_sync(tsc, TSC210X_DAC_GAIN_CTRL);
+       if (val < 0) {
+               dev_dbg(dev, "%s, err %d\n", __FUNCTION__, val);
+               return;
+       }
+
+       val &= 0x8080;  /* Preserve mute-bits */
+       val |= (left_ch << 8) | right_ch;
+
+       tsc210x_write_sync(tsc, TSC210X_DAC_GAIN_CTRL, val);
+       /* NOTE: write_sync() could fail */
+}
+
+void tsc210x_set_dac_mute(struct device *dev, int left_ch, int right_ch)
+{
+       struct tsc210x_dev *tsc = dev_get_drvdata(dev);
+       int val;
+
+       val = tsc210x_read_sync(tsc, TSC210X_DAC_GAIN_CTRL);
+       if (val < 0) {
+               dev_dbg(dev, "%s, err %d\n", __FUNCTION__, val);
+               return;
+       }
+
+       val &= 0x7f7f;  /* Preserve volume settings */
+       val |= (left_ch << 15) | (right_ch << 7);
+
+       tsc210x_write_sync(tsc, TSC210X_DAC_GAIN_CTRL, val);
+       /* NOTE: write_sync() could fail */
+}
+
+void tsc210x_get_dac_mute(struct device *dev, int *left_ch, int *right_ch)
+{
+       struct tsc210x_dev *tsc = dev_get_drvdata(dev);
+       int val;
+
+       val = tsc210x_read_sync(tsc, TSC210X_DAC_GAIN_CTRL);
+       if (val < 0) {
+               dev_dbg(dev, "%s, err %d\n", __FUNCTION__, val);
+               return;
+       }
+
+       *left_ch = !!(val & (1 << 15));
+       *right_ch = !!(val & (1 << 7));
+}
+
+void tsc210x_set_deemphasis(struct device *dev, int enable)
+{
+       struct tsc210x_dev *tsc = dev_get_drvdata(dev);
+       int val;
+
+       val = tsc210x_read_sync(tsc, TSC210X_POWER_CTRL);
+       if (val < 0) {
+               dev_dbg(dev, "%s, err %d\n", __FUNCTION__, val);
+               return;
+       }
+
+       if (enable)
+               val &= ~TSC210X_DEEMPF;
+       else
+               val |= TSC210X_DEEMPF;
+
+       tsc210x_write_sync(tsc, TSC210X_POWER_CTRL, val);
+       /* NOTE: write_sync() could fail */
+}
+
+void tsc2102_set_bassboost(struct device *dev, int enable)
+{
+       struct tsc210x_dev *tsc = dev_get_drvdata(dev);
+       int val;
+
+       val = tsc210x_read_sync(tsc, TSC210X_POWER_CTRL);
+       if (val < 0) {
+               dev_dbg(dev, "%s, err %d\n", __FUNCTION__, val);
+               return;
+       }
+
+       if (enable)
+               val &= ~TSC2102_BASSBC;
+       else
+               val |= TSC2102_BASSBC;
+
+       tsc210x_write_sync(tsc, TSC210X_POWER_CTRL, val);
+       /* NOTE: write_sync() could fail */
+}
+
+/*     {rate, dsor, fsref}     */
+static const struct tsc210x_rate_info_s tsc2101_rates[] = {
+       /* Fsref / 6.0 */
+       {7350,  7,      1},
+       {8000,  7,      0},
+       /* Fsref / 5.5 */
+       {8018,  6,      1},
+       {8727,  6,      0},
+       /* Fsref / 5.0 */
+       {8820,  5,      1},
+       {9600,  5,      0},
+       /* Fsref / 4.0 */
+       {11025, 4,      1},
+       {12000, 4,      0},
+       /* Fsref / 3.0 */
+       {14700, 3,      1},
+       {16000, 3,      0},
+       /* Fsref / 2.0 */
+       {22050, 2,      1},
+       {24000, 2,      0},
+       /* Fsref / 1.5 */
+       {29400, 1,      1},
+       {32000, 1,      0},
+       /* Fsref */
+       {44100, 0,      1},
+       {48000, 0,      0},
+
+       {0,     0,      0},
+};
+
+/*     {rate, dsor, fsref}     */
+static const struct tsc210x_rate_info_s tsc2102_rates[] = {
+       /* Fsref / 6.0 */
+       {7350,  63,     1},
+       {8000,  63,     0},
+       /* Fsref / 6.0 */
+       {7350,  54,     1},
+       {8000,  54,     0},
+       /* Fsref / 5.0 */
+       {8820,  45,     1},
+       {9600,  45,     0},
+       /* Fsref / 4.0 */
+       {11025, 36,     1},
+       {12000, 36,     0},
+       /* Fsref / 3.0 */
+       {14700, 27,     1},
+       {16000, 27,     0},
+       /* Fsref / 2.0 */
+       {22050, 18,     1},
+       {24000, 18,     0},
+       /* Fsref / 1.5 */
+       {29400, 9,      1},
+       {32000, 9,      0},
+       /* Fsref */
+       {44100, 0,      1},
+       {48000, 0,      0},
+
+       {0,     0,      0},
+};
+
+int tsc210x_set_rate(struct device *dev, int rate)
+{
+       struct tsc210x_dev *tsc = dev_get_drvdata(dev);
+       int i;
+       int val;
+       const struct tsc210x_rate_info_s *rates;
+
+       if (tsc->kind == tsc2101)
+               rates = tsc2101_rates;
+       else
+               rates = tsc2102_rates;
+
+       for (i = 0; rates[i].sample_rate; i ++)
+               if (rates[i].sample_rate == rate)
+                       break;
+       if (rates[i].sample_rate == 0) {
+               dev_err(dev, "Unknown sampling rate %i.0 Hz\n", rate);
+               return -EINVAL;
+       }
+
+       if (tsc->kind == tsc2101) {
+               val = tsc210x_read_sync(tsc, TSC210X_AUDIO1_CTRL);
+               if (val < 0) {
+                       dev_dbg(dev, "%s, err %d\n", __FUNCTION__, val);
+                       return val;
+               }
+               val &= ~((7 << 3) | (7 << 0));
+               val |= rates[i].divisor << 3;
+               val |= rates[i].divisor << 0;
+       } else
+               val = rates[i].divisor;
+
+       tsc210x_write_sync(tsc, TSC210X_AUDIO1_CTRL, val);
+       /* NOTE: write_sync() could fail */
+
+       val = tsc210x_read_sync(tsc, TSC210X_AUDIO3_CTRL);
+       if (val < 0) {
+               dev_dbg(dev, "%s, err %d\n", __FUNCTION__, val);
+               return val;
+       }
+
+       if (tsc2102_rates[i].fs_44k) {
+               tsc210x_write_sync(tsc,
+                               TSC210X_AUDIO3_CTRL, val | TSC210X_FS44K);
+               /* Enable Phase-locked-loop, set up clock dividers */
+               tsc210x_write_sync(tsc, TSC210X_PLL1_CTRL, TSC210X_PLL1_44K);
+               tsc210x_write_sync(tsc, TSC210X_PLL2_CTRL, TSC210X_PLL2_44K);
+       } else {
+               tsc210x_write_sync(tsc,
+                               TSC210X_AUDIO3_CTRL, val & ~TSC210X_FS44K);
+               /* Enable Phase-locked-loop, set up clock dividers */
+               tsc210x_write_sync(tsc, TSC210X_PLL1_CTRL, TSC210X_PLL1_48K);
+               tsc210x_write_sync(tsc, TSC210X_PLL2_CTRL, TSC210X_PLL2_48K);
+       }
+
+       return 0;
+}
+
+/*
+ * Perform basic set-up with default values and power the DAC/ADC on.
+ */
+void tsc210x_dac_power(struct device *dev, int on)
+{
+       struct tsc210x_dev *tsc = dev_get_drvdata(dev);
+
+       /* NOTE: write_sync() could fail */
+       if (on) {
+               /* 16-bit words, DSP mode, sample at Fsref */
+               tsc210x_write_sync(tsc,
+                               TSC210X_AUDIO1_CTRL, 0x0100);
+               /* Keyclicks off, soft-stepping at normal rate */
+               tsc210x_write_sync(tsc,
+                               TSC210X_AUDIO2_CTRL, TSC210X_KEYCLICK_OFF);
+               /* 44.1 kHz Fsref, continuous transfer mode, master DAC */
+               tsc210x_write_sync(tsc,
+                               TSC210X_AUDIO3_CTRL, 0x2000);
+               /* Soft-stepping enabled, 1 dB MIX AGC hysteresis */
+               tsc210x_write_sync(tsc,
+                               TSC210X_AUDIO4_CTRL, 0x0000);
+
+               /* PLL generates 44.1 kHz */
+               tsc210x_write_sync(tsc,
+                               TSC210X_PLL1_CTRL, TSC210X_PLL1_44K);
+               tsc210x_write_sync(tsc,
+                               TSC210X_PLL2_CTRL, TSC210X_PLL2_44K);
+
+               /* Codec & DAC power up, virtual ground disabled */
+               tsc210x_write_sync(tsc,
+                               TSC210X_POWER_CTRL, (tsc->kind == tsc2101) ?
+                               TSC2101_DAC_ON : TSC2102_DAC_ON);
+       } else {
+               /* All off */
+               tsc210x_write_sync(tsc,
+                               TSC210X_AUDIO2_CTRL, TSC210X_KEYCLICK_OFF);
+               tsc210x_write_sync(tsc,
+                               TSC210X_PLL1_CTRL, TSC210X_PLL1_OFF);
+#if 0
+               tsc210x_write_sync(tsc,
+                               TSC210X_POWER_CTRL, (tsc->kind == tsc2101) ?
+                               TSC2102_DAC_OFF : TSC2102_DAC_OFF);
+#endif
+       }
+}
+
+void tsc210x_set_i2s_master(struct device *dev, int state)
+{
+       struct tsc210x_dev *tsc = dev_get_drvdata(dev);
+       int val;
+
+       val = tsc210x_read_sync(tsc, TSC210X_AUDIO3_CTRL);
+       if (val < 0) {
+               dev_dbg(dev, "%s, err %d\n", __FUNCTION__, val);
+               return;
+       }
+
+       /* NOTE: write_sync() could fail */
+       if (state)
+               tsc210x_write_sync(tsc, TSC210X_AUDIO3_CTRL,
+                               val | TSC210X_SLVMS);
+       else
+               tsc210x_write_sync(tsc, TSC210X_AUDIO3_CTRL,
+                               val & ~TSC210X_SLVMS);
+}
+#endif /* CONFIG_SOUND */
+
+static int tsc210x_configure(struct tsc210x_dev *dev)
+{
+       /* NOTE: write_sync() could fail */
+
+       /* Reset the chip */
+       tsc210x_write_sync(dev, TSC210X_TS_RESET_CTRL, TSC210X_RESET);
+
+       /* Reference mode */
+       if (dev->pdata->use_internal)
+               tsc210x_write_sync(dev,
+                               TSC210X_TS_REF_CTRL, TSC210X_ADC_INT_REF);
+       else
+               tsc210x_write_sync(dev,
+                               TSC210X_TS_REF_CTRL, TSC210X_ADC_EXT_REF);
+
+       /* Precharge and sense delays, pen touch detection on */
+       tsc210x_write_sync(dev, TSC210X_TS_CONFIG_CTRL, TSC210X_CONFIG_TIMES);
+
+       /* PINT/DAV acts as DAV */
+       tsc210x_write_sync(dev, TSC210X_TS_STATUS_CTRL, TSC210X_ADC_DAV);
+
+       tsc210x_queue_scan(dev);
+       return 0;
+}
+
+void tsc210x_keyclick(struct tsc210x_dev *dev,
+               int amplitude, int freq, int length)
+{
+       int val;
+
+       val = tsc210x_read_sync(dev, TSC210X_AUDIO2_CTRL);
+       if (val < 0) {
+               dev_dbg(&dev->spi->dev, "%s, err %d\n",
+                               __FUNCTION__, val);
+               return;
+       }
+       val &= 0x800f;
+
+       /* Set amplitude */
+       switch (amplitude) {
+       case 1:
+               val |= 4 << 12;
+               break;
+       case 2:
+               val |= 7 << 12;
+               break;
+       default:
+               break;
+       }
+
+       /* Frequency */
+       val |= (freq & 0x7) << 8;
+
+       /* Round to nearest supported length */
+       if (dev->kind == tsc2101)
+               val = (min(length - 1, 31) >> 1) << 4;
+       else {
+               if (length > 20)
+                       val |= 4 << 4;
+               else if (length > 6)
+                       val |= 3 << 4;
+               else if (length > 4)
+                       val |= 2 << 4;
+               else if (length > 2)
+                       val |= 1 << 4;
+       }
+
+       /* Enable keyclick */
+       val |= 0x8000;
+
+       /* NOTE: write_sync() could fail */
+       tsc210x_write_sync(dev, TSC210X_AUDIO2_CTRL, val);
+}
+EXPORT_SYMBOL(tsc210x_keyclick);
+
+#ifdef CONFIG_PM
+/*
+ * Suspend the chip.
+ */
+static int
+tsc210x_suspend(struct spi_device *spi, pm_message_t state)
+{
+       struct tsc210x_dev *dev = dev_get_drvdata(&spi->dev);
+
+       if (!dev)
+               return -ENODEV;
+
+       /* Stop the inputs scan loop */
+       mutex_lock(&dev->queue_lock);
+       dev->flushing = 1;
+       cancel_delayed_work(&dev->sensor_worker);
+       mutex_unlock(&dev->queue_lock);
+       flush_workqueue(dev->queue);
+
+       /* Wait until pen-up happens */
+       while (dev->pendown)
+               flush_workqueue(dev->queue);
+
+       /* Abort current conversion and power down the ADC */
+       tsc210x_write_sync(dev, TSC210X_TS_ADC_CTRL, TSC210X_ADC_ADST);
+       /* NOTE: write_sync() could fail */
+
+       return 0;
+}
+
+/*
+ * Resume chip operation.
+ */
+static int tsc210x_resume(struct spi_device *spi)
+{
+       struct tsc210x_dev *dev = dev_get_drvdata(&spi->dev);
+       int err;
+
+       if (!dev)
+               return 0;
+
+       mutex_lock(&dev->queue_lock);
+       err = tsc210x_configure(dev);
+
+       dev->flushing = 0;
+       mutex_unlock(&dev->queue_lock);
+
+       return err;
+}
+#else
+#define tsc210x_suspend        NULL
+#define tsc210x_resume NULL
+#endif
+
+/* REVISIT don't make these static */
+static struct platform_device tsc210x_ts_device = {
+       .name           = "tsc210x-ts",
+       .id             = -1,
+};
+
+static struct platform_device tsc210x_hwmon_device = {
+       .name           = "tsc210x-hwmon",
+       .id             = -1,
+};
+
+static struct platform_device tsc210x_alsa_device = {
+       .name           = "tsc210x-alsa",
+       .id             = -1,
+};
+
+static int tsc210x_probe(struct spi_device *spi, enum tsc_type type)
+{
+       struct tsc210x_config *pdata = spi->dev.platform_data;
+       struct spi_transfer *spi_buffer;
+       struct tsc210x_dev *dev;
+       int reg;
+       int err = 0;
+
+       if (!pdata) {
+               dev_dbg(&spi->dev, "Platform data not supplied\n");
+               return -ENOENT;
+       }
+
+       if (!spi->irq) {
+               dev_dbg(&spi->dev, "Invalid irq value\n");
+               return -EINVAL;
+       }
+
+       dev = (struct tsc210x_dev *)
+               kzalloc(sizeof(struct tsc210x_dev), GFP_KERNEL);
+       if (!dev) {
+               dev_dbg(&spi->dev, "No memory\n");
+               return -ENOMEM;
+       }
+
+       dev->pdata = pdata;
+       dev->pendown = 0;
+       dev->spi = spi;
+       dev->kind = type;
+       dev->queue = create_singlethread_workqueue(spi->dev.driver->name);
+       if (!dev->queue) {
+               dev_dbg(&spi->dev, "Can't make a workqueue\n");
+               err = -ENOMEM;
+               goto err_queue;
+       }
+
+       mutex_init(&dev->queue_lock);
+       init_completion(&dev->data_avail);
+
+       /* Allocate enough struct spi_transfer's for all requests */
+       spi_buffer = kzalloc(sizeof(struct spi_transfer) * 16, GFP_KERNEL);
+       if (!spi_buffer) {
+               dev_dbg(&spi->dev, "No memory for SPI buffers\n");
+               err = -ENOMEM;
+               goto err_buffers;
+       }
+
+       dev->transfers = spi_buffer;
+       tsc210x_request_alloc(dev, &dev->req_adc, 0,
+                       TSC210X_TS_X, 4, dev->adc_data,
+                       tsc210x_coords_report, &spi_buffer);
+       tsc210x_request_alloc(dev, &dev->req_status, 0,
+                       TSC210X_TS_STATUS_CTRL, 1, &dev->status,
+                       tsc210x_status_report, &spi_buffer);
+       tsc210x_request_alloc(dev, &dev->req_mode, 1,
+                       TSC210X_TS_ADC_CTRL, 1, NULL,
+                       tsc210x_complete_dummy, &spi_buffer);
+       tsc210x_request_alloc(dev, &dev->req_stop, 1,
+                       TSC210X_TS_ADC_CTRL, 1, NULL,
+                       tsc210x_complete_dummy, &spi_buffer);
+
+       if (pdata->bclk) {
+               /* Get the BCLK */
+               dev->bclk_ck = clk_get(&spi->dev, pdata->bclk);
+               if (IS_ERR(dev->bclk_ck)) {
+                       err = PTR_ERR(dev->bclk_ck);
+                       dev_dbg(&spi->dev, "Unable to get '%s': %i\n",
+                                       pdata->bclk, err);
+                       goto err_clk;
+               }
+
+               clk_enable(dev->bclk_ck);
+       }
+
+       INIT_DELAYED_WORK(&dev->ts_worker, tsc210x_pressure);
+       INIT_DELAYED_WORK(&dev->sensor_worker, tsc210x_input_scan);
+
+       /* Setup the communication bus */
+       dev_set_drvdata(&spi->dev, dev);
+       spi->mode = SPI_MODE_1;
+       spi->bits_per_word = 16;
+       err = spi_setup(spi);
+       if (err)
+               goto err_spi;
+
+       /* Now try to detect the chip, make first contact.  These chips
+        * don't self-identify, but we can expect that the status register
+        * reports the ADC is idle and use that as a sanity check.  (It'd
+        * be even better if we did a soft reset first...)
+        */
+       reg = tsc210x_read_sync(dev, TSC210X_TS_ADC_CTRL);
+       if (reg < 0) {
+               err = reg;
+               dev_dbg(&dev->spi->dev, "adc_ctrl, err %d\n", err);
+               goto err_spi;
+       }
+       if (!(reg & (1 << 14))) {
+               err = -EIO;
+               dev_dbg(&dev->spi->dev, "adc_ctrl, busy? - %04x\n", reg);
+               goto err_spi;
+       }
+
+       reg = tsc210x_read_sync(dev, TSC210X_AUDIO3_CTRL);
+       if (reg < 0) {
+               err = reg;
+               dev_dbg(&dev->spi->dev, "revision, err %d\n", err);
+               goto err_spi;
+       }
+       if (reg == 0xffff) {
+               err = -ENODEV;
+               dev_dbg(&dev->spi->dev, "no device, err %d\n", err);
+               goto err_spi;
+       }
+       dev_info(&spi->dev, "rev %d, irq %d\n", reg & 0x0007, spi->irq);
+
+       err = tsc210x_configure(dev);
+       if (err)
+               goto err_spi;
+
+       /* We want no interrupts before configuration succeeds.  */
+       mutex_lock(&dev->queue_lock);
+       dev->flushing = 1;
+
+       if (request_irq(spi->irq, tsc210x_handler, IRQF_SAMPLE_RANDOM |
+                               IRQF_TRIGGER_FALLING, spi->dev.driver->name,
+                               dev)) {
+               dev_dbg(&spi->dev, "Could not allocate touchscreen IRQ!\n");
+               err = -EINVAL;
+               goto err_irq;
+       }
+
+       /* Register subdevices controlled by the TSC 2101/2102 */
+       tsc210x_ts_device.dev.platform_data = dev;
+       tsc210x_ts_device.dev.parent = &spi->dev;
+       err = platform_device_register(&tsc210x_ts_device);
+       if (err)
+               goto err_irq;
+
+       tsc210x_hwmon_device.dev.platform_data = pdata;
+       tsc210x_hwmon_device.dev.parent = &spi->dev;
+       err = platform_device_register(&tsc210x_hwmon_device);
+       if (err)
+               goto err_hwmon;
+
+       tsc210x_alsa_device.dev.platform_data = pdata->alsa_config;
+       tsc210x_alsa_device.dev.parent = &spi->dev;
+       err = platform_device_register(&tsc210x_alsa_device);
+       if (err)
+               goto err_alsa;
+
+       dev->flushing = 0;
+       mutex_unlock(&dev->queue_lock);
+       return 0;
+
+err_alsa:
+       platform_device_unregister(&tsc210x_hwmon_device);
+err_hwmon:
+       platform_device_unregister(&tsc210x_ts_device);
+err_irq:
+       mutex_unlock(&dev->queue_lock);
+err_spi:
+       dev_set_drvdata(&spi->dev, NULL);
+       clk_disable(dev->bclk_ck);
+       clk_put(dev->bclk_ck);
+err_clk:
+       kfree(dev->transfers);
+err_buffers:
+       destroy_workqueue(dev->queue);
+err_queue:
+       kfree(dev);
+       return err;
+}
+
+static int tsc2101_probe(struct spi_device *spi)
+{
+       return tsc210x_probe(spi, tsc2101);
+}
+
+static int tsc2102_probe(struct spi_device *spi)
+{
+       return tsc210x_probe(spi, tsc2102);
+}
+
+static int tsc210x_remove(struct spi_device *spi)
+{
+       struct tsc210x_dev *dev = dev_get_drvdata(&spi->dev);
+
+       /* Stop the inputs scan loop */
+       mutex_lock(&dev->queue_lock);
+       dev->flushing = 1;
+       cancel_delayed_work(&dev->sensor_worker);
+       mutex_unlock(&dev->queue_lock);
+       flush_workqueue(dev->queue);
+
+       /* Wait for pen-up */
+       while (dev->pendown)
+               flush_workqueue(dev->queue);
+
+       /* Abort current conversion and power down the ADC */
+       tsc210x_write_sync(dev, TSC210X_TS_ADC_CTRL, TSC210X_ADC_ADST);
+       /* NOTE: write_sync() could fail */
+
+       destroy_workqueue(dev->queue);
+
+       platform_device_unregister(&tsc210x_ts_device);
+       platform_device_unregister(&tsc210x_hwmon_device);
+       platform_device_unregister(&tsc210x_alsa_device);
+
+       dev_set_drvdata(&spi->dev, NULL);
+
+       /* Release the BCLK */
+       clk_disable(dev->bclk_ck);
+       clk_put(dev->bclk_ck);
+
+       kfree(dev->transfers);
+       kfree(dev);
+
+       return 0;
+}
+
+static struct spi_driver tsc2101_driver = {
+       .probe          = tsc2101_probe,
+       .remove         = tsc210x_remove,
+       .suspend        = tsc210x_suspend,
+       .resume         = tsc210x_resume,
+       .driver         = {
+               .name   = "tsc2101",
+               .owner  = THIS_MODULE,
+               .bus    = &spi_bus_type,
+       },
+};
+
+static struct spi_driver tsc2102_driver = {
+       .probe          = tsc2102_probe,
+       .remove         = tsc210x_remove,
+       .suspend        = tsc210x_suspend,
+       .resume         = tsc210x_resume,
+       .driver         = {
+               .name   = "tsc2102",
+               .owner  = THIS_MODULE,
+               .bus    = &spi_bus_type,
+       },
+};
+
+static char __initdata banner[] = KERN_INFO "TI TSC210x driver initializing\n";
+
+static int __init tsc210x_init(void)
+{
+       int err;
+       printk(banner);
+
+       settings.ts_msecs = 20;
+       settings.mode_msecs = 1000;
+
+       err = spi_register_driver(&tsc2101_driver);
+       if (err != 0)
+               return err;
+
+       err = spi_register_driver(&tsc2102_driver);
+       if (err != 0)
+               spi_unregister_driver(&tsc2101_driver);
+
+       return err;
+}
+module_init(tsc210x_init);
+
+static void __exit tsc210x_exit(void)
+{
+       spi_unregister_driver(&tsc2101_driver);
+       spi_unregister_driver(&tsc2102_driver);
+}
+module_exit(tsc210x_exit);
+
+MODULE_AUTHOR("Andrzej Zaborowski");
+MODULE_DESCRIPTION("Interface driver for TI TSC210x chips.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/spi/tsc2301-core.c b/drivers/spi/tsc2301-core.c
new file mode 100644 (file)
index 0000000..d64a274
--- /dev/null
@@ -0,0 +1,287 @@
+/*
+ * TSC2301 driver
+ *
+ * Copyright (C) 2005, 2006 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 <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/tsc2301.h>
+
+u16 tsc2301_read_reg(struct tsc2301 *tsc, int reg)
+{
+       struct spi_transfer t[2];
+       struct spi_message m;
+       u16 data = 0, cmd;
+
+       cmd = reg;
+       cmd |= 0x8000;
+
+       memset(t, 0, sizeof(t));
+       spi_message_init(&m);
+       m.spi = tsc->spi;
+
+       t[0].tx_buf = &cmd;
+       t[0].rx_buf = NULL;
+       t[0].len = 2;
+       spi_message_add_tail(&t[0], &m);
+
+       t[1].tx_buf = NULL;
+       t[1].rx_buf = &data;
+       t[1].len = 2;
+       spi_message_add_tail(&t[1], &m);
+
+       spi_sync(m.spi, &m);
+
+       return data;
+}
+
+void tsc2301_write_reg(struct tsc2301 *tsc, int reg, u16 val)
+{
+       struct spi_transfer t;
+       struct spi_message m;
+       u16 data[2];
+
+       /* Now we prepare the command for transferring */
+       data[0] = reg;
+       data[1] = val;
+
+       spi_message_init(&m);
+       m.spi = tsc->spi;
+
+       memset(&t, 0, sizeof(t));
+       t.tx_buf = data;
+       t.rx_buf = NULL;
+       t.len = 4;
+       spi_message_add_tail(&t, &m);
+
+       spi_sync(m.spi, &m);
+}
+
+void tsc2301_write_kbc(struct tsc2301 *tsc, int val)
+{
+       u16 w;
+
+       w = tsc->config2_shadow;
+       w &= ~(0x03 << 14);
+       w |= (val & 0x03) << 14;
+       tsc2301_write_reg(tsc, TSC2301_REG_CONFIG2, w);
+       tsc->config2_shadow = w;
+}
+
+void tsc2301_write_pll(struct tsc2301 *tsc,
+                      int pll_n, int pll_a, int pll_pdc, int pct_e, int pll_o)
+{
+       u16 w;
+
+       w = tsc->config2_shadow;
+       w &= ~0x3fff;
+       w |= (pll_n & 0x0f) | ((pll_a & 0x0f) << 4) | ((pll_pdc & 0x0f) << 8);
+       w |= pct_e ? (1 << 12) : 0;
+       w |= pll_o ? (1 << 13) : 0;
+       tsc2301_write_reg(tsc, TSC2301_REG_CONFIG2, w);
+       tsc->config2_shadow = w;
+}
+
+void tsc2301_read_buf(struct tsc2301 *tsc, int reg, u16 *rx_buf, int len)
+{
+       struct spi_transfer t[2];
+       struct spi_message m;
+       u16 cmd, i;
+
+       cmd = reg;
+       cmd |= 0x8000;
+
+       spi_message_init(&m);
+       m.spi = tsc->spi;
+
+       memset(t, 0, sizeof(t));
+       t[0].tx_buf = &cmd;
+       t[0].rx_buf = NULL;
+       t[0].len = 2;
+       spi_message_add_tail(&t[0], &m);
+
+       t[1].tx_buf = NULL;
+       t[1].rx_buf = rx_buf;
+       t[1].len = 2 * len;
+       spi_message_add_tail(&t[1], &m);
+
+       spi_sync(m.spi, &m);
+
+       for (i = 0; i < len; i++)
+               printk(KERN_DEBUG "rx_buf[%d]: %04x\n", i, rx_buf[i]);
+}
+
+static int __devinit tsc2301_probe(struct spi_device *spi)
+{
+       struct tsc2301                  *tsc;
+       struct tsc2301_platform_data    *pdata = spi->dev.platform_data;
+       int r;
+       u16 w;
+
+       if (!pdata) {
+               dev_dbg(&spi->dev, "no platform data?\n");
+               return -ENODEV;
+       }
+
+       tsc = kzalloc(sizeof(*tsc), GFP_KERNEL);
+       if (tsc == NULL)
+               return -ENOMEM;
+
+       dev_set_drvdata(&spi->dev, tsc);
+       tsc->spi = spi;
+
+       tsc->enable_clock = pdata->enable_clock;
+       tsc->disable_clock = pdata->disable_clock;
+
+       if (pdata->reset_gpio >= 0) {
+               tsc->reset_gpio = pdata->reset_gpio;
+               r = gpio_request(tsc->reset_gpio, "TSC2301 reset");
+               if (r < 0)
+                       goto err1;
+               gpio_direction_output(tsc->reset_gpio, 1);
+               mdelay(1);
+               gpio_set_value(tsc->reset_gpio, 0);
+       } else
+               tsc->reset_gpio = -1;
+
+       spi->mode = SPI_MODE_1;
+       spi->bits_per_word = 16;
+       /* The max speed might've been defined by the board-specific
+        * struct */
+       if (!spi->max_speed_hz)
+               spi->max_speed_hz = TSC2301_HZ;
+       spi_setup(spi);
+
+       /* Soft reset */
+       tsc2301_write_reg(tsc, TSC2301_REG_RESET, 0xbb00);
+       msleep(1);
+
+       w = tsc2301_read_reg(tsc, TSC2301_REG_ADC);
+       if (!(w & (1 << 14))) {
+               dev_err(&spi->dev, "invalid ADC reg value: %04x\n", w);
+               r = -ENODEV;
+               goto err1;
+       }
+
+       w = tsc2301_read_reg(tsc, TSC2301_REG_DAC);
+       if (!(w & (1 << 15))) {
+               dev_err(&spi->dev, "invalid DAC reg value: %04x\n", w);
+               r = -ENODEV;
+               goto err1;
+       }
+
+       /* Stop keypad scanning */
+       tsc2301_write_reg(tsc, TSC2301_REG_KEY, 0x4000);
+
+       /* We have to cache this for read-modify-write, since we can't
+        * read back BIT15 */
+       w = tsc2301_read_reg(tsc, TSC2301_REG_CONFIG2);
+       /* By default BIT15 is set */
+       w |= 1 << 15;
+       tsc->config2_shadow = w;
+
+       r = tsc2301_kp_init(tsc, pdata);
+       if (r)
+               goto err1;
+       r = tsc2301_ts_init(tsc, pdata);
+       if (r)
+               goto err2;
+       return 0;
+
+       tsc2301_ts_exit(tsc);
+err2:
+       tsc2301_kp_exit(tsc);
+err1:
+       kfree(tsc);
+       return r;
+}
+
+static int __devexit tsc2301_remove(struct spi_device *spi)
+{
+       struct tsc2301 *tsc = dev_get_drvdata(&spi->dev);
+
+       tsc2301_ts_exit(tsc);
+       tsc2301_kp_exit(tsc);
+       if (tsc->reset_gpio >= 0)
+               gpio_free(tsc->reset_gpio);
+       kfree(tsc);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int tsc2301_suspend(struct spi_device *spi, pm_message_t mesg)
+{
+       struct tsc2301 *tsc = dev_get_drvdata(&spi->dev);
+       int r;
+
+       if ((r = tsc2301_kp_suspend(tsc)) < 0)
+               return r;
+       if ((r = tsc2301_ts_suspend(tsc)) < 0) {
+               tsc2301_kp_resume(tsc);
+               return r;
+       }
+
+       return 0;
+}
+
+static int tsc2301_resume(struct spi_device *spi)
+{
+       struct tsc2301 *tsc = dev_get_drvdata(&spi->dev);
+
+       tsc2301_ts_resume(tsc);
+       tsc2301_kp_resume(tsc);
+
+       return 0;
+}
+#endif
+
+static struct spi_driver tsc2301_driver = {
+       .driver = {
+                  .name = "tsc2301",
+                  .bus = &spi_bus_type,
+                  .owner = THIS_MODULE,
+       },
+#ifdef CONFIG_PM
+       .suspend = tsc2301_suspend,
+       .resume = tsc2301_resume,
+#endif
+       .probe = tsc2301_probe,
+       .remove = __devexit_p(tsc2301_remove),
+};
+
+static int __init tsc2301_init(void)
+{
+       printk("TSC2301 driver initializing\n");
+
+       return spi_register_driver(&tsc2301_driver);
+}
+module_init(tsc2301_init);
+
+static void __exit tsc2301_exit(void)
+{
+       spi_unregister_driver(&tsc2301_driver);
+}
+module_exit(tsc2301_exit);
+
+MODULE_AUTHOR("Juha Yrjölä <juha.yrjola@nokia.com>");
+MODULE_LICENSE("GPL");
index 44408850e2ebc2d0047e1bae7d7a82a5d735e135..c355b59d2d816993f8a61716d75866dd12c93123 100644 (file)
@@ -7,6 +7,69 @@ config FB_OMAP
        help
           Frame buffer driver for OMAP based boards.
 
        help
           Frame buffer driver for OMAP based boards.
 
+config FB_OMAP_LCD_VGA
+        bool "Use LCD in VGA mode"
+               depends on MACH_OMAP_3430SDP || MACH_OMAP_LDP
+
+choice
+       depends on FB_OMAP && MACH_OVERO
+       prompt "Screen resolution"
+       default FB_OMAP_079M3R
+       help
+         Selected desired screen resolution
+
+config FB_OMAP_031M3R
+       boolean "640 x 480 @ 60 Hz Reduced blanking"
+
+config FB_OMAP_048M3R
+       boolean "800 x 600 @ 60 Hz Reduced blanking"
+
+config FB_OMAP_079M3R
+       boolean "1024 x 768 @ 60 Hz Reduced blanking"
+
+config FB_OMAP_092M9R
+       boolean "1280 x 720 @ 60 Hz Reduced blanking"
+
+endchoice
+
+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_LCDC_BLIZZARD
+       bool "Epson Blizzard 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 Blizzard 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_MIPID
+       bool "MIPI DBI-C/DCS compatible LCD support"
+       depends on FB_OMAP && SPI_MASTER && CBUS_TAHVO
+       help
+         Say Y here if you want to have support for LCDs compatible with
+         the Mobile Industry Processor Interface DBI-C/DCS
+         specification. (Supported LCDs: Philips LPH8923, Sharp LS041Y3)
+
 config FB_OMAP_BOOTLOADER_INIT
        bool "Check bootloader initialization"
        depends on FB_OMAP
 config FB_OMAP_BOOTLOADER_INIT
        bool "Check bootloader initialization"
        depends on FB_OMAP
@@ -36,23 +99,4 @@ config FB_OMAP_DMA_TUNE
           answer yes. Answer no if you have a dedicated video
           memory, or don't use any of the accelerated features.
 
           answer yes. Answer no if you have a dedicated video
           memory, or don't use any of the accelerated features.
 
-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_LCDC_BLIZZARD
-       bool "Epson Blizzard 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 Blizzard LCD controller.
index ed13889c116223e16f99ec32de9c0c1762e54897..b63b198d1f03285ad5240253250512798ea68f38 100644 (file)
@@ -8,6 +8,7 @@ objs-yy := omapfb_main.o
 
 objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o
 objs-y$(CONFIG_ARCH_OMAP2) += dispc.o
 
 objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o
 objs-y$(CONFIG_ARCH_OMAP2) += dispc.o
+objs-y$(CONFIG_ARCH_OMAP3) += 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-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o
 objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o
@@ -15,6 +16,7 @@ objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o
 objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o
 objs-y$(CONFIG_FB_OMAP_LCDC_BLIZZARD) += blizzard.o
 
 objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o
 objs-y$(CONFIG_FB_OMAP_LCDC_BLIZZARD) += blizzard.o
 
+objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o
 objs-y$(CONFIG_MACH_OMAP_H4) += lcd_h4.o
 objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o
 objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o
 objs-y$(CONFIG_MACH_OMAP_H4) += lcd_h4.o
 objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o
 objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o
@@ -24,5 +26,15 @@ 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-$(CONFIG_ARCH_OMAP15XX)$(CONFIG_MACH_OMAP_INNOVATOR) += lcd_inn1510.o
 objs-y$(CONFIG_MACH_OMAP_OSK) += lcd_osk.o
 
+objs-y$(CONFIG_MACH_OMAP_APOLLON) += lcd_apollon.o
+objs-y$(CONFIG_MACH_OMAP_2430SDP) += lcd_2430sdp.o
+objs-y$(CONFIG_MACH_OMAP_3430SDP) += lcd_2430sdp.o
+objs-y$(CONFIG_MACH_OMAP_LDP) += lcd_ldp.o
+objs-y$(CONFIG_MACH_OMAP2EVM) += lcd_omap2evm.o
+objs-y$(CONFIG_MACH_OMAP3EVM) += lcd_omap3evm.o
+objs-y$(CONFIG_MACH_OMAP3_BEAGLE) += lcd_omap3beagle.o
+objs-y$(CONFIG_FB_OMAP_LCD_MIPID) += lcd_mipid.o
+objs-y$(CONFIG_MACH_OVERO) += lcd_overo.o
+
 omapfb-objs := $(objs-yy)
 
 omapfb-objs := $(objs-yy)
 
index 9dfcf39d3367370c0d3ec2507aa3bb3e036edef6..f60a2338ef987e289878273bbe72f0a903bdad55 100644 (file)
@@ -44,6 +44,7 @@
 #define BLIZZARD_CLK_SRC                       0x0e
 #define BLIZZARD_MEM_BANK0_ACTIVATE            0x10
 #define BLIZZARD_MEM_BANK0_STATUS              0x14
 #define BLIZZARD_CLK_SRC                       0x0e
 #define BLIZZARD_MEM_BANK0_ACTIVATE            0x10
 #define BLIZZARD_MEM_BANK0_STATUS              0x14
+#define BLIZZARD_PANEL_CONFIGURATION           0x28
 #define BLIZZARD_HDISP                         0x2a
 #define BLIZZARD_HNDP                          0x2c
 #define BLIZZARD_VDISP0                                0x2e
 #define BLIZZARD_HDISP                         0x2a
 #define BLIZZARD_HNDP                          0x2c
 #define BLIZZARD_VDISP0                                0x2e
@@ -162,6 +163,10 @@ struct blizzard_struct {
        int                     vid_scaled;
        int                     last_color_mode;
        int                     zoom_on;
        int                     vid_scaled;
        int                     last_color_mode;
        int                     zoom_on;
+       int                     zoom_area_gx1;
+       int                     zoom_area_gx2;
+       int                     zoom_area_gy1;
+       int                     zoom_area_gy2;
        int                     screen_width;
        int                     screen_height;
        unsigned                te_connected:1;
        int                     screen_width;
        int                     screen_height;
        unsigned                te_connected:1;
@@ -513,6 +518,12 @@ static int do_full_screen_update(struct blizzard_request *req)
        return REQ_PENDING;
 }
 
        return REQ_PENDING;
 }
 
+static int check_1d_intersect(int a1, int a2, int b1, int b2)
+{
+    if (a2 <= b1 || b2 <= a1) return 0;
+    return 1;
+}
+
 /* Setup all planes with an overlapping area with the update window. */
 static int do_partial_update(struct blizzard_request *req, int plane,
                             int x, int y, int w, int h,
 /* Setup all planes with an overlapping area with the update window. */
 static int do_partial_update(struct blizzard_request *req, int plane,
                             int x, int y, int w, int h,
@@ -525,6 +536,7 @@ static int do_partial_update(struct blizzard_request *req, int plane,
        int color_mode;
        int flags;
        int zoom_off;
        int color_mode;
        int flags;
        int zoom_off;
+       int have_zoom_for_this_update = 0;
 
        /* Global coordinates, relative to pixel 0,0 of the LCD */
        gx1 = x + blizzard.plane[plane].pos_x;
 
        /* Global coordinates, relative to pixel 0,0 of the LCD */
        gx1 = x + blizzard.plane[plane].pos_x;
@@ -544,10 +556,6 @@ static int do_partial_update(struct blizzard_request *req, int plane,
                gx2_out = gx1_out + w_out;
                gy2_out = gy1_out + h_out;
        }
                gx2_out = gx1_out + w_out;
                gy2_out = gy1_out + h_out;
        }
-       zoom_off = blizzard.zoom_on && gx1 == 0 && gy1 == 0 &&
-               w == blizzard.screen_width && h == blizzard.screen_height;
-       blizzard.zoom_on = (!zoom_off && blizzard.zoom_on) ||
-                          (w < w_out || h < h_out);
 
        for (i = 0; i < OMAPFB_PLANE_NUM; i++) {
                struct plane_info *p = &blizzard.plane[i];
 
        for (i = 0; i < OMAPFB_PLANE_NUM; i++) {
                struct plane_info *p = &blizzard.plane[i];
@@ -653,8 +661,49 @@ static int do_partial_update(struct blizzard_request *req, int plane,
        else
                disable_tearsync();
 
        else
                disable_tearsync();
 
+       if ((gx2_out - gx1_out) != (gx2 - gx1) ||
+           (gy2_out - gy1_out) != (gy2 - gy1))
+               have_zoom_for_this_update = 1;
+
+       /* 'background' type of screen update (as opposed to 'destructive') 
+          can be used to disable scaling if scaling is active */
+       zoom_off = blizzard.zoom_on && !have_zoom_for_this_update &&
+           (gx1_out == 0) && (gx2_out == blizzard.screen_width) &&
+           (gy1_out == 0) && (gy2_out == blizzard.screen_height) &&
+           (gx1 == 0) && (gy1 == 0);
+
+       if (blizzard.zoom_on && !have_zoom_for_this_update && !zoom_off &&
+           check_1d_intersect(blizzard.zoom_area_gx1, blizzard.zoom_area_gx2,
+                              gx1_out, gx2_out) &&
+           check_1d_intersect(blizzard.zoom_area_gy1, blizzard.zoom_area_gy2,
+                              gy1_out, gy2_out)) {
+               /* Previous screen update was using scaling, current update
+                * is not using it. Additionally, current screen update is
+                * going to overlap with the scaled area. Scaling needs to be
+                * disabled in order to avoid 'magnifying glass' effect.
+                * Dummy setup of background window can be used for this.
+                */
+               set_window_regs(0, 0, blizzard.screen_width,
+                               blizzard.screen_height,
+                               0, 0, blizzard.screen_width,
+                               blizzard.screen_height,
+                               BLIZZARD_COLOR_RGB565, 1, flags);
+               blizzard.zoom_on = 0;
+       }
+
+       /* remember scaling settings if we have scaled update */
+       if (have_zoom_for_this_update) {
+               blizzard.zoom_on = 1;
+               blizzard.zoom_area_gx1 = gx1_out;
+               blizzard.zoom_area_gx2 = gx2_out;
+               blizzard.zoom_area_gy1 = gy1_out;
+               blizzard.zoom_area_gy2 = gy2_out;
+       }
+
        set_window_regs(gx1, gy1, gx2, gy2, gx1_out, gy1_out, gx2_out, gy2_out,
                        color_mode, zoom_off, flags);
        set_window_regs(gx1, gy1, gx2, gy2, gx1_out, gy1_out, gx2_out, gy2_out,
                        color_mode, zoom_off, flags);
+       if (zoom_off)
+               blizzard.zoom_on = 0;
 
        blizzard.extif->set_bits_per_cycle(16);
        /* set_window_regs has left the register index at the right
 
        blizzard.extif->set_bits_per_cycle(16);
        /* set_window_regs has left the register index at the right
@@ -908,6 +957,35 @@ static int blizzard_set_scale(int plane, int orig_w, int orig_h,
        return 0;
 }
 
        return 0;
 }
 
+static int blizzard_set_rotate(int angle)
+{
+       u32 l;
+
+       l = blizzard_read_reg(BLIZZARD_PANEL_CONFIGURATION);
+       l &= ~0x03;
+
+       switch (angle) {
+       case 0:
+               l = l | 0x00;
+               break;
+       case 90:
+               l = l | 0x03;
+               break;
+       case 180:
+               l = l | 0x02;
+               break;
+       case 270:
+               l = l | 0x01;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       blizzard_write_reg(BLIZZARD_PANEL_CONFIGURATION, l);
+
+       return 0;
+}
+
 static int blizzard_enable_plane(int plane, int enable)
 {
        if (enable)
 static int blizzard_enable_plane(int plane, int enable)
 {
        if (enable)
@@ -1285,7 +1363,8 @@ static void blizzard_get_caps(int plane, struct omapfb_caps *caps)
        caps->ctrl |= OMAPFB_CAPS_MANUAL_UPDATE |
                OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE |
                OMAPFB_CAPS_WINDOW_SCALE |
        caps->ctrl |= OMAPFB_CAPS_MANUAL_UPDATE |
                OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE |
                OMAPFB_CAPS_WINDOW_SCALE |
-               OMAPFB_CAPS_WINDOW_OVERLAY;
+               OMAPFB_CAPS_WINDOW_OVERLAY |
+               OMAPFB_CAPS_WINDOW_ROTATE;
        if (blizzard.te_connected)
                caps->ctrl |= OMAPFB_CAPS_TEARSYNC;
        caps->wnd_color |= (1 << OMAPFB_COLOR_RGB565) |
        if (blizzard.te_connected)
                caps->ctrl |= OMAPFB_CAPS_TEARSYNC;
        caps->wnd_color |= (1 << OMAPFB_COLOR_RGB565) |
@@ -1560,6 +1639,7 @@ struct lcd_ctrl blizzard_ctrl = {
        .setup_plane            = blizzard_setup_plane,
        .set_scale              = blizzard_set_scale,
        .enable_plane           = blizzard_enable_plane,
        .setup_plane            = blizzard_setup_plane,
        .set_scale              = blizzard_set_scale,
        .enable_plane           = blizzard_enable_plane,
+       .set_rotate             = blizzard_set_rotate,
        .update_window          = blizzard_update_window_async,
        .sync                   = blizzard_sync,
        .suspend                = blizzard_suspend,
        .update_window          = blizzard_update_window_async,
        .sync                   = blizzard_sync,
        .suspend                = blizzard_suspend,
index dfb72f5e4c962ad35098fb07a65252b99be31941..c140c214223c64545ba9f6d303776ea24301428f 100644 (file)
@@ -155,6 +155,8 @@ struct resmap {
        unsigned long   *map;
 };
 
        unsigned long   *map;
 };
 
+#define MAX_IRQ_HANDLERS            4
+
 static struct {
        void __iomem    *base;
 
 static struct {
        void __iomem    *base;
 
@@ -167,9 +169,11 @@ static struct {
 
        int             ext_mode;
 
 
        int             ext_mode;
 
-       unsigned long   enabled_irqs;
-       void            (*irq_callback)(void *);
-       void            *irq_callback_data;
+       struct {
+               u32     irq_mask;
+               void    (*callback)(void *);
+               void    *data;
+       } irq_handlers[MAX_IRQ_HANDLERS];
        struct completion       frame_done;
 
        int             fir_hinc[OMAPFB_PLANE_NUM];
        struct completion       frame_done;
 
        int             fir_hinc[OMAPFB_PLANE_NUM];
@@ -286,7 +290,7 @@ static void setup_plane_fifo(int plane, int ext_mode)
        BUG_ON(plane > 2);
 
        l = dispc_read_reg(fsz_reg[plane]);
        BUG_ON(plane > 2);
 
        l = dispc_read_reg(fsz_reg[plane]);
-       l &= FLD_MASK(0, 9);
+       l &= FLD_MASK(0, 11);
        if (ext_mode) {
                low = l * 3 / 4;
                high = l;
        if (ext_mode) {
                low = l * 3 / 4;
                high = l;
@@ -294,7 +298,7 @@ static void setup_plane_fifo(int plane, int ext_mode)
                low = l / 4;
                high = l * 3 / 4;
        }
                low = l / 4;
                high = l * 3 / 4;
        }
-       MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 9) | FLD_MASK(0, 9),
+       MOD_REG_FLD(ftrs_reg[plane], FLD_MASK(16, 12) | FLD_MASK(0, 12),
                        (high << 16) | low);
 }
 
                        (high << 16) | low);
 }
 
@@ -519,8 +523,7 @@ static int omap_dispc_set_scale(int plane,
        if ((unsigned)plane > OMAPFB_PLANE_NUM)
                return -ENODEV;
 
        if ((unsigned)plane > OMAPFB_PLANE_NUM)
                return -ENODEV;
 
-       if (plane == OMAPFB_PLANE_GFX &&
-           (out_width != orig_width || out_height != orig_height))
+       if (out_width != orig_width || out_height != orig_height)
                return -EINVAL;
 
        enable_lcd_clocks(1);
                return -EINVAL;
 
        enable_lcd_clocks(1);
@@ -809,57 +812,74 @@ static void set_lcd_timings(void)
        panel->pixel_clock = fck / lck_div / pck_div / 1000;
 }
 
        panel->pixel_clock = fck / lck_div / pck_div / 1000;
 }
 
-int omap_dispc_request_irq(void (*callback)(void *data), void *data)
+static void recalc_irq_mask(void)
 {
 {
-       int r = 0;
+       int i;
+       unsigned long irq_mask = DISPC_IRQ_MASK_ERROR;
 
 
-       BUG_ON(callback == NULL);
+       for (i = 0; i < MAX_IRQ_HANDLERS; i++) {
+               if (!dispc.irq_handlers[i].callback)
+                       continue;
 
 
-       if (dispc.irq_callback)
-               r = -EBUSY;
-       else {
-               dispc.irq_callback = callback;
-               dispc.irq_callback_data = data;
+               irq_mask |= dispc.irq_handlers[i].irq_mask;
        }
 
        }
 
-       return r;
-}
-EXPORT_SYMBOL(omap_dispc_request_irq);
-
-void omap_dispc_enable_irqs(int irq_mask)
-{
        enable_lcd_clocks(1);
        enable_lcd_clocks(1);
-       dispc.enabled_irqs = irq_mask;
-       irq_mask |= DISPC_IRQ_MASK_ERROR;
        MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask);
        enable_lcd_clocks(0);
 }
        MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask);
        enable_lcd_clocks(0);
 }
-EXPORT_SYMBOL(omap_dispc_enable_irqs);
 
 
-void omap_dispc_disable_irqs(int irq_mask)
+int omap_dispc_request_irq(unsigned long irq_mask, void (*callback)(void *data),
+                          void *data)
 {
 {
-       enable_lcd_clocks(1);
-       dispc.enabled_irqs &= ~irq_mask;
-       irq_mask &= ~DISPC_IRQ_MASK_ERROR;
-       MOD_REG_FLD(DISPC_IRQENABLE, 0x7fff, irq_mask);
-       enable_lcd_clocks(0);
+       int i;
+
+       BUG_ON(callback == NULL);
+
+       for (i = 0; i < MAX_IRQ_HANDLERS; i++) {
+               if (dispc.irq_handlers[i].callback)
+                       continue;
+
+               dispc.irq_handlers[i].irq_mask = irq_mask;
+               dispc.irq_handlers[i].callback = callback;
+               dispc.irq_handlers[i].data = data;
+               recalc_irq_mask();
+
+               return 0;
+       }
+
+       return -EBUSY;
 }
 }
-EXPORT_SYMBOL(omap_dispc_disable_irqs);
+EXPORT_SYMBOL(omap_dispc_request_irq);
 
 
-void omap_dispc_free_irq(void)
+void omap_dispc_free_irq(unsigned long irq_mask, void (*callback)(void *data),
+                        void *data)
 {
 {
-       enable_lcd_clocks(1);
-       omap_dispc_disable_irqs(DISPC_IRQ_MASK_ALL);
-       dispc.irq_callback = NULL;
-       dispc.irq_callback_data = NULL;
-       enable_lcd_clocks(0);
+       int i;
+
+       for (i = 0; i < MAX_IRQ_HANDLERS; i++) {
+               if (dispc.irq_handlers[i].callback == callback &&
+                   dispc.irq_handlers[i].data == data) {
+                       dispc.irq_handlers[i].irq_mask = 0;
+                       dispc.irq_handlers[i].callback = NULL;
+                       dispc.irq_handlers[i].data = NULL;
+                       recalc_irq_mask();
+                       return;
+               }
+       }
+
+       BUG();
 }
 EXPORT_SYMBOL(omap_dispc_free_irq);
 
 static irqreturn_t omap_dispc_irq_handler(int irq, void *dev)
 {
 }
 EXPORT_SYMBOL(omap_dispc_free_irq);
 
 static irqreturn_t omap_dispc_irq_handler(int irq, void *dev)
 {
-       u32 stat = dispc_read_reg(DISPC_IRQSTATUS);
+       u32 stat;
+       int i = 0;
 
 
+       enable_lcd_clocks(1);
+
+       stat = dispc_read_reg(DISPC_IRQSTATUS);
        if (stat & DISPC_IRQ_FRAMEMASK)
                complete(&dispc.frame_done);
 
        if (stat & DISPC_IRQ_FRAMEMASK)
                complete(&dispc.frame_done);
 
@@ -870,30 +890,39 @@ static irqreturn_t omap_dispc_irq_handler(int irq, void *dev)
                }
        }
 
                }
        }
 
-       if ((stat & dispc.enabled_irqs) && dispc.irq_callback)
-               dispc.irq_callback(dispc.irq_callback_data);
+       for (i = 0; i < MAX_IRQ_HANDLERS; i++) {
+               if (unlikely(dispc.irq_handlers[i].callback &&
+                            (stat & dispc.irq_handlers[i].irq_mask)))
+               dispc.irq_handlers[i].callback(dispc.irq_handlers[i].data);
+       }
 
        dispc_write_reg(DISPC_IRQSTATUS, stat);
 
 
        dispc_write_reg(DISPC_IRQSTATUS, stat);
 
+       enable_lcd_clocks(0);
+
        return IRQ_HANDLED;
 }
 
 static int get_dss_clocks(void)
 {
        return IRQ_HANDLED;
 }
 
 static int get_dss_clocks(void)
 {
-       if (IS_ERR((dispc.dss_ick = clk_get(dispc.fbdev->dev, "dss_ick")))) {
-               dev_err(dispc.fbdev->dev, "can't get dss_ick\n");
+       char *dss_ick = "dss_ick";
+       char *dss1_fck = cpu_is_omap34xx() ? "dss1_alwon_fck" : "dss1_fck";
+       char *tv_fck = cpu_is_omap34xx() ? "dss_tv_fck" : "dss_54m_fck";
+
+       if (IS_ERR((dispc.dss_ick = clk_get(dispc.fbdev->dev, dss_ick)))) {
+               dev_err(dispc.fbdev->dev, "can't get %s", dss_ick);
                return PTR_ERR(dispc.dss_ick);
        }
 
                return PTR_ERR(dispc.dss_ick);
        }
 
-       if (IS_ERR((dispc.dss1_fck = clk_get(dispc.fbdev->dev, "dss1_fck")))) {
-               dev_err(dispc.fbdev->dev, "can't get dss1_fck\n");
+       if (IS_ERR((dispc.dss1_fck = clk_get(dispc.fbdev->dev, dss1_fck)))) {
+               dev_err(dispc.fbdev->dev, "can't get %s", dss1_fck);
                clk_put(dispc.dss_ick);
                return PTR_ERR(dispc.dss1_fck);
        }
 
        if (IS_ERR((dispc.dss_54m_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")))) {
-               dev_err(dispc.fbdev->dev, "can't get dss_54m_fck\n");
+                               clk_get(dispc.fbdev->dev, tv_fck)))) {
+               dev_err(dispc.fbdev->dev, "can't get %s", tv_fck);
                clk_put(dispc.dss_ick);
                clk_put(dispc.dss1_fck);
                return PTR_ERR(dispc.dss_54m_fck);
                clk_put(dispc.dss_ick);
                clk_put(dispc.dss1_fck);
                return PTR_ERR(dispc.dss_54m_fck);
@@ -911,18 +940,13 @@ static void put_dss_clocks(void)
 
 static void enable_lcd_clocks(int enable)
 {
 
 static void enable_lcd_clocks(int enable)
 {
-       if (enable)
+       if (enable) {
+               clk_enable(dispc.dss_ick);
                clk_enable(dispc.dss1_fck);
                clk_enable(dispc.dss1_fck);
-       else
+       } else {
                clk_disable(dispc.dss1_fck);
                clk_disable(dispc.dss1_fck);
-}
-
-static void enable_interface_clocks(int enable)
-{
-       if (enable)
-               clk_enable(dispc.dss_ick);
-       else
                clk_disable(dispc.dss_ick);
                clk_disable(dispc.dss_ick);
+       }
 }
 
 static void enable_digit_clocks(int enable)
 }
 
 static void enable_digit_clocks(int enable)
@@ -1363,7 +1387,6 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
        if ((r = get_dss_clocks()) < 0)
                goto fail0;
 
        if ((r = get_dss_clocks()) < 0)
                goto fail0;
 
-       enable_interface_clocks(1);
        enable_lcd_clocks(1);
 
 #ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
        enable_lcd_clocks(1);
 
 #ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
@@ -1394,10 +1417,10 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
                enable_digit_clocks(0);
        }
 
                enable_digit_clocks(0);
        }
 
-       /* Enable smart idle and autoidle */
-       l = dispc_read_reg(DISPC_CONTROL);
+       /* Enable smart standby/idle, autoidle and wakeup */
+       l = dispc_read_reg(DISPC_SYSCONFIG);
        l &= ~((3 << 12) | (3 << 3));
        l &= ~((3 << 12) | (3 << 3));
-       l |= (2 << 12) | (2 << 3) | (1 << 0);
+       l |= (2 << 12) | (2 << 3) | (1 << 2) | (1 << 0);
        dispc_write_reg(DISPC_SYSCONFIG, l);
        omap_writel(1 << 0, DSS_BASE + DSS_SYSCONFIG);
 
        dispc_write_reg(DISPC_SYSCONFIG, l);
        omap_writel(1 << 0, DSS_BASE + DSS_SYSCONFIG);
 
@@ -1407,10 +1430,9 @@ static int omap_dispc_init(struct omapfb_device *fbdev, int ext_mode,
        dispc_write_reg(DISPC_CONFIG, l);
 
        l = dispc_read_reg(DISPC_IRQSTATUS);
        dispc_write_reg(DISPC_CONFIG, l);
 
        l = dispc_read_reg(DISPC_IRQSTATUS);
-       dispc_write_reg(l, DISPC_IRQSTATUS);
+       dispc_write_reg(DISPC_IRQSTATUS, l);
 
 
-       /* Enable those that we handle always */
-       omap_dispc_enable_irqs(DISPC_IRQ_FRAMEMASK);
+       recalc_irq_mask();
 
        if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler,
                           0, MODULE_NAME, fbdev)) < 0) {
 
        if ((r = request_irq(INT_24XX_DSS_IRQ, omap_dispc_irq_handler,
                           0, MODULE_NAME, fbdev)) < 0) {
@@ -1467,7 +1489,6 @@ fail2:
        free_irq(INT_24XX_DSS_IRQ, fbdev);
 fail1:
        enable_lcd_clocks(0);
        free_irq(INT_24XX_DSS_IRQ, fbdev);
 fail1:
        enable_lcd_clocks(0);
-       enable_interface_clocks(0);
        put_dss_clocks();
 fail0:
        iounmap(dispc.base);
        put_dss_clocks();
 fail0:
        iounmap(dispc.base);
@@ -1485,7 +1506,6 @@ static void omap_dispc_cleanup(void)
        cleanup_fbmem();
        free_palette_ram();
        free_irq(INT_24XX_DSS_IRQ, dispc.fbdev);
        cleanup_fbmem();
        free_palette_ram();
        free_irq(INT_24XX_DSS_IRQ, dispc.fbdev);
-       enable_interface_clocks(0);
        put_dss_clocks();
        iounmap(dispc.base);
 }
        put_dss_clocks();
        iounmap(dispc.base);
 }
index ef720a78f6d513155d38ae1cba4061c966d0d1fa..0a370cf07f71723dbb8d34865e20b8cb13b41cef 100644 (file)
@@ -37,8 +37,10 @@ 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 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);
+extern int omap_dispc_request_irq(unsigned long irq_mask,
+                                  void (*callback)(void *data), void *data);
+extern void omap_dispc_free_irq(unsigned long irq_mask,
+                                void (*callback)(void *data), void *data);
 
 extern const struct lcd_ctrl omap2_int_ctrl;
 
 
 extern const struct lcd_ctrl omap2_int_ctrl;
 
diff --git a/drivers/video/omap/lcd_2430sdp.c b/drivers/video/omap/lcd_2430sdp.c
new file mode 100644 (file)
index 0000000..a22b452
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * LCD panel support for the TI 2430SDP board
+ *
+ * Copyright (C) 2007 MontaVista
+ * Author: Hunyue Yau <hyau@mvista.com>
+ *
+ * Derived from drivers/video/omap/lcd-apollon.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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c/twl4030.h>
+
+#include <mach/mux.h>
+#include <mach/omapfb.h>
+#include <asm/mach-types.h>
+
+#define SDP2430_LCD_PANEL_BACKLIGHT_GPIO       91
+#define SDP2430_LCD_PANEL_ENABLE_GPIO          154
+#define SDP3430_LCD_PANEL_BACKLIGHT_GPIO       24
+#define SDP3430_LCD_PANEL_ENABLE_GPIO          28
+
+static unsigned backlight_gpio;
+static unsigned enable_gpio;
+
+#define LCD_PIXCLOCK_MAX               5400 /* freq 5.4 MHz */
+#define PM_RECEIVER             TWL4030_MODULE_PM_RECEIVER
+#define ENABLE_VAUX2_DEDICATED  0x09
+#define ENABLE_VAUX2_DEV_GRP    0x20
+#define ENABLE_VAUX3_DEDICATED 0x03
+#define ENABLE_VAUX3_DEV_GRP   0x20
+
+#define ENABLE_VPLL2_DEDICATED          0x05
+#define ENABLE_VPLL2_DEV_GRP            0xE0
+#define TWL4030_VPLL2_DEV_GRP           0x33
+#define TWL4030_VPLL2_DEDICATED         0x36
+
+#define t2_out(c, r, v) twl4030_i2c_write_u8(c, r, v)
+
+
+static int sdp2430_panel_init(struct lcd_panel *panel,
+                               struct omapfb_device *fbdev)
+{
+       if (machine_is_omap_3430sdp()) {
+               enable_gpio    = SDP3430_LCD_PANEL_ENABLE_GPIO;
+               backlight_gpio = SDP3430_LCD_PANEL_BACKLIGHT_GPIO;
+       } else {
+               enable_gpio    = SDP2430_LCD_PANEL_ENABLE_GPIO;
+               backlight_gpio = SDP2430_LCD_PANEL_BACKLIGHT_GPIO;
+       }
+
+       gpio_request(enable_gpio, "LCD enable");        /* LCD panel */
+       gpio_request(backlight_gpio, "LCD bl");         /* LCD backlight */
+       gpio_direction_output(enable_gpio, 0);
+       gpio_direction_output(backlight_gpio, 0);
+
+       return 0;
+}
+
+static void sdp2430_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int sdp2430_panel_enable(struct lcd_panel *panel)
+{
+       u8 ded_val, ded_reg;
+       u8 grp_val, grp_reg;
+
+       if (machine_is_omap_3430sdp()) {
+               ded_reg = TWL4030_VAUX3_DEDICATED;
+               ded_val = ENABLE_VAUX3_DEDICATED;
+               grp_reg = TWL4030_VAUX3_DEV_GRP;
+               grp_val = ENABLE_VAUX3_DEV_GRP;
+
+               if (omap_rev() > OMAP3430_REV_ES1_0) {
+                       t2_out(PM_RECEIVER, ENABLE_VPLL2_DEDICATED,
+                                       TWL4030_VPLL2_DEDICATED);
+                       t2_out(PM_RECEIVER, ENABLE_VPLL2_DEV_GRP,
+                                       TWL4030_VPLL2_DEV_GRP);
+               }
+       } else {
+               ded_reg = TWL4030_VAUX2_DEDICATED;
+               ded_val = ENABLE_VAUX2_DEDICATED;
+               grp_reg = TWL4030_VAUX2_DEV_GRP;
+               grp_val = ENABLE_VAUX2_DEV_GRP;
+       }
+               
+       gpio_set_value(enable_gpio, 1);
+       gpio_set_value(backlight_gpio, 1);
+
+       if (0 != t2_out(PM_RECEIVER, ded_val, ded_reg))
+               return -EIO;
+       if (0 != t2_out(PM_RECEIVER, grp_val, grp_reg))
+               return -EIO;
+
+       return 0;
+}
+
+static void sdp2430_panel_disable(struct lcd_panel *panel)
+{
+       gpio_set_value(enable_gpio, 0);
+       gpio_set_value(backlight_gpio, 0);
+       if (omap_rev() > OMAP3430_REV_ES1_0) {
+               t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEDICATED);
+               t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEV_GRP);
+               mdelay(4);
+       }
+}
+
+static unsigned long sdp2430_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel sdp2430_panel = {
+       .name           = "sdp2430",
+       .config         = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+                         OMAP_LCDC_INV_HSYNC,
+
+       .bpp            = 16,
+       .data_lines     = 16,
+       .x_res          = 240,
+       .y_res          = 320,
+       .hsw            = 3,            /* hsync_len (4) - 1 */
+       .hfp            = 3,            /* right_margin (4) - 1 */
+       .hbp            = 39,           /* left_margin (40) - 1 */
+       .vsw            = 1,            /* vsync_len (2) - 1 */
+       .vfp            = 2,            /* lower_margin */
+       .vbp            = 7,            /* upper_margin (8) - 1 */
+
+       .pixel_clock    = LCD_PIXCLOCK_MAX,
+
+       .init           = sdp2430_panel_init,
+       .cleanup        = sdp2430_panel_cleanup,
+       .enable         = sdp2430_panel_enable,
+       .disable        = sdp2430_panel_disable,
+       .get_caps       = sdp2430_panel_get_caps,
+};
+
+static int sdp2430_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&sdp2430_panel);
+       return 0;
+}
+
+static int sdp2430_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int sdp2430_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int sdp2430_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver sdp2430_panel_driver = {
+       .probe          = sdp2430_panel_probe,
+       .remove         = sdp2430_panel_remove,
+       .suspend        = sdp2430_panel_suspend,
+       .resume         = sdp2430_panel_resume,
+       .driver         = {
+               .name   = "sdp2430_lcd",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init sdp2430_panel_drv_init(void)
+{
+       return platform_driver_register(&sdp2430_panel_driver);
+}
+
+static void __exit sdp2430_panel_drv_exit(void)
+{
+       platform_driver_unregister(&sdp2430_panel_driver);
+}
+
+module_init(sdp2430_panel_drv_init);
+module_exit(sdp2430_panel_drv_exit);
diff --git a/drivers/video/omap/lcd_ams_delta.c b/drivers/video/omap/lcd_ams_delta.c
new file mode 100644 (file)
index 0000000..3fd5342
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * File: drivers/video/omap/lcd_ams_delta.c
+ *
+ * Based on drivers/video/omap/lcd_inn1510.c
+ *
+ * LCD panel support for the Amstrad E3 (Delta) videophone.
+ *
+ * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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 <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+
+#include <mach/board-ams-delta.h>
+#include <mach/hardware.h>
+#include <mach/omapfb.h>
+
+#define AMS_DELTA_DEFAULT_CONTRAST     112
+
+static int ams_delta_panel_init(struct lcd_panel *panel,
+               struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void ams_delta_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int ams_delta_panel_enable(struct lcd_panel *panel)
+{
+       ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP,
+                       AMS_DELTA_LATCH2_LCD_NDISP);
+       ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN,
+                       AMS_DELTA_LATCH2_LCD_VBLEN);
+
+       omap_writeb(1, OMAP_PWL_CLK_ENABLE);
+       omap_writeb(AMS_DELTA_DEFAULT_CONTRAST, OMAP_PWL_ENABLE);
+
+       return 0;
+}
+
+static void ams_delta_panel_disable(struct lcd_panel *panel)
+{
+       ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_VBLEN, 0);
+       ams_delta_latch2_write(AMS_DELTA_LATCH2_LCD_NDISP, 0);
+}
+
+static unsigned long ams_delta_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+static struct lcd_panel ams_delta_panel = {
+       .name           = "ams-delta",
+       .config         = 0,
+
+       .bpp            = 12,
+       .data_lines     = 16,
+       .x_res          = 480,
+       .y_res          = 320,
+       .pixel_clock    = 4687,
+       .hsw            = 3,
+       .hfp            = 1,
+       .hbp            = 1,
+       .vsw            = 1,
+       .vfp            = 0,
+       .vbp            = 0,
+       .pcd            = 0,
+       .acb            = 37,
+
+       .init           = ams_delta_panel_init,
+       .cleanup        = ams_delta_panel_cleanup,
+       .enable         = ams_delta_panel_enable,
+       .disable        = ams_delta_panel_disable,
+       .get_caps       = ams_delta_panel_get_caps,
+};
+
+static int ams_delta_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&ams_delta_panel);
+       return 0;
+}
+
+static int ams_delta_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int ams_delta_panel_suspend(struct platform_device *pdev,
+               pm_message_t mesg)
+{
+       return 0;
+}
+
+static int ams_delta_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver ams_delta_panel_driver = {
+       .probe          = ams_delta_panel_probe,
+       .remove         = ams_delta_panel_remove,
+       .suspend        = ams_delta_panel_suspend,
+       .resume         = ams_delta_panel_resume,
+       .driver         = {
+               .name   = "lcd_ams_delta",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int ams_delta_panel_drv_init(void)
+{
+       return platform_driver_register(&ams_delta_panel_driver);
+}
+
+static void ams_delta_panel_drv_cleanup(void)
+{
+       platform_driver_unregister(&ams_delta_panel_driver);
+}
+
+module_init(ams_delta_panel_drv_init);
+module_exit(ams_delta_panel_drv_cleanup);
diff --git a/drivers/video/omap/lcd_apollon.c b/drivers/video/omap/lcd_apollon.c
new file mode 100644 (file)
index 0000000..beae5d9
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * LCD panel support for the Samsung OMAP2 Apollon board
+ *
+ * Copyright (C) 2005,2006 Samsung Electronics
+ * Author: Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * 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 <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <mach/gpio.h>
+#include <mach/mux.h>
+#include <mach/omapfb.h>
+
+/* #define USE_35INCH_LCD 1 */
+
+static int apollon_panel_init(struct lcd_panel *panel,
+                               struct omapfb_device *fbdev)
+{
+       /* configure LCD PWR_EN */
+       omap_cfg_reg(M21_242X_GPIO11);
+       return 0;
+}
+
+static void apollon_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int apollon_panel_enable(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+static void apollon_panel_disable(struct lcd_panel *panel)
+{
+}
+
+static unsigned long apollon_panel_get_caps(struct lcd_panel *panel)
+{
+       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)
+{
+       omapfb_register_panel(&apollon_panel);
+       return 0;
+}
+
+static int apollon_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int apollon_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int apollon_panel_resume(struct platform_device *pdev)
+{
+       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_ldp.c b/drivers/video/omap/lcd_ldp.c
new file mode 100644 (file)
index 0000000..8925230
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * LCD panel support for the TI LDP board
+ *
+ * Copyright (C) 2007 WindRiver
+ * Author: Stanley Miao <stanley.miao@windriver.com>
+ *
+ * Derived from drivers/video/omap/lcd-2430sdp.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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/i2c/twl4030.h>
+
+#include <mach/gpio.h>
+#include <mach/mux.h>
+#include <mach/omapfb.h>
+#include <asm/mach-types.h>
+
+#define LCD_PANEL_BACKLIGHT_GPIO       (15 + OMAP_MAX_GPIO_LINES)
+#define LCD_PANEL_ENABLE_GPIO          (7 + OMAP_MAX_GPIO_LINES)
+
+#define LCD_PANEL_RESET_GPIO           55
+#define LCD_PANEL_QVGA_GPIO            56
+
+#ifdef CONFIG_FB_OMAP_LCD_VGA
+#define LCD_XRES               480
+#define LCD_YRES               640
+#define LCD_PIXCLOCK_MAX       41700
+#else
+#define LCD_XRES               240
+#define LCD_YRES               320
+#define LCD_PIXCLOCK_MAX       185186
+#endif
+
+#define PM_RECEIVER             TWL4030_MODULE_PM_RECEIVER
+#define ENABLE_VAUX2_DEDICATED  0x09
+#define ENABLE_VAUX2_DEV_GRP    0x20
+#define ENABLE_VAUX3_DEDICATED 0x03
+#define ENABLE_VAUX3_DEV_GRP   0x20
+
+#define ENABLE_VPLL2_DEDICATED          0x05
+#define ENABLE_VPLL2_DEV_GRP            0xE0
+#define TWL4030_VPLL2_DEV_GRP           0x33
+#define TWL4030_VPLL2_DEDICATED         0x36
+
+#define t2_out(c, r, v) twl4030_i2c_write_u8(c, r, v)
+
+
+static int ldp_panel_init(struct lcd_panel *panel,
+                               struct omapfb_device *fbdev)
+{
+       gpio_request(LCD_PANEL_RESET_GPIO, "lcd reset");
+       gpio_request(LCD_PANEL_QVGA_GPIO, "lcd qvga");
+       gpio_request(LCD_PANEL_ENABLE_GPIO, "lcd panel");
+       gpio_request(LCD_PANEL_BACKLIGHT_GPIO, "lcd backlight");
+
+       gpio_direction_output(LCD_PANEL_QVGA_GPIO, 0);
+       gpio_direction_output(LCD_PANEL_RESET_GPIO, 0);
+       gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 0);
+       gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 0);
+
+#ifdef CONFIG_FB_OMAP_LCD_VGA
+       gpio_set_value(LCD_PANEL_QVGA_GPIO, 0);
+#else
+       gpio_set_value(LCD_PANEL_QVGA_GPIO, 1);
+#endif
+       gpio_set_value(LCD_PANEL_RESET_GPIO, 1);
+
+       return 0;
+}
+
+static void ldp_panel_cleanup(struct lcd_panel *panel)
+{
+       gpio_free(LCD_PANEL_RESET_GPIO);
+       gpio_free(LCD_PANEL_QVGA_GPIO);
+       gpio_free(LCD_PANEL_ENABLE_GPIO);
+       gpio_free(LCD_PANEL_BACKLIGHT_GPIO);
+}
+
+static int ldp_panel_enable(struct lcd_panel *panel)
+{
+       if (0 != t2_out(PM_RECEIVER, ENABLE_VPLL2_DEDICATED,
+                       TWL4030_VPLL2_DEDICATED))
+               return -EIO;
+       if (0 != t2_out(PM_RECEIVER, ENABLE_VPLL2_DEV_GRP,
+                       TWL4030_VPLL2_DEV_GRP))
+               return -EIO;
+
+       gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 1);
+       gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 1);
+
+       if (0 != t2_out(PM_RECEIVER, ENABLE_VAUX3_DEDICATED,
+                               TWL4030_VAUX3_DEDICATED))
+               return -EIO;
+       if (0 != t2_out(PM_RECEIVER, ENABLE_VAUX3_DEV_GRP,
+                               TWL4030_VAUX3_DEV_GRP))
+               return -EIO;
+
+       return 0;
+}
+
+static void ldp_panel_disable(struct lcd_panel *panel)
+{
+       gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 0);
+       gpio_direction_output(LCD_PANEL_BACKLIGHT_GPIO, 0);
+
+       t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEDICATED);
+       t2_out(PM_RECEIVER, 0x0, TWL4030_VPLL2_DEV_GRP);
+       mdelay(4);
+}
+
+static unsigned long ldp_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel ldp_panel = {
+       .name           = "ldp",
+       .config         = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+                         OMAP_LCDC_INV_HSYNC,
+
+       .bpp            = 16,
+       .data_lines     = 18,
+       .x_res          = LCD_XRES,
+       .y_res          = LCD_YRES,
+       .hsw            = 3,            /* hsync_len (4) - 1 */
+       .hfp            = 3,            /* right_margin (4) - 1 */
+       .hbp            = 39,           /* left_margin (40) - 1 */
+       .vsw            = 1,            /* vsync_len (2) - 1 */
+       .vfp            = 2,            /* lower_margin */
+       .vbp            = 7,            /* upper_margin (8) - 1 */
+
+       .pixel_clock    = LCD_PIXCLOCK_MAX,
+
+       .init           = ldp_panel_init,
+       .cleanup        = ldp_panel_cleanup,
+       .enable         = ldp_panel_enable,
+       .disable        = ldp_panel_disable,
+       .get_caps       = ldp_panel_get_caps,
+};
+
+static int ldp_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&ldp_panel);
+       return 0;
+}
+
+static int ldp_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int ldp_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int ldp_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver ldp_panel_driver = {
+       .probe          = ldp_panel_probe,
+       .remove         = ldp_panel_remove,
+       .suspend        = ldp_panel_suspend,
+       .resume         = ldp_panel_resume,
+       .driver         = {
+               .name   = "ldp_lcd",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init ldp_panel_drv_init(void)
+{
+       return platform_driver_register(&ldp_panel_driver);
+}
+
+static void __exit ldp_panel_drv_exit(void)
+{
+       platform_driver_unregister(&ldp_panel_driver);
+}
+
+module_init(ldp_panel_drv_init);
+module_exit(ldp_panel_drv_exit);
diff --git a/drivers/video/omap/lcd_mipid.c b/drivers/video/omap/lcd_mipid.c
new file mode 100644 (file)
index 0000000..1895997
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * LCD driver for MIPI DBI-C / DCS compatible LCDs
+ *
+ * Copyright (C) 2006 Nokia Corporation
+ * Author: Imre Deak <imre.deak@nokia.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.
+ */
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+
+#include <mach/omapfb.h>
+#include <mach/lcd_mipid.h>
+
+#include "../../cbus/tahvo.h"
+
+#define MIPID_MODULE_NAME              "lcd_mipid"
+
+#define MIPID_CMD_READ_DISP_ID         0x04
+#define MIPID_CMD_READ_RED             0x06
+#define MIPID_CMD_READ_GREEN           0x07
+#define MIPID_CMD_READ_BLUE            0x08
+#define MIPID_CMD_READ_DISP_STATUS     0x09
+#define MIPID_CMD_RDDSDR               0x0F
+#define MIPID_CMD_SLEEP_IN             0x10
+#define MIPID_CMD_SLEEP_OUT            0x11
+#define MIPID_CMD_DISP_OFF             0x28
+#define MIPID_CMD_DISP_ON              0x29
+
+#define MIPID_VER_LPH8923              3
+#define MIPID_VER_LS041Y3              4
+
+#define MIPID_ESD_CHECK_PERIOD         msecs_to_jiffies(5000)
+
+#define to_mipid_device(p)             container_of(p, struct mipid_device, \
+                                               panel)
+struct mipid_device {
+       int             enabled;
+       int             model;
+       int             revision;
+       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;
+       struct mutex            mutex;
+       struct lcd_panel        panel;
+
+       struct workqueue_struct *esd_wq;
+       struct delayed_work     esd_work;
+       void                    (*esd_check)(struct mipid_device *m);
+};
+
+static void mipid_transfer(struct mipid_device *md, int cmd, const u8 *wbuf,
+                          int wlen, u8 *rbuf, int rlen)
+{
+       struct spi_message      m;
+       struct spi_transfer     *x, xfer[4];
+       u16                     w;
+       int                     r;
+
+       BUG_ON(md->spi == NULL);
+
+       spi_message_init(&m);
+
+       memset(xfer, 0, sizeof(xfer));
+       x = &xfer[0];
+
+       cmd &=  0xff;
+       x->tx_buf       = &cmd;
+       x->bits_per_word= 9;
+       x->len          = 2;
+       spi_message_add_tail(x, &m);
+
+       if (wlen) {
+               x++;
+               x->tx_buf       = wbuf;
+               x->len          = wlen;
+               x->bits_per_word= 9;
+               spi_message_add_tail(x, &m);
+       }
+
+       if (rlen) {
+               x++;
+               x->rx_buf       = &w;
+               x->len          = 1;
+               spi_message_add_tail(x, &m);
+
+               if (rlen > 1) {
+                       /* Arrange for the extra clock before the first
+                        * data bit.
+                        */
+                       x->bits_per_word = 9;
+                       x->len           = 2;
+
+                       x++;
+                       x->rx_buf        = &rbuf[1];
+                       x->len           = rlen - 1;
+                       spi_message_add_tail(x, &m);
+               }
+       }
+
+       r = spi_sync(md->spi, &m);
+       if (r < 0)
+               dev_dbg(&md->spi->dev, "spi_sync %d\n", r);
+
+       if (rlen)
+               rbuf[0] = w & 0xff;
+}
+
+static inline void mipid_cmd(struct mipid_device *md, int cmd)
+{
+       mipid_transfer(md, cmd, NULL, 0, NULL, 0);
+}
+
+static inline void mipid_write(struct mipid_device *md,
+                              int reg, const u8 *buf, int len)
+{
+       mipid_transfer(md, reg, buf, len, NULL, 0);
+}
+
+static inline void mipid_read(struct mipid_device *md,
+                             int reg, u8 *buf, int len)
+{
+       mipid_transfer(md, reg, NULL, 0, buf, len);
+}
+
+static void set_data_lines(struct mipid_device *md, int data_lines)
+{
+       u16 par;
+
+       switch (data_lines) {
+       case 16:
+               par = 0x150;
+               break;
+       case 18:
+               par = 0x160;
+               break;
+       case 24:
+               par = 0x170;
+               break;
+       }
+       mipid_write(md, 0x3a, (u8 *)&par, 2);
+}
+
+static void send_init_string(struct mipid_device *md)
+{
+       u16 initpar[] = { 0x0102, 0x0100, 0x0100 };
+
+       mipid_write(md, 0xc2, (u8 *)initpar, sizeof(initpar));
+       set_data_lines(md, md->panel.data_lines);
+}
+
+static void hw_guard_start(struct mipid_device *md, int guard_msec)
+{
+       md->hw_guard_wait = msecs_to_jiffies(guard_msec);
+       md->hw_guard_end = jiffies + md->hw_guard_wait;
+}
+
+static void hw_guard_wait(struct mipid_device *md)
+{
+       unsigned long wait = md->hw_guard_end - jiffies;
+
+       if ((long)wait > 0 && wait <= md->hw_guard_wait) {
+               set_current_state(TASK_UNINTERRUPTIBLE);
+               schedule_timeout(wait);
+       }
+}
+
+static void set_sleep_mode(struct mipid_device *md, int on)
+{
+       int cmd, sleep_time = 50;
+
+       if (on)
+               cmd = MIPID_CMD_SLEEP_IN;
+       else
+               cmd = MIPID_CMD_SLEEP_OUT;
+       hw_guard_wait(md);
+       mipid_cmd(md, cmd);
+       hw_guard_start(md, 120);
+       /*
+        * When we enable the panel, it seems we _have_ to sleep
+        * 120 ms before sending the init string. When disabling the
+        * panel we'll sleep for the duration of 2 frames, so that the
+        * controller can still provide the PCLK,HS,VS signals. */
+       if (!on)
+               sleep_time = 120;
+       msleep(sleep_time);
+}
+
+static void set_display_state(struct mipid_device *md, int enabled)
+{
+       int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF;
+
+       mipid_cmd(md, cmd);
+}
+
+static int mipid_set_bklight_level(struct lcd_panel *panel, unsigned int level)
+{
+       struct mipid_device *md = to_mipid_device(panel);
+
+       if (level > tahvo_get_max_backlight_level())
+               return -EINVAL;
+       if (!md->enabled) {
+               md->saved_bklight_level = level;
+               return 0;
+       }
+       tahvo_set_backlight_level(level);
+
+       return 0;
+}
+
+static unsigned int mipid_get_bklight_level(struct lcd_panel *panel)
+{
+       return tahvo_get_backlight_level();
+}
+
+static unsigned int mipid_get_bklight_max(struct lcd_panel *panel)
+{
+       return tahvo_get_max_backlight_level();
+}
+
+
+static unsigned long mipid_get_caps(struct lcd_panel *panel)
+{
+       return OMAPFB_CAPS_SET_BACKLIGHT;
+}
+
+static u16 read_first_pixel(struct mipid_device *md)
+{
+       u16 pixel;
+       u8 red, green, blue;
+
+       mutex_lock(&md->mutex);
+       mipid_read(md, MIPID_CMD_READ_RED, &red, 1);
+       mipid_read(md, MIPID_CMD_READ_GREEN, &green, 1);
+       mipid_read(md, MIPID_CMD_READ_BLUE, &blue, 1);
+       mutex_unlock(&md->mutex);
+
+       switch (md->panel.data_lines) {
+       case 16:
+               pixel = ((red >> 1) << 11) | (green << 5) | (blue >> 1);
+               break;
+       case 24:
+               /* 24 bit -> 16 bit */
+               pixel = ((red >> 3) << 11) | ((green >> 2) << 5) |
+                       (blue >> 3);
+               break;
+       default:
+               BUG();
+       }
+
+       return pixel;
+}
+
+static int mipid_run_test(struct lcd_panel *panel, int test_num)
+{
+       struct mipid_device *md = to_mipid_device(panel);
+       static const u16 test_values[4] = {
+               0x0000, 0xffff, 0xaaaa, 0x5555,
+       };
+       int i;
+
+       if (test_num != MIPID_TEST_RGB_LINES)
+               return MIPID_TEST_INVALID;
+
+       for (i = 0; i < ARRAY_SIZE(test_values); i++) {
+               int delay;
+               unsigned long tmo;
+
+               omapfb_write_first_pixel(md->fbdev, test_values[i]);
+               tmo = jiffies + msecs_to_jiffies(100);
+               delay = 25;
+               while (1) {
+                       u16 pixel;
+
+                       msleep(delay);
+                       pixel = read_first_pixel(md);
+                       if (pixel == test_values[i])
+                               break;
+                       if (time_after(jiffies, tmo)) {
+                               dev_err(&md->spi->dev,
+                                       "MIPI LCD RGB I/F test failed: "
+                                       "expecting %04x, got %04x\n",
+                                       test_values[i], pixel);
+                               return MIPID_TEST_FAILED;
+                       }
+                       delay = 10;
+               }
+       }
+
+       return 0;
+}
+
+static void ls041y3_esd_recover(struct mipid_device *md)
+{
+       dev_err(&md->spi->dev, "performing LCD ESD recovery\n");
+       set_sleep_mode(md, 1);
+       set_sleep_mode(md, 0);
+}
+
+static void ls041y3_esd_check_mode1(struct mipid_device *md)
+{
+       u8 state1, state2;
+
+       mipid_read(md, MIPID_CMD_RDDSDR, &state1, 1);
+       set_sleep_mode(md, 0);
+       mipid_read(md, MIPID_CMD_RDDSDR, &state2, 1);
+       dev_dbg(&md->spi->dev, "ESD mode 1 state1 %02x state2 %02x\n",
+               state1, state2);
+       /* Each sleep out command will trigger a self diagnostic and flip
+       * Bit6 if the test passes.
+       */
+       if (!((state1 ^ state2) & (1 << 6)))
+               ls041y3_esd_recover(md);
+}
+
+static void ls041y3_esd_check_mode2(struct mipid_device *md)
+{
+       int i;
+       u8 rbuf[2];
+       static const struct {
+               int     cmd;
+               int     wlen;
+               u16     wbuf[3];
+       } *rd, rd_ctrl[7] = {
+               { 0xb0, 4, { 0x0101, 0x01fe, } },
+               { 0xb1, 4, { 0x01de, 0x0121, } },
+               { 0xc2, 4, { 0x0100, 0x0100, } },
+               { 0xbd, 2, { 0x0100, } },
+               { 0xc2, 4, { 0x01fc, 0x0103, } },
+               { 0xb4, 0, },
+               { 0x00, 0, },
+       };
+
+       rd = rd_ctrl;
+       for (i = 0; i < 3; i++, rd++)
+               mipid_write(md, rd->cmd, (u8 *)rd->wbuf, rd->wlen);
+
+       udelay(10);
+       mipid_read(md, rd->cmd, rbuf, 2);
+       rd++;
+
+       for (i = 0; i < 3; i++, rd++) {
+               udelay(10);
+               mipid_write(md, rd->cmd, (u8 *)rd->wbuf, rd->wlen);
+       }
+
+       dev_dbg(&md->spi->dev, "ESD mode 2 state %02x\n", rbuf[1]);
+       if (rbuf[1] == 0x00)
+               ls041y3_esd_recover(md);
+}
+
+static void ls041y3_esd_check(struct mipid_device *md)
+{
+       ls041y3_esd_check_mode1(md);
+       if (md->revision >= 0x88)
+               ls041y3_esd_check_mode2(md);
+}
+
+static void mipid_esd_start_check(struct mipid_device *md)
+{
+       if (md->esd_check != NULL)
+               queue_delayed_work(md->esd_wq, &md->esd_work,
+                                  MIPID_ESD_CHECK_PERIOD);
+}
+
+static void mipid_esd_stop_check(struct mipid_device *md)
+{
+       if (md->esd_check != NULL)
+               cancel_rearming_delayed_workqueue(md->esd_wq, &md->esd_work);
+}
+
+static void mipid_esd_work(struct work_struct *work)
+{
+       struct mipid_device *md = container_of(work, struct mipid_device, esd_work.work);
+
+       mutex_lock(&md->mutex);
+       md->esd_check(md);
+       mutex_unlock(&md->mutex);
+       mipid_esd_start_check(md);
+}
+
+static int mipid_enable(struct lcd_panel *panel)
+{
+       struct mipid_device *md = to_mipid_device(panel);
+
+       mutex_lock(&md->mutex);
+
+       if (md->enabled) {
+               mutex_unlock(&md->mutex);
+               return 0;
+       }
+       set_sleep_mode(md, 0);
+       md->enabled = 1;
+       send_init_string(md);
+       set_display_state(md, 1);
+       mipid_set_bklight_level(panel, md->saved_bklight_level);
+       mipid_esd_start_check(md);
+
+       mutex_unlock(&md->mutex);
+       return 0;
+}
+
+static void mipid_disable(struct lcd_panel *panel)
+{
+       struct mipid_device *md = to_mipid_device(panel);
+
+       /*
+        * A final ESD work might be called before returning,
+        * so do this without holding the lock.
+        */
+       mipid_esd_stop_check(md);
+       mutex_lock(&md->mutex);
+
+       if (!md->enabled) {
+               mutex_unlock(&md->mutex);
+               return;
+       }
+       md->saved_bklight_level = mipid_get_bklight_level(panel);
+       mipid_set_bklight_level(panel, 0);
+       set_display_state(md, 0);
+       set_sleep_mode(md, 1);
+       md->enabled = 0;
+
+       mutex_unlock(&md->mutex);
+}
+
+static int panel_enabled(struct mipid_device *md)
+{
+       u32 disp_status;
+       int enabled;
+
+       mipid_read(md, MIPID_CMD_READ_DISP_STATUS, (u8 *)&disp_status, 4);
+       disp_status = __be32_to_cpu(disp_status);
+       enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10));
+       dev_dbg(&md->spi->dev,
+               "LCD panel %senabled by bootloader (status 0x%04x)\n",
+               enabled ? "" : "not ", disp_status);
+       return enabled;
+}
+
+static int mipid_init(struct lcd_panel *panel,
+                           struct omapfb_device *fbdev)
+{
+       struct mipid_device *md = to_mipid_device(panel);
+
+       md->fbdev = fbdev;
+       md->esd_wq = create_singlethread_workqueue("mipid_esd");
+       if (md->esd_wq == NULL) {
+               dev_err(&md->spi->dev, "can't create ESD workqueue\n");
+               return -ENOMEM;
+       }
+       INIT_DELAYED_WORK(&md->esd_work, mipid_esd_work);
+       mutex_init(&md->mutex);
+
+       md->enabled = panel_enabled(md);
+
+       if (md->enabled)
+               mipid_esd_start_check(md);
+       else
+               md->saved_bklight_level = mipid_get_bklight_level(panel);
+
+       return 0;
+}
+
+static void mipid_cleanup(struct lcd_panel *panel)
+{
+       struct mipid_device *md = to_mipid_device(panel);
+
+       if (md->enabled)
+               mipid_esd_stop_check(md);
+       destroy_workqueue(md->esd_wq);
+}
+
+static struct lcd_panel mipid_panel = {
+       .config         = OMAP_LCDC_PANEL_TFT,
+
+       .bpp            = 16,
+       .x_res          = 800,
+       .y_res          = 480,
+       .pixel_clock    = 21940,
+       .hsw            = 50,
+       .hfp            = 20,
+       .hbp            = 15,
+       .vsw            = 2,
+       .vfp            = 1,
+       .vbp            = 3,
+
+       .init           = mipid_init,
+       .cleanup        = mipid_cleanup,
+       .enable         = mipid_enable,
+       .disable        = mipid_disable,
+       .get_caps       = mipid_get_caps,
+       .set_bklight_level= mipid_set_bklight_level,
+       .get_bklight_level= mipid_get_bklight_level,
+       .get_bklight_max= mipid_get_bklight_max,
+       .run_test       = mipid_run_test,
+};
+
+static int mipid_detect(struct mipid_device *md)
+{
+       struct mipid_platform_data *pdata;
+
+       pdata = md->spi->dev.platform_data;
+       if (pdata == NULL) {
+               dev_err(&md->spi->dev, "missing platform data\n");
+               return -ENOENT;
+       }
+
+       mipid_read(md, MIPID_CMD_READ_DISP_ID, md->display_id, 3);
+       dev_dbg(&md->spi->dev, "MIPI display ID: %02x%02x%02x\n",
+               md->display_id[0], md->display_id[1], md->display_id[2]);
+
+       switch (md->display_id[0]) {
+       case 0x45:
+               md->model = MIPID_VER_LPH8923;
+               md->panel.name = "lph8923";
+               break;
+       case 0x83:
+               md->model = MIPID_VER_LS041Y3;
+               md->panel.name = "ls041y3";
+               md->esd_check = ls041y3_esd_check;
+               break;
+       default:
+               md->panel.name = "unknown";
+               dev_err(&md->spi->dev, "invalid display ID\n");
+               return -ENODEV;
+       }
+
+       md->revision = md->display_id[1];
+       md->panel.data_lines = pdata->data_lines;
+       pr_info("omapfb: %s rev %02x LCD detected\n",
+                       md->panel.name, md->revision);
+
+       return 0;
+}
+
+static int mipid_spi_probe(struct spi_device *spi)
+{
+       struct mipid_device *md;
+       int r;
+
+       md = kzalloc(sizeof(*md), GFP_KERNEL);
+       if (md == NULL) {
+               dev_err(&spi->dev, "out of memory\n");
+               return -ENOMEM;
+       }
+
+       spi->mode = SPI_MODE_0;
+       md->spi = spi;
+       dev_set_drvdata(&spi->dev, md);
+       md->panel = mipid_panel;
+
+       r = mipid_detect(md);
+       if (r < 0)
+               return r;
+
+       omapfb_register_panel(&md->panel);
+
+       return 0;
+}
+
+static int mipid_spi_remove(struct spi_device *spi)
+{
+       struct mipid_device *md = dev_get_drvdata(&spi->dev);
+
+       mipid_disable(&md->panel);
+       kfree(md);
+
+       return 0;
+}
+
+static struct spi_driver mipid_spi_driver = {
+       .driver = {
+               .name   = MIPID_MODULE_NAME,
+               .bus    = &spi_bus_type,
+               .owner  = THIS_MODULE,
+       },
+       .probe  = mipid_spi_probe,
+       .remove = __devexit_p(mipid_spi_remove),
+};
+
+static int mipid_drv_init(void)
+{
+       spi_register_driver(&mipid_spi_driver);
+
+       return 0;
+}
+module_init(mipid_drv_init);
+
+static void mipid_drv_cleanup(void)
+{
+       spi_unregister_driver(&mipid_spi_driver);
+}
+module_exit(mipid_drv_cleanup);
+
+MODULE_DESCRIPTION("MIPI display driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap/lcd_omap2evm.c b/drivers/video/omap/lcd_omap2evm.c
new file mode 100644 (file)
index 0000000..2fc46c2
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * LCD panel support for the MISTRAL OMAP2EVM board
+ *
+ * Author: Arun C <arunedarath@mistralsolutions.com>
+ *
+ * Derived from drivers/video/omap/lcd_omap3evm.c
+ * Derived from drivers/video/omap/lcd-apollon.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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/i2c/twl4030.h>
+
+#include <mach/mux.h>
+#include <mach/omapfb.h>
+#include <asm/mach-types.h>
+
+#define LCD_PANEL_ENABLE_GPIO  154
+#define LCD_PANEL_LR           128
+#define LCD_PANEL_UD           129
+#define LCD_PANEL_INI          152
+#define LCD_PANEL_QVGA         148
+#define LCD_PANEL_RESB         153
+
+#define LCD_XRES               480
+#define LCD_YRES               640
+#define LCD_PIXCLOCK_MAX       20000 /* in kHz */
+
+#define TWL_LED_LEDEN          0x00
+#define TWL_PWMA_PWMAON                0x00
+#define TWL_PWMA_PWMAOFF       0x01
+
+static unsigned int bklight_level;
+
+static int omap2evm_panel_init(struct lcd_panel *panel,
+                               struct omapfb_device *fbdev)
+{
+       gpio_request(LCD_PANEL_ENABLE_GPIO, "LCD enable");
+       gpio_request(LCD_PANEL_LR, "LCD lr");
+       gpio_request(LCD_PANEL_UD, "LCD ud");
+       gpio_request(LCD_PANEL_INI, "LCD ini");
+       gpio_request(LCD_PANEL_QVGA, "LCD qvga");
+       gpio_request(LCD_PANEL_RESB, "LCD resb");
+
+       gpio_direction_output(LCD_PANEL_ENABLE_GPIO, 1);
+       gpio_direction_output(LCD_PANEL_RESB, 1);
+       gpio_direction_output(LCD_PANEL_INI, 1);
+       gpio_direction_output(LCD_PANEL_QVGA, 0);
+       gpio_direction_output(LCD_PANEL_LR, 1);
+       gpio_direction_output(LCD_PANEL_UD, 1);
+
+       twl4030_i2c_write_u8(TWL4030_MODULE_LED, 0x11, TWL_LED_LEDEN);
+       twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x01, TWL_PWMA_PWMAON);
+       twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x02, TWL_PWMA_PWMAOFF);
+       bklight_level = 100;
+
+       return 0;
+}
+
+static void omap2evm_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int omap2evm_panel_enable(struct lcd_panel *panel)
+{
+       gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0);
+       return 0;
+}
+
+static void omap2evm_panel_disable(struct lcd_panel *panel)
+{
+       gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1);
+}
+
+static unsigned long omap2evm_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+static int omap2evm_bklight_setlevel(struct lcd_panel *panel,
+                                               unsigned int level)
+{
+       u8 c;
+       if ((level >= 0) && (level <= 100)) {
+               c = (125 * (100 - level)) / 100 + 2;
+               twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, c, TWL_PWMA_PWMAOFF);
+               bklight_level = level;
+       }
+       return 0;
+}
+
+static unsigned int omap2evm_bklight_getlevel(struct lcd_panel *panel)
+{
+       return bklight_level;
+}
+
+static unsigned int omap2evm_bklight_getmaxlevel(struct lcd_panel *panel)
+{
+       return 100;
+}
+
+struct lcd_panel omap2evm_panel = {
+       .name           = "omap2evm",
+       .config         = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+                         OMAP_LCDC_INV_HSYNC,
+
+       .bpp            = 16,
+       .data_lines     = 18,
+       .x_res          = LCD_XRES,
+       .y_res          = LCD_YRES,
+       .hsw            = 3,
+       .hfp            = 0,
+       .hbp            = 28,
+       .vsw            = 2,
+       .vfp            = 1,
+       .vbp            = 0,
+
+       .pixel_clock    = LCD_PIXCLOCK_MAX,
+
+       .init           = omap2evm_panel_init,
+       .cleanup        = omap2evm_panel_cleanup,
+       .enable         = omap2evm_panel_enable,
+       .disable        = omap2evm_panel_disable,
+       .get_caps       = omap2evm_panel_get_caps,
+       .set_bklight_level      = omap2evm_bklight_setlevel,
+       .get_bklight_level      = omap2evm_bklight_getlevel,
+       .get_bklight_max        = omap2evm_bklight_getmaxlevel,
+};
+
+static int omap2evm_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&omap2evm_panel);
+       return 0;
+}
+
+static int omap2evm_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int omap2evm_panel_suspend(struct platform_device *pdev,
+                                  pm_message_t mesg)
+{
+       return 0;
+}
+
+static int omap2evm_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver omap2evm_panel_driver = {
+       .probe          = omap2evm_panel_probe,
+       .remove         = omap2evm_panel_remove,
+       .suspend        = omap2evm_panel_suspend,
+       .resume         = omap2evm_panel_resume,
+       .driver         = {
+               .name   = "omap2evm_lcd",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init omap2evm_panel_drv_init(void)
+{
+       return platform_driver_register(&omap2evm_panel_driver);
+}
+
+static void __exit omap2evm_panel_drv_exit(void)
+{
+       platform_driver_unregister(&omap2evm_panel_driver);
+}
+
+module_init(omap2evm_panel_drv_init);
+module_exit(omap2evm_panel_drv_exit);
diff --git a/drivers/video/omap/lcd_omap3beagle.c b/drivers/video/omap/lcd_omap3beagle.c
new file mode 100644 (file)
index 0000000..eae43e4
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * LCD panel support for the TI OMAP3 Beagle board
+ *
+ * Author: Koen Kooi <koen@openembedded.org>
+ *
+ * Derived from drivers/video/omap/lcd-omap3evm.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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/i2c/twl4030.h>
+
+#include <mach/mux.h>
+#include <mach/omapfb.h>
+#include <asm/mach-types.h>
+
+#define LCD_PANEL_ENABLE_GPIO       170
+
+#define LCD_XRES               1024    
+#define LCD_YRES               768
+#define LCD_PIXCLOCK           64000 /* in kHz */
+
+static int omap3beagle_panel_init(struct lcd_panel *panel,
+                               struct omapfb_device *fbdev)
+{
+       gpio_request(LCD_PANEL_ENABLE_GPIO, "LCD enable");
+       return 0;
+}
+
+static void omap3beagle_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int omap3beagle_panel_enable(struct lcd_panel *panel)
+{
+       gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1);
+       return 0;
+}
+
+static void omap3beagle_panel_disable(struct lcd_panel *panel)
+{
+       gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0);
+}
+
+static unsigned long omap3beagle_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel omap3beagle_panel = {
+       .name           = "omap3beagle",
+       .config         = OMAP_LCDC_PANEL_TFT,
+
+       .bpp            = 16,
+       .data_lines     = 24,
+       .x_res          = LCD_XRES,
+       .y_res          = LCD_YRES,
+       .hsw            = 3,            /* hsync_len (4) - 1 */
+       .hfp            = 3,            /* right_margin (4) - 1 */
+       .hbp            = 39,           /* left_margin (40) - 1 */
+       .vsw            = 1,            /* vsync_len (2) - 1 */
+       .vfp            = 2,            /* lower_margin */
+       .vbp            = 7,            /* upper_margin (8) - 1 */
+
+       .pixel_clock    = LCD_PIXCLOCK,
+
+       .init           = omap3beagle_panel_init,
+       .cleanup        = omap3beagle_panel_cleanup,
+       .enable         = omap3beagle_panel_enable,
+       .disable        = omap3beagle_panel_disable,
+       .get_caps       = omap3beagle_panel_get_caps,
+};
+
+static int omap3beagle_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&omap3beagle_panel);
+       return 0;
+}
+
+static int omap3beagle_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int omap3beagle_panel_suspend(struct platform_device *pdev,
+                                  pm_message_t mesg)
+{
+       return 0;
+}
+
+static int omap3beagle_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver omap3beagle_panel_driver = {
+       .probe          = omap3beagle_panel_probe,
+       .remove         = omap3beagle_panel_remove,
+       .suspend        = omap3beagle_panel_suspend,
+       .resume         = omap3beagle_panel_resume,
+       .driver         = {
+               .name   = "omap3beagle_lcd",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init omap3beagle_panel_drv_init(void)
+{
+       return platform_driver_register(&omap3beagle_panel_driver);
+}
+
+static void __exit omap3beagle_panel_drv_exit(void)
+{
+       platform_driver_unregister(&omap3beagle_panel_driver);
+}
+
+module_init(omap3beagle_panel_drv_init);
+module_exit(omap3beagle_panel_drv_exit);
diff --git a/drivers/video/omap/lcd_omap3evm.c b/drivers/video/omap/lcd_omap3evm.c
new file mode 100644 (file)
index 0000000..1c3d814
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * LCD panel support for the TI OMAP3 EVM board
+ *
+ * Author: Steve Sakoman <steve@sakoman.com>
+ *
+ * Derived from drivers/video/omap/lcd-apollon.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 <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/i2c/twl4030.h>
+
+#include <mach/mux.h>
+#include <mach/omapfb.h>
+#include <asm/mach-types.h>
+
+#define LCD_PANEL_ENABLE_GPIO       153
+#define LCD_PANEL_LR                2
+#define LCD_PANEL_UD                3
+#define LCD_PANEL_INI               152
+#define LCD_PANEL_QVGA              154
+#define LCD_PANEL_RESB              155
+
+#define LCD_XRES               480
+#define LCD_YRES               640
+#define LCD_PIXCLOCK           26000 /* in kHz  */
+
+#define ENABLE_VDAC_DEDICATED  0x03
+#define ENABLE_VDAC_DEV_GRP    0x20
+#define ENABLE_VPLL2_DEDICATED 0x05
+#define ENABLE_VPLL2_DEV_GRP   0xE0
+
+#define TWL_LED_LEDEN          0x00
+#define TWL_PWMA_PWMAON                0x00
+#define TWL_PWMA_PWMAOFF       0x01
+
+static unsigned int bklight_level;
+
+static int omap3evm_panel_init(struct lcd_panel *panel,
+                               struct omapfb_device *fbdev)
+{
+       gpio_request(LCD_PANEL_LR, "LCD lr");
+       gpio_request(LCD_PANEL_UD, "LCD ud");
+       gpio_request(LCD_PANEL_INI, "LCD ini");
+       gpio_request(LCD_PANEL_RESB, "LCD resb");
+       gpio_request(LCD_PANEL_QVGA, "LCD qvga");
+
+       gpio_direction_output(LCD_PANEL_RESB, 1);
+       gpio_direction_output(LCD_PANEL_INI, 1);
+       gpio_direction_output(LCD_PANEL_QVGA, 0);
+       gpio_direction_output(LCD_PANEL_LR, 1);
+       gpio_direction_output(LCD_PANEL_UD, 1);
+
+       twl4030_i2c_write_u8(TWL4030_MODULE_LED, 0x11, TWL_LED_LEDEN);
+       twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x01, TWL_PWMA_PWMAON);
+       twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, 0x02, TWL_PWMA_PWMAOFF);
+       bklight_level = 100;
+
+       return 0;
+}
+
+static void omap3evm_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int omap3evm_panel_enable(struct lcd_panel *panel)
+{
+       gpio_set_value(LCD_PANEL_ENABLE_GPIO, 0);
+       return 0;
+}
+
+static void omap3evm_panel_disable(struct lcd_panel *panel)
+{
+       gpio_set_value(LCD_PANEL_ENABLE_GPIO, 1);
+}
+
+static unsigned long omap3evm_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+static int omap3evm_bklight_setlevel(struct lcd_panel *panel,
+                                               unsigned int level)
+{
+       u8 c;
+       if ((level >= 0) && (level <= 100)) {
+               c = (125 * (100 - level)) / 100 + 2;
+               twl4030_i2c_write_u8(TWL4030_MODULE_PWMA, c, TWL_PWMA_PWMAOFF);
+               bklight_level = level;
+       }
+       return 0;
+}
+
+static unsigned int omap3evm_bklight_getlevel(struct lcd_panel *panel)
+{
+       return bklight_level;
+}
+
+static unsigned int omap3evm_bklight_getmaxlevel(struct lcd_panel *panel)
+{
+       return 100;
+}
+
+struct lcd_panel omap3evm_panel = {
+       .name           = "omap3evm",
+       .config         = OMAP_LCDC_PANEL_TFT | OMAP_LCDC_INV_VSYNC |
+                         OMAP_LCDC_INV_HSYNC,
+
+       .bpp            = 16,
+       .data_lines     = 18,
+       .x_res          = LCD_XRES,
+       .y_res          = LCD_YRES,
+       .hsw            = 3,            /* hsync_len (4) - 1 */
+       .hfp            = 3,            /* right_margin (4) - 1 */
+       .hbp            = 39,           /* left_margin (40) - 1 */
+       .vsw            = 1,            /* vsync_len (2) - 1 */
+       .vfp            = 2,            /* lower_margin */
+       .vbp            = 7,            /* upper_margin (8) - 1 */
+
+       .pixel_clock    = LCD_PIXCLOCK,
+
+       .init           = omap3evm_panel_init,
+       .cleanup        = omap3evm_panel_cleanup,
+       .enable         = omap3evm_panel_enable,
+       .disable        = omap3evm_panel_disable,
+       .get_caps       = omap3evm_panel_get_caps,
+       .set_bklight_level      = omap3evm_bklight_setlevel,
+       .get_bklight_level      = omap3evm_bklight_getlevel,
+       .get_bklight_max        = omap3evm_bklight_getmaxlevel,
+};
+
+static int omap3evm_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&omap3evm_panel);
+       return 0;
+}
+
+static int omap3evm_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int omap3evm_panel_suspend(struct platform_device *pdev,
+                                  pm_message_t mesg)
+{
+       return 0;
+}
+
+static int omap3evm_panel_resume(struct platform_device *pdev)
+{
+       return 0;
+}
+
+struct platform_driver omap3evm_panel_driver = {
+       .probe          = omap3evm_panel_probe,
+       .remove         = omap3evm_panel_remove,
+       .suspend        = omap3evm_panel_suspend,
+       .resume         = omap3evm_panel_resume,
+       .driver         = {
+               .name   = "omap3evm_lcd",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init omap3evm_panel_drv_init(void)
+{
+       return platform_driver_register(&omap3evm_panel_driver);
+}
+
+static void __exit omap3evm_panel_drv_exit(void)
+{
+       platform_driver_unregister(&omap3evm_panel_driver);
+}
+
+module_init(omap3evm_panel_drv_init);
+module_exit(omap3evm_panel_drv_exit);
diff --git a/drivers/video/omap/lcd_overo.c b/drivers/video/omap/lcd_overo.c
new file mode 100644 (file)
index 0000000..2bc5c92
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * LCD panel support for the Gumstix Overo
+ *
+ * Author: Steve Sakoman <steve@sakoman.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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl4030.h>
+
+#include <mach/gpio.h>
+#include <mach/mux.h>
+#include <mach/omapfb.h>
+#include <asm/mach-types.h>
+
+#define LCD_ENABLE       144
+
+static int overo_panel_init(struct lcd_panel *panel,
+                               struct omapfb_device *fbdev)
+{
+       if ((gpio_request(LCD_ENABLE, "LCD_ENABLE") == 0) &&
+           (gpio_direction_output(LCD_ENABLE, 1) == 0))
+               gpio_export(LCD_ENABLE, 0);
+       else
+               printk(KERN_ERR "could not obtain gpio for LCD_ENABLE\n");
+
+       return 0;
+}
+
+static void overo_panel_cleanup(struct lcd_panel *panel)
+{
+       gpio_free(LCD_ENABLE);
+}
+
+static int overo_panel_enable(struct lcd_panel *panel)
+{
+       gpio_set_value(LCD_ENABLE, 1);
+       return 0;
+}
+
+static void overo_panel_disable(struct lcd_panel *panel)
+{
+       gpio_set_value(LCD_ENABLE, 0);
+}
+
+static unsigned long overo_panel_get_caps(struct lcd_panel *panel)
+{
+       return 0;
+}
+
+struct lcd_panel overo_panel = {
+       .name           = "overo",
+       .config         = OMAP_LCDC_PANEL_TFT,
+       .bpp            = 16,
+       .data_lines     = 24,
+
+#if defined CONFIG_FB_OMAP_031M3R
+
+       /* 640 x 480 @ 60 Hz  Reduced blanking VESA CVT 0.31M3-R */
+       .x_res          = 640,
+       .y_res          = 480,
+       .hfp            = 48,
+       .hsw            = 32,
+       .hbp            = 80,
+       .vfp            = 3,
+       .vsw            = 4,
+       .vbp            = 7,
+       .pixel_clock    = 23500,
+
+#elif defined CONFIG_FB_OMAP_048M3R
+
+       /* 800 x 600 @ 60 Hz  Reduced blanking VESA CVT 0.48M3-R */
+       .x_res          = 800,
+       .y_res          = 600,
+       .hfp            = 48,
+       .hsw            = 32,
+       .hbp            = 80,
+       .vfp            = 3,
+       .vsw            = 4,
+       .vbp            = 11,
+       .pixel_clock    = 35500,
+
+#elif defined CONFIG_FB_OMAP_079M3R
+
+       /* 1024 x 768 @ 60 Hz  Reduced blanking VESA CVT 0.79M3-R */
+       .x_res          = 1024,
+       .y_res          = 768,
+       .hfp            = 48,
+       .hsw            = 32,
+       .hbp            = 80,
+       .vfp            = 3,
+       .vsw            = 4,
+       .vbp            = 15,
+       .pixel_clock    = 56000,
+
+#elif defined CONFIG_FB_OMAP_092M9R
+
+       /* 1280 x 720 @ 60 Hz  Reduced blanking VESA CVT 0.92M9-R */
+       .x_res          = 1280,
+       .y_res          = 720,
+       .hfp            = 48,
+       .hsw            = 32,
+       .hbp            = 80,
+       .vfp            = 3,
+       .vsw            = 5,
+       .vbp            = 13,
+       .pixel_clock    = 64000,
+
+#else
+
+       /* use 640 x 480 if no config option */
+       /* 640 x 480 @ 60 Hz  Reduced blanking VESA CVT 0.31M3-R */
+       .x_res          = 640,
+       .y_res          = 480,
+       .hfp            = 48,
+       .hsw            = 32,
+       .hbp            = 80,
+       .vfp            = 3,
+       .vsw            = 4,
+       .vbp            = 7,
+       .pixel_clock    = 23500,
+
+#endif
+
+       .init           = overo_panel_init,
+       .cleanup        = overo_panel_cleanup,
+       .enable         = overo_panel_enable,
+       .disable        = overo_panel_disable,
+       .get_caps       = overo_panel_get_caps,
+};
+
+static int overo_panel_probe(struct platform_device *pdev)
+{
+       omapfb_register_panel(&overo_panel);
+       return 0;
+}
+
+static int overo_panel_remove(struct platform_device *pdev)
+{
+       /* omapfb does not have unregister_panel */
+       return 0;
+}
+
+static struct platform_driver overo_panel_driver = {
+       .probe          = overo_panel_probe,
+       .remove         = overo_panel_remove,
+       .driver         = {
+               .name   = "overo_lcd",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init overo_panel_drv_init(void)
+{
+       return platform_driver_register(&overo_panel_driver);
+}
+
+static void __exit overo_panel_drv_exit(void)
+{
+       platform_driver_unregister(&overo_panel_driver);
+}
+
+module_init(overo_panel_drv_init);
+module_exit(overo_panel_drv_exit);
diff --git a/drivers/video/omap/lcd_p2.c b/drivers/video/omap/lcd_p2.c
new file mode 100644 (file)
index 0000000..dd40fd7
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * LCD panel support for the TI OMAP P2 board
+ *
+ * Authors:
+ *   jekyll <jekyll@mail.jekyll.idv.tw>
+ *   B Jp <lastjp_fr@yahoo.fr>
+ *   Brian Swetland <swetland@android.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.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+
+#include <mach/mux.h>
+#include <mach/gpio.h>
+#include <mach/omapfb.h>
+
+/*
+ * 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 <dpeverley at mpc-data.co.uk>
+ *
+ *  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 "../drivers/ssi/omap-uwire.h"
+
+#define LCD_UWIRE_CS 0
+
+static int p2_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev)
+{
+       return 0;
+}
+
+static void p2_panel_cleanup(struct lcd_panel *panel)
+{
+}
+
+static int p2_panel_enable(struct lcd_panel *panel)
+{
+       int i;
+       unsigned long value;
+
+               /* thwack the reset line */
+       gpio_direction_output(19, 0);
+       mdelay(2);
+       gpio_set_value(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 */
+       gpio_direction_output(134, 1);
+
+       return 0;
+}
+
+static void p2_panel_disable(struct lcd_panel *panel)
+{
+}
+
+static unsigned long p2_panel_get_caps(struct lcd_panel *panel)
+{
+       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)
+{
+       omapfb_register_panel(&p2_panel);
+       return 0;
+}
+
+static int p2_panel_remove(struct platform_device *pdev)
+{
+       return 0;
+}
+
+static int p2_panel_suspend(struct platform_device *pdev, pm_message_t mesg)
+{
+       return 0;
+}
+
+static int p2_panel_resume(struct platform_device *pdev)
+{
+       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);
+
index 060d72fe57cb1f639861ae24cf10a6ed0babdb40..7617dd406a32332a838ff767966a6b9bac299424 100644 (file)
@@ -67,6 +67,7 @@ static struct caps_table_struct ctrl_caps[] = {
        { OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE, "pixel double window" },
        { OMAPFB_CAPS_WINDOW_SCALE,   "scale window" },
        { OMAPFB_CAPS_WINDOW_OVERLAY, "overlay window" },
        { OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE, "pixel double window" },
        { OMAPFB_CAPS_WINDOW_SCALE,   "scale window" },
        { OMAPFB_CAPS_WINDOW_OVERLAY, "overlay window" },
+       { OMAPFB_CAPS_WINDOW_ROTATE,  "rotate window" },
        { OMAPFB_CAPS_SET_BACKLIGHT,  "backlight setting" },
 };
 
        { OMAPFB_CAPS_SET_BACKLIGHT,  "backlight setting" },
 };
 
@@ -215,13 +216,22 @@ static int ctrl_change_mode(struct fb_info *fbi)
                                 offset, var->xres_virtual,
                                 plane->info.pos_x, plane->info.pos_y,
                                 var->xres, var->yres, plane->color_mode);
                                 offset, var->xres_virtual,
                                 plane->info.pos_x, plane->info.pos_y,
                                 var->xres, var->yres, plane->color_mode);
-       if (fbdev->ctrl->set_scale != NULL)
+       if (r < 0)
+               return r;
+
+       if (fbdev->ctrl->set_rotate != NULL)
+               if((r = fbdev->ctrl->set_rotate(var->rotate)) < 0)
+                       return r;
+
+       if ((fbdev->ctrl->set_scale != NULL) && (plane->idx > 0))
                r = fbdev->ctrl->set_scale(plane->idx,
                                   var->xres, var->yres,
                                   plane->info.out_width,
                                   plane->info.out_height);
                r = fbdev->ctrl->set_scale(plane->idx,
                                   var->xres, var->yres,
                                   plane->info.out_width,
                                   plane->info.out_height);
+       if (r < 0)
+               return r;
 
 
-       return r;
+       return 0;
 }
 
 /*
 }
 
 /*
@@ -552,7 +562,6 @@ static int set_fb_var(struct fb_info *fbi,
                var->xoffset = var->xres_virtual - var->xres;
        if (var->yres + var->yoffset > var->yres_virtual)
                var->yoffset = var->yres_virtual - var->yres;
                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;
 
        if (plane->color_mode == OMAPFB_COLOR_RGB444) {
                var->red.offset   = 8; var->red.length   = 4;
 
        if (plane->color_mode == OMAPFB_COLOR_RGB444) {
                var->red.offset   = 8; var->red.length   = 4;
@@ -598,7 +607,7 @@ static void omapfb_rotate(struct fb_info *fbi, int rotate)
        struct omapfb_device *fbdev = plane->fbdev;
 
        omapfb_rqueue_lock(fbdev);
        struct omapfb_device *fbdev = plane->fbdev;
 
        omapfb_rqueue_lock(fbdev);
-       if (cpu_is_omap15xx() && rotate != fbi->var.rotate) {
+       if (rotate != fbi->var.rotate) {
                struct fb_var_screeninfo *new_var = &fbdev->new_var;
 
                memcpy(new_var, &fbi->var, sizeof(*new_var));
                struct fb_var_screeninfo *new_var = &fbdev->new_var;
 
                memcpy(new_var, &fbi->var, sizeof(*new_var));
@@ -705,28 +714,42 @@ int omapfb_update_window_async(struct fb_info *fbi,
                                void (*callback)(void *),
                                void *callback_data)
 {
                                void (*callback)(void *),
                                void *callback_data)
 {
+       int xres, yres;
        struct omapfb_plane_struct *plane = fbi->par;
        struct omapfb_device *fbdev = plane->fbdev;
        struct omapfb_plane_struct *plane = fbi->par;
        struct omapfb_device *fbdev = plane->fbdev;
-       struct fb_var_screeninfo *var;
+       struct fb_var_screeninfo *var = &fbi->var;
+
+       switch (var->rotate) {
+       case 0:
+       case 180:
+               xres = fbdev->panel->x_res;
+               yres = fbdev->panel->y_res;
+               break;
+       case 90:
+       case 270:
+               xres = fbdev->panel->y_res;
+               yres = fbdev->panel->x_res;
+               break;
+       default:
+               return -EINVAL;
+       }
 
 
-       var = &fbi->var;
-       if (win->x >= var->xres || win->y >= var->yres ||
-           win->out_x > var->xres || win->out_y >= var->yres)
+       if (win->x >= xres || win->y >= yres ||
+           win->out_x > xres || win->out_y > yres)
                return -EINVAL;
 
        if (!fbdev->ctrl->update_window ||
            fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)
                return -ENODEV;
 
                return -EINVAL;
 
        if (!fbdev->ctrl->update_window ||
            fbdev->ctrl->get_update_mode() != OMAPFB_MANUAL_UPDATE)
                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;
-       /* The out sizes should be cropped to the LCD size */
-       if (win->out_x + win->out_width > fbdev->panel->x_res)
-               win->out_width = fbdev->panel->x_res - win->out_x;
-       if (win->out_y + win->out_height > fbdev->panel->y_res)
-               win->out_height = fbdev->panel->y_res - win->out_y;
+       if (win->x + win->width > xres)
+               win->width = xres - win->x;
+       if (win->y + win->height > yres)
+               win->height = yres - win->y;
+       if (win->out_x + win->out_width > xres)
+               win->out_width = xres - win->out_x;
+       if (win->out_y + win->out_height > yres)
+               win->out_height = yres - win->out_y;
        if (!win->width || !win->height || !win->out_width || !win->out_height)
                return 0;
 
        if (!win->width || !win->height || !win->out_width || !win->out_height)
                return 0;
 
@@ -1695,8 +1718,8 @@ static int omapfb_do_probe(struct platform_device *pdev,
 
        pr_info("omapfb: configured for panel %s\n", fbdev->panel->name);
 
 
        pr_info("omapfb: configured for panel %s\n", fbdev->panel->name);
 
-       def_vxres = def_vxres ? : fbdev->panel->x_res;
-       def_vyres = def_vyres ? : fbdev->panel->y_res;
+       def_vxres = def_vxres ? def_vxres : fbdev->panel->x_res;
+       def_vyres = def_vyres ? def_vyres : fbdev->panel->y_res;
 
        init_state++;
 
 
        init_state++;
 
@@ -1818,8 +1841,8 @@ static int omapfb_suspend(struct platform_device *pdev, pm_message_t mesg)
 {
        struct omapfb_device *fbdev = platform_get_drvdata(pdev);
 
 {
        struct omapfb_device *fbdev = platform_get_drvdata(pdev);
 
-       omapfb_blank(FB_BLANK_POWERDOWN, fbdev->fb_info[0]);
-
+       if (fbdev != NULL)
+               omapfb_blank(VESA_POWERDOWN, fbdev->fb_info[0]);
        return 0;
 }
 
        return 0;
 }
 
@@ -1828,7 +1851,8 @@ static int omapfb_resume(struct platform_device *pdev)
 {
        struct omapfb_device *fbdev = platform_get_drvdata(pdev);
 
 {
        struct omapfb_device *fbdev = platform_get_drvdata(pdev);
 
-       omapfb_blank(FB_BLANK_UNBLANK, fbdev->fb_info[0]);
+       if (fbdev != NULL)
+               omapfb_blank(VESA_NO_BLANKING, fbdev->fb_info[0]);
        return 0;
 }
 
        return 0;
 }
 
index a13c8dcad2a8429c1a5da98f489a60b0da46ff51..29fa368f5221e2bfc97cbe0110958bd8cf52631f 100644 (file)
@@ -2,7 +2,7 @@
  * OMAP2 Remote Frame Buffer Interface support
  *
  * Copyright (C) 2005 Nokia Corporation
  * OMAP2 Remote Frame Buffer Interface support
  *
  * Copyright (C) 2005 Nokia Corporation
- * Author: Juha Yrjölä <juha.yrjola@nokia.com>
+ * Author: Juha Yrj�l� <juha.yrjola@nokia.com>
  *        Imre Deak <imre.deak@nokia.com>
  *
  * This program is free software; you can redistribute it and/or modify it
  *        Imre Deak <imre.deak@nokia.com>
  *
  * This program is free software; you can redistribute it and/or modify it
@@ -57,6 +57,7 @@
 
 #define DISPC_BASE             0x48050400
 #define DISPC_CONTROL          0x0040
 
 #define DISPC_BASE             0x48050400
 #define DISPC_CONTROL          0x0040
+#define DISPC_IRQ_FRAMEMASK     0x0001
 
 static struct {
        void __iomem    *base;
 
 static struct {
        void __iomem    *base;
@@ -551,7 +552,8 @@ static int rfbi_init(struct omapfb_device *fbdev)
        l = (0x01 << 2);
        rfbi_write_reg(RFBI_CONTROL, l);
 
        l = (0x01 << 2);
        rfbi_write_reg(RFBI_CONTROL, l);
 
-       if ((r = omap_dispc_request_irq(rfbi_dma_callback, NULL)) < 0) {
+       if ((r = omap_dispc_request_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback,
+                                        NULL)) < 0) {
                dev_err(fbdev->dev, "can't get DISPC irq\n");
                rfbi_enable_clocks(0);
                return r;
                dev_err(fbdev->dev, "can't get DISPC irq\n");
                rfbi_enable_clocks(0);
                return r;
@@ -568,7 +570,7 @@ static int rfbi_init(struct omapfb_device *fbdev)
 
 static void rfbi_cleanup(void)
 {
 
 static void rfbi_cleanup(void)
 {
-       omap_dispc_free_irq();
+       omap_dispc_free_irq(DISPC_IRQ_FRAMEMASK, rfbi_dma_callback, NULL);
        rfbi_put_clocks();
        iounmap(rfbi.base);
 }
        rfbi_put_clocks();
        iounmap(rfbi.base);
 }
index a7694622024925befb1f26692523937146dd3588..cc697ccd82953bdcdaa83ff287a96ef0dc9e685c 100644 (file)
@@ -2,7 +2,7 @@
  * OMAP1 Special OptimiSed Screen Interface support
  *
  * Copyright (C) 2004-2005 Nokia Corporation
  * OMAP1 Special OptimiSed Screen Interface support
  *
  * Copyright (C) 2004-2005 Nokia Corporation
- * Author: Juha Yrjölä <juha.yrjola@nokia.com>
+ * Author: Juha Yrj�l� <juha.yrjola@nokia.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
  *
  * 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
index e02c15d158fc978d72e0b0647c87391a446af76b..e1bf4707aa7a244cc030486ff02a9be15221482d 100644 (file)
@@ -1,2 +1,3 @@
 arch
 arch
+asm-offsets.h
 mach-types.h
 mach-types.h
diff --git a/include/asm-arm/hardware/tsc2101.h b/include/asm-arm/hardware/tsc2101.h
new file mode 100644 (file)
index 0000000..4490458
--- /dev/null
@@ -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 */
index b9966e64604e81b965526afcc39b4209e3a8148c..94d742bdf25970aad2856edceb06a1d9873c9d8b 100644 (file)
@@ -36,6 +36,8 @@
 #define CN_VAL_CIFS                     0x1
 #define CN_W1_IDX                      0x3     /* w1 communication */
 #define CN_W1_VAL                      0x1
 #define CN_VAL_CIFS                     0x1
 #define CN_W1_IDX                      0x3     /* w1 communication */
 #define CN_W1_VAL                      0x1
+#define CN_IDX_SX1SND                  0x4
+#define CN_VAL_SX1SND                  0x1
 #define CN_IDX_V86D                    0x4
 #define CN_VAL_V86D_UVESAFB            0x1
 #define CN_IDX_BB                      0x5     /* BlackBoard, from the TSP GPL sampling framework */
 #define CN_IDX_V86D                    0x4
 #define CN_VAL_V86D_UVESAFB            0x1
 #define CN_IDX_BB                      0x5     /* BlackBoard, from the TSP GPL sampling framework */
diff --git a/include/linux/i2c/lm8323.h b/include/linux/i2c/lm8323.h
new file mode 100644 (file)
index 0000000..67e82bc
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * lm8323.h - Configuration for LM8323 keypad driver.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation (version 2 of the License only).
+ *
+ * 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 __LINUX_LM8323_H
+#define __LINUX_LM8323_H
+
+#include <linux/types.h>
+
+/*
+ * Largest keycode that the chip can send, plus one,
+ * so keys can be mapped directly at the index of the
+ * LM8323 keycode instead of subtracting one.
+ */
+#define LM8323_KEYMAP_SIZE (0x7f + 1)
+
+struct lm8323_platform_data {
+       int debounce_time; /* Time to watch for key bouncing, in ms. */
+       int active_time; /* Idle time until sleep, in ms. */
+
+       int size_x;
+       int size_y;
+       int repeat:1;
+       const s16 *keymap;
+
+       char *pwm1_name; /* Device name for PWM1. */
+       char *pwm2_name; /* Device name for PWM2. */
+       char *pwm3_name; /* Device name for PWM3. */
+
+       char *name; /* Device name. */
+};
+
+#endif /* __LINUX_LM8323_H */
diff --git a/include/linux/i2c/lp5521.h b/include/linux/i2c/lp5521.h
new file mode 100644 (file)
index 0000000..070e8be
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * lp5521.h - header for LP5521 LED driver
+ *
+ * Copyright (C) 2009 Nokia Corporation
+ *
+ * Contact: Felipe Balbi <felipe.balbi@nokia.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 __LP5521_H_
+#define __LP5521_H_
+
+enum lp5521_mode {
+       LP5521_MODE_LOAD,
+       LP5521_MODE_RUN,
+       LP5521_MODE_DIRECT_CONTROL,
+};
+
+struct lp5521_platform_data {
+       enum lp5521_mode        mode;
+
+       unsigned                red_present:1;
+       unsigned                green_present:1;
+       unsigned                blue_present:1;
+
+       const char              *label;
+};
+
+#endif /* End of __LP5521_H */
diff --git a/include/linux/i2c/menelaus.h b/include/linux/i2c/menelaus.h
new file mode 100644 (file)
index 0000000..9587398
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * include/linux/i2c/menelaus.h
+ *
+ * Functions to access Menelaus power management chip
+ */
+
+#ifndef __ASM_ARCH_MENELAUS_H
+#define __ASM_ARCH_MENELAUS_H
+
+#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 */
+
+/* VCORE_CTRL1 register */
+#define VCORE_CTRL1_BYP_COMP           (1 << 5)
+#define VCORE_CTRL1_HW_NSW             (1 << 7)
+
+/* GPIO_CTRL register */
+#define GPIO_CTRL_SLOTSELEN            (1 << 5)
+#define GPIO_CTRL_SLPCTLEN             (1 << 6)
+#define GPIO1_DIR_INPUT                (1 << 0)
+#define GPIO2_DIR_INPUT                (1 << 1)
+#define GPIO3_DIR_INPUT                (1 << 2)
+
+/* MCT_CTRL1 register */
+#define MCT_CTRL1_S1_CMD_OD            (1 << 2)
+#define MCT_CTRL1_S2_CMD_OD            (1 << 3)
+
+/* MCT_CTRL2 register */
+#define MCT_CTRL2_VS2_SEL_D0           (1 << 0)
+#define MCT_CTRL2_VS2_SEL_D1           (1 << 1)
+#define MCT_CTRL2_S1CD_BUFEN           (1 << 4)
+#define MCT_CTRL2_S2CD_BUFEN           (1 << 5)
+#define MCT_CTRL2_S1CD_DBEN            (1 << 6)
+#define MCT_CTRL2_S2CD_BEN             (1 << 7)
+
+/* MCT_CTRL3 register */
+#define MCT_CTRL3_SLOT1_EN             (1 << 0)
+#define MCT_CTRL3_SLOT2_EN             (1 << 1)
+#define MCT_CTRL3_S1_AUTO_EN           (1 << 2)
+#define MCT_CTRL3_S2_AUTO_EN           (1 << 3)
+
+/* MCT_PIN_ST register */
+#define MCT_PIN_ST_S1_CD_ST            (1 << 0)
+#define MCT_PIN_ST_S2_CD_ST            (1 << 1)
+
+struct device;
+
+struct menelaus_platform_data {
+       int (*late_init)(struct device *dev);
+};
+
+extern int menelaus_register_mmc_callback(void (*callback)(void *data,
+                                         u8 card_mask),
+                                         void *data);
+extern void menelaus_unregister_mmc_callback(void);
+extern int menelaus_set_mmc_opendrain(int slot, int enable);
+extern int menelaus_set_mmc_slot(int slot, int enable, int power, int cd_on);
+extern int menelaus_enable_slot(int slot, int enable);
+
+extern int menelaus_set_vmem(unsigned int mV);
+extern int menelaus_set_vio(unsigned int mV);
+extern int menelaus_set_vmmc(unsigned int mV);
+extern int menelaus_set_vaux(unsigned int mV);
+extern int menelaus_set_vdcdc(int dcdc, unsigned int mV);
+extern int menelaus_set_slot_sel(int enable);
+extern int menelaus_get_slot_pin_states(void);
+extern int menelaus_set_vcore_sw(unsigned int mV);
+extern int menelaus_set_vcore_hw(unsigned int roof_mV, unsigned int floor_mV);
+
+#define EN_VPLL_SLEEP  (1 << 7)
+#define EN_VMMC_SLEEP  (1 << 6)
+#define EN_VAUX_SLEEP  (1 << 5)
+#define EN_VIO_SLEEP   (1 << 4)
+#define EN_VMEM_SLEEP  (1 << 3)
+#define EN_DC3_SLEEP   (1 << 2)
+#define EN_DC2_SLEEP   (1 << 1)
+#define EN_VC_SLEEP    (1 << 0)
+
+extern int menelaus_set_regulator_sleep(int enable, u32 val);
+
+#if defined(CONFIG_ARCH_OMAP24XX) && defined(CONFIG_MENELAUS)
+#define omap_has_menelaus()    1
+#else
+#define omap_has_menelaus()    0
+#endif
+
+#endif
diff --git a/include/linux/i2c/twl4030-madc.h b/include/linux/i2c/twl4030-madc.h
new file mode 100644 (file)
index 0000000..24523b5
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * include/linux/i2c/twl4030-madc.h
+ *
+ * TWL4030 MADC module driver header
+ *
+ * Copyright (C) 2008 Nokia Corporation
+ * Mikko Ylinen <mikko.k.ylinen@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _TWL4030_MADC_H
+#define _TWL4030_MADC_H
+
+struct twl4030_madc_conversion_method {
+       u8 sel;
+       u8 avg;
+       u8 rbase;
+       u8 ctrl;
+};
+
+#define TWL4030_MADC_MAX_CHANNELS 16
+
+struct twl4030_madc_request {
+       u16 channels;
+       u16 do_avg;
+       u16 method;
+       u16 type;
+       int active;
+       int result_pending;
+       int rbuf[TWL4030_MADC_MAX_CHANNELS];
+       void (*func_cb)(int len, int channels, int *buf);
+};
+
+enum conversion_methods {
+       TWL4030_MADC_RT,
+       TWL4030_MADC_SW1,
+       TWL4030_MADC_SW2,
+       TWL4030_MADC_NUM_METHODS
+};
+
+enum sample_type {
+       TWL4030_MADC_WAIT,
+       TWL4030_MADC_IRQ_ONESHOT,
+       TWL4030_MADC_IRQ_REARM
+};
+
+#define TWL4030_MADC_CTRL1             0x00
+#define TWL4030_MADC_CTRL2             0x01
+
+#define TWL4030_MADC_RTSELECT_LSB      0x02
+#define TWL4030_MADC_SW1SELECT_LSB     0x06
+#define TWL4030_MADC_SW2SELECT_LSB     0x0A
+
+#define TWL4030_MADC_RTAVERAGE_LSB     0x04
+#define TWL4030_MADC_SW1AVERAGE_LSB    0x08
+#define TWL4030_MADC_SW2AVERAGE_LSB    0x0C
+
+#define TWL4030_MADC_CTRL_SW1          0x12
+#define TWL4030_MADC_CTRL_SW2          0x13
+
+#define TWL4030_MADC_RTCH0_LSB         0x17
+#define TWL4030_MADC_GPCH0_LSB         0x37
+
+#define TWL4030_MADC_MADCON            (1<<0)  /* MADC power on */
+#define TWL4030_MADC_BUSY              (1<<0)  /* MADC busy */
+#define TWL4030_MADC_EOC_SW            (1<<1)  /* MADC conversion completion */
+#define TWL4030_MADC_SW_START          (1<<5)  /* MADC SWx start conversion */
+
+#define        TWL4030_MADC_ADCIN0             (1<<0)
+#define        TWL4030_MADC_ADCIN1             (1<<1)
+#define        TWL4030_MADC_ADCIN2             (1<<2)
+#define        TWL4030_MADC_ADCIN3             (1<<3)
+#define        TWL4030_MADC_ADCIN4             (1<<4)
+#define        TWL4030_MADC_ADCIN5             (1<<5)
+#define        TWL4030_MADC_ADCIN6             (1<<6)
+#define        TWL4030_MADC_ADCIN7             (1<<7)
+#define        TWL4030_MADC_ADCIN8             (1<<8)
+#define        TWL4030_MADC_ADCIN9             (1<<9)
+#define        TWL4030_MADC_ADCIN10            (1<<10)
+#define        TWL4030_MADC_ADCIN11            (1<<11)
+#define        TWL4030_MADC_ADCIN12            (1<<12)
+#define        TWL4030_MADC_ADCIN13            (1<<13)
+#define        TWL4030_MADC_ADCIN14            (1<<14)
+#define        TWL4030_MADC_ADCIN15            (1<<15)
+
+/* Fixed channels */
+#define TWL4030_MADC_BTEMP             TWL4030_MADC_ADCIN1
+#define TWL4030_MADC_VBUS              TWL4030_MADC_ADCIN8
+#define TWL4030_MADC_VBKB              TWL4030_MADC_ADCIN9
+#define        TWL4030_MADC_ICHG               TWL4030_MADC_ADCIN10
+#define TWL4030_MADC_VCHG              TWL4030_MADC_ADCIN11
+#define        TWL4030_MADC_VBAT               TWL4030_MADC_ADCIN12
+
+/* BCI related - XXX To be moved elsewhere */
+#define TWL4030_BCI_BCICTL1            0x23
+#define        TWL4030_BCI_MESBAT              (1<<1)
+#define        TWL4030_BCI_TYPEN               (1<<4)
+#define        TWL4030_BCI_ITHEN               (1<<3)
+
+#define TWL4030_MADC_IOC_MAGIC '`'
+#define TWL4030_MADC_IOCX_ADC_RAW_READ         _IO(TWL4030_MADC_IOC_MAGIC, 0)
+
+struct twl4030_madc_user_parms {
+       int channel;
+       int average;
+       int status;
+       u16 result;
+};
+
+int twl4030_madc_conversion(struct twl4030_madc_request *conv);
+
+#endif
index 0dc80ef249752dea6c1d1cb735d6200341f46a9e..87accda0e40c2b08e7b4932f7addc86f773911e0 100644 (file)
@@ -243,6 +243,37 @@ int twl4030_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes);
 #define RES_STATE_SLEEP                0x8
 #define RES_STATE_OFF          0x0
 
 #define RES_STATE_SLEEP                0x8
 #define RES_STATE_OFF          0x0
 
+/* Power resources */
+
+#define RES_VAUX1               1
+#define RES_VAUX2               2
+#define RES_VAUX3               3
+#define RES_VAUX4               4
+#define RES_VMMC1               5
+#define RES_VMMC2               6
+#define RES_VPLL1               7
+#define RES_VPLL2               8
+#define RES_VSIM                9
+#define RES_VDAC                10
+#define RES_VINTANA1            11
+#define RES_VINTANA2            12
+#define RES_VINTDIG             13
+#define RES_VIO                 14
+#define RES_VDD1                15
+#define RES_VDD2                16
+#define RES_VUSB_1V5            17
+#define RES_VUSB_1V8            18
+#define RES_VUSB_3V1            19
+#define RES_VUSBCP              20
+#define RES_REGEN               21
+#define RES_NRES_PWRON          22
+#define RES_CLKEN               23
+#define RES_SYSEN               24
+#define RES_HFCLKOUT            25
+#define RES_32KCLKOUT           26
+#define RES_RESET               27
+#define RES_Main_Ref            28
+
 /*
  * Power Bus Message Format ... these can be sent individually by Linux,
  * but are usually part of downloaded scripts that are run when various
 /*
  * Power Bus Message Format ... these can be sent individually by Linux,
  * but are usually part of downloaded scripts that are run when various
@@ -302,12 +333,19 @@ struct twl4030_madc_platform_data {
        int             irq_line;
 };
 
        int             irq_line;
 };
 
+/* Boards have uniqe mappings of {col, row} --> keycode.
+ * Column and row are 4 bits, but range only from 0..7;
+ * a PERSISTENT_KEY is "always on" and never reported.
+ */
+#define KEY_PERSISTENT         0x00800000
+#define KEY(col, row, keycode) (((col) << 28) | ((row) << 24) | (keycode))
+#define PERSISTENT_KEY(c, r)   KEY(c, r, KEY_PERSISTENT)
+
 struct twl4030_keypad_data {
 struct twl4030_keypad_data {
-       int rows;
-       int cols;
-       int *keymap;
-       int irq;
-       unsigned int keymapsize;
+       unsigned rows;
+       unsigned cols;
+       unsigned *keymap;
+       unsigned short keymapsize;
        unsigned int rep:1;
 };
 
        unsigned int rep:1;
 };
 
@@ -320,6 +358,34 @@ struct twl4030_usb_data {
        enum twl4030_usb_mode   usb_mode;
 };
 
        enum twl4030_usb_mode   usb_mode;
 };
 
+struct twl4030_ins {
+       u16 pmb_message;
+       u8 delay;
+};
+
+struct twl4030_script {
+       struct twl4030_ins *script;
+       unsigned size;
+       u8 flags;
+#define TRITON_WRST_SCRIPT     (1<<0)
+#define TRITON_WAKEUP12_SCRIPT (1<<1)
+#define TRITON_WAKEUP3_SCRIPT  (1<<2)
+#define TRITON_SLEEP_SCRIPT    (1<<3)
+};
+
+struct twl4030_resconfig {
+       u8 resource;
+       u8 devgroup;
+       u8 type;
+       u8 type2;
+};
+
+struct twl4030_power_data {
+       struct twl4030_script **scripts;
+       unsigned size;
+       const struct twl4030_resconfig *resource_config;
+};
+
 struct twl4030_platform_data {
        unsigned                                irq_base, irq_end;
        struct twl4030_bci_platform_data        *bci;
 struct twl4030_platform_data {
        unsigned                                irq_base, irq_end;
        struct twl4030_bci_platform_data        *bci;
@@ -327,6 +393,7 @@ struct twl4030_platform_data {
        struct twl4030_madc_platform_data       *madc;
        struct twl4030_keypad_data              *keypad;
        struct twl4030_usb_data                 *usb;
        struct twl4030_madc_platform_data       *madc;
        struct twl4030_keypad_data              *keypad;
        struct twl4030_usb_data                 *usb;
+       struct twl4030_power_data               *power;
 
        /* LDO regulators */
        struct regulator_init_data              *vdac;
 
        /* LDO regulators */
        struct regulator_init_data              *vdac;
@@ -357,7 +424,6 @@ int twl4030_sih_setup(int module);
 #define TWL4030_VAUX3_DEV_GRP          0x1F
 #define TWL4030_VAUX3_DEDICATED                0x22
 
 #define TWL4030_VAUX3_DEV_GRP          0x1F
 #define TWL4030_VAUX3_DEDICATED                0x22
 
-
 #if defined(CONFIG_TWL4030_BCI_BATTERY) || \
        defined(CONFIG_TWL4030_BCI_BATTERY_MODULE)
        extern int twl4030charger_usb_en(int enable);
 #if defined(CONFIG_TWL4030_BCI_BATTERY) || \
        defined(CONFIG_TWL4030_BCI_BATTERY_MODULE)
        extern int twl4030charger_usb_en(int enable);
diff --git a/include/linux/netfilter_ipv4/ipt_IDLETIMER.h b/include/linux/netfilter_ipv4/ipt_IDLETIMER.h
new file mode 100644 (file)
index 0000000..89993e2
--- /dev/null
@@ -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 <ext-timo.teras@nokia.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.
+ */
+
+#ifndef _IPT_TIMER_H
+#define _IPT_TIMER_H
+
+struct ipt_idletimer_info {
+       unsigned int timeout;
+};
+
+#endif
index 96c0d93fc2cacd753926559b100c4791a621909c..850db2e805107fcf306b57bd835f4d931b22b6a8 100644 (file)
 #define UART_OMAP_MVER         0x14    /* Module version register */
 #define UART_OMAP_SYSC         0x15    /* System configuration register */
 #define UART_OMAP_SYSS         0x16    /* System status register */
 #define UART_OMAP_MVER         0x14    /* Module version register */
 #define UART_OMAP_SYSC         0x15    /* System configuration register */
 #define UART_OMAP_SYSS         0x16    /* System status register */
+#define UART_OMAP_WER          0x17    /* Wake-up enable register */
 
 #endif /* _LINUX_SERIAL_REG_H */
 
 
 #endif /* _LINUX_SERIAL_REG_H */
 
index 05eab2f11e6323b9817e24a53b9f2c7180184106..e284a1f1b5ad5efc6f16f03888a98d96afccc06c 100644 (file)
@@ -51,5 +51,10 @@ struct ads7846_platform_data {
                                 void **filter_data);
        int     (*filter)       (void *filter_data, int data_idx, int *val);
        void    (*filter_cleanup)(void *filter_data);
                                 void **filter_data);
        int     (*filter)       (void *filter_data, int data_idx, int *val);
        void    (*filter_cleanup)(void *filter_data);
+
+       /* controls enabling/disabling*/
+       int     (*vaux_control)(int vaux_cntrl);
+#define VAUX_ENABLE    1
+#define VAUX_DISABLE   0
 };
 
 };
 
diff --git a/include/linux/spi/tsc2005.h b/include/linux/spi/tsc2005.h
new file mode 100644 (file)
index 0000000..dbc01f7
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef _LINUX_SPI_TSC2005_H
+#define _LINUX_SPI_TSC2005_H
+
+#include <linux/types.h>
+
+struct tsc2005_platform_data {
+       s16     reset_gpio;
+       s16     dav_gpio;
+       s16     pen_int_gpio;
+       u16     ts_x_plate_ohm;
+       u32     ts_stab_time;   /* voltage settling time */
+       u8      ts_hw_avg;      /* HW assiseted averaging. Can be
+                                  0, 4, 8, 16 samples per reading */
+       u32     ts_touch_pressure;      /* Pressure limit until we report a
+                                          touch event. After that we switch
+                                          to ts_max_pressure. */
+       u32     ts_pressure_max;/* Samples with bigger pressure value will
+                                  be ignored, since the corresponding X, Y
+                                  values are unreliable */
+       u32     ts_pressure_fudge;
+       u32     ts_x_max;
+       u32     ts_x_fudge;
+       u32     ts_y_max;
+       u32     ts_y_fudge;
+
+       unsigned ts_ignore_last : 1;
+};
+
+#endif
diff --git a/include/linux/spi/tsc210x.h b/include/linux/spi/tsc210x.h
new file mode 100644 (file)
index 0000000..820bc9c
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * include/linux/spi/tsc210x.h
+ *
+ * TI TSC2101/2102 control register definitions.
+ *
+ * Copyright (c) 2005-2007 Andrzej Zaborowski  <balrog@zabor.org>
+ *
+ * This package 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 package 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 package; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef __LINUX_SPI_TSC210X_H
+#define __LINUX_SPI_TSC210X_H
+
+struct apm_power_info;
+
+struct tsc210x_config {
+       int use_internal;       /* Use internal reference voltage */
+       u32 monitor;            /* What inputs are wired on this board */
+       int temp_at25c[2];      /* Thermometer calibration data */
+       void (*apm_report)(struct apm_power_info *info, int battery[]);
+                               /* Report status to APM based on battery[] */
+       void *alsa_config;      /* .platform_data for the ALSA device */
+       const char *mclk;       /* Optional: mclk name */
+       const char *bclk;       /* Optional: bclk name */
+};
+
+#define TSC_BAT1       (1 << 0)
+#define TSC_BAT2       (1 << 1)
+#define TSC_AUX1       (1 << 2)
+#define TSC_AUX2       (1 << 3)
+#define TSC_TEMP       (1 << 4)
+
+#define TSC_AUX                TSC_AUX1
+#define TSC_VBAT       TSC_BAT1
+
+struct tsc210x_dev;
+
+/* Drivers for tsc210x components like touchscreen, sensor, and audio
+ * are packaged as platform drivers which can issue synchronous register
+ * acceses, and may also register a callback to process their particular
+ * type of data when that data is automatically sampled.  The platform
+ * device is a child of the TSC spi device.
+ */
+
+extern int tsc210x_read_sync(struct tsc210x_dev *dev, int page, u8 address);
+extern int tsc210x_reads_sync(struct tsc210x_dev *dev, int page,
+               u8 startaddress, u16 *data, int numregs);
+extern int tsc210x_write_sync(struct tsc210x_dev *dev, int page,
+               u8 address, u16 data);
+
+typedef void (*tsc210x_touch_t)(void *context, int touching);
+typedef void (*tsc210x_coords_t)(void *context, int x, int y, int z1, int z2);
+typedef void (*tsc210x_ports_t)(void *context, int bat[], int aux[]);
+typedef void (*tsc210x_temp_t)(void *context, int temp);
+
+extern int tsc210x_touch_cb(struct device *dev,
+               tsc210x_touch_t handler, void *context);
+extern int tsc210x_coords_cb(struct device *dev,
+               tsc210x_coords_t handler, void *context);
+extern int tsc210x_ports_cb(struct device *dev,
+               tsc210x_ports_t handler, void *context);
+extern int tsc210x_temp1_cb(struct device *dev,
+               tsc210x_temp_t handler, void *context);
+extern int tsc210x_temp2_cb(struct device *dev,
+               tsc210x_temp_t handler, void *context);
+
+#if defined(CONFIG_SOUND) || defined(CONFIG_SOUND_MODULE)
+extern void tsc210x_set_dac_volume(struct device *dev, u8 left, u8 right);
+extern void tsc210x_set_dac_mute(struct device *dev, int left, int right);
+extern void tsc210x_get_dac_mute(struct device *dev, int *left, int *right);
+extern void tsc210x_dac_power(struct device *dev, int on);
+extern int tsc210x_set_rate(struct device *dev, int rate);
+extern void tsc210x_set_i2s_master(struct device *dev, int state);
+extern void tsc210x_set_deemphasis(struct device *dev, int enable);
+extern void tsc2102_set_bassboost(struct device *dev, int enable);
+#endif
+
+/*
+ * Emit a short keyclick typically in order to give feedback to
+ * user on specific events.
+ *
+ * amplitude must be between 0 (lowest) and 2 (highest).
+ * freq must be between 0 (corresponds to 62.5 Hz) and 7 (8 kHz).
+ * length should be between 2 and 32 periods.
+ *
+ * This function sleeps but for a period unrelated to the length of
+ * the sound, i.e. returning doesn't indicate that the sound has
+ * finished.
+ */
+extern void tsc210x_keyclick(struct tsc210x_dev *dev,
+               int amplitude, int freq, int length);
+
+/* Page 0, Touch Screen & Keypad Data registers */
+#define TSC210X_TS_X                   0, 0x00
+#define TSC210X_TS_Y                   0, 0x01
+#define TSC210X_TS_Z1                  0, 0x02
+#define TSC210X_TS_Z2                  0, 0x03
+#define TSC210X_TS_BAT1                        0, 0x05
+#define TSC2102_TS_BAT2                        0, 0x06
+#define TSC210X_TS_AUX1                        0, 0x07
+#define TSC2101_TS_AUX2                        0, 0x08
+#define TSC210X_TS_TEMP1               0, 0x09
+#define TSC210X_TS_TEMP2               0, 0x0a
+
+/* Page 1, Touch Screen & Keypad Control registers */
+#define TSC210X_TS_ADC_CTRL            1, 0x00
+#define TSC210X_TS_STATUS_CTRL         1, 0x01
+#define TSC2101_TS_BUFFER_CTRL         1, 0x02
+#define TSC210X_TS_REF_CTRL            1, 0x03
+#define TSC210X_TS_RESET_CTRL          1, 0x04
+#define TSC210X_TS_CONFIG_CTRL         1, 0x05
+#define TSC2101_TS_TEMPMAX_CTRL                1, 0x06
+#define TSC2101_TS_TEMPMIN_CTRL                1, 0x07
+#define TSC2101_TS_AUX1MAX_CTRL                1, 0x08
+#define TSC2101_TS_AUX1MIN_CTRL                1, 0x09
+#define TSC2101_TS_AUX2MAX_CTRL                1, 0x0a
+#define TSC2101_TS_AUX2MIN_CTRL                1, 0x0b
+#define TSC2101_TS_MCONFIG_CTRL                1, 0x0c
+#define TSC2101_TS_DELAY_CTRL          1, 0x0d
+
+/* Page 2, Audio Control registers */
+#define TSC210X_AUDIO1_CTRL            2, 0x00
+#define TSC2101_HEADSET_GAIN_CTRL      2, 0x01
+#define TSC210X_DAC_GAIN_CTRL          2, 0x02
+#define TSC2101_MIXER_GAIN_CTRL                2, 0x03
+#define TSC210X_AUDIO2_CTRL            2, 0x04
+#define TSC210X_POWER_CTRL             2, 0x05
+#define TSC210X_AUDIO3_CTRL            2, 0x06
+#define TSC210X_LCH_BASS_BOOST_N0      2, 0x07
+#define TSC210X_LCH_BASS_BOOST_N1      2, 0x08
+#define TSC210X_LCH_BASS_BOOST_N2      2, 0x09
+#define TSC210X_LCH_BASS_BOOST_N3      2, 0x0a
+#define TSC210X_LCH_BASS_BOOST_N4      2, 0x0b
+#define TSC210X_LCH_BASS_BOOST_N5      2, 0x0c
+#define TSC210X_LCH_BASS_BOOST_D1      2, 0x0d
+#define TSC210X_LCH_BASS_BOOST_D2      2, 0x0e
+#define TSC210X_LCH_BASS_BOOST_D4      2, 0x0f
+#define TSC210X_LCH_BASS_BOOST_D5      2, 0x10
+#define TSC210X_RCH_BASS_BOOST_N0      2, 0x11
+#define TSC210X_RCH_BASS_BOOST_N1      2, 0x12
+#define TSC210X_RCH_BASS_BOOST_N2      2, 0x13
+#define TSC210X_RCH_BASS_BOOST_N3      2, 0x14
+#define TSC210X_RCH_BASS_BOOST_N4      2, 0x15
+#define TSC210X_RCH_BASS_BOOST_N5      2, 0x16
+#define TSC210X_RCH_BASS_BOOST_D1      2, 0x17
+#define TSC210X_RCH_BASS_BOOST_D2      2, 0x18
+#define TSC210X_RCH_BASS_BOOST_D4      2, 0x19
+#define TSC210X_RCH_BASS_BOOST_D5      2, 0x1a
+#define TSC210X_PLL1_CTRL              2, 0x1b
+#define TSC210X_PLL2_CTRL              2, 0x1c
+#define TSC210X_AUDIO4_CTRL            2, 0x1d
+#define TSC2101_HANDSET_GAIN_CTRL      2, 0x1e
+#define TSC2101_CELL_GAIN_CTRL         2, 0x1f
+#define TSC2101_AUIDO5_CTRL            2, 0x20
+#define TSC2101_AUDIO6_CTRL            2, 0x21
+#define TSC2101_AUDIO7_CTRL            2, 0x22
+#define TSC2101_GPIO_CTRL              2, 0x23
+#define TSC2101_IN_AGC_CTRL            2, 0x24
+#define TSC2101_POWER_STATUS           2, 0x25
+#define TSC2101_MIX_AGC_CTRL           2, 0x26
+#define TSC2101_CELL_AGC_CTRL          2, 0x27
+
+/* Field masks for Audio Control 1 */
+#define AC1_WLEN(ARG)                  (((ARG) & 0x03) << 10)
+#define AC1_DATFM(ARG)                 (((ARG) & 0x03) << 8)
+#define AC1_DACFS(ARG)                 ((ARG) & 0x3f)
+
+/* Field masks for TSC2102_DAC_GAIN_CTRL */
+#define DGC_DALMU                      (1 << 15)
+#define DGC_DALVL(ARG)                 (((ARG) & 0x7f) << 8)
+#define DGC_DARMU                      (1 << 7)
+#define DGC_DARVL(ARG)                 (((ARG) & 0x7f))
+
+/* Field formats for TSC210X_AUDIO2_CTRL */
+#define AC2_KCLEN                      (1 << 15)
+#define AC2_KCLAC(ARG)                 (((ARG) & 0x07) << 12)
+#define AC2_KCLFRQ(ARG)                        (((ARG) & 0x07) << 8)
+#define AC2_KCLLN(ARG)                 (((ARG) & 0x0f) << 4)
+#define AC2_DLGAF                      (1 << 3)
+#define AC2_DRGAF                      (1 << 2)
+#define AC2_DASTC                      (1 << 1)
+
+/* Field masks for TSC210X_DAC_POWER_CTRL */
+#define CPC_PWDNC                      (1 << 15)
+#define CPC_DAODRC                     (1 << 12)
+#define CPC_DAPWDN                     (1 << 10)
+#define CPC_VGPWDN                     (1 << 8)
+#define CPC_DAPWDF                     (1 << 6)
+#define CPC_BASSBC                     (1 << 1)
+#define CPC_DEEMPF                     (0x01)
+
+/* Field masks for TSC210X_AUDIO3_CTRL */
+#define AC3_DMSVOL(ARG)                        (((ARG) & 0x03) << 14)
+#define AC3_REFFS                      (1 << 13)
+#define AC3_DAXFM                      (1 << 12)
+#define AC3_SLVMS                      (1 << 11)
+#define AC3_DALOVF                     (1 << 7)
+#define AC3_DAROVF                     (1 << 6)
+#define AC3_REVID(ARG)                 (((ARG) & 0x07))
+
+/* Field masks for TSC210X_PLL1_CTRL */
+#define PLL1_PLLEN                     (1 << 15)
+#define PLL1_Q_VAL(ARG)                        (((ARG) & 0x0f) << 11)
+#define PLL1_P_VAL(ARG)                        (((ARG) & 0x07) << 8)
+#define PLL1_I_VAL(ARG)                        (((ARG) & 0x3f) << 2)
+
+/* Field masks for TSC210X_PLL2_CTRL */
+#define PLL2_D_VAL(ARG)                        (((ARG) & 0x3fff) << 2)
+
+/* Field masks for TSC210X_AUDIO4_CTRL */
+#define AC4_DASTPD                     (1 << 14)
+
+struct tsc210x_rate_info_s {
+       u16 sample_rate;
+       u8 divisor;
+       u8 fs_44k;      /* 44.1 kHz Fsref if 1, 48 kHz if 0 */
+};
+
+#endif /* __LINUX_SPI_TSC210X_H */
diff --git a/include/linux/spi/tsc2301.h b/include/linux/spi/tsc2301.h
new file mode 100644 (file)
index 0000000..62e5862
--- /dev/null
@@ -0,0 +1,197 @@
+#ifndef _LINUX_SPI_TSC2301_H
+#define _LINUX_SPI_TSC2301_H
+
+#include <linux/types.h>
+#include <linux/timer.h>
+
+struct tsc2301_platform_data {
+       /*
+        * Keypad
+        */
+       s16     reset_gpio;
+       s16     keyb_int;
+       s16     keymap[16];     /* Set a key to a negative value if not used */
+       unsigned kp_rep:1;      /* Enable keypad repeating */
+       char    *keyb_name;     /* Keyboard device name */
+
+       /*
+        * Touchscreen
+        */
+       s16     dav_int;
+       u16     ts_x_plate_ohm;
+       u32     ts_stab_time;   /* voltage settling time */
+       u8      ts_hw_avg;      /* HW assiseted averaging. Can be
+                                  0, 4, 8, 16 samples per reading */
+       u32     ts_max_pressure;/* Samples with bigger pressure value will
+                                  be ignored, since the corresponding X, Y
+                                  values are unreliable */
+       u32     ts_touch_pressure;      /* Pressure limit until we report a
+                                          touch event. After that we switch
+                                          to ts_max_pressure. */
+       u32     ts_pressure_fudge;
+       u32     ts_x_max;
+       u32     ts_x_fudge;
+       u32     ts_y_max;
+       u32     ts_y_fudge;
+
+       /*
+        * Audio
+        */
+       unsigned        pll_pdc:4;
+       unsigned        pll_a:4;
+       unsigned        pll_n:4;
+       unsigned        pll_output:1; /* Output PLL on GPIO_0 */
+
+       unsigned        mclk_ratio:2;
+       unsigned        i2s_sample_rate:4;
+       unsigned        i2s_format:2;
+       /* Mask for audio blocks to be powered down */
+       u16             power_down_blocks;
+
+       /* Called after codec has been initialized, can be NULL */
+       int (* codec_init)(struct device *tsc2301_dev);
+       /* Called when codec is being removed, can be NULL */
+       void (* codec_cleanup)(struct device *tsc2301_dev);
+       int     (*enable_clock)(struct device *dev);
+       void    (*disable_clock)(struct device *dev);
+
+       const struct tsc2301_mixer_gpio {
+               const char      *name;
+               unsigned        gpio:4;
+               unsigned        inverted:1;
+               unsigned        def_enable:1; /* enable by default */
+               unsigned        deactivate_on_pd:1; /* power-down flag */
+       } *mixer_gpios;
+       int     n_mixer_gpios;
+};
+
+struct tsc2301_kp;
+struct tsc2301_ts;
+struct tsc2301_mixer;
+
+struct tsc2301 {
+       struct spi_device       *spi;
+
+       s16                     reset_gpio;
+       u16                     config2_shadow;
+
+        struct tsc2301_kp      *kp;
+       struct tsc2301_ts       *ts;
+       struct tsc2301_mixer    *mixer;
+
+       int                     (*enable_clock)(struct device *dev);
+       void                    (*disable_clock)(struct device *dev);
+};
+
+
+#define TSC2301_HZ     33000000
+
+#define TSC2301_REG(page, addr)  (((page) << 11) | ((addr) << 5))
+#define TSC2301_REG_TO_PAGE(reg) (((reg) >> 11) & 0x03)
+#define TSC2301_REG_TO_ADDR(reg) (((reg) >> 5)  & 0x1f)
+
+#define TSC2301_REG_X          TSC2301_REG(0, 0)
+#define TSC2301_REG_Y          TSC2301_REG(0, 1)
+#define TSC2301_REG_Z1         TSC2301_REG(0, 2)
+#define TSC2301_REG_Z2         TSC2301_REG(0, 3)
+#define TSC2301_REG_KPDATA     TSC2301_REG(0, 4)
+#define TSC2301_REG_ADC                TSC2301_REG(1, 0)
+#define TSC2301_REG_KEY                TSC2301_REG(1, 1)
+#define TSC2301_REG_DAC                TSC2301_REG(1, 2)
+#define TSC2301_REG_REF                TSC2301_REG(1, 3)
+#define TSC2301_REG_RESET      TSC2301_REG(1, 4)
+#define TSC2301_REG_CONFIG     TSC2301_REG(1, 5)
+#define TSC2301_REG_CONFIG2    TSC2301_REG(1, 6)
+#define TSC2301_REG_KPMASK     TSC2301_REG(1, 16)
+#define TSC2301_REG_AUDCNTL    TSC2301_REG(2, 0)
+#define TSC2301_REG_ADCVOL     TSC2301_REG(2, 1)
+#define TSC2301_REG_DACVOL     TSC2301_REG(2, 2)
+#define TSC2301_REG_BPVOL      TSC2301_REG(2, 3)
+#define TSC2301_REG_KEYCTL     TSC2301_REG(2, 4)
+#define TSC2301_REG_PD_MISC    TSC2301_REG(2, 5)
+#define TSC2301_REG_GPIO       TSC2301_REG(2, 6)
+#define TSC2301_REG_ADCLKCFG   TSC2301_REG(2, 27)
+
+#define TSC2301_REG_PD_MISC_APD                (1 << 15)
+#define TSC2301_REG_PD_MISC_AVPD       (1 << 14)
+#define TSC2301_REG_PD_MISC_ABPD       (1 << 13)
+#define TSC2301_REG_PD_MISC_HAPD       (1 << 12)
+#define TSC2301_REG_PD_MISC_MOPD       (1 << 11)
+#define TSC2301_REG_PD_MISC_DAPD       (1 << 10)
+#define TSC2301_REG_PD_MISC_ADPDL      (1 << 9)
+#define TSC2301_REG_PD_MISC_ADPDR      (1 << 8)
+#define TSC2301_REG_PD_MISC_PDSTS      (1 << 7)
+#define TSC2301_REG_PD_MISC_MIBPD      (1 << 6)
+#define TSC2301_REG_PD_MISC_OTSYN      (1 << 2)
+
+/* I2S sample rate */
+#define TSC2301_I2S_SR_48000   0x00
+#define TSC2301_I2S_SR_44100   0x01
+#define TSC2301_I2S_SR_32000   0x02
+#define TSC2301_I2S_SR_24000   0x03
+#define TSC2301_I2S_SR_22050   0x04
+#define TSC2301_I2S_SR_16000   0x05
+#define TSC2301_I2S_SR_12000   0x06
+#define TSC2301_I2S_SR_11050   0x07
+#define TSC2301_I2S_SR_8000    0x08
+
+/* 16-bit, MSB-first. DAC Right-Justified, ADC Left-Justified */
+#define TSC2301_I2S_FORMAT0    0x00
+/* 20-bit, MSB-first. DAC Right-Justified, ADC Left-Justified */
+#define TSC2301_I2S_FORMAT1    0x01
+/* 20-bit, MSB-first. DAC Left-Justified, ADC Left-Justified */
+#define TSC2301_I2S_FORMAT2    0x02
+/* 20-bit, MSB-first */
+#define TSC2301_I2S_FORMAT3    0x03
+
+/* Master Clock Ratio */
+#define TSC2301_MCLK_256xFS    0x00 /* default */
+#define TSC2301_MCLK_384xFS    0x01
+#define TSC2301_MCLK_512xFS    0x02
+
+
+extern u16 tsc2301_read_reg(struct tsc2301 *tsc, int reg);
+extern void tsc2301_write_reg(struct tsc2301 *tsc, int reg, u16 val);
+extern void tsc2301_write_kbc(struct tsc2301 *tsc, int val);
+extern void tsc2301_write_pll(struct tsc2301 *tsc, int pll_n, int pll_a,
+                             int pll_pdc, int pct_e, int pll_o);
+extern void tsc2301_read_buf(struct tsc2301 *tsc, int reg, u16 *buf, int len);
+
+#define TSC2301_DECL_MOD(module)                                       \
+extern int  tsc2301_##module##_init(struct tsc2301 *tsc,               \
+                          struct tsc2301_platform_data *pdata);        \
+extern void tsc2301_##module##_exit(struct tsc2301 *tsc);              \
+extern int  tsc2301_##module##_suspend(struct tsc2301 *tsc);           \
+extern void tsc2301_##module##_resume(struct tsc2301 *tsc);
+
+#define TSC2301_DECL_EMPTY_MOD(module)                                 \
+static inline int tsc2301_##module##_init(struct tsc2301 *tsc,         \
+                          struct tsc2301_platform_data *pdata)         \
+{                                                                      \
+       return 0;                                                       \
+}                                                                      \
+static inline void tsc2301_##module##_exit(struct tsc2301 *tsc) {}     \
+static inline int  tsc2301_##module##_suspend(struct tsc2301 *tsc)     \
+{                                                                      \
+       return 0;                                                       \
+}                                                                      \
+static inline void tsc2301_##module##_resume(struct tsc2301 *tsc) {}
+
+#ifdef CONFIG_KEYBOARD_TSC2301
+TSC2301_DECL_MOD(kp)
+void tsc2301_kp_restart(struct tsc2301 *tsc);
+#else
+TSC2301_DECL_EMPTY_MOD(kp)
+static inline void tsc2301_kp_restart(struct tsc2301 *tsc) {}
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_TSC2301
+TSC2301_DECL_MOD(ts)
+#else
+TSC2301_DECL_EMPTY_MOD(ts)
+#endif
+
+extern void tsc2301_mixer_enable_mclk(struct device *tsc_dev);
+extern void tsc2301_mixer_disable_mclk(struct device *tsc_dev);
+
+#endif
index 1833bdbf9805474c729483e5abef53ffe99e120d..147c68730a5309c94c8916938b4c5fb3c5615a22 100644 (file)
@@ -202,6 +202,19 @@ config IP_NF_TARGET_REDIRECT
          come to the local machine instead of passing through.  This is
          useful for transparent proxies.
 
          come to the local machine instead of passing through.  This is
          useful for transparent proxies.
 
+         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.
+
+
          To compile it as a module, choose M here.  If unsure, say N.
 
 config NF_NAT_SNMP_BASIC
          To compile it as a module, choose M here.  If unsure, say N.
 
 config NF_NAT_SNMP_BASIC
index 48111594ee9bf4921a64a09760b3423840c79d4c..60bdaf1abc4f6f34534962b796af94913e6ae4dc 100644 (file)
@@ -60,6 +60,7 @@ obj-$(CONFIG_IP_NF_TARGET_MASQUERADE) += ipt_MASQUERADE.o
 obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
 obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
 obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
 obj-$(CONFIG_IP_NF_TARGET_NETMAP) += ipt_NETMAP.o
 obj-$(CONFIG_IP_NF_TARGET_REDIRECT) += ipt_REDIRECT.o
 obj-$(CONFIG_IP_NF_TARGET_REJECT) += ipt_REJECT.o
+obj-$(CONFIG_IP_NF_TARGET_IDLETIMER) += ipt_IDLETIMER.o
 obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o
 
 # generic ARP tables
 obj-$(CONFIG_IP_NF_TARGET_ULOG) += ipt_ULOG.o
 
 # generic ARP tables
diff --git a/net/ipv4/netfilter/ipt_IDLETIMER.c b/net/ipv4/netfilter/ipt_IDLETIMER.c
new file mode 100644 (file)
index 0000000..6432192
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * 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. All rights reserved.
+ * Written by Timo Teras <ext-timo.teras@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/notifier.h>
+#include <linux/netfilter.h>
+#include <linux/rtnetlink.h>
+#include <linux/netfilter/x_tables.h>
+#include <linux/netfilter_ipv4/ipt_IDLETIMER.h>
+#include <linux/kobject.h>
+#include <linux/workqueue.h>
+
+#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 device *, struct device_attribute *attr, char *buf);
+static ssize_t utimer_attr_store(struct device *, struct device_attribute *attr,
+                                const char *buf, size_t count);
+
+struct utimer_t {
+       char name[IFNAMSIZ];
+       struct list_head entry;
+       struct timer_list timer;
+       struct work_struct work;
+};
+
+static LIST_HEAD(active_utimer_head);
+static DEFINE_SPINLOCK(list_lock);
+static 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_work(struct work_struct *work)
+{
+       struct utimer_t *timer = container_of(work, struct utimer_t, work);
+       struct net_device *netdev;
+
+       netdev = dev_get_by_name(&init_net, timer->name);
+
+       if (netdev != NULL) {
+               sysfs_notify(&netdev->dev.kobj, NULL,
+                            "idletimer");
+               dev_put(netdev);
+       }
+}
+
+static void utimer_expired(unsigned long data)
+{
+       struct utimer_t *timer = (struct utimer_t *) data;
+
+       DEBUGP("Timer '%s' expired\n", timer->name);
+
+       spin_lock_bh(&list_lock);
+       utimer_delete(timer);
+       spin_unlock_bh(&list_lock);
+
+       schedule_work(&timer->work);
+}
+
+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;
+
+       INIT_WORK(&timer->work, utimer_work);
+
+       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 device *dev, struct device_attribute *attr, char *buf)
+{
+       struct utimer_t *timer;
+       unsigned long expires = 0;
+       struct net_device *netdev = container_of(dev, struct net_device, dev);
+
+       spin_lock_bh(&list_lock);
+       timer = __utimer_find(netdev->name);
+       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 device *dev, struct device_attribute *attr,
+                                const char *buf, size_t count)
+{
+       int expires;
+       struct net_device *netdev = container_of(dev, struct net_device, dev);
+
+       if (sscanf(buf, "%d", &expires) == 1) {
+               if (expires > 0)
+                       utimer_modify(netdev->name,
+                                     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;
+       int ret = NOTIFY_DONE;
+
+       switch (event) {
+       case NETDEV_UP:
+               DEBUGP("NETDEV_UP: %s\n", dev->name);
+               ret = device_create_file(&dev->dev,
+                                        &dev_attr_idletimer);
+               break;
+       case NETDEV_DOWN:
+               DEBUGP("NETDEV_DOWN: %s\n", dev->name);
+               device_remove_file(&dev->dev,
+                                        &dev_attr_idletimer);
+               break;
+       }
+
+       return ret;
+}
+
+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_each_netdev(&init_net, dev)
+               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 struct xt_target *xttarget,
+                                        const void *targinfo)
+{
+       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 XT_CONTINUE;
+}
+
+static bool ipt_idletimer_checkentry(const char *tablename,
+                                   const void *e,
+                                   const struct xt_target *target,
+                                   void *targinfo,
+                                   unsigned int hookmask)
+{
+       struct ipt_idletimer_info *info =
+               (struct ipt_idletimer_info *) targinfo;
+
+       if (info->timeout == 0) {
+               DEBUGP("timeout value is zero\n");
+               return 0;
+       }
+
+       return true;
+}
+
+static struct xt_target ipt_idletimer = {
+       .name           = "IDLETIMER",
+       .target         = ipt_idletimer_target,
+       .checkentry     = ipt_idletimer_checkentry,
+       .me             = THIS_MODULE,
+       .targetsize     = sizeof(struct ipt_idletimer_info),
+};
+
+static int __init init(void)
+{
+       int ret;
+
+       ret = utimer_init();
+       if (ret)
+               return ret;
+
+       if (xt_register_target(&ipt_idletimer)) {
+               utimer_fini();
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void __exit fini(void)
+{
+       xt_unregister_target(&ipt_idletimer);
+       utimer_fini();
+}
+
+module_init(init);
+module_exit(fini);
+
+MODULE_AUTHOR("Timo Teras <ext-timo.teras@nokia.com>");
+MODULE_DESCRIPTION("iptables idletimer target module");
+MODULE_LICENSE("GPL");