]> pilppa.com 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 [changed mode: 0644->0755]
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"
 
+if ARCH_OMAP
+source "drivers/cbus/Kconfig"
+endif
+
 endmenu
 
 source "fs/Kconfig"
index fbe5eef1f6c9c031b2763e1ba9bb734de941f91b..98d589a59b58ad2f513eb9420f19ac2e6585f53d 100644 (file)
@@ -40,6 +40,10 @@ ifeq ($(CONFIG_PXA_SHARPSL),y)
 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
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;
 };
 
+/* 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
 
@@ -161,6 +168,11 @@ struct tag {
                 */
                struct tag_acorn        acorn;
 
+               /*
+                * OMAP specific
+                 */
+                struct tag_omap         omap;
+
                /*
                 * DC21285 specific
                 */
index 21e17dc94cb56757f6f25f05b61ba4c2d6e095ca..1bc918cbdb189989878981ae375657b31f0e9b8e 100644 (file)
@@ -282,7 +282,7 @@ __create_page_tables:
        .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.
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
-#      select GPIOEXPANDER_OMAP
        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
 
+# DSP
+obj-$(CONFIG_OMAP_MBOX_FWK)    += mailbox_mach.o
+mailbox_mach-objs              := mailbox.o
+
 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)
 {
-       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)
 {
-       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;
 
@@ -47,7 +41,7 @@ static int mmc_late_init(struct device *dev)
        return ret;
 }
 
-static void mmc_shutdown(struct device *dev)
+static void mmc_cleanup(struct device *dev)
 {
        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,
-       .shutdown                       = mmc_shutdown,
+       .cleanup                        = mmc_cleanup,
        .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)
 {
-       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;
 }
 
index 4695965114c4e67e86456439fa4435149e0cf374..f597968733b4f2baa980419519edade5e0203961 100644 (file)
 #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/irda.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,
 };
 
-
-/* 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,
@@ -395,7 +295,6 @@ static struct platform_device *devices[] __initdata = {
        &nand_device,
         &smc91x_device,
        &intlat_device,
-       &h3_irda_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/usb.h>
 
 #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)
 {
-       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;
 }
 
index 64ab386a65c775ff7553d24b65ab8c4928d64210..3a2214cb37aba18fb5f4964223d79693902b1029 100644 (file)
@@ -10,10 +10,13 @@ config ARCH_OMAP2420
        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
+       select ARCH_OMAP_OTG
+       select CPU_V6
 
 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
+       select CPU_V7
 
 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
 
+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"
-       depends on ARCH_OMAP2 && ARCH_OMAP24XX
+       depends on ARCH_OMAP2 && ARCH_OMAP2420
        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"
-       depends on ARCH_OMAP2 && ARCH_OMAP24XX
+       depends on ARCH_OMAP2 && ARCH_OMAP2420
 
 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
 
-config MACH_OMAP3_BEAGLE
-       bool "OMAP3 BEAGLE board"
+config MACH_OMAP_3430SDP
+       bool "OMAP 3430 SDP board"
        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
index c49d9bfa3abde7694e261115e40424f586f1152f..76acefa0c765384648fb44e29797a8c6d50be087 100644 (file)
@@ -28,28 +28,59 @@ endif
 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 \
+                                          board-2430sdp-flash.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 \
+                                          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
-
+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 \
+                                          board-n800-flash.o \
+                                          board-rx51-flash.o \
+                                          board-rx51-sdram.o \
+                                          board-rx51-video.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
-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
+
+
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/input.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/gpio.h>
 #include <mach/mux.h>
 #include <mach/board.h>
+#include <mach/usb.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
-
 #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 */
        {
@@ -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,
@@ -119,9 +139,125 @@ static struct platform_device sdp2430_smc91x_device = {
        .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,
+       &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)
@@ -199,8 +335,16 @@ static struct omap_uart_config sdp2430_uart_config __initdata = {
        .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_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,
 };
 
+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,
+       .madc           = &sdp2430_madc_data,
+       .keypad         = &sdp2430_kp_data,
+       .usb            = &sdp2430_usb_data,
 };
 
 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();
-       twl4030_mmc_init(mmc);
+
+       msecure_init();
+
+       sdp2430_flash_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)
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/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/gpio.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
@@ -30,6 +30,7 @@
 #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/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
 
+#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 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
 
+extern void sdp3430_flash_init(void);
+
 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 __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
  *
@@ -141,9 +184,42 @@ 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;
+
+       /* 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,
+       .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)
 {
-       omap2_init_common_hw(NULL);
+       omap2_init_common_hw(hyb18m512160af6_sdrc_params);
        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,
 };
 
+
+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.
@@ -472,6 +629,7 @@ static struct twl4030_platform_data sdp3430_twldata = {
        .gpio           = &sdp3430_gpio_data,
        .madc           = &sdp3430_madc_data,
        .keypad         = &sdp3430_kp_data,
+       .power          = &sdp3430_t2scripts_data,
        .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();
+       sdp3430_flash_init();
+       msecure_init();
        omap_serial_init();
        usb_musb_init();
+       usb_ehci_init();
 }
 
 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
  *
- * Copyright (C) 2005,2006 Samsung Electronics
+ * Copyright (C) 2005-2008 Samsung Electronics
  * 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.
 #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/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/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
-#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_NOR_CS         3
 #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",
@@ -108,6 +123,63 @@ static struct platform_device apollon_onenand_device = {
        .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;
@@ -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;
+
+       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[] = {
@@ -143,34 +222,37 @@ static struct platform_device apollon_lcd_device = {
        .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 = {
-       .name           = "omap-led",
+       .name           = "leds-gpio",
        .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,
+       &apollon_nor_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;
-
        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);
        }
 
-       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;
        }
@@ -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);
-               gpmc_cs_free(APOLLON_ETH_CS);
+               gpmc_cs_free(eth_cs);
                goto out;
        }
        gpio_direction_input(APOLLON_ETHR_GPIO_IRQ);
@@ -256,6 +338,24 @@ static void __init omap_apollon_init_irq(void)
        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),
 };
@@ -271,7 +371,7 @@ static struct omap_lcd_config apollon_lcd_config __initdata = {
        .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 },
 };
@@ -280,16 +380,18 @@ static void __init apollon_led_init(void)
 {
        /* 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);
-       gpio_request(LED1_GPIO14, "LED1");
-       gpio_direction_output(LED1_GPIO14, 0);
        /* 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)
@@ -302,21 +404,101 @@ static void __init apollon_usb_init(void)
        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();
-
-       /* 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.
@@ -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_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)
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)),
 };
 
-static struct omap_board_config_kernel generic_config[] = {
+static struct omap_board_config_kernel generic_config[] __initdata = {
        { 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_register_i2c_bus(1, 100, NULL, 0);
+       omap_register_i2c_bus(2, 100, NULL, 0);
 }
 
 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/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/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 <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/menelaus.h>
 #include <mach/dma.h>
 #include <mach/gpmc.h>
 
 
 #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 };
 
@@ -138,31 +186,16 @@ static struct platform_device h4_flash_device = {
        .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)
 {
-       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)
@@ -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;
-       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)
@@ -199,6 +219,10 @@ static int h4_transceiver_mode(struct device *dev, int mode)
 
        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,
@@ -269,6 +293,8 @@ static u32 get_sysboot_value(void)
                 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
@@ -372,7 +398,11 @@ static void __init omap_h4_init_irq(void)
 }
 
 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)),
+#endif
 };
 
 static struct omap_lcd_config h4_lcd_config __initdata = {
@@ -412,21 +442,309 @@ static struct omap_usb_config h4_usb_config __initdata = {
 #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 },
 };
 
+#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[] = {
+       {       /* 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),
        },
+#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,
@@ -457,14 +775,54 @@ static void __init omap_h4_init(void)
        }
 #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);
-       omap_usb_init(&h4_usb_config);
        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)
index da57b0fcda14c5c2ca0ac92e1da076f7176763ee..804fd8e7e974d0eeb40d492a15db89a7f1585147 100644 (file)
 #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/io.h>
 #include <linux/smsc911x.h>
 
 #include <mach/hardware.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 <mach/usb.h>
 
 #include "mmc-twl4030.h"
 
 #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] = {
@@ -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,
+       &ldp_lcd_device,
+       &ldp_gpio_keys_device,
 };
 
 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);
 }
 
+
 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)),
 };
 
+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 },
+       { 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 = {
@@ -132,12 +480,21 @@ static struct twl4030_gpio_platform_data ldp_gpio_data = {
        .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 */
+       .bci            = &ldp_bci_data,
+       .madc           = &ldp_madc_data,
+       .usb            = &ldp_usb_data,
+       .power          = &sdp3430_t2scripts_data,
        .gpio           = &ldp_gpio_data,
+       .keypad         = &ldp_kp_twl4030_data,
 };
 
 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);
+       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();
-       twl4030_mmc_init(mmc);
        usb_musb_init();
+       twl4030_mmc_init(mmc);
 }
 
 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/regulator/machine.h>
 #include <linux/i2c/twl4030.h>
 
 #include <mach/hardware.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/usb.h>
 
+#include "twl4030-generic-scripts.h"
 #include "mmc-twl4030.h"
 
+
 #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,
 };
 
+#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 twl4030_usb_data beagle_usb_data = {
+       .usb_mode       = T2_USB_MODE_ULPI,
+};
+
 static struct twl4030_hsmmc_info mmc[] = {
        {
                .mmc            = 1,
@@ -117,6 +126,14 @@ static struct twl4030_hsmmc_info mmc[] = {
        {}      /* 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,
@@ -127,6 +144,10 @@ static int beagle_twl_gpio_setup(struct device *dev,
        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
         */
@@ -155,12 +176,114 @@ static struct twl4030_gpio_platform_data beagle_gpio_data = {
        .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 */
+       .usb            = &beagle_usb_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[] = {
@@ -184,16 +307,11 @@ static int __init omap3_beagle_i2c_init(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();
 }
 
-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",
 };
@@ -315,6 +433,7 @@ static void __init omap3_beagle_init(void)
        gpio_direction_output(170, true);
 
        usb_musb_init();
+       usb_ehci_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/io.h>
 #include <linux/kernel.h>
 #include <linux/platform_device.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/flash.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/mcspi.h>
+#include <mach/nand.h>
 #include <mach/usb.h>
+#include <mach/mcspi.h>
 
+#include "sdram-micron-mt46h32m32lf-6.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
 
+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,
@@ -118,7 +220,7 @@ static int __init omap3pandora_i2c_init(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();
 }
@@ -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));
-       omap3pandora_ads7846_init();
        usb_musb_init();
+       usb_ehci_init();
+       omap3pandora_flash_init();
+       omap3pandora_ads7846_init();
 }
 
 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/regulator/machine.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
@@ -45,6 +46,8 @@
 #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
@@ -57,8 +60,8 @@
 #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)
@@ -180,6 +183,8 @@ static inline void __init overo_init_smsc911x(void)
 static inline void __init overo_init_smsc911x(void) { return; }
 #endif
 
+
+
 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)),
 };
 
+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,
+       .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,
+       .usb            = &overo_usb_data,
+       .power          = GENERIC3430_T2SCRIPTS_DATA,
+       .vmmc1          = &overo_vmmc1,
 };
 
 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,
@@ -303,7 +364,7 @@ static int __init overo_i2c_init(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();
 }
@@ -326,23 +387,6 @@ static struct platform_device *overo_devices[] __initdata = {
        &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();
@@ -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();
-       twl4030_mmc_init(mmc);
-       overo_flash_init();
        usb_musb_init();
+       usb_ehci_init();
+       overo_flash_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/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/gpio.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/keypad.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 RX51_TSC2005_RESET_GPIO                104
+#define RX51_TSC2005_IRQ_GPIO          100
+
 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,
 };
 
+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),
@@ -199,6 +236,18 @@ free1:
        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,
 };
@@ -259,7 +308,7 @@ static struct regulator_init_data rx51_vaux2 = {
 };
 
 /* 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,
@@ -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",
@@ -382,10 +447,8 @@ static struct twl4030_platform_data rx51_twldata = {
 
        .vaux1                  = &rx51_vaux1,
        .vaux2                  = &rx51_vaux2,
-       .vaux3                  = &rx51_vaux3,
        .vaux4                  = &rx51_vaux4,
        .vmmc1                  = &rx51_vmmc1,
-       .vmmc2                  = &rx51_vmmc2,
        .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)
 {
+       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);
@@ -413,7 +483,10 @@ void __init rx51_peripherals_init(void)
 {
        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_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/gpio.h>
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
@@ -24,6 +23,7 @@
 #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>
@@ -31,6 +31,7 @@
 #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)),
@@ -62,12 +63,14 @@ static struct omap_board_config_kernel rx51_config[] = {
 
 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();
 }
 
+extern void __init rx51_flash_init(void);
 extern void __init rx51_peripherals_init(void);
+extern void __init rx51_video_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();
+       rx51_flash_init();
        rx51_peripherals_init();
+       rx51_video_init();
 }
 
 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/eac.h>
 #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_mcspi3);
+       if (cpu_is_omap2430() || cpu_is_omap343x())
+               platform_device_register(&omap2_mcspi3);
 #endif
 #ifdef CONFIG_ARCH_OMAP3
-       platform_device_register(&omap2_mcspi4);
+       if (cpu_is_omap343x())
+               platform_device_register(&omap2_mcspi4);
 #endif
 }
 
@@ -366,38 +367,6 @@ static void omap_init_mcspi(void)
 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[] = {
        {
index 34b5914e0f8b18d094d45a31e9ce166af215b410..32ceaa6582408e36e96ba968e0cbe116d15a220f 100644 (file)
@@ -45,6 +45,28 @@ int omap_chip_is(struct omap_chip_id oci)
 }
 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
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);
-       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);
index dc40b3e72206080164b1c1f5b17def1f4d211688..46d6e9bcadfa5c495a9c9ab026b0a260bf922cb1 100644 (file)
@@ -16,8 +16,8 @@
 #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 "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;
@@ -59,19 +37,16 @@ static u16 control_devconf1_offset;
 
 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)
 {
@@ -117,16 +92,60 @@ static int twl_mmc_late_init(struct device *dev)
        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) {
+                       struct regulator *reg;
+
                        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;
                }
        }
@@ -173,96 +192,6 @@ static int twl_mmc_resume(struct device *dev, int slot)
 #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)
 {
@@ -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
-        * 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.
+        *
+        * FIXME handle VMMC1A as needed ...
         */
        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);
 
-               ret = twl_mmc_set_voltage(c, vdd);
+               ret = mmc_regulator_set_ocr(c->vcc, vdd);
 
                /* 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);
 
-               ret = twl_mmc_set_voltage(c, 0);
+               ret = mmc_regulator_set_ocr(c->vcc, 0);
 
                /* 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;
 }
 
-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;
 
+       /* 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.
+        *
+        * 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) {
+               /* only MMC2 supports a CLKIN */
                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);
                }
-               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 {
-               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;
 }
 
-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)
@@ -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
-                       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->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)) {
-                       mmc->init = twl_mmc_late_init;
                        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;
 
-               /* 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:
+                       /* on-chip level shifting via PBIAS0/PBIAS1 */
                        mmc->slots[0].set_power = twl_mmc1_set_power;
-                       mmc->slots[0].ocr_mask = MMC1_OCR;
                        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:
-                       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);
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     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))
 
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)
 {
-       if (cpu_is_omap243x())
+       if (cpu_is_omap243x()) {
                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].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) {
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);
 }
 
-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)
 {
@@ -187,7 +185,7 @@ int tusb6010_platform_retime(unsigned is_refclk)
        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;
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.
 
+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
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_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
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
 
-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;
 
+#ifdef CONFIG_OMAP_BOOT_TAG
+
+static int __init parse_tag_omap(const struct tag *tag)
+{
+       u32 size = tag->hdr.size - (sizeof(tag->hdr) >> 2);
+
+        size <<= 2;
+       if (size > sizeof(omap_bootloader_tag))
+               return -1;
+
+       memcpy(omap_bootloader_tag, tag->u.omap.data, size);
+       omap_bootloader_tag_len = size;
+
+        return 0;
+}
+
+__tagtable(ATAG_BOARD, parse_tag_omap);
+
+#endif
+
 static const void *get_config(u16 tag, size_t len, int skip, size_t *len_out)
 {
        struct omap_board_config_kernel *kinfo = NULL;
diff --git a/arch/arm/plat-omap/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");
old mode 100644 (file)
new mode 100755 (executable)
index 21cc014..6093018
@@ -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));
-       } else if (cpu_is_omap7xx() || cpu_is_omap15xx()) {
+       } else if (cpu_is_omap730() || cpu_is_omap15xx()) {
                dma_write(dev_id, CCR(free_ch));
        }
 
@@ -760,19 +760,12 @@ void omap_free_dma(int lch)
 {
        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);
-               spin_unlock_irqrestore(&dma_chan_lock, flags);
                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));
@@ -798,6 +791,12 @@ void omap_free_dma(int 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);
 
@@ -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;
-       } 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",
@@ -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);
 
+       /* 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,
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 = 0x48304000, .irq = INT_24XX_GPTIMER12 },
+       { .phys_base = 0x48304000, .irq = INT_34XX_GPT12_IRQ },
 };
 
 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 */
 
        /*
-        * 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);
 
index ce6b4baeedec416aa48d64f17872e799079cd328..3746222bed10ff52f6002556c21cfca486e2babf 100644 (file)
@@ -206,9 +206,10 @@ void __init omapfb_reserve_sdram(void)
                        config_invalid = 1;
                        return;
                }
-               if (rg.paddr)
+               if (rg.paddr) {
                        reserve_bootmem(rg.paddr, rg.size, BOOTMEM_DEFAULT);
-               reserved += rg.size;
+                       reserved += rg.size;
+               }
                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)
+       reg = bank->base + OMAP24XX_GPIO_IRQSTATUS2;
        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
 }
 
@@ -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);
-               if (enable) {
+               if (enable)
                        bank->suspend_wakeup |= (1 << gpio);
-                       enable_irq_wake(bank->irq);
-               } else {
-                       disable_irq_wake(bank->irq);
+               else
                        bank->suspend_wakeup &= ~(1 << gpio);
-               }
                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);
-               if (enable) {
+               if (enable)
                        bank->suspend_wakeup |= (1 << gpio);
-                       enable_irq_wake(bank->irq);
-               } else {
-                       disable_irq_wake(bank->irq);
+               else
                        bank->suspend_wakeup &= ~(1 << gpio);
-               }
                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_PARTITION      0x4f0b
+#define OMAP_TAG_TEA5761       0x4f10
+#define OMAP_TAG_TMP105                0x4f11
 
 #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 {
@@ -43,12 +46,6 @@ struct omap_sti_console_config {
        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)
@@ -108,9 +105,9 @@ struct omap_pwm_led_platform_data {
 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 {
@@ -118,8 +115,25 @@ struct omap_uart_config {
        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];
 };
 
index 54fe9665b1826f674b761685d443f54c48567864..224b0770ec2dcd41e461aadf49df797731153f30 100644 (file)
 #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
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
  *
  */
-#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;
@@ -48,7 +49,11 @@ struct omap_gpio_switch {
 };
 
 /* Call at init time only */
+#ifdef CONFIG_OMAP_GPIO_SWITCH
 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
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
 
+/*
+ * 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_CS0_BASE           0x60
+#define GPMC_CS_SIZE            0x30
+
 
 #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_BENCH_MPU_EMUL 3
-
 /* 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);
+extern int omap_irq_pending(void);
 #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 irq;
        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 ROWCOL_MASK            0xFF000000
+#define KEY_PERSISTENT         0x00800000
+#define KEYNUM_MASK            0x00EFFFFF
 #define KEY(col, row, val) (((col) << 28) | ((row) << 24) | (val))
+#define PERSISTENT_KEY(col, row) (((col) << 28) | ((row) << 24) | \
+                                               KEY_PERSISTENT)
 
 #endif
 
index 4435bd434e179b995adfbec2944ac24436aa6119..81d5b36534b340680ffc36fde156e9cafbb70e2d 100644 (file)
@@ -79,7 +79,6 @@ struct omap_mmc_platform_data {
 
                /* use the internal clock */
                unsigned internal_clock:1;
-               s16 power_pin;
 
                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)
 {
 }
-#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);
 
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
  */
-#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 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
@@ -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)
-{
+ {
        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)
@@ -378,7 +378,7 @@ void restore_sram_functions(void)
                               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,
@@ -387,7 +387,7 @@ int __init omap34xx_sram_init(void)
        return 0;
 }
 #else
-static inline int omap34xx_sram_init(void)
+static inline int omap3_sram_init(void)
 {
        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())
-               omap34xx_sram_init();
+               omap3_sram_init();
 
        return 0;
 }
index 1ba9e471ca456c335d5eccb31ca55ca6d60d799d..625f69d8ecdc7f618e96e06efd0eba2edaaf03a3 100644 (file)
@@ -36,9 +36,13 @@ obj-$(CONFIG_FB_INTEL)          += video/intelfb/
 
 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                          += i2c/
 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_SERIO)            += input/serio/
 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/
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).
 
+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
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_HCIBRF6150)    += brf6150.o
+obj-$(CONFIG_BT_HCIH4P)                += hci_h4p/
 
 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,
        },
-       .probe          = omap_rng_probe,
        .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;
 
-       return platform_driver_register(&omap_rng_driver);
+       return platform_driver_probe(&omap_rng_driver, omap_rng_probe);
 }
 
 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).
 
+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
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_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
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.
 
+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
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_TSC210X)  += tsc210x_sensors.o
 obj-$(CONFIG_SENSORS_W83L786NG)        += w83l786ng.o
+obj-$(CONFIG_SENSORS_OMAP34XX)  += omap34xx_temp.o
 
 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
+       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,
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
-                                       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--;
@@ -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
-                                       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--;
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.
 
+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
@@ -89,4 +114,24 @@ config SENSORS_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
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_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
-
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.
 
+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
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_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
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/spinlock.h>
 #include <linux/errno.h>
+#include <linux/i2c/menelaus.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/mach-types.h>
 #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;
+       int suspended;
+       spinlock_t suspend_lock;
 };
 
 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;
+       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()) {
@@ -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)
 {
-       /* 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)
 {
-       /* Nothing yet */
+       struct omap_kp *omap_kp = platform_get_drvdata(dev);
 
+       omap_kp->suspended = 0;
        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;
-       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");
@@ -301,7 +327,9 @@ static int __devinit omap_kp_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, omap_kp);
 
+       spin_lock_init(&omap_kp->suspend_lock);
        omap_kp->input = input_dev;
+       omap_kp->suspended = 0;
 
        /* 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.
 
+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
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_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
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.
 
+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
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_TSC2007)      += tsc2007.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_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
index 056ac77e2cf0ed6fe5624d9f62f35bbab2d57697..c5ecce1842fea53539e828c2bfd67d9fc5583cc3 100644 (file)
@@ -878,6 +878,15 @@ static int __devinit ads7846_probe(struct spi_device *spi)
                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",
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.
 
-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
-         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"
@@ -90,7 +105,7 @@ config LEDS_COBALT_QUBE
 
 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.
@@ -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.
 
+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
@@ -193,13 +218,6 @@ config LEDS_PCA955X
          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
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_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_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
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.
 
+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
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_TWL4030_POWER)    += twl4030-power.o
 
 obj-$(CONFIG_MFD_CORE)         += mfd-core.o
 
index ec90e953adced9f0d2b3f3d4abe878d6b3511da9..769b34bd48e445880ac0920423d9b73eabaf4cb7 100644 (file)
 #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
 #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) */
 
@@ -225,6 +237,8 @@ static struct twl4030mapping twl4030_map[TWL4030_MODULE_LAST + 1] = {
        { 3, TWL4030_BASEADD_SECURED_REG },
 };
 
+extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
+
 /*----------------------------------------------------------------------*/
 
 /* Exported Functions */
@@ -526,6 +540,13 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)
                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);
@@ -776,6 +797,10 @@ twl4030_probe(struct i2c_client *client, const struct i2c_device_id *id)
        /* 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
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.
 
+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
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_OMAP_STI)         += sti/
 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"
-       depends on ARCH_OMAP
+       depends on ARCH_OMAP1 || (ARCH_OMAP2 && ARCH_OMAP2420)
        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,
index 5570849188cc806032cab4791ab3ce10706a51f2..bfa25c01c8722f17684520b1ab3adfee9bdef193 100644 (file)
@@ -157,8 +157,6 @@ struct mmc_omap_host {
        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;
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_READ(host->base, STAT); /* flush posted write */
 
        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->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)
@@ -1107,13 +1107,14 @@ static int __init omap_mmc_probe(struct platform_device *pdev)
                goto err_irq;
        }
 
+       /* initialize power supplies, gpios, etc */
        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;
                }
        }
+       mmc->ocr_avail = mmc_slot(host).ocr_mask;
 
        /* 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.
  */
-static int mtdpart_setup(char *s)
+int mtdpart_setup(char *s)
 {
        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'.
 
+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 )
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_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
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);
+       struct flash_platform_data *pdata = pdev->dev.platform_data;
 
        platform_set_drvdata(pdev, NULL);
 
        if (info) {
-               if (info->parts) {
+               if (info->parts || (pdata && pdata->parts)) {
                        del_mtd_partitions(info->mtd);
                        kfree(info->parts);
                } else
index 890936d0275ed21b957e2a46a9af52764a1fe28b..80b691b67b5ef49819c0105760baf40895b3368e 100644 (file)
@@ -74,6 +74,31 @@ config MTD_NAND_AMS_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
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_TOTO)            += toto.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_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
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.
 
+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
 
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_OMAP_IR)          += omap-ir.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);
 
+       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)) {
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.
 
+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
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_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
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");
 
+#ifdef CONFIG_ARCH_OMAP15XX
+       return IRQ_HANDLED;     /* FIXME: iir status not ready on 1510 */
+#else
        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);
                }
+
+               /* 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);
index 83a185d52961e5075cd5330de7f770fc4eb28e79..8bc6d1147217ca74a8b02795bb661494ebdcf4e9 100644 (file)
@@ -230,6 +230,47 @@ config SPI_XILINX
 #
 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
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
+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)
index d6d0c5d241ce1bcbcd9b68bc15caacbccb5f5be8..f2b4d329e1aa5f1c9e496597cd052da2c09e56f0 100644 (file)
 
 /* 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_EPOL                (1 << 6)
+#define OMAP2_MCSPI_CHCONF_EPOL                BIT(6)
 #define OMAP2_MCSPI_CHCONF_WL_MASK     (0x1f << 7)
-#define OMAP2_MCSPI_CHCONF_TRM_RX_ONLY (0x01 << 12)
-#define OMAP2_MCSPI_CHCONF_TRM_TX_ONLY (0x02 << 12)
+#define OMAP2_MCSPI_CHCONF_TRM_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_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 {
@@ -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,
-                       /* (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);
 
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.
 
+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
@@ -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.
 
-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_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
@@ -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_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
@@ -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-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)
 
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_PANEL_CONFIGURATION           0x28
 #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                     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;
@@ -513,6 +518,12 @@ static int do_full_screen_update(struct blizzard_request *req)
        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,
@@ -525,6 +536,7 @@ static int do_partial_update(struct blizzard_request *req, int plane,
        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;
@@ -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;
        }
-       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];
@@ -653,8 +661,49 @@ static int do_partial_update(struct blizzard_request *req, int plane,
        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);
+       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
@@ -908,6 +957,35 @@ static int blizzard_set_scale(int plane, int orig_w, int orig_h,
        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)
@@ -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 |
-               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) |
@@ -1560,6 +1639,7 @@ struct lcd_ctrl blizzard_ctrl = {
        .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,
index dfb72f5e4c962ad35098fb07a65252b99be31941..c140c214223c64545ba9f6d303776ea24301428f 100644 (file)
@@ -155,6 +155,8 @@ struct resmap {
        unsigned long   *map;
 };
 
+#define MAX_IRQ_HANDLERS            4
+
 static struct {
        void __iomem    *base;
 
@@ -167,9 +169,11 @@ static struct {
 
        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];
@@ -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]);
-       l &= FLD_MASK(0, 9);
+       l &= FLD_MASK(0, 11);
        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;
        }
-       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);
 }
 
@@ -519,8 +523,7 @@ static int omap_dispc_set_scale(int plane,
        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);
@@ -809,57 +812,74 @@ static void set_lcd_timings(void)
        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);
-       dispc.enabled_irqs = irq_mask;
-       irq_mask |= DISPC_IRQ_MASK_ERROR;
        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)
 {
-       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);
 
@@ -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);
 
+       enable_lcd_clocks(0);
+
        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);
        }
 
-       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_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);
@@ -911,18 +940,13 @@ static void put_dss_clocks(void)
 
 static void enable_lcd_clocks(int enable)
 {
-       if (enable)
+       if (enable) {
+               clk_enable(dispc.dss_ick);
                clk_enable(dispc.dss1_fck);
-       else
+       } else {
                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);
+       }
 }
 
 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;
 
-       enable_interface_clocks(1);
        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 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 |= (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);
 
@@ -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(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) {
@@ -1467,7 +1489,6 @@ fail2:
        free_irq(INT_24XX_DSS_IRQ, fbdev);
 fail1:
        enable_lcd_clocks(0);
-       enable_interface_clocks(0);
        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);
-       enable_interface_clocks(0);
        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 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;
 
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_ROTATE,  "rotate window" },
        { 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);
-       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);
+       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;
-       line_size = var->xres * bpp / 8;
 
        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);
-       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));
@@ -705,28 +714,42 @@ int omapfb_update_window_async(struct fb_info *fbi,
                                void (*callback)(void *),
                                void *callback_data)
 {
+       int xres, yres;
        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;
 
-       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;
 
@@ -1695,8 +1718,8 @@ static int omapfb_do_probe(struct platform_device *pdev,
 
        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++;
 
@@ -1818,8 +1841,8 @@ static int omapfb_suspend(struct platform_device *pdev, pm_message_t mesg)
 {
        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;
 }
 
@@ -1828,7 +1851,8 @@ static int omapfb_resume(struct platform_device *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;
 }
 
index a13c8dcad2a8429c1a5da98f489a60b0da46ff51..29fa368f5221e2bfc97cbe0110958bd8cf52631f 100644 (file)
@@ -2,7 +2,7 @@
  * 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
@@ -57,6 +57,7 @@
 
 #define DISPC_BASE             0x48050400
 #define DISPC_CONTROL          0x0040
+#define DISPC_IRQ_FRAMEMASK     0x0001
 
 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);
 
-       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;
@@ -568,7 +570,7 @@ static int rfbi_init(struct omapfb_device *fbdev)
 
 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);
 }
index a7694622024925befb1f26692523937146dd3588..cc697ccd82953bdcdaa83ff287a96ef0dc9e685c 100644 (file)
@@ -2,7 +2,7 @@
  * 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
index e02c15d158fc978d72e0b0647c87391a446af76b..e1bf4707aa7a244cc030486ff02a9be15221482d 100644 (file)
@@ -1,2 +1,3 @@
 arch
+asm-offsets.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_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 */
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
 
+/* 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
@@ -302,12 +333,19 @@ struct twl4030_madc_platform_data {
        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 {
-       int rows;
-       int cols;
-       int *keymap;
-       int irq;
-       unsigned int keymapsize;
+       unsigned rows;
+       unsigned cols;
+       unsigned *keymap;
+       unsigned short keymapsize;
        unsigned int rep:1;
 };
 
@@ -320,6 +358,34 @@ struct twl4030_usb_data {
        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;
@@ -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_power_data               *power;
 
        /* 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
 
-
 #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_WER          0x17    /* Wake-up enable register */
 
 #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);
+
+       /* 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.
 
+         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
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_IDLETIMER) += ipt_IDLETIMER.o
 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");