diff options
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 @@ -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, ®); + if (reg & 0x50) { + ret = lm3535_read_reg (LM3535_ALS_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, ®); + 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, ®); + 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(®_access); + + retval = cpcap_config_for_read(spi, register_info_tbl + [reg].address, value_ptr); + + mutex_unlock(®_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(®_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(®_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, ®val); + 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, ®val); + + if (regval & (1 << macro)) + retval = 1; + } else { + cpcap_regacc_read(cpcap, CPCAP_REG_MIM1, ®val); + + 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 *)¤tLogLevels); + while (1) { + tag = strsep(logbuf, "=,\n "); + if (tag == NULL) + break; + level = strsep(logbuf, "=,\n "); + if (level == NULL) + break; + ParseAndUpdateLogLevels(tag, level, ¤tLogLevels); + } + + return m4sensorhub_reg_write(m4sensorhub_misc_data, + M4SH_REG_LOG_LOGENABLE, (char *)¤tLogLevels, + 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(®_access); + ret = m4sensorhub_i2c_write_read(m4sensorhub, buf, 2, num); + mutex_unlock(®_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(®_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(®_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(®_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(®_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, ®_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,\ + ¬ify, 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 *)×tamp); + 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(®ulators[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 - |