--- /dev/null
+
+ 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>
--- /dev/null
+
+ 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>
source "drivers/staging/Kconfig"
+if ARCH_OMAP
+source "drivers/cbus/Kconfig"
+endif
+
endmenu
source "fs/Kconfig"
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
--- /dev/null
+/*
+ * 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
__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
*/
struct tag_acorn acorn;
+ /*
+ * OMAP specific
+ */
+ struct tag_omap omap;
+
/*
* DC21285 specific
*/
.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.
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.
# 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
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;
return ret;
}
-static void mmc_shutdown(struct device *dev)
+static void mmc_cleanup(struct device *dev)
{
gpio_free(H2_TPS_GPIO_MMC_PWR_EN);
}
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,
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;
}
#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>
.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,
&nand_device,
&smc91x_device,
&intlat_device,
- &h3_irda_device,
&h3_kp_device,
&h3_lcd_device,
};
#include <mach/omapfb.h>
#include <mach/lcd_mipid.h>
#include <mach/mmc.h>
+#include <mach/usb.h>
#define ADS7846_PENDOWN_GPIO 15
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;
}
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"
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
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
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
+
+
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
#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 */
{
},
};
+static struct platform_device sdp2430_lcd_device = {
+ .name = "sdp2430_lcd",
+ .id = -1,
+};
+
static struct platform_device sdp2430_smc91x_device = {
.name = "smc91x",
.id = -1,
.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)
.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},
};
.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[] = {
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)
--- /dev/null
+/*
+ * 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");
+ }
+}
#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>
#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,
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
*
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,
};
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();
.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.
.gpio = &sdp3430_gpio_data,
.madc = &sdp3430_madc_data,
.keypad = &sdp3430_kp_data,
+ .power = &sdp3430_t2scripts_data,
.usb = &sdp3430_usb_data,
.vaux1 = &sdp3430_vaux1,
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)
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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 */
/*
* 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",
.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;
}
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[] = {
.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,
static struct platform_device *apollon_devices[] __initdata = {
&apollon_onenand_device,
+ &apollon_nor_device,
&apollon_smc91x_device,
&apollon_lcd_device,
&apollon_led_device,
static inline void __init apollon_init_smc91x(void)
{
unsigned long base;
-
unsigned int rate;
struct clk *gpmc_fck;
int eth_cs;
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;
}
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);
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),
};
.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 },
};
{
/* 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)
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.
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)
.enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
};
-static struct omap_board_config_kernel generic_config[] = {
+static struct omap_board_config_kernel generic_config[] __initdata = {
{ OMAP_TAG_UART, &generic_uart_config },
};
omap_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)
#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 };
.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)
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)
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,
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
}
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 = {
#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,
}
#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)
#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] = {
},
};
+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)
gpio_direction_input(eth_gpio);
}
+
static void __init omap_ldp_init_irq(void)
{
omap2_init_common_hw(NULL);
.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 = {
.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[] = {
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)
--- /dev/null
+/*
+ * 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();
+}
+
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
#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
.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,
{} /* 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,
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
*/
.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[] = {
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",
};
gpio_direction_output(170, true);
usb_musb_init();
+ usb_ehci_init();
omap3beagle_flash_init();
}
--- /dev/null
+/*
+ * 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");
+ }
+}
+
--- /dev/null
+/*
+ * 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
*
*/
+#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,
static void __init omap3pandora_init_irq(void)
{
- omap2_init_common_hw(NULL);
+ omap2_init_common_hw(mt46h32m32lf6_sdrc_params);
omap_init_irq();
omap_gpio_init();
}
omap_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)
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/i2c/twl4030.h>
+#include <linux/regulator/machine.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <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
#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)
static inline void __init overo_init_smsc911x(void) { return; }
#endif
+
+
static struct mtd_partition overo_nand_partitions[] = {
{
.name = "xloader",
.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,
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();
}
&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();
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();
--- /dev/null
+/*
+ * 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();
+}
+
#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,
.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),
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,
};
};
/* 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,
},
};
+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",
.vaux1 = &rx51_vaux1,
.vaux2 = &rx51_vaux2,
- .vaux3 = &rx51_vaux3,
.vaux4 = &rx51_vaux4,
.vmmc1 = &rx51_vmmc1,
- .vmmc2 = &rx51_vmmc2,
.vsim = &rx51_vsim,
.vdac = &rx51_vdac,
};
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);
{
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();
}
--- /dev/null
+/*
+ * 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];
+}
+
--- /dev/null
+/*
+ * 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();
+}
+
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
#include <mach/hardware.h>
#include <asm/mach-types.h>
#include <asm/mach/map.h>
#include <mach/mcspi.h>
+#include <mach/gpio.h>
#include <mach/mux.h>
#include <mach/board.h>
#include <mach/common.h>
#include <mach/dma.h>
#include <mach/gpmc.h>
#include <mach/usb.h>
+#include <mach/board-rx51.h>
static struct omap_uart_config rx51_uart_config = {
.enabled_uarts = ((1 << 0) | (1 << 1) | (1 << 2)),
static 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)
{
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)
#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)
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
}
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[] = {
{
}
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
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);
#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;
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)
{
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;
}
}
#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)
{
/*
* 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()) {
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);
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);
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;
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)
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;
} 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);
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))
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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;
+ }
+}
+
+
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) {
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)
{
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;
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
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
--- /dev/null
+/*
+ * 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);
#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;
--- /dev/null
+/*
+ * 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");
* 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));
}
{
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));
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);
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",
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,
{ .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 = {
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);
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++;
}
--- /dev/null
+/*
+ * 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");
/* 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
}
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
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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 */
+
#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 {
u8 channel;
};
-struct omap_camera_sensor_config {
- u16 reset_gpio;
- int (*power_on)(void * data);
- int (*power_off)(void * data);
-};
-
struct omap_usb_config {
/* Configure drivers according to the connectors on your board:
* - "A" connector (rectagular)
struct omap_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 {
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];
};
#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
+++ /dev/null
-/*
- * 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 */
* 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;
};
/* 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
+++ /dev/null
-/*
- * 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 */
#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)
#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>
int rows;
int cols;
int *keymap;
+ int irq;
unsigned int keymapsize;
unsigned int rep:1;
unsigned long delay;
#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
/* use the internal clock */
unsigned internal_clock:1;
- s16 power_pin;
int switch_pin; /* gpio (card detect) */
int gpio_wp; /* gpio (write protect) */
--- /dev/null
+#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 */
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);
* 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)
#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
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)
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,
return 0;
}
#else
-static inline int omap34xx_sram_init(void)
+static inline int omap3_sram_init(void)
{
return 0;
}
else if (cpu_is_omap2430())
omap243x_sram_init();
else if (cpu_is_omap34xx())
- omap34xx_sram_init();
+ omap3_sram_init();
return 0;
}
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/
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/
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
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
--- /dev/null
+/*
+ * 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>");
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#
+# 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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
.name = "omap_rng",
.owner = THIS_MODULE,
},
- .probe = omap_rng_probe,
.remove = __exit_p(omap_rng_remove),
.suspend = omap_rng_suspend,
.resume = omap_rng_resume
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)
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
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
--- /dev/null
+/*
+ * 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");
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
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
--- /dev/null
+/*
+ * 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)
+
--- /dev/null
+/*
+ * 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");
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,
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--;
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--;
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
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
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
-
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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,
+ ®val, 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,
+ ®val, 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");
+
--- /dev/null
+/*
+ * 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");
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
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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
+
#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
unsigned int cols;
unsigned long delay;
unsigned int debounce;
+ int suspended;
+ spinlock_t suspend_lock;
};
static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
static 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()) {
#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
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");
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())
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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, ®, 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");
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
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
--- /dev/null
+/**
+ * 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>");
+
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
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
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",
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
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"
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.
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
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
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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/* 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");
--- /dev/null
+/* 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");
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
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
#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) */
{ 3, TWL4030_BASEADD_SECURED_REG },
};
+extern void twl4030_power_init(struct twl4030_power_data *triton2_scripts);
+
/*----------------------------------------------------------------------*/
/* Exported Functions */
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);
/* 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
--- /dev/null
+/*
+ * 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");
+}
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
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/
--- /dev/null
+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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
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,
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;
}
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);
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)
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)) {
*
* This function needs to be visible for bootloaders.
*/
-static int mtdpart_setup(char *s)
+int mtdpart_setup(char *s)
{
cmdline = s;
return 1;
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 )
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
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
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
obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o
obj-$(CONFIG_MTD_NAND_SPIA) += spia.o
obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o
+obj-$(CONFIG_MTD_NAND_TOTO) += toto.o
obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
obj-$(CONFIG_MTD_NAND_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
--- /dev/null
+/*
+ * 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);
+
--- /dev/null
+/*
+ * 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");
+
--- /dev/null
+/*
+ * 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");
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
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
--- /dev/null
+/*
+ * 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");
+
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)) {
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
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
--- /dev/null
+/*
+ * 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);
+
DEBUG_INTR("end.\n");
+#ifdef CONFIG_ARCH_OMAP15XX
+ return IRQ_HANDLED; /* FIXME: iir status not ready on 1510 */
+#else
return IRQ_RETVAL(handled);
+#endif
}
/*
/* 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);
#
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
# 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)
/* 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 {
} 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);
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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");
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
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.
objs-y$(CONFIG_ARCH_OMAP1) += lcdc.o
objs-y$(CONFIG_ARCH_OMAP2) += dispc.o
+objs-y$(CONFIG_ARCH_OMAP3) += dispc.o
objs-$(CONFIG_ARCH_OMAP1)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += sossi.o
objs-$(CONFIG_ARCH_OMAP2)$(CONFIG_FB_OMAP_LCDC_EXTERNAL) += rfbi.o
objs-y$(CONFIG_FB_OMAP_LCDC_HWA742) += hwa742.o
objs-y$(CONFIG_FB_OMAP_LCDC_BLIZZARD) += blizzard.o
+objs-y$(CONFIG_MACH_AMS_DELTA) += lcd_ams_delta.o
objs-y$(CONFIG_MACH_OMAP_H4) += lcd_h4.o
objs-y$(CONFIG_MACH_OMAP_H3) += lcd_h3.o
objs-y$(CONFIG_MACH_OMAP_PALMTE) += lcd_palmte.o
objs-$(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)
#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
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;
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,
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;
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];
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
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)
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) |
.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,
unsigned long *map;
};
+#define MAX_IRQ_HANDLERS 4
+
static struct {
void __iomem *base;
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];
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;
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);
}
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);
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);
}
}
- 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);
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)
if ((r = get_dss_clocks()) < 0)
goto fail0;
- enable_interface_clocks(1);
enable_lcd_clocks(1);
#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
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);
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) {
free_irq(INT_24XX_DSS_IRQ, fbdev);
fail1:
enable_lcd_clocks(0);
- enable_interface_clocks(0);
put_dss_clocks();
fail0:
iounmap(dispc.base);
cleanup_fbmem();
free_palette_ram();
free_irq(INT_24XX_DSS_IRQ, dispc.fbdev);
- enable_interface_clocks(0);
put_dss_clocks();
iounmap(dispc.base);
}
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;
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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");
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
+
{ 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" },
};
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;
}
/*
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;
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));
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;
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++;
{
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;
}
{
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;
}
* 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
#define DISPC_BASE 0x48050400
#define DISPC_CONTROL 0x0040
+#define DISPC_IRQ_FRAMEMASK 0x0001
static struct {
void __iomem *base;
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;
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);
}
* 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
arch
+asm-offsets.h
mach-types.h
--- /dev/null
+/*
+ *
+ * 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 */
#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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
#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
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;
};
enum twl4030_usb_mode usb_mode;
};
+struct twl4030_ins {
+ u16 pmb_message;
+ u8 delay;
+};
+
+struct twl4030_script {
+ struct twl4030_ins *script;
+ unsigned size;
+ u8 flags;
+#define TRITON_WRST_SCRIPT (1<<0)
+#define TRITON_WAKEUP12_SCRIPT (1<<1)
+#define TRITON_WAKEUP3_SCRIPT (1<<2)
+#define TRITON_SLEEP_SCRIPT (1<<3)
+};
+
+struct twl4030_resconfig {
+ u8 resource;
+ u8 devgroup;
+ u8 type;
+ u8 type2;
+};
+
+struct twl4030_power_data {
+ struct twl4030_script **scripts;
+ unsigned size;
+ const struct twl4030_resconfig *resource_config;
+};
+
struct twl4030_platform_data {
unsigned irq_base, irq_end;
struct twl4030_bci_platform_data *bci;
struct twl4030_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;
#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);
--- /dev/null
+/*
+ * 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
#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 */
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
};
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#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
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
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
--- /dev/null
+/*
+ * 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");