summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--AndroidKernel.mk104
-rw-r--r--Makefile10
-rw-r--r--arch/arm/boot/dts/Makefile1
-rw-r--r--arch/arm/boot/dts/omap3-minnow.dts225
-rw-r--r--arch/arm/configs/.gitignore12
-rw-r--r--arch/arm/configs/ext_config/eng_bld.config43
-rw-r--r--arch/arm/configs/ext_config/lttng.config31
-rw-r--r--arch/arm/configs/minnow_defconfig2483
-rw-r--r--arch/arm/include/uapi/asm/setup.h8
-rw-r--r--arch/arm/kernel/atags.h2
-rw-r--r--arch/arm/kernel/atags_parse.c23
-rw-r--r--arch/arm/kernel/setup.c11
-rw-r--r--arch/arm/mach-omap2/Kconfig6
-rw-r--r--arch/arm/mach-omap2/Makefile5
-rw-r--r--arch/arm/mach-omap2/board-minnow-cpcap-client.c50
-rw-r--r--arch/arm/mach-omap2/board-minnow-sensors.c90
-rw-r--r--arch/arm/mach-omap2/board-minnow-spi.c491
-rw-r--r--arch/arm/mach-omap2/board-minnow-wireless.c104
-rw-r--r--arch/arm/mach-omap2/board-minnow.c69
-rw-r--r--arch/arm/mach-omap2/board-minnow.h22
-rw-r--r--arch/arm/mach-omap2/dss-common.c26
-rw-r--r--arch/arm/mach-omap2/dss-common.h2
-rw-r--r--arch/arm/mach-omap2/sdram-toshiba-hynix-numonyx.h66
-rw-r--r--arch/arm/tools/mach-types1
-rw-r--r--defconfig.mk31
-rw-r--r--drivers/dma/of-dma.c2
-rw-r--r--drivers/input/touchscreen/Kconfig8
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/atmxt.c3786
-rw-r--r--drivers/input/touchscreen/atmxt.h186
-rw-r--r--drivers/leds/Kconfig6
-rw-r--r--drivers/leds/Makefile1
-rw-r--r--drivers/leds/als.h14
-rw-r--r--drivers/leds/leds-lm3535.c1470
-rw-r--r--drivers/mfd/Kconfig19
-rw-r--r--drivers/mfd/Makefile19
-rw-r--r--drivers/mfd/cpcap-adc.c708
-rw-r--r--drivers/mfd/cpcap-core.c577
-rw-r--r--drivers/mfd/cpcap-irq.c809
-rw-r--r--drivers/mfd/cpcap-key.c146
-rw-r--r--drivers/mfd/cpcap-regacc.c393
-rw-r--r--drivers/mfd/cpcap-uc.c927
-rw-r--r--drivers/mfd/cpcap-usb-det.c948
-rw-r--r--drivers/mfd/m4sensorhub-core.c569
-rw-r--r--drivers/mfd/m4sensorhub-irq.c675
-rw-r--r--drivers/mfd/m4sensorhub-panic.c248
-rw-r--r--drivers/mfd/m4sensorhub-reg.c384
-rw-r--r--drivers/mfd/m4sensorhub-reg.h188
-rw-r--r--drivers/mfd/m4sensorhub-stm32-fw.c419
-rw-r--r--drivers/mfd/tps65912-core.c11
-rw-r--r--drivers/mfd/tps65912-debugfs.c213
-rw-r--r--drivers/misc/Kconfig4
-rw-r--r--drivers/misc/Makefile12
-rw-r--r--drivers/misc/m4sensorhub_audio.c496
-rw-r--r--drivers/misc/m4sensorhub_bmp180.c453
-rw-r--r--drivers/misc/m4sensorhub_display.c558
-rw-r--r--drivers/misc/m4sensorhub_download.c501
-rw-r--r--drivers/misc/m4sensorhub_gesture.c415
-rw-r--r--drivers/misc/m4sensorhub_mpu9150.c1243
-rw-r--r--drivers/misc/m4sensorhub_passive.c325
-rw-r--r--drivers/misc/m4sensorhub_pedometer.c689
-rw-r--r--drivers/misc/m4sensorhub_stillmode.c425
-rw-r--r--drivers/misc/m4sensorhub_tmp006.c432
-rw-r--r--drivers/misc/m4sensorhub_wrist.c836
-rw-r--r--drivers/misc/m4sensorhub_wrist.h106
-rw-r--r--drivers/misc/vib-gpio.c224
-rw-r--r--drivers/mmc/host/omap_hsmmc.c29
-rw-r--r--drivers/of/base.c4
-rw-r--r--drivers/pinctrl/pinconf.c4
-rw-r--r--drivers/power/Kconfig12
-rw-r--r--drivers/power/Makefile4
-rw-r--r--drivers/power/cpcap-battery.c823
-rw-r--r--drivers/power/cpcap-charger.c76
-rw-r--r--drivers/power/cpcap-factory.c81
-rw-r--r--drivers/power/cpcap-usb-charger.c81
-rw-r--r--drivers/regulator/Kconfig6
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/cpcap-regulator.c605
-rw-r--r--drivers/usb/gadget/f_usbnet.c839
-rw-r--r--drivers/usb/gadget/gadget_chips.h11
-rw-r--r--drivers/usb/phy/Kconfig6
-rw-r--r--drivers/usb/phy/Makefile1
-rw-r--r--drivers/usb/phy/cpcap-usb.c211
-rw-r--r--drivers/video/omap2/displays/Kconfig7
-rw-r--r--drivers/video/omap2/displays/Makefile1
-rw-r--r--drivers/video/omap2/displays/panel-minnow.c1761
-rw-r--r--drivers/video/omap2/dss/dsi.c26
-rw-r--r--drivers/video/omap2/omapfb/omapfb-ioctl.c16
-rw-r--r--drivers/video/omap2/omapfb/omapfb-main.c10
-rw-r--r--firmware/Makefile1
-rw-r--r--firmware/cpcap/firmware_0_2x.HEX104
-rw-r--r--firmware/cpcap/firmware_1_2x.H163
-rw-r--r--include/linux/input/touch_platform.h63
-rw-r--r--include/linux/m4sensorhub.h200
-rw-r--r--include/linux/m4sensorhub/MemMapAccelSensor.h29
-rw-r--r--include/linux/m4sensorhub/MemMapAudio.h29
-rw-r--r--include/linux/m4sensorhub/MemMapCompassSensor.h30
-rw-r--r--include/linux/m4sensorhub/MemMapDownload.h51
-rw-r--r--include/linux/m4sensorhub/MemMapFusionSensor.h37
-rw-r--r--include/linux/m4sensorhub/MemMapGesture.h46
-rw-r--r--include/linux/m4sensorhub/MemMapGyroSensor.h29
-rw-r--r--include/linux/m4sensorhub/MemMapLog.h49
-rw-r--r--include/linux/m4sensorhub/MemMapPassive.h26
-rw-r--r--include/linux/m4sensorhub/MemMapPedometer.h34
-rw-r--r--include/linux/m4sensorhub/MemMapPressureSensor.h29
-rw-r--r--include/linux/m4sensorhub/MemMapTempSensor.h26
-rw-r--r--include/linux/m4sensorhub/m4sensorhub_bank_enum.h51
-rw-r--r--include/linux/m4sensorhub/m4sensorhub_irqs.h78
-rw-r--r--include/linux/m4sensorhub/m4sensorhub_reg_enum.h160
-rw-r--r--include/linux/m4sensorhub/m4sensorhub_registers.h33
-rw-r--r--include/linux/m4sensorhub_client_ioctl.h151
-rw-r--r--include/linux/m4sensorhub_gpio.h28
-rw-r--r--include/linux/mfd/tps65912.h3
-rw-r--r--include/linux/mod_devicetable.h6
-rw-r--r--include/linux/spi/cpcap-regbits.h957
-rw-r--r--include/linux/spi/cpcap.h862
-rw-r--r--include/linux/vib-gpio.h39
-rw-r--r--include/linux/wl127x-rfkill.h47
-rw-r--r--include/uapi/linux/fb.h1
-rw-r--r--include/uapi/linux/input.h72
-rw-r--r--lib/Kconfig.debug9
-rw-r--r--net/bluetooth/hidp/core.c2
-rw-r--r--net/rfkill/Kconfig9
-rw-r--r--net/rfkill/Makefile1
-rw-r--r--net/rfkill/wl127x-rfkill.c288
-rw-r--r--net/wireless/Kconfig9
-rw-r--r--scripts/pre-commit20
127 files changed, 31549 insertions, 40 deletions
diff --git a/AndroidKernel.mk b/AndroidKernel.mk
new file mode 100644
index 00000000000..58aedd48cbb
--- /dev/null
+++ b/AndroidKernel.mk
@@ -0,0 +1,104 @@
+#Android makefile to build kernel as a part of Android Build
+ifneq ($(BUILD_KERNEL),)
+
+KERNEL_SRCDIR := kernel/omap-moto-cw
+KERNEL_OUT := $(ANDROID_PRODUCT_OUT)/obj/KERNEL_OBJ
+KERNEL_CONFIG := $(KERNEL_OUT)/.config
+TARGET_PREBUILT_INT_KERNEL := $(KERNEL_OUT)/arch/arm/boot/zImage
+TARGET_PREBUILT_INT_DTB := $(KERNEL_OUT)/arch/arm/boot/dts/omap3-minnow.dtb
+KERNEL_HEADERS_INSTALL := $(KERNEL_OUT)/usr
+KERNEL_MODULES_INSTALL := system
+KERNEL_MODULES_OUT := $(TARGET_OUT)/lib/modules
+KERNEL_IMG=$(KERNEL_OUT)/arch/arm/boot/Image
+# relative path from KERNEL_OUT to kernel source directory
+KERNEL_SOURCE_RELATIVE_PATH := ../../../../../../$(KERNEL_SRCDIR)
+PRODUCT_PREBUILT_KERNEL := $(TARGET_PREBUILT_KERNEL)
+
+ifeq ($(TARGET_USES_UNCOMPRESSED_KERNEL),true)
+$(info Using uncompressed kernel)
+TARGET_PREBUILT_KERNEL := $(KERNEL_OUT)/piggy
+else
+TARGET_PREBUILT_KERNEL := $(TARGET_PREBUILT_INT_KERNEL)
+endif
+TARGET_PREBUILT_DTB := $(TARGET_PREBUILT_INT_DTB)
+
+define mv-modules
+mdpath=`find $(KERNEL_MODULES_OUT) -type f -name modules.order`;\
+if [ "$$mdpath" != "" ];then\
+mpath=`dirname $$mdpath`;\
+ko=`find $$mpath/kernel -type f -name *.ko`;\
+for i in $$ko; do mv $$i $(KERNEL_MODULES_OUT)/; done;\
+fi
+endef
+
+define clean-module-folder
+mdpath=`find $(KERNEL_MODULES_OUT) -type f -name modules.order`;\
+if [ "$$mdpath" != "" ];then\
+mpath=`dirname $$mdpath`; rm -rf $$mpath;\
+fi
+endef
+
+define update-prebuilts
+if [ -f $(TARGET_PREBUILT_INT_KERNEL) -a\
+ -f $(PRODUCT_PREBUILT_KERNEL) ]; then\
+ cp -f $(TARGET_PREBUILT_INT_KERNEL) $(PRODUCT_PREBUILT_KERNEL);\
+fi;\
+pdir=$(PRODUCT_PREBUILT_KERNEL);\
+pdir=$${pdir%$$(basename $$pdir)};\
+if [ -f $(TARGET_PREBUILT_INT_DTB) -a\
+ -d $$pdir ]; then\
+ cp -f $(TARGET_PREBUILT_INT_DTB) $$pdir;\
+fi
+endef
+
+include $(KERNEL_SRCDIR)/defconfig.mk
+
+define do-kernel-config
+ ( cp $(3) $(2) && $(7) -C $(4) O=$(1) ARCH=$(5) CROSS_COMPILE=$(6) oldconfig ) || ( rm -f $(2) && false )
+endef
+
+GIT_HOOKS_DIR := $(KERNEL_SRCDIR)/.git/hooks
+inst_hook: $(GIT_HOOKS_DIR)/pre-commit $(GIT_HOOKS_DIR)/checkpatch.pl
+
+$(GIT_HOOKS_DIR)/pre-commit: $(KERNEL_SRCDIR)/scripts/pre-commit
+ @-cp -f $< $@
+ @-chmod ugo+x $@
+
+$(GIT_HOOKS_DIR)/checkpatch.pl: $(KERNEL_SRCDIR)/scripts/checkpatch.pl
+ @-cp -f $< $@
+ @-chmod ugo+x $@
+
+$(KERNEL_OUT):
+ mkdir -p $(KERNEL_OUT)
+
+$(KERNEL_CONFIG): $(KERNEL_OUT) $(TARGET_DEFCONFIG) inst_hook
+ $(call do-kernel-config,$(KERNEL_OUT),$@,$(TARGET_DEFCONFIG),$(KERNEL_SRCDIR),arm,arm-eabi-,$(MAKE))
+
+$(KERNEL_OUT)/piggy : $(TARGET_PREBUILT_INT_KERNEL)
+ $(hide) gunzip -c $(KERNEL_OUT)/arch/arm/boot/compressed/piggy.gzip > $(KERNEL_OUT)/piggy
+
+$(TARGET_PREBUILT_INT_DTB): $(KERNEL_OUT) $(KERNEL_CONFIG) $(KERNEL_HEADERS_INSTALL)
+ $(MAKE) -C $(KERNEL_SRCDIR) KBUILD_RELSRC=$(KERNEL_SOURCE_RELATIVE_PATH) O=$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- dtbs
+
+$(TARGET_PREBUILT_INT_KERNEL): $(KERNEL_OUT) $(KERNEL_CONFIG) $(KERNEL_HEADERS_INSTALL) $(TARGET_PREBUILT_INT_DTB)
+ $(MAKE) -C $(KERNEL_SRCDIR) KBUILD_RELSRC=$(KERNEL_SOURCE_RELATIVE_PATH) O=$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi-
+ $(MAKE) -C $(KERNEL_SRCDIR) KBUILD_RELSRC=$(KERNEL_SOURCE_RELATIVE_PATH) O=$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- modules
+ $(MAKE) -C $(KERNEL_SRCDIR) KBUILD_RELSRC=$(KERNEL_SOURCE_RELATIVE_PATH) O=$(KERNEL_OUT) INSTALL_MOD_PATH=../../$(KERNEL_MODULES_INSTALL) INSTALL_MOD_STRIP="--strip-debug --remove-section=.note.gnu.build-id" ARCH=arm CROSS_COMPILE=arm-eabi- modules_install
+ $(update-prebuilts)
+ $(mv-modules)
+ $(clean-module-folder)
+
+$(KERNEL_HEADERS_INSTALL): $(KERNEL_OUT) $(KERNEL_CONFIG)
+ $(MAKE) -C $(KERNEL_SRCDIR) O=$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- headers_install
+
+kerneltags: $(KERNEL_OUT) $(KERNEL_CONFIG)
+ $(MAKE) -C $(KERNEL_SRCDIR) O=$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- tags
+
+kernelconfig: $(KERNEL_OUT) $(KERNEL_CONFIG)
+ env KCONFIG_NOTIMESTAMP=true \
+ $(MAKE) -C $(KERNEL_SRCDIR) O=$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- menuconfig
+ env KCONFIG_NOTIMESTAMP=true \
+ $(MAKE) -C $(KERNEL_SRCDIR) O=$(KERNEL_OUT) ARCH=arm CROSS_COMPILE=arm-eabi- savedefconfig
+ cp $(KERNEL_OUT)/defconfig kernel/arch/arm/configs/$(KERNEL_DEFCONFIG)
+
+endif
diff --git a/Makefile b/Makefile
index e5e3ba08519..dc8b8475cb1 100644
--- a/Makefile
+++ b/Makefile
@@ -369,7 +369,7 @@ LINUXINCLUDE := \
KBUILD_CPPFLAGS := -D__KERNEL__
-KBUILD_CFLAGS := -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
+KBUILD_CFLAGS := -Werror -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs \
-fno-strict-aliasing -fno-common \
-Werror-implicit-function-declaration \
-Wno-format-security \
@@ -1400,8 +1400,12 @@ quiet_cmd_rmfiles = $(if $(wildcard $(rm-files)),CLEAN $(wildcard $(rm-files))
# Run depmod only if we have System.map and depmod is executable
quiet_cmd_depmod = DEPMOD $(KERNELRELEASE)
- cmd_depmod = $(CONFIG_SHELL) $(srctree)/scripts/depmod.sh $(DEPMOD) \
- $(KERNELRELEASE) "$(patsubst y,_,$(CONFIG_HAVE_UNDERSCORE_SYMBOL_PREFIX))"
+ cmd_depmod = \
+ if [ -r System.map -a -x $(DEPMOD) ]; then \
+ $(DEPMOD) -ae -F System.map \
+ $(if $(strip $(INSTALL_MOD_PATH)), -b $(INSTALL_MOD_PATH) ) \
+ $(KERNELRELEASE); \
+ fi
# Create temporary dir for module support files
# clean it up only when building all modules
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index b83cc5083a2..19a8faba463 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -153,6 +153,7 @@ dtb-$(CONFIG_ARCH_OMAP2PLUS) += omap2420-h4.dtb \
am335x-evm.dtb \
am335x-evmsk.dtb \
am335x-bone.dtb
+dtb-$(CONFIG_MACH_MINNOW) += omap3-minnow.dtb
dtb-$(CONFIG_ARCH_ORION5X) += orion5x-lacie-ethernet-disk-mini-v2.dtb
dtb-$(CONFIG_ARCH_PRIMA2) += prima2-evb.dtb
dtb-$(CONFIG_ARCH_U8500) += snowball.dtb \
diff --git a/arch/arm/boot/dts/omap3-minnow.dts b/arch/arm/boot/dts/omap3-minnow.dts
new file mode 100644
index 00000000000..e88a2749286
--- /dev/null
+++ b/arch/arm/boot/dts/omap3-minnow.dts
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2013 Motorola Mobility LLC
+ *
+ * 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.
+ */
+/dts-v1/;
+
+/include/ "omap36xx.dtsi"
+
+/ {
+ model = "Motorola OMAP3 Platform";
+ compatible = "mot,omap3-minnow", "ti,omap3";
+
+ Display@0 {
+ compatible = "mot,minnow-panel-dsi-cm";
+ /* 0: MINNOW_PANEL_CM_220X176
+ * 1: MINNOW_PANEL_CM_220X220
+ */
+ id_panel = <1>;
+ gpio_reset = <&gpio1 14 0>; /* RESET gpio-14 */
+ /* declare it if ext_te enable */
+ //gpio_te = <&gpio1 0 0>; /* EXT_TE gpio-0 */
+ pins = <0 1 2 3>; /* DSI Pin config */
+ esd_interval = <0>; /* ESD_INTERVAL */
+ pixel_clock = <4608>; /* kHZ = 320*240*60/1000*/
+ /* 0: RGB888
+ * 1: RGB666
+ * 2: RGB666_PACKED
+ * 3: RGB565
+ */
+ pixel_format = <1>;
+ hs_clk = <100000000 150000000>; /* min max*/
+ lp_clk = <7000000 9000000>; /* min max*/
+ };
+
+ /* FIXME: this dummy regulator is only needed as
+ a reference, but is enough to let devices
+ reference something. The regulator_get will
+ fall back to a dummy regulator internally.
+ */
+ regulators {
+ compatible = "simple-bus";
+
+ dummy: regulator-dummy {
+ compatible = "regulator-fixed";
+ regulator-name = "dummy";
+ regulator-always-on;
+ };
+
+ vmmc: gpio-regulator {
+ compatible = "regulator-fixed";
+
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ regulator-name = "vmmc";
+ gpio = <&gpio6 26 0>;
+ startup-delay-us = <70000>;
+ enable-active-high;
+ };
+
+ };
+
+ m4sensor {
+ compatible = "simple-bus";
+ wrist {
+ compatible = "mot,m4wrist";
+ mot,wrist_xres = <&gpio6 15 0>; /* xres gpio-175 */
+ mot,wrist_clk = <&gpio1 29 0>; /* clk gpio-029 */
+ mot,wrist_data = <&gpio1 15 0>; /* data gpio-015 */
+ };
+
+ pressure {
+ compatible = "mot,m4pressure";
+ };
+ mpu9150 {
+ compatible = "mot,m4mpu9150";
+ };
+ pedometer {
+ compatible = "mot,m4pedometer";
+ };
+ passive {
+ compatible = "mot,m4passive";
+ };
+ gesture {
+ compatible = "mot,m4gesture";
+ };
+ display {
+ compatible = "mot,m4display";
+ };
+ download {
+ compatible = "mot,m4download";
+ };
+ stillmode {
+ compatible = "mot,m4stillmode";
+ };
+ };
+};
+
+
+
+&i2c1 {
+ clock-frequency = <400000>;
+
+ atmxt@4a {
+ compatible = "atmel,atmxt-ts";
+ reg = <0x4a>;
+ interrupt-parent = <&intc>;
+ gpios = <&gpio4 3 0>, /* IRQ gpio-099 */
+ <&gpio6 4 0>; /* RESET gpio-164*/
+ atmel,atmxt-tdat-filename = "atmxt-r2.tdat";
+ };
+};
+
+&i2c2 {
+ m4sensorhub@18 {
+ compatible = "mot,m4sensorhub";
+ reg = <0x18>;
+
+ mot,irq-gpio = <&gpio2 27 1>; /* gpio-059 */
+ mot,reset-gpio = <&gpio5 1 2>; /* gpio-129 */
+ mot,wake-gpio = <&gpio4 30 0>; /* gpio-126 */
+ mot,boot0-gpio = <&gpio1 21 0>; /* gpio-021 */
+ mot,boot1-gpio = <&gpio1 24 0>; /* gpio-024 */
+ mot,enable-gpio = <&gpio1 28 0>; /* gpio-028 */
+ };
+};
+
+&mcspi2 {
+ m4sensorhubaudio {
+ compatible = "mot,m4audio";
+ reg = <0>; /* CS 0 */
+ spi-max-frequency = <2000000>;
+ spi-cpha = <0>;
+ spi-cpol = <0>;
+ };
+};
+
+&i2c3 {
+
+ clock-frequency = <400000>;
+
+ lm3535@38 {
+ compatible = "ti,lm3535";
+ reg = <0x38>;
+ };
+};
+
+
+&omap3_pmx_core {
+ pinctrl-names = "default";
+ pinctrl-0 = <&board_pins>;
+
+ board_pins: pinmux_board_pins {
+ pinctrl-single,pins = <
+ 0x08e 0x104 /* GPMC_CLK, MODE4 | INPUT */
+ 0x0a0 0x002 /* GPMC_WAIT2, MODE2 | OUTPUT */
+ 0x0a2 0x102 /* GPMC_WAIT3, MODE2 | INPUT */
+ 0x0c6 0x004 /* DSS_DATA13, MODE4 | OUTPUT */
+ 0x0ea 0x004 /* MUXCAM_D2, MODE4 | OUTPUT */
+ 0x0e4 0x004 /* CAM_FLD, MODE4 | OUTPUT */
+ 0x0e6 0x11c /* CAM_D0, MODE4 | INPUT_PULLUP */
+ 0x122 0x004 /* SIM_CLK, MODE4 | OUTPUT */
+ 0x124 0x104 /* SIM_PWRCTRL, MODE4 | INPUT */
+ 0x144 0x000 /* UART2_TX, MODE0 | OUTPUT */
+ 0x146 0x000 /* UART2_RTS, MODE0 | OUTPUT */
+ 0x148 0x100 /* UART2_CTS, MODE0 | INPUT */
+ 0x14a 0x100 /* UART2_RX, MODE0 | INPUT */
+ 0x14c 0x000 /* UART1_TX, MODE0 | OUTPUT */
+ 0x14e 0x000 /* UART1_RTS, MODE0 | OUTPUT */
+ 0x150 0x100 /* UART1_CTS, MODE0 | INPUT */
+ 0x152 0x100 /* UART1_RX, MODE0 | INPUT */
+ 0x168 0x104 /* MCBSP1_CLKX, MODE4 | INPUT */
+ 0x16c 0x004 /* UART3_RTS_SD, MODE4 | OUTPUT */
+ 0x1a0 0x11b /* MCSPI1_CS1, MODE3 | INPUT_PULLUP */
+ 0x18e 0x118 /* I2C2_SCL, MODE0 | INPUT_PULLUP */
+ 0x190 0x118 /* I2C2_SDA, MODE0 | INPUT_PULLUP */
+ 0x1a6 0x100 /* MCSPI2_CLK, MODE0 | INPUT */
+ 0x1a8 0x000 /* MCSPI2_SIMO, MODE0 | OUTPUT */
+ 0x1aa 0x100 /* MCSPI2_SOMI, MODE0 | INPUT */
+ 0x1ac 0x000 /* MCSPI2_CS0, MODE0 | OUTPUT */
+ 0x1b0 0x104 /* SYS_NIRQ, MODE4 | INPUT */
+ 0x1b2 0x004 /* SYS_CLKOUT2, MODE4 | OUTPUT */
+ 0x5a8 0x11a /* ETK_CLK, MODE2 | INPUT_PULLUP */
+ 0x5aa 0x11a /* ETK_CTL, MODE2 | INPUT_PULLUP */
+ 0x5b2 0x11a /* ETK_D3, MODE2 | INPUT_PULLUP */
+ 0x5b4 0x11a /* ETK_D4, MODE2 | INPUT_PULLUP */
+ 0x5b6 0x11a /* ETK_D5, MODE2 | INPUT_PULLUP */
+ 0x5b8 0x11a /* ETK_D6, MODE2 | INPUT_PULLUP */
+ 0x5ba 0x004 /* ETK_D7, MODE4 | OUTPUT */
+ 0x5c0 0x004 /* ETK_D10, MODE4 | OUTPUT */
+ 0x5c8 0x004 /* ETK_D14, MODE4 | OUTPUT */
+ >;
+
+ };
+};
+
+&omap3_pmx_wkup{
+ pinctrl-names = "default";
+ pinctrl-0 = <&wkup_pins>;
+
+ wkup_pins: pinmux_board_pins {
+ pinctrl-single,pins = <
+ 0x01a 0x10c /* SYS_CLKOUT1, MODE4 | INPUT_PULLDOWN */
+ >;
+ };
+};
+
+/* internal */
+&mmc2 {
+ ti,non-removeable;
+ bus-width = <8>;
+ vmmc-supply = <&dummy>;
+ vmmc_aux-supply = <&dummy>;
+};
+
+/* wireless */
+&mmc3 {
+ ti,non-removeable;
+ cap-power-off-card;
+ bus-width = <4>;
+ vmmc-supply = <&vmmc>;
+ wl_host_wake_gpio = <10>;
+};
diff --git a/arch/arm/configs/.gitignore b/arch/arm/configs/.gitignore
new file mode 100644
index 00000000000..ad0f5338923
--- /dev/null
+++ b/arch/arm/configs/.gitignore
@@ -0,0 +1,12 @@
+#
+# NOTE! Don't add files that are generated in specific
+# subdirectories here. Add them in the ".gitignore" file
+# in that subdirectory instead.
+#
+# NOTE! Please use 'git ls-files -i --exclude-standard'
+# command after changing this file, to see if there are
+# any tracked files which get ignored after the change.
+#
+# Normal rules
+#
+__ext_mapphone_defconfig
diff --git a/arch/arm/configs/ext_config/eng_bld.config b/arch/arm/configs/ext_config/eng_bld.config
new file mode 100644
index 00000000000..3190ad30e28
--- /dev/null
+++ b/arch/arm/configs/ext_config/eng_bld.config
@@ -0,0 +1,43 @@
+#
+# Define options needed for engineering build
+# The options need to be turned off for production release
+#
+CONFIG_TOUCHSCREEN_DEBUG=y
+CONFIG_LTT_LITE=y
+CONFIG_DEBUG_SLAB=y
+# CONFIG_DEBUG_SLAB_LEAK is not set
+CONFIG_DEBUG_SLAB_KMALLOC_SINGLE_PAGE=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_MMC_TEST_INSERT_REMOVE=y
+CONFIG_CORE_DUMP_PERMISSION=y
+CONFIG_OMAP_WATCHDOG_CONTROL=y
+CONFIG_NON_NESTED_FIQ=y
+CONFIG_OMAP_WATCHDOG_FIQ=y
+CONFIG_IPC_USBHOST_DBG=y
+CONFIG_APANIC_MMC_MEMDUMP=y
+CONFIG_OMAP_SECURE_FIQ_HACK=y
+CONFIG_USB_TESTING_POWER=y
+CONFIG_DUMP_TASKS_ON_NOPAGE=y
+CONFIG_DEBUG_FS=y
+CONFIG_DEBUG_NETMUX=y
+CONFIG_PM_DBG_DRV=y
+CONFIG_TIMER_STATS=y
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_LL=y
+CONFIG_DEBUG_OMAP2PLUS_UART=y
+# CONFIG_DEBUG_ICEDCC is not set
+# CONFIG_DEBUG_SEMIHOSTING is not set
+# CONFIG_DEBUG_OMAP2UART1 is not set
+# CONFIG_DEBUG_OMAP2UART2 is not set
+# CONFIG_DEBUG_OMAP2UART3 is not set
+CONFIG_DEBUG_OMAP3UART3=y
+# CONFIG_DEBUG_OMAP4UART3 is not set
+# CONFIG_DEBUG_OMAP3UART4 is not set
+# CONFIG_DEBUG_OMAP4UART4 is not set
+# CONFIG_DEBUG_TI81XXUART1 is not set
+# CONFIG_DEBUG_TI81XXUART2 is not set
+# CONFIG_DEBUG_TI81XXUART3 is not set
+# CONFIG_DEBUG_AM33XXUART1 is not set
+# CONFIG_DEBUG_ZOOM_UART is not set
+CONFIG_DEBUG_LL_INCLUDE="debug/omap2plus.S"
+CONFIG_EARLY_PRINTK=y
diff --git a/arch/arm/configs/ext_config/lttng.config b/arch/arm/configs/ext_config/lttng.config
new file mode 100644
index 00000000000..3fc2c5b9f82
--- /dev/null
+++ b/arch/arm/configs/ext_config/lttng.config
@@ -0,0 +1,31 @@
+CONFIG_TRACEPOINTS=y
+CONFIG_MARKERS=y
+CONFIG_LTT=y
+CONFIG_LTT_FILTER=y
+CONFIG_LTT_RELAY=y
+CONFIG_LTT_RELAY_LOCKLESS=y
+# CONFIG_LTT_RELAY_IRQOFF is not set
+# CONFIG_LTT_RELAY_LOCKED is not set
+CONFIG_LTT_SERIALIZE=y
+CONFIG_LTT_FAST_SERIALIZE=y
+CONFIG_LTT_TRACEPROBES=y
+CONFIG_LTT_TRACE_CONTROL=y
+CONFIG_LTT_TRACER=y
+CONFIG_LTT_ALIGNMENT=y
+CONFIG_LTT_CHECK_ARCH_EFFICIENT_UNALIGNED_ACCESS=y
+# CONFIG_LTT_DEBUG_EVENT_SIZE is not set
+CONFIG_LTT_USERSPACE_EVENT=y
+CONFIG_LTT_VMCORE=y
+CONFIG_LTT_STATEDUMP=y
+# CONFIG_LTT_ASCII is not set
+# CONFIG_NET_DROP_MONITOR is not set
+CONFIG_STACKTRACE=y
+CONFIG_NOP_TRACER=y
+CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_TRACING=y
+CONFIG_GENERIC_TRACER=y
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+CONFIG_BINARY_PRINTF=y
diff --git a/arch/arm/configs/minnow_defconfig b/arch/arm/configs/minnow_defconfig
new file mode 100644
index 00000000000..cf5d45a31b4
--- /dev/null
+++ b/arch/arm/configs/minnow_defconfig
@@ -0,0 +1,2483 @@
+#
+# Linux/arm 3.10.0 Kernel Configuration
+#
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+CONFIG_HAVE_PROC_CPU=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+CONFIG_ARCH_HAS_CPUFREQ=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_VECTORS_BASE=0xffff0000
+CONFIG_ARM_PATCH_PHYS_VIRT=y
+CONFIG_GENERIC_BUG=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_IRQ_WORK=y
+CONFIG_BUILDTIME_EXTABLE_SORT=y
+
+#
+# General setup
+#
+CONFIG_BROKEN_ON_SMP=y
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE=""
+CONFIG_LOCALVERSION=""
+CONFIG_LOCALVERSION_AUTO=y
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_XZ=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_KERNEL_GZIP=y
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_XZ is not set
+# CONFIG_KERNEL_LZO is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_FHANDLE is not set
+# CONFIG_AUDIT is not set
+CONFIG_HAVE_GENERIC_HARDIRQS=y
+
+#
+# IRQ subsystem
+#
+CONFIG_GENERIC_HARDIRQS=y
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_HARDIRQS_SW_RESEND=y
+CONFIG_GENERIC_IRQ_CHIP=y
+CONFIG_IRQ_DOMAIN=y
+# CONFIG_IRQ_DOMAIN_DEBUG is not set
+CONFIG_SPARSE_IRQ=y
+CONFIG_KTIME_SCALAR=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+
+#
+# Timers subsystem
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ_COMMON=y
+# CONFIG_HZ_PERIODIC is not set
+CONFIG_NO_HZ_IDLE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+
+#
+# CPU/Task time and stats accounting
+#
+CONFIG_TICK_CPU_ACCOUNTING=y
+# CONFIG_IRQ_TIME_ACCOUNTING is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+# CONFIG_TASKSTATS is not set
+
+#
+# RCU Subsystem
+#
+# CONFIG_TREE_PREEMPT_RCU is not set
+CONFIG_TINY_PREEMPT_RCU=y
+CONFIG_PREEMPT_RCU=y
+# CONFIG_RCU_STALL_COMMON is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_RCU_BOOST is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=18
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+# CONFIG_CGROUP_FREEZER is not set
+# CONFIG_CGROUP_DEVICE is not set
+# CONFIG_CPUSETS is not set
+# CONFIG_CGROUP_CPUACCT is not set
+# CONFIG_RESOURCE_COUNTERS is not set
+# CONFIG_CGROUP_SCHED is not set
+# CONFIG_BLK_CGROUP is not set
+# CONFIG_CHECKPOINT_RESTORE is not set
+# CONFIG_NAMESPACES is not set
+CONFIG_UIDGID_CONVERTED=y
+CONFIG_UIDGID_STRICT_TYPE_CHECKS=y
+# CONFIG_SCHED_AUTOGROUP is not set
+# CONFIG_SYSFS_DEPRECATED is not set
+# CONFIG_RELAY is not set
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_RD_GZIP=y
+# CONFIG_RD_BZIP2 is not set
+# CONFIG_RD_LZMA is not set
+# CONFIG_RD_XZ is not set
+# CONFIG_RD_LZO is not set
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+CONFIG_HAVE_UID16=y
+CONFIG_HOTPLUG=y
+CONFIG_EXPERT=y
+CONFIG_UID16=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+# CONFIG_KALLSYMS_ALL is not set
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_EMBEDDED=y
+CONFIG_HAVE_PERF_EVENTS=y
+CONFIG_PERF_USE_VMALLOC=y
+
+#
+# Kernel Performance Events And Counters
+#
+# CONFIG_PERF_EVENTS is not set
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_COMPAT_BRK=y
+CONFIG_SLAB=y
+# CONFIG_SLUB is not set
+# CONFIG_SLOB is not set
+# CONFIG_PROFILING is not set
+CONFIG_HAVE_OPROFILE=y
+CONFIG_KPROBES=y
+# CONFIG_JUMP_LABEL is not set
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_KRETPROBES=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_HAVE_DMA_CONTIGUOUS=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_GENERIC_IDLE_POLL_SETUP=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_CLK=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_MODULES_USE_ELF_REL=y
+CONFIG_CLONE_BACKWARDS=y
+CONFIG_OLD_SIGSUSPEND3=y
+CONFIG_OLD_SIGACTION=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+CONFIG_HAVE_GENERIC_DMA_COHERENT=y
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+CONFIG_MODULES=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+CONFIG_MODVERSIONS=y
+# CONFIG_MODULE_SRCVERSION_ALL is not set
+# CONFIG_MODULE_SIG is not set
+CONFIG_BLOCK=y
+CONFIG_LBDAF=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+# CONFIG_EFI_PARTITION is not set
+# CONFIG_SYSV68_PARTITION is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_DEADLINE is not set
+CONFIG_IOSCHED_CFQ=y
+CONFIG_DEFAULT_CFQ=y
+# CONFIG_DEFAULT_NOOP is not set
+CONFIG_DEFAULT_IOSCHED="cfq"
+CONFIG_UNINLINE_SPIN_UNLOCK=y
+CONFIG_FREEZER=y
+
+#
+# System Type
+#
+CONFIG_MMU=y
+CONFIG_ARCH_MULTIPLATFORM=y
+# CONFIG_ARCH_INTEGRATOR is not set
+# CONFIG_ARCH_REALVIEW is not set
+# CONFIG_ARCH_VERSATILE is not set
+# CONFIG_ARCH_AT91 is not set
+# CONFIG_ARCH_CLPS711X is not set
+# CONFIG_ARCH_GEMINI is not set
+# CONFIG_ARCH_EBSA110 is not set
+# CONFIG_ARCH_EP93XX is not set
+# CONFIG_ARCH_FOOTBRIDGE is not set
+# CONFIG_ARCH_NETX is not set
+# CONFIG_ARCH_IOP13XX is not set
+# CONFIG_ARCH_IOP32X is not set
+# CONFIG_ARCH_IOP33X is not set
+# CONFIG_ARCH_IXP4XX is not set
+# CONFIG_ARCH_DOVE is not set
+# CONFIG_ARCH_KIRKWOOD is not set
+# CONFIG_ARCH_MV78XX0 is not set
+# CONFIG_ARCH_ORION5X is not set
+# CONFIG_ARCH_MMP is not set
+# CONFIG_ARCH_KS8695 is not set
+# CONFIG_ARCH_W90X900 is not set
+# CONFIG_ARCH_LPC32XX is not set
+# CONFIG_ARCH_PXA is not set
+# CONFIG_ARCH_MSM is not set
+# CONFIG_ARCH_SHMOBILE is not set
+# CONFIG_ARCH_RPC is not set
+# CONFIG_ARCH_SA1100 is not set
+# CONFIG_ARCH_S3C24XX is not set
+# CONFIG_ARCH_S3C64XX is not set
+# CONFIG_ARCH_S5P64X0 is not set
+# CONFIG_ARCH_S5PC100 is not set
+# CONFIG_ARCH_S5PV210 is not set
+# CONFIG_ARCH_EXYNOS is not set
+# CONFIG_ARCH_SHARK is not set
+# CONFIG_ARCH_U300 is not set
+# CONFIG_ARCH_DAVINCI is not set
+# CONFIG_ARCH_OMAP1 is not set
+
+#
+# Multiple platform selection
+#
+
+#
+# CPU Core family selection
+#
+# CONFIG_ARCH_MULTI_V6 is not set
+CONFIG_ARCH_MULTI_V7=y
+CONFIG_ARCH_MULTI_V6_V7=y
+# CONFIG_ARCH_MULTI_CPU_AUTO is not set
+# CONFIG_ARCH_MVEBU is not set
+# CONFIG_ARCH_BCM is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_ARCH_HIGHBANK is not set
+# CONFIG_ARCH_MXC is not set
+
+#
+# TI OMAP Common Features
+#
+
+#
+# OMAP Feature Selections
+#
+CONFIG_OMAP_RESET_CLOCKS=y
+CONFIG_OMAP_MUX=y
+CONFIG_OMAP_MUX_DEBUG=y
+CONFIG_OMAP_MUX_WARNINGS=y
+CONFIG_OMAP_32K_TIMER=y
+CONFIG_OMAP3_L2_AUX_SECURE_SAVE_RESTORE=y
+CONFIG_OMAP3_L2_AUX_SECURE_SERVICE_SET_ID=41
+CONFIG_OMAP_DM_TIMER=y
+CONFIG_OMAP_PM_NOOP=y
+# CONFIG_MACH_OMAP_GENERIC is not set
+CONFIG_ARCH_OMAP=y
+CONFIG_ARCH_OMAP2PLUS=y
+
+#
+# TI OMAP2/3/4 Specific Features
+#
+# CONFIG_ARCH_OMAP2PLUS_TYPICAL is not set
+CONFIG_SOC_HAS_OMAP2_SDRC=y
+CONFIG_ARCH_OMAP3=y
+# CONFIG_ARCH_OMAP4 is not set
+# CONFIG_SOC_OMAP5 is not set
+CONFIG_SOC_OMAP3430=y
+# CONFIG_SOC_TI81XX is not set
+# CONFIG_SOC_AM33XX is not set
+CONFIG_OMAP_PACKAGE_CBP=y
+
+#
+# OMAP Board Type
+#
+# CONFIG_MACH_OMAP3_BEAGLE is not set
+# CONFIG_MACH_DEVKIT8000 is not set
+# CONFIG_MACH_OMAP_LDP is not set
+# CONFIG_MACH_OMAP3530_LV_SOM is not set
+# CONFIG_MACH_OMAP3_TORPEDO is not set
+# CONFIG_MACH_OVERO is not set
+# CONFIG_MACH_OMAP3EVM is not set
+# CONFIG_MACH_OMAP3517EVM is not set
+# CONFIG_MACH_CRANEBOARD is not set
+# CONFIG_MACH_OMAP3_PANDORA is not set
+# CONFIG_MACH_TOUCHBOOK is not set
+# CONFIG_MACH_OMAP_3430SDP is not set
+# CONFIG_MACH_NOKIA_RM680 is not set
+# CONFIG_MACH_NOKIA_RX51 is not set
+# CONFIG_MACH_OMAP_ZOOM2 is not set
+# CONFIG_MACH_OMAP_ZOOM3 is not set
+# CONFIG_MACH_CM_T35 is not set
+# CONFIG_MACH_CM_T3517 is not set
+# CONFIG_MACH_IGEP0020 is not set
+# CONFIG_MACH_IGEP0030 is not set
+# CONFIG_MACH_SBC3530 is not set
+# CONFIG_MACH_OMAP_3630SDP is not set
+CONFIG_MACH_MINNOW=y
+# CONFIG_OMAP3_EMU is not set
+# CONFIG_OMAP3_SDRC_AC_TIMING is not set
+# CONFIG_ARCH_SOCFPGA is not set
+# CONFIG_PLAT_SPEAR is not set
+# CONFIG_ARCH_SUNXI is not set
+# CONFIG_ARCH_SIRF is not set
+# CONFIG_ARCH_TEGRA is not set
+# CONFIG_ARCH_U8500 is not set
+# CONFIG_ARCH_VEXPRESS is not set
+# CONFIG_ARCH_VIRT is not set
+# CONFIG_ARCH_WM8850 is not set
+# CONFIG_ARCH_ZYNQ is not set
+
+#
+# Processor Type
+#
+CONFIG_CPU_V7=y
+CONFIG_CPU_32v6K=y
+CONFIG_CPU_32v7=y
+CONFIG_CPU_ABRT_EV7=y
+CONFIG_CPU_PABRT_V7=y
+CONFIG_CPU_CACHE_V7=y
+CONFIG_CPU_CACHE_VIPT=y
+CONFIG_CPU_COPY_V6=y
+CONFIG_CPU_TLB_V7=y
+CONFIG_CPU_HAS_ASID=y
+CONFIG_CPU_CP15=y
+CONFIG_CPU_CP15_MMU=y
+
+#
+# Processor Features
+#
+# CONFIG_ARM_LPAE is not set
+# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set
+CONFIG_ARM_THUMB=y
+# CONFIG_ARM_THUMBEE is not set
+CONFIG_ARM_VIRT_EXT=y
+# CONFIG_SWP_EMULATE is not set
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_BPREDICT_DISABLE is not set
+# CONFIG_CACHE_L2X0 is not set
+CONFIG_ARM_L1_CACHE_SHIFT_6=y
+CONFIG_ARM_L1_CACHE_SHIFT=6
+CONFIG_ARM_DMA_MEM_BUFFERABLE=y
+CONFIG_ARM_NR_BANKS=8
+CONFIG_MULTI_IRQ_HANDLER=y
+# CONFIG_ARM_ERRATA_430973 is not set
+CONFIG_ARM_ERRATA_720789=y
+CONFIG_ARM_ERRATA_754322=y
+CONFIG_ARM_ERRATA_775420=y
+
+#
+# Bus support
+#
+# CONFIG_PCI_SYSCALL is not set
+# CONFIG_PCCARD is not set
+
+#
+# Kernel Features
+#
+# CONFIG_HAVE_ARM_ARCH_TIMER is not set
+CONFIG_VMSPLIT_3G=y
+# CONFIG_VMSPLIT_2G is not set
+# CONFIG_VMSPLIT_1G is not set
+CONFIG_PAGE_OFFSET=0xC0000000
+# CONFIG_ARM_PSCI is not set
+CONFIG_ARCH_NR_GPIO=0
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+CONFIG_PREEMPT=y
+CONFIG_PREEMPT_COUNT=y
+CONFIG_HZ=100
+CONFIG_SCHED_HRTICK=y
+# CONFIG_THUMB2_KERNEL is not set
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_ARCH_HAS_HOLES_MEMORYMODEL=y
+# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
+# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
+CONFIG_HAVE_ARCH_PFN_VALID=y
+# CONFIG_HIGHMEM is not set
+CONFIG_FLATMEM=y
+CONFIG_FLAT_NODE_MEM_MAP=y
+CONFIG_HAVE_MEMBLOCK=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=4
+# CONFIG_COMPACTION is not set
+# CONFIG_PHYS_ADDR_T_64BIT is not set
+CONFIG_ZONE_DMA_FLAG=0
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+CONFIG_CROSS_MEMORY_ATTACH=y
+CONFIG_NEED_PER_CPU_KM=y
+# CONFIG_CLEANCACHE is not set
+CONFIG_FORCE_MAX_ZONEORDER=11
+CONFIG_ALIGNMENT_TRAP=y
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_CC_STACKPROTECTOR is not set
+# CONFIG_XEN is not set
+
+#
+# Boot options
+#
+CONFIG_USE_OF=y
+CONFIG_ATAGS=y
+# CONFIG_DEPRECATED_PARAM_STRUCT is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+# CONFIG_ARM_APPENDED_DTB is not set
+CONFIG_CMDLINE="rootwait earlyprintk console=ttyO2,115200n8 mem=254M@0x80000000 init=/init"
+# CONFIG_CMDLINE_FROM_BOOTLOADER is not set
+# CONFIG_CMDLINE_EXTEND is not set
+CONFIG_CMDLINE_FORCE=y
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+CONFIG_AUTO_ZRELADDR=y
+
+#
+# CPU Power Management
+#
+
+#
+# CPU Frequency scaling
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_GOV_COMMON=y
+CONFIG_CPU_FREQ_STAT=y
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
+# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set
+# CONFIG_GENERIC_CPUFREQ_CPU0 is not set
+
+#
+# ARM CPU frequency scaling drivers
+#
+# CONFIG_ARM_EXYNOS4210_CPUFREQ is not set
+# CONFIG_ARM_EXYNOS4X12_CPUFREQ is not set
+# CONFIG_ARM_EXYNOS5250_CPUFREQ is not set
+# CONFIG_ARM_EXYNOS5440_CPUFREQ is not set
+# CONFIG_ARM_KIRKWOOD_CPUFREQ is not set
+CONFIG_ARM_OMAP2PLUS_CPUFREQ=y
+CONFIG_CPU_IDLE=y
+# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set
+
+#
+# Floating point emulation
+#
+
+#
+# At least one emulation must be selected
+#
+CONFIG_VFP=y
+CONFIG_VFPv3=y
+CONFIG_NEON=y
+
+#
+# Userspace binary formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
+CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
+CONFIG_BINFMT_SCRIPT=y
+CONFIG_HAVE_AOUT=y
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_COREDUMP=y
+
+#
+# Power management options
+#
+CONFIG_SUSPEND=y
+CONFIG_SUSPEND_FREEZER=y
+CONFIG_PM_SLEEP=y
+# CONFIG_PM_AUTOSLEEP is not set
+# CONFIG_PM_WAKELOCKS is not set
+CONFIG_PM_RUNTIME=y
+CONFIG_PM=y
+CONFIG_PM_DEBUG=y
+# CONFIG_PM_ADVANCED_DEBUG is not set
+# CONFIG_PM_TEST_SUSPEND is not set
+CONFIG_PM_SLEEP_DEBUG=y
+# CONFIG_APM_EMULATION is not set
+CONFIG_ARCH_HAS_OPP=y
+CONFIG_PM_OPP=y
+CONFIG_PM_CLK=y
+CONFIG_CPU_PM=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARM_CPU_SUSPEND=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_DIAG is not set
+CONFIG_UNIX=y
+# CONFIG_UNIX_DIAG is not set
+CONFIG_XFRM=y
+CONFIG_XFRM_ALGO=y
+CONFIG_XFRM_USER=y
+# CONFIG_XFRM_SUB_POLICY is not set
+CONFIG_XFRM_MIGRATE=y
+# CONFIG_XFRM_STATISTICS is not set
+CONFIG_XFRM_IPCOMP=y
+CONFIG_NET_KEY=y
+CONFIG_NET_KEY_MIGRATE=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE_DEMUX is not set
+CONFIG_NET_IP_TUNNEL=y
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_NET_IPVTI is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=y
+CONFIG_INET_XFRM_MODE_TRANSPORT=y
+CONFIG_INET_XFRM_MODE_TUNNEL=y
+CONFIG_INET_XFRM_MODE_BEET=y
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+# CONFIG_IPV6_ROUTE_INFO is not set
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_INET6_XFRM_TUNNEL=y
+CONFIG_INET6_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_TRANSPORT=y
+CONFIG_INET6_XFRM_MODE_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_BEET=y
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=y
+# CONFIG_IPV6_SIT_6RD is not set
+CONFIG_IPV6_NDISC_NODETYPE=y
+CONFIG_IPV6_TUNNEL=y
+# CONFIG_IPV6_GRE is not set
+CONFIG_IPV6_MULTIPLE_TABLES=y
+# CONFIG_IPV6_SUBTREES is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_NETFILTER_ADVANCED=y
+
+#
+# Core Netfilter Configuration
+#
+# CONFIG_NETFILTER_NETLINK_ACCT is not set
+# CONFIG_NETFILTER_NETLINK_QUEUE is not set
+# CONFIG_NETFILTER_NETLINK_LOG is not set
+# CONFIG_NF_CONNTRACK is not set
+# CONFIG_NETFILTER_XTABLES is not set
+# CONFIG_IP_VS is not set
+
+#
+# IP: Netfilter Configuration
+#
+# CONFIG_NF_DEFRAG_IPV4 is not set
+# CONFIG_IP_NF_IPTABLES is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+
+#
+# IPv6: Netfilter Configuration
+#
+# CONFIG_NF_DEFRAG_IPV6 is not set
+# CONFIG_IP6_NF_IPTABLES is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_RDS is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_L2TP is not set
+# CONFIG_BRIDGE is not set
+CONFIG_HAVE_NET_DSA=y
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+CONFIG_NET_SCHED=y
+
+#
+# Queueing/Scheduling
+#
+# CONFIG_NET_SCH_CBQ is not set
+# CONFIG_NET_SCH_HTB is not set
+# CONFIG_NET_SCH_HFSC is not set
+# CONFIG_NET_SCH_PRIO is not set
+# CONFIG_NET_SCH_MULTIQ is not set
+# CONFIG_NET_SCH_RED is not set
+# CONFIG_NET_SCH_SFB is not set
+# CONFIG_NET_SCH_SFQ is not set
+# CONFIG_NET_SCH_TEQL is not set
+# CONFIG_NET_SCH_TBF is not set
+# CONFIG_NET_SCH_GRED is not set
+# CONFIG_NET_SCH_DSMARK is not set
+# CONFIG_NET_SCH_NETEM is not set
+# CONFIG_NET_SCH_DRR is not set
+# CONFIG_NET_SCH_MQPRIO is not set
+# CONFIG_NET_SCH_CHOKE is not set
+# CONFIG_NET_SCH_QFQ is not set
+# CONFIG_NET_SCH_CODEL is not set
+# CONFIG_NET_SCH_FQ_CODEL is not set
+# CONFIG_NET_SCH_PLUG is not set
+
+#
+# Classification
+#
+# CONFIG_NET_CLS_BASIC is not set
+# CONFIG_NET_CLS_TCINDEX is not set
+# CONFIG_NET_CLS_ROUTE4 is not set
+# CONFIG_NET_CLS_FW is not set
+# CONFIG_NET_CLS_U32 is not set
+# CONFIG_NET_CLS_RSVP is not set
+# CONFIG_NET_CLS_RSVP6 is not set
+# CONFIG_NET_CLS_FLOW is not set
+# CONFIG_NET_CLS_CGROUP is not set
+# CONFIG_NET_EMATCH is not set
+# CONFIG_NET_CLS_ACT is not set
+CONFIG_NET_SCH_FIFO=y
+# CONFIG_DCB is not set
+CONFIG_DNS_RESOLVER=y
+# CONFIG_BATMAN_ADV is not set
+# CONFIG_OPENVSWITCH is not set
+# CONFIG_VSOCKETS is not set
+# CONFIG_NETLINK_MMAP is not set
+# CONFIG_NETLINK_DIAG is not set
+# CONFIG_NETPRIO_CGROUP is not set
+CONFIG_BQL=y
+# CONFIG_BPF_JIT is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_NET_TCPPROBE is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+CONFIG_BT=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+# CONFIG_BT_BNEP_MC_FILTER is not set
+# CONFIG_BT_BNEP_PROTO_FILTER is not set
+CONFIG_BT_HIDP=y
+
+#
+# Bluetooth device drivers
+#
+# CONFIG_BT_HCIBTUSB is not set
+# CONFIG_BT_HCIBTSDIO is not set
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+# CONFIG_BT_HCIUART_BCSP is not set
+# CONFIG_BT_HCIUART_ATH3K is not set
+CONFIG_BT_HCIUART_LL=y
+# CONFIG_BT_HCIUART_3WIRE is not set
+# CONFIG_BT_HCIBCM203X is not set
+# CONFIG_BT_HCIBPA10X is not set
+# CONFIG_BT_HCIBFUSB is not set
+# CONFIG_BT_HCIVHCI is not set
+# CONFIG_BT_MRVL is not set
+# CONFIG_BT_WILINK is not set
+# CONFIG_AF_RXRPC is not set
+CONFIG_FIB_RULES=y
+CONFIG_WIRELESS=y
+# CONFIG_CFG80211 is not set
+# CONFIG_LIB80211 is not set
+CONFIG_WIRELESS_EXT=y
+
+#
+# CFG80211 needs to be enabled for MAC80211
+#
+# CONFIG_WIMAX is not set
+CONFIG_RFKILL=y
+# CONFIG_RFKILL_INPUT is not set
+# CONFIG_RFKILL_REGULATOR is not set
+# CONFIG_RFKILL_GPIO is not set
+CONFIG_RFKILL_WL127X=y
+# CONFIG_NET_9P is not set
+# CONFIG_CAIF is not set
+# CONFIG_CEPH_LIB is not set
+# CONFIG_NFC is not set
+CONFIG_HAVE_BPF_JIT=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_DEVTMPFS is not set
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+CONFIG_FW_LOADER_USER_HELPER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_GENERIC_CPU_DEVICES is not set
+CONFIG_SOC_BUS=y
+# CONFIG_DMA_SHARED_BUFFER is not set
+# CONFIG_CMA is not set
+
+#
+# Bus devices
+#
+# CONFIG_OMAP_OCP2SCP is not set
+CONFIG_OMAP_INTERCONNECT=y
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
+CONFIG_MTD=y
+# CONFIG_MTD_TESTS is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+CONFIG_MTD_CMDLINE_PARTS=y
+# CONFIG_MTD_AFS_PARTS is not set
+CONFIG_MTD_OF_PARTS=y
+# CONFIG_MTD_AR7_PARTS is not set
+
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_BLKDEVS=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+# CONFIG_RFD_FTL is not set
+# CONFIG_SSFDC is not set
+# CONFIG_SM_FTL is not set
+# CONFIG_MTD_OOPS is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+CONFIG_MTD_MAP_BANK_WIDTH_4=y
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+CONFIG_MTD_CFI_I2=y
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PLATRAM is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_DATAFLASH is not set
+# CONFIG_MTD_M25P80 is not set
+# CONFIG_MTD_SST25L is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_PHRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLOCK2MTD is not set
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOCG3 is not set
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_ONENAND is not set
+
+#
+# LPDDR flash memory drivers
+#
+# CONFIG_MTD_LPDDR is not set
+# CONFIG_MTD_UBI is not set
+CONFIG_DTC=y
+CONFIG_OF=y
+
+#
+# Device Tree and Open Firmware support
+#
+CONFIG_PROC_DEVICETREE=y
+# CONFIG_OF_SELFTEST is not set
+CONFIG_OF_FLATTREE=y
+CONFIG_OF_EARLY_FLATTREE=y
+CONFIG_OF_ADDRESS=y
+CONFIG_OF_IRQ=y
+CONFIG_OF_DEVICE=y
+CONFIG_OF_I2C=y
+CONFIG_OF_NET=y
+CONFIG_OF_MDIO=y
+CONFIG_OF_MTD=y
+# CONFIG_PARPORT is not set
+CONFIG_BLK_DEV=y
+# CONFIG_BLK_DEV_COW_COMMON is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
+# CONFIG_BLK_DEV_CRYPTOLOOP is not set
+# CONFIG_BLK_DEV_DRBD is not set
+# CONFIG_BLK_DEV_NBD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+# CONFIG_BLK_DEV_XIP is not set
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MG_DISK is not set
+# CONFIG_BLK_DEV_RBD is not set
+
+#
+# Misc devices
+#
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_AD525X_DPOT is not set
+# CONFIG_ATMEL_PWM is not set
+# CONFIG_DUMMY_IRQ is not set
+# CONFIG_ICS932S401 is not set
+# CONFIG_ATMEL_SSC is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_APDS9802ALS is not set
+# CONFIG_ISL29003 is not set
+# CONFIG_ISL29020 is not set
+# CONFIG_SENSORS_TSL2550 is not set
+# CONFIG_SENSORS_BH1780 is not set
+# CONFIG_SENSORS_BH1770 is not set
+# CONFIG_SENSORS_APDS990X is not set
+# CONFIG_HMC6352 is not set
+# CONFIG_DS1682 is not set
+# CONFIG_TI_DAC7512 is not set
+# CONFIG_BMP085_I2C is not set
+# CONFIG_BMP085_SPI is not set
+# CONFIG_USB_SWITCH_FSA9480 is not set
+# CONFIG_LATTICE_ECP3_CONFIG is not set
+# CONFIG_SRAM is not set
+# CONFIG_C2PORT is not set
+CONFIG_VIB_GPIO=y
+
+#
+# EEPROM support
+#
+# CONFIG_EEPROM_AT24 is not set
+# CONFIG_EEPROM_AT25 is not set
+# CONFIG_EEPROM_LEGACY is not set
+# CONFIG_EEPROM_MAX6875 is not set
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_EEPROM_93XX46 is not set
+
+#
+# Texas Instruments shared transport line discipline
+#
+CONFIG_TI_ST=m
+# CONFIG_SENSORS_LIS3_SPI is not set
+# CONFIG_SENSORS_LIS3_I2C is not set
+
+#
+# Altera FPGA firmware download module
+#
+# CONFIG_ALTERA_STAPL is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+CONFIG_NETDEVICES=y
+CONFIG_NET_CORE=y
+# CONFIG_BONDING is not set
+# CONFIG_DUMMY is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_MII is not set
+# CONFIG_NET_TEAM is not set
+# CONFIG_MACVLAN is not set
+# CONFIG_VXLAN is not set
+# CONFIG_NETCONSOLE is not set
+# CONFIG_NETPOLL is not set
+# CONFIG_NET_POLL_CONTROLLER is not set
+# CONFIG_TUN is not set
+# CONFIG_VETH is not set
+
+#
+# CAIF transport drivers
+#
+
+#
+# Distributed Switch Architecture drivers
+#
+# CONFIG_NET_DSA_MV88E6XXX is not set
+# CONFIG_NET_DSA_MV88E6060 is not set
+# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set
+# CONFIG_NET_DSA_MV88E6131 is not set
+# CONFIG_NET_DSA_MV88E6123_61_65 is not set
+# CONFIG_ETHERNET is not set
+CONFIG_PHYLIB=y
+
+#
+# MII PHY device drivers
+#
+# CONFIG_AT803X_PHY is not set
+# CONFIG_AMD_PHY is not set
+# CONFIG_MARVELL_PHY is not set
+# CONFIG_DAVICOM_PHY is not set
+# CONFIG_QSEMI_PHY is not set
+# CONFIG_LXT_PHY is not set
+# CONFIG_CICADA_PHY is not set
+# CONFIG_VITESSE_PHY is not set
+# CONFIG_SMSC_PHY is not set
+# CONFIG_BROADCOM_PHY is not set
+# CONFIG_BCM87XX_PHY is not set
+# CONFIG_ICPLUS_PHY is not set
+# CONFIG_REALTEK_PHY is not set
+# CONFIG_NATIONAL_PHY is not set
+# CONFIG_STE10XP is not set
+# CONFIG_LSI_ET1011C_PHY is not set
+# CONFIG_MICREL_PHY is not set
+# CONFIG_FIXED_PHY is not set
+# CONFIG_MDIO_BITBANG is not set
+# CONFIG_MDIO_BUS_MUX_GPIO is not set
+# CONFIG_MDIO_BUS_MUX_MMIOREG is not set
+# CONFIG_MICREL_KS8995MA is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# USB Network Adapters
+#
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_RTL8152 is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_USB_HSO is not set
+# CONFIG_USB_IPHETH is not set
+CONFIG_WLAN=y
+# CONFIG_USB_ZD1201 is not set
+# CONFIG_HOSTAP is not set
+CONFIG_WL_TI=y
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+# CONFIG_WAN is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+CONFIG_INPUT_MATRIXKMAP=y
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+CONFIG_INPUT_TOUCHSCREEN=y
+# CONFIG_TOUCHSCREEN_ADS7846 is not set
+# CONFIG_TOUCHSCREEN_AD7877 is not set
+# CONFIG_TOUCHSCREEN_AD7879 is not set
+# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
+CONFIG_TOUCHSCREEN_ATMXT=y
+# CONFIG_TOUCHSCREEN_AUO_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_BU21013 is not set
+# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
+# CONFIG_TOUCHSCREEN_CYTTSP_CORE is not set
+# CONFIG_TOUCHSCREEN_DYNAPRO is not set
+# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
+# CONFIG_TOUCHSCREEN_EETI is not set
+# CONFIG_TOUCHSCREEN_EGALAX is not set
+# CONFIG_TOUCHSCREEN_FUJITSU is not set
+# CONFIG_TOUCHSCREEN_ILI210X is not set
+# CONFIG_TOUCHSCREEN_GUNZE is not set
+# CONFIG_TOUCHSCREEN_ELO is not set
+# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
+# CONFIG_TOUCHSCREEN_WACOM_I2C is not set
+# CONFIG_TOUCHSCREEN_MAX11801 is not set
+# CONFIG_TOUCHSCREEN_MCS5000 is not set
+# CONFIG_TOUCHSCREEN_MMS114 is not set
+# CONFIG_TOUCHSCREEN_MTOUCH is not set
+# CONFIG_TOUCHSCREEN_INEXIO is not set
+# CONFIG_TOUCHSCREEN_MK712 is not set
+# CONFIG_TOUCHSCREEN_PENMOUNT is not set
+# CONFIG_TOUCHSCREEN_EDT_FT5X06 is not set
+# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
+# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
+# CONFIG_TOUCHSCREEN_PIXCIR is not set
+# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
+# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
+# CONFIG_TOUCHSCREEN_TSC_SERIO is not set
+# CONFIG_TOUCHSCREEN_TSC2005 is not set
+# CONFIG_TOUCHSCREEN_TSC2007 is not set
+# CONFIG_TOUCHSCREEN_W90X900 is not set
+# CONFIG_TOUCHSCREEN_ST1232 is not set
+# CONFIG_TOUCHSCREEN_TPS6507X is not set
+CONFIG_INPUT_MISC=y
+# CONFIG_INPUT_AD714X is not set
+# CONFIG_INPUT_BMA150 is not set
+# CONFIG_INPUT_MMA8450 is not set
+# CONFIG_INPUT_MPU3050 is not set
+# CONFIG_INPUT_GP2A is not set
+# CONFIG_INPUT_GPIO_TILT_POLLED is not set
+# CONFIG_INPUT_ATI_REMOTE2 is not set
+# CONFIG_INPUT_KEYSPAN_REMOTE is not set
+# CONFIG_INPUT_KXTJ9 is not set
+# CONFIG_INPUT_POWERMATE is not set
+# CONFIG_INPUT_YEALINK is not set
+# CONFIG_INPUT_CM109 is not set
+# CONFIG_INPUT_UINPUT is not set
+# CONFIG_INPUT_PCF8574 is not set
+# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set
+# CONFIG_INPUT_ADXL34X is not set
+# CONFIG_INPUT_IMS_PCU is not set
+# CONFIG_INPUT_CMA3000 is not set
+
+#
+# Hardware I/O ports
+#
+# CONFIG_SERIO is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_TTY=y
+# CONFIG_VT is not set
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_N_GSM is not set
+# CONFIG_TRACE_SINK is not set
+CONFIG_DEVKMEM=y
+
+#
+# Serial drivers
+#
+# CONFIG_SERIAL_8250 is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_MAX3100 is not set
+# CONFIG_SERIAL_MAX310X is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_SERIAL_OMAP=y
+CONFIG_SERIAL_OMAP_CONSOLE=y
+# CONFIG_SERIAL_SCCNXP is not set
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_IFX6X60 is not set
+# CONFIG_SERIAL_XILINX_PS_UART is not set
+# CONFIG_SERIAL_ARC is not set
+# CONFIG_TTY_PRINTK is not set
+# CONFIG_HVC_DCC is not set
+# CONFIG_IPMI_HANDLER is not set
+CONFIG_HW_RANDOM=y
+# CONFIG_HW_RANDOM_TIMERIOMEM is not set
+# CONFIG_HW_RANDOM_ATMEL is not set
+# CONFIG_HW_RANDOM_EXYNOS is not set
+# CONFIG_R3964 is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_TCG_TPM is not set
+CONFIG_I2C=y
+CONFIG_I2C_BOARDINFO=y
+CONFIG_I2C_COMPAT=y
+CONFIG_I2C_CHARDEV=y
+# CONFIG_I2C_MUX is not set
+CONFIG_I2C_HELPER_AUTO=y
+
+#
+# I2C Hardware Bus support
+#
+
+#
+# I2C system bus drivers (mostly embedded / system-on-chip)
+#
+# CONFIG_I2C_CBUS_GPIO is not set
+# CONFIG_I2C_DESIGNWARE_PLATFORM is not set
+# CONFIG_I2C_GPIO is not set
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_OMAP=y
+# CONFIG_I2C_PCA_PLATFORM is not set
+# CONFIG_I2C_PXA_PCI is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_XILINX is not set
+
+#
+# External I2C/SMBus adapter drivers
+#
+# CONFIG_I2C_DIOLAN_U2C is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_TINY_USB is not set
+
+#
+# Other I2C/SMBus bus drivers
+#
+# CONFIG_I2C_STUB is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_ALGO is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+CONFIG_SPI=y
+# CONFIG_SPI_DEBUG is not set
+CONFIG_SPI_MASTER=y
+
+#
+# SPI Master Controller Drivers
+#
+# CONFIG_SPI_ALTERA is not set
+# CONFIG_SPI_BITBANG is not set
+# CONFIG_SPI_GPIO is not set
+# CONFIG_SPI_FSL_SPI is not set
+# CONFIG_SPI_OC_TINY is not set
+CONFIG_SPI_OMAP24XX=y
+# CONFIG_SPI_PXA2XX_PCI is not set
+# CONFIG_SPI_SC18IS602 is not set
+# CONFIG_SPI_XCOMM is not set
+# CONFIG_SPI_XILINX is not set
+# CONFIG_SPI_DESIGNWARE is not set
+
+#
+# SPI Protocol Masters
+#
+CONFIG_SPI_SPIDEV=y
+# CONFIG_SPI_TLE62X0 is not set
+
+#
+# Qualcomm MSM SSBI bus support
+#
+# CONFIG_SSBI is not set
+# CONFIG_HSI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+
+#
+# PPS generators support
+#
+
+#
+# PTP clock support
+#
+# CONFIG_PTP_1588_CLOCK is not set
+
+#
+# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.
+#
+# CONFIG_PTP_1588_CLOCK_PCH is not set
+CONFIG_PINCTRL=y
+
+#
+# Pin controllers
+#
+CONFIG_PINMUX=y
+CONFIG_PINCONF=y
+# CONFIG_DEBUG_PINCTRL is not set
+CONFIG_PINCTRL_SINGLE=y
+# CONFIG_PINCTRL_EXYNOS is not set
+# CONFIG_PINCTRL_EXYNOS5440 is not set
+CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y
+CONFIG_ARCH_REQUIRE_GPIOLIB=y
+CONFIG_GPIO_DEVRES=y
+CONFIG_GPIOLIB=y
+CONFIG_OF_GPIO=y
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_SYSFS=y
+
+#
+# Memory mapped GPIO drivers:
+#
+# CONFIG_GPIO_GENERIC_PLATFORM is not set
+# CONFIG_GPIO_EM is not set
+# CONFIG_GPIO_RCAR is not set
+# CONFIG_GPIO_TS5500 is not set
+# CONFIG_GPIO_GRGPIO is not set
+
+#
+# I2C GPIO expanders:
+#
+# CONFIG_GPIO_MAX7300 is not set
+# CONFIG_GPIO_MAX732X is not set
+# CONFIG_GPIO_PCF857X is not set
+# CONFIG_GPIO_SX150X is not set
+# CONFIG_GPIO_ADP5588 is not set
+# CONFIG_GPIO_ADNP is not set
+
+#
+# PCI GPIO expanders:
+#
+
+#
+# SPI GPIO expanders:
+#
+# CONFIG_GPIO_MAX7301 is not set
+# CONFIG_GPIO_MCP23S08 is not set
+# CONFIG_GPIO_MC33880 is not set
+# CONFIG_GPIO_74X164 is not set
+
+#
+# AC97 GPIO expanders:
+#
+
+#
+# MODULbus GPIO expanders:
+#
+
+#
+# USB GPIO expanders:
+#
+CONFIG_W1=y
+CONFIG_W1_CON=y
+
+#
+# 1-wire Bus Masters
+#
+# CONFIG_W1_MASTER_DS2490 is not set
+# CONFIG_W1_MASTER_DS2482 is not set
+# CONFIG_W1_MASTER_DS1WM is not set
+# CONFIG_W1_MASTER_GPIO is not set
+CONFIG_HDQ_MASTER_OMAP=y
+
+#
+# 1-wire Slaves
+#
+# CONFIG_W1_SLAVE_THERM is not set
+# CONFIG_W1_SLAVE_SMEM is not set
+# CONFIG_W1_SLAVE_DS2408 is not set
+# CONFIG_W1_SLAVE_DS2413 is not set
+# CONFIG_W1_SLAVE_DS2423 is not set
+# CONFIG_W1_SLAVE_DS2431 is not set
+# CONFIG_W1_SLAVE_DS2433 is not set
+# CONFIG_W1_SLAVE_DS2760 is not set
+# CONFIG_W1_SLAVE_DS2780 is not set
+# CONFIG_W1_SLAVE_DS2781 is not set
+# CONFIG_W1_SLAVE_DS28E04 is not set
+# CONFIG_W1_SLAVE_BQ27000 is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_TEST_POWER is not set
+# CONFIG_BATTERY_DS2780 is not set
+# CONFIG_BATTERY_DS2781 is not set
+# CONFIG_BATTERY_DS2782 is not set
+# CONFIG_BATTERY_SBS is not set
+# CONFIG_BATTERY_BQ27x00 is not set
+# CONFIG_BATTERY_MAX17040 is not set
+# CONFIG_BATTERY_MAX17042 is not set
+# CONFIG_CHARGER_ISP1704 is not set
+# CONFIG_CHARGER_MAX8903 is not set
+# CONFIG_CHARGER_LP8727 is not set
+# CONFIG_CHARGER_GPIO is not set
+# CONFIG_CHARGER_MANAGER is not set
+# CONFIG_CHARGER_BQ2415X is not set
+# CONFIG_CHARGER_SMB347 is not set
+# CONFIG_BATTERY_GOLDFISH is not set
+CONFIG_CHARGER_CPCAP=y
+CONFIG_BATTERY_CPCAP=y
+# CONFIG_POWER_RESET is not set
+# CONFIG_POWER_RESET_RESTART is not set
+# CONFIG_POWER_AVS is not set
+# CONFIG_HWMON is not set
+# CONFIG_THERMAL is not set
+CONFIG_WATCHDOG=y
+CONFIG_WATCHDOG_CORE=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+
+#
+# Watchdog Device Drivers
+#
+# CONFIG_SOFT_WATCHDOG is not set
+# CONFIG_DW_WATCHDOG is not set
+CONFIG_OMAP_WATCHDOG=y
+# CONFIG_MAX63XX_WATCHDOG is not set
+
+#
+# USB-based Watchdog Cards
+#
+# CONFIG_USBPCWATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+CONFIG_BCMA_POSSIBLE=y
+
+#
+# Broadcom specific AMBA
+#
+# CONFIG_BCMA is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_AS3711 is not set
+# CONFIG_PMIC_ADP5520 is not set
+# CONFIG_MFD_AAT2870_CORE is not set
+# CONFIG_MFD_CROS_EC is not set
+# CONFIG_MFD_ASIC3 is not set
+# CONFIG_PMIC_DA903X is not set
+# CONFIG_MFD_DA9052_SPI is not set
+# CONFIG_MFD_DA9052_I2C is not set
+# CONFIG_MFD_DA9055 is not set
+# CONFIG_MFD_MC13XXX_SPI is not set
+# CONFIG_MFD_MC13XXX_I2C is not set
+# CONFIG_HTC_EGPIO is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_HTC_I2CPLD is not set
+# CONFIG_MFD_88PM800 is not set
+# CONFIG_MFD_88PM805 is not set
+# CONFIG_MFD_88PM860X is not set
+# CONFIG_MFD_MAX77686 is not set
+# CONFIG_MFD_MAX77693 is not set
+# CONFIG_MFD_MAX8907 is not set
+# CONFIG_MFD_MAX8925 is not set
+# CONFIG_MFD_MAX8997 is not set
+# CONFIG_MFD_MAX8998 is not set
+# CONFIG_EZX_PCAP is not set
+CONFIG_MFD_CPCAP=y
+# CONFIG_MFD_VIPERBOARD is not set
+# CONFIG_MFD_RETU is not set
+# CONFIG_MFD_PCF50633 is not set
+# CONFIG_MFD_RC5T583 is not set
+# CONFIG_MFD_SEC_CORE is not set
+# CONFIG_MFD_SI476X_CORE is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_MFD_SMSC is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_MFD_STMPE is not set
+# CONFIG_MFD_SYSCON is not set
+# CONFIG_MFD_TI_AM335X_TSCADC is not set
+# CONFIG_MFD_LP8788 is not set
+# CONFIG_MFD_PALMAS is not set
+# CONFIG_TPS6105X is not set
+# CONFIG_TPS65010 is not set
+# CONFIG_TPS6507X is not set
+# CONFIG_MFD_TPS65090 is not set
+# CONFIG_MFD_TPS65217 is not set
+# CONFIG_MFD_TPS6586X is not set
+# CONFIG_MFD_TPS65910 is not set
+# CONFIG_MFD_TPS65912 is not set
+# CONFIG_MFD_TPS65912_I2C is not set
+# CONFIG_MFD_TPS65912_SPI is not set
+# CONFIG_MFD_TPS80031 is not set
+# CONFIG_TWL4030_CORE is not set
+# CONFIG_TWL6040_CORE is not set
+# CONFIG_MFD_WL1273_CORE is not set
+# CONFIG_MFD_LM3533 is not set
+# CONFIG_MFD_TC3589X is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+# CONFIG_MFD_TC6393XB is not set
+# CONFIG_MFD_ARIZONA_I2C is not set
+# CONFIG_MFD_ARIZONA_SPI is not set
+# CONFIG_MFD_WM8400 is not set
+# CONFIG_MFD_WM831X_I2C is not set
+# CONFIG_MFD_WM831X_SPI is not set
+# CONFIG_MFD_WM8350_I2C is not set
+# CONFIG_MFD_WM8994 is not set
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+CONFIG_REGULATOR_DUMMY=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+# CONFIG_REGULATOR_USERSPACE_CONSUMER is not set
+# CONFIG_REGULATOR_GPIO is not set
+# CONFIG_REGULATOR_AD5398 is not set
+# CONFIG_REGULATOR_FAN53555 is not set
+# CONFIG_REGULATOR_ISL6271A is not set
+# CONFIG_REGULATOR_MAX1586 is not set
+# CONFIG_REGULATOR_MAX8649 is not set
+# CONFIG_REGULATOR_MAX8660 is not set
+# CONFIG_REGULATOR_MAX8952 is not set
+# CONFIG_REGULATOR_MAX8973 is not set
+# CONFIG_REGULATOR_LP3971 is not set
+# CONFIG_REGULATOR_LP3972 is not set
+# CONFIG_REGULATOR_LP872X is not set
+# CONFIG_REGULATOR_LP8755 is not set
+# CONFIG_REGULATOR_TPS51632 is not set
+# CONFIG_REGULATOR_TPS62360 is not set
+# CONFIG_REGULATOR_TPS65023 is not set
+# CONFIG_REGULATOR_TPS6507X is not set
+# CONFIG_REGULATOR_TPS6524X is not set
+CONFIG_REGULATOR_CPCAP=y
+# CONFIG_MEDIA_SUPPORT is not set
+CONFIG_MFD_M4SENSORHUB=m
+
+#
+# Graphics support
+#
+# CONFIG_DRM is not set
+# CONFIG_TEGRA_HOST1X is not set
+# CONFIG_VGASTATE is not set
+# CONFIG_VIDEO_OUTPUT_CONTROL is not set
+CONFIG_FB=y
+CONFIG_FIRMWARE_EDID=y
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BOOT_VESA_SUPPORT is not set
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_UVESA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_SMSCUFX is not set
+# CONFIG_FB_UDL is not set
+# CONFIG_FB_GOLDFISH is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_BROADSHEET is not set
+# CONFIG_FB_AUO_K190X is not set
+# CONFIG_FB_SIMPLE is not set
+CONFIG_OMAP2_VRFB=y
+CONFIG_OMAP2_DSS=y
+CONFIG_OMAP2_DSS_DEBUG=y
+# CONFIG_OMAP2_DSS_DEBUGFS is not set
+# CONFIG_OMAP2_DSS_DPI is not set
+# CONFIG_OMAP2_DSS_RFBI is not set
+# CONFIG_OMAP2_DSS_VENC is not set
+# CONFIG_OMAP4_DSS_HDMI is not set
+# CONFIG_OMAP2_DSS_SDI is not set
+CONFIG_OMAP2_DSS_DSI=y
+CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=0
+CONFIG_OMAP2_DSS_SLEEP_AFTER_VENC_RESET=y
+CONFIG_FB_OMAP2=y
+CONFIG_FB_OMAP2_DEBUG_SUPPORT=y
+CONFIG_FB_OMAP2_NUM_FBS=1
+
+#
+# OMAP2/3 Display Device Drivers
+#
+# CONFIG_PANEL_TAAL is not set
+CONFIG_PANEL_MINNOW=y
+# CONFIG_EXYNOS_VIDEO is not set
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+# CONFIG_LCD_L4F00242T03 is not set
+# CONFIG_LCD_LMS283GF05 is not set
+# CONFIG_LCD_LTV350QV is not set
+# CONFIG_LCD_ILI922X is not set
+# CONFIG_LCD_ILI9320 is not set
+# CONFIG_LCD_TDO24M is not set
+# CONFIG_LCD_VGG2432A4 is not set
+CONFIG_LCD_PLATFORM=y
+# CONFIG_LCD_S6E63M0 is not set
+# CONFIG_LCD_LD9040 is not set
+# CONFIG_LCD_AMS369FG06 is not set
+# CONFIG_LCD_LMS501KF03 is not set
+# CONFIG_LCD_HX8357 is not set
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_GENERIC=y
+# CONFIG_BACKLIGHT_ADP8860 is not set
+# CONFIG_BACKLIGHT_ADP8870 is not set
+# CONFIG_BACKLIGHT_LM3630 is not set
+# CONFIG_BACKLIGHT_LM3639 is not set
+# CONFIG_BACKLIGHT_LP855X is not set
+# CONFIG_LOGO is not set
+# CONFIG_FB_SSD1307 is not set
+CONFIG_SOUND=y
+CONFIG_SOUND_OSS_CORE=y
+CONFIG_SOUND_OSS_CORE_PRECLAIM=y
+# CONFIG_SND is not set
+CONFIG_SOUND_PRIME=y
+
+#
+# HID support
+#
+CONFIG_HID=y
+# CONFIG_HID_BATTERY_STRENGTH is not set
+# CONFIG_HIDRAW is not set
+# CONFIG_UHID is not set
+CONFIG_HID_GENERIC=y
+
+#
+# Special HID drivers
+#
+# CONFIG_HID_A4TECH is not set
+# CONFIG_HID_ACRUX is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_APPLEIR is not set
+# CONFIG_HID_AUREAL is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_CHERRY is not set
+# CONFIG_HID_CHICONY is not set
+# CONFIG_HID_CYPRESS is not set
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_EMS_FF is not set
+# CONFIG_HID_ELECOM is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_HOLTEK is not set
+# CONFIG_HID_KEYTOUCH is not set
+# CONFIG_HID_KYE is not set
+# CONFIG_HID_UCLOGIC is not set
+# CONFIG_HID_WALTOP is not set
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_ICADE is not set
+# CONFIG_HID_TWINHAN is not set
+# CONFIG_HID_KENSINGTON is not set
+# CONFIG_HID_LCPOWER is not set
+# CONFIG_HID_LENOVO_TPKBD is not set
+# CONFIG_HID_LOGITECH is not set
+# CONFIG_HID_MAGICMOUSE is not set
+# CONFIG_HID_MICROSOFT is not set
+# CONFIG_HID_MONTEREY is not set
+# CONFIG_HID_MULTITOUCH is not set
+# CONFIG_HID_NTRIG is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_PRIMAX is not set
+# CONFIG_HID_PS3REMOTE is not set
+# CONFIG_HID_ROCCAT is not set
+# CONFIG_HID_SAITEK is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SONY is not set
+# CONFIG_HID_SPEEDLINK is not set
+# CONFIG_HID_STEELSERIES is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_TIVO is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_THINGM is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_WACOM is not set
+# CONFIG_HID_WIIMOTE is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
+# CONFIG_HID_SENSOR_HUB is not set
+
+#
+# USB HID support
+#
+CONFIG_USB_HID=y
+# CONFIG_HID_PID is not set
+# CONFIG_USB_HIDDEV is not set
+
+#
+# I2C HID support
+#
+# CONFIG_I2C_HID is not set
+CONFIG_USB_ARCH_HAS_OHCI=y
+CONFIG_USB_ARCH_HAS_EHCI=y
+# CONFIG_USB_ARCH_HAS_XHCI is not set
+CONFIG_USB_SUPPORT=y
+CONFIG_USB_COMMON=y
+CONFIG_USB_ARCH_HAS_HCD=y
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEFAULT_PERSIST=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+# CONFIG_USB_OTG is not set
+# CONFIG_USB_OTG_WHITELIST is not set
+# CONFIG_USB_OTG_BLACKLIST_HUB is not set
+CONFIG_USB_MON=y
+# CONFIG_USB_WUSB_CBAF is not set
+
+#
+# USB Host Controller Drivers
+#
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_ISP1362_HCD is not set
+# CONFIG_USB_OHCI_HCD is not set
+# CONFIG_USB_SL811_HCD is not set
+# CONFIG_USB_R8A66597_HCD is not set
+CONFIG_USB_MUSB_HDRC=y
+# CONFIG_USB_MUSB_TUSB6010 is not set
+CONFIG_USB_MUSB_OMAP2PLUS=y
+# CONFIG_USB_MUSB_AM35X is not set
+# CONFIG_USB_MUSB_DSPS is not set
+# CONFIG_USB_MUSB_UX500 is not set
+# CONFIG_USB_INVENTRA_DMA is not set
+CONFIG_MUSB_PIO_ONLY=y
+# CONFIG_USB_RENESAS_USBHS is not set
+
+#
+# USB Device Class drivers
+#
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+# CONFIG_USB_WDM is not set
+# CONFIG_USB_TMC is not set
+
+#
+# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
+#
+
+#
+# also be needed; see USB_STORAGE Help for more info
+#
+
+#
+# USB Imaging devices
+#
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_DWC3 is not set
+# CONFIG_USB_CHIPIDEA is not set
+
+#
+# USB port drivers
+#
+# CONFIG_USB_SERIAL is not set
+
+#
+# USB Miscellaneous drivers
+#
+# CONFIG_USB_EMI62 is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_ADUTUX is not set
+# CONFIG_USB_SEVSEG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_LEGOTOWER is not set
+# CONFIG_USB_LCD is not set
+# CONFIG_USB_LED is not set
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+# CONFIG_USB_CYTHERM is not set
+# CONFIG_USB_IDMOUSE is not set
+# CONFIG_USB_FTDI_ELAN is not set
+# CONFIG_USB_APPLEDISPLAY is not set
+# CONFIG_USB_SISUSBVGA is not set
+# CONFIG_USB_LD is not set
+# CONFIG_USB_TRANCEVIBRATOR is not set
+# CONFIG_USB_IOWARRIOR is not set
+# CONFIG_USB_TEST is not set
+# CONFIG_USB_ISIGHTFW is not set
+# CONFIG_USB_YUREX is not set
+# CONFIG_USB_EZUSB_FX2 is not set
+# CONFIG_USB_HSIC_USB3503 is not set
+CONFIG_USB_PHY=y
+# CONFIG_NOP_USB_XCEIV is not set
+# CONFIG_OMAP_CONTROL_USB is not set
+# CONFIG_OMAP_USB2 is not set
+# CONFIG_OMAP_USB3 is not set
+# CONFIG_SAMSUNG_USBPHY is not set
+# CONFIG_SAMSUNG_USB2PHY is not set
+# CONFIG_SAMSUNG_USB3PHY is not set
+# CONFIG_USB_GPIO_VBUS is not set
+# CONFIG_USB_ISP1301 is not set
+# CONFIG_USB_RCAR_PHY is not set
+# CONFIG_USB_ULPI is not set
+CONFIG_CPCAP_USB=y
+CONFIG_USB_GADGET=y
+# CONFIG_USB_GADGET_DEBUG is not set
+# CONFIG_USB_GADGET_DEBUG_FILES is not set
+# CONFIG_USB_GADGET_DEBUG_FS is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
+
+#
+# USB Peripheral Controller
+#
+# CONFIG_USB_FUSB300 is not set
+# CONFIG_USB_R8A66597 is not set
+# CONFIG_USB_PXA27X is not set
+# CONFIG_USB_MV_UDC is not set
+# CONFIG_USB_MV_U3D is not set
+CONFIG_USB_GADGET_MUSB_HDRC=y
+# CONFIG_USB_M66592 is not set
+# CONFIG_USB_NET2272 is not set
+# CONFIG_USB_DUMMY_HCD is not set
+CONFIG_USB_LIBCOMPOSITE=y
+CONFIG_USB_F_ACM=y
+CONFIG_USB_U_SERIAL=y
+# CONFIG_USB_ZERO is not set
+# CONFIG_USB_ETH is not set
+# CONFIG_USB_G_NCM is not set
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_FUNCTIONFS is not set
+# CONFIG_USB_MASS_STORAGE is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_G_PRINTER is not set
+CONFIG_USB_G_ANDROID=y
+# CONFIG_USB_ANDROID_RNDIS_DWORD_ALIGNED is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
+# CONFIG_USB_G_ACM_MS is not set
+# CONFIG_USB_G_MULTI is not set
+# CONFIG_USB_G_HID is not set
+# CONFIG_USB_G_DBGP is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_UNSAFE_RESUME=y
+# CONFIG_MMC_CLKGATE is not set
+
+#
+# MMC/SD/SDIO Card Drivers
+#
+CONFIG_MMC_BLOCK=y
+CONFIG_MMC_BLOCK_MINORS=32
+CONFIG_MMC_BLOCK_BOUNCE=y
+CONFIG_SDIO_UART=y
+# CONFIG_MMC_TEST is not set
+
+#
+# MMC/SD/SDIO Host Controller Drivers
+#
+# CONFIG_MMC_SDHCI is not set
+# CONFIG_MMC_SDHCI_PXAV3 is not set
+# CONFIG_MMC_SDHCI_PXAV2 is not set
+# CONFIG_MMC_OMAP is not set
+CONFIG_MMC_OMAP_HS=y
+# CONFIG_MMC_SPI is not set
+# CONFIG_MMC_DW is not set
+# CONFIG_MMC_VUB300 is not set
+# CONFIG_MMC_USHC is not set
+# CONFIG_MEMSTICK is not set
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+
+#
+# LED drivers
+#
+CONFIG_LEDS_LM3535=y
+# CONFIG_LEDS_LM3530 is not set
+# CONFIG_LEDS_LM3642 is not set
+# CONFIG_LEDS_PCA9532 is not set
+# CONFIG_LEDS_GPIO is not set
+# CONFIG_LEDS_LP3944 is not set
+# CONFIG_LEDS_LP5521 is not set
+# CONFIG_LEDS_LP5523 is not set
+# CONFIG_LEDS_LP5562 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_PCA9633 is not set
+# CONFIG_LEDS_DAC124S085 is not set
+# CONFIG_LEDS_REGULATOR is not set
+# CONFIG_LEDS_BD2802 is not set
+# CONFIG_LEDS_LT3593 is not set
+# CONFIG_LEDS_RENESAS_TPU is not set
+# CONFIG_LEDS_TCA6507 is not set
+# CONFIG_LEDS_LM355x is not set
+# CONFIG_LEDS_OT200 is not set
+# CONFIG_LEDS_BLINKM is not set
+
+#
+# LED Triggers
+#
+# CONFIG_LEDS_TRIGGERS is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_EDAC is not set
+CONFIG_RTC_LIB=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_SYSTOHC=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+# CONFIG_RTC_DEBUG is not set
+
+#
+# RTC interfaces
+#
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+# CONFIG_RTC_DRV_TEST is not set
+
+#
+# I2C RTC drivers
+#
+# CONFIG_RTC_DRV_DS1307 is not set
+# CONFIG_RTC_DRV_DS1374 is not set
+# CONFIG_RTC_DRV_DS1672 is not set
+# CONFIG_RTC_DRV_DS3232 is not set
+# CONFIG_RTC_DRV_MAX6900 is not set
+# CONFIG_RTC_DRV_RS5C372 is not set
+# CONFIG_RTC_DRV_ISL1208 is not set
+# CONFIG_RTC_DRV_ISL12022 is not set
+# CONFIG_RTC_DRV_X1205 is not set
+# CONFIG_RTC_DRV_PCF8523 is not set
+# CONFIG_RTC_DRV_PCF8563 is not set
+# CONFIG_RTC_DRV_PCF8583 is not set
+# CONFIG_RTC_DRV_M41T80 is not set
+# CONFIG_RTC_DRV_BQ32K is not set
+# CONFIG_RTC_DRV_S35390A is not set
+# CONFIG_RTC_DRV_FM3130 is not set
+# CONFIG_RTC_DRV_RX8581 is not set
+# CONFIG_RTC_DRV_RX8025 is not set
+# CONFIG_RTC_DRV_EM3027 is not set
+# CONFIG_RTC_DRV_RV3029C2 is not set
+
+#
+# SPI RTC drivers
+#
+# CONFIG_RTC_DRV_M41T93 is not set
+# CONFIG_RTC_DRV_M41T94 is not set
+# CONFIG_RTC_DRV_DS1305 is not set
+# CONFIG_RTC_DRV_DS1390 is not set
+# CONFIG_RTC_DRV_MAX6902 is not set
+# CONFIG_RTC_DRV_R9701 is not set
+# CONFIG_RTC_DRV_RS5C348 is not set
+# CONFIG_RTC_DRV_DS3234 is not set
+# CONFIG_RTC_DRV_PCF2123 is not set
+# CONFIG_RTC_DRV_RX4581 is not set
+
+#
+# Platform RTC drivers
+#
+# CONFIG_RTC_DRV_CMOS is not set
+# CONFIG_RTC_DRV_DS1286 is not set
+# CONFIG_RTC_DRV_DS1511 is not set
+# CONFIG_RTC_DRV_DS1553 is not set
+# CONFIG_RTC_DRV_DS1742 is not set
+# CONFIG_RTC_DRV_STK17TA8 is not set
+# CONFIG_RTC_DRV_M48T86 is not set
+# CONFIG_RTC_DRV_M48T35 is not set
+# CONFIG_RTC_DRV_M48T59 is not set
+# CONFIG_RTC_DRV_MSM6242 is not set
+# CONFIG_RTC_DRV_BQ4802 is not set
+# CONFIG_RTC_DRV_RP5C01 is not set
+# CONFIG_RTC_DRV_V3020 is not set
+# CONFIG_RTC_DRV_DS2404 is not set
+
+#
+# on-CPU RTC drivers
+#
+# CONFIG_RTC_DRV_SNVS is not set
+
+#
+# HID Sensor RTC drivers
+#
+# CONFIG_RTC_DRV_HID_SENSOR_TIME is not set
+CONFIG_DMADEVICES=y
+# CONFIG_DMADEVICES_DEBUG is not set
+
+#
+# DMA Devices
+#
+# CONFIG_DW_DMAC is not set
+# CONFIG_TIMB_DMA is not set
+CONFIG_DMA_OMAP=y
+CONFIG_DMA_ENGINE=y
+CONFIG_DMA_VIRTUAL_CHANNELS=y
+CONFIG_DMA_OF=y
+
+#
+# DMA Clients
+#
+# CONFIG_NET_DMA is not set
+# CONFIG_ASYNC_TX_DMA is not set
+# CONFIG_DMATEST is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+# CONFIG_VIRT_DRIVERS is not set
+
+#
+# Virtio drivers
+#
+# CONFIG_VIRTIO_MMIO is not set
+
+#
+# Microsoft Hyper-V guest support
+#
+CONFIG_STAGING=y
+# CONFIG_USBIP_CORE is not set
+# CONFIG_ECHO is not set
+# CONFIG_COMEDI is not set
+# CONFIG_ASUS_OLED is not set
+# CONFIG_RTLLIB is not set
+# CONFIG_R8712U is not set
+# CONFIG_TRANZPORT is not set
+# CONFIG_VT6656 is not set
+# CONFIG_ZSMALLOC is not set
+# CONFIG_BCM_WIMAX is not set
+# CONFIG_FT1000 is not set
+
+#
+# Speakup console speech
+#
+# CONFIG_TOUCHSCREEN_CLEARPAD_TM1217 is not set
+# CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4 is not set
+# CONFIG_STAGING_MEDIA is not set
+
+#
+# Android
+#
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ASHMEM=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_TIMED_OUTPUT=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_ANDROID_INTF_ALARM_DEV=y
+# CONFIG_SYNC is not set
+# CONFIG_USB_WPAN_HCD is not set
+# CONFIG_WIMAX_GDM72XX is not set
+# CONFIG_CED1401 is not set
+# CONFIG_DGRP is not set
+CONFIG_CLKDEV_LOOKUP=y
+CONFIG_HAVE_CLK_PREPARE=y
+CONFIG_COMMON_CLK=y
+
+#
+# Common Clock Framework
+#
+# CONFIG_COMMON_CLK_DEBUG is not set
+# CONFIG_COMMON_CLK_SI5351 is not set
+
+#
+# Hardware Spinlock drivers
+#
+CONFIG_CLKSRC_MMIO=y
+# CONFIG_MAILBOX is not set
+CONFIG_IOMMU_SUPPORT=y
+CONFIG_OF_IOMMU=y
+# CONFIG_OMAP_IOMMU is not set
+
+#
+# Remoteproc drivers
+#
+# CONFIG_STE_MODEM_RPROC is not set
+
+#
+# Rpmsg drivers
+#
+# CONFIG_PM_DEVFREQ is not set
+# CONFIG_EXTCON is not set
+# CONFIG_MEMORY is not set
+# CONFIG_IIO is not set
+# CONFIG_PWM is not set
+CONFIG_IRQCHIP=y
+# CONFIG_IPACK_BUS is not set
+# CONFIG_RESET_CONTROLLER is not set
+
+#
+# File systems
+#
+CONFIG_DCACHE_WORD_ACCESS=y
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_USE_FOR_EXT23=y
+# CONFIG_EXT4_FS_POSIX_ACL is not set
+# CONFIG_EXT4_FS_SECURITY is not set
+CONFIG_EXT4_DEBUG=y
+CONFIG_JBD2=y
+# CONFIG_JBD2_DEBUG is not set
+CONFIG_FS_MBCACHE=y
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+# CONFIG_FS_POSIX_ACL is not set
+CONFIG_FILE_LOCKING=y
+CONFIG_FSNOTIFY=y
+CONFIG_DNOTIFY=y
+CONFIG_INOTIFY_USER=y
+# CONFIG_FANOTIFY is not set
+CONFIG_QUOTA=y
+# CONFIG_QUOTA_NETLINK_INTERFACE is not set
+CONFIG_PRINT_QUOTA_WARNING=y
+# CONFIG_QUOTA_DEBUG is not set
+CONFIG_QUOTA_TREE=y
+# CONFIG_QFMT_V1 is not set
+CONFIG_QFMT_V2=y
+CONFIG_QUOTACTL=y
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+# CONFIG_ISO9660_FS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+# CONFIG_TMPFS_POSIX_ACL is not set
+# CONFIG_TMPFS_XATTR is not set
+# CONFIG_HUGETLB_PAGE is not set
+# CONFIG_CONFIGFS_FS is not set
+CONFIG_MISC_FILESYSTEMS=y
+# CONFIG_ADFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_ECRYPT_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+# CONFIG_LOGFS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_SQUASHFS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_OMFS_FS is not set
+# CONFIG_HPFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX6FS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_PSTORE is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_F2FS_FS is not set
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+CONFIG_NLS_CODEPAGE_437=y
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+CONFIG_NLS_ISO8859_1=y
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_MAC_ROMAN is not set
+# CONFIG_NLS_MAC_CELTIC is not set
+# CONFIG_NLS_MAC_CENTEURO is not set
+# CONFIG_NLS_MAC_CROATIAN is not set
+# CONFIG_NLS_MAC_CYRILLIC is not set
+# CONFIG_NLS_MAC_GAELIC is not set
+# CONFIG_NLS_MAC_GREEK is not set
+# CONFIG_NLS_MAC_ICELAND is not set
+# CONFIG_NLS_MAC_INUIT is not set
+# CONFIG_NLS_MAC_ROMANIAN is not set
+# CONFIG_NLS_MAC_TURKISH is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_DEFAULT_MESSAGE_LOGLEVEL=1
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_STRIP_ASM_SYMS is not set
+# CONFIG_READABLE_ASM is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+# CONFIG_DEBUG_FS is not set
+# CONFIG_HEADERS_CHECK is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+CONFIG_DEBUG_KERNEL=y
+# CONFIG_DEBUG_SHIRQ is not set
+# CONFIG_LOCKUP_DETECTOR is not set
+# CONFIG_PANIC_ON_OOPS is not set
+CONFIG_PANIC_ON_OOPS_VALUE=0
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_SCHEDSTATS is not set
+# CONFIG_TIMER_STATS is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_SLAB is not set
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+# CONFIG_DEBUG_KMEMLEAK is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_DEBUG_ATOMIC_SLEEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+# CONFIG_DEBUG_STACK_USAGE is not set
+# CONFIG_DEBUG_KOBJECT is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+CONFIG_DEBUG_INFO=y
+# CONFIG_DEBUG_INFO_REDUCED is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_WRITECOUNT is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_LIST is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_DEBUG_SG is not set
+# CONFIG_DEBUG_NOTIFIERS is not set
+# CONFIG_DEBUG_CREDENTIALS is not set
+CONFIG_FRAME_POINTER=y
+# CONFIG_BOOT_PRINTK_DELAY is not set
+
+#
+# RCU Debugging
+#
+# CONFIG_PROVE_RCU_DELAY is not set
+# CONFIG_SPARSE_RCU_POINTER is not set
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_TRACE is not set
+# CONFIG_KPROBES_SANITY_TEST is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+# CONFIG_LKDTM is not set
+# CONFIG_NOTIFIER_ERROR_INJECTION is not set
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_LATENCYTOP is not set
+# CONFIG_DEBUG_PAGEALLOC is not set
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_TRACING_SUPPORT=y
+# CONFIG_FTRACE is not set
+# CONFIG_BRANCH_PROFILE_NONE is not set
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_RBTREE_TEST is not set
+# CONFIG_INTERVAL_TREE_TEST is not set
+# CONFIG_DMA_API_DEBUG is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_TEST_STRING_HELPERS is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_STRICT_DEVMEM is not set
+# CONFIG_ARM_UNWIND is not set
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_LL is not set
+CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
+CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
+# CONFIG_ARM_KPROBES_TEST is not set
+# CONFIG_PID_IN_CONTEXTIDR is not set
+
+#
+# Security options
+#
+CONFIG_KEYS=y
+# CONFIG_ENCRYPTED_KEYS is not set
+# CONFIG_KEYS_DEBUG_PROC_KEYS is not set
+# CONFIG_SECURITY_DMESG_RESTRICT is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_PCOMP2=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_USER is not set
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+CONFIG_CRYPTO_WORKQUEUE=y
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AUTHENC=y
+# CONFIG_CRYPTO_TEST is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+# CONFIG_CRYPTO_CCM is not set
+# CONFIG_CRYPTO_GCM is not set
+# CONFIG_CRYPTO_SEQIV is not set
+
+#
+# Block modes
+#
+CONFIG_CRYPTO_CBC=y
+# CONFIG_CRYPTO_CTR is not set
+# CONFIG_CRYPTO_CTS is not set
+CONFIG_CRYPTO_ECB=y
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_CMAC is not set
+CONFIG_CRYPTO_HMAC=y
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_VMAC is not set
+
+#
+# Digest
+#
+CONFIG_CRYPTO_CRC32C=y
+# CONFIG_CRYPTO_CRC32 is not set
+# CONFIG_CRYPTO_GHASH is not set
+# CONFIG_CRYPTO_MD4 is not set
+CONFIG_CRYPTO_MD5=y
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+CONFIG_CRYPTO_SHA1=y
+# CONFIG_CRYPTO_SHA1_ARM is not set
+CONFIG_CRYPTO_SHA256=y
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+
+#
+# Ciphers
+#
+CONFIG_CRYPTO_AES=y
+# CONFIG_CRYPTO_AES_ARM is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+CONFIG_CRYPTO_ARC4=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+CONFIG_CRYPTO_DES=y
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+
+#
+# Compression
+#
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_ZLIB is not set
+# CONFIG_CRYPTO_LZO is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_USER_API_HASH is not set
+# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
+CONFIG_CRYPTO_HW=y
+# CONFIG_CRYPTO_DEV_OMAP_SHAM is not set
+# CONFIG_CRYPTO_DEV_OMAP_AES is not set
+# CONFIG_ASYMMETRIC_KEY_TYPE is not set
+# CONFIG_BINARY_PRINTF is not set
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_IO=y
+CONFIG_CRC_CCITT=y
+CONFIG_CRC16=y
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC32_SELFTEST is not set
+CONFIG_CRC32_SLICEBY8=y
+# CONFIG_CRC32_SLICEBY4 is not set
+# CONFIG_CRC32_SARWATE is not set
+# CONFIG_CRC32_BIT is not set
+# CONFIG_CRC7 is not set
+CONFIG_LIBCRC32C=y
+# CONFIG_CRC8 is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+CONFIG_XZ_DEC=y
+CONFIG_XZ_DEC_X86=y
+CONFIG_XZ_DEC_POWERPC=y
+CONFIG_XZ_DEC_IA64=y
+CONFIG_XZ_DEC_ARM=y
+CONFIG_XZ_DEC_ARMTHUMB=y
+CONFIG_XZ_DEC_SPARC=y
+CONFIG_XZ_DEC_BCJ=y
+# CONFIG_XZ_DEC_TEST is not set
+CONFIG_DECOMPRESS_GZIP=y
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_DQL=y
+CONFIG_NLATTR=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_AVERAGE=y
+# CONFIG_CORDIC is not set
+# CONFIG_DDR is not set
+# CONFIG_VIRTUALIZATION is not set
diff --git a/arch/arm/include/uapi/asm/setup.h b/arch/arm/include/uapi/asm/setup.h
index 979ff401640..fc6f9ea5fba 100644
--- a/arch/arm/include/uapi/asm/setup.h
+++ b/arch/arm/include/uapi/asm/setup.h
@@ -143,6 +143,12 @@ struct tag_memclk {
__u32 fmemclk;
};
+#define ATAG_FLAT_DEV_TREE_ADDRESS 0xf100040A
+struct tag_flat_dev_tree_address {
+ u32 address;
+ u32 size;
+};
+
struct tag {
struct tag_header hdr;
union {
@@ -165,6 +171,8 @@ struct tag {
* DC21285 specific
*/
struct tag_memclk memclk;
+
+ struct tag_flat_dev_tree_address flat_dev_tree;
} u;
};
diff --git a/arch/arm/kernel/atags.h b/arch/arm/kernel/atags.h
index 9edc9692332..dc34649d87d 100644
--- a/arch/arm/kernel/atags.h
+++ b/arch/arm/kernel/atags.h
@@ -6,6 +6,8 @@ static inline void save_atags(struct tag *tags) { }
void convert_to_tag_list(struct tag *tags);
+extern u32 flat_dev_tree_address; /* 32bit physical address */
+
#ifdef CONFIG_ATAGS
struct machine_desc *setup_machine_tags(phys_addr_t __atags_pointer, unsigned int machine_nr);
#else
diff --git a/arch/arm/kernel/atags_parse.c b/arch/arm/kernel/atags_parse.c
index 14512e6931d..44b65f47500 100644
--- a/arch/arm/kernel/atags_parse.c
+++ b/arch/arm/kernel/atags_parse.c
@@ -139,6 +139,29 @@ static int __init parse_tag_cmdline(const struct tag *tag)
__tagtable(ATAG_CMDLINE, parse_tag_cmdline);
+u32 flat_dev_tree_address; /* 32bit physical address */
+
+#ifdef CONFIG_MACH_MINNOW
+/* process Motorola device tree */
+static int __init parse_tag_flat_dev_tree_address(const struct tag *tag)
+{
+ printk(KERN_INFO "flat_dev_tree tag == 0x%08x\n",
+ ATAG_FLAT_DEV_TREE_ADDRESS);
+ printk(KERN_INFO "flat_dev_tree_address (phys) == 0x%08x\n",
+ tag->u.flat_dev_tree.address);
+ printk(KERN_INFO "flat_dev_tree_address (virt) == 0x%p\n",
+ phys_to_virt(tag->u.flat_dev_tree.address));
+ printk(KERN_INFO "flat_dev_tree_size == 0x%08x\n",
+ tag->u.flat_dev_tree.size);
+
+ flat_dev_tree_address = tag->u.flat_dev_tree.address;
+ return 0;
+}
+
+__tagtable(ATAG_FLAT_DEV_TREE_ADDRESS, parse_tag_flat_dev_tree_address);
+
+#endif /* CONFIG_MACH_MINNOW */
+
/*
* Scan the tag table for this tag, and call its parse function.
* The tag table is built by the linker from all the __tagtable
diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c
index b4b1d397592..c698698efd4 100644
--- a/arch/arm/kernel/setup.c
+++ b/arch/arm/kernel/setup.c
@@ -788,6 +788,17 @@ void __init setup_arch(char **cmdline_p)
arm_memblock_init(&meminfo, mdesc);
paging_init(mdesc);
+
+#ifdef CONFIG_MACH_MINNOW
+ if (flat_dev_tree_address) {
+ struct boot_param_header *dt =
+ phys_to_virt(flat_dev_tree_address);
+ if (be32_to_cpu(dt->magic) == OF_DT_HEADER) {
+ pr_info("Use ATAG dev_tree from this point.\n");
+ initial_boot_params = dt;
+ }
+ }
+#endif
request_standard_resources(mdesc);
if (mdesc->restart)
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index f49cd51e162..9b9c99c9f34 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -368,6 +368,12 @@ config MACH_OMAP_3630SDP
default y
select OMAP_PACKAGE_CBP
+config MACH_MINNOW
+ bool "Motorola Minnow Product"
+ depends on ARCH_OMAP3
+ default y
+ select OMAP_PACKAGE_CBP
+
config MACH_TI8168EVM
bool "TI8168 Evaluation Module"
depends on SOC_TI81XX
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 55a9d677768..9fe571fe9de 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -253,6 +253,11 @@ obj-$(CONFIG_MACH_IGEP0020) += board-igep0020.o
obj-$(CONFIG_MACH_TOUCHBOOK) += board-omap3touchbook.o
obj-$(CONFIG_MACH_OMAP_4430SDP) += board-4430sdp.o
obj-$(CONFIG_MACH_OMAP4_PANDA) += board-omap4panda.o
+obj-$(CONFIG_MACH_MINNOW) += board-minnow.o \
+ board-minnow-spi.o \
+ board-minnow-wireless.o \
+ board-minnow-sensors.o \
+ board-minnow-cpcap-client.o
obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o
diff --git a/arch/arm/mach-omap2/board-minnow-cpcap-client.c b/arch/arm/mach-omap2/board-minnow-cpcap-client.c
new file mode 100644
index 00000000000..b2fd25f8ce3
--- /dev/null
+++ b/arch/arm/mach-omap2/board-minnow-cpcap-client.c
@@ -0,0 +1,50 @@
+/*
+ * arch/arm/mach-omap2/board-minnow-cpcap-client.c
+ *
+ * Copyright (C) 2009-2010 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/spi/cpcap.h>
+#include <omap34xx.h>
+
+/*
+ * CPCAP devcies are common for different HW Rev.
+ *
+ */
+
+
+static struct platform_device cpcap_usb_device = {
+ .name = "cpcap_usb",
+ .id = -1,
+ .dev.platform_data = NULL,
+};
+static struct platform_device cpcap_usb_det_device = {
+ .name = "cpcap_usb_det",
+ .id = -1,
+ .dev = {
+ .platform_data = NULL,
+ },
+};
+
+static struct platform_device *cpcap_devices[] = {
+ &cpcap_usb_device,
+ &cpcap_usb_det_device,
+};
+
+
+void __init minnow_cpcap_client_init(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cpcap_devices); i++)
+ cpcap_device_register(cpcap_devices[i]);
+
+}
diff --git a/arch/arm/mach-omap2/board-minnow-sensors.c b/arch/arm/mach-omap2/board-minnow-sensors.c
new file mode 100644
index 00000000000..512c4448f1d
--- /dev/null
+++ b/arch/arm/mach-omap2/board-minnow-sensors.c
@@ -0,0 +1,90 @@
+/*
+ * linux/arch/arm/mach-omap2/board-minnow-sensors.c
+ *
+ * Copyright (C) 2009 Google, Inc.
+ * Copyright (C) 2009-2012 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+
+#if defined(CONFIG_MFD_M4SENSORHUB) || defined(CONFIG_MFD_M4SENSORHUB_MODULE)
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub_gpio.h>
+#endif
+#include <linux/regulator/consumer.h>
+#include <linux/vib-gpio.h>
+
+#include <linux/gpio.h>
+
+#ifdef CONFIG_ARM_OF
+#include <mach/dt_path.h>
+#include <asm/prom.h>
+#endif
+
+static struct regulator *minnow_vibrator_regulator;
+static int minnow_vibrator_initialization(void)
+{
+ struct regulator *reg;
+ reg = regulator_get(NULL, "vvib");
+ if (IS_ERR(reg))
+ return PTR_ERR(reg);
+ minnow_vibrator_regulator = reg;
+ return 0;
+}
+
+static void minnow_vibrator_exit(void)
+{
+ regulator_put(minnow_vibrator_regulator);
+}
+
+static int minnow_vibrator_power_on(void)
+{
+ if (minnow_vibrator_regulator)
+ return regulator_enable(minnow_vibrator_regulator);
+ return 0;
+}
+
+static int minnow_vibrator_power_off(void)
+{
+ if (minnow_vibrator_regulator)
+ return regulator_disable(minnow_vibrator_regulator);
+ return 0;
+}
+
+static struct vib_gpio_platform_data minnow_vib_gpio_data = {
+ .gpio = -1,
+ .max_timeout = 15000,
+ .active_low = 0,
+ .initial_vibrate = 0,
+
+ .init = minnow_vibrator_initialization,
+ .exit = minnow_vibrator_exit,
+ .power_on = minnow_vibrator_power_on,
+ .power_off = minnow_vibrator_power_off,
+};
+
+static struct platform_device minnow_vib_gpio = {
+ .name = "vib-gpio",
+ .id = -1,
+ .dev = {
+ .platform_data = &minnow_vib_gpio_data,
+ },
+};
+
+
+
+/*
+ * Sensors
+ */
+
+void __init minnow_sensors_init(void)
+{
+ platform_device_register(&minnow_vib_gpio);
+}
diff --git a/arch/arm/mach-omap2/board-minnow-spi.c b/arch/arm/mach-omap2/board-minnow-spi.c
new file mode 100644
index 00000000000..a00cb01c0d9
--- /dev/null
+++ b/arch/arm/mach-omap2/board-minnow-spi.c
@@ -0,0 +1,491 @@
+/*
+ * arch/arm/mach-omap2/board-minnow-spi.c
+ *
+ * Copyright (C) 2009-2010 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/platform_device.h>
+#include <linux/power_supply.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/spi/cpcap.h>
+#include <linux/spi/spi.h>
+
+#include "mux.h"
+
+struct cpcap_spi_init_data minnow_cpcap_spi_init[CPCAP_REG_SIZE + 1] = {
+ {CPCAP_REG_ASSIGN1, 0x0101},
+ {CPCAP_REG_ASSIGN2, 0x0000},
+ {CPCAP_REG_ASSIGN3, 0x0000},
+ {CPCAP_REG_ASSIGN4, 0x0000},
+ {CPCAP_REG_ASSIGN5, 0x0000},
+ {CPCAP_REG_ASSIGN6, 0x0000},
+ {CPCAP_REG_UCC1, 0x0000},
+ {CPCAP_REG_PC1, 0x010A},
+ {CPCAP_REG_PC2, 0x0150},
+ {CPCAP_REG_PGC, 0x0000},
+ {CPCAP_REG_SDVSPLL, 0xDB14},
+ {CPCAP_REG_SI2CC1, 0x0281},
+ {CPCAP_REG_Si2CC2, 0x00C4},
+ {CPCAP_REG_S1C1, 0x6438},
+ {CPCAP_REG_S1C2, 0x3838},
+ {CPCAP_REG_S2C1, 0x6434},
+ {CPCAP_REG_S2C2, 0x3C14},
+ {CPCAP_REG_S3C, 0x053B},
+ {CPCAP_REG_S4C1, 0x4920},
+ {CPCAP_REG_S4C2, 0x2020},
+ {CPCAP_REG_S6C, 0x0000},
+ {CPCAP_REG_VWLAN2C, 0x0001},
+ {CPCAP_REG_VUSBINT1C, 0x0029},
+ {CPCAP_REG_VUSBINT2C, 0x0029},
+ {CPCAP_REG_VAUDIOC, 0x0060},
+ {CPCAP_REG_CCCC2, 0x002B},
+ {CPCAP_REG_ADCC1, 0x9000},
+ {CPCAP_REG_ADCC2, 0x4136},
+ {CPCAP_REG_USBC1, 0x1201},
+ {CPCAP_REG_USBC3, 0x7DFB},
+ {CPCAP_REG_UIER2, 0x001F},
+ {CPCAP_REG_UIEF2, 0x001F},
+ {CPCAP_REG_OWDC, 0x0003},
+ {CPCAP_REG_GPIO0, 0x3004},
+ {CPCAP_REG_GPIO1, 0x3004},
+ {CPCAP_REG_GPIO2, 0x3204},
+ {CPCAP_REG_GPIO3, 0x3008},
+ {CPCAP_REG_GPIO4, 0x3204},
+ {CPCAP_REG_GPIO5, 0x3008},
+ {CPCAP_REG_GPIO6, 0x3004},
+ {CPCAP_REG_MDLC, 0x3008},
+ {CPCAP_REG_KLC, 0x0000},
+ {CPCAP_REG_UNUSED, 0x0000},
+};
+
+unsigned short cpcap_regulator_mode_values[CPCAP_NUM_REGULATORS] = {
+ [CPCAP_SW4] = 0x4900,
+ [CPCAP_SW5] = 0x0022,
+ [CPCAP_VCAM] = 0x0003,
+ [CPCAP_VCSI] = 0x0003,
+ [CPCAP_VDAC] = 0x0003,
+ [CPCAP_VDIG] = 0x0003,
+ [CPCAP_VFUSE] = 0x0080,
+ [CPCAP_VHVIO] = 0x0012,
+ [CPCAP_VSDIO] = 0x0003,
+ [CPCAP_VPLL] = 0x0042,
+ [CPCAP_VRF1] = 0x000C,
+ [CPCAP_VRF2] = 0x0003,
+ [CPCAP_VRFREF] = 0x0022,
+ [CPCAP_VWLAN1] = 0x0003,
+ [CPCAP_VWLAN2] = 0x000C,
+ [CPCAP_VSIM] = 0x0003,
+ [CPCAP_VSIMCARD] = 0x1E00,
+ [CPCAP_VVIB] = 0x0001,
+ [CPCAP_VUSB] = 0x000C,
+ [CPCAP_VAUDIO] = 0x0006,
+};
+
+unsigned short cpcap_regulator_off_mode_values[CPCAP_NUM_REGULATORS] = {
+ [CPCAP_SW4] = 0x0000,
+ [CPCAP_SW5] = 0x0000,
+ [CPCAP_VCAM] = 0x0000,
+ [CPCAP_VCSI] = 0x0000,
+ [CPCAP_VDAC] = 0x0000,
+ [CPCAP_VDIG] = 0x0000,
+ [CPCAP_VFUSE] = 0x0000,
+ [CPCAP_VHVIO] = 0x0000,
+ [CPCAP_VSDIO] = 0x0000,
+ [CPCAP_VPLL] = 0x0000,
+ [CPCAP_VRF1] = 0x0000,
+ [CPCAP_VRF2] = 0x0000,
+ [CPCAP_VRFREF] = 0x0000,
+ [CPCAP_VWLAN1] = 0x0000,
+ [CPCAP_VWLAN2] = 0x0000,
+ [CPCAP_VSIM] = 0x0000,
+ [CPCAP_VSIMCARD] = 0x0000,
+ [CPCAP_VVIB] = 0x0000,
+ [CPCAP_VUSB] = 0x0000,
+ [CPCAP_VAUDIO] = 0x0000,
+};
+
+#define CPCAP_GPIO 0
+
+struct regulator_consumer_supply cpcap_sw4_consumers[] = {
+ REGULATOR_SUPPLY("sw4", NULL /* DSP */),
+};
+
+struct regulator_consumer_supply cpcap_sw5_consumers[] = {
+ REGULATOR_SUPPLY("sw5", NULL /* lighting_driver */),
+};
+
+struct regulator_consumer_supply cpcap_vcam_consumers[] = {
+ REGULATOR_SUPPLY("vcam", NULL /* cpcap_cam_device */),
+};
+
+struct regulator_consumer_supply cpcap_vhvio_consumers[] = {
+ REGULATOR_SUPPLY("vhvio", NULL /* lighting_driver */),
+#if 0
+ REGULATOR_SUPPLY("vhvio", NULL /* lighting_driver */),
+ REGULATOR_SUPPLY("vhvio", NULL /* magnetometer */),
+ REGULATOR_SUPPLY("vhvio", NULL /* light sensor */),
+ REGULATOR_SUPPLY("vhvio", NULL /* accelerometer */),
+ REGULATOR_SUPPLY("vhvio", NULL /* display */),
+#endif
+};
+
+struct regulator_consumer_supply cpcap_vsdio_consumers[] = {
+ REGULATOR_SUPPLY("vsdio", NULL),
+};
+
+struct regulator_consumer_supply cpcap_vcsi_consumers[] = {
+ REGULATOR_SUPPLY("vcsi", NULL),
+};
+
+struct regulator_consumer_supply cpcap_vwlan1_consumers[] = {
+ REGULATOR_SUPPLY("vwlan1", NULL /* cpcap_cam_device */),
+};
+
+struct regulator_consumer_supply cpcap_vwlan2_consumers[] = {
+ REGULATOR_SUPPLY("vwlan2", NULL /* sd slot */),
+};
+
+struct regulator_consumer_supply cpcap_vsim_consumers[] = {
+ REGULATOR_SUPPLY("vsim", NULL),
+};
+
+struct regulator_consumer_supply cpcap_vsimcard_consumers[] = {
+ REGULATOR_SUPPLY("vsimcard", NULL),
+};
+
+struct regulator_consumer_supply cpcap_vvib_consumers[] = {
+ REGULATOR_SUPPLY("vvib", NULL /* vibrator */),
+};
+
+struct regulator_consumer_supply cpcap_vusb_consumers[] = {
+ REGULATOR_SUPPLY("vusb", NULL /* accy det */),
+};
+
+struct regulator_consumer_supply cpcap_vaudio_consumers[] = {
+ REGULATOR_SUPPLY("vaudio", NULL /* mic opamp */),
+};
+
+struct regulator_consumer_supply cpcap_vfuse_consumers[] = {
+ REGULATOR_SUPPLY("vfuse", NULL),
+};
+
+struct regulator_consumer_supply cpcap_vrf1_consumers[] = {
+ REGULATOR_SUPPLY("vrf1", NULL),
+};
+
+
+static struct regulator_init_data cpcap_regulator[CPCAP_NUM_REGULATORS] = {
+ [CPCAP_SW4] = {
+ .constraints = {
+ .min_uV = 1000000,
+ .max_uV = 1000000,
+ .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS),
+ .apply_uV = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_sw4_consumers),
+ .consumer_supplies = cpcap_sw4_consumers,
+ },
+ [CPCAP_SW5] = {
+ .constraints = {
+ .min_uV = 5050000,
+ .max_uV = 5050000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .apply_uV = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_sw5_consumers),
+ .consumer_supplies = cpcap_sw5_consumers,
+ },
+ [CPCAP_VCAM] = {
+ .constraints = {
+ .min_uV = 2900000,
+ .max_uV = 2900000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .apply_uV = 1,
+
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vcam_consumers),
+ .consumer_supplies = cpcap_vcam_consumers,
+ },
+ [CPCAP_VCSI] = {
+ .constraints = {
+ .min_uV = 1200000,
+ .max_uV = 1200000,
+ .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS),
+ .apply_uV = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vcsi_consumers),
+ .consumer_supplies = cpcap_vcsi_consumers,
+ },
+ [CPCAP_VDAC] = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .apply_uV = 1,
+ },
+ },
+ [CPCAP_VDIG] = {
+ .constraints = {
+ .min_uV = 1875000,
+ .max_uV = 1875000,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE,
+ .apply_uV = 1,
+ },
+ },
+ [CPCAP_VFUSE] = {
+ .constraints = {
+ .min_uV = 1500000,
+ .max_uV = 3150000,
+ .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS),
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vfuse_consumers),
+ .consumer_supplies = cpcap_vfuse_consumers,
+ },
+ [CPCAP_VHVIO] = {
+ .constraints = {
+ .min_uV = 2775000,
+ .max_uV = 2775000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .always_on = 1,
+ .apply_uV = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vhvio_consumers),
+ .consumer_supplies = cpcap_vhvio_consumers,
+ },
+ [CPCAP_VSDIO] = {
+ .constraints = {
+ .min_uV = 2900000,
+ .max_uV = 2900000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .apply_uV = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vsdio_consumers),
+ .consumer_supplies = cpcap_vsdio_consumers,
+ },
+ [CPCAP_VPLL] = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .apply_uV = 1,
+ },
+ },
+ [CPCAP_VRF1] = {
+ .constraints = {
+ .min_uV = 2500000,
+ .max_uV = 2775000,
+ .valid_ops_mask = 0,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vrf1_consumers),
+ .consumer_supplies = cpcap_vrf1_consumers,
+ },
+ [CPCAP_VRF2] = {
+ .constraints = {
+ .min_uV = 2775000,
+ .max_uV = 2775000,
+ .valid_ops_mask = 0,
+ },
+ },
+ [CPCAP_VRFREF] = {
+ .constraints = {
+ .min_uV = 2775000,
+ .max_uV = 2775000,
+ .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS),
+ .always_on = 1,
+ .apply_uV = 1,
+ },
+ },
+ [CPCAP_VWLAN1] = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS),
+ .apply_uV = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vwlan1_consumers),
+ .consumer_supplies = cpcap_vwlan1_consumers,
+ },
+ [CPCAP_VWLAN2] = {
+ .constraints = {
+ .min_uV = 3000000,
+ .max_uV = 3000000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .apply_uV = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vwlan2_consumers),
+ .consumer_supplies = cpcap_vwlan2_consumers,
+ },
+ [CPCAP_VSIM] = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 2900000,
+ .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS),
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vsim_consumers),
+ .consumer_supplies = cpcap_vsim_consumers,
+ },
+ [CPCAP_VSIMCARD] = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 2900000,
+ .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS),
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vsimcard_consumers),
+ .consumer_supplies = cpcap_vsimcard_consumers,
+ },
+ [CPCAP_VVIB] = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS),
+ .apply_uV = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vvib_consumers),
+ .consumer_supplies = cpcap_vvib_consumers,
+ },
+ [CPCAP_VUSB] = {
+ .constraints = {
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .apply_uV = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vusb_consumers),
+ .consumer_supplies = cpcap_vusb_consumers,
+ },
+ [CPCAP_VAUDIO] = {
+ .constraints = {
+ .min_uV = 2775000,
+ .max_uV = 2775000,
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL |
+ REGULATOR_MODE_STANDBY),
+ .valid_ops_mask = (REGULATOR_CHANGE_VOLTAGE |
+ REGULATOR_CHANGE_STATUS),
+ .apply_uV = 1,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(cpcap_vaudio_consumers),
+ .consumer_supplies = cpcap_vaudio_consumers,
+ },
+};
+
+static struct cpcap_adc_ato minnow_cpcap_adc_ato = {
+ .ato_in = 0x0480,
+ .atox_in = 0,
+ .adc_ps_factor_in = 0x0200,
+ .atox_ps_factor_in = 0,
+ .ato_out = 0,
+ .atox_out = 0,
+ .adc_ps_factor_out = 0,
+ .atox_ps_factor_out = 0,
+ .ichrg_sense_res = 220,
+};
+
+static struct cpcap_leds minnow_cpcap_leds = {
+ .display_led = {
+ .display_reg = CPCAP_REG_MDLC,
+ .display_mask = 0xFFFF,
+ .display_off = 0xFFFA,
+ .display_init = 0xB019,
+ .poll_intvl = 3000,
+ },
+ .button_led = {
+ .button_reg = CPCAP_REG_BLUEC,
+ .button_mask = 0x03FF,
+ .button_on = 0x00F5,
+ .button_off = 0x00F4,
+ },
+ .kpad_led = {
+ .kpad_reg = CPCAP_REG_KLC,
+ .kpad_mask = 0x7FFF,
+ .kpad_on = 0x5FF5,
+ .kpad_off = 0x5FF0,
+ },
+ /* To find LUX value from ALS data,
+ below variables are used.
+ * lux_max - LUX maximum value
+ * lux_minimum - LUX minimum value
+ * als_max - Maximum ALS data
+ * als_min - Minimum ALS data */
+ .als_data = {
+ .lux_max = 5115,
+ .lux_min = 0,
+ .als_max = 1023,
+ .als_min = 0,
+ },
+};
+
+static struct cpcap_platform_data minnow_cpcap_data = {
+ .init = minnow_cpcap_spi_init,
+ .regulator_mode_values = cpcap_regulator_mode_values,
+ .regulator_off_mode_values = cpcap_regulator_off_mode_values,
+ .regulator_init = cpcap_regulator,
+ .adc_ato = &minnow_cpcap_adc_ato,
+ .leds = &minnow_cpcap_leds,
+ .ac_changed = NULL,
+ .batt_changed = NULL,
+ .usb_changed = NULL,
+ .is_umts = 0,
+ .hwcfg = {CPCAP_HWCFG0_NONE, CPCAP_HWCFG1_STBY_GPIO},
+ .irq_gpio = CPCAP_GPIO,
+};
+
+static struct spi_board_info minnow_spi_board_info[] __initdata = {
+ /* CPCAP must be 1st object beacuse it is modified in spi_init() */
+ {
+ .modalias = "cpcap",
+ .bus_num = 1,
+ .chip_select = 0,
+ .max_speed_hz = 3000000,
+ .controller_data = &minnow_cpcap_data,
+ .mode = SPI_CS_HIGH,
+ },
+};
+
+void __init minnow_spi_init(void)
+{
+ int irq;
+ int ret;
+ int i;
+
+ for (i = 0; i < CPCAP_REG_SIZE; i++) {
+ if (minnow_cpcap_spi_init[i].reg == CPCAP_REG_UNUSED)
+ break;
+ }
+ minnow_cpcap_data.init_len = i;
+
+ ret = gpio_request(CPCAP_GPIO, "cpcap-irq");
+ if (ret)
+ return;
+ ret = gpio_direction_input(CPCAP_GPIO);
+ if (ret) {
+ gpio_free(CPCAP_GPIO);
+ return;
+ }
+
+ irq = gpio_to_irq(CPCAP_GPIO);
+ irq_set_irq_type(irq, IRQ_TYPE_EDGE_RISING);
+ minnow_spi_board_info[0].irq = irq;
+
+ spi_register_board_info(minnow_spi_board_info,
+ ARRAY_SIZE(minnow_spi_board_info));
+
+ /* regulator_has_full_constraints(); */
+}
diff --git a/arch/arm/mach-omap2/board-minnow-wireless.c b/arch/arm/mach-omap2/board-minnow-wireless.c
new file mode 100644
index 00000000000..672917cfff3
--- /dev/null
+++ b/arch/arm/mach-omap2/board-minnow-wireless.c
@@ -0,0 +1,104 @@
+/*
+ * linux/arch/arm/mach-omap2/board-minnow-wireless.c
+ *
+ * Copyright (C) 2013 Motorola Mobility, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/wl127x-rfkill.h>
+#include "common.h"
+
+
+
+void change_vio_mode(int source, int value)
+{
+ printk(KERN_DEBUG "Entering change_vio_mode\n");
+#if 0
+ static int bt_request;
+ static int wifi_request;
+
+ /*If have DC-DC converter, skip it*/
+ if (misc_cpcap == NULL)
+ return;
+
+ mutex_lock(&vio_access);
+ if (source == 0)
+ bt_request = value;
+ else if (source == 1)
+ wifi_request = value;
+ else {
+ printk(KERN_ERR "unknown source to vio ");
+ mutex_unlock(&vio_access);
+ return;
+ }
+
+ if (bt_request | wifi_request)
+ cpcap_regacc_write(misc_cpcap, CPCAP_REG_S3C,
+ 0, CPCAP_BIT_SW3STBY);
+ else
+ cpcap_regacc_write(misc_cpcap, CPCAP_REG_S3C,
+ CPCAP_BIT_SW3STBY, CPCAP_BIT_SW3STBY);
+
+ mutex_unlock(&vio_access);
+#endif
+}
+
+
+static int minnow_wl1271_init(void);
+static int minnow_wl1271_release(void);
+static int minnow_wl1271_enable(void);
+static int minnow_wl1271_disable(void);
+
+static struct wl127x_rfkill_platform_data minnow_wl1271_pdata = {
+ .bt_nshutdown_gpio = -1,
+ .fm_enable_gpio = -1,
+ .bt_hw_init = minnow_wl1271_init,
+ .bt_hw_release = minnow_wl1271_release,
+ .bt_hw_enable = minnow_wl1271_enable,
+ .bt_hw_disable = minnow_wl1271_disable,
+};
+
+static int minnow_wl1271_init(void)
+{
+ return 0;
+}
+
+static int minnow_wl1271_release(void)
+{
+ return 0;
+}
+
+static int minnow_wl1271_enable(void)
+{
+ /* FIXME
+ * Change vio mode dynamically if necessary
+ */
+ change_vio_mode(0, 1);
+ return 0;
+}
+
+static int minnow_wl1271_disable(void)
+{
+ /* FIXME
+ * Change vio mode dynamically if necessary
+ */
+ change_vio_mode(0, 0);
+ return 0;
+}
+
+static struct platform_device minnow_wl1271_device = {
+ .name = "wl127x-rfkill",
+ .id = 0,
+ .dev.platform_data = &minnow_wl1271_pdata,
+};
+
+void __init minnow_bt_init(void)
+{
+ /* TODO use device tree once ported */
+ minnow_wl1271_pdata.bt_nshutdown_gpio = 83;
+
+ platform_device_register(&minnow_wl1271_device);
+}
diff --git a/arch/arm/mach-omap2/board-minnow.c b/arch/arm/mach-omap2/board-minnow.c
new file mode 100644
index 00000000000..ef21cfb8956
--- /dev/null
+++ b/arch/arm/mach-omap2/board-minnow.c
@@ -0,0 +1,69 @@
+/*
+ * linux/arch/arm/mach-omap2/board-minnow.c
+ *
+ * Copyright (C) 2013 Motorola Mobility, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <asm/mach/arch.h>
+#include <asm/mach-types.h>
+
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/input/touch_platform.h>
+#include <linux/usb/musb.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/nop-usb-xceiv.h>
+#include "board-minnow.h"
+#include "mux.h"
+#include "common.h"
+#include "dss-common.h"
+#include "board-minnow.h"
+
+#include "sdram-toshiba-hynix-numonyx.h"
+
+static struct of_device_id omap_dt_match_table[] __initdata = {
+ { .compatible = "simple-bus", },
+ { .compatible = "ti,omap-infra", },
+ { }
+};
+
+static const char *omap3_gp_boards_compat[] __initdata = {
+ "mot,omap3-minnow",
+ NULL,
+};
+static void __init minnow_musb_init(void)
+{
+ usb_bind_phy("musb-hdrc.1.auto", 0, "cpcap_usb");
+ usb_musb_init(NULL);
+}
+
+static void __init minnow_init(void)
+{
+ of_platform_populate(NULL, omap_dt_match_table, NULL, NULL);
+
+ omap_sdrc_init(JEDEC_JESD209A_sdrc_params, JEDEC_JESD209A_sdrc_params);
+ omap_minnow_display_init();
+ minnow_spi_init();
+ minnow_bt_init();
+ minnow_sensors_init();
+ minnow_cpcap_client_init();
+ minnow_musb_init();
+}
+
+MACHINE_START(MINNOW, "minnow")
+ .atag_offset = 0x100,
+ .reserve = omap_reserve,
+ .map_io = omap3_map_io,
+ .init_early = omap3630_init_early,
+ .init_irq = omap_intc_of_init,
+ .handle_irq = omap3_intc_handle_irq,
+ .init_machine = minnow_init,
+ .init_late = omap3630_init_late,
+ .init_time = omap3_sync32k_timer_init,
+ .dt_compat = omap3_gp_boards_compat,
+ .restart = omap3xxx_restart,
+MACHINE_END
diff --git a/arch/arm/mach-omap2/board-minnow.h b/arch/arm/mach-omap2/board-minnow.h
new file mode 100644
index 00000000000..9e5ede30395
--- /dev/null
+++ b/arch/arm/mach-omap2/board-minnow.h
@@ -0,0 +1,22 @@
+/*
+ * linux/arch/arm/mach-omap2/board-minnow.h
+ *
+ * Copyright (C) 2013 Motorola Mobility, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/i2c.h>
+
+extern void __init minnow_bt_init(void);
+extern void __init minnow_spi_init(void);
+extern void __init minnow_sensors_init(void);
+extern void __init minnow_touch_init(struct i2c_board_info *i2c_info);
+extern void __init minnow_cpcap_client_init(void);
+
+#if defined(CONFIG_MFD_M4SENSORHUB) || defined(CONFIG_MFD_M4SENSORHUB_MODULE)
+extern int m4sensorhub_stillmode_exit(void);
+/* m4_ctrl == 0 OMAP and 1 Sensorhub */
+extern int m4sensorhub_set_display_control(int m4_ctrl, int gpio_mipi_mux);
+#endif
diff --git a/arch/arm/mach-omap2/dss-common.c b/arch/arm/mach-omap2/dss-common.c
index 393aeefaebb..d850864aa7f 100644
--- a/arch/arm/mach-omap2/dss-common.c
+++ b/arch/arm/mach-omap2/dss-common.c
@@ -249,3 +249,29 @@ void __init omap_4430sdp_display_init_of(void)
omap_display_init(&sdp4430_dss_data);
}
+
+static struct omap_dss_device minnow_panel_lcd_device = {
+ .name = "lcd",
+ .driver_name = "minnow-panel",
+ .type = OMAP_DISPLAY_TYPE_DSI,
+ .data = NULL,
+ .phy.dsi = {
+ .module = 0,
+ },
+ .channel = OMAP_DSS_CHANNEL_LCD,
+};
+
+static struct omap_dss_device *minnow_panel_dss_devices[] = {
+ &minnow_panel_lcd_device,
+};
+
+static struct omap_dss_board_info minnow_panel_dss_data = {
+ .num_devices = ARRAY_SIZE(minnow_panel_dss_devices),
+ .devices = minnow_panel_dss_devices,
+ .default_device = &minnow_panel_lcd_device,
+};
+
+void __init omap_minnow_display_init(void)
+{
+ omap_display_init(&minnow_panel_dss_data);
+}
diff --git a/arch/arm/mach-omap2/dss-common.h b/arch/arm/mach-omap2/dss-common.h
index 915f6fff510..c8d24f42f49 100644
--- a/arch/arm/mach-omap2/dss-common.h
+++ b/arch/arm/mach-omap2/dss-common.h
@@ -11,4 +11,6 @@ void __init omap4_panda_display_init_of(void);
void __init omap_4430sdp_display_init(void);
void __init omap_4430sdp_display_init_of(void);
+void __init omap_minnow_display_init(void);
+
#endif
diff --git a/arch/arm/mach-omap2/sdram-toshiba-hynix-numonyx.h b/arch/arm/mach-omap2/sdram-toshiba-hynix-numonyx.h
new file mode 100644
index 00000000000..c7acdb8312d
--- /dev/null
+++ b/arch/arm/mach-omap2/sdram-toshiba-hynix-numonyx.h
@@ -0,0 +1,66 @@
+/*
+ * SDRC register values for the Toshiba, Hynxi and Numonyx.
+ * These are common SDRC parameters for all vendors since we
+ * use the JEDEC JESD209A parameters.
+ *
+ * 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_TOSHIBA_HYNIX_NUMONYX
+
+#define ARCH_ARM_MACH_OMAP2_SDRAM_TOSHIBA_HYNIX_NUMONYX
+
+#include "sdrc.h"
+
+static struct omap_sdrc_params JEDEC_JESD209A_sdrc_params[] = {
+ [0] = {
+ .rate = 200000000,
+ .actim_ctrla = 0xE2E1B4C6,
+ .actim_ctrlb = 0x00022228,
+ .rfr_ctrl = 0x0005E602,
+ .mr = 0x00000032,
+ },
+ [1] = {
+ .rate = 100000000,
+ .actim_ctrla = 0x7211B485,
+ .actim_ctrlb = 0x00022214,
+ .rfr_ctrl = 0x0002DA02,
+ .mr = 0x00000032,
+ },
+ [2] = {
+ .rate = 166000000,
+ .actim_ctrla = 0xE2E1B4C6,
+ .actim_ctrlb = 0x00022228,
+ .rfr_ctrl = 0x0004DD02,
+ .mr = 0x00000032,
+ },
+ [3] = {
+ .rate = 83000000,
+ .actim_ctrla = 0x7215B485,
+ .actim_ctrlb = 0x00022214,
+ .rfr_ctrl = 0x00025602,
+ .mr = 0x00000032,
+ },
+ [4] = {
+ .rate = 160000000,
+ .actim_ctrla = 0xBA9DB4C6,
+ .actim_ctrlb = 0x00022220,
+ .rfr_ctrl = 0x0004AE02,
+ .mr = 0x00000032,
+ },
+ [5] = {
+ .rate = 80000000,
+ .actim_ctrla = 0x49512284,
+ .actim_ctrlb = 0x0001120C,
+ .rfr_ctrl = 0x23E02,
+ .mr = 0x00000032,
+ },
+ [6] = {
+ .rate = 0
+ },
+};
+
+#endif
+
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index a10297da122..e4203b7eeba 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -1007,3 +1007,4 @@ eco5_bx2 MACH_ECO5_BX2 ECO5_BX2 4572
eukrea_cpuimx28sd MACH_EUKREA_CPUIMX28SD EUKREA_CPUIMX28SD 4573
domotab MACH_DOMOTAB DOMOTAB 4574
pfla03 MACH_PFLA03 PFLA03 4575
+minnow MACH_MINNOW MINNOW 4799
diff --git a/defconfig.mk b/defconfig.mk
new file mode 100644
index 00000000000..946ab782641
--- /dev/null
+++ b/defconfig.mk
@@ -0,0 +1,31 @@
+DEFCONFIGSRC := $(KERNEL_SRCDIR)/arch/arm/configs
+LJAPDEFCONFIGSRC := ${DEFCONFIGSRC}/ext_config
+PRODUCT_SPECIFIC_DEFCONFIGS := $(DEFCONFIGSRC)/$(KERNEL_DEFCONFIG)
+TARGET_DEFCONFIG := $(KERNEL_OUT)/target_defconfig
+
+ifneq ($(KERNEL_EXTRA_CONFIG),)
+PRODUCT_SPECIFIC_DEFCONFIGS += $(LJAPDEFCONFIGSRC)/$(KERNEL_EXTRA_CONFIG).config
+endif
+
+# build eng kernel for eng and userdebug Android variants
+ifneq ($(TARGET_BUILD_VARIANT), user)
+PRODUCT_SPECIFIC_DEFCONFIGS += ${LJAPDEFCONFIGSRC}/eng_bld.config
+
+ifneq ($(KERNEL_EXTRA_CONFIG),)
+PRODUCT_SPECIFIC_DEFCONFIGS += $(LJAPDEFCONFIGSRC)/eng_bld-$(KERNEL_EXTRA_CONFIG).config
+endif
+endif
+
+define do-make-defconfig
+ $(hide) mkdir -p $(dir $(1))
+ ( perl -le 'print "# This file was automatically generated from:\n#\t" . join("\n#\t", @ARGV) . "\n"' $(2) && cat $(2) ) > $(1) || ( rm -f $(1) && false )
+endef
+
+#
+# make combined defconfig file
+#---------------------------------------
+$(TARGET_DEFCONFIG): FORCE $(PRODUCT_SPECIFIC_DEFCONFIGS)
+ $(call do-make-defconfig,$@,$(PRODUCT_SPECIFIC_DEFCONFIGS))
+
+.PHONY: FORCE
+FORCE:
diff --git a/drivers/dma/of-dma.c b/drivers/dma/of-dma.c
index 7aa0864cd48..4dd80703513 100644
--- a/drivers/dma/of-dma.c
+++ b/drivers/dma/of-dma.c
@@ -64,7 +64,7 @@ int of_dma_controller_register(struct device_node *np,
void *data)
{
struct of_dma *ofdma;
- int nbcells;
+ int nbcells = 0;
const __be32 *prop;
if (!np || !of_dma_xlate) {
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index f9a5fd89bc0..bb3895a81c9 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -11,6 +11,14 @@ menuconfig INPUT_TOUCHSCREEN
if INPUT_TOUCHSCREEN
+config TOUCHSCREEN_ATMXT
+ tristate "Atmel mXT Touchscreen Driver"
+ depends on I2C
+ help
+ Say Y here if you have an Atmel mXT touchscreen.
+
+ If unsure, say N.
+
config TOUCHSCREEN_88PM860X
tristate "Marvell 88PM860x touchscreen"
depends on MFD_88PM860X
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 6bfbeab67c9..5f41086e3b1 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -72,3 +72,4 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_ATMXT) += atmxt.o
diff --git a/drivers/input/touchscreen/atmxt.c b/drivers/input/touchscreen/atmxt.c
new file mode 100644
index 00000000000..e872282c258
--- /dev/null
+++ b/drivers/input/touchscreen/atmxt.c
@@ -0,0 +1,3786 @@
+/*
+ * Copyright (C) 2010-2012 Motorola Mobility, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+/* Driver for Atmel maXTouch touchscreens that uses tdat files */
+#include "atmxt.h"
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/device.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/firmware.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#define FAMILY_ID 0x82
+
+static int atmxt_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int atmxt_remove(struct i2c_client *client);
+static int atmxt_suspend(struct i2c_client *client, pm_message_t message);
+static int atmxt_resume(struct i2c_client *client);
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void atmxt_early_suspend(struct early_suspend *handler);
+static void atmxt_late_resume(struct early_suspend *handler);
+#endif
+static int atmxt_init(void);
+static void atmxt_exit(void);
+static void atmxt_free(struct atmxt_driver_data *dd);
+static void atmxt_free_ic_data(struct atmxt_driver_data *dd);
+static void atmxt_set_drv_state(struct atmxt_driver_data *dd,
+ enum atmxt_driver_state state);
+static int atmxt_get_drv_state(struct atmxt_driver_data *dd);
+static void atmxt_set_ic_state(struct atmxt_driver_data *dd,
+ enum atmxt_ic_state state);
+static int atmxt_get_ic_state(struct atmxt_driver_data *dd);
+static int atmxt_verify_pdata(struct atmxt_driver_data *dd);
+static int atmxt_request_tdat(struct atmxt_driver_data *dd);
+static void atmxt_tdat_callback(const struct firmware *tdat, void *context);
+static int atmxt_validate_tdat(const struct firmware *tdat);
+static int atmxt_validate_settings(uint8_t *data, uint32_t size);
+static int atmxt_gpio_init(struct atmxt_driver_data *dd);
+static int atmxt_register_inputs(struct atmxt_driver_data *dd,
+ uint8_t *rdat, int rsize);
+static int atmxt_request_irq(struct atmxt_driver_data *dd);
+static int atmxt_restart_ic(struct atmxt_driver_data *dd);
+static irqreturn_t atmxt_isr(int irq, void *handle);
+static int atmxt_get_info_header(struct atmxt_driver_data *dd);
+static int atmxt_get_object_table(struct atmxt_driver_data *dd);
+static int atmxt_process_object_table(struct atmxt_driver_data *dd);
+static uint8_t *atmxt_get_settings_entry(struct atmxt_driver_data *dd,
+ uint16_t num);
+static int atmxt_copy_platform_data(uint8_t *reg, uint8_t *entry,
+ uint8_t *tsett);
+static int atmxt_check_settings(struct atmxt_driver_data *dd, bool *reset);
+static int atmxt_send_settings(struct atmxt_driver_data *dd, bool save_nvm);
+static int atmxt_recalibrate_ic(struct atmxt_driver_data *dd);
+static int atmxt_start_ic_calibration_fix(struct atmxt_driver_data *dd);
+static int atmxt_verify_ic_calibration_fix(struct atmxt_driver_data *dd);
+static int atmxt_stop_ic_calibration_fix(struct atmxt_driver_data *dd);
+static int atmxt_i2c_write(struct atmxt_driver_data *dd,
+ uint8_t addr_lo, uint8_t addr_hi, uint8_t *buf, int size);
+static int atmxt_i2c_read(struct atmxt_driver_data *dd, uint8_t *buf, int size);
+static int atmxt_save_internal_data(struct atmxt_driver_data *dd);
+static int atmxt_save_data5(struct atmxt_driver_data *dd, uint8_t *entry);
+static int atmxt_save_data6(struct atmxt_driver_data *dd, uint8_t *entry);
+static int atmxt_save_data7(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg);
+static int atmxt_save_data8(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg);
+static int atmxt_save_data9(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg);
+static void atmxt_compute_checksum(struct atmxt_driver_data *dd);
+static void atmxt_compute_partial_checksum(uint8_t *byte1, uint8_t *byte2,
+ uint8_t *low, uint8_t *mid, uint8_t *high);
+static void atmxt_active_handler(struct atmxt_driver_data *dd);
+static int atmxt_process_message(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size);
+static void atmxt_report_touches(struct atmxt_driver_data *dd);
+static void atmxt_release_touches(struct atmxt_driver_data *dd);
+static int atmxt_message_handler6(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size);
+static int atmxt_message_handler9(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size);
+static int atmxt_message_handler42(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size);
+static int atmxt_resume_restart(struct atmxt_driver_data *dd);
+static int atmxt_force_bootloader(struct atmxt_driver_data *dd);
+static bool atmxt_check_firmware_update(struct atmxt_driver_data *dd);
+static int atmxt_validate_firmware(uint8_t *data, uint32_t size);
+static int atmxt_flash_firmware(struct atmxt_driver_data *dd);
+static char *atmxt_msg2str(const uint8_t *msg, uint8_t size);
+static bool atmxt_wait4irq(struct atmxt_driver_data *dd);
+static int atmxt_create_sysfs_files(struct atmxt_driver_data *dd);
+static void atmxt_remove_sysfs_files(struct atmxt_driver_data *dd);
+
+static const struct i2c_device_id atmxt_id[] = {
+ /* This name must match the i2c_board_info name */
+ { ATMXT_I2C_NAME, 0 }, { }
+};
+
+MODULE_DEVICE_TABLE(i2c, atmxt_id);
+
+#ifdef CONFIG_OF
+static struct of_device_id atmxt_match_tbl[] = {
+ { .compatible = "atmel,atmxt-ts" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, atmxt_match_tbl);
+#endif
+
+static struct i2c_driver atmxt_driver = {
+ .driver = {
+ .name = ATMXT_I2C_NAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = of_match_ptr(atmxt_match_tbl),
+#endif
+ },
+ .probe = atmxt_probe,
+ .remove = atmxt_remove,
+ .id_table = atmxt_id,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = atmxt_suspend,
+ .resume = atmxt_resume,
+#endif
+};
+
+#ifdef CONFIG_OF
+static struct touch_platform_data *
+atmxt_of_init(struct i2c_client *client)
+{
+ struct touch_platform_data *pdata;
+ struct device_node *np = client->dev.of_node;
+ const char *fp = NULL;
+
+ pdata = devm_kzalloc(&client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&client->dev, "pdata allocation failure\n");
+ return NULL;
+ }
+
+ of_property_read_string(np, "atmel,atmxt-tdat-filename", &fp);
+
+ pdata->filename = (char *)fp;
+ pdata->gpio_interrupt = of_get_gpio(np, 0);
+ pdata->gpio_reset = of_get_gpio(np, 1);
+
+ return pdata;
+}
+#else
+static inline struct touch_platform_data *
+atmxt_of_init(struct i2c_client *client)
+{
+ return NULL;
+}
+#endif
+
+static int atmxt_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct atmxt_driver_data *dd = NULL;
+ int err = 0;
+ bool debugfail = false;
+
+ printk(KERN_INFO "%s: Driver: %s, Version: %s, Date: %s\n", __func__,
+ ATMXT_I2C_NAME, ATMXT_DRIVER_VERSION, ATMXT_DRIVER_DATE);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ printk(KERN_ERR "%s: Missing I2C adapter support.\n", __func__);
+ err = -ENODEV;
+ goto atmxt_probe_fail;
+ }
+
+ dd = kzalloc(sizeof(struct atmxt_driver_data), GFP_KERNEL);
+ if (dd == NULL) {
+ printk(KERN_ERR "%s: Unable to create driver data.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_probe_fail;
+ }
+
+ dd->drv_stat = ATMXT_DRV_INIT;
+ dd->ic_stat = ATMXT_IC_UNKNOWN;
+ dd->status = 0x0000;
+ dd->client = client;
+
+ if (client->dev.of_node)
+ dd->pdata = atmxt_of_init(client);
+ else
+ dd->pdata = client->dev.platform_data;
+
+ if (!dd->pdata) {
+ printk(KERN_ERR "%s: No platform data found.\n",
+ __func__);
+ err = -EINVAL;
+ goto atmxt_probe_fail;
+ }
+
+ i2c_set_clientdata(client, dd);
+ dd->in_dev = NULL;
+
+ dd->mutex = kzalloc(sizeof(struct mutex), GFP_KERNEL);
+ if (dd->mutex == NULL) {
+ printk(KERN_ERR "%s: Unable to create mutex lock.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_probe_fail;
+ }
+ mutex_init(dd->mutex);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ dd->dbg = kzalloc(sizeof(struct atmxt_debug), GFP_KERNEL);
+ if (dd->dbg == NULL) {
+ printk(KERN_ERR "%s: Unable to create driver debug data.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_probe_fail;
+ }
+ dd->dbg->dbg_lvl = ATMXT_DBG0;
+#endif
+
+ dd->util = kzalloc(sizeof(struct atmxt_util_data), GFP_KERNEL);
+ if (dd->util == NULL) {
+ printk(KERN_ERR "%s: Unable to create touch utility data.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_probe_fail;
+ }
+
+ err = atmxt_verify_pdata(dd);
+ if (err < 0)
+ goto atmxt_probe_fail;
+
+ err = atmxt_gpio_init(dd);
+ if (err < 0)
+ goto atmxt_probe_fail;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ dd->es.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ dd->es.suspend = atmxt_early_suspend;
+ dd->es.resume = atmxt_late_resume;
+ register_early_suspend(&dd->es);
+#endif
+
+ err = atmxt_request_irq(dd);
+ if (err < 0)
+ goto atmxt_unreg_suspend;
+
+ err = atmxt_request_tdat(dd);
+ if (err < 0)
+ goto atmxt_free_irq;
+
+ err = atmxt_create_sysfs_files(dd);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Probe had error %d when creating sysfs files.\n",
+ __func__, err);
+ debugfail = true;
+ }
+
+ goto atmxt_probe_pass;
+
+atmxt_free_irq:
+ free_irq(dd->client->irq, dd);
+atmxt_unreg_suspend:
+ gpio_free(dd->pdata->gpio_reset);
+ gpio_free(dd->pdata->gpio_interrupt);
+atmxt_probe_fail:
+ atmxt_free(dd);
+ printk(KERN_ERR "%s: Probe failed with error code %d.\n",
+ __func__, err);
+ return err;
+
+atmxt_probe_pass:
+ if (debugfail) {
+ printk(KERN_INFO "%s: Probe completed with errors.\n",
+ __func__);
+ } else {
+ printk(KERN_INFO "%s: Probe successful.\n", __func__);
+ }
+ return 0;
+}
+
+static int atmxt_remove(struct i2c_client *client)
+{
+ struct atmxt_driver_data *dd = NULL;
+
+ dd = i2c_get_clientdata(client);
+ if (dd != NULL) {
+ free_irq(dd->client->irq, dd);
+ atmxt_remove_sysfs_files(dd);
+ gpio_free(dd->pdata->gpio_reset);
+ gpio_free(dd->pdata->gpio_interrupt);
+ atmxt_free(dd);
+ }
+
+ i2c_set_clientdata(client, NULL);
+
+ return 0;
+}
+
+static int atmxt_suspend(struct i2c_client *client, pm_message_t message)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd;
+ int drv_state;
+ int ic_state;
+ uint8_t sleep_cmd[2] = {0x00, 0x00};
+
+ dd = i2c_get_clientdata(client);
+ if (dd == NULL) {
+ printk(KERN_ERR "%s: Driver data is missing.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_suspend_no_dd_fail;
+ }
+
+ mutex_lock(dd->mutex);
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Suspending...\n", __func__);
+
+ drv_state = atmxt_get_drv_state(dd);
+ ic_state = atmxt_get_ic_state(dd);
+
+ switch (drv_state) {
+ case ATMXT_DRV_ACTIVE:
+ case ATMXT_DRV_IDLE:
+ switch (ic_state) {
+ case ATMXT_IC_ACTIVE:
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Putting touch IC to sleep...\n", __func__);
+ dd->status = dd->status &
+ ~(1 << ATMXT_FIXING_CALIBRATION);
+ err = atmxt_i2c_write(dd,
+ dd->addr->pwr[0], dd->addr->pwr[1],
+ &(sleep_cmd[0]), 2);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: %s %s %d.\n", __func__,
+ "Failed to put touch IC to sleep",
+ "with error code", err);
+ goto atmxt_suspend_fail;
+ } else {
+ atmxt_set_ic_state(dd, ATMXT_IC_SLEEP);
+ }
+ break;
+ default:
+ printk(KERN_ERR "%s: Driver %s, IC %s suspend.\n",
+ __func__, atmxt_driver_state_string[drv_state],
+ atmxt_ic_state_string[ic_state]);
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "%s: Driver state \"%s\" suspend.\n",
+ __func__, atmxt_driver_state_string[drv_state]);
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Suspend complete.\n", __func__);
+
+atmxt_suspend_fail:
+ mutex_unlock(dd->mutex);
+
+atmxt_suspend_no_dd_fail:
+ return err;
+}
+
+static int atmxt_resume(struct i2c_client *client)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd;
+ int drv_state;
+ int ic_state;
+
+ dd = i2c_get_clientdata(client);
+ if (dd == NULL) {
+ printk(KERN_ERR "%s: Driver data is missing.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_resume_no_dd_fail;
+ }
+
+ mutex_lock(dd->mutex);
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Resuming...\n", __func__);
+
+ drv_state = atmxt_get_drv_state(dd);
+ ic_state = atmxt_get_ic_state(dd);
+
+ switch (drv_state) {
+ case ATMXT_DRV_ACTIVE:
+ case ATMXT_DRV_IDLE:
+ switch (ic_state) {
+ case ATMXT_IC_ACTIVE:
+ printk(KERN_ERR "%s: Driver %s, IC %s resume.\n",
+ __func__, atmxt_driver_state_string[drv_state],
+ atmxt_ic_state_string[ic_state]);
+ break;
+ case ATMXT_IC_SLEEP:
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Waking touch IC...\n", __func__);
+ err = atmxt_i2c_write(dd,
+ dd->addr->pwr[0], dd->addr->pwr[1],
+ &(dd->data->pwr[0]), 2);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to wake touch IC %s %d.\n",
+ __func__, "with error code", err);
+ err = atmxt_resume_restart(dd);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: %s %s %d.\n",
+ __func__,
+ "Failed restart after resume",
+ "with error code", err);
+ }
+ goto atmxt_resume_fail;
+ } else {
+ atmxt_set_ic_state(dd, ATMXT_IC_ACTIVE);
+ }
+ err = atmxt_start_ic_calibration_fix(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: %s %s %d.\n", __func__,
+ "Failed to start calibration fix",
+ "with error code", err);
+ goto atmxt_resume_fail;
+ }
+ err = atmxt_recalibrate_ic(dd);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Recalibration failed %s %d.\n",
+ __func__, "with error code", err);
+ goto atmxt_resume_fail;
+ }
+ atmxt_release_touches(dd);
+ break;
+ default:
+ printk(KERN_ERR "%s: Driver %s, IC %s resume--%s...\n",
+ __func__, atmxt_driver_state_string[drv_state],
+ atmxt_ic_state_string[ic_state], "recovering");
+ err = atmxt_resume_restart(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Recovery failed %s %d.\n",
+ __func__, "with error code", err);
+ goto atmxt_resume_fail;
+ }
+ }
+ break;
+
+ case ATMXT_DRV_INIT:
+ printk(KERN_ERR "%s: Driver state \"%s\" resume.\n",
+ __func__, atmxt_driver_state_string[drv_state]);
+ break;
+
+ default:
+ printk(KERN_ERR "%s: Driver %s, IC %s resume--%s...\n",
+ __func__, atmxt_driver_state_string[drv_state],
+ atmxt_ic_state_string[ic_state], "recovering");
+ err = atmxt_resume_restart(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Recovery failed %s %d.\n",
+ __func__, "with error code", err);
+ goto atmxt_resume_fail;
+ }
+ break;
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Resume complete.\n", __func__);
+
+atmxt_resume_fail:
+ mutex_unlock(dd->mutex);
+
+atmxt_resume_no_dd_fail:
+ return err;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void atmxt_early_suspend(struct early_suspend *handler)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd;
+
+ dd = container_of(handler, struct atmxt_driver_data, es);
+
+ err = atmxt_suspend(dd->client, PMSG_SUSPEND);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Suspend failed with error code %d",
+ __func__, err);
+ }
+
+ return;
+}
+static void atmxt_late_resume(struct early_suspend *handler)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd;
+
+ dd = container_of(handler, struct atmxt_driver_data, es);
+
+ err = atmxt_resume(dd->client);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Resume failed with error code %d",
+ __func__, err);
+ }
+
+ return;
+}
+#endif
+
+static int __init atmxt_init(void)
+{
+ return i2c_add_driver(&atmxt_driver);
+}
+
+static void __exit atmxt_exit(void)
+{
+ i2c_del_driver(&atmxt_driver);
+ return;
+}
+
+module_init(atmxt_init);
+module_exit(atmxt_exit);
+
+static void atmxt_free(struct atmxt_driver_data *dd)
+{
+ if (dd != NULL) {
+ dd->pdata = NULL;
+ dd->client = NULL;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ if (dd->es.link.prev != NULL && dd->es.link.next != NULL)
+ unregister_early_suspend(&dd->es);
+#endif
+
+ if (dd->mutex != NULL) {
+ kfree(dd->mutex);
+ dd->mutex = NULL;
+ }
+
+ if (dd->util != NULL) {
+ kfree(dd->util->data);
+ dd->util->data = NULL;
+ kfree(dd->util);
+ dd->util = NULL;
+ }
+
+ if (dd->in_dev != NULL) {
+ input_unregister_device(dd->in_dev);
+ dd->in_dev = NULL;
+ }
+
+ atmxt_free_ic_data(dd);
+
+ if (dd->rdat != NULL) {
+ kfree(dd->rdat);
+ dd->rdat = NULL;
+ }
+
+ if (dd->dbg != NULL) {
+ kfree(dd->dbg);
+ dd->dbg = NULL;
+ }
+
+ kfree(dd);
+ dd = NULL;
+ }
+
+ return;
+}
+
+static void atmxt_free_ic_data(struct atmxt_driver_data *dd)
+{
+ if (dd->info_blk != NULL) {
+ kfree(dd->info_blk->data);
+ dd->info_blk->data = NULL;
+ kfree(dd->info_blk->msg_id);
+ dd->info_blk->msg_id = NULL;
+ kfree(dd->info_blk);
+ dd->info_blk = NULL;
+ }
+
+ if (dd->nvm != NULL) {
+ kfree(dd->nvm->data);
+ dd->nvm->data = NULL;
+ kfree(dd->nvm);
+ dd->nvm = NULL;
+ }
+
+ if (dd->addr != NULL) {
+ kfree(dd->addr);
+ dd->addr = NULL;
+ }
+
+ if (dd->data != NULL) {
+ kfree(dd->data);
+ dd->data = NULL;
+ }
+
+ return;
+}
+
+static void atmxt_set_drv_state(struct atmxt_driver_data *dd,
+ enum atmxt_driver_state state)
+{
+ printk(KERN_INFO "%s: Driver state %s -> %s\n", __func__,
+ atmxt_driver_state_string[dd->drv_stat],
+ atmxt_driver_state_string[state]);
+ dd->drv_stat = state;
+ return;
+}
+
+static int atmxt_get_drv_state(struct atmxt_driver_data *dd)
+{
+ return dd->drv_stat;
+}
+
+static void atmxt_set_ic_state(struct atmxt_driver_data *dd,
+ enum atmxt_ic_state state)
+{
+ printk(KERN_INFO "%s: IC state %s -> %s\n", __func__,
+ atmxt_ic_state_string[dd->ic_stat],
+ atmxt_ic_state_string[state]);
+ dd->ic_stat = state;
+ return;
+}
+
+static int atmxt_get_ic_state(struct atmxt_driver_data *dd)
+{
+ return dd->ic_stat;
+}
+
+static int atmxt_verify_pdata(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Verifying platform data...\n", __func__);
+
+ if (dd->pdata == NULL) {
+ printk(KERN_ERR "%s: Platform data is missing.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_verify_pdata_fail;
+ }
+
+ if (dd->pdata->gpio_reset == 0) {
+ printk(KERN_ERR "%s: Reset GPIO is invalid.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_verify_pdata_fail;
+ }
+
+ if (dd->pdata->gpio_interrupt == 0) {
+ printk(KERN_ERR "%s: Interrupt GPIO is invalid.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_verify_pdata_fail;
+ }
+
+ if (dd->pdata->filename == NULL) {
+ printk(KERN_ERR "%s: Touch data filename is missing.\n",
+ __func__);
+ err = -ENODATA;
+ goto atmxt_verify_pdata_fail;
+ }
+
+atmxt_verify_pdata_fail:
+ return err;
+}
+
+static int atmxt_request_tdat(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Requesting tdat data...%s\n", __func__,
+ dd->pdata->filename);
+
+ err = request_firmware_nowait(THIS_MODULE,
+ FW_ACTION_HOTPLUG, dd->pdata->filename, &(dd->client->dev),
+ GFP_KERNEL, dd, atmxt_tdat_callback);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to schedule tdat request.\n",
+ __func__);
+ goto atmxt_request_tdat_fail;
+ }
+
+atmxt_request_tdat_fail:
+ return err;
+}
+
+static void atmxt_tdat_callback(const struct firmware *tdat, void *context)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = context;
+ bool icfail = false;
+ uint8_t cur_id = 0x00;
+ uint32_t cur_size = 0;
+ uint8_t *cur_data = NULL;
+ size_t loc = 0;
+
+ mutex_lock(dd->mutex);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ if (dd->status & (1 << ATMXT_WAITING_FOR_TDAT)) {
+ printk(KERN_INFO "%s: Processing new tdat file...\n", __func__);
+ dd->status = dd->status & ~(1 << ATMXT_WAITING_FOR_TDAT);
+ } else {
+ printk(KERN_INFO "%s: Processing %s...\n", __func__,
+ dd->pdata->filename);
+ }
+#else
+ printk(KERN_INFO "%s: Processing %s...\n", __func__,
+ dd->pdata->filename);
+#endif
+
+ if (tdat == NULL) {
+ printk(KERN_ERR "%s: No data received.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_tdat_callback_fail;
+ }
+
+ err = atmxt_validate_tdat(tdat);
+ if (err < 0)
+ goto atmxt_tdat_callback_fail;
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ if (atmxt_get_drv_state(dd) != ATMXT_DRV_INIT) {
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ dd->status = dd->status &
+ ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ }
+ atmxt_set_drv_state(dd, ATMXT_DRV_INIT);
+ }
+
+ if (dd->util->data != NULL) {
+ kfree(dd->util->data);
+ dd->util->data = NULL;
+ dd->util->size = 0;
+ dd->util->tsett = NULL;
+ dd->util->tsett_size = 0;
+ dd->util->fw = NULL;
+ dd->util->fw_size = 0;
+ dd->util->addr[0] = 0x00;
+ dd->util->addr[1] = 0x00;
+ }
+#endif
+
+ dd->util->data = kzalloc(tdat->size * sizeof(uint8_t), GFP_KERNEL);
+ if (dd->util->data == NULL) {
+ printk(KERN_ERR "%s: Unable to copy tdat.\n", __func__);
+ err = -ENOMEM;
+ goto atmxt_tdat_callback_fail;
+ }
+ memcpy(dd->util->data, tdat->data, tdat->size);
+ dd->util->size = tdat->size;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: MCV is 0x%02X.\n", __func__,
+ dd->util->data[loc]);
+ loc++;
+
+ while (loc < dd->util->size) {
+ cur_id = dd->util->data[loc];
+ cur_size = (dd->util->data[loc+3] << 16) |
+ (dd->util->data[loc+2] << 8) |
+ dd->util->data[loc+1];
+ cur_data = &(dd->util->data[loc+4]);
+
+ switch (cur_id) {
+ case 0x00:
+ break;
+
+ case 0x01:
+ dd->util->tsett = cur_data;
+ dd->util->tsett_size = cur_size;
+ break;
+
+ case 0x02:
+ dd->util->fw = cur_data;
+ dd->util->fw_size = cur_size;
+ break;
+
+ case 0x03:
+ if (cur_size == 0 || cur_size % 10 != 0) {
+ printk(KERN_ERR
+ "%s: Abs data format is invalid.\n",
+ __func__);
+ err = -EINVAL;
+ goto atmxt_tdat_callback_fail;
+ }
+
+ err = atmxt_register_inputs(dd, cur_data, cur_size);
+ if (err < 0)
+ goto atmxt_tdat_callback_fail;
+ break;
+
+ case 0x04:
+ if (cur_size < 4) {
+ printk(KERN_ERR
+ "%s: Driver data is too small.\n",
+ __func__);
+ err = -EINVAL;
+ goto atmxt_tdat_callback_fail;
+ }
+ dd->settings = (cur_data[1] << 8) | cur_data[0];
+ dd->util->addr[0] = cur_data[2];
+ dd->util->addr[1] = cur_data[3];
+ dd->client->addr = dd->util->addr[0];
+ break;
+
+ default:
+ printk(KERN_ERR "%s: Record %hu found but not used.\n",
+ __func__, cur_id);
+ break;
+ }
+
+ loc = loc + cur_size + 4;
+ }
+
+ err = atmxt_validate_settings(dd->util->tsett, dd->util->tsett_size);
+ if (err < 0)
+ goto atmxt_tdat_callback_fail;
+
+ err = atmxt_validate_firmware(dd->util->fw, dd->util->fw_size);
+ if (err < 0)
+ goto atmxt_tdat_callback_fail;
+
+ err = atmxt_restart_ic(dd);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Restarting IC failed with error code %d.\n",
+ __func__, err);
+ icfail = true;
+ }
+
+ if (!icfail) {
+ atmxt_set_drv_state(dd, ATMXT_DRV_ACTIVE);
+ dd->status = dd->status | (1 << ATMXT_IRQ_ENABLED_FLAG);
+ enable_irq(dd->client->irq);
+ printk(KERN_INFO "%s: Touch initialization successful.\n",
+ __func__);
+ } else {
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ printk(KERN_INFO
+ "%s: Touch initialization completed with errors.\n",
+ __func__);
+ }
+
+ goto atmxt_tdat_callback_exit;
+
+atmxt_tdat_callback_fail:
+ printk(KERN_ERR "%s: Touch initialization failed with error code %d.\n",
+ __func__, err);
+
+atmxt_tdat_callback_exit:
+ release_firmware(tdat);
+ mutex_unlock(dd->mutex);
+
+ return;
+}
+
+static int atmxt_validate_tdat(const struct firmware *tdat)
+{
+ int err = 0;
+ int length = 0;
+ size_t loc = 0;
+
+ if (tdat->data == NULL || tdat->size == 0) {
+ printk(KERN_ERR "%s: No data found.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_validate_tdat_fail;
+ }
+
+ if (tdat->data[loc] != 0x31) {
+ printk(KERN_ERR "%s: MCV 0x%02X is not supported.\n",
+ __func__, tdat->data[loc]);
+ err = -EINVAL;
+ goto atmxt_validate_tdat_fail;
+ }
+ loc++;
+
+ while (loc < (tdat->size - 3)) {
+ length = (tdat->data[loc+3] << 16) |
+ (tdat->data[loc+2] << 8) |
+ tdat->data[loc+1];
+ if ((loc + length + 4) > tdat->size) {
+ printk(KERN_ERR
+ "%s: Overflow in data at byte %u %s %hu.\n",
+ __func__, loc, "and record", tdat->data[loc]);
+ err = -EOVERFLOW;
+ goto atmxt_validate_tdat_fail;
+ }
+
+ loc = loc + length + 4;
+ }
+
+ if (loc != (tdat->size)) {
+ printk(KERN_ERR "%s: Data is misaligned.\n", __func__);
+ err = -ENOEXEC;
+ goto atmxt_validate_tdat_fail;
+ }
+
+atmxt_validate_tdat_fail:
+ return err;
+}
+
+static int atmxt_validate_settings(uint8_t *data, uint32_t size)
+{
+ int err = 0;
+ uint32_t iter = 0;
+ uint16_t length = 0x0000;
+
+ if (data == NULL || size == 0) {
+ printk(KERN_ERR "%s: No settings data found.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_validate_settings_fail;
+ } else if (size <= 5) {
+ printk(KERN_ERR "%s: Settings data is malformed.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_validate_settings_fail;
+ }
+
+ while (iter < (size - 1)) {
+ length = (data[iter+4] << 8) | data[iter+3];
+ if ((iter + length + 5) > size) {
+ printk(KERN_ERR
+ "%s: Group record overflow on iter %u.\n",
+ __func__, iter);
+ err = -EOVERFLOW;
+ goto atmxt_validate_settings_fail;
+ }
+
+ iter = iter + length + 5;
+ }
+
+ if (iter != size) {
+ printk(KERN_ERR "%s: Group records misaligned.\n", __func__);
+ err = -ENOEXEC;
+ goto atmxt_validate_settings_fail;
+ }
+
+atmxt_validate_settings_fail:
+ return err;
+}
+
+static int atmxt_gpio_init(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ struct gpio touch_gpio[] = {
+ {dd->pdata->gpio_reset, GPIOF_OUT_INIT_LOW, "touch_reset"},
+ {dd->pdata->gpio_interrupt, GPIOF_IN, "touch_irq"},
+ };
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Requesting touch GPIOs...\n", __func__);
+
+ err = gpio_request_array(touch_gpio, ARRAY_SIZE(touch_gpio));
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to request touch GPIOs.\n",
+ __func__);
+ goto atmxt_gpio_init_fail;
+ }
+
+ dd->client->irq = gpio_to_irq(dd->pdata->gpio_interrupt);
+
+atmxt_gpio_init_fail:
+ return err;
+}
+
+static int atmxt_register_inputs(struct atmxt_driver_data *dd,
+ uint8_t *rdat, int rsize)
+{
+ int err = 0;
+ int i = 0;
+ int iter = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Registering inputs...\n", __func__);
+
+ if (dd->rdat != NULL)
+ kfree(dd->rdat);
+
+ dd->rdat = kzalloc(sizeof(struct atmxt_report_data), GFP_KERNEL);
+ if (dd->rdat == NULL) {
+ printk(KERN_ERR "%s: Unable to create report data.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_register_inputs_fail;
+ }
+
+ if (dd->in_dev != NULL)
+ input_unregister_device(dd->in_dev);
+
+ dd->in_dev = input_allocate_device();
+ if (dd->in_dev == NULL) {
+ printk(KERN_ERR "%s: Failed to allocate input device.\n",
+ __func__);
+ err = -ENODEV;
+ goto atmxt_register_inputs_fail;
+ }
+
+ dd->in_dev->name = ATMXT_I2C_NAME;
+ input_set_drvdata(dd->in_dev, dd);
+ set_bit(INPUT_PROP_DIRECT, dd->in_dev->propbit);
+
+ set_bit(EV_ABS, dd->in_dev->evbit);
+ for (i = 0; i < rsize; i += 10) {
+ if (((rdat[i+1] << 8) | rdat[i+0]) != ATMXT_ABS_RESERVED) {
+ input_set_abs_params(dd->in_dev,
+ (rdat[i+1] << 8) | rdat[i+0],
+ (rdat[i+3] << 8) | rdat[i+2],
+ (rdat[i+5] << 8) | rdat[i+4],
+ (rdat[i+7] << 8) | rdat[i+6],
+ (rdat[i+9] << 8) | rdat[i+8]);
+ }
+
+ if (iter < ARRAY_SIZE(dd->rdat->axis)) {
+ dd->rdat->axis[iter] = (rdat[i+1] << 8) | rdat[i+0];
+ iter++;
+ }
+ }
+
+ for (i = iter; i < ARRAY_SIZE(dd->rdat->axis); i++)
+ dd->rdat->axis[i] = ATMXT_ABS_RESERVED;
+
+ input_set_events_per_packet(dd->in_dev,
+ ATMXT_MAX_TOUCHES * (ARRAY_SIZE(dd->rdat->axis) + 1));
+
+ err = input_register_device(dd->in_dev);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to register input device.\n",
+ __func__);
+ err = -ENODEV;
+ input_free_device(dd->in_dev);
+ dd->in_dev = NULL;
+ goto atmxt_register_inputs_fail;
+ }
+
+atmxt_register_inputs_fail:
+ return err;
+}
+
+static int atmxt_request_irq(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Requesting IRQ...\n", __func__);
+
+ err = gpio_get_value(dd->pdata->gpio_interrupt);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Cannot test IRQ line level.\n", __func__);
+ goto atmxt_request_irq_fail;
+ } else if (err == 0) {
+ printk(KERN_ERR
+ "%s: Line already active; cannot request IRQ.\n",
+ __func__);
+ err = -EIO;
+ goto atmxt_request_irq_fail;
+ }
+
+ err = request_threaded_irq(dd->client->irq, NULL, atmxt_isr,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT, ATMXT_I2C_NAME, dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: IRQ request failed.\n", __func__);
+ goto atmxt_request_irq_fail;
+ }
+
+ disable_irq_nosync(dd->client->irq);
+
+atmxt_request_irq_fail:
+ return err;
+}
+
+static int atmxt_restart_ic(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ bool irq_low = false;
+ uint32_t size = 0;
+ bool update_fw = false;
+ bool need_reset = false;
+ int cur_drv_state = 0;
+ bool update_complete = false;
+
+atmxt_restart_ic_start:
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Restarting IC...\n", __func__);
+
+ atmxt_free_ic_data(dd);
+ atmxt_release_touches(dd);
+ irq_low = false;
+ if (atmxt_get_ic_state(dd) != ATMXT_IC_UNKNOWN)
+ atmxt_set_ic_state(dd, ATMXT_IC_UNKNOWN);
+
+ if (!update_fw && !update_complete) {
+ atmxt_dbg(dd, ATMXT_DBG2,
+ "%s: Resetting touch IC...\n", __func__);
+ gpio_set_value(dd->pdata->gpio_reset, 0);
+ udelay(ATMXT_IC_RESET_HOLD_TIME);
+ gpio_set_value(dd->pdata->gpio_reset, 1);
+ }
+
+ irq_low = atmxt_wait4irq(dd);
+ if (!irq_low && !update_fw) {
+ printk(KERN_ERR "%s: Timeout waiting for interrupt.\n",
+ __func__);
+ err = -ETIME;
+ goto atmxt_restart_ic_fail;
+ }
+
+ dd->info_blk = kzalloc(sizeof(struct atmxt_info_block), GFP_KERNEL);
+ if (dd->info_blk == NULL) {
+ printk(KERN_ERR "%s: Unable to create info block data.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_restart_ic_fail;
+ }
+
+ if (update_fw) {
+ if (!irq_low) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Ignored interrupt timeout.\n", __func__);
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Trying bootloader...\n", __func__);
+ update_fw = false;
+ goto atmxt_restart_ic_updatefw_start;
+ }
+
+ err = atmxt_get_info_header(dd);
+ if (err < 0) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Error reaching IC in normal mode. %s\n",
+ __func__, "Trying bootloader...");
+atmxt_restart_ic_updatefw_start:
+ dd->client->addr = dd->util->addr[1];
+ err = atmxt_i2c_read(dd, &(dd->info_blk->header[0]), 3);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to find touch IC.\n",
+ __func__);
+ dd->client->addr = dd->util->addr[0];
+ goto atmxt_restart_ic_fail;
+ }
+
+ atmxt_set_ic_state(dd, ATMXT_IC_BOOTLOADER);
+ if (dd->info_blk->header[0] & 0x20) {
+ printk(KERN_INFO "%s: %s: 0x%02X, %s: 0x%02X\n",
+ __func__,
+ "Bootloader ID", dd->info_blk->header[1],
+ "Bootloader Version", dd->info_blk->header[2]);
+ } else {
+ printk(KERN_INFO "%s: Bootloader ID: 0x%02X\n",
+ __func__, dd->info_blk->header[0] & 0x1F);
+ }
+
+ if ((dd->info_blk->header[0] & 0xC0) == 0x40) {
+ if (update_complete) {
+ printk(KERN_ERR "%s: Firmware CRC failure.\n",
+ __func__);
+ } else {
+ printk(KERN_INFO
+ "%s: Firmware CRC failure; %s.\n",
+ __func__, "going to try reflashing IC");
+ }
+ }
+
+ if (update_complete) {
+ printk(KERN_ERR "%s: %s--%s.\n", __func__,
+ "Still in bootloader mode after reflash",
+ "check firmware image");
+ dd->client->addr = dd->util->addr[0];
+ err = -EINVAL;
+ goto atmxt_restart_ic_fail;
+ }
+
+ cur_drv_state = atmxt_get_drv_state(dd);
+ atmxt_set_drv_state(dd, ATMXT_DRV_REFLASH);
+ err = atmxt_flash_firmware(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to update IC firmware.\n",
+ __func__);
+ dd->client->addr = dd->util->addr[0];
+ atmxt_set_drv_state(dd, cur_drv_state);
+ goto atmxt_restart_ic_fail;
+ }
+
+ atmxt_set_drv_state(dd, cur_drv_state);
+ dd->client->addr = dd->util->addr[0];
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Reflash completed. Re-starting cycle...\n",
+ __func__);
+ update_complete = true;
+ goto atmxt_restart_ic_start;
+ }
+
+ if (dd->info_blk->header[0] != FAMILY_ID) {
+ printk(KERN_ERR "%s: Family ID mismatch:"
+ " expected 0x%02x actual is 0x%02x\n",
+ __func__, FAMILY_ID, dd->info_blk->header[0]);
+ err = -EIO;
+ goto atmxt_restart_ic_fail;
+ }
+
+ atmxt_set_ic_state(dd, ATMXT_IC_PRESENT);
+ printk(KERN_INFO "%s: Family ID: 0x%02X, Variant ID: 0x%02X, " \
+ "Version: 0x%02X, Build: 0x%02X, Matrix: %ux%u, Objects: %u\n",
+ __func__, dd->info_blk->header[0], dd->info_blk->header[1],
+ dd->info_blk->header[2], dd->info_blk->header[3],
+ dd->info_blk->header[4], dd->info_blk->header[5],
+ dd->info_blk->header[6]);
+
+ if (atmxt_get_drv_state(dd) == ATMXT_DRV_INIT) {
+ update_fw = atmxt_check_firmware_update(dd);
+ if (update_fw & update_complete) {
+ printk(KERN_ERR "%s: %s %s %s.\n", __func__,
+ "Platform firmware version",
+ "does not match platform firmware",
+ "after update");
+ update_fw = false;
+ }
+ }
+
+ size = dd->info_blk->header[6] * 6;
+ if (size > 255) {
+ printk(KERN_ERR "%s: Too many objects present.\n", __func__);
+ err = -EOVERFLOW;
+ goto atmxt_restart_ic_fail;
+ }
+ dd->info_blk->size = size;
+
+ dd->info_blk->data = kzalloc(sizeof(uint8_t) * size, GFP_KERNEL);
+ if (dd->info_blk->data == NULL) {
+ printk(KERN_ERR "%s: Unable to create table data.\n", __func__);
+ err = -ENOMEM;
+ goto atmxt_restart_ic_fail;
+ }
+
+ err = atmxt_get_object_table(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error getting object table.\n", __func__);
+ if (update_fw)
+ goto atmxt_restart_updatefw_check;
+ else
+ goto atmxt_restart_ic_fail;
+ }
+
+atmxt_restart_updatefw_check:
+ if (update_fw) {
+ printk(KERN_INFO "%s: Resetting IC to update firmware...\n",
+ __func__);
+ err = atmxt_force_bootloader(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Unable to force flash mode.\n",
+ __func__);
+ goto atmxt_restart_ic_fail;
+ }
+ goto atmxt_restart_ic_start;
+ }
+
+ dd->nvm = kzalloc(sizeof(struct atmxt_nvm), GFP_KERNEL);
+ if (dd->nvm == NULL) {
+ printk(KERN_ERR "%s: Unable to create NVM struct.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_restart_ic_fail;
+ }
+
+ dd->addr = kzalloc(sizeof(struct atmxt_addr), GFP_KERNEL);
+ if (dd->addr == NULL) {
+ printk(KERN_ERR "%s: Unable to create address book.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_restart_ic_fail;
+ }
+
+ dd->data = kzalloc(sizeof(struct atmxt_data), GFP_KERNEL);
+ if (dd->data == NULL) {
+ printk(KERN_ERR "%s: Unable to create data book.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_restart_ic_fail;
+ }
+
+ err = atmxt_process_object_table(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Processing info block failed.\n",
+ __func__);
+ goto atmxt_restart_ic_fail;
+ }
+
+ err = atmxt_save_internal_data(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to save internal data.\n",
+ __func__);
+ goto atmxt_restart_ic_fail;
+ }
+
+ atmxt_compute_checksum(dd);
+
+ err = atmxt_check_settings(dd, &need_reset);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Unable to check/update IC %s.\n",
+ __func__, "with platform settings");
+ goto atmxt_restart_ic_fail;
+ } else if (need_reset) {
+ update_fw = false;
+ update_complete = false;
+ goto atmxt_restart_ic_start;
+ }
+
+ atmxt_set_ic_state(dd, ATMXT_IC_ACTIVE);
+
+ err = atmxt_start_ic_calibration_fix(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to start IC calibration fix.\n",
+ __func__);
+ goto atmxt_restart_ic_fail;
+ }
+
+atmxt_restart_ic_fail:
+ return err;
+}
+
+static irqreturn_t atmxt_isr(int irq, void *handle)
+{
+ struct atmxt_driver_data *dd = handle;
+ int drv_state;
+ int ic_state;
+
+ mutex_lock(dd->mutex);
+
+ drv_state = atmxt_get_drv_state(dd);
+ ic_state = atmxt_get_ic_state(dd);
+
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: IRQ Received -- Driver: %s, IC: %s\n", __func__,
+ atmxt_driver_state_string[drv_state],
+ atmxt_ic_state_string[ic_state]);
+
+ switch (drv_state) {
+ case ATMXT_DRV_ACTIVE:
+ switch (ic_state) {
+ case ATMXT_IC_SLEEP:
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Servicing IRQ during sleep...\n",
+ __func__);
+ case ATMXT_IC_ACTIVE:
+ atmxt_active_handler(dd);
+ break;
+ default:
+ printk(KERN_ERR "%s: Driver %s, IC %s IRQ received.\n",
+ __func__, atmxt_driver_state_string[drv_state],
+ atmxt_ic_state_string[ic_state]);
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ dd->status = dd->status &
+ ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ }
+ break;
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "%s: Driver state \"%s\" IRQ received.\n",
+ __func__, atmxt_driver_state_string[drv_state]);
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ dd->status = dd->status &
+ ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ }
+ break;
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: IRQ Serviced.\n", __func__);
+ mutex_unlock(dd->mutex);
+
+ return IRQ_HANDLED;
+}
+
+static int atmxt_get_info_header(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+
+ err = atmxt_i2c_write(dd, 0x00, 0x00, NULL, 0);
+ if (err < 0)
+ goto atmxt_get_info_header_fail;
+
+ err = atmxt_i2c_read(dd, &(dd->info_blk->header[0]), 7);
+ if (err < 0)
+ goto atmxt_get_info_header_fail;
+
+atmxt_get_info_header_fail:
+ return err;
+}
+
+static int atmxt_get_object_table(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int i = 0; /* Fix Atmel's data order */
+ int top = 0; /* Fix Atmel's data order */
+ int cur = 0; /* Fix Atmel's data order */
+ uint8_t lo_addr = 255; /* Fix Atmel's data order */
+ uint8_t hi_addr = 255; /* Fix Atmel's data order */
+ uint8_t temp[6]; /* Fix Atmel's data order */
+
+ err = atmxt_i2c_write(dd, 0x07, 0x00, NULL, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Unable to set address pointer to object table.\n",
+ __func__);
+ goto atmxt_get_object_table_fail;
+ }
+
+ err = atmxt_i2c_read(dd, dd->info_blk->data, dd->info_blk->size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Object table read failed.\n", __func__);
+ goto atmxt_get_object_table_fail;
+ }
+
+ /* Fix Atmel's data order */
+ while (top < dd->info_blk->size) {
+ for (i = top; i < dd->info_blk->size; i += 6) {
+ if (dd->info_blk->data[i+2] < hi_addr) {
+ lo_addr = dd->info_blk->data[i+1];
+ hi_addr = dd->info_blk->data[i+2];
+ cur = i;
+ } else if ((dd->info_blk->data[i+2] == hi_addr) &&
+ (dd->info_blk->data[i+1] < lo_addr)) {
+ lo_addr = dd->info_blk->data[i+1];
+ hi_addr = dd->info_blk->data[i+2];
+ cur = i;
+ }
+ }
+
+ memcpy(&(temp[0]), &(dd->info_blk->data[top]), 6);
+ memmove(&(dd->info_blk->data[top]),
+ &(dd->info_blk->data[cur]), 6);
+ memcpy(&(dd->info_blk->data[cur]), &(temp[0]), 6);
+
+ lo_addr = 255;
+ hi_addr = 255;
+ top = top + 6;
+ cur = top;
+ }
+
+atmxt_get_object_table_fail:
+ return err;
+}
+
+static int atmxt_process_object_table(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ int j = 0;
+ int k = 0;
+ uint32_t ids = 0;
+ uint32_t nvm_size = 0;
+ int usr_start = 0;
+ bool usr_start_seen = false;
+ uint8_t *tsett = NULL;
+ int id_iter = 0;
+ int nvm_iter = 0;
+
+ ids++;
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ ids += dd->info_blk->data[i+5] * (dd->info_blk->data[i+4] + 1);
+ if (usr_start_seen) {
+ nvm_size += (dd->info_blk->data[i+3] + 1)
+ * (dd->info_blk->data[i+4] + 1);
+ } else if (dd->info_blk->data[i+0] == 38) {
+ usr_start = i;
+ usr_start_seen = true;
+ }
+ }
+
+ if (ids > 255) {
+ printk(KERN_ERR "%s: Too many report IDs used.\n", __func__);
+ err = -EOVERFLOW;
+ goto atmxt_process_object_table_fail;
+ }
+
+ dd->info_blk->msg_id = kzalloc(sizeof(uint8_t) * ids, GFP_KERNEL);
+ if (dd->info_blk->msg_id == NULL) {
+ printk(KERN_ERR "%s: Unable to create ID table.\n", __func__);
+ err = -ENOMEM;
+ goto atmxt_process_object_table_fail;
+ }
+ dd->info_blk->id_size = ids;
+
+ dd->nvm->data = kzalloc(sizeof(uint8_t) * nvm_size, GFP_KERNEL);
+ if (dd->nvm->data == NULL) {
+ printk(KERN_ERR "%s: Unable to create NVM block.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_process_object_table_fail;
+ }
+ dd->nvm->size = nvm_size;
+ dd->nvm->addr[0] = dd->info_blk->data[usr_start+6+1];
+ dd->nvm->addr[1] = dd->info_blk->data[usr_start+6+2];
+
+ dd->info_blk->msg_id[id_iter] = 0;
+ id_iter++;
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ for (j = 0; j <= dd->info_blk->data[i+4]; j++) {
+ for (k = 0; k < dd->info_blk->data[i+5]; k++) {
+ dd->info_blk->msg_id[id_iter] =
+ dd->info_blk->data[i+0];
+ id_iter++;
+ }
+ }
+
+ if (i <= usr_start)
+ continue;
+
+ tsett = atmxt_get_settings_entry(dd, dd->info_blk->data[i+0]);
+ err = atmxt_copy_platform_data(&(dd->nvm->data[nvm_iter]),
+ &(dd->info_blk->data[i+0]), tsett);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to copy platform data.\n",
+ __func__);
+ goto atmxt_process_object_table_fail;
+ }
+
+ nvm_iter += (dd->info_blk->data[i+3] + 1) *
+ (dd->info_blk->data[i+4] + 1);
+ }
+
+atmxt_process_object_table_fail:
+ return err;
+}
+
+static uint8_t *atmxt_get_settings_entry(struct atmxt_driver_data *dd,
+ uint16_t num)
+{
+ uint8_t *entry = NULL;
+ uint32_t iter = 0;
+
+ while (iter < dd->util->tsett_size) {
+ if (num == ((dd->util->tsett[iter+1] << 8) |
+ dd->util->tsett[iter])) {
+ entry = &(dd->util->tsett[iter]);
+ break;
+ } else {
+ iter += 5 + ((dd->util->tsett[iter+4] << 8) |
+ dd->util->tsett[iter+3]);
+ }
+ }
+
+ return entry;
+}
+
+static int atmxt_copy_platform_data(uint8_t *reg, uint8_t *entry,
+ uint8_t *tsett)
+{
+ int err = 0;
+ int i = 0;
+ int iter = 0;
+ int size = 0;
+ uint8_t *data = NULL;
+ int data_size = 0;
+ int obj_size = 0;
+ int obj_inst = 0;
+ uint8_t inst_count = 0x00;
+ uint16_t tsett_size = 0x0000;
+
+ if (tsett == NULL)
+ goto atmxt_copy_platform_data_fail;
+
+ tsett_size = (tsett[4] << 8) | tsett[3];
+ if (tsett[2] == 0) {
+ inst_count = 1;
+ data_size = tsett_size;
+ data = &(tsett[5]);
+ } else {
+ inst_count = tsett[2];
+
+ if ((tsett_size % inst_count) != 0) {
+ printk(KERN_ERR "%s: Settings data unevenly packed.\n",
+ __func__);
+ err = -EINVAL;
+ goto atmxt_copy_platform_data_fail;
+ }
+
+ data_size = tsett_size / inst_count;
+ data = &(tsett[5]);
+ }
+
+ obj_size = entry[3] + 1;
+ obj_inst = entry[4] + 1;
+
+ if (data_size > obj_size)
+ size = obj_size;
+ else
+ size = data_size;
+
+ while ((i < inst_count) && (i < obj_inst)) {
+ memcpy(&(reg[i*obj_size]), &(data[iter]), size);
+ iter += data_size;
+ i++;
+ }
+
+atmxt_copy_platform_data_fail:
+ return err;
+}
+
+static int atmxt_check_settings(struct atmxt_driver_data *dd, bool *reset)
+{
+ int err = 0;
+ uint8_t *msg_buf = NULL;
+ char *contents = NULL;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Checking IC settings...\n", __func__);
+
+ if (dd->data->max_msg_size < 5) {
+ printk(KERN_ERR "%s: Message size is too small.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_check_settings_fail;
+ }
+
+ msg_buf = kzalloc(sizeof(uint8_t) * dd->data->max_msg_size, GFP_KERNEL);
+ if (msg_buf == NULL) {
+ printk(KERN_ERR
+ "%s: Unable to allocate memory for message buffer.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_check_settings_fail;
+ }
+
+ err = atmxt_i2c_write(dd, dd->addr->msg[0], dd->addr->msg[1], NULL, 0);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to set message buffer pointer.\n",
+ __func__);
+ goto atmxt_check_settings_fail;
+ }
+
+ dd->status = dd->status | (1 << ATMXT_SET_MESSAGE_POINTER);
+
+ err = atmxt_i2c_read(dd, msg_buf, dd->data->max_msg_size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to read message.\n", __func__);
+ goto atmxt_check_settings_fail;
+ }
+
+ if (msg_buf[0] <= (dd->info_blk->id_size-1)) {
+ if (dd->info_blk->msg_id[msg_buf[0]] == 6) {
+ if (!(msg_buf[1] & 0x80)) {
+ printk(KERN_ERR "%s: %s: 0x%02X\n", __func__,
+ "Received checksum without reset",
+ msg_buf[1]);
+ }
+ } else {
+ contents = atmxt_msg2str(msg_buf,
+ dd->data->max_msg_size);
+ printk(KERN_ERR "%s: %s--%s %u instead: %s.\n",
+ __func__, "Failed to receive reset message",
+ "received this from Object",
+ dd->info_blk->msg_id[msg_buf[0]], contents);
+ err = -EIO;
+ goto atmxt_check_settings_fail;
+ }
+ } else {
+ contents = atmxt_msg2str(msg_buf, dd->data->max_msg_size);
+ printk(KERN_ERR "%s: %s--%s: %s.\n",
+ __func__, "Failed to receive reset message",
+ "received unknown message instead", contents);
+ err = -EIO;
+ goto atmxt_check_settings_fail;
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: %s 0x%02X%02X%02X, %s 0x%02X%02X%02X.\n", __func__,
+ "Driver checksum is", dd->nvm->chksum[0],
+ dd->nvm->chksum[1], dd->nvm->chksum[2],
+ "IC checksum is", msg_buf[2], msg_buf[3], msg_buf[4]);
+
+ if ((msg_buf[2] == dd->nvm->chksum[0]) &&
+ (msg_buf[3] == dd->nvm->chksum[1]) &&
+ (msg_buf[4] == dd->nvm->chksum[2])) {
+ err = atmxt_process_message(dd,
+ msg_buf, dd->data->max_msg_size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error processing first message.\n",
+ __func__);
+ goto atmxt_check_settings_fail;
+ }
+ *reset = false;
+ } else if (*reset) {
+ printk(KERN_ERR "%s: %s.\n", __func__,
+ "Previous attempt to write platform settings failed");
+ err = -EINVAL;
+ goto atmxt_check_settings_fail;
+ } else {
+ printk(KERN_INFO "%s: Updating IC settings...\n", __func__);
+ err = atmxt_send_settings(dd, true);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to update IC settings.\n",
+ __func__);
+ goto atmxt_check_settings_fail;
+ }
+
+ msleep(500);
+ *reset = true;
+ }
+
+atmxt_check_settings_fail:
+ kfree(msg_buf);
+ kfree(contents);
+ return err;
+}
+
+static int atmxt_send_settings(struct atmxt_driver_data *dd, bool save_nvm)
+{
+ int err = 0;
+ uint8_t nvm_cmd = 0x55;
+
+ err = atmxt_i2c_write(dd, dd->nvm->addr[0], dd->nvm->addr[1],
+ dd->nvm->data, dd->nvm->size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error writing settings to IC.\n",
+ __func__);
+ goto atmxt_send_settings_fail;
+ }
+
+ if (save_nvm) {
+ err = atmxt_i2c_write(dd, dd->addr->nvm[0], dd->addr->nvm[1],
+ &nvm_cmd, sizeof(uint8_t));
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error backing up to NVM.\n",
+ __func__);
+ goto atmxt_send_settings_fail;
+ }
+ }
+
+atmxt_send_settings_fail:
+ return err;
+}
+
+static int atmxt_recalibrate_ic(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ uint8_t cmd = 0x01;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Asking touch IC to recalibrate...\n",
+ __func__);
+
+ err = atmxt_i2c_write(dd, dd->addr->cal[0], dd->addr->cal[1], &cmd, 1);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to send calibrate to touch IC.\n",
+ __func__);
+ goto atmxt_recalibrate_ic_fail;
+ }
+
+atmxt_recalibrate_ic_fail:
+ return err;
+}
+
+static int atmxt_start_ic_calibration_fix(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ uint8_t sett[6] = {0x05, 0x00, 0x00, 0x00, 0x01, 0x80};
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Starting IC calibration fix...\n",
+ __func__);
+
+ sett[1] = dd->data->acq[1];
+ err = atmxt_i2c_write(dd, dd->addr->acq[0], dd->addr->acq[1],
+ &(sett[0]), 6);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to update acquisition settings.\n",
+ __func__);
+ goto atmxt_start_ic_calibration_fix_fail;
+ }
+
+ dd->data->timer = 0;
+ dd->status = dd->status & ~(1 << ATMXT_RECEIVED_CALIBRATION);
+ dd->status = dd->status | (1 << ATMXT_FIXING_CALIBRATION);
+
+atmxt_start_ic_calibration_fix_fail:
+ return err;
+}
+
+static int atmxt_verify_ic_calibration_fix(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ unsigned long toc = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Verifying IC calibration fix...\n",
+ __func__);
+
+ toc = jiffies;
+ if (dd->status & (1 << ATMXT_RECEIVED_CALIBRATION)) {
+ dd->data->timer = 0;
+ dd->status = dd->status & ~(1 << ATMXT_RECEIVED_CALIBRATION);
+ } else if (dd->status & (1 << ATMXT_REPORT_TOUCHES)) {
+ if (dd->data->timer == 0)
+ dd->data->timer = toc;
+
+ if (((toc - dd->data->timer) * 1000 / HZ) >= 2500) {
+ err = atmxt_stop_ic_calibration_fix(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: %s %s.\n", __func__,
+ "Failed to stop",
+ "fixing IC calibration");
+ goto atmxt_verify_ic_calibration_fix_fail;
+ }
+ }
+ }
+
+atmxt_verify_ic_calibration_fix_fail:
+ return err;
+}
+
+static int atmxt_stop_ic_calibration_fix(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Stopping IC calibration fix...\n",
+ __func__);
+
+ err = atmxt_i2c_write(dd, dd->addr->acq[0], dd->addr->acq[1],
+ &(dd->data->acq[0]), 6);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to update acquisition settings.\n",
+ __func__);
+ goto atmxt_stop_ic_calibration_fix_fail;
+ }
+
+ dd->status = dd->status & ~(1 << ATMXT_FIXING_CALIBRATION);
+
+atmxt_stop_ic_calibration_fix_fail:
+ return err;
+}
+
+static int atmxt_i2c_write(struct atmxt_driver_data *dd,
+ uint8_t addr_lo, uint8_t addr_hi, uint8_t *buf, int size)
+{
+ int err = 0;
+ uint8_t *data_out = NULL;
+ int size_out = 0;
+ int i = 0;
+ char *str = NULL;
+
+ dd->status = dd->status & ~(1 << ATMXT_SET_MESSAGE_POINTER);
+
+ size_out = size + 2;
+ data_out = kzalloc(sizeof(uint8_t) * size_out, GFP_KERNEL);
+ if (data_out == NULL) {
+ printk(KERN_ERR "%s: Unable to allocate write memory.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_i2c_write_exit;
+ }
+
+ data_out[0] = addr_lo;
+ data_out[1] = addr_hi;
+ if (buf != NULL && size > 0)
+ memcpy(&(data_out[2]), buf, size);
+
+ for (i = 1; i <= ATMXT_I2C_ATTEMPTS; i++) {
+ err = i2c_master_send(dd->client, data_out, size_out);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: %s %d, failed with error code %d.\n",
+ __func__, "On I2C write attempt", i, err);
+ } else if (err < size_out) {
+ printk(KERN_ERR
+ "%s: %s %d, wrote %d bytes instead of %d.\n",
+ __func__, "On I2C write attempt", i, err,
+ size_out);
+ err = -EBADE;
+ } else {
+ break;
+ }
+
+ udelay(ATMXT_I2C_WAIT_TIME);
+ }
+
+ if (err < 0)
+ printk(KERN_ERR "%s: I2C write failed.\n", __func__);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ if ((dd->dbg->dbg_lvl) >= ATMXT_DBG2)
+ str = atmxt_msg2str(data_out, size_out);
+#endif
+ atmxt_dbg(dd, ATMXT_DBG2, "%s: %s\n", __func__, str);
+ kfree(str);
+
+atmxt_i2c_write_exit:
+ kfree(data_out);
+
+ return err;
+}
+
+static int atmxt_i2c_read(struct atmxt_driver_data *dd, uint8_t *buf, int size)
+{
+ int err = 0;
+ int i = 0;
+ char *str = NULL;
+
+ for (i = 1; i <= ATMXT_I2C_ATTEMPTS; i++) {
+ err = i2c_master_recv(dd->client, buf, size);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: %s %d, failed with error code %d.\n",
+ __func__, "On I2C read attempt", i, err);
+ } else if (err < size) {
+ printk(KERN_ERR
+ "%s: %s %d, received %d bytes instead of %d.\n",
+ __func__, "On I2C read attempt", i, err, size);
+ err = -EBADE;
+ } else {
+ break;
+ }
+
+ udelay(ATMXT_I2C_WAIT_TIME);
+ }
+
+ if (err < 0)
+ printk(KERN_ERR "%s: I2C read failed.\n", __func__);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ if ((dd->dbg->dbg_lvl) >= ATMXT_DBG1)
+ str = atmxt_msg2str(buf, size);
+#endif
+ atmxt_dbg(dd, ATMXT_DBG1, "%s: %s\n", __func__, str);
+ kfree(str);
+
+ return err;
+}
+
+static int atmxt_save_internal_data(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ bool usr_start_seen = false;
+ int nvm_iter = 0;
+ bool chk_5 = false;
+ bool chk_6 = false;
+ bool chk_7 = false;
+ bool chk_8 = false;
+ bool chk_9 = false;
+
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ switch (dd->info_blk->data[i+0]) {
+ case 5:
+ chk_5 = true;
+ err = atmxt_save_data5(dd, &(dd->info_blk->data[i+0]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ case 6:
+ chk_6 = true;
+ err = atmxt_save_data6(dd, &(dd->info_blk->data[i+0]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ case 7:
+ chk_7 = true;
+ err = atmxt_save_data7(dd, &(dd->info_blk->data[i+0]),
+ &(dd->nvm->data[nvm_iter]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ case 8:
+ chk_8 = true;
+ err = atmxt_save_data8(dd, &(dd->info_blk->data[i+0]),
+ &(dd->nvm->data[nvm_iter]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ case 9:
+ chk_9 = true;
+ err = atmxt_save_data9(dd, &(dd->info_blk->data[i+0]),
+ &(dd->nvm->data[nvm_iter]));
+ if (err < 0)
+ goto atmxt_save_internal_data_fail;
+ break;
+
+ case 38:
+ usr_start_seen = true;
+ break;
+
+ default:
+ break;
+ }
+
+ if (usr_start_seen && (dd->info_blk->data[i+0] != 38)) {
+ nvm_iter += (dd->info_blk->data[i+3] + 1) *
+ (dd->info_blk->data[i+4] + 1);
+ }
+ }
+
+ if (!chk_5) {
+ printk(KERN_ERR "%s: Object 5 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+ if (!chk_6) {
+ printk(KERN_ERR "%s: Object 6 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+ if (!chk_7) {
+ printk(KERN_ERR "%s: Object 7 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+ if (!chk_8) {
+ printk(KERN_ERR "%s: Object 8 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+ if (!chk_9) {
+ printk(KERN_ERR "%s: Object 9 is missing.\n", __func__);
+ err = -ENODATA;
+ }
+
+atmxt_save_internal_data_fail:
+ return err;
+}
+
+static int atmxt_save_data5(struct atmxt_driver_data *dd, uint8_t *entry)
+{
+ int err = 0;
+
+ dd->addr->msg[0] = entry[1];
+ dd->addr->msg[1] = entry[2];
+ dd->data->max_msg_size = entry[3];
+
+ return err;
+}
+
+static int atmxt_save_data6(struct atmxt_driver_data *dd, uint8_t *entry)
+{
+ int err = 0;
+
+ if (entry[3] < 2) {
+ printk(KERN_ERR "%s: Command object is too small.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_save_data6_fail;
+ }
+
+ dd->addr->rst[0] = entry[1];
+ dd->addr->rst[1] = entry[2];
+
+ dd->addr->nvm[0] = entry[1] + 1;
+ dd->addr->nvm[1] = entry[2];
+ if (dd->addr->nvm[0] < entry[1])
+ dd->addr->nvm[1]++;
+
+ dd->addr->cal[0] = entry[1] + 2;
+ dd->addr->cal[1] = entry[2];
+ if (dd->addr->cal[0] < entry[1])
+ dd->addr->cal[1]++;
+
+atmxt_save_data6_fail:
+ return err;
+}
+
+static int atmxt_save_data7(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg)
+{
+ int err = 0;
+
+ if (entry[3] < 1) {
+ printk(KERN_ERR "%s: Power object is too small.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_save_data7_fail;
+ }
+
+ dd->addr->pwr[0] = entry[1];
+ dd->addr->pwr[1] = entry[2];
+ dd->data->pwr[0] = reg[0];
+ dd->data->pwr[1] = reg[1];
+
+atmxt_save_data7_fail:
+ return err;
+}
+
+static int atmxt_save_data8(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg)
+{
+ int err = 0;
+
+ if (entry[3] < 9) {
+ printk(KERN_ERR "%s: Acquisition object is too small.\n",
+ __func__);
+ err = -ENODATA;
+ goto atmxt_save_data8_fail;
+ }
+
+ dd->addr->acq[0] = entry[1] + 4;
+ dd->addr->acq[1] = entry[2];
+ if (dd->addr->acq[0] < entry[1])
+ dd->addr->acq[1]++;
+
+ dd->data->acq[0] = reg[4];
+ dd->data->acq[1] = reg[5];
+ dd->data->acq[2] = reg[6];
+ dd->data->acq[3] = reg[7];
+ dd->data->acq[4] = reg[8];
+ dd->data->acq[5] = reg[9];
+
+atmxt_save_data8_fail:
+ return err;
+}
+
+static int atmxt_save_data9(struct atmxt_driver_data *dd,
+ uint8_t *entry, uint8_t *reg)
+{
+ int err = 0;
+ int i = 0;
+
+ for (i = 1; i < dd->info_blk->id_size; i++) {
+ if (dd->info_blk->msg_id[i] == 9) {
+ dd->data->touch_id_offset = i;
+ break;
+ }
+ }
+
+ if (dd->data->touch_id_offset == 0) {
+ printk(KERN_ERR
+ "%s: Touch object has reporting error.\n",
+ __func__);
+ err = -ENODATA;
+ goto atmxt_save_data9_fail;
+ }
+
+ dd->data->res[0] = false;
+ dd->data->res[1] = false;
+
+ if (entry[3] < 21) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Only 10-bit resolution is available.\n", __func__);
+ } else {
+ if (reg[19] >= 0x04)
+ dd->data->res[0] = true;
+
+ if (reg[21] >= 0x04)
+ dd->data->res[1] = true;
+ }
+
+atmxt_save_data9_fail:
+ return err;
+}
+
+static void atmxt_compute_checksum(struct atmxt_driver_data *dd)
+{
+ uint8_t low = 0x00;
+ uint8_t mid = 0x00;
+ uint8_t high = 0x00;
+ uint8_t byte1 = 0x00;
+ uint8_t byte2 = 0x00;
+ uint32_t iter = 0;
+ uint32_t range = 0;
+
+ range = dd->nvm->size - (dd->nvm->size % 2);
+
+ while (iter < range) {
+ byte1 = dd->nvm->data[iter];
+ iter++;
+ byte2 = dd->nvm->data[iter];
+ iter++;
+ atmxt_compute_partial_checksum(&byte1, &byte2,
+ &low, &mid, &high);
+ }
+
+ if ((dd->nvm->size % 2) != 0) {
+ byte1 = dd->nvm->data[iter];
+ byte2 = 0x00;
+ atmxt_compute_partial_checksum(&byte1, &byte2,
+ &low, &mid, &high);
+ }
+
+ dd->nvm->chksum[0] = low;
+ dd->nvm->chksum[1] = mid;
+ dd->nvm->chksum[2] = high;
+
+ return;
+}
+
+static void atmxt_compute_partial_checksum(uint8_t *byte1, uint8_t *byte2,
+ uint8_t *low, uint8_t *mid, uint8_t *high)
+{
+ bool xor_result = false;
+
+ if (*high & 0x80)
+ xor_result = true;
+
+ *high = *high << 1;
+ if (*mid & 0x80)
+ (*high)++;
+
+ *mid = *mid << 1;
+ if (*low & 0x80)
+ (*mid)++;
+
+ *low = *low << 1;
+
+ *low = *low ^ *byte1;
+ *mid = *mid ^ *byte2;
+
+ if (xor_result) {
+ *low = *low ^ 0x1B;
+ *high = *high ^ 0x80;
+ }
+
+ return;
+}
+
+static void atmxt_active_handler(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ uint8_t *msg_buf = NULL;
+ int size = 0;
+ char *contents = NULL;
+ bool msg_fail = false;
+ int last_err = 0;
+ int msg_size = 0;
+ bool inv_msg_seen = false;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Starting active handler...\n", __func__);
+
+ msg_size = dd->data->max_msg_size;
+ size = (dd->rdat->active_touches + 1) * msg_size;
+ if (size == msg_size)
+ size = msg_size * 2;
+
+ msg_buf = kzalloc(sizeof(uint8_t) * size, GFP_KERNEL);
+ if (msg_buf == NULL) {
+ printk(KERN_ERR
+ "%s: Unable to allocate memory for message buffer.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_active_handler_fail;
+ }
+
+ if (!(dd->status & (1 << ATMXT_SET_MESSAGE_POINTER))) {
+ err = atmxt_i2c_write(dd, dd->addr->msg[0], dd->addr->msg[1],
+ NULL, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to set message buffer pointer.\n",
+ __func__);
+ goto atmxt_active_handler_fail;
+ }
+
+ dd->status = dd->status | (1 << ATMXT_SET_MESSAGE_POINTER);
+ }
+
+ err = atmxt_i2c_read(dd, msg_buf, size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to read messages.\n", __func__);
+ goto atmxt_active_handler_fail;
+ }
+
+ if (msg_buf[0] == 0xFF) {
+ contents = atmxt_msg2str(msg_buf, size);
+ printk(KERN_ERR "%s: Received invalid data: %s.\n",
+ __func__, contents);
+ err = -EINVAL;
+ goto atmxt_active_handler_fail;
+ }
+
+ for (i = 0; i < size; i += msg_size) {
+ if (msg_buf[i] == 0xFF) {
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Reached 0xFF message.\n",
+ __func__);
+ inv_msg_seen = true;
+ continue;
+ }
+
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Processing message %d...\n", __func__,
+ (i + 1) / msg_size);
+
+ if (inv_msg_seen) {
+ printk(KERN_INFO "%s: %s %s (%d).\n", __func__,
+ "System response time lagging",
+ "IC report rate",
+ (i / msg_size) + 1);
+ }
+
+ err = atmxt_process_message(dd, &(msg_buf[i]), msg_size);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Processing message %d failed %s %d.\n",
+ __func__, (i / msg_size) + 1,
+ "with error code", err);
+ msg_fail = true;
+ last_err = err;
+ }
+ }
+
+ if (dd->status & (1 << ATMXT_RESTART_REQUIRED)) {
+ printk(KERN_ERR "%s: Restarting touch IC...\n", __func__);
+ dd->status = dd->status & ~(1 << ATMXT_RESTART_REQUIRED);
+ err = atmxt_resume_restart(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to restart touch IC.\n",
+ __func__);
+ goto atmxt_active_handler_fail;
+ } else if (msg_fail) {
+ err = last_err;
+ goto atmxt_active_handler_fail;
+ } else {
+ goto atmxt_active_handler_pass;
+ }
+ }
+
+ if (dd->status & (1 << ATMXT_FIXING_CALIBRATION)) {
+ err = atmxt_verify_ic_calibration_fix(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Unable to verify IC calibration.\n",
+ __func__);
+ goto atmxt_active_handler_fail;
+ }
+ }
+
+ if (dd->status & (1 << ATMXT_REPORT_TOUCHES)) {
+ atmxt_report_touches(dd);
+ dd->status = dd->status & ~(1 << ATMXT_REPORT_TOUCHES);
+ }
+
+ if (msg_fail) {
+ err = last_err;
+ goto atmxt_active_handler_fail;
+ }
+
+ goto atmxt_active_handler_pass;
+
+atmxt_active_handler_fail:
+ printk(KERN_ERR "%s: Touch active handler failed with error code %d.\n",
+ __func__, err);
+
+atmxt_active_handler_pass:
+ kfree(msg_buf);
+ kfree(contents);
+ return;
+}
+
+static int atmxt_process_message(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size)
+{
+ int err = 0;
+ char *contents = NULL;
+
+ if (msg[0] <= (dd->info_blk->id_size-1)) {
+ switch (dd->info_blk->msg_id[msg[0]]) {
+ case 6:
+ err = atmxt_message_handler6(dd, msg, size);
+ break;
+ case 9:
+ err = atmxt_message_handler9(dd, msg, size);
+ break;
+ case 42:
+ err = atmxt_message_handler42(dd, msg, size);
+ break;
+ default:
+ contents = atmxt_msg2str(msg, size);
+ printk(KERN_ERR "%s: Object %u sent this: %s.\n",
+ __func__, dd->info_blk->msg_id[msg[0]],
+ contents);
+ break;
+ }
+ } else {
+ contents = atmxt_msg2str(msg, size);
+ printk(KERN_ERR "%s: Received unknown message: %s.\n",
+ __func__, contents);
+ }
+
+ if (err < 0)
+ printk(KERN_ERR "%s: Message processing failed.\n", __func__);
+
+ kfree(contents);
+ return err;
+}
+
+static void atmxt_report_touches(struct atmxt_driver_data *dd)
+{
+ int i = 0;
+ int j = 0;
+ int rval = 0;
+ int id = 0;
+ int x = 0;
+ int y = 0;
+ int p = 0;
+ int w = 0;
+
+ dd->rdat->active_touches = 0;
+
+ for (i = 0; i < ATMXT_MAX_TOUCHES; i++) {
+ if (!(dd->rdat->tchdat[i].active))
+ continue;
+
+ id = dd->rdat->tchdat[i].id;
+ x = dd->rdat->tchdat[i].x;
+ y = dd->rdat->tchdat[i].y;
+ p = dd->rdat->tchdat[i].p;
+ w = dd->rdat->tchdat[i].w;
+
+ dd->rdat->active_touches++;
+
+ atmxt_dbg(dd, ATMXT_DBG1, "%s: ID=%d, X=%d, Y=%d, P=%d, W=%d\n",
+ __func__, id, x, y, p, w);
+
+ for (j = 0; j < ARRAY_SIZE(dd->rdat->axis); j++) {
+ switch (j) {
+ case 0:
+ rval = x;
+ break;
+ case 1:
+ rval = y;
+ break;
+ case 2:
+ rval = p;
+ break;
+ case 3:
+ rval = w;
+ break;
+ case 4:
+ rval = id;
+ break;
+ }
+ if (dd->rdat->axis[j] != ATMXT_ABS_RESERVED) {
+ input_report_abs(dd->in_dev,
+ dd->rdat->axis[j], rval);
+ }
+ }
+ input_mt_sync(dd->in_dev);
+ }
+
+ if (dd->rdat->active_touches == 0)
+ input_mt_sync(dd->in_dev);
+
+ input_sync(dd->in_dev);
+
+ return;
+}
+
+static void atmxt_release_touches(struct atmxt_driver_data *dd)
+{
+ int i = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG1, "%s: Releasing all touches...\n", __func__);
+
+ for (i = 0; i < ATMXT_MAX_TOUCHES; i++)
+ dd->rdat->tchdat[i].active = false;
+
+ atmxt_report_touches(dd);
+ dd->status = dd->status & ~(1 << ATMXT_REPORT_TOUCHES);
+
+ return;
+}
+
+static int atmxt_message_handler6(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Handling message type 6...\n", __func__);
+
+ if (size < 5) {
+ printk(KERN_ERR "%s: Message size is too small.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_message_handler6_fail;
+ }
+
+ if (msg[1] & 0x80) {
+ printk(KERN_INFO "%s: Touch IC reset complete.\n", __func__);
+ dd->data->last_stat = 0x00;
+ }
+
+ if ((msg[1] & 0x40) && !(dd->data->last_stat & 0x40)) {
+ printk(KERN_ERR "%s: Acquisition cycle overflow.\n", __func__);
+ } else if (!(msg[1] & 0x40) && (dd->data->last_stat & 0x40)) {
+ printk(KERN_INFO "%s: Acquisition cycle now normal.\n",
+ __func__);
+ }
+
+ if ((msg[1] & 0x20) && !(dd->data->last_stat & 0x20)) {
+ printk(KERN_ERR "%s: Signal error in IC acquisition.\n",
+ __func__);
+ } else if (!(msg[1] & 0x20) && (dd->data->last_stat & 0x20)) {
+ printk(KERN_INFO "%s: IC acquisition signal now in range.\n",
+ __func__);
+ }
+
+ if ((msg[1] & 0x10) && !(dd->data->last_stat & 0x10)) {
+ printk(KERN_INFO "%s: Touch IC is calibrating.\n", __func__);
+ dd->status = dd->status | (1 << ATMXT_RECEIVED_CALIBRATION);
+ } else if (!(msg[1] & 0x10) && (dd->data->last_stat & 0x10)) {
+ printk(KERN_INFO "%s: Touch IC calibration complete.\n",
+ __func__);
+ dd->status = dd->status | (1 << ATMXT_RECEIVED_CALIBRATION);
+ }
+
+ if (msg[1] & 0x08) {
+ printk(KERN_ERR "%s: Hardware configuration error--%s.\n",
+ __func__, "check platform settings");
+ dd->data->last_stat = dd->data->last_stat & 0xF7;
+ } else if (!(msg[1] & 0x08) && (dd->data->last_stat & 0x08)) {
+ printk(KERN_INFO
+ "%s: Hardware configuration error corrected.\n",
+ __func__);
+ }
+
+ if (msg[1] & 0x04) {
+ printk(KERN_ERR "%s: IC reports I2C communication error.\n",
+ __func__);
+ }
+
+ if (msg[1] == dd->data->last_stat) {
+ printk(KERN_INFO "%s: Received checksum 0x%02X%02X%02X.\n",
+ __func__, msg[2], msg[3], msg[4]);
+ }
+
+ dd->data->last_stat = msg[1];
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ if (dd->status & (1 << ATMXT_IGNORE_CHECKSUM))
+ goto atmxt_message_handler6_fail;
+#endif
+
+ if ((msg[2] != dd->nvm->chksum[0]) ||
+ (msg[3] != dd->nvm->chksum[1]) ||
+ (msg[4] != dd->nvm->chksum[2])) {
+ if (!(dd->status & (1 << ATMXT_CHECKSUM_FAILED))) {
+ printk(KERN_ERR "%s: IC settings checksum fail.\n",
+ __func__);
+ dd->status = dd->status | (1 << ATMXT_RESTART_REQUIRED);
+ dd->status = dd->status | (1 << ATMXT_CHECKSUM_FAILED);
+ } else {
+ printk(KERN_ERR "%s: IC settings checksum fail. %s\n",
+ __func__, "Sending settings (no backup)...");
+ err = atmxt_send_settings(dd, false);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to update IC settings.\n",
+ __func__);
+ goto atmxt_message_handler6_fail;
+ }
+ }
+ }
+
+atmxt_message_handler6_fail:
+ return err;
+}
+
+static int atmxt_message_handler9(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size)
+{
+ int err = 0;
+ uint8_t tchidx = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Handling message type 9...\n", __func__);
+
+ if (size < 7) {
+ printk(KERN_ERR "%s: Message size is too small.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_message_handler9_fail;
+ }
+
+ tchidx = msg[0] - dd->data->touch_id_offset;
+ if (tchidx >= ARRAY_SIZE(dd->rdat->tchdat)) {
+ printk(KERN_ERR "%s: Touch %hu is unsupported.\n",
+ __func__, tchidx);
+ err = -EOVERFLOW;
+ goto atmxt_message_handler9_fail;
+ }
+
+ dd->status = dd->status | (1 << ATMXT_REPORT_TOUCHES);
+
+ dd->rdat->tchdat[tchidx].id = tchidx;
+
+ dd->rdat->tchdat[tchidx].x = (msg[2] << 4) | ((msg[4] & 0xF0) >> 4);
+ if (!(dd->data->res[0]))
+ dd->rdat->tchdat[tchidx].x = dd->rdat->tchdat[tchidx].x >> 2;
+
+ dd->rdat->tchdat[tchidx].y = (msg[3] << 4) | (msg[4] & 0x0F);
+ if (!(dd->data->res[1]))
+ dd->rdat->tchdat[tchidx].y = dd->rdat->tchdat[tchidx].y >> 2;
+
+ dd->rdat->tchdat[tchidx].p = msg[6];
+ dd->rdat->tchdat[tchidx].w = msg[5];
+
+ if (((msg[1] & 0x40) && (msg[1] & 0x20)) ||
+ ((msg[1] & 0x40) && (msg[1] & 0x02)) ||
+ ((msg[1] & 0x20) && (msg[1] & 0x02))) {
+ printk(KERN_ERR "%s: System too slow %s 0x%02X.\n",
+ __func__, "to see all touch events for report id",
+ msg[0]);
+ }
+
+ if (msg[1] & 0x22) {
+ atmxt_dbg(dd, ATMXT_DBG1, "%s: Touch ID %hu released.\n",
+ __func__, tchidx);
+ dd->rdat->tchdat[tchidx].active = false;
+ } else {
+ dd->rdat->tchdat[tchidx].active = true;
+ }
+
+ if (msg[1] & 0x02) {
+ printk(KERN_INFO "%s: Touch ID %hu suppressed.\n",
+ __func__, tchidx);
+ }
+
+atmxt_message_handler9_fail:
+ return err;
+}
+
+static int atmxt_message_handler42(struct atmxt_driver_data *dd,
+ uint8_t *msg, uint8_t size)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Handling message type 42...\n", __func__);
+
+ if (size < 2) {
+ printk(KERN_ERR "%s: Message size is too small.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_message_handler42_fail;
+ }
+
+ if (msg[1] & 0x01) {
+ printk(KERN_ERR "%s: Touch suppression is active.\n",
+ __func__);
+ } else {
+ printk(KERN_INFO "%s: Touch suppression is disabled.\n",
+ __func__);
+ }
+
+atmxt_message_handler42_fail:
+ return err;
+}
+
+static int atmxt_resume_restart(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+
+ atmxt_dbg(dd, ATMXT_DBG3, "%s: Resume restarting IC...\n", __func__);
+
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ dd->status = dd->status & ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ }
+
+ err = atmxt_restart_ic(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to restart the touch IC.\n",
+ __func__);
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ goto atmxt_resume_restart_fail;
+ }
+
+ if (!(dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG))) {
+ if (atmxt_get_drv_state(dd) != ATMXT_DRV_ACTIVE)
+ atmxt_set_drv_state(dd, ATMXT_DRV_ACTIVE);
+ dd->status = dd->status | (1 << ATMXT_IRQ_ENABLED_FLAG);
+ enable_irq(dd->client->irq);
+ }
+
+atmxt_resume_restart_fail:
+ return err;
+}
+
+static int atmxt_force_bootloader(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ uint8_t cmd = 0x00;
+ bool chg_used = false;
+ uint8_t chg_cmd[2] = {0x00, 0x00};
+ uint8_t rst[2] = {0x00, 0x00};
+
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ if (dd->info_blk->data[i+0] == 18) {
+ if (dd->info_blk->data[i+3] < 1) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Comm is too small--%s.\n",
+ __func__, "will pool instead");
+ goto atmxt_force_bootloader_check_reset;
+ }
+ chg_cmd[0] = dd->info_blk->data[i+1] + 1;
+ chg_cmd[1] = dd->info_blk->data[i+2];
+ if (chg_cmd[0] < dd->info_blk->data[i+1])
+ chg_cmd[1]++;
+ break;
+ }
+ }
+
+ if ((chg_cmd[0] == 0x00) && (chg_cmd[1] == 0x00)) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: No interrupt force available--%s.\n",
+ __func__, "will pool instead");
+ goto atmxt_force_bootloader_check_reset;
+ }
+
+ cmd = 0x02;
+ err = atmxt_i2c_write(dd, chg_cmd[0], chg_cmd[1], &cmd, 1);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Unable to force interrupt low--%s.\n",
+ __func__, "will poll instead");
+ } else {
+ chg_used = true;
+ }
+
+atmxt_force_bootloader_check_reset:
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ if (dd->info_blk->data[i+0] == 6) {
+ rst[0] = dd->info_blk->data[i+1];
+ rst[1] = dd->info_blk->data[i+2];
+ break;
+ }
+ }
+
+ if ((rst[0] == 0x00) && (rst[1] == 0x00)) {
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: No soft reset available--%s.\n", __func__,
+ "will try hardware recovery instead");
+ goto atmxt_force_bootloader_use_recov;
+ }
+
+ cmd = 0xA5;
+ err = atmxt_i2c_write(dd, rst[0], rst[1], &cmd, 1);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Unable to send flash reset command.\n",
+ __func__);
+ goto atmxt_force_bootloader_use_recov;
+ }
+
+ if (!chg_used) {
+ for (i = 0; i < 128; i++) {
+ if (gpio_get_value(dd->pdata->gpio_interrupt) == 1)
+ break;
+ else
+ udelay(2000);
+ }
+
+ if (i == 128) {
+ printk(KERN_ERR "%s: %s.\n", __func__,
+ "Waiting for flash reset timed out");
+ err = -ETIME;
+ goto atmxt_force_bootloader_exit;
+ }
+ }
+
+ goto atmxt_force_bootloader_exit;
+
+atmxt_force_bootloader_use_recov:
+ atmxt_dbg(dd, ATMXT_DBG2, "%s: Using hardware recovery...\n", __func__);
+ printk(KERN_ERR "%s: Forced hardware recovery failed--%s.\n",
+ __func__, "unable to reflash IC");
+ err = -ENOSYS;
+
+atmxt_force_bootloader_exit:
+ return err;
+}
+
+static bool atmxt_check_firmware_update(struct atmxt_driver_data *dd)
+{
+ bool update_fw = false;
+
+ if ((dd->util->fw[1] != dd->info_blk->header[0]) ||
+ (dd->util->fw[2] != dd->info_blk->header[1])) {
+ printk(KERN_ERR
+ "%s: Platform firmware does not match touch IC. %s\n",
+ __func__, "Unable to check for firmware update.");
+ goto atmxt_check_firmware_update_exit;
+ }
+
+ update_fw = !((dd->util->fw[3] == dd->info_blk->header[2]) &&
+ (dd->util->fw[4] == dd->info_blk->header[3]));
+
+atmxt_check_firmware_update_exit:
+ return update_fw;
+}
+
+static int atmxt_validate_firmware(uint8_t *data, uint32_t size)
+{
+ int err = 0;
+ uint32_t iter = 0;
+ int length = 0;
+
+ if (data == NULL || size == 0) {
+ printk(KERN_ERR "%s: No firmware data found.\n", __func__);
+ err = -ENODATA;
+ goto atmxt_validate_firmware_fail;
+ }
+
+ if (data[0] < 4) {
+ printk(KERN_ERR "%s: Invalid firmware header.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_validate_firmware_fail;
+ } else if ((data[0] + 1) >= size) {
+ printk(KERN_ERR "%s: Firmware is malformed.\n", __func__);
+ err = -EOVERFLOW;
+ goto atmxt_validate_firmware_fail;
+ }
+
+ iter = iter + data[0] + 1;
+
+ while (iter < (size - 1)) {
+ length = (data[iter+0] << 8) | data[iter+1];
+ if ((iter + length + 2) > size) {
+ printk(KERN_ERR
+ "%s: Overflow in firmware image %s %u.\n",
+ __func__, "on iter", iter);
+ err = -EOVERFLOW;
+ goto atmxt_validate_firmware_fail;
+ }
+
+ iter = iter + length + 2;
+ }
+
+ if (iter != size) {
+ printk(KERN_ERR "%s: Firmware image misaligned.\n", __func__);
+ err = -ENOEXEC;
+ goto atmxt_validate_firmware_fail;
+ }
+
+atmxt_validate_firmware_fail:
+ return err;
+}
+
+static int atmxt_flash_firmware(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ uint8_t *img = dd->util->fw;
+ uint32_t size = dd->util->fw_size;
+ uint32_t iter = 0;
+ uint8_t status = 0x00;
+ bool irq_low = false;
+ bool frame_crc_failed = false;
+
+ printk(KERN_INFO "%s: Reflashing touch IC...\n", __func__);
+
+ err = atmxt_i2c_write(dd, 0xDC, 0xAA, NULL, 0);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Unable to send unlock command.\n",
+ __func__);
+ goto atmxt_flash_firmware_fail;
+ }
+
+ iter = img[0] + 1;
+ while (iter < (size - 1)) {
+ irq_low = atmxt_wait4irq(dd);
+ if (!irq_low) {
+ printk(KERN_ERR
+ "%s: Timeout waiting %s for iter %u.\n",
+ __func__, "for frame interrupt", iter);
+ err = -ETIME;
+ goto atmxt_flash_firmware_fail;
+ }
+
+ err = atmxt_i2c_read(dd, &status, 1);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Error reading frame byte for iter %u.\n",
+ __func__, iter);
+ goto atmxt_flash_firmware_fail;
+ }
+
+ if ((status & 0xC0) != 0x80) {
+ printk(KERN_ERR "%s: %s 0x%02X %s %u.\n", __func__,
+ "Unexpected wait status", status,
+ "received for iter", iter);
+ err = -EPROTO;
+ goto atmxt_flash_firmware_fail;
+ }
+
+ err = atmxt_i2c_write(dd, img[iter+0], img[iter+1],
+ &(img[iter+2]), (img[iter+0] << 8) | img[iter+1]);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error sending frame iter %u.\n",
+ __func__, iter);
+ goto atmxt_flash_firmware_fail;
+ }
+
+ irq_low = atmxt_wait4irq(dd);
+ if (!irq_low) {
+ printk(KERN_ERR
+ "%s: Timeout waiting %s for iter %u.\n",
+ __func__, "for check interrupt", iter);
+ err = -ETIME;
+ goto atmxt_flash_firmware_fail;
+ }
+
+ err = atmxt_i2c_read(dd, &status, 1);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Error reading check byte for iter %u.\n",
+ __func__, iter);
+ goto atmxt_flash_firmware_fail;
+ }
+
+ if (status != 0x02) {
+ printk(KERN_ERR "%s: %s 0x%02X %s %u.\n", __func__,
+ "Unexpected frame status", status,
+ "received for iter", iter);
+ err = -EPROTO;
+ goto atmxt_flash_firmware_fail;
+ }
+
+ irq_low = atmxt_wait4irq(dd);
+ if (!irq_low) {
+ printk(KERN_ERR
+ "%s: Timeout waiting %s for iter %u.\n",
+ __func__, "for result interrupt", iter);
+ err = -ETIME;
+ goto atmxt_flash_firmware_fail;
+ }
+
+ err = atmxt_i2c_read(dd, &status, 1);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Error reading result byte for iter %u.\n",
+ __func__, iter);
+ goto atmxt_flash_firmware_fail;
+ }
+
+ if (status == 0x04) {
+ iter = iter + ((img[iter+0] << 8) | img[iter+1]) + 2;
+ frame_crc_failed = false;
+ } else if (!frame_crc_failed) {
+ printk(KERN_ERR "%s: %s %u--%s.\n",
+ __func__, "Frame CRC failed for iter",
+ iter, "will try to re-send");
+ frame_crc_failed = true;
+ } else {
+ printk(KERN_ERR "%s: %s %u--%s.\n",
+ __func__, "Frame CRC failed for iter",
+ iter, "check firmware image");
+ err = -ECOMM;
+ goto atmxt_flash_firmware_fail;
+ }
+ }
+
+atmxt_flash_firmware_fail:
+ return err;
+}
+
+static char *atmxt_msg2str(const uint8_t *msg, uint8_t size)
+{
+ char *str = NULL;
+ int i = 0;
+ int err = 0;
+
+ str = kzalloc(sizeof(char) * (size * 5), GFP_KERNEL);
+ if (str == NULL) {
+ printk(KERN_ERR "%s: Failed to allocate message string.\n",
+ __func__);
+ goto atmxt_msg2str_exit;
+ }
+
+ for (i = 0; i < size; i++) {
+ err = sprintf(str, "%s0x%02X ", str, msg[i]);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error in sprintf on pass %d",
+ __func__, i);
+ goto atmxt_msg2str_exit;
+ }
+ }
+
+ str[err-1] = '\0';
+
+atmxt_msg2str_exit:
+ return str;
+}
+
+static bool atmxt_wait4irq(struct atmxt_driver_data *dd)
+{
+ bool irq_low = false;
+ int i = 0;
+
+ for (i = 0; i < 500; i++) {
+ if (gpio_get_value(dd->pdata->gpio_interrupt) != 0) {
+ msleep(20);
+ } else {
+ irq_low = true;
+ break;
+ }
+ }
+
+ return irq_low;
+}
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+static ssize_t atmxt_debug_drv_debug_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "Current Debug Level: %hu\n", dd->dbg->dbg_lvl);
+}
+static ssize_t atmxt_debug_drv_debug_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ unsigned long value = 0;
+
+ mutex_lock(dd->mutex);
+
+ err = strict_strtoul(buf, 10, &value);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to convert value.\n", __func__);
+ goto atmxt_debug_drv_debug_store_exit;
+ }
+
+ if (value > 255) {
+ printk(KERN_ERR "%s: Invalid debug level %lu--setting to %u.\n",
+ __func__, value, ATMXT_DBG3);
+ dd->dbg->dbg_lvl = ATMXT_DBG3;
+ } else {
+ dd->dbg->dbg_lvl = value;
+ printk(KERN_INFO "%s: Debug level is now %hu.\n",
+ __func__, dd->dbg->dbg_lvl);
+ }
+
+ err = size;
+
+atmxt_debug_drv_debug_store_exit:
+ mutex_unlock(dd->mutex);
+
+ return err;
+}
+static DEVICE_ATTR(drv_debug, S_IRUSR | S_IWUSR,
+ atmxt_debug_drv_debug_show, atmxt_debug_drv_debug_store);
+
+static ssize_t atmxt_debug_drv_flags_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "Current Driver Flags: 0x%04X\n", dd->settings);
+}
+static ssize_t atmxt_debug_drv_flags_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ unsigned long value = 0;
+
+ mutex_lock(dd->mutex);
+
+ err = strict_strtoul(buf, 16, &value);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to convert value.\n", __func__);
+ goto atmxt_debug_drv_flags_store_exit;
+ }
+
+ if (value > 65535) {
+ printk(KERN_ERR "%s: Invalid flag settings 0x%08lX passed.\n",
+ __func__, value);
+ err = -EOVERFLOW;
+ goto atmxt_debug_drv_flags_store_exit;
+ } else {
+ dd->settings = value;
+ atmxt_dbg(dd, ATMXT_DBG3,
+ "%s: Driver flags now set to 0x%04X.\n",
+ __func__, dd->settings);
+ }
+
+ err = size;
+
+atmxt_debug_drv_flags_store_exit:
+ mutex_unlock(dd->mutex);
+
+ return err;
+}
+static DEVICE_ATTR(drv_flags, S_IRUSR | S_IWUSR,
+ atmxt_debug_drv_flags_show, atmxt_debug_drv_flags_store);
+#endif
+
+static ssize_t atmxt_debug_drv_irq_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG))
+ return sprintf(buf, "Driver interrupt is ENABLED.\n");
+ else
+ return sprintf(buf, "Driver interrupt is DISABLED.\n");
+}
+static ssize_t atmxt_debug_drv_irq_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ unsigned long value = 0;
+
+ mutex_lock(dd->mutex);
+
+ err = strict_strtoul(buf, 10, &value);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to convert value.\n", __func__);
+ goto atmxt_debug_drv_irq_store_exit;
+ }
+
+ if ((atmxt_get_drv_state(dd) != ATMXT_DRV_ACTIVE) &&
+ (atmxt_get_drv_state(dd) != ATMXT_DRV_IDLE)) {
+ printk(KERN_ERR "%s: %s %s or %s states.\n",
+ __func__, "Interrupt can be changed only in",
+ atmxt_driver_state_string[ATMXT_DRV_ACTIVE],
+ atmxt_driver_state_string[ATMXT_DRV_IDLE]);
+ err = -EACCES;
+ goto atmxt_debug_drv_irq_store_exit;
+ }
+
+ switch (value) {
+ case 0:
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ dd->status =
+ dd->status & ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ }
+ break;
+
+ case 1:
+ if (!(dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG))) {
+ dd->status =
+ dd->status | (1 << ATMXT_IRQ_ENABLED_FLAG);
+ enable_irq(dd->client->irq);
+ atmxt_set_drv_state(dd, ATMXT_DRV_ACTIVE);
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "%s: Invalid value passed.\n", __func__);
+ err = -EINVAL;
+ goto atmxt_debug_drv_irq_store_exit;
+ }
+
+ err = size;
+
+atmxt_debug_drv_irq_store_exit:
+ mutex_unlock(dd->mutex);
+
+ return err;
+}
+static DEVICE_ATTR(drv_irq, S_IRUSR | S_IWUSR,
+ atmxt_debug_drv_irq_show, atmxt_debug_drv_irq_store);
+
+static ssize_t atmxt_debug_drv_stat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "Driver state is %s.\nIC state is %s.\n",
+ atmxt_driver_state_string[atmxt_get_drv_state(dd)],
+ atmxt_ic_state_string[atmxt_get_ic_state(dd)]);
+}
+static DEVICE_ATTR(drv_stat, S_IRUGO, atmxt_debug_drv_stat_show, NULL);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+static ssize_t atmxt_debug_drv_tdat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ if (dd->status & (1 << ATMXT_WAITING_FOR_TDAT))
+ return sprintf(buf, "Driver is waiting for data load.\n");
+ else
+ return sprintf(buf, "No data loading in progress.\n");
+}
+static ssize_t atmxt_debug_drv_tdat_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ mutex_lock(dd->mutex);
+
+ if (dd->status & (1 << ATMXT_WAITING_FOR_TDAT)) {
+ printk(KERN_ERR "%s: Driver is already waiting for data.\n",
+ __func__);
+ err = -EALREADY;
+ goto atmxt_debug_drv_tdat_store_fail;
+ }
+
+ printk(KERN_INFO "%s: Enabling firmware class loader...\n", __func__);
+
+ err = request_firmware_nowait(THIS_MODULE,
+ FW_ACTION_NOHOTPLUG, "", &(dd->client->dev),
+ GFP_KERNEL, dd, atmxt_tdat_callback);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Firmware request failed with error code %d.\n",
+ __func__, err);
+ goto atmxt_debug_drv_tdat_store_fail;
+ }
+
+ dd->status = dd->status | (1 << ATMXT_WAITING_FOR_TDAT);
+ err = size;
+
+atmxt_debug_drv_tdat_store_fail:
+ mutex_unlock(dd->mutex);
+
+ return err;
+}
+static DEVICE_ATTR(drv_tdat, S_IRUSR | S_IWUSR,
+ atmxt_debug_drv_tdat_show, atmxt_debug_drv_tdat_store);
+#endif
+
+static ssize_t atmxt_debug_drv_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "Driver: %s\nVersion: %s\nDate: %s\n",
+ ATMXT_I2C_NAME, ATMXT_DRIVER_VERSION, ATMXT_DRIVER_DATE);
+}
+static DEVICE_ATTR(drv_ver, S_IRUGO, atmxt_debug_drv_ver_show, NULL);
+
+static ssize_t atmxt_debug_hw_irqstat_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ err = gpio_get_value(dd->pdata->gpio_interrupt);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to read irq level with error code %d.\n",
+ __func__, err);
+ err = sprintf(buf,
+ "Failed to read irq level with error code %d.\n",
+ err);
+ goto atmxt_debug_hw_irqstat_show_exit;
+ }
+
+ switch (err) {
+ case 0:
+ err = sprintf(buf, "Interrupt line is LOW.\n");
+ break;
+ case 1:
+ err = sprintf(buf, "Interrupt line is HIGH.\n");
+ break;
+ default:
+ err = sprintf(buf, "Read irq level of %d.\n", err);
+ break;
+ }
+
+atmxt_debug_hw_irqstat_show_exit:
+ return err;
+}
+static DEVICE_ATTR(hw_irqstat, S_IRUGO, atmxt_debug_hw_irqstat_show, NULL);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+static ssize_t atmxt_debug_hw_reset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ mutex_lock(dd->mutex);
+
+ if (atmxt_get_drv_state(dd) == ATMXT_DRV_INIT) {
+ printk(KERN_ERR "%s: %s %s.\n", __func__,
+ "Unable to restart IC in driver state",
+ atmxt_driver_state_string[ATMXT_DRV_INIT]);
+ err = -EACCES;
+ goto atmxt_debug_hw_reset_store_fail;
+ }
+
+ if (dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)) {
+ disable_irq_nosync(dd->client->irq);
+ dd->status = dd->status & ~(1 << ATMXT_IRQ_ENABLED_FLAG);
+ }
+
+ err = atmxt_restart_ic(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to %s with error code %d.\n",
+ __func__, "re-initialize the touch IC", err);
+ atmxt_set_drv_state(dd, ATMXT_DRV_IDLE);
+ goto atmxt_debug_hw_reset_store_fail;
+ }
+
+ if (((atmxt_get_drv_state(dd) == ATMXT_DRV_ACTIVE)) &&
+ (!(dd->status & (1 << ATMXT_IRQ_ENABLED_FLAG)))) {
+ dd->status = dd->status | (1 << ATMXT_IRQ_ENABLED_FLAG);
+ enable_irq(dd->client->irq);
+ }
+
+ err = size;
+
+atmxt_debug_hw_reset_store_fail:
+ mutex_unlock(dd->mutex);
+ return err;
+}
+static DEVICE_ATTR(hw_reset, S_IWUSR, NULL, atmxt_debug_hw_reset_store);
+#endif
+
+static ssize_t atmxt_debug_hw_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "Touch Data File: %s\n", dd->pdata->filename);
+}
+static DEVICE_ATTR(hw_ver, S_IRUGO, atmxt_debug_hw_ver_show, NULL);
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+static ssize_t atmxt_debug_ic_grpdata_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ int i = 0;
+ uint8_t addr_lo = 0x00;
+ uint8_t addr_hi = 0x00;
+ uint8_t *entry = NULL;
+ int size = 0;
+ uint8_t *data_in = NULL;
+
+ mutex_lock(dd->mutex);
+
+ if ((atmxt_get_ic_state(dd) != ATMXT_IC_ACTIVE) &&
+ (atmxt_get_ic_state(dd) != ATMXT_IC_SLEEP)) {
+ printk(KERN_ERR "%s: %s %s or %s states.\n", __func__,
+ "Group data can be read only in IC",
+ atmxt_ic_state_string[ATMXT_IC_ACTIVE],
+ atmxt_ic_state_string[ATMXT_IC_SLEEP]);
+ err = sprintf(buf, "%s %s or %s states.\n",
+ "Group data can be read only in IC",
+ atmxt_ic_state_string[ATMXT_IC_ACTIVE],
+ atmxt_ic_state_string[ATMXT_IC_SLEEP]);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ if (dd->info_blk->data[i+0] == dd->dbg->grp_num) {
+ entry = &(dd->info_blk->data[i]);
+ break;
+ }
+ }
+
+ if (entry == NULL) {
+ printk(KERN_ERR "%s: Group %hu does not exist.\n",
+ __func__, dd->dbg->grp_num);
+ err = sprintf(buf, "Group %hu does not exist.\n",
+ dd->dbg->grp_num);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ if (dd->dbg->grp_off > entry[3]) {
+ printk(KERN_ERR "%s: Offset %hu exceeds group size of %u.\n",
+ __func__, dd->dbg->grp_off, entry[3]+1);
+ err = sprintf(buf, "Offset %hu exceeds group size of %u.\n",
+ dd->dbg->grp_off, entry[3]+1);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ addr_lo = entry[1] + dd->dbg->grp_off;
+ addr_hi = entry[2];
+ if (addr_lo < entry[1])
+ addr_hi++;
+
+ err = atmxt_i2c_write(dd, addr_lo, addr_hi, NULL, 0);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to set group pointer with error code %d.\n",
+ __func__, err);
+ err = sprintf(buf,
+ "Failed to set group pointer with error code %d.\n",
+ err);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ size = entry[3] - dd->dbg->grp_off + 1;
+ data_in = kzalloc(sizeof(uint8_t) * size, GFP_KERNEL);
+ if (data_in == NULL) {
+ printk(KERN_ERR "%s: Unable to allocate memory buffer.\n",
+ __func__);
+ err = sprintf(buf, "Unable to allocate memory buffer.\n");
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ err = atmxt_i2c_read(dd, data_in, size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to read group data.\n", __func__);
+ err = sprintf(buf, "Failed to read group data.\n");
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ err = sprintf(buf, "Group %hu, Offset %hu:\n",
+ dd->dbg->grp_num, dd->dbg->grp_off);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error in header sprintf.\n", __func__);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+ for (i = 0; i < size; i++) {
+ err = sprintf(buf, "%s0x%02hX\n", buf, data_in[i]);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error in sprintf loop %d.\n",
+ __func__, i);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+ }
+
+ err = sprintf(buf, "%s(%u bytes)\n", buf, size);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Error in byte count sprintf.\n", __func__);
+ goto atmxt_debug_ic_grpdata_show_exit;
+ }
+
+atmxt_debug_ic_grpdata_show_exit:
+ kfree(data_in);
+ mutex_unlock(dd->mutex);
+
+ return (ssize_t) err;
+}
+static ssize_t atmxt_debug_ic_grpdata_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ int i = 0;
+ uint8_t addr_lo = 0x00;
+ uint8_t addr_hi = 0x00;
+ uint8_t *entry = NULL;
+ int data_size = 0;
+ uint8_t *data_out = NULL;
+ unsigned long value = 0;
+ uint8_t *conv_buf = NULL;
+
+ mutex_lock(dd->mutex);
+
+ if ((atmxt_get_ic_state(dd) != ATMXT_IC_ACTIVE) &&
+ (atmxt_get_ic_state(dd) != ATMXT_IC_SLEEP)) {
+ printk(KERN_ERR "%s: %s %s or %s states.\n", __func__,
+ "Group data can be written only in IC",
+ atmxt_ic_state_string[ATMXT_IC_ACTIVE],
+ atmxt_ic_state_string[ATMXT_IC_SLEEP]);
+ err = -EACCES;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ for (i = 0; i < dd->info_blk->size; i += 6) {
+ if (dd->info_blk->data[i+0] == dd->dbg->grp_num) {
+ entry = &(dd->info_blk->data[i]);
+ break;
+ }
+ }
+
+ if (entry == NULL) {
+ printk(KERN_ERR "%s: Group %hu does not exist.\n",
+ __func__, dd->dbg->grp_num);
+ err = -ENOENT;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ if ((dd->dbg->grp_off) > entry[3]) {
+ printk(KERN_ERR "%s: Offset %hu exceeds data size.\n",
+ __func__, dd->dbg->grp_off);
+ err = -EOVERFLOW;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ if ((size % 5 != 0) || (size == 0)) {
+ printk(KERN_ERR "%s: Invalid data format. %s\n",
+ __func__, "Use \"0xHH,0xHH,...,0xHH\" instead.");
+ err = -EINVAL;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ data_out = kzalloc(sizeof(uint8_t) * (size / 5), GFP_KERNEL);
+ if (data_out == NULL) {
+ printk(KERN_ERR "%s: Unable to allocate output buffer.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ conv_buf = kzalloc(sizeof(uint8_t) * 5, GFP_KERNEL);
+ if (conv_buf == NULL) {
+ printk(KERN_ERR "%s: Unable to allocate conversion buffer.\n",
+ __func__);
+ err = -ENOMEM;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ for (i = 0; i < size; i += 5) {
+ memcpy(conv_buf, &(buf[i]), 4);
+ err = strict_strtoul(conv_buf, 16, &value);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Argument conversion failed.\n",
+ __func__);
+ goto atmxt_debug_ic_grpdata_store_exit;
+ } else if (value > 255) {
+ printk(KERN_ERR "%s: Value 0x%lX is too large.\n",
+ __func__, value);
+ err = -EOVERFLOW;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ data_out[data_size] = value;
+ data_size++;
+ }
+
+ if ((dd->dbg->grp_off + data_size) > (entry[3] + 1)) {
+ printk(KERN_ERR "%s: Trying to write %d bytes at offset %hu, "
+ "which exceeds group size of %hu.\n", __func__,
+ data_size, dd->dbg->grp_off, entry[3]+1);
+ err = -EOVERFLOW;
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ addr_lo = entry[1] + dd->dbg->grp_off;
+ addr_hi = entry[2];
+ if (addr_lo < entry[1])
+ addr_hi++;
+
+ err = atmxt_i2c_write(dd, addr_lo, addr_hi, data_out, data_size);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to write data with error code %d.\n",
+ __func__, err);
+ goto atmxt_debug_ic_grpdata_store_exit;
+ }
+
+ if (!(dd->status & (1 << ATMXT_IGNORE_CHECKSUM))) {
+ printk(KERN_INFO
+ "%s: Disabled settings checksum verification %s.\n",
+ __func__, "until next boot");
+ }
+ dd->status = dd->status | (1 << ATMXT_IGNORE_CHECKSUM);
+
+ err = size;
+
+atmxt_debug_ic_grpdata_store_exit:
+ kfree(data_out);
+ kfree(conv_buf);
+ mutex_unlock(dd->mutex);
+
+ return (ssize_t) err;
+}
+static DEVICE_ATTR(ic_grpdata, S_IRUSR | S_IWUSR,
+ atmxt_debug_ic_grpdata_show, atmxt_debug_ic_grpdata_store);
+
+static ssize_t atmxt_debug_ic_grpnum_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "Current Group: %hu\n", dd->dbg->grp_num);
+}
+static ssize_t atmxt_debug_ic_grpnum_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ unsigned long value = 0;
+
+ mutex_lock(dd->mutex);
+
+ err = strict_strtoul(buf, 10, &value);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to convert value.\n", __func__);
+ goto atmxt_debug_ic_grpnum_store_exit;
+ }
+
+ if (value > 255) {
+ printk(KERN_ERR "%s: Invalid group number %lu--%s.\n",
+ __func__, value, "setting to 255");
+ dd->dbg->grp_num = 255;
+ } else {
+ dd->dbg->grp_num = value;
+ }
+
+ err = size;
+
+atmxt_debug_ic_grpnum_store_exit:
+ mutex_unlock(dd->mutex);
+
+ return err;
+}
+static DEVICE_ATTR(ic_grpnum, S_IRUSR | S_IWUSR,
+ atmxt_debug_ic_grpnum_show, atmxt_debug_ic_grpnum_store);
+
+static ssize_t atmxt_debug_ic_grpoffset_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ return sprintf(buf, "Current Offset: %hu\n", dd->dbg->grp_off);
+}
+static ssize_t atmxt_debug_ic_grpoffset_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ int err = 0;
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+ unsigned long value = 0;
+
+ mutex_lock(dd->mutex);
+
+ err = strict_strtoul(buf, 10, &value);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to convert value.\n", __func__);
+ goto atmxt_debug_ic_grpoffset_store_exit;
+ }
+
+ if (value > 255) {
+ printk(KERN_ERR "%s: Invalid offset %lu--setting to 255.\n",
+ __func__, value);
+ dd->dbg->grp_off = 255;
+ } else {
+ dd->dbg->grp_off = value;
+ }
+
+ err = size;
+
+atmxt_debug_ic_grpoffset_store_exit:
+ mutex_unlock(dd->mutex);
+
+ return err;
+}
+static DEVICE_ATTR(ic_grpoffset, S_IRUSR | S_IWUSR,
+ atmxt_debug_ic_grpoffset_show, atmxt_debug_ic_grpoffset_store);
+#endif
+
+static ssize_t atmxt_debug_ic_ver_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct atmxt_driver_data *dd = dev_get_drvdata(dev);
+
+ if (dd->info_blk == NULL) {
+ return sprintf(buf,
+ "No touch IC version information is available.\n");
+ } else {
+ return sprintf(buf, "%s0x%02X\n%s0x%02X\n%s0x%02X\n%s0x%02X\n",
+ "Family ID: ", dd->info_blk->header[0],
+ "Variant ID: ", dd->info_blk->header[1],
+ "Version: ", dd->info_blk->header[2],
+ "Build: ", dd->info_blk->header[3]);
+ }
+}
+static DEVICE_ATTR(ic_ver, S_IRUGO, atmxt_debug_ic_ver_show, NULL);
+
+static int atmxt_create_sysfs_files(struct atmxt_driver_data *dd)
+{
+ int err = 0;
+ int check = 0;
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ check = device_create_file(&(dd->client->dev), &dev_attr_drv_debug);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create drv_debug.\n", __func__);
+ err = check;
+ }
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_drv_flags);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create drv_flags.\n", __func__);
+ err = check;
+ }
+#endif
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_drv_irq);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create drv_irq.\n", __func__);
+ err = check;
+ }
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_drv_stat);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create drv_stat.\n", __func__);
+ err = check;
+ }
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ check = device_create_file(&(dd->client->dev), &dev_attr_drv_tdat);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create drv_tdat.\n", __func__);
+ err = check;
+ }
+#endif
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_drv_ver);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create drv_ver.\n", __func__);
+ err = check;
+ }
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_hw_irqstat);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create hw_irqstat.\n", __func__);
+ err = check;
+ }
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ check = device_create_file(&(dd->client->dev), &dev_attr_hw_reset);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create hw_reset.\n", __func__);
+ err = check;
+ }
+#endif
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_hw_ver);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create hw_ver.\n", __func__);
+ err = check;
+ }
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ check = device_create_file(&(dd->client->dev), &dev_attr_ic_grpdata);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create ic_grpdata.\n", __func__);
+ err = check;
+ }
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_ic_grpnum);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create ic_grpnum.\n", __func__);
+ err = check;
+ }
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_ic_grpoffset);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create ic_grpoffset.\n",
+ __func__);
+ err = check;
+ }
+#endif
+
+ check = device_create_file(&(dd->client->dev), &dev_attr_ic_ver);
+ if (check < 0) {
+ printk(KERN_ERR "%s: Failed to create ic_ver.\n", __func__);
+ err = check;
+ }
+
+ return err;
+}
+
+static void atmxt_remove_sysfs_files(struct atmxt_driver_data *dd)
+{
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ device_remove_file(&(dd->client->dev), &dev_attr_drv_debug);
+ device_remove_file(&(dd->client->dev), &dev_attr_drv_flags);
+#endif
+ device_remove_file(&(dd->client->dev), &dev_attr_drv_irq);
+ device_remove_file(&(dd->client->dev), &dev_attr_drv_stat);
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ device_remove_file(&(dd->client->dev), &dev_attr_drv_tdat);
+#endif
+ device_remove_file(&(dd->client->dev), &dev_attr_drv_ver);
+ device_remove_file(&(dd->client->dev), &dev_attr_hw_irqstat);
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ device_remove_file(&(dd->client->dev), &dev_attr_hw_reset);
+#endif
+ device_remove_file(&(dd->client->dev), &dev_attr_hw_ver);
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+ device_remove_file(&(dd->client->dev), &dev_attr_ic_grpdata);
+ device_remove_file(&(dd->client->dev), &dev_attr_ic_grpnum);
+ device_remove_file(&(dd->client->dev), &dev_attr_ic_grpoffset);
+#endif
+ device_remove_file(&(dd->client->dev), &dev_attr_ic_ver);
+ return;
+}
diff --git a/drivers/input/touchscreen/atmxt.h b/drivers/input/touchscreen/atmxt.h
new file mode 100644
index 00000000000..3711ef5180f
--- /dev/null
+++ b/drivers/input/touchscreen/atmxt.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2010-2012 Motorola Mobility, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+/* Local header for Atmel maXTouch touchscreens that uses tdat files */
+#ifndef _LINUX_ATMXT_H
+#define _LINUX_ATMXT_H
+
+#include <linux/types.h>
+#include <linux/input/touch_platform.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/mutex.h>
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#define ATMXT_DRIVER_VERSION "YN-04-01"
+#define ATMXT_DRIVER_DATE "2012-06-28"
+
+#ifdef CONFIG_TOUCHSCREEN_DEBUG
+#define atmxt_dbg(dd, level, format, args...) \
+{\
+ if ((dd->dbg->dbg_lvl) >= level) \
+ printk(KERN_INFO format, ## args); \
+}
+#else
+#define atmxt_dbg(dd, level, format, args...) {}
+#endif
+
+#define ATMXT_DBG0 0
+#define ATMXT_DBG1 1
+#define ATMXT_DBG2 2
+#define ATMXT_DBG3 3
+
+#define ATMXT_IRQ_ENABLED_FLAG 0
+#define ATMXT_WAITING_FOR_TDAT 1
+#define ATMXT_CHECKSUM_FAILED 2
+#define ATMXT_IGNORE_CHECKSUM 3
+#define ATMXT_REPORT_TOUCHES 4
+#define ATMXT_FIXING_CALIBRATION 5
+#define ATMXT_RECEIVED_CALIBRATION 6
+#define ATMXT_RESTART_REQUIRED 7
+#define ATMXT_SET_MESSAGE_POINTER 8
+
+#define ATMXT_I2C_ATTEMPTS 10
+#define ATMXT_I2C_WAIT_TIME 50
+#define ATMXT_MAX_TOUCHES 10
+#define ATMXT_ABS_RESERVED 0xFFFF
+#define ATMXT_IC_RESET_HOLD_TIME 1000
+
+
+enum atmxt_driver_state {
+ ATMXT_DRV_ACTIVE,
+ ATMXT_DRV_IDLE,
+ ATMXT_DRV_REFLASH,
+ ATMXT_DRV_INIT,
+};
+static const char * const atmxt_driver_state_string[] = {
+ "ACTIVE",
+ "IDLE",
+ "REFLASH",
+ "INIT",
+};
+
+enum atmxt_ic_state {
+ ATMXT_IC_ACTIVE,
+ ATMXT_IC_SLEEP,
+ ATMXT_IC_UNKNOWN,
+ ATMXT_IC_BOOTLOADER,
+ ATMXT_IC_PRESENT,
+};
+static const char * const atmxt_ic_state_string[] = {
+ "ACTIVE",
+ "SLEEP",
+ "UNKNOWN",
+ "BOOTLOADER",
+ "PRESENT",
+};
+
+
+struct atmxt_util_data {
+ uint8_t *data;
+ size_t size;
+ uint8_t *tsett;
+ uint32_t tsett_size;
+ uint8_t *fw;
+ uint32_t fw_size;
+ uint8_t addr[2];
+} __packed;
+
+struct atmxt_nvm {
+ uint8_t *data;
+ uint16_t size;
+ uint8_t addr[2];
+ uint8_t chksum[3];
+} __packed;
+
+struct atmxt_info_block {
+ uint8_t header[7];
+ uint8_t *data;
+ uint8_t size;
+ uint8_t *msg_id;
+ uint8_t id_size;
+} __packed;
+
+struct atmxt_addr {
+ uint8_t msg[2];
+ uint8_t pwr[2];
+ uint8_t rst[2];
+ uint8_t nvm[2];
+ uint8_t cal[2];
+ uint8_t acq[2];
+} __packed;
+
+struct atmxt_data {
+ uint8_t pwr[2];
+ uint8_t max_msg_size;
+ uint8_t touch_id_offset;
+ bool res[2];
+ uint8_t acq[6];
+ unsigned long timer;
+ uint8_t last_stat;
+} __packed;
+
+struct atmxt_touch_data {
+ bool active;
+ uint16_t x;
+ uint16_t y;
+ uint8_t p;
+ uint8_t w;
+ uint8_t id;
+} __packed;
+
+struct atmxt_report_data {
+ uint16_t axis[5];
+ uint8_t active_touches;
+ struct atmxt_touch_data tchdat[ATMXT_MAX_TOUCHES];
+} __packed;
+
+struct atmxt_debug {
+ uint8_t dbg_lvl;
+ uint8_t grp_num;
+ uint8_t grp_off;
+} __packed;
+
+
+struct atmxt_driver_data {
+ struct touch_platform_data *pdata;
+ struct atmxt_util_data *util;
+ struct i2c_client *client;
+ struct mutex *mutex;
+ struct input_dev *in_dev;
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend es;
+#endif
+
+ enum atmxt_driver_state drv_stat;
+ enum atmxt_ic_state ic_stat;
+
+ struct atmxt_info_block *info_blk;
+ struct atmxt_nvm *nvm;
+ struct atmxt_addr *addr;
+ struct atmxt_data *data;
+ struct atmxt_report_data *rdat;
+ struct atmxt_debug *dbg;
+
+ uint16_t status;
+ uint16_t settings;
+} __packed;
+
+#endif /* _LINUX_ATMXT_H */
diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig
index ef992293598..9828842ceda 100644
--- a/drivers/leds/Kconfig
+++ b/drivers/leds/Kconfig
@@ -50,6 +50,12 @@ config LEDS_LM3530
controlled manually or using PWM input or using ambient
light automatically.
+config LEDS_LM3535
+ tristate "LM3535 ALS driver"
+ depends on LEDS_CLASS && I2C
+ help
+ This option enables support for the LM3535 .
+
config LEDS_LM3533
tristate "LED support for LM3533"
depends on LEDS_CLASS
diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile
index ac2897732b0..a287da98d30 100644
--- a/drivers/leds/Makefile
+++ b/drivers/leds/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_LEDS_ATMEL_PWM) += leds-atmel-pwm.o
obj-$(CONFIG_LEDS_BD2802) += leds-bd2802.o
obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o
obj-$(CONFIG_LEDS_LM3530) += leds-lm3530.o
+obj-$(CONFIG_LEDS_LM3535) += leds-lm3535.o
obj-$(CONFIG_LEDS_LM3533) += leds-lm3533.o
obj-$(CONFIG_LEDS_LM3642) += leds-lm3642.o
obj-$(CONFIG_LEDS_MIKROTIK_RB532) += leds-rb532.o
diff --git a/drivers/leds/als.h b/drivers/leds/als.h
new file mode 100644
index 00000000000..6084e46b95f
--- /dev/null
+++ b/drivers/leds/als.h
@@ -0,0 +1,14 @@
+#ifndef __ALS_H
+#define __ALS_H
+
+#define NUM_ALS_ZONES 6 // 5 zones and 1 undefined
+
+typedef void (*als_cb)(unsigned prev_zone, unsigned curr_zone, uint32_t cookie);
+int lm3535_register_als_callback(als_cb func, uint32_t cookie);
+void lm3535_unregister_als_callback(als_cb func);
+int adp8862_register_als_callback(als_cb func, uint32_t cookie);
+void adp8862_unregister_als_callback(als_cb func);
+unsigned lm3535_als_is_dark(void);
+unsigned adp8862_als_is_dark(void);
+typedef unsigned (*als_is_dark_func)(void);
+#endif
diff --git a/drivers/leds/leds-lm3535.c b/drivers/leds/leds-lm3535.c
new file mode 100644
index 00000000000..03915b63b5e
--- /dev/null
+++ b/drivers/leds/leds-lm3535.c
@@ -0,0 +1,1470 @@
+/*
+ * Copyright (C) 2013 Motorola Mobility, Inc.
+ * Author: Alina Yakovleva <qvdh43@motorola.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., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+// Linux driver for LM3535 display backlight
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/hrtimer.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <linux/clk.h>
+#include <linux/irq.h>
+#include <linux/sysfs.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/leds.h>
+#include <linux/mutex.h>
+#include <linux/debugfs.h>
+#include <linux/list.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include "als.h"
+#undef CONFIG_HAS_EARLYSUSPEND
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+#ifdef CONFIG_LM3535_ESD_RECOVERY
+#include <mot/esd_poll.h>
+#endif /* CONFIG_LM3535_ESD_RECOVERY */
+
+#define MODULE_NAME "leds_lm3535"
+
+/******************************************************************************
+ * LM3535 registers
+ ******************************************************************************/
+#define LM3535_ENABLE_REG 0x10
+#define LM3535_CONFIG_REG 0x20
+#define LM3535_OPTIONS_REG 0x30
+#define LM3535_ALS_REG 0x40
+#define LM3535_ALS_CTRL_REG 0x50
+#define LM3535_ALS_RESISTOR_REG 0x51
+#ifdef CONFIG_LM3535_BUTTON_BL
+#define LM3535_ALS_SELECT_REG 0x52
+#endif
+#define LM3535_BRIGHTNESS_CTRL_REG_A 0xA0
+#define LM3535_BRIGHTNESS_CTRL_REG_B 0xB0
+#define LM3535_BRIGHTNESS_CTRL_REG_C 0xC0
+#define LM3535_ALS_ZB0_REG 0X60
+#define LM3535_ALS_ZB1_REG 0X61
+#define LM3535_ALS_ZB2_REG 0X62
+#define LM3535_ALS_ZB3_REG 0X63
+#define LM3535_ALS_Z0T_REG 0X70
+#define LM3535_ALS_Z1T_REG 0X71
+#define LM3535_ALS_Z2T_REG 0X72
+#define LM3535_ALS_Z3T_REG 0X73
+#define LM3535_ALS_Z4T_REG 0X74
+#define LM3535_TRIM_REG 0xD0
+
+#define ALS_FLAG_MASK 0x08
+#define ALS_ZONE_MASK 0x07
+#define ALS_NO_ZONE 5
+#define LM3535_TRIM_VALUE 0x68
+
+#define LM3535_LED_MAX 0x7F // Max brightness value supported by LM3535
+
+#define LM3535_HWEN_GPIO 98
+
+/* Final revision CONFIG value */
+#define CONFIG_VALUE 0x4C
+#define CONFIG_VALUE_NO_ALS 0x0C
+
+/* ALS Averaging time */
+#define ALS_AVERAGING 0x50 // 1600ms, needs 2 ave. periods
+
+/* Zone boundaries */
+static unsigned als_zb[] = {0x04, 0x42, 0x96, 0xFF};
+module_param_array(als_zb, uint, NULL, 0644);
+static unsigned resistor_value = 0x30;
+module_param(resistor_value, uint, 0644);
+static unsigned pwm_value = 0x1;
+module_param(pwm_value, uint, 0644);
+
+static unsigned als_sleep = 350;
+module_param(als_sleep, uint, 0644);
+static unsigned ramp_time = 200000;
+module_param(ramp_time, uint, 0644);
+enum {
+ TRACE_SUSPEND = 0x1,
+ TRACE_ALS = 0x2,
+ TRACE_BRIGHTNESS = 0x4,
+ TRACE_WRITE = 0x8,
+ TRACE_EVENT = 0x10,
+};
+unsigned do_trace; /* = TRACE_ALS | TRACE_SUSPEND | TRACE_BRIGHTNESS;*/
+module_param(do_trace, uint, 0644);
+
+#define printk_write(fmt,args...) if (do_trace & TRACE_WRITE) printk(KERN_INFO fmt, ##args)
+#define printk_br(fmt,args...) if (do_trace & TRACE_BRIGHTNESS) printk(KERN_INFO fmt, ##args)
+#define printk_als(fmt,args...) if (do_trace & TRACE_ALS) printk(KERN_INFO fmt, ##args)
+#define printk_suspend(fmt,args...) if (do_trace & TRACE_SUSPEND) printk(KERN_INFO fmt, ##args)
+#define printk_event(fmt,args...) if (do_trace & TRACE_EVENT) printk(KERN_INFO fmt, ##args)
+
+/* ALS callbacks */
+static DEFINE_MUTEX(als_cb_mutex);
+static LIST_HEAD(als_callbacks);
+struct als_callback {
+ als_cb cb;
+ uint32_t cookie;
+ struct list_head entry;
+};
+
+struct lm3535_options_register_r1 {
+ int rs : 2; // Ramp step
+ int gt : 2; // Gain transition filter
+ int rev : 2;
+};
+/* Ramp times for Rev1 in microseconds */
+static unsigned int lm3535_ramp_r1[] = {51, 13000, 26000, 52000};
+
+struct lm3535_options_register_r2 {
+ int rs_down : 2;
+ int rs_up : 2;
+ int gt : 2;
+};
+/* Ramp times for Rev2 in microseconds */
+static unsigned int lm3535_ramp_r2[] = {6, 6000, 12000, 24000};
+
+struct lm3535_options_register_r3 {
+ int rs_down : 3;
+ int rs_up : 3;
+ int gt : 2;
+};
+/* Ramp times for Rev3 in microseconds */
+static unsigned int lm3535_ramp_r3[] =
+ {6, 770, 1500, 3000, 6000, 12000, 25000, 50000};
+
+static void lm3535_send_als_event (int zone);
+static char *reg_name (int reg);
+static int lm3535_configure (void);
+static void lm3535_call_als_callbacks (unsigned old_zone, unsigned zone);
+static void lm3535_set_options_r1 (uint8_t *buf, unsigned ramp);
+static void lm3535_set_options_r2 (uint8_t *buf, unsigned ramp);
+static void lm3535_set_options_r3 (uint8_t *buf, unsigned ramp);
+static int lm3535_write_reg (unsigned reg, uint8_t value, const char *caller);
+static int lm3535_read_reg (unsigned reg, uint8_t *value);
+static int lm3535_set_ramp (struct i2c_client *client,
+ unsigned int on, unsigned int nsteps, unsigned int *rtime);
+static int lm3535_enable(struct i2c_client *client, unsigned int on);
+static int lm3535_probe(struct i2c_client *client,
+ const struct i2c_device_id *id);
+static int lm3535_setup (struct i2c_client *client);
+static int lm3535_remove (struct i2c_client *client);
+static void lm3535_work_func (struct work_struct *work);
+static irqreturn_t lm3535_irq_handler (int irq, void *dev_id);
+static void lm3535_brightness_set(struct led_classdev *led_cdev,
+ enum led_brightness value);
+/* static unsigned lm3535_read_als_zone (void); */
+#ifdef CONFIG_PM
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void lm3535_early_suspend (struct early_suspend *h);
+static void lm3535_late_resume (struct early_suspend *h);
+static struct early_suspend early_suspend_data = {
+ .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 5,
+ .suspend = lm3535_early_suspend,
+ .resume = lm3535_late_resume,
+};
+
+#endif
+static int lm3535_suspend (struct i2c_client *client, pm_message_t mesg);
+static int lm3535_resume (struct i2c_client *client);
+#endif
+
+static void (*lm3535_set_options_f)(uint8_t *buf, unsigned ramp) =
+ lm3535_set_options_r1;
+static unsigned int *lm3535_ramp = lm3535_ramp_r1;
+
+/* LED class struct */
+static struct led_classdev lm3535_led = {
+ .name = "lcd-backlight",
+ .brightness_set = lm3535_brightness_set,
+};
+
+/* LED class struct for no ramping */
+static struct led_classdev lm3535_led_noramp = {
+ .name = "lcd-nr-backlight",
+ .brightness_set = lm3535_brightness_set,
+};
+#ifdef CONFIG_LM3535_BUTTON_BL
+static struct led_classdev lm3535_led_button = {
+ .name = "button-backlight",
+ .brightness_set = lm3535_button_brightness_set,
+};
+#endif
+
+static const struct of_device_id of_lm3535_match[] = {
+ { .compatible = "ti,lm3535", },
+ {},
+};
+
+static const struct i2c_device_id lm3535_id[] = {
+ { "lm3535", 0 },
+ { }
+};
+
+/* This is the I2C driver that will be inserted */
+static struct i2c_driver lm3535_driver =
+{
+ .driver = {
+ .name = "lm3535",
+ .of_match_table = of_match_ptr(of_lm3535_match),
+ },
+ .id_table = lm3535_id,
+ .probe = lm3535_probe,
+ .remove = lm3535_remove,
+#ifndef CONFIG_HAS_EARLYSUSPEND
+ .suspend = lm3535_suspend,
+ .resume = lm3535_resume,
+#endif
+};
+
+#define LM3535_NUM_ZONES 6
+struct lm3535 {
+ uint16_t addr;
+ struct i2c_client *client;
+ struct input_dev *idev;
+ unsigned initialized;
+ unsigned enabled;
+ int use_irq;
+ int revision;
+ int nramp;
+ atomic_t als_zone; // Current ALS zone
+ atomic_t bright_zone; // Current brightness zone, diff. from ALS
+ atomic_t use_als; // Whether to use ALS
+ atomic_t in_suspend; // Whether the driver is in TCMD SUSPEND mode
+ atomic_t do_als_config; // Whether to configure ALS averaging
+ unsigned bvalue; // Current brightness register value
+ unsigned saved_bvalue; // Brightness before TCMD SUSPEND
+ //struct hrtimer timer;
+ struct work_struct work;
+};
+static DEFINE_MUTEX(lm3535_mutex);
+
+static struct lm3535 lm3535_data = {
+ .nramp = 4,
+ .bvalue = 0x79,
+};
+
+#if 0
+unsigned lm3535_read_als_zone (void)
+{
+ uint8_t reg;
+ int ret;
+
+ if (lm3535_data.revision <= 1) {
+ printk (KERN_ERR "%s: early revison, setting zone to 5 (no ALS)\n",
+ __FUNCTION__);
+ atomic_set (lm3535_data.als_zone, ALS_NO_ZONE);
+ return ALS_NO_ZONE;
+ }
+ lm3535_read_reg (LM3535_CONFIG_REG, &reg);
+ if (reg & 0x50) {
+ ret = lm3535_read_reg (LM3535_ALS_REG, &reg);
+ lm3535_data.als_zone = reg & ALS_ZONE_MASK;
+ return lm3535_data.als_zone;
+ } else {
+ printk (KERN_ERR "%s: ALS is not enabled, CONFIG=0x%x, zone=5\n",
+ __FUNCTION__, reg);
+ lm3535_data.als_zone = 5;
+ return 5;
+ }
+}
+#endif
+int lm3535_register_als_callback(als_cb func, uint32_t cookie)
+{
+ struct als_callback *c;
+
+ //printk (KERN_INFO "%s: enter\n", __FUNCTION__);
+ c = kzalloc (sizeof (struct als_callback), GFP_KERNEL);
+ if (c == NULL) {
+ printk (KERN_ERR "%s: unable to register ALS callback: kzalloc\n",
+ __FUNCTION__);
+ return -ENOMEM;
+ }
+ c->cb = func;
+ c->cookie = cookie;
+ mutex_lock (&als_cb_mutex);
+ list_add (&c->entry, &als_callbacks);
+ mutex_unlock (&als_cb_mutex);
+ return 0;
+}
+EXPORT_SYMBOL(lm3535_register_als_callback);
+
+void lm3535_unregister_als_callback (als_cb func)
+{
+ struct als_callback *c;
+
+ if (!lm3535_data.initialized) {
+ printk (KERN_ERR "%s: not initialized\n", __FUNCTION__);
+ return;
+ }
+ printk (KERN_INFO "%s: enter\n", __FUNCTION__);
+ mutex_lock (&als_cb_mutex);
+ list_for_each_entry(c, &als_callbacks, entry) {
+ if (c->cb == func) {
+ list_del (&c->entry);
+ kfree(c);
+ mutex_unlock (&als_cb_mutex);
+ return;
+ }
+ }
+ mutex_unlock (&als_cb_mutex);
+ printk (KERN_ERR "%s: callback 0x%x not found\n",
+ __FUNCTION__, (unsigned int)func);
+}
+EXPORT_SYMBOL(lm3535_unregister_als_callback);
+
+unsigned lm3535_als_is_dark (void)
+{
+ unsigned zone;
+
+ zone = atomic_read (&lm3535_data.als_zone);
+ printk (KERN_ERR "%s: enter, zone = %d\n",
+ __FUNCTION__, zone);
+ if (zone == 0 || zone == 5)
+ return 1;
+ else
+ return 0;
+}
+EXPORT_SYMBOL(lm3535_als_is_dark);
+
+static int lm3535_read_reg (unsigned reg, uint8_t *value)
+{
+ struct i2c_client *client = lm3535_data.client;
+ uint8_t buf[1];
+ int ret = 0;
+
+ if (!value)
+ return -EINVAL;
+ buf[0] = reg;
+ ret = i2c_master_send (client, buf, 1);
+ if (ret > 0) {
+ msleep_interruptible (1);
+ ret = i2c_master_recv (client, buf, 1);
+ if (ret > 0)
+ *value = buf[0];
+ }
+ return ret;
+}
+
+static int lm3535_write_reg (unsigned reg, uint8_t value, const char *caller)
+{
+ uint8_t buf[2] = {reg, value};
+ int ret = 0;
+
+ printk_write ("%s: writing 0x%X to reg 0x%X (%s) at addr 0x%X\n",
+ caller, buf[1], buf[0], reg_name (reg), lm3535_data.client->addr);
+ ret = i2c_master_send (lm3535_data.client, buf, 2);
+ if (ret < 0)
+ printk (KERN_ERR "%s: i2c_master_send error %d\n",
+ caller, ret);
+ return ret;
+}
+
+/* ALS Coefficients */
+static long als_z0[] = {-48, 22225, -616209, 587754883};
+module_param_array(als_z0, long, NULL, 0644);
+
+static long als_z1[] = {24, -14596, 3987380, 659307205};
+module_param_array(als_z1, long, NULL, 0644);
+
+static long als_z2[] = {0, -5700, 2553000, 953424200};
+module_param_array(als_z2, long, NULL, 0644);
+
+static long als_z3[] = {0, -5700, 2553000, 953424200};
+module_param_array(als_z3, long, NULL, 0644);
+
+static long als_z4[] = {0, -5700, 2553000, 953424200};
+module_param_array(als_z4, long, NULL, 0644);
+
+static unsigned long als_denom = 10000000;
+module_param(als_denom, ulong, 0644);
+
+static unsigned dim_values[] = {0x24, 0x30, 0x50, 0x50, 0x50};
+module_param_array(dim_values, uint, NULL, 0644);
+
+/* Convert slider value into LM3535 register value */
+static uint8_t lm3535_convert_value (unsigned value, unsigned zone)
+{
+ uint8_t reg;
+ uint32_t res;
+
+ if (!value)
+ return 0;
+
+ if (atomic_read (&lm3535_data.in_suspend)) {
+ printk_br ("%s: in TCMD SUSPEND, returning 0x%x\n",
+ __FUNCTION__, value/2);
+ return value/2;
+ }
+ switch (zone) {
+ case 0:
+ if (value == 1)
+ res = dim_values[0]; // DIM value
+ else
+ res = als_z0[0] * value * value * value
+ +als_z0[1] * value * value
+ +als_z0[2] * value
+ +als_z0[3];
+ break;
+ case 1:
+ if (value == 1)
+ res = dim_values[1]; // DIM value
+ else
+ res = als_z1[0] * value * value * value
+ +als_z1[1] * value * value
+ +als_z1[2] * value
+ +als_z1[3];
+ break;
+ case 2:
+ if (value == 1)
+ res = dim_values[2]; // DIM value
+ else
+ res = als_z2[0] * value * value * value
+ +als_z2[1] * value * value
+ +als_z2[2] * value
+ +als_z2[3];
+ break;
+ case 3:
+ if (value == 1)
+ res = dim_values[3]; // DIM value
+ else
+ res = als_z3[0] * value * value * value
+ +als_z3[1] * value * value
+ +als_z3[2] * value
+ +als_z3[3];
+ break;
+ case 4:
+ default:
+ if (value == 1)
+ res = dim_values[4]; // DIM value
+ else
+ res = als_z4[0] * value * value * value
+ +als_z4[1] * value * value
+ +als_z4[2] * value
+ +als_z4[3];
+ break;
+ }
+ if (value == 1)
+ reg = res;
+ else
+ reg = res / als_denom;
+ printk_br (KERN_INFO "%s: v=%d, z=%d, res=0x%x, reg=0x%x\n",
+ __FUNCTION__, value, zone, res, reg);
+ return reg;
+}
+
+static void lm3535_brightness_set (struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct i2c_client *client = lm3535_data.client;
+ int ret, nsteps;
+ unsigned int total_time = 0;
+ unsigned breg = LM3535_BRIGHTNESS_CTRL_REG_A;
+ unsigned bright_zone;
+ unsigned bvalue;
+ unsigned do_ramp = 1;
+
+ printk_br ("%s: %s, 0x%x (%d)\n", __FUNCTION__,
+ led_cdev->name, value, value);
+ if (!lm3535_data.initialized) {
+ printk (KERN_ERR "%s: not initialized\n", __FUNCTION__);
+ return;
+ }
+ if (strstr (led_cdev->name, "nr"))
+ do_ramp = 0;
+
+ bright_zone = atomic_read (&lm3535_data.bright_zone);
+ mutex_lock (&lm3535_mutex);
+ if (value == -1) { // Special case for ALS adjustment
+ value = led_cdev->brightness;
+ }
+
+ if ((value > 0) && (value <= 5))
+ value = 0; /* Special case for G2 */
+ if ((value > 5) && (value <= 10))
+ value = 1; /* Special dim case for G2 */
+
+ if ((value == 0) && (!lm3535_data.enabled)) {
+ /* If LED already disabled, we don't need to do anything */
+ mutex_unlock(&lm3535_mutex);
+ return;
+ }
+
+#ifdef CONFIG_LM3535_ESD_RECOVERY
+ if (value == LED_OFF && esd_polling)
+ {
+ esd_poll_stop(lm3535_check_esd);
+ esd_polling = 0;
+ }
+#endif /* CONFIG_LM3535_ESD_RECOVERY */
+ if (!lm3535_data.enabled && value != 0)
+ lm3535_enable(client, 1);
+
+ /* Calculate brightness value for each zone relative to its cap */
+ bvalue = lm3535_convert_value (value, bright_zone);
+
+ /* Calculate number of steps for ramping */
+ nsteps = bvalue - lm3535_data.bvalue;
+ if (nsteps < 0)
+ nsteps = nsteps * (-1);
+
+ lm3535_set_ramp (client, do_ramp, nsteps, &total_time);
+
+ printk_br ("%s: zone %d, 0x%x => 0x%x, %d steps, ramp time %dus\n",
+ __FUNCTION__, bright_zone,
+ lm3535_data.bvalue, bvalue, nsteps, total_time);
+
+ /* Write to each zone brightness register so that when it jumps into
+ * the next zone the value is adjusted automatically
+ */
+ ret = lm3535_write_reg (breg, bvalue, __FUNCTION__);
+ lm3535_data.bvalue = bvalue;
+
+ if (value == 0) {
+ /* Disable everything */
+ if (do_ramp) {
+ /* Wait for ramping to finish */
+ udelay (total_time);
+ }
+ lm3535_enable(client, 0);
+ }
+
+#ifdef CONFIG_LM3535_ESD_RECOVERY
+ if ((value > 0) && (!esd_polling))
+ {
+ esd_poll_start(lm3535_check_esd, 0);
+ esd_polling = 1;
+ }
+#endif /* CONFIG_LM3535_ESD_RECOVERY */
+
+
+ mutex_unlock (&lm3535_mutex);
+}
+
+#ifdef CONFIG_LM3535_BUTTON_BL
+static void lm3535_button_brightness_set (struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ int ret;
+ unsigned breg = LM3535_BRIGHTNESS_CTRL_REG_C;
+ struct i2c_client *client = lm3535_data.client;
+
+ printk_br ("%s: %s, 0x%x (%d)\n", __FUNCTION__,
+ led_cdev->name, value, value);
+
+ mutex_lock (&lm3535_mutex);
+
+ if (!lm3535_data.button_enabled && value != 0) {
+ lm3535_enable (client, lm3535_data.enabled, 1);
+ }
+
+ ret = lm3535_write_reg (breg, 0xF8, __FUNCTION__); // Lowest setting
+
+ if (value == 0)
+ lm3535_enable(client, 0);
+
+ mutex_unlock(&lm3535_mutex);
+}
+#endif
+
+static int lm3535_als_open (struct inode *inode, struct file *file)
+{
+ if (!lm3535_data.initialized)
+ return -ENODEV;
+
+ return 0;
+}
+
+static int lm3535_als_release (struct inode *inode, struct file *file)
+{
+ return 0;
+}
+#define CMD_LEN 5
+static ssize_t lm3535_als_write (struct file *fp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ unsigned char cmd[CMD_LEN];
+ int len;
+ uint8_t value;
+ unsigned old_zone;
+
+ if (count < 1)
+ return 0;
+
+ len = count > CMD_LEN-1 ? CMD_LEN-1 : count;
+
+ if (copy_from_user (cmd, buf, len))
+ return -EFAULT;
+
+ if (lm3535_data.revision <= 1)
+ return -EFAULT;
+
+ cmd[len] = '\0';
+ if (cmd[len-1] == '\n') {
+ cmd[len-1] = '\0';
+ len--;
+ }
+ if (!strcmp (cmd, "1")) {
+ printk (KERN_INFO "%s: enabling ALS\n", __FUNCTION__);
+ value = CONFIG_VALUE | 0x80;
+ mutex_lock (&lm3535_mutex);
+ atomic_set (&lm3535_data.use_als, 1);
+ /* No need to change ALS zone; interrupt handler will do it */
+ lm3535_write_reg (LM3535_CONFIG_REG, value, __FUNCTION__);
+ mutex_unlock (&lm3535_mutex);
+ } else if (!strcmp (cmd, "0")) {
+ printk (KERN_INFO "%s: disabling ALS\n", __FUNCTION__);
+ value = CONFIG_VALUE_NO_ALS;
+ mutex_lock (&lm3535_mutex);
+ old_zone = atomic_read (&lm3535_data.als_zone);
+ lm3535_write_reg (LM3535_CONFIG_REG, value, __FUNCTION__);
+ atomic_set (&lm3535_data.use_als, 0);
+ atomic_set (&lm3535_data.als_zone, ALS_NO_ZONE);
+ mutex_unlock (&lm3535_mutex);
+ if (atomic_read (&lm3535_data.bright_zone) < 2) {
+ atomic_set (&lm3535_data.bright_zone, ALS_NO_ZONE);
+ printk_als ("%s: ALS canceled; changing brightness\n",
+ __FUNCTION__);
+ /* Adjust brightness */
+ lm3535_brightness_set (&lm3535_led, -1);
+ } else {
+ atomic_set (&lm3535_data.bright_zone, ALS_NO_ZONE);
+ }
+ lm3535_call_als_callbacks (old_zone, 0);
+ lm3535_send_als_event (0);
+ } else {
+ printk (KERN_ERR "%s: invalid command %s\n", __FUNCTION__, cmd);
+ return -EFAULT;
+ }
+
+ return count;
+}
+
+static ssize_t lm3535_als_read (struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char z[23];
+
+ if (file->private_data)
+ return 0;
+
+ if (!atomic_read (&lm3535_data.use_als)) {
+ sprintf (z, "%d\n", ALS_NO_ZONE);
+ } else {
+ sprintf (z, "%d %d\n",
+ atomic_read (&lm3535_data.als_zone),
+ atomic_read (&lm3535_data.bright_zone));
+ }
+ if (copy_to_user (buf, z, strlen (z)))
+ return -EFAULT;
+
+ file->private_data = (void *)1;
+ return strlen (z);
+}
+
+static const struct file_operations als_fops = {
+ .owner = THIS_MODULE,
+ .read = lm3535_als_read,
+ .write = lm3535_als_write,
+ .open = lm3535_als_open,
+ .release = lm3535_als_release,
+};
+
+static struct miscdevice als_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "als",
+ .fops = &als_fops,
+};
+
+static ssize_t lm3535_suspend_show (struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ sprintf (buf, "%d\n", atomic_read (&lm3535_data.in_suspend));
+ return strlen(buf)+1;
+}
+
+static ssize_t lm3535_suspend_store (struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned value = 0;
+
+ if (!buf || size == 0) {
+ printk (KERN_ERR "%s: invalid command\n", __FUNCTION__);
+ return -EINVAL;
+ }
+
+ sscanf (buf, "%d", &value);
+ if (value) {
+ printk (KERN_INFO "%s: going into TCMD SUSPEND mode\n",
+ __FUNCTION__);
+ atomic_set (&lm3535_data.in_suspend, 1);
+ lm3535_data.saved_bvalue = lm3535_led.brightness;
+ lm3535_led.brightness = 255;
+ } else {
+ printk (KERN_INFO "%s: exiting TCMD SUSPEND mode\n",
+ __FUNCTION__);
+ atomic_set (&lm3535_data.in_suspend, 0);
+ lm3535_led.brightness = lm3535_data.saved_bvalue;
+ }
+ /* Adjust brightness */
+ lm3535_brightness_set (&lm3535_led, -1);
+ return size;
+}
+static DEVICE_ATTR(suspend, 0644, lm3535_suspend_show, lm3535_suspend_store);
+
+/* This function is called by i2c_probe */
+static int lm3535_probe (struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = 0;
+ unsigned long request_flags = IRQF_TRIGGER_LOW;
+
+ gpio_request(LM3535_HWEN_GPIO, "LM3535 HWEN");
+ gpio_direction_output(LM3535_HWEN_GPIO, 1);
+ msleep(1);
+
+ /* We should be able to read and write byte data */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ printk (KERN_ERR "%s: I2C_FUNC_I2C not supported\n",
+ __FUNCTION__);
+ return -ENOTSUPP;
+ }
+
+ lm3535_data.client = client;
+ i2c_set_clientdata (client, &lm3535_data);
+
+ /* Initialize chip */
+ lm3535_setup (lm3535_data.client);
+
+ /* Initialize interrupts */
+ if (lm3535_data.revision > 1) {
+ INIT_WORK(&lm3535_data.work, lm3535_work_func);
+ if (client->irq) {
+ ret = request_irq (client->irq, lm3535_irq_handler, request_flags,
+ "lm3535", &lm3535_data);
+
+ if (ret == 0) {
+ lm3535_data.use_irq = 1;
+ ret = irq_set_irq_wake (client->irq, 1);
+ } else {
+ printk (KERN_ERR "request_irq %d for lm3535 failed: %d\n",
+ client->irq, ret);
+ free_irq (client->irq, &lm3535_data);
+ lm3535_data.use_irq = 0;
+ }
+ }
+ }
+
+ /* Register LED class */
+ ret = led_classdev_register (&client->adapter->dev, &lm3535_led);
+ if (ret) {
+ printk (KERN_ERR "%s: led_classdev_register %s failed: %d\n",
+ __FUNCTION__, lm3535_led.name, ret);
+ return ret;
+ }
+
+ /* Register LED class for no ramping */
+ ret = led_classdev_register (&client->adapter->dev, &lm3535_led_noramp);
+ if (ret) {
+ printk (KERN_ERR "%s: led_classdev_register %s failed: %d\n",
+ __FUNCTION__, lm3535_led.name, ret);
+ }
+ if ((ret = misc_register (&als_miscdev))) {
+ printk (KERN_ERR "%s: misc_register failed, error %d\n",
+ __FUNCTION__, ret);
+ led_classdev_unregister (&lm3535_led);
+ led_classdev_unregister(&lm3535_led_noramp);
+ return ret;
+ }
+
+ atomic_set (&lm3535_data.in_suspend, 0);
+ ret = device_create_file (lm3535_led.dev, &dev_attr_suspend);
+ if (ret) {
+ printk (KERN_ERR "%s: unable to create suspend device file for %s: %d\n",
+ __FUNCTION__, lm3535_led.name, ret);
+ led_classdev_unregister (&lm3535_led);
+ led_classdev_unregister(&lm3535_led_noramp);
+ misc_deregister (&als_miscdev);
+ return ret;
+ }
+ dev_set_drvdata (lm3535_led.dev, &lm3535_led);
+#if 0
+ lm3535_data.idev = input_allocate_device();
+ if (lm3535_data.idev == NULL) {
+ printk (KERN_ERR "%s: unable to allocate input device file for als\n",
+ __FUNCTION__);
+ led_classdev_unregister (&lm3535_led);
+ led_classdev_unregister(&lm3535_led_noramp);
+ misc_deregister (&als_miscdev);
+ device_remove_file (lm3535_led.dev, &dev_attr_suspend);
+ return -ENOMEM;
+ }
+ lm3535_data.idev->name = "als";
+ input_set_capability(lm3535_data.idev, EV_MSC, MSC_RAW);
+ input_set_capability(lm3535_data.idev, EV_LED, LED_MISC);
+ ret = input_register_device (lm3535_data.idev);
+ if (ret) {
+ printk (KERN_ERR "%s: unable to register input device file for als: %d\n",
+ __FUNCTION__, ret);
+ led_classdev_unregister (&lm3535_led);
+ led_classdev_unregister(&lm3535_led_noramp);
+ misc_deregister (&als_miscdev);
+ device_remove_file (lm3535_led.dev, &dev_attr_suspend);
+ input_free_device (lm3535_data.idev);
+ return ret;
+ }
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ register_early_suspend (&early_suspend_data);
+#endif
+
+ lm3535_led.brightness = 255;
+ lm3535_led_noramp.brightness = 255;
+ //lm3535_brightness_set (&lm3535_led_noramp, 255);
+ lm3535_write_reg (LM3535_BRIGHTNESS_CTRL_REG_A, 0x79, __FUNCTION__);
+ lm3535_data.initialized = 1;
+
+ return 0;
+}
+
+static irqreturn_t lm3535_irq_handler (int irq, void *dev_id)
+{
+ struct lm3535 *data_ptr = (struct lm3535 *)dev_id;
+
+ pr_debug ("%s: got an interrupt %d\n", __FUNCTION__, irq);
+
+ disable_irq (irq);
+ schedule_work (&data_ptr->work);
+
+ return IRQ_HANDLED;
+}
+
+static void lm3535_send_als_event (int zone)
+{
+#ifdef CONFIG_ALS_UEVENT
+ char event_string[20];
+ char *envp[] = {event_string, NULL};
+ int ret;
+
+ sprintf (event_string, "ALS_ZONE=%d", zone);
+ ret = kobject_uevent_env (&als_miscdev.this_device->kobj,
+ KOBJ_CHANGE, envp);
+ if (ret) {
+ printk (KERN_ERR "%s: kobject_uevent_env failed: %d\n",
+ __FUNCTION__, ret);
+ } else {
+ printk_event ("%s: kobject_uevent_env %s success\n",
+ __FUNCTION__, event_string);
+ }
+#else
+ //input_event (lm3535_data.idev, EV_MSC, MSC_RAW, light_value);
+ input_event (lm3535_data.idev, EV_LED, LED_MISC, zone);
+ input_sync (lm3535_data.idev);
+
+#endif
+}
+
+static void lm3535_call_als_callbacks(unsigned old_zone, unsigned zone)
+{
+ struct als_callback *c;
+ unsigned old, new;
+
+ old = (old_zone == ALS_NO_ZONE) ? 0 : old_zone;
+ new = (zone == ALS_NO_ZONE) ? 0 : zone;
+
+ mutex_lock (&als_cb_mutex);
+ list_for_each_entry(c, &als_callbacks, entry) {
+ c->cb(old, new, c->cookie);
+ }
+ mutex_unlock (&als_cb_mutex);
+}
+
+static void lm3535_work_func (struct work_struct *work)
+{
+ int ret;
+ uint8_t reg;
+ unsigned zone, old_zone;
+
+ pr_debug ("%s: work function called\n", __FUNCTION__);
+ ret = lm3535_read_reg (LM3535_ALS_REG, &reg);
+ if (ret) {
+ if (reg & ALS_FLAG_MASK) {
+ zone = reg & ALS_ZONE_MASK;
+ if (zone > 4)
+ zone = 4;
+ old_zone = atomic_read (&lm3535_data.als_zone);
+ printk_als ("%s: ALS zone changed: %d => %d, register = 0x%x\n",
+ __FUNCTION__, old_zone, zone, reg);
+ atomic_set (&lm3535_data.als_zone, zone);
+ if (zone > atomic_read (&lm3535_data.bright_zone) ||
+ atomic_read (&lm3535_data.bright_zone) == ALS_NO_ZONE) {
+ atomic_set (&lm3535_data.bright_zone, zone);
+ if (!atomic_read (&lm3535_data.in_suspend)) {
+ printk_als ("%s: ALS zone increased; changing brightness\n",
+ __FUNCTION__);
+ /* Adjust brightness */
+ lm3535_brightness_set (&lm3535_led, -1);
+ } else {
+ printk_als ("%s: ALS zone increased; SUSPEND mode - not changing brightness\n",
+ __FUNCTION__);
+ }
+ }
+ /* See if PWM needs to be changed */
+ if (old_zone < 2 && zone >= 2) {
+ lm3535_write_reg (LM3535_CONFIG_REG, CONFIG_VALUE | 0x80,
+ __FUNCTION__);
+ printk_als ("%s: moved from dim/dark to bright; disable PWM\n",
+ __FUNCTION__);
+ } else if (old_zone >= 2 && zone < 2) {
+ lm3535_write_reg (LM3535_CONFIG_REG,
+ CONFIG_VALUE|0x80|pwm_value,
+ __FUNCTION__);
+ printk_als ("%s: moved from bright to dim/dark; enable PWM\n",
+ __FUNCTION__);
+ }
+ if (!atomic_read (&lm3535_data.in_suspend)) {
+ lm3535_call_als_callbacks (old_zone, zone);
+ }
+ lm3535_send_als_event (zone);
+ } else {
+ printk_als ("%s: got ALS interrupt but flag is not set: 0x%x\n",
+ __FUNCTION__, reg);
+ }
+ }
+ if (atomic_read (&lm3535_data.do_als_config)) {
+ lm3535_write_reg (LM3535_ALS_CTRL_REG, ALS_AVERAGING, __FUNCTION__);
+ atomic_set (&lm3535_data.do_als_config, 0);
+ printk_als ("%s: configured ALS averaging 0x%x\n",
+ __FUNCTION__, ALS_AVERAGING);
+ }
+ enable_irq (lm3535_data.client->irq);
+}
+
+#if 0
+static enum hrtimer_restart lm3535_timer_func (struct hrtimer *timer)
+{
+ schedule_work(&lm3535_data.work);
+
+ hrtimer_start(&lm3535_data.timer,
+ ktime_set(1, 0), HRTIMER_MODE_REL);
+ return HRTIMER_NORESTART;
+}
+#endif
+
+static void lm3535_set_options_r1 (uint8_t *buf, unsigned ramp)
+{
+ struct lm3535_options_register_r1 *r =
+ (struct lm3535_options_register_r1 *)buf;
+
+ if (!r)
+ return;
+ *buf = 0;
+ r->rs = ramp;
+ r->gt = 0x2;
+ r->rev = 0;
+}
+
+static void lm3535_set_options_r2 (uint8_t *buf, unsigned ramp)
+{
+ struct lm3535_options_register_r2 *r =
+ (struct lm3535_options_register_r2 *)buf;
+
+ if (!r)
+ return;
+ *buf = 0;
+ r->rs_up = ramp;
+ r->rs_down = ramp;
+ r->gt = 0;
+}
+
+static void lm3535_set_options_r3 (uint8_t *buf, unsigned ramp)
+{
+ struct lm3535_options_register_r3 *r =
+ (struct lm3535_options_register_r3 *)buf;
+
+ if (!r)
+ return;
+ *buf = 0;
+ r->rs_up = ramp;
+ r->rs_down = ramp;
+ r->gt = 0x02;
+}
+
+/* This function calculates ramp step time so that total ramp time is
+ * equal to ramp_time defined currently at 200ms
+ */
+static int lm3535_set_ramp (struct i2c_client *client,
+ unsigned int on, unsigned int nsteps, unsigned int *rtime)
+{
+ int ret, i = 0;
+ uint8_t value = 0;
+ unsigned int total_time = 0;
+
+ if (on) {
+ /* Calculate the closest possible ramp time */
+ for (i = 0; i < lm3535_data.nramp; i++) {
+ total_time = nsteps * lm3535_ramp[i];
+ if (total_time >= ramp_time)
+ break;
+ }
+ if (i > 0 && total_time > ramp_time) {
+#if 0
+ /* If previous value is closer */
+ if (total_time - ramp_time >
+ ramp_time - nsteps * lm3535_ramp[i-1]) {
+ i--;
+ total_time = nsteps * lm3535_ramp[i];
+ }
+#endif
+ i--;
+ total_time = nsteps * lm3535_ramp[i];
+ }
+ lm3535_set_options_f (&value, i);
+ }
+
+#if 0
+ printk (KERN_ERR "%s: ramp = %s, ramp step = %d us (total = %d us)\n",
+ __FUNCTION__, on ? "on" : "off", lm3535_ramp[i], total_time);
+#endif
+ if (rtime)
+ *rtime = total_time;
+ ret = lm3535_write_reg (LM3535_OPTIONS_REG, value, __FUNCTION__);
+#if 0
+ printk_br ("%s: nsteps = %d, OPTIONS_REG = 0x%x, total ramp = %dus\n",
+ __FUNCTION__, nsteps, value, lm3535_ramp[i] * nsteps);
+#endif
+ return ret;
+}
+
+static int lm3535_enable(struct i2c_client *client, unsigned int on)
+{
+ int ret;
+ uint8_t value = 0x0F; // Enable A
+
+ if (on) {
+ gpio_set_value(LM3535_HWEN_GPIO, 1);
+ msleep(1);
+ } else
+ value = 0;
+
+ ret = lm3535_write_reg (LM3535_ENABLE_REG, value, __FUNCTION__);
+ if (ret < 0)
+ return ret;
+
+ if (lm3535_data.revision == 2 || lm3535_data.revision == 3) {
+ if (on) {
+ ret = lm3535_write_reg (LM3535_TRIM_REG, LM3535_TRIM_VALUE,
+ __FUNCTION__);
+ }
+ }
+ if (!on)
+ gpio_set_value(LM3535_HWEN_GPIO, 0);
+
+ lm3535_data.enabled = on;
+ return ret;
+}
+
+static int lm3535_configure (void)
+{
+ int ret = 0;
+
+#if 1
+ /* On G2, we are not using ALS interrupt so we want to leave the ALS
+ interrupt disabled and ALS resistor in high impedance mode */
+ /* Disable ALS interrupt */
+ lm3535_write_reg(LM3535_CONFIG_REG, 0, __func__);
+ /* Put ALS Resistor into high impedance mode to save current */
+ lm3535_write_reg(LM3535_ALS_RESISTOR_REG, 0, __func__);
+
+#else
+ uint8_t value = 0x03;
+ uint8_t reg = 0;
+ unsigned old_zone = 0;
+ unsigned new_zone = ALS_NO_ZONE;
+
+ /* Config register bits:
+ * Rev1: AVE2 AVE1 AVE0 ALS-SD ALS-EN PWM-EN 53A 62A
+ * Rev2: ALSF ALS-SD ALS-ENB ALS-ENA 62A 53A PWM-P PWM-EN
+ * Rev3: ALSF ALS-EN ALS-ENB ALS-ENA 62A 53A PWM-P PWM-EN
+ *
+ * ALSF: sets E1B/INT pin to interrupt Pin; 0 = D1B, 1 = INT.
+ * Open Drain Interrupt. Pulls low when change occurs. Flag cleared
+ * once a I2C read command for register 0x40 occurs.
+ * ALS-SD (rev2) and ALS-EN (final): turn off ALS feature
+ * ALS-SD: 0 = Active, 1 = Shutdown. Rev2 cannot have ALS active without
+ * LEDs turned on (ENxx bits = 1).
+ * ALS-EN: 0 = Shutdown, 1 = Active. Final can have ALS active without
+ * LEDs turned on. ALS-EN overrides the ENC bit.
+ * ALS-ENB and ALS-ENA: 1 enables ALS control of diode current. BankA has
+ * full ALS control, BankB just has on/off ability.
+ * 62A and 53A: 1 sets D62 and D53 to BankA (Required for 6 LEDs in BankA).
+ * 0 sets them to BankB.
+ * PWM-P: PWM Polarity, 0 = Active (Diodes on) High, 1 = Active (Diodes on)
+ * Low.
+ * PWM-EN: Enables PWM Functionality. 1 = Active.
+ */
+
+#ifndef CONFIG_MAC_MOT
+#ifdef CONFIG_LM3535_ESD_RECOVERY
+ /* Configure lighting zone max brightness */
+ lm3535_write_reg (LM3535_ALS_Z0T_REG, als_zone_max[0],
+ __FUNCTION__);
+ lm3535_write_reg (LM3535_ALS_Z1T_REG, als_zone_max[1],
+ __FUNCTION__);
+ lm3535_write_reg (LM3535_ALS_Z2T_REG, als_zone_max[2],
+ __FUNCTION__);
+ lm3535_write_reg (LM3535_ALS_Z3T_REG, als_zone_max[3],
+ __FUNCTION__);
+ lm3535_write_reg (LM3535_ALS_Z4T_REG, als_zone_max[4],
+ __FUNCTION__);
+#endif
+#endif
+
+ if (lm3535_data.revision > 1) {
+ /* Configure internal ALS resistor register */
+ ret = lm3535_write_reg (LM3535_ALS_RESISTOR_REG,
+ (uint8_t)resistor_value, __FUNCTION__);
+
+#ifndef CONFIG_MAC_MOT
+#ifdef CONFIG_LM3535_BUTTON_BL
+ ret = lm3535_write_reg (LM3535_ALS_SELECT_REG, 0x2,
+ __FUNCTION__);
+#endif
+#endif
+ /* Configure lighting zone boundaries */
+ lm3535_write_reg (LM3535_ALS_ZB0_REG, als_zb[0], __FUNCTION__);
+ lm3535_write_reg (LM3535_ALS_ZB1_REG, als_zb[1], __FUNCTION__);
+ lm3535_write_reg (LM3535_ALS_ZB2_REG, als_zb[2], __FUNCTION__);
+ lm3535_write_reg (LM3535_ALS_ZB3_REG, als_zb[3], __FUNCTION__);
+
+ /* Configure ALS averaging to be very short the first time */
+ lm3535_write_reg (LM3535_ALS_CTRL_REG, 0x80, __FUNCTION__);
+ }
+
+ if (lm3535_data.revision == 0) {
+ value = 0x3; // Just enable A, don't bother with ALS
+ atomic_set (&lm3535_data.use_als, 0);
+ } else if (lm3535_data.revision == 1) {
+ value = 0x0C; // No ALS or PWM
+ atomic_set (&lm3535_data.use_als, 0);
+ } else {
+ if (atomic_read (&lm3535_data.use_als))
+ value = CONFIG_VALUE;
+ else
+ value = CONFIG_VALUE_NO_ALS;
+ }
+ pr_debug ("%s: use_als is %d, value = 0x%x\n", __FUNCTION__,
+ atomic_read (&lm3535_data.use_als), value);
+ ret = lm3535_write_reg (LM3535_CONFIG_REG, value, __FUNCTION__);
+ // Nothing else to do for older revisions
+ if (lm3535_data.revision <= 1) {
+ return 0;
+ }
+
+ /* Has to be at least 300ms even with ALS averaging set to 0 */
+ msleep_interruptible (als_sleep); // Wait for ALS to kick in
+ /* Read current ALS zone */
+ old_zone = atomic_read (&lm3535_data.als_zone);
+ if (atomic_read (&lm3535_data.use_als)) {
+ ret = lm3535_read_reg (LM3535_ALS_REG, &reg);
+ if (ret) {
+ new_zone = reg & ALS_ZONE_MASK;
+ if (new_zone > 4) {
+ new_zone = 4;
+ }
+ atomic_set (&lm3535_data.als_zone, new_zone);
+ printk_als ("%s: ALS Register: 0x%x, zone %d\n",
+ __FUNCTION__, reg, atomic_read (&lm3535_data.als_zone));
+ } else {
+ atomic_set (&lm3535_data.als_zone, ALS_NO_ZONE);
+ atomic_set (&lm3535_data.use_als, 0);
+ printk (KERN_ERR "%s: unable to read ALS zone; disabling ALS\n",
+ __FUNCTION__);
+ }
+ } else {
+ atomic_set (&lm3535_data.als_zone, ALS_NO_ZONE);
+ }
+ /* Brightness zone is for now the same as ALS zone */
+ atomic_set (&lm3535_data.bright_zone,
+ atomic_read (&lm3535_data.als_zone));
+ lm3535_call_als_callbacks (old_zone, new_zone);
+ if (lm3535_data.initialized) {
+ lm3535_send_als_event (new_zone);
+ }
+ if (atomic_read (&lm3535_data.use_als)) {
+ /* Configure averaging */
+ //lm3535_write_reg (LM3535_ALS_CTRL_REG, ALS_AVERAGING, __FUNCTION__);
+ /* Enable interrupt and PWM for CABC */
+ if (new_zone <= 1) {
+ lm3535_write_reg (LM3535_CONFIG_REG, CONFIG_VALUE|0x80|pwm_value,
+ __FUNCTION__);
+ } else {
+ lm3535_write_reg (LM3535_CONFIG_REG, CONFIG_VALUE | 0x80,
+ __FUNCTION__);
+ }
+ } else {
+ lm3535_write_reg (LM3535_CONFIG_REG, CONFIG_VALUE | pwm_value,
+ __FUNCTION__);
+ }
+#endif
+
+ return ret;
+}
+
+static int lm3535_setup (struct i2c_client *client)
+{
+ int ret;
+ uint8_t value;
+
+ /* Read revision number */
+ ret = lm3535_read_reg (LM3535_ALS_CTRL_REG, &value);
+ if (ret < 0) {
+ printk (KERN_ERR "%s: unable to read from chip: %d\n",
+ __FUNCTION__, ret);
+ printk(KERN_ERR "client->addr = %x failed\n", client->addr);
+
+ /* If the first I2C address doesn't work, try 0x36 */
+ client->addr = 0x36;
+ ret = lm3535_read_reg (LM3535_ALS_CTRL_REG, &value);
+ if (ret < 0) {
+ printk (KERN_ERR "%s: unable to read from chip: %d\n",
+ __FUNCTION__, ret);
+ printk(KERN_ERR "client->addr = %x failed\n", client->addr);
+ return ret;
+ }
+ }
+
+ switch (value) {
+ case 0xFF: lm3535_data.revision = 0; break;
+ case 0xF0: lm3535_data.revision = 1; break;
+ case 0x02: lm3535_data.revision = 2; break;
+ case 0x00: lm3535_data.revision = 3; break;
+ case 0x01: lm3535_data.revision = 4; break;
+ default: lm3535_data.revision = 4; break; // Assume final
+ }
+ /* revision is going to be an index to lm3535_ramp array */
+ printk (KERN_INFO "%s: revision %d (0x%X)\n",
+ __FUNCTION__, lm3535_data.revision+1, value);
+ if (lm3535_data.revision == 0) {
+ lm3535_ramp = lm3535_ramp_r1;
+ lm3535_set_options_f = lm3535_set_options_r1;
+ } else if (lm3535_data.revision == 1) {
+ lm3535_ramp = lm3535_ramp_r2;
+ lm3535_set_options_f = lm3535_set_options_r2;
+ } else {
+ lm3535_ramp = lm3535_ramp_r3;
+ lm3535_set_options_f = lm3535_set_options_r3;
+ lm3535_data.nramp = 8;
+ }
+
+ /* PWM */
+ if (lm3535_data.revision < 4) {
+ pwm_value = 0;
+ }
+ atomic_set (&lm3535_data.als_zone, ALS_NO_ZONE);
+ atomic_set (&lm3535_data.use_als, 1);
+ atomic_set (&lm3535_data.do_als_config, 1);
+ ret = lm3535_configure ();
+ if (ret < 0)
+ return ret;
+ ret = lm3535_enable(client, 1);
+ if (ret < 0)
+ return ret;
+
+ //hrtimer_init (&lm3535_data.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ //lm3535_data.timer.function = lm3535_timer_func;
+ //hrtimer_start(&lm3535_data.timer, ktime_set(2, 0), HRTIMER_MODE_REL);
+
+ return ret;
+}
+
+static int lm3535_remove (struct i2c_client *client)
+{
+ struct lm3535 *data_ptr = i2c_get_clientdata(client);
+ if (data_ptr->use_irq)
+ free_irq (client->irq, data_ptr);
+ led_classdev_unregister (&lm3535_led);
+ led_classdev_unregister(&lm3535_led_noramp);
+/* led_classdev_unregister (&lm3535_led_noramp); */
+ misc_deregister (&als_miscdev);
+ device_remove_file (lm3535_led.dev, &dev_attr_suspend);
+#if 0
+ input_unregister_device (lm3535_data.idev);
+ input_free_device (lm3535_data.idev);
+#endif
+ return 0;
+}
+
+static int lm3535_suspend (struct i2c_client *client, pm_message_t mesg)
+{
+ printk_suspend ("%s: called with pm message %d\n",
+ __FUNCTION__, mesg.event);
+
+ led_classdev_suspend (&lm3535_led);
+
+#if 0
+ /* Disable ALS interrupt */
+ lm3535_write_reg (LM3535_CONFIG_REG, 0, __FUNCTION__);
+ /* Put ALS Resistor into high impedance mode to save current */
+ lm3535_write_reg (LM3535_ALS_RESISTOR_REG, 0, __FUNCTION__);
+
+ /* Reset ALS averaging */
+ lm3535_write_reg (LM3535_ALS_CTRL_REG, 0, __FUNCTION__);
+ atomic_set (&lm3535_data.do_als_config, 1);
+#endif
+
+ gpio_set_value(LM3535_HWEN_GPIO, 0);
+
+ return 0;
+}
+
+static int lm3535_resume (struct i2c_client *client)
+{
+ printk_suspend ("%s: resuming\n", __FUNCTION__);
+ mutex_lock (&lm3535_mutex);
+
+ /* If LED was disabled going into suspend, we don't want to enable yet
+ until brightness is set to non-zero value */
+ if (lm3535_data.enabled == 0) {
+ gpio_set_value(LM3535_HWEN_GPIO, 1);
+ udelay(10);
+
+ lm3535_configure();
+ }
+ mutex_unlock (&lm3535_mutex);
+ led_classdev_resume (&lm3535_led);
+ printk_suspend ("%s: driver resumed\n", __FUNCTION__);
+
+ return 0;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void lm3535_early_suspend (struct early_suspend *h)
+{
+ lm3535_suspend (lm3535_data.client, PMSG_SUSPEND);
+}
+
+static void lm3535_late_resume (struct early_suspend *h)
+{
+ lm3535_resume (lm3535_data.client);
+}
+#endif
+
+
+static int __init lm3535_init (void)
+{
+ int ret;
+
+ printk (KERN_INFO "%s: enter\n", __FUNCTION__);
+ ret = i2c_add_driver (&lm3535_driver);
+ if (ret) {
+ printk (KERN_ERR "%s: i2c_add_driver failed, error %d\n",
+ __FUNCTION__, ret);
+ }
+
+ return ret;
+}
+
+static void __exit lm3535_exit(void)
+{
+ i2c_del_driver (&lm3535_driver);
+}
+
+static char *reg_name (int reg)
+{
+ switch (reg) {
+ case (LM3535_ENABLE_REG): return "ENABLE"; break;
+ case (LM3535_CONFIG_REG): return "CONFIG"; break;
+ case (LM3535_OPTIONS_REG): return "OPTIONS"; break;
+ case (LM3535_ALS_REG): return "ALS"; break;
+ case (LM3535_ALS_RESISTOR_REG): return "ALS_RESISTOR"; break;
+ case (LM3535_ALS_CTRL_REG): return "ALS CONTROL"; break;
+ case (LM3535_BRIGHTNESS_CTRL_REG_A): return "BRIGHTNESS_CTRL_A"; break;
+ case (LM3535_BRIGHTNESS_CTRL_REG_B): return "BRIGHTNESS_CTRL_B"; break;
+ case (LM3535_BRIGHTNESS_CTRL_REG_C): return "BRIGHTNESS_CTRL_C"; break;
+ case (LM3535_ALS_ZB0_REG): return "ALS_ZB0"; break;
+ case (LM3535_ALS_ZB1_REG): return "ALS_ZB1"; break;
+ case (LM3535_ALS_ZB2_REG): return "ALS_ZB2"; break;
+ case (LM3535_ALS_ZB3_REG): return "ALS_ZB3"; break;
+ case (LM3535_ALS_Z0T_REG): return "ALS_Z0T"; break;
+ case (LM3535_ALS_Z1T_REG): return "ALS_Z1T"; break;
+ case (LM3535_ALS_Z2T_REG): return "ALS_Z2T"; break;
+ case (LM3535_ALS_Z3T_REG): return "ALS_Z3T"; break;
+ case (LM3535_ALS_Z4T_REG): return "ALS_Z4T"; break;
+ case (LM3535_TRIM_REG): return "TRIM"; break;
+ default: return "UNKNOWN"; break;
+ }
+ return "UNKNOWN";
+}
+
+unsigned lmxxxx_detect_esd (void)
+{
+ uint8_t value = 0;
+
+ if (!lm3535_data.initialized) {
+ printk (KERN_ERR "%s: not initialized\n", __FUNCTION__);
+ return 0;
+ }
+ //0 - no ESD, 1 - ESD
+ lm3535_read_reg (LM3535_ALS_RESISTOR_REG, &value);
+ if (value != (uint8_t)resistor_value)
+ return 1;
+ return 0;
+}
+EXPORT_SYMBOL(lmxxxx_detect_esd);
+
+void lmxxxx_fix_esd (void)
+{
+ if (!lm3535_data.initialized) {
+ printk (KERN_ERR "%s: not initialized\n", __FUNCTION__);
+ return;
+ }
+ mutex_lock (&lm3535_mutex);
+ lm3535_configure ();
+ mutex_unlock (&lm3535_mutex);
+ return;
+}
+EXPORT_SYMBOL(lmxxxx_fix_esd);
+
+void lmxxxx_set_pwm (unsigned en_dis)
+{
+ if (en_dis) {
+ pwm_value = 1;
+ } else {
+ pwm_value = 0;
+ }
+ printk (KERN_INFO "%s: setting PWM to %d\n",
+ __FUNCTION__, pwm_value);
+}
+EXPORT_SYMBOL(lmxxxx_set_pwm);
+module_init(lm3535_init);
+module_exit(lm3535_exit);
+
+MODULE_DESCRIPTION("LM3535 DISPLAY BACKLIGHT DRIVER");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index d54e985748b..2bed2fd1315 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -356,6 +356,12 @@ config EZX_PCAP
This enables the PCAP ASIC present on EZX Phones. This is
needed for MMC, TouchScreen, Sound, USB, etc..
+config MFD_CPCAP
+ tristate "Support for CPCAP"
+ depends on SPI
+ help
+ Say yes here if you want to include drivers for the CPCAP chip.
+
config MFD_VIPERBOARD
tristate "Nano River Technologies Viperboard"
select MFD_CORE
@@ -602,6 +608,12 @@ config MFD_DB8500_PRCMU
system controller running an XP70 microprocessor, which is accessed
through a register map.
+config MFD_M4SENSORHUB
+ tristate "Support for M4 Sensor Hub"
+ depends on I2C
+ help
+ Say yes here if you want to include drivers for the M4 sensor hub.
+
config MFD_STMPE
bool "STMicroelectronics STMPE"
depends on (I2C=y || SPI_MASTER=y) && GENERIC_HARDIRQS
@@ -841,6 +853,13 @@ config MFD_TPS65912_SPI
If you say yes here you get support for the TPS65912 series of
PM chips with SPI interface.
+config MFD_TPS65912_DEBUGFS
+ bool "TI TPS65912 Power Management chip debugfs support"
+ depends on MFD_TPS65912 && DEBUG_FS
+ help
+ If you say yes here you get support for the TPS65912 series of
+ PM chips debugfs register access.
+
config MFD_TPS80031
bool "TI TPS80031/TPS80032 Power Management chips"
depends on I2C=y && GENERIC_HARDIRQS
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 718e94a2a9a..c4aa99058c7 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -64,6 +64,7 @@ tps65912-objs := tps65912-core.o tps65912-irq.o
obj-$(CONFIG_MFD_TPS65912) += tps65912.o
obj-$(CONFIG_MFD_TPS65912_I2C) += tps65912-i2c.o
obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o
+obj-$(CONFIG_MFD_TPS65912_DEBUGFS) += tps65912-debugfs.o
obj-$(CONFIG_MFD_TPS80031) += tps80031.o
obj-$(CONFIG_MENELAUS) += menelaus.o
@@ -155,3 +156,21 @@ obj-$(CONFIG_MFD_LM3533) += lm3533-core.o lm3533-ctrlbank.o
obj-$(CONFIG_VEXPRESS_CONFIG) += vexpress-config.o vexpress-sysreg.o
obj-$(CONFIG_MFD_RETU) += retu-mfd.o
obj-$(CONFIG_MFD_AS3711) += as3711.o
+
+cpcap-objs := cpcap-core.o \
+ cpcap-irq.o \
+ cpcap-regacc.o \
+ cpcap-usb-det.o \
+ cpcap-key.o \
+ cpcap-adc.o \
+ cpcap-uc.o
+obj-$(CONFIG_MFD_CPCAP) += cpcap.o
+
+m4sensorhub-objs := m4sensorhub-core.o \
+ m4sensorhub-reg.o \
+ m4sensorhub-irq.o \
+ m4sensorhub-panic.o \
+ m4sensorhub-stm32-fw.o
+
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub.o
+
diff --git a/drivers/mfd/cpcap-adc.c b/drivers/mfd/cpcap-adc.c
new file mode 100644
index 00000000000..6389c23ea61
--- /dev/null
+++ b/drivers/mfd/cpcap-adc.c
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2009-2010 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+#include <linux/spi/spi.h>
+
+
+#define MAX_ADC_FIFO_DEPTH 8 /* this must be a power of 2 */
+#define MAX_TEMP_LVL 27
+#define FOUR_POINT_TWO_ADC 801
+
+#define DEFAULT_ICHARGE_SENSE_RES 100
+#define DEFAULT_ISENSE_RANGE 5000
+
+struct cpcap_adc {
+ struct cpcap_device *cpcap;
+
+ /* Private stuff */
+ struct cpcap_adc_request *queue[MAX_ADC_FIFO_DEPTH];
+ int queue_head;
+ int queue_tail;
+ struct mutex queue_mutex;
+ struct delayed_work work;
+};
+
+struct phasing_tbl {
+ short offset;
+ unsigned short multiplier;
+ unsigned short divider;
+ short min;
+ short max;
+};
+
+static struct phasing_tbl bank0_phasing[CPCAP_ADC_BANK0_NUM] = {
+ [CPCAP_ADC_AD0_BATTDETB] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_BATTP] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_VBUS] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_AD3] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_BPLUS_AD4] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_CHG_ISENSE] = {0, 0x80, 0x80, -512, 511},
+ [CPCAP_ADC_BATTI_ADC] = {0, 0x80, 0x80, -512, 511},
+ [CPCAP_ADC_USB_ID] = {0, 0x80, 0x80, 0, 1023},
+};
+
+static struct phasing_tbl bank1_phasing[CPCAP_ADC_BANK1_NUM] = {
+ [CPCAP_ADC_AD8] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_AD9] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_LICELL] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_HV_BATTP] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_TSX1_AD12] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_TSX2_AD13] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_TSY1_AD14] = {0, 0x80, 0x80, 0, 1023},
+ [CPCAP_ADC_TSY2_AD15] = {0, 0x80, 0x80, 0, 1023},
+};
+
+enum conv_type {
+ CONV_TYPE_NONE,
+ CONV_TYPE_DIRECT,
+ CONV_TYPE_MAPPING,
+};
+
+struct conversion_tbl {
+ enum conv_type conv_type;
+ int align_offset;
+ int conv_offset;
+ int cal_offset;
+ int multiplier;
+ int divider;
+};
+
+static struct conversion_tbl bank0_conversion[CPCAP_ADC_BANK0_NUM] = {
+ [CPCAP_ADC_AD0_BATTDETB] = {
+ CONV_TYPE_MAPPING, 0, 0, 0, 1, 1},
+ [CPCAP_ADC_BATTP] = {
+ CONV_TYPE_DIRECT, 0, 2400, 0, 2300, 1023},
+ [CPCAP_ADC_VBUS] = {
+ CONV_TYPE_DIRECT, 0, 0, 0, 10000, 1023},
+ [CPCAP_ADC_AD3] = {
+ CONV_TYPE_MAPPING, 0, 0, 0, 1, 1},
+ [CPCAP_ADC_BPLUS_AD4] = {
+ CONV_TYPE_DIRECT, 0, 2400, 0, 2300, 1023},
+ /* ISENSE multiplier is updated in probe function below */
+ [CPCAP_ADC_CHG_ISENSE] = {
+ CONV_TYPE_DIRECT, -512, 2, 0, 5000, 1023},
+ [CPCAP_ADC_BATTI_ADC] = {
+ CONV_TYPE_DIRECT, -512, 2, 0, 5000, 1023},
+ [CPCAP_ADC_USB_ID] = {
+ CONV_TYPE_NONE, 0, 0, 0, 1, 1},
+};
+
+static struct conversion_tbl bank1_conversion[CPCAP_ADC_BANK1_NUM] = {
+ [CPCAP_ADC_AD8] = {CONV_TYPE_NONE, 0, 0, 0, 1, 1},
+ [CPCAP_ADC_AD9] = {CONV_TYPE_NONE, 0, 0, 0, 1, 1},
+ [CPCAP_ADC_LICELL] = {CONV_TYPE_DIRECT, 0, 0, 0, 3400, 1023},
+ [CPCAP_ADC_HV_BATTP] = {CONV_TYPE_NONE, 0, 0, 0, 1, 1},
+ [CPCAP_ADC_TSX1_AD12] = {CONV_TYPE_NONE, 0, 0, 0, 1, 1},
+ [CPCAP_ADC_TSX2_AD13] = {CONV_TYPE_NONE, 0, 0, 0, 1, 1},
+ [CPCAP_ADC_TSY1_AD14] = {CONV_TYPE_NONE, 0, 0, 0, 1, 1},
+ [CPCAP_ADC_TSY2_AD15] = {CONV_TYPE_NONE, 0, 0, 0, 1, 1},
+};
+
+static const unsigned short temp_map[MAX_TEMP_LVL][2] = {
+ {0x03ff, 233}, /* -40C */
+ {0x03ff, 238}, /* -35C */
+ {0x03ef, 243}, /* -30C */
+ {0x03b2, 248}, /* -25C */
+ {0x036c, 253}, /* -20C */
+ {0x0320, 258}, /* -15C */
+ {0x02d0, 263}, /* -10C */
+ {0x027f, 268}, /* -5C */
+ {0x022f, 273}, /* 0C */
+ {0x01e4, 278}, /* 5C */
+ {0x019f, 283}, /* 10C */
+ {0x0161, 288}, /* 15C */
+ {0x012b, 293}, /* 20C */
+ {0x00fc, 298}, /* 25C */
+ {0x00d4, 303}, /* 30C */
+ {0x00b2, 308}, /* 35C */
+ {0x0095, 313}, /* 40C */
+ {0x007d, 318}, /* 45C */
+ {0x0069, 323}, /* 50C */
+ {0x0059, 328}, /* 55C */
+ {0x004b, 333}, /* 60C */
+ {0x003f, 338}, /* 65C */
+ {0x0036, 343}, /* 70C */
+ {0x002e, 348}, /* 75C */
+ {0x0027, 353}, /* 80C */
+ {0x0022, 358}, /* 85C */
+ {0x001d, 363}, /* 90C */
+};
+
+static unsigned short convert_to_kelvins(unsigned short value)
+{
+ int i;
+ unsigned short result = 0;
+ signed short alpha = 0;
+
+ if (value <= temp_map[MAX_TEMP_LVL - 1][0])
+ return temp_map[MAX_TEMP_LVL - 1][1];
+
+ if (value >= temp_map[0][0])
+ return temp_map[0][1];
+
+ for (i = 0; i < MAX_TEMP_LVL - 1; i++) {
+ if ((value <= temp_map[i][0]) &&
+ (value >= temp_map[i+1][0])) {
+ if (value == temp_map[i][0])
+ result = temp_map[i][1];
+ else if (value == temp_map[i+1][0])
+ result = temp_map[i+1][1];
+ else {
+ alpha = ((value - temp_map[i][0])*1000)/
+ (temp_map[i+1][0] - temp_map[i][0]);
+
+ result = temp_map[i][1] +
+ ((alpha*(temp_map[i+1][1] -
+ temp_map[i][1]))/1000);
+ }
+ break;
+ }
+ }
+ return result;
+}
+
+static void adc_setup(struct cpcap_device *cpcap,
+ struct cpcap_adc_request *req)
+{
+ struct cpcap_adc_ato *ato;
+ struct cpcap_platform_data *data;
+ unsigned short value1 = 0;
+ unsigned short value2 = 0;
+
+ data = cpcap->spi->controller_data;
+ ato = data->adc_ato;
+
+ if (req->type == CPCAP_ADC_TYPE_BANK_1)
+ value1 |= CPCAP_BIT_AD_SEL1;
+ else if (req->type == CPCAP_ADC_TYPE_BATT_PI)
+ value1 |= CPCAP_BIT_RAND1;
+
+ switch (req->timing) {
+ case CPCAP_ADC_TIMING_IN:
+ value1 |= ato->ato_in;
+ value1 |= ato->atox_in;
+ value2 |= ato->adc_ps_factor_in;
+ value2 |= ato->atox_ps_factor_in;
+ break;
+
+ case CPCAP_ADC_TIMING_OUT:
+ value1 |= ato->ato_out;
+ value1 |= ato->atox_out;
+ value2 |= ato->adc_ps_factor_out;
+ value2 |= ato->atox_ps_factor_out;
+ break;
+
+ case CPCAP_ADC_TIMING_IMM:
+ default:
+ break;
+ }
+
+ cpcap_regacc_write(cpcap, CPCAP_REG_ADCC1, value1,
+ (CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX |
+ CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 |
+ CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 |
+ CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 |
+ CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 |
+ CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0));
+
+ cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, value2,
+ (CPCAP_BIT_ATOX_PS_FACTOR |
+ CPCAP_BIT_ADC_PS_FACTOR1 |
+ CPCAP_BIT_ADC_PS_FACTOR0));
+
+ if (req->timing == CPCAP_ADC_TIMING_IMM) {
+ cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS,
+ CPCAP_BIT_ADTRIG_DIS);
+ cpcap_irq_clear(cpcap, CPCAP_IRQ_ADCDONE);
+ cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ASC,
+ CPCAP_BIT_ASC);
+ } else {
+ cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_ONESHOT,
+ CPCAP_BIT_ADTRIG_ONESHOT);
+ cpcap_irq_clear(cpcap, CPCAP_IRQ_ADCDONE);
+ cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+ 0,
+ CPCAP_BIT_ADTRIG_DIS);
+ }
+
+ schedule_delayed_work(&((struct cpcap_adc *)(cpcap->adcdata))->work,
+ msecs_to_jiffies(500));
+
+ cpcap_irq_unmask(cpcap, CPCAP_IRQ_ADCDONE);
+}
+
+static void adc_setup_calibrate(struct cpcap_device *cpcap,
+ enum cpcap_adc_bank0 chan)
+{
+ unsigned short value = 0;
+ unsigned long timeout = jiffies + msecs_to_jiffies(11);
+
+ if ((chan != CPCAP_ADC_CHG_ISENSE) &&
+ (chan != CPCAP_ADC_BATTI_ADC))
+ return;
+
+ value |= CPCAP_BIT_CAL_MODE | CPCAP_BIT_RAND0;
+ value |= ((chan << 4) &
+ (CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 | CPCAP_BIT_ADA0));
+
+ cpcap_regacc_write(cpcap, CPCAP_REG_ADCC1, value,
+ (CPCAP_BIT_CAL_MODE | CPCAP_BIT_ATOX |
+ CPCAP_BIT_ATO3 | CPCAP_BIT_ATO2 |
+ CPCAP_BIT_ATO1 | CPCAP_BIT_ATO0 |
+ CPCAP_BIT_ADA2 | CPCAP_BIT_ADA1 |
+ CPCAP_BIT_ADA0 | CPCAP_BIT_AD_SEL1 |
+ CPCAP_BIT_RAND1 | CPCAP_BIT_RAND0));
+
+ cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2, 0,
+ (CPCAP_BIT_ATOX_PS_FACTOR |
+ CPCAP_BIT_ADC_PS_FACTOR1 |
+ CPCAP_BIT_ADC_PS_FACTOR0));
+
+ cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS,
+ CPCAP_BIT_ADTRIG_DIS);
+
+ cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ASC,
+ CPCAP_BIT_ASC);
+
+ do {
+ schedule_timeout_uninterruptible(1);
+ cpcap_regacc_read(cpcap, CPCAP_REG_ADCC2, &value);
+ } while ((value & CPCAP_BIT_ASC) && time_before(jiffies, timeout));
+
+ if (value & CPCAP_BIT_ASC)
+ dev_err(&(cpcap->spi->dev),
+ "Timeout waiting for calibration to complete\n");
+
+ cpcap_irq_clear(cpcap, CPCAP_IRQ_ADCDONE);
+
+ cpcap_regacc_write(cpcap, CPCAP_REG_ADCC1, 0, CPCAP_BIT_CAL_MODE);
+}
+
+static void trigger_next_adc_job_if_any(struct cpcap_device *cpcap)
+{
+ struct cpcap_adc *adc = cpcap->adcdata;
+ int head;
+
+ mutex_lock(&adc->queue_mutex);
+
+ head = adc->queue_head;
+
+ if (!adc->queue[head]) {
+ mutex_unlock(&adc->queue_mutex);
+ return;
+ }
+ mutex_unlock(&adc->queue_mutex);
+
+ adc_setup(cpcap, adc->queue[head]);
+}
+
+static int
+adc_enqueue_request(struct cpcap_device *cpcap, struct cpcap_adc_request *req)
+{
+ struct cpcap_adc *adc = cpcap->adcdata;
+ int head;
+ int tail;
+ int running;
+
+ mutex_lock(&adc->queue_mutex);
+
+ head = adc->queue_head;
+ tail = adc->queue_tail;
+ running = (head != tail);
+
+ if (adc->queue[tail]) {
+ mutex_unlock(&adc->queue_mutex);
+ return -EBUSY;
+ }
+
+ adc->queue[tail] = req;
+ adc->queue_tail = (tail + 1) & (MAX_ADC_FIFO_DEPTH - 1);
+
+ mutex_unlock(&adc->queue_mutex);
+
+ if (!running)
+ trigger_next_adc_job_if_any(cpcap);
+
+ return 0;
+}
+
+static void
+cpcap_adc_sync_read_callback(struct cpcap_device *cpcap, void *param)
+{
+ struct cpcap_adc_request *req = param;
+
+ complete(&req->completion);
+}
+
+int cpcap_adc_sync_read(struct cpcap_device *cpcap,
+ struct cpcap_adc_request *request)
+{
+ int ret;
+
+ request->callback = cpcap_adc_sync_read_callback;
+ request->callback_param = request;
+ init_completion(&request->completion);
+ ret = adc_enqueue_request(cpcap, request);
+ if (ret)
+ return ret;
+ wait_for_completion(&request->completion);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cpcap_adc_sync_read);
+
+int cpcap_adc_async_read(struct cpcap_device *cpcap,
+ struct cpcap_adc_request *request)
+{
+ return adc_enqueue_request(cpcap, request);
+}
+EXPORT_SYMBOL_GPL(cpcap_adc_async_read);
+
+void cpcap_adc_phase(struct cpcap_device *cpcap, struct cpcap_adc_phase *phase)
+{
+ bank0_phasing[CPCAP_ADC_BATTI_ADC].offset = phase->offset_batti;
+ bank0_phasing[CPCAP_ADC_BATTI_ADC].multiplier = phase->slope_batti;
+
+ bank0_phasing[CPCAP_ADC_CHG_ISENSE].offset = phase->offset_chrgi;
+ bank0_phasing[CPCAP_ADC_CHG_ISENSE].multiplier = phase->slope_chrgi;
+
+ bank0_phasing[CPCAP_ADC_BATTP].offset = phase->offset_battp;
+ bank0_phasing[CPCAP_ADC_BATTP].multiplier = phase->slope_battp;
+
+ bank0_phasing[CPCAP_ADC_BPLUS_AD4].offset = phase->offset_bp;
+ bank0_phasing[CPCAP_ADC_BPLUS_AD4].multiplier = phase->slope_bp;
+
+ bank0_phasing[CPCAP_ADC_AD0_BATTDETB].offset = phase->offset_battt;
+ bank0_phasing[CPCAP_ADC_AD0_BATTDETB].multiplier = phase->slope_battt;
+
+ bank0_phasing[CPCAP_ADC_VBUS].offset = phase->offset_chrgv;
+ bank0_phasing[CPCAP_ADC_VBUS].multiplier = phase->slope_chrgv;
+}
+EXPORT_SYMBOL_GPL(cpcap_adc_phase);
+
+static void adc_phase(struct cpcap_adc_request *req, int index)
+{
+ struct conversion_tbl *conv_tbl = bank0_conversion;
+ struct phasing_tbl *phase_tbl = bank0_phasing;
+ int tbl_index = index;
+
+ if (req->type == CPCAP_ADC_TYPE_BANK_1) {
+ conv_tbl = bank1_conversion;
+ phase_tbl = bank1_phasing;
+ }
+
+ if (req->type == CPCAP_ADC_TYPE_BATT_PI)
+ tbl_index = (tbl_index % 2) ? CPCAP_ADC_BATTI_ADC :
+ CPCAP_ADC_BATTP;
+
+ if (((req->type == CPCAP_ADC_TYPE_BANK_0) ||
+ (req->type == CPCAP_ADC_TYPE_BATT_PI)) &&
+ (tbl_index == CPCAP_ADC_BATTP)) {
+ req->result[index] -= phase_tbl[tbl_index].offset;
+ req->result[index] -= FOUR_POINT_TWO_ADC;
+ req->result[index] *= phase_tbl[tbl_index].multiplier;
+ req->result[index] /= phase_tbl[tbl_index].divider;
+ req->result[index] += FOUR_POINT_TWO_ADC;
+ } else {
+ req->result[index] += conv_tbl[tbl_index].cal_offset;
+ req->result[index] += conv_tbl[tbl_index].align_offset;
+ req->result[index] *= phase_tbl[tbl_index].multiplier;
+ req->result[index] /= phase_tbl[tbl_index].divider;
+ req->result[index] += phase_tbl[tbl_index].offset;
+ }
+
+ if (req->result[index] < phase_tbl[tbl_index].min)
+ req->result[index] = phase_tbl[tbl_index].min;
+ else if (req->result[index] > phase_tbl[tbl_index].max)
+ req->result[index] = phase_tbl[tbl_index].max;
+}
+
+static void adc_convert(struct cpcap_adc_request *req, int index)
+{
+ struct conversion_tbl *conv_tbl = bank0_conversion;
+ int tbl_index = index;
+
+ if (req->type == CPCAP_ADC_TYPE_BANK_1)
+ conv_tbl = bank1_conversion;
+
+ if (req->type == CPCAP_ADC_TYPE_BATT_PI)
+ tbl_index = (tbl_index % 2) ? CPCAP_ADC_BATTI_ADC :
+ CPCAP_ADC_BATTP;
+
+ if (conv_tbl[tbl_index].conv_type == CONV_TYPE_DIRECT) {
+ req->result[index] *= conv_tbl[tbl_index].multiplier;
+ req->result[index] /= conv_tbl[tbl_index].divider;
+ req->result[index] += conv_tbl[tbl_index].conv_offset;
+ } else if (conv_tbl[tbl_index].conv_type == CONV_TYPE_MAPPING)
+ req->result[index] = convert_to_kelvins(req->result[tbl_index]);
+}
+
+static void adc_raw(struct cpcap_adc_request *req, int index)
+{
+ struct conversion_tbl *conv_tbl = bank0_conversion;
+ struct phasing_tbl *phase_tbl = bank0_phasing;
+ int tbl_index = index;
+
+ if (req->type == CPCAP_ADC_TYPE_BANK_1)
+ return;
+
+ if (req->type == CPCAP_ADC_TYPE_BATT_PI)
+ tbl_index = (tbl_index % 2) ? CPCAP_ADC_BATTI_ADC :
+ CPCAP_ADC_BATTP;
+
+ req->result[index] += conv_tbl[tbl_index].cal_offset;
+
+ if (req->result[index] <
+ (phase_tbl[tbl_index].min - conv_tbl[tbl_index].align_offset)) {
+ req->result[index] = (phase_tbl[tbl_index].min -
+ conv_tbl[tbl_index].align_offset);
+ } else if (req->result[index] >
+ (phase_tbl[tbl_index].max -
+ conv_tbl[tbl_index].align_offset)) {
+ req->result[index] = (phase_tbl[tbl_index].max -
+ conv_tbl[tbl_index].align_offset);
+ }
+}
+
+static void adc_result(struct cpcap_device *cpcap,
+ struct cpcap_adc_request *req)
+{
+ int i;
+ int j;
+ unsigned short cal_data;
+
+ cal_data = 0;
+ cpcap_regacc_read(cpcap, CPCAP_REG_ADCAL1, &cal_data);
+ bank0_conversion[CPCAP_ADC_CHG_ISENSE].cal_offset =
+ ((short)cal_data * -1) + 512;
+
+ cal_data = 0;
+ cpcap_regacc_read(cpcap, CPCAP_REG_ADCAL2, &cal_data);
+ bank0_conversion[CPCAP_ADC_BATTI_ADC].cal_offset =
+ ((short)cal_data * -1) + 512;
+
+
+ for (i = CPCAP_REG_ADCD0; i <= CPCAP_REG_ADCD7; i++) {
+ j = i - CPCAP_REG_ADCD0;
+ cpcap_regacc_read(cpcap, i, (unsigned short *)&req->result[j]);
+ req->result[j] &= 0x3FF;
+
+ switch (req->format) {
+ case CPCAP_ADC_FORMAT_PHASED:
+ adc_phase(req, j);
+ break;
+
+ case CPCAP_ADC_FORMAT_CONVERTED:
+ adc_phase(req, j);
+ adc_convert(req, j);
+ break;
+
+ case CPCAP_ADC_FORMAT_RAW:
+ adc_raw(req, j);
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+static void cpcap_adc_irq(enum cpcap_irqs irq, void *data)
+{
+ struct cpcap_adc *adc = data;
+ struct cpcap_device *cpcap = adc->cpcap;
+ struct cpcap_adc_request *req;
+ int head;
+
+ cancel_delayed_work_sync(&adc->work);
+
+ cpcap_regacc_write(cpcap, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS,
+ CPCAP_BIT_ADTRIG_DIS);
+
+ mutex_lock(&adc->queue_mutex);
+ head = adc->queue_head;
+
+ req = adc->queue[head];
+ if (!req) {
+ dev_info(&(cpcap->spi->dev),
+ "cpcap_adc_irq: ADC queue empty!\n");
+ mutex_unlock(&adc->queue_mutex);
+ return;
+ }
+ adc->queue[head] = NULL;
+ adc->queue_head = (head + 1) & (MAX_ADC_FIFO_DEPTH - 1);
+
+ mutex_unlock(&adc->queue_mutex);
+
+ adc_result(cpcap, req);
+
+ trigger_next_adc_job_if_any(cpcap);
+
+ req->status = 0;
+
+ req->callback(cpcap, req->callback_param);
+}
+
+static void cpcap_adc_cancel(struct work_struct *work)
+{
+ int head;
+ struct cpcap_adc_request *req;
+ struct cpcap_adc *adc =
+ container_of(work, struct cpcap_adc, work.work);
+
+ cpcap_irq_mask(adc->cpcap, CPCAP_IRQ_ADCDONE);
+
+ cpcap_regacc_write(adc->cpcap, CPCAP_REG_ADCC2,
+ CPCAP_BIT_ADTRIG_DIS,
+ CPCAP_BIT_ADTRIG_DIS);
+
+ mutex_lock(&adc->queue_mutex);
+ head = adc->queue_head;
+
+ req = adc->queue[head];
+ if (!req) {
+ dev_info(&(adc->cpcap->spi->dev),
+ "cpcap_adc_cancel: ADC queue empty!\n");
+ mutex_unlock(&adc->queue_mutex);
+ return;
+ }
+ adc->queue[head] = NULL;
+ adc->queue_head = (head + 1) & (MAX_ADC_FIFO_DEPTH - 1);
+
+ mutex_unlock(&adc->queue_mutex);
+
+ req->status = -ETIMEDOUT;
+
+ req->callback(adc->cpcap, req->callback_param);
+
+ trigger_next_adc_job_if_any(adc->cpcap);
+}
+
+static int cpcap_adc_probe(struct platform_device *pdev)
+{
+ struct cpcap_adc *adc;
+ unsigned short cal_data;
+ struct cpcap_platform_data *data;
+
+ if (pdev->dev.platform_data == NULL) {
+ dev_err(&pdev->dev, "no platform_data\n");
+ return -EINVAL;
+ }
+
+ adc = kzalloc(sizeof(*adc), GFP_KERNEL);
+ if (!adc)
+ return -ENOMEM;
+
+ adc->cpcap = pdev->dev.platform_data;
+
+ platform_set_drvdata(pdev, adc);
+ adc->cpcap->adcdata = adc;
+
+ data = adc->cpcap->spi->controller_data;
+
+ /* Scale ICHRG sense ADCs for non-standard sense resistor */
+ bank0_conversion[CPCAP_ADC_CHG_ISENSE].multiplier =
+ (DEFAULT_ICHARGE_SENSE_RES * DEFAULT_ISENSE_RANGE) /
+ data->adc_ato->ichrg_sense_res;
+
+ mutex_init(&adc->queue_mutex);
+
+ adc_setup_calibrate(adc->cpcap, CPCAP_ADC_CHG_ISENSE);
+ adc_setup_calibrate(adc->cpcap, CPCAP_ADC_BATTI_ADC);
+
+ cal_data = 0;
+ cpcap_regacc_read(adc->cpcap, CPCAP_REG_ADCAL1, &cal_data);
+ bank0_conversion[CPCAP_ADC_CHG_ISENSE].cal_offset =
+ ((short)cal_data * -1) + 512;
+
+ cal_data = 0;
+ cpcap_regacc_read(adc->cpcap, CPCAP_REG_ADCAL2, &cal_data);
+ bank0_conversion[CPCAP_ADC_BATTI_ADC].cal_offset =
+ ((short)cal_data * -1) + 512;
+
+ INIT_DELAYED_WORK(&adc->work, cpcap_adc_cancel);
+
+ cpcap_irq_register(adc->cpcap, CPCAP_IRQ_ADCDONE,
+ cpcap_adc_irq, adc);
+
+ dev_info(&pdev->dev, "CPCAP ADC device probed\n");
+
+ return 0;
+}
+
+static int cpcap_adc_remove(struct platform_device *pdev)
+{
+ struct cpcap_adc *adc = platform_get_drvdata(pdev);
+ int head;
+
+ cancel_delayed_work_sync(&adc->work);
+
+ cpcap_irq_free(adc->cpcap, CPCAP_IRQ_ADCDONE);
+
+ mutex_lock(&adc->queue_mutex);
+ head = adc->queue_head;
+
+ if (WARN_ON(adc->queue[head]))
+ dev_err(&pdev->dev,
+ "adc driver removed with request pending\n");
+
+ mutex_unlock(&adc->queue_mutex);
+ kfree(adc);
+
+ return 0;
+}
+
+static struct platform_driver cpcap_adc_driver = {
+ .driver = {
+ .name = "cpcap_adc",
+ },
+ .probe = cpcap_adc_probe,
+ .remove = cpcap_adc_remove,
+};
+
+static int __init cpcap_adc_init(void)
+{
+ return platform_driver_register(&cpcap_adc_driver);
+}
+subsys_initcall(cpcap_adc_init);
+
+static void __exit cpcap_adc_exit(void)
+{
+ platform_driver_unregister(&cpcap_adc_driver);
+}
+module_exit(cpcap_adc_exit);
+
+MODULE_ALIAS("platform:cpcap_adc");
+MODULE_DESCRIPTION("CPCAP ADC driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cpcap-core.c b/drivers/mfd/cpcap-core.c
new file mode 100644
index 00000000000..b248c9d0d4a
--- /dev/null
+++ b/drivers/mfd/cpcap-core.c
@@ -0,0 +1,577 @@
+/*
+ * Copyright (C) 2007-2010 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/machine.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+#include <linux/uaccess.h>
+#include <linux/reboot.h>
+#include <linux/notifier.h>
+#include <linux/delay.h>
+
+struct cpcap_driver_info {
+ struct list_head list;
+ struct platform_device *pdev;
+};
+
+static long ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+static int cpcap_probe(struct spi_device *spi);
+static int cpcap_remove(struct spi_device *spi);
+
+const static struct file_operations cpcap_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = ioctl,
+};
+
+static struct miscdevice cpcap_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = CPCAP_DEV_NAME,
+ .fops = &cpcap_fops,
+};
+
+static struct spi_driver cpcap_driver = {
+ .driver = {
+ .name = "cpcap",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = cpcap_probe,
+ .remove = cpcap_remove,
+};
+
+static struct platform_device cpcap_adc_device = {
+ .name = "cpcap_adc",
+ .id = -1,
+ .dev.platform_data = NULL,
+};
+
+
+static struct platform_device cpcap_key_device = {
+ .name = "cpcap_key",
+ .id = -1,
+ .dev.platform_data = NULL,
+};
+
+static struct platform_device cpcap_batt_device = {
+ .name = "cpcap_battery",
+ .id = -1,
+ .dev.platform_data = NULL,
+};
+
+static struct platform_device cpcap_uc_device = {
+ .name = "cpcap_uc",
+ .id = -1,
+ .dev.platform_data = NULL,
+};
+
+static struct platform_device cpcap_rtc_device = {
+ .name = "cpcap_rtc",
+ .id = -1,
+ .dev.platform_data = NULL,
+};
+
+/* List of required CPCAP devices that will ALWAYS be present.
+ *
+ * DO NOT ADD NEW DEVICES TO THIS LIST! You must use cpcap_driver_register()
+ * for any new drivers for non-core functionality of CPCAP.
+ */
+static struct platform_device *cpcap_devices[] = {
+ &cpcap_uc_device,
+ &cpcap_adc_device,
+ &cpcap_key_device,
+ &cpcap_batt_device,
+ &cpcap_rtc_device,
+};
+
+static struct cpcap_device *misc_cpcap;
+
+static LIST_HEAD(cpcap_device_list);
+static DEFINE_MUTEX(cpcap_driver_lock);
+
+static int cpcap_reboot(struct notifier_block *this, unsigned long code,
+ void *cmd)
+{
+ int ret = -1;
+ int result = NOTIFY_DONE;
+ char *mode = cmd;
+ unsigned short value;
+ unsigned short counter = 0;
+
+ /* Disable the USB transceiver */
+ ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_USBC2, 0,
+ CPCAP_BIT_USBXCVREN);
+
+ if (ret) {
+ dev_err(&(misc_cpcap->spi->dev),
+ "Disable Transciever failure.\n");
+ result = NOTIFY_BAD;
+ }
+
+ if (code == SYS_RESTART) {
+ if (mode != NULL && !strncmp("outofcharge", mode, 12)) {
+ /* Set the outofcharge bit in the cpcap */
+ ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_VAL1,
+ CPCAP_BIT_OUT_CHARGE_ONLY,
+ CPCAP_BIT_OUT_CHARGE_ONLY);
+ if (ret) {
+ dev_err(&(misc_cpcap->spi->dev),
+ "outofcharge cpcap set failure.\n");
+ result = NOTIFY_BAD;
+ }
+ /* Set the soft reset bit in the cpcap */
+ cpcap_regacc_write(misc_cpcap, CPCAP_REG_VAL1,
+ CPCAP_BIT_SOFT_RESET,
+ CPCAP_BIT_SOFT_RESET);
+ if (ret) {
+ dev_err(&(misc_cpcap->spi->dev),
+ "reset cpcap set failure.\n");
+ result = NOTIFY_BAD;
+ }
+ }
+
+ /* Check if we are starting recovery mode */
+ if (mode != NULL && !strncmp("recovery", mode, 9)) {
+ /* Set the fota (recovery mode) bit in the cpcap */
+ ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_VAL1,
+ CPCAP_BIT_FOTA_MODE, CPCAP_BIT_FOTA_MODE);
+ if (ret) {
+ dev_err(&(misc_cpcap->spi->dev),
+ "Recovery cpcap set failure.\n");
+ result = NOTIFY_BAD;
+ }
+ } else {
+ /* Set the fota (recovery mode) bit in the cpcap */
+ ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_VAL1, 0,
+ CPCAP_BIT_FOTA_MODE);
+ if (ret) {
+ dev_err(&(misc_cpcap->spi->dev),
+ "Recovery cpcap clear failure.\n");
+ result = NOTIFY_BAD;
+ }
+ }
+ /* Check if we are going into fast boot mode */
+ if (mode != NULL && !strncmp("bootloader", mode, 11)) {
+ /* Set the bootmode bit in the cpcap */
+ ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_VAL1,
+ CPCAP_BIT_BOOT_MODE, CPCAP_BIT_BOOT_MODE);
+ if (ret) {
+ dev_err(&(misc_cpcap->spi->dev),
+ "Boot mode cpcap set failure.\n");
+ result = NOTIFY_BAD;
+ }
+ }
+ cpcap_regacc_write(misc_cpcap, CPCAP_REG_MI2, 0, 0xFFFF);
+ } else {
+ ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_VAL1,
+ 0,
+ CPCAP_BIT_OUT_CHARGE_ONLY);
+ if (ret) {
+ dev_err(&(misc_cpcap->spi->dev),
+ "outofcharge cpcap set failure.\n");
+ result = NOTIFY_BAD;
+ }
+
+ /* Clear the soft reset bit in the cpcap */
+ ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_VAL1, 0,
+ CPCAP_BIT_SOFT_RESET);
+ if (ret) {
+ dev_err(&(misc_cpcap->spi->dev),
+ "SW Reset cpcap set failure.\n");
+ result = NOTIFY_BAD;
+ }
+ /* Clear the fota (recovery mode) bit in the cpcap */
+ ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_VAL1, 0,
+ CPCAP_BIT_FOTA_MODE);
+ if (ret) {
+ dev_err(&(misc_cpcap->spi->dev),
+ "Recovery cpcap clear failure.\n");
+ result = NOTIFY_BAD;
+ }
+ }
+
+ /* Always clear the kpanic bit */
+ ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_VAL1,
+ 0, CPCAP_BIT_AP_KERNEL_PANIC);
+ if (ret) {
+ dev_err(&(misc_cpcap->spi->dev),
+ "Clear kernel panic bit failure.\n");
+ result = NOTIFY_BAD;
+ }
+
+ /* Always clear the power cut bit on SW Shutdown*/
+ ret = cpcap_regacc_write(misc_cpcap, CPCAP_REG_PC1,
+ 0, CPCAP_BIT_PC1_PCEN);
+ if (ret) {
+ dev_err(&(misc_cpcap->spi->dev),
+ "Clear Power Cut bit failure.\n");
+ result = NOTIFY_BAD;
+ }
+
+ cpcap_regacc_write(misc_cpcap, CPCAP_REG_CRM, 0x0300, 0x3FFF);
+
+ (void)cpcap_regacc_read(misc_cpcap, CPCAP_REG_INTS2, &value);
+ if (!(value & CPCAP_BIT_VBUSVLD_S)) {
+ while ((value & CPCAP_BIT_SESSVLD_S) && (counter < 100)) {
+ mdelay(10);
+ counter++;
+ (void)cpcap_regacc_read(misc_cpcap, CPCAP_REG_INTS2,
+ &value);
+ }
+ }
+
+ /* Clear the charger and charge path settings to avoid a false turn on
+ * event in caused by CPCAP. After clearing these settings, 100ms is
+ * needed to before SYSRSTRTB is pulled low to avoid the false turn on
+ * event.
+ */
+ cpcap_regacc_write(misc_cpcap, CPCAP_REG_CRM, 0, 0x3FFF);
+ mdelay(100);
+
+ return result;
+}
+
+static struct notifier_block cpcap_reboot_notifier = {
+ .notifier_call = cpcap_reboot,
+};
+
+static int __init cpcap_init(void)
+{
+ return spi_register_driver(&cpcap_driver);
+}
+
+static void cpcap_vendor_read(struct cpcap_device *cpcap)
+{
+ unsigned short value;
+
+ (void)cpcap_regacc_read(cpcap, CPCAP_REG_VERSC1, &value);
+
+ cpcap->vendor = (enum cpcap_vendor)((value >> 6) & 0x0007);
+ cpcap->revision = (enum cpcap_revision)(((value >> 3) & 0x0007) |
+ ((value << 3) & 0x0038));
+}
+
+
+int cpcap_device_unregister(struct platform_device *pdev)
+{
+ struct cpcap_driver_info *info;
+ struct cpcap_driver_info *tmp;
+ int found;
+
+
+ found = 0;
+ mutex_lock(&cpcap_driver_lock);
+
+ list_for_each_entry_safe(info, tmp, &cpcap_device_list, list) {
+ if (info->pdev == pdev) {
+ list_del(&info->list);
+
+ /*
+ * misc_cpcap != NULL suggests pdev
+ * already registered
+ */
+ if (misc_cpcap) {
+ printk(KERN_INFO "CPCAP: unregister %s\n",
+ pdev->name);
+ platform_device_unregister(pdev);
+ }
+ info->pdev = NULL;
+ kfree(info);
+ found = 1;
+ }
+ }
+
+ mutex_unlock(&cpcap_driver_lock);
+
+ BUG_ON(!found);
+ return 0;
+}
+
+int cpcap_device_register(struct platform_device *pdev)
+{
+ int retval;
+ struct cpcap_driver_info *info;
+
+ retval = 0;
+
+ info = kzalloc(sizeof(*info), GFP_KERNEL);
+ if (!info) {
+ printk(KERN_ERR "Cannot save device %s\n", pdev->name);
+ return -ENOMEM;
+ }
+
+ mutex_lock(&cpcap_driver_lock);
+
+ info->pdev = pdev;
+ list_add_tail(&info->list, &cpcap_device_list);
+
+ /* If misc_cpcap is valid, the CPCAP driver has already been probed.
+ * Therefore, call platform_device_register() to probe the device.
+ */
+ if (misc_cpcap) {
+ dev_info(&(misc_cpcap->spi->dev),
+ "Probing CPCAP device %s\n", pdev->name);
+
+ /*
+ * platform_data is non-empty indicates
+ * CPCAP client devices need to pass their own data
+ * In that case we put cpcap data in driver_data
+ */
+ if (pdev->dev.platform_data != NULL)
+ platform_set_drvdata(pdev, misc_cpcap);
+ else
+ pdev->dev.platform_data = misc_cpcap;
+ retval = platform_device_register(pdev);
+ } else
+ printk(KERN_INFO "CPCAP: delaying %s probe\n",
+ pdev->name);
+ mutex_unlock(&cpcap_driver_lock);
+
+ return retval;
+}
+
+static int cpcap_probe(struct spi_device *spi)
+{
+ int retval = -EINVAL;
+ struct cpcap_device *cpcap;
+ struct cpcap_platform_data *data;
+ int i;
+ struct cpcap_driver_info *info;
+
+ cpcap = kzalloc(sizeof(*cpcap), GFP_KERNEL);
+ if (cpcap == NULL)
+ return -ENOMEM;
+
+ cpcap->spi = spi;
+ data = spi->controller_data;
+ spi_set_drvdata(spi, cpcap);
+
+ retval = cpcap_regacc_init(cpcap);
+ if (retval < 0)
+ goto free_mem;
+ retval = cpcap_irq_init(cpcap);
+ if (retval < 0)
+ goto free_cpcap_irq;
+
+ cpcap_vendor_read(cpcap);
+
+ for (i = 0; i < ARRAY_SIZE(cpcap_devices); i++)
+ cpcap_devices[i]->dev.platform_data = cpcap;
+
+ retval = misc_register(&cpcap_dev);
+ if (retval < 0)
+ goto free_cpcap_irq;
+
+ /* loop twice becuase cpcap_regulator_probe may refer to other devices
+ * in this list to handle dependencies between regulators. Create them
+ * all and then add them */
+ for (i = 0; i < CPCAP_NUM_REGULATORS; i++) {
+ struct platform_device *pdev;
+
+ pdev = platform_device_alloc("cpcap-regltr", i);
+ if (!pdev) {
+ dev_err(&(spi->dev), "Cannot create regulator\n");
+ continue;
+ }
+
+ pdev->dev.parent = &(spi->dev);
+ pdev->dev.platform_data = &data->regulator_init[i];
+ platform_set_drvdata(pdev, cpcap);
+ cpcap->regulator_pdev[i] = pdev;
+ }
+
+ for (i = 0; i < CPCAP_NUM_REGULATORS; i++) {
+ /* vusb has to be added after sw5 so skip it for now,
+ * it will be added from probe of sw5 */
+ if (i == CPCAP_VUSB)
+ continue;
+ platform_device_add(cpcap->regulator_pdev[i]);
+ }
+
+ platform_add_devices(cpcap_devices, ARRAY_SIZE(cpcap_devices));
+
+ mutex_lock(&cpcap_driver_lock);
+ misc_cpcap = cpcap; /* kept for misc device */
+
+ list_for_each_entry(info, &cpcap_device_list, list) {
+ int ret = 0;
+ dev_info(&(spi->dev), "Probing CPCAP device %s\n",
+ info->pdev->name);
+ if (info->pdev->dev.platform_data != NULL)
+ platform_set_drvdata(info->pdev, cpcap);
+ else
+ info->pdev->dev.platform_data = cpcap;
+ ret = platform_device_register(info->pdev);
+ }
+ mutex_unlock(&cpcap_driver_lock);
+
+ register_reboot_notifier(&cpcap_reboot_notifier);
+
+ return 0;
+
+free_cpcap_irq:
+ cpcap_irq_shutdown(cpcap);
+free_mem:
+ kfree(cpcap);
+ return retval;
+}
+
+static int cpcap_remove(struct spi_device *spi)
+{
+ struct cpcap_device *cpcap = spi_get_drvdata(spi);
+ struct cpcap_driver_info *info;
+ int i;
+
+ unregister_reboot_notifier(&cpcap_reboot_notifier);
+
+ mutex_lock(&cpcap_driver_lock);
+ list_for_each_entry(info, &cpcap_device_list, list) {
+ dev_info(&(spi->dev), "Removing CPCAP device %s\n",
+ info->pdev->name);
+ platform_device_unregister(info->pdev);
+ }
+ misc_cpcap = NULL;
+ mutex_unlock(&cpcap_driver_lock);
+
+ for (i = ARRAY_SIZE(cpcap_devices); i > 0; i--)
+ platform_device_unregister(cpcap_devices[i-1]);
+
+ for (i = 0; i < CPCAP_NUM_REGULATORS; i++)
+ platform_device_unregister(cpcap->regulator_pdev[i]);
+
+ misc_deregister(&cpcap_dev);
+ cpcap_irq_shutdown(cpcap);
+ kfree(cpcap);
+ return 0;
+}
+
+
+static int test_ioctl(unsigned int cmd, unsigned long arg)
+{
+ int retval = -EINVAL;
+ struct cpcap_regacc read_data;
+ struct cpcap_regacc write_data;
+
+ switch (cmd) {
+ case CPCAP_IOCTL_TEST_READ_REG:
+ if (copy_from_user((void *)&read_data, (void *)arg,
+ sizeof(read_data)))
+ return -EFAULT;
+ retval = cpcap_regacc_read(misc_cpcap, read_data.reg,
+ &read_data.value);
+ if (retval < 0)
+ return retval;
+ if (copy_to_user((void *)arg, (void *)&read_data,
+ sizeof(read_data)))
+ return -EFAULT;
+ return 0;
+ break;
+
+ case CPCAP_IOCTL_TEST_WRITE_REG:
+ if (copy_from_user((void *) &write_data,
+ (void *) arg,
+ sizeof(write_data)))
+ return -EFAULT;
+ retval = cpcap_regacc_write(misc_cpcap, write_data.reg,
+ write_data.value, write_data.mask);
+ break;
+
+ default:
+ retval = -ENOTTY;
+ break;
+ }
+
+ return retval;
+}
+
+static int adc_ioctl(unsigned int cmd, unsigned long arg)
+{
+ int retval = -EINVAL;
+ struct cpcap_adc_phase phase;
+
+ switch (cmd) {
+ case CPCAP_IOCTL_ADC_PHASE:
+ if (copy_from_user((void *) &phase, (void *) arg,
+ sizeof(phase)))
+ return -EFAULT;
+
+ cpcap_adc_phase(misc_cpcap, &phase);
+ retval = 0;
+ break;
+
+ default:
+ retval = -ENOTTY;
+ break;
+ }
+
+ return retval;
+}
+
+#if defined(CONFIG_LEDS_FLASH_RESET)
+int cpcap_direct_misc_write(unsigned short reg, unsigned short value,\
+ unsigned short mask)
+{
+ int retval = -EINVAL;
+
+ retval = cpcap_regacc_write(misc_cpcap, reg, value, mask);
+
+ return retval;
+}
+#endif
+
+static long ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int retval = -ENOTTY;
+ unsigned int cmd_num;
+
+ cmd_num = _IOC_NR(cmd);
+
+ if ((cmd_num > CPCAP_IOCTL_NUM_TEST__START) &&
+ (cmd_num < CPCAP_IOCTL_NUM_TEST__END)) {
+ retval = test_ioctl(cmd, arg);
+ }
+ if ((cmd_num > CPCAP_IOCTL_NUM_ADC__START) &&
+ (cmd_num < CPCAP_IOCTL_NUM_ADC__END)) {
+ retval = adc_ioctl(cmd, arg);
+ }
+
+ return retval;
+}
+
+static void cpcap_shutdown(void)
+{
+ spi_unregister_driver(&cpcap_driver);
+}
+
+int cpcap_disable_offmode_wakeups(bool disable)
+{
+ int retval = 0;
+ return retval;
+}
+
+subsys_initcall(cpcap_init);
+module_exit(cpcap_shutdown);
+
+MODULE_ALIAS("platform:cpcap");
+MODULE_DESCRIPTION("CPCAP driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cpcap-irq.c b/drivers/mfd/cpcap-irq.c
new file mode 100644
index 00000000000..8d253ea2c56
--- /dev/null
+++ b/drivers/mfd/cpcap-irq.c
@@ -0,0 +1,809 @@
+/*
+ * Copyright (C) 2009 - 2010, Motorola, All Rights Reserved.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/wakelock.h>
+
+#include <linux/spi/cpcap.h>
+#include <linux/spi/spi.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#ifdef CONFIG_PM_DEEPSLEEP
+#include <linux/suspend.h>
+#endif
+
+#define LONG_KEYPRESS_DURATION 4 /* in seconds */
+
+#define NUM_INT_REGS 5
+#define NUM_INTS_PER_REG 16
+
+#define CPCAP_INT1_VALID_BITS 0xFFFB
+#define CPCAP_INT2_VALID_BITS 0xFFFF
+#define CPCAP_INT3_VALID_BITS 0xFFFF
+#define CPCAP_INT4_VALID_BITS 0x03FF
+#define CPCAP_INT5_VALID_BITS 0xFFFF
+
+struct cpcap_event_handler {
+ void (*func)(enum cpcap_irqs, void *);
+ void *data;
+};
+
+struct cpcap_irq_info {
+ uint8_t registered;
+ uint8_t enabled;
+ uint32_t count;
+};
+
+struct cpcap_irqdata {
+ struct mutex lock;
+ struct work_struct work;
+ struct workqueue_struct *workqueue;
+ struct cpcap_device *cpcap;
+ struct cpcap_event_handler event_handler[CPCAP_IRQ__NUM];
+ struct cpcap_irq_info irq_info[CPCAP_IRQ__NUM];
+ struct wake_lock wake_lock;
+};
+
+#define EVENT_MASK(event) (1 << ((event) % NUM_INTS_PER_REG))
+
+enum pwrkey_states {
+ PWRKEY_RELEASE, /* Power key released state. */
+ PWRKEY_PRESS, /* Power key pressed state. */
+ PWRKEY_UNKNOWN, /* Unknown power key state. */
+};
+
+static irqreturn_t event_isr(int irq, void *data)
+{
+ struct cpcap_irqdata *irq_data = data;
+ disable_irq_nosync(irq);
+ wake_lock(&irq_data->wake_lock);
+ queue_work(irq_data->workqueue, &irq_data->work);
+
+ return IRQ_HANDLED;
+}
+
+static unsigned short get_int_reg(enum cpcap_irqs event)
+{
+ unsigned short ret;
+
+ if ((event) >= CPCAP_IRQ_INT5_INDEX)
+ ret = CPCAP_REG_MI1;
+ else if ((event) >= CPCAP_IRQ_INT4_INDEX)
+ ret = CPCAP_REG_INT4;
+ else if ((event) >= CPCAP_IRQ_INT3_INDEX)
+ ret = CPCAP_REG_INT3;
+ else if ((event) >= CPCAP_IRQ_INT2_INDEX)
+ ret = CPCAP_REG_INT2;
+ else
+ ret = CPCAP_REG_INT1;
+
+ return ret;
+}
+
+static unsigned short get_mask_reg(enum cpcap_irqs event)
+{
+ unsigned short ret;
+
+ if (event >= CPCAP_IRQ_INT5_INDEX)
+ ret = CPCAP_REG_MIM1;
+ else if (event >= CPCAP_IRQ_INT4_INDEX)
+ ret = CPCAP_REG_INTM4;
+ else if (event >= CPCAP_IRQ_INT3_INDEX)
+ ret = CPCAP_REG_INTM3;
+ else if (event >= CPCAP_IRQ_INT2_INDEX)
+ ret = CPCAP_REG_INTM2;
+ else
+ ret = CPCAP_REG_INTM1;
+
+ return ret;
+}
+
+static unsigned short get_sense_reg(enum cpcap_irqs event)
+{
+ unsigned short ret;
+
+ if (event >= CPCAP_IRQ_INT5_INDEX)
+ ret = CPCAP_REG_MI2;
+ else if (event >= CPCAP_IRQ_INT4_INDEX)
+ ret = CPCAP_REG_INTS4;
+ else if (event >= CPCAP_IRQ_INT3_INDEX)
+ ret = CPCAP_REG_INTS3;
+ else if (event >= CPCAP_IRQ_INT2_INDEX)
+ ret = CPCAP_REG_INTS2;
+ else
+ ret = CPCAP_REG_INTS1;
+
+ return ret;
+}
+
+void cpcap_irq_mask_all(struct cpcap_device *cpcap)
+{
+ int i;
+
+ static const struct {
+ unsigned short mask_reg;
+ unsigned short valid;
+ } int_reg[NUM_INT_REGS] = {
+ {CPCAP_REG_INTM1, CPCAP_INT1_VALID_BITS},
+ {CPCAP_REG_INTM2, CPCAP_INT2_VALID_BITS},
+ {CPCAP_REG_INTM3, CPCAP_INT3_VALID_BITS},
+ {CPCAP_REG_INTM4, CPCAP_INT4_VALID_BITS},
+ {CPCAP_REG_MIM1, CPCAP_INT5_VALID_BITS}
+ };
+
+ for (i = 0; i < NUM_INT_REGS; i++) {
+ cpcap_regacc_write(cpcap, int_reg[i].mask_reg,
+ int_reg[i].valid,
+ int_reg[i].valid);
+ }
+}
+
+struct pwrkey_data {
+ struct cpcap_device *cpcap;
+ enum pwrkey_states state;
+ struct wake_lock wake_lock;
+ struct delayed_work pwrkey_work;
+ int power_double_pressed;
+#ifdef CONFIG_PM_DEEPSLEEP
+ struct hrtimer longPress_timer;
+ int expired;
+#endif
+
+};
+
+#ifdef CONFIG_PM_DBG_DRV
+static struct cpcap_irq_pm_dbg {
+ unsigned short en_ints[NUM_INT_REGS];
+ unsigned char suspend;
+ unsigned char wakeup;
+} pm_dbg_info;
+#endif /* CONFIG_PM_DBG_DRV */
+
+#ifdef CONFIG_PM_DEEPSLEEP
+
+static enum hrtimer_restart longPress_timer_callback(struct hrtimer *timer)
+{
+ struct pwrkey_data *pwrkey_data =
+ container_of(timer, struct pwrkey_data, longPress_timer);
+ struct cpcap_device *cpcap = pwrkey_data->cpcap;
+ enum pwrkey_states new_state = PWRKEY_PRESS;
+
+ if (wake_lock_active(&pwrkey_data->wake_lock))
+ wake_unlock(&pwrkey_data->wake_lock);
+ wake_lock_timeout(&pwrkey_data->wake_lock, 20);
+
+ /* long timer expired without being cancelled so send long press
+ keydown event */
+ pwrkey_data->expired = 1;
+ cpcap_broadcast_key_event(cpcap, KEY_SENDFILE, new_state);
+ pwrkey_data->state = new_state;
+
+ return HRTIMER_NORESTART;
+
+}
+#endif
+
+static void pwrkey_work_func(struct work_struct *work)
+{
+ struct pwrkey_data *pwrkey_data =
+ container_of(work, struct pwrkey_data, pwrkey_work.work);
+ struct cpcap_device *cpcap = pwrkey_data->cpcap;
+
+ if (wake_lock_active(&pwrkey_data->wake_lock))
+ wake_unlock(&pwrkey_data->wake_lock);
+ wake_lock_timeout(&pwrkey_data->wake_lock, 20);
+ if (pwrkey_data->state == PWRKEY_RELEASE) {
+ /* keyup was detected before keydown was sent, so send
+ keydown first */
+ cpcap_broadcast_key_event(cpcap, KEY_END, PWRKEY_PRESS);
+ }
+
+ /* Send detected state (keyup/keydown) */
+ cpcap_broadcast_key_event(cpcap, KEY_END, pwrkey_data->state);
+}
+
+static void pwrkey_handler(enum cpcap_irqs irq, void *data)
+{
+ struct pwrkey_data *pwrkey_data = data;
+ enum pwrkey_states new_state, last_state = pwrkey_data->state;
+ struct cpcap_device *cpcap = pwrkey_data->cpcap;
+
+ new_state = (enum pwrkey_states) cpcap_irq_sense(cpcap, irq, 0);
+
+ /* First do long keypress detection */
+ if (new_state == PWRKEY_RELEASE) {
+#ifdef CONFIG_PM_DEEPSLEEP
+ /* Got a keyup so cancel 2 second timer */
+ hrtimer_cancel(&pwrkey_data->longPress_timer);
+ /* If longpress keydown was previously sent, then send the
+ long press keyup */
+ if (pwrkey_data->expired == 1) {
+ pwrkey_data->expired = 0;
+#endif
+ cpcap_broadcast_key_event(cpcap,
+ KEY_SENDFILE, new_state);
+ pwrkey_data->state = new_state;
+#ifdef CONFIG_PM_DEEPSLEEP
+ }
+#endif
+ } else if (new_state == PWRKEY_PRESS) {
+#ifdef CONFIG_PM_DEEPSLEEP
+ /* Got a keydown so start long keypress timer */
+ pwrkey_data->expired = 0;
+ hrtimer_start(&pwrkey_data->longPress_timer,
+ ktime_set(LONG_KEYPRESS_DURATION, 0),
+ HRTIMER_MODE_REL);
+#endif
+ wake_lock_timeout(&pwrkey_data->wake_lock,
+ (LONG_KEYPRESS_DURATION*HZ)+5);
+ }
+
+ /* Now do normal powerkey detection (in addition to long press) */
+ if ((new_state < PWRKEY_UNKNOWN) && (new_state != last_state)) {
+ if (new_state == PWRKEY_PRESS) {
+ if (wake_lock_active(&pwrkey_data->wake_lock))
+ wake_unlock(&pwrkey_data->wake_lock);
+ if (delayed_work_pending(&pwrkey_data->pwrkey_work)) {
+ /* If 600ms delayed work exists and we got a
+ keydown, then a doublepress has occured */
+ cancel_delayed_work_sync(&pwrkey_data-> \
+ pwrkey_work);
+ wake_lock_timeout(&pwrkey_data->wake_lock, 20);
+ cpcap_broadcast_key_event(cpcap,
+ KEY_POWER_DOUBLE, new_state);
+ pwrkey_data->power_double_pressed = 1;
+ } else {
+ /* If no delayed work was pending and we got a
+ keydown, then start 600ms delayed work */
+ wake_lock(&pwrkey_data->wake_lock);
+ schedule_delayed_work(&pwrkey_data->pwrkey_work,
+ msecs_to_jiffies(600));
+ }
+ } else {
+ /* Got a keyup. If we previously sent a doublepress
+ keydown, then send a doublepress keyup now */
+ if (pwrkey_data->power_double_pressed) {
+ if (wake_lock_active(&pwrkey_data->wake_lock))
+ wake_unlock(&pwrkey_data->wake_lock);
+ wake_lock_timeout(&pwrkey_data->wake_lock, 20);
+ cpcap_broadcast_key_event(cpcap,
+ KEY_POWER_DOUBLE, new_state);
+ pwrkey_data->power_double_pressed = 0;
+ /* If the 600ms delayed work is done and we got a keyup,
+ then send the keyup now */
+ } else if (!delayed_work_pending(&pwrkey_data-> \
+ pwrkey_work)) {
+ if (wake_lock_active(&pwrkey_data->wake_lock))
+ wake_unlock(&pwrkey_data->wake_lock);
+ wake_lock_timeout(&pwrkey_data->wake_lock, 20);
+ cpcap_broadcast_key_event(cpcap, KEY_END,
+ new_state);
+ }
+ /* If we got a keyup while 600ms delayed work is still
+ pending, then do nothing now and let the delayed
+ work handler handle this */
+ }
+ pwrkey_data->state = new_state;
+ }
+ cpcap_irq_unmask(cpcap, CPCAP_IRQ_ON);
+}
+
+static int pwrkey_init(struct cpcap_device *cpcap)
+{
+ struct pwrkey_data *data = kmalloc(sizeof(struct pwrkey_data),
+ GFP_KERNEL);
+ int retval;
+
+ if (!data)
+ return -ENOMEM;
+ data->cpcap = cpcap;
+ data->state = PWRKEY_RELEASE;
+ data->power_double_pressed = 0;
+ retval = cpcap_irq_register(cpcap, CPCAP_IRQ_ON, pwrkey_handler, data);
+ if (retval)
+ kfree(data);
+ wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "pwrkey");
+#ifdef CONFIG_PM_DEEPSLEEP
+
+ hrtimer_init(&(data->longPress_timer),
+ CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+
+ (data->longPress_timer).function = longPress_timer_callback;
+#endif
+ INIT_DELAYED_WORK(&data->pwrkey_work, pwrkey_work_func);
+
+ return retval;
+}
+
+static void pwrkey_remove(struct cpcap_device *cpcap)
+{
+ struct pwrkey_data *data;
+
+ cpcap_irq_get_data(cpcap, CPCAP_IRQ_ON, (void **)&data);
+ if (!data)
+ return;
+ cancel_delayed_work_sync(&data->pwrkey_work);
+ cpcap_irq_free(cpcap, CPCAP_IRQ_ON);
+ wake_lock_destroy(&data->wake_lock);
+ kfree(data);
+}
+
+static int int_read_and_clear(struct cpcap_device *cpcap,
+ unsigned short status_reg,
+ unsigned short mask_reg,
+ unsigned short valid_mask,
+ unsigned short *en)
+{
+ unsigned short ireg_val, mreg_val;
+ int ret;
+ ret = cpcap_regacc_read(cpcap, status_reg, &ireg_val);
+ if (ret)
+ return ret;
+ ret = cpcap_regacc_read(cpcap, mask_reg, &mreg_val);
+ if (ret)
+ return ret;
+ *en |= ireg_val & ~mreg_val;
+ *en &= valid_mask;
+ ret = cpcap_regacc_write(cpcap, mask_reg, *en, *en);
+ if (ret)
+ return ret;
+ ret = cpcap_regacc_write(cpcap, status_reg, *en, *en);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+
+static void irq_work_func(struct work_struct *work)
+{
+ int retval = 0;
+ unsigned short en_ints[NUM_INT_REGS];
+ int i;
+ struct cpcap_irqdata *data;
+ struct cpcap_device *cpcap;
+ struct spi_device *spi;
+ struct cpcap_platform_data *pdata;
+ unsigned int irq_gpio;
+
+ static const struct {
+ unsigned short status_reg;
+ unsigned short mask_reg;
+ unsigned short valid;
+ } int_reg[NUM_INT_REGS] = {
+ {CPCAP_REG_INT1, CPCAP_REG_INTM1, CPCAP_INT1_VALID_BITS},
+ {CPCAP_REG_INT2, CPCAP_REG_INTM2, CPCAP_INT2_VALID_BITS},
+ {CPCAP_REG_INT3, CPCAP_REG_INTM3, CPCAP_INT3_VALID_BITS},
+ {CPCAP_REG_INT4, CPCAP_REG_INTM4, CPCAP_INT4_VALID_BITS},
+ {CPCAP_REG_MI1, CPCAP_REG_MIM1, CPCAP_INT5_VALID_BITS}
+ };
+
+ for (i = 0; i < NUM_INT_REGS; ++i)
+ en_ints[i] = 0;
+
+ data = container_of(work, struct cpcap_irqdata, work);
+ cpcap = data->cpcap;
+ spi = cpcap->spi;
+ pdata = (struct cpcap_platform_data *) spi->controller_data;
+ irq_gpio = pdata->irq_gpio;
+
+ while (gpio_get_value(irq_gpio)) {
+ for (i = 0; i < NUM_INT_REGS; ++i) {
+ retval = int_read_and_clear(cpcap,
+ int_reg[i].status_reg,
+ int_reg[i].mask_reg,
+ int_reg[i].valid,
+ &en_ints[i]);
+ if (retval < 0) {
+ dev_err(&cpcap->spi->dev,
+ "Error reading interrupts\n");
+ break;
+ }
+ }
+ }
+ enable_irq(spi->irq);
+
+#ifdef CONFIG_PM_DBG_DRV
+ if ((pm_dbg_info.suspend != 0) && (pm_dbg_info.wakeup == 0)) {
+ for (i = 0; i < NUM_INT_REGS; ++i)
+ pm_dbg_info.en_ints[i] = en_ints[i];
+ pm_dbg_info.wakeup = 1;
+ }
+#endif /* CONFIG_PM_DBG_DRV */
+
+ /* lock protects event handlers and data */
+ mutex_lock(&data->lock);
+ for (i = 0; i < NUM_INT_REGS; ++i) {
+ unsigned char index;
+
+ while (en_ints[i] > 0) {
+ struct cpcap_event_handler *event_handler;
+
+ /* find the first set bit */
+ index = (unsigned char)(ffs(en_ints[i]) - 1);
+ if (index >= CPCAP_IRQ__NUM)
+ goto error;
+ /* clear the bit */
+ en_ints[i] &= ~(1 << index);
+ /* find the event that occurred */
+ index += CPCAP_IRQ__START + (i * NUM_INTS_PER_REG);
+ if (index >= CPCAP_IRQ__NUM)
+ goto error;
+ event_handler = &data->event_handler[index];
+
+ if (event_handler->func)
+ event_handler->func(index, event_handler->data);
+
+ data->irq_info[index].count++;
+
+ }
+ }
+error:
+ mutex_unlock(&data->lock);
+ wake_unlock(&data->wake_lock);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int cpcap_dbg_irq_show(struct seq_file *s, void *data)
+{
+ static const char *irq_name[] = {
+ [CPCAP_IRQ_HSCLK] = "HSCLK",
+ [CPCAP_IRQ_PRIMAC] = "PRIMAC",
+ [CPCAP_IRQ_SECMAC] = "SECMAC",
+ [CPCAP_IRQ_LOWBPL] = "LOWBPL",
+ [CPCAP_IRQ_SEC2PRI] = "SEC2PRI",
+ [CPCAP_IRQ_LOWBPH] = "LOWBPH",
+ [CPCAP_IRQ_EOL] = "EOL",
+ [CPCAP_IRQ_TS] = "TS",
+ [CPCAP_IRQ_ADCDONE] = "ADCDONE",
+ [CPCAP_IRQ_HS] = "HS",
+ [CPCAP_IRQ_MB2] = "MB2",
+ [CPCAP_IRQ_VBUSOV] = "VBUSOV",
+ [CPCAP_IRQ_RVRS_CHRG] = "RVRS_CHRG",
+ [CPCAP_IRQ_CHRG_DET] = "CHRG_DET",
+ [CPCAP_IRQ_IDFLOAT] = "IDFLOAT",
+ [CPCAP_IRQ_IDGND] = "IDGND",
+
+ [CPCAP_IRQ_SE1] = "SE1",
+ [CPCAP_IRQ_SESSEND] = "SESSEND",
+ [CPCAP_IRQ_SESSVLD] = "SESSVLD",
+ [CPCAP_IRQ_VBUSVLD] = "VBUSVLD",
+ [CPCAP_IRQ_CHRG_CURR1] = "CHRG_CURR1",
+ [CPCAP_IRQ_CHRG_CURR2] = "CHRG_CURR2",
+ [CPCAP_IRQ_RVRS_MODE] = "RVRS_MODE",
+ [CPCAP_IRQ_ON] = "ON",
+ [CPCAP_IRQ_ON2] = "ON2",
+ [CPCAP_IRQ_CLK] = "CLK",
+ [CPCAP_IRQ_1HZ] = "1HZ",
+ [CPCAP_IRQ_PTT] = "PTT",
+ [CPCAP_IRQ_SE0CONN] = "SE0CONN",
+ [CPCAP_IRQ_CHRG_SE1B] = "CHRG_SE1B",
+ [CPCAP_IRQ_UART_ECHO_OVERRUN] = "UART_ECHO_OVERRUN",
+ [CPCAP_IRQ_EXTMEMHD] = "EXTMEMHD",
+
+ [CPCAP_IRQ_WARM] = "WARM",
+ [CPCAP_IRQ_SYSRSTR] = "SYSRSTR",
+ [CPCAP_IRQ_SOFTRST] = "SOFTRST",
+ [CPCAP_IRQ_DIEPWRDWN] = "DIEPWRDWN",
+ [CPCAP_IRQ_DIETEMPH] = "DIETEMPH",
+ [CPCAP_IRQ_PC] = "PC",
+ [CPCAP_IRQ_OFLOWSW] = "OFLOWSW",
+ [CPCAP_IRQ_TODA] = "TODA",
+ [CPCAP_IRQ_OPT_SEL_DTCH] = "OPT_SEL_DTCH",
+ [CPCAP_IRQ_OPT_SEL_STATE] = "OPT_SEL_STATE",
+ [CPCAP_IRQ_ONEWIRE1] = "ONEWIRE1",
+ [CPCAP_IRQ_ONEWIRE2] = "ONEWIRE2",
+ [CPCAP_IRQ_ONEWIRE3] = "ONEWIRE3",
+ [CPCAP_IRQ_UCRESET] = "UCRESET",
+ [CPCAP_IRQ_PWRGOOD] = "PWRGOOD",
+ [CPCAP_IRQ_USBDPLLCLK] = "USBDPLLCLK",
+
+ [CPCAP_IRQ_DPI] = "DPI",
+ [CPCAP_IRQ_DMI] = "DMI",
+ [CPCAP_IRQ_UCBUSY] = "UCBUSY",
+ [CPCAP_IRQ_GCAI_CURR1] = "GCAI_CURR1",
+ [CPCAP_IRQ_GCAI_CURR2] = "GCAI_CURR2",
+ [CPCAP_IRQ_SB_MAX_RETRANSMIT_ERR] = "SB_MAX_RETRANSMIT_ERR",
+ [CPCAP_IRQ_BATTDETB] = "BATTDETB",
+ [CPCAP_IRQ_PRIHALT] = "PRIHALT",
+ [CPCAP_IRQ_SECHALT] = "SECHALT",
+ [CPCAP_IRQ_CC_CAL] = "CC_CAL",
+
+ [CPCAP_IRQ_UC_PRIROMR] = "UC_PRIROMR",
+ [CPCAP_IRQ_UC_PRIRAMW] = "UC_PRIRAMW",
+ [CPCAP_IRQ_UC_PRIRAMR] = "UC_PRIRAMR",
+ [CPCAP_IRQ_UC_USEROFF] = "UC_USEROFF",
+ [CPCAP_IRQ_UC_PRIMACRO_4] = "UC_PRIMACRO_4",
+ [CPCAP_IRQ_UC_PRIMACRO_5] = "UC_PRIMACRO_5",
+ [CPCAP_IRQ_UC_PRIMACRO_6] = "UC_PRIMACRO_6",
+ [CPCAP_IRQ_UC_PRIMACRO_7] = "UC_PRIMACRO_7",
+ [CPCAP_IRQ_UC_PRIMACRO_8] = "UC_PRIMACRO_8",
+ [CPCAP_IRQ_UC_PRIMACRO_9] = "UC_PRIMACRO_9",
+ [CPCAP_IRQ_UC_PRIMACRO_10] = "UC_PRIMACRO_10",
+ [CPCAP_IRQ_UC_PRIMACRO_11] = "UC_PRIMACRO_11",
+ [CPCAP_IRQ_UC_PRIMACRO_12] = "UC_PRIMACRO_12",
+ [CPCAP_IRQ_UC_PRIMACRO_13] = "UC_PRIMACRO_13",
+ [CPCAP_IRQ_UC_PRIMACRO_14] = "UC_PRIMACRO_14",
+ [CPCAP_IRQ_UC_PRIMACRO_15] = "UC_PRIMACRO_15",
+ };
+ unsigned int i;
+ struct cpcap_irqdata *irqdata = s->private;
+
+ seq_printf(s, "%21s%9s%12s%10s\n",
+ "CPCAP IRQ", "Enabled", "Registered", "Count");
+
+ for (i = 0; i < CPCAP_IRQ__NUM; i++) {
+ if ((i <= CPCAP_IRQ_CC_CAL) || (i >= CPCAP_IRQ_UC_PRIROMR)) {
+ seq_printf(s, "%21s%9d%12d%10d\n",
+ irq_name[i],
+ irqdata->irq_info[i].enabled,
+ irqdata->irq_info[i].registered,
+ irqdata->irq_info[i].count);
+ }
+ }
+ return 0;
+}
+
+static int cpcap_dbg_irq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, cpcap_dbg_irq_show, inode->i_private);
+}
+
+static const struct file_operations debug_fops = {
+ .open = cpcap_dbg_irq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+int cpcap_irq_init(struct cpcap_device *cpcap)
+{
+ int retval;
+ struct spi_device *spi = cpcap->spi;
+ struct cpcap_irqdata *data;
+
+ data = kzalloc(sizeof(struct cpcap_irqdata), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ cpcap_irq_mask_all(cpcap);
+
+ data->workqueue = create_workqueue("cpcap_irq");
+ INIT_WORK(&data->work, irq_work_func);
+ mutex_init(&data->lock);
+ wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "cpcap-irq");
+ data->cpcap = cpcap;
+
+ retval = request_irq(spi->irq, event_isr, IRQF_DISABLED |
+ IRQF_TRIGGER_RISING, "cpcap-irq", data);
+ if (retval) {
+ printk(KERN_ERR "cpcap_irq: Failed requesting irq.\n");
+ goto error;
+ }
+
+ enable_irq_wake(spi->irq);
+
+ cpcap->irqdata = data;
+ retval = pwrkey_init(cpcap);
+ if (retval) {
+ printk(KERN_ERR "cpcap_irq: Failed initializing pwrkey.\n");
+ goto error;
+ }
+#ifdef CONFIG_DEBUG_FS
+ (void)debugfs_create_file("cpcap-irq", S_IRUGO, NULL, data,
+ &debug_fops);
+#endif
+ return 0;
+
+error:
+ free_irq(spi->irq, data);
+ kfree(data);
+ printk(KERN_ERR "cpcap_irq: Error registering cpcap irq.\n");
+ return retval;
+}
+
+void cpcap_irq_shutdown(struct cpcap_device *cpcap)
+{
+ struct spi_device *spi = cpcap->spi;
+ struct cpcap_irqdata *data = cpcap->irqdata;
+
+ pwrkey_remove(cpcap);
+ cancel_work_sync(&data->work);
+ destroy_workqueue(data->workqueue);
+ free_irq(spi->irq, data);
+ kfree(data);
+}
+
+int cpcap_irq_register(struct cpcap_device *cpcap,
+ enum cpcap_irqs irq,
+ void (*cb_func) (enum cpcap_irqs, void *),
+ void *data)
+{
+ struct cpcap_irqdata *irqdata = cpcap->irqdata;
+ int retval = 0;
+
+ if ((irq >= CPCAP_IRQ__NUM) || (!cb_func))
+ return -EINVAL;
+
+ mutex_lock(&irqdata->lock);
+
+ if (irqdata->event_handler[irq].func == NULL) {
+ irqdata->irq_info[irq].registered = 1;
+ cpcap_irq_unmask(cpcap, irq);
+ irqdata->event_handler[irq].func = cb_func;
+ irqdata->event_handler[irq].data = data;
+ } else
+ retval = -EPERM;
+
+ mutex_unlock(&irqdata->lock);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_register);
+
+int cpcap_irq_free(struct cpcap_device *cpcap, enum cpcap_irqs irq)
+{
+ struct cpcap_irqdata *data = cpcap->irqdata;
+ int retval;
+
+ if (irq >= CPCAP_IRQ__NUM)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+ retval = cpcap_irq_mask(cpcap, irq);
+ data->event_handler[irq].func = NULL;
+ data->event_handler[irq].data = NULL;
+ data->irq_info[irq].registered = 0;
+ mutex_unlock(&data->lock);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_free);
+
+int cpcap_irq_get_data(struct cpcap_device *cpcap,
+ enum cpcap_irqs irq,
+ void **data)
+{
+ struct cpcap_irqdata *irqdata = cpcap->irqdata;
+
+ if (irq >= CPCAP_IRQ__NUM)
+ return -EINVAL;
+
+ mutex_lock(&irqdata->lock);
+ *data = irqdata->event_handler[irq].data;
+ mutex_unlock(&irqdata->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_get_data);
+
+int cpcap_irq_clear(struct cpcap_device *cpcap,
+ enum cpcap_irqs irq)
+{
+ int retval = -EINVAL;
+
+ if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) {
+ retval = cpcap_regacc_write(cpcap,
+ get_int_reg(irq),
+ EVENT_MASK(irq),
+ EVENT_MASK(irq));
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_clear);
+
+int cpcap_irq_mask(struct cpcap_device *cpcap,
+ enum cpcap_irqs irq)
+{
+ struct cpcap_irqdata *data = cpcap->irqdata;
+ int retval = -EINVAL;
+
+ if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) {
+ data->irq_info[irq].enabled = 0;
+ retval = cpcap_regacc_write(cpcap,
+ get_mask_reg(irq),
+ EVENT_MASK(irq),
+ EVENT_MASK(irq));
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_mask);
+
+int cpcap_irq_unmask(struct cpcap_device *cpcap,
+ enum cpcap_irqs irq)
+{
+ struct cpcap_irqdata *data = cpcap->irqdata;
+ int retval = -EINVAL;
+
+ if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC)) {
+ data->irq_info[irq].enabled = 1;
+ retval = cpcap_regacc_write(cpcap,
+ get_mask_reg(irq),
+ 0,
+ EVENT_MASK(irq));
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_unmask);
+
+int cpcap_irq_mask_get(struct cpcap_device *cpcap,
+ enum cpcap_irqs irq)
+{
+ struct cpcap_irqdata *data = cpcap->irqdata;
+ int retval = -EINVAL;
+
+ if ((irq < CPCAP_IRQ__NUM) && (irq != CPCAP_IRQ_SECMAC))
+ return data->irq_info[irq].enabled;
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_mask_get);
+
+int cpcap_irq_sense(struct cpcap_device *cpcap,
+ enum cpcap_irqs irq,
+ unsigned char clear)
+{
+ unsigned short val;
+ int retval;
+
+ if (irq >= CPCAP_IRQ__NUM)
+ return -EINVAL;
+
+ retval = cpcap_regacc_read(cpcap, get_sense_reg(irq), &val);
+ if (retval)
+ return retval;
+
+ if (clear)
+ retval = cpcap_irq_clear(cpcap, irq);
+ if (retval)
+ return retval;
+
+ return ((val & EVENT_MASK(irq)) != 0) ? 1 : 0;
+}
+EXPORT_SYMBOL_GPL(cpcap_irq_sense);
+
+#ifdef CONFIG_PM_DBG_DRV
+void cpcap_irq_pm_dbg_suspend(void)
+{
+ pm_dbg_info.suspend = 1;
+ pm_dbg_info.wakeup = 0;
+}
+
+void cpcap_irq_pm_dbg_resume(void)
+{
+ pm_dbg_info.suspend = 0;
+ if (pm_dbg_info.wakeup != 0) {
+ printk(KERN_INFO "PM_DBG WAKEUP CPCAP IRQ = 0x%x.0x%x.0%x.0x%x.0x%x\n",
+ pm_dbg_info.en_ints[0],
+ pm_dbg_info.en_ints[1],
+ pm_dbg_info.en_ints[2],
+ pm_dbg_info.en_ints[3],
+ pm_dbg_info.en_ints[4]);
+ }
+}
+#endif /* CONFIG_PM_DBG_DRV */
diff --git a/drivers/mfd/cpcap-key.c b/drivers/mfd/cpcap-key.c
new file mode 100644
index 00000000000..c9f066391fb
--- /dev/null
+++ b/drivers/mfd/cpcap-key.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2009 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+
+#if defined(CONFIG_MFD_M4SENSORHUB) || defined(CONFIG_MFD_M4SENSORHUB_MODULE)
+extern int m4sensorhub_stillmode_exit(void);
+#endif
+
+struct cpcap_key_data {
+ struct input_dev *input_dev;
+ struct cpcap_device *cpcap;
+};
+
+static int __init cpcap_key_probe(struct platform_device *pdev)
+{
+ int err;
+ struct cpcap_key_data *key;
+
+ if (pdev->dev.platform_data == NULL) {
+ dev_err(&pdev->dev, "no platform_data\n");
+ return -EINVAL;
+ }
+
+ key = kzalloc(sizeof(*key), GFP_KERNEL);
+ if (!key)
+ return -ENOMEM;
+
+ key->cpcap = pdev->dev.platform_data;
+
+ key->input_dev = input_allocate_device();
+ if (key->input_dev == NULL) {
+ dev_err(&pdev->dev, "can't allocate input device\n");
+ err = -ENOMEM;
+ goto err0;
+ }
+
+ set_bit(EV_KEY, key->input_dev->evbit);
+ set_bit(KEY_MEDIA, key->input_dev->keybit);
+ set_bit(KEY_END, key->input_dev->keybit);
+ set_bit(KEY_POWER_DOUBLE, key->input_dev->keybit);
+ set_bit(KEY_PLAYCD, key->input_dev->keybit);
+ set_bit(KEY_VOLUMEDOWN, key->input_dev->keybit);
+ set_bit(KEY_VOLUMEUP, key->input_dev->keybit);
+ set_bit(KEY_POWER_SONG, key->input_dev->keybit);
+ set_bit(KEY_SENDFILE, key->input_dev->keybit);
+
+ key->input_dev->name = "cpcap-key";
+
+ err = input_register_device(key->input_dev);
+ if (err < 0) {
+ dev_err(&pdev->dev, "could not register input device.\n");
+ goto err1;
+ }
+
+ platform_set_drvdata(pdev, key);
+ cpcap_set_keydata(key->cpcap, key);
+
+ dev_info(&pdev->dev, "CPCAP key device probed\n");
+
+ return 0;
+
+err1:
+ input_free_device(key->input_dev);
+err0:
+ kfree(key);
+ return err;
+}
+
+static int __exit cpcap_key_remove(struct platform_device *pdev)
+{
+ struct cpcap_key_data *key = platform_get_drvdata(pdev);
+
+ input_unregister_device(key->input_dev);
+ input_free_device(key->input_dev);
+ kfree(key);
+
+ return 0;
+}
+
+void cpcap_broadcast_key_event(struct cpcap_device *cpcap,
+ unsigned int code, int value)
+{
+ struct cpcap_key_data *key = cpcap_get_keydata(cpcap);
+
+/* TODO
+#if defined(CONFIG_MFD_M4SENSORHUB) || defined(CONFIG_MFD_M4SENSORHUB_MODULE)
+ //Notify sensorhub driver of power key down event
+ if (key && value)
+ m4sensorhub_stillmode_exit();
+#endif
+*/
+ if (key && key->input_dev) {
+ input_report_key(key->input_dev, code, value);
+ /*sync with input subsystem to solve the key cached problem*/
+ input_sync(key->input_dev);
+ }
+}
+EXPORT_SYMBOL(cpcap_broadcast_key_event);
+
+static struct platform_driver cpcap_key_driver = {
+ .probe = cpcap_key_probe,
+ .remove = __exit_p(cpcap_key_remove),
+ .driver = {
+ .name = "cpcap_key",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init cpcap_key_init(void)
+{
+ return platform_driver_register(&cpcap_key_driver);
+}
+module_init(cpcap_key_init);
+
+static void __exit cpcap_key_exit(void)
+{
+ platform_driver_unregister(&cpcap_key_driver);
+}
+module_exit(cpcap_key_exit);
+
+MODULE_ALIAS("platform:cpcap_key");
+MODULE_DESCRIPTION("CPCAP KEY driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/cpcap-regacc.c b/drivers/mfd/cpcap-regacc.c
new file mode 100644
index 00000000000..1a616014188
--- /dev/null
+++ b/drivers/mfd/cpcap-regacc.c
@@ -0,0 +1,393 @@
+/*
+ * Copyright (C) 2007-2009 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+
+#define IS_CPCAP(reg) ((reg) >= CPCAP_REG_START && (reg) <= CPCAP_REG_END)
+
+static DEFINE_MUTEX(reg_access);
+
+/*
+ * This table contains information about a single register in the power IC.
+ * It is used during register access to information such as the register address
+ * and the modifiability of each bit in the register. Special notes for
+ * particular elements of this structure follows:
+ *
+ * constant_mask: A '1' in this mask indicates that the corresponding bit has a
+ * 'constant' modifiability, and therefore must never be changed by any register
+ * access.
+ *
+ * It is important to note that any bits that are 'constant' must have
+ * synchronized read/write values. That is to say, when a 'constant' bit is
+ * read the value read must be identical to the value that must be written to
+ * that bit in order for that bit to be read with the same value.
+ *
+ * rbw_mask: A '1' in this mask indicates that the corresponding bit (when not
+ * being changed) should be written with the current value of that bit. A '0'
+ * in this mask indicates that the corresponding bit (when not being changed)
+ * should be written with a value of '0'.
+ */
+static const struct {
+ unsigned short address; /* Address of the register */
+ unsigned short constant_mask; /* Constant modifiability mask */
+ unsigned short rbw_mask; /* Read-before-write mask */
+} register_info_tbl[CPCAP_NUM_REG_CPCAP] = {
+ [CPCAP_REG_INT1] = {0, 0x0004, 0x0000},
+ [CPCAP_REG_INT2] = {1, 0x0000, 0x0000},
+ [CPCAP_REG_INT3] = {2, 0x0000, 0x0000},
+ [CPCAP_REG_INT4] = {3, 0xFC00, 0x0000},
+ [CPCAP_REG_INTM1] = {4, 0x0004, 0xFFFF},
+ [CPCAP_REG_INTM2] = {5, 0x0000, 0xFFFF},
+ [CPCAP_REG_INTM3] = {6, 0x0000, 0xFFFF},
+ [CPCAP_REG_INTM4] = {7, 0xFC00, 0xFFFF},
+ [CPCAP_REG_INTS1] = {8, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_INTS2] = {9, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_INTS3] = {10, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_INTS4] = {11, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_ASSIGN1] = {12, 0x80F8, 0xFFFF},
+ [CPCAP_REG_ASSIGN2] = {13, 0x0000, 0xFFFF},
+ [CPCAP_REG_ASSIGN3] = {14, 0x0004, 0xFFFF},
+ [CPCAP_REG_ASSIGN4] = {15, 0x0068, 0xFFFF},
+ [CPCAP_REG_ASSIGN5] = {16, 0x0000, 0xFFFF},
+ [CPCAP_REG_ASSIGN6] = {17, 0xFC00, 0xFFFF},
+ [CPCAP_REG_VERSC1] = {18, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_VERSC2] = {19, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_MI1] = {128, 0x0000, 0x0000},
+ [CPCAP_REG_MIM1] = {129, 0x0000, 0xFFFF},
+ [CPCAP_REG_MI2] = {130, 0x0000, 0xFFFF},
+ [CPCAP_REG_MIM2] = {131, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_UCC1] = {132, 0xF000, 0xFFFF},
+ [CPCAP_REG_UCC2] = {133, 0xFC00, 0xFFFF},
+ [CPCAP_REG_PC1] = {135, 0xFC00, 0xFFFF},
+ [CPCAP_REG_PC2] = {136, 0xFC00, 0xFFFF},
+ [CPCAP_REG_BPEOL] = {137, 0xFE00, 0xFFFF},
+ [CPCAP_REG_PGC] = {138, 0xFE00, 0xFFFF},
+ [CPCAP_REG_MT1] = {139, 0x0000, 0x0000},
+ [CPCAP_REG_MT2] = {140, 0x0000, 0x0000},
+ [CPCAP_REG_MT3] = {141, 0x0000, 0x0000},
+ [CPCAP_REG_PF] = {142, 0x0000, 0xFFFF},
+ [CPCAP_REG_SCC] = {256, 0xFF00, 0xFFFF},
+ [CPCAP_REG_SW1] = {257, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_SW2] = {258, 0xFC7F, 0xFFFF},
+ [CPCAP_REG_UCTM] = {259, 0xFFFE, 0xFFFF},
+ [CPCAP_REG_TOD1] = {260, 0xFF00, 0xFFFF},
+ [CPCAP_REG_TOD2] = {261, 0xFE00, 0xFFFF},
+ [CPCAP_REG_TODA1] = {262, 0xFF00, 0xFFFF},
+ [CPCAP_REG_TODA2] = {263, 0xFE00, 0xFFFF},
+ [CPCAP_REG_DAY] = {264, 0x8000, 0xFFFF},
+ [CPCAP_REG_DAYA] = {265, 0x8000, 0xFFFF},
+ [CPCAP_REG_VAL1] = {266, 0x0000, 0xFFFF},
+ [CPCAP_REG_VAL2] = {267, 0x0000, 0xFFFF},
+ [CPCAP_REG_SDVSPLL] = {384, 0x2488, 0xFFFF},
+ [CPCAP_REG_SI2CC1] = {385, 0x8000, 0xFFFF},
+ [CPCAP_REG_Si2CC2] = {386, 0xFF00, 0xFFFF},
+ [CPCAP_REG_S1C1] = {387, 0x9080, 0xFFFF},
+ [CPCAP_REG_S1C2] = {388, 0x8080, 0xFFFF},
+ [CPCAP_REG_S2C1] = {389, 0x9080, 0xFFFF},
+ [CPCAP_REG_S2C2] = {390, 0x8080, 0xFFFF},
+ [CPCAP_REG_S3C] = {391, 0xFA84, 0xFFFF},
+ [CPCAP_REG_S4C1] = {392, 0x9080, 0xFFFF},
+ [CPCAP_REG_S4C2] = {393, 0x8080, 0xFFFF},
+ [CPCAP_REG_S5C] = {394, 0xFFD5, 0xFFFF},
+ [CPCAP_REG_S6C] = {395, 0xFFF4, 0xFFFF},
+ [CPCAP_REG_VCAMC] = {396, 0xFF48, 0xFFFF},
+ [CPCAP_REG_VCSIC] = {397, 0xFFA8, 0xFFFF},
+ [CPCAP_REG_VDACC] = {398, 0xFF48, 0xFFFF},
+ [CPCAP_REG_VDIGC] = {399, 0xFF48, 0xFFFF},
+ [CPCAP_REG_VFUSEC] = {400, 0xFF50, 0xFFFF},
+ [CPCAP_REG_VHVIOC] = {401, 0xFFE8, 0xFFFF},
+ [CPCAP_REG_VSDIOC] = {402, 0xFF40, 0xFFFF},
+ [CPCAP_REG_VPLLC] = {403, 0xFFA4, 0xFFFF},
+ [CPCAP_REG_VRF1C] = {404, 0xFF50, 0xFFFF},
+ [CPCAP_REG_VRF2C] = {405, 0xFFD4, 0xFFFF},
+ [CPCAP_REG_VRFREFC] = {406, 0xFFD4, 0xFFFF},
+ [CPCAP_REG_VWLAN1C] = {407, 0xFFA8, 0xFFFF},
+ [CPCAP_REG_VWLAN2C] = {408, 0xFD32, 0xFFFF},
+ [CPCAP_REG_VSIMC] = {409, 0xE154, 0xFFFF},
+ [CPCAP_REG_VVIBC] = {410, 0xFFF2, 0xFFFF},
+#ifdef CONFIG_EMU_UART_DEBUG
+ [CPCAP_REG_VUSBC] = {411, 0xFFFF, 0xFFFF},
+#else
+ [CPCAP_REG_VUSBC] = {411, 0xFEA2, 0xFFFF},
+#endif
+ [CPCAP_REG_VUSBINT1C] = {412, 0xFFD4, 0xFFFF},
+ [CPCAP_REG_VUSBINT2C] = {413, 0xFFD4, 0xFFFF},
+ [CPCAP_REG_URT] = {414, 0xFFFE, 0xFFFF},
+ [CPCAP_REG_URM1] = {415, 0x0000, 0xFFFF},
+ [CPCAP_REG_URM2] = {416, 0xFC00, 0xFFFF},
+ [CPCAP_REG_VAUDIOC] = {512, 0xFF88, 0xFFFF},
+ [CPCAP_REG_CC] = {513, 0x0000, 0xFEDF},
+ [CPCAP_REG_CDI] = {514, 0x4000, 0xFFFF},
+ [CPCAP_REG_SDAC] = {515, 0xF000, 0xFCFF},
+ [CPCAP_REG_SDACDI] = {516, 0xC000, 0xFFFF},
+ [CPCAP_REG_TXI] = {517, 0x0000, 0xFFFF},
+ [CPCAP_REG_TXMP] = {518, 0xF000, 0xFFFF},
+ [CPCAP_REG_RXOA] = {519, 0xF800, 0xFFFF},
+ [CPCAP_REG_RXVC] = {520, 0x00C3, 0xFFFF},
+ [CPCAP_REG_RXCOA] = {521, 0xF800, 0xFFFF},
+ [CPCAP_REG_RXSDOA] = {522, 0xE000, 0xFFFF},
+ [CPCAP_REG_RXEPOA] = {523, 0x8000, 0xFFFF},
+ [CPCAP_REG_RXLL] = {524, 0x0000, 0xFFFF},
+ [CPCAP_REG_A2LA] = {525, 0xFF00, 0xFFFF},
+ [CPCAP_REG_MIPIS1] = {526, 0x0000, 0xFFFF},
+ [CPCAP_REG_MIPIS2] = {527, 0xFF00, 0xFFFF},
+ [CPCAP_REG_MIPIS3] = {528, 0xFFFC, 0xFFFF},
+ [CPCAP_REG_LVAB] = {529, 0xFFFC, 0xFFFF},
+ [CPCAP_REG_CCC1] = {640, 0xFFF0, 0xFFFF},
+ [CPCAP_REG_CRM] = {641, 0xC000, 0xFFFF},
+ [CPCAP_REG_CCCC2] = {642, 0xFFC0, 0xFFFF},
+ [CPCAP_REG_CCS1] = {643, 0x0000, 0xFFFF},
+ [CPCAP_REG_CCS2] = {644, 0xFF00, 0xFFFF},
+ [CPCAP_REG_CCA1] = {645, 0x0000, 0xFFFF},
+ [CPCAP_REG_CCA2] = {646, 0x0000, 0xFFFF},
+ [CPCAP_REG_CCM] = {647, 0xFC00, 0xFFFF},
+ [CPCAP_REG_CCO] = {648, 0xFC00, 0xFFFF},
+ [CPCAP_REG_CCI] = {649, 0xC000, 0xFFFF},
+ [CPCAP_REG_ADCC1] = {768, 0x0000, 0xFFFF},
+ [CPCAP_REG_ADCC2] = {769, 0x0080, 0xFFFF},
+ [CPCAP_REG_ADCD0] = {770, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_ADCD1] = {771, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_ADCD2] = {772, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_ADCD3] = {773, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_ADCD4] = {774, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_ADCD5] = {775, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_ADCD6] = {776, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_ADCD7] = {777, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_ADCAL1] = {778, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_ADCAL2] = {779, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_USBC1] = {896, 0x0000, 0xFFFF},
+#ifdef CONFIG_EMU_UART_DEBUG
+ [CPCAP_REG_USBC2] = {897, 0x0F07, 0xFFFF},
+#else
+ [CPCAP_REG_USBC2] = {897, 0x0000, 0xFFFF},
+#endif
+ [CPCAP_REG_USBC3] = {898, 0x8200, 0xFFFF},
+ [CPCAP_REG_UVIDL] = {899, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_UVIDH] = {900, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_UPIDL] = {901, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_UPIDH] = {902, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_UFC1] = {903, 0xFF80, 0xFFFF},
+ [CPCAP_REG_UFC2] = {904, 0xFF80, 0xFFFF},
+ [CPCAP_REG_UFC3] = {905, 0xFF80, 0xFFFF},
+ [CPCAP_REG_UIC1] = {906, 0xFF64, 0xFFFF},
+ [CPCAP_REG_UIC2] = {907, 0xFF64, 0xFFFF},
+ [CPCAP_REG_UIC3] = {908, 0xFF64, 0xFFFF},
+ [CPCAP_REG_USBOTG1] = {909, 0xFFC0, 0xFFFF},
+ [CPCAP_REG_USBOTG2] = {910, 0xFFC0, 0xFFFF},
+ [CPCAP_REG_USBOTG3] = {911, 0xFFC0, 0xFFFF},
+ [CPCAP_REG_UIER1] = {912, 0xFFE0, 0xFFFF},
+ [CPCAP_REG_UIER2] = {913, 0xFFE0, 0xFFFF},
+ [CPCAP_REG_UIER3] = {914, 0xFFE0, 0xFFFF},
+ [CPCAP_REG_UIEF1] = {915, 0xFFE0, 0xFFFF},
+ [CPCAP_REG_UIEF2] = {916, 0xFFE0, 0xFFFF},
+ [CPCAP_REG_UIEF3] = {917, 0xFFE0, 0xFFFF},
+ [CPCAP_REG_UIS] = {918, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_UIL] = {919, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_USBD] = {920, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_SCR1] = {921, 0xFF00, 0xFFFF},
+ [CPCAP_REG_SCR2] = {922, 0xFF00, 0xFFFF},
+ [CPCAP_REG_SCR3] = {923, 0xFF00, 0xFFFF},
+ [CPCAP_REG_VMC] = {939, 0xFFFE, 0xFFFF},
+ [CPCAP_REG_OWDC] = {940, 0xFFFC, 0xFFFF},
+ [CPCAP_REG_GPIO0] = {941, 0x0D11, 0x3FFF},
+ [CPCAP_REG_GPIO1] = {943, 0x0D11, 0x3FFF},
+ [CPCAP_REG_GPIO2] = {945, 0x0D11, 0x3FFF},
+ [CPCAP_REG_GPIO3] = {947, 0x0D11, 0x3FFF},
+ [CPCAP_REG_GPIO4] = {949, 0x0D11, 0x3FFF},
+ [CPCAP_REG_GPIO5] = {951, 0x0C11, 0x3FFF},
+ [CPCAP_REG_GPIO6] = {953, 0x0C11, 0x3FFF},
+ [CPCAP_REG_MDLC] = {1024, 0x0000, 0xFFFF},
+ [CPCAP_REG_KLC] = {1025, 0x8000, 0xFFFF},
+ [CPCAP_REG_ADLC] = {1026, 0x8000, 0xFFFF},
+ [CPCAP_REG_REDC] = {1027, 0xFC00, 0xFFFF},
+ [CPCAP_REG_GREENC] = {1028, 0xFC00, 0xFFFF},
+ [CPCAP_REG_BLUEC] = {1029, 0xFC00, 0xFFFF},
+ [CPCAP_REG_CFC] = {1030, 0xF000, 0xFFFF},
+ [CPCAP_REG_ABC] = {1031, 0xFFC3, 0xFFFF},
+ [CPCAP_REG_BLEDC] = {1032, 0xFC00, 0xFFFF},
+ [CPCAP_REG_CLEDC] = {1033, 0xFC00, 0xFFFF},
+ [CPCAP_REG_OW1C] = {1152, 0xFF00, 0xFFFF},
+ [CPCAP_REG_OW1D] = {1153, 0xFF00, 0xFFFF},
+ [CPCAP_REG_OW1I] = {1154, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_OW1IE] = {1155, 0xFF00, 0xFFFF},
+ [CPCAP_REG_OW1] = {1157, 0xFF00, 0xFFFF},
+ [CPCAP_REG_OW2C] = {1160, 0xFF00, 0xFFFF},
+ [CPCAP_REG_OW2D] = {1161, 0xFF00, 0xFFFF},
+ [CPCAP_REG_OW2I] = {1162, 0xFFFF, 0xFFFF},
+ [CPCAP_REG_OW2IE] = {1163, 0xFF00, 0xFFFF},
+ [CPCAP_REG_OW2] = {1165, 0xFF00, 0xFFFF},
+ [CPCAP_REG_OW3C] = {1168, 0xFF00, 0xFFFF},
+ [CPCAP_REG_OW3D] = {1169, 0xFF00, 0xFFFF},
+ [CPCAP_REG_OW3I] = {1170, 0xFF00, 0xFFFF},
+ [CPCAP_REG_OW3IE] = {1171, 0xFF00, 0xFFFF},
+ [CPCAP_REG_OW3] = {1173, 0xFF00, 0xFFFF},
+ [CPCAP_REG_GCAIC] = {1174, 0xFF00, 0xFFFF},
+ [CPCAP_REG_GCAIM] = {1175, 0xFF00, 0xFFFF},
+ [CPCAP_REG_LGDIR] = {1176, 0xFFE0, 0xFFFF},
+ [CPCAP_REG_LGPU] = {1177, 0xFFE0, 0xFFFF},
+ [CPCAP_REG_LGPIN] = {1178, 0xFF00, 0xFFFF},
+ [CPCAP_REG_LGMASK] = {1179, 0xFFE0, 0xFFFF},
+ [CPCAP_REG_LDEB] = {1180, 0xFF00, 0xFFFF},
+ [CPCAP_REG_LGDET] = {1181, 0xFF00, 0xFFFF},
+ [CPCAP_REG_LMISC] = {1182, 0xFF07, 0xFFFF},
+ [CPCAP_REG_LMACE] = {1183, 0xFFF8, 0xFFFF},
+ [CPCAP_REG_TEST] = {7936, 0x0000, 0xFFFF},
+ [CPCAP_REG_ST_TEST1] = {8002, 0x0000, 0xFFFF},
+};
+
+static int cpcap_spi_access(struct spi_device *spi, u8 *buf,
+ size_t len)
+{
+ struct spi_message m;
+ struct spi_transfer t = {
+ .tx_buf = buf,
+ .len = len,
+ .rx_buf = buf,
+ .bits_per_word = 32,
+ };
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ return spi_sync(spi, &m);
+}
+
+static int cpcap_config_for_read(struct spi_device *spi, unsigned short reg,
+ unsigned short *data)
+{
+ int status = -ENOTTY;
+ u32 buf32; /* force buf to be 32bit aligned */
+ u8 *buf = (u8 *) &buf32;
+
+ if (spi != NULL) {
+ buf[3] = (reg >> 6) & 0x000000FF;
+ buf[2] = (reg << 2) & 0x000000FF;
+ buf[1] = 0;
+ buf[0] = 0;
+
+ status = cpcap_spi_access(spi, buf, 4);
+
+ if (status == 0)
+ *data = buf[0] | (buf[1] << 8);
+ }
+
+ return status;
+}
+
+static int cpcap_config_for_write(struct spi_device *spi, unsigned short reg,
+ unsigned short data)
+{
+ int status = -ENOTTY;
+ u32 buf32; /* force buf to be 32bit aligned */
+ u8 *buf = (u8 *) &buf32;
+
+ if (spi != NULL) {
+ buf[3] = ((reg >> 6) & 0x000000FF) | 0x80;
+ buf[2] = (reg << 2) & 0x000000FF;
+ buf[1] = (data >> 8) & 0x000000FF;
+ buf[0] = data & 0x000000FF;
+
+ status = cpcap_spi_access(spi, buf, 4);
+ }
+
+ return status;
+}
+
+int cpcap_regacc_read(struct cpcap_device *cpcap, enum cpcap_reg reg,
+ unsigned short *value_ptr)
+{
+ int retval = -EINVAL;
+ struct spi_device *spi = cpcap->spi;
+
+ if (IS_CPCAP(reg) && (value_ptr != 0)) {
+ mutex_lock(&reg_access);
+
+ retval = cpcap_config_for_read(spi, register_info_tbl
+ [reg].address, value_ptr);
+
+ mutex_unlock(&reg_access);
+ }
+
+ return retval;
+}
+
+int cpcap_regacc_write(struct cpcap_device *cpcap,
+ enum cpcap_reg reg,
+ unsigned short value,
+ unsigned short mask)
+{
+ int retval = -EINVAL;
+ unsigned short old_value = 0;
+ struct cpcap_platform_data *data;
+ struct spi_device *spi = cpcap->spi;
+
+ data = (struct cpcap_platform_data *)spi->controller_data;
+
+ if (IS_CPCAP(reg) &&
+ (mask & register_info_tbl[reg].constant_mask) == 0) {
+ mutex_lock(&reg_access);
+
+ value &= mask;
+
+ if ((register_info_tbl[reg].rbw_mask) != 0) {
+ retval = cpcap_config_for_read(spi, register_info_tbl
+ [reg].address,
+ &old_value);
+ if (retval != 0)
+ goto error;
+ }
+
+ old_value &= register_info_tbl[reg].rbw_mask;
+ old_value &= ~mask;
+ value |= old_value;
+ retval = cpcap_config_for_write(spi,
+ register_info_tbl[reg].address,
+ value);
+error:
+ mutex_unlock(&reg_access);
+ }
+
+ return retval;
+}
+
+int cpcap_regacc_init(struct cpcap_device *cpcap)
+{
+ unsigned short i;
+ unsigned short mask;
+ int retval = 0;
+ struct cpcap_platform_data *data;
+ struct spi_device *spi = cpcap->spi;
+
+ data = (struct cpcap_platform_data *)spi->controller_data;
+
+ for (i = 0; i < data->init_len; i++) {
+ mask = 0xFFFF;
+ mask &= ~(register_info_tbl[data->init[i].reg].constant_mask);
+
+ retval = cpcap_regacc_write(cpcap, data->init[i].reg,
+ data->init[i].data,
+ mask);
+ if (retval)
+ break;
+ }
+
+ return retval;
+}
diff --git a/drivers/mfd/cpcap-uc.c b/drivers/mfd/cpcap-uc.c
new file mode 100644
index 00000000000..2626bfcb5f7
--- /dev/null
+++ b/drivers/mfd/cpcap-uc.c
@@ -0,0 +1,927 @@
+/*
+ * Copyright (C) 2008-2010 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/completion.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <linux/fs.h>
+#include <linux/ihex.h>
+#include <linux/miscdevice.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+#include <linux/spi/spi.h>
+
+#define ERROR_MACRO_TIMEOUT 0x81
+#define ERROR_MACRO_WRITE 0x82
+#define ERROR_MACRO_READ 0x83
+
+#define RAM_START_TI 0x9000
+#define RAM_END_TI 0x9FA0
+#define RAM_START_ST 0x0000
+#define RAM_END_ST 0x0FFF
+
+#define HWCFG_ADDR_ST 0x0148
+#define HWCFG_ADDR_TI 0x90F4 /* Not yet implemented in the TI uC. */
+
+enum {
+ READ_STATE_1, /* Send size and location of RAM read. */
+ READ_STATE_2, /*!< Read MT registers. */
+ READ_STATE_3, /*!< Read data from uC. */
+ READ_STATE_4, /*!< Check for error. */
+};
+
+enum {
+ WRITE_STATE_1, /* Send size and location of RAM write. */
+ WRITE_STATE_2, /* Check for error. */
+ WRITE_STATE_3, /* Write data to uC. */
+ WRITE_STATE_4 /* Check for error. */
+};
+
+struct cpcap_uc_data {
+ struct cpcap_device *cpcap;
+ unsigned char is_supported;
+ unsigned char is_ready;
+ struct completion completion;
+ int cb_status;
+ struct mutex lock;
+ unsigned char uc_reset;
+ unsigned char state;
+ unsigned short state_cntr;
+ struct {
+ unsigned short address;
+ unsigned short *data;
+ unsigned short num_words;
+ } req;
+};
+
+static struct cpcap_uc_data *cpcap_uc_info;
+
+static int fops_open(struct inode *inode, struct file *file);
+static long fops_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
+static ssize_t fops_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos);
+static ssize_t fops_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos);
+
+
+static const struct file_operations fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = fops_ioctl,
+ .open = fops_open,
+ .read = fops_read,
+ .write = fops_write,
+};
+
+static struct miscdevice uc_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "cpcap_uc",
+ .fops = &fops,
+};
+
+static int is_valid_address(struct cpcap_device *cpcap, unsigned short address,
+ unsigned short num_words)
+{
+ int vld = 0;
+
+ if (cpcap->vendor == CPCAP_VENDOR_TI) {
+ vld = (address >= RAM_START_TI) &&
+ ((address + num_words) <= RAM_END_TI);
+ } else if (cpcap->vendor == CPCAP_VENDOR_ST) {
+ vld = ((address + num_words) <= RAM_END_ST);
+ }
+
+ return vld;
+}
+
+static void ram_read_state_machine(enum cpcap_irqs irq, void *data)
+{
+ struct cpcap_uc_data *uc_data = data;
+ unsigned short temp;
+
+ if (irq != CPCAP_IRQ_UC_PRIRAMR)
+ return;
+
+ switch (uc_data->state) {
+ case READ_STATE_1:
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT1,
+ uc_data->req.address, 0xFFFF);
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT2,
+ uc_data->req.num_words, 0xFFFF);
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT3, 0, 0xFFFF);
+
+ if (uc_data->cpcap->vendor == CPCAP_VENDOR_ST)
+ uc_data->state = READ_STATE_2;
+ else
+ uc_data->state = READ_STATE_3;
+
+ cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+
+ break;
+
+ case READ_STATE_2:
+ cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, &temp);
+
+ if (temp == ERROR_MACRO_READ) {
+ uc_data->state = READ_STATE_1;
+ uc_data->state_cntr = 0;
+
+ cpcap_irq_mask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+
+ uc_data->cb_status = -EIO;
+
+ complete(&uc_data->completion);
+ } else {
+ cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT2, &temp);
+ cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT3, &temp);
+
+ uc_data->state = READ_STATE_3;
+ cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+ }
+ break;
+
+ case READ_STATE_3:
+ cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1,
+ uc_data->req.data + uc_data->state_cntr);
+
+ uc_data->state_cntr += 1;
+
+ if (uc_data->state_cntr == uc_data->req.num_words)
+ cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT2, &temp);
+ else {
+ cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT2,
+ uc_data->req.data +
+ uc_data->state_cntr);
+
+ uc_data->state_cntr += 1;
+ }
+
+ if (uc_data->state_cntr == uc_data->req.num_words)
+ cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT3, &temp);
+ else {
+ cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT3,
+ uc_data->req.data +
+ uc_data->state_cntr);
+
+ uc_data->state_cntr += 1;
+ }
+
+ if (uc_data->state_cntr == uc_data->req.num_words)
+ uc_data->state = READ_STATE_4;
+
+ cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+ break;
+
+ case READ_STATE_4:
+ cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, &temp);
+
+ if (temp != ERROR_MACRO_READ)
+ uc_data->cb_status = 0;
+ else
+ uc_data->cb_status = -EIO;
+
+ complete(&uc_data->completion);
+
+ uc_data->state = READ_STATE_1;
+ uc_data->state_cntr = 0;
+ break;
+
+ default:
+ uc_data->state = READ_STATE_1;
+ uc_data->state_cntr = 0;
+ break;
+ }
+}
+
+static void ram_write_state_machine(enum cpcap_irqs irq, void *data)
+{
+ struct cpcap_uc_data *uc_data = data;
+ unsigned short error_check;
+
+ if (irq != CPCAP_IRQ_UC_PRIRAMW)
+ return;
+
+ switch (uc_data->state) {
+ case WRITE_STATE_1:
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT1,
+ uc_data->req.address, 0xFFFF);
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT2,
+ uc_data->req.num_words, 0xFFFF);
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT3, 0, 0xFFFF);
+
+ uc_data->state = WRITE_STATE_2;
+ cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMW);
+ break;
+
+ case WRITE_STATE_2:
+ cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, &error_check);
+
+ if (error_check == ERROR_MACRO_WRITE) {
+ uc_data->state = WRITE_STATE_1;
+ uc_data->state_cntr = 0;
+
+ cpcap_irq_mask(uc_data->cpcap,
+ CPCAP_IRQ_UC_PRIRAMW);
+
+ uc_data->cb_status = -EIO;
+ complete(&uc_data->completion);
+ break;
+ } else
+ uc_data->state = WRITE_STATE_3;
+
+ /* No error has occured, fall through */
+
+ case WRITE_STATE_3:
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT1,
+ *(uc_data->req.data + uc_data->state_cntr),
+ 0xFFFF);
+ uc_data->state_cntr += 1;
+
+ if (uc_data->state_cntr == uc_data->req.num_words)
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT2, 0,
+ 0xFFFF);
+ else {
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT2,
+ *(uc_data->req.data +
+ uc_data->state_cntr), 0xFFFF);
+
+ uc_data->state_cntr += 1;
+ }
+
+ if (uc_data->state_cntr == uc_data->req.num_words)
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT3, 0,
+ 0xFFFF);
+ else {
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MT3,
+ *(uc_data->req.data +
+ uc_data->state_cntr), 0xFFFF);
+
+ uc_data->state_cntr += 1;
+ }
+
+ if (uc_data->state_cntr == uc_data->req.num_words)
+ uc_data->state = WRITE_STATE_4;
+
+ cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMW);
+ break;
+
+ case WRITE_STATE_4:
+ cpcap_regacc_read(uc_data->cpcap, CPCAP_REG_MT1, &error_check);
+
+ if (error_check != ERROR_MACRO_WRITE)
+ uc_data->cb_status = 0;
+ else
+ uc_data->cb_status = -EIO;
+
+ complete(&uc_data->completion);
+
+ uc_data->state = WRITE_STATE_1;
+ uc_data->state_cntr = 0;
+ break;
+
+ default:
+ uc_data->state = WRITE_STATE_1;
+ uc_data->state_cntr = 0;
+ break;
+ }
+}
+
+static void reset_handler(enum cpcap_irqs irq, void *data)
+{
+ int i;
+ unsigned short regval;
+ struct cpcap_uc_data *uc_data = data;
+
+ if (irq != CPCAP_IRQ_UCRESET)
+ return;
+
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCC1,
+ CPCAP_BIT_PRIHALT, CPCAP_BIT_PRIHALT);
+
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_PGC,
+ CPCAP_BIT_PRI_UC_SUSPEND, CPCAP_BIT_PRI_UC_SUSPEND);
+
+ uc_data->uc_reset = 1;
+ uc_data->cb_status = -EIO;
+ complete(&uc_data->completion);
+
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MI2, 0, 0xFFFF);
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MIM1, 0xFFFF, 0xFFFF);
+ cpcap_irq_mask(uc_data->cpcap, CPCAP_IRQ_PRIMAC);
+ cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UCRESET);
+
+ for (i = 0; i <= CPCAP_REG_END; i++) {
+ cpcap_regacc_read(uc_data->cpcap, i, &regval);
+ dev_err(&uc_data->cpcap->spi->dev,
+ "cpcap reg %d = 0x%04X\n", i, regval);
+ }
+
+ BUG();
+}
+
+static void primac_handler(enum cpcap_irqs irq, void *data)
+{
+ struct cpcap_uc_data *uc_data = data;
+
+ if (irq == CPCAP_IRQ_PRIMAC)
+ cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_PRIMAC);
+}
+
+static int ram_write(struct cpcap_uc_data *uc_data, unsigned short address,
+ unsigned short num_words, unsigned short *data)
+{
+ int retval = -EFAULT;
+
+ mutex_lock(&uc_data->lock);
+
+ if ((uc_data->cpcap->vendor == CPCAP_VENDOR_ST) &&
+ (uc_data->cpcap->revision <= CPCAP_REVISION_2_0)) {
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCTM,
+ CPCAP_BIT_UCTM, CPCAP_BIT_UCTM);
+ }
+
+ if (uc_data->is_supported && (num_words > 0) &&
+ (data != NULL) &&
+ is_valid_address(uc_data->cpcap, address, num_words) &&
+ !uc_data->uc_reset) {
+ uc_data->req.address = address;
+ uc_data->req.data = data;
+ uc_data->req.num_words = num_words;
+ uc_data->state = WRITE_STATE_1;
+ uc_data->state_cntr = 0;
+ INIT_COMPLETION(uc_data->completion);
+
+ retval = cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MI2,
+ CPCAP_BIT_PRIRAMW,
+ CPCAP_BIT_PRIRAMW);
+ if (retval)
+ goto err;
+
+ /* Cannot call cpcap_irq_register() here because unregister
+ * cannot be called from the state machine. Doing so causes
+ * a deadlock. */
+ retval = cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMW);
+ if (retval)
+ goto err;
+
+ wait_for_completion(&uc_data->completion);
+ retval = uc_data->cb_status;
+ }
+
+err:
+ if ((uc_data->cpcap->vendor == CPCAP_VENDOR_ST) &&
+ (uc_data->cpcap->revision <= CPCAP_REVISION_2_0)) {
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCTM,
+ 0, CPCAP_BIT_UCTM);
+ }
+
+ mutex_unlock(&uc_data->lock);
+
+ return retval;
+}
+
+static int ram_read(struct cpcap_uc_data *uc_data, unsigned short address,
+ unsigned short num_words, unsigned short *data)
+{
+ int retval = -EFAULT;
+
+ mutex_lock(&uc_data->lock);
+
+ if ((uc_data->cpcap->vendor == CPCAP_VENDOR_ST) &&
+ (uc_data->cpcap->revision <= CPCAP_REVISION_2_0)) {
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCTM,
+ CPCAP_BIT_UCTM, CPCAP_BIT_UCTM);
+ }
+
+ if (uc_data->is_supported && (num_words > 0) &&
+ is_valid_address(uc_data->cpcap, address, num_words) &&
+ !uc_data->uc_reset) {
+ uc_data->req.address = address;
+ uc_data->req.data = data;
+ uc_data->req.num_words = num_words;
+ uc_data->state = READ_STATE_1;
+ uc_data->state_cntr = 0;
+ INIT_COMPLETION(uc_data->completion);
+
+ retval = cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_MI2,
+ CPCAP_BIT_PRIRAMR,
+ CPCAP_BIT_PRIRAMR);
+ if (retval)
+ goto err;
+
+ /* Cannot call cpcap_irq_register() here because unregister
+ * cannot be called from the state machine. Doing so causes
+ * a deadlock. */
+ retval = cpcap_irq_unmask(uc_data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+ if (retval)
+ goto err;
+
+ wait_for_completion(&uc_data->completion);
+ retval = uc_data->cb_status;
+ }
+
+err:
+ if ((uc_data->cpcap->vendor == CPCAP_VENDOR_ST) &&
+ (uc_data->cpcap->revision <= CPCAP_REVISION_2_0)) {
+ cpcap_regacc_write(uc_data->cpcap, CPCAP_REG_UCTM,
+ 0, CPCAP_BIT_UCTM);
+ }
+
+ mutex_unlock(&uc_data->lock);
+
+ return retval;
+}
+
+static int ram_load(struct cpcap_uc_data *uc_data, unsigned int num_words,
+ unsigned short *data)
+{
+ int retval = -EINVAL;
+
+ if ((data != NULL) && (num_words > 0))
+ retval = ram_write(uc_data, data[0], (num_words - 1),
+ (data + 1));
+
+ return retval;
+}
+
+static ssize_t fops_write(struct file *file, const char *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t retval = -EINVAL;
+ unsigned short address;
+ unsigned short num_words;
+ unsigned short *data;
+ struct cpcap_uc_data *uc_data = file->private_data;
+
+ if ((buf != NULL) && (ppos != NULL) && (count >= 2)) {
+ data = kzalloc(count, GFP_KERNEL);
+
+ if (data != NULL) {
+ num_words = (unsigned short) (count >> 1);
+
+ /* If the position (uC RAM address) is zero then the
+ * data contains the address */
+ if (*ppos == 0) {
+ if (copy_from_user((void *) data, (void *) buf,
+ count) == 0)
+ retval = ram_load(uc_data, num_words,
+ data);
+ else
+ retval = -EFAULT;
+ }
+ /* If the position (uC RAM address) is not zero then the
+ * position holds the address to load the data */
+ else {
+ address = (unsigned short) (*ppos);
+
+ if (copy_from_user((void *) data, (void *) buf,
+ count) == 0)
+ retval = ram_write(uc_data, address,
+ num_words, data);
+ else
+ retval = -EFAULT;
+ }
+
+ kfree(data);
+ } else {
+ retval = -ENOMEM;
+ }
+ }
+
+ if (retval == 0)
+ retval = num_words;
+
+ return retval;
+}
+
+static ssize_t fops_read(struct file *file, char *buf,
+ size_t count, loff_t *ppos)
+{
+ ssize_t retval = -EFAULT;
+ unsigned short address;
+ unsigned short num_words;
+ unsigned short *data;
+ struct cpcap_uc_data *uc_data = file->private_data;
+
+ if ((buf != NULL) && (ppos != NULL) && (count >= 2)) {
+ data = kzalloc(count, GFP_KERNEL);
+
+ if (data != NULL) {
+ address = (unsigned short) (*ppos);
+ num_words = (unsigned short) (count >> 1);
+
+ retval = ram_read(uc_data, address, num_words, data);
+
+ if (retval)
+ goto err;
+
+ if (copy_to_user((void *)buf, (void *)data, count) == 0)
+ retval = count;
+ else
+ retval = -EFAULT;
+
+err:
+ kfree(data);
+ } else {
+ retval = -ENOMEM;
+ }
+ }
+
+ return retval;
+}
+
+static long fops_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int retval = -ENOTTY;
+ struct cpcap_uc_data *data = file->private_data;
+
+ switch (cmd) {
+ case CPCAP_IOCTL_UC_MACRO_START:
+ /* User space will only attempt to start the init macro if
+ * the ram load requests complete successfully. This is used
+ * as an indication that kernel requests to start macros can
+ * be allowed.
+ */
+ data->is_ready = 1;
+
+ retval = cpcap_uc_start(data->cpcap, (enum cpcap_macro)arg);
+
+ break;
+
+ case CPCAP_IOCTL_UC_MACRO_STOP:
+ retval = cpcap_uc_stop(data->cpcap, (enum cpcap_macro)arg);
+ break;
+
+ case CPCAP_IOCTL_UC_GET_VENDOR:
+ retval = copy_to_user((enum cpcap_vendor *)arg,
+ &(data->cpcap->vendor),
+ sizeof(enum cpcap_vendor));
+ break;
+
+ case CPCAP_IOCTL_UC_SET_TURBO_MODE:
+ if (arg != 0)
+ arg = 1;
+ retval = cpcap_regacc_write(data->cpcap, CPCAP_REG_UCTM,
+ (unsigned short)arg,
+ CPCAP_BIT_UCTM);
+ break;
+
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+static int fops_open(struct inode *inode, struct file *file)
+{
+ int retval = -ENOTTY;
+
+ if (cpcap_uc_info->is_supported)
+ retval = 0;
+
+ file->private_data = cpcap_uc_info;
+ dev_info(&cpcap_uc_info->cpcap->spi->dev, "CPCAP uC: open status:%d\n",
+ retval);
+
+ return retval;
+}
+
+int cpcap_uc_start(struct cpcap_device *cpcap, enum cpcap_macro macro)
+{
+ int retval = -EFAULT;
+ struct cpcap_uc_data *data = cpcap->ucdata;
+
+ if ((data->is_ready) &&
+ (macro > CPCAP_MACRO_USEROFF) && (macro < CPCAP_MACRO__END) &&
+ (data->uc_reset == 0)) {
+ if ((macro == CPCAP_MACRO_4) ||
+ ((cpcap->vendor == CPCAP_VENDOR_ST) &&
+ (macro == CPCAP_MACRO_12)) ||
+ ((cpcap->vendor == CPCAP_VENDOR_ST) &&
+ (macro == CPCAP_MACRO_15))) {
+ retval = cpcap_regacc_write(cpcap, CPCAP_REG_MI2,
+ (1 << macro),
+ (1 << macro));
+ } else {
+ retval = cpcap_regacc_write(cpcap, CPCAP_REG_MIM1,
+ 0, (1 << macro));
+ }
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_uc_start);
+
+int cpcap_uc_stop(struct cpcap_device *cpcap, enum cpcap_macro macro)
+{
+ int retval = -EFAULT;
+
+ if ((macro > CPCAP_MACRO_4) &&
+ (macro < CPCAP_MACRO__END)) {
+ if ((cpcap->vendor == CPCAP_VENDOR_ST) &&
+ (macro == CPCAP_MACRO_12)) {
+ retval = cpcap_regacc_write(cpcap, CPCAP_REG_MI2,
+ 0, (1 << macro));
+ } else {
+ retval = cpcap_regacc_write(cpcap, CPCAP_REG_MIM1,
+ (1 << macro), (1 << macro));
+ }
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_uc_stop);
+
+unsigned char cpcap_uc_status(struct cpcap_device *cpcap,
+ enum cpcap_macro macro)
+{
+ unsigned char retval = 0;
+ unsigned short regval;
+
+ if (macro < CPCAP_MACRO__END) {
+ if ((macro <= CPCAP_MACRO_4) ||
+ ((cpcap->vendor == CPCAP_VENDOR_ST) &&
+ (macro == CPCAP_MACRO_12))) {
+ cpcap_regacc_read(cpcap, CPCAP_REG_MI2, &regval);
+
+ if (regval & (1 << macro))
+ retval = 1;
+ } else {
+ cpcap_regacc_read(cpcap, CPCAP_REG_MIM1, &regval);
+
+ if (!(regval & (1 << macro)))
+ retval = 1;
+ }
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(cpcap_uc_status);
+
+#ifdef CONFIG_PM_DBG_DRV
+int cpcap_uc_ram_write(struct cpcap_device *cpcap, unsigned short address,
+ unsigned short num_words, unsigned short *data)
+{
+ return ram_write(cpcap->ucdata, address, num_words, data);
+}
+
+int cpcap_uc_ram_read(struct cpcap_device *cpcap, unsigned short address,
+ unsigned short num_words, unsigned short *data)
+{
+ return ram_read(cpcap->ucdata, address, num_words, data);
+}
+#endif /* CONFIG_PM_DBG_DRV */
+
+static int fw_load(struct cpcap_uc_data *uc_data, struct device *dev)
+{
+ int err;
+ const struct ihex_binrec *rec;
+ const struct firmware *fw;
+ unsigned short *buf;
+ int i;
+ unsigned short num_bytes;
+ unsigned short num_words;
+ unsigned char odd_bytes;
+ struct cpcap_platform_data *data;
+
+ data = uc_data->cpcap->spi->controller_data;
+
+ if (!uc_data || !dev)
+ return -EINVAL;
+
+ if (uc_data->cpcap->vendor == CPCAP_VENDOR_ST)
+ err = request_ihex_firmware(&fw, "cpcap/firmware_0_2x.fw", dev);
+ else
+ err = request_ihex_firmware(&fw, "cpcap/firmware_1_2x.fw", dev);
+
+ if (err) {
+ dev_err(dev, "Failed to load \"cpcap/firmware_%d_2x.fw\": %d\n",
+ uc_data->cpcap->vendor, err);
+ goto err;
+ }
+
+ for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
+ odd_bytes = 0;
+ num_bytes = be16_to_cpu(rec->len);
+
+ /* Since loader requires words, need even number of bytes. */
+ if (be16_to_cpu(rec->len) % 2) {
+ num_bytes++;
+ odd_bytes = 1;
+ }
+
+ num_words = num_bytes >> 1;
+ dev_dbg(dev, "Loading %d word(s) at 0x%04x\n",
+ num_words, be32_to_cpu(rec->addr));
+
+ buf = kzalloc(num_bytes, GFP_KERNEL);
+ if (buf) {
+ for (i = 0; i < num_words; i++) {
+ if (odd_bytes && (i == (num_words - 1)))
+ buf[i] = rec->data[i * 2];
+ else
+ buf[i] = ((uint16_t *)rec->data)[i];
+
+ buf[i] = be16_to_cpu(buf[i]);
+ }
+
+ err = ram_write(uc_data, be32_to_cpu(rec->addr),
+ num_words, buf);
+ kfree(buf);
+
+ if (err) {
+ dev_err(dev, "RAM write failed: %d\n", err);
+ break;
+ }
+ } else {
+ err = -ENOMEM;
+ dev_err(dev, "RAM write failed: %d\n", err);
+ break;
+ }
+ }
+
+ release_firmware(fw);
+
+ if (!err) {
+ uc_data->is_ready = 1;
+
+ if (uc_data->cpcap->vendor == CPCAP_VENDOR_ST)
+ err = ram_write(uc_data, 0x012C, 1, &(data->is_umts));
+ else
+ err = ram_write(uc_data, 0x90F0, 1, &(data->is_umts));
+
+ dev_info(dev, "Loaded Sec SPI Init = %d: %d\n",
+ data->is_umts, err);
+
+ if (uc_data->cpcap->vendor == CPCAP_VENDOR_ST)
+ err = ram_write(uc_data, HWCFG_ADDR_ST,
+ CPCAP_HWCFG_NUM, data->hwcfg);
+ else
+ err = ram_write(uc_data, HWCFG_ADDR_TI,
+ CPCAP_HWCFG_NUM, data->hwcfg);
+
+ dev_info(dev, "Loaded HWCFG data:");
+ for (i = 0; i < CPCAP_HWCFG_NUM; i++)
+ dev_info(dev, " 0x%04x", data->hwcfg[i]);
+ dev_info(dev, "result: %d\n", err);
+
+ err = cpcap_uc_start(uc_data->cpcap, CPCAP_MACRO_4);
+ dev_info(dev, "Started macro 4: %d\n", err);
+
+ err = cpcap_uc_start(uc_data->cpcap, CPCAP_MACRO_15);
+ dev_info(dev, "Started macro 15: %d\n", err);
+ }
+
+err:
+ return err;
+}
+
+static int cpcap_uc_probe(struct platform_device *pdev)
+{
+ int retval = 0;
+ struct cpcap_uc_data *data;
+
+ if (pdev->dev.platform_data == NULL) {
+ dev_err(&pdev->dev, "no platform_data\n");
+ return -EINVAL;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->cpcap = pdev->dev.platform_data;
+ data->uc_reset = 0;
+ data->is_supported = 0;
+ data->req.address = 0;
+ data->req.data = NULL;
+ data->req.num_words = 0;
+
+ init_completion(&data->completion);
+ mutex_init(&data->lock);
+ platform_set_drvdata(pdev, data);
+ cpcap_uc_info = data;
+ data->cpcap->ucdata = data;
+
+ if (((data->cpcap->vendor == CPCAP_VENDOR_TI) &&
+ (data->cpcap->revision >= CPCAP_REVISION_2_0)) ||
+ (data->cpcap->vendor == CPCAP_VENDOR_ST)) {
+ retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_PRIMAC,
+ primac_handler, data);
+ if (retval)
+ goto err_free;
+
+ cpcap_irq_clear(data->cpcap, CPCAP_IRQ_UCRESET);
+ retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_UCRESET,
+ reset_handler, data);
+ if (retval)
+ goto err_primac;
+
+ retval = cpcap_irq_register(data->cpcap,
+ CPCAP_IRQ_UC_PRIRAMR,
+ ram_read_state_machine, data);
+ if (retval)
+ goto err_ucreset;
+
+ retval = cpcap_irq_register(data->cpcap,
+ CPCAP_IRQ_UC_PRIRAMW,
+ ram_write_state_machine, data);
+ if (retval)
+ goto err_priramr;
+
+ retval = misc_register(&uc_dev);
+ if (retval)
+ goto err_priramw;
+
+ data->is_supported = 1;
+
+ cpcap_regacc_write(data->cpcap, CPCAP_REG_MIM1, 0xFFFF,
+ 0xFFFF);
+
+ retval = fw_load(data, &pdev->dev);
+ if (retval)
+ goto err_fw;
+ } else
+ retval = -ENODEV;
+
+ return retval;
+
+err_fw:
+ misc_deregister(&uc_dev);
+err_priramw:
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIRAMW);
+err_priramr:
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+err_ucreset:
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_UCRESET);
+err_primac:
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_PRIMAC);
+err_free:
+ kfree(data);
+
+ return retval;
+}
+
+static int __exit cpcap_uc_remove(struct platform_device *pdev)
+{
+ struct cpcap_uc_data *data = platform_get_drvdata(pdev);
+
+ misc_deregister(&uc_dev);
+
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_PRIMAC);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIRAMW);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_UC_PRIRAMR);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_UCRESET);
+
+ kfree(data);
+ return 0;
+}
+
+
+static struct platform_driver cpcap_uc_driver = {
+ .probe = cpcap_uc_probe,
+ .remove = __exit_p(cpcap_uc_remove),
+ .driver = {
+ .name = "cpcap_uc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init cpcap_uc_init(void)
+{
+ return platform_driver_register(&cpcap_uc_driver);
+}
+subsys_initcall(cpcap_uc_init);
+
+static void __exit cpcap_uc_exit(void)
+{
+ platform_driver_unregister(&cpcap_uc_driver);
+}
+module_exit(cpcap_uc_exit);
+
+MODULE_ALIAS("platform:cpcap_uc");
+MODULE_DESCRIPTION("CPCAP uC driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+MODULE_FIRMWARE("cpcap/firmware_0_2x.fw");
+MODULE_FIRMWARE("cpcap/firmware_1_2x.fw");
diff --git a/drivers/mfd/cpcap-usb-det.c b/drivers/mfd/cpcap-usb-det.c
new file mode 100644
index 00000000000..d594b123098
--- /dev/null
+++ b/drivers/mfd/cpcap-usb-det.c
@@ -0,0 +1,948 @@
+/*
+ * Copyright (C) 2007 - 2010 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/ktime.h>
+#include <linux/gpio.h>
+
+#include <linux/regulator/consumer.h>
+
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+#include <linux/spi/spi.h>
+
+#define MS_TO_NS(x) ((x) * NSEC_PER_MSEC)
+#define CPCAP_SENSE4_LS 8
+#define CPCAP_BIT_DP_S_LS (CPCAP_BIT_DP_S << CPCAP_SENSE4_LS)
+#define CPCAP_BIT_DM_S_LS (CPCAP_BIT_DM_S << CPCAP_SENSE4_LS)
+
+#define SENSE_USB (CPCAP_BIT_ID_FLOAT_S | \
+ CPCAP_BIT_CHRGCURR1_S | \
+ CPCAP_BIT_VBUSVLD_S | \
+ CPCAP_BIT_SESSVLD_S)
+
+#define SENSE_2WIRE (CPCAP_BIT_ID_FLOAT_S | \
+ CPCAP_BIT_CHRGCURR1_S | \
+ CPCAP_BIT_VBUSVLD_S | \
+ CPCAP_BIT_SESSVLD_S | \
+ CPCAP_BIT_DP_S_LS)
+
+#define SENSE_USB_FLASH (CPCAP_BIT_CHRGCURR1_S | \
+ CPCAP_BIT_VBUSVLD_S | \
+ CPCAP_BIT_SESSVLD_S)
+
+#define SENSE_FACTORY (CPCAP_BIT_ID_FLOAT_S | \
+ CPCAP_BIT_ID_GROUND_S | \
+ CPCAP_BIT_CHRGCURR1_S | \
+ CPCAP_BIT_VBUSVLD_S | \
+ CPCAP_BIT_SESSVLD_S)
+
+/* This Sense mask is needed because on TI the CHRGCURR1 interrupt is not always
+ * set. In Factory Mode the comparator follows the Charge current only. */
+#define SENSE_FACTORY_COM (CPCAP_BIT_ID_FLOAT_S | \
+ CPCAP_BIT_ID_GROUND_S | \
+ CPCAP_BIT_VBUSVLD_S | \
+ CPCAP_BIT_SESSVLD_S)
+
+#define SENSE_CHARGER_FLOAT (CPCAP_BIT_ID_FLOAT_S | \
+ CPCAP_BIT_CHRGCURR1_S | \
+ CPCAP_BIT_VBUSVLD_S | \
+ CPCAP_BIT_SESSVLD_S | \
+ CPCAP_BIT_SE1_S | \
+ CPCAP_BIT_DM_S_LS | \
+ CPCAP_BIT_DP_S_LS)
+
+#define SENSE_CHARGER (CPCAP_BIT_CHRGCURR1_S | \
+ CPCAP_BIT_VBUSVLD_S | \
+ CPCAP_BIT_SESSVLD_S | \
+ CPCAP_BIT_SE1_S | \
+ CPCAP_BIT_DM_S_LS | \
+ CPCAP_BIT_DP_S_LS)
+
+#define SENSE_IDLOW_CHARGER (CPCAP_BIT_CHRGCURR1_S | \
+ CPCAP_BIT_VBUSVLD_S | \
+ CPCAP_BIT_SESSVLD_S | \
+ CPCAP_BIT_ID_GROUND_S | \
+ CPCAP_BIT_DP_S_LS)
+
+#define SENSE_CHARGER_MASK (CPCAP_BIT_ID_GROUND_S | \
+ CPCAP_BIT_SESSVLD_S)
+
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+#define TWOWIRE_HANDSHAKE_LEN 6 /* Number of bytes in handshake sequence */
+#define TWOWIRE_DELAY MS_TO_NS(10) /* delay between edges in ns */
+#define BI2BY 8 /* bits per byte */
+#define TWOWIRE_HANDSHAKE_SEQUENCE {0x07, 0xC1, 0xF3, 0xE7, 0xCF, 0x9F}
+#endif
+
+#define UNDETECT_TRIES 5
+
+#define CPCAP_USB_DET_PRINT_STATUS (1U << 0)
+#define CPCAP_USB_DET_PRINT_TRANSITION (1U << 1)
+static int cpcap_usb_det_debug_mask;
+
+module_param_named(cpcap_usb_det_debug_mask, cpcap_usb_det_debug_mask, int,
+ S_IRUGO | S_IWUSR | S_IWGRP);
+
+#define cpcap_usb_det_debug(debug_level_mask, args...) \
+ do { \
+ if (cpcap_usb_det_debug_mask & \
+ CPCAP_USB_DET_PRINT_##debug_level_mask) { \
+ pr_info(args); \
+ } \
+ } while (0)
+
+enum cpcap_det_state {
+ CONFIG,
+ SAMPLE_1,
+ SAMPLE_2,
+ IDENTIFY,
+ USB,
+ FACTORY,
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ START2WIRE,
+ FINISH2WIRE,
+#endif
+};
+
+enum cpcap_accy {
+ CPCAP_ACCY_USB,
+ CPCAP_ACCY_FACTORY,
+ CPCAP_ACCY_CHARGER,
+ CPCAP_ACCY_NONE,
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ CPCAP_ACCY_2WIRE,
+#endif
+ /* Used while debouncing the accessory. */
+ CPCAP_ACCY_UNKNOWN,
+};
+
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+enum cpcap_twowire_state {
+ CPCAP_TWOWIRE_RUNNING,
+ CPCAP_TWOWIRE_DONE,
+};
+
+struct cpcap_usb_det_2wire {
+ int gpio;
+ unsigned short pos;
+ unsigned char data[TWOWIRE_HANDSHAKE_LEN];
+ enum cpcap_twowire_state state;
+};
+#endif
+
+struct cpcap_usb_det_data {
+ struct cpcap_device *cpcap;
+ struct delayed_work work;
+ unsigned short sense;
+ unsigned short prev_sense;
+ enum cpcap_det_state state;
+ enum cpcap_accy usb_accy;
+ struct platform_device *usb_dev;
+ struct platform_device *usb_connected_dev;
+ struct platform_device *charger_connected_dev;
+ struct regulator *regulator;
+ struct wake_lock wake_lock;
+ unsigned char is_vusb_enabled;
+ unsigned char undetect_cnt;
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ struct hrtimer hr_timer;
+ struct cpcap_usb_det_2wire twowire_data;
+#endif
+};
+
+static unsigned char vbus_valid_adc_check(struct cpcap_usb_det_data *data);
+
+static const char *accy_devices[] = {
+ "cpcap_usb_charger",
+ "cpcap_factory",
+ "cpcap_charger",
+};
+
+#ifdef CONFIG_USB_TESTING_POWER
+static int testing_power_enable = -1;
+module_param(testing_power_enable, int, 0644);
+MODULE_PARM_DESC(testing_power_enable, "Enable factory cable power "
+ "supply function for testing");
+#endif
+
+static void vusb_enable(struct cpcap_usb_det_data *data)
+{
+ int ret;
+ if (!data->is_vusb_enabled) {
+ wake_lock(&data->wake_lock);
+ ret = regulator_enable(data->regulator);
+ data->is_vusb_enabled = 1;
+ }
+}
+
+static void vusb_disable(struct cpcap_usb_det_data *data)
+{
+ int ret;
+ if (data->is_vusb_enabled) {
+ wake_unlock(&data->wake_lock);
+ ret = regulator_disable(data->regulator);
+ data->is_vusb_enabled = 0;
+ }
+}
+
+static int get_sense(struct cpcap_usb_det_data *data)
+{
+ int retval = -EFAULT;
+ unsigned short value;
+ struct cpcap_device *cpcap;
+
+ if (!data)
+ return -EFAULT;
+ cpcap = data->cpcap;
+
+ retval = cpcap_regacc_read(cpcap, CPCAP_REG_INTS1, &value);
+ if (retval)
+ return retval;
+
+ /* Clear ASAP after read. */
+ retval = cpcap_regacc_write(cpcap, CPCAP_REG_INT1,
+ (CPCAP_BIT_CHRG_DET_I |
+ CPCAP_BIT_ID_FLOAT_I |
+ CPCAP_BIT_ID_GROUND_I),
+ (CPCAP_BIT_CHRG_DET_I |
+ CPCAP_BIT_ID_FLOAT_I |
+ CPCAP_BIT_ID_GROUND_I));
+ if (retval)
+ return retval;
+
+ data->sense = value & (CPCAP_BIT_ID_FLOAT_S |
+ CPCAP_BIT_ID_GROUND_S);
+
+ retval = cpcap_regacc_read(cpcap, CPCAP_REG_INTS2, &value);
+ if (retval)
+ return retval;
+
+ /* Clear ASAP after read. */
+ retval = cpcap_regacc_write(cpcap, CPCAP_REG_INT2,
+ (CPCAP_BIT_CHRGCURR1_I |
+ CPCAP_BIT_VBUSVLD_I |
+ CPCAP_BIT_SESSVLD_I |
+ CPCAP_BIT_SE1_I),
+ (CPCAP_BIT_CHRGCURR1_I |
+ CPCAP_BIT_VBUSVLD_I |
+ CPCAP_BIT_SESSVLD_I |
+ CPCAP_BIT_SE1_I));
+ if (retval)
+ return retval;
+
+ data->sense |= value & (CPCAP_BIT_CHRGCURR1_S |
+ CPCAP_BIT_VBUSVLD_S |
+ CPCAP_BIT_SESSVLD_S |
+ CPCAP_BIT_SE1_S);
+
+ retval = cpcap_regacc_read(cpcap, CPCAP_REG_INTS4, &value);
+ if (retval)
+ return retval;
+
+ /* Clear ASAP after read. */
+ retval = cpcap_regacc_write(cpcap, CPCAP_REG_INT4,
+ (CPCAP_BIT_DP_I |
+ CPCAP_BIT_DM_I),
+ (CPCAP_BIT_DP_I |
+ CPCAP_BIT_DM_I));
+ if (retval)
+ return retval;
+
+ data->sense |= (value & (CPCAP_BIT_DP_S |
+ CPCAP_BIT_DM_S)) << CPCAP_SENSE4_LS;
+
+ return 0;
+}
+
+static int configure_hardware(struct cpcap_usb_det_data *data,
+ enum cpcap_accy accy)
+{
+ int retval;
+
+ /* Take control of pull up from ULPI. */
+ retval = cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC3,
+ CPCAP_BIT_PU_SPI,
+ CPCAP_BIT_PU_SPI);
+ retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC1,
+ CPCAP_BIT_DP150KPU,
+ (CPCAP_BIT_DP150KPU | CPCAP_BIT_DP1K5PU |
+ CPCAP_BIT_DM1K5PU | CPCAP_BIT_DPPD |
+ CPCAP_BIT_DMPD));
+
+ switch (accy) {
+ case CPCAP_ACCY_USB:
+ case CPCAP_ACCY_FACTORY:
+ retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC1, 0,
+ CPCAP_BIT_VBUSPD);
+ retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC2,
+ CPCAP_BIT_USBXCVREN,
+ CPCAP_BIT_USBXCVREN);
+ /* Give USB driver control of pull up via ULPI. */
+ retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC3,
+ 0,
+ CPCAP_BIT_PU_SPI |
+ CPCAP_BIT_DMPD_SPI |
+ CPCAP_BIT_DPPD_SPI |
+ CPCAP_BIT_SUSPEND_SPI |
+ CPCAP_BIT_ULPI_SPI_SEL);
+
+ if ((data->cpcap->vendor == CPCAP_VENDOR_ST) &&
+ (data->cpcap->revision == CPCAP_REVISION_2_0))
+ vusb_enable(data);
+
+ break;
+
+ case CPCAP_ACCY_CHARGER:
+ /* Disable Reverse Mode */
+ retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_CRM,
+ 0, CPCAP_BIT_RVRSMODE);
+ /* Enable VBus PullDown */
+ retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC1,
+ CPCAP_BIT_VBUSPD,
+ CPCAP_BIT_VBUSPD);
+ retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC3, 0,
+ CPCAP_BIT_VBUSSTBY_EN);
+ break;
+
+ case CPCAP_ACCY_UNKNOWN:
+ retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC1, 0,
+ CPCAP_BIT_VBUSPD);
+ break;
+
+ case CPCAP_ACCY_NONE:
+ default:
+ retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC1,
+ CPCAP_BIT_VBUSPD,
+ CPCAP_BIT_VBUSPD);
+ retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC2, 0,
+ CPCAP_BIT_USBXCVREN);
+ retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC3,
+ CPCAP_BIT_DMPD_SPI |
+ CPCAP_BIT_DPPD_SPI |
+ CPCAP_BIT_SUSPEND_SPI |
+ CPCAP_BIT_ULPI_SPI_SEL,
+ CPCAP_BIT_DMPD_SPI |
+ CPCAP_BIT_DPPD_SPI |
+ CPCAP_BIT_SUSPEND_SPI |
+ CPCAP_BIT_ULPI_SPI_SEL);
+ break;
+ }
+
+ if (retval != 0)
+ retval = -EFAULT;
+
+ return retval;
+}
+
+static unsigned char vbus_valid_adc_check(struct cpcap_usb_det_data *data)
+{
+ struct cpcap_adc_request req;
+ int ret;
+
+ req.format = CPCAP_ADC_FORMAT_CONVERTED;
+ req.timing = CPCAP_ADC_TIMING_IMM;
+ req.type = CPCAP_ADC_TYPE_BANK_0;
+
+ ret = cpcap_adc_sync_read(data->cpcap, &req);
+ if (ret) {
+ dev_err(&data->cpcap->spi->dev,
+ "%s: ADC Read failed\n", __func__);
+ return false;
+ }
+ return ((req.result[CPCAP_ADC_CHG_ISENSE] < 50) &&
+ (req.result[CPCAP_ADC_VBUS] <
+ (req.result[CPCAP_ADC_BATTP]))) ? false : true;
+}
+
+
+static void notify_accy(struct cpcap_usb_det_data *data, enum cpcap_accy accy)
+{
+ dev_info(&data->cpcap->spi->dev, "notify_accy: accy=%d\n", accy);
+
+ if ((data->usb_accy != CPCAP_ACCY_NONE) && (data->usb_dev != NULL)) {
+ platform_device_del(data->usb_dev);
+ data->usb_dev = NULL;
+ }
+
+ configure_hardware(data, accy);
+ data->usb_accy = accy;
+
+ if (accy != CPCAP_ACCY_NONE) {
+ data->usb_dev = platform_device_alloc(accy_devices[accy], -1);
+ if (data->usb_dev) {
+ data->usb_dev->dev.platform_data = data->cpcap;
+ platform_device_add(data->usb_dev);
+ }
+ } else
+ vusb_disable(data);
+
+ if ((accy == CPCAP_ACCY_USB) || (accy == CPCAP_ACCY_FACTORY)) {
+ if (!data->usb_connected_dev) {
+ data->usb_connected_dev =
+ platform_device_alloc("cpcap_usb_connected", -1);
+ platform_device_add(data->usb_connected_dev);
+ }
+ } else if (data->usb_connected_dev) {
+ platform_device_del(data->usb_connected_dev);
+ data->usb_connected_dev = NULL;
+ }
+
+ if (accy == CPCAP_ACCY_CHARGER) {
+ if (!data->charger_connected_dev) {
+ data->charger_connected_dev =
+ platform_device_alloc("cpcap_charger_connected",
+ -1);
+ platform_device_add(data->charger_connected_dev);
+ }
+ } else if (data->charger_connected_dev) {
+ platform_device_del(data->charger_connected_dev);
+ data->charger_connected_dev = NULL;
+ }
+}
+
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+static enum hrtimer_restart cpcap_send_2wire_sendbit(struct hrtimer *timer)
+{
+ struct cpcap_usb_det_data *usb_det_data =
+ container_of(timer, struct cpcap_usb_det_data, hr_timer);
+ struct cpcap_usb_det_2wire *twd = &(usb_det_data->twowire_data);
+ enum hrtimer_restart ret = HRTIMER_NORESTART;
+ bool value;
+
+ if (gpio_is_valid(twd->gpio) &&
+ (twd->pos < TWOWIRE_HANDSHAKE_LEN * BI2BY)) {
+ value = !!(twd->data[twd->pos/BI2BY] &
+ (1 << (BI2BY - (twd->pos % BI2BY) - 1)));
+ gpio_set_value(twd->gpio, value);
+ ret = HRTIMER_RESTART;
+ }
+
+ if (++twd->pos == TWOWIRE_HANDSHAKE_LEN * BI2BY ||
+ !gpio_is_valid(twd->gpio)) {
+ twd->state = CPCAP_TWOWIRE_DONE;
+ ret = HRTIMER_NORESTART;
+ }
+
+ if (ret == HRTIMER_RESTART)
+ hrtimer_forward(timer, ktime_get(), ns_to_ktime(TWOWIRE_DELAY));
+
+ return ret;
+}
+#endif
+
+static void detection_work(struct work_struct *work)
+{
+ struct cpcap_usb_det_data *data =
+ container_of(work, struct cpcap_usb_det_data, work.work);
+ unsigned char isVBusValid = 0;
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ ktime_t next_time;
+ int sessvalid;
+ unsigned char handshake[TWOWIRE_HANDSHAKE_LEN] =
+ TWOWIRE_HANDSHAKE_SEQUENCE;
+#endif
+
+ switch (data->state) {
+ case CONFIG:
+ vusb_enable(data);
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_CHRG_DET);
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_CHRG_CURR1);
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_SE1);
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_IDGND);
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_VBUSVLD);
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_IDFLOAT);
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_DPI);
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_DMI);
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_SESSVLD);
+
+ configure_hardware(data, CPCAP_ACCY_UNKNOWN);
+
+ data->undetect_cnt = 0;
+ data->state = SAMPLE_1;
+ schedule_delayed_work(&data->work, msecs_to_jiffies(11));
+ break;
+
+ case SAMPLE_1:
+ get_sense(data);
+ data->state = SAMPLE_2;
+ schedule_delayed_work(&data->work, msecs_to_jiffies(100));
+ break;
+
+ case SAMPLE_2:
+ data->prev_sense = data->sense;
+ get_sense(data);
+
+ if (data->prev_sense != data->sense) {
+ /* Stay in this state */
+ data->state = SAMPLE_2;
+ schedule_delayed_work(&data->work,
+ msecs_to_jiffies(100));
+ } else if (!(data->sense & CPCAP_BIT_SE1_S) &&
+ (data->sense & CPCAP_BIT_ID_FLOAT_S) &&
+ !(data->sense & CPCAP_BIT_ID_GROUND_S) &&
+ !(data->sense & CPCAP_BIT_SESSVLD_S)) {
+ data->state = IDENTIFY;
+ schedule_delayed_work(&data->work,
+ msecs_to_jiffies(100));
+ } else {
+ data->state = IDENTIFY;
+ schedule_delayed_work(&data->work, 0);
+ }
+ break;
+
+ case IDENTIFY:
+ get_sense(data);
+ data->state = CONFIG;
+ isVBusValid = vbus_valid_adc_check(data);
+
+ if ((data->sense == SENSE_USB) ||
+ (data->sense == SENSE_USB_FLASH)) {
+ notify_accy(data, CPCAP_ACCY_USB);
+
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_CHRG_DET);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_CHRG_CURR1);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_SE1);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDGND);
+
+ /* Special handling of USB cable undetect. */
+ data->state = USB;
+ } else if ((data->sense == SENSE_FACTORY) ||
+ (data->sense == SENSE_FACTORY_COM)) {
+#ifdef CONFIG_USB_TESTING_POWER
+ if (testing_power_enable > 0) {
+ notify_accy(data, CPCAP_ACCY_NONE);
+ cpcap_irq_unmask(data->cpcap,
+ CPCAP_IRQ_CHRG_DET);
+ cpcap_irq_unmask(data->cpcap,
+ CPCAP_IRQ_CHRG_CURR1);
+ cpcap_irq_unmask(data->cpcap,
+ CPCAP_IRQ_VBUSVLD);
+ break;
+ }
+#endif
+ notify_accy(data, CPCAP_ACCY_FACTORY);
+
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_SE1);
+
+ /* Special handling of factory cable undetect. */
+ data->state = FACTORY;
+ } else if (((data->sense | CPCAP_BIT_VBUSVLD_S) == \
+ SENSE_CHARGER_FLOAT) ||
+ ((data->sense | CPCAP_BIT_VBUSVLD_S) == \
+ SENSE_CHARGER) ||
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ (data->usb_accy == CPCAP_ACCY_2WIRE) ||
+#endif
+ (data->sense == SENSE_IDLOW_CHARGER)) {
+
+ if ((isVBusValid) && ((data->sense == \
+ SENSE_CHARGER_FLOAT) ||
+ (data->sense == SENSE_CHARGER) ||
+ (data->sense == SENSE_IDLOW_CHARGER) ||
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ (data->usb_accy == CPCAP_ACCY_2WIRE) ||
+#endif
+ (data->sense & CPCAP_BIT_SESSVLD_S))) {
+ /* Wakeup device from Suspend especially when
+ * you are coming from dipping voltage[<4.2V]
+ * to higher one [4.6V - VBUS,5V]
+ */
+ if (!(wake_lock_active(&data->wake_lock)))
+ wake_lock(&data->wake_lock);
+
+ notify_accy(data, CPCAP_ACCY_CHARGER);
+ /* VBUS is valid and also session valid bit
+ * is set hence, we notify that charger is
+ * connected
+ */
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_SE1);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDGND);
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ data->state = FINISH2WIRE;
+ schedule_delayed_work(&data->work,
+ msecs_to_jiffies(500));
+#else
+ data->state = CONFIG;
+#endif
+ } else if ((!isVBusValid) &&
+ ((!(data->sense & CPCAP_BIT_SESSVLD_S) ||
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ (data->usb_accy == CPCAP_ACCY_2WIRE) ||
+#endif
+ (!(data->sense & CPCAP_BIT_VBUSVLD_S))))) {
+ /* Condition when the USB charger is connected &
+ * for some reason Voltage falls below the 4.4V
+ * threshold. Since USB is connected, we reset
+ * the State Machine and wait for the voltage to
+ * reach the high threshold
+ */
+ data->state = CONFIG;
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ data->usb_accy = CPCAP_ACCY_NONE;
+ if (gpio_is_valid(data->twowire_data.gpio))
+ gpio_set_value(data->twowire_data.gpio,
+ 0);
+#endif
+
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDGND);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_SE1);
+ cpcap_irq_unmask(data->cpcap,
+ CPCAP_IRQ_VBUSVLD);
+ cpcap_irq_unmask(data->cpcap,
+ CPCAP_IRQ_CHRG_DET);
+
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_VBUSVLD);
+ schedule_delayed_work(&data->work, 0);
+ }
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ } else if ((data->sense == SENSE_2WIRE) &&
+ (data->usb_accy == CPCAP_ACCY_NONE)) {
+ /* wait 750ms with GPIO low to force idle state */
+ if (gpio_is_valid(data->twowire_data.gpio)) {
+ gpio_set_value(data->twowire_data.gpio, 0);
+ data->state = START2WIRE;
+ schedule_delayed_work(&data->work,
+ msecs_to_jiffies(750));
+ } else {
+ printk(KERN_ERR "Detected 2wire charger but "
+ "GPIO is not configured\n");
+ data->state = CONFIG;
+ cpcap_irq_unmask(data->cpcap,
+ CPCAP_IRQ_CHRG_DET);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_DPI);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_DMI);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_SE1);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDGND);
+ }
+#endif
+ } else if ((data->sense & CPCAP_BIT_VBUSVLD_S) &&
+ (data->usb_accy == CPCAP_ACCY_NONE)) {
+ data->state = CONFIG;
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_CHRG_DET);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_DPI);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_DMI);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_SE1);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDGND);
+ } else {
+ notify_accy(data, CPCAP_ACCY_NONE);
+
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_CHRG_DET);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_CHRG_CURR1);
+
+ /* When a charger is unpowered by unplugging from the
+ * wall, VBUS voltage will drop below CHRG_DET (3.5V)
+ * until the ICHRG bits are cleared. Once ICHRG is
+ * cleared, VBUS will rise above CHRG_DET, but below
+ * VBUSVLD (4.4V) briefly as it decays. If the charger
+ * is re-powered while VBUS is within this window, the
+ * VBUSVLD interrupt is needed to trigger charger
+ * detection.
+ *
+ * VBUSVLD must be masked before going into suspend.
+ * See cpcap_usb_det_suspend() for details.
+ */
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_VBUSVLD);
+ }
+ break;
+
+ case USB:
+ get_sense(data);
+
+ if ((data->sense & CPCAP_BIT_SE1_S) ||
+ (data->sense & CPCAP_BIT_ID_GROUND_S)) {
+ data->state = CONFIG;
+ schedule_delayed_work(&data->work, 0);
+ } else if (!(data->sense & CPCAP_BIT_VBUSVLD_S)) {
+ if (data->undetect_cnt++ < UNDETECT_TRIES) {
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_CHRG_DET);
+ cpcap_irq_mask(data->cpcap,
+ CPCAP_IRQ_CHRG_CURR1);
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_SE1);
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_IDGND);
+ data->state = USB;
+ schedule_delayed_work(&data->work,
+ msecs_to_jiffies(100));
+ } else {
+ data->state = CONFIG;
+ schedule_delayed_work(&data->work, 0);
+ }
+ } else {
+ data->state = USB;
+ data->undetect_cnt = 0;
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_CHRG_DET);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_CHRG_CURR1);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_SE1);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDGND);
+ }
+ break;
+
+ case FACTORY:
+ get_sense(data);
+
+ /* The removal of a factory cable can only be detected if a
+ * charger is attached.
+ */
+ if (data->sense & CPCAP_BIT_SE1_S) {
+#ifdef CONFIG_TTA_CHARGER
+ enable_tta();
+#endif
+ data->state = CONFIG;
+ schedule_delayed_work(&data->work, 0);
+ } else {
+ data->state = FACTORY;
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_SE1);
+ }
+ break;
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ case START2WIRE:
+ sessvalid = (data->sense & CPCAP_BIT_SESSVLD_S);
+ memcpy(data->twowire_data.data, handshake,
+ TWOWIRE_HANDSHAKE_LEN);
+ data->twowire_data.pos = 5;
+ data->twowire_data.state = CPCAP_TWOWIRE_RUNNING;
+ next_time = ktime_set(0, TWOWIRE_DELAY);
+ hrtimer_start(&data->hr_timer, next_time, HRTIMER_MODE_REL);
+
+ while (sessvalid && data->twowire_data.state !=
+ CPCAP_TWOWIRE_DONE) {
+ msleep(10);
+ get_sense(data);
+ sessvalid = (data->sense & CPCAP_BIT_SESSVLD_S);
+ }
+
+ if (sessvalid && data->twowire_data.state ==
+ CPCAP_TWOWIRE_DONE) {
+ data->usb_accy = CPCAP_ACCY_2WIRE;
+ data->state = IDENTIFY;
+ schedule_delayed_work(&data->work, 0);
+ } else {
+ printk(KERN_ERR "2wire removed durring handshake\n");
+ hrtimer_cancel(&data->hr_timer);
+ if (gpio_is_valid(data->twowire_data.gpio))
+ gpio_set_value(data->twowire_data.gpio, 0);
+ data->state = CONFIG;
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_CHRG_DET);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_DPI);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_DMI);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_SE1);
+ cpcap_irq_unmask(data->cpcap, CPCAP_IRQ_IDGND);
+ }
+ break;
+ case FINISH2WIRE:
+ if (gpio_is_valid(data->twowire_data.gpio))
+ gpio_set_value(data->twowire_data.gpio, 0);
+ data->state = CONFIG;
+ break;
+#endif
+ default:
+ /* This shouldn't happen. Need to reset state machine. */
+ vusb_disable(data);
+ data->state = CONFIG;
+ schedule_delayed_work(&data->work, 0);
+ break;
+ }
+}
+
+static void int_handler(enum cpcap_irqs int_event, void *data)
+{
+ struct cpcap_usb_det_data *usb_det_data = data;
+ schedule_delayed_work(&(usb_det_data->work), 0);
+}
+
+static int cpcap_usb_det_probe(struct platform_device *pdev)
+{
+ int retval;
+ struct cpcap_usb_det_data *data;
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ struct cpcap_platform_data *platform_data;
+#endif
+
+ if (pdev->dev.platform_data == NULL) {
+ dev_err(&pdev->dev, "no platform_data\n");
+ return -EINVAL;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->cpcap = pdev->dev.platform_data;
+ data->state = CONFIG;
+ platform_set_drvdata(pdev, data);
+ INIT_DELAYED_WORK(&data->work, detection_work);
+ data->usb_accy = CPCAP_ACCY_NONE;
+ wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "usb");
+ data->undetect_cnt = 0;
+
+ data->regulator = regulator_get(NULL, "vusb");
+ if (IS_ERR(data->regulator)) {
+ dev_err(&pdev->dev, "Could not get regulator for cpcap_usb\n");
+ retval = PTR_ERR(data->regulator);
+ goto free_mem;
+ }
+ regulator_set_voltage(data->regulator, 3300000, 3300000);
+
+ retval = cpcap_irq_register(data->cpcap, CPCAP_IRQ_CHRG_DET,
+ int_handler, data);
+ retval |= cpcap_irq_register(data->cpcap, CPCAP_IRQ_CHRG_CURR1,
+ int_handler, data);
+ retval |= cpcap_irq_register(data->cpcap, CPCAP_IRQ_SE1,
+ int_handler, data);
+ retval |= cpcap_irq_register(data->cpcap, CPCAP_IRQ_IDGND,
+ int_handler, data);
+ retval |= cpcap_irq_register(data->cpcap, CPCAP_IRQ_VBUSVLD,
+ int_handler, data);
+ retval |= cpcap_irq_register(data->cpcap, CPCAP_IRQ_IDFLOAT,
+ int_handler, data);
+ retval |= cpcap_irq_register(data->cpcap, CPCAP_IRQ_DPI,
+ int_handler, data);
+ retval |= cpcap_irq_register(data->cpcap, CPCAP_IRQ_DMI,
+ int_handler, data);
+ retval |= cpcap_irq_register(data->cpcap, CPCAP_IRQ_SESSVLD,
+ int_handler, data);
+
+ /* Now that HW initialization is done, give USB control via ULPI. */
+ retval |= cpcap_regacc_write(data->cpcap, CPCAP_REG_USBC3,
+ 0, CPCAP_BIT_ULPI_SPI_SEL);
+
+#ifdef CONFIG_CHARGER_CPCAP_2WIRE
+ hrtimer_init(&(data->hr_timer), CLOCK_REALTIME, HRTIMER_MODE_REL);
+ data->hr_timer.function = &cpcap_send_2wire_sendbit;
+ if (data->cpcap->spi && data->cpcap->spi->controller_data) {
+ platform_data = data->cpcap->spi->controller_data;
+ data->twowire_data.gpio = platform_data->twowire_hndshk_gpio;
+ } else {
+ data->twowire_data.gpio = -1;
+ dev_err(&pdev->dev, "SPI platform_data missing\n");
+ retval = -EINVAL;
+ }
+#endif
+
+ if (retval != 0) {
+ dev_err(&pdev->dev, "Initialization Error\n");
+ retval = -ENODEV;
+ goto free_irqs;
+ }
+
+ dev_info(&pdev->dev, "CPCAP USB detection device probed\n");
+
+ /* Perform initial detection */
+ detection_work(&(data->work.work));
+
+ return 0;
+
+free_irqs:
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_VBUSVLD);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_IDGND);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_SE1);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_CHRG_CURR1);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_CHRG_DET);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_IDFLOAT);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_DPI);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_DMI);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_SESSVLD);
+ regulator_put(data->regulator);
+free_mem:
+ wake_lock_destroy(&data->wake_lock);
+ kfree(data);
+
+ return retval;
+}
+
+static int cpcap_usb_det_remove(struct platform_device *pdev)
+{
+ struct cpcap_usb_det_data *data = platform_get_drvdata(pdev);
+
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_CHRG_DET);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_CHRG_CURR1);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_SE1);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_IDGND);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_VBUSVLD);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_IDFLOAT);
+ cpcap_irq_free(data->cpcap, CPCAP_IRQ_SESSVLD);
+
+ configure_hardware(data, CPCAP_ACCY_NONE);
+ cancel_delayed_work_sync(&data->work);
+
+ if ((data->usb_accy != CPCAP_ACCY_NONE) && (data->usb_dev != NULL))
+ platform_device_del(data->usb_dev);
+
+ vusb_disable(data);
+ regulator_put(data->regulator);
+
+ wake_lock_destroy(&data->wake_lock);
+
+ kfree(data);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cpcap_usb_det_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct cpcap_usb_det_data *data = platform_get_drvdata(pdev);
+
+ /* VBUSVLD cannot be unmasked when entering suspend. If left
+ * unmasked, a false interrupt will be received, keeping the
+ * device out of suspend. The interrupt does not need to be
+ * unmasked when resuming from suspend since the use case
+ * for having the interrupt unmasked is over.
+ */
+ cpcap_irq_mask(data->cpcap, CPCAP_IRQ_VBUSVLD);
+
+ return 0;
+}
+#else
+#define cpcap_usb_det_suspend NULL
+#endif
+
+static struct platform_driver cpcap_usb_det_driver = {
+ .probe = cpcap_usb_det_probe,
+ .remove = cpcap_usb_det_remove,
+ .suspend = cpcap_usb_det_suspend,
+ .driver = {
+ .name = "cpcap_usb_det",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init cpcap_usb_det_init(void)
+{
+ return cpcap_driver_register(&cpcap_usb_det_driver);
+}
+/* The CPCAP USB detection driver must be started later to give the MUSB
+ * driver time to complete its initialization. */
+late_initcall(cpcap_usb_det_init);
+
+static void __exit cpcap_usb_det_exit(void)
+{
+ platform_driver_unregister(&cpcap_usb_det_driver);
+}
+module_exit(cpcap_usb_det_exit);
+
+MODULE_ALIAS("platform:cpcap_usb_det");
+MODULE_DESCRIPTION("CPCAP USB detection driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/m4sensorhub-core.c b/drivers/mfd/m4sensorhub-core.c
new file mode 100644
index 00000000000..cf1a363921a
--- /dev/null
+++ b/drivers/mfd/m4sensorhub-core.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2012 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/rtc.h>
+#include <linux/gpio.h>
+#include <linux/string.h>
+#include <linux/m4sensorhub/MemMapLog.h>
+#include <linux/m4sensorhub.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/delay.h>
+
+
+#define M4SENSORHUB_NUM_GPIOS 6
+
+/* --------------- Global Declarations -------------- */
+char m4sensorhub_debug;
+EXPORT_SYMBOL_GPL(m4sensorhub_debug);
+
+/* ------------ Local Function Prototypes ----------- */
+
+/* -------------- Local Data Structures ------------- */
+static struct miscdevice m4sensorhub_misc_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = M4SENSORHUB_DRIVER_NAME,
+};
+
+/* --------------- Local Declarations -------------- */
+static struct m4sensorhub_data *m4sensorhub_misc_data;
+static DEFINE_MUTEX(m4sensorhub_driver_lock);
+
+unsigned short force_upgrade;
+module_param(force_upgrade, short, 0644);
+MODULE_PARM_DESC(force_upgrade, "Force FW download ignoring version check");
+
+unsigned short debug_level;
+module_param(debug_level, short, 0644);
+MODULE_PARM_DESC(debug_level, "Set debug level 1 (CRITICAL) to "
+ "7 (VERBOSE_DEBUG)");
+
+/* -------------- Global Functions ----------------- */
+struct m4sensorhub_data *m4sensorhub_client_get_drvdata(void)
+{
+ return m4sensorhub_misc_data;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_client_get_drvdata);
+
+
+/* -------------- Local Functions ----------------- */
+
+static ssize_t m4sensorhub_get_dbg(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", m4sensorhub_debug);
+}
+
+/* BEGIN BOARD FILE */
+/* TODO: replace with request array */
+
+int m4sensorhub_set_bootmode(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_bootmode bootmode)
+{
+ if (!m4sensorhub) {
+ printk(KERN_ERR "set_bootmode: invalid pointer\n");
+ return -EINVAL;
+ }
+
+ switch (bootmode) {
+ case BOOTMODE00:
+ gpio_set_value(m4sensorhub->hwconfig.boot0_gpio, 0);
+ gpio_set_value(m4sensorhub->hwconfig.boot1_gpio, 0);
+ break;
+ case BOOTMODE01:
+ gpio_set_value(m4sensorhub->hwconfig.boot0_gpio, 1);
+ gpio_set_value(m4sensorhub->hwconfig.boot1_gpio, 0);
+ break;
+ case BOOTMODE10:
+ gpio_set_value(m4sensorhub->hwconfig.boot0_gpio, 0);
+ gpio_set_value(m4sensorhub->hwconfig.boot1_gpio, 1);
+ break;
+ case BOOTMODE11:
+ gpio_set_value(m4sensorhub->hwconfig.boot0_gpio, 1);
+ gpio_set_value(m4sensorhub->hwconfig.boot1_gpio, 1);
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static void minnow_m4sensorhub_hw_reset(struct m4sensorhub_data *m4sensorhub)
+{
+ if (!m4sensorhub) {
+ printk(KERN_ERR "m4sensorhub_hw_reset: invalid pointer\n");
+ return;
+ }
+
+ m4sensorhub_set_bootmode(m4sensorhub, BOOTMODE00);
+ gpio_set_value(m4sensorhub->hwconfig.reset_gpio, 1);
+ msleep(5);
+ gpio_set_value(m4sensorhub->hwconfig.reset_gpio, 0);
+ msleep(5);
+ gpio_set_value(m4sensorhub->hwconfig.reset_gpio, 1);
+}
+
+/* callback from driver to initialize hardware on probe */
+static int minnow_m4sensorhub_hw_init(struct m4sensorhub_data *m4sensorhub,
+ struct device_node *node)
+{
+ int gpio;
+ int err = -EINVAL;
+
+ if (!m4sensorhub) {
+ printk(KERN_ERR "m4sensorhub_hw_init: invalid pointer\n");
+ err = -EINVAL;
+ goto error;
+ }
+ if (node == NULL) {
+ printk(KERN_ERR "m4sensorhub_hw_init: node null\n");
+ err = -EINVAL;
+ goto error;
+ }
+
+ gpio = of_get_named_gpio_flags(node, "mot,irq-gpio", 0, NULL);
+ err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-intr");
+ if (err) {
+ pr_err("Failed acquiring M4 Sensor Hub IRQ GPIO-%d (%d)\n",
+ gpio, err);
+ goto error;
+ }
+ gpio_direction_input(gpio);
+ m4sensorhub->hwconfig.irq_gpio = gpio;
+
+ gpio = of_get_named_gpio_flags(node, "mot,reset-gpio", 0, NULL);
+ err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-reset");
+ if (err) {
+ pr_err("Failed acquiring M4 Sensor Hub Reset GPIO-%d (%d)\n",
+ gpio, err);
+ goto error_reset;
+ }
+ gpio_direction_output(gpio, 1);
+ m4sensorhub->hwconfig.reset_gpio = gpio;
+
+ gpio = of_get_named_gpio_flags(node, "mot,wake-gpio", 0, NULL);
+ err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-wake");
+ if (err) {
+ pr_err("Failed acquiring M4 Sensor Hub Wake GPIO-%d (%d)\n",
+ gpio, err);
+ goto error_wake;
+ }
+ gpio_direction_output(gpio, 0);
+ m4sensorhub->hwconfig.wake_gpio = gpio;
+
+ gpio = of_get_named_gpio_flags(node, "mot,boot0-gpio", 0, NULL);
+ err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-boot0");
+ if (err) {
+ pr_err("Failed acquiring M4 Sensor Hub Boot0 GPIO-%d (%d)\n",
+ gpio, err);
+ goto error_boot0;
+ }
+ gpio_direction_output(gpio, 0);
+ m4sensorhub->hwconfig.boot0_gpio = gpio;
+
+ gpio = of_get_named_gpio_flags(node, "mot,boot1-gpio", 0, NULL);
+ err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-boot1");
+ if (err) {
+ pr_err("Failed acquiring M4 Sensor Hub Boot1 GPIO-%d (%d)\n",
+ gpio, err);
+ goto error_boot1;
+ }
+ gpio_direction_output(gpio, 0);
+ m4sensorhub->hwconfig.boot1_gpio = gpio;
+
+ gpio = of_get_named_gpio_flags(node, "mot,enable-gpio", 0, NULL);
+ err = (gpio < 0) ? -ENODEV : gpio_request(gpio, "m4sensorhub-enable");
+ if (err) {
+ pr_err("Failed acquiring M4 Sensor Hub Enable GPIO-%d (%d)\n",
+ gpio, err);
+ goto error_enable;
+ }
+ gpio_direction_output(gpio, 0);
+ m4sensorhub->hwconfig.mpu_9150_en_gpio = gpio;
+
+ minnow_m4sensorhub_hw_reset(m4sensorhub);
+
+ return 0;
+
+error_enable:
+ gpio_free(m4sensorhub->hwconfig.boot1_gpio);
+ m4sensorhub->hwconfig.boot1_gpio = -1;
+error_boot1:
+ gpio_free(m4sensorhub->hwconfig.boot0_gpio);
+ m4sensorhub->hwconfig.boot0_gpio = -1;
+error_boot0:
+ gpio_free(m4sensorhub->hwconfig.wake_gpio);
+ m4sensorhub->hwconfig.wake_gpio = -1;
+error_wake:
+ gpio_free(m4sensorhub->hwconfig.reset_gpio);
+ m4sensorhub->hwconfig.reset_gpio = -1;
+error_reset:
+ gpio_free(m4sensorhub->hwconfig.irq_gpio);
+ m4sensorhub->hwconfig.irq_gpio = -1;
+error:
+ return err;
+}
+
+/* callback from driver to free hardware on shutdown */
+static void minnow_m4sensorhub_hw_free(struct m4sensorhub_data *m4sensorhub)
+{
+
+ if (!m4sensorhub) {
+ printk(KERN_ERR "hw_free: invalid pointer\n");
+ return;
+ }
+
+ if (m4sensorhub->hwconfig.irq_gpio >= 0) {
+ gpio_free(m4sensorhub->hwconfig.irq_gpio);
+ m4sensorhub->hwconfig.irq_gpio = -1;
+ }
+
+ if (m4sensorhub->hwconfig.reset_gpio >= 0) {
+ gpio_free(m4sensorhub->hwconfig.reset_gpio);
+ m4sensorhub->hwconfig.reset_gpio = -1;
+ }
+
+ if (m4sensorhub->hwconfig.wake_gpio >= 0) {
+ gpio_free(m4sensorhub->hwconfig.wake_gpio);
+ m4sensorhub->hwconfig.wake_gpio = -1;
+ }
+
+ if (m4sensorhub->hwconfig.boot0_gpio >= 0) {
+ gpio_free(m4sensorhub->hwconfig.boot0_gpio);
+ m4sensorhub->hwconfig.boot0_gpio = -1;
+ }
+
+ if (m4sensorhub->hwconfig.boot1_gpio >= 0) {
+ gpio_free(m4sensorhub->hwconfig.boot1_gpio);
+ m4sensorhub->hwconfig.boot1_gpio = -1;
+ }
+
+ if (m4sensorhub->hwconfig.mpu_9150_en_gpio >= 0) {
+ gpio_free(m4sensorhub->hwconfig.mpu_9150_en_gpio);
+ m4sensorhub->hwconfig.mpu_9150_en_gpio = -1;
+ }
+}
+
+/* END BOARD FILE FUNCTIONS */
+
+static ssize_t m4sensorhub_set_dbg(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long debug;
+
+ if ((strict_strtol(buf, 10, &debug) < 0) ||
+ (debug < M4SH_NODEBUG) || (debug > M4SH_VERBOSE_DEBUG))
+ return -EINVAL;
+
+ m4sensorhub_debug = debug;
+ KDEBUG(M4SH_CRITICAL, "M4 Sensor Hub debug level = %d\n",
+ m4sensorhub_debug);
+
+ return count;
+}
+
+static DEVICE_ATTR(debug_level, S_IRUGO|S_IWUGO, m4sensorhub_get_dbg,
+ m4sensorhub_set_dbg);
+
+static ssize_t m4sensorhub_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long long loglevel;
+
+ m4sensorhub_reg_read(m4sensorhub_misc_data,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ KDEBUG(M4SH_INFO, "M4 loglevel = %llx", loglevel);
+ return sprintf(buf, "%llu\n", loglevel);
+}
+void ParseAndUpdateLogLevels(char *tag, char *level,
+ unsigned long long *logLevels)
+{
+ int i;
+ int levelindex = -1;
+ int tagindex = -1;
+ unsigned long long mask;
+
+ for (i = 0; i < LOG_LEVELS_MAX; i++) {
+ if (strcmp(acLogLevels[i], level) == 0) {
+ levelindex = i;
+ break;
+ }
+ }
+
+ for (i = 0; i < LOG_MAX; i++) {
+ if (strcmp(acLogTags[i], tag) == 0) {
+ tagindex = i;
+ break;
+ }
+ }
+
+ if ((tagindex == -1) || (levelindex == -1))
+ return;
+
+ /*Clear the revelant bits*/
+ mask = 0x03;
+ *logLevels &= ~(mask << (tagindex * 2));
+ /*set debug level for the relevant bits*/
+ *logLevels |= (levelindex << (tagindex * 2));
+ KDEBUG(M4SH_INFO, "New M4 log levels = 0x%llx \n", *logLevels);
+}
+
+/* Usage: adb shell into the directory of sysinterface log_level and
+ echo LOG_ACCEL=LOG_DEGUB,LOG_POWER=LOG_ERROR > log_level */
+static ssize_t m4sensorhub_set_loglevel(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ unsigned long long currentLogLevels;
+ char *tag, *level;
+ char **logbuf = (char **) &buf;
+
+ m4sensorhub_reg_read(m4sensorhub_misc_data,
+ M4SH_REG_LOG_LOGENABLE, (char *)&currentLogLevels);
+ while (1) {
+ tag = strsep(logbuf, "=,\n ");
+ if (tag == NULL)
+ break;
+ level = strsep(logbuf, "=,\n ");
+ if (level == NULL)
+ break;
+ ParseAndUpdateLogLevels(tag, level, &currentLogLevels);
+ }
+
+ return m4sensorhub_reg_write(m4sensorhub_misc_data,
+ M4SH_REG_LOG_LOGENABLE, (char *)&currentLogLevels,
+ m4sh_no_mask);
+}
+
+static DEVICE_ATTR(log_level, S_IRUGO|S_IWUGO, m4sensorhub_get_loglevel,
+ m4sensorhub_set_loglevel);
+
+static int m4sensorhub_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct m4sensorhub_data *m4sensorhub;
+ struct device_node *node = client->dev.of_node;
+ int err = -EINVAL;
+
+
+ /* Set debug based on module argument if set, otherwise use
+ default logging rate based on build type */
+ if (debug_level)
+ m4sensorhub_debug = debug_level;
+ else {
+#ifdef CONFIG_DEBUG_FS
+ /* engineering build */
+ m4sensorhub_debug = M4SH_INFO;
+#else
+ /* user/userdebug builds */
+ m4sensorhub_debug = M4SH_ERROR;
+#endif
+ }
+ KDEBUG(M4SH_ERROR, "Initializing M4 Sensor Hub: force_upgrade=%d "
+ "debug=%d\n", force_upgrade, m4sensorhub_debug);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ KDEBUG(M4SH_ERROR, "client not i2c capable\n");
+ err = -ENODEV;
+ goto err_unload;
+ }
+ m4sensorhub = kzalloc(sizeof(*m4sensorhub), GFP_KERNEL);
+ if (m4sensorhub == NULL) {
+ err = -ENOMEM;
+ KDEBUG(M4SH_ERROR,
+ "failed to allocate memory for module data: %d\n", err);
+ goto err_unload;
+ }
+ m4sensorhub_misc_data = m4sensorhub;
+
+ err = minnow_m4sensorhub_hw_init(m4sensorhub, node);
+ if (err)
+ printk(KERN_ERR "%s: hw_init Failed!", __func__);
+
+ /* link i2c_client to m4sensorhub */
+ i2c_set_clientdata(client, m4sensorhub);
+
+ /* link m4sensorhub to i2c_client */
+ m4sensorhub->i2c_client = client;
+
+ err = misc_register(&m4sensorhub_misc_device);
+ if (err < 0) {
+ KDEBUG(M4SH_ERROR, "misc register failed: %d\n", err);
+ goto err_hw_free;
+ }
+
+ err = device_create_file(&client->dev, &dev_attr_debug_level);
+ if (err < 0) {
+ KDEBUG(M4SH_ERROR, "Error creating debug_level file\n");
+ goto err_deregister;
+ }
+
+ err = device_create_file(&client->dev, &dev_attr_log_level);
+ if (err < 0) {
+ KDEBUG(M4SH_ERROR, "Error creating log_level file\n");
+ goto err_del_debug_file;
+ }
+
+ err = m4sensorhub_load_firmware(m4sensorhub, force_upgrade);
+ if (err < 0) {
+ dev_err(&client->dev, "load firmware file failed: %d\n", err);
+ goto err_del_log_file;
+ }
+
+ err = m4sensorhub_reg_init(m4sensorhub);
+ if (err < 0)
+ goto err_set_bootmode;
+
+ if (m4sensorhub->hwconfig.irq_gpio >= 0)
+ client->irq = gpio_to_irq(m4sensorhub->hwconfig.irq_gpio);
+ else {
+ KDEBUG(M4SH_ERROR, "Error: No IRQ configured\n");
+ err = -ENODEV;
+ goto err_reg_shutdown;
+ }
+
+ err = m4sensorhub_panic_init(m4sensorhub);
+ if (err < 0)
+ goto err_reg_shutdown;
+
+ err = m4sensorhub_irq_init(m4sensorhub);
+ if (err < 0)
+ goto err_panic_shutdown;
+
+
+ KDEBUG(M4SH_NOTICE, "Registered M4 Sensor Hub\n");
+
+ goto done;
+
+err_panic_shutdown:
+ m4sensorhub_panic_shutdown(m4sensorhub);
+err_reg_shutdown:
+ m4sensorhub_reg_shutdown(m4sensorhub);
+err_set_bootmode:
+ minnow_m4sensorhub_hw_reset(m4sensorhub);
+err_del_log_file:
+ device_remove_file(&client->dev, &dev_attr_log_level);
+err_del_debug_file:
+ device_remove_file(&client->dev, &dev_attr_debug_level);
+err_deregister:
+ misc_deregister(&m4sensorhub_misc_device);
+err_hw_free:
+ m4sensorhub->i2c_client = NULL;
+ i2c_set_clientdata(client, NULL);
+ minnow_m4sensorhub_hw_free(m4sensorhub);
+ kfree(m4sensorhub);
+ m4sensorhub = NULL;
+ m4sensorhub_misc_data = NULL;
+err_unload:
+done:
+ return err;
+}
+
+static int __exit m4sensorhub_remove(struct i2c_client *client)
+{
+ struct m4sensorhub_data *m4sensorhub = i2c_get_clientdata(client);
+ KDEBUG(M4SH_INFO, "Removing M4 Sensor Hub Driver\n");
+
+ m4sensorhub_irq_shutdown(m4sensorhub);
+ m4sensorhub_panic_shutdown(m4sensorhub);
+ m4sensorhub_reg_shutdown(m4sensorhub);
+ device_remove_file(&client->dev, &dev_attr_log_level);
+ device_remove_file(&client->dev, &dev_attr_debug_level);
+ minnow_m4sensorhub_hw_reset(m4sensorhub);
+ misc_deregister(&m4sensorhub_misc_device);
+ m4sensorhub->i2c_client = NULL;
+ i2c_set_clientdata(client, NULL);
+ minnow_m4sensorhub_hw_free(m4sensorhub);
+ kfree(m4sensorhub);
+ m4sensorhub = NULL;
+ m4sensorhub_misc_data = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int m4sensorhub_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ int err = 0;
+ KDEBUG(M4SH_INFO, "%s\n", __func__);
+ m4sensorhub_irq_pm_dbg_suspend();
+ return err;
+}
+
+static int m4sensorhub_resume(struct i2c_client *client)
+{
+
+ int err = 0;
+ KDEBUG(M4SH_INFO, "%s\n", __func__);
+ m4sensorhub_irq_pm_dbg_resume();
+ return err;
+}
+#endif /* CONFIG_PM */
+static const struct of_device_id of_m4sensorhub_match[] = {
+ { .compatible = "mot,m4sensorhub", },
+ {},
+};
+
+static const struct i2c_device_id m4sensorhub_id[] = {
+ {M4SENSORHUB_DRIVER_NAME, 0},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, m4sensorhub_id);
+
+static struct i2c_driver m4sensorhub_driver = {
+ .driver = {
+ .name = M4SENSORHUB_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(of_m4sensorhub_match),
+ },
+ .probe = m4sensorhub_probe,
+ .remove = __exit_p(m4sensorhub_remove),
+#ifdef CONFIG_PM
+ .suspend = m4sensorhub_suspend,
+ .resume = m4sensorhub_resume,
+#endif /* CONFIG_PM */
+ .id_table = m4sensorhub_id,
+};
+
+static int __init m4sensorhub_init(void)
+{
+ return i2c_add_driver(&m4sensorhub_driver);
+}
+
+static void __exit m4sensorhub_exit(void)
+{
+ i2c_del_driver(&m4sensorhub_driver);
+ return;
+}
+
+module_init(m4sensorhub_init);
+module_exit(m4sensorhub_exit);
+
+MODULE_ALIAS("platform:m4sensorhub");
+MODULE_DESCRIPTION("M4 Sensor Hub driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/m4sensorhub-irq.c b/drivers/mfd/m4sensorhub-irq.c
new file mode 100644
index 00000000000..56b9504fecd
--- /dev/null
+++ b/drivers/mfd/m4sensorhub-irq.c
@@ -0,0 +1,675 @@
+/*
+ * Copyright (C) 2012 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/wakelock.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+#include <linux/m4sensorhub.h>
+
+#ifdef CONFIG_PM_DEEPSLEEP
+#include <linux/suspend.h>
+#endif
+
+#define NUM_INT_REGS 2
+#define NUM_INTS_PER_REG 8
+#define NUM_INTS_LAST_REG (((M4SH_IRQ__NUM-1)%NUM_INTS_PER_REG)+1)
+#define INTR_VALID_BITS(n) (unsigned char)((1 << (n)) - 1)
+
+#define EVENT_MASK(event) (1 << ((event) % NUM_INTS_PER_REG))
+
+#define DBG_BUF_LINE_LEN 80
+
+/* --------------- Global Declarations -------------- */
+
+/* ------------ Local Function Prototypes ----------- */
+static int m4sensorhub_irq_disable_all(struct m4sensorhub_data *m4sensorhub);
+static unsigned short get_enable_reg(enum m4sensorhub_irqs event);
+static void irq_work_func(struct work_struct *work);
+#ifdef CONFIG_DEBUG_FS
+static int m4sensorhub_dbg_irq_open(struct inode *inode, struct file *file);
+#endif
+static void m4sensorhub_irq_restore(struct m4sensorhub_data *m4sensorhub,\
+ void *data);
+
+/* ---------------- Local Declarations -------------- */
+
+static const char *irq_name[] = {
+ [M4SH_IRQ_TMP_DATA_READY] = "TMP_DATA_READY",
+ [M4SH_IRQ_PRESSURE_DATA_READY] = "PRES_DATA_READY",
+ [M4SH_IRQ_GYRO_DATA_READY] = "GYRO_DATA_READY",
+ [M4SH_IRQ_PEDOMETER_DATA_READY] = "PEDO_DATA_READY",
+ [M4SH_IRQ_COMPASS_DATA_READY] = "COMPASS_DATA_READY",
+ [M4SH_IRQ_FUSION_DATA_READY] = "FUSION_DATA_READY",
+ [M4SH_IRQ_ACCEL_DATA_READY] = "ACCEL_DATA_READY",
+ [M4SH_IRQ_GESTURE_DETECTED] = "GESTURE_DETECTED",
+ [M4SH_IRQ_STILL_DETECTED] = "STILL_DETECTED",
+ [M4SH_IRQ_MOTION_DETECTED] = "MOTION_DETECTED",
+ [M4SH_IRQ_ACTIVITY_CHANGE] = "ACTIVITY_CHANGE",
+ [M4SH_IRQ_DLCMD_RESP_READY] = "DLCMD_RESP_READY",
+ [M4SH_IRQ_MIC_DATA_READY] = "MIC_DATA_READY",
+ [M4SH_IRQ_WRIST_READY] = "WRIST_READY",
+ [M4SH_IRQ_PASSIVE_BUFFER_FULL] = "PASSIVE_BUFFER_FULL",
+};
+
+/* -------------- Local Data Structures ------------- */
+
+struct m4sensorhub_event_handler {
+ void (*func)(enum m4sensorhub_irqs, void *);
+ void *data;
+};
+
+struct m4sensorhub_irq_info {
+ uint8_t registered;
+ uint8_t enabled;
+ uint32_t ena_fired;
+ uint32_t disa_fired;
+};
+
+struct m4sensorhub_irqdata {
+ struct mutex lock; /* lock event handlers and data */
+ struct work_struct work;
+ struct workqueue_struct *workqueue;
+ struct m4sensorhub_data *m4sensorhub;
+ struct m4sensorhub_event_handler event_handler[M4SH_IRQ__NUM];
+ struct m4sensorhub_irq_info irq_info[M4SH_IRQ__NUM];
+ struct wake_lock wake_lock;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *debugfs;
+#endif
+};
+
+
+static const struct {
+ enum m4sensorhub_reg status_reg;
+ enum m4sensorhub_reg enable_reg;
+ unsigned char valid_bits;
+} int_registers[NUM_INT_REGS] = {
+ {M4SH_REG_GENERAL_INTERRUPT0STATUS,
+ M4SH_REG_GENERAL_INTERRUPT0ENABLE,
+ INTR_VALID_BITS(NUM_INTS_PER_REG)},
+ {M4SH_REG_GENERAL_INTERRUPT1STATUS,
+ M4SH_REG_GENERAL_INTERRUPT1ENABLE,
+ INTR_VALID_BITS(NUM_INTS_LAST_REG)},
+};
+
+static irqreturn_t event_isr(int irq, void *data)
+{
+ /* Interrupts are left enabled; if multiple interrupts arrive, there
+ * will be multiple jobs in the workqueue. In this case, the first
+ * job in the workqueue may service multple interrupts and
+ * susbsequent jobs will have no interrupts left to service.
+ */
+ struct m4sensorhub_irqdata *irq_data = data;
+ wake_lock(&irq_data->wake_lock);
+ queue_work(irq_data->workqueue, &irq_data->work);
+
+ return IRQ_HANDLED;
+}
+
+static struct mrsensorhub_irq_dbg {
+ unsigned short en_ints[NUM_INT_REGS];
+ unsigned char suspend;
+ unsigned char wakeup;
+} irq_dbg_info;
+
+#ifdef CONFIG_DEBUG_FS
+static const struct file_operations debug_fops = {
+ .open = m4sensorhub_dbg_irq_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+/* -------------- Global Functions ----------------- */
+
+/* m4sensorhub_irq_init()
+
+ Intialize M4 sensor hub IRQ subsystem
+
+ Returns 0 on success. Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+*/
+
+int m4sensorhub_irq_init(struct m4sensorhub_data *m4sensorhub)
+{
+ int retval;
+ struct i2c_client *i2c = m4sensorhub->i2c_client;
+ struct m4sensorhub_irqdata *data;
+
+ data = kzalloc(sizeof(struct m4sensorhub_irqdata), GFP_KERNEL);
+ if (!data) {
+ KDEBUG(M4SH_ERROR, "m4sensorhub: Memory error in irq_init\n");
+ retval = -ENOMEM;
+ goto done;
+ }
+
+ KDEBUG(M4SH_INFO, "m4sensorhub: %u IRQs with valid_bits %02X%02X\n",\
+ M4SH_IRQ__NUM, int_registers[1].valid_bits,\
+ int_registers[0].valid_bits);
+ retval = m4sensorhub_irq_disable_all(m4sensorhub);
+ if (retval) {
+ KDEBUG(M4SH_ERROR, "m4sensorhub: Failed disable all irqs\n");
+ goto err_free;
+ }
+
+ data->workqueue = create_workqueue("m4sensorhub_irq");
+ if (data->workqueue == NULL) {
+ KDEBUG(M4SH_ERROR, "m4sensorhub: IRQ Workqueue init failure\n");
+ retval = -ENOMEM;
+ goto err_free;
+ }
+ INIT_WORK(&data->work, irq_work_func);
+
+ mutex_init(&data->lock);
+
+ wake_lock_init(&data->wake_lock, WAKE_LOCK_SUSPEND, "m4sensorhub-irq");
+
+ retval = request_irq(i2c->irq, event_isr, IRQF_DISABLED |
+ IRQF_TRIGGER_RISING, "m4sensorhub-irq", data);
+ if (retval) {
+ KDEBUG(M4SH_ERROR, "m4sensorhub: Failed requesting irq.\n");
+ goto err_destroy_wq;
+ }
+
+ data->m4sensorhub = m4sensorhub;
+ m4sensorhub->irqdata = data;
+
+ retval = enable_irq_wake(i2c->irq);
+ if (retval) {
+ KDEBUG(M4SH_ERROR, "m4sensorhub: Failed enabling irq wake.\n");
+ goto err_free_irq;
+ }
+
+#ifdef CONFIG_DEBUG_FS
+ data->debugfs = debugfs_create_file("m4sensorhub-irq", S_IRUGO, NULL,
+ data, &debug_fops);
+ if (data->debugfs == NULL) {
+ KDEBUG(M4SH_ERROR, "m4sensorhub: Error creating debufs\n");
+ retval = -EINVAL;
+ goto err_disa_irq;
+ }
+#endif
+ m4sensorhub_panic_register(m4sensorhub, PANICHDL_IRQ_RESTORE,\
+ m4sensorhub_irq_restore, data);
+ KDEBUG(M4SH_INFO, "m4sensorhub IRQ subsystem initialized\n");
+ retval = 0;
+ goto done;
+
+err_disa_irq:
+ disable_irq_wake(i2c->irq);
+err_free_irq:
+ free_irq(i2c->irq, data);
+ m4sensorhub->irqdata = NULL;
+ data->m4sensorhub = NULL;
+err_destroy_wq:
+ wake_lock_destroy(&data->wake_lock);
+ mutex_destroy(&data->lock);
+ destroy_workqueue(data->workqueue);
+err_free:
+ kfree(data);
+done:
+ return retval;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_irq_init);
+
+/* m4sensorhub_irq_shutdown()
+
+ Shutdown the M4 sensor hub IRQ subsystem
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+*/
+void m4sensorhub_irq_shutdown(struct m4sensorhub_data *m4sensorhub)
+{
+ struct i2c_client *i2c = m4sensorhub->i2c_client;
+ struct m4sensorhub_irqdata *data = m4sensorhub->irqdata;
+
+ KDEBUG(M4SH_INFO, "shutdown m4sensorhub IRQ subsystem\n");
+
+ m4sensorhub_panic_unregister(m4sensorhub, PANICHDL_IRQ_RESTORE);
+
+#ifdef CONFIG_DEBUG_FS
+ debugfs_remove(data->debugfs);
+#endif
+
+ disable_irq_wake(i2c->irq);
+ free_irq(i2c->irq, data);
+
+ m4sensorhub->irqdata = NULL;
+ data->m4sensorhub = NULL;
+
+ if (wake_lock_active(&data->wake_lock))
+ wake_unlock(&data->wake_lock);
+ wake_lock_destroy(&data->wake_lock);
+
+ if (mutex_is_locked(&data->lock))
+ mutex_unlock(&data->lock);
+ mutex_destroy(&data->lock);
+
+ cancel_work_sync(&data->work);
+ destroy_workqueue(data->workqueue);
+
+ kfree(data);
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_irq_shutdown);
+
+/* m4sensorhub_irq_register()
+
+ Register an interupt handler in the M4 Sensor Hub IRQ subsystem.
+ This does not enable the IRQ, that needs to be done by caller
+ with m4sensorhub_irq_enable()
+
+ Returns 0 on success. Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ irq - M4 Sensor Hub interupt to resiter for
+ cb_func - IRQ handler function to execute on inturrupt
+ data - pointer to data for IRQ handler function
+*/
+
+int m4sensorhub_irq_register(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_irqs irq,
+ void (*cb_func) (enum m4sensorhub_irqs, void *),
+ void *data)
+{
+ struct m4sensorhub_irqdata *irqdata;
+ int retval = 0;
+
+ if ((!m4sensorhub) || (irq >= M4SH_IRQ__NUM) || (!cb_func))
+ return -EINVAL;
+
+ irqdata = m4sensorhub->irqdata;
+
+ mutex_lock(&irqdata->lock);
+
+ if (irqdata->event_handler[irq].func == NULL) {
+ irqdata->irq_info[irq].registered = 1;
+ irqdata->event_handler[irq].func = cb_func;
+ irqdata->event_handler[irq].data = data;
+ KDEBUG(M4SH_NOTICE, "m4sensorhub: %s IRQ registered\n",
+ irq_name[irq]);
+ } else {
+ KDEBUG(M4SH_ERROR, "m4sensorhub: %s IRQ registration failed\n",
+ irq_name[irq]);
+ retval = -EPERM;
+ }
+
+ mutex_unlock(&irqdata->lock);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_irq_register);
+
+/* m4sensorhub_irq_unregister()
+
+ Unregister an interupt handler in the M4 Sensor Hub IRQ subsystem
+
+ Returns 0 on success. Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ irq - M4 Sensor Hub interupt to unresiter for
+*/
+int m4sensorhub_irq_unregister(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_irqs irq)
+{
+ struct m4sensorhub_irqdata *data = m4sensorhub->irqdata;
+ int retval;
+
+ if (irq >= M4SH_IRQ__NUM)
+ return -EINVAL;
+
+ retval = m4sensorhub_irq_disable(m4sensorhub, irq);
+
+ mutex_lock(&data->lock);
+ data->event_handler[irq].func = NULL;
+ data->event_handler[irq].data = NULL;
+ data->irq_info[irq].registered = 0;
+ mutex_unlock(&data->lock);
+
+ KDEBUG(M4SH_NOTICE, "m4sensorhub: %s IRQ un-registered\n",
+ irq_name[irq]);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_irq_unregister);
+
+/* m4sensorhub_irq_enable_get()
+
+ Check if an IRQ is enabled
+
+ Returns 1 if enabled, 0 if disabled.
+ Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ irq - M4 Sensor Hub interupt to check
+*/
+
+int m4sensorhub_irq_enable_get(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_irqs irq)
+{
+ struct m4sensorhub_irqdata *data = m4sensorhub->irqdata;
+ int retval = -EINVAL;
+
+ if (irq < M4SH_IRQ__NUM)
+ return data->irq_info[irq].enabled;
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_irq_enable_get);
+
+/* m4sensorhub_irq_disable()
+
+ Disable M4 Sensor Hub subsystem IRQ
+
+ Returns 0 on success. Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ irq - M4 Sensor Hub interupt to disable
+*/
+
+int m4sensorhub_irq_disable(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_irqs irq)
+{
+ struct m4sensorhub_irqdata *data = m4sensorhub->irqdata;
+ int retval = -EINVAL;
+
+ if (irq < M4SH_IRQ__NUM) {
+ mutex_lock(&data->lock);
+ data->irq_info[irq].enabled = 0;
+ mutex_unlock(&data->lock);
+ retval = m4sensorhub_reg_write_1byte(m4sensorhub,
+ get_enable_reg(irq), 0, EVENT_MASK(irq));
+ retval = CHECK_REG_ACCESS_RETVAL(m4sensorhub, retval,
+ get_enable_reg(irq));
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_irq_disable);
+
+/* m4sensorhub_irq_enable()
+
+ Enable M4 Sensor Hub subsystem IRQ
+
+ Returns 0 on success. Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ irq - M4 Sensor Hub interupt to enable
+*/
+
+int m4sensorhub_irq_enable(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_irqs irq)
+{
+ struct m4sensorhub_irqdata *data = m4sensorhub->irqdata;
+ int retval = -EINVAL;
+
+ if (irq < M4SH_IRQ__NUM) {
+ mutex_lock(&data->lock);
+ data->irq_info[irq].enabled = 1;
+ mutex_unlock(&data->lock);
+ retval = m4sensorhub_reg_write_1byte(m4sensorhub,
+ get_enable_reg(irq), EVENT_MASK(irq),
+ EVENT_MASK(irq));
+ retval = CHECK_REG_ACCESS_RETVAL(m4sensorhub, retval,
+ get_enable_reg(irq));
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_irq_enable);
+
+/* m4sensorhub_irq_pm_suspend()
+
+ Called by core to track suspend state and wakeup cause
+
+*/
+
+void m4sensorhub_irq_pm_dbg_suspend(void)
+{
+ irq_dbg_info.suspend = 1;
+ irq_dbg_info.wakeup = 0;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_irq_pm_dbg_suspend);
+
+/* m4sensorhub_irq_pm_resume()
+
+ Called by core to print interupt source on M4SH wakeup
+
+*/
+
+void m4sensorhub_irq_pm_dbg_resume(void)
+{
+ char buffer[DBG_BUF_LINE_LEN];
+ int i;
+
+ irq_dbg_info.suspend = 0;
+ if ((irq_dbg_info.wakeup != 0) && (m4sensorhub_debug >= M4SH_NOTICE)) {
+ strcpy(buffer, "M4 Sensor Hub IRQ registers:");
+ for (i = 0; (i < NUM_INT_REGS) &&
+ (strlen(buffer) < DBG_BUF_LINE_LEN-5); ++i) {
+ sprintf(&buffer[strlen(buffer)], " 0x%02x",
+ irq_dbg_info.en_ints[i]);
+ }
+
+ KDEBUG(M4SH_NOTICE, "newbuf: %s\n", buffer);
+
+ /* Decode the bits */
+ KDEBUG(M4SH_NOTICE, "M4 Sensor Hub IRQ sources:\n");
+ for (i = 0; i < NUM_INT_REGS; ++i) {
+ unsigned char index;
+
+ while (irq_dbg_info.en_ints[i] > 0) {
+ /* find the first set bit */
+ index = (unsigned char)
+ (ffs(irq_dbg_info.en_ints[i]) - 1);
+ if (index >= M4SH_IRQ__NUM)
+ goto error;
+
+ /* clear the bit */
+ irq_dbg_info.en_ints[i] &= ~(1 << index);
+ /* find the event that occurred */
+ index += M4SH_IRQ__START +
+ (i * NUM_INTS_PER_REG);
+ if (index >= M4SH_IRQ__NUM)
+ goto error;
+
+ KDEBUG(M4SH_NOTICE, "\t%s\n", irq_name[index]);
+ }
+ }
+ }
+error:
+ return;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_irq_pm_dbg_resume);
+
+/* --------------- Local Functions ----------------- */
+
+static unsigned short get_enable_reg(enum m4sensorhub_irqs event)
+{
+ unsigned short ret;
+
+ if ((event) >= M4SH_IRQ__NUM)
+ ret = M4SH_REG__INVALID;
+ else if ((event) >= M4SH_IRQ_INT1_INDEX)
+ ret = M4SH_REG_GENERAL_INTERRUPT1ENABLE;
+ else if ((event) >= M4SH_IRQ_INT0_INDEX)
+ ret = M4SH_REG_GENERAL_INTERRUPT0ENABLE;
+ else
+ ret = M4SH_REG__INVALID;
+
+ return ret;
+}
+
+static int m4sensorhub_irq_disable_all(struct m4sensorhub_data *m4sensorhub)
+{
+ int i;
+
+ for (i = 0; i < NUM_INT_REGS; i++) {
+ if (1 != m4sensorhub_reg_write_1byte(m4sensorhub,
+ int_registers[i].enable_reg, 0,
+ int_registers[i].valid_bits)) {
+ KDEBUG(M4SH_ERROR, "m4sensorhub_irq: "
+ "Failed disabling INT%d\n", i);
+ return -EFAULT;
+ }
+ }
+ return 0;
+}
+
+static void irq_work_func(struct work_struct *work)
+{
+ unsigned short en_ints[NUM_INT_REGS] = { 0 };
+ int i;
+ struct m4sensorhub_irqdata *data;
+ struct m4sensorhub_data *m4sensorhub;
+ struct i2c_client *i2c;
+ unsigned char value, is_irq_set = 0;
+
+ data = container_of(work, struct m4sensorhub_irqdata, work);
+ m4sensorhub = data->m4sensorhub;
+ i2c = m4sensorhub->i2c_client;
+
+ for (i = 0; i < NUM_INT_REGS; ++i) {
+ /* M4 is expected to clear these bits when read */
+ if (1 != m4sensorhub_reg_read(m4sensorhub,
+ int_registers[i].status_reg, &value)) {
+ dev_err(&m4sensorhub->i2c_client->dev,
+ "Error reading INT%d\n", i);
+ goto error;
+ }
+ en_ints[i] = value;
+ is_irq_set |= value;
+ }
+
+ if (!is_irq_set) {
+ /* Got the checkpoint to check if M4 panicked */
+ m4sensorhub_panic_process(m4sensorhub);
+ goto error;
+ }
+
+ if ((irq_dbg_info.suspend != 0) && (irq_dbg_info.wakeup == 0)) {
+ for (i = 0; i < NUM_INT_REGS; ++i)
+ irq_dbg_info.en_ints[i] = en_ints[i];
+ irq_dbg_info.wakeup = 1;
+ }
+
+ for (i = 0; i < NUM_INT_REGS; ++i) {
+ unsigned char index;
+
+ while (en_ints[i] > 0) {
+ struct m4sensorhub_event_handler *event_handler;
+
+ /* find the first set bit */
+ index = (unsigned char)(ffs(en_ints[i]) - 1);
+ if (index >= M4SH_IRQ__NUM)
+ goto error;
+ /* clear the bit */
+ en_ints[i] &= ~(1 << index);
+ /* find the event that occurred */
+ index += M4SH_IRQ__START + (i * NUM_INTS_PER_REG);
+ if (index >= M4SH_IRQ__NUM)
+ goto error;
+
+ if (data->irq_info[index].enabled) {
+ event_handler = &data->event_handler[index];
+
+ if (event_handler && event_handler->func)
+ event_handler->func(index,
+ event_handler->data);
+
+ mutex_lock(&data->lock);
+ data->irq_info[index].ena_fired++;
+ mutex_unlock(&data->lock);
+ } else {
+ mutex_lock(&data->lock);
+ data->irq_info[index].disa_fired++;
+ mutex_unlock(&data->lock);
+ }
+ }
+ }
+error:
+ wake_unlock(&data->wake_lock);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int m4sensorhub_dbg_irq_show(struct seq_file *s, void *data)
+{
+ unsigned int i;
+ struct m4sensorhub_irqdata *irqdata = s->private;
+
+ seq_printf(s, "%21s%9s%12s%15s%16s\n",
+ "M4SENSORHUB IRQ", "Enabled", "Registered",
+ "Fired Enabled", "Fired Disabled");
+
+ for (i = 0; i < M4SH_IRQ__NUM; i++) {
+ seq_printf(s, "%21s%9d%12d%15d%16d\n",
+ irq_name[i],
+ irqdata->irq_info[i].enabled,
+ irqdata->irq_info[i].registered,
+ irqdata->irq_info[i].ena_fired,
+ irqdata->irq_info[i].disa_fired);
+ }
+ return 0;
+}
+
+static int m4sensorhub_dbg_irq_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, m4sensorhub_dbg_irq_show, inode->i_private);
+}
+#endif
+
+/* m4sensorhub_irq_restore()
+
+ Callback Handler is called by Panic after M4 has been restarted
+
+*/
+static void m4sensorhub_irq_restore(\
+ struct m4sensorhub_data *m4sensorhub, void *data)
+{
+ int i;
+ unsigned short en_ints[NUM_INT_REGS] = {0};
+
+ mutex_lock(&((struct m4sensorhub_irqdata *)data)->lock);
+ for (i = 0; i < M4SH_IRQ__NUM; i++) {
+ if (!((struct m4sensorhub_irqdata *)data)->irq_info[i].enabled)
+ continue;
+ en_ints[i/NUM_INTS_PER_REG] |= EVENT_MASK(i);
+ }
+ mutex_unlock(&((struct m4sensorhub_irqdata *)data)->lock);
+
+ for (i = 0; i < NUM_INT_REGS; i++) {
+ KDEBUG(M4SH_INFO, "m4sensorhub_irq: Reseting INT%d-%02X\n",\
+ i, en_ints[i]);
+ if (1 != m4sensorhub_reg_write_1byte(m4sensorhub,
+ int_registers[i].enable_reg, en_ints[i],
+ int_registers[i].valid_bits)) {
+ KDEBUG(M4SH_ERROR, "m4sensorhub_irq: "
+ "Failed reseting INT%d\n", i);
+ }
+ }
+}
diff --git a/drivers/mfd/m4sensorhub-panic.c b/drivers/mfd/m4sensorhub-panic.c
new file mode 100644
index 00000000000..aaf35f89886
--- /dev/null
+++ b/drivers/mfd/m4sensorhub-panic.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2012 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/m4sensorhub.h>
+#include <linux/slab.h>
+
+
+
+/* --------------- Global Declarations -------------- */
+#define PANIC_BANK 0xFF /* Reserved for Panic bank */
+#define PANIC_CMD_CHECK 0xCD /* Panic Handoff command */
+#define PANIC_RESP_CHECK 0xDeadBeef /* Panic Handoff Magic code */
+
+/* ------------ Local Function Prototypes ----------- */
+
+/* --------------- Local Declarations -------------- */
+static const char *callback_name[PANICHDL_MAX] = {
+ [PANICHDL_IRQ_RESTORE] = "irq_restore",
+};
+
+struct m4sensorhub_panic_callback {
+ void (*callback)(struct m4sensorhub_data *, void *);
+ void *data;
+};
+
+struct m4sensorhub_panicdata {
+ struct mutex lock; /* lock callback and data */
+ struct m4sensorhub_panic_callback funcs[PANICHDL_MAX];
+};
+
+union panic_buf {
+ struct _in {
+ unsigned char bank;
+ unsigned char cmd;
+ } in;
+ unsigned int data;
+};
+
+/* -------------- Local Data Structures ------------- */
+
+/* -------------- Global Functions ----------------- */
+
+/* m4sensorhub_panic_init()
+
+ Initialized panic private data structures.
+
+ Returns 0 on success or negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+*/
+int m4sensorhub_panic_init(struct m4sensorhub_data *m4sensorhub)
+{
+ int retval = 0;
+ struct m4sensorhub_panicdata *data;
+
+ data = kzalloc(sizeof(struct m4sensorhub_panicdata), GFP_KERNEL);
+ if (data) {
+ mutex_init(&data->lock);
+ m4sensorhub->panicdata = data;
+ } else {
+ KDEBUG(M4SH_ERROR, "m4sensorhub: Memory error in panic_init\n");
+ retval = -ENOMEM;
+ }
+ return retval;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_panic_init);
+
+/* m4sensorhub_panic_shutdown()
+
+ Shutdown the M4 sensor hub Panic subsystem
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+*/
+void m4sensorhub_panic_shutdown(struct m4sensorhub_data *m4sensorhub)
+{
+ if (m4sensorhub && m4sensorhub->panicdata) {
+ struct m4sensorhub_panicdata *data = m4sensorhub->panicdata;
+ m4sensorhub->panicdata = NULL;
+ if (mutex_is_locked(&data->lock))
+ mutex_unlock(&data->lock);
+ mutex_destroy(&data->lock);
+ kfree(data);
+ }
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_panic_shutdown);
+
+/* m4sensorhub_panic_register()
+
+ Register an panic handler to monitor M4 panic reset
+
+ Returns 0 on success or negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ index - M4 Sensor Hub panic handler to resiter for
+ cb_func - panic handler function to execute after M4 reset
+ data - pointer to data for panic handler function
+*/
+
+int m4sensorhub_panic_register(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_panichdl_index index,
+ void (*cb_func) (struct m4sensorhub_data *, void *),
+ void *data)
+{
+ struct m4sensorhub_panicdata *panicdata;
+ int retval = 0;
+
+ if (!m4sensorhub || (index >= PANICHDL_MAX) || !cb_func)
+ return -EINVAL;
+
+ panicdata = (struct m4sensorhub_panicdata *)m4sensorhub->panicdata;
+ mutex_lock(&panicdata->lock);
+ if (panicdata->funcs[index].callback == NULL) {
+ panicdata->funcs[index].callback = cb_func;
+ panicdata->funcs[index].data = data;
+ KDEBUG(M4SH_NOTICE, "m4sensorhub: %s callback registered\n",
+ callback_name[index]);
+ } else {
+ KDEBUG(M4SH_ERROR, "m4sensorhub: %s callback"\
+ " registration failed\n", callback_name[index]);
+ retval = -EPERM;
+ }
+ mutex_unlock(&panicdata->lock);
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_panic_register);
+
+/* m4sensorhub_panic_unregister()
+
+ Unregister an panic handler to monitor M4 panic reset
+
+ Returns 0 on success or negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ index - M4 Sensor Hub panic handler to unresiter for
+*/
+int m4sensorhub_panic_unregister(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_panichdl_index index)
+{
+ struct m4sensorhub_panicdata *panicdata;
+
+ if (!m4sensorhub || (index >= PANICHDL_MAX))
+ return -EINVAL;
+
+ panicdata = (struct m4sensorhub_panicdata *)m4sensorhub->panicdata;
+ mutex_lock(&panicdata->lock);
+ panicdata->funcs[index].callback = NULL;
+ panicdata->funcs[index].data = NULL;
+ mutex_unlock(&panicdata->lock);
+ KDEBUG(M4SH_NOTICE, "m4sensorhub: %s callback un-registered\n",
+ callback_name[index]);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_panic_unregister);
+
+
+/* m4sensorhub_panic_process()
+
+ Check M4 if it's panicked, use I2C to communicate with M4 panic handler
+ OMAP use the same i2c sequences to send command via i2c master, then M4
+ i2c slave program will handle these commands, it may have 2 slave programs
+ 1. Normal i2c slave program handles all vaild banks'(limit on
+ M4SH_TYPE__NUM) command, for invalid bank, it always responses 0xFF
+ 2. Panic i2c slave program handles panic bank(reserved 0xFF for it) command,
+ for others, it always responses 0x00
+
+ To detect whether M4 is panicked, the process should be
+ i. When OMAP got interrupt from M4, OMAP will check which irq is raised, it
+ send normal banks' command to M4, for panic case, it always returns 0x00,
+ so OMAP has a checkpoint as there's interrupt request from M4 without
+ active IRQ
+ ii.Then OMAP will confirm if M4 is panic via send panic bank command, if M4
+ is panicked, it will handle this bank and response panic magic code;
+ Otherwise, if no panic magic code returned from M4, it always means M4
+ isn't panicked.
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ */
+void m4sensorhub_panic_process(struct m4sensorhub_data *m4sensorhub)
+{
+ int i, ret;
+ union panic_buf buf;
+ struct m4sensorhub_panic_callback handler;
+
+ if (!m4sensorhub || !m4sensorhub->panicdata) {
+ KDEBUG(M4SH_ERROR, "m4sensorhub: Invalid parameter in %s!\n",\
+ __func__);
+ return;
+ }
+
+ m4sensorhub_reg_access_lock();
+
+ buf.in.bank = PANIC_BANK;
+ buf.in.cmd = PANIC_CMD_CHECK;
+ ret = m4sensorhub_i2c_write_read(m4sensorhub,\
+ (u8 *)&buf, sizeof(buf.in), sizeof(buf.data));
+ if ((ret != sizeof(buf.data)) || (buf.data != PANIC_RESP_CHECK)) {
+ /* TODO maybe we shall check if M4/OMAP i2c broken */
+ KDEBUG(M4SH_ERROR, "m4sensorhub: Unknown IRQ status! "\
+ "M4 panic handoff ret=%d, data=0x%x\n",\
+ ret, buf.data);
+ m4sensorhub_reg_access_unlock();
+ return;
+ }
+
+ KDEBUG(M4SH_ERROR, "m4sensorhub_panic: Detected M4 panic, reset M4!\n");
+ m4sensorhub->pdev->hw_reset(m4sensorhub);
+ msleep(100);
+ ret = m4sensorhub_load_firmware(m4sensorhub, 0);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "m4sensorhub_panic: "\
+ "Failed to restart M4, ret = %d\n", ret);
+ BUG();
+ }
+
+ m4sensorhub_reg_access_unlock();
+
+ for (i = 0; i < PANICHDL_MAX; i++) {
+ handler = ((struct m4sensorhub_panicdata *)\
+ (m4sensorhub->panicdata))->funcs[i];
+ if (handler.callback) {
+ KDEBUG(M4SH_NOTICE, "m4sensorhub_panic: "\
+ "Calling %s as M4 restarted!\n",\
+ callback_name[i]);
+ handler.callback(m4sensorhub, handler.data);
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_panic_process);
diff --git a/drivers/mfd/m4sensorhub-reg.c b/drivers/mfd/m4sensorhub-reg.c
new file mode 100644
index 00000000000..cb9e8b1b371
--- /dev/null
+++ b/drivers/mfd/m4sensorhub-reg.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2012 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/m4sensorhub.h>
+#include <linux/slab.h>
+
+#include "m4sensorhub-reg.h" /* auto-generated header defining registers */
+
+#define I2C_RETRY_DELAY 5
+#define I2C_RETRIES 5
+
+#define DEBUG_LINE_LENGTH 80
+
+/* --------------- Global Declarations -------------- */
+
+/* ------------ Local Function Prototypes ----------- */
+static int m4sensorhub_mapsize(enum m4sensorhub_reg reg);
+
+/* --------------- Local Declarations -------------- */
+static DEFINE_MUTEX(reg_access);
+
+/* -------------- Local Data Structures ------------- */
+
+/* -------------- Global Functions ----------------- */
+
+/* m4sensorhub_reg_init()
+
+ Initialized register access data structures.
+
+ Returns 0 on success or negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+*/
+
+int m4sensorhub_reg_init(struct m4sensorhub_data *m4sensorhub)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_reg_init);
+
+/* m4sensorhub_reg_shutdown()
+
+ Clean up register subsystem on driver removal
+
+ Returns 0 on success or negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+*/
+
+int m4sensorhub_reg_shutdown(struct m4sensorhub_data *m4sensorhub)
+{
+ return 0;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_reg_shutdown);
+
+/* m4sensorhub_reg_read_n()
+
+ Read a n bytes from the M4 sensor hub starting at 'register'. Use
+ m4sensorhub_reg_read() instead where possible;
+
+ Returns number of bytes read on success.
+ Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ reg - Register to be read
+ value - array to return data. Needs to be at least register's size
+ num - number of bytes to read
+*/
+
+int m4sensorhub_reg_read_n(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_reg reg, unsigned char *value,
+ short num)
+{
+ int ret = -EINVAL;
+ u8 stack_buf[M4SH_MAX_STACK_BUF_SIZE];
+
+ if (!m4sensorhub || !value || !num) {
+ KDEBUG(M4SH_ERROR, "%s() invalid parameter\n", __func__);
+ return ret;
+ }
+
+ if ((reg < M4SH_REG__NUM) && num <= M4SH_MAX_REG_SIZE &&\
+ register_info_tbl[reg].offset + num <=
+ m4sensorhub_mapsize(reg)) {
+ u8 *buf = (num > (M4SH_MAX_STACK_BUF_SIZE-2))\
+ ? kmalloc(num+2, GFP_KERNEL) : stack_buf;
+ if (!buf) {
+ KDEBUG(M4SH_ERROR, "%s() Failed alloc %d memeory\n"\
+ , __func__, num+2);
+ return -ENOMEM;
+ }
+ buf[0] = register_info_tbl[reg].type;
+ buf[1] = register_info_tbl[reg].offset;
+
+ mutex_lock(&reg_access);
+ ret = m4sensorhub_i2c_write_read(m4sensorhub, buf, 2, num);
+ mutex_unlock(&reg_access);
+
+ if (ret != num)
+ KDEBUG(M4SH_ERROR, "%s() read failure\n", __func__);
+ else
+ memcpy(value, buf, num);
+ if (buf != stack_buf)
+ kfree(buf);
+ } else {
+ KDEBUG(M4SH_ERROR, "%s() invalid register access reg=%d "
+ "maxreg=%d size=%d maxsze=%d mapsize=%d\n", __func__,
+ reg, M4SH_REG__NUM, num, M4SH_MAX_REG_SIZE,
+ m4sensorhub_mapsize(reg));
+ }
+ return ret;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_reg_read_n);
+
+/* m4sensorhub_reg_write()
+
+ Write data to a register in the M4 sensor hub. Use
+ m4sensorhub_reg_write() instead where possible;
+
+ Returns number of bytes written on success.
+ Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ reg - Register to be written to
+ value - array of data to write. Needs to be at least register's size
+ mask - mask representing which bits to change in register. If all bits
+ are to be changed, then &m4sh_no_mask can be passed here.
+ num - number of bytes to write
+*/
+
+int m4sensorhub_reg_write_n(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_reg reg, unsigned char *value,
+ unsigned char *mask, short num)
+{
+ int i, ret = -EINVAL;
+ u8 stack_buf[M4SH_MAX_STACK_BUF_SIZE];
+
+ if (!m4sensorhub || !value || !num) {
+ KDEBUG(M4SH_ERROR, "%s() invalid parameter\n", __func__);
+ return ret;
+ }
+
+ if ((reg < M4SH_REG__NUM) && num <= M4SH_MAX_REG_SIZE &&\
+ register_info_tbl[reg].offset + num <=
+ m4sensorhub_mapsize(reg)) {
+ u8 *buf = (num > (M4SH_MAX_STACK_BUF_SIZE-2))\
+ ? kmalloc(num+2, GFP_KERNEL) : stack_buf;
+ if (!buf) {
+ KDEBUG(M4SH_ERROR, "%s() Failed alloc %d memeory\n"\
+ , __func__, num+2);
+ return -ENOMEM;
+ }
+
+ buf[0] = register_info_tbl[reg].type;
+ buf[1] = register_info_tbl[reg].offset;
+
+ mutex_lock(&reg_access);
+ if (mask) {
+ ret = m4sensorhub_i2c_write_read(m4sensorhub, buf,
+ 2, num);
+ if (ret != num) {
+ KDEBUG(M4SH_ERROR, "%s() register read"
+ "failure\n", __func__);
+ goto error;
+ }
+ /* move data right 2 positions and apply mask and
+ new data to prepare for writeback */
+ for (i = num-1; i >= 0; i--) {
+ buf[i+2] = (buf[i] & ~mask[i]) |
+ (value[i] & mask[i]);
+ }
+ buf[0] = register_info_tbl[reg].type;
+ buf[1] = register_info_tbl[reg].offset;
+ } else
+ memcpy(&buf[2], value, num);
+
+ ret = m4sensorhub_i2c_write_read(m4sensorhub, buf,
+ num + 2, 0);
+ if (ret != num + 2) {
+ KDEBUG(M4SH_ERROR, "%s() register write failure\n",
+ __func__);
+ ret = -EINVAL;
+ } else
+ ret -= 2;
+
+error: mutex_unlock(&reg_access);
+ if (buf != stack_buf)
+ kfree(buf);
+ } else {
+ KDEBUG(M4SH_ERROR, "%s() invalid register access reg=%d"
+ " maxreg=%d num=%d maxsze=%d mapsize=%d\n", __func__,
+ reg, M4SH_REG__NUM, num, M4SH_MAX_REG_SIZE,
+ m4sensorhub_mapsize(reg));
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_reg_write_n);
+
+/* m4sensorhub_reg_write_1byte()
+
+ Write data to a 1 byte register in the M4 sensor hub. Avoids need to pass
+ data and mask by reference
+
+ Returns number of bytes written on success.
+ Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ reg - Register to be written to
+ value - byte of data to write
+ mask - mask representing which bits to change in register.
+*/
+
+int m4sensorhub_reg_write_1byte(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_reg reg, unsigned char value,
+ unsigned char mask)
+{
+ if (register_info_tbl[reg].size == 1)
+ return m4sensorhub_reg_write(m4sensorhub, reg, &value, &mask);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_reg_write_1byte);
+
+/* m4sensorhub_reg_getsize()
+
+ Get the size of an M4 register
+
+ Returns size of register on success
+ Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ reg - Register to get size of
+*/
+
+int m4sensorhub_reg_getsize(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_reg reg)
+{
+ if (reg < M4SH_REG__NUM)
+ return register_info_tbl[reg].size;
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_reg_getsize);
+
+/* m4sensorhub_reg_access_lock()
+
+ Lock reg access to avoid broken I2C transmit process
+
+*/
+
+void m4sensorhub_reg_access_lock(void)
+{
+ mutex_lock(&reg_access);
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_reg_access_lock);
+
+/* m4sensorhub_reg_access_unlock()
+
+ Unlock reg access to wake up blocked I2C transmit process
+
+*/
+
+void m4sensorhub_reg_access_unlock(void)
+{
+ mutex_unlock(&reg_access);
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_reg_access_unlock);
+
+/* m4sensorhub_i2c_write_read()
+
+ Directly I2C access to communicate with the M4 sensor hub.
+ It always read after write if both write and read length are non-zero
+
+ Returns number of bytes write on success if readlen is zero
+ Returns number of bytes read on success if readlen is non-zero
+ Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ buf - buffer to be used for write to and read from
+ writelen - number of bytes write to
+ readlen - number of bytes read from
+*/
+
+int m4sensorhub_i2c_write_read(struct m4sensorhub_data *m4sensorhub,
+ u8 *buf, int writelen, int readlen)
+{
+ int i, msglen, msgstart, err, tries = 0;
+ char buffer[DEBUG_LINE_LENGTH];
+ struct i2c_msg msgs[] = {
+ {
+ .addr = m4sensorhub->i2c_client->addr,
+ .flags = m4sensorhub->i2c_client->flags,
+ .len = writelen,
+ .buf = buf,
+ },
+ {
+ .addr = m4sensorhub->i2c_client->addr,
+ .flags = m4sensorhub->i2c_client->flags | I2C_M_RD,
+ .len = readlen,
+ .buf = buf,
+ },
+ };
+
+ if (buf == NULL || (writelen == 0 && readlen == 0))
+ return -EFAULT;
+
+ /* Offset and size in msgs array depending on msg type */
+ msglen = (writelen && readlen) ? 2 : 1;
+ msgstart = writelen ? 0 : 1;
+
+ if (m4sensorhub_debug >= M4SH_VERBOSE_DEBUG && writelen) {
+ sprintf(buffer, "Writing to M4:");
+ for (i = 0; i < writelen; i++) {
+ if (strlen(buffer) >= DEBUG_LINE_LENGTH-5) {
+ KDEBUG(M4SH_VERBOSE_DEBUG, "%s\n", buffer);
+ buffer[0] = '\0';
+ }
+ sprintf(&buffer[strlen(buffer)], " 0x%02x", buf[i]);
+ }
+ KDEBUG(M4SH_VERBOSE_DEBUG, "%s\n", buffer);
+ }
+
+ do {
+ err = i2c_transfer(m4sensorhub->i2c_client->adapter,
+ &msgs[msgstart], msglen);
+ if (err != msglen)
+ msleep_interruptible(I2C_RETRY_DELAY);
+ } while ((err != msglen) && (++tries < I2C_RETRIES));
+ if (err != msglen) {
+ dev_err(&m4sensorhub->i2c_client->dev, "i2c transfer error; "
+ "type=%d offset=%d\n", buf[0], buf[1]);
+ err = -EIO;
+ } else {
+ err = (readlen ? readlen : writelen);
+
+ if (m4sensorhub_debug >= M4SH_VERBOSE_DEBUG && readlen) {
+ sprintf(buffer, "Read from M4:");
+ for (i = 0; i < readlen; i++) {
+ if (strlen(buffer) >= DEBUG_LINE_LENGTH-5) {
+ KDEBUG(M4SH_VERBOSE_DEBUG, "%s\n",
+ buffer);
+ buffer[0] = '\0';
+ }
+ sprintf(&buffer[strlen(buffer)], " 0x%02x",
+ buf[i]);
+ }
+ KDEBUG(M4SH_VERBOSE_DEBUG, "%s\n", buffer);
+ }
+ }
+ return err;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_i2c_write_read);
+
+/* -------------- Local Functions ----------------- */
+
+static int m4sensorhub_mapsize(enum m4sensorhub_reg reg)
+{
+ int retval = -EINVAL;
+
+ if (reg < M4SH_REG__NUM)
+ retval = bank_size_tbl[register_info_tbl[reg].type];
+
+ return retval;
+}
diff --git a/drivers/mfd/m4sensorhub-reg.h b/drivers/mfd/m4sensorhub-reg.h
new file mode 100644
index 00000000000..087726c064e
--- /dev/null
+++ b/drivers/mfd/m4sensorhub-reg.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2013, Motorola, Inc. All Rights Reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/***************************** WARNING ******************************
+ * *
+ * DO NOT EDIT THIS FILE *
+ * *
+ * This is an auto-generated file based on register maps in the *
+ * M4 Sensor Hub source tree. *
+ * *
+ ***************************** WARNING *****************************/
+
+#ifndef __M4SENSORHUB_MEM_INTERNAL_H__
+#define __M4SENSORHUB_MEM_INTERNAL_H__
+static const struct {
+ enum m4sensorhub_type type; /* Type of register */
+ unsigned short offset; /* Offset into type's memory */
+ unsigned short size; /* Size of register in bytes */
+} register_info_tbl[M4SH_REG__NUM] = {
+ [M4SH_REG_ACCEL_VERSION] = {M4SH_TYPE_ACCEL, 0x0, 1},
+ [M4SH_REG_ACCEL_DUMMY] = {M4SH_TYPE_ACCEL, 0x1, 1},
+ [M4SH_REG_ACCEL_SAMPLERATE] = {M4SH_TYPE_ACCEL, 0x2, 2},
+ [M4SH_REG_ACCEL_X] = {M4SH_TYPE_ACCEL, 0x4, 4},
+ [M4SH_REG_ACCEL_Y] = {M4SH_TYPE_ACCEL, 0x8, 4},
+ [M4SH_REG_ACCEL_Z] = {M4SH_TYPE_ACCEL, 0xc, 4},
+ [M4SH_REG_ACCEL_SCALEDMAGSQUARED] = {M4SH_TYPE_ACCEL, 0x10, 4},
+ [M4SH_REG_ACCEL_MAGNITUDE] = {M4SH_TYPE_ACCEL, 0x14, 4},
+ [M4SH_REG_ACCEL_TILT] = {M4SH_TYPE_ACCEL, 0x18, 2},
+ [M4SH_REG_ACCEL_ORIENTATION] = {M4SH_TYPE_ACCEL, 0x1a, 2},
+ [M4SH_REG_TEMP_VERSION] = {M4SH_TYPE_TEMP, 0x0, 1},
+ [M4SH_REG_TEMP_DUMMY] = {M4SH_TYPE_TEMP, 0x1, 1},
+ [M4SH_REG_TEMP_SAMPLERATE] = {M4SH_TYPE_TEMP, 0x2, 2},
+ [M4SH_REG_TEMP_EXTRNLTEMP] = {M4SH_TYPE_TEMP, 0x4, 2},
+ [M4SH_REG_TEMP_INTRNLTEMP] = {M4SH_TYPE_TEMP, 0x6, 2},
+ [M4SH_REG_GENERAL_UTC] = {M4SH_TYPE_GENERAL, 0x0, 4},
+ [M4SH_REG_GENERAL_LOCALTIMEZONE] = {M4SH_TYPE_GENERAL, 0x4, 2},
+ [M4SH_REG_GENERAL_VERSION] = {M4SH_TYPE_GENERAL, 0x6, 1},
+ [M4SH_REG_GENERAL_INTERRUPT0ENABLE] = {M4SH_TYPE_GENERAL, 0x7, 1},
+ [M4SH_REG_GENERAL_INTERRUPT1ENABLE] = {M4SH_TYPE_GENERAL, 0x8, 1},
+ [M4SH_REG_GENERAL_INTERRUPT0STATUS] = {M4SH_TYPE_GENERAL, 0x9, 1},
+ [M4SH_REG_GENERAL_INTERRUPT1STATUS] = {M4SH_TYPE_GENERAL, 0xa, 1},
+ [M4SH_REG_PRESSURE_VERSION] = {M4SH_TYPE_PRESSURE, 0x0, 1},
+ [M4SH_REG_PRESSURE_DUMMY] = {M4SH_TYPE_PRESSURE, 0x1, 1},
+ [M4SH_REG_PRESSURE_SAMPLERATE] = {M4SH_TYPE_PRESSURE, 0x2, 2},
+ [M4SH_REG_PRESSURE_PRESSURE] = {M4SH_TYPE_PRESSURE, 0x4, 4},
+ [M4SH_REG_PRESSURE_REFERENCEALTITUDE] = {M4SH_TYPE_PRESSURE, 0x8, 4},
+ [M4SH_REG_PRESSURE_SEALEVELPRESSURE] = {M4SH_TYPE_PRESSURE, 0xc, 4},
+ [M4SH_REG_PRESSURE_ABSOLUTEALTITUDE] = {M4SH_TYPE_PRESSURE, 0x10, 4},
+ [M4SH_REG_PRESSURE_TEMPERATURE] = {M4SH_TYPE_PRESSURE, 0x14, 2},
+ [M4SH_REG_PRESSURE_ISVALID] = {M4SH_TYPE_PRESSURE, 0x16, 1},
+ [M4SH_REG_PEDOMETER_VERSION] = {M4SH_TYPE_PEDOMETER, 0x0, 1},
+ [M4SH_REG_PEDOMETER_TESTCMD] = {M4SH_TYPE_PEDOMETER, 0x1, 1},
+ [M4SH_REG_PEDOMETER_ACTIVITY] = {M4SH_TYPE_PEDOMETER, 0x2, 1},
+ [M4SH_REG_PEDOMETER_EQUIPMENTTYPE] = {M4SH_TYPE_PEDOMETER, 0x3, 1},
+ [M4SH_REG_PEDOMETER_TOTALSTEPS] = {M4SH_TYPE_PEDOMETER, 0x4, 2},
+ [M4SH_REG_PEDOMETER_FLOORSCLIMBED] = {M4SH_TYPE_PEDOMETER, 0x6, 2},
+ [M4SH_REG_PEDOMETER_TOTATDISTANCE] = {M4SH_TYPE_PEDOMETER, 0x8, 4},
+ [M4SH_REG_PEDOMETER_CURRENTSPEED] = {M4SH_TYPE_PEDOMETER, 0xc, 4},
+ [M4SH_REG_PEDOMETER_REPORTEDDISTANCE] = {M4SH_TYPE_PEDOMETER, 0x10, 4},
+ [M4SH_REG_PEDOMETER_USERDISTANCE] = {M4SH_TYPE_PEDOMETER, 0x14, 4},
+ [M4SH_REG_TCMD_OPCODE] = {M4SH_TYPE_TCMD, 0x0, 1},
+ [M4SH_REG_LOG_LOGENABLE] = {M4SH_TYPE_LOG, 0x0, 8},
+ [M4SH_REG_LOG_ISLOGIMMEDIATE] = {M4SH_TYPE_LOG, 0x8, 1},
+ [M4SH_REG_FUSION_VERSION] = {M4SH_TYPE_FUSION, 0x0, 1},
+ [M4SH_REG_FUSION_DUMMY] = {M4SH_TYPE_FUSION, 0x1, 1},
+ [M4SH_REG_FUSION_SAMPLERATE] = {M4SH_TYPE_FUSION, 0x2, 2},
+ [M4SH_REG_FUSION_EULERPITCH] = {M4SH_TYPE_FUSION, 0x4, 4},
+ [M4SH_REG_FUSION_EULERROLL] = {M4SH_TYPE_FUSION, 0x8, 4},
+ [M4SH_REG_FUSION_EULERYAW] = {M4SH_TYPE_FUSION, 0xc, 4},
+ [M4SH_REG_FUSION_LOCALX] = {M4SH_TYPE_FUSION, 0x10, 4},
+ [M4SH_REG_FUSION_LOCALY] = {M4SH_TYPE_FUSION, 0x14, 4},
+ [M4SH_REG_FUSION_LOCALZ] = {M4SH_TYPE_FUSION, 0x18, 4},
+ [M4SH_REG_FUSION_WORLDX] = {M4SH_TYPE_FUSION, 0x1c, 4},
+ [M4SH_REG_FUSION_WORLDY] = {M4SH_TYPE_FUSION, 0x20, 4},
+ [M4SH_REG_FUSION_WORLDZ] = {M4SH_TYPE_FUSION, 0x24, 4},
+ [M4SH_REG_FUSION_HEADING] = {M4SH_TYPE_FUSION, 0x28, 2},
+ [M4SH_REG_FUSION_HEADING_ACCURACY] = {M4SH_TYPE_FUSION, 0x2a, 1},
+ [M4SH_REG_COMPASS_VERSION] = {M4SH_TYPE_COMPASS, 0x0, 1},
+ [M4SH_REG_COMPASS_DUMMY] = {M4SH_TYPE_COMPASS, 0x1, 1},
+ [M4SH_REG_COMPASS_SAMPLERATE] = {M4SH_TYPE_COMPASS, 0x2, 2},
+ [M4SH_REG_COMPASS_X] = {M4SH_TYPE_COMPASS, 0x4, 4},
+ [M4SH_REG_COMPASS_Y] = {M4SH_TYPE_COMPASS, 0x8, 4},
+ [M4SH_REG_COMPASS_Z] = {M4SH_TYPE_COMPASS, 0xc, 4},
+ [M4SH_REG_COMPASS_ACCURACY] = {M4SH_TYPE_COMPASS, 0x10, 1},
+ [M4SH_REG_GYRO_VERSION] = {M4SH_TYPE_GYRO, 0x0, 1},
+ [M4SH_REG_GYRO_DUMMY] = {M4SH_TYPE_GYRO, 0x1, 1},
+ [M4SH_REG_GYRO_SAMPLERATE] = {M4SH_TYPE_GYRO, 0x2, 2},
+ [M4SH_REG_GYRO_X] = {M4SH_TYPE_GYRO, 0x4, 4},
+ [M4SH_REG_GYRO_Y] = {M4SH_TYPE_GYRO, 0x8, 4},
+ [M4SH_REG_GYRO_Z] = {M4SH_TYPE_GYRO, 0xc, 4},
+ [M4SH_REG_METS_VERSION] = {M4SH_TYPE_METS, 0x0, 1},
+ [M4SH_REG_METS_METSACTIVITY] = {M4SH_TYPE_METS, 0x1, 1},
+ [M4SH_REG_METS_MSSAMPLETIME] = {M4SH_TYPE_METS, 0x2, 2},
+ [M4SH_REG_METS_METS] = {M4SH_TYPE_METS, 0x4, 4},
+ [M4SH_REG_METS_CALORIES] = {M4SH_TYPE_METS, 0x8, 4},
+ [M4SH_REG_USERSETTINGS_VERSION] = {M4SH_TYPE_USERSETTINGS, 0x0, 1},
+ [M4SH_REG_USERSETTINGS_USERAGE] = {M4SH_TYPE_USERSETTINGS, 0x1, 1},
+ [M4SH_REG_USERSETTINGS_USERGENDER] = {M4SH_TYPE_USERSETTINGS, 0x2, 1},
+ [M4SH_REG_USERSETTINGS_USERHEIGHT] = {M4SH_TYPE_USERSETTINGS, 0x3, 1},
+ [M4SH_REG_USERSETTINGS_USERWEIGHT] = {M4SH_TYPE_USERSETTINGS, 0x4, 2},
+ [M4SH_REG_USERSETTINGS_SCREENSTATUS] = {M4SH_TYPE_USERSETTINGS, 0x6, 1},
+ [M4SH_REG_USERSETTINGS_RTCRESET] = {M4SH_TYPE_USERSETTINGS, 0x7, 1},
+ [M4SH_REG_POWER_VERSION] = {M4SH_TYPE_POWER, 0x0, 1},
+ [M4SH_REG_POWER_DUMMY] = {M4SH_TYPE_POWER, 0x1, 1},
+ [M4SH_REG_POWER_STILLMODETIMEOUT] = {M4SH_TYPE_POWER, 0x2, 2},
+ [M4SH_REG_POWER_MOTIONDURATION] = {M4SH_TYPE_POWER, 0x4, 1},
+ [M4SH_REG_POWER_MOTIONTHRESHOLD] = {M4SH_TYPE_POWER, 0x5, 1},
+ [M4SH_REG_POWER_NOMOTIONDURATION] = {M4SH_TYPE_POWER, 0x6, 1},
+ [M4SH_REG_POWER_NOMOTIONTHRESHOLD] = {M4SH_TYPE_POWER, 0x7, 1},
+ [M4SH_REG_LOCATION_VERSION] = {M4SH_TYPE_LOCATION, 0x0, 1},
+ [M4SH_REG_LOCATION_SOURCE] = {M4SH_TYPE_LOCATION, 0x1, 1},
+ [M4SH_REG_LOCATION_SPEED] = {M4SH_TYPE_LOCATION, 0x2, 2},
+ [M4SH_REG_LOCATION_LATITUDE] = {M4SH_TYPE_LOCATION, 0x4, 4},
+ [M4SH_REG_LOCATION_LONGITUDE] = {M4SH_TYPE_LOCATION, 0x8, 4},
+ [M4SH_REG_LOCATION_ALTITUDE] = {M4SH_TYPE_LOCATION, 0xc, 2},
+ [M4SH_REG_DOWNLOAD_COMMAND] = {M4SH_TYPE_DOWNLOAD, 0x0, 1},
+ [M4SH_REG_DOWNLOAD_STATUS] = {M4SH_TYPE_DOWNLOAD, 0x1, 1},
+ [M4SH_REG_DOWNLOAD_SIZE] = {M4SH_TYPE_DOWNLOAD, 0x2, 2},
+ [M4SH_REG_DOWNLOAD_CHECKSUM] = {M4SH_TYPE_DOWNLOAD, 0x4, 4},
+ [M4SH_REG_DOWNLOAD_FILENAME] = {M4SH_TYPE_DOWNLOAD, 0x8, 16},
+ [M4SH_REG_DOWNLOAD_PACKET] = {M4SH_TYPE_DOWNLOAD, 0x18, 2048},
+ [M4SH_REG_AUDIO_VERSION] = {M4SH_TYPE_AUDIO, 0x0, 1},
+ [M4SH_REG_AUDIO_ENABLE] = {M4SH_TYPE_AUDIO, 0x1, 1},
+ [M4SH_REG_AUDIO_DUMMY] = {M4SH_TYPE_AUDIO, 0x2, 2},
+ [M4SH_REG_AUDIO_TOTALPACKETS] = {M4SH_TYPE_AUDIO, 0x4, 4},
+ [M4SH_REG_TIMEPIECE_VERSION] = {M4SH_TYPE_TIMEPIECE, 0x0, 1},
+ [M4SH_REG_TIMEPIECE_ENABLE] = {M4SH_TYPE_TIMEPIECE, 0x1, 1},
+ [M4SH_REG_TIMEPIECE_SAMPLERATE] = {M4SH_TYPE_TIMEPIECE, 0x2, 2},
+ [M4SH_REG_TIMEPIECE_OFFSETSTEPS] = {M4SH_TYPE_TIMEPIECE, 0x4, 4},
+ [M4SH_REG_WRIST_VERSION] = {M4SH_TYPE_WRIST, 0x0, 1},
+ [M4SH_REG_WRIST_ENABLE] = {M4SH_TYPE_WRIST, 0x1, 1},
+ [M4SH_REG_WRIST_INTERRUPTREASON] = {M4SH_TYPE_WRIST, 0x2, 1},
+ [M4SH_REG_WRIST_HOSTRESPONSE] = {M4SH_TYPE_WRIST, 0x3, 1},
+ [M4SH_REG_WRIST_FMONCHIP] = {M4SH_TYPE_WRIST, 0x4, 2},
+ [M4SH_REG_WRIST_FMONFILE] = {M4SH_TYPE_WRIST, 0x6, 2},
+ [M4SH_REG_GESTURE_VERSION] = {M4SH_TYPE_GESTURE, 0x0, 1},
+ [M4SH_REG_GESTURE_GESTURE1] = {M4SH_TYPE_GESTURE, 0x1, 1},
+ [M4SH_REG_GESTURE_CONFIDENCE1] = {M4SH_TYPE_GESTURE, 0x2, 1},
+ [M4SH_REG_GESTURE_VALUE1] = {M4SH_TYPE_GESTURE, 0x3, 1},
+ [M4SH_REG_GESTURE_GESTURE2] = {M4SH_TYPE_GESTURE, 0x4, 1},
+ [M4SH_REG_GESTURE_CONFIDENCE2] = {M4SH_TYPE_GESTURE, 0x5, 1},
+ [M4SH_REG_GESTURE_VALUE2] = {M4SH_TYPE_GESTURE, 0x6, 1},
+ [M4SH_REG_GESTURE_GESTURE3] = {M4SH_TYPE_GESTURE, 0x7, 1},
+ [M4SH_REG_GESTURE_CONFIDENCE3] = {M4SH_TYPE_GESTURE, 0x8, 1},
+ [M4SH_REG_GESTURE_VALUE3] = {M4SH_TYPE_GESTURE, 0x9, 1},
+ [M4SH_REG_PASSIVE_TIMESTAMP] = {M4SH_TYPE_PASSIVE, 0x0, 48},
+ [M4SH_REG_PASSIVE_STEPS] = {M4SH_TYPE_PASSIVE, 0x30, 48},
+ [M4SH_REG_PASSIVE_METS] = {M4SH_TYPE_PASSIVE, 0x60, 48},
+ [M4SH_REG_PASSIVE_FLOORSCLIMBED] = {M4SH_TYPE_PASSIVE, 0x90, 48},
+};
+
+static const unsigned int bank_size_tbl[M4SH_TYPE__NUM] = {
+ [M4SH_TYPE_ACCEL] = 28,
+ [M4SH_TYPE_TEMP] = 8,
+ [M4SH_TYPE_GENERAL] = 11,
+ [M4SH_TYPE_PRESSURE] = 23,
+ [M4SH_TYPE_PEDOMETER] = 24,
+ [M4SH_TYPE_TCMD] = 1,
+ [M4SH_TYPE_LOG] = 9,
+ [M4SH_TYPE_FUSION] = 43,
+ [M4SH_TYPE_COMPASS] = 17,
+ [M4SH_TYPE_GYRO] = 16,
+ [M4SH_TYPE_METS] = 12,
+ [M4SH_TYPE_USERSETTINGS] = 8,
+ [M4SH_TYPE_POWER] = 8,
+ [M4SH_TYPE_LOCATION] = 14,
+ [M4SH_TYPE_DOWNLOAD] = 2072,
+ [M4SH_TYPE_AUDIO] = 8,
+ [M4SH_TYPE_TIMEPIECE] = 8,
+ [M4SH_TYPE_WRIST] = 8,
+ [M4SH_TYPE_GESTURE] = 10,
+ [M4SH_TYPE_PASSIVE] = 192,
+};
+#endif /*__M4SENSORHUB_MEM_INTERNAL_H__ */
diff --git a/drivers/mfd/m4sensorhub-stm32-fw.c b/drivers/mfd/m4sensorhub-stm32-fw.c
new file mode 100644
index 00000000000..55aca3ebb05
--- /dev/null
+++ b/drivers/mfd/m4sensorhub-stm32-fw.c
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2012 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/m4sensorhub.h>
+#include <linux/slab.h>
+
+/* --------------- Global Declarations -------------- */
+
+/* ------------ Local Function Prototypes ----------- */
+static int m4sensorhub_jump_to_user(struct m4sensorhub_data *m4sensorhub);
+
+/* --------------- Local Declarations -------------- */
+/* Firmware */
+#define FIRMWARE_NAME "m4sensorhub.bin"
+/* The M4 Flash Memory Map
+ * From the Flash Programming Manual:
+ Sector Start Address Size comments
+ ------ ------------- ---------- ----------------------------
+ 0 0x08000000 16 Kbytes reserved for M4 bootload
+ 1 0x08004000 16 Kbytes first M4 firmware code page
+ 2 0x08008000 16 Kbytes
+ 3 0x0800C000 16 Kbytes
+ 4 0x08010000 64 Kbytes
+ 5 0x08020000 128 Kbytes last M4 firmware code page
+ 6 0x08040000 128 Kbytes first M4 file system page
+ 7 0x08060000 128 Kbytes
+ 8 0x08080000 128 Kbytes
+ 9 0x080A0000 128 Kbytes
+ 10 0x080C0000 128 Kbytes
+ 11 0x080E0000 128 Kbytes last M4 file system page
+ */
+enum {
+ M4_FLASH_SECTOR0 = 0x08000000,
+ M4_FLASH_SECTOR1 = 0x08004000,
+ M4_FLASH_SECTOR2 = 0x08008000,
+ M4_FLASH_SECTOR3 = 0x0800C000,
+ M4_FLASH_SECTOR4 = 0x08010000,
+ M4_FLASH_SECTOR5 = 0x08020000,
+ M4_FLASH_SECTOR6 = 0x08040000,
+ M4_FLASH_SECTOR7 = 0x08060000,
+ M4_FLASH_SECTOR8 = 0x08080000,
+ M4_FLASH_SECTOR9 = 0x080A0000,
+ M4_FLASH_SECTORA = 0x080C0000,
+ M4_FLASH_SECTORB = 0x080E0000,
+ M4_FLASH_END = 0x08100000
+};
+#define USER_FLASH_FIRST_PAGE_ADDRESS M4_FLASH_SECTOR1
+#define USER_FLASH_FIRST_FILE_ADDRESS M4_FLASH_SECTOR6
+#define VERSION_OFFSET 0x200
+#define VERSION_ADDRESS (USER_FLASH_FIRST_PAGE_ADDRESS + VERSION_OFFSET)
+#define BARKER_SIZE 4
+#define BARKER_ADDRESS (USER_FLASH_FIRST_FILE_ADDRESS - BARKER_SIZE)
+#define BARKER_NUMBER 0xACEC0DE
+/* The MAX_FILE_SIZE is the size of sectors 1-5 where the firmware code
+ * will reside (minus the barker size).
+ */
+#define MAX_FILE_SIZE (USER_FLASH_FIRST_FILE_ADDRESS \
+ - USER_FLASH_FIRST_PAGE_ADDRESS \
+ - BARKER_SIZE) /* bytes */
+#define MAX_TRANSFER_SIZE 1024 /* bytes */
+#define MAX_RETRIES 5
+#define OPC_READ (uint8_t)(0x03)
+#define OPC_WREN (uint8_t)(0x06)
+#define OPC_ERPG (uint8_t)(0x20)
+#define OPC_ERUSM (uint8_t)(0x60)
+#define OPC_USRCD (uint8_t)(0x77)
+
+/* -------------- Local Data Structures ------------- */
+#define NUM_FLASH_TO_ERASE 5
+int flash_address[NUM_FLASH_TO_ERASE] = {
+ M4_FLASH_SECTOR1,
+ M4_FLASH_SECTOR2,
+ M4_FLASH_SECTOR3,
+ M4_FLASH_SECTOR4,
+ M4_FLASH_SECTOR5
+};
+int flash_delay[NUM_FLASH_TO_ERASE] = {
+ 440, 440, 440, 1320, 4000
+};
+
+/* -------------- Global Functions ----------------- */
+
+/* m4sensorhub_load_firmware()
+
+ Check firmware and load if different from what's already on the M4.
+ Then jump to user code on M4.
+
+ Returns 0 on success or negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+*/
+
+int m4sensorhub_load_firmware(struct m4sensorhub_data *m4sensorhub,
+ unsigned short force_upgrade)
+{
+ const struct firmware *firmware;
+ int i = MAX_RETRIES;
+ int ret = 0;
+ int bytes_left, bytes_to_write;
+ int address_to_write;
+ u8 *buf_to_read, *buf = NULL;
+ u16 fw_version_file, fw_version_device;
+ u32 barker_read_from_device;
+ int j = 0;
+
+ buf = kzalloc(MAX_TRANSFER_SIZE+8, GFP_KERNEL);
+ if (!buf) {
+ ret = -ENOMEM;
+ goto done;
+ }
+
+ ret = request_firmware(&firmware, FIRMWARE_NAME,
+ &m4sensorhub->i2c_client->dev);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "%s: request_firmware failed for %s\n",
+ __func__, FIRMWARE_NAME);
+ KDEBUG(M4SH_ERROR, "Trying to run firmware already on hw.\n");
+ ret = 0;
+ goto done;
+ }
+
+ if (firmware->size > MAX_FILE_SIZE) {
+ KDEBUG(M4SH_ERROR, "%s: firmware file size is too big.\n",
+ __func__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ fw_version_file = *(u16 *) (firmware->data +
+ VERSION_ADDRESS - USER_FLASH_FIRST_PAGE_ADDRESS);
+
+ if (!force_upgrade) {
+ /* Verify Barker number from device */
+ buf[0] = OPC_READ;
+ buf[1] = (BARKER_ADDRESS >> 24) & 0xFF;
+ buf[2] = (BARKER_ADDRESS >> 16) & 0xFF;
+ buf[3] = (BARKER_ADDRESS >> 8) & 0xFF;
+ buf[4] = BARKER_ADDRESS & 0xFF;
+ buf[5] = 0x00;
+ buf[6] = 0x04;
+ if (m4sensorhub_i2c_write_read(m4sensorhub,
+ buf, 7, 0) < 0) {
+ KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ msleep(100);
+
+ if (m4sensorhub_i2c_write_read(m4sensorhub,
+ (u8 *) &barker_read_from_device, 0, BARKER_SIZE) < 0) {
+ KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (barker_read_from_device != BARKER_NUMBER) {
+ KDEBUG(M4SH_NOTICE,
+ "Barker Number read 0x%8x does not match 0x%8x\n",
+ barker_read_from_device, BARKER_NUMBER);
+ KDEBUG(M4SH_NOTICE,
+ "forcing firmware update from file\n");
+ } else {
+
+ /* Read firmware version from device */
+ buf[0] = OPC_READ;
+ buf[1] = (VERSION_ADDRESS >> 24) & 0xFF;
+ buf[2] = (VERSION_ADDRESS >> 16) & 0xFF;
+ buf[3] = (VERSION_ADDRESS >> 8) & 0xFF;
+ buf[4] = VERSION_ADDRESS & 0xFF;
+ buf[5] = 0x00;
+ buf[6] = 0x02;
+ if (m4sensorhub_i2c_write_read(
+ m4sensorhub, buf, 7, 0) < 0) {
+ KDEBUG(M4SH_ERROR,
+ "%s : %d : I2C transfer error\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ msleep(100);
+
+ if (m4sensorhub_i2c_write_read(m4sensorhub,
+ (u8 *) &fw_version_device, 0, 2) < 0) {
+ KDEBUG(M4SH_ERROR,
+ "%s : %d : I2C transfer error\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (fw_version_file == fw_version_device) {
+ KDEBUG(M4SH_NOTICE,
+ "Version of firmware on device is 0x%04x\n",
+ fw_version_device);
+ KDEBUG(M4SH_NOTICE,
+ "Firmware on device same as file, not loading firmware.\n");
+ goto done;
+ }
+ /* Print statement below isn't really an ERROR, but
+ * this ensures it is always printed */
+ KDEBUG(M4SH_ERROR,
+ "Version of firmware on device is 0x%04x\n",
+ fw_version_device);
+ KDEBUG(M4SH_ERROR,
+ "Version of firmware on file is 0x%04x\n",
+ fw_version_file);
+ KDEBUG(M4SH_ERROR,
+ "Firmware on device different from file, updating...\n");
+ }
+ } else {
+ KDEBUG(M4SH_NOTICE, "Version of firmware on file is 0x%04x\n",
+ fw_version_file);
+ }
+
+ /* The flash memory to update has to be erased before updating */
+ for (j = 0; j < NUM_FLASH_TO_ERASE; j++) {
+ buf[0] = OPC_ERPG;
+ buf[1] = (flash_address[j] >> 24) & 0xFF;
+ buf[2] = (flash_address[j] >> 16) & 0xFF;
+ buf[3] = (flash_address[j] >> 8) & 0xFF;
+ buf[4] = flash_address[j] & 0xFF;
+ buf[5] = 0x00;
+ buf[6] = 0x01;
+ if (m4sensorhub_i2c_write_read(m4sensorhub, buf, 7, 0) < 0) {
+ KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n",
+ __func__, __LINE__);
+ KDEBUG(M4SH_ERROR, "Erasing %8x address failed\n",
+ flash_address[j]);
+ ret = -EINVAL;
+ goto done;
+ }
+ msleep(flash_delay[j]);
+ }
+
+ bytes_left = firmware->size;
+ address_to_write = USER_FLASH_FIRST_PAGE_ADDRESS;
+ buf_to_read = (u8 *) firmware->data;
+
+ KDEBUG(M4SH_DEBUG, "%s: %d bytes to be written\n", __func__,
+ firmware->size);
+
+ while (bytes_left && i) {
+ if (bytes_left > MAX_TRANSFER_SIZE)
+ bytes_to_write = MAX_TRANSFER_SIZE;
+ else
+ bytes_to_write = bytes_left;
+
+ buf[0] = OPC_WREN;
+ buf[1] = (address_to_write >> 24) & 0xFF;
+ buf[2] = (address_to_write >> 16) & 0xFF;
+ buf[3] = (address_to_write >> 8) & 0xFF;
+ buf[4] = address_to_write & 0xFF;
+ buf[5] = (bytes_to_write >> 8) & 0xFF;
+ buf[6] = bytes_to_write & 0xFF;
+ buf[7] = 0xFF;
+ memcpy(&buf[8], buf_to_read, bytes_to_write);
+ if (m4sensorhub_i2c_write_read(m4sensorhub, buf,
+ bytes_to_write+8, 0) < 0) {
+ KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ msleep(20);
+
+ /* Read back the code that was written and validate it */
+ buf[0] = OPC_READ;
+ buf[1] = (address_to_write >> 24) & 0xFF;
+ buf[2] = (address_to_write >> 16) & 0xFF;
+ buf[3] = (address_to_write >> 8) & 0xFF;
+ buf[4] = address_to_write & 0xFF;
+ buf[5] = (bytes_to_write >> 8) & 0xFF;
+ buf[6] = bytes_to_write & 0xFF;
+ if (m4sensorhub_i2c_write_read(
+ m4sensorhub, buf, 7, 0) < 0) {
+ KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (m4sensorhub_i2c_write_read(m4sensorhub, buf, 0,
+ bytes_to_write) < 0) {
+ KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if (memcmp(buf, buf_to_read, bytes_to_write) != 0) {
+ /* If memory write fails, try again */
+ KDEBUG(M4SH_ERROR,
+ "memory write to 0x%x of %d bytes failed, try again\n",
+ address_to_write, bytes_to_write);
+ i--;
+ } else {
+ address_to_write += bytes_to_write;
+ buf_to_read += bytes_to_write;
+ bytes_left -= bytes_to_write;
+ /* Reset reter of retries */
+ i = MAX_RETRIES;
+ }
+ }
+
+ if (!i) {
+ KDEBUG(M4SH_ERROR, "%s: firmware transfer failed\n", __func__);
+ ret = -EINVAL;
+ } else {
+ /* Write barker number when firmware successfully written */
+ buf[0] = OPC_WREN;
+ buf[1] = (BARKER_ADDRESS >> 24) & 0xFF;
+ buf[2] = (BARKER_ADDRESS >> 16) & 0xFF;
+ buf[3] = (BARKER_ADDRESS >> 8) & 0xFF;
+ buf[4] = BARKER_ADDRESS & 0xFF;
+ buf[5] = 0x00;
+ buf[6] = 0x04;
+ buf[7] = 0xFF;
+ buf[8] = BARKER_NUMBER & 0xFF;
+ buf[9] = (BARKER_NUMBER >> 8) & 0xFF;
+ buf[10] = (BARKER_NUMBER >> 16) & 0xFF;
+ buf[11] = (BARKER_NUMBER >> 24) & 0xFF;
+ if (m4sensorhub_i2c_write_read(m4sensorhub,
+ buf, 12, 0) < 0) {
+ KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n",
+ __func__, __LINE__);
+ ret = -EINVAL;
+ goto done;
+ }
+
+ KDEBUG(M4SH_NOTICE, "%s: %d bytes written successfully\n",
+ __func__, firmware->size);
+ }
+
+done:
+ release_firmware(firmware);
+
+ /* If ret is invalid, then we don't try to jump to user code */
+ if (ret >= 0 && m4sensorhub_jump_to_user(m4sensorhub) < 0)
+ /* If jump to user code fails, return failure */
+ ret = -EINVAL;
+
+ kfree(buf);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_load_firmware);
+
+
+/* -------------- Local Functions ----------------- */
+
+static int m4sensorhub_jump_to_user(struct m4sensorhub_data *m4sensorhub)
+{
+ int ret = -1;
+ u8 buf[7] = {0};
+ u32 barker_read_from_device = 0;
+
+ buf[0] = OPC_READ;
+ buf[1] = (BARKER_ADDRESS >> 24) & 0xFF;
+ buf[2] = (BARKER_ADDRESS >> 16) & 0xFF;
+ buf[3] = (BARKER_ADDRESS >> 8) & 0xFF;
+ buf[4] = BARKER_ADDRESS & 0xFF;
+ buf[5] = 0x00;
+ buf[6] = 0x04;
+ if (m4sensorhub_i2c_write_read(m4sensorhub, buf, 7, 0) < 0) {
+ KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
+ msleep(100);
+
+ if (m4sensorhub_i2c_write_read(m4sensorhub,
+ (u8 *) &barker_read_from_device, 0, BARKER_SIZE) < 0) {
+ KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n",
+ __func__, __LINE__);
+ return ret;
+ }
+
+ if (barker_read_from_device == BARKER_NUMBER) {
+ buf[0] = OPC_USRCD;
+ if (m4sensorhub_i2c_write_read(m4sensorhub, buf, 1, 0) < 0) {
+ KDEBUG(M4SH_ERROR, "%s : %d : I2C transfer error\n",
+ __func__, __LINE__);
+ return ret;
+ }
+ KDEBUG(M4SH_NOTICE, "Executing M4 code \n");
+ msleep(5000); /* 5 secs delay */
+ ret = 0;
+ } else {
+ KDEBUG(M4SH_ERROR,
+ "Barker Number read 0x%8x does not match 0x%8x\n",
+ barker_read_from_device, BARKER_NUMBER);
+ KDEBUG(M4SH_ERROR, "*** Not executing M4 code ***\n");
+ }
+
+ return ret;
+}
diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c
index aeb8e40ab42..dfd832d0aef 100644
--- a/drivers/mfd/tps65912-core.c
+++ b/drivers/mfd/tps65912-core.c
@@ -156,9 +156,17 @@ int tps65912_device_init(struct tps65912 *tps65912)
if (ret < 0)
goto err;
+#ifdef CONFIG_MFD_TPS65912_DEBUGFS
+ ret = tps65912_debugfs_create(tps65912);
+ if (ret < 0)
+ goto err_debugfs;
+#endif
+
kfree(init_data);
return ret;
+err_debugfs:
+ tps65912_irq_exit(tps65912);
err:
kfree(init_data);
mfd_remove_devices(tps65912->dev);
@@ -168,6 +176,9 @@ err:
void tps65912_device_exit(struct tps65912 *tps65912)
{
+#ifdef CONFIG_MFD_TPS65912_DEBUGFS
+ tps65912_debugfs_remove(tps65912);
+#endif
mfd_remove_devices(tps65912->dev);
tps65912_irq_exit(tps65912);
kfree(tps65912);
diff --git a/drivers/mfd/tps65912-debugfs.c b/drivers/mfd/tps65912-debugfs.c
new file mode 100644
index 00000000000..2a7d870aa8e
--- /dev/null
+++ b/drivers/mfd/tps65912-debugfs.c
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2013 Motorola Mobility LLC
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/mfd/tps65912.h>
+
+
+struct tps65912_debugfs_reg_descr {
+ char *name; /* Debugfs file name */
+ u8 reg; /* Register address */
+};
+
+/* Registers exposed in debugfs */
+static const struct tps65912_debugfs_reg_descr debugfs_regs[] = {
+ { "DCDC1_CTRL", 0x00 },
+ { "DCDC2_CTRL", 0x01 },
+ { "DCDC3_CTRL", 0x02 },
+ { "DCDC4_CTRL", 0x03 },
+ { "DCDC1_OP", 0x04 },
+ { "DCDC1_AVS", 0x05 },
+ { "DCDC1_LIMIT", 0x06 },
+ { "DCDC2_OP", 0x07 },
+ { "DCDC2_AVS", 0x08 },
+ { "DCDC2_LIMIT", 0x09 },
+ { "DCDC3_OP", 0x0A },
+ { "DCDC3_AVS", 0x0B },
+ { "DCDC3_LIMIT", 0x0C },
+ { "DCDC4_OP", 0x0D },
+ { "DCDC4_AVS", 0x0E },
+ { "DCDC4_LIMIT", 0x0F },
+ { "LDO1_OP", 0x10 },
+ { "LDO1_AVS", 0x11 },
+ { "LDO1_LIMIT", 0x12 },
+ { "LDO2_OP", 0x13 },
+ { "LDO2_AVS", 0x14 },
+ { "LDO2_LIMIT", 0x15 },
+ { "LDO3_OP", 0x16 },
+ { "LDO3_AVS", 0x17 },
+ { "LDO3_LIMIT", 0x18 },
+ { "LDO4_OP", 0x19 },
+ { "LDO4_AVS", 0x1A },
+ { "LDO4_LIMIT", 0x1B },
+ { "LDO5", 0x1C },
+ { "LDO6", 0x1D },
+ { "LDO7", 0x1E },
+ { "LDO8", 0x1F },
+ { "LDO9", 0x20 },
+ { "LDO10", 0x21 },
+ { "THRM", 0x22 },
+ { "CLK32OUT", 0x23 },
+ { "DEVCTRL", 0x24 },
+ { "DEVCTRL2", 0x25 },
+ { "I2C_SPI_CFG", 0x26 },
+ { "KEEP_ON", 0x27 },
+ { "KEEP_ON2", 0x28 },
+ { "SET_OFF1", 0x29 },
+ { "SET_OFF2", 0x2A },
+ { "DEF_VOLT", 0x2B },
+ { "DEF_VOLT_MAPPING", 0x2C },
+ { "DISCHARGE", 0x2D },
+ { "DISCHARGE2", 0x2E },
+ { "EN1_SET1", 0x2F },
+ { "EN1_SET2", 0x30 },
+ { "EN2_SET1", 0x31 },
+ { "EN2_SET2", 0x32 },
+ { "EN3_SET1", 0x33 },
+ { "EN3_SET2", 0x34 },
+ { "EN4_SET1", 0x35 },
+ { "EN4_SET2", 0x36 },
+ { "PGOOD", 0x37 },
+ { "PGOOD2", 0x38 },
+ { "INT_STS", 0x39 },
+ { "INT_MSK", 0x3A },
+ { "INT_STS2", 0x3B },
+ { "INT_MSK2", 0x3C },
+ { "INT_STS3", 0x3D },
+ { "INT_MSK3", 0x3E },
+ { "INT_STS4", 0x3F },
+ { "INT_MSK4", 0x40 },
+ { "GPIO1", 0x41 },
+ { "GPIO2", 0x42 },
+ { "GPIO3", 0x43 },
+ { "GPIO4", 0x44 },
+ { "GPIO5", 0x45 },
+ { "VMON", 0x46 },
+ { "LEDA_CTRL1", 0x47 },
+ { "LEDA_CTRL2", 0x48 },
+ { "LEDA_CTRL3", 0x49 },
+ { "LEDA_CTRL4", 0x4A },
+ { "LEDA_CTRL5", 0x4B },
+ { "LEDA_CTRL6", 0x4C },
+ { "LEDA_CTRL7", 0x4D },
+ { "LEDA_CTRL8", 0x4E },
+ { "LEDB_CTRL1", 0x4F },
+ { "LEDB_CTRL2", 0x50 },
+ { "LEDB_CTRL3", 0x51 },
+ { "LEDB_CTRL4", 0x52 },
+ { "LEDB_CTRL5", 0x53 },
+ { "LEDB_CTRL6", 0x54 },
+ { "LEDB_CTRL7", 0x55 },
+ { "LEDB_CTRL8", 0x56 },
+ { "LEDC_CTRL1", 0x57 },
+ { "LEDC_CTRL2", 0x58 },
+ { "LEDC_CTRL3", 0x59 },
+ { "LEDC_CTRL4", 0x5A },
+ { "LEDC_CTRL5", 0x5B },
+ { "LEDC_CTRL6", 0x5C },
+ { "LEDC_CTRL7", 0x5D },
+ { "LEDC_CTRL8", 0x5E },
+ { "LED_RAMP_UP_TIME", 0x5F },
+ { "LED_RAMP_DOWN_TIME", 0x60 },
+ { "LED_SEQ_EN", 0x61 },
+ { "LOADSWITCH", 0x62 },
+ { "SPARE", 0x63 },
+ { "VERNUM", 0x64 },
+};
+
+struct tps65912_debugfs_reg_data {
+ struct tps65912 *tps65912; /* Device */
+ u8 reg; /* Register address */
+};
+
+struct tps65912_debugfs_data {
+ struct dentry *root; /* Debugfs directory of the device */
+ struct tps65912_debugfs_reg_data reg_data[ARRAY_SIZE(debugfs_regs)];
+};
+
+static int reg_read(void *data, u64 *val)
+{
+ struct tps65912_debugfs_reg_data *reg_data;
+
+ reg_data = (struct tps65912_debugfs_reg_data *) data;
+ *val = tps65912_reg_read(reg_data->tps65912, reg_data->reg);
+ return 0;
+}
+
+static int reg_write(void *data, u64 val)
+{
+ struct tps65912_debugfs_reg_data *reg_data;
+
+ reg_data = (struct tps65912_debugfs_reg_data *) data;
+ return tps65912_reg_write(reg_data->tps65912, reg_data->reg, val);
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(reg_fops, reg_read, reg_write, "0x%02llx\n");
+
+int tps65912_debugfs_create(struct tps65912 *tps65912)
+{
+ int i;
+ struct tps65912_debugfs_data *debugfs_data;
+
+ debugfs_data = kzalloc(sizeof(struct tps65912_debugfs_data),
+ GFP_KERNEL);
+ if (!debugfs_data)
+ goto err_data;
+
+ debugfs_data->root = debugfs_create_dir(dev_name(tps65912->dev), NULL);
+ if (!debugfs_data->root)
+ goto err_root;
+
+ for (i = 0; i < ARRAY_SIZE(debugfs_regs); i++) {
+ debugfs_data->reg_data[i].tps65912 = tps65912;
+ debugfs_data->reg_data[i].reg = debugfs_regs[i].reg;
+ if (!debugfs_create_file(debugfs_regs[i].name,
+ S_IRUGO | S_IWUSR, debugfs_data->root,
+ debugfs_data->reg_data + i, &reg_fops))
+ goto err_file;
+ }
+
+ tps65912->debugfs_data = debugfs_data;
+ return 0;
+
+err_file:
+ debugfs_remove_recursive(debugfs_data->root);
+err_root:
+ kfree(debugfs_data);
+err_data:
+ tps65912->debugfs_data = NULL;
+ return -ENOMEM;
+}
+
+void tps65912_debugfs_remove(struct tps65912 *tps65912)
+{
+ struct tps65912_debugfs_data *debugfs_data;
+
+ debugfs_data = (struct tps65912_debugfs_data *) tps65912->debugfs_data;
+ if (debugfs_data) {
+ debugfs_remove_recursive(debugfs_data->root);
+ kfree(debugfs_data);
+ }
+}
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 3f5743424ff..18c65fcca01 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -333,6 +333,10 @@ config ISL29020
This driver can also be built as a module. If so, the module
will be called isl29020.
+config VIB_GPIO
+ bool "GPIO Vibrator"
+ default n
+
config SENSORS_TSL2550
tristate "Taos TSL2550 ambient light sensor"
depends on I2C && SYSFS
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index a57666cec34..9f72ecb358d 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -54,3 +54,15 @@ obj-$(CONFIG_INTEL_MEI) += mei/
obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/
obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o
obj-$(CONFIG_SRAM) += sram.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_tmp006.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_bmp180.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_mpu9150.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_pedometer.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_gesture.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_stillmode.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_download.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_display.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_audio.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_wrist.o
+obj-$(CONFIG_MFD_M4SENSORHUB) += m4sensorhub_passive.o
+obj-$(CONFIG_VIB_GPIO) += vib-gpio.o
diff --git a/drivers/misc/m4sensorhub_audio.c b/drivers/misc/m4sensorhub_audio.c
new file mode 100644
index 00000000000..511e2776bf8
--- /dev/null
+++ b/drivers/misc/m4sensorhub_audio.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (C) 2013 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Adds ability to program periodic interrupts from user space that
+ * can wake the phone out of low power modes.
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/sound.h>
+#include <linux/soundcard.h>
+#include <linux/types.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+#include <linux/m4sensorhub/MemMapAudio.h>
+#include <linux/spi/spi.h>
+
+
+#define AUDIO_CLIENT_DRIVER_NAME "m4sensorhub_audio"
+/* This is the number of total kernel buffers */
+#define AUDIO_NBFRAGS_READ 20
+#define AUDIO_SAMPLE_RATE 16000
+#define AUDIO_TIMEOUT HZ
+#define MIC_ENABLE 0x01
+#define MIC_DISABLE 0x00
+
+/* Mutex used to prevent mutiple calls to audio functions at same time */
+DEFINE_MUTEX(audio_lock);
+
+struct audio_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct spi_device *spi;
+ int dev_dsp;
+ int dev_dsp_open_count;
+ char *buffers[AUDIO_NBFRAGS_READ];
+ unsigned int usr_head; /* user index where app is reading from */
+ unsigned int buf_head; /* SPI index where SPI writing to */
+ unsigned int usr_offset; /* offset in usr_head buffer to read */
+ int read_buf_full; /* num buffers available for app */
+ u32 total_buf_cnt; /* total num of bufs read from since audio enable*/
+ wait_queue_head_t wq; /* wait till read buffer is available */
+ int active; /* Indicates if audio transfer is active */
+};
+
+struct audio_client *audio_data;
+
+static ssize_t audio_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long long loglevel;
+ struct spi_device *spi = to_spi_device(dev);
+ struct audio_client *audio_client_data = spi_get_drvdata(spi);
+
+ m4sensorhub_reg_read(audio_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, AUDIO_MASK_BIT_1);
+ return sprintf(buf, "%llu\n", loglevel);
+}
+static ssize_t audio_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ unsigned long long mask = 0, newlevel;
+ struct spi_device *spi = to_spi_device(dev);
+ struct audio_client *audio_client_data = spi_get_drvdata(spi);
+
+ if (strict_strtoul(buf, 10, &level) < 0)
+ return -1;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -1;
+ }
+ mask = (1ULL << AUDIO_MASK_BIT_1) | (1ULL << AUDIO_MASK_BIT_2);
+ newlevel = (unsigned long long)level << AUDIO_MASK_BIT_1;
+ return m4sensorhub_reg_write(audio_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&newlevel,
+ (unsigned char *)&mask);
+}
+static DEVICE_ATTR(LogLevel, 0664, audio_get_loglevel, audio_set_loglevel);
+
+static void audio_client_spidma_read(struct audio_client *audio_client_data,
+ int len)
+{
+ int ret = 0;
+ struct spi_message msg;
+ struct spi_transfer rx;
+ unsigned char txbuff[AUDIO_BUFFER_SIZE];
+
+ memset(&rx, 0x00, sizeof(struct spi_transfer));
+ memset(&msg, 0x00, sizeof(struct spi_message));
+
+ rx.rx_buf = audio_client_data->buffers[
+ audio_client_data->buf_head];
+ rx.tx_buf = txbuff;
+ rx.len = len;
+
+ spi_message_init(&msg);
+ spi_message_add_tail(&rx, &msg);
+
+ ret = spi_sync(audio_client_data->spi, &msg);
+ if (ret < 0)
+ KDEBUG(M4SH_ERROR, "%s failed to read %d bytes, ret = %d\n",
+ __func__, len, ret);
+ else {
+ audio_data->read_buf_full++;
+ audio_data->total_buf_cnt++;
+ wake_up_interruptible(&audio_data->wq);
+
+ if (++audio_client_data->buf_head >= AUDIO_NBFRAGS_READ)
+ audio_client_data->buf_head = 0;
+ }
+
+}
+
+static void m4_handle_audio_irq(enum m4sensorhub_irqs int_event,
+ void *data)
+{
+ u32 m4_buf_cnt = 0;
+ u32 bufs_to_read = 0;
+ struct audio_client *audio_client_data = (struct audio_client *)data;
+ int ret = 0;
+
+ mutex_lock(&audio_lock);
+
+ /* Read the total buf count from M4 */
+ ret = m4sensorhub_reg_read(audio_client_data->m4sensorhub,
+ M4SH_REG_AUDIO_TOTALPACKETS,
+ (char *)&m4_buf_cnt);
+
+ if (ret != m4sensorhub_reg_getsize(audio_client_data->m4sensorhub,
+ M4SH_REG_AUDIO_TOTALPACKETS)) {
+ KDEBUG(M4SH_ERROR, "M4 packet count read failed %d\n", ret);
+ goto EXIT;
+ }
+
+ bufs_to_read = m4_buf_cnt - audio_data->total_buf_cnt;
+ KDEBUG(M4SH_DEBUG, "R = %u, m4_cnt = %u, omap_cnt = %u\n",
+ bufs_to_read, m4_buf_cnt, audio_data->total_buf_cnt);
+
+ /* If no free buffers, then skip reads from SPI */
+ while ((bufs_to_read) &&
+ (audio_data->read_buf_full < AUDIO_NBFRAGS_READ)) {
+ audio_client_spidma_read(audio_client_data, AUDIO_BUFFER_SIZE);
+ bufs_to_read--;
+ }
+
+EXIT:
+ mutex_unlock(&audio_lock);
+}
+
+static int audio_client_open(struct inode *inode, struct file *file)
+{
+ int ret = 0, i = 0;
+ mutex_lock(&audio_lock);
+
+ if (audio_data->dev_dsp_open_count == 1) {
+ KDEBUG(M4SH_ERROR, "Mic already opened, can't open again\n");
+ ret = -EBUSY;
+ goto out;
+ }
+
+ for (i = 0; i < AUDIO_NBFRAGS_READ; i++) {
+ audio_data->buffers[i] = kmalloc(AUDIO_BUFFER_SIZE,
+ GFP_KERNEL | GFP_DMA);
+ if (!audio_data->buffers[i]) {
+ KDEBUG(M4SH_ERROR, "Can't allocate memory for mic\n");
+ ret = -ENOMEM;
+ goto free_buffers;
+ }
+ }
+
+ audio_data->active = 0;
+ audio_data->usr_head = 0;
+ audio_data->usr_offset = 0;
+ audio_data->buf_head = 0;
+ audio_data->read_buf_full = 0;
+
+ ret = m4sensorhub_irq_enable(audio_data->m4sensorhub,
+ M4SH_IRQ_MIC_DATA_READY);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Unable to enable mic irq, ret = %d\n",
+ ret);
+ goto free_buffers;
+ }
+
+ init_waitqueue_head(&audio_data->wq);
+
+ audio_data->dev_dsp_open_count = 1;
+ KDEBUG(M4SH_INFO, "M4 mic driver opened\n");
+ goto out;
+
+free_buffers:
+ for (i = 0; i < AUDIO_NBFRAGS_READ; i++) {
+ kfree((void *) audio_data->buffers[i]);
+ audio_data->buffers[i] = NULL;
+ }
+out:
+ mutex_unlock(&audio_lock);
+ return ret;
+}
+
+static int audio_client_release(struct inode *inode, struct file *file)
+{
+ int i = 0, ret;
+ mutex_lock(&audio_lock);
+
+ audio_data->active = 0;
+ ret = m4sensorhub_irq_disable(audio_data->m4sensorhub,
+ M4SH_IRQ_MIC_DATA_READY);
+ if (ret < 0)
+ KDEBUG(M4SH_ERROR, "Unable to disable mic, ret = %d\n", ret);
+ ret = m4sensorhub_reg_write_1byte(audio_data->m4sensorhub,
+ M4SH_REG_AUDIO_ENABLE, MIC_DISABLE, 0xFF);
+ /* Check that we wrote 1 byte */
+ if (ret != 1)
+ KDEBUG(M4SH_ERROR, "Unable to disable mic, size = %d\n", ret);
+
+ audio_data->dev_dsp_open_count = 0;
+
+ for (i = 0; i < AUDIO_NBFRAGS_READ; i++) {
+ kfree((void *) audio_data->buffers[i]);
+ audio_data->buffers[i] = NULL;
+ }
+
+ mutex_unlock(&audio_lock);
+ KDEBUG(M4SH_INFO, "M4 mic driver closed\n");
+ return 0;
+}
+
+static long audio_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ unsigned int samp_rate;
+
+ mutex_lock(&audio_lock);
+
+ switch (cmd) {
+ case OSS_GETVERSION:
+ ret = put_user(SOUND_VERSION, (int *)arg);
+ break;
+
+ case SNDCTL_DSP_SPEED:
+ if (copy_from_user(&samp_rate, (unsigned int *)arg,
+ sizeof(unsigned int)))
+ ret = -EFAULT;
+ else if (samp_rate != AUDIO_SAMPLE_RATE)
+ ret = -EINVAL;
+
+ break;
+
+ case SNDCTL_DSP_GETBLKSIZE:
+ put_user(AUDIO_BUFFER_SIZE, (int *)arg);
+ break;
+
+ default:
+ break;
+ }
+ mutex_unlock(&audio_lock);
+ return ret;
+}
+
+static ssize_t audio_client_read(struct file *file, char *buffer, size_t size,
+ loff_t *nouse)
+{
+ int ret = 0;
+ int local_size = size;
+ int local_offset = 0; /* offset into output buffer */
+ int remainder_buff = 0; /* Indicates bytes remaining in input buffer */
+
+ mutex_lock(&audio_lock);
+
+ if (!audio_data->active) {
+ ret = m4sensorhub_reg_write_1byte(audio_data->m4sensorhub,
+ M4SH_REG_AUDIO_ENABLE, MIC_ENABLE, 0xFF);
+ /* Check that we wrote 1 byte */
+ if (ret != 1) {
+ KDEBUG(M4SH_ERROR, "Unable to enable mic, size = %d\n",
+ ret);
+ goto out;
+ }
+ audio_data->active = 1;
+ audio_data->total_buf_cnt = 0;
+ }
+
+ while (local_size > 0) {
+ mutex_unlock(&audio_lock);
+ ret = wait_event_interruptible_timeout(audio_data->wq,
+ audio_data->read_buf_full > 0, AUDIO_TIMEOUT);
+ mutex_lock(&audio_lock);
+ if (!ret) {
+ KDEBUG(M4SH_ERROR,
+ "Timed out waiting for mic buffer\n");
+ goto out;
+ }
+
+ remainder_buff = AUDIO_BUFFER_SIZE - audio_data->usr_offset;
+ if (local_size > remainder_buff) {
+
+ if (copy_to_user(buffer + local_offset,
+ audio_data->buffers
+ [audio_data->usr_head] +
+ audio_data->usr_offset,
+ remainder_buff)) {
+ KDEBUG(M4SH_ERROR,
+ "Mic driver: copy_to_user failed \n");
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (++audio_data->usr_head >= AUDIO_NBFRAGS_READ)
+ audio_data->usr_head = 0;
+
+ if (--audio_data->read_buf_full < 0)
+ audio_data->read_buf_full = 0;
+
+ local_size -= remainder_buff;
+ local_offset += remainder_buff;
+ audio_data->usr_offset = 0;
+ } else {
+
+ if (copy_to_user(buffer + local_offset,
+ audio_data->buffers
+ [audio_data->usr_head] +
+ audio_data->usr_offset, local_size)) {
+ KDEBUG(M4SH_ERROR,
+ "Mic driver: copy_to_user failed \n");
+ ret = -EFAULT;
+ goto out;
+ }
+
+ if (local_size == remainder_buff) {
+ if (++audio_data->usr_head >=
+ AUDIO_NBFRAGS_READ)
+ audio_data->usr_head = 0;
+
+ if (--audio_data->read_buf_full < 0)
+ audio_data->read_buf_full = 0;
+
+ audio_data->usr_offset = 0;
+
+ } else {
+ audio_data->usr_offset += local_size;
+ }
+
+ local_size = 0;
+ }
+ }
+ ret = size;
+
+out:
+ mutex_unlock(&audio_lock);
+ return ret;
+}
+
+/* File Ops structure */
+static const struct file_operations audio_client_fops = {
+ .owner = THIS_MODULE,
+ .open = audio_client_open,
+ .release = audio_client_release,
+ .unlocked_ioctl = audio_client_ioctl,
+ .read = audio_client_read,
+};
+
+static int audio_client_probe(struct spi_device *spi)
+{
+ int ret = -1;
+ struct audio_client *audio_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ audio_client_data = kzalloc(sizeof(*audio_client_data), GFP_KERNEL);
+ if (!audio_client_data)
+ return -ENOMEM;
+ audio_client_data->m4sensorhub = m4sensorhub;
+ spi_set_drvdata(spi, audio_client_data);
+ audio_client_data->spi = spi;
+ audio_data = audio_client_data;
+
+ ret = register_sound_dsp(&audio_client_fops, -1);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n",
+ AUDIO_CLIENT_DRIVER_NAME);
+ goto free_client_data;
+ }
+ audio_client_data->dev_dsp = ret;
+ audio_client_data->dev_dsp_open_count = 0;
+
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_MIC_DATA_READY,
+ m4_handle_audio_irq, audio_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_MIC_DATA_READY, ret);
+ goto unregister_sound_device;
+ }
+
+ ret = device_create_file(&spi->dev, &dev_attr_LogLevel);
+ if (ret) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ AUDIO_CLIENT_DRIVER_NAME);
+ goto unregister_irq;
+ }
+
+ KDEBUG(M4SH_ERROR, "Initialized %s driver\n", AUDIO_CLIENT_DRIVER_NAME);
+ return 0;
+
+unregister_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_MIC_DATA_READY);
+unregister_sound_device:
+ unregister_sound_dsp(audio_client_data->dev_dsp);
+free_client_data:
+ spi_set_drvdata(spi, NULL);
+ kfree(audio_client_data);
+ return ret;
+}
+
+static int __exit audio_client_remove(struct spi_device *spi)
+{
+ struct audio_client *audio_client_data = spi_get_drvdata(spi);
+
+ device_remove_file(&spi->dev, &dev_attr_LogLevel);
+ m4sensorhub_irq_disable(audio_client_data->m4sensorhub,
+ M4SH_IRQ_MIC_DATA_READY);
+ m4sensorhub_irq_unregister(audio_client_data->m4sensorhub,
+ M4SH_IRQ_MIC_DATA_READY);
+ unregister_sound_dsp(audio_client_data->dev_dsp);
+ spi_set_drvdata(spi, NULL);
+ kfree(audio_client_data);
+ return 0;
+}
+
+
+static struct of_device_id m4audio_match_tbl[] = {
+ {.compatible = "mot,m4audio"},
+ {},
+};
+
+static struct spi_driver audio_client_spi_driver = {
+ .driver = {
+ .name = AUDIO_CLIENT_DRIVER_NAME,
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4audio_match_tbl),
+ },
+ .suspend = NULL,
+ .resume = NULL,
+ .probe = audio_client_probe,
+ .remove = __exit_p(audio_client_remove),
+};
+
+static int __init audio_client_init(void)
+{
+ int ret = 0;
+
+ ret = spi_register_driver(&audio_client_spi_driver);
+
+ return ret;
+}
+
+static void __exit audio_client_exit(void)
+{
+ spi_unregister_driver(&audio_client_spi_driver);
+}
+
+module_init(audio_client_init);
+module_exit(audio_client_exit);
+
+MODULE_ALIAS("platform:audio_client");
+MODULE_DESCRIPTION("M4 Sensor Hub audio client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_bmp180.c b/drivers/misc/m4sensorhub_bmp180.c
new file mode 100644
index 00000000000..c64fa377ee0
--- /dev/null
+++ b/drivers/misc/m4sensorhub_bmp180.c
@@ -0,0 +1,453 @@
+/*
+ * Copyright (C) 2012 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Adds ability to program periodic interrupts from user space that
+ * can wake the phone out of low power modes.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/m4sensorhub/MemMapPressureSensor.h>
+#include <linux/slab.h>
+
+#define PRESSURE_CLIENT_DRIVER_NAME "m4sensorhub_bmp180"
+#define PRESSURE_MIN 30000
+#define PRESSURE_MAX 110000
+
+struct pressure_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+ int pressure;
+ int altitude;
+ signed short samplerate;
+};
+
+struct pressure_client *misc_pressure_data;
+
+static int pressure_client_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+
+ err = nonseekable_open(inode, file);
+ if (err < 0) {
+ KDEBUG(M4SH_ERROR, " %s failed\n", __func__);
+ return err;
+ }
+ file->private_data = misc_pressure_data;
+
+ return 0;
+}
+
+static int pressure_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, " pressure_client in %s\n", __func__);
+ return 0;
+}
+
+static void m4_report_pressure_inputevent(struct pressure_client
+ *pressure_client_data)
+{
+ input_report_abs(pressure_client_data->input_dev, ABS_PRESSURE,
+ pressure_client_data->pressure);
+ input_report_abs(pressure_client_data->input_dev, ABS_ALTITUDE,
+ pressure_client_data->altitude);
+ input_sync(pressure_client_data->input_dev);
+}
+
+static void m4_read_pressure_data(struct pressure_client *pressure_data)
+{
+ sPressureData pressure;
+
+ m4sensorhub_reg_read(pressure_data->m4sensorhub,
+ M4SH_REG_PRESSURE_PRESSURE,
+ (char *)&pressure.pressure);
+ pressure_data->pressure = pressure.pressure;
+ m4sensorhub_reg_read(pressure_data->m4sensorhub,
+ M4SH_REG_PRESSURE_ABSOLUTEALTITUDE,
+ (char *)&pressure.absoluteAltitude);
+ pressure_data->altitude = pressure.absoluteAltitude;
+}
+
+static void m4_handle_pressure_irq(enum m4sensorhub_irqs int_event,
+ void *pressure_data)
+{
+ struct pressure_client *pressure_client_data = pressure_data;
+
+ m4_read_pressure_data(pressure_client_data);
+ m4_report_pressure_inputevent(pressure_client_data);
+}
+
+static int m4_set_pressure_samplerate(
+ struct pressure_client *pressure_client_data,
+ signed int samplerate)
+{
+ int ret = 0;
+
+ if (samplerate != pressure_client_data->samplerate) {
+ ret = m4sensorhub_reg_write(pressure_client_data->m4sensorhub,
+ M4SH_REG_PRESSURE_SAMPLERATE,
+ (char *)&samplerate, m4sh_no_mask);
+ if (ret != m4sensorhub_reg_getsize(
+ pressure_client_data->m4sensorhub,
+ M4SH_REG_PRESSURE_SAMPLERATE)) {
+ KDEBUG(M4SH_ERROR, "Unable to set delay for \
+ pressure sensor\n");
+ return ret;
+ }
+
+ KDEBUG(M4SH_DEBUG, "%s() updating samplerate from %d to %d\n",
+ __func__, pressure_client_data->samplerate,
+ samplerate);
+ pressure_client_data->samplerate = samplerate;
+
+ if (samplerate >= 0)
+ ret = m4sensorhub_irq_enable(
+ pressure_client_data->m4sensorhub,
+ M4SH_IRQ_PRESSURE_DATA_READY);
+ else
+ ret = m4sensorhub_irq_disable(
+ pressure_client_data->m4sensorhub,
+ M4SH_IRQ_PRESSURE_DATA_READY);
+ if (ret != 0)
+ KDEBUG(M4SH_ERROR, "Unable to enable/disable \
+ pressure irq\n");
+ }
+
+ return ret;
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long pressure_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ int delay;
+ unsigned char mask[] = {0xff, 0xff, 0xff, 0xff};
+ static int status;
+ int altitude = 0;
+ void __user *argp = (void __user *)arg;
+ struct pressure_client *pressure_client_data = filp->private_data;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_GET_PRESSURE:
+ m4_read_pressure_data(pressure_client_data);
+ m4_report_pressure_inputevent(pressure_client_data);
+ break;
+
+ case M4_SENSOR_IOCTL_SET_DELAY:
+ if (copy_from_user(&delay, argp, sizeof(delay)))
+ return -EFAULT;
+ if (delay >= 0)
+ ret = m4_set_pressure_samplerate(pressure_client_data,
+ delay);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_PRESSURE_DATA_READY, ret);
+ return -EFAULT;
+ }
+ break;
+
+ case M4_SENSOR_IOCTL_APP_GET_FLAG:
+ if (copy_to_user(argp, &status, sizeof(status)))
+ return -EFAULT;
+ break;
+
+ case M4_SENSOR_IOCTL_APP_SET_FLAG:
+ if (copy_from_user(&status, argp, sizeof(status)))
+ return -EFAULT;
+ break;
+ case M4_SENSOR_IOCTL_SET_ALTITUDE:
+ if (copy_from_user(&altitude, argp, sizeof(altitude)))
+ return -EFAULT;
+ if (altitude > 0)
+ m4sensorhub_reg_write(pressure_client_data->m4sensorhub,
+ M4SH_REG_PRESSURE_REFERENCEALTITUDE,
+ (char *)&altitude, mask);
+
+ break;
+ default:
+ KDEBUG(M4SH_ERROR, "Invalid IOCTL Command in %s \n", __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static ssize_t pressure_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pressure_client *pressure_client_data
+ = platform_get_drvdata(pdev);
+
+ m4_read_pressure_data(pressure_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : Pressure : = %d",
+ __func__, pressure_client_data->pressure);
+ return sprintf(buf, "%d\n", pressure_client_data->pressure);
+}
+
+static ssize_t altitude_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pressure_client *pressure_client_data
+ = platform_get_drvdata(pdev);
+
+ m4_read_pressure_data(pressure_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : Altitude : %d",
+ __func__, pressure_client_data->altitude);
+ return sprintf(buf, "%d\n", pressure_client_data->altitude);
+}
+
+static ssize_t bmp_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long long loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pressure_client *pressure_client_data
+ = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(pressure_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, BMP_MASK_BIT_1);
+ return sprintf(buf, "%llu\n", loglevel);
+}
+static ssize_t bmp_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ unsigned long long mask = 0, newlevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pressure_client *pressure_client_data
+ = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -1;
+ }
+ mask = (1ULL << BMP_MASK_BIT_1) | (1ULL << BMP_MASK_BIT_2);
+ newlevel = ((unsigned long long)level << BMP_MASK_BIT_1);
+ return m4sensorhub_reg_write(pressure_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&newlevel, (unsigned char *)&mask);
+}
+
+static DEVICE_ATTR(pressure, 0444, pressure_show, NULL);
+static DEVICE_ATTR(altitude, 0444, altitude_show, NULL);
+static DEVICE_ATTR(LogLevel, 0664, bmp_get_loglevel, bmp_set_loglevel);
+
+static const struct file_operations pressure_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = pressure_client_ioctl,
+ .open = pressure_client_open,
+ .release = pressure_client_close,
+};
+
+static struct miscdevice pressure_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = PRESSURE_CLIENT_DRIVER_NAME,
+ .fops = &pressure_client_fops,
+};
+
+static int pressure_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct pressure_client *pressure_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ pressure_client_data = kzalloc(sizeof(*pressure_client_data),
+ GFP_KERNEL);
+ if (!pressure_client_data)
+ return -ENOMEM;
+
+ pressure_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, pressure_client_data);
+
+ pressure_client_data->input_dev = input_allocate_device();
+ if (!pressure_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_mem;
+ }
+
+ pressure_client_data->input_dev->name = PRESSURE_CLIENT_DRIVER_NAME;
+ set_bit(EV_ABS, pressure_client_data->input_dev->evbit);
+ set_bit(ABS_PRESSURE, pressure_client_data->input_dev->absbit);
+ set_bit(ABS_ALTITUDE, pressure_client_data->input_dev->absbit);
+ input_set_abs_params(pressure_client_data->input_dev, ABS_PRESSURE,
+ PRESSURE_MIN, PRESSURE_MAX, 0, 0);
+
+ if (input_register_device(pressure_client_data->input_dev)) {
+ KDEBUG(M4SH_ERROR, "%s: input device register failed\n",
+ __func__);
+ input_free_device(pressure_client_data->input_dev);
+ goto free_mem;
+ }
+
+ ret = misc_register(&pressure_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n",
+ PRESSURE_CLIENT_DRIVER_NAME);
+ goto unregister_input_device;
+ }
+ misc_pressure_data = pressure_client_data;
+ ret = m4sensorhub_irq_register(m4sensorhub,
+ M4SH_IRQ_PRESSURE_DATA_READY, m4_handle_pressure_irq,
+ pressure_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_PRESSURE_DATA_READY, ret);
+ goto unregister_misc_device;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_pressure)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ PRESSURE_CLIENT_DRIVER_NAME);
+ ret = -1;
+ goto unregister_irq;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_altitude)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ PRESSURE_CLIENT_DRIVER_NAME);
+ ret = -1;
+ goto remove_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_LogLevel)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ PRESSURE_CLIENT_DRIVER_NAME);
+ ret = -1;
+ goto remove_alt_device_file;
+ }
+ KDEBUG(M4SH_ERROR, "Initialized %s driver\n", __func__);
+ return 0;
+
+remove_alt_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_altitude);
+remove_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_pressure);
+unregister_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_PRESSURE_DATA_READY);
+unregister_misc_device:
+ misc_pressure_data = NULL;
+ misc_deregister(&pressure_client_miscdrv);
+unregister_input_device:
+ input_unregister_device(pressure_client_data->input_dev);
+free_mem:
+ platform_set_drvdata(pdev, NULL);
+ pressure_client_data->m4sensorhub = NULL;
+ kfree(pressure_client_data);
+ pressure_client_data = NULL;
+ return ret;
+}
+
+static int __exit pressure_client_remove(struct platform_device *pdev)
+{
+ struct pressure_client *pressure_client_data =
+ platform_get_drvdata(pdev);
+
+ device_remove_file(&pdev->dev, &dev_attr_LogLevel);
+ device_remove_file(&pdev->dev, &dev_attr_pressure);
+ device_remove_file(&pdev->dev, &dev_attr_altitude);
+ m4sensorhub_irq_disable(pressure_client_data->m4sensorhub,
+ M4SH_IRQ_PRESSURE_DATA_READY);
+ m4sensorhub_irq_unregister(pressure_client_data->m4sensorhub,
+ M4SH_IRQ_PRESSURE_DATA_READY);
+ misc_pressure_data = NULL;
+ misc_deregister(&pressure_client_miscdrv);
+ input_unregister_device(pressure_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ pressure_client_data->m4sensorhub = NULL;
+ kfree(pressure_client_data);
+ pressure_client_data = NULL;
+ return 0;
+}
+
+static void pressure_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+#ifdef CONFIG_PM
+static int pressure_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ struct pressure_client *pressure_client_data =
+ platform_get_drvdata(pdev);
+
+ return m4_set_pressure_samplerate(pressure_client_data, -1);
+}
+
+static int pressure_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define pressure_client_suspend NULL
+#define pressure_client_resume NULL
+#endif
+
+static struct of_device_id m4pressure_match_tbl[] = {
+ {.compatible = "mot,m4pressure" },
+ {},
+};
+
+static struct platform_driver pressure_client_driver = {
+ .probe = pressure_client_probe,
+ .remove = __exit_p(pressure_client_remove),
+ .shutdown = pressure_client_shutdown,
+ .suspend = pressure_client_suspend,
+ .resume = pressure_client_resume,
+ .driver = {
+ .name = PRESSURE_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4pressure_match_tbl),
+ },
+};
+
+static int __init pressure_client_init(void)
+{
+ return platform_driver_register(&pressure_client_driver);
+}
+
+static void __exit pressure_client_exit(void)
+{
+ platform_driver_unregister(&pressure_client_driver);
+}
+
+module_init(pressure_client_init);
+module_exit(pressure_client_exit);
+
+MODULE_ALIAS("platform:pressure_client");
+MODULE_DESCRIPTION("M4 Sensor Hub Pressure client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_display.c b/drivers/misc/m4sensorhub_display.c
new file mode 100644
index 00000000000..2bc96643d89
--- /dev/null
+++ b/drivers/misc/m4sensorhub_display.c
@@ -0,0 +1,558 @@
+/*
+ * Copyright (C) 2013 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Adds ability to program periodic interrupts from user space that
+ * can wake the phone out of low power modes.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/input.h>
+#include <linux/regulator/consumer.h>
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/uaccess.h>
+#include <linux/time.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+
+#define DISPLAY_CLIENT_DRIVER_NAME "m4sensorhub_display"
+#define SENSORHUB_MIPI_DRIVER_REGULATOR "vcsi"
+#define SYNC_CLOCK_RETRY_TIMES 3
+#define INVALID_UTC_TIME 0xFFFFFFFF
+
+struct display_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct regulator *regulator;
+ atomic_t m4_lockcnt;
+ struct mutex m4_mutex;
+ int gpio_mipi_mux;
+ int m4_control;
+ int m4_enable;
+ int timezone_offset;
+ int m4_timezone_offset;
+ int dailystep_offset;
+ int m4_dailystep_offset;
+};
+
+struct display_client *global_display_data;
+
+static u32 m4_display_get_clock(void);
+static u32 m4_display_get_kernel_clock(void);
+static int m4_display_set_clock(u32 ms);
+static int m4_display_sync_timezone(struct display_client *data);
+static int m4_display_sync_dailystep(struct display_client *data);
+static int m4_display_sync_clock(void);
+static int m4_display_sync_state(struct display_client *display_data);
+static int m4_display_lock(struct display_client *display_data, int lock);
+static int m4_display_set_control(int m4_ctrl, int gpio_mipi_mux);
+
+static int display_client_open(struct inode *inode, struct file *file)
+{
+ int ret = -EFAULT;
+ KDEBUG(M4SH_DEBUG, "%s:\n", __func__);
+ if (global_display_data) {
+ ret = nonseekable_open(inode, file);
+ if (ret >= 0) {
+ file->private_data = global_display_data;
+ ret = 0;
+ }
+ }
+ if (ret)
+ KDEBUG(M4SH_ERROR, "%s: failed, err=%d\n", __func__, -ret);
+ return ret;
+}
+
+static int display_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, "%s:\n", __func__);
+ file->private_data = NULL;
+ return 0;
+}
+
+static long display_client_ioctl(
+ struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+ struct display_client *display_data = filp->private_data;
+ int value;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_SET_TIMEZONE_OFFSET:
+ if (copy_from_user(&value, argp, sizeof(value)))
+ return -EFAULT;
+ KDEBUG(M4SH_INFO, "%s update timezone offset %d\n",\
+ __func__, value);
+ m4_display_lock(display_data, true);
+ display_data->timezone_offset = value;
+ value = m4_display_sync_timezone(display_data);
+ /* sync M4 clock every time when app set timezone offset */
+ /* FIXME:Setting rtc clock on M4 happens in M4 ISR and leads to
+ * i2c timeout for the first clock set, this causes the bus to
+ * fail for a few mins on klockworks,hence commenting this part
+ * of the code until fixed for klockworks
+ if (value == 0)
+ m4_display_sync_clock();
+ */
+ m4_display_lock(display_data, false);
+ return value;
+ case M4_SENSOR_IOCTL_SET_DAILYSTEP_OFFSET:
+ if (copy_from_user(&value, argp, sizeof(value)))
+ return -EFAULT;
+ KDEBUG(M4SH_INFO, "%s update dailystep offset %d\n",\
+ __func__, value);
+ display_data->dailystep_offset = value;
+ return m4_display_sync_dailystep(display_data);
+ case M4_SENSOR_IOCTL_LOCK_CLOCKFACE:
+ if (copy_from_user(&value, argp, sizeof(value)))
+ return -EFAULT;
+ return m4_display_lock(display_data, value);
+ default:
+ KDEBUG(M4SH_ERROR, "%s Invaild ioctl cmd %d\n", __func__, cmd);
+ break;
+ }
+ return -EINVAL;
+}
+
+/* Get current M4 RTC time of seconds elapsed since 00:00:00
+ * on January 1, 1970, Coordinated Universal Time
+ * return 0xFFFFFFFF if it's something wrong
+ */
+static u32 m4_display_get_clock(void)
+{
+ u32 seconds;
+ struct display_client *data = global_display_data;
+ if (m4sensorhub_reg_getsize(data->m4sensorhub,\
+ M4SH_REG_GENERAL_UTC) != m4sensorhub_reg_read(\
+ data->m4sensorhub, M4SH_REG_GENERAL_UTC,\
+ (char *)&seconds)) {
+ seconds = INVALID_UTC_TIME;
+ KDEBUG(M4SH_ERROR, "%s: Failed get M4 clock!\n", __func__);
+ }
+ return seconds;
+}
+
+/* Set current M4 RTC time of seconds elapsed since 00:00:00
+ * on January 1, 1970, Coordinated Universal Time
+ * return < 0 if it's something wrong
+ */
+static int m4_display_set_clock(u32 seconds)
+{
+ struct display_client *data = global_display_data;
+ if (m4sensorhub_reg_getsize(data->m4sensorhub,\
+ M4SH_REG_GENERAL_UTC) != m4sensorhub_reg_write(\
+ data->m4sensorhub, M4SH_REG_GENERAL_UTC,\
+ (char *)&seconds, m4sh_no_mask)) {
+ KDEBUG(M4SH_ERROR, "%s: Failed set M4 clock!\n", __func__);
+ return -EIO;
+ }
+ return 0;
+}
+
+static void m4_notify_clock_change(void)
+{
+ struct display_client *data = global_display_data;
+ char notify = 1;
+ if (m4sensorhub_reg_getsize(data->m4sensorhub,\
+ M4SH_REG_USERSETTINGS_RTCRESET) != m4sensorhub_reg_write(\
+ data->m4sensorhub, M4SH_REG_USERSETTINGS_RTCRESET,\
+ &notify, m4sh_no_mask)) {
+ KDEBUG(M4SH_ERROR, "Failed to notify clock change");
+ }
+}
+
+/* Get current Kernel RTC time of seconds elapsed since 00:00:00
+ * on January 1, 1970, Coordinated Universal Time
+ */
+static u32 m4_display_get_kernel_clock(void)
+{
+ struct timespec now = current_kernel_time();
+ /* adjust with 500ms */
+ if (now.tv_nsec > 500000000)
+ now.tv_sec++;
+ return (u32)now.tv_sec;
+}
+
+void print_time(char *info, u32 time)
+{
+ struct tm t; /* it convert to year since 1900 */
+ time_to_tm((time_t)(time), 0, &t);
+ KDEBUG(M4SH_INFO, "%s(%d) %02d:%02d:%02d %02d/%02d/%04d\n",\
+ info, (int)time, t.tm_hour, t.tm_min, t.tm_sec,\
+ t.tm_mon+1, t.tm_mday, (int)t.tm_year+1900);
+}
+
+/* Sync M4 clock with current kernel time */
+static int m4_display_sync_clock(void)
+{
+ int retry = 0;
+ do {
+ u32 m4_time = m4_display_get_clock();
+ u32 kernel_time = m4_display_get_kernel_clock();
+ u32 diff_time = m4_time > kernel_time \
+ ? m4_time-kernel_time : kernel_time-m4_time;
+#ifdef DEBUG_CLOCK
+ print_time("M4 :", m4_time);
+ print_time("KNL:", kernel_time);
+#endif
+ /* it needs adjust M4 time if different large than 1 second */
+ if (diff_time < 2) {
+ if (retry) {
+ print_time("Synced M4 clock to", m4_time);
+ m4_notify_clock_change();
+ }
+ return 0;
+ }
+ m4_display_set_clock(kernel_time);
+ } while (retry++ < SYNC_CLOCK_RETRY_TIMES);
+ KDEBUG(M4SH_ERROR, "%s: Failed to sync M4 clock!\n", __func__);
+ return -EIO;
+}
+
+/* Sync M4 TimeZone offset */
+static int m4_display_sync_timezone(struct display_client *data)
+{
+ if (data->m4_timezone_offset == data->timezone_offset)
+ return 0;
+ if (m4sensorhub_reg_getsize(data->m4sensorhub,\
+ M4SH_REG_GENERAL_LOCALTIMEZONE) != m4sensorhub_reg_write(\
+ data->m4sensorhub, M4SH_REG_GENERAL_LOCALTIMEZONE,\
+ (char *)&(data->timezone_offset), m4sh_no_mask)) {
+ KDEBUG(M4SH_ERROR, "%s: Failed set M4 timezone!\n", __func__);
+ return -EIO;
+ }
+ data->m4_timezone_offset = data->timezone_offset;
+ return 0;
+}
+
+/* Sync M4 Daily Steps offset */
+static int m4_display_sync_dailystep(struct display_client *data)
+{
+ if (data->m4_dailystep_offset == data->dailystep_offset)
+ return 0;
+ if (m4sensorhub_reg_getsize(data->m4sensorhub,\
+ M4SH_REG_TIMEPIECE_OFFSETSTEPS) != m4sensorhub_reg_write(\
+ data->m4sensorhub, M4SH_REG_TIMEPIECE_OFFSETSTEPS,\
+ (char *)&(data->dailystep_offset), m4sh_no_mask)) {
+ KDEBUG(M4SH_ERROR, "%s: Failed set M4 dailystep!\n", __func__);
+ return -EIO;
+ }
+ data->m4_dailystep_offset = data->dailystep_offset;
+ return 0;
+}
+
+/* Sync M4 clockface state, it will be enable only when clockface is unlocked
+ */
+static int m4_display_sync_state(struct display_client *display_data)
+{
+ int enable = !!display_data->m4_control;
+ if (atomic_inc_return(&(display_data->m4_lockcnt)) != 1)
+ enable = 0;
+ atomic_dec(&(display_data->m4_lockcnt));
+
+ mutex_lock(&(display_data->m4_mutex));
+ if (enable == display_data->m4_enable)
+ goto __done__;
+ if (enable) {
+ /* switch display control to M4 */
+ m4_display_sync_clock();
+ m4_display_sync_timezone(display_data);
+ m4_display_sync_dailystep(display_data);
+ gpio_set_value(display_data->gpio_mipi_mux, 1);
+ if (regulator_enable(display_data->regulator)) {
+ KDEBUG(M4SH_ERROR, "Failed enable regulator!\n");
+ goto __error__;
+ }
+ }
+ /* comunicate with M4 via I2C */
+ if (1 != m4sensorhub_reg_write_1byte(\
+ display_data->m4sensorhub,\
+ M4SH_REG_TIMEPIECE_ENABLE, enable, 0xFF)) {
+ KDEBUG(M4SH_ERROR, "Failed set m4 display!\n");
+ goto __error__;
+ }
+ if (!enable) {
+ mdelay(2);
+ /* switch display control to OMAP */
+ regulator_disable(display_data->regulator);
+ gpio_set_value(display_data->gpio_mipi_mux, 0);
+ }
+ display_data->m4_enable = enable;
+ KDEBUG(M4SH_INFO, "Synced M4 display state(%d)\n", enable);
+__done__:
+ mutex_unlock(&(display_data->m4_mutex));
+ return 0;
+__error__:
+ /* when error occured, it always set control to OMAP */
+ regulator_disable(display_data->regulator);
+ gpio_set_value(display_data->gpio_mipi_mux, 0);
+ mutex_unlock(&(display_data->m4_mutex));
+ KDEBUG(M4SH_ERROR, "Failed sync m4 with state(%d)!\n", enable);
+ return -EIO;
+}
+
+/* M4 clockface lock/unlock */
+static int m4_display_lock(struct display_client *display_data, int lock)
+{
+ if (lock) {
+ atomic_inc(&(display_data->m4_lockcnt));
+ } else {
+ if (atomic_dec_return(&(display_data->m4_lockcnt)) == -1) {
+ atomic_inc(&(display_data->m4_lockcnt));
+ KDEBUG(M4SH_ERROR, "%s zero unlock count!\n",\
+ __func__);
+ return -EINVAL;
+ }
+ }
+ return m4_display_sync_state(display_data);
+}
+
+static int m4_display_set_control(int m4_ctrl, int gpio_mipi_mux)
+{
+ struct display_client *display_data = global_display_data;
+ KDEBUG(M4SH_INFO, "%s(%d)\n", __func__, m4_ctrl);
+
+ if (!display_data || !display_data->m4sensorhub || (gpio_mipi_mux < 0))
+ return -EINVAL;
+
+ if (m4_ctrl == display_data->m4_control) {
+ KDEBUG(M4SH_DEBUG, "%s is already set!\n", __func__);
+ return 0;
+ }
+
+ display_data->gpio_mipi_mux = gpio_mipi_mux;
+ display_data->m4_control = m4_ctrl;
+
+ return m4_display_sync_state(display_data);
+}
+
+int m4sensorhub_set_display_control(int m4_ctrl, int gpio_mipi_mux)
+{
+ return m4_display_set_control(m4_ctrl, gpio_mipi_mux);
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_set_display_control);
+
+static ssize_t display_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ uint64_t loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct display_client *display_data =
+ platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(display_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, DISPLAY_MASK_BIT_1);
+ return sprintf(buf, "%d\n", (int)loglevel);
+}
+
+static ssize_t display_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ uint64_t level = 0;
+ uint64_t mask = 0x3;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct display_client *display_data =
+ platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, (unsigned long *)&level)) < 0)
+ return -EINVAL;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -EINVAL;
+ }
+ mask <<= DISPLAY_MASK_BIT_1;
+ level <<= DISPLAY_MASK_BIT_1;
+ return m4sensorhub_reg_write(display_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&level, (unsigned char *)&mask);
+}
+
+static DEVICE_ATTR(LogLevel, 0664, \
+ display_get_loglevel, display_set_loglevel);
+
+static const struct file_operations display_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = display_client_ioctl,
+ .open = display_client_open,
+ .release = display_client_close,
+};
+
+static struct miscdevice display_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = DISPLAY_CLIENT_DRIVER_NAME,
+ .fops = &display_client_fops,
+};
+
+/* display_panic_restore()
+
+ Panic Callback Handler is called after M4 has been restarted
+
+*/
+static void display_panic_restore(\
+ struct m4sensorhub_data *m4sensorhub, void *data)
+{
+ struct display_client *display_data = (struct display_client *)data;
+ display_data->m4_timezone_offset = 0;
+ display_data->m4_dailystep_offset = 0;
+ if (display_data->m4_enable) {
+ m4_display_sync_clock();
+ m4_display_sync_timezone(display_data);
+ m4_display_sync_dailystep(display_data);
+ /* comunicate with M4 via I2C */
+ if (1 != m4sensorhub_reg_write_1byte(\
+ display_data->m4sensorhub,\
+ M4SH_REG_TIMEPIECE_ENABLE, 1, 0xFF)) {
+ KDEBUG(M4SH_ERROR, "Failed re-enable m4 display!\n");
+ /* TODO retry ? */
+ }
+ }
+}
+
+static int display_client_probe(struct platform_device *pdev)
+{
+ int ret = -EINVAL;
+ struct display_client *display_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ display_data = kzalloc(sizeof(*display_data),
+ GFP_KERNEL);
+ if (!display_data)
+ return -ENOMEM;
+
+ display_data->m4sensorhub = m4sensorhub;
+ display_data->timezone_offset = 0;
+ display_data->dailystep_offset = 0;
+ display_data->m4_timezone_offset = 0;
+ display_data->m4_dailystep_offset = 0;
+
+ platform_set_drvdata(pdev, display_data);
+
+ display_data->regulator = regulator_get(NULL,
+ SENSORHUB_MIPI_DRIVER_REGULATOR);
+ if (IS_ERR(display_data->regulator)) {
+ KDEBUG(M4SH_ERROR, "Error requesting %s regulator\n",
+ SENSORHUB_MIPI_DRIVER_REGULATOR);
+ ret = -EFAULT;
+ goto free_mem;
+ }
+
+ ret = misc_register(&display_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n",
+ DISPLAY_CLIENT_DRIVER_NAME);
+ goto disable_regulator;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_LogLevel)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ DISPLAY_CLIENT_DRIVER_NAME);
+ ret = -EFAULT;
+ goto unregister_misc_device;
+ }
+
+ /* default to host control */
+ display_data->m4_control = 0;
+ display_data->m4_enable = 0;
+ atomic_set(&(display_data->m4_lockcnt), 0);
+ mutex_init(&(display_data->m4_mutex));
+
+ global_display_data = display_data;
+
+ m4sensorhub_panic_register(m4sensorhub, PANICHDL_DISPLAY_RESTORE,\
+ display_panic_restore, display_data);
+
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n", __func__);
+ return 0;
+
+unregister_misc_device:
+ misc_deregister(&display_client_miscdrv);
+disable_regulator:
+ regulator_put(display_data->regulator);
+free_mem:
+ global_display_data = NULL;
+ platform_set_drvdata(pdev, NULL);
+ kfree(display_data);
+ return ret;
+}
+
+static int __exit display_client_remove(struct platform_device *pdev)
+{
+ struct display_client *display_data =
+ platform_get_drvdata(pdev);
+
+ display_data->m4sensorhub->pdev->set_display_control = NULL;
+ device_remove_file(&pdev->dev, &dev_attr_LogLevel);
+ misc_deregister(&display_client_miscdrv);
+ global_display_data = NULL;
+ regulator_put(display_data->regulator);
+ platform_set_drvdata(pdev, NULL);
+ display_data->m4sensorhub = NULL;
+ kfree(display_data);
+ return 0;
+}
+
+static void display_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+
+static struct of_device_id m4display_match_tbl[] = {
+ { .compatible = "mot,m4display" },
+ {},
+};
+
+static struct platform_driver display_client_driver = {
+ .probe = display_client_probe,
+ .remove = __exit_p(display_client_remove),
+ .shutdown = display_client_shutdown,
+ .suspend = NULL,
+ .resume = NULL,
+ .driver = {
+ .name = DISPLAY_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4display_match_tbl),
+ },
+};
+
+static int __init display_client_init(void)
+{
+ return platform_driver_register(&display_client_driver);
+}
+
+static void __exit display_client_exit(void)
+{
+ platform_driver_unregister(&display_client_driver);
+}
+
+module_init(display_client_init);
+module_exit(display_client_exit);
+
+MODULE_ALIAS("platform:display_client");
+MODULE_DESCRIPTION("M4 Sensor Hub AOD display driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/m4sensorhub_download.c b/drivers/misc/m4sensorhub_download.c
new file mode 100644
index 00000000000..7ad1aae47b5
--- /dev/null
+++ b/drivers/misc/m4sensorhub_download.c
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2012 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Adds ability to program periodic interrupts from user space that
+ * can wake the phone out of low power modes.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/input.h>
+#include <linux/uaccess.h>
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/m4sensorhub/MemMapDownload.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+
+#define DOWNLOAD_CLIENT_DRIVER_NAME "m4sensorhub_download"
+#define M4_SENSOR_DL_MAX_RETRY_CNT 3
+#define M4_SENSOR_DL_MAX_RET_SIZE 8
+#define M4_SENSOR_DL_MIN_INPUT_SIZE \
+ (sizeof(struct m4sh_download_packet) - M4_SENSOR_DL_MAX_PACKET_SIZE)
+
+enum {
+ i2c_reg_end,
+ i2c_reg_read,
+ i2c_reg_write,
+ i2c_reg_wait,
+};
+
+struct i2c_reg_sequence {
+ int direct;
+ int reg;
+};
+
+struct download_client {
+ struct m4sensorhub_data *m4sensorhub;
+};
+
+struct download_client *misc_download_data;
+static wait_queue_head_t download_wq;
+static atomic_t m4_dlcmd_resp_ready;
+static atomic_t download_client_entry;
+
+static struct i2c_reg_sequence seq_m4dlm_get_checksum[] = {
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_FILENAME},
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_COMMAND},
+ {i2c_reg_read, M4SH_REG_DOWNLOAD_CHECKSUM},
+ {i2c_reg_read, M4SH_REG_DOWNLOAD_STATUS},
+ {i2c_reg_end, i2c_reg_end}
+};
+
+static struct i2c_reg_sequence seq_m4dlm_open_file[] = {
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_FILENAME},
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_COMMAND},
+ {i2c_reg_wait, M4SH_REG_DOWNLOAD_STATUS},
+ {i2c_reg_end, i2c_reg_end}
+};
+
+static struct i2c_reg_sequence seq_m4dlm_close_file[] = {
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_COMMAND},
+ {i2c_reg_wait, M4SH_REG_DOWNLOAD_STATUS},
+ {i2c_reg_read, M4SH_REG_DOWNLOAD_CHECKSUM},
+ {i2c_reg_end, i2c_reg_end}
+};
+
+static struct i2c_reg_sequence seq_m4dlm_delete_file[] = {
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_FILENAME},
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_COMMAND},
+ {i2c_reg_wait, M4SH_REG_DOWNLOAD_STATUS},
+ {i2c_reg_end, i2c_reg_end}
+};
+
+static struct i2c_reg_sequence seq_m4dlm_write_file[] = {
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_PACKET},
+ {i2c_reg_wait, M4SH_REG_DOWNLOAD_STATUS},
+ {i2c_reg_end, i2c_reg_end}
+};
+
+static struct i2c_reg_sequence seq_m4dlm_write_size_file[] = {
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_SIZE},
+ {i2c_reg_write, M4SH_REG_DOWNLOAD_PACKET},
+ {i2c_reg_wait, M4SH_REG_DOWNLOAD_STATUS},
+ {i2c_reg_end, i2c_reg_end}
+};
+
+static int download_client_open(struct inode *inode, struct file *file)
+{
+ int err = atomic_inc_return(&download_client_entry);
+ if (err == 1) {
+ err = nonseekable_open(inode, file);
+ if (err >= 0) {
+ file->private_data = misc_download_data;
+ return 0;
+ }
+ } else
+ err = -EBUSY;
+
+ atomic_dec_return(&download_client_entry);
+ KDEBUG(M4SH_ERROR, "%s: failed, err=%d\n", __func__, -err);
+ return err;
+}
+
+static int download_client_close(struct inode *inode, struct file *file)
+{
+ int entry = atomic_dec_return(&download_client_entry);
+ file->private_data = NULL;
+ KDEBUG(M4SH_DEBUG, "%s: entry = %d\n", __func__, entry);
+ return 0;
+}
+
+static void m4_handle_download_irq(enum m4sensorhub_irqs int_event,
+ void *download_data)
+{
+ atomic_set(&m4_dlcmd_resp_ready, true);
+ wake_up_interruptible(&download_wq);
+}
+
+static inline void wait_m4_cmd_executed(void)
+{
+ wait_event_interruptible(download_wq, \
+ (atomic_read(&m4_dlcmd_resp_ready)));
+ atomic_set(&m4_dlcmd_resp_ready, false);
+}
+
+static char *m4dlm_i2c_reg_seq_getptr(
+ int reg, struct m4sh_download_packet *dl_packet)
+{
+ switch (reg) {
+ case M4SH_REG_DOWNLOAD_COMMAND:
+ return (char *)(&(dl_packet->command));
+ case M4SH_REG_DOWNLOAD_STATUS:
+ return (char *)(&(dl_packet->status));
+ case M4SH_REG_DOWNLOAD_SIZE:
+ return (char *)(&(dl_packet->size));
+ case M4SH_REG_DOWNLOAD_CHECKSUM:
+ return (char *)(&(dl_packet->checksum));
+ case M4SH_REG_DOWNLOAD_FILENAME:
+ return (char *)(dl_packet->filename);
+ case M4SH_REG_DOWNLOAD_PACKET:
+ return (char *)(dl_packet->buffer);
+ }
+ KDEBUG(M4SH_ERROR, "%s Invaild i2c reg %d\n", __func__, reg);
+ return NULL;
+}
+
+static int m4dlm_i2c_reg_seq_process(
+ struct m4sensorhub_data *m4sensorhub,
+ struct m4sh_download_packet *dl_packet,
+ struct i2c_reg_sequence *i2c_reg_seq)
+{
+ int ret;
+ for (; i2c_reg_seq->direct != i2c_reg_end; i2c_reg_seq++) {
+ /*we don't need retry for I2C read/write as
+ *m4sensorhub_reg_write/read already had retry mechanism
+ */
+ switch (i2c_reg_seq->direct) {
+ case i2c_reg_write:
+ ret = m4sensorhub_reg_write(m4sensorhub,
+ i2c_reg_seq->reg,
+ m4dlm_i2c_reg_seq_getptr(\
+ i2c_reg_seq->reg, dl_packet),
+ m4sh_no_mask);
+ break;
+ case i2c_reg_wait:
+ /*Wait for IRQ answered*/
+ wait_m4_cmd_executed();
+ /*fallback to read status*/
+ case i2c_reg_read:
+ ret = m4sensorhub_reg_read(m4sensorhub,
+ i2c_reg_seq->reg,
+ m4dlm_i2c_reg_seq_getptr(\
+ i2c_reg_seq->reg, dl_packet)
+ );
+ break;
+ default:
+ /*should be fault*/
+ KDEBUG(M4SH_ERROR, "%s: Invaild I2C direct %d\n", \
+ __func__, i2c_reg_seq->direct);
+ return -ENOEXEC;
+ }
+ if (ret != m4sensorhub_reg_getsize(\
+ m4sensorhub, i2c_reg_seq->reg)) {
+ KDEBUG(M4SH_ERROR, "%s: Process I2C [%d-%d] failed!\n",
+ __func__, i2c_reg_seq->direct,
+ i2c_reg_seq->reg);
+ return -EIO;
+ }
+ }
+ return 0;
+}
+
+static long download_client_ioctl(
+ struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ void __user *argp = (void __user *)arg;
+
+ struct download_client *download_data = filp->private_data;
+ struct i2c_reg_sequence *i2c_req_seq = NULL;
+ int ret = -EINVAL, retry;
+ static struct m4sh_download_packet packet;
+ static unsigned short packet_size;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_DL_SEND_PACKET:
+ if (copy_from_user(&packet, argp, M4_SENSOR_DL_MIN_INPUT_SIZE))
+ return -EFAULT;
+
+ KDEBUG(M4SH_INFO, "%s cmd = %d\n", __func__, packet.command);
+
+ switch (packet.command) {
+ case M4_SENSOR_DL_CMD_GET_CHECKSUM:
+ i2c_req_seq = seq_m4dlm_get_checksum;
+ break;
+ case M4_SENSOR_DL_CMD_OPEN_FILE:
+ packet_size = 0;
+ i2c_req_seq = seq_m4dlm_open_file;
+ break;
+ case M4_SENSOR_DL_CMD_DELETE_FILE:
+ i2c_req_seq = seq_m4dlm_delete_file;
+ break;
+ case M4_SENSOR_DL_CMD_CLOSE_FILE:
+ i2c_req_seq = seq_m4dlm_close_file;
+ break;
+ case M4_SENSOR_DL_CMD_WRITE_FILE:
+ if (!packet.size || \
+ (packet.size > M4_SENSOR_DL_MAX_PACKET_SIZE)) {
+ packet.status = M4_SENSOR_DL_ERROR_INVALID_SIZE;
+ /*we only copy packet data before filename*/
+ if (copy_to_user(argp, &packet, \
+ M4_SENSOR_DL_MAX_RET_SIZE))
+ return -EFAULT;
+ return 0;
+ }
+ if (copy_from_user(&packet, argp, sizeof(packet)))
+ return -EFAULT;
+ if (packet.size != packet_size) {
+ i2c_req_seq = seq_m4dlm_write_size_file;
+ packet_size = packet.size;
+ } else
+ i2c_req_seq = seq_m4dlm_write_file;
+ break;
+ default:
+ /*should be wrong command received*/
+ KDEBUG(M4SH_ERROR, "%s Invaild packet cmd %d\n", \
+ __func__, packet.command);
+ return -EINVAL;
+ }
+ for (retry = 0; retry++ < M4_SENSOR_DL_MAX_RETRY_CNT; ) {
+ ret = m4dlm_i2c_reg_seq_process(\
+ download_data->m4sensorhub,
+ &packet, i2c_req_seq);
+ /*only retry if M4 has internal error*/
+ if (!ret) {
+ switch (packet.status) {
+ case M4_SENSOR_DL_ERROR_SEND_CMD:
+ case M4_SENSOR_DL_ERROR_DATA_CHECKSUM:
+ /*something wrong and we need retry*/
+ KDEBUG(M4SH_ERROR, \
+ "Tried %d times for packet cmd %d\n", \
+ retry, packet.command);
+ continue;
+ }
+ }
+ break; /*exit retry loop*/
+ }
+ if (!ret) {
+ /*we only copy packet data before filename for return*/
+ if (copy_to_user(argp, &packet, \
+ M4_SENSOR_DL_MAX_RET_SIZE))
+ return -EFAULT;
+ }
+ break;
+ default:
+ KDEBUG(M4SH_ERROR, "%s Invaild ioctl cmd %d\n", __func__, cmd);
+ break;
+ }
+ return ret;
+}
+
+static ssize_t download_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct download_client *download_client_data =
+ platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(download_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, DOWNLOAD_MASK_BIT_1);
+ return sprintf(buf, "%d\n", loglevel);
+}
+
+static ssize_t download_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ unsigned int mask = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct download_client *download_client_data =
+ platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -EINVAL;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -EINVAL;
+ }
+ mask = (1 << DOWNLOAD_MASK_BIT_1) | (1 << DOWNLOAD_MASK_BIT_2);
+ level = (level << DOWNLOAD_MASK_BIT_1);
+ return m4sensorhub_reg_write(download_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&level, (unsigned char *)&mask);
+}
+
+static DEVICE_ATTR(LogLevel, 0664, \
+ download_get_loglevel, download_set_loglevel);
+
+static const struct file_operations download_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = download_client_ioctl,
+ .open = download_client_open,
+ .release = download_client_close,
+};
+
+static struct miscdevice download_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = DOWNLOAD_CLIENT_DRIVER_NAME,
+ .fops = &download_client_fops,
+};
+
+static int download_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct download_client *download_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub) {
+ printk(KERN_WARNING "m4sensorhub is null\n");
+ return -EFAULT;
+ }
+
+ download_client_data =
+ kzalloc(sizeof(*download_client_data), GFP_KERNEL);
+ if (!download_client_data)
+ return -ENOMEM;
+
+ download_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, download_client_data);
+
+ ret = misc_register(&download_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n",
+ DOWNLOAD_CLIENT_DRIVER_NAME);
+ goto free_memory;
+ }
+ misc_download_data = download_client_data;
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_DLCMD_RESP_READY,
+ m4_handle_download_irq,
+ download_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_DLCMD_RESP_READY, ret);
+ goto unregister_misc_device;
+ }
+
+ ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_DLCMD_RESP_READY);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enable irq %d (%d)\n",
+ M4SH_IRQ_DLCMD_RESP_READY, ret);
+ goto unregister_irq;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_LogLevel)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ DOWNLOAD_CLIENT_DRIVER_NAME);
+ ret = -1;
+ goto disable_irq;
+ }
+
+ init_waitqueue_head(&download_wq);
+ atomic_set(&m4_dlcmd_resp_ready, false);
+ atomic_set(&download_client_entry, 0);
+
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n",
+ DOWNLOAD_CLIENT_DRIVER_NAME);
+ return 0;
+
+disable_irq:
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_DLCMD_RESP_READY);
+unregister_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_DLCMD_RESP_READY);
+unregister_misc_device:
+ misc_download_data = NULL;
+ misc_deregister(&download_client_miscdrv);
+free_memory:
+ platform_set_drvdata(pdev, NULL);
+ download_client_data->m4sensorhub = NULL;
+ kfree(download_client_data);
+ download_client_data = NULL;
+ return ret;
+}
+
+static int __exit download_client_remove(struct platform_device *pdev)
+{
+ struct download_client *download_client_data =
+ platform_get_drvdata(pdev);
+
+ device_remove_file(&pdev->dev, &dev_attr_LogLevel);
+ m4sensorhub_irq_disable(download_client_data->m4sensorhub,
+ M4SH_IRQ_DLCMD_RESP_READY);
+ m4sensorhub_irq_unregister(download_client_data->m4sensorhub,
+ M4SH_IRQ_DLCMD_RESP_READY);
+ misc_download_data = NULL;
+ misc_deregister(&download_client_miscdrv);
+ platform_set_drvdata(pdev, NULL);
+ download_client_data->m4sensorhub = NULL;
+ kfree(download_client_data);
+ download_client_data = NULL;
+ return 0;
+}
+
+static void download_client_shutdown(struct platform_device *pdev)
+{
+}
+
+#ifdef CONFIG_PM
+
+static int download_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ return 0;
+}
+
+static int download_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+#else
+#define download_client_suspend NULL
+#define download_client_resume NULL
+#endif
+
+static struct of_device_id m4download_match_tbl[] = {
+ { .compatible = "mot,m4download" },
+ {},
+};
+
+static struct platform_driver download_client_driver = {
+ .probe = download_client_probe,
+ .remove = __exit_p(download_client_remove),
+ .shutdown = download_client_shutdown,
+ .suspend = download_client_suspend,
+ .resume = download_client_resume,
+ .driver = {
+ .name = DOWNLOAD_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4download_match_tbl),
+ },
+};
+
+static int __init download_client_init(void)
+{
+ return platform_driver_register(&download_client_driver);
+}
+
+static void __exit download_client_exit(void)
+{
+ platform_driver_unregister(&download_client_driver);
+}
+
+module_init(download_client_init);
+module_exit(download_client_exit);
+
+MODULE_ALIAS("platform:download_client");
+MODULE_DESCRIPTION("M4 Sensor Hub driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_gesture.c b/drivers/misc/m4sensorhub_gesture.c
new file mode 100644
index 00000000000..b067be71d24
--- /dev/null
+++ b/drivers/misc/m4sensorhub_gesture.c
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2012 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Adds ability to program periodic interrupts from user space that
+ * can wake the phone out of low power modes.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/input.h>
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/uaccess.h>
+#include <linux/m4sensorhub/MemMapGesture.h>
+#include <linux/slab.h>
+
+#define GESTURE_CLIENT_DRIVER_NAME "m4sensorhub_gesture"
+
+struct gesture_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+ struct memMapGesture gesture_data;
+};
+
+static bool read_gesture_value(struct gesture_client *list,
+ signed char *value,
+ eGestureType gesture)
+{
+ if (list->gesture_data.gesture1 == gesture) {
+ *value = list->gesture_data.value1;
+ return true;
+ } else if (list->gesture_data.gesture2 == gesture) {
+ *value = list->gesture_data.value2;
+ return true;
+ } else if (list->gesture_data.gesture3 == gesture) {
+ *value = list->gesture_data.value3;
+ return true;
+ } else {
+ *value = 0;
+ return false;
+ }
+}
+
+static struct gesture_client *misc_gesture_data;
+
+static int gesture_client_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+
+ err = nonseekable_open(inode, file);
+ if (err < 0) {
+ KDEBUG(M4SH_ERROR, "%s failed\n", __func__);
+ return err;
+ }
+ file->private_data = misc_gesture_data;
+
+ return 0;
+}
+
+static int gesture_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, "gesture_client in %s\n", __func__);
+ return 0;
+}
+
+static char m4_read_gesture_data(struct gesture_client *gesture_client_data)
+{
+ int ret;
+ struct memMapGesture *gdata = &(gesture_client_data->gesture_data);
+
+ ret = m4sensorhub_reg_read_n(gesture_client_data->m4sensorhub,
+ M4SH_REG_GESTURE_VERSION,
+ (char *)gdata,
+ sizeof(gesture_client_data->gesture_data));
+
+ if (ret != sizeof(gesture_client_data->gesture_data))
+ goto ERR;
+
+ KDEBUG(M4SH_DEBUG, "Gesture1 = %d, gesture2 = %d, gesture3 = %d\n",
+ gdata->gesture1, gdata->gesture2, gdata->gesture3);
+ KDEBUG(M4SH_DEBUG, "Confidence1 = %d, confidence2 = %d, confidence3 = %d\n",
+ gdata->confidence1, gdata->confidence2, gdata->confidence3);
+ KDEBUG(M4SH_DEBUG, "Value1 = %d, value2 = %d, value3 = %d\n",
+ gdata->value1, gdata->value2, gdata->value3);
+ return 0;
+ERR:
+ KDEBUG(M4SH_ERROR, "Gesture read failed\n");
+ return -1;
+}
+
+static void m4_handle_gesture_irq(enum m4sensorhub_irqs int_event,
+ void *gesture_data)
+{
+ signed char value;
+ /*Trigger broadcast of display gesture intent*/
+ struct gesture_client *gesture_client_data =
+ (struct gesture_client *)gesture_data;
+
+ struct memMapGesture *gdata = &(gesture_client_data->gesture_data);
+
+ if (m4_read_gesture_data(gesture_client_data) < 0) {
+ KDEBUG(M4SH_ERROR, "m4_read_gesture_data returned \
+ error %s\n", __func__);
+ return;
+ }
+
+ if (read_gesture_value(gesture_client_data, &value, GESTURE_TILT_SCROLL)) {
+ input_event(gesture_client_data->input_dev, EV_ABS,
+ ABS_TILTSCROLL, value);
+ } else {
+ if (read_gesture_value(gesture_client_data, &value, GESTURE_WRIST_ROTATE)) {
+ /* send event to stop scrolling for wrist rotate */
+ input_event(gesture_client_data->input_dev, EV_ABS,
+ ABS_TILTSCROLL, 0);
+ }
+ }
+
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE1, gdata->gesture1);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE2, gdata->gesture2);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE3, gdata->gesture3);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE_CONFIDENCE1, gdata->confidence1);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE_CONFIDENCE2, gdata->confidence2);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE_CONFIDENCE3, gdata->confidence3);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE_VALUE1, gdata->value1);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE_VALUE2, gdata->value2);
+ input_event(gesture_client_data->input_dev, EV_MSC,
+ MSC_GESTURE_VALUE3, gdata->value3);
+
+ input_sync(gesture_client_data->input_dev);
+
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long gesture_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ void __user *argp = (void __user *)arg;
+ struct gesture_client *gesture_client_data = filp->private_data;
+ unsigned char byte;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_SET_SCREEN_ON_GESTURE_STATUS:
+ /* TODO
+ Turn on/off the gesture feature on M4 */
+ break;
+ case M4_SENSOR_IOCTL_SET_SCREEN_STATUS:
+ if (copy_from_user(&byte, argp, sizeof(byte))) {
+ KDEBUG(M4SH_ERROR, "Copy frm usr err:screen status\n");
+ ret = -EFAULT;
+ break;
+ }
+ /* validate data */
+ if (byte > 1) {
+ KDEBUG(M4SH_DEBUG, "Invalid screen status=0x%x", byte);
+ ret = -EINVAL;
+ break;
+ }
+ KDEBUG(M4SH_DEBUG, "Screen status set to = 0x%x", byte);
+ ret = m4sensorhub_reg_write_1byte(
+ gesture_client_data->m4sensorhub,
+ M4SH_REG_USERSETTINGS_SCREENSTATUS, byte, 0xFF);
+ if (ret != 1)
+ KDEBUG(M4SH_ERROR, "Error writing screen status\n");
+ break;
+ default:
+ KDEBUG(M4SH_ERROR, "Invalid IOCTL Command in %s\n", __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static ssize_t m4_gesture_status(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct gesture_client *gesture_client_data = platform_get_drvdata(pdev);
+
+ if (m4_read_gesture_data(gesture_client_data) < 0)
+ KDEBUG(M4SH_ERROR, "%s: Read gesture data failed \n", __func__);
+ KDEBUG(M4SH_DEBUG, "%s:Gesture1,2,3 = %d %d %d conf1,2,3 = %d %d %d value1,2,3 = %d %d %d\n",
+ __func__, gesture_client_data->gesture_data.gesture1,
+ gesture_client_data->gesture_data.gesture2,
+ gesture_client_data->gesture_data.gesture3,
+ gesture_client_data->gesture_data.confidence1,
+ gesture_client_data->gesture_data.confidence2,
+ gesture_client_data->gesture_data.confidence3,
+ gesture_client_data->gesture_data.value1,
+ gesture_client_data->gesture_data.value2,
+ gesture_client_data->gesture_data.value3);
+
+ return sprintf(buf, "gesture1,2,3=%d %d %d,confidence1,2,3=%d %d %d value1,2,3 = %d %d %d\n",
+ gesture_client_data->gesture_data.gesture1,
+ gesture_client_data->gesture_data.gesture2,
+ gesture_client_data->gesture_data.gesture3,
+ gesture_client_data->gesture_data.confidence1,
+ gesture_client_data->gesture_data.confidence2,
+ gesture_client_data->gesture_data.confidence3,
+ gesture_client_data->gesture_data.value1,
+ gesture_client_data->gesture_data.value2,
+ gesture_client_data->gesture_data.value3);
+}
+
+static DEVICE_ATTR(gesture_status, 0444, m4_gesture_status, NULL);
+
+static const struct file_operations gesture_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = gesture_client_ioctl,
+ .open = gesture_client_open,
+ .release = gesture_client_close,
+};
+
+static struct miscdevice gesture_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = GESTURE_CLIENT_DRIVER_NAME,
+ .fops = &gesture_client_fops,
+};
+
+static int gesture_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct gesture_client *gesture_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ gesture_client_data = kzalloc(sizeof(*gesture_client_data),
+ GFP_KERNEL);
+ if (!gesture_client_data)
+ return -ENOMEM;
+
+ gesture_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, gesture_client_data);
+
+ gesture_client_data->input_dev = input_allocate_device();
+ if (!gesture_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_mem;
+ }
+
+ gesture_client_data->input_dev->name = GESTURE_CLIENT_DRIVER_NAME;
+ set_bit(EV_MSC, gesture_client_data->input_dev->evbit);
+ set_bit(MSC_GESTURE1, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE2, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE3, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE_CONFIDENCE1, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE_CONFIDENCE2, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE_CONFIDENCE3, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE_VALUE1, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE_VALUE2, gesture_client_data->input_dev->mscbit);
+ set_bit(MSC_GESTURE_VALUE3, gesture_client_data->input_dev->mscbit);
+
+ set_bit(EV_ABS, gesture_client_data->input_dev->evbit);
+ set_bit(ABS_TILTSCROLL, gesture_client_data->input_dev->absbit);
+
+ if (input_register_device(gesture_client_data->input_dev)) {
+ KDEBUG(M4SH_ERROR, "%s: input device register failed\n",
+ __func__);
+ input_free_device(gesture_client_data->input_dev);
+ goto free_mem;
+ }
+
+ ret = misc_register(&gesture_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n", __func__);
+ goto unregister_input_device;
+ }
+ misc_gesture_data = gesture_client_data;
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_GESTURE_DETECTED,
+ m4_handle_gesture_irq,
+ gesture_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_GESTURE_DETECTED, ret);
+ goto unregister_misc_device;
+ }
+ ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_GESTURE_DETECTED);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_GESTURE_DETECTED, ret);
+ goto unregister_display_gesture_irq;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_gesture_status)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto disable_display_gesture_irq;
+ }
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n", __func__);
+ return 0;
+
+disable_display_gesture_irq:
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_GESTURE_DETECTED);
+unregister_display_gesture_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_GESTURE_DETECTED);
+unregister_misc_device:
+ misc_gesture_data = NULL;
+ misc_deregister(&gesture_client_miscdrv);
+unregister_input_device:
+ input_unregister_device(gesture_client_data->input_dev);
+free_mem:
+ platform_set_drvdata(pdev, NULL);
+ gesture_client_data->m4sensorhub = NULL;
+ kfree(gesture_client_data);
+ gesture_client_data = NULL;
+ return ret;
+}
+
+static int __exit gesture_client_remove(struct platform_device *pdev)
+{
+ struct gesture_client *gesture_client_data =
+ platform_get_drvdata(pdev);
+
+ device_remove_file(&pdev->dev, &dev_attr_gesture_status);
+ m4sensorhub_irq_disable(gesture_client_data->m4sensorhub,
+ M4SH_IRQ_GESTURE_DETECTED);
+ m4sensorhub_irq_unregister(gesture_client_data->m4sensorhub,
+ M4SH_IRQ_GESTURE_DETECTED);
+ misc_gesture_data = NULL;
+ misc_deregister(&gesture_client_miscdrv);
+ input_unregister_device(gesture_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ gesture_client_data->m4sensorhub = NULL;
+ kfree(gesture_client_data);
+ gesture_client_data = NULL;
+ return 0;
+}
+
+static void gesture_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+#ifdef CONFIG_PM
+static int gesture_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ return 0;
+}
+
+static int gesture_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define gesture_client_suspend NULL
+#define gesture_client_resume NULL
+#endif
+
+static struct of_device_id m4gesture_match_tbl[] = {
+ { .compatible = "mot,m4gesture" },
+ {},
+};
+
+static struct platform_driver gesture_client_driver = {
+ .probe = gesture_client_probe,
+ .remove = __exit_p(gesture_client_remove),
+ .shutdown = gesture_client_shutdown,
+ .suspend = gesture_client_suspend,
+ .resume = gesture_client_resume,
+ .driver = {
+ .name = GESTURE_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4gesture_match_tbl),
+ },
+};
+
+static int __init gesture_client_init(void)
+{
+ return platform_driver_register(&gesture_client_driver);
+}
+
+static void __exit gesture_client_exit(void)
+{
+ platform_driver_unregister(&gesture_client_driver);
+}
+
+module_init(gesture_client_init);
+module_exit(gesture_client_exit);
+
+MODULE_ALIAS("platform:gesture_client");
+MODULE_DESCRIPTION("M4 Sensor Hub Gesture client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_mpu9150.c b/drivers/misc/m4sensorhub_mpu9150.c
new file mode 100644
index 00000000000..55195f4d955
--- /dev/null
+++ b/drivers/misc/m4sensorhub_mpu9150.c
@@ -0,0 +1,1243 @@
+/*
+ * Copyright (C) 2012 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Adds ability to program periodic interrupts from user space that
+ * can wake the phone out of low power modes.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/input.h>
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/m4sensorhub/MemMapGyroSensor.h>
+#include <linux/m4sensorhub/MemMapAccelSensor.h>
+#include <linux/m4sensorhub/MemMapCompassSensor.h>
+#include <linux/m4sensorhub/MemMapFusionSensor.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_DEBUG_FS
+#define MPU9150_DEBUG 1
+#else
+#define MPU9150_DEBUG 0
+#endif
+
+#define MPU9150_CLIENT_DRIVER_NAME "m4sensorhub_mpu9150"
+#define SENSOR_IRQ_ENABLE 1
+#define SENSOR_IRQ_DISABLE 0
+
+struct mpu9150_accel_data {
+ int x;
+ int y;
+ int z;
+};
+struct mpu9150_gyro_data {
+ int rx;
+ int ry;
+ int rz;
+};
+struct mpu9150_compass_data {
+ int cx;
+ int cy;
+ int cz;
+ int ca;
+};
+struct mpu9150_accel_local_data {
+ int lx;
+ int ly;
+ int lz;
+};
+struct mpu9150_accel_world_data {
+ int wx;
+ int wy;
+ int wz;
+};
+struct mpu9150_euler_data {
+ int roll;
+ int pitch;
+ int yaw;
+};
+struct mpu9150_heading_data {
+ int heading;
+ int accuracy;
+};
+enum mpu9150_sensor {
+ TYPE_GYRO,
+ TYPE_COMPASS,
+ TYPE_ACCEL,
+ TYPE_FUSION,
+
+ NUM_TYPES, /* Leave as last element */
+} sensor;
+
+struct mpu9150_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+ signed short samplerate[NUM_TYPES];
+ struct mpu9150_accel_data accel_data;
+ struct mpu9150_gyro_data gyro_data;
+ struct mpu9150_compass_data compass_data;
+ struct mpu9150_accel_local_data accel_local_data;
+ struct mpu9150_accel_world_data accel_world_data;
+ struct mpu9150_euler_data euler_data;
+ struct mpu9150_heading_data heading_data;
+};
+
+struct mpu9150_client *misc_mpu9150_data;
+static int mpu9150_irq_enable_disable(struct mpu9150_client *mpu9150_client_data,
+ enum mpu9150_sensor type, int flag);
+
+static int mpu9150_client_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+
+ err = nonseekable_open(inode, file);
+ if (err < 0) {
+ KDEBUG(M4SH_ERROR, "%s failed\n", __func__);
+ return err;
+ }
+ file->private_data = misc_mpu9150_data;
+
+ return 0;
+}
+
+static int mpu9150_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, "mpu9150_client in %s\n", __func__);
+ return 0;
+}
+
+static void m4_report_mpu9150_inputevent(
+ struct mpu9150_client *mpu9150_client_data,
+ enum mpu9150_sensor type)
+{
+ switch (type) {
+ case TYPE_GYRO:
+ input_report_rel(mpu9150_client_data->input_dev, REL_RX,
+ mpu9150_client_data->gyro_data.rx);
+ input_report_rel(mpu9150_client_data->input_dev, REL_RY,
+ mpu9150_client_data->gyro_data.ry);
+ input_report_rel(mpu9150_client_data->input_dev, REL_RZ,
+ mpu9150_client_data->gyro_data.rz);
+ input_sync(mpu9150_client_data->input_dev);
+ break;
+ case TYPE_ACCEL:
+ input_report_abs(mpu9150_client_data->input_dev, ABS_X,
+ mpu9150_client_data->accel_data.x);
+ input_report_abs(mpu9150_client_data->input_dev, ABS_Y,
+ mpu9150_client_data->accel_data.y);
+ input_report_abs(mpu9150_client_data->input_dev, ABS_Z,
+ mpu9150_client_data->accel_data.z);
+ input_sync(mpu9150_client_data->input_dev);
+ break;
+ case TYPE_COMPASS:
+ input_report_abs(mpu9150_client_data->input_dev, ABS_COMPASS_X,
+ mpu9150_client_data->compass_data.cx);
+ input_report_abs(mpu9150_client_data->input_dev, ABS_COMPASS_Y,
+ mpu9150_client_data->compass_data.cy);
+ input_report_abs(mpu9150_client_data->input_dev, ABS_COMPASS_Z,
+ mpu9150_client_data->compass_data.cz);
+ input_report_abs(mpu9150_client_data->input_dev,
+ ABS_COMPASS_ACCURACY,
+ mpu9150_client_data->compass_data.ca);
+ input_sync(mpu9150_client_data->input_dev);
+ break;
+ case TYPE_FUSION:
+ input_report_rel(mpu9150_client_data->input_dev, REL_X,
+ mpu9150_client_data->accel_data.x);
+ input_report_rel(mpu9150_client_data->input_dev, REL_Y,
+ mpu9150_client_data->accel_data.y);
+ input_report_rel(mpu9150_client_data->input_dev, REL_Z,
+ mpu9150_client_data->accel_data.z);
+ input_report_rel(mpu9150_client_data->input_dev, REL_GX,
+ mpu9150_client_data->gyro_data.rx);
+ input_report_rel(mpu9150_client_data->input_dev, REL_GY,
+ mpu9150_client_data->gyro_data.ry);
+ input_report_rel(mpu9150_client_data->input_dev, REL_GZ,
+ mpu9150_client_data->gyro_data.rz);
+ input_report_rel(mpu9150_client_data->input_dev, REL_LX,
+ mpu9150_client_data->accel_local_data.lx);
+ input_report_rel(mpu9150_client_data->input_dev, REL_LY,
+ mpu9150_client_data->accel_local_data.ly);
+ input_report_rel(mpu9150_client_data->input_dev, REL_LZ,
+ mpu9150_client_data->accel_local_data.lz);
+ input_report_rel(mpu9150_client_data->input_dev, REL_WX,
+ mpu9150_client_data->accel_world_data.wx);
+ input_report_rel(mpu9150_client_data->input_dev, REL_WY,
+ mpu9150_client_data->accel_world_data.wy);
+ input_report_rel(mpu9150_client_data->input_dev, REL_WZ,
+ mpu9150_client_data->accel_world_data.wz);
+ input_report_rel(mpu9150_client_data->input_dev, REL_ROLL,
+ mpu9150_client_data->euler_data.roll);
+ input_report_rel(mpu9150_client_data->input_dev, REL_PITCH,
+ mpu9150_client_data->euler_data.pitch);
+ input_report_rel(mpu9150_client_data->input_dev, REL_YAW,
+ mpu9150_client_data->euler_data.yaw);
+ input_report_rel(mpu9150_client_data->input_dev, REL_HEADING,
+ mpu9150_client_data->heading_data.heading);
+ input_report_rel(mpu9150_client_data->input_dev,
+ REL_HEADING_ACCURACY,
+ mpu9150_client_data->heading_data.accuracy);
+ input_sync(mpu9150_client_data->input_dev);
+ break;
+ default:
+ break;
+ }
+}
+
+/* TO DO
+***** implement the delay functionality when M4 changes will be ready
+*/
+static void m4_set_mpu9150_delay(struct mpu9150_client *mpu9150_client_data,
+ int delay, enum mpu9150_sensor type)
+{
+ if (delay != mpu9150_client_data->samplerate[type]) {
+ switch (type) {
+ case TYPE_GYRO:
+ m4sensorhub_reg_write(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_SAMPLERATE, (char *)&delay, m4sh_no_mask);
+ break;
+ case TYPE_ACCEL:
+ m4sensorhub_reg_write(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_SAMPLERATE, (char *)&delay, m4sh_no_mask);
+ break;
+ case TYPE_COMPASS:
+ m4sensorhub_reg_write(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_COMPASS_SAMPLERATE, (char *)&delay, m4sh_no_mask);
+ break;
+ case TYPE_FUSION:
+ m4sensorhub_reg_write(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_SAMPLERATE, (char *)&delay, m4sh_no_mask);
+ break;
+ default:
+ return;
+ break;
+ }
+ KDEBUG(M4SH_DEBUG, "%s() updating samplerate for type %d from"
+ " %d to %d\n", __func__, type,
+ mpu9150_client_data->samplerate[type],
+ delay);
+
+ mpu9150_client_data->samplerate[type] = delay;
+ }
+}
+
+static void m4_read_mpu9150_data(struct mpu9150_client *mpu9150_client_data,
+ enum mpu9150_sensor type)
+{
+ sFusionData fusiondata;
+ sCompassData compassdata;
+ sAccelData acceldata;
+ sGyroData gyrodata;
+
+ switch (type) {
+ case TYPE_GYRO:
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_X, (char *)&gyrodata.x);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_Y, (char *)&gyrodata.y);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_Z, (char *)&gyrodata.z);
+ mpu9150_client_data->gyro_data.rx = gyrodata.x;
+ mpu9150_client_data->gyro_data.ry = gyrodata.y;
+ mpu9150_client_data->gyro_data.rz = gyrodata.z;
+ break;
+ case TYPE_ACCEL:
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_X, (char *)&acceldata.x);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_Y, (char *)&acceldata.y);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_Z, (char *)&acceldata.z);
+ mpu9150_client_data->accel_data.x = acceldata.x;
+ mpu9150_client_data->accel_data.y = acceldata.y;
+ mpu9150_client_data->accel_data.z = acceldata.z;
+ break;
+ case TYPE_COMPASS:
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_COMPASS_X, (char *)&compassdata.x);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_COMPASS_Y, (char *)&compassdata.y);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_COMPASS_Z, (char *)&compassdata.z);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_COMPASS_ACCURACY,
+ (char *)&compassdata.accuracy);
+
+ mpu9150_client_data->compass_data.cx = compassdata.x;
+ mpu9150_client_data->compass_data.cy = compassdata.y;
+ mpu9150_client_data->compass_data.cz = compassdata.z;
+ mpu9150_client_data->compass_data.ca = compassdata.accuracy;
+
+ break;
+ case TYPE_FUSION:
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_X, (char *)&acceldata.x);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_Y, (char *)&acceldata.y);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_ACCEL_Z, (char *)&acceldata.z);
+ mpu9150_client_data->accel_data.x = acceldata.x;
+ mpu9150_client_data->accel_data.y = acceldata.y;
+ mpu9150_client_data->accel_data.z = acceldata.z;
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_X, (char *)&gyrodata.x);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_Y, (char *)&gyrodata.y);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_GYRO_Z, (char *)&gyrodata.z);
+ mpu9150_client_data->gyro_data.rx = gyrodata.x;
+ mpu9150_client_data->gyro_data.ry = gyrodata.y;
+ mpu9150_client_data->gyro_data.rz = gyrodata.z;
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_LOCALX, (char *)&fusiondata.localX);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_LOCALY, (char *)&fusiondata.localY);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_LOCALZ, (char *)&fusiondata.localZ);
+ mpu9150_client_data->accel_local_data.lx = fusiondata.localX;
+ mpu9150_client_data->accel_local_data.ly = fusiondata.localY;
+ mpu9150_client_data->accel_local_data.lz = fusiondata.localZ;
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_WORLDX, (char *)&fusiondata.worldX);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_WORLDY, (char *)&fusiondata.worldY);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_WORLDZ, (char *)&fusiondata.worldZ);
+ mpu9150_client_data->accel_world_data.wx = fusiondata.worldX;
+ mpu9150_client_data->accel_world_data.wy = fusiondata.worldY;
+ mpu9150_client_data->accel_world_data.wz = fusiondata.worldZ;
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_EULERPITCH,
+ (char *)&fusiondata.eulerPitch);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_EULERROLL,
+ (char *)&fusiondata.eulerRoll);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_EULERYAW,
+ (char *)&fusiondata.eulerYaw);
+ mpu9150_client_data->euler_data.pitch = fusiondata.eulerPitch;
+ mpu9150_client_data->euler_data.roll = fusiondata.eulerRoll;
+ mpu9150_client_data->euler_data.yaw = fusiondata.eulerYaw;
+
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_HEADING,
+ (char *)&fusiondata.heading);
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_FUSION_HEADING_ACCURACY,
+ (char *)&fusiondata.heading_accuracy);
+ mpu9150_client_data->heading_data.heading = fusiondata.heading;
+ mpu9150_client_data->heading_data.accuracy =
+ fusiondata.heading_accuracy;
+
+ break;
+ default:
+ break;
+ }
+
+}
+static void m4_handle_mpu9150_gyro_irq(enum m4sensorhub_irqs int_event,
+ void *mpu9150_data)
+{
+ struct mpu9150_client *mpu9150_client_data = mpu9150_data;
+
+ m4_read_mpu9150_data(mpu9150_client_data, TYPE_GYRO);
+ m4_report_mpu9150_inputevent(mpu9150_client_data, TYPE_GYRO);
+}
+
+static void m4_handle_mpu9150_accel_irq(enum m4sensorhub_irqs int_event,
+ void *mpu9150_data)
+{
+ struct mpu9150_client *mpu9150_client_data = mpu9150_data;
+
+ m4_read_mpu9150_data(mpu9150_client_data, TYPE_ACCEL);
+ m4_report_mpu9150_inputevent(mpu9150_client_data, TYPE_ACCEL);
+}
+
+static void m4_handle_mpu9150_compass_irq(enum m4sensorhub_irqs int_event,
+ void *mpu9150_data)
+{
+ struct mpu9150_client *mpu9150_client_data = mpu9150_data;
+
+ m4_read_mpu9150_data(mpu9150_client_data, TYPE_COMPASS);
+ m4_report_mpu9150_inputevent(mpu9150_client_data, TYPE_COMPASS);
+}
+
+static void m4_handle_mpu9150_fusion_irq(enum m4sensorhub_irqs int_event,
+ void *mpu9150_data)
+{
+ struct mpu9150_client *mpu9150_client_data = mpu9150_data;
+
+ m4_read_mpu9150_data(mpu9150_client_data, TYPE_FUSION);
+ m4_report_mpu9150_inputevent(mpu9150_client_data, TYPE_FUSION);
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long mpu9150_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ int delay;
+ static int status;
+ void __user *argp = (void __user *)arg;
+ struct mpu9150_client *mpu9150_client_data = filp->private_data;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_APP_GET_FLAG:
+ if (copy_to_user(argp, &status, sizeof(status)))
+ return -EFAULT;
+ break;
+ case M4_SENSOR_IOCTL_APP_SET_FLAG:
+ if (copy_from_user(&status, argp, sizeof(status)))
+ return -EFAULT;
+ break;
+ case M4_SENSOR_IOCTL_GYRO_SET_DELAY:
+ if (copy_from_user(&delay, argp, sizeof(delay)))
+ return -EFAULT;
+ m4_set_mpu9150_delay(mpu9150_client_data, delay, TYPE_GYRO);
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_GYRO,
+ SENSOR_IRQ_ENABLE);
+ break;
+ case M4_SENSOR_IOCTL_COMPASS_SET_DELAY:
+ if (copy_from_user(&delay, argp, sizeof(delay)))
+ return -EFAULT;
+ m4_set_mpu9150_delay(mpu9150_client_data, delay, TYPE_COMPASS);
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_COMPASS,
+ SENSOR_IRQ_ENABLE);
+ break;
+ case M4_SENSOR_IOCTL_ACCEL_SET_DELAY:
+ if (copy_from_user(&delay, argp, sizeof(delay)))
+ return -EFAULT;
+ m4_set_mpu9150_delay(mpu9150_client_data, delay, TYPE_ACCEL);
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_ACCEL,
+ SENSOR_IRQ_ENABLE);
+ break;
+ case M4_SENSOR_IOCTL_FUSION_SET_DELAY:
+ if (copy_from_user(&delay, argp, sizeof(delay)))
+ return -EFAULT;
+ if (delay >= 0)
+ m4_set_mpu9150_delay(mpu9150_client_data, delay, TYPE_FUSION);
+ if (delay)
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_FUSION,
+ SENSOR_IRQ_ENABLE);
+ else
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_FUSION,
+ SENSOR_IRQ_DISABLE);
+ break;
+ default:
+ KDEBUG(M4SH_ERROR, "Invalid IOCTL Command in %s\n", __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+#ifdef MPU9150_DEBUG
+static ssize_t m4_mpu9150_local_x(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : x_local = %d\n",
+ __func__, mpu9150_client_data->accel_local_data.lx);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_local_data.lx);
+}
+
+static ssize_t m4_mpu9150_local_y(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : y_local = %d\n",
+ __func__, mpu9150_client_data->accel_local_data.ly);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_local_data.ly);
+}
+
+static ssize_t m4_mpu9150_local_z(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : z_local = %d\n",
+ __func__, mpu9150_client_data->accel_local_data.lz);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_local_data.lz);
+}
+static ssize_t m4_mpu9150_world_x(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : x_world = %d\n",
+ __func__, mpu9150_client_data->accel_world_data.wx);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_world_data.wx);
+}
+
+static ssize_t m4_mpu9150_world_y(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : y_world = %d\n",
+ __func__, mpu9150_client_data->accel_world_data.wy);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_world_data.wy);
+}
+
+static ssize_t m4_mpu9150_world_z(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : z_world = %d\n",
+ __func__, mpu9150_client_data->accel_world_data.wz);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_world_data.wz);
+}
+static ssize_t m4_mpu9150_pitch(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : pitch = %d\n",
+ __func__, mpu9150_client_data->euler_data.pitch);
+ return sprintf(buf, "%d \n", mpu9150_client_data->euler_data.pitch);
+}
+
+static ssize_t m4_mpu9150_roll(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : roll = %d\n",
+ __func__, mpu9150_client_data->euler_data.roll);
+ return sprintf(buf, "%d \n", mpu9150_client_data->euler_data.roll);
+}
+
+static ssize_t m4_mpu9150_yaw(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : yaw = %d\n",
+ __func__, mpu9150_client_data->euler_data.yaw);
+ return sprintf(buf, "%d \n", mpu9150_client_data->euler_data.yaw);
+}
+
+static ssize_t m4_mpu9150_heading(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : heading = %d\n",
+ __func__, mpu9150_client_data->heading_data.heading);
+ return sprintf(buf, "%d \n", mpu9150_client_data->heading_data.heading);
+}
+
+static ssize_t m4_mpu9150_heading_acc(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : heading_acc = %d\n",
+ __func__, mpu9150_client_data->heading_data.accuracy);
+ return sprintf(buf, "%d\n", mpu9150_client_data->heading_data.accuracy);
+}
+
+static ssize_t m4_mpu9150_x(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : raw x = %d\n",
+ __func__, mpu9150_client_data->accel_data.x);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_data.x);
+}
+
+static ssize_t m4_mpu9150_y(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : raw y = %d\n",
+ __func__, mpu9150_client_data->accel_data.y);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_data.y);
+}
+
+static ssize_t m4_mpu9150_z(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : raw z = %d\n",
+ __func__, mpu9150_client_data->accel_data.z);
+ return sprintf(buf, "%d \n", mpu9150_client_data->accel_data.z);
+}
+
+static ssize_t m4_mpu9150_cx(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : compass cx = %d\n",
+ __func__, mpu9150_client_data->compass_data.cx);
+ return sprintf(buf, "%d \n", mpu9150_client_data->compass_data.cx);
+}
+
+static ssize_t m4_mpu9150_cy(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : compass cy = %d\n",
+ __func__, mpu9150_client_data->compass_data.cy);
+ return sprintf(buf, "%d \n", mpu9150_client_data->compass_data.cy);
+}
+
+static ssize_t m4_mpu9150_cz(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : compass cz = %d\n",
+ __func__, mpu9150_client_data->compass_data.cz);
+ return sprintf(buf, "%d \n", mpu9150_client_data->compass_data.cz);
+}
+
+static ssize_t m4_mpu9150_ca(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : compass ca = %d\n",
+ __func__, mpu9150_client_data->compass_data.ca);
+ return sprintf(buf, "%d \n", mpu9150_client_data->compass_data.ca);
+}
+
+static ssize_t m4_mpu9150_rx(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : rx = %d\n",
+ __func__, mpu9150_client_data->gyro_data.rx);
+ return sprintf(buf, "%d \n", mpu9150_client_data->gyro_data.rx);
+}
+static ssize_t m4_mpu9150_ry(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : ry = %d\n",
+ __func__, mpu9150_client_data->gyro_data.ry);
+ return sprintf(buf, "%d \n", mpu9150_client_data->gyro_data.ry);
+}
+
+static ssize_t m4_mpu9150_rz(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : rz = %d\n",
+ __func__, mpu9150_client_data->gyro_data.rz);
+ return sprintf(buf, "%d \n", mpu9150_client_data->gyro_data.rz);
+}
+
+static int mpu9150_set_loglevel(struct mpu9150_client *mpu9150_client_data,
+ unsigned long long level, enum mpu9150_sensor type)
+{
+ unsigned long long mask;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %llu !!!\n", level);
+ return -1;
+ }
+ switch (type) {
+ case TYPE_GYRO:
+ mask = (1ULL << GYRO_MASK_BIT_1) | (1ULL << GYRO_MASK_BIT_2);
+ level = (level << GYRO_MASK_BIT_1);
+ break;
+ case TYPE_ACCEL:
+ mask = (1ULL << ACCEL_MASK_BIT_1) | (1ULL << ACCEL_MASK_BIT_2);
+ level = (level << ACCEL_MASK_BIT_1);
+ break;
+ case TYPE_COMPASS:
+ mask = (1ULL << COMPASS_MASK_BIT_1) | (1ULL << COMPASS_MASK_BIT_2);
+ level = (level << COMPASS_MASK_BIT_1);
+ break;
+ case TYPE_FUSION:
+ mask = (1ULL << FUSION_MASK_BIT_1) | (1ULL << FUSION_MASK_BIT_2);
+ level = (level << FUSION_MASK_BIT_1);
+ break;
+ default:
+ return -1;
+ }
+ return m4sensorhub_reg_write(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&level, (unsigned char *)&mask);
+}
+
+static ssize_t gyro_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ return mpu9150_set_loglevel(mpu9150_client_data, level, TYPE_GYRO);
+}
+
+static ssize_t gyro_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long long loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, GYRO_MASK_BIT_1);
+ return sprintf(buf, "%llu\n", loglevel);
+}
+
+static ssize_t accel_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ return mpu9150_set_loglevel(mpu9150_client_data, level, TYPE_ACCEL);
+}
+
+static ssize_t accel_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, ACCEL_MASK_BIT_1);
+ return sprintf(buf, "%d\n", loglevel);
+}
+
+static ssize_t compass_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ return mpu9150_set_loglevel(mpu9150_client_data, level, TYPE_COMPASS);
+}
+
+static ssize_t compass_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, COMPASS_MASK_BIT_1);
+ return sprintf(buf, "%d\n", loglevel);
+}
+
+static ssize_t fusion_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ return mpu9150_set_loglevel(mpu9150_client_data, level, TYPE_FUSION);
+}
+static ssize_t fusion_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mpu9150_client *mpu9150_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(mpu9150_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, FUSION_MASK_BIT_1);
+ return sprintf(buf, "%d\n", loglevel);
+}
+
+static DEVICE_ATTR(x_local, 0444, m4_mpu9150_local_x, NULL);
+static DEVICE_ATTR(y_local, 0444, m4_mpu9150_local_y, NULL);
+static DEVICE_ATTR(z_local, 0444, m4_mpu9150_local_z, NULL);
+static DEVICE_ATTR(x_world, 0444, m4_mpu9150_world_x, NULL);
+static DEVICE_ATTR(y_world, 0444, m4_mpu9150_world_y, NULL);
+static DEVICE_ATTR(z_world, 0444, m4_mpu9150_world_z, NULL);
+static DEVICE_ATTR(pitch, 0444, m4_mpu9150_pitch, NULL);
+static DEVICE_ATTR(roll, 0444, m4_mpu9150_roll, NULL);
+static DEVICE_ATTR(yaw, 0444, m4_mpu9150_yaw, NULL);
+static DEVICE_ATTR(heading, 0444, m4_mpu9150_heading, NULL);
+static DEVICE_ATTR(heading_acc, 0444, m4_mpu9150_heading_acc, NULL);
+static DEVICE_ATTR(raw_x, 0444, m4_mpu9150_x, NULL);
+static DEVICE_ATTR(raw_y, 0444, m4_mpu9150_y, NULL);
+static DEVICE_ATTR(raw_z, 0444, m4_mpu9150_z, NULL);
+static DEVICE_ATTR(compass_cx, 0444, m4_mpu9150_cx, NULL);
+static DEVICE_ATTR(compass_cy, 0444, m4_mpu9150_cy, NULL);
+static DEVICE_ATTR(compass_cz, 0444, m4_mpu9150_cz, NULL);
+static DEVICE_ATTR(compass_ca, 0444, m4_mpu9150_ca, NULL);
+static DEVICE_ATTR(rx, 0444, m4_mpu9150_rx, NULL);
+static DEVICE_ATTR(ry, 0444, m4_mpu9150_ry, NULL);
+static DEVICE_ATTR(rz, 0444, m4_mpu9150_rz, NULL);
+static DEVICE_ATTR(gyroLogLevel, 0644, gyro_get_loglevel,
+ gyro_set_loglevel);
+static DEVICE_ATTR(accelLogLevel, 0644, accel_get_loglevel,
+ accel_set_loglevel);
+static DEVICE_ATTR(compassLogLevel, 0644, compass_get_loglevel,
+ compass_set_loglevel);
+static DEVICE_ATTR(fusionLogLevel, 0644, fusion_get_loglevel,
+ fusion_set_loglevel);
+
+static struct attribute *mpu9150_attributes[] = {
+ &dev_attr_x_local.attr,
+ &dev_attr_y_local.attr,
+ &dev_attr_z_local.attr,
+ &dev_attr_x_world.attr,
+ &dev_attr_y_world.attr,
+ &dev_attr_z_world.attr,
+ &dev_attr_pitch.attr,
+ &dev_attr_roll.attr,
+ &dev_attr_yaw.attr,
+ &dev_attr_heading.attr,
+ &dev_attr_heading_acc.attr,
+ &dev_attr_raw_x.attr,
+ &dev_attr_raw_y.attr,
+ &dev_attr_raw_z.attr,
+ &dev_attr_compass_cx.attr,
+ &dev_attr_compass_cy.attr,
+ &dev_attr_compass_cz.attr,
+ &dev_attr_compass_ca.attr,
+ &dev_attr_rx.attr,
+ &dev_attr_ry.attr,
+ &dev_attr_rz.attr,
+ &dev_attr_gyroLogLevel.attr,
+ &dev_attr_accelLogLevel.attr,
+ &dev_attr_compassLogLevel.attr,
+ &dev_attr_fusionLogLevel.attr,
+ NULL
+};
+
+static const struct attribute_group mpu9150_group = {
+ .attrs = mpu9150_attributes,
+};
+#endif
+
+static const struct file_operations mpu9150_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = mpu9150_client_ioctl,
+ .open = mpu9150_client_open,
+ .release = mpu9150_client_close,
+};
+
+static struct miscdevice mpu9150_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = MPU9150_CLIENT_DRIVER_NAME,
+ .fops = &mpu9150_client_fops,
+};
+
+static int mpu9150_irq_init(struct mpu9150_client *mpu9150_client_data)
+{
+ int ret = -1;
+
+ ret = m4sensorhub_irq_register(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_GYRO_DATA_READY,
+ m4_handle_mpu9150_gyro_irq,
+ mpu9150_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_GYRO_DATA_READY, ret);
+ return ret;
+ }
+ ret = m4sensorhub_irq_register(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_ACCEL_DATA_READY,
+ m4_handle_mpu9150_accel_irq,
+ mpu9150_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_ACCEL_DATA_READY, ret);
+ goto unregister_gyro_irq;
+ }
+ ret = m4sensorhub_irq_register(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_COMPASS_DATA_READY,
+ m4_handle_mpu9150_compass_irq,
+ mpu9150_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_COMPASS_DATA_READY, ret);
+ goto unregister_accel_irq;
+ }
+ ret = m4sensorhub_irq_register(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_FUSION_DATA_READY,
+ m4_handle_mpu9150_fusion_irq,
+ mpu9150_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_FUSION_DATA_READY, ret);
+ goto unregister_compass_irq;
+ }
+ return ret;
+
+unregister_compass_irq:
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_COMPASS_DATA_READY);
+unregister_accel_irq:
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_ACCEL_DATA_READY);
+unregister_gyro_irq:
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_GYRO_DATA_READY);
+ return ret;
+}
+
+static void mpu9150_irq_deinit(struct mpu9150_client *mpu9150_client_data)
+{
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_FUSION_DATA_READY);
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_COMPASS_DATA_READY);
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_ACCEL_DATA_READY);
+ m4sensorhub_irq_unregister(mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_GYRO_DATA_READY);
+}
+
+static int mpu9150_irq_enable_disable(struct mpu9150_client *mpu9150_client_data,
+ enum mpu9150_sensor type, int flag)
+{
+ int ret = 0;
+ int irq_status = 0;
+
+ switch (type) {
+ case TYPE_GYRO:
+ irq_status = m4sensorhub_irq_enable_get(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_GYRO_DATA_READY);
+ if (flag && (!irq_status)) {
+ ret = m4sensorhub_irq_enable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_GYRO_DATA_READY);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_GYRO_DATA_READY, ret);
+ return ret;
+ }
+ } else if ((!flag) && irq_status)
+ m4sensorhub_irq_disable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_GYRO_DATA_READY);
+ break;
+ case TYPE_ACCEL:
+ irq_status = m4sensorhub_irq_enable_get(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_ACCEL_DATA_READY);
+ if (flag && (!irq_status)) {
+ ret = m4sensorhub_irq_enable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_ACCEL_DATA_READY);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_ACCEL_DATA_READY, ret);
+ return ret;
+ }
+ } else if ((!flag) && irq_status)
+ m4sensorhub_irq_disable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_ACCEL_DATA_READY);
+ break;
+ case TYPE_COMPASS:
+ irq_status = m4sensorhub_irq_enable_get(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_COMPASS_DATA_READY);
+ if (flag && (!irq_status)) {
+ ret = m4sensorhub_irq_enable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_COMPASS_DATA_READY);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_COMPASS_DATA_READY, ret);
+ return ret;
+ }
+ } else if ((!flag) && irq_status)
+ m4sensorhub_irq_disable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_COMPASS_DATA_READY);
+ break;
+ case TYPE_FUSION:
+ irq_status = m4sensorhub_irq_enable_get(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_FUSION_DATA_READY);
+ if (flag && (!irq_status)) {
+ ret = m4sensorhub_irq_enable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_FUSION_DATA_READY);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_FUSION_DATA_READY, ret);
+ return ret;
+ }
+ } else if ((!flag) && irq_status)
+ m4sensorhub_irq_disable(
+ mpu9150_client_data->m4sensorhub,
+ M4SH_IRQ_FUSION_DATA_READY);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int mpu9150_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct mpu9150_client *mpu9150_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ mpu9150_client_data = kzalloc(sizeof(*mpu9150_client_data),
+ GFP_KERNEL);
+ if (!mpu9150_client_data)
+ return -ENOMEM;
+
+ mpu9150_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, mpu9150_client_data);
+
+ mpu9150_client_data->input_dev = input_allocate_device();
+ if (!mpu9150_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_mem;
+ }
+
+ mpu9150_client_data->input_dev->name = MPU9150_CLIENT_DRIVER_NAME;
+ set_bit(EV_ABS, mpu9150_client_data->input_dev->evbit);
+ set_bit(EV_REL, mpu9150_client_data->input_dev->evbit);
+
+ set_bit(ABS_COMPASS_X, mpu9150_client_data->input_dev->absbit);
+ set_bit(ABS_COMPASS_Y, mpu9150_client_data->input_dev->absbit);
+ set_bit(ABS_COMPASS_Z, mpu9150_client_data->input_dev->absbit);
+ set_bit(ABS_COMPASS_ACCURACY, mpu9150_client_data->input_dev->absbit);
+ set_bit(ABS_X, mpu9150_client_data->input_dev->absbit);
+ set_bit(ABS_Y, mpu9150_client_data->input_dev->absbit);
+ set_bit(ABS_Z, mpu9150_client_data->input_dev->absbit);
+
+ set_bit(REL_X, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_Y, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_Z, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_GX, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_GY, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_GZ, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_LX, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_LY, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_LZ, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_WX, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_WY, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_WZ, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_ROLL, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_PITCH, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_YAW, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_RX, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_RY, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_RZ, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_HEADING, mpu9150_client_data->input_dev->relbit);
+ set_bit(REL_HEADING_ACCURACY, mpu9150_client_data->input_dev->relbit);
+
+ if (input_register_device(mpu9150_client_data->input_dev)) {
+ KDEBUG(M4SH_ERROR, "%s: input device register failed\n",
+ __func__);
+ input_free_device(mpu9150_client_data->input_dev);
+ goto free_mem;
+ }
+
+ ret = misc_register(&mpu9150_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n", __func__);
+ goto unregister_input_device;
+ }
+ misc_mpu9150_data = mpu9150_client_data;
+ ret = mpu9150_irq_init(mpu9150_client_data);
+ if (ret < 0)
+ goto unregister_misc_device;
+
+#ifdef MPU9150_DEBUG
+ ret = sysfs_create_group(&pdev->dev.kobj, &mpu9150_group);
+ if (ret)
+ goto unregister_irq;
+#endif
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n", __func__);
+ return 0;
+
+#ifdef MPU9150_DEBUG
+unregister_irq:
+ mpu9150_irq_deinit(mpu9150_client_data);
+#endif
+unregister_misc_device:
+ misc_mpu9150_data = NULL;
+ misc_deregister(&mpu9150_client_miscdrv);
+unregister_input_device:
+ input_unregister_device(mpu9150_client_data->input_dev);
+free_mem:
+ platform_set_drvdata(pdev, NULL);
+ mpu9150_client_data->m4sensorhub = NULL;
+ kfree(mpu9150_client_data);
+ mpu9150_client_data = NULL;
+ return ret;
+}
+
+static int __exit mpu9150_client_remove(struct platform_device *pdev)
+{
+ struct mpu9150_client *mpu9150_client_data =
+ platform_get_drvdata(pdev);
+#ifdef MPU9150_DEBUG
+ sysfs_remove_group(&pdev->dev.kobj, &mpu9150_group);
+#endif
+ mpu9150_irq_deinit(mpu9150_client_data);
+ misc_mpu9150_data = NULL;
+ misc_deregister(&mpu9150_client_miscdrv);
+ input_unregister_device(mpu9150_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ mpu9150_client_data->m4sensorhub = NULL;
+ kfree(mpu9150_client_data);
+ mpu9150_client_data = NULL;
+ return 0;
+}
+
+static void mpu9150_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+#ifdef CONFIG_PM
+static int mpu9150_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ struct mpu9150_client *mpu9150_client_data =
+ platform_get_drvdata(pdev);
+
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_COMPASS,
+ SENSOR_IRQ_DISABLE);
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_GYRO,
+ SENSOR_IRQ_DISABLE);
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_ACCEL,
+ SENSOR_IRQ_DISABLE);
+ mpu9150_irq_enable_disable(mpu9150_client_data, TYPE_FUSION,
+ SENSOR_IRQ_DISABLE);
+
+ m4_set_mpu9150_delay(mpu9150_client_data, -1, TYPE_COMPASS);
+ m4_set_mpu9150_delay(mpu9150_client_data, -1, TYPE_GYRO);
+ m4_set_mpu9150_delay(mpu9150_client_data, -1, TYPE_ACCEL);
+ m4_set_mpu9150_delay(mpu9150_client_data, -1, TYPE_FUSION);
+
+ return 0;
+}
+
+static int mpu9150_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define mpu9150_client_suspend NULL
+#define mpu9150_client_resume NULL
+#endif
+
+static struct of_device_id m4mpu9150_match_tbl[] = {
+ { .compatible = "mot,m4mpu9150" },
+ {},
+};
+
+static struct platform_driver mpu9150_client_driver = {
+ .probe = mpu9150_client_probe,
+ .remove = __exit_p(mpu9150_client_remove),
+ .shutdown = mpu9150_client_shutdown,
+ .suspend = mpu9150_client_suspend,
+ .resume = mpu9150_client_resume,
+ .driver = {
+ .name = MPU9150_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4mpu9150_match_tbl),
+ },
+};
+
+static int __init mpu9150_client_init(void)
+{
+ return platform_driver_register(&mpu9150_client_driver);
+}
+
+static void __exit mpu9150_client_exit(void)
+{
+ platform_driver_unregister(&mpu9150_client_driver);
+}
+
+module_init(mpu9150_client_init);
+module_exit(mpu9150_client_exit);
+
+MODULE_ALIAS("platform:mpu9150_client");
+MODULE_DESCRIPTION("M4 Sensor Hub Mpu9150 client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/m4sensorhub_passive.c b/drivers/misc/m4sensorhub_passive.c
new file mode 100644
index 00000000000..86c58fcc7e2
--- /dev/null
+++ b/drivers/misc/m4sensorhub_passive.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2012 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Adds ability to program periodic interrupts from user space that
+ * can wake the phone out of low power modes.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/input.h>
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub/MemMapPassive.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#define PASSIVE_CLIENT_DRIVER_NAME "m4sensorhub_passive"
+
+struct m4_passive_data {
+ u32 mets;
+ u32 steps;
+ u32 floorsClimbed;
+ u32 timestamp;
+};
+
+struct passive_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+};
+
+static struct passive_client *misc_passive_data;
+static struct m4_passive_data pdata_buffer[MAX_PASSIVE_BUFFERS] = {};
+
+static int passive_client_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+
+ err = nonseekable_open(inode, file);
+ if (err < 0) {
+ KDEBUG(M4SH_ERROR, "%s failed\n", __func__);
+ return err;
+ }
+ file->private_data = misc_passive_data;
+
+ return 0;
+}
+
+static int passive_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, "passive_client in %s\n", __func__);
+ return 0;
+}
+
+static void m4_report_passive_inputevent(
+ struct passive_client *passive_client_data)
+{
+ int i;
+ for (i = 0; i < MAX_PASSIVE_BUFFERS; i++) {
+ input_event(passive_client_data->input_dev, EV_MSC,
+ MSC_PASSIVE_STEPS,
+ pdata_buffer[i].steps);
+ input_event(passive_client_data->input_dev, EV_MSC,
+ MSC_PASSIVE_METS,
+ pdata_buffer[i].mets);
+ input_event(passive_client_data->input_dev, EV_MSC,
+ MSC_PASSIVE_TIMESTAMP,
+ pdata_buffer[i].timestamp);
+ input_event(passive_client_data->input_dev, EV_MSC,
+ MSC_PASSIVE_FLOORSCLIMBED,
+ pdata_buffer[i].floorsClimbed);
+ input_sync(passive_client_data->input_dev);
+ }
+}
+
+
+static void m4_read_passive_data(struct passive_client *passive_client_data)
+{
+ int i;
+ u32 steps[MAX_PASSIVE_BUFFERS] = {0};
+ u32 mets[MAX_PASSIVE_BUFFERS] = {0};
+ u32 timestamp[12] = {0};
+ u32 floorsClimbed[MAX_PASSIVE_BUFFERS] = {0};
+
+ /*read all buffers of steps*/
+ m4sensorhub_reg_read(passive_client_data->m4sensorhub,
+ M4SH_REG_PASSIVE_STEPS,
+ (char *)&steps);
+ m4sensorhub_reg_read(passive_client_data->m4sensorhub,
+ M4SH_REG_PASSIVE_METS,
+ (char *)&mets);
+ m4sensorhub_reg_read(passive_client_data->m4sensorhub,
+ M4SH_REG_PASSIVE_TIMESTAMP,
+ (char *)&timestamp);
+ m4sensorhub_reg_read(passive_client_data->m4sensorhub,
+ M4SH_REG_PASSIVE_FLOORSCLIMBED,
+ (char *)&floorsClimbed);
+ for (i = 0; i < MAX_PASSIVE_BUFFERS; i++) {
+ pdata_buffer[i].steps = steps[i];
+ pdata_buffer[i].mets = mets[i];
+ pdata_buffer[i].timestamp = timestamp[i];
+ pdata_buffer[i].floorsClimbed = floorsClimbed[i];
+ KDEBUG(M4SH_DEBUG, "steps = %u, mets = %u, timestamp = %u,\
+ floorsClimbed = %u", pdata_buffer[i].steps,
+ pdata_buffer[i].mets, pdata_buffer[i].timestamp,
+ pdata_buffer[i].floorsClimbed);
+ }
+}
+
+static void m4_handle_passive_irq(enum m4sensorhub_irqs int_event,
+ void *passive_data)
+{
+ struct passive_client *passive_client_data = passive_data;
+
+ m4_read_passive_data(passive_client_data);
+ m4_report_passive_inputevent(passive_client_data);
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long passive_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ struct passive_client *passive_client_data = filp->private_data;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_GET_PASSIVE_DATA:
+ m4_read_passive_data(passive_client_data);
+ m4_report_passive_inputevent(passive_client_data);
+ break;
+ default:
+ KDEBUG(M4SH_ERROR, "Invalid IOCTL Command in %s\n", __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static const struct file_operations passive_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = passive_client_ioctl,
+ .open = passive_client_open,
+ .release = passive_client_close,
+};
+
+static struct miscdevice passive_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = PASSIVE_CLIENT_DRIVER_NAME,
+ .fops = &passive_client_fops,
+};
+
+static int passive_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct passive_client *passive_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ passive_client_data = kzalloc(sizeof(*passive_client_data),
+ GFP_KERNEL);
+ if (!passive_client_data)
+ return -ENOMEM;
+
+ passive_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, passive_client_data);
+
+ passive_client_data->input_dev = input_allocate_device();
+ if (!passive_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_mem;
+ }
+
+ passive_client_data->input_dev->name = PASSIVE_CLIENT_DRIVER_NAME;
+ set_bit(EV_MSC, passive_client_data->input_dev->evbit);
+ set_bit(MSC_PASSIVE_STEPS, passive_client_data->input_dev->mscbit);
+ set_bit(MSC_PASSIVE_METS, passive_client_data->input_dev->mscbit);
+ set_bit(MSC_PASSIVE_TIMESTAMP, passive_client_data->input_dev->mscbit);
+ set_bit(MSC_PASSIVE_FLOORSCLIMBED,
+ passive_client_data->input_dev->mscbit);
+ if (input_register_device(passive_client_data->input_dev)) {
+ KDEBUG(M4SH_ERROR, "%s: input device register failed\n",
+ __func__);
+ input_free_device(passive_client_data->input_dev);
+ goto free_mem;
+ }
+
+ ret = misc_register(&passive_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n", __func__);
+ goto unregister_input_device;
+ }
+ misc_passive_data = passive_client_data;
+
+ ret = m4sensorhub_irq_register(m4sensorhub,
+ M4SH_IRQ_PASSIVE_BUFFER_FULL,
+ m4_handle_passive_irq,
+ passive_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_PASSIVE_BUFFER_FULL, ret);
+ goto unregister_misc_device;
+ }
+ ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_PASSIVE_BUFFER_FULL);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_PASSIVE_BUFFER_FULL, ret);
+ goto unregister_irq;
+ }
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n", __func__);
+ return 0;
+
+unregister_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_PASSIVE_BUFFER_FULL);
+unregister_misc_device:
+ misc_passive_data = NULL;
+ misc_deregister(&passive_client_miscdrv);
+unregister_input_device:
+ input_unregister_device(passive_client_data->input_dev);
+free_mem:
+ platform_set_drvdata(pdev, NULL);
+ passive_client_data->m4sensorhub = NULL;
+ kfree(passive_client_data);
+ passive_client_data = NULL;
+ return ret;
+}
+
+static int __exit passive_client_remove(struct platform_device *pdev)
+{
+ struct passive_client *passive_client_data =
+ platform_get_drvdata(pdev);
+
+ m4sensorhub_irq_disable(passive_client_data->m4sensorhub,
+ M4SH_IRQ_PASSIVE_BUFFER_FULL);
+ m4sensorhub_irq_unregister(passive_client_data->m4sensorhub,
+ M4SH_IRQ_PASSIVE_BUFFER_FULL);
+
+ misc_passive_data = NULL;
+ misc_deregister(&passive_client_miscdrv);
+ input_unregister_device(passive_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ passive_client_data->m4sensorhub = NULL;
+ kfree(passive_client_data);
+ passive_client_data = NULL;
+ return 0;
+}
+
+static void passive_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+#ifdef CONFIG_PM
+static int passive_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ return 0;
+}
+
+static int passive_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define passive_client_suspend NULL
+#define passive_client_resume NULL
+#endif
+
+static struct of_device_id m4passive_match_tbl[] = {
+ { .compatible = "mot,m4passive" },
+ {},
+};
+
+static struct platform_driver passive_client_driver = {
+ .probe = passive_client_probe,
+ .remove = __exit_p(passive_client_remove),
+ .shutdown = passive_client_shutdown,
+ .suspend = passive_client_suspend,
+ .resume = passive_client_resume,
+ .driver = {
+ .name = PASSIVE_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4passive_match_tbl),
+ },
+};
+
+static int __init passive_client_init(void)
+{
+ return platform_driver_register(&passive_client_driver);
+}
+
+static void __exit passive_client_exit(void)
+{
+ platform_driver_unregister(&passive_client_driver);
+}
+
+module_init(passive_client_init);
+module_exit(passive_client_exit);
+
+MODULE_ALIAS("platform:passive_client");
+MODULE_DESCRIPTION("M4 Sensor Hub Passive mode client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_pedometer.c b/drivers/misc/m4sensorhub_pedometer.c
new file mode 100644
index 00000000000..9a6707fba0c
--- /dev/null
+++ b/drivers/misc/m4sensorhub_pedometer.c
@@ -0,0 +1,689 @@
+/*
+ * Copyright (C) 2012 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Adds ability to program periodic interrupts from user space that
+ * can wake the phone out of low power modes.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/input.h>
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/m4sensorhub/MemMapPedometer.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+
+#define PEDOMETER_CLIENT_DRIVER_NAME "m4sensorhub_pedometer"
+
+struct pedometer_data {
+ unsigned char activity;
+ unsigned int distance;
+ unsigned int mets;
+ unsigned char metsactivity;
+ unsigned int calories;
+ unsigned short stepcount;
+ unsigned short speed;
+ unsigned short floorsclimbed;
+};
+
+struct pedometer_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+ struct pedometer_data prev_data;
+ struct pedometer_data curr_data;
+};
+
+struct pedometer_client *misc_pedometer_data;
+
+static int pedometer_client_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+
+ err = nonseekable_open(inode, file);
+ if (err < 0) {
+ KDEBUG(M4SH_ERROR, "%s failed\n", __func__);
+ return err;
+ }
+ file->private_data = misc_pedometer_data;
+
+ return 0;
+}
+
+static int pedometer_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, "pedometer_client in %s\n", __func__);
+ return 0;
+}
+
+static void m4_report_pedometer_inputevent(
+ struct pedometer_client *pedo_client_data)
+{
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_ACTIVITY_TYPE,
+ pedo_client_data->curr_data.activity);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_STEPCOUNT,
+ pedo_client_data->curr_data.stepcount);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_DISTANCE,
+ pedo_client_data->curr_data.distance);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_SPEED,
+ pedo_client_data->curr_data.speed);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_METS,
+ pedo_client_data->curr_data.mets);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_CALORIES,
+ pedo_client_data->curr_data.calories);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_FLOORSCLIMBED,
+ pedo_client_data->curr_data.floorsclimbed);
+ input_event(pedo_client_data->input_dev, EV_MSC, MSC_METSACTIVITY,
+ pedo_client_data->curr_data.metsactivity);
+ input_sync(pedo_client_data->input_dev);
+
+ KDEBUG(M4SH_DEBUG, "Sending pedometer data : stepcount = %d,\
+ speed = %d,distance = %d,mets = %d,calories = %d, \
+ activity = %d,floorsclimbed = %d, metsactivity = %d\n",
+ pedo_client_data->curr_data.stepcount,
+ pedo_client_data->curr_data.speed,
+ pedo_client_data->curr_data.distance,
+ pedo_client_data->curr_data.mets,
+ pedo_client_data->curr_data.calories,
+ pedo_client_data->curr_data.activity,
+ pedo_client_data->curr_data.floorsclimbed,
+ pedo_client_data->curr_data.metsactivity);
+}
+
+
+static void m4_set_delay(int delay)
+{
+
+}
+
+static void m4_read_pedometer_data(struct pedometer_client *pedo_client_data)
+{
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_ACTIVITY,
+ (char *)&pedo_client_data->curr_data.activity);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_TOTATDISTANCE,
+ (char *)&pedo_client_data->curr_data.distance);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_TOTALSTEPS,
+ (char *)&pedo_client_data->curr_data.stepcount);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_CURRENTSPEED,
+ (char *)&pedo_client_data->curr_data.speed);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_METS_METS,
+ (char *)&pedo_client_data->curr_data.mets);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_METS_CALORIES,
+ (char *)&pedo_client_data->curr_data.calories);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_FLOORSCLIMBED,
+ (char *)&pedo_client_data->curr_data.floorsclimbed);
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_METS_METSACTIVITY,
+ (char *)&pedo_client_data->curr_data.metsactivity);
+}
+
+static void m4_handle_pedometer_irq(enum m4sensorhub_irqs int_event,
+ void *pedometer_data)
+{
+ struct pedometer_client *pedometer_client_data = pedometer_data;
+
+ m4_read_pedometer_data(pedometer_client_data);
+ m4_report_pedometer_inputevent(pedometer_client_data);
+}
+
+/*
+ * Handle commands from user-space.
+ */
+static long pedometer_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ int flag;
+ unsigned char byte;
+ void __user *argp = (void __user *)arg;
+ struct m4sh_user_profile user;
+ struct m4sh_workout_data workout_data;
+ struct pedometer_client *pedometer_client_data = filp->private_data;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_GET_PEDOMETER:
+ m4_read_pedometer_data(pedometer_client_data);
+ m4_report_pedometer_inputevent(pedometer_client_data);
+ break;
+ case M4_SENSOR_IOCTL_SET_DELAY:
+ if (copy_from_user(&flag, argp, sizeof(flag)))
+ return -EFAULT;
+ m4_set_delay(flag);
+ break;
+ /* TO DO
+ Need to implement the following ioctl's when M4 side implementation
+ will be ready
+ */
+ case M4_SENSOR_IOCTL_SET_POSIX_TIME:
+ break;
+ case M4_SENSOR_IOCTL_SET_EQUIPMENT_TYPE:
+ if (copy_from_user(&byte, argp, sizeof(byte))) {
+ printk(KERN_ERR "copy from user returned error eq type\n");
+ ret = -EFAULT;
+ break;
+ }
+ m4sensorhub_reg_write(pedometer_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_EQUIPMENTTYPE, &byte, m4sh_no_mask);
+ break;
+ case M4_SENSOR_IOCTL_SET_MANUAL_CALIB_WALK_SPEED:
+ break;
+ case M4_SENSOR_IOCTL_SET_MANUAL_CALIB_JOG_SPEED:
+ break;
+ case M4_SENSOR_IOCTL_SET_MANUAL_CALIB_RUN_SPEED:
+ break;
+ case M4_SENSOR_IOCTL_SET_MANUAL_CALIB_STATUS:
+ break;
+ case M4_SENSOR_IOCTL_SET_USER_PROFILE:
+ if (copy_from_user(&user, argp, sizeof(user))) {
+ printk(KERN_ERR "copy from user returned error\n");
+ ret = -EFAULT;
+ break;
+ }
+ m4sensorhub_reg_write_1byte(pedometer_client_data->m4sensorhub,
+ M4SH_REG_USERSETTINGS_USERAGE, user.age, 0xff);
+ m4sensorhub_reg_write_1byte(pedometer_client_data->m4sensorhub,
+ M4SH_REG_USERSETTINGS_USERGENDER, user.gender, 0xff);
+ m4sensorhub_reg_write_1byte(pedometer_client_data->m4sensorhub,
+ M4SH_REG_USERSETTINGS_USERHEIGHT, user.height, 0xff);
+ m4sensorhub_reg_write_1byte(pedometer_client_data->m4sensorhub,
+ M4SH_REG_USERSETTINGS_USERWEIGHT, user.weight, 0xff);
+ break;
+ case M4_SENSOR_IOCTL_SET_USER_DISTANCE:
+ if (copy_from_user(&workout_data, argp, sizeof(workout_data))) {
+ printk(KERN_ERR "copy from user returned error\n");
+ ret = -EFAULT;
+ break;
+ }
+ m4sensorhub_reg_write(pedometer_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_USERDISTANCE,
+ (unsigned char *)&workout_data.user_distance,
+ m4sh_no_mask);
+ m4sensorhub_reg_write(pedometer_client_data->m4sensorhub,
+ M4SH_REG_PEDOMETER_REPORTEDDISTANCE,
+ (unsigned char *)&workout_data.msp_distance,
+ m4sh_no_mask);
+ break;
+ case M4_SENSOR_IOCTL_SET_USER_CALIB_TABLE:
+ break;
+ case M4_SENSOR_IOCTL_GET_MANUAL_CALIB_STATUS:
+ break;
+ case M4_SENSOR_IOCTL_ERASE_CALIB:
+ break;
+ default:
+ KDEBUG(M4SH_ERROR, "Invalid IOCTL Command in %s\n", __func__);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static ssize_t m4_pedometer_activity(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4_read_pedometer_data(pedo_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : activity = %d\n",
+ __func__, pedo_client_data->curr_data.activity);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.activity);
+}
+
+static ssize_t m4_pedometer_distance(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4_read_pedometer_data(pedo_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : distance = %d\n",
+ __func__, pedo_client_data->curr_data.distance);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.distance);
+}
+
+static ssize_t m4_pedometer_speed(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4_read_pedometer_data(pedo_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : speed = %d\n",
+ __func__, pedo_client_data->curr_data.speed);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.speed);
+}
+
+static ssize_t m4_pedometer_stepcount(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4_read_pedometer_data(pedo_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : stepcount = %d\n",
+ __func__, pedo_client_data->curr_data.stepcount);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.stepcount);
+}
+
+static ssize_t m4_pedometer_mets(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : mets = %d\n",
+ __func__, pedo_client_data->curr_data.mets);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.mets);
+}
+
+static ssize_t m4_pedometer_calories(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : calories = %d\n",
+ __func__, pedo_client_data->curr_data.calories);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.calories);
+}
+
+static ssize_t m4_pedometer_floorsclimbed(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4_read_pedometer_data(pedo_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : floorsclimbed = %d\n",
+ __func__, pedo_client_data->curr_data.floorsclimbed);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.floorsclimbed);
+}
+
+static ssize_t m4_pedometer_metsactivity(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ KDEBUG(M4SH_DEBUG, "%s : metsactivity = %d\n",
+ __func__, pedo_client_data->curr_data.metsactivity);
+ return sprintf(buf, "%d \n", pedo_client_data->curr_data.metsactivity);
+}
+
+static ssize_t pedo_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+
+ int loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, PEDO_MASK_BIT_1);
+ return sprintf(buf, "%d\n", loglevel);
+}
+
+static ssize_t pedo_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ unsigned int mask = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -1;
+ }
+ mask = (1 << PEDO_MASK_BIT_1) | (1 << PEDO_MASK_BIT_2);
+ level = (level << PEDO_MASK_BIT_1);
+ return m4sensorhub_reg_write(pedo_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&level, (unsigned char *)&mask);
+}
+
+static ssize_t mets_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(pedo_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, METS_MASK_BIT_1);
+ return sprintf(buf, "%d\n", loglevel);
+}
+
+static ssize_t mets_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ unsigned int mask = 0;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct pedometer_client *pedo_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -1;
+ }
+ mask = (1 << METS_MASK_BIT_1) | (1 << METS_MASK_BIT_2);
+ level = (level << METS_MASK_BIT_1);
+ return m4sensorhub_reg_write(pedo_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&level, (unsigned char *)&mask);
+}
+
+static DEVICE_ATTR(activity, 0444, m4_pedometer_activity, NULL);
+static DEVICE_ATTR(distance, 0444, m4_pedometer_distance, NULL);
+static DEVICE_ATTR(speed, 0444, m4_pedometer_speed, NULL);
+static DEVICE_ATTR(stepcount, 0444, m4_pedometer_stepcount, NULL);
+static DEVICE_ATTR(mets, 0444, m4_pedometer_mets, NULL);
+static DEVICE_ATTR(calories, 0444, m4_pedometer_calories, NULL);
+static DEVICE_ATTR(floorsclimbed, 0444, m4_pedometer_floorsclimbed, NULL);
+static DEVICE_ATTR(metsactivity, 0444, m4_pedometer_metsactivity, NULL);
+static DEVICE_ATTR(pedoLogLevel, 0664, pedo_get_loglevel, pedo_set_loglevel);
+static DEVICE_ATTR(metsLogLevel, 0644, mets_get_loglevel, mets_set_loglevel);
+
+static const struct file_operations pedometer_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = pedometer_client_ioctl,
+ .open = pedometer_client_open,
+ .release = pedometer_client_close,
+};
+
+static struct miscdevice pedometer_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = PEDOMETER_CLIENT_DRIVER_NAME,
+ .fops = &pedometer_client_fops,
+};
+
+static int pedometer_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct pedometer_client *pedometer_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ pedometer_client_data = kzalloc(sizeof(*pedometer_client_data),
+ GFP_KERNEL);
+ if (!pedometer_client_data)
+ return -ENOMEM;
+
+ pedometer_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, pedometer_client_data);
+
+ pedometer_client_data->prev_data.stepcount = 0;
+ pedometer_client_data->prev_data.distance = 0;
+ pedometer_client_data->prev_data.activity = 0;
+ pedometer_client_data->prev_data.speed = 0;
+ pedometer_client_data->prev_data.floorsclimbed = 0;
+
+ pedometer_client_data->input_dev = input_allocate_device();
+ if (!pedometer_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_mem;
+ }
+
+ pedometer_client_data->input_dev->name = PEDOMETER_CLIENT_DRIVER_NAME;
+ set_bit(EV_MSC, pedometer_client_data->input_dev->evbit);
+ set_bit(MSC_ACTIVITY_TYPE, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_STEPCOUNT, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_SPEED, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_DISTANCE, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_METS, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_CALORIES, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_FLOORSCLIMBED, pedometer_client_data->input_dev->mscbit);
+ set_bit(MSC_METSACTIVITY, pedometer_client_data->input_dev->mscbit);
+
+ if (input_register_device(pedometer_client_data->input_dev)) {
+ KDEBUG(M4SH_ERROR, "%s: input device register failed\n",
+ __func__);
+ input_free_device(pedometer_client_data->input_dev);
+ goto free_mem;
+ }
+
+ ret = misc_register(&pedometer_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n", __func__);
+ goto unregister_input_device;
+ }
+ misc_pedometer_data = pedometer_client_data;
+ ret = m4sensorhub_irq_register(m4sensorhub,
+ M4SH_IRQ_PEDOMETER_DATA_READY, m4_handle_pedometer_irq,
+ pedometer_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_PEDOMETER_DATA_READY, ret);
+ goto unregister_misc_device;
+ }
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_ACTIVITY_CHANGE,
+ m4_handle_pedometer_irq,
+ pedometer_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_ACTIVITY_CHANGE, ret);
+ goto unregister_pedometer_irq;
+ }
+
+ ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_ACTIVITY_CHANGE);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling int %d (%d)\n",
+ M4SH_IRQ_ACTIVITY_CHANGE, ret);
+ goto unregister_activity_irq;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_activity)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto disable_activity_irq;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_distance)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_activity_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_speed)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_distance_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_stepcount)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_speed_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_mets)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_stepcount_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_pedoLogLevel)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_mets_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_metsLogLevel)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_pedoLogLevel_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_calories)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_metsLogLevel_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_floorsclimbed)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_cals_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_metsactivity)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n", __func__);
+ ret = -1;
+ goto remove_floorsclimbed_device_file;
+ }
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n", __func__);
+ return 0;
+
+remove_floorsclimbed_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_floorsclimbed);
+remove_cals_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_calories);
+remove_metsLogLevel_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_metsLogLevel);
+remove_pedoLogLevel_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_pedoLogLevel);
+remove_mets_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_mets);
+remove_stepcount_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_stepcount);
+remove_speed_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_speed);
+remove_distance_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_distance);
+remove_activity_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_activity);
+disable_activity_irq:
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_ACTIVITY_CHANGE);
+unregister_activity_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_ACTIVITY_CHANGE);
+unregister_pedometer_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_PEDOMETER_DATA_READY);
+unregister_misc_device:
+ misc_pedometer_data = NULL;
+ misc_deregister(&pedometer_client_miscdrv);
+unregister_input_device:
+ input_unregister_device(pedometer_client_data->input_dev);
+free_mem:
+ platform_set_drvdata(pdev, NULL);
+ pedometer_client_data->m4sensorhub = NULL;
+ kfree(pedometer_client_data);
+ pedometer_client_data = NULL;
+ return ret;
+}
+
+static int __exit pedometer_client_remove(struct platform_device *pdev)
+{
+ struct pedometer_client *pedometer_client_data =
+ platform_get_drvdata(pdev);
+
+ device_remove_file(&pdev->dev, &dev_attr_metsLogLevel);
+ device_remove_file(&pdev->dev, &dev_attr_pedoLogLevel);
+ device_remove_file(&pdev->dev, &dev_attr_mets);
+ device_remove_file(&pdev->dev, &dev_attr_calories);
+ device_remove_file(&pdev->dev, &dev_attr_stepcount);
+ device_remove_file(&pdev->dev, &dev_attr_speed);
+ device_remove_file(&pdev->dev, &dev_attr_distance);
+ device_remove_file(&pdev->dev, &dev_attr_activity);
+ device_remove_file(&pdev->dev, &dev_attr_floorsclimbed);
+ device_remove_file(&pdev->dev, &dev_attr_metsactivity);
+
+ m4sensorhub_irq_unregister(pedometer_client_data->m4sensorhub,
+ M4SH_IRQ_PEDOMETER_DATA_READY);
+ m4sensorhub_irq_disable(pedometer_client_data->m4sensorhub,
+ M4SH_IRQ_ACTIVITY_CHANGE);
+ m4sensorhub_irq_unregister(pedometer_client_data->m4sensorhub,
+ M4SH_IRQ_ACTIVITY_CHANGE);
+ misc_pedometer_data = NULL;
+ misc_deregister(&pedometer_client_miscdrv);
+ input_unregister_device(pedometer_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ pedometer_client_data->m4sensorhub = NULL;
+ kfree(pedometer_client_data);
+ pedometer_client_data = NULL;
+ return 0;
+}
+
+static void pedometer_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+#ifdef CONFIG_PM
+static int pedometer_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ return 0;
+}
+
+static int pedometer_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define pedometer_client_suspend NULL
+#define pedometer_client_resume NULL
+#endif
+
+
+static struct of_device_id m4pedometer_match_tbl[] = {
+ { .compatible = "mot,m4pedometer" },
+ {},
+};
+
+
+static struct platform_driver pedometer_client_driver = {
+ .probe = pedometer_client_probe,
+ .remove = __exit_p(pedometer_client_remove),
+ .shutdown = pedometer_client_shutdown,
+ .suspend = pedometer_client_suspend,
+ .resume = pedometer_client_resume,
+ .driver = {
+ .name = PEDOMETER_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4pedometer_match_tbl),
+ },
+};
+
+static int __init pedometer_client_init(void)
+{
+ return platform_driver_register(&pedometer_client_driver);
+}
+
+static void __exit pedometer_client_exit(void)
+{
+ platform_driver_unregister(&pedometer_client_driver);
+}
+
+module_init(pedometer_client_init);
+module_exit(pedometer_client_exit);
+
+MODULE_ALIAS("platform:pedometer_client");
+MODULE_DESCRIPTION("M4 Sensor Hub Pedometer client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_stillmode.c b/drivers/misc/m4sensorhub_stillmode.c
new file mode 100644
index 00000000000..3fa61219245
--- /dev/null
+++ b/drivers/misc/m4sensorhub_stillmode.c
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2012 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Adds ability to program periodic interrupts from user space that
+ * can wake the phone out of low power modes.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/input.h>
+#include <linux/wakelock.h>
+#include <linux/workqueue.h>
+#include <linux/m4sensorhub.h>
+#include <linux/slab.h>
+
+#define STILLMODE_CLIENT_DRIVER_NAME "m4sensorhub_stillmode"
+#define STILLMODE_DEFAULT_TIMEOUT 600 /* 10 minutes */
+
+static DEFINE_MUTEX(state_access);
+
+enum m4_stillmode_type {
+ MOTION,
+ STILL,
+};
+
+struct stillmode_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+ enum m4_stillmode_type state;
+ struct wake_lock wakelock;
+ struct work_struct queued_work;
+ u16 timeout;
+};
+
+static struct stillmode_client *g_stillmode_data;
+
+static int stillmode_set_timeout(struct stillmode_client *stillmode_client_data,
+ u16 timeout)
+{
+ int ret;
+
+ ret = m4sensorhub_reg_write(stillmode_client_data->m4sensorhub,
+ M4SH_REG_POWER_STILLMODETIMEOUT,
+ (char *)&timeout, m4sh_no_mask);
+ if (ret == m4sensorhub_reg_getsize(stillmode_client_data->m4sensorhub,
+ M4SH_REG_POWER_STILLMODETIMEOUT)) {
+ stillmode_client_data->timeout = timeout;
+ ret = 0;
+ } else
+ ret = -EIO;
+
+ return ret;
+
+}
+
+static void stillmode_set_state(struct stillmode_client *stillmode_client_data,
+ enum m4_stillmode_type state)
+{
+ mutex_lock(&state_access);
+ if (stillmode_client_data->state == state) {
+ mutex_unlock(&state_access);
+ printk(KERN_WARNING "M4SH duplicate stillmode update (%s)\n",
+ (state == STILL) ? "still" : "moving");
+ } else {
+ stillmode_client_data->state = state;
+ mutex_unlock(&state_access);
+
+ /* Hold a 500ms wakelock to let data get to KineticManager */
+ wake_lock_timeout(&stillmode_client_data->wakelock, 0.5 * HZ);
+
+ input_report_switch(stillmode_client_data->input_dev,
+ SW_STILL_MODE,
+ (stillmode_client_data->state == STILL));
+ input_sync(stillmode_client_data->input_dev);
+ printk(KERN_INFO "stillmode state changed to %s (%d)\n",
+ (state == STILL) ? "still" : "moving", state);
+ }
+}
+
+static int m4_stillmode_exit(void)
+{
+ struct stillmode_client *stillmode_client_data = g_stillmode_data;
+ int ret = 0;
+
+ KDEBUG(M4SH_INFO, "Resetting stillmode timer\n");
+
+ /* writing timeout value to M4 resets its timer */
+ ret = stillmode_set_timeout(stillmode_client_data,
+ stillmode_client_data->timeout);
+ if (ret == 0) {
+ if (stillmode_client_data->state == STILL)
+ stillmode_set_state(stillmode_client_data, MOTION);
+ } else
+ KDEBUG(M4SH_ERROR, "M4SH Error setting timeout (%d)\n", ret);
+
+ return ret;
+}
+
+
+int m4sensorhub_stillmode_exit(void)
+{
+ return m4_stillmode_exit();
+}
+EXPORT_SYMBOL_GPL(m4sensorhub_stillmode_exit);
+
+
+static void m4sensorhub_stillmode_work(struct work_struct *work)
+{
+ m4sensorhub_stillmode_exit();
+}
+
+static void m4_handle_stillmode_irq(enum m4sensorhub_irqs int_event,
+ void *stillmode_data)
+{
+ struct stillmode_client *stillmode_client_data = stillmode_data;
+ enum m4_stillmode_type new_state;
+
+ KDEBUG(M4SH_INFO, "%s() got irq %d (%s)\n", __func__, int_event,
+ int_event == M4SH_IRQ_STILL_DETECTED ? "STILL_MODE" : "MOTION_MODE");
+
+ switch (int_event) {
+ case (M4SH_IRQ_STILL_DETECTED):
+ new_state = STILL;
+ break;
+ case (M4SH_IRQ_MOTION_DETECTED):
+ new_state = MOTION;
+ break;
+ default:
+ printk(KERN_ERR "%s() Unexpected irq: %d\n",
+ __func__, int_event);
+ return;
+ break;
+ }
+
+ stillmode_set_state(stillmode_client_data, new_state);
+}
+
+static ssize_t m4_stillmode_getstate(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stillmode_client *stillmode_client_data =
+ platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d \n", stillmode_client_data->state);
+}
+
+static ssize_t m4_stillmode_setstate(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stillmode_client *stillmode_client_data =
+ platform_get_drvdata(pdev);
+ long value;
+ int ret = size;
+
+ if (((strict_strtoul(buf, 10, &value)) < 0) ||
+ (value != MOTION)) {
+ KDEBUG(M4SH_ERROR, "M4SH stillmode invalid value: %ld. Only "
+ "%d is allowed\n", value, MOTION);
+ return -EINVAL;
+ }
+
+ if (value != stillmode_client_data->state)
+ return m4sensorhub_stillmode_exit();
+
+ return ret;
+}
+
+static ssize_t m4_stillmode_get_timeout(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stillmode_client *stillmode_client_data =
+ platform_get_drvdata(pdev);
+
+ return sprintf(buf, "%d \n", stillmode_client_data->timeout);
+}
+
+static ssize_t m4_stillmode_set_timeout(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct stillmode_client *stillmode_client_data =
+ platform_get_drvdata(pdev);
+ long value;
+ int ret;
+
+ if (((strict_strtoul(buf, 10, &value)) < 0) ||
+ (value < 0) || (value > USHRT_MAX)) {
+ KDEBUG(M4SH_ERROR, "M4SH stillmode invalid timeout: %ld\n",
+ value);
+ return -EINVAL;
+ }
+
+ KDEBUG(M4SH_DEBUG, "%s() setting timeout to %ld\n", __func__, value);
+
+ ret = stillmode_set_timeout(stillmode_client_data, value);
+
+ return ((ret == 0) ? size : ret);
+}
+
+static DEVICE_ATTR(state, 0664, m4_stillmode_getstate,
+ m4_stillmode_setstate);
+static DEVICE_ATTR(timeout, 0664, m4_stillmode_get_timeout,
+ m4_stillmode_set_timeout);
+
+static int stillmode_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct stillmode_client *stillmode_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ stillmode_client_data = kzalloc(sizeof(*stillmode_client_data),
+ GFP_KERNEL);
+ if (!stillmode_client_data)
+ return -ENOMEM;
+
+ g_stillmode_data = stillmode_client_data;
+ stillmode_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, stillmode_client_data);
+ stillmode_client_data->state = MOTION;
+ stillmode_client_data->timeout = STILLMODE_DEFAULT_TIMEOUT;
+
+ stillmode_client_data->input_dev = input_allocate_device();
+ if (!stillmode_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_memory;
+ }
+
+ stillmode_client_data->input_dev->name = STILLMODE_CLIENT_DRIVER_NAME;
+ set_bit(EV_SW, stillmode_client_data->input_dev->evbit);
+ set_bit(SW_STILL_MODE, stillmode_client_data->input_dev->swbit);
+
+ if (input_register_device(stillmode_client_data->input_dev)) {
+ KDEBUG(M4SH_ERROR, "%s: input device register failed\n",
+ __func__);
+ input_free_device(stillmode_client_data->input_dev);
+ goto free_memory;
+ }
+
+ wake_lock_init(&stillmode_client_data->wakelock, WAKE_LOCK_SUSPEND,
+ STILLMODE_CLIENT_DRIVER_NAME);
+
+ INIT_WORK(&stillmode_client_data->queued_work,
+ m4sensorhub_stillmode_work);
+
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_STILL_DETECTED,
+ m4_handle_stillmode_irq,
+ stillmode_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering still mode IRQ: "
+ "%d\n", ret);
+ goto destroy_wakelock;
+ }
+ ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_STILL_DETECTED);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling still mode int: "
+ "%d\n", ret);
+ goto unregister_still_irq;
+ }
+
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_MOTION_DETECTED,
+ m4_handle_stillmode_irq,
+ stillmode_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering moving mode IRQ: "
+ "%d\n", ret);
+ goto disable_still_irq;
+ }
+ ret = m4sensorhub_irq_enable(m4sensorhub, M4SH_IRQ_MOTION_DETECTED);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error enabling moving mode int: "
+ "%d\n", ret);
+ goto unregister_moving_irq;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_state)) {
+ KDEBUG(M4SH_ERROR, "Error creating stillmode sys entry\n");
+ ret = -1;
+ goto disable_moving_irq;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_timeout)) {
+ KDEBUG(M4SH_ERROR, "Error creating timeout sys entry\n");
+ ret = -1;
+ goto remove_stillmode_sysfs;
+ }
+
+ /* initialize timer on M4 */
+ m4sensorhub_stillmode_exit();
+
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n",
+ STILLMODE_CLIENT_DRIVER_NAME);
+
+ return 0;
+
+remove_stillmode_sysfs:
+ device_remove_file(&pdev->dev, &dev_attr_state);
+disable_moving_irq:
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_MOTION_DETECTED);
+unregister_moving_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_MOTION_DETECTED);
+disable_still_irq:
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_STILL_DETECTED);
+unregister_still_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_STILL_DETECTED);
+destroy_wakelock:
+ wake_lock_destroy(&stillmode_client_data->wakelock);
+ input_unregister_device(stillmode_client_data->input_dev);
+free_memory:
+ platform_set_drvdata(pdev, NULL);
+ m4sensorhub->pdev->stillmode_exit = NULL;
+ stillmode_client_data->m4sensorhub = NULL;
+ kfree(stillmode_client_data);
+ g_stillmode_data = NULL;
+
+ return ret;
+}
+
+static int __exit stillmode_client_remove(struct platform_device *pdev)
+{
+ struct stillmode_client *stillmode_client_data =
+ platform_get_drvdata(pdev);
+ struct m4sensorhub_data *m4sensorhub =
+ stillmode_client_data->m4sensorhub;
+
+ device_remove_file(&pdev->dev, &dev_attr_timeout);
+ device_remove_file(&pdev->dev, &dev_attr_state);
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_MOTION_DETECTED);
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_MOTION_DETECTED);
+ m4sensorhub_irq_disable(m4sensorhub, M4SH_IRQ_STILL_DETECTED);
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_STILL_DETECTED);
+ wake_lock_destroy(&stillmode_client_data->wakelock);
+ input_unregister_device(stillmode_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ m4sensorhub->pdev->stillmode_exit = NULL;
+ stillmode_client_data->m4sensorhub = NULL;
+ kfree(stillmode_client_data);
+ g_stillmode_data = NULL;
+
+ return 0;
+}
+
+static void stillmode_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+#ifdef CONFIG_PM
+static int stillmode_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ return 0;
+}
+
+static int stillmode_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+#else
+#define stillmode_client_suspend NULL
+#define stillmode_client_resume NULL
+#endif
+
+
+static struct of_device_id m4stillmode_match_tbl[] = {
+ { .compatible = "mot,m4stillmode" },
+ {},
+};
+
+static struct platform_driver stillmode_client_driver = {
+ .probe = stillmode_client_probe,
+ .remove = __exit_p(stillmode_client_remove),
+ .shutdown = stillmode_client_shutdown,
+ .suspend = stillmode_client_suspend,
+ .resume = stillmode_client_resume,
+ .driver = {
+ .name = STILLMODE_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4stillmode_match_tbl),
+ },
+};
+
+static int __init stillmode_client_init(void)
+{
+ return platform_driver_register(&stillmode_client_driver);
+}
+
+static void __exit stillmode_client_exit(void)
+{
+ platform_driver_unregister(&stillmode_client_driver);
+}
+
+module_init(stillmode_client_init);
+module_exit(stillmode_client_exit);
+
+MODULE_ALIAS("platform:stillmode_client");
+MODULE_DESCRIPTION("M4 sensorhub still mode client driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_tmp006.c b/drivers/misc/m4sensorhub_tmp006.c
new file mode 100644
index 00000000000..44cb293f9a4
--- /dev/null
+++ b/drivers/misc/m4sensorhub_tmp006.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2012 Motorola, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Adds ability to program periodic interrupts from user space that
+ * can wake the phone out of low power modes.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/proc_fs.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/m4sensorhub.h>
+#include <linux/m4sensorhub_client_ioctl.h>
+#include <linux/m4sensorhub/MemMapTempSensor.h>
+
+#define TMP_CLIENT_DRIVER_NAME "m4sensorhub_tmp006"
+#define TMP_MAX 1250
+#define TMP_MIN -400
+
+struct tmp_client {
+ struct m4sensorhub_data *m4sensorhub;
+ struct input_dev *input_dev;
+ int internal_tmp;
+ int external_tmp;
+ signed short samplerate;
+};
+
+struct tmp_client *misc_tmp_data;
+
+static int temperature_client_open(struct inode *inode, struct file *file)
+{
+ int err = 0;
+
+ err = nonseekable_open(inode, file);
+ if (err < 0) {
+ KDEBUG(M4SH_DEBUG, "temperature_clientopen failed\n");
+ return err;
+ }
+ file->private_data = misc_tmp_data;
+
+ return 0;
+}
+
+static int temperature_client_close(struct inode *inode, struct file *file)
+{
+ KDEBUG(M4SH_DEBUG, "temperature_client in %s\n", __func__);
+ return 0;
+}
+
+static void m4_report_temp_inputevent(struct tmp_client *tmp_data)
+{
+ input_report_abs(tmp_data->input_dev, ABS_THROTTLE,
+ tmp_data->external_tmp);
+ input_sync(tmp_data->input_dev);
+}
+
+static void m4_read_temp_data(struct tmp_client *tmp_data)
+{
+ sTempData tmp;
+
+ m4sensorhub_reg_read(tmp_data->m4sensorhub,
+ M4SH_REG_TEMP_EXTRNLTEMP, (char *)&tmp.extrnlTemp);
+ m4sensorhub_reg_read(tmp_data->m4sensorhub,
+ M4SH_REG_TEMP_INTRNLTEMP, (char *)&tmp.intrnlTemp);
+ tmp_data->internal_tmp = tmp.intrnlTemp;
+ tmp_data->external_tmp = tmp.extrnlTemp;
+}
+
+static void m4_handle_tmp_irq(enum m4sensorhub_irqs int_event,
+ void *tmp_data)
+{
+ struct tmp_client *tmp_client_data = tmp_data;
+
+ m4_read_temp_data(tmp_client_data);
+ m4_report_temp_inputevent(tmp_client_data);
+}
+
+static int m4_set_tmp_samplerate(
+ struct tmp_client *tmp_client_data,
+ signed int samplerate)
+{
+ int ret = 0;
+
+ if (samplerate != tmp_client_data->samplerate) {
+ ret = m4sensorhub_reg_write(tmp_client_data->m4sensorhub,
+ M4SH_REG_TEMP_SAMPLERATE,
+ (char *)&samplerate, m4sh_no_mask);
+ if (ret != m4sensorhub_reg_getsize(
+ tmp_client_data->m4sensorhub,
+ M4SH_REG_TEMP_SAMPLERATE)) {
+ KDEBUG(M4SH_ERROR, "Unable to set delay \
+ for temperature sensor\n");
+ return ret;
+ }
+
+ KDEBUG(M4SH_DEBUG, "%s() updating samplerate from %d to %d\n",
+ __func__, tmp_client_data->samplerate,
+ samplerate);
+ tmp_client_data->samplerate = samplerate;
+
+ if (samplerate >= 0)
+ ret = m4sensorhub_irq_enable(
+ tmp_client_data->m4sensorhub,
+ M4SH_IRQ_TMP_DATA_READY);
+ else
+ ret = m4sensorhub_irq_disable(
+ tmp_client_data->m4sensorhub,
+ M4SH_IRQ_TMP_DATA_READY);
+ if (ret != 0)
+ KDEBUG(M4SH_ERROR, "Unable to enable/disable \
+ temperature irq\n");
+ }
+
+ return ret;
+}
+
+
+/*
+ * Handle commands from user-space.
+ */
+static long temperature_client_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ int delay = 0;
+ static int status;
+
+ void __user *argp = (void __user *)arg;
+ struct tmp_client *tmp_data = filp->private_data;
+
+ switch (cmd) {
+ case M4_SENSOR_IOCTL_GET_TEMPRATURE:
+ m4_read_temp_data(tmp_data);
+ m4_report_temp_inputevent(tmp_data);
+ break;
+ case M4_SENSOR_IOCTL_SET_DELAY:
+ if (copy_from_user(&delay, argp, sizeof(delay)))
+ return -EFAULT;
+ if (delay >= 0)
+ ret = m4_set_tmp_samplerate(tmp_data, delay);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error setting samplerate to %d"
+ " (%d)\n", delay, ret);
+ return -EFAULT;
+ }
+ break;
+ case M4_SENSOR_IOCTL_APP_GET_FLAG:
+ if (copy_to_user(argp, &status, sizeof(status)))
+ return -EFAULT;
+ break;
+ case M4_SENSOR_IOCTL_APP_SET_FLAG:
+ if (copy_from_user(&status, argp, sizeof(status)))
+ return -EFAULT;
+ break;
+ default:
+ KDEBUG(M4SH_ERROR, "Invalid IOCTL Command %d\n", cmd);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static ssize_t GetExternalTemp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tmp_client *tmp_client_data = platform_get_drvdata(pdev);
+
+ m4_read_temp_data(tmp_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : external temp = %d",
+ __func__, tmp_client_data->external_tmp);
+ return sprintf(buf, "%d\n", tmp_client_data->external_tmp);
+}
+
+static ssize_t GetInternalTemp(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tmp_client *tmp_client_data = platform_get_drvdata(pdev);
+
+ m4_read_temp_data(tmp_client_data);
+ KDEBUG(M4SH_DEBUG, "%s : internal temp = %d",
+ __func__, tmp_client_data->internal_tmp);
+ return sprintf(buf, "%d\n", tmp_client_data->internal_tmp);
+}
+
+static ssize_t tmp_get_loglevel(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ unsigned long long loglevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tmp_client *tmp_client_data = platform_get_drvdata(pdev);
+
+ m4sensorhub_reg_read(tmp_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&loglevel);
+ loglevel = get_log_level(loglevel, TMP_MASK_BIT_1);
+ return sprintf(buf, "%llu\n", loglevel);
+}
+
+static ssize_t tmp_set_loglevel(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ unsigned long level;
+ unsigned long long mask = 0, newlevel;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct tmp_client *tmp_client_data = platform_get_drvdata(pdev);
+
+ if ((strict_strtoul(buf, 10, &level)) < 0)
+ return -1;
+ if (level > M4_MAX_LOG_LEVEL) {
+ KDEBUG(M4SH_ERROR, " Invalid log level - %d\n", (int)level);
+ return -1;
+ }
+ mask = (1ULL << TMP_MASK_BIT_1) | (1ULL << TMP_MASK_BIT_2);
+ newlevel = (level << TMP_MASK_BIT_1);
+ return m4sensorhub_reg_write(tmp_client_data->m4sensorhub,
+ M4SH_REG_LOG_LOGENABLE, (char *)&newlevel, (unsigned char *)&mask);
+}
+
+static DEVICE_ATTR(internal, 0444, GetInternalTemp, NULL);
+static DEVICE_ATTR(external, 0444, GetExternalTemp, NULL);
+static DEVICE_ATTR(LogLevel, 0444, tmp_get_loglevel, tmp_set_loglevel);
+
+static const struct file_operations temperature_client_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = temperature_client_ioctl,
+ .open = temperature_client_open,
+ .release = temperature_client_close,
+};
+
+static struct miscdevice temperature_client_miscdrv = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = TMP_CLIENT_DRIVER_NAME,
+ .fops = &temperature_client_fops,
+};
+
+static int temperature_client_probe(struct platform_device *pdev)
+{
+ int ret = -1;
+ struct tmp_client *tmp_client_data;
+ struct m4sensorhub_data *m4sensorhub = m4sensorhub_client_get_drvdata();
+
+ if (!m4sensorhub)
+ return -EFAULT;
+
+ tmp_client_data = kzalloc(sizeof(*tmp_client_data), GFP_KERNEL);
+ if (!tmp_client_data)
+ return -ENOMEM;
+
+ tmp_client_data->m4sensorhub = m4sensorhub;
+ platform_set_drvdata(pdev, tmp_client_data);
+
+ tmp_client_data->input_dev = input_allocate_device();
+ if (!tmp_client_data->input_dev) {
+ ret = -ENOMEM;
+ KDEBUG(M4SH_ERROR, "%s: input device allocate failed: %d\n",
+ __func__, ret);
+ goto free_memory;
+ }
+
+ tmp_client_data->input_dev->name = TMP_CLIENT_DRIVER_NAME;
+ set_bit(EV_ABS, tmp_client_data->input_dev->evbit);
+ set_bit(ABS_THROTTLE, tmp_client_data->input_dev->absbit);
+ input_set_abs_params(tmp_client_data->input_dev, ABS_THROTTLE,
+ TMP_MIN, TMP_MAX, 0, 0);
+
+ if (input_register_device(tmp_client_data->input_dev)) {
+ KDEBUG(M4SH_INFO, "%s: input device register failed\n",
+ __func__);
+ input_free_device(tmp_client_data->input_dev);
+ goto free_memory;
+ }
+
+ ret = misc_register(&temperature_client_miscdrv);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering %s driver\n",
+ TMP_CLIENT_DRIVER_NAME);
+ goto unregister_input_device;
+ }
+ misc_tmp_data = tmp_client_data;
+ ret = m4sensorhub_irq_register(m4sensorhub, M4SH_IRQ_TMP_DATA_READY,
+ m4_handle_tmp_irq,
+ tmp_client_data);
+ if (ret < 0) {
+ KDEBUG(M4SH_ERROR, "Error registering int %d (%d)\n",
+ M4SH_IRQ_TMP_DATA_READY, ret);
+ goto unregister_misc_device;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_internal)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ TMP_CLIENT_DRIVER_NAME);
+ ret = -1;
+ goto unregister_irq;
+ }
+
+ if (device_create_file(&pdev->dev, &dev_attr_external)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ TMP_CLIENT_DRIVER_NAME);
+ ret = -1;
+ goto remove_internal_device_file;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_LogLevel)) {
+ KDEBUG(M4SH_ERROR, "Error creating %s sys entry\n",
+ TMP_CLIENT_DRIVER_NAME);
+ ret = -1;
+ goto remove_external_device_file;
+ }
+ KDEBUG(M4SH_INFO, "Initialized %s driver\n", TMP_CLIENT_DRIVER_NAME);
+ return 0;
+
+remove_external_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_external);
+remove_internal_device_file:
+ device_remove_file(&pdev->dev, &dev_attr_internal);
+unregister_irq:
+ m4sensorhub_irq_unregister(m4sensorhub, M4SH_IRQ_TMP_DATA_READY);
+unregister_misc_device:
+ misc_tmp_data = NULL;
+ misc_deregister(&temperature_client_miscdrv);
+unregister_input_device:
+ input_unregister_device(tmp_client_data->input_dev);
+free_memory:
+ platform_set_drvdata(pdev, NULL);
+ tmp_client_data->m4sensorhub = NULL;
+ kfree(tmp_client_data);
+ tmp_client_data = NULL;
+ return ret;
+}
+
+static int __exit temperature_client_remove(struct platform_device *pdev)
+{
+ struct tmp_client *tmp_client_data = platform_get_drvdata(pdev);
+
+ device_remove_file(&pdev->dev, &dev_attr_LogLevel);
+ device_remove_file(&pdev->dev, &dev_attr_external);
+ device_remove_file(&pdev->dev, &dev_attr_internal);
+ m4sensorhub_irq_disable(tmp_client_data->m4sensorhub,
+ M4SH_IRQ_TMP_DATA_READY);
+ m4sensorhub_irq_unregister(tmp_client_data->m4sensorhub,
+ M4SH_IRQ_TMP_DATA_READY);
+ misc_tmp_data = NULL;
+ misc_deregister(&temperature_client_miscdrv);
+ input_unregister_device(tmp_client_data->input_dev);
+ platform_set_drvdata(pdev, NULL);
+ tmp_client_data->m4sensorhub = NULL;
+ kfree(tmp_client_data);
+ tmp_client_data = NULL;
+ return 0;
+}
+
+static void temperature_client_shutdown(struct platform_device *pdev)
+{
+ return;
+}
+
+#ifdef CONFIG_PM
+
+static int temperature_client_suspend(struct platform_device *pdev,
+ pm_message_t message)
+{
+ struct tmp_client *tmp_client_data = platform_get_drvdata(pdev);
+
+ return m4_set_tmp_samplerate(tmp_client_data, -1);
+}
+
+static int temperature_client_resume(struct platform_device *pdev)
+{
+ return 0;
+}
+
+#else
+#define temperature_client_suspend NULL
+#define temperature_client_resume NULL
+#endif
+
+static struct of_device_id m4temp_match_tbl[] = {
+ { .compatible = "mot,m4temperature" },
+ {},
+};
+
+static struct platform_driver temp_client_driver = {
+ .probe = temperature_client_probe,
+ .remove = __exit_p(temperature_client_remove),
+ .shutdown = temperature_client_shutdown,
+ .suspend = temperature_client_suspend,
+ .resume = temperature_client_resume,
+ .driver = {
+ .name = TMP_CLIENT_DRIVER_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4temp_match_tbl),
+ },
+};
+
+static int __init temperature_client_init(void)
+{
+ return platform_driver_register(&temp_client_driver);
+}
+
+static void __exit temperature_client_exit(void)
+{
+ platform_driver_unregister(&temp_client_driver);
+}
+
+module_init(temperature_client_init);
+module_exit(temperature_client_exit);
+
+MODULE_ALIAS("platform:temperature_client");
+MODULE_DESCRIPTION("M4 Sensor Hub driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
+
diff --git a/drivers/misc/m4sensorhub_wrist.c b/drivers/misc/m4sensorhub_wrist.c
new file mode 100644
index 00000000000..faed5f4068b
--- /dev/null
+++ b/drivers/misc/m4sensorhub_wrist.c
@@ -0,0 +1,836 @@
+/*
+ * Copyright (C) 2013 Motorola Mobility, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include "m4sensorhub_wrist.h"
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/m4sensorhub.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/m4sensorhub_gpio.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+
+static struct platform_driver m4wrist_client_driver;
+static int m4wrist_probe(struct platform_device *pdev);
+static int m4wrist_remove(struct platform_device *pdev);
+static int m4wrist_init(void);
+static void m4wrist_exit(void);
+static void m4wrist_free(struct m4wrist_driver_data *dd);
+static int m4wrist_gpio_init(struct m4wrist_driver_data *dd);
+static int m4wrist_request_firmware(struct m4wrist_driver_data *dd);
+static int m4wrist_request_irq(struct m4wrist_driver_data *dd);
+static void m4wrist_firmware_callback(const struct firmware *fw,
+ void *context);
+static void m4wrist_irq(enum m4sensorhub_irqs event, void *context);
+static int m4wrist_gpio_control(struct m4wrist_driver_data *dd);
+static int m4wrist_gpio_release(struct m4wrist_driver_data *dd);
+static int m4wrist_reflash_ic(struct m4wrist_driver_data *dd);
+static int m4wrist_enter_reset_mode(struct m4wrist_driver_data *dd);
+static int m4wrist_erase_flash(struct m4wrist_driver_data *dd);
+static int m4wrist_program_image(struct m4wrist_driver_data *dd);
+static void m4wrist_send_bitstream(struct m4wrist_driver_data *dd,
+ uint8_t *stream, uint32_t bits);
+static void m4wrist_toggle_clock(struct m4wrist_driver_data *dd, int cycles);
+static int m4wrist_wait_poll(struct m4wrist_driver_data *dd);
+static int m4wrist_read_id_word(struct m4wrist_driver_data *dd, uint8_t *data);
+
+
+static struct of_device_id m4wrist_match_tbl[] = {
+ { .compatible = "mot,m4wrist" },
+ {},
+};
+
+static struct platform_driver m4wrist_client_driver = {
+ .probe = m4wrist_probe,
+ .remove = m4wrist_remove,
+ .driver = {
+ .name = "m4sensorhub_wrist",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(m4wrist_match_tbl),
+ },
+};
+
+static int m4wrist_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct m4wrist_driver_data *dd = NULL;
+ struct device_node *node = pdev->dev.of_node;
+
+ if (!node) {
+ pr_warn("devtree node not present!\n");
+ return -ENODEV;
+ }
+ dd = kzalloc(sizeof(struct m4wrist_driver_data), GFP_KERNEL);
+ if (dd == NULL) {
+ printk(KERN_ERR "%s: Unable to create driver data.\n",
+ __func__);
+ err = -ENOMEM;
+ goto m4wrist_probe_fail;
+ }
+
+ dd->pdev = pdev;
+ platform_set_drvdata(pdev, dd);
+
+ dd->mutex = kzalloc(sizeof(struct mutex), GFP_KERNEL);
+ if (dd->mutex == NULL) {
+ printk(KERN_ERR "%s: Unable to create mutex lock.\n",
+ __func__);
+ err = -ENOMEM;
+ goto m4wrist_probe_fail;
+ }
+ mutex_init(dd->mutex);
+
+ dd->gpio_xres = of_get_named_gpio_flags(node,
+ "mot,wrist_xres", 0, NULL);
+ if (dd->gpio_xres <= 0) {
+ printk(KERN_ERR "%s: gpio_xres is invalid.\n", __func__);
+ err = -EINVAL;
+ goto m4wrist_probe_fail;
+ }
+ dd->gpio_clk = of_get_named_gpio_flags(node, "mot,wrist_clk", 0, NULL);
+ if (dd->gpio_clk <= 0) {
+ printk(KERN_ERR "%s: gpio_clk is invalid.\n", __func__);
+ err = -EINVAL;
+ goto m4wrist_probe_fail;
+ }
+ dd->gpio_data = of_get_named_gpio_flags(node,
+ "mot,wrist_data", 0, NULL);
+ if (dd->gpio_data <= 0) {
+ printk(KERN_ERR "%s: gpio_data is invalid.\n", __func__);
+ err = -EINVAL;
+ goto m4wrist_probe_fail;
+ }
+
+ err = m4wrist_gpio_init(dd);
+ if (err < 0)
+ goto m4wrist_probe_fail;
+
+ err = m4wrist_request_firmware(dd);
+ if (err < 0)
+ goto m4wrist_probe_fail;
+
+ err = m4wrist_request_irq(dd);
+ if (err < 0)
+ goto m4wrist_probe_fail;
+
+ goto m4wrist_probe_pass;
+
+m4wrist_probe_fail:
+ m4wrist_free(dd);
+ printk(KERN_ERR "%s: Probe failed with error code %d.\n",
+ __func__, err);
+ return err;
+
+m4wrist_probe_pass:
+ return 0;
+}
+
+static void m4wrist_free(struct m4wrist_driver_data *dd)
+{
+ if (dd != NULL) {
+ kfree(dd->img);
+
+ if (dd->client != NULL) {
+ m4sensorhub_irq_disable(dd->client,
+ M4SH_IRQ_WRIST_READY);
+ m4sensorhub_irq_unregister(dd->client,
+ M4SH_IRQ_WRIST_READY);
+ }
+
+ kfree(dd->mutex);
+ platform_set_drvdata(dd->pdev, NULL);
+ kfree(dd);
+ }
+
+ return;
+}
+
+static int m4wrist_gpio_init(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ int gpio_nums[3] = {dd->gpio_xres, dd->gpio_clk, dd->gpio_data};
+ char *gpio_names[3] = {"wrist_xres", "wrist_clk", "wrist_data"};
+
+ for (i = 0; i < 3; i++) {
+ err = gpio_request(gpio_nums[i], gpio_names[i]);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to request %s.\n",
+ __func__, gpio_names[i]);
+ i--;
+ goto m4wrist_gpio_init_fail;
+ }
+
+ err = gpio_direction_input(gpio_nums[i]);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to make %s an input.\n",
+ __func__, gpio_names[i]);
+ gpio_free(gpio_nums[i]);
+ i--;
+ goto m4wrist_gpio_init_fail;
+ }
+ }
+
+ goto m4wrist_gpio_init_exit;
+
+m4wrist_gpio_init_fail:
+ while (i >= 0) {
+ gpio_free(gpio_nums[i]);
+ i--;
+ }
+
+m4wrist_gpio_init_exit:
+ return err;
+}
+
+static int m4wrist_request_firmware(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ const struct firmware *fw = NULL;
+
+ err = request_firmware(&fw,
+ "m4sensorhub_wrist.bin", &(dd->pdev->dev));
+ if (err < 0) {
+ printk(KERN_ERR "%s: Firmware request failed.\n", __func__);
+ goto m4wrist_request_firmware_fail;
+ }
+
+ m4wrist_firmware_callback(fw, dd);
+
+m4wrist_request_firmware_fail:
+ return err;
+}
+
+static int m4wrist_request_irq(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+
+ dd->client = m4sensorhub_client_get_drvdata();
+ if (dd->client == NULL) {
+ printk(KERN_ERR "%s: No client data retrieved.\n",
+ __func__);
+ err = -ENODATA;
+ goto m4wrist_request_irq_fail;
+ }
+
+ err = m4sensorhub_irq_register(dd->client,
+ M4SH_IRQ_WRIST_READY, m4wrist_irq, dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to register IRQ.\n", __func__);
+ dd->client = NULL;
+ goto m4wrist_request_irq_fail;
+ }
+
+ err = m4sensorhub_irq_enable(dd->client, M4SH_IRQ_WRIST_READY);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to enable IRQ.\n", __func__);
+ m4sensorhub_irq_unregister(dd->client, M4SH_IRQ_WRIST_READY);
+ dd->client = NULL;
+ goto m4wrist_request_irq_fail;
+ }
+
+m4wrist_request_irq_fail:
+ return err;
+}
+
+static void m4wrist_firmware_callback(const struct firmware *fw,
+ void *context)
+{
+ struct m4wrist_driver_data *dd = context;
+
+ mutex_lock(dd->mutex);
+
+ if (fw == NULL) {
+ printk(KERN_ERR "%s: No firmware received.\n", __func__);
+ goto m4wrist_firmware_callback_fail;
+ } else if (fw->data == NULL || fw->size == 0) {
+ printk(KERN_ERR "%s: No data found.\n", __func__);
+ goto m4wrist_firmware_callback_fail;
+ } else if (fw->data[0] < 4) {
+ printk(KERN_ERR "%s: Firmware header is too small.\n",
+ __func__);
+ goto m4wrist_firmware_callback_fail;
+ } else if (fw->data[0] >= fw->size) {
+ printk(KERN_ERR "%s: Firmware data is missing.\n", __func__);
+ goto m4wrist_firmware_callback_fail;
+ }
+
+ dd->size = fw->size - (fw->data[0] + 1);
+ dd->img = kzalloc(dd->size * sizeof(uint8_t), GFP_KERNEL);
+ if (dd->img == NULL) {
+ printk(KERN_ERR "%s: Failed to allocate memory for firmware.\n",
+ __func__);
+ goto m4wrist_firmware_callback_fail;
+ }
+ memcpy(dd->img, &(fw->data[fw->data[0] + 1]), dd->size);
+ dd->si_id[0] = fw->data[1];
+ dd->si_id[1] = fw->data[2];
+ dd->fw_ver[0] = fw->data[3];
+ dd->fw_ver[1] = fw->data[4];
+
+m4wrist_firmware_callback_fail:
+ release_firmware(fw);
+ mutex_unlock(dd->mutex);
+ return;
+}
+
+static int m4wrist_remove(struct platform_device *pdev)
+{
+ struct m4wrist_driver_data *dd = NULL;
+
+ dd = platform_get_drvdata(pdev);
+ if (dd != NULL) {
+ gpio_free(dd->gpio_xres);
+ gpio_free(dd->gpio_clk);
+ gpio_free(dd->gpio_data);
+ m4wrist_free(dd);
+ }
+
+ return 0;
+}
+
+static int m4wrist_init(void)
+{
+ return platform_driver_register(&m4wrist_client_driver);
+}
+
+static void m4wrist_exit(void)
+{
+ platform_driver_unregister(&m4wrist_client_driver);
+}
+
+module_init(m4wrist_init);
+module_exit(m4wrist_exit);
+
+static void m4wrist_irq(enum m4sensorhub_irqs event, void *context)
+{
+ struct m4wrist_driver_data *dd = context;
+ int err = 0;
+ uint8_t irq_reason = 0x00;
+ uint8_t val[2] = {0x00, 0x00};
+ uint8_t mask[2] = {0xFF, 0xFF};
+ mutex_lock(dd->mutex);
+
+ if (dd->img == NULL) {
+ printk(KERN_ERR "%s: Firmware image is missing--%s.\n",
+ __func__, "unable to respond to interrupts");
+ err = -ENODATA;
+ goto m4wrist_irq_fail;
+ }
+
+ err = m4sensorhub_reg_read(dd->client,
+ M4SH_REG_WRIST_INTERRUPTREASON, &irq_reason);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to read interrupt reason.\n",
+ __func__);
+ goto m4wrist_irq_fail;
+ } else if (err < 1) {
+ printk(KERN_ERR "%s: Read %d bytes instead of 1.\n",
+ __func__, err);
+ err = -EINVAL;
+ goto m4wrist_irq_fail;
+ }
+
+ switch (irq_reason) {
+ case 0x00:
+ err = m4wrist_gpio_control(dd);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Failed to take control of GPIO lines.\n",
+ __func__);
+ goto m4wrist_irq_fail;
+ }
+
+ err = m4wrist_reflash_ic(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to reflash IC.\n",
+ __func__);
+ goto m4wrist_irq_fail;
+ }
+
+ err = m4wrist_gpio_release(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to release GPIO lines.\n",
+ __func__);
+ goto m4wrist_irq_fail;
+ }
+
+ val[0] = 0x01;
+ err = m4sensorhub_reg_write(dd->client,
+ M4SH_REG_WRIST_HOSTRESPONSE, &(val[0]), &(mask[0]));
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to write host response.\n",
+ __func__);
+ goto m4wrist_irq_fail;
+ } else if (err < 1) {
+ printk(KERN_ERR "%s: Wrote %d bytes instead of 1.\n",
+ __func__, err);
+ err = -EINVAL;
+ goto m4wrist_irq_fail;
+ }
+ break;
+
+ case 0x01:
+ val[0] = dd->fw_ver[0];
+ val[1] = dd->fw_ver[1];
+ err = m4sensorhub_reg_write(dd->client,
+ M4SH_REG_WRIST_FMONFILE, &(val[0]), &(mask[0]));
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to write firmware version.\n",
+ __func__);
+ goto m4wrist_irq_fail;
+ } else if (err < 2) {
+ printk(KERN_ERR "%s: Wrote %d bytes instead of 2.\n",
+ __func__, err);
+ err = -EINVAL;
+ goto m4wrist_irq_fail;
+ }
+ break;
+
+ default:
+ printk(KERN_ERR "%s: Unexpected interrupt 0x%02X received.\n",
+ __func__, irq_reason);
+ err = -EINVAL;
+ goto m4wrist_irq_fail;
+ break;
+ }
+
+ goto m4wrist_irq_pass;
+
+m4wrist_irq_fail:
+ printk(KERN_ERR "%s: IRQ handler failed with error code %d.\n",
+ __func__, err);
+
+m4wrist_irq_pass:
+ mutex_unlock(dd->mutex);
+ return;
+}
+
+static int m4wrist_gpio_control(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ int gpio_nums[3] = {dd->gpio_xres, dd->gpio_clk, dd->gpio_data};
+ char *gpio_names[3] = {"wrist_xres", "wrist_clk", "wrist_data"};
+
+ for (i = 0; i < 3; i++) {
+ err = gpio_direction_output(gpio_nums[i], 0);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to take control of %s.\n",
+ __func__, gpio_names[i]);
+ goto m4wrist_gpio_control_fail;
+ }
+ }
+
+m4wrist_gpio_control_fail:
+ return err;
+}
+
+static int m4wrist_gpio_release(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ int gpio_nums[3] = {dd->gpio_xres, dd->gpio_clk, dd->gpio_data};
+ char *gpio_names[3] = {"wrist_xres", "wrist_clk", "wrist_data"};
+
+ for (i = 0; i < 3; i++) {
+ err = gpio_direction_input(gpio_nums[i]);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to release %s.\n",
+ __func__, gpio_names[i]);
+ goto m4wrist_gpio_release_fail;
+ }
+ }
+
+m4wrist_gpio_release_fail:
+ return err;
+}
+
+static int m4wrist_reflash_ic(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+
+ err = m4wrist_enter_reset_mode(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to enter programming mode.\n",
+ __func__);
+ goto m4wrist_reflash_ic_fail;
+ }
+
+ err = m4wrist_erase_flash(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to erase IC flash.\n", __func__);
+ goto m4wrist_reflash_ic_fail;
+ }
+
+ err = m4wrist_program_image(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to program firmware image",
+ __func__);
+ goto m4wrist_reflash_ic_fail;
+ }
+
+ gpio_set_value(dd->gpio_xres, 1);
+ udelay(100);
+ gpio_set_value(dd->gpio_xres, 0);
+
+m4wrist_reflash_ic_fail:
+ return err;
+}
+
+static int m4wrist_enter_reset_mode(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ uint8_t silicon_id[2] = {0x00, 0x00};
+
+ msleep(20);
+ gpio_set_value(dd->gpio_xres, 1);
+ udelay(400);
+ gpio_set_value(dd->gpio_xres, 0);
+ udelay(1);
+
+ m4wrist_send_bitstream(dd, &(m4wrist_id_setup_1[0]),
+ M4WRIST_ID_SETUP_1_BITS);
+
+ err = m4wrist_wait_poll(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to wait-and-poll 1.\n", __func__);
+ goto m4wrist_enter_reset_mode_fail;
+ }
+
+ m4wrist_send_bitstream(dd, &(m4wrist_id_setup_2[0]),
+ M4WRIST_ID_SETUP_2_BITS);
+
+ err = m4wrist_wait_poll(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to wait-and-poll 2.\n", __func__);
+ goto m4wrist_enter_reset_mode_fail;
+ }
+
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_enable[0]),
+ M4WRIST_SYNC_ENABLE_BITS);
+
+ err = m4wrist_read_id_word(dd, &(silicon_id[0]));
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to read silicon ID\n", __func__);
+ goto m4wrist_enter_reset_mode_fail;
+ }
+
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_disable[0]),
+ M4WRIST_SYNC_DISABLE_BITS);
+
+ if (silicon_id[0] != dd->si_id[0] || silicon_id[1] != dd->si_id[1]) {
+ printk(KERN_ERR "%s: Silicon ID mismatch (read 0x%02X%02X).\n",
+ __func__, silicon_id[0], silicon_id[1]);
+ err = -EINVAL;
+ goto m4wrist_enter_reset_mode_fail;
+ }
+
+m4wrist_enter_reset_mode_fail:
+ return err;
+}
+
+static int m4wrist_erase_flash(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+
+ m4wrist_send_bitstream(dd, &(m4wrist_erase[0]),
+ M4WRIST_ERASE_BITS);
+
+ err = m4wrist_wait_poll(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to wait-and-poll.\n", __func__);
+ goto m4wrist_erase_flash_fail;
+ }
+
+m4wrist_erase_flash_fail:
+ return err;
+}
+
+static int m4wrist_program_image(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ uint32_t iter = 0;
+ int i = 0;
+ uint8_t blk_num = 0x00;
+ uint8_t cur_addr = 0x00;
+ uint8_t cur_vector[3] = {0x00, 0x00, 0x00};
+ uint8_t cur_blk_data[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ uint8_t status[2] = {0x00, 0x00};
+
+ if (((dd->size % 128) != 0) || (dd->size == 0)) {
+ printk(KERN_ERR "%s: Firmware has invalid size of %u.\n",
+ __func__, dd->size);
+ err = -EINVAL;
+ goto m4wrist_program_image_fail;
+ }
+
+ printk(KERN_INFO "%s: Flashing version 0x%02X 0x%02X...\n", __func__,
+ dd->fw_ver[0], dd->fw_ver[1]);
+
+ while (iter < (dd->size - 1)) {
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_enable[0]),
+ M4WRIST_SYNC_ENABLE_BITS);
+
+ m4wrist_send_bitstream(dd, &(m4wrist_read_write_setup[0]),
+ M4WRIST_READ_WRITE_SETUP_BITS);
+
+ cur_addr = 0x00;
+ for (i = 0; i < 128; i++) {
+ cur_vector[0] = 0x90 | ((cur_addr & 0x78) >> 3);
+ cur_vector[1] = ((cur_addr & 0x07) << 5) |
+ ((dd->img[iter] & 0xF8) >> 3);
+ cur_vector[2] = ((dd->img[iter] & 0x07) << 5) | 0x1C;
+ m4wrist_send_bitstream(dd, &(cur_vector[0]), 22);
+
+ cur_addr++;
+ iter++;
+ }
+
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_enable[0]),
+ M4WRIST_SYNC_ENABLE_BITS);
+
+ cur_blk_data[0] = 0xDE;
+ cur_blk_data[1] = 0xE0;
+ cur_blk_data[2] = 0x1E;
+ cur_blk_data[3] = 0x7D;
+ cur_blk_data[4] = (blk_num & 0xFE) >> 1;
+ cur_blk_data[5] = ((blk_num & 0x01) << 7) | 0x70;
+ m4wrist_send_bitstream(dd, &(cur_blk_data[0]), 44);
+
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_disable[0]),
+ M4WRIST_SYNC_DISABLE_BITS);
+
+ m4wrist_send_bitstream(dd, &(m4wrist_program_and_verify[0]),
+ M4WRIST_PROGRAM_AND_VERIFY_BITS);
+
+ err = m4wrist_wait_poll(dd);
+ if (err < 0) {
+ printk(KERN_ERR "%s: %s for block 0x%02X.\n",
+ __func__, "Failed to wait-and-poll", blk_num);
+ goto m4wrist_program_image_fail;
+ }
+
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_enable[0]),
+ M4WRIST_SYNC_ENABLE_BITS);
+
+ err = m4wrist_read_id_word(dd, &(status[0]));
+ if (err < 0) {
+ printk(KERN_ERR "%s: %s for block 0x%02X.\n",
+ __func__, "Failed to read status", blk_num);
+ }
+
+ m4wrist_send_bitstream(dd, &(m4wrist_sync_disable[0]),
+ M4WRIST_SYNC_DISABLE_BITS);
+
+ if (status[0] != 0x00) {
+ printk(KERN_ERR "%s: %s 0x%02X %s 0x%02X.\n", __func__,
+ "Programming block", blk_num,
+ "failed with error code", status[0]);
+ err = -EINVAL;
+ goto m4wrist_program_image_fail;
+ }
+
+ blk_num++;
+ }
+
+m4wrist_program_image_fail:
+ return err;
+}
+
+static void m4wrist_send_bitstream(struct m4wrist_driver_data *dd,
+ uint8_t *stream, uint32_t bits)
+{
+ int i = 0;
+ int j = 0;
+ uint32_t bits_sent = 0;
+
+ j = 7;
+ while (bits_sent < bits) {
+ if (stream[i] & (0x01 << j))
+ gpio_set_value(dd->gpio_data, 1);
+ else
+ gpio_set_value(dd->gpio_data, 0);
+
+ gpio_set_value(dd->gpio_clk, 1);
+ gpio_set_value(dd->gpio_clk, 0);
+
+ bits_sent++;
+ j--;
+ if (j < 0) {
+ j = 7;
+ i++;
+ }
+ }
+
+ return;
+}
+
+static void m4wrist_toggle_clock(struct m4wrist_driver_data *dd, int cycles)
+{
+ int i = 0;
+
+ for (i = 0; i < cycles; i++) {
+ gpio_set_value(dd->gpio_clk, 1);
+ gpio_set_value(dd->gpio_clk, 0);
+ }
+
+ return;
+}
+
+static int m4wrist_wait_poll(struct m4wrist_driver_data *dd)
+{
+ int err = 0;
+ int i = 0;
+ bool saw_event = false;
+ uint8_t clear[5] = {0x00, 0x00, 0x00, 0x00, 0x00};
+
+ err = gpio_direction_input(dd->gpio_data);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to release data line.\n", __func__);
+ goto m4wrist_wait_poll_fail;
+ }
+
+ udelay(1);
+
+ for (i = 0; i < 200000; i++) {
+ if (gpio_get_value(dd->gpio_data) == 1) {
+ saw_event = true;
+ break;
+ } else {
+ m4wrist_toggle_clock(dd, 1);
+ }
+ }
+
+ if (!saw_event) {
+ printk(KERN_ERR "%s: Timeout waiting for data high.\n",
+ __func__);
+ err = -ETIME;
+ goto m4wrist_wait_poll_fail;
+ }
+
+ saw_event = false;
+ for (i = 0; i < 200000; i++) {
+ if (gpio_get_value(dd->gpio_data) == 0) {
+ saw_event = true;
+ break;
+ } else {
+ udelay(1);
+ }
+ }
+
+ if (!saw_event) {
+ printk(KERN_ERR "%s: Timeout waiting for data low.\n",
+ __func__);
+ err = -ETIME;
+ goto m4wrist_wait_poll_fail;
+ }
+
+ err = gpio_direction_output(dd->gpio_data, 0);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to acquire data line.\n",
+ __func__);
+ goto m4wrist_wait_poll_fail;
+ }
+
+ m4wrist_send_bitstream(dd, &(clear[0]), 40);
+
+m4wrist_wait_poll_fail:
+ return err;
+}
+
+static int m4wrist_read_id_word(struct m4wrist_driver_data *dd, uint8_t *data)
+{
+ int err = 0;
+ int i = 0;
+ int bit = 0;
+ uint8_t stream1[2] = {0xBF, 0x00};
+ uint8_t stream2[2] = {0xDF, 0x90};
+
+ m4wrist_send_bitstream(dd, &(stream1[0]), 11);
+
+ err = gpio_direction_input(dd->gpio_data);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to release initial data line.\n",
+ __func__);
+ goto m4wrist_read_id_word_fail;
+ }
+
+ m4wrist_toggle_clock(dd, 2);
+
+ data[0] = 0x00;
+ for (i = 0; i <= 7; i++) {
+ gpio_set_value(dd->gpio_clk, 1);
+ data[0] = (data[0] << 1);
+ bit = gpio_get_value(dd->gpio_data);
+ if (bit == 1)
+ (data[0])++;
+
+ gpio_set_value(dd->gpio_clk, 0);
+ }
+
+ gpio_set_value(dd->gpio_clk, 1);
+
+ err = gpio_direction_output(dd->gpio_data, 1);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to acquire initial data line.\n",
+ __func__);
+ goto m4wrist_read_id_word_fail;
+ }
+
+ m4wrist_send_bitstream(dd, &(stream2[0]), 12);
+
+ err = gpio_direction_input(dd->gpio_data);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to release final data line.\n",
+ __func__);
+ goto m4wrist_read_id_word_fail;
+ }
+
+ m4wrist_toggle_clock(dd, 2);
+
+ data[1] = 0x00;
+ for (i = 0; i <= 7; i++) {
+ gpio_set_value(dd->gpio_clk, 1);
+ data[1] = (data[1] << 1);
+ bit = gpio_get_value(dd->gpio_data);
+ if (bit == 1)
+ (data[1])++;
+
+ gpio_set_value(dd->gpio_clk, 0);
+ }
+
+ m4wrist_toggle_clock(dd, 1);
+
+ err = gpio_direction_output(dd->gpio_data, 0);
+ if (err < 0) {
+ printk(KERN_ERR "%s: Failed to acquire final data line.\n",
+ __func__);
+ goto m4wrist_read_id_word_fail;
+ }
+
+ m4wrist_toggle_clock(dd, 1);
+
+m4wrist_read_id_word_fail:
+ return err;
+}
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/m4sensorhub_wrist.h b/drivers/misc/m4sensorhub_wrist.h
new file mode 100644
index 00000000000..7065935cc17
--- /dev/null
+++ b/drivers/misc/m4sensorhub_wrist.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2013 Motorola Mobility, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _M4SENSORHUB_WRIST_H_
+#define _M4SENSORHUB_WRIST_H_
+
+#include <linux/types.h>
+#include <linux/platform_device.h>
+#include <linux/m4sensorhub.h>
+
+struct m4wrist_driver_data {
+ struct m4sensorhub_data *client;
+ struct mutex *mutex;
+ struct platform_device *pdev;
+
+ int gpio_xres;
+ int gpio_clk;
+ int gpio_data;
+
+ uint8_t *img;
+ uint32_t size;
+ uint8_t si_id[2];
+ uint8_t fw_ver[2];
+} __packed;
+
+#define M4WRIST_ID_SETUP_1_BITS 594
+static uint8_t m4wrist_id_setup_1[] = {
+ 0xCA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0D, 0xEE, 0x21, 0xF7, 0xF0, 0x27, 0xDC, 0x40,
+ 0x9F, 0x70, 0x01, 0xFD, 0xEE, 0x01, 0xE7, 0xC1,
+ 0xD7, 0x9F, 0x20, 0x7E, 0x7D, 0x88, 0x7D, 0xEE,
+ 0x21, 0xF7, 0xF0, 0x07, 0xDC, 0x40, 0x1F, 0x70,
+ 0x01, 0xFD, 0xEE, 0x01, 0xF7, 0xA0, 0x1F, 0xDE,
+ 0xA0, 0x1F, 0x7B, 0x00, 0x7D, 0xE0, 0x13, 0xF7,
+ 0xC0, 0x07, 0xDF, 0x28, 0x1F, 0x7D, 0x18, 0x7D,
+ 0xFE, 0x25, 0x80,
+};
+
+#define M4WRIST_ID_SETUP_2_BITS 418
+static uint8_t m4wrist_id_setup_2[] = {
+ 0xDE, 0xE2, 0x1F, 0x7F, 0x02, 0x7D, 0xC4, 0x09,
+ 0xF7, 0x00, 0x1F, 0x9F, 0x07, 0x5E, 0x7C, 0x81,
+ 0xF9, 0xF4, 0x01, 0xF7, 0xF0, 0x07, 0xDC, 0x40,
+ 0x1F, 0x70, 0x01, 0xFD, 0xEE, 0x01, 0xF7, 0xA0,
+ 0x1F, 0xDE, 0xA0, 0x1F, 0x7B, 0x00, 0x7D, 0xE0,
+ 0x0D, 0xF7, 0xC0, 0x07, 0xDF, 0x28, 0x1F, 0x7D,
+ 0x18, 0x7D, 0xFE, 0x25, 0x80,
+};
+
+#define M4WRIST_SYNC_ENABLE_BITS 110
+static uint8_t m4wrist_sync_enable[] = {
+ 0xDE, 0xE2, 0x1F, 0x7F, 0x02, 0x7D, 0xC4, 0x09,
+ 0xF7, 0x00, 0x1F, 0xDE, 0xE0, 0x1C,
+};
+
+#define M4WRIST_SYNC_DISABLE_BITS 110
+static uint8_t m4wrist_sync_disable[] = {
+ 0xDE, 0xE2, 0x1F, 0x71, 0x00, 0x7D, 0xFC, 0x01,
+ 0xF7, 0x00, 0x1F, 0xDE, 0xE0, 0x1C,
+};
+
+#define M4WRIST_ERASE_BITS 396
+static uint8_t m4wrist_erase[] = {
+ 0xDE, 0xE2, 0x1F, 0x7F, 0x02, 0x7D, 0xC4, 0x09,
+ 0xF7, 0x00, 0x1F, 0x9F, 0x07, 0x5E, 0x7C, 0x85,
+ 0xFD, 0xFC, 0x01, 0xF7, 0x10, 0x07, 0xDC, 0x00,
+ 0x7F, 0x7B, 0x80, 0x7D, 0xE0, 0x0B, 0xF7, 0xA0,
+ 0x1F, 0xDE, 0xA0, 0x1F, 0x7B, 0x04, 0x7D, 0xF0,
+ 0x01, 0xF7, 0xC9, 0x87, 0xDF, 0x48, 0x1F, 0x7F,
+ 0x89, 0x60,
+};
+
+#define M4WRIST_READ_WRITE_SETUP_BITS 66
+static uint8_t m4wrist_read_write_setup[] = {
+ 0xDE, 0xF0, 0x1F, 0x78, 0x00, 0x7D, 0xA0, 0x03,
+ 0xC0,
+};
+
+#define M4WRIST_PROGRAM_AND_VERIFY_BITS 440
+static uint8_t m4wrist_program_and_verify[] = {
+ 0xDE, 0xE2, 0x1F, 0x7F, 0x02, 0x7D, 0xC4, 0x09,
+ 0xF7, 0x00, 0x1F, 0x9F, 0x07, 0x5E, 0x7C, 0x81,
+ 0xF9, 0xF7, 0x01, 0xF7, 0xF0, 0x07, 0xDC, 0x40,
+ 0x1F, 0x70, 0x01, 0xFD, 0xEE, 0x01, 0xF6, 0xA0,
+ 0x0F, 0xDE, 0x80, 0x7F, 0x7A, 0x80, 0x7D, 0xEC,
+ 0x01, 0xF7, 0x80, 0x57, 0xDF, 0x00, 0x1F, 0x7C,
+ 0xA0, 0x7D, 0xF4, 0x61, 0xF7, 0xF8, 0x96,
+};
+
+#endif /* _M4SENSORHUB_WRIST_H_ */
diff --git a/drivers/misc/vib-gpio.c b/drivers/misc/vib-gpio.c
new file mode 100644
index 00000000000..d80d34db9f3
--- /dev/null
+++ b/drivers/misc/vib-gpio.c
@@ -0,0 +1,224 @@
+/* drivers/misc/vib-gpio.c
+ *
+ * Copyright (C) 2009 Motorola, Inc.
+ * Copyright (C) 2008 Google, Inc.
+ * Author: Mike Lockwood <lockwood@android.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/vib-gpio.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+
+/* TODO: replace with correct header */
+#include "../staging/android/timed_output.h"
+
+struct vib_gpio_data {
+ struct timed_output_dev dev;
+ struct work_struct vib_work;
+ struct hrtimer timer;
+ spinlock_t lock;
+
+ struct vib_gpio_platform_data *pdata;
+
+ int vib_power_state;
+ int vib_state;
+};
+
+struct vib_gpio_data *misc_data;
+
+static void vib_gpio_set(int on)
+{
+ if (on) {
+ if (misc_data->pdata->power_on && !misc_data->vib_power_state) {
+ misc_data->pdata->power_on();
+ misc_data->vib_power_state = 1;
+ }
+ if (misc_data->pdata->gpio >= 0)
+ gpio_direction_output(misc_data->pdata->gpio,
+ misc_data->pdata->active_low ?
+ 0 : 1);
+ } else {
+ if (misc_data->pdata->gpio >= 0)
+ gpio_direction_output(misc_data->pdata->gpio,
+ misc_data->pdata->active_low ?
+ 1 : 0);
+
+ if (misc_data->pdata->power_off && misc_data->vib_power_state) {
+ misc_data->pdata->power_off();
+ misc_data->vib_power_state = 0;
+ }
+ }
+}
+
+static void vib_gpio_update(struct work_struct *work)
+{
+ vib_gpio_set(misc_data->vib_state);
+}
+
+static enum hrtimer_restart gpio_timer_func(struct hrtimer *timer)
+{
+ struct vib_gpio_data *data =
+ container_of(timer, struct vib_gpio_data, timer);
+ data->vib_state = 0;
+ schedule_work(&data->vib_work);
+ return HRTIMER_NORESTART;
+}
+
+static int vib_gpio_get_time(struct timed_output_dev *dev)
+{
+ struct vib_gpio_data *data =
+ container_of(dev, struct vib_gpio_data, dev);
+
+ if (hrtimer_active(&data->timer)) {
+ ktime_t r = hrtimer_get_remaining(&data->timer);
+ struct timeval t = ktime_to_timeval(r);
+ return t.tv_sec * 1000 + t.tv_usec / 1000;
+ } else
+ return 0;
+}
+
+static void vib_gpio_enable(struct timed_output_dev *dev, int value)
+{
+ struct vib_gpio_data *data =
+ container_of(dev, struct vib_gpio_data, dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&data->lock, flags);
+ hrtimer_cancel(&data->timer);
+
+ if (value == 0)
+ data->vib_state = 0;
+ else {
+ value = (value > data->pdata->max_timeout ?
+ data->pdata->max_timeout : value);
+ data->vib_state = 1;
+ hrtimer_start(&data->timer,
+ ktime_set(value / 1000, (value % 1000) * 1000000),
+ HRTIMER_MODE_REL);
+ }
+
+ spin_unlock_irqrestore(&data->lock, flags);
+
+ schedule_work(&data->vib_work);
+}
+
+/* This is a temporary solution until a more global haptics soltion is
+ * available for haptics that need to occur in any application */
+void vibrator_haptic_fire(int value)
+{
+ vib_gpio_enable(&misc_data->dev, value);
+}
+
+static int vib_gpio_probe(struct platform_device *pdev)
+{
+ struct vib_gpio_platform_data *pdata = pdev->dev.platform_data;
+ struct vib_gpio_data *gpio_data;
+ int ret = 0;
+
+ if (!pdata) {
+ ret = -EBUSY;
+ goto err0;
+ }
+ gpio_data = kzalloc(sizeof(struct vib_gpio_data), GFP_KERNEL);
+ if (!gpio_data) {
+ ret = -ENOMEM;
+ goto err0;
+ }
+
+ gpio_data->pdata = pdata;
+
+ INIT_WORK(&gpio_data->vib_work, vib_gpio_update);
+
+ hrtimer_init(&gpio_data->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+
+ gpio_data->timer.function = gpio_timer_func;
+ spin_lock_init(&gpio_data->lock);
+
+ gpio_data->dev.name = "vibrator";
+ gpio_data->dev.get_time = vib_gpio_get_time;
+ gpio_data->dev.enable = vib_gpio_enable;
+ ret = timed_output_dev_register(&gpio_data->dev);
+ if (ret < 0)
+ goto err1;
+
+ if (gpio_data->pdata->init)
+ ret = gpio_data->pdata->init();
+ if (ret < 0)
+ goto err2;
+
+ misc_data = gpio_data;
+ if (misc_data->pdata->gpio >= 0)
+ gpio_direction_output(gpio_data->pdata->gpio,
+ gpio_data->pdata->active_low);
+
+ platform_set_drvdata(pdev, gpio_data);
+
+ vib_gpio_enable(&gpio_data->dev, gpio_data->pdata->initial_vibrate);
+
+ pr_info("vib gpio probe done");
+ return 0;
+
+err2:
+ timed_output_dev_unregister(&gpio_data->dev);
+err1:
+ kfree(gpio_data->pdata);
+ kfree(gpio_data);
+err0:
+ return ret;
+}
+
+static int vib_gpio_remove(struct platform_device *pdev)
+{
+ struct vib_gpio_data *gpio_data = platform_get_drvdata(pdev);
+
+ if (gpio_data->pdata->exit)
+ gpio_data->pdata->exit();
+
+ timed_output_dev_unregister(&gpio_data->dev);
+
+ kfree(gpio_data->pdata);
+ kfree(gpio_data);
+
+ return 0;
+}
+
+static struct platform_driver vib_gpio_driver = {
+ .probe = vib_gpio_probe,
+ .remove = vib_gpio_remove,
+ .driver = {
+ .name = VIB_GPIO_NAME,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init vib_gpio_init(void)
+{
+ return platform_driver_register(&vib_gpio_driver);
+}
+
+static void __exit vib_gpio_exit(void)
+{
+ platform_driver_unregister(&vib_gpio_driver);
+}
+
+late_initcall(vib_gpio_init);
+module_exit(vib_gpio_exit);
+
+MODULE_AUTHOR("Motorola");
+MODULE_DESCRIPTION("vib gpio driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index eccedc7d06a..a347ade7664 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -40,6 +40,8 @@
#include <linux/pinctrl/consumer.h>
#include <linux/pm_runtime.h>
#include <linux/platform_data/mmc-omap.h>
+#include <linux/wl12xx.h>
+#include <asm-generic/gpio.h>
/* OMAP HSMMC Host Controller Registers */
#define OMAP_HSMMC_SYSSTATUS 0x0014
@@ -1716,9 +1718,10 @@ MODULE_DEVICE_TABLE(of, omap_mmc_of_match);
static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
{
struct omap_mmc_platform_data *pdata;
+ struct wl12xx_platform_data minnow_wlan_data;
struct device_node *np = dev->of_node;
u32 bus_width, max_freq;
- int cd_gpio, wp_gpio;
+ int cd_gpio, wp_gpio, wl_host_wake_gpio = -1;
cd_gpio = of_get_named_gpio(np, "cd-gpios", 0);
wp_gpio = of_get_named_gpio(np, "wp-gpios", 0);
@@ -1756,6 +1759,30 @@ static struct omap_mmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
if (of_find_property(np, "ti,needs-special-hs-handling", NULL))
pdata->slots[0].features |= HSMMC_HAS_HSPE_SUPPORT;
+ /* wlan_host_wake_gpio is needed by TI WLAN driver in TI proprietary
+ * code under repo hardware/ti/wlan, retrieve this gpio here from dts
+ * and pass to TI WLAN driver through the existing interface
+ * of wl12xx_set_platform_data, so not to chanage TI code
+ */
+ if (!of_property_read_u32(np, "wl_host_wake_gpio",
+ &wl_host_wake_gpio)) {
+ pr_info("wl_host_wake_gpio is %d\n", wl_host_wake_gpio);
+ if (gpio_request(wl_host_wake_gpio, "wifi_irq") < 0) {
+ printk(KERN_ERR "%s: can't reserve GPIO: %d\n",
+ __func__, wl_host_wake_gpio);
+ devm_kfree(dev, pdata);
+ return NULL;
+ }
+ gpio_direction_input(wl_host_wake_gpio);
+ minnow_wlan_data.irq = __gpio_to_irq(wl_host_wake_gpio);
+ if (wl12xx_set_platform_data(&minnow_wlan_data)) {
+ printk(KERN_ERR "%s: Error setting wl18xx data\n",
+ __func__);
+ devm_kfree(dev, pdata);
+ return NULL;
+ }
+ }
+
return pdata;
}
#else
diff --git a/drivers/of/base.c b/drivers/of/base.c
index a6f584a7f4a..57534569fbc 100644
--- a/drivers/of/base.c
+++ b/drivers/of/base.c
@@ -237,7 +237,7 @@ static int __of_device_is_compatible(const struct device_node *device,
const char *compat)
{
const char* cp;
- int cplen, l;
+ int cplen = 0, l;
cp = __of_get_property(device, "compatible", &cplen);
if (cp == NULL)
@@ -301,7 +301,7 @@ EXPORT_SYMBOL(of_machine_is_compatible);
static int __of_device_is_available(const struct device_node *device)
{
const char *status;
- int statlen;
+ int statlen = 0;
status = __of_get_property(device, "status", &statlen);
if (status == NULL)
diff --git a/drivers/pinctrl/pinconf.c b/drivers/pinctrl/pinconf.c
index 694c3ace452..4d9ee680553 100644
--- a/drivers/pinctrl/pinconf.c
+++ b/drivers/pinctrl/pinconf.c
@@ -604,7 +604,7 @@ static int pinconf_dbg_config_print(struct seq_file *s, void *d)
const struct pinctrl_map *map;
struct pinctrl_dev *pctldev = NULL;
const struct pinconf_ops *confops = NULL;
- const struct pinctrl_map_configs *configs;
+ const struct pinctrl_map_configs *configs = NULL;
struct dbg_cfg *dbg = &pinconf_dbg_conf;
int i, j;
bool found = false;
@@ -682,7 +682,7 @@ static int pinconf_dbg_config_write(struct file *file,
struct pinctrl_dev *pctldev = NULL;
const struct pinconf_ops *confops = NULL;
struct dbg_cfg *dbg = &pinconf_dbg_conf;
- const struct pinctrl_map_configs *configs;
+ const struct pinctrl_map_configs *configs = NULL;
char config[MAX_NAME_LEN+1];
bool found = false;
char buf[128];
diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig
index 7b8979c63f4..1aefca3cb15 100644
--- a/drivers/power/Kconfig
+++ b/drivers/power/Kconfig
@@ -362,6 +362,18 @@ config BATTERY_GOLDFISH
Say Y to enable support for the battery and AC power in the
Goldfish emulator.
+config CHARGER_CPCAP
+ tristate "CPCAP Charger Driver"
+ depends on MFD_CPCAP && BATTERY_CPCAP
+ help
+ Say Y to include support for CPCAP Main Battery Charger.
+
+config BATTERY_CPCAP
+ tristate "CPCAP Battery Driver"
+ depends on MFD_CPCAP
+ help
+ Say Y to include support for CPCAP Main Battery.
+
source "drivers/power/reset/Kconfig"
endif # POWER_SUPPLY
diff --git a/drivers/power/Makefile b/drivers/power/Makefile
index 653bf6ceff3..9311e1a75d6 100644
--- a/drivers/power/Makefile
+++ b/drivers/power/Makefile
@@ -54,3 +54,7 @@ obj-$(CONFIG_POWER_AVS) += avs/
obj-$(CONFIG_CHARGER_SMB347) += smb347-charger.o
obj-$(CONFIG_CHARGER_TPS65090) += tps65090-charger.o
obj-$(CONFIG_POWER_RESET) += reset/
+obj-$(CONFIG_CHARGER_CPCAP) += cpcap-charger.o
+obj-$(CONFIG_CHARGER_CPCAP) += cpcap-usb-charger.o
+obj-$(CONFIG_CHARGER_CPCAP) += cpcap-factory.o
+obj-$(CONFIG_BATTERY_CPCAP) += cpcap-battery.o \ No newline at end of file
diff --git a/drivers/power/cpcap-battery.c b/drivers/power/cpcap-battery.c
new file mode 100644
index 00000000000..517d41bb919
--- /dev/null
+++ b/drivers/power/cpcap-battery.c
@@ -0,0 +1,823 @@
+/*
+ * Copyright (C) 2007-2009 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <asm/div64.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/poll.h>
+#include <linux/power_supply.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+#include <linux/spi/spi.h>
+#include <linux/time.h>
+#include <linux/miscdevice.h>
+#include <linux/debugfs.h>
+
+#define CPCAP_BATT_IRQ_BATTDET 0x01
+#define CPCAP_BATT_IRQ_OV 0x02
+#define CPCAP_BATT_IRQ_CC_CAL 0x04
+#define CPCAP_BATT_IRQ_ADCDONE 0x08
+#define CPCAP_BATT_IRQ_MACRO 0x10
+
+static long cpcap_batt_ioctl(
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg);
+static unsigned int cpcap_batt_poll(struct file *file, poll_table *wait);
+static int cpcap_batt_open(struct inode *inode, struct file *file);
+static ssize_t cpcap_batt_read(struct file *file, char *buf, size_t count,
+ loff_t *ppos);
+static int cpcap_batt_probe(struct platform_device *pdev);
+static int cpcap_batt_remove(struct platform_device *pdev);
+static int cpcap_batt_resume(struct platform_device *pdev);
+
+struct cpcap_batt_ps {
+ struct power_supply batt;
+ struct power_supply ac;
+ struct power_supply usb;
+ struct cpcap_device *cpcap;
+ struct cpcap_batt_data batt_state;
+ struct cpcap_batt_ac_data ac_state;
+ struct cpcap_batt_usb_data usb_state;
+ struct cpcap_adc_request req;
+ struct mutex lock;
+ char irq_status;
+ char data_pending;
+ wait_queue_head_t wait;
+ char async_req_pending;
+ unsigned long last_run_time;
+ bool no_update;
+};
+
+static const struct file_operations batt_fops = {
+ .owner = THIS_MODULE,
+ .open = cpcap_batt_open,
+ .unlocked_ioctl = cpcap_batt_ioctl,
+ .read = cpcap_batt_read,
+ .poll = cpcap_batt_poll,
+};
+
+static struct miscdevice batt_dev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "cpcap_batt",
+ .fops = &batt_fops,
+};
+
+static enum power_supply_property cpcap_batt_props[] = {
+ POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
+ POWER_SUPPLY_PROP_PRESENT,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_VOLTAGE_NOW,
+ POWER_SUPPLY_PROP_TEMP,
+ POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ POWER_SUPPLY_PROP_CHARGE_COUNTER
+};
+
+static enum power_supply_property cpcap_batt_ac_props[] =
+{
+ POWER_SUPPLY_PROP_ONLINE
+};
+
+static enum power_supply_property cpcap_batt_usb_props[] =
+{
+ POWER_SUPPLY_PROP_ONLINE,
+ POWER_SUPPLY_PROP_CURRENT_NOW,
+ POWER_SUPPLY_PROP_MODEL_NAME
+};
+
+static struct platform_driver cpcap_batt_driver = {
+ .probe = cpcap_batt_probe,
+ .remove = cpcap_batt_remove,
+ .resume = cpcap_batt_resume,
+ .driver = {
+ .name = "cpcap_battery",
+ .owner = THIS_MODULE,
+ },
+};
+
+static struct cpcap_batt_ps *cpcap_batt_sply;
+
+void cpcap_batt_irq_hdlr(enum cpcap_irqs irq, void *data)
+{
+ struct cpcap_batt_ps *sply = data;
+
+ mutex_lock(&sply->lock);
+ sply->data_pending = 1;
+
+ switch (irq) {
+ case CPCAP_IRQ_BATTDETB:
+ sply->irq_status |= CPCAP_BATT_IRQ_BATTDET;
+ cpcap_irq_unmask(sply->cpcap, irq);
+ break;
+
+ case CPCAP_IRQ_VBUSOV:
+ sply->irq_status |= CPCAP_BATT_IRQ_OV;
+ cpcap_irq_unmask(sply->cpcap, irq);
+ break;
+
+ case CPCAP_IRQ_CC_CAL:
+ sply->irq_status |= CPCAP_BATT_IRQ_CC_CAL;
+ cpcap_irq_unmask(sply->cpcap, irq);
+ break;
+
+ case CPCAP_IRQ_UC_PRIMACRO_7:
+ case CPCAP_IRQ_UC_PRIMACRO_8:
+ case CPCAP_IRQ_UC_PRIMACRO_9:
+ case CPCAP_IRQ_UC_PRIMACRO_10:
+ case CPCAP_IRQ_UC_PRIMACRO_11:
+ sply->irq_status |= CPCAP_BATT_IRQ_MACRO;
+ break;
+ default:
+ break;
+ }
+
+ mutex_unlock(&sply->lock);
+
+ wake_up_interruptible(&sply->wait);
+}
+
+void cpcap_batt_adc_hdlr(struct cpcap_device *cpcap, void *data)
+{
+ struct cpcap_batt_ps *sply = data;
+ mutex_lock(&sply->lock);
+
+ sply->async_req_pending = 0;
+
+ sply->data_pending = 1;
+
+ sply->irq_status |= CPCAP_BATT_IRQ_ADCDONE;
+
+ mutex_unlock(&sply->lock);
+
+ wake_up_interruptible(&sply->wait);
+}
+
+static int cpcap_batt_open(struct inode *inode, struct file *file)
+{
+ file->private_data = cpcap_batt_sply;
+ return 0;
+}
+
+static unsigned int cpcap_batt_poll(struct file *file, poll_table *wait)
+{
+ struct cpcap_batt_ps *sply = file->private_data;
+ unsigned int ret = 0;
+
+ poll_wait(file, &sply->wait, wait);
+
+ if (sply->data_pending)
+ ret = (POLLIN | POLLRDNORM);
+
+ return ret;
+}
+
+static ssize_t cpcap_batt_read(struct file *file,
+ char *buf, size_t count, loff_t *ppos)
+{
+ struct cpcap_batt_ps *sply = file->private_data;
+ int ret = -EFBIG;
+ unsigned long long temp;
+
+ if (count >= sizeof(char)) {
+ mutex_lock(&sply->lock);
+ if (!copy_to_user((void *)buf, (void *)&sply->irq_status,
+ sizeof(sply->irq_status)))
+ ret = sizeof(sply->irq_status);
+ else
+ ret = -EFAULT;
+ sply->data_pending = 0;
+ temp = sched_clock();
+ do_div(temp, NSEC_PER_SEC);
+ sply->last_run_time = (unsigned long)temp;
+
+ sply->irq_status = 0;
+ mutex_unlock(&sply->lock);
+ }
+
+ return ret;
+}
+
+static long cpcap_batt_ioctl(
+ struct file *file,
+ unsigned int cmd,
+ unsigned long arg)
+{
+ long ret = 0;
+ int i;
+ struct cpcap_batt_ps *sply = file->private_data;
+ struct cpcap_adc_request *req_async = &sply->req;
+ struct cpcap_adc_request req;
+ struct cpcap_adc_us_request req_us;
+ struct spi_device *spi = sply->cpcap->spi;
+ struct cpcap_platform_data *data = spi->controller_data;
+
+ switch (cmd) {
+ case CPCAP_IOCTL_BATT_DISPLAY_UPDATE:
+ if (sply->no_update)
+ return 0;
+ if (copy_from_user((void *)&sply->batt_state,
+ (void *)arg, sizeof(struct cpcap_batt_data)))
+ return -EFAULT;
+ power_supply_changed(&sply->batt);
+
+ if (data->batt_changed)
+ data->batt_changed(&sply->batt, &sply->batt_state);
+ break;
+
+ case CPCAP_IOCTL_BATT_ATOD_ASYNC:
+ mutex_lock(&sply->lock);
+ if (!sply->async_req_pending) {
+ if (copy_from_user((void *)&req_us, (void *)arg,
+ sizeof(struct cpcap_adc_us_request)
+ )) {
+ mutex_unlock(&sply->lock);
+ return -EFAULT;
+ }
+
+ req_async->format = req_us.format;
+ req_async->timing = req_us.timing;
+ req_async->type = req_us.type;
+ req_async->callback = cpcap_batt_adc_hdlr;
+ req_async->callback_param = sply;
+
+ ret = cpcap_adc_async_read(sply->cpcap, req_async);
+ if (!ret)
+ sply->async_req_pending = 1;
+ } else {
+ ret = -EAGAIN;
+ }
+ mutex_unlock(&sply->lock);
+
+ break;
+
+ case CPCAP_IOCTL_BATT_ATOD_SYNC:
+ if (copy_from_user((void *)&req_us, (void *)arg,
+ sizeof(struct cpcap_adc_us_request)))
+ return -EFAULT;
+
+ req.format = req_us.format;
+ req.timing = req_us.timing;
+ req.type = req_us.type;
+
+ ret = cpcap_adc_sync_read(sply->cpcap, &req);
+
+ if (ret)
+ return ret;
+
+ req_us.status = req.status;
+ for (i = 0; i < CPCAP_ADC_BANK0_NUM; i++)
+ req_us.result[i] = req.result[i];
+
+ if (copy_to_user((void *)arg, (void *)&req_us,
+ sizeof(struct cpcap_adc_us_request)))
+ return -EFAULT;
+ break;
+
+ case CPCAP_IOCTL_BATT_ATOD_READ:
+ req_us.format = req_async->format;
+ req_us.timing = req_async->timing;
+ req_us.type = req_async->type;
+ req_us.status = req_async->status;
+ for (i = 0; i < CPCAP_ADC_BANK0_NUM; i++)
+ req_us.result[i] = req_async->result[i];
+
+ if (copy_to_user((void *)arg, (void *)&req_us,
+ sizeof(struct cpcap_adc_us_request)))
+ return -EFAULT;
+ break;
+
+ default:
+ return -ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+
+static int cpcap_batt_ac_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ struct cpcap_batt_ps *sply = container_of(psy, struct cpcap_batt_ps,
+ ac);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = sply->ac_state.online;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static char *cpcap_batt_usb_models[] = {
+ "none", "usb", "factory"
+};
+
+static int cpcap_batt_usb_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ struct cpcap_batt_ps *sply = container_of(psy, struct cpcap_batt_ps,
+ usb);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_ONLINE:
+ val->intval = sply->usb_state.online;
+ break;
+ case POWER_SUPPLY_PROP_CURRENT_NOW:
+ val->intval = sply->usb_state.current_now;
+ break;
+ case POWER_SUPPLY_PROP_MODEL_NAME:
+ val->strval = cpcap_batt_usb_models[sply->usb_state.model];
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int cpcap_batt_get_property(struct power_supply *psy,
+ enum power_supply_property psp,
+ union power_supply_propval *val)
+{
+ int ret = 0;
+ struct cpcap_batt_ps *sply = container_of(psy, struct cpcap_batt_ps,
+ batt);
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ val->intval = sply->batt_state.status;
+ break;
+
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = sply->batt_state.health;
+ break;
+
+ case POWER_SUPPLY_PROP_PRESENT:
+ val->intval = sply->batt_state.present;
+ break;
+
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
+
+ case POWER_SUPPLY_PROP_CAPACITY:
+ val->intval = sply->batt_state.capacity;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ val->intval = sply->batt_state.batt_volt;
+ break;
+
+ case POWER_SUPPLY_PROP_TEMP:
+ val->intval = sply->batt_state.batt_temp;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ val->intval = sply->batt_state.batt_full_capacity;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ val->intval = sply->batt_state.batt_capacity_one;
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int cpcap_batt_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct cpcap_batt_ps *sply;
+
+ if (pdev->dev.platform_data == NULL) {
+ dev_err(&pdev->dev, "no platform_data\n");
+ ret = -EINVAL;
+ goto prb_exit;
+ }
+
+ sply = kzalloc(sizeof(struct cpcap_batt_ps), GFP_KERNEL);
+ if (sply == NULL) {
+ ret = -ENOMEM;
+ goto prb_exit;
+ }
+
+ sply->cpcap = pdev->dev.platform_data;
+ mutex_init(&sply->lock);
+ init_waitqueue_head(&sply->wait);
+
+ sply->batt_state.status = POWER_SUPPLY_STATUS_UNKNOWN;
+ sply->batt_state.health = POWER_SUPPLY_HEALTH_GOOD;
+ sply->batt_state.present = 1;
+ sply->batt_state.capacity = 100; /* Percentage */
+ sply->batt_state.batt_volt = 4200000; /* uV */
+ sply->batt_state.batt_temp = 230; /* tenths of degrees Celsius */
+ sply->batt_state.batt_full_capacity = 0;
+ sply->batt_state.batt_capacity_one = 100;
+
+ sply->ac_state.online = 0;
+
+ sply->usb_state.online = 0;
+ sply->usb_state.current_now = 0;
+ sply->usb_state.model = CPCAP_BATT_USB_MODEL_NONE;
+
+ sply->batt.properties = cpcap_batt_props;
+ sply->batt.num_properties = ARRAY_SIZE(cpcap_batt_props);
+ sply->batt.get_property = cpcap_batt_get_property;
+ sply->batt.name = "battery";
+ sply->batt.type = POWER_SUPPLY_TYPE_BATTERY;
+
+ sply->ac.properties = cpcap_batt_ac_props;
+ sply->ac.num_properties = ARRAY_SIZE(cpcap_batt_ac_props);
+ sply->ac.get_property = cpcap_batt_ac_get_property;
+ sply->ac.name = "ac";
+ sply->ac.type = POWER_SUPPLY_TYPE_MAINS;
+
+ sply->usb.properties = cpcap_batt_usb_props;
+ sply->usb.num_properties = ARRAY_SIZE(cpcap_batt_usb_props);
+ sply->usb.get_property = cpcap_batt_usb_get_property;
+ sply->usb.name = "usb";
+ sply->usb.type = POWER_SUPPLY_TYPE_USB;
+
+ sply->no_update = false;
+
+ ret = power_supply_register(&pdev->dev, &sply->ac);
+ if (ret)
+ goto prb_exit;
+ ret = power_supply_register(&pdev->dev, &sply->batt);
+ if (ret)
+ goto unregac_exit;
+ ret = power_supply_register(&pdev->dev, &sply->usb);
+ if (ret)
+ goto unregbatt_exit;
+ platform_set_drvdata(pdev, sply);
+ sply->cpcap->battdata = sply;
+ cpcap_batt_sply = sply;
+
+ ret = misc_register(&batt_dev);
+ if (ret)
+ goto unregusb_exit;
+
+ ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_VBUSOV,
+ cpcap_batt_irq_hdlr, sply);
+ if (ret)
+ goto unregmisc_exit;
+ ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_BATTDETB,
+ cpcap_batt_irq_hdlr, sply);
+ if (ret)
+ goto unregirq_exit;
+ ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_CC_CAL,
+ cpcap_batt_irq_hdlr, sply);
+ if (ret)
+ goto unregirq_exit;
+
+ ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_7,
+ cpcap_batt_irq_hdlr, sply);
+ cpcap_irq_mask(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_7);
+
+ if (ret)
+ goto unregirq_exit;
+
+ ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_8,
+ cpcap_batt_irq_hdlr, sply);
+ cpcap_irq_mask(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_8);
+
+ if (ret)
+ goto unregirq_exit;
+
+ ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_9,
+ cpcap_batt_irq_hdlr, sply);
+ cpcap_irq_mask(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_9);
+
+ if (ret)
+ goto unregirq_exit;
+
+ ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_10,
+ cpcap_batt_irq_hdlr, sply);
+ cpcap_irq_mask(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_10);
+
+ if (ret)
+ goto unregirq_exit;
+
+ ret = cpcap_irq_register(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_11,
+ cpcap_batt_irq_hdlr, sply);
+ cpcap_irq_mask(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_11);
+
+ if (ret)
+ goto unregirq_exit;
+
+ goto prb_exit;
+
+unregirq_exit:
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_VBUSOV);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_BATTDETB);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_CC_CAL);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_7);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_8);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_9);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_10);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_11);
+unregmisc_exit:
+ misc_deregister(&batt_dev);
+unregusb_exit:
+ power_supply_unregister(&sply->usb);
+unregbatt_exit:
+ power_supply_unregister(&sply->batt);
+unregac_exit:
+ power_supply_unregister(&sply->ac);
+
+prb_exit:
+ return ret;
+}
+
+static int cpcap_batt_remove(struct platform_device *pdev)
+{
+ struct cpcap_batt_ps *sply = platform_get_drvdata(pdev);
+
+ power_supply_unregister(&sply->batt);
+ power_supply_unregister(&sply->ac);
+ power_supply_unregister(&sply->usb);
+ misc_deregister(&batt_dev);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_VBUSOV);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_BATTDETB);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_CC_CAL);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_7);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_8);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_9);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_10);
+ cpcap_irq_free(sply->cpcap, CPCAP_IRQ_UC_PRIMACRO_11);
+ sply->cpcap->battdata = NULL;
+ kfree(sply);
+
+ return 0;
+}
+
+static int cpcap_batt_resume(struct platform_device *pdev)
+{
+ struct cpcap_batt_ps *sply = platform_get_drvdata(pdev);
+ unsigned long cur_time;
+ unsigned long long temp;
+
+ temp = sched_clock();
+ do_div(temp, NSEC_PER_SEC);
+ cur_time = ((unsigned long)temp);
+ if ((cur_time - sply->last_run_time) < 0)
+ sply->last_run_time = 0;
+
+ if ((cur_time - sply->last_run_time) > 50) {
+ mutex_lock(&sply->lock);
+ sply->data_pending = 1;
+ sply->irq_status |= CPCAP_BATT_IRQ_MACRO;
+
+ mutex_unlock(&sply->lock);
+
+ wake_up_interruptible(&sply->wait);
+ }
+
+ return 0;
+}
+
+void cpcap_batt_set_ac_prop(struct cpcap_device *cpcap, int online)
+{
+ struct cpcap_batt_ps *sply = cpcap->battdata;
+ struct spi_device *spi = cpcap->spi;
+ struct cpcap_platform_data *data = spi->controller_data;
+
+ if (sply != NULL) {
+ sply->ac_state.online = online;
+ power_supply_changed(&sply->ac);
+
+ if (data->ac_changed)
+ data->ac_changed(&sply->ac, &sply->ac_state);
+ }
+}
+EXPORT_SYMBOL(cpcap_batt_set_ac_prop);
+
+void cpcap_batt_set_usb_prop_online(struct cpcap_device *cpcap, int online,
+ enum cpcap_batt_usb_model model)
+{
+ struct cpcap_batt_ps *sply = cpcap->battdata;
+ struct spi_device *spi = cpcap->spi;
+ struct cpcap_platform_data *data = spi->controller_data;
+
+ if (sply != NULL) {
+ sply->usb_state.online = online;
+ sply->usb_state.model = model;
+ power_supply_changed(&sply->usb);
+
+ if (data->usb_changed)
+ data->usb_changed(&sply->usb, &sply->usb_state);
+ }
+}
+EXPORT_SYMBOL(cpcap_batt_set_usb_prop_online);
+
+void cpcap_batt_set_usb_prop_curr(struct cpcap_device *cpcap, unsigned int curr)
+{
+ struct cpcap_batt_ps *sply = cpcap->battdata;
+ struct spi_device *spi = cpcap->spi;
+ struct cpcap_platform_data *data = spi->controller_data;
+
+ if (sply != NULL) {
+ sply->usb_state.current_now = curr;
+ power_supply_changed(&sply->usb);
+
+ if (data->usb_changed)
+ data->usb_changed(&sply->usb, &sply->usb_state);
+ }
+}
+EXPORT_SYMBOL(cpcap_batt_set_usb_prop_curr);
+
+/*
+ * Debugfs interface to test how system works with different values of
+ * the battery properties. Once the propety value is set through the
+ * debugfs, updtes from the drivers will be discarded.
+ */
+#ifdef CONFIG_DEBUG_FS
+
+static int cpcap_batt_debug_set(void *prop, u64 val)
+{
+ int data = (int)val;
+ enum power_supply_property psp = (enum power_supply_property)prop;
+ struct cpcap_batt_ps *sply = cpcap_batt_sply;
+ bool changed = true;
+ sply->no_update = true;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ sply->batt_state.status = data;
+ break;
+
+ case POWER_SUPPLY_PROP_HEALTH:
+ sply->batt_state.health = data;
+ break;
+
+ case POWER_SUPPLY_PROP_PRESENT:
+ sply->batt_state.present = data;
+ break;
+
+ case POWER_SUPPLY_PROP_CAPACITY:
+ sply->batt_state.capacity = data;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ sply->batt_state.batt_volt = data;
+ break;
+
+ case POWER_SUPPLY_PROP_TEMP:
+ sply->batt_state.batt_temp = data;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ sply->batt_state.batt_full_capacity = data;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ sply->batt_state.batt_capacity_one = data;
+ break;
+
+ default:
+ changed = false;
+ break;
+ }
+
+ if (changed)
+ power_supply_changed(&sply->batt);
+
+ return 0;
+}
+
+static int cpcap_batt_debug_get(void *prop, u64 *val)
+{
+ enum power_supply_property psp = (enum power_supply_property)prop;
+ struct cpcap_batt_ps *sply = cpcap_batt_sply;
+
+ switch (psp) {
+ case POWER_SUPPLY_PROP_STATUS:
+ *val = sply->batt_state.status;
+ break;
+
+ case POWER_SUPPLY_PROP_HEALTH:
+ *val = sply->batt_state.health;
+ break;
+
+ case POWER_SUPPLY_PROP_PRESENT:
+ *val = sply->batt_state.present;
+ break;
+
+ case POWER_SUPPLY_PROP_CAPACITY:
+ *val = sply->batt_state.capacity;
+ break;
+
+ case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+ *val = sply->batt_state.batt_volt;
+ break;
+
+ case POWER_SUPPLY_PROP_TEMP:
+ *val = sply->batt_state.batt_temp;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN:
+ *val = sply->batt_state.batt_full_capacity;
+ break;
+
+ case POWER_SUPPLY_PROP_CHARGE_COUNTER:
+ *val = sply->batt_state.batt_capacity_one;
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(cpcap_battery_fops, cpcap_batt_debug_get,
+ cpcap_batt_debug_set, "%llu\n");
+
+static int __init cpcap_batt_debug_init(void)
+{
+ struct dentry *dent = debugfs_create_dir("battery", 0);
+ int ret = 0;
+
+ if (!IS_ERR(dent)) {
+ debugfs_create_file("status", 0666, dent,
+ (void *)POWER_SUPPLY_PROP_STATUS, &cpcap_battery_fops);
+ debugfs_create_file("health", 0666, dent,
+ (void *)POWER_SUPPLY_PROP_HEALTH, &cpcap_battery_fops);
+ debugfs_create_file("present", 0666, dent,
+ (void *)POWER_SUPPLY_PROP_PRESENT, &cpcap_battery_fops);
+ debugfs_create_file("voltage", 0666, dent,
+ (void *)POWER_SUPPLY_PROP_VOLTAGE_NOW, &cpcap_battery_fops);
+ debugfs_create_file("capacity", 0666, dent,
+ (void *)POWER_SUPPLY_PROP_CAPACITY, &cpcap_battery_fops);
+ debugfs_create_file("temp", 0666, dent,
+ (void *)POWER_SUPPLY_PROP_TEMP, &cpcap_battery_fops);
+ debugfs_create_file("charge_full_design", 0666, dent,
+ (void *)POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN,
+ &cpcap_battery_fops);
+ debugfs_create_file("charge_counter", 0666, dent,
+ (void *)POWER_SUPPLY_PROP_CHARGE_COUNTER,
+ &cpcap_battery_fops);
+ } else {
+ ret = PTR_ERR(dent);
+ }
+
+ return ret;
+}
+
+late_initcall(cpcap_batt_debug_init);
+
+#endif /* CONFIG_DEBUG_FS */
+
+static int __init cpcap_batt_init(void)
+{
+ return platform_driver_register(&cpcap_batt_driver);
+}
+subsys_initcall(cpcap_batt_init);
+
+static void __exit cpcap_batt_exit(void)
+{
+ platform_driver_unregister(&cpcap_batt_driver);
+}
+module_exit(cpcap_batt_exit);
+
+MODULE_ALIAS("platform:cpcap_batt");
+MODULE_DESCRIPTION("CPCAP BATTERY driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/cpcap-charger.c b/drivers/power/cpcap-charger.c
new file mode 100644
index 00000000000..1d73e43ea8f
--- /dev/null
+++ b/drivers/power/cpcap-charger.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2007-2009 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/cpcap.h>
+
+static int cpcap_chgr_probe(struct platform_device *pdev);
+static int cpcap_chgr_remove(struct platform_device *pdev);
+
+static struct platform_driver cpcap_chgr_driver = {
+ .probe = cpcap_chgr_probe,
+ .remove = cpcap_chgr_remove,
+ .driver = {
+ .name = "cpcap_charger",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int cpcap_chgr_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct cpcap_device *cpcap;
+
+ if (pdev->dev.platform_data == NULL) {
+ dev_err(&pdev->dev, "no platform_data\n");
+ return -EINVAL;
+ }
+
+ cpcap = pdev->dev.platform_data;
+ platform_set_drvdata(pdev, cpcap);
+ cpcap_batt_set_ac_prop(cpcap, 1);
+ return ret;
+}
+
+static int cpcap_chgr_remove(struct platform_device *pdev)
+{
+ struct cpcap_device *cpcap = platform_get_drvdata(pdev);
+ cpcap_batt_set_ac_prop(cpcap, 0);
+
+ return 0;
+}
+
+static int __init cpcap_chgr_init(void)
+{
+ return platform_driver_register(&cpcap_chgr_driver);
+}
+module_init(cpcap_chgr_init);
+
+static void __exit cpcap_chgr_exit(void)
+{
+ platform_driver_unregister(&cpcap_chgr_driver);
+}
+module_exit(cpcap_chgr_exit);
+
+MODULE_ALIAS("platform:cpcap_chgr");
+MODULE_DESCRIPTION("CPCAP CHARGER driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/cpcap-factory.c b/drivers/power/cpcap-factory.c
new file mode 100644
index 00000000000..802b917ecd2
--- /dev/null
+++ b/drivers/power/cpcap-factory.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2007-2009 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/cpcap.h>
+
+static int cpcap_factory_probe(struct platform_device *pdev);
+static int cpcap_factory_remove(struct platform_device *pdev);
+
+static struct platform_driver cpcap_factory_driver = {
+ .probe = cpcap_factory_probe,
+ .remove = cpcap_factory_remove,
+ .driver = {
+ .name = "cpcap_factory",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int cpcap_factory_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct cpcap_device *cpcap;
+
+ if (pdev->dev.platform_data == NULL) {
+ dev_err(&pdev->dev, "no platform_data\n");
+ return -EINVAL;
+ }
+
+ cpcap = pdev->dev.platform_data;
+ platform_set_drvdata(pdev, cpcap);
+
+ cpcap_batt_set_usb_prop_curr(cpcap, 0);
+ cpcap_batt_set_usb_prop_online(cpcap, 1, CPCAP_BATT_USB_MODEL_FACTORY);
+
+ return ret;
+}
+
+static int cpcap_factory_remove(struct platform_device *pdev)
+{
+ struct cpcap_device *cpcap = platform_get_drvdata(pdev);
+
+ cpcap_batt_set_usb_prop_curr(cpcap, 0);
+ cpcap_batt_set_usb_prop_online(cpcap, 0, CPCAP_BATT_USB_MODEL_NONE);
+
+ return 0;
+}
+
+static int __init cpcap_factory_init(void)
+{
+ return platform_driver_register(&cpcap_factory_driver);
+}
+module_init(cpcap_factory_init);
+
+static void __exit cpcap_factory_exit(void)
+{
+ platform_driver_unregister(&cpcap_factory_driver);
+}
+module_exit(cpcap_factory_exit);
+
+MODULE_ALIAS("platform:cpcap_factory");
+MODULE_DESCRIPTION("CPCAP Factory Device driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/cpcap-usb-charger.c b/drivers/power/cpcap-usb-charger.c
new file mode 100644
index 00000000000..ac98a144fef
--- /dev/null
+++ b/drivers/power/cpcap-usb-charger.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2007-2009 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spi/cpcap.h>
+
+static int cpcap_usb_chgr_probe(struct platform_device *pdev);
+static int cpcap_usb_chgr_remove(struct platform_device *pdev);
+
+static struct platform_driver cpcap_usb_chgr_driver = {
+ .probe = cpcap_usb_chgr_probe,
+ .remove = cpcap_usb_chgr_remove,
+ .driver = {
+ .name = "cpcap_usb_charger",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int cpcap_usb_chgr_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct cpcap_device *cpcap;
+
+ if (pdev->dev.platform_data == NULL) {
+ dev_err(&pdev->dev, "no platform_data\n");
+ return -EINVAL;
+ }
+
+ cpcap = pdev->dev.platform_data;
+ platform_set_drvdata(pdev, cpcap);
+
+ cpcap_batt_set_usb_prop_curr(cpcap, 500);
+ cpcap_batt_set_usb_prop_online(cpcap, 1, CPCAP_BATT_USB_MODEL_USB);
+
+ return ret;
+}
+
+static int cpcap_usb_chgr_remove(struct platform_device *pdev)
+{
+ struct cpcap_device *cpcap = platform_get_drvdata(pdev);
+
+ cpcap_batt_set_usb_prop_curr(cpcap, 0);
+ cpcap_batt_set_usb_prop_online(cpcap, 0, CPCAP_BATT_USB_MODEL_NONE);
+
+ return 0;
+}
+
+static int __init cpcap_usb_chgr_init(void)
+{
+ return platform_driver_register(&cpcap_usb_chgr_driver);
+}
+module_init(cpcap_usb_chgr_init);
+
+static void __exit cpcap_usb_chgr_exit(void)
+{
+ platform_driver_unregister(&cpcap_usb_chgr_driver);
+}
+module_exit(cpcap_usb_chgr_exit);
+
+MODULE_ALIAS("platform:cpcap_usb_chgr");
+MODULE_DESCRIPTION("CPCAP USB Charger Device driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 8bb26446037..07ad8046641 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -514,5 +514,11 @@ config REGULATOR_AS3711
This driver provides support for the voltage regulators on the
AS3711 PMIC
+config REGULATOR_CPCAP
+ tristate "CPCAP regulator driver"
+ depends on MFD_CPCAP
+ help
+ Say Y here to support the voltage regulators on CPCAP
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 47a34ff88f9..f9d18aacf81 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -70,6 +70,7 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
+obj-$(CONFIG_REGULATOR_CPCAP) += cpcap-regulator.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/cpcap-regulator.c b/drivers/regulator/cpcap-regulator.c
new file mode 100644
index 00000000000..1bed6fb32ac
--- /dev/null
+++ b/drivers/regulator/cpcap-regulator.c
@@ -0,0 +1,605 @@
+/*
+ * Copyright (C) 2009-2011 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+
+#include <linux/spi/spi.h>
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+
+#define CPCAP_REGULATOR(_name, _id) \
+ { \
+ .name = _name, \
+ .id = _id, \
+ .ops = &cpcap_regulator_ops, \
+ .type = REGULATOR_VOLTAGE, \
+ .owner = THIS_MODULE, \
+ }
+
+
+static const int sw4_val_tbl[] = {600000, 612500, 625000, 637500, 650000,
+ 662500, 675000, 687500, 700000, 712500,
+ 725000, 737500, 750000, 762500, 775000,
+ 787500, 800000, 812500, 825000, 837500,
+ 850000, 862500, 885000, 887500, 900000,
+ 912500, 925000, 937500, 950000, 962500,
+ 975000, 987500, 1000000, 1012500, 1025000,
+ 1037500, 1050000, 1062500, 1075000, 1087500,
+ 1100000, 1112500, 1125000, 1137500, 1150000,
+ 1162500, 1175000, 1187500, 1200000, 1212500,
+ 1225000, 1237500, 1250000, 1262500, 1275000,
+ 1287500, 1300000, 1312500, 1325000, 1337500,
+ 1350000, 1362500, 1375000, 1387500, 1400000,
+ 1412500, 1425000, 1437500, 1450000};
+static const int sw5_val_tbl[] = {0, 5050000};
+static const int vcam_val_tbl[] = {2600000, 2700000, 2800000, 2900000};
+static const int vcsi_val_tbl[] = {1200000, 1800000};
+static const int vdac_val_tbl[] = {1200000, 1500000, 1800000, 2500000};
+static const int vdig_val_tbl[] = {1200000, 1350000, 1500000, 1875000};
+static const int vfuse_val_tbl[] = {1500000, 1600000, 1700000, 1800000, 1900000,
+ 2000000, 2100000, 2200000, 2300000, 2400000,
+ 2500000, 2600000, 2700000, 3150000};
+static const int vhvio_val_tbl[] = {2775000};
+static const int vsdio_val_tbl[] = {1500000, 1600000, 1800000, 2600000,
+ 2700000, 2800000, 2900000, 3000000};
+static const int vpll_val_tbl[] = {1200000, 1300000, 1400000, 1800000};
+static const int vrf1_val_tbl[] = {2775000, 2500000}; /* Yes, this is correct */
+static const int vrf2_val_tbl[] = {0, 2775000};
+static const int vrfref_val_tbl[] = {2500000, 2775000};
+static const int vwlan1_val_tbl[] = {1800000, 1900000};
+static const int vwlan2_val_tbl[] = {2775000, 3000000, 3300000, 3300000};
+static const int vsim_val_tbl[] = {1800000, 2900000};
+static const int vsimcard_val_tbl[] = {1800000, 2900000};
+static const int vvib_val_tbl[] = {1300000, 1800000, 2000000, 3000000};
+static const int vusb_val_tbl[] = {0, 3300000};
+static const int vaudio_val_tbl[] = {0, 2775000};
+
+static struct {
+ const enum cpcap_reg reg;
+ const unsigned short mode_mask;
+ const unsigned short volt_mask;
+ const unsigned char volt_shft;
+ unsigned short mode_val;
+ unsigned short off_mode_val;
+ const int val_tbl_sz;
+ const int *val_tbl;
+ unsigned int mode_cntr;
+ const unsigned int volt_trans_time; /* in micro seconds */
+ const unsigned int turn_on_time; /* in micro seconds */
+} cpcap_regltr_data[CPCAP_NUM_REGULATORS] = {
+ [CPCAP_SW4] = {CPCAP_REG_S4C1,
+ 0x6F00,
+ 0x007F,
+ 0,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(sw4_val_tbl),
+ sw4_val_tbl,
+ 0,
+ 100,
+ 1500},
+
+ [CPCAP_SW5] = {CPCAP_REG_S5C,
+ 0x002A,
+ 0x0000,
+ 0,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(sw5_val_tbl),
+ sw5_val_tbl,
+ 0,
+ 0,
+ 1500},
+
+ [CPCAP_VCAM] = {CPCAP_REG_VCAMC,
+ 0x0087,
+ 0x0030,
+ 4,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vcam_val_tbl),
+ vcam_val_tbl,
+ 0,
+ 420,
+ 1000},
+
+ [CPCAP_VCSI] = {CPCAP_REG_VCSIC,
+ 0x0047,
+ 0x0010,
+ 4,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vcsi_val_tbl),
+ vcsi_val_tbl,
+ 0,
+ 350,
+ 1000},
+
+ [CPCAP_VDAC] = {CPCAP_REG_VDACC,
+ 0x0087,
+ 0x0030,
+ 4,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vdac_val_tbl),
+ vdac_val_tbl,
+ 0,
+ 420,
+ 1000},
+
+ [CPCAP_VDIG] = {CPCAP_REG_VDIGC,
+ 0x0087,
+ 0x0030,
+ 4,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vdig_val_tbl),
+ vdig_val_tbl,
+ 0,
+ 420,
+ 1000},
+
+ [CPCAP_VFUSE] = {CPCAP_REG_VFUSEC,
+ 0x0080,
+ 0x000F,
+ 0,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vfuse_val_tbl),
+ vfuse_val_tbl,
+ 0,
+ 420,
+ 1000},
+
+ [CPCAP_VHVIO] = {CPCAP_REG_VHVIOC,
+ 0x0017,
+ 0x0000,
+ 0,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vhvio_val_tbl),
+ vhvio_val_tbl,
+ 0,
+ 0,
+ 1000},
+
+ [CPCAP_VSDIO] = {CPCAP_REG_VSDIOC,
+ 0x0087,
+ 0x0038,
+ 3,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vsdio_val_tbl),
+ vsdio_val_tbl,
+ 0,
+ 420,
+ 1000},
+
+ [CPCAP_VPLL] = {CPCAP_REG_VPLLC,
+ 0x0043,
+ 0x0018,
+ 3,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vpll_val_tbl),
+ vpll_val_tbl,
+ 0,
+ 420,
+ 100},
+
+ [CPCAP_VRF1] = {CPCAP_REG_VRF1C,
+ 0x00AC,
+ 0x0002,
+ 1,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vrf1_val_tbl),
+ vrf1_val_tbl,
+ 0,
+ 10,
+ 1000},
+
+ [CPCAP_VRF2] = {CPCAP_REG_VRF2C,
+ 0x0023,
+ 0x0008,
+ 3,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vrf2_val_tbl),
+ vrf2_val_tbl,
+ 0,
+ 10,
+ 1000},
+
+ [CPCAP_VRFREF] = {CPCAP_REG_VRFREFC,
+ 0x0023,
+ 0x0008,
+ 3,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vrfref_val_tbl),
+ vrfref_val_tbl,
+ 0,
+ 420,
+ 100},
+
+ [CPCAP_VWLAN1] = {CPCAP_REG_VWLAN1C,
+ 0x0047,
+ 0x0010,
+ 4,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vwlan1_val_tbl),
+ vwlan1_val_tbl,
+ 0,
+ 420,
+ 1000},
+
+ [CPCAP_VWLAN2] = {CPCAP_REG_VWLAN2C,
+ 0x020C,
+ 0x00C0,
+ 6,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vwlan2_val_tbl),
+ vwlan2_val_tbl,
+ 0,
+ 420,
+ 1000},
+
+ [CPCAP_VSIM] = {CPCAP_REG_VSIMC,
+ 0x0023,
+ 0x0008,
+ 3,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vsim_val_tbl),
+ vsim_val_tbl,
+ 0,
+ 420,
+ 1000},
+
+ [CPCAP_VSIMCARD] = {CPCAP_REG_VSIMC,
+ 0x1E80,
+ 0x0008,
+ 3,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vsimcard_val_tbl),
+ vsimcard_val_tbl,
+ 0,
+ 420,
+ 1000},
+
+ [CPCAP_VVIB] = {CPCAP_REG_VVIBC,
+ 0x0001,
+ 0x000C,
+ 2,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vvib_val_tbl),
+ vvib_val_tbl,
+ 0,
+ 500,
+ 500},
+
+ [CPCAP_VUSB] = {CPCAP_REG_VUSBC,
+ 0x011C,
+ 0x0040,
+ 6,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vusb_val_tbl),
+ vusb_val_tbl,
+ 0,
+ 0,
+ 1000},
+
+ [CPCAP_VAUDIO] = {CPCAP_REG_VAUDIOC,
+ 0x0016,
+ 0x0001,
+ 0,
+ 0x0000,
+ 0x0000,
+ ARRAY_SIZE(vaudio_val_tbl),
+ vaudio_val_tbl,
+ 0,
+ 0,
+ 1000},
+};
+
+static int cpcap_regulator_set_voltage(struct regulator_dev *rdev, int min_uV,
+ int max_uV, unsigned *selector)
+{
+ struct cpcap_device *cpcap;
+ int regltr_id;
+ int retval;
+ enum cpcap_reg regnr;
+ int i;
+
+ cpcap = rdev_get_drvdata(rdev);
+
+ regltr_id = rdev_get_id(rdev);
+ if (regltr_id >= CPCAP_NUM_REGULATORS)
+ return -EINVAL;
+
+ regnr = cpcap_regltr_data[regltr_id].reg;
+
+ if (regltr_id == CPCAP_VRF1) {
+ if (min_uV > 2500000)
+ i = 0;
+ else
+ i = cpcap_regltr_data[regltr_id].volt_mask;
+ } else {
+ for (i = 0; i < cpcap_regltr_data[regltr_id].val_tbl_sz; i++)
+ if (cpcap_regltr_data[regltr_id].val_tbl[i] >= min_uV)
+ break;
+
+ if (i >= cpcap_regltr_data[regltr_id].val_tbl_sz)
+ i--;
+
+ i <<= cpcap_regltr_data[regltr_id].volt_shft;
+ }
+
+ retval = cpcap_regacc_write(cpcap, regnr, i,
+ cpcap_regltr_data[regltr_id].volt_mask);
+
+ if ((cpcap_regltr_data[regltr_id].volt_trans_time) && (retval == 0))
+ udelay(cpcap_regltr_data[regltr_id].volt_trans_time);
+
+ return retval;
+}
+
+static int cpcap_regulator_get_voltage(struct regulator_dev *rdev)
+{
+ struct cpcap_device *cpcap;
+ int regltr_id;
+ unsigned short volt_bits;
+ enum cpcap_reg regnr;
+ unsigned int shift;
+
+ cpcap = rdev_get_drvdata(rdev);
+
+ regltr_id = rdev_get_id(rdev);
+ if (regltr_id >= CPCAP_NUM_REGULATORS)
+ return -EINVAL;
+
+ regnr = cpcap_regltr_data[regltr_id].reg;
+
+ if (cpcap_regacc_read(cpcap, regnr, &volt_bits) < 0)
+ return -1;
+
+ if (!(volt_bits & cpcap_regltr_data[regltr_id].mode_mask))
+ return 0;
+
+ volt_bits &= cpcap_regltr_data[regltr_id].volt_mask;
+ shift = cpcap_regltr_data[regltr_id].volt_shft;
+
+ return cpcap_regltr_data[regltr_id].val_tbl[volt_bits >> shift];
+}
+
+static int cpcap_regulator_enable(struct regulator_dev *rdev)
+{
+ struct cpcap_device *cpcap = rdev_get_drvdata(rdev);
+ int regltr_id;
+ int retval;
+ enum cpcap_reg regnr;
+
+ regltr_id = rdev_get_id(rdev);
+ if (regltr_id >= CPCAP_NUM_REGULATORS)
+ return -EINVAL;
+
+ regnr = cpcap_regltr_data[regltr_id].reg;
+
+ retval = cpcap_regacc_write(cpcap, regnr,
+ cpcap_regltr_data[regltr_id].mode_val,
+ cpcap_regltr_data[regltr_id].mode_mask);
+
+ if ((cpcap_regltr_data[regltr_id].turn_on_time) && (retval == 0))
+ udelay(cpcap_regltr_data[regltr_id].turn_on_time);
+
+ return retval;
+}
+
+static int cpcap_regulator_disable(struct regulator_dev *rdev)
+{
+ struct cpcap_device *cpcap = rdev_get_drvdata(rdev);
+ int regltr_id;
+ enum cpcap_reg regnr;
+
+ regltr_id = rdev_get_id(rdev);
+ if (regltr_id >= CPCAP_NUM_REGULATORS)
+ return -EINVAL;
+
+ regnr = cpcap_regltr_data[regltr_id].reg;
+
+ return cpcap_regacc_write(cpcap, regnr,
+ cpcap_regltr_data[regltr_id].off_mode_val,
+ cpcap_regltr_data[regltr_id].mode_mask);
+}
+
+static int cpcap_regulator_is_enabled(struct regulator_dev *rdev)
+{
+ struct cpcap_device *cpcap = rdev_get_drvdata(rdev);
+ int regltr_id;
+ enum cpcap_reg regnr;
+ unsigned short value;
+
+ regltr_id = rdev_get_id(rdev);
+ if (regltr_id >= CPCAP_NUM_REGULATORS)
+ return -EINVAL;
+
+ regnr = cpcap_regltr_data[regltr_id].reg;
+
+ if (cpcap_regacc_read(cpcap, regnr, &value))
+ return -1;
+
+ return ((value & cpcap_regltr_data[regltr_id].mode_mask)
+ == cpcap_regltr_data[regltr_id].mode_val) ? 1 : 0;
+}
+
+static int cpcap_regulator_set_mode(struct regulator_dev *rdev,
+ unsigned int mode)
+{
+ struct cpcap_device *cpcap = rdev_get_drvdata(rdev);
+ int regltr_id;
+ enum cpcap_reg regnr;
+ int ret = 0;
+
+ regltr_id = rdev_get_id(rdev);
+ if (regltr_id != CPCAP_VAUDIO)
+ return -EINVAL;
+
+ regnr = cpcap_regltr_data[regltr_id].reg;
+
+ if (mode == REGULATOR_MODE_NORMAL) {
+ if (cpcap_regltr_data[regltr_id].mode_cntr == 0) {
+ ret = cpcap_regacc_write(cpcap, regnr,
+ 0,
+ CPCAP_BIT_AUDIO_LOW_PWR);
+ }
+ if (ret == 0)
+ cpcap_regltr_data[regltr_id].mode_cntr++;
+ } else if (mode == REGULATOR_MODE_STANDBY) {
+ if (cpcap_regltr_data[regltr_id].mode_cntr == 1) {
+ ret = cpcap_regacc_write(cpcap, regnr,
+ CPCAP_BIT_AUDIO_LOW_PWR,
+ CPCAP_BIT_AUDIO_LOW_PWR);
+ } else if (WARN((cpcap_regltr_data[regltr_id].mode_cntr == 0),
+ "Unbalanced modes for supply vaudio\n"))
+ ret = -EIO;
+
+ if (ret == 0)
+ cpcap_regltr_data[regltr_id].mode_cntr--;
+ }
+
+ return ret;
+}
+
+static struct regulator_ops cpcap_regulator_ops = {
+ .set_voltage = cpcap_regulator_set_voltage,
+ .get_voltage = cpcap_regulator_get_voltage,
+ .enable = cpcap_regulator_enable,
+ .disable = cpcap_regulator_disable,
+ .is_enabled = cpcap_regulator_is_enabled,
+ .set_mode = cpcap_regulator_set_mode,
+};
+
+static struct regulator_desc regulators[] = {
+ [CPCAP_SW4] = CPCAP_REGULATOR("sw4", CPCAP_SW4),
+ [CPCAP_SW5] = CPCAP_REGULATOR("sw5", CPCAP_SW5),
+ [CPCAP_VCAM] = CPCAP_REGULATOR("vcam", CPCAP_VCAM),
+ [CPCAP_VCSI] = CPCAP_REGULATOR("vcsi", CPCAP_VCSI),
+ [CPCAP_VDAC] = CPCAP_REGULATOR("vdac", CPCAP_VDAC),
+ [CPCAP_VDIG] = CPCAP_REGULATOR("vdig", CPCAP_VDIG),
+ [CPCAP_VFUSE] = CPCAP_REGULATOR("vfuse", CPCAP_VFUSE),
+ [CPCAP_VHVIO] = CPCAP_REGULATOR("vhvio", CPCAP_VHVIO),
+ [CPCAP_VSDIO] = CPCAP_REGULATOR("vsdio", CPCAP_VSDIO),
+ [CPCAP_VPLL] = CPCAP_REGULATOR("vpll", CPCAP_VPLL),
+ [CPCAP_VRF1] = CPCAP_REGULATOR("vrf1", CPCAP_VRF1),
+ [CPCAP_VRF2] = CPCAP_REGULATOR("vrf2", CPCAP_VRF2),
+ [CPCAP_VRFREF] = CPCAP_REGULATOR("vrfref", CPCAP_VRFREF),
+ [CPCAP_VWLAN1] = CPCAP_REGULATOR("vwlan1", CPCAP_VWLAN1),
+ [CPCAP_VWLAN2] = CPCAP_REGULATOR("vwlan2", CPCAP_VWLAN2),
+ [CPCAP_VSIM] = CPCAP_REGULATOR("vsim", CPCAP_VSIM),
+ [CPCAP_VSIMCARD] = CPCAP_REGULATOR("vsimcard", CPCAP_VSIMCARD),
+ [CPCAP_VVIB] = CPCAP_REGULATOR("vvib", CPCAP_VVIB),
+ [CPCAP_VUSB] = CPCAP_REGULATOR("vusb", CPCAP_VUSB),
+ [CPCAP_VAUDIO] = CPCAP_REGULATOR("vaudio", CPCAP_VAUDIO),
+};
+
+static int cpcap_regulator_probe(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev;
+ struct cpcap_device *cpcap;
+ struct cpcap_platform_data *data;
+ struct regulator_config config;
+ int i;
+
+ /* Already set by core driver */
+ cpcap = platform_get_drvdata(pdev);
+ data = cpcap->spi->controller_data;
+ config.dev = &pdev->dev;
+ config.init_data = pdev->dev.platform_data;
+ config.driver_data = platform_get_drvdata(pdev);
+
+ for (i = 0; i < CPCAP_NUM_REGULATORS; i++) {
+ cpcap_regltr_data[i].mode_val = data->regulator_mode_values[i];
+ cpcap_regltr_data[i].off_mode_val =
+ data->regulator_off_mode_values[i];
+ }
+
+ rdev = regulator_register(&regulators[pdev->id], &config);
+ if (IS_ERR(rdev))
+ return PTR_ERR(rdev);
+ /* this is ok since the cpcap is still reachable from the rdev */
+ platform_set_drvdata(pdev, rdev);
+
+ if (pdev->id == CPCAP_SW5) {
+ data->regulator_init =
+ cpcap->regulator_pdev[CPCAP_VUSB]->dev.platform_data;
+ data->regulator_init->supply_regulator =
+ regulators[CPCAP_SW5].name;
+ platform_device_add(cpcap->regulator_pdev[CPCAP_VUSB]);
+ }
+
+ return 0;
+}
+
+static int cpcap_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+
+ regulator_unregister(rdev);
+
+ return 0;
+}
+
+static struct platform_driver cpcap_regulator_driver = {
+ .driver = {
+ .name = "cpcap-regltr",
+ },
+ .probe = cpcap_regulator_probe,
+ .remove = cpcap_regulator_remove,
+};
+
+static int __init cpcap_regulator_init(void)
+{
+ return platform_driver_register(&cpcap_regulator_driver);
+}
+subsys_initcall(cpcap_regulator_init);
+
+static void __exit cpcap_regulator_exit(void)
+{
+ platform_driver_unregister(&cpcap_regulator_driver);
+}
+module_exit(cpcap_regulator_exit);
+
+MODULE_ALIAS("platform:cpcap-regulator");
+MODULE_DESCRIPTION("CPCAP regulator driver");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/f_usbnet.c b/drivers/usb/gadget/f_usbnet.c
new file mode 100644
index 00000000000..5431406aa20
--- /dev/null
+++ b/drivers/usb/gadget/f_usbnet.c
@@ -0,0 +1,839 @@
+/*
+ * Gadget Driver for Motorola USBNet
+ *
+ * Copyright (C) 2009 Motorola 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/device.h>
+#include <linux/fcntl.h>
+#include <linux/spinlock.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if.h>
+#include <linux/inetdevice.h>
+#include <linux/uaccess.h>
+#include <linux/workqueue.h>
+
+#include <linux/usb/ch9.h>
+#include "gadget_chips.h"
+
+
+/*
+ * Macro Defines
+ */
+
+#define EP0_BUFSIZE 256
+
+
+/* Vendor Request to config IP */
+#define USBNET_SET_IP_ADDRESS 0x05
+#define USBNET_SET_SUBNET_MASK 0x06
+#define USBNET_SET_HOST_IP 0x07
+
+/* Linux Network Interface */
+#define USB_MTU 1536
+#define MAX_BULK_TX_REQ_NUM 8
+#define MAX_BULK_RX_REQ_NUM 8
+#define MAX_INTR_RX_REQ_NUM 8
+#define STRING_INTERFACE 0
+
+struct usbnet_context {
+ spinlock_t lock; /* For RX/TX list */
+ struct net_device *dev;
+
+ struct usb_gadget *gadget;
+
+ struct usb_ep *bulk_in;
+ struct usb_ep *bulk_out;
+ struct usb_ep *intr_out;
+ u16 config; /* current USB config w_value */
+
+ struct list_head rx_reqs;
+ struct list_head tx_reqs;
+
+ struct net_device_stats stats;
+ struct work_struct usbnet_config_wq;
+ u32 ip_addr;
+ u32 subnet_mask;
+ u32 router_ip;
+ u32 iff_flag;
+};
+
+
+struct usbnet_device {
+ struct usb_function function;
+ struct usb_composite_dev *cdev;
+ struct usbnet_context *net_ctxt;
+};
+
+/* static strings, in UTF-8 */
+static struct usb_string usbnet_string_defs[] = {
+ [STRING_INTERFACE].s = "Motorola Networking Interface",
+ { /* ZEROES END LIST */ },
+};
+
+static struct usb_gadget_strings usbnet_string_table = {
+ .language = 0x0409, /* en-us */
+ .strings = usbnet_string_defs,
+};
+
+static struct usb_gadget_strings *usbnet_strings[] = {
+ &usbnet_string_table,
+ NULL,
+};
+
+
+
+/* There is only one interface. */
+
+static struct usb_interface_descriptor usbnet_intf_desc = {
+ .bLength = sizeof usbnet_intf_desc,
+ .bDescriptorType = USB_DT_INTERFACE,
+
+ .bNumEndpoints = 3,
+ .bInterfaceClass = 0x02,
+ .bInterfaceSubClass = 0x0a,
+ .bInterfaceProtocol = 0x01,
+};
+
+
+static struct usb_endpoint_descriptor usbnet_fs_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor usbnet_fs_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_endpoint_descriptor fs_intr_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(64),
+ .bInterval = 1,
+};
+
+static struct usb_descriptor_header *fs_function[] = {
+ (struct usb_descriptor_header *) &usbnet_intf_desc,
+ (struct usb_descriptor_header *) &usbnet_fs_bulk_in_desc,
+ (struct usb_descriptor_header *) &usbnet_fs_bulk_out_desc,
+ (struct usb_descriptor_header *) &fs_intr_out_desc,
+ NULL,
+};
+
+static struct usb_endpoint_descriptor usbnet_hs_bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+ .bInterval = 0,
+};
+
+static struct usb_endpoint_descriptor usbnet_hs_bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+ .bInterval = 0,
+};
+
+static struct usb_endpoint_descriptor hs_intr_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_INT,
+ .wMaxPacketSize = __constant_cpu_to_le16(64),
+ .bInterval = 1,
+};
+
+static struct usb_descriptor_header *hs_function[] = {
+ (struct usb_descriptor_header *) &usbnet_intf_desc,
+ (struct usb_descriptor_header *) &usbnet_hs_bulk_in_desc,
+ (struct usb_descriptor_header *) &usbnet_hs_bulk_out_desc,
+ (struct usb_descriptor_header *) &hs_intr_out_desc,
+ NULL,
+};
+/*
+static struct switch_dev usbnet_enable_device = {
+ .name = "usbnet_enable",
+};*/
+
+#define DO_NOT_STOP_QUEUE 0
+#define STOP_QUEUE 1
+
+#define USBNETDBG(context, fmt, args...) \
+ if (context && context->gadget) \
+ dev_err(&(context->gadget->dev) , fmt , ## args)
+
+static const char *usb_description = "Motorola BLAN Interface";
+
+static ssize_t usbnet_desc_show(struct device *dev,
+ struct device_attribute *attr, char *buff)
+{
+ ssize_t status = 0;
+ status = sprintf(buff, "%s\n", usb_description);
+ return status;
+}
+
+static DEVICE_ATTR(description, S_IRUGO, usbnet_desc_show, NULL);
+
+static inline struct usbnet_device *usbnet_func_to_dev(struct usb_function *f)
+{
+ return container_of(f, struct usbnet_device, function);
+}
+
+
+static int ether_queue_out(struct usb_request *req ,
+ struct usbnet_context *context)
+{
+ unsigned long flags;
+ struct sk_buff *skb;
+ int ret;
+
+ skb = alloc_skb(USB_MTU + NET_IP_ALIGN, GFP_ATOMIC);
+ if (!skb) {
+ USBNETDBG(context, "%s: failed to alloc skb\n", __func__);
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ /*
+ * RX: Do not move data by IP_ALIGN
+ * if the DMA Controller cannot handle it
+ */
+ if (!gadget_dma32(context->gadget))
+ skb_reserve(skb, NET_IP_ALIGN);
+
+ req->buf = skb->data;
+ req->length = USB_MTU;
+ req->context = skb;
+
+ ret = usb_ep_queue(context->bulk_out, req, GFP_KERNEL);
+ if (ret == 0)
+ return 0;
+ else
+ kfree_skb(skb);
+fail:
+ spin_lock_irqsave(&context->lock, flags);
+ list_add_tail(&req->list, &context->rx_reqs);
+ spin_unlock_irqrestore(&context->lock, flags);
+
+ return ret;
+}
+
+struct usb_request *usb_get_recv_request(struct usbnet_context *context)
+{
+ unsigned long flags;
+ struct usb_request *req;
+
+ spin_lock_irqsave(&context->lock, flags);
+ if (list_empty(&context->rx_reqs)) {
+ req = NULL;
+ } else {
+ req = list_first_entry(&context->rx_reqs,
+ struct usb_request, list);
+ list_del(&req->list);
+ }
+ spin_unlock_irqrestore(&context->lock, flags);
+
+ return req;
+}
+
+struct usb_request *usb_get_xmit_request(int stop_flag, struct net_device *dev)
+{
+ struct usbnet_context *context = netdev_priv(dev);
+ unsigned long flags;
+ struct usb_request *req;
+
+ spin_lock_irqsave(&context->lock, flags);
+ if (list_empty(&context->tx_reqs)) {
+ req = NULL;
+ } else {
+ req = list_first_entry(&context->tx_reqs,
+ struct usb_request, list);
+ list_del(&req->list);
+ if (stop_flag == STOP_QUEUE &&
+ list_empty(&context->tx_reqs))
+ netif_stop_queue(dev);
+ }
+ spin_unlock_irqrestore(&context->lock, flags);
+ return req;
+}
+
+static int usb_ether_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+ struct usbnet_context *context = netdev_priv(dev);
+ struct usb_request *req;
+ unsigned long flags;
+ unsigned len;
+ int rc;
+
+ req = usb_get_xmit_request(STOP_QUEUE, dev);
+
+ if (!req) {
+ USBNETDBG(context, "%s: could not obtain tx request\n",
+ __func__);
+ return 1;
+ }
+
+ /* Add 4 bytes CRC */
+ skb->len += 4;
+
+ /* ensure that we end with a short packet */
+ len = skb->len;
+ if (!(len & 63) || !(len & 511))
+ len++;
+
+ /* Align Data to 32 bit if the dma controller requires it */
+ if (gadget_dma32(context->gadget)) {
+ unsigned long align = (unsigned long)skb->data & 3;
+ if (WARN_ON(skb_headroom(skb) < align)) {
+ goto drop;
+ } else if (align) {
+ u8 *data = skb->data;
+ size_t len = skb_headlen(skb);
+ skb->data -= align;
+ memmove(skb->data, data, len);
+ skb_set_tail_pointer(skb, len);
+ }
+ }
+
+ req->context = skb;
+ req->buf = skb->data;
+ req->length = len;
+
+ rc = usb_ep_queue(context->bulk_in, req, GFP_KERNEL);
+ if (rc != 0) {
+drop:
+ spin_lock_irqsave(&context->lock, flags);
+ if (list_empty(&context->tx_reqs))
+ netif_start_queue(context->dev);
+ list_add_tail(&req->list, &context->tx_reqs);
+ spin_unlock_irqrestore(&context->lock, flags);
+
+ dev_kfree_skb_any(skb);
+ context->stats.tx_dropped++;
+
+ USBNETDBG(context,
+ "%s: could not queue tx request\n", __func__);
+ }
+
+ return 0;
+}
+
+static int usb_ether_open(struct net_device *dev)
+{
+ struct usbnet_context *context = netdev_priv(dev);
+ USBNETDBG(context, "%s\n", __func__);
+ return 0;
+}
+
+static int usb_ether_stop(struct net_device *dev)
+{
+ struct usbnet_context *context = netdev_priv(dev);
+ USBNETDBG(context, "%s\n", __func__);
+ return 0;
+}
+
+static struct net_device_stats *usb_ether_get_stats(struct net_device *dev)
+{
+ struct usbnet_context *context = netdev_priv(dev);
+ USBNETDBG(context, "%s\n", __func__);
+ return &context->stats;
+}
+
+static void usbnet_if_config(struct work_struct *work)
+{
+ struct ifreq ifr;
+ mm_segment_t saved_fs;
+ unsigned err;
+ struct sockaddr_in *sin;
+ struct usbnet_context *context = container_of(work,
+ struct usbnet_context, usbnet_config_wq);
+
+ pr_info("%s : Configuring with config = %d, ip_addr = 0x%08x,"
+ " subnet = 0x%08x, router_ip = 0x%08x, flags = 0x%08x\n",
+ __func__, context->config, context->ip_addr,
+ context->subnet_mask, context->router_ip, context->iff_flag);
+ memset(&ifr, 0, sizeof(ifr));
+ sin = (void *) &(ifr.ifr_ifru.ifru_addr);
+ strncpy(ifr.ifr_ifrn.ifrn_name, context->dev->name,
+ sizeof(ifr.ifr_ifrn.ifrn_name));
+ sin->sin_family = AF_INET;
+
+ sin->sin_addr.s_addr = context->ip_addr;
+ saved_fs = get_fs();
+ set_fs(get_ds());
+ err = devinet_ioctl(dev_net(context->dev), SIOCSIFADDR, &ifr);
+ if (err)
+ USBNETDBG(context, "%s: Error in SIOCSIFADDR\n", __func__);
+
+ sin->sin_addr.s_addr = context->subnet_mask;
+ err = devinet_ioctl(dev_net(context->dev), SIOCSIFNETMASK, &ifr);
+ if (err)
+ USBNETDBG(context, "%s: Error in SIOCSIFNETMASK\n", __func__);
+
+ sin->sin_addr.s_addr = context->ip_addr | ~(context->subnet_mask);
+ err = devinet_ioctl(dev_net(context->dev), SIOCSIFBRDADDR, &ifr);
+ if (err)
+ USBNETDBG(context, "%s: Error in SIOCSIFBRDADDR\n", __func__);
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_ifrn.ifrn_name, context->dev->name,
+ sizeof(ifr.ifr_ifrn.ifrn_name));
+ ifr.ifr_flags = context->dev->flags & ~IFF_UP;
+ ifr.ifr_flags |= context->iff_flag;
+ err = devinet_ioctl(dev_net(context->dev), SIOCSIFFLAGS, &ifr);
+ if (err)
+ USBNETDBG(context, "%s: Error in SIOCSIFFLAGS\n", __func__);
+
+ set_fs(saved_fs);
+ //switch_set_state(&usbnet_enable_device, context->config);
+}
+
+static const struct net_device_ops usbnet_eth_netdev_ops = {
+ .ndo_open = usb_ether_open,
+ .ndo_stop = usb_ether_stop,
+ .ndo_start_xmit = usb_ether_xmit,
+ .ndo_get_stats = usb_ether_get_stats,
+};
+
+static void usb_ether_setup(struct net_device *dev)
+{
+ struct usbnet_context *context = netdev_priv(dev);
+ INIT_LIST_HEAD(&context->rx_reqs);
+ INIT_LIST_HEAD(&context->tx_reqs);
+
+ spin_lock_init(&context->lock);
+ context->dev = dev;
+
+ dev->netdev_ops = &usbnet_eth_netdev_ops;
+ dev->watchdog_timeo = 20;
+
+ ether_setup(dev);
+
+ random_ether_addr(dev->dev_addr);
+}
+
+/*-------------------------------------------------------------------------*/
+static void usbnet_cleanup(struct usbnet_device *dev)
+{
+ struct usbnet_context *context = dev->net_ctxt;
+ if (context) {
+ device_remove_file(&(context->dev->dev), &dev_attr_description);
+ unregister_netdev(context->dev);
+ free_netdev(context->dev);
+ dev->net_ctxt = NULL;
+ }
+}
+
+static void usbnet_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usbnet_device *dev = usbnet_func_to_dev(f);
+ struct usb_composite_dev *cdev = c->cdev;
+ struct usbnet_context *context = dev->net_ctxt;
+ struct usb_request *req;
+
+ dev->cdev = cdev;
+
+ /* Free BULK OUT Requests */
+ while ((req = usb_get_recv_request(context)))
+ usb_ep_free_request(context->bulk_out, req);
+
+ /* Free BULK IN Requests */
+ while ((req = usb_get_xmit_request(DO_NOT_STOP_QUEUE,
+ context->dev))) {
+ usb_ep_free_request(context->bulk_in, req);
+ }
+
+ context->config = 0;
+}
+
+static void ether_out_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct sk_buff *skb = req->context;
+ struct usbnet_context *context = ep->driver_data;
+
+ if (req->status == 0) {
+ skb_put(skb, req->actual);
+ skb->protocol = eth_type_trans(skb, context->dev);
+ if (gadget_dma32(context->gadget) && NET_IP_ALIGN) {
+ u8 *data = skb->data;
+ size_t len = skb_headlen(skb);
+ skb_reserve(skb, NET_IP_ALIGN);
+ memmove(skb->data, data, len);
+ }
+ context->stats.rx_packets++;
+ context->stats.rx_bytes += req->actual;
+ netif_rx(skb);
+ } else {
+ dev_kfree_skb_any(skb);
+ context->stats.rx_errors++;
+ }
+
+ /* don't bother requeuing if we just went offline */
+ if ((req->status == -ENODEV) || (req->status == -ESHUTDOWN)) {
+ unsigned long flags;
+ spin_lock_irqsave(&context->lock, flags);
+ list_add_tail(&req->list, &context->rx_reqs);
+ spin_unlock_irqrestore(&context->lock, flags);
+ } else {
+ if (ether_queue_out(req, context))
+ USBNETDBG(context, "ether_out: cannot requeue\n");
+ }
+}
+
+static void ether_in_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ unsigned long flags;
+ struct sk_buff *skb = req->context;
+ struct usbnet_context *context = ep->driver_data;
+
+ if (req->status == 0) {
+ context->stats.tx_packets++;
+ context->stats.tx_bytes += req->actual;
+ } else {
+ context->stats.tx_errors++;
+ }
+
+ dev_kfree_skb_any(skb);
+
+ spin_lock_irqsave(&context->lock, flags);
+ if (list_empty(&context->tx_reqs))
+ netif_start_queue(context->dev);
+
+ list_add_tail(&req->list, &context->tx_reqs);
+ spin_unlock_irqrestore(&context->lock, flags);
+}
+
+static int usbnet_bind(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct usbnet_device *dev = usbnet_func_to_dev(f);
+ struct usbnet_context *context = dev->net_ctxt;
+ int n, rc, id;
+ struct usb_ep *ep;
+ struct usb_request *req;
+ unsigned long flags;
+
+ dev->cdev = cdev;
+
+ id = usb_interface_id(c, f);
+ if (id < 0)
+ return id;
+ usbnet_intf_desc.bInterfaceNumber = id;
+ context->gadget = cdev->gadget;
+
+ /* Find all the endpoints we will use */
+ ep = usb_ep_autoconfig(cdev->gadget, &usbnet_fs_bulk_in_desc);
+ if (!ep) {
+ USBNETDBG(context, "%s auto-configure hs_bulk_in_desc error\n",
+ __func__);
+ goto autoconf_fail;
+ }
+ ep->driver_data = context;
+ context->bulk_in = ep;
+
+ ep = usb_ep_autoconfig(cdev->gadget, &usbnet_fs_bulk_out_desc);
+ if (!ep) {
+ USBNETDBG(context, "%s auto-configure hs_bulk_out_desc error\n",
+ __func__);
+ goto autoconf_fail;
+ }
+ ep->driver_data = context;
+ context->bulk_out = ep;
+
+
+ ep = usb_ep_autoconfig(cdev->gadget, &fs_intr_out_desc);
+ if (!ep) {
+ USBNETDBG(context, "%s auto-configure hs_intr_out_desc error\n",
+ __func__);
+ goto autoconf_fail;
+ }
+ ep->driver_data = context;
+ context->intr_out = ep;
+
+ if (gadget_is_dualspeed(cdev->gadget)) {
+
+ /* Assume endpoint addresses are the same for both speeds */
+ usbnet_hs_bulk_in_desc.bEndpointAddress =
+ usbnet_fs_bulk_in_desc.bEndpointAddress;
+ usbnet_hs_bulk_out_desc.bEndpointAddress =
+ usbnet_fs_bulk_out_desc.bEndpointAddress;
+ hs_intr_out_desc.bEndpointAddress =
+ fs_intr_out_desc.bEndpointAddress;
+ }
+
+
+ rc = -ENOMEM;
+
+ for (n = 0; n < MAX_BULK_RX_REQ_NUM; n++) {
+ req = usb_ep_alloc_request(context->bulk_out,
+ GFP_KERNEL);
+ if (!req) {
+ USBNETDBG(context, "%s: alloc request bulk_out fail\n",
+ __func__);
+ break;
+ }
+ req->complete = ether_out_complete;
+ spin_lock_irqsave(&context->lock, flags);
+ list_add_tail(&req->list, &context->rx_reqs);
+ spin_unlock_irqrestore(&context->lock, flags);
+ }
+ for (n = 0; n < MAX_BULK_TX_REQ_NUM; n++) {
+ req = usb_ep_alloc_request(context->bulk_in,
+ GFP_KERNEL);
+ if (!req) {
+ USBNETDBG(context, "%s: alloc request bulk_in fail\n",
+ __func__);
+ break;
+ }
+ req->complete = ether_in_complete;
+ spin_lock_irqsave(&context->lock, flags);
+ list_add_tail(&req->list, &context->tx_reqs);
+ spin_unlock_irqrestore(&context->lock, flags);
+ }
+
+ return 0;
+
+autoconf_fail:
+ rc = -ENOTSUPP;
+ usbnet_unbind(c, f);
+ return rc;
+}
+
+
+
+
+static void do_set_config(struct usb_function *f, u16 new_config)
+{
+ struct usbnet_device *dev = usbnet_func_to_dev(f);
+ struct usbnet_context *context = dev->net_ctxt;
+ int result = 0;
+ struct usb_request *req;
+ int high_speed_flag = 0;
+
+ if (context->config == new_config) /* Config did not change */
+ return;
+
+ context->config = new_config;
+
+ if (new_config == 1) { /* Enable End points */
+ if (gadget_is_dualspeed(context->gadget)
+ && context->gadget->speed == USB_SPEED_HIGH)
+ high_speed_flag = 1;
+
+ if (high_speed_flag){
+ context->bulk_in->desc = &usbnet_hs_bulk_in_desc;
+ result = usb_ep_enable(context->bulk_in);
+ }
+ else{
+ context->bulk_in->desc = &usbnet_fs_bulk_in_desc;
+ result = usb_ep_enable(context->bulk_in);
+ }
+
+ if (result != 0) {
+ USBNETDBG(context,
+ "%s: failed to enable BULK_IN EP ret=%d\n",
+ __func__, result);
+ }
+
+ context->bulk_in->driver_data = context;
+
+ if (high_speed_flag){
+ context->bulk_out->desc = &usbnet_hs_bulk_out_desc;
+ result = usb_ep_enable(context->bulk_out);
+ }
+ else{
+ context->bulk_out->desc = &usbnet_fs_bulk_out_desc;
+ result = usb_ep_enable(context->bulk_out);
+ }
+
+ if (result != 0) {
+ USBNETDBG(context,
+ "%s: failed to enable BULK_OUT EP ret = %d\n",
+ __func__, result);
+ }
+
+ context->bulk_out->driver_data = context;
+
+ if (high_speed_flag){
+ context->intr_out->desc = &hs_intr_out_desc;
+ result = usb_ep_enable(context->intr_out);
+ }
+ else{
+ context->intr_out->desc = &fs_intr_out_desc;
+ result = usb_ep_enable(context->intr_out);
+ }
+
+ if (result != 0) {
+ USBNETDBG(context,
+ "%s: failed to enable INTR_OUT EP ret = %d\n",
+ __func__, result);
+ }
+
+ context->intr_out->driver_data = context;
+
+ /* we're online -- get all rx requests queued */
+ while ((req = usb_get_recv_request(context))) {
+ if (ether_queue_out(req, context)) {
+ USBNETDBG(context,
+ "%s: ether_queue_out failed\n",
+ __func__);
+ break;
+ }
+ }
+
+ } else {/* Disable Endpoints */
+ if (context->bulk_in)
+ usb_ep_disable(context->bulk_in);
+ if (context->bulk_out)
+ usb_ep_disable(context->bulk_out);
+ if (context->intr_out)
+ usb_ep_disable(context->intr_out);
+ context->ip_addr = 0;
+ context->subnet_mask = 0;
+ context->router_ip = 0;
+ context->iff_flag = 0;
+ schedule_work(&context->usbnet_config_wq);
+ }
+}
+
+
+static int usbnet_set_alt(struct usb_function *f,
+ unsigned intf, unsigned alt)
+{
+ struct usbnet_device *dev = usbnet_func_to_dev(f);
+ struct usbnet_context *context = dev->net_ctxt;
+ USBNETDBG(context, "usbnet_set_alt intf: %d alt: %d\n", intf, alt);
+ do_set_config(f, 1);
+ return 0;
+}
+
+static int usbnet_ctrlrequest(struct usbnet_device *dev,
+ struct usb_composite_dev *cdev,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct usbnet_context *context = dev->net_ctxt;
+ int rc = -EOPNOTSUPP;
+ int wIndex = le16_to_cpu(ctrl->wIndex);
+ int wValue = le16_to_cpu(ctrl->wValue);
+ int wLength = le16_to_cpu(ctrl->wLength);
+ struct usb_request *req = cdev->req;
+
+ if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_VENDOR) {
+ switch (ctrl->bRequest) {
+ case USBNET_SET_IP_ADDRESS:
+ context->ip_addr = (wValue << 16) | wIndex;
+ rc = 0;
+ break;
+ case USBNET_SET_SUBNET_MASK:
+ context->subnet_mask = (wValue << 16) | wIndex;
+ rc = 0;
+ break;
+ case USBNET_SET_HOST_IP:
+ context->router_ip = (wValue << 16) | wIndex;
+ rc = 0;
+ break;
+ default:
+ break;
+ }
+
+ if (context->ip_addr && context->subnet_mask
+ && context->router_ip) {
+ context->iff_flag = IFF_UP;
+ /* schedule a work queue to do this because we
+ need to be able to sleep */
+ schedule_work(&context->usbnet_config_wq);
+ }
+ }
+
+ /* respond with data transfer or status phase? */
+ if (rc >= 0) {
+ req->zero = rc < wLength;
+ req->length = rc;
+ rc = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ if (rc < 0)
+ USBNETDBG(context, "usbnet setup response error\n");
+ }
+
+ return rc;
+}
+
+static void usbnet_disable(struct usb_function *f)
+{
+ struct usbnet_device *dev = usbnet_func_to_dev(f);
+ struct usbnet_context *context = dev->net_ctxt;
+ USBNETDBG(context, "%s\n", __func__);
+ do_set_config(f, 0);
+}
+
+static void usbnet_suspend(struct usb_function *f)
+{
+ struct usbnet_device *dev = usbnet_func_to_dev(f);
+ struct usbnet_context *context = dev->net_ctxt;
+ USBNETDBG(context, "%s\n", __func__);
+}
+
+static void usbnet_resume(struct usb_function *f)
+{
+ struct usbnet_device *dev = usbnet_func_to_dev(f);
+ struct usbnet_context *context = dev->net_ctxt;
+ USBNETDBG(context, "%s\n", __func__);
+}
+
+int usbnet_bind_config(struct usbnet_device *dev, struct usb_configuration *c)
+{
+ int ret, status;
+
+ pr_debug("usbnet_bind_config\n");
+
+ if (usbnet_string_defs[STRING_INTERFACE].id == 0) {
+ status = usb_string_id(c->cdev);
+ if (status >= 0) {
+ usbnet_string_defs[STRING_INTERFACE].id = status;
+ usbnet_intf_desc.iInterface = status;
+ }
+ }
+
+ dev->cdev = c->cdev;
+ dev->function.name = "usbnet";
+ dev->function.fs_descriptors = fs_function;
+ dev->function.hs_descriptors = hs_function;
+ dev->function.bind = usbnet_bind;
+ dev->function.unbind = usbnet_unbind;
+ dev->function.set_alt = usbnet_set_alt;
+ dev->function.disable = usbnet_disable;
+ dev->function.suspend = usbnet_suspend;
+ dev->function.resume = usbnet_resume;
+ dev->function.strings = usbnet_strings;
+
+ ret = usb_add_function(c, &dev->function);
+
+ return ret;
+}
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index bcd04bc66b9..9ee7a549651 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -51,5 +51,14 @@ static inline bool gadget_supports_altsettings(struct usb_gadget *gadget)
/* Everything else is *presumably* fine ... */
return true;
}
-
+ /**
+ * gadget_dma32 - return true if we want buffer aligned on 32 bits (for dma)
+ * @gadget: the gadget in question
+ */
+static inline bool gadget_dma32(struct usb_gadget *gadget)
+{
+ if (gadget_is_musbhdrc(gadget))
+ return true;
+ return false;
+}
#endif /* __GADGET_CHIPS_H */
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 3312ad2bb67..6fcfa960fad 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -217,5 +217,9 @@ config USB_ULPI_VIEWPORT
help
Provides read/write operations to the ULPI phy register set for
controllers with a viewport register (e.g. Chipidea/ARC controllers).
-
+config CPCAP_USB
+ bool "CPCAP USB Transceiver Driver"
+ help
+ Enable this to support the USB OTG transceiver on the
+ Motorola CPCAP PMIC
endif # USB_PHY
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index a0a6cbad880..bef5f525ab8 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -32,3 +32,4 @@ obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o
obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
+obj-$(CONFIG_CPCAP_USB) += cpcap-usb.o
diff --git a/drivers/usb/phy/cpcap-usb.c b/drivers/usb/phy/cpcap-usb.c
new file mode 100644
index 00000000000..6e7bc16b695
--- /dev/null
+++ b/drivers/usb/phy/cpcap-usb.c
@@ -0,0 +1,211 @@
+/*
+ * cpcap_usb - CPCAP USB transceiver, talking to OMAP OTG controller
+ *
+ * Copyright (C) 2004-2007 Texas Instruments
+ * Copyright (C) 2008 Nokia Corporation
+ * Copyright (C) 2009 Google, Inc.
+ * Contact: Erik Gilling <konkers@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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Based on twl4030-usb.c
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/usb/otg.h>
+#include <linux/spi/cpcap.h>
+#include <linux/spi/cpcap-regbits.h>
+
+enum linkstat {
+ USB_LINK_UNKNOWN = 0,
+ USB_LINK_NONE,
+ USB_LINK_VBUS,
+ USB_LINK_ID,
+};
+
+struct cpcap_usb {
+ //struct otg_transceiver otg;
+ struct usb_phy phy;
+ struct device *dev;
+
+ struct cpcap_device *cpcap;
+
+ /* for vbus reporting with irqs disabled */
+ spinlock_t lock;
+
+ int irq;
+ u8 linkstat;
+ u8 asleep;
+ bool irq_enabled;
+};
+
+static int cpcap_set_suspend(struct usb_phy *x, int suspend)
+{
+ return 0;
+}
+
+
+static int cpcap_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *gadget)
+{
+ struct usb_phy *phy = otg->phy;
+
+ otg->gadget = gadget;
+ if (!gadget)
+ phy->state = OTG_STATE_UNDEFINED;
+
+ return 0;
+}
+
+static int cpcap_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+ struct usb_phy *phy = otg->phy;
+
+ otg->host = host;
+ if (!host)
+ phy->state = OTG_STATE_UNDEFINED;
+
+ return 0;
+}
+
+
+static int cpcap_usb_setup(struct cpcap_usb *cpcap)
+{
+ unsigned short mask;
+ unsigned short value;
+ int r;
+
+ r = cpcap_regacc_read(cpcap->cpcap, CPCAP_REG_INTS2, &value);
+
+ if (value & CPCAP_BIT_SE1_S)
+ mask = CPCAP_BIT_VBUSEN_SPI | CPCAP_BIT_VBUSPU_SPI |
+ CPCAP_BIT_SUSPEND_SPI | CPCAP_BIT_ULPI_SPI_SEL;
+ else
+ mask = CPCAP_BIT_VBUSEN_SPI | CPCAP_BIT_VBUSPU_SPI |
+ CPCAP_BIT_DMPD_SPI | CPCAP_BIT_DPPD_SPI |
+ CPCAP_BIT_SUSPEND_SPI | CPCAP_BIT_PU_SPI |
+ CPCAP_BIT_ULPI_SPI_SEL;
+
+ r = cpcap_regacc_write(cpcap->cpcap, CPCAP_REG_USBC3, 0x0, mask);
+ if (r < 0) {
+ dev_err(cpcap->dev,
+ "Can't disable SPI control of CPCAP transceiver\n");
+ return r;
+ }
+ return 0;
+}
+static int cpcap_usb_set_vbus(struct usb_otg *otg, bool enabled)
+{
+ return 0;
+}
+static int cpcap_usb_start_srp(struct usb_otg *otg)
+{
+ return 0;
+}
+static int cpcap_usb_probe(struct platform_device *pdev)
+{
+
+ struct cpcap_usb *cpcap;
+ struct usb_otg *otg;
+ int err;
+ cpcap = devm_kzalloc(&pdev->dev, sizeof(*cpcap), GFP_KERNEL);
+ if (!cpcap) {
+ dev_err(&pdev->dev, "unable to allocate memory for cpcap PHY\n");
+ return -ENOMEM;
+ }
+
+ otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
+ if (!otg) {
+ dev_err(&pdev->dev, "unable to allocate memory for USB OTG\n");
+ return -ENOMEM;
+ }
+
+ cpcap->dev = &pdev->dev;
+ cpcap->phy.dev = cpcap->dev;
+ cpcap->phy.label = "cpcap";
+ cpcap->phy.set_suspend = cpcap_set_suspend;
+ cpcap->phy.otg = otg;
+ cpcap->phy.type = USB_PHY_TYPE_USB2;
+ cpcap->asleep = 1;
+ cpcap->cpcap = pdev->dev.platform_data;
+
+
+ otg->set_host = cpcap_set_host;
+ otg->set_peripheral = cpcap_set_peripheral;
+ otg->set_vbus = cpcap_usb_set_vbus;
+ otg->start_srp = cpcap_usb_start_srp;
+ otg->phy = &cpcap->phy;
+
+ spin_lock_init(&cpcap->lock);
+ usb_add_phy_dev(&cpcap->phy);
+
+ platform_set_drvdata(pdev, cpcap);
+
+
+ err = cpcap_usb_setup(cpcap);
+ if (err < 0)
+ goto err0;
+ return 0;
+
+err0:
+ usb_remove_phy(&cpcap->phy);
+ devm_kfree(&pdev->dev,cpcap);
+ devm_kfree(&pdev->dev,otg);
+ return err;
+}
+
+static int cpcap_usb_remove(struct platform_device *pdev)
+{
+ struct cpcap_usb *cpcap = platform_get_drvdata(pdev);
+ devm_kfree(&pdev->dev,cpcap);
+
+ return 0;
+}
+
+static struct platform_driver cpcap_usb_driver = {
+ .probe = cpcap_usb_probe,
+ .remove = (cpcap_usb_remove),
+ .driver = {
+ .name = "cpcap_usb",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init cpcap_usb_init(void)
+{
+ return cpcap_driver_register(&cpcap_usb_driver);
+}
+subsys_initcall(cpcap_usb_init);
+
+static void __exit cpcap_usb_exit(void)
+{
+ platform_driver_unregister(&cpcap_usb_driver);
+}
+module_exit(cpcap_usb_exit);
+
+
+
+
+MODULE_ALIAS("platform:cpcap_usb");
+MODULE_DESCRIPTION("CPCAP USB transceiver driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig
index c3853c92279..af8f97ebbe0 100644
--- a/drivers/video/omap2/displays/Kconfig
+++ b/drivers/video/omap2/displays/Kconfig
@@ -72,4 +72,11 @@ config PANEL_N8X0
depends on BACKLIGHT_CLASS_DEVICE
help
This is the LCD panel used on Nokia N8x0
+
+config PANEL_MINNOW
+ tristate "Minnow DSI Command Mode Panel"
+ depends on OMAP2_DSS_DSI
+ help
+ Driver for Minnow DSI command mode panels.
+
endmenu
diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile
index 58a5176b07b..abc0d9dae56 100644
--- a/drivers/video/omap2/displays/Makefile
+++ b/drivers/video/omap2/displays/Makefile
@@ -9,3 +9,4 @@ obj-$(CONFIG_PANEL_PICODLP) += panel-picodlp.o
obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
obj-$(CONFIG_PANEL_ACX565AKM) += panel-acx565akm.o
obj-$(CONFIG_PANEL_N8X0) += panel-n8x0.o
+obj-$(CONFIG_PANEL_MINNOW) += panel-minnow.o
diff --git a/drivers/video/omap2/displays/panel-minnow.c b/drivers/video/omap2/displays/panel-minnow.c
new file mode 100644
index 00000000000..aa0a380a5da
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-minnow.c
@@ -0,0 +1,1761 @@
+/*
+ * Minnow DSI command mode panel
+ *
+ * Copyright (C) 2013 Motorola Mobility LLC.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*#define DEBUG*/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/workqueue.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-data.h>
+#include <video/mipi_display.h>
+
+/* DSI Virtual channel. Hardcoded for now. */
+#define TCH 0
+
+#define DCS_READ_NUM_ERRORS 0x05
+#define DCS_BRIGHTNESS 0x51
+#define DCS_CTRL_DISPLAY 0x53
+#define DCS_WRITE_CABC 0x55
+#define DCS_READ_CABC 0x56
+#define DCS_GET_ID1 0xda
+#define DCS_GET_ID2 0xdb
+#define DCS_GET_ID3 0xdc
+
+enum minnow_panel_id {
+ MINNOW_PANEL_CM_220X176,
+ MINNOW_PANEL_CM_220X220,
+ MINNOW_PANEL_MAX
+};
+
+/* Panel Initialize DSI DCS command buffer description:
+ * it uses compact DCS command buffer to store all DCS commands, the first
+ * byte of each command is the command length in byte
+ */
+static u8 panel_init_220x176[] = {
+/*n, data_0, data_1 ... data_n-1*/
+ 3, 0xF0, 0x5A, 0x5A,
+ 3, 0xF1, 0x5A, 0x5A,
+18, 0xF2, 0x16, 0xDC, 0x03, 0x28, 0x28, 0x10, 0x00, 0x60, 0xF8, 0x00, 0x07, 0x02, 0x00, 0x00, 0xDC, 0x28, 0x28,
+15, 0xF4, 0x0A, 0x00, 0x00, 0x00, 0x77, 0x7F, 0x07, 0x22, 0x2A, 0x43, 0x07, 0x2A, 0x43, 0x07,
+11, 0xF5, 0x00, 0x50, 0x28, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00,
+10, 0xF6, 0x07, 0x00, 0x07, 0x00, 0x0B, 0x04, 0x04, 0x04, 0x07,
+ 5, 0xF7, 0x00, 0x00, 0x00, 0x00,
+ 3, 0xF8, 0x44, 0x08,
+ 2, 0xF9, 0x04,
+17, 0xFA, 0x0F, 0x0F, 0x1E, 0x23, 0x26, 0x2D, 0x21, 0x2B, 0x33, 0x32, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00,
+17, 0xFB, 0x0F, 0x0F, 0x1E, 0x23, 0x26, 0x2D, 0x21, 0x2B, 0x33, 0x32, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 2, 0xF9, 0x02,
+17, 0xFA, 0x00, 0x00, 0x0A, 0x16, 0x1D, 0x27, 0x1C, 0x30, 0x38, 0x37, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
+17, 0xFB, 0x00, 0x00, 0x0A, 0x16, 0x1D, 0x27, 0x1C, 0x30, 0x38, 0x37, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 2, 0xF9, 0x01,
+17, 0xFA, 0x00, 0x00, 0x13, 0x14, 0x19, 0x24, 0x1A, 0x31, 0x39, 0x38, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
+17, 0xFB, 0x00, 0x00, 0x13, 0x14, 0x19, 0x24, 0x1A, 0x31, 0x39, 0x38, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 3, 0xF0, 0x00, 0x00,
+ 3, 0xF1, 0x00, 0x00,
+ 2, 0x36, 0xD0,
+ 2, 0x3A, 0x06
+};
+
+static u8 panel_init_220x220[] = {
+/*n, data_0, data_1 ... data_n-1*/
+ 3, 0xF0, 0x5A, 0x5A,
+ 3, 0xF1, 0x5A, 0x5A,
+18, 0xF2, 0x1C, 0xDC, 0x03, 0x28, 0x28, 0x10, 0x00, 0x60, 0xF8, 0x00, 0x07, 0x02, 0x00, 0x00, 0xDC, 0x28, 0x28,
+15, 0xF4, 0x0A, 0x00, 0x00, 0x00, 0x77, 0x7F, 0x07, 0x22, 0x2A, 0x43, 0x07, 0x2A, 0x43, 0x07,
+11, 0xF5, 0x00, 0x50, 0x28, 0x00, 0x00, 0x09, 0x00, 0x00, 0x01, 0x01,
+10, 0xF6, 0x07, 0x00, 0x07, 0x00, 0x0B, 0x04, 0x04, 0x04, 0x07,
+ 5, 0xF7, 0x00, 0x00, 0x00, 0x00,
+ 3, 0xF8, 0x44, 0x02,
+ 2, 0xF9, 0x04,
+17, 0xFA, 0x1E, 0x1E, 0x0D, 0x1D, 0x21, 0x2C, 0x23, 0x28, 0x2C, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00,
+17, 0xFB, 0x1E, 0x1E, 0x0D, 0x1D, 0x21, 0x2C, 0x23, 0x28, 0x2C, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 2, 0xF9, 0x02,
+17, 0xFA, 0x19, 0x18, 0x08, 0x0F, 0x18, 0x26, 0x1E, 0x2C, 0x30, 0x2C, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00,
+17, 0xFB, 0x19, 0x18, 0x08, 0x0F, 0x18, 0x26, 0x1E, 0x2C, 0x30, 0x2C, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 2, 0xF9, 0x01,
+17, 0xFA, 0x19, 0x19, 0x09, 0x0D, 0x12, 0x21, 0x1B, 0x2E, 0x31, 0x2E, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00,
+17, 0xFB, 0x19, 0x19, 0x09, 0x0D, 0x12, 0x21, 0x1B, 0x2E, 0x31, 0x2E, 0x2E, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 3, 0xF0, 0x00, 0x00,
+ 3, 0xF1, 0x00, 0x00,
+ 2, 0x36, 0xD0,
+ 2, 0x3A, 0x06
+};
+
+struct minnow_panel_attr {
+ int xres;
+ int yres;
+ int rate;
+ int xoffset;
+ int yoffset;
+ int init_cmd_count;
+ u8 *init_cmd;
+};
+
+#define INIT_CMD(buf) .init_cmd_count = sizeof(buf), .init_cmd = (buf)
+static struct minnow_panel_attr panel_attr_table[MINNOW_PANEL_MAX] = {
+ [MINNOW_PANEL_CM_220X176] = {
+ .xres = 220,
+ .yres = 176,
+ .rate = 60,
+ .xoffset = 0x32,
+ .yoffset = 0,
+ INIT_CMD(panel_init_220x176)
+ },
+ [MINNOW_PANEL_CM_220X220] = {
+ .xres = 220,
+ .yres = 220,
+ .rate = 60,
+ .xoffset = 0x32,
+ .yoffset = 0x4,
+ INIT_CMD(panel_init_220x220)
+ },
+};
+
+static irqreturn_t minnow_panel_te_isr(int irq, void *data);
+static void minnow_panel_te_timeout_work_callback(struct work_struct *work);
+static int _minnow_panel_enable_te(struct omap_dss_device *dssdev, bool enable);
+
+static int minnow_panel_reset(struct omap_dss_device *dssdev);
+
+struct minnow_panel_data {
+ struct mutex lock; /* mutex */
+
+ struct backlight_device *bldev;
+
+ 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 omap_dss_device *dssdev;
+
+ /* panel HW configuration from DT or platform data */
+ int reset_gpio;
+ int ext_te_gpio;
+
+ bool use_dsi_backlight;
+
+ struct omap_dsi_pin_config pin_config;
+ struct omap_dss_dsi_config dsi_config;
+
+ u8 *init_cmd_data;
+ int init_cmd_count;
+ int x_offset;
+ int y_offset;
+
+ /* runtime variables */
+ bool enabled;
+
+ bool te_enabled;
+
+ atomic_t do_update;
+ int channel;
+
+ struct delayed_work te_timeout_work;
+
+ unsigned cabc_mode;
+
+ bool intro_printed;
+
+ struct workqueue_struct *workqueue;
+
+ struct delayed_work esd_work;
+ unsigned esd_interval;
+
+ bool ulps_enabled;
+ unsigned ulps_timeout;
+ struct delayed_work ulps_work;
+};
+
+static void minnow_panel_esd_work(struct work_struct *work);
+static void minnow_panel_ulps_work(struct work_struct *work);
+
+static void hw_guard_start(struct minnow_panel_data *mpd, int guard_msec)
+{
+ mpd->hw_guard_wait = msecs_to_jiffies(guard_msec);
+ mpd->hw_guard_end = jiffies + mpd->hw_guard_wait;
+}
+
+static void hw_guard_wait(struct minnow_panel_data *mpd)
+{
+ unsigned long wait = mpd->hw_guard_end - jiffies;
+
+ if ((long)wait > 0 && wait <= mpd->hw_guard_wait) {
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(wait);
+ }
+}
+
+static int minnow_panel_dcs_read_1(struct minnow_panel_data *mpd, u8 dcs_cmd, u8 *data)
+{
+ int r;
+ u8 buf[1];
+
+ r = dsi_vc_dcs_read(mpd->dssdev, mpd->channel, dcs_cmd, buf, 1);
+
+ if (r < 0)
+ return r;
+
+ *data = buf[0];
+
+ return 0;
+}
+
+static int minnow_panel_dcs_write_0(struct minnow_panel_data *mpd, u8 dcs_cmd)
+{
+ return dsi_vc_dcs_write(mpd->dssdev, mpd->channel, &dcs_cmd, 1);
+}
+
+static int minnow_panel_dcs_write_1(struct minnow_panel_data *mpd, u8 dcs_cmd, u8 param)
+{
+ u8 buf[2];
+ buf[0] = dcs_cmd;
+ buf[1] = param;
+ return dsi_vc_dcs_write(mpd->dssdev, mpd->channel, buf, 2);
+}
+
+static int minnow_panel_sleep_in(struct minnow_panel_data *mpd)
+
+{
+ u8 cmd;
+ int r;
+
+ hw_guard_wait(mpd);
+
+ cmd = MIPI_DCS_ENTER_SLEEP_MODE;
+ r = dsi_vc_dcs_write_nosync(mpd->dssdev, mpd->channel, &cmd, 1);
+ if (r)
+ return r;
+
+ hw_guard_start(mpd, 120);
+
+ msleep(10);
+
+ return 0;
+}
+
+static int minnow_panel_sleep_out(struct minnow_panel_data *mpd)
+{
+ int r;
+
+ hw_guard_wait(mpd);
+
+ r = minnow_panel_dcs_write_0(mpd, MIPI_DCS_EXIT_SLEEP_MODE);
+ if (r)
+ return r;
+
+ hw_guard_start(mpd, 120);
+
+ msleep(10);
+
+ return 0;
+}
+
+static int minnow_panel_get_id(struct minnow_panel_data *mpd, u8 *id1, u8 *id2, u8 *id3)
+{
+ int r;
+
+ r = minnow_panel_dcs_read_1(mpd, DCS_GET_ID1, id1);
+ if (r)
+ return r;
+ r = minnow_panel_dcs_read_1(mpd, DCS_GET_ID2, id2);
+ if (r)
+ return r;
+ r = minnow_panel_dcs_read_1(mpd, DCS_GET_ID3, id3);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+static int _minnow_panel_init(struct minnow_panel_data *mpd)
+{
+ int i, r;
+ for (i = 0; i < mpd->init_cmd_count; ) {
+ u8 *data = mpd->init_cmd_data + i;
+ r = *data++;
+ i += r + 1;
+ if (i > mpd->init_cmd_count) {
+ dev_err(&mpd->dssdev->dev, "Invalid init command data selected!\n");
+ return -EINVAL;
+ }
+ r = dsi_vc_dcs_write(mpd->dssdev, mpd->channel, data, r);
+ if (r)
+ break;
+ }
+ return r;
+}
+
+static int minnow_panel_set_update_window(struct minnow_panel_data *mpd,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ int r;
+ u16 x1 = x + mpd->x_offset;
+ u16 x2 = x + mpd->x_offset + w - 1;
+ u16 y1 = y + mpd->y_offset;
+ u16 y2 = y + mpd->y_offset + h - 1;
+
+ u8 buf[5];
+ buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS;
+ buf[1] = (x1 >> 8) & 0xff;
+ buf[2] = (x1 >> 0) & 0xff;
+ buf[3] = (x2 >> 8) & 0xff;
+ buf[4] = (x2 >> 0) & 0xff;
+
+ r = dsi_vc_dcs_write_nosync(mpd->dssdev, mpd->channel, buf, sizeof(buf));
+ if (r)
+ return r;
+
+ buf[0] = MIPI_DCS_SET_PAGE_ADDRESS;
+ buf[1] = (y1 >> 8) & 0xff;
+ buf[2] = (y1 >> 0) & 0xff;
+ buf[3] = (y2 >> 8) & 0xff;
+ buf[4] = (y2 >> 0) & 0xff;
+
+ r = dsi_vc_dcs_write_nosync(mpd->dssdev, mpd->channel, buf, sizeof(buf));
+ if (r)
+ return r;
+
+ dsi_vc_send_bta_sync(mpd->dssdev, mpd->channel);
+
+ return r;
+}
+
+static void minnow_panel_queue_esd_work(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+
+ if (mpd->esd_interval > 0)
+ queue_delayed_work(mpd->workqueue, &mpd->esd_work,
+ msecs_to_jiffies(mpd->esd_interval));
+}
+
+static void minnow_panel_cancel_esd_work(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+
+ cancel_delayed_work(&mpd->esd_work);
+}
+
+static void minnow_panel_queue_ulps_work(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+
+ if (mpd->ulps_timeout > 0)
+ queue_delayed_work(mpd->workqueue, &mpd->ulps_work,
+ msecs_to_jiffies(mpd->ulps_timeout));
+}
+
+static void minnow_panel_cancel_ulps_work(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+
+ cancel_delayed_work(&mpd->ulps_work);
+}
+
+static int minnow_panel_enter_ulps(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ if (mpd->ulps_enabled)
+ return 0;
+
+ minnow_panel_cancel_ulps_work(dssdev);
+
+ r = _minnow_panel_enable_te(dssdev, false);
+ if (r)
+ goto err;
+
+ if (gpio_is_valid(mpd->ext_te_gpio))
+ disable_irq(gpio_to_irq(mpd->ext_te_gpio));
+
+ omapdss_dsi_display_disable(dssdev, false, true);
+
+ mpd->ulps_enabled = true;
+
+ return 0;
+
+err:
+ dev_err(&dssdev->dev, "enter ULPS failed");
+ minnow_panel_reset(dssdev);
+
+ mpd->ulps_enabled = false;
+
+ minnow_panel_queue_ulps_work(dssdev);
+
+ return r;
+}
+
+static int minnow_panel_exit_ulps(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ if (!mpd->ulps_enabled)
+ return 0;
+
+ r = omapdss_dsi_display_enable(dssdev);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to enable DSI\n");
+ goto err1;
+ }
+
+ omapdss_dsi_vc_enable_hs(dssdev, mpd->channel, true);
+
+ r = _minnow_panel_enable_te(dssdev, true);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to re-enable TE");
+ goto err2;
+ }
+
+ if (gpio_is_valid(mpd->ext_te_gpio))
+ enable_irq(gpio_to_irq(mpd->ext_te_gpio));
+
+ minnow_panel_queue_ulps_work(dssdev);
+
+ mpd->ulps_enabled = false;
+
+ return 0;
+
+err2:
+ dev_err(&dssdev->dev, "failed to exit ULPS");
+
+ r = minnow_panel_reset(dssdev);
+ if (!r) {
+ if (gpio_is_valid(mpd->ext_te_gpio))
+ enable_irq(gpio_to_irq(mpd->ext_te_gpio));
+ mpd->ulps_enabled = false;
+ }
+err1:
+ minnow_panel_queue_ulps_work(dssdev);
+
+ return r;
+}
+
+static int minnow_panel_wake_up(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+
+ if (mpd->ulps_enabled)
+ return minnow_panel_exit_ulps(dssdev);
+
+ minnow_panel_cancel_ulps_work(dssdev);
+ minnow_panel_queue_ulps_work(dssdev);
+ return 0;
+}
+
+static int minnow_panel_bl_update_status(struct backlight_device *dev)
+{
+ struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev);
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ int r;
+ int level;
+
+ if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+ dev->props.power == FB_BLANK_UNBLANK)
+ level = dev->props.brightness;
+ else
+ level = 0;
+
+ dev_dbg(&dssdev->dev, "update brightness to %d\n", level);
+
+ mutex_lock(&mpd->lock);
+
+ if (mpd->enabled) {
+ dsi_bus_lock(dssdev);
+
+ r = minnow_panel_wake_up(dssdev);
+ if (!r)
+ r = minnow_panel_dcs_write_1(mpd, DCS_BRIGHTNESS, level);
+
+ dsi_bus_unlock(dssdev);
+ } else {
+ r = 0;
+ }
+
+ mutex_unlock(&mpd->lock);
+
+ return r;
+}
+
+static int minnow_panel_bl_get_intensity(struct backlight_device *dev)
+{
+ if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
+ dev->props.power == FB_BLANK_UNBLANK)
+ return dev->props.brightness;
+
+ return 0;
+}
+
+static const struct backlight_ops minnow_panel_bl_ops = {
+ .get_brightness = minnow_panel_bl_get_intensity,
+ .update_status = minnow_panel_bl_update_status,
+};
+
+static void minnow_panel_get_resolution(struct omap_dss_device *dssdev,
+ u16 *xres, u16 *yres)
+{
+ *xres = dssdev->panel.timings.x_res;
+ *yres = dssdev->panel.timings.y_res;
+}
+
+static ssize_t minnow_panel_num_errors_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ u8 errors = 0;
+ int r;
+
+ mutex_lock(&mpd->lock);
+
+ if (mpd->enabled) {
+ dsi_bus_lock(dssdev);
+
+ r = minnow_panel_wake_up(dssdev);
+ if (!r)
+ r = minnow_panel_dcs_read_1(mpd, DCS_READ_NUM_ERRORS, &errors);
+
+ dsi_bus_unlock(dssdev);
+ } else {
+ r = -ENODEV;
+ }
+
+ mutex_unlock(&mpd->lock);
+
+ if (r)
+ return r;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", errors);
+}
+
+static ssize_t minnow_panel_hw_revision_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ u8 id1, id2, id3;
+ int r;
+
+ mutex_lock(&mpd->lock);
+
+ if (mpd->enabled) {
+ dsi_bus_lock(dssdev);
+
+ r = minnow_panel_wake_up(dssdev);
+ if (!r)
+ r = minnow_panel_get_id(mpd, &id1, &id2, &id3);
+
+ dsi_bus_unlock(dssdev);
+ } else {
+ r = -ENODEV;
+ }
+
+ mutex_unlock(&mpd->lock);
+
+ if (r)
+ return r;
+
+ return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3);
+}
+
+static const char *cabc_modes[] = {
+ "off", /* used also always when CABC is not supported */
+ "ui",
+ "still-image",
+ "moving-image",
+};
+
+static ssize_t show_cabc_mode(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ const char *mode_str;
+ int mode;
+ int len;
+
+ mode = mpd->cabc_mode;
+
+ mode_str = "unknown";
+ if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes))
+ mode_str = cabc_modes[mode];
+ len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str);
+
+ return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1;
+}
+
+static ssize_t store_cabc_mode(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ int i;
+ int r;
+
+ for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) {
+ if (sysfs_streq(cabc_modes[i], buf))
+ break;
+ }
+
+ if (i == ARRAY_SIZE(cabc_modes))
+ return -EINVAL;
+
+ mutex_lock(&mpd->lock);
+
+ if (mpd->enabled) {
+ dsi_bus_lock(dssdev);
+
+ r = minnow_panel_wake_up(dssdev);
+ if (r)
+ goto err;
+
+ r = minnow_panel_dcs_write_1(mpd, DCS_WRITE_CABC, i);
+ if (r)
+ goto err;
+
+ dsi_bus_unlock(dssdev);
+ }
+
+ mpd->cabc_mode = i;
+
+ mutex_unlock(&mpd->lock);
+
+ return count;
+err:
+ dsi_bus_unlock(dssdev);
+ mutex_unlock(&mpd->lock);
+ return r;
+}
+
+static ssize_t show_cabc_available_modes(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int len;
+ int i;
+
+ for (i = 0, len = 0;
+ len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++)
+ len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s",
+ i ? " " : "", cabc_modes[i],
+ i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : "");
+
+ return len < PAGE_SIZE ? len : PAGE_SIZE - 1;
+}
+
+static ssize_t minnow_panel_store_esd_interval(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+
+ unsigned long t;
+ int r;
+
+ r = strict_strtoul(buf, 10, &t);
+ if (r)
+ return r;
+
+ mutex_lock(&mpd->lock);
+ minnow_panel_cancel_esd_work(dssdev);
+ mpd->esd_interval = t;
+ if (mpd->enabled)
+ minnow_panel_queue_esd_work(dssdev);
+ mutex_unlock(&mpd->lock);
+
+ return count;
+}
+
+static ssize_t minnow_panel_show_esd_interval(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ unsigned t;
+
+ mutex_lock(&mpd->lock);
+ t = mpd->esd_interval;
+ mutex_unlock(&mpd->lock);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", t);
+}
+
+static ssize_t minnow_panel_store_ulps(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ unsigned long t;
+ int r;
+
+ r = strict_strtoul(buf, 10, &t);
+ if (r)
+ return r;
+
+ mutex_lock(&mpd->lock);
+
+ if (mpd->enabled) {
+ dsi_bus_lock(dssdev);
+
+ if (t)
+ r = minnow_panel_enter_ulps(dssdev);
+ else
+ r = minnow_panel_wake_up(dssdev);
+
+ dsi_bus_unlock(dssdev);
+ }
+
+ mutex_unlock(&mpd->lock);
+
+ if (r)
+ return r;
+
+ return count;
+}
+
+static ssize_t minnow_panel_show_ulps(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ unsigned t;
+
+ mutex_lock(&mpd->lock);
+ t = mpd->ulps_enabled;
+ mutex_unlock(&mpd->lock);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", t);
+}
+
+static ssize_t minnow_panel_store_ulps_timeout(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ unsigned long t;
+ int r;
+
+ r = strict_strtoul(buf, 10, &t);
+ if (r)
+ return r;
+
+ mutex_lock(&mpd->lock);
+ mpd->ulps_timeout = t;
+
+ if (mpd->enabled) {
+ /* minnow_panel_wake_up will restart the timer */
+ dsi_bus_lock(dssdev);
+ r = minnow_panel_wake_up(dssdev);
+ dsi_bus_unlock(dssdev);
+ }
+
+ mutex_unlock(&mpd->lock);
+
+ if (r)
+ return r;
+
+ return count;
+}
+
+static ssize_t minnow_panel_show_ulps_timeout(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct omap_dss_device *dssdev = to_dss_device(dev);
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ unsigned t;
+
+ mutex_lock(&mpd->lock);
+ t = mpd->ulps_timeout;
+ mutex_unlock(&mpd->lock);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", t);
+}
+
+static DEVICE_ATTR(num_dsi_errors, S_IRUGO, minnow_panel_num_errors_show, NULL);
+static DEVICE_ATTR(hw_revision, S_IRUGO, minnow_panel_hw_revision_show, NULL);
+static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR,
+ show_cabc_mode, store_cabc_mode);
+static DEVICE_ATTR(cabc_available_modes, S_IRUGO,
+ show_cabc_available_modes, NULL);
+static DEVICE_ATTR(esd_interval, S_IRUGO | S_IWUSR,
+ minnow_panel_show_esd_interval, minnow_panel_store_esd_interval);
+static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR,
+ minnow_panel_show_ulps, minnow_panel_store_ulps);
+static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR,
+ minnow_panel_show_ulps_timeout, minnow_panel_store_ulps_timeout);
+
+static struct attribute *minnow_panel_attrs[] = {
+ &dev_attr_num_dsi_errors.attr,
+ &dev_attr_hw_revision.attr,
+ &dev_attr_cabc_mode.attr,
+ &dev_attr_cabc_available_modes.attr,
+ &dev_attr_esd_interval.attr,
+ &dev_attr_ulps.attr,
+ &dev_attr_ulps_timeout.attr,
+ NULL,
+};
+
+static struct attribute_group minnow_panel_attr_group = {
+ .attrs = minnow_panel_attrs,
+};
+
+static void minnow_panel_hw_reset(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+
+ if (!gpio_is_valid(mpd->reset_gpio))
+ return;
+
+ gpio_set_value(mpd->reset_gpio, 1);
+ udelay(10);
+ /* reset the panel */
+ gpio_set_value(mpd->reset_gpio, 0);
+ /* assert reset */
+ udelay(10);
+ gpio_set_value(mpd->reset_gpio, 1);
+
+ /* wait after releasing reset */
+ msleep(5);
+}
+
+#define DEBUG_DT
+#ifdef DEBUG_DT
+#define DTINFO(fmt, ...) \
+ printk(KERN_INFO "minnow-panel DT: " fmt, ## __VA_ARGS__)
+#define DTINFO_PIXFMT(msg, pix) \
+{ char *fmt[4] = {"RGB888", "RGB666", "RGB666_PACKED", "RGB565"};\
+ DTINFO(msg"%s\n", fmt[pix]);\
+}
+#define DTINFO_ARRAY(msg, a, n, fmt, blen) \
+{ int i; char str[blen], *p = str;\
+ for (i = 0; i < n; i++) {\
+ sprintf(p, fmt, a[i]);\
+ p += strlen(p); \
+ } \
+ DTINFO(msg"%s\n", str);\
+}
+#else /* DEBUG_DT */
+#define DTINFO(fmt, ...)
+#define DTINFO_PIXFMT(msg, pix)
+#define DTINFO_ARRAY(msg, a, n, fmt, blen)
+#endif
+
+static struct of_device_id minnow_panel_ids[] = {
+ { .compatible = "mot,minnow-panel-dsi-cm" },
+ { /*sentinel*/ }
+};
+
+static int minnow_panel_dt_init(struct minnow_panel_data *mpd)
+{
+ u32 range[2], value = 0;
+ struct minnow_panel_attr *panel_attr;
+ struct device_node *dt_node;
+
+ dt_node = of_find_matching_node(NULL, minnow_panel_ids);
+ if (dt_node == NULL) {
+ dev_err(&mpd->dssdev->dev, "No dt_node found!\n");
+ return -ENODEV;
+ }
+
+ if (of_property_read_u32(dt_node, "id_panel", &value) \
+ || (value >= MINNOW_PANEL_MAX)) {
+ dev_err(&mpd->dssdev->dev, \
+ "Invalid id_panel = %u!\n", value);
+ return -EINVAL;
+ }
+ DTINFO("id_panel = %d\n", value);
+ panel_attr = &panel_attr_table[value];
+ mpd->init_cmd_data = panel_attr->init_cmd;
+ mpd->init_cmd_count = panel_attr->init_cmd_count;
+ mpd->x_offset = panel_attr->xoffset;
+ mpd->y_offset = panel_attr->yoffset;
+ mpd->dssdev->panel.timings.x_res = panel_attr->xres;
+ mpd->dssdev->panel.timings.y_res = panel_attr->yres;
+ mpd->dssdev->panel.timings.pixel_clock = DIV_ROUND_UP(panel_attr->xres\
+ * panel_attr->yres * panel_attr->rate, 1000);
+ mpd->dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
+ mpd->dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
+ OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
+ mpd->dsi_config.mode = OMAP_DSS_DSI_CMD_MODE;
+ mpd->dsi_config.pixel_format = OMAP_DSS_DSI_FMT_RGB888;
+ mpd->dsi_config.hs_clk_min = 90000000;
+ mpd->dsi_config.hs_clk_max = 150000000;
+ mpd->dsi_config.lp_clk_min = 7000000;
+ mpd->dsi_config.lp_clk_max = 9000000;
+
+ mpd->reset_gpio = of_get_named_gpio(dt_node, "gpio_reset", 0);
+ DTINFO("gpio_reset = %d\n", mpd->reset_gpio);
+ mpd->ext_te_gpio = of_get_named_gpio(dt_node, "gpio_te", 0);
+ DTINFO("ext_te: gpio_te = %d\n", mpd->ext_te_gpio);
+
+ mpd->esd_interval = 0;
+ if (!of_property_read_u32(dt_node, "esd_interval", &value)) {
+ mpd->esd_interval = value;
+ DTINFO("esd_interval = %d\n", mpd->esd_interval);
+ }
+ mpd->ulps_timeout = 0;
+ mpd->use_dsi_backlight = false;
+
+ mpd->pin_config.num_pins = 4;
+ mpd->pin_config.pins[0] = 0;
+ mpd->pin_config.pins[1] = 1;
+ mpd->pin_config.pins[2] = 2;
+ mpd->pin_config.pins[3] = 3;
+ if (of_get_property(dt_node, "pins", &value)) {
+ u32 pins[OMAP_DSS_MAX_DSI_PINS];
+ u32 num_pins = value / sizeof(u32);
+ if (!num_pins || (num_pins > OMAP_DSS_MAX_DSI_PINS)) {
+ dev_err(&mpd->dssdev->dev, \
+ "Invalid DSI pins count = %u!\n", num_pins);
+ return -EINVAL;
+ }
+ value = 0;
+ if (!of_property_read_u32_array(dt_node, \
+ "pins", pins, num_pins)) {
+ for (; value < num_pins; value++) {
+ if (pins[value] >= OMAP_DSS_MAX_DSI_PINS)
+ break;
+ mpd->pin_config.pins[value]\
+ = pins[value];
+ }
+ }
+ if (value < num_pins) {
+ dev_err(&mpd->dssdev->dev, \
+ "Invalid DSI pins config!\n");
+ return -EINVAL;
+ }
+ mpd->pin_config.num_pins = num_pins;
+ DTINFO("num_pins = %d\n", \
+ mpd->pin_config.num_pins);
+ DTINFO_ARRAY("pins =", mpd->pin_config.pins,\
+ mpd->pin_config.num_pins, " %u", 64);
+ }
+
+ if (!of_property_read_u32(dt_node, "pixel_clock", &value)) {
+ if (value < mpd->dssdev->panel.timings.pixel_clock) {
+ dev_err(&mpd->dssdev->dev, \
+ "Invalid pixel_clock = %u!\n", value);
+ return -EINVAL;
+ }
+ mpd->dssdev->panel.timings.pixel_clock = value;
+ DTINFO("pixel_clock = %u\n", \
+ mpd->dssdev->panel.timings.pixel_clock);
+ }
+
+ if (!of_property_read_u32(dt_node, "pixel_format", &value)) {
+ switch (value) {
+ case OMAP_DSS_DSI_FMT_RGB888:
+ case OMAP_DSS_DSI_FMT_RGB666:
+ case OMAP_DSS_DSI_FMT_RGB666_PACKED:
+ case OMAP_DSS_DSI_FMT_RGB565:
+ break;
+ default:
+ dev_err(&mpd->dssdev->dev, \
+ "Invalid pixel_format = %u!\n", value);
+ return -EINVAL;
+ }
+ mpd->dssdev->panel.dsi_pix_fmt = \
+ mpd->dsi_config.pixel_format = value;
+ DTINFO_PIXFMT("pixel_format = ", \
+ mpd->dssdev->panel.dsi_pix_fmt);
+ }
+
+ if (!of_property_read_u32_array(dt_node, "hs_clk", range, 2)) {
+ mpd->dsi_config.hs_clk_min = range[0];
+ mpd->dsi_config.hs_clk_max = range[1];
+ DTINFO("hs_clk_min = %lu, hs_clk_max = %lu\n", \
+ mpd->dsi_config.hs_clk_min, \
+ mpd->dsi_config.hs_clk_max);
+ }
+
+ if (!of_property_read_u32_array(dt_node, "lp_clk", range, 2)) {
+ mpd->dsi_config.lp_clk_min = range[0];
+ mpd->dsi_config.lp_clk_max = range[1];
+ DTINFO("lp_clk_min = %lu, lp_clk_max = %lu\n", \
+ mpd->dsi_config.lp_clk_min, \
+ mpd->dsi_config.lp_clk_max);
+ }
+
+ return 0;
+}
+
+static int minnow_panel_probe(struct omap_dss_device *dssdev)
+{
+ struct backlight_properties props;
+ struct minnow_panel_data *mpd;
+ struct backlight_device *bldev = NULL;
+ int r;
+
+ dev_dbg(&dssdev->dev, "probe\n");
+
+ mpd = devm_kzalloc(&dssdev->dev, sizeof(*mpd), GFP_KERNEL);
+ if (!mpd)
+ return -ENOMEM;
+
+ dev_set_drvdata(&dssdev->dev, mpd);
+ mpd->dssdev = dssdev;
+
+ r = minnow_panel_dt_init(mpd);
+ if (r)
+ return r;
+
+ mutex_init(&mpd->lock);
+
+ atomic_set(&mpd->do_update, 0);
+
+ if (gpio_is_valid(mpd->reset_gpio)) {
+ r = devm_gpio_request_one(&dssdev->dev, mpd->reset_gpio,
+ GPIOF_OUT_INIT_HIGH, "minnow-panel reset");
+ if (r) {
+ dev_err(&dssdev->dev, "failed to request reset gpio\n");
+ return r;
+ }
+ }
+
+ if (gpio_is_valid(mpd->ext_te_gpio)) {
+ r = devm_gpio_request_one(&dssdev->dev, mpd->ext_te_gpio,
+ GPIOF_IN, "minnow-panel irq");
+ if (r) {
+ dev_err(&dssdev->dev, "failed to request ext_te gpio\n");
+ return r;
+ }
+
+ r = devm_request_irq(&dssdev->dev, gpio_to_irq(mpd->ext_te_gpio),
+ minnow_panel_te_isr,
+ IRQF_TRIGGER_RISING,
+ "minnow-panel vsync", dssdev);
+
+ if (r) {
+ dev_err(&dssdev->dev, "IRQ request failed\n");
+ return r;
+ }
+
+ INIT_DEFERRABLE_WORK(&mpd->te_timeout_work,
+ minnow_panel_te_timeout_work_callback);
+
+ dev_dbg(&dssdev->dev, "Using GPIO TE\n");
+ }
+
+ mpd->workqueue = create_singlethread_workqueue("minnow_panel_esd");
+ if (mpd->workqueue == NULL) {
+ dev_err(&dssdev->dev, "can't create ESD workqueue\n");
+ return -ENOMEM;
+ }
+ INIT_DEFERRABLE_WORK(&mpd->esd_work, minnow_panel_esd_work);
+ INIT_DELAYED_WORK(&mpd->ulps_work, minnow_panel_ulps_work);
+
+ if (mpd->use_dsi_backlight) {
+ memset(&props, 0, sizeof(struct backlight_properties));
+ props.max_brightness = 255;
+
+ props.type = BACKLIGHT_RAW;
+ bldev = backlight_device_register(dev_name(&dssdev->dev),
+ &dssdev->dev, dssdev, &minnow_panel_bl_ops, &props);
+ if (IS_ERR(bldev)) {
+ r = PTR_ERR(bldev);
+ goto err_bl;
+ }
+
+ mpd->bldev = bldev;
+
+ bldev->props.fb_blank = FB_BLANK_UNBLANK;
+ bldev->props.power = FB_BLANK_UNBLANK;
+ bldev->props.brightness = 255;
+
+ minnow_panel_bl_update_status(bldev);
+ }
+
+ r = omap_dsi_request_vc(dssdev, &mpd->channel);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to get virtual channel\n");
+ goto err_req_vc;
+ }
+
+ r = omap_dsi_set_vc_id(dssdev, mpd->channel, TCH);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to set VC_ID\n");
+ goto err_vc_id;
+ }
+
+ r = sysfs_create_group(&dssdev->dev.kobj, &minnow_panel_attr_group);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to create sysfs files\n");
+ goto err_vc_id;
+ }
+
+ return 0;
+
+err_vc_id:
+ omap_dsi_release_vc(dssdev, mpd->channel);
+err_req_vc:
+ if (bldev != NULL)
+ backlight_device_unregister(bldev);
+err_bl:
+ destroy_workqueue(mpd->workqueue);
+ return r;
+}
+
+static void __exit minnow_panel_remove(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ struct backlight_device *bldev;
+
+ dev_dbg(&dssdev->dev, "remove\n");
+
+ sysfs_remove_group(&dssdev->dev.kobj, &minnow_panel_attr_group);
+ omap_dsi_release_vc(dssdev, mpd->channel);
+
+ bldev = mpd->bldev;
+ if (bldev != NULL) {
+ bldev->props.power = FB_BLANK_POWERDOWN;
+ minnow_panel_bl_update_status(bldev);
+ backlight_device_unregister(bldev);
+ }
+
+ minnow_panel_cancel_ulps_work(dssdev);
+ minnow_panel_cancel_esd_work(dssdev);
+ destroy_workqueue(mpd->workqueue);
+
+ /* reset, to be sure that the panel is in a valid state */
+ minnow_panel_hw_reset(dssdev);
+}
+
+static int minnow_panel_power_on(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ u8 id1, id2, id3;
+ int r;
+
+ r = omapdss_dsi_configure_pins(dssdev, &mpd->pin_config);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to configure DSI pins\n");
+ goto err0;
+ };
+
+ mpd->dsi_config.timings = &dssdev->panel.timings;
+ r = omapdss_dsi_set_config(dssdev, &mpd->dsi_config);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to configure DSI\n");
+ goto err0;
+ }
+
+ r = omapdss_dsi_display_enable(dssdev);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to enable DSI\n");
+ goto err0;
+ }
+
+ if (mpd->intro_printed)
+ minnow_panel_hw_reset(dssdev);
+
+ omapdss_dsi_vc_enable_hs(dssdev, mpd->channel, false);
+
+ r = minnow_panel_sleep_out(mpd);
+ if (r)
+ goto err;
+
+ _minnow_panel_init(mpd);
+
+ r = minnow_panel_get_id(mpd, &id1, &id2, &id3);
+ if (r)
+ goto err;
+
+ r = minnow_panel_dcs_write_0(mpd, MIPI_DCS_SET_DISPLAY_ON);
+ if (r)
+ goto err;
+
+ r = _minnow_panel_enable_te(dssdev, mpd->te_enabled);
+ if (r)
+ goto err;
+
+ r = dsi_enable_video_output(dssdev, mpd->channel);
+ if (r)
+ goto err;
+
+ mpd->enabled = 1;
+
+ if (!mpd->intro_printed) {
+ dev_info(&dssdev->dev, "panel revision %02x.%02x.%02x\n",
+ id1, id2, id3);
+ mpd->intro_printed = true;
+ }
+
+ omapdss_dsi_vc_enable_hs(dssdev, mpd->channel, true);
+
+ return 0;
+err:
+ dev_err(&dssdev->dev, "error while enabling panel, issuing HW reset\n");
+
+ minnow_panel_hw_reset(dssdev);
+
+ omapdss_dsi_display_disable(dssdev, true, false);
+err0:
+ return r;
+}
+
+static void minnow_panel_power_off(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ dsi_disable_video_output(dssdev, mpd->channel);
+
+ r = minnow_panel_dcs_write_0(mpd, MIPI_DCS_SET_DISPLAY_OFF);
+ if (!r)
+ r = minnow_panel_sleep_in(mpd);
+
+ if (r) {
+ dev_err(&dssdev->dev,
+ "error disabling panel, issuing HW reset\n");
+ minnow_panel_hw_reset(dssdev);
+ }
+
+ omapdss_dsi_display_disable(dssdev, true, false);
+
+ mpd->enabled = 0;
+}
+
+static int minnow_panel_reset(struct omap_dss_device *dssdev)
+{
+ dev_err(&dssdev->dev, "performing LCD reset\n");
+
+ minnow_panel_power_off(dssdev);
+ minnow_panel_hw_reset(dssdev);
+ return minnow_panel_power_on(dssdev);
+}
+
+static int minnow_panel_enable(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ dev_dbg(&dssdev->dev, "enable\n");
+
+ mutex_lock(&mpd->lock);
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
+ r = -EINVAL;
+ goto err;
+ }
+
+ dsi_bus_lock(dssdev);
+
+ r = minnow_panel_power_on(dssdev);
+
+ dsi_bus_unlock(dssdev);
+
+ if (r)
+ goto err;
+
+ minnow_panel_queue_esd_work(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+
+ mutex_unlock(&mpd->lock);
+
+ return 0;
+err:
+ dev_dbg(&dssdev->dev, "enable failed\n");
+ mutex_unlock(&mpd->lock);
+ return r;
+}
+
+static void minnow_panel_disable(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+
+ dev_dbg(&dssdev->dev, "disable\n");
+
+ mutex_lock(&mpd->lock);
+
+ minnow_panel_cancel_ulps_work(dssdev);
+ minnow_panel_cancel_esd_work(dssdev);
+
+ dsi_bus_lock(dssdev);
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+ int r;
+
+ r = minnow_panel_wake_up(dssdev);
+ if (!r)
+ minnow_panel_power_off(dssdev);
+ }
+
+ dsi_bus_unlock(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+
+ mutex_unlock(&mpd->lock);
+}
+
+static void minnow_panel_framedone_cb(int err, void *data)
+{
+ struct omap_dss_device *dssdev = data;
+ dev_dbg(&dssdev->dev, "framedone, err %d\n", err);
+ dsi_bus_unlock(dssdev);
+}
+
+static irqreturn_t minnow_panel_te_isr(int irq, void *data)
+{
+ struct omap_dss_device *dssdev = data;
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ int old;
+ int r;
+
+ old = atomic_cmpxchg(&mpd->do_update, 1, 0);
+
+ if (old) {
+ cancel_delayed_work(&mpd->te_timeout_work);
+
+ r = omap_dsi_update(dssdev, mpd->channel, minnow_panel_framedone_cb,
+ dssdev);
+ if (r)
+ goto err;
+ }
+
+ return IRQ_HANDLED;
+err:
+ dev_err(&dssdev->dev, "start update failed\n");
+ dsi_bus_unlock(dssdev);
+ return IRQ_HANDLED;
+}
+
+static void minnow_panel_te_timeout_work_callback(struct work_struct *work)
+{
+ struct minnow_panel_data *mpd = container_of(work, struct minnow_panel_data,
+ te_timeout_work.work);
+ struct omap_dss_device *dssdev = mpd->dssdev;
+
+ dev_err(&dssdev->dev, "TE not received for 250ms!\n");
+
+ atomic_set(&mpd->do_update, 0);
+ dsi_bus_unlock(dssdev);
+}
+
+static int minnow_panel_update(struct omap_dss_device *dssdev,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
+
+ mutex_lock(&mpd->lock);
+ dsi_bus_lock(dssdev);
+
+ r = minnow_panel_wake_up(dssdev);
+ if (r)
+ goto err;
+
+ if (!mpd->enabled) {
+ r = 0;
+ goto err;
+ }
+
+ /* XXX no need to send this every frame, but dsi break if not done */
+ r = minnow_panel_set_update_window(mpd, 0, 0,
+ dssdev->panel.timings.x_res,
+ dssdev->panel.timings.y_res);
+ if (r)
+ goto err;
+
+ if (mpd->te_enabled && gpio_is_valid(mpd->ext_te_gpio)) {
+ schedule_delayed_work(&mpd->te_timeout_work,
+ msecs_to_jiffies(250));
+ atomic_set(&mpd->do_update, 1);
+ } else {
+ r = omap_dsi_update(dssdev, mpd->channel, minnow_panel_framedone_cb,
+ dssdev);
+ if (r)
+ goto err;
+ }
+
+ /* note: no bus_unlock here. unlock is in framedone_cb */
+ mutex_unlock(&mpd->lock);
+ return 0;
+err:
+ dsi_bus_unlock(dssdev);
+ mutex_unlock(&mpd->lock);
+ return r;
+}
+
+static int minnow_panel_sync(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+
+ dev_dbg(&dssdev->dev, "sync\n");
+
+ mutex_lock(&mpd->lock);
+ dsi_bus_lock(dssdev);
+ dsi_bus_unlock(dssdev);
+ mutex_unlock(&mpd->lock);
+
+ dev_dbg(&dssdev->dev, "sync done\n");
+
+ return 0;
+}
+
+static int _minnow_panel_enable_te(struct omap_dss_device *dssdev, bool enable)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ if (enable)
+ r = minnow_panel_dcs_write_1(mpd, MIPI_DCS_SET_TEAR_ON, 0);
+ else
+ r = minnow_panel_dcs_write_0(mpd, MIPI_DCS_SET_TEAR_OFF);
+
+ if (!gpio_is_valid(mpd->ext_te_gpio))
+ omapdss_dsi_enable_te(dssdev, enable);
+
+ /* possible panel bug */
+ msleep(100);
+
+ return r;
+}
+
+static int minnow_panel_enable_te(struct omap_dss_device *dssdev, bool enable)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ mutex_lock(&mpd->lock);
+
+ if (mpd->te_enabled == enable)
+ goto end;
+
+ dsi_bus_lock(dssdev);
+
+ if (mpd->enabled) {
+ r = minnow_panel_wake_up(dssdev);
+ if (r)
+ goto err;
+
+ r = _minnow_panel_enable_te(dssdev, enable);
+ if (r)
+ goto err;
+ }
+
+ mpd->te_enabled = enable;
+
+ dsi_bus_unlock(dssdev);
+end:
+ mutex_unlock(&mpd->lock);
+
+ return 0;
+err:
+ dsi_bus_unlock(dssdev);
+ mutex_unlock(&mpd->lock);
+
+ return r;
+}
+
+static int minnow_panel_get_te(struct omap_dss_device *dssdev)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ int r;
+
+ mutex_lock(&mpd->lock);
+ r = mpd->te_enabled;
+ mutex_unlock(&mpd->lock);
+
+ return r;
+}
+
+static int minnow_panel_run_test(struct omap_dss_device *dssdev, int test_num)
+{
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+ u8 id1, id2, id3;
+ int r;
+
+ mutex_lock(&mpd->lock);
+
+ if (!mpd->enabled) {
+ r = -ENODEV;
+ goto err1;
+ }
+
+ dsi_bus_lock(dssdev);
+
+ r = minnow_panel_wake_up(dssdev);
+ if (r)
+ goto err2;
+
+ r = minnow_panel_dcs_read_1(mpd, DCS_GET_ID1, &id1);
+ if (r)
+ goto err2;
+ r = minnow_panel_dcs_read_1(mpd, DCS_GET_ID2, &id2);
+ if (r)
+ goto err2;
+ r = minnow_panel_dcs_read_1(mpd, DCS_GET_ID3, &id3);
+ if (r)
+ goto err2;
+
+ dsi_bus_unlock(dssdev);
+ mutex_unlock(&mpd->lock);
+ return 0;
+err2:
+ dsi_bus_unlock(dssdev);
+err1:
+ mutex_unlock(&mpd->lock);
+ return r;
+}
+
+static int minnow_panel_memory_read(struct omap_dss_device *dssdev,
+ void *buf, size_t size,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ int r;
+ int first = 1;
+ int plen;
+ unsigned buf_used = 0;
+ struct minnow_panel_data *mpd = dev_get_drvdata(&dssdev->dev);
+
+ if (size < w * h * 3)
+ return -ENOMEM;
+
+ mutex_lock(&mpd->lock);
+
+ if (!mpd->enabled) {
+ r = -ENODEV;
+ goto err1;
+ }
+
+ size = min(w * h * 3,
+ dssdev->panel.timings.x_res *
+ dssdev->panel.timings.y_res * 3);
+
+ dsi_bus_lock(dssdev);
+
+ r = minnow_panel_wake_up(dssdev);
+ if (r)
+ goto err2;
+
+ /* plen 1 or 2 goes into short packet. until checksum error is fixed,
+ * use short packets. plen 32 works, but bigger packets seem to cause
+ * an error. */
+ if (size % 2)
+ plen = 1;
+ else
+ plen = 2;
+
+ minnow_panel_set_update_window(mpd, x, y, w, h);
+
+ r = dsi_vc_set_max_rx_packet_size(dssdev, mpd->channel, plen);
+ if (r)
+ goto err2;
+
+ while (buf_used < size) {
+ u8 dcs_cmd = first ? 0x2e : 0x3e;
+ first = 0;
+
+ r = dsi_vc_dcs_read(dssdev, mpd->channel, dcs_cmd,
+ buf + buf_used, size - buf_used);
+
+ if (r < 0) {
+ dev_err(&dssdev->dev, "read error\n");
+ goto err3;
+ }
+
+ buf_used += r;
+
+ if (r < plen) {
+ dev_err(&dssdev->dev, "short read\n");
+ break;
+ }
+
+ if (signal_pending(current)) {
+ dev_err(&dssdev->dev, "signal pending, "
+ "aborting memory read\n");
+ r = -ERESTARTSYS;
+ goto err3;
+ }
+ }
+
+ r = buf_used;
+
+err3:
+ dsi_vc_set_max_rx_packet_size(dssdev, mpd->channel, 1);
+err2:
+ dsi_bus_unlock(dssdev);
+err1:
+ mutex_unlock(&mpd->lock);
+ return r;
+}
+
+static void minnow_panel_ulps_work(struct work_struct *work)
+{
+ struct minnow_panel_data *mpd = container_of(work, struct minnow_panel_data,
+ ulps_work.work);
+ struct omap_dss_device *dssdev = mpd->dssdev;
+
+ mutex_lock(&mpd->lock);
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !mpd->enabled) {
+ mutex_unlock(&mpd->lock);
+ return;
+ }
+
+ dsi_bus_lock(dssdev);
+
+ minnow_panel_enter_ulps(dssdev);
+
+ dsi_bus_unlock(dssdev);
+ mutex_unlock(&mpd->lock);
+}
+
+static void minnow_panel_esd_work(struct work_struct *work)
+{
+ struct minnow_panel_data *mpd = container_of(work, struct minnow_panel_data,
+ esd_work.work);
+ struct omap_dss_device *dssdev = mpd->dssdev;
+ u8 state1, state2;
+ int r;
+
+ mutex_lock(&mpd->lock);
+
+ if (!mpd->enabled) {
+ mutex_unlock(&mpd->lock);
+ return;
+ }
+
+ dsi_bus_lock(dssdev);
+
+ r = minnow_panel_wake_up(dssdev);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to exit ULPS\n");
+ goto err;
+ }
+
+ r = minnow_panel_dcs_read_1(mpd, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state1);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to read minnow-panel status\n");
+ goto err;
+ }
+
+ /* Run self diagnostics */
+ r = minnow_panel_sleep_out(mpd);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to run minnow-panel self-diagnostics\n");
+ goto err;
+ }
+
+ r = minnow_panel_dcs_read_1(mpd, MIPI_DCS_GET_DIAGNOSTIC_RESULT, &state2);
+ if (r) {
+ dev_err(&dssdev->dev, "failed to read minnow-panel status\n");
+ goto err;
+ }
+
+ /* Each sleep out command will trigger a self diagnostic and flip
+ * Bit6 if the test passes.
+ */
+ if (!((state1 ^ state2) & (1 << 6))) {
+ dev_err(&dssdev->dev, "LCD self diagnostics failed\n");
+ goto err;
+ }
+ /* Self-diagnostics result is also shown on TE GPIO line. We need
+ * to re-enable TE after self diagnostics */
+ if (mpd->te_enabled && gpio_is_valid(mpd->ext_te_gpio)) {
+ r = minnow_panel_dcs_write_1(mpd, MIPI_DCS_SET_TEAR_ON, 0);
+ if (r)
+ goto err;
+ }
+
+ dsi_bus_unlock(dssdev);
+
+ minnow_panel_queue_esd_work(dssdev);
+
+ mutex_unlock(&mpd->lock);
+
+ return;
+err:
+ dev_err(&dssdev->dev, "performing LCD reset\n");
+
+ minnow_panel_reset(dssdev);
+
+ dsi_bus_unlock(dssdev);
+
+ minnow_panel_queue_esd_work(dssdev);
+
+ mutex_unlock(&mpd->lock);
+}
+
+static struct omap_dss_driver minnow_panel_driver = {
+ .probe = minnow_panel_probe,
+ .remove = __exit_p(minnow_panel_remove),
+
+ .enable = minnow_panel_enable,
+ .disable = minnow_panel_disable,
+
+ .update = minnow_panel_update,
+ .sync = minnow_panel_sync,
+
+ .get_resolution = minnow_panel_get_resolution,
+ .get_recommended_bpp = omapdss_default_get_recommended_bpp,
+
+ .enable_te = minnow_panel_enable_te,
+ .get_te = minnow_panel_get_te,
+
+ .run_test = minnow_panel_run_test,
+ .memory_read = minnow_panel_memory_read,
+
+ .driver = {
+ .name = "minnow-panel",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init minnow_panel_init(void)
+{
+ omap_dss_register_driver(&minnow_panel_driver);
+
+ return 0;
+}
+
+static void __exit minnow_panel_exit(void)
+{
+ omap_dss_unregister_driver(&minnow_panel_driver);
+}
+
+module_init(minnow_panel_init);
+module_exit(minnow_panel_exit);
+
+MODULE_DESCRIPTION("Minnow Panel DSI Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c
index a73dedc3310..749aa38605a 100644
--- a/drivers/video/omap2/dss/dsi.c
+++ b/drivers/video/omap2/dss/dsi.c
@@ -4272,10 +4272,6 @@ static void dsi_update_screen_dispc(struct platform_device *dsidev)
dsi_perf_mark_start(dsidev);
- r = schedule_delayed_work(&dsi->framedone_timeout_work,
- msecs_to_jiffies(250));
- BUG_ON(r == 0);
-
dss_mgr_set_timings(mgr, &dsi->timings);
dss_mgr_start_update(mgr);
@@ -4291,6 +4287,11 @@ static void dsi_update_screen_dispc(struct platform_device *dsidev)
mod_timer(&dsi->te_timer, jiffies + msecs_to_jiffies(250));
#endif
}
+ /* Start timer at bottom to avoid dispc update start delayed sometimes
+ */
+ r = schedule_delayed_work(&dsi->framedone_timeout_work,
+ msecs_to_jiffies(250));
+ BUG_ON(r == 0);
}
#ifdef DSI_CATCH_MISSING_TE
@@ -4593,6 +4594,9 @@ int omapdss_dsi_display_enable(struct omap_dss_device *dssdev)
dsi_enable_pll_clock(dsidev, 1);
+ dsi_vc_enable(dsidev, 0, 0);
+ dsi_vc_enable(dsidev, 1, 0);
+
_dsi_initialize_irq(dsidev);
r = dsi_display_init_dsi(dsidev);
@@ -5521,13 +5525,6 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
return -ENODEV;
}
- r = devm_request_irq(&dsidev->dev, dsi->irq, omap_dsi_irq_handler,
- IRQF_SHARED, dev_name(&dsidev->dev), dsi->pdev);
- if (r < 0) {
- DSSERR("request_irq failed\n");
- return r;
- }
-
/* DSI VCs initialization */
for (i = 0; i < ARRAY_SIZE(dsi->vc); i++) {
dsi->vc[i].source = DSI_VC_SOURCE_L4;
@@ -5547,6 +5544,13 @@ static int omap_dsihw_probe(struct platform_device *dsidev)
if (r)
goto err_runtime_get;
+ r = devm_request_irq(&dsidev->dev, dsi->irq, omap_dsi_irq_handler,
+ IRQF_SHARED, dev_name(&dsidev->dev), dsi->pdev);
+ if (r < 0) {
+ DSSERR("request_irq failed\n");
+ goto err_runtime_get;
+ }
+
rev = dsi_read_reg(dsidev, DSI_REVISION);
dev_dbg(&dsidev->dev, "OMAP DSI rev %d.%d\n",
FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0));
diff --git a/drivers/video/omap2/omapfb/omapfb-ioctl.c b/drivers/video/omap2/omapfb/omapfb-ioctl.c
index d30b45d7264..c9879f3a204 100644
--- a/drivers/video/omap2/omapfb/omapfb-ioctl.c
+++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c
@@ -303,6 +303,18 @@ static int omapfb_update_window(struct fb_info *fbi,
return display->driver->update(display, x, y, w, h);
}
+static int omapfb_update_display(struct fb_info *fbi)
+{
+ struct omap_dss_device *display = fb2display(fbi);
+ u16 dw, dh;
+
+ if (!display)
+ return 0;
+
+ display->driver->get_resolution(display, &dw, &dh);
+ return display->driver->update(display, 0, 0, dw, dh);
+}
+
int omapfb_set_update_mode(struct fb_info *fbi,
enum omapfb_update_mode mode)
{
@@ -604,6 +616,10 @@ int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg)
int r = 0;
switch (cmd) {
+ case FBIO_UPDATE_DISPLAY:
+ DBG("ioctl FBIO_UPDATE_DISPLAY\n");
+ r = omapfb_update_display(fbi);
+ break;
case OMAPFB_SYNC_GFX:
DBG("ioctl SYNC_GFX\n");
if (!display || !display->driver->sync) {
diff --git a/drivers/video/omap2/omapfb/omapfb-main.c b/drivers/video/omap2/omapfb/omapfb-main.c
index 856917b3361..f5f57a4e9a2 100644
--- a/drivers/video/omap2/omapfb/omapfb-main.c
+++ b/drivers/video/omap2/omapfb/omapfb-main.c
@@ -2326,8 +2326,6 @@ static int omapfb_init_display(struct omapfb2_device *fbdev,
d->fbdev = fbdev;
if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) {
- u16 w, h;
-
if (auto_update) {
omapfb_start_auto_update(fbdev, dssdev);
d->update_mode = OMAPFB_AUTO_UPDATE;
@@ -2342,14 +2340,6 @@ static int omapfb_init_display(struct omapfb2_device *fbdev,
return r;
}
}
-
- dssdrv->get_resolution(dssdev, &w, &h);
- r = dssdrv->update(dssdev, 0, 0, w, h);
- if (r) {
- dev_err(fbdev->dev,
- "Failed to update display\n");
- return r;
- }
} else {
d->update_mode = OMAPFB_AUTO_UPDATE;
}
diff --git a/firmware/Makefile b/firmware/Makefile
index cbb09ce9730..bedc96082a5 100644
--- a/firmware/Makefile
+++ b/firmware/Makefile
@@ -135,6 +135,7 @@ fw-shipped-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda/xircom_pgs.fw
fw-shipped-$(CONFIG_USB_VICAM) += vicam/firmware.fw
fw-shipped-$(CONFIG_VIDEO_CPIA2) += cpia2/stv0672_vp4.bin
fw-shipped-$(CONFIG_YAM) += yam/1200.bin yam/9600.bin
+fw-shipped-$(CONFIG_MFD_CPCAP) += cpcap/firmware_0_2x.fw cpcap/firmware_1_2x.fw
fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-)
diff --git a/firmware/cpcap/firmware_0_2x.HEX b/firmware/cpcap/firmware_0_2x.HEX
new file mode 100644
index 00000000000..b2b781243fc
--- /dev/null
+++ b/firmware/cpcap/firmware_0_2x.HEX
@@ -0,0 +1,104 @@
+:2000180001880000000000000000000000000000000000000000000000000000000000003F
+:200038000000000000000000000000000000000001F8000000000000000000000214000099
+:20005800000002220000000000000000000000000000000000000000000000000000000064
+:20007800000000000000000000000000000000000000000000000000000000000000000068
+:20009800000000000000000000000000000000000000000000000000000000000000000048
+:0800B800000000000000000040
+:20012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
+:20014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000000000000000000000AF
+:2001600000000000000000000000000000000000000000000000000000000000000000007F
+:200180000000000000000000055F00000000000000000000000000000000000000000000FB
+:2001A00000000000000000000000000000000000000000000000000000000000000000003F
+:2001C00000000000000000000000000000000000000000000000000000000000000000001F
+:2001E00000000000000000000000000000000000000000000000000007FA0885040001006C
+:2002000000000000000000000000000000000000000000000A510000000000000000000083
+:1002200000000B0A0B21004000000000000000004D
+:200230000000000000000000000000000000000000000000000000000000000200000000AC
+:14025000000000000000000000000000000000000200000098
+:200276005BA00C41A200418B5BAB0941A900B7E2CD0D125BAB0541A900B7E2CD0D125BABB4
+:200296000141A900B7E2CD0D12C6450DCE450C6B0C72EF0B4F6B0A6B095BAB0941A900B7D6
+:2002B600E2A610CD0CC43FEB3FEA5BAB0941A900B7E2CD0BF4C6450BCE450ACD0C145BAB1C
+:2002D6000941A900B7E2CD0C7A5BAB0941A900B7E2CD0D12350200E2AE30CD0CEE5BAB05E2
+:2002F60041A900B7E2CD0BF47B052A0B5BAB0541A900B7E2CD0C4C350100E2AE28A610CDC0
+:200316000CC43FEB3FEA5BAB0141A900B7E2CD0BF4350100E2AE28CD0D12A610CD0BE43FC3
+:20033600E93FE85BAB0141A900B7E2CD0C7A5BAB0541A900B7E2CD0D125BAB0141A900B798
+:20035600E2CD0C1E25187B0CC702337B0BC702327B0AC702317B09C70230A60120014F6BEF
+:200376000C5BAB0B41A900418B8481885BA00E41A200418BC64509CE45086B0A72EF094FF2
+:200396006B086B075BAB0741A900B7E2A610CD0CC43FEB3FEA5BAB0741A900B7E2CD0BF4D0
+:2003B600C64507CE4506CD0C145BAB0741A900B7E2CD0C7AC6450DCE450C6B0E72EF0D4FBF
+:2003D6006B0C6B0B5BAB0B41A900B7E2A610CD0CC43FEB3FEA5BAB0B41A900B7E2CD0BF480
+:2003F600C6450BCE450ACD0C145BAB0B41A900B7E2CD0C7AC64509CE45086B0672EF054F8B
+:200416006B046B035BAB0341A900B7E2A610CD0CC43FEB3FEA5BAB0341A900B7E2CD0BF45F
+:20043600C64507CE4506CD0C145BAB0341A900B7E2CD0C7A5BAB0741A900B7E2CD0D125BD8
+:20045600AB0341A900B7E2CD0C1E2740C6450DCE450C6B0E72EF0D4F6B0C6B0B5BAB0B414B
+:20047600A900B7E2A610CD0CC43FEB3FEA5BAB0B41A900B7E2CD0BF4C6450BCE450ACD0C12
+:20049600145BAB0B41A900B7E2CD0C7AC645126B01C645136B027B0FA0022707A0022741D3
+:2004B600CC05567B0EC702577B0DC702567B0CC702557B0BC702547B06C702537B05C70281
+:2004D600527B04C702517B03C7025055024F014355024E01427B02C7013D7B01C7013C2090
+:2004F6005F7B0E6B0A7B0D6B097B0C6B087B0B6B07350200E2AE54CD0D125BAB0741A9009D
+:20051600B7E2CD0C807B0AC701377B09C701367B08C701357B07C701345BAB0341A900B725
+:20053600E2CD0D12350200E2AE50CD0CEE350100E2AE38CD0BF455024F014555024E014459
+:200556005BAB0F41A900418B814FC70264C70265C70266C70267C70268C70269C7026AC7D3
+:20057600026BC7026CC7026DC7026EC7026F35690123725F012235590125725F0124355138
+:200596000127725F01263520012B35A1012A35070129350001284CC70130725F013135055E
+:2005B6000132725F0133720A410303CD072D720C410372725C02672604725C0266C6480749
+:2005D600C64806A402974FA30226014C5FC7024DCF024CC1024B2605C3024A27304A261498
+:2005F6005D2611A602C702724CC702734AC70274A621200E35030272352502733503027441
+:20061600A625C7027555024D024BCF024AC60267C00239C60266C20238250ACD07734FC7CF
+:200636000266C70267720E410320725C02692604725C0268C60269CA02682715C64101CE16
+:200656004100AA80C74101CF41004FC70268C702697200410224725C026B2604725C026AD6
+:20067600C6026BA00AC6026AA2002515CE4101C64100AA01CF4101C741004FC7026AC70253
+:200696006B7202410224725C026D2604725C026CC6026DA014C6026CA2002515CE4101C68C
+:2006B6004100AA02CF4101C741004FC7026CC7026D7204410224725C026F2604725C026EE0
+:2006D600C6026FA058C6026EA2022515CE4101C64100AA04CF4101C741004FC7026EC70294
+:2006F6006F7206410217C602372712725F0237CE4101C64100AA08CF4101C74100720A41C2
+:200716000203CD09847201013003CD09BFA6CCAE0CCDF009CC05BC88888888C640106B0107
+:20073600C640116B02C640126B03C640136B047B01A50427067B03A50826093501025C844D
+:2007560084848481C6025C27F6725F025CC64101CE4100AA20C74101CF410020E272000296
+:200776005D3CC64807CE4806AA01C74807CF4806C64809CE4808AA01C74809CF4808C64896
+:200796000BCE480AAA01C7480BCF480ACE025D725C025DD602725FC70239CF023881C64895
+:2007B60007CE4806A4FEC74807CF4806C64809CE4808A4FEC74809CF4808C6480BCE480AE4
+:2007D600A4FEC7480BCF480ACE025D725C025DD602725FC70239CF0238C6025DA104260325
+:2007F600CF025D81A6CCAE0CCDF009725C025F2604725C025EC6025FA058C6025EA20225AD
+:200816003DCE4601C64600AA90CF4601C74600CE4009C64008A4FECF4009C74008C646030A
+:20083600CE4602AA7741AA91CF4603C74602A6CCAE0CCDF0004FC7025EC7025F201CC6023D
+:200856005FA1572615CE025EA302260EC64603CE4602AA40C74603CF4602CD02764D278476
+:200876007207410203CC07FA35010237CC07FA8889CE4009C64008AA01CF4009C74008CEC4
+:200896004001C64000AA01CF4001C74000C64603CE4602A4BFC74603CF4602CE4605C64665
+:2008B60004A403CF023BC7023ACE4607C64606A403CF023DC7023CCE4609C64608A403CFDA
+:2008D600023FC7023ECE460BC6460AA403CF0241C70240CE460DC6460CA403CF0243C70206
+:2008F60042CE460FC6460EA403CF0245C70244CE4611C64610A403CF0247C70246CE461363
+:20091600C64612A403CF0249C7024855023B023655023A0235C60127C00236C60126C202A8
+:20093600352504A6022014C60125C00236C60124C20235250BC6023426064CC70234201BC3
+:20095600C60123C00236C60122C20235240DC602342708725F023435010237C602342704C4
+:20097600350102377B02AE01CDF2B68484818888C64011C64010A402974F6B0272EF01C16F
+:20099600026126069FC1026027187B02C702617B01C70260CE4101C64100AA20CF4101C7AC
+:2009B6004100848481003FFFFD88888888C64509CE45086B0472EF034F6B026B015BAB01CB
+:2009D60041A900B7E2A610CD0CC43FEB3FEA5BAB0141A900B7E2CD0BF4C64507CE4506CD8A
+:2009F6000C145BAB0141A900B7E2CD0C7A5BAB0141A900B7E2CD0D12350900E2AEBBCD0C0C
+:200A16001E2406725F0262202D5BAB0141A900B7E2CD0D12350900E2AEBBCD0C1E2517C6FE
+:200A360002622612CE4101C64100AA40CF4101C741003501026284848484818888884F6B6D
+:200A560001AE03D70134D701385A2AF7725F013C725F013D725F0142725F0143725F0144DB
+:200A7600725F0145725F01465FCF024ECF024FAE03D70250D702545A2AF772100146C6453D
+:200A960013C70141CE4512CF0140C64513C7013FCE4512CF013EC645126B02C645136B0381
+:200AB600C6013F72E003C6013E72E202240A7B03C7013F7B02C7013E7B01261CC6013299E4
+:200AD600C2024F4FC2024E240FA602CD038172120146A6016B012009A604CD0381721401D7
+:200AF60046A6CCAE0CCDF009725C024F26A8725C024E20A27209014A11725F475B353047E5
+:200B16005AA6CCAE0CCDF00920F7818889C62014CE2013AA40C72014CF20137208014A0320
+:200B3600CC0BBC7201475B4CC64307C70259CE4306CF0258C6430BC7025BCE430ACF025ABB
+:200B5600C64301CE4300AA70C74301CF4300CE4307C64306A4F0CF4307C74306CE430BC667
+:200B7600430AA4F0CF430BC7430AC68115CE8114AA80202C550259430755025843065502CF
+:200B96005B430B55025A430AC64301CE4300A48FAA10C74301CF4300C68115CE8114A47F91
+:200BB600C78115CF8114C62014CE2013A4BFC72014CF20137B02AE01CDF2B62008354020A5
+:200BD60011725F2010720C2011F3848481004D270B34E836E936EA36EB4A26F58100BFE33F
+:200BF60088B6E892C7E2AE01B6E992D7E25CB6EA92D7E25CB6EB92D7E284BEE38100B7EB0E
+:200C1600BFEA3FE93FE881003FE3B6E892D1E226233CE3B6E992D1E226123CE3B6EA92D19A
+:200C3600E226093CE3B6EB92D1E227082404A6FF2002A6018100BFE3723300E2AE01726395
+:200C560000E25C726300E25C726000E226125A726C00E2260B5A726C00E22604723C00E222
+:200C7600BEE38100CD0CA0CC0BF6CD0CEECD0C8ACC0BF60033E833E933EA30EB260A3CEA3A
+:200C960026063CE926023CE88100BFE3B6E892CAE2B7E8B6E9AE0192DAE2B7E9B6EA5C9239
+:200CB600DAE2B7EAB6EB5C92DAE2B7EB8100CD0D122003CD0CDC4D270B38EB39EA39E9396F
+:200CD600E84A26F5810088F6B7E8E601B7E9E602B7EAE603B7EB8481BFE3AE03B6EB92D01D
+:200CF600E2B7EBB6EA5A92D2E2B7EAB6E95A92D2E2B7E9B6E892C2E2B7E88100BFE38892EA
+:1C0D1600C6E2B7E8AE0192D6E2B7E95C92D6E2B7EA5C92D6E2B7EB84BEE38100AC
+:00000001FF \ No newline at end of file
diff --git a/firmware/cpcap/firmware_1_2x.H16 b/firmware/cpcap/firmware_1_2x.H16
new file mode 100644
index 00000000000..61bb3dc5078
--- /dev/null
+++ b/firmware/cpcap/firmware_1_2x.H16
@@ -0,0 +1,3 @@
+:05949130009188919A24000004010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000012005030001E410F82A1000052A1000041301200503000BC410F82A1000052A100004130938290F0240F40B20040531A40B20040532640B20080532840B20020532A40B20020532C43829160438291624382916443829166438291684382916A4382916C40B2006990E640B2005990E840B2005190EA40B2A12090EC40B2000790EE43829170438291724382917443C2918243C291834382916E43C2918043D29181D0B201000110C0B201000108C0B2040000C0C0B2040000C640B20C80018440B20010018012005030041E435C52A100004130120A435AB0B201009FA4280412005030018C3C0412005030000C4C4A4A4C413A52A100004130B0B2002011022C13B0B204000100280DB0B208000102280993C29180240843C29180D0B2002001C83C0243D29180B0B2004011022C22539291629292916E9162281C93C29181200AD3921806D3921808D392180A403F0003435E3C09C3921806C3921808C392180A403F001B434E4EC291814F82916E43829162B0B2008011022C06539291642405D0B2008001C843829164B0B2010011022C095392916690B2000A91662805D0B2010001C843829166B0B2020011022C095392916890B2001491682805D0B2020001C843829168B0B2040011022C095392916A90B2003C916A2805D0B2040001C84382916AB0B2100011022C295392916C90B20258916C280940B29000030040B2917703024382916C3C0790B20257916C2003D0B20040030212005030012C934C240243D29183B0B2080011022C0693C291832405D0B2080001C843C29183B0B2400011022C0C93C29185200512005030020243D291851200503002463C0243C291851200503001B6435C52A10000413080310020410C403E9140403D001012005030027CC0B200400302403E03FF4E0CF21C03044C810000438100024E0CF21C03064C810004438100064E0CF21C03084C8100084381000A4E0CF21C030A4C81000C4381000E4E0CF21C030C4C810010438100124E0CF21C030E4C810014438100164E0CF21C03104C8100184381001AF21E03124E81001C4381001E41A29174421F91749F8290EA280343E291823C16425E91829F8290E82805934E200343D291823C0C9F8290E62C07934E240543C2918243D291833C04934E240243D291835031002052A100004130421F150C421E150A4E0C4F0D821C9170721D9172930D3404E33CE33D531C630D921D90EE280C2003921C90EC28084E8291704F829172435C52A100004130434C52A100004130120A120B120812094219150842181506421F150C421E150A421B1508421A15069A0820029B092404421F150C421E150A421D151242189176836C2403836C240E3C1F4E82917C4F82917E4A8291784B82917A488291164D8291103C12821E917C721F917E4E8291084F82910A821A9178721B917A4A82910C4B82910E4882911841394138413B413A52A100004130421F1508421E1506903F003F28042008903EFFFD2C0543C2918652A10000413093C291862005D0B2400001C843D2918652A10000413043C29184438291084382910A4382910C4382910E43829110438291164382911843C2911A43829176438291784382917A4382917C4382917ED3D2911A42921512911442921512911252A100004130421F15129F8291122C024F82911293C29184200E425F91029F8291762809436C12005030FEC8D3E2911A43D291843C06426C12005030FEB6D2E2911A5392917652A10000413043D2910043C2910140F20005910243C2910352A100004130120C4EBC0000532C831D23FB413C52A1000041304C0F5D0F3C0343CC0000531C9F0C23FB52A100004130403C9160403D002712005030FFDC00009F
+:0002901800913095
+:0000000001FF
diff --git a/include/linux/input/touch_platform.h b/include/linux/input/touch_platform.h
new file mode 100644
index 00000000000..89c489ea61e
--- /dev/null
+++ b/include/linux/input/touch_platform.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 Motorola Mobility, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+/* Defines generic platform structures for touch drivers */
+#ifndef _LINUX_TOUCH_PLATFORM_H
+#define _LINUX_TOUCH_PLATFORM_H
+
+#include <linux/types.h>
+
+#define ATMXT_I2C_NAME "atmxt-i2c"
+
+struct touch_settings {
+ const uint8_t *data;
+ uint8_t size;
+ uint8_t tag;
+} __attribute__ ((packed));
+
+struct touch_firmware {
+ const uint8_t *img;
+ uint32_t size;
+ const uint8_t *ver;
+ uint8_t vsize;
+} __attribute__ ((packed));
+
+struct touch_framework {
+ const uint16_t *abs;
+ uint8_t size;
+ uint8_t enable_vkeys;
+} __attribute__ ((packed));
+
+struct touch_platform_data {
+ struct touch_settings *sett[256];
+ struct touch_firmware *fw;
+ struct touch_framework *frmwrk;
+
+ uint8_t addr[2];
+ uint16_t flags;
+
+ int gpio_reset;
+ int gpio_interrupt;
+ char *filename;
+
+ int (*hw_reset)(void);
+ int (*hw_recov)(int);
+ int (*irq_stat)(void);
+} __attribute__ ((packed));
+
+#endif /* _LINUX_TOUCH_PLATFORM_H */
diff --git a/include/linux/m4sensorhub.h b/include/linux/m4sensorhub.h
new file mode 100644
index 00000000000..c9a14917011
--- /dev/null
+++ b/include/linux/m4sensorhub.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (c) 2012, Motorola, Inc. All Rights Reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __M4SENSORHUB_H__
+#define __M4SENSORHUB_H__
+
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/m4sensorhub/m4sensorhub_registers.h>
+#include <linux/m4sensorhub/m4sensorhub_irqs.h>
+
+#ifdef __KERNEL__
+
+extern char m4sensorhub_debug;
+
+#define M4SENSORHUB_DRIVER_NAME "m4sensorhub"
+#define M4SENSORHUB_I2C_ADDR 0x18
+
+#define KDEBUG(i, format, s...) \
+ do { \
+ if (m4sensorhub_debug >= i) \
+ printk(KERN_CRIT format, ##s); \
+ } while (0)
+
+#define CHECK_REG_ACCESS_RETVAL(m4sensorhub, retval, reg) \
+ ((retval == m4sensorhub_reg_getsize(m4sensorhub, reg)) \
+ ? 0 : -EFAULT);
+
+enum m4sensorhub_debug_level {
+ M4SH_NODEBUG = 0x0,
+ M4SH_CRITICAL,
+ M4SH_ERROR,
+ M4SH_WARNING,
+ M4SH_NOTICE,
+ M4SH_INFO,
+ M4SH_DEBUG,
+ M4SH_VERBOSE_DEBUG
+};
+
+enum m4sensorhub_mode {
+ UNINITIALIZED,
+ BOOTMODE,
+ NORMALMODE,
+ FACTORYMODE
+};
+
+enum m4sensorhub_bootmode {
+ BOOTMODE00,
+ BOOTMODE01,
+ BOOTMODE10,
+ BOOTMODE11,
+};
+
+/* This enum is used to register M4 panic callback
+ * The sequence of this enum is also the sequence of calling
+ * i.e. it will be called follow this enum 0, 1, 2 ... max
+*/
+enum m4sensorhub_panichdl_index {
+ PANICHDL_DISPLAY_RESTORE,
+ /* Please add enum before PANICHDL_IRQ_RESTORE
+ to make sure IRQ resotre will be called at last
+ */
+ PANICHDL_IRQ_RESTORE, /* Keep it as the last one */
+ PANICHDL_MAX = PANICHDL_IRQ_RESTORE+1
+};
+
+struct m4sensorhub_data;
+
+struct m4sensorhub_platform_data {
+ int (*hw_init)(struct m4sensorhub_data *);
+ void (*hw_free)(struct m4sensorhub_data *);
+ void (*hw_reset)(struct m4sensorhub_data *);
+ int (*set_bootmode)(struct m4sensorhub_data *,
+ enum m4sensorhub_bootmode);
+ int (*stillmode_exit)(void);
+ int (*set_display_control)(int m4_ctrl, int gpio_mipi_mux);
+};
+
+struct m4sensorhub_hwconfig {
+ int irq_gpio;
+ int reset_gpio;
+ int wake_gpio;
+ int boot0_gpio;
+ int boot1_gpio;
+ int mpu_9150_en_gpio;
+};
+
+struct m4sensorhub_data {
+ struct i2c_client *i2c_client;
+ void *irqdata;
+ void *panicdata;
+ enum m4sensorhub_mode mode;
+ struct m4sensorhub_platform_data *pdev;
+ struct m4sensorhub_hwconfig hwconfig;
+};
+
+/* Global (kernel) functions */
+
+/* Client devices */
+struct m4sensorhub_data *m4sensorhub_client_get_drvdata(void);
+
+/* Register access */
+
+/* m4sensorhub_reg_read()
+
+ Read a register from the M4 sensor hub.
+
+ Returns number of bytes read on success.
+ Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ reg - Register to be read
+ value - array to return data. Needs to be at least register's size
+*/
+#define m4sensorhub_reg_read(m4sensorhub, reg, value) \
+ m4sensorhub_reg_read_n(m4sensorhub, reg, value, \
+ m4sensorhub_reg_getsize(m4sensorhub, reg))
+
+/* m4sensorhub_reg_write()
+
+ Read a register from the M4 sensor hub.
+
+ Returns number of bytes write on success.
+ Returns negative error code on failure
+
+ m4sensorhub - pointer to the main m4sensorhub data struct
+ reg - Register to be write
+ value - array to return data. Needs to be at least register's size
+ mask - mask representing which bits to change in register. If all bits
+ are to be changed, then &m4sh_no_mask can be passed here.
+*/
+#define m4sensorhub_reg_write(m4sensorhub, reg, value, mask) \
+ m4sensorhub_reg_write_n(m4sensorhub, reg, value, mask, \
+ m4sensorhub_reg_getsize(m4sensorhub, reg))
+int m4sensorhub_reg_init(struct m4sensorhub_data *m4sensorhub);
+int m4sensorhub_reg_shutdown(struct m4sensorhub_data *m4sensorhub);
+int m4sensorhub_reg_read_n(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_reg reg, unsigned char *value,
+ short num);
+int m4sensorhub_reg_write_n(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_reg reg, unsigned char *value,
+ unsigned char *mask, short num);
+int m4sensorhub_reg_write_1byte(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_reg reg, unsigned char value,
+ unsigned char mask);
+int m4sensorhub_reg_getsize(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_reg reg);
+void m4sensorhub_reg_access_lock(void);
+void m4sensorhub_reg_access_unlock(void);
+int m4sensorhub_i2c_write_read(struct m4sensorhub_data *m4sensorhub,
+ u8 *buf, int writelen, int readlen);
+
+int m4sensorhub_load_firmware(struct m4sensorhub_data *m4sensorhub,
+ unsigned short force_upgrade);
+
+/* Interrupt handler */
+int m4sensorhub_irq_init(struct m4sensorhub_data *m4sensorhub);
+void m4sensorhub_irq_shutdown(struct m4sensorhub_data *m4sensorhub);
+int m4sensorhub_irq_register(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_irqs irq,
+ void (*cb_func) (enum m4sensorhub_irqs, void *),
+ void *data);
+int m4sensorhub_irq_unregister(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_irqs irq);
+int m4sensorhub_irq_disable(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_irqs irq);
+int m4sensorhub_irq_enable(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_irqs irq);
+int m4sensorhub_irq_enable_get(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_irqs irq);
+void m4sensorhub_irq_pm_dbg_suspend(void);
+void m4sensorhub_irq_pm_dbg_resume(void);
+
+int m4sensorhub_panic_init(struct m4sensorhub_data *m4sensorhub);
+void m4sensorhub_panic_shutdown(struct m4sensorhub_data *m4sensorhub);
+int m4sensorhub_panic_register(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_panichdl_index index,
+ void (*cb_func)(struct m4sensorhub_data *, void *),
+ void *data);
+int m4sensorhub_panic_unregister(struct m4sensorhub_data *m4sensorhub,
+ enum m4sensorhub_panichdl_index index);
+void m4sensorhub_panic_process(struct m4sensorhub_data *m4sensorhub);
+
+#endif /* __KERNEL__ */
+#endif /* __M4SENSORHUB_H__ */
+
diff --git a/include/linux/m4sensorhub/MemMapAccelSensor.h b/include/linux/m4sensorhub/MemMapAccelSensor.h
new file mode 100644
index 00000000000..5ba43e340e5
--- /dev/null
+++ b/include/linux/m4sensorhub/MemMapAccelSensor.h
@@ -0,0 +1,29 @@
+/*********************************************************************
+*
+* Copyright (C) 2012 Motorola, Inc.
+*
+**********************************************************************
+
+File : MemMapAccelSensor.h
+Purpose :
+**********************************************************************/
+#ifndef __MEMMAP_ACCELSENSOR_H__
+#define __MEMMAP_ACCELSENSOR_H__
+/****************************** Defines *******************************/
+typedef struct memMapAccel {
+ u8 version;
+ u8 testCmd;
+ u16 dummy; /* Align to 32-bit boundary */
+ s32 x;
+ s32 y;
+ s32 z;
+} sAccelData;
+
+/**************************** Globals ********************************/
+
+
+/***************************** Prototypes *****************************/
+
+
+
+#endif /* __MEMMAP_ACCELSENSOR_H__ */
diff --git a/include/linux/m4sensorhub/MemMapAudio.h b/include/linux/m4sensorhub/MemMapAudio.h
new file mode 100644
index 00000000000..4a874beac39
--- /dev/null
+++ b/include/linux/m4sensorhub/MemMapAudio.h
@@ -0,0 +1,29 @@
+/*********************************************************************
+*
+* Copyright (C) 2012 Motorola, Inc.
+*
+**********************************************************************
+
+File : MemMapAudio.h
+Purpose :
+**********************************************************************/
+#ifndef __MEMMAP_AUDIO_H__
+#define __MEMMAP_AUDIO_H__
+/****************************** Defines *******************************/
+typedef struct memMapAudio {
+ u8 version;
+ u8 enable;
+ u16 dummy;
+ u32 totalPackets;
+} sAudioData;
+
+#define AUDIO_BUFFER_SIZE 800
+/**************************** Globals ********************************/
+
+
+/***************************** Prototypes *****************************/
+
+
+
+#endif /* __MEMMAP_AUDIO_H__ */
+
diff --git a/include/linux/m4sensorhub/MemMapCompassSensor.h b/include/linux/m4sensorhub/MemMapCompassSensor.h
new file mode 100644
index 00000000000..a4da37d43b1
--- /dev/null
+++ b/include/linux/m4sensorhub/MemMapCompassSensor.h
@@ -0,0 +1,30 @@
+/*********************************************************************
+*
+* Copyright (C) 2012 Motorola, Inc.
+*
+**********************************************************************
+
+File : MemMapCompassSensor.h
+Purpose :
+**********************************************************************/
+#ifndef __MEMMAP_COMPASSSENSOR_H__
+#define __MEMMAP_COMPASSSENSOR_H__
+/****************************** Defines *******************************/
+typedef struct memMapCompass {
+ u8 version;
+ u8 testCmd;
+ u16 dummy; /* Align to 32-bit boundary */
+ s32 x;
+ s32 y;
+ s32 z;
+ s8 accuracy;
+} sCompassData;
+
+/**************************** Globals ********************************/
+
+
+/***************************** Prototypes *****************************/
+
+
+
+#endif /* __MEMMAP_COMPASSSENSOR_H__ */
diff --git a/include/linux/m4sensorhub/MemMapDownload.h b/include/linux/m4sensorhub/MemMapDownload.h
new file mode 100644
index 00000000000..526a917c0b9
--- /dev/null
+++ b/include/linux/m4sensorhub/MemMapDownload.h
@@ -0,0 +1,51 @@
+/*********************************************************************
+*
+* Copyright (C) 2012 Motorola, Inc.
+*
+**********************************************************************
+File : MemMapDownload.h
+Purpose :
+**********************************************************************/
+#ifndef __MEMMAP_DOWNLOAD_H__
+#define __MEMMAP_DOWNLOAD_H__
+/****************************** Defines *******************************/
+
+/* These enums and defines need to match up with the enums
+ * in m4sensorhub_client_ioctl.h
+ */
+
+#define M4SH_DL_FILENAME_SIZE 16
+#define M4SH_DL_PACKET_SIZE 100
+
+enum download_error_codes {
+ DOWNLOAD_SUCCESS,
+ DOWNLOAD_ERROR_GET_CHECKSUM,
+ DOWNLOAD_ERROR_OPEN_FILE,
+ DOWNLOAD_ERROR_WRITE_FILE,
+ DOWNLOAD_ERROR_CLOSE_FILE,
+ DOWNLOAD_ERROR_DELETE_FILE,
+ DOWNLOAD_ERROR_INVALID_SIZE,
+ /*internal error code for M4<==>Kernel*/
+ DOWNLOAD_ERROR_SEND_CMD = 0x80,
+ DOWNLOAD_ERROR_DATA_CHECKSUM,
+};
+
+enum downloadCmds {
+ DOWNLOAD_CMD_GET_CHECKSUM,
+ DOWNLOAD_CMD_OPEN_FILE,
+ DOWNLOAD_CMD_WRITE_FILE,
+ DOWNLOAD_CMD_CLOSE_FILE,
+ DOWNLOAD_CMD_DELETE_FILE,
+};
+
+typedef struct memMapDownload {
+ u8 version;
+ u8 command;
+ u8 status;
+ u8 size;
+ u32 checksum;
+ u8 filename[M4SH_DL_FILENAME_SIZE];
+ u8 packet[M4SH_DL_PACKET_SIZE];
+} sDownload;
+
+#endif /*__MEMMAP_DOWNLOAD_H__*/
diff --git a/include/linux/m4sensorhub/MemMapFusionSensor.h b/include/linux/m4sensorhub/MemMapFusionSensor.h
new file mode 100644
index 00000000000..2d4c2bfb56f
--- /dev/null
+++ b/include/linux/m4sensorhub/MemMapFusionSensor.h
@@ -0,0 +1,37 @@
+/*********************************************************************
+*
+* Copyright (C) 2012 Motorola, Inc.
+*
+**********************************************************************
+
+File : MemMapFusionSensor.h
+Purpose :
+**********************************************************************/
+#ifndef __MEMMAP_FUSIONSENSOR_H__
+#define __MEMMAP_FUSIONSENSOR_H__
+/****************************** Defines *******************************/
+typedef struct memMapFusion {
+ u8 version;
+ u8 testCmd;
+ u16 dummy;
+ s32 eulerPitch;
+ s32 eulerRoll;
+ s32 eulerYaw;
+ s32 localX;
+ s32 localY;
+ s32 localZ;
+ s32 worldX;
+ s32 worldY;
+ s32 worldZ;
+ s16 heading;
+ s8 heading_accuracy;
+} sFusionData;
+
+/**************************** Globals ********************************/
+
+
+/***************************** Prototypes *****************************/
+
+
+
+#endif /* __MEMMAP_FUSIONSENSOR_H__ */
diff --git a/include/linux/m4sensorhub/MemMapGesture.h b/include/linux/m4sensorhub/MemMapGesture.h
new file mode 100644
index 00000000000..9480a42ae11
--- /dev/null
+++ b/include/linux/m4sensorhub/MemMapGesture.h
@@ -0,0 +1,46 @@
+/*********************************************************************
+*
+* Copyright (C) 2012 Motorola, Inc.
+*
+**********************************************************************
+
+File : MemMapGesture.h
+Purpose :
+**********************************************************************/
+#ifndef __MEMMAP_GESTURE_H__
+#define __MEMMAP_GESTURE_H__
+/********************************** Defines **************************/
+typedef struct memMapGesture {
+ u8 version;
+ u8 gesture1;
+ u8 confidence1;
+ s8 value1;
+ u8 gesture2;
+ u8 confidence2;
+ s8 value2;
+ u8 gesture3;
+ u8 confidence3;
+ s8 value3;
+} sGestureData;
+
+typedef enum {
+ GESTURE_NONE,
+ GESTURE_VIEW,
+ GESTURE_WRIST_ROTATE,
+ GESTURE_TAP,
+ GESTURE_HANDSHAKE,
+ GESTURE_HANDWAVE,
+ GESTURE_FISTBUMP,
+ GESTURE_WATCH_ON,
+ GESTURE_WATCH_OFF,
+ GESTURE_TILT_SCROLL,
+ GESTURE_MAX
+} eGestureType;
+
+/********************************** Globals **************************/
+
+/********************************** Prototypes ***********************/
+
+
+
+#endif /* __MEMMAP_GESTURE_H__ */
diff --git a/include/linux/m4sensorhub/MemMapGyroSensor.h b/include/linux/m4sensorhub/MemMapGyroSensor.h
new file mode 100644
index 00000000000..af2f46574cd
--- /dev/null
+++ b/include/linux/m4sensorhub/MemMapGyroSensor.h
@@ -0,0 +1,29 @@
+/*********************************************************************
+*
+* Copyright (C) 2012 Motorola, Inc.
+*
+**********************************************************************
+
+File : MemMapGyroSensor.h
+Purpose :
+**********************************************************************/
+#ifndef __MEMMAP_GYROSENSOR_H__
+#define __MEMMAP_GYROSENSOR_H__
+/****************************** Defines *******************************/
+typedef struct memMapGyro {
+ u8 version;
+ u8 testCmd;
+ u16 dummy; /* Align to 32-bit boundary */
+ s32 x;
+ s32 y;
+ s32 z;
+} sGyroData;
+
+/**************************** Globals ********************************/
+
+
+/***************************** Prototypes *****************************/
+
+
+
+#endif /* __MEMMAP_GYROSENSOR_H__*/
diff --git a/include/linux/m4sensorhub/MemMapLog.h b/include/linux/m4sensorhub/MemMapLog.h
new file mode 100644
index 00000000000..390053900ca
--- /dev/null
+++ b/include/linux/m4sensorhub/MemMapLog.h
@@ -0,0 +1,49 @@
+/*********************************************************************
+*
+* Copyright (C) 2012 Motorola, Inc.
+*
+**********************************************************************
+File : MemMapLog.h
+Purpose :
+**********************************************************************/
+#ifndef __MEMMAP_LOG_H__
+#define __MEMMAP_LOG_H__
+/****************************** Defines *******************************/
+struct memMapLog {
+ u64 logEnable;
+ u8 isLogImmediate;
+};
+
+#define LOG_MAX 20
+#define LOG_LEVELS_MAX 4
+
+static char acLogTags[LOG_MAX][40] = {
+ "LOG_GENERAL",
+ "LOG_TIMER",
+ "LOG_ACCEL",
+ "LOG_TEMPERATURE",
+ "LOG_PRESSURE",
+ "LOG_PEDOMETER",
+ "LOG_TCMD",
+ "LOG_GYRO",
+ "LOG_COMPASS",
+ "LOG_FUSION",
+ "LOG_METS",
+ "LOG_GESTURE",
+ "LOG_POWER",
+ "LOG_CORRELATION",
+ "LOG_GPS",
+ "LOG_DL",
+ "LOG_AUDIO",
+ "LOG_DISP",
+ "LOG_WRIST",
+ "LOG_PASSIVE"
+};
+
+static char acLogLevels[LOG_LEVELS_MAX][15] = {
+ "LOG_DISABLE",
+ "LOG_ERROR",
+ "LOG_VERBOSE",
+ "LOG_DEBUG"
+};
+#endif /* __MEMMAP_LOG_H__ */
diff --git a/include/linux/m4sensorhub/MemMapPassive.h b/include/linux/m4sensorhub/MemMapPassive.h
new file mode 100644
index 00000000000..3d2c016b1ed
--- /dev/null
+++ b/include/linux/m4sensorhub/MemMapPassive.h
@@ -0,0 +1,26 @@
+/*********************************************************************
+*
+* Copyright (C) 2012 Motorola, Inc.
+*
+**********************************************************************
+
+File : MemMapPassive.h
+Purpose :
+**********************************************************************/
+#ifndef __MEMMAP_PASSIVE_H__
+#define __MEMMAP_PASSIVE_H__
+/********************************** Defines **************************/
+
+#define MAX_PASSIVE_BUFFERS 12
+
+typedef struct memMapPassive {
+ u32 timestamp[MAX_PASSIVE_BUFFERS];
+ u32 steps[MAX_PASSIVE_BUFFERS];
+ u32 mets[MAX_PASSIVE_BUFFERS];
+ u32 floorsClimbed[MAX_PASSIVE_BUFFERS];
+} sPassive;
+
+/********************************** Globals ***************************/
+
+/********************************** Prototypes ************************/
+#endif
diff --git a/include/linux/m4sensorhub/MemMapPedometer.h b/include/linux/m4sensorhub/MemMapPedometer.h
new file mode 100644
index 00000000000..129acee157d
--- /dev/null
+++ b/include/linux/m4sensorhub/MemMapPedometer.h
@@ -0,0 +1,34 @@
+/*********************************************************************
+*
+* Copyright (C) 2012 Motorola, Inc.
+*
+**********************************************************************
+
+File : Pedometer.h
+Purpose :
+**********************************************************************/
+#ifndef __PEDOMETER_H__
+#define __PEDOMETER_H__
+/****************************** Defines *******************************/
+typedef struct memMapPedometer {
+ u8 version;
+ u8 testCmd;
+ u16 totalSteps;
+ u32 totatDistance;
+ u32 currentSpeed;
+ u8 activity;
+} sPedoData;
+
+typedef enum {
+ WALK,
+ JOG,
+ RUN
+} eActivity;
+/**************************** Globals ********************************/
+
+
+/***************************** Prototypes *****************************/
+
+
+
+#endif /* __PEDOMETER_H__ */
diff --git a/include/linux/m4sensorhub/MemMapPressureSensor.h b/include/linux/m4sensorhub/MemMapPressureSensor.h
new file mode 100644
index 00000000000..9c192a26447
--- /dev/null
+++ b/include/linux/m4sensorhub/MemMapPressureSensor.h
@@ -0,0 +1,29 @@
+/*********************************************************************
+*
+* Copyright (C) 2012 Motorola, Inc.
+*
+**********************************************************************
+
+File : MemMapPressureSensor.h
+Purpose :
+**********************************************************************/
+#ifndef __PRESS_SENSOR_H__
+#define __PRESS_SENSOR_H__
+/****************************** Defines *******************************/
+typedef struct memMapPressure {
+ u8 version;
+ u8 dummy; /* Align the boundary */
+ u16 sampleRate;
+ u32 pressure;
+ s32 referenceAltitude;
+ u32 seaLevelPressure;
+ s32 absoluteAltitude;
+ s16 temperature;
+} sPressureData;
+
+/**************************** Globals ********************************/
+
+
+/***************************** Prototypes *****************************/
+
+#endif
diff --git a/include/linux/m4sensorhub/MemMapTempSensor.h b/include/linux/m4sensorhub/MemMapTempSensor.h
new file mode 100644
index 00000000000..51fad5d13e5
--- /dev/null
+++ b/include/linux/m4sensorhub/MemMapTempSensor.h
@@ -0,0 +1,26 @@
+/*********************************************************************
+*
+* Copyright (C) 2012 Motorola, Inc.
+*
+**********************************************************************
+
+File : MemMapTempSensor.h
+Purpose :
+**********************************************************************/
+#ifndef __MEMMAP_TEMPSENSOR_H__
+#define __MEMMAP_TEMPSENSOR_H__
+/****************************** Defines *******************************/
+typedef struct memMapTemp {
+ u8 version;
+ u8 testCmd;
+ s16 extrnlTemp;
+ s16 intrnlTemp;
+} sTempData;
+
+/**************************** Globals ********************************/
+
+
+/***************************** Prototypes *****************************/
+
+
+#endif
diff --git a/include/linux/m4sensorhub/m4sensorhub_bank_enum.h b/include/linux/m4sensorhub/m4sensorhub_bank_enum.h
new file mode 100644
index 00000000000..46844ed0b6a
--- /dev/null
+++ b/include/linux/m4sensorhub/m4sensorhub_bank_enum.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013, Motorola, Inc. All Rights Reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/***************************** WARNING ******************************
+ * *
+ * DO NOT EDIT THIS FILE *
+ * *
+ * This is an auto-generated file based on register maps in the *
+ * M4 Sensor Hub source tree. *
+ * *
+ ***************************** WARNING *****************************/
+
+enum m4sensorhub_type {
+ M4SH_TYPE_ACCEL,
+ M4SH_TYPE_TEMP,
+ M4SH_TYPE_GENERAL,
+ M4SH_TYPE_PRESSURE,
+ M4SH_TYPE_PEDOMETER,
+ M4SH_TYPE_TCMD,
+ M4SH_TYPE_LOG,
+ M4SH_TYPE_FUSION,
+ M4SH_TYPE_COMPASS,
+ M4SH_TYPE_GYRO,
+ M4SH_TYPE_METS,
+ M4SH_TYPE_USERSETTINGS,
+ M4SH_TYPE_POWER,
+ M4SH_TYPE_LOCATION,
+ M4SH_TYPE_DOWNLOAD,
+ M4SH_TYPE_AUDIO,
+ M4SH_TYPE_TIMEPIECE,
+ M4SH_TYPE_WRIST,
+ M4SH_TYPE_GESTURE,
+ M4SH_TYPE_PASSIVE,
+
+ M4SH_TYPE__NUM
+};
+
diff --git a/include/linux/m4sensorhub/m4sensorhub_irqs.h b/include/linux/m4sensorhub/m4sensorhub_irqs.h
new file mode 100644
index 00000000000..c41c5c5ed89
--- /dev/null
+++ b/include/linux/m4sensorhub/m4sensorhub_irqs.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2012, Motorola, Inc. All Rights Reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __M4SENSORHUB_IRQS_H__
+#define __M4SENSORHUB_IRQS_H__
+
+#define M4SH_IRQ_INT0_INDEX 0
+#define M4SH_IRQ_INT1_INDEX 8
+
+/* Keep name mapping in m4sensorhub-irq.c 'irq_name' synchronized */
+enum m4sensorhub_irqs {
+ /* 1st supported interrupt */
+ M4SH_IRQ__START,
+
+ /* INT0 0x01: Pressure interrupt */
+ M4SH_IRQ_PRESSURE_DATA_READY = M4SH_IRQ_INT0_INDEX,
+
+ /* INT0 0x02: Temperature interrupt */
+ M4SH_IRQ_TMP_DATA_READY,
+
+ /* INT0 0x04: Gyro interrupt */
+ M4SH_IRQ_GYRO_DATA_READY,
+
+ /* INT0 0x80: Pedometer interrupt */
+ M4SH_IRQ_PEDOMETER_DATA_READY,
+
+ /* INT0 0x10: Compass data interrupt */
+ M4SH_IRQ_COMPASS_DATA_READY,
+
+ /* INT0 0x20: Fusion data interrupt */
+ M4SH_IRQ_FUSION_DATA_READY,
+
+ /* INT0 0x40: Accel data interrupt */
+ M4SH_IRQ_ACCEL_DATA_READY,
+
+ /* INT0 0x80: Gesture interrupt */
+ M4SH_IRQ_GESTURE_DETECTED,
+
+ /* INT1 0x01 : still mode interrupt */
+ M4SH_IRQ_STILL_DETECTED = M4SH_IRQ_INT1_INDEX,
+
+ /* INT1 0x02 : motion detected interrupt */
+ M4SH_IRQ_MOTION_DETECTED,
+
+ /* INT1 0x04 : activity change interrupt */
+ M4SH_IRQ_ACTIVITY_CHANGE,
+
+ /* INT1 0x08 : download command complete interrupt */
+ M4SH_IRQ_DLCMD_RESP_READY,
+
+ /* INT1 0x10 : mic data ready interrupt */
+ M4SH_IRQ_MIC_DATA_READY,
+
+ /* INT1 0x20 : wrist command interrupt */
+ M4SH_IRQ_WRIST_READY,
+
+ /* INT1 0x40 : passive buffer full */
+ M4SH_IRQ_PASSIVE_BUFFER_FULL,
+
+ /* Number of allocated interrupts */
+ M4SH_IRQ__NUM
+};
+#endif /* __M4SENSORHUB_IRQS_H__ */
+
diff --git a/include/linux/m4sensorhub/m4sensorhub_reg_enum.h b/include/linux/m4sensorhub/m4sensorhub_reg_enum.h
new file mode 100644
index 00000000000..09e3a9496c5
--- /dev/null
+++ b/include/linux/m4sensorhub/m4sensorhub_reg_enum.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2013, Motorola, Inc. All Rights Reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+/***************************** WARNING ******************************
+ * *
+ * DO NOT EDIT THIS FILE *
+ * *
+ * This is an auto-generated file based on register maps in the *
+ * M4 Sensor Hub source tree. *
+ * *
+ ***************************** WARNING *****************************/
+
+enum m4sensorhub_reg {
+ M4SH_REG_ACCEL_VERSION,
+ M4SH_REG_ACCEL_DUMMY, /* Align the boundary */
+ M4SH_REG_ACCEL_SAMPLERATE,
+ M4SH_REG_ACCEL_X,
+ M4SH_REG_ACCEL_Y,
+ M4SH_REG_ACCEL_Z,
+ M4SH_REG_ACCEL_SCALEDMAGSQUARED,
+ M4SH_REG_ACCEL_MAGNITUDE,
+ M4SH_REG_ACCEL_TILT,
+ M4SH_REG_ACCEL_ORIENTATION,
+ M4SH_REG_TEMP_VERSION,
+ M4SH_REG_TEMP_DUMMY, /* Align the boundary */
+ M4SH_REG_TEMP_SAMPLERATE,
+ M4SH_REG_TEMP_EXTRNLTEMP,
+ M4SH_REG_TEMP_INTRNLTEMP,
+ M4SH_REG_GENERAL_UTC,
+ M4SH_REG_GENERAL_LOCALTIMEZONE,
+ M4SH_REG_GENERAL_VERSION, /* M4 software version */
+ M4SH_REG_GENERAL_INTERRUPT0ENABLE,
+ M4SH_REG_GENERAL_INTERRUPT1ENABLE,
+ M4SH_REG_GENERAL_INTERRUPT0STATUS,
+ M4SH_REG_GENERAL_INTERRUPT1STATUS,
+ M4SH_REG_PRESSURE_VERSION,
+ M4SH_REG_PRESSURE_DUMMY, /* Align the boundary */
+ M4SH_REG_PRESSURE_SAMPLERATE,
+ M4SH_REG_PRESSURE_PRESSURE,
+ M4SH_REG_PRESSURE_REFERENCEALTITUDE,
+ M4SH_REG_PRESSURE_SEALEVELPRESSURE,
+ M4SH_REG_PRESSURE_ABSOLUTEALTITUDE,
+ M4SH_REG_PRESSURE_TEMPERATURE,
+ M4SH_REG_PRESSURE_ISVALID,
+ M4SH_REG_PEDOMETER_VERSION,
+ M4SH_REG_PEDOMETER_TESTCMD,
+ M4SH_REG_PEDOMETER_ACTIVITY,
+ M4SH_REG_PEDOMETER_EQUIPMENTTYPE,
+ M4SH_REG_PEDOMETER_TOTALSTEPS,
+ M4SH_REG_PEDOMETER_FLOORSCLIMBED,
+ M4SH_REG_PEDOMETER_TOTATDISTANCE,
+ M4SH_REG_PEDOMETER_CURRENTSPEED,
+ M4SH_REG_PEDOMETER_REPORTEDDISTANCE,
+ M4SH_REG_PEDOMETER_USERDISTANCE,
+ M4SH_REG_TCMD_OPCODE,
+ M4SH_REG_LOG_LOGENABLE,
+ M4SH_REG_LOG_ISLOGIMMEDIATE,
+ M4SH_REG_FUSION_VERSION,
+ M4SH_REG_FUSION_DUMMY,
+ M4SH_REG_FUSION_SAMPLERATE,
+ M4SH_REG_FUSION_EULERPITCH,
+ M4SH_REG_FUSION_EULERROLL,
+ M4SH_REG_FUSION_EULERYAW,
+ M4SH_REG_FUSION_LOCALX,
+ M4SH_REG_FUSION_LOCALY,
+ M4SH_REG_FUSION_LOCALZ,
+ M4SH_REG_FUSION_WORLDX,
+ M4SH_REG_FUSION_WORLDY,
+ M4SH_REG_FUSION_WORLDZ,
+ M4SH_REG_FUSION_HEADING,
+ M4SH_REG_FUSION_HEADING_ACCURACY,
+ M4SH_REG_COMPASS_VERSION,
+ M4SH_REG_COMPASS_DUMMY, /* Align the boundary */
+ M4SH_REG_COMPASS_SAMPLERATE,
+ M4SH_REG_COMPASS_X,
+ M4SH_REG_COMPASS_Y,
+ M4SH_REG_COMPASS_Z,
+ M4SH_REG_COMPASS_ACCURACY,
+ M4SH_REG_GYRO_VERSION,
+ M4SH_REG_GYRO_DUMMY, /* Align the boundary */
+ M4SH_REG_GYRO_SAMPLERATE,
+ M4SH_REG_GYRO_X,
+ M4SH_REG_GYRO_Y,
+ M4SH_REG_GYRO_Z,
+ M4SH_REG_METS_VERSION,
+ M4SH_REG_METS_METSACTIVITY,
+ M4SH_REG_METS_MSSAMPLETIME,
+ M4SH_REG_METS_METS,
+ M4SH_REG_METS_CALORIES,
+ M4SH_REG_USERSETTINGS_VERSION,
+ M4SH_REG_USERSETTINGS_USERAGE,
+ M4SH_REG_USERSETTINGS_USERGENDER,
+ M4SH_REG_USERSETTINGS_USERHEIGHT,
+ M4SH_REG_USERSETTINGS_USERWEIGHT,
+ M4SH_REG_USERSETTINGS_SCREENSTATUS,
+ M4SH_REG_USERSETTINGS_RTCRESET,
+ M4SH_REG_POWER_VERSION,
+ M4SH_REG_POWER_DUMMY,
+ M4SH_REG_POWER_STILLMODETIMEOUT,
+ M4SH_REG_POWER_MOTIONDURATION,
+ M4SH_REG_POWER_MOTIONTHRESHOLD,
+ M4SH_REG_POWER_NOMOTIONDURATION,
+ M4SH_REG_POWER_NOMOTIONTHRESHOLD,
+ M4SH_REG_LOCATION_VERSION,
+ M4SH_REG_LOCATION_SOURCE,
+ M4SH_REG_LOCATION_SPEED,
+ M4SH_REG_LOCATION_LATITUDE,
+ M4SH_REG_LOCATION_LONGITUDE,
+ M4SH_REG_LOCATION_ALTITUDE,
+ M4SH_REG_DOWNLOAD_COMMAND,
+ M4SH_REG_DOWNLOAD_STATUS,
+ M4SH_REG_DOWNLOAD_SIZE,
+ M4SH_REG_DOWNLOAD_CHECKSUM,
+ M4SH_REG_DOWNLOAD_FILENAME,
+ M4SH_REG_DOWNLOAD_PACKET,
+ M4SH_REG_AUDIO_VERSION,
+ M4SH_REG_AUDIO_ENABLE,
+ M4SH_REG_AUDIO_DUMMY,
+ M4SH_REG_AUDIO_TOTALPACKETS,
+ M4SH_REG_TIMEPIECE_VERSION,
+ M4SH_REG_TIMEPIECE_ENABLE, /* 0: host control 1: sensorhub control */
+ M4SH_REG_TIMEPIECE_SAMPLERATE,
+ M4SH_REG_TIMEPIECE_OFFSETSTEPS,
+ M4SH_REG_WRIST_VERSION,
+ M4SH_REG_WRIST_ENABLE,
+ M4SH_REG_WRIST_INTERRUPTREASON,
+ M4SH_REG_WRIST_HOSTRESPONSE,
+ M4SH_REG_WRIST_FMONCHIP,
+ M4SH_REG_WRIST_FMONFILE,
+ M4SH_REG_GESTURE_VERSION,
+ M4SH_REG_GESTURE_GESTURE1,
+ M4SH_REG_GESTURE_CONFIDENCE1,
+ M4SH_REG_GESTURE_VALUE1,
+ M4SH_REG_GESTURE_GESTURE2,
+ M4SH_REG_GESTURE_CONFIDENCE2,
+ M4SH_REG_GESTURE_VALUE2,
+ M4SH_REG_GESTURE_GESTURE3,
+ M4SH_REG_GESTURE_CONFIDENCE3,
+ M4SH_REG_GESTURE_VALUE3,
+ M4SH_REG_PASSIVE_TIMESTAMP,
+ M4SH_REG_PASSIVE_STEPS,
+ M4SH_REG_PASSIVE_METS,
+ M4SH_REG_PASSIVE_FLOORSCLIMBED,
+ M4SH_REG__INVALID, /* Marker for invalid register */
+ M4SH_REG__NUM = M4SH_REG__INVALID /* Number of registers */
+};
diff --git a/include/linux/m4sensorhub/m4sensorhub_registers.h b/include/linux/m4sensorhub/m4sensorhub_registers.h
new file mode 100644
index 00000000000..f33bef2f66e
--- /dev/null
+++ b/include/linux/m4sensorhub/m4sensorhub_registers.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2012, Motorola, Inc. All Rights Reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __M4SENSORHUB_MEMMAP_H__
+#define __M4SENSORHUB_MEMMAP_H__
+
+#include "m4sensorhub_bank_enum.h"
+#include "m4sensorhub_reg_enum.h"
+
+#ifdef __KERNEL__
+
+/* Global (kernel) definitions */
+
+#define M4SH_MAX_REG_SIZE 2048
+#define M4SH_MAX_STACK_BUF_SIZE 32
+#define m4sh_no_mask NULL
+
+#endif /*__KERNEL__ */
+#endif /*__M4SENSORHUB_MEMMAP_H__ */
diff --git a/include/linux/m4sensorhub_client_ioctl.h b/include/linux/m4sensorhub_client_ioctl.h
new file mode 100644
index 00000000000..a6e2c455c4c
--- /dev/null
+++ b/include/linux/m4sensorhub_client_ioctl.h
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 2012, Motorola, Inc. All Rights Reserved.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __M4SENSORHUB_CLIENT_IOCTL_H__
+#define __M4SENSORHUB_CLIENT_IOCTL_H__
+
+/** The following define the IOCTL command values via the ioctl macros */
+
+#define M4_SENSOR_IOCTL_BASE 0xA2
+#define M4_SENSOR_IOCTL_GET_TEMPRATURE _IOR(M4_SENSOR_IOCTL_BASE, 01, int)
+#define M4_SENSOR_IOCTL_GET_PRESSURE _IOR(M4_SENSOR_IOCTL_BASE, 02, int)
+#define M4_SENSOR_IOCTL_GET_GYRO _IOR(M4_SENSOR_IOCTL_BASE, 03, int)
+#define M4_SENSOR_IOCTL_SET_DELAY _IOW(M4_SENSOR_IOCTL_BASE, 04, int)
+#define M4_SENSOR_IOCTL_APP_GET_FLAG _IOR(M4_SENSOR_IOCTL_BASE, 05, int)
+#define M4_SENSOR_IOCTL_APP_SET_FLAG _IOW(M4_SENSOR_IOCTL_BASE, 06, int)
+#define M4_SENSOR_IOCTL_GET_PEDOMETER _IOR(M4_SENSOR_IOCTL_BASE, 07, int)
+#define M4_SENSOR_IOCTL_GET_PASSIVE_DATA _IOR(M4_SENSOR_IOCTL_BASE, 10, int)
+#define M4_SENSOR_IOCTL_GET_SLEEP_DATA _IOR(M4_SENSOR_IOCTL_BASE, 11, int)
+#define M4_SENSOR_IOCTL_SET_POSIX_TIME _IOW(M4_SENSOR_IOCTL_BASE, 12, int)
+#define M4_SENSOR_IOCTL_SET_ACTIVE_MODE _IOW(M4_SENSOR_IOCTL_BASE, 13, int)
+#define M4_SENSOR_IOCTL_SET_PASSIVE_MODE _IOW(M4_SENSOR_IOCTL_BASE, 14, int)
+#define M4_SENSOR_IOCTL_SLEEP_ANALYSIS _IOW(M4_SENSOR_IOCTL_BASE, 15, int)
+#define M4_SENSOR_IOCTL_SET_EQUIPMENT_TYPE _IOW(M4_SENSOR_IOCTL_BASE, 16, int)
+#define M4_SENSOR_IOCTL_SET_MANUAL_CALIB_WALK_SPEED _IOW(M4_SENSOR_IOCTL_BASE, 17, int)
+#define M4_SENSOR_IOCTL_SET_MANUAL_CALIB_JOG_SPEED _IOW(M4_SENSOR_IOCTL_BASE, 20, int)
+#define M4_SENSOR_IOCTL_SET_MANUAL_CALIB_RUN_SPEED _IOW(M4_SENSOR_IOCTL_BASE, 21, int)
+#define M4_SENSOR_IOCTL_SET_MANUAL_CALIB_STATUS _IOW(M4_SENSOR_IOCTL_BASE, 22, int)
+#define M4_SENSOR_IOCTL_SET_USER_PROFILE _IOW(M4_SENSOR_IOCTL_BASE, 23, int)
+#define M4_SENSOR_IOCTL_SET_USER_DISTANCE _IOW(M4_SENSOR_IOCTL_BASE, 24, int)
+#define M4_SENSOR_IOCTL_SET_USER_CALIB_TABLE _IOW(M4_SENSOR_IOCTL_BASE, 25, int)
+#define M4_SENSOR_IOCTL_GET_MANUAL_CALIB_STATUS _IOR(M4_SENSOR_IOCTL_BASE, 26, int)
+#define M4_SENSOR_IOCTL_ERASE_CALIB _IOW(M4_SENSOR_IOCTL_BASE, 27, int)
+#define M4_SENSOR_IOCTL_SET_SCREEN_ON_GESTURE_STATUS _IOW(M4_SENSOR_IOCTL_BASE, 30, int)
+#define M4_SENSOR_IOCTL_SET_ALTITUDE _IOW(M4_SENSOR_IOCTL_BASE, 31, int)
+#define M4_SENSOR_IOCTL_GET_FUSION _IOR(M4_SENSOR_IOCTL_BASE, 32, int)
+#define M4_SENSOR_IOCTL_GET_COMPASS _IOR(M4_SENSOR_IOCTL_BASE, 33, int)
+#define M4_SENSOR_IOCTL_GET_ACCEL _IOR(M4_SENSOR_IOCTL_BASE, 34, int)
+#define M4_SENSOR_IOCTL_GYRO_SET_DELAY _IOW(M4_SENSOR_IOCTL_BASE, 35, int)
+#define M4_SENSOR_IOCTL_COMPASS_SET_DELAY _IOW(M4_SENSOR_IOCTL_BASE, 36, int)
+#define M4_SENSOR_IOCTL_ACCEL_SET_DELAY _IOW(M4_SENSOR_IOCTL_BASE, 37, int)
+#define M4_SENSOR_IOCTL_FUSION_SET_DELAY _IOW(M4_SENSOR_IOCTL_BASE, 40, int)
+#define M4_SENSOR_IOCTL_SET_SCREEN_STATUS _IOW(M4_SENSOR_IOCTL_BASE, 41, int)
+#define M4_SENSOR_IOCTL_DL_SEND_PACKET _IOW(M4_SENSOR_IOCTL_BASE, 42, int)
+#define M4_SENSOR_IOCTL_SET_TIMEZONE_OFFSET _IOW(M4_SENSOR_IOCTL_BASE, 43, int)
+#define M4_SENSOR_IOCTL_LOCK_CLOCKFACE _IOW(M4_SENSOR_IOCTL_BASE, 44, int)
+#define M4_SENSOR_IOCTL_SET_DAILYSTEP_OFFSET _IOW(M4_SENSOR_IOCTL_BASE, 45, int)
+
+#define M4_SENSOR_DL_MAX_FILE_SIZE 16
+#define M4_SENSOR_DL_MAX_PACKET_SIZE 2048
+
+enum M4_SENSOR_DL_CMDS {
+ M4_SENSOR_DL_CMD_GET_CHECKSUM,
+ M4_SENSOR_DL_CMD_OPEN_FILE,
+ M4_SENSOR_DL_CMD_WRITE_FILE,
+ M4_SENSOR_DL_CMD_CLOSE_FILE,
+ M4_SENSOR_DL_CMD_DELETE_FILE,
+};
+
+enum M4_SENSOR_DL_ERROR_CODES {
+ M4_SENSOR_DL_SUCCESS,
+ M4_SENSOR_DL_ERROR_GET_CHECKSUM,
+ M4_SENSOR_DL_ERROR_OPEN_FILE,
+ M4_SENSOR_DL_ERROR_WRITE_FILE,
+ M4_SENSOR_DL_ERROR_CLOSE_FILE,
+ M4_SENSOR_DL_ERROR_DELETE_FILE,
+ M4_SENSOR_DL_ERROR_INVALID_SIZE,
+ /*internal error between M4<==>Kernel*/
+ M4_SENSOR_DL_ERROR_SEND_CMD = 0x80,
+ M4_SENSOR_DL_ERROR_DATA_CHECKSUM,
+};
+
+#define M4_MAX_LOG_LEVEL 3
+#define get_log_level(loglevel, mask) ((loglevel >> mask) & 0x3)
+
+struct m4sh_user_profile {
+ unsigned char gender;
+ unsigned char age;
+ unsigned char height;
+ unsigned char weight;
+};
+
+struct m4sh_workout_data {
+ int msp_distance;
+ int user_distance;
+};
+
+struct m4sh_download_packet{
+ unsigned char command;
+ unsigned char status;
+ unsigned short size;
+ unsigned int checksum;
+ char filename[M4_SENSOR_DL_MAX_FILE_SIZE];
+ unsigned char buffer[M4_SENSOR_DL_MAX_PACKET_SIZE];
+};
+
+enum m4sh_log_level_mask {
+ GEN_MASK_BIT_1,
+ GEN_MASK_BIT_2,
+ TIMER_MASK_BIT_1,
+ TIMER_MASK_BIT_2,
+ ACCEL_MASK_BIT_1,
+ ACCEL_MASK_BIT_2,
+ TMP_MASK_BIT_1,
+ TMP_MASK_BIT_2,
+ BMP_MASK_BIT_1,
+ BMP_MASK_BIT_2,
+ PEDO_MASK_BIT_1,
+ PEDO_MASK_BIT_2,
+ TCMD_MASK_BIT_1,
+ TCMD_MASK_BIT_2,
+ GYRO_MASK_BIT_1,
+ GYRO_MASK_BIT_2,
+ COMPASS_MASK_BIT_1,
+ COMPASS_MASK_BIT_2,
+ FUSION_MASK_BIT_1,
+ FUSION_MASK_BIT_2,
+ METS_MASK_BIT_1,
+ METS_MASK_BIT_2,
+ GESTURE_MASK_BIT_1,
+ GESTURE_MASK_BIT_2,
+ POWER_MASK_BIT_1,
+ POWER_MASK_BIT_2,
+ CORRELATION_MASK_BIT_1,
+ CORRELATION_MASK_BIT_2,
+ GPS_MASK_BIT_1,
+ GPS_MASK_BIT_2,
+ DOWNLOAD_MASK_BIT_1,
+ DOWNLOAD_MASK_BIT_2,
+ AUDIO_MASK_BIT_1,
+ AUDIO_MASK_BIT_2,
+ DISPLAY_MASK_BIT_1,
+ DISPLAY_MASK_BIT_2,
+ WRIST_MASK_BIT_1,
+ WRIST_MASK_BIT_2,
+};
+
+#endif /* __M4SENSORHUB_CLIENT_IOCTL_H__ */
diff --git a/include/linux/m4sensorhub_gpio.h b/include/linux/m4sensorhub_gpio.h
new file mode 100644
index 00000000000..287acd98571
--- /dev/null
+++ b/include/linux/m4sensorhub_gpio.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013 Motorola Mobility, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#ifndef _M4SENSORHUB_GPIO_H_
+#define _M4SENSORHUB_GPIO_H_
+
+struct m4wrist_gpio_data {
+ int gpio_xres;
+ int gpio_clk;
+ int gpio_data;
+};
+
+#endif /* _M4SENSORHUB_GPIO_H_ */
diff --git a/include/linux/mfd/tps65912.h b/include/linux/mfd/tps65912.h
index 6d309032dc0..f456529d800 100644
--- a/include/linux/mfd/tps65912.h
+++ b/include/linux/mfd/tps65912.h
@@ -306,6 +306,7 @@ struct tps65912 {
int irq_base;
int irq_num;
u32 irq_mask;
+ void *debugfs_data;
};
struct tps65912_platform_data {
@@ -324,5 +325,7 @@ void tps65912_device_exit(struct tps65912 *tps65912);
int tps65912_irq_init(struct tps65912 *tps65912, int irq,
struct tps65912_platform_data *pdata);
int tps65912_irq_exit(struct tps65912 *tps65912);
+int tps65912_debugfs_create(struct tps65912 *tps65912);
+void tps65912_debugfs_remove(struct tps65912 *tps65912);
#endif /* __LINUX_MFD_TPS65912_H */
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index b508016fb76..0545b6ca961 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -276,9 +276,9 @@ struct pcmcia_device_id {
#define INPUT_DEVICE_ID_EV_MAX 0x1f
#define INPUT_DEVICE_ID_KEY_MIN_INTERESTING 0x71
#define INPUT_DEVICE_ID_KEY_MAX 0x2ff
-#define INPUT_DEVICE_ID_REL_MAX 0x0f
-#define INPUT_DEVICE_ID_ABS_MAX 0x3f
-#define INPUT_DEVICE_ID_MSC_MAX 0x07
+#define INPUT_DEVICE_ID_REL_MAX 0x1f
+#define INPUT_DEVICE_ID_ABS_MAX 0x7f
+#define INPUT_DEVICE_ID_MSC_MAX 0x1f
#define INPUT_DEVICE_ID_LED_MAX 0x0f
#define INPUT_DEVICE_ID_SND_MAX 0x07
#define INPUT_DEVICE_ID_FF_MAX 0x7f
diff --git a/include/linux/spi/cpcap-regbits.h b/include/linux/spi/cpcap-regbits.h
new file mode 100644
index 00000000000..bf3aa7e0d69
--- /dev/null
+++ b/include/linux/spi/cpcap-regbits.h
@@ -0,0 +1,957 @@
+#ifndef __CPCAP_REGBITS_H__
+#define __CPCAP_REGBITS_H__
+
+/*
+ * Copyright (C) 2007-2009 Motorola, 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+/*
+ * Register 0 - CPCAP_REG_INT_0 bits
+ */
+#define CPCAP_BIT_ID_GROUND_I 0x00008000
+#define CPCAP_BIT_ID_FLOAT_I 0x00004000
+#define CPCAP_BIT_CHRG_DET_I 0x00002000
+#define CPCAP_BIT_RVRS_CHRG_I 0x00001000
+#define CPCAP_BIT_VBUSOV_I 0x00000800
+#define CPCAP_BIT_MB2_I 0x00000400
+#define CPCAP_BIT_HS_I 0x00000200
+#define CPCAP_BIT_ADCDONE_I 0x00000100
+#define CPCAP_BIT_TS_I 0x00000080
+#define CPCAP_BIT_EOL_I 0x00000040
+#define CPCAP_BIT_LOWBPH_I 0x00000020
+#define CPCAP_BIT_SEC2PRI_I 0x00000010
+#define CPCAP_BIT_LOWBPL_I 0x00000008
+#define CPCAP_BIT_UNUSED_0_2_I 0x00000004
+#define CPCAP_BIT_PRIMAC_I 0x00000002
+#define CPCAP_BIT_HSCLK_I 0x00000001
+
+/*
+ * Register 1 - CPCAP_REG_INT_1 bits
+ */
+#define CPCAP_BIT_EXTMEMHD_I 0x00008000
+#define CPCAP_BIT_UART_ECHO_OVERRUN_I 0x00004000
+#define CPCAP_BIT_CHRG_SE1B_I 0x00002000
+#define CPCAP_BIT_SE0CONN_I 0x00001000
+#define CPCAP_BIT_PTT_I 0x00000800
+#define CPCAP_BIT_1HZ_I 0x00000400
+#define CPCAP_BIT_CLK_I 0x00000200
+#define CPCAP_BIT_ON2_I 0x00000100
+#define CPCAP_BIT_ON_I 0x00000080
+#define CPCAP_BIT_RVRS_MODE_I 0x00000040
+#define CPCAP_BIT_CHRGCURR2_I 0x00000020
+#define CPCAP_BIT_CHRGCURR1_I 0x00000010
+#define CPCAP_BIT_VBUSVLD_I 0x00000008
+#define CPCAP_BIT_SESSVLD_I 0x00000004
+#define CPCAP_BIT_SESSEND_I 0x00000002
+#define CPCAP_BIT_SE1_I 0x00000001
+
+/*
+ * Register 2 CPCAP_REG_INT_2 - bits
+ */
+#define CPCAP_BIT_USBDPLLCLK_I 0x00008000
+#define CPCAP_BIT_PWRGOOD_I 0x00004000
+#define CPCAP_BIT_UCRESET_I 0x00002000
+#define CPCAP_BIT_ONEWIRE3_I 0x00001000
+#define CPCAP_BIT_ONEWIRE2_I 0x00000800
+#define CPCAP_BIT_ONEWIRE1_I 0x00000400
+#define CPCAP_BIT_OPT_SEL_STATE_I 0x00000200
+#define CPCAP_BIT_OPT_SEL_DTCH_I 0x00000100
+#define CPCAP_BIT_TODA_I 0x00000080
+#define CPCAP_BIT_OFLOWSW_I 0x00000040
+#define CPCAP_BIT_PC_I 0x00000020
+#define CPCAP_BIT_DIETEMPH_I 0x00000010
+#define CPCAP_BIT_DIEPWRDWN_I 0x00000008
+#define CPCAP_BIT_SOFTRST_I 0x00000004
+#define CPCAP_BIT_SYSRSTRT_I 0x00000002
+#define CPCAP_BIT_WARM_I 0x00000001
+
+/*
+ * Register 3 - CPCAP_REG_INT_3 bits
+ */
+#define CPCAP_BIT_UNUSED_3_15_I 0x00008000
+#define CPCAP_BIT_UNUSED_3_14_I 0x00004000
+#define CPCAP_BIT_SPARE_3_13_I 0x00002000
+#define CPCAP_BIT_SPARE_3_12_I 0x00001000
+#define CPCAP_BIT_SPARE_3_11_I 0x00000800
+#define CPCAP_BIT_SPARE_3_10_I 0x00000400
+#define CPCAP_BIT_CC_CAL_I 0x00000200
+#define CPCAP_BIT_SECHALT_I 0x00000100
+#define CPCAP_BIT_PRIHALT_I 0x00000080
+#define CPCAP_BIT_BATTDETB_I 0x00000040
+#define CPCAP_BIT_SB_MAX_RETX_ERR_I 0x00000020
+#define CPCAP_BIT_GCAI_CURR2_I 0x00000010
+#define CPCAP_BIT_GCAI_CURR1_I 0x00000008
+#define CPCAP_BIT_UCBUSY_I 0x00000004
+#define CPCAP_BIT_DM_I 0x00000002
+#define CPCAP_BIT_DP_I 0x00000001
+
+/*
+ * Register 4 - CPCAP_REG_INTM1 bits
+ */
+#define CPCAP_BIT_ID_GROUND_M 0x00008000
+#define CPCAP_BIT_ID_FLOAT_M 0x00004000
+#define CPCAP_BIT_CHRG_DET_M 0x00002000
+#define CPCAP_BIT_RVRS_CHRG_M 0x00001000
+#define CPCAP_BIT_VBUSOV_M 0x00000800
+#define CPCAP_BIT_MB2_M 0x00000400
+#define CPCAP_BIT_HS_M 0x00000200
+#define CPCAP_BIT_ADCDONE_M 0x00000100
+#define CPCAP_BIT_TS_M 0x00000080
+#define CPCAP_BIT_EOL_M 0x00000040
+#define CPCAP_BIT_LOWBPH_M 0x00000020
+#define CPCAP_BIT_SEC2PRI_M 0x00000010
+#define CPCAP_BIT_LOWBPL_M 0x00000008
+#define CPCAP_BIT_UNUSED_4_2_M 0x00000004
+#define CPCAP_BIT_PRIMAC_M 0x00000002
+#define CPCAP_BIT_HSCLK_M 0x00000001
+
+/*
+ * Register 5 - CPCAP_REG_INTM2 bits
+ */
+#define CPCAP_BIT_EXTMEMHD_M 0x00008000
+#define CPCAP_BIT_UART_ECHO_OVERRUN_M 0x00004000
+#define CPCAP_BIT_CHRG_SE1B_M 0x00002000
+#define CPCAP_BIT_SE0CONN_M 0x00001000
+#define CPCAP_BIT_PTT_M 0x00000800
+#define CPCAP_BIT_1HZ_M 0x00000400
+#define CPCAP_BIT_CLK_M 0x00000200
+#define CPCAP_BIT_ON2_M 0x00000100
+#define CPCAP_BIT_ON_M 0x00000080
+#define CPCAP_BIT_RVRS_MODE_M 0x00000040
+#define CPCAP_BIT_CHRGCURR2_M 0x00000020
+#define CPCAP_BIT_CHRGCURR1_M 0x00000010
+#define CPCAP_BIT_VBUSVLD_M 0x00000008
+#define CPCAP_BIT_SESSVLD_M 0x00000004
+#define CPCAP_BIT_SESSEND_M 0x00000002
+#define CPCAP_BIT_SE1_M 0x00000001
+
+/*
+ * Register 6 - CPCAP_REG_INTM3 bits
+ */
+#define CPCAP_BIT_USBDPLLCLK_M 0x00008000
+#define CPCAP_BIT_PWRGOOD_M 0x00004000
+#define CPCAP_BIT_UCRESET_M 0x00002000
+#define CPCAP_BIT_ONEWIRE3_M 0x00001000
+#define CPCAP_BIT_ONEWIRE2_M 0x00000800
+#define CPCAP_BIT_ONEWIRE1_M 0x00000400
+#define CPCAP_BIT_OPT_SEL_STATE_M 0x00000200
+#define CPCAP_BIT_OPT_SEL_DTCH_M 0x00000100
+#define CPCAP_BIT_TODA_M 0x00000080
+#define CPCAP_BIT_OFLOWSW_M 0x00000040
+#define CPCAP_BIT_PC_M 0x00000020
+#define CPCAP_BIT_DIETEMPH_M 0x00000010
+#define CPCAP_BIT_DIEPWRDWN_M 0x00000008
+#define CPCAP_BIT_SOFTRST_M 0x00000004
+#define CPCAP_BIT_SYSRSTRT_M 0x00000002
+#define CPCAP_BIT_WARM_M 0x00000001
+
+/*
+ * Register 7 - CPCAP_REG_INTM4 bits
+ */
+#define CPCAP_BIT_UNUSED_7_15_M 0x00008000
+#define CPCAP_BIT_UNUSED_7_14_M 0x00004000
+#define CPCAP_BIT_SPARE_7_13_M 0x00002000
+#define CPCAP_BIT_SPARE_7_12_M 0x00001000
+#define CPCAP_BIT_SPARE_7_11_M 0x00000800
+#define CPCAP_BIT_SPARE_7_10_M 0x00000400
+#define CPCAP_BIT_CC_CAL_M 0x00000200
+#define CPCAP_BIT_SECHALT_M 0x00000100
+#define CPCAP_BIT_PRIHALT_M 0x00000080
+#define CPCAP_BIT_BATTDETB_M 0x00000040
+#define CPCAP_BIT_SB_MAX_RETX_ERR_M 0x00000020
+#define CPCAP_BIT_GCAI_CURR2_M 0x00000010
+#define CPCAP_BIT_GCAI_CURR1_M 0x00000008
+#define CPCAP_BIT_UCBUSY_M 0x00000004
+#define CPCAP_BIT_DM_M 0x00000002
+#define CPCAP_BIT_DP_M 0x00000001
+
+/*
+ * Register 8 - CPCAP_REG_INTS1 bits
+ */
+#define CPCAP_BIT_ID_GROUND_S 0x00008000
+#define CPCAP_BIT_ID_FLOAT_S 0x00004000
+#define CPCAP_BIT_CHRG_DET_S 0x00002000
+#define CPCAP_BIT_RVRS_CHRG_S 0x00001000
+#define CPCAP_BIT_VBUSOV_S 0x00000800
+#define CPCAP_BIT_MB2_S 0x00000400
+#define CPCAP_BIT_HS_S 0x00000200
+#define CPCAP_BIT_ADCDONE_S 0x00000100
+#define CPCAP_BIT_TS_S 0x00000080
+#define CPCAP_BIT_EOL_S 0x00000040
+#define CPCAP_BIT_LOWBPH_S 0x00000020
+#define CPCAP_BIT_SEC2PRI_S 0x00000010
+#define CPCAP_BIT_LOWBPL_S 0x00000008
+#define CPCAP_BIT_UNUSED_8_2_S 0x00000004
+#define CPCAP_BIT_PRIMAC_S 0x00000002
+#define CPCAP_BIT_HSCLK_S 0x00000001
+
+/*
+ * Register 9 - CPCAP_REG_INTS2 bits
+ */
+#define CPCAP_BIT_EXTMEMHD_S 0x00008000
+#define CPCAP_BIT_UART_ECHO_OVERRUN_S 0x00004000
+#define CPCAP_BIT_CHRG_SE1B_S 0x00002000
+#define CPCAP_BIT_SE0CONN_S 0x00001000
+#define CPCAP_BIT_PTT_S 0x00000800
+#define CPCAP_BIT_1HZ_S 0x00000400
+#define CPCAP_BIT_CLK_S 0x00000200
+#define CPCAP_BIT_ON2_S 0x00000100
+#define CPCAP_BIT_ON_S 0x00000080
+#define CPCAP_BIT_RVRS_MODE_S 0x00000040
+#define CPCAP_BIT_CHRGCURR2_S 0x00000020
+#define CPCAP_BIT_CHRGCURR1_S 0x00000010
+#define CPCAP_BIT_VBUSVLD_S 0x00000008
+#define CPCAP_BIT_SESSVLD_S 0x00000004
+#define CPCAP_BIT_SESSEND_S 0x00000002
+#define CPCAP_BIT_SE1_S 0x00000001
+
+/*
+ * Register 10 - CPCAP_REG_INTS3 bits
+ */
+#define CPCAP_BIT_USBDPLLCLK_S 0x00008000
+#define CPCAP_BIT_PWRGOOD_S 0x00004000
+#define CPCAP_BIT_UCRESET_S 0x00002000
+#define CPCAP_BIT_ONEWIRE3_S 0x00001000
+#define CPCAP_BIT_ONEWIRE2_S 0x00000800
+#define CPCAP_BIT_ONEWIRE1_S 0x00000400
+#define CPCAP_BIT_OPT_SEL_STATE_S 0x00000200
+#define CPCAP_BIT_OPT_SEL_DTCH_S 0x00000100
+#define CPCAP_BIT_TODA_S 0x00000080
+#define CPCAP_BIT_OFLOWSW_S 0x00000040
+#define CPCAP_BIT_PC_S 0x00000020
+#define CPCAP_BIT_DIETEMPH_S 0x00000010
+#define CPCAP_BIT_DIEPWRDWN_S 0x00000008
+#define CPCAP_BIT_SOFTRST_S 0x00000004
+#define CPCAP_BIT_SYSRSTRT_S 0x00000002
+#define CPCAP_BIT_WARM_S 0x00000001
+
+/*
+ * Register 11 - CPCAP_REG_INTS4 bits
+ */
+#define CPCAP_BIT_UNUSED_11_15_S 0x00008000
+#define CPCAP_BIT_UNUSED_11_14_S 0x00004000
+#define CPCAP_BIT_SPARE_11_13_S 0x00002000
+#define CPCAP_BIT_SPARE_11_12_S 0x00001000
+#define CPCAP_BIT_SPARE_11_11_S 0x00000800
+#define CPCAP_BIT_SPARE_11_10_S 0x00000400
+#define CPCAP_BIT_CC_CAL_S 0x00000200
+#define CPCAP_BIT_SECHALT_S 0x00000100
+#define CPCAP_BIT_PRIHALT_S 0x00000080
+#define CPCAP_BIT_BATTDETB_S 0x00000040
+#define CPCAP_BIT_SB_MAX_RETX_ERR_S 0x00000020
+#define CPCAP_BIT_GCAI_CURR2_S 0x00000010
+#define CPCAP_BIT_GCAI_CURR1_S 0x00000008
+#define CPCAP_BIT_UCBUSY_S 0x00000004
+#define CPCAP_BIT_DM_S 0x00000002
+#define CPCAP_BIT_DP_S 0x00000001
+
+/*
+ * Register 128 - CPCAP_REG_MI1 bits
+ */
+#define CPCAP_BIT_PRIMACRO_15_S 0x00008000
+#define CPCAP_BIT_PRIMACRO_14_S 0x00004000
+#define CPCAP_BIT_PRIMACRO_13_S 0x00002000
+#define CPCAP_BIT_PRIMACRO_12_S 0x00001000
+#define CPCAP_BIT_PRIMACRO_11_S 0x00000800
+#define CPCAP_BIT_PRIMACRO_10_S 0x00000400
+#define CPCAP_BIT_PRIMACRO_9_S 0x00000200
+#define CPCAP_BIT_PRIMACRO_8_S 0x00000100
+#define CPCAP_BIT_PRIMACRO_7_S 0x00000080
+#define CPCAP_BIT_PRIMACRO_6_S 0x00000040
+#define CPCAP_BIT_PRIMACRO_5_S 0x00000020
+#define CPCAP_BIT_PRIMACRO_4_S 0x00000010
+#define CPCAP_BIT_USEROFF_S 0x00000008
+#define CPCAP_BIT_PRIRAMR_S 0x00000004
+#define CPCAP_BIT_PRIRAMW_S 0x00000002
+#define CPCAP_BIT_PRIROMR_S 0x00000001
+
+/*
+ * Register 129 - CPCAP_REG_MIM1 bits
+ */
+#define CPCAP_BIT_PRIMACRO_15M 0x00008000
+#define CPCAP_BIT_PRIMACRO_14M 0x00004000
+#define CPCAP_BIT_PRIMACRO_13M 0x00002000
+#define CPCAP_BIT_PRIMACRO_12M 0x00001000
+#define CPCAP_BIT_PRIMACRO_11M 0x00000800
+#define CPCAP_BIT_PRIMACRO_10M 0x00000400
+#define CPCAP_BIT_PRIMACRO_9M 0x00000200
+#define CPCAP_BIT_PRIMACRO_8M 0x00000100
+#define CPCAP_BIT_PRIMACRO_7M 0x00000080
+#define CPCAP_BIT_PRIMACRO_6M 0x00000040
+#define CPCAP_BIT_PRIMACRO_5M 0x00000020
+#define CPCAP_BIT_PRIMACRO_4M 0x00000010
+#define CPCAP_BIT_USEROFFM 0x00000008
+#define CPCAP_BIT_PRIRAMRM 0x00000004
+#define CPCAP_BIT_PRIRAMWM 0x00000002
+#define CPCAP_BIT_PRIROMRM 0x00000001
+
+/*
+ * Register 130 - CPCAP_REG_MI2 bits
+ */
+#define CPCAP_BIT_PRIMACRO_15 0x00008000
+#define CPCAP_BIT_PRIMACRO_14 0x00004000
+#define CPCAP_BIT_PRIMACRO_13 0x00002000
+#define CPCAP_BIT_PRIMACRO_12 0x00001000
+#define CPCAP_BIT_PRIMACRO_11 0x00000800
+#define CPCAP_BIT_PRIMACRO_10 0x00000400
+#define CPCAP_BIT_PRIMACRO_9 0x00000200
+#define CPCAP_BIT_PRIMACRO_8 0x00000100
+#define CPCAP_BIT_PRIMACRO_7 0x00000080
+#define CPCAP_BIT_PRIMACRO_6 0x00000040
+#define CPCAP_BIT_PRIMACRO_5 0x00000020
+#define CPCAP_BIT_PRIMACRO_4 0x00000010
+#define CPCAP_BIT_USEROFF 0x00000008
+#define CPCAP_BIT_PRIRAMR 0x00000004
+#define CPCAP_BIT_PRIRAMW 0x00000002
+#define CPCAP_BIT_PRIROMR 0x00000001
+
+/*
+ * Register 131 - CPCAP_REG_MIM2 bits
+ */
+#define CPCAP_BIT_PRIMACRO_15S 0x00008000
+#define CPCAP_BIT_PRIMACRO_14S 0x00004000
+#define CPCAP_BIT_PRIMACRO_13S 0x00002000
+#define CPCAP_BIT_PRIMACRO_12S 0x00001000
+#define CPCAP_BIT_PRIMACRO_11S 0x00000800
+#define CPCAP_BIT_PRIMACRO_10S 0x00000400
+#define CPCAP_BIT_PRIMACRO_9S 0x00000200
+#define CPCAP_BIT_PRIMACRO_8S 0x00000100
+#define CPCAP_BIT_PRIMACRO_7S 0x00000080
+#define CPCAP_BIT_PRIMACRO_6S 0x00000040
+#define CPCAP_BIT_PRIMACRO_5S 0x00000020
+#define CPCAP_BIT_PRIMACRO_4S 0x00000010
+#define CPCAP_BIT_USEROFFS 0x00000008
+#define CPCAP_BIT_PRIRAMRS 0x00000004
+#define CPCAP_BIT_PRIRAMWS 0x00000002
+#define CPCAP_BIT_PRIROMRS 0x00000001
+
+/*
+ * Register 132 - CPCAP_REG_UCC1 bits
+ */
+#define CPCAP_BIT_UNUSED_132_15 0x00008000
+#define CPCAP_BIT_UNUSED_132_14 0x00004000
+#define CPCAP_BIT_UNUSED_132_13 0x00002000
+#define CPCAP_BIT_UNUSED_132_12 0x00001000
+#define CPCAP_BIT_PRI_GPIO6_2MAC10 0x00000800
+#define CPCAP_BIT_PRI_GPIO5_2MAC9 0x00000400
+#define CPCAP_BIT_PRI_GPIO4_2MAC8 0x00000200
+#define CPCAP_BIT_PRI_GPIO3_2MAC7 0x00000100
+#define CPCAP_BIT_PRI_GPIO2_2MAC6 0x00000080
+#define CPCAP_BIT_PRI_GPIO1_2MAC5 0x00000040
+#define CPCAP_BIT_PRI_GPIO0_2MAC4 0x00000020
+#define CPCAP_BIT_USEROFFCLK 0x00000010
+#define CPCAP_BIT_UO_MH_PFM_EN 0x00000008
+#define CPCAP_BIT_CNTRLSEC 0x00000004
+#define CPCAP_BIT_SCHDOVERRIDE 0x00000002
+#define CPCAP_BIT_PRIHALT 0x00000001
+
+/*
+ * Register 135 - CPCAP_REG_PC1 bits
+ */
+#define CPCAP_BIT_UNUSED_135_15 0x00008000
+#define CPCAP_BIT_UNUSED_135_14 0x00004000
+#define CPCAP_BIT_UNUSED_135_13 0x00002000
+#define CPCAP_BIT_UNUSED_135_12 0x00001000
+#define CPCAP_BIT_UNUSED_135_11 0x00000800
+#define CPCAP_BIT_UNUSED_135_10 0x00000400
+#define CPCAP_BIT_PC1_SC_SHTDWN_EN 0x00000200
+#define CPCAP_BIT_PC1_PCEN 0x00000100
+#define CPCAP_BIT_PC1_PCT7 0x00000080
+#define CPCAP_BIT_PC1_PCT6 0x00000040
+#define CPCAP_BIT_PC1_PCT5 0x00000020
+#define CPCAP_BIT_PC1_PCT4 0x00000010
+#define CPCAP_BIT_PC1_PCT3 0x00000008
+#define CPCAP_BIT_PC1_PCT2 0x00000004
+#define CPCAP_BIT_PC1_PCT1 0x00000002
+#define CPCAP_BIT_PC1_PCT0 0x00000001
+
+/*
+ * Register 138 - CPCAP_REG_PGC bits
+ */
+#define CPCAP_BIT_UNUSED_138_15 0x00008000
+#define CPCAP_BIT_UNUSED_138_14 0x00004000
+#define CPCAP_BIT_UNUSED_138_13 0x00002000
+#define CPCAP_BIT_UNUSED_138_12 0x00001000
+#define CPCAP_BIT_UNUSED_138_11 0x00000800
+#define CPCAP_BIT_UNUSED_138_10 0x00000400
+#define CPCAP_BIT_UNUSED_138_9 0x00000200
+#define CPCAP_BIT_REVENINV 0x00000100
+#define CPCAP_BIT_PRISTBYINV 0x00000080
+#define CPCAP_BIT_SYS_RST_MODE 0x00000040
+#define CPCAP_BIT_MAC_TIME_LONG 0x00000020
+#define CPCAP_BIT_PRI_UC_SUSPEND 0x00000010
+#define CPCAP_BIT_PRIWARMSTART 0x00000008
+#define CPCAP_BIT_PRIPRESVRAM 0x00000004
+#define CPCAP_BIT_SPI_PWRGT1EN 0x00000002
+#define CPCAP_BIT_SPI_PWRGT2EN 0x00000001
+
+/*
+ * Register 259 - CPCAP_REG_UCTM bits */
+#define CPCAP_BIT_UNUSED_259_15 0x00008000
+#define CPCAP_BIT_UNUSED_259_14 0x00004000
+#define CPCAP_BIT_UNUSED_259_13 0x00002000
+#define CPCAP_BIT_UNUSED_259_12 0x00001000
+#define CPCAP_BIT_UNUSED_259_11 0x00000800
+#define CPCAP_BIT_UNUSED_259_10 0x00000400
+#define CPCAP_BIT_UNUSED_259_9 0x00000200
+#define CPCAP_BIT_UNUSED_259_8 0x00000100
+#define CPCAP_BIT_UNUSED_259_7 0x00000080
+#define CPCAP_BIT_UNUSED_259_6 0x00000040
+#define CPCAP_BIT_UNUSED_259_5 0x00000020
+#define CPCAP_BIT_UNUSED_259_4 0x00000010
+#define CPCAP_BIT_UNUSED_259_3 0x00000008
+#define CPCAP_BIT_UNUSED_259_2 0x00000004
+#define CPCAP_BIT_UNUSED_259_1 0x00000002
+#define CPCAP_BIT_UCTM 0x00000001
+
+/*
+ * Register 266 - CPCAP_REG_VAL1 bits
+ */
+#define CPCAP_BIT_INVM_MC_MODE 0x00008000
+#define CPCAP_BIT_NVFLASH_MODE 0x00004000
+#define CPCAP_BIT_RECOVERY_MODE 0x00002000
+#define CPCAP_BIT_FASTBOOT_MODE 0x00001000
+#define CPCAP_BIT_BOOT_MODE 0x00000800
+#define CPCAP_BIT_BP2_ONLY_FLASH 0x00000400
+#define CPCAP_BIT_OUT_CHARGE_ONLY 0x00000200
+#define CPCAP_BIT_USB_BATT_RECOVERY 0x00000100
+#define CPCAP_BIT_PANIC 0x00000080
+#define CPCAP_BIT_BP_ONLY_FLASH 0x00000040
+#define CPCAP_BIT_WATCHDOG_RESET 0x00000020
+#define CPCAP_BIT_SOFT_RESET 0x00000010
+#define CPCAP_BIT_FLASH_FAIL 0x00000008
+#define CPCAP_BIT_FOTA_MODE 0x00000004
+#define CPCAP_BIT_AP_KERNEL_PANIC 0x00000002
+#define CPCAP_BIT_FLASH_MODE 0x00000001
+
+/*
+ * Register 385 - CPCAP_REG_SI2CC1
+ */
+#define CPCAP_BIT_CLK3M2_GATE_OVERRIDE 0x00000080
+
+/*
+ * Register 391 - CPCAP_REG_S3C
+ */
+#define CPCAP_BIT_SW3STBY 0x00000100
+
+/*
+ * Register 411 - CPCAP_REG_VUSB bits
+ */
+#define CPCAP_BIT_UNUSED_411_15 0x00008000
+#define CPCAP_BIT_UNUSED_411_14 0x00004000
+#define CPCAP_BIT_UNUSED_411_13 0x00002000
+#define CPCAP_BIT_UNUSED_411_12 0x00001000
+#define CPCAP_BIT_UNUSED_411_11 0x00000800
+#define CPCAP_BIT_UNUSED_411_10 0x00000400
+#define CPCAP_BIT_UNUSED_411_9 0x00000200
+#define CPCAP_BIT_VUSBSTBY 0x00000100
+#define CPCAP_BIT_UNUSED_411_7 0x00000080
+#define CPCAP_BIT_VUSB 0x00000040
+#define CPCAP_BIT_UNUSED_411_5 0x00000020
+#define CPCAP_BIT_VUSB_MODE2 0x00000010
+#define CPCAP_BIT_VUSB_MODE1 0x00000008
+#define CPCAP_BIT_VUSB_MODE0 0x00000004
+#define CPCAP_BIT_SPARE_411_1 0x00000002
+#define CPCAP_BIT_VBUS_SWITCH 0x00000001
+/*
+ * Register 512 - Audio Regulator and Bias Voltage
+ */
+
+#define CPCAP_BIT_AUDIO_LOW_PWR 0x00000040
+#define CPCAP_BIT_AUD_LOWPWR_SPEED 0x00000020
+#define CPCAP_BIT_VAUDIOPRISTBY 0x00000010
+#define CPCAP_BIT_VAUDIO_MODE1 0x00000004
+#define CPCAP_BIT_VAUDIO_MODE0 0x00000002
+#define CPCAP_BIT_V_AUDIO_EN 0x00000001
+
+/*
+ * Register 513 CODEC
+ */
+
+#define CPCAP_BIT_CDC_CLK2 0x00008000
+#define CPCAP_BIT_CDC_CLK1 0x00004000
+#define CPCAP_BIT_CDC_CLK0 0x00002000
+#define CPCAP_BIT_CDC_SR3 0x00001000
+#define CPCAP_BIT_CDC_SR2 0x00000800
+#define CPCAP_BIT_CDC_SR1 0x00000400
+#define CPCAP_BIT_CDC_SR0 0x00000200
+#define CPCAP_BIT_CDC_CLOCK_TREE_RESET 0x00000100
+#define CPCAP_BIT_MIC2_CDC_EN 0x00000080
+#define CPCAP_BIT_CDC_EN_RX 0x00000040
+#define CPCAP_BIT_DF_RESET 0x00000020
+#define CPCAP_BIT_MIC1_CDC_EN 0x00000010
+#define CPCAP_BIT_AUDOHPF_1 0x00000008
+#define CPCAP_BIT_AUDOHPF_0 0x00000004
+#define CPCAP_BIT_AUDIHPF_1 0x00000002
+#define CPCAP_BIT_AUDIHPF_0 0x00000001
+
+/*
+ * Register 514 CODEC Digital Audio Interface
+ */
+
+#define CPCAP_BIT_CDC_PLL_SEL 0x00008000
+#define CPCAP_BIT_CLK_IN_SEL 0x00002000
+#define CPCAP_BIT_DIG_AUD_IN 0x00001000
+#define CPCAP_BIT_CDC_CLK_EN 0x00000800
+#define CPCAP_BIT_CDC_DIG_AUD_FS1 0x00000400
+#define CPCAP_BIT_CDC_DIG_AUD_FS0 0x00000200
+#define CPCAP_BIT_MIC2_TIMESLOT2 0x00000100
+#define CPCAP_BIT_MIC2_TIMESLOT1 0x00000080
+#define CPCAP_BIT_MIC2_TIMESLOT0 0x00000040
+#define CPCAP_BIT_MIC1_RX_TIMESLOT2 0x00000020
+#define CPCAP_BIT_MIC1_RX_TIMESLOT1 0x00000010
+#define CPCAP_BIT_MIC1_RX_TIMESLOT0 0x00000008
+#define CPCAP_BIT_FS_INV 0x00000004
+#define CPCAP_BIT_CLK_INV 0x00000002
+#define CPCAP_BIT_SMB_CDC 0x00000001
+
+/*
+ * Register 515 Stereo DAC
+ */
+
+#define CPCAP_BIT_FSYNC_CLK_IN_COMMON 0x00000800
+#define CPCAP_BIT_SLAVE_PLL_CLK_INPUT 0x00000400
+#define CPCAP_BIT_ST_CLOCK_TREE_RESET 0x00000200
+#define CPCAP_BIT_DF_RESET_ST_DAC 0x00000100
+#define CPCAP_BIT_ST_SR3 0x00000080
+#define CPCAP_BIT_ST_SR2 0x00000040
+#define CPCAP_BIT_ST_SR1 0x00000020
+#define CPCAP_BIT_ST_SR0 0x00000010
+#define CPCAP_BIT_ST_DAC_CLK2 0x00000008
+#define CPCAP_BIT_ST_DAC_CLK1 0x00000004
+#define CPCAP_BIT_ST_DAC_CLK0 0x00000002
+#define CPCAP_BIT_ST_DAC_EN 0x00000001
+
+/*
+ * Register 516 Stereo DAC Digital Audio Interface
+ */
+
+#define CPCAP_BIT_ST_L_TIMESLOT2 0x00002000
+#define CPCAP_BIT_ST_L_TIMESLOT1 0x00001000
+#define CPCAP_BIT_ST_L_TIMESLOT0 0x00000800
+#define CPCAP_BIT_ST_R_TIMESLOT2 0x00000400
+#define CPCAP_BIT_ST_R_TIMESLOT1 0x00000200
+#define CPCAP_BIT_ST_R_TIMESLOT0 0x00000100
+#define CPCAP_BIT_ST_DAC_CLK_IN_SEL 0x00000080
+#define CPCAP_BIT_ST_FS_INV 0x00000040
+#define CPCAP_BIT_ST_CLK_INV 0x00000020
+#define CPCAP_BIT_ST_DIG_AUD_FS1 0x00000010
+#define CPCAP_BIT_ST_DIG_AUD_FS0 0x00000008
+#define CPCAP_BIT_DIG_AUD_IN_ST_DAC 0x00000004
+#define CPCAP_BIT_ST_CLK_EN 0x00000002
+#define CPCAP_BIT_SMB_ST_DAC 0x00000001
+
+/*
+ * Register 517 - CPCAP_REG_TXI bits
+ */
+#define CPCAP_BIT_PTT_TH 0x00008000
+#define CPCAP_BIT_PTT_CMP_EN 0x00004000
+#define CPCAP_BIT_HS_ID_TX 0x00002000
+#define CPCAP_BIT_MB_ON2 0x00001000
+#define CPCAP_BIT_MB_ON1L 0x00000800
+#define CPCAP_BIT_MB_ON1R 0x00000400
+#define CPCAP_BIT_RX_L_ENCODE 0x00000200
+#define CPCAP_BIT_RX_R_ENCODE 0x00000100
+#define CPCAP_BIT_MIC2_MUX 0x00000080
+#define CPCAP_BIT_MIC2_PGA_EN 0x00000040
+#define CPCAP_BIT_CDET_DIS 0x00000020
+#define CPCAP_BIT_EMU_MIC_MUX 0x00000010
+#define CPCAP_BIT_HS_MIC_MUX 0x00000008
+#define CPCAP_BIT_MIC1_MUX 0x00000004
+#define CPCAP_BIT_MIC1_PGA_EN 0x00000002
+#define CPCAP_BIT_DLM 0x00000001
+
+/*
+ * Register 518 MIC PGA's
+ */
+#define CPCAP_BIT_MB_BIAS_R1 0x00000800
+#define CPCAP_BIT_MB_BIAS_R0 0x00000400
+#define CPCAP_BIT_MIC2_GAIN_4 0x00000200
+#define CPCAP_BIT_MIC2_GAIN_3 0x00000100
+#define CPCAP_BIT_MIC2_GAIN_2 0x00000080
+#define CPCAP_BIT_MIC2_GAIN_1 0x00000040
+#define CPCAP_BIT_MIC2_GAIN_0 0x00000020
+#define CPCAP_BIT_MIC1_GAIN_4 0x00000010
+#define CPCAP_BIT_MIC1_GAIN_3 0x00000008
+#define CPCAP_BIT_MIC1_GAIN_2 0x00000004
+#define CPCAP_BIT_MIC1_GAIN_1 0x00000002
+#define CPCAP_BIT_MIC1_GAIN_0 0x00000001
+
+/*
+ * Register 519 - CPCAP_REG_RXOA bits
+ */
+#define CPCAP_BIT_UNUSED_519_15 0x00008000
+#define CPCAP_BIT_UNUSED_519_14 0x00004000
+#define CPCAP_BIT_UNUSED_519_13 0x00002000
+#define CPCAP_BIT_STDAC_LOW_PWR_DISABLE 0x00001000
+#define CPCAP_BIT_HS_LOW_PWR 0x00000800
+#define CPCAP_BIT_HS_ID_RX 0x00000400
+#define CPCAP_BIT_ST_HS_CP_EN 0x00000200
+#define CPCAP_BIT_EMU_SPKR_R_EN 0x00000100
+#define CPCAP_BIT_EMU_SPKR_L_EN 0x00000080
+#define CPCAP_BIT_HS_L_EN 0x00000040
+#define CPCAP_BIT_HS_R_EN 0x00000020
+#define CPCAP_BIT_A4_LINEOUT_L_EN 0x00000010
+#define CPCAP_BIT_A4_LINEOUT_R_EN 0x00000008
+#define CPCAP_BIT_A2_LDSP_L_EN 0x00000004
+#define CPCAP_BIT_A2_LDSP_R_EN 0x00000002
+#define CPCAP_BIT_A1_EAR_EN 0x00000001
+
+/*
+ * Register 520 RX Volume Control
+ */
+#define CPCAP_BIT_VOL_EXT3 0x00008000
+#define CPCAP_BIT_VOL_EXT2 0x00004000
+#define CPCAP_BIT_VOL_EXT1 0x00002000
+#define CPCAP_BIT_VOL_EXT0 0x00001000
+#define CPCAP_BIT_VOL_DAC3 0x00000800
+#define CPCAP_BIT_VOL_DAC2 0x00000400
+#define CPCAP_BIT_VOL_DAC1 0x00000200
+#define CPCAP_BIT_VOL_DAC0 0x00000100
+#define CPCAP_BIT_VOL_DAC_LSB_1dB1 0x00000080
+#define CPCAP_BIT_VOL_DAC_LSB_1dB0 0x00000040
+#define CPCAP_BIT_VOL_CDC3 0x00000020
+#define CPCAP_BIT_VOL_CDC2 0x00000010
+#define CPCAP_BIT_VOL_CDC1 0x00000008
+#define CPCAP_BIT_VOL_CDC0 0x00000004
+#define CPCAP_BIT_VOL_CDC_LSB_1dB1 0x00000002
+#define CPCAP_BIT_VOL_CDC_LSB_1dB0 0x00000001
+
+/*
+ * Register 521 Codec to Output Amp Switches
+ */
+#define CPCAP_BIT_PGA_CDC_EN 0x00000400
+#define CPCAP_BIT_CDC_SW 0x00000200
+#define CPCAP_BIT_PGA_OUTR_USBDP_CDC_SW 0x00000100
+#define CPCAP_BIT_PGA_OUTL_USBDN_CDC_SW 0x00000080
+#define CPCAP_BIT_ALEFT_HS_CDC_SW 0x00000040
+#define CPCAP_BIT_ARIGHT_HS_CDC_SW 0x00000020
+#define CPCAP_BIT_A4_LINEOUT_L_CDC_SW 0x00000010
+#define CPCAP_BIT_A4_LINEOUT_R_CDC_SW 0x00000008
+#define CPCAP_BIT_A2_LDSP_L_CDC_SW 0x00000004
+#define CPCAP_BIT_A2_LDSP_R_CDC_SW 0x00000002
+#define CPCAP_BIT_A1_EAR_CDC_SW 0x00000001
+
+/*
+ * Register 522 RX Stereo DAC to Output Amp Switches
+ */
+#define CPCAP_BIT_PGA_DAC_EN 0x00001000
+#define CPCAP_BIT_ST_DAC_SW 0x00000800
+#define CPCAP_BIT_MONO_DAC1 0x00000400
+#define CPCAP_BIT_MONO_DAC0 0x00000200
+#define CPCAP_BIT_PGA_OUTR_USBDP_DAC_SW 0x00000100
+#define CPCAP_BIT_PGA_OUTL_USBDN_DAC_SW 0x00000080
+#define CPCAP_BIT_ALEFT_HS_DAC_SW 0x00000040
+#define CPCAP_BIT_ARIGHT_HS_DAC_SW 0x00000020
+#define CPCAP_BIT_A4_LINEOUT_L_DAC_SW 0x00000010
+#define CPCAP_BIT_A4_LINEOUT_R_DAC_SW 0x00000008
+#define CPCAP_BIT_A2_LDSP_L_DAC_SW 0x00000004
+#define CPCAP_BIT_A2_LDSP_R_DAC_SW 0x00000002
+#define CPCAP_BIT_A1_EAR_DAC_SW 0x00000001
+
+/*
+ * Register 523 RX External PGA to Output Amp Switches
+ */
+#define CPCAP_BIT_PGA_EXT_L_EN 0x00004000
+#define CPCAP_BIT_PGA_EXT_R_EN 0x00002000
+#define CPCAP_BIT_PGA_IN_L_SW 0x00001000
+#define CPCAP_BIT_PGA_IN_R_SW 0x00000800
+#define CPCAP_BIT_MONO_EXT1 0x00000400
+#define CPCAP_BIT_MONO_EXT0 0x00000200
+#define CPCAP_BIT_PGA_OUTR_USBDP_EXT_SW 0x00000100
+#define CPCAP_BIT_PGA_OUTL_USBDN_EXT_SW 0x00000080
+#define CPCAP_BIT_ALEFT_HS_EXT_SW 0x00000040
+#define CPCAP_BIT_ARIGHT_HS_EXT_SW 0x00000020
+#define CPCAP_BIT_A4_LINEOUT_L_EXT_SW 0x00000010
+#define CPCAP_BIT_A4_LINEOUT_R_EXT_SW 0x00000008
+#define CPCAP_BIT_A2_LDSP_L_EXT_SW 0x00000004
+#define CPCAP_BIT_A2_LDSP_R_EXT_SW 0x00000002
+#define CPCAP_BIT_A1_EAR_EXT_SW 0x00000001
+
+/*
+ * Register 525 Loudspeaker Amplifier and Clock Configuration for Headset
+ */
+#define CPCAP_BIT_NCP_CLK_SYNC 0x00000080
+#define CPCAP_BIT_A2_CLK_SYNC 0x00000040
+#define CPCAP_BIT_A2_FREE_RUN 0x00000020
+#define CPCAP_BIT_A2_CLK2 0x00000010
+#define CPCAP_BIT_A2_CLK1 0x00000008
+#define CPCAP_BIT_A2_CLK0 0x00000004
+#define CPCAP_BIT_A2_CLK_IN 0x00000002
+#define CPCAP_BIT_A2_CONFIG 0x00000001
+
+/*
+ * Register 641 - CPCAP_REG_CHRGR_1 bits
+ */
+#define CPCAP_BIT_UNUSED_641_15 0x00008000
+#define CPCAP_BIT_UNUSED_641_14 0x00004000
+#define CPCAP_BIT_CHRG_LED_EN 0x00002000
+#define CPCAP_BIT_RVRSMODE 0x00001000
+#define CPCAP_BIT_ICHRG_TR1 0x00000800
+#define CPCAP_BIT_ICHRG_TR0 0x00000400
+#define CPCAP_BIT_FET_OVRD 0x00000200
+#define CPCAP_BIT_FET_CTRL 0x00000100
+#define CPCAP_BIT_VCHRG3 0x00000080
+#define CPCAP_BIT_VCHRG2 0x00000040
+#define CPCAP_BIT_VCHRG1 0x00000020
+#define CPCAP_BIT_VCHRG0 0x00000010
+#define CPCAP_BIT_ICHRG3 0x00000008
+#define CPCAP_BIT_ICHRG2 0x00000004
+#define CPCAP_BIT_ICHRG1 0x00000002
+#define CPCAP_BIT_ICHRG0 0x00000001
+
+/*
+ * Register 768 - CPCAP_REG_ADCC1 bits
+ */
+#define CPCAP_BIT_ADEN_AUTO_CLR 0x00008000
+#define CPCAP_BIT_CAL_MODE 0x00004000
+#define CPCAP_BIT_ADC_CLK_SEL1 0x00002000
+#define CPCAP_BIT_ADC_CLK_SEL0 0x00001000
+#define CPCAP_BIT_ATOX 0x00000800
+#define CPCAP_BIT_ATO3 0x00000400
+#define CPCAP_BIT_ATO2 0x00000200
+#define CPCAP_BIT_ATO1 0x00000100
+#define CPCAP_BIT_ATO0 0x00000080
+#define CPCAP_BIT_ADA2 0x00000040
+#define CPCAP_BIT_ADA1 0x00000020
+#define CPCAP_BIT_ADA0 0x00000010
+#define CPCAP_BIT_AD_SEL1 0x00000008
+#define CPCAP_BIT_RAND1 0x00000004
+#define CPCAP_BIT_RAND0 0x00000002
+#define CPCAP_BIT_ADEN 0x00000001
+
+/*
+ * Register 769 - CPCAP_REG_ADCC2 bits
+ */
+#define CPCAP_BIT_CAL_FACTOR_ENABLE 0x00008000
+#define CPCAP_BIT_BATDETB_EN 0x00004000
+#define CPCAP_BIT_ADTRIG_ONESHOT 0x00002000
+#define CPCAP_BIT_ASC 0x00001000
+#define CPCAP_BIT_ATOX_PS_FACTOR 0x00000800
+#define CPCAP_BIT_ADC_PS_FACTOR1 0x00000400
+#define CPCAP_BIT_ADC_PS_FACTOR0 0x00000200
+#define CPCAP_BIT_AD4_SELECT 0x00000100
+#define CPCAP_BIT_ADC_BUSY 0x00000080
+#define CPCAP_BIT_THERMBIAS_EN 0x00000040
+#define CPCAP_BIT_ADTRIG_DIS 0x00000020
+#define CPCAP_BIT_LIADC 0x00000010
+#define CPCAP_BIT_TS_REFEN 0x00000008
+#define CPCAP_BIT_TS_M2 0x00000004
+#define CPCAP_BIT_TS_M1 0x00000002
+#define CPCAP_BIT_TS_M0 0x00000001
+
+/*
+ * Register 896 - CPCAP_REG_USBC1 bits
+ */
+#define CPCAP_BIT_IDPULSE 0x00008000
+#define CPCAP_BIT_ID100KPU 0x00004000
+#define CPCAP_BIT_IDPUCNTRL 0x00002000
+#define CPCAP_BIT_IDPU 0x00001000
+#define CPCAP_BIT_IDPD 0x00000800
+#define CPCAP_BIT_VBUSCHRGTMR3 0x00000400
+#define CPCAP_BIT_VBUSCHRGTMR2 0x00000200
+#define CPCAP_BIT_VBUSCHRGTMR1 0x00000100
+#define CPCAP_BIT_VBUSCHRGTMR0 0x00000080
+#define CPCAP_BIT_VBUSPU 0x00000040
+#define CPCAP_BIT_VBUSPD 0x00000020
+#define CPCAP_BIT_DMPD 0x00000010
+#define CPCAP_BIT_DPPD 0x00000008
+#define CPCAP_BIT_DM1K5PU 0x00000004
+#define CPCAP_BIT_DP1K5PU 0X00000002
+#define CPCAP_BIT_DP150KPU 0x00000001
+
+/*
+ * Register 897 - CPCAP_REG_USBC2 bits
+ */
+#define CPCAP_BIT_ZHSDRV1 0x00008000
+#define CPCAP_BIT_ZHSDRV0 0x00004000
+#define CPCAP_BIT_DPLLCLKREQ 0x00002000
+#define CPCAP_BIT_SE0CONN 0x00001000
+#define CPCAP_BIT_UARTTXTRI 0x00000800
+#define CPCAP_BIT_UARTSWAP 0x00000400
+#define CPCAP_BIT_UARTMUX1 0x00000200
+#define CPCAP_BIT_UARTMUX0 0x00000100
+#define CPCAP_BIT_ULPISTPLOW 0x00000080
+#define CPCAP_BIT_TXENPOL 0x00000040
+#define CPCAP_BIT_USBXCVREN 0x00000020
+#define CPCAP_BIT_USBCNTRL 0x00000010
+#define CPCAP_BIT_USBSUSPEND 0x00000008
+#define CPCAP_BIT_EMUMODE2 0x00000004
+#define CPCAP_BIT_EMUMODE1 0x00000002
+#define CPCAP_BIT_EMUMODE0 0x00000001
+
+/*
+ * Register 898 - CPCAP_REG_USBC3 bits
+ */
+#define CPCAP_BIT_SPARE_898_15 0x00008000
+#define CPCAP_BIT_IHSTX03 0x00004000
+#define CPCAP_BIT_IHSTX02 0x00002000
+#define CPCAP_BIT_IHSTX01 0x00001000
+#define CPCAP_BIT_IHSTX0 0x00000800
+#define CPCAP_BIT_IDPU_SPI 0x00000400
+#define CPCAP_BIT_UNUSED_898_9 0x00000200
+#define CPCAP_BIT_VBUSSTBY_EN 0x00000100
+#define CPCAP_BIT_VBUSEN_SPI 0x00000080
+#define CPCAP_BIT_VBUSPU_SPI 0x00000040
+#define CPCAP_BIT_VBUSPD_SPI 0x00000020
+#define CPCAP_BIT_DMPD_SPI 0x00000010
+#define CPCAP_BIT_DPPD_SPI 0x00000008
+#define CPCAP_BIT_SUSPEND_SPI 0x00000004
+#define CPCAP_BIT_PU_SPI 0x00000002
+#define CPCAP_BIT_ULPI_SPI_SEL 0x00000001
+
+/*
+ * Register 941 - CPCAP_REG_GPIO0 bits
+ */
+#define CPCAP_BIT_GPIO0MACROINITL 0x00008000
+#define CPCAP_BIT_GPIO0MACROINITH 0x00004000
+#define CPCAP_BIT_GPIO0MACROML 0x00002000
+#define CPCAP_BIT_GPIO0MACROMH 0x00001000
+#define CPCAP_BIT_UNUSED_941_11 0x00000800
+#define CPCAP_BIT_UNUSED_941_10 0x00000400
+#define CPCAP_BIT_GPIO0VLEV 0x00000200
+#define CPCAP_BIT_UNUSED_941_8 0x00000100
+#define CPCAP_BIT_GPIO0MUX1 0x00000080
+#define CPCAP_BIT_GPIO0MUX0 0x00000040
+#define CPCAP_BIT_GPIO0OT 0x00000020
+#define CPCAP_BIT_SPARE_941_4 0x00000010
+#define CPCAP_BIT_GPIO0PUEN 0x00000008
+#define CPCAP_BIT_GPIO0DIR 0x00000004
+#define CPCAP_BIT_GPIO0DRV 0x00000002
+#define CPCAP_BIT_GPIO0S 0x00000001
+
+/*
+ * Register 943 - CPCAP_REG_GPIO1 bits
+ */
+#define CPCAP_BIT_GPIO1MACROINITL 0x00008000
+#define CPCAP_BIT_GPIO1MACROINITH 0x00004000
+#define CPCAP_BIT_GPIO1MACROML 0x00002000
+#define CPCAP_BIT_GPIO1MACROMH 0x00001000
+#define CPCAP_BIT_UNUSED_943_11 0x00000800
+#define CPCAP_BIT_UNUSED_943_10 0x00000400
+#define CPCAP_BIT_GPIO1VLEV 0x00000200
+#define CPCAP_BIT_UNUSED_943_8 0x00000100
+#define CPCAP_BIT_GPIO1MUX1 0x00000080
+#define CPCAP_BIT_GPIO1MUX0 0x00000040
+#define CPCAP_BIT_GPIO1OT 0x00000020
+#define CPCAP_BIT_SPARE_943_4 0x00000010
+#define CPCAP_BIT_GPIO1PUEN 0x00000008
+#define CPCAP_BIT_GPIO1DIR 0x00000004
+#define CPCAP_BIT_GPIO1DRV 0x00000002
+#define CPCAP_BIT_GPIO1S 0x00000001
+
+/*
+ * Register 945 - CPCAP_REG_GPIO2 bits
+ */
+#define CPCAP_BIT_GPIO2MACROINITL 0x00008000
+#define CPCAP_BIT_GPIO2MACROINITH 0x00004000
+#define CPCAP_BIT_GPIO2MACROML 0x00002000
+#define CPCAP_BIT_GPIO2MACROMH 0x00001000
+#define CPCAP_BIT_UNUSED_945_11 0x00000800
+#define CPCAP_BIT_UNUSED_945_10 0x00000400
+#define CPCAP_BIT_GPIO2VLEV 0x00000200
+#define CPCAP_BIT_UNUSED_945_8 0x00000100
+#define CPCAP_BIT_GPIO2MUX1 0x00000080
+#define CPCAP_BIT_GPIO2MUX0 0x00000040
+#define CPCAP_BIT_GPIO2OT 0x00000020
+#define CPCAP_BIT_SPARE_945_4 0x00000010
+#define CPCAP_BIT_GPIO2PUEN 0x00000008
+#define CPCAP_BIT_GPIO2DIR 0x00000004
+#define CPCAP_BIT_GPIO2DRV 0x00000002
+#define CPCAP_BIT_GPIO2S 0x00000001
+
+/*
+ * Register 947 - CPCAP_REG_GPIO3 bits
+ */
+#define CPCAP_BIT_GPIO3MACROINITL 0x00008000
+#define CPCAP_BIT_GPIO3MACROINITH 0x00004000
+#define CPCAP_BIT_GPIO3MACROML 0x00002000
+#define CPCAP_BIT_GPIO3MACROMH 0x00001000
+#define CPCAP_BIT_UNUSED_947_11 0x00000800
+#define CPCAP_BIT_UNUSED_947_10 0x00000400
+#define CPCAP_BIT_GPIO3VLEV 0x00000200
+#define CPCAP_BIT_UNUSED_947_8 0x00000100
+#define CPCAP_BIT_GPIO3MUX1 0x00000080
+#define CPCAP_BIT_GPIO3MUX0 0x00000040
+#define CPCAP_BIT_GPIO3OT 0x00000020
+#define CPCAP_BIT_SPARE_947_4 0x00000010
+#define CPCAP_BIT_GPIO3PUEN 0x00000008
+#define CPCAP_BIT_GPIO3DIR 0x00000004
+#define CPCAP_BIT_GPIO3DRV 0x00000002
+#define CPCAP_BIT_GPIO3S 0x00000001
+
+/*
+ * Register 949 - CPCAP_REG_GPIO4 bits
+ */
+#define CPCAP_BIT_GPIO4MACROINITL 0x00008000
+#define CPCAP_BIT_GPIO4MACROINITH 0x00004000
+#define CPCAP_BIT_GPIO4MACROML 0x00002000
+#define CPCAP_BIT_GPIO4MACROMH 0x00001000
+#define CPCAP_BIT_UNUSED_949_11 0x00000800
+#define CPCAP_BIT_UNUSED_949_10 0x00000400
+#define CPCAP_BIT_GPIO4VLEV 0x00000200
+#define CPCAP_BIT_UNUSED_949_8 0x00000100
+#define CPCAP_BIT_GPIO4MUX1 0x00000080
+#define CPCAP_BIT_GPIO4MUX0 0x00000040
+#define CPCAP_BIT_GPIO4OT 0x00000020
+#define CPCAP_BIT_SPARE_949_4 0x00000010
+#define CPCAP_BIT_GPIO4PUEN 0x00000008
+#define CPCAP_BIT_GPIO4DIR 0x00000004
+#define CPCAP_BIT_GPIO4DRV 0x00000002
+#define CPCAP_BIT_GPIO4S 0x00000001
+
+/*
+ * Register 951 - CPCAP_REG_GPIO5 bits
+ */
+#define CPCAP_BIT_GPIO5MACROINITL 0x00008000
+#define CPCAP_BIT_GPIO5MACROINITH 0x00004000
+#define CPCAP_BIT_GPIO5MACROML 0x00002000
+#define CPCAP_BIT_GPIO5MACROMH 0x00001000
+#define CPCAP_BIT_UNUSED_951_11 0x00000800
+#define CPCAP_BIT_UNUSED_951_10 0x00000400
+#define CPCAP_BIT_GPIO5VLEV 0x00000200
+#define CPCAP_BIT_GPIO5MUX2 0x00000100
+#define CPCAP_BIT_GPIO5MUX1 0x00000080
+#define CPCAP_BIT_GPIO5MUX0 0x00000040
+#define CPCAP_BIT_GPIO5OT 0x00000020
+#define CPCAP_BIT_SPARE_951_4 0x00000010
+#define CPCAP_BIT_GPIO5PUEN 0x00000008
+#define CPCAP_BIT_GPIO5DIR 0x00000004
+#define CPCAP_BIT_GPIO5DRV 0x00000002
+#define CPCAP_BIT_GPIO5S 0x00000001
+
+/*
+ * Register 953 - CPCAP_REG_GPIO6 bits
+ */
+#define CPCAP_BIT_GPIO6MACROINITL 0x00008000
+#define CPCAP_BIT_GPIO6MACROINITH 0x00004000
+#define CPCAP_BIT_GPIO6MACROML 0x00002000
+#define CPCAP_BIT_GPIO6MACROMH 0x00001000
+#define CPCAP_BIT_UNUSED_953_11 0x00000800
+#define CPCAP_BIT_UNUSED_953_10 0x00000400
+#define CPCAP_BIT_GPIO6VLEV 0x00000200
+#define CPCAP_BIT_GPIO6MUX2 0x00000100
+#define CPCAP_BIT_GPIO6MUX1 0x00000080
+#define CPCAP_BIT_GPIO6MUX0 0x00000040
+#define CPCAP_BIT_GPIO6OT 0x00000020
+#define CPCAP_BIT_SPARE_953_4 0x00000010
+#define CPCAP_BIT_GPIO6PUEN 0x00000008
+#define CPCAP_BIT_GPIO6DIR 0x00000004
+#define CPCAP_BIT_GPIO6DRV 0x00000002
+#define CPCAP_BIT_GPIO6S 0x00000001
+
+#endif /* __CPCAP_REGBITS_H__ */
diff --git a/include/linux/spi/cpcap.h b/include/linux/spi/cpcap.h
new file mode 100644
index 00000000000..9a00b1f5ccb
--- /dev/null
+++ b/include/linux/spi/cpcap.h
@@ -0,0 +1,862 @@
+#ifndef _LINUX_SPI_CPCAP_H
+#define _LINUX_SPI_CPCAP_H
+
+/*
+ * 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., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ *
+ */
+
+#include <linux/ioctl.h>
+#include <linux/rtc.h>
+#ifdef __KERNEL__
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <linux/power_supply.h>
+#include <linux/platform_device.h>
+#endif
+
+#define CPCAP_DEV_NAME "cpcap"
+#define CPCAP_NUM_REG_CPCAP (CPCAP_REG_END - CPCAP_REG_START + 1)
+
+#define CPCAP_IRQ_INT1_INDEX 0
+#define CPCAP_IRQ_INT2_INDEX 16
+#define CPCAP_IRQ_INT3_INDEX 32
+#define CPCAP_IRQ_INT4_INDEX 48
+#define CPCAP_IRQ_INT5_INDEX 64
+
+#define CPCAP_HWCFG_NUM 2 /* The number of hardware config words. */
+/*
+ * Tell the uC to setup the secondary standby bits for the regulators used.
+ */
+#define CPCAP_HWCFG0_NONE 0x0000
+#define CPCAP_HWCFG0_SEC_STBY_SW1 0x0001
+#define CPCAP_HWCFG0_SEC_STBY_SW2 0x0002
+#define CPCAP_HWCFG0_SEC_STBY_SW3 0x0004
+#define CPCAP_HWCFG0_SEC_STBY_SW4 0x0008
+#define CPCAP_HWCFG0_SEC_STBY_SW5 0x0010
+#define CPCAP_HWCFG0_SEC_STBY_VAUDIO 0x0020
+#define CPCAP_HWCFG0_SEC_STBY_VCAM 0x0040
+#define CPCAP_HWCFG0_SEC_STBY_VCSI 0x0080
+#define CPCAP_HWCFG0_SEC_STBY_VDAC 0x0100
+#define CPCAP_HWCFG0_SEC_STBY_VDIG 0x0200
+#define CPCAP_HWCFG0_SEC_STBY_VHVIO 0x0400
+#define CPCAP_HWCFG0_SEC_STBY_VPLL 0x0800
+#define CPCAP_HWCFG0_SEC_STBY_VRF1 0x1000
+#define CPCAP_HWCFG0_SEC_STBY_VRF2 0x2000
+#define CPCAP_HWCFG0_SEC_STBY_VRFREF 0x4000
+#define CPCAP_HWCFG0_SEC_STBY_VSDIO 0x8000
+
+#define CPCAP_HWCFG1_NONE 0x0000
+#define CPCAP_HWCFG1_SEC_STBY_VWLAN1 0x0001
+#define CPCAP_HWCFG1_SEC_STBY_VWLAN2 0x0002
+#define CPCAP_HWCFG1_SEC_STBY_VSIM 0x0004
+#define CPCAP_HWCFG1_SEC_STBY_VSIMCARD 0x0008
+#define CPCAP_HWCFG1_SEC_STBY_VUSB 0x0010
+/* Enable mapping of the PRI_STANDBY and SEC_STANDBY lines onto CPCAP GPIO. */
+#define CPCAP_HWCFG1_STBY_GPIO 0x1000
+
+#define CPCAP_WHISPER_MODE_PU 0x00000001
+#define CPCAP_WHISPER_ENABLE_UART 0x00000002
+
+enum cpcap_regulator_id {
+ CPCAP_SW1,
+ CPCAP_SW2,
+ CPCAP_SW3,
+ CPCAP_SW4,
+ CPCAP_SW5,
+ CPCAP_VCAM,
+ CPCAP_VCSI,
+ CPCAP_VDAC,
+ CPCAP_VDIG,
+ CPCAP_VFUSE,
+ CPCAP_VHVIO,
+ CPCAP_VSDIO,
+ CPCAP_VPLL,
+ CPCAP_VRF1,
+ CPCAP_VRF2,
+ CPCAP_VRFREF,
+ CPCAP_VWLAN1,
+ CPCAP_VWLAN2,
+ CPCAP_VSIM,
+ CPCAP_VSIMCARD,
+ CPCAP_VVIB,
+ CPCAP_VUSB,
+ CPCAP_VAUDIO,
+
+ CPCAP_NUM_REGULATORS
+};
+
+/*
+ * Enumeration of all registers in the cpcap. Note that the register
+ * numbers on the CPCAP IC are not contiguous. The values of the enums below
+ * are not the actual register numbers.
+ */
+enum cpcap_reg {
+ CPCAP_REG_START, /* Start of CPCAP registers. */
+
+ CPCAP_REG_INT1 = CPCAP_REG_START, /* Interrupt 1 */
+ CPCAP_REG_INT2, /* Interrupt 2 */
+ CPCAP_REG_INT3, /* Interrupt 3 */
+ CPCAP_REG_INT4, /* Interrupt 4 */
+ CPCAP_REG_INTM1, /* Interrupt Mask 1 */
+ CPCAP_REG_INTM2, /* Interrupt Mask 2 */
+ CPCAP_REG_INTM3, /* Interrupt Mask 3 */
+ CPCAP_REG_INTM4, /* Interrupt Mask 4 */
+ CPCAP_REG_INTS1, /* Interrupt Sense 1 */
+ CPCAP_REG_INTS2, /* Interrupt Sense 2 */
+ CPCAP_REG_INTS3, /* Interrupt Sense 3 */
+ CPCAP_REG_INTS4, /* Interrupt Sense 4 */
+ CPCAP_REG_ASSIGN1, /* Resource Assignment 1 */
+ CPCAP_REG_ASSIGN2, /* Resource Assignment 2 */
+ CPCAP_REG_ASSIGN3, /* Resource Assignment 3 */
+ CPCAP_REG_ASSIGN4, /* Resource Assignment 4 */
+ CPCAP_REG_ASSIGN5, /* Resource Assignment 5 */
+ CPCAP_REG_ASSIGN6, /* Resource Assignment 6 */
+ CPCAP_REG_VERSC1, /* Version Control 1 */
+ CPCAP_REG_VERSC2, /* Version Control 2 */
+
+ CPCAP_REG_MI1, /* Macro Interrupt 1 */
+ CPCAP_REG_MIM1, /* Macro Interrupt Mask 1 */
+ CPCAP_REG_MI2, /* Macro Interrupt 2 */
+ CPCAP_REG_MIM2, /* Macro Interrupt Mask 2 */
+ CPCAP_REG_UCC1, /* UC Control 1 */
+ CPCAP_REG_UCC2, /* UC Control 2 */
+ CPCAP_REG_PC1, /* Power Cut 1 */
+ CPCAP_REG_PC2, /* Power Cut 2 */
+ CPCAP_REG_BPEOL, /* BP and EOL */
+ CPCAP_REG_PGC, /* Power Gate and Control */
+ CPCAP_REG_MT1, /* Memory Transfer 1 */
+ CPCAP_REG_MT2, /* Memory Transfer 2 */
+ CPCAP_REG_MT3, /* Memory Transfer 3 */
+ CPCAP_REG_PF, /* Print Format */
+
+ CPCAP_REG_SCC, /* System Clock Control */
+ CPCAP_REG_SW1, /* Stop Watch 1 */
+ CPCAP_REG_SW2, /* Stop Watch 2 */
+ CPCAP_REG_UCTM, /* UC Turbo Mode */
+ CPCAP_REG_TOD1, /* Time of Day 1 */
+ CPCAP_REG_TOD2, /* Time of Day 2 */
+ CPCAP_REG_TODA1, /* Time of Day Alarm 1 */
+ CPCAP_REG_TODA2, /* Time of Day Alarm 2 */
+ CPCAP_REG_DAY, /* Day */
+ CPCAP_REG_DAYA, /* Day Alarm */
+ CPCAP_REG_VAL1, /* Validity 1 */
+ CPCAP_REG_VAL2, /* Validity 2 */
+
+ CPCAP_REG_SDVSPLL, /* Switcher DVS and PLL */
+ CPCAP_REG_SI2CC1, /* Switcher I2C Control 1 */
+ CPCAP_REG_Si2CC2, /* Switcher I2C Control 2 */
+ CPCAP_REG_S1C1, /* Switcher 1 Control 1 */
+ CPCAP_REG_S1C2, /* Switcher 1 Control 2 */
+ CPCAP_REG_S2C1, /* Switcher 2 Control 1 */
+ CPCAP_REG_S2C2, /* Switcher 2 Control 2 */
+ CPCAP_REG_S3C, /* Switcher 3 Control */
+ CPCAP_REG_S4C1, /* Switcher 4 Control 1 */
+ CPCAP_REG_S4C2, /* Switcher 4 Control 2 */
+ CPCAP_REG_S5C, /* Switcher 5 Control */
+ CPCAP_REG_S6C, /* Switcher 6 Control */
+ CPCAP_REG_VCAMC, /* VCAM Control */
+ CPCAP_REG_VCSIC, /* VCSI Control */
+ CPCAP_REG_VDACC, /* VDAC Control */
+ CPCAP_REG_VDIGC, /* VDIG Control */
+ CPCAP_REG_VFUSEC, /* VFUSE Control */
+ CPCAP_REG_VHVIOC, /* VHVIO Control */
+ CPCAP_REG_VSDIOC, /* VSDIO Control */
+ CPCAP_REG_VPLLC, /* VPLL Control */
+ CPCAP_REG_VRF1C, /* VRF1 Control */
+ CPCAP_REG_VRF2C, /* VRF2 Control */
+ CPCAP_REG_VRFREFC, /* VRFREF Control */
+ CPCAP_REG_VWLAN1C, /* VWLAN1 Control */
+ CPCAP_REG_VWLAN2C, /* VWLAN2 Control */
+ CPCAP_REG_VSIMC, /* VSIM Control */
+ CPCAP_REG_VVIBC, /* VVIB Control */
+ CPCAP_REG_VUSBC, /* VUSB Control */
+ CPCAP_REG_VUSBINT1C, /* VUSBINT1 Control */
+ CPCAP_REG_VUSBINT2C, /* VUSBINT2 Control */
+ CPCAP_REG_URT, /* Useroff Regulator Trigger */
+ CPCAP_REG_URM1, /* Useroff Regulator Mask 1 */
+ CPCAP_REG_URM2, /* Useroff Regulator Mask 2 */
+
+ CPCAP_REG_VAUDIOC, /* VAUDIO Control */
+ CPCAP_REG_CC, /* Codec Control */
+ CPCAP_REG_CDI, /* Codec Digital Interface */
+ CPCAP_REG_SDAC, /* Stereo DAC */
+ CPCAP_REG_SDACDI, /* Stereo DAC Digital Interface */
+ CPCAP_REG_TXI, /* TX Inputs */
+ CPCAP_REG_TXMP, /* TX MIC PGA's */
+ CPCAP_REG_RXOA, /* RX Output Amplifiers */
+ CPCAP_REG_RXVC, /* RX Volume Control */
+ CPCAP_REG_RXCOA, /* RX Codec to Output Amps */
+ CPCAP_REG_RXSDOA, /* RX Stereo DAC to Output Amps */
+ CPCAP_REG_RXEPOA, /* RX External PGA to Output Amps */
+ CPCAP_REG_RXLL, /* RX Low Latency */
+ CPCAP_REG_A2LA, /* A2 Loudspeaker Amplifier */
+ CPCAP_REG_MIPIS1, /* MIPI Slimbus 1 */
+ CPCAP_REG_MIPIS2, /* MIPI Slimbus 2 */
+ CPCAP_REG_MIPIS3, /* MIPI Slimbus 3. */
+ CPCAP_REG_LVAB, /* LMR Volume and A4 Balanced. */
+
+ CPCAP_REG_CCC1, /* Coulomb Counter Control 1 */
+ CPCAP_REG_CRM, /* Charger and Reverse Mode */
+ CPCAP_REG_CCCC2, /* Coincell and Coulomb Ctr Ctrl 2 */
+ CPCAP_REG_CCS1, /* Coulomb Counter Sample 1 */
+ CPCAP_REG_CCS2, /* Coulomb Counter Sample 2 */
+ CPCAP_REG_CCA1, /* Coulomb Counter Accumulator 1 */
+ CPCAP_REG_CCA2, /* Coulomb Counter Accumulator 2 */
+ CPCAP_REG_CCM, /* Coulomb Counter Mode */
+ CPCAP_REG_CCO, /* Coulomb Counter Offset */
+ CPCAP_REG_CCI, /* Coulomb Counter Integrator */
+
+ CPCAP_REG_ADCC1, /* A/D Converter Configuration 1 */
+ CPCAP_REG_ADCC2, /* A/D Converter Configuration 2 */
+ CPCAP_REG_ADCD0, /* A/D Converter Data 0 */
+ CPCAP_REG_ADCD1, /* A/D Converter Data 1 */
+ CPCAP_REG_ADCD2, /* A/D Converter Data 2 */
+ CPCAP_REG_ADCD3, /* A/D Converter Data 3 */
+ CPCAP_REG_ADCD4, /* A/D Converter Data 4 */
+ CPCAP_REG_ADCD5, /* A/D Converter Data 5 */
+ CPCAP_REG_ADCD6, /* A/D Converter Data 6 */
+ CPCAP_REG_ADCD7, /* A/D Converter Data 7 */
+ CPCAP_REG_ADCAL1, /* A/D Converter Calibration 1 */
+ CPCAP_REG_ADCAL2, /* A/D Converter Calibration 2 */
+
+ CPCAP_REG_USBC1, /* USB Control 1 */
+ CPCAP_REG_USBC2, /* USB Control 2 */
+ CPCAP_REG_USBC3, /* USB Control 3 */
+ CPCAP_REG_UVIDL, /* ULPI Vendor ID Low */
+ CPCAP_REG_UVIDH, /* ULPI Vendor ID High */
+ CPCAP_REG_UPIDL, /* ULPI Product ID Low */
+ CPCAP_REG_UPIDH, /* ULPI Product ID High */
+ CPCAP_REG_UFC1, /* ULPI Function Control 1 */
+ CPCAP_REG_UFC2, /* ULPI Function Control 2 */
+ CPCAP_REG_UFC3, /* ULPI Function Control 3 */
+ CPCAP_REG_UIC1, /* ULPI Interface Control 1 */
+ CPCAP_REG_UIC2, /* ULPI Interface Control 2 */
+ CPCAP_REG_UIC3, /* ULPI Interface Control 3 */
+ CPCAP_REG_USBOTG1, /* USB OTG Control 1 */
+ CPCAP_REG_USBOTG2, /* USB OTG Control 2 */
+ CPCAP_REG_USBOTG3, /* USB OTG Control 3 */
+ CPCAP_REG_UIER1, /* USB Interrupt Enable Rising 1 */
+ CPCAP_REG_UIER2, /* USB Interrupt Enable Rising 2 */
+ CPCAP_REG_UIER3, /* USB Interrupt Enable Rising 3 */
+ CPCAP_REG_UIEF1, /* USB Interrupt Enable Falling 1 */
+ CPCAP_REG_UIEF2, /* USB Interrupt Enable Falling 1 */
+ CPCAP_REG_UIEF3, /* USB Interrupt Enable Falling 1 */
+ CPCAP_REG_UIS, /* USB Interrupt Status */
+ CPCAP_REG_UIL, /* USB Interrupt Latch */
+ CPCAP_REG_USBD, /* USB Debug */
+ CPCAP_REG_SCR1, /* Scratch 1 */
+ CPCAP_REG_SCR2, /* Scratch 2 */
+ CPCAP_REG_SCR3, /* Scratch 3 */
+ CPCAP_REG_VMC, /* Video Mux Control */
+ CPCAP_REG_OWDC, /* One Wire Device Control */
+ CPCAP_REG_GPIO0, /* GPIO 0 Control */
+ CPCAP_REG_GPIO1, /* GPIO 1 Control */
+ CPCAP_REG_GPIO2, /* GPIO 2 Control */
+ CPCAP_REG_GPIO3, /* GPIO 3 Control */
+ CPCAP_REG_GPIO4, /* GPIO 4 Control */
+ CPCAP_REG_GPIO5, /* GPIO 5 Control */
+ CPCAP_REG_GPIO6, /* GPIO 6 Control */
+
+ CPCAP_REG_MDLC, /* Main Display Lighting Control */
+ CPCAP_REG_KLC, /* Keypad Lighting Control */
+ CPCAP_REG_ADLC, /* Aux Display Lighting Control */
+ CPCAP_REG_REDC, /* Red Triode Control */
+ CPCAP_REG_GREENC, /* Green Triode Control */
+ CPCAP_REG_BLUEC, /* Blue Triode Control */
+ CPCAP_REG_CFC, /* Camera Flash Control */
+ CPCAP_REG_ABC, /* Adaptive Boost Control */
+ CPCAP_REG_BLEDC, /* Bluetooth LED Control */
+ CPCAP_REG_CLEDC, /* Camera Privacy LED Control */
+
+ CPCAP_REG_OW1C, /* One Wire 1 Command */
+ CPCAP_REG_OW1D, /* One Wire 1 Data */
+ CPCAP_REG_OW1I, /* One Wire 1 Interrupt */
+ CPCAP_REG_OW1IE, /* One Wire 1 Interrupt Enable */
+ CPCAP_REG_OW1, /* One Wire 1 Control */
+ CPCAP_REG_OW2C, /* One Wire 2 Command */
+ CPCAP_REG_OW2D, /* One Wire 2 Data */
+ CPCAP_REG_OW2I, /* One Wire 2 Interrupt */
+ CPCAP_REG_OW2IE, /* One Wire 2 Interrupt Enable */
+ CPCAP_REG_OW2, /* One Wire 2 Control */
+ CPCAP_REG_OW3C, /* One Wire 3 Command */
+ CPCAP_REG_OW3D, /* One Wire 3 Data */
+ CPCAP_REG_OW3I, /* One Wire 3 Interrupt */
+ CPCAP_REG_OW3IE, /* One Wire 3 Interrupt Enable */
+ CPCAP_REG_OW3, /* One Wire 3 Control */
+ CPCAP_REG_GCAIC, /* GCAI Clock Control */
+ CPCAP_REG_GCAIM, /* GCAI GPIO Mode */
+ CPCAP_REG_LGDIR, /* LMR GCAI GPIO Direction */
+ CPCAP_REG_LGPU, /* LMR GCAI GPIO Pull-up */
+ CPCAP_REG_LGPIN, /* LMR GCAI GPIO Pin */
+ CPCAP_REG_LGMASK, /* LMR GCAI GPIO Mask */
+ CPCAP_REG_LDEB, /* LMR Debounce Settings */
+ CPCAP_REG_LGDET, /* LMR GCAI Detach Detect */
+ CPCAP_REG_LMISC, /* LMR Misc Bits */
+ CPCAP_REG_LMACE, /* LMR Mace IC Support */
+ CPCAP_REG_TEST, /* Test */
+ CPCAP_REG_ST_TEST1, /* ST Test 1 */
+
+ CPCAP_REG_END = CPCAP_REG_ST_TEST1, /* End of CPCAP registers. */
+
+ CPCAP_REG_MAX /* The largest valid register value. */
+ = CPCAP_REG_END,
+
+ CPCAP_REG_SIZE = CPCAP_REG_MAX + 1,
+ CPCAP_REG_UNUSED = CPCAP_REG_MAX + 2,
+};
+
+enum {
+ CPCAP_IOCTL_NUM_TEST__START,
+ CPCAP_IOCTL_NUM_TEST_READ_REG,
+ CPCAP_IOCTL_NUM_TEST_WRITE_REG,
+ CPCAP_IOCTL_NUM_TEST__END,
+
+ CPCAP_IOCTL_NUM_ADC__START,
+ CPCAP_IOCTL_NUM_ADC_PHASE,
+ CPCAP_IOCTL_NUM_ADC__END,
+
+ CPCAP_IOCTL_NUM_BATT__START,
+ CPCAP_IOCTL_NUM_BATT_DISPLAY_UPDATE,
+ CPCAP_IOCTL_NUM_BATT_ATOD_ASYNC,
+ CPCAP_IOCTL_NUM_BATT_ATOD_SYNC,
+ CPCAP_IOCTL_NUM_BATT_ATOD_READ,
+ CPCAP_IOCTL_NUM_BATT__END,
+
+ CPCAP_IOCTL_NUM_UC__START,
+ CPCAP_IOCTL_NUM_UC_MACRO_START,
+ CPCAP_IOCTL_NUM_UC_MACRO_STOP,
+ CPCAP_IOCTL_NUM_UC_GET_VENDOR,
+ CPCAP_IOCTL_NUM_UC_SET_TURBO_MODE,
+ CPCAP_IOCTL_NUM_UC__END,
+
+ CPCAP_IOCTL_NUM_RTC__START,
+ CPCAP_IOCTL_NUM_RTC_COUNT,
+ CPCAP_IOCTL_NUM_RTC__END,
+
+ CPCAP_IOCTL_NUM_ACCY__START,
+ CPCAP_IOCTL_NUM_ACCY_WHISPER,
+ CPCAP_IOCTL_NUM_ACCY__END,
+
+ CPCAP_IOCTL_NUM_AUDIO_PWR__START,
+ CPCAP_IOCTL_NUM_AUDIO_PWR_MODE,
+ CPCAP_IOCTL_NUM_AUDIO_PWR_ENABLE,
+ CPCAP_IOCTL_NUM_AUDIO_PWR__END,
+};
+
+enum cpcap_irqs {
+ CPCAP_IRQ__START, /* 1st supported interrupt event */
+ CPCAP_IRQ_HSCLK = CPCAP_IRQ_INT1_INDEX, /* High Speed Clock */
+ CPCAP_IRQ_PRIMAC, /* Primary Macro */
+ CPCAP_IRQ_SECMAC, /* Secondary Macro */
+ CPCAP_IRQ_LOWBPL, /* Low Battery Low Threshold */
+ CPCAP_IRQ_SEC2PRI, /* 2nd Macro to Primary Processor */
+ CPCAP_IRQ_LOWBPH, /* Low Battery High Threshold */
+ CPCAP_IRQ_EOL, /* End of Life */
+ CPCAP_IRQ_TS, /* Touchscreen */
+ CPCAP_IRQ_ADCDONE, /* ADC Conversion Complete */
+ CPCAP_IRQ_HS, /* Headset */
+ CPCAP_IRQ_MB2, /* Mic Bias2 */
+ CPCAP_IRQ_VBUSOV, /* Overvoltage Detected */
+ CPCAP_IRQ_RVRS_CHRG, /* Reverse Charge */
+ CPCAP_IRQ_CHRG_DET, /* Charger Detected */
+ CPCAP_IRQ_IDFLOAT, /* ID Float */
+ CPCAP_IRQ_IDGND, /* ID Ground */
+
+ CPCAP_IRQ_SE1 = CPCAP_IRQ_INT2_INDEX, /* SE1 Detector */
+ CPCAP_IRQ_SESSEND, /* Session End */
+ CPCAP_IRQ_SESSVLD, /* Session Valid */
+ CPCAP_IRQ_VBUSVLD, /* VBUS Valid */
+ CPCAP_IRQ_CHRG_CURR1, /* Charge Current Monitor (20mA) */
+ CPCAP_IRQ_CHRG_CURR2, /* Charge Current Monitor (250mA) */
+ CPCAP_IRQ_RVRS_MODE, /* Reverse Current Limit */
+ CPCAP_IRQ_ON, /* On Signal */
+ CPCAP_IRQ_ON2, /* On 2 Signal */
+ CPCAP_IRQ_CLK, /* 32k Clock Transition */
+ CPCAP_IRQ_1HZ, /* 1Hz Tick */
+ CPCAP_IRQ_PTT, /* Push To Talk */
+ CPCAP_IRQ_SE0CONN, /* SE0 Condition */
+ CPCAP_IRQ_CHRG_SE1B, /* CHRG_SE1B Pin */
+ CPCAP_IRQ_UART_ECHO_OVERRUN, /* UART Buffer Overflow */
+ CPCAP_IRQ_EXTMEMHD, /* External MEMHOLD */
+
+ CPCAP_IRQ_WARM = CPCAP_IRQ_INT3_INDEX, /* Warm Start */
+ CPCAP_IRQ_SYSRSTR, /* System Restart */
+ CPCAP_IRQ_SOFTRST, /* Soft Reset */
+ CPCAP_IRQ_DIEPWRDWN, /* Die Temperature Powerdown */
+ CPCAP_IRQ_DIETEMPH, /* Die Temperature High */
+ CPCAP_IRQ_PC, /* Power Cut */
+ CPCAP_IRQ_OFLOWSW, /* Stopwatch Overflow */
+ CPCAP_IRQ_TODA, /* TOD Alarm */
+ CPCAP_IRQ_OPT_SEL_DTCH, /* Detach Detect */
+ CPCAP_IRQ_OPT_SEL_STATE, /* State Change */
+ CPCAP_IRQ_ONEWIRE1, /* Onewire 1 Block */
+ CPCAP_IRQ_ONEWIRE2, /* Onewire 2 Block */
+ CPCAP_IRQ_ONEWIRE3, /* Onewire 3 Block */
+ CPCAP_IRQ_UCRESET, /* Microcontroller Reset */
+ CPCAP_IRQ_PWRGOOD, /* BP Turn On */
+ CPCAP_IRQ_USBDPLLCLK, /* USB DPLL Status */
+
+ CPCAP_IRQ_DPI = CPCAP_IRQ_INT4_INDEX, /* DP Line */
+ CPCAP_IRQ_DMI, /* DM Line */
+ CPCAP_IRQ_UCBUSY, /* Microcontroller Busy */
+ CPCAP_IRQ_GCAI_CURR1, /* Charge Current Monitor (65mA) */
+ CPCAP_IRQ_GCAI_CURR2, /* Charge Current Monitor (600mA) */
+ CPCAP_IRQ_SB_MAX_RETRANSMIT_ERR,/* SLIMbus Retransmit Error */
+ CPCAP_IRQ_BATTDETB, /* Battery Presence Detected */
+ CPCAP_IRQ_PRIHALT, /* Primary Microcontroller Halt */
+ CPCAP_IRQ_SECHALT, /* Secondary Microcontroller Halt */
+ CPCAP_IRQ_CC_CAL, /* CC Calibration */
+
+ CPCAP_IRQ_UC_PRIROMR = CPCAP_IRQ_INT5_INDEX, /* Prim ROM Rd Macro Int */
+ CPCAP_IRQ_UC_PRIRAMW, /* Primary RAM Write Macro Int */
+ CPCAP_IRQ_UC_PRIRAMR, /* Primary RAM Read Macro Int */
+ CPCAP_IRQ_UC_USEROFF, /* USEROFF Macro Interrupt */
+ CPCAP_IRQ_UC_PRIMACRO_4, /* Primary Macro 4 Interrupt */
+ CPCAP_IRQ_UC_PRIMACRO_5, /* Primary Macro 5 Interrupt */
+ CPCAP_IRQ_UC_PRIMACRO_6, /* Primary Macro 6 Interrupt */
+ CPCAP_IRQ_UC_PRIMACRO_7, /* Primary Macro 7 Interrupt */
+ CPCAP_IRQ_UC_PRIMACRO_8, /* Primary Macro 8 Interrupt */
+ CPCAP_IRQ_UC_PRIMACRO_9, /* Primary Macro 9 Interrupt */
+ CPCAP_IRQ_UC_PRIMACRO_10, /* Primary Macro 10 Interrupt */
+ CPCAP_IRQ_UC_PRIMACRO_11, /* Primary Macro 11 Interrupt */
+ CPCAP_IRQ_UC_PRIMACRO_12, /* Primary Macro 12 Interrupt */
+ CPCAP_IRQ_UC_PRIMACRO_13, /* Primary Macro 13 Interrupt */
+ CPCAP_IRQ_UC_PRIMACRO_14, /* Primary Macro 14 Interrupt */
+ CPCAP_IRQ_UC_PRIMACRO_15, /* Primary Macro 15 Interrupt */
+ CPCAP_IRQ__NUM /* Number of allocated events */
+};
+
+enum cpcap_adc_bank0 {
+ CPCAP_ADC_AD0_BATTDETB,
+ CPCAP_ADC_BATTP,
+ CPCAP_ADC_VBUS,
+ CPCAP_ADC_AD3,
+ CPCAP_ADC_BPLUS_AD4,
+ CPCAP_ADC_CHG_ISENSE,
+ CPCAP_ADC_BATTI_ADC,
+ CPCAP_ADC_USB_ID,
+
+ CPCAP_ADC_BANK0_NUM,
+};
+
+enum cpcap_adc_bank1 {
+ CPCAP_ADC_AD8,
+ CPCAP_ADC_AD9,
+ CPCAP_ADC_LICELL,
+ CPCAP_ADC_HV_BATTP,
+ CPCAP_ADC_TSX1_AD12,
+ CPCAP_ADC_TSX2_AD13,
+ CPCAP_ADC_TSY1_AD14,
+ CPCAP_ADC_TSY2_AD15,
+
+ CPCAP_ADC_BANK1_NUM,
+};
+
+enum cpcap_adc_format {
+ CPCAP_ADC_FORMAT_RAW,
+ CPCAP_ADC_FORMAT_PHASED,
+ CPCAP_ADC_FORMAT_CONVERTED,
+};
+
+enum cpcap_adc_timing {
+ CPCAP_ADC_TIMING_IMM,
+ CPCAP_ADC_TIMING_IN,
+ CPCAP_ADC_TIMING_OUT,
+};
+
+enum cpcap_adc_type {
+ CPCAP_ADC_TYPE_BANK_0,
+ CPCAP_ADC_TYPE_BANK_1,
+ CPCAP_ADC_TYPE_BATT_PI,
+};
+
+enum cpcap_bank {
+ CPCAP_BANK_PRIMARY,
+ CPCAP_BANK_SECONDARY,
+};
+
+enum cpcap_standby {
+ CPCAP_PRISTANDBY = 0x01,
+ CPCAP_SECSTANDBY = 0x02,
+};
+
+enum cpcap_macro {
+ CPCAP_MACRO_ROMR,
+ CPCAP_MACRO_RAMW,
+ CPCAP_MACRO_RAMR,
+ CPCAP_MACRO_USEROFF,
+ CPCAP_MACRO_4,
+ CPCAP_MACRO_5,
+ CPCAP_MACRO_6,
+ CPCAP_MACRO_7,
+ CPCAP_MACRO_8,
+ CPCAP_MACRO_9,
+ CPCAP_MACRO_10,
+ CPCAP_MACRO_11,
+ CPCAP_MACRO_12,
+ CPCAP_MACRO_13,
+ CPCAP_MACRO_14,
+ CPCAP_MACRO_15,
+
+ CPCAP_MACRO__END,
+};
+
+enum cpcap_vendor {
+ CPCAP_VENDOR_ST,
+ CPCAP_VENDOR_TI,
+};
+
+enum cpcap_revision {
+ CPCAP_REVISION_1_0 = 0x08,
+ CPCAP_REVISION_1_1 = 0x09,
+ CPCAP_REVISION_2_0 = 0x10,
+ CPCAP_REVISION_2_1 = 0x11,
+};
+
+enum cpcap_batt_usb_model {
+ CPCAP_BATT_USB_MODEL_NONE,
+ CPCAP_BATT_USB_MODEL_USB,
+ CPCAP_BATT_USB_MODEL_FACTORY,
+};
+
+struct cpcap_spi_init_data {
+ enum cpcap_reg reg;
+ unsigned short data;
+};
+
+struct cpcap_adc_ato {
+ unsigned short ato_in;
+ unsigned short atox_in;
+ unsigned short adc_ps_factor_in;
+ unsigned short atox_ps_factor_in;
+ unsigned short ato_out;
+ unsigned short atox_out;
+ unsigned short adc_ps_factor_out;
+ unsigned short atox_ps_factor_out;
+ unsigned short ichrg_sense_res;
+};
+
+struct cpcap_display_led {
+ unsigned int display_reg;
+ unsigned int display_mask;
+ unsigned int display_on;
+ unsigned int display_off;
+ unsigned int display_init;
+ unsigned int poll_intvl;
+ unsigned int zone0;
+ unsigned int zone1;
+ unsigned int zone2;
+ unsigned int zone3;
+ unsigned int zone4;
+};
+
+struct cpcap_button_led {
+ unsigned int button_reg;
+ unsigned int button_mask;
+ unsigned int button_on;
+ unsigned int button_off;
+};
+
+struct cpcap_kpad_led {
+ unsigned int kpad_reg;
+ unsigned int kpad_mask;
+ unsigned int kpad_on;
+ unsigned int kpad_off;
+};
+
+struct cpcap_rgb_led {
+ unsigned int rgb_reg;
+ unsigned int rgb_mask;
+ unsigned int rgb_on;
+ unsigned int rgb_off;
+};
+struct cpcap_als_data {
+ unsigned short lux_max;
+ unsigned short lux_min;
+ unsigned short als_max;
+ unsigned short als_min;
+};
+
+struct cpcap_leds {
+ struct cpcap_display_led display_led;
+ struct cpcap_button_led button_led;
+ struct cpcap_kpad_led kpad_led;
+ struct cpcap_rgb_led rgb_led;
+ struct cpcap_als_data als_data;
+};
+
+struct cpcap_batt_data {
+ int status;
+ int health;
+ int present;
+ int capacity;
+ int batt_volt;
+ int batt_temp;
+ int batt_full_capacity;
+ int batt_capacity_one;
+ int cycle_count;
+ int timestamp;
+ unsigned long charge_cycle_counter;
+ unsigned char charge_cycle_counter_percentage;
+};
+
+struct cpcap_batt_ac_data {
+ int online;
+};
+
+struct cpcap_batt_usb_data {
+ int online;
+ int current_now;
+ enum cpcap_batt_usb_model model;
+};
+
+struct cpcap_rtc_time_cnt {
+ struct rtc_time time;
+ unsigned short count;
+};
+
+struct cpcap_device;
+
+#ifdef __KERNEL__
+struct cpcap_platform_data {
+ struct cpcap_spi_init_data *init;
+ int init_len;
+ unsigned short *regulator_mode_values;
+ unsigned short *regulator_off_mode_values;
+ struct regulator_init_data *regulator_init;
+ struct cpcap_adc_ato *adc_ato;
+ struct cpcap_leds *leds;
+ void (*ac_changed)(struct power_supply *,
+ struct cpcap_batt_ac_data *);
+ void (*batt_changed)(struct power_supply *,
+ struct cpcap_batt_data *);
+ void (*usb_changed)(struct power_supply *,
+ struct cpcap_batt_usb_data *);
+ u16 hwcfg[CPCAP_HWCFG_NUM];
+ unsigned short is_umts;
+ unsigned int irq_gpio;
+};
+
+struct cpcap_adc_request {
+ enum cpcap_adc_format format;
+ enum cpcap_adc_timing timing;
+ enum cpcap_adc_type type;
+ int status;
+ int result[CPCAP_ADC_BANK0_NUM];
+ void (*callback)(struct cpcap_device *, void *);
+ void *callback_param;
+
+ /* Used in case of sync requests */
+ struct completion completion;
+};
+#endif
+
+struct cpcap_adc_us_request {
+ enum cpcap_adc_format format;
+ enum cpcap_adc_timing timing;
+ enum cpcap_adc_type type;
+ int status;
+ int result[CPCAP_ADC_BANK0_NUM];
+};
+
+struct cpcap_adc_phase {
+ signed char offset_batti;
+ unsigned char slope_batti;
+ signed char offset_chrgi;
+ unsigned char slope_chrgi;
+ signed char offset_battp;
+ unsigned char slope_battp;
+ signed char offset_bp;
+ unsigned char slope_bp;
+ signed char offset_battt;
+ unsigned char slope_battt;
+ signed char offset_chrgv;
+ unsigned char slope_chrgv;
+};
+
+struct cpcap_regacc {
+ unsigned short reg;
+ unsigned short value;
+ unsigned short mask;
+};
+
+/*
+ * Gets the contents of the specified cpcap register.
+ *
+ * INPUTS: The register number in the cpcap driver's format.
+ *
+ * OUTPUTS: The command writes the register data back to user space at the
+ * location specified, or it may return an error code.
+ */
+#define CPCAP_IOCTL_GET_RTC_TIME_COUNTER \
+ _IOR(0, CPCAP_IOCTL_NUM_RTC_COUNT, struct cpcap_rtc_time_cnt)
+
+#define CPCAP_IOCTL_TEST_READ_REG \
+ _IOWR(0, CPCAP_IOCTL_NUM_TEST_READ_REG, struct cpcap_regacc*)
+
+/*
+ * Writes the specifed cpcap register.
+ *
+ * This function writes the specified cpcap register with the specified
+ * data.
+ *
+ * INPUTS: The register number in the cpcap driver's format and the data to
+ * write to that register.
+ *
+ * OUTPUTS: The command has no output other than the returned error code for
+ * the ioctl() call.
+ */
+#define CPCAP_IOCTL_TEST_WRITE_REG \
+ _IOWR(0, CPCAP_IOCTL_NUM_TEST_WRITE_REG, struct cpcap_regacc*)
+
+#define CPCAP_IOCTL_ADC_PHASE \
+ _IOWR(0, CPCAP_IOCTL_NUM_ADC_PHASE, struct cpcap_adc_phase*)
+
+#define CPCAP_IOCTL_BATT_DISPLAY_UPDATE \
+ _IOW(0, CPCAP_IOCTL_NUM_BATT_DISPLAY_UPDATE, struct cpcap_batt_data*)
+
+#define CPCAP_IOCTL_BATT_ATOD_ASYNC \
+ _IOW(0, CPCAP_IOCTL_NUM_BATT_ATOD_ASYNC, struct cpcap_adc_us_request*)
+
+#define CPCAP_IOCTL_BATT_ATOD_SYNC \
+ _IOWR(0, CPCAP_IOCTL_NUM_BATT_ATOD_SYNC, struct cpcap_adc_us_request*)
+
+#define CPCAP_IOCTL_BATT_ATOD_READ \
+ _IOWR(0, CPCAP_IOCTL_NUM_BATT_ATOD_READ, struct cpcap_adc_us_request*)
+
+
+#define CPCAP_IOCTL_UC_MACRO_START \
+ _IOWR(0, CPCAP_IOCTL_NUM_UC_MACRO_START, enum cpcap_macro)
+
+#define CPCAP_IOCTL_UC_MACRO_STOP \
+ _IOWR(0, CPCAP_IOCTL_NUM_UC_MACRO_STOP, enum cpcap_macro)
+
+#define CPCAP_IOCTL_UC_GET_VENDOR \
+ _IOWR(0, CPCAP_IOCTL_NUM_UC_GET_VENDOR, enum cpcap_vendor)
+
+#define CPCAP_IOCTL_UC_SET_TURBO_MODE \
+ _IOW(0, CPCAP_IOCTL_NUM_UC_SET_TURBO_MODE, unsigned short)
+
+#define CPCAP_IOCTL_ACCY_WHISPER \
+ _IOW(0, CPCAP_IOCTL_NUM_ACCY_WHISPER, unsigned long)
+
+#define CPCAP_IOCTL_AUDIO_PWR_MODE \
+ _IOW(0, CPCAP_IOCTL_NUM_AUDIO_PWR_MODE, unsigned short)
+
+#define CPCAP_IOCTL_AUDIO_PWR_ENABLE \
+ _IOW(0, CPCAP_IOCTL_NUM_AUDIO_PWR_ENABLE, unsigned short)
+
+#ifdef __KERNEL__
+struct cpcap_device {
+ struct spi_device *spi;
+ enum cpcap_vendor vendor;
+ enum cpcap_revision revision;
+ void *keydata;
+ struct platform_device *regulator_pdev[CPCAP_NUM_REGULATORS];
+ void *irqdata;
+ void *adcdata;
+ void *battdata;
+ void *ucdata;
+ void (*h2w_new_state)(int);
+};
+
+static inline void cpcap_set_keydata(struct cpcap_device *cpcap, void *data)
+{
+ cpcap->keydata = data;
+}
+
+static inline void *cpcap_get_keydata(struct cpcap_device *cpcap)
+{
+ return cpcap->keydata;
+}
+
+int cpcap_regacc_write(struct cpcap_device *cpcap, enum cpcap_reg reg,
+ unsigned short value, unsigned short mask);
+
+int cpcap_regacc_read(struct cpcap_device *cpcap, enum cpcap_reg reg,
+ unsigned short *value_ptr);
+
+int cpcap_regacc_init(struct cpcap_device *cpcap);
+
+void cpcap_broadcast_key_event(struct cpcap_device *cpcap,
+ unsigned int code, int value);
+
+int cpcap_irq_init(struct cpcap_device *cpcap);
+
+void cpcap_irq_shutdown(struct cpcap_device *cpcap);
+
+int cpcap_irq_register(struct cpcap_device *cpcap, enum cpcap_irqs irq,
+ void (*cb_func) (enum cpcap_irqs, void *), void *data);
+
+int cpcap_irq_free(struct cpcap_device *cpcap, enum cpcap_irqs irq);
+
+int cpcap_irq_get_data(struct cpcap_device *cpcap, enum cpcap_irqs irq,
+ void **data);
+
+int cpcap_irq_clear(struct cpcap_device *cpcap, enum cpcap_irqs int_event);
+
+int cpcap_irq_mask(struct cpcap_device *cpcap, enum cpcap_irqs int_event);
+
+int cpcap_irq_unmask(struct cpcap_device *cpcap, enum cpcap_irqs int_event);
+
+int cpcap_irq_mask_get(struct cpcap_device *cpcap, enum cpcap_irqs int_event);
+
+int cpcap_irq_sense(struct cpcap_device *cpcap, enum cpcap_irqs int_event,
+ unsigned char clear);
+
+int cpcap_adc_sync_read(struct cpcap_device *cpcap,
+ struct cpcap_adc_request *request);
+
+int cpcap_adc_async_read(struct cpcap_device *cpcap,
+ struct cpcap_adc_request *request);
+
+void cpcap_adc_phase(struct cpcap_device *cpcap, struct cpcap_adc_phase *phase);
+
+void cpcap_batt_set_ac_prop(struct cpcap_device *cpcap, int online);
+
+void cpcap_batt_set_usb_prop_online(struct cpcap_device *cpcap, int online,
+ enum cpcap_batt_usb_model model);
+
+void cpcap_batt_set_usb_prop_curr(struct cpcap_device *cpcap,
+ unsigned int curr);
+
+int cpcap_uc_start(struct cpcap_device *cpcap, enum cpcap_macro macro);
+
+int cpcap_uc_stop(struct cpcap_device *cpcap, enum cpcap_macro macro);
+
+unsigned char cpcap_uc_status(struct cpcap_device *cpcap,
+ enum cpcap_macro macro);
+
+int cpcap_disable_offmode_wakeups(bool disable);
+
+#ifdef CONFIG_PM_DBG_DRV
+void cpcap_irq_pm_dbg_suspend(void);
+void cpcap_irq_pm_dbg_resume(void);
+int cpcap_uc_ram_write(struct cpcap_device *cpcap, unsigned short address,
+ unsigned short num_words, unsigned short *data);
+
+int cpcap_uc_ram_read(struct cpcap_device *cpcap, unsigned short address,
+ unsigned short num_words, unsigned short *data);
+#endif /* CONFIG_PM_DBG_DRV */
+
+#define cpcap_driver_register platform_driver_register
+#define cpcap_driver_unregister platform_driver_unregister
+
+int cpcap_device_register(struct platform_device *pdev);
+int cpcap_device_unregister(struct platform_device *pdev);
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_SPI_CPCAP_H */
diff --git a/include/linux/vib-gpio.h b/include/linux/vib-gpio.h
new file mode 100644
index 00000000000..3706aa0d391
--- /dev/null
+++ b/include/linux/vib-gpio.h
@@ -0,0 +1,39 @@
+/* include/linux/timed_gpio.h
+ *
+ * Copyright (C) 2008 Google, Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+*/
+
+#ifndef _LINUX_VIB_GPIO_H
+#define _LINUX_VIB_GPIO_H
+
+#ifdef __KERNEL__
+
+#define VIB_GPIO_NAME "vib-gpio"
+
+struct vib_gpio_platform_data {
+ int gpio;
+ int max_timeout;
+ u8 active_low;
+ int initial_vibrate;
+
+ int (*init)(void);
+ void (*exit)(void);
+ int (*power_on)(void);
+ int (*power_off)(void);
+};
+
+#endif /* __KERNEL__ */
+
+void vibrator_haptic_fire(int value);
+
+#endif /* _LINUX_VIB_GPIO_H */
diff --git a/include/linux/wl127x-rfkill.h b/include/linux/wl127x-rfkill.h
new file mode 100644
index 00000000000..4b8528400ad
--- /dev/null
+++ b/include/linux/wl127x-rfkill.h
@@ -0,0 +1,47 @@
+/*
+ * Bluetooth TI wl127x rfkill power control via GPIO
+ *
+ * Copyright (C) 2009 Motorola, Inc.
+ * Copyright (C) 2008 Texas Instruments
+ * Initial code: Pavan Savoy <pavan.savoy@gmail.com> (wl127x_power.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
+ *
+ */
+
+#ifndef _LINUX_WL127X_RFKILL_H
+#define _LINUX_WL127X_RFKILL_H
+
+#include <linux/rfkill.h>
+
+enum wl127x_devices {
+ WL127X_BLUETOOTH = 0,
+ WL127X_FM,
+ WL127X_MAX_DEV,
+};
+
+/* Set bt_nshutdown_gpio or fm_enable_gpio to -1 to disable the corresponding
+ * rfkill driver */
+struct wl127x_rfkill_platform_data {
+ int bt_nshutdown_gpio;
+ int fm_enable_gpio;
+ int (*bt_hw_init)(void);
+ int (*bt_hw_release)(void);
+ int (*bt_hw_enable)(void);
+ int (*bt_hw_disable)(void);
+ struct rfkill *rfkill[WL127X_MAX_DEV]; /* for driver only */
+};
+
+#endif
diff --git a/include/uapi/linux/fb.h b/include/uapi/linux/fb.h
index fb795c3b3c1..18a657838b1 100644
--- a/include/uapi/linux/fb.h
+++ b/include/uapi/linux/fb.h
@@ -16,6 +16,7 @@
#define FBIOGETCMAP 0x4604
#define FBIOPUTCMAP 0x4605
#define FBIOPAN_DISPLAY 0x4606
+#define FBIO_UPDATE_DISPLAY 0x4607
#ifndef __KERNEL__
#define FBIO_CURSOR _IOWR('F', 0x08, struct fb_cursor)
#endif
diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
index 93a956489e8..18131ef2438 100644
--- a/include/uapi/linux/input.h
+++ b/include/uapi/linux/input.h
@@ -468,6 +468,7 @@ struct input_keymap_entry {
#define KEY_RFKILL 247 /* Key that controls all radios */
#define KEY_MICMUTE 248 /* Mute / unmute the microphone */
+#define KEY_POWER_DOUBLE 249
/* Code 255 is reserved for special needs of AT keyboard driver */
@@ -709,6 +710,7 @@ struct input_keymap_entry {
#define KEY_ATTENDANT_OFF 0x21c
#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */
#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */
+#define KEY_POWER_SONG 0x220
#define BTN_TRIGGER_HAPPY 0x2c0
#define BTN_TRIGGER_HAPPY1 0x2c0
@@ -771,7 +773,22 @@ struct input_keymap_entry {
#define REL_DIAL 0x07
#define REL_WHEEL 0x08
#define REL_MISC 0x09
-#define REL_MAX 0x0f
+#define REL_ROLL 0x0a
+#define REL_PITCH 0x0b
+#define REL_YAW 0x0c
+#define REL_LX 0x0d
+#define REL_LY 0x0e
+#define REL_LZ 0x0f
+#define REL_WX 0x10
+#define REL_WY 0x11
+#define REL_WZ 0x12
+#define REL_GX 0x13
+#define REL_GY 0x14
+#define REL_GZ 0x15
+#define REL_HEADING 0x16
+#define REL_HEADING_ACCURACY 0x17
+#define REL_ACTIVITY_LEVEL 0x18
+#define REL_MAX 0x1f
#define REL_CNT (REL_MAX+1)
/*
@@ -824,7 +841,33 @@ struct input_keymap_entry {
#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
-#define ABS_MAX 0x3f
+/* MSP (sensor) related events */
+#define ABS_STEPCOUNT 0x40
+#define ABS_ACTIVITY 0x41
+#define ABS_SPEED 0x42
+#define ABS_CADENCE 0x43
+#define ABS_ALTITUDE 0x44
+#define ABS_PRESSURE_PASCAL 0x45
+#define ABS_ASCENT 0x46
+#define ABS_DESCENT 0x47
+#define ABS_INCLINATION 0x48
+#define ABS_STEPLENGTH 0x49
+#define ABS_3D_TAP_TAP 0x4a
+#define ABS_HEADING 0x4b
+#define ABS_TEMPERATURE 0x4c
+#define ABS_MSP_LATITUDE 0x4d
+#define ABS_MSP_LONGITUDE 0x4e
+#define ABS_MSP_HEADING 0x4f
+#define ABS_MSP_ACCURACY 0x50
+
+#define ABS_COMPASS_X 0x51
+#define ABS_COMPASS_Y 0x52
+#define ABS_COMPASS_Z 0x53
+#define ABS_COMPASS_ACCURACY 0x54
+
+#define ABS_TILTSCROLL 0x55
+
+#define ABS_MAX 0x7f
#define ABS_CNT (ABS_MAX+1)
/*
@@ -847,6 +890,7 @@ struct input_keymap_entry {
#define SW_FRONT_PROXIMITY 0x0b /* set = front proximity sensor active */
#define SW_ROTATE_LOCK 0x0c /* set = rotate locked/disabled */
#define SW_LINEIN_INSERT 0x0d /* set = inserted */
+#define SW_STILL_MODE 0x0e /* set = device is sitting still */
#define SW_MAX 0x0f
#define SW_CNT (SW_MAX+1)
@@ -860,7 +904,29 @@ struct input_keymap_entry {
#define MSC_RAW 0x03
#define MSC_SCAN 0x04
#define MSC_TIMESTAMP 0x05
-#define MSC_MAX 0x07
+#define MSC_GESTURE1 0x06
+#define MSC_GESTURE2 0x07
+#define MSC_GESTURE3 0x08
+#define MSC_GESTURE_CONFIDENCE1 0x09
+#define MSC_GESTURE_CONFIDENCE2 0x0a
+#define MSC_GESTURE_CONFIDENCE3 0x0b
+#define MSC_GESTURE_VALUE1 0x0c
+#define MSC_GESTURE_VALUE2 0x0d
+#define MSC_GESTURE_VALUE3 0x0e
+#define MSC_STEPCOUNT 0x0f
+#define MSC_DISTANCE 0x10
+#define MSC_SPEED 0x11
+#define MSC_ACTIVITY_TYPE 0x12
+#define MSC_METS 0x13
+#define MSC_CALORIES 0x14
+#define MSC_METSACTIVITY 0x15
+#define MSC_FLOORSCLIMBED 0x16
+#define MSC_PASSIVE_STEPS 0x17
+#define MSC_PASSIVE_METS 0x18
+#define MSC_PASSIVE_TIMESTAMP 0x19
+#define MSC_PASSIVE_FLOORSCLIMBED 0x1a
+
+#define MSC_MAX 0x1f
#define MSC_CNT (MSC_MAX+1)
/*
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 86ed2395e58..e21ce177c2e 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -162,6 +162,15 @@ config DEBUG_KERNEL
Say Y here if you are developing drivers or trying to debug and
identify kernel problems.
+config TOUCHSCREEN_DEBUG
+ bool "Compile kernel with touchscreen debug interface"
+ depends on DEBUG_KERNEL
+
+ help
+ Say Y here if you want access to the touchscreen debug interface.
+
+ If unsure, say N.
+
config DEBUG_SHIRQ
bool "Debug shared IRQ handlers"
depends on DEBUG_KERNEL && GENERIC_HARDIRQS
diff --git a/net/bluetooth/hidp/core.c b/net/bluetooth/hidp/core.c
index 940f5acb669..88edc8f1c0d 100644
--- a/net/bluetooth/hidp/core.c
+++ b/net/bluetooth/hidp/core.c
@@ -1263,7 +1263,7 @@ int hidp_connection_add(struct hidp_connadd_req *req,
struct socket *ctrl_sock,
struct socket *intr_sock)
{
- struct hidp_session *session;
+ struct hidp_session *session = NULL;
struct l2cap_conn *conn;
struct l2cap_chan *chan = l2cap_pi(ctrl_sock->sk)->chan;
int ret;
diff --git a/net/rfkill/Kconfig b/net/rfkill/Kconfig
index 8e12c8a2b82..383ca3a2248 100644
--- a/net/rfkill/Kconfig
+++ b/net/rfkill/Kconfig
@@ -47,3 +47,12 @@ config RFKILL_GPIO
If you say yes here you get support of a generic gpio RFKILL
driver. The platform should fill in the appropriate fields in the
rfkill_gpio_platform_data structure and pass that to the driver.
+
+config RFKILL_WL127X
+ tristate "Bluetooth power control driver for TI wl127x"
+ depends on RFKILL
+ default n
+ ---help---
+ Creates an rfkill entry in sysfs for power control of Bluetooth
+ TI wl127x chips.
+
diff --git a/net/rfkill/Makefile b/net/rfkill/Makefile
index 311768783f4..4cfa25a8688 100644
--- a/net/rfkill/Makefile
+++ b/net/rfkill/Makefile
@@ -7,3 +7,4 @@ rfkill-$(CONFIG_RFKILL_INPUT) += input.o
obj-$(CONFIG_RFKILL) += rfkill.o
obj-$(CONFIG_RFKILL_REGULATOR) += rfkill-regulator.o
obj-$(CONFIG_RFKILL_GPIO) += rfkill-gpio.o
+obj-$(CONFIG_RFKILL_WL127X) += wl127x-rfkill.o
diff --git a/net/rfkill/wl127x-rfkill.c b/net/rfkill/wl127x-rfkill.c
new file mode 100644
index 00000000000..9d15d3996a1
--- /dev/null
+++ b/net/rfkill/wl127x-rfkill.c
@@ -0,0 +1,288 @@
+/*
+ * Bluetooth TI wl127x rfkill power control via GPIO
+ *
+ * Copyright (C) 2009 Motorola, Inc.
+ * Copyright (C) 2008 Texas Instruments
+ * Initial code: Pavan Savoy <pavan.savoy@gmail.com> (wl127x_power.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/init.h>
+#include <linux/gpio.h>
+#include <linux/rfkill.h>
+#include <linux/platform_device.h>
+#include <linux/wl127x-rfkill.h>
+#include <linux/delay.h>
+
+static int wl127x_bt_rfkill_set_power(void *data, bool blocked)
+{
+ struct wl127x_rfkill_platform_data *pdata =
+ (struct wl127x_rfkill_platform_data *) data;
+ int nshutdown_gpio = pdata->bt_nshutdown_gpio;
+
+ if (blocked) {
+ gpio_set_value(nshutdown_gpio, 0);
+ if (pdata->bt_hw_disable)
+ pdata->bt_hw_disable();
+ } else {
+ if (pdata->bt_hw_enable)
+ pdata->bt_hw_enable();
+ gpio_set_value(nshutdown_gpio, 1);
+ }
+ return 0;
+}
+
+static int wl127x_fm_rfkill_set_power(void *data, bool blocked)
+{
+ int nshutdown_gpio = (int) data;
+
+ if (blocked)
+ gpio_set_value(nshutdown_gpio, 0);
+ else
+ gpio_set_value(nshutdown_gpio, 1);
+
+ return 0;
+}
+
+static const struct rfkill_ops wl127x_bt_rfkill_ops = {
+ .set_block = wl127x_bt_rfkill_set_power,
+};
+
+static const struct rfkill_ops wl127x_fm_rfkill_ops = {
+ .set_block = wl127x_fm_rfkill_set_power,
+};
+
+
+
+/**
+Added to reset the BT chip after Ram download
+*/
+static ssize_t reset_wl18xx_chip(struct device *dev,
+ struct device_attribute
+ *attr, const char *buf, size_t size)
+{
+
+ int bt_enable_gpio;
+ printk(KERN_DEBUG "Calling reset_wl18xx_chip \n");
+
+ /* TODO, rework once device tree is pulled in */
+ /* bt_enable_gpio = get_gpio_by_name("bt_reset_b"); */
+ bt_enable_gpio = 83;
+
+ printk(KERN_DEBUG "bt_enable_gpio = %d\n", bt_enable_gpio);
+
+ if (bt_enable_gpio < 0) {
+
+ printk(KERN_DEBUG "reset_wl18xx_chip: cannot retrieve bt_reset_b gpio from device tree\n");
+ bt_enable_gpio = -1;
+ return -EINVAL;
+ }
+
+ gpio_set_value(bt_enable_gpio, 0);
+ msleep(5);
+ gpio_set_value(bt_enable_gpio, 1);
+ printk(KERN_DEBUG " successfully set the value\n");
+
+ return 0;
+
+}
+
+static DEVICE_ATTR(reset_vio, 0644, NULL, reset_wl18xx_chip);
+
+
+static int wl127x_rfkill_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ bool fm_deinit_required_flag = false;
+ struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data;
+
+ if (pdata->bt_nshutdown_gpio >= 0) {
+ bool default_blocked = true; /* power off */
+ printk(KERN_DEBUG "Entered the bt enable part \n");
+ rc = gpio_request(pdata->bt_nshutdown_gpio,
+ "wl127x_bt_nshutdown_gpio");
+ if (unlikely(rc))
+ return rc;
+
+ rc = gpio_direction_output(pdata->bt_nshutdown_gpio, 0);
+ if (unlikely(rc)) {
+ printk(KERN_ERR "wl127x_rfkill_probe failure could not set output direction for gpio bt_nshutdown_gpio \n");
+ goto bt_err_gpio_direction;
+ }
+
+ if (pdata->bt_hw_init)
+ rc = pdata->bt_hw_init();
+ if (unlikely(rc)) {
+ printk(KERN_ERR "wl127x_rfkill_probe failure could not init hardware \n");
+ goto bt_err_gpio_direction;
+ }
+
+
+ wl127x_bt_rfkill_set_power((void *)pdata, default_blocked);
+
+ pdata->rfkill[WL127X_BLUETOOTH] = rfkill_alloc(
+ "wl127x Bluetooth", &pdev->dev,
+ RFKILL_TYPE_BLUETOOTH, &wl127x_bt_rfkill_ops,
+ (void *)pdata);
+ if (unlikely(!pdata->rfkill[WL127X_BLUETOOTH])) {
+ printk(KERN_ERR "wl127x_rfkill_probe failure could not allocate memory \n");
+ rc = -ENOMEM;
+ goto bt_err_rfkill_alloc;
+ }
+
+ rfkill_set_states(pdata->rfkill[WL127X_BLUETOOTH],
+ default_blocked, false);
+
+ rc = rfkill_register(pdata->rfkill[WL127X_BLUETOOTH]);
+ if (unlikely(rc)) {
+ printk(KERN_ERR "wl127x_rfkill_probe failure could to register BT \n");
+ goto bt_err_rfkill_register;
+ }
+
+ }
+
+ if (pdata->fm_enable_gpio >= 0) {
+ bool default_blocked = true; /* power off */
+ printk(KERN_DEBUG "Entered the fm enable part \n");
+ rc = gpio_request(pdata->fm_enable_gpio,
+ "wl127x_fm_enable_gpio");
+ if (unlikely(rc))
+ return rc;
+
+ rc = gpio_direction_output(pdata->fm_enable_gpio, 0);
+ if (unlikely(rc)) {
+ printk(KERN_ERR "wl127x_rfkill_probe failure could not set output direction for gpio fm_enable_gpio \n");
+ goto fm_err_gpio_direction;
+ }
+
+ wl127x_fm_rfkill_set_power((void *)pdata->fm_enable_gpio,
+ default_blocked);
+
+ pdata->rfkill[WL127X_FM] = rfkill_alloc("wl127x FM Radio",
+ &pdev->dev, RFKILL_TYPE_FM,
+ &wl127x_fm_rfkill_ops,
+ (void *)pdata->fm_enable_gpio);
+ if (unlikely(!pdata->rfkill[WL127X_FM])) {
+ printk(KERN_ERR "wl127x_rfkill_probe failure could not allocate memory for fm\n");
+ rc = -ENOMEM;
+ goto fm_err_gpio_direction;
+
+ }
+
+ rfkill_set_states(pdata->rfkill[WL127X_FM], default_blocked,
+ false);
+
+ rc = rfkill_register(pdata->rfkill[WL127X_FM]);
+ if (unlikely(rc)) {
+ printk(KERN_ERR "wl127x_rfkill_probe failure could to register FM \n");
+ goto fm_err_rfkill_register;
+ }
+
+ fm_deinit_required_flag = true;
+ }
+
+ /* Create device file to expose interface to user space to
+ reset the vio of BT, this should be done independent of whether
+ BT/FM is initialised as both run on the same w18xx chip
+ */
+ if (device_create_file(&pdev->dev, &dev_attr_reset_vio)) {
+ printk(KERN_DEBUG "Error creating sys entry for reset vio\n");
+ rc = -1;
+ goto bt_err_rfkill_register;
+ }
+
+ goto done;
+
+ /* Clean up for BT generic registration */
+bt_err_rfkill_register:
+ rfkill_destroy(pdata->rfkill[WL127X_BLUETOOTH]);
+bt_err_rfkill_alloc:
+ if (pdata->bt_hw_release)
+ pdata->bt_hw_release();
+bt_err_gpio_direction:
+ if (pdata->bt_nshutdown_gpio >= 0)
+ gpio_free(pdata->bt_nshutdown_gpio);
+
+ if (fm_deinit_required_flag == false)
+ goto done;
+
+ fm_deinit_required_flag = false;
+
+ /* Clean up for FM generic registration
+ do not clean up BT process as we still want to use BT
+ */
+fm_err_rfkill_register:
+ rfkill_destroy(pdata->rfkill[WL127X_FM]);
+fm_err_gpio_direction:
+ gpio_free(pdata->fm_enable_gpio);
+
+done:
+ return rc;
+}
+
+static int wl127x_rfkill_remove(struct platform_device *pdev)
+{
+ struct wl127x_rfkill_platform_data *pdata = pdev->dev.platform_data;
+
+ if (pdata->bt_nshutdown_gpio >= 0) {
+ rfkill_unregister(pdata->rfkill[WL127X_BLUETOOTH]);
+ rfkill_destroy(pdata->rfkill[WL127X_BLUETOOTH]);
+ if (pdata->bt_hw_release)
+ pdata->bt_hw_release();
+ gpio_free(pdata->bt_nshutdown_gpio);
+ }
+
+
+ if (pdata->fm_enable_gpio >= 0) {
+ rfkill_unregister(pdata->rfkill[WL127X_FM]);
+ rfkill_destroy(pdata->rfkill[WL127X_FM]);
+ gpio_free(pdata->fm_enable_gpio);
+ }
+
+ /* remove the sys fs file created as part of probe*/
+ device_remove_file(&pdev->dev, &dev_attr_reset_vio);
+
+ return 0;
+}
+
+static struct platform_driver wl127x_rfkill_platform_driver = {
+ .probe = wl127x_rfkill_probe,
+ .remove = wl127x_rfkill_remove,
+ .driver = {
+ .name = "wl127x-rfkill",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init wl127x_rfkill_init(void)
+{
+ return platform_driver_register(&wl127x_rfkill_platform_driver);
+}
+
+static void __exit wl127x_rfkill_exit(void)
+{
+ platform_driver_unregister(&wl127x_rfkill_platform_driver);
+}
+
+module_init(wl127x_rfkill_init);
+module_exit(wl127x_rfkill_exit);
+
+MODULE_ALIAS("platform:wl127x");
+MODULE_DESCRIPTION("wl127x-rfkill");
+MODULE_AUTHOR("Motorola");
+MODULE_LICENSE("GPL");
diff --git a/net/wireless/Kconfig b/net/wireless/Kconfig
index 4c602d10048..a5cff0a24b6 100644
--- a/net/wireless/Kconfig
+++ b/net/wireless/Kconfig
@@ -1,5 +1,12 @@
config WIRELESS_EXT
- bool
+ bool "Wireless extensions"
+ default y
+ ---help---
+ This option enables the legacy wireless extensions
+ (wireless network interface configuration via ioctls.)
+
+ Say Y unless you've upgraded all your userspace to use
+ nl80211 instead of wireless extensions.
config WEXT_CORE
def_bool y
diff --git a/scripts/pre-commit b/scripts/pre-commit
new file mode 100644
index 00000000000..104908fa5c9
--- /dev/null
+++ b/scripts/pre-commit
@@ -0,0 +1,20 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by git-commit with no arguments. The hook should
+# exit with non-zero status after issuing an appropriate message if
+# it wants to stop the commit.
+#
+# To enable this hook, make this file executable.
+
+# This is slightly modified from Andrew Morton's Perfect Patch.
+# Lines you introduce should not have trailing whitespace.
+# Also check for an indentation that has SP before a TAB.
+if git-rev-parse --verify HEAD 2>/dev/null
+then
+ git-diff-index -p -M --cached HEAD
+else
+ # NEEDSWORK: we should produce a diff with an empty tree here
+ # if we want to do the same verification for the initial import.
+ :
+fi | scripts/checkpatch.pl --no-signoff --strict -