aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/configs/tuna_defconfig429
-rw-r--r--arch/arm/mach-omap2/Kconfig9
-rw-r--r--arch/arm/mach-omap2/Makefile17
-rw-r--r--arch/arm/mach-omap2/board-omap4panda.c3
-rw-r--r--arch/arm/mach-omap2/board-tuna-bluetooth.c344
-rw-r--r--arch/arm/mach-omap2/board-tuna-connector.c982
-rw-r--r--arch/arm/mach-omap2/board-tuna-display.c1126
-rw-r--r--arch/arm/mach-omap2/board-tuna-emif.c108
-rw-r--r--arch/arm/mach-omap2/board-tuna-input.c185
-rw-r--r--arch/arm/mach-omap2/board-tuna-jack.c152
-rwxr-xr-xarch/arm/mach-omap2/board-tuna-modems.c760
-rw-r--r--arch/arm/mach-omap2/board-tuna-nfc.c148
-rw-r--r--arch/arm/mach-omap2/board-tuna-pogo.c480
-rw-r--r--arch/arm/mach-omap2/board-tuna-power.c533
-rwxr-xr-xarch/arm/mach-omap2/board-tuna-sensors.c212
-rw-r--r--arch/arm/mach-omap2/board-tuna-usbhost.c85
-rwxr-xr-xarch/arm/mach-omap2/board-tuna-vibrator.c185
-rw-r--r--arch/arm/mach-omap2/board-tuna-wifi.c437
-rw-r--r--arch/arm/mach-omap2/board-tuna.c1436
-rw-r--r--arch/arm/mach-omap2/board-tuna.h60
-rw-r--r--arch/arm/mach-omap2/dpll3xxx.c12
-rw-r--r--arch/arm/mach-omap2/dpll44xx.c6
-rw-r--r--arch/arm/mach-omap2/emif.c14
-rw-r--r--arch/arm/mach-omap2/include/mach/emif.h1
-rw-r--r--arch/arm/mach-omap2/include/mach/tf_mshield.h11
-rw-r--r--arch/arm/mach-omap2/io.c4
-rw-r--r--arch/arm/mach-omap2/omap2plus-cpufreq.c94
-rw-r--r--arch/arm/mach-omap2/omap4-common.c2
-rwxr-xr-x[-rw-r--r--]arch/arm/mach-omap2/omap_hsi.c212
-rw-r--r--arch/arm/mach-omap2/omap_hwmod.c1
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_44xx_data.c8
-rw-r--r--arch/arm/mach-omap2/omap_l3_noc.c49
-rw-r--r--arch/arm/mach-omap2/opp4xxx_data.c5
-rwxr-xr-x[-rw-r--r--]arch/arm/mach-omap2/pm44xx.c51
-rw-r--r--arch/arm/mach-omap2/usb-host.c8
-rw-r--r--arch/arm/plat-omap/include/plat/board-tuna-bluetooth.h30
-rw-r--r--arch/arm/plat-omap/include/plat/memory.h12
-rw-r--r--arch/arm/plat-omap/include/plat/omap_hsi.h50
-rw-r--r--arch/arm/plat-omap/include/plat/uncompress.h1
-rw-r--r--arch/arm/plat-omap/omap_rpmsg.c20
-rw-r--r--drivers/i2c/busses/i2c-omap.c54
-rw-r--r--drivers/input/misc/Kconfig7
-rw-r--r--drivers/input/misc/Makefile2
-rw-r--r--drivers/input/misc/gp2a.c639
-rw-r--r--drivers/input/touchscreen/Kconfig12
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c4
-rwxr-xr-xdrivers/input/touchscreen/mms_ts.c961
-rw-r--r--drivers/mfd/omap-usb-host.c3
-rw-r--r--drivers/mfd/twl6030-irq.c14
-rw-r--r--drivers/mfd/twl6030-madc.c7
-rw-r--r--drivers/mfd/twl6040-codec.c2
-rw-r--r--drivers/misc/Kconfig39
-rw-r--r--drivers/misc/Makefile6
-rwxr-xr-xdrivers/misc/bmp180.c644
-rwxr-xr-xdrivers/misc/fsa9480.c1067
-rw-r--r--drivers/misc/inv_mpu/Kconfig29
-rw-r--r--drivers/misc/inv_mpu/Makefile22
-rw-r--r--drivers/misc/inv_mpu/README276
-rw-r--r--drivers/misc/inv_mpu/accel/Kconfig133
-rw-r--r--drivers/misc/inv_mpu/accel/Makefile37
-rw-r--r--drivers/misc/inv_mpu/accel/adxl34x.c728
-rw-r--r--drivers/misc/inv_mpu/accel/bma150.c777
-rw-r--r--drivers/misc/inv_mpu/accel/bma222.c654
-rw-r--r--drivers/misc/inv_mpu/accel/bma250.c783
-rw-r--r--drivers/misc/inv_mpu/accel/cma3000.c222
-rw-r--r--drivers/misc/inv_mpu/accel/kxsd9.c264
-rw-r--r--drivers/misc/inv_mpu/accel/kxtf9.c841
-rw-r--r--drivers/misc/inv_mpu/accel/lis331.c740
-rw-r--r--drivers/misc/inv_mpu/accel/lis3dh.c738
-rw-r--r--drivers/misc/inv_mpu/accel/lsm303a.c878
-rw-r--r--drivers/misc/inv_mpu/accel/mma8450.c807
-rw-r--r--drivers/misc/inv_mpu/accel/mma845x.c713
-rw-r--r--drivers/misc/inv_mpu/accel/mpu6050.c433
-rw-r--r--drivers/misc/inv_mpu/accel/mpu6050.h28
-rw-r--r--drivers/misc/inv_mpu/compass/Kconfig112
-rw-r--r--drivers/misc/inv_mpu/compass/Makefile34
-rw-r--r--drivers/misc/inv_mpu/compass/ak8975.c500
-rw-r--r--drivers/misc/inv_mpu/compass/ami306.c1020
-rw-r--r--drivers/misc/inv_mpu/compass/ami30x.c308
-rw-r--r--drivers/misc/inv_mpu/compass/ami_hw.h87
-rw-r--r--drivers/misc/inv_mpu/compass/ami_sensor_def.h136
-rw-r--r--drivers/misc/inv_mpu/compass/hmc5883.c391
-rw-r--r--drivers/misc/inv_mpu/compass/hscdtd002b.c294
-rw-r--r--drivers/misc/inv_mpu/compass/hscdtd004a.c294
-rw-r--r--drivers/misc/inv_mpu/compass/lsm303m.c386
-rw-r--r--drivers/misc/inv_mpu/compass/mmc314x.c313
-rw-r--r--drivers/misc/inv_mpu/compass/yas529-kernel.c611
-rw-r--r--drivers/misc/inv_mpu/compass/yas530.c784
-rw-r--r--drivers/misc/inv_mpu/log.h287
-rw-r--r--drivers/misc/inv_mpu/mldl_cfg.c1808
-rw-r--r--drivers/misc/inv_mpu/mldl_cfg.h329
-rw-r--r--drivers/misc/inv_mpu/mlsl-kernel.c389
-rw-r--r--drivers/misc/inv_mpu/mlsl.h186
-rw-r--r--drivers/misc/inv_mpu/mltypes.h229
-rw-r--r--drivers/misc/inv_mpu/mpu-dev.c1309
-rw-r--r--drivers/misc/inv_mpu/mpu-dev.h36
-rw-r--r--drivers/misc/inv_mpu/mpu3050.h251
-rw-r--r--drivers/misc/inv_mpu/mpu6050a2.h420
-rw-r--r--drivers/misc/inv_mpu/mpu6050b1.h432
-rw-r--r--drivers/misc/inv_mpu/mpuirq.c253
-rw-r--r--drivers/misc/inv_mpu/mpuirq.h36
-rw-r--r--drivers/misc/inv_mpu/pressure/Kconfig20
-rw-r--r--drivers/misc/inv_mpu/pressure/Makefile7
-rw-r--r--drivers/misc/inv_mpu/pressure/bma085.c370
-rw-r--r--drivers/misc/inv_mpu/slaveirq.c262
-rw-r--r--drivers/misc/inv_mpu/slaveirq.h38
-rw-r--r--drivers/misc/inv_mpu/timerirq.c293
-rw-r--r--drivers/misc/inv_mpu/timerirq.h30
-rw-r--r--drivers/misc/leds-an30259a.c495
-rwxr-xr-xdrivers/misc/modem_if/Kconfig40
-rwxr-xr-xdrivers/misc/modem_if/Makefile8
-rwxr-xr-xdrivers/misc/modem_if/lte_modem_bootloader.c320
-rw-r--r--drivers/misc/modem_if/modem.c218
-rwxr-xr-xdrivers/misc/modem_if/modem_io_device.c924
-rwxr-xr-xdrivers/misc/modem_if/modem_link_device_dpram.c1006
-rwxr-xr-xdrivers/misc/modem_if/modem_link_device_dpram.h115
-rwxr-xr-xdrivers/misc/modem_if/modem_link_device_mipi.c1694
-rwxr-xr-xdrivers/misc/modem_if/modem_link_device_mipi.h168
-rwxr-xr-xdrivers/misc/modem_if/modem_link_device_usb.c873
-rw-r--r--drivers/misc/modem_if/modem_link_device_usb.h108
-rwxr-xr-xdrivers/misc/modem_if/modem_modemctl_device_cbp71.c234
-rw-r--r--drivers/misc/modem_if/modem_modemctl_device_cmc221.c254
-rwxr-xr-xdrivers/misc/modem_if/modem_modemctl_device_xmm6260.c213
-rwxr-xr-xdrivers/misc/modem_if/modem_net_flowcontrol_device.c115
-rwxr-xr-xdrivers/misc/modem_if/modem_prj.h236
-rwxr-xr-xdrivers/misc/modem_if/modem_variation.h119
-rwxr-xr-xdrivers/misc/sec_jack.c505
-rw-r--r--drivers/omap_hsi/hsi-char.c6
-rw-r--r--drivers/omap_hsi/hsi-if.c9
-rw-r--r--drivers/omap_hsi/hsi-if.h1
-rwxr-xr-x[-rw-r--r--]drivers/omap_hsi/hsi_driver.c197
-rw-r--r--drivers/omap_hsi/hsi_driver.h49
-rw-r--r--drivers/omap_hsi/hsi_driver_debugfs.c2
-rw-r--r--drivers/omap_hsi/hsi_driver_dma.c23
-rw-r--r--drivers/omap_hsi/hsi_driver_fifo.c37
-rw-r--r--drivers/omap_hsi/hsi_driver_if.c149
-rw-r--r--drivers/omap_hsi/hsi_driver_int.c198
-rw-r--r--drivers/power/max17040_battery.c497
-rw-r--r--drivers/remoteproc/omap_remoteproc.c18
-rw-r--r--drivers/rpmsg/rpmsg_omx.c2
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c4
-rw-r--r--drivers/usb/core/driver.c16
-rw-r--r--drivers/usb/core/hub.c27
-rw-r--r--drivers/usb/core/quirks.c4
-rw-r--r--drivers/usb/gadget/android.c16
-rw-r--r--drivers/usb/gadget/f_dm.c306
-rw-r--r--drivers/usb/host/ehci-dbg.c75
-rw-r--r--drivers/usb/host/ehci-hub.c158
-rwxr-xr-x[-rw-r--r--]drivers/usb/host/ehci-omap.c90
-rw-r--r--drivers/usb/host/ehci-q.c43
-rw-r--r--drivers/usb/host/ehci.h28
-rw-r--r--drivers/usb/musb/musb_core.c35
-rw-r--r--drivers/usb/musb/musb_core.h2
-rw-r--r--drivers/usb/musb/musb_gadget.c56
-rw-r--r--drivers/usb/musb/musb_host.c5
-rw-r--r--drivers/usb/musb/musb_regs.h4
-rw-r--r--drivers/usb/musb/omap2430.c8
-rw-r--r--drivers/usb/otg/otg-wakelock.c2
-rw-r--r--drivers/video/Kconfig7
-rw-r--r--drivers/video/Makefile2
-rw-r--r--drivers/video/omap2/displays/Kconfig7
-rw-r--r--drivers/video/omap2/displays/Makefile1
-rw-r--r--drivers/video/omap2/displays/panel-generic-dpi.c25
-rw-r--r--drivers/video/omap2/displays/panel-s6e8aa0.c1970
-rw-r--r--drivers/video/sii9234.c1438
-rw-r--r--include/linux/gp2a.h34
-rw-r--r--include/linux/hsi_char.h49
-rw-r--r--include/linux/hsi_driver_if.h20
-rw-r--r--include/linux/i2c/twl.h6
-rw-r--r--include/linux/leds-an30259a.h76
-rw-r--r--include/linux/max17040_battery.h16
-rw-r--r--include/linux/mpu.h330
-rw-r--r--include/linux/platform_data/fsa9480.h73
-rwxr-xr-xinclude/linux/platform_data/lte_modem_bootloader.h40
-rw-r--r--include/linux/platform_data/mms_ts.h34
-rwxr-xr-xinclude/linux/platform_data/modem.h91
-rw-r--r--include/linux/platform_data/panel-s6e8aa0.h124
-rw-r--r--include/linux/remoteproc.h2
-rw-r--r--include/linux/rproc_drm.h45
-rwxr-xr-xinclude/linux/sec_jack.h60
-rw-r--r--include/linux/sii9234.h56
-rw-r--r--include/linux/usb.h1
-rw-r--r--include/linux/usb/quirks.h6
-rw-r--r--security/Kconfig1
-rw-r--r--security/smc/Kconfig4
-rw-r--r--security/smc/Makefile49
-rw-r--r--security/smc/bridge_pub2sec.S242
-rw-r--r--security/smc/omap4/Makefile35
-rw-r--r--security/smc/omap4/scx_protocol.h676
-rw-r--r--security/smc/omap4/scx_public_crypto.c355
-rw-r--r--security/smc/omap4/scx_public_dma.c137
-rw-r--r--security/smc/omap4/scx_public_dma.h78
-rw-r--r--security/smc/omap4/scxlnx_comm_mshield.c97
-rw-r--r--security/smc/omap4/scxlnx_device.c89
-rw-r--r--security/smc/rproc_drm.c103
-rw-r--r--security/smc/s_version.h92
-rw-r--r--security/smc/tee_client_api.h189
-rw-r--r--security/smc/tee_client_api_ex.h59
-rw-r--r--security/smc/tee_client_api_imp.h68
-rw-r--r--security/smc/tf_comm.c1746
-rw-r--r--security/smc/tf_comm.h204
-rw-r--r--security/smc/tf_comm_mshield.c1013
-rw-r--r--security/smc/tf_conn.c1647
-rw-r--r--security/smc/tf_conn.h87
-rw-r--r--security/smc/tf_crypto.c1278
-rw-r--r--security/smc/tf_crypto.h (renamed from security/smc/omap4/scx_public_crypto.h)177
-rw-r--r--security/smc/tf_crypto_aes.c (renamed from security/smc/omap4/scx_public_crypto_AES.c)722
-rw-r--r--security/smc/tf_crypto_des.c404
-rw-r--r--security/smc/tf_crypto_digest.c (renamed from security/smc/omap4/scx_public_crypto_Digest.c)602
-rw-r--r--security/smc/tf_defs.h (renamed from security/smc/omap4/scxlnx_defs.h)292
-rw-r--r--security/smc/tf_device.c728
-rw-r--r--security/smc/tf_device_mshield.c351
-rw-r--r--security/smc/tf_dma.c106
-rw-r--r--security/smc/tf_dma.h64
-rw-r--r--security/smc/tf_protocol.h674
-rw-r--r--security/smc/tf_teec.c624
-rw-r--r--security/smc/tf_teec.h (renamed from security/smc/omap4/scxlnx_util.c)34
-rw-r--r--security/smc/tf_util.c1145
-rw-r--r--security/smc/tf_util.h (renamed from security/smc/omap4/scxlnx_util.h)63
-rw-r--r--security/smc/tf_zebra.h (renamed from security/smc/omap4/scxlnx_mshield.h)24
-rw-r--r--sound/soc/omap/Kconfig2
-rw-r--r--sound/soc/omap/omap-abe.c4
-rwxr-xr-xsound/soc/omap/sdp4430.c196
224 files changed, 58940 insertions, 2848 deletions
diff --git a/arch/arm/configs/tuna_defconfig b/arch/arm/configs/tuna_defconfig
new file mode 100644
index 0000000..ac5e41a
--- /dev/null
+++ b/arch/arm/configs/tuna_defconfig
@@ -0,0 +1,429 @@
+CONFIG_EXPERIMENTAL=y
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_CGROUPS=y
+CONFIG_CGROUP_DEBUG=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_RESOURCE_COUNTERS=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_PANIC_TIMEOUT=5
+CONFIG_KALLSYMS_ALL=y
+CONFIG_ASHMEM=y
+# CONFIG_AIO is not set
+CONFIG_EMBEDDED=y
+# CONFIG_PERF_EVENTS is not set
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+CONFIG_ARCH_OMAP=y
+CONFIG_OMAP_SMARTREFLEX=y
+CONFIG_OMAP_SMARTREFLEX_CLASS3=y
+CONFIG_OMAP_SMARTREFLEX_CLASS1P5=y
+CONFIG_OMAP_RESET_CLOCKS=y
+CONFIG_OMAP_TEMP_SENSOR=y
+CONFIG_OMAP_REMOTEPROC_MEMPOOL_SIZE=0x0
+# CONFIG_ARCH_OMAP2 is not set
+# CONFIG_ARCH_OMAP3 is not set
+# CONFIG_MACH_OMAP_4430SDP is not set
+CONFIG_OMAP_ALLOW_OSWR=y
+CONFIG_ARM_THUMBEE=y
+CONFIG_PL310_ERRATA_727915=y
+CONFIG_FIQ_DEBUGGER_CONSOLE=y
+CONFIG_ARM_ERRATA_764369=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
+CONFIG_PREEMPT=y
+CONFIG_HIGHMEM=y
+CONFIG_COMPACTION=y
+CONFIG_ARM_FLUSH_CONSOLE_ON_RESTART=y
+CONFIG_CMDLINE="console=ttyFIQ0 androidboot.console=ttyFIQ0 mem=1G vmalloc=768M omap_wdt.timer_margin=30 no_console_suspend"
+CONFIG_CMDLINE_EXTEND=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
+CONFIG_CPU_FREQ_GOV_HOTPLUG=y
+CONFIG_CPU_IDLE=y
+# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
+CONFIG_BINFMT_MISC=y
+CONFIG_WAKELOCK=y
+CONFIG_PM_DEBUG=y
+CONFIG_SUSPEND_TIME=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=y
+CONFIG_INET6_ESP=y
+CONFIG_INET6_IPCOMP=y
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_TUNNEL=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+CONFIG_NF_CT_PROTO_DCCP=y
+CONFIG_NF_CT_PROTO_SCTP=y
+CONFIG_NF_CT_PROTO_UDPLITE=y
+CONFIG_NF_CONNTRACK_AMANDA=y
+CONFIG_NF_CONNTRACK_FTP=y
+CONFIG_NF_CONNTRACK_H323=y
+CONFIG_NF_CONNTRACK_IRC=y
+CONFIG_NF_CONNTRACK_NETBIOS_NS=y
+CONFIG_NF_CONNTRACK_PPTP=y
+CONFIG_NF_CONNTRACK_SANE=y
+CONFIG_NF_CONNTRACK_TFTP=y
+CONFIG_NF_CT_NETLINK=y
+CONFIG_NETFILTER_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=y
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=y
+CONFIG_NETFILTER_XT_TARGET_MARK=y
+CONFIG_NETFILTER_XT_TARGET_NFLOG=y
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=y
+CONFIG_NETFILTER_XT_TARGET_TPROXY=y
+CONFIG_NETFILTER_XT_TARGET_TRACE=y
+CONFIG_NETFILTER_XT_MATCH_COMMENT=y
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=y
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=y
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=y
+CONFIG_NETFILTER_XT_MATCH_HELPER=y
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=y
+CONFIG_NETFILTER_XT_MATCH_LENGTH=y
+CONFIG_NETFILTER_XT_MATCH_LIMIT=y
+CONFIG_NETFILTER_XT_MATCH_MAC=y
+CONFIG_NETFILTER_XT_MATCH_MARK=y
+CONFIG_NETFILTER_XT_MATCH_POLICY=y
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=y
+CONFIG_NETFILTER_XT_MATCH_QTAGUID=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2=y
+CONFIG_NETFILTER_XT_MATCH_QUOTA2_LOG=y
+CONFIG_NETFILTER_XT_MATCH_SOCKET=y
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
+CONFIG_NETFILTER_XT_MATCH_STRING=y
+CONFIG_NETFILTER_XT_MATCH_TIME=y
+CONFIG_NETFILTER_XT_MATCH_U32=y
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_MATCH_AH=y
+CONFIG_IP_NF_MATCH_ECN=y
+CONFIG_IP_NF_MATCH_TTL=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_REJECT_SKERR=y
+CONFIG_IP_NF_TARGET_LOG=y
+CONFIG_NF_NAT=y
+CONFIG_IP_NF_TARGET_MASQUERADE=y
+CONFIG_IP_NF_TARGET_NETMAP=y
+CONFIG_IP_NF_TARGET_REDIRECT=y
+CONFIG_IP_NF_MANGLE=y
+CONFIG_IP_NF_RAW=y
+CONFIG_IP_NF_ARPTABLES=y
+CONFIG_IP_NF_ARPFILTER=y
+CONFIG_IP_NF_ARP_MANGLE=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_TARGET_LOG=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_IP6_NF_TARGET_REJECT=y
+CONFIG_IP6_NF_TARGET_REJECT_SKERR=y
+CONFIG_IP6_NF_MANGLE=y
+CONFIG_IP6_NF_RAW=y
+CONFIG_BRIDGE=y
+# CONFIG_BRIDGE_IGMP_SNOOPING is not set
+CONFIG_PHONET=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_HTB=y
+CONFIG_NET_SCH_INGRESS=y
+CONFIG_NET_CLS_U32=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_U32=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=y
+CONFIG_NET_ACT_GACT=y
+CONFIG_NET_ACT_MIRRED=y
+CONFIG_BT=y
+CONFIG_BT_L2CAP=y
+CONFIG_BT_SCO=y
+CONFIG_BT_RFCOMM=y
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=y
+CONFIG_BT_HIDP=y
+CONFIG_BT_HCIUART=y
+CONFIG_BT_HCIUART_H4=y
+CONFIG_CFG80211=y
+CONFIG_NL80211_TESTMODE=y
+# CONFIG_CFG80211_WEXT is not set
+CONFIG_CFG80211_ALLOW_RECONNECT=y
+CONFIG_RFKILL=y
+CONFIG_RFKILL_INPUT=y
+CONFIG_MTD=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_M25P80=y
+CONFIG_MTD_NAND_IDS=y
+CONFIG_MTD_ONENAND=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_MISC_DEVICES=y
+CONFIG_SAMSUNG_JACK=y
+CONFIG_UID_STAT=y
+CONFIG_BMP180=y
+CONFIG_USB_SWITCH_FSA9480=y
+CONFIG_OMAP_DIE_TEMP_SENSOR=y
+CONFIG_LEDS_AN30259A=y
+CONFIG_MPU_SENSORS_TIMERIRQ=y
+CONFIG_INV_SENSORS=y
+CONFIG_MPU_SENSORS_MPU3050=y
+CONFIG_MPU_SENSORS_BMA250=y
+CONFIG_MPU_SENSORS_YAS530=y
+CONFIG_SEC_MODEM=y
+CONFIG_UMTS_LINK_MIPI=y
+CONFIG_UMTS_MODEM_XMM6260=y
+CONFIG_CDMA_LINK_DPRAM=y
+CONFIG_CDMA_MODEM_CBP71=y
+CONFIG_LTE_LINK_USB=y
+CONFIG_LTE_MODEM_CMC221=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_DEBUG=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_IFB=y
+CONFIG_TUN=y
+CONFIG_WIFI_CONTROL_FUNC=y
+CONFIG_BCMDHD=y
+CONFIG_BCMDHD_FW_PATH="/system/vendor/firmware/fw_bcmdhd.bin"
+CONFIG_PPP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+# CONFIG_INPUT_MOUSEDEV is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_OMAP4=y
+# CONFIG_INPUT_MOUSE is not set
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_XPAD=y
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_INPUT_TABLET=y
+CONFIG_TABLET_USB_ACECAD=y
+CONFIG_TABLET_USB_AIPTEK=y
+CONFIG_TABLET_USB_GTCO=y
+CONFIG_TABLET_USB_HANWANG=y
+CONFIG_TABLET_USB_KBTAB=y
+CONFIG_TABLET_USB_WACOM=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_MMS=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_KEYCHORD=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+CONFIG_OPTICAL_GP2A=y
+CONFIG_SERIO_LIBPS2=y
+# CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_HW_RANDOM=y
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_GPIO=y
+CONFIG_SPI=y
+CONFIG_SPI_OMAP24XX=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_TWL4030=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_PDA_POWER=y
+CONFIG_BATTERY_MAX17040=y
+# CONFIG_HWMON is not set
+CONFIG_WATCHDOG=y
+CONFIG_OMAP_WATCHDOG=y
+CONFIG_TWL6030_POWER=y
+CONFIG_TWL6030_PWM=y
+CONFIG_TWL6030_MADC=y
+CONFIG_REGULATOR_TWL4030=y
+CONFIG_MEDIA_SUPPORT=y
+# CONFIG_RC_CORE is not set
+CONFIG_PVR_SGX=y
+CONFIG_PVR_NEED_PVR_DPF=y
+CONFIG_PVR_NEED_PVR_ASSERT=y
+CONFIG_PVR_USSE_EDM_STATUS_DEBUG=y
+CONFIG_SGX_DVFS_MODE_OPTIMIZED=y
+CONFIG_ION=y
+CONFIG_ION_OMAP=y
+CONFIG_FB=y
+CONFIG_SII9234=y
+CONFIG_FB_OMAP_BOOTLOADER_INIT=y
+CONFIG_OMAP2_DSS=y
+CONFIG_OMAP2_VRAM_SIZE=16
+# CONFIG_OMAP2_DSS_DPI is not set
+# CONFIG_OMAP2_DSS_VENC is not set
+CONFIG_OMAP2_DSS_DSI=y
+CONFIG_FB_OMAP2=y
+CONFIG_PANEL_S6E8AA0=y
+CONFIG_OMAP4_HDCP=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+# CONFIG_LCD_CLASS_DEVICE is not set
+CONFIG_DISPLAY_SUPPORT=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_OMAP_SOC=y
+CONFIG_SND_OMAP_SOC_SDP4430=y
+CONFIG_SND_OMAP_SOC_OMAP4_HDMI=y
+CONFIG_HID_A4TECH=y
+CONFIG_HID_ACRUX=y
+CONFIG_HID_ACRUX_FF=y
+CONFIG_HID_APPLE=y
+CONFIG_HID_BELKIN=y
+CONFIG_HID_CHERRY=y
+CONFIG_HID_CHICONY=y
+CONFIG_HID_CYPRESS=y
+CONFIG_HID_DRAGONRISE=y
+CONFIG_DRAGONRISE_FF=y
+CONFIG_HID_EMS_FF=y
+CONFIG_HID_ELECOM=y
+CONFIG_HID_EZKEY=y
+CONFIG_HID_KEYTOUCH=y
+CONFIG_HID_KYE=y
+CONFIG_HID_UCLOGIC=y
+CONFIG_HID_WALTOP=y
+CONFIG_HID_GYRATION=y
+CONFIG_HID_TWINHAN=y
+CONFIG_HID_KENSINGTON=y
+CONFIG_HID_LCPOWER=y
+CONFIG_HID_LOGITECH=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_LOGIWII_FF=y
+CONFIG_HID_MAGICMOUSE=y
+CONFIG_HID_MICROSOFT=y
+CONFIG_HID_MONTEREY=y
+CONFIG_HID_MULTITOUCH=y
+CONFIG_HID_NTRIG=y
+CONFIG_HID_ORTEK=y
+CONFIG_HID_PANTHERLORD=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_HID_PETALYNX=y
+CONFIG_HID_PICOLCD=y
+CONFIG_HID_QUANTA=y
+CONFIG_HID_ROCCAT_ARVO=y
+CONFIG_HID_ROCCAT_KONE=y
+CONFIG_HID_ROCCAT_KONEPLUS=y
+CONFIG_HID_ROCCAT_KOVAPLUS=y
+CONFIG_HID_ROCCAT_PYRA=y
+CONFIG_HID_SAMSUNG=y
+CONFIG_HID_SONY=y
+CONFIG_HID_SUNPLUS=y
+CONFIG_HID_GREENASIA=y
+CONFIG_GREENASIA_FF=y
+CONFIG_HID_SMARTJOYPLUS=y
+CONFIG_SMARTJOYPLUS_FF=y
+CONFIG_HID_TOPSEED=y
+CONFIG_HID_THRUSTMASTER=y
+CONFIG_THRUSTMASTER_FF=y
+CONFIG_HID_WACOM=y
+CONFIG_HID_ZEROPLUS=y
+CONFIG_ZEROPLUS_FF=y
+CONFIG_HID_ZYDACRON=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_SUSPEND=y
+# CONFIG_USB_OTG_WHITELIST is not set
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_MUSB_HDRC=y
+CONFIG_USB_MUSB_OMAP2PLUS=y
+CONFIG_USB_MUSB_OTG=y
+CONFIG_USB_GADGET_MUSB_HDRC=y
+CONFIG_USB_ACM=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_SERIAL=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_VBUS_DRAW=500
+CONFIG_USB_G_ANDROID=y
+CONFIG_USB_OTG_WAKELOCK=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+CONFIG_MMC_OMAP=y
+CONFIG_MMC_OMAP_HS=y
+CONFIG_SWITCH=y
+CONFIG_SWITCH_GPIO=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_TWL4030=y
+CONFIG_STAGING=y
+CONFIG_ANDROID=y
+CONFIG_ANDROID_BINDER_IPC=y
+CONFIG_ANDROID_LOGGER=y
+CONFIG_ANDROID_RAM_CONSOLE=y
+CONFIG_ANDROID_RAM_CONSOLE_ERROR_CORRECTION=y
+CONFIG_ANDROID_TIMED_GPIO=y
+CONFIG_ANDROID_LOW_MEMORY_KILLER=y
+CONFIG_OMAP_HSI=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT4_FS=y
+# CONFIG_EXT4_FS_XATTR is not set
+# CONFIG_DNOTIFY is not set
+CONFIG_FUSE_FS=y
+CONFIG_MSDOS_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+# CONFIG_NETWORK_FILESYSTEMS is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_ISO8859_1=y
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=10
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_DEBUG_INFO=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+CONFIG_SCHED_TRACER=y
+# CONFIG_ARM_UNWIND is not set
+CONFIG_DEBUG_USER=y
+CONFIG_SECURITY_MIDDLEWARE_COMPONENT=y
+# CONFIG_SMC_KERNEL_CRYPTO is not set
+CONFIG_CRYPTO_SHA256=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRC_CCITT=y
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index f7b228c..97e7205 100644
--- a/arch/arm/mach-omap2/Kconfig
+++ b/arch/arm/mach-omap2/Kconfig
@@ -325,6 +325,15 @@ config MACH_OMAP4_PANDA
select OMAP_PACKAGE_CBS
select REGULATOR_FIXED_VOLTAGE
+config MACH_TUNA
+ bool "Tuna Board"
+ default y
+ depends on ARCH_OMAP4
+ select OMAP_PACKAGE_CBL
+ select OMAP_PACKAGE_CBS
+ select REGULATOR_FIXED_VOLTAGE
+ select OMAP_TPS6236X
+
config OMAP3_EMU
bool "OMAP3 debugging peripherals"
depends on ARCH_OMAP3
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index aa42baf..6d72578 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -254,6 +254,22 @@ obj-$(CONFIG_MACH_OMAP_4430SDP) += board-4430sdp.o \
obj-$(CONFIG_MACH_OMAP4_PANDA) += board-omap4panda.o \
hsmmc.o \
omap_phy_internal.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna.o \
+ hsmmc.o \
+ omap_phy_internal.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-display.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-input.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-jack.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-nfc.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-power.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-sensors.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-vibrator.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-wifi.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-bluetooth.o \
+ board-tuna-emif.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-connector.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-pogo.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-usbhost.o
obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o \
omap_phy_internal.o \
@@ -292,5 +308,6 @@ obj-y += common-board-devices.o
obj-$(CONFIG_OMAP_REMOTE_PROC) += remoteproc.o
obj-$(CONFIG_OMAP_HSI_DEVICE) += omap_hsi.o
+obj-$(CONFIG_SEC_MODEM) += board-tuna-modems.o
obj-$(CONFIG_ARCH_OMAP4) += omap_dmm.o
obj-$(CONFIG_OMAP_FIQ_DEBUGGER) += omap_fiq_debugger.o
diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c
index 4fcd26a..5a00efc 100644
--- a/arch/arm/mach-omap2/board-omap4panda.c
+++ b/arch/arm/mach-omap2/board-omap4panda.c
@@ -680,8 +680,6 @@ void omap4_panda_display_init(void)
omap_display_init(&omap4_panda_dss_data);
}
-extern void __init omap4_panda_android_init(void);
-
static void __init omap4_panda_init(void)
{
int package = OMAP_PACKAGE_CBS;
@@ -712,7 +710,6 @@ static void __init omap4_panda_init(void)
omap_dmm_init();
omap4_panda_display_init();
-
}
static void __init omap4_panda_map_io(void)
diff --git a/arch/arm/mach-omap2/board-tuna-bluetooth.c b/arch/arm/mach-omap2/board-tuna-bluetooth.c
new file mode 100644
index 0000000..bfe66ab
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-bluetooth.c
@@ -0,0 +1,344 @@
+/*
+ * Bluetooth Broadcomm and low power control via GPIO
+ *
+ * Copyright (C) 2011 Google, 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
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/hrtimer.h>
+#include <linux/irq.h>
+#include <linux/rfkill.h>
+#include <linux/platform_device.h>
+#include <linux/wakelock.h>
+#include <asm/mach-types.h>
+#include <plat/serial.h>
+#include <plat/board-tuna-bluetooth.h>
+#include <linux/regulator/driver.h>
+
+#define BT_REG_GPIO 103
+#define BT_RESET_GPIO 42
+
+#define BT_WAKE_GPIO 27
+#define BT_HOST_WAKE_GPIO 177
+
+static struct rfkill *bt_rfkill;
+static struct regulator *clk32kaudio_reg;
+static bool bt_enabled;
+static bool host_wake_uart_enabled;
+static bool wake_uart_enabled;
+
+struct bcm_bt_lpm {
+ int wake;
+ int host_wake;
+
+ struct hrtimer enter_lpm_timer;
+ ktime_t enter_lpm_delay;
+
+ struct uart_port *uport;
+
+ struct wake_lock wake_lock;
+ char wake_lock_name[100];
+} bt_lpm;
+
+static int bcm4330_bt_rfkill_set_power(void *data, bool blocked)
+{
+ // rfkill_ops callback. Turn transmitter on when blocked is false
+ if (!blocked) {
+ if (clk32kaudio_reg && !bt_enabled)
+ regulator_enable(clk32kaudio_reg);
+
+ gpio_set_value(BT_REG_GPIO, 1);
+ gpio_set_value(BT_RESET_GPIO, 1);
+
+ } else {
+ gpio_set_value(BT_RESET_GPIO, 0);
+ gpio_set_value(BT_REG_GPIO, 0);
+ if (clk32kaudio_reg && bt_enabled)
+ regulator_disable(clk32kaudio_reg);
+ }
+
+ bt_enabled = !blocked;
+
+ return 0;
+}
+
+static const struct rfkill_ops bcm4330_bt_rfkill_ops = {
+ .set_block = bcm4330_bt_rfkill_set_power,
+};
+
+static void set_wake_locked(int wake)
+{
+ bt_lpm.wake = wake;
+
+ if (!wake)
+ wake_unlock(&bt_lpm.wake_lock);
+
+ if (!wake_uart_enabled && wake)
+ omap_uart_enable(2);
+
+ gpio_set_value(BT_WAKE_GPIO, wake);
+
+ if (wake_uart_enabled && !wake)
+ omap_uart_disable(2);
+
+ wake_uart_enabled = wake;
+}
+
+static enum hrtimer_restart enter_lpm(struct hrtimer *timer) {
+ unsigned long flags;
+ spin_lock_irqsave(&bt_lpm.uport->lock, flags);
+ set_wake_locked(0);
+ spin_unlock_irqrestore(&bt_lpm.uport->lock, flags);
+
+ return HRTIMER_NORESTART;
+}
+
+void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport) {
+ bt_lpm.uport = uport;
+
+ hrtimer_try_to_cancel(&bt_lpm.enter_lpm_timer);
+
+ set_wake_locked(1);
+
+ hrtimer_start(&bt_lpm.enter_lpm_timer, bt_lpm.enter_lpm_delay,
+ HRTIMER_MODE_REL);
+}
+EXPORT_SYMBOL(bcm_bt_lpm_exit_lpm_locked);
+
+static void update_host_wake_locked(int host_wake)
+{
+ if (host_wake == bt_lpm.host_wake)
+ return;
+
+ bt_lpm.host_wake = host_wake;
+
+ if (host_wake) {
+ wake_lock(&bt_lpm.wake_lock);
+ if (!host_wake_uart_enabled)
+ omap_uart_enable(2);
+ } else {
+ if (host_wake_uart_enabled)
+ omap_uart_disable(2);
+ // Take a timed wakelock, so that upper layers can take it.
+ // The chipset deasserts the hostwake lock, when there is no
+ // more data to send.
+ wake_lock_timeout(&bt_lpm.wake_lock, HZ/2);
+ }
+
+ host_wake_uart_enabled = host_wake;
+
+}
+
+static irqreturn_t host_wake_isr(int irq, void *dev)
+{
+ int host_wake;
+ unsigned long flags;
+
+ host_wake = gpio_get_value(BT_HOST_WAKE_GPIO);
+ irq_set_irq_type(irq, host_wake ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+
+ if (!bt_lpm.uport) {
+ bt_lpm.host_wake = host_wake;
+ return IRQ_HANDLED;
+ }
+
+ spin_lock_irqsave(&bt_lpm.uport->lock, flags);
+ update_host_wake_locked(host_wake);
+ spin_unlock_irqrestore(&bt_lpm.uport->lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static int bcm_bt_lpm_init(struct platform_device *pdev)
+{
+ int irq;
+ int ret;
+ int rc;
+
+ rc = gpio_request(BT_WAKE_GPIO, "bcm4330_wake_gpio");
+ if (unlikely(rc)) {
+ return rc;
+ }
+
+ rc = gpio_request(BT_HOST_WAKE_GPIO, "bcm4330_host_wake_gpio");
+ if (unlikely(rc)) {
+ gpio_free(BT_WAKE_GPIO);
+ return rc;
+ }
+
+ hrtimer_init(&bt_lpm.enter_lpm_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ bt_lpm.enter_lpm_delay = ktime_set(1, 0); /* 1 sec */
+ bt_lpm.enter_lpm_timer.function = enter_lpm;
+
+ bt_lpm.host_wake = 0;
+
+ irq = gpio_to_irq(BT_HOST_WAKE_GPIO);
+ ret = request_irq(irq, host_wake_isr, IRQF_TRIGGER_HIGH,
+ "bt host_wake", NULL);
+ if (ret) {
+ gpio_free(BT_WAKE_GPIO);
+ gpio_free(BT_HOST_WAKE_GPIO);
+ return ret;
+ }
+
+ ret = irq_set_irq_wake(irq, 1);
+ if (ret) {
+ gpio_free(BT_WAKE_GPIO);
+ gpio_free(BT_HOST_WAKE_GPIO);
+ return ret;
+ }
+
+ gpio_direction_output(BT_WAKE_GPIO, 0);
+ gpio_direction_input(BT_HOST_WAKE_GPIO);
+
+ snprintf(bt_lpm.wake_lock_name, sizeof(bt_lpm.wake_lock_name),
+ "BTLowPower");
+ wake_lock_init(&bt_lpm.wake_lock, WAKE_LOCK_SUSPEND,
+ bt_lpm.wake_lock_name);
+ return 0;
+}
+
+static int bcm4330_bluetooth_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+ int ret = 0;
+
+ rc = gpio_request(BT_RESET_GPIO, "bcm4330_nreset_gpip");
+ if (unlikely(rc)) {
+ return rc;
+ }
+
+ rc = gpio_request(BT_REG_GPIO, "bcm4330_nshutdown_gpio");
+ if (unlikely(rc)) {
+ gpio_free(BT_RESET_GPIO);
+ return rc;
+ }
+
+ clk32kaudio_reg = regulator_get(0, "clk32kaudio");
+ if (IS_ERR(clk32kaudio_reg)) {
+ pr_err("clk32kaudio reg not found!\n");
+ clk32kaudio_reg = NULL;
+ }
+
+ gpio_direction_output(BT_REG_GPIO, 1);
+ gpio_direction_output(BT_RESET_GPIO, 1);
+
+ bt_rfkill = rfkill_alloc("bcm4330 Bluetooth", &pdev->dev,
+ RFKILL_TYPE_BLUETOOTH, &bcm4330_bt_rfkill_ops,
+ NULL);
+
+ if (unlikely(!bt_rfkill)) {
+ gpio_free(BT_RESET_GPIO);
+ gpio_free(BT_REG_GPIO);
+ return -ENOMEM;
+ }
+
+ rc = rfkill_register(bt_rfkill);
+
+ if (unlikely(rc)) {
+ rfkill_destroy(bt_rfkill);
+ gpio_free(BT_RESET_GPIO);
+ gpio_free(BT_REG_GPIO);
+ return -1;
+ }
+
+ rfkill_set_states(bt_rfkill, true, false);
+ bcm4330_bt_rfkill_set_power(NULL, true);
+
+ ret = bcm_bt_lpm_init(pdev);
+ if (ret) {
+ rfkill_unregister(bt_rfkill);
+ rfkill_destroy(bt_rfkill);
+
+ gpio_free(BT_RESET_GPIO);
+ gpio_free(BT_REG_GPIO);
+ }
+
+ return ret;
+}
+
+static int bcm4330_bluetooth_remove(struct platform_device *pdev)
+{
+ rfkill_unregister(bt_rfkill);
+ rfkill_destroy(bt_rfkill);
+
+ gpio_free(BT_REG_GPIO);
+ gpio_free(BT_RESET_GPIO);
+ gpio_free(BT_WAKE_GPIO);
+ gpio_free(BT_HOST_WAKE_GPIO);
+ regulator_put(clk32kaudio_reg);
+
+ wake_lock_destroy(&bt_lpm.wake_lock);
+ return 0;
+}
+
+int bcm4430_bluetooth_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ int irq = gpio_to_irq(BT_HOST_WAKE_GPIO);
+ int host_wake;
+
+ disable_irq(irq);
+ host_wake = gpio_get_value(BT_HOST_WAKE_GPIO);
+
+ if (host_wake) {
+ enable_irq(irq);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+int bcm4430_bluetooth_resume(struct platform_device *pdev)
+{
+ int irq = gpio_to_irq(BT_HOST_WAKE_GPIO);
+ enable_irq(irq);
+ return 0;
+}
+
+static struct platform_driver bcm4330_bluetooth_platform_driver = {
+ .probe = bcm4330_bluetooth_probe,
+ .remove = bcm4330_bluetooth_remove,
+ .suspend = bcm4430_bluetooth_suspend,
+ .resume = bcm4430_bluetooth_resume,
+ .driver = {
+ .name = "bcm4330_bluetooth",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init bcm4330_bluetooth_init(void)
+{
+ bt_enabled = false;
+ return platform_driver_register(&bcm4330_bluetooth_platform_driver);
+}
+
+static void __exit bcm4330_bluetooth_exit(void)
+{
+ platform_driver_unregister(&bcm4330_bluetooth_platform_driver);
+}
+
+
+module_init(bcm4330_bluetooth_init);
+module_exit(bcm4330_bluetooth_exit);
+
+MODULE_ALIAS("platform:bcm4330");
+MODULE_DESCRIPTION("bcm4330_bluetooth");
+MODULE_AUTHOR("Jaikumar Ganesh <jaikumar@google.com>");
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-omap2/board-tuna-connector.c b/arch/arm/mach-omap2/board-tuna-connector.c
new file mode 100644
index 0000000..7c09aef
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-connector.c
@@ -0,0 +1,982 @@
+/*
+ * Copyright (C) 2011 Samsung, Inc.
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Author: Adam Hampson <ahampson@sta.samsung.com>
+ * Dima Zavin <dima@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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_data/fsa9480.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/otg.h>
+#include <linux/delay.h>
+#include <linux/sii9234.h>
+#include <linux/i2c/twl.h>
+#include <linux/mutex.h>
+#include <linux/switch.h>
+
+#include <plat/usb.h>
+
+#include "mux.h"
+#include "board-tuna.h"
+
+#define GPIO_JACK_INT_N 4
+#define GPIO_CP_USB_ON 22
+#define GPIO_USB_OTG_ID 24
+#define GPIO_MHL_SEL 96
+#define GPIO_AP_SEL 97
+#define GPIO_MUX3_SEL0 139
+#define GPIO_MUX3_SEL1 140
+#define GPIO_USB_ID_SEL 191
+#define GPIO_IF_UART_SEL 101
+
+#define GPIO_MHL_RST 161
+#define GPIO_MHL_WAKEUP 64
+#define GPIO_MHL_INT 175
+#define GPIO_HDMI_EN 100
+
+#define MUX3_SEL0_AP 1
+#define MUX3_SEL1_AP 1
+#define MUX3_SEL0_MHL 1
+#define MUX3_SEL1_MHL 0
+#define MUX3_SEL0_FSA 0
+#define MUX3_SEL1_FSA 1
+
+#define FSA3200_AP_SEL_AP 0
+#define FSA3200_MHL_SEL_AP 0
+#define FSA3200_AP_SEL_FSA 1
+#define FSA3200_MHL_SEL_FSA 0
+#define FSA3200_AP_SEL_MHL 1
+#define FSA3200_MHL_SEL_MHL 1
+
+#define USB_ID_SEL_FSA 0
+#define USB_ID_SEL_MHL 1
+
+#define IF_UART_SEL_DEFAULT 1
+#define IF_UART_SEL_AP 1
+#define IF_UART_SEL_CP 0
+
+#define TUNA_MANUAL_USB_NONE 0
+#define TUNA_MANUAL_USB_MODEM 1
+#define TUNA_MANUAL_USB_AP 2
+
+#define TUNA_MANUAL_UART_NONE 0
+#define TUNA_MANUAL_UART_MODEM 1
+#define TUNA_MANUAL_UART_LTE 2
+#define TUNA_MANUAL_UART_AP 3
+
+#define CHARGERUSB_CTRL1 0x8
+#define CHARGERUSB_CTRL3 0xA
+#define CHARGERUSB_CINLIMIT 0xE
+
+#define TWL6030_VBUS_IRQ (TWL6030_IRQ_BASE + USB_PRES_INTR_OFFSET)
+#define TWL6030_VBUS_FLAGS (IRQF_TRIGGER_FALLING | IRQF_ONESHOT)
+
+#define TWL_REG_CONTROLLER_INT_MASK 0x00
+#define TWL_CONTROLLER_MVBUS_DET BIT(1)
+#define TWL_CONTROLLER_RSVD BIT(5)
+
+#define TWL_REG_CONTROLLER_STAT1 0x03
+#define TWL_STAT1_VBUS_DET BIT(2)
+
+struct tuna_otg {
+ struct otg_transceiver otg;
+ struct device dev;
+
+ struct regulator *vusb;
+ struct work_struct set_vbus_work;
+ struct mutex lock;
+
+ bool reg_on;
+ bool need_vbus_drive;
+ int usb_manual_mode;
+ int uart_manual_mode;
+ int current_device;
+
+ struct switch_dev dock_switch;
+};
+static struct tuna_otg tuna_otg_xceiv;
+
+enum {
+ TUNA_USB_MUX_FSA = 0,
+ TUNA_USB_MUX_MHL,
+ TUNA_USB_MUX_AP,
+ NUM_TUNA_USB_MUX,
+
+ TUNA_USB_MUX_DEFAULT = TUNA_USB_MUX_FSA,
+};
+
+static struct {
+ int mux3_sel0;
+ int mux3_sel1;
+} tuna_usb_mux_states[] = {
+ [TUNA_USB_MUX_FSA] = { MUX3_SEL0_FSA, MUX3_SEL1_FSA },
+ [TUNA_USB_MUX_MHL] = { MUX3_SEL0_MHL, MUX3_SEL1_MHL },
+ [TUNA_USB_MUX_AP] = { MUX3_SEL0_AP, MUX3_SEL1_AP },
+};
+
+static struct {
+ int ap_sel;
+ int mhl_sel;
+} tuna_fsa3200_mux_pair_states[] = {
+ [TUNA_USB_MUX_FSA] = { FSA3200_AP_SEL_FSA, FSA3200_MHL_SEL_FSA },
+ [TUNA_USB_MUX_MHL] = { FSA3200_AP_SEL_MHL, FSA3200_MHL_SEL_MHL },
+ [TUNA_USB_MUX_AP] = { FSA3200_AP_SEL_AP, FSA3200_MHL_SEL_AP },
+};
+
+static int tuna_usb_id_mux_states[] = {
+ [TUNA_USB_MUX_FSA] = USB_ID_SEL_FSA,
+ [TUNA_USB_MUX_MHL] = USB_ID_SEL_MHL,
+ [TUNA_USB_MUX_AP] = USB_ID_SEL_FSA,
+};
+
+static ssize_t tuna_otg_usb_sel_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t tuna_otg_usb_sel_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size);
+static ssize_t tuna_otg_uart_switch_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t tuna_otg_uart_switch_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size);
+
+static DEVICE_ATTR(usb_sel, S_IRUSR | S_IWUSR,
+ tuna_otg_usb_sel_show, tuna_otg_usb_sel_store);
+static DEVICE_ATTR(uart_sel, S_IRUSR | S_IWUSR,
+ tuna_otg_uart_switch_show, tuna_otg_uart_switch_store);
+
+static struct attribute *manual_mode_attributes[] = {
+ &dev_attr_usb_sel.attr,
+ &dev_attr_uart_sel.attr,
+ NULL,
+};
+
+static const struct attribute_group manual_mode_group = {
+ .attrs = manual_mode_attributes,
+};
+
+static bool tuna_twl_chgctrl_init;
+
+static void tuna_mux_usb(int state)
+{
+ BUG_ON(state >= NUM_TUNA_USB_MUX);
+
+ pr_debug("mux to %d\n", state);
+ gpio_direction_output(GPIO_MUX3_SEL0,
+ tuna_usb_mux_states[state].mux3_sel0);
+ gpio_direction_output(GPIO_MUX3_SEL1,
+ tuna_usb_mux_states[state].mux3_sel1);
+}
+
+static void tuna_mux_usb_id(int state)
+{
+ BUG_ON(state >= NUM_TUNA_USB_MUX);
+
+ pr_debug("mux to %d\n", state);
+ gpio_direction_output(GPIO_USB_ID_SEL, tuna_usb_id_mux_states[state]);
+}
+
+static void tuna_fsa3200_mux_pair(int state)
+{
+ BUG_ON(state >= NUM_TUNA_USB_MUX);
+
+ pr_debug("mux to %d\n", state);
+ gpio_direction_output(GPIO_AP_SEL,
+ tuna_fsa3200_mux_pair_states[state].ap_sel);
+ gpio_direction_output(GPIO_MHL_SEL,
+ tuna_fsa3200_mux_pair_states[state].mhl_sel);
+}
+
+static void tuna_mux_usb_to_fsa(bool enable)
+{
+ if (omap4_tuna_get_revision() >= 3) {
+ tuna_fsa3200_mux_pair(enable ? TUNA_USB_MUX_FSA :
+ TUNA_USB_MUX_DEFAULT);
+ } else {
+ tuna_mux_usb(enable ? TUNA_USB_MUX_FSA : TUNA_USB_MUX_DEFAULT);
+
+ /* When switching ID away from FSA, we want to ensure we switch
+ * it off FSA, and force it to MHL. Ideally, we'd just say mux
+ * to default, but FSA is likely the default mux position and
+ * there's no way to force the ID pin to float to the FSA.
+ */
+ tuna_mux_usb_id(enable ? TUNA_USB_MUX_FSA : TUNA_USB_MUX_MHL);
+ }
+}
+
+static void tuna_mux_usb_to_mhl(bool enable)
+{
+ if (omap4_tuna_get_revision() >= 3) {
+ tuna_fsa3200_mux_pair(enable ? TUNA_USB_MUX_MHL :
+ TUNA_USB_MUX_DEFAULT);
+ } else {
+ tuna_mux_usb(enable ? TUNA_USB_MUX_MHL : TUNA_USB_MUX_DEFAULT);
+ tuna_mux_usb_id(enable ? TUNA_USB_MUX_MHL : TUNA_USB_MUX_DEFAULT);
+ }
+}
+
+static void tuna_vusb_enable(struct tuna_otg *tuna_otg, bool enable)
+{
+ /* delay getting the regulator until later */
+ if (IS_ERR_OR_NULL(tuna_otg->vusb)) {
+ tuna_otg->vusb = regulator_get(&tuna_otg->dev, "vusb");
+ if (IS_ERR(tuna_otg->vusb)) {
+ dev_err(&tuna_otg->dev, "cannot get vusb regulator\n");
+ return;
+ }
+ }
+
+ if (enable) {
+ regulator_enable(tuna_otg->vusb);
+ tuna_otg->reg_on = true;
+ } else if (tuna_otg->reg_on) {
+ regulator_disable(tuna_otg->vusb);
+ tuna_otg->reg_on = false;
+ }
+}
+
+static void tuna_set_vbus_drive(bool enable)
+{
+ if (enable) {
+ /* Set the VBUS current limit to 500mA */
+ twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x09,
+ CHARGERUSB_CINLIMIT);
+
+ /* The TWL6030 has a feature to automatically turn on
+ * boost mode (VBUS Drive) when the ID signal is not
+ * grounded. This feature needs to be disabled on Tuna
+ * as the ID signal is not hooked up to the TWL6030.
+ */
+ twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x21,
+ CHARGERUSB_CTRL3);
+ twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x40,
+ CHARGERUSB_CTRL1);
+ } else {
+ twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0x01,
+ CHARGERUSB_CTRL3);
+ twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE, 0, CHARGERUSB_CTRL1);
+ }
+}
+
+static void tuna_ap_usb_attach(struct tuna_otg *tuna_otg)
+{
+ tuna_vusb_enable(tuna_otg, true);
+
+ if (omap4_tuna_get_revision() >= 3) {
+ tuna_fsa3200_mux_pair(TUNA_USB_MUX_AP);
+ } else {
+ tuna_mux_usb(TUNA_USB_MUX_AP);
+ tuna_mux_usb_id(TUNA_USB_MUX_FSA);
+ }
+
+ tuna_otg->otg.state = OTG_STATE_B_IDLE;
+ tuna_otg->otg.default_a = false;
+ tuna_otg->otg.last_event = USB_EVENT_VBUS;
+ atomic_notifier_call_chain(&tuna_otg->otg.notifier,
+ USB_EVENT_VBUS,
+ tuna_otg->otg.gadget);
+}
+
+static void tuna_ap_usb_detach(struct tuna_otg *tuna_otg)
+{
+ tuna_vusb_enable(tuna_otg, false);
+
+ tuna_otg->otg.state = OTG_STATE_B_IDLE;
+ tuna_otg->otg.default_a = false;
+ tuna_otg->otg.last_event = USB_EVENT_NONE;
+ atomic_notifier_call_chain(&tuna_otg->otg.notifier,
+ USB_EVENT_NONE,
+ tuna_otg->otg.gadget);
+}
+
+static void tuna_cp_usb_attach(struct tuna_otg *tuna_otg)
+{
+ if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO)
+ gpio_set_value(GPIO_CP_USB_ON, 1);
+
+ tuna_mux_usb_to_fsa(true);
+}
+
+static void tuna_cp_usb_detach(struct tuna_otg *tuna_otg)
+{
+ if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO)
+ gpio_set_value(GPIO_CP_USB_ON, 0);
+}
+
+static void tuna_usb_host_detach(struct tuna_otg *tuna_otg)
+{
+ /* Make sure the VBUS drive is turned off */
+ tuna_set_vbus_drive(false);
+
+ tuna_vusb_enable(tuna_otg, false);
+
+ tuna_otg->otg.state = OTG_STATE_B_IDLE;
+ tuna_otg->otg.default_a = false;
+ tuna_otg->otg.last_event = USB_EVENT_NONE;
+ atomic_notifier_call_chain(&tuna_otg->otg.notifier,
+ USB_EVENT_NONE,
+ tuna_otg->otg.gadget);
+}
+
+static void tuna_ap_uart_actions(struct tuna_otg *tuna_otg)
+{
+ tuna_mux_usb_to_fsa(true);
+ gpio_set_value(GPIO_IF_UART_SEL, IF_UART_SEL_AP);
+}
+
+static void tuna_cp_uart_actions(struct tuna_otg *tuna_otg)
+{
+ tuna_mux_usb_to_fsa(true);
+ gpio_set_value(GPIO_IF_UART_SEL, IF_UART_SEL_CP);
+}
+
+static void tuna_lte_uart_actions(struct tuna_otg *tuna_otg)
+{
+ tuna_mux_usb_to_fsa(true);
+
+ /* The LTE modem's UART lines are connected to the V_AUDIO_L and
+ * V_AUDIO_R pins on the FSA9480. The RIL will configure the FSA9480
+ * separately to set manual routing.
+ */
+}
+
+static void tuna_otg_mask_vbus_irq(void)
+{
+ twl6030_interrupt_mask(TWL6030_CHARGER_CTRL_INT_MASK,
+ REG_INT_MSK_LINE_C);
+ twl6030_interrupt_mask(TWL6030_CHARGER_CTRL_INT_MASK,
+ REG_INT_MSK_STS_C);
+}
+
+static void tuna_otg_unmask_vbus_irq(void)
+{
+ if (!tuna_twl_chgctrl_init) {
+ int r;
+
+ r = twl_i2c_write_u8(TWL_MODULE_MAIN_CHARGE,
+ (u8) ~(TWL_CONTROLLER_RSVD |
+ TWL_CONTROLLER_MVBUS_DET),
+ TWL_REG_CONTROLLER_INT_MASK);
+
+ if (r)
+ pr_err_once("%s: Error writing twl charge ctrl int mask\n",
+ __func__);
+ else
+ tuna_twl_chgctrl_init = true;
+ }
+
+ twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
+ REG_INT_MSK_LINE_C);
+ twl6030_interrupt_unmask(TWL6030_CHARGER_CTRL_INT_MASK,
+ REG_INT_MSK_STS_C);
+}
+
+static bool tuna_otg_vbus_present(void)
+{
+ u8 vbus_state;
+
+ twl_i2c_read_u8(TWL_MODULE_MAIN_CHARGE, &vbus_state,
+ TWL_REG_CONTROLLER_STAT1);
+
+ return !!(vbus_state & TWL_STAT1_VBUS_DET);
+}
+
+static void tuna_fsa_usb_detected(int device)
+{
+ struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
+ int old_device;
+
+ mutex_lock(&tuna_otg->lock);
+
+ old_device = tuna_otg->current_device;
+ tuna_otg->current_device = device;
+
+ pr_debug("detected %x\n", device);
+ switch (device) {
+ case FSA9480_DETECT_AV_365K_CHARGER:
+ tuna_otg_set_dock_switch(1);
+ /* intentional fall-through */
+ case FSA9480_DETECT_USB:
+ if (tuna_otg->usb_manual_mode == TUNA_MANUAL_USB_MODEM)
+ tuna_cp_usb_attach(tuna_otg);
+ else
+ tuna_ap_usb_attach(tuna_otg);
+ break;
+ case FSA9480_DETECT_CHARGER:
+ tuna_mux_usb_to_fsa(true);
+
+ tuna_otg->otg.state = OTG_STATE_B_IDLE;
+ tuna_otg->otg.default_a = false;
+ tuna_otg->otg.last_event = USB_EVENT_CHARGER;
+ atomic_notifier_call_chain(&tuna_otg->otg.notifier,
+ USB_EVENT_CHARGER,
+ tuna_otg->otg.gadget);
+ break;
+ case FSA9480_DETECT_USB_HOST:
+ tuna_vusb_enable(tuna_otg, true);
+
+ if (omap4_tuna_get_revision() >= 3) {
+ tuna_fsa3200_mux_pair(TUNA_USB_MUX_AP);
+ } else {
+ tuna_mux_usb(TUNA_USB_MUX_AP);
+ tuna_mux_usb_id(TUNA_USB_MUX_FSA);
+ }
+
+ tuna_otg->otg.state = OTG_STATE_A_IDLE;
+ tuna_otg->otg.default_a = true;
+ tuna_otg->otg.last_event = USB_EVENT_ID;
+ atomic_notifier_call_chain(&tuna_otg->otg.notifier,
+ USB_EVENT_ID,
+ tuna_otg->otg.gadget);
+ break;
+ case FSA9480_DETECT_NONE:
+ tuna_mux_usb_to_fsa(true);
+
+ switch (old_device) {
+ case FSA9480_DETECT_JIG:
+ if (tuna_otg->uart_manual_mode == TUNA_MANUAL_UART_NONE)
+ tuna_ap_uart_actions(tuna_otg);
+ break;
+ case FSA9480_DETECT_AV_365K_CHARGER:
+ tuna_otg_set_dock_switch(0);
+ /* intentional fall-through */
+ case FSA9480_DETECT_USB:
+ if (tuna_otg->usb_manual_mode == TUNA_MANUAL_USB_MODEM)
+ tuna_cp_usb_detach(tuna_otg);
+ else
+ tuna_ap_usb_detach(tuna_otg);
+ break;
+ case FSA9480_DETECT_USB_HOST:
+ tuna_usb_host_detach(tuna_otg);
+ break;
+ case FSA9480_DETECT_CHARGER:
+ tuna_ap_usb_detach(tuna_otg);
+ break;
+ case FSA9480_DETECT_AV_365K:
+ tuna_otg_set_dock_switch(0);
+ break;
+ case FSA9480_DETECT_UART:
+ default:
+ break;
+ };
+ break;
+ case FSA9480_DETECT_JIG:
+ switch (tuna_otg->uart_manual_mode) {
+ case TUNA_MANUAL_UART_AP:
+ tuna_ap_uart_actions(tuna_otg);
+ break;
+ case TUNA_MANUAL_UART_LTE:
+ tuna_lte_uart_actions(tuna_otg);
+ break;
+ case TUNA_MANUAL_UART_MODEM:
+ default:
+ tuna_cp_uart_actions(tuna_otg);
+ break;
+ };
+ break;
+ case FSA9480_DETECT_AV_365K:
+ tuna_otg_set_dock_switch(1);
+ break;
+ case FSA9480_DETECT_UART:
+ default:
+ break;
+ }
+
+ mutex_unlock(&tuna_otg->lock);
+}
+
+static struct fsa9480_detect_set fsa_detect_sets[] = {
+ {
+ .prio = TUNA_OTG_ID_FSA9480_PRIO,
+ .mask = FSA9480_DETECT_ALL,
+ },
+ {
+ .prio = TUNA_OTG_ID_FSA9480_LAST_PRIO,
+ .mask = 0,
+ .fallback = true,
+ },
+};
+
+static struct fsa9480_platform_data tuna_fsa9480_pdata = {
+ .detect_time = 500,
+ .detect_sets = fsa_detect_sets,
+ .num_sets = ARRAY_SIZE(fsa_detect_sets),
+
+ .enable = tuna_mux_usb_to_fsa,
+ .detected = tuna_fsa_usb_detected,
+ .external_id = GPIO_USB_OTG_ID,
+
+ .external_vbus_irq = TWL6030_VBUS_IRQ,
+ .external_vbus_flags = TWL6030_VBUS_FLAGS,
+ .mask_vbus_irq = tuna_otg_mask_vbus_irq,
+ .unmask_vbus_irq = tuna_otg_unmask_vbus_irq,
+ .vbus_present = tuna_otg_vbus_present,
+};
+
+static struct i2c_board_info __initdata tuna_connector_i2c4_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("fsa9480", 0x4A >> 1),
+ .irq = OMAP_GPIO_IRQ(GPIO_JACK_INT_N),
+ .platform_data = &tuna_fsa9480_pdata,
+ },
+};
+
+static int tuna_otg_set_host(struct otg_transceiver *otg, struct usb_bus *host)
+{
+ otg->host = host;
+ if (!host)
+ otg->state = OTG_STATE_UNDEFINED;
+ return 0;
+}
+
+static int tuna_otg_set_peripheral(struct otg_transceiver *otg,
+ struct usb_gadget *gadget)
+{
+ otg->gadget = gadget;
+ if (!gadget)
+ otg->state = OTG_STATE_UNDEFINED;
+ return 0;
+}
+
+static void tuna_otg_work(struct work_struct *data)
+{
+ struct tuna_otg *tuna_otg = container_of(data, struct tuna_otg,
+ set_vbus_work);
+
+ mutex_lock(&tuna_otg->lock);
+
+ /* Only allow VBUS drive when in host mode. */
+ if (tuna_otg->current_device != FSA9480_DETECT_USB_HOST) {
+ mutex_unlock(&tuna_otg->lock);
+ return;
+ }
+
+ tuna_set_vbus_drive(tuna_otg->need_vbus_drive);
+
+ mutex_unlock(&tuna_otg->lock);
+}
+
+static int tuna_otg_set_vbus(struct otg_transceiver *otg, bool enabled)
+{
+ struct tuna_otg *tuna_otg = container_of(otg, struct tuna_otg, otg);
+
+ dev_dbg(otg->dev, "vbus %s\n", enabled ? "on" : "off");
+
+ tuna_otg->need_vbus_drive = enabled;
+ schedule_work(&tuna_otg->set_vbus_work);
+
+ return 0;
+}
+
+static int tuna_otg_phy_init(struct otg_transceiver *otg)
+{
+ if (otg->last_event == USB_EVENT_ID)
+ omap4430_phy_power(otg->dev, 1, 1);
+ else
+ omap4430_phy_power(otg->dev, 0, 1);
+ return 0;
+}
+
+static void tuna_otg_phy_shutdown(struct otg_transceiver *otg)
+{
+ omap4430_phy_power(otg->dev, 0, 0);
+}
+
+static int tuna_otg_set_suspend(struct otg_transceiver *otg, int suspend)
+{
+ return omap4430_phy_suspend(otg->dev, suspend);
+}
+
+static ssize_t tuna_otg_usb_sel_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tuna_otg *tuna_otg = dev_get_drvdata(dev);
+ const char* mode;
+
+ switch (tuna_otg->usb_manual_mode) {
+ case TUNA_MANUAL_USB_AP:
+ mode = "PDA";
+ break;
+ case TUNA_MANUAL_USB_MODEM:
+ mode = "MODEM";
+ break;
+ default:
+ mode = "NONE";
+ };
+
+ return sprintf(buf, "%s\n", mode);
+}
+
+static ssize_t tuna_otg_usb_sel_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct tuna_otg *tuna_otg = dev_get_drvdata(dev);
+ int old_mode;
+
+ mutex_lock(&tuna_otg->lock);
+
+ old_mode = tuna_otg->usb_manual_mode;
+
+ if (!strncasecmp(buf, "PDA", 3)) {
+ tuna_otg->usb_manual_mode = TUNA_MANUAL_USB_AP;
+
+ /* If we are transitioning from CP USB to AP USB then notify the
+ * USB stack that is now attached.
+ */
+ if (tuna_otg->current_device == FSA9480_DETECT_USB &&
+ old_mode == TUNA_MANUAL_USB_MODEM) {
+ tuna_cp_usb_detach(tuna_otg);
+ tuna_ap_usb_attach(tuna_otg);
+ }
+ } else if (!strncasecmp(buf, "MODEM", 5)) {
+ tuna_otg->usb_manual_mode = TUNA_MANUAL_USB_MODEM;
+
+ /* If we are transitioning from AP USB to CP USB then notify the
+ * USB stack that is has been detached.
+ */
+ if (tuna_otg->current_device == FSA9480_DETECT_USB &&
+ (old_mode == TUNA_MANUAL_USB_AP ||
+ old_mode == TUNA_MANUAL_USB_NONE)) {
+ tuna_ap_usb_detach(tuna_otg);
+ tuna_cp_usb_attach(tuna_otg);
+ }
+ } else if (!strncasecmp(buf, "NONE", 4)) {
+ tuna_otg->usb_manual_mode = TUNA_MANUAL_USB_NONE;
+
+ /* If we are transitioning from CP USB to AP USB then notify the
+ * USB stack that it is now attached.
+ */
+ if (tuna_otg->current_device == FSA9480_DETECT_USB &&
+ old_mode == TUNA_MANUAL_USB_MODEM) {
+ tuna_cp_usb_detach(tuna_otg);
+ tuna_ap_usb_attach(tuna_otg);
+ }
+ }
+
+ mutex_unlock(&tuna_otg->lock);
+
+ return size;
+}
+
+static ssize_t tuna_otg_uart_switch_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct tuna_otg *tuna_otg = dev_get_drvdata(dev);
+ const char* mode;
+
+ switch (tuna_otg->uart_manual_mode) {
+ case TUNA_MANUAL_UART_AP:
+ mode = "PDA";
+ break;
+ case TUNA_MANUAL_UART_MODEM:
+ mode = "MODEM";
+ break;
+ case TUNA_MANUAL_UART_LTE:
+ mode = "LTEMODEM";
+ break;
+ default:
+ mode = "NONE";
+ };
+
+ return sprintf(buf, "%s\n", mode);
+}
+
+static ssize_t tuna_otg_uart_switch_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct tuna_otg *tuna_otg = dev_get_drvdata(dev);
+
+ mutex_lock(&tuna_otg->lock);
+
+ if (!strncasecmp(buf, "PDA", 3)) {
+ tuna_otg->uart_manual_mode = TUNA_MANUAL_UART_AP;
+
+ if (tuna_otg->current_device == FSA9480_DETECT_JIG)
+ tuna_ap_uart_actions(tuna_otg);
+ } else if (!strncasecmp(buf, "MODEM", 5)) {
+ tuna_otg->uart_manual_mode = TUNA_MANUAL_UART_MODEM;
+
+ if (tuna_otg->current_device == FSA9480_DETECT_JIG)
+ tuna_cp_uart_actions(tuna_otg);
+ } else if (!strncasecmp(buf, "LTEMODEM", 8) &&
+ omap4_tuna_get_type() == TUNA_TYPE_TORO) {
+ tuna_otg->uart_manual_mode = TUNA_MANUAL_UART_LTE;
+
+ if (tuna_otg->current_device == FSA9480_DETECT_JIG)
+ tuna_lte_uart_actions(tuna_otg);
+ } else if (!strncasecmp(buf, "NONE", 4)) {
+ tuna_otg->uart_manual_mode = TUNA_MANUAL_UART_NONE;
+
+ if (tuna_otg->current_device == FSA9480_DETECT_JIG)
+ tuna_ap_uart_actions(tuna_otg);
+ }
+
+ mutex_unlock(&tuna_otg->lock);
+
+ return size;
+}
+
+#define OMAP_HDMI_HPD_ADDR 0x4A100098
+#define OMAP_HDMI_PULLTYPE_MASK 0x00000010
+static void sii9234_power(int on)
+{
+ struct omap_mux_partition *p = omap_mux_get("core");
+
+ u16 mux;
+
+ mux = omap_mux_read(p, OMAP4_CTRL_MODULE_PAD_HDMI_HPD_OFFSET);
+
+ if (on) {
+ gpio_set_value(GPIO_HDMI_EN, 1);
+ msleep(20);
+ gpio_set_value(GPIO_MHL_RST, 1);
+
+ omap_mux_write(p, mux | OMAP_PULL_UP,
+ OMAP4_CTRL_MODULE_PAD_HDMI_HPD_OFFSET);
+ } else {
+ omap_mux_write(p, mux & ~OMAP_PULL_UP,
+ OMAP4_CTRL_MODULE_PAD_HDMI_HPD_OFFSET);
+
+ gpio_set_value(GPIO_HDMI_EN, 0);
+ gpio_set_value(GPIO_MHL_RST, 0);
+
+ }
+}
+
+static void sii9234_enable_vbus(bool enable)
+{
+
+}
+
+static void sii9234_connect(bool on, u8 *devcap)
+{
+ struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
+ unsigned long val;
+ int dock = 0;
+
+ if (on) {
+ val = USB_EVENT_VBUS;
+ if (devcap) {
+ u16 adopter_id =
+ (devcap[MHL_DEVCAP_ADOPTER_ID_H] << 8) |
+ devcap[MHL_DEVCAP_ADOPTER_ID_L];
+ u16 device_id =
+ (devcap[MHL_DEVCAP_DEVICE_ID_H] << 8) |
+ devcap[MHL_DEVCAP_DEVICE_ID_L];
+
+ if (adopter_id == 0x3333 || adopter_id == 321) {
+ if (devcap[MHL_DEVCAP_RESERVED] == 2)
+ val = USB_EVENT_CHARGER;
+
+ if (device_id == 0x1234)
+ dock = 1;
+ }
+ }
+ } else {
+ val = USB_EVENT_NONE;
+ }
+
+ tuna_otg->otg.state = OTG_STATE_B_IDLE;
+ tuna_otg->otg.default_a = false;
+ tuna_otg->otg.last_event = val;
+
+ atomic_notifier_call_chain(&tuna_otg->otg.notifier,
+ val, tuna_otg->otg.gadget);
+ tuna_otg_set_dock_switch(dock);
+
+}
+
+void tuna_otg_pogo_charger(enum pogo_power_state pogo_state)
+{
+ struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
+ unsigned long power_state;
+
+ switch (pogo_state) {
+ case POGO_POWER_CHARGER:
+ power_state = USB_EVENT_CHARGER;
+ break;
+ case POGO_POWER_HOST:
+ power_state = USB_EVENT_VBUS;
+ break;
+ case POGO_POWER_DISCONNECTED:
+ default:
+ power_state = USB_EVENT_NONE;
+ break;
+ }
+
+ tuna_otg->otg.state = OTG_STATE_B_IDLE;
+ tuna_otg->otg.default_a = false;
+ tuna_otg->otg.last_event = power_state;
+ atomic_notifier_call_chain(&tuna_otg->otg.notifier, power_state,
+ tuna_otg->otg.gadget);
+}
+
+void tuna_otg_set_dock_switch(int enable)
+{
+ struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
+
+ switch_set_state(&tuna_otg->dock_switch, enable);
+}
+
+static struct sii9234_platform_data sii9234_pdata = {
+ .prio = TUNA_OTG_ID_SII9234_PRIO,
+ .enable = tuna_mux_usb_to_mhl,
+ .power = sii9234_power,
+ .enable_vbus = sii9234_enable_vbus,
+ .connect = sii9234_connect,
+};
+
+static struct i2c_board_info __initdata tuna_i2c5_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("sii9234_mhl_tx", 0x72>>1),
+ .irq = OMAP_GPIO_IRQ(GPIO_MHL_INT),
+ .platform_data = &sii9234_pdata,
+ },
+ {
+ I2C_BOARD_INFO("sii9234_tpi", 0x7A>>1),
+ .platform_data = &sii9234_pdata,
+ },
+ {
+ I2C_BOARD_INFO("sii9234_hdmi_rx", 0x92>>1),
+ .platform_data = &sii9234_pdata,
+ },
+ {
+ I2C_BOARD_INFO("sii9234_cbus", 0xC8>>1),
+ .platform_data = &sii9234_pdata,
+ },
+};
+
+int __init omap4_tuna_connector_init(void)
+{
+ struct tuna_otg *tuna_otg = &tuna_otg_xceiv;
+ int ret;
+
+ if (omap4_tuna_get_revision() >= 3) {
+ gpio_request(GPIO_MHL_SEL, "fsa3200_mhl_sel");
+ gpio_request(GPIO_AP_SEL, "fsa3200_ap_sel");
+
+ tuna_fsa3200_mux_pair(TUNA_USB_MUX_DEFAULT);
+
+ omap_mux_init_gpio(GPIO_MHL_SEL, OMAP_PIN_OUTPUT);
+ omap_mux_init_gpio(GPIO_AP_SEL, OMAP_PIN_OUTPUT);
+ } else {
+ gpio_request(GPIO_MUX3_SEL0, "usb_mux3_sel0");
+ gpio_request(GPIO_MUX3_SEL1, "usb_mux3_sel1");
+ gpio_request(GPIO_USB_ID_SEL, "usb_id_sel");
+
+ tuna_mux_usb(TUNA_USB_MUX_DEFAULT);
+ tuna_mux_usb_id(TUNA_USB_MUX_DEFAULT);
+
+ omap_mux_init_gpio(GPIO_MUX3_SEL0, OMAP_PIN_OUTPUT);
+ omap_mux_init_gpio(GPIO_MUX3_SEL1, OMAP_PIN_OUTPUT);
+ omap_mux_init_gpio(GPIO_USB_ID_SEL, OMAP_PIN_OUTPUT);
+ }
+
+ if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO) {
+ gpio_request(GPIO_CP_USB_ON, "cp_usb_on");
+ omap_mux_init_gpio(GPIO_CP_USB_ON, OMAP_PIN_OUTPUT);
+ gpio_direction_output(GPIO_CP_USB_ON, 0);
+ }
+
+ omap_mux_init_gpio(GPIO_IF_UART_SEL, OMAP_PIN_OUTPUT);
+ gpio_request(GPIO_IF_UART_SEL, "uart_sel");
+ gpio_direction_output(GPIO_IF_UART_SEL, IF_UART_SEL_DEFAULT);
+
+ omap_mux_init_gpio(GPIO_USB_OTG_ID, OMAP_PIN_INPUT |
+ OMAP_WAKEUP_EN);
+
+ omap_mux_init_gpio(GPIO_JACK_INT_N,
+ OMAP_PIN_INPUT_PULLUP |
+ OMAP_PIN_OFF_INPUT_PULLUP);
+
+ mutex_init(&tuna_otg->lock);
+
+ INIT_WORK(&tuna_otg->set_vbus_work, tuna_otg_work);
+
+ device_initialize(&tuna_otg->dev);
+ dev_set_name(&tuna_otg->dev, "%s", "tuna_otg");
+ ret = device_add(&tuna_otg->dev);
+ if (ret) {
+ pr_err("%s: cannot reg device '%s' (%d)\n", __func__,
+ dev_name(&tuna_otg->dev), ret);
+ return ret;
+ }
+
+ dev_set_drvdata(&tuna_otg->dev, tuna_otg);
+
+ tuna_otg->otg.dev = &tuna_otg->dev;
+ tuna_otg->otg.label = "tuna_otg_xceiv";
+ tuna_otg->otg.set_host = tuna_otg_set_host;
+ tuna_otg->otg.set_peripheral = tuna_otg_set_peripheral;
+ tuna_otg->otg.set_suspend = tuna_otg_set_suspend;
+ tuna_otg->otg.set_vbus = tuna_otg_set_vbus;
+ tuna_otg->otg.init = tuna_otg_phy_init;
+ tuna_otg->otg.shutdown = tuna_otg_phy_shutdown;
+
+ ATOMIC_INIT_NOTIFIER_HEAD(&tuna_otg->otg.notifier);
+
+ ret = otg_set_transceiver(&tuna_otg->otg);
+ if (ret)
+ pr_err("tuna_otg: cannot set transceiver (%d)\n", ret);
+
+ omap4430_phy_init(&tuna_otg->dev);
+ tuna_otg_set_suspend(&tuna_otg->otg, 0);
+
+ i2c_register_board_info(4, tuna_connector_i2c4_boardinfo,
+ ARRAY_SIZE(tuna_connector_i2c4_boardinfo));
+
+ ret = sysfs_create_group(&tuna_otg->dev.kobj, &manual_mode_group);
+ if (ret)
+ pr_err("tuna_otg: Unable to create manual mode sysfs group"
+ "(%d)\n", ret);
+
+ gpio_request(GPIO_HDMI_EN, NULL);
+ omap_mux_init_gpio(GPIO_HDMI_EN, OMAP_PIN_OUTPUT);
+ gpio_direction_output(GPIO_HDMI_EN, 0);
+
+ gpio_request(GPIO_MHL_RST, NULL);
+ omap_mux_init_gpio(GPIO_MHL_RST, OMAP_PIN_OUTPUT);
+ gpio_direction_output(GPIO_MHL_RST, 0);
+
+ gpio_request(GPIO_MHL_INT, NULL);
+ omap_mux_init_gpio(GPIO_MHL_INT, OMAP_PIN_INPUT);
+ gpio_direction_input(GPIO_MHL_INT);
+
+ gpio_request(TUNA_GPIO_HDMI_HPD, NULL);
+ omap_mux_init_gpio(TUNA_GPIO_HDMI_HPD, OMAP_PIN_INPUT | OMAP_PULL_ENA);
+ gpio_direction_input(TUNA_GPIO_HDMI_HPD);
+
+ i2c_register_board_info(5, tuna_i2c5_boardinfo,
+ ARRAY_SIZE(tuna_i2c5_boardinfo));
+
+ tuna_otg->dock_switch.name = "dock";
+ switch_dev_register(&tuna_otg->dock_switch);
+
+ return 0;
+}
diff --git a/arch/arm/mach-omap2/board-tuna-display.c b/arch/arm/mach-omap2/board-tuna-display.c
new file mode 100644
index 0000000..d593a53
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-display.c
@@ -0,0 +1,1126 @@
+/* Display panel support for Samsung Tuna Board.
+ *
+ * Copyright (C) 2011 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.
+ */
+
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/kernel.h>
+#include <linux/omapfb.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/platform_data/panel-s6e8aa0.h>
+
+#include <plat/vram.h>
+
+#include <video/omapdss.h>
+#include <video/omap-panel-generic-dpi.h>
+
+#include "board-tuna.h"
+#include "control.h"
+#include "mux.h"
+
+#define TUNA_FB_RAM_SIZE SZ_16M /* ~1280*720*4 * 2 */
+
+#define TUNA_GPIO_MLCD_RST 23
+
+/* 4.65" Panel ID Info (D1h 1st Para) */
+#define M3 0xA1
+#define SM2 0x12
+#define SM2A2 0xA2
+
+static unsigned int panel_id;
+struct regulator *tuna_oled_reg;
+struct regulator *tuna_oled_reg_iovcc;
+
+
+static void tuna_oled_set_power(bool enable)
+{
+ if (IS_ERR_OR_NULL(tuna_oled_reg)) {
+ tuna_oled_reg = regulator_get(NULL, "vlcd");
+ if (IS_ERR_OR_NULL(tuna_oled_reg)) {
+ pr_err("Can't get vlcd for display!\n");
+ return;
+ }
+ }
+
+ if (omap4_tuna_get_revision() >= 5) {
+ if (IS_ERR_OR_NULL(tuna_oled_reg_iovcc)) {
+ tuna_oled_reg_iovcc = regulator_get(NULL, "vlcd-iovcc");
+ if (IS_ERR_OR_NULL(tuna_oled_reg_iovcc)) {
+ pr_err("Can't get vlcd for display!\n");
+ return;
+ }
+ }
+
+ if (enable) {
+ regulator_enable(tuna_oled_reg_iovcc);
+ regulator_enable(tuna_oled_reg);
+ } else {
+ regulator_disable(tuna_oled_reg);
+ regulator_disable(tuna_oled_reg_iovcc);
+ }
+ } else {
+ if (enable)
+ regulator_enable(tuna_oled_reg);
+ else
+ regulator_disable(tuna_oled_reg);
+ }
+}
+
+static const struct s6e8aa0_acl_parameters tuna_oled_acl[] = {
+ {
+ .cd = 40,
+ .acl_val = 43,
+ .regs = {
+ 0xC1, /* ACL Control2 Register */
+ 0x47,
+ 0x53,
+ 0x13,
+ 0x53,
+ 0x00,
+ 0x00,
+ 0x02,
+ 0xCF,
+ 0x00,
+ 0x00,
+ 0x04,
+ 0xFF,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x01,
+ 0x07,
+ 0x0D,
+ 0x14,
+ 0x1A,
+ 0x20,
+ 0x26,
+ 0x2C,
+ 0x33,
+ 0x39,
+ 0x3F,
+ },
+ },
+ {
+ .cd = 300,
+ .acl_val = 45,
+ .regs = {
+ 0xC1, /* ACL Control2 Register */
+ 0x47,
+ 0x53,
+ 0x13,
+ 0x53,
+ 0x00,
+ 0x00,
+ 0x02,
+ 0xCF,
+ 0x00,
+ 0x00,
+ 0x04,
+ 0xFF,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x01,
+ 0x07,
+ 0x0E,
+ 0x14,
+ 0x1B,
+ 0x21,
+ 0x27,
+ 0x2E,
+ 0x34,
+ 0x3B,
+ 0x41,
+ },
+ },
+};
+
+static const struct s6e8aa0_elvss_parameters tuna_oled_elvss[] = {
+ {
+ .cd = 100,
+ .elvss_val = 0x11,
+ },
+ {
+ .cd = 160,
+ .elvss_val = 0x0D,
+ },
+ {
+ .cd = 200,
+ .elvss_val = 0x08,
+ },
+ {
+ .cd = 300,
+ .elvss_val = 0x00,
+ },
+};
+
+static const u8 tuna_oled_cmd_init_pre0[] = {
+ 0xF0,
+ 0x5A,
+ 0x5A,
+};
+
+static const u8 tuna_oled_cmd_init_pre1[] = {
+ 0xF1,
+ 0x5A,
+ 0x5A,
+};
+
+static const u8 tuna_oled_cmd_sleep_out[] = {
+ 0x11,
+};
+
+static const u8 tuna_oled_cmd_init_panel_m3[] = {
+ 0xF8, /* Panel Condition Set */
+ 0x3D, /* DOTC[0:1], GTCON[2:4], SS, DOTC_H[6:7] */
+ 0x35, /* FLTE[0:7] */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x8D,
+ 0x00,
+ 0x4C, /* SCTE[0:7] */
+ 0x6E,
+ 0x10,
+ 0x27,
+ 0x7D, /* INTE[0:7] */
+ 0x3F, /* INWE[0:7] */
+ 0x10,
+ 0x00,
+ 0x00,
+ 0x20,
+ 0x04, /* E_FLWE_H[0:7] */
+ 0x08, /* E_SCTE[0:7] */
+ 0x6E, /* E_SCWE[0:7] */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x02,
+ 0x08,
+ 0x08,
+ 0x23,
+ 0x23,
+ 0xC0,
+ 0xC8, /* CLK2_CON[0:2], CLK1_CON[3:5], CLK2_DC, CLK1_DC */
+ 0x08, /* INT2_CON[0:2], INT1_CON[3:5], INT2_DC, INT1_DC */
+ 0x48, /* BICTLB_CON[0:2], BICTL_CON[3:5], BICTLB_DC, BICTL_DC */
+ 0xC1,
+ 0x00,
+ 0xC3, /* EM_FLM_CON[0:2], ACL_FLM_CON[3:5], EM_FLM_DC, ACL_FLM_DC */
+ 0xFF, /* EM_CLK1B_CON[0:2], EM_CLK1_CON[3:5], EM_CLK1B_DC, EM_CLK1_DC */
+ 0xFF, /* EM_CLK2B_CON[0:2], EM_CLK2_CON[3:5], EM_CLK2B_DC, EM_CLK2_DC */
+ 0xC8, /* EM_INT2_CON[0:2], EM_INT1_CON[3:5], EM_INT2_DC, EM_INT1_DC */
+};
+
+static const u8 tuna_oled_cmd_init_panel_sm2[] = {
+ 0xF8, /* Panel Condition Set */
+ 0x3D, /* DOTC[0:1], GTCON[2:4], SS, DOTC_H[6:7] */
+ 0x31, /* FLTE[0:7] */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x8C, /* FLWE */
+ 0x00,
+ 0x39, /* SCTE[0:7] */
+ 0x77, /* SCWE */
+ 0x08, /* INTE */
+ 0x25,
+ 0x77, /* INTE[0:7] */
+ 0x3C, /* INWE[0:7] */
+ 0x00, /* EMPS */
+ 0x00,
+ 0x00,
+ 0x20,
+ 0x04, /* E_FLWE_H[0:7] */
+ 0x08, /* E_SCTE[0:7] */
+ 0x68, /* E_SCWE[0:7] */
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x02,
+ 0x07,
+ 0x07,
+ 0x21,
+ 0x21,
+ 0xC0,
+ 0xC8, /* CLK2_CON[0:2], CLK1_CON[3:5], CLK2_DC, CLK1_DC */
+ 0x08, /* INT2_CON[0:2], INT1_CON[3:5], INT2_DC, INT1_DC */
+ 0x48, /* BICTLB_CON[0:2], BICTL_CON[3:5], BICTLB_DC, BICTL_DC */
+ 0xC1,
+ 0x00,
+ 0xC1, /* EM_FLM_CON[0:2], ACL_FLM_CON[3:5], EM_FLM_DC, ACL_FLM_DC */
+ 0xFF, /* EM_CLK1B_CON[0:2], EM_CLK1_CON[3:5], EM_CLK1B_DC, EM_CLK1_DC */
+ 0xFF, /* EM_CLK2B_CON[0:2], EM_CLK2_CON[3:5], EM_CLK2B_DC, EM_CLK2_DC */
+ 0xC8, /* EM_INT2_CON[0:2], EM_INT1_CON[3:5], EM_INT2_DC, EM_INT1_DC */
+};
+
+static const u8 tuna_oled_cmd_init_display[] = {
+ 0xF2, /* Display Condition set */
+ 0x80, /* Display area */
+ 0x03, /* VBP : 3 HsYNC */
+ 0x0D, /* VFP : 13HSYNC */
+};
+
+static const struct s6e8aa0_sequence_entry tuna_oled_seq_display_set_m3[] = {
+ {
+ .cmd = tuna_oled_cmd_init_pre0,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_pre0),
+ },
+ {
+ .cmd = tuna_oled_cmd_sleep_out,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_sleep_out),
+ .msleep = 5,
+ },
+ {
+ .cmd = tuna_oled_cmd_init_panel_m3,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_panel_m3),
+ },
+ {
+ .cmd = tuna_oled_cmd_init_display,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_display),
+ },
+};
+
+static const struct s6e8aa0_sequence_entry tuna_oled_seq_display_set_sm2[] = {
+ {
+ .cmd = tuna_oled_cmd_init_pre0,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_pre0),
+ },
+ {
+ .cmd = tuna_oled_cmd_init_pre1,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_pre1),
+ },
+ {
+ .cmd = tuna_oled_cmd_sleep_out,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_sleep_out),
+ .msleep = 5,
+ },
+ {
+ .cmd = tuna_oled_cmd_init_panel_sm2,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_panel_sm2),
+ },
+ {
+ .cmd = tuna_oled_cmd_init_display,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_display),
+ },
+};
+
+static const u8 tuna_oled_cmd_gamma_ltps_update[] = {
+ 0xF7,
+ 0x03, /* Gamma/LTPS update */
+};
+
+static const u8 tuna_oled_cmd_init_post0[] = {
+ 0xF6,
+ 0x00,
+ 0x02,
+ 0x00,
+};
+
+static const u8 tuna_oled_cmd_init_post1[] = {
+ 0xB6,
+ 0x0C,
+ 0x02,
+ 0x03,
+ 0x32,
+ 0xFF,
+ 0x44,
+ 0x44,
+ 0xC0,
+ 0x00,
+};
+
+static const u8 tuna_oled_cmd_init_post2_m3[] = {
+ 0xD9,
+ 0x14,
+ 0x40,
+ 0x0C,
+ 0xCB,
+ 0xCE,
+ 0x6E,
+ 0xC4,
+ 0x07, /* COLUMN_CHOP, FRAME_CHOP, LINE_CHOP, CHOP_EN */
+ 0x40,
+ 0x40, /* ELVSS_CON : 0 */
+ 0xD0, /* ELVSS -4.9V */
+ 0x00,
+ 0x60,
+ 0x19,
+};
+
+static const u8 tuna_oled_cmd_power_ctrl_m3[] = {
+ 0xF4, /* Power Control */
+ 0xCF,
+ 0x0A,
+ 0x0F, /* Vreg1 : 4.5V(default) */
+ 0x10, /* VGH : 5.2v(default) */
+ 0x19, /* VGL : -7.0v(default) */
+ 0x33,
+ 0x02,
+};
+
+static const u8 tuna_oled_cmd_init_post2_sm2[] = {
+ 0xD9,
+ 0x14,
+ 0x40,
+ 0x0C,
+ 0xCB,
+ 0xCE,
+ 0x6E,
+ 0xC4,
+ 0x07, /* COLUMN_CHOP, FRAME_CHOP, LINE_CHOP, CHOP_EN */
+ 0x40,
+ 0x41, /* ELVSS_CON : 1 */
+ 0xD0, /* ELVSS -4.9V */
+ 0x00,
+ 0x60,
+ 0x19,
+};
+
+static const u8 tuna_oled_cmd_power_ctrl_sm2[] = {
+ 0xF4, /* Power Control */
+ 0xCF,
+ 0x0A,
+ 0x12, /* Vreg1 : 4.6V */
+ 0x10, /* VGH : 5.2v(default) */
+ 0x1E, /* VGL : -8.0v */
+ 0x33,
+ 0x02,
+};
+
+static const u8 tuna_oled_cmd_display_on[] = {
+ 0x29,
+};
+
+static const struct s6e8aa0_sequence_entry tuna_oled_seq_etc_set_m3[] = {
+ {
+ .cmd = tuna_oled_cmd_gamma_ltps_update,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_gamma_ltps_update),
+ },
+ {
+ .cmd = tuna_oled_cmd_init_post0,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_post0),
+ },
+ {
+ .cmd = tuna_oled_cmd_init_post1,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_post1),
+ },
+ {
+ .cmd = tuna_oled_cmd_init_post2_m3,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_post2_m3),
+ },
+ {
+ .cmd = tuna_oled_cmd_power_ctrl_m3,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_power_ctrl_m3),
+ .msleep = 120,
+ },
+ {
+ .cmd = tuna_oled_cmd_display_on,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_display_on),
+ },
+};
+
+static const struct s6e8aa0_sequence_entry tuna_oled_seq_etc_set_sm2[] = {
+ {
+ .cmd = tuna_oled_cmd_gamma_ltps_update,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_gamma_ltps_update),
+ },
+ {
+ .cmd = tuna_oled_cmd_init_post0,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_post0),
+ },
+ {
+ .cmd = tuna_oled_cmd_init_post1,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_post1),
+ },
+ {
+ .cmd = tuna_oled_cmd_init_post2_sm2,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_init_post2_sm2),
+ },
+ {
+ .cmd = tuna_oled_cmd_power_ctrl_sm2,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_power_ctrl_sm2),
+ .msleep = 120,
+ },
+ {
+ .cmd = tuna_oled_cmd_display_on,
+ .cmd_len = ARRAY_SIZE(tuna_oled_cmd_display_on),
+ },
+};
+
+static const struct s6e8aa0_gamma_entry tuna_oled_gamma_table_m3[] = {
+ { BV_0, { 4500000, 4500000, 4500000, }, },
+ { 0x00000001, { 4350000, 4350000, 4350000, }, },
+ { 0x0001F8F0, { 4320166, 4338185, 4200000, }, },
+ { 0x0002F71F, { 4305148, 4332238, 4102500, }, },
+ { 0x00038485, { 4296793, 4328930, 4065000, }, },
+ { 0x00053C55, { 4270807, 4318639, 3982500, }, },
+ { 0x00060EEF, { 4258364, 4313711, 3960000, }, },
+ { 0x00075444, { 4239141, 4306099, 3930000, }, },
+ { 0x00095733, { 4208717, 4294050, 3892500, }, },
+ { 0x000EC060, { 4126874, 4261640, 3810000, }, },
+ { 0x00129EAB, { 4068363, 4238469, 3757500, }, },
+ { 0x00179214, { 3993478, 4208813, 3720000, }, },
+ { 0x001A47A0, { 3952500, 4192586, 3712500, }, },
+ { 0x0025E12C, { 3802500, 4123103, 3682500, }, },
+ { 0x002D413D, { 3756465, 4078926, 3673450, }, },
+ { 0x0035D13F, { 3741586, 4027637, 3659844, }, },
+ { 0x00400000, { 3726401, 3966643, 3645758, }, },
+ { 0x004C1BF8, { 3710894, 3894110, 3631166, }, },
+ { 0x005A827A, { 3695052, 3855649, 3616042, }, },
+ { 0x006BA27E, { 3678859, 3823316, 3600360, }, },
+ { 0x00800000, { 3662298, 3794488, 3584091, }, },
+ { 0x009837F0, { 3645351, 3767841, 3567202, }, },
+ { 0x00B504F3, { 3627999, 3742607, 3549662, }, },
+ { 0x00D744FD, { 3610220, 3718295, 3531434, }, },
+ { 0x01000000, { 3591992, 3694568, 3512481, }, },
+ { 0x01306FE1, { 3573291, 3671183, 3492761, }, },
+ { 0x016A09E6, { 3554089, 3647957, 3472232, }, },
+ { 0x01AE89FA, { 3534358, 3624743, 3450847, }, },
+ { 0x02000000, { 3514065, 3601422, 3428554, }, },
+ { 0x0260DFC1, { 3493177, 3577893, 3405301, }, },
+ { 0x02D413CD, { 3471654, 3554067, 3381029, }, },
+ { 0x035D13F3, { 3449455, 3529864, 3355676, }, },
+ { 0x04000000, { 3426534, 3505211, 3329174, }, },
+ { 0x04C1BF83, { 3402839, 3480034, 3301451, }, },
+ { 0x05A8279A, { 3378312, 3454264, 3272429, }, },
+ { 0x06BA27E6, { 3352890, 3427831, 3242021, }, },
+ { 0x08000000, { 3326501, 3400661, 3210138, }, },
+ { 0x09837F05, { 3299062, 3372679, 3176678, }, },
+ { 0x0B504F33, { 3270483, 3343804, 3141535, }, },
+ { 0x0D744FCD, { 3240658, 3313948, 3104592, }, },
+ { 0x10000000, { 3209466, 3283015, 3065722, }, },
+ { 0x1306FE0A, { 3176767, 3250899, 3024787, }, },
+ { 0x16A09E66, { 3142400, 3217481, 2981638, }, },
+ { 0x1AE89F99, { 3106171, 3182624, 2936111, }, },
+ { 0x20000000, { 3067855, 3146174, 2888031, }, },
+ { 0x260DFC14, { 3027178, 3107951, 2837203, }, },
+ { 0x2D413CCD, { 2983809, 3067744, 2783420, }, },
+ { 0x35D13F32, { 2937340, 3025303, 2726454, }, },
+ { 0x40000000, { 2887259, 2980328, 2666057, }, },
+ { 0x4C1BF828, { 2832912, 2932454, 2601961, }, },
+ { 0x5A82799A, { 2773444, 2881230, 2533878, }, },
+ { 0x6BA27E65, { 2707706, 2826086, 2461495, }, },
+ { 0x80000000, { 2639098, 2766291, 2384478, }, },
+ { 0x9837F051, { 2560291, 2700878, 2302469, }, },
+ { 0xB504F333, { 2472698, 2628531, 2215093, }, },
+ { 0xD744FCCA, { 2375331, 2547389, 2121959, }, },
+ { 0xFFFFFFFF, { 2266945, 2454682, 2022667, }, },
+};
+
+static const struct s6e8aa0_gamma_entry tuna_oled_gamma_table_sm2[] = {
+ { BV_0, { 4600000, 4600000, 4600000, }, },
+ { 0x00000001, { 4561667, 4561667, 4561667, }, },
+ { 0x000004C2, { 4102930, 4561654, 4561115, }, },
+ { 0x000005A8, { 4093308, 4561651, 3799195, }, },
+ { 0x000006BA, { 4083466, 4561645, 3793888, }, },
+ { 0x00000800, { 4073413, 4561639, 3788484, }, },
+ { 0x00000983, { 4063166, 4561630, 3782992, }, },
+ { 0x00000B50, { 4052685, 4561618, 3777391, }, },
+ { 0x00000D74, { 4041989, 4561602, 3771689, }, },
+ { 0x00001000, { 4031064, 4561582, 3765880, }, },
+ { 0x00001307, { 4019915, 4561555, 3759964, }, },
+ { 0x000016A1, { 4008527, 4561519, 3753935, }, },
+ { 0x00001AE9, { 3996905, 4561472, 3747792, }, },
+ { 0x00002000, { 3985042, 4561410, 3741533, }, },
+ { 0x0000260E, { 3972926, 4561328, 3735148, }, },
+ { 0x00002D41, { 3960557, 4561219, 3728639, }, },
+ { 0x000035D1, { 3947926, 4561076, 3721998, }, },
+ { 0x00004000, { 3935029, 4560888, 3715222, }, },
+ { 0x00004C1C, { 3921862, 4560639, 3708307, }, },
+ { 0x00005A82, { 3908420, 4560310, 3701250, }, },
+ { 0x00006BA2, { 3894694, 4559877, 3694045, }, },
+ { 0x00008000, { 3880678, 4559305, 3686685, }, },
+ { 0x00009838, { 3866369, 4558550, 3679168, }, },
+ { 0x0000B505, { 3851759, 4557554, 3671487, }, },
+ { 0x0000D745, { 3836842, 4556240, 3663637, }, },
+ { 0x00010000, { 3821612, 4554507, 3655612, }, },
+ { 0x00013070, { 3806062, 4552219, 3647405, }, },
+ { 0x00016A0A, { 3790185, 4549200, 3639010, }, },
+ { 0x0001AE8A, { 3773975, 4545217, 3630420, }, },
+ { 0x00020000, { 3757424, 4539961, 3621628, }, },
+ { 0x000260E0, { 3740525, 4533026, 3612625, }, },
+ { 0x0002D414, { 3723271, 4523876, 3603405, }, },
+ { 0x00035D14, { 3705654, 4511801, 3593959, }, },
+ { 0x00040000, { 3687668, 4495869, 3584277, }, },
+ { 0x0004C1C0, { 3669303, 4474845, 3574351, }, },
+ { 0x0005A828, { 3650553, 4447106, 3564170, }, },
+ { 0x0006BA28, { 3631408, 4410503, 3553725, }, },
+ { 0x00080000, { 3611862, 4362204, 3543003, }, },
+ { 0x0009837F, { 3591904, 4298475, 3531993, }, },
+ { 0x000B504F, { 3571527, 4214383, 3520683, }, },
+ { 0x000D7450, { 3557593, 4103423, 3509060, }, },
+ { 0x00100000, { 3544015, 4010592, 3497110, }, },
+ { 0x001306FE, { 3530633, 3962306, 3484817, }, },
+ { 0x0016A09E, { 3517328, 3926468, 3472166, }, },
+ { 0x001AE8A0, { 3504007, 3896492, 3459141, }, },
+ { 0x00200000, { 3490597, 3869832, 3445722, }, },
+ { 0x00260DFC, { 3477036, 3845220, 3431893, }, },
+ { 0x002D413D, { 3463269, 3821929, 3417630, }, },
+ { 0x0035D13F, { 3449251, 3799494, 3402914, }, },
+ { 0x00400000, { 3434937, 3777603, 3387721, }, },
+ { 0x004C1BF8, { 3420287, 3756027, 3372025, }, },
+ { 0x005A827A, { 3405262, 3734596, 3355799, }, },
+ { 0x006BA27E, { 3389824, 3713176, 3339016, }, },
+ { 0x00800000, { 3373936, 3691658, 3321643, }, },
+ { 0x009837F0, { 3357560, 3669948, 3303646, }, },
+ { 0x00B504F3, { 3340656, 3647968, 3284991, }, },
+ { 0x00D744FD, { 3323186, 3625646, 3265636, }, },
+ { 0x01000000, { 3305106, 3602915, 3245540, }, },
+ { 0x01306FE1, { 3286372, 3579714, 3224657, }, },
+ { 0x016A09E6, { 3266937, 3555983, 3202935, }, },
+ { 0x01AE89FA, { 3246751, 3531662, 3180321, }, },
+ { 0x02000000, { 3225759, 3506692, 3156754, }, },
+ { 0x0260DFC1, { 3203902, 3481011, 3132167, }, },
+ { 0x02D413CD, { 3181115, 3454554, 3106490, }, },
+ { 0x035D13F3, { 3157329, 3427255, 3079643, }, },
+ { 0x04000000, { 3132467, 3399041, 3051538, }, },
+ { 0x04C1BF83, { 3106444, 3369833, 3022078, }, },
+ { 0x05A8279A, { 3079166, 3339546, 2991155, }, },
+ { 0x06BA27E6, { 3050528, 3308086, 2958650, }, },
+ { 0x08000000, { 3020414, 3275348, 2924429, }, },
+ { 0x09837F05, { 2988694, 3241215, 2888342, }, },
+ { 0x0B504F33, { 2955220, 3205555, 2850219, }, },
+ { 0x0D744FCD, { 2919827, 3168219, 2809871, }, },
+ { 0x10000000, { 2882325, 3129034, 2767081, }, },
+ { 0x1306FE0A, { 2842499, 3087803, 2721603, }, },
+ { 0x16A09E66, { 2800102, 3044294, 2673154, }, },
+ { 0x1AE89F99, { 2754846, 2998238, 2621408, }, },
+ { 0x20000000, { 2706399, 2949314, 2565989, }, },
+ { 0x260DFC14, { 2654372, 2897137, 2506458, }, },
+ { 0x2D413CCD, { 2598304, 2841239, 2442298, }, },
+ { 0x35D13F32, { 2537647, 2781048, 2372900, }, },
+ { 0x40000000, { 2471743, 2715846, 2297536, }, },
+ { 0x4C1BF828, { 2399793, 2644720, 2215328, }, },
+ { 0x5A82799A, { 2320814, 2566484, 2125212, }, },
+ { 0x6BA27E65, { 2233581, 2479554, 2025874, }, },
+ { 0x80000000, { 2136547, 2381755, 1915679, }, },
+ { 0x9837F051, { 2027719, 2269975, 1792556, }, },
+ { 0xB504F333, { 1904479, 2139541, 1653843, }, },
+ { 0xD744FCCA, { 1763299, 1982960, 1496041, }, },
+ { 0xFFFFFFFF, { 1599291, 1787064, 1314455, }, },
+};
+
+static const struct s6e8aa0_gamma_entry tuna_oled_gamma_table_sm2a2[] = {
+ { BV_0, { 4600000, 4600000, 4600000, }, },
+ { 0x00000001, { 4561667, 4561667, 4561667, }, },
+ { 0x000004C2, { 4320569, 4507119, 4172023, }, },
+ { 0x000005A8, { 4313803, 4504412, 4164881, }, },
+ { 0x000006BA, { 4306834, 4501566, 4157595, }, },
+ { 0x00000800, { 4299666, 4498576, 4150172, }, },
+ { 0x00000983, { 4292309, 4495443, 4142625, }, },
+ { 0x00000B50, { 4284732, 4492149, 4134926, }, },
+ { 0x00000D74, { 4276946, 4488692, 4127090, }, },
+ { 0x00001000, { 4268937, 4485062, 4119106, }, },
+ { 0x00001307, { 4260707, 4481254, 4110980, }, },
+ { 0x000016A1, { 4252243, 4477254, 4102702, }, },
+ { 0x00001AE9, { 4243544, 4473058, 4094275, }, },
+ { 0x00002000, { 4234603, 4468654, 4085695, }, },
+ { 0x0000260E, { 4225408, 4464030, 4076956, }, },
+ { 0x00002D41, { 4215956, 4459176, 4068057, }, },
+ { 0x000035D1, { 4206236, 4454081, 4058993, }, },
+ { 0x00004000, { 4196243, 4448731, 4049762, }, },
+ { 0x00004C1C, { 4185969, 4443116, 4040363, }, },
+ { 0x00005A82, { 4175408, 4437223, 4030792, }, },
+ { 0x00006BA2, { 4164549, 4431036, 4021044, }, },
+ { 0x00008000, { 4153383, 4424541, 4011116, }, },
+ { 0x00009838, { 4141905, 4417724, 4001007, }, },
+ { 0x0000B505, { 4130104, 4410567, 3990713, }, },
+ { 0x0000D745, { 4117971, 4403055, 3980229, }, },
+ { 0x00010000, { 4105497, 4395170, 3969553, }, },
+ { 0x00013070, { 4092672, 4386892, 3958681, }, },
+ { 0x00016A0A, { 4079487, 4378203, 3947609, }, },
+ { 0x0001AE8A, { 4065931, 4369081, 3936334, }, },
+ { 0x00020000, { 4051994, 4359507, 3924852, }, },
+ { 0x000260E0, { 4037665, 4349456, 3913159, }, },
+ { 0x0002D414, { 4022934, 4338906, 3901251, }, },
+ { 0x00035D14, { 4007788, 4327831, 3889125, }, },
+ { 0x00040000, { 3992216, 4316205, 3876776, }, },
+ { 0x0004C1C0, { 3976207, 4304001, 3864200, }, },
+ { 0x0005A828, { 3959747, 4291191, 3851393, }, },
+ { 0x0006BA28, { 3942825, 4277744, 3838351, }, },
+ { 0x00080000, { 3925427, 4263628, 3825070, }, },
+ { 0x0009837F, { 3907540, 4248811, 3811545, }, },
+ { 0x000B504F, { 3889150, 4233257, 3797772, }, },
+ { 0x000D7450, { 3873393, 4216929, 3783745, }, },
+ { 0x00100000, { 3857665, 4199790, 3769461, }, },
+ { 0x001306FE, { 3841894, 4181799, 3754915, }, },
+ { 0x0016A09E, { 3826021, 4162913, 3740102, }, },
+ { 0x001AE8A0, { 3809994, 4143088, 3725017, }, },
+ { 0x00200000, { 3793770, 4122278, 3709654, }, },
+ { 0x00260DFC, { 3777308, 4100433, 3694010, }, },
+ { 0x002D413D, { 3760573, 4077502, 3677663, }, },
+ { 0x0035D13F, { 3743530, 4053431, 3660904, }, },
+ { 0x00400000, { 3726147, 4028163, 3643715, }, },
+ { 0x004C1BF8, { 3708392, 4001639, 3626075, }, },
+ { 0x005A827A, { 3690235, 3973797, 3607960, }, },
+ { 0x006BA27E, { 3671644, 3953685, 3589346, }, },
+ { 0x00800000, { 3652589, 3933053, 3570207, }, },
+ { 0x009837F0, { 3633036, 3911876, 3550514, }, },
+ { 0x00B504F3, { 3612951, 3890122, 3530237, }, },
+ { 0x00D744FD, { 3592301, 3867762, 3509342, }, },
+ { 0x01000000, { 3571046, 3844761, 3487793, }, },
+ { 0x01306FE1, { 3549148, 3821082, 3465552, }, },
+ { 0x016A09E6, { 3526565, 3796686, 3442574, }, },
+ { 0x01AE89FA, { 3503250, 3771528, 3418813, }, },
+ { 0x02000000, { 3479155, 3745560, 3394218, }, },
+ { 0x0260DFC1, { 3454224, 3718731, 3368732, }, },
+ { 0x02D413CD, { 3428401, 3690982, 3342292, }, },
+ { 0x035D13F3, { 3401619, 3662250, 3314830, }, },
+ { 0x04000000, { 3373808, 3632465, 3286268, }, },
+ { 0x04C1BF83, { 3344890, 3601547, 3256522, }, },
+ { 0x05A8279A, { 3314777, 3569411, 3225495, }, },
+ { 0x06BA27E6, { 3283370, 3535958, 3193081, }, },
+ { 0x08000000, { 3250562, 3501080, 3159159, }, },
+ { 0x09837F05, { 3216227, 3464651, 3123590, }, },
+ { 0x0B504F33, { 3180227, 3426533, 3086220, }, },
+ { 0x0D744FCD, { 3142401, 3386564, 3046868, }, },
+ { 0x10000000, { 3102567, 3344561, 3005329, }, },
+ { 0x1306FE0A, { 3060513, 3300311, 2961362, }, },
+ { 0x16A09E66, { 3015995, 3253568, 2914685, }, },
+ { 0x1AE89F99, { 2968726, 3204040, 2864970, }, },
+ { 0x20000000, { 2918367, 3151384, 2811819, }, },
+ { 0x260DFC14, { 2864513, 3095191, 2754761, }, },
+ { 0x2D413CCD, { 2806678, 3034964, 2693215, }, },
+ { 0x35D13F32, { 2744269, 2970096, 2626472, }, },
+ { 0x40000000, { 2676551, 2899833, 2553637, }, },
+ { 0x4C1BF828, { 2602603, 2823224, 2473575, }, },
+ { 0x5A82799A, { 2521246, 2739043, 2384804, }, },
+ { 0x6BA27E65, { 2430943, 2645676, 2285353, }, },
+ { 0x80000000, { 2329631, 2540937, 2172509, }, },
+ { 0x9837F051, { 2214459, 2421765, 2042412, }, },
+ { 0xB504F333, { 2081337, 2283682, 1889300, }, },
+ { 0xD744FCCA, { 1924091, 2119771, 1704041, }, },
+ { 0xFFFFFFFF, { 1732795, 1918520, 1470915, }, },
+};
+
+static struct s6e8aa0_factory_calibration_info tuna_oled_factory_info_old = {
+ .regs = {
+ [1][0][6] = 0x090,
+ [1][1][6] = 0x081,
+ [1][2][6] = 0x0c5,
+ },
+ .brightness = {
+ [1][6] = BV_255, /* 300 cd/m2 */
+ },
+ .color_adj = {
+ /* Convert from 8500K to D65, assuming:
+ * Rx 0.66950, Ry 0.33100
+ * Gx 0.18800, Gy 0.74350
+ * Bx 0.14142, By 0.04258
+ */
+ .mult = {
+ 2318372099U,
+ 2117262806U,
+ 1729744557U,
+ },
+ .rshift = 31,
+ },
+};
+
+static struct s6e8aa0_factory_calibration_info tuna_oled_factory_info_m2t1 = {
+ .regs = {
+ [1][0][6] = 0x090,
+ [1][1][6] = 0x081,
+ [1][2][6] = 0x0c5,
+ },
+ .brightness = {
+ [1][6] = BV_255, /* 300 cd/m2 */
+ },
+};
+
+static struct s6e8aa0_factory_calibration_info tuna_oled_factory_info_8500k = {
+ .regs = {
+ [1][0][0] = 0x0f,
+ [1][1][0] = 0x0f,
+ [1][2][0] = 0x0f,
+
+ [1][0][1] = 0xcc,
+ [1][1][1] = 0x9c,
+ [1][2][1] = 0xd7,
+
+ [1][0][2] = 0xc1,
+ [1][1][2] = 0xba,
+ [1][2][2] = 0xc1,
+
+ [1][0][3] = 0xcf,
+ [1][1][3] = 0xcd,
+ [1][2][3] = 0xcf,
+
+ [1][0][4] = 0xaa,
+ [1][1][4] = 0xaa,
+ [1][2][4] = 0xa7,
+
+ [1][0][5] = 0xbe,
+ [1][1][5] = 0xbe,
+ [1][2][5] = 0xba,
+
+ [1][0][6] = 0x090,
+ [1][1][6] = 0x081,
+ [1][2][6] = 0x0c5,
+ },
+ .brightness = {
+ [1][4] = 403193777, /* 28.16275996 cd/m2 */
+ [1][6] = BV_255, /* 300 cd/m2 */
+ },
+ .color_adj = {
+ /* Convert from 8500K to D65, assuming:
+ * Rx 0.66950, Ry 0.33100
+ * Gx 0.18800, Gy 0.74350
+ * Bx 0.14142, By 0.04258
+ *
+ * These values are adjusted down by x 0.9333 to bring
+ * maximum brightness down from 300 cd/m2 to 280.
+ */
+ .mult = {
+ 2163736680U,
+ 1976041377U,
+ 1614370595U,
+ },
+ .rshift = 31,
+ },
+};
+
+static struct s6e8aa0_factory_calibration_info tuna_oled_factory_info_6500k = {
+ .regs = {
+ [1][0][0] = 0x7c, /* sRGB Gamma 300cd, 6500K */
+ [1][1][0] = 0x3c,
+ [1][2][0] = 0x87,
+
+ [1][0][1] = 0xbb,
+ [1][1][1] = 0xd4,
+ [1][2][1] = 0xa8,
+
+ [1][0][2] = 0xac,
+ [1][1][2] = 0xc0,
+ [1][2][2] = 0xa1,
+
+ [1][0][3] = 0xb8,
+ [1][1][3] = 0xc8,
+ [1][2][3] = 0xb2,
+
+ [1][0][4] = 0x8c,
+ [1][1][4] = 0x9f,
+ [1][2][4] = 0x84,
+
+ [1][0][5] = 0xa7,
+ [1][1][5] = 0xb2,
+ [1][2][5] = 0xa3,
+
+ [1][0][6] = 0x0d8,
+ [1][1][6] = 0x0bd,
+ [1][2][6] = 0x0f7,
+ },
+ .brightness = {
+ [1][1] = BV_15, /* 1.43 cd/m2 */
+ [1][2] = BV_35, /* 5.04 cd/m2 */
+ [1][3] = BV_59, /* 13.12 cd/m2 */
+ [1][4] = BV_87, /* 28.59 cd/m2 */
+ [1][5] = BV_171, /* 122.17 cd/m2 */
+ [1][6] = BV_255, /* 300 cd/m2 */
+ },
+ .color_adj = {
+ /*
+ * These values are adjusted down by x 0.9333 to bring
+ * maximum brightness down from 300 cd/m2 to 280.
+ */
+ .mult = {
+ 2004318071U,
+ 2004318071U,
+ 2004318071U,
+ },
+ .rshift = 31,
+ },
+};
+
+static struct s6e8aa0_factory_calibration_info tuna_oled_factory_info_sm2a2 = {
+ .regs = {
+ [1][0][0] = 0x52, /* sRGB Gamma 300cd, 6500K, A2 Line */
+ [1][1][0] = 0x24,
+ [1][2][0] = 0x5d,
+
+ [1][0][1] = 0xba,
+ [1][1][1] = 0xcd,
+ [1][2][1] = 0xb3,
+
+ [1][0][2] = 0xad,
+ [1][1][2] = 0xc0,
+ [1][2][2] = 0xb1,
+
+ [1][0][3] = 0xbf,
+ [1][1][3] = 0xc7,
+ [1][2][3] = 0xbc,
+
+ [1][0][4] = 0x90,
+ [1][1][4] = 0x97,
+ [1][2][4] = 0x8a,
+
+ [1][0][5] = 0xaa,
+ [1][1][5] = 0xae,
+ [1][2][5] = 0xa5,
+
+ [1][0][6] = 0x0c2,
+ [1][1][6] = 0x0a8,
+ [1][2][6] = 0x0d7,
+ },
+ .brightness = {
+ [1][1] = BV_15, /* 1.43 cd/m2 */
+ [1][2] = BV_35, /* 5.04 cd/m2 */
+ [1][3] = BV_59, /* 13.12 cd/m2 */
+ [1][4] = BV_87, /* 28.59 cd/m2 */
+ [1][5] = BV_171, /* 122.17 cd/m2 */
+ [1][6] = BV_255, /* 300 cd/m2 */
+ },
+ .color_adj = {
+ /*
+ * These values are adjusted down by x 0.9333 to bring
+ * maximum brightness down from 300 cd/m2 to 280.
+ */
+ .mult = {
+ 2004318071U,
+ 2004318071U,
+ 2004318071U,
+ },
+ .rshift = 31,
+ },
+};
+
+static struct panel_s6e8aa0_data tuna_oled_data_m3 = {
+ .reset_gpio = TUNA_GPIO_MLCD_RST,
+ .set_power = tuna_oled_set_power,
+ .seq_display_set = tuna_oled_seq_display_set_m3,
+ .seq_display_set_size = ARRAY_SIZE(tuna_oled_seq_display_set_m3),
+ .seq_etc_set = tuna_oled_seq_etc_set_m3,
+ .seq_etc_set_size = ARRAY_SIZE(tuna_oled_seq_etc_set_m3),
+ .gamma_table = tuna_oled_gamma_table_m3,
+ .gamma_table_size = ARRAY_SIZE(tuna_oled_gamma_table_m3),
+ .factory_info = &tuna_oled_factory_info_8500k,
+ .acl_table = tuna_oled_acl,
+ .acl_table_size = ARRAY_SIZE(tuna_oled_acl),
+ .acl_average = 6, /* use 20 frame Y average accumulation count */
+};
+
+static struct panel_s6e8aa0_data tuna_oled_data_sm2 = {
+ .reset_gpio = TUNA_GPIO_MLCD_RST,
+ .set_power = tuna_oled_set_power,
+ .seq_display_set = tuna_oled_seq_display_set_sm2,
+ .seq_display_set_size = ARRAY_SIZE(tuna_oled_seq_display_set_sm2),
+ .seq_etc_set = tuna_oled_seq_etc_set_sm2,
+ .seq_etc_set_size = ARRAY_SIZE(tuna_oled_seq_etc_set_sm2),
+ .gamma_table = tuna_oled_gamma_table_sm2,
+ .gamma_table_size = ARRAY_SIZE(tuna_oled_gamma_table_sm2),
+ .factory_info = &tuna_oled_factory_info_6500k,
+ .acl_table = tuna_oled_acl,
+ .acl_table_size = ARRAY_SIZE(tuna_oled_acl),
+ .acl_average = 6, /* use 20 frame Y average accumulation count */
+ .elvss_table = tuna_oled_elvss,
+ .elvss_table_size = ARRAY_SIZE(tuna_oled_elvss),
+};
+
+static struct panel_s6e8aa0_data tuna_oled_data_sm2a2 = {
+ .reset_gpio = TUNA_GPIO_MLCD_RST,
+ .set_power = tuna_oled_set_power,
+ .seq_display_set = tuna_oled_seq_display_set_sm2,
+ .seq_display_set_size = ARRAY_SIZE(tuna_oled_seq_display_set_sm2),
+ .seq_etc_set = tuna_oled_seq_etc_set_sm2,
+ .seq_etc_set_size = ARRAY_SIZE(tuna_oled_seq_etc_set_sm2),
+ .gamma_table = tuna_oled_gamma_table_sm2a2,
+ .gamma_table_size = ARRAY_SIZE(tuna_oled_gamma_table_sm2a2),
+ .factory_info = &tuna_oled_factory_info_sm2a2,
+ .acl_table = tuna_oled_acl,
+ .acl_table_size = ARRAY_SIZE(tuna_oled_acl),
+ .acl_average = 6, /* use 20 frame Y average accumulation count */
+ .elvss_table = tuna_oled_elvss,
+ .elvss_table_size = ARRAY_SIZE(tuna_oled_elvss),
+};
+
+static struct omap_dss_device tuna_oled_device = {
+ .name = "lcd",
+ .driver_name = "s6e8aa0",
+ .type = OMAP_DISPLAY_TYPE_DSI,
+ .phy.dsi = {
+ .type = OMAP_DSS_DSI_TYPE_VIDEO_MODE,
+ .clk_lane = 1,
+ .clk_pol = 0,
+ .data1_lane = 2,
+ .data1_pol = 0,
+ .data2_lane = 3,
+ .data2_pol = 0,
+ .data3_lane = 4,
+ .data3_pol = 0,
+ .data4_lane = 5,
+ .data4_pol = 0,
+ },
+ .panel = {
+ .width_in_um = 58000,
+ .height_in_um = 102000,
+ },
+ .clocks = {
+ .dispc = {
+ .channel = {
+ .lck_div = 1, /* LCD */
+ .pck_div = 2, /* PCD */
+ .lcd_clk_src
+ = OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DISPC,
+ },
+ .dispc_fclk_src = OMAP_DSS_CLK_SRC_FCK,
+ },
+ .dsi = {
+ .regn = 19, /* DSI_PLL_REGN */
+ .regm = 236, /* DSI_PLL_REGM */
+
+ .regm_dispc = 6, /* PLL_CLK1 (M4) */
+ .regm_dsi = 6, /* PLL_CLK2 (M5) */
+ .lp_clk_div = 8, /* LPDIV */
+ .offset_ddr_clk = 122, /* DDR PRE & DDR POST
+ * offset increase
+ */
+
+ .dsi_fclk_src = OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI,
+ },
+ },
+
+ .channel = OMAP_DSS_CHANNEL_LCD,
+#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT
+ .skip_init = true,
+#else
+ .skip_init = false,
+#endif
+};
+
+static void tuna_hdmi_mux_init(void)
+{
+ u32 r;
+
+ /* PAD0_HDMI_HPD_PAD1_HDMI_CEC */
+ omap_mux_init_signal("hdmi_hpd.hdmi_hpd",
+ OMAP_PIN_INPUT_PULLDOWN);
+ omap_mux_init_signal("gpmc_wait2.gpio_100",
+ OMAP_PIN_INPUT_PULLDOWN);
+ omap_mux_init_signal("hdmi_cec.hdmi_cec",
+ OMAP_PIN_INPUT_PULLUP);
+ /* PAD0_HDMI_DDC_SCL_PAD1_HDMI_DDC_SDA */
+ omap_mux_init_signal("hdmi_ddc_scl.hdmi_ddc_scl",
+ OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_signal("hdmi_ddc_sda.hdmi_ddc_sda",
+ OMAP_PIN_INPUT_PULLUP);
+
+ /* strong pullup on DDC lines using unpublished register */
+ r = ((1 << 24) | (1 << 28)) ;
+ omap4_ctrl_pad_writel(r, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_I2C_1);
+
+}
+
+static struct omap_dss_device tuna_hdmi_device = {
+ .name = "hdmi",
+ .driver_name = "hdmi_panel",
+ .type = OMAP_DISPLAY_TYPE_HDMI,
+ .clocks = {
+ .dispc = {
+ .dispc_fclk_src = OMAP_DSS_CLK_SRC_FCK,
+ },
+ .hdmi = {
+ .regn = 15,
+ .regm2 = 1,
+ .max_pixclk_khz = 75000,
+ },
+ },
+ .hpd_gpio = TUNA_GPIO_HDMI_HPD,
+ .channel = OMAP_DSS_CHANNEL_DIGIT,
+};
+
+static struct omap_dss_device *tuna_dss_devices[] = {
+ &tuna_oled_device,
+ &tuna_hdmi_device,
+};
+
+static struct omap_dss_board_info tuna_dss_data = {
+ .num_devices = ARRAY_SIZE(tuna_dss_devices),
+ .devices = tuna_dss_devices,
+ .default_device = &tuna_oled_device,
+};
+
+static struct omapfb_platform_data tuna_fb_pdata = {
+ .mem_desc = {
+ .region_cnt = 1,
+ .region = {
+ [0] = {
+ .size = TUNA_FB_RAM_SIZE,
+ },
+ },
+ },
+};
+
+void __init omap4_tuna_display_init(void)
+{
+ struct panel_s6e8aa0_data *panel;
+
+ if (omap4_tuna_get_revision() ==
+ (omap4_tuna_get_type() == TUNA_TYPE_MAGURO ? 2 : 1)) {
+ /*
+ * Older devices were not calibrated the same way as newer
+ * devices. These values are probably not correct, but the older
+ * devices tested look closer to the newer devices with these
+ * values than they do using the same register values as the
+ * newer devices.
+ */
+ tuna_oled_data_m3.factory_info = &tuna_oled_factory_info_m2t1;
+ } else if (omap4_tuna_get_revision() <= 1) {
+ tuna_oled_data_m3.factory_info = &tuna_oled_factory_info_old;
+ }
+
+ switch (panel_id) {
+ case SM2:
+ panel = &tuna_oled_data_sm2;
+ break;
+ case SM2A2:
+ panel = &tuna_oled_data_sm2a2;
+ break;
+ default:
+ panel = &tuna_oled_data_m3;
+ break;
+ }
+
+ tuna_oled_device.data = panel;
+
+ omap4_ctrl_pad_writel(0x1FF80000,
+ OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_DSIPHY);
+ omap_mux_init_gpio(panel->reset_gpio, OMAP_PIN_OUTPUT);
+
+ pr_info("Using %ps\n", panel->factory_info);
+
+ omap_vram_set_sdram_vram(TUNA_FB_RAM_SIZE, 0);
+ omapfb_set_platform_data(&tuna_fb_pdata);
+ tuna_hdmi_mux_init();
+ omap_display_init(&tuna_dss_data);
+}
+
+static int __init get_panel_id(char *str)
+{
+ long value;
+ int ret;
+
+ ret = strict_strtol(str, 0, &value);
+ if (ret < 0)
+ return ret;
+
+ panel_id = (unsigned int)value;
+ return 0;
+}
+__setup("mms_ts.panel_id=", get_panel_id);
+
diff --git a/arch/arm/mach-omap2/board-tuna-emif.c b/arch/arm/mach-omap2/board-tuna-emif.c
new file mode 100644
index 0000000..84819ce
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-emif.c
@@ -0,0 +1,108 @@
+/*
+ * LPDDR2 data as per SAMSUNG data sheet
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * Santosh Shilimkar <santosh.shilimkar@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+
+#include <mach/emif.h>
+#include "board-tuna.h"
+
+const struct lpddr2_timings lpddr2_samsung_timings_400_mhz = {
+ .max_freq = 400000000,
+ .RL = 6,
+ .tRPab = 21,
+ .tRCD = 18,
+ .tWR = 15,
+ .tRASmin = 42,
+ .tRRD = 10,
+ .tWTRx2 = 15,
+ .tXSR = 140,
+ .tXPx2 = 15,
+ .tRFCab = 130,
+ .tRTPx2 = 15,
+ .tCKE = 3,
+ .tCKESR = 15,
+ .tZQCS = 90,
+ .tZQCL = 360,
+ .tZQINIT = 1000,
+ .tDQSCKMAXx2 = 11,
+ .tRASmax = 70,
+ .tFAW = 50,
+};
+
+const struct lpddr2_timings lpddr2_samsung_timings_200_mhz = {
+ .max_freq = 200000000,
+ .RL = 3,
+ .tRPab = 21,
+ .tRCD = 18,
+ .tWR = 15,
+ .tRASmin = 42,
+ .tRRD = 10,
+ .tWTRx2 = 20,
+ .tXSR = 140,
+ .tXPx2 = 15,
+ .tRFCab = 130,
+ .tRTPx2 = 15,
+ .tCKE = 3,
+ .tCKESR = 15,
+ .tZQCS = 90,
+ .tZQCL = 360,
+ .tZQINIT = 1000,
+ .tDQSCKMAXx2 = 11,
+ .tRASmax = 70,
+ .tFAW = 50,
+};
+
+const struct lpddr2_min_tck lpddr2_samsung_min_tck = {
+ .tRL = 3,
+ .tRP_AB = 3,
+ .tRCD = 3,
+ .tWR = 3,
+ .tRAS_MIN = 3,
+ .tRRD = 2,
+ .tWTR = 2,
+ .tXP = 2,
+ .tRTP = 2,
+ .tCKE = 3,
+ .tCKESR = 3,
+ .tFAW = 8
+};
+
+struct lpddr2_device_info lpddr2_samsung_4G_S4_dev = {
+ .device_timings = {
+ &lpddr2_samsung_timings_200_mhz,
+ &lpddr2_samsung_timings_400_mhz
+ },
+ .min_tck = &lpddr2_samsung_min_tck,
+ .type = LPDDR2_TYPE_S4,
+ .density = LPDDR2_DENSITY_4Gb,
+ .io_width = LPDDR2_IO_WIDTH_32,
+ .emif_ddr_selfrefresh_cycles = 262144,
+};
+
+/*
+ * LPDDR2 Configuration Data:
+ * The memory organisation is as below :
+ * EMIF1 - CS0 - 4 Gb
+ * EMIF2 - CS0 - 4 Gb
+ * --------------------
+ * TOTAL - 8 Gb
+ *
+ * Same devices installed on EMIF1 and EMIF2
+ */
+static __initdata struct emif_device_details emif_devices = {
+ .cs0_device = &lpddr2_samsung_4G_S4_dev,
+};
+
+void __init omap4_tuna_emif_init(void)
+{
+ omap_emif_setup_device_details(&emif_devices, &emif_devices);
+}
diff --git a/arch/arm/mach-omap2/board-tuna-input.c b/arch/arm/mach-omap2/board-tuna-input.c
new file mode 100644
index 0000000..6c12dd0
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-input.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/keyreset.h>
+#include <linux/gpio_event.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/i2c/atmel_mxt_ts.h>
+#include <linux/platform_data/mms_ts.h>
+#include <asm/mach-types.h>
+#include <plat/omap4-keypad.h>
+
+#include "board-tuna.h"
+#include "mux.h"
+
+#define GPIO_TOUCH_EN 19
+#define GPIO_TOUCH_IRQ 46
+
+/* touch is on i2c3 */
+#define GPIO_TOUCH_SCL 130
+#define GPIO_TOUCH_SDA 131
+
+static int mms_ts_panel_id;
+
+static struct gpio_event_direct_entry tuna_gpio_keypad_keys_map_high[] = {
+ {
+ .code = KEY_POWER,
+ .gpio = 3,
+ },
+};
+
+static struct gpio_event_input_info tuna_gpio_keypad_keys_info_high = {
+ .info.func = gpio_event_input_func,
+ .info.no_suspend = true,
+ .type = EV_KEY,
+ .keymap = tuna_gpio_keypad_keys_map_high,
+ .keymap_size = ARRAY_SIZE(tuna_gpio_keypad_keys_map_high),
+ .flags = GPIOEDF_ACTIVE_HIGH,
+ .debounce_time.tv64 = 2 * NSEC_PER_MSEC,
+};
+
+static struct gpio_event_direct_entry tuna_gpio_keypad_keys_map_low[] = {
+ {
+ .code = KEY_VOLUMEDOWN,
+ .gpio = 8,
+ },
+ {
+ .code = KEY_VOLUMEUP,
+ .gpio = 30,
+ },
+};
+
+static struct gpio_event_input_info tuna_gpio_keypad_keys_info_low = {
+ .info.func = gpio_event_input_func,
+ .info.no_suspend = true,
+ .type = EV_KEY,
+ .keymap = tuna_gpio_keypad_keys_map_low,
+ .keymap_size = ARRAY_SIZE(tuna_gpio_keypad_keys_map_low),
+ .debounce_time.tv64 = 2 * NSEC_PER_MSEC,
+};
+
+static struct gpio_event_info *tuna_gpio_keypad_info[] = {
+ &tuna_gpio_keypad_keys_info_high.info,
+ &tuna_gpio_keypad_keys_info_low.info,
+};
+
+static struct gpio_event_platform_data tuna_gpio_keypad_data = {
+ .name = "tuna-gpio-keypad",
+ .info = tuna_gpio_keypad_info,
+ .info_count = ARRAY_SIZE(tuna_gpio_keypad_info)
+};
+
+static struct platform_device tuna_gpio_keypad_device = {
+ .name = GPIO_EVENT_DEV_NAME,
+ .id = 0,
+ .dev = {
+ .platform_data = &tuna_gpio_keypad_data,
+ },
+};
+
+static int melfas_mux_fw_flash(bool to_gpios)
+{
+ /* TOUCH_EN is always an output */
+ if (to_gpios) {
+ gpio_direction_output(GPIO_TOUCH_IRQ, 0);
+ omap_mux_set_gpio(
+ OMAP_PIN_INPUT | OMAP_MUX_MODE3,
+ GPIO_TOUCH_IRQ);
+
+ gpio_direction_output(GPIO_TOUCH_SCL, 0);
+ omap_mux_set_gpio(OMAP_PIN_INPUT | OMAP_MUX_MODE3,
+ GPIO_TOUCH_SCL);
+
+ gpio_direction_output(GPIO_TOUCH_SDA, 0);
+ omap_mux_set_gpio(OMAP_PIN_INPUT | OMAP_MUX_MODE3,
+ GPIO_TOUCH_SDA);
+ } else {
+ gpio_direction_output(GPIO_TOUCH_IRQ, 1);
+ gpio_direction_input(GPIO_TOUCH_IRQ);
+ omap_mux_set_gpio(
+ OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE3,
+ GPIO_TOUCH_IRQ);
+
+ gpio_direction_output(GPIO_TOUCH_SCL, 1);
+ gpio_direction_input(GPIO_TOUCH_SCL);
+ omap_mux_set_gpio(OMAP_PIN_INPUT | OMAP_MUX_MODE0,
+ GPIO_TOUCH_SCL);
+
+ gpio_direction_output(GPIO_TOUCH_SDA, 1);
+ gpio_direction_input(GPIO_TOUCH_SDA);
+ omap_mux_set_gpio(OMAP_PIN_INPUT | OMAP_MUX_MODE0,
+ GPIO_TOUCH_SDA);
+ }
+
+ return 0;
+}
+
+static int __init mms_ts_panel_id_setup(char *str)
+{
+ mms_ts_panel_id = simple_strtol(str, NULL, 0);
+ return 1;
+}
+__setup("mms_ts.panel_id=", mms_ts_panel_id_setup);
+
+static struct mms_ts_platform_data mms_ts_pdata = {
+ .max_x = 720,
+ .max_y = 1280,
+ .mux_fw_flash = melfas_mux_fw_flash,
+ .gpio_resetb = GPIO_TOUCH_IRQ,
+ .gpio_vdd_en = GPIO_TOUCH_EN,
+ .gpio_scl = GPIO_TOUCH_SCL,
+ .gpio_sda = GPIO_TOUCH_SDA,
+};
+
+static struct i2c_board_info __initdata tuna_i2c3_boardinfo_final[] = {
+ {
+ I2C_BOARD_INFO("mms_ts", 0x48),
+ .flags = I2C_CLIENT_WAKE,
+ .platform_data = &mms_ts_pdata,
+ .irq = OMAP_GPIO_IRQ(GPIO_TOUCH_IRQ),
+ },
+};
+
+void __init omap4_tuna_input_init(void)
+{
+ gpio_request(GPIO_TOUCH_IRQ, "tsp_int_n");
+ gpio_direction_input(GPIO_TOUCH_IRQ);
+ omap_mux_init_gpio(GPIO_TOUCH_IRQ,
+ OMAP_PIN_INPUT_PULLUP);
+
+ gpio_request(GPIO_TOUCH_EN, "tsp_en");
+ gpio_direction_output(GPIO_TOUCH_EN, 1);
+ omap_mux_init_gpio(GPIO_TOUCH_EN, OMAP_PIN_OUTPUT);
+ gpio_request(GPIO_TOUCH_SCL, "ap_i2c3_scl");
+ gpio_request(GPIO_TOUCH_SDA, "ap_i2c3_sda");
+
+ /* 0x12 == FPCB 3.2
+ * 0xa1 == FPCB 3.1
+ */
+ if (mms_ts_panel_id == 0x12)
+ mms_ts_pdata.fw_name = "mms144_ts_rev32.fw";
+ else
+ mms_ts_pdata.fw_name = "mms144_ts_rev31.fw";
+
+ i2c_register_board_info(3, tuna_i2c3_boardinfo_final,
+ ARRAY_SIZE(tuna_i2c3_boardinfo_final));
+
+ omap_mux_init_gpio(8, OMAP_PIN_INPUT);
+ omap_mux_init_gpio(30, OMAP_PIN_INPUT);
+
+ platform_device_register(&tuna_gpio_keypad_device);
+}
diff --git a/arch/arm/mach-omap2/board-tuna-jack.c b/arch/arm/mach-omap2/board-tuna-jack.c
new file mode 100644
index 0000000..d3dc6ac
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-jack.c
@@ -0,0 +1,152 @@
+/* Board support file for Samsung Tuna Board.
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Based on mach-omap2/board-omap4panda.c
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/i2c/twl6030-madc.h>
+#include <linux/sec_jack.h>
+
+#include "mux.h"
+#include "board-tuna.h"
+
+#define GPIO_EAR_MICBIAS_EN 49
+#define GPIO_DET_35 0
+#define GPIO_EAR_SEND_END 1
+
+#define ADC_CHANNEL_JACK 2
+
+static void sec_jack_set_micbias_state(bool on)
+{
+ gpio_set_value(GPIO_EAR_MICBIAS_EN, on);
+}
+
+static struct sec_jack_zone sec_jack_zones[] = {
+ {
+ /* adc < 50, unstable zone, default to 3pole if it stays
+ * in this range for a half second (20ms delays, 25 samples)
+ */
+ .adc_high = 50,
+ .delay_ms = 20,
+ .check_count = 25,
+ .jack_type = SEC_HEADSET_3POLE,
+ },
+ {
+ /* 50 < adc <= 490, unstable zone, default to 3pole if it stays
+ * in this range for a second (10ms delays, 100 samples)
+ */
+ .adc_high = 490,
+ .delay_ms = 10,
+ .check_count = 100,
+ .jack_type = SEC_HEADSET_3POLE,
+ },
+ {
+ /* 490 < adc <= 900, unstable zone, default to 4pole if it
+ * stays in this range for a second (10ms delays, 100 samples)
+ */
+ .adc_high = 900,
+ .delay_ms = 10,
+ .check_count = 100,
+ .jack_type = SEC_HEADSET_4POLE,
+ },
+ {
+ /* 900 < adc <= 1500, 4 pole zone, default to 4pole if it
+ * stays in this range for 200ms (20ms delays, 10 samples)
+ */
+ .adc_high = 1500,
+ .delay_ms = 20,
+ .check_count = 10,
+ .jack_type = SEC_HEADSET_4POLE,
+ },
+ {
+ /* adc > 1500, unstable zone, default to 3pole if it stays
+ * in this range for a second (10ms delays, 100 samples)
+ */
+ .adc_high = 0x7fffffff,
+ .delay_ms = 10,
+ .check_count = 100,
+ .jack_type = SEC_HEADSET_3POLE,
+ },
+};
+
+/* To support 3-buttons earjack */
+static struct sec_jack_buttons_zone sec_jack_buttons_zones[] = {
+ {
+ /* 0 <= adc <= 93, stable zone */
+ .code = KEY_MEDIA,
+ .adc_low = 0,
+ .adc_high = 93,
+ },
+ {
+ /* 94 <= adc <= 167, stable zone */
+ .code = KEY_PREVIOUSSONG,
+ .adc_low = 94,
+ .adc_high = 167,
+ },
+ {
+ /* 168 <= adc <= 370, stable zone */
+ .code = KEY_NEXTSONG,
+ .adc_low = 168,
+ .adc_high = 370,
+ },
+};
+
+static int sec_jack_get_adc_value(void)
+{
+ int value;
+
+ value = twl6030_get_madc_conversion(ADC_CHANNEL_JACK);
+ return (int)(1800*value) / 1024;
+}
+
+struct sec_jack_platform_data sec_jack_pdata = {
+ .set_micbias_state = sec_jack_set_micbias_state,
+ .get_adc_value = sec_jack_get_adc_value,
+ .zones = sec_jack_zones,
+ .num_zones = ARRAY_SIZE(sec_jack_zones),
+ .buttons_zones = sec_jack_buttons_zones,
+ .num_buttons_zones = ARRAY_SIZE(sec_jack_buttons_zones),
+ .det_gpio = GPIO_DET_35,
+ .send_end_gpio = GPIO_EAR_SEND_END,
+};
+
+static struct platform_device sec_device_jack = {
+ .name = "sec_jack",
+ .id = 1, /* will be used also for gpio_event id */
+ .dev.platform_data = &sec_jack_pdata,
+};
+
+void __init omap4_tuna_jack_init(void)
+{
+ omap_mux_init_signal("sim_io.gpio_wk0", OMAP_PIN_INPUT);
+ gpio_request(GPIO_DET_35, "det_35_en");
+ gpio_direction_input(GPIO_DET_35);
+
+ omap_mux_init_signal("sim_clk.gpio_wk1", OMAP_PIN_INPUT);
+ gpio_request(GPIO_EAR_SEND_END, "ear_send_end");
+ gpio_direction_input(GPIO_EAR_SEND_END);
+
+ omap_mux_init_signal("gpmc_a25.gpio_49", OMAP_PIN_OUTPUT | OMAP_MUX_MODE3);
+ gpio_request(GPIO_EAR_MICBIAS_EN, "ear_micbias_en");
+ gpio_direction_output(GPIO_EAR_MICBIAS_EN, 0);
+
+ gpio_free(GPIO_DET_35);
+ gpio_free(GPIO_EAR_SEND_END);
+ platform_device_register(&sec_device_jack);
+}
diff --git a/arch/arm/mach-omap2/board-tuna-modems.c b/arch/arm/mach-omap2/board-tuna-modems.c
new file mode 100755
index 0000000..410e3da
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-modems.c
@@ -0,0 +1,760 @@
+/* linux/arch/arm/mach-xxxx/board-tuna-modems.c
+ * Copyright (C) 2010 Samsung Electronics. All rights reserved.
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+
+#include <mach/omap4-common.h>
+#include <linux/platform_data/modem.h>
+#include "board-tuna.h"
+#include "mux.h"
+
+#define OMAP_GPIO_MIPI_HSI_CP_ON 53
+#define OMAP_GPIO_MIPI_HSI_RESET_REQ_N 50
+#define OMAP_GPIO_MIPI_HSI_CP_RST 15
+#define OMAP_GPIO_MIPI_HSI_PDA_ACTIVE 119
+#define OMAP_GPIO_MIPI_HSI_PHONE_ACTIVE 120
+#define OMAP_GPIO_MIPI_HSI_CP_DUMP_INT 95
+#define OMAP_GPIO_MIPI_HSI_GPS_UART_SEL 164
+
+#define OMAP_GPIO_DPRAM_VIA_RST 15
+#define OMAP_GPIO_DPRAM_PDA_ACTIVE 119
+#define OMAP_GPIO_DPRAM_PHONE_ACTIVE 120
+#define OMAP_GPIO_DPRAM_PWRHOLD_OFF 53
+#define OMAP_GPIO_DPRAM_INT_N 62
+
+#define OMAP_GPIO_CMC_SPI_CLK_ACK 178
+#define OMAP_GPIO_CMC_SPI_CLK_REQ 164
+#define OMAP_GPIO_CMC_SPI_WAKEUP_INT 134
+#define OMAP_GPIO_LTE_ACTIVE 47
+#define OMAP_GPIO_CMC2AP_INT1 61
+#define OMAP_GPIO_CMC2AP_INT2 160
+#define OMAP_GPIO_AP2CMC_INT1 18
+#define OMAP_GPIO_AP2CMC_INT2 28
+#define OMAP_GPIO_221_PMIC_PWRON 41
+#define OMAP_GPIO_CMC_RST 50
+#define OMAP_GPIO_221_PMIC_PWRHOLD_OFF 163
+
+#define DPRAM_START_ADDRESS 0x04000000
+#define DPRAM_SIZE 0x4000
+#define DPRAM_END_ADDRESS (DPRAM_START_ADDRESS + DPRAM_SIZE - 1)
+
+#define GPMC_CONTROL_BASE_ADDR 0x50000000
+#define GPMC_CONFIG1_1 (GPMC_CONTROL_BASE_ADDR + 0x90)
+#define GPMC_CONFIG2_1 (GPMC_CONTROL_BASE_ADDR + 0x94)
+#define GPMC_CONFIG3_1 (GPMC_CONTROL_BASE_ADDR + 0x98)
+#define GPMC_CONFIG4_1 (GPMC_CONTROL_BASE_ADDR + 0x9C)
+#define GPMC_CONFIG5_1 (GPMC_CONTROL_BASE_ADDR + 0xA0)
+#define GPMC_CONFIG6_1 (GPMC_CONTROL_BASE_ADDR + 0xA4)
+#define GPMC_CONFIG7_1 (GPMC_CONTROL_BASE_ADDR + 0xA8)
+
+#define DPRAM_GPMC_CONFIG1 0x00001201
+#define DPRAM_GPMC_CONFIG2 0x000f1200
+#define DPRAM_GPMC_CONFIG3 0x44040400
+#define DPRAM_GPMC_CONFIG4 0x0e05f155
+#define DPRAM_GPMC_CONFIG5 0x000e1016
+#define DPRAM_GPMC_CONFIG6 0x060603c3
+#define DPRAM_GPMC_CONFIG7 0x00000F44
+
+/* umts target platform data */
+static struct modem_io_t umts_io_devices[] = {
+ [0] = {
+ .name = "umts_ipc0",
+ .id = 0x1,
+ .format = IPC_FMT,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_MIPI,
+ },
+ [1] = {
+ .name = "umts_rfs0",
+ .id = 0x41,
+ .format = IPC_RFS,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_MIPI,
+ },
+ [2] = {
+ .name = "rmnet0",
+ .id = 0x2A,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_MIPI,
+ },
+ [3] = {
+ .name = "umts_boot0",
+ .id = 0x0,
+ .format = IPC_BOOT,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_MIPI,
+ },
+ [4] = {
+ .name = "rmnet1",
+ .id = 0x2B,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_MIPI,
+ },
+ [5] = {
+ .name = "rmnet2",
+ .id = 0x2C,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_MIPI,
+ },
+ [6] = {
+ .name = "multipdp",
+ .id = 0x1,
+ .format = IPC_MULTI_RAW,
+ .io_type = IODEV_DUMMY,
+ .link = LINKDEV_MIPI,
+ },
+ [7] = {
+ .name = "umts_ramdump0",
+ .id = 0x0,
+ .format = IPC_RAMDUMP,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_MIPI,
+ },
+ [8] = {
+ .name = "umts_boot1",
+ .id = 0x1,
+ .format = IPC_BOOT,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_MIPI,
+ },
+};
+
+static struct modem_data umts_modem_data = {
+ .name = "xmm6260",
+
+ .gpio_cp_on = OMAP_GPIO_MIPI_HSI_CP_ON,
+ .gpio_reset_req_n = OMAP_GPIO_MIPI_HSI_RESET_REQ_N,
+ .gpio_cp_reset = OMAP_GPIO_MIPI_HSI_CP_RST,
+ .gpio_pda_active = OMAP_GPIO_MIPI_HSI_PDA_ACTIVE,
+ .gpio_phone_active = OMAP_GPIO_MIPI_HSI_PHONE_ACTIVE,
+ .gpio_cp_dump_int = OMAP_GPIO_MIPI_HSI_CP_DUMP_INT,
+ .gpio_flm_uart_sel = OMAP_GPIO_MIPI_HSI_GPS_UART_SEL,
+ .gpio_cp_warm_reset = 0,
+
+ .modem_type = IMC_XMM6260,
+ .link_type = LINKDEV_MIPI,
+ .modem_net = UMTS_NETWORK,
+
+ .num_iodevs = ARRAY_SIZE(umts_io_devices),
+ .iodevs = umts_io_devices,
+};
+
+static void umts_modem_cfg_gpio(void)
+{
+ unsigned gpio_reset_req_n = umts_modem_data.gpio_reset_req_n;
+ unsigned gpio_cp_on = umts_modem_data.gpio_cp_on;
+ unsigned gpio_cp_rst = umts_modem_data.gpio_cp_reset;
+ unsigned gpio_pda_active = umts_modem_data.gpio_pda_active;
+ unsigned gpio_phone_active = umts_modem_data.gpio_phone_active;
+ unsigned gpio_cp_dump_int = umts_modem_data.gpio_cp_dump_int;
+ unsigned gpio_flm_uart_sel = umts_modem_data.gpio_flm_uart_sel;
+
+ /* gpio mux setting */
+ omap_mux_init_signal("gpmc_ncs0.gpio_50", OMAP_PIN_OUTPUT |
+ OMAP_OFF_EN | OMAP_OFFOUT_VAL);
+ omap_mux_init_signal("gpmc_ncs3.gpio_53", OMAP_PIN_OUTPUT |
+ OMAP_OFF_EN);
+ omap_mux_init_signal("dpm_emu4.gpio_15", OMAP_PIN_OUTPUT |
+ OMAP_OFF_EN | OMAP_OFFOUT_VAL);
+ omap_mux_init_signal("abe_dmic_clk1.gpio_119", OMAP_PIN_OUTPUT |
+ OMAP_OFF_EN);
+ omap_mux_init_signal("abe_dmic_din1.gpio_120", OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_signal("usbb1_ulpitll_dat7.gpio_95", OMAP_PIN_INPUT);
+ omap_mux_init_signal("usbb2_ulpitll_dat3.gpio_164", OMAP_PIN_OUTPUT |
+ OMAP_OFF_EN | OMAP_OFFOUT_VAL);
+ omap_mux_init_signal("uart3_cts_rctx.uart1_tx", OMAP_PIN_INPUT);
+ omap_mux_init_signal("mcspi1_cs1.uart1_rx", OMAP_PIN_INPUT);
+
+ if (gpio_reset_req_n) {
+ gpio_request(gpio_reset_req_n, "RESET_REQ_N");
+ gpio_direction_output(gpio_reset_req_n, 0);
+ }
+
+ if (gpio_cp_on) {
+ gpio_request(gpio_cp_on, "CP_ON");
+ gpio_direction_output(gpio_cp_on, 0);
+ }
+
+ if (gpio_cp_rst) {
+ gpio_request(gpio_cp_rst, "CP_RST");
+ gpio_direction_output(gpio_cp_rst, 0);
+ }
+
+ if (gpio_pda_active) {
+ gpio_request(gpio_pda_active, "PDA_ACTIVE");
+ gpio_direction_output(gpio_pda_active, 0);
+ }
+
+ if (gpio_phone_active) {
+ gpio_request(gpio_phone_active, "PHONE_ACTIVE");
+ gpio_direction_input(gpio_phone_active);
+ }
+
+ if (gpio_cp_dump_int) {
+ gpio_request(gpio_cp_dump_int, "CP_DUMP_INT");
+ gpio_direction_input(gpio_cp_dump_int);
+ }
+
+ if (gpio_flm_uart_sel) {
+ gpio_request(gpio_flm_uart_sel, "GPS_UART_SEL");
+ gpio_direction_output(gpio_flm_uart_sel, 1);
+ }
+
+ if (gpio_phone_active)
+ irq_set_irq_type(
+ OMAP_GPIO_IRQ(OMAP_GPIO_MIPI_HSI_PHONE_ACTIVE),
+ IRQ_TYPE_LEVEL_HIGH);
+
+ pr_debug("umts_modem_cfg_gpio done\n");
+}
+
+/* To get modem state, register phone active irq using resource */
+static struct resource umts_modem_res[] = {
+ [0] = {
+ .name = "umts_phone_active",
+ .start = OMAP_GPIO_IRQ(OMAP_GPIO_MIPI_HSI_PHONE_ACTIVE),
+ .end = OMAP_GPIO_IRQ(OMAP_GPIO_MIPI_HSI_PHONE_ACTIVE),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+/* if use more than one modem device, then set id num */
+static struct platform_device umts_modem = {
+ .name = "modem_if",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(umts_modem_res),
+ .resource = umts_modem_res,
+ .dev = {
+ .platform_data = &umts_modem_data,
+ },
+};
+
+static struct modem_io_t cdma_io_devices[] = {
+ [0] = {
+ .name = "multipdp",
+ .id = 0x1,
+ .format = IPC_MULTI_RAW,
+ .io_type = IODEV_DUMMY,
+ .link = LINKDEV_DPRAM,
+ },
+ [1] = {
+ .name = "cdma_ipc0",
+ .id = 0x1,
+ .format = IPC_FMT,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_DPRAM,
+ },
+ [2] = {
+ .name = "cdma_boot0",
+ .id = 0x1,
+ .format = IPC_BOOT,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_DPRAM,
+ },
+ [3] = {
+ .name = "cdma_rmnet0",
+ .id = 0x2A,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_DPRAM,
+ },
+ [4] = {
+ .name = "cdma_rmnet1",
+ .id = 0x2B,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_DPRAM,
+ },
+ [5] = {
+ .name = "cdma_rmnet2",
+ .id = 0x2C,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_DPRAM,
+ },
+ [6] = {
+ .name = "cdma_rmnet3",
+ .id = 0x2D,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_DPRAM,
+ },
+ [7] = {
+ .name = "cdma_rmnet4",
+ .id = 0x27,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_DPRAM,
+ },
+ [8] = {
+ .name = "cdma_rmnet5", /* DM Port io-device */
+ .id = 0x3A,
+ .format = IPC_RAW,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_DPRAM,
+ },
+ [9] = {
+ .name = "cdma_ramdump0",
+ .id = 0x1,
+ .format = IPC_RAMDUMP,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_DPRAM,
+ },
+ [10] = {
+ .name = "cdma_rmnet6", /* AT CMD io-device */
+ .id = 0x31,
+ .format = IPC_RAW,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_DPRAM,
+ },
+};
+
+/* cdma target platform data */
+static struct modem_data cdma_modem_data = {
+ .name = "cbp7.1",
+
+ /*ToDo: always power on vbat 3.3v it is not cennected GPIO*/
+ .gpio_cp_on = 0,
+ .gpio_reset_req_n = 0,
+ .gpio_cp_reset = OMAP_GPIO_DPRAM_VIA_RST,
+ .gpio_pda_active = OMAP_GPIO_DPRAM_PDA_ACTIVE,
+ .gpio_phone_active = OMAP_GPIO_DPRAM_PHONE_ACTIVE,
+ .gpio_cp_dump_int = 0, /*ToDo:*/
+ .gpio_cp_warm_reset = 0,
+ .gpio_cp_off = OMAP_GPIO_DPRAM_PWRHOLD_OFF,
+
+ .modem_type = VIA_CBP71,
+ .link_type = LINKDEV_DPRAM,
+ .modem_net = CDMA_NETWORK,
+
+ .num_iodevs = ARRAY_SIZE(cdma_io_devices),
+ .iodevs = cdma_io_devices,
+};
+
+static void dpram_cfg_gpio(void)
+{
+ int nRetry = 0;
+ int resetdone;
+
+ omap_mux_init_signal("gpmc_ad0", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad1", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad2", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad3", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad4", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad5", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad6", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad7", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad8", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad9", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad10", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad11", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad12", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad13", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad14", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+ omap_mux_init_signal("gpmc_ad15", OMAP_PULL_ENA|OMAP_PULL_UP|OMAP_INPUT_EN);
+
+ omap_mux_init_signal("gpmc_nadv_ale", 0);
+ omap_mux_init_signal("gpmc_nwe", 0);
+ omap_mux_init_signal("gpmc_nbe0_cle", 0);
+ omap_mux_init_signal("gpmc_ncs1", 0);
+ omap_mux_init_signal("gpmc_nbe1", 0);
+
+ omap_mux_init_signal("gpmc_wait1.gpio_62", OMAP_WAKEUP_EN | OMAP_INPUT_EN);
+ omap_mux_init_signal("dpm_emu3", OMAP_MUX_MODE3);
+ omap_mux_init_signal("gpmc_ncs3.gpio_53", OMAP_PIN_OUTPUT);
+
+ gpio_request(OMAP_GPIO_DPRAM_INT_N, "dpram_int");
+ gpio_direction_input(OMAP_GPIO_DPRAM_INT_N);
+ irq_set_irq_type(OMAP_GPIO_IRQ(OMAP_GPIO_DPRAM_INT_N),
+ IRQ_TYPE_LEVEL_LOW);
+
+ /*dpram platform init setting*/
+ __raw_writel(0x02, OMAP4_GPMC_IO_ADDRESS((OMAP44XX_GPMC_BASE + 0x10)));
+
+ while (nRetry < 100) {
+ msleep(20);
+ resetdone = __raw_readl(OMAP4_GPMC_IO_ADDRESS(OMAP44XX_GPMC_BASE
+ + 0x14));
+ if (resetdone == 0x1)
+ break;
+
+ nRetry++;
+ }
+
+ __raw_writel(0x10, OMAP4_GPMC_IO_ADDRESS((OMAP44XX_GPMC_BASE + 0x10)));
+
+ __raw_writel((u32)DPRAM_GPMC_CONFIG1,
+ OMAP4_GPMC_IO_ADDRESS(GPMC_CONFIG1_1));
+ __raw_writel((u32)DPRAM_GPMC_CONFIG2,
+ OMAP4_GPMC_IO_ADDRESS(GPMC_CONFIG2_1));
+ __raw_writel((u32)DPRAM_GPMC_CONFIG3,
+ OMAP4_GPMC_IO_ADDRESS(GPMC_CONFIG3_1));
+ __raw_writel((u32)DPRAM_GPMC_CONFIG4,
+ OMAP4_GPMC_IO_ADDRESS(GPMC_CONFIG4_1));
+ __raw_writel((u32)DPRAM_GPMC_CONFIG5,
+ OMAP4_GPMC_IO_ADDRESS(GPMC_CONFIG5_1));
+ __raw_writel((u32)DPRAM_GPMC_CONFIG6,
+ OMAP4_GPMC_IO_ADDRESS(GPMC_CONFIG6_1));
+
+ __raw_writel((u32)0xF04, OMAP4_GPMC_IO_ADDRESS(GPMC_CONFIG7_1));
+ msleep(50);
+ __raw_writel((u32)(0xF04 | 0x040),
+ OMAP4_GPMC_IO_ADDRESS(GPMC_CONFIG7_1));
+}
+
+static int dpram_cfg_gpmc_clk(void)
+{
+ struct clk *dpram_gpmc_ck;
+ struct clk *dpram_gpmc_ick;
+
+ dpram_gpmc_ck = clk_get(NULL, "gpmc_ck");
+ if (IS_ERR(dpram_gpmc_ck)) {
+ pr_err("Could not get GPMC clock gpmc_ck\n");
+ return -ENOENT;
+ }
+ clk_enable(dpram_gpmc_ck);
+
+ dpram_gpmc_ick = clk_get(NULL, "gpmc_ick");
+ if (IS_ERR(dpram_gpmc_ick)) {
+ clk_disable(dpram_gpmc_ck);
+ pr_err("Could not get GPMC clock gpmc_ick\n");
+ return -ENOENT;
+ }
+ clk_enable(dpram_gpmc_ick);
+
+ return 0;
+}
+
+static void cdma_modem_cfg_gpio(void)
+{
+ unsigned gpio_cp_rst = cdma_modem_data.gpio_cp_reset;
+ unsigned gpio_pda_active = cdma_modem_data.gpio_pda_active;
+ unsigned gpio_phone_active = cdma_modem_data.gpio_phone_active;
+ unsigned gpio_cp_off = cdma_modem_data.gpio_cp_off;
+
+ dpram_cfg_gpio();
+ if (dpram_cfg_gpmc_clk()) {
+ pr_err("fail to enable GPMC clock\n");
+ return;
+ }
+
+ omap_mux_init_signal("abe_dmic_din1.gpio_120", OMAP_PIN_INPUT);
+ omap_mux_init_signal("abe_dmic_clk1.gpio_119", OMAP_PIN_OUTPUT |
+ OMAP_PIN_OFF_OUTPUT_LOW);
+
+ /* gpio mux setting */
+ if (gpio_cp_rst) {
+ gpio_request(gpio_cp_rst, "CP_RST");
+ gpio_direction_output(gpio_cp_rst, 0);
+ }
+
+ if (gpio_pda_active) {
+ gpio_request(gpio_pda_active, "PDA_ACTIVE");
+ gpio_direction_output(gpio_pda_active, 0);
+ }
+
+ if (gpio_phone_active) {
+ gpio_request(gpio_phone_active, "PHONE_ACTIVE");
+ gpio_direction_input(gpio_phone_active);
+ }
+
+ if (gpio_cp_off) {
+ gpio_request(gpio_cp_off, "VIA_OFF");
+ gpio_direction_output(gpio_cp_off, 1);
+ }
+
+ if (gpio_phone_active)
+ irq_set_irq_type(
+ OMAP_GPIO_IRQ(OMAP_GPIO_DPRAM_PHONE_ACTIVE),
+ IRQ_TYPE_LEVEL_HIGH);
+}
+
+static struct resource cdma_modem_res[] = {
+ [0] = {
+ .name = "cdma_phone_active",
+ .start = OMAP_GPIO_IRQ(OMAP_GPIO_DPRAM_PHONE_ACTIVE),
+ .end = OMAP_GPIO_IRQ(OMAP_GPIO_DPRAM_PHONE_ACTIVE),
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .name = "cdma_dpram_int",
+ .start = OMAP_GPIO_IRQ(OMAP_GPIO_DPRAM_INT_N),
+ .end = OMAP_GPIO_IRQ(OMAP_GPIO_DPRAM_INT_N),
+ .flags = IORESOURCE_IRQ,
+ },
+ [2] = {
+ .name = "cdma_dpram",
+ .start = DPRAM_START_ADDRESS,
+ .end = DPRAM_END_ADDRESS,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct platform_device cdma_modem = {
+ .name = "modem_if",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(cdma_modem_res),
+ .resource = cdma_modem_res,
+ .dev = {
+ .platform_data = &cdma_modem_data,
+ },
+};
+
+/* lte target platform data */
+static struct modem_io_t lte_io_devices[] = {
+ [0] = {
+ .name = "lte_ipc0",
+ .id = 0x1,
+ .format = IPC_FMT,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_USB,
+ },
+ [1] = {
+ .name = "lte_rmnet0",
+ .id = 0x2A,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_USB,
+ },
+ [2] = {
+ .name = "lte_rfs0",
+ .id = 0x0,
+ .format = IPC_RFS,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_USB,
+ },
+ [3] = {
+ .name = "lte_boot0",
+ .id = 0x0,
+ .format = IPC_BOOT,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_USB,
+ },
+ [4] = {
+ .name = "lte_rmnet1",
+ .id = 0x2B,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_USB,
+ },
+ [5] = {
+ .name = "lte_rmnet2",
+ .id = 0x2C,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_USB,
+ },
+ [6] = {
+ .name = "lte_rmnet3",
+ .id = 0x2D,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_USB,
+ },
+ [7] = {
+ .name = "lte_multipdp",
+ .id = 0x1,
+ .format = IPC_MULTI_RAW,
+ .io_type = IODEV_DUMMY,
+ .link = LINKDEV_USB,
+ },
+ [8] = {
+ .name = "lte_rmnet4", /* DM Port io-device */
+ .id = 0x3F,
+ .format = IPC_RAW,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_USB,
+ },
+ [9] = {
+ .name = "lte_ramdump0",
+ .id = 0x0,
+ .format = IPC_RAMDUMP,
+ .io_type = IODEV_MISC,
+ .link = LINKDEV_USB,
+ },
+};
+
+/*
+Prime vs P4 usage
+CMC2AP_INT1 vs CMC2AP_STATUS
+AP2CMC_INT1 vs AP2CMC_STATUS
+CMC2AP_INT2 vs CMC2AP_WAKEUP
+AP2CMC_INT2 vs AP2CMC_WAKEUP
+*/
+static struct modem_data lte_modem_data = {
+ .name = "cmc221",
+
+ .gpio_cp_on = OMAP_GPIO_221_PMIC_PWRON,
+ .gpio_reset_req_n = 0,
+ .gpio_cp_reset = OMAP_GPIO_CMC_RST,
+ .gpio_pda_active = 0,/*NOT YET CONNECTED*/
+ .gpio_phone_active = OMAP_GPIO_LTE_ACTIVE,
+ .gpio_cp_dump_int = OMAP_GPIO_LTE_ACTIVE,/*TO BE CHECKED*/
+
+ .gpio_cp_warm_reset = 0,
+#ifdef CONFIG_LTE_MODEM_CMC221
+ .gpio_cp_off = OMAP_GPIO_221_PMIC_PWRHOLD_OFF,
+ .gpio_slave_wakeup = OMAP_GPIO_AP2CMC_INT2,
+ .gpio_host_wakeup = OMAP_GPIO_CMC2AP_INT2,
+ .gpio_host_active = OMAP_GPIO_AP2CMC_INT1,
+#endif
+
+ .modem_type = SEC_CMC221,
+ .link_type = LINKDEV_USB,
+ .modem_net = LTE_NETWORK,
+
+ .num_iodevs = ARRAY_SIZE(lte_io_devices),
+ .iodevs = lte_io_devices,
+};
+
+static void omap_lte_mux_init(void)
+{
+ pr_debug("[MODEM_IF] %s IN!\n", __func__);
+
+ omap_mux_init_signal("gpmc_a17.gpio_41", OMAP_PIN_OUTPUT);
+ omap_mux_init_signal("usbb2_ulpitll_dat2.gpio_163", OMAP_PIN_OUTPUT);
+ omap_mux_init_signal("gpmc_ncs0.gpio_50", OMAP_PIN_OUTPUT);
+ omap_mux_init_signal("dpm_emu7.gpio_18", OMAP_PIN_OUTPUT);
+ omap_mux_init_signal("usbb2_ulpitll_nxt.gpio_160",
+ OMAP_PIN_INPUT | OMAP_PIN_OFF_WAKEUPENABLE);
+ omap_mux_init_signal("dpm_emu17.gpio_28", OMAP_PIN_OUTPUT);
+ omap_mux_init_signal("gpmc_a23.gpio_47", OMAP_PIN_INPUT_PULLDOWN);
+}
+
+static void lte_modem_cfg_gpio(void)
+{
+ unsigned gpio_cp_on = lte_modem_data.gpio_cp_on;
+ unsigned gpio_cp_rst = lte_modem_data.gpio_cp_reset;
+ unsigned gpio_phone_active = lte_modem_data.gpio_phone_active;
+#ifdef CONFIG_LTE_MODEM_CMC221
+ unsigned gpio_cp_off = lte_modem_data.gpio_cp_off;
+ unsigned gpio_slave_wakeup = lte_modem_data.gpio_slave_wakeup;
+ unsigned gpio_host_wakeup = lte_modem_data.gpio_host_wakeup;
+ unsigned gpio_host_active = lte_modem_data.gpio_host_active;
+#endif
+
+ omap_lte_mux_init();
+ if (gpio_cp_on) {
+ gpio_request(gpio_cp_on, "LTE_ON");
+ gpio_direction_output(gpio_cp_on, 0);
+ }
+
+ if (gpio_cp_rst) {
+ gpio_request(gpio_cp_rst, "LTE_RST");
+ gpio_direction_output(gpio_cp_rst, 0);
+ }
+
+ if (gpio_phone_active) {
+ gpio_request(gpio_phone_active, "LTE_ACTIVE");
+ gpio_direction_input(gpio_phone_active);
+ }
+
+#ifdef CONFIG_LTE_MODEM_CMC221
+ if (gpio_cp_off) {
+ gpio_request(gpio_cp_off, "LTE_OFF");
+ gpio_direction_output(gpio_cp_off, 1);
+ }
+
+ if (gpio_slave_wakeup) {
+ gpio_request(gpio_slave_wakeup, "LTE_SLAVE_WAKEUP");
+ gpio_direction_output(gpio_slave_wakeup, 0);
+ }
+
+ if (gpio_host_wakeup) {
+ gpio_request(gpio_host_wakeup, "LTE_HOST_WAKEUP");
+ gpio_direction_input(gpio_host_wakeup);
+ }
+
+ if (gpio_host_active) {
+ gpio_request(gpio_host_active, "LTE_HOST_ACTIVE");
+ gpio_direction_output(gpio_host_active, 1);
+ }
+#endif
+}
+
+static struct resource lte_modem_res[] = {
+ [0] = {
+ .name = "lte_phone_active",
+ /* phone active irq */
+ .start = OMAP_GPIO_IRQ(OMAP_GPIO_LTE_ACTIVE),
+ .end = OMAP_GPIO_IRQ(OMAP_GPIO_LTE_ACTIVE),
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .name = "lte_host_wakeup",
+ /* host wakeup irq */
+ .start = OMAP_GPIO_IRQ(OMAP_GPIO_CMC2AP_INT2),
+ .end = OMAP_GPIO_IRQ(OMAP_GPIO_CMC2AP_INT2),
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device lte_modem_wake = {
+ .name = "modem_lte_wake",
+ .id = -1,
+};
+
+static struct platform_device lte_modem = {
+ .name = "modem_if",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(lte_modem_res),
+ .resource = lte_modem_res,
+ .dev = {
+ .platform_data = &lte_modem_data,
+ },
+};
+
+/* lte_modem_wake must be registered before the ehci driver */
+void __init modem_toro_init(void)
+{
+ lte_modem_wake.dev.platform_data = &lte_modem_data;
+ platform_device_register(&lte_modem_wake);
+}
+
+static int __init init_modem(void)
+{
+ pr_debug("[MODEM_IF] init_modem\n");
+
+ switch (omap4_tuna_get_type()) {
+ case TUNA_TYPE_MAGURO: /* HSPA */
+ /* umts gpios configuration */
+ umts_modem_cfg_gpio();
+ platform_device_register(&umts_modem);
+ break;
+
+ case TUNA_TYPE_TORO: /* LTE */
+ /* cdma gpios configuration */
+ cdma_modem_cfg_gpio();
+ platform_device_register(&cdma_modem);
+
+ /* lte gpios configuration */
+ lte_modem_cfg_gpio();
+ platform_device_register(&lte_modem);
+ break;
+
+ default:
+ break;
+ }
+ return 0;
+}
+late_initcall(init_modem);
diff --git a/arch/arm/mach-omap2/board-tuna-nfc.c b/arch/arm/mach-omap2/board-tuna-nfc.c
new file mode 100644
index 0000000..032ceae
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-nfc.c
@@ -0,0 +1,148 @@
+/* Control power to pn544
+ *
+ * Copyright (C) 2011 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/types.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/printk.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/wakelock.h>
+#include <plat/serial.h>
+
+#include "mux.h"
+
+#define GPIO_NFC_EN 173
+#define GPIO_NFC_FW 172
+#define GPIO_NFC_IRQ 17
+
+#define PWR_OFF 0
+#define PWR_ON 1
+#define PWR_ON_FW 2
+
+#define NFC_UART_NUM 4 /* omap_uart_wake() counts from 1 */
+
+static unsigned int nfc_power;
+static struct wake_lock nfc_wake_lock;
+
+static void nfc_power_apply(void) {
+ switch (nfc_power) {
+ case PWR_OFF:
+ pr_info("%s OFF\n", __func__);
+ gpio_set_value(GPIO_NFC_FW, 0);
+ gpio_set_value(GPIO_NFC_EN, 0);
+ msleep(60);
+ break;
+ case PWR_ON:
+ pr_info("%s ON\n", __func__);
+ gpio_set_value(GPIO_NFC_FW, 0);
+ gpio_set_value(GPIO_NFC_EN, 1);
+ msleep(20);
+ break;
+ case PWR_ON_FW:
+ pr_info("%s ON (firmware download)\n", __func__);
+ gpio_set_value(GPIO_NFC_FW, 1);
+ gpio_set_value(GPIO_NFC_EN, 1);
+ msleep(20);
+ gpio_set_value(GPIO_NFC_EN, 0); /* fw mode requires reset */
+ msleep(60);
+ gpio_set_value(GPIO_NFC_EN, 1);
+ msleep(20);
+ break;
+ }
+}
+
+static ssize_t nfc_power_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", nfc_power);
+}
+
+static ssize_t nfc_power_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ int rc;
+ unsigned int val;
+
+ rc = kstrtouint(buf, 0, &val);
+ if (rc < 0)
+ return rc;
+ if (val > PWR_ON_FW)
+ return -EINVAL;
+ nfc_power = val;
+ nfc_power_apply();
+ return count;
+}
+
+static DEVICE_ATTR(nfc_power, S_IWUSR | S_IRUGO, nfc_power_show,
+ nfc_power_store);
+
+static irqreturn_t nfc_irq_isr(int irq, void *dev)
+{
+ omap_uart_wake(NFC_UART_NUM);
+
+ /*
+ * take a 500ms wakelock, to give time for higher layers
+ * to either take their own wakelock or finish processing
+ */
+ wake_lock_timeout(&nfc_wake_lock, msecs_to_jiffies(500));
+
+ return IRQ_HANDLED;
+}
+
+void __init omap4_tuna_nfc_init(void)
+{
+ struct platform_device *pdev;
+ int irq;
+
+ gpio_request(GPIO_NFC_FW, "nfc_fw");
+ gpio_direction_output(GPIO_NFC_FW, 0);
+ omap_mux_init_gpio(GPIO_NFC_FW, OMAP_PIN_OUTPUT);
+
+ gpio_request(GPIO_NFC_EN, "nfc_en");
+ gpio_direction_output(GPIO_NFC_EN, 0);
+ omap_mux_init_gpio(GPIO_NFC_EN, OMAP_PIN_OUTPUT);
+
+ gpio_request(GPIO_NFC_IRQ, "nfc_irq");
+ gpio_direction_input(GPIO_NFC_IRQ);
+ omap_mux_init_gpio(GPIO_NFC_IRQ, OMAP_PIN_INPUT_PULLUP |
+ OMAP_PIN_OFF_WAKEUPENABLE);
+
+ wake_lock_init(&nfc_wake_lock, WAKE_LOCK_SUSPEND, "nfc");
+
+ irq = gpio_to_irq(GPIO_NFC_IRQ);
+ if (request_irq(irq, nfc_irq_isr, IRQF_TRIGGER_RISING, "nfc_irq",
+ NULL)) {
+ pr_err("%s: request_irq() failed\n", __func__);
+ return;
+ }
+
+ if (enable_irq_wake(irq)) {
+ pr_err("%s: irq_set_irq_wake() failed\n", __func__);
+ return;
+ }
+
+ nfc_power = PWR_OFF;
+
+ pdev = platform_device_register_simple("nfc-power", -1, NULL, 0);
+ if (IS_ERR(pdev)) {
+ pr_err("%s: platform_device_register_simple() failed\n", __func__);
+ return;
+ }
+ if (device_create_file(&pdev->dev, &dev_attr_nfc_power))
+ pr_err("%s: device_create_file() failed\n", __func__);
+}
diff --git a/arch/arm/mach-omap2/board-tuna-pogo.c b/arch/arm/mach-omap2/board-tuna-pogo.c
new file mode 100644
index 0000000..e9021f0
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-pogo.c
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2011 Samsung, Inc.
+ *
+ * Author: Adam Hampson <ahampson@sta.samsung.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.
+ *
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/switch.h>
+#include <linux/wakelock.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/otg_id.h>
+
+#include <asm/div64.h>
+
+#include "mux.h"
+#include "control.h"
+#include "board-tuna.h"
+
+#define GPIO_POGO_DATA 121
+#define GPIO_POGO_DET 169
+
+/* The below constants are in milliseconds */
+#define POGO_WAKE_PERIOD 100
+#define POGO_ID_PERIOD_TIMEOUT 750
+#define POGO_ID_DESKDOCK 50
+#define POGO_ID_CARDOCK 100
+#define POGO_ID_CHARGER 50
+#define POGO_ID_USB 100
+#define POGO_ID_AUDIO 50
+#define POGO_ID_NO_AUDIO 100
+#define POGO_ENTER_SPDIF_WAIT_PERIOD 100
+#define POGO_ID_PERIOD_TOLERANCE 20
+#define POGO_DET_DEBOUNCE 80
+
+#define POGO_DOCK_ID_MAX_RETRY 10
+
+#define POGO_AUDIO_DISCONNECTED 0
+#define POGO_AUDIO_CONNECTED 2
+
+enum {
+ POGO_DOCK_DOCKED = BIT(0),
+ POGO_DOCK_DESK = BIT(1), /* 0 = Car */
+ POGO_DOCK_CHARGER = BIT(2),
+ POGO_DOCK_AUDIO_CONN = BIT(3),
+};
+
+enum debounce_state {
+ POGO_DET_DOCKED, /* interrupt enabled, timer stopped */
+ POGO_DET_UNSTABLE, /* interrupt disabled, timer running */
+ POGO_DET_WAIT_STABLE, /* interrupt enabled, timer running */
+ POGO_DET_UNDOCKED, /* interrupt disabled, timer stopped */
+};
+
+struct tuna_pogo {
+ struct otg_id_notifier_block otg_id_nb;
+ struct switch_dev audio_switch;
+ struct wake_lock wake_lock;
+ struct completion completion;
+ struct timespec timestamps[4];
+ int ts_index;
+ int det_irq;
+ int data_irq;
+ unsigned int dock_type;
+ struct mutex mutex;
+ struct timer_list det_timer;
+ struct work_struct det_work;
+ spinlock_t det_irq_lock;
+ enum debounce_state debounce_state;
+};
+static struct tuna_pogo tuna_pogo;
+
+static void pogo_send_pulse(unsigned int duration_in_ms)
+{
+ gpio_direction_output(GPIO_POGO_DATA, 1);
+ msleep(duration_in_ms);
+ gpio_direction_output(GPIO_POGO_DATA, 0);
+}
+
+static int pogo_read_id_period(struct tuna_pogo *pogo,
+ unsigned int timeout_in_ms)
+{
+ int ret;
+
+ memset(pogo->timestamps, 0, sizeof(pogo->timestamps));
+ pogo->ts_index = 0;
+
+ gpio_direction_input(GPIO_POGO_DATA);
+
+ irq_set_irq_type(pogo->data_irq, IRQF_TRIGGER_HIGH);
+
+ enable_irq(pogo->data_irq);
+
+ ret = wait_for_completion_timeout(&pogo->completion,
+ msecs_to_jiffies(timeout_in_ms));
+ if (ret <= 0) {
+ if (pogo->ts_index != ARRAY_SIZE(pogo->timestamps))
+ pr_debug("No response to wake within timeout\n");
+ else
+ pr_debug("ID period did not conclude within timeout\n");
+ disable_irq(pogo->data_irq);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void pogo_dock_change(struct tuna_pogo *pogo)
+{
+ char *dock_type_str;
+ char *dock_audio_str;
+ char *dock_charger_str;
+
+ if (!(pogo->dock_type & POGO_DOCK_DOCKED)) {
+ tuna_otg_set_dock_switch(0);
+ switch_set_state(&pogo->audio_switch, POGO_AUDIO_DISCONNECTED);
+ tuna_otg_pogo_charger(POGO_POWER_DISCONNECTED);
+ pr_info("Undocked\n");
+ return;
+ } else {
+ tuna_otg_set_dock_switch(pogo->dock_type & POGO_DOCK_DESK ? 1 : 2);
+ }
+
+ dock_type_str = pogo->dock_type & POGO_DOCK_DESK ? "Desk" : "Car";
+ if (pogo->dock_type & POGO_DOCK_AUDIO_CONN) {
+ switch_set_state(&pogo->audio_switch, POGO_AUDIO_CONNECTED);
+ dock_audio_str = "";
+ } else {
+ switch_set_state(&pogo->audio_switch, POGO_AUDIO_DISCONNECTED);
+ dock_audio_str = " no";
+ }
+ if (pogo->dock_type & POGO_DOCK_CHARGER) {
+ tuna_otg_pogo_charger(POGO_POWER_CHARGER);
+ dock_charger_str = "charger";
+ } else {
+ tuna_otg_pogo_charger(POGO_POWER_HOST);
+ dock_charger_str = "host";
+ }
+
+ pr_info("%s dock,%s audio, USB %s\n", dock_type_str, dock_audio_str,
+ dock_charger_str);
+}
+
+static int pogo_detect_callback(struct otg_id_notifier_block *nb)
+{
+ struct tuna_pogo *pogo = container_of(nb, struct tuna_pogo, otg_id_nb);
+ int dock_type_period;
+ int power_type_period;
+ int audio_cable_period;
+ struct timespec temp;
+ unsigned int retry = 0;
+ unsigned long irqflags;
+
+ if (gpio_get_value(GPIO_POGO_DET)) {
+ wake_lock(&pogo->wake_lock);
+
+ while (!(pogo->dock_type & POGO_DOCK_DOCKED)) {
+
+ if (!gpio_get_value(GPIO_POGO_DET)) {
+ wake_unlock(&pogo->wake_lock);
+ return OTG_ID_UNHANDLED;
+ }
+
+ if (retry++ > POGO_DOCK_ID_MAX_RETRY) {
+ wake_unlock(&pogo->wake_lock);
+ pr_err("Unable to identify pogo dock\n");
+ return OTG_ID_UNHANDLED;
+ }
+
+ /* Start the detection process by sending a wake pulse
+ * to the dock.
+ */
+ pogo_send_pulse(POGO_WAKE_PERIOD);
+
+ if (pogo_read_id_period(pogo, POGO_ID_PERIOD_TIMEOUT))
+ continue;
+
+ temp = timespec_sub(pogo->timestamps[1],
+ pogo->timestamps[0]);
+ dock_type_period = temp.tv_nsec / NSEC_PER_MSEC;
+
+ temp = timespec_sub(pogo->timestamps[2],
+ pogo->timestamps[1]);
+ power_type_period = temp.tv_nsec / NSEC_PER_MSEC;
+
+ temp = timespec_sub(pogo->timestamps[3],
+ pogo->timestamps[2]);
+ audio_cable_period = temp.tv_nsec / NSEC_PER_MSEC;
+
+ /* The length of the ID period will indicate the type of
+ * dock that is attached.
+ */
+ if (abs(dock_type_period - POGO_ID_DESKDOCK) <=
+ POGO_ID_PERIOD_TOLERANCE) {
+ pogo->dock_type |= POGO_DOCK_DESK;
+ } else if (abs(dock_type_period - POGO_ID_CARDOCK) >
+ POGO_ID_PERIOD_TOLERANCE) {
+ continue;
+ }
+
+ if (abs(power_type_period - POGO_ID_CHARGER) <=
+ POGO_ID_PERIOD_TOLERANCE) {
+ pogo->dock_type |= POGO_DOCK_CHARGER;
+ } else if (abs(power_type_period - POGO_ID_USB) >
+ POGO_ID_PERIOD_TOLERANCE) {
+ continue;
+ }
+
+ if (abs(audio_cable_period - POGO_ID_AUDIO) <=
+ POGO_ID_PERIOD_TOLERANCE) {
+ pogo->dock_type |= POGO_DOCK_AUDIO_CONN;
+ pogo->dock_type |= POGO_DOCK_DOCKED;
+ } else if (abs(audio_cable_period -
+ POGO_ID_NO_AUDIO) <=
+ POGO_ID_PERIOD_TOLERANCE) {
+ pogo->dock_type |= POGO_DOCK_DOCKED;
+ }
+ }
+
+ msleep(POGO_ENTER_SPDIF_WAIT_PERIOD);
+
+ mutex_lock(&pogo->mutex);
+ omap_mux_set_gpio(OMAP_MUX_MODE2 | OMAP_PIN_OUTPUT,
+ GPIO_POGO_DATA);
+
+ pogo_dock_change(pogo);
+ wake_lock_timeout(&pogo->wake_lock, msecs_to_jiffies(1000));
+
+ spin_lock_irqsave(&pogo->det_irq_lock, irqflags);
+ pogo->debounce_state = POGO_DET_DOCKED;
+ enable_irq(pogo->det_irq);
+ enable_irq_wake(pogo->det_irq);
+ spin_unlock_irqrestore(&pogo->det_irq_lock, irqflags);
+ mutex_unlock(&pogo->mutex);
+
+ return OTG_ID_HANDLED;
+ }
+
+ return OTG_ID_UNHANDLED;
+}
+
+static void pogo_dock_undock(struct tuna_pogo *pogo)
+{
+ mutex_lock(&pogo->mutex);
+ if ((pogo->dock_type & POGO_DOCK_DOCKED) &&
+ pogo->debounce_state == POGO_DET_UNDOCKED) {
+ pogo->dock_type = 0;
+ pogo_dock_change(pogo);
+
+ omap_mux_set_gpio(OMAP_MUX_MODE3 | OMAP_PIN_INPUT_PULLDOWN,
+ GPIO_POGO_DATA);
+ }
+ mutex_unlock(&pogo->mutex);
+}
+
+/* This callback is used to cancel any ownership of the chain */
+static void pogo_cancel_callback(struct otg_id_notifier_block *nb)
+{
+ struct tuna_pogo *pogo = container_of(nb, struct tuna_pogo, otg_id_nb);
+ unsigned long irqflags;
+
+ /* Disable the POGO_DET IRQ and cancel any pending timer if needed */
+ spin_lock_irqsave(&pogo->det_irq_lock, irqflags);
+ switch (pogo->debounce_state) {
+ case POGO_DET_UNDOCKED:
+ break;
+ case POGO_DET_UNSTABLE:
+ del_timer(&pogo->det_timer);
+ break;
+ case POGO_DET_WAIT_STABLE:
+ del_timer(&pogo->det_timer);
+ /* fall through */
+ case POGO_DET_DOCKED:
+ disable_irq_wake(pogo->det_irq);
+ disable_irq_nosync(pogo->det_irq);
+ break;
+ }
+ pogo->debounce_state = POGO_DET_UNDOCKED;
+ spin_unlock_irqrestore(&pogo->det_irq_lock, irqflags);
+
+ /* Change the state to undocked */
+ pogo_dock_undock(pogo);
+}
+
+static void det_work_func(struct work_struct *work)
+{
+ struct tuna_pogo *pogo = container_of(work, struct tuna_pogo, det_work);
+
+ pogo_dock_undock(pogo);
+
+ /* Notify the otg_id chain that a change has occurred */
+ otg_id_notify();
+}
+
+static void pogo_det_timer_func(unsigned long arg)
+{
+ struct tuna_pogo *pogo = (struct tuna_pogo *)arg;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&pogo->det_irq_lock, irqflags);
+ switch (pogo->debounce_state) {
+ case POGO_DET_DOCKED:
+ break;
+ case POGO_DET_UNSTABLE:
+ /*
+ * The detect gpio changed in one the previous two timeslots,
+ * so enable the irq, reset the timer, and wait again. If the
+ * detect gpio changed after we last disabled the interrupt we
+ * will get anther interrupt right away and the state will go
+ * back to POGO_DET_UNSTABLE.
+ */
+ pogo->debounce_state = POGO_DET_WAIT_STABLE;
+ enable_irq(pogo->det_irq);
+ enable_irq_wake(pogo->det_irq);
+ mod_timer(&pogo->det_timer,
+ jiffies + msecs_to_jiffies(POGO_DET_DEBOUNCE));
+ break;
+ case POGO_DET_WAIT_STABLE:
+ if (gpio_get_value(GPIO_POGO_DET) == 0) {
+ pogo->debounce_state = POGO_DET_UNDOCKED;
+ disable_irq_wake(pogo->det_irq);
+ disable_irq_nosync(pogo->det_irq);
+ wake_lock_timeout(&pogo->wake_lock,
+ msecs_to_jiffies(1000));
+ schedule_work(&pogo->det_work);
+ } else {
+ /* The device appears to be back in the dock */
+ pogo->debounce_state = POGO_DET_DOCKED;
+ wake_unlock(&pogo->wake_lock);
+ }
+ break;
+ case POGO_DET_UNDOCKED:
+ break;
+ }
+ spin_unlock_irqrestore(&pogo->det_irq_lock, irqflags);
+}
+
+static irqreturn_t pogo_det_irq(int irq, void *data)
+{
+ struct tuna_pogo *pogo = data;
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&pogo->det_irq_lock, irqflags);
+ switch (pogo->debounce_state) {
+ case POGO_DET_DOCKED:
+ wake_lock(&pogo->wake_lock);
+ mod_timer(&pogo->det_timer,
+ jiffies + msecs_to_jiffies(POGO_DET_DEBOUNCE));
+ /* fall through */
+ case POGO_DET_WAIT_STABLE:
+ /*
+ * Disable IRQ line in case there is noise. It will be
+ * re-enabled when the timer expires
+ */
+ pogo->debounce_state = POGO_DET_UNSTABLE;
+ disable_irq_wake(pogo->det_irq);
+ disable_irq_nosync(pogo->det_irq);
+ break;
+ case POGO_DET_UNSTABLE:
+ case POGO_DET_UNDOCKED:
+ break;
+ }
+ spin_unlock_irqrestore(&pogo->det_irq_lock, irqflags);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t pogo_data_irq(int irq, void *data)
+{
+ struct tuna_pogo *pogo = data;
+
+ ktime_get_ts(&pogo->timestamps[pogo->ts_index++]);
+ irq_set_irq_type(pogo->data_irq, gpio_get_value(GPIO_POGO_DATA) ?
+ IRQF_TRIGGER_LOW :
+ IRQF_TRIGGER_HIGH);
+ if (pogo->ts_index >= ARRAY_SIZE(pogo->timestamps)) {
+ complete(&pogo->completion);
+ disable_irq_nosync(pogo->data_irq);
+ }
+ return IRQ_HANDLED;
+}
+
+void __init omap4_tuna_pogo_init(void)
+{
+ struct tuna_pogo *pogo = &tuna_pogo;
+ unsigned int r;
+ int ret;
+
+ omap_mux_init_signal("usbb2_hsic_data.gpio_169",
+ OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE3 |
+ OMAP_WAKEUP_EN);
+
+ /* The pullup/pulldown controls in the mux register are not the controls
+ * that you are looking for. The usbb2_hsic_data signal has a separate
+ * special control in the CONTROL_USBB_HSIC register.
+ */
+ r = omap4_ctrl_pad_readl(OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_USBB_HSIC);
+ r &= ~OMAP4_USBB2_HSIC_DATA_WD_MASK;
+ omap4_ctrl_pad_writel(r, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_USBB_HSIC);
+
+ ret = gpio_request(GPIO_POGO_DET, "pogo_det");
+ if (ret < 0)
+ pr_err("request for pogo_det gpio failed, err %d\n", ret);
+
+ gpio_direction_input(GPIO_POGO_DET);
+
+ omap_mux_init_signal("abe_dmic_din2.gpio_121",
+ OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE3);
+ ret = gpio_request(GPIO_POGO_DATA, "pogo_data");
+ if (ret < 0)
+ pr_err("request for pogo_data gpio failed, err %d\n", ret);
+
+ gpio_direction_output(GPIO_POGO_DATA, 0);
+
+ /* The POGO dock does not involve USB but we are reusing the existing
+ * usb audio switch report the availabilty of SPDIF audio through the
+ * POGO dock.
+ */
+ pogo->audio_switch.name = "usb_audio";
+
+ switch_dev_register(&pogo->audio_switch);
+
+ wake_lock_init(&pogo->wake_lock, WAKE_LOCK_SUSPEND, "pogo");
+
+ init_completion(&pogo->completion);
+
+ pogo->otg_id_nb.detect = pogo_detect_callback;
+ pogo->otg_id_nb.proxy_wait = NULL;
+ pogo->otg_id_nb.cancel = pogo_cancel_callback;
+ pogo->otg_id_nb.priority = TUNA_OTG_ID_POGO_PRIO;
+
+ ret = otg_id_register_notifier(&pogo->otg_id_nb);
+ if (ret < 0)
+ pr_err("Unable to register notifier\n");
+
+ setup_timer(&pogo->det_timer, pogo_det_timer_func, (unsigned long)pogo);
+ spin_lock_init(&pogo->det_irq_lock);
+ mutex_init(&pogo->mutex);
+ INIT_WORK(&pogo->det_work, det_work_func);
+ pogo->debounce_state = POGO_DET_UNDOCKED;
+
+ pogo->det_irq = gpio_to_irq(GPIO_POGO_DET);
+
+ ret = request_irq(pogo->det_irq, pogo_det_irq,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ "pogo_det", pogo);
+ if (ret < 0)
+ pr_err("Unable to register pogo_det interrupt (%d)\n", ret);
+
+ disable_irq(pogo->det_irq);
+
+ pogo->data_irq = gpio_to_irq(GPIO_POGO_DATA);
+
+ ret = request_irq(pogo->data_irq, pogo_data_irq,
+ IRQF_TRIGGER_HIGH,
+ "pogo_data", pogo);
+ if (ret < 0)
+ pr_err("Unable to register pogo_data interrupt (%d)\n", ret);
+
+ disable_irq(pogo->data_irq);
+}
diff --git a/arch/arm/mach-omap2/board-tuna-power.c b/arch/arm/mach-omap2/board-tuna-power.c
new file mode 100644
index 0000000..53a6338
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-power.c
@@ -0,0 +1,533 @@
+/* Power support for Samsung Tuna Board.
+ *
+ * Copyright (C) 2011 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.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/max17040_battery.h>
+#include <linux/moduleparam.h>
+#include <linux/pda_power.h>
+#include <linux/platform_device.h>
+#include <linux/i2c/twl6030-madc.h>
+#include <linux/delay.h>
+
+#include <plat/cpu.h>
+#include <plat/omap-pm.h>
+
+#include "board-tuna.h"
+#include "mux.h"
+#include "pm.h"
+
+/* These will be different on pre-lunchbox, lunchbox, and final */
+#define GPIO_CHARGING_N 83
+#define GPIO_TA_NCONNECTED 142
+#define GPIO_CHARGE_N 13
+#define GPIO_CHG_CUR_ADJ 102
+#define GPIO_FUEL_ALERT 44
+
+#define TPS62361_GPIO 7
+#define ADC_NUM_SAMPLES 5
+#define ADC_LIMIT_ERR_COUNT 5
+#define ISET_ADC_CHANNEL 3
+#define TEMP_ADC_CHANNEL 1
+
+#define CHARGE_FULL_ADC 150
+
+#define HIGH_BLOCK_TEMP_MAGURO 500
+#define HIGH_RECOVER_TEMP_MAGURO 420
+#define LOW_BLOCK_TEMP_MAGURO (-50)
+#define LOW_RECOVER_TEMP_MAGURO 0
+
+#define HIGH_BLOCK_TEMP_TORO 490
+#define HIGH_RECOVER_TEMP_TORO 420
+#define LOW_BLOCK_TEMP_TORO (-50)
+#define LOW_RECOVER_TEMP_TORO 0
+
+/**
+** temp_adc_table_data
+** @adc_value : thermistor adc value
+** @temperature : temperature(C) * 10
+**/
+struct temp_adc_table_data {
+ int adc_value;
+ int temperature;
+};
+
+static DEFINE_SPINLOCK(charge_en_lock);
+static int charger_state;
+static bool is_charging_mode;
+
+static struct temp_adc_table_data temper_table_maguro[] = {
+ /* ADC, Temperature (C/10) */
+ { 75, 700 },
+ { 78, 690 },
+ { 82, 680 },
+ { 84, 670 },
+ { 87, 660 },
+ { 89, 650 },
+ { 92, 640 },
+ { 95, 630 },
+ { 99, 620 },
+ { 102, 610 },
+ { 105, 600 },
+ { 109, 590 },
+ { 113, 580 },
+ { 117, 570 },
+ { 121, 560 },
+ { 124, 550 },
+ { 127, 540 },
+ { 135, 530 },
+ { 139, 520 },
+ { 143, 510 },
+ { 147, 500 },
+ { 153, 490 },
+ { 158, 480 },
+ { 163, 470 },
+ { 169, 460 },
+ { 175, 450 },
+ { 181, 440 },
+ { 187, 430 },
+ { 193, 420 },
+ { 199, 410 },
+ { 205, 400 },
+ { 212, 390 },
+ { 218, 380 },
+ { 227, 370 },
+ { 233, 360 },
+ { 240, 350 },
+ { 249, 340 },
+ { 258, 330 },
+ { 267, 320 },
+ { 276, 310 },
+ { 285, 300 },
+ { 299, 290 },
+ { 308, 280 },
+ { 313, 270 },
+ { 322, 260 },
+ { 331, 250 },
+ { 342, 240 },
+ { 355, 230 },
+ { 363, 220 },
+ { 373, 210 },
+ { 383, 200 },
+ { 394, 190 },
+ { 407, 180 },
+ { 417, 170 },
+ { 427, 160 },
+ { 437, 150 },
+ { 450, 140 },
+ { 465, 130 },
+ { 475, 120 },
+ { 487, 110 },
+ { 500, 100 },
+ { 514, 90 },
+ { 526, 80 },
+ { 540, 70 },
+ { 552, 60 },
+ { 565, 50 },
+ { 577, 40 },
+ { 589, 30 },
+ { 603, 20 },
+ { 614, 10 },
+ { 628, 0 },
+ { 639, (-10) },
+ { 664, (-20) },
+ { 689, (-30) },
+ { 717, (-40) },
+ { 744, (-50) },
+ { 754, (-60) },
+ { 765, (-70) },
+ { 776, (-80) },
+ { 787, (-90) },
+ { 798, (-100) },
+};
+
+static struct temp_adc_table_data temper_table_toro[] = {
+ /* ADC, Temperature (C/10) */
+ { 70, 700 },
+ { 72, 690 },
+ { 75, 680 },
+ { 77, 670 },
+ { 80, 660 },
+ { 82, 650 },
+ { 85, 640 },
+ { 88, 630 },
+ { 91, 620 },
+ { 94, 610 },
+ { 97, 600 },
+ { 101, 590 },
+ { 104, 580 },
+ { 108, 570 },
+ { 111, 560 },
+ { 115, 550 },
+ { 119, 540 },
+ { 124, 530 },
+ { 129, 520 },
+ { 133, 510 },
+ { 137, 500 },
+ { 141, 490 },
+ { 146, 480 },
+ { 150, 470 },
+ { 155, 460 },
+ { 160, 450 },
+ { 166, 440 },
+ { 171, 430 },
+ { 177, 420 },
+ { 184, 410 },
+ { 190, 400 },
+ { 196, 390 },
+ { 203, 380 },
+ { 212, 370 },
+ { 219, 360 },
+ { 226, 350 },
+ { 234, 340 },
+ { 242, 330 },
+ { 250, 320 },
+ { 258, 310 },
+ { 266, 300 },
+ { 275, 290 },
+ { 284, 280 },
+ { 294, 270 },
+ { 303, 260 },
+ { 312, 250 },
+ { 322, 240 },
+ { 333, 230 },
+ { 344, 220 },
+ { 354, 210 },
+ { 364, 200 },
+ { 375, 190 },
+ { 387, 180 },
+ { 399, 170 },
+ { 410, 160 },
+ { 422, 150 },
+ { 431, 140 },
+ { 443, 130 },
+ { 456, 120 },
+ { 468, 110 },
+ { 480, 100 },
+ { 493, 90 },
+ { 506, 80 },
+ { 519, 70 },
+ { 532, 60 },
+ { 545, 50 },
+ { 558, 40 },
+ { 571, 30 },
+ { 582, 20 },
+ { 595, 10 },
+ { 608, 0 },
+ { 620, (-10) },
+ { 632, (-20) },
+ { 645, (-30) },
+ { 658, (-40) },
+ { 670, (-50) },
+ { 681, (-60) },
+ { 696, (-70) },
+ { 708, (-80) },
+ { 720, (-90) },
+ { 732, (-100) },
+};
+
+static struct temp_adc_table_data *temper_table = temper_table_maguro;
+static int temper_table_size = ARRAY_SIZE(temper_table_maguro);
+
+static bool enable_sr = true;
+module_param(enable_sr, bool, S_IRUSR | S_IRGRP | S_IROTH);
+
+static struct gpio charger_gpios[] = {
+ { .gpio = GPIO_CHARGING_N, .flags = GPIOF_IN, .label = "charging_n" },
+ { .gpio = GPIO_TA_NCONNECTED, .flags = GPIOF_IN, .label = "charger_n" },
+ { .gpio = GPIO_CHARGE_N, .flags = GPIOF_OUT_INIT_HIGH, .label = "charge_n" },
+ { .gpio = GPIO_CHG_CUR_ADJ, .flags = GPIOF_OUT_INIT_LOW, .label = "charge_cur_adj" },
+};
+
+static int twl6030_get_adc_data(int ch)
+{
+ int adc_data;
+ int adc_max = -1;
+ int adc_min = 1 << 11;
+ int adc_total = 0;
+ int i, j;
+
+ for (i = 0; i < ADC_NUM_SAMPLES; i++) {
+ adc_data = twl6030_get_madc_conversion(ch);
+ if (adc_data == -EAGAIN) {
+ for (j = 0; j < ADC_LIMIT_ERR_COUNT; j++) {
+ msleep(20);
+ adc_data = twl6030_get_madc_conversion(ch);
+ if (adc_data > 0)
+ break;
+ }
+ if (j >= ADC_LIMIT_ERR_COUNT) {
+ pr_err("%s: Retry count exceeded[ch:%d]\n",
+ __func__, ch);
+ return adc_data;
+ }
+ } else if (adc_data < 0) {
+ pr_err("%s: Failed read adc value : %d [ch:%d]\n",
+ __func__, adc_data, ch);
+ return adc_data;
+ }
+
+ if (adc_data > adc_max)
+ adc_max = adc_data;
+ if (adc_data < adc_min)
+ adc_min = adc_data;
+
+ adc_total += adc_data;
+ }
+ return (adc_total - adc_max - adc_min) / (ADC_NUM_SAMPLES - 2);
+}
+
+static int iset_adc_value(void)
+{
+ return twl6030_get_adc_data(ISET_ADC_CHANNEL);
+}
+
+static int temp_adc_value(void)
+{
+ return twl6030_get_adc_data(TEMP_ADC_CHANNEL);
+}
+
+static bool check_charge_full(void)
+{
+ int ret;
+
+ ret = iset_adc_value();
+ if (ret < 0) {
+ pr_err("%s: invalid iset adc value [%d]\n",
+ __func__, ret);
+ return false;
+ }
+ pr_debug("%s : iset adc value : %d\n", __func__, ret);
+
+ return ret < CHARGE_FULL_ADC;
+}
+
+static int get_bat_temp_by_adc(int *batt_temp)
+{
+ int array_size = temper_table_size;
+ int temp_adc = temp_adc_value();
+ int mid;
+ int left_side = 0;
+ int right_side = array_size - 1;
+ int temp = 0;
+
+ if (temp_adc < 0) {
+ pr_err("%s : Invalid temperature adc value [%d]\n",
+ __func__, temp_adc);
+ return temp_adc;
+ }
+
+ while (left_side <= right_side) {
+ mid = (left_side + right_side) / 2;
+ if (mid == 0 || mid == array_size - 1 ||
+ (temper_table[mid].adc_value <= temp_adc &&
+ temper_table[mid+1].adc_value > temp_adc)) {
+ temp = temper_table[mid].temperature;
+ break;
+ } else if (temp_adc - temper_table[mid].adc_value > 0) {
+ left_side = mid + 1;
+ } else {
+ right_side = mid - 1;
+ }
+ }
+
+ pr_debug("%s: temp adc : %d, temp : %d\n", __func__, temp_adc, temp);
+ *batt_temp = temp;
+ return 0;
+}
+
+static int charger_init(struct device *dev)
+{
+ return gpio_request_array(charger_gpios, ARRAY_SIZE(charger_gpios));
+}
+
+static void charger_exit(struct device *dev)
+{
+ gpio_free_array(charger_gpios, ARRAY_SIZE(charger_gpios));
+}
+
+static void set_charge_en(int state)
+{
+ gpio_set_value(GPIO_CHARGE_N, !state);
+}
+
+static void charger_set_charge(int state)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&charge_en_lock, flags);
+ gpio_set_value(GPIO_CHG_CUR_ADJ, !!(state & PDA_POWER_CHARGE_AC));
+ charger_state = state;
+ set_charge_en(state);
+ spin_unlock_irqrestore(&charge_en_lock, flags);
+}
+
+static void charger_set_only_charge(int state)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&charge_en_lock, flags);
+ if (charger_state)
+ set_charge_en(state);
+ spin_unlock_irqrestore(&charge_en_lock, flags);
+ /* CHG_ING_N level changed after set charge_en and 150ms */
+ msleep(150);
+}
+
+static int charger_is_online(void)
+{
+ return !gpio_get_value(GPIO_TA_NCONNECTED);
+}
+
+static int charger_is_charging(void)
+{
+ return !gpio_get_value(GPIO_CHARGING_N);
+}
+
+static char *tuna_charger_supplied_to[] = {
+ "battery",
+};
+
+static const __initdata struct pda_power_pdata charger_pdata = {
+ .init = charger_init,
+ .exit = charger_exit,
+ .set_charge = charger_set_charge,
+ .wait_for_status = 500,
+ .wait_for_charger = 500,
+ .supplied_to = tuna_charger_supplied_to,
+ .num_supplicants = ARRAY_SIZE(tuna_charger_supplied_to),
+ .use_otg_notifier = true,
+};
+
+static struct max17040_platform_data max17043_pdata = {
+ .charger_online = charger_is_online,
+ .charger_enable = charger_is_charging,
+ .allow_charging = charger_set_only_charge,
+ .skip_reset = true,
+ .min_capacity = 3,
+ .is_full_charge = check_charge_full,
+ .get_bat_temp = get_bat_temp_by_adc,
+ .high_block_temp = HIGH_BLOCK_TEMP_MAGURO,
+ .high_recover_temp = HIGH_RECOVER_TEMP_MAGURO,
+ .low_block_temp = LOW_BLOCK_TEMP_MAGURO,
+ .low_recover_temp = LOW_RECOVER_TEMP_MAGURO,
+ .fully_charged_vol = 4150000,
+ .recharge_vol = 4140000,
+ .limit_charging_time = 21600, /* 6 hours */
+ .limit_recharging_time = 5400, /* 90 min */
+};
+
+static const __initdata struct i2c_board_info max17043_i2c[] = {
+ {
+ I2C_BOARD_INFO("max17040", (0x6C >> 1)),
+ .platform_data = &max17043_pdata,
+ .irq = OMAP_GPIO_IRQ(GPIO_FUEL_ALERT),
+ }
+};
+
+static int __init tuna_charger_mode_setup(char *str)
+{
+ if (!str) /* No mode string */
+ return 0;
+
+ is_charging_mode = !strcmp(str, "charger");
+
+ pr_debug("Charge mode string = \"%s\" charger mode = %d\n", str,
+ is_charging_mode);
+
+ return 1;
+}
+
+__setup("androidboot.mode=", tuna_charger_mode_setup);
+
+void __init omap4_tuna_power_init(void)
+{
+ struct platform_device *pdev;
+ int status;
+
+ /* Vsel0 = gpio, vsel1 = gnd */
+ status = omap_tps6236x_board_setup(true, TPS62361_GPIO, -1,
+ OMAP_PIN_OFF_OUTPUT_HIGH, -1);
+ if (status)
+ pr_err("TPS62361 initialization failed: %d\n", status);
+ /*
+ * Some Tuna devices have a 4430 chip on a 4460 board, manually
+ * tweak the power tree to the 4460 style with the TPS regulator.
+ */
+ if (cpu_is_omap443x()) {
+ /* Disable 4430 mapping */
+ omap_twl_pmic_update("mpu", CHIP_IS_OMAP443X, 0x0);
+ omap_twl_pmic_update("core", CHIP_IS_OMAP443X, 0x0);
+ /* make 4460 map usable for 4430 */
+ omap_twl_pmic_update("core", CHIP_IS_OMAP446X, CHIP_IS_OMAP443X);
+ omap_tps6236x_update("mpu", CHIP_IS_OMAP446X, CHIP_IS_OMAP443X);
+ }
+
+ /* Update temperature data from board type */
+ if (omap4_tuna_get_type() == TUNA_TYPE_TORO) {
+ temper_table = temper_table_toro;
+ temper_table_size = ARRAY_SIZE(temper_table_toro);
+
+ max17043_pdata.high_block_temp = HIGH_BLOCK_TEMP_TORO;
+ max17043_pdata.high_recover_temp = HIGH_RECOVER_TEMP_TORO;
+ max17043_pdata.low_block_temp = LOW_BLOCK_TEMP_TORO;
+ max17043_pdata.low_recover_temp = LOW_RECOVER_TEMP_TORO;
+ }
+
+ /* Update oscillator information */
+ if (omap4_tuna_get_revision() <= 0x3) {
+ /*
+ * until sample 4 (Toro and Maguro), we used KC2520B38:
+ * ST = 10ms
+ * Output Disable time = 100ns
+ * Output enable time = 5ms
+ * tstart = 10ms + 5ms = 15ms.
+ * tshut = 1us (rounded)
+ */
+ omap_pm_set_osc_lp_time(15000, 1);
+ } else {
+ /*
+ * sample 5 onwards (Toro and Maguro), we use SQ200384:
+ * ST = 10ms
+ * Output Disable time = 100ns
+ * Output enable time = 10ms
+ * tstart = 10ms + 10ms = 20ms.
+ * tshut = 1us (rounded)
+ */
+ omap_pm_set_osc_lp_time(20000, 1);
+ }
+
+ omap_mux_init_gpio(charger_gpios[0].gpio, OMAP_PIN_INPUT);
+ omap_mux_init_gpio(charger_gpios[1].gpio, OMAP_PIN_INPUT);
+ omap_mux_init_gpio(charger_gpios[2].gpio, OMAP_PIN_OUTPUT);
+ omap_mux_init_gpio(charger_gpios[3].gpio, OMAP_PIN_OUTPUT);
+ omap_mux_init_gpio(GPIO_FUEL_ALERT, OMAP_PIN_INPUT);
+
+ pdev = platform_device_register_resndata(NULL, "pda-power", -1,
+ NULL, 0, &charger_pdata, sizeof(charger_pdata));
+ if (IS_ERR_OR_NULL(pdev))
+ pr_err("cannot register pda-power\n");
+
+ max17043_pdata.use_fuel_alert = !is_charging_mode;
+ i2c_register_board_info(4, max17043_i2c, ARRAY_SIZE(max17043_i2c));
+
+ if (enable_sr)
+ omap_enable_smartreflex_on_init();
+
+ omap_pm_enable_off_mode();
+}
diff --git a/arch/arm/mach-omap2/board-tuna-sensors.c b/arch/arm/mach-omap2/board-tuna-sensors.c
new file mode 100755
index 0000000..f818aad
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-sensors.c
@@ -0,0 +1,212 @@
+/* Sensor support for Samsung Tuna Board.
+ *
+ * Copyright (C) 2011 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/mpu.h>
+#include <linux/gp2a.h>
+#include <linux/i2c/twl6030-madc.h>
+
+#include "mux.h"
+#include "board-tuna.h"
+
+#define GPIO_GYRO_INT 45
+#define GPIO_ACC_INT 122
+#define GPIO_MAG_INT 176
+#define GPIO_PS_ON 25
+#define GPIO_PS_VOUT 21
+#define GPIO_MSENSE_IRQ 157
+
+#define GP2A_LIGHT_ADC_CHANNEL 4
+
+static int gp2a_light_adc_value(void)
+{
+ return twl6030_get_madc_conversion(GP2A_LIGHT_ADC_CHANNEL);
+}
+
+static void gp2a_power(bool on)
+{
+ /* this controls the power supply rail to the gp2a IC */
+ gpio_set_value(GPIO_PS_ON, on);
+}
+
+static void gp2a_gpio_init(void)
+{
+ int ret = gpio_request(GPIO_PS_ON, "gp2a_power_supply_on");
+ if (ret) {
+ pr_err("%s Failed to request gpio gp2a power supply\n",
+ __func__);
+ return;
+ }
+ /* set power pin to output, initially powered off*/
+ ret = gpio_direction_output(GPIO_PS_ON, 0);
+ if (ret) {
+ pr_err("%s Failed in gpio_direction_output, value 0 with error %d\n",
+ __func__, ret);
+ }
+}
+
+static s8 orientation_back[] = {
+ -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1,
+};
+
+static s8 orientation_back_right_90[] = {
+ 0, -1, 0,
+ -1, 0, 0,
+ 0, 0, -1,
+};
+
+static s8 orientation_back_left_90[] = {
+ 0, 1, 0,
+ 1, 0, 0,
+ 0, 0, -1,
+};
+
+static s8 orientation_back_180[] = {
+ 1, 0, 0,
+ 0, -1, 0,
+ 0, 0, -1,
+};
+
+/*
+ * A correction matrix for YAS530
+ * which takes care of soft iron effect in TORO
+ */
+static s32 compass_correction_matrix_toro[] = {
+ 1072, -51, -22,
+ -30, 910, -4,
+ -23, -63, 1024,
+};
+
+static void rotcpy(s8 dst[3 * 3], const s8 src[3 * 3])
+{
+ memcpy(dst, src, 3 * 3);
+}
+
+static struct mpu_platform_data mpu_data = {
+ .int_config = 0x10,
+ .orientation = { 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1 },
+ /* accel */
+ .accel = {
+ .irq = OMAP_GPIO_IRQ(GPIO_ACC_INT),
+ .adapt_num = 4,
+ .bus = EXT_SLAVE_BUS_SECONDARY,
+ .address = 0x18,
+ .orientation = { 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1 },
+ },
+ /* compass */
+ .compass = {
+ .irq = OMAP_GPIO_IRQ(GPIO_MAG_INT),
+ .adapt_num = 4,
+ .bus = EXT_SLAVE_BUS_PRIMARY,
+ .address = 0x2E,
+ .orientation = { 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1 },
+ },
+};
+
+static struct gp2a_platform_data gp2a_pdata = {
+ .power = gp2a_power,
+ .p_out = GPIO_PS_VOUT,
+ .light_adc_value = gp2a_light_adc_value,
+};
+
+static struct i2c_board_info __initdata tuna_sensors_i2c4_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("mpu3050", 0x68),
+ .irq = OMAP_GPIO_IRQ(GPIO_GYRO_INT),
+ .platform_data = &mpu_data,
+ },
+ {
+ I2C_BOARD_INFO("bma250", 0x18),
+ .irq = OMAP_GPIO_IRQ(GPIO_ACC_INT),
+ .platform_data = &mpu_data.accel,
+ },
+ {
+ I2C_BOARD_INFO("yas530", 0x2e),
+ .irq = OMAP_GPIO_IRQ(GPIO_MAG_INT),
+ .platform_data = &mpu_data.compass,
+ },
+ {
+ I2C_BOARD_INFO("gp2a", 0x44),
+ .platform_data = &gp2a_pdata,
+ },
+ {
+ I2C_BOARD_INFO("bmp180", 0x77),
+ },
+};
+
+static void omap4_tuna_fixup_orientations_maguro(int revision)
+{
+ if (revision >= 3) {
+ rotcpy(mpu_data.orientation, orientation_back_right_90);
+ rotcpy(mpu_data.accel.orientation, orientation_back_left_90);
+ } else if (revision >= 2) {
+ rotcpy(mpu_data.orientation, orientation_back_right_90);
+ rotcpy(mpu_data.accel.orientation, orientation_back_180);
+ } else if (revision == 1) {
+ rotcpy(mpu_data.accel.orientation, orientation_back_left_90);
+ }
+}
+
+static void omap4_tuna_fixup_orientations_toro(int revision)
+{
+ if (revision >= 2) {
+ rotcpy(mpu_data.orientation, orientation_back_left_90);
+ rotcpy(mpu_data.accel.orientation, orientation_back);
+ rotcpy(mpu_data.compass.orientation, orientation_back_180);
+ } else if (revision >= 1) {
+ rotcpy(mpu_data.orientation, orientation_back_left_90);
+ rotcpy(mpu_data.accel.orientation, orientation_back_180);
+ rotcpy(mpu_data.compass.orientation, orientation_back_left_90);
+ }
+}
+
+void __init omap4_tuna_sensors_init(void)
+{
+ omap_mux_init_gpio(GPIO_GYRO_INT, OMAP_PIN_INPUT);
+ omap_mux_init_gpio(GPIO_ACC_INT, OMAP_PIN_INPUT);
+ omap_mux_init_gpio(GPIO_MAG_INT, OMAP_PIN_INPUT);
+ omap_mux_init_gpio(GPIO_PS_ON, OMAP_PIN_OUTPUT);
+ omap_mux_init_gpio(GPIO_PS_VOUT, OMAP_WAKEUP_EN | OMAP_PIN_INPUT);
+
+ gpio_request(GPIO_GYRO_INT, "GYRO_INT");
+ gpio_direction_input(GPIO_GYRO_INT);
+ gpio_request(GPIO_ACC_INT, "ACC_INT");
+ gpio_direction_input(GPIO_ACC_INT);
+ gpio_request(GPIO_MAG_INT, "MAG_INT");
+ gpio_direction_input(GPIO_MAG_INT);
+ gpio_request(GPIO_MSENSE_IRQ, "MSENSE_IRQ");
+ gpio_direction_output(GPIO_MSENSE_IRQ, 1);
+ /* optical sensor */
+ gp2a_gpio_init();
+
+ if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO) {
+ omap4_tuna_fixup_orientations_maguro(omap4_tuna_get_revision());
+ } else if (omap4_tuna_get_type() == TUNA_TYPE_TORO) {
+ omap4_tuna_fixup_orientations_toro(omap4_tuna_get_revision());
+ mpu_data.compass.private_data = compass_correction_matrix_toro;
+ }
+
+ i2c_register_board_info(4, tuna_sensors_i2c4_boardinfo,
+ ARRAY_SIZE(tuna_sensors_i2c4_boardinfo));
+}
diff --git a/arch/arm/mach-omap2/board-tuna-usbhost.c b/arch/arm/mach-omap2/board-tuna-usbhost.c
new file mode 100644
index 0000000..e11e954
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-usbhost.c
@@ -0,0 +1,85 @@
+/* USB Host (EHCI) support for Samsung Tuna Board.
+ *
+ * Copyright (C) 2011 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.
+ */
+
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <plat/usb.h>
+
+#include "board-tuna.h"
+#include "mux.h"
+
+#define GPIO_USB3333_RESETB 159
+
+static struct usbhs_omap_board_data usbhs_bdata = {
+ .port_mode[0] = OMAP_EHCI_PORT_MODE_PHY,
+ .port_mode[1] = OMAP_USBHS_PORT_MODE_UNUSED,
+ .port_mode[2] = OMAP_USBHS_PORT_MODE_UNUSED,
+ .phy_reset = false,
+ .reset_gpio_port[0] = -EINVAL,
+ .reset_gpio_port[1] = -EINVAL,
+ .reset_gpio_port[2] = -EINVAL
+};
+
+void __init omap4_ehci_init(void)
+{
+ int ret = 0;
+ struct clk *phy_ref_clk;
+
+ omap_mux_init_gpio(GPIO_USB3333_RESETB, OMAP_PIN_OUTPUT |
+ OMAP_PIN_OFF_NONE);
+
+ ret = gpio_request(GPIO_USB3333_RESETB, "usb3333_resetb");
+ if (ret) {
+ pr_err("omap: ehci: Cannot request GPIO %d",
+ GPIO_USB3333_RESETB);
+ return;
+ }
+ gpio_direction_output(GPIO_USB3333_RESETB, 0);
+ gpio_set_value(GPIO_USB3333_RESETB, 0);
+
+ /* FREF_CLK3 provides the 19.2 MHz reference clock to the PHY */
+ omap_mux_init_signal("fref_clk3_out", OMAP_PIN_OUTPUT | OMAP_MUX_MODE0);
+
+ phy_ref_clk = clk_get(NULL, "auxclk3_ck");
+ if (IS_ERR(phy_ref_clk)) {
+ pr_err("omap: ehci: Cannot request auxclk3");
+ return;
+ }
+ ret = clk_set_rate(phy_ref_clk, 19200000);
+ if (ret < 0) {
+ pr_err("omap: ehci: Cannot clk_set_rate auxclk3 err %d", ret);
+ return;
+ }
+ ret = clk_enable(phy_ref_clk);
+ if (ret < 0) {
+ pr_err("omap: ehci: Cannot clk_enable auxclk3 err %d", ret);
+ return;
+ }
+
+ udelay(100);
+ gpio_set_value(GPIO_USB3333_RESETB, 1);
+
+ /* Everything went well with phy clock, pass it to ehci driver for
+ * low power managment now
+ */
+ usbhs_bdata.transceiver_clk[0] = phy_ref_clk;
+
+ usbhs_init(&usbhs_bdata);
+
+ pr_info("usb:ehci initialized");
+ return;
+}
diff --git a/arch/arm/mach-omap2/board-tuna-vibrator.c b/arch/arm/mach-omap2/board-tuna-vibrator.c
new file mode 100755
index 0000000..de1d5e7
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-vibrator.c
@@ -0,0 +1,185 @@
+/* arch/arm/mach-omap2/board-tuna-vibrator.c
+ *
+ * Copyright (C) 2011 Samsung Electronics Co. Ltd. All Rights Reserved.
+ * Author: Rom Lemarchand <rlemarchand@sta.samsung.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/hrtimer.h>
+#include <linux/gpio.h>
+#include <linux/wakelock.h>
+#include <linux/mutex.h>
+#include <asm/mach-types.h>
+#include <plat/dmtimer.h>
+
+#include <../../../drivers/staging/android/timed_output.h>
+
+#include "mux.h"
+#include "board-tuna.h"
+
+/* Vibrator enable pin is changed on Rev 05 to block not intended vibration. */
+#define GPIO_MOTOR_EN 162
+#define GPIO_MOTOR_EN_REV05 54
+
+#define VIB_GPTIMER_NUM 10
+#define PWM_DUTY_MAX 1450
+#define MAX_TIMEOUT 10000 /* 10s */
+
+static struct vibrator {
+ struct wake_lock wklock;
+ struct hrtimer timer;
+ struct mutex lock;
+ struct omap_dm_timer *gptimer;
+ bool enabled;
+ unsigned gpio_en;
+} vibdata;
+
+static void vibrator_off(void)
+{
+ if (!vibdata.enabled)
+ return;
+ omap_dm_timer_stop(vibdata.gptimer);
+ gpio_set_value(vibdata.gpio_en, 0);
+ vibdata.enabled = false;
+ wake_unlock(&vibdata.wklock);
+}
+
+static int vibrator_get_time(struct timed_output_dev *dev)
+{
+ if (hrtimer_active(&vibdata.timer)) {
+ ktime_t r = hrtimer_get_remaining(&vibdata.timer);
+ return ktime_to_ms(r);
+ }
+
+ return 0;
+}
+
+static void vibrator_enable(struct timed_output_dev *dev, int value)
+{
+ mutex_lock(&vibdata.lock);
+
+ /* cancel previous timer and set GPIO according to value */
+ hrtimer_cancel(&vibdata.timer);
+
+ if (value) {
+ wake_lock(&vibdata.wklock);
+
+ gpio_set_value(vibdata.gpio_en, 1);
+ omap_dm_timer_start(vibdata.gptimer);
+
+ vibdata.enabled = true;
+
+ if (value > 0) {
+ if (value > MAX_TIMEOUT)
+ value = MAX_TIMEOUT;
+
+ hrtimer_start(&vibdata.timer,
+ ns_to_ktime((u64)value * NSEC_PER_MSEC),
+ HRTIMER_MODE_REL);
+ }
+ } else {
+ vibrator_off();
+ }
+
+ mutex_unlock(&vibdata.lock);
+}
+
+static struct timed_output_dev to_dev = {
+ .name = "vibrator",
+ .get_time = vibrator_get_time,
+ .enable = vibrator_enable,
+};
+
+static enum hrtimer_restart vibrator_timer_func(struct hrtimer *timer)
+{
+ vibrator_off();
+ return HRTIMER_NORESTART;
+}
+
+static int __init vibrator_init(void)
+{
+ int ret;
+
+ vibdata.enabled = false;
+
+ hrtimer_init(&vibdata.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ vibdata.timer.function = vibrator_timer_func;
+
+ vibdata.gptimer = omap_dm_timer_request_specific(VIB_GPTIMER_NUM);
+ if (vibdata.gptimer == NULL)
+ return -1;
+
+ ret = omap_dm_timer_set_source(vibdata.gptimer,
+ OMAP_TIMER_SRC_SYS_CLK);
+ if (ret < 0)
+ goto err_dm_timer_src;
+
+ omap_dm_timer_set_load(vibdata.gptimer, 1, -PWM_DUTY_MAX);
+ omap_dm_timer_set_match(vibdata.gptimer, 1, -PWM_DUTY_MAX+10);
+ omap_dm_timer_set_pwm(vibdata.gptimer, 0, 1,
+ OMAP_TIMER_TRIGGER_OVERFLOW_AND_COMPARE);
+ omap_dm_timer_enable(vibdata.gptimer);
+ omap_dm_timer_write_counter(vibdata.gptimer, -2);
+ omap_dm_timer_disable(vibdata.gptimer);
+
+ wake_lock_init(&vibdata.wklock, WAKE_LOCK_SUSPEND, "vibrator");
+ mutex_init(&vibdata.lock);
+
+ ret = timed_output_dev_register(&to_dev);
+ if (ret < 0)
+ goto err_to_dev_reg;
+
+ return 0;
+
+err_to_dev_reg:
+ mutex_destroy(&vibdata.lock);
+ wake_lock_destroy(&vibdata.wklock);
+
+err_dm_timer_src:
+ omap_dm_timer_free(vibdata.gptimer);
+ vibdata.gptimer = NULL;
+
+ return -1;
+}
+
+static int __init omap4_tuna_vibrator_init(void)
+{
+ int ret;
+
+ if (!machine_is_tuna())
+ return 0;
+
+ vibdata.gpio_en = (omap4_tuna_get_revision() >= 5) ?
+ GPIO_MOTOR_EN_REV05 : GPIO_MOTOR_EN;
+
+ omap_mux_init_gpio(vibdata.gpio_en, OMAP_PIN_OUTPUT |
+ OMAP_PIN_OFF_OUTPUT_LOW);
+ omap_mux_init_signal("dpm_emu18.dmtimer10_pwm_evt", OMAP_PIN_OUTPUT);
+
+ ret = gpio_request(vibdata.gpio_en, "vibrator-en");
+ if (ret)
+ return ret;
+
+ gpio_direction_output(vibdata.gpio_en, 0);
+
+ ret = vibrator_init();
+ if (ret < 0)
+ gpio_free(vibdata.gpio_en);
+
+ return ret;
+}
+
+/*
+ * This is needed because the vibrator is dependent on omap_dm_timers which get
+ * initialized at device_init time
+ */
+late_initcall(omap4_tuna_vibrator_init);
diff --git a/arch/arm/mach-omap2/board-tuna-wifi.c b/arch/arm/mach-omap2/board-tuna-wifi.c
new file mode 100644
index 0000000..65d4fbd
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-wifi.c
@@ -0,0 +1,437 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <asm/mach-types.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <asm/setup.h>
+#include <linux/if.h>
+#include <linux/skbuff.h>
+#include <linux/wlan_plat.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/fixed.h>
+#include <plat/mmc.h>
+
+#include <linux/random.h>
+#include <linux/jiffies.h>
+
+#include "hsmmc.h"
+#include "control.h"
+#include "mux.h"
+#include "board-tuna.h"
+
+#define GPIO_WLAN_PMENA 104
+#define GPIO_WLAN_IRQ 2
+
+#define ATAG_TUNA_MAC 0x57464d41
+/* #define ATAG_TUNA_MAC_DEBUG */
+
+#ifdef CONFIG_DHD_USE_STATIC_BUF
+#define PREALLOC_WLAN_NUMBER_OF_SECTIONS 4
+#define PREALLOC_WLAN_NUMBER_OF_BUFFERS 160
+#define PREALLOC_WLAN_SECTION_HEADER 24
+
+#define WLAN_SECTION_SIZE_0 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128)
+#define WLAN_SECTION_SIZE_1 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 128)
+#define WLAN_SECTION_SIZE_2 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 512)
+#define WLAN_SECTION_SIZE_3 (PREALLOC_WLAN_NUMBER_OF_BUFFERS * 1024)
+
+#define WLAN_SKB_BUF_NUM 16
+
+static struct sk_buff *wlan_static_skb[WLAN_SKB_BUF_NUM];
+
+typedef struct wifi_mem_prealloc_struct {
+ void *mem_ptr;
+ unsigned long size;
+} wifi_mem_prealloc_t;
+
+static wifi_mem_prealloc_t wifi_mem_array[PREALLOC_WLAN_NUMBER_OF_SECTIONS] = {
+ { NULL, (WLAN_SECTION_SIZE_0 + PREALLOC_WLAN_SECTION_HEADER) },
+ { NULL, (WLAN_SECTION_SIZE_1 + PREALLOC_WLAN_SECTION_HEADER) },
+ { NULL, (WLAN_SECTION_SIZE_2 + PREALLOC_WLAN_SECTION_HEADER) },
+ { NULL, (WLAN_SECTION_SIZE_3 + PREALLOC_WLAN_SECTION_HEADER) }
+};
+
+static void *tuna_wifi_mem_prealloc(int section, unsigned long size)
+{
+ if (section == PREALLOC_WLAN_NUMBER_OF_SECTIONS)
+ return wlan_static_skb;
+ if ((section < 0) || (section > PREALLOC_WLAN_NUMBER_OF_SECTIONS))
+ return NULL;
+ if (wifi_mem_array[section].size < size)
+ return NULL;
+ return wifi_mem_array[section].mem_ptr;
+}
+#endif
+
+int __init tuna_init_wifi_mem(void)
+{
+#ifdef CONFIG_DHD_USE_STATIC_BUF
+ int i;
+
+ for(i=0;( i < WLAN_SKB_BUF_NUM );i++) {
+ if (i < (WLAN_SKB_BUF_NUM/2))
+ wlan_static_skb[i] = dev_alloc_skb(4096);
+ else
+ wlan_static_skb[i] = dev_alloc_skb(8192);
+ }
+ for(i=0;( i < PREALLOC_WLAN_NUMBER_OF_SECTIONS );i++) {
+ wifi_mem_array[i].mem_ptr = kmalloc(wifi_mem_array[i].size,
+ GFP_KERNEL);
+ if (wifi_mem_array[i].mem_ptr == NULL)
+ return -ENOMEM;
+ }
+#endif
+ return 0;
+}
+
+static struct resource tuna_wifi_resources[] = {
+ [0] = {
+ .name = "bcmdhd_wlan_irq",
+ .start = OMAP_GPIO_IRQ(GPIO_WLAN_IRQ),
+ .end = OMAP_GPIO_IRQ(GPIO_WLAN_IRQ),
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHLEVEL | IORESOURCE_IRQ_SHAREABLE,
+ },
+};
+
+#if 0
+/* BCM4329 returns wrong sdio_vsn(1) when we read cccr,
+ * we use predefined value (sdio_vsn=2) here to initial sdio driver well
+ */
+static struct embedded_sdio_data tuna_wifi_emb_data = {
+ .cccr = {
+ .sdio_vsn = 2,
+ .multi_block = 1,
+ .low_speed = 0,
+ .wide_bus = 0,
+ .high_power = 1,
+ .high_speed = 1,
+ },
+};
+#endif
+
+static int tuna_wifi_cd = 0; /* WIFI virtual 'card detect' status */
+static void (*wifi_status_cb)(int card_present, void *dev_id);
+static void *wifi_status_cb_devid;
+static struct regulator *clk32kaudio_reg;
+
+static int tuna_wifi_status_register(
+ void (*callback)(int card_present, void *dev_id),
+ void *dev_id)
+{
+ if (wifi_status_cb)
+ return -EAGAIN;
+ wifi_status_cb = callback;
+ wifi_status_cb_devid = dev_id;
+ return 0;
+}
+
+static unsigned int tuna_wifi_status(struct device *dev)
+{
+ return tuna_wifi_cd;
+}
+
+struct mmc_platform_data tuna_wifi_data = {
+ .ocr_mask = MMC_VDD_165_195 | MMC_VDD_20_21,
+ .built_in = 1,
+ .status = tuna_wifi_status,
+ .card_present = 0,
+ .register_status_notify = tuna_wifi_status_register,
+};
+
+static int tuna_wifi_set_carddetect(int val)
+{
+ pr_debug("%s: %d\n", __func__, val);
+ tuna_wifi_cd = val;
+ if (wifi_status_cb) {
+ wifi_status_cb(val, wifi_status_cb_devid);
+ } else
+ pr_warning("%s: Nobody to notify\n", __func__);
+ return 0;
+}
+
+static int tuna_wifi_power_state;
+
+struct fixed_voltage_data {
+ struct regulator_desc desc;
+ struct regulator_dev *dev;
+ int microvolts;
+ int gpio;
+ unsigned startup_delay;
+ bool enable_high;
+ bool is_enabled;
+};
+
+static struct regulator_consumer_supply tuna_vmmc5_supply = {
+ .supply = "vmmc",
+ .dev_name = "omap_hsmmc.4",
+};
+
+static struct regulator_init_data tuna_vmmc5 = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 1,
+ .consumer_supplies = &tuna_vmmc5_supply,
+};
+
+static struct fixed_voltage_config tuna_vwlan = {
+ .supply_name = "vwl1271",
+ .microvolts = 2000000, /* 2.0V */
+ .gpio = GPIO_WLAN_PMENA,
+ .startup_delay = 70000, /* 70msec */
+ .enable_high = 1,
+ .enabled_at_boot = 0,
+ .init_data = &tuna_vmmc5,
+};
+
+static struct platform_device omap_vwlan_device = {
+ .name = "reg-fixed-voltage",
+ .id = 1,
+ .dev = {
+ .platform_data = &tuna_vwlan,
+ },
+};
+
+static int tuna_wifi_power(int on)
+{
+ int ret = 0;
+
+ pr_debug("%s: %d\n", __func__, on);
+ if (!clk32kaudio_reg) {
+ clk32kaudio_reg = regulator_get(0, "clk32kaudio");
+ if (IS_ERR(clk32kaudio_reg)) {
+ pr_err("%s: clk32kaudio reg not found!\n", __func__);
+ clk32kaudio_reg = NULL;
+ }
+ }
+
+ if (clk32kaudio_reg && on && !tuna_wifi_power_state) {
+ ret = regulator_enable(clk32kaudio_reg);
+ if (ret)
+ pr_err("%s: regulator_enable failed: %d\n", __func__,
+ ret);
+ }
+ msleep(300);
+ gpio_set_value(GPIO_WLAN_PMENA, on);
+ msleep(200);
+
+ if (clk32kaudio_reg && !on && tuna_wifi_power_state) {
+ ret = regulator_disable(clk32kaudio_reg);
+ if (ret)
+ pr_err("%s: regulator_disable failed: %d\n", __func__,
+ ret);
+ }
+ tuna_wifi_power_state = on;
+ return ret;
+}
+
+static int tuna_wifi_reset_state;
+
+static int tuna_wifi_reset(int on)
+{
+ pr_debug("%s: do nothing\n", __func__);
+ tuna_wifi_reset_state = on;
+ return 0;
+}
+
+static unsigned char tuna_mac_addr[IFHWADDRLEN] = { 0,0x90,0x4c,0,0,0 };
+
+static int __init tuna_mac_addr_setup(char *str)
+{
+ char macstr[IFHWADDRLEN*3];
+ char *macptr = macstr;
+ char *token;
+ int i = 0;
+
+ if (!str)
+ return 0;
+ pr_debug("wlan MAC = %s\n", str);
+ if (strlen(str) >= sizeof(macstr))
+ return 0;
+ strcpy(macstr, str);
+
+ while ((token = strsep(&macptr, ":")) != NULL) {
+ unsigned long val;
+ int res;
+
+ if (i >= IFHWADDRLEN)
+ break;
+ res = strict_strtoul(token, 0x10, &val);
+ if (res < 0)
+ return 0;
+ tuna_mac_addr[i++] = (u8)val;
+ }
+
+ return 1;
+}
+
+__setup("androidboot.macaddr=", tuna_mac_addr_setup);
+
+static int tuna_wifi_get_mac_addr(unsigned char *buf)
+{
+ int type = omap4_tuna_get_type();
+ uint rand_mac;
+
+ if (type != TUNA_TYPE_TORO)
+ return -EINVAL;
+
+ if (!buf)
+ return -EFAULT;
+
+ if ((tuna_mac_addr[4] == 0) && (tuna_mac_addr[5] == 0)) {
+ srandom32((uint)jiffies);
+ rand_mac = random32();
+ tuna_mac_addr[3] = (unsigned char)rand_mac;
+ tuna_mac_addr[4] = (unsigned char)(rand_mac >> 8);
+ tuna_mac_addr[5] = (unsigned char)(rand_mac >> 16);
+ }
+ memcpy(buf, tuna_mac_addr, IFHWADDRLEN);
+ return 0;
+}
+
+/* Customized Locale table : OPTIONAL feature */
+#define WLC_CNTRY_BUF_SZ 4
+typedef struct cntry_locales_custom {
+ char iso_abbrev[WLC_CNTRY_BUF_SZ];
+ char custom_locale[WLC_CNTRY_BUF_SZ];
+ int custom_locale_rev;
+} cntry_locales_custom_t;
+
+static cntry_locales_custom_t tuna_wifi_translate_custom_table[] = {
+/* Table should be filled out based on custom platform regulatory requirement */
+ {"", "XY", 4}, /* universal */
+ {"US", "US", 69}, /* input ISO "US" to : US regrev 69 */
+ {"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */
+ {"EU", "EU", 5}, /* European union countries */
+ {"AT", "EU", 5},
+ {"BE", "EU", 5},
+ {"BG", "EU", 5},
+ {"CY", "EU", 5},
+ {"CZ", "EU", 5},
+ {"DK", "EU", 5},
+ {"EE", "EU", 5},
+ {"FI", "EU", 5},
+ {"FR", "EU", 5},
+ {"DE", "EU", 5},
+ {"GR", "EU", 5},
+ {"HU", "EU", 5},
+ {"IE", "EU", 5},
+ {"IT", "EU", 5},
+ {"LV", "EU", 5},
+ {"LI", "EU", 5},
+ {"LT", "EU", 5},
+ {"LU", "EU", 5},
+ {"MT", "EU", 5},
+ {"NL", "EU", 5},
+ {"PL", "EU", 5},
+ {"PT", "EU", 5},
+ {"RO", "EU", 5},
+ {"SK", "EU", 5},
+ {"SI", "EU", 5},
+ {"ES", "EU", 5},
+ {"SE", "EU", 5},
+ {"GB", "EU", 5}, /* input ISO "GB" to : EU regrev 05 */
+ {"IL", "IL", 0},
+ {"CH", "CH", 0},
+ {"TR", "TR", 0},
+ {"NO", "NO", 0},
+ {"KR", "KR", 25},
+ {"AU", "XY", 3},
+ {"CN", "CN", 0},
+ {"TW", "XY", 3},
+ {"AR", "XY", 3},
+ {"MX", "XY", 3},
+ {"JP", "EU", 0},
+ {"BR", "KR", 25}
+};
+
+static void *tuna_wifi_get_country_code(char *ccode)
+{
+ int size = ARRAY_SIZE(tuna_wifi_translate_custom_table);
+ int i;
+
+ if (!ccode)
+ return NULL;
+
+ for (i = 0; i < size; i++)
+ if (strcmp(ccode, tuna_wifi_translate_custom_table[i].iso_abbrev) == 0)
+ return &tuna_wifi_translate_custom_table[i];
+ return &tuna_wifi_translate_custom_table[0];
+}
+
+static struct wifi_platform_data tuna_wifi_control = {
+ .set_power = tuna_wifi_power,
+ .set_reset = tuna_wifi_reset,
+ .set_carddetect = tuna_wifi_set_carddetect,
+#ifdef CONFIG_DHD_USE_STATIC_BUF
+ .mem_prealloc = tuna_wifi_mem_prealloc,
+#else
+ .mem_prealloc = NULL,
+#endif
+ .get_mac_addr = tuna_wifi_get_mac_addr,
+ .get_country_code = tuna_wifi_get_country_code,
+};
+
+static struct platform_device tuna_wifi_device = {
+ .name = "bcmdhd_wlan",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(tuna_wifi_resources),
+ .resource = tuna_wifi_resources,
+ .dev = {
+ .platform_data = &tuna_wifi_control,
+ },
+};
+
+static void __init tuna_wlan_gpio(void)
+{
+ pr_debug("%s: start\n", __func__);
+
+ /* WLAN SDIO: MMC5 CMD */
+ omap_mux_init_signal("sdmmc5_cmd", OMAP_PIN_INPUT_PULLUP);
+ /* WLAN SDIO: MMC5 CLK */
+ omap_mux_init_signal("sdmmc5_clk", OMAP_PIN_INPUT_PULLUP);
+ /* WLAN SDIO: MMC5 DAT[0-3] */
+ omap_mux_init_signal("sdmmc5_dat0", OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_signal("sdmmc5_dat1", OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_signal("sdmmc5_dat2", OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_signal("sdmmc5_dat3", OMAP_PIN_INPUT_PULLUP);
+ /* WLAN OOB - BCM4330 - GPIO 16 or GPIO 2 */
+ omap_mux_init_signal("sim_reset.gpio_wk2", OMAP_PIN_INPUT);
+ omap_mux_init_signal("kpd_row1.safe_mode", 0);
+ /* WLAN PMENA - GPIO 104 */
+ omap_mux_init_signal("gpmc_ncs7.gpio_104", OMAP_PIN_OUTPUT);
+ /* Enable power to gpio_wk0-gpio_wk2 */
+ omap4_ctrl_wk_pad_writel(0xb0000000,
+ OMAP4_CTRL_MODULE_PAD_WKUP_CONTROL_USIMIO);
+
+ /* gpio_enable(GPIO_WLAN_IRQ); */
+ gpio_request(GPIO_WLAN_IRQ, "wlan_irq");
+ gpio_direction_input(GPIO_WLAN_IRQ);
+}
+
+int __init tuna_wlan_init(void)
+{
+ pr_debug("%s: start\n", __func__);
+
+ tuna_wlan_gpio();
+ tuna_init_wifi_mem();
+ platform_device_register(&omap_vwlan_device);
+ return platform_device_register(&tuna_wifi_device);
+}
diff --git a/arch/arm/mach-omap2/board-tuna.c b/arch/arm/mach-omap2/board-tuna.c
new file mode 100644
index 0000000..ba69f87
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna.c
@@ -0,0 +1,1436 @@
+/* Board support file for Samsung Tuna Board.
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2010 Texas Instruments
+ *
+ * Based on mach-omap2/board-omap4panda.c
+ *
+ * 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/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/ion.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+#include <linux/memblock.h>
+#include <linux/omap_ion.h>
+#include <linux/usb/otg.h>
+#include <linux/i2c/twl.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/fixed.h>
+#include <linux/wl12xx.h>
+#include <linux/reboot.h>
+#include <linux/memblock.h>
+#include <linux/sysfs.h>
+#include <linux/uaccess.h>
+#include <linux/proc_fs.h>
+#include <linux/spi/spi.h>
+#include <linux/spi/flash.h>
+#include <linux/platform_data/lte_modem_bootloader.h>
+#include <linux/platform_data/ram_console.h>
+#include <plat/mcspi.h>
+#include <linux/i2c-gpio.h>
+
+#include <mach/hardware.h>
+#include <mach/omap4-common.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <plat/board-tuna-bluetooth.h>
+#include <plat/omap-serial.h>
+#include <plat/board.h>
+#include <plat/common.h>
+#include <plat/cpu.h>
+#include <plat/usb.h>
+#include <plat/mmc.h>
+#include <plat/remoteproc.h>
+#include <plat/omap-serial.h>
+
+#include <mach/omap_fiq_debugger.h>
+
+#include <mach/id.h>
+#include "timer-gp.h"
+
+#include "omap4-sar-layout.h"
+#include "hsmmc.h"
+#include "control.h"
+#include "mux.h"
+#include "board-tuna.h"
+#include "resetreason.h"
+#include <mach/dmm.h>
+
+#define TUNA_RAMCONSOLE_START (PLAT_PHYS_OFFSET + SZ_512M)
+#define TUNA_RAMCONSOLE_SIZE SZ_2M
+
+struct class *sec_class;
+EXPORT_SYMBOL(sec_class);
+
+/* For LTE(CMC221) */
+#define OMAP_GPIO_LTE_ACTIVE 47
+#define OMAP_GPIO_CMC2AP_INT1 61
+
+#define GPIO_AUD_PWRON 127
+#define GPIO_AUD_PWRON_TORO_V1 20
+
+/* GPS GPIO Setting */
+#define GPIO_AP_AGPS_TSYNC 18
+#define GPIO_GPS_nRST 136
+#define GPIO_GPS_PWR_EN 137
+#define GPIO_GPS_UART_SEL 164
+
+#define GPIO_MHL_SCL_18V 99
+#define GPIO_MHL_SDA_18V 98
+
+#define REBOOT_FLAG_RECOVERY 0x52564352
+#define REBOOT_FLAG_FASTBOOT 0x54534146
+#define REBOOT_FLAG_NORMAL 0x4D524F4E
+#define REBOOT_FLAG_POWER_OFF 0x46464F50
+
+#define UART_NUM_FOR_GPS 0
+
+static int tuna_hw_rev;
+
+static struct gpio tuna_hw_rev_gpios[] = {
+ {76, GPIOF_IN, "hw_rev0"},
+ {75, GPIOF_IN, "hw_rev1"},
+ {74, GPIOF_IN, "hw_rev2"},
+ {73, GPIOF_IN, "hw_rev3"},
+ {170, GPIOF_IN, "hw_rev4"},
+};
+
+static const char const *omap4_tuna_hw_name_maguro[] = {
+ [0x00] = "Toro Lunchbox #1",
+ [0x01] = "Maguro 1st Sample",
+ [0x02] = "Maguro 2nd Sample",
+ [0x03] = "Maguro 4th Sample",
+ [0x05] = "Maguro 5th Sample",
+ [0x07] = "Maguro 6th Sample",
+ [0x08] = "Maguro 7th Sample",
+ [0x09] = "Maguro 8th Sample",
+};
+
+static const char const *omap4_tuna_hw_name_toro[] = {
+ [0x00] = "Toro Lunchbox #2",
+ [0x01] = "Toro 1st Sample",
+ [0x02] = "Toro 2nd Sample",
+ [0x03] = "Toro 4th Sample",
+ [0x05] = "Toro 5th Sample",
+ [0x06] = "Toro 8th Sample",
+ [0x08] = "Toro 8th Sample",
+ [0x09] = "Toro 8-1th Sample",
+};
+
+int omap4_tuna_get_revision(void)
+{
+ return tuna_hw_rev & TUNA_REV_MASK;
+}
+
+int omap4_tuna_get_type(void)
+{
+ return tuna_hw_rev & TUNA_TYPE_MASK;
+}
+
+
+static const char *omap4_tuna_hw_rev_name(void) {
+ const char *ret;
+ const char **names;
+ int num;
+ int rev;
+
+ if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO) {
+ names = omap4_tuna_hw_name_maguro;
+ num = ARRAY_SIZE(omap4_tuna_hw_name_maguro);
+ ret = "Maguro unknown";
+ } else {
+ names = omap4_tuna_hw_name_toro;
+ num = ARRAY_SIZE(omap4_tuna_hw_name_toro);
+ ret = "Toro unknown";
+ }
+
+ rev = omap4_tuna_get_revision();
+ if (rev >= num || !names[rev])
+ return ret;
+
+ return names[rev];
+}
+
+/*
+ * Store a handy board information string which we can use elsewhere like
+ * like in panic situation
+ */
+static char omap4_tuna_bd_info_string[255];
+static void omap4_tuna_init_hw_rev(void)
+{
+ int ret;
+ int i;
+ u32 r;
+
+ /* Disable weak driver pulldown on usbb2_hsic_strobe */
+ r = omap4_ctrl_pad_readl(OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_USBB_HSIC);
+ r &= ~OMAP4_USBB2_HSIC_STROBE_WD_MASK;
+ omap4_ctrl_pad_writel(r, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_USBB_HSIC);
+
+ ret = gpio_request_array(tuna_hw_rev_gpios,
+ ARRAY_SIZE(tuna_hw_rev_gpios));
+
+ BUG_ON(ret);
+
+ for (i = 0; i < ARRAY_SIZE(tuna_hw_rev_gpios); i++)
+ tuna_hw_rev |= gpio_get_value(tuna_hw_rev_gpios[i].gpio) << i;
+
+ snprintf(omap4_tuna_bd_info_string,
+ ARRAY_SIZE(omap4_tuna_bd_info_string),
+ "Tuna HW revision: %02x (%s), cpu %s ES%d.%d ",
+ tuna_hw_rev,
+ omap4_tuna_hw_rev_name(),
+ cpu_is_omap443x() ? "OMAP4430" : "OMAP4460",
+ (GET_OMAP_REVISION() >> 4) & 0xf,
+ GET_OMAP_REVISION() & 0xf);
+
+ pr_info("%s\n", omap4_tuna_bd_info_string);
+ mach_panic_string = omap4_tuna_bd_info_string;
+}
+
+/* wl127x BT, FM, GPS connectivity chip */
+static int wl1271_gpios[] = {46, -1, -1};
+static struct platform_device wl1271_device = {
+ .name = "kim",
+ .id = -1,
+ .dev = {
+ .platform_data = &wl1271_gpios,
+ },
+};
+
+static struct resource ramconsole_resources[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ .start = TUNA_RAMCONSOLE_START,
+ .end = TUNA_RAMCONSOLE_START + TUNA_RAMCONSOLE_SIZE - 1,
+ },
+};
+
+static struct ram_console_platform_data ramconsole_pdata;
+
+static struct platform_device ramconsole_device = {
+ .name = "ram_console",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ramconsole_resources),
+ .resource = ramconsole_resources,
+ .dev = {
+ .platform_data = &ramconsole_pdata,
+ },
+};
+
+static struct platform_device bcm4330_bluetooth_device = {
+ .name = "bcm4330_bluetooth",
+ .id = -1,
+};
+
+static void __init tuna_bt_init(void)
+{
+ /* BT_EN - GPIO 104 */
+ omap_mux_init_signal("gpmc_ncs6.gpio_103", OMAP_PIN_OUTPUT);
+ /*BT_nRST - GPIO 42 */
+ omap_mux_init_signal("gpmc_a18.gpio_42", OMAP_PIN_OUTPUT);
+ /* BT_WAKE - GPIO 27 */
+ omap_mux_init_signal("dpm_emu16.gpio_27", OMAP_PIN_OUTPUT);
+ /* BT_HOST_WAKE - GPIO 177 */
+ omap_mux_init_signal("kpd_row5.gpio_177", OMAP_WAKEUP_EN | OMAP_PIN_INPUT);
+
+ platform_device_register(&bcm4330_bluetooth_device);
+}
+
+static struct twl4030_madc_platform_data twl6030_madc = {
+ .irq_line = -1,
+};
+
+static struct platform_device twl6030_madc_device = {
+ .name = "twl6030_madc",
+ .id = -1,
+ .dev = {
+ .platform_data = &twl6030_madc,
+ },
+};
+
+
+static struct i2c_gpio_platform_data tuna_gpio_i2c5_pdata = {
+ .sda_pin = GPIO_MHL_SDA_18V,
+ .scl_pin = GPIO_MHL_SCL_18V,
+ .udelay = 3,
+ .timeout = 0,
+};
+
+static struct platform_device tuna_gpio_i2c5_device = {
+ .name = "i2c-gpio",
+ .id = 5,
+ .dev = {
+ .platform_data = &tuna_gpio_i2c5_pdata,
+ }
+};
+
+#define PHYS_ADDR_SMC_SIZE (SZ_1M * 3)
+#define PHYS_ADDR_DUCATI_SIZE (SZ_1M * 105)
+#define OMAP_TUNA_ION_HEAP_SECURE_INPUT_SIZE (SZ_1M * 90)
+#define OMAP_TUNA_ION_HEAP_TILER_SIZE (SZ_1M * 81)
+#define OMAP_TUNA_ION_HEAP_NONSECURE_TILER_SIZE (SZ_1M * 15)
+
+#define PHYS_ADDR_SMC_MEM (0x80000000 + SZ_1G - PHYS_ADDR_SMC_SIZE)
+#define PHYS_ADDR_DUCATI_MEM (PHYS_ADDR_SMC_MEM - \
+ PHYS_ADDR_DUCATI_SIZE - \
+ OMAP_TUNA_ION_HEAP_SECURE_INPUT_SIZE)
+
+static struct ion_platform_data tuna_ion_data = {
+ .nr = 3,
+ .heaps = {
+ {
+ .type = ION_HEAP_TYPE_CARVEOUT,
+ .id = OMAP_ION_HEAP_SECURE_INPUT,
+ .name = "secure_input",
+ .base = PHYS_ADDR_SMC_MEM -
+ OMAP_TUNA_ION_HEAP_SECURE_INPUT_SIZE,
+ .size = OMAP_TUNA_ION_HEAP_SECURE_INPUT_SIZE,
+ },
+ { .type = OMAP_ION_HEAP_TYPE_TILER,
+ .id = OMAP_ION_HEAP_TILER,
+ .name = "tiler",
+ .base = PHYS_ADDR_DUCATI_MEM -
+ OMAP_TUNA_ION_HEAP_TILER_SIZE,
+ .size = OMAP_TUNA_ION_HEAP_TILER_SIZE,
+ },
+ { .type = OMAP_ION_HEAP_TYPE_TILER,
+ .id = OMAP_ION_HEAP_NONSECURE_TILER,
+ .name = "nonsecure_tiler",
+ .base = PHYS_ADDR_DUCATI_MEM -
+ OMAP_TUNA_ION_HEAP_TILER_SIZE -
+ OMAP_TUNA_ION_HEAP_NONSECURE_TILER_SIZE,
+ .size = OMAP_TUNA_ION_HEAP_NONSECURE_TILER_SIZE,
+ },
+ },
+};
+
+static struct platform_device tuna_ion_device = {
+ .name = "ion-omap4",
+ .id = -1,
+ .dev = {
+ .platform_data = &tuna_ion_data,
+ },
+};
+
+static struct platform_device tuna_spdif_dit_device = {
+ .name = "spdif-dit",
+ .id = 0,
+};
+
+static struct platform_device *tuna_devices[] __initdata = {
+ &ramconsole_device,
+ &wl1271_device,
+ &twl6030_madc_device,
+ &tuna_ion_device,
+ &tuna_gpio_i2c5_device,
+ &tuna_spdif_dit_device,
+};
+
+/*
+ * The UART1 is for GPS, and CSR GPS chip should control uart1 rts level
+ * for gps firmware download.
+ */
+static int uart1_rts_ctrl_write(struct file *file, const char __user *buffer,
+ size_t count, loff_t *offs)
+{
+ char buf[10] = {0,};
+
+ if (omap4_tuna_get_revision() < TUNA_REV_SAMPLE_4)
+ return -ENXIO;
+ if (count > sizeof(buf) - 1)
+ return -EINVAL;
+ if (copy_from_user(buf, buffer, count))
+ return -EFAULT;
+
+ if (!strncmp(buf, "1", 1)) {
+ omap_rts_mux_write(OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE7,
+ UART_NUM_FOR_GPS);
+ } else if (!strncmp(buf, "0", 1)) {
+ omap_rts_mux_write(OMAP_PIN_OUTPUT | OMAP_MUX_MODE1,
+ UART_NUM_FOR_GPS);
+ }
+
+ return count;
+}
+
+static const struct file_operations uart1_rts_ctrl_proc_fops = {
+ .write = uart1_rts_ctrl_write,
+};
+
+static int __init tuna_gps_rts_ctrl_init(void)
+{
+ struct proc_dir_entry *p_entry;
+
+ p_entry = proc_create("mcspi1_cs3_ctrl", 0666, NULL,
+ &uart1_rts_ctrl_proc_fops);
+
+ if (!p_entry)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void tuna_gsd4t_gps_gpio(void)
+{
+ /* AP_AGPS_TSYNC - GPIO 18 */
+ omap_mux_init_signal("dpm_emu7.gpio_18", OMAP_PIN_OUTPUT);
+ /* GPS_nRST - GPIO 136 */
+ omap_mux_init_signal("mcspi1_simo.gpio_136", OMAP_PIN_OUTPUT);
+ /* GPS_PWR_EN - GPIO 137 */
+ omap_mux_init_signal("mcspi1_cs0.gpio_137", OMAP_PIN_OUTPUT);
+ /* GPS_UART_SEL - GPIO 164 */
+ omap_mux_init_signal("usbb2_ulpitll_dat3.gpio_164", OMAP_PIN_OUTPUT);
+}
+
+static void tuna_gsd4t_gps_init(void)
+{
+ struct device *gps_dev;
+
+ gps_dev = device_create(sec_class, NULL, 0, NULL, "gps");
+ if (IS_ERR(gps_dev)) {
+ pr_err("Failed to create device(gps)!\n");
+ goto err;
+ }
+ tuna_gsd4t_gps_gpio();
+
+ gpio_request(GPIO_AP_AGPS_TSYNC, "AP_AGPS_TSYNC");
+ gpio_direction_output(GPIO_AP_AGPS_TSYNC, 0);
+
+ gpio_request(GPIO_GPS_nRST, "GPS_nRST");
+ gpio_direction_output(GPIO_GPS_nRST, 1);
+
+ gpio_request(GPIO_GPS_PWR_EN, "GPS_PWR_EN");
+ gpio_direction_output(GPIO_GPS_PWR_EN, 0);
+
+ gpio_request(GPIO_GPS_UART_SEL , "GPS_UART_SEL");
+ gpio_direction_output(GPIO_GPS_UART_SEL , 0);
+
+ gpio_export(GPIO_GPS_nRST, 1);
+ gpio_export(GPIO_GPS_PWR_EN, 1);
+
+ gpio_export_link(gps_dev, "GPS_nRST", GPIO_GPS_nRST);
+ gpio_export_link(gps_dev, "GPS_PWR_EN", GPIO_GPS_PWR_EN);
+
+ tuna_gps_rts_ctrl_init();
+
+err:
+ return;
+}
+
+static int __init sec_common_init(void)
+{
+ sec_class = class_create(THIS_MODULE, "sec");
+ if (IS_ERR(sec_class))
+ pr_err("Failed to create class(sec)!\n");
+
+ return 0;
+}
+
+static void __init tuna_init_early(void)
+{
+ omap2_init_common_infrastructure();
+ omap2_init_common_devices(NULL, NULL);
+}
+
+static struct omap_musb_board_data musb_board_data = {
+ .interface_type = MUSB_INTERFACE_UTMI,
+#ifdef CONFIG_USB_MUSB_OTG
+ .mode = MUSB_OTG,
+#else
+ .mode = MUSB_PERIPHERAL,
+#endif
+ .power = 500,
+};
+
+static struct omap2_hsmmc_info mmc[] = {
+ {
+ .mmc = 1,
+ .nonremovable = true,
+ .caps = MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA,
+ .ocr_mask = MMC_VDD_165_195,
+ .gpio_wp = -EINVAL,
+ .gpio_cd = -EINVAL,
+ },
+ {
+ .name = "omap_wlan",
+ .mmc = 5,
+ .caps = MMC_CAP_4_BIT_DATA,
+ .gpio_wp = -EINVAL,
+ .gpio_cd = -EINVAL,
+ .ocr_mask = MMC_VDD_165_195 | MMC_VDD_20_21,
+ .nonremovable = false,
+ .mmc_data = &tuna_wifi_data,
+ },
+ {} /* Terminator */
+};
+
+static struct regulator_consumer_supply tuna_vmmc_supply[] = {
+ {
+ .supply = "vmmc",
+ .dev_name = "omap_hsmmc.0",
+ },
+ {
+ .supply = "vmmc",
+ .dev_name = "omap_hsmmc.1",
+ },
+};
+
+static struct regulator_init_data tuna_vaux1 = {
+ .constraints = {
+ .min_uV = 3000000,
+ .max_uV = 3000000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ .always_on = true,
+ },
+};
+
+static struct regulator_init_data tuna_vaux2 = {
+ .constraints = {
+ .min_uV = 1200000,
+ .max_uV = 2800000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ .state_mem = {
+ .disabled = true,
+ },
+ .initial_state = PM_SUSPEND_MEM,
+ },
+};
+
+static struct regulator_consumer_supply tuna_vaux3_supplies[] = {
+ {
+ .supply = "vlcd",
+ },
+};
+
+static struct regulator_init_data tuna_vaux3 = {
+ .constraints = {
+ .min_uV = 3100000,
+ .max_uV = 3100000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(tuna_vaux3_supplies),
+ .consumer_supplies = tuna_vaux3_supplies,
+};
+
+static struct regulator_init_data tuna_vmmc = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = 2,
+ .consumer_supplies = tuna_vmmc_supply,
+};
+
+static struct regulator_init_data tuna_vpp = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 2500000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_VOLTAGE
+ | REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ .state_mem = {
+ .disabled = true,
+ },
+ .initial_state = PM_SUSPEND_MEM,
+ },
+};
+
+static struct regulator_consumer_supply tuna_vusim_supplies[] = {
+ {
+ .supply = "vlcd-iovcc",
+ },
+};
+
+static struct regulator_init_data tuna_vusim = {
+ .constraints = {
+ .min_uV = 2200000,
+ .max_uV = 2200000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(tuna_vusim_supplies),
+ .consumer_supplies = tuna_vusim_supplies,
+};
+
+static struct regulator_init_data tuna_vana = {
+ .constraints = {
+ .min_uV = 2100000,
+ .max_uV = 2100000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ .always_on = true,
+ },
+};
+
+static struct regulator_consumer_supply tuna_vcxio_supply[] = {
+ REGULATOR_SUPPLY("vdds_dsi", "omapdss_dss"),
+ REGULATOR_SUPPLY("vdds_dsi", "omapdss_dsi1"),
+};
+
+static struct regulator_init_data tuna_vcxio = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(tuna_vcxio_supply),
+ .consumer_supplies = tuna_vcxio_supply,
+
+};
+
+static struct regulator_consumer_supply tuna_vdac_supply[] = {
+ {
+ .supply = "hdmi_vref",
+ },
+};
+
+static struct regulator_init_data tuna_vdac = {
+ .constraints = {
+ .min_uV = 1800000,
+ .max_uV = 1800000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(tuna_vdac_supply),
+ .consumer_supplies = tuna_vdac_supply,
+};
+
+static struct regulator_consumer_supply tuna_vusb_supply[] = {
+ REGULATOR_SUPPLY("vusb", "tuna_otg"),
+};
+
+static struct regulator_init_data tuna_vusb = {
+ .constraints = {
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ .state_mem = {
+ .disabled = true,
+ },
+ .initial_state = PM_SUSPEND_MEM,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(tuna_vusb_supply),
+ .consumer_supplies = tuna_vusb_supply,
+};
+
+/* clk32kg is a twl6030 32khz clock modeled as a regulator, used by GPS */
+static struct regulator_init_data tuna_clk32kg = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .always_on = true,
+ },
+};
+
+static struct regulator_consumer_supply tuna_clk32kaudio_supply[] = {
+ {
+ .supply = "clk32kaudio",
+ },
+ {
+ .supply = "twl6040_clk32k",
+ }
+};
+
+static struct regulator_init_data tuna_clk32kaudio = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .boot_on = true,
+ },
+ .num_consumer_supplies = ARRAY_SIZE(tuna_clk32kaudio_supply),
+ .consumer_supplies = tuna_clk32kaudio_supply,
+};
+
+
+static struct regulator_init_data tuna_vdd3 = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .state_mem = {
+ .disabled = true,
+ },
+ .initial_state = PM_SUSPEND_MEM,
+ },
+};
+
+/*
+ * VMEM is unused. Register it to regulator framework and let it
+ * be in disabled state.
+ */
+static struct regulator_init_data tuna_vmem = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ .state_mem = {
+ .disabled = true,
+ },
+ .initial_state = PM_SUSPEND_MEM,
+ },
+};
+
+static struct regulator_init_data tuna_v2v1 = {
+ .constraints = {
+ .min_uV = 2100000,
+ .max_uV = 2100000,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ .always_on = true,
+ },
+};
+
+static struct twl4030_codec_audio_data twl6040_audio = {
+ /* single-step ramp for headset and handsfree */
+ .hs_left_step = 0x0f,
+ .hs_right_step = 0x0f,
+ .hf_left_step = 0x1d,
+ .hf_right_step = 0x1d,
+ .ep_step = 0x0f,
+};
+
+static struct regulator *twl6040_clk32kreg;
+
+static int tuna_twl6040_get_ext_clk32k(void)
+{
+ int ret = 0;
+
+ twl6040_clk32kreg = regulator_get(NULL, "twl6040_clk32k");
+ if (IS_ERR(twl6040_clk32kreg)) {
+ ret = PTR_ERR(twl6040_clk32kreg);
+ pr_err("failed to get CLK32K %d\n", ret);
+ }
+
+ return ret;
+}
+
+static void tuna_twl6040_put_ext_clk32k(void)
+{
+ regulator_put(twl6040_clk32kreg);
+}
+
+static int tuna_twl6040_set_ext_clk32k(bool on)
+{
+ int ret;
+
+ if (IS_ERR_OR_NULL(twl6040_clk32kreg))
+ return -EINVAL;
+
+ if (on)
+ ret = regulator_enable(twl6040_clk32kreg);
+ else
+ ret = regulator_disable(twl6040_clk32kreg);
+
+ if (ret)
+ pr_err("failed to enable TWL6040 CLK32K %d\n", ret);
+
+ return ret;
+}
+
+static struct twl4030_codec_data twl6040_codec = {
+ .audio = &twl6040_audio,
+ .naudint_irq = OMAP44XX_IRQ_SYS_2N,
+ .irq_base = TWL6040_CODEC_IRQ_BASE,
+ .get_ext_clk32k = tuna_twl6040_get_ext_clk32k,
+ .put_ext_clk32k = tuna_twl6040_put_ext_clk32k,
+ .set_ext_clk32k = tuna_twl6040_set_ext_clk32k,
+};
+
+static struct twl4030_platform_data tuna_twldata = {
+ .irq_base = TWL6030_IRQ_BASE,
+ .irq_end = TWL6030_IRQ_END,
+
+ /* Regulators */
+ .vmmc = &tuna_vmmc,
+ .vpp = &tuna_vpp,
+ .vusim = &tuna_vusim,
+ .vana = &tuna_vana,
+ .vcxio = &tuna_vcxio,
+ .vdac = &tuna_vdac,
+ .vusb = &tuna_vusb,
+ .vaux1 = &tuna_vaux1,
+ .vaux2 = &tuna_vaux2,
+ .vaux3 = &tuna_vaux3,
+ .clk32kg = &tuna_clk32kg,
+ .clk32kaudio = &tuna_clk32kaudio,
+
+ /* children */
+ .codec = &twl6040_codec,
+ .madc = &twl6030_madc,
+
+ /* SMPS */
+ .vdd3 = &tuna_vdd3,
+ .vmem = &tuna_vmem,
+ .v2v1 = &tuna_v2v1,
+};
+
+static void tuna_audio_init(void)
+{
+ unsigned int aud_pwron;
+
+ /* twl6040 naudint */
+ omap_mux_init_signal("sys_nirq2.sys_nirq2", \
+ OMAP_PIN_INPUT_PULLUP);
+
+ /* aud_pwron */
+ if (omap4_tuna_get_type() == TUNA_TYPE_TORO &&
+ omap4_tuna_get_revision() >= 1)
+ aud_pwron = GPIO_AUD_PWRON_TORO_V1;
+ else
+ aud_pwron = GPIO_AUD_PWRON;
+ omap_mux_init_gpio(aud_pwron, OMAP_PIN_OUTPUT);
+ twl6040_codec.audpwron_gpio = aud_pwron;
+
+ omap_mux_init_signal("gpmc_a24.gpio_48", OMAP_PIN_OUTPUT | OMAP_MUX_MODE3);
+ omap_mux_init_signal("kpd_col3.gpio_171", OMAP_PIN_OUTPUT | OMAP_MUX_MODE3);
+}
+
+static struct i2c_board_info __initdata tuna_i2c1_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("twl6030", 0x48),
+ .flags = I2C_CLIENT_WAKE,
+ .irq = OMAP44XX_IRQ_SYS_1N,
+ .platform_data = &tuna_twldata,
+ },
+};
+
+static struct i2c_board_info __initdata tuna_i2c2_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("ducati", 0x20),
+ .irq = OMAP44XX_IRQ_I2C2,
+ .ext_master = true,
+ },
+};
+
+static struct i2c_board_info __initdata tuna_i2c4_boardinfo[] = {
+ {
+ I2C_BOARD_INFO("an30259a", 0x30),
+ },
+};
+
+static int __init tuna_i2c_init(void)
+{
+ u32 r;
+
+ omap_mux_init_signal("sys_nirq1", OMAP_PIN_INPUT_PULLUP |
+ OMAP_WAKEUP_EN);
+ omap_mux_init_signal("i2c1_scl.i2c1_scl", OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_signal("i2c1_sda.i2c1_sda", OMAP_PIN_INPUT_PULLUP);
+
+ /*
+ * This will allow unused regulator to be shutdown. This flag
+ * should be set in the board file. Before regulators are registered.
+ */
+ regulator_has_full_constraints();
+
+ /*
+ * Phoenix Audio IC needs I2C1 to
+ * start with 400 KHz or less
+ */
+ omap_register_i2c_bus(1, 400, tuna_i2c1_boardinfo,
+ ARRAY_SIZE(tuna_i2c1_boardinfo));
+ omap_register_i2c_bus(2, 400, tuna_i2c2_boardinfo,
+ ARRAY_SIZE(tuna_i2c2_boardinfo));
+ omap_register_i2c_bus(3, 400, NULL, 0);
+
+ /* Disable internal pullup on i2c.4 line:
+ * as external 2.2K is already present
+ */
+ r = omap4_ctrl_pad_readl(OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_I2C_0);
+ r |= (1 << OMAP4_I2C4_SDA_PULLUPRESX_SHIFT);
+ omap4_ctrl_pad_writel(r, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_I2C_0);
+
+ omap_register_i2c_bus(4, 400, NULL, 0);
+
+ /*
+ * Drive MSECURE high for TWL6030 write access.
+ */
+ omap_mux_init_signal("fref_clk0_out.gpio_wk6", OMAP_PIN_OUTPUT);
+ gpio_request(6, "msecure");
+ gpio_direction_output(6, 1);
+
+ return 0;
+}
+
+#ifdef CONFIG_OMAP_MUX
+static struct omap_board_mux board_mux[] __initdata = {
+ /* camera gpios */
+ OMAP4_MUX(MCSPI1_SOMI,
+ OMAP_MUX_MODE3 | OMAP_PIN_INPUT_PULLDOWN), /* gpio_135 */
+ OMAP4_MUX(KPD_COL0,
+ OMAP_MUX_MODE3 | OMAP_PIN_INPUT_PULLDOWN), /* gpio_173 */
+ OMAP4_MUX(GPMC_A19,
+ OMAP_MUX_MODE3 | OMAP_PIN_INPUT_PULLDOWN), /* gpio_43 */
+ /* hwrev */
+ OMAP4_MUX(CSI21_DY4, OMAP_MUX_MODE3 | OMAP_PIN_INPUT),
+ OMAP4_MUX(CSI21_DX4, OMAP_MUX_MODE3 | OMAP_PIN_INPUT),
+ OMAP4_MUX(CSI21_DY3, OMAP_MUX_MODE3 | OMAP_PIN_INPUT),
+ OMAP4_MUX(CSI21_DX3, OMAP_MUX_MODE3 | OMAP_PIN_INPUT),
+ OMAP4_MUX(USBB2_HSIC_STROBE, OMAP_MUX_MODE3 | OMAP_PIN_INPUT),
+ /* fRom */
+ OMAP4_MUX(USBB2_ULPITLL_DAT4,
+ OMAP_MUX_MODE4 | OMAP_PIN_INPUT), /* mcpsi3_somi */
+ OMAP4_MUX(USBB2_ULPITLL_DAT5,
+ OMAP_MUX_MODE4 | OMAP_PIN_INPUT_PULLDOWN), /* mcpsi3_cs0 */
+ OMAP4_MUX(USBB2_ULPITLL_DAT6,
+ OMAP_MUX_MODE4 | OMAP_PIN_INPUT), /* mcpsi3_simo */
+ OMAP4_MUX(USBB2_ULPITLL_DAT7,
+ OMAP_MUX_MODE4 | OMAP_PIN_INPUT), /* mcpsi3_clk */
+ { .reg_offset = OMAP_MUX_TERMINATOR },
+};
+
+static struct omap_board_mux board_wkup_mux[] __initdata = {
+ /* power button */
+ OMAP4_MUX(SIM_CD, OMAP_MUX_MODE3 | OMAP_PIN_INPUT),
+ { .reg_offset = OMAP_MUX_TERMINATOR },
+};
+
+#else
+#define board_mux NULL
+#define board_wkup_mux NULL
+#endif
+
+/* sample4+ adds gps rts/cts lines */
+static struct omap_device_pad tuna_uart1_pads_sample4[] __initdata = {
+ {
+ .name = "mcspi1_cs3.uart1_rts",
+ .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE1,
+ },
+ {
+ .name = "mcspi1_cs2.uart1_cts",
+ .enable = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE1,
+ },
+ {
+ .name = "uart3_cts_rctx.uart1_tx",
+ .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE1,
+ },
+ {
+ .name = "mcspi1_cs1.uart1_rx",
+ .flags = OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP,
+ .enable = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE1,
+ .idle = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE1,
+ },
+};
+
+static struct omap_device_pad tuna_uart1_pads[] __initdata = {
+ {
+ .name = "uart3_cts_rctx.uart1_tx",
+ .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE1,
+ },
+ {
+ .name = "mcspi1_cs1.uart1_rx",
+ .flags = OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP,
+ .enable = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE1,
+ .idle = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE1,
+ },
+};
+
+static struct omap_device_pad tuna_uart2_pads[] __initdata = {
+ {
+ .name = "uart2_cts.uart2_cts",
+ .enable = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0,
+ },
+ {
+ .name = "uart2_rts.uart2_rts",
+ .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE0,
+ },
+ {
+ .name = "uart2_tx.uart2_tx",
+ .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE0,
+ },
+ {
+ .name = "uart2_rx.uart2_rx",
+ .enable = OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0,
+ },
+};
+
+static struct omap_device_pad tuna_uart3_pads[] __initdata = {
+ {
+ .name = "uart3_tx_irtx.uart3_tx_irtx",
+ .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE0,
+ },
+ {
+ .name = "uart3_rx_irrx.uart3_rx_irrx",
+ .flags = OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP,
+ .enable = OMAP_PIN_INPUT | OMAP_MUX_MODE0,
+ .idle = OMAP_PIN_INPUT | OMAP_MUX_MODE0,
+ },
+};
+
+static struct omap_device_pad tuna_uart4_pads[] __initdata = {
+ {
+ .name = "uart4_tx.uart4_tx",
+ .enable = OMAP_PIN_OUTPUT | OMAP_MUX_MODE0,
+ },
+ {
+ .name = "uart4_rx.uart4_rx",
+ .enable = OMAP_PIN_INPUT | OMAP_MUX_MODE0,
+ },
+};
+
+static struct omap_uart_port_info tuna_uart2_info __initdata = {
+ .use_dma = 0,
+ .dma_rx_buf_size = DEFAULT_RXDMA_BUFSIZE,
+ .dma_rx_poll_rate = DEFAULT_RXDMA_POLLRATE,
+ .dma_rx_timeout = DEFAULT_RXDMA_TIMEOUT,
+ .auto_sus_timeout = 0,
+ .wake_peer = bcm_bt_lpm_exit_lpm_locked,
+ .rts_mux_driver_control = 1,
+};
+
+static inline void __init board_serial_init(void)
+{
+ struct omap_device_pad *uart1_pads;
+ int uart1_pads_sz;
+
+ if (omap4_tuna_get_revision() >= TUNA_REV_SAMPLE_4) {
+ uart1_pads = tuna_uart1_pads_sample4;
+ uart1_pads_sz = ARRAY_SIZE(tuna_uart1_pads_sample4);
+ } else {
+ uart1_pads = tuna_uart1_pads;
+ uart1_pads_sz = ARRAY_SIZE(tuna_uart1_pads);
+ }
+
+ omap_serial_init_port_pads(0, uart1_pads, uart1_pads_sz, NULL);
+ omap_serial_init_port_pads(1, tuna_uart2_pads,
+ ARRAY_SIZE(tuna_uart2_pads), &tuna_uart2_info);
+ omap_serial_init_port_pads(3, tuna_uart4_pads,
+ ARRAY_SIZE(tuna_uart4_pads), NULL);
+}
+
+/* fiq_debugger initializes really early but OMAP resource mgmt
+ * is not yet ready @ arch_init, so init the serial debugger later */
+static int __init board_serial_debug_init(void)
+{
+ return omap_serial_debug_init(2, false, true,
+ tuna_uart3_pads, ARRAY_SIZE(tuna_uart3_pads));
+}
+device_initcall(board_serial_debug_init);
+
+/* SPI flash memory in camera module */
+#define F_ROM_SPI_BUS_NUM 3
+#define F_ROM_SPI_CS 0
+#define F_ROM_SPI_SPEED_HZ 24000000
+
+static const struct flash_platform_data w25q80_pdata = {
+ .name = "w25q80",
+ .type = "w25q80",
+};
+
+static struct omap2_mcspi_device_config f_rom_mcspi_config = {
+ .turbo_mode = 0,
+ .single_channel = 1, /* 0: slave, 1: master */
+ .swap_datalines = 1,
+};
+
+static struct spi_board_info tuna_f_rom[] __initdata = {
+ {
+ .modalias = "m25p80",
+ .controller_data = &f_rom_mcspi_config,
+ .platform_data = &w25q80_pdata,
+ .bus_num = F_ROM_SPI_BUS_NUM,
+ .chip_select = F_ROM_SPI_CS,
+ .max_speed_hz = F_ROM_SPI_SPEED_HZ,
+ .mode = SPI_MODE_0,
+ },
+};
+
+static void tuna_from_init(void)
+{
+ int err;
+
+ if (tuna_hw_rev >= 0x07)
+ f_rom_mcspi_config.swap_datalines = 0;
+
+ err = spi_register_board_info(tuna_f_rom, ARRAY_SIZE(tuna_f_rom));
+ if (err)
+ pr_err("failed to register SPI F-ROM\n");
+}
+
+/*SPI for LTE modem bootloader*/
+#define LTE_MODEM_SPI_BUS_NUM 4
+#define LTE_MODEM_SPI_CS 0
+#define LTE_MODEM_SPI_MAX_HZ 1500000
+
+struct lte_modem_bootloader_platform_data lte_modem_bootloader_pdata = {
+ .name = "lte_modem_int",
+ .gpio_lte2ap_status = OMAP_GPIO_CMC2AP_INT1,
+};
+
+static struct omap2_mcspi_device_config lte_mcspi_config = {
+ .turbo_mode = 0,
+ .single_channel = 1, /* 0: slave, 1: master */
+};
+
+static struct spi_board_info tuna_lte_modem[] __initdata = {
+ {
+ .modalias = "lte_modem_spi",
+ .controller_data = &lte_mcspi_config,
+ .platform_data = &lte_modem_bootloader_pdata,
+ .max_speed_hz = LTE_MODEM_SPI_MAX_HZ,
+ .bus_num = LTE_MODEM_SPI_BUS_NUM,
+ .chip_select = LTE_MODEM_SPI_CS,
+ .mode = SPI_MODE_0,
+ },
+};
+
+static int tuna_notifier_call(struct notifier_block *this,
+ unsigned long code, void *_cmd)
+{
+ void __iomem *sar_base;
+ unsigned int flag = REBOOT_FLAG_NORMAL;
+
+ sar_base = omap4_get_sar_ram_base();
+
+ if (!sar_base)
+ return notifier_from_errno(-ENOMEM);
+
+ if (code == SYS_RESTART) {
+ if (_cmd) {
+ if (!strcmp(_cmd, "recovery"))
+ flag = REBOOT_FLAG_RECOVERY;
+ else if (!strcmp(_cmd, "bootloader"))
+ flag = REBOOT_FLAG_FASTBOOT;
+ }
+ } else if (code == SYS_POWER_OFF) {
+ flag = REBOOT_FLAG_POWER_OFF;
+ }
+
+ /* The Samsung LOKE bootloader will look for the boot flag at a fixed
+ * offset from the end of the 1st SAR bank.
+ */
+ writel(flag, sar_base + SAR_BANK2_OFFSET - 0xC);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block tuna_reboot_notifier = {
+ .notifier_call = tuna_notifier_call,
+};
+
+static ssize_t tuna_soc_family_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "OMAP%04x\n", GET_OMAP_TYPE);
+}
+
+static ssize_t tuna_soc_revision_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "ES%d.%d\n", (GET_OMAP_REVISION() >> 4) & 0xf,
+ GET_OMAP_REVISION() & 0xf);
+}
+
+static ssize_t tuna_soc_die_id_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct omap_die_id oid;
+ omap_get_die_id(&oid);
+ return sprintf(buf, "%08X-%08X-%08X-%08X\n", oid.id_3, oid.id_2,
+ oid.id_1, oid.id_0);
+}
+
+static ssize_t tuna_soc_prod_id_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ struct omap_die_id oid;
+ omap_get_production_id(&oid);
+ return sprintf(buf, "%08X-%08X\n", oid.id_1, oid.id_0);
+}
+
+static ssize_t tuna_soc_msv_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%08X\n", omap_ctrl_readl(0x013c));
+}
+
+static const char *omap_types[] = {
+ [OMAP2_DEVICE_TYPE_TEST] = "TST",
+ [OMAP2_DEVICE_TYPE_EMU] = "EMU",
+ [OMAP2_DEVICE_TYPE_SEC] = "HS",
+ [OMAP2_DEVICE_TYPE_GP] = "GP",
+ [OMAP2_DEVICE_TYPE_BAD] = "BAD",
+};
+
+static ssize_t tuna_soc_type_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", omap_types[omap_type()]);
+}
+
+#define TUNA_ATTR_RO(_type, _name, _show) \
+ struct kobj_attribute tuna_##_type##_prop_attr_##_name = \
+ __ATTR(_name, S_IRUGO, _show, NULL)
+
+static TUNA_ATTR_RO(soc, family, tuna_soc_family_show);
+static TUNA_ATTR_RO(soc, revision, tuna_soc_revision_show);
+static TUNA_ATTR_RO(soc, type, tuna_soc_type_show);
+static TUNA_ATTR_RO(soc, die_id, tuna_soc_die_id_show);
+static TUNA_ATTR_RO(soc, production_id, tuna_soc_prod_id_show);
+static TUNA_ATTR_RO(soc, msv, tuna_soc_msv_show);
+
+static struct attribute *tuna_soc_prop_attrs[] = {
+ &tuna_soc_prop_attr_family.attr,
+ &tuna_soc_prop_attr_revision.attr,
+ &tuna_soc_prop_attr_type.attr,
+ &tuna_soc_prop_attr_die_id.attr,
+ &tuna_soc_prop_attr_production_id.attr,
+ &tuna_soc_prop_attr_msv.attr,
+ NULL,
+};
+
+static struct attribute_group tuna_soc_prop_attr_group = {
+ .attrs = tuna_soc_prop_attrs,
+};
+
+static ssize_t tuna_board_revision_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s (0x%02x)\n", omap4_tuna_hw_rev_name(),
+ tuna_hw_rev);
+}
+
+static TUNA_ATTR_RO(board, revision, tuna_board_revision_show);
+static struct attribute *tuna_board_prop_attrs[] = {
+ &tuna_board_prop_attr_revision.attr,
+ NULL,
+};
+
+static struct attribute_group tuna_board_prop_attr_group = {
+ .attrs = tuna_board_prop_attrs,
+};
+
+static void __init omap4_tuna_create_board_props(void)
+{
+ struct kobject *board_props_kobj;
+ struct kobject *soc_kobj;
+ int ret = 0;
+
+ board_props_kobj = kobject_create_and_add("board_properties", NULL);
+ if (!board_props_kobj)
+ goto err_board_obj;
+
+ soc_kobj = kobject_create_and_add("soc", board_props_kobj);
+ if (!soc_kobj)
+ goto err_soc_obj;
+
+ ret = sysfs_create_group(board_props_kobj, &tuna_board_prop_attr_group);
+ if (ret)
+ goto err_board_sysfs_create;
+
+ ret = sysfs_create_group(soc_kobj, &tuna_soc_prop_attr_group);
+ if (ret)
+ goto err_soc_sysfs_create;
+
+ return;
+
+err_soc_sysfs_create:
+ sysfs_remove_group(board_props_kobj, &tuna_board_prop_attr_group);
+err_board_sysfs_create:
+ kobject_put(soc_kobj);
+err_soc_obj:
+ kobject_put(board_props_kobj);
+err_board_obj:
+ if (!board_props_kobj || !soc_kobj || ret)
+ pr_err("failed to create board_properties\n");
+}
+
+#define HSMMC2_MUX (OMAP_MUX_MODE1 | OMAP_PIN_INPUT_PULLUP)
+#define HSMMC1_MUX OMAP_PIN_INPUT_PULLUP
+
+static void __init omap4_tuna_led_init(void)
+{
+ i2c_register_board_info(4, tuna_i2c4_boardinfo,
+ ARRAY_SIZE(tuna_i2c4_boardinfo));
+}
+
+/* always reboot into charger mode on "power-off". Let the bootloader
+ * figure out if we should truly power-off or not.
+ */
+static void tuna_power_off(void)
+{
+ printk(KERN_EMERG "Rebooting into bootloader for power-off.\n");
+ arm_pm_restart('c', NULL);
+}
+
+static void __init tuna_init(void)
+{
+ int package = OMAP_PACKAGE_CBS;
+
+ if (omap_rev() == OMAP4430_REV_ES1_0)
+ package = OMAP_PACKAGE_CBL;
+ omap4_mux_init(board_mux, board_wkup_mux, package);
+
+ omap4_tuna_init_hw_rev();
+
+ omap4_tuna_emif_init();
+
+ pm_power_off = tuna_power_off;
+
+ register_reboot_notifier(&tuna_reboot_notifier);
+
+ /* hsmmc d0-d7 */
+ omap_mux_init_signal("sdmmc1_dat0.sdmmc1_dat0", HSMMC1_MUX);
+ omap_mux_init_signal("sdmmc1_dat1.sdmmc1_dat1", HSMMC1_MUX);
+ omap_mux_init_signal("sdmmc1_dat2.sdmmc1_dat2", HSMMC1_MUX);
+ omap_mux_init_signal("sdmmc1_dat3.sdmmc1_dat3", HSMMC1_MUX);
+ omap_mux_init_signal("sdmmc1_dat4.sdmmc1_dat4", HSMMC1_MUX);
+ omap_mux_init_signal("sdmmc1_dat5.sdmmc1_dat5", HSMMC1_MUX);
+ omap_mux_init_signal("sdmmc1_dat6.sdmmc1_dat6", HSMMC1_MUX);
+ omap_mux_init_signal("sdmmc1_dat7.sdmmc1_dat7", HSMMC1_MUX);
+ /* hsmmc cmd */
+ omap_mux_init_signal("sdmmc1_cmd.sdmmc1_cmd", HSMMC1_MUX);
+ /* hsmmc clk */
+ omap_mux_init_signal("sdmmc1_clk.sdmmc1_clk", HSMMC1_MUX);
+
+ gpio_request(158, "emmc_en");
+ gpio_direction_output(158, 1);
+ omap_mux_init_gpio(158, OMAP_PIN_INPUT_PULLUP);
+
+ omap_mux_init_gpio(GPIO_MHL_SDA_18V, OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_gpio(GPIO_MHL_SCL_18V, OMAP_PIN_INPUT_PULLUP);
+
+ sec_common_init();
+
+ if (TUNA_TYPE_TORO == omap4_tuna_get_type()) {
+ omap_mux_init_signal("gpmc_wait0",
+ OMAP_MUX_MODE3 | OMAP_PIN_INPUT_PULLDOWN);
+ gpio_request(OMAP_GPIO_CMC2AP_INT1, "gpio_61");
+ gpio_direction_input(OMAP_GPIO_CMC2AP_INT1);
+
+ omap_mux_init_signal("mcspi4_clk", OMAP_MUX_MODE0);
+ omap_mux_init_signal("mcspi4_simo", OMAP_MUX_MODE0);
+ omap_mux_init_signal("mcspi4_somi", OMAP_MUX_MODE0);
+ omap_mux_init_signal("mcspi4_cs0", OMAP_MUX_MODE0);
+ }
+
+ tuna_wlan_init();
+ tuna_audio_init();
+ tuna_i2c_init();
+ tuna_gsd4t_gps_init();
+ ramconsole_pdata.bootinfo = omap4_get_resetreason();
+ platform_add_devices(tuna_devices, ARRAY_SIZE(tuna_devices));
+ board_serial_init();
+ tuna_bt_init();
+ omap2_hsmmc_init(mmc);
+ usb_musb_init(&musb_board_data);
+ omap4_tuna_create_board_props();
+ if (TUNA_TYPE_TORO == omap4_tuna_get_type()) {
+ spi_register_board_info(tuna_lte_modem,
+ ARRAY_SIZE(tuna_lte_modem));
+ }
+ tuna_from_init();
+ omap_dmm_init();
+ omap4_tuna_display_init();
+ omap4_tuna_input_init();
+ omap4_tuna_nfc_init();
+ omap4_tuna_power_init();
+ omap4_tuna_jack_init();
+ omap4_tuna_sensors_init();
+ omap4_tuna_led_init();
+ omap4_tuna_connector_init();
+ omap4_tuna_pogo_init();
+#ifdef CONFIG_OMAP_HSI_DEVICE
+ if (TUNA_TYPE_MAGURO == omap4_tuna_get_type())
+ omap_hsi_init();
+#endif
+#ifdef CONFIG_USB_EHCI_HCD_OMAP
+ if (TUNA_TYPE_TORO == omap4_tuna_get_type()) {
+#ifdef CONFIG_SEC_MODEM
+ modem_toro_init();
+#endif
+ omap4_ehci_init();
+ }
+#endif
+}
+
+static void __init tuna_map_io(void)
+{
+ omap2_set_globals_443x();
+ omap44xx_map_common_io();
+}
+
+static void __init tuna_reserve(void)
+{
+ int i;
+ int ret;
+
+ /* do the static reservations first */
+ memblock_remove(TUNA_RAMCONSOLE_START, TUNA_RAMCONSOLE_SIZE);
+ memblock_remove(PHYS_ADDR_SMC_MEM, PHYS_ADDR_SMC_SIZE);
+ memblock_remove(PHYS_ADDR_DUCATI_MEM, PHYS_ADDR_DUCATI_SIZE);
+
+ for (i = 0; i < tuna_ion_data.nr; i++)
+ if (tuna_ion_data.heaps[i].type == ION_HEAP_TYPE_CARVEOUT ||
+ tuna_ion_data.heaps[i].type == OMAP_ION_HEAP_TYPE_TILER) {
+ ret = memblock_remove(tuna_ion_data.heaps[i].base,
+ tuna_ion_data.heaps[i].size);
+ if (ret)
+ pr_err("memblock remove of %x@%lx failed\n",
+ tuna_ion_data.heaps[i].size,
+ tuna_ion_data.heaps[i].base);
+ }
+
+ /* ipu needs to recognize secure input buffer area as well */
+ omap_ipu_set_static_mempool(PHYS_ADDR_DUCATI_MEM, PHYS_ADDR_DUCATI_SIZE +
+ OMAP_TUNA_ION_HEAP_SECURE_INPUT_SIZE);
+ omap_reserve();
+}
+
+MACHINE_START(TUNA, "Tuna")
+ /* Maintainer: Google, Inc */
+ .boot_params = 0x80000100,
+ .reserve = tuna_reserve,
+ .map_io = tuna_map_io,
+ .init_early = tuna_init_early,
+ .init_irq = gic_init_irq,
+ .init_machine = tuna_init,
+ .timer = &omap_timer,
+MACHINE_END
diff --git a/arch/arm/mach-omap2/board-tuna.h b/arch/arm/mach-omap2/board-tuna.h
new file mode 100644
index 0000000..619da15
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 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 _MACH_OMAP2_BOARD_TUNA_H_
+#define _MACH_OMAP2_BOARD_TUNA_H_
+
+#define TUNA_REV_MASK 0xf
+#define TUNA_REV_03 0x3
+#define TUNA_REV_SAMPLE_4 0x3
+
+#define TUNA_TYPE_TORO 0x10
+#define TUNA_TYPE_MAGURO 0x00
+#define TUNA_TYPE_MASK 0x10
+
+#define TUNA_GPIO_HDMI_HPD 63
+
+#define TUNA_OTG_ID_POGO_PRIO INT_MIN
+#define TUNA_OTG_ID_FSA9480_PRIO (INT_MIN + 1)
+#define TUNA_OTG_ID_SII9234_PRIO (INT_MIN + 2)
+#define TUNA_OTG_ID_FSA9480_LAST_PRIO INT_MAX
+
+int omap4_tuna_get_revision(void);
+int omap4_tuna_get_type(void);
+bool omap4_tuna_final_gpios(void);
+void omap4_tuna_display_init(void);
+void omap4_tuna_input_init(void);
+void omap4_tuna_jack_init(void);
+void omap4_tuna_nfc_init(void);
+void omap4_tuna_power_init(void);
+void omap4_tuna_sensors_init(void);
+void omap4_tuna_pogo_init(void);
+int omap4_tuna_connector_init(void);
+int tuna_wlan_init(void);
+int omap_hsi_init(void);
+void omap4_tuna_emif_init(void);
+void omap4_ehci_init(void);
+void modem_toro_init(void);
+
+enum pogo_power_state {
+ POGO_POWER_DISCONNECTED,
+ POGO_POWER_CHARGER,
+ POGO_POWER_HOST,
+};
+void tuna_otg_pogo_charger(enum pogo_power_state);
+void tuna_otg_set_dock_switch(int enable);
+
+extern struct mmc_platform_data tuna_wifi_data;
+extern struct class *sec_class;
+
+#endif
diff --git a/arch/arm/mach-omap2/dpll3xxx.c b/arch/arm/mach-omap2/dpll3xxx.c
index 7fb0d21..ff4a10b 100644
--- a/arch/arm/mach-omap2/dpll3xxx.c
+++ b/arch/arm/mach-omap2/dpll3xxx.c
@@ -96,7 +96,6 @@ retry:
/* Try Error Recovery: for failing usbdpll locking */
if (!strcmp(clk->name, "dpll_usb_ck")) {
-
reg = __raw_readl(dd->mult_div1_reg);
/* Put in MN bypass */
@@ -132,6 +131,17 @@ retry:
first_time = false;
goto retry;
}
+
+ pr_info("\n========== USB DPLL DUMP ===========\n");
+ pr_info("CM_CLKMODE_DPLL_USB :%08x\n", omap_readl(0x4A008180));
+ pr_info("CM_IDLEST_DPLL_USB :%08x\n", omap_readl(0x4A008184));
+ pr_info("CM_AUTOIDLE_DPLL_USB :%08x\n", omap_readl(0x4A008188));
+ pr_info("CM_CLKSEL_DPLL_USB :%08x\n", omap_readl(0x4A00818C));
+ pr_info("CM_DIV_M2_DPLL_USB :%08x\n", omap_readl(0x4A008190));
+ pr_info("CM_SSC_DELTAMSTEP_DPLL_USB :%08x\n", omap_readl(0x4A0081A8));
+ pr_info("CM_SSC_MODFREQDIV_DPLL_USB :%08x\n", omap_readl(0x4A0081AC));
+ pr_info("CM_CLKDCOLDO_DPLL_USB :%08x\n", omap_readl(0x4A0081B4));
+ pr_info("========== USB DPLL DUMP: End ===========\n");
}
} else {
pr_debug("clock: %s transition to '%s' in %d loops\n",
diff --git a/arch/arm/mach-omap2/dpll44xx.c b/arch/arm/mach-omap2/dpll44xx.c
index bc6d418..7e509e7 100644
--- a/arch/arm/mach-omap2/dpll44xx.c
+++ b/arch/arm/mach-omap2/dpll44xx.c
@@ -175,6 +175,12 @@ int omap4_prcm_freq_update(void)
if (i == MAX_FREQ_UPDATE_TIMEOUT) {
pr_err("%s: Frequency update failed (call from %pF)\n",
__func__, (void *)_RET_IP_);
+ pr_err("CLKCTRL: EMIF_1=0x%x EMIF_2=0x%x DMM=0x%x\n",
+ __raw_readl(OMAP4430_CM_MEMIF_EMIF_1_CLKCTRL),
+ __raw_readl(OMAP4430_CM_MEMIF_EMIF_2_CLKCTRL),
+ __raw_readl(OMAP4430_CM_MEMIF_DMM_CLKCTRL));
+ emif_dump(0);
+ emif_dump(1);
return -1;
}
diff --git a/arch/arm/mach-omap2/emif.c b/arch/arm/mach-omap2/emif.c
index 50f1128..968bcde 100644
--- a/arch/arm/mach-omap2/emif.c
+++ b/arch/arm/mach-omap2/emif.c
@@ -60,6 +60,20 @@ static struct omap_device_pm_latency omap_emif_latency[] = {
},
};
+static u32 get_temperature_level(u32 emif_nr);
+
+void emif_dump(int emif_nr)
+{
+ void __iomem *base = emif[emif_nr].base;
+
+ printk("EMIF%d s=0x%x is_sys=0x%x is_ll=0x%x temp=0x%02x\n",
+ emif_nr + 1,
+ __raw_readl(base + OMAP44XX_EMIF_STATUS),
+ __raw_readl(base + OMAP44XX_EMIF_IRQSTATUS_SYS),
+ __raw_readl(base + OMAP44XX_EMIF_IRQSTATUS_LL),
+ get_temperature_level(emif_nr));
+}
+
static void do_cancel_out(u32 *num, u32 *den, u32 factor)
{
while (1) {
diff --git a/arch/arm/mach-omap2/include/mach/emif.h b/arch/arm/mach-omap2/include/mach/emif.h
index 3a65b20..99a7c69 100644
--- a/arch/arm/mach-omap2/include/mach/emif.h
+++ b/arch/arm/mach-omap2/include/mach/emif.h
@@ -263,4 +263,5 @@ int omap_emif_setup_device_details(
const struct emif_device_details *emif2_devices);
void emif_clear_irq(int emif_id);
+void emif_dump(int emif_nr);
#endif
diff --git a/arch/arm/mach-omap2/include/mach/tf_mshield.h b/arch/arm/mach-omap2/include/mach/tf_mshield.h
new file mode 100644
index 0000000..52f98bf
--- /dev/null
+++ b/arch/arm/mach-omap2/include/mach/tf_mshield.h
@@ -0,0 +1,11 @@
+/**
+ * Copyright (c) 2010 Trusted Logic S.A.
+ *
+ * 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.
+ */
+
+#ifdef CONFIG_SECURITY_MIDDLEWARE_COMPONENT
+void tf_allocate_workspace(void);
+#endif
diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c
index b5c8e80..6694dfb 100644
--- a/arch/arm/mach-omap2/io.c
+++ b/arch/arm/mach-omap2/io.c
@@ -44,6 +44,7 @@
#include "clockdomain.h"
#include <plat/omap_hwmod.h>
#include <plat/multi.h>
+#include <mach/tf_mshield.h>
/*
* The machine specific code may provide the extra mapping besides the
@@ -251,6 +252,9 @@ static void __init _omap2_map_common_io(void)
omap2_check_revision();
omap_sram_init();
+#ifdef CONFIG_SECURITY_MIDDLEWARE_COMPONENT
+ tf_allocate_workspace();
+#endif
}
#ifdef CONFIG_SOC_OMAP2420
diff --git a/arch/arm/mach-omap2/omap2plus-cpufreq.c b/arch/arm/mach-omap2/omap2plus-cpufreq.c
index 7c5a6f9..6c1261c 100644
--- a/arch/arm/mach-omap2/omap2plus-cpufreq.c
+++ b/arch/arm/mach-omap2/omap2plus-cpufreq.c
@@ -27,6 +27,7 @@
#include <linux/io.h>
#include <linux/opp.h>
#include <linux/cpu.h>
+#include <linux/earlysuspend.h>
#include <linux/platform_device.h>
#include <asm/system.h>
@@ -59,8 +60,10 @@ static struct device *mpu_dev;
static DEFINE_MUTEX(omap_cpufreq_lock);
static unsigned int max_thermal;
+static unsigned int max_capped;
static unsigned int max_freq;
static unsigned int current_target_freq;
+static unsigned int screen_off_max_freq;
static bool omap_cpufreq_ready;
static bool omap_cpufreq_suspended;
@@ -91,6 +94,9 @@ static int omap_cpufreq_scale(unsigned int target_freq, unsigned int cur_freq)
if (freqs.new > max_thermal)
freqs.new = max_thermal;
+ if (max_capped && freqs.new > max_capped)
+ freqs.new = max_capped;
+
if ((freqs.old == freqs.new) && (cur_freq = freqs.new))
return 0;
@@ -256,6 +262,46 @@ static int omap_target(struct cpufreq_policy *policy,
return ret;
}
+static void omap_cpu_early_suspend(struct early_suspend *h)
+{
+ unsigned int cur;
+
+ mutex_lock(&omap_cpufreq_lock);
+
+ if (screen_off_max_freq) {
+ max_capped = screen_off_max_freq;
+
+ cur = omap_getspeed(0);
+ if (cur > max_capped)
+ omap_cpufreq_scale(max_capped, cur);
+ }
+
+ mutex_unlock(&omap_cpufreq_lock);
+}
+
+static void omap_cpu_late_resume(struct early_suspend *h)
+{
+ unsigned int cur;
+
+ mutex_lock(&omap_cpufreq_lock);
+
+ if (max_capped) {
+ max_capped = 0;
+
+ cur = omap_getspeed(0);
+ if (cur != current_target_freq)
+ omap_cpufreq_scale(current_target_freq, cur);
+ }
+
+ mutex_unlock(&omap_cpufreq_lock);
+}
+
+static struct early_suspend omap_cpu_early_suspend_handler = {
+ .level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN,
+ .suspend = omap_cpu_early_suspend,
+ .resume = omap_cpu_late_resume,
+};
+
static inline void freq_table_free(void)
{
if (atomic_dec_and_test(&freq_table_users))
@@ -332,8 +378,52 @@ static int omap_cpu_exit(struct cpufreq_policy *policy)
return 0;
}
+static ssize_t show_screen_off_freq(struct cpufreq_policy *policy, char *buf)
+{
+ return sprintf(buf, "%u\n", screen_off_max_freq);
+}
+
+static ssize_t store_screen_off_freq(struct cpufreq_policy *policy,
+ const char *buf, size_t count)
+{
+ unsigned int freq = 0;
+ int ret;
+ int index;
+
+ if (!freq_table)
+ return -EINVAL;
+
+ ret = sscanf(buf, "%u", &freq);
+ if (ret != 1)
+ return -EINVAL;
+
+ mutex_lock(&omap_cpufreq_lock);
+
+ ret = cpufreq_frequency_table_target(policy, freq_table, freq,
+ CPUFREQ_RELATION_H, &index);
+ if (ret)
+ goto out;
+
+ screen_off_max_freq = freq_table[index].frequency;
+
+ ret = count;
+
+out:
+ mutex_unlock(&omap_cpufreq_lock);
+ return ret;
+}
+
+struct freq_attr omap_cpufreq_attr_screen_off_freq = {
+ .attr = { .name = "screen_off_max_freq",
+ .mode = 0644,
+ },
+ .show = show_screen_off_freq,
+ .store = store_screen_off_freq,
+};
+
static struct freq_attr *omap_cpufreq_attr[] = {
&cpufreq_freq_attr_scaling_available_freqs,
+ &omap_cpufreq_attr_screen_off_freq,
NULL,
};
@@ -407,6 +497,8 @@ static int __init omap_cpufreq_init(void)
return -EINVAL;
}
+ register_early_suspend(&omap_cpu_early_suspend_handler);
+
ret = cpufreq_register_driver(&omap_driver);
omap_cpufreq_ready = !ret;
@@ -429,6 +521,8 @@ static int __init omap_cpufreq_init(void)
static void __exit omap_cpufreq_exit(void)
{
cpufreq_unregister_driver(&omap_driver);
+
+ unregister_early_suspend(&omap_cpu_early_suspend_handler);
platform_driver_unregister(&omap_cpufreq_platform_driver);
platform_device_unregister(&omap_cpufreq_device);
}
diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c
index 7bf033c..488a73f 100644
--- a/arch/arm/mach-omap2/omap4-common.c
+++ b/arch/arm/mach-omap2/omap4-common.c
@@ -274,6 +274,7 @@ static int __init omap_barriers_init(void)
}
core_initcall(omap_barriers_init);
+#ifndef CONFIG_SECURITY_MIDDLEWARE_COMPONENT
/*
* omap4_sec_dispatcher: Routine to dispatch low power secure
* service routines
@@ -324,3 +325,4 @@ u32 omap4_secure_dispatcher(u32 idx, u32 flag, u32 nargs, u32 arg1, u32 arg2,
return ret;
}
+#endif
diff --git a/arch/arm/mach-omap2/omap_hsi.c b/arch/arm/mach-omap2/omap_hsi.c
index ce8fa54..3597e4c 100644..100755
--- a/arch/arm/mach-omap2/omap_hsi.c
+++ b/arch/arm/mach-omap2/omap_hsi.c
@@ -25,8 +25,6 @@
#include <linux/notifier.h>
#include <linux/hsi_driver_if.h>
-#include <asm/clkdev.h>
-
#include <plat/omap_hsi.h>
#include <plat/omap_hwmod.h>
#include <plat/omap_device.h>
@@ -35,6 +33,8 @@
#include "clock.h"
#include "mux.h"
#include "control.h"
+#include "pm.h"
+#include "dvfs.h"
static int omap_hsi_wakeup_enable(int hsi_port);
static int omap_hsi_wakeup_disable(int hsi_port);
@@ -42,8 +42,6 @@ static int omap_hsi_wakeup_disable(int hsi_port);
#define OMAP_HSI_PLATFORM_DEVICE_NAME "omap_hsi.0"
#define OMAP_HSI_HWMOD_NAME "hsi"
#define OMAP_HSI_HWMOD_CLASSNAME "hsi"
-#define OMAP_HSI_PADCONF_CAWAKE_PIN "usbb1_ulpitll_clk.hsi1_cawake"
-#define OMAP_HSI_PADCONF_CAWAKE_MODE OMAP_MUX_MODE1
#define OMAP_MUX_MODE_MASK 0x7
@@ -85,8 +83,9 @@ static int omap_mux_disable_wakeup(const char *muxname)
*/
-static struct port_ctx hsi_port_ctx[] = {
+static struct hsi_port_ctx omap_hsi_port_ctx[] = {
[0] = {
+ .port_number = 1,
.hst.mode = HSI_MODE_FRAME,
.hst.flow = HSI_FLOW_SYNCHRONIZED,
.hst.frame_size = HSI_FRAMESIZE_DEFAULT,
@@ -101,24 +100,28 @@ static struct port_ctx hsi_port_ctx[] = {
.hsr.counters = HSI_COUNTERS_FT_DEFAULT |
HSI_COUNTERS_TB_DEFAULT |
HSI_COUNTERS_FB_DEFAULT,
+ .cawake_padconf_name = "usbb1_ulpitll_clk.hsi1_cawake",
+ .cawake_padconf_hsi_mode = OMAP_MUX_MODE1,
},
};
-static struct ctrl_ctx hsi_ctx = {
+static struct hsi_ctrl_ctx omap_hsi_ctrl_ctx = {
.sysconfig = 0,
.gdd_gcr = 0,
.dll = 0,
- .pctx = hsi_port_ctx,
+ .pctx = omap_hsi_port_ctx,
};
static struct hsi_platform_data omap_hsi_platform_data = {
- .num_ports = ARRAY_SIZE(hsi_port_ctx),
+ .num_ports = ARRAY_SIZE(omap_hsi_port_ctx),
.hsi_gdd_chan_count = HSI_HSI_DMA_CHANNEL_MAX,
.default_hsi_fclk = HSI_DEFAULT_FCLK,
- .ctx = &hsi_ctx,
+ .fifo_mapping_strategy = HSI_FIFO_MAPPING_ALL_PORT1,
+ .ctx = &omap_hsi_ctrl_ctx,
.device_enable = omap_device_enable,
.device_idle = omap_device_idle,
.device_shutdown = omap_device_shutdown,
+ .device_scale = omap_device_scale,
.wakeup_enable = omap_hsi_wakeup_enable,
.wakeup_disable = omap_hsi_wakeup_disable,
.wakeup_is_from_hsi = omap_hsi_is_io_wakeup_from_hsi,
@@ -126,6 +129,19 @@ static struct hsi_platform_data omap_hsi_platform_data = {
};
+static u32 omap_hsi_configure_errata(void)
+{
+ u32 errata = 0;
+
+ if (cpu_is_omap44xx()) {
+ SET_HSI_ERRATA(errata, HSI_ERRATUM_i696_SW_RESET_FSM_STUCK);
+ SET_HSI_ERRATA(errata, HSI_ERRATUM_ixxx_3WIRES_NO_SWAKEUP);
+ SET_HSI_ERRATA(errata, HSI_ERRATUM_i702_PM_HSI_SWAKEUP);
+ }
+
+ return errata;
+}
+
static struct platform_device *hsi_get_hsi_platform_device(void)
{
struct device *dev;
@@ -167,23 +183,49 @@ static struct hsi_dev *hsi_get_hsi_controller_data(struct platform_device *pd)
}
/**
+* hsi_get_hsi_port_ctx_data - Returns a pointer on the port context
+*
+* @hsi_port - port number to obtain context. Range [1, 2]
+*
+* Return value :* If success: pointer on the HSI port context requested
+* * else NULL
+*/
+static struct hsi_port_ctx *hsi_get_hsi_port_ctx_data(int hsi_port)
+{
+ int i;
+
+ for (i = 0; i < omap_hsi_platform_data.num_ports; i++)
+ if (omap_hsi_platform_data.ctx->pctx[i].port_number == hsi_port)
+ return &omap_hsi_platform_data.ctx->pctx[i];
+
+ return NULL;
+}
+
+/**
* omap_hsi_is_io_pad_hsi - Indicates if IO Pad has been muxed for HSI CAWAKE
*
+* @hsi_port - port number to check for HSI muxing. Range [1, 2]
+*
* Return value :* 0 if CAWAKE Padconf has not been found or CAWAKE not muxed for
* CAWAKE
* * else 1
*/
-static int omap_hsi_is_io_pad_hsi(void)
+static int omap_hsi_is_io_pad_hsi(int hsi_port)
{
+ struct hsi_port_ctx *port_ctx;
u16 val;
+ port_ctx = hsi_get_hsi_port_ctx_data(hsi_port);
+ if (!port_ctx)
+ return 0;
+
/* Check for IO pad */
- val = omap_mux_read_signal(OMAP_HSI_PADCONF_CAWAKE_PIN);
+ val = omap_mux_read_signal(port_ctx->cawake_padconf_name);
if (val == -ENODEV)
return 0;
/* Continue only if CAWAKE is muxed */
- if ((val & OMAP_MUX_MODE_MASK) != OMAP_HSI_PADCONF_CAWAKE_MODE)
+ if ((val & OMAP_MUX_MODE_MASK) != port_ctx->cawake_padconf_hsi_mode)
return 0;
return 1;
@@ -192,49 +234,66 @@ static int omap_hsi_is_io_pad_hsi(void)
/**
* omap_hsi_is_io_wakeup_from_hsi - Indicates an IO wakeup from HSI CAWAKE
*
-* Return value :* 0 if CAWAKE Padconf has not been found or no IOWAKEUP event
-* occured for CAWAKE
-* * else 1
-* TODO : return value should indicate the HSI port which has awaken
+* @hsi_port - returns port number which triggered wakeup. Range [1, 2].
+* Only valid if return value is 1 (HSI wakeup detected)
+*
+* Return value :* false if CAWAKE Padconf has not been found or no IOWAKEUP event
+* occured for CAWAKE.
+* * true if HSI wakeup detected on port *hsi_port
*/
-int omap_hsi_is_io_wakeup_from_hsi(void)
+bool omap_hsi_is_io_wakeup_from_hsi(int *hsi_port)
{
+ struct hsi_port_ctx *port_ctx;
u16 val;
+ int i;
+
+ for (i = 0; i < omap_hsi_platform_data.num_ports; i++) {
+ port_ctx = &omap_hsi_platform_data.ctx->pctx[i];
/* Check for IO pad wakeup */
- val = omap_mux_read_signal(OMAP_HSI_PADCONF_CAWAKE_PIN);
+ val = omap_mux_read_signal(port_ctx->cawake_padconf_name);
if (val == -ENODEV)
- return 0;
+ continue;
/* Continue only if CAWAKE is muxed */
- if ((val & OMAP_MUX_MODE_MASK) != OMAP_HSI_PADCONF_CAWAKE_MODE)
- return 0;
+ if ((val & OMAP_MUX_MODE_MASK) !=
+ port_ctx->cawake_padconf_hsi_mode)
+ continue;
+
+ if (val & OMAP44XX_PADCONF_WAKEUPEVENT0) {
+ *hsi_port = port_ctx->port_number;
+ return true;
+ }
+ }
- if (val & OMAP44XX_PADCONF_WAKEUPEVENT0)
- return 1;
+ *hsi_port = 0;
- return 0;
+ return false;
}
/**
* omap_hsi_wakeup_enable - Enable HSI wakeup feature from RET/OFF mode
*
* @hsi_port - reference to the HSI port onto which enable wakeup feature.
+* Range [1, 2]
*
* Return value :* 0 if CAWAKE has been configured to wakeup platform
* * -ENODEV if CAWAKE is not muxed on padconf
*/
static int omap_hsi_wakeup_enable(int hsi_port)
{
+ struct hsi_port_ctx *port_ctx;
int ret = -ENODEV;
- if (omap_hsi_is_io_pad_hsi())
- ret = omap_mux_enable_wakeup(OMAP_HSI_PADCONF_CAWAKE_PIN);
- else
- pr_debug("Trying to enable HSI IO wakeup on non HSI board\n");
-
+ if (omap_hsi_is_io_pad_hsi(hsi_port)) {
+ port_ctx = hsi_get_hsi_port_ctx_data(hsi_port);
+ ret = omap_mux_enable_wakeup(port_ctx->cawake_padconf_name);
+ omap4_trigger_ioctrl();
+ } else {
+ pr_debug("HSI port %d not muxed, failed to enable IO wakeup\n",
+ hsi_port);
+ }
- /* TODO: handle hsi_port param and use it to find the correct Pad */
return ret;
}
@@ -242,21 +301,24 @@ static int omap_hsi_wakeup_enable(int hsi_port)
* omap_hsi_wakeup_disable - Disable HSI wakeup feature from RET/OFF mode
*
* @hsi_port - reference to the HSI port onto which disable wakeup feature.
+* Range [1, 2]
*
* Return value :* 0 if CAWAKE has been configured to not wakeup platform
* * -ENODEV if CAWAKE is not muxed on padconf
*/
static int omap_hsi_wakeup_disable(int hsi_port)
{
+ struct hsi_port_ctx *port_ctx;
int ret = -ENODEV;
- if (omap_hsi_is_io_pad_hsi())
- ret = omap_mux_disable_wakeup(OMAP_HSI_PADCONF_CAWAKE_PIN);
- else
- pr_debug("Trying to disable HSI IO wakeup on non HSI board\n");
-
-
- /* TODO: handle hsi_port param and use it to find the correct Pad */
+ if (omap_hsi_is_io_pad_hsi(hsi_port)) {
+ port_ctx = hsi_get_hsi_port_ctx_data(hsi_port);
+ ret = omap_mux_disable_wakeup(port_ctx->cawake_padconf_name);
+ omap4_trigger_ioctrl();
+ } else {
+ pr_debug("HSI port %d not muxed, failed to disable IO wakeup\n",
+ hsi_port);
+ }
return ret;
}
@@ -264,6 +326,9 @@ static int omap_hsi_wakeup_disable(int hsi_port)
/**
* omap_hsi_prepare_suspend - Prepare HSI for suspend mode
*
+* @hsi_port - reference to the HSI port. Range [1, 2]
+* @dev_may_wakeup - value of sysfs flag indicating device wakeup capability
+*
* Return value :* 0 if CAWAKE padconf has been configured properly
* * -ENODEV if CAWAKE is not muxed on padconf.
*
@@ -281,42 +346,79 @@ int omap_hsi_prepare_suspend(int hsi_port, bool dev_may_wakeup)
}
/**
+* omap_hsi_io_wakeup_check - Check if IO wakeup is from HSI and schedule HSI
+* processing tasklet
+*
+* Return value : * 0 if HSI tasklet scheduled.
+* * negative value else.
+*/
+int omap_hsi_io_wakeup_check(void)
+{
+ int hsi_port, ret = -1;
+
+ /* Modem HSI wakeup */
+ if (omap_hsi_is_io_wakeup_from_hsi(&hsi_port))
+ ret = omap_hsi_wakeup(hsi_port);
+
+ return ret;
+}
+
+/**
* omap_hsi_wakeup - Prepare HSI for wakeup from suspend mode (RET/OFF)
*
-* Return value : 1 if IO wakeup source is HSI
-* 0 if IO wakeup source is not HSI.
+* @hsi_port - reference to the HSI port which triggered wakeup.
+* Range [1, 2]
+*
+* Return value : * 0 if HSI tasklet scheduled.
+* * negative value else.
*/
int omap_hsi_wakeup(int hsi_port)
{
static struct platform_device *pdev;
static struct hsi_dev *hsi_ctrl;
+ int i;
if (!pdev) {
- pdev = hsi_get_hsi_platform_device();
+ pdev = hsi_get_hsi_platform_device();
if (!pdev)
- return -ENODEV;
-}
+ return -ENODEV;
+ }
if (!device_may_wakeup(&pdev->dev)) {
- dev_info(&pdev->dev, "Modem not allowed to wakeup platform");
+ dev_info(&pdev->dev, "Modem not allowed to wakeup platform\n");
return -EPERM;
}
if (!hsi_ctrl) {
- hsi_ctrl = hsi_get_hsi_controller_data(pdev);
- if (!hsi_ctrl)
- return -ENODEV;
+ hsi_ctrl = hsi_get_hsi_controller_data(pdev);
+ if (!hsi_ctrl)
+ return -ENODEV;
}
- dev_dbg(hsi_ctrl->dev, "Modem wakeup detected from HSI CAWAKE Pad");
+ for (i = 0; i < omap_hsi_platform_data.num_ports; i++) {
+ if (omap_hsi_platform_data.ctx->pctx[i].port_number == hsi_port)
+ break;
+ }
+
+ if (i == omap_hsi_platform_data.num_ports)
+ return -ENODEV;
+
+
+ /* Check no other interrupt handler has already scheduled the tasklet */
+ if (test_and_set_bit(HSI_FLAGS_TASKLET_LOCK,
+ &hsi_ctrl->hsi_port[i].flags))
+ return -EBUSY;
+
+ dev_dbg(hsi_ctrl->dev, "Modem wakeup detected from HSI CAWAKE Pad port "
+ "%d\n", hsi_port);
/* CAWAKE falling or rising edge detected */
- hsi_ctrl->hsi_port->cawake_off_event = true;
- tasklet_hi_schedule(&hsi_ctrl->hsi_port->hsi_tasklet);
+ hsi_ctrl->hsi_port[i].cawake_off_event = true;
+ tasklet_hi_schedule(&hsi_ctrl->hsi_port[i].hsi_tasklet);
/* Disable interrupt until Bottom Half has cleared */
/* the IRQ status register */
- disable_irq_nosync(hsi_ctrl->hsi_port->irq);
+ disable_irq_nosync(hsi_ctrl->hsi_port[i].irq);
return 0;
}
@@ -344,6 +446,8 @@ static int __init omap_hsi_register(struct omap_hwmod *oh, void *user)
return -EEXIST;
}
+ omap_hsi_platform_data.errata = omap_hsi_configure_errata();
+
od = omap_device_build(OMAP_HSI_PLATFORM_DEVICE_DRIVER_NAME, 0, oh,
pdata, sizeof(*pdata), omap_hsi_latency,
ARRAY_SIZE(omap_hsi_latency), false);
@@ -376,19 +480,19 @@ static void __init omap_4430hsi_pad_conf(void)
/* hsi1_acready */
omap_mux_init_signal("usbb1_ulpitll_nxt.hsi1_acready", \
OMAP_PIN_OUTPUT | \
- OMAP_PIN_OFF_OUTPUT_LOW);
+ OMAP_OFF_EN);
/* hsi1_acwake */
omap_mux_init_signal("usbb1_ulpitll_dat0.hsi1_acwake", \
OMAP_PIN_OUTPUT | \
- OMAP_PIN_OFF_NONE);
+ OMAP_OFF_EN);
/* hsi1_acdata */
omap_mux_init_signal("usbb1_ulpitll_dat1.hsi1_acdata", \
OMAP_PIN_OUTPUT | \
- OMAP_PIN_OFF_NONE);
+ OMAP_OFF_EN);
/* hsi1_acflag */
omap_mux_init_signal("usbb1_ulpitll_dat2.hsi1_acflag", \
OMAP_PIN_OUTPUT | \
- OMAP_PIN_OFF_NONE);
+ OMAP_OFF_EN);
/* hsi1_caready */
omap_mux_init_signal("usbb1_ulpitll_dat3.hsi1_caready", \
OMAP_PIN_INPUT | \
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index fac4aec..aea82b7 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -152,6 +152,7 @@
#include "prm44xx.h"
#include "mux.h"
#include "pm.h"
+#include "board-tuna.h"
/* Maximum microseconds to wait for OMAP module to softreset */
#define MAX_MODULE_SOFTRESET_WAIT 10000
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index b5c5b10..a650628 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -1059,7 +1059,7 @@ static struct omap_hwmod_class omap44xx_ctrl_module_hwmod_class = {
static struct omap_hwmod omap44xx_ctrl_module_core_hwmod;
static struct omap_hwmod_irq_info omap44xx_ctrl_module_core_irqs[] = {
{ .name = "sec_evts", .irq = 8 + OMAP44XX_IRQ_GIC_START },
- { .name = "thermal_alert", .irq = 126 + OMAP44XX_IRQ_GIC_START },
+ { .name = "thermal_alert", .irq = 127 + OMAP44XX_IRQ_GIC_START },
};
static struct omap_hwmod_addr_space omap44xx_ctrl_module_core_addrs[] = {
@@ -1106,7 +1106,7 @@ static struct omap_hwmod_class omap44xx_thermal_sensor_hwmod_class = {
};
static struct omap_hwmod_irq_info omap44xx_thermal_sensor_irqs[] = {
- { .name = "thermal_alert", .irq = 126 + OMAP44XX_IRQ_GIC_START },
+ { .name = "thermal_alert", .irq = 127 + OMAP44XX_IRQ_GIC_START },
};
static struct omap_hwmod_addr_space omap44xx_thermal_sensor_addrs[] = {
@@ -5573,7 +5573,7 @@ static struct omap_hwmod_ocp_if *omap44xx_uart3_slaves[] = {
static struct omap_hwmod omap44xx_uart3_hwmod = {
.name = "uart3",
.class = &omap44xx_uart_hwmod_class,
- .flags = (HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET),
+ .flags = (HWMOD_SWSUP_SIDLE | HWMOD_INIT_NO_IDLE | HWMOD_INIT_NO_RESET),
.mpu_irqs = omap44xx_uart3_irqs,
.mpu_irqs_cnt = ARRAY_SIZE(omap44xx_uart3_irqs),
.sdma_reqs = omap44xx_uart3_sdma_reqs,
@@ -5941,7 +5941,7 @@ static struct omap_hwmod_addr_space omap44xx_usbhs_ohci_addrs[] = {
.name = "ohci",
.pa_start = 0x4A064800,
.pa_end = 0x4A064BFF,
- .flags = ADDR_MAP_ON_INIT
+ .flags = ADDR_MAP_ON_INIT | ADDR_TYPE_RT
}
};
diff --git a/arch/arm/mach-omap2/omap_l3_noc.c b/arch/arm/mach-omap2/omap_l3_noc.c
index 9d152de..0d6aaae 100644
--- a/arch/arm/mach-omap2/omap_l3_noc.c
+++ b/arch/arm/mach-omap2/omap_l3_noc.c
@@ -28,30 +28,10 @@
#include <linux/slab.h>
#include "omap_l3_noc.h"
+#include "board-tuna.h"
#define NUM_OF_L3_MASTERS ARRAY_SIZE(l3_masters)
-static void l3_dump_targ_context(u32 baseaddr)
-{
- pr_err("COREREG : 0x%08x\n", readl(baseaddr + L3_COREREG));
- pr_err("VERSIONREG : 0x%08x\n", readl(baseaddr + L3_VERSIONREG));
- pr_err("MAINCTLREG : 0x%08x\n", readl(baseaddr + L3_MAINCTLREG));
- pr_err("NTTPADDR_0 : 0x%08x\n", readl(baseaddr + L3_NTTPADDR_0));
- pr_err("SVRTSTDLVL : 0x%08x\n", readl(baseaddr + L3_SVRTSTDLVL));
- pr_err("SVRTCUSTOMLVL: 0x%08x\n", readl(baseaddr + L3_SVRTCUSTOMLVL));
- pr_err("MAIN : 0x%08x\n", readl(baseaddr + L3_MAIN));
- pr_err("HDR : 0x%08x\n", readl(baseaddr + L3_HDR));
- pr_err("MSTADDR : 0x%08x\n", readl(baseaddr + L3_MSTADDR));
- pr_err("SLVADDR : 0x%08x\n", readl(baseaddr + L3_SLVADDR));
- pr_err("INFO : 0x%08x\n", readl(baseaddr + L3_INFO));
- pr_err("SLVOFSLSB : 0x%08x\n", readl(baseaddr + L3_SLVOFSLSB));
- pr_err("SLVOFSMSB : 0x%08x\n", readl(baseaddr + L3_SLVOFSMSB));
- pr_err("CUSTOMINFO_INFO : 0x%08x\n", readl(baseaddr + L3_CUSTOMINFO_INFO));
- pr_err("CUSTOMINFO_MSTADDR: 0x%08x\n", readl(baseaddr + L3_CUSTOMINFO_MSTADDR));
- pr_err("CUSTOMINFO_OPCODE : 0x%08x\n", readl(baseaddr + L3_CUSTOMINFO_OPCODE));
- pr_err("ADDRSPACESIZELOG : 0x%08x\n", readl(baseaddr + L3_ADDRSPACESIZELOG));
-}
-
/*
* Interrupt Handler for L3 error detection.
* 1) Identify the L3 clockdomain partition to which the error belongs to.
@@ -116,10 +96,23 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
slave_addr = std_err_main_addr +
L3_SLAVE_ADDRESS_OFFSET;
- WARN(true, "L3 standard error: SOURCE:%s at address 0x%x\n",
- source_name, readl(slave_addr));
-
- l3_dump_targ_context(base + regoffset);
+ pr_err("L3 standard error: SOURCE:%s at address 0x%x MSTADDR=0x%x hdr=0x%x\n",
+ source_name, readl(slave_addr),
+ readl(base + regoffset + L3_MSTADDR),
+ readl(base + regoffset + L3_HDR));
+ WARN_ONCE(true, "L3 standard error");
+
+ /* Disable ABE L3 Interrupt on LTE boards */
+ if ((readl(base + regoffset + L3_MSTADDR) == 0xc0) &&
+ (readl(base + regoffset + L3_SLVADDR) == 0x3) &&
+ (omap4_tuna_get_type() == TUNA_TYPE_TORO)) {
+ pr_err("** Disabling ABE L3 interrupt for now....\n");
+ writel(0x1, base + regoffset + L3_MAINCTLREG);
+ writel(0x0, base + regoffset + L3_SVRTSTDLVL);
+ writel(0x0, base + regoffset + L3_SVRTCUSTOMLVL);
+ writel(0x0, base + regoffset + L3_MAIN);
+ writel(0x1F, base + regoffset + L3_ADDRSPACESIZELOG);
+ }
/* clear the std error log*/
clear = std_err_main | CLEAR_STDERR_LOG;
@@ -131,8 +124,10 @@ static irqreturn_t l3_interrupt_handler(int irq, void *_l3)
l3_targ_stderrlog_main_name[i][err_src];
regoffset = targ_reg_offset[i][err_src];
- WARN(true, "CUSTOM SRESP error with SOURCE:%s\n",
- source_name);
+ pr_err("L3 CUSTOM SRESP error with SOURCE:%s info=0x%x\n",
+ source_name,
+ readl(base + regoffset + L3_CUSTOMINFO_INFO));
+ WARN_ONCE(true, "L3 custom sresp error");
masterid = readl(base + regoffset +
L3_CUSTOMINFO_MSTADDR);
diff --git a/arch/arm/mach-omap2/opp4xxx_data.c b/arch/arm/mach-omap2/opp4xxx_data.c
index 255f132..c0fd100 100644
--- a/arch/arm/mach-omap2/opp4xxx_data.c
+++ b/arch/arm/mach-omap2/opp4xxx_data.c
@@ -324,8 +324,9 @@ int __init omap4_opp_init(void)
if (!r) {
if (omap4_has_mpu_1_2ghz())
omap4_mpu_opp_enable(1200000000);
- if (omap4_has_mpu_1_5ghz())
- omap4_mpu_opp_enable(1500000000);
+ /* The tuna PCB doesn't support 1.5GHz, so disable it for now */
+ /*if (omap4_has_mpu_1_5ghz())
+ omap4_mpu_opp_enable(1500000000);*/
}
return r;
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index b9e2bf1..52ee078 100644..100755
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -23,7 +23,10 @@
#include <linux/irq.h>
#include <asm/hardware/gic.h>
+#include <asm/mach-types.h>
+
#include <mach/omap4-common.h>
+
#include <plat/omap_hsi.h>
#include <plat/common.h>
#include <plat/temperature_sensor.h>
@@ -80,6 +83,12 @@ static struct clockdomain *emif_clkdm, *mpuss_clkdm;
/* Yet un-named erratum which requires AUTORET to be disabled for IVA PD */
#define OMAP4_PM_ERRATUM_IVA_AUTO_RET_iXXX BIT(1)
+/*
+* HSI - OMAP4430-2.2BUG00055:
+* HSI: DSP Swakeup generated is the same than MPU Swakeup.
+* System can’t enter in off mode due to the DSP.
+*/
+#define OMAP4_PM_ERRATUM_HSI_SWAKEUP_iXXX BIT(2)
/* Dynamic dependendency Cannot be enabled due to i688 erratum ID for 443x */
#define OMAP4_PM_ERRATUM_MPU_EMIF_NO_DYNDEP_i688 BIT(3)
@@ -98,9 +107,22 @@ static struct clockdomain *emif_clkdm, *mpuss_clkdm;
*/
#define OMAP4_PM_ERRATUM_MPU_EMIF_NO_DYNDEP_IDLE_iXXX BIT(4)
-static u8 pm44xx_errata;
+u8 pm44xx_errata;
#define is_pm44xx_erratum(erratum) (pm44xx_errata & OMAP4_PM_ERRATUM_##erratum)
+/* HACK: check CAWAKE wakeup event */
+#define USBB1_ULPITLL_CLK 0x4A1000C0
+#define CONTROL_PADCONF_WAKEUPEVENT_2 0x4A1001E0
+static int cawake_event_flag = 0;
+void check_cawake_wakeup_event(void)
+{
+ if ((omap_readl(USBB1_ULPITLL_CLK) & 0x80000000) ||
+ (omap_readl(CONTROL_PADCONF_WAKEUPEVENT_2) & 0x2)) {
+ pr_info("[HSI] PORT 1 CAWAKE WAKEUP EVENT\n");
+ cawake_event_flag = 1;
+ }
+}
+
#define MAX_IOPAD_LATCH_TIME 1000
void omap4_trigger_ioctrl(void)
{
@@ -706,6 +728,10 @@ static int omap4_pm_suspend(void)
* More details can be found in OMAP4430 TRM section 4.3.4.2.
*/
omap4_enter_sleep(0, PWRDM_POWER_OFF, true);
+
+ /* HACK: check CAWAKE wakeup event */
+ check_cawake_wakeup_event();
+
omap4_print_wakeirq();
prcmdebug_dump(PRCMDEBUG_LASTSLEEP);
@@ -1021,7 +1047,7 @@ no_32k:
*
* Bug ref is HSI-C1BUG00106 : dsp swakeup generated by HSI same as mpu swakeup
*/
-static void omap_pm_clear_dsp_wake_up(void)
+void omap_pm_clear_dsp_wake_up(void)
{
int ret;
int timeout = 10;
@@ -1083,6 +1109,7 @@ static void omap_pm_clear_dsp_wake_up(void)
static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
{
u32 irqenable_mpu, irqstatus_mpu;
+ int hsi_port;
irqenable_mpu = omap4_prm_read_inst_reg(OMAP4430_PRM_OCP_SOCKET_INST,
OMAP4_PRM_IRQENABLE_MPU_OFFSET);
@@ -1092,12 +1119,19 @@ static irqreturn_t prcm_interrupt_handler (int irq, void *dev_id)
/* Check if a IO_ST interrupt */
if (irqstatus_mpu & OMAP4430_IO_ST_MASK) {
/* Check if HSI caused the IO wakeup */
- if (omap_hsi_is_io_wakeup_from_hsi()) {
- omap_pm_clear_dsp_wake_up();
- omap_hsi_wakeup(0);
- }
+
+ /* HACK: check CAWAKE wakeup event */
+ if (cawake_event_flag) {
+ hsi_port = 1;
+ cawake_event_flag = 0;
+ omap_hsi_wakeup(hsi_port);
+ } else
+ if (omap_hsi_is_io_wakeup_from_hsi(&hsi_port))
+ omap_hsi_wakeup(hsi_port);
+
omap_uart_resume_idle();
- usbhs_wakeup();
+ if (!machine_is_tuna())
+ usbhs_wakeup();
omap_debug_uart_resume_idle();
omap4_trigger_ioctrl();
}
@@ -1201,7 +1235,8 @@ static void __init omap4_pm_setup_errata(void)
* all OMAP4 silica
*/
if (cpu_is_omap44xx())
- pm44xx_errata |= OMAP4_PM_ERRATUM_IVA_AUTO_RET_iXXX;
+ pm44xx_errata |= OMAP4_PM_ERRATUM_IVA_AUTO_RET_iXXX |
+ OMAP4_PM_ERRATUM_HSI_SWAKEUP_iXXX;
/* Dynamic Dependency errata for all silicon !=443x */
if (cpu_is_omap443x())
pm44xx_errata |= OMAP4_PM_ERRATUM_MPU_EMIF_NO_DYNDEP_i688;
diff --git a/arch/arm/mach-omap2/usb-host.c b/arch/arm/mach-omap2/usb-host.c
index 7b422b5..046f690 100644
--- a/arch/arm/mach-omap2/usb-host.c
+++ b/arch/arm/mach-omap2/usb-host.c
@@ -75,9 +75,7 @@ static struct omap_device_pad port1_phy_pads[] __initdata = {
},
{
.name = "usbb1_ulpitll_dir.usbb1_ulpiphy_dir",
- .flags = OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP,
- .enable = (OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE4) & ~OMAP_WAKEUP_EN,
- .idle = OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE4,
+ .enable = OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE4,
},
{
.name = "usbb1_ulpitll_nxt.usbb1_ulpiphy_nxt",
@@ -85,9 +83,7 @@ static struct omap_device_pad port1_phy_pads[] __initdata = {
},
{
.name = "usbb1_ulpitll_dat0.usbb1_ulpiphy_dat0",
- .flags = OMAP_DEVICE_PAD_REMUX | OMAP_DEVICE_PAD_WAKEUP,
- .enable = (OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE4) & ~OMAP_WAKEUP_EN,
- .idle = OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE4,
+ .enable = OMAP_PIN_INPUT_PULLDOWN | OMAP_MUX_MODE4,
},
{
.name = "usbb1_ulpitll_dat1.usbb1_ulpiphy_dat1",
diff --git a/arch/arm/plat-omap/include/plat/board-tuna-bluetooth.h b/arch/arm/plat-omap/include/plat/board-tuna-bluetooth.h
new file mode 100644
index 0000000..c74de05
--- /dev/null
+++ b/arch/arm/plat-omap/include/plat/board-tuna-bluetooth.h
@@ -0,0 +1,30 @@
+/*
+ * Bluetooth Broadcomm and low power control via GPIO
+ *
+ * Copyright (C) 2011 Samsung, Inc.
+ * Copyright (C) 2011 Google, 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
+ *
+ */
+
+#ifndef __BOARD_TUNA_BLUETOOTH_H__
+#define __BOARD_TUNA_BLUETOOTH_H__
+
+#include <linux/serial_core.h>
+
+extern void bcm_bt_lpm_exit_lpm_locked(struct uart_port *uport);
+
+#endif /* __BOARD_TUNA_BLUETOOTH_H__ */
diff --git a/arch/arm/plat-omap/include/plat/memory.h b/arch/arm/plat-omap/include/plat/memory.h
index e6720aa..0c7260a 100644
--- a/arch/arm/plat-omap/include/plat/memory.h
+++ b/arch/arm/plat-omap/include/plat/memory.h
@@ -86,17 +86,7 @@
#endif /* CONFIG_ARCH_OMAP15XX */
/* Override the ARM default */
-#ifdef CONFIG_FB_OMAP_CONSISTENT_DMA_SIZE
-
-#if (CONFIG_FB_OMAP_CONSISTENT_DMA_SIZE == 0)
-#undef CONFIG_FB_OMAP_CONSISTENT_DMA_SIZE
-#define CONFIG_FB_OMAP_CONSISTENT_DMA_SIZE 2
-#endif
-
-#define CONSISTENT_DMA_SIZE \
- (((CONFIG_FB_OMAP_CONSISTENT_DMA_SIZE + 1) & ~1) * 1024 * 1024)
-
-#endif
+#define CONSISTENT_DMA_SIZE (14 * SZ_1M)
#endif
diff --git a/arch/arm/plat-omap/include/plat/omap_hsi.h b/arch/arm/plat-omap/include/plat/omap_hsi.h
index 1a75ed4..ce9dadf 100644
--- a/arch/arm/plat-omap/include/plat/omap_hsi.h
+++ b/arch/arm/plat-omap/include/plat/omap_hsi.h
@@ -135,10 +135,11 @@
#define HSI_SSI_WAKE_MASK 0xff /* for SSI */
#define HSI_WAKE_MASK 0xffff /* for HSI */
#define HSI_SET_WAKE_4_WIRES (0 << 16)
+#define HSI_SET_WAKE_3_WIRES (1 << 16)
+#define HSI_SET_WAKE_3_WIRES_MASK 0xfffcffff /* 3-wires + ACREADY to 1 */
#define HSI_SET_WAKE_READY_LVL_0 (0 << 17)
-#define HSI_SET_WAKE(channel) (1 << (channel) |\
- HSI_SET_WAKE_4_WIRES |\
- HSI_SET_WAKE_READY_LVL_0)
+#define HSI_SET_WAKE_READY_LVL_1 (1 << 17)
+#define HSI_SET_WAKE(channel) (1 << (channel))
#define HSI_CLEAR_WAKE(channel) (1 << (channel))
#define HSI_WAKE(channel) (1 << (channel))
@@ -259,6 +260,11 @@
#define HSI_HSR_ERROR_TBE (1 << 4) /* HSI only */
#define HSI_HSR_ERROR_RME (1 << 7) /* HSI only */
#define HSI_HSR_ERROR_TME (1 << 11) /* HSI only */
+#define HSI_HSR_ERROR_ALL (HSI_HSR_ERROR_SIG | \
+ HSI_HSR_ERROR_FTE | \
+ HSI_HSR_ERROR_TBE | \
+ HSI_HSR_ERROR_RME | \
+ HSI_HSR_ERROR_TME)
#define HSI_HSR_ERRORACK_REG(port) (HSI_HSR_BASE(port) + 0x0024)
@@ -268,6 +274,7 @@
#define HSI_HSR_OVERRUNACK_REG(port) (HSI_HSR_BASE(port) + 0x0030)
+/* HSR_COUNTERS_Pp is former SSI_TIMEOUT_REG */
#define HSI_HSR_COUNTERS_REG(port) (HSI_HSR_BASE(port) + 0x0034)
#define SSI_TIMEOUT_REG(port) (HSI_HSR_COUNTERS_REG(port))
#define HSI_TIMEOUT_DEFAULT 0 /* SSI only */
@@ -287,6 +294,7 @@
(((FB << HSI_COUNTERS_FB_OFFSET) & HSI_COUNTERS_FB_MASK) \
((TB << HSI_COUNTERS_TB_OFFSET) & HSI_COUNTERS_TB_MASK) \
((FT << HSI_COUNTERS_FT_OFFSET) & HSI_COUNTERS_FT_MASK))
+/* For SSI */
#define SSI_SSR_COMBINE_COUNTERS(FT) \
((FT << HSI_SSI_RX_TIMEOUT_OFFSET) & HSI_SSI_RX_TIMEOUT_MASK)
@@ -454,9 +462,34 @@
HSI_SYS_MPU_U_ENABLE_REG(port, irq))
#define HSI_SYS_MPU_STATUS_CH_REG(port, irq, channel) \
- ((channel < HSI_SSI_CHANNELS_MAX) ? \
+ (((channel) < HSI_SSI_CHANNELS_MAX) ? \
HSI_SYS_MPU_STATUS_REG(port, irq) : \
HSI_SYS_MPU_U_STATUS_REG(port, irq))
+
+
+/* HSI errata handling */
+#define IS_HSI_ERRATA(errata, id) (errata & (id))
+#define SET_HSI_ERRATA(errata, id) (errata |= (id))
+
+/* HSI-C1BUG00088: i696: HSI: Issue with SW reset
+ * No recovery from SW reset under specific circumstances
+ * If a SW RESET is done while some HSI errors are still not
+ * acknowledged, the HSR FSM is stucked. */
+#define HSI_ERRATUM_i696_SW_RESET_FSM_STUCK BIT(0)
+
+/* HSI-C1BUG00085: ixxx: HSI wakeup issue in 3 wires mode
+ * HSI will NOT generate the Swakeup for 2nd frame if it entered
+ * IDLE after 1st received frame */
+#define HSI_ERRATUM_ixxx_3WIRES_NO_SWAKEUP BIT(1)
+
+/*
+* HSI - OMAP4430-2.2BUG00055: i702
+* HSI: DSP Swakeup generated is the same than MPU Swakeup.
+* System cannot enter in off mode due to the DSP.
+*/
+#define HSI_ERRATUM_i702_PM_HSI_SWAKEUP BIT(2)
+
+
/**
* struct omap_ssi_config - SSI board configuration
* @num_ports: Number of ports in use
@@ -479,16 +512,15 @@ extern int omap_hsi_config(struct omap_hsi_board_config *hsi_config);
#ifdef CONFIG_OMAP_HSI
extern int omap_hsi_prepare_suspend(int hsi_port, bool dev_may_wakeup);
-extern int omap_hsi_prepare_idle(void);
+extern int omap_hsi_io_wakeup_check(void);
extern int omap_hsi_wakeup(int hsi_port);
-extern int omap_hsi_is_io_wakeup_from_hsi(void);
+extern bool omap_hsi_is_io_wakeup_from_hsi(int *hsi_port);
#else
inline int omap_hsi_prepare_suspend(int hsi_port,
bool dev_may_wakeup) { return -ENOSYS; }
-inline int omap_hsi_prepare_idle(void) { return -ENOSYS; }
+inline int omap_hsi_io_wakeup_check(void) { return -ENOSYS; }
inline int omap_hsi_wakeup(int hsi_port) { return -ENOSYS; }
-inline int omap_hsi_is_io_wakeup_from_hsi(void) { return -ENOSYS; }
-
+inline bool omap_hsi_is_io_wakeup_from_hsi(int *hsi_port) { return false; }
#endif
#endif /* __OMAP_HSI_H__ */
diff --git a/arch/arm/plat-omap/include/plat/uncompress.h b/arch/arm/plat-omap/include/plat/uncompress.h
index ac4b60d..cf07178 100644
--- a/arch/arm/plat-omap/include/plat/uncompress.h
+++ b/arch/arm/plat-omap/include/plat/uncompress.h
@@ -164,6 +164,7 @@ static inline void __arch_decomp_setup(unsigned long arch_id)
/* omap4 based boards using UART3 */
DEBUG_LL_OMAP4(3, omap_4430sdp);
DEBUG_LL_OMAP4(3, omap4_panda);
+ DEBUG_LL_OMAP4(3, tuna);
/* zoom2/3 external uart */
DEBUG_LL_ZOOM(omap_zoom2);
diff --git a/arch/arm/plat-omap/omap_rpmsg.c b/arch/arm/plat-omap/omap_rpmsg.c
index 3653833..269f206 100644
--- a/arch/arm/plat-omap/omap_rpmsg.c
+++ b/arch/arm/plat-omap/omap_rpmsg.c
@@ -47,6 +47,7 @@ struct omap_rpmsg_vproc {
char *mbox_name;
char *rproc_name;
struct omap_mbox *mbox;
+ struct mutex lock;
struct rproc *rproc;
struct notifier_block nb;
struct notifier_block rproc_nb;
@@ -145,13 +146,25 @@ static void omap_rpmsg_notify(struct virtqueue *vq)
{
struct omap_rpmsg_vq_info *rpvq = vq->priv;
int ret;
+ int count = 5;
pr_debug("sending mailbox msg: %d\n", rpvq->vq_id);
- rproc_last_busy(rpvq->rpdev->rproc);
+ do {
+ rproc_last_busy(rpvq->rpdev->rproc);
+ mutex_lock(&rpvq->rpdev->lock);
+ if (rpvq->rpdev->mbox)
+ break;
+ mutex_unlock(&rpvq->rpdev->lock);
+ } while (--count);
+ if (!count) {
+ pr_err("mbox handle is NULL\n");
+ return;
+ }
/* send the index of the triggered virtqueue as the mailbox payload */
ret = omap_mbox_msg_send(rpvq->rpdev->mbox, rpvq->vq_id);
if (ret)
pr_err("ugh, omap_mbox_msg_send() failed: %d\n", ret);
+ mutex_unlock(&rpvq->rpdev->lock);
}
static int omap_rpmsg_mbox_callback(struct notifier_block *this,
@@ -228,18 +241,22 @@ static int rpmsg_rproc_suspend(struct omap_rpmsg_vproc *rpdev)
static int rpmsg_rproc_pos_suspend(struct omap_rpmsg_vproc *rpdev)
{
+ mutex_lock(&rpdev->lock);
if (rpdev->mbox) {
omap_mbox_put(rpdev->mbox, &rpdev->nb);
rpdev->mbox = NULL;
}
+ mutex_unlock(&rpdev->lock);
return NOTIFY_DONE;
}
static int rpmsg_rproc_resume(struct omap_rpmsg_vproc *rpdev)
{
+ mutex_lock(&rpdev->lock);
if (!rpdev->mbox)
rpdev->mbox = omap_mbox_get(rpdev->mbox_name, &rpdev->nb);
+ mutex_unlock(&rpdev->lock);
return NOTIFY_DONE;
}
@@ -315,6 +332,7 @@ static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
/* system-wide unique id for this virtqueue */
rpvq->vq_id = rpdev->base_vq_id + index;
rpvq->rpdev = rpdev;
+ mutex_init(&rpdev->lock);
return vq;
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
index ead17f9..0088b8c 100644
--- a/drivers/i2c/busses/i2c-omap.c
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -42,6 +42,11 @@
#include <linux/pm_runtime.h>
#include <linux/pm_qos_params.h>
+#ifdef CONFIG_ARCH_OMAP4
+#include "../../../arch/arm/mach-omap2/cm2_44xx.h"
+#include "../../../arch/arm/mach-omap2/cm-regbits-44xx.h"
+#endif
+
/* I2C controller revisions */
#define OMAP_I2C_REV_2 0x20
@@ -264,6 +269,51 @@ static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg)
(i2c_dev->regs[reg] << i2c_dev->reg_shift));
}
+static void omap_i2c_dump(struct omap_i2c_dev *dev)
+{
+ struct clk *fclk;
+ unsigned long fclk_rate;
+
+ dev_info(dev->dev, "sysc=0x%04x stat=0x%04x syss=0x%04x con=0x%04x\n",
+ omap_i2c_read_reg(dev, OMAP_I2C_SYSC_REG),
+ omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG),
+ omap_i2c_read_reg(dev, OMAP_I2C_SYSS_REG),
+ omap_i2c_read_reg(dev, OMAP_I2C_CON_REG));
+
+ pr_info("we=0x%04x sa=0x%04x cnt=%d buf=0x%04x bufstat=0x%04x\n",
+ omap_i2c_read_reg(dev, OMAP_I2C_WE_REG),
+ omap_i2c_read_reg(dev, OMAP_I2C_SA_REG),
+ omap_i2c_read_reg(dev, OMAP_I2C_CNT_REG),
+ omap_i2c_read_reg(dev, OMAP_I2C_BUF_REG),
+ omap_i2c_read_reg(dev, OMAP_I2C_BUFSTAT_REG));
+
+ fclk = clk_get(dev->dev, "fck");
+ fclk_rate = clk_get_rate(fclk);
+ clk_put(fclk);
+
+ pr_info("fclk=%lu"
+#ifdef CONFIG_ARCH_OMAP4
+ " gated=%s"
+#endif
+ " psc=0x%04x scll=0x%04x sclh=0x%04x\n",
+ fclk_rate,
+#ifdef CONFIG_ARCH_OMAP4
+ __raw_readl(OMAP4430_CM_L4PER_CLKSTCTRL) &
+ OMAP4430_CLKACTIVITY_PER_96M_GFCLK_MASK ? "no" : "yes",
+#endif
+ omap_i2c_read_reg(dev, OMAP_I2C_PSC_REG),
+ omap_i2c_read_reg(dev, OMAP_I2C_SCLL_REG),
+ omap_i2c_read_reg(dev, OMAP_I2C_SCLH_REG));
+
+#ifdef CONFIG_ARCH_OMAP4
+ pr_info("CLKCTRL: 1:0x%08x 2:0x%08x 3:0x%08x 4:0x%08x\n",
+ __raw_readl(OMAP4430_CM_L4PER_I2C1_CLKCTRL),
+ __raw_readl(OMAP4430_CM_L4PER_I2C2_CLKCTRL),
+ __raw_readl(OMAP4430_CM_L4PER_I2C3_CLKCTRL),
+ __raw_readl(OMAP4430_CM_L4PER_I2C4_CLKCTRL));
+#endif
+}
+
static void omap_i2c_unidle(struct omap_i2c_dev *dev)
{
struct platform_device *pdev;
@@ -501,6 +551,7 @@ static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
while (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
if (time_after(jiffies, timeout)) {
dev_warn(dev->dev, "timeout waiting for bus ready\n");
+ omap_i2c_dump(dev);
return -ETIMEDOUT;
}
msleep(1);
@@ -591,6 +642,9 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
return r;
if (r == 0) {
dev_err(dev->dev, "controller timed out\n");
+ pr_info("addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
+ msg->addr, msg->len, msg->flags, stop);
+ omap_i2c_dump(dev);
omap_i2c_init(dev);
return -ETIMEDOUT;
}
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 6f4ad1a..1f8e6d5 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -494,4 +494,11 @@ config INPUT_XEN_KBDDEV_FRONTEND
To compile this driver as a module, choose M here: the
module will be called xen-kbdfront.
+config OPTICAL_GP2A
+ depends on I2C && GENERIC_GPIO
+ tristate "GP2A ambient light and proximity input device"
+ default n
+ help
+ This option enables proximity & light sensors using gp2a driver.
+
endif
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index eb73834..059ce58 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -47,4 +47,4 @@ obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o
obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
-
+obj-$(CONFIG_OPTICAL_GP2A) += gp2a.o \ No newline at end of file
diff --git a/drivers/input/misc/gp2a.c b/drivers/input/misc/gp2a.c
new file mode 100644
index 0000000..472da90
--- /dev/null
+++ b/drivers/input/misc/gp2a.c
@@ -0,0 +1,639 @@
+/* linux/driver/input/misc/gp2a.c
+ * Copyright (C) 2010 Samsung Electronics. 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/i2c.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/leds.h>
+#include <linux/gpio.h>
+#include <linux/wakelock.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/workqueue.h>
+#include <linux/uaccess.h>
+#include <linux/gp2a.h>
+
+
+/* Note about power vs enable/disable:
+ * The chip has two functions, proximity and ambient light sensing.
+ * There is no separate power enablement to the two functions (unlike
+ * the Capella CM3602/3623).
+ * This module implements two drivers: /dev/proximity and /dev/light.
+ * When either driver is enabled (via sysfs attributes), we give power
+ * to the chip. When both are disabled, we remove power from the chip.
+ * In suspend, we remove power if light is disabled but not if proximity is
+ * enabled (proximity is allowed to wakeup from suspend).
+ *
+ * There are no ioctls for either driver interfaces. Output is via
+ * input device framework and control via sysfs attributes.
+ */
+
+
+#define gp2a_dbgmsg(str, args...) pr_debug("%s: " str, __func__, ##args)
+
+/* ADDSEL is LOW */
+#define REGS_PROX 0x0 /* Read Only */
+#define REGS_GAIN 0x1 /* Write Only */
+#define REGS_HYS 0x2 /* Write Only */
+#define REGS_CYCLE 0x3 /* Write Only */
+#define REGS_OPMOD 0x4 /* Write Only */
+
+/* sensor type */
+#define LIGHT 0
+#define PROXIMITY 1
+#define ALL 2
+
+#define DELAY_LOWBOUND (5 * NSEC_PER_MSEC)
+
+/* start time delay for light sensor in nano seconds */
+#define LIGHT_SENSOR_START_TIME_DELAY 50000000
+
+static u8 reg_defaults[5] = {
+ 0x00, /* PROX: read only register */
+ 0x08, /* GAIN: large LED drive level */
+ 0xC2, /* HYS: receiver sensitivity */
+ 0x04, /* CYCLE: */
+ 0x01, /* OPMOD: normal operating mode */
+};
+
+enum {
+ LIGHT_ENABLED = BIT(0),
+ PROXIMITY_ENABLED = BIT(1),
+};
+
+/* driver data */
+struct gp2a_data {
+ struct input_dev *proximity_input_dev;
+ struct input_dev *light_input_dev;
+ struct gp2a_platform_data *pdata;
+ struct i2c_client *i2c_client;
+ int irq;
+ struct work_struct work_light;
+ struct hrtimer timer;
+ ktime_t light_poll_delay;
+ bool on;
+ u8 power_state;
+ struct mutex power_lock;
+ struct wake_lock prx_wake_lock;
+ struct workqueue_struct *wq;
+};
+
+int gp2a_i2c_write(struct gp2a_data *gp2a, u8 reg, u8 *val)
+{
+ int err = 0;
+ struct i2c_msg msg[1];
+ unsigned char data[2];
+ int retry = 10;
+ struct i2c_client *client = gp2a->i2c_client;
+
+ if ((client == NULL) || (!client->adapter))
+ return -ENODEV;
+
+ while (retry--) {
+ data[0] = reg;
+ data[1] = *val;
+
+ msg->addr = client->addr;
+ msg->flags = 0; /* write */
+ msg->len = 2;
+ msg->buf = data;
+
+ err = i2c_transfer(client->adapter, msg, 1);
+
+ if (err >= 0)
+ return 0;
+ }
+ return err;
+}
+
+static void gp2a_light_enable(struct gp2a_data *gp2a)
+{
+ gp2a_dbgmsg("starting poll timer, delay %lldns\n",
+ ktime_to_ns(gp2a->light_poll_delay));
+ /*
+ * Set far out of range ABS_MISC value, -1024, to enable real value to
+ * go through next.
+ */
+ input_abs_set_val(gp2a->light_input_dev, ABS_MISC, -1024);
+ hrtimer_start(&gp2a->timer, ktime_set(0, LIGHT_SENSOR_START_TIME_DELAY),
+ HRTIMER_MODE_REL);
+}
+
+static void gp2a_light_disable(struct gp2a_data *gp2a)
+{
+ gp2a_dbgmsg("cancelling poll timer\n");
+ hrtimer_cancel(&gp2a->timer);
+ cancel_work_sync(&gp2a->work_light);
+}
+
+static ssize_t poll_delay_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gp2a_data *gp2a = dev_get_drvdata(dev);
+ return sprintf(buf, "%lld\n", ktime_to_ns(gp2a->light_poll_delay));
+}
+
+
+static ssize_t poll_delay_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct gp2a_data *gp2a = dev_get_drvdata(dev);
+ int64_t new_delay;
+ int err;
+
+ err = strict_strtoll(buf, 10, &new_delay);
+ if (err < 0)
+ return err;
+
+ gp2a_dbgmsg("new delay = %lldns, old delay = %lldns\n",
+ new_delay, ktime_to_ns(gp2a->light_poll_delay));
+
+ if (new_delay < DELAY_LOWBOUND) {
+ gp2a_dbgmsg("new delay less than low bound, so set delay "
+ "to %lld\n", (int64_t)DELAY_LOWBOUND);
+ new_delay = DELAY_LOWBOUND;
+ }
+
+ mutex_lock(&gp2a->power_lock);
+ if (new_delay != ktime_to_ns(gp2a->light_poll_delay)) {
+ gp2a->light_poll_delay = ns_to_ktime(new_delay);
+ if (gp2a->power_state & LIGHT_ENABLED) {
+ gp2a_light_disable(gp2a);
+ gp2a_light_enable(gp2a);
+ }
+ }
+ mutex_unlock(&gp2a->power_lock);
+
+ return size;
+}
+
+static ssize_t light_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gp2a_data *gp2a = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n",
+ (gp2a->power_state & LIGHT_ENABLED) ? 1 : 0);
+}
+
+static ssize_t proximity_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gp2a_data *gp2a = dev_get_drvdata(dev);
+ return sprintf(buf, "%d\n",
+ (gp2a->power_state & PROXIMITY_ENABLED) ? 1 : 0);
+}
+
+static ssize_t light_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct gp2a_data *gp2a = dev_get_drvdata(dev);
+ bool new_value;
+
+ if (sysfs_streq(buf, "1"))
+ new_value = true;
+ else if (sysfs_streq(buf, "0"))
+ new_value = false;
+ else {
+ pr_err("%s: invalid value %d\n", __func__, *buf);
+ return -EINVAL;
+ }
+
+ mutex_lock(&gp2a->power_lock);
+ gp2a_dbgmsg("new_value = %d, old state = %d\n",
+ new_value, (gp2a->power_state & LIGHT_ENABLED) ? 1 : 0);
+ if (new_value && !(gp2a->power_state & LIGHT_ENABLED)) {
+ if (!gp2a->power_state)
+ gp2a->pdata->power(true);
+ gp2a->power_state |= LIGHT_ENABLED;
+ gp2a_light_enable(gp2a);
+ } else if (!new_value && (gp2a->power_state & LIGHT_ENABLED)) {
+ gp2a_light_disable(gp2a);
+ gp2a->power_state &= ~LIGHT_ENABLED;
+ if (!gp2a->power_state)
+ gp2a->pdata->power(false);
+ }
+ mutex_unlock(&gp2a->power_lock);
+ return size;
+}
+
+static ssize_t proximity_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct gp2a_data *gp2a = dev_get_drvdata(dev);
+ bool new_value;
+
+ if (sysfs_streq(buf, "1"))
+ new_value = true;
+ else if (sysfs_streq(buf, "0"))
+ new_value = false;
+ else {
+ pr_err("%s: invalid value %d\n", __func__, *buf);
+ return -EINVAL;
+ }
+
+ mutex_lock(&gp2a->power_lock);
+ gp2a_dbgmsg("new_value = %d, old state = %d\n",
+ new_value, (gp2a->power_state & PROXIMITY_ENABLED) ? 1 : 0);
+ if (new_value && !(gp2a->power_state & PROXIMITY_ENABLED)) {
+ if (!gp2a->power_state)
+ gp2a->pdata->power(true);
+ gp2a->power_state |= PROXIMITY_ENABLED;
+ enable_irq(gp2a->irq);
+ enable_irq_wake(gp2a->irq);
+ gp2a_i2c_write(gp2a, REGS_GAIN, &reg_defaults[1]);
+ gp2a_i2c_write(gp2a, REGS_HYS, &reg_defaults[2]);
+ gp2a_i2c_write(gp2a, REGS_CYCLE, &reg_defaults[3]);
+ gp2a_i2c_write(gp2a, REGS_OPMOD, &reg_defaults[4]);
+ } else if (!new_value && (gp2a->power_state & PROXIMITY_ENABLED)) {
+ disable_irq_wake(gp2a->irq);
+ disable_irq(gp2a->irq);
+ gp2a_i2c_write(gp2a, REGS_OPMOD, &reg_defaults[0]);
+ gp2a->power_state &= ~PROXIMITY_ENABLED;
+ if (!gp2a->power_state)
+ gp2a->pdata->power(false);
+ }
+ mutex_unlock(&gp2a->power_lock);
+ return size;
+}
+
+static DEVICE_ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ poll_delay_show, poll_delay_store);
+
+static struct device_attribute dev_attr_light_enable =
+ __ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
+ light_enable_show, light_enable_store);
+
+static struct device_attribute dev_attr_proximity_enable =
+ __ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
+ proximity_enable_show, proximity_enable_store);
+
+static struct attribute *light_sysfs_attrs[] = {
+ &dev_attr_light_enable.attr,
+ &dev_attr_poll_delay.attr,
+ NULL
+};
+
+static struct attribute_group light_attribute_group = {
+ .attrs = light_sysfs_attrs,
+};
+
+static struct attribute *proximity_sysfs_attrs[] = {
+ &dev_attr_proximity_enable.attr,
+ NULL
+};
+
+static struct attribute_group proximity_attribute_group = {
+ .attrs = proximity_sysfs_attrs,
+};
+
+static void gp2a_work_func_light(struct work_struct *work)
+{
+ struct gp2a_data *gp2a = container_of(work, struct gp2a_data,
+ work_light);
+ int adc = gp2a->pdata->light_adc_value();
+ if (adc < 0) {
+ pr_err("adc returned error %d\n", adc);
+ return;
+ }
+ gp2a_dbgmsg("adc returned light value %d\n", adc);
+ input_report_abs(gp2a->light_input_dev, ABS_MISC, adc);
+ input_sync(gp2a->light_input_dev);
+}
+
+/* This function is for light sensor. It operates every a few seconds.
+ * It asks for work to be done on a thread because i2c needs a thread
+ * context (slow and blocking) and then reschedules the timer to run again.
+ */
+static enum hrtimer_restart gp2a_timer_func(struct hrtimer *timer)
+{
+ struct gp2a_data *gp2a = container_of(timer, struct gp2a_data, timer);
+ queue_work(gp2a->wq, &gp2a->work_light);
+ hrtimer_forward_now(&gp2a->timer, gp2a->light_poll_delay);
+ return HRTIMER_RESTART;
+}
+
+/* interrupt happened due to transition/change of near/far proximity state */
+irqreturn_t gp2a_irq_handler(int irq, void *data)
+{
+ struct gp2a_data *ip = data;
+ int val = gpio_get_value(ip->pdata->p_out);
+ if (val < 0) {
+ pr_err("%s: gpio_get_value error %d\n", __func__, val);
+ return IRQ_HANDLED;
+ }
+
+ gp2a_dbgmsg("gp2a: proximity val=%d\n", val);
+
+ /* 0 is close, 1 is far */
+ input_report_abs(ip->proximity_input_dev, ABS_DISTANCE, val);
+ input_sync(ip->proximity_input_dev);
+ wake_lock_timeout(&ip->prx_wake_lock, 3*HZ);
+ return IRQ_HANDLED;
+}
+
+static int gp2a_setup_irq(struct gp2a_data *gp2a)
+{
+ int rc = -EIO;
+ struct gp2a_platform_data *pdata = gp2a->pdata;
+ int irq;
+
+ gp2a_dbgmsg("start\n");
+
+ rc = gpio_request(pdata->p_out, "gpio_proximity_out");
+ if (rc < 0) {
+ pr_err("%s: gpio %d request failed (%d)\n",
+ __func__, pdata->p_out, rc);
+ return rc;
+ }
+
+ rc = gpio_direction_input(pdata->p_out);
+ if (rc < 0) {
+ pr_err("%s: failed to set gpio %d as input (%d)\n",
+ __func__, pdata->p_out, rc);
+ goto err_gpio_direction_input;
+ }
+
+ irq = gpio_to_irq(pdata->p_out);
+ rc = request_irq(irq,
+ gp2a_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "proximity_int",
+ gp2a);
+ if (rc < 0) {
+ pr_err("%s: request_irq(%d) failed for gpio %d (%d)\n",
+ __func__, irq,
+ pdata->p_out, rc);
+ goto err_request_irq;
+ }
+
+ /* start with interrupts disabled */
+ disable_irq(irq);
+ gp2a->irq = irq;
+
+ /* sync input device with proximity gpio pin default value */
+ gp2a_irq_handler(gp2a->irq, gp2a);
+
+ gp2a_dbgmsg("success\n");
+
+ goto done;
+
+err_request_irq:
+err_gpio_direction_input:
+ gpio_free(pdata->p_out);
+done:
+ return rc;
+}
+
+static int gp2a_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret = -ENODEV;
+ struct input_dev *input_dev;
+ struct gp2a_data *gp2a;
+ struct gp2a_platform_data *pdata = client->dev.platform_data;
+
+ if (!pdata) {
+ pr_err("%s: missing pdata!\n", __func__);
+ return ret;
+ }
+ if (!pdata->power || !pdata->light_adc_value) {
+ pr_err("%s: incomplete pdata!\n", __func__);
+ return ret;
+ }
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s: i2c functionality check failed!\n", __func__);
+ return ret;
+ }
+
+ gp2a = kzalloc(sizeof(struct gp2a_data), GFP_KERNEL);
+ if (!gp2a) {
+ pr_err("%s: failed to alloc memory for module data\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ gp2a->pdata = pdata;
+ gp2a->i2c_client = client;
+ i2c_set_clientdata(client, gp2a);
+
+
+ wake_lock_init(&gp2a->prx_wake_lock, WAKE_LOCK_SUSPEND,
+ "prx_wake_lock");
+ mutex_init(&gp2a->power_lock);
+
+ /* allocate proximity input_device */
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ pr_err("%s: could not allocate input device\n", __func__);
+ goto err_input_allocate_device_proximity;
+ }
+ gp2a->proximity_input_dev = input_dev;
+ input_set_drvdata(input_dev, gp2a);
+ input_dev->name = "proximity";
+ input_set_capability(input_dev, EV_ABS, ABS_DISTANCE);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0, 1, 0, 0);
+
+ ret = gp2a_setup_irq(gp2a);
+ if (ret) {
+ pr_err("%s: could not setup irq\n", __func__);
+ input_free_device(input_dev);
+ goto err_setup_irq;
+ }
+
+ gp2a_dbgmsg("registering proximity input device\n");
+ ret = input_register_device(input_dev);
+ if (ret < 0) {
+ pr_err("%s: could not register input device\n", __func__);
+ input_free_device(input_dev);
+ goto err_input_register_device_proximity;
+ }
+ ret = sysfs_create_group(&input_dev->dev.kobj,
+ &proximity_attribute_group);
+ if (ret) {
+ pr_err("%s: could not create sysfs group\n", __func__);
+ goto err_sysfs_create_group_proximity;
+ }
+
+ /* hrtimer settings. we poll for light values using a timer. */
+ hrtimer_init(&gp2a->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ gp2a->light_poll_delay = ns_to_ktime(200 * NSEC_PER_MSEC);
+ gp2a->timer.function = gp2a_timer_func;
+
+ /* the timer just fires off a work queue request. we need a thread
+ * to read the i2c (can be slow and blocking)
+ */
+ gp2a->wq = create_singlethread_workqueue("gp2a_wq");
+ if (!gp2a->wq) {
+ ret = -ENOMEM;
+ pr_err("%s: could not create workqueue\n", __func__);
+ goto err_create_workqueue;
+ }
+ /* this is the thread function we run on the work queue */
+ INIT_WORK(&gp2a->work_light, gp2a_work_func_light);
+
+ /* allocate lightsensor-level input_device */
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ pr_err("%s: could not allocate input device\n", __func__);
+ ret = -ENOMEM;
+ goto err_input_allocate_device_light;
+ }
+ input_set_drvdata(input_dev, gp2a);
+ input_dev->name = "lightsensor-level";
+ input_set_capability(input_dev, EV_ABS, ABS_MISC);
+ input_set_abs_params(input_dev, ABS_MISC, 0, 1023, 8, 0);
+
+ gp2a_dbgmsg("registering lightsensor-level input device\n");
+ ret = input_register_device(input_dev);
+ if (ret < 0) {
+ pr_err("%s: could not register input device\n", __func__);
+ input_free_device(input_dev);
+ goto err_input_register_device_light;
+ }
+ gp2a->light_input_dev = input_dev;
+ ret = sysfs_create_group(&input_dev->dev.kobj,
+ &light_attribute_group);
+ if (ret) {
+ pr_err("%s: could not create sysfs group\n", __func__);
+ goto err_sysfs_create_group_light;
+ }
+ goto done;
+
+ /* error, unwind it all */
+err_sysfs_create_group_light:
+ input_unregister_device(gp2a->light_input_dev);
+err_input_register_device_light:
+err_input_allocate_device_light:
+ destroy_workqueue(gp2a->wq);
+err_create_workqueue:
+ sysfs_remove_group(&gp2a->proximity_input_dev->dev.kobj,
+ &proximity_attribute_group);
+err_sysfs_create_group_proximity:
+ input_unregister_device(gp2a->proximity_input_dev);
+err_input_register_device_proximity:
+ free_irq(gp2a->irq, gp2a);
+ gpio_free(gp2a->pdata->p_out);
+err_setup_irq:
+err_input_allocate_device_proximity:
+ mutex_destroy(&gp2a->power_lock);
+ wake_lock_destroy(&gp2a->prx_wake_lock);
+ kfree(gp2a);
+done:
+ return ret;
+}
+
+static int gp2a_suspend(struct device *dev)
+{
+ /* We disable power only if proximity is disabled. If proximity
+ * is enabled, we leave power on because proximity is allowed
+ * to wake up device. We remove power without changing
+ * gp2a->power_state because we use that state in resume
+ */
+ struct i2c_client *client = to_i2c_client(dev);
+ struct gp2a_data *gp2a = i2c_get_clientdata(client);
+ if (gp2a->power_state & LIGHT_ENABLED)
+ gp2a_light_disable(gp2a);
+ if (gp2a->power_state == LIGHT_ENABLED)
+ gp2a->pdata->power(false);
+ return 0;
+}
+
+static int gp2a_resume(struct device *dev)
+{
+ /* Turn power back on if we were before suspend. */
+ struct i2c_client *client = to_i2c_client(dev);
+ struct gp2a_data *gp2a = i2c_get_clientdata(client);
+ if (gp2a->power_state == LIGHT_ENABLED)
+ gp2a->pdata->power(true);
+ if (gp2a->power_state & LIGHT_ENABLED)
+ gp2a_light_enable(gp2a);
+ return 0;
+}
+
+static int gp2a_i2c_remove(struct i2c_client *client)
+{
+ struct gp2a_data *gp2a = i2c_get_clientdata(client);
+ sysfs_remove_group(&gp2a->light_input_dev->dev.kobj,
+ &light_attribute_group);
+ sysfs_remove_group(&gp2a->proximity_input_dev->dev.kobj,
+ &proximity_attribute_group);
+ free_irq(gp2a->irq, gp2a);
+ destroy_workqueue(gp2a->wq);
+ input_unregister_device(gp2a->light_input_dev);
+ input_unregister_device(gp2a->proximity_input_dev);
+ gpio_free(gp2a->pdata->p_out);
+ if (gp2a->power_state) {
+ gp2a->power_state = 0;
+ if (gp2a->power_state & LIGHT_ENABLED)
+ gp2a_light_disable(gp2a);
+ gp2a->pdata->power(false);
+ }
+ mutex_destroy(&gp2a->power_lock);
+ wake_lock_destroy(&gp2a->prx_wake_lock);
+ kfree(gp2a);
+ return 0;
+}
+
+static const struct i2c_device_id gp2a_device_id[] = {
+ {"gp2a", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, gp2a_device_id);
+
+static const struct dev_pm_ops gp2a_pm_ops = {
+ .suspend = gp2a_suspend,
+ .resume = gp2a_resume
+};
+
+static struct i2c_driver gp2a_i2c_driver = {
+ .driver = {
+ .name = "gp2a",
+ .owner = THIS_MODULE,
+ .pm = &gp2a_pm_ops
+ },
+ .probe = gp2a_i2c_probe,
+ .remove = gp2a_i2c_remove,
+ .id_table = gp2a_device_id,
+};
+
+
+static int __init gp2a_init(void)
+{
+ return i2c_add_driver(&gp2a_i2c_driver);
+}
+
+static void __exit gp2a_exit(void)
+{
+ i2c_del_driver(&gp2a_i2c_driver);
+}
+
+module_init(gp2a_init);
+module_exit(gp2a_exit);
+
+MODULE_AUTHOR("mjchen@sta.samsung.com");
+MODULE_DESCRIPTION("Optical Sensor driver for gp2ap002a00f");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 4104103..86bc7a7 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -272,6 +272,18 @@ config TOUCHSCREEN_MCS5000
To compile this driver as a module, choose M here: the
module will be called mcs5000_ts.
+config TOUCHSCREEN_MMS
+ tristate "MELFAS MMS-series touchscreen"
+ depends on I2C
+ help
+ Say Y here if you have a MELFAS MMS-series touchscreen controller
+ chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mms_ts.
+
config TOUCHSCREEN_MTOUCH
tristate "MicroTouch serial touchscreens"
select SERIO
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 0738f19..ce1fbe2 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_TOUCHSCREEN_LPC32XX) += lpc32xx_ts.o
obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o
obj-$(CONFIG_TOUCHSCREEN_MC13783) += mc13783_ts.o
obj-$(CONFIG_TOUCHSCREEN_MCS5000) += mcs5000_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MMS) += mms_ts.o
obj-$(CONFIG_TOUCHSCREEN_MIGOR) += migor_ts.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
diff --git a/drivers/input/touchscreen/atmel_mxt_ts.c b/drivers/input/touchscreen/atmel_mxt_ts.c
index 1e61387..d4e1515 100644
--- a/drivers/input/touchscreen/atmel_mxt_ts.c
+++ b/drivers/input/touchscreen/atmel_mxt_ts.c
@@ -829,6 +829,10 @@ static int mxt_initialize(struct mxt_data *data)
MXT_COMMAND_RESET, 1);
msleep(MXT_RESET_TIME);
+ error = mxt_make_highchg(data);
+ if (error)
+ return error;
+
/* Update matrix size at info struct */
error = mxt_read_reg(client, MXT_MATRIX_X_SIZE, &val);
if (error)
diff --git a/drivers/input/touchscreen/mms_ts.c b/drivers/input/touchscreen/mms_ts.c
new file mode 100755
index 0000000..a6ec6eb
--- /dev/null
+++ b/drivers/input/touchscreen/mms_ts.c
@@ -0,0 +1,961 @@
+/*
+ * mms_ts.c - Touchscreen driver for Melfas MMS-series touch controllers
+ *
+ * Copyright (C) 2011 Google Inc.
+ * Author: Dima Zavin <dima@android.com>
+ * Simon Wilson <simonwilson@google.com>
+ *
+ * ISP reflashing code based on original code from Melfas.
+ *
+ * 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.
+ *
+ */
+
+//#define DEBUG
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/earlysuspend.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+
+#include <linux/platform_data/mms_ts.h>
+
+#include <asm/unaligned.h>
+
+#define MAX_FINGERS 10
+#define MAX_WIDTH 30
+#define MAX_PRESSURE 255
+
+/* Registers */
+#define MMS_MODE_CONTROL 0x01
+#define MMS_XYRES_HI 0x02
+#define MMS_XRES_LO 0x03
+#define MMS_YRES_LO 0x04
+
+#define MMS_INPUT_EVENT_PKT_SZ 0x0F
+#define MMS_INPUT_EVENT0 0x10
+#define FINGER_EVENT_SZ 6
+
+#define MMS_TSP_REVISION 0xF0
+#define MMS_HW_REVISION 0xF1
+#define MMS_COMPAT_GROUP 0xF2
+#define MMS_FW_VERSION 0xF3
+
+enum {
+ ISP_MODE_FLASH_ERASE = 0x59F3,
+ ISP_MODE_FLASH_WRITE = 0x62CD,
+ ISP_MODE_FLASH_READ = 0x6AC9,
+};
+
+/* each address addresses 4-byte words */
+#define ISP_MAX_FW_SIZE (0x1F00 * 4)
+#define ISP_IC_INFO_ADDR 0x1F00
+
+static bool mms_force_reflash = false;
+module_param_named(force_reflash, mms_force_reflash, bool, S_IWUSR | S_IRUGO);
+
+static bool mms_flash_from_probe;
+module_param_named(flash_from_probe, mms_flash_from_probe, bool,
+ S_IWUSR | S_IRUGO);
+
+static bool mms_die_on_flash_fail = true;
+module_param_named(die_on_flash_fail, mms_die_on_flash_fail, bool,
+ S_IWUSR | S_IRUGO);
+
+struct mms_ts_info {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ char phys[32];
+
+ int max_x;
+ int max_y;
+
+ bool invert_x;
+ bool invert_y;
+
+ int irq;
+
+ struct mms_ts_platform_data *pdata;
+
+ char *fw_name;
+ struct completion init_done;
+ struct early_suspend early_suspend;
+
+ /* protects the enabled flag */
+ struct mutex lock;
+ bool enabled;
+};
+
+struct mms_fw_image {
+ __le32 hdr_len;
+ __le32 data_len;
+ __le32 fw_ver;
+ __le32 hdr_ver;
+ u8 data[0];
+} __attribute__ ((packed));
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mms_ts_early_suspend(struct early_suspend *h);
+static void mms_ts_late_resume(struct early_suspend *h);
+#endif
+
+static irqreturn_t mms_ts_interrupt(int irq, void *dev_id)
+{
+ struct mms_ts_info *info = dev_id;
+ struct i2c_client *client = info->client;
+ u8 buf[MAX_FINGERS*FINGER_EVENT_SZ] = { 0 };
+ int ret;
+ int i;
+ int sz;
+ u8 reg = MMS_INPUT_EVENT0;
+ struct i2c_msg msg[] = {
+ {
+ .addr = client->addr,
+ .flags = 0,
+ .buf = &reg,
+ .len = 1,
+ }, {
+ .addr = client->addr,
+ .flags = I2C_M_RD,
+ .buf = buf,
+ },
+ };
+
+ sz = i2c_smbus_read_byte_data(client, MMS_INPUT_EVENT_PKT_SZ);
+ if (sz < 0) {
+ dev_err(&client->dev, "%s bytes=%d\n", __func__, sz);
+ goto out;
+ }
+ dev_dbg(&client->dev, "bytes available: %d\n", sz);
+ BUG_ON(sz > MAX_FINGERS*FINGER_EVENT_SZ);
+ if (sz == 0)
+ goto out;
+
+ msg[1].len = sz;
+ ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
+ if (ret != ARRAY_SIZE(msg)) {
+ dev_err(&client->dev,
+ "failed to read %d bytes of touch data (%d)\n",
+ sz, ret);
+ goto out;
+ }
+
+#if defined(VERBOSE_DEBUG)
+ print_hex_dump(KERN_DEBUG, "mms_ts raw: ",
+ DUMP_PREFIX_OFFSET, 32, 1, buf, sz, false);
+#endif
+ for (i = 0; i < sz; i += FINGER_EVENT_SZ) {
+ u8 *tmp = &buf[i];
+ int id = (tmp[0] & 0xf) - 1;
+ int x = tmp[2] | ((tmp[1] & 0xf) << 8);
+ int y = tmp[3] | (((tmp[1] >> 4) & 0xf) << 8);
+
+ if (info->invert_x) {
+ x = info->max_x - x;
+ if (x < 0)
+ x = 0;
+ }
+ if (info->invert_y) {
+ y = info->max_y - y;
+ if (y < 0)
+ y = 0;
+ }
+
+ if ((tmp[0] & 0x80) == 0) {
+ dev_dbg(&client->dev, "finger %d up\n", id);
+ input_mt_slot(info->input_dev, id);
+ input_mt_report_slot_state(info->input_dev,
+ MT_TOOL_FINGER, false);
+ continue;
+ }
+
+ input_mt_slot(info->input_dev, id);
+ input_mt_report_slot_state(info->input_dev,
+ MT_TOOL_FINGER, true);
+ input_report_abs(info->input_dev, ABS_MT_TOUCH_MAJOR, tmp[4]);
+ input_report_abs(info->input_dev, ABS_MT_PRESSURE, tmp[5]);
+ input_report_abs(info->input_dev, ABS_MT_POSITION_X, x);
+ input_report_abs(info->input_dev, ABS_MT_POSITION_Y, y);
+
+ dev_dbg(&client->dev,
+ "finger %d: x=%d y=%d p=%d w=%d\n", id, x, y, tmp[5],
+ tmp[4]);
+ }
+
+ input_sync(info->input_dev);
+
+out:
+ return IRQ_HANDLED;
+}
+
+static void hw_reboot(struct mms_ts_info *info, bool bootloader)
+{
+ gpio_direction_output(info->pdata->gpio_vdd_en, 0);
+ gpio_direction_output(info->pdata->gpio_sda, bootloader ? 0 : 1);
+ gpio_direction_output(info->pdata->gpio_scl, bootloader ? 0 : 1);
+ gpio_direction_output(info->pdata->gpio_resetb, 0);
+ msleep(30);
+ gpio_set_value(info->pdata->gpio_vdd_en, 1);
+ msleep(30);
+
+ if (bootloader) {
+ gpio_set_value(info->pdata->gpio_scl, 0);
+ gpio_set_value(info->pdata->gpio_sda, 1);
+ } else {
+ gpio_set_value(info->pdata->gpio_resetb, 1);
+ gpio_direction_input(info->pdata->gpio_resetb);
+ gpio_direction_input(info->pdata->gpio_scl);
+ gpio_direction_input(info->pdata->gpio_sda);
+ }
+ msleep(40);
+}
+
+static inline void hw_reboot_bootloader(struct mms_ts_info *info)
+{
+ hw_reboot(info, true);
+}
+
+static inline void hw_reboot_normal(struct mms_ts_info *info)
+{
+ hw_reboot(info, false);
+}
+
+static inline void mms_pwr_on_reset(struct mms_ts_info *info)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(info->client->dev.parent);
+
+ if (!info->pdata->mux_fw_flash) {
+ dev_info(&info->client->dev,
+ "missing platform data, can't do power-on-reset\n");
+ return;
+ }
+
+ i2c_lock_adapter(adapter);
+ info->pdata->mux_fw_flash(true);
+
+ gpio_direction_output(info->pdata->gpio_vdd_en, 0);
+ gpio_direction_output(info->pdata->gpio_sda, 1);
+ gpio_direction_output(info->pdata->gpio_scl, 1);
+ gpio_direction_output(info->pdata->gpio_resetb, 1);
+ msleep(50);
+ gpio_direction_output(info->pdata->gpio_vdd_en, 1);
+ msleep(50);
+
+ info->pdata->mux_fw_flash(false);
+ i2c_unlock_adapter(adapter);
+
+ /* TODO: Seems long enough for the firmware to boot.
+ * Find the right value */
+ msleep(250);
+}
+
+static void isp_toggle_clk(struct mms_ts_info *info, int start_lvl, int end_lvl,
+ int hold_us)
+{
+ gpio_set_value(info->pdata->gpio_scl, start_lvl);
+ udelay(hold_us);
+ gpio_set_value(info->pdata->gpio_scl, end_lvl);
+ udelay(hold_us);
+}
+
+/* 1 <= cnt <= 32 bits to write */
+static void isp_send_bits(struct mms_ts_info *info, u32 data, int cnt)
+{
+ gpio_direction_output(info->pdata->gpio_resetb, 0);
+ gpio_direction_output(info->pdata->gpio_scl, 0);
+ gpio_direction_output(info->pdata->gpio_sda, 0);
+
+ /* clock out the bits, msb first */
+ while (cnt--) {
+ gpio_set_value(info->pdata->gpio_sda, (data >> cnt) & 1);
+ udelay(3);
+ isp_toggle_clk(info, 1, 0, 3);
+ }
+}
+
+/* 1 <= cnt <= 32 bits to read */
+static u32 isp_recv_bits(struct mms_ts_info *info, int cnt)
+{
+ u32 data = 0;
+
+ gpio_direction_output(info->pdata->gpio_resetb, 0);
+ gpio_direction_output(info->pdata->gpio_scl, 0);
+ gpio_set_value(info->pdata->gpio_sda, 0);
+ gpio_direction_input(info->pdata->gpio_sda);
+
+ /* clock in the bits, msb first */
+ while (cnt--) {
+ isp_toggle_clk(info, 0, 1, 1);
+ data = (data << 1) | (!!gpio_get_value(info->pdata->gpio_sda));
+ }
+
+ gpio_direction_output(info->pdata->gpio_sda, 0);
+ return data;
+}
+
+static void isp_enter_mode(struct mms_ts_info *info, u32 mode)
+{
+ int cnt;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ gpio_direction_output(info->pdata->gpio_resetb, 0);
+ gpio_direction_output(info->pdata->gpio_scl, 0);
+ gpio_direction_output(info->pdata->gpio_sda, 1);
+
+ mode &= 0xffff;
+ for (cnt = 15; cnt >= 0; cnt--) {
+ gpio_set_value(info->pdata->gpio_resetb, (mode >> cnt) & 1);
+ udelay(3);
+ isp_toggle_clk(info, 1, 0, 3);
+ }
+
+ gpio_set_value(info->pdata->gpio_resetb, 0);
+ local_irq_restore(flags);
+}
+
+static void isp_exit_mode(struct mms_ts_info *info)
+{
+ int i;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ gpio_direction_output(info->pdata->gpio_resetb, 0);
+ udelay(3);
+
+ for (i = 0; i < 10; i++)
+ isp_toggle_clk(info, 1, 0, 3);
+ local_irq_restore(flags);
+}
+
+static void flash_set_address(struct mms_ts_info *info, u16 addr)
+{
+ /* Only 13 bits of addr are valid.
+ * The addr is in bits 13:1 of cmd */
+ isp_send_bits(info, (u32)(addr & 0x1fff) << 1, 18);
+}
+
+static void flash_erase(struct mms_ts_info *info)
+{
+ isp_enter_mode(info, ISP_MODE_FLASH_ERASE);
+
+ gpio_direction_output(info->pdata->gpio_resetb, 0);
+ gpio_direction_output(info->pdata->gpio_scl, 0);
+ gpio_direction_output(info->pdata->gpio_sda, 1);
+
+ /* 4 clock cycles with different timings for the erase to
+ * get processed, clk is already 0 from above */
+ udelay(7);
+ isp_toggle_clk(info, 1, 0, 3);
+ udelay(7);
+ isp_toggle_clk(info, 1, 0, 3);
+ usleep_range(25000, 35000);
+ isp_toggle_clk(info, 1, 0, 3);
+ usleep_range(150, 200);
+ isp_toggle_clk(info, 1, 0, 3);
+
+ gpio_set_value(info->pdata->gpio_sda, 0);
+
+ isp_exit_mode(info);
+}
+
+static u32 flash_readl(struct mms_ts_info *info, u16 addr)
+{
+ int i;
+ u32 val;
+ unsigned long flags;
+
+ local_irq_save(flags);
+ isp_enter_mode(info, ISP_MODE_FLASH_READ);
+ flash_set_address(info, addr);
+
+ gpio_direction_output(info->pdata->gpio_scl, 0);
+ gpio_direction_output(info->pdata->gpio_sda, 0);
+ udelay(40);
+
+ /* data load cycle */
+ for (i = 0; i < 6; i++)
+ isp_toggle_clk(info, 1, 0, 10);
+
+ val = isp_recv_bits(info, 32);
+ isp_exit_mode(info);
+ local_irq_restore(flags);
+
+ return val;
+}
+
+static void flash_writel(struct mms_ts_info *info, u16 addr, u32 val)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ isp_enter_mode(info, ISP_MODE_FLASH_WRITE);
+ flash_set_address(info, addr);
+ isp_send_bits(info, val, 32);
+
+ gpio_direction_output(info->pdata->gpio_sda, 1);
+ /* 6 clock cycles with different timings for the data to get written
+ * into flash */
+ isp_toggle_clk(info, 0, 1, 3);
+ isp_toggle_clk(info, 0, 1, 3);
+ isp_toggle_clk(info, 0, 1, 6);
+ isp_toggle_clk(info, 0, 1, 12);
+ isp_toggle_clk(info, 0, 1, 3);
+ isp_toggle_clk(info, 0, 1, 3);
+
+ isp_toggle_clk(info, 1, 0, 1);
+
+ gpio_direction_output(info->pdata->gpio_sda, 0);
+ isp_exit_mode(info);
+ local_irq_restore(flags);
+ usleep_range(300, 400);
+}
+
+static bool flash_is_erased(struct mms_ts_info *info)
+{
+ struct i2c_client *client = info->client;
+ u32 val;
+ u16 addr;
+
+ for (addr = 0; addr < (ISP_MAX_FW_SIZE / 4); addr++) {
+ udelay(40);
+ val = flash_readl(info, addr);
+
+ if (val != 0xffffffff) {
+ dev_dbg(&client->dev,
+ "addr 0x%x not erased: 0x%08x != 0xffffffff\n",
+ addr, val);
+ return false;
+ }
+ }
+ return true;
+}
+
+static int fw_write_image(struct mms_ts_info *info, const u8 *data, size_t len)
+{
+ struct i2c_client *client = info->client;
+ u16 addr = 0;
+
+ for (addr = 0; addr < (len / 4); addr++, data += 4) {
+ u32 val = get_unaligned_le32(data);
+ u32 verify_val;
+ int retries = 3;
+
+ while (retries--) {
+ flash_writel(info, addr, val);
+ verify_val = flash_readl(info, addr);
+ if (val == verify_val)
+ break;
+ dev_err(&client->dev,
+ "mismatch @ addr 0x%x: 0x%x != 0x%x\n",
+ addr, verify_val, val);
+ hw_reboot_bootloader(info);
+ continue;
+ }
+ if (retries < 0)
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int fw_download(struct mms_ts_info *info, const u8 *data, size_t len)
+{
+ struct i2c_client *client = info->client;
+ u32 val;
+ int ret = 0;
+
+ if (len % 4) {
+ dev_err(&client->dev,
+ "fw image size (%d) must be a multiple of 4 bytes\n",
+ len);
+ return -EINVAL;
+ } else if (len > ISP_MAX_FW_SIZE) {
+ dev_err(&client->dev,
+ "fw image is too big, %d > %d\n", len, ISP_MAX_FW_SIZE);
+ return -EINVAL;
+ }
+
+ dev_info(&client->dev, "fw download start\n");
+
+ gpio_direction_output(info->pdata->gpio_vdd_en, 0);
+ gpio_direction_output(info->pdata->gpio_sda, 0);
+ gpio_direction_output(info->pdata->gpio_scl, 0);
+ gpio_direction_output(info->pdata->gpio_resetb, 0);
+
+ hw_reboot_bootloader(info);
+
+ val = flash_readl(info, ISP_IC_INFO_ADDR);
+ dev_info(&client->dev, "IC info: 0x%02x (%x)\n", val & 0xff, val);
+
+ dev_info(&client->dev, "fw erase...\n");
+ flash_erase(info);
+ if (!flash_is_erased(info)) {
+ ret = -ENXIO;
+ goto err;
+ }
+
+ dev_info(&client->dev, "fw write...\n");
+ /* XXX: what does this do?! */
+ flash_writel(info, ISP_IC_INFO_ADDR, 0xffffff00 | (val & 0xff));
+ usleep_range(1000, 1500);
+ ret = fw_write_image(info, data, len);
+ if (ret)
+ goto err;
+ usleep_range(1000, 1500);
+
+ hw_reboot_normal(info);
+ usleep_range(1000, 1500);
+ dev_info(&client->dev, "fw download done...\n");
+ return 0;
+
+err:
+ dev_err(&client->dev, "fw download failed...\n");
+ hw_reboot_normal(info);
+ return ret;
+}
+
+static int get_fw_version(struct mms_ts_info *info)
+{
+ int ret;
+ int retries = 3;
+
+ /* this seems to fail sometimes after a reset.. retry a few times */
+ do {
+ ret = i2c_smbus_read_byte_data(info->client, MMS_FW_VERSION);
+ } while (ret < 0 && retries-- > 0);
+
+ return ret;
+}
+
+static int mms_ts_enable(struct mms_ts_info *info)
+{
+ mutex_lock(&info->lock);
+ if (info->enabled)
+ goto out;
+ /* wake up the touch controller. */
+ i2c_smbus_write_byte_data(info->client, 0, 0);
+ usleep_range(3000, 5000);
+ info->enabled = true;
+ enable_irq(info->irq);
+out:
+ mutex_unlock(&info->lock);
+ return 0;
+}
+
+static int mms_ts_disable(struct mms_ts_info *info)
+{
+ mutex_lock(&info->lock);
+ if (!info->enabled)
+ goto out;
+ disable_irq(info->irq);
+ i2c_smbus_write_byte_data(info->client, MMS_MODE_CONTROL, 0);
+ usleep_range(10000, 12000);
+ info->enabled = false;
+out:
+ mutex_unlock(&info->lock);
+ return 0;
+}
+
+static int mms_ts_input_open(struct input_dev *dev)
+{
+ struct mms_ts_info *info = input_get_drvdata(dev);
+ int ret;
+
+ ret = wait_for_completion_interruptible_timeout(&info->init_done,
+ msecs_to_jiffies(90 * MSEC_PER_SEC));
+
+ if (ret > 0) {
+ if (info->irq != -1)
+ ret = mms_ts_enable(info);
+ else
+ ret = -ENXIO;
+ } else if (ret < 0) {
+ dev_err(&dev->dev,
+ "error while waiting for device to init (%d)\n", ret);
+ ret = -ENXIO;
+ } else if (ret == 0) {
+ dev_err(&dev->dev,
+ "timedout while waiting for device to init\n");
+ ret = -ENXIO;
+ }
+
+ return ret;
+}
+
+static void mms_ts_input_close(struct input_dev *dev)
+{
+ struct mms_ts_info *info = input_get_drvdata(dev);
+ mms_ts_disable(info);
+}
+
+static int mms_ts_finish_config(struct mms_ts_info *info)
+{
+ struct i2c_client *client = info->client;
+ int ret;
+
+ ret = request_threaded_irq(client->irq, NULL, mms_ts_interrupt,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "mms_ts", info);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_req_irq;
+ }
+ disable_irq(client->irq);
+
+ info->irq = client->irq;
+ barrier();
+
+ dev_info(&client->dev,
+ "Melfas MMS-series touch controller initialized\n");
+
+ complete_all(&info->init_done);
+ return 0;
+
+err_req_irq:
+ return ret;
+}
+
+static void mms_ts_fw_load(const struct firmware *fw, void *context)
+{
+ struct mms_ts_info *info = context;
+ struct i2c_client *client = info->client;
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ int ret = 0;
+ int ver;
+ int retries = 3;
+ struct mms_fw_image *fw_img;
+
+ ver = get_fw_version(info);
+ if (ver < 0) {
+ ver = 0;
+ dev_err(&client->dev,
+ "can't read version, controller dead? forcing reflash");
+ }
+
+ if (!fw) {
+ dev_info(&client->dev, "could not find firmware file '%s'\n",
+ info->fw_name);
+ goto done;
+ }
+
+ fw_img = (struct mms_fw_image *)fw->data;
+ if (fw_img->hdr_len != sizeof(struct mms_fw_image) ||
+ fw_img->data_len + fw_img->hdr_len != fw->size ||
+ fw_img->hdr_ver != 0x1) {
+ dev_err(&client->dev,
+ "firmware image '%s' invalid, may continue\n",
+ info->fw_name);
+ goto err;
+ }
+
+ if (ver == fw_img->fw_ver && !mms_force_reflash) {
+ dev_info(&client->dev,
+ "fw version 0x%02x already present\n", ver);
+ goto done;
+ }
+
+ dev_info(&client->dev, "need fw update (0x%02x != 0x%02x)\n",
+ ver, fw_img->fw_ver);
+
+ if (!info->pdata || !info->pdata->mux_fw_flash) {
+ dev_err(&client->dev,
+ "fw cannot be updated, missing platform data\n");
+ goto err;
+ }
+
+ while (retries--) {
+ i2c_lock_adapter(adapter);
+ info->pdata->mux_fw_flash(true);
+
+ ret = fw_download(info, fw_img->data, fw_img->data_len);
+
+ info->pdata->mux_fw_flash(false);
+ i2c_unlock_adapter(adapter);
+
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "error updating firmware to version 0x%02x\n",
+ fw_img->fw_ver);
+ if (retries)
+ dev_err(&client->dev, "retrying flashing\n");
+ continue;
+ }
+
+ ver = get_fw_version(info);
+ if (ver == fw_img->fw_ver) {
+ dev_info(&client->dev,
+ "fw update done. ver = 0x%02x\n", ver);
+ goto done;
+ } else {
+ dev_err(&client->dev,
+ "ERROR: fw update succeeded, but fw version is still wrong (0x%x != 0x%x)\n",
+ ver, fw_img->fw_ver);
+ }
+ if (retries)
+ dev_err(&client->dev, "retrying flashing\n");
+ }
+
+ dev_err(&client->dev, "could not flash firmware, ran out of retries\n");
+ BUG_ON(mms_die_on_flash_fail);
+
+err:
+ /* complete anyway, so open() doesn't get blocked */
+ complete_all(&info->init_done);
+ goto out;
+
+done:
+ mms_ts_finish_config(info);
+out:
+ release_firmware(fw);
+}
+
+static int __devinit mms_ts_config(struct mms_ts_info *info, bool nowait)
+{
+ struct i2c_client *client = info->client;
+ int ret = 0;
+ const char *filename = info->pdata->fw_name ?: "mms144_ts.fw";
+
+ mms_pwr_on_reset(info);
+
+ if (nowait) {
+ const struct firmware *fw;
+ info->fw_name = kasprintf(GFP_KERNEL, "melfas/%s", filename);
+ ret = request_firmware(&fw, info->fw_name, &client->dev);
+ if (ret) {
+ dev_err(&client->dev,
+ "error requesting built-in firmware\n");
+ goto out;
+ }
+ mms_ts_fw_load(fw, info);
+ } else {
+ info->fw_name = kstrdup(filename, GFP_KERNEL);
+ ret = request_firmware_nowait(THIS_MODULE, true, info->fw_name,
+ &client->dev, GFP_KERNEL,
+ info, mms_ts_fw_load);
+ if (ret)
+ dev_err(&client->dev,
+ "cannot schedule firmware update (%d)\n", ret);
+ }
+
+out:
+ return ret;
+}
+
+static int __devinit mms_ts_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct mms_ts_info *info;
+ struct input_dev *input_dev;
+ int ret = 0;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
+ return -EIO;
+
+ info = kzalloc(sizeof(struct mms_ts_info), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!info || !input_dev) {
+ dev_err(&client->dev, "Failed to allocate memory\n");
+ goto err_alloc;
+ }
+
+ info->client = client;
+ info->input_dev = input_dev;
+ info->pdata = client->dev.platform_data;
+ init_completion(&info->init_done);
+ info->irq = -1;
+ mutex_init(&info->lock);
+
+ if (info->pdata) {
+ info->max_x = info->pdata->max_x;
+ info->max_y = info->pdata->max_y;
+ info->invert_x = info->pdata->invert_x;
+ info->invert_y = info->pdata->invert_y;
+ } else {
+ info->max_x = 720;
+ info->max_y = 1280;
+ }
+
+ input_mt_init_slots(input_dev, MAX_FINGERS);
+
+ snprintf(info->phys, sizeof(info->phys),
+ "%s/input0", dev_name(&client->dev));
+ input_dev->name = "Melfas MMSxxx Touchscreen";
+ input_dev->phys = info->phys;
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+ input_dev->open = mms_ts_input_open;
+ input_dev->close = mms_ts_input_close;
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(INPUT_PROP_DIRECT, input_dev->propbit);
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, MAX_WIDTH, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_PRESSURE, 0, MAX_PRESSURE, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, info->max_x, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, info->max_y, 0, 0);
+
+ input_set_drvdata(input_dev, info);
+
+ ret = input_register_device(input_dev);
+ if (ret) {
+ dev_err(&client->dev, "failed to register input dev (%d)\n",
+ ret);
+ goto err_reg_input_dev;
+ }
+
+ i2c_set_clientdata(client, info);
+
+ ret = mms_ts_config(info, mms_flash_from_probe);
+ if (ret) {
+ dev_err(&client->dev, "failed to initialize (%d)\n", ret);
+ goto err_config;
+ }
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ info->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ info->early_suspend.suspend = mms_ts_early_suspend;
+ info->early_suspend.resume = mms_ts_late_resume;
+ register_early_suspend(&info->early_suspend);
+#endif
+
+ return 0;
+
+err_config:
+ input_unregister_device(input_dev);
+ input_dev = NULL;
+err_reg_input_dev:
+err_alloc:
+ input_free_device(input_dev);
+ kfree(info->fw_name);
+ kfree(info);
+ return ret;
+}
+
+static int __devexit mms_ts_remove(struct i2c_client *client)
+{
+ struct mms_ts_info *info = i2c_get_clientdata(client);
+
+ if (info->irq >= 0)
+ free_irq(info->irq, info);
+ input_unregister_device(info->input_dev);
+ kfree(info->fw_name);
+ kfree(info);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM) || defined(CONFIG_HAS_EARLYSUSPEND)
+static int mms_ts_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mms_ts_info *info = i2c_get_clientdata(client);
+ int i;
+
+ /* TODO: turn off the power (set vdd_en to 0) to the touchscreen
+ * on suspend
+ */
+
+ mutex_lock(&info->input_dev->mutex);
+ if (!info->input_dev->users)
+ goto out;
+
+ mms_ts_disable(info);
+ for (i = 0; i < MAX_FINGERS; i++) {
+ input_mt_slot(info->input_dev, i);
+ input_mt_report_slot_state(info->input_dev, MT_TOOL_FINGER,
+ false);
+ }
+ input_sync(info->input_dev);
+
+out:
+ mutex_unlock(&info->input_dev->mutex);
+ return 0;
+}
+
+static int mms_ts_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mms_ts_info *info = i2c_get_clientdata(client);
+ int ret = 0;
+
+ mutex_lock(&info->input_dev->mutex);
+ if (info->input_dev->users)
+ ret = mms_ts_enable(info);
+ mutex_unlock(&info->input_dev->mutex);
+
+ return ret;
+}
+#endif
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void mms_ts_early_suspend(struct early_suspend *h)
+{
+ struct mms_ts_info *info;
+ info = container_of(h, struct mms_ts_info, early_suspend);
+ mms_ts_suspend(&info->client->dev);
+}
+
+static void mms_ts_late_resume(struct early_suspend *h)
+{
+ struct mms_ts_info *info;
+ info = container_of(h, struct mms_ts_info, early_suspend);
+ mms_ts_resume(&info->client->dev);
+}
+#endif
+
+#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
+static const struct dev_pm_ops mms_ts_pm_ops = {
+ .suspend = mms_ts_suspend,
+ .resume = mms_ts_resume,
+};
+#endif
+
+static const struct i2c_device_id mms_ts_id[] = {
+ { "mms_ts", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mms_ts_id);
+
+static struct i2c_driver mms_ts_driver = {
+ .probe = mms_ts_probe,
+ .remove = __devexit_p(mms_ts_remove),
+ .driver = {
+ .name = "mms_ts",
+#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
+ .pm = &mms_ts_pm_ops,
+#endif
+ },
+ .id_table = mms_ts_id,
+};
+
+static int __init mms_ts_init(void)
+{
+ return i2c_add_driver(&mms_ts_driver);
+}
+
+static void __exit mms_ts_exit(void)
+{
+ i2c_del_driver(&mms_ts_driver);
+}
+
+module_init(mms_ts_init);
+module_exit(mms_ts_exit);
+
+/* Module information */
+MODULE_DESCRIPTION("Touchscreen driver for Melfas MMS-series controllers");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c
index 212a338..130d950 100644
--- a/drivers/mfd/omap-usb-host.c
+++ b/drivers/mfd/omap-usb-host.c
@@ -762,7 +762,8 @@ static void omap_usbhs_init(struct device *dev)
reg |= (OMAP_UHH_HOSTCONFIG_INCR4_BURST_EN
| OMAP_UHH_HOSTCONFIG_INCR8_BURST_EN
| OMAP_UHH_HOSTCONFIG_INCR16_BURST_EN);
- reg |= OMAP4_UHH_HOSTCONFIG_APP_START_CLK;
+
+ /* Keep ENA_INCR_ALIGN = 0: Known to cause OCP delays */
reg &= ~OMAP_UHH_HOSTCONFIG_INCRX_ALIGN_EN;
if (is_omap_usbhs_rev1(omap)) {
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index aa77f02..9b3b987 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -92,6 +92,8 @@ static struct task_struct *task;
static struct completion irq_event;
static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0);
+static u8 vbatmin_hi_threshold;
+
static int twl6030_irq_pm_notifier(struct notifier_block *notifier,
unsigned long pm_event, void *unused)
{
@@ -223,8 +225,15 @@ static irqreturn_t handle_twl6030_pih(int irq, void *devid)
*/
static irqreturn_t handle_twl6030_vlow(int irq, void *unused)
{
- pr_info("handle_twl6030_vlow: kernel_power_off()\n");
+ pr_err("twl6030: BAT_VLOW interrupt; threshold=%dmV\n",
+ 2300 + (vbatmin_hi_threshold - 0b110) * 50);
+
+#if 1 /* temporary */
+ WARN_ON_ONCE(1);
+#else
+ pr_emerg("handle_twl6030_vlow: kernel_power_off()\n");
kernel_power_off();
+#endif
return IRQ_HANDLED;
}
@@ -407,6 +416,9 @@ int twl6030_vlow_init(int vlow_irq)
return status;
}
+ twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &vbatmin_hi_threshold,
+ TWL6030_VBATMIN_HI_THRESHOLD);
+
/* install an irq handler for vlow */
status = request_threaded_irq(vlow_irq, NULL, handle_twl6030_vlow,
IRQF_ONESHOT,
diff --git a/drivers/mfd/twl6030-madc.c b/drivers/mfd/twl6030-madc.c
index f537ba5..ebff3d7 100644
--- a/drivers/mfd/twl6030-madc.c
+++ b/drivers/mfd/twl6030-madc.c
@@ -264,6 +264,13 @@ static int __devinit twl6030_madc_probe(struct platform_device *pdev)
madc, DEBUG_FOPS);
wake_lock_init(&madc->wakelock, WAKE_LOCK_SUSPEND, "twl6030 adc");
twl6030_madc = madc;
+
+ if (twl_i2c_write_u8(TWL_MODULE_MADC, TWL6030_MADC_TEMP1_EN |
+ TWL6030_MADC_SCALER_EN_CH2,
+ TWL6030_MADC_CTRL))
+ dev_err(twl6030_madc->dev, "unable to write to register 0x%X\n",
+ TWL6030_MADC_CTRL);
+
return 0;
}
diff --git a/drivers/mfd/twl6040-codec.c b/drivers/mfd/twl6040-codec.c
index 4633d70..68dec57 100644
--- a/drivers/mfd/twl6040-codec.c
+++ b/drivers/mfd/twl6040-codec.c
@@ -329,7 +329,7 @@ static int twl6040_power_up_completion(struct twl6040 *twl6040,
do {
gpio_set_value(twl6040->audpwron, 1);
time_left = wait_for_completion_timeout(&twl6040->ready,
- msecs_to_jiffies(144));
+ msecs_to_jiffies(700));
if (!time_left) {
intid = twl6040_reg_read(twl6040, TWL6040_REG_INTID);
if (!(intid & TWL6040_READYINT)) {
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 42ecf16..984aacb 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -313,6 +313,15 @@ config SGI_GRU_DEBUG
This option enables addition debugging code for the SGI GRU driver. If
you are unsure, say N.
+config SAMSUNG_JACK
+ bool "3.5MM ear jack driver for Samsung devices"
+ depends on INPUT
+ default n
+ ---help---
+ This is 3.5MM ear jack driver for Samsung devices.
+
+ If unsure, say N.
+
config APDS9802ALS
tristate "Medfield Avago APDS9802 ALS Sensor module"
depends on I2C
@@ -482,6 +491,16 @@ config BMP085
To compile this driver as a module, choose M here: the
module will be called bmp085.
+config BMP180
+ tristate "BMP180 digital pressure sensor"
+ depends on I2C && SYSFS
+ help
+ If you say yes here you get support for the Bosch Sensortec
+ BMP180 digital pressure sensor.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bmp180.
+
config PCH_PHUB
tristate "Intel EG20T PCH / OKI SEMICONDUCTOR IOH(ML7213/ML7223) PHUB"
depends on PCI
@@ -524,6 +543,15 @@ config APANIC_PLABEL
If your platform uses a different flash partition label for storing
crashdumps, enter it here.
+config USB_SWITCH_FSA9480
+ tristate "FSA9480 USB Switch"
+ depends on I2C
+ help
+ The FSA9480 is a USB port accessory detector and switch.
+ The FSA9480 is fully controlled using I2C and enables USB data,
+ stereo and mono audio, video, microphone and UART data to use
+ a common connector port.
+
config OMAP_DIE_TEMP_SENSOR
bool "OMAP On-Die temp sensor support"
depends on OMAP_TEMP_SENSOR
@@ -531,12 +559,23 @@ config OMAP_DIE_TEMP_SENSOR
Enabling this config will give support for the on-die
temp sensor for the OMAP platform.
+config LEDS_AN30259A
+ tristate "LED driver for panasonic AN30259A RGB LED"
+ depends on I2C
+ help
+ This option enables support for AN30259A RGB LED chips
+ accessed via the I2C bus.
+ AN30259A has three channel LED driver. By synchronous clock
+ function, combined operation is possible.
+
source "drivers/misc/c2port/Kconfig"
source "drivers/misc/eeprom/Kconfig"
source "drivers/misc/cb710/Kconfig"
+source "drivers/misc/inv_mpu/Kconfig"
source "drivers/misc/iwmc3200top/Kconfig"
source "drivers/misc/ti-st/Kconfig"
source "drivers/misc/lis3lv02d/Kconfig"
source "drivers/misc/carma/Kconfig"
+source "drivers/misc/modem_if/Kconfig"
endif # MISC_DEVICES
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index ff217f2..9b0ece8 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o
obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o
obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o
obj-$(CONFIG_BMP085) += bmp085.o
+obj-$(CONFIG_BMP180) += bmp180.o
obj-$(CONFIG_ICS932S401) += ics932s401.o
obj-$(CONFIG_LKDTM) += lkdtm.o
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
@@ -19,6 +20,7 @@ obj-$(CONFIG_PHANTOM) += phantom.o
obj-$(CONFIG_SENSORS_BH1780) += bh1780gli.o
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o
+obj-$(CONFIG_SAMSUNG_JACK) += sec_jack.o
obj-$(CONFIG_SGI_IOC4) += ioc4.o
obj-$(CONFIG_ENCLOSURE_SERVICES) += enclosure.o
obj-$(CONFIG_KGDB_TESTS) += kgdbts.o
@@ -50,4 +52,8 @@ obj-y += carma/
obj-$(CONFIG_WL127X_RFKILL) += wl127x-rfkill.o
obj-$(CONFIG_APANIC) += apanic.o
obj-$(CONFIG_SENSORS_AK8975) += akm8975.o
+obj-y += inv_mpu/
+obj-$(CONFIG_SEC_MODEM) += modem_if/
+obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o
obj-$(CONFIG_OMAP_DIE_TEMP_SENSOR) += omap_temp_sensor.o
+obj-$(CONFIG_LEDS_AN30259A) += leds-an30259a.o
diff --git a/drivers/misc/bmp180.c b/drivers/misc/bmp180.c
new file mode 100755
index 0000000..867e5d9
--- /dev/null
+++ b/drivers/misc/bmp180.c
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics. 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/i2c.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+
+#define BMP180_DRV_NAME "bmp180"
+#define DRIVER_VERSION "1.0"
+
+/* Register definitions */
+#define BMP180_TAKE_MEAS_REG 0xf4
+#define BMP180_READ_MEAS_REG_U 0xf6
+#define BMP180_READ_MEAS_REG_L 0xf7
+#define BMP180_READ_MEAS_REG_XL 0xf8
+
+/*
+ * Bytes defined by the spec to take measurements
+ * Temperature will take 4.5ms before EOC
+ */
+#define BMP180_MEAS_TEMP 0x2e
+/* 4.5ms wait for measurement */
+#define BMP180_MEAS_PRESS_OVERSAMP_0 0x34
+/* 7.5ms wait for measurement */
+#define BMP180_MEAS_PRESS_OVERSAMP_1 0x74
+/* 13.5ms wait for measurement */
+#define BMP180_MEAS_PRESS_OVERSAMP_2 0xb4
+/* 25.5ms wait for measurement */
+#define BMP180_MEAS_PRESS_OVERSAMP_3 0xf4
+
+/*
+ * EEPROM registers each is a two byte value so there is
+ * an upper byte and a lower byte
+ */
+#define BMP180_EEPROM_AC1_U 0xaa
+#define BMP180_EEPROM_AC1_L 0xab
+#define BMP180_EEPROM_AC2_U 0xac
+#define BMP180_EEPROM_AC2_L 0xad
+#define BMP180_EEPROM_AC3_U 0xae
+#define BMP180_EEPROM_AC3_L 0xaf
+#define BMP180_EEPROM_AC4_U 0xb0
+#define BMP180_EEPROM_AC4_L 0xb1
+#define BMP180_EEPROM_AC5_U 0xb2
+#define BMP180_EEPROM_AC5_L 0xb3
+#define BMP180_EEPROM_AC6_U 0xb4
+#define BMP180_EEPROM_AC6_L 0xb5
+#define BMP180_EEPROM_B1_U 0xb6
+#define BMP180_EEPROM_B1_L 0xb7
+#define BMP180_EEPROM_B2_U 0xb8
+#define BMP180_EEPROM_B2_L 0xb9
+#define BMP180_EEPROM_MB_U 0xba
+#define BMP180_EEPROM_MB_L 0xbb
+#define BMP180_EEPROM_MC_U 0xbc
+#define BMP180_EEPROM_MC_L 0xbd
+#define BMP180_EEPROM_MD_U 0xbe
+#define BMP180_EEPROM_MD_L 0xbf
+
+#define I2C_TRIES 5
+#define AUTO_INCREMENT 0x80
+
+#define DELAY_LOWBOUND (50 * NSEC_PER_MSEC)
+#define DELAY_DEFAULT (200 * NSEC_PER_MSEC)
+
+#define PRESSURE_MAX 125000
+#define PRESSURE_MIN 95000
+#define PRESSURE_FUZZ 5
+#define PRESSURE_FLAT 5
+
+struct bmp180_eeprom_data {
+ s16 AC1, AC2, AC3;
+ u16 AC4, AC5, AC6;
+ s16 B1, B2;
+ s16 MB, MC, MD;
+};
+
+struct bmp180_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ struct workqueue_struct *wq;
+ struct work_struct work_pressure;
+ struct input_dev *input_dev;
+ struct hrtimer timer;
+ ktime_t poll_delay;
+ u8 oversampling_rate;
+ struct bmp180_eeprom_data bmp180_eeprom_vals;
+ bool enabled;
+ bool on_before_suspend;
+};
+
+static int bmp180_i2c_read(const struct i2c_client *client, u8 cmd,
+ u8 *buf, int len)
+{
+ int err;
+ int tries = 0;
+
+ do {
+ err = i2c_smbus_read_i2c_block_data(client, cmd, len, buf);
+ if (err == len)
+ return 0;
+ } while (++tries < I2C_TRIES);
+
+ return err;
+}
+
+static int bmp180_i2c_write(const struct i2c_client *client, u8 cmd, u8 data)
+{
+ int err;
+ int tries = 0;
+
+ do {
+ err = i2c_smbus_write_byte_data(client, cmd, data);
+ if (!err)
+ return 0;
+ } while (++tries < I2C_TRIES);
+
+ return err;
+}
+
+static void bmp180_enable(struct bmp180_data *barom)
+{
+ pr_debug("%s: bmp180_enable\n", __func__);
+ if (!barom->enabled) {
+ barom->enabled = true;
+ pr_debug("%s: start timer\n", __func__);
+ hrtimer_start(&barom->timer, barom->poll_delay,
+ HRTIMER_MODE_REL);
+ }
+}
+
+static void bmp180_disable(struct bmp180_data *barom)
+{
+ pr_debug("%s: bmp180_disable\n", __func__);
+ if (barom->enabled) {
+ barom->enabled = false;
+ pr_debug("%s: stop timer\n", __func__);
+ hrtimer_cancel(&barom->timer);
+ cancel_work_sync(&barom->work_pressure);
+ }
+}
+
+static int bmp180_get_raw_temperature(struct bmp180_data *barom,
+ u16 *raw_temperature)
+{
+ int err;
+ u16 buf;
+
+ pr_debug("%s: read uncompensated temperature value\n", __func__);
+ err = bmp180_i2c_write(barom->client, BMP180_TAKE_MEAS_REG,
+ BMP180_MEAS_TEMP);
+ if (err) {
+ pr_err("%s: can't write BMP180_TAKE_MEAS_REG\n", __func__);
+ return err;
+ }
+
+ msleep(5);
+
+ err = bmp180_i2c_read(barom->client, BMP180_READ_MEAS_REG_U,
+ (u8 *)&buf, 2);
+ if (err) {
+ pr_err("%s: Fail to read uncompensated temperature\n",
+ __func__);
+ return err;
+ }
+ *raw_temperature = be16_to_cpu(buf);
+ pr_debug("%s: uncompensated temperature: %d\n",
+ __func__, *raw_temperature);
+ return err;
+}
+
+static int bmp180_get_raw_pressure(struct bmp180_data *barom,
+ u32 *raw_pressure)
+{
+ int err;
+ u32 buf = 0;
+
+ pr_debug("%s: read uncompensated pressure value\n", __func__);
+
+ err = bmp180_i2c_write(barom->client, BMP180_TAKE_MEAS_REG,
+ BMP180_MEAS_PRESS_OVERSAMP_0 |
+ (barom->oversampling_rate << 6));
+ if (err) {
+ pr_err("%s: can't write BMP180_TAKE_MEAS_REG\n", __func__);
+ return err;
+ }
+
+ msleep(2+(3 << barom->oversampling_rate));
+
+ err = bmp180_i2c_read(barom->client, BMP180_READ_MEAS_REG_U,
+ ((u8 *)&buf)+1, 3);
+ if (err) {
+ pr_err("%s: Fail to read uncompensated pressure\n", __func__);
+ return err;
+ }
+
+ *raw_pressure = be32_to_cpu(buf);
+ *raw_pressure >>= (8 - barom->oversampling_rate);
+ pr_debug("%s: uncompensated pressure: %d\n",
+ __func__, *raw_pressure);
+
+ return err;
+}
+
+static void bmp180_get_pressure_data(struct work_struct *work)
+{
+ u16 raw_temperature;
+ u32 raw_pressure;
+ long x1, x2, x3, b3, b5, b6;
+ unsigned long b4, b7;
+ long p;
+ int pressure;
+ int err;
+
+ struct bmp180_data *barom =
+ container_of(work, struct bmp180_data, work_pressure);
+
+ if (bmp180_get_raw_temperature(barom, &raw_temperature)) {
+ pr_err("%s: can't read uncompensated temperature\n", __func__);
+ return;
+ }
+
+ if (bmp180_get_raw_pressure(barom, &raw_pressure)) {
+ pr_err("%s: Fail to read uncompensated pressure\n", __func__);
+ return;
+ }
+
+ x1 = ((raw_temperature - barom->bmp180_eeprom_vals.AC6) *
+ barom->bmp180_eeprom_vals.AC5) >> 15;
+ x2 = (barom->bmp180_eeprom_vals.MC << 11) /
+ (x1 + barom->bmp180_eeprom_vals.MD);
+ b5 = x1 + x2;
+
+ b6 = (b5 - 4000);
+ x1 = (barom->bmp180_eeprom_vals.B2 * ((b6 * b6) >> 12)) >> 11;
+ x2 = (barom->bmp180_eeprom_vals.AC2 * b6) >> 11;
+ x3 = x1 + x2;
+ b3 = (((((long)barom->bmp180_eeprom_vals.AC1) * 4 +
+ x3) << barom->oversampling_rate) + 2) >> 2;
+ x1 = (barom->bmp180_eeprom_vals.AC3 * b6) >> 13;
+ x2 = (barom->bmp180_eeprom_vals.B1 * (b6 * b6 >> 12)) >> 16;
+ x3 = ((x1 + x2) + 2) >> 2;
+ b4 = (barom->bmp180_eeprom_vals.AC4 *
+ (unsigned long)(x3 + 32768)) >> 15;
+ b7 = ((unsigned long)raw_pressure - b3) *
+ (50000 >> barom->oversampling_rate);
+ if (b7 < 0x80000000)
+ p = (b7 * 2) / b4;
+ else
+ p = (b7 / b4) * 2;
+
+ x1 = (p >> 8) * (p >> 8);
+ x1 = (x1 * 3038) >> 16;
+ x2 = (-7357 * p) >> 16;
+ pressure = p + ((x1 + x2 + 3791) >> 4);
+ pr_debug("%s: calibrated pressure: %d\n",
+ __func__, pressure);
+
+ input_report_abs(barom->input_dev, ABS_PRESSURE, pressure);
+ input_sync(barom->input_dev);
+
+ return;
+}
+
+static int bmp180_input_init(struct bmp180_data *barom)
+{
+ int err;
+
+ pr_debug("%s: enter\n", __func__);
+ barom->input_dev = input_allocate_device();
+ if (!barom->input_dev) {
+ pr_err("%s: could not allocate input device\n", __func__);
+ return -ENOMEM;
+ }
+ input_set_drvdata(barom->input_dev, barom);
+ barom->input_dev->name = "barometer";
+ input_set_capability(barom->input_dev, EV_ABS, ABS_PRESSURE);
+
+ /* Need to define the correct min and max */
+ input_set_abs_params(barom->input_dev, ABS_PRESSURE,
+ PRESSURE_MIN, PRESSURE_MAX,
+ PRESSURE_FUZZ, PRESSURE_FLAT);
+
+ pr_debug("%s: registering barometer input device\n", __func__);
+
+ err = input_register_device(barom->input_dev);
+ if (err) {
+ pr_err("%s: unable to register input polled device %s\n",
+ __func__, barom->input_dev->name);
+ goto err_register_device;
+ }
+
+ goto done;
+
+err_register_device:
+ input_free_device(barom->input_dev);
+done:
+ return err;
+}
+
+static void bmp180_input_cleanup(struct bmp180_data *barom)
+{
+ input_unregister_device(barom->input_dev);
+ input_free_device(barom->input_dev);
+}
+
+static int bmp180_read_store_eeprom_val(struct bmp180_data *barom)
+{
+ int err;
+ u16 buf[11];
+
+ err = bmp180_i2c_read(barom->client, BMP180_EEPROM_AC1_U,
+ (u8 *)buf, 22);
+ if (err) {
+ pr_err("%s: Cannot read EEPROM values\n", __func__);
+ return err;
+ }
+ barom->bmp180_eeprom_vals.AC1 = be16_to_cpu(buf[0]);
+ barom->bmp180_eeprom_vals.AC2 = be16_to_cpu(buf[1]);
+ barom->bmp180_eeprom_vals.AC3 = be16_to_cpu(buf[2]);
+ barom->bmp180_eeprom_vals.AC4 = be16_to_cpu(buf[3]);
+ barom->bmp180_eeprom_vals.AC5 = be16_to_cpu(buf[4]);
+ barom->bmp180_eeprom_vals.AC6 = be16_to_cpu(buf[5]);
+ barom->bmp180_eeprom_vals.B1 = be16_to_cpu(buf[6]);
+ barom->bmp180_eeprom_vals.B2 = be16_to_cpu(buf[7]);
+ barom->bmp180_eeprom_vals.MB = be16_to_cpu(buf[8]);
+ barom->bmp180_eeprom_vals.MC = be16_to_cpu(buf[9]);
+ barom->bmp180_eeprom_vals.MD = be16_to_cpu(buf[10]);
+ return 0;
+}
+
+static enum hrtimer_restart bmp180_timer_func(struct hrtimer *timer)
+{
+ struct bmp180_data *barom = container_of(timer,
+ struct bmp180_data, timer);
+
+ pr_debug("%s: start\n", __func__);
+ queue_work(barom->wq, &barom->work_pressure);
+ hrtimer_forward_now(&barom->timer, barom->poll_delay);
+ return HRTIMER_RESTART;
+}
+
+static ssize_t bmp180_poll_delay_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bmp180_data *barom = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%lld\n",
+ ktime_to_ns(barom->poll_delay));
+}
+
+static ssize_t bmp180_poll_delay_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ int err;
+ int64_t new_delay;
+ struct bmp180_data *barom = dev_get_drvdata(dev);
+
+ err = strict_strtoll(buf, 10, &new_delay);
+ if (err < 0)
+ return err;
+
+ pr_debug("%s: new delay = %lldns, old delay = %lldns\n",
+ __func__, new_delay, ktime_to_ns(barom->poll_delay));
+
+ if (new_delay < DELAY_LOWBOUND)
+ new_delay = DELAY_LOWBOUND;
+
+ mutex_lock(&barom->lock);
+ if (new_delay != ktime_to_ns(barom->poll_delay))
+ barom->poll_delay = ns_to_ktime(new_delay);
+
+ mutex_unlock(&barom->lock);
+
+ return size;
+}
+
+static ssize_t bmp180_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bmp180_data *barom = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", barom->enabled);
+}
+
+static ssize_t bmp180_enable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ bool new_value;
+ struct bmp180_data *barom = dev_get_drvdata(dev);
+
+ pr_debug("%s: enable %s\n", __func__, buf);
+
+ if (sysfs_streq(buf, "1")) {
+ new_value = true;
+ } else if (sysfs_streq(buf, "0")) {
+ new_value = false;
+ } else {
+ pr_err("%s: invalid value %d\n", __func__, *buf);
+ return -EINVAL;
+ }
+
+ pr_debug("%s: new_value = %d, old state = %d\n",
+ __func__, new_value, barom->enabled);
+
+ mutex_lock(&barom->lock);
+ if (new_value)
+ bmp180_enable(barom);
+ else
+ bmp180_disable(barom);
+
+ mutex_unlock(&barom->lock);
+
+ return size;
+}
+
+static ssize_t bmp180_oversampling_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct bmp180_data *barom = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", barom->oversampling_rate);
+}
+
+static ssize_t bmp180_oversampling_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct bmp180_data *barom = dev_get_drvdata(dev);
+
+ unsigned long oversampling;
+ int success = strict_strtoul(buf, 10, &oversampling);
+ if (success == 0) {
+ if (oversampling > 3)
+ oversampling = 3;
+ barom->oversampling_rate = oversampling;
+ return count;
+ }
+ return success;
+}
+
+static DEVICE_ATTR(poll_delay, S_IRUGO | S_IWUSR | S_IWGRP,
+ bmp180_poll_delay_show, bmp180_poll_delay_store);
+
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR | S_IWGRP,
+ bmp180_enable_show, bmp180_enable_store);
+
+static DEVICE_ATTR(oversampling, S_IWUSR | S_IRUGO,
+ bmp180_oversampling_show, bmp180_oversampling_store);
+
+static struct attribute *bmp180_sysfs_attrs[] = {
+ &dev_attr_enable.attr,
+ &dev_attr_poll_delay.attr,
+ &dev_attr_oversampling.attr,
+ NULL
+};
+
+static struct attribute_group bmp180_attribute_group = {
+ .attrs = bmp180_sysfs_attrs,
+};
+
+static int __devinit bmp180_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int err;
+ struct bmp180_data *barom;
+
+ pr_debug("%s: enter\n", __func__);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ pr_err("%s: client not i2c capable\n", __func__);
+ return -EIO;
+ }
+
+ barom = kzalloc(sizeof(*barom), GFP_KERNEL);
+ if (!barom) {
+ pr_err("%s: failed to allocate memory for module\n", __func__);
+ return -ENOMEM;
+ }
+
+ mutex_init(&barom->lock);
+ barom->client = client;
+
+ i2c_set_clientdata(client, barom);
+
+ err = bmp180_read_store_eeprom_val(barom);
+ if (err) {
+ pr_err("%s: Reading the EEPROM failed\n", __func__);
+ err = -ENODEV;
+ goto err_read_eeprom;
+ }
+
+ hrtimer_init(&barom->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ barom->poll_delay = ns_to_ktime(DELAY_DEFAULT);
+ barom->timer.function = bmp180_timer_func;
+
+ barom->wq = alloc_workqueue("bmp180_wq",
+ WQ_UNBOUND | WQ_RESCUER, 1);
+ if (!barom->wq) {
+ err = -ENOMEM;
+ pr_err("%s: could not create workqueue\n", __func__);
+ goto err_create_workqueue;
+ }
+
+ INIT_WORK(&barom->work_pressure, bmp180_get_pressure_data);
+
+ err = bmp180_input_init(barom);
+ if (err) {
+ pr_err("%s: could not create input device\n", __func__);
+ goto err_input_init;
+ }
+ err = sysfs_create_group(&barom->input_dev->dev.kobj,
+ &bmp180_attribute_group);
+ if (err) {
+ pr_err("%s: could not create sysfs group\n", __func__);
+ goto err_sysfs_create_group;
+ }
+
+ goto done;
+
+err_sysfs_create_group:
+ bmp180_input_cleanup(barom);
+err_input_init:
+ destroy_workqueue(barom->wq);
+err_create_workqueue:
+err_read_eeprom:
+ mutex_destroy(&barom->lock);
+ kfree(barom);
+done:
+ return err;
+}
+
+static int __devexit bmp180_remove(struct i2c_client *client)
+{
+ /* TO DO: revisit ordering here once _probe order is finalized */
+ struct bmp180_data *barom = i2c_get_clientdata(client);
+
+ pr_debug("%s: bmp180_remove +\n", __func__);
+ sysfs_remove_group(&barom->input_dev->dev.kobj,
+ &bmp180_attribute_group);
+
+ bmp180_disable(barom);
+
+ bmp180_input_cleanup(barom);
+
+ destroy_workqueue(barom->wq);
+
+ mutex_destroy(&barom->lock);
+ kfree(barom);
+
+ pr_debug("%s: bmp180_remove -\n", __func__);
+ return 0;
+}
+
+static int bmp180_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bmp180_data *barom = i2c_get_clientdata(client);
+ pr_debug("%s: on_before_suspend %d\n",
+ __func__, barom->on_before_suspend);
+
+ if (barom->on_before_suspend)
+ bmp180_enable(barom);
+ return 0;
+}
+
+static int bmp180_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct bmp180_data *barom = i2c_get_clientdata(client);
+
+ barom->on_before_suspend = barom->enabled;
+ pr_debug("%s: on_before_suspend %d\n",
+ __func__, barom->on_before_suspend);
+ bmp180_disable(barom);
+ return 0;
+}
+
+static const struct i2c_device_id bmp180_id[] = {
+ {BMP180_DRV_NAME, 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, bmp180_id);
+static const struct dev_pm_ops bmp180_pm_ops = {
+ .suspend = bmp180_suspend,
+ .resume = bmp180_resume,
+};
+
+static struct i2c_driver bmp180_driver = {
+ .driver = {
+ .name = BMP180_DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &bmp180_pm_ops,
+ },
+ .probe = bmp180_probe,
+ .remove = __devexit_p(bmp180_remove),
+ .id_table = bmp180_id,
+};
+
+static int __init bmp180_init(void)
+{
+ pr_debug("%s: _init\n", __func__);
+ return i2c_add_driver(&bmp180_driver);
+}
+
+static void __exit bmp180_exit(void)
+{
+ pr_debug("%s: _exit +\n", __func__);
+ i2c_del_driver(&bmp180_driver);
+ pr_debug("%s: _exit -\n", __func__);
+ return;
+}
+
+MODULE_AUTHOR("Hyoung Wook Ham <hwham@sta.samsung.com>");
+MODULE_DESCRIPTION("BMP180 Pressure sensor driver");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(bmp180_init);
+module_exit(bmp180_exit);
diff --git a/drivers/misc/fsa9480.c b/drivers/misc/fsa9480.c
new file mode 100755
index 0000000..92382ea
--- /dev/null
+++ b/drivers/misc/fsa9480.c
@@ -0,0 +1,1067 @@
+/*
+ * driver/misc/fsa9480.c - FSA9480 micro USB switch device driver
+ *
+ * Copyright (C) 2010 Samsung Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ * Wonguk Jeong <wonguk.jeong@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_data/fsa9480.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb/otg_id.h>
+#include <linux/seq_file.h>
+#include <linux/debugfs.h>
+
+#define DEBUG_DUMP_REGISTERS
+
+/* FSA9480 I2C registers */
+#define FSA9480_REG_DEVID 0x01
+#define FSA9480_REG_CTRL 0x02
+#define FSA9480_REG_INT1 0x03
+#define FSA9480_REG_INT2 0x04
+#define FSA9480_REG_INT1_MASK 0x05
+#define FSA9480_REG_INT2_MASK 0x06
+#define FSA9480_REG_ADC 0x07
+#define FSA9480_REG_TIMING1 0x08
+#define FSA9480_REG_TIMING2 0x09
+#define FSA9480_REG_DEV_T1 0x0a
+#define FSA9480_REG_DEV_T2 0x0b
+#define FSA9480_REG_BTN1 0x0c
+#define FSA9480_REG_BTN2 0x0d
+#define FSA9480_REG_CK 0x0e
+#define FSA9480_REG_CK_INT1 0x0f
+#define FSA9480_REG_CK_INT2 0x10
+#define FSA9480_REG_CK_INTMASK1 0x11
+#define FSA9480_REG_CK_INTMASK2 0x12
+#define FSA9480_REG_MANSW1 0x13
+#define FSA9480_REG_MANSW2 0x14
+#define FSA9480_REG_ANALOG_TEST 0x15
+#define FSA9480_REG_SCAN_TEST 0x16
+#define FSA9480_REG_DAC_OVERRIDE_1 0x17
+#define FSA9480_REG_DAC_OVERRIDE_2 0x18
+#define FSA9480_REG_VIDEO_DETECT 0x19
+#define FSA9480_REG_CK_PULSE_WIDTH 0x1A
+#define FSA9480_REG_MANOVERRIDE1 0x1B
+#define FSA9480_REG_STATUS1 0x1C
+#define FSA9480_REG_STATUS2 0x1D
+#define FSA9480_REG_FUSE1 0x1E
+
+/* Control */
+#define CON_SWITCH_OPEN (1 << 4)
+#define CON_RAW_DATA (1 << 3)
+#define CON_MANUAL_SW (1 << 2)
+#define CON_WAIT (1 << 1)
+#define CON_INT_MASK (1 << 0)
+#define CON_MASK (CON_SWITCH_OPEN | CON_RAW_DATA | \
+ CON_MANUAL_SW | CON_WAIT)
+
+/* we always read these as a word */
+/* Device Type 2 */
+#define DEV_AV (1 << 14)
+#define DEV_TTY (1 << 13)
+#define DEV_PPD (1 << 12)
+#define DEV_JIG_UART_OFF (1 << 11)
+#define DEV_JIG_UART_ON (1 << 10)
+#define DEV_JIG_USB_OFF (1 << 9)
+#define DEV_JIG_USB_ON (1 << 8)
+/* Device Type 1 */
+#define DEV_USB_OTG (1 << 7)
+#define DEV_DEDICATED_CHG (1 << 6)
+#define DEV_USB_CHG (1 << 5)
+#define DEV_CAR_KIT (1 << 4)
+#define DEV_UART (1 << 3)
+#define DEV_USB (1 << 2)
+#define DEV_AUDIO_2 (1 << 1)
+#define DEV_AUDIO_1 (1 << 0)
+
+#define DEV_USB_MASK (DEV_USB | DEV_JIG_USB_OFF | DEV_JIG_USB_ON)
+#define DEV_UART_MASK (DEV_UART | DEV_JIG_UART_OFF)
+#define DEV_JIG_MASK (DEV_JIG_USB_OFF | DEV_JIG_USB_ON | \
+ DEV_JIG_UART_OFF | DEV_JIG_UART_ON)
+#define DEV_CHARGER_MASK (DEV_DEDICATED_CHG | DEV_USB_CHG | DEV_CAR_KIT)
+
+/*
+ * Manual Switch
+ * D- [7:5] / D+ [4:2]
+ * 000: Open all / 001: USB / 010: AUDIO / 011: UART / 100: V_AUDIO
+ */
+#define SW_VAUDIO ((4 << 5) | (4 << 2))
+#define SW_UART ((3 << 5) | (3 << 2))
+#define SW_AUDIO ((2 << 5) | (2 << 2))
+#define SW_DHOST ((1 << 5) | (1 << 2))
+#define SW_AUTO ((0 << 5) | (0 << 2))
+
+/* Interrupt Mask */
+#define INT_STUCK_KEY_RCV (1 << 12)
+#define INT_STUCK_KEY (1 << 11)
+#define INT_ADC_CHANGE (1 << 10)
+#define INT_RESERVE_ATTACH (1 << 9)
+#define INT_AV_CHARGING (1 << 8)
+#define INT_OVP_OCP_DIS (1 << 7)
+#define INT_OCP_EN (1 << 6)
+#define INT_OVP_EN (1 << 5)
+#define INT_LKR (1 << 4)
+#define INT_LKP (1 << 3)
+#define INT_KP (1 << 2)
+#define INT_DETACH (1 << 1)
+#define INT_ATTACH (1 << 0)
+
+static const unsigned int adc_timing[] = {
+ 50, /* ms */
+ 100,
+ 150,
+ 200,
+ 300,
+ 400,
+ 500,
+ 600,
+ 700,
+ 800,
+ 900,
+ 1000
+};
+
+static const char *device_names[] = {
+ [FSA9480_DETECT_NONE] = "unknown/none",
+ [FSA9480_DETECT_USB] = "usb-peripheral",
+ [FSA9480_DETECT_USB_HOST] = "usb-host",
+ [FSA9480_DETECT_CHARGER] = "charger",
+ [FSA9480_DETECT_JIG] = "jig",
+ [FSA9480_DETECT_UART] = "uart",
+ [FSA9480_DETECT_AV_365K] = "av-365k",
+ [FSA9480_DETECT_AV_365K_CHARGER] = "av-365k-charger",
+};
+
+struct usbsw_nb_info {
+ struct otg_id_notifier_block otg_id_nb;
+ struct fsa9480_detect_set *detect_set;
+
+ struct fsa9480_usbsw *usbsw;
+};
+
+struct fsa9480_usbsw {
+ struct i2c_client *client;
+ struct fsa9480_platform_data *pdata;
+ int mansw;
+ u32 curr_dev;
+ struct mutex lock;
+ u16 intr_mask;
+ u8 timing;
+ int external_id_irq;
+ bool wake_enabled;
+#if defined(CONFIG_DEBUG_FS) && defined(DEBUG_DUMP_REGISTERS)
+ struct dentry *debug_dir;
+#endif
+
+ int num_notifiers;
+ struct usbsw_nb_info notifiers[0];
+};
+#define xceiv_to_fsa(x) container_of((x), struct fsa9480_usbsw, otg)
+
+#if defined(CONFIG_DEBUG_FS) && defined(DEBUG_DUMP_REGISTERS)
+
+#define DUMP_FSA9480_REG(client, m, x) ({ \
+ int __val; \
+ __val = i2c_smbus_read_byte_data((client), FSA9480_REG_##x); \
+ seq_printf((m), "%s = 0x%02x\n", #x, __val); \
+ __val; \
+})
+
+static int fsa9480_show_registers(struct seq_file *m, void *p)
+{
+ struct fsa9480_usbsw *usbsw = m->private;
+
+ DUMP_FSA9480_REG(usbsw->client, m, DEVID);
+ DUMP_FSA9480_REG(usbsw->client, m, CTRL);
+ DUMP_FSA9480_REG(usbsw->client, m, INT1);
+ DUMP_FSA9480_REG(usbsw->client, m, INT2);
+ DUMP_FSA9480_REG(usbsw->client, m, INT1_MASK);
+ DUMP_FSA9480_REG(usbsw->client, m, INT2_MASK);
+ DUMP_FSA9480_REG(usbsw->client, m, ADC);
+ DUMP_FSA9480_REG(usbsw->client, m, TIMING1);
+ DUMP_FSA9480_REG(usbsw->client, m, TIMING2);
+ DUMP_FSA9480_REG(usbsw->client, m, DEV_T1);
+ DUMP_FSA9480_REG(usbsw->client, m, DEV_T2);
+ DUMP_FSA9480_REG(usbsw->client, m, BTN1);
+ DUMP_FSA9480_REG(usbsw->client, m, BTN2);
+ DUMP_FSA9480_REG(usbsw->client, m, CK);
+ DUMP_FSA9480_REG(usbsw->client, m, CK_INT1);
+ DUMP_FSA9480_REG(usbsw->client, m, CK_INT2);
+ DUMP_FSA9480_REG(usbsw->client, m, CK_INTMASK1);
+ DUMP_FSA9480_REG(usbsw->client, m, CK_INTMASK2);
+ DUMP_FSA9480_REG(usbsw->client, m, MANSW1);
+ DUMP_FSA9480_REG(usbsw->client, m, MANSW2);
+ DUMP_FSA9480_REG(usbsw->client, m, BTN1);
+ DUMP_FSA9480_REG(usbsw->client, m, BTN2);
+ DUMP_FSA9480_REG(usbsw->client, m, ANALOG_TEST);
+ DUMP_FSA9480_REG(usbsw->client, m, SCAN_TEST);
+ DUMP_FSA9480_REG(usbsw->client, m, DAC_OVERRIDE_1);
+ DUMP_FSA9480_REG(usbsw->client, m, DAC_OVERRIDE_2);
+ DUMP_FSA9480_REG(usbsw->client, m, VIDEO_DETECT);
+ DUMP_FSA9480_REG(usbsw->client, m, CK_PULSE_WIDTH);
+ DUMP_FSA9480_REG(usbsw->client, m, MANOVERRIDE1);
+ DUMP_FSA9480_REG(usbsw->client, m, STATUS1);
+ DUMP_FSA9480_REG(usbsw->client, m, STATUS2);
+ DUMP_FSA9480_REG(usbsw->client, m, FUSE1);
+
+ return 0;
+}
+
+static int fsa9480_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, fsa9480_show_registers, inode->i_private);
+}
+
+static const struct file_operations fsa9480_regs_fops = {
+ .open = fsa9480_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+#endif
+
+static ssize_t fsa9480_show_control(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fsa9480_usbsw *usbsw = dev_get_drvdata(dev);
+ struct i2c_client *client = usbsw->client;
+ s32 value;
+
+ value = i2c_smbus_read_byte_data(client, FSA9480_REG_CTRL);
+ if (value < 0) {
+ dev_err(&client->dev, "%s: err %d\n", __func__, value);
+ return (ssize_t)value;
+ }
+
+ return sprintf(buf, "%02x\n", value);
+}
+
+static ssize_t fsa9480_show_device_type(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct fsa9480_usbsw *usbsw = dev_get_drvdata(dev);
+ struct i2c_client *client = usbsw->client;
+ s32 value;
+
+ value = i2c_smbus_read_word_data(client, FSA9480_REG_DEV_T1);
+ if (value < 0) {
+ dev_err(&client->dev, "%s: err %d\n", __func__, value);
+ return (ssize_t)value;
+ }
+
+ return sprintf(buf, "%04x\n", value);
+}
+
+static ssize_t fsa9480_show_manualsw(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct fsa9480_usbsw *usbsw = dev_get_drvdata(dev);
+ struct i2c_client *client = usbsw->client;
+ s32 value;
+
+ value = i2c_smbus_read_byte_data(client, FSA9480_REG_MANSW1);
+ if (value < 0) {
+ dev_err(&client->dev, "%s: err %d\n", __func__, value);
+ return (ssize_t)value;
+ }
+
+ if (value == SW_VAUDIO)
+ return sprintf(buf, "VAUDIO\n");
+ else if (value == SW_UART)
+ return sprintf(buf, "UART\n");
+ else if (value == SW_AUDIO)
+ return sprintf(buf, "AUDIO\n");
+ else if (value == SW_DHOST)
+ return sprintf(buf, "DHOST\n");
+ else if (value == SW_AUTO)
+ return sprintf(buf, "AUTO\n");
+ else
+ return sprintf(buf, "%x", value);
+}
+
+static ssize_t fsa9480_set_manualsw(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct fsa9480_usbsw *usbsw = dev_get_drvdata(dev);
+ struct i2c_client *client = usbsw->client;
+ s32 value;
+ unsigned int path = 0;
+ int ret;
+
+ value = i2c_smbus_read_byte_data(client, FSA9480_REG_CTRL);
+ if (value < 0) {
+ dev_err(&client->dev, "%s: err %d\n", __func__, value);
+ return (ssize_t)value;
+ }
+
+ if ((value & ~CON_MANUAL_SW) !=
+ (CON_SWITCH_OPEN | CON_RAW_DATA | CON_WAIT))
+ return -EINVAL;
+
+ if (!strncmp(buf, "VAUDIO", 6)) {
+ path = SW_VAUDIO;
+ value &= ~CON_MANUAL_SW;
+ } else if (!strncmp(buf, "UART", 4)) {
+ path = SW_UART;
+ value &= ~CON_MANUAL_SW;
+ } else if (!strncmp(buf, "AUDIO", 5)) {
+ path = SW_AUDIO;
+ value &= ~CON_MANUAL_SW;
+ } else if (!strncmp(buf, "DHOST", 5)) {
+ path = SW_DHOST;
+ value &= ~CON_MANUAL_SW;
+ } else if (!strncmp(buf, "AUTO", 4)) {
+ path = SW_AUTO;
+ value |= CON_MANUAL_SW;
+ } else {
+ dev_err(dev, "Wrong command\n");
+ return -EINVAL;
+ }
+
+ usbsw->mansw = path;
+
+ ret = i2c_smbus_write_byte_data(client, FSA9480_REG_MANSW1, path);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+ return (ssize_t)value;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, FSA9480_REG_CTRL, value);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+ return (ssize_t)value;
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(control, S_IRUGO, fsa9480_show_control, NULL);
+static DEVICE_ATTR(device_type, S_IRUGO, fsa9480_show_device_type, NULL);
+static DEVICE_ATTR(switch, S_IRUGO | S_IWUSR,
+ fsa9480_show_manualsw, fsa9480_set_manualsw);
+
+static struct attribute *fsa9480_attributes[] = {
+ &dev_attr_control.attr,
+ &dev_attr_device_type.attr,
+ &dev_attr_switch.attr,
+ NULL
+};
+
+static const struct attribute_group fsa9480_group = {
+ .attrs = fsa9480_attributes,
+};
+
+static int fsa9480_reg_init(struct fsa9480_usbsw *usbsw)
+{
+ struct i2c_client *client = usbsw->client;
+ unsigned int ctrl = CON_MASK;
+ s32 ret;
+
+ ret = i2c_smbus_write_word_data(client, FSA9480_REG_INT1_MASK,
+ usbsw->intr_mask);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* mask all car kit interrupts */
+ ret = i2c_smbus_write_word_data(client, FSA9480_REG_CK_INTMASK1,
+ 0x07ff);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, FSA9480_REG_TIMING1,
+ usbsw->timing);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+ return ret;
+ }
+
+ ret = i2c_smbus_write_byte_data(client, FSA9480_REG_MANSW1,
+ usbsw->mansw);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+ return ret;
+ }
+
+ if (usbsw->mansw)
+ ctrl &= ~CON_MANUAL_SW; /* Manual Switching Mode */
+
+ ret = i2c_smbus_write_byte_data(client, FSA9480_REG_CTRL, ctrl);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int fsa9480_reset(struct fsa9480_usbsw *usbsw)
+{
+ struct i2c_client *client = usbsw->client;
+ s32 ret;
+
+ /* soft reset to re-initialize the fsa, and re-do detection */
+ ret = i2c_smbus_write_byte_data(client, FSA9480_REG_MANOVERRIDE1, 1);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot soft reset, err %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+static void _detected(struct fsa9480_usbsw *usbsw, int device)
+{
+ dev_info(&usbsw->client->dev,
+ "cable detect change, from '%s' to '%s'\n",
+ device_names[usbsw->curr_dev], device_names[device]);
+ usbsw->curr_dev = device;
+ usbsw->pdata->detected(usbsw->curr_dev);
+}
+
+static int fsa9480_detect_callback(struct otg_id_notifier_block *nb)
+{
+ struct usbsw_nb_info *nb_info =
+ container_of(nb, struct usbsw_nb_info, otg_id_nb);
+ struct fsa9480_usbsw *usbsw = nb_info->usbsw;
+ struct i2c_client *client = usbsw->client;
+ u16 dev_type;
+ u8 adc_val;
+ u32 prev_dev;
+ int max_events = 100;
+
+ mutex_lock(&usbsw->lock);
+
+ usbsw->pdata->enable(true);
+
+ /* the fsa could have queued up a few events if we haven't processed
+ * them promptly
+ */
+ while (max_events-- > 0) {
+ s32 ret = i2c_smbus_read_word_data(client, FSA9480_REG_INT1);
+ if (!ret)
+ break;
+ }
+ if (!max_events)
+ dev_warn(&client->dev, "too many events. fsa hosed?\n");
+
+ /* fsa may take some time to update the dev_type reg after reading
+ * the int reg.
+ */
+ usleep_range(200, 300);
+
+ dev_type = i2c_smbus_read_word_data(client, FSA9480_REG_DEV_T1);
+ adc_val = i2c_smbus_read_byte_data(client, FSA9480_REG_ADC);
+ if (dev_type < 0 || adc_val < 0) {
+ dev_err(&client->dev, "error reading adc/dev_type regs\n");
+ goto err;
+ }
+
+ dev_dbg(&client->dev, "trying detect (prio=%d): type=%x adc=%x\n",
+ nb_info->detect_set->prio, dev_type, adc_val);
+
+ prev_dev = usbsw->curr_dev;
+
+ if (dev_type & DEV_USB_MASK) {
+ /* If there is an external id signal then verify that the ID
+ * signal is floating. If the ID signal is pulled low then this
+ * may be a cable misidentification. This can occur if the
+ * board allows for the ID signal to be redirected away from the
+ * FSA9480. If the ID signal is not visible to the FSA9480 and
+ * VBUS is present then the cable will be identified as a USB
+ * peripheral cable.
+ *
+ * In the event of a cable misidentification the FSA9480 chip
+ * will be reset to force a new detection cycle.
+ */
+ if (usbsw->pdata->external_id >= 0 &&
+ !gpio_get_value(usbsw->pdata->external_id)) {
+ dev_info(&usbsw->client->dev, "Cable misidentified as "
+ "a USB-peripheral cable, resetting the "
+ "FSA9480\n");
+ fsa9480_reset(usbsw);
+ goto handled;
+ }
+
+ /* usb peripheral mode */
+ if (!(nb_info->detect_set->mask & FSA9480_DETECT_USB))
+ goto unhandled;
+ _detected(usbsw, FSA9480_DETECT_USB);
+ goto handled;
+ } else if (dev_type & DEV_UART_MASK) {
+ if (!(nb_info->detect_set->mask & FSA9480_DETECT_UART))
+ goto unhandled;
+ _detected(usbsw, FSA9480_DETECT_UART);
+ goto handled;
+ } else if (dev_type & DEV_CHARGER_MASK) {
+ if (!(nb_info->detect_set->mask & FSA9480_DETECT_CHARGER))
+ goto unhandled;
+ _detected(usbsw, FSA9480_DETECT_CHARGER);
+ goto handled;
+ } else if (dev_type & DEV_JIG_MASK) {
+ if (!(nb_info->detect_set->mask & FSA9480_DETECT_JIG))
+ goto unhandled;
+ _detected(usbsw, FSA9480_DETECT_JIG);
+ goto handled;
+ } else if (dev_type & DEV_USB_OTG) {
+ if (!(nb_info->detect_set->mask & FSA9480_DETECT_USB_HOST))
+ goto unhandled;
+ _detected(usbsw, FSA9480_DETECT_USB_HOST);
+
+ mutex_unlock(&usbsw->lock);
+
+ /* Enable the external ID interrupt to detect the detach of the
+ * USB host cable since the FSA9480 is unable to detect it.
+ * The FSA9480 takes a while pulling that line down, so a sleep
+ * is needed.
+ */
+ usleep_range(10000, 11000);
+ enable_irq(usbsw->external_id_irq);
+ return OTG_ID_HANDLED;
+ } else if (dev_type & DEV_AV) {
+ /* There are two ID resistances, 1K and 365K that the FSA9480
+ * will resolve to the A/V Cable device type. The ADC value can
+ * be used to tell the difference between the two.
+ */
+ if (adc_val == 0x1a) {
+ /* Delay to allow VBUS to be seen, if present. There's
+ * a possibility that we won't charge if it takes
+ * longer than this for VBUS to be present. */
+ msleep(10);
+ if ((nb_info->detect_set->mask &
+ FSA9480_DETECT_AV_365K_CHARGER) &&
+ usbsw->pdata->vbus_present()) {
+ _detected(usbsw,
+ FSA9480_DETECT_AV_365K_CHARGER);
+ /* The FSA9480 will not interrupt when a USB or
+ * charger cable is disconnected from the dock
+ * so we must detect loss of VBUS via an
+ * external interrupt. */
+ enable_irq(usbsw->pdata->external_vbus_irq);
+ mutex_unlock(&usbsw->lock);
+ return OTG_ID_HANDLED;
+ } else if ((nb_info->detect_set->mask &
+ FSA9480_DETECT_AV_365K) &&
+ !usbsw->pdata->vbus_present()) {
+ _detected(usbsw, FSA9480_DETECT_AV_365K);
+ } else {
+ goto unhandled;
+ }
+ goto handled;
+ }
+ } else if (dev_type == 0) {
+ usbsw->curr_dev = 0;
+ dev_info(&usbsw->client->dev,
+ "nothing attached, keeping ownership of port\n");
+ goto handled;
+ }
+
+unhandled:
+ usbsw->curr_dev = 0;
+ if (nb_info->detect_set->fallback) {
+ /* In this case, we are the last resort and we are supposed to
+ * keep ownership of ID/D+/D- to monitor them for changes.
+ * This can happen when no one else
+ * detected a valid device and it is not one of the above.
+ */
+
+ dev_info(&usbsw->client->dev,
+ "nothing known attached, keeping ownership of port\n");
+ goto handled;
+ }
+
+
+err:
+ usbsw->pdata->enable(false);
+ mutex_unlock(&usbsw->lock);
+ return OTG_ID_UNHANDLED;
+
+handled:
+ BUG_ON((usbsw->curr_dev == FSA9480_DETECT_NONE) &&
+ (prev_dev != FSA9480_DETECT_NONE));
+
+ mutex_unlock(&usbsw->lock);
+ enable_irq_wake(usbsw->client->irq);
+ enable_irq(usbsw->client->irq);
+
+ return OTG_ID_HANDLED;
+}
+
+static int fsa9480_proxy_wait_callback(struct otg_id_notifier_block *nb)
+{
+ struct usbsw_nb_info *nb_info =
+ container_of(nb, struct usbsw_nb_info, otg_id_nb);
+ struct fsa9480_usbsw *usbsw = nb_info->usbsw;
+
+ dev_info(&usbsw->client->dev, "taking proxy ownership of port\n");
+
+ usbsw->pdata->enable(true);
+ enable_irq_wake(usbsw->client->irq);
+ enable_irq(usbsw->client->irq);
+
+ return OTG_ID_HANDLED;
+}
+
+static void fsa9480_cancel_callback(struct otg_id_notifier_block *nb)
+{
+ struct usbsw_nb_info *nb_info =
+ container_of(nb, struct usbsw_nb_info, otg_id_nb);
+ struct fsa9480_usbsw *usbsw = nb_info->usbsw;
+ struct i2c_client *client = usbsw->client;
+
+ dev_info(&client->dev, "cancelling");
+}
+
+static irqreturn_t fsa9480_irq_thread(int irq, void *data)
+{
+ struct fsa9480_usbsw *usbsw = data;
+ struct i2c_client *client = usbsw->client;
+ s32 intr;
+
+ /* read and clear interrupt status bits */
+ intr = i2c_smbus_read_word_data(client, FSA9480_REG_INT1);
+ if (intr < 0) {
+ dev_err(&client->dev, "%s: err %d\n", __func__, intr);
+ intr = 0;
+ } else if (intr == 0) {
+ /* When the FSA9480 triggers an interrupt with no status bits
+ * set the FSA9480 may have reset and the registers need to be
+ * reinitialized.
+ */
+ fsa9480_reg_init(usbsw);
+ dev_warn(&client->dev, "irq fired, but nothing happened\n");
+ } else {
+ dev_dbg(&client->dev, "got irq 0x%x\n", intr);
+ }
+
+ if (intr & INT_OCP_EN)
+ dev_err(&client->dev, "entering over-current protection\n");
+
+ if (intr & INT_OVP_EN)
+ dev_err(&client->dev, "entering over-voltage protection\n");
+
+ if (intr & INT_OVP_OCP_DIS)
+ dev_err(&client->dev, "exiting protection mode\n");
+
+ disable_irq_nosync(client->irq);
+ disable_irq_wake(client->irq);
+
+ mutex_lock(&usbsw->lock);
+ if (usbsw->curr_dev != FSA9480_DETECT_NONE) {
+ _detected(usbsw, FSA9480_DETECT_NONE);
+
+ /* undo whatever else we did */
+ }
+ mutex_unlock(&usbsw->lock);
+
+ otg_id_notify();
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t usb_id_irq_thread(int irq, void *data)
+{
+ struct fsa9480_usbsw *usbsw = data;
+ struct i2c_client *client = usbsw->client;
+
+ mutex_lock(&usbsw->lock);
+
+ /* The external ID interrupt is only used when a USB host cable is
+ * attached.
+ */
+ if (usbsw->curr_dev != FSA9480_DETECT_USB_HOST) {
+ disable_irq_nosync(usbsw->external_id_irq);
+ mutex_unlock(&usbsw->lock);
+ return IRQ_HANDLED;
+ }
+
+ /* The FSA9480 has a bug that prevents it from detecting a change in the
+ * ID signal when the device type is USB OTG. As a workaround the
+ * driver uses an external mechanism to determine if the USB OTG cable
+ * has been detached.
+ */
+ if (gpio_get_value(usbsw->pdata->external_id)) {
+ disable_irq_nosync(usbsw->external_id_irq);
+
+ usbsw->pdata->enable(true);
+
+ /* If the client has been informed of the USB host attach then
+ * report the disconnect before reseting the FSA9480. VBUS
+ * drive needs to be turned off before the reset otherwise the
+ * FSA9480 will misidentify the unattached state as a USB
+ * peripheral cable.
+ */
+ _detected(usbsw, FSA9480_DETECT_NONE);
+
+ dev_dbg(&client->dev, "usb host detach workaround, resetting"
+ " FSA9480 chip\n");
+
+ /* The FSA9480 will not be able to detect a new cable until it
+ * has been reset.
+ */
+ fsa9480_reset(usbsw);
+
+ enable_irq_wake(client->irq);
+ enable_irq(client->irq);
+ }
+
+ mutex_unlock(&usbsw->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t vbus_irq_thread(int irq, void *data)
+{
+ struct fsa9480_usbsw *usbsw = data;
+
+ disable_irq_nosync(usbsw->pdata->external_vbus_irq);
+
+ mutex_lock(&usbsw->lock);
+ if (usbsw->curr_dev != FSA9480_DETECT_AV_365K_CHARGER) {
+ mutex_unlock(&usbsw->lock);
+ return IRQ_HANDLED;
+ }
+
+ /* VBUS has gone away when docked, so reset the state to
+ * FSA_DETECT_NONE and reset the FSA9480, because it cannot
+ * detect ID pin changes correctly after dock detach. */
+ _detected(usbsw, FSA9480_DETECT_NONE);
+ fsa9480_reset(usbsw);
+ enable_irq_wake(usbsw->client->irq);
+ enable_irq(usbsw->client->irq);
+ mutex_unlock(&usbsw->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit fsa9480_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct fsa9480_platform_data *pdata = client->dev.platform_data;
+ struct fsa9480_usbsw *usbsw;
+ int ret = 0;
+ int i;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+ if (!pdata || !pdata->detected || !pdata->enable ||
+ !pdata->mask_vbus_irq || !pdata->unmask_vbus_irq ||
+ !pdata->vbus_present ||
+ (pdata->external_vbus_irq < 0)) {
+ dev_err(&client->dev, "missing/invalid platform data\n");
+ return -EINVAL;
+ }
+
+ usbsw = kzalloc(sizeof(struct fsa9480_usbsw) +
+ pdata->num_sets * sizeof(struct usbsw_nb_info),
+ GFP_KERNEL);
+ if (!usbsw) {
+ dev_err(&client->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ usbsw->client = client;
+ usbsw->pdata = pdata;
+
+ i2c_set_clientdata(client, usbsw);
+ mutex_init(&usbsw->lock);
+
+ if (usbsw->pdata->external_id >= 0) {
+ gpio_request(usbsw->pdata->external_id, "fsa9840_external_id");
+
+ usbsw->external_id_irq = gpio_to_irq(usbsw->pdata->external_id);
+
+ ret = request_threaded_irq(usbsw->external_id_irq, NULL,
+ usb_id_irq_thread,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "fsa9480_external_id", usbsw);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to request ID IRQ err %d\n",
+ ret);
+ goto err_req_id_irq;
+ }
+ }
+
+ pdata->mask_vbus_irq();
+ ret = request_threaded_irq(pdata->external_vbus_irq, NULL,
+ vbus_irq_thread, pdata->external_vbus_flags,
+ "external_vbus", usbsw);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to request vbus IRQ err %d\n",
+ ret);
+ goto err_req_vbus_irq;
+ }
+ disable_irq(pdata->external_vbus_irq);
+ pdata->unmask_vbus_irq();
+
+ /* mask all irqs to prevent event processing between
+ * request_irq and disable_irq
+ */
+ usbsw->intr_mask = 0x1fff;
+ i2c_smbus_write_word_data(client, FSA9480_REG_INT1_MASK,
+ usbsw->intr_mask);
+
+ ret = request_threaded_irq(client->irq, NULL, fsa9480_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT, "fsa9480",
+ usbsw);
+ if (ret) {
+ dev_err(&client->dev, "failed to request IRQ\n");
+ goto err_req_irq;
+ }
+ disable_irq(client->irq);
+
+ ret = enable_irq_wake(client->irq);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "failed to enable wakeup src %d\n", ret);
+ goto err_en_wake;
+ }
+ disable_irq_wake(client->irq);
+
+ /* Reconcile the requested ADC detect time with the available settings
+ * on the FSA9480.
+ */
+ for (i = 0; i < ARRAY_SIZE(adc_timing); i++) {
+ if (usbsw->pdata->detect_time <= adc_timing[i]) {
+ usbsw->timing = i;
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(adc_timing)) {
+ ret = -ERANGE;
+ goto err_timing;
+ }
+
+ /* mask interrupts (unmask attach/detach only) */
+ usbsw->intr_mask = ~(INT_ATTACH | INT_DETACH | INT_OCP_EN | INT_OVP_EN |
+ INT_OVP_OCP_DIS | INT_AV_CHARGING);
+ ret = fsa9480_reset(usbsw);
+ if (ret < 0)
+ goto err_reset;
+
+ ret = fsa9480_reg_init(usbsw);
+ if (ret)
+ goto err_reg_init;
+
+ ret = sysfs_create_group(&client->dev.kobj, &fsa9480_group);
+ if (ret) {
+ dev_err(&client->dev,
+ "failed to create fsa9480 attribute group\n");
+ goto err_sys_create;
+ }
+
+ usbsw->num_notifiers = pdata->num_sets;
+ for (i = 0; i < usbsw->num_notifiers; i++) {
+ struct usbsw_nb_info *info = &usbsw->notifiers[i];
+
+ info->detect_set = &pdata->detect_sets[i];
+ info->usbsw = usbsw;
+ info->otg_id_nb.detect = fsa9480_detect_callback;
+ info->otg_id_nb.proxy_wait = fsa9480_proxy_wait_callback;
+ info->otg_id_nb.cancel = fsa9480_cancel_callback;
+ info->otg_id_nb.priority = pdata->detect_sets[i].prio;
+
+ ret = otg_id_register_notifier(&info->otg_id_nb);
+ if (ret < 0) {
+ dev_err(&client->dev, "Unable to register notifier\n");
+ goto err_reg_notifiers;
+ }
+ }
+#if defined(CONFIG_DEBUG_FS) && defined(DEBUG_DUMP_REGISTERS)
+ usbsw->debug_dir = debugfs_create_dir("fsa9480", NULL);
+
+ if (usbsw->debug_dir)
+ debugfs_create_file("regs", S_IRUSR, usbsw->debug_dir, usbsw,
+ &fsa9480_regs_fops);
+#endif
+
+ return 0;
+
+err_reg_notifiers:
+ for (i--; i >= 0; i--)
+ otg_id_unregister_notifier(&usbsw->notifiers[i].otg_id_nb);
+ sysfs_remove_group(&client->dev.kobj, &fsa9480_group);
+err_sys_create:
+err_reset:
+err_timing:
+err_reg_init:
+err_en_wake:
+ if (client->irq)
+ free_irq(client->irq, usbsw);
+err_req_irq:
+ free_irq(usbsw->pdata->external_vbus_irq, usbsw);
+err_req_vbus_irq:
+ if (usbsw->pdata->external_id >= 0)
+ free_irq(usbsw->external_id_irq, usbsw);
+err_req_id_irq:
+ if (usbsw->pdata->external_id >= 0)
+ gpio_free(usbsw->pdata->external_id);
+ mutex_destroy(&usbsw->lock);
+ i2c_set_clientdata(client, NULL);
+ kfree(usbsw);
+ return ret;
+}
+
+static int __devexit fsa9480_remove(struct i2c_client *client)
+{
+ struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
+ int i;
+
+#if defined(CONFIG_DEBUG_FS) && defined(DEBUG_DUMP_REGISTERS)
+ if (usbsw->debug_dir)
+ debugfs_remove_recursive(usbsw->debug_dir);
+#endif
+
+ for (i = 0; i < usbsw->num_notifiers; i++)
+ otg_id_unregister_notifier(&usbsw->notifiers[i].otg_id_nb);
+
+ if (usbsw->curr_dev != FSA9480_DETECT_NONE)
+ _detected(usbsw, FSA9480_DETECT_NONE);
+
+ if (client->irq) {
+ disable_irq_wake(client->irq);
+ free_irq(client->irq, usbsw);
+ }
+
+ if (usbsw->pdata->external_id >= 0) {
+ if (usbsw->wake_enabled)
+ disable_irq_wake(usbsw->external_id_irq);
+ free_irq(usbsw->external_id_irq, usbsw);
+ gpio_free(usbsw->pdata->external_id);
+ }
+
+ free_irq(usbsw->pdata->external_vbus_irq, usbsw);
+
+ i2c_set_clientdata(client, NULL);
+
+ sysfs_remove_group(&client->dev.kobj, &fsa9480_group);
+ mutex_destroy(&usbsw->lock);
+
+ kfree(usbsw);
+
+ return 0;
+}
+
+#if defined(CONFIG_PM)
+static int fsa9480_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
+
+ if (usbsw->wake_enabled) {
+ disable_irq_wake(usbsw->external_id_irq);
+ usbsw->wake_enabled = false;
+ }
+
+ otg_id_resume();
+ enable_irq(usbsw->external_id_irq);
+ enable_irq(client->irq);
+
+ return 0;
+}
+
+static int fsa9480_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct fsa9480_usbsw *usbsw = i2c_get_clientdata(client);
+ int ret;
+
+ disable_irq(client->irq);
+ disable_irq(usbsw->external_id_irq);
+
+ mutex_lock(&usbsw->lock);
+ if (usbsw->curr_dev == FSA9480_DETECT_USB_HOST) {
+ enable_irq_wake(usbsw->external_id_irq);
+ usbsw->wake_enabled = true;
+ }
+ mutex_unlock(&usbsw->lock);
+
+ ret = otg_id_suspend();
+ if (ret)
+ goto err;
+
+ return 0;
+
+err:
+ if (usbsw->wake_enabled) {
+ disable_irq_wake(usbsw->external_id_irq);
+ usbsw->wake_enabled = false;
+ }
+ enable_irq(usbsw->external_id_irq);
+ enable_irq(client->irq);
+ return ret;
+}
+
+static const struct dev_pm_ops fsa9480_pm_ops = {
+ .suspend = fsa9480_suspend,
+ .resume = fsa9480_resume,
+};
+#endif
+
+static const struct i2c_device_id fsa9480_id[] = {
+ {"fsa9480", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, fsa9480_id);
+
+static struct i2c_driver fsa9480_i2c_driver = {
+ .driver = {
+ .name = "fsa9480",
+#if defined(CONFIG_PM)
+ .pm = &fsa9480_pm_ops,
+#endif
+ },
+ .probe = fsa9480_probe,
+ .remove = __devexit_p(fsa9480_remove),
+ .id_table = fsa9480_id,
+};
+
+static int __init fsa9480_init(void)
+{
+ return i2c_add_driver(&fsa9480_i2c_driver);
+}
+module_init(fsa9480_init);
+
+static void __exit fsa9480_exit(void)
+{
+ i2c_del_driver(&fsa9480_i2c_driver);
+}
+module_exit(fsa9480_exit);
+
+MODULE_AUTHOR("Minkyu Kang <mk7.kang@samsung.com>");
+MODULE_DESCRIPTION("FSA9480 USB Switch driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/inv_mpu/Kconfig b/drivers/misc/inv_mpu/Kconfig
new file mode 100644
index 0000000..2eb4d64
--- /dev/null
+++ b/drivers/misc/inv_mpu/Kconfig
@@ -0,0 +1,29 @@
+config MPU_SENSORS_TIMERIRQ
+ tristate "MPU Timer IRQ"
+ help
+ If you say yes here you get access to the timerirq device handle which
+ can be used to select on. This can be used instead of IRQ's, sleeping,
+ or timer threads. Reading from this device returns the same type of
+ information as reading from the MPU and slave IRQ's.
+
+menuconfig: INV_SENSORS
+ bool "Motion Processing Unit"
+ depends on I2C
+ default n
+
+if INV_SENSORS
+
+config MPU_SENSORS_MPU3050
+ tristate "MPU3050"
+ depends on I2C && INV_SENSORS
+ select MPU_SENSORS_MPU3050_GYRO
+ help
+ If you say yes here you get support for the MPU3050 Gyroscope driver
+ This driver can also be built as a module. If so, the module
+ will be called mpu3050.
+
+source "drivers/misc/inv_mpu/accel/Kconfig"
+source "drivers/misc/inv_mpu/compass/Kconfig"
+source "drivers/misc/inv_mpu/pressure/Kconfig"
+
+endif #INV_SENSORS
diff --git a/drivers/misc/inv_mpu/Makefile b/drivers/misc/inv_mpu/Makefile
new file mode 100644
index 0000000..97f9be8
--- /dev/null
+++ b/drivers/misc/inv_mpu/Makefile
@@ -0,0 +1,22 @@
+
+# Kernel makefile for motions sensors
+#
+#
+
+# MPU
+obj-$(CONFIG_MPU_SENSORS_MPU3050) += mpu3050.o
+
+mpu3050-objs += mpuirq.o
+mpu3050-objs += slaveirq.o
+mpu3050-objs += mpu-dev.o
+mpu3050-objs += mlsl-kernel.o
+mpu3050-objs += mldl_cfg.o
+
+EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
+
+obj-$(CONFIG_MPU_SENSORS_TIMERIRQ)+= timerirq.o
+
+obj-y += accel/
+obj-y += compass/
+obj-y += pressure/
+
diff --git a/drivers/misc/inv_mpu/README b/drivers/misc/inv_mpu/README
new file mode 100644
index 0000000..322af09
--- /dev/null
+++ b/drivers/misc/inv_mpu/README
@@ -0,0 +1,276 @@
+Kernel driver mpu
+=====================
+
+Supported chips:
+ * InvenSense IMU3050
+ Prefix: 'mpu3050'
+ Datasheet:
+ PS-MPU-3000A-00.2.4b.pdf
+
+ * InvenSense IMU6000
+ Prefix: 'mpu6000'
+ Datasheet:
+ MPU-6000A-00 v1.0.pdf
+
+Author: InvenSense <http://invensense.com>
+
+Description
+-----------
+The mpu is a motion processor unit that controls the mpu3050 gyroscope, a slave
+accelerometer, a compass and a pressure sensor, or the mpu6000 and slave
+compass. This document describes how to install the driver into a Linux kernel
+and a small note about how to set up the file permissions in an android file
+system.
+
+Sysfs entries
+-------------
+/dev/mpu
+/dev/mpuirq
+/dev/accelirq
+/dev/compassirq
+/dev/pressureirq
+
+General Remarks MPU3050
+-----------------------
+* Valid addresses for the MPU3050 is 0x68.
+* Accelerometer must be on the secondary I2C bus for MPU3050, the
+ magnetometer must be on the primary bus and pressure sensor must
+ be on the primary bus.
+
+General Remarks MPU6000
+-----------------------
+* Valid addresses for the MPU6000 is 0x68.
+* Magnetometer must be on the secondary I2C bus for the MPU6000.
+* Accelerometer slave address must be set to 0x68
+* Gyro and Accel orientation matrices should be the same
+
+Programming the chip using /dev/mpu
+----------------------------------
+Programming of MPU3050 or MPU6000 is done by first opening the /dev/mpu file and
+then performing a series of IOCTLS on the handle returned. The IOCTL codes can
+be found in mpu.h. Typically this is done by the mllite library in user
+space.
+
+Adding to a Kernel
+==================
+
+The mpu driver is designed to be inserted in the drivers/misc part of the
+kernel. Extracting the tarball from the root kernel dir will place the
+contents of the tarball here:
+
+ <kernel root dir>/drivers/misc/inv_mpu
+ <kernel root dir>/include/linux/mpu.h
+ <kernel root dir>/include/linux/mpu3050.h
+ <kernel root dir>/include/linux/mpu6000.h
+
+After this is done the drivers/misc/Kconfig must be edited to add the line:
+
+ source "drivers/misc/inv_mpu/Kconfig"
+
+Similarly drivers/misc/Makefile must be edited to add the line:
+
+ obj-y += inv_mpu/
+
+Configuration can then be done as normal.
+
+NOTE: This driver depends on a kernel patch to drivers/char/char.c. This patch
+started to be included in most 2.6.35 based kernels.
+drivers: misc: pass miscdevice pointer via file private data
+https://patchwork.kernel.org/patch/96412/
+
+---
+ drivers/char/misc.c | 1 +
+ 1 files changed, 1 insertions(+), 0 deletions(-)
+
+
+diff --git a/drivers/char/misc.c b/drivers/char/misc.c
+index 92ab03d..cd650ca 100644
+--- a/drivers/char/misc.c
++++ b/drivers/char/misc.c
+@@ -144,6 +144,7 @@ static int misc_open(struct inode * inode, struct file * file)
+ old_fops = file->f_op;
+ file->f_op = new_fops;
+ if (file->f_op->open) {
++ file->private_data = c;
+ err=file->f_op->open(inode,file);
+ if (err) {
+ fops_put(file->f_op);
+---
+
+Board and Platform Data
+-----------------------
+
+In order for the driver to work, board and platform data specific to the device
+needs to be added to the board file. A mpu_platform_data structure must
+be created and populated and set in the i2c_board_info_structure. For details
+of each structure member see mpu.h. All values below are simply an example and
+should be modified for your platform.
+
+#include <linux/mpu.h>
+
+#if defined(CONFIG_MPU_SENSORS_MPU3050)
+
+static struct mpu_platform_data mpu_data = {
+ .int_config = 0x10,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ /* accel */
+ .accel = {
+#ifdef CONFIG_INV_SENSORS_MODULE
+ .get_slave_descr = NULL,
+#else
+ .get_slave_descr = get_accel_slave_descr,
+#endif
+ .irq = (IH_GPIO_BASE + 138),
+ .adapt_num = 2,
+ .bus = EXT_SLAVE_BUS_SECONDARY,
+#ifdef CONFIG_MPU_SENSORS_KXTF9
+ .address = 0x0F,
+#elif defined CONFIG_MPU_SENSORS_BMA150
+ .address = 0x38,
+#elif defined CONFIG_MPU_SENSORS_LIS331 || defined CONFIG_MPU_SENSORS_LIS303 \
+ || defined CONFIG_MPU_SENSORS_LIS3DH || defined CONFIG_MPU_SENSORS_KXSD9
+ .address = 0x18,
+#elif defined CONFIG_MPU_SENSORS_MMA845X || defined CONFIG_MPU_SENSORS_MMA8450
+ .address = 0x1C,
+#elif defined CONFIG_MPU_SENSORS_ADI346
+ .address = 0x53,
+#endif
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ },
+ /* compass */
+ .compass = {
+#ifdef CONFIG_INV_SENSORS_MODULE
+ .get_slave_descr = NULL,
+#else
+ .get_slave_descr = get_compass_slave_descr,
+#endif
+ .irq = (IH_GPIO_BASE + 137),
+ .adapt_num = 2,
+ .bus = EXT_SLAVE_BUS_PRIMARY,
+#ifdef CONFIG_MPU_SENSORS_AK8975
+ .address = 0x0E,
+#elif defined CONFIG_MPU_SENSORS_AMI30x
+ .address = 0x0E,
+#elif defined CONFIG_MPU_SENSORS_AMI306
+ .address = 0x0E,
+#elif defined CONFIG_MPU_SENSORS_YAS529
+ .address = 0x2E,
+#elif defined CONFIG_MPU_SENSORS_MMC314X
+ .address = 0x30,
+#elif defined CONFIG_MPU_SENSORS_HSCDTD00XX
+ .address = 0x0C,
+#endif
+ .orientation = { 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1 },
+ },
+};
+#endif
+
+#if defined(CONFIG_MPU_SENSORS_MPU6050A2) || \
+ defined(CONFIG_MPU_SENSORS_MPU6050B1)
+
+static struct mpu_platform_data mpu_data = {
+ .int_config = 0x10,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ /* accel */
+ .accel = {
+#if defined CONFIG_INV_SENSORS_MODULE
+ .get_slave_descr = NULL,
+#else
+ .get_slave_descr = get_accel_slave_descr,
+#endif
+ .irq = (IH_GPIO_BASE + 137),
+ .adapt_num = 2,
+ .bus = EXT_SLAVE_BUS_PRIMARY,
+ .address = 0x68,
+ .orientation = { -1, 0, 0,
+ 0, 1, 0,
+ 0, 0, -1 },
+ },
+ /* compass */
+ .compass = {
+#if defined CONFIG_INV_SENSORS_MODULE
+ .get_slave_descr = NULL,
+#else
+ .get_slave_descr = get_compass_slave_descr,
+#endif
+ .irq = (IH_GPIO_BASE + 138),
+ .adapt_num = 2,
+ .bus = EXT_SLAVE_BUS_SECONDARY,
+#ifdef CONFIG_MPU_SENSORS_AK8975
+ .address = 0x0E,
+#elif defined CONFIG_MPU_SENSORS_AMI30x
+ .address = 0x0E,
+#elif defined CONFIG_MPU_SENSORS_AMI306
+ .address = 0x0E,
+#elif defined CONFIG_MPU_SENSORS_YAS529
+ .address = 0x2E,
+#elif defined CONFIG_MPU_SENSORS_MMC314X
+ .address = 0x30,
+#elif defined CONFIG_MPU_SENSORS_HSCDTD00XX
+ .address = 0x0C,
+#endif
+ .orientation = { 1, 0, 0,
+ 0, 1, 0,
+ 0, 0, 1 },
+ },
+};
+
+#endif
+
+static struct i2c_board_info __initdata beagle_i2c_2_boardinfo[] = {
+#if defined(CONFIG_MPU_SENSORS_MPU3050) || \
+ defined(CONFIG_MPU_SENSORS_MPU6050A2) || \
+ defined(CONFIG_MPU_SENSORS_MPU6050B1)
+ {
+ I2C_BOARD_INFO(MPU_NAME, 0x68),
+ .irq = (IH_GPIO_BASE + MPUIRQ_GPIO),
+ .platform_data = &mpu_data,
+ },
+#endif
+};
+
+Typically the IRQ is a GPIO input pin and needs to be configured properly. If
+in the above example GPIO 168 corresponds to IRQ 299, the following should be
+done as well:
+
+#define MPU_GPIO_IRQ 168
+
+ gpio_request(MPU_GPIO_IRQ,"MPUIRQ");
+ gpio_direction_input(MPU_GPIO_IRQ)
+
+Dynamic Debug
+=============
+
+The mpu3050 makes use of dynamic debug. For details on how to use this
+refer to Documentation/dynamic-debug-howto.txt
+
+Android File Permissions
+========================
+
+To set up the file permissions on an android system, the /dev/mpu and
+/dev/mpuirq files needs to be added to the system/core/init/devices.c file
+inside the perms_ structure.
+
+static struct perms_ devperms[] = {
+ { "/dev/mpu" ,0660, AID_SYSTEM, AID_SYSTEM, 1 },
+};
+
+Sufficient file permissions need to be give to read and write it by the system.
+
+For gingerbread and later the system/core/rootdir/ueventd.rc file needs to be
+modified with the appripriate lines added.
+
+# MPU sensors and IRQ
+/dev/mpu 0660 system system
+/dev/mpuirq 0660 system system
+/dev/accelirq 0660 system system
+/dev/compassirq 0660 system system
+/dev/pressureirq 0660 system system
diff --git a/drivers/misc/inv_mpu/accel/Kconfig b/drivers/misc/inv_mpu/accel/Kconfig
new file mode 100644
index 0000000..bef6d61
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/Kconfig
@@ -0,0 +1,133 @@
+menuconfig INV_SENSORS_ACCELEROMETERS
+ bool "Accelerometer Slave Sensors"
+ default y
+ ---help---
+ Say Y here to get to see options for device drivers for various
+ accelerometrs for integration with the MPU3050 or MPU6050 driver.
+ This option alone does not add any kernel code.
+
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if INV_SENSORS_ACCELEROMETERS
+
+config MPU_SENSORS_ADXL34X
+ tristate "ADI adxl34x"
+ depends on MPU_SENSORS_MPU3050
+ help
+ This enables support for the ADI adxl345 or adxl346 accelerometers.
+ This support is for integration with the MPU3050 gyroscope device
+ driver. Only one accelerometer can be registered at a time.
+ Specifying more that one accelerometer in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_BMA222
+ tristate "Bosch BMA222"
+ depends on MPU_SENSORS_MPU3050
+ help
+ This enables support for the Bosch BMA222 accelerometer
+ This support is for integration with the MPU3050 gyroscope device
+ driver. Only one accelerometer can be registered at a time.
+ Specifying more that one accelerometer in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_BMA150
+ tristate "Bosch BMA150"
+ depends on MPU_SENSORS_MPU3050
+ help
+ This enables support for the Bosch BMA150 accelerometer
+ This support is for integration with the MPU3050 gyroscope device
+ driver. Only one accelerometer can be registered at a time.
+ Specifying more that one accelerometer in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_BMA250
+ tristate "Bosch BMA250"
+ depends on MPU_SENSORS_MPU3050
+ help
+ This enables support for the Bosch BMA250 accelerometer
+ This support is for integration with the MPU3050 gyroscope device
+ driver. Only one accelerometer can be registered at a time.
+ Specifying more that one accelerometer in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_KXSD9
+ tristate "Kionix KXSD9"
+ depends on MPU_SENSORS_MPU3050
+ help
+ This enables support for the Kionix KXSD9 accelerometer
+ This support is for integration with the MPU3050 gyroscope device
+ driver. Only one accelerometer can be registered at a time.
+ Specifying more that one accelerometer in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_KXTF9
+ tristate "Kionix KXTF9"
+ depends on MPU_SENSORS_MPU3050
+ help
+ This enables support for the Kionix KXFT9 accelerometer
+ This support is for integration with the MPU3050 gyroscope device
+ driver. Only one accelerometer can be registered at a time.
+ Specifying more that one accelerometer in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_LIS331DLH
+ tristate "ST lis331dlh"
+ depends on MPU_SENSORS_MPU3050
+ help
+ This enables support for the ST lis331dlh accelerometer
+ This support is for integration with the MPU3050 gyroscope device
+ driver. Only one accelerometer can be registered at a time.
+ Specifying more that one accelerometer in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_LIS3DH
+ tristate "ST lis3dh"
+ depends on MPU_SENSORS_MPU3050
+ help
+ This enables support for the ST lis3dh accelerometer
+ This support is for integration with the MPU3050 gyroscope device
+ driver. Only one accelerometer can be registered at a time.
+ Specifying more that one accelerometer in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_LSM303DLHA
+ tristate "ST lsm303dlh"
+ depends on MPU_SENSORS_MPU3050
+ help
+ This enables support for the ST lsm303dlh accelerometer
+ This support is for integration with the MPU3050 gyroscope device
+ driver. Only one accelerometer can be registered at a time.
+ Specifying more that one accelerometer in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_MMA8450
+ tristate "Freescale mma8450"
+ depends on MPU_SENSORS_MPU3050
+ help
+ This enables support for the Freescale mma8450 accelerometer
+ This support is for integration with the MPU3050 gyroscope device
+ driver. Only one accelerometer can be registered at a time.
+ Specifying more that one accelerometer in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_MMA845X
+ tristate "Freescale mma8451/8452/8453"
+ depends on MPU_SENSORS_MPU3050
+ help
+ This enables support for the Freescale mma8451/8452/8453 accelerometer
+ This support is for integration with the MPU3050 gyroscope device
+ driver. Only one accelerometer can be registered at a time.
+ Specifying more that one accelerometer in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_MPU6050_ACCEL
+ tristate "MPU6050 built in accelerometer"
+ depends on MPU_SENSORS_MPU6050B1 || MPU_SENSORS_MPU6050A2
+ help
+ This enables support for the MPU6050 built in accelerometer.
+ This the built in support for integration with the MPU6050 gyroscope
+ device driver. This is the only accelerometer supported with the
+ MPU6050. Specifying another accelerometer in the board file will
+ result in runtime errors.
+
+endif
diff --git a/drivers/misc/inv_mpu/accel/Makefile b/drivers/misc/inv_mpu/accel/Makefile
new file mode 100644
index 0000000..10699fb
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/Makefile
@@ -0,0 +1,37 @@
+#
+# Accel Slaves to MPUxxxx
+#
+obj-$(CONFIG_MPU_SENSORS_ADXL34X) += inv_mpu_adxl34x.o
+inv_mpu_adxl34x-objs += adxl34x.o
+
+obj-$(CONFIG_MPU_SENSORS_BMA150) += inv_mpu_bma150.o
+inv_mpu_bma150-objs += bma150.o
+
+obj-$(CONFIG_MPU_SENSORS_KXTF9) += inv_mpu_kxtf9.o
+inv_mpu_kxtf9-objs += kxtf9.o
+
+obj-$(CONFIG_MPU_SENSORS_BMA222) += inv_mpu_bma222.o
+inv_mpu_bma222-objs += bma222.o
+
+obj-$(CONFIG_MPU_SENSORS_BMA250) += inv_mpu_bma250.o
+inv_mpu_bma250-objs += bma250.o
+
+obj-$(CONFIG_MPU_SENSORS_KXSD9) += inv_mpu_kxsd9.o
+inv_mpu_kxsd9-objs += kxsd9.o
+
+obj-$(CONFIG_MPU_SENSORS_LIS331DLH) += inv_mpu_lis331.o
+inv_mpu_lis331-objs += lis331.o
+
+obj-$(CONFIG_MPU_SENSORS_LIS3DH) += inv_mpu_lis3dh.o
+inv_mpu_lis3dh-objs += lis3dh.o
+
+obj-$(CONFIG_MPU_SENSORS_LSM303DLHA) += inv_mpu_lsm303a.o
+inv_mpu_lsm303a-objs += lsm303a.o
+
+obj-$(CONFIG_MPU_SENSORS_MMA8450) += inv_mpu_mma8450.o
+inv_mpu_mma8450-objs += mma8450.o
+
+obj-$(CONFIG_MPU_SENSORS_MMA845X) += inv_mpu_mma845x.o
+inv_mpu_mma845x-objs += mma845x.o
+
+EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
diff --git a/drivers/misc/inv_mpu/accel/adxl34x.c b/drivers/misc/inv_mpu/accel/adxl34x.c
new file mode 100644
index 0000000..a389d5b
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/adxl34x.c
@@ -0,0 +1,728 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup ACCELDL
+ * @brief Provides the interface to setup and handle an accelerometer.
+ *
+ * @{
+ * @file adxl34x.c
+ * @brief Accelerometer setup and handling methods for AD adxl345 and
+ * adxl346.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+/* -------------------------------------------------------------------------- */
+
+/* registers */
+#define ADXL34X_ODR_REG (0x2C)
+#define ADXL34X_PWR_REG (0x2D)
+#define ADXL34X_DATAFORMAT_REG (0x31)
+
+/* masks */
+#define ADXL34X_ODR_MASK (0x0F)
+#define ADXL34X_PWR_SLEEP_MASK (0x04)
+#define ADXL34X_PWR_MEAS_MASK (0x08)
+#define ADXL34X_DATAFORMAT_JUSTIFY_MASK (0x04)
+#define ADXL34X_DATAFORMAT_FSR_MASK (0x03)
+
+/* -------------------------------------------------------------------------- */
+
+struct adxl34x_config {
+ unsigned int odr; /** < output data rate in mHz */
+ unsigned int fsr; /** < full scale range mg */
+ unsigned int fsr_reg_mask; /** < register setting for fsr */
+};
+
+struct adxl34x_private_data {
+ struct adxl34x_config suspend; /** < suspend configuration */
+ struct adxl34x_config resume; /** < resume configuration */
+};
+
+/**
+ * @brief Set the output data rate for the particular configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * Config to modify with new ODR.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param odr
+ * Output data rate in units of 1/1000Hz (mHz).
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int adxl34x_set_odr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct adxl34x_config *config,
+ int apply,
+ long odr)
+{
+ int result = INV_SUCCESS;
+ unsigned char new_odr_mask;
+
+ /* ADXL346 (Rev. A) pages 13, 24 */
+ if (odr >= 3200000) {
+ new_odr_mask = 0x0F;
+ config->odr = 3200000;
+ } else if (odr >= 1600000) {
+ new_odr_mask = 0x0E;
+ config->odr = 1600000;
+ } else if (odr >= 800000) {
+ new_odr_mask = 0x0D;
+ config->odr = 800000;
+ } else if (odr >= 400000) {
+ new_odr_mask = 0x0C;
+ config->odr = 400000;
+ } else if (odr >= 200000) {
+ new_odr_mask = 0x0B;
+ config->odr = 200000;
+ } else if (odr >= 100000) {
+ new_odr_mask = 0x0A;
+ config->odr = 100000;
+ } else if (odr >= 50000) {
+ new_odr_mask = 0x09;
+ config->odr = 50000;
+ } else if (odr >= 25000) {
+ new_odr_mask = 0x08;
+ config->odr = 25000;
+ } else if (odr >= 12500) {
+ new_odr_mask = 0x07;
+ config->odr = 12500;
+ } else if (odr >= 6250) {
+ new_odr_mask = 0x06;
+ config->odr = 6250;
+ } else if (odr >= 3130) {
+ new_odr_mask = 0x05;
+ config->odr = 3130;
+ } else if (odr >= 1560) {
+ new_odr_mask = 0x04;
+ config->odr = 1560;
+ } else if (odr >= 780) {
+ new_odr_mask = 0x03;
+ config->odr = 780;
+ } else if (odr >= 390) {
+ new_odr_mask = 0x02;
+ config->odr = 390;
+ } else if (odr >= 200) {
+ new_odr_mask = 0x01;
+ config->odr = 200;
+ } else {
+ new_odr_mask = 0x00;
+ config->odr = 100;
+ }
+
+ if (apply) {
+ unsigned char reg_odr;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ ADXL34X_ODR_REG, 1, &reg_odr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ reg_odr &= ~ADXL34X_ODR_MASK;
+ reg_odr |= new_odr_mask;
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ADXL34X_ODR_REG, reg_odr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ MPL_LOGV("ODR: %d mHz\n", config->odr);
+ }
+ return result;
+}
+
+/**
+ * @brief Set the full scale range of the accels
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * pointer to configuration.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param fsr
+ * requested full scale range in milli gees (mg).
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int adxl34x_set_fsr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct adxl34x_config *config,
+ int apply,
+ long fsr)
+{
+ int result = INV_SUCCESS;
+
+ if (fsr <= 2000) {
+ config->fsr_reg_mask = 0x00;
+ config->fsr = 2000;
+ } else if (fsr <= 4000) {
+ config->fsr_reg_mask = 0x01;
+ config->fsr = 4000;
+ } else if (fsr <= 8000) {
+ config->fsr_reg_mask = 0x02;
+ config->fsr = 8000;
+ } else { /* 8001 -> oo */
+ config->fsr_reg_mask = 0x03;
+ config->fsr = 16000;
+ }
+
+ if (apply) {
+ unsigned char reg_df;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ ADXL34X_DATAFORMAT_REG, 1, &reg_df);
+ reg_df &= ~ADXL34X_DATAFORMAT_FSR_MASK;
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ADXL34X_DATAFORMAT_REG,
+ reg_df | config->fsr_reg_mask);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ MPL_LOGV("FSR: %d mg\n", config->fsr);
+ }
+ return result;
+}
+
+/**
+ * @brief facility to retrieve the device configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a pointer to store the returned configuration data structure.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int adxl34x_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct adxl34x_private_data *private_data =
+ (struct adxl34x_private_data *)(pdata->private_data);
+
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.odr;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.odr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief device configuration facility.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a pointer to the configuration data structure.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int adxl34x_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct adxl34x_private_data *private_data =
+ (struct adxl34x_private_data *)(pdata->private_data);
+
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ return adxl34x_set_odr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ return adxl34x_set_odr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ return adxl34x_set_fsr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ return adxl34x_set_fsr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief suspends the device to put it in its lowest power mode.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int adxl34x_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+
+ /*
+ struct adxl34x_config *suspend_config =
+ &((struct adxl34x_private_data *)pdata->private_data)->suspend;
+
+ result = adxl34x_set_odr(mlsl_handle, pdata, suspend_config,
+ TRUE, suspend_config->odr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+}
+ result = adxl34x_set_fsr(mlsl_handle, pdata, suspend_config,
+ TRUE, suspend_config->fsr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+}
+ */
+
+ /*
+ Page 25
+ When clearing the sleep bit, it is recommended that the part
+ be placed into standby mode and then set back to measurement mode
+ with a subsequent write.
+ This is done to ensure that the device is properly biased if sleep
+ mode is manually disabled; otherwise, the first few samples of data
+ after the sleep bit is cleared may have additional noise,
+ especially if the device was asleep when the bit was cleared. */
+
+ /* go in standy-by mode (suspends measurements) */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ADXL34X_PWR_REG, ADXL34X_PWR_MEAS_MASK);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* and then in sleep */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ADXL34X_PWR_REG,
+ ADXL34X_PWR_MEAS_MASK | ADXL34X_PWR_SLEEP_MASK);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+/**
+ * @brief resume the device in the proper power state given the configuration
+ * chosen.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int adxl34x_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ struct adxl34x_config *resume_config =
+ &((struct adxl34x_private_data *)pdata->private_data)->resume;
+ unsigned char reg;
+
+ /*
+ Page 25
+ When clearing the sleep bit, it is recommended that the part
+ be placed into standby mode and then set back to measurement mode
+ with a subsequent write.
+ This is done to ensure that the device is properly biased if sleep
+ mode is manually disabled; otherwise, the first few samples of data
+ after the sleep bit is cleared may have additional noise,
+ especially if the device was asleep when the bit was cleared. */
+
+ /* remove sleep, but leave in stand-by */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ADXL34X_PWR_REG, ADXL34X_PWR_MEAS_MASK);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = adxl34x_set_odr(mlsl_handle, pdata, resume_config,
+ TRUE, resume_config->odr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /*
+ -> FSR
+ -> Justiy bit for Big endianess
+ -> resulution to 10 bits
+ */
+ reg = ADXL34X_DATAFORMAT_JUSTIFY_MASK;
+ reg |= resume_config->fsr_reg_mask;
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ADXL34X_DATAFORMAT_REG, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* go in measurement mode */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ADXL34X_PWR_REG, 0x00);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* DATA_FORMAT: full resolution of +/-2g; data is left justified */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ 0x31, reg);
+
+ return result;
+}
+
+/**
+ * @brief one-time device driver initialization function.
+ * If the driver is built as a kernel module, this function will be
+ * called when the module is loaded in the kernel.
+ * If the driver is built-in in the kernel, this function will be
+ * called at boot time.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int adxl34x_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ long range;
+
+ struct adxl34x_private_data *private_data;
+ private_data = (struct adxl34x_private_data *)
+ kzalloc(sizeof(struct adxl34x_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ pdata->private_data = private_data;
+
+ result = adxl34x_set_odr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = adxl34x_set_odr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 200000);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ range = range_fixedpoint_to_long_mg(slave->range);
+ result = adxl34x_set_fsr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, range);
+ result = adxl34x_set_fsr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, range);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = adxl34x_suspend(mlsl_handle, slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * @brief one-time device driver exit function.
+ * If the driver is built as a kernel module, this function will be
+ * called when the module is removed from the kernel.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int adxl34x_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief read the sensor data from the device.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a buffer to store the data read.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int adxl34x_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ slave->read_reg, slave->read_len, data);
+ return result;
+}
+
+static struct ext_slave_descr adxl34x_descr = {
+ .init = adxl34x_init,
+ .exit = adxl34x_exit,
+ .suspend = adxl34x_suspend,
+ .resume = adxl34x_resume,
+ .read = adxl34x_read,
+ .config = adxl34x_config,
+ .get_config = adxl34x_get_config,
+ .name = "adxl34x", /* 5 or 6 */
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ACCEL_ID_ADXL34X,
+ .read_reg = 0x32,
+ .read_len = 6,
+ .endian = EXT_SLAVE_LITTLE_ENDIAN,
+ .range = {2, 0},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *adxl34x_get_slave_descr(void)
+{
+ return &adxl34x_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct adxl34x_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int adxl34x_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct adxl34x_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ adxl34x_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int adxl34x_mod_remove(struct i2c_client *client)
+{
+ struct adxl34x_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ adxl34x_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id adxl34x_mod_id[] = {
+ { "adxl34x", ACCEL_ID_ADXL34X },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, adxl34x_mod_id);
+
+static struct i2c_driver adxl34x_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = adxl34x_mod_probe,
+ .remove = adxl34x_mod_remove,
+ .id_table = adxl34x_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "adxl34x_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init adxl34x_mod_init(void)
+{
+ int res = i2c_add_driver(&adxl34x_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "adxl34x_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit adxl34x_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&adxl34x_mod_driver);
+}
+
+module_init(adxl34x_mod_init);
+module_exit(adxl34x_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate ADXL34X sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("adxl34x_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/accel/bma150.c b/drivers/misc/inv_mpu/accel/bma150.c
new file mode 100644
index 0000000..2d0a511
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/bma150.c
@@ -0,0 +1,777 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup ACCELDL
+ * @brief Provides the interface to setup and handle an accelerometer.
+ *
+ * @{
+ * @file bma150.c
+ * @brief Accelerometer setup and handling methods for Bosch BMA150.
+ */
+
+/* -------------------------------------------------------------------------- */
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+
+/* -------------------------------------------------------------------------- */
+/* registers */
+#define BMA150_CTRL_REG (0x14)
+#define BMA150_INT_REG (0x15)
+#define BMA150_PWR_REG (0x0A)
+
+/* masks */
+#define BMA150_CTRL_MASK (0x18)
+#define BMA150_CTRL_MASK_ODR (0xF8)
+#define BMA150_CTRL_MASK_FSR (0xE7)
+#define BMA150_INT_MASK_WUP (0xF8)
+#define BMA150_INT_MASK_IRQ (0xDF)
+#define BMA150_PWR_MASK_SLEEP (0x01)
+#define BMA150_PWR_MASK_SOFT_RESET (0x02)
+
+/* -------------------------------------------------------------------------- */
+struct bma150_config {
+ unsigned int odr; /** < output data rate mHz */
+ unsigned int fsr; /** < full scale range mgees */
+ unsigned int irq_type; /** < type of IRQ, see bma150_set_irq */
+ unsigned char ctrl_reg; /** < control register value */
+ unsigned char int_reg; /** < interrupt control register value */
+};
+
+struct bma150_private_data {
+ struct bma150_config suspend; /** < suspend configuration */
+ struct bma150_config resume; /** < resume configuration */
+};
+
+/**
+ * @brief Simply disables the IRQ since it is not usable on BMA150 devices.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * configuration to apply to, suspend or resume
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param irq_type
+ * the type of IRQ. Valid values are
+ * - MPU_SLAVE_IRQ_TYPE_NONE
+ * - MPU_SLAVE_IRQ_TYPE_MOTION
+ * - MPU_SLAVE_IRQ_TYPE_DATA_READY
+ * The only supported IRQ type is MPU_SLAVE_IRQ_TYPE_NONE which
+ * corresponds to disabling the IRQ completely.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma150_set_irq(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct bma150_config *config,
+ int apply,
+ long irq_type)
+{
+ int result = INV_SUCCESS;
+
+ if (irq_type != MPU_SLAVE_IRQ_TYPE_NONE)
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+
+ config->irq_type = MPU_SLAVE_IRQ_TYPE_NONE;
+ config->int_reg = 0x00;
+
+ if (apply) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_CTRL_REG, config->ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_INT_REG, config->int_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ return result;
+}
+
+/**
+ * @brief Set the output data rate for the particular configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * Config to modify with new ODR.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param odr
+ * Output data rate in units of 1/1000Hz (mHz).
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma150_set_odr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct bma150_config *config,
+ int apply,
+ long odr)
+{
+ unsigned char odr_bits = 0;
+ unsigned char wup_bits = 0;
+ int result = INV_SUCCESS;
+
+ if (odr > 100000) {
+ config->odr = 190000;
+ odr_bits = 0x03;
+ } else if (odr > 50000) {
+ config->odr = 100000;
+ odr_bits = 0x02;
+ } else if (odr > 25000) {
+ config->odr = 50000;
+ odr_bits = 0x01;
+ } else if (odr > 0) {
+ config->odr = 25000;
+ odr_bits = 0x00;
+ } else {
+ config->odr = 0;
+ wup_bits = 0x00;
+ }
+
+ config->int_reg &= BMA150_INT_MASK_WUP;
+ config->ctrl_reg &= BMA150_CTRL_MASK_ODR;
+ config->ctrl_reg |= odr_bits;
+
+ MPL_LOGV("ODR: %d\n", config->odr);
+ if (apply) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_CTRL_REG, config->ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_INT_REG, config->int_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * @brief Set the full scale range of the accels
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * pointer to configuration.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param fsr
+ * requested full scale range.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma150_set_fsr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct bma150_config *config,
+ int apply,
+ long fsr)
+{
+ unsigned char fsr_bits;
+ int result = INV_SUCCESS;
+
+ if (fsr <= 2048) {
+ fsr_bits = 0x00;
+ config->fsr = 2048;
+ } else if (fsr <= 4096) {
+ fsr_bits = 0x08;
+ config->fsr = 4096;
+ } else {
+ fsr_bits = 0x10;
+ config->fsr = 8192;
+ }
+
+ config->ctrl_reg &= BMA150_CTRL_MASK_FSR;
+ config->ctrl_reg |= fsr_bits;
+
+ MPL_LOGV("FSR: %d\n", config->fsr);
+ if (apply) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_CTRL_REG, config->ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_CTRL_REG, config->ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ return result;
+}
+
+/**
+ * @brief one-time device driver initialization function.
+ * If the driver is built as a kernel module, this function will be
+ * called when the module is loaded in the kernel.
+ * If the driver is built-in in the kernel, this function will be
+ * called at boot time.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma150_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ unsigned char reg;
+ long range;
+
+ struct bma150_private_data *private_data;
+ private_data = (struct bma150_private_data *)
+ kzalloc(sizeof(struct bma150_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ pdata->private_data = private_data;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_PWR_REG, BMA150_PWR_MASK_SOFT_RESET);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(1);
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ BMA150_CTRL_REG, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ private_data->resume.ctrl_reg = reg;
+ private_data->suspend.ctrl_reg = reg;
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ BMA150_INT_REG, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ private_data->resume.int_reg = reg;
+ private_data->suspend.int_reg = reg;
+
+ result = bma150_set_odr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = bma150_set_odr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 200000);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ range = range_fixedpoint_to_long_mg(slave->range);
+ result = bma150_set_fsr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, range);
+ result = bma150_set_fsr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, range);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = bma150_set_irq(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = bma150_set_irq(mlsl_handle, pdata, &private_data->resume,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_PWR_REG, BMA150_PWR_MASK_SLEEP);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * @brief one-time device driver exit function.
+ * If the driver is built as a kernel module, this function will be
+ * called when the module is removed from the kernel.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma150_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief device configuration facility.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a pointer to the configuration data structure.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma150_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct bma150_private_data *private_data =
+ (struct bma150_private_data *)(pdata->private_data);
+
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ return bma150_set_odr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ return bma150_set_odr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ return bma150_set_fsr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ return bma150_set_fsr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ return bma150_set_irq(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ return bma150_set_irq(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief facility to retrieve the device configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a pointer to store the returned configuration data structure.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma150_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct bma150_private_data *private_data =
+ (struct bma150_private_data *)(pdata->private_data);
+
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.odr;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.odr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.irq_type;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.irq_type;
+ break;
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief suspends the device to put it in its lowest power mode.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma150_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ unsigned char ctrl_reg;
+ unsigned char int_reg;
+
+ struct bma150_private_data *private_data =
+ (struct bma150_private_data *)(pdata->private_data);
+
+ ctrl_reg = private_data->suspend.ctrl_reg;
+ int_reg = private_data->suspend.int_reg;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_PWR_REG, BMA150_PWR_MASK_SOFT_RESET);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(1);
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_CTRL_REG, ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_INT_REG, int_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_PWR_REG, BMA150_PWR_MASK_SLEEP);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * @brief resume the device in the proper power state given the configuration
+ * chosen.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma150_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ unsigned char ctrl_reg;
+ unsigned char int_reg;
+
+ struct bma150_private_data *private_data =
+ (struct bma150_private_data *)(pdata->private_data);
+
+ ctrl_reg = private_data->resume.ctrl_reg;
+ int_reg = private_data->resume.int_reg;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_PWR_REG, BMA150_PWR_MASK_SOFT_RESET);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(1);
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_CTRL_REG, ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_INT_REG, int_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA150_PWR_REG, 0x00);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * @brief read the sensor data from the device.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a buffer to store the data read.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma150_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ return inv_serial_read(mlsl_handle, pdata->address,
+ slave->read_reg, slave->read_len, data);
+}
+
+static struct ext_slave_descr bma150_descr = {
+ .init = bma150_init,
+ .exit = bma150_exit,
+ .suspend = bma150_suspend,
+ .resume = bma150_resume,
+ .read = bma150_read,
+ .config = bma150_config,
+ .get_config = bma150_get_config,
+ .name = "bma150",
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ACCEL_ID_BMA150,
+ .read_reg = 0x02,
+ .read_len = 6,
+ .endian = EXT_SLAVE_LITTLE_ENDIAN,
+ .range = {2, 0},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *bma150_get_slave_descr(void)
+{
+ return &bma150_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Platform data for the MPU */
+struct bma150_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int bma150_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct bma150_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ bma150_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int bma150_mod_remove(struct i2c_client *client)
+{
+ struct bma150_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ bma150_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id bma150_mod_id[] = {
+ { "bma150", ACCEL_ID_BMA150 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, bma150_mod_id);
+
+static struct i2c_driver bma150_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = bma150_mod_probe,
+ .remove = bma150_mod_remove,
+ .id_table = bma150_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bma150_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init bma150_mod_init(void)
+{
+ int res = i2c_add_driver(&bma150_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "bma150_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit bma150_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&bma150_mod_driver);
+}
+
+module_init(bma150_mod_init);
+module_exit(bma150_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate BMA150 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("bma150_mod");
+
+/**
+ * @}
+ */
+
diff --git a/drivers/misc/inv_mpu/accel/bma222.c b/drivers/misc/inv_mpu/accel/bma222.c
new file mode 100644
index 0000000..94c6a6d
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/bma222.c
@@ -0,0 +1,654 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/*
+ * @addtogroup ACCELDL
+ * @brief Provides the interface to setup and handle an accelerometer.
+ *
+ * @{
+ * @file bma222.c
+ * @brief Accelerometer setup and handling methods for Bosch BMA222.
+ */
+
+/* ------------------ */
+/* - Include Files. - */
+/* ------------------ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+
+/* -------------------------------------------------------------------------- */
+
+#define BMA222_STATUS_REG (0x0A)
+#define BMA222_FSR_REG (0x0F)
+#define ADXL34X_ODR_REG (0x10)
+#define BMA222_PWR_REG (0x11)
+#define BMA222_SOFTRESET_REG (0x14)
+
+#define BMA222_STATUS_RDY_MASK (0x80)
+#define BMA222_FSR_MASK (0x0F)
+#define BMA222_ODR_MASK (0x1F)
+#define BMA222_PWR_SLEEP_MASK (0x80)
+#define BMA222_PWR_AWAKE_MASK (0x00)
+#define BMA222_SOFTRESET_MASK (0xB6)
+#define BMA222_SOFTRESET_MASK (0xB6)
+
+/* -------------------------------------------------------------------------- */
+
+struct bma222_config {
+ unsigned int odr; /** < output data rate in mHz */
+ unsigned int fsr; /** < full scale range mg */
+};
+
+struct bma222_private_data {
+ struct bma222_config suspend; /** < suspend configuration */
+ struct bma222_config resume; /** < resume configuration */
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * @brief Set the output data rate for the particular configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * Config to modify with new ODR.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param odr
+ * Output data rate in units of 1/1000Hz (mHz).
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma222_set_odr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct bma222_config *config,
+ int apply,
+ long odr)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg_odr;
+
+ if (odr >= 1000000) {
+ reg_odr = 0x0F;
+ config->odr = 1000000;
+ } else if (odr >= 500000) {
+ reg_odr = 0x0E;
+ config->odr = 500000;
+ } else if (odr >= 250000) {
+ reg_odr = 0x0D;
+ config->odr = 250000;
+ } else if (odr >= 125000) {
+ reg_odr = 0x0C;
+ config->odr = 125000;
+ } else if (odr >= 62500) {
+ reg_odr = 0x0B;
+ config->odr = 62500;
+ } else if (odr >= 32000) {
+ reg_odr = 0x0A;
+ config->odr = 32000;
+ } else if (odr >= 16000) {
+ reg_odr = 0x09;
+ config->odr = 16000;
+ } else {
+ reg_odr = 0x08;
+ config->odr = 8000;
+ }
+
+ if (apply) {
+ MPL_LOGV("ODR: %d\n", config->odr);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ADXL34X_ODR_REG, reg_odr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ return result;
+}
+
+/**
+ * @brief Set the full scale range of the accels
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * pointer to configuration.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param fsr
+ * requested full scale range.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma222_set_fsr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct bma222_config *config,
+ int apply,
+ long fsr)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg_fsr_mask;
+
+ if (fsr <= 2000) {
+ reg_fsr_mask = 0x03;
+ config->fsr = 2000;
+ } else if (fsr <= 4000) {
+ reg_fsr_mask = 0x05;
+ config->fsr = 4000;
+ } else if (fsr <= 8000) {
+ reg_fsr_mask = 0x08;
+ config->fsr = 8000;
+ } else { /* 8001 -> oo */
+ reg_fsr_mask = 0x0C;
+ config->fsr = 16000;
+ }
+
+ if (apply) {
+ MPL_LOGV("FSR: %d\n", config->fsr);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA222_FSR_REG, reg_fsr_mask);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ return result;
+}
+
+/**
+ * @brief one-time device driver initialization function.
+ * If the driver is built as a kernel module, this function will be
+ * called when the module is loaded in the kernel.
+ * If the driver is built-in in the kernel, this function will be
+ * called at boot time.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma222_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+
+ struct bma222_private_data *private_data;
+ private_data = (struct bma222_private_data *)
+ kzalloc(sizeof(struct bma222_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ pdata->private_data = private_data;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA222_SOFTRESET_REG, BMA222_SOFTRESET_MASK);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(1);
+
+ result = bma222_set_odr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = bma222_set_odr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 200000);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = bma222_set_fsr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 2000);
+ result = bma222_set_fsr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 2000);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA222_PWR_REG, BMA222_PWR_SLEEP_MASK);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * @brief one-time device driver exit function.
+ * If the driver is built as a kernel module, this function will be
+ * called when the module is removed from the kernel.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma222_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+
+/**
+ * @brief facility to retrieve the device configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a pointer to store the returned configuration data structure.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma222_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct bma222_private_data *private_data =
+ (struct bma222_private_data *)(pdata->private_data);
+
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.odr;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.odr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief device configuration facility.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a pointer to the configuration data structure.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma222_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct bma222_private_data *private_data =
+ (struct bma222_private_data *)(pdata->private_data);
+
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ return bma222_set_odr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ return bma222_set_odr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ return bma222_set_fsr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ return bma222_set_fsr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief suspends the device to put it in its lowest power mode.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma222_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ struct bma222_config *suspend_config =
+ &((struct bma222_private_data *)pdata->private_data)->suspend;
+
+ result = bma222_set_odr(mlsl_handle, pdata, suspend_config,
+ TRUE, suspend_config->odr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = bma222_set_fsr(mlsl_handle, pdata, suspend_config,
+ TRUE, suspend_config->fsr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA222_PWR_REG, BMA222_PWR_SLEEP_MASK);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ msleep(3); /* 3 ms powerup time maximum */
+ return result;
+}
+
+/**
+ * @brief resume the device in the proper power state given the configuration
+ * chosen.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma222_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ struct bma222_config *resume_config =
+ &((struct bma222_private_data *)pdata->private_data)->resume;
+
+ /* Soft reset */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA222_SOFTRESET_REG, BMA222_SOFTRESET_MASK);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(10);
+
+ result = bma222_set_odr(mlsl_handle, pdata, resume_config,
+ TRUE, resume_config->odr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = bma222_set_fsr(mlsl_handle, pdata, resume_config,
+ TRUE, resume_config->fsr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * @brief read the sensor data from the device.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a buffer to store the data read.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma222_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result = INV_SUCCESS;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ BMA222_STATUS_REG, 1, data);
+ if (data[0] & BMA222_STATUS_RDY_MASK) {
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ slave->read_reg, slave->read_len, data);
+ return result;
+ } else
+ return INV_ERROR_ACCEL_DATA_NOT_READY;
+}
+
+static struct ext_slave_descr bma222_descr = {
+ .init = bma222_init,
+ .exit = bma222_exit,
+ .suspend = bma222_suspend,
+ .resume = bma222_resume,
+ .read = bma222_read,
+ .config = bma222_config,
+ .get_config = bma222_get_config,
+ .name = "bma222",
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ACCEL_ID_BMA222,
+ .read_reg = 0x02,
+ .read_len = 6,
+ .endian = EXT_SLAVE_LITTLE_ENDIAN,
+ .range = {2, 0},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *bma222_get_slave_descr(void)
+{
+ return &bma222_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct bma222_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int bma222_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct bma222_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ bma222_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int bma222_mod_remove(struct i2c_client *client)
+{
+ struct bma222_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ bma222_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id bma222_mod_id[] = {
+ { "bma222", ACCEL_ID_BMA222 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, bma222_mod_id);
+
+static struct i2c_driver bma222_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = bma222_mod_probe,
+ .remove = bma222_mod_remove,
+ .id_table = bma222_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bma222_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init bma222_mod_init(void)
+{
+ int res = i2c_add_driver(&bma222_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "bma222_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit bma222_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&bma222_mod_driver);
+}
+
+module_init(bma222_mod_init);
+module_exit(bma222_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate BMA222 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("bma222_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/accel/bma250.c b/drivers/misc/inv_mpu/accel/bma250.c
new file mode 100644
index 0000000..0f42ae8
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/bma250.c
@@ -0,0 +1,783 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup ACCELDL
+ * @brief Provides the interface to setup and handle an accelerometer.
+ *
+ * @{
+ * @file bma250.c
+ * @brief Accelerometer setup and handling methods for Bosch BMA250.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+
+/* -------------------------------------------------------------------------- */
+
+/* registers */
+#define BMA250_STATUS_REG (0x0A)
+#define BMA250_FSR_REG (0x0F)
+#define BMA250_ODR_REG (0x10)
+#define BMA250_PWR_REG (0x11)
+#define BMA250_SOFTRESET_REG (0x14)
+#define BMA250_INT_TYPE_REG (0x17)
+#define BMA250_INT_DST_REG (0x1A)
+#define BMA250_INT_SRC_REG (0x1E)
+
+/* masks */
+#define BMA250_STATUS_RDY_MASK (0x80)
+#define BMA250_FSR_MASK (0x0F)
+#define BMA250_ODR_MASK (0x1F)
+#define BMA250_PWR_SLEEP_MASK (0x80)
+#define BMA250_PWR_AWAKE_MASK (0x00)
+#define BMA250_SOFTRESET_MASK (0xB6)
+#define BMA250_INT_TYPE_MASK (0x10)
+#define BMA250_INT_DST_1_MASK (0x01)
+#define BMA250_INT_DST_2_MASK (0x80)
+#define BMA250_INT_SRC_MASK (0x00)
+
+/* -------------------------------------------------------------------------- */
+
+struct bma250_config {
+ unsigned int odr; /** < output data rate in mHz */
+ unsigned int fsr; /** < full scale range mg */
+ unsigned char irq_type;
+};
+
+struct bma250_private_data {
+ struct bma250_config suspend; /** < suspend configuration */
+ struct bma250_config resume; /** < resume configuration */
+};
+
+/* -------------------------------------------------------------------------- */
+/**
+ * @brief Sets the IRQ to fire when one of the IRQ events occur.
+ * Threshold and duration will not be used unless the type is MOT or
+ * NMOT.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * configuration to apply to, suspend or resume
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param irq_type
+ * the type of IRQ. Valid values are
+ * - MPU_SLAVE_IRQ_TYPE_NONE
+ * - MPU_SLAVE_IRQ_TYPE_MOTION
+ * - MPU_SLAVE_IRQ_TYPE_DATA_READY
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma250_set_irq(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct bma250_config *config,
+ int apply, long irq_type)
+{
+ int result = INV_SUCCESS;
+ unsigned char irqtype_reg;
+ unsigned char irqdst_reg;
+ unsigned char irqsrc_reg;
+
+ switch (irq_type) {
+ case MPU_SLAVE_IRQ_TYPE_DATA_READY:
+ /* data ready int. */
+ irqtype_reg = BMA250_INT_TYPE_MASK;
+ /* routed to interrupt pin 1 */
+ irqdst_reg = BMA250_INT_DST_1_MASK;
+ /* from filtered data */
+ irqsrc_reg = BMA250_INT_SRC_MASK;
+ break;
+ /* unfinished
+ case MPU_SLAVE_IRQ_TYPE_MOTION:
+ reg1 = 0x00;
+ reg2 = config->mot_int1_cfg;
+ reg3 = ;
+ break;
+ */
+ case MPU_SLAVE_IRQ_TYPE_NONE:
+ irqtype_reg = 0x00;
+ irqdst_reg = 0x00;
+ irqsrc_reg = 0x00;
+ break;
+ default:
+ return INV_ERROR_INVALID_PARAMETER;
+ break;
+ }
+
+ config->irq_type = (unsigned char)irq_type;
+
+ if (apply) {
+ /* select the type of interrupt to use */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA250_INT_TYPE_REG, irqtype_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* select to which interrupt pin to route it to */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA250_INT_DST_REG, irqdst_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* select whether the interrupt works off filtered or
+ unfiltered data */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA250_INT_SRC_REG, irqsrc_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ return result;
+}
+
+/**
+ * @brief Set the output data rate for the particular configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * Config to modify with new ODR.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param odr
+ * Output data rate in units of 1/1000Hz (mHz).
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma250_set_odr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct bma250_config *config,
+ int apply,
+ long odr)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg_odr;
+
+ if (odr >= 1000000) {
+ reg_odr = 0x0F;
+ config->odr = 1000000;
+ } else if (odr >= 500000) {
+ reg_odr = 0x0E;
+ config->odr = 500000;
+ } else if (odr >= 250000) {
+ reg_odr = 0x0D;
+ config->odr = 250000;
+ } else if (odr >= 125000) {
+ reg_odr = 0x0C;
+ config->odr = 125000;
+ } else if (odr >= 62500) {
+ reg_odr = 0x0B;
+ config->odr = 62500;
+ } else if (odr >= 31250) {
+ reg_odr = 0x0A;
+ config->odr = 31250;
+ } else if (odr >= 15630) {
+ reg_odr = 0x09;
+ config->odr = 15630;
+ } else {
+ reg_odr = 0x08;
+ config->odr = 7810;
+ }
+
+ if (apply) {
+ MPL_LOGV("ODR: %d\n", config->odr);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA250_ODR_REG, reg_odr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * @brief Set the full scale range of the accels
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * pointer to configuration.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param fsr
+ * requested full scale range.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma250_set_fsr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct bma250_config *config,
+ int apply,
+ long fsr)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg_fsr_mask;
+
+ if (fsr <= 2000) {
+ reg_fsr_mask = 0x03;
+ config->fsr = 2000;
+ } else if (fsr <= 4000) {
+ reg_fsr_mask = 0x05;
+ config->fsr = 4000;
+ } else if (fsr <= 8000) {
+ reg_fsr_mask = 0x08;
+ config->fsr = 8000;
+ } else { /* 8001 -> oo */
+ reg_fsr_mask = 0x0C;
+ config->fsr = 16000;
+ }
+
+ if (apply) {
+ MPL_LOGV("FSR: %d\n", config->fsr);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA250_FSR_REG, reg_fsr_mask);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ return result;
+}
+
+/**
+ * @brief one-time device driver initialization function.
+ * If the driver is built as a kernel module, this function will be
+ * called when the module is loaded in the kernel.
+ * If the driver is built-in in the kernel, this function will be
+ * called at boot time.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma250_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ long range;
+
+ struct bma250_private_data *private_data;
+ private_data = kzalloc(sizeof(struct bma250_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ pdata->private_data = private_data;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA250_SOFTRESET_REG, BMA250_SOFTRESET_MASK);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(1);
+
+ result = bma250_set_odr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = bma250_set_odr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 200000);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ range = range_fixedpoint_to_long_mg(slave->range);
+ result = bma250_set_fsr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, range);
+ result = bma250_set_fsr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, range);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = bma250_set_irq(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = bma250_set_irq(mlsl_handle, pdata, &private_data->resume,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA250_PWR_REG, BMA250_PWR_SLEEP_MASK);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * @brief one-time device driver exit function.
+ * If the driver is built as a kernel module, this function will be
+ * called when the module is removed from the kernel.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma250_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief device configuration facility.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a pointer to the configuration data structure.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma250_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct bma250_private_data *private_data =
+ (struct bma250_private_data *)(pdata->private_data);
+
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ return bma250_set_odr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ return bma250_set_odr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ return bma250_set_fsr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ return bma250_set_fsr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ return bma250_set_irq(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ return bma250_set_irq(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief facility to retrieve the device configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a pointer to store the returned configuration data structure.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma250_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct bma250_private_data *private_data =
+ (struct bma250_private_data *)(pdata->private_data);
+
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.odr;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.odr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.irq_type;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.irq_type;
+ break;
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief suspends the device to put it in its lowest power mode.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma250_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ struct bma250_config *suspend_config =
+ &((struct bma250_private_data *)pdata->private_data)->suspend;
+
+ result = bma250_set_odr(mlsl_handle, pdata, suspend_config,
+ TRUE, suspend_config->odr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = bma250_set_fsr(mlsl_handle, pdata, suspend_config,
+ TRUE, suspend_config->fsr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = bma250_set_irq(mlsl_handle, pdata, suspend_config,
+ TRUE, suspend_config->irq_type);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA250_PWR_REG, BMA250_PWR_SLEEP_MASK);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ msleep(3); /* 3 ms powerup time maximum */
+ return result;
+}
+
+/**
+ * @brief resume the device in the proper power state given the configuration
+ * chosen.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma250_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ struct bma250_config *resume_config =
+ &((struct bma250_private_data *)pdata->private_data)->resume;
+
+ result = bma250_set_odr(mlsl_handle, pdata, resume_config,
+ TRUE, resume_config->odr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = bma250_set_fsr(mlsl_handle, pdata, resume_config,
+ TRUE, resume_config->fsr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = bma250_set_irq(mlsl_handle, pdata, resume_config,
+ TRUE, resume_config->irq_type);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ BMA250_PWR_REG, BMA250_PWR_AWAKE_MASK);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * @brief read the sensor data from the device.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a buffer to store the data read.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int bma250_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result = INV_SUCCESS;
+ if (1) { /* KLP - workaroud for small data ready window */
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ slave->read_reg, slave->read_len, data);
+ return result;
+ } else
+ return INV_ERROR_ACCEL_DATA_NOT_READY;
+}
+
+static struct ext_slave_descr bma250_descr = {
+ .init = bma250_init,
+ .exit = bma250_exit,
+ .suspend = bma250_suspend,
+ .resume = bma250_resume,
+ .read = bma250_read,
+ .config = bma250_config,
+ .get_config = bma250_get_config,
+ .name = "bma250",
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ACCEL_ID_BMA250,
+ .read_reg = 0x02,
+ .read_len = 6,
+ .endian = EXT_SLAVE_LITTLE_ENDIAN,
+ .range = {2, 0},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *bma250_get_slave_descr(void)
+{
+ return &bma250_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+
+/* Platform data for the MPU */
+struct bma250_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int bma250_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct bma250_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ bma250_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int bma250_mod_remove(struct i2c_client *client)
+{
+ struct bma250_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ bma250_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id bma250_mod_id[] = {
+ { "bma250", ACCEL_ID_BMA250 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, bma250_mod_id);
+
+static struct i2c_driver bma250_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = bma250_mod_probe,
+ .remove = bma250_mod_remove,
+ .id_table = bma250_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bma250_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init bma250_mod_init(void)
+{
+ int res = i2c_add_driver(&bma250_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "bma250_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit bma250_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&bma250_mod_driver);
+}
+
+module_init(bma250_mod_init);
+module_exit(bma250_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate BMA250 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("bma250_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/accel/cma3000.c b/drivers/misc/inv_mpu/accel/cma3000.c
new file mode 100644
index 0000000..17bdee4
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/cma3000.c
@@ -0,0 +1,222 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/*
+ * @addtogroup ACCELDL
+ * @brief Accelerometer setup and handling methods for VTI CMA3000.
+ *
+ * @{
+ * @file cma3000.c
+ * @brief Accelerometer setup and handling methods for VTI CMA3000
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+/* -------------------------------------------------------------------------- */
+
+static int cma3000_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ /* RAM reset */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address, 0x1d, 0xcd);
+ return result;
+}
+
+static int cma3000_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+
+ return INV_SUCCESS;
+}
+
+static int cma3000_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ slave->reg, slave->len, data);
+ return result;
+}
+
+static struct ext_slave_descr cma3000_descr = {
+ .init = NULL,
+ .exit = NULL,
+ .suspend = cma3000_suspend,
+ .resume = cma3000_resume,
+ .read = cma3000_read,
+ .config = NULL,
+ .get_config = NULL,
+ .name = "cma3000",
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ID_INVALID,
+ .read_reg = 0x06,
+ .read_len = 6,
+ .endian = EXT_SLAVE_LITTLE_ENDIAN,
+ .range = {2, 0},
+ .trigger = NULL,
+
+};
+
+static
+struct ext_slave_descr *cma3000_get_slave_descr(void)
+{
+ return &cma3000_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+
+struct cma3000_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int cma3000_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct cma3000_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ cma3000_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int cma3000_mod_remove(struct i2c_client *client)
+{
+ struct cma3000_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ cma3000_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id cma3000_mod_id[] = {
+ { "cma3000", ACCEL_ID_CMA3000 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cma3000_mod_id);
+
+static struct i2c_driver cma3000_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = cma3000_mod_probe,
+ .remove = cma3000_mod_remove,
+ .id_table = cma3000_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "cma3000_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init cma3000_mod_init(void)
+{
+ int res = i2c_add_driver(&cma3000_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "cma3000_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit cma3000_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&cma3000_mod_driver);
+}
+
+module_init(cma3000_mod_init);
+module_exit(cma3000_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate CMA3000 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("cma3000_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/accel/kxsd9.c b/drivers/misc/inv_mpu/accel/kxsd9.c
new file mode 100644
index 0000000..7d19df5
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/kxsd9.c
@@ -0,0 +1,264 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup ACCELDL
+ * @brief Accelerometer setup and handling methods for Kionix KXSD9.
+ *
+ * @{
+ * @file kxsd9.c
+ * @brief Accelerometer setup and handling methods for Kionix KXSD9.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+/* -------------------------------------------------------------------------- */
+
+static int kxsd9_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ /* CTRL_REGB: low-power standby mode */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address, 0x0d, 0x0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+/* full scale setting - register and mask */
+#define ACCEL_KIONIX_CTRL_REG (0x0C)
+#define ACCEL_KIONIX_CTRL_MASK (0x3)
+
+static int kxsd9_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg;
+
+ /* Full Scale */
+ reg = 0x0;
+ reg &= ~ACCEL_KIONIX_CTRL_MASK;
+ reg |= 0x00;
+ if (slave->range.mantissa == 4) { /* 4g scale = 4.9951 */
+ reg |= 0x2;
+ slave->range.fraction = 9951;
+ } else if (slave->range.mantissa == 7) { /* 6g scale = 7.5018 */
+ reg |= 0x1;
+ slave->range.fraction = 5018;
+ } else if (slave->range.mantissa == 9) { /* 8g scale = 9.9902 */
+ reg |= 0x0;
+ slave->range.fraction = 9902;
+ } else {
+ slave->range.mantissa = 2; /* 2g scale = 2.5006 */
+ slave->range.fraction = 5006;
+ reg |= 0x3;
+ }
+ reg |= 0xC0; /* 100Hz LPF */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_KIONIX_CTRL_REG, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* normal operation */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address, 0x0d, 0x40);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return INV_SUCCESS;
+}
+
+static int kxsd9_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ slave->read_reg, slave->read_len, data);
+ return result;
+}
+
+static struct ext_slave_descr kxsd9_descr = {
+ .init = NULL,
+ .exit = NULL,
+ .suspend = kxsd9_suspend,
+ .resume = kxsd9_resume,
+ .read = kxsd9_read,
+ .config = NULL,
+ .get_config = NULL,
+ .name = "kxsd9",
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ACCEL_ID_KXSD9,
+ .read_reg = 0x00,
+ .read_len = 6,
+ .endian = EXT_SLAVE_BIG_ENDIAN,
+ .range = {2, 5006},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *kxsd9_get_slave_descr(void)
+{
+ return &kxsd9_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct kxsd9_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int kxsd9_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct kxsd9_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ kxsd9_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int kxsd9_mod_remove(struct i2c_client *client)
+{
+ struct kxsd9_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ kxsd9_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id kxsd9_mod_id[] = {
+ { "kxsd9", ACCEL_ID_KXSD9 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, kxsd9_mod_id);
+
+static struct i2c_driver kxsd9_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = kxsd9_mod_probe,
+ .remove = kxsd9_mod_remove,
+ .id_table = kxsd9_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "kxsd9_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init kxsd9_mod_init(void)
+{
+ int res = i2c_add_driver(&kxsd9_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "kxsd9_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit kxsd9_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&kxsd9_mod_driver);
+}
+
+module_init(kxsd9_mod_init);
+module_exit(kxsd9_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate KXSD9 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("kxsd9_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/accel/kxtf9.c b/drivers/misc/inv_mpu/accel/kxtf9.c
new file mode 100644
index 0000000..4a6b4b0
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/kxtf9.c
@@ -0,0 +1,841 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup ACCELDL
+ * @brief Accelerometer setup and handling methods for Kionix KXTF9.
+ *
+ * @{
+ * @file kxtf9.c
+ * @brief Accelerometer setup and handling methods for Kionix KXTF9.
+*/
+
+/* -------------------------------------------------------------------------- */
+
+#undef MPL_LOG_NDEBUG
+#define MPL_LOG_NDEBUG 1
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+#define KXTF9_XOUT_HPF_L (0x00) /* 0000 0000 */
+#define KXTF9_XOUT_HPF_H (0x01) /* 0000 0001 */
+#define KXTF9_YOUT_HPF_L (0x02) /* 0000 0010 */
+#define KXTF9_YOUT_HPF_H (0x03) /* 0000 0011 */
+#define KXTF9_ZOUT_HPF_L (0x04) /* 0001 0100 */
+#define KXTF9_ZOUT_HPF_H (0x05) /* 0001 0101 */
+#define KXTF9_XOUT_L (0x06) /* 0000 0110 */
+#define KXTF9_XOUT_H (0x07) /* 0000 0111 */
+#define KXTF9_YOUT_L (0x08) /* 0000 1000 */
+#define KXTF9_YOUT_H (0x09) /* 0000 1001 */
+#define KXTF9_ZOUT_L (0x0A) /* 0001 1010 */
+#define KXTF9_ZOUT_H (0x0B) /* 0001 1011 */
+#define KXTF9_ST_RESP (0x0C) /* 0000 1100 */
+#define KXTF9_WHO_AM_I (0x0F) /* 0000 1111 */
+#define KXTF9_TILT_POS_CUR (0x10) /* 0001 0000 */
+#define KXTF9_TILT_POS_PRE (0x11) /* 0001 0001 */
+#define KXTF9_INT_SRC_REG1 (0x15) /* 0001 0101 */
+#define KXTF9_INT_SRC_REG2 (0x16) /* 0001 0110 */
+#define KXTF9_STATUS_REG (0x18) /* 0001 1000 */
+#define KXTF9_INT_REL (0x1A) /* 0001 1010 */
+#define KXTF9_CTRL_REG1 (0x1B) /* 0001 1011 */
+#define KXTF9_CTRL_REG2 (0x1C) /* 0001 1100 */
+#define KXTF9_CTRL_REG3 (0x1D) /* 0001 1101 */
+#define KXTF9_INT_CTRL_REG1 (0x1E) /* 0001 1110 */
+#define KXTF9_INT_CTRL_REG2 (0x1F) /* 0001 1111 */
+#define KXTF9_INT_CTRL_REG3 (0x20) /* 0010 0000 */
+#define KXTF9_DATA_CTRL_REG (0x21) /* 0010 0001 */
+#define KXTF9_TILT_TIMER (0x28) /* 0010 1000 */
+#define KXTF9_WUF_TIMER (0x29) /* 0010 1001 */
+#define KXTF9_TDT_TIMER (0x2B) /* 0010 1011 */
+#define KXTF9_TDT_H_THRESH (0x2C) /* 0010 1100 */
+#define KXTF9_TDT_L_THRESH (0x2D) /* 0010 1101 */
+#define KXTF9_TDT_TAP_TIMER (0x2E) /* 0010 1110 */
+#define KXTF9_TDT_TOTAL_TIMER (0x2F) /* 0010 1111 */
+#define KXTF9_TDT_LATENCY_TIMER (0x30) /* 0011 0000 */
+#define KXTF9_TDT_WINDOW_TIMER (0x31) /* 0011 0001 */
+#define KXTF9_WUF_THRESH (0x5A) /* 0101 1010 */
+#define KXTF9_TILT_ANGLE (0x5C) /* 0101 1100 */
+#define KXTF9_HYST_SET (0x5F) /* 0101 1111 */
+
+#define KXTF9_MAX_DUR (0xFF)
+#define KXTF9_MAX_THS (0xFF)
+#define KXTF9_THS_COUNTS_P_G (32)
+
+/* -------------------------------------------------------------------------- */
+
+struct kxtf9_config {
+ unsigned int odr; /* Output data rate mHz */
+ unsigned int fsr; /* full scale range mg */
+ unsigned int ths; /* Motion no-motion thseshold mg */
+ unsigned int dur; /* Motion no-motion duration ms */
+ unsigned int irq_type;
+ unsigned char reg_ths;
+ unsigned char reg_dur;
+ unsigned char reg_odr;
+ unsigned char reg_int_cfg1;
+ unsigned char reg_int_cfg2;
+ unsigned char ctrl_reg1;
+};
+
+struct kxtf9_private_data {
+ struct kxtf9_config suspend;
+ struct kxtf9_config resume;
+};
+
+static int kxtf9_set_ths(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct kxtf9_config *config, int apply, long ths)
+{
+ int result = INV_SUCCESS;
+ if ((ths * KXTF9_THS_COUNTS_P_G / 1000) > KXTF9_MAX_THS)
+ ths = (KXTF9_MAX_THS * 1000) / KXTF9_THS_COUNTS_P_G;
+
+ if (ths < 0)
+ ths = 0;
+
+ config->ths = ths;
+ config->reg_ths = (unsigned char)
+ ((long)(ths * KXTF9_THS_COUNTS_P_G) / 1000);
+ MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_WUF_THRESH,
+ config->reg_ths);
+ return result;
+}
+
+static int kxtf9_set_dur(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct kxtf9_config *config, int apply, long dur)
+{
+ int result = INV_SUCCESS;
+ long reg_dur = (dur * config->odr) / 1000000;
+ config->dur = dur;
+
+ if (reg_dur > KXTF9_MAX_DUR)
+ reg_dur = KXTF9_MAX_DUR;
+
+ config->reg_dur = (unsigned char)reg_dur;
+ MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_WUF_TIMER,
+ (unsigned char)reg_dur);
+ return result;
+}
+
+/**
+ * Sets the IRQ to fire when one of the IRQ events occur. Threshold and
+ * duration will not be used uless the type is MOT or NMOT.
+ *
+ * @param config configuration to apply to, suspend or resume
+ * @param irq_type The type of IRQ. Valid values are
+ * - MPU_SLAVE_IRQ_TYPE_NONE
+ * - MPU_SLAVE_IRQ_TYPE_MOTION
+ * - MPU_SLAVE_IRQ_TYPE_DATA_READY
+ */
+static int kxtf9_set_irq(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct kxtf9_config *config, int apply, long irq_type)
+{
+ int result = INV_SUCCESS;
+ struct kxtf9_private_data *private_data = pdata->private_data;
+
+ config->irq_type = (unsigned char)irq_type;
+ config->ctrl_reg1 &= ~0x22;
+ if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
+ config->ctrl_reg1 |= 0x20;
+ config->reg_int_cfg1 = 0x38;
+ config->reg_int_cfg2 = 0x00;
+ } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
+ config->ctrl_reg1 |= 0x02;
+ if ((unsigned long)config ==
+ (unsigned long)&private_data->suspend)
+ config->reg_int_cfg1 = 0x34;
+ else
+ config->reg_int_cfg1 = 0x24;
+ config->reg_int_cfg2 = 0xE0;
+ } else {
+ config->reg_int_cfg1 = 0x00;
+ config->reg_int_cfg2 = 0x00;
+ }
+
+ if (apply) {
+ /* Must clear bit 7 before writing new configuration */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG1, 0x40);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_INT_CTRL_REG1,
+ config->reg_int_cfg1);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_INT_CTRL_REG2,
+ config->reg_int_cfg2);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG1,
+ config->ctrl_reg1);
+ }
+ MPL_LOGV("CTRL_REG1: %lx, INT_CFG1: %lx, INT_CFG2: %lx\n",
+ (unsigned long)config->ctrl_reg1,
+ (unsigned long)config->reg_int_cfg1,
+ (unsigned long)config->reg_int_cfg2);
+
+ return result;
+}
+
+/**
+ * Set the Output data rate for the particular configuration
+ *
+ * @param config Config to modify with new ODR
+ * @param odr Output data rate in units of 1/1000Hz
+ */
+static int kxtf9_set_odr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct kxtf9_config *config, int apply, long odr)
+{
+ unsigned char bits;
+ int result = INV_SUCCESS;
+
+ /* Data sheet says there is 12.5 hz, but that seems to produce a single
+ * correct data value, thus we remove it from the table */
+ if (odr > 400000) {
+ config->odr = 800000;
+ bits = 0x06;
+ } else if (odr > 200000) {
+ config->odr = 400000;
+ bits = 0x05;
+ } else if (odr > 100000) {
+ config->odr = 200000;
+ bits = 0x04;
+ } else if (odr > 50000) {
+ config->odr = 100000;
+ bits = 0x03;
+ } else if (odr > 25000) {
+ config->odr = 50000;
+ bits = 0x02;
+ } else if (odr != 0) {
+ config->odr = 25000;
+ bits = 0x01;
+ } else {
+ config->odr = 0;
+ bits = 0;
+ }
+
+ if (odr != 0)
+ config->ctrl_reg1 |= 0x80;
+ else
+ config->ctrl_reg1 &= ~0x80;
+
+ config->reg_odr = bits;
+ kxtf9_set_dur(mlsl_handle, pdata, config, apply, config->dur);
+ MPL_LOGV("ODR: %d, 0x%02x\n", config->odr, (int)config->ctrl_reg1);
+ if (apply) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_DATA_CTRL_REG,
+ config->reg_odr);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG1, 0x40);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG1,
+ config->ctrl_reg1);
+ }
+ return result;
+}
+
+/**
+ * Set the full scale range of the accels
+ *
+ * @param config pointer to configuration
+ * @param fsr requested full scale range
+ */
+static int kxtf9_set_fsr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct kxtf9_config *config, int apply, long fsr)
+{
+ int result = INV_SUCCESS;
+
+ config->ctrl_reg1 = (config->ctrl_reg1 & 0xE7);
+ if (fsr <= 2000) {
+ config->fsr = 2000;
+ config->ctrl_reg1 |= 0x00;
+ } else if (fsr <= 4000) {
+ config->fsr = 4000;
+ config->ctrl_reg1 |= 0x08;
+ } else {
+ config->fsr = 8000;
+ config->ctrl_reg1 |= 0x10;
+ }
+
+ MPL_LOGV("FSR: %d\n", config->fsr);
+ if (apply) {
+ /* Must clear bit 7 before writing new configuration */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG1, 0x40);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG1,
+ config->ctrl_reg1);
+ }
+ return result;
+}
+
+static int kxtf9_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ unsigned char data;
+ struct kxtf9_private_data *private_data = pdata->private_data;
+
+ /* Wake up */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG1, 0x40);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* INT_CTRL_REG1: */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_INT_CTRL_REG1,
+ private_data->suspend.reg_int_cfg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* WUF_THRESH: */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_WUF_THRESH,
+ private_data->suspend.reg_ths);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* DATA_CTRL_REG */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_DATA_CTRL_REG,
+ private_data->suspend.reg_odr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* WUF_TIMER */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_WUF_TIMER,
+ private_data->suspend.reg_dur);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Normal operation */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG1,
+ private_data->suspend.ctrl_reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ KXTF9_INT_REL, 1, &data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+/* full scale setting - register and mask */
+#define ACCEL_KIONIX_CTRL_REG (0x1b)
+#define ACCEL_KIONIX_CTRL_MASK (0x18)
+
+static int kxtf9_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ unsigned char data;
+ struct kxtf9_private_data *private_data = pdata->private_data;
+
+ /* Wake up */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG1, 0x40);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* INT_CTRL_REG1: */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_INT_CTRL_REG1,
+ private_data->resume.reg_int_cfg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* WUF_THRESH: */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_WUF_THRESH,
+ private_data->resume.reg_ths);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* DATA_CTRL_REG */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_DATA_CTRL_REG,
+ private_data->resume.reg_odr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* WUF_TIMER */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_WUF_TIMER,
+ private_data->resume.reg_dur);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Normal operation */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG1,
+ private_data->resume.ctrl_reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ KXTF9_INT_REL, 1, &data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return INV_SUCCESS;
+}
+
+static int kxtf9_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+
+ struct kxtf9_private_data *private_data;
+ int result = INV_SUCCESS;
+
+ private_data = (struct kxtf9_private_data *)
+ kzalloc(sizeof(struct kxtf9_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ /* RAM reset */
+ /* Fastest Reset */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG1, 0x40);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Fastest Reset */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_DATA_CTRL_REG, 0x36);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Reset */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ KXTF9_CTRL_REG3, 0xcd);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(2);
+
+ pdata->private_data = private_data;
+
+ private_data->resume.ctrl_reg1 = 0xC0;
+ private_data->suspend.ctrl_reg1 = 0x40;
+
+ result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 1000);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = kxtf9_set_dur(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 2540);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 50000);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = kxtf9_set_odr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 200000);
+
+ result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 2000);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = kxtf9_set_fsr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 2000);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 80);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = kxtf9_set_ths(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 40);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = kxtf9_set_irq(mlsl_handle, pdata, &private_data->resume,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+static int kxtf9_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+static int kxtf9_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct kxtf9_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ return kxtf9_set_odr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ return kxtf9_set_odr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ return kxtf9_set_fsr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ return kxtf9_set_fsr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ return kxtf9_set_ths(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ return kxtf9_set_ths(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ return kxtf9_set_dur(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ return kxtf9_set_dur(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ return kxtf9_set_irq(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ return kxtf9_set_irq(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static int kxtf9_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct kxtf9_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.odr;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.odr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.ths;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.ths;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.dur;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.dur;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.irq_type;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.irq_type;
+ break;
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static int kxtf9_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result;
+ unsigned char reg;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ KXTF9_INT_SRC_REG2, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (!(reg & 0x10))
+ return INV_ERROR_ACCEL_DATA_NOT_READY;
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ slave->read_reg, slave->read_len, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+static struct ext_slave_descr kxtf9_descr = {
+ .init = kxtf9_init,
+ .exit = kxtf9_exit,
+ .suspend = kxtf9_suspend,
+ .resume = kxtf9_resume,
+ .read = kxtf9_read,
+ .config = kxtf9_config,
+ .get_config = kxtf9_get_config,
+ .name = "kxtf9",
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ACCEL_ID_KXTF9,
+ .read_reg = 0x06,
+ .read_len = 6,
+ .endian = EXT_SLAVE_LITTLE_ENDIAN,
+ .range = {2, 0},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *kxtf9_get_slave_descr(void)
+{
+ return &kxtf9_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct kxtf9_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int kxtf9_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct kxtf9_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ kxtf9_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int kxtf9_mod_remove(struct i2c_client *client)
+{
+ struct kxtf9_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ kxtf9_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id kxtf9_mod_id[] = {
+ { "kxtf9", ACCEL_ID_KXTF9 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, kxtf9_mod_id);
+
+static struct i2c_driver kxtf9_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = kxtf9_mod_probe,
+ .remove = kxtf9_mod_remove,
+ .id_table = kxtf9_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "kxtf9_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init kxtf9_mod_init(void)
+{
+ int res = i2c_add_driver(&kxtf9_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "kxtf9_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit kxtf9_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&kxtf9_mod_driver);
+}
+
+module_init(kxtf9_mod_init);
+module_exit(kxtf9_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate KXTF9 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("kxtf9_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/accel/lis331.c b/drivers/misc/inv_mpu/accel/lis331.c
new file mode 100644
index 0000000..adf33e5
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/lis331.c
@@ -0,0 +1,740 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup ACCELDL
+ * @brief Provides the interface to setup and handle an accelerometer.
+ *
+ * @{
+ * @file lis331.c
+ * @brief Accelerometer setup and handling methods for ST LIS331DLH.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#undef MPL_LOG_NDEBUG
+#define MPL_LOG_NDEBUG 1
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+/* full scale setting - register & mask */
+#define LIS331_CTRL_REG1 (0x20)
+#define LIS331_CTRL_REG2 (0x21)
+#define LIS331_CTRL_REG3 (0x22)
+#define LIS331_CTRL_REG4 (0x23)
+#define LIS331_CTRL_REG5 (0x24)
+#define LIS331_HP_FILTER_RESET (0x25)
+#define LIS331_REFERENCE (0x26)
+#define LIS331_STATUS_REG (0x27)
+#define LIS331_OUT_X_L (0x28)
+#define LIS331_OUT_X_H (0x29)
+#define LIS331_OUT_Y_L (0x2a)
+#define LIS331_OUT_Y_H (0x2b)
+#define LIS331_OUT_Z_L (0x2b)
+#define LIS331_OUT_Z_H (0x2d)
+
+#define LIS331_INT1_CFG (0x30)
+#define LIS331_INT1_SRC (0x31)
+#define LIS331_INT1_THS (0x32)
+#define LIS331_INT1_DURATION (0x33)
+
+#define LIS331_INT2_CFG (0x34)
+#define LIS331_INT2_SRC (0x35)
+#define LIS331_INT2_THS (0x36)
+#define LIS331_INT2_DURATION (0x37)
+
+#define LIS331_CTRL_MASK (0x30)
+#define LIS331_SLEEP_MASK (0x20)
+
+#define LIS331_MAX_DUR (0x7F)
+
+/* -------------------------------------------------------------------------- */
+
+struct lis331dlh_config {
+ unsigned int odr;
+ unsigned int fsr; /* full scale range mg */
+ unsigned int ths; /* Motion no-motion thseshold mg */
+ unsigned int dur; /* Motion no-motion duration ms */
+ unsigned char reg_ths;
+ unsigned char reg_dur;
+ unsigned char ctrl_reg1;
+ unsigned char irq_type;
+ unsigned char mot_int1_cfg;
+};
+
+struct lis331dlh_private_data {
+ struct lis331dlh_config suspend;
+ struct lis331dlh_config resume;
+};
+
+/* -------------------------------------------------------------------------- */
+static int lis331dlh_set_ths(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lis331dlh_config *config,
+ int apply, long ths)
+{
+ int result = INV_SUCCESS;
+ if ((unsigned int)ths >= config->fsr)
+ ths = (long)config->fsr - 1;
+
+ if (ths < 0)
+ ths = 0;
+
+ config->ths = ths;
+ config->reg_ths = (unsigned char)(long)((ths * 128L) / (config->fsr));
+ MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_THS,
+ config->reg_ths);
+ return result;
+}
+
+static int lis331dlh_set_dur(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lis331dlh_config *config,
+ int apply, long dur)
+{
+ int result = INV_SUCCESS;
+ long reg_dur = (dur * config->odr) / 1000000L;
+ config->dur = dur;
+
+ if (reg_dur > LIS331_MAX_DUR)
+ reg_dur = LIS331_MAX_DUR;
+
+ config->reg_dur = (unsigned char)reg_dur;
+ MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_DURATION,
+ (unsigned char)reg_dur);
+ return result;
+}
+
+/**
+ * Sets the IRQ to fire when one of the IRQ events occur. Threshold and
+ * duration will not be used uless the type is MOT or NMOT.
+ *
+ * @param config configuration to apply to, suspend or resume
+ * @param irq_type The type of IRQ. Valid values are
+ * - MPU_SLAVE_IRQ_TYPE_NONE
+ * - MPU_SLAVE_IRQ_TYPE_MOTION
+ * - MPU_SLAVE_IRQ_TYPE_DATA_READY
+ */
+static int lis331dlh_set_irq(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lis331dlh_config *config,
+ int apply, long irq_type)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg1;
+ unsigned char reg2;
+
+ config->irq_type = (unsigned char)irq_type;
+ if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
+ reg1 = 0x02;
+ reg2 = 0x00;
+ } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
+ reg1 = 0x00;
+ reg2 = config->mot_int1_cfg;
+ } else {
+ reg1 = 0x00;
+ reg2 = 0x00;
+ }
+
+ if (apply) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG3, reg1);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_CFG, reg2);
+ }
+
+ return result;
+}
+
+/**
+ * Set the Output data rate for the particular configuration
+ *
+ * @param config Config to modify with new ODR
+ * @param odr Output data rate in units of 1/1000Hz
+ */
+static int lis331dlh_set_odr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lis331dlh_config *config,
+ int apply, long odr)
+{
+ unsigned char bits;
+ int result = INV_SUCCESS;
+
+ if (odr > 400000) {
+ config->odr = 1000000;
+ bits = 0x38;
+ } else if (odr > 100000) {
+ config->odr = 400000;
+ bits = 0x30;
+ } else if (odr > 50000) {
+ config->odr = 100000;
+ bits = 0x28;
+ } else if (odr > 10000) {
+ config->odr = 50000;
+ bits = 0x20;
+ } else if (odr > 5000) {
+ config->odr = 10000;
+ bits = 0xC0;
+ } else if (odr > 2000) {
+ config->odr = 5000;
+ bits = 0xB0;
+ } else if (odr > 1000) {
+ config->odr = 2000;
+ bits = 0x80;
+ } else if (odr > 500) {
+ config->odr = 1000;
+ bits = 0x60;
+ } else if (odr > 0) {
+ config->odr = 500;
+ bits = 0x40;
+ } else {
+ config->odr = 0;
+ bits = 0;
+ }
+
+ config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0x7);
+ lis331dlh_set_dur(mlsl_handle, pdata, config, apply, config->dur);
+ MPL_LOGV("ODR: %d, 0x%02x\n", config->odr, (int)config->ctrl_reg1);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG1,
+ config->ctrl_reg1);
+ return result;
+}
+
+/**
+ * Set the full scale range of the accels
+ *
+ * @param config pointer to configuration
+ * @param fsr requested full scale range
+ */
+static int lis331dlh_set_fsr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lis331dlh_config *config,
+ int apply, long fsr)
+{
+ unsigned char reg1 = 0x40;
+ int result = INV_SUCCESS;
+
+ if (fsr <= 2048) {
+ config->fsr = 2048;
+ } else if (fsr <= 4096) {
+ reg1 |= 0x30;
+ config->fsr = 4096;
+ } else {
+ reg1 |= 0x10;
+ config->fsr = 8192;
+ }
+
+ lis331dlh_set_ths(mlsl_handle, pdata, config, apply, config->ths);
+ MPL_LOGV("FSR: %d\n", config->fsr);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG4, reg1);
+
+ return result;
+}
+
+static int lis331dlh_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg1;
+ unsigned char reg2;
+ struct lis331dlh_private_data *private_data =
+ (struct lis331dlh_private_data *)(pdata->private_data);
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG1,
+ private_data->suspend.ctrl_reg1);
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG2, 0x0f);
+ reg1 = 0x40;
+ if (private_data->suspend.fsr == 8192)
+ reg1 |= 0x30;
+ else if (private_data->suspend.fsr == 4096)
+ reg1 |= 0x10;
+ /* else bits [4..5] are already zero */
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG4, reg1);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_THS,
+ private_data->suspend.reg_ths);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_DURATION,
+ private_data->suspend.reg_dur);
+
+ if (private_data->suspend.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
+ reg1 = 0x02;
+ reg2 = 0x00;
+ } else if (private_data->suspend.irq_type ==
+ MPU_SLAVE_IRQ_TYPE_MOTION) {
+ reg1 = 0x00;
+ reg2 = private_data->suspend.mot_int1_cfg;
+ } else {
+ reg1 = 0x00;
+ reg2 = 0x00;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG3, reg1);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_CFG, reg2);
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ LIS331_HP_FILTER_RESET, 1, &reg1);
+ return result;
+}
+
+static int lis331dlh_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg1;
+ unsigned char reg2;
+ struct lis331dlh_private_data *private_data =
+ (struct lis331dlh_private_data *)(pdata->private_data);
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG1,
+ private_data->resume.ctrl_reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(6);
+
+ /* Full Scale */
+ reg1 = 0x40;
+ if (private_data->resume.fsr == 8192)
+ reg1 |= 0x30;
+ else if (private_data->resume.fsr == 4096)
+ reg1 |= 0x10;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG4, reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Configure high pass filter */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG2, 0x0F);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
+ reg1 = 0x02;
+ reg2 = 0x00;
+ } else if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
+ reg1 = 0x00;
+ reg2 = private_data->resume.mot_int1_cfg;
+ } else {
+ reg1 = 0x00;
+ reg2 = 0x00;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG3, reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_THS,
+ private_data->resume.reg_ths);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_DURATION,
+ private_data->resume.reg_dur);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_CFG, reg2);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ LIS331_HP_FILTER_RESET, 1, &reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+static int lis331dlh_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result = INV_SUCCESS;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ LIS331_STATUS_REG, 1, data);
+ if (data[0] & 0x0F) {
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ slave->read_reg, slave->read_len,
+ data);
+ return result;
+ } else
+ return INV_ERROR_ACCEL_DATA_NOT_READY;
+}
+
+static int lis331dlh_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ struct lis331dlh_private_data *private_data;
+ long range;
+ private_data = (struct lis331dlh_private_data *)
+ kzalloc(sizeof(struct lis331dlh_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ pdata->private_data = private_data;
+
+ private_data->resume.ctrl_reg1 = 0x37;
+ private_data->suspend.ctrl_reg1 = 0x47;
+ private_data->resume.mot_int1_cfg = 0x95;
+ private_data->suspend.mot_int1_cfg = 0x2a;
+
+ lis331dlh_set_odr(mlsl_handle, pdata, &private_data->suspend, FALSE, 0);
+ lis331dlh_set_odr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 200000);
+
+ range = range_fixedpoint_to_long_mg(slave->range);
+ lis331dlh_set_fsr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, range);
+ lis331dlh_set_fsr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, range);
+
+ lis331dlh_set_ths(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 80);
+ lis331dlh_set_ths(mlsl_handle, pdata, &private_data->resume, FALSE, 40);
+
+
+ lis331dlh_set_dur(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 1000);
+ lis331dlh_set_dur(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 2540);
+
+ lis331dlh_set_irq(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ lis331dlh_set_irq(mlsl_handle, pdata, &private_data->resume,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ return INV_SUCCESS;
+}
+
+static int lis331dlh_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+static int lis331dlh_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct lis331dlh_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ return lis331dlh_set_odr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ return lis331dlh_set_odr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ return lis331dlh_set_fsr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ return lis331dlh_set_fsr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ return lis331dlh_set_ths(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ return lis331dlh_set_ths(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ return lis331dlh_set_dur(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ return lis331dlh_set_dur(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ return lis331dlh_set_irq(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ return lis331dlh_set_irq(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ default:
+ LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static int lis331dlh_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct lis331dlh_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.odr;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.odr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.ths;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.ths;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.dur;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.dur;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.irq_type;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.irq_type;
+ break;
+ default:
+ LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static struct ext_slave_descr lis331dlh_descr = {
+ .init = lis331dlh_init,
+ .exit = lis331dlh_exit,
+ .suspend = lis331dlh_suspend,
+ .resume = lis331dlh_resume,
+ .read = lis331dlh_read,
+ .config = lis331dlh_config,
+ .get_config = lis331dlh_get_config,
+ .name = "lis331dlh",
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ACCEL_ID_LIS331,
+ .read_reg = (0x28 | 0x80), /* 0x80 for burst reads */
+ .read_len = 6,
+ .endian = EXT_SLAVE_BIG_ENDIAN,
+ .range = {2, 480},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *lis331_get_slave_descr(void)
+{
+ return &lis331dlh_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct lis331_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int lis331_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct lis331_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ lis331_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int lis331_mod_remove(struct i2c_client *client)
+{
+ struct lis331_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ lis331_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id lis331_mod_id[] = {
+ { "lis331", ACCEL_ID_LIS331 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lis331_mod_id);
+
+static struct i2c_driver lis331_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = lis331_mod_probe,
+ .remove = lis331_mod_remove,
+ .id_table = lis331_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "lis331_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init lis331_mod_init(void)
+{
+ int res = i2c_add_driver(&lis331_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "lis331_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit lis331_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&lis331_mod_driver);
+}
+
+module_init(lis331_mod_init);
+module_exit(lis331_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate LIS331 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("lis331_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/accel/lis3dh.c b/drivers/misc/inv_mpu/accel/lis3dh.c
new file mode 100644
index 0000000..9a07fcc
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/lis3dh.c
@@ -0,0 +1,738 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup ACCELDL
+ * @brief Provides the interface to setup and handle an accelerometer.
+ *
+ * @{
+ * @file lis3dh.c
+ * @brief Accelerometer setup and handling methods for ST LIS3DH.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#undef MPL_LOG_NDEBUG
+#define MPL_LOG_NDEBUG 0
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+/* full scale setting - register & mask */
+#define LIS3DH_CTRL_REG1 (0x20)
+#define LIS3DH_CTRL_REG2 (0x21)
+#define LIS3DH_CTRL_REG3 (0x22)
+#define LIS3DH_CTRL_REG4 (0x23)
+#define LIS3DH_CTRL_REG5 (0x24)
+#define LIS3DH_CTRL_REG6 (0x25)
+#define LIS3DH_REFERENCE (0x26)
+#define LIS3DH_STATUS_REG (0x27)
+#define LIS3DH_OUT_X_L (0x28)
+#define LIS3DH_OUT_X_H (0x29)
+#define LIS3DH_OUT_Y_L (0x2a)
+#define LIS3DH_OUT_Y_H (0x2b)
+#define LIS3DH_OUT_Z_L (0x2b)
+#define LIS3DH_OUT_Z_H (0x2d)
+
+#define LIS3DH_INT1_CFG (0x30)
+#define LIS3DH_INT1_SRC (0x31)
+#define LIS3DH_INT1_THS (0x32)
+#define LIS3DH_INT1_DURATION (0x33)
+
+#define LIS3DH_MAX_DUR (0x7F)
+
+/* -------------------------------------------------------------------------- */
+
+struct lis3dh_config {
+ unsigned int odr;
+ unsigned int fsr; /* full scale range mg */
+ unsigned int ths; /* Motion no-motion thseshold mg */
+ unsigned int dur; /* Motion no-motion duration ms */
+ unsigned char reg_ths;
+ unsigned char reg_dur;
+ unsigned char ctrl_reg1;
+ unsigned char irq_type;
+ unsigned char mot_int1_cfg;
+};
+
+struct lis3dh_private_data {
+ struct lis3dh_config suspend;
+ struct lis3dh_config resume;
+};
+
+/* -------------------------------------------------------------------------- */
+
+static int lis3dh_set_ths(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lis3dh_config *config, int apply, long ths)
+{
+ int result = INV_SUCCESS;
+ if ((unsigned int)ths > 1000 * config->fsr)
+ ths = (long)1000 * config->fsr;
+
+ if (ths < 0)
+ ths = 0;
+
+ config->ths = ths;
+ config->reg_ths = (unsigned char)(long)((ths * 128L) / (config->fsr));
+ MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_INT1_THS,
+ config->reg_ths);
+ return result;
+}
+
+static int lis3dh_set_dur(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lis3dh_config *config, int apply, long dur)
+{
+ int result = INV_SUCCESS;
+ long reg_dur = (dur * config->odr) / 1000000L;
+ config->dur = dur;
+
+ if (reg_dur > LIS3DH_MAX_DUR)
+ reg_dur = LIS3DH_MAX_DUR;
+
+ config->reg_dur = (unsigned char)reg_dur;
+ MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_INT1_DURATION,
+ (unsigned char)reg_dur);
+ return result;
+}
+
+/**
+ * Sets the IRQ to fire when one of the IRQ events occur. Threshold and
+ * duration will not be used uless the type is MOT or NMOT.
+ *
+ * @param config configuration to apply to, suspend or resume
+ * @param irq_type The type of IRQ. Valid values are
+ * - MPU_SLAVE_IRQ_TYPE_NONE
+ * - MPU_SLAVE_IRQ_TYPE_MOTION
+ * - MPU_SLAVE_IRQ_TYPE_DATA_READY
+ */
+static int lis3dh_set_irq(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lis3dh_config *config,
+ int apply, long irq_type)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg1;
+ unsigned char reg2;
+
+ config->irq_type = (unsigned char)irq_type;
+ if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
+ reg1 = 0x10;
+ reg2 = 0x00;
+ } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
+ reg1 = 0x40;
+ reg2 = config->mot_int1_cfg;
+ } else {
+ reg1 = 0x00;
+ reg2 = 0x00;
+ }
+
+ if (apply) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG3, reg1);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_INT1_CFG, reg2);
+ }
+
+ return result;
+}
+
+/**
+ * Set the Output data rate for the particular configuration
+ *
+ * @param config Config to modify with new ODR
+ * @param odr Output data rate in units of 1/1000Hz
+ */
+static int lis3dh_set_odr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lis3dh_config *config, int apply, long odr)
+{
+ unsigned char bits;
+ int result = INV_SUCCESS;
+
+ if (odr > 400000) {
+ config->odr = 1250000;
+ bits = 0x90;
+ } else if (odr > 200000) {
+ config->odr = 400000;
+ bits = 0x70;
+ } else if (odr > 100000) {
+ config->odr = 200000;
+ bits = 0x60;
+ } else if (odr > 50000) {
+ config->odr = 100000;
+ bits = 0x50;
+ } else if (odr > 25000) {
+ config->odr = 50000;
+ bits = 0x40;
+ } else if (odr > 10000) {
+ config->odr = 25000;
+ bits = 0x30;
+ } else if (odr > 1000) {
+ config->odr = 10000;
+ bits = 0x20;
+ } else if (odr > 500) {
+ config->odr = 1000;
+ bits = 0x10;
+ } else {
+ config->odr = 0;
+ bits = 0;
+ }
+
+ config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0xf);
+ lis3dh_set_dur(mlsl_handle, pdata, config, apply, config->dur);
+ MPL_LOGV("ODR: %d, 0x%02x\n", config->odr, (int)config->ctrl_reg1);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG1,
+ config->ctrl_reg1);
+ return result;
+}
+
+/**
+ * Set the full scale range of the accels
+ *
+ * @param config pointer to configuration
+ * @param fsr requested full scale range
+ */
+static int lis3dh_set_fsr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lis3dh_config *config, int apply, long fsr)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg1 = 0x48;
+
+ if (fsr <= 2048) {
+ config->fsr = 2048;
+ } else if (fsr <= 4096) {
+ reg1 |= 0x10;
+ config->fsr = 4096;
+ } else if (fsr <= 8192) {
+ reg1 |= 0x20;
+ config->fsr = 8192;
+ } else {
+ reg1 |= 0x30;
+ config->fsr = 16348;
+ }
+
+ lis3dh_set_ths(mlsl_handle, pdata, config, apply, config->ths);
+ MPL_LOGV("FSR: %d\n", config->fsr);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG4, reg1);
+
+ return result;
+}
+
+static int lis3dh_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg1;
+ unsigned char reg2;
+ struct lis3dh_private_data *private_data = pdata->private_data;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG1,
+ private_data->suspend.ctrl_reg1);
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG2, 0x31);
+ reg1 = 0x48;
+ if (private_data->suspend.fsr == 16384)
+ reg1 |= 0x30;
+ else if (private_data->suspend.fsr == 8192)
+ reg1 |= 0x20;
+ else if (private_data->suspend.fsr == 4096)
+ reg1 |= 0x10;
+ else if (private_data->suspend.fsr == 2048)
+ reg1 |= 0x00;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG4, reg1);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_INT1_THS,
+ private_data->suspend.reg_ths);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_INT1_DURATION,
+ private_data->suspend.reg_dur);
+
+ if (private_data->suspend.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
+ reg1 = 0x10;
+ reg2 = 0x00;
+ } else if (private_data->suspend.irq_type ==
+ MPU_SLAVE_IRQ_TYPE_MOTION) {
+ reg1 = 0x40;
+ reg2 = private_data->suspend.mot_int1_cfg;
+ } else {
+ reg1 = 0x00;
+ reg2 = 0x00;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG3, reg1);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_INT1_CFG, reg2);
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG6, 1, &reg1);
+
+ return result;
+}
+
+static int lis3dh_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ unsigned char reg1;
+ unsigned char reg2;
+ struct lis3dh_private_data *private_data = pdata->private_data;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG1,
+ private_data->resume.ctrl_reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(6);
+
+ /* Full Scale */
+ reg1 = 0x48;
+ if (private_data->suspend.fsr == 16384)
+ reg1 |= 0x30;
+ else if (private_data->suspend.fsr == 8192)
+ reg1 |= 0x20;
+ else if (private_data->suspend.fsr == 4096)
+ reg1 |= 0x10;
+ else if (private_data->suspend.fsr == 2048)
+ reg1 |= 0x00;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG4, reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Configure high pass filter */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG2, 0x31);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
+ reg1 = 0x10;
+ reg2 = 0x00;
+ } else if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
+ reg1 = 0x40;
+ reg2 = private_data->resume.mot_int1_cfg;
+ } else {
+ reg1 = 0x00;
+ reg2 = 0x00;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG3, reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_INT1_THS,
+ private_data->resume.reg_ths);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_INT1_DURATION,
+ private_data->resume.reg_dur);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_INT1_CFG, reg2);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG6, 1, &reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+static int lis3dh_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result = INV_SUCCESS;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ LIS3DH_STATUS_REG, 1, data);
+ if (data[0] & 0x0F) {
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ slave->read_reg, slave->read_len,
+ data);
+ return result;
+ } else
+ return INV_ERROR_ACCEL_DATA_NOT_READY;
+}
+
+static int lis3dh_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ long range;
+ struct lis3dh_private_data *private_data;
+ private_data = (struct lis3dh_private_data *)
+ kzalloc(sizeof(struct lis3dh_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ pdata->private_data = private_data;
+
+ private_data->resume.ctrl_reg1 = 0x67;
+ private_data->suspend.ctrl_reg1 = 0x18;
+ private_data->resume.mot_int1_cfg = 0x95;
+ private_data->suspend.mot_int1_cfg = 0x2a;
+
+ lis3dh_set_odr(mlsl_handle, pdata, &private_data->suspend, FALSE, 0);
+ lis3dh_set_odr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 200000);
+
+ range = range_fixedpoint_to_long_mg(slave->range);
+ lis3dh_set_fsr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, range);
+ lis3dh_set_fsr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, range);
+
+ lis3dh_set_ths(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 80);
+ lis3dh_set_ths(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 40);
+
+ lis3dh_set_dur(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 1000);
+ lis3dh_set_dur(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 2540);
+
+ lis3dh_set_irq(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ lis3dh_set_irq(mlsl_handle, pdata, &private_data->resume,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS3DH_CTRL_REG1, 0x07);
+ msleep(6);
+
+ return INV_SUCCESS;
+}
+
+static int lis3dh_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+static int lis3dh_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct lis3dh_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ return lis3dh_set_odr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ return lis3dh_set_odr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ return lis3dh_set_fsr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ return lis3dh_set_fsr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ return lis3dh_set_ths(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ return lis3dh_set_ths(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ return lis3dh_set_dur(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ return lis3dh_set_dur(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ return lis3dh_set_irq(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ return lis3dh_set_irq(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+ return INV_SUCCESS;
+}
+
+static int lis3dh_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct lis3dh_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.odr;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.odr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.ths;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.ths;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.dur;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.dur;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.irq_type;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.irq_type;
+ break;
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static struct ext_slave_descr lis3dh_descr = {
+ .init = lis3dh_init,
+ .exit = lis3dh_exit,
+ .suspend = lis3dh_suspend,
+ .resume = lis3dh_resume,
+ .read = lis3dh_read,
+ .config = lis3dh_config,
+ .get_config = lis3dh_get_config,
+ .name = "lis3dh",
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ACCEL_ID_LIS3DH,
+ .read_reg = 0x28 | 0x80, /* 0x80 for burst reads */
+ .read_len = 6,
+ .endian = EXT_SLAVE_BIG_ENDIAN,
+ .range = {2, 480},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *lis3dh_get_slave_descr(void)
+{
+ return &lis3dh_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct lis3dh_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int lis3dh_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct lis3dh_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ lis3dh_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int lis3dh_mod_remove(struct i2c_client *client)
+{
+ struct lis3dh_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ lis3dh_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id lis3dh_mod_id[] = {
+ { "lis3dh", ACCEL_ID_LIS3DH },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lis3dh_mod_id);
+
+static struct i2c_driver lis3dh_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = lis3dh_mod_probe,
+ .remove = lis3dh_mod_remove,
+ .id_table = lis3dh_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "lis3dh_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init lis3dh_mod_init(void)
+{
+ int res = i2c_add_driver(&lis3dh_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "lis3dh_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit lis3dh_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&lis3dh_mod_driver);
+}
+
+module_init(lis3dh_mod_init);
+module_exit(lis3dh_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate LIS3DH sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("lis3dh_mod");
+
+/*
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/accel/lsm303a.c b/drivers/misc/inv_mpu/accel/lsm303a.c
new file mode 100644
index 0000000..5df6916
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/lsm303a.c
@@ -0,0 +1,878 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup ACCELDL
+ * @brief Provides the interface to setup and handle an accelerometer.
+ *
+ * @{
+ * @file lsm303a.c
+ * @brief Accelerometer setup and handling methods for ST LSM303.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+/* -------------------------------------------------------------------------- */
+
+/* full scale setting - register & mask */
+#define LIS331_CTRL_REG1 (0x20)
+#define LIS331_CTRL_REG2 (0x21)
+#define LIS331_CTRL_REG3 (0x22)
+#define LIS331_CTRL_REG4 (0x23)
+#define LIS331_CTRL_REG5 (0x24)
+#define LIS331_HP_FILTER_RESET (0x25)
+#define LIS331_REFERENCE (0x26)
+#define LIS331_STATUS_REG (0x27)
+#define LIS331_OUT_X_L (0x28)
+#define LIS331_OUT_X_H (0x29)
+#define LIS331_OUT_Y_L (0x2a)
+#define LIS331_OUT_Y_H (0x2b)
+#define LIS331_OUT_Z_L (0x2b)
+#define LIS331_OUT_Z_H (0x2d)
+
+#define LIS331_INT1_CFG (0x30)
+#define LIS331_INT1_SRC (0x31)
+#define LIS331_INT1_THS (0x32)
+#define LIS331_INT1_DURATION (0x33)
+
+#define LIS331_INT2_CFG (0x34)
+#define LIS331_INT2_SRC (0x35)
+#define LIS331_INT2_THS (0x36)
+#define LIS331_INT2_DURATION (0x37)
+
+#define LIS331_CTRL_MASK (0x30)
+#define LIS331_SLEEP_MASK (0x20)
+
+#define LIS331_MAX_DUR (0x7F)
+
+/* -------------------------------------------------------------------------- */
+
+struct lsm303dlha_config {
+ unsigned int odr;
+ unsigned int fsr; /** < full scale range mg */
+ unsigned int ths; /** < Motion no-motion thseshold mg */
+ unsigned int dur; /** < Motion no-motion duration ms */
+ unsigned char reg_ths;
+ unsigned char reg_dur;
+ unsigned char ctrl_reg1;
+ unsigned char irq_type;
+ unsigned char mot_int1_cfg;
+};
+
+struct lsm303dlha_private_data {
+ struct lsm303dlha_config suspend;
+ struct lsm303dlha_config resume;
+};
+
+/* -------------------------------------------------------------------------- */
+
+static int lsm303dlha_set_ths(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lsm303dlha_config *config,
+ int apply,
+ long ths)
+{
+ int result = INV_SUCCESS;
+ if ((unsigned int) ths >= config->fsr)
+ ths = (long) config->fsr - 1;
+
+ if (ths < 0)
+ ths = 0;
+
+ config->ths = ths;
+ config->reg_ths = (unsigned char)(long)((ths * 128L) / (config->fsr));
+ MPL_LOGV("THS: %d, 0x%02x\n", config->ths, (int)config->reg_ths);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_THS,
+ config->reg_ths);
+ return result;
+}
+
+static int lsm303dlha_set_dur(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lsm303dlha_config *config,
+ int apply,
+ long dur)
+{
+ int result = INV_SUCCESS;
+ long reg_dur = (dur * config->odr) / 1000000L;
+ config->dur = dur;
+
+ if (reg_dur > LIS331_MAX_DUR)
+ reg_dur = LIS331_MAX_DUR;
+
+ config->reg_dur = (unsigned char) reg_dur;
+ MPL_LOGV("DUR: %d, 0x%02x\n", config->dur, (int)config->reg_dur);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_DURATION,
+ (unsigned char)reg_dur);
+ return result;
+}
+
+/**
+ * Sets the IRQ to fire when one of the IRQ events occur. Threshold and
+ * duration will not be used uless the type is MOT or NMOT.
+ *
+ * @param config configuration to apply to, suspend or resume
+ * @param irq_type The type of IRQ. Valid values are
+ * - MPU_SLAVE_IRQ_TYPE_NONE
+ * - MPU_SLAVE_IRQ_TYPE_MOTION
+ * - MPU_SLAVE_IRQ_TYPE_DATA_READY
+ */
+static int lsm303dlha_set_irq(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lsm303dlha_config *config,
+ int apply,
+ long irq_type)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg1;
+ unsigned char reg2;
+
+ config->irq_type = (unsigned char)irq_type;
+ if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
+ reg1 = 0x02;
+ reg2 = 0x00;
+ } else if (irq_type == MPU_SLAVE_IRQ_TYPE_MOTION) {
+ reg1 = 0x00;
+ reg2 = config->mot_int1_cfg;
+ } else {
+ reg1 = 0x00;
+ reg2 = 0x00;
+ }
+
+ if (apply) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG3, reg1);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_CFG, reg2);
+ }
+
+ return result;
+}
+
+/**
+ * @brief Set the output data rate for the particular configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * Config to modify with new ODR.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param odr
+ * Output data rate in units of 1/1000Hz (mHz).
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int lsm303dlha_set_odr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lsm303dlha_config *config,
+ int apply,
+ long odr)
+{
+ unsigned char bits;
+ int result = INV_SUCCESS;
+
+ if (odr > 400000) {
+ config->odr = 1000000;
+ bits = 0x38;
+ } else if (odr > 100000) {
+ config->odr = 400000;
+ bits = 0x30;
+ } else if (odr > 50000) {
+ config->odr = 100000;
+ bits = 0x28;
+ } else if (odr > 10000) {
+ config->odr = 50000;
+ bits = 0x20;
+ } else if (odr > 5000) {
+ config->odr = 10000;
+ bits = 0xC0;
+ } else if (odr > 2000) {
+ config->odr = 5000;
+ bits = 0xB0;
+ } else if (odr > 1000) {
+ config->odr = 2000;
+ bits = 0x80;
+ } else if (odr > 500) {
+ config->odr = 1000;
+ bits = 0x60;
+ } else if (odr > 0) {
+ config->odr = 500;
+ bits = 0x40;
+ } else {
+ config->odr = 0;
+ bits = 0;
+ }
+
+ config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0x7);
+ lsm303dlha_set_dur(mlsl_handle, pdata,
+ config, apply, config->dur);
+ MPL_LOGV("ODR: %d, 0x%02x\n", config->odr, (int)config->ctrl_reg1);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG1,
+ config->ctrl_reg1);
+ return result;
+}
+
+/**
+ * @brief Set the full scale range of the accels
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * pointer to configuration.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param fsr
+ * requested full scale range.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int lsm303dlha_set_fsr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct lsm303dlha_config *config,
+ int apply,
+ long fsr)
+{
+ unsigned char reg1 = 0x40;
+ int result = INV_SUCCESS;
+
+ if (fsr <= 2048) {
+ config->fsr = 2048;
+ } else if (fsr <= 4096) {
+ reg1 |= 0x30;
+ config->fsr = 4096;
+ } else {
+ reg1 |= 0x10;
+ config->fsr = 8192;
+ }
+
+ lsm303dlha_set_ths(mlsl_handle, pdata,
+ config, apply, config->ths);
+ MPL_LOGV("FSR: %d\n", config->fsr);
+ if (apply)
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG4, reg1);
+
+ return result;
+}
+
+/**
+ * @brief suspends the device to put it in its lowest power mode.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int lsm303dlha_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg1;
+ unsigned char reg2;
+ struct lsm303dlha_private_data *private_data =
+ (struct lsm303dlha_private_data *)(pdata->private_data);
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG1,
+ private_data->suspend.ctrl_reg1);
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG2, 0x0f);
+ reg1 = 0x40;
+ if (private_data->suspend.fsr == 8192)
+ reg1 |= 0x30;
+ else if (private_data->suspend.fsr == 4096)
+ reg1 |= 0x10;
+ /* else bits [4..5] are already zero */
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG4, reg1);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_THS,
+ private_data->suspend.reg_ths);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_DURATION,
+ private_data->suspend.reg_dur);
+
+ if (private_data->suspend.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
+ reg1 = 0x02;
+ reg2 = 0x00;
+ } else if (private_data->suspend.irq_type ==
+ MPU_SLAVE_IRQ_TYPE_MOTION) {
+ reg1 = 0x00;
+ reg2 = private_data->suspend.mot_int1_cfg;
+ } else {
+ reg1 = 0x00;
+ reg2 = 0x00;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG3, reg1);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_CFG, reg2);
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ LIS331_HP_FILTER_RESET, 1, &reg1);
+ return result;
+}
+
+/**
+ * @brief resume the device in the proper power state given the configuration
+ * chosen.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int lsm303dlha_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg1;
+ unsigned char reg2;
+ struct lsm303dlha_private_data *private_data =
+ (struct lsm303dlha_private_data *)(pdata->private_data);
+
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG1,
+ private_data->resume.ctrl_reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(6);
+
+ /* Full Scale */
+ reg1 = 0x40;
+ if (private_data->resume.fsr == 8192)
+ reg1 |= 0x30;
+ else if (private_data->resume.fsr == 4096)
+ reg1 |= 0x10;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG4, reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Configure high pass filter */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG2, 0x0F);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (private_data->resume.irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
+ reg1 = 0x02;
+ reg2 = 0x00;
+ } else if (private_data->resume.irq_type ==
+ MPU_SLAVE_IRQ_TYPE_MOTION) {
+ reg1 = 0x00;
+ reg2 = private_data->resume.mot_int1_cfg;
+ } else {
+ reg1 = 0x00;
+ reg2 = 0x00;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_CTRL_REG3, reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_THS,
+ private_data->resume.reg_ths);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_DURATION,
+ private_data->resume.reg_dur);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ LIS331_INT1_CFG, reg2);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ LIS331_HP_FILTER_RESET, 1, &reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+/**
+ * @brief read the sensor data from the device.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a buffer to store the data read.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int lsm303dlha_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result = INV_SUCCESS;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ LIS331_STATUS_REG, 1, data);
+ if (data[0] & 0x0F) {
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ slave->read_reg, slave->read_len, data);
+ return result;
+ } else
+ return INV_ERROR_ACCEL_DATA_NOT_READY;
+}
+
+/**
+ * @brief one-time device driver initialization function.
+ * If the driver is built as a kernel module, this function will be
+ * called when the module is loaded in the kernel.
+ * If the driver is built-in in the kernel, this function will be
+ * called at boot time.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int lsm303dlha_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ long range;
+ struct lsm303dlha_private_data *private_data;
+ private_data = (struct lsm303dlha_private_data *)
+ kzalloc(sizeof(struct lsm303dlha_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ pdata->private_data = private_data;
+
+ private_data->resume.ctrl_reg1 = 0x37;
+ private_data->suspend.ctrl_reg1 = 0x47;
+ private_data->resume.mot_int1_cfg = 0x95;
+ private_data->suspend.mot_int1_cfg = 0x2a;
+
+ lsm303dlha_set_odr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 0);
+ lsm303dlha_set_odr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 200000);
+
+ range = range_fixedpoint_to_long_mg(slave->range);
+ lsm303dlha_set_fsr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, range);
+ lsm303dlha_set_fsr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, range);
+
+ lsm303dlha_set_ths(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 80);
+ lsm303dlha_set_ths(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 40);
+
+ lsm303dlha_set_dur(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 1000);
+ lsm303dlha_set_dur(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 2540);
+
+ lsm303dlha_set_irq(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ lsm303dlha_set_irq(mlsl_handle, pdata, &private_data->resume,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief one-time device driver exit function.
+ * If the driver is built as a kernel module, this function will be
+ * called when the module is removed from the kernel.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int lsm303dlha_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief device configuration facility.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a pointer to the configuration data structure.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int lsm303dlha_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct lsm303dlha_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ return lsm303dlha_set_odr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ return lsm303dlha_set_odr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ return lsm303dlha_set_fsr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ return lsm303dlha_set_fsr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ return lsm303dlha_set_ths(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ return lsm303dlha_set_ths(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ return lsm303dlha_set_dur(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ return lsm303dlha_set_dur(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ return lsm303dlha_set_irq(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ return lsm303dlha_set_irq(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ default:
+ LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief facility to retrieve the device configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a pointer to store the returned configuration data structure.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int lsm303dlha_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct lsm303dlha_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.odr;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.odr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.ths;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.ths;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.dur;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.dur;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.irq_type;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.irq_type;
+ break;
+ default:
+ LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static struct ext_slave_descr lsm303dlha_descr = {
+ .init = lsm303dlha_init,
+ .exit = lsm303dlha_exit,
+ .suspend = lsm303dlha_suspend,
+ .resume = lsm303dlha_resume,
+ .read = lsm303dlha_read,
+ .config = lsm303dlha_config,
+ .get_config = lsm303dlha_get_config,
+ .name = "lsm303dlha",
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ACCEL_ID_LSM303A,
+ .read_reg = (0x28 | 0x80), /* 0x80 for burst reads */
+ .read_len = 6,
+ .endian = EXT_SLAVE_BIG_ENDIAN,
+ .range = {2, 480},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *lsm303a_get_slave_descr(void)
+{
+ return &lsm303dlha_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct lsm303a_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int lsm303a_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct lsm303a_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ lsm303a_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int lsm303a_mod_remove(struct i2c_client *client)
+{
+ struct lsm303a_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ lsm303a_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id lsm303a_mod_id[] = {
+ { "lsm303a", ACCEL_ID_LSM303A },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lsm303a_mod_id);
+
+static struct i2c_driver lsm303a_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = lsm303a_mod_probe,
+ .remove = lsm303a_mod_remove,
+ .id_table = lsm303a_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "lsm303a_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init lsm303a_mod_init(void)
+{
+ int res = i2c_add_driver(&lsm303a_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "lsm303a_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit lsm303a_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&lsm303a_mod_driver);
+}
+
+module_init(lsm303a_mod_init);
+module_exit(lsm303a_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate LSM303A sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("lsm303a_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/accel/mma8450.c b/drivers/misc/inv_mpu/accel/mma8450.c
new file mode 100644
index 0000000..772fc46
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/mma8450.c
@@ -0,0 +1,807 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup ACCELDL
+ * @brief Provides the interface to setup and handle an accelerometer.
+ *
+ * @{
+ * @file mma8450.c
+ * @brief Accelerometer setup and handling methods for Freescale MMA8450.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+/* full scale setting - register & mask */
+#define ACCEL_MMA8450_XYZ_DATA_CFG (0x16)
+
+#define ACCEL_MMA8450_CTRL_REG1 (0x38)
+#define ACCEL_MMA8450_CTRL_REG4 (0x3B)
+#define ACCEL_MMA8450_CTRL_REG5 (0x3C)
+
+#define ACCEL_MMA8450_CTRL_REG (0x38)
+#define ACCEL_MMA8450_CTRL_MASK (0x03)
+
+#define ACCEL_MMA8450_SLEEP_MASK (0x03)
+
+/* -------------------------------------------------------------------------- */
+
+struct mma8450_config {
+ unsigned int odr;
+ unsigned int fsr; /** < full scale range mg */
+ unsigned int ths; /** < Motion no-motion thseshold mg */
+ unsigned int dur; /** < Motion no-motion duration ms */
+ unsigned char reg_ths;
+ unsigned char reg_dur;
+ unsigned char ctrl_reg1;
+ unsigned char irq_type;
+ unsigned char mot_int1_cfg;
+};
+
+struct mma8450_private_data {
+ struct mma8450_config suspend;
+ struct mma8450_config resume;
+};
+
+
+/* -------------------------------------------------------------------------- */
+
+static int mma8450_set_ths(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct mma8450_config *config,
+ int apply,
+ long ths)
+{
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+}
+
+static int mma8450_set_dur(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct mma8450_config *config,
+ int apply,
+ long dur)
+{
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+}
+
+/**
+ * @brief Sets the IRQ to fire when one of the IRQ events occur.
+ * Threshold and duration will not be used unless the type is MOT or
+ * NMOT.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * configuration to apply to, suspend or resume
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param irq_type
+ * the type of IRQ. Valid values are
+ * - MPU_SLAVE_IRQ_TYPE_NONE
+ * - MPU_SLAVE_IRQ_TYPE_MOTION
+ * - MPU_SLAVE_IRQ_TYPE_DATA_READY
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma8450_set_irq(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct mma8450_config *config,
+ int apply,
+ long irq_type)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg1;
+ unsigned char reg2;
+ unsigned char reg3;
+
+ config->irq_type = (unsigned char)irq_type;
+ if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
+ reg1 = 0x01;
+ reg2 = 0x01;
+ reg3 = 0x07;
+ } else if (irq_type == MPU_SLAVE_IRQ_TYPE_NONE) {
+ reg1 = 0x00;
+ reg2 = 0x00;
+ reg3 = 0x00;
+ } else {
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ }
+
+ if (apply) {
+ /* XYZ_DATA_CFG: event flag enabled on Z axis */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA8450_XYZ_DATA_CFG, reg3);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA8450_CTRL_REG4, reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA8450_CTRL_REG5, reg2);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * @brief Set the output data rate for the particular configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * Config to modify with new ODR.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param odr
+ * Output data rate in units of 1/1000Hz (mHz).
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma8450_set_odr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct mma8450_config *config,
+ int apply,
+ long odr)
+{
+ unsigned char bits;
+ int result = INV_SUCCESS;
+
+ if (odr > 200000) {
+ config->odr = 400000;
+ bits = 0x00;
+ } else if (odr > 100000) {
+ config->odr = 200000;
+ bits = 0x04;
+ } else if (odr > 50000) {
+ config->odr = 100000;
+ bits = 0x08;
+ } else if (odr > 25000) {
+ config->odr = 50000;
+ bits = 0x0B;
+ } else if (odr > 12500) {
+ config->odr = 25000;
+ bits = 0x40; /* Sleep -> Auto wake mode */
+ } else if (odr > 1563) {
+ config->odr = 12500;
+ bits = 0x10;
+ } else if (odr > 0) {
+ config->odr = 1563;
+ bits = 0x14;
+ } else {
+ config->ctrl_reg1 = 0; /* Set FS1.FS2 to Standby */
+ config->odr = 0;
+ bits = 0;
+ }
+
+ config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0x3);
+ if (apply) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA8450_CTRL_REG1, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA8450_CTRL_REG1, config->ctrl_reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ MPL_LOGV("ODR: %d mHz, 0x%02x\n",
+ config->odr, (int)config->ctrl_reg1);
+ }
+ return result;
+}
+
+/**
+ * @brief Set the full scale range of the accels
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * pointer to configuration.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param fsr
+ * requested full scale range.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma8450_set_fsr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct mma8450_config *config,
+ int apply,
+ long fsr)
+{
+ unsigned char bits;
+ int result = INV_SUCCESS;
+
+ if (fsr <= 2000) {
+ bits = 0x01;
+ config->fsr = 2000;
+ } else if (fsr <= 4000) {
+ bits = 0x02;
+ config->fsr = 4000;
+ } else {
+ bits = 0x03;
+ config->fsr = 8000;
+ }
+
+ config->ctrl_reg1 = bits | (config->ctrl_reg1 & 0xFC);
+ if (apply) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA8450_CTRL_REG1, config->ctrl_reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ MPL_LOGV("FSR: %d mg\n", config->fsr);
+ }
+ return result;
+}
+
+/**
+ * @brief suspends the device to put it in its lowest power mode.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma8450_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ struct mma8450_private_data *private_data = pdata->private_data;
+
+ if (private_data->suspend.fsr == 4000)
+ slave->range.mantissa = 4;
+ else if (private_data->suspend.fsr == 8000)
+ slave->range.mantissa = 8;
+ else
+ slave->range.mantissa = 2;
+ slave->range.fraction = 0;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA8450_CTRL_REG1, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if (private_data->resume.ctrl_reg1) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA8450_CTRL_REG1,
+ private_data->suspend.ctrl_reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ result = mma8450_set_irq(mlsl_handle, pdata,
+ &private_data->suspend,
+ TRUE, private_data->suspend.irq_type);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+/**
+ * @brief resume the device in the proper power state given the configuration
+ * chosen.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma8450_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ struct mma8450_private_data *private_data = pdata->private_data;
+
+ /* Full Scale */
+ if (private_data->resume.fsr == 4000)
+ slave->range.mantissa = 4;
+ else if (private_data->resume.fsr == 8000)
+ slave->range.mantissa = 8;
+ else
+ slave->range.mantissa = 2;
+ slave->range.fraction = 0;
+
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA8450_CTRL_REG1, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if (private_data->resume.ctrl_reg1) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA8450_CTRL_REG1,
+ private_data->resume.ctrl_reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ result = mma8450_set_irq(mlsl_handle, pdata,
+ &private_data->resume,
+ TRUE, private_data->resume.irq_type);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * @brief read the sensor data from the device.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a buffer to store the data read.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma8450_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata, unsigned char *data)
+{
+ int result;
+ unsigned char local_data[4]; /* Status register + 3 bytes data */
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ 0x00,
+ sizeof(local_data), local_data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ memcpy(data, &local_data[1], (slave->read_len) - 1);
+ MPL_LOGV("Data Not Ready: %02x %02x %02x %02x\n",
+ local_data[0],
+ local_data[1],
+ local_data[2],
+ local_data[3]);
+ if (!((local_data[0]) & 0x04))
+ result = INV_ERROR_ACCEL_DATA_NOT_READY;
+
+ return result;
+}
+
+/**
+ * @brief one-time device driver initialization function.
+ * If the driver is built as a kernel module, this function will be
+ * called when the module is loaded in the kernel.
+ * If the driver is built-in in the kernel, this function will be
+ * called at boot time.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma8450_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ struct mma8450_private_data *private_data;
+ private_data = (struct mma8450_private_data *)
+ kzalloc(sizeof(struct mma8450_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ pdata->private_data = private_data;
+
+ mma8450_set_odr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 0);
+ mma8450_set_odr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 200000);
+ mma8450_set_fsr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 2000);
+ mma8450_set_fsr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 2000);
+ mma8450_set_irq(mlsl_handle, pdata, &private_data->suspend,
+ FALSE,
+ MPU_SLAVE_IRQ_TYPE_NONE);
+ mma8450_set_irq(mlsl_handle, pdata, &private_data->resume,
+ FALSE,
+ MPU_SLAVE_IRQ_TYPE_NONE);
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief one-time device driver exit function.
+ * If the driver is built as a kernel module, this function will be
+ * called when the module is removed from the kernel.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma8450_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief device configuration facility.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a pointer to the configuration data structure.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma8450_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct mma8450_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ return mma8450_set_odr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ return mma8450_set_odr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ return mma8450_set_fsr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ return mma8450_set_fsr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ return mma8450_set_ths(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ return mma8450_set_ths(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ return mma8450_set_dur(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ return mma8450_set_dur(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ return mma8450_set_irq(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ return mma8450_set_irq(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ default:
+ LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+/**
+ * @brief facility to retrieve the device configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a pointer to store the returned configuration data structure.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma8450_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct mma8450_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.odr;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.odr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.ths;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.ths;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.dur;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.dur;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.irq_type;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.irq_type;
+ break;
+ default:
+ LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static struct ext_slave_descr mma8450_descr = {
+ .init = mma8450_init,
+ .exit = mma8450_exit,
+ .suspend = mma8450_suspend,
+ .resume = mma8450_resume,
+ .read = mma8450_read,
+ .config = mma8450_config,
+ .get_config = mma8450_get_config,
+ .name = "mma8450",
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ACCEL_ID_MMA8450,
+ .read_reg = 0x00,
+ .read_len = 4,
+ .endian = EXT_SLAVE_FS8_BIG_ENDIAN,
+ .range = {2, 0},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *mma8450_get_slave_descr(void)
+{
+ return &mma8450_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct mma8450_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int mma8450_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct mma8450_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ mma8450_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int mma8450_mod_remove(struct i2c_client *client)
+{
+ struct mma8450_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ mma8450_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id mma8450_mod_id[] = {
+ { "mma8450", ACCEL_ID_MMA8450 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, mma8450_mod_id);
+
+static struct i2c_driver mma8450_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = mma8450_mod_probe,
+ .remove = mma8450_mod_remove,
+ .id_table = mma8450_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mma8450_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init mma8450_mod_init(void)
+{
+ int res = i2c_add_driver(&mma8450_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "mma8450_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit mma8450_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&mma8450_mod_driver);
+}
+
+module_init(mma8450_mod_init);
+module_exit(mma8450_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate MMA8450 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("mma8450_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/accel/mma845x.c b/drivers/misc/inv_mpu/accel/mma845x.c
new file mode 100644
index 0000000..46d1d1f
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/mma845x.c
@@ -0,0 +1,713 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup ACCELDL
+ * @brief Provides the interface to setup and handle an accelerometer.
+ *
+ * @{
+ * @file mma845x.c
+ * @brief Accelerometer setup and handling methods for Freescale MMA845X
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+#define ACCEL_MMA845X_XYZ_DATA_CFG (0x0E)
+#define ACCEL_MMA845X_CTRL_REG1 (0x2A)
+#define ACCEL_MMA845X_CTRL_REG4 (0x2D)
+#define ACCEL_MMA845X_CTRL_REG5 (0x2E)
+
+#define ACCEL_MMA845X_SLEEP_MASK (0x01)
+
+/* full scale setting - register & mask */
+#define ACCEL_MMA845X_CFG_REG (0x0E)
+#define ACCEL_MMA845X_CTRL_MASK (0x03)
+
+/* -------------------------------------------------------------------------- */
+
+struct mma845x_config {
+ unsigned int odr;
+ unsigned int fsr; /** < full scale range mg */
+ unsigned int ths; /** < Motion no-motion thseshold mg */
+ unsigned int dur; /** < Motion no-motion duration ms */
+ unsigned char reg_ths;
+ unsigned char reg_dur;
+ unsigned char ctrl_reg1;
+ unsigned char irq_type;
+ unsigned char mot_int1_cfg;
+};
+
+struct mma845x_private_data {
+ struct mma845x_config suspend;
+ struct mma845x_config resume;
+};
+
+/* -------------------------------------------------------------------------- */
+
+static int mma845x_set_ths(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct mma845x_config *config,
+ int apply,
+ long ths)
+{
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+}
+
+static int mma845x_set_dur(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct mma845x_config *config,
+ int apply,
+ long dur)
+{
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+}
+
+/**
+ * @brief Sets the IRQ to fire when one of the IRQ events occur.
+ * Threshold and duration will not be used unless the type is MOT or
+ * NMOT.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * configuration to apply to, suspend or resume
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param irq_type
+ * the type of IRQ. Valid values are
+ * - MPU_SLAVE_IRQ_TYPE_NONE
+ * - MPU_SLAVE_IRQ_TYPE_MOTION
+ * - MPU_SLAVE_IRQ_TYPE_DATA_READY
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma845x_set_irq(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct mma845x_config *config,
+ int apply,
+ long irq_type)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg1;
+ unsigned char reg2;
+
+ config->irq_type = (unsigned char)irq_type;
+ if (irq_type == MPU_SLAVE_IRQ_TYPE_DATA_READY) {
+ reg1 = 0x01;
+ reg2 = 0x01;
+ } else if (irq_type == MPU_SLAVE_IRQ_TYPE_NONE) {
+ reg1 = 0x00;
+ reg2 = 0x00;
+ } else {
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ }
+
+ if (apply) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA845X_CTRL_REG4, reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA845X_CTRL_REG5, reg2);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * @brief Set the output data rate for the particular configuration.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * Config to modify with new ODR.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param odr
+ * Output data rate in units of 1/1000Hz (mHz).
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma845x_set_odr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct mma845x_config *config,
+ int apply,
+ long odr)
+{
+ unsigned char bits;
+ int result = INV_SUCCESS;
+
+ if (odr > 400000) {
+ config->odr = 800000;
+ bits = 0x01;
+ } else if (odr > 200000) {
+ config->odr = 400000;
+ bits = 0x09;
+ } else if (odr > 100000) {
+ config->odr = 200000;
+ bits = 0x11;
+ } else if (odr > 50000) {
+ config->odr = 100000;
+ bits = 0x19;
+ } else if (odr > 12500) {
+ config->odr = 50000;
+ bits = 0x21;
+ } else if (odr > 6250) {
+ config->odr = 12500;
+ bits = 0x29;
+ } else if (odr > 1560) {
+ config->odr = 6250;
+ bits = 0x31;
+ } else if (odr > 0) {
+ config->odr = 1560;
+ bits = 0x39;
+ } else {
+ config->ctrl_reg1 = 0; /* Set FS1.FS2 to Standby */
+ config->odr = 0;
+ bits = 0;
+ }
+
+ config->ctrl_reg1 = bits;
+ if (apply) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA845X_CTRL_REG1,
+ config->ctrl_reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ MPL_LOGV("ODR: %d mHz, 0x%02x\n", config->odr,
+ (int)config->ctrl_reg1);
+ }
+ return result;
+}
+
+/**
+ * @brief Set the full scale range of the accels
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param config
+ * pointer to configuration.
+ * @param apply
+ * whether to apply immediately or save the settings to be applied
+ * at the next resume.
+ * @param fsr
+ * requested full scale range.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma845x_set_fsr(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ struct mma845x_config *config,
+ int apply,
+ long fsr)
+{
+ unsigned char bits;
+ int result = INV_SUCCESS;
+
+ if (fsr <= 2000) {
+ bits = 0x00;
+ config->fsr = 2000;
+ } else if (fsr <= 4000) {
+ bits = 0x01;
+ config->fsr = 4000;
+ } else {
+ bits = 0x02;
+ config->fsr = 8000;
+ }
+
+ if (apply) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA845X_XYZ_DATA_CFG,
+ bits);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ MPL_LOGV("FSR: %d mg\n", config->fsr);
+ }
+ return result;
+}
+
+/**
+ * @brief suspends the device to put it in its lowest power mode.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma845x_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ struct mma845x_private_data *private_data = pdata->private_data;
+
+ /* Full Scale */
+ if (private_data->suspend.fsr == 4000)
+ slave->range.mantissa = 4;
+ else if (private_data->suspend.fsr == 8000)
+ slave->range.mantissa = 8;
+ else
+ slave->range.mantissa = 2;
+
+ slave->range.fraction = 0;
+
+ result = mma845x_set_fsr(mlsl_handle, pdata,
+ &private_data->suspend,
+ TRUE, private_data->suspend.fsr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA845X_CTRL_REG1,
+ private_data->suspend.ctrl_reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * @brief resume the device in the proper power state given the configuration
+ * chosen.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma845x_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ struct mma845x_private_data *private_data = pdata->private_data;
+
+ /* Full Scale */
+ if (private_data->resume.fsr == 4000)
+ slave->range.mantissa = 4;
+ else if (private_data->resume.fsr == 8000)
+ slave->range.mantissa = 8;
+ else
+ slave->range.mantissa = 2;
+
+ slave->range.fraction = 0;
+
+ result = mma845x_set_fsr(mlsl_handle, pdata,
+ &private_data->resume,
+ TRUE, private_data->resume.fsr);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ ACCEL_MMA845X_CTRL_REG1,
+ private_data->resume.ctrl_reg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+/**
+ * @brief read the sensor data from the device.
+ *
+ * @param mlsl_handle
+ * the handle to the serial channel the device is connected to.
+ * @param slave
+ * a pointer to the slave descriptor data structure.
+ * @param pdata
+ * a pointer to the slave platform data.
+ * @param data
+ * a buffer to store the data read.
+ *
+ * @return INV_SUCCESS if successful or a non-zero error code.
+ */
+static int mma845x_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata, unsigned char *data)
+{
+ int result;
+ unsigned char local_data[7]; /* Status register + 6 bytes data */
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ slave->read_reg, sizeof(local_data),
+ local_data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ memcpy(data, &local_data[1], slave->read_len);
+ return result;
+}
+
+static int mma845x_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ long range;
+ struct mma845x_private_data *private_data;
+ private_data = (struct mma845x_private_data *)
+ kzalloc(sizeof(struct mma845x_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ pdata->private_data = private_data;
+
+ mma845x_set_odr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 0);
+ mma845x_set_odr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, 200000);
+
+ range = range_fixedpoint_to_long_mg(slave->range);
+ mma845x_set_fsr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, range);
+ mma845x_set_fsr(mlsl_handle, pdata, &private_data->resume,
+ FALSE, range);
+
+ mma845x_set_irq(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ mma845x_set_irq(mlsl_handle, pdata, &private_data->resume,
+ FALSE, MPU_SLAVE_IRQ_TYPE_NONE);
+ return INV_SUCCESS;
+}
+
+static int mma845x_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+static int mma845x_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct mma845x_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ return mma845x_set_odr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ return mma845x_set_odr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ return mma845x_set_fsr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ return mma845x_set_fsr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ return mma845x_set_ths(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ return mma845x_set_ths(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ return mma845x_set_dur(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ return mma845x_set_dur(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ return mma845x_set_irq(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply,
+ *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ return mma845x_set_irq(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply,
+ *((long *)data->data));
+ default:
+ LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static int mma845x_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct mma845x_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.odr;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.odr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.ths;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.ths;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.dur;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.dur;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->suspend.irq_type;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long) private_data->resume.irq_type;
+ break;
+ default:
+ LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static struct ext_slave_descr mma845x_descr = {
+ .init = mma845x_init,
+ .exit = mma845x_exit,
+ .suspend = mma845x_suspend,
+ .resume = mma845x_resume,
+ .read = mma845x_read,
+ .config = mma845x_config,
+ .get_config = mma845x_get_config,
+ .name = "mma845x",
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ACCEL_ID_MMA845X,
+ .read_reg = 0x00,
+ .read_len = 6,
+ .endian = EXT_SLAVE_FS16_BIG_ENDIAN,
+ .range = {2, 0},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *mma845x_get_slave_descr(void)
+{
+ return &mma845x_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct mma845x_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int mma845x_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct mma845x_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ mma845x_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int mma845x_mod_remove(struct i2c_client *client)
+{
+ struct mma845x_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ mma845x_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id mma845x_mod_id[] = {
+ { "mma845x", ACCEL_ID_MMA845X },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, mma845x_mod_id);
+
+static struct i2c_driver mma845x_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = mma845x_mod_probe,
+ .remove = mma845x_mod_remove,
+ .id_table = mma845x_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mma845x_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init mma845x_mod_init(void)
+{
+ int res = i2c_add_driver(&mma845x_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "mma845x_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit mma845x_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&mma845x_mod_driver);
+}
+
+module_init(mma845x_mod_init);
+module_exit(mma845x_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate MMA845X sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("mma845x_mod");
+
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/accel/mpu6050.c b/drivers/misc/inv_mpu/accel/mpu6050.c
new file mode 100644
index 0000000..57c01d2
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/mpu6050.c
@@ -0,0 +1,433 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup ACCELDL
+ * @brief Provides the interface to setup and handle an accelerometer.
+ *
+ * @{
+ * @file mpu6050.c
+ * @brief Accelerometer setup and handling methods for Invensense MPU6050
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+/* -------------------------------------------------------------------------- */
+
+struct mpu6050_config {
+ unsigned int odr; /**< output data rate 1/1000 Hz */
+ unsigned int fsr; /**< full scale range mg */
+ unsigned int ths; /**< mot/no-mot thseshold mg */
+ unsigned int dur; /**< mot/no-mot duration ms */
+};
+
+struct mpu6050_private_data {
+ struct mpu6050_config suspend;
+ struct mpu6050_config resume;
+};
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * Record the odr for use in computing duration values.
+ *
+ * @param config Config to set, suspend or resume structure
+ * @param odr output data rate in 1/1000 hz
+ */
+static int mpu6050_set_odr(void *mlsl_handle,
+ struct ext_slave_platform_data *slave,
+ struct mpu6050_config *config, long apply, long odr)
+{
+ config->odr = odr;
+ return INV_SUCCESS;
+}
+
+static int mpu6050_set_ths(void *mlsl_handle,
+ struct ext_slave_platform_data *slave,
+ struct mpu6050_config *config, long apply, long ths)
+{
+ if (ths < 0)
+ ths = 0;
+
+ config->ths = ths;
+ MPL_LOGV("THS: %d\n", config->ths);
+ return INV_SUCCESS;
+}
+
+static int mpu6050_set_dur(void *mlsl_handle,
+ struct ext_slave_platform_data *slave,
+ struct mpu6050_config *config, long apply, long dur)
+{
+ if (dur < 0)
+ dur = 0;
+
+ config->dur = dur;
+ MPL_LOGV("DUR: %d\n", config->dur);
+ return INV_SUCCESS;
+}
+
+static int mpu6050_set_fsr(void *mlsl_handle,
+ struct ext_slave_platform_data *slave,
+ struct mpu6050_config *config, long apply, long fsr)
+{
+ if (fsr <= 2000)
+ config->fsr = 2000;
+ else if (fsr <= 4000)
+ config->fsr = 4000;
+ else
+ config->fsr = 8000;
+
+ MPL_LOGV("FSR: %d\n", config->fsr);
+ return INV_SUCCESS;
+}
+
+static int mpu6050_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ struct mpu6050_private_data *private_data;
+
+ (void *)private_data;
+ return INV_ERROR_INVALID_MODULE;
+
+ private_data = (struct mpu6050_private_data *)
+ kzalloc(sizeof(struct mpu6050_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ pdata->private_data = private_data;
+
+ mpu6050_set_odr(mlsl_handle, pdata, &private_data->suspend, FALSE, 0);
+ mpu6050_set_odr(mlsl_handle, pdata,
+ &private_data->resume, FALSE, 200000);
+ mpu6050_set_fsr(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 2000);
+ mpu6050_set_fsr(mlsl_handle, pdata, &private_data->resume, FALSE, 2000);
+ mpu6050_set_ths(mlsl_handle, pdata, &private_data->suspend, FALSE, 80);
+ mpu6050_set_ths(mlsl_handle, pdata, &private_data->resume, FALSE, 40);
+ mpu6050_set_dur(mlsl_handle, pdata, &private_data->suspend,
+ FALSE, 1000);
+ mpu6050_set_dur(mlsl_handle, pdata, &private_data->resume, FALSE, 2540);
+ return INV_SUCCESS;
+}
+
+static int mpu6050_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+static int mpu6050_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ unsigned char reg;
+ int result;
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ MPUREG_PWR_MGMT_2, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ reg |= (BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA);
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ MPUREG_PWR_MGMT_2, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return INV_SUCCESS;
+}
+
+static int mpu6050_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg;
+ struct mpu6050_private_data *private_data;
+
+ private_data = (struct mpu6050_private_data *)pdata->private_data;
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ MPUREG_PWR_MGMT_1, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if ((reg & BITS_PWRSEL) != BITS_PWRSEL) {
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ MPUREG_PWR_MGMT_1, reg | BITS_PWRSEL);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ msleep(2);
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ MPUREG_PWR_MGMT_2, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ reg &= ~(BIT_STBY_XA | BIT_STBY_YA | BIT_STBY_ZA);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ MPUREG_PWR_MGMT_2, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (slave->range.mantissa == 2)
+ reg = 0;
+ else if (slave->range.mantissa == 4)
+ reg = 1 << 3;
+ else if (slave->range.mantissa == 8)
+ reg = 2 << 3;
+ else if (slave->range.mantissa == 16)
+ reg = 3 << 3;
+ else
+ return INV_ERROR;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ MPUREG_ACCEL_CONFIG, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ reg = (unsigned char)private_data->suspend.ths / ACCEL_MOT_THR_LSB;
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ MPUREG_ACCEL_MOT_THR, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ reg = (unsigned char)
+ ACCEL_ZRMOT_THR_LSB_CONVERSION(private_data->resume.ths);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ MPUREG_ACCEL_ZRMOT_THR, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ reg = (unsigned char)private_data->suspend.ths / ACCEL_MOT_DUR_LSB;
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ MPUREG_ACCEL_MOT_DUR, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ reg = (unsigned char)private_data->resume.ths / ACCEL_ZRMOT_DUR_LSB;
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ MPUREG_ACCEL_ZRMOT_DUR, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+static int mpu6050_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ slave->read_reg, slave->read_len, data);
+ return result;
+}
+
+static int mpu6050_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct mpu6050_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ return mpu6050_set_odr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ return mpu6050_set_odr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ return mpu6050_set_fsr(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ return mpu6050_set_fsr(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ return mpu6050_set_ths(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ return mpu6050_set_ths(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ return mpu6050_set_dur(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ return mpu6050_set_dur(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+#if 0
+ return mpu6050_set_irq(mlsl_handle, pdata,
+ &private_data->suspend,
+ data->apply, *((long *)data->data));
+#endif
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+#if 0
+ return mpu6050_set_irq(mlsl_handle, pdata,
+ &private_data->resume,
+ data->apply, *((long *)data->data));
+#endif
+ break;
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static int mpu6050_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct mpu6050_private_data *private_data = pdata->private_data;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.odr;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.odr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.fsr;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.ths;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.ths;
+ break;
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.dur;
+ break;
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.dur;
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+#if 0
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->suspend.irq_type;
+#endif
+ break;
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+#if 0
+ (*(unsigned long *)data->data) =
+ (unsigned long)private_data->resume.irq_type;
+#endif
+ break;
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static struct ext_slave_descr mpu6050_descr = {
+ .init = mpu6050_init,
+ .exit = mpu6050_exit,
+ .suspend = mpu6050_suspend,
+ .resume = mpu6050_resume,
+ .read = mpu6050_read,
+ .config = mpu6050_config,
+ .get_config = mpu6050_get_config,
+ .name = "mpu6050",
+ .type = EXT_SLAVE_TYPE_ACCELEROMETER,
+ .id = ACCEL_ID_MPU6050,
+ .read_reg = 0x3B,
+ .read_len = 6,
+ .endian = EXT_SLAVE_BIG_ENDIAN,
+ .range = {2, 0},
+ .trigger = NULL,
+};
+
+struct ext_slave_descr *mpu6050_get_slave_descr(void)
+{
+ return &mpu6050_descr;
+}
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/accel/mpu6050.h b/drivers/misc/inv_mpu/accel/mpu6050.h
new file mode 100644
index 0000000..4d4d74e
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/mpu6050.h
@@ -0,0 +1,28 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+
+#ifndef __MPU6050_H__
+#define __MPU6050_H__
+
+#include <linux/mpu.h>
+
+struct ext_slave_descr *mpu6050_get_slave_descr(void);
+
+#endif
diff --git a/drivers/misc/inv_mpu/compass/Kconfig b/drivers/misc/inv_mpu/compass/Kconfig
new file mode 100644
index 0000000..db6d86b
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/Kconfig
@@ -0,0 +1,112 @@
+menuconfig INV_SENSORS_COMPASS
+ bool "Compass Slave Sensors"
+ default y
+ ---help---
+ Say Y here to get to see options for device drivers for various
+ compasses. This option alone does not add any kernel code.
+
+ If you say N, all options in this submenu will be skipped and disabled.
+
+if INV_SENSORS_COMPASS
+
+config MPU_SENSORS_AK8975
+ tristate "AKM ak8975"
+ help
+ This enables support for the AKM ak8975 compass
+ This support is for integration with the MPU3050 or MPU6050 gyroscope
+ device driver. Only one compass can be registered at a time.
+ Specifying more that one compass in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_MMC314X
+ tristate "MEMSIC mmc314x"
+ help
+ This enables support for the MEMSIC mmc314x compass
+ This support is for integration with the MPU3050 or MPU6050 gyroscope
+ device driver. Only one compass can be registered at a time.
+ Specifying more that one compass in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_AMI30X
+ tristate "Aichi Steel ami30X"
+ help
+ This enables support for the Aichi Steel ami304/ami305 compass
+ This support is for integration with the MPU3050 or MPU6050 gyroscope
+ device driver. Only one compass can be registered at a time.
+ Specifying more that one compass in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_AMI306
+ tristate "Aichi Steel ami306"
+ help
+ This enables support for the Aichi Steel ami306 compass
+ This support is for integration with the MPU3050 or MPU6050 gyroscope
+ device driver. Only one compass can be registered at a time.
+ Specifying more that one compass in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_HMC5883
+ tristate "Honeywell hmc5883"
+ help
+ This enables support for the Honeywell hmc5883 compass
+ This support is for integration with the MPU3050 or MPU6050 gyroscope
+ device driver. Only one compass can be registered at a time.
+ Specifying more that one compass in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_LSM303DLHM
+ tristate "ST lsm303dlh"
+ help
+ This enables support for the ST lsm303dlh compass
+ This support is for integration with the MPU3050 or MPU6050 gyroscope
+ device driver. Only one compass can be registered at a time.
+ Specifying more that one compass in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_MMC314XMS
+ tristate "MEMSIC mmc314xMS"
+ help
+ This enables support for the MEMSIC mmc314xMS compass
+ This support is for integration with the MPU3050 or MPU6050 gyroscope
+ device driver. Only one compass can be registered at a time.
+ Specifying more that one compass in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_YAS529
+ tristate "Yamaha yas529"
+ depends on INPUT_YAS_MAGNETOMETER
+ help
+ This enables support for the Yamaha yas529 compass
+ This support is for integration with the MPU3050 or MPU6050 gyroscope
+ device driver. Only one compass can be registered at a time.
+ Specifying more that one compass in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_YAS530
+ tristate "Yamaha yas530"
+ help
+ This enables support for the Yamaha yas530 compass
+ This support is for integration with the MPU3050 or MPU6050 gyroscope
+ device driver. Only one compass can be registered at a time.
+ Specifying more that one compass in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_HSCDTD002B
+ tristate "Alps hscdtd002b"
+ help
+ This enables support for the Alps hscdtd002b compass
+ This support is for integration with the MPU3050 or MPU6050 gyroscope
+ device driver. Only one compass can be registered at a time.
+ Specifying more that one compass in the board file will result
+ in runtime errors.
+
+config MPU_SENSORS_HSCDTD004A
+ tristate "Alps hscdtd004a"
+ help
+ This enables support for the Alps hscdtd004a compass
+ This support is for integration with the MPU3050 or MPU6050 gyroscope
+ device driver. Only one compass can be registered at a time.
+ Specifying more that one compass in the board file will result
+ in runtime errors.
+
+endif
diff --git a/drivers/misc/inv_mpu/compass/Makefile b/drivers/misc/inv_mpu/compass/Makefile
new file mode 100644
index 0000000..602e9f8
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/Makefile
@@ -0,0 +1,34 @@
+#
+# Compass Slaves MPUxxxx
+#
+obj-$(CONFIG_MPU_SENSORS_AMI30X) += inv_mpu_ami30x.o
+inv_mpu_ami30x-objs += ami30x.o
+
+obj-$(CONFIG_MPU_SENSORS_AMI306) += inv_mpu_ami306.o
+inv_mpu_ami306-objs += ami306.o
+
+obj-$(CONFIG_MPU_SENSORS_HMC5883) += inv_mpu_hmc5883.o
+inv_mpu_hmc5883-objs += hmc5883.o
+
+obj-$(CONFIG_MPU_SENSORS_LSM303DLHM) += inv_mpu_lsm303m.o
+inv_mpu_lsm303m-objs += lsm303m.o
+
+obj-$(CONFIG_MPU_SENSORS_MMC314X) += inv_mpu_mmc314x.o
+inv_mpu_mmc314x-objs += mmc314x.o
+
+obj-$(CONFIG_MPU_SENSORS_YAS529) += inv_mpu_yas529.o
+inv_mpu_yas529-objs += yas529-kernel.o
+
+obj-$(CONFIG_MPU_SENSORS_YAS530) += inv_mpu_yas530.o
+inv_mpu_yas530-objs += yas530.o
+
+obj-$(CONFIG_MPU_SENSORS_HSCDTD002B) += inv_mpu_hscdtd002b.o
+inv_mpu_hscdtd002b-objs += hscdtd002b.o
+
+obj-$(CONFIG_MPU_SENSORS_HSCDTD004A) += inv_mpu_hscdtd004a.o
+inv_mpu_hscdtd004a-objs += hscdtd004a.o
+
+obj-$(CONFIG_MPU_SENSORS_AK8975) += inv_mpu_ak8975.o
+inv_mpu_ak8975-objs += ak8975.o
+
+EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
diff --git a/drivers/misc/inv_mpu/compass/ak8975.c b/drivers/misc/inv_mpu/compass/ak8975.c
new file mode 100644
index 0000000..5c60d49
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ak8975.c
@@ -0,0 +1,500 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup COMPASSDL
+ *
+ * @{
+ * @file ak8975.c
+ * @brief Magnetometer setup and handling methods for the AKM AK8975,
+ * AKM AK8975B, and AKM AK8975C compass devices.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-compass"
+
+/* -------------------------------------------------------------------------- */
+#define AK8975_REG_ST1 (0x02)
+#define AK8975_REG_HXL (0x03)
+#define AK8975_REG_ST2 (0x09)
+
+#define AK8975_REG_CNTL (0x0A)
+#define AK8975_REG_ASAX (0x10)
+#define AK8975_REG_ASAY (0x11)
+#define AK8975_REG_ASAZ (0x12)
+
+#define AK8975_CNTL_MODE_POWER_DOWN (0x00)
+#define AK8975_CNTL_MODE_SINGLE_MEASUREMENT (0x01)
+#define AK8975_CNTL_MODE_FUSE_ROM_ACCESS (0x0f)
+
+/* -------------------------------------------------------------------------- */
+struct ak8975_config {
+ char asa[COMPASS_NUM_AXES]; /* axis sensitivity adjustment */
+};
+
+struct ak8975_private_data {
+ struct ak8975_config init;
+};
+
+/* -------------------------------------------------------------------------- */
+static int ak8975_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ unsigned char serial_data[COMPASS_NUM_AXES];
+
+ struct ak8975_private_data *private_data;
+ private_data = (struct ak8975_private_data *)
+ kzalloc(sizeof(struct ak8975_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ AK8975_REG_CNTL,
+ AK8975_CNTL_MODE_POWER_DOWN);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Wait at least 100us */
+ udelay(100);
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ AK8975_REG_CNTL,
+ AK8975_CNTL_MODE_FUSE_ROM_ACCESS);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Wait at least 200us */
+ udelay(200);
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AK8975_REG_ASAX,
+ COMPASS_NUM_AXES, serial_data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ pdata->private_data = private_data;
+
+ private_data->init.asa[0] = serial_data[0];
+ private_data->init.asa[1] = serial_data[1];
+ private_data->init.asa[2] = serial_data[2];
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ AK8975_REG_CNTL,
+ AK8975_CNTL_MODE_POWER_DOWN);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ udelay(100);
+ return INV_SUCCESS;
+}
+
+static int ak8975_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+static int ak8975_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ AK8975_REG_CNTL,
+ AK8975_CNTL_MODE_POWER_DOWN);
+ msleep(1); /* wait at least 100us */
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+static int ak8975_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ AK8975_REG_CNTL,
+ AK8975_CNTL_MODE_SINGLE_MEASUREMENT);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+static int ak8975_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata, unsigned char *data)
+{
+ unsigned char regs[8];
+ unsigned char *stat = &regs[0];
+ unsigned char *stat2 = &regs[7];
+ int result = INV_SUCCESS;
+ int status = INV_SUCCESS;
+
+ result =
+ inv_serial_read(mlsl_handle, pdata->address, AK8975_REG_ST1,
+ 8, regs);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Always return the data and the status registers */
+ memcpy(data, &regs[1], 6);
+ data[6] = regs[0];
+ data[7] = regs[7];
+
+ /*
+ * ST : data ready -
+ * Measurement has been completed and data is ready to be read.
+ */
+ if (*stat & 0x01)
+ status = INV_SUCCESS;
+
+ /*
+ * ST2 : data error -
+ * occurs when data read is started outside of a readable period;
+ * data read would not be correct.
+ * Valid in continuous measurement mode only.
+ * In single measurement mode this error should not occour but we
+ * stil account for it and return an error, since the data would be
+ * corrupted.
+ * DERR bit is self-clearing when ST2 register is read.
+ */
+ if (*stat2 & 0x04)
+ status = INV_ERROR_COMPASS_DATA_ERROR;
+ /*
+ * ST2 : overflow -
+ * the sum of the absolute values of all axis |X|+|Y|+|Z| < 2400uT.
+ * This is likely to happen in presence of an external magnetic
+ * disturbance; it indicates, the sensor data is incorrect and should
+ * be ignored.
+ * An error is returned.
+ * HOFL bit clears when a new measurement starts.
+ */
+ if (*stat2 & 0x08)
+ status = INV_ERROR_COMPASS_DATA_OVERFLOW;
+ /*
+ * ST : overrun -
+ * the previous sample was not fetched and lost.
+ * Valid in continuous measurement mode only.
+ * In single measurement mode this error should not occour and we
+ * don't consider this condition an error.
+ * DOR bit is self-clearing when ST2 or any meas. data register is
+ * read.
+ */
+ if (*stat & 0x02) {
+ /* status = INV_ERROR_COMPASS_DATA_UNDERFLOW; */
+ status = INV_SUCCESS;
+ }
+
+ /*
+ * trigger next measurement if:
+ * - stat is non zero;
+ * - if stat is zero and stat2 is non zero.
+ * Won't trigger if data is not ready and there was no error.
+ */
+ if (*stat != 0x00 || *stat2 != 0x00) {
+ result = inv_serial_single_write(
+ mlsl_handle, pdata->address,
+ AK8975_REG_CNTL, AK8975_CNTL_MODE_SINGLE_MEASUREMENT);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ return status;
+}
+
+static int ak8975_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ int result;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_WRITE_REGISTERS:
+ result = inv_serial_write(mlsl_handle, pdata->address,
+ data->len,
+ (unsigned char *)data->data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ break;
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static int ak8975_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ struct ak8975_private_data *private_data = pdata->private_data;
+ int result;
+ if (!data->data)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ switch (data->key) {
+ case MPU_SLAVE_READ_REGISTERS:
+ {
+ unsigned char *serial_data =
+ (unsigned char *)data->data;
+ result =
+ inv_serial_read(mlsl_handle, pdata->address,
+ serial_data[0], data->len - 1,
+ &serial_data[1]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ break;
+ }
+ case MPU_SLAVE_READ_SCALE:
+ {
+ unsigned char *serial_data =
+ (unsigned char *)data->data;
+ serial_data[0] = private_data->init.asa[0];
+ serial_data[1] = private_data->init.asa[1];
+ serial_data[2] = private_data->init.asa[2];
+ result = INV_SUCCESS;
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ break;
+ }
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) = 0;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) = 8000;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static struct ext_slave_read_trigger ak8975_read_trigger = {
+ /*.reg = */ 0x0A,
+ /*.value = */ 0x01
+};
+
+static struct ext_slave_descr ak8975_descr = {
+ .init = ak8975_init,
+ .exit = ak8975_exit,
+ .suspend = ak8975_suspend,
+ .resume = ak8975_resume,
+ .read = ak8975_read,
+ .config = ak8975_config,
+ .get_config = ak8975_get_config,
+ .name = "ak8975",
+ .type = EXT_SLAVE_TYPE_COMPASS,
+ .id = COMPASS_ID_AK8975,
+ .read_reg = 0x01,
+ .read_len = 9,
+ .endian = EXT_SLAVE_LITTLE_ENDIAN,
+ .range = {9830, 4000},
+ .trigger = &ak8975_read_trigger,
+};
+
+static
+struct ext_slave_descr *ak8975_get_slave_descr(void)
+{
+ return &ak8975_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct ak8975_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int ak8975_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct ak8975_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ ak8975_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int ak8975_mod_remove(struct i2c_client *client)
+{
+ struct ak8975_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ ak8975_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id ak8975_mod_id[] = {
+ { "ak8975", COMPASS_ID_AK8975 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ak8975_mod_id);
+
+static struct i2c_driver ak8975_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = ak8975_mod_probe,
+ .remove = ak8975_mod_remove,
+ .id_table = ak8975_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ak8975_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init ak8975_mod_init(void)
+{
+ int res = i2c_add_driver(&ak8975_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "ak8975_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit ak8975_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&ak8975_mod_driver);
+}
+
+module_init(ak8975_mod_init);
+module_exit(ak8975_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate AK8975 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ak8975_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/compass/ami306.c b/drivers/misc/inv_mpu/compass/ami306.c
new file mode 100644
index 0000000..ecf4541
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ami306.c
@@ -0,0 +1,1020 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup COMPASSDL
+ *
+ * @{
+ * @file ami306.c
+ * @brief Magnetometer setup and handling methods for Aichi AMI306
+ * compass.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include "ami_hw.h"
+#include "ami_sensor_def.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-compass"
+
+/* -------------------------------------------------------------------------- */
+#define AMI306_REG_DATAX (0x10)
+#define AMI306_REG_STAT1 (0x18)
+#define AMI306_REG_CNTL1 (0x1B)
+#define AMI306_REG_CNTL2 (0x1C)
+#define AMI306_REG_CNTL3 (0x1D)
+#define AMI306_REG_CNTL4_1 (0x5C)
+#define AMI306_REG_CNTL4_2 (0x5D)
+
+#define AMI306_BIT_CNTL1_PC1 (0x80)
+#define AMI306_BIT_CNTL1_ODR1 (0x10)
+#define AMI306_BIT_CNTL1_FS1 (0x02)
+
+#define AMI306_BIT_CNTL2_IEN (0x10)
+#define AMI306_BIT_CNTL2_DREN (0x08)
+#define AMI306_BIT_CNTL2_DRP (0x04)
+#define AMI306_BIT_CNTL3_F0RCE (0x40)
+
+#define AMI_FINE_MAX (96)
+#define AMI_STANDARD_OFFSET (0x800)
+#define AMI_GAIN_COR_DEFAULT (1000)
+
+/* -------------------------------------------------------------------------- */
+struct ami306_private_data {
+ int isstandby;
+ unsigned char fine[3];
+ AMI_SENSOR_PARAMETOR param;
+ AMI_WIN_PARAMETER win;
+};
+
+/* -------------------------------------------------------------------------- */
+static inline unsigned short little_u8_to_u16(unsigned char *p_u8)
+{
+ return p_u8[0] | (p_u8[1] << 8);
+}
+
+static int ami306_set_bits8(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ unsigned char reg, unsigned char bits)
+{
+ int result;
+ unsigned char buf;
+
+ result = inv_serial_read(mlsl_handle, pdata->address, reg, 1, &buf);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ buf |= bits;
+ result = inv_serial_single_write(mlsl_handle, pdata->address, reg, buf);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+static int ami306_wait_data_ready(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ unsigned long usecs, unsigned long times)
+{
+ int result = 0;
+ unsigned char buf;
+
+ for (; 0 < times; --times) {
+ udelay(usecs);
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI_REG_STA1, 1, &buf);
+ if (buf & AMI_STA1_DRDY_BIT)
+ return 0;
+ else if (buf & AMI_STA1_DOR_BIT)
+ return INV_ERROR_COMPASS_DATA_OVERFLOW;
+ }
+ return INV_ERROR_COMPASS_DATA_NOT_READY;
+}
+
+static int ami306_read_raw_data(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ short dat[3])
+{
+ int result;
+ unsigned char buf[6];
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI_REG_DATAX, sizeof(buf), buf);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dat[0] = little_u8_to_u16(&buf[0]);
+ dat[1] = little_u8_to_u16(&buf[2]);
+ dat[2] = little_u8_to_u16(&buf[4]);
+ return result;
+}
+
+#define AMI_WAIT_DATAREADY_RETRY 3 /* retry times */
+#define AMI_DRDYWAIT 800 /* u(micro) sec */
+static int ami306_force_mesurement(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ short ver[3])
+{
+ int result;
+ int status;
+ result = ami306_set_bits8(mlsl_handle, pdata,
+ AMI_REG_CTRL3, AMI_CTRL3_FORCE_BIT);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = ami306_wait_data_ready(mlsl_handle, pdata,
+ AMI_DRDYWAIT, AMI_WAIT_DATAREADY_RETRY);
+ if (result && result != INV_ERROR_COMPASS_DATA_OVERFLOW) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* READ DATA X,Y,Z */
+ status = ami306_read_raw_data(mlsl_handle, pdata, ver);
+ if (status) {
+ LOG_RESULT_LOCATION(status);
+ return status;
+ }
+
+ return result;
+}
+
+static int ami306_mea(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata, short val[3])
+{
+ int result = ami306_force_mesurement(mlsl_handle, pdata, val);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ val[0] += AMI_STANDARD_OFFSET;
+ val[1] += AMI_STANDARD_OFFSET;
+ val[2] += AMI_STANDARD_OFFSET;
+ return result;
+}
+
+static int ami306_write_offset(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *fine)
+{
+ int result = 0;
+ unsigned char dat[3];
+ dat[0] = AMI_REG_OFFX;
+ dat[1] = 0x7f & fine[0];
+ dat[2] = 0;
+ result = inv_serial_write(mlsl_handle, pdata->address,
+ sizeof(dat), dat);
+ dat[0] = AMI_REG_OFFY;
+ dat[1] = 0x7f & fine[1];
+ dat[2] = 0;
+ result = inv_serial_write(mlsl_handle, pdata->address,
+ sizeof(dat), dat);
+ dat[0] = AMI_REG_OFFZ;
+ dat[1] = 0x7f & fine[2];
+ dat[2] = 0;
+ result = inv_serial_write(mlsl_handle, pdata->address,
+ sizeof(dat), dat);
+ return result;
+}
+
+static int ami306_start_sensor(void *mlsl_handle,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = 0;
+ unsigned char buf[3];
+ struct ami306_private_data *private_data = pdata->private_data;
+
+ /* Step 1 */
+ result = ami306_set_bits8(mlsl_handle, pdata,
+ AMI_REG_CTRL1,
+ AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Step 2 */
+ result = ami306_set_bits8(mlsl_handle, pdata,
+ AMI_REG_CTRL2, AMI_CTRL2_DREN);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Step 3 */
+ buf[0] = AMI_REG_CTRL4;
+ buf[1] = AMI_CTRL4_HS & 0xFF;
+ buf[2] = (AMI_CTRL4_HS >> 8) & 0xFF;
+ result = inv_serial_write(mlsl_handle, pdata->address,
+ sizeof(buf), buf);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Step 4 */
+ result = ami306_write_offset(mlsl_handle, pdata, private_data->fine);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+/**
+ * This function does this.
+ *
+ * @param mlsl_handle this param is this.
+ * @param slave
+ * @param pdata
+ *
+ * @return INV_SUCCESS or non-zero error code.
+ */
+static int ami306_read_param(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = 0;
+ unsigned char regs[12];
+ struct ami306_private_data *private_data = pdata->private_data;
+ AMI_SENSOR_PARAMETOR *param = &private_data->param;
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI_REG_SENX, sizeof(regs), regs);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Little endian 16 bit registers */
+ param->m_gain.x = little_u8_to_u16(&regs[0]);
+ param->m_gain.y = little_u8_to_u16(&regs[2]);
+ param->m_gain.z = little_u8_to_u16(&regs[4]);
+
+ param->m_interference.xy = regs[7];
+ param->m_interference.xz = regs[6];
+ param->m_interference.yx = regs[9];
+ param->m_interference.yz = regs[8];
+ param->m_interference.zx = regs[11];
+ param->m_interference.zy = regs[10];
+
+ param->m_offset.x = AMI_STANDARD_OFFSET;
+ param->m_offset.y = AMI_STANDARD_OFFSET;
+ param->m_offset.z = AMI_STANDARD_OFFSET;
+
+ param->m_gain_cor.x = AMI_GAIN_COR_DEFAULT;
+ param->m_gain_cor.y = AMI_GAIN_COR_DEFAULT;
+ param->m_gain_cor.z = AMI_GAIN_COR_DEFAULT;
+
+ return result;
+}
+
+static int ami306_initial_b0_adjust(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ unsigned char fine[3] = { 0 };
+ short data[3];
+ int diff[3] = { 0x7fff, 0x7fff, 0x7fff };
+ int fn = 0;
+ int ax = 0;
+ unsigned char buf[3];
+ struct ami306_private_data *private_data = pdata->private_data;
+
+ result = ami306_set_bits8(mlsl_handle, pdata,
+ AMI_REG_CTRL2, AMI_CTRL2_DREN);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ buf[0] = AMI_REG_CTRL4;
+ buf[1] = AMI_CTRL4_HS & 0xFF;
+ buf[2] = (AMI_CTRL4_HS >> 8) & 0xFF;
+ result = inv_serial_write(mlsl_handle, pdata->address,
+ sizeof(buf), buf);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ for (fn = 0; fn < AMI_FINE_MAX; ++fn) { /* fine 0 -> 95 */
+ fine[0] = fine[1] = fine[2] = fn;
+ result = ami306_write_offset(mlsl_handle, pdata, fine);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = ami306_force_mesurement(mlsl_handle, pdata, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ MPL_LOGV("[%d] x:%-5d y:%-5d z:%-5d\n",
+ fn, data[0], data[1], data[2]);
+
+ for (ax = 0; ax < 3; ax++) {
+ /* search point most close to zero. */
+ if (diff[ax] > abs(data[ax])) {
+ private_data->fine[ax] = fn;
+ diff[ax] = abs(data[ax]);
+ }
+ }
+ }
+ MPL_LOGV("fine x:%-5d y:%-5d z:%-5d\n",
+ private_data->fine[0], private_data->fine[1],
+ private_data->fine[2]);
+
+ result = ami306_write_offset(mlsl_handle, pdata, private_data->fine);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Software Reset */
+ result = ami306_set_bits8(mlsl_handle, pdata,
+ AMI_REG_CTRL3, AMI_CTRL3_SRST_BIT);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+#define SEH_RANGE_MIN 100
+#define SEH_RANGE_MAX 3950
+static int ami306_search_offset(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ int axis;
+ unsigned char regs[6];
+ unsigned char run_flg[3] = { 1, 1, 1 };
+ unsigned char fine[3];
+ unsigned char win_range_fine[3];
+ unsigned short fine_output[3];
+ short val[3];
+ unsigned short cnt[3] = { 0 };
+ struct ami306_private_data *private_data = pdata->private_data;
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI_FINEOUTPUT_X, sizeof(regs), regs);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ fine_output[0] = little_u8_to_u16(&regs[0]);
+ fine_output[1] = little_u8_to_u16(&regs[2]);
+ fine_output[2] = little_u8_to_u16(&regs[4]);
+
+ for (axis = 0; axis < 3; ++axis) {
+ if (fine_output[axis] == 0) {
+ MPL_LOGV("error fine_output %d axis:%d\n",
+ __LINE__, axis);
+ return -1;
+ }
+ /* fines per a window */
+ win_range_fine[axis] = (SEH_RANGE_MAX - SEH_RANGE_MIN)
+ / fine_output[axis];
+ }
+
+ /* get current fine */
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI_REG_OFFX, 2, &regs[0]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI_REG_OFFY, 2, &regs[2]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI_REG_OFFZ, 2, &regs[4]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ fine[0] = (unsigned char)(regs[0] & 0x7f);
+ fine[1] = (unsigned char)(regs[2] & 0x7f);
+ fine[2] = (unsigned char)(regs[4] & 0x7f);
+
+ while (run_flg[0] == 1 || run_flg[1] == 1 || run_flg[2] == 1) {
+
+ result = ami306_mea(mlsl_handle, pdata, val);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ MPL_LOGV("val x:%-5d y:%-5d z:%-5d\n", val[0], val[1], val[2]);
+ MPL_LOGV("now fine x:%-5d y:%-5d z:%-5d\n",
+ fine[0], fine[1], fine[2]);
+
+ for (axis = 0; axis < 3; ++axis) {
+ if (axis == 0) { /* X-axis is reversed */
+ val[axis] = 0x0FFF & ~val[axis];
+ }
+ if (val[axis] < SEH_RANGE_MIN) {
+ /* At the case of less low limmit. */
+ fine[axis] -= win_range_fine[axis];
+ MPL_LOGV("min : fine=%d diff=%d\n",
+ fine[axis], win_range_fine[axis]);
+ }
+ if (val[axis] > SEH_RANGE_MAX) {
+ /* At the case of over high limmit. */
+ fine[axis] += win_range_fine[axis];
+ MPL_LOGV("max : fine=%d diff=%d\n",
+ fine[axis], win_range_fine[axis]);
+ }
+ if (SEH_RANGE_MIN <= val[axis] &&
+ val[axis] <= SEH_RANGE_MAX) {
+ /* In the current window. */
+ int diff_fine =
+ (val[axis] - AMI_STANDARD_OFFSET) /
+ fine_output[axis];
+ fine[axis] += diff_fine;
+ run_flg[axis] = 0;
+ MPL_LOGV("mid : fine=%d diff=%d\n",
+ fine[axis], diff_fine);
+ }
+
+ if (!(0 <= fine[axis] && fine[axis] < AMI_FINE_MAX)) {
+ MPL_LOGE("fine err :%d\n", cnt[axis]);
+ goto out;
+ }
+ if (cnt[axis] > 3) {
+ MPL_LOGE("cnt err :%d\n", cnt[axis]);
+ goto out;
+ }
+ cnt[axis]++;
+ }
+ MPL_LOGV("new fine x:%-5d y:%-5d z:%-5d\n",
+ fine[0], fine[1], fine[2]);
+
+ /* set current fine */
+ result = ami306_write_offset(mlsl_handle, pdata, fine);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ memcpy(private_data->fine, fine, sizeof(fine));
+ out:
+ result = ami306_set_bits8(mlsl_handle, pdata,
+ AMI_REG_CTRL3, AMI_CTRL3_SRST_BIT);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ udelay(250 + 50);
+ return 0;
+}
+
+static int ami306_read_win(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = 0;
+ unsigned char regs[6];
+ struct ami306_private_data *private_data = pdata->private_data;
+ AMI_WIN_PARAMETER *win = &private_data->win;
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI_REG_OFFOTPX, sizeof(regs), regs);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ win->m_0Gauss_fine.x = (unsigned char)(regs[0] & 0x7f);
+ win->m_0Gauss_fine.y = (unsigned char)(regs[2] & 0x7f);
+ win->m_0Gauss_fine.z = (unsigned char)(regs[4] & 0x7f);
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI_REG_OFFX, 2, &regs[0]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI_REG_OFFY, 2, &regs[2]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI_REG_OFFZ, 2, &regs[4]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ win->m_fine.x = (unsigned char)(regs[0] & 0x7f);
+ win->m_fine.y = (unsigned char)(regs[2] & 0x7f);
+ win->m_fine.z = (unsigned char)(regs[4] & 0x7f);
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI_FINEOUTPUT_X, sizeof(regs), regs);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ win->m_fine_output.x = little_u8_to_u16(&regs[0]);
+ win->m_fine_output.y = little_u8_to_u16(&regs[2]);
+ win->m_fine_output.z = little_u8_to_u16(&regs[4]);
+
+ return result;
+}
+
+static int ami306_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ unsigned char reg;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ AMI306_REG_CNTL1, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ reg &= ~(AMI306_BIT_CNTL1_PC1 | AMI306_BIT_CNTL1_FS1);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ AMI306_REG_CNTL1, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+static int ami306_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ unsigned char regs[] = {
+ AMI306_REG_CNTL4_1,
+ 0x7E,
+ 0xA0
+ };
+ /* Step1. Set CNTL1 reg to power model active (Write CNTL1:PC1=1) */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ AMI306_REG_CNTL1,
+ AMI306_BIT_CNTL1_PC1 |
+ AMI306_BIT_CNTL1_FS1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Step2. Set CNTL2 reg to DRDY active high and enabled
+ (Write CNTL2:DREN=1) */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ AMI306_REG_CNTL2,
+ AMI306_BIT_CNTL2_DREN |
+ AMI306_BIT_CNTL2_DRP);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Step3. Set CNTL4 reg to for measurement speed: Write CNTL4, 0xA07E */
+ result = inv_serial_write(mlsl_handle, pdata->address,
+ ARRAY_SIZE(regs), regs);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Step4. skipped */
+
+ /* Step5. Set CNTL3 reg to forced measurement period
+ (Write CNTL3:FORCE=1) */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ AMI306_REG_CNTL3,
+ AMI306_BIT_CNTL3_F0RCE);
+
+ return result;
+}
+
+static int ami306_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result = INV_SUCCESS;
+ int ii;
+ short val[COMPASS_NUM_AXES];
+
+ result = ami306_mea(mlsl_handle, pdata, val);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ for (ii = 0; ii < COMPASS_NUM_AXES; ii++) {
+ val[ii] -= AMI_STANDARD_OFFSET;
+ data[2 * ii] = val[ii] & 0xFF;
+ data[(2 * ii) + 1] = (val[ii] >> 8) & 0xFF;
+ }
+ return result;
+}
+
+static int ami306_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ struct ami306_private_data *private_data;
+ private_data = (struct ami306_private_data *)
+ kzalloc(sizeof(struct ami306_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ pdata->private_data = private_data;
+ result = ami306_set_bits8(mlsl_handle, pdata,
+ AMI_REG_CTRL1,
+ AMI_CTRL1_PC1 | AMI_CTRL1_FS1_FORCE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Read Parameters */
+ result = ami306_read_param(mlsl_handle, slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Read Window */
+ result = ami306_initial_b0_adjust(mlsl_handle, slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = ami306_start_sensor(mlsl_handle, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = ami306_read_win(mlsl_handle, slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ AMI306_REG_CNTL1, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return INV_SUCCESS;
+}
+
+static int ami306_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+static int ami306_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ if (!data->data) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ switch (data->key) {
+ case MPU_SLAVE_PARAM:
+ case MPU_SLAVE_WINDOW:
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ default:
+ LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static int ami306_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ int result;
+ struct ami306_private_data *private_data = pdata->private_data;
+ if (!data->data) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ switch (data->key) {
+ case MPU_SLAVE_PARAM:
+ if (sizeof(AMI_SENSOR_PARAMETOR) > data->len) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ if (data->apply) {
+ result = ami306_read_param(mlsl_handle, slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ memcpy(data->data, &private_data->param,
+ sizeof(AMI_SENSOR_PARAMETOR));
+ break;
+ case MPU_SLAVE_WINDOW:
+ if (sizeof(AMI_WIN_PARAMETER) > data->len) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ if (data->apply) {
+ result = ami306_read_win(mlsl_handle, slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ memcpy(data->data, &private_data->win,
+ sizeof(AMI_WIN_PARAMETER));
+ break;
+ case MPU_SLAVE_SEARCHOFFSET:
+ if (sizeof(AMI_WIN_PARAMETER) > data->len) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ if (data->apply) {
+ result = ami306_search_offset(mlsl_handle,
+ slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Start sensor */
+ result = ami306_start_sensor(mlsl_handle, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = ami306_read_win(mlsl_handle, slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ memcpy(data->data, &private_data->win,
+ sizeof(AMI_WIN_PARAMETER));
+ break;
+ case MPU_SLAVE_READWINPARAMS:
+ if (sizeof(AMI_WIN_PARAMETER) > data->len) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ if (data->apply) {
+ result = ami306_initial_b0_adjust(mlsl_handle,
+ slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Start sensor */
+ result = ami306_start_sensor(mlsl_handle, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = ami306_read_win(mlsl_handle, slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ memcpy(data->data, &private_data->win,
+ sizeof(AMI_WIN_PARAMETER));
+ break;
+ case MPU_SLAVE_CONFIG_ODR_SUSPEND:
+ (*(unsigned long *)data->data) = 0;
+ break;
+ case MPU_SLAVE_CONFIG_ODR_RESUME:
+ (*(unsigned long *)data->data) = 50000;
+ break;
+ case MPU_SLAVE_CONFIG_FSR_SUSPEND:
+ case MPU_SLAVE_CONFIG_FSR_RESUME:
+ case MPU_SLAVE_CONFIG_MOT_THS:
+ case MPU_SLAVE_CONFIG_NMOT_THS:
+ case MPU_SLAVE_CONFIG_MOT_DUR:
+ case MPU_SLAVE_CONFIG_NMOT_DUR:
+ case MPU_SLAVE_CONFIG_IRQ_SUSPEND:
+ case MPU_SLAVE_CONFIG_IRQ_RESUME:
+ case MPU_SLAVE_READ_SCALE:
+ default:
+ LOG_RESULT_LOCATION(INV_ERROR_FEATURE_NOT_IMPLEMENTED);
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ };
+
+ return INV_SUCCESS;
+}
+
+static struct ext_slave_read_trigger ami306_read_trigger = {
+ /*.reg = */ AMI_REG_CTRL3,
+ /*.value = */ AMI_CTRL3_FORCE_BIT
+};
+
+static struct ext_slave_descr ami306_descr = {
+ .init = ami306_init,
+ .exit = ami306_exit,
+ .suspend = ami306_suspend,
+ .resume = ami306_resume,
+ .read = ami306_read,
+ .config = ami306_config,
+ .get_config = ami306_get_config,
+ .name = "ami306",
+ .type = EXT_SLAVE_TYPE_COMPASS,
+ .id = COMPASS_ID_AMI306,
+ .read_reg = 0x0E,
+ .read_len = 13,
+ .endian = EXT_SLAVE_LITTLE_ENDIAN,
+ .range = {5461, 3333},
+ .trigger = &ami306_read_trigger,
+};
+
+static
+struct ext_slave_descr *ami306_get_slave_descr(void)
+{
+ return &ami306_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct ami306_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int ami306_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct ami306_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ ami306_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int ami306_mod_remove(struct i2c_client *client)
+{
+ struct ami306_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ ami306_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id ami306_mod_id[] = {
+ { "ami306", COMPASS_ID_AMI306 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ami306_mod_id);
+
+static struct i2c_driver ami306_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = ami306_mod_probe,
+ .remove = ami306_mod_remove,
+ .id_table = ami306_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ami306_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init ami306_mod_init(void)
+{
+ int res = i2c_add_driver(&ami306_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "ami306_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit ami306_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&ami306_mod_driver);
+}
+
+module_init(ami306_mod_init);
+module_exit(ami306_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate AMI306 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ami306_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/compass/ami30x.c b/drivers/misc/inv_mpu/compass/ami30x.c
new file mode 100644
index 0000000..4cb0daa
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ami30x.c
@@ -0,0 +1,308 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup COMPASSDL
+ *
+ * @{
+ * @file ami30x.c
+ * @brief Magnetometer setup and handling methods for Aichi AMI304
+ * and AMI305 compass devices.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-compass"
+
+/* -------------------------------------------------------------------------- */
+#define AMI30X_REG_DATAX (0x10)
+#define AMI30X_REG_STAT1 (0x18)
+#define AMI30X_REG_CNTL1 (0x1B)
+#define AMI30X_REG_CNTL2 (0x1C)
+#define AMI30X_REG_CNTL3 (0x1D)
+
+#define AMI30X_BIT_CNTL1_PC1 (0x80)
+#define AMI30X_BIT_CNTL1_ODR1 (0x10)
+#define AMI30X_BIT_CNTL1_FS1 (0x02)
+
+#define AMI30X_BIT_CNTL2_IEN (0x10)
+#define AMI30X_BIT_CNTL2_DREN (0x08)
+#define AMI30X_BIT_CNTL2_DRP (0x04)
+#define AMI30X_BIT_CNTL3_F0RCE (0x40)
+
+/* -------------------------------------------------------------------------- */
+static int ami30x_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ unsigned char reg;
+ result =
+ inv_serial_read(mlsl_handle, pdata->address, AMI30X_REG_CNTL1,
+ 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ reg &= ~(AMI30X_BIT_CNTL1_PC1 | AMI30X_BIT_CNTL1_FS1);
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ AMI30X_REG_CNTL1, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+static int ami30x_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ /* Set CNTL1 reg to power model active */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ AMI30X_REG_CNTL1,
+ AMI30X_BIT_CNTL1_PC1 |
+ AMI30X_BIT_CNTL1_FS1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Set CNTL2 reg to DRDY active high and enabled */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ AMI30X_REG_CNTL2,
+ AMI30X_BIT_CNTL2_DREN |
+ AMI30X_BIT_CNTL2_DRP);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Set CNTL3 reg to forced measurement period */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ AMI30X_REG_CNTL3, AMI30X_BIT_CNTL3_F0RCE);
+
+ return result;
+}
+
+static int ami30x_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ unsigned char stat;
+ int result = INV_SUCCESS;
+
+ /* Read status reg and check if data ready (DRDY) */
+ result =
+ inv_serial_read(mlsl_handle, pdata->address, AMI30X_REG_STAT1,
+ 1, &stat);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (stat & 0x40) {
+ result =
+ inv_serial_read(mlsl_handle, pdata->address,
+ AMI30X_REG_DATAX, 6, (unsigned char *)data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* start another measurement */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ AMI30X_REG_CNTL3,
+ AMI30X_BIT_CNTL3_F0RCE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return INV_SUCCESS;
+ }
+
+ return INV_ERROR_COMPASS_DATA_NOT_READY;
+}
+
+
+/* For AMI305,the range field needs to be modified to {9830.4f} */
+static struct ext_slave_descr ami30x_descr = {
+ .init = NULL,
+ .exit = NULL,
+ .suspend = ami30x_suspend,
+ .resume = ami30x_resume,
+ .read = ami30x_read,
+ .config = NULL,
+ .get_config = NULL,
+ .name = "ami30x",
+ .type = EXT_SLAVE_TYPE_COMPASS,
+ .id = COMPASS_ID_AMI30X,
+ .read_reg = 0x06,
+ .read_len = 6,
+ .endian = EXT_SLAVE_LITTLE_ENDIAN,
+ .range = {5461, 3333},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *ami30x_get_slave_descr(void)
+{
+ return &ami30x_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct ami30x_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int ami30x_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct ami30x_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ ami30x_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int ami30x_mod_remove(struct i2c_client *client)
+{
+ struct ami30x_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ ami30x_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id ami30x_mod_id[] = {
+ { "ami30x", COMPASS_ID_AMI30X },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, ami30x_mod_id);
+
+static struct i2c_driver ami30x_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = ami30x_mod_probe,
+ .remove = ami30x_mod_remove,
+ .id_table = ami30x_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "ami30x_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init ami30x_mod_init(void)
+{
+ int res = i2c_add_driver(&ami30x_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "ami30x_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit ami30x_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&ami30x_mod_driver);
+}
+
+module_init(ami30x_mod_init);
+module_exit(ami30x_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate AMI30X sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ami30x_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/compass/ami_hw.h b/drivers/misc/inv_mpu/compass/ami_hw.h
new file mode 100644
index 0000000..995355c
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ami_hw.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2010 Information System Products Co.,Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AMI_HW_H
+#define AMI_HW_H
+
+/* N of /dev/i2c-N. */
+#define AMI_I2C_BUS_NUM 2
+
+#ifdef AMI304_MODEL
+/* new Addr=0x0E(Low), old Addr=0x0F(High) */
+#define AMI_I2C_ADDRESS 0x0F
+#else
+/* new Addr=0x0E(Low), old Addr=0x0F(High) */
+#define AMI_I2C_ADDRESS 0x0E
+#endif
+
+/* AMI-Sensor Internal Register Address
+ *(Please refer to AMI-Sensor Specifications)
+ */
+#define AMI_MOREINFO_CMDCODE 0x0d
+#define AMI_WHOIAM_CMDCODE 0x0f
+#define AMI_REG_DATAX 0x10
+#define AMI_REG_DATAY 0x12
+#define AMI_REG_DATAZ 0x14
+#define AMI_REG_STA1 0x18
+#define AMI_REG_CTRL1 0x1b
+#define AMI_REG_CTRL2 0x1c
+#define AMI_REG_CTRL3 0x1d
+#define AMI_REG_B0X 0x20
+#define AMI_REG_B0Y 0x22
+#define AMI_REG_B0Z 0x24
+#define AMI_REG_CTRL5 0x40
+#define AMI_REG_CTRL4 0x5c
+#define AMI_REG_TEMP 0x60
+#define AMI_REG_DELAYX 0x68
+#define AMI_REG_DELAYY 0x6e
+#define AMI_REG_DELAYZ 0x74
+#define AMI_REG_OFFX 0x6c
+#define AMI_REG_OFFY 0x72
+#define AMI_REG_OFFZ 0x78
+#define AMI_FINEOUTPUT_X 0x90
+#define AMI_FINEOUTPUT_Y 0x92
+#define AMI_FINEOUTPUT_Z 0x94
+#define AMI_REG_SENX 0x96
+#define AMI_REG_SENY 0x98
+#define AMI_REG_SENZ 0x9a
+#define AMI_REG_GAINX 0x9c
+#define AMI_REG_GAINY 0x9e
+#define AMI_REG_GAINZ 0xa0
+#define AMI_GETVERSION_CMDCODE 0xe8
+#define AMI_SERIALNUMBER_CMDCODE 0xea
+#define AMI_REG_B0OTPX 0xa2
+#define AMI_REG_B0OTPY 0xb8
+#define AMI_REG_B0OTPZ 0xce
+#define AMI_REG_OFFOTPX 0xf8
+#define AMI_REG_OFFOTPY 0xfa
+#define AMI_REG_OFFOTPZ 0xfc
+
+/* AMI-Sensor Control Bit (Please refer to AMI-Sensor Specifications) */
+#define AMI_CTRL1_PC1 0x80
+#define AMI_CTRL1_FS1_FORCE 0x02
+#define AMI_CTRL1_ODR1 0x10
+#define AMI_CTRL2_DREN 0x08
+#define AMI_CTRL2_DRP 0x04
+#define AMI_CTRL3_FORCE_BIT 0x40
+#define AMI_CTRL3_B0_LO_BIT 0x10
+#define AMI_CTRL3_SRST_BIT 0x80
+#define AMI_CTRL4_HS 0xa07e
+#define AMI_CTRL4_AB 0x0001
+#define AMI_STA1_DRDY_BIT 0x40
+#define AMI_STA1_DOR_BIT 0x20
+
+#endif
diff --git a/drivers/misc/inv_mpu/compass/ami_sensor_def.h b/drivers/misc/inv_mpu/compass/ami_sensor_def.h
new file mode 100644
index 0000000..454d5fa
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/ami_sensor_def.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2010 Information System Products Co.,Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Definitions for ami306 compass chip.
+ */
+#ifndef AMI_SENSOR_DEF_H
+#define AMI_SENSOR_DEF_H
+
+/*********************************************************************
+ Constant
+ *********************************************************************/
+#define AMI_OK 0x00 /**< Normal */
+#define AMI_PARAM_ERR 0x01 /**< Parameter Error */
+#define AMI_SEQ_ERR 0x02 /**< Squence Error */
+#define AMI_SYSTEM_ERR 0x10 /**< System Error */
+#define AMI_BLOCK_ERR 0x20 /**< Block Error */
+#define AMI_ERROR 0x99 /**< other Error */
+
+/*********************************************************************
+ Type definition
+ *********************************************************************/
+typedef signed char ami_sint8; /**< signed char */
+typedef unsigned char ami_uint8; /**< unsigned char */
+typedef signed short ami_sint16; /**< signed short */
+typedef unsigned short ami_uint16; /**< unsigned short */
+typedef signed long ami_sint32; /**< signed long */
+typedef unsigned long ami_uint32; /**< unsigned long */
+typedef double ami_double; /**< double */
+
+/*********************************************************************
+ Struct definition
+ *********************************************************************/
+/** axis sensitivity(gain) calibration parameter information */
+typedef struct {
+ ami_sint16 x; /**< X-axis */
+ ami_sint16 y; /**< Y-axis */
+ ami_sint16 z; /**< Z-axis */
+} AMI_VECTOR3D;
+
+/** axis interference information */
+typedef struct {
+ ami_sint16 xy; /**< Y-axis magnetic field for X-axis correction value */
+ ami_sint16 xz; /**< Z-axis magnetic field for X-axis correction value */
+ ami_sint16 yx; /**< X-axis magnetic field for Y-axis correction value */
+ ami_sint16 yz; /**< Z-axis magnetic field for Y-axis correction value */
+ ami_sint16 zx; /**< X-axis magnetic field for Z-axis correction value */
+ ami_sint16 zy; /**< Y-axis magnetic field for Z-axis correction value */
+} AMI_INTERFERENCE;
+
+/** sensor calibration Parameter information */
+typedef struct {
+ AMI_VECTOR3D m_gain; /**< geomagnetic field sensor gain */
+
+ /** geomagnetic field sensor gain correction parameter */
+ AMI_VECTOR3D m_gain_cor;
+ AMI_VECTOR3D m_offset; /**< geomagnetic field sensor offset */
+ /** geomagnetic field sensor axis interference parameter */
+ AMI_INTERFERENCE m_interference;
+#ifdef AMI_6AXIS
+ AMI_VECTOR3D a_gain; /**< acceleration sensor gain */
+ AMI_VECTOR3D a_offset; /**< acceleration sensor offset */
+#endif
+} AMI_SENSOR_PARAMETOR;
+
+/** G2-Sensor measurement value (voltage ADC value ) */
+typedef struct {
+ /** geomagnetic field sensor measurement X-axis value
+ (mounted position/direction reference) */
+ ami_uint16 mx;
+ /** geomagnetic field sensor measurement Y-axis value
+ (mounted position/direction reference) */
+ ami_uint16 my;
+ /** geomagnetic field sensor measurement Z-axis value
+ (mounted position/direction reference) */
+ ami_uint16 mz;
+#ifdef AMI_6AXIS
+ /** acceleration sensor measurement X-axis value
+ (mounted position/direction reference) */
+ ami_uint16 ax;
+ /** acceleration sensor measurement Y-axis value
+ (mounted position/direction reference) */
+ ami_uint16 ay;
+ /** acceleration sensor measurement Z-axis value
+ (mounted position/direction reference) */
+ ami_uint16 az;
+#endif
+ /** temperature sensor measurement value */
+ ami_uint16 temperature;
+} AMI_SENSOR_RAWVALUE;
+
+/** Window function Parameter information */
+typedef struct {
+ AMI_VECTOR3D m_fine; /**< current fine value */
+ AMI_VECTOR3D m_fine_output; /**< change per 1coarse */
+ AMI_VECTOR3D m_0Gauss_fine; /**< fine value at zero gauss */
+#ifdef AMI304
+ AMI_VECTOR3D m_b0; /**< current b0 value */
+ AMI_VECTOR3D m_coar; /**< current coarse value */
+ AMI_VECTOR3D m_coar_output; /**< change per 1fine */
+ AMI_VECTOR3D m_0Gauss_coar; /**< coarse value at zero gauss */
+ AMI_VECTOR3D m_delay; /**< delay value */
+#endif
+} AMI_WIN_PARAMETER;
+
+/** AMI chip information ex) 1)model 2)s/n 3)ver 4)more info in the chip */
+typedef struct {
+ ami_uint16 info; /* INFO 0x0d/0x0e reg. */
+ ami_uint16 ver; /* VER 0xe8/0xe9 reg. */
+ ami_uint16 sn; /* SN 0xea/0xeb reg. */
+ ami_uint8 wia; /* WIA 0x0f reg. */
+} AMI_CHIPINFO;
+
+/** AMI Driver Information */
+typedef struct {
+ ami_uint8 remarks[40 + 1]; /* Some Information */
+ ami_uint8 datetime[30 + 1]; /* compiled date&time */
+ ami_uint8 ver_major; /* major version */
+ ami_uint8 ver_middle; /* middle.. */
+ ami_uint8 ver_minor; /* minor .. */
+} AMI_DRIVERINFO;
+
+#endif
diff --git a/drivers/misc/inv_mpu/compass/hmc5883.c b/drivers/misc/inv_mpu/compass/hmc5883.c
new file mode 100644
index 0000000..06d428e
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/hmc5883.c
@@ -0,0 +1,391 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup COMPASSDL
+ *
+ * @{
+ * @file hmc5883.c
+ * @brief Magnetometer setup and handling methods for Honeywell
+ * HMC5883 compass.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-compass"
+
+/* -------------------------------------------------------------------------- */
+enum HMC_REG {
+ HMC_REG_CONF_A = 0x0,
+ HMC_REG_CONF_B = 0x1,
+ HMC_REG_MODE = 0x2,
+ HMC_REG_X_M = 0x3,
+ HMC_REG_X_L = 0x4,
+ HMC_REG_Z_M = 0x5,
+ HMC_REG_Z_L = 0x6,
+ HMC_REG_Y_M = 0x7,
+ HMC_REG_Y_L = 0x8,
+ HMC_REG_STATUS = 0x9,
+ HMC_REG_ID_A = 0xA,
+ HMC_REG_ID_B = 0xB,
+ HMC_REG_ID_C = 0xC
+};
+
+enum HMC_CONF_A {
+ HMC_CONF_A_DRATE_MASK = 0x1C,
+ HMC_CONF_A_DRATE_0_75 = 0x00,
+ HMC_CONF_A_DRATE_1_5 = 0x04,
+ HMC_CONF_A_DRATE_3 = 0x08,
+ HMC_CONF_A_DRATE_7_5 = 0x0C,
+ HMC_CONF_A_DRATE_15 = 0x10,
+ HMC_CONF_A_DRATE_30 = 0x14,
+ HMC_CONF_A_DRATE_75 = 0x18,
+ HMC_CONF_A_MEAS_MASK = 0x3,
+ HMC_CONF_A_MEAS_NORM = 0x0,
+ HMC_CONF_A_MEAS_POS = 0x1,
+ HMC_CONF_A_MEAS_NEG = 0x2
+};
+
+enum HMC_CONF_B {
+ HMC_CONF_B_GAIN_MASK = 0xE0,
+ HMC_CONF_B_GAIN_0_9 = 0x00,
+ HMC_CONF_B_GAIN_1_2 = 0x20,
+ HMC_CONF_B_GAIN_1_9 = 0x40,
+ HMC_CONF_B_GAIN_2_5 = 0x60,
+ HMC_CONF_B_GAIN_4_0 = 0x80,
+ HMC_CONF_B_GAIN_4_6 = 0xA0,
+ HMC_CONF_B_GAIN_5_5 = 0xC0,
+ HMC_CONF_B_GAIN_7_9 = 0xE0
+};
+
+enum HMC_MODE {
+ HMC_MODE_MASK = 0x3,
+ HMC_MODE_CONT = 0x0,
+ HMC_MODE_SINGLE = 0x1,
+ HMC_MODE_IDLE = 0x2,
+ HMC_MODE_SLEEP = 0x3
+};
+
+/* -------------------------------------------------------------------------- */
+static int hmc5883_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ HMC_REG_MODE, HMC_MODE_SLEEP);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(3);
+
+ return result;
+}
+
+static int hmc5883_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ /* Use single measurement mode. Start at sleep state. */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ HMC_REG_MODE, HMC_MODE_SLEEP);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Config normal measurement */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ HMC_REG_CONF_A, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Adjust gain to 307 LSB/Gauss */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ HMC_REG_CONF_B, HMC_CONF_B_GAIN_5_5);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+static int hmc5883_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ unsigned char stat;
+ int result = INV_SUCCESS;
+ unsigned char tmp;
+ short axisFixed;
+
+ /* Read status reg. to check if data is ready */
+ result =
+ inv_serial_read(mlsl_handle, pdata->address, HMC_REG_STATUS, 1,
+ &stat);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if (stat & 0x01) {
+ result =
+ inv_serial_read(mlsl_handle, pdata->address,
+ HMC_REG_X_M, 6, (unsigned char *)data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* switch YZ axis to proper position */
+ tmp = data[2];
+ data[2] = data[4];
+ data[4] = tmp;
+ tmp = data[3];
+ data[3] = data[5];
+ data[5] = tmp;
+
+ /*drop data if overflows */
+ if ((data[0] == 0xf0) || (data[2] == 0xf0)
+ || (data[4] == 0xf0)) {
+ /* trigger next measurement read */
+ result =
+ inv_serial_single_write(mlsl_handle,
+ pdata->address,
+ HMC_REG_MODE,
+ HMC_MODE_SINGLE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return INV_ERROR_COMPASS_DATA_OVERFLOW;
+ }
+ /* convert to fixed point and apply sensitivity correction for
+ Z-axis */
+ axisFixed =
+ (short)((unsigned short)data[5] +
+ (unsigned short)data[4] * 256);
+ /* scale up by 1.125 (36/32) */
+ axisFixed = (short)(axisFixed * 36);
+ data[4] = axisFixed >> 8;
+ data[5] = axisFixed & 0xFF;
+
+ axisFixed =
+ (short)((unsigned short)data[3] +
+ (unsigned short)data[2] * 256);
+ axisFixed = (short)(axisFixed * 32);
+ data[2] = axisFixed >> 8;
+ data[3] = axisFixed & 0xFF;
+
+ axisFixed =
+ (short)((unsigned short)data[1] +
+ (unsigned short)data[0] * 256);
+ axisFixed = (short)(axisFixed * 32);
+ data[0] = axisFixed >> 8;
+ data[1] = axisFixed & 0xFF;
+
+ /* trigger next measurement read */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ HMC_REG_MODE, HMC_MODE_SINGLE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return INV_SUCCESS;
+ } else {
+ /* trigger next measurement read */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ HMC_REG_MODE, HMC_MODE_SINGLE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return INV_ERROR_COMPASS_DATA_NOT_READY;
+ }
+}
+
+static struct ext_slave_descr hmc5883_descr = {
+ .init = NULL,
+ .exit = NULL,
+ .suspend = hmc5883_suspend,
+ .resume = hmc5883_resume,
+ .read = hmc5883_read,
+ .config = NULL,
+ .get_config = NULL,
+ .name = "hmc5883",
+ .type = EXT_SLAVE_TYPE_COMPASS,
+ .id = COMPASS_ID_HMC5883,
+ .read_reg = 0x06,
+ .read_len = 6,
+ .endian = EXT_SLAVE_BIG_ENDIAN,
+ .range = {10673, 6156},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *hmc5883_get_slave_descr(void)
+{
+ return &hmc5883_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct hmc5883_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int hmc5883_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct hmc5883_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ hmc5883_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int hmc5883_mod_remove(struct i2c_client *client)
+{
+ struct hmc5883_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ hmc5883_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id hmc5883_mod_id[] = {
+ { "hmc5883", COMPASS_ID_HMC5883 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, hmc5883_mod_id);
+
+static struct i2c_driver hmc5883_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = hmc5883_mod_probe,
+ .remove = hmc5883_mod_remove,
+ .id_table = hmc5883_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "hmc5883_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init hmc5883_mod_init(void)
+{
+ int res = i2c_add_driver(&hmc5883_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "hmc5883_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit hmc5883_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&hmc5883_mod_driver);
+}
+
+module_init(hmc5883_mod_init);
+module_exit(hmc5883_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate HMC5883 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("hmc5883_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/compass/hscdtd002b.c b/drivers/misc/inv_mpu/compass/hscdtd002b.c
new file mode 100644
index 0000000..978822f
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/hscdtd002b.c
@@ -0,0 +1,294 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup COMPASSDL
+ *
+ * @{
+ * @file hscdtd002b.c
+ * @brief Magnetometer setup and handling methods for Alps HSCDTD002B
+ * compass.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-compass"
+
+/* -------------------------------------------------------------------------- */
+#define COMPASS_HSCDTD002B_STAT (0x18)
+#define COMPASS_HSCDTD002B_CTRL1 (0x1B)
+#define COMPASS_HSCDTD002B_CTRL2 (0x1C)
+#define COMPASS_HSCDTD002B_CTRL3 (0x1D)
+#define COMPASS_HSCDTD002B_DATAX (0x10)
+
+/* -------------------------------------------------------------------------- */
+static int hscdtd002b_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ /* Power mode: stand-by */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD002B_CTRL1, 0x00);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(1); /* turn-off time */
+
+ return result;
+}
+
+static int hscdtd002b_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ /* Soft reset */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD002B_CTRL3, 0x80);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Force state; Power mode: active */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD002B_CTRL1, 0x82);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Data ready enable */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD002B_CTRL2, 0x08);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(1); /* turn-on time */
+
+ return result;
+}
+
+static int hscdtd002b_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ unsigned char stat;
+ int result = INV_SUCCESS;
+ int status = INV_SUCCESS;
+
+ /* Read status reg. to check if data is ready */
+ result =
+ inv_serial_read(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD002B_STAT, 1, &stat);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if (stat & 0x40) {
+ result =
+ inv_serial_read(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD002B_DATAX, 6,
+ (unsigned char *)data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ status = INV_SUCCESS;
+ } else if (stat & 0x20) {
+ status = INV_ERROR_COMPASS_DATA_OVERFLOW;
+ } else {
+ status = INV_ERROR_COMPASS_DATA_NOT_READY;
+ }
+ /* trigger next measurement read */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD002B_CTRL3, 0x40);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return status;
+}
+
+static struct ext_slave_descr hscdtd002b_descr = {
+ .init = NULL,
+ .exit = NULL,
+ .suspend = hscdtd002b_suspend,
+ .resume = hscdtd002b_resume,
+ .read = hscdtd002b_read,
+ .config = NULL,
+ .get_config = NULL,
+ .name = "hscdtd002b",
+ .type = EXT_SLAVE_TYPE_COMPASS,
+ .id = COMPASS_ID_HSCDTD002B,
+ .read_reg = 0x10,
+ .read_len = 6,
+ .endian = EXT_SLAVE_LITTLE_ENDIAN,
+ .range = {9830, 4000},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *hscdtd002b_get_slave_descr(void)
+{
+ return &hscdtd002b_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct hscdtd002b_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int hscdtd002b_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct hscdtd002b_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ hscdtd002b_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int hscdtd002b_mod_remove(struct i2c_client *client)
+{
+ struct hscdtd002b_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ hscdtd002b_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id hscdtd002b_mod_id[] = {
+ { "hscdtd002b", COMPASS_ID_HSCDTD002B },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, hscdtd002b_mod_id);
+
+static struct i2c_driver hscdtd002b_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = hscdtd002b_mod_probe,
+ .remove = hscdtd002b_mod_remove,
+ .id_table = hscdtd002b_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "hscdtd002b_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init hscdtd002b_mod_init(void)
+{
+ int res = i2c_add_driver(&hscdtd002b_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "hscdtd002b_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit hscdtd002b_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&hscdtd002b_mod_driver);
+}
+
+module_init(hscdtd002b_mod_init);
+module_exit(hscdtd002b_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate HSCDTD002B sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("hscdtd002b_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/compass/hscdtd004a.c b/drivers/misc/inv_mpu/compass/hscdtd004a.c
new file mode 100644
index 0000000..e196032
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/hscdtd004a.c
@@ -0,0 +1,294 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup COMPASSDL
+ *
+ * @{
+ * @file hscdtd004a.c
+ * @brief Magnetometer setup and handling methods for Alps HSCDTD004A
+ * compass.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-compass"
+
+/* -------------------------------------------------------------------------- */
+#define COMPASS_HSCDTD004A_STAT (0x18)
+#define COMPASS_HSCDTD004A_CTRL1 (0x1B)
+#define COMPASS_HSCDTD004A_CTRL2 (0x1C)
+#define COMPASS_HSCDTD004A_CTRL3 (0x1D)
+#define COMPASS_HSCDTD004A_DATAX (0x10)
+
+/* -------------------------------------------------------------------------- */
+
+static int hscdtd004a_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ /* Power mode: stand-by */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD004A_CTRL1, 0x00);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(1); /* turn-off time */
+
+ return result;
+}
+
+static int hscdtd004a_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ /* Soft reset */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD004A_CTRL3, 0x80);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Normal state; Power mode: active */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD004A_CTRL1, 0x82);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Data ready enable */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD004A_CTRL2, 0x7C);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(1); /* turn-on time */
+ return result;
+}
+
+static int hscdtd004a_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ unsigned char stat;
+ int result = INV_SUCCESS;
+ int status = INV_SUCCESS;
+
+ /* Read status reg. to check if data is ready */
+ result =
+ inv_serial_read(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD004A_STAT, 1, &stat);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if (stat & 0x48) {
+ result =
+ inv_serial_read(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD004A_DATAX, 6,
+ (unsigned char *)data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ status = INV_SUCCESS;
+ } else if (stat & 0x68) {
+ status = INV_ERROR_COMPASS_DATA_OVERFLOW;
+ } else {
+ status = INV_ERROR_COMPASS_DATA_NOT_READY;
+ }
+ /* trigger next measurement read */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ COMPASS_HSCDTD004A_CTRL3, 0x40);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return status;
+
+}
+
+static struct ext_slave_descr hscdtd004a_descr = {
+ .init = NULL,
+ .exit = NULL,
+ .suspend = hscdtd004a_suspend,
+ .resume = hscdtd004a_resume,
+ .read = hscdtd004a_read,
+ .config = NULL,
+ .get_config = NULL,
+ .name = "hscdtd004a",
+ .type = EXT_SLAVE_TYPE_COMPASS,
+ .id = COMPASS_ID_HSCDTD004A,
+ .read_reg = 0x10,
+ .read_len = 6,
+ .endian = EXT_SLAVE_LITTLE_ENDIAN,
+ .range = {9830, 4000},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *hscdtd004a_get_slave_descr(void)
+{
+ return &hscdtd004a_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct hscdtd004a_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int hscdtd004a_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct hscdtd004a_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ hscdtd004a_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int hscdtd004a_mod_remove(struct i2c_client *client)
+{
+ struct hscdtd004a_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ hscdtd004a_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id hscdtd004a_mod_id[] = {
+ { "hscdtd004a", COMPASS_ID_HSCDTD004A },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, hscdtd004a_mod_id);
+
+static struct i2c_driver hscdtd004a_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = hscdtd004a_mod_probe,
+ .remove = hscdtd004a_mod_remove,
+ .id_table = hscdtd004a_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "hscdtd004a_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init hscdtd004a_mod_init(void)
+{
+ int res = i2c_add_driver(&hscdtd004a_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "hscdtd004a_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit hscdtd004a_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&hscdtd004a_mod_driver);
+}
+
+module_init(hscdtd004a_mod_init);
+module_exit(hscdtd004a_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate HSCDTD004A sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("hscdtd004a_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/compass/lsm303m.c b/drivers/misc/inv_mpu/compass/lsm303m.c
new file mode 100644
index 0000000..3ca1a67
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/lsm303m.c
@@ -0,0 +1,386 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup COMPASSDL
+ *
+ * @{
+ * @file lsm303m.c
+ * @brief Magnetometer setup and handling methods for ST LSM303
+ * compass.
+ * This magnetometer device is part of a combo chip with the
+ * ST LIS331DLH accelerometer and the logic in entirely based
+ * on the Honeywell HMC5883 magnetometer.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-compass"
+
+/* -------------------------------------------------------------------------- */
+enum LSM_REG {
+ LSM_REG_CONF_A = 0x0,
+ LSM_REG_CONF_B = 0x1,
+ LSM_REG_MODE = 0x2,
+ LSM_REG_X_M = 0x3,
+ LSM_REG_X_L = 0x4,
+ LSM_REG_Z_M = 0x5,
+ LSM_REG_Z_L = 0x6,
+ LSM_REG_Y_M = 0x7,
+ LSM_REG_Y_L = 0x8,
+ LSM_REG_STATUS = 0x9,
+ LSM_REG_ID_A = 0xA,
+ LSM_REG_ID_B = 0xB,
+ LSM_REG_ID_C = 0xC
+};
+
+enum LSM_CONF_A {
+ LSM_CONF_A_DRATE_MASK = 0x1C,
+ LSM_CONF_A_DRATE_0_75 = 0x00,
+ LSM_CONF_A_DRATE_1_5 = 0x04,
+ LSM_CONF_A_DRATE_3 = 0x08,
+ LSM_CONF_A_DRATE_7_5 = 0x0C,
+ LSM_CONF_A_DRATE_15 = 0x10,
+ LSM_CONF_A_DRATE_30 = 0x14,
+ LSM_CONF_A_DRATE_75 = 0x18,
+ LSM_CONF_A_MEAS_MASK = 0x3,
+ LSM_CONF_A_MEAS_NORM = 0x0,
+ LSM_CONF_A_MEAS_POS = 0x1,
+ LSM_CONF_A_MEAS_NEG = 0x2
+};
+
+enum LSM_CONF_B {
+ LSM_CONF_B_GAIN_MASK = 0xE0,
+ LSM_CONF_B_GAIN_0_9 = 0x00,
+ LSM_CONF_B_GAIN_1_2 = 0x20,
+ LSM_CONF_B_GAIN_1_9 = 0x40,
+ LSM_CONF_B_GAIN_2_5 = 0x60,
+ LSM_CONF_B_GAIN_4_0 = 0x80,
+ LSM_CONF_B_GAIN_4_6 = 0xA0,
+ LSM_CONF_B_GAIN_5_5 = 0xC0,
+ LSM_CONF_B_GAIN_7_9 = 0xE0
+};
+
+enum LSM_MODE {
+ LSM_MODE_MASK = 0x3,
+ LSM_MODE_CONT = 0x0,
+ LSM_MODE_SINGLE = 0x1,
+ LSM_MODE_IDLE = 0x2,
+ LSM_MODE_SLEEP = 0x3
+};
+
+/* -------------------------------------------------------------------------- */
+
+static int lsm303dlhm_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ LSM_REG_MODE, LSM_MODE_SLEEP);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(3);
+
+ return result;
+}
+
+static int lsm303dlhm_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ /* Use single measurement mode. Start at sleep state. */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ LSM_REG_MODE, LSM_MODE_SLEEP);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Config normal measurement */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ LSM_REG_CONF_A, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Adjust gain to 320 LSB/Gauss */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ LSM_REG_CONF_B, LSM_CONF_B_GAIN_5_5);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+static int lsm303dlhm_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ unsigned char stat;
+ int result = INV_SUCCESS;
+ short axisFixed;
+
+ /* Read status reg. to check if data is ready */
+ result =
+ inv_serial_read(mlsl_handle, pdata->address, LSM_REG_STATUS, 1,
+ &stat);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if (stat & 0x01) {
+ result =
+ inv_serial_read(mlsl_handle, pdata->address,
+ LSM_REG_X_M, 6, (unsigned char *)data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /*drop data if overflows */
+ if ((data[0] == 0xf0) || (data[2] == 0xf0)
+ || (data[4] == 0xf0)) {
+ /* trigger next measurement read */
+ result =
+ inv_serial_single_write(mlsl_handle,
+ pdata->address,
+ LSM_REG_MODE,
+ LSM_MODE_SINGLE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return INV_ERROR_COMPASS_DATA_OVERFLOW;
+ }
+ /* convert to fixed point and apply sensitivity correction for
+ Z-axis */
+ axisFixed =
+ (short)((unsigned short)data[5] +
+ (unsigned short)data[4] * 256);
+ /* scale up by 1.125 (36/32) approximate of 1.122 (320/285) */
+ axisFixed = (short)(axisFixed * 36);
+ data[4] = axisFixed >> 8;
+ data[5] = axisFixed & 0xFF;
+
+ axisFixed =
+ (short)((unsigned short)data[3] +
+ (unsigned short)data[2] * 256);
+ axisFixed = (short)(axisFixed * 32);
+ data[2] = axisFixed >> 8;
+ data[3] = axisFixed & 0xFF;
+
+ axisFixed =
+ (short)((unsigned short)data[1] +
+ (unsigned short)data[0] * 256);
+ axisFixed = (short)(axisFixed * 32);
+ data[0] = axisFixed >> 8;
+ data[1] = axisFixed & 0xFF;
+
+ /* trigger next measurement read */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ LSM_REG_MODE, LSM_MODE_SINGLE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return INV_SUCCESS;
+ } else {
+ /* trigger next measurement read */
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ LSM_REG_MODE, LSM_MODE_SINGLE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return INV_ERROR_COMPASS_DATA_NOT_READY;
+ }
+}
+
+static struct ext_slave_descr lsm303dlhm_descr = {
+ .init = NULL,
+ .exit = NULL,
+ .suspend = lsm303dlhm_suspend,
+ .resume = lsm303dlhm_resume,
+ .read = lsm303dlhm_read,
+ .config = NULL,
+ .get_config = NULL,
+ .name = "lsm303dlhm",
+ .type = EXT_SLAVE_TYPE_COMPASS,
+ .id = COMPASS_ID_LSM303M,
+ .read_reg = 0x06,
+ .read_len = 6,
+ .endian = EXT_SLAVE_BIG_ENDIAN,
+ .range = {10240, 0},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *lsm303m_get_slave_descr(void)
+{
+ return &lsm303dlhm_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct lsm303m_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int lsm303m_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct lsm303m_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ lsm303m_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int lsm303m_mod_remove(struct i2c_client *client)
+{
+ struct lsm303m_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ lsm303m_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id lsm303m_mod_id[] = {
+ { "lsm303m", COMPASS_ID_LSM303M },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, lsm303m_mod_id);
+
+static struct i2c_driver lsm303m_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = lsm303m_mod_probe,
+ .remove = lsm303m_mod_remove,
+ .id_table = lsm303m_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "lsm303m_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init lsm303m_mod_init(void)
+{
+ int res = i2c_add_driver(&lsm303m_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "lsm303m_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit lsm303m_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&lsm303m_mod_driver);
+}
+
+module_init(lsm303m_mod_init);
+module_exit(lsm303m_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate LSM303M sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("lsm303m_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/compass/mmc314x.c b/drivers/misc/inv_mpu/compass/mmc314x.c
new file mode 100644
index 0000000..f1b300e
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/mmc314x.c
@@ -0,0 +1,313 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup COMPASSDL
+ *
+ * @{
+ * @file mmc314x.c
+ * @brief Magnetometer setup and handling methods for the
+ * MEMSIC MMC314x compass.
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-compass"
+
+/* -------------------------------------------------------------------------- */
+
+static int reset_int = 1000;
+static int read_count = 1;
+static char reset_mode; /* in Z-init section */
+
+/* -------------------------------------------------------------------------- */
+#define MMC314X_REG_ST (0x00)
+#define MMC314X_REG_X_MSB (0x01)
+
+#define MMC314X_CNTL_MODE_WAKE_UP (0x01)
+#define MMC314X_CNTL_MODE_SET (0x02)
+#define MMC314X_CNTL_MODE_RESET (0x04)
+
+/* -------------------------------------------------------------------------- */
+
+static int mmc314x_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ return result;
+}
+
+static int mmc314x_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+
+ int result;
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ MMC314X_REG_ST, MMC314X_CNTL_MODE_RESET);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(10);
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ MMC314X_REG_ST, MMC314X_CNTL_MODE_SET);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(10);
+ read_count = 1;
+ return INV_SUCCESS;
+}
+
+static int mmc314x_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result, ii;
+ short tmp[3];
+ unsigned char tmpdata[6];
+
+ if (read_count > 1000)
+ read_count = 1;
+
+ result =
+ inv_serial_read(mlsl_handle, pdata->address, MMC314X_REG_X_MSB,
+ 6, (unsigned char *)data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ for (ii = 0; ii < 6; ii++)
+ tmpdata[ii] = data[ii];
+
+ for (ii = 0; ii < 3; ii++) {
+ tmp[ii] = (short)((tmpdata[2 * ii] << 8) + tmpdata[2 * ii + 1]);
+ tmp[ii] = tmp[ii] - 4096;
+ tmp[ii] = tmp[ii] * 16;
+ }
+
+ for (ii = 0; ii < 3; ii++) {
+ data[2 * ii] = (unsigned char)(tmp[ii] >> 8);
+ data[2 * ii + 1] = (unsigned char)(tmp[ii]);
+ }
+
+ if (read_count % reset_int == 0) {
+ if (reset_mode) {
+ result =
+ inv_serial_single_write(mlsl_handle,
+ pdata->address,
+ MMC314X_REG_ST,
+ MMC314X_CNTL_MODE_RESET);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ reset_mode = 0;
+ return INV_ERROR_COMPASS_DATA_NOT_READY;
+ } else {
+ result =
+ inv_serial_single_write(mlsl_handle,
+ pdata->address,
+ MMC314X_REG_ST,
+ MMC314X_CNTL_MODE_SET);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ reset_mode = 1;
+ read_count++;
+ return INV_ERROR_COMPASS_DATA_NOT_READY;
+ }
+ }
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ MMC314X_REG_ST, MMC314X_CNTL_MODE_WAKE_UP);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ read_count++;
+
+ return INV_SUCCESS;
+}
+
+static struct ext_slave_descr mmc314x_descr = {
+ .init = NULL,
+ .exit = NULL,
+ .suspend = mmc314x_suspend,
+ .resume = mmc314x_resume,
+ .read = mmc314x_read,
+ .config = NULL,
+ .get_config = NULL,
+ .name = "mmc314x",
+ .type = EXT_SLAVE_TYPE_COMPASS,
+ .id = COMPASS_ID_MMC314X,
+ .read_reg = 0x01,
+ .read_len = 6,
+ .endian = EXT_SLAVE_BIG_ENDIAN,
+ .range = {400, 0},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *mmc314x_get_slave_descr(void)
+{
+ return &mmc314x_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct mmc314x_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int mmc314x_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct mmc314x_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ mmc314x_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int mmc314x_mod_remove(struct i2c_client *client)
+{
+ struct mmc314x_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ mmc314x_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id mmc314x_mod_id[] = {
+ { "mmc314x", COMPASS_ID_MMC314X },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, mmc314x_mod_id);
+
+static struct i2c_driver mmc314x_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = mmc314x_mod_probe,
+ .remove = mmc314x_mod_remove,
+ .id_table = mmc314x_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mmc314x_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init mmc314x_mod_init(void)
+{
+ int res = i2c_add_driver(&mmc314x_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "mmc314x_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit mmc314x_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&mmc314x_mod_driver);
+}
+
+module_init(mmc314x_mod_init);
+module_exit(mmc314x_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate MMC314X sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("mmc314x_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/compass/yas529-kernel.c b/drivers/misc/inv_mpu/compass/yas529-kernel.c
new file mode 100644
index 0000000..4758d63
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/yas529-kernel.c
@@ -0,0 +1,611 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <log.h>
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-acc"
+
+/*----- YAMAHA YAS529 Registers ------*/
+enum YAS_REG {
+ YAS_REG_CMDR = 0x00, /* 000 < 5 */
+ YAS_REG_XOFFSETR = 0x20, /* 001 < 5 */
+ YAS_REG_Y1OFFSETR = 0x40, /* 010 < 5 */
+ YAS_REG_Y2OFFSETR = 0x60, /* 011 < 5 */
+ YAS_REG_ICOILR = 0x80, /* 100 < 5 */
+ YAS_REG_CAL = 0xA0, /* 101 < 5 */
+ YAS_REG_CONFR = 0xC0, /* 110 < 5 */
+ YAS_REG_DOUTR = 0xE0 /* 111 < 5 */
+};
+
+/* -------------------------------------------------------------------------- */
+
+static long a1;
+static long a2;
+static long a3;
+static long a4;
+static long a5;
+static long a6;
+static long a7;
+static long a8;
+static long a9;
+
+/* -------------------------------------------------------------------------- */
+static int yas529_sensor_i2c_write(struct i2c_adapter *i2c_adap,
+ unsigned char address,
+ unsigned int len, unsigned char *data)
+{
+ struct i2c_msg msgs[1];
+ int res;
+
+ if (NULL == data || NULL == i2c_adap)
+ return -EINVAL;
+
+ msgs[0].addr = address;
+ msgs[0].flags = 0; /* write */
+ msgs[0].buf = (unsigned char *)data;
+ msgs[0].len = len;
+
+ res = i2c_transfer(i2c_adap, msgs, 1);
+ if (res < 1)
+ return res;
+ else
+ return 0;
+}
+
+static int yas529_sensor_i2c_read(struct i2c_adapter *i2c_adap,
+ unsigned char address,
+ unsigned char reg,
+ unsigned int len, unsigned char *data)
+{
+ struct i2c_msg msgs[2];
+ int res;
+
+ if (NULL == data || NULL == i2c_adap)
+ return -EINVAL;
+
+ msgs[0].addr = address;
+ msgs[0].flags = I2C_M_RD;
+ msgs[0].buf = data;
+ msgs[0].len = len;
+
+ res = i2c_transfer(i2c_adap, msgs, 1);
+ if (res < 1)
+ return res;
+ else
+ return 0;
+}
+
+static int yas529_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ return result;
+}
+
+static int yas529_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ unsigned char dummyData[1] = { 0 };
+ unsigned char dummyRegister = 0;
+ unsigned char rawData[6];
+ unsigned char calData[9];
+
+ short xoffset, y1offset, y2offset;
+ short d2, d3, d4, d5, d6, d7, d8, d9;
+
+ /* YAS529 Application Manual MS-3C - Section 4.4.5 */
+ /* =============================================== */
+ /* Step 1 - register initialization */
+ /* zero initialization coil register - "100 00 000" */
+ dummyData[0] = YAS_REG_ICOILR | 0x00;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* zero config register - "110 00 000" */
+ dummyData[0] = YAS_REG_CONFR | 0x00;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Step 2 - initialization coil operation */
+ dummyData[0] = YAS_REG_ICOILR | 0x11;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x01;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x12;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x02;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x13;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x03;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x14;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x04;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x15;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x05;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x16;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x06;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x17;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x07;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x10;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_ICOILR | 0x00;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Step 3 - rough offset measurement */
+ /* Config register - Measurements results - "110 00 000" */
+ dummyData[0] = YAS_REG_CONFR | 0x00;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Measurements command register - Rough offset measurement -
+ "000 00001" */
+ dummyData[0] = YAS_REG_CMDR | 0x01;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(2); /* wait at least 1.5ms */
+
+ /* Measurement data read */
+ result =
+ yas529_sensor_i2c_read(mlsl_handle, pdata->address,
+ dummyRegister, 6, rawData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ xoffset =
+ (short)((unsigned short)rawData[5] +
+ ((unsigned short)rawData[4] & 0x7) * 256) - 5;
+ if (xoffset < 0)
+ xoffset = 0;
+ y1offset =
+ (short)((unsigned short)rawData[3] +
+ ((unsigned short)rawData[2] & 0x7) * 256) - 5;
+ if (y1offset < 0)
+ y1offset = 0;
+ y2offset =
+ (short)((unsigned short)rawData[1] +
+ ((unsigned short)rawData[0] & 0x7) * 256) - 5;
+ if (y2offset < 0)
+ y2offset = 0;
+
+ /* Step 4 - rough offset setting */
+ /* Set rough offset register values */
+ dummyData[0] = YAS_REG_XOFFSETR | xoffset;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_Y1OFFSETR | y1offset;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ dummyData[0] = YAS_REG_Y2OFFSETR | y2offset;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* CAL matrix read (first read is invalid) */
+ /* Config register - CAL register read - "110 01 000" */
+ dummyData[0] = YAS_REG_CONFR | 0x08;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* CAL data read */
+ result =
+ yas529_sensor_i2c_read(mlsl_handle, pdata->address,
+ dummyRegister, 9, calData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Config register - CAL register read - "110 01 000" */
+ dummyData[0] = YAS_REG_CONFR | 0x08;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* CAL data read */
+ result =
+ yas529_sensor_i2c_read(mlsl_handle, pdata->address,
+ dummyRegister, 9, calData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Calculate coefficients of the sensitivity correction matrix */
+ a1 = 100;
+ d2 = (calData[0] & 0xFC) >> 2; /* [71..66] 6bit */
+ a2 = (short)(d2 - 32);
+ /* [65..62] 4bit */
+ d3 = ((calData[0] & 0x03) << 2) | ((calData[1] & 0xC0) >> 6);
+ a3 = (short)(d3 - 8);
+ d4 = (calData[1] & 0x3F); /* [61..56] 6bit */
+ a4 = (short)(d4 - 32);
+ d5 = (calData[2] & 0xFC) >> 2; /* [55..50] 6bit */
+ a5 = (short)(d5 - 32) + 70;
+ /* [49..44] 6bit */
+ d6 = ((calData[2] & 0x03) << 4) | ((calData[3] & 0xF0) >> 4);
+ a6 = (short)(d6 - 32);
+ /* [43..38] 6bit */
+ d7 = ((calData[3] & 0x0F) << 2) | ((calData[4] & 0xC0) >> 6);
+ a7 = (short)(d7 - 32);
+ d8 = (calData[4] & 0x3F); /* [37..32] 6bit */
+ a8 = (short)(d8 - 32);
+ d9 = (calData[5] & 0xFE) >> 1; /* [31..25] 7bit */
+ a9 = (short)(d9 - 64) + 130;
+
+ return result;
+}
+
+static int yas529_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ unsigned char stat;
+ unsigned char rawData[6];
+ unsigned char dummyData[1] = { 0 };
+ unsigned char dummyRegister = 0;
+ int result = INV_SUCCESS;
+ short SX, SY1, SY2, SY, SZ;
+ short row1fixed, row2fixed, row3fixed;
+
+ /* Config register - Measurements results - "110 00 000" */
+ dummyData[0] = YAS_REG_CONFR | 0x00;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Measurements command register - Normal magnetic field measurement -
+ "000 00000" */
+ dummyData[0] = YAS_REG_CMDR | 0x00;
+ result =
+ yas529_sensor_i2c_write(mlsl_handle, pdata->address, 1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(10);
+ /* Measurement data read */
+ result =
+ yas529_sensor_i2c_read(mlsl_handle, pdata->address,
+ dummyRegister, 6, (unsigned char *)&rawData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ stat = rawData[0] & 0x80;
+ if (stat == 0x00) {
+ /* Extract raw data */
+ SX = (short)((unsigned short)rawData[5] +
+ ((unsigned short)rawData[4] & 0x7) * 256);
+ SY1 =
+ (short)((unsigned short)rawData[3] +
+ ((unsigned short)rawData[2] & 0x7) * 256);
+ SY2 =
+ (short)((unsigned short)rawData[1] +
+ ((unsigned short)rawData[0] & 0x7) * 256);
+ if ((SX <= 1) || (SY1 <= 1) || (SY2 <= 1))
+ return INV_ERROR_COMPASS_DATA_UNDERFLOW;
+ if ((SX >= 1024) || (SY1 >= 1024) || (SY2 >= 1024))
+ return INV_ERROR_COMPASS_DATA_OVERFLOW;
+ /* Convert to XYZ axis */
+ SX = -1 * SX;
+ SY = SY2 - SY1;
+ SZ = SY1 + SY2;
+
+ /* Apply sensitivity correction matrix */
+ row1fixed = (short)((a1 * SX + a2 * SY + a3 * SZ) >> 7) * 41;
+ row2fixed = (short)((a4 * SX + a5 * SY + a6 * SZ) >> 7) * 41;
+ row3fixed = (short)((a7 * SX + a8 * SY + a9 * SZ) >> 7) * 41;
+
+ data[0] = row1fixed >> 8;
+ data[1] = row1fixed & 0xFF;
+ data[2] = row2fixed >> 8;
+ data[3] = row2fixed & 0xFF;
+ data[4] = row3fixed >> 8;
+ data[5] = row3fixed & 0xFF;
+
+ return INV_SUCCESS;
+ } else {
+ return INV_ERROR_COMPASS_DATA_NOT_READY;
+ }
+}
+
+static struct ext_slave_descr yas529_descr = {
+ .init = NULL,
+ .exit = NULL,
+ .suspend = yas529_suspend,
+ .resume = yas529_resume,
+ .read = yas529_read,
+ .config = NULL,
+ .get_config = NULL,
+ .name = "yas529",
+ .type = EXT_SLAVE_TYPE_COMPASS,
+ .id = COMPASS_ID_YAS529,
+ .read_reg = 0x06,
+ .read_len = 6,
+ .endian = EXT_SLAVE_BIG_ENDIAN,
+ .range = {19660, 8000},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *yas529_get_slave_descr(void)
+{
+ return &yas529_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct yas529_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int yas529_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct yas529_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ yas529_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int yas529_mod_remove(struct i2c_client *client)
+{
+ struct yas529_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ yas529_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id yas529_mod_id[] = {
+ { "yas529", COMPASS_ID_YAS529 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, yas529_mod_id);
+
+static struct i2c_driver yas529_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = yas529_mod_probe,
+ .remove = yas529_mod_remove,
+ .id_table = yas529_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "yas529_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init yas529_mod_init(void)
+{
+ int res = i2c_add_driver(&yas529_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "yas529_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit yas529_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&yas529_mod_driver);
+}
+
+module_init(yas529_mod_init);
+module_exit(yas529_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate YAS529 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("yas529_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/compass/yas530.c b/drivers/misc/inv_mpu/compass/yas530.c
new file mode 100644
index 0000000..fbe7d7f
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/yas530.c
@@ -0,0 +1,784 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup COMPASSDL
+ *
+ * @{
+ * @file yas530.c
+ * @brief Magnetometer setup and handling methods for Yamaha YAS530
+ * compass when used in a user-space solution (no kernel driver).
+ */
+
+/* -------------------------------------------------------------------------- */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include "log.h"
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "MPL-compass"
+
+enum {
+ FLAG_OFFSETS_VALID = 0x00000001,
+ FLAG_RESUMED = 0x00000002,
+};
+
+struct yas530_private_data {
+ int flags;
+ char offsets[3];
+ const int *correction_matrix;
+};
+
+/* -------------------------------------------------------------------------- */
+#define YAS530_REGADDR_DEVICE_ID (0x80)
+#define YAS530_REGADDR_ACTUATE_INIT_COIL (0x81)
+#define YAS530_REGADDR_MEASURE_COMMAND (0x82)
+#define YAS530_REGADDR_CONFIG (0x83)
+#define YAS530_REGADDR_MEASURE_INTERVAL (0x84)
+#define YAS530_REGADDR_OFFSET_X (0x85)
+#define YAS530_REGADDR_OFFSET_Y1 (0x86)
+#define YAS530_REGADDR_OFFSET_Y2 (0x87)
+#define YAS530_REGADDR_TEST1 (0x88)
+#define YAS530_REGADDR_TEST2 (0x89)
+#define YAS530_REGADDR_CAL (0x90)
+#define YAS530_REGADDR_MEASURE_DATA (0xb0)
+
+/* -------------------------------------------------------------------------- */
+static int Cx, Cy1, Cy2;
+static int /*a1, */ a2, a3, a4, a5, a6, a7, a8, a9;
+static int k;
+
+static unsigned char dx, dy1, dy2;
+static unsigned char d2, d3, d4, d5, d6, d7, d8, d9, d0;
+static unsigned char dck;
+
+/* -------------------------------------------------------------------------- */
+
+static int is_overunderflow(short xy1y2)
+{
+ return (xy1y2 == 0 || xy1y2 == 4095);
+}
+
+static int set_hardware_offset(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ char offset_x, char offset_y1, char offset_y2)
+{
+ char data;
+ int result = INV_SUCCESS;
+
+ data = offset_x & 0x3f;
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ YAS530_REGADDR_OFFSET_X, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ data = offset_y1 & 0x3f;
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ YAS530_REGADDR_OFFSET_Y1, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ data = offset_y2 & 0x3f;
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ YAS530_REGADDR_OFFSET_Y2, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+static int set_measure_command(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ int ldtc, int fors, int dlymes)
+{
+ int result = INV_SUCCESS;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ YAS530_REGADDR_MEASURE_COMMAND, 0x01);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+static int measure_normal(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ int *busy, unsigned short *t,
+ unsigned short *x, unsigned short *y1,
+ unsigned short *y2)
+{
+ unsigned char data[8];
+ unsigned short b, to, xo, y1o, y2o;
+ int result;
+ ktime_t sleeptime;
+ result = set_measure_command(mlsl_handle, slave, pdata, 0, 0, 0);
+ sleeptime = ktime_set(0, 2 * NSEC_PER_MSEC);
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_hrtimeout(&sleeptime, HRTIMER_MODE_REL);
+
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ YAS530_REGADDR_MEASURE_DATA, 8, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* msleep(2); */
+
+ b = (data[0] >> 7) & 0x01;
+ to = ((data[0] << 2) & 0x1fc) | ((data[1] >> 6) & 0x03);
+ xo = ((data[2] << 5) & 0xfe0) | ((data[3] >> 3) & 0x1f);
+ y1o = ((data[4] << 5) & 0xfe0) | ((data[5] >> 3) & 0x1f);
+ y2o = ((data[6] << 5) & 0xfe0) | ((data[7] >> 3) & 0x1f);
+
+ *busy = b;
+ *t = to;
+ *x = xo;
+ *y1 = y1o;
+ *y2 = y2o;
+
+ return result;
+}
+
+static int check_offset(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ char offset_x, char offset_y1, char offset_y2,
+ int *flag_x, int *flag_y1, int *flag_y2)
+{
+ int result;
+ int busy;
+ short t, x, y1, y2;
+
+ result = set_hardware_offset(mlsl_handle, slave, pdata,
+ offset_x, offset_y1, offset_y2);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = measure_normal(mlsl_handle, slave, pdata,
+ &busy, &t, &x, &y1, &y2);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ *flag_x = 0;
+ *flag_y1 = 0;
+ *flag_y2 = 0;
+
+ if (x > 2048)
+ *flag_x = 1;
+ if (y1 > 2048)
+ *flag_y1 = 1;
+ if (y2 > 2048)
+ *flag_y2 = 1;
+ if (x < 2048)
+ *flag_x = -1;
+ if (y1 < 2048)
+ *flag_y1 = -1;
+ if (y2 < 2048)
+ *flag_y2 = -1;
+
+ return result;
+}
+
+static int measure_and_set_offset(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ char *offset)
+{
+ int i;
+ int result = INV_SUCCESS;
+ char offset_x = 0, offset_y1 = 0, offset_y2 = 0;
+ int flag_x = 0, flag_y1 = 0, flag_y2 = 0;
+ static const int correct[5] = { 16, 8, 4, 2, 1 };
+
+ for (i = 0; i < 5; i++) {
+ result = check_offset(mlsl_handle, slave, pdata,
+ offset_x, offset_y1, offset_y2,
+ &flag_x, &flag_y1, &flag_y2);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (flag_x)
+ offset_x += flag_x * correct[i];
+ if (flag_y1)
+ offset_y1 += flag_y1 * correct[i];
+ if (flag_y2)
+ offset_y2 += flag_y2 * correct[i];
+ }
+
+ result = set_hardware_offset(mlsl_handle, slave, pdata,
+ offset_x, offset_y1, offset_y2);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ offset[0] = offset_x;
+ offset[1] = offset_y1;
+ offset[2] = offset_y2;
+
+ return result;
+}
+
+static void coordinate_conversion(short x, short y1, short y2, short t,
+ int32_t *xo, int32_t *yo, int32_t *zo)
+{
+ int32_t sx, sy1, sy2, sy, sz;
+ int32_t hx, hy, hz;
+
+ sx = x - (Cx * t) / 100;
+ sy1 = y1 - (Cy1 * t) / 100;
+ sy2 = y2 - (Cy2 * t) / 100;
+
+ sy = sy1 - sy2;
+ sz = -sy1 - sy2;
+
+ hx = k * ((100 * sx + a2 * sy + a3 * sz) / 10);
+ hy = k * ((a4 * sx + a5 * sy + a6 * sz) / 10);
+ hz = k * ((a7 * sx + a8 * sy + a9 * sz) / 10);
+
+ *xo = hx;
+ *yo = hy;
+ *zo = hz;
+}
+
+static int power_up(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ unsigned char dummyData = 0x00;
+ unsigned char data[16];
+ unsigned char read_reg[1];
+
+ /* =============================================== */
+
+ /* Step 1 - Test register initialization */
+ dummyData = 0x00;
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ YAS530_REGADDR_TEST1, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result =
+ inv_serial_single_write(mlsl_handle, pdata->address,
+ YAS530_REGADDR_TEST2, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Device ID read */
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ YAS530_REGADDR_DEVICE_ID, 1, read_reg);
+
+ /*Step 2 Read the CAL register */
+ /* CAL data read */
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ YAS530_REGADDR_CAL, 16, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* CAL data Second Read */
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ YAS530_REGADDR_CAL, 16, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /*Cal data */
+ dx = data[0];
+ dy1 = data[1];
+ dy2 = data[2];
+ d2 = (data[3] >> 2) & 0x03f;
+ d3 = ((data[3] << 2) & 0x0c) | ((data[4] >> 6) & 0x03);
+ d4 = data[4] & 0x3f;
+ d5 = (data[5] >> 2) & 0x3f;
+ d6 = ((data[5] << 4) & 0x30) | ((data[6] >> 4) & 0x0f);
+ d7 = ((data[6] << 3) & 0x78) | ((data[7] >> 5) & 0x07);
+ d8 = ((data[7] << 1) & 0x3e) | ((data[8] >> 7) & 0x01);
+ d9 = ((data[8] << 1) & 0xfe) | ((data[9] >> 7) & 0x01);
+ d0 = (data[9] >> 2) & 0x1f;
+ dck = ((data[9] << 1) & 0x06) | ((data[10] >> 7) & 0x01);
+
+ /*Correction Data */
+ Cx = (int)dx * 6 - 768;
+ Cy1 = (int)dy1 * 6 - 768;
+ Cy2 = (int)dy2 * 6 - 768;
+ a2 = (int)d2 - 32;
+ a3 = (int)d3 - 8;
+ a4 = (int)d4 - 32;
+ a5 = (int)d5 + 38;
+ a6 = (int)d6 - 32;
+ a7 = (int)d7 - 64;
+ a8 = (int)d8 - 32;
+ a9 = (int)d9;
+ k = (int)d0 + 10;
+
+ /*Obtain the [49:47] bits */
+ dck &= 0x07;
+
+ /*Step 3 : Storing the CONFIG with the CLK value */
+ dummyData = 0x00 | (dck << 2);
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ YAS530_REGADDR_CONFIG, dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /*Step 4 : Set Acquisition Interval Register */
+ dummyData = 0x00;
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ YAS530_REGADDR_MEASURE_INTERVAL,
+ dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /*Step 5 : Reset Coil */
+ dummyData = 0x00;
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ YAS530_REGADDR_ACTUATE_INIT_COIL,
+ dummyData);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+
+}
+
+static int yas530_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ return result;
+}
+
+
+static int yas530_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+
+ struct yas530_private_data *private_data = pdata->private_data;
+
+ result = power_up(mlsl_handle, slave, pdata);
+
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ private_data->flags |= FLAG_RESUMED;
+
+ return result;
+}
+
+static int yas530_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result = INV_SUCCESS;
+
+ int busy;
+ short t, x, y1, y2;
+ int32_t xyz[3];
+ int32_t tmp[3];
+ int i;
+ short rawfixed[3];
+ struct yas530_private_data *private_data = pdata->private_data;
+ const int *correction_matrix = private_data->correction_matrix;
+
+ result = measure_normal(mlsl_handle, slave, pdata,
+ &busy, &t, &x, &y1, &y2);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ coordinate_conversion(x, y1, y2, t, &xyz[0], &xyz[1], &xyz[2]);
+ if (correction_matrix) {
+ for (i = 0; i < 3; i++) {
+ tmp[i] = (correction_matrix[i * 3 + 0]
+ * (xyz[0] / 10)) / 100
+ + (correction_matrix[i * 3 + 1]
+ * (xyz[1] / 10)) / 100
+ + (correction_matrix[i * 3 + 2]
+ * (xyz[2] / 10)) / 100;
+ }
+ for (i = 0; i < 3; i++)
+ xyz[i] = tmp[i];
+ }
+
+
+ rawfixed[0] = (short)(xyz[0] / 100);
+ rawfixed[1] = (short)(xyz[1] / 100);
+ rawfixed[2] = (short)(xyz[2] / 100);
+
+ data[0] = rawfixed[0] >> 8;
+ data[1] = rawfixed[0] & 0xFF;
+ data[2] = rawfixed[1] >> 8;
+ data[3] = rawfixed[1] & 0xFF;
+ data[4] = rawfixed[2] >> 8;
+ data[5] = rawfixed[2] & 0xFF;
+ data[6] = is_overunderflow(x)
+ || is_overunderflow(y1)
+ || is_overunderflow(y2);
+
+ return result;
+}
+
+static int yas530_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ int result = INV_SUCCESS;
+ struct yas530_private_data *private_data = pdata->private_data;
+ switch (data->key) {
+ case MPU_SLAVE_OFFSET_VALS: {
+ char offs_x, offs_y1, offs_y2;
+ offs_x = ((char *)(data->data))[0];
+ offs_y1 = ((char *)(data->data))[1];
+ offs_y2 = ((char *)(data->data))[2];
+ result = set_hardware_offset(mlsl_handle, slave, pdata,
+ offs_x, offs_y1, offs_y2);
+ if (result == INV_SUCCESS) {
+ private_data->flags |= FLAG_OFFSETS_VALID;
+ private_data->offsets[0] = offs_x;
+ private_data->offsets[1] = offs_y1;
+ private_data->offsets[2] = offs_y2;
+ }
+ break;
+ }
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ }
+
+ return result;
+
+}
+
+static int yas530_get_config(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *data)
+{
+ int result = INV_SUCCESS;
+ struct yas530_private_data *private_data = pdata->private_data;
+
+ switch (data->key) {
+ case MPU_SLAVE_OFFSET_VALS: {
+ if (!(private_data->flags & FLAG_RESUMED)) {
+ result = power_up(mlsl_handle, slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ } else {
+ result = inv_serial_single_write(mlsl_handle,
+ pdata->address,
+ YAS530_REGADDR_ACTUATE_INIT_COIL, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ result = measure_and_set_offset(mlsl_handle, slave, pdata,
+ (char *)(data->data));
+ if (result == INV_SUCCESS) {
+ private_data->flags |= FLAG_OFFSETS_VALID;
+ private_data->offsets[0] = ((char *)(data->data))[0];
+ private_data->offsets[1] = ((char *)(data->data))[1];
+ private_data->offsets[2] = ((char *)(data->data))[2];
+ }
+ break;
+ }
+ case MPU_SLAVE_RANGE_CHECK: {
+ int busy;
+ short t, x, y1, y2;
+ char flag_x = 0, flag_y1 = 0, flag_y2 = 0;
+
+ if (!(private_data->flags & FLAG_OFFSETS_VALID))
+ return INV_ERROR_INVALID_CONFIGURATION;
+
+ if (!(private_data->flags & FLAG_RESUMED)) {
+ result = power_up(mlsl_handle, slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ result = measure_normal(mlsl_handle, slave, pdata,
+ &busy, &t, &x, &y1, &y2);
+
+ if (x < 1024)
+ flag_x = -1;
+ if (x > 3072)
+ flag_x = 1;
+ if (y1 < 1024)
+ flag_y1 = -1;
+ if (y1 > 3072)
+ flag_y1 = 1;
+ if (y2 < 1024)
+ flag_y2 = -1;
+ if (y2 > 3072)
+ flag_y2 = 1;
+
+ ((char *) (data->data))[0] = (char) flag_x;
+ ((char *) (data->data))[1] = (char) flag_y1;
+ ((char *) (data->data))[2] = (char) flag_y2;
+ break;
+ }
+ default:
+ return INV_ERROR_FEATURE_NOT_IMPLEMENTED;
+ }
+
+ return result;
+
+}
+
+static int yas530_init(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+
+ struct yas530_private_data *private_data;
+ int result = INV_SUCCESS;
+ char offset[3] = {0, 0, 0};
+
+ private_data = (struct yas530_private_data *)
+ kzalloc(sizeof(struct yas530_private_data), GFP_KERNEL);
+
+ if (!private_data)
+ return INV_ERROR_MEMORY_EXAUSTED;
+
+ private_data->correction_matrix = pdata->private_data;
+
+ pdata->private_data = private_data;
+
+ result = power_up(mlsl_handle, slave, pdata);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = measure_and_set_offset(mlsl_handle, slave, pdata, offset);
+ if (result == INV_SUCCESS) {
+ private_data->flags |= FLAG_OFFSETS_VALID;
+ private_data->offsets[0] = offset[0];
+ private_data->offsets[1] = offset[1];
+ private_data->offsets[2] = offset[2];
+ }
+
+ return result;
+}
+
+static int yas530_exit(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ kfree(pdata->private_data);
+ return INV_SUCCESS;
+}
+
+static struct ext_slave_descr yas530_descr = {
+ .init = yas530_init,
+ .exit = yas530_exit,
+ .suspend = yas530_suspend,
+ .resume = yas530_resume,
+ .read = yas530_read,
+ .config = yas530_config,
+ .get_config = yas530_get_config,
+ .name = "yas530",
+ .type = EXT_SLAVE_TYPE_COMPASS,
+ .id = COMPASS_ID_YAS530,
+ .read_reg = 0x06,
+ .read_len = 7,
+ .endian = EXT_SLAVE_BIG_ENDIAN,
+ .range = {3276, 8001},
+ .trigger = NULL,
+};
+
+static
+struct ext_slave_descr *yas530_get_slave_descr(void)
+{
+ return &yas530_descr;
+}
+
+/* -------------------------------------------------------------------------- */
+struct yas530_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int yas530_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct yas530_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ yas530_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int yas530_mod_remove(struct i2c_client *client)
+{
+ struct yas530_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ yas530_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id yas530_mod_id[] = {
+ { "yas530", COMPASS_ID_YAS530 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, yas530_mod_id);
+
+static struct i2c_driver yas530_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = yas530_mod_probe,
+ .remove = yas530_mod_remove,
+ .id_table = yas530_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "yas530_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init yas530_mod_init(void)
+{
+ int res = i2c_add_driver(&yas530_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "yas530_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit yas530_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&yas530_mod_driver);
+}
+
+module_init(yas530_mod_init);
+module_exit(yas530_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate YAS530 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("yas530_mod");
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/log.h b/drivers/misc/inv_mpu/log.h
new file mode 100644
index 0000000..99e6b4f
--- /dev/null
+++ b/drivers/misc/inv_mpu/log.h
@@ -0,0 +1,287 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/*
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *
+ * Copyright (C) 2005 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * C/C++ logging functions. See the logging documentation for API details.
+ *
+ * We'd like these to be available from C code (in case we import some from
+ * somewhere), so this has a C interface.
+ *
+ * The output will be correct when the log file is shared between multiple
+ * threads and/or multiple processes so long as the operating system
+ * supports O_APPEND. These calls have mutex-protected data structures
+ * and so are NOT reentrant. Do not use MPL_LOG in a signal handler.
+ */
+#ifndef _LIBS_CUTILS_MPL_LOG_H
+#define _LIBS_CUTILS_MPL_LOG_H
+
+#include "mltypes.h"
+#include <stdarg.h>
+
+
+#include <linux/kernel.h>
+
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Normally we strip MPL_LOGV (VERBOSE messages) from release builds.
+ * You can modify this (for example with "#define MPL_LOG_NDEBUG 0"
+ * at the top of your source file) to change that behavior.
+ */
+#ifndef MPL_LOG_NDEBUG
+#ifdef NDEBUG
+#define MPL_LOG_NDEBUG 1
+#else
+#define MPL_LOG_NDEBUG 0
+#endif
+#endif
+
+#define MPL_LOG_UNKNOWN MPL_LOG_VERBOSE
+#define MPL_LOG_DEFAULT KERN_DEFAULT
+#define MPL_LOG_VERBOSE KERN_CONT
+#define MPL_LOG_DEBUG KERN_NOTICE
+#define MPL_LOG_INFO KERN_INFO
+#define MPL_LOG_WARN KERN_WARNING
+#define MPL_LOG_ERROR KERN_ERR
+#define MPL_LOG_SILENT MPL_LOG_VERBOSE
+
+
+
+/*
+ * This is the local tag used for the following simplified
+ * logging macros. You can change this preprocessor definition
+ * before using the other macros to change the tag.
+ */
+#ifndef MPL_LOG_TAG
+#define MPL_LOG_TAG
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Simplified macro to send a verbose log message using the current MPL_LOG_TAG.
+ */
+#ifndef MPL_LOGV
+#if MPL_LOG_NDEBUG
+#define MPL_LOGV(fmt, ...) \
+ do { \
+ if (0) \
+ MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__);\
+ } while (0)
+#else
+#define MPL_LOGV(fmt, ...) MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__)
+#endif
+#endif
+
+#ifndef CONDITION
+#define CONDITION(cond) ((cond) != 0)
+#endif
+
+#ifndef MPL_LOGV_IF
+#if MPL_LOG_NDEBUG
+#define MPL_LOGV_IF(cond, fmt, ...) \
+ do { if (0) MPL_LOG(fmt, ##__VA_ARGS__); } while (0)
+#else
+#define MPL_LOGV_IF(cond, fmt, ...) \
+ ((CONDITION(cond)) \
+ ? MPL_LOG(LOG_VERBOSE, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
+ : (void)0)
+#endif
+#endif
+
+/*
+ * Simplified macro to send a debug log message using the current MPL_LOG_TAG.
+ */
+#ifndef MPL_LOGD
+#define MPL_LOGD(fmt, ...) MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__)
+#endif
+
+#ifndef MPL_LOGD_IF
+#define MPL_LOGD_IF(cond, fmt, ...) \
+ ((CONDITION(cond)) \
+ ? MPL_LOG(LOG_DEBUG, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
+ : (void)0)
+#endif
+
+/*
+ * Simplified macro to send an info log message using the current MPL_LOG_TAG.
+ */
+#ifndef MPL_LOGI
+#define MPL_LOGI(fmt, ...) MPL_LOG(LOG_INFO, MPL_LOG_TAG, fmt, ##__VA_ARGS__)
+#endif
+
+#ifndef MPL_LOGI_IF
+#define MPL_LOGI_IF(cond, fmt, ...) \
+ ((CONDITION(cond)) \
+ ? MPL_LOG(LOG_INFO, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
+ : (void)0)
+#endif
+
+/*
+ * Simplified macro to send a warning log message using the current MPL_LOG_TAG.
+ */
+#ifndef MPL_LOGW
+#define MPL_LOGW(fmt, ...) printk(KERN_WARNING MPL_LOG_TAG fmt, ##__VA_ARGS__)
+#endif
+
+#ifndef MPL_LOGW_IF
+#define MPL_LOGW_IF(cond, fmt, ...) \
+ ((CONDITION(cond)) \
+ ? MPL_LOG(LOG_WARN, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
+ : (void)0)
+#endif
+
+/*
+ * Simplified macro to send an error log message using the current MPL_LOG_TAG.
+ */
+#ifndef MPL_LOGE
+#define MPL_LOGE(fmt, ...) printk(KERN_ERR MPL_LOG_TAG fmt, ##__VA_ARGS__)
+#endif
+
+#ifndef MPL_LOGE_IF
+#define MPL_LOGE_IF(cond, fmt, ...) \
+ ((CONDITION(cond)) \
+ ? MPL_LOG(LOG_ERROR, MPL_LOG_TAG, fmt, ##__VA_ARGS__) \
+ : (void)0)
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Log a fatal error. If the given condition fails, this stops program
+ * execution like a normal assertion, but also generating the given message.
+ * It is NOT stripped from release builds. Note that the condition test
+ * is -inverted- from the normal assert() semantics.
+ */
+#define MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ...) \
+ ((CONDITION(cond)) \
+ ? ((void)android_printAssert(#cond, MPL_LOG_TAG, \
+ fmt, ##__VA_ARGS__)) \
+ : (void)0)
+
+#define MPL_LOG_ALWAYS_FATAL(fmt, ...) \
+ (((void)android_printAssert(NULL, MPL_LOG_TAG, fmt, ##__VA_ARGS__)))
+
+/*
+ * Versions of MPL_LOG_ALWAYS_FATAL_IF and MPL_LOG_ALWAYS_FATAL that
+ * are stripped out of release builds.
+ */
+#if MPL_LOG_NDEBUG
+#define MPL_LOG_FATAL_IF(cond, fmt, ...) \
+ do { \
+ if (0) \
+ MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__); \
+ } while (0)
+#define MPL_LOG_FATAL(fmt, ...) \
+ do { \
+ if (0) \
+ MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__) \
+ } while (0)
+#else
+#define MPL_LOG_FATAL_IF(cond, fmt, ...) \
+ MPL_LOG_ALWAYS_FATAL_IF(cond, fmt, ##__VA_ARGS__)
+#define MPL_LOG_FATAL(fmt, ...) \
+ MPL_LOG_ALWAYS_FATAL(fmt, ##__VA_ARGS__)
+#endif
+
+/*
+ * Assertion that generates a log message when the assertion fails.
+ * Stripped out of release builds. Uses the current MPL_LOG_TAG.
+ */
+#define MPL_LOG_ASSERT(cond, fmt, ...) \
+ MPL_LOG_FATAL_IF(!(cond), fmt, ##__VA_ARGS__)
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Basic log message macro.
+ *
+ * Example:
+ * MPL_LOG(MPL_LOG_WARN, NULL, "Failed with error %d", errno);
+ *
+ * The second argument may be NULL or "" to indicate the "global" tag.
+ */
+#ifndef MPL_LOG
+#define MPL_LOG(priority, tag, fmt, ...) \
+ MPL_LOG_PRI(priority, tag, fmt, ##__VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to specify a number for the priority.
+ */
+#ifndef MPL_LOG_PRI
+#define MPL_LOG_PRI(priority, tag, fmt, ...) \
+ pr_debug(MPL_##priority tag fmt, ##__VA_ARGS__)
+#endif
+
+/*
+ * Log macro that allows you to pass in a varargs ("args" is a va_list).
+ */
+#ifndef MPL_LOG_PRI_VA
+/* not allowed in the Kernel because there is no dev_dbg that takes a va_list */
+#endif
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * ===========================================================================
+ *
+ * The stuff in the rest of this file should not be used directly.
+ */
+
+int _MLPrintLog(int priority, const char *tag, const char *fmt, ...);
+int _MLPrintVaLog(int priority, const char *tag, const char *fmt, va_list args);
+/* Final implementation of actual writing to a character device */
+int _MLWriteLog(const char *buf, int buflen);
+
+static inline void __print_result_location(int result,
+ const char *file,
+ const char *func, int line)
+{
+ MPL_LOGE("%s|%s|%d returning %d\n", file, func, line, result);
+}
+
+#define LOG_RESULT_LOCATION(condition) \
+ do { \
+ __print_result_location((int)(condition), __FILE__, \
+ __func__, __LINE__); \
+ } while (0)
+
+
+#endif /* _LIBS_CUTILS_MPL_LOG_H */
diff --git a/drivers/misc/inv_mpu/mldl_cfg.c b/drivers/misc/inv_mpu/mldl_cfg.c
new file mode 100644
index 0000000..2ae894e
--- /dev/null
+++ b/drivers/misc/inv_mpu/mldl_cfg.c
@@ -0,0 +1,1808 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup MLDL
+ *
+ * @{
+ * @file mldl_cfg.c
+ * @brief The Motion Library Driver Layer.
+ */
+
+/* -------------------------------------------------------------------------- */
+#include <linux/delay.h>
+
+#include <stddef.h>
+
+#include "mldl_cfg.h"
+#include <linux/mpu.h>
+# include "mpu3050.h"
+
+#include "mlsl.h"
+
+#include "log.h"
+#undef MPL_LOG_TAG
+#define MPL_LOG_TAG "mldl_cfg:"
+
+/* -------------------------------------------------------------------------- */
+
+#define SLEEP 1
+#define WAKE_UP 0
+#define RESET 1
+#define STANDBY 1
+
+/* -------------------------------------------------------------------------- */
+
+/**
+ * @brief Stop the DMP running
+ *
+ * @return INV_SUCCESS or non-zero error code
+ */
+static int dmp_stop(struct mldl_cfg *mldl_cfg, void *gyro_handle)
+{
+ unsigned char user_ctrl_reg;
+ int result;
+
+ if (!mldl_cfg->dmp_is_running)
+ return INV_SUCCESS;
+
+ result = inv_serial_read(gyro_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL, 1, &user_ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ user_ctrl_reg = (user_ctrl_reg & (~BIT_FIFO_EN)) | BIT_FIFO_RST;
+ user_ctrl_reg = (user_ctrl_reg & (~BIT_DMP_EN)) | BIT_DMP_RST;
+
+ result = inv_serial_single_write(gyro_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL, user_ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ mldl_cfg->dmp_is_running = FALSE;
+
+ return result;
+}
+
+/**
+ * @brief Starts the DMP running
+ *
+ * @return INV_SUCCESS or non-zero error code
+ */
+static int dmp_start(struct mldl_cfg *pdata, void *mlsl_handle)
+{
+ unsigned char user_ctrl_reg;
+ int result;
+
+ if (pdata->dmp_is_running == pdata->dmp_enable)
+ return INV_SUCCESS;
+
+ result = inv_serial_read(mlsl_handle, pdata->addr,
+ MPUREG_USER_CTRL, 1, &user_ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_single_write(mlsl_handle, pdata->addr,
+ MPUREG_USER_CTRL,
+ ((user_ctrl_reg & (~BIT_FIFO_EN))
+ | BIT_FIFO_RST));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_single_write(mlsl_handle, pdata->addr,
+ MPUREG_USER_CTRL, user_ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_read(mlsl_handle, pdata->addr,
+ MPUREG_USER_CTRL, 1, &user_ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (pdata->dmp_enable)
+ user_ctrl_reg |= BIT_DMP_EN;
+ else
+ user_ctrl_reg &= ~BIT_DMP_EN;
+
+ if (pdata->fifo_enable)
+ user_ctrl_reg |= BIT_FIFO_EN;
+ else
+ user_ctrl_reg &= ~BIT_FIFO_EN;
+
+ user_ctrl_reg |= BIT_DMP_RST;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->addr,
+ MPUREG_USER_CTRL, user_ctrl_reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ pdata->dmp_is_running = pdata->dmp_enable;
+
+ return result;
+}
+
+
+
+static int mpu3050_set_i2c_bypass(struct mldl_cfg *mldl_cfg,
+ void *mlsl_handle, unsigned char enable)
+{
+ unsigned char b;
+ int result;
+
+ if ((mldl_cfg->gyro_is_bypassed && enable) ||
+ (!mldl_cfg->gyro_is_bypassed && !enable))
+ return INV_SUCCESS;
+
+ /*---- get current 'USER_CTRL' into b ----*/
+ result = inv_serial_read(mlsl_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL, 1, &b);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ b &= ~BIT_AUX_IF_EN;
+
+ if (!enable) {
+ result = inv_serial_single_write(mlsl_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL,
+ (b | BIT_AUX_IF_EN));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ } else {
+ /* Coming out of I2C is tricky due to several erratta. Do not
+ * modify this algorithm
+ */
+ /*
+ * 1) wait for the right time and send the command to change
+ * the aux i2c slave address to an invalid address that will
+ * get nack'ed
+ *
+ * 0x00 is broadcast. 0x7F is unlikely to be used by any aux.
+ */
+ result = inv_serial_single_write(mlsl_handle, mldl_cfg->addr,
+ MPUREG_AUX_SLV_ADDR, 0x7F);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /*
+ * 2) wait enough time for a nack to occur, then go into
+ * bypass mode:
+ */
+ msleep(2);
+ result = inv_serial_single_write(mlsl_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL, (b));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /*
+ * 3) wait for up to one MPU cycle then restore the slave
+ * address
+ */
+ msleep(inv_mpu_get_sampling_period_us(mldl_cfg) / 1000);
+ result = inv_serial_single_write(
+ mlsl_handle, mldl_cfg->addr,
+ MPUREG_AUX_SLV_ADDR,
+ mldl_cfg->pdata->accel.address);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /*
+ * 4) reset the ime interface
+ */
+
+ result = inv_serial_single_write(mlsl_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL,
+ (b | BIT_AUX_IF_RST));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(2);
+ }
+ mldl_cfg->gyro_is_bypassed = enable;
+
+ return result;
+}
+
+/**
+ * @brief enables/disables the I2C bypass to an external device
+ * connected to MPU's secondary I2C bus.
+ * @param enable
+ * Non-zero to enable pass through.
+ * @return INV_SUCCESS if successful, a non-zero error code otherwise.
+ */
+static int mpu_set_i2c_bypass(struct mldl_cfg *mldl_cfg,
+ void *mlsl_handle, unsigned char enable)
+{
+ return mpu3050_set_i2c_bypass(mldl_cfg, mlsl_handle, enable);
+}
+
+
+#define NUM_OF_PROD_REVS (ARRAY_SIZE(prod_rev_map))
+
+/* NOTE : when not indicated, product revision
+ is considered an 'npp'; non production part */
+
+struct prod_rev_map_t {
+ unsigned char silicon_rev;
+ unsigned short gyro_trim;
+};
+
+#define OLDEST_PROD_REV_SUPPORTED 11
+static struct prod_rev_map_t prod_rev_map[] = {
+ {0, 0},
+ {MPU_SILICON_REV_A4, 131}, /* 1 A? OBSOLETED */
+ {MPU_SILICON_REV_A4, 131}, /* 2 | */
+ {MPU_SILICON_REV_A4, 131}, /* 3 | */
+ {MPU_SILICON_REV_A4, 131}, /* 4 | */
+ {MPU_SILICON_REV_A4, 131}, /* 5 | */
+ {MPU_SILICON_REV_A4, 131}, /* 6 | */
+ {MPU_SILICON_REV_A4, 131}, /* 7 | */
+ {MPU_SILICON_REV_A4, 131}, /* 8 | */
+ {MPU_SILICON_REV_A4, 131}, /* 9 | */
+ {MPU_SILICON_REV_A4, 131}, /* 10 V */
+ {MPU_SILICON_REV_B1, 131}, /* 11 B1 */
+ {MPU_SILICON_REV_B1, 131}, /* 12 | */
+ {MPU_SILICON_REV_B1, 131}, /* 13 | */
+ {MPU_SILICON_REV_B1, 131}, /* 14 V */
+ {MPU_SILICON_REV_B4, 131}, /* 15 B4 */
+ {MPU_SILICON_REV_B4, 131}, /* 16 | */
+ {MPU_SILICON_REV_B4, 131}, /* 17 | */
+ {MPU_SILICON_REV_B4, 131}, /* 18 | */
+ {MPU_SILICON_REV_B4, 115}, /* 19 | */
+ {MPU_SILICON_REV_B4, 115}, /* 20 V */
+ {MPU_SILICON_REV_B6, 131}, /* 21 B6 (B6/A9) */
+ {MPU_SILICON_REV_B4, 115}, /* 22 B4 (B7/A10) */
+ {MPU_SILICON_REV_B6, 0}, /* 23 B6 */
+ {MPU_SILICON_REV_B6, 0}, /* 24 | */
+ {MPU_SILICON_REV_B6, 0}, /* 25 | */
+ {MPU_SILICON_REV_B6, 131}, /* 26 V (B6/A11) */
+};
+
+/**
+ * @internal
+ * @brief Get the silicon revision ID from OTP for MPU3050.
+ * The silicon revision number is in read from OTP bank 0,
+ * ADDR6[7:2]. The corresponding ID is retrieved by lookup
+ * in a map.
+ *
+ * @param mldl_cfg
+ * a pointer to the mldl config data structure.
+ * @param mlsl_handle
+ * an file handle to the serial communication device the
+ * device is connected to.
+ *
+ * @return 0 on success, a non-zero error code otherwise.
+ */
+static int inv_get_silicon_rev_mpu3050(
+ struct mldl_cfg *mldl_cfg, void *mlsl_handle)
+{
+ int result;
+ unsigned char index = 0x00;
+ unsigned char bank =
+ (BIT_PRFTCH_EN | BIT_CFG_USER_BANK | MPU_MEM_OTP_BANK_0);
+ unsigned short mem_addr = ((bank << 8) | 0x06);
+
+ result = inv_serial_read(mlsl_handle, mldl_cfg->addr,
+ MPUREG_PRODUCT_ID, 1, &mldl_cfg->product_id);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_serial_read_mem(mlsl_handle, mldl_cfg->addr,
+ mem_addr, 1, &index);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ index >>= 2;
+
+ /* clean the prefetch and cfg user bank bits */
+ result = inv_serial_single_write(mlsl_handle, mldl_cfg->addr,
+ MPUREG_BANK_SEL, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (index < OLDEST_PROD_REV_SUPPORTED || index >= NUM_OF_PROD_REVS) {
+ mldl_cfg->silicon_revision = 0;
+ mldl_cfg->gyro_sens_trim = 0;
+ MPL_LOGE("Unsupported Product Revision Detected : %d\n", index);
+ return INV_ERROR_INVALID_MODULE;
+ }
+
+ mldl_cfg->product_revision = index;
+ mldl_cfg->silicon_revision = prod_rev_map[index].silicon_rev;
+ mldl_cfg->gyro_sens_trim = prod_rev_map[index].gyro_trim;
+ if (mldl_cfg->gyro_sens_trim == 0) {
+ MPL_LOGE("gyro sensitivity trim is 0"
+ " - unsupported non production part.\n");
+ return INV_ERROR_INVALID_MODULE;
+ }
+
+ return result;
+}
+#define inv_get_silicon_rev inv_get_silicon_rev_mpu3050
+
+
+/**
+ * @brief Enable / Disable the use MPU's secondary I2C interface level
+ * shifters.
+ * When enabled the secondary I2C interface to which the external
+ * device is connected runs at VDD voltage (main supply).
+ * When disabled the 2nd interface runs at VDDIO voltage.
+ * See the device specification for more details.
+ *
+ * @note using this API may produce unpredictable results, depending on how
+ * the MPU and slave device are setup on the target platform.
+ * Use of this API should entirely be restricted to system
+ * integrators. Once the correct value is found, there should be no
+ * need to change the level shifter at runtime.
+ *
+ * @pre Must be called after inv_serial_start().
+ * @note Typically called before inv_dmp_open().
+ *
+ * @param[in] enable:
+ * 0 to run at VDDIO (default),
+ * 1 to run at VDD.
+ *
+ * @return INV_SUCCESS if successfull, a non-zero error code otherwise.
+ */
+static int inv_mpu_set_level_shifter_bit(struct mldl_cfg *pdata,
+ void *mlsl_handle, unsigned char enable)
+{
+ int result;
+ unsigned char reg;
+ unsigned char mask;
+ unsigned char regval;
+
+ if (0 == pdata->silicon_revision)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ /*-- on parts before B6 the VDDIO bit is bit 7 of ACCEL_BURST_ADDR --
+ NOTE: this is incompatible with ST accelerometers where the VDDIO
+ bit MUST be set to enable ST's internal logic to autoincrement
+ the register address on burst reads --*/
+ if ((pdata->silicon_revision & 0xf) < MPU_SILICON_REV_B6) {
+ reg = MPUREG_ACCEL_BURST_ADDR;
+ mask = 0x80;
+ } else {
+ /*-- on B6 parts the VDDIO bit was moved to FIFO_EN2 =>
+ the mask is always 0x04 --*/
+ reg = MPUREG_FIFO_EN2;
+ mask = 0x04;
+ }
+
+ result = inv_serial_read(mlsl_handle, pdata->addr, reg, 1, &regval);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (enable)
+ regval |= mask;
+ else
+ regval &= ~mask;
+
+ result = inv_serial_single_write(mlsl_handle, pdata->addr, reg, regval);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+ return INV_SUCCESS;
+}
+
+
+/**
+ * @internal
+ * @brief This function controls the power management on the MPU device.
+ * The entire chip can be put to low power sleep mode, or individual
+ * gyros can be turned on/off.
+ *
+ * Putting the device into sleep mode depending upon the changing needs
+ * of the associated applications is a recommended method for reducing
+ * power consuption. It is a safe opearation in that sleep/wake up of
+ * gyros while running will not result in any interruption of data.
+ *
+ * Although it is entirely allowed to put the device into full sleep
+ * while running the DMP, it is not recomended because it will disrupt
+ * the ongoing calculations carried on inside the DMP and consequently
+ * the sensor fusion algorithm. Furthermore, while in sleep mode
+ * read & write operation from the app processor on both registers and
+ * memory are disabled and can only regained by restoring the MPU in
+ * normal power mode.
+ * Disabling any of the gyro axis will reduce the associated power
+ * consuption from the PLL but will not stop the DMP from running
+ * state.
+ *
+ * @param reset
+ * Non-zero to reset the device. Note that this setting
+ * is volatile and the corresponding register bit will
+ * clear itself right after being applied.
+ * @param sleep
+ * Non-zero to put device into full sleep.
+ * @param disable_gx
+ * Non-zero to disable gyro X.
+ * @param disable_gy
+ * Non-zero to disable gyro Y.
+ * @param disable_gz
+ * Non-zero to disable gyro Z.
+ *
+ * @return INV_SUCCESS if successfull; a non-zero error code otherwise.
+ */
+static int mpu3050_pwr_mgmt(struct mldl_cfg *mldl_cfg,
+ void *mlsl_handle,
+ unsigned char reset,
+ unsigned char sleep,
+ unsigned char disable_gx,
+ unsigned char disable_gy,
+ unsigned char disable_gz)
+{
+ unsigned char b;
+ int result;
+
+ result =
+ inv_serial_read(mlsl_handle, mldl_cfg->addr, MPUREG_PWR_MGM, 1, &b);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* If we are awake, we need to put it in bypass before resetting */
+ if ((!(b & BIT_SLEEP)) && reset)
+ result = mpu_set_i2c_bypass(mldl_cfg, mlsl_handle, 1);
+
+ /* Reset if requested */
+ if (reset) {
+ MPL_LOGV("Reset MPU3050\n");
+ result = inv_serial_single_write(mlsl_handle, mldl_cfg->addr,
+ MPUREG_PWR_MGM,
+ b | BIT_H_RESET);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ msleep(5);
+ mldl_cfg->gyro_needs_reset = FALSE;
+ /* Some chips are awake after reset and some are asleep,
+ * check the status */
+ result = inv_serial_read(mlsl_handle, mldl_cfg->addr,
+ MPUREG_PWR_MGM, 1, &b);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ /* Update the suspended state just in case we return early */
+ if (b & BIT_SLEEP)
+ mldl_cfg->gyro_is_suspended = TRUE;
+ else
+ mldl_cfg->gyro_is_suspended = FALSE;
+
+ /* if power status match requested, nothing else's left to do */
+ if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) ==
+ (((sleep != 0) * BIT_SLEEP) |
+ ((disable_gx != 0) * BIT_STBY_XG) |
+ ((disable_gy != 0) * BIT_STBY_YG) |
+ ((disable_gz != 0) * BIT_STBY_ZG))) {
+ return INV_SUCCESS;
+ }
+
+ /*
+ * This specific transition between states needs to be reinterpreted:
+ * (1,1,1,1) -> (0,1,1,1) has to become
+ * (1,1,1,1) -> (1,0,0,0) -> (0,1,1,1)
+ * where
+ * (1,1,1,1) is (sleep=1,disable_gx=1,disable_gy=1,disable_gz=1)
+ */
+ if ((b & (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)) ==
+ (BIT_SLEEP | BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG)
+ && ((!sleep) && disable_gx && disable_gy && disable_gz)) {
+ result = mpu3050_pwr_mgmt(mldl_cfg, mlsl_handle, 0, 1, 0, 0, 0);
+ if (result)
+ return result;
+ b |= BIT_SLEEP;
+ b &= ~(BIT_STBY_XG | BIT_STBY_YG | BIT_STBY_ZG);
+ }
+
+ if ((b & BIT_SLEEP) != ((sleep != 0) * BIT_SLEEP)) {
+ if (sleep) {
+ result = mpu_set_i2c_bypass(mldl_cfg, mlsl_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ b |= BIT_SLEEP;
+ result =
+ inv_serial_single_write(mlsl_handle, mldl_cfg->addr,
+ MPUREG_PWR_MGM, b);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ mldl_cfg->gyro_is_suspended = TRUE;
+ } else {
+ b &= ~BIT_SLEEP;
+ result =
+ inv_serial_single_write(mlsl_handle, mldl_cfg->addr,
+ MPUREG_PWR_MGM, b);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ mldl_cfg->gyro_is_suspended = FALSE;
+ msleep(5);
+ }
+ }
+ /*---
+ WORKAROUND FOR PUTTING GYRO AXIS in STAND-BY MODE
+ 1) put one axis at a time in stand-by
+ ---*/
+ if ((b & BIT_STBY_XG) != ((disable_gx != 0) * BIT_STBY_XG)) {
+ b ^= BIT_STBY_XG;
+ result = inv_serial_single_write(mlsl_handle, mldl_cfg->addr,
+ MPUREG_PWR_MGM, b);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ if ((b & BIT_STBY_YG) != ((disable_gy != 0) * BIT_STBY_YG)) {
+ b ^= BIT_STBY_YG;
+ result = inv_serial_single_write(mlsl_handle, mldl_cfg->addr,
+ MPUREG_PWR_MGM, b);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ if ((b & BIT_STBY_ZG) != ((disable_gz != 0) * BIT_STBY_ZG)) {
+ b ^= BIT_STBY_ZG;
+ result = inv_serial_single_write(mlsl_handle, mldl_cfg->addr,
+ MPUREG_PWR_MGM, b);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ return INV_SUCCESS;
+}
+
+
+/**
+ * @brief sets the clock source for the gyros.
+ * @param mldl_cfg
+ * a pointer to the struct mldl_cfg data structure.
+ * @param gyro_handle
+ * an handle to the serial device the gyro is assigned to.
+ * @return ML_SUCCESS if successful, a non-zero error code otherwise.
+ */
+static int mpu_set_clock_source(void *gyro_handle, struct mldl_cfg *mldl_cfg)
+{
+ int result;
+ unsigned char cur_clk_src;
+ unsigned char reg;
+
+ /* clock source selection */
+ result = inv_serial_read(gyro_handle, mldl_cfg->addr,
+ MPUREG_PWR_MGM, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ cur_clk_src = reg & BITS_CLKSEL;
+ reg &= ~BITS_CLKSEL;
+
+
+ result = inv_serial_single_write(gyro_handle, mldl_cfg->addr,
+ MPUREG_PWR_MGM,
+ mldl_cfg->clk_src | reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* TODO : workarounds to be determined and implemented */
+
+ return result;
+}
+
+void mpu_print_cfg(struct mldl_cfg *mldl_cfg)
+{
+ struct mpu_platform_data *pdata = mldl_cfg->pdata;
+ struct ext_slave_platform_data *accel = &mldl_cfg->pdata->accel;
+ struct ext_slave_platform_data *compass = &mldl_cfg->pdata->compass;
+ struct ext_slave_platform_data *pressure = &mldl_cfg->pdata->pressure;
+
+ MPL_LOGD("mldl_cfg.addr = %02x\n", mldl_cfg->addr);
+ MPL_LOGD("mldl_cfg.int_config = %02x\n", mldl_cfg->int_config);
+ MPL_LOGD("mldl_cfg.ext_sync = %02x\n", mldl_cfg->ext_sync);
+ MPL_LOGD("mldl_cfg.full_scale = %02x\n", mldl_cfg->full_scale);
+ MPL_LOGD("mldl_cfg.lpf = %02x\n", mldl_cfg->lpf);
+ MPL_LOGD("mldl_cfg.clk_src = %02x\n", mldl_cfg->clk_src);
+ MPL_LOGD("mldl_cfg.divider = %02x\n", mldl_cfg->divider);
+ MPL_LOGD("mldl_cfg.dmp_enable = %02x\n", mldl_cfg->dmp_enable);
+ MPL_LOGD("mldl_cfg.fifo_enable = %02x\n", mldl_cfg->fifo_enable);
+ MPL_LOGD("mldl_cfg.dmp_cfg1 = %02x\n", mldl_cfg->dmp_cfg1);
+ MPL_LOGD("mldl_cfg.dmp_cfg2 = %02x\n", mldl_cfg->dmp_cfg2);
+ MPL_LOGD("mldl_cfg.offset_tc[0] = %02x\n", mldl_cfg->offset_tc[0]);
+ MPL_LOGD("mldl_cfg.offset_tc[1] = %02x\n", mldl_cfg->offset_tc[1]);
+ MPL_LOGD("mldl_cfg.offset_tc[2] = %02x\n", mldl_cfg->offset_tc[2]);
+ MPL_LOGD("mldl_cfg.silicon_revision = %02x\n",
+ mldl_cfg->silicon_revision);
+ MPL_LOGD("mldl_cfg.product_revision = %02x\n",
+ mldl_cfg->product_revision);
+ MPL_LOGD("mldl_cfg.product_id = %02x\n", mldl_cfg->product_id);
+ MPL_LOGD("mldl_cfg.gyro_sens_trim = %02x\n",
+ mldl_cfg->gyro_sens_trim);
+ MPL_LOGD("mldl_cfg.requested_sensors= %04lx\n",
+ mldl_cfg->requested_sensors);
+
+ if (mldl_cfg->accel) {
+ MPL_LOGD("slave_accel->suspend = %02x\n",
+ (int)mldl_cfg->accel->suspend);
+ MPL_LOGD("slave_accel->resume = %02x\n",
+ (int)mldl_cfg->accel->resume);
+ MPL_LOGD("slave_accel->read = %02x\n",
+ (int)mldl_cfg->accel->read);
+ MPL_LOGD("slave_accel->type = %02x\n",
+ mldl_cfg->accel->type);
+ MPL_LOGD("slave_accel->reg = %02x\n",
+ mldl_cfg->accel->read_reg);
+ MPL_LOGD("slave_accel->len = %02x\n",
+ mldl_cfg->accel->read_len);
+ MPL_LOGD("slave_accel->endian = %02x\n",
+ mldl_cfg->accel->endian);
+ MPL_LOGD("slave_accel->range.mantissa= %02lx\n",
+ mldl_cfg->accel->range.mantissa);
+ MPL_LOGD("slave_accel->range.fraction= %02lx\n",
+ mldl_cfg->accel->range.fraction);
+ } else {
+ MPL_LOGD("slave_accel = NULL\n");
+ }
+
+ if (mldl_cfg->compass) {
+ MPL_LOGD("slave_compass->suspend = %02x\n",
+ (int)mldl_cfg->compass->suspend);
+ MPL_LOGD("slave_compass->resume = %02x\n",
+ (int)mldl_cfg->compass->resume);
+ MPL_LOGD("slave_compass->read = %02x\n",
+ (int)mldl_cfg->compass->read);
+ MPL_LOGD("slave_compass->type = %02x\n",
+ mldl_cfg->compass->type);
+ MPL_LOGD("slave_compass->reg = %02x\n",
+ mldl_cfg->compass->read_reg);
+ MPL_LOGD("slave_compass->len = %02x\n",
+ mldl_cfg->compass->read_len);
+ MPL_LOGD("slave_compass->endian = %02x\n",
+ mldl_cfg->compass->endian);
+ MPL_LOGD("slave_compass->range.mantissa= %02lx\n",
+ mldl_cfg->compass->range.mantissa);
+ MPL_LOGD("slave_compass->range.fraction= %02lx\n",
+ mldl_cfg->compass->range.fraction);
+
+ } else {
+ MPL_LOGD("slave_compass = NULL\n");
+ }
+
+ if (mldl_cfg->pressure) {
+ MPL_LOGD("slave_pressure->suspend = %02x\n",
+ (int)mldl_cfg->pressure->suspend);
+ MPL_LOGD("slave_pressure->resume = %02x\n",
+ (int)mldl_cfg->pressure->resume);
+ MPL_LOGD("slave_pressure->read = %02x\n",
+ (int)mldl_cfg->pressure->read);
+ MPL_LOGD("slave_pressure->type = %02x\n",
+ mldl_cfg->pressure->type);
+ MPL_LOGD("slave_pressure->reg = %02x\n",
+ mldl_cfg->pressure->read_reg);
+ MPL_LOGD("slave_pressure->len = %02x\n",
+ mldl_cfg->pressure->read_len);
+ MPL_LOGD("slave_pressure->endian = %02x\n",
+ mldl_cfg->pressure->endian);
+ MPL_LOGD("slave_pressure->range.mantissa= %02lx\n",
+ mldl_cfg->pressure->range.mantissa);
+ MPL_LOGD("slave_pressure->range.fraction= %02lx\n",
+ mldl_cfg->pressure->range.fraction);
+
+ } else {
+ MPL_LOGD("slave_pressure = NULL\n");
+ }
+ MPL_LOGD("accel->get_slave_descr = %x\n",
+ (unsigned int)accel->get_slave_descr);
+ MPL_LOGD("accel->irq = %02x\n", accel->irq);
+ MPL_LOGD("accel->adapt_num = %02x\n", accel->adapt_num);
+ MPL_LOGD("accel->bus = %02x\n", accel->bus);
+ MPL_LOGD("accel->address = %02x\n", accel->address);
+ MPL_LOGD("accel->orientation =\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n",
+ accel->orientation[0], accel->orientation[1],
+ accel->orientation[2], accel->orientation[3],
+ accel->orientation[4], accel->orientation[5],
+ accel->orientation[6], accel->orientation[7],
+ accel->orientation[8]);
+ MPL_LOGD("compass->get_slave_descr = %x\n",
+ (unsigned int)compass->get_slave_descr);
+ MPL_LOGD("compass->irq = %02x\n", compass->irq);
+ MPL_LOGD("compass->adapt_num = %02x\n", compass->adapt_num);
+ MPL_LOGD("compass->bus = %02x\n", compass->bus);
+ MPL_LOGD("compass->address = %02x\n", compass->address);
+ MPL_LOGD("compass->orientation =\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n",
+ compass->orientation[0], compass->orientation[1],
+ compass->orientation[2], compass->orientation[3],
+ compass->orientation[4], compass->orientation[5],
+ compass->orientation[6], compass->orientation[7],
+ compass->orientation[8]);
+ MPL_LOGD("pressure->get_slave_descr = %x\n",
+ (unsigned int)pressure->get_slave_descr);
+ MPL_LOGD("pressure->irq = %02x\n", pressure->irq);
+ MPL_LOGD("pressure->adapt_num = %02x\n", pressure->adapt_num);
+ MPL_LOGD("pressure->bus = %02x\n", pressure->bus);
+ MPL_LOGD("pressure->address = %02x\n", pressure->address);
+ MPL_LOGD("pressure->orientation =\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n",
+ pressure->orientation[0], pressure->orientation[1],
+ pressure->orientation[2], pressure->orientation[3],
+ pressure->orientation[4], pressure->orientation[5],
+ pressure->orientation[6], pressure->orientation[7],
+ pressure->orientation[8]);
+
+ MPL_LOGD("pdata->int_config = %02x\n", pdata->int_config);
+ MPL_LOGD("pdata->level_shifter = %02x\n", pdata->level_shifter);
+ MPL_LOGD("pdata->orientation =\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n"
+ " %2d %2d %2d\n",
+ pdata->orientation[0], pdata->orientation[1],
+ pdata->orientation[2], pdata->orientation[3],
+ pdata->orientation[4], pdata->orientation[5],
+ pdata->orientation[6], pdata->orientation[7],
+ pdata->orientation[8]);
+
+ MPL_LOGD("Struct sizes: mldl_cfg: %d, "
+ "ext_slave_descr:%d, "
+ "mpu_platform_data:%d: RamOffset: %d\n",
+ sizeof(struct mldl_cfg), sizeof(struct ext_slave_descr),
+ sizeof(struct mpu_platform_data),
+ offsetof(struct mldl_cfg, ram));
+}
+
+/**
+ * Configures the MPU I2C Master
+ *
+ * @mldl_cfg Handle to the configuration data
+ * @gyro_handle handle to the gyro communictation interface
+ * @slave Can be Null if turning off the slave
+ * @slave_pdata Can be null if turning off the slave
+ * @slave_id enum ext_slave_type to determine which index to use
+ *
+ *
+ * This fucntion configures the slaves by:
+ * 1) Setting up the read
+ * a) Read Register
+ * b) Read Length
+ * 2) Set up the data trigger (MPU6050 only)
+ * a) Set trigger write register
+ * b) Set Trigger write value
+ * 3) Set up the divider (MPU6050 only)
+ * 4) Set the slave bypass mode depending on slave
+ *
+ * returns INV_SUCCESS or non-zero error code
+ */
+static int mpu_set_slave_mpu3050(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *slave_pdata,
+ int slave_id)
+{
+ int result;
+ unsigned char reg;
+ unsigned char slave_reg;
+ unsigned char slave_len;
+ unsigned char slave_endian;
+ unsigned char slave_address;
+
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, TRUE);
+
+ if (NULL == slave || NULL == slave_pdata) {
+ slave_reg = 0;
+ slave_len = 0;
+ slave_endian = 0;
+ slave_address = 0;
+ mldl_cfg->i2c_slaves_enabled = 0;
+ } else {
+ slave_reg = slave->read_reg;
+ slave_len = slave->read_len;
+ slave_endian = slave->endian;
+ slave_address = slave_pdata->address;
+ mldl_cfg->i2c_slaves_enabled = 1;
+ }
+
+ /* Address */
+ result = inv_serial_single_write(gyro_handle,
+ mldl_cfg->addr,
+ MPUREG_AUX_SLV_ADDR, slave_address);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ /* Register */
+ result = inv_serial_read(gyro_handle, mldl_cfg->addr,
+ MPUREG_ACCEL_BURST_ADDR, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ reg = ((reg & 0x80) | slave_reg);
+ result = inv_serial_single_write(gyro_handle,
+ mldl_cfg->addr,
+ MPUREG_ACCEL_BURST_ADDR, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Length */
+ result = inv_serial_read(gyro_handle, mldl_cfg->addr,
+ MPUREG_USER_CTRL, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ reg = (reg & ~BIT_AUX_RD_LENG);
+ result = inv_serial_single_write(gyro_handle,
+ mldl_cfg->addr, MPUREG_USER_CTRL, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ return result;
+}
+
+
+static int mpu_set_slave(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *slave_pdata,
+ int slave_id)
+{
+ return mpu_set_slave_mpu3050(mldl_cfg, gyro_handle, slave,
+ slave_pdata, slave_id);
+}
+/**
+ * Check to see if the gyro was reset by testing a couple of registers known
+ * to change on reset.
+ *
+ * @mldl_cfg mldl configuration structure
+ * @gyro_handle handle used to communicate with the gyro
+ *
+ * @return INV_SUCCESS or non-zero error code
+ */
+static int mpu_was_reset(struct mldl_cfg *mldl_cfg, void *gyro_handle)
+{
+ int result = INV_SUCCESS;
+ unsigned char reg;
+
+ result = inv_serial_read(gyro_handle, mldl_cfg->addr,
+ MPUREG_DMP_CFG_2, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (mldl_cfg->dmp_cfg2 != reg)
+ return TRUE;
+
+ if (0 != mldl_cfg->dmp_cfg1)
+ return FALSE;
+
+ result = inv_serial_read(gyro_handle, mldl_cfg->addr,
+ MPUREG_SMPLRT_DIV, 1, &reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if (reg != mldl_cfg->divider)
+ return TRUE;
+
+ if (0 != mldl_cfg->divider)
+ return FALSE;
+
+ /* Inconclusive assume it was reset */
+ return TRUE;
+}
+
+static int gyro_resume(struct mldl_cfg *mldl_cfg, void *gyro_handle,
+ unsigned long sensors)
+{
+ int result;
+ int ii;
+ int jj;
+ unsigned char reg;
+ unsigned char regs[7];
+
+ /* Wake up the part */
+ result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, FALSE, FALSE,
+ !(sensors & INV_X_GYRO),
+ !(sensors & INV_Y_GYRO),
+ !(sensors & INV_Z_GYRO));
+
+ if (!mldl_cfg->gyro_needs_reset &&
+ !mpu_was_reset(mldl_cfg, gyro_handle)) {
+ return INV_SUCCESS;
+ }
+
+ result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle, TRUE, FALSE,
+ !(sensors & INV_X_GYRO),
+ !(sensors & INV_Y_GYRO),
+ !(sensors & INV_Z_GYRO));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(gyro_handle, mldl_cfg->addr,
+ MPUREG_INT_CFG,
+ (mldl_cfg->int_config |
+ mldl_cfg->pdata->int_config));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = mpu_set_clock_source(gyro_handle, mldl_cfg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(gyro_handle, mldl_cfg->addr,
+ MPUREG_SMPLRT_DIV, mldl_cfg->divider);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ reg = DLPF_FS_SYNC_VALUE(mldl_cfg->ext_sync,
+ mldl_cfg->full_scale, mldl_cfg->lpf);
+ result = inv_serial_single_write(gyro_handle, mldl_cfg->addr,
+ MPUREG_DLPF_FS_SYNC, reg);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(gyro_handle, mldl_cfg->addr,
+ MPUREG_DMP_CFG_1, mldl_cfg->dmp_cfg1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(gyro_handle, mldl_cfg->addr,
+ MPUREG_DMP_CFG_2, mldl_cfg->dmp_cfg2);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Write and verify memory */
+ for (ii = 0; ii < MPU_MEM_NUM_RAM_BANKS; ii++) {
+ unsigned char read[MPU_MEM_BANK_SIZE];
+
+ result = inv_serial_write_mem(gyro_handle,
+ mldl_cfg->addr,
+ ((ii << 8) | 0x00),
+ MPU_MEM_BANK_SIZE,
+ mldl_cfg->ram[ii]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read_mem(gyro_handle, mldl_cfg->addr,
+ ((ii << 8) | 0x00),
+ MPU_MEM_BANK_SIZE, read);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+#define ML_SKIP_CHECK 20
+ for (jj = 0; jj < MPU_MEM_BANK_SIZE; jj++) {
+ /* skip the register memory locations */
+ if (ii == 0 && jj < ML_SKIP_CHECK)
+ continue;
+ if (mldl_cfg->ram[ii][jj] != read[jj]) {
+ result = INV_ERROR_SERIAL_WRITE;
+ break;
+ }
+ }
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ result = inv_serial_single_write(gyro_handle, mldl_cfg->addr,
+ MPUREG_XG_OFFS_TC,
+ mldl_cfg->offset_tc[0]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(gyro_handle, mldl_cfg->addr,
+ MPUREG_YG_OFFS_TC,
+ mldl_cfg->offset_tc[1]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_single_write(gyro_handle, mldl_cfg->addr,
+ MPUREG_ZG_OFFS_TC,
+ mldl_cfg->offset_tc[2]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ regs[0] = MPUREG_X_OFFS_USRH;
+ for (ii = 0; ii < ARRAY_SIZE(mldl_cfg->offset); ii++) {
+ regs[1 + ii * 2] = (unsigned char)(mldl_cfg->offset[ii] >> 8)
+ & 0xff;
+ regs[1 + ii * 2 + 1] =
+ (unsigned char)(mldl_cfg->offset[ii] & 0xff);
+ }
+ result = inv_serial_write(gyro_handle, mldl_cfg->addr, 7, regs);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Configure slaves */
+ result = inv_mpu_set_level_shifter_bit(mldl_cfg, gyro_handle,
+ mldl_cfg->pdata->level_shifter);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ return result;
+}
+
+/*******************************************************************************
+ *******************************************************************************
+ * Exported functions
+ *******************************************************************************
+ ******************************************************************************/
+
+/**
+ * Initializes the pdata structure to defaults.
+ *
+ * Opens the device to read silicon revision, product id and whoami.
+ *
+ * @mldl_cfg
+ * The internal device configuration data structure.
+ * @mlsl_handle
+ * The serial communication handle.
+ *
+ * @return INV_SUCCESS if silicon revision, product id and woami are supported
+ * by this software.
+ */
+int inv_mpu_open(struct mldl_cfg *mldl_cfg,
+ void *mlsl_handle,
+ void *accel_handle,
+ void *compass_handle, void *pressure_handle)
+{
+ int result;
+ /* Default is Logic HIGH, pushpull, latch disabled, anyread to clear */
+ mldl_cfg->ignore_system_suspend = FALSE;
+ mldl_cfg->int_config = BIT_DMP_INT_EN;
+ mldl_cfg->clk_src = MPU_CLK_SEL_PLLGYROZ;
+ mldl_cfg->lpf = MPU_FILTER_42HZ;
+ mldl_cfg->full_scale = MPU_FS_2000DPS;
+ mldl_cfg->divider = 4;
+ mldl_cfg->dmp_enable = 1;
+ mldl_cfg->fifo_enable = 1;
+ mldl_cfg->ext_sync = 0;
+ mldl_cfg->dmp_cfg1 = 0;
+ mldl_cfg->dmp_cfg2 = 0;
+ mldl_cfg->i2c_slaves_enabled = 0;
+ mldl_cfg->dmp_is_running = FALSE;
+ mldl_cfg->gyro_is_suspended = TRUE;
+ mldl_cfg->accel_is_suspended = TRUE;
+ mldl_cfg->compass_is_suspended = TRUE;
+ mldl_cfg->pressure_is_suspended = TRUE;
+ mldl_cfg->gyro_needs_reset = FALSE;
+ if (mldl_cfg->addr == 0)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ /*
+ * Reset,
+ * Take the DMP out of sleep, and
+ * read the product_id, sillicon rev and whoami
+ */
+ mldl_cfg->gyro_is_bypassed = TRUE;
+ result = mpu3050_pwr_mgmt(mldl_cfg, mlsl_handle, RESET, 0, 0, 0, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = inv_get_silicon_rev(mldl_cfg, mlsl_handle);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Get the factory temperature compensation offsets */
+ result = inv_serial_read(mlsl_handle, mldl_cfg->addr,
+ MPUREG_XG_OFFS_TC, 1, &mldl_cfg->offset_tc[0]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(mlsl_handle, mldl_cfg->addr,
+ MPUREG_YG_OFFS_TC, 1, &mldl_cfg->offset_tc[1]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = inv_serial_read(mlsl_handle, mldl_cfg->addr,
+ MPUREG_ZG_OFFS_TC, 1, &mldl_cfg->offset_tc[2]);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* Into bypass mode before sleeping and calling the slaves init */
+ result = mpu_set_i2c_bypass(mldl_cfg, mlsl_handle, TRUE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ result = mpu3050_pwr_mgmt(mldl_cfg, mlsl_handle, 0, SLEEP, 0, 0, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (mldl_cfg->accel && mldl_cfg->accel->init) {
+ result = mldl_cfg->accel->init(accel_handle,
+ mldl_cfg->accel,
+ &mldl_cfg->pdata->accel);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ if (mldl_cfg->compass && mldl_cfg->compass->init) {
+ result = mldl_cfg->compass->init(compass_handle,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass);
+ if (INV_SUCCESS != result) {
+ MPL_LOGE("mldl_cfg->compass->init returned %d\n",
+ result);
+ goto out_accel;
+ }
+ }
+ if (mldl_cfg->pressure && mldl_cfg->pressure->init) {
+ result = mldl_cfg->pressure->init(pressure_handle,
+ mldl_cfg->pressure,
+ &mldl_cfg->pdata->pressure);
+ if (INV_SUCCESS != result) {
+ MPL_LOGE("mldl_cfg->pressure->init returned %d\n",
+ result);
+ goto out_compass;
+ }
+ }
+
+ mldl_cfg->requested_sensors = INV_THREE_AXIS_GYRO;
+ if (mldl_cfg->accel && mldl_cfg->accel->resume)
+ mldl_cfg->requested_sensors |= INV_THREE_AXIS_ACCEL;
+
+ if (mldl_cfg->compass && mldl_cfg->compass->resume)
+ mldl_cfg->requested_sensors |= INV_THREE_AXIS_COMPASS;
+
+ if (mldl_cfg->pressure && mldl_cfg->pressure->resume)
+ mldl_cfg->requested_sensors |= INV_THREE_AXIS_PRESSURE;
+
+ return result;
+
+ out_compass:
+ if (mldl_cfg->compass->init)
+ mldl_cfg->compass->exit(compass_handle,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass);
+ out_accel:
+ if (mldl_cfg->accel->init)
+ mldl_cfg->accel->exit(accel_handle,
+ mldl_cfg->accel, &mldl_cfg->pdata->accel);
+ return result;
+}
+
+/**
+ * Close the mpu interface
+ *
+ * @mldl_cfg pointer to the configuration structure
+ * @mlsl_handle pointer to the serial layer handle
+ *
+ * @return INV_SUCCESS or non-zero error code
+ */
+int inv_mpu_close(struct mldl_cfg *mldl_cfg,
+ void *mlsl_handle,
+ void *accel_handle,
+ void *compass_handle,
+ void *pressure_handle)
+{
+ int result = INV_SUCCESS;
+ int ret_result = INV_SUCCESS;
+
+ if (mldl_cfg->accel && mldl_cfg->accel->exit) {
+ result = mldl_cfg->accel->exit(accel_handle,
+ mldl_cfg->accel,
+ &mldl_cfg->pdata->accel);
+ if (INV_SUCCESS != result)
+ MPL_LOGE("Accel exit failed %d\n", result);
+ ret_result = result;
+ }
+ if (INV_SUCCESS == ret_result)
+ ret_result = result;
+
+ if (mldl_cfg->compass && mldl_cfg->compass->exit) {
+ result = mldl_cfg->compass->exit(compass_handle,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass);
+ if (INV_SUCCESS != result)
+ MPL_LOGE("Compass exit failed %d\n", result);
+ }
+ if (INV_SUCCESS == ret_result)
+ ret_result = result;
+
+ if (mldl_cfg->pressure && mldl_cfg->pressure->exit) {
+ result = mldl_cfg->pressure->exit(pressure_handle,
+ mldl_cfg->pressure,
+ &mldl_cfg->pdata->pressure);
+ if (INV_SUCCESS != result)
+ MPL_LOGE("Pressure exit failed %d\n", result);
+ }
+ if (INV_SUCCESS == ret_result)
+ ret_result = result;
+
+ return ret_result;
+}
+
+/**
+ * @brief resume the MPU device and all the other sensor
+ * devices from their low power state.
+ *
+ * @mldl_cfg
+ * pointer to the configuration structure
+ * @gyro_handle
+ * the main file handle to the MPU device.
+ * @accel_handle
+ * an handle to the accelerometer device, if sitting
+ * onto a separate bus. Can match mlsl_handle if
+ * the accelerometer device operates on the same
+ * primary bus of MPU.
+ * @compass_handle
+ * an handle to the compass device, if sitting
+ * onto a separate bus. Can match mlsl_handle if
+ * the compass device operates on the same
+ * primary bus of MPU.
+ * @pressure_handle
+ * an handle to the pressure sensor device, if sitting
+ * onto a separate bus. Can match mlsl_handle if
+ * the pressure sensor device operates on the same
+ * primary bus of MPU.
+ * @resume_gyro
+ * whether resuming the gyroscope device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @resume_accel
+ * whether resuming the accelerometer device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @resume_compass
+ * whether resuming the compass device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @resume_pressure
+ * whether resuming the pressure sensor device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @return INV_SUCCESS or a non-zero error code.
+ */
+int inv_mpu_resume(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *accel_handle,
+ void *compass_handle,
+ void *pressure_handle,
+ unsigned long sensors)
+{
+ bool resume_dmp = sensors & INV_DMP_PROCESSOR;
+ bool resume_gyro = sensors & INV_THREE_AXIS_GYRO;
+ bool resume_accel = sensors & INV_THREE_AXIS_ACCEL;
+ bool resume_compass = sensors & INV_THREE_AXIS_COMPASS;
+ bool resume_pressure = sensors & INV_THREE_AXIS_PRESSURE;
+ int result = INV_SUCCESS;
+
+#ifdef CONFIG_MPU_SENSORS_DEBUG
+ mpu_print_cfg(mldl_cfg);
+#endif
+
+ if (resume_accel && ((!mldl_cfg->accel) || (!mldl_cfg->accel->resume)))
+ return INV_ERROR_INVALID_PARAMETER;
+ if (resume_compass &&
+ ((!mldl_cfg->compass) || (!mldl_cfg->compass->resume)))
+ return INV_ERROR_INVALID_PARAMETER;
+ if (resume_pressure &&
+ ((!mldl_cfg->pressure) || (!mldl_cfg->pressure->resume)))
+ return INV_ERROR_INVALID_PARAMETER;
+
+ if (resume_gyro && mldl_cfg->gyro_is_suspended) {
+ result = gyro_resume(mldl_cfg, gyro_handle, sensors);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ if (resume_accel && mldl_cfg->accel_is_suspended) {
+ if (EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle,
+ TRUE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ result = mldl_cfg->accel->resume(accel_handle,
+ mldl_cfg->accel,
+ &mldl_cfg->pdata->accel);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ mldl_cfg->accel_is_suspended = FALSE;
+ }
+
+ if (resume_dmp && !mldl_cfg->accel_is_suspended &&
+ EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) {
+ result = mpu_set_slave(mldl_cfg,
+ gyro_handle,
+ mldl_cfg->accel,
+ &mldl_cfg->pdata->accel,
+ mldl_cfg->accel->type);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ if (resume_compass && mldl_cfg->compass_is_suspended) {
+ if (EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle,
+ TRUE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ result = mldl_cfg->compass->resume(compass_handle,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ mldl_cfg->compass_is_suspended = FALSE;
+ }
+
+ if (resume_dmp && !mldl_cfg->compass_is_suspended &&
+ EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) {
+ result = mpu_set_slave(mldl_cfg,
+ gyro_handle,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass,
+ mldl_cfg->compass->type);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ if (resume_pressure && mldl_cfg->pressure_is_suspended) {
+ if (EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle,
+ TRUE);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ result = mldl_cfg->pressure->resume(pressure_handle,
+ mldl_cfg->pressure,
+ &mldl_cfg->pdata->pressure);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ mldl_cfg->pressure_is_suspended = FALSE;
+ }
+
+ if (resume_dmp && !mldl_cfg->pressure_is_suspended &&
+ EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) {
+ result = mpu_set_slave(mldl_cfg,
+ gyro_handle,
+ mldl_cfg->pressure,
+ &mldl_cfg->pdata->pressure,
+ mldl_cfg->pressure->type);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ /* Turn on the master i2c iterface if necessary */
+ if (resume_dmp) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle,
+ !(mldl_cfg->i2c_slaves_enabled));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ /* Now start */
+ if (resume_dmp) {
+ result = dmp_start(mldl_cfg, gyro_handle);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ return result;
+}
+
+/**
+ * @brief suspend the MPU device and all the other sensor
+ * devices into their low power state.
+ * @gyro_handle
+ * the main file handle to the MPU device.
+ * @accel_handle
+ * an handle to the accelerometer device, if sitting
+ * onto a separate bus. Can match gyro_handle if
+ * the accelerometer device operates on the same
+ * primary bus of MPU.
+ * @compass_handle
+ * an handle to the compass device, if sitting
+ * onto a separate bus. Can match gyro_handle if
+ * the compass device operates on the same
+ * primary bus of MPU.
+ * @pressure_handle
+ * an handle to the pressure sensor device, if sitting
+ * onto a separate bus. Can match gyro_handle if
+ * the pressure sensor device operates on the same
+ * primary bus of MPU.
+ * @accel
+ * whether suspending the accelerometer device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @compass
+ * whether suspending the compass device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @pressure
+ * whether suspending the pressure sensor device is
+ * actually needed (if the device supports low power
+ * mode of some sort).
+ * @return INV_SUCCESS or a non-zero error code.
+ */
+int inv_mpu_suspend(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *accel_handle,
+ void *compass_handle,
+ void *pressure_handle,
+ unsigned long sensors)
+{
+ int result = INV_SUCCESS;
+ bool suspend_dmp = ((sensors & INV_DMP_PROCESSOR) == INV_DMP_PROCESSOR);
+ bool suspend_gyro = ((sensors & (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO))
+ == (INV_X_GYRO | INV_Y_GYRO | INV_Z_GYRO));
+ bool suspend_accel = ((sensors & INV_THREE_AXIS_ACCEL) ==
+ INV_THREE_AXIS_ACCEL);
+ bool suspend_compass = ((sensors & INV_THREE_AXIS_COMPASS) ==
+ INV_THREE_AXIS_COMPASS);
+ bool suspend_pressure = ((sensors & INV_THREE_AXIS_PRESSURE) ==
+ INV_THREE_AXIS_PRESSURE);
+
+ if (suspend_dmp) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ result = dmp_stop(mldl_cfg, gyro_handle);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ /* Gyro */
+ if (suspend_gyro && !mldl_cfg->gyro_is_suspended) {
+ result = mpu3050_pwr_mgmt(mldl_cfg, gyro_handle,
+ 0, suspend_dmp && suspend_gyro,
+ (sensors & INV_X_GYRO),
+ (sensors & INV_Y_GYRO),
+ (sensors & INV_Z_GYRO));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ /* Accel */
+ if (!mldl_cfg->accel_is_suspended && suspend_accel &&
+ mldl_cfg->accel && mldl_cfg->accel->suspend) {
+ if (EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ result = mldl_cfg->accel->suspend(accel_handle,
+ mldl_cfg->accel,
+ &mldl_cfg->pdata->accel);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if (EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->accel.bus) {
+ result = mpu_set_slave(mldl_cfg, gyro_handle,
+ NULL, NULL,
+ mldl_cfg->accel->type);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ mldl_cfg->accel_is_suspended = TRUE;
+ }
+
+ /* Compass */
+ if (!mldl_cfg->compass_is_suspended && suspend_compass &&
+ mldl_cfg->compass && mldl_cfg->compass->suspend) {
+ if (EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ result = mldl_cfg->compass->suspend(compass_handle,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if (EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->compass.bus) {
+ result = mpu_set_slave(mldl_cfg, gyro_handle,
+ NULL, NULL,
+ mldl_cfg->compass->type);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ mldl_cfg->compass_is_suspended = TRUE;
+ }
+ /* Pressure */
+ if (!mldl_cfg->pressure_is_suspended && suspend_pressure &&
+ mldl_cfg->pressure && mldl_cfg->pressure->suspend) {
+ if (EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ result = mldl_cfg->pressure->suspend(
+ pressure_handle,
+ mldl_cfg->pressure,
+ &mldl_cfg->pdata->pressure);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ if (EXT_SLAVE_BUS_SECONDARY == mldl_cfg->pdata->pressure.bus) {
+ result = mpu_set_slave(mldl_cfg, gyro_handle,
+ NULL, NULL,
+ mldl_cfg->pressure->type);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ mldl_cfg->pressure_is_suspended = TRUE;
+ }
+
+ /* Re-enable the i2c master if there are configured slaves and DMP */
+ if (!suspend_dmp) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle,
+ !(mldl_cfg->i2c_slaves_enabled));
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ return result;
+}
+
+int inv_mpu_slave_read(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *slave_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result;
+ int bypass_result;
+ int remain_bypassed = TRUE;
+
+ if (NULL == slave || NULL == slave->read) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION);
+ return INV_ERROR_INVALID_CONFIGURATION;
+ }
+
+ if ((EXT_SLAVE_BUS_SECONDARY == pdata->bus)
+ && (!mldl_cfg->gyro_is_bypassed)) {
+ remain_bypassed = FALSE;
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ result = slave->read(slave_handle, slave, pdata, data);
+
+ if (!remain_bypassed) {
+ bypass_result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0);
+ if (bypass_result) {
+ LOG_RESULT_LOCATION(bypass_result);
+ return bypass_result;
+ }
+ }
+ return result;
+}
+
+int inv_mpu_slave_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *slave_handle,
+ struct ext_slave_config *data,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ int remain_bypassed = TRUE;
+
+ if (NULL == slave || NULL == slave->config) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION);
+ return INV_ERROR_INVALID_CONFIGURATION;
+ }
+
+ if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus)
+ && (!mldl_cfg->gyro_is_bypassed)) {
+ remain_bypassed = FALSE;
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ result = slave->config(slave_handle, slave, pdata, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (!remain_bypassed) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ return result;
+}
+
+int inv_mpu_get_slave_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *slave_handle,
+ struct ext_slave_config *data,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ int remain_bypassed = TRUE;
+
+ if (NULL == slave || NULL == slave->get_config) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_CONFIGURATION);
+ return INV_ERROR_INVALID_CONFIGURATION;
+ }
+
+ if (data->apply && (EXT_SLAVE_BUS_SECONDARY == pdata->bus)
+ && (!mldl_cfg->gyro_is_bypassed)) {
+ remain_bypassed = FALSE;
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 1);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+
+ result = slave->get_config(slave_handle, slave, pdata, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ if (!remain_bypassed) {
+ result = mpu_set_i2c_bypass(mldl_cfg, gyro_handle, 0);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ }
+ return result;
+}
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/mldl_cfg.h b/drivers/misc/inv_mpu/mldl_cfg.h
new file mode 100644
index 0000000..9306a26
--- /dev/null
+++ b/drivers/misc/inv_mpu/mldl_cfg.h
@@ -0,0 +1,329 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @addtogroup MLDL
+ *
+ * @{
+ * @file mldl_cfg.h
+ * @brief The Motion Library Driver Layer Configuration header file.
+ */
+
+#ifndef __MLDL_CFG_H__
+#define __MLDL_CFG_H__
+
+#include "mltypes.h"
+#include "mlsl.h"
+#include <linux/mpu.h>
+# include "mpu3050.h"
+
+#include "log.h"
+
+/*************************************************************************
+ * Sensors
+ *************************************************************************/
+
+#define INV_X_GYRO (0x0001)
+#define INV_Y_GYRO (0x0002)
+#define INV_Z_GYRO (0x0004)
+#define INV_DMP_PROCESSOR (0x0008)
+
+#define INV_X_ACCEL (0x0010)
+#define INV_Y_ACCEL (0x0020)
+#define INV_Z_ACCEL (0x0040)
+
+#define INV_X_COMPASS (0x0080)
+#define INV_Y_COMPASS (0x0100)
+#define INV_Z_COMPASS (0x0200)
+
+#define INV_X_PRESSURE (0x0300)
+#define INV_Y_PRESSURE (0x0800)
+#define INV_Z_PRESSURE (0x1000)
+
+#define INV_TEMPERATURE (0x2000)
+#define INV_TIME (0x4000)
+
+#define INV_THREE_AXIS_GYRO (0x000F)
+#define INV_THREE_AXIS_ACCEL (0x0070)
+#define INV_THREE_AXIS_COMPASS (0x0380)
+#define INV_THREE_AXIS_PRESSURE (0x1C00)
+
+#define INV_FIVE_AXIS (0x007B)
+#define INV_SIX_AXIS_GYRO_ACCEL (0x007F)
+#define INV_SIX_AXIS_ACCEL_COMPASS (0x03F0)
+#define INV_NINE_AXIS (0x03FF)
+#define INV_ALL_SENSORS (0x7FFF)
+
+#define MPL_PROD_KEY(ver, rev) (ver * 100 + rev)
+
+/* -------------------------------------------------------------------------- */
+
+/* Platform data for the MPU */
+struct mldl_cfg {
+ /* MPU related configuration */
+ unsigned long requested_sensors;
+ unsigned char ignore_system_suspend;
+ unsigned char addr;
+ unsigned char int_config;
+ unsigned char ext_sync;
+ unsigned char full_scale;
+ unsigned char lpf;
+ unsigned char clk_src;
+ unsigned char divider;
+ unsigned char dmp_enable;
+ unsigned char fifo_enable;
+ unsigned char dmp_cfg1;
+ unsigned char dmp_cfg2;
+ unsigned char offset_tc[GYRO_NUM_AXES];
+ unsigned short offset[GYRO_NUM_AXES];
+ unsigned char ram[MPU_MEM_NUM_RAM_BANKS][MPU_MEM_BANK_SIZE];
+
+ /* MPU Related stored status and info */
+ unsigned char product_revision;
+ unsigned char silicon_revision;
+ unsigned char product_id;
+ unsigned short gyro_sens_trim;
+#if defined CONFIG_MPU_SENSORS_MPU6050A2 || \
+ defined CONFIG_MPU_SENSORS_MPU6050B1
+ unsigned short accel_sens_trim;
+#endif
+
+ /* Driver/Kernel related state information */
+ int gyro_is_bypassed;
+ int i2c_slaves_enabled;
+ int dmp_is_running;
+ int gyro_is_suspended;
+ int accel_is_suspended;
+ int compass_is_suspended;
+ int pressure_is_suspended;
+ int gyro_needs_reset;
+
+ /* Slave related information */
+ struct ext_slave_descr *accel;
+ struct ext_slave_descr *compass;
+ struct ext_slave_descr *pressure;
+
+ /* Platform Data */
+ struct mpu_platform_data *pdata;
+};
+
+/* -------------------------------------------------------------------------- */
+
+int inv_mpu_open(struct mldl_cfg *mldl_cfg,
+ void *mlsl_handle,
+ void *accel_handle,
+ void *compass_handle,
+ void *pressure_handle);
+int inv_mpu_close(struct mldl_cfg *mldl_cfg,
+ void *mlsl_handle,
+ void *accel_handle,
+ void *compass_handle,
+ void *pressure_handle);
+int inv_mpu_resume(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *accel_handle,
+ void *compass_handle,
+ void *pressure_handle,
+ unsigned long sensors);
+int inv_mpu_suspend(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *accel_handle,
+ void *compass_handle,
+ void *pressure_handle,
+ unsigned long sensors);
+
+/* Slave Read functions */
+int inv_mpu_slave_read(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *slave_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data);
+static inline int inv_mpu_read_accel(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *accel_handle, unsigned char *data)
+{
+ if (!mldl_cfg || !(mldl_cfg->pdata)) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ return inv_mpu_slave_read(mldl_cfg, gyro_handle, accel_handle,
+ mldl_cfg->accel, &mldl_cfg->pdata->accel,
+ data);
+}
+
+static inline int inv_mpu_read_compass(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *compass_handle,
+ unsigned char *data)
+{
+ if (!mldl_cfg || !(mldl_cfg->pdata)) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ return inv_mpu_slave_read(mldl_cfg, gyro_handle, compass_handle,
+ mldl_cfg->compass, &mldl_cfg->pdata->compass,
+ data);
+}
+
+static inline int inv_mpu_read_pressure(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *pressure_handle,
+ unsigned char *data)
+{
+ if (!mldl_cfg || !(mldl_cfg->pdata)) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ return inv_mpu_slave_read(mldl_cfg, gyro_handle, pressure_handle,
+ mldl_cfg->pressure,
+ &mldl_cfg->pdata->pressure, data);
+}
+
+/* Slave Config functions */
+int inv_mpu_slave_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *slave_handle,
+ struct ext_slave_config *data,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata);
+static inline int inv_mpu_config_accel(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *accel_handle,
+ struct ext_slave_config *data)
+{
+ if (!mldl_cfg || !(mldl_cfg->pdata)) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ return inv_mpu_slave_config(mldl_cfg, gyro_handle, accel_handle, data,
+ mldl_cfg->accel, &mldl_cfg->pdata->accel);
+}
+
+static inline int inv_mpu_config_compass(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *compass_handle,
+ struct ext_slave_config *data)
+{
+ if (!mldl_cfg || !(mldl_cfg->pdata)) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ return inv_mpu_slave_config(mldl_cfg, gyro_handle, compass_handle, data,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass);
+}
+
+static inline int inv_mpu_config_pressure(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *pressure_handle,
+ struct ext_slave_config *data)
+{
+ if (!mldl_cfg || !(mldl_cfg->pdata)) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ return inv_mpu_slave_config(mldl_cfg, gyro_handle, pressure_handle,
+ data, mldl_cfg->pressure,
+ &mldl_cfg->pdata->pressure);
+}
+
+/* Slave get config functions */
+int inv_mpu_get_slave_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *slave_handle,
+ struct ext_slave_config *data,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata);
+
+static inline int inv_mpu_get_accel_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *accel_handle,
+ struct ext_slave_config *data)
+{
+ if (!mldl_cfg || !(mldl_cfg->pdata)) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ return inv_mpu_get_slave_config(mldl_cfg, gyro_handle, accel_handle,
+ data, mldl_cfg->accel,
+ &mldl_cfg->pdata->accel);
+}
+
+static inline int inv_mpu_get_compass_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *compass_handle,
+ struct ext_slave_config *data)
+{
+ if (!mldl_cfg || !(mldl_cfg->pdata)) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ return inv_mpu_get_slave_config(mldl_cfg, gyro_handle, compass_handle,
+ data, mldl_cfg->compass,
+ &mldl_cfg->pdata->compass);
+}
+
+static inline int inv_mpu_get_pressure_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_handle,
+ void *pressure_handle,
+ struct ext_slave_config *data)
+{
+ if (!mldl_cfg || !(mldl_cfg->pdata)) {
+ LOG_RESULT_LOCATION(INV_ERROR_INVALID_PARAMETER);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+
+ return inv_mpu_get_slave_config(mldl_cfg, gyro_handle,
+ pressure_handle, data,
+ mldl_cfg->pressure,
+ &mldl_cfg->pdata->pressure);
+}
+
+/* -------------------------------------------------------------------------- */
+
+static inline long inv_mpu_get_sampling_rate_hz(struct mldl_cfg *mldl_cfg)
+{
+ if (((mldl_cfg->lpf) == 0) || ((mldl_cfg->lpf) == 7))
+ return 8000L / (mldl_cfg->divider + 1);
+ else
+ return 1000L / (mldl_cfg->divider + 1);
+}
+
+static inline long inv_mpu_get_sampling_period_us(struct mldl_cfg *mldl_cfg)
+{
+ if (((mldl_cfg->lpf) == 0) || ((mldl_cfg->lpf) == 7))
+ return (long) (1000000L * (mldl_cfg->divider + 1)) / 8000L;
+ else
+ return (long) (1000000L * (mldl_cfg->divider + 1)) / 1000L;
+}
+
+#endif /* __MLDL_CFG_H__ */
+
+/**
+ *@}
+ */
diff --git a/drivers/misc/inv_mpu/mlsl-kernel.c b/drivers/misc/inv_mpu/mlsl-kernel.c
new file mode 100644
index 0000000..dd3186b
--- /dev/null
+++ b/drivers/misc/inv_mpu/mlsl-kernel.c
@@ -0,0 +1,389 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+#include "mlsl.h"
+#include <linux/i2c.h>
+# include "mpu3050.h"
+
+static int inv_i2c_write(struct i2c_adapter *i2c_adap,
+ unsigned char address,
+ unsigned int len, unsigned char const *data)
+{
+ struct i2c_msg msgs[1];
+ int res;
+
+ if (!data || !i2c_adap)
+ return -EINVAL;
+
+ msgs[0].addr = address;
+ msgs[0].flags = 0; /* write */
+ msgs[0].buf = (unsigned char *)data;
+ msgs[0].len = len;
+
+ res = i2c_transfer(i2c_adap, msgs, 1);
+ if (res < 1) {
+ if (res == 0)
+ res = -EIO;
+ return res;
+ } else
+ return 0;
+}
+
+static int inv_i2c_write_register(struct i2c_adapter *i2c_adap,
+ unsigned char address,
+ unsigned char reg, unsigned char value)
+{
+ unsigned char data[2];
+
+ data[0] = reg;
+ data[1] = value;
+ return inv_i2c_write(i2c_adap, address, 2, data);
+}
+
+static int inv_i2c_read(struct i2c_adapter *i2c_adap,
+ unsigned char address, unsigned char reg,
+ unsigned int len, unsigned char *data)
+{
+ struct i2c_msg msgs[2];
+ int res;
+
+ if (!data || !i2c_adap)
+ return -EINVAL;
+
+ msgs[0].addr = address;
+ msgs[0].flags = 0; /* write */
+ msgs[0].buf = &reg;
+ msgs[0].len = 1;
+
+ msgs[1].addr = address;
+ msgs[1].flags = I2C_M_RD;
+ msgs[1].buf = data;
+ msgs[1].len = len;
+
+ res = i2c_transfer(i2c_adap, msgs, 2);
+ if (res < 2) {
+ if (res >= 0)
+ res = -EIO;
+ return res;
+ } else
+ return 0;
+}
+
+static int mpu_memory_read(struct i2c_adapter *i2c_adap,
+ unsigned char mpu_addr,
+ unsigned short mem_addr,
+ unsigned int len, unsigned char *data)
+{
+ unsigned char bank[2];
+ unsigned char addr[2];
+ unsigned char buf;
+
+ struct i2c_msg msgs[4];
+ int res;
+
+ if (!data || !i2c_adap)
+ return -EINVAL;
+
+ bank[0] = MPUREG_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = MPUREG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf = MPUREG_MEM_R_W;
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = &buf;
+ msgs[2].len = 1;
+
+ msgs[3].addr = mpu_addr;
+ msgs[3].flags = I2C_M_RD;
+ msgs[3].buf = data;
+ msgs[3].len = len;
+
+ res = i2c_transfer(i2c_adap, msgs, 4);
+ if (res != 4) {
+ if (res >= 0)
+ res = -EIO;
+ return res;
+ } else
+ return 0;
+}
+
+static int mpu_memory_write(struct i2c_adapter *i2c_adap,
+ unsigned char mpu_addr,
+ unsigned short mem_addr,
+ unsigned int len, unsigned char const *data)
+{
+ unsigned char bank[2];
+ unsigned char addr[2];
+ unsigned char buf[513];
+
+ struct i2c_msg msgs[3];
+ int res;
+
+ if (!data || !i2c_adap)
+ return -EINVAL;
+ if (len >= (sizeof(buf) - 1))
+ return -ENOMEM;
+
+ bank[0] = MPUREG_BANK_SEL;
+ bank[1] = mem_addr >> 8;
+
+ addr[0] = MPUREG_MEM_START_ADDR;
+ addr[1] = mem_addr & 0xFF;
+
+ buf[0] = MPUREG_MEM_R_W;
+ memcpy(buf + 1, data, len);
+
+ /* write message */
+ msgs[0].addr = mpu_addr;
+ msgs[0].flags = 0;
+ msgs[0].buf = bank;
+ msgs[0].len = sizeof(bank);
+
+ msgs[1].addr = mpu_addr;
+ msgs[1].flags = 0;
+ msgs[1].buf = addr;
+ msgs[1].len = sizeof(addr);
+
+ msgs[2].addr = mpu_addr;
+ msgs[2].flags = 0;
+ msgs[2].buf = (unsigned char *)buf;
+ msgs[2].len = len + 1;
+
+ res = i2c_transfer(i2c_adap, msgs, 3);
+ if (res != 3) {
+ if (res >= 0)
+ res = -EIO;
+ return res;
+ } else
+ return 0;
+}
+
+int inv_serial_single_write(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned char register_addr,
+ unsigned char data)
+{
+ return inv_i2c_write_register((struct i2c_adapter *)sl_handle,
+ slave_addr, register_addr, data);
+}
+EXPORT_SYMBOL(inv_serial_single_write);
+
+int inv_serial_write(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short length,
+ unsigned char const *data)
+{
+ int result;
+ const unsigned short data_length = length - 1;
+ const unsigned char start_reg_addr = data[0];
+ unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1];
+ unsigned short bytes_written = 0;
+
+ while (bytes_written < data_length) {
+ unsigned short this_len = min(SERIAL_MAX_TRANSFER_SIZE,
+ data_length - bytes_written);
+ if (bytes_written == 0) {
+ result = inv_i2c_write((struct i2c_adapter *)
+ sl_handle, slave_addr,
+ 1 + this_len, data);
+ } else {
+ /* manually increment register addr between chunks */
+ i2c_write[0] = start_reg_addr + bytes_written;
+ memcpy(&i2c_write[1], &data[1 + bytes_written],
+ this_len);
+ result = inv_i2c_write((struct i2c_adapter *)
+ sl_handle, slave_addr,
+ 1 + this_len, i2c_write);
+ }
+ if (result)
+ return result;
+ bytes_written += this_len;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(inv_serial_write);
+
+int inv_serial_read(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned char register_addr,
+ unsigned short length,
+ unsigned char *data)
+{
+ int result;
+ unsigned short bytes_read = 0;
+
+ if (register_addr == MPUREG_FIFO_R_W || register_addr == MPUREG_MEM_R_W)
+ return INV_ERROR_INVALID_PARAMETER;
+
+ while (bytes_read < length) {
+ unsigned short this_len =
+ min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read);
+ result = inv_i2c_read((struct i2c_adapter *)sl_handle,
+ slave_addr, register_addr + bytes_read,
+ this_len, &data[bytes_read]);
+ if (result)
+ return result;
+ bytes_read += this_len;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(inv_serial_read);
+
+int inv_serial_write_mem(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short mem_addr,
+ unsigned short length,
+ unsigned char const *data)
+{
+ int result;
+ unsigned short bytes_written = 0;
+
+ if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) {
+ pr_err("memory read length (%d B) extends beyond its"
+ " limits (%d) if started at location %d\n", length,
+ MPU_MEM_BANK_SIZE, mem_addr & 0xFF);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ while (bytes_written < length) {
+ unsigned short this_len =
+ min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written);
+ result = mpu_memory_write((struct i2c_adapter *)sl_handle,
+ slave_addr, mem_addr + bytes_written,
+ this_len, &data[bytes_written]);
+ if (result)
+ return result;
+ bytes_written += this_len;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(inv_serial_write_mem);
+
+int inv_serial_read_mem(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short mem_addr,
+ unsigned short length,
+ unsigned char *data)
+{
+ int result;
+ unsigned short bytes_read = 0;
+
+ if ((mem_addr & 0xFF) + length > MPU_MEM_BANK_SIZE) {
+ printk
+ ("memory read length (%d B) extends beyond its limits (%d) "
+ "if started at location %d\n", length,
+ MPU_MEM_BANK_SIZE, mem_addr & 0xFF);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ while (bytes_read < length) {
+ unsigned short this_len =
+ min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read);
+ result =
+ mpu_memory_read((struct i2c_adapter *)sl_handle,
+ slave_addr, mem_addr + bytes_read,
+ this_len, &data[bytes_read]);
+ if (result)
+ return result;
+ bytes_read += this_len;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(inv_serial_read_mem);
+
+int inv_serial_write_fifo(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short length,
+ unsigned char const *data)
+{
+ int result;
+ unsigned char i2c_write[SERIAL_MAX_TRANSFER_SIZE + 1];
+ unsigned short bytes_written = 0;
+
+ if (length > FIFO_HW_SIZE) {
+ printk(KERN_ERR
+ "maximum fifo write length is %d\n", FIFO_HW_SIZE);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ while (bytes_written < length) {
+ unsigned short this_len =
+ min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_written);
+ i2c_write[0] = MPUREG_FIFO_R_W;
+ memcpy(&i2c_write[1], &data[bytes_written], this_len);
+ result = inv_i2c_write((struct i2c_adapter *)sl_handle,
+ slave_addr, this_len + 1, i2c_write);
+ if (result)
+ return result;
+ bytes_written += this_len;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(inv_serial_write_fifo);
+
+int inv_serial_read_fifo(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short length,
+ unsigned char *data)
+{
+ int result;
+ unsigned short bytes_read = 0;
+
+ if (length > FIFO_HW_SIZE) {
+ printk(KERN_ERR
+ "maximum fifo read length is %d\n", FIFO_HW_SIZE);
+ return INV_ERROR_INVALID_PARAMETER;
+ }
+ while (bytes_read < length) {
+ unsigned short this_len =
+ min(SERIAL_MAX_TRANSFER_SIZE, length - bytes_read);
+ result = inv_i2c_read((struct i2c_adapter *)sl_handle,
+ slave_addr, MPUREG_FIFO_R_W, this_len,
+ &data[bytes_read]);
+ if (result)
+ return result;
+ bytes_read += this_len;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(inv_serial_read_fifo);
+
+/**
+ * @}
+ */
diff --git a/drivers/misc/inv_mpu/mlsl.h b/drivers/misc/inv_mpu/mlsl.h
new file mode 100644
index 0000000..9e29ce6
--- /dev/null
+++ b/drivers/misc/inv_mpu/mlsl.h
@@ -0,0 +1,186 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+#ifndef __MLSL_H__
+#define __MLSL_H__
+
+/**
+ * @defgroup MLSL
+ * @brief Motion Library - Serial Layer.
+ * The Motion Library System Layer provides the Motion Library
+ * with the communication interface to the hardware.
+ *
+ * The communication interface is assumed to support serial
+ * transfers in burst of variable length up to
+ * SERIAL_MAX_TRANSFER_SIZE.
+ * The default value for SERIAL_MAX_TRANSFER_SIZE is 128 bytes.
+ * Transfers of length greater than SERIAL_MAX_TRANSFER_SIZE, will
+ * be subdivided in smaller transfers of length <=
+ * SERIAL_MAX_TRANSFER_SIZE.
+ * The SERIAL_MAX_TRANSFER_SIZE definition can be modified to
+ * overcome any host processor transfer size limitation down to
+ * 1 B, the minimum.
+ * An higher value for SERIAL_MAX_TRANSFER_SIZE will favor
+ * performance and efficiency while requiring higher resource usage
+ * (mostly buffering). A smaller value will increase overhead and
+ * decrease efficiency but allows to operate with more resource
+ * constrained processor and master serial controllers.
+ * The SERIAL_MAX_TRANSFER_SIZE definition can be found in the
+ * mlsl.h header file and master serial controllers.
+ * The SERIAL_MAX_TRANSFER_SIZE definition can be found in the
+ * mlsl.h header file.
+ *
+ * @{
+ * @file mlsl.h
+ * @brief The Motion Library System Layer.
+ *
+ */
+
+#include "mltypes.h"
+#include <linux/mpu.h>
+
+
+/*
+ * NOTE : to properly support Yamaha compass reads,
+ * the max transfer size should be at least 9 B.
+ * Length in bytes, typically a power of 2 >= 2
+ */
+#define SERIAL_MAX_TRANSFER_SIZE 128
+
+
+/**
+ * inv_serial_single_write() - used to write a single byte of data.
+ * @sl_handle pointer to the serial device used for the communication.
+ * @slave_addr I2C slave address of device.
+ * @register_addr Register address to write.
+ * @data Single byte of data to write.
+ *
+ * It is called by the MPL to write a single byte of data to the MPU.
+ *
+ * returns INV_SUCCESS if successful, a non-zero error code otherwise.
+ */
+int inv_serial_single_write(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned char register_addr,
+ unsigned char data);
+
+/**
+ * inv_serial_write() - used to write multiple bytes of data to registers.
+ * @sl_handle a file handle to the serial device used for the communication.
+ * @slave_addr I2C slave address of device.
+ * @register_addr Register address to write.
+ * @length Length of burst of data.
+ * @data Pointer to block of data.
+ *
+ * returns INV_SUCCESS if successful, a non-zero error code otherwise.
+ */
+int inv_serial_write(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short length,
+ unsigned char const *data);
+
+/**
+ * inv_serial_read() - used to read multiple bytes of data from registers.
+ * @sl_handle a file handle to the serial device used for the communication.
+ * @slave_addr I2C slave address of device.
+ * @register_addr Register address to read.
+ * @length Length of burst of data.
+ * @data Pointer to block of data.
+ *
+ * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise.
+ */
+int inv_serial_read(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned char register_addr,
+ unsigned short length,
+ unsigned char *data);
+
+/**
+ * inv_serial_read_mem() - used to read multiple bytes of data from the memory.
+ * This should be sent by I2C or SPI.
+ *
+ * @sl_handle a file handle to the serial device used for the communication.
+ * @slave_addr I2C slave address of device.
+ * @mem_addr The location in the memory to read from.
+ * @length Length of burst data.
+ * @data Pointer to block of data.
+ *
+ * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise.
+ */
+int inv_serial_read_mem(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short mem_addr,
+ unsigned short length,
+ unsigned char *data);
+
+/**
+ * inv_serial_write_mem() - used to write multiple bytes of data to the memory.
+ * @sl_handle a file handle to the serial device used for the communication.
+ * @slave_addr I2C slave address of device.
+ * @mem_addr The location in the memory to write to.
+ * @length Length of burst data.
+ * @data Pointer to block of data.
+ *
+ * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise.
+ */
+int inv_serial_write_mem(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short mem_addr,
+ unsigned short length,
+ unsigned char const *data);
+
+/**
+ * inv_serial_read_fifo() - used to read multiple bytes of data from the fifo.
+ * @sl_handle a file handle to the serial device used for the communication.
+ * @slave_addr I2C slave address of device.
+ * @length Length of burst of data.
+ * @data Pointer to block of data.
+ *
+ * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise.
+ */
+int inv_serial_read_fifo(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short length,
+ unsigned char *data);
+
+/**
+ * inv_serial_write_fifo() - used to write multiple bytes of data to the fifo.
+ * @sl_handle a file handle to the serial device used for the communication.
+ * @slave_addr I2C slave address of device.
+ * @length Length of burst of data.
+ * @data Pointer to block of data.
+ *
+ * returns INV_SUCCESS == 0 if successful; a non-zero error code otherwise.
+ */
+int inv_serial_write_fifo(
+ void *sl_handle,
+ unsigned char slave_addr,
+ unsigned short length,
+ unsigned char const *data);
+
+/**
+ * @}
+ */
+#endif /* __MLSL_H__ */
diff --git a/drivers/misc/inv_mpu/mltypes.h b/drivers/misc/inv_mpu/mltypes.h
new file mode 100644
index 0000000..b89087c
--- /dev/null
+++ b/drivers/misc/inv_mpu/mltypes.h
@@ -0,0 +1,229 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @defgroup MLERROR
+ * @brief Motion Library - Error definitions.
+ * Definition of the error codes used within the MPL and
+ * returned to the user.
+ * Every function tries to return a meaningful error code basing
+ * on the occuring error condition. The error code is numeric.
+ *
+ * The available error codes and their associated values are:
+ * - (0) INV_SUCCESS
+ * - (1) INV_ERROR
+ * - (2) INV_ERROR_INVALID_PARAMETER
+ * - (3) INV_ERROR_FEATURE_NOT_ENABLED
+ * - (4) INV_ERROR_FEATURE_NOT_IMPLEMENTED
+ * - (6) INV_ERROR_DMP_NOT_STARTED
+ * - (7) INV_ERROR_DMP_STARTED
+ * - (8) INV_ERROR_NOT_OPENED
+ * - (9) INV_ERROR_OPENED
+ * - (10) INV_ERROR_INVALID_MODULE
+ * - (11) INV_ERROR_MEMORY_EXAUSTED
+ * - (12) INV_ERROR_DIVIDE_BY_ZERO
+ * - (13) INV_ERROR_ASSERTION_FAILURE
+ * - (14) INV_ERROR_FILE_OPEN
+ * - (15) INV_ERROR_FILE_READ
+ * - (16) INV_ERROR_FILE_WRITE
+ * - (17) INV_ERROR_INVALID_CONFIGURATION
+ * - (20) INV_ERROR_SERIAL_CLOSED
+ * - (21) INV_ERROR_SERIAL_OPEN_ERROR
+ * - (22) INV_ERROR_SERIAL_READ
+ * - (23) INV_ERROR_SERIAL_WRITE
+ * - (24) INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED
+ * - (25) INV_ERROR_SM_TRANSITION
+ * - (26) INV_ERROR_SM_IMPROPER_STATE
+ * - (30) INV_ERROR_FIFO_OVERFLOW
+ * - (31) INV_ERROR_FIFO_FOOTER
+ * - (32) INV_ERROR_FIFO_READ_COUNT
+ * - (33) INV_ERROR_FIFO_READ_DATA
+ * - (40) INV_ERROR_MEMORY_SET
+ * - (50) INV_ERROR_LOG_MEMORY_ERROR
+ * - (51) INV_ERROR_LOG_OUTPUT_ERROR
+ * - (60) INV_ERROR_OS_BAD_PTR
+ * - (61) INV_ERROR_OS_BAD_HANDLE
+ * - (62) INV_ERROR_OS_CREATE_FAILED
+ * - (63) INV_ERROR_OS_LOCK_FAILED
+ * - (70) INV_ERROR_COMPASS_DATA_OVERFLOW
+ * - (71) INV_ERROR_COMPASS_DATA_UNDERFLOW
+ * - (72) INV_ERROR_COMPASS_DATA_NOT_READY
+ * - (73) INV_ERROR_COMPASS_DATA_ERROR
+ * - (75) INV_ERROR_CALIBRATION_LOAD
+ * - (76) INV_ERROR_CALIBRATION_STORE
+ * - (77) INV_ERROR_CALIBRATION_LEN
+ * - (78) INV_ERROR_CALIBRATION_CHECKSUM
+ * - (79) INV_ERROR_ACCEL_DATA_OVERFLOW
+ * - (80) INV_ERROR_ACCEL_DATA_UNDERFLOW
+ * - (81) INV_ERROR_ACCEL_DATA_NOT_READY
+ * - (82) INV_ERROR_ACCEL_DATA_ERROR
+ *
+ * @{
+ * @file mltypes.h
+ * @}
+ */
+
+#ifndef MLTYPES_H
+#define MLTYPES_H
+
+#include <linux/types.h>
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+/* - ML Errors. - */
+#define ERROR_NAME(x) (#x)
+#define ERROR_CHECK_FIRST(first, x) \
+ { if (INV_SUCCESS == first) first = x; }
+
+#define INV_SUCCESS (0)
+/* Generic Error code. Proprietary Error Codes only */
+#define INV_ERROR (1)
+
+/* Compatibility and other generic error codes */
+#define INV_ERROR_INVALID_PARAMETER (2)
+#define INV_ERROR_FEATURE_NOT_ENABLED (3)
+#define INV_ERROR_FEATURE_NOT_IMPLEMENTED (4)
+#define INV_ERROR_DMP_NOT_STARTED (6)
+#define INV_ERROR_DMP_STARTED (7)
+#define INV_ERROR_NOT_OPENED (8)
+#define INV_ERROR_OPENED (9)
+#define INV_ERROR_INVALID_MODULE (10)
+#define INV_ERROR_MEMORY_EXAUSTED (11)
+#define INV_ERROR_DIVIDE_BY_ZERO (12)
+#define INV_ERROR_ASSERTION_FAILURE (13)
+#define INV_ERROR_FILE_OPEN (14)
+#define INV_ERROR_FILE_READ (15)
+#define INV_ERROR_FILE_WRITE (16)
+#define INV_ERROR_INVALID_CONFIGURATION (17)
+
+/* Serial Communication */
+#define INV_ERROR_SERIAL_CLOSED (20)
+#define INV_ERROR_SERIAL_OPEN_ERROR (21)
+#define INV_ERROR_SERIAL_READ (22)
+#define INV_ERROR_SERIAL_WRITE (23)
+#define INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED (24)
+
+/* SM = State Machine */
+#define INV_ERROR_SM_TRANSITION (25)
+#define INV_ERROR_SM_IMPROPER_STATE (26)
+
+/* Fifo */
+#define INV_ERROR_FIFO_OVERFLOW (30)
+#define INV_ERROR_FIFO_FOOTER (31)
+#define INV_ERROR_FIFO_READ_COUNT (32)
+#define INV_ERROR_FIFO_READ_DATA (33)
+
+/* Memory & Registers, Set & Get */
+#define INV_ERROR_MEMORY_SET (40)
+
+#define INV_ERROR_LOG_MEMORY_ERROR (50)
+#define INV_ERROR_LOG_OUTPUT_ERROR (51)
+
+/* OS interface errors */
+#define INV_ERROR_OS_BAD_PTR (60)
+#define INV_ERROR_OS_BAD_HANDLE (61)
+#define INV_ERROR_OS_CREATE_FAILED (62)
+#define INV_ERROR_OS_LOCK_FAILED (63)
+
+/* Compass errors */
+#define INV_ERROR_COMPASS_DATA_OVERFLOW (70)
+#define INV_ERROR_COMPASS_DATA_UNDERFLOW (71)
+#define INV_ERROR_COMPASS_DATA_NOT_READY (72)
+#define INV_ERROR_COMPASS_DATA_ERROR (73)
+
+/* Load/Store calibration */
+#define INV_ERROR_CALIBRATION_LOAD (75)
+#define INV_ERROR_CALIBRATION_STORE (76)
+#define INV_ERROR_CALIBRATION_LEN (77)
+#define INV_ERROR_CALIBRATION_CHECKSUM (78)
+
+/* Accel errors */
+#define INV_ERROR_ACCEL_DATA_OVERFLOW (79)
+#define INV_ERROR_ACCEL_DATA_UNDERFLOW (80)
+#define INV_ERROR_ACCEL_DATA_NOT_READY (81)
+#define INV_ERROR_ACCEL_DATA_ERROR (82)
+
+#ifdef INV_USE_LEGACY_NAMES
+#define ML_SUCCESS INV_SUCCESS
+#define ML_ERROR INV_ERROR
+#define ML_ERROR_INVALID_PARAMETER INV_ERROR_INVALID_PARAMETER
+#define ML_ERROR_FEATURE_NOT_ENABLED INV_ERROR_FEATURE_NOT_ENABLED
+#define ML_ERROR_FEATURE_NOT_IMPLEMENTED INV_ERROR_FEATURE_NOT_IMPLEMENTED
+#define ML_ERROR_DMP_NOT_STARTED INV_ERROR_DMP_NOT_STARTED
+#define ML_ERROR_DMP_STARTED INV_ERROR_DMP_STARTED
+#define ML_ERROR_NOT_OPENED INV_ERROR_NOT_OPENED
+#define ML_ERROR_OPENED INV_ERROR_OPENED
+#define ML_ERROR_INVALID_MODULE INV_ERROR_INVALID_MODULE
+#define ML_ERROR_MEMORY_EXAUSTED INV_ERROR_MEMORY_EXAUSTED
+#define ML_ERROR_DIVIDE_BY_ZERO INV_ERROR_DIVIDE_BY_ZERO
+#define ML_ERROR_ASSERTION_FAILURE INV_ERROR_ASSERTION_FAILURE
+#define ML_ERROR_FILE_OPEN INV_ERROR_FILE_OPEN
+#define ML_ERROR_FILE_READ INV_ERROR_FILE_READ
+#define ML_ERROR_FILE_WRITE INV_ERROR_FILE_WRITE
+#define ML_ERROR_INVALID_CONFIGURATION INV_ERROR_INVALID_CONFIGURATION
+#define ML_ERROR_SERIAL_CLOSED INV_ERROR_SERIAL_CLOSED
+#define ML_ERROR_SERIAL_OPEN_ERROR INV_ERROR_SERIAL_OPEN_ERROR
+#define ML_ERROR_SERIAL_READ INV_ERROR_SERIAL_READ
+#define ML_ERROR_SERIAL_WRITE INV_ERROR_SERIAL_WRITE
+#define ML_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED \
+ INV_ERROR_SERIAL_DEVICE_NOT_RECOGNIZED
+#define ML_ERROR_SM_TRANSITION INV_ERROR_SM_TRANSITION
+#define ML_ERROR_SM_IMPROPER_STATE INV_ERROR_SM_IMPROPER_STATE
+#define ML_ERROR_FIFO_OVERFLOW INV_ERROR_FIFO_OVERFLOW
+#define ML_ERROR_FIFO_FOOTER INV_ERROR_FIFO_FOOTER
+#define ML_ERROR_FIFO_READ_COUNT INV_ERROR_FIFO_READ_COUNT
+#define ML_ERROR_FIFO_READ_DATA INV_ERROR_FIFO_READ_DATA
+#define ML_ERROR_MEMORY_SET INV_ERROR_MEMORY_SET
+#define ML_ERROR_LOG_MEMORY_ERROR INV_ERROR_LOG_MEMORY_ERROR
+#define ML_ERROR_LOG_OUTPUT_ERROR INV_ERROR_LOG_OUTPUT_ERROR
+#define ML_ERROR_OS_BAD_PTR INV_ERROR_OS_BAD_PTR
+#define ML_ERROR_OS_BAD_HANDLE INV_ERROR_OS_BAD_HANDLE
+#define ML_ERROR_OS_CREATE_FAILED INV_ERROR_OS_CREATE_FAILED
+#define ML_ERROR_OS_LOCK_FAILED INV_ERROR_OS_LOCK_FAILED
+#define ML_ERROR_COMPASS_DATA_OVERFLOW INV_ERROR_COMPASS_DATA_OVERFLOW
+#define ML_ERROR_COMPASS_DATA_UNDERFLOW INV_ERROR_COMPASS_DATA_UNDERFLOW
+#define ML_ERROR_COMPASS_DATA_NOT_READY INV_ERROR_COMPASS_DATA_NOT_READY
+#define ML_ERROR_COMPASS_DATA_ERROR INV_ERROR_COMPASS_DATA_ERROR
+#define ML_ERROR_CALIBRATION_LOAD INV_ERROR_CALIBRATION_LOAD
+#define ML_ERROR_CALIBRATION_STORE INV_ERROR_CALIBRATION_STORE
+#define ML_ERROR_CALIBRATION_LEN INV_ERROR_CALIBRATION_LEN
+#define ML_ERROR_CALIBRATION_CHECKSUM INV_ERROR_CALIBRATION_CHECKSUM
+#define ML_ERROR_ACCEL_DATA_OVERFLOW INV_ERROR_ACCEL_DATA_OVERFLOW
+#define ML_ERROR_ACCEL_DATA_UNDERFLOW INV_ERROR_ACCEL_DATA_UNDERFLOW
+#define ML_ERROR_ACCEL_DATA_NOT_READY INV_ERROR_ACCEL_DATA_NOT_READY
+#define ML_ERROR_ACCEL_DATA_ERROR INV_ERROR_ACCEL_DATA_ERROR
+#endif
+
+/* For Linux coding compliance */
+
+/*---------------------------
+ p-Types
+---------------------------*/
+
+#endif /* MLTYPES_H */
diff --git a/drivers/misc/inv_mpu/mpu-dev.c b/drivers/misc/inv_mpu/mpu-dev.c
new file mode 100644
index 0000000..74b930c
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu-dev.c
@@ -0,0 +1,1309 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/signal.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/pm.h>
+#include <linux/mutex.h>
+#include <linux/suspend.h>
+#include <linux/poll.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include "mpuirq.h"
+#include "slaveirq.h"
+#include "mlsl.h"
+#include "mldl_cfg.h"
+#include <linux/mpu.h>
+
+
+/* Platform data for the MPU */
+struct mpu_private_data {
+ struct miscdevice dev;
+ struct i2c_client *client;
+ struct mldl_cfg mldl_cfg;
+
+ struct mutex mutex;
+ wait_queue_head_t mpu_event_wait;
+ struct completion completion;
+ struct timer_list timeout;
+ struct notifier_block nb;
+ struct mpuirq_data mpu_pm_event;
+ int response_timeout; /* In seconds */
+ unsigned long event;
+ int pid;
+ struct module *slave_modules[EXT_SLAVE_NUM_TYPES];
+};
+
+struct mpu_private_data *mpu_private_data;
+
+static void mpu_pm_timeout(u_long data)
+{
+ struct mpu_private_data *mpu = (struct mpu_private_data *)data;
+ struct i2c_client *client = mpu->client;
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+ complete(&mpu->completion);
+}
+
+static int mpu_pm_notifier_callback(struct notifier_block *nb,
+ unsigned long event, void *unused)
+{
+ struct mpu_private_data *mpu =
+ container_of(nb, struct mpu_private_data, nb);
+ struct i2c_client *client = mpu->client;
+ dev_dbg(&client->adapter->dev, "%s: %ld\n", __func__, event);
+
+ /* Prevent the file handle from being closed before we initialize
+ the completion event */
+ mutex_lock(&mpu->mutex);
+ if (!(mpu->pid) ||
+ (event != PM_SUSPEND_PREPARE && event != PM_POST_SUSPEND)) {
+ mutex_unlock(&mpu->mutex);
+ return NOTIFY_OK;
+ }
+
+ if (event == PM_SUSPEND_PREPARE)
+ mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE;
+ if (event == PM_POST_SUSPEND)
+ mpu->event = MPU_PM_EVENT_POST_SUSPEND;
+
+ mpu->mpu_pm_event.irqtime = ktime_to_ns(ktime_get());
+ mpu->mpu_pm_event.interruptcount++;
+ mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT;
+ mpu->mpu_pm_event.data = mpu->event;
+
+ if (mpu->response_timeout > 0) {
+ mpu->timeout.expires = jiffies + mpu->response_timeout * HZ;
+ add_timer(&mpu->timeout);
+ }
+ INIT_COMPLETION(mpu->completion);
+ mutex_unlock(&mpu->mutex);
+
+ wake_up_interruptible(&mpu->mpu_event_wait);
+ wait_for_completion(&mpu->completion);
+ del_timer_sync(&mpu->timeout);
+ dev_dbg(&client->adapter->dev, "%s: %ld DONE\n", __func__, event);
+ return NOTIFY_OK;
+}
+
+static int mpu_dev_open(struct inode *inode, struct file *file)
+{
+ struct mpu_private_data *mpu =
+ container_of(file->private_data, struct mpu_private_data, dev);
+ struct i2c_client *client = mpu->client;
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ int result;
+ int ii;
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+ dev_dbg(&client->adapter->dev, "current->pid %d\n", current->pid);
+
+ result = mutex_lock_interruptible(&mpu->mutex);
+ if (mpu->pid) {
+ mutex_unlock(&mpu->mutex);
+ return -EBUSY;
+ }
+ mpu->pid = current->pid;
+
+ /* Reset the sensors to the default */
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "%s: mutex_lock_interruptible returned %d\n",
+ __func__, result);
+ return result;
+ }
+
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++)
+ __module_get(mpu->slave_modules[ii]);
+
+ mldl_cfg->requested_sensors = INV_THREE_AXIS_GYRO;
+ if (mldl_cfg->accel && mldl_cfg->accel->resume)
+ mldl_cfg->requested_sensors |= INV_THREE_AXIS_ACCEL;
+
+ if (mldl_cfg->compass && mldl_cfg->compass->resume)
+ mldl_cfg->requested_sensors |= INV_THREE_AXIS_COMPASS;
+
+ if (mldl_cfg->pressure && mldl_cfg->pressure->resume)
+ mldl_cfg->requested_sensors |= INV_THREE_AXIS_PRESSURE;
+ mutex_unlock(&mpu->mutex);
+ return 0;
+}
+
+/* close function - called when the "file" /dev/mpu is closed in userspace */
+static int mpu_release(struct inode *inode, struct file *file)
+{
+ struct mpu_private_data *mpu =
+ container_of(file->private_data, struct mpu_private_data, dev);
+ struct i2c_client *client = mpu->client;
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+ int result = 0;
+ int ii;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ mutex_lock(&mpu->mutex);
+ mldl_cfg->requested_sensors = 0;
+ result = inv_mpu_suspend(mldl_cfg, client->adapter,
+ accel_adapter, compass_adapter,
+ pressure_adapter, INV_ALL_SENSORS);
+ mpu->pid = 0;
+ for (ii = 0; ii < EXT_SLAVE_NUM_TYPES; ii++)
+ module_put(mpu->slave_modules[ii]);
+
+ mutex_unlock(&mpu->mutex);
+ complete(&mpu->completion);
+ dev_dbg(&client->adapter->dev, "mpu_release\n");
+
+ return result;
+}
+
+/* read function called when from /dev/mpu is read. Read from the FIFO */
+static ssize_t mpu_read(struct file *file,
+ char __user *buf, size_t count, loff_t *offset)
+{
+ struct mpu_private_data *mpu =
+ container_of(file->private_data, struct mpu_private_data, dev);
+ struct i2c_client *client = mpu->client;
+ size_t len = sizeof(mpu->mpu_pm_event) + sizeof(unsigned long);
+ int err;
+
+ if (!mpu->event && (!(file->f_flags & O_NONBLOCK)))
+ wait_event_interruptible(mpu->mpu_event_wait, mpu->event);
+
+ if (!mpu->event || !buf
+ || count < sizeof(mpu->mpu_pm_event))
+ return 0;
+
+ err = copy_to_user(buf, &mpu->mpu_pm_event, sizeof(mpu->mpu_pm_event));
+ if (err) {
+ dev_err(&client->adapter->dev,
+ "Copy to user returned %d\n", err);
+ return -EFAULT;
+ }
+ mpu->event = 0;
+ return len;
+}
+
+static unsigned int mpu_poll(struct file *file, struct poll_table_struct *poll)
+{
+ struct mpu_private_data *mpu =
+ container_of(file->private_data, struct mpu_private_data, dev);
+ int mask = 0;
+
+ poll_wait(file, &mpu->mpu_event_wait, poll);
+ if (mpu->event)
+ mask |= POLLIN | POLLRDNORM;
+ return mask;
+}
+
+static int
+mpu_dev_ioctl_set_mpu_pdata(struct i2c_client *client, unsigned long arg)
+{
+ int ii;
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mpu_platform_data *pdata = mpu->mldl_cfg.pdata;
+ struct mpu_platform_data local_pdata;
+
+ if (copy_from_user(&local_pdata, (unsigned char __user *)arg,
+ sizeof(local_pdata)))
+ return -EFAULT;
+
+ pdata->int_config = local_pdata.int_config;
+ for (ii = 0; ii < ARRAY_SIZE(pdata->orientation); ii++)
+ pdata->orientation[ii] = local_pdata.orientation[ii];
+ pdata->level_shifter = local_pdata.level_shifter;
+
+ pdata->accel.address = local_pdata.accel.address;
+ for (ii = 0; ii < ARRAY_SIZE(pdata->accel.orientation); ii++)
+ pdata->accel.orientation[ii] =
+ local_pdata.accel.orientation[ii];
+
+ pdata->compass.address = local_pdata.compass.address;
+ for (ii = 0; ii < ARRAY_SIZE(pdata->compass.orientation); ii++)
+ pdata->compass.orientation[ii] =
+ local_pdata.compass.orientation[ii];
+
+ pdata->pressure.address = local_pdata.pressure.address;
+ for (ii = 0; ii < ARRAY_SIZE(pdata->pressure.orientation); ii++)
+ pdata->pressure.orientation[ii] =
+ local_pdata.pressure.orientation[ii];
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ return 0;
+}
+
+static int
+mpu_dev_ioctl_set_mpu_config(struct i2c_client *client, unsigned long arg)
+{
+ int ii;
+ int result = 0;
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct mldl_cfg *temp_mldl_cfg;
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ temp_mldl_cfg = kmalloc(offsetof(struct mldl_cfg, silicon_revision),
+ GFP_KERNEL);
+ if (!temp_mldl_cfg)
+ return -ENOMEM;
+
+ /*
+ * User space is not allowed to modify accel compass pressure or
+ * pdata structs, as well as silicon_revision product_id or trim
+ */
+ if (copy_from_user(temp_mldl_cfg, (struct mldl_cfg __user *)arg,
+ offsetof(struct mldl_cfg, silicon_revision))) {
+ result = -EFAULT;
+ goto out;
+ }
+
+ if (mldl_cfg->gyro_is_suspended) {
+ if (mldl_cfg->addr != temp_mldl_cfg->addr)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->int_config != temp_mldl_cfg->int_config)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->ext_sync != temp_mldl_cfg->ext_sync)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->full_scale != temp_mldl_cfg->full_scale)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->lpf != temp_mldl_cfg->lpf)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->clk_src != temp_mldl_cfg->clk_src)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->divider != temp_mldl_cfg->divider)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->dmp_enable != temp_mldl_cfg->dmp_enable)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->fifo_enable != temp_mldl_cfg->fifo_enable)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->dmp_cfg1 != temp_mldl_cfg->dmp_cfg1)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (mldl_cfg->dmp_cfg2 != temp_mldl_cfg->dmp_cfg2)
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ for (ii = 0; ii < GYRO_NUM_AXES; ii++)
+ if (mldl_cfg->offset_tc[ii] !=
+ temp_mldl_cfg->offset_tc[ii])
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ for (ii = 0; ii < GYRO_NUM_AXES; ii++)
+ if (mldl_cfg->offset[ii] != temp_mldl_cfg->offset[ii])
+ mldl_cfg->gyro_needs_reset = TRUE;
+
+ if (memcmp(mldl_cfg->ram, temp_mldl_cfg->ram,
+ MPU_MEM_NUM_RAM_BANKS * MPU_MEM_BANK_SIZE *
+ sizeof(unsigned char)))
+ mldl_cfg->gyro_needs_reset = TRUE;
+ }
+
+ memcpy(mldl_cfg, temp_mldl_cfg,
+ offsetof(struct mldl_cfg, silicon_revision));
+
+ out:
+ kfree(temp_mldl_cfg);
+ return result;
+}
+
+static int
+mpu_dev_ioctl_get_mpu_config(struct i2c_client *client,
+ struct mldl_cfg __user *arg)
+{
+ /* Have to be careful as there are 3 pointers in the mldl_cfg
+ * structure */
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct mldl_cfg *local_mldl_cfg;
+ int retval = 0;
+
+ local_mldl_cfg = kmalloc(sizeof(struct mldl_cfg), GFP_KERNEL);
+ if (!local_mldl_cfg)
+ return -ENOMEM;
+
+ retval =
+ copy_from_user(local_mldl_cfg, arg, sizeof(*arg));
+ if (retval) {
+ dev_err(&client->adapter->dev,
+ "%s|%s:%d: EFAULT on arg\n",
+ __FILE__, __func__, __LINE__);
+ retval = -EFAULT;
+ goto out;
+ }
+
+ /* Fill in the accel, compass, pressure and pdata pointers */
+ if (mldl_cfg->accel) {
+ retval = copy_to_user((void __user *)local_mldl_cfg->accel,
+ mldl_cfg->accel,
+ sizeof(*mldl_cfg->accel));
+ if (retval) {
+ dev_err(&client->adapter->dev,
+ "%s|%s:%d: EFAULT on accel\n",
+ __FILE__, __func__, __LINE__);
+ retval = -EFAULT;
+ goto out;
+ }
+ }
+
+ if (mldl_cfg->compass) {
+ retval = copy_to_user((void __user *)local_mldl_cfg->compass,
+ mldl_cfg->compass,
+ sizeof(*mldl_cfg->compass));
+ if (retval) {
+ dev_err(&client->adapter->dev,
+ "%s|%s:%d: EFAULT on compass\n",
+ __FILE__, __func__, __LINE__);
+ retval = -EFAULT;
+ goto out;
+ }
+ }
+
+ if (mldl_cfg->pressure) {
+ retval = copy_to_user((void __user *)local_mldl_cfg->pressure,
+ mldl_cfg->pressure,
+ sizeof(*mldl_cfg->pressure));
+ if (retval) {
+ dev_err(&client->adapter->dev,
+ "%s|%s:%d: EFAULT on pressure\n",
+ __FILE__, __func__, __LINE__);
+ retval = -EFAULT;
+ goto out;
+ }
+ }
+
+ if (mldl_cfg->pdata) {
+ retval = copy_to_user((void __user *)local_mldl_cfg->pdata,
+ mldl_cfg->pdata,
+ sizeof(*mldl_cfg->pdata));
+ if (retval) {
+ dev_err(&client->adapter->dev,
+ "%s|%s:%d: EFAULT on pdata\n",
+ __FILE__, __func__, __LINE__);
+ retval = -EFAULT;
+ goto out;
+ }
+ }
+
+ /* Do not modify the accel, compass, pressure and pdata pointers */
+ retval = copy_to_user(arg, mldl_cfg, offsetof(struct mldl_cfg, accel));
+
+ if (retval)
+ retval = -EFAULT;
+ out:
+ kfree(local_mldl_cfg);
+ return retval;
+}
+
+/**
+ * slave_config() - Pass a requested slave configuration to the slave sensor
+ *
+ * @adapter the adaptor to use to communicate with the slave
+ * @mldl_cfg the mldl configuration structuer
+ * @slave pointer to the slave descriptor
+ * @usr_config The configuration to pass to the slave sensor
+ *
+ * returns 0 or non-zero error code
+ */
+static int slave_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_adapter,
+ void *slave_adapter,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config __user *usr_config)
+{
+ int retval = 0;
+ struct ext_slave_config config;
+ if ((!slave) || (!slave->config))
+ return -ENODEV;
+
+ retval = copy_from_user(&config, usr_config, sizeof(config));
+ if (retval)
+ return -EFAULT;
+
+ if (config.len && config.data) {
+ void *data;
+ data = kmalloc(config.len, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ retval = copy_from_user(data,
+ (void __user *)config.data, config.len);
+ if (retval) {
+ retval = -EFAULT;
+ kfree(data);
+ return retval;
+ }
+ config.data = data;
+ }
+ retval = inv_mpu_slave_config(mldl_cfg, gyro_adapter, slave_adapter,
+ &config, slave, pdata);
+ kfree(config.data);
+ return retval;
+}
+
+/**
+ * slave_get_config() - Get requested slave configuration from the slave sensor
+ *
+ * @adapter the adaptor to use to communicate with the slave
+ * @mldl_cfg the mldl configuration structuer
+ * @slave pointer to the slave descriptor
+ * @usr_config The configuration for the slave to fill out
+ *
+ * returns 0 or non-zero error code
+ */
+static int slave_get_config(struct mldl_cfg *mldl_cfg,
+ void *gyro_adapter,
+ void *slave_adapter,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config __user *usr_config)
+{
+ int retval = 0;
+ struct ext_slave_config config;
+ void *user_data;
+ if (!(slave) || !(slave->get_config))
+ return -ENODEV;
+
+ retval = copy_from_user(&config, usr_config, sizeof(config));
+ if (retval)
+ return -EFAULT;
+
+ user_data = config.data;
+ if (config.len && config.data) {
+ void *data;
+ data = kmalloc(config.len, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ retval = copy_from_user(data,
+ (void __user *)config.data, config.len);
+ if (retval) {
+ retval = -EFAULT;
+ kfree(data);
+ return retval;
+ }
+ config.data = data;
+ }
+ retval = inv_mpu_get_slave_config(mldl_cfg, gyro_adapter,
+ slave_adapter, &config, slave, pdata);
+ if (retval) {
+ kfree(config.data);
+ return retval;
+ }
+ retval = copy_to_user((unsigned char __user *)user_data,
+ config.data, config.len);
+ kfree(config.data);
+ return retval;
+}
+
+static int inv_slave_read(struct mldl_cfg *mldl_cfg,
+ void *gyro_adapter,
+ void *slave_adapter,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ void __user *usr_data)
+{
+ int retval;
+ unsigned char *data;
+ data = kzalloc(slave->read_len, GFP_KERNEL);
+ if (!data)
+ return -EFAULT;
+
+ retval = inv_mpu_slave_read(mldl_cfg, gyro_adapter, slave_adapter,
+ slave, pdata, data);
+
+ if ((!retval) &&
+ (copy_to_user((unsigned char __user *)usr_data,
+ data, slave->read_len)))
+ retval = -EFAULT;
+
+ kfree(data);
+ return retval;
+}
+
+static int mpu_handle_mlsl(void *sl_handle,
+ unsigned char addr,
+ unsigned int cmd,
+ struct mpu_read_write __user *usr_msg)
+{
+ int retval = 0;
+ struct mpu_read_write msg;
+ unsigned char *user_data;
+ retval = copy_from_user(&msg, usr_msg, sizeof(msg));
+ if (retval)
+ return -EFAULT;
+
+ user_data = msg.data;
+ if (msg.length && msg.data) {
+ unsigned char *data;
+ data = kmalloc(msg.length, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ retval = copy_from_user(data,
+ (void __user *)msg.data, msg.length);
+ if (retval) {
+ retval = -EFAULT;
+ kfree(data);
+ return retval;
+ }
+ msg.data = data;
+ } else {
+ return -EPERM;
+ }
+
+ switch (cmd) {
+ case MPU_READ:
+ retval = inv_serial_read(sl_handle, addr,
+ msg.address, msg.length, msg.data);
+ break;
+ case MPU_WRITE:
+ retval = inv_serial_write(sl_handle, addr,
+ msg.length, msg.data);
+ break;
+ case MPU_READ_MEM:
+ retval = inv_serial_read_mem(sl_handle, addr,
+ msg.address, msg.length, msg.data);
+ break;
+ case MPU_WRITE_MEM:
+ retval = inv_serial_write_mem(sl_handle, addr,
+ msg.address, msg.length,
+ msg.data);
+ break;
+ case MPU_READ_FIFO:
+ retval = inv_serial_read_fifo(sl_handle, addr,
+ msg.length, msg.data);
+ break;
+ case MPU_WRITE_FIFO:
+ retval = inv_serial_write_fifo(sl_handle, addr,
+ msg.length, msg.data);
+ break;
+
+ };
+ if (retval) {
+ dev_err(&((struct i2c_adapter *)sl_handle)->dev,
+ "%s: i2c %d error %d\n",
+ __func__, cmd, retval);
+ kfree(msg.data);
+ return retval;
+ }
+ retval = copy_to_user((unsigned char __user *)user_data,
+ msg.data, msg.length);
+ kfree(msg.data);
+ return retval;
+}
+
+/* ioctl - I/O control */
+static long mpu_dev_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct mpu_private_data *mpu =
+ container_of(file->private_data, struct mpu_private_data, dev);
+ struct i2c_client *client = mpu->client;
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ int retval = 0;
+ struct i2c_adapter *gyro_adapter;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+
+ gyro_adapter = client->adapter;
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ retval = mutex_lock_interruptible(&mpu->mutex);
+ if (retval) {
+ dev_err(&client->adapter->dev,
+ "%s: mutex_lock_interruptible returned %d\n",
+ __func__, retval);
+ return retval;
+ }
+
+ switch (cmd) {
+ case MPU_SET_MPU_CONFIG:
+ retval = mpu_dev_ioctl_set_mpu_config(client, arg);
+ break;
+ case MPU_SET_PLATFORM_DATA:
+ retval = mpu_dev_ioctl_set_mpu_pdata(client, arg);
+ break;
+ case MPU_GET_MPU_CONFIG:
+ retval = mpu_dev_ioctl_get_mpu_config(client,
+ (struct mldl_cfg __user *)arg);
+ break;
+ case MPU_READ:
+ case MPU_WRITE:
+ case MPU_READ_MEM:
+ case MPU_WRITE_MEM:
+ case MPU_READ_FIFO:
+ case MPU_WRITE_FIFO:
+ retval = mpu_handle_mlsl(gyro_adapter, mldl_cfg->addr, cmd,
+ (struct mpu_read_write __user *)arg);
+ break;
+ case MPU_CONFIG_ACCEL:
+ retval = slave_config(mldl_cfg,
+ gyro_adapter,
+ accel_adapter,
+ mldl_cfg->accel,
+ &mldl_cfg->pdata->accel,
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_CONFIG_COMPASS:
+ retval = slave_config(mldl_cfg,
+ gyro_adapter,
+ compass_adapter,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass,
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_CONFIG_PRESSURE:
+ retval = slave_config(mldl_cfg,
+ gyro_adapter,
+ pressure_adapter,
+ mldl_cfg->pressure,
+ &mldl_cfg->pdata->pressure,
+ (struct ext_slave_config __user *)arg);
+ break;
+ case MPU_GET_CONFIG_ACCEL:
+ retval = slave_get_config(mldl_cfg,
+ gyro_adapter,
+ accel_adapter,
+ mldl_cfg->accel,
+ &mldl_cfg->pdata->accel,
+ (struct ext_slave_config __user *)
+ arg);
+ break;
+ case MPU_GET_CONFIG_COMPASS:
+ retval = slave_get_config(mldl_cfg,
+ gyro_adapter,
+ compass_adapter,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass,
+ (struct ext_slave_config __user *)
+ arg);
+ break;
+ case MPU_GET_CONFIG_PRESSURE:
+ retval = slave_get_config(mldl_cfg,
+ gyro_adapter,
+ pressure_adapter,
+ mldl_cfg->pressure,
+ &mldl_cfg->pdata->pressure,
+ (struct ext_slave_config __user *)
+ arg);
+ break;
+ case MPU_SUSPEND:
+ retval = inv_mpu_suspend(mldl_cfg,
+ gyro_adapter,
+ accel_adapter,
+ compass_adapter,
+ pressure_adapter,
+ (~(mldl_cfg->requested_sensors))
+ & INV_ALL_SENSORS);
+ break;
+ case MPU_RESUME:
+ retval = inv_mpu_resume(mldl_cfg,
+ gyro_adapter,
+ accel_adapter,
+ compass_adapter,
+ pressure_adapter,
+ mldl_cfg->requested_sensors);
+ break;
+ case MPU_PM_EVENT_HANDLED:
+ dev_dbg(&client->adapter->dev, "%s: %d\n", __func__, cmd);
+ complete(&mpu->completion);
+ break;
+ case MPU_READ_ACCEL:
+ retval = inv_slave_read(mldl_cfg,
+ gyro_adapter,
+ accel_adapter,
+ mldl_cfg->accel,
+ &mldl_cfg->pdata->accel,
+ (unsigned char __user *)arg);
+ break;
+ case MPU_READ_COMPASS:
+ retval = inv_slave_read(mldl_cfg,
+ gyro_adapter,
+ compass_adapter,
+ mldl_cfg->compass,
+ &mldl_cfg->pdata->compass,
+ (unsigned char __user *)arg);
+ break;
+ case MPU_READ_PRESSURE:
+ retval = inv_slave_read(mldl_cfg,
+ gyro_adapter,
+ pressure_adapter,
+ mldl_cfg->pressure,
+ &mldl_cfg->pdata->pressure,
+ (unsigned char __user *)arg);
+ break;
+ default:
+ dev_err(&client->adapter->dev,
+ "%s: Unknown cmd %x, arg %lu: MIN %x MAX %x\n",
+ __func__, cmd, arg,
+ MPU_SET_MPU_CONFIG, MPU_SET_MPU_CONFIG);
+ retval = -EINVAL;
+ };
+
+ mutex_unlock(&mpu->mutex);
+ return retval;
+}
+
+void mpu_shutdown(struct i2c_client *client)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ mutex_lock(&mpu->mutex);
+ (void)inv_mpu_suspend(mldl_cfg, client->adapter,
+ accel_adapter, compass_adapter, pressure_adapter,
+ INV_ALL_SENSORS);
+ mutex_unlock(&mpu->mutex);
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+}
+
+int mpu_dev_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ mutex_lock(&mpu->mutex);
+ if (!mldl_cfg->ignore_system_suspend) {
+ dev_dbg(&client->adapter->dev,
+ "%s: suspending on event %d\n", __func__, mesg.event);
+ (void)inv_mpu_suspend(mldl_cfg, client->adapter,
+ accel_adapter, compass_adapter,
+ pressure_adapter, INV_ALL_SENSORS);
+ } else {
+ dev_dbg(&client->adapter->dev,
+ "%s: Already suspended %d\n", __func__, mesg.event);
+ }
+ mutex_unlock(&mpu->mutex);
+ return 0;
+}
+
+int mpu_dev_resume(struct i2c_client *client)
+{
+ struct mpu_private_data *mpu =
+ (struct mpu_private_data *)i2c_get_clientdata(client);
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ mutex_lock(&mpu->mutex);
+ if (mpu->pid && !mldl_cfg->ignore_system_suspend) {
+ (void)inv_mpu_resume(mldl_cfg, client->adapter,
+ accel_adapter,
+ compass_adapter,
+ pressure_adapter,
+ mldl_cfg->requested_sensors);
+ dev_dbg(&client->adapter->dev,
+ "%s for pid %d\n", __func__, mpu->pid);
+ }
+ mutex_unlock(&mpu->mutex);
+ return 0;
+}
+
+/* define which file operations are supported */
+static const struct file_operations mpu_fops = {
+ .owner = THIS_MODULE,
+ .read = mpu_read,
+ .poll = mpu_poll,
+ .unlocked_ioctl = mpu_dev_ioctl,
+ .open = mpu_dev_open,
+ .release = mpu_release,
+};
+
+int inv_mpu_register_slave(struct module *slave_module,
+ struct i2c_client *slave_client,
+ struct ext_slave_platform_data *slave_pdata,
+ struct ext_slave_descr *(*get_slave_descr)(void))
+{
+ struct mpu_private_data *mpu = mpu_private_data;
+ struct mldl_cfg *mldl_cfg;
+ struct mpu_platform_data *pdata;
+ struct ext_slave_descr *slave_descr;
+ int result = 0;
+
+ if (!slave_client || !slave_pdata || !get_slave_descr)
+ return -EINVAL;
+
+ if (!mpu) {
+ dev_err(&slave_client->adapter->dev,
+ "%s: Null mpu_private_data\n", __func__);
+ return -EINVAL;
+ }
+ mldl_cfg = &mpu->mldl_cfg;
+ pdata = mldl_cfg->pdata;
+
+ slave_descr = get_slave_descr();
+ if (!slave_descr) {
+ dev_err(&slave_client->adapter->dev,
+ "%s: Null ext_slave_descr\n", __func__);
+ return -EINVAL;
+ }
+
+ mutex_lock(&mpu->mutex);
+ if (mpu->pid) {
+ mutex_unlock(&mpu->mutex);
+ return -EBUSY;
+ }
+
+ mpu->slave_modules[slave_descr->type] = slave_module;
+
+ switch (slave_descr->type) {
+ case EXT_SLAVE_TYPE_ACCELEROMETER:
+ if (pdata->accel.get_slave_descr) {
+ result = -EBUSY;
+ break;
+ }
+
+ pdata->accel.address = slave_client->addr;
+ pdata->accel.irq = slave_client->irq;
+ pdata->accel.adapt_num = i2c_adapter_id(slave_client->adapter);
+
+ if (pdata->accel.irq > 0) {
+ dev_info(&slave_client->adapter->dev,
+ "Installing Accel irq using %d\n",
+ pdata->accel.irq);
+ result = slaveirq_init(slave_client->adapter,
+ &pdata->accel, "accelirq");
+ if (result)
+ break;
+ } else {
+ dev_WARN(&slave_client->adapter->dev,
+ "Accel irq not assigned\n");
+ }
+
+ if (slave_descr->init) {
+ result = slave_descr->init(slave_client->adapter,
+ slave_descr,
+ &pdata->accel);
+ if (result) {
+ dev_err(&slave_client->adapter->dev,
+ "Accel init failed %d\n", result);
+ if (pdata->accel.irq > 0)
+ slaveirq_exit(&pdata->accel);
+ break;
+ }
+ }
+
+ pdata->accel.get_slave_descr = get_slave_descr;
+ mldl_cfg->accel = slave_descr;
+ dev_info(&slave_client->adapter->dev,
+ "%s: +%s\n", MPU_NAME, mldl_cfg->accel->name);
+ break;
+ case EXT_SLAVE_TYPE_COMPASS:
+ if (pdata->compass.get_slave_descr) {
+ result = -EBUSY;
+ break;
+ }
+
+ pdata->compass.address = slave_client->addr;
+ pdata->compass.irq = slave_client->irq;
+ pdata->compass.adapt_num =
+ i2c_adapter_id(slave_client->adapter);
+ if (pdata->compass.irq > 0) {
+ dev_info(&slave_client->adapter->dev,
+ "Installing Compass irq using %d\n",
+ pdata->compass.irq);
+ result = slaveirq_init(slave_client->adapter,
+ &pdata->compass,
+ "compassirq");
+ if (result)
+ break;
+ } else {
+ dev_warn(&slave_client->adapter->dev,
+ "Compass irq not assigned\n");
+ }
+
+ if (slave_descr->init) {
+ result = slave_descr->init(slave_client->adapter,
+ slave_descr,
+ &pdata->compass);
+ if (result) {
+ dev_err(&slave_client->adapter->dev,
+ "Compass init failed %d\n", result);
+ if (pdata->compass.irq > 0)
+ slaveirq_exit(&pdata->compass);
+ break;
+ }
+ }
+
+ pdata->compass.get_slave_descr = get_slave_descr;
+ mldl_cfg->compass = pdata->compass.get_slave_descr();
+ dev_info(&slave_client->adapter->dev,
+ "%s: +%s\n", MPU_NAME,
+ mldl_cfg->compass->name);
+ break;
+ case EXT_SLAVE_TYPE_PRESSURE:
+ if (pdata->pressure.get_slave_descr) {
+ result = -EBUSY;
+ break;
+ }
+
+ pdata->pressure.address = slave_client->addr;
+ pdata->pressure.irq = slave_client->irq;
+ pdata->pressure.adapt_num =
+ i2c_adapter_id(slave_client->adapter);
+ if (pdata->pressure.irq > 0) {
+ dev_info(&slave_client->adapter->dev,
+ "Installing Pressure irq using %d\n",
+ pdata->pressure.irq);
+ result = slaveirq_init(slave_client->adapter,
+ &pdata->pressure,
+ "pressureirq");
+ if (result)
+ break;
+ } else {
+ dev_warn(&slave_client->adapter->dev,
+ "Pressure irq not assigned\n");
+ }
+
+ if (slave_descr->init) {
+ result = slave_descr->init(slave_client->adapter,
+ slave_descr,
+ &pdata->pressure);
+ if (result) {
+ dev_err(&slave_client->adapter->dev,
+ "Pressure init failed %d\n", result);
+ if (pdata->pressure.irq > 0)
+ slaveirq_exit(&pdata->pressure);
+ break;
+ }
+ }
+
+ pdata->pressure.get_slave_descr = get_slave_descr;
+ mldl_cfg->pressure = pdata->pressure.get_slave_descr();
+ dev_info(&slave_client->adapter->dev,
+ "%s: +%s\n", MPU_NAME,
+ mldl_cfg->pressure->name);
+ break;
+ default:
+ dev_err(&slave_client->adapter->dev,
+ "Invalid slave type %d\n", slave_descr->type);
+ result = -EINVAL;
+ break;
+ };
+
+ mutex_unlock(&mpu->mutex);
+ return result;
+}
+EXPORT_SYMBOL(inv_mpu_register_slave);
+
+void inv_mpu_unregister_slave(struct i2c_client *slave_client,
+ struct ext_slave_platform_data *slave_pdata,
+ struct ext_slave_descr *(*get_slave_descr)(void))
+{
+ struct mpu_private_data *mpu = mpu_private_data;
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct mpu_platform_data *pdata;
+ struct ext_slave_descr *slave_descr;
+ int result;
+
+ dev_info(&slave_client->adapter->dev, "%s\n", __func__);
+
+ if (!slave_client || !slave_pdata || !get_slave_descr)
+ return;
+
+ slave_descr = get_slave_descr();
+ if (!slave_descr)
+ return;
+
+ pdata = mldl_cfg->pdata;
+ if (!pdata)
+ return;
+
+ mutex_lock(&mpu->mutex);
+
+ if (slave_descr->exit) {
+ result = slave_descr->exit(slave_client->adapter,
+ slave_descr,
+ slave_pdata);
+ if (INV_SUCCESS != result)
+ MPL_LOGE("Accel exit failed %d\n", result);
+ }
+
+ if (slave_pdata->irq)
+ slaveirq_exit(slave_pdata);
+
+ switch (slave_descr->type) {
+ case EXT_SLAVE_TYPE_ACCELEROMETER:
+ mldl_cfg->accel = NULL;
+ pdata->accel.get_slave_descr = NULL;
+ break;
+ case EXT_SLAVE_TYPE_COMPASS:
+ mldl_cfg->compass = NULL;
+ pdata->compass.get_slave_descr = NULL;
+ break;
+ case EXT_SLAVE_TYPE_PRESSURE:
+ mldl_cfg->pressure = NULL;
+ pdata->pressure.get_slave_descr = NULL;
+ break;
+ default:
+ break;
+ };
+ mpu->slave_modules[slave_descr->type] = NULL;
+ mutex_unlock(&mpu->mutex);
+}
+EXPORT_SYMBOL(inv_mpu_unregister_slave);
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static const struct i2c_device_id mpu_id[] = {
+ {"mpu3050", 0},
+ {"mpu6050", 0},
+ {"mpu6050_no_accel", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, mpu_id);
+
+int mpu_probe(struct i2c_client *client, const struct i2c_device_id *devid)
+{
+ struct mpu_platform_data *pdata;
+ struct mpu_private_data *mpu;
+ struct mldl_cfg *mldl_cfg;
+ int res = 0;
+ struct i2c_adapter *accel_adapter = NULL;
+ struct i2c_adapter *compass_adapter = NULL;
+ struct i2c_adapter *pressure_adapter = NULL;
+ int ii = 0;
+
+ dev_info(&client->adapter->dev, "%s: %d\n", __func__, ii++);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ res = -ENODEV;
+ goto out_check_functionality_failed;
+ }
+
+ mpu = kzalloc(sizeof(struct mpu_private_data), GFP_KERNEL);
+ if (!mpu) {
+ res = -ENOMEM;
+ goto out_alloc_data_failed;
+ }
+ mpu_private_data = mpu;
+ i2c_set_clientdata(client, mpu);
+ mpu->client = client;
+ mldl_cfg = &mpu->mldl_cfg;
+
+ init_waitqueue_head(&mpu->mpu_event_wait);
+
+ mutex_init(&mpu->mutex);
+ init_completion(&mpu->completion);
+
+ mpu->response_timeout = 60; /* Seconds */
+ mpu->timeout.function = mpu_pm_timeout;
+ mpu->timeout.data = (u_long) mpu;
+ init_timer(&mpu->timeout);
+
+ mpu->nb.notifier_call = mpu_pm_notifier_callback;
+ mpu->nb.priority = 0;
+ register_pm_notifier(&mpu->nb);
+
+ pdata = (struct mpu_platform_data *)client->dev.platform_data;
+ if (!pdata) {
+ dev_WARN(&client->adapter->dev,
+ "Missing platform data for mpu\n");
+ }
+ mldl_cfg->pdata = pdata;
+
+ mldl_cfg->addr = client->addr;
+ res = inv_mpu_open(&mpu->mldl_cfg, client->adapter,
+ accel_adapter, compass_adapter, pressure_adapter);
+
+ if (res) {
+ dev_err(&client->adapter->dev,
+ "Unable to open %s %d\n", MPU_NAME, res);
+ res = -ENODEV;
+ goto out_whoami_failed;
+ }
+
+ mpu->dev.minor = MISC_DYNAMIC_MINOR;
+ mpu->dev.name = "mpu"; /* Same for both 3050 and 6000 */
+ mpu->dev.fops = &mpu_fops;
+ res = misc_register(&mpu->dev);
+ if (res < 0) {
+ dev_err(&client->adapter->dev,
+ "ERROR: misc_register returned %d\n", res);
+ goto out_misc_register_failed;
+ }
+
+ if (client->irq) {
+ dev_info(&client->adapter->dev,
+ "Installing irq using %d\n", client->irq);
+ res = mpuirq_init(client, mldl_cfg);
+ if (res)
+ goto out_mpuirq_failed;
+ } else {
+ dev_WARN(&client->adapter->dev,
+ "Missing %s IRQ\n", MPU_NAME);
+ }
+
+ return res;
+
+ out_mpuirq_failed:
+ misc_deregister(&mpu->dev);
+ out_misc_register_failed:
+ inv_mpu_close(&mpu->mldl_cfg, client->adapter,
+ accel_adapter, compass_adapter, pressure_adapter);
+ out_whoami_failed:
+ kfree(mpu);
+ mpu_private_data = NULL;
+ out_alloc_data_failed:
+ out_check_functionality_failed:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, res);
+ return res;
+
+}
+
+static int mpu_remove(struct i2c_client *client)
+{
+ struct mpu_private_data *mpu = i2c_get_clientdata(client);
+ struct i2c_adapter *accel_adapter;
+ struct i2c_adapter *compass_adapter;
+ struct i2c_adapter *pressure_adapter;
+ struct mldl_cfg *mldl_cfg = &mpu->mldl_cfg;
+ struct mpu_platform_data *pdata = mldl_cfg->pdata;
+
+ accel_adapter = i2c_get_adapter(mldl_cfg->pdata->accel.adapt_num);
+ compass_adapter = i2c_get_adapter(mldl_cfg->pdata->compass.adapt_num);
+ pressure_adapter = i2c_get_adapter(mldl_cfg->pdata->pressure.adapt_num);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_close(mldl_cfg, client->adapter,
+ accel_adapter, compass_adapter, pressure_adapter);
+
+ if (client->irq)
+ mpuirq_exit();
+
+ if (pdata && pdata->pressure.get_slave_descr && pdata->pressure.irq) {
+ slaveirq_exit(&pdata->pressure);
+ pdata->pressure.get_slave_descr = NULL;
+ }
+
+ if (pdata && pdata->compass.get_slave_descr && pdata->compass.irq) {
+ slaveirq_exit(&pdata->compass);
+ pdata->compass.get_slave_descr = NULL;
+ }
+
+ if (pdata && pdata->accel.get_slave_descr && pdata->accel.irq) {
+ slaveirq_exit(&pdata->accel);
+ pdata->accel.get_slave_descr = NULL;
+ }
+
+ misc_deregister(&mpu->dev);
+ kfree(mpu);
+
+ return 0;
+}
+
+static struct i2c_driver mpu_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = mpu_probe,
+ .remove = mpu_remove,
+ .id_table = mpu_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = MPU_NAME,
+ },
+ .address_list = normal_i2c,
+ .shutdown = mpu_shutdown, /* optional */
+ .suspend = mpu_dev_suspend, /* optional */
+ .resume = mpu_dev_resume, /* optional */
+
+};
+
+static int __init mpu_init(void)
+{
+ int res = i2c_add_driver(&mpu_driver);
+ pr_info("%s: Probe name %s\n", __func__, MPU_NAME);
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit mpu_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&mpu_driver);
+}
+
+module_init(mpu_init);
+module_exit(mpu_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("User space character device interface for MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS(MPU_NAME);
diff --git a/drivers/misc/inv_mpu/mpu-dev.h b/drivers/misc/inv_mpu/mpu-dev.h
new file mode 100644
index 0000000..38b3f6f
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu-dev.h
@@ -0,0 +1,36 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+
+#ifndef __MPU_DEV_H__
+#define __MPU_DEV_H__
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mpu.h>
+
+int inv_mpu_register_slave(struct module *slave_module,
+ struct i2c_client *client,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_descr *(*slave_descr)(void));
+
+void inv_mpu_unregister_slave(struct i2c_client *client,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_descr *(*slave_descr)(void));
+#endif
diff --git a/drivers/misc/inv_mpu/mpu3050.h b/drivers/misc/inv_mpu/mpu3050.h
new file mode 100644
index 0000000..335f527
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu3050.h
@@ -0,0 +1,251 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+#ifndef __MPU_H_
+#error Do not include this file directly. Include mpu.h instead.
+#endif
+
+#ifndef __MPU3050_H_
+#define __MPU3050_H_
+
+#include <linux/types.h>
+
+
+#define MPU_NAME "mpu3050"
+#define DEFAULT_MPU_SLAVEADDR 0x68
+
+/*==== MPU REGISTER SET ====*/
+enum mpu_register {
+ MPUREG_WHO_AM_I = 0, /* 00 0x00 */
+ MPUREG_PRODUCT_ID, /* 01 0x01 */
+ MPUREG_02_RSVD, /* 02 0x02 */
+ MPUREG_03_RSVD, /* 03 0x03 */
+ MPUREG_04_RSVD, /* 04 0x04 */
+ MPUREG_XG_OFFS_TC, /* 05 0x05 */
+ MPUREG_06_RSVD, /* 06 0x06 */
+ MPUREG_07_RSVD, /* 07 0x07 */
+ MPUREG_YG_OFFS_TC, /* 08 0x08 */
+ MPUREG_09_RSVD, /* 09 0x09 */
+ MPUREG_0A_RSVD, /* 10 0x0a */
+ MPUREG_ZG_OFFS_TC, /* 11 0x0b */
+ MPUREG_X_OFFS_USRH, /* 12 0x0c */
+ MPUREG_X_OFFS_USRL, /* 13 0x0d */
+ MPUREG_Y_OFFS_USRH, /* 14 0x0e */
+ MPUREG_Y_OFFS_USRL, /* 15 0x0f */
+ MPUREG_Z_OFFS_USRH, /* 16 0x10 */
+ MPUREG_Z_OFFS_USRL, /* 17 0x11 */
+ MPUREG_FIFO_EN1, /* 18 0x12 */
+ MPUREG_FIFO_EN2, /* 19 0x13 */
+ MPUREG_AUX_SLV_ADDR, /* 20 0x14 */
+ MPUREG_SMPLRT_DIV, /* 21 0x15 */
+ MPUREG_DLPF_FS_SYNC, /* 22 0x16 */
+ MPUREG_INT_CFG, /* 23 0x17 */
+ MPUREG_ACCEL_BURST_ADDR,/* 24 0x18 */
+ MPUREG_19_RSVD, /* 25 0x19 */
+ MPUREG_INT_STATUS, /* 26 0x1a */
+ MPUREG_TEMP_OUT_H, /* 27 0x1b */
+ MPUREG_TEMP_OUT_L, /* 28 0x1c */
+ MPUREG_GYRO_XOUT_H, /* 29 0x1d */
+ MPUREG_GYRO_XOUT_L, /* 30 0x1e */
+ MPUREG_GYRO_YOUT_H, /* 31 0x1f */
+ MPUREG_GYRO_YOUT_L, /* 32 0x20 */
+ MPUREG_GYRO_ZOUT_H, /* 33 0x21 */
+ MPUREG_GYRO_ZOUT_L, /* 34 0x22 */
+ MPUREG_23_RSVD, /* 35 0x23 */
+ MPUREG_24_RSVD, /* 36 0x24 */
+ MPUREG_25_RSVD, /* 37 0x25 */
+ MPUREG_26_RSVD, /* 38 0x26 */
+ MPUREG_27_RSVD, /* 39 0x27 */
+ MPUREG_28_RSVD, /* 40 0x28 */
+ MPUREG_29_RSVD, /* 41 0x29 */
+ MPUREG_2A_RSVD, /* 42 0x2a */
+ MPUREG_2B_RSVD, /* 43 0x2b */
+ MPUREG_2C_RSVD, /* 44 0x2c */
+ MPUREG_2D_RSVD, /* 45 0x2d */
+ MPUREG_2E_RSVD, /* 46 0x2e */
+ MPUREG_2F_RSVD, /* 47 0x2f */
+ MPUREG_30_RSVD, /* 48 0x30 */
+ MPUREG_31_RSVD, /* 49 0x31 */
+ MPUREG_32_RSVD, /* 50 0x32 */
+ MPUREG_33_RSVD, /* 51 0x33 */
+ MPUREG_34_RSVD, /* 52 0x34 */
+ MPUREG_DMP_CFG_1, /* 53 0x35 */
+ MPUREG_DMP_CFG_2, /* 54 0x36 */
+ MPUREG_BANK_SEL, /* 55 0x37 */
+ MPUREG_MEM_START_ADDR, /* 56 0x38 */
+ MPUREG_MEM_R_W, /* 57 0x39 */
+ MPUREG_FIFO_COUNTH, /* 58 0x3a */
+ MPUREG_FIFO_COUNTL, /* 59 0x3b */
+ MPUREG_FIFO_R_W, /* 60 0x3c */
+ MPUREG_USER_CTRL, /* 61 0x3d */
+ MPUREG_PWR_MGM, /* 62 0x3e */
+ MPUREG_3F_RSVD, /* 63 0x3f */
+ NUM_OF_MPU_REGISTERS /* 64 0x40 */
+};
+
+/*==== BITS FOR MPU ====*/
+
+/*---- MPU 'FIFO_EN1' register (12) ----*/
+#define BIT_TEMP_OUT 0x80
+#define BIT_GYRO_XOUT 0x40
+#define BIT_GYRO_YOUT 0x20
+#define BIT_GYRO_ZOUT 0x10
+#define BIT_ACCEL_XOUT 0x08
+#define BIT_ACCEL_YOUT 0x04
+#define BIT_ACCEL_ZOUT 0x02
+#define BIT_AUX_1OUT 0x01
+/*---- MPU 'FIFO_EN2' register (13) ----*/
+#define BIT_AUX_2OUT 0x02
+#define BIT_AUX_3OUT 0x01
+/*---- MPU 'DLPF_FS_SYNC' register (16) ----*/
+#define BITS_EXT_SYNC_NONE 0x00
+#define BITS_EXT_SYNC_TEMP 0x20
+#define BITS_EXT_SYNC_GYROX 0x40
+#define BITS_EXT_SYNC_GYROY 0x60
+#define BITS_EXT_SYNC_GYROZ 0x80
+#define BITS_EXT_SYNC_ACCELX 0xA0
+#define BITS_EXT_SYNC_ACCELY 0xC0
+#define BITS_EXT_SYNC_ACCELZ 0xE0
+#define BITS_EXT_SYNC_MASK 0xE0
+#define BITS_FS_250DPS 0x00
+#define BITS_FS_500DPS 0x08
+#define BITS_FS_1000DPS 0x10
+#define BITS_FS_2000DPS 0x18
+#define BITS_FS_MASK 0x18
+#define BITS_DLPF_CFG_256HZ_NOLPF2 0x00
+#define BITS_DLPF_CFG_188HZ 0x01
+#define BITS_DLPF_CFG_98HZ 0x02
+#define BITS_DLPF_CFG_42HZ 0x03
+#define BITS_DLPF_CFG_20HZ 0x04
+#define BITS_DLPF_CFG_10HZ 0x05
+#define BITS_DLPF_CFG_5HZ 0x06
+#define BITS_DLPF_CFG_2100HZ_NOLPF 0x07
+#define BITS_DLPF_CFG_MASK 0x07
+/*---- MPU 'INT_CFG' register (17) ----*/
+#define BIT_ACTL 0x80
+#define BIT_ACTL_LOW 0x80
+#define BIT_ACTL_HIGH 0x00
+#define BIT_OPEN 0x40
+#define BIT_OPEN_DRAIN 0x40
+#define BIT_PUSH_PULL 0x00
+#define BIT_LATCH_INT_EN 0x20
+#define BIT_INT_PULSE_WIDTH_50US 0x00
+#define BIT_INT_ANYRD_2CLEAR 0x10
+#define BIT_INT_STAT_READ_2CLEAR 0x00
+#define BIT_MPU_RDY_EN 0x04
+#define BIT_DMP_INT_EN 0x02
+#define BIT_RAW_RDY_EN 0x01
+/*---- MPU 'INT_STATUS' register (1A) ----*/
+#define BIT_INT_STATUS_FIFO_OVERLOW 0x80
+#define BIT_MPU_RDY 0x04
+#define BIT_DMP_INT 0x02
+#define BIT_RAW_RDY 0x01
+/*---- MPU 'BANK_SEL' register (37) ----*/
+#define BIT_PRFTCH_EN 0x20
+#define BIT_CFG_USER_BANK 0x10
+#define BITS_MEM_SEL 0x0f
+/*---- MPU 'USER_CTRL' register (3D) ----*/
+#define BIT_DMP_EN 0x80
+#define BIT_FIFO_EN 0x40
+#define BIT_AUX_IF_EN 0x20
+#define BIT_AUX_RD_LENG 0x10
+#define BIT_AUX_IF_RST 0x08
+#define BIT_DMP_RST 0x04
+#define BIT_FIFO_RST 0x02
+#define BIT_GYRO_RST 0x01
+/*---- MPU 'PWR_MGM' register (3E) ----*/
+#define BIT_H_RESET 0x80
+#define BIT_SLEEP 0x40
+#define BIT_STBY_XG 0x20
+#define BIT_STBY_YG 0x10
+#define BIT_STBY_ZG 0x08
+#define BITS_CLKSEL 0x07
+
+/*---- MPU Silicon Revision ----*/
+#define MPU_SILICON_REV_A4 1 /* MPU A4 Device */
+#define MPU_SILICON_REV_B1 2 /* MPU B1 Device */
+#define MPU_SILICON_REV_B4 3 /* MPU B4 Device */
+#define MPU_SILICON_REV_B6 4 /* MPU B6 Device */
+
+/*---- MPU Memory ----*/
+#define MPU_MEM_BANK_SIZE (256)
+#define FIFO_HW_SIZE (512)
+
+enum MPU_MEMORY_BANKS {
+ MPU_MEM_RAM_BANK_0 = 0,
+ MPU_MEM_RAM_BANK_1,
+ MPU_MEM_RAM_BANK_2,
+ MPU_MEM_RAM_BANK_3,
+ MPU_MEM_NUM_RAM_BANKS,
+ MPU_MEM_OTP_BANK_0 = MPU_MEM_NUM_RAM_BANKS,
+ /* This one is always last */
+ MPU_MEM_NUM_BANKS
+};
+
+/*---- structure containing control variables used by MLDL ----*/
+/*---- MPU clock source settings ----*/
+/*---- MPU filter selections ----*/
+enum mpu_filter {
+ MPU_FILTER_256HZ_NOLPF2 = 0,
+ MPU_FILTER_188HZ,
+ MPU_FILTER_98HZ,
+ MPU_FILTER_42HZ,
+ MPU_FILTER_20HZ,
+ MPU_FILTER_10HZ,
+ MPU_FILTER_5HZ,
+ MPU_FILTER_2100HZ_NOLPF,
+ NUM_MPU_FILTER
+};
+
+enum mpu_fullscale {
+ MPU_FS_250DPS = 0,
+ MPU_FS_500DPS,
+ MPU_FS_1000DPS,
+ MPU_FS_2000DPS,
+ NUM_MPU_FS
+};
+
+enum mpu_clock_sel {
+ MPU_CLK_SEL_INTERNAL = 0,
+ MPU_CLK_SEL_PLLGYROX,
+ MPU_CLK_SEL_PLLGYROY,
+ MPU_CLK_SEL_PLLGYROZ,
+ MPU_CLK_SEL_PLLEXT32K,
+ MPU_CLK_SEL_PLLEXT19M,
+ MPU_CLK_SEL_RESERVED,
+ MPU_CLK_SEL_STOP,
+ NUM_CLK_SEL
+};
+
+enum mpu_ext_sync {
+ MPU_EXT_SYNC_NONE = 0,
+ MPU_EXT_SYNC_TEMP,
+ MPU_EXT_SYNC_GYROX,
+ MPU_EXT_SYNC_GYROY,
+ MPU_EXT_SYNC_GYROZ,
+ MPU_EXT_SYNC_ACCELX,
+ MPU_EXT_SYNC_ACCELY,
+ MPU_EXT_SYNC_ACCELZ,
+ NUM_MPU_EXT_SYNC
+};
+
+#define DLPF_FS_SYNC_VALUE(ext_sync, full_scale, lpf) \
+ ((ext_sync << 5) | (full_scale << 3) | lpf)
+
+#endif /* __MPU3050_H_ */
diff --git a/drivers/misc/inv_mpu/mpu6050a2.h b/drivers/misc/inv_mpu/mpu6050a2.h
new file mode 100644
index 0000000..62f3dc8
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050a2.h
@@ -0,0 +1,420 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @defgroup
+ * @brief
+ *
+ * @{
+ * @file mpu6050.h
+ * @brief
+ */
+
+#ifndef __MPU_H_
+#error Do not include this file directly. Include mpu.h instead.
+#endif
+
+#ifndef __MPU6050A2_H_
+#define __MPU6050A2_H_
+
+#error Invalid or undefined CONFIG_MPU_SENSORS_MPUxxxx
+
+#define MPU_NAME "mpu6000a2"
+#define DEFAULT_MPU_SLAVEADDR (0x68)
+
+/*==== MPU6050A2 REGISTER SET ====*/
+enum {
+ MPUREG_XG_OFFS_TC = 0, /* 0x00, 0 */
+ MPUREG_YG_OFFS_TC, /* 0x01, 1 */
+ MPUREG_ZG_OFFS_TC, /* 0x02, 2 */
+ MPUREG_X_FINE_GAIN, /* 0x03, 3 */
+ MPUREG_Y_FINE_GAIN, /* 0x04, 4 */
+ MPUREG_Z_FINE_GAIN, /* 0x05, 5 */
+ MPUREG_XA_OFFS_H, /* 0x06, 6 */
+ MPUREG_XA_OFFS_L_TC, /* 0x07, 7 */
+ MPUREG_YA_OFFS_H, /* 0x08, 8 */
+ MPUREG_YA_OFFS_L_TC, /* 0x09, 9 */
+ MPUREG_ZA_OFFS_H, /* 0x0a, 10 */
+ MPUREG_ZA_OFFS_L_TC, /* 0x0B, 11 */
+ MPUREG_0C_RSVD, /* 0x0c, 12 */
+ MPUREG_0D_RSVD, /* 0x0d, 13 */
+ MPUREG_0E_RSVD, /* 0x0e, 14 */
+ MPUREG_0F_RSVD, /* 0x0f, 15 */
+ MPUREG_10_RSVD, /* 0x00, 16 */
+ MPUREG_11_RSVD, /* 0x11, 17 */
+ MPUREG_12_RSVD, /* 0x12, 18 */
+ MPUREG_XG_OFFS_USRH, /* 0x13, 19 */
+ MPUREG_XG_OFFS_USRL, /* 0x14, 20 */
+ MPUREG_YG_OFFS_USRH, /* 0x15, 21 */
+ MPUREG_YG_OFFS_USRL, /* 0x16, 22 */
+ MPUREG_ZG_OFFS_USRH, /* 0x17, 23 */
+ MPUREG_ZG_OFFS_USRL, /* 0x18, 24 */
+ MPUREG_SMPLRT_DIV, /* 0x19, 25 */
+ MPUREG_CONFIG, /* 0x1A, 26 */
+ MPUREG_GYRO_CONFIG, /* 0x1b, 27 */
+ MPUREG_ACCEL_CONFIG, /* 0x1c, 28 */
+ MPUREG_ACCEL_FF_THR, /* 0x1d, 29 */
+ MPUREG_ACCEL_FF_DUR, /* 0x1e, 30 */
+ MPUREG_ACCEL_MOT_THR, /* 0x1f, 31 */
+ MPUREG_ACCEL_MOT_DUR, /* 0x20, 32 */
+ MPUREG_ACCEL_ZRMOT_THR, /* 0x21, 33 */
+ MPUREG_ACCEL_ZRMOT_DUR, /* 0x22, 34 */
+ MPUREG_FIFO_EN, /* 0x23, 35 */
+ MPUREG_I2C_MST_CTRL, /* 0x24, 36 */
+ MPUREG_I2C_SLV0_ADDR, /* 0x25, 37 */
+ MPUREG_I2C_SLV0_REG, /* 0x26, 38 */
+ MPUREG_I2C_SLV0_CTRL, /* 0x27, 39 */
+ MPUREG_I2C_SLV1_ADDR, /* 0x28, 40 */
+ MPUREG_I2C_SLV1_REG_PASSWORD, /* 0x29, 41 */
+ MPUREG_I2C_SLV1_CTRL, /* 0x2a, 42 */
+ MPUREG_I2C_SLV2_ADDR, /* 0x2B, 43 */
+ MPUREG_I2C_SLV2_REG, /* 0x2c, 44 */
+ MPUREG_I2C_SLV2_CTRL, /* 0x2d, 45 */
+ MPUREG_I2C_SLV3_ADDR, /* 0x2E, 46 */
+ MPUREG_I2C_SLV3_REG, /* 0x2f, 47 */
+ MPUREG_I2C_SLV3_CTRL, /* 0x30, 48 */
+ MPUREG_I2C_SLV4_ADDR, /* 0x31, 49 */
+ MPUREG_I2C_SLV4_REG, /* 0x32, 50 */
+ MPUREG_I2C_SLV4_DO, /* 0x33, 51 */
+ MPUREG_I2C_SLV4_CTRL, /* 0x34, 52 */
+ MPUREG_I2C_SLV4_DI, /* 0x35, 53 */
+ MPUREG_I2C_MST_STATUS, /* 0x36, 54 */
+ MPUREG_INT_PIN_CFG, /* 0x37, 55 */
+ MPUREG_INT_ENABLE, /* 0x38, 56 */
+ MPUREG_DMP_INT_STATUS, /* 0x39, 57 */
+ MPUREG_INT_STATUS, /* 0x3A, 58 */
+ MPUREG_ACCEL_XOUT_H, /* 0x3B, 59 */
+ MPUREG_ACCEL_XOUT_L, /* 0x3c, 60 */
+ MPUREG_ACCEL_YOUT_H, /* 0x3d, 61 */
+ MPUREG_ACCEL_YOUT_L, /* 0x3e, 62 */
+ MPUREG_ACCEL_ZOUT_H, /* 0x3f, 63 */
+ MPUREG_ACCEL_ZOUT_L, /* 0x40, 64 */
+ MPUREG_TEMP_OUT_H, /* 0x41, 65 */
+ MPUREG_TEMP_OUT_L, /* 0x42, 66 */
+ MPUREG_GYRO_XOUT_H, /* 0x43, 67 */
+ MPUREG_GYRO_XOUT_L, /* 0x44, 68 */
+ MPUREG_GYRO_YOUT_H, /* 0x45, 69 */
+ MPUREG_GYRO_YOUT_L, /* 0x46, 70 */
+ MPUREG_GYRO_ZOUT_H, /* 0x47, 71 */
+ MPUREG_GYRO_ZOUT_L, /* 0x48, 72 */
+ MPUREG_EXT_SLV_SENS_DATA_00, /* 0x49, 73 */
+ MPUREG_EXT_SLV_SENS_DATA_01, /* 0x4a, 74 */
+ MPUREG_EXT_SLV_SENS_DATA_02, /* 0x4b, 75 */
+ MPUREG_EXT_SLV_SENS_DATA_03, /* 0x4c, 76 */
+ MPUREG_EXT_SLV_SENS_DATA_04, /* 0x4d, 77 */
+ MPUREG_EXT_SLV_SENS_DATA_05, /* 0x4e, 78 */
+ MPUREG_EXT_SLV_SENS_DATA_06, /* 0x4F, 79 */
+ MPUREG_EXT_SLV_SENS_DATA_07, /* 0x50, 80 */
+ MPUREG_EXT_SLV_SENS_DATA_08, /* 0x51, 81 */
+ MPUREG_EXT_SLV_SENS_DATA_09, /* 0x52, 82 */
+ MPUREG_EXT_SLV_SENS_DATA_10, /* 0x53, 83 */
+ MPUREG_EXT_SLV_SENS_DATA_11, /* 0x54, 84 */
+ MPUREG_EXT_SLV_SENS_DATA_12, /* 0x55, 85 */
+ MPUREG_EXT_SLV_SENS_DATA_13, /* 0x56, 86 */
+ MPUREG_EXT_SLV_SENS_DATA_14, /* 0x57, 87 */
+ MPUREG_EXT_SLV_SENS_DATA_15, /* 0x58, 88 */
+ MPUREG_EXT_SLV_SENS_DATA_16, /* 0x59, 89 */
+ MPUREG_EXT_SLV_SENS_DATA_17, /* 0x5a, 90 */
+ MPUREG_EXT_SLV_SENS_DATA_18, /* 0x5B, 91 */
+ MPUREG_EXT_SLV_SENS_DATA_19, /* 0x5c, 92 */
+ MPUREG_EXT_SLV_SENS_DATA_20, /* 0x5d, 93 */
+ MPUREG_EXT_SLV_SENS_DATA_21, /* 0x5e, 94 */
+ MPUREG_EXT_SLV_SENS_DATA_22, /* 0x5f, 95 */
+ MPUREG_EXT_SLV_SENS_DATA_23, /* 0x60, 96 */
+ MPUREG_ACCEL_INTEL_STATUS, /* 0x61, 97 */
+ MPUREG_62_RSVD, /* 0x62, 98 */
+ MPUREG_63_RSVD, /* 0x63, 99 */
+ MPUREG_64_RSVD, /* 0x64, 100 */
+ MPUREG_65_RSVD, /* 0x65, 101 */
+ MPUREG_66_RSVD, /* 0x66, 102 */
+ MPUREG_67_RSVD, /* 0x67, 103 */
+ MPUREG_SIGNAL_PATH_RESET, /* 0x68, 104 */
+ MPUREG_ACCEL_INTEL_CTRL, /* 0x69, 105 */
+ MPUREG_USER_CTRL, /* 0x6A, 106 */
+ MPUREG_PWR_MGMT_1, /* 0x6B, 107 */
+ MPUREG_PWR_MGMT_2, /* 0x6C, 108 */
+ MPUREG_BANK_SEL, /* 0x6D, 109 */
+ MPUREG_MEM_START_ADDR, /* 0x6E, 100 */
+ MPUREG_MEM_R_W, /* 0x6F, 111 */
+ MPUREG_DMP_CFG_1, /* 0x70, 112 */
+ MPUREG_DMP_CFG_2, /* 0x71, 113 */
+ MPUREG_FIFO_COUNTH, /* 0x72, 114 */
+ MPUREG_FIFO_COUNTL, /* 0x73, 115 */
+ MPUREG_FIFO_R_W, /* 0x74, 116 */
+ MPUREG_WHOAMI, /* 0x75, 117 */
+
+ NUM_OF_MPU_REGISTERS /* = 0x76, 118 */
+};
+
+/*==== MPU6050A2 MEMORY ====*/
+enum MPU_MEMORY_BANKS {
+ MEM_RAM_BANK_0 = 0,
+ MEM_RAM_BANK_1,
+ MEM_RAM_BANK_2,
+ MEM_RAM_BANK_3,
+ MEM_RAM_BANK_4,
+ MEM_RAM_BANK_5,
+ MEM_RAM_BANK_6,
+ MEM_RAM_BANK_7,
+ MEM_RAM_BANK_8,
+ MEM_RAM_BANK_9,
+ MEM_RAM_BANK_10,
+ MEM_RAM_BANK_11,
+ MPU_MEM_NUM_RAM_BANKS,
+ MPU_MEM_OTP_BANK_0 = 16
+};
+
+
+/*==== MPU6050A2 parameters ====*/
+
+#define NUM_REGS (NUM_OF_MPU_REGISTERS)
+#define START_SENS_REGS (0x3B)
+#define NUM_SENS_REGS (0x60 - START_SENS_REGS + 1)
+
+/*---- MPU Memory ----*/
+#define NUM_BANKS (MPU_MEM_NUM_RAM_BANKS)
+#define BANK_SIZE (256)
+#define MEM_SIZE (NUM_BANKS * BANK_SIZE)
+#define MPU_MEM_BANK_SIZE (BANK_SIZE) /*alternative name */
+
+#define FIFO_HW_SIZE (1024)
+
+#define NUM_EXT_SLAVES (4)
+
+
+/*==== BITS FOR MPU6050A2 ====*/
+
+/*---- MPU6050A2 'XG_OFFS_TC' register (0, 1, 2) ----*/
+#define BIT_PWR_MODE 0x80
+#define BITS_XG_OFFS_TC 0x7E
+#define BIT_OTP_BNK_VLD 0x01
+
+#define BITS_YG_OFFS_TC 0x7E
+#define BITS_ZG_OFFS_TC 0x7E
+/*---- MPU6050A2 'FIFO_EN' register (23) ----*/
+#define BIT_TEMP_OUT 0x80
+#define BIT_GYRO_XOUT 0x40
+#define BIT_GYRO_YOUT 0x20
+#define BIT_GYRO_ZOUT 0x10
+#define BIT_ACCEL 0x08
+#define BIT_SLV_2 0x04
+#define BIT_SLV_1 0x02
+#define BIT_SLV_0 0x01
+/*---- MPU6050A2 'CONFIG' register (1A) ----*/
+/*NONE 0xC0 */
+#define BITS_EXT_SYNC_SET 0x38
+#define BITS_DLPF_CFG 0x07
+/*---- MPU6050A2 'GYRO_CONFIG' register (1B) ----*/
+/* voluntarily modified label from BITS_FS_SEL to
+ * BITS_GYRO_FS_SEL to avoid confusion with MPU
+ */
+#define BITS_GYRO_FS_SEL 0x18
+/*NONE 0x07 */
+/*---- MPU6050A2 'ACCEL_CONFIG' register (1C) ----*/
+#define BITS_ACCEL_FS_SEL 0x18
+#define BITS_ACCEL_HPF 0x07
+/*---- MPU6050A2 'I2C_MST_CTRL' register (24) ----*/
+#define BIT_MULT_MST_DIS 0x80
+#define BIT_WAIT_FOR_ES 0x40
+#define BIT_I2C_MST_VDDIO 0x20
+/*NONE 0x10 */
+#define BITS_I2C_MST_CLK 0x0F
+/*---- MPU6050A2 'I2C_SLV?_CTRL' register (27,2A,2D,30) ----*/
+#define BIT_SLV_ENABLE 0x80
+#define BIT_SLV_BYTE_SW 0x40
+/*NONE 0x20 */
+#define BIT_SLV_GRP 0x10
+#define BITS_SLV_LENG 0x0F
+/*---- MPU6050A2 'I2C_SLV4_ADDR' register (31) ----*/
+#define BIT_I2C_SLV4_RNW 0x80
+/*---- MPU6050A2 'I2C_SLV4_CTRL' register (34) ----*/
+#define BIT_I2C_SLV4_EN 0x80
+#define BIT_SLV4_DONE_INT_EN 0x40
+/*NONE 0x3F */
+/*---- MPU6050A2 'I2C_MST_STATUS' register (36) ----*/
+#define BIT_PASSTHROUGH 0x80
+#define BIT_I2C_SLV4_DONE 0x40
+#define BIT_I2C_LOST_ARB 0x20
+#define BIT_I2C_SLV4_NACK 0x10
+#define BIT_I2C_SLV3_NACK 0x08
+#define BIT_I2C_SLV2_NACK 0x04
+#define BIT_I2C_SLV1_NACK 0x02
+#define BIT_I2C_SLV0_NACK 0x01
+/*---- MPU6050A2 'INT_PIN_CFG' register (37) ----*/
+#define BIT_ACTL 0x80
+#define BIT_ACTL_LOW 0x80
+#define BIT_ACTL_HIGH 0x00
+#define BIT_OPEN 0x40
+#define BIT_LATCH_INT_EN 0x20
+#define BIT_INT_ANYRD_2CLEAR 0x10
+#define BIT_ACTL_FSYNC 0x08
+#define BIT_FSYNC_INT_EN 0x04
+#define BIT_BYPASS_EN 0x02
+#define BIT_CLKOUT_EN 0x01
+/*---- MPU6050A2 'INT_ENABLE' register (38) ----*/
+#define BIT_FF_EN 0x80
+#define BIT_MOT_EN 0x40
+#define BIT_ZMOT_EN 0x20
+#define BIT_FIFO_OVERFLOW_EN 0x10
+#define BIT_I2C_MST_INT_EN 0x08
+#define BIT_PLL_RDY_EN 0x04
+#define BIT_DMP_INT_EN 0x02
+#define BIT_RAW_RDY_EN 0x01
+/*---- MPU6050A2 'DMP_INT_STATUS' register (39) ----*/
+/*NONE 0x80 */
+/*NONE 0x40 */
+#define BIT_DMP_INT_5 0x20
+#define BIT_DMP_INT_4 0x10
+#define BIT_DMP_INT_3 0x08
+#define BIT_DMP_INT_2 0x04
+#define BIT_DMP_INT_1 0x02
+#define BIT_DMP_INT_0 0x01
+/*---- MPU6050A2 'INT_STATUS' register (3A) ----*/
+#define BIT_FF_INT 0x80
+#define BIT_MOT_INT 0x40
+#define BIT_ZMOT_INT 0x20
+#define BIT_FIFO_OVERFLOW_INT 0x10
+#define BIT_I2C_MST_INT 0x08
+#define BIT_PLL_RDY_INT 0x04
+#define BIT_DMP_INT 0x02
+#define BIT_RAW_DATA_RDY_INT 0x01
+/*---- MPU6050A2 'BANK_SEL' register (6D) ----*/
+#define BIT_PRFTCH_EN 0x40
+#define BIT_CFG_USER_BANK 0x20
+#define BITS_MEM_SEL 0x1f
+/*---- MPU6050A2 'USER_CTRL' register (6A) ----*/
+#define BIT_DMP_EN 0x80
+#define BIT_FIFO_EN 0x40
+#define BIT_I2C_MST_EN 0x20
+#define BIT_I2C_IF_DIS 0x10
+#define BIT_DMP_RST 0x08
+#define BIT_FIFO_RST 0x04
+#define BIT_I2C_MST_RST 0x02
+#define BIT_SIG_COND_RST 0x01
+/*---- MPU6050A2 'PWR_MGMT_1' register (6B) ----*/
+#define BIT_H_RESET 0x80
+#define BITS_PWRSEL 0x70
+#define BIT_WKUP_INT 0x08
+#define BITS_CLKSEL 0x07
+/*---- MPU6050A2 'PWR_MGMT_2' register (6C) ----*/
+#define BITS_LPA_WAKE_CTRL 0xC0
+#define BIT_STBY_XA 0x20
+#define BIT_STBY_YA 0x10
+#define BIT_STBY_ZA 0x08
+#define BIT_STBY_XG 0x04
+#define BIT_STBY_YG 0x02
+#define BIT_STBY_ZG 0x01
+
+#define ACCEL_MOT_THR_LSB (32) /* mg */
+#define ACCEL_MOT_DUR_LSB (1)
+#define ACCEL_ZRMOT_THR_LSB_CONVERSION(mg) ((mg * 1000) / 255)
+#define ACCEL_ZRMOT_DUR_LSB (64)
+
+/*----------------------------------------------------------------------------*/
+/*---- Alternative names to take care of conflicts with current mpu3050.h ----*/
+/*----------------------------------------------------------------------------*/
+
+/*-- registers --*/
+#define MPUREG_DLPF_FS_SYNC MPUREG_CONFIG /* 0x1A */
+
+#define MPUREG_PRODUCT_ID MPUREG_WHOAMI /* 0x75 HACK!*/
+#define MPUREG_PWR_MGM MPUREG_PWR_MGMT_1 /* 0x6B */
+#define MPUREG_FIFO_EN1 MPUREG_FIFO_EN /* 0x23 */
+#define MPUREG_INT_CFG MPUREG_INT_ENABLE /* 0x38 */
+#define MPUREG_X_OFFS_USRH MPUREG_XG_OFFS_USRH /* 0x13 */
+#define MPUREG_WHO_AM_I MPUREG_WHOAMI /* 0x75 */
+#define MPUREG_23_RSVD MPUREG_EXT_SLV_SENS_DATA_00 /* 0x49 */
+#define MPUREG_AUX_SLV_ADDR MPUREG_I2C_SLV0_ADDR /* 0x25 */
+#define MPUREG_ACCEL_BURST_ADDR MPUREG_I2C_SLV0_REG /* 0x26 */
+
+/*-- bits --*/
+/* 'USER_CTRL' register */
+#define BIT_AUX_IF_EN BIT_I2C_MST_EN
+#define BIT_AUX_RD_LENG BIT_I2C_MST_EN
+#define BIT_IME_IF_RST BIT_I2C_MST_RST
+#define BIT_GYRO_RST BIT_SIG_COND_RST
+/* 'INT_ENABLE' register */
+#define BIT_RAW_RDY BIT_RAW_DATA_RDY_INT
+#define BIT_MPU_RDY_EN BIT_PLL_RDY_EN
+/* 'INT_STATUS' register */
+#define BIT_INT_STATUS_FIFO_OVERLOW BIT_FIFO_OVERFLOW_INT
+
+
+
+/*---- MPU6050A2 Silicon Revisions ----*/
+#define MPU_SILICON_REV_A2 1 /* MPU6050A2 Device */
+#define MPU_SILICON_REV_B1 2 /* MPU6050A2 Device */
+
+/*---- structure containing control variables used by MLDL ----*/
+/*---- MPU clock source settings ----*/
+/*---- MPU filter selections ----*/
+enum mpu_filter {
+ MPU_FILTER_256HZ_NOLPF2 = 0,
+ MPU_FILTER_188HZ,
+ MPU_FILTER_98HZ,
+ MPU_FILTER_42HZ,
+ MPU_FILTER_20HZ,
+ MPU_FILTER_10HZ,
+ MPU_FILTER_5HZ,
+ MPU_FILTER_2100HZ_NOLPF,
+ NUM_MPU_FILTER
+};
+
+enum mpu_fullscale {
+ MPU_FS_250DPS = 0,
+ MPU_FS_500DPS,
+ MPU_FS_1000DPS,
+ MPU_FS_2000DPS,
+ NUM_MPU_FS
+};
+
+enum mpu_clock_sel {
+ MPU_CLK_SEL_INTERNAL = 0,
+ MPU_CLK_SEL_PLLGYROX,
+ MPU_CLK_SEL_PLLGYROY,
+ MPU_CLK_SEL_PLLGYROZ,
+ MPU_CLK_SEL_PLLEXT32K,
+ MPU_CLK_SEL_PLLEXT19M,
+ MPU_CLK_SEL_RESERVED,
+ MPU_CLK_SEL_STOP,
+ NUM_CLK_SEL
+};
+
+enum mpu_ext_sync {
+ MPU_EXT_SYNC_NONE = 0,
+ MPU_EXT_SYNC_TEMP,
+ MPU_EXT_SYNC_GYROX,
+ MPU_EXT_SYNC_GYROY,
+ MPU_EXT_SYNC_GYROZ,
+ MPU_EXT_SYNC_ACCELX,
+ MPU_EXT_SYNC_ACCELY,
+ MPU_EXT_SYNC_ACCELZ,
+ NUM_MPU_EXT_SYNC
+};
+
+#define MPUREG_CONFIG_VALUE(ext_sync, lpf) \
+ ((ext_sync << 3) | lpf)
+
+#define MPUREG_GYRO_CONFIG_VALUE(x_st, y_st, z_st, full_scale) \
+ ((x_st ? 0x80 : 0) | \
+ (y_st ? 0x70 : 0) | \
+ (z_st ? 0x60 : 0) | \
+ (full_scale << 3))
+
+#endif /* __MPU6050_H_ */
diff --git a/drivers/misc/inv_mpu/mpu6050b1.h b/drivers/misc/inv_mpu/mpu6050b1.h
new file mode 100644
index 0000000..4acc3be
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu6050b1.h
@@ -0,0 +1,432 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @defgroup
+ * @brief
+ *
+ * @{
+ * @file mpu6050.h
+ * @brief
+ */
+
+#ifndef __MPU_H_
+#error Do not include this file directly. Include mpu.h instead.
+#endif
+
+#ifndef __MPU6050B1_H_
+#define __MPU6050B1_H_
+
+#error Invalid or undefined CONFIG_MPU_SENSORS_MPUxxxx
+
+#define MPU_NAME "mpu6050B1"
+#define DEFAULT_MPU_SLAVEADDR 0x68
+
+/*==== MPU6050B1 REGISTER SET ====*/
+enum {
+ MPUREG_XG_OFFS_TC = 0, /* 0x00, 0 */
+ MPUREG_YG_OFFS_TC, /* 0x01, 1 */
+ MPUREG_ZG_OFFS_TC, /* 0x02, 2 */
+ MPUREG_X_FINE_GAIN, /* 0x03, 3 */
+ MPUREG_Y_FINE_GAIN, /* 0x04, 4 */
+ MPUREG_Z_FINE_GAIN, /* 0x05, 5 */
+ MPUREG_XA_OFFS_H, /* 0x06, 6 */
+ MPUREG_XA_OFFS_L, /* 0x07, 7 */
+ MPUREG_YA_OFFS_H, /* 0x08, 8 */
+ MPUREG_YA_OFFS_L, /* 0x09, 9 */
+ MPUREG_ZA_OFFS_H, /* 0x0a, 10 */
+ MPUREG_ZA_OFFS_L, /* 0x0B, 11 */
+ MPUREG_PRODUCT_ID, /* 0x0c, 12 */
+ MPUREG_0D_RSVD, /* 0x0d, 13 */
+ MPUREG_0E_RSVD, /* 0x0e, 14 */
+ MPUREG_0F_RSVD, /* 0x0f, 15 */
+ MPUREG_10_RSVD, /* 0x00, 16 */
+ MPUREG_11_RSVD, /* 0x11, 17 */
+ MPUREG_12_RSVD, /* 0x12, 18 */
+ MPUREG_XG_OFFS_USRH, /* 0x13, 19 */
+ MPUREG_XG_OFFS_USRL, /* 0x14, 20 */
+ MPUREG_YG_OFFS_USRH, /* 0x15, 21 */
+ MPUREG_YG_OFFS_USRL, /* 0x16, 22 */
+ MPUREG_ZG_OFFS_USRH, /* 0x17, 23 */
+ MPUREG_ZG_OFFS_USRL, /* 0x18, 24 */
+ MPUREG_SMPLRT_DIV, /* 0x19, 25 */
+ MPUREG_CONFIG, /* 0x1A, 26 */
+ MPUREG_GYRO_CONFIG, /* 0x1b, 27 */
+ MPUREG_ACCEL_CONFIG, /* 0x1c, 28 */
+ MPUREG_ACCEL_FF_THR, /* 0x1d, 29 */
+ MPUREG_ACCEL_FF_DUR, /* 0x1e, 30 */
+ MPUREG_ACCEL_MOT_THR, /* 0x1f, 31 */
+ MPUREG_ACCEL_MOT_DUR, /* 0x20, 32 */
+ MPUREG_ACCEL_ZRMOT_THR, /* 0x21, 33 */
+ MPUREG_ACCEL_ZRMOT_DUR, /* 0x22, 34 */
+ MPUREG_FIFO_EN, /* 0x23, 35 */
+ MPUREG_I2C_MST_CTRL, /* 0x24, 36 */
+ MPUREG_I2C_SLV0_ADDR, /* 0x25, 37 */
+ MPUREG_I2C_SLV0_REG, /* 0x26, 38 */
+ MPUREG_I2C_SLV0_CTRL, /* 0x27, 39 */
+ MPUREG_I2C_SLV1_ADDR, /* 0x28, 40 */
+ MPUREG_I2C_SLV1_REG, /* 0x29, 41 */
+ MPUREG_I2C_SLV1_CTRL, /* 0x2a, 42 */
+ MPUREG_I2C_SLV2_ADDR, /* 0x2B, 43 */
+ MPUREG_I2C_SLV2_REG, /* 0x2c, 44 */
+ MPUREG_I2C_SLV2_CTRL, /* 0x2d, 45 */
+ MPUREG_I2C_SLV3_ADDR, /* 0x2E, 46 */
+ MPUREG_I2C_SLV3_REG, /* 0x2f, 47 */
+ MPUREG_I2C_SLV3_CTRL, /* 0x30, 48 */
+ MPUREG_I2C_SLV4_ADDR, /* 0x31, 49 */
+ MPUREG_I2C_SLV4_REG, /* 0x32, 50 */
+ MPUREG_I2C_SLV4_DO, /* 0x33, 51 */
+ MPUREG_I2C_SLV4_CTRL, /* 0x34, 52 */
+ MPUREG_I2C_SLV4_DI, /* 0x35, 53 */
+ MPUREG_I2C_MST_STATUS, /* 0x36, 54 */
+ MPUREG_INT_PIN_CFG, /* 0x37, 55 */
+ MPUREG_INT_ENABLE, /* 0x38, 56 */
+ MPUREG_DMP_INT_STATUS, /* 0x39, 57 */
+ MPUREG_INT_STATUS, /* 0x3A, 58 */
+ MPUREG_ACCEL_XOUT_H, /* 0x3B, 59 */
+ MPUREG_ACCEL_XOUT_L, /* 0x3c, 60 */
+ MPUREG_ACCEL_YOUT_H, /* 0x3d, 61 */
+ MPUREG_ACCEL_YOUT_L, /* 0x3e, 62 */
+ MPUREG_ACCEL_ZOUT_H, /* 0x3f, 63 */
+ MPUREG_ACCEL_ZOUT_L, /* 0x40, 64 */
+ MPUREG_TEMP_OUT_H, /* 0x41, 65 */
+ MPUREG_TEMP_OUT_L, /* 0x42, 66 */
+ MPUREG_GYRO_XOUT_H, /* 0x43, 67 */
+ MPUREG_GYRO_XOUT_L, /* 0x44, 68 */
+ MPUREG_GYRO_YOUT_H, /* 0x45, 69 */
+ MPUREG_GYRO_YOUT_L, /* 0x46, 70 */
+ MPUREG_GYRO_ZOUT_H, /* 0x47, 71 */
+ MPUREG_GYRO_ZOUT_L, /* 0x48, 72 */
+ MPUREG_EXT_SLV_SENS_DATA_00, /* 0x49, 73 */
+ MPUREG_EXT_SLV_SENS_DATA_01, /* 0x4a, 74 */
+ MPUREG_EXT_SLV_SENS_DATA_02, /* 0x4b, 75 */
+ MPUREG_EXT_SLV_SENS_DATA_03, /* 0x4c, 76 */
+ MPUREG_EXT_SLV_SENS_DATA_04, /* 0x4d, 77 */
+ MPUREG_EXT_SLV_SENS_DATA_05, /* 0x4e, 78 */
+ MPUREG_EXT_SLV_SENS_DATA_06, /* 0x4F, 79 */
+ MPUREG_EXT_SLV_SENS_DATA_07, /* 0x50, 80 */
+ MPUREG_EXT_SLV_SENS_DATA_08, /* 0x51, 81 */
+ MPUREG_EXT_SLV_SENS_DATA_09, /* 0x52, 82 */
+ MPUREG_EXT_SLV_SENS_DATA_10, /* 0x53, 83 */
+ MPUREG_EXT_SLV_SENS_DATA_11, /* 0x54, 84 */
+ MPUREG_EXT_SLV_SENS_DATA_12, /* 0x55, 85 */
+ MPUREG_EXT_SLV_SENS_DATA_13, /* 0x56, 86 */
+ MPUREG_EXT_SLV_SENS_DATA_14, /* 0x57, 87 */
+ MPUREG_EXT_SLV_SENS_DATA_15, /* 0x58, 88 */
+ MPUREG_EXT_SLV_SENS_DATA_16, /* 0x59, 89 */
+ MPUREG_EXT_SLV_SENS_DATA_17, /* 0x5a, 90 */
+ MPUREG_EXT_SLV_SENS_DATA_18, /* 0x5B, 91 */
+ MPUREG_EXT_SLV_SENS_DATA_19, /* 0x5c, 92 */
+ MPUREG_EXT_SLV_SENS_DATA_20, /* 0x5d, 93 */
+ MPUREG_EXT_SLV_SENS_DATA_21, /* 0x5e, 94 */
+ MPUREG_EXT_SLV_SENS_DATA_22, /* 0x5f, 95 */
+ MPUREG_EXT_SLV_SENS_DATA_23, /* 0x60, 96 */
+ MPUREG_ACCEL_INTEL_STATUS, /* 0x61, 97 */
+ MPUREG_62_RSVD, /* 0x62, 98 */
+ MPUREG_I2C_SLV0_DO, /* 0x63, 99 */
+ MPUREG_I2C_SLV1_DO, /* 0x64, 100 */
+ MPUREG_I2C_SLV2_DO, /* 0x65, 101 */
+ MPUREG_I2C_SLV3_DO, /* 0x66, 102 */
+ MPUREG_I2C_MST_DELAY_CTRL, /* 0x67, 103 */
+ MPUREG_SIGNAL_PATH_RESET, /* 0x68, 104 */
+ MPUREG_ACCEL_INTEL_CTRL, /* 0x69, 105 */
+ MPUREG_USER_CTRL, /* 0x6A, 106 */
+ MPUREG_PWR_MGMT_1, /* 0x6B, 107 */
+ MPUREG_PWR_MGMT_2, /* 0x6C, 108 */
+ MPUREG_BANK_SEL, /* 0x6D, 109 */
+ MPUREG_MEM_START_ADDR, /* 0x6E, 100 */
+ MPUREG_MEM_R_W, /* 0x6F, 111 */
+ MPUREG_DMP_CFG_1, /* 0x70, 112 */
+ MPUREG_DMP_CFG_2, /* 0x71, 113 */
+ MPUREG_FIFO_COUNTH, /* 0x72, 114 */
+ MPUREG_FIFO_COUNTL, /* 0x73, 115 */
+ MPUREG_FIFO_R_W, /* 0x74, 116 */
+ MPUREG_WHOAMI, /* 0x75, 117 */
+
+ NUM_OF_MPU_REGISTERS /* = 0x76, 118 */
+};
+
+/*==== MPU6050B1 MEMORY ====*/
+enum MPU_MEMORY_BANKS {
+ MEM_RAM_BANK_0 = 0,
+ MEM_RAM_BANK_1,
+ MEM_RAM_BANK_2,
+ MEM_RAM_BANK_3,
+ MEM_RAM_BANK_4,
+ MEM_RAM_BANK_5,
+ MEM_RAM_BANK_6,
+ MEM_RAM_BANK_7,
+ MEM_RAM_BANK_8,
+ MEM_RAM_BANK_9,
+ MEM_RAM_BANK_10,
+ MEM_RAM_BANK_11,
+ MPU_MEM_NUM_RAM_BANKS,
+ MPU_MEM_OTP_BANK_0 = 16
+};
+
+
+/*==== MPU6050B1 parameters ====*/
+
+#define NUM_REGS (NUM_OF_MPU_REGISTERS)
+#define START_SENS_REGS (0x3B)
+#define NUM_SENS_REGS (0x60 - START_SENS_REGS + 1)
+
+/*---- MPU Memory ----*/
+#define NUM_BANKS (MPU_MEM_NUM_RAM_BANKS)
+#define BANK_SIZE (256)
+#define MEM_SIZE (NUM_BANKS * BANK_SIZE)
+#define MPU_MEM_BANK_SIZE (BANK_SIZE) /*alternative name */
+
+#define FIFO_HW_SIZE (1024)
+
+#define NUM_EXT_SLAVES (4)
+
+
+/*==== BITS FOR MPU6050B1 ====*/
+/*---- MPU6050B1 'XG_OFFS_TC' register (0, 1, 2) ----*/
+#define BIT_PU_SLEEP_MODE 0x80
+#define BITS_XG_OFFS_TC 0x7E
+#define BIT_OTP_BNK_VLD 0x01
+
+#define BIT_I2C_MST_VDDIO 0x80
+#define BITS_YG_OFFS_TC 0x7E
+#define BITS_ZG_OFFS_TC 0x7E
+/*---- MPU6050B1 'FIFO_EN' register (23) ----*/
+#define BIT_TEMP_OUT 0x80
+#define BIT_GYRO_XOUT 0x40
+#define BIT_GYRO_YOUT 0x20
+#define BIT_GYRO_ZOUT 0x10
+#define BIT_ACCEL 0x08
+#define BIT_SLV_2 0x04
+#define BIT_SLV_1 0x02
+#define BIT_SLV_0 0x01
+/*---- MPU6050B1 'CONFIG' register (1A) ----*/
+/*NONE 0xC0 */
+#define BITS_EXT_SYNC_SET 0x38
+#define BITS_DLPF_CFG 0x07
+/*---- MPU6050B1 'GYRO_CONFIG' register (1B) ----*/
+/* voluntarily modified label from BITS_FS_SEL to
+ * BITS_GYRO_FS_SEL to avoid confusion with MPU
+ */
+#define BITS_GYRO_FS_SEL 0x18
+/*NONE 0x07 */
+/*---- MPU6050B1 'ACCEL_CONFIG' register (1C) ----*/
+#define BITS_ACCEL_FS_SEL 0x18
+#define BITS_ACCEL_HPF 0x07
+/*---- MPU6050B1 'I2C_MST_CTRL' register (24) ----*/
+#define BIT_MULT_MST_EN 0x80
+#define BIT_WAIT_FOR_ES 0x40
+#define BIT_SLV_3_FIFO_EN 0x20
+#define BIT_I2C_MST_PSR 0x10
+#define BITS_I2C_MST_CLK 0x0F
+/*---- MPU6050B1 'I2C_SLV?_ADDR' register (27,2A,2D,30) ----*/
+#define BIT_I2C_READ 0x80
+#define BIT_I2C_WRITE 0x00
+#define BITS_I2C_ADDR 0x7F
+/*---- MPU6050B1 'I2C_SLV?_CTRL' register (27,2A,2D,30) ----*/
+#define BIT_SLV_ENABLE 0x80
+#define BIT_SLV_BYTE_SW 0x40
+#define BIT_SLV_REG_DIS 0x20
+#define BIT_SLV_GRP 0x10
+#define BITS_SLV_LENG 0x0F
+/*---- MPU6050B1 'I2C_SLV4_ADDR' register (31) ----*/
+#define BIT_I2C_SLV4_RNW 0x80
+/*---- MPU6050B1 'I2C_SLV4_CTRL' register (34) ----*/
+#define BIT_I2C_SLV4_EN 0x80
+#define BIT_SLV4_DONE_INT_EN 0x40
+#define BIT_SLV4_REG_DIS 0x20
+#define MASK_I2C_MST_DLY 0x1F
+/*---- MPU6050B1 'I2C_MST_STATUS' register (36) ----*/
+#define BIT_PASS_THROUGH 0x80
+#define BIT_I2C_SLV4_DONE 0x40
+#define BIT_I2C_LOST_ARB 0x20
+#define BIT_I2C_SLV4_NACK 0x10
+#define BIT_I2C_SLV3_NACK 0x08
+#define BIT_I2C_SLV2_NACK 0x04
+#define BIT_I2C_SLV1_NACK 0x02
+#define BIT_I2C_SLV0_NACK 0x01
+/*---- MPU6050B1 'INT_PIN_CFG' register (37) ----*/
+#define BIT_ACTL 0x80
+#define BIT_ACTL_LOW 0x80
+#define BIT_ACTL_HIGH 0x00
+#define BIT_OPEN 0x40
+#define BIT_LATCH_INT_EN 0x20
+#define BIT_INT_ANYRD_2CLEAR 0x10
+#define BIT_ACTL_FSYNC 0x08
+#define BIT_FSYNC_INT_EN 0x04
+#define BIT_BYPASS_EN 0x02
+#define BIT_CLKOUT_EN 0x01
+/*---- MPU6050B1 'INT_ENABLE' register (38) ----*/
+#define BIT_FF_EN 0x80
+#define BIT_MOT_EN 0x40
+#define BIT_ZMOT_EN 0x20
+#define BIT_FIFO_OVERFLOW_EN 0x10
+#define BIT_I2C_MST_INT_EN 0x08
+#define BIT_PLL_RDY_EN 0x04
+#define BIT_DMP_INT_EN 0x02
+#define BIT_RAW_RDY_EN 0x01
+/*---- MPU6050B1 'DMP_INT_STATUS' register (39) ----*/
+/*NONE 0x80 */
+/*NONE 0x40 */
+#define BIT_DMP_INT_5 0x20
+#define BIT_DMP_INT_4 0x10
+#define BIT_DMP_INT_3 0x08
+#define BIT_DMP_INT_2 0x04
+#define BIT_DMP_INT_1 0x02
+#define BIT_DMP_INT_0 0x01
+/*---- MPU6050B1 'INT_STATUS' register (3A) ----*/
+#define BIT_FF_INT 0x80
+#define BIT_MOT_INT 0x40
+#define BIT_ZMOT_INT 0x20
+#define BIT_FIFO_OVERFLOW_INT 0x10
+#define BIT_I2C_MST_INT 0x08
+#define BIT_PLL_RDY_INT 0x04
+#define BIT_DMP_INT 0x02
+#define BIT_RAW_DATA_RDY_INT 0x01
+/*---- MPU6050B1 'MPUREG_I2C_MST_DELAY_CTRL' register (0x67) ----*/
+#define BIT_DELAY_ES_SHADOW 0x80
+#define BIT_SLV4_DLY_EN 0x10
+#define BIT_SLV3_DLY_EN 0x08
+#define BIT_SLV2_DLY_EN 0x04
+#define BIT_SLV1_DLY_EN 0x02
+#define BIT_SLV0_DLY_EN 0x01
+/*---- MPU6050B1 'BANK_SEL' register (6D) ----*/
+#define BIT_PRFTCH_EN 0x40
+#define BIT_CFG_USER_BANK 0x20
+#define BITS_MEM_SEL 0x1f
+/*---- MPU6050B1 'USER_CTRL' register (6A) ----*/
+#define BIT_DMP_EN 0x80
+#define BIT_FIFO_EN 0x40
+#define BIT_I2C_MST_EN 0x20
+#define BIT_I2C_IF_DIS 0x10
+#define BIT_DMP_RST 0x08
+#define BIT_FIFO_RST 0x04
+#define BIT_I2C_MST_RST 0x02
+#define BIT_SIG_COND_RST 0x01
+/*---- MPU6050B1 'PWR_MGMT_1' register (6B) ----*/
+#define BIT_H_RESET 0x80
+#define BIT_SLEEP 0x40
+#define BIT_CYCLE 0x20
+#define BIT_PD_PTAT 0x08
+#define BITS_CLKSEL 0x07
+/*---- MPU6050B1 'PWR_MGMT_2' register (6C) ----*/
+#define BITS_LPA_WAKE_CTRL 0xC0
+#define BIT_STBY_XA 0x20
+#define BIT_STBY_YA 0x10
+#define BIT_STBY_ZA 0x08
+#define BIT_STBY_XG 0x04
+#define BIT_STBY_YG 0x02
+#define BIT_STBY_ZG 0x01
+
+#define ACCEL_MOT_THR_LSB (32) /* mg */
+#define ACCEL_MOT_DUR_LSB (1)
+#define ACCEL_ZRMOT_THR_LSB_CONVERSION(mg) ((mg * 1000) / 255)
+#define ACCEL_ZRMOT_DUR_LSB (64)
+
+/*----------------------------------------------------------------------------*/
+/*---- Alternative names to take care of conflicts with current mpu3050.h ----*/
+/*----------------------------------------------------------------------------*/
+
+/*-- registers --*/
+#define MPUREG_DLPF_FS_SYNC MPUREG_CONFIG /* 0x1A */
+
+#define MPUREG_PWR_MGM MPUREG_PWR_MGMT_1 /* 0x6B */
+#define MPUREG_FIFO_EN1 MPUREG_FIFO_EN /* 0x23 */
+#define MPUREG_INT_CFG MPUREG_INT_ENABLE /* 0x38 */
+#define MPUREG_X_OFFS_USRH MPUREG_XG_OFFS_USRH /* 0x13 */
+#define MPUREG_WHO_AM_I MPUREG_WHOAMI /* 0x75 */
+#define MPUREG_23_RSVD MPUREG_EXT_SLV_SENS_DATA_00 /* 0x49 */
+
+/*-- bits --*/
+/* 'USER_CTRL' register */
+#define BIT_AUX_IF_EN BIT_I2C_MST_EN
+#define BIT_AUX_RD_LENG BIT_I2C_MST_EN
+#define BIT_IME_IF_RST BIT_I2C_MST_RST
+#define BIT_GYRO_RST BIT_SIG_COND_RST
+/* 'INT_ENABLE' register */
+#define BIT_RAW_RDY BIT_RAW_DATA_RDY_INT
+#define BIT_MPU_RDY_EN BIT_PLL_RDY_EN
+/* 'INT_STATUS' register */
+#define BIT_INT_STATUS_FIFO_OVERLOW BIT_FIFO_OVERFLOW_INT
+
+/*---- MPU6050 Silicon Revisions ----*/
+#define MPU_SILICON_REV_A2 1 /* MPU6050A2 Device */
+#define MPU_SILICON_REV_B1 2 /* MPU6050B1 Device */
+
+/*---- MPU6050 notable product revisions ----*/
+#define MPU_PRODUCT_KEY_B1_E1_5 105
+#define MPU_PRODUCT_KEY_B2_F1 431
+
+/*---- structure containing control variables used by MLDL ----*/
+/*---- MPU clock source settings ----*/
+/*---- MPU filter selections ----*/
+enum mpu_filter {
+ MPU_FILTER_256HZ_NOLPF2 = 0,
+ MPU_FILTER_188HZ,
+ MPU_FILTER_98HZ,
+ MPU_FILTER_42HZ,
+ MPU_FILTER_20HZ,
+ MPU_FILTER_10HZ,
+ MPU_FILTER_5HZ,
+ MPU_FILTER_2100HZ_NOLPF,
+ NUM_MPU_FILTER
+};
+
+enum mpu_fullscale {
+ MPU_FS_250DPS = 0,
+ MPU_FS_500DPS,
+ MPU_FS_1000DPS,
+ MPU_FS_2000DPS,
+ NUM_MPU_FS
+};
+
+enum mpu_clock_sel {
+ MPU_CLK_SEL_INTERNAL = 0,
+ MPU_CLK_SEL_PLLGYROX,
+ MPU_CLK_SEL_PLLGYROY,
+ MPU_CLK_SEL_PLLGYROZ,
+ MPU_CLK_SEL_PLLEXT32K,
+ MPU_CLK_SEL_PLLEXT19M,
+ MPU_CLK_SEL_RESERVED,
+ MPU_CLK_SEL_STOP,
+ NUM_CLK_SEL
+};
+
+enum mpu_ext_sync {
+ MPU_EXT_SYNC_NONE = 0,
+ MPU_EXT_SYNC_TEMP,
+ MPU_EXT_SYNC_GYROX,
+ MPU_EXT_SYNC_GYROY,
+ MPU_EXT_SYNC_GYROZ,
+ MPU_EXT_SYNC_ACCELX,
+ MPU_EXT_SYNC_ACCELY,
+ MPU_EXT_SYNC_ACCELZ,
+ NUM_MPU_EXT_SYNC
+};
+
+#define MPUREG_CONFIG_VALUE(ext_sync, lpf) \
+ ((ext_sync << 3) | lpf)
+
+#define MPUREG_GYRO_CONFIG_VALUE(x_st, y_st, z_st, full_scale) \
+ ((x_st ? 0x80 : 0) | \
+ (y_st ? 0x70 : 0) | \
+ (z_st ? 0x60 : 0) | \
+ (full_scale << 3))
+
+#endif /* __MPU6050_H_ */
diff --git a/drivers/misc/inv_mpu/mpuirq.c b/drivers/misc/inv_mpu/mpuirq.c
new file mode 100644
index 0000000..094712c
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpuirq.c
@@ -0,0 +1,253 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/irq.h>
+#include <linux/signal.h>
+#include <linux/miscdevice.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/poll.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+
+#include <linux/mpu.h>
+#include "mpuirq.h"
+#include "mldl_cfg.h"
+
+#define MPUIRQ_NAME "mpuirq"
+
+/* function which gets accel data and sends it to MPU */
+
+DECLARE_WAIT_QUEUE_HEAD(mpuirq_wait);
+
+struct mpuirq_dev_data {
+ struct i2c_client *mpu_client;
+ struct miscdevice *dev;
+ int irq;
+ int pid;
+ int accel_divider;
+ int data_ready;
+ int timeout;
+};
+
+static struct mpuirq_dev_data mpuirq_dev_data;
+static struct mpuirq_data mpuirq_data;
+static char *interface = MPUIRQ_NAME;
+
+static int mpuirq_open(struct inode *inode, struct file *file)
+{
+ dev_dbg(mpuirq_dev_data.dev->this_device,
+ "%s current->pid %d\n", __func__, current->pid);
+ mpuirq_dev_data.pid = current->pid;
+ file->private_data = &mpuirq_dev_data;
+ return 0;
+}
+
+/* close function - called when the "file" /dev/mpuirq is closed in userspace */
+static int mpuirq_release(struct inode *inode, struct file *file)
+{
+ dev_dbg(mpuirq_dev_data.dev->this_device, "mpuirq_release\n");
+ return 0;
+}
+
+/* read function called when from /dev/mpuirq is read */
+static ssize_t mpuirq_read(struct file *file,
+ char *buf, size_t count, loff_t *ppos)
+{
+ int len, err;
+ struct mpuirq_dev_data *p_mpuirq_dev_data = file->private_data;
+
+ if (!mpuirq_dev_data.data_ready &&
+ mpuirq_dev_data.timeout && (!(file->f_flags & O_NONBLOCK))) {
+ wait_event_interruptible_timeout(mpuirq_wait,
+ mpuirq_dev_data.data_ready,
+ mpuirq_dev_data.timeout);
+ }
+
+ if (mpuirq_dev_data.data_ready && NULL != buf
+ && count >= sizeof(mpuirq_data)) {
+ err = copy_to_user(buf, &mpuirq_data, sizeof(mpuirq_data));
+ mpuirq_data.data_type = 0;
+ } else {
+ return 0;
+ }
+ if (err != 0) {
+ dev_err(p_mpuirq_dev_data->dev->this_device,
+ "Copy to user returned %d\n", err);
+ return -EFAULT;
+ }
+ mpuirq_dev_data.data_ready = 0;
+ len = sizeof(mpuirq_data);
+ return len;
+}
+
+unsigned int mpuirq_poll(struct file *file, struct poll_table_struct *poll)
+{
+ int mask = 0;
+
+ poll_wait(file, &mpuirq_wait, poll);
+ if (mpuirq_dev_data.data_ready)
+ mask |= POLLIN | POLLRDNORM;
+ return mask;
+}
+
+/* ioctl - I/O control */
+static long mpuirq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ int data;
+
+ switch (cmd) {
+ case MPUIRQ_SET_TIMEOUT:
+ mpuirq_dev_data.timeout = arg;
+ break;
+
+ case MPUIRQ_GET_INTERRUPT_CNT:
+ data = mpuirq_data.interruptcount - 1;
+ if (mpuirq_data.interruptcount > 1)
+ mpuirq_data.interruptcount = 1;
+
+ if (copy_to_user((int *)arg, &data, sizeof(int)))
+ return -EFAULT;
+ break;
+ case MPUIRQ_GET_IRQ_TIME:
+ if (copy_to_user((int *)arg, &mpuirq_data.irqtime,
+ sizeof(mpuirq_data.irqtime)))
+ return -EFAULT;
+ mpuirq_data.irqtime = 0;
+ break;
+ case MPUIRQ_SET_FREQUENCY_DIVIDER:
+ mpuirq_dev_data.accel_divider = arg;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+static irqreturn_t mpuirq_handler(int irq, void *dev_id)
+{
+ static int mycount;
+ mycount++;
+
+ mpuirq_data.interruptcount++;
+
+ /* wake up (unblock) for reading data from userspace */
+ /* and ignore first interrupt generated in module init */
+ mpuirq_dev_data.data_ready = 1;
+
+ mpuirq_data.irqtime = ktime_to_ns(ktime_get());
+ mpuirq_data.data_type = MPUIRQ_DATA_TYPE_MPU_IRQ;
+ mpuirq_data.data = 0;
+
+ wake_up_interruptible(&mpuirq_wait);
+
+ return IRQ_HANDLED;
+
+}
+
+/* define which file operations are supported */
+const struct file_operations mpuirq_fops = {
+ .owner = THIS_MODULE,
+ .read = mpuirq_read,
+ .poll = mpuirq_poll,
+
+ .unlocked_ioctl = mpuirq_ioctl,
+ .open = mpuirq_open,
+ .release = mpuirq_release,
+};
+
+static struct miscdevice mpuirq_device = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = MPUIRQ_NAME,
+ .fops = &mpuirq_fops,
+};
+
+int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg)
+{
+
+ int res;
+
+ mpuirq_dev_data.mpu_client = mpu_client;
+
+ dev_info(&mpu_client->adapter->dev,
+ "Module Param interface = %s\n", interface);
+
+ mpuirq_dev_data.irq = mpu_client->irq;
+ mpuirq_dev_data.pid = 0;
+ mpuirq_dev_data.accel_divider = -1;
+ mpuirq_dev_data.data_ready = 0;
+ mpuirq_dev_data.timeout = 0;
+ mpuirq_dev_data.dev = &mpuirq_device;
+
+ if (mpuirq_dev_data.irq) {
+ unsigned long flags;
+ if (BIT_ACTL_LOW == ((mldl_cfg->pdata->int_config) & BIT_ACTL))
+ flags = IRQF_TRIGGER_FALLING;
+ else
+ flags = IRQF_TRIGGER_RISING;
+
+ res =
+ request_irq(mpuirq_dev_data.irq, mpuirq_handler, flags,
+ interface, &mpuirq_dev_data.irq);
+ if (res) {
+ dev_err(&mpu_client->adapter->dev,
+ "myirqtest: cannot register IRQ %d\n",
+ mpuirq_dev_data.irq);
+ } else {
+ res = misc_register(&mpuirq_device);
+ if (res < 0) {
+ dev_err(&mpu_client->adapter->dev,
+ "misc_register returned %d\n", res);
+ free_irq(mpuirq_dev_data.irq,
+ &mpuirq_dev_data.irq);
+ }
+ }
+
+ } else {
+ res = 0;
+ }
+
+ return res;
+}
+
+void mpuirq_exit(void)
+{
+ if (mpuirq_dev_data.irq > 0)
+ free_irq(mpuirq_dev_data.irq, &mpuirq_dev_data.irq);
+
+ dev_info(mpuirq_device.this_device, "Unregistering %s\n", MPUIRQ_NAME);
+ misc_deregister(&mpuirq_device);
+
+ return;
+}
+
+module_param(interface, charp, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(interface, "The Interface name");
diff --git a/drivers/misc/inv_mpu/mpuirq.h b/drivers/misc/inv_mpu/mpuirq.h
new file mode 100644
index 0000000..36b2faf
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpuirq.h
@@ -0,0 +1,36 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+#ifndef __MPUIRQ__
+#define __MPUIRQ__
+
+#include <linux/i2c-dev.h>
+#include <linux/time.h>
+#include <linux/ioctl.h>
+#include "mldl_cfg.h"
+
+#define MPUIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x40, unsigned long)
+#define MPUIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x41, unsigned long)
+#define MPUIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x42, struct timeval)
+#define MPUIRQ_SET_FREQUENCY_DIVIDER _IOW(MPU_IOCTL, 0x43, unsigned long)
+
+void mpuirq_exit(void);
+int mpuirq_init(struct i2c_client *mpu_client, struct mldl_cfg *mldl_cfg);
+
+#endif
diff --git a/drivers/misc/inv_mpu/pressure/Kconfig b/drivers/misc/inv_mpu/pressure/Kconfig
new file mode 100644
index 0000000..f1c021e
--- /dev/null
+++ b/drivers/misc/inv_mpu/pressure/Kconfig
@@ -0,0 +1,20 @@
+menuconfig: INV_SENSORS_PRESSURE
+ bool "Pressure Sensor Slaves"
+ depends on INV_SENSORS
+ default y
+ help
+ Select y to see a list of supported pressure sensors that can be
+ integrated with the MPUxxxx set of motion processors.
+
+if INV_SENSORS_PRESSURE
+
+config MPU_SENSORS_BMA085
+ tristate "Bosch BMA085"
+ help
+ This enables support for the Bosch bma085 pressure sensor
+ This support is for integration with the MPU3050 or MPU6050 gyroscope
+ device driver. Only one accelerometer can be registered at a time.
+ Specifying more that one accelerometer in the board file will result
+ in runtime errors.
+
+endif
diff --git a/drivers/misc/inv_mpu/pressure/Makefile b/drivers/misc/inv_mpu/pressure/Makefile
new file mode 100644
index 0000000..dd669d2
--- /dev/null
+++ b/drivers/misc/inv_mpu/pressure/Makefile
@@ -0,0 +1,7 @@
+#
+# Pressure Slaves to MPUxxxx
+#
+obj-$(CONFIG_MPU_SENSORS_BMA085) += inv_mpu_bma085.o
+inv_mpu_bma085-objs += bma085.o
+
+EXTRA_CFLAGS += -Idrivers/misc/inv_mpu
diff --git a/drivers/misc/inv_mpu/pressure/bma085.c b/drivers/misc/inv_mpu/pressure/bma085.c
new file mode 100644
index 0000000..397adbb
--- /dev/null
+++ b/drivers/misc/inv_mpu/pressure/bma085.c
@@ -0,0 +1,370 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+/**
+ * @defgroup ACCELDL (Motion Library - Pressure Driver Layer)
+ * @brief Provides the interface to setup and handle a pressure
+ * connected to the secondary I2C interface of the gyroscope.
+ *
+ * @{
+ * @file bma085.c
+ * @brief Pressure setup and handling methods.
+ */
+
+/* ------------------ */
+/* - Include Files. - */
+/* ------------------ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include "mpu-dev.h"
+
+#include <linux/mpu.h>
+#include "mlsl.h"
+#include "log.h"
+
+/* --------------------- */
+/* - Variables. - */
+/* --------------------- */
+
+/** this structure holds all device specific calibration parameters
+*/
+struct bmp085_calibration_param_t {
+ short ac1;
+ short ac2;
+ short ac3;
+ unsigned short ac4;
+ unsigned short ac5;
+ unsigned short ac6;
+ short b1;
+ short b2;
+ short mb;
+ short mc;
+ short md;
+ long param_b5;
+};
+
+struct bmp085_calibration_param_t cal_param;
+
+#define PRESSURE_BMA085_PARAM_MG 3038 /* calibration parameter */
+#define PRESSURE_BMA085_PARAM_MH -7357 /* calibration parameter */
+#define PRESSURE_BMA085_PARAM_MI 3791 /* calibration parameter */
+
+/*********************************************
+ Pressure Initialization Functions
+**********************************************/
+
+static int bma085_suspend(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result = INV_SUCCESS;
+ return result;
+}
+
+#define PRESSURE_BMA085_PROM_START_ADDR (0xAA)
+#define PRESSURE_BMA085_PROM_DATA_LEN (22)
+#define PRESSURE_BMP085_CTRL_MEAS_REG (0xF4)
+/* temperature measurent */
+#define PRESSURE_BMP085_T_MEAS (0x2E)
+/* pressure measurement; oversampling_setting */
+#define PRESSURE_BMP085_P_MEAS_OSS_0 (0x34)
+#define PRESSURE_BMP085_P_MEAS_OSS_1 (0x74)
+#define PRESSURE_BMP085_P_MEAS_OSS_2 (0xB4)
+#define PRESSURE_BMP085_P_MEAS_OSS_3 (0xF4)
+#define PRESSURE_BMP085_ADC_OUT_MSB_REG (0xF6)
+#define PRESSURE_BMP085_ADC_OUT_LSB_REG (0xF7)
+
+static int bma085_resume(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata)
+{
+ int result;
+ unsigned char data[PRESSURE_BMA085_PROM_DATA_LEN];
+
+ result =
+ inv_serial_read(mlsl_handle, pdata->address,
+ PRESSURE_BMA085_PROM_START_ADDR,
+ PRESSURE_BMA085_PROM_DATA_LEN, data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ /* parameters AC1-AC6 */
+ cal_param.ac1 = (data[0] << 8) | data[1];
+ cal_param.ac2 = (data[2] << 8) | data[3];
+ cal_param.ac3 = (data[4] << 8) | data[5];
+ cal_param.ac4 = (data[6] << 8) | data[7];
+ cal_param.ac5 = (data[8] << 8) | data[9];
+ cal_param.ac6 = (data[10] << 8) | data[11];
+
+ /* parameters B1,B2 */
+ cal_param.b1 = (data[12] << 8) | data[13];
+ cal_param.b2 = (data[14] << 8) | data[15];
+
+ /* parameters MB,MC,MD */
+ cal_param.mb = (data[16] << 8) | data[17];
+ cal_param.mc = (data[18] << 8) | data[19];
+ cal_param.md = (data[20] << 8) | data[21];
+
+ return result;
+}
+
+static int bma085_read(void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data)
+{
+ int result;
+ long pressure, x1, x2, x3, b3, b6;
+ unsigned long b4, b7;
+ unsigned long up;
+ unsigned short ut;
+ short oversampling_setting = 0;
+ short temperature;
+ long divisor;
+
+ /* get temprature */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ PRESSURE_BMP085_CTRL_MEAS_REG,
+ PRESSURE_BMP085_T_MEAS);
+ msleep(5);
+ result =
+ inv_serial_read(mlsl_handle, pdata->address,
+ PRESSURE_BMP085_ADC_OUT_MSB_REG, 2,
+ (unsigned char *)data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ ut = (data[0] << 8) | data[1];
+
+ x1 = (((long) ut - (long)cal_param.ac6) * (long)cal_param.ac5) >> 15;
+ divisor = x1 + cal_param.md;
+ if (!divisor)
+ return INV_ERROR_DIVIDE_BY_ZERO;
+
+ x2 = ((long)cal_param.mc << 11) / (x1 + cal_param.md);
+ cal_param.param_b5 = x1 + x2;
+ /* temperature in 0.1 degree C */
+ temperature = (short)((cal_param.param_b5 + 8) >> 4);
+
+ /* get pressure */
+ result = inv_serial_single_write(mlsl_handle, pdata->address,
+ PRESSURE_BMP085_CTRL_MEAS_REG,
+ PRESSURE_BMP085_P_MEAS_OSS_0);
+ msleep(5);
+ result =
+ inv_serial_read(mlsl_handle, pdata->address,
+ PRESSURE_BMP085_ADC_OUT_MSB_REG, 2,
+ (unsigned char *)data);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+ up = (((unsigned long) data[0] << 8) | ((unsigned long) data[1]));
+
+ b6 = cal_param.param_b5 - 4000;
+ /* calculate B3 */
+ x1 = (b6*b6) >> 12;
+ x1 *= cal_param.b2;
+ x1 >>= 11;
+
+ x2 = (cal_param.ac2*b6);
+ x2 >>= 11;
+
+ x3 = x1 + x2;
+
+ b3 = (((((long)cal_param.ac1) * 4 + x3)
+ << oversampling_setting) + 2) >> 2;
+
+ /* calculate B4 */
+ x1 = (cal_param.ac3 * b6) >> 13;
+ x2 = (cal_param.b1 * ((b6*b6) >> 12)) >> 16;
+ x3 = ((x1 + x2) + 2) >> 2;
+ b4 = (cal_param.ac4 * (unsigned long) (x3 + 32768)) >> 15;
+ if (!b4)
+ return INV_ERROR;
+
+ b7 = ((unsigned long)(up - b3) * (50000>>oversampling_setting));
+ if (b7 < 0x80000000)
+ pressure = (b7 << 1) / b4;
+ else
+ pressure = (b7 / b4) << 1;
+
+ x1 = pressure >> 8;
+ x1 *= x1;
+ x1 = (x1 * PRESSURE_BMA085_PARAM_MG) >> 16;
+ x2 = (pressure * PRESSURE_BMA085_PARAM_MH) >> 16;
+ /* pressure in Pa */
+ pressure += (x1 + x2 + PRESSURE_BMA085_PARAM_MI) >> 4;
+
+ data[0] = (unsigned char)(pressure >> 16);
+ data[1] = (unsigned char)(pressure >> 8);
+ data[2] = (unsigned char)(pressure & 0xFF);
+
+ return result;
+}
+
+static struct ext_slave_descr bma085_descr = {
+ .init = NULL,
+ .exit = NULL,
+ .suspend = bma085_suspend,
+ .resume = bma085_resume,
+ .read = bma085_read,
+ .config = NULL,
+ .get_config = NULL,
+ .name = "bma085",
+ .type = EXT_SLAVE_TYPE_PRESSURE,
+ .id = PRESSURE_ID_BMA085,
+ .read_reg = 0xF6,
+ .read_len = 3,
+ .endian = EXT_SLAVE_BIG_ENDIAN,
+ .range = {0, 0},
+};
+
+static
+struct ext_slave_descr *bma085_get_slave_descr(void)
+{
+ return &bma085_descr;
+}
+
+/* Platform data for the MPU */
+struct bma085_mod_private_data {
+ struct i2c_client *client;
+ struct ext_slave_platform_data *pdata;
+};
+
+static unsigned short normal_i2c[] = { I2C_CLIENT_END };
+
+static int bma085_mod_probe(struct i2c_client *client,
+ const struct i2c_device_id *devid)
+{
+ struct ext_slave_platform_data *pdata;
+ struct bma085_mod_private_data *private_data;
+ int result = 0;
+
+ dev_info(&client->adapter->dev, "%s: %s\n", __func__, devid->name);
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ result = -ENODEV;
+ goto out_no_free;
+ }
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->adapter->dev,
+ "Missing platform data for slave %s\n", devid->name);
+ result = -EFAULT;
+ goto out_no_free;
+ }
+
+ private_data = kzalloc(sizeof(*private_data), GFP_KERNEL);
+ if (!private_data) {
+ result = -ENOMEM;
+ goto out_no_free;
+ }
+
+ i2c_set_clientdata(client, private_data);
+ private_data->client = client;
+ private_data->pdata = pdata;
+
+ result = inv_mpu_register_slave(THIS_MODULE, client, pdata,
+ bma085_get_slave_descr);
+ if (result) {
+ dev_err(&client->adapter->dev,
+ "Slave registration failed: %s, %d\n",
+ devid->name, result);
+ goto out_free_memory;
+ }
+
+ return result;
+
+out_free_memory:
+ kfree(private_data);
+out_no_free:
+ dev_err(&client->adapter->dev, "%s failed %d\n", __func__, result);
+ return result;
+
+}
+
+static int bma085_mod_remove(struct i2c_client *client)
+{
+ struct bma085_mod_private_data *private_data =
+ i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+
+ inv_mpu_unregister_slave(client, private_data->pdata,
+ bma085_get_slave_descr);
+
+ kfree(private_data);
+ return 0;
+}
+
+static const struct i2c_device_id bma085_mod_id[] = {
+ { "bma085", PRESSURE_ID_BMA085 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, bma085_mod_id);
+
+static struct i2c_driver bma085_mod_driver = {
+ .class = I2C_CLASS_HWMON,
+ .probe = bma085_mod_probe,
+ .remove = bma085_mod_remove,
+ .id_table = bma085_mod_id,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "bma085_mod",
+ },
+ .address_list = normal_i2c,
+};
+
+static int __init bma085_mod_init(void)
+{
+ int res = i2c_add_driver(&bma085_mod_driver);
+ pr_info("%s: Probe name %s\n", __func__, "bma085_mod");
+ if (res)
+ pr_err("%s failed\n", __func__);
+ return res;
+}
+
+static void __exit bma085_mod_exit(void)
+{
+ pr_info("%s\n", __func__);
+ i2c_del_driver(&bma085_mod_driver);
+}
+
+module_init(bma085_mod_init);
+module_exit(bma085_mod_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Driver to integrate BMA085 sensor with the MPU");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("bma085_mod");
+/**
+ * @}
+**/
diff --git a/drivers/misc/inv_mpu/slaveirq.c b/drivers/misc/inv_mpu/slaveirq.c
new file mode 100644
index 0000000..efbaeb8
--- /dev/null
+++ b/drivers/misc/inv_mpu/slaveirq.c
@@ -0,0 +1,262 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/irq.h>
+#include <linux/signal.h>
+#include <linux/miscdevice.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/poll.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/slab.h>
+
+#include <linux/mpu.h>
+#include "slaveirq.h"
+#include "mldl_cfg.h"
+
+/* function which gets slave data and sends it to SLAVE */
+
+struct slaveirq_dev_data {
+ struct miscdevice dev;
+ struct i2c_client *slave_client;
+ struct mpuirq_data data;
+ wait_queue_head_t slaveirq_wait;
+ int irq;
+ int pid;
+ int data_ready;
+ int timeout;
+};
+
+/* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28
+ * drivers: misc: pass miscdevice pointer via file private data
+ */
+static int slaveirq_open(struct inode *inode, struct file *file)
+{
+ /* Device node is availabe in the file->private_data, this is
+ * exactly what we want so we leave it there */
+ struct slaveirq_dev_data *data =
+ container_of(file->private_data, struct slaveirq_dev_data, dev);
+
+ dev_dbg(data->dev.this_device,
+ "%s current->pid %d\n", __func__, current->pid);
+ data->pid = current->pid;
+ return 0;
+}
+
+static int slaveirq_release(struct inode *inode, struct file *file)
+{
+ struct slaveirq_dev_data *data =
+ container_of(file->private_data, struct slaveirq_dev_data, dev);
+ dev_dbg(data->dev.this_device, "slaveirq_release\n");
+ return 0;
+}
+
+/* read function called when from /dev/slaveirq is read */
+static ssize_t slaveirq_read(struct file *file,
+ char *buf, size_t count, loff_t *ppos)
+{
+ int len, err;
+ struct slaveirq_dev_data *data =
+ container_of(file->private_data, struct slaveirq_dev_data, dev);
+
+ if (!data->data_ready && data->timeout &&
+ !(file->f_flags & O_NONBLOCK)) {
+ wait_event_interruptible_timeout(data->slaveirq_wait,
+ data->data_ready,
+ data->timeout);
+ }
+
+ if (data->data_ready && NULL != buf && count >= sizeof(data->data)) {
+ err = copy_to_user(buf, &data->data, sizeof(data->data));
+ data->data.data_type = 0;
+ } else {
+ return 0;
+ }
+ if (err != 0) {
+ dev_err(data->dev.this_device,
+ "Copy to user returned %d\n", err);
+ return -EFAULT;
+ }
+ data->data_ready = 0;
+ len = sizeof(data->data);
+ return len;
+}
+
+static unsigned int slaveirq_poll(struct file *file,
+ struct poll_table_struct *poll)
+{
+ int mask = 0;
+ struct slaveirq_dev_data *data =
+ container_of(file->private_data, struct slaveirq_dev_data, dev);
+
+ poll_wait(file, &data->slaveirq_wait, poll);
+ if (data->data_ready)
+ mask |= POLLIN | POLLRDNORM;
+ return mask;
+}
+
+/* ioctl - I/O control */
+static long slaveirq_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ int tmp;
+ struct slaveirq_dev_data *data =
+ container_of(file->private_data, struct slaveirq_dev_data, dev);
+
+ switch (cmd) {
+ case SLAVEIRQ_SET_TIMEOUT:
+ data->timeout = arg;
+ break;
+
+ case SLAVEIRQ_GET_INTERRUPT_CNT:
+ tmp = data->data.interruptcount - 1;
+ if (data->data.interruptcount > 1)
+ data->data.interruptcount = 1;
+
+ if (copy_to_user((int *)arg, &tmp, sizeof(int)))
+ return -EFAULT;
+ break;
+ case SLAVEIRQ_GET_IRQ_TIME:
+ if (copy_to_user((int *)arg, &data->data.irqtime,
+ sizeof(data->data.irqtime)))
+ return -EFAULT;
+ data->data.irqtime = 0;
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+static irqreturn_t slaveirq_handler(int irq, void *dev_id)
+{
+ struct slaveirq_dev_data *data = (struct slaveirq_dev_data *)dev_id;
+ static int mycount;
+ mycount++;
+
+ data->data.interruptcount++;
+
+ /* wake up (unblock) for reading data from userspace */
+ data->data_ready = 1;
+
+ data->data.irqtime = ktime_to_ns(ktime_get());
+ data->data.data_type |= 1;
+
+ wake_up_interruptible(&data->slaveirq_wait);
+
+ return IRQ_HANDLED;
+
+}
+
+/* define which file operations are supported */
+static const struct file_operations slaveirq_fops = {
+ .owner = THIS_MODULE,
+ .read = slaveirq_read,
+ .poll = slaveirq_poll,
+
+#if HAVE_COMPAT_IOCTL
+ .compat_ioctl = slaveirq_ioctl,
+#endif
+#if HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = slaveirq_ioctl,
+#endif
+ .open = slaveirq_open,
+ .release = slaveirq_release,
+};
+
+int slaveirq_init(struct i2c_adapter *slave_adapter,
+ struct ext_slave_platform_data *pdata, char *name)
+{
+
+ int res;
+ struct slaveirq_dev_data *data;
+
+ if (!pdata->irq)
+ return -EINVAL;
+
+ pdata->irq_data = kzalloc(sizeof(*data), GFP_KERNEL);
+ data = (struct slaveirq_dev_data *)pdata->irq_data;
+ if (!data)
+ return -ENOMEM;
+
+ data->dev.minor = MISC_DYNAMIC_MINOR;
+ data->dev.name = name;
+ data->dev.fops = &slaveirq_fops;
+ data->irq = pdata->irq;
+ data->pid = 0;
+ data->data_ready = 0;
+ data->timeout = 0;
+
+ init_waitqueue_head(&data->slaveirq_wait);
+
+ res = request_irq(data->irq, slaveirq_handler, IRQF_TRIGGER_RISING,
+ data->dev.name, data);
+
+ if (res) {
+ dev_err(&slave_adapter->dev,
+ "myirqtest: cannot register IRQ %d\n", data->irq);
+ goto out_request_irq;
+ }
+
+ res = misc_register(&data->dev);
+ if (res < 0) {
+ dev_err(&slave_adapter->dev,
+ "misc_register returned %d\n", res);
+ goto out_misc_register;
+ }
+
+ return res;
+
+ out_misc_register:
+ free_irq(data->irq, data);
+ out_request_irq:
+ kfree(pdata->irq_data);
+ pdata->irq_data = NULL;
+
+ return res;
+}
+
+void slaveirq_exit(struct ext_slave_platform_data *pdata)
+{
+ struct slaveirq_dev_data *data = pdata->irq_data;
+
+ if (!pdata->irq_data || data->irq <= 0)
+ return;
+
+ dev_info(data->dev.this_device, "Unregistering %s\n", data->dev.name);
+
+ free_irq(data->irq, data);
+ misc_deregister(&data->dev);
+ kfree(pdata->irq_data);
+ pdata->irq_data = NULL;
+}
diff --git a/drivers/misc/inv_mpu/slaveirq.h b/drivers/misc/inv_mpu/slaveirq.h
new file mode 100644
index 0000000..ee8ad85
--- /dev/null
+++ b/drivers/misc/inv_mpu/slaveirq.h
@@ -0,0 +1,38 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+#ifndef __SLAVEIRQ__
+#define __SLAVEIRQ__
+
+#include <linux/i2c-dev.h>
+
+#include <linux/mpu.h>
+#include "mpuirq.h"
+
+#define SLAVEIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x50, unsigned long)
+#define SLAVEIRQ_GET_INTERRUPT_CNT _IOR(MPU_IOCTL, 0x51, unsigned long)
+#define SLAVEIRQ_GET_IRQ_TIME _IOR(MPU_IOCTL, 0x52, unsigned long)
+
+
+void slaveirq_exit(struct ext_slave_platform_data *pdata);
+int slaveirq_init(struct i2c_adapter *slave_adapter,
+ struct ext_slave_platform_data *pdata, char *name);
+
+
+#endif
diff --git a/drivers/misc/inv_mpu/timerirq.c b/drivers/misc/inv_mpu/timerirq.c
new file mode 100644
index 0000000..495a808
--- /dev/null
+++ b/drivers/misc/inv_mpu/timerirq.c
@@ -0,0 +1,293 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/signal.h>
+#include <linux/miscdevice.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <linux/poll.h>
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/timer.h>
+#include <linux/slab.h>
+
+#include <linux/mpu.h>
+#include "mltypes.h"
+#include "timerirq.h"
+
+/* function which gets timer data and sends it to TIMER */
+struct timerirq_data {
+ int pid;
+ int data_ready;
+ int run;
+ int timeout;
+ unsigned long period;
+ struct mpuirq_data data;
+ struct completion timer_done;
+ wait_queue_head_t timerirq_wait;
+ struct timer_list timer;
+ struct miscdevice *dev;
+};
+
+static struct miscdevice *timerirq_dev_data;
+
+static void timerirq_handler(unsigned long arg)
+{
+ struct timerirq_data *data = (struct timerirq_data *)arg;
+
+ data->data.interruptcount++;
+
+ data->data_ready = 1;
+
+ data->data.irqtime = ktime_to_ns(ktime_get());
+ data->data.data_type |= 1;
+
+ dev_dbg(data->dev->this_device,
+ "%s, %lld, %ld\n", __func__, data->data.irqtime,
+ (unsigned long)data);
+
+ wake_up_interruptible(&data->timerirq_wait);
+
+ if (data->run)
+ mod_timer(&data->timer,
+ jiffies + msecs_to_jiffies(data->period));
+ else
+ complete(&data->timer_done);
+}
+
+static int start_timerirq(struct timerirq_data *data)
+{
+ dev_dbg(data->dev->this_device,
+ "%s current->pid %d\n", __func__, current->pid);
+
+ /* Timer already running... success */
+ if (data->run)
+ return 0;
+
+ /* Don't allow a period of 0 since this would fire constantly */
+ if (!data->period)
+ return -EINVAL;
+
+ data->run = TRUE;
+ data->data_ready = FALSE;
+
+ init_completion(&data->timer_done);
+ setup_timer(&data->timer, timerirq_handler, (unsigned long)data);
+
+ return mod_timer(&data->timer,
+ jiffies + msecs_to_jiffies(data->period));
+}
+
+static int stop_timerirq(struct timerirq_data *data)
+{
+ dev_dbg(data->dev->this_device,
+ "%s current->pid %lx\n", __func__, (unsigned long)data);
+
+ if (data->run) {
+ data->run = FALSE;
+ mod_timer(&data->timer, jiffies + 1);
+ wait_for_completion(&data->timer_done);
+ }
+ return 0;
+}
+
+/* The following depends on patch fa1f68db6ca7ebb6fc4487ac215bffba06c01c28
+ * drivers: misc: pass miscdevice pointer via file private data
+ */
+static int timerirq_open(struct inode *inode, struct file *file)
+{
+ /* Device node is availabe in the file->private_data, this is
+ * exactly what we want so we leave it there */
+ struct miscdevice *dev_data = file->private_data;
+ struct timerirq_data *data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = dev_data;
+ file->private_data = data;
+ data->pid = current->pid;
+ init_waitqueue_head(&data->timerirq_wait);
+
+ dev_dbg(data->dev->this_device,
+ "%s current->pid %d\n", __func__, current->pid);
+ return 0;
+}
+
+static int timerirq_release(struct inode *inode, struct file *file)
+{
+ struct timerirq_data *data = file->private_data;
+ dev_dbg(data->dev->this_device, "timerirq_release\n");
+ if (data->run)
+ stop_timerirq(data);
+ kfree(data);
+ return 0;
+}
+
+/* read function called when from /dev/timerirq is read */
+static ssize_t timerirq_read(struct file *file,
+ char *buf, size_t count, loff_t *ppos)
+{
+ int len, err;
+ struct timerirq_data *data = file->private_data;
+
+ if (!data->data_ready && data->timeout &&
+ !(file->f_flags & O_NONBLOCK)) {
+ wait_event_interruptible_timeout(data->timerirq_wait,
+ data->data_ready,
+ data->timeout);
+ }
+
+ if (data->data_ready && NULL != buf && count >= sizeof(data->data)) {
+ err = copy_to_user(buf, &data->data, sizeof(data->data));
+ data->data.data_type = 0;
+ } else {
+ return 0;
+ }
+ if (err != 0) {
+ dev_err(data->dev->this_device,
+ "Copy to user returned %d\n", err);
+ return -EFAULT;
+ }
+ data->data_ready = 0;
+ len = sizeof(data->data);
+ return len;
+}
+
+static unsigned int timerirq_poll(struct file *file,
+ struct poll_table_struct *poll)
+{
+ int mask = 0;
+ struct timerirq_data *data = file->private_data;
+
+ poll_wait(file, &data->timerirq_wait, poll);
+ if (data->data_ready)
+ mask |= POLLIN | POLLRDNORM;
+ return mask;
+}
+
+/* ioctl - I/O control */
+static long timerirq_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int retval = 0;
+ int tmp;
+ struct timerirq_data *data = file->private_data;
+
+ dev_dbg(data->dev->this_device,
+ "%s current->pid %d, %d, %ld\n",
+ __func__, current->pid, cmd, arg);
+
+ if (!data)
+ return -EFAULT;
+
+ switch (cmd) {
+ case TIMERIRQ_SET_TIMEOUT:
+ data->timeout = arg;
+ break;
+ case TIMERIRQ_GET_INTERRUPT_CNT:
+ tmp = data->data.interruptcount - 1;
+ if (data->data.interruptcount > 1)
+ data->data.interruptcount = 1;
+
+ if (copy_to_user((int *)arg, &tmp, sizeof(int)))
+ return -EFAULT;
+ break;
+ case TIMERIRQ_START:
+ data->period = arg;
+ retval = start_timerirq(data);
+ break;
+ case TIMERIRQ_STOP:
+ retval = stop_timerirq(data);
+ break;
+ default:
+ retval = -EINVAL;
+ }
+ return retval;
+}
+
+/* define which file operations are supported */
+static const struct file_operations timerirq_fops = {
+ .owner = THIS_MODULE,
+ .read = timerirq_read,
+ .poll = timerirq_poll,
+
+#if HAVE_COMPAT_IOCTL
+ .compat_ioctl = timerirq_ioctl,
+#endif
+#if HAVE_UNLOCKED_IOCTL
+ .unlocked_ioctl = timerirq_ioctl,
+#endif
+ .open = timerirq_open,
+ .release = timerirq_release,
+};
+
+static int __init timerirq_init(void)
+{
+
+ int res;
+ static struct miscdevice *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ timerirq_dev_data = data;
+ data->minor = MISC_DYNAMIC_MINOR;
+ data->name = "timerirq";
+ data->fops = &timerirq_fops;
+
+ res = misc_register(data);
+ if (res < 0) {
+ dev_err(data->this_device, "misc_register returned %d\n", res);
+ return res;
+ }
+
+ return res;
+}
+
+module_init(timerirq_init);
+
+static void __exit timerirq_exit(void)
+{
+ struct miscdevice *data = timerirq_dev_data;
+
+ dev_info(data->this_device, "Unregistering %s\n", data->name);
+
+ misc_deregister(data);
+ kfree(data);
+
+ timerirq_dev_data = NULL;
+}
+
+module_exit(timerirq_exit);
+
+MODULE_AUTHOR("Invensense Corporation");
+MODULE_DESCRIPTION("Timer IRQ device driver.");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("timerirq");
diff --git a/drivers/misc/inv_mpu/timerirq.h b/drivers/misc/inv_mpu/timerirq.h
new file mode 100644
index 0000000..4455cac
--- /dev/null
+++ b/drivers/misc/inv_mpu/timerirq.h
@@ -0,0 +1,30 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+#ifndef __TIMERIRQ__
+#define __TIMERIRQ__
+
+#include <linux/mpu.h>
+
+#define TIMERIRQ_SET_TIMEOUT _IOW(MPU_IOCTL, 0x60, unsigned long)
+#define TIMERIRQ_GET_INTERRUPT_CNT _IOW(MPU_IOCTL, 0x61, unsigned long)
+#define TIMERIRQ_START _IOW(MPU_IOCTL, 0x62, unsigned long)
+#define TIMERIRQ_STOP _IO(MPU_IOCTL, 0x63)
+
+#endif
diff --git a/drivers/misc/leds-an30259a.c b/drivers/misc/leds-an30259a.c
new file mode 100644
index 0000000..4f113b6
--- /dev/null
+++ b/drivers/misc/leds-an30259a.c
@@ -0,0 +1,495 @@
+/*
+ * leds_an30259a.c - driver for panasonic AN30259A led control chip
+ *
+ * Copyright (C) 2011, Samsung Electronics Co. Ltd. All Rights Reserved.
+ *
+ * Contact: Yufi Li <tai-yun.li@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/leds-an30259a.h>
+
+/* AN30259A register map */
+#define AN30259A_REG_SRESET 0x00
+#define AN30259A_REG_LEDON 0x01
+#define AN30259A_REG_SEL 0x02
+
+#define AN30259A_REG_LED1CC 0x03
+#define AN30259A_REG_LED2CC 0x04
+#define AN30259A_REG_LED3CC 0x05
+
+#define AN30259A_REG_LED1SLP 0x06
+#define AN30259A_REG_LED2SLP 0x07
+#define AN30259A_REG_LED3SLP 0x08
+
+#define AN30259A_REG_LED1CNT1 0x09
+#define AN30259A_REG_LED1CNT2 0x0a
+#define AN30259A_REG_LED1CNT3 0x0b
+#define AN30259A_REG_LED1CNT4 0x0c
+
+#define AN30259A_REG_LED2CNT1 0x0d
+#define AN30259A_REG_LED2CNT2 0x0e
+#define AN30259A_REG_LED2CNT3 0x0f
+#define AN30259A_REG_LED2CNT4 0x10
+
+#define AN30259A_REG_LED3CNT1 0x11
+#define AN30259A_REG_LED3CNT2 0x12
+#define AN30259A_REG_LED3CNT3 0x13
+#define AN30259A_REG_LED3CNT4 0x14
+#define AN30259A_REG_MAX 0x15
+/* MASK */
+#define AN30259A_MASK_IMAX 0xc0
+#define AN30259A_MASK_DELAY 0xf0
+#define AN30259A_SRESET 0x01
+#define LED_SLOPE_MODE 0x10
+#define LED_ON 0x01
+
+#define DUTYMAX_MAX_VALUE 0x7f
+#define DUTYMIN_MIN_VALUE 0x00
+#define SLPTT_MAX_VALUE 0x0f
+
+#define DETENTION_MAX_VALUE 60
+#define DELAY_MAX_VALUE 7500
+#define AN30259A_TIME_UNIT 500
+#define AN30259A_DT_TIME_UNIT 4
+
+#define LED_R_MASK 0x00ff0000
+#define LED_G_MASK 0x0000ff00
+#define LED_B_MASK 0x000000ff
+#define LED_R_SHIFT 16
+#define LED_G_SHIFT 8
+#define LED_IMAX_SHIFT 6
+#define AN30259A_CTN_RW_FLG 0x80
+
+enum an30259a_led {
+ LED_R,
+ LED_G,
+ LED_B,
+};
+
+struct an30259a_data {
+ struct i2c_client *client;
+ struct miscdevice dev;
+ struct mutex mutex;
+ u8 shadow_reg[AN30259A_REG_MAX];
+};
+
+static int leds_i2c_write_all(struct i2c_client *client)
+{
+ struct an30259a_data *data = i2c_get_clientdata(client);
+ int ret;
+
+ /*we need to set all the configs setting first, then LEDON later*/
+ ret = i2c_smbus_write_i2c_block_data(client,
+ AN30259A_REG_SEL | AN30259A_CTN_RW_FLG,
+ AN30259A_REG_MAX - AN30259A_REG_SEL,
+ &data->shadow_reg[AN30259A_REG_SEL]);
+ if (ret < 0) {
+ dev_err(&client->adapter->dev,
+ "%s: failure on i2c block write\n",
+ __func__);
+ return ret;
+ }
+ ret = i2c_smbus_write_byte_data(client, AN30259A_REG_LEDON,
+ data->shadow_reg[AN30259A_REG_LEDON]);
+
+ if (ret < 0) {
+ dev_err(&client->adapter->dev,
+ "%s: failure on i2c byte write\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * leds_set_slope_mode() sets correct values to corresponding shadow registers.
+ * led: stands for LED_R or LED_G or LED_B.
+ * delay: represents for starting delay time in multiple of .5 second.
+ * dutymax: led at slope lighting maximum PWM Duty setting.
+ * dutymid: led at slope lighting middle PWM Duty setting.
+ * dutymin: led at slope lighting minimum PWM Duty Setting.
+ * slptt1: total time of slope operation 1 and 2, in multiple of .5 second.
+ * slptt2: total time of slope operation 3 and 4, in multiple of .5 second.
+ * dt1: detention time at each step in slope operation 1, in multiple of 4ms.
+ * dt2: detention time at each step in slope operation 2, in multiple of 4ms.
+ * dt3: detention time at each step in slope operation 3, in multiple of 4ms.
+ * dt4: detention time at each step in slope operation 4, in multiple of 4ms.
+ */
+static void leds_set_slope_mode(struct i2c_client *client,
+ enum an30259a_led led, u8 delay,
+ u8 dutymax, u8 dutymid, u8 dutymin,
+ u8 slptt1, u8 slptt2,
+ u8 dt1, u8 dt2, u8 dt3, u8 dt4)
+{
+ struct an30259a_data *data = i2c_get_clientdata(client);
+
+ data->shadow_reg[AN30259A_REG_LED1CNT1 + led * 4] =
+ dutymax << 4 | dutymid;
+ data->shadow_reg[AN30259A_REG_LED1CNT2 + led * 4] =
+ delay << 4 | dutymin;
+ data->shadow_reg[AN30259A_REG_LED1CNT3 + led * 4] = dt2 << 4 | dt1;
+ data->shadow_reg[AN30259A_REG_LED1CNT4 + led * 4] = dt4 << 4 | dt3;
+ data->shadow_reg[AN30259A_REG_LED1SLP + led] = slptt2 << 4 | slptt1;
+}
+
+static void leds_on(struct i2c_client *client, enum an30259a_led led,
+ bool on, bool slopemode,
+ u8 ledcc)
+{
+ struct an30259a_data *data = i2c_get_clientdata(client);
+
+ if (on)
+ data->shadow_reg[AN30259A_REG_LEDON] |= LED_ON << led;
+ else {
+ data->shadow_reg[AN30259A_REG_LEDON] &= ~(LED_ON << led);
+ data->shadow_reg[AN30259A_REG_LED1CNT2 + led * 4] &=
+ ~AN30259A_MASK_DELAY;
+ }
+ if (slopemode)
+ data->shadow_reg[AN30259A_REG_LEDON] |= LED_SLOPE_MODE << led;
+ else
+ data->shadow_reg[AN30259A_REG_LEDON] &=
+ ~(LED_SLOPE_MODE << led);
+
+ data->shadow_reg[AN30259A_REG_LED1CC + led] = ledcc;
+}
+
+/* calculate the detention time for each step, return ms */
+static u8 calculate_dt(u8 min, u8 max, u16 time)
+{
+ u16 step_time;
+ u16 detention_time;
+
+ if (min >= max)
+ return 0;
+
+ step_time = time / (u16)(max - min);
+ detention_time = (step_time + 0x03) & 0xfffc;
+ /* the detention time at each step can be set as 4ms, 8ms, ...60ms */
+ detention_time = (detention_time > DETENTION_MAX_VALUE) ?
+ DETENTION_MAX_VALUE : detention_time;
+ return detention_time;
+}
+
+/* calculate the constant current output */
+static u8 calculate_cc(u32 brightness, enum an30259a_led led)
+{
+ u8 value = 0;
+ switch (led) {
+ case LED_R:
+ value = (brightness & LED_R_MASK) >> LED_R_SHIFT;
+ break;
+ case LED_G:
+ value = (brightness & LED_G_MASK) >> LED_G_SHIFT;
+ break;
+ case LED_B:
+ value = brightness & LED_B_MASK;
+ break;
+ default:
+ break;
+ }
+ return value;
+}
+
+static u8 calculate_slope_tt(u8 mid, u8 dt1, u8 dt2, u16 time)
+{
+ u8 slptt;
+ u16 tt = (dt1 * (mid - DUTYMIN_MIN_VALUE) +
+ dt2 * (DUTYMAX_MAX_VALUE - mid)) * AN30259A_DT_TIME_UNIT + time;
+ /* round up to the nearest .5 seconds */
+ slptt = (tt + AN30259A_TIME_UNIT - 1) / AN30259A_TIME_UNIT;
+ slptt = slptt > SLPTT_MAX_VALUE ? SLPTT_MAX_VALUE : slptt;
+ return slptt;
+}
+
+static int leds_handle_cmds(struct i2c_client *client,
+ enum an30259a_led color,
+ struct an30259a_pr_control *leds)
+{
+ u8 cc, delay, dutymid, dt1, dt2, dt3, dt4, tt1, tt2;
+
+ cc = calculate_cc(leds->color, color);
+ switch (leds->state) {
+ case LED_LIGHT_OFF:
+ leds_on(client, color, false, false, 0);
+ break;
+ case LED_LIGHT_ON:
+ leds_on(client, color, true, false, cc);
+ break;
+ case LED_LIGHT_PULSE:
+ /*
+ * PULSE is a special case of slope with delay=0,
+ * dutymid = dutymax ,dt1,dt2,dt3,dt4 are all zero
+ */
+ leds_on(client, color, true, true, cc);
+ leds_set_slope_mode(client, color, 0,
+ DUTYMAX_MAX_VALUE >> 3, DUTYMAX_MAX_VALUE >> 3,
+ DUTYMIN_MIN_VALUE >> 3,
+ (leds->time_on + AN30259A_TIME_UNIT - 1) /
+ AN30259A_TIME_UNIT,
+ (leds->time_off + AN30259A_TIME_UNIT - 1) /
+ AN30259A_TIME_UNIT,
+ 0, 0, 0, 0);
+
+ break;
+ case LED_LIGHT_SLOPE:
+ if (leds->mid_brightness > DUTYMAX_MAX_VALUE)
+ return -EINVAL;
+
+ delay = ((leds->start_delay > DELAY_MAX_VALUE) ?
+ DELAY_MAX_VALUE : leds->start_delay) /
+ AN30259A_TIME_UNIT;
+
+ dutymid = leds->mid_brightness >> 3;
+ dt1 = calculate_dt(DUTYMIN_MIN_VALUE, leds->mid_brightness,
+ leds->time_slope_up_1) / AN30259A_DT_TIME_UNIT;
+ dt2 = calculate_dt(leds->mid_brightness, DUTYMAX_MAX_VALUE,
+ leds->time_slope_up_2) / AN30259A_DT_TIME_UNIT;
+ dt3 = calculate_dt(leds->mid_brightness, DUTYMAX_MAX_VALUE,
+ leds->time_slope_down_1) /
+ AN30259A_DT_TIME_UNIT;
+ dt4 = calculate_dt(DUTYMIN_MIN_VALUE, leds->mid_brightness,
+ leds->time_slope_down_2) /
+ AN30259A_DT_TIME_UNIT;
+ tt1 = calculate_slope_tt(leds->mid_brightness,
+ dt1, dt2, leds->time_on);
+ tt2 = calculate_slope_tt(leds->mid_brightness,
+ dt4, dt3, leds->time_off);
+
+ leds_on(client, color, true, true, cc);
+ leds_set_slope_mode(client, color, delay,
+ DUTYMAX_MAX_VALUE >> 3, dutymid, DUTYMIN_MIN_VALUE >> 3,
+ tt1, tt2, dt1, dt2, dt3, dt4);
+ break;
+ }
+
+ return 0;
+}
+
+static int leds_set_imax(struct i2c_client *client, u8 imax)
+{
+ int ret;
+ struct an30259a_data *data = i2c_get_clientdata(client);
+
+ data->shadow_reg[AN30259A_REG_SEL] &= ~AN30259A_MASK_IMAX;
+ data->shadow_reg[AN30259A_REG_SEL] |= imax << LED_IMAX_SHIFT;
+
+ ret = i2c_smbus_write_byte_data(client, AN30259A_REG_SEL,
+ data->shadow_reg[AN30259A_REG_SEL]);
+ if (ret < 0) {
+ dev_err(&client->adapter->dev,
+ "%s: failure on i2c write\n",
+ __func__);
+ }
+ return 0;
+}
+
+static long an30250a_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ struct an30259a_data *leds_data = container_of(file->private_data,
+ struct an30259a_data, dev);
+ struct i2c_client *client = leds_data->client;
+ int retval, i;
+ u8 imax;
+ struct an30259a_pr_control leds[3];
+
+ mutex_lock(&leds_data->mutex);
+
+ switch (cmd) {
+ case AN30259A_PR_SET_LED:
+ retval = copy_from_user(leds, (unsigned char __user *)arg,
+ sizeof(struct an30259a_pr_control));
+ if (retval)
+ break;
+
+ for (i = LED_R; i <= LED_B; i++) {
+ retval = leds_handle_cmds(client, i, leds);
+ if (retval < 0)
+ goto an30259a_ioctl_failed;
+ }
+ retval = leds_i2c_write_all(client);
+ break;
+ case AN30259A_PR_SET_LEDS:
+ retval = copy_from_user(leds, (unsigned char __user *)arg,
+ 3 * sizeof(struct an30259a_pr_control));
+
+ if (retval)
+ break;
+
+ for (i = LED_R; i <= LED_B; i++) {
+ retval = leds_handle_cmds(client, i, &leds[i]);
+ if (retval < 0)
+ goto an30259a_ioctl_failed;
+ }
+ retval = leds_i2c_write_all(client);
+ break;
+
+ case AN30259A_PR_SET_IMAX:
+ retval = copy_from_user(&imax, (unsigned char __user *)arg,
+ sizeof(u8));
+ if (retval)
+ break;
+
+ retval = leds_set_imax(client, imax);
+ break;
+
+ default:
+ dev_err(&client->adapter->dev,
+ "%s: Unknown cmd %x, arg %lu\n",
+ __func__, cmd, arg);
+ retval = -EINVAL;
+ break;
+ }
+
+an30259a_ioctl_failed:
+ mutex_unlock(&leds_data->mutex);
+ return retval;
+}
+
+static int an30250a_open(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static const struct file_operations an30259a_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = an30250a_ioctl,
+ .open = an30250a_open,
+};
+
+static int __devinit an30259a_initialize(struct i2c_client *client)
+{
+ struct an30259a_data *data = i2c_get_clientdata(client);
+ int ret;
+
+ /* reset an30259a*/
+ ret = i2c_smbus_write_byte_data(client, AN30259A_REG_SRESET,
+ AN30259A_SRESET);
+ if (ret < 0) {
+ dev_err(&client->adapter->dev,
+ "%s: failure on i2c write (reg = 0x%2x)\n",
+ __func__, AN30259A_REG_SRESET);
+ return ret;
+ }
+ ret = i2c_smbus_read_i2c_block_data(client,
+ AN30259A_REG_SRESET | AN30259A_CTN_RW_FLG,
+ AN30259A_REG_MAX, data->shadow_reg);
+ if (ret < 0) {
+ dev_err(&client->adapter->dev,
+ "%s: failure on i2c read block(ledxcc)\n",
+ __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int __devinit an30259a_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct an30259a_data *data;
+ int ret;
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "need I2C_FUNC_I2C.\n");
+ return -ENODEV;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&client->adapter->dev,
+ "failed to allocate driver data.\n");
+ return -ENOMEM;
+ }
+
+ data->client = client;
+
+ i2c_set_clientdata(client, data);
+
+ mutex_init(&data->mutex);
+
+ /* initialize LED */
+ ret = an30259a_initialize(client);
+ if (ret < 0) {
+ dev_err(&client->adapter->dev, "failure on initialization\n");
+ goto exit;
+ }
+
+ data->dev.minor = MISC_DYNAMIC_MINOR;
+ data->dev.name = "an30259a_leds";
+ data->dev.fops = &an30259a_fops;
+ ret = misc_register(&data->dev);
+ if (ret < 0) {
+ dev_err(&client->adapter->dev,
+ "%s: ERROR: misc_register returned %d\n",
+ __func__, ret);
+ goto exit;
+ }
+
+ return 0;
+exit:
+ mutex_destroy(&data->mutex);
+ kfree(data);
+ return ret;
+}
+
+static int __devexit an30259a_remove(struct i2c_client *client)
+{
+ struct an30259a_data *data = i2c_get_clientdata(client);
+
+ dev_dbg(&client->adapter->dev, "%s\n", __func__);
+ misc_deregister(&data->dev);
+ mutex_destroy(&data->mutex);
+ kfree(data);
+ return 0;
+}
+
+static struct i2c_device_id an30259a_id[] = {
+ {"an30259a", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, an30259a_id);
+
+static struct i2c_driver an30259a_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "an30259a",
+ },
+ .id_table = an30259a_id,
+ .probe = an30259a_probe,
+ .remove = __devexit_p(an30259a_remove),
+};
+
+static int __init an30259a_init(void)
+{
+ return i2c_add_driver(&an30259a_i2c_driver);
+}
+
+static void __exit an30259a_exit(void)
+{
+ i2c_del_driver(&an30259a_i2c_driver);
+}
+
+module_init(an30259a_init);
+module_exit(an30259a_exit);
+
+MODULE_DESCRIPTION("AN30259A LED driver");
+MODULE_AUTHOR("Yufi Li <tai-yun.li@samsung.com");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/misc/modem_if/Kconfig b/drivers/misc/modem_if/Kconfig
new file mode 100755
index 0000000..223a275
--- /dev/null
+++ b/drivers/misc/modem_if/Kconfig
@@ -0,0 +1,40 @@
+menuconfig SEC_MODEM
+ bool "Modem Interface Driver"
+ default n
+ ---help---
+ Samsung Modem Interface Driver.
+
+config UMTS_LINK_MIPI
+ bool "modem driver link device MIPI-HSI"
+ depends on SEC_MODEM
+ default n
+
+config UMTS_LINK_HSIC
+ bool "modem driver link device HSIC"
+ depends on SEC_MODEM
+ default n
+
+config UMTS_MODEM_XMM6260
+ bool "modem chip : IMC xmm6260"
+ depends on SEC_MODEM
+ default n
+
+config CDMA_LINK_DPRAM
+ bool "modem driver link device DPRAM"
+ depends on SEC_MODEM
+ default n
+
+config CDMA_MODEM_CBP71
+ bool "modem chip : cbp71"
+ depends on SEC_MODEM
+ default n
+
+config LTE_LINK_USB
+ bool "modem driver link device US"
+ depends on SEC_MODEM
+ default n
+
+config LTE_MODEM_CMC221
+ bool "modem chip : cmc221"
+ depends on SEC_MODEM
+ default n
diff --git a/drivers/misc/modem_if/Makefile b/drivers/misc/modem_if/Makefile
new file mode 100755
index 0000000..e76c8b2
--- /dev/null
+++ b/drivers/misc/modem_if/Makefile
@@ -0,0 +1,8 @@
+obj-y += modem.o modem_io_device.o modem_net_flowcontrol_device.o
+obj-$(CONFIG_UMTS_MODEM_XMM6260) += modem_modemctl_device_xmm6260.o
+obj-$(CONFIG_UMTS_LINK_MIPI) += modem_link_device_mipi.o
+obj-$(CONFIG_UMTS_LINK_HSIC) += modem_link_device_hsic.o
+obj-$(CONFIG_CDMA_MODEM_CBP71) += modem_modemctl_device_cbp71.o
+obj-$(CONFIG_CDMA_LINK_DPRAM) += modem_link_device_dpram.o
+obj-$(CONFIG_LTE_MODEM_CMC221) += modem_modemctl_device_cmc221.o lte_modem_bootloader.o
+obj-$(CONFIG_LTE_LINK_USB) += modem_link_device_usb.o
diff --git a/drivers/misc/modem_if/lte_modem_bootloader.c b/drivers/misc/modem_if/lte_modem_bootloader.c
new file mode 100755
index 0000000..38591a5
--- /dev/null
+++ b/drivers/misc/modem_if/lte_modem_bootloader.c
@@ -0,0 +1,320 @@
+/* Lte modem bootloader support for Samsung Tuna Board.
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2011 Samsung Electronics.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/wakelock.h>
+#include <linux/delay.h>
+#include <linux/spi/spi.h>
+
+#include <linux/platform_data/lte_modem_bootloader.h>
+
+#define LEN_XMIT_DELEY 10
+#define MAX_XMIT_SIZE 16
+
+#ifdef AIRPLAIN_MODE_TEST
+int lte_airplain_mode;
+#endif
+
+enum xmit_bootloader_status {
+ XMIT_BOOT_READY,
+ XMIT_LOADER_READY,
+};
+
+struct lte_modem_bootloader {
+ struct spi_device *spi_dev;
+ struct miscdevice dev;
+
+ struct mutex lock;
+
+ unsigned int gpio_lte2ap_status;
+ enum xmit_bootloader_status xmit_status;
+};
+#define to_loader(misc) container_of(misc, struct lte_modem_bootloader, dev);
+
+static inline
+int spi_xmit(struct lte_modem_bootloader *loader,
+ const char *buf, int size_per_xmit)
+{
+ int i;
+ int ret;
+ unsigned char xmit_buf[MAX_XMIT_SIZE];
+ struct spi_message msg;
+ struct spi_transfer xfers[MAX_XMIT_SIZE];
+
+ memcpy(xmit_buf, buf, sizeof(xmit_buf));
+ spi_message_init(&msg);
+ memset(xfers, 0, sizeof(xfers));
+ for (i = 0; i < size_per_xmit ; i++) {
+ xfers[i].cs_change = 1;
+ xfers[i].len = 1;
+ xfers[i].tx_buf = xmit_buf + i;
+ spi_message_add_tail(&xfers[i], &msg);
+ }
+ ret = spi_sync(loader->spi_dev, &msg);
+
+ if (ret < 0)
+ dev_err(&loader->spi_dev->dev,
+ "%s - error %d\n", __func__, ret);
+
+ return ret;
+}
+
+
+static
+int bootloader_write(struct lte_modem_bootloader *loader,
+ const char *addr, const int len)
+{
+ int i;
+ int ret = 0;
+ unsigned char lenbuf[4];
+
+ if (loader->xmit_status == XMIT_LOADER_READY) {
+ memcpy(lenbuf, &len, ARRAY_SIZE(lenbuf));
+ ret = spi_xmit(loader, lenbuf,
+ ARRAY_SIZE(lenbuf));
+ if (ret < 0)
+ return ret;
+ msleep(LEN_XMIT_DELEY);
+ }
+
+ for (i = 0 ; i < len / MAX_XMIT_SIZE ; i++) {
+ ret = spi_xmit(loader,
+ addr + i * MAX_XMIT_SIZE,
+ MAX_XMIT_SIZE);
+ if (ret < 0)
+ return ret;
+ }
+ ret = spi_xmit(loader, addr + i * MAX_XMIT_SIZE , len % MAX_XMIT_SIZE);
+
+ return 0;
+}
+
+
+static
+int bootloader_open(struct inode *inode, struct file *flip)
+{
+ struct lte_modem_bootloader *loader = to_loader(flip->private_data);
+ flip->private_data = loader;
+
+ return 0;
+}
+
+static
+long bootloader_ioctl(struct file *flip,
+ unsigned int cmd, unsigned long arg)
+{
+ int ret = 0;
+ int status;
+ struct lte_modem_bootloader_param param;
+ struct lte_modem_bootloader *loader = flip->private_data;
+
+ mutex_lock(&loader->lock);
+ switch (cmd) {
+ case IOCTL_LTE_MODEM_XMIT_BOOT:
+
+ ret = copy_from_user(&param, (const void __user *)arg,
+ sizeof(param));
+ if (ret) {
+ dev_err(&loader->spi_dev->dev, "%s - can not copy userdata\n",
+ __func__);
+ ret = -EFAULT;
+ goto exit_err;
+ }
+
+ dev_info(&loader->spi_dev->dev,
+ "IOCTL_LTE_MODEM_XMIT_BOOT - bin size: %d\n",
+ param.len);
+
+ ret = bootloader_write(loader, param.buf, param.len);
+ if (ret < 0) {
+ dev_err(&loader->spi_dev->dev, "failed to xmit boot bin\n");
+ } else {
+ if (loader->xmit_status == XMIT_BOOT_READY)
+ loader->xmit_status = XMIT_LOADER_READY;
+ else
+ loader->xmit_status = XMIT_BOOT_READY;
+ }
+
+ break;
+ case IOCTL_LTE_MODEM_LTE2AP_STATUS:
+ status = gpio_get_value(loader->gpio_lte2ap_status);
+ pr_debug("LTE2AP status :%d\n", status);
+ ret = copy_to_user((unsigned int *)arg, &status,
+ sizeof(status));
+
+ break;
+#ifdef AIRPLAIN_MODE_TEST
+ case IOCTL_LTE_MODEM_AIRPLAIN_ON:
+ lte_airplain_mode = 1;
+ pr_info("usb %s, IOCTL_LTE_MODEM LPM_ON\n", __func__);
+ break;
+ case IOCTL_LTE_MODEM_AIRPLAIN_OFF:
+ pr_info("usb %s, IOCTL_LTE_MODEM LPM_OFF\n", __func__);
+ lte_airplain_mode = 0;
+ break;
+#endif
+ default:
+ dev_err(&loader->spi_dev->dev,
+ "%s - ioctl cmd error\n",
+ __func__);
+ ret = -ENOIOCTLCMD;
+
+ break;
+ }
+ mutex_unlock(&loader->lock);
+
+exit_err:
+ return ret;
+}
+
+static const struct file_operations lte_modem_bootloader_fops = {
+ .owner = THIS_MODULE,
+ .open = bootloader_open,
+ .unlocked_ioctl = bootloader_ioctl,
+};
+
+static
+int bootloader_gpio_setup(struct lte_modem_bootloader *loader)
+{
+ if (!loader->gpio_lte2ap_status)
+ return -EINVAL;
+
+ gpio_request(loader->gpio_lte2ap_status, "GPIO_LTE2AP_STATUS");
+ gpio_direction_input(loader->gpio_lte2ap_status);
+
+ return 0;
+}
+
+static
+int __devinit lte_modem_bootloader_probe(struct spi_device *spi)
+{
+ int ret;
+
+ struct lte_modem_bootloader *loader;
+ struct lte_modem_bootloader_platform_data *pdata;
+
+ loader = kzalloc(sizeof(*loader), GFP_KERNEL);
+ if (!loader) {
+ pr_err("failed to allocate for lte_modem_bootloader\n");
+ ret = -ENOMEM;
+ goto err_alloc;
+ }
+ mutex_init(&loader->lock);
+
+ spi->bits_per_word = 8;
+
+ if (spi_setup(spi)) {
+ pr_err("failed to setup spi for lte_modem_bootloader\n");
+ ret = -EINVAL;
+ goto err_setup;
+ }
+
+ loader->spi_dev = spi;
+
+ if (!spi->dev.platform_data) {
+ pr_err("failed to get platform data for lte_modem_bootloader\n");
+ ret = -EINVAL;
+ goto err_setup;
+ }
+ pdata = (struct lte_modem_bootloader_platform_data *)spi->dev.platform_data;
+ loader->gpio_lte2ap_status = pdata->gpio_lte2ap_status;
+
+ ret = bootloader_gpio_setup(loader);
+ if (ret) {
+ pr_err("failed to set gpio for lte_modem_boot_loader\n");
+ goto err_setup;
+ }
+
+ loader->gpio_lte2ap_status = pdata->gpio_lte2ap_status;
+ loader->xmit_status = XMIT_BOOT_READY;
+
+ spi_set_drvdata(spi, loader);
+
+ loader->dev.minor = MISC_DYNAMIC_MINOR;
+ loader->dev.name = "lte_spi";
+ loader->dev.fops = &lte_modem_bootloader_fops;
+ ret = misc_register(&loader->dev);
+ if (ret) {
+ pr_err("failed to register misc dev for lte_modem_bootloader\n");
+ goto err_setup;
+ }
+ pr_info("lte_modem_bootloader successfully probed\n");
+#ifdef AIRPLAIN_MODE_TEST
+ lte_airplain_mode = 0;
+#endif
+ return 0;
+
+err_setup:
+ mutex_destroy(&loader->lock);
+ kfree(loader);
+
+err_alloc:
+
+ return ret;
+}
+
+static
+int __devexit lte_modem_bootloader_remove(struct spi_device *spi)
+{
+ struct lte_modem_bootloader *loader = spi_get_drvdata(spi);
+
+ misc_deregister(&loader->dev);
+ mutex_destroy(&loader->lock);
+ kfree(loader);
+
+ return 0;
+}
+
+static
+struct spi_driver lte_modem_bootloader_driver = {
+ .driver = {
+ .name = "lte_modem_spi",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = lte_modem_bootloader_probe,
+ .remove = __devexit_p(lte_modem_bootloader_remove),
+};
+
+static
+int __init lte_modem_bootloader_init(void)
+{
+ return spi_register_driver(&lte_modem_bootloader_driver);
+}
+
+static
+void __exit lte_modem_bootloader_exit(void)
+{
+ spi_unregister_driver(&lte_modem_bootloader_driver);
+}
+
+module_init(lte_modem_bootloader_init);
+module_exit(lte_modem_bootloader_exit);
+
+MODULE_DESCRIPTION("LTE Modem Bootloader driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/misc/modem_if/modem.c b/drivers/misc/modem_if/modem.c
new file mode 100644
index 0000000..b96d96e
--- /dev/null
+++ b/drivers/misc/modem_if/modem.c
@@ -0,0 +1,218 @@
+/* linux/drivers/modem/modem.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+#include <linux/io.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/wakelock.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include "modem_variation.h"
+
+
+static struct modem_ctl *create_modemctl_device(struct platform_device *pdev)
+{
+ int ret = 0;
+ struct modem_data *pdata;
+ struct modem_ctl *modemctl;
+ struct device *dev = &pdev->dev;
+
+ /* create modem control device */
+ modemctl = kzalloc(sizeof(struct modem_ctl), GFP_KERNEL);
+ if (!modemctl)
+ return NULL;
+
+ modemctl->dev = dev;
+ modemctl->phone_state = STATE_OFFLINE;
+
+ pdata = pdev->dev.platform_data;
+ modemctl->name = pdata->name;
+
+ /* init modemctl device for getting modemctl operations */
+ ret = call_modem_init_func(modemctl, pdata);
+ if (ret) {
+ kfree(modemctl);
+ return NULL;
+ }
+
+ pr_debug("[MODEM_IF] %s:create_modemctl_device DONE\n", modemctl->name);
+ return modemctl;
+}
+
+static struct io_device *create_io_device(struct modem_io_t *io_t,
+ struct modem_ctl *modemctl, enum modem_network modem_net)
+{
+ int ret = 0;
+ struct io_device *iod = NULL;
+
+ iod = kzalloc(sizeof(struct io_device), GFP_KERNEL);
+ if (!iod) {
+ pr_err("[MODEM_IF] io device memory alloc fail\n");
+ return NULL;
+ }
+
+ iod->name = io_t->name;
+ iod->id = io_t->id;
+ iod->format = io_t->format;
+ iod->io_typ = io_t->io_type;
+ iod->net_typ = modem_net;
+
+ /* link between io device and modem control */
+ iod->mc = modemctl;
+ if (iod->format == IPC_FMT)
+ modemctl->iod = iod;
+
+ /* register misc device or net device */
+ ret = init_io_device(iod);
+ if (ret) {
+ kfree(iod);
+ return NULL;
+ }
+
+ pr_debug("[MODEM_IF] %s : create_io_device DONE\n", io_t->name);
+ return iod;
+}
+
+static int __devinit modem_probe(struct platform_device *pdev)
+{
+ int i;
+ struct modem_data *pdata;
+ struct modem_ctl *modemctl;
+ struct io_device *iod[MAX_NUM_IO_DEV];
+ struct link_device *ld;
+ struct io_raw_devices *io_raw_devs = NULL;
+
+ pdata = pdev->dev.platform_data;
+ memset(iod, 0, sizeof(iod));
+
+ modemctl = create_modemctl_device(pdev);
+ if (!modemctl)
+ return -ENOMEM;
+
+ /* create link device */
+ ld = call_link_init_func(pdev, pdata->link_type);
+ if (!ld)
+ goto err_free_modemctl;
+
+ io_raw_devs = kzalloc(sizeof(struct io_raw_devices), GFP_KERNEL);
+ if (!io_raw_devs)
+ return -ENOMEM;
+
+ /* create io deivces and connect to modemctl device */
+ for (i = 0; i < pdata->num_iodevs; i++) {
+ iod[i] = create_io_device(&pdata->iodevs[i], modemctl,
+ pdata->modem_net);
+ if (!iod[i])
+ goto err_free_modemctl;
+
+ if (iod[i]->format == IPC_RAW) {
+ int ch = iod[i]->id & 0x1F;
+ io_raw_devs->raw_devices[ch] = iod[i];
+ io_raw_devs->num_of_raw_devs++;
+ iod[i]->link = ld;
+ } else {
+ /* connect io devices to one link device */
+ ld->attach(ld, iod[i]);
+ }
+
+ if (iod[i]->format == IPC_MULTI_RAW)
+ iod[i]->private_data = (void *)io_raw_devs;
+ }
+
+ platform_set_drvdata(pdev, modemctl);
+
+ pr_debug("[MODEM_IF] modem_probe DONE\n");
+ return 0;
+
+err_free_modemctl:
+ for (i = 0; i < pdata->num_iodevs; i++)
+ if (iod[i] != NULL)
+ kfree(iod[i]);
+
+ if (io_raw_devs != NULL)
+ kfree(io_raw_devs);
+
+ if (modemctl != NULL)
+ kfree(modemctl);
+
+ return -ENOMEM;
+}
+
+static void modem_shutdown(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct modem_ctl *mc = dev_get_drvdata(dev);
+
+ if (!mc)
+ return;
+
+ free_irq(mc->irq_phone_active, mc);
+
+ if (mc->ops.modem_off)
+ mc->ops.modem_off(mc);
+}
+
+static int modem_suspend(struct device *pdev)
+{
+ struct modem_ctl *mc = dev_get_drvdata(pdev);
+ gpio_set_value(mc->gpio_pda_active, 0);
+ return 0;
+}
+
+static int modem_resume(struct device *pdev)
+{
+ struct modem_ctl *mc = dev_get_drvdata(pdev);
+ gpio_set_value(mc->gpio_pda_active, 1);
+ return 0;
+}
+
+static const struct dev_pm_ops modem_pm_ops = {
+ .suspend = modem_suspend,
+ .resume = modem_resume,
+};
+
+static struct platform_driver modem_driver = {
+ .probe = modem_probe,
+ .shutdown = modem_shutdown,
+ .driver = {
+ .name = "modem_if",
+ .pm = &modem_pm_ops,
+ },
+};
+
+static int __init modem_init(void)
+{
+ return platform_driver_register(&modem_driver);
+}
+
+module_init(modem_init);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Samsung Modem Interface Driver");
diff --git a/drivers/misc/modem_if/modem_io_device.c b/drivers/misc/modem_if/modem_io_device.c
new file mode 100755
index 0000000..2909bc5
--- /dev/null
+++ b/drivers/misc/modem_if/modem_io_device.c
@@ -0,0 +1,924 @@
+/* /linux/drivers/misc/modem_if/modem_io_device.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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/init.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/if_arp.h>
+#include <linux/ip.h>
+#include <linux/if_ether.h>
+#include <linux/etherdevice.h>
+#include <linux/ratelimit.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+
+
+#define HDLC_START 0x7F
+#define HDLC_END 0x7E
+#define SIZE_OF_HDLC_START 1
+#define SIZE_OF_HDLC_END 1
+#define MAX_RXDATA_SIZE (4096 - 512)
+
+static const char hdlc_start[1] = { HDLC_START };
+static const char hdlc_end[1] = { HDLC_END };
+
+struct fmt_hdr {
+ u16 len;
+ u8 control;
+} __attribute__ ((packed));
+
+struct raw_hdr {
+ u32 len;
+ u8 channel;
+ u8 control;
+} __attribute__ ((packed));
+
+struct rfs_hdr {
+ u32 len;
+ u8 cmd;
+ u8 id;
+} __attribute__ ((packed));
+
+static const char const *modem_state_name[] = {
+ [STATE_OFFLINE] = "OFFLINE",
+ [STATE_CRASH_EXIT] = "CRASH_EXIT",
+ [STATE_BOOTING] = "BOOTING",
+ [STATE_ONLINE] = "ONLINE",
+ [STATE_LOADER_DONE] = "LOADER_DONE",
+};
+
+static int rx_iodev_skb(struct io_device *iod);
+
+static int get_header_size(struct io_device *iod)
+{
+ switch (iod->format) {
+ case IPC_FMT:
+ return sizeof(struct fmt_hdr);
+
+ case IPC_RAW:
+ case IPC_MULTI_RAW:
+ return sizeof(struct raw_hdr);
+
+ case IPC_RFS:
+ return sizeof(struct rfs_hdr);
+
+ case IPC_BOOT:
+ /* minimum size for transaction align */
+ return 4;
+
+ case IPC_RAMDUMP:
+ default:
+ return 0;
+ }
+}
+
+static int get_hdlc_size(struct io_device *iod, char *buf)
+{
+ struct fmt_hdr *fmt_header;
+ struct raw_hdr *raw_header;
+ struct rfs_hdr *rfs_header;
+
+ pr_debug("[MODEM_IF] buf : %02x %02x %02x (%d)\n", *buf, *(buf + 1),
+ *(buf + 2), __LINE__);
+
+ switch (iod->format) {
+ case IPC_FMT:
+ fmt_header = (struct fmt_hdr *)buf;
+ return fmt_header->len;
+ case IPC_RAW:
+ case IPC_MULTI_RAW:
+ raw_header = (struct raw_hdr *)buf;
+ return raw_header->len;
+ case IPC_RFS:
+ rfs_header = (struct rfs_hdr *)buf;
+ return rfs_header->len;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static void *get_header(struct io_device *iod, size_t count,
+ char *frame_header_buf)
+{
+ struct fmt_hdr *fmt_h;
+ struct raw_hdr *raw_h;
+ struct rfs_hdr *rfs_h;
+
+ switch (iod->format) {
+ case IPC_FMT:
+ fmt_h = (struct fmt_hdr *)frame_header_buf;
+
+ fmt_h->len = count + sizeof(struct fmt_hdr);
+ fmt_h->control = 0;
+
+ return (void *)frame_header_buf;
+
+ case IPC_RAW:
+ case IPC_MULTI_RAW:
+ raw_h = (struct raw_hdr *)frame_header_buf;
+
+ raw_h->len = count + sizeof(struct raw_hdr);
+ raw_h->channel = iod->id & 0x1F;
+ raw_h->control = 0;
+
+ return (void *)frame_header_buf;
+
+ case IPC_RFS:
+ rfs_h = (struct rfs_hdr *)frame_header_buf;
+
+ rfs_h->len = count + sizeof(struct raw_hdr);
+ rfs_h->id = iod->id;
+
+ return (void *)frame_header_buf;
+
+ default:
+ return 0;
+ }
+}
+
+static inline int rx_hdlc_head_start_check(char *buf)
+{
+ /* check hdlc head and return size of start byte */
+ return (buf[0] == HDLC_START) ? SIZE_OF_HDLC_START : -EBADMSG;
+}
+
+static inline int rx_hdlc_tail_check(char *buf)
+{
+ /* check hdlc tail and return size of tail byte */
+ return (buf[0] == HDLC_END) ? SIZE_OF_HDLC_END : -EBADMSG;
+}
+
+/* remove hdlc header and store IPC header */
+static int rx_hdlc_head_check(struct io_device *iod, char *buf, unsigned rest)
+{
+ struct header_data *hdr = &iod->h_data;
+ int head_size = get_header_size(iod);
+ int done_len = 0;
+ int len = 0;
+
+ /* first frame, remove start header 7F */
+ if (!hdr->start) {
+ len = rx_hdlc_head_start_check(buf);
+ if (len < 0) {
+ pr_err("[MODEM_IF] Wrong HDLC start: 0x%x(%s)\n",
+ *buf, iod->name);
+ return len; /*Wrong hdlc start*/
+ }
+
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len,
+ rest, __LINE__);
+
+ /* set the start flag of current packet */
+ hdr->start = HDLC_START;
+ hdr->len = 0;
+
+ buf += len;
+ done_len += len;
+ rest -= len; /* rest, call by value */
+ }
+
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len, rest,
+ __LINE__);
+
+ /* store the IPC header to iod priv */
+ if (hdr->len < head_size) {
+ len = min(rest, head_size - hdr->len);
+ memcpy(hdr->hdr + hdr->len, buf, len);
+ hdr->len += len;
+ done_len += len;
+ }
+
+ pr_debug("[MODEM_IF] check done_len : %d, rest : %d (%d)\n", done_len,
+ rest, __LINE__);
+ return done_len;
+}
+
+/* alloc skb and copy dat to skb */
+static int rx_hdlc_data_check(struct io_device *iod, char *buf, unsigned rest)
+{
+ struct header_data *hdr = &iod->h_data;
+ struct sk_buff *skb = iod->skb_recv;
+ int head_size = get_header_size(iod);
+ int data_size = get_hdlc_size(iod, hdr->hdr) - head_size;
+ int alloc_size = min(data_size, MAX_RXDATA_SIZE);
+ int len;
+ int done_len = 0;
+ int rest_len = data_size - hdr->flag_len;
+
+ /* first payload data - alloc skb */
+ if (!skb) {
+ switch (iod->format) {
+ case IPC_RFS:
+ alloc_size = min(data_size + head_size, MAX_RXDATA_SIZE);
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+ /* copy the RFS haeder to skb->data */
+ memcpy(skb_put(skb, head_size), hdr->hdr, head_size);
+ break;
+
+ case IPC_MULTI_RAW:
+ if (data_size > MAX_RXDATA_SIZE) {
+ pr_err("%s: %s: packet size too large (%d)\n",
+ __func__, iod->name, data_size);
+ return -EINVAL;
+ }
+
+ if (iod->net_typ == UMTS_NETWORK)
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
+ else
+ skb = alloc_skb(alloc_size +
+ sizeof(struct ethhdr), GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+
+ if (iod->net_typ != UMTS_NETWORK)
+ skb_reserve(skb, sizeof(struct ethhdr));
+ break;
+
+ default:
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+ break;
+ }
+ iod->skb_recv = skb;
+ }
+
+ while (rest > 0) {
+ len = min(rest, alloc_size - skb->len);
+ len = min(len, rest_len);
+ memcpy(skb_put(skb, len), buf, len);
+ buf += len;
+ done_len += len;
+ hdr->flag_len += len;
+ rest -= len;
+ rest_len -= len;
+
+ if (!rest_len || !rest)
+ break;
+
+ rx_iodev_skb(iod);
+ iod->skb_recv = NULL;
+
+ alloc_size = min(rest_len, MAX_RXDATA_SIZE);
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+ iod->skb_recv = skb;
+ }
+
+ return done_len;
+}
+
+static int rx_iodev_skb_raw(struct io_device *iod)
+{
+ int err;
+ struct sk_buff *skb = iod->skb_recv;
+ struct net_device *ndev;
+ struct iphdr *ip_header;
+ struct ethhdr *ehdr;
+ const char source[ETH_ALEN] = SOURCE_MAC_ADDR;
+
+ switch (iod->io_typ) {
+ case IODEV_MISC:
+ skb_queue_tail(&iod->sk_rx_q, iod->skb_recv);
+ wake_up(&iod->wq);
+ return 0;
+
+ case IODEV_NET:
+ ndev = iod->ndev;
+ if (!ndev)
+ return NET_RX_DROP;
+
+ skb->dev = ndev;
+ ndev->stats.rx_packets++;
+ ndev->stats.rx_bytes += skb->len;
+
+ /* check the version of IP */
+ ip_header = (struct iphdr *)skb->data;
+ if (ip_header->version == IP6VERSION)
+ skb->protocol = htons(ETH_P_IPV6);
+ else
+ skb->protocol = htons(ETH_P_IP);
+
+ if (iod->net_typ == UMTS_NETWORK) {
+ skb_reset_mac_header(skb);
+ } else {
+ ehdr = (void *)skb_push(skb, sizeof(struct ethhdr));
+ memcpy(ehdr->h_dest, ndev->dev_addr, ETH_ALEN);
+ memcpy(ehdr->h_source, source, ETH_ALEN);
+ ehdr->h_proto = skb->protocol;
+ skb->ip_summed = CHECKSUM_NONE;
+ skb_reset_mac_header(skb);
+
+ skb_pull(skb, sizeof(struct ethhdr));
+ }
+
+ err = netif_rx_ni(skb);
+ if (err != NET_RX_SUCCESS)
+ dev_err(&ndev->dev, "rx error: %d\n", err);
+ return err;
+
+ default:
+ pr_err("[MODEM_IF] wrong io_type : %d\n", iod->io_typ);
+ return -EINVAL;
+ }
+}
+
+static void rx_iodev_work(struct work_struct *work)
+{
+ int ret;
+ struct sk_buff *skb;
+ struct io_device *real_iod;
+ struct io_device *iod = container_of(work, struct io_device,
+ rx_work.work);
+
+ skb = skb_dequeue(&iod->sk_rx_q);
+ while (skb) {
+ real_iod = *((struct io_device **)skb->cb);
+ real_iod->skb_recv = skb;
+
+ ret = rx_iodev_skb_raw(real_iod);
+ if (ret == NET_RX_DROP) {
+ pr_err("[MODEM_IF] %s: queue delayed work!\n",
+ __func__);
+ skb_queue_head(&iod->sk_rx_q, skb);
+ schedule_delayed_work(&iod->rx_work,
+ msecs_to_jiffies(20));
+ break;
+ } else if (ret < 0)
+ dev_kfree_skb_any(skb);
+
+ skb = skb_dequeue(&iod->sk_rx_q);
+ }
+}
+
+static int rx_multipdp(struct io_device *iod)
+{
+ u8 ch;
+ struct raw_hdr *raw_header = (struct raw_hdr *)&iod->h_data.hdr;
+ struct io_raw_devices *io_raw_devs =
+ (struct io_raw_devices *)iod->private_data;
+ struct io_device *real_iod;
+
+ ch = raw_header->channel;
+ real_iod = io_raw_devs->raw_devices[ch];
+ if (!real_iod) {
+ pr_err("[MODEM_IF] %s: wrong channel %d\n", __func__, ch);
+ return -1;
+ }
+
+ *((struct io_device **)iod->skb_recv->cb) = real_iod;
+ skb_queue_tail(&iod->sk_rx_q, iod->skb_recv);
+ pr_debug("sk_rx_qlen:%d\n", iod->sk_rx_q.qlen);
+
+ schedule_delayed_work(&iod->rx_work, 0);
+ return 0;
+}
+
+/* de-mux function draft */
+static int rx_iodev_skb(struct io_device *iod)
+{
+ switch (iod->format) {
+ case IPC_MULTI_RAW:
+ return rx_multipdp(iod);
+
+ case IPC_FMT:
+ case IPC_RFS:
+ default:
+ skb_queue_tail(&iod->sk_rx_q, iod->skb_recv);
+
+ pr_debug("[MODEM_IF] wake up fmt,rfs skb\n");
+ wake_up(&iod->wq);
+ return 0;
+ }
+}
+
+static int rx_hdlc_packet(struct io_device *iod, const char *data,
+ unsigned recv_size)
+{
+ unsigned rest = recv_size;
+ char *buf = (char *)data;
+ int err = 0;
+ int len;
+
+ if (rest <= 0)
+ goto exit;
+
+ pr_debug("[MODEM_IF] RX_SIZE=%d\n", rest);
+
+ if (iod->h_data.flag_len)
+ goto data_check;
+
+next_frame:
+ err = len = rx_hdlc_head_check(iod, buf, rest);
+ if (err < 0)
+ goto exit; /* buf++; rest--; goto next_frame; */
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len, rest,
+ __LINE__);
+
+ buf += len;
+ rest -= len;
+ if (rest <= 0)
+ goto exit;
+
+data_check:
+ err = len = rx_hdlc_data_check(iod, buf, rest);
+ if (err < 0)
+ goto exit;
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len, rest,
+ __LINE__);
+
+ buf += len;
+ rest -= len;
+
+ if (!rest && iod->h_data.flag_len)
+ return 0;
+ else if (rest <= 0)
+ goto exit;
+
+ err = len = rx_hdlc_tail_check(buf);
+ if (err < 0) {
+ pr_err("[MODEM_IF] Wrong HDLC end: 0x%x(%s)\n",
+ *buf, iod->name);
+ goto exit;
+ }
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len, rest,
+ __LINE__);
+
+ buf += len;
+ rest -= len;
+ if (rest < 0)
+ goto exit;
+
+ err = rx_iodev_skb(iod);
+ if (err < 0)
+ goto exit;
+
+ /* initialize header & skb */
+ iod->skb_recv = NULL;
+ memset(&iod->h_data, 0x00, sizeof(struct header_data));
+
+ if (rest)
+ goto next_frame;
+
+exit:
+ /* free buffers. mipi-hsi re-use recv buf */
+ if (rest < 0)
+ err = -ERANGE;
+
+ if (err < 0) {
+ /* clear headers */
+ memset(&iod->h_data, 0x00, sizeof(struct header_data));
+
+ if (iod->skb_recv) {
+ dev_kfree_skb_any(iod->skb_recv);
+ iod->skb_recv = NULL;
+ }
+ }
+
+ return err;
+}
+
+/* called from link device when a packet arrives for this io device */
+static int io_dev_recv_data_from_link_dev(struct io_device *iod,
+ const char *data, unsigned int len)
+{
+ struct sk_buff *skb;
+ int err;
+
+ switch (iod->format) {
+ case IPC_FMT:
+ case IPC_RAW:
+ case IPC_RFS:
+ case IPC_MULTI_RAW:
+ err = rx_hdlc_packet(iod, data, len);
+ if (err < 0)
+ pr_err("[MODEM_IF] fail process hdlc fram\n");
+ return err;
+
+ case IPC_CMD:
+ /* TODO- handle flow control command from CP */
+ return 0;
+
+ case IPC_BOOT:
+ case IPC_RAMDUMP:
+ /* save packet to sk_buff */
+ skb = alloc_skb(len, GFP_ATOMIC);
+ if (!skb) {
+ pr_err("[MODEM_IF] fail alloc skb (%d)\n", __LINE__);
+ return -ENOMEM;
+ }
+
+ pr_debug("[MODEM_IF] boot len : %d\n", len);
+
+ memcpy(skb_put(skb, len), data, len);
+ skb_queue_tail(&iod->sk_rx_q, skb);
+ pr_debug("[MODEM_IF] skb len : %d\n", skb->len);
+
+ wake_up(&iod->wq);
+ return len;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/* inform the IO device that the modem is now online or offline or
+ * crashing or whatever...
+ */
+static void io_dev_modem_state_changed(struct io_device *iod,
+ enum modem_state state)
+{
+ iod->mc->phone_state = state;
+ pr_info("[MODEM_IF] %s state changed: %s\n", iod->name, modem_state_name[state]);
+
+ if (state == STATE_CRASH_EXIT)
+ wake_up(&iod->wq);
+}
+
+static int misc_open(struct inode *inode, struct file *filp)
+{
+ struct io_device *iod = to_io_device(filp->private_data);
+ filp->private_data = (void *)iod;
+
+ pr_info("[MODEM_IF] misc_open : %s\n", iod->name);
+
+ if (iod->link->init_comm)
+ return iod->link->init_comm(iod->link, iod);
+ return 0;
+}
+
+static int misc_release(struct inode *inode, struct file *filp)
+{
+ struct io_device *iod = (struct io_device *)filp->private_data;
+
+ pr_info("[MODEM_IF] misc_release : %s\n", iod->name);
+
+ if (iod->link->terminate_comm)
+ iod->link->terminate_comm(iod->link, iod);
+
+ skb_queue_purge(&iod->sk_rx_q);
+ return 0;
+}
+
+static unsigned int misc_poll(struct file *filp, struct poll_table_struct *wait)
+{
+ struct io_device *iod = (struct io_device *)filp->private_data;
+
+ poll_wait(filp, &iod->wq, wait);
+
+ if ((!skb_queue_empty(&iod->sk_rx_q))
+ && (iod->mc->phone_state != STATE_OFFLINE))
+ return POLLIN | POLLRDNORM;
+ else if (iod->mc->phone_state == STATE_CRASH_EXIT)
+ return POLLHUP;
+ else
+ return 0;
+}
+
+static long misc_ioctl(struct file *filp, unsigned int cmd, unsigned long _arg)
+{
+ struct io_device *iod = (struct io_device *)filp->private_data;
+
+ pr_debug("[MODEM_IF] misc_ioctl : 0x%x\n", cmd);
+
+ switch (cmd) {
+ case IOCTL_MODEM_ON:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_ON\n");
+ return iod->mc->ops.modem_on(iod->mc);
+
+ case IOCTL_MODEM_OFF:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_OFF\n");
+ return iod->mc->ops.modem_off(iod->mc);
+
+ case IOCTL_MODEM_RESET:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_RESET\n");
+ return iod->mc->ops.modem_reset(iod->mc);
+
+ case IOCTL_MODEM_FORCE_CRASH_EXIT:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_FORCE_CRASH_EXIT\n");
+ return iod->mc->ops.modem_force_crash_exit(iod->mc);
+
+ case IOCTL_MODEM_DUMP_RESET:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_FORCE_CRASH_EXIT\n");
+ return iod->mc->ops.modem_dump_reset(iod->mc);
+
+ case IOCTL_MODEM_BOOT_ON:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_BOOT_ON\n");
+ return iod->mc->ops.modem_boot_on(iod->mc);
+
+ case IOCTL_MODEM_BOOT_OFF:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_BOOT_OFF\n");
+ return iod->mc->ops.modem_boot_off(iod->mc);
+
+ /* TODO - will remove this command after ril updated */
+ case IOCTL_MODEM_START:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_START\n");
+ return 0;
+
+ case IOCTL_MODEM_STATUS:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_STATUS\n");
+ return iod->mc->phone_state;
+
+ case IOCTL_MODEM_DUMP_START:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_DUMP_START\n");
+ return iod->link->dump_start(iod->link, iod);
+
+ case IOCTL_MODEM_DUMP_UPDATE:
+ pr_debug("[MODEM_IF] misc_ioctl : IOCTL_MODEM_DUMP_UPDATE\n");
+ return iod->link->dump_update(iod->link, iod, _arg);
+
+ case IOCTL_MODEM_GOTA_START:
+ pr_debug("[GOTA] misc_ioctl : IOCTL_MODEM_GOTA_START\n");
+ return iod->link->gota_start(iod->link, iod);
+
+ case IOCTL_MODEM_FW_UPDATE:
+ pr_debug("[GOTA] misc_ioctl : IOCTL_MODEM_FW_UPDATE\n");
+ return iod->link->modem_update(iod->link, iod, _arg);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t misc_write(struct file *filp, const char __user * buf,
+ size_t count, loff_t *ppos)
+{
+ struct io_device *iod = (struct io_device *)filp->private_data;
+ int frame_len = 0;
+ char frame_header_buf[sizeof(struct raw_hdr)];
+ struct sk_buff *skb;
+
+ /* TODO - check here flow control for only raw data */
+
+ if (iod->format == IPC_BOOT || iod->format == IPC_RAMDUMP)
+ frame_len = count + get_header_size(iod);
+ else
+ frame_len = count + SIZE_OF_HDLC_START + get_header_size(iod)
+ + SIZE_OF_HDLC_END;
+
+ skb = alloc_skb(frame_len, GFP_KERNEL);
+ if (!skb) {
+ pr_err("[MODEM_IF] fail alloc skb (%d)\n", __LINE__);
+ return -ENOMEM;
+ }
+
+ switch (iod->format) {
+ case IPC_BOOT:
+ case IPC_RAMDUMP:
+ if (copy_from_user(skb_put(skb, count), buf, count) != 0) {
+ dev_kfree_skb_any(skb);
+ return -EFAULT;
+ }
+ break;
+
+ case IPC_RFS:
+ memcpy(skb_put(skb, SIZE_OF_HDLC_START), hdlc_start,
+ SIZE_OF_HDLC_START);
+ if (copy_from_user(skb_put(skb, count), buf, count) != 0) {
+ dev_kfree_skb_any(skb);
+ return -EFAULT;
+ }
+ memcpy(skb_put(skb, SIZE_OF_HDLC_END), hdlc_end,
+ SIZE_OF_HDLC_END);
+ break;
+
+ default:
+ memcpy(skb_put(skb, SIZE_OF_HDLC_START), hdlc_start,
+ SIZE_OF_HDLC_START);
+ memcpy(skb_put(skb, get_header_size(iod)),
+ get_header(iod, count, frame_header_buf),
+ get_header_size(iod));
+ if (copy_from_user(skb_put(skb, count), buf, count) != 0) {
+ dev_kfree_skb_any(skb);
+ return -EFAULT;
+ }
+ memcpy(skb_put(skb, SIZE_OF_HDLC_END), hdlc_end,
+ SIZE_OF_HDLC_END);
+ break;
+ }
+
+ /* send data with sk_buff, link device will put sk_buff
+ * into the specific sk_buff_q and run work-q to send data
+ */
+ return iod->link->send(iod->link, iod, skb);
+}
+
+static ssize_t misc_read(struct file *filp, char *buf, size_t count,
+ loff_t *f_pos)
+{
+ struct io_device *iod = (struct io_device *)filp->private_data;
+ struct sk_buff *skb;
+ int pktsize = 0;
+
+ skb = skb_dequeue(&iod->sk_rx_q);
+ if (!skb) {
+ printk_ratelimited(KERN_ERR "[MODEM_IF] no data from sk_rx_q, "
+ "modem_state : %s(%s)\n",
+ modem_state_name[iod->mc->phone_state], iod->name);
+ return 0;
+ }
+
+ if (skb->len > count) {
+ pr_err("[MODEM_IF] skb len is too big = %d,%d!(%d)\n",
+ count, skb->len, __LINE__);
+ dev_kfree_skb_any(skb);
+ return -EIO;
+ }
+ pr_debug("[MODEM_IF] skb len : %d\n", skb->len);
+
+ pktsize = skb->len;
+ if (copy_to_user(buf, skb->data, pktsize) != 0)
+ return -EIO;
+ dev_kfree_skb_any(skb);
+
+ pr_debug("[MODEM_IF] copy to user : %d\n", pktsize);
+
+ return pktsize;
+}
+
+static const struct file_operations misc_io_fops = {
+ .owner = THIS_MODULE,
+ .open = misc_open,
+ .release = misc_release,
+ .poll = misc_poll,
+ .unlocked_ioctl = misc_ioctl,
+ .write = misc_write,
+ .read = misc_read,
+};
+
+static int vnet_open(struct net_device *ndev)
+{
+ netif_start_queue(ndev);
+ return 0;
+}
+
+static int vnet_stop(struct net_device *ndev)
+{
+ netif_stop_queue(ndev);
+ return 0;
+}
+
+static int vnet_xmit(struct sk_buff *skb, struct net_device *ndev)
+{
+ int ret;
+ struct raw_hdr hd;
+ struct sk_buff *skb_new;
+ struct vnet *vnet = netdev_priv(ndev);
+ struct io_device *iod = vnet->iod;
+
+ /* umts doesn't need to discard ethernet header */
+ if (iod->net_typ != UMTS_NETWORK) {
+ if (iod->id >= PSD_DATA_CHID_BEGIN &&
+ iod->id <= PSD_DATA_CHID_END)
+ skb_pull(skb, sizeof(struct ethhdr));
+ }
+
+ hd.len = skb->len + sizeof(hd);
+ hd.control = 0;
+ hd.channel = iod->id & 0x1F;
+
+ skb_new = skb_copy_expand(skb, sizeof(hd) + sizeof(hdlc_start),
+ sizeof(hdlc_end), GFP_ATOMIC);
+ if (!skb_new) {
+ dev_kfree_skb_any(skb);
+ return -ENOMEM;
+ }
+
+ memcpy(skb_push(skb_new, sizeof(hd)), &hd, sizeof(hd));
+ memcpy(skb_push(skb_new, sizeof(hdlc_start)), hdlc_start,
+ sizeof(hdlc_start));
+ memcpy(skb_put(skb_new, sizeof(hdlc_end)), hdlc_end, sizeof(hdlc_end));
+
+ ret = iod->link->send(iod->link, iod, skb_new);
+ if (ret < 0) {
+ dev_kfree_skb_any(skb);
+ return NETDEV_TX_BUSY;
+ }
+
+ ndev->stats.tx_packets++;
+ ndev->stats.tx_bytes += skb->len;
+ dev_kfree_skb_any(skb);
+
+ return NETDEV_TX_OK;
+}
+
+static struct net_device_ops vnet_ops = {
+ .ndo_open = vnet_open,
+ .ndo_stop = vnet_stop,
+ .ndo_start_xmit = vnet_xmit,
+};
+
+static void vnet_setup(struct net_device *ndev)
+{
+ ndev->netdev_ops = &vnet_ops;
+ ndev->type = ARPHRD_PPP;
+ ndev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST;
+ ndev->addr_len = 0;
+ ndev->hard_header_len = 0;
+ ndev->tx_queue_len = 1000;
+ ndev->mtu = ETH_DATA_LEN;
+ ndev->watchdog_timeo = 5 * HZ;
+}
+
+static void vnet_setup_ether(struct net_device *ndev)
+{
+ ndev->netdev_ops = &vnet_ops;
+ ndev->type = ARPHRD_ETHER;
+ ndev->flags = IFF_POINTOPOINT | IFF_NOARP | IFF_MULTICAST | IFF_SLAVE;
+ ndev->addr_len = ETH_ALEN;
+ random_ether_addr(ndev->dev_addr);
+ ndev->hard_header_len = 0;
+ ndev->tx_queue_len = 1000;
+ ndev->mtu = ETH_DATA_LEN;
+ ndev->watchdog_timeo = 5 * HZ;
+}
+
+int init_io_device(struct io_device *iod)
+{
+ int ret = 0;
+ struct vnet *vnet;
+
+ /* get modem state from modem control device */
+ iod->modem_state_changed = io_dev_modem_state_changed;
+ /* get data from link device */
+ iod->recv = io_dev_recv_data_from_link_dev;
+
+ INIT_LIST_HEAD(&iod->list);
+
+ /* register misc or net drv */
+ switch (iod->io_typ) {
+ case IODEV_MISC:
+ init_waitqueue_head(&iod->wq);
+ skb_queue_head_init(&iod->sk_rx_q);
+ INIT_DELAYED_WORK(&iod->rx_work, rx_iodev_work);
+
+ iod->miscdev.minor = MISC_DYNAMIC_MINOR;
+ iod->miscdev.name = iod->name;
+ iod->miscdev.fops = &misc_io_fops;
+
+ ret = misc_register(&iod->miscdev);
+ if (ret)
+ pr_err("failed to register misc io device : %s\n",
+ iod->name);
+
+ break;
+
+ case IODEV_NET:
+ if (iod->net_typ == UMTS_NETWORK)
+ iod->ndev = alloc_netdev(0, iod->name, vnet_setup);
+ else
+ iod->ndev = alloc_netdev(0, iod->name,
+ vnet_setup_ether);
+ if (!iod->ndev) {
+ pr_err("failed to alloc netdev\n");
+ return -ENOMEM;
+ }
+
+ ret = register_netdev(iod->ndev);
+ if (ret)
+ free_netdev(iod->ndev);
+
+ pr_debug("%s: %d(iod:0x%p)\n", __func__, __LINE__, iod);
+ vnet = netdev_priv(iod->ndev);
+ pr_debug("%s: %d(vnet:0x%p)\n", __func__, __LINE__, vnet);
+ vnet->iod = iod;
+
+ break;
+
+ case IODEV_DUMMY:
+ skb_queue_head_init(&iod->sk_rx_q);
+ INIT_DELAYED_WORK(&iod->rx_work, rx_iodev_work);
+
+ break;
+
+ default:
+ pr_err("wrong io_type : %d\n", iod->io_typ);
+ return -EINVAL;
+ }
+
+ pr_debug("[MODEM_IF] %s(%d) : init_io_device() done : %d\n",
+ iod->name, iod->io_typ, ret);
+ return ret;
+}
+
diff --git a/drivers/misc/modem_if/modem_link_device_dpram.c b/drivers/misc/modem_if/modem_link_device_dpram.c
new file mode 100755
index 0000000..60c5c7f
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_dpram.c
@@ -0,0 +1,1006 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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/irq.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/wakelock.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/vmalloc.h>
+#include <linux/if_arp.h>
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include "modem_link_device_dpram.h"
+
+/* interrupt masks.*/
+#define INT_MASK_VALID 0x0080
+#define INT_MASK_CMD 0x0040
+#define INT_MASK_REQ_ACK_F 0x0020
+#define INT_MASK_REQ_ACK_R 0x0010
+#define INT_MASK_RES_ACK_F 0x0008
+#define INT_MASK_RES_ACK_R 0x0004
+#define INT_MASK_SEND_F 0x0002
+#define INT_MASK_SEND_R 0x0001
+#define INT_VALID(x) ((x) & INT_MASK_VALID)
+#define INT_CMD_VALID(x) ((x) & INT_MASK_CMD)
+#define INT_NON_CMD(x) (INT_MASK_VALID | (x))
+#define INT_CMD(x) (INT_MASK_VALID | INT_MASK_CMD | (x))
+
+#define INT_CMD_MASK(x) ((x) & 0xF)
+#define INT_CMD_INIT_START 0x1
+#define INT_CMD_INIT_END 0x2
+#define INT_CMD_REQ_ACTIVE 0x3
+#define INT_CMD_RES_ACTIVE 0x4
+#define INT_CMD_REQ_TIME_SYNC 0x5
+#define INT_CMD_PHONE_START 0x8
+#define INT_CMD_ERR_DISPLAY 0x9
+#define INT_CMD_PHONE_DEEP_SLEEP 0xA
+#define INT_CMD_NV_REBUILDING 0xB
+#define INT_CMD_EMER_DOWN 0xC
+#define INT_CMD_PIF_INIT_DONE 0xD
+#define INT_CMD_SILENT_NV_REBUILDING 0xE
+#define INT_CMD_NORMAL_POWER_OFF 0xF
+
+/* special interrupt cmd indicating modem boot failure. */
+#define INT_POWERSAFE_FAIL 0xDEAD
+
+#define GOTA_CMD_VALID(x) (((x) & 0xA000) == 0xA000)
+#define GOTA_RESULT_FAIL 0x2
+#define GOTA_RESULT_SUCCESS 0x1
+#define GOTA_CMD_MASK(x) (((x) >> 8) & 0xF)
+#define GOTA_CMD_RECEIVE_READY 0x1
+#define GOTA_CMD_DOWNLOAD_START_REQ 0x2
+#define GOTA_CMD_DOWNLOAD_START_RESP 0x3
+#define GOTA_CMD_IMAGE_SEND_REQ 0x4
+#define GOTA_CMD_IMAGE_SEND_RESP 0x5
+#define GOTA_CMD_SEND_DONE_REQ 0x6
+#define GOTA_CMD_SEND_DONE_RESP 0x7
+#define GOTA_CMD_STATUS_UPDATE 0x8
+#define GOTA_CMD_UPDATE_DONE 0x9
+#define GOTA_CMD_EFS_CLEAR_RESP 0xB
+#define GOTA_CMD_ALARM_BOOT_OK 0xC
+#define GOTA_CMD_ALARM_BOOT_FAIL 0xD
+
+#define CMD_DL_START_REQ 0x9200
+#define CMD_IMG_SEND_REQ 0x9400
+#define CMD_DL_SEND_DONE_REQ 0x9600
+#define CMD_UL_RECEIVE_RESP 0x9601
+#define CMD_UL_RECEIVE_DONE_RESP 0x9801
+
+#define START_INDEX 0x7F
+#define END_INDEX 0x7E
+
+#define DP_MAGIC_CODE 0xAA
+#define DP_MAGIC_DMDL 0x4445444C
+#define DP_MAGIC_UMDL 0x4445444D
+#define DP_DPRAM_SIZE 0x4000
+#define DP_DEFAULT_WRITE_LEN 8168
+#define DP_DEFAULT_DUMP_LEN 16366
+#define DP_DUMP_HEADER_SIZE 7
+
+#define GOTA_TIMEOUT (50 * HZ)
+#define GOTA_SEND_TIMEOUT (200 * HZ)
+#define DUMP_TIMEOUT (30 * HZ)
+#define DUMP_START_TIMEOUT (100 * HZ)
+
+static int
+dpram_download(struct dpram_link_device *dpld, const char *buf, int len);
+static int
+dpram_upload(struct dpram_link_device *dpld, struct dpram_firmware *uploaddata);
+
+static inline int dpram_readh(void __iomem *p_dest)
+{
+ unsigned long dest = (unsigned long)p_dest;
+ return ioread16(dest);
+}
+
+static inline void dpram_writew(u32 value, void __iomem *p_dest)
+{
+ unsigned long dest = (unsigned long)p_dest;
+ iowrite32(value, dest);
+}
+
+static inline void dpram_writeh(u16 value, void __iomem *p_dest)
+{
+ unsigned long dest = (unsigned long)p_dest;
+ iowrite16(value, dest);
+}
+
+static inline void dpram_writeb(u8 value, void __iomem *p_dest)
+{
+ unsigned long dest = (unsigned long)p_dest;
+ iowrite8(value, dest);
+}
+
+
+static void dpram_write_command(struct dpram_link_device *dpld, u16 cmd)
+{
+ dpram_writeh(cmd, &dpld->dpram->mbx_ap2cp);
+}
+
+static void dpram_clear_interrupt(struct dpram_link_device *dpld)
+{
+ dpram_writeh(0, &dpld->dpram->mbx_cp2ap);
+}
+
+static void dpram_drop_data(struct dpram_device *device, u16 head)
+{
+ dpram_writeh(head, &device->in->tail);
+}
+
+static void dpram_zero_circ(struct dpram_circ *circ)
+{
+ dpram_writeh(0, &circ->head);
+ dpram_writeh(0, &circ->tail);
+}
+
+static void dpram_clear(struct dpram_link_device *dpld)
+{
+ dpram_zero_circ(&dpld->dpram->fmt_out);
+ dpram_zero_circ(&dpld->dpram->raw_out);
+ dpram_zero_circ(&dpld->dpram->fmt_in);
+ dpram_zero_circ(&dpld->dpram->raw_in);
+}
+
+static bool dpram_circ_valid(int size, u16 head, u16 tail)
+{
+ if (head >= size) {
+ pr_err("[DPRAM] head(%d) >= size(%d)\n", head, size);
+ return false;
+ }
+ if (tail >= size) {
+ pr_err("[DPRAM] tail(%d) >= size(%d)\n", tail, size);
+ return false;
+ }
+ return true;
+}
+
+static int dpram_init_and_report(struct dpram_link_device *dpld)
+{
+ const u16 init_end = INT_CMD(INT_CMD_INIT_END);
+ u16 magic;
+ u16 enable;
+
+ dpram_writeh(0, &dpld->dpram->enable);
+ dpram_clear(dpld);
+ dpram_writeh(DP_MAGIC_CODE, &dpld->dpram->magic);
+ dpram_writeh(1, &dpld->dpram->enable);
+
+ /* Send init end code to modem */
+ dpram_write_command(dpld, init_end);
+
+ magic = dpram_readh(&dpld->dpram->magic);
+ if (magic != DP_MAGIC_CODE) {
+ pr_err("[DPRAM]: %s: Failed to check magic\n", __func__);
+ return -1;
+ }
+
+ enable = dpram_readh(&dpld->dpram->enable);
+ if (!enable) {
+ pr_err("[DPRAM]: %s: DPRAM enable failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct io_device *dpram_find_iod(struct dpram_link_device *dpld, int id)
+{
+ struct io_device *iod;
+
+ list_for_each_entry(iod, &dpld->list_of_io_devices, list) {
+ if ((id == FMT_IDX && iod->format == IPC_FMT) ||
+ (id == RAW_IDX && iod->format == IPC_MULTI_RAW))
+ return iod;
+ }
+
+ return NULL;
+}
+
+static void cmd_req_active_handler(struct dpram_link_device *dpld)
+{
+ dpram_write_command(dpld, INT_CMD(INT_CMD_RES_ACTIVE));
+}
+
+static void cmd_error_display_handler(struct dpram_link_device *dpld)
+{
+ struct io_device *iod = dpram_find_iod(dpld, FMT_IDX);
+
+ pr_info("[DPRAM] Received 0xc9 from modem (CP Crash)\n");
+ pr_info("[DPRAM] %s\n", dpld->dpram->fmt_in_buff);
+
+ if (iod && iod->modem_state_changed)
+ iod->modem_state_changed(iod, STATE_CRASH_EXIT);
+}
+
+static void cmd_phone_start_handler(struct dpram_link_device *dpld)
+{
+ pr_debug("[DPRAM] Received 0xc8 from modem (Boot OK)\n");
+ complete_all(&dpld->dpram_init_cmd);
+ dpram_init_and_report(dpld);
+}
+
+static void command_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ pr_debug("[DPRAM] %s: %x\n", __func__, cmd);
+
+ switch (INT_CMD_MASK(cmd)) {
+ case INT_CMD_REQ_ACTIVE:
+ cmd_req_active_handler(dpld);
+ break;
+
+ case INT_CMD_ERR_DISPLAY:
+ cmd_error_display_handler(dpld);
+ break;
+
+ case INT_CMD_PHONE_START:
+ cmd_phone_start_handler(dpld);
+ break;
+
+ case INT_CMD_NV_REBUILDING:
+ pr_err("[MODEM_IF] NV_REBUILDING\n");
+ break;
+
+ case INT_CMD_PIF_INIT_DONE:
+ complete_all(&dpld->modem_pif_init_done);
+ break;
+
+ case INT_CMD_SILENT_NV_REBUILDING:
+ pr_err("[MODEM_IF] SILENT_NV_REBUILDING\n");
+ break;
+
+ case INT_CMD_NORMAL_POWER_OFF:
+ /*ToDo:*/
+ /*kernel_sec_set_cp_ack()*/;
+ break;
+
+ case INT_CMD_REQ_TIME_SYNC:
+ case INT_CMD_PHONE_DEEP_SLEEP:
+ case INT_CMD_EMER_DOWN:
+ break;
+
+ default:
+ pr_err("Unknown command.. %x\n", cmd);
+ }
+}
+
+
+static int dpram_process_modem_update(struct dpram_link_device *dpld,
+ struct dpram_firmware *pfw)
+{
+ int ret = 0;
+ char *buff = vmalloc(pfw->size);
+
+ pr_debug("[GOTA] modem size =[%d]\n", pfw->size);
+
+ if (!buff)
+ return -ENOMEM;
+
+ ret = copy_from_user(buff, pfw->firmware, pfw->size);
+ if (ret < 0) {
+ pr_err("[%s:%d] Copy from user failed\n", __func__, __LINE__);
+ goto out;
+ }
+
+ ret = dpram_download(dpld, buff, pfw->size);
+ if (ret < 0)
+ pr_err("firmware write failed\n");
+
+out:
+ vfree(buff);
+ return ret;
+}
+
+
+static int dpram_modem_update(struct link_device *ld, struct io_device *iod,
+ unsigned long _arg)
+{
+ int ret;
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ struct dpram_firmware fw;
+
+ pr_debug("[GOTA] dpram_modem_update\n");
+
+ ret = copy_from_user(&fw, (void __user *)_arg, sizeof(fw));
+ if (ret < 0) {
+ pr_err("copy from user failed!");
+ return ret;
+ }
+
+ return dpram_process_modem_update(dpld, &fw);
+}
+
+static int dpram_dump_update(struct link_device *ld, struct io_device *iod,
+ unsigned long _arg)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ struct dpram_firmware *fw = (struct dpram_firmware *)_arg ;
+
+ pr_debug("[DPRAM] dpram_dump_update()\n");
+
+ return dpram_upload(dpld, fw);
+}
+
+static int dpram_read(struct dpram_link_device *dpld,
+ struct dpram_device *device, int dev_idx)
+{
+ struct io_device *iod;
+ int size;
+ int tmp_size;
+ u16 head, tail;
+ char *buff;
+
+ head = dpram_readh(&device->in->head);
+ tail = dpram_readh(&device->in->tail);
+ pr_debug("=====> %s, head: %d, tail: %d\n", __func__, head, tail);
+
+ if (head == tail) {
+ pr_err("[DPRAM] %s: head == tail\n", __func__);
+ goto err_dpram_read;
+ }
+
+ if (!dpram_circ_valid(device->in_buff_size, head, tail)) {
+ pr_err("[DPRAM] %s: invalid circular buffer\n", __func__);
+ dpram_zero_circ(device->in);
+ goto err_dpram_read;
+ }
+
+ iod = dpram_find_iod(dpld, dev_idx);
+ if (!iod) {
+ pr_err("[DPRAM] iod == NULL\n");
+ goto err_dpram_read;
+ }
+
+ /* Get data size in DPRAM*/
+ size = (head > tail) ? (head - tail) :
+ (device->in_buff_size - tail + head);
+
+ /* ----- (tail) 7f 00 00 7e (head) ----- */
+ if (head > tail) {
+ buff = device->in_buff_addr + tail;
+ if (iod->recv(iod, buff, size) < 0) {
+ pr_err("[DPRAM] %s: recv error, dropping data\n",
+ __func__);
+ dpram_drop_data(device, head);
+ goto err_dpram_read;
+ }
+ } else { /* 00 7e (head) ----------- (tail) 7f 00 */
+ /* 1. tail -> buffer end.*/
+ tmp_size = device->in_buff_size - tail;
+ buff = device->in_buff_addr + tail;
+ if (iod->recv(iod, buff, tmp_size) < 0) {
+ pr_err("[DPRAM] %s: recv error, dropping data\n",
+ __func__);
+ dpram_drop_data(device, head);
+ goto err_dpram_read;
+ }
+
+ /* 2. buffer start -> head.*/
+ if (size > tmp_size) {
+ buff = (char *)device->in_buff_addr;
+ if (iod->recv(iod, buff, (size - tmp_size)) < 0) {
+ pr_err("[DPRAM] %s: recv error, dropping data\n",
+ __func__);
+ dpram_drop_data(device, head);
+ goto err_dpram_read;
+ }
+ }
+ }
+
+ /* new tail */
+ tail = (u16)((tail + size) % device->in_buff_size);
+ dpram_writeh(tail, &device->in->tail);
+
+ return size;
+
+err_dpram_read:
+ return -EINVAL;
+}
+
+static void non_command_handler(struct dpram_link_device *dpld,
+ u16 non_cmd)
+{
+ struct dpram_device *device = NULL;
+ u16 head, tail;
+ u16 magic, access;
+ int ret = 0;
+
+ pr_debug("[DPRAM] Entering non_command_handler(0x%04X)\n", non_cmd);
+
+ magic = dpram_readh(&dpld->dpram->magic);
+ access = dpram_readh(&dpld->dpram->enable);
+
+ if (!access || magic != DP_MAGIC_CODE) {
+ pr_err("fmr recevie error!!!! access = 0x%x, magic =0x%x",
+ access, magic);
+ return;
+ }
+
+ /* Check formatted data region */
+ device = &dpld->dev_map[FMT_IDX];
+ head = dpram_readh(&device->in->head);
+ tail = dpram_readh(&device->in->tail);
+
+ if (!dpram_circ_valid(device->in_buff_size, head, tail)) {
+ pr_err("[DPRAM] %s: invalid circular buffer\n", __func__);
+ dpram_zero_circ(device->in);
+ return;
+ }
+
+ if (head != tail) {
+ if (non_cmd & INT_MASK_REQ_ACK_F)
+ atomic_inc(&dpld->fmt_txq_req_ack_rcvd);
+
+ ret = dpram_read(dpld, device, FMT_IDX);
+ if (ret < 0)
+ pr_err("%s, dpram_read failed\n", __func__);
+
+ if (atomic_read(&dpld->fmt_txq_req_ack_rcvd) > 0) {
+ dpram_write_command(dpld,
+ INT_NON_CMD(INT_MASK_RES_ACK_F));
+ atomic_set(&dpld->fmt_txq_req_ack_rcvd, 0);
+ }
+ } else {
+ if (non_cmd & INT_MASK_REQ_ACK_F) {
+ dpram_write_command(dpld,
+ INT_NON_CMD(INT_MASK_RES_ACK_F));
+ atomic_set(&dpld->fmt_txq_req_ack_rcvd, 0);
+ }
+ }
+
+ /* Check raw data region */
+ device = &dpld->dev_map[RAW_IDX];
+ head = dpram_readh(&device->in->head);
+ tail = dpram_readh(&device->in->tail);
+
+ if (!dpram_circ_valid(device->in_buff_size, head, tail)) {
+ pr_err("[DPRAM] %s: invalid circular buffer\n", __func__);
+ dpram_zero_circ(device->in);
+ return;
+ }
+
+ if (head != tail) {
+ if (non_cmd & INT_MASK_REQ_ACK_R)
+ atomic_inc(&dpld->raw_txq_req_ack_rcvd);
+
+ ret = dpram_read(dpld, device, RAW_IDX);
+ if (ret < 0)
+ pr_err("%s, dpram_read failed\n", __func__);
+
+ if (atomic_read(&dpld->raw_txq_req_ack_rcvd) > 0) {
+ dpram_write_command(dpld,
+ INT_NON_CMD(INT_MASK_RES_ACK_R));
+ atomic_set(&dpld->raw_txq_req_ack_rcvd, 0);
+ }
+ } else {
+ if (non_cmd & INT_MASK_REQ_ACK_R) {
+ dpram_write_command(dpld,
+ INT_NON_CMD(INT_MASK_RES_ACK_R));
+ atomic_set(&dpld->raw_txq_req_ack_rcvd, 0);
+ }
+ }
+}
+
+static void gota_cmd_handler(struct dpram_link_device *dpld, u16 cmd)
+{
+ if (cmd & GOTA_RESULT_FAIL) {
+ pr_err("[GOTA] Command failed: %04x\n", cmd);
+ return;
+ }
+
+ switch (GOTA_CMD_MASK(cmd)) {
+ case GOTA_CMD_RECEIVE_READY:
+ pr_debug("[GOTA] Send CP-->AP RECEIVE_READY\n");
+ dpram_write_command(dpld, CMD_DL_START_REQ);
+ break;
+
+ case GOTA_CMD_DOWNLOAD_START_RESP:
+ pr_debug("[GOTA] Send CP-->AP DOWNLOAD_START_RESP\n");
+ complete_all(&dpld->gota_download_start_complete);
+ break;
+
+ case GOTA_CMD_SEND_DONE_RESP:
+ pr_debug("[GOTA] Send CP-->AP SEND_DONE_RESP\n");
+ complete_all(&dpld->gota_send_done);
+ break;
+
+ case GOTA_CMD_UPDATE_DONE:
+ pr_debug("[GOTA] Send CP-->AP UPDATE_DONE\n");
+ complete_all(&dpld->gota_update_done);
+ break;
+
+ case GOTA_CMD_IMAGE_SEND_RESP:
+ pr_debug("[DPRAM] Send CP-->AP IMAGE_SEND_RESP\n");
+ complete_all(&dpld->dump_receive_done);
+ break;
+
+ default:
+ pr_err("[GOTA] Unknown command.. %x\n", cmd);
+ }
+}
+
+static irqreturn_t dpram_irq_handler(int irq, void *p_ld)
+{
+ u16 cp2ap;
+
+ struct link_device *ld = (struct link_device *)p_ld;
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+
+ cp2ap = dpram_readh(&dpld->dpram->mbx_cp2ap);
+
+ pr_debug("[DPRAM] received CP2AP = 0x%x\n", cp2ap);
+
+ if (cp2ap == INT_POWERSAFE_FAIL) {
+ pr_err("[DPRAM] Received POWERSAFE_FAIL\n");
+ goto exit_irq;
+ }
+
+ if (GOTA_CMD_VALID(cp2ap))
+ gota_cmd_handler(dpld, cp2ap);
+ else if (INT_CMD_VALID(cp2ap))
+ command_handler(dpld, cp2ap);
+ else if (INT_VALID(cp2ap))
+ non_command_handler(dpld, cp2ap);
+ else
+ pr_err("[DPRAM] Invalid command %04x\n", cp2ap);
+
+exit_irq:
+ return IRQ_HANDLED;
+}
+
+static int dpram_attach_io_dev(struct link_device *ld, struct io_device *iod)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+
+ iod->link = ld;
+ /* list up io devices */
+ list_add(&iod->list, &dpld->list_of_io_devices);
+
+ return 0;
+}
+
+static int dpram_write(struct dpram_link_device *dpld,
+ struct dpram_device *device,
+ const unsigned char *buf,
+ int len)
+{
+ u16 head;
+ u16 tail;
+ u16 irq_mask;
+ int free_space;
+ int last_size;
+
+ head = dpram_readh(&device->out->head);
+ tail = dpram_readh(&device->out->tail);
+
+ if (!dpram_circ_valid(device->out_buff_size, head, tail)) {
+ pr_err("[DPRAM] %s: invalid circular buffer\n", __func__);
+ dpram_zero_circ(device->out);
+ return -EINVAL;
+ }
+
+ free_space = (head < tail) ? tail - head - 1 :
+ device->out_buff_size + tail - head - 1;
+ if (len > free_space) {
+ pr_debug("WRITE: No space in Q\n"
+ "len[%d] free_space[%d] head[%u] tail[%u] out_buff_size =%d\n",
+ len, free_space, head, tail, device->out_buff_size);
+ return -EINVAL;
+ }
+
+ pr_debug("WRITE: len[%d] free_space[%d] head[%u] tail[%u] out_buff_size =%d\n",
+ len, free_space, head, tail, device->out_buff_size);
+
+ if (head < tail) {
+ /* +++++++++ head ---------- tail ++++++++++ */
+ memcpy((device->out_buff_addr + head), buf, len);
+ } else {
+ /* ------ tail +++++++++++ head ------------ */
+ last_size = device->out_buff_size - head;
+ memcpy((device->out_buff_addr + head), buf,
+ len > last_size ? last_size : len);
+ if (len > last_size) {
+ memcpy(device->out_buff_addr, (buf + last_size),
+ (len - last_size));
+ }
+ }
+
+ /* Update new head */
+ head = (u16)((head + len) % device->out_buff_size);
+ dpram_writeh(head, &device->out->head);
+
+ irq_mask = INT_MASK_VALID;
+
+ if (len > 0)
+ irq_mask |= device->mask_send;
+
+ dpram_write_command(dpld, irq_mask);
+
+ return len;
+}
+
+static void dpram_write_work(struct work_struct *work)
+{
+ struct link_device *ld =
+ container_of(work, struct link_device, tx_delayed_work.work);
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ struct dpram_device *device;
+ struct sk_buff *skb;
+ bool reschedule = false;
+ int ret;
+
+ device = &dpld->dev_map[FMT_IDX];
+ while ((skb = skb_dequeue(&ld->sk_fmt_tx_q))) {
+ ret = dpram_write(dpld, device, skb->data, skb->len);
+ if (ret < 0) {
+ skb_queue_head(&ld->sk_fmt_tx_q, skb);
+ reschedule = true;
+ break;
+ }
+ dev_kfree_skb_any(skb);
+ }
+
+ device = &dpld->dev_map[RAW_IDX];
+ while ((skb = skb_dequeue(&ld->sk_raw_tx_q))) {
+ ret = dpram_write(dpld, device, skb->data, skb->len);
+ if (ret < 0) {
+ skb_queue_head(&ld->sk_raw_tx_q, skb);
+ reschedule = true;
+ break;
+ }
+ dev_kfree_skb_any(skb);
+ }
+
+ if (reschedule)
+ schedule_delayed_work(&ld->tx_delayed_work,
+ msecs_to_jiffies(10));
+}
+
+static int dpram_send(struct link_device *ld, struct io_device *iod,
+ struct sk_buff *skb)
+{
+ int len = skb->len;
+ pr_debug("%s: iod->format = %d\n", __func__, iod->format);
+
+ switch (iod->format) {
+ case IPC_FMT:
+ skb_queue_tail(&ld->sk_fmt_tx_q, skb);
+ break;
+
+ case IPC_RAW:
+ skb_queue_tail(&ld->sk_raw_tx_q, skb);
+ break;
+
+ case IPC_BOOT:
+ case IPC_RFS:
+ default:
+ dev_kfree_skb_any(skb);
+ return 0;
+ }
+
+ schedule_delayed_work(&ld->tx_delayed_work, 0);
+ return len;
+}
+
+static void dpram_table_init(struct dpram_link_device *dpld)
+{
+ struct dpram_device *dev;
+ struct dpram_map __iomem *dpram = dpld->dpram;
+
+ dev = &dpld->dev_map[FMT_IDX];
+ dev->in = &dpram->fmt_in;
+ dev->in_buff_addr = dpram->fmt_in_buff;
+ dev->in_buff_size = DP_FMT_IN_BUFF_SIZE;
+ dev->out = &dpram->fmt_out;
+ dev->out_buff_addr = dpram->fmt_out_buff;
+ dev->out_buff_size = DP_FMT_OUT_BUFF_SIZE;
+ dev->mask_req_ack = INT_MASK_REQ_ACK_F;
+ dev->mask_res_ack = INT_MASK_RES_ACK_F;
+ dev->mask_send = INT_MASK_SEND_F;
+
+ dev = &dpld->dev_map[RAW_IDX];
+ dev->in = &dpram->raw_in;
+ dev->in_buff_addr = dpram->raw_in_buff;
+ dev->in_buff_size = DP_RAW_IN_BUFF_SIZE;
+ dev->out = &dpram->raw_out;
+ dev->out_buff_addr = dpram->raw_out_buff;
+ dev->out_buff_size = DP_RAW_OUT_BUFF_SIZE;
+ dev->mask_req_ack = INT_MASK_REQ_ACK_R;
+ dev->mask_res_ack = INT_MASK_RES_ACK_R;
+ dev->mask_send = INT_MASK_SEND_R;
+}
+
+static int dpram_set_dlmagic(struct link_device *ld, struct io_device *iod)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ dpram_writew(DP_MAGIC_DMDL, &dpld->dpram->magic);
+ return 0;
+}
+
+static int dpram_set_ulmagic(struct link_device *ld, struct io_device *iod)
+{
+ struct dpram_link_device *dpld = to_dpram_link_device(ld);
+ struct dpram_map *dpram = (void *)dpld->dpram;
+ u8 *dest;
+ dest = (u8 *)(&dpram->fmt_out);
+
+ dpram_writew(DP_MAGIC_UMDL, &dpld->dpram->magic);
+
+ dpram_writeb((u8)START_INDEX, dest + 0);
+ dpram_writeb((u8)0x1, dest + 1);
+ dpram_writeb((u8)0x1, dest + 2);
+ dpram_writeb((u8)0x0, dest + 3);
+ dpram_writeb((u8)END_INDEX, dest + 4);
+
+ init_completion(&dpld->gota_download_start_complete);
+ dpram_write_command(dpld, CMD_DL_START_REQ);
+
+ return 0;
+}
+
+static int
+dpram_download(struct dpram_link_device *dpld, const char *buf, int len)
+{
+ struct dpram_map *dpram = (void *)dpld->dpram;
+ struct dpram_ota_header header;
+ u16 nframes;
+ u16 curframe = 1;
+ u16 plen;
+ u8 *dest;
+ int ret;
+
+ nframes = DIV_ROUND_UP(len, DP_DEFAULT_WRITE_LEN);
+
+ pr_debug("[GOTA] download len = %d\n", len);
+
+ header.start_index = START_INDEX;
+ header.nframes = nframes;
+
+ while (len > 0) {
+ plen = min(len, DP_DEFAULT_WRITE_LEN);
+ dest = (u8 *)&dpram->fmt_out;
+
+ pr_debug("[GOTA] Start write frame %d/%d\n", curframe, nframes);
+
+ header.curframe = curframe;
+ header.len = plen;
+
+ memcpy(dest, &header, sizeof(header));
+ dest += sizeof(header);
+
+ memcpy(dest, buf, plen);
+ dest += plen;
+ buf += plen;
+ len -= plen;
+
+ dpram_writeb(END_INDEX, dest+3);
+
+ init_completion(&dpld->gota_send_done);
+
+ if (curframe == 1) {
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->gota_download_start_complete,
+ GOTA_TIMEOUT);
+ if (!ret) {
+ pr_err("[GOTA] CP didn't send DOWNLOAD_START\n");
+ return -ENXIO;
+ }
+ }
+
+ dpram_write_command(dpld, CMD_IMG_SEND_REQ);
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->gota_send_done, GOTA_SEND_TIMEOUT);
+ if (!ret) {
+ pr_err("[GOTA] CP didn't send SEND_DONE_RESP\n");
+ return -ENXIO;
+ }
+
+ curframe++;
+ }
+
+ dpram_write_command(dpld, CMD_DL_SEND_DONE_REQ);
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->gota_update_done, GOTA_TIMEOUT);
+ if (!ret) {
+ pr_err("[GOTA] CP didn't send UPDATE_DONE_NOTIFICATION\n");
+ return -ENXIO;
+ }
+
+ return 0;
+}
+
+static int
+dpram_upload(struct dpram_link_device *dpld, struct dpram_firmware *uploaddata)
+{
+ struct dpram_map *dpram = (void *)dpld->dpram;
+ struct ul_header header;
+ u8 *dest;
+ u8 *buff = vmalloc(DP_DEFAULT_DUMP_LEN);
+ u16 plen = 0;
+ u32 tlen = 0;
+ int ret;
+ int region = 0;
+
+ pr_debug("[DPRAM] dpram_upload()\n");
+
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->gota_download_start_complete,
+ DUMP_START_TIMEOUT);
+ if (!ret) {
+ pr_err("[GOTA] CP didn't send DOWNLOAD_START\n");
+ goto err_out;
+ }
+
+ wake_lock(&dpld->dpram_wake_lock);
+
+ memset(buff, 0, DP_DEFAULT_DUMP_LEN);
+
+ dpram_write_command(dpld, CMD_IMG_SEND_REQ);
+ pr_debug("[DPRAM] write CMD_IMG_SEND_REQ(0x9400)\n");
+
+ while (1) {
+ init_completion(&dpld->dump_receive_done);
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->dump_receive_done, DUMP_TIMEOUT);
+ if (!ret) {
+ pr_err("[DPRAM] CP didn't send DATA_SEND_DONE_RESP\n");
+ goto err_out;
+ }
+
+ dest = (u8 *)(&dpram->fmt_out);
+
+ header.bop = *(u8 *)(dest);
+ header.total_frame = *(u16 *)(dest + 1);
+ header.curr_frame = *(u16 *)(dest + 3);
+ header.len = *(u16 *)(dest + 5);
+
+ pr_debug("total frame:%d, current frame:%d, data len:%d\n",
+ header.total_frame, header.curr_frame,
+ header.len);
+
+ dest += DP_DUMP_HEADER_SIZE;
+ plen = min(header.len, (u16)DP_DEFAULT_DUMP_LEN);
+
+ memcpy(buff, dest, plen);
+ dest += plen;
+
+ ret = copy_to_user(uploaddata->firmware + tlen, buff, plen);
+ if (ret < 0) {
+ pr_err("[DPRAM] Copy to user failed\n");
+ goto err_out;
+ }
+
+ tlen += plen;
+
+ if (header.total_frame == header.curr_frame) {
+ if (region) {
+ uploaddata->is_delta = tlen - uploaddata->size;
+ dpram_write_command(dpld, CMD_UL_RECEIVE_RESP);
+ break;
+ } else {
+ uploaddata->size = tlen;
+ region = 1;
+ }
+ }
+ dpram_write_command(dpld, CMD_UL_RECEIVE_RESP);
+ }
+
+ pr_debug("1st dump region data size=%d\n", uploaddata->size);
+ pr_debug("2st dump region data size=%d\n", uploaddata->is_delta);
+
+ init_completion(&dpld->gota_send_done);
+ ret = wait_for_completion_interruptible_timeout(
+ &dpld->gota_send_done, DUMP_TIMEOUT);
+ if (!ret) {
+ pr_err("[GOTA] CP didn't send SEND_DONE_RESP\n");
+ goto err_out;
+ }
+
+ dpram_write_command(dpld, CMD_UL_RECEIVE_DONE_RESP);
+ pr_debug("[DPRAM] write CMD_UL_RECEIVE_DONE_RESP(0x9801)\n");
+
+ dpram_writew(0, &dpld->dpram->magic); /*clear magic code */
+
+ wake_unlock(&dpld->dpram_wake_lock);
+
+ vfree(buff);
+ return 0;
+
+err_out:
+ vfree(buff);
+ dpram_writew(0, &dpld->dpram->magic);
+ pr_err("CDMA dump error out\n");
+ wake_unlock(&dpld->dpram_wake_lock);
+ return -EIO;
+}
+
+struct link_device *dpram_create_link_device(struct platform_device *pdev)
+{
+ int ret;
+ struct dpram_link_device *dpld;
+ struct link_device *ld;
+ struct resource *res;
+
+ BUILD_BUG_ON(sizeof(struct dpram_map) != DP_DPRAM_SIZE);
+
+ dpld = kzalloc(sizeof(struct dpram_link_device), GFP_KERNEL);
+ if (!dpld)
+ return NULL;
+
+ ld = &dpld->ld;
+
+ INIT_LIST_HEAD(&dpld->list_of_io_devices);
+ skb_queue_head_init(&ld->sk_fmt_tx_q);
+ skb_queue_head_init(&ld->sk_raw_tx_q);
+ INIT_DELAYED_WORK(&ld->tx_delayed_work, dpram_write_work);
+
+ wake_lock_init(&dpld->dpram_wake_lock, WAKE_LOCK_SUSPEND, "DPRAM");
+
+ init_completion(&dpld->modem_pif_init_done);
+ init_completion(&dpld->dpram_init_cmd);
+ init_completion(&dpld->gota_send_done);
+ init_completion(&dpld->gota_update_done);
+ init_completion(&dpld->gota_download_start_complete);
+ init_completion(&dpld->dump_receive_done);
+
+ ld->name = "dpram";
+ ld->attach = dpram_attach_io_dev;
+ ld->send = dpram_send;
+ ld->gota_start = dpram_set_dlmagic;
+ ld->modem_update = dpram_modem_update;
+ ld->dump_start = dpram_set_ulmagic;
+ ld->dump_update = dpram_dump_update;
+
+ dpld->clear_interrupt = dpram_clear_interrupt;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ pr_err("[DPRAM] Failed to get mem region\n");
+ goto err;
+ }
+ dpld->dpram = ioremap(res->start, resource_size(res));
+
+ dpld->irq = platform_get_irq(pdev, 1);
+ if (!dpld->irq) {
+ pr_err("[MODEM_IF] %s: Failed to get IRQ\n", __func__);
+ goto err;
+ }
+
+ dpram_table_init(dpld);
+
+ atomic_set(&dpld->raw_txq_req_ack_rcvd, 0);
+ atomic_set(&dpld->fmt_txq_req_ack_rcvd, 0);
+
+ dpram_clear_interrupt(dpld);
+ dpram_writeh(0, &dpld->dpram->magic);
+
+ ret = request_irq(dpld->irq, dpram_irq_handler, IRQ_TYPE_LEVEL_LOW,
+ "dpram irq", ld);
+ if (ret) {
+ pr_err("DPRAM interrupt handler failed\n");
+ goto err;
+ }
+ enable_irq_wake(dpld->irq);
+
+ return ld;
+
+err:
+ iounmap(dpld->dpram);
+ kfree(dpld);
+ return NULL;
+}
diff --git a/drivers/misc/modem_if/modem_link_device_dpram.h b/drivers/misc/modem_if/modem_link_device_dpram.h
new file mode 100755
index 0000000..3af6d90
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_dpram.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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/wakelock.h>
+
+#ifndef __MODEM_LINK_DEVICE_DPRAM_H__
+#define __MODEM_LINK_DEVICE_DPRAM_H__
+
+#define FMT_IDX 0
+#define RAW_IDX 1
+#define MAX_IDX 2
+
+#define DP_FMT_OUT_BUFF_SIZE 2044
+#define DP_RAW_OUT_BUFF_SIZE 6128
+#define DP_FMT_IN_BUFF_SIZE 2044
+#define DP_RAW_IN_BUFF_SIZE 6128
+
+struct dpram_circ {
+ u16 head;
+ u16 tail;
+};
+
+struct dpram_ota_header {
+ u8 start_index;
+ u16 nframes;
+ u16 curframe;
+ u16 len;
+
+} __packed;
+
+struct dpram_map {
+ u16 magic;
+ u16 enable;
+
+ struct dpram_circ fmt_out;
+ u8 fmt_out_buff[DP_FMT_OUT_BUFF_SIZE];
+
+ struct dpram_circ raw_out;
+ u8 raw_out_buff[DP_RAW_OUT_BUFF_SIZE];
+
+ struct dpram_circ fmt_in;
+ u8 fmt_in_buff[DP_FMT_IN_BUFF_SIZE];
+
+ struct dpram_circ raw_in;
+ u8 raw_in_buff[DP_RAW_IN_BUFF_SIZE];
+
+ u8 padding[16];
+ u16 mbx_cp2ap;
+ u16 mbx_ap2cp;
+
+} __packed;
+
+struct dpram_device {
+ struct dpram_circ __iomem *in;
+ u8 __iomem *in_buff_addr;
+ int in_buff_size;
+
+ struct dpram_circ __iomem *out;
+ u8 __iomem *out_buff_addr;
+ int out_buff_size;
+
+ u16 mask_req_ack;
+ u16 mask_res_ack;
+ u16 mask_send;
+};
+
+struct ul_header {
+ u8 bop;
+ u16 total_frame;
+ u16 curr_frame;
+ u16 len;
+};
+
+struct dpram_link_device {
+ struct link_device ld;
+
+ /* maybe -list of io devices for the link device to use
+ * to find where to send incoming packets to */
+ struct list_head list_of_io_devices;
+
+ atomic_t raw_txq_req_ack_rcvd;
+ atomic_t fmt_txq_req_ack_rcvd;
+
+ struct dpram_map __iomem *dpram;
+ struct dpram_device dev_map[MAX_IDX];
+
+ struct wake_lock dpram_wake_lock;
+
+ struct completion dpram_init_cmd;
+ struct completion modem_pif_init_done;
+ struct completion gota_download_start_complete;
+ struct completion gota_send_done;
+ struct completion gota_update_done;
+ struct completion dump_receive_done;
+
+ int irq;
+ void (*clear_interrupt)(struct dpram_link_device *);
+};
+
+/* converts from struct link_device* to struct xxx_link_device* */
+#define to_dpram_link_device(linkdev) \
+ container_of(linkdev, struct dpram_link_device, ld)
+
+#endif
diff --git a/drivers/misc/modem_if/modem_link_device_mipi.c b/drivers/misc/modem_if/modem_link_device_mipi.c
new file mode 100755
index 0000000..3150cd2
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_mipi.c
@@ -0,0 +1,1694 @@
+/* /linux/drivers/new_modem_if/link_dev_mipi.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/poll.h>
+#include <linux/gpio.h>
+#include <linux/if_arp.h>
+#include <linux/wakelock.h>
+
+#include <linux/hsi_driver_if.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include "modem_link_device_mipi.h"
+
+
+static int mipi_hsi_attach_io_dev(struct link_device *ld,
+ struct io_device *iod)
+{
+ struct mipi_link_device *mipi_ld = to_mipi_link_device(ld);
+
+ iod->link = ld;
+
+ /* list up io devices */
+ list_add_tail(&iod->list, &mipi_ld->list_of_io_devices);
+ return 0;
+}
+
+static int mipi_hsi_init_communication(struct link_device *ld,
+ struct io_device *iod)
+{
+ struct mipi_link_device *mipi_ld = to_mipi_link_device(ld);
+
+ switch (iod->format) {
+ case IPC_FMT:
+ return hsi_init_handshake(mipi_ld, HSI_INIT_MODE_NORMAL);
+
+ case IPC_BOOT:
+ if (iod->id == 0x0)
+ return hsi_init_handshake(mipi_ld,
+ HSI_INIT_MODE_FLASHLESS_BOOT);
+
+ return hsi_init_handshake(mipi_ld,
+ HSI_INIT_MODE_FLASHLESS_BOOT_EBL);
+
+ case IPC_RAMDUMP:
+ return hsi_init_handshake(mipi_ld,
+ HSI_INIT_MODE_CP_RAMDUMP);
+
+ case IPC_RFS:
+ case IPC_RAW:
+ default:
+ return 0;
+ }
+}
+
+static void mipi_hsi_terminate_communication(
+ struct link_device *ld, struct io_device *iod)
+{
+ struct mipi_link_device *mipi_ld = to_mipi_link_device(ld);
+
+ switch (iod->format) {
+ case IPC_BOOT:
+ if (&mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].opened)
+ if_hsi_close_channel(&mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL]);
+ if (wake_lock_active(&mipi_ld->wlock))
+ wake_unlock(&mipi_ld->wlock);
+ break;
+
+ case IPC_RAMDUMP:
+ if (&mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL].opened)
+ if_hsi_close_channel(&mipi_ld->hsi_channles[
+ HSI_CP_RAMDUMP_CHANNEL]);
+ if (wake_lock_active(&mipi_ld->wlock))
+ wake_unlock(&mipi_ld->wlock);
+ break;
+
+ case IPC_FMT:
+ case IPC_RFS:
+ case IPC_RAW:
+ default:
+ break;
+ }
+}
+
+static int mipi_hsi_send(struct link_device *ld, struct io_device *iod,
+ struct sk_buff *skb)
+{
+ int ret;
+ struct mipi_link_device *mipi_ld = to_mipi_link_device(ld);
+
+ struct sk_buff_head *txq;
+
+ switch (iod->format) {
+ case IPC_RAW:
+ txq = &ld->sk_raw_tx_q;
+ break;
+
+ case IPC_RAMDUMP:
+ ret = if_hsi_write(&mipi_ld->hsi_channles[
+ HSI_CP_RAMDUMP_CHANNEL],
+ (u32 *)skb->data, skb->len);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] write fail : %d\n", ret);
+ dev_kfree_skb_any(skb);
+ return ret;
+ } else
+ pr_debug("[MIPI-HSI] write Done\n");
+ dev_kfree_skb_any(skb);
+ return ret;
+
+ case IPC_BOOT:
+ ret = if_hsi_write(&mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL],
+ (u32 *)skb->data, skb->len);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] write fail : %d\n", ret);
+ dev_kfree_skb_any(skb);
+ return ret;
+ } else
+ pr_debug("[MIPI-HSI] write Done\n");
+ dev_kfree_skb_any(skb);
+ return ret;
+
+ case IPC_FMT:
+ case IPC_RFS:
+ default:
+ txq = &ld->sk_fmt_tx_q;
+ break;
+ }
+
+ /* save io device into cb area */
+ *((struct io_device **)skb->cb) = iod;
+ /* en queue skb data */
+ skb_queue_tail(txq, skb);
+
+ if (iod->format == IPC_RAW)
+ queue_delayed_work(ld->tx_raw_wq, &ld->tx_delayed_work, 0);
+ else
+ queue_work(ld->tx_wq, &ld->tx_work);
+ return skb->len;
+}
+
+static void mipi_hsi_tx_work(struct work_struct *work)
+{
+ int ret;
+ struct link_device *ld = container_of(work, struct link_device,
+ tx_work);
+ struct mipi_link_device *mipi_ld = to_mipi_link_device(ld);
+ struct io_device *iod;
+ struct sk_buff *fmt_skb;
+ int send_channel = 0;
+
+ while (ld->sk_fmt_tx_q.qlen) {
+ pr_debug("[MIPI-HSI] fmt qlen : %d\n", ld->sk_fmt_tx_q.qlen);
+
+ if (ld->com_state != COM_ONLINE) {
+ pr_debug("[MIPI-HSI] fmt CP not ready\n");
+ return;
+ }
+
+ fmt_skb = skb_dequeue(&ld->sk_fmt_tx_q);
+ if (fmt_skb) {
+ iod = *((struct io_device **)fmt_skb->cb);
+
+ pr_debug("[MIPI-HSI] dequeue. fmt qlen : %d\n",
+ ld->sk_fmt_tx_q.qlen);
+
+ switch (iod->format) {
+ case IPC_FMT:
+ send_channel = HSI_FMT_CHANNEL;
+ break;
+
+ case IPC_RFS:
+ send_channel = HSI_RFS_CHANNEL;
+ break;
+
+ case IPC_BOOT:
+ send_channel = HSI_FLASHLESS_CHANNEL;
+ break;
+
+ case IPC_RAMDUMP:
+ send_channel = HSI_CP_RAMDUMP_CHANNEL;
+ break;
+
+ default:
+ break;
+ }
+ ret = if_hsi_protocol_send(mipi_ld, send_channel,
+ (u32 *)fmt_skb->data, fmt_skb->len);
+ if (ret < 0) {
+ /* TODO: Re Enqueue */
+ pr_err("[MIPI-HSI] write fail : %d\n", ret);
+ } else
+ pr_debug("[MIPI-HSI] write Done\n");
+
+ dev_kfree_skb_any(fmt_skb);
+ }
+ }
+}
+
+static void mipi_hsi_tx_raw_work(struct work_struct *work)
+{
+ int ret;
+ struct link_device *ld = container_of(work, struct link_device,
+ tx_delayed_work.work);
+ struct mipi_link_device *mipi_ld = to_mipi_link_device(ld);
+ struct sk_buff *raw_skb;
+ unsigned bulk_size;
+
+ while (ld->sk_raw_tx_q.qlen) {
+ pr_debug("[MIPI-HSI] raw qlen:%d\n", ld->sk_raw_tx_q.qlen);
+
+ if (ld->com_state != COM_ONLINE) {
+ pr_debug("[MIPI-HSI] raw CP not ready\n");
+ return;
+ }
+
+ bulk_size = 0;
+ raw_skb = skb_dequeue(&ld->sk_raw_tx_q);
+ while (raw_skb) {
+ if (bulk_size + raw_skb->len < MIPI_BULK_TX_SIZE) {
+ memcpy(mipi_ld->bulk_tx_buf + bulk_size,
+ raw_skb->data, raw_skb->len);
+ bulk_size += raw_skb->len;
+ skb_queue_head(&mipi_ld->bulk_txq, raw_skb);
+ } else {
+ skb_queue_head(&ld->sk_raw_tx_q, raw_skb);
+ break;
+ }
+ raw_skb = skb_dequeue(&ld->sk_raw_tx_q);
+ }
+
+ ret = if_hsi_protocol_send(mipi_ld, HSI_RAW_CHANNEL,
+ (u32 *)mipi_ld->bulk_tx_buf, bulk_size);
+ if (ret < 0) {
+ raw_skb = skb_dequeue(&mipi_ld->bulk_txq);
+ while (raw_skb) {
+ skb_queue_head(&ld->sk_raw_tx_q, raw_skb);
+ raw_skb = skb_dequeue(&mipi_ld->bulk_txq);
+ }
+ } else
+ skb_queue_purge(&mipi_ld->bulk_txq);
+ }
+}
+
+static int __devinit if_hsi_probe(struct hsi_device *dev);
+static struct hsi_device_driver if_hsi_driver = {
+ .ctrl_mask = ANY_HSI_CONTROLLER,
+ .probe = if_hsi_probe,
+ .driver = {
+ .name = "if_hsi_driver"
+ },
+};
+
+static int if_hsi_set_wakeline(struct if_hsi_channel *channel,
+ unsigned int state)
+{
+ int ret;
+
+ spin_lock_bh(&channel->acwake_lock);
+ if (channel->acwake == state) {
+ spin_unlock_bh(&channel->acwake_lock);
+ return 0;
+ }
+
+ ret = hsi_ioctl(channel->dev, state ?
+ HSI_IOCTL_ACWAKE_UP : HSI_IOCTL_ACWAKE_DOWN, NULL);
+ if (ret) {
+ if (ret != -EPERM)
+ pr_err("[MIPI-HSI] ACWAKE(%d) setting fail : %d\n",
+ state, ret);
+ /* duplicate operation */
+ if (ret == -EPERM)
+ channel->acwake = state;
+ spin_unlock_bh(&channel->acwake_lock);
+ return ret;
+ }
+
+ channel->acwake = state;
+ spin_unlock_bh(&channel->acwake_lock);
+
+ pr_debug("[MIPI-HSI] ACWAKE_%d(%d)\n", channel->channel_id, state);
+ return 0;
+}
+
+static void if_hsi_acwake_down_func(unsigned long data)
+{
+ int i;
+ struct if_hsi_channel *channel;
+ struct mipi_link_device *mipi_ld = (struct mipi_link_device *)data;
+
+ pr_debug("[MIPI-HSI] %s\n", __func__);
+
+ for (i = 0; i < HSI_NUM_OF_USE_CHANNELS; i++) {
+ channel = &mipi_ld->hsi_channles[i];
+
+ if ((channel->send_step == STEP_IDLE) &&
+ (channel->recv_step == STEP_IDLE)) {
+ if_hsi_set_wakeline(channel, 0);
+ } else {
+ mod_timer(&mipi_ld->hsi_acwake_down_timer, jiffies +
+ HSI_ACWAKE_DOWN_TIMEOUT);
+ pr_debug("[MIPI-HSI] mod_timer done(%d)\n",
+ HSI_ACWAKE_DOWN_TIMEOUT);
+ return;
+ }
+ }
+}
+
+static int if_hsi_open_channel(struct if_hsi_channel *channel)
+{
+ int ret;
+
+ if (channel->opened) {
+ pr_debug("[MIPI-HSI] channel %d is already opened\n",
+ channel->channel_id);
+ return 0;
+ }
+
+ ret = hsi_open(channel->dev);
+ if (ret) {
+ pr_err("[MIPI-HSI] hsi_open fail : %d\n", ret);
+ if (ret == -EBUSY)
+ pr_err("[MIPI-HSI] ch %d already opened\n",
+ channel->channel_id);
+ else
+ return ret;
+ }
+ channel->opened = 1;
+
+ pr_debug("[MIPI-HSI] hsi_open Done : %d\n", channel->channel_id);
+ return 0;
+}
+
+static int if_hsi_close_channel(struct if_hsi_channel *channel)
+{
+ unsigned long int flags;
+
+ if (!channel->opened) {
+ pr_debug("[MIPI-HSI] channel %d is already closed\n",
+ channel->channel_id);
+ return 0;
+ }
+
+ if_hsi_set_wakeline(channel, 0);
+ hsi_write_cancel(channel->dev);
+ hsi_read_cancel(channel->dev);
+
+ spin_lock_irqsave(&channel->tx_state_lock, flags);
+ channel->tx_state &= ~HSI_CHANNEL_TX_STATE_WRITING;
+ spin_unlock_irqrestore(&channel->tx_state_lock, flags);
+ spin_lock_irqsave(&channel->rx_state_lock, flags);
+ channel->rx_state &= ~HSI_CHANNEL_RX_STATE_READING;
+ spin_unlock_irqrestore(&channel->rx_state_lock, flags);
+
+ hsi_close(channel->dev);
+ channel->opened = 0;
+
+ channel->send_step = STEP_CLOSED;
+ channel->recv_step = STEP_CLOSED;
+
+ pr_debug("[MIPI-HSI] hsi_close Done : %d\n", channel->channel_id);
+ return 0;
+}
+
+static void mipi_hsi_start_work(struct work_struct *work)
+{
+ int ret;
+ u32 start_cmd = 0xC2;
+ struct mipi_link_device *mipi_ld =
+ container_of(work, struct mipi_link_device,
+ start_work.work);
+
+ mipi_ld->ld.com_state = COM_HANDSHAKE;
+ ret = if_hsi_protocol_send(mipi_ld, HSI_CMD_CHANNEL, &start_cmd, 1);
+ if (ret < 0) {
+ /* TODO: Re Enqueue */
+ pr_err("[MIPI-HSI] First write fail : %d\n", ret);
+ } else {
+ pr_debug("[MIPI-HSI] First write Done : %d\n", ret);
+ mipi_ld->ld.com_state = COM_ONLINE;
+ }
+}
+
+static int hsi_init_handshake(struct mipi_link_device *mipi_ld, int mode)
+{
+ int ret;
+ int i;
+ struct hst_ctx tx_config;
+ struct hsr_ctx rx_config;
+
+ switch (mode) {
+ case HSI_INIT_MODE_NORMAL:
+ if (timer_pending(&mipi_ld->hsi_acwake_down_timer))
+ del_timer(&mipi_ld->hsi_acwake_down_timer);
+
+ for (i = 0; i < HSI_NUM_OF_USE_CHANNELS; i++) {
+ if (mipi_ld->hsi_channles[i].opened) {
+ hsi_write_cancel(mipi_ld->hsi_channles[i].dev);
+ hsi_read_cancel(mipi_ld->hsi_channles[i].dev);
+ } else {
+ ret = if_hsi_open_channel(
+ &mipi_ld->hsi_channles[i]);
+ if (ret)
+ return ret;
+ }
+ mipi_ld->hsi_channles[i].send_step = STEP_IDLE;
+ mipi_ld->hsi_channles[i].recv_step = STEP_IDLE;
+
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_GET_TX, &tx_config);
+ tx_config.mode = 2;
+ tx_config.divisor = 0; /* Speed : 96MHz */
+ tx_config.channels = HSI_MAX_CHANNELS;
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_SET_TX, &tx_config);
+
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_GET_RX, &rx_config);
+ rx_config.mode = 2;
+ rx_config.divisor = 0; /* Speed : 96MHz */
+ rx_config.channels = HSI_MAX_CHANNELS;
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_SET_RX, &rx_config);
+ pr_debug("[MIPI-HSI] Set TX/RX MIPI-HSI\n");
+ }
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].dev,
+ HSI_IOCTL_SET_WAKE_RX_4WIRES_MODE, NULL);
+ pr_debug("[MIPI-HSI] Set 4 WIRE MODE\n");
+
+ if (mipi_ld->ld.com_state != COM_ONLINE)
+ mipi_ld->ld.com_state = COM_HANDSHAKE;
+
+ ret = hsi_read(mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].dev,
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].rx_data,
+ 1);
+ if (ret)
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+
+ if (mipi_ld->ld.com_state != COM_ONLINE)
+ schedule_delayed_work(&mipi_ld->start_work, 3 * HZ);
+
+ pr_debug("[MIPI-HSI] hsi_init_handshake Done : MODE_NORMAL\n");
+ return 0;
+
+ case HSI_INIT_MODE_FLASHLESS_BOOT:
+ mipi_ld->ld.com_state = COM_BOOT;
+
+ if (mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].opened) {
+ hsi_ioctl(mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL].dev, HSI_IOCTL_SW_RESET,
+ NULL);
+ for (i = 0; i < HSI_NUM_OF_USE_CHANNELS; i++)
+ mipi_ld->hsi_channles[i].opened = 0;
+ }
+
+ if (!mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].opened)
+ if_hsi_open_channel(
+ &mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL]);
+ mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].send_step
+ = STEP_IDLE;
+ mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].recv_step
+ = STEP_IDLE;
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_GET_TX, &tx_config);
+ tx_config.mode = 2;
+ tx_config.divisor = 3; /* Speed : 24MHz */
+ tx_config.channels = 1;
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_SET_TX, &tx_config);
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_GET_RX, &rx_config);
+ rx_config.mode = 2;
+ rx_config.divisor = 3; /* Speed : 24MHz */
+ rx_config.channels = 1;
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_SET_RX, &rx_config);
+ pr_debug("[MIPI-HSI] Set TX/RX MIPI-HSI\n");
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_SET_WAKE_RX_3WIRES_MODE, NULL);
+ pr_debug("[MIPI-HSI] Set 3 WIRE MODE\n");
+
+ if (!wake_lock_active(&mipi_ld->wlock)) {
+ wake_lock(&mipi_ld->wlock);
+ pr_debug("[MIPI-HSI] wake_lock\n");
+ }
+
+ ret = hsi_read(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].rx_data, 1);
+ if (ret)
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+
+ pr_debug("[MIPI-HSI] hsi_init_handshake Done : FLASHLESS_BOOT\n");
+ return 0;
+
+ case HSI_INIT_MODE_FLASHLESS_BOOT_EBL:
+ mipi_ld->ld.com_state = COM_BOOT_EBL;
+
+ if (mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].opened) {
+ hsi_ioctl(mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL].dev, HSI_IOCTL_SW_RESET,
+ NULL);
+ for (i = 0; i < HSI_NUM_OF_USE_CHANNELS; i++)
+ mipi_ld->hsi_channles[i].opened = 0;
+ }
+
+ if (!mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].opened)
+ if_hsi_open_channel(
+ &mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL]);
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_GET_TX, &tx_config);
+ tx_config.mode = 2;
+ tx_config.divisor = 0; /* Speed : 96MHz */
+ tx_config.channels = 1;
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_SET_TX, &tx_config);
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_GET_RX, &rx_config);
+ rx_config.mode = 2;
+ rx_config.divisor = 0; /* Speed : 96MHz */
+ rx_config.channels = 1;
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_SET_RX, &rx_config);
+ pr_debug("[MIPI-HSI] Set TX/RX MIPI-HSI\n");
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_SET_WAKE_RX_4WIRES_MODE, NULL);
+ pr_debug("[MIPI-HSI] Set 4 WIRE MODE\n");
+
+ if (!wake_lock_active(&mipi_ld->wlock)) {
+ wake_lock(&mipi_ld->wlock);
+ pr_debug("[MIPI-HSI] wake_lock\n");
+ }
+
+ if_hsi_set_wakeline(
+ &mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL], 1);
+
+ ret = hsi_read(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].rx_data, 1);
+ if (ret)
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+
+ pr_debug("[MIPI-HSI] hsi_init_handshake Done : FLASHLESS_BOOT_EBL\n");
+ return 0;
+
+ case HSI_INIT_MODE_CP_RAMDUMP:
+ mipi_ld->ld.com_state = COM_CRASH;
+
+ if (mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL].opened) {
+ hsi_ioctl(mipi_ld->hsi_channles[
+ HSI_CP_RAMDUMP_CHANNEL].dev, HSI_IOCTL_SW_RESET,
+ NULL);
+ for (i = 0; i < HSI_NUM_OF_USE_CHANNELS; i++)
+ mipi_ld->hsi_channles[i].opened = 0;
+ }
+
+ if (!mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL].opened)
+ if_hsi_open_channel(
+ &mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL]);
+ mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL].send_step
+ = STEP_IDLE;
+ mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL].recv_step
+ = STEP_IDLE;
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL].dev,
+ HSI_IOCTL_GET_TX, &tx_config);
+ tx_config.mode = 2;
+ tx_config.divisor = 0; /* Speed : 96MHz */
+ tx_config.channels = 1;
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL].dev,
+ HSI_IOCTL_SET_TX, &tx_config);
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL].dev,
+ HSI_IOCTL_GET_RX, &rx_config);
+ rx_config.mode = 2;
+ rx_config.divisor = 0; /* Speed : 96MHz */
+ rx_config.channels = 1;
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL].dev,
+ HSI_IOCTL_SET_RX, &rx_config);
+ pr_debug("[MIPI-HSI] Set TX/RX MIPI-HSI\n");
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL].dev,
+ HSI_IOCTL_SET_WAKE_RX_4WIRES_MODE, NULL);
+ pr_debug("[MIPI-HSI] Set 4 WIRE MODE\n");
+
+ if (!wake_lock_active(&mipi_ld->wlock)) {
+ wake_lock(&mipi_ld->wlock);
+ pr_debug("[MIPI-HSI] wake_lock\n");
+ }
+
+ if_hsi_set_wakeline(
+ &mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL], 1);
+
+ ret = hsi_read(
+ mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL].dev,
+ mipi_ld->hsi_channles[HSI_CP_RAMDUMP_CHANNEL].rx_data,
+ DUMP_ERR_INFO_SIZE);
+ if (ret)
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+
+ pr_debug("[MIPI-HSI] hsi_init_handshake Done : RAMDUMP\n");
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void hsi_conn_err_recovery(struct mipi_link_device *mipi_ld)
+{
+ int i;
+ int ret;
+ struct hst_ctx tx_config;
+ struct hsr_ctx rx_config;
+
+ if (mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].opened) {
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].dev,
+ HSI_IOCTL_SW_RESET, NULL);
+ for (i = 0; i < HSI_NUM_OF_USE_CHANNELS; i++)
+ mipi_ld->hsi_channles[i].opened = 0;
+ }
+
+ for (i = 0; i < HSI_NUM_OF_USE_CHANNELS; i++) {
+ if (!mipi_ld->hsi_channles[i].opened)
+ if_hsi_open_channel(&mipi_ld->hsi_channles[i]);
+
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_GET_TX, &tx_config);
+ tx_config.mode = 2;
+ tx_config.divisor = 0; /* Speed : 96MHz */
+ tx_config.channels = HSI_MAX_CHANNELS;
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_SET_TX, &tx_config);
+
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_GET_RX, &rx_config);
+ rx_config.mode = 2;
+ rx_config.divisor = 0; /* Speed : 96MHz */
+ rx_config.channels = HSI_MAX_CHANNELS;
+ hsi_ioctl(mipi_ld->hsi_channles[i].dev,
+ HSI_IOCTL_SET_RX, &rx_config);
+ pr_debug("[MIPI-HSI] Set TX/RX MIPI-HSI\n");
+ }
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].dev,
+ HSI_IOCTL_SET_WAKE_RX_4WIRES_MODE, NULL);
+ pr_debug("[MIPI-HSI] Set 4 WIRE MODE\n");
+
+ ret = hsi_read(mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].dev,
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].rx_data, 1);
+ if (ret)
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+
+ for (i = 1; i < HSI_NUM_OF_USE_CHANNELS; i++) {
+ if ((mipi_ld->hsi_channles[i].recv_step == STEP_RX) &&
+ (mipi_ld->hsi_channles[i].rx_count)) {
+ pr_err("[MIPI-HSI] there was rx pending. ch:%d, len:%d",
+ i, mipi_ld->hsi_channles[i].rx_count);
+ ret = hsi_read(mipi_ld->hsi_channles[i].dev,
+ mipi_ld->hsi_channles[i].rx_data,
+ mipi_ld->hsi_channles[i].rx_count / 4);
+ if (ret)
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+ }
+ }
+
+ pr_debug("[MIPI-HSI] hsi_conn_err_recovery Done\n");
+}
+
+static u32 if_hsi_create_cmd(u32 cmd_type, int ch, void *arg)
+{
+ u32 cmd = 0;
+ unsigned int size = 0;
+
+ switch (cmd_type) {
+ case HSI_LL_MSG_BREAK:
+ return 0;
+
+ case HSI_LL_MSG_CONN_CLOSED:
+ cmd = ((HSI_LL_MSG_CONN_CLOSED & 0x0000000F) << 28)
+ |((ch & 0x000000FF) << 24);
+ return cmd;
+
+ case HSI_LL_MSG_ACK:
+ size = *(unsigned int *)arg;
+
+ cmd = ((HSI_LL_MSG_ACK & 0x0000000F) << 28)
+ |((ch & 0x000000FF) << 24) | ((size & 0x00FFFFFF));
+ return cmd;
+
+ case HSI_LL_MSG_NAK:
+ cmd = ((HSI_LL_MSG_NAK & 0x0000000F) << 28)
+ |((ch & 0x000000FF) << 24);
+ return cmd;
+
+ case HSI_LL_MSG_OPEN_CONN_OCTET:
+ size = *(unsigned int *)arg;
+
+ cmd = ((HSI_LL_MSG_OPEN_CONN_OCTET & 0x0000000F)
+ << 28) | ((ch & 0x000000FF) << 24)
+ | ((size & 0x00FFFFFF));
+ return cmd;
+
+ case HSI_LL_MSG_OPEN_CONN:
+ case HSI_LL_MSG_CONF_RATE:
+ case HSI_LL_MSG_CANCEL_CONN:
+ case HSI_LL_MSG_CONN_READY:
+ case HSI_LL_MSG_ECHO:
+ case HSI_LL_MSG_INFO_REQ:
+ case HSI_LL_MSG_INFO:
+ case HSI_LL_MSG_CONFIGURE:
+ case HSI_LL_MSG_ALLOCATE_CH:
+ case HSI_LL_MSG_RELEASE_CH:
+ case HSI_LL_MSG_INVALID:
+ default:
+ pr_err("[MIPI-HSI] ERROR... CMD Not supported : %08x\n",
+ cmd_type);
+ return -EINVAL;
+ }
+}
+
+static void if_hsi_cmd_work(struct work_struct *work)
+{
+ int ret;
+ int retry_count = 0;
+ unsigned long int flags;
+ struct mipi_link_device *mipi_ld =
+ container_of(work, struct mipi_link_device, cmd_work.work);
+ struct if_hsi_channel *channel =
+ &mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL];
+ struct if_hsi_command *hsi_cmd;
+
+ pr_debug("[MIPI-HSI] cmd_work\n");
+
+ do {
+ spin_lock_irqsave(&mipi_ld->list_cmd_lock, flags);
+ if (!list_empty(&mipi_ld->list_of_hsi_cmd)) {
+ hsi_cmd = list_entry(mipi_ld->list_of_hsi_cmd.next,
+ struct if_hsi_command, list);
+ list_del(&hsi_cmd->list);
+ spin_unlock_irqrestore(&mipi_ld->list_cmd_lock, flags);
+
+ channel->send_step = STEP_TX;
+ if_hsi_set_wakeline(channel, 1);
+ mod_timer(&mipi_ld->hsi_acwake_down_timer, jiffies +
+ HSI_ACWAKE_DOWN_TIMEOUT);
+ } else {
+ spin_unlock_irqrestore(&mipi_ld->list_cmd_lock, flags);
+ channel->send_step = STEP_IDLE;
+ break;
+ }
+ pr_debug("[MIPI-HSI] take command : %08x\n", hsi_cmd->command);
+
+ ret = if_hsi_write(channel, &hsi_cmd->command, 4);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] write command fail : %d\n", ret);
+
+ retry_count++;
+ if (retry_count > 5) {
+ channel->send_step = STEP_IDLE;
+ kfree(hsi_cmd);
+ return;
+ }
+
+ hsi_conn_err_recovery(mipi_ld);
+ channel->send_step = STEP_IDLE;
+
+ spin_lock_irqsave(&mipi_ld->list_cmd_lock, flags);
+ list_add(&hsi_cmd->list, &mipi_ld->list_of_hsi_cmd);
+ spin_unlock_irqrestore(&mipi_ld->list_cmd_lock, flags);
+
+ pr_err("[MIPI-HSI] retry write command : %d\n",
+ retry_count);
+ continue;
+ }
+ pr_debug("[MIPI-HSI] SEND CMD : %08x\n", hsi_cmd->command);
+
+ kfree(hsi_cmd);
+ } while (true);
+}
+
+static int if_hsi_send_command(struct mipi_link_device *mipi_ld,
+ u32 cmd_type, int ch, u32 param)
+{
+ unsigned long int flags;
+ struct if_hsi_command *hsi_cmd;
+
+ hsi_cmd = kmalloc(sizeof(struct if_hsi_command), GFP_ATOMIC);
+ if (!hsi_cmd) {
+ pr_err("[MIPI-HSI] hsi_cmd kmalloc fail\n");
+ return -ENOMEM;
+ }
+ INIT_LIST_HEAD(&hsi_cmd->list);
+
+ hsi_cmd->command = if_hsi_create_cmd(cmd_type, ch, &param);
+ pr_debug("[MIPI-HSI] made command : %08x\n", hsi_cmd->command);
+
+ spin_lock_irqsave(&mipi_ld->list_cmd_lock, flags);
+ list_add_tail(&hsi_cmd->list, &mipi_ld->list_of_hsi_cmd);
+ spin_unlock_irqrestore(&mipi_ld->list_cmd_lock, flags);
+
+ pr_debug("[MIPI-HSI] queue_work : cmd_work\n");
+ queue_delayed_work(mipi_ld->mipi_wq, &mipi_ld->cmd_work, 0);
+
+ return 0;
+}
+
+static int if_hsi_decode_cmd(u32 *cmd_data, u32 *cmd, u32 *ch,
+ u32 *param)
+{
+ u32 data = *cmd_data;
+ u8 lrc_cal, lrc_act;
+ u8 val1, val2, val3;
+
+ *cmd = ((data & 0xF0000000) >> 28);
+ switch (*cmd) {
+ case HSI_LL_MSG_BREAK:
+ pr_err("[MIPI-HSI] Command MSG_BREAK Received\n");
+ return -1;
+
+ case HSI_LL_MSG_OPEN_CONN:
+ *ch = ((data & 0x0F000000) >> 24);
+ *param = ((data & 0x00FFFF00) >> 8);
+ val1 = ((data & 0xFF000000) >> 24);
+ val2 = ((data & 0x00FF0000) >> 16);
+ val3 = ((data & 0x0000FF00) >> 8);
+ lrc_act = (data & 0x000000FF);
+ lrc_cal = val1 ^ val2 ^ val3;
+
+ if (lrc_cal != lrc_act) {
+ pr_err("[MIPI-HSI] CAL is broken\n");
+ return -1;
+ }
+ return 0;
+
+ case HSI_LL_MSG_CONN_READY:
+ case HSI_LL_MSG_CONN_CLOSED:
+ case HSI_LL_MSG_CANCEL_CONN:
+ case HSI_LL_MSG_NAK:
+ *ch = ((data & 0x0F000000) >> 24);
+ return 0;
+
+ case HSI_LL_MSG_ACK:
+ *ch = ((data & 0x0F000000) >> 24);
+ *param = (data & 0x00FFFFFF);
+ return 0;
+
+ case HSI_LL_MSG_CONF_RATE:
+ *ch = ((data & 0x0F000000) >> 24);
+ *param = ((data & 0x0F000000) >> 24);
+ return 0;
+
+ case HSI_LL_MSG_OPEN_CONN_OCTET:
+ *ch = ((data & 0x0F000000) >> 24);
+ *param = (data & 0x00FFFFFF);
+ return 0;
+
+ case HSI_LL_MSG_ECHO:
+ case HSI_LL_MSG_INFO_REQ:
+ case HSI_LL_MSG_INFO:
+ case HSI_LL_MSG_CONFIGURE:
+ case HSI_LL_MSG_ALLOCATE_CH:
+ case HSI_LL_MSG_RELEASE_CH:
+ case HSI_LL_MSG_INVALID:
+ default:
+ pr_err("[MIPI-HSI] Invalid command received : %08x\n", *cmd);
+ *cmd = HSI_LL_MSG_INVALID;
+ *ch = HSI_LL_INVALID_CHANNEL;
+ return -1;
+ }
+ return 0;
+}
+
+static int if_hsi_rx_cmd_handle(struct mipi_link_device *mipi_ld, u32 cmd,
+ u32 ch, u32 param)
+{
+ int ret;
+ struct if_hsi_channel *channel = &mipi_ld->hsi_channles[ch];
+
+ pr_debug("[MIPI-HSI] if_hsi_rx_cmd_handle cmd=0x%x, ch=%d, param=%d\n",
+ cmd, ch, param);
+
+ switch (cmd) {
+ case HSI_LL_MSG_OPEN_CONN_OCTET:
+ switch (channel->recv_step) {
+ case STEP_SEND_TO_CONN_CLOSED:
+ case STEP_IDLE:
+ channel->recv_step = STEP_TO_ACK;
+
+ if (!wake_lock_active(&mipi_ld->wlock)) {
+ wake_lock(&mipi_ld->wlock);
+ pr_debug("[MIPI-HSI] wake_lock\n");
+ }
+
+ if_hsi_set_wakeline(channel, 1);
+ mod_timer(&mipi_ld->hsi_acwake_down_timer, jiffies +
+ HSI_ACWAKE_DOWN_TIMEOUT);
+ pr_debug("[MIPI-HSI] mod_timer done(%d)\n",
+ HSI_ACWAKE_DOWN_TIMEOUT);
+
+ ret = if_hsi_send_command(mipi_ld, HSI_LL_MSG_ACK, ch,
+ param);
+ if (ret) {
+ pr_err("[MIPI-HSI] if_hsi_send_command fail : %d\n",
+ ret);
+ return ret;
+ }
+
+ channel->packet_size = param;
+ channel->recv_step = STEP_RX;
+ if (param % 4)
+ param += (4 - (param % 4));
+ channel->rx_count = param;
+ ret = hsi_read(channel->dev, channel->rx_data,
+ channel->rx_count / 4);
+ if (ret) {
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+ return ret;
+ }
+ return 0;
+
+ case STEP_NOT_READY:
+ ret = if_hsi_send_command(mipi_ld, HSI_LL_MSG_NAK, ch,
+ param);
+ if (ret) {
+ pr_err("[MIPI-HSI] if_hsi_send_command fail : %d\n",
+ ret);
+ return ret;
+ }
+ return 0;
+
+ default:
+ pr_err("[MIPI-HSI] wrong state : %08x, recv_step : %d\n",
+ cmd, channel->recv_step);
+
+ ret = if_hsi_send_command(mipi_ld, HSI_LL_MSG_ACK, ch,
+ param);
+ if (ret) {
+ pr_err("[MIPI-HSI] if_hsi_send_command fail : %d\n",
+ ret);
+ return ret;
+ }
+ pr_err("[MIPI-HSI] Send ACK AGAIN\n");
+ return -1;
+ }
+
+ case HSI_LL_MSG_ACK:
+ case HSI_LL_MSG_NAK:
+ switch (channel->send_step) {
+ case STEP_WAIT_FOR_ACK:
+ case STEP_SEND_OPEN_CONN:
+ if (cmd == HSI_LL_MSG_ACK) {
+ channel->send_step = STEP_TX;
+ channel->got_nack = 0;
+ pr_debug("[MIPI-HSI] got ack\n");
+ } else {
+ channel->send_step = STEP_WAIT_FOR_ACK;
+ channel->got_nack = 1;
+ pr_debug("[MIPI-HSI] got nack\n");
+ }
+
+ up(&channel->ack_done_sem);
+ return 0;
+
+ default:
+ pr_err("[MIPI-HSI] wrong state : %d, %08x(%d)\n",
+ channel->send_step, cmd, channel->channel_id);
+ return -1;
+ }
+
+ case HSI_LL_MSG_CONN_CLOSED:
+ switch (channel->send_step) {
+ case STEP_TX:
+ case STEP_WAIT_FOR_CONN_CLOSED:
+ pr_debug("[MIPI-HSI] got close\n");
+
+ channel->send_step = STEP_IDLE;
+ up(&channel->close_conn_done_sem);
+ return 0;
+
+ default:
+ pr_err("[MIPI-HSI] wrong state : %d, %08x(%d)\n",
+ channel->send_step, cmd, channel->channel_id);
+ return -1;
+ }
+
+ case HSI_LL_MSG_OPEN_CONN:
+ case HSI_LL_MSG_ECHO:
+ case HSI_LL_MSG_CANCEL_CONN:
+ case HSI_LL_MSG_CONF_RATE:
+ default:
+ pr_err("[MIPI-HSI] ERROR... CMD Not supported : %08x\n", cmd);
+ return -EINVAL;
+ }
+}
+
+static int if_hsi_protocol_send(struct mipi_link_device *mipi_ld, int ch,
+ u32 *data, unsigned int len)
+{
+ int ret;
+ int retry_count = 0;
+ int ack_timeout_cnt = 0;
+ struct io_device *iod;
+ struct if_hsi_channel *channel = &mipi_ld->hsi_channles[ch];
+
+ if (channel->send_step != STEP_IDLE) {
+ pr_err("[MIPI-HSI] send step is not IDLE : %d\n",
+ channel->send_step);
+ return -EBUSY;
+ }
+ channel->send_step = STEP_SEND_OPEN_CONN;
+
+ if (!wake_lock_active(&mipi_ld->wlock)) {
+ wake_lock(&mipi_ld->wlock);
+ pr_debug("[MIPI-HSI] wake_lock\n");
+ }
+
+ if_hsi_set_wakeline(channel, 1);
+ mod_timer(&mipi_ld->hsi_acwake_down_timer, jiffies +
+ HSI_ACWAKE_DOWN_TIMEOUT);
+ pr_debug("[MIPI-HSI] mod_timer done(%d)\n",
+ HSI_ACWAKE_DOWN_TIMEOUT);
+
+retry_send:
+
+ ret = if_hsi_send_command(mipi_ld, HSI_LL_MSG_OPEN_CONN_OCTET, ch,
+ len);
+ if (ret) {
+ pr_err("[MIPI-HSI] if_hsi_send_command fail : %d\n", ret);
+ if_hsi_set_wakeline(channel, 0);
+ channel->send_step = STEP_IDLE;
+ return -1;
+ }
+
+ channel->send_step = STEP_WAIT_FOR_ACK;
+
+ if (down_timeout(&channel->ack_done_sem, HSI_ACK_DONE_TIMEOUT) < 0) {
+ pr_err("[MIPI-HSI] ch=%d, ack_done timeout\n",
+ channel->channel_id);
+
+ list_for_each_entry(iod, &mipi_ld->list_of_io_devices, list)
+ if (iod->format == IPC_FMT)
+ break;
+
+ if (((mipi_ld->ld.com_state == COM_ONLINE) ||
+ (mipi_ld->ld.com_state == COM_HANDSHAKE)) &&
+ (iod->mc->phone_state == STATE_ONLINE)) {
+ channel->send_step = STEP_SEND_OPEN_CONN;
+ hsi_conn_err_recovery(mipi_ld);
+
+ ack_timeout_cnt++;
+ if (ack_timeout_cnt < 5) {
+ pr_err("[MIPI-HSI] check ack again. cnt:%d\n",
+ ack_timeout_cnt);
+ msleep(10);
+ if (down_trylock(&channel->ack_done_sem)) {
+ pr_err("[MIPI-HSI] retry send open\n");
+ if_hsi_set_wakeline(channel, 0);
+ if_hsi_set_wakeline(channel, 1);
+ sema_init(&channel->ack_done_sem, 0);
+ goto retry_send;
+ } else {
+ pr_err("[MIPI-HSI] got ack after sw-reset\n");
+ goto check_nack;
+ }
+ }
+
+ /* try to recover cp */
+ list_for_each_entry(iod, &mipi_ld->list_of_io_devices,
+ list) {
+ if (iod->format == IPC_FMT) {
+ iod->modem_state_changed(iod,
+ STATE_CRASH_EXIT);
+ break;
+ }
+ }
+ }
+
+ channel->send_step = STEP_IDLE;
+ return -ETIMEDOUT;
+ }
+
+check_nack:
+
+ pr_debug("[MIPI-HSI] ch=%d, got ack_done=%d\n", channel->channel_id,
+ channel->got_nack);
+
+ if (channel->got_nack && (retry_count < 10)) {
+ pr_debug("[MIPI-HSI] ch=%d, got nack=%d retry=%d\n",
+ channel->channel_id, channel->got_nack,
+ retry_count);
+ retry_count++;
+ msleep(1);
+ goto retry_send;
+ }
+ retry_count = 0;
+
+ channel->send_step = STEP_TX;
+
+ ret = if_hsi_write(channel, data, len);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] if_hsi_write fail : %d\n", ret);
+ if_hsi_set_wakeline(channel, 0);
+ channel->send_step = STEP_IDLE;
+ return ret;
+ }
+ pr_debug("[MIPI-HSI] SEND DATA : %08x(%d)\n", *data, len);
+
+ pr_debug("%08x %08x %08x %08x %08x %08x %08x %08x\n",
+ *channel->tx_data, *(channel->tx_data + 1),
+ *(channel->tx_data + 2), *(channel->tx_data + 3),
+ *(channel->tx_data + 4), *(channel->tx_data + 5),
+ *(channel->tx_data + 6), *(channel->tx_data + 7));
+
+ channel->send_step = STEP_WAIT_FOR_CONN_CLOSED;
+ if (down_timeout(&channel->close_conn_done_sem,
+ HSI_CLOSE_CONN_DONE_TIMEOUT) < 0) {
+ pr_err("[MIPI-HSI] ch=%d, close conn timeout\n",
+ channel->channel_id);
+
+ channel->send_step = STEP_IDLE;
+ hsi_conn_err_recovery(mipi_ld);
+ }
+ pr_debug("[MIPI-HSI] ch=%d, got close_conn_done\n",
+ channel->channel_id);
+
+ channel->send_step = STEP_IDLE;
+
+ pr_debug("[MIPI-HSI] write protocol Done : %d\n", channel->tx_count);
+ return channel->tx_count;
+}
+
+static int if_hsi_write(struct if_hsi_channel *channel, u32 *data,
+ unsigned int size)
+{
+ int ret;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&channel->tx_state_lock, flags);
+ if (channel->tx_state & HSI_CHANNEL_TX_STATE_WRITING) {
+ spin_unlock_irqrestore(&channel->tx_state_lock, flags);
+ return -EBUSY;
+ }
+ channel->tx_state |= HSI_CHANNEL_TX_STATE_WRITING;
+ spin_unlock_irqrestore(&channel->tx_state_lock, flags);
+
+ channel->tx_data = data;
+ if (size % 4)
+ size += (4 - (size % 4));
+ channel->tx_count = size;
+
+ pr_debug("[MIPI-HSI] submit write data : 0x%x(%d)\n",
+ *(u32 *)channel->tx_data, channel->tx_count);
+ ret = hsi_write(channel->dev, channel->tx_data, channel->tx_count / 4);
+ if (ret) {
+ pr_err("[MIPI-HSI] ch=%d, hsi_write fail : %d\n",
+ channel->channel_id, ret);
+
+ spin_lock_irqsave(&channel->tx_state_lock, flags);
+ channel->tx_state &= ~HSI_CHANNEL_TX_STATE_WRITING;
+ spin_unlock_irqrestore(&channel->tx_state_lock, flags);
+
+ return ret;
+ }
+
+ if (down_timeout(&channel->write_done_sem,
+ HSI_WRITE_DONE_TIMEOUT) < 0) {
+ pr_err("[MIPI-HSI] ch=%d, hsi_write_done timeout : %d\n",
+ channel->channel_id, size);
+
+ print_hex_dump_bytes("[HSI]", DUMP_PREFIX_OFFSET,
+ channel->tx_data, size);
+
+ hsi_write_cancel(channel->dev);
+
+ spin_lock_irqsave(&channel->tx_state_lock, flags);
+ channel->tx_state &= ~HSI_CHANNEL_TX_STATE_WRITING;
+ spin_unlock_irqrestore(&channel->tx_state_lock, flags);
+
+ return -ETIMEDOUT;
+ }
+
+ if (channel->tx_count != size)
+ pr_err("[MIPI-HSI] ch:%d,write_done fail,write_size:%d,origin_size:%d\n",
+ channel->channel_id, channel->tx_count, size);
+
+#ifdef DEBUG
+ print_hex_dump_bytes("[HSI]", DUMP_PREFIX_OFFSET,
+ channel->tx_data, size);
+#endif
+
+ return channel->tx_count;
+}
+
+static void if_hsi_write_done(struct hsi_device *dev, unsigned int size)
+{
+ unsigned long int flags;
+ struct mipi_link_device *mipi_ld =
+ (struct mipi_link_device *)if_hsi_driver.priv_data;
+ struct if_hsi_channel *channel = &mipi_ld->hsi_channles[dev->n_ch];
+
+ if ((channel->channel_id == HSI_CONTROL_CHANNEL) &&
+ (((*channel->tx_data & 0xF0000000) >> 28) ==
+ HSI_LL_MSG_CONN_CLOSED) &&
+ mipi_ld->ld.com_state == COM_ONLINE) {
+ mipi_ld->hsi_channles[
+ (*channel->tx_data & 0x0F000000) >> 24].recv_step = STEP_IDLE;
+ }
+
+ pr_debug("[MIPI-HSI] got write data : 0x%x(%d)\n",
+ *(u32 *)channel->tx_data, size);
+
+ spin_lock_irqsave(&channel->tx_state_lock, flags);
+ channel->tx_state &= ~HSI_CHANNEL_TX_STATE_WRITING;
+ spin_unlock_irqrestore(&channel->tx_state_lock, flags);
+
+ pr_debug("%08x %08x %08x %08x %08x %08x %08x %08x\n",
+ *channel->tx_data, *(channel->tx_data + 1),
+ *(channel->tx_data + 2), *(channel->tx_data + 3),
+ *(channel->tx_data + 4), *(channel->tx_data + 5),
+ *(channel->tx_data + 6), *(channel->tx_data + 7));
+
+ channel->tx_count = 4 * size;
+ up(&channel->write_done_sem);
+}
+
+static void if_hsi_read_done(struct hsi_device *dev, unsigned int size)
+{
+ int ret;
+ unsigned long int flags;
+ u32 cmd = 0, ch = 0, param = 0;
+ struct mipi_link_device *mipi_ld =
+ (struct mipi_link_device *)if_hsi_driver.priv_data;
+ struct if_hsi_channel *channel = &mipi_ld->hsi_channles[dev->n_ch];
+ struct io_device *iod;
+ enum dev_format format_type = 0;
+
+ pr_debug("[MIPI-HSI] got read data : 0x%x(%d)\n",
+ *(u32 *)channel->rx_data, size);
+
+ spin_lock_irqsave(&channel->rx_state_lock, flags);
+ channel->rx_state &= ~HSI_CHANNEL_RX_STATE_READING;
+ spin_unlock_irqrestore(&channel->rx_state_lock, flags);
+
+ channel->rx_count = 4 * size;
+
+ switch (channel->channel_id) {
+ case HSI_CONTROL_CHANNEL:
+ switch (mipi_ld->ld.com_state) {
+ case COM_HANDSHAKE:
+ case COM_ONLINE:
+ pr_debug("[MIPI-HSI] RECV CMD : %08x\n",
+ *channel->rx_data);
+
+ if (channel->rx_count != 4) {
+ pr_err("[MIPI-HSI] wrong command len : %d\n",
+ channel->rx_count);
+ return;
+ }
+
+ ret = if_hsi_decode_cmd(channel->rx_data, &cmd, &ch,
+ &param);
+ if (ret)
+ pr_err("[MIPI-HSI] decode_cmd fail=%d, cmd=%x\n",
+ ret, cmd);
+ else {
+ pr_debug("[MIPI-HSI] decode_cmd : %08x\n", cmd);
+ ret = if_hsi_rx_cmd_handle(mipi_ld, cmd, ch,
+ param);
+ if (ret)
+ pr_debug("[MIPI-HSI] handle cmd cmd=%x\n",
+ cmd);
+ }
+
+ ret = hsi_read(channel->dev, channel->rx_data, 1);
+ if (ret)
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+
+ return;
+
+ case COM_BOOT:
+ pr_debug("[MIPI-HSI] receive data : 0x%x(%d)\n",
+ *channel->rx_data, channel->rx_count);
+
+ list_for_each_entry(iod, &mipi_ld->list_of_io_devices,
+ list) {
+ if ((iod->format == IPC_BOOT) &&
+ (iod->id == 0x0)) {
+ ret = iod->recv(iod,
+ (char *)channel->rx_data,
+ channel->rx_count);
+ if (ret < 0)
+ pr_err("[MIPI-HSI] recv call "
+ "fail : %d\n", ret);
+
+ break;
+ }
+ }
+
+ ret = hsi_read(channel->dev, channel->rx_data, 1);
+ if (ret)
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+ return;
+
+ case COM_BOOT_EBL:
+ pr_debug("[MIPI-HSI] receive data : 0x%x(%d)\n",
+ *channel->rx_data, channel->rx_count);
+
+ list_for_each_entry(iod, &mipi_ld->list_of_io_devices,
+ list) {
+ if ((iod->format == IPC_BOOT) &&
+ (iod->id == 0x1)) {
+ ret = iod->recv(iod,
+ (char *)channel->rx_data,
+ channel->rx_count);
+ if (ret < 0)
+ pr_err("[MIPI-HSI] recv call "
+ "fail : %d\n", ret);
+
+ break;
+ }
+ }
+
+ ret = hsi_read(channel->dev, channel->rx_data, 1);
+ if (ret)
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+ return;
+
+ case COM_CRASH:
+ pr_debug("[MIPI-HSI] receive data : 0x%x(%d)\n",
+ *channel->rx_data, channel->rx_count);
+
+ list_for_each_entry(iod, &mipi_ld->list_of_io_devices,
+ list) {
+ if (iod->format == IPC_RAMDUMP) {
+ channel->packet_size =
+ *channel->rx_data;
+ pr_debug("[MIPI-HSI] ramdump packet size : "
+ "%d\n", channel->packet_size);
+
+ ret = iod->recv(iod,
+ (char *)channel->rx_data + 4,
+ channel->packet_size);
+ if (ret < 0)
+ pr_err("[MIPI-HSI] recv call "
+ "fail : %d\n", ret);
+
+ break;
+ }
+ }
+
+ ret = hsi_read(channel->dev, channel->rx_data,
+ DUMP_PACKET_SIZE);
+ if (ret)
+ pr_err("[MIPI-HSI] hsi_read fail : %d\n", ret);
+ return;
+
+ case COM_NONE:
+ default:
+ pr_err("[MIPI-HSI] receive data in wrong state : 0x%x(%d)\n",
+ *channel->rx_data, channel->rx_count);
+ return;
+ }
+ break;
+
+ case HSI_FMT_CHANNEL:
+ pr_debug("[MIPI-HSI] iodevice format : IPC_FMT\n");
+ format_type = IPC_FMT;
+ break;
+ case HSI_RAW_CHANNEL:
+ pr_debug("[MIPI-HSI] iodevice format : IPC_MULTI_RAW\n");
+ format_type = IPC_MULTI_RAW;
+ break;
+ case HSI_RFS_CHANNEL:
+ pr_debug("[MIPI-HSI] iodevice format : IPC_RFS\n");
+ format_type = IPC_RFS;
+ break;
+
+ case HSI_CMD_CHANNEL:
+ pr_debug("[MIPI-HSI] receive command data : 0x%x\n",
+ *channel->rx_data);
+
+ channel->recv_step = STEP_SEND_TO_CONN_CLOSED;
+
+ ch = channel->channel_id;
+ param = 0;
+ ret = if_hsi_send_command(mipi_ld, HSI_LL_MSG_CONN_CLOSED,
+ ch, param);
+ if (ret)
+ pr_err("[MIPI-HSI] send_cmd fail=%d\n", ret);
+ return;
+
+ default:
+ return;
+ }
+
+ list_for_each_entry(iod, &mipi_ld->list_of_io_devices, list) {
+ pr_debug("[MIPI-HSI] iodevice format : %d\n", iod->format);
+
+ if (iod->format == format_type) {
+ channel->recv_step = STEP_NOT_READY;
+
+ pr_debug("[MIPI-HSI] RECV DATA : %08x(%d)-%d\n",
+ *channel->rx_data, channel->packet_size,
+ iod->format);
+
+ pr_debug("%08x %08x %08x %08x %08x %08x %08x %08x\n",
+ *channel->rx_data, *(channel->rx_data + 1),
+ *(channel->rx_data + 2), *(channel->rx_data + 3),
+ *(channel->rx_data + 4), *(channel->rx_data + 5),
+ *(channel->rx_data + 6), *(channel->rx_data + 7));
+
+ ret = iod->recv(iod, (char *)channel->rx_data,
+ channel->packet_size);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] recv call fail : %d\n", ret);
+ print_hex_dump_bytes("[HSI]",
+ DUMP_PREFIX_OFFSET,
+ channel->rx_data, channel->packet_size);
+ hsi_conn_err_recovery(mipi_ld);
+ }
+ channel->packet_size = 0;
+
+ channel->recv_step = STEP_SEND_TO_CONN_CLOSED;
+
+ ch = channel->channel_id;
+ param = 0;
+ ret = if_hsi_send_command(mipi_ld,
+ HSI_LL_MSG_CONN_CLOSED, ch, param);
+ if (ret)
+ pr_err("[MIPI-HSI] send_cmd fail=%d\n", ret);
+ return;
+ }
+ }
+}
+
+static void if_hsi_port_event(struct hsi_device *dev, unsigned int event,
+ void *arg)
+{
+ int acwake_level = 1;
+ struct mipi_link_device *mipi_ld =
+ (struct mipi_link_device *)if_hsi_driver.priv_data;
+
+ switch (event) {
+ case HSI_EVENT_BREAK_DETECTED:
+ pr_err("[MIPI-HSI] HSI_EVENT_BREAK_DETECTED\n");
+ return;
+
+ case HSI_EVENT_HSR_DATAAVAILABLE:
+ pr_err("[MIPI-HSI] HSI_EVENT_HSR_DATAAVAILABLE\n");
+ return;
+
+ case HSI_EVENT_CAWAKE_UP:
+ if (dev->n_ch == HSI_CONTROL_CHANNEL) {
+ if (!wake_lock_active(&mipi_ld->wlock)) {
+ wake_lock(&mipi_ld->wlock);
+ pr_debug("[MIPI-HSI] wake_lock\n");
+ }
+ pr_debug("[MIPI-HSI] CAWAKE_%d(1)\n", dev->n_ch);
+ }
+ return;
+
+ case HSI_EVENT_CAWAKE_DOWN:
+ if (dev->n_ch == HSI_CONTROL_CHANNEL)
+ pr_debug("[MIPI-HSI] CAWAKE_%d(0)\n", dev->n_ch);
+
+ if ((dev->n_ch == HSI_CONTROL_CHANNEL) &&
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].opened) {
+ hsi_ioctl(
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].dev,
+ HSI_IOCTL_GET_ACWAKE, &acwake_level);
+
+ pr_debug("[MIPI-HSI] GET_ACWAKE. Ch : %d, level : %d\n",
+ dev->n_ch, acwake_level);
+
+ if (!acwake_level) {
+ wake_unlock(&mipi_ld->wlock);
+ pr_debug("[MIPI-HSI] wake_unlock\n");
+ }
+ }
+ return;
+
+ case HSI_EVENT_ERROR:
+ pr_err("[MIPI-HSI] HSI_EVENT_ERROR\n");
+ return;
+
+ default:
+ pr_err("[MIPI-HSI] Unknown Event : %d\n", event);
+ return;
+ }
+}
+
+static int __devinit if_hsi_probe(struct hsi_device *dev)
+{
+ int port = 0;
+ unsigned long *address;
+
+ struct mipi_link_device *mipi_ld =
+ (struct mipi_link_device *)if_hsi_driver.priv_data;
+
+ for (port = 0; port < HSI_MAX_PORTS; port++) {
+ if (if_hsi_driver.ch_mask[port])
+ break;
+ }
+ address = (unsigned long *)&if_hsi_driver.ch_mask[port];
+
+ if (test_bit(dev->n_ch, address) && (dev->n_p == port)) {
+ /* Register callback func */
+ hsi_set_write_cb(dev, if_hsi_write_done);
+ hsi_set_read_cb(dev, if_hsi_read_done);
+ hsi_set_port_event_cb(dev, if_hsi_port_event);
+
+ /* Init device data */
+ mipi_ld->hsi_channles[dev->n_ch].dev = dev;
+ mipi_ld->hsi_channles[dev->n_ch].tx_count = 0;
+ mipi_ld->hsi_channles[dev->n_ch].rx_count = 0;
+ mipi_ld->hsi_channles[dev->n_ch].tx_state = 0;
+ mipi_ld->hsi_channles[dev->n_ch].rx_state = 0;
+ mipi_ld->hsi_channles[dev->n_ch].packet_size = 0;
+ mipi_ld->hsi_channles[dev->n_ch].acwake = 0;
+ mipi_ld->hsi_channles[dev->n_ch].send_step = STEP_UNDEF;
+ mipi_ld->hsi_channles[dev->n_ch].recv_step = STEP_UNDEF;
+ spin_lock_init(&mipi_ld->hsi_channles[dev->n_ch].tx_state_lock);
+ spin_lock_init(&mipi_ld->hsi_channles[dev->n_ch].rx_state_lock);
+ spin_lock_init(&mipi_ld->hsi_channles[dev->n_ch].acwake_lock);
+ sema_init(&mipi_ld->hsi_channles[dev->n_ch].write_done_sem, 0);
+ sema_init(&mipi_ld->hsi_channles[dev->n_ch].ack_done_sem, 0);
+ sema_init(&mipi_ld->hsi_channles[dev->n_ch].close_conn_done_sem,
+ 0);
+ }
+
+ pr_debug("[MIPI-HSI] if_hsi_probe() done. ch : %d\n", dev->n_ch);
+ return 0;
+}
+
+static int if_hsi_init(struct link_device *ld)
+{
+ int ret;
+ int i = 0;
+ struct mipi_link_device *mipi_ld = to_mipi_link_device(ld);
+
+ for (i = 0; i < HSI_MAX_PORTS; i++)
+ if_hsi_driver.ch_mask[i] = 0;
+
+ for (i = 0; i < HSI_MAX_CHANNELS; i++) {
+ mipi_ld->hsi_channles[i].dev = NULL;
+ mipi_ld->hsi_channles[i].opened = 0;
+ mipi_ld->hsi_channles[i].channel_id = i;
+ }
+ if_hsi_driver.ch_mask[0] = CHANNEL_MASK;
+
+ /* TODO - need to get priv data (request to TI) */
+ if_hsi_driver.priv_data = (void *)mipi_ld;
+ ret = hsi_register_driver(&if_hsi_driver);
+ if (ret) {
+ pr_err("[MIPI-HSI] hsi_register_driver() fail : %d\n", ret);
+ return ret;
+ }
+
+ mipi_ld->mipi_wq = alloc_workqueue("mipi_cmd_wq",
+ WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
+ if (!mipi_ld->mipi_wq) {
+ pr_err("[MIPI-HSI] fail to create work Q.\n");
+ return -ENOMEM;
+ }
+ INIT_DELAYED_WORK(&mipi_ld->cmd_work, if_hsi_cmd_work);
+ INIT_DELAYED_WORK(&mipi_ld->start_work, mipi_hsi_start_work);
+
+ setup_timer(&mipi_ld->hsi_acwake_down_timer, if_hsi_acwake_down_func,
+ (unsigned long)mipi_ld);
+
+ /* TODO - allocate rx buff */
+ mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].rx_data =
+ kmalloc(64 * 1024, GFP_DMA | GFP_ATOMIC);
+ if (!mipi_ld->hsi_channles[HSI_CONTROL_CHANNEL].rx_data) {
+ pr_err("[MIPI-HSI] alloc HSI_CONTROL_CHANNEL rx_data fail\n");
+ return -ENOMEM;
+ }
+ mipi_ld->hsi_channles[HSI_FMT_CHANNEL].rx_data =
+ kmalloc(256 * 1024, GFP_DMA | GFP_ATOMIC);
+ if (!mipi_ld->hsi_channles[HSI_FMT_CHANNEL].rx_data) {
+ pr_err("[MIPI-HSI] alloc HSI_FMT_CHANNEL rx_data fail\n");
+ return -ENOMEM;
+ }
+ mipi_ld->hsi_channles[HSI_RAW_CHANNEL].rx_data =
+ kmalloc(256 * 1024, GFP_DMA | GFP_ATOMIC);
+ if (!mipi_ld->hsi_channles[HSI_RAW_CHANNEL].rx_data) {
+ pr_err("[MIPI-HSI] alloc HSI_RAW_CHANNEL rx_data fail\n");
+ return -ENOMEM;
+ }
+ mipi_ld->hsi_channles[HSI_RFS_CHANNEL].rx_data =
+ kmalloc(256 * 1024, GFP_DMA | GFP_ATOMIC);
+ if (!mipi_ld->hsi_channles[HSI_RFS_CHANNEL].rx_data) {
+ pr_err("[MIPI-HSI] alloc HSI_RFS_CHANNEL rx_data fail\n");
+ return -ENOMEM;
+ }
+ mipi_ld->hsi_channles[HSI_CMD_CHANNEL].rx_data =
+ kmalloc(256 * 1024, GFP_DMA | GFP_ATOMIC);
+ if (!mipi_ld->hsi_channles[HSI_CMD_CHANNEL].rx_data) {
+ pr_err("[MIPI-HSI] alloc HSI_CMD_CHANNEL rx_data fail\n");
+ return -ENOMEM;
+ }
+
+ mipi_ld->bulk_tx_buf = kmalloc(MIPI_BULK_TX_SIZE, GFP_DMA | GFP_ATOMIC);
+ if (!mipi_ld->bulk_tx_buf) {
+ pr_err("[MIPI-HSI] alloc bulk tx buffer fail\n");
+ return -ENOMEM;
+ }
+
+ skb_queue_head_init(&mipi_ld->bulk_txq);
+
+ return 0;
+}
+
+struct link_device *mipi_create_link_device(struct platform_device *pdev)
+{
+ int ret;
+ struct mipi_link_device *mipi_ld;
+ struct link_device *ld;
+
+ /* for dpram int */
+ /* struct modem_data *pdata = pdev->dev.platform_data; */
+
+ mipi_ld = kzalloc(sizeof(struct mipi_link_device), GFP_KERNEL);
+ if (!mipi_ld)
+ return NULL;
+
+ INIT_LIST_HEAD(&mipi_ld->list_of_io_devices);
+ INIT_LIST_HEAD(&mipi_ld->list_of_hsi_cmd);
+ spin_lock_init(&mipi_ld->list_cmd_lock);
+ skb_queue_head_init(&mipi_ld->ld.sk_fmt_tx_q);
+ skb_queue_head_init(&mipi_ld->ld.sk_raw_tx_q);
+
+ wake_lock_init(&mipi_ld->wlock, WAKE_LOCK_SUSPEND, "mipi_link");
+
+ ld = &mipi_ld->ld;
+
+ ld->name = "mipi_hsi";
+ ld->attach = mipi_hsi_attach_io_dev;
+ ld->init_comm = mipi_hsi_init_communication;
+ ld->terminate_comm = mipi_hsi_terminate_communication;
+ ld->send = mipi_hsi_send;
+ ld->com_state = COM_NONE;
+
+ /* for dpram int */
+ /* ld->irq = gpio_to_irq(pdata->gpio); s*/
+
+ ld->tx_wq = create_singlethread_workqueue("mipi_tx_wq");
+ if (!ld->tx_wq) {
+ pr_err("[MIPI-HSI] fail to create work Q.\n");
+ return NULL;
+ }
+ INIT_WORK(&ld->tx_work, mipi_hsi_tx_work);
+
+ ld->tx_raw_wq = alloc_workqueue("mipi_tx_raw_wq",
+ WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
+ if (!ld->tx_raw_wq) {
+ pr_err("[MIPI-HSI] fail to create raw work Q.\n");
+ return NULL;
+ }
+ INIT_DELAYED_WORK(&ld->tx_delayed_work, mipi_hsi_tx_raw_work);
+
+ ret = if_hsi_init(ld);
+ if (ret)
+ return NULL;
+
+ return ld;
+}
+
diff --git a/drivers/misc/modem_if/modem_link_device_mipi.h b/drivers/misc/modem_if/modem_link_device_mipi.h
new file mode 100755
index 0000000..fb628a3
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_mipi.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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 __MODEM_LINK_DEVICE_MIPI_H__
+#define __MODEM_LINK_DEVICE_MIPI_H__
+
+
+#define HSI_MAX_CHANNELS 16
+#define CHANNEL_MASK 0xFF
+
+#define HSI_CHANNEL_TX_STATE_UNAVAIL (1 << 0)
+#define HSI_CHANNEL_TX_STATE_WRITING (1 << 1)
+#define HSI_CHANNEL_RX_STATE_UNAVAIL (1 << 0)
+#define HSI_CHANNEL_RX_STATE_READING (1 << 1)
+
+#define HSI_WRITE_DONE_TIMEOUT (HZ)
+#define HSI_READ_DONE_TIMEOUT (HZ)
+#define HSI_ACK_DONE_TIMEOUT (HZ / 2)
+#define HSI_CLOSE_CONN_DONE_TIMEOUT (HZ / 10)
+#define HSI_ACWAKE_DOWN_TIMEOUT (HZ / 2)
+
+#define HSI_CONTROL_CHANNEL 0
+#define HSI_FLASHLESS_CHANNEL 0
+#define HSI_CP_RAMDUMP_CHANNEL 0
+#define HSI_FMT_CHANNEL 1
+#define HSI_RAW_CHANNEL 2
+#define HSI_RFS_CHANNEL 3
+#define HSI_CMD_CHANNEL 4
+#define HSI_NUM_OF_USE_CHANNELS 5
+
+#define HSI_LL_INVALID_CHANNEL 0xFF
+
+#define DUMP_PACKET_SIZE 12289 /* 48K + 4 length, word unit */
+#define DUMP_ERR_INFO_SIZE 39 /* 150 bytes + 4 length , word unit */
+
+#define MIPI_BULK_TX_SIZE (8 * 1024)
+
+enum {
+ HSI_LL_MSG_BREAK, /* 0x0 */
+ HSI_LL_MSG_ECHO,
+ HSI_LL_MSG_INFO_REQ,
+ HSI_LL_MSG_INFO,
+ HSI_LL_MSG_CONFIGURE,
+ HSI_LL_MSG_ALLOCATE_CH,
+ HSI_LL_MSG_RELEASE_CH,
+ HSI_LL_MSG_OPEN_CONN,
+ HSI_LL_MSG_CONN_READY,
+ HSI_LL_MSG_CONN_CLOSED, /* 0x9 */
+ HSI_LL_MSG_CANCEL_CONN,
+ HSI_LL_MSG_ACK, /* 0xB */
+ HSI_LL_MSG_NAK, /* 0xC */
+ HSI_LL_MSG_CONF_RATE,
+ HSI_LL_MSG_OPEN_CONN_OCTET, /* 0xE */
+ HSI_LL_MSG_INVALID = 0xFF,
+};
+
+enum {
+ STEP_UNDEF,
+ STEP_CLOSED,
+ STEP_NOT_READY,
+ STEP_IDLE,
+ STEP_ERROR,
+ STEP_SEND_OPEN_CONN,
+ STEP_SEND_ACK,
+ STEP_WAIT_FOR_ACK,
+ STEP_TO_ACK,
+ STEP_SEND_NACK,
+ STEP_GET_NACK,
+ STEP_SEND_CONN_READY,
+ STEP_WAIT_FOR_CONN_READY,
+ STEP_SEND_CONF_RATE,
+ STEP_WAIT_FOR_CONF_ACK,
+ STEP_TX,
+ STEP_RX,
+ STEP_SEND_CONN_CLOSED,
+ STEP_WAIT_FOR_CONN_CLOSED,
+ STEP_SEND_BREAK,
+ STEP_SEND_TO_CONN_CLOSED,
+};
+
+
+struct if_hsi_channel {
+ struct hsi_device *dev;
+ unsigned int channel_id;
+
+ u32 *tx_data;
+ unsigned int tx_count;
+ u32 *rx_data;
+ unsigned int rx_count;
+ unsigned int packet_size;
+
+ unsigned int tx_state;
+ unsigned int rx_state;
+ spinlock_t tx_state_lock;
+ spinlock_t rx_state_lock;
+
+ unsigned int send_step;
+ unsigned int recv_step;
+
+ unsigned int got_nack;
+ unsigned int acwake;
+ spinlock_t acwake_lock;
+
+ struct semaphore write_done_sem;
+ struct semaphore ack_done_sem;
+ struct semaphore close_conn_done_sem;
+
+ unsigned int opened;
+};
+
+struct if_hsi_command {
+ u32 command;
+ struct list_head list;
+};
+
+struct mipi_link_device {
+ struct link_device ld;
+
+ /* mipi specific link data */
+ struct if_hsi_channel hsi_channles[HSI_MAX_CHANNELS];
+ struct list_head list_of_hsi_cmd;
+ spinlock_t list_cmd_lock;
+
+ struct workqueue_struct *mipi_wq;
+ struct delayed_work cmd_work;
+ struct delayed_work start_work;
+
+ struct wake_lock wlock;
+ struct timer_list hsi_acwake_down_timer;
+
+ /* maybe -list of io devices for the link device to use
+ * to find where to send incoming packets to */
+ struct list_head list_of_io_devices;
+
+ void *bulk_tx_buf;
+ struct sk_buff_head bulk_txq;
+};
+/* converts from struct link_device* to struct xxx_link_device* */
+#define to_mipi_link_device(linkdev) \
+ container_of(linkdev, struct mipi_link_device, ld)
+
+
+enum {
+ HSI_INIT_MODE_NORMAL,
+ HSI_INIT_MODE_FLASHLESS_BOOT,
+ HSI_INIT_MODE_CP_RAMDUMP,
+ HSI_INIT_MODE_FLASHLESS_BOOT_EBL,
+};
+static int hsi_init_handshake(struct mipi_link_device *mipi_ld, int mode);
+static int if_hsi_write(struct if_hsi_channel *channel, u32 *data,
+ unsigned int size);
+static int if_hsi_protocol_send(struct mipi_link_device *mipi_ld, int ch,
+ u32 *data, unsigned int len);
+static int if_hsi_close_channel(struct if_hsi_channel *channel);
+
+#endif
diff --git a/drivers/misc/modem_if/modem_link_device_usb.c b/drivers/misc/modem_if/modem_link_device_usb.c
new file mode 100755
index 0000000..c1280ac
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_usb.c
@@ -0,0 +1,873 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <linux/poll.h>
+#include <linux/gpio.h>
+#include <linux/if_arp.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include "modem_link_device_usb.h"
+
+#define URB_COUNT 4
+
+static irqreturn_t usb_resume_irq(int irq, void *data);
+
+static int usb_attach_io_dev(struct link_device *ld,
+ struct io_device *iod)
+{
+ struct usb_link_device *usb_ld = to_usb_link_device(ld);
+
+ iod->link = ld;
+
+ /* list up io devices */
+ list_add(&iod->list, &usb_ld->list_of_io_devices);
+
+ return 0;
+}
+
+static void
+usb_free_urbs(struct usb_link_device *usb_ld, struct if_usb_devdata *pipe)
+{
+ struct usb_device *usbdev = usb_ld->usbdev;
+ struct urb *urb;
+
+ while ((urb = usb_get_from_anchor(&pipe->urbs))) {
+ usb_poison_urb(urb);
+ usb_free_coherent(usbdev, pipe->rx_buf_size,
+ urb->transfer_buffer, urb->transfer_dma);
+ urb->transfer_buffer = NULL;
+ usb_put_urb(urb);
+ usb_free_urb(urb);
+ }
+}
+
+static int usb_init_communication(struct link_device *ld,
+ struct io_device *iod)
+{
+ switch (iod->format) {
+ case IPC_BOOT:
+ ld->com_state = COM_BOOT;
+ skb_queue_purge(&ld->sk_fmt_tx_q);
+ break;
+
+ case IPC_RAMDUMP:
+ ld->com_state = COM_CRASH;
+ break;
+
+ case IPC_FMT:
+ case IPC_RFS:
+ case IPC_RAW:
+ default:
+ ld->com_state = COM_ONLINE;
+ break;
+ }
+
+ pr_debug("%s: iod = %s, com_state = %d\n", __func__, iod->name,
+ ld->com_state);
+ return 0;
+}
+
+static void usb_terminate_communication(
+ struct link_device *ld, struct io_device *iod)
+{
+ pr_debug("%s: iod = %s, com_state = %d\n", __func__, iod->name,
+ ld->com_state);
+}
+
+
+static int usb_rx_submit(struct if_usb_devdata *pipe, struct urb *urb, gfp_t gfp_flags)
+{
+ int ret;
+
+ usb_anchor_urb(urb, &pipe->reading);
+ ret = usb_submit_urb(urb, gfp_flags);
+ if (ret) {
+ usb_unanchor_urb(urb);
+ usb_anchor_urb(urb, &pipe->urbs);
+ pr_err("%s: submit urb fail with ret (%d)\n", __func__, ret);
+ }
+
+ usb_mark_last_busy(urb->dev);
+ return ret;
+}
+
+static void usb_rx_complete(struct urb *urb)
+{
+ struct if_usb_devdata *pipe_data = urb->context;
+ struct usb_link_device *usb_ld = usb_get_intfdata(pipe_data->data_intf);
+ struct io_device *iod;
+ int iod_format = IPC_FMT;
+ int ret;
+
+ switch (urb->status) {
+ case 0:
+ case -ENOENT:
+ if (!urb->actual_length)
+ goto re_submit;
+ /* call iod recv */
+ /* how we can distinguish boot ch with fmt ch ?? */
+ switch (pipe_data->format) {
+ case IF_USB_FMT_EP:
+ iod_format = IPC_FMT;
+ break;
+ case IF_USB_RAW_EP:
+ iod_format = IPC_MULTI_RAW;
+ break;
+ case IF_USB_RFS_EP:
+ iod_format = IPC_RFS;
+ break;
+ default:
+ break;
+ }
+
+ list_for_each_entry(iod, &usb_ld->list_of_io_devices, list) {
+ /* during boot stage fmt end point */
+ /* shared with boot io device */
+ /* when we use fmt device only, at boot and ipc exchange
+ it can be reduced to 1 device */
+ if (iod_format == IPC_FMT &&
+ usb_ld->ld.com_state == COM_BOOT)
+ iod_format = IPC_BOOT;
+ if (iod_format == IPC_FMT &&
+ usb_ld->ld.com_state == COM_CRASH)
+ iod_format = IPC_RAMDUMP;
+
+ if (iod->format == iod_format) {
+ ret = iod->recv(iod,
+ (char *)urb->transfer_buffer,
+ urb->actual_length);
+ if (ret < 0)
+ pr_err("%s: io device recv error :%d\n",
+ __func__, ret);
+ break;
+ }
+ }
+re_submit:
+ if (urb->status || atomic_read(&usb_ld->suspend_count))
+ break;
+ usb_rx_submit(pipe_data, urb, GFP_ATOMIC);
+ return;
+ case -ESHUTDOWN:
+ case -EPROTO:
+ break;
+ case -EOVERFLOW:
+ pr_err("%s: RX overflow\n", __func__);
+ break;
+ default:
+ pr_err("%s: RX complete Status (%d)\n", __func__, urb->status);
+ break;
+ }
+
+ usb_anchor_urb(urb, &pipe_data->urbs);
+}
+
+static int usb_send(struct link_device *ld, struct io_device *iod,
+ struct sk_buff *skb)
+{
+ struct sk_buff_head *txq;
+
+ if (iod->format == IPC_RAW)
+ txq = &ld->sk_raw_tx_q;
+ else
+ txq = &ld->sk_fmt_tx_q;
+
+ /* save io device into cb area */
+ *((struct io_device **)skb->cb) = iod;
+ /* en queue skb data */
+ skb_queue_tail(txq, skb);
+
+ queue_delayed_work(ld->tx_wq, &ld->tx_delayed_work, 0);
+
+ return skb->len;
+}
+
+static void usb_tx_complete(struct urb *urb)
+{
+ int ret = 0;
+ struct sk_buff *skb = urb->context;
+
+ switch (urb->status) {
+ case 0:
+ break;
+ default:
+ pr_err("%s:TX error (%d)\n", __func__, urb->status);
+ }
+
+ usb_mark_last_busy(urb->dev);
+ ret = pm_runtime_put_autosuspend(&urb->dev->dev);
+ if (ret < 0)
+ pr_debug("%s pm_runtime_put_autosuspend failed : ret(%d)\n", __func__, ret);
+ usb_free_urb(urb);
+ dev_kfree_skb_any(skb);
+}
+
+static void if_usb_force_disconnect(struct work_struct *work)
+{
+ struct usb_link_device *usb_ld =
+ container_of(work, struct usb_link_device, disconnect_work);
+ struct usb_device *udev = usb_ld->usbdev;
+
+ pm_runtime_get_sync(&udev->dev);
+ if (udev->state != USB_STATE_NOTATTACHED) {
+ usb_force_disconnect(udev);
+ pr_info("force disconnect by modem not responding!!\n");
+ }
+ pm_runtime_put_autosuspend(&udev->dev);
+}
+
+static void
+usb_change_modem_state(struct usb_link_device *usb_ld, enum modem_state state)
+{
+ struct io_device *iod;
+
+ list_for_each_entry(iod, &usb_ld->list_of_io_devices, list) {
+ if (iod->format == IPC_FMT) {
+ iod->modem_state_changed(iod, state);
+ return;
+ }
+ }
+}
+
+static int usb_tx_urb_with_skb(struct usb_link_device *usb_ld,
+ struct sk_buff *skb, struct if_usb_devdata *pipe_data)
+{
+ int ret, cnt = 0;
+ struct urb *urb;
+ struct usb_device *usbdev = usb_ld->usbdev;
+ unsigned long flags;
+
+ if (!usbdev || (usbdev->state == USB_STATE_NOTATTACHED) ||
+ usb_ld->host_wake_timeout_flag)
+ return -ENODEV;
+
+ pm_runtime_get_noresume(&usbdev->dev);
+
+ if (usbdev->dev.power.runtime_status == RPM_SUSPENDED ||
+ usbdev->dev.power.runtime_status == RPM_SUSPENDING) {
+ usb_ld->resume_status = AP_INITIATED_RESUME;
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 1);
+
+ while (!wait_event_interruptible_timeout(usb_ld->l2_wait,
+ usbdev->dev.power.runtime_status == RPM_ACTIVE ||
+ pipe_data->disconnected,
+ HOST_WAKEUP_TIMEOUT_JIFFIES)) {
+
+ if (cnt == MAX_RETRY) {
+ pr_err("host wakeup timeout !!\n");
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 0);
+ pm_runtime_put_autosuspend(&usbdev->dev);
+ schedule_work(&usb_ld->disconnect_work);
+ usb_ld->host_wake_timeout_flag = 1;
+ return -1;
+ }
+ pr_err("host wakeup timeout ! retry..\n");
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 0);
+ udelay(100);
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 1);
+ cnt++;
+ }
+
+ if (pipe_data->disconnected) {
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 0);
+ pm_runtime_put_autosuspend(&usbdev->dev);
+ return -ENODEV;
+ }
+
+ pr_debug("wait_q done (runtime_status=%d)\n",
+ usbdev->dev.power.runtime_status);
+ }
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ pr_err("%s alloc urb error\n", __func__);
+ if (pm_runtime_put_autosuspend(&usbdev->dev) < 0)
+ pr_debug("pm_runtime_put_autosuspend fail\n");
+ return -ENOMEM;
+ }
+
+ urb->transfer_flags = URB_ZERO_PACKET;
+ usb_fill_bulk_urb(urb, usbdev, pipe_data->tx_pipe, skb->data,
+ skb->len, usb_tx_complete, (void *)skb);
+
+ spin_lock_irqsave(&usb_ld->lock, flags);
+ if (atomic_read(&usb_ld->suspend_count)) {
+ /* transmission will be done in resume */
+ usb_anchor_urb(urb, &usb_ld->deferred);
+ usb_put_urb(urb);
+ pr_debug("%s: anchor urb (0x%p)\n", __func__, urb);
+ spin_unlock_irqrestore(&usb_ld->lock, flags);
+ return 0;
+ }
+ spin_unlock_irqrestore(&usb_ld->lock, flags);
+
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0) {
+ pr_err("%s usb_submit_urb with ret(%d)\n", __func__, ret);
+ if (pm_runtime_put_autosuspend(&usbdev->dev) < 0)
+ pr_debug("pm_runtime_put_autosuspend fail\n");
+ }
+ return ret;
+}
+
+static void usb_tx_work(struct work_struct *work)
+{
+ int ret = 0;
+ struct link_device *ld =
+ container_of(work, struct link_device, tx_delayed_work.work);
+ struct usb_link_device *usb_ld = to_usb_link_device(ld);
+ struct io_device *iod;
+ struct sk_buff *skb;
+ struct if_usb_devdata *pipe_data;
+
+ while (ld->sk_fmt_tx_q.qlen || ld->sk_raw_tx_q.qlen) {
+ /* send skb from fmt_txq and raw_txq,
+ * one by one for fair flow control */
+ skb = skb_dequeue(&ld->sk_fmt_tx_q);
+ if (skb) {
+ iod = *((struct io_device **)skb->cb);
+ switch (iod->format) {
+ case IPC_BOOT:
+ case IPC_RAMDUMP:
+ case IPC_FMT:
+ /* boot device uses same intf with fmt*/
+ pipe_data = &usb_ld->devdata[IF_USB_FMT_EP];
+ break;
+ case IPC_RFS:
+ pipe_data = &usb_ld->devdata[IF_USB_RFS_EP];
+ break;
+ default:
+ /* wrong packet for fmt tx q , drop it */
+ dev_kfree_skb_any(skb);
+ continue;
+ }
+
+ ret = usb_tx_urb_with_skb(usb_ld, skb, pipe_data);
+ if (ret < 0) {
+ pr_err("%s usb_tx_urb_with_skb for iod(%d), ret(%d)\n",
+ __func__, iod->format, ret);
+ skb_queue_head(&ld->sk_fmt_tx_q, skb);
+ return;
+ }
+ }
+
+ skb = skb_dequeue(&ld->sk_raw_tx_q);
+ if (skb) {
+ pipe_data = &usb_ld->devdata[IF_USB_RAW_EP];
+ ret = usb_tx_urb_with_skb(usb_ld, skb, pipe_data);
+ if (ret < 0) {
+ pr_err("%s usb_tx_urb_with_skb for raw, ret(%d)\n",
+ __func__, ret);
+ skb_queue_head(&ld->sk_raw_tx_q, skb);
+ return;
+ }
+ }
+ }
+}
+
+static int if_usb_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct usb_link_device *usb_ld = usb_get_intfdata(intf);
+ int i;
+
+ if (atomic_inc_return(&usb_ld->suspend_count) == IF_USB_DEVNUM_MAX) {
+ pr_debug("%s\n", __func__);
+
+ for (i = 0; i < IF_USB_DEVNUM_MAX; i++)
+ usb_kill_anchored_urbs(&usb_ld->devdata[i].reading);
+
+ wake_unlock(&usb_ld->susplock);
+ }
+
+ return 0;
+}
+
+static void runtime_pm_work(struct work_struct *work)
+{
+ struct usb_link_device *usb_ld =
+ container_of(work, struct usb_link_device, runtime_pm_work.work);
+ int ret;
+
+ ret = pm_request_autosuspend(&usb_ld->usbdev->dev);
+
+ if (ret == -EAGAIN || ret == 1)
+ queue_delayed_work(system_nrt_wq, &usb_ld->runtime_pm_work,
+ msecs_to_jiffies(50));
+}
+
+static void wait_enumeration_work(struct work_struct *work)
+{
+ struct usb_link_device *usb_ld =
+ container_of(work, struct usb_link_device, wait_enumeration.work);
+ if (usb_ld->if_usb_connected == 0) {
+ pr_err("USB disconnected and not enumerated for long time\n");
+ usb_change_modem_state(usb_ld, STATE_CRASH_EXIT);
+ }
+}
+
+static int if_usb_resume(struct usb_interface *intf)
+{
+ int i, ret;
+ struct sk_buff *skb;
+ struct usb_link_device *usb_ld = usb_get_intfdata(intf);
+ struct if_usb_devdata *pipe;
+ struct urb *urb;
+
+ spin_lock_irq(&usb_ld->lock);
+ if (!atomic_dec_return(&usb_ld->suspend_count)) {
+ spin_unlock_irq(&usb_ld->lock);
+
+ pr_debug("%s\n", __func__);
+ wake_lock(&usb_ld->susplock);
+
+ /* HACK: Runtime pm does not allow requesting autosuspend from
+ * resume callback, delayed it after resume */
+ queue_delayed_work(system_nrt_wq, &usb_ld->runtime_pm_work,
+ msecs_to_jiffies(50));
+
+ for (i = 0; i < IF_USB_DEVNUM_MAX; i++) {
+ pipe = &usb_ld->devdata[i];
+ while ((urb = usb_get_from_anchor(&pipe->urbs))) {
+ ret = usb_rx_submit(pipe, urb, GFP_KERNEL);
+ if (ret < 0) {
+ usb_put_urb(urb);
+ pr_err("%s: usb_rx_submit error with (%d)\n",
+ __func__, ret);
+ return ret;
+ }
+ usb_put_urb(urb);
+ }
+ }
+
+ while ((urb = usb_get_from_anchor(&usb_ld->deferred))) {
+ pr_debug("%s: got urb (0x%p) from anchor & resubmit\n",
+ __func__, urb);
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0) {
+ pr_err("%s: resubmit failed\n", __func__);
+ skb = urb->context;
+ dev_kfree_skb_any(skb);
+ usb_free_urb(urb);
+ if (pm_runtime_put_autosuspend(&usb_ld->usbdev->dev) < 0)
+ pr_debug("pm_runtime_put_autosuspend fail\n");
+ }
+ }
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 1);
+ udelay(100);
+ SET_SLAVE_WAKEUP(usb_ld->pdata, 0);
+ return 0;
+ }
+
+ spin_unlock_irq(&usb_ld->lock);
+ return 0;
+}
+
+static int if_usb_reset_resume(struct usb_interface *intf)
+{
+ int ret;
+
+ pr_debug("%s\n", __func__);
+ ret = if_usb_resume(intf);
+ return ret;
+}
+
+static struct usb_device_id if_usb_ids[] = {
+ { USB_DEVICE(0x04e8, 0x6999), /* CMC221 LTE Modem */
+ /*.driver_info = 0,*/
+ },
+ { } /* terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, if_usb_ids);
+
+static struct usb_driver if_usb_driver;
+static void if_usb_disconnect(struct usb_interface *intf)
+{
+ struct usb_link_device *usb_ld = usb_get_intfdata(intf);
+ struct usb_device *usbdev = usb_ld->usbdev;
+ int dev_id = intf->altsetting->desc.bInterfaceNumber;
+ struct if_usb_devdata *pipe_data = &usb_ld->devdata[dev_id];
+
+ usb_set_intfdata(intf, NULL);
+
+ pipe_data->disconnected = 1;
+ smp_wmb();
+
+ wake_up(&usb_ld->l2_wait);
+
+ if (usb_ld->if_usb_connected) {
+ disable_irq_wake(usb_ld->pdata->irq_host_wakeup);
+ free_irq(usb_ld->pdata->irq_host_wakeup, usb_ld);
+ }
+ usb_ld->if_usb_connected = 0;
+ usb_ld->flow_suspend = 1;
+
+ dev_dbg(&usbdev->dev, "%s\n", __func__);
+ usb_ld->dev_count--;
+ usb_driver_release_interface(&if_usb_driver, pipe_data->data_intf);
+
+ usb_kill_anchored_urbs(&pipe_data->reading);
+ usb_free_urbs(usb_ld, pipe_data);
+
+ if (usb_ld->dev_count == 0) {
+ cancel_delayed_work_sync(&usb_ld->runtime_pm_work);
+ cancel_delayed_work_sync(&usb_ld->ld.tx_delayed_work);
+ usb_put_dev(usbdev);
+ usb_ld->usbdev = NULL;
+ schedule_delayed_work(&usb_ld->wait_enumeration,
+ WAIT_ENUMURATION_TIMEOUT_JIFFIES);
+ }
+}
+
+static int __devinit if_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_host_interface *data_desc;
+ struct usb_link_device *usb_ld =
+ (struct usb_link_device *)id->driver_info;
+ struct link_device *ld = &usb_ld->ld;
+ struct usb_interface *data_intf;
+ struct usb_device *usbdev = interface_to_usbdev(intf);
+ struct device *dev;
+ struct if_usb_devdata *pipe;
+ struct urb *urb;
+ int i;
+ int j;
+ int dev_id;
+ int err;
+
+ /* To detect usb device order probed */
+ dev_id = intf->cur_altsetting->desc.bInterfaceNumber;
+
+ if (dev_id >= IF_USB_DEVNUM_MAX) {
+ dev_err(&intf->dev, "Device id %d cannot support\n",
+ dev_id);
+ return -EINVAL;
+ }
+
+ if (!usb_ld) {
+ dev_err(&intf->dev,
+ "if_usb device doesn't be allocated\n");
+ err = ENOMEM;
+ goto out;
+ }
+
+ pr_info("%s: probe dev_id=%d usb_device_id(0x%p), usb_ld (0x%p)\n",
+ __func__, dev_id, id, usb_ld);
+
+ usb_ld->usbdev = usbdev;
+
+ usb_get_dev(usbdev);
+
+ for (i = 0; i < IF_USB_DEVNUM_MAX; i++) {
+ data_intf = usb_ifnum_to_if(usbdev, i);
+
+ /* remap endpoint of RAW to no.1 for LTE modem */
+ if (i == 0)
+ pipe = &usb_ld->devdata[1];
+ else if (i == 1)
+ pipe = &usb_ld->devdata[0];
+ else
+ pipe = &usb_ld->devdata[i];
+
+ pipe->disconnected = 0;
+ pipe->data_intf = data_intf;
+ data_desc = data_intf->cur_altsetting;
+
+ /* Endpoints */
+ if (usb_pipein(data_desc->endpoint[0].desc.bEndpointAddress)) {
+ pipe->rx_pipe = usb_rcvbulkpipe(usbdev,
+ data_desc->endpoint[0].desc.bEndpointAddress);
+ pipe->tx_pipe = usb_sndbulkpipe(usbdev,
+ data_desc->endpoint[1].desc.bEndpointAddress);
+ pipe->rx_buf_size = 1024*4;
+ } else {
+ pipe->rx_pipe = usb_rcvbulkpipe(usbdev,
+ data_desc->endpoint[1].desc.bEndpointAddress);
+ pipe->tx_pipe = usb_sndbulkpipe(usbdev,
+ data_desc->endpoint[0].desc.bEndpointAddress);
+ pipe->rx_buf_size = 1024*4;
+ }
+
+ if (i == 0) {
+ dev_info(&usbdev->dev, "USB IF USB device found\n");
+ } else {
+ err = usb_driver_claim_interface(&if_usb_driver,
+ data_intf, usb_ld);
+ if (err < 0) {
+ pr_err("%s - failed to cliam usb interface\n",
+ __func__);
+ goto out;
+ }
+ }
+
+ usb_set_intfdata(data_intf, usb_ld);
+ usb_ld->dev_count++;
+ pm_suspend_ignore_children(&data_intf->dev, true);
+
+ for (j = 0; j < URB_COUNT; j++) {
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ pr_err("%s: alloc urb fail\n", __func__);
+ err = -ENOMEM;
+ goto out2;
+ }
+
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ urb->transfer_buffer = usb_alloc_coherent(usbdev,
+ pipe->rx_buf_size, GFP_KERNEL, &urb->transfer_dma);
+ if (!urb->transfer_buffer) {
+ pr_err("%s: Failed to allocate transfer buffer\n", __func__);
+ usb_free_urb(urb);
+ err = -ENOMEM;
+ goto out2;
+ }
+
+ usb_fill_bulk_urb(urb, usbdev, pipe->rx_pipe,
+ urb->transfer_buffer, pipe->rx_buf_size,
+ usb_rx_complete, pipe);
+ usb_anchor_urb(urb, &pipe->urbs);
+ }
+ }
+
+ /* temporary call reset_resume */
+ atomic_set(&usb_ld->suspend_count, 1);
+ if_usb_reset_resume(data_intf);
+ atomic_set(&usb_ld->suspend_count, 0);
+
+ SET_HOST_ACTIVE(usb_ld->pdata, 1);
+ usb_ld->host_wake_timeout_flag = 0;
+
+ if (gpio_get_value(usb_ld->pdata->gpio_phone_active)) {
+ pm_runtime_set_autosuspend_delay(
+ &usbdev->dev, AUTOSUSPEND_DELAY_MS);
+ dev = &usb_ld->usbdev->dev;
+ if (dev->parent) {
+ dev_dbg(&usbdev->dev, "if_usb Runtime PM Start!!\n");
+ usb_enable_autosuspend(usb_ld->usbdev);
+ }
+ usb_ld->if_usb_connected = 1;
+ usb_ld->flow_suspend = 0;
+ err = request_threaded_irq(usb_ld->pdata->irq_host_wakeup,
+ NULL,
+ usb_resume_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "modem_usb_wake",
+ usb_ld);
+ if (err)
+ pr_err("Failed to allocate an interrupt(%d)\n",
+ usb_ld->pdata->irq_host_wakeup);
+
+ enable_irq_wake(usb_ld->pdata->irq_host_wakeup);
+
+ /* Queue work if skbs were pending before a disconnect/probe */
+ if (ld->sk_fmt_tx_q.qlen || ld->sk_raw_tx_q.qlen)
+ queue_delayed_work(ld->tx_wq, &ld->tx_delayed_work, 0);
+
+ usb_change_modem_state(usb_ld, STATE_ONLINE);
+ } else {
+ usb_change_modem_state(usb_ld, STATE_LOADER_DONE);
+ }
+
+ return 0;
+
+out2:
+ usb_ld->dev_count--;
+ for (i = 0; i < IF_USB_DEVNUM_MAX; i++)
+ usb_free_urbs(usb_ld, &usb_ld->devdata[i]);
+out:
+ usb_set_intfdata(intf, NULL);
+ return err;
+}
+
+static irqreturn_t usb_resume_irq(int irq, void *data)
+{
+ int ret;
+ struct usb_link_device *usb_ld = data;
+ int val;
+ struct device *dev;
+
+ val = gpio_get_value(usb_ld->pdata->gpio_host_wakeup);
+ irq_set_irq_type(irq, val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH);
+ dev = &usb_ld->usbdev->dev;
+ wake_lock_timeout(&usb_ld->gpiolock, 100);
+
+ pr_debug("< H-WUP %d\n", val);
+
+ if (val == usb_ld->wake_status) {
+ pr_err("%s: Received spurious wake irq: %d", __func__, val);
+ return IRQ_HANDLED;
+ }
+
+ usb_ld->wake_status = val;
+
+ if (val) {
+ device_lock(dev);
+ if (dev->power.is_prepared || dev->power.is_suspended) {
+ pm_runtime_get_noresume(dev);
+ ret = 0;
+ } else {
+ ret = pm_runtime_get_sync(dev);
+ }
+ device_unlock(dev);
+ if (ret < 0) {
+ pr_err("%s pm_runtime_get fail (%d)\n", __func__, ret);
+ return IRQ_HANDLED;
+ }
+ } else {
+ if (usb_ld->resume_status == AP_INITIATED_RESUME)
+ wake_up(&usb_ld->l2_wait);
+ usb_ld->resume_status = CP_INITIATED_RESUME;
+ pm_runtime_mark_last_busy(&usb_ld->usbdev->dev);
+ pm_runtime_put_autosuspend(&usb_ld->usbdev->dev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int if_usb_init(struct usb_link_device *usb_ld)
+{
+ int ret;
+ int i;
+ struct if_usb_devdata *pipe;
+
+ /* give it to probe, or global variable needed */
+ if_usb_ids[0].driver_info = (unsigned long)usb_ld;
+
+ for (i = 0; i < IF_USB_DEVNUM_MAX; i++) {
+ pipe = &usb_ld->devdata[i];
+ pipe->format = i;
+ pipe->disconnected = 1;
+ init_usb_anchor(&pipe->urbs);
+ init_usb_anchor(&pipe->reading);
+ }
+
+ init_waitqueue_head(&usb_ld->l2_wait);
+ init_usb_anchor(&usb_ld->deferred);
+
+ ret = usb_register(&if_usb_driver);
+ if (ret) {
+ pr_err("usb_register_driver() fail : %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+struct link_device *usb_create_link_device(void *data)
+{
+ int ret;
+ struct modem_data *pdata;
+ struct platform_device *pdev = (struct platform_device *)data;
+ struct usb_link_device *usb_ld;
+ struct link_device *ld;
+
+ pdata = pdev->dev.platform_data;
+
+ usb_ld = kzalloc(sizeof(struct usb_link_device), GFP_KERNEL);
+ if (!usb_ld)
+ return NULL;
+
+ INIT_LIST_HEAD(&usb_ld->list_of_io_devices);
+ skb_queue_head_init(&usb_ld->ld.sk_fmt_tx_q);
+ skb_queue_head_init(&usb_ld->ld.sk_raw_tx_q);
+ spin_lock_init(&usb_ld->lock);
+
+ ld = &usb_ld->ld;
+ usb_ld->pdata = pdata;
+
+ ld->name = "usb";
+ ld->attach = usb_attach_io_dev;
+ ld->init_comm = usb_init_communication;
+ ld->terminate_comm = usb_terminate_communication;
+ ld->send = usb_send;
+ ld->com_state = COM_NONE;
+
+ /*ld->tx_wq = create_singlethread_workqueue("usb_tx_wq");*/
+ ld->tx_wq = alloc_workqueue("usb_tx_wq",
+ WQ_HIGHPRI | WQ_UNBOUND | WQ_RESCUER, 1);
+
+ if (!ld->tx_wq) {
+ pr_err("fail to create work Q.\n");
+ return NULL;
+ }
+
+ usb_ld->pdata->irq_host_wakeup = platform_get_irq(pdev, 1);
+ wake_lock_init(&usb_ld->gpiolock, WAKE_LOCK_SUSPEND, "modem_usb_gpio_wake");
+ wake_lock_init(&usb_ld->susplock, WAKE_LOCK_SUSPEND, "modem_usb_suspend_block");
+
+ INIT_DELAYED_WORK(&ld->tx_delayed_work, usb_tx_work);
+ INIT_DELAYED_WORK(&usb_ld->runtime_pm_work, runtime_pm_work);
+ INIT_DELAYED_WORK(&usb_ld->wait_enumeration, wait_enumeration_work);
+ INIT_WORK(&usb_ld->disconnect_work, if_usb_force_disconnect);
+
+ ret = if_usb_init(usb_ld);
+ if (ret)
+ return NULL;
+
+ return ld;
+}
+
+static struct usb_driver if_usb_driver = {
+ .name = "if_usb_driver",
+ .probe = if_usb_probe,
+ .disconnect = if_usb_disconnect,
+ .id_table = if_usb_ids,
+ .suspend = if_usb_suspend,
+ .resume = if_usb_resume,
+ .reset_resume = if_usb_reset_resume,
+ .supports_autosuspend = 1,
+};
+
+static void __exit if_usb_exit(void)
+{
+ usb_deregister(&if_usb_driver);
+}
+
+
+static int lte_wake_resume(struct device *pdev)
+{
+ struct modem_data *pdata = pdev->platform_data;
+ int val;
+
+ val = gpio_get_value(pdata->gpio_host_wakeup);
+ if (!val) {
+ pr_debug("%s: > S-WUP 1\n", __func__);
+ gpio_set_value(pdata->gpio_slave_wakeup, 1);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops lte_wake_pm_ops = {
+ .resume = lte_wake_resume,
+};
+
+static struct platform_driver lte_wake_driver = {
+ .driver = {
+ .name = "modem_lte_wake",
+ .pm = &lte_wake_pm_ops,
+ },
+};
+
+static int __init lte_wake_init(void)
+{
+ return platform_driver_register(&lte_wake_driver);
+}
+module_init(lte_wake_init);
diff --git a/drivers/misc/modem_if/modem_link_device_usb.h b/drivers/misc/modem_if/modem_link_device_usb.h
new file mode 100644
index 0000000..1a5bdd9
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_usb.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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 __MODEM_LINK_DEVICE_USB_H__
+#define __MODEM_LINK_DEVICE_USB_H__
+
+#include <linux/usb.h>
+#include <linux/wakelock.h>
+
+#define IF_USB_DEVNUM_MAX 3
+
+#define IF_USB_FMT_EP 0
+#define IF_USB_RAW_EP 1
+#define IF_USB_RFS_EP 2
+
+#define AUTOSUSPEND_DELAY_MS 500
+#define HOST_WAKEUP_TIMEOUT_JIFFIES msecs_to_jiffies(500)
+#define WAIT_ENUMURATION_TIMEOUT_JIFFIES msecs_to_jiffies(15000)
+#define MAX_RETRY 3
+
+enum RESUME_STATUS {
+ CP_INITIATED_RESUME,
+ AP_INITIATED_RESUME,
+};
+
+struct if_usb_devdata {
+ struct usb_interface *data_intf;
+ unsigned int tx_pipe;
+ unsigned int rx_pipe;
+ u8 disconnected;
+
+ int format;
+ struct usb_anchor urbs;
+ struct usb_anchor reading;
+ unsigned int rx_buf_size;
+};
+
+struct usb_link_device {
+ /*COMMON LINK DEVICE*/
+ struct link_device ld;
+
+ struct modem_data *pdata;
+
+ /*USB SPECIFIC LINK DEVICE*/
+ struct usb_device *usbdev;
+ struct if_usb_devdata devdata[IF_USB_DEVNUM_MAX];
+ struct delayed_work runtime_pm_work;
+ struct delayed_work wait_enumeration;
+ struct work_struct disconnect_work;
+
+ struct wake_lock gpiolock;
+ struct wake_lock susplock;
+
+ unsigned int dev_count;
+ unsigned int suspended;
+ atomic_t suspend_count;
+ enum RESUME_STATUS resume_status;
+ int if_usb_connected;
+ int flow_suspend;
+ int host_wake_timeout_flag;
+ int wake_status;
+
+ unsigned gpio_slave_wakeup;
+ unsigned gpio_host_wakeup;
+ unsigned gpio_host_active;
+ int irq_host_wakeup;
+ struct delayed_work dwork;
+ struct work_struct resume_work;
+ int cpcrash_flag;
+ wait_queue_head_t l2_wait;
+
+ spinlock_t lock;
+ struct usb_anchor deferred;
+
+ /*COMMON LINK DEVICE*/
+ /* maybe -list of io devices for the link device to use */
+ /* to find where to send incoming packets to */
+ struct list_head list_of_io_devices;
+};
+/* converts from struct link_device* to struct xxx_link_device* */
+#define to_usb_link_device(linkdev) \
+ container_of(linkdev, struct usb_link_device, ld)
+#endif
+
+#define SET_SLAVE_WAKEUP(_pdata, _value) \
+do { \
+ gpio_set_value(_pdata->gpio_slave_wakeup, _value); \
+ pr_debug("> S-WUP %s\n", _value ? "1" : "0"); \
+} while (0)
+
+#define SET_HOST_ACTIVE(_pdata, _value) \
+do { \
+ gpio_set_value(_pdata->gpio_host_active, _value); \
+ pr_debug("> H-ACT %s\n", _value ? "1" : "0"); \
+} while (0)
+
diff --git a/drivers/misc/modem_if/modem_modemctl_device_cbp71.c b/drivers/misc/modem_if/modem_modemctl_device_cbp71.c
new file mode 100755
index 0000000..fcb1c12
--- /dev/null
+++ b/drivers/misc/modem_if/modem_modemctl_device_cbp71.c
@@ -0,0 +1,234 @@
+/* /linux/drivers/misc/modem_if/modem_modemctl_device_cbp7.1.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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/init.h>
+
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include "modem_link_device_dpram.h"
+
+#define PIF_TIMEOUT (180 * HZ)
+#define DPRAM_INIT_TIMEOUT (15 * HZ)
+
+static int cbp71_on(struct modem_ctl *mc)
+{
+ int ret;
+ struct dpram_link_device *dpram_ld =
+ to_dpram_link_device(mc->iod->link);
+
+ pr_info("[MODEM_IF] cbp71_on()\n");
+
+ if (!mc->gpio_cp_off || !mc->gpio_cp_reset) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ gpio_set_value(mc->gpio_cp_off, 1);
+ msleep(600);
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ gpio_set_value(mc->gpio_cp_off, 0);
+
+ msleep(300);
+ gpio_set_value(mc->gpio_pda_active, 1);
+
+ mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
+
+ /* Wait here until the PHONE is up.
+ * Waiting as the this called from IOCTL->UM thread */
+ pr_debug("[MODEM_IF] power control waiting for INT_MASK_CMD_PIF_INIT_DONE\n");
+
+ dpram_ld->clear_interrupt(dpram_ld);
+
+ ret = wait_for_completion_interruptible_timeout(
+ &dpram_ld->dpram_init_cmd, DPRAM_INIT_TIMEOUT);
+ if (!ret) {
+ /* ret will be 0 on timeout, < zero if interrupted */
+ pr_warn("[MODEM_IF] INIT_START cmd was not arrived.\n");
+ pr_warn("init_cmd_wait_condition is 0 and wait timeout happend\n");
+ return -ENXIO;
+ }
+
+ ret = wait_for_completion_interruptible_timeout(
+ &dpram_ld->modem_pif_init_done, PIF_TIMEOUT);
+ if (!ret) {
+ pr_warn("[MODEM_IF] PIF init failed\n");
+ pr_warn("pif_init_wait_condition is 0 and wait timeout happend\n");
+ return -ENXIO;
+ }
+
+ pr_debug("[MODEM_IF] complete cbp71_on\n");
+
+ mc->iod->modem_state_changed(mc->iod, STATE_ONLINE);
+
+ return 0;
+}
+
+static int cbp71_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] cbp71_off()\n");
+
+ if (!mc->gpio_cp_off || !mc->gpio_cp_reset) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_off, 1);
+ gpio_set_value(mc->gpio_cp_reset, 0);
+
+ mc->iod->modem_state_changed(mc->iod, STATE_OFFLINE);
+
+ return 0;
+}
+
+static int cbp71_reset(struct modem_ctl *mc)
+{
+ int ret = 0;
+
+ pr_debug("[MODEM_IF] cbp71_reset()\n");
+
+ ret = cbp71_off(mc);
+ if (ret)
+ return -ENXIO;
+
+ msleep(100);
+
+ ret = cbp71_on(mc);
+ if (ret)
+ return -ENXIO;
+
+ return 0;
+}
+
+static int cbp71_boot_on(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] cbp71_boot_on()\n");
+
+ if (!mc->gpio_cp_reset) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ msleep(600);
+ gpio_set_value(mc->gpio_cp_reset, 1);
+
+ mc->iod->modem_state_changed(mc->iod, STATE_BOOTING);
+
+ return 0;
+}
+
+static int cbp71_boot_off(struct modem_ctl *mc)
+{
+ pr_debug("[MODEM_IF] cbp71_boot_off()\n");
+ return 0;
+}
+
+static irqreturn_t phone_active_irq_handler(int irq, void *_mc)
+{
+ int phone_reset = 0;
+ int phone_active_value = 0;
+ int phone_state = 0;
+ struct modem_ctl *mc = (struct modem_ctl *)_mc;
+
+ if (!mc->gpio_cp_reset || !mc->gpio_phone_active) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return IRQ_HANDLED;
+ }
+
+ phone_reset = gpio_get_value(mc->gpio_cp_reset);
+ phone_active_value = gpio_get_value(mc->gpio_phone_active);
+
+ if (phone_reset && phone_active_value) {
+ phone_state = STATE_ONLINE;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, phone_state);
+ } else if (phone_reset && !phone_active_value) {
+ if (mc->phone_state == STATE_ONLINE) {
+ phone_state = STATE_CRASH_EXIT;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod,
+ phone_state);
+ }
+ } else {
+ phone_state = STATE_OFFLINE;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, phone_state);
+ }
+
+ if (phone_active_value)
+ irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_LOW);
+ else
+ irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_HIGH);
+
+ pr_info("phone_active_irq_handler : phone_state=%d\n", phone_state);
+
+ return IRQ_HANDLED;
+}
+
+static void cbp71_get_ops(struct modem_ctl *mc)
+{
+ mc->ops.modem_on = cbp71_on;
+ mc->ops.modem_off = cbp71_off;
+ mc->ops.modem_reset = cbp71_reset;
+ mc->ops.modem_boot_on = cbp71_boot_on;
+ mc->ops.modem_boot_off = cbp71_boot_off;
+}
+
+int cbp71_init_modemctl_device(struct modem_ctl *mc,
+ struct modem_data *pdata)
+{
+ int ret = 0;
+ struct platform_device *pdev;
+
+ mc->gpio_cp_on = pdata->gpio_cp_on;
+ mc->gpio_reset_req_n = pdata->gpio_reset_req_n;
+ mc->gpio_cp_reset = pdata->gpio_cp_reset;
+ mc->gpio_pda_active = pdata->gpio_pda_active;
+ mc->gpio_phone_active = pdata->gpio_phone_active;
+ mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
+ mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
+ mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset;
+ mc->gpio_cp_off = pdata->gpio_cp_off;
+
+ pdev = to_platform_device(mc->dev);
+ mc->irq_phone_active = platform_get_irq(pdev, 0);
+
+ cbp71_get_ops(mc);
+
+ ret = request_irq(mc->irq_phone_active, phone_active_irq_handler,
+ IRQF_TRIGGER_HIGH, "phone_active", mc);
+ if (ret) {
+ pr_err("[MODEM_IF]failed to irq_phone_active request_irq: %d\n"
+ , ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(mc->irq_phone_active);
+ if (ret) {
+ pr_err("[MODEM_IF] %s: failed to enable_irq_wake:%d\n",
+ __func__, ret);
+ free_irq(mc->irq_phone_active, mc);
+ return ret;
+ }
+
+ return ret;
+}
diff --git a/drivers/misc/modem_if/modem_modemctl_device_cmc221.c b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c
new file mode 100644
index 0000000..97428e9
--- /dev/null
+++ b/drivers/misc/modem_if/modem_modemctl_device_cmc221.c
@@ -0,0 +1,254 @@
+/* /linux/drivers/misc/modem_if/modem_modemctl_device_cmc221.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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/init.h>
+
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+#include "modem_link_device_usb.h"
+
+static int cmc221_on(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s()\n", __func__);
+
+ if (!mc->gpio_cp_off || !mc->gpio_cp_on || !mc->gpio_cp_reset) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_on, 1);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_off, 0);
+ msleep(300);
+ mc->phone_state = STATE_BOOTING;
+ return 0;
+}
+
+static int cmc221_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s()\n", __func__);
+
+ if (!mc->gpio_cp_off || !mc->gpio_cp_on || !mc->gpio_cp_reset) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_off, 1);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_reset, 0);
+
+ mc->phone_state = STATE_OFFLINE;
+
+ return 0;
+}
+
+static int cmc221_force_crash_exit(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s: # %d\n", __func__, ++(mc->crash_cnt));
+
+ mc->phone_state = STATE_CRASH_EXIT;/* DUMP START */
+
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, mc->phone_state);
+
+ return 0;
+}
+
+static int cmc221_dump_reset(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s()\n", __func__);
+
+ if (!mc->gpio_cp_reset)
+ return -ENXIO;
+
+ gpio_set_value(mc->gpio_host_active, 0);
+ mc->cpcrash_flag = 1;
+
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ msleep(300);
+
+ mc->phone_state = STATE_BOOTING;
+
+ return 0;
+}
+
+static int cmc221_reset(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s()\n", __func__);
+
+ if (!mc->gpio_cp_reset)
+ return -ENXIO;
+
+ if (cmc221_off(mc))
+ return -ENXIO;
+ msleep(100);
+ if (cmc221_on(mc))
+ return -ENXIO;
+
+ mc->phone_state = STATE_BOOTING;
+
+ return 0;
+}
+
+static int cmc221_boot_on(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s()\n", __func__);
+ return 0;
+}
+
+static int cmc221_boot_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] %s()\n", __func__);
+ return 0;
+}
+
+static int cmc221_get_active(struct modem_ctl *mc)
+{
+ if (!mc->gpio_phone_active || !mc->gpio_cp_reset)
+ return -ENXIO;
+
+ pr_debug("cp %d phone %d\n",
+ gpio_get_value(mc->gpio_cp_reset),
+ gpio_get_value(mc->gpio_phone_active));
+
+ if (gpio_get_value(mc->gpio_cp_reset))
+ return gpio_get_value(mc->gpio_phone_active);
+
+ return 0;
+}
+
+
+static void mc_work(struct work_struct *work_arg)
+{
+
+ struct modem_ctl *mc = container_of(work_arg, struct modem_ctl,
+ dwork.work);
+
+ int phone_active;
+
+ phone_active = cmc221_get_active(mc);
+ if (phone_active < 0) {
+ pr_err("[MODEM_IF] gpio not initialized\n");
+ return;
+ }
+
+ switch (mc->phone_state) {
+ case STATE_CRASH_EXIT:
+ case STATE_BOOTING:
+ case STATE_LOADER_DONE:
+ if (phone_active) {
+ if (mc->cpcrash_flag) {
+ pr_info("[MODEM_IF] LTE DUMP END!!\n");
+ mc->cpcrash_flag = 0;
+ }
+ }
+ break;
+ case STATE_ONLINE:
+ if (!phone_active) {
+ pr_info("[MODEM_IF] LTE CRASHED!! LTE DUMP START!!\n");
+ mc->phone_state = STATE_CRASH_EXIT;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod,
+ mc->phone_state);
+ }
+ break;
+ default:
+ mc->phone_state = STATE_OFFLINE;
+ pr_err("[MODEM_IF], phone_status changed to invalid!!\n");
+ break;
+ }
+}
+
+
+
+static irqreturn_t phone_active_irq_handler(int irq, void *_mc)
+{
+ struct modem_ctl *mc = (struct modem_ctl *)_mc;
+
+ schedule_delayed_work(&mc->dwork, 20);
+
+ return IRQ_HANDLED;
+}
+
+static void cmc221_get_ops(struct modem_ctl *mc)
+{
+ mc->ops.modem_on = cmc221_on;
+ mc->ops.modem_off = cmc221_off;
+ mc->ops.modem_reset = cmc221_reset;
+ mc->ops.modem_boot_on = cmc221_boot_on;
+ mc->ops.modem_boot_off = cmc221_boot_off;
+ mc->ops.modem_force_crash_exit = cmc221_force_crash_exit;
+ mc->ops.modem_dump_reset = cmc221_dump_reset;
+}
+
+int cmc221_init_modemctl_device(struct modem_ctl *mc,
+ struct modem_data *pdata)
+{
+ int ret = 0;
+ struct platform_device *pdev;
+
+ mc->gpio_cp_on = pdata->gpio_cp_on;
+ mc->gpio_reset_req_n = pdata->gpio_reset_req_n;
+ mc->gpio_cp_reset = pdata->gpio_cp_reset;
+ mc->gpio_pda_active = pdata->gpio_pda_active;
+ mc->gpio_phone_active = pdata->gpio_phone_active;
+ mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
+ mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
+ mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset;
+ mc->gpio_cp_off = pdata->gpio_cp_off;
+ mc->gpio_slave_wakeup = pdata->gpio_slave_wakeup;
+ mc->gpio_host_active = pdata->gpio_host_active;
+ mc->gpio_host_wakeup = pdata->gpio_host_wakeup;
+
+ pdev = to_platform_device(mc->dev);
+ mc->irq_phone_active = platform_get_irq(pdev, 0);
+ mc->irq_host_wakeup = platform_get_irq(pdev, 1);
+
+ cmc221_get_ops(mc);
+
+ dev_set_drvdata(mc->dev, mc);
+
+ INIT_DELAYED_WORK(&mc->dwork, mc_work);
+
+ ret = request_irq(mc->irq_phone_active, phone_active_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "phone_active", mc);
+ if (ret) {
+ pr_err("[MODEM_IF] Failed to allocate an interrupt(%d)\n",
+ mc->irq_phone_active);
+ goto irq_fail;
+ }
+ mc->irq[0] = mc->irq_phone_active;
+ enable_irq_wake(mc->irq_phone_active);
+ /*disable_irq(mc->irq_phone_active);*/
+
+ return ret;
+
+irq_fail:
+ kfree(mc);
+ return ret;
+}
diff --git a/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c
new file mode 100755
index 0000000..c5e6cb7
--- /dev/null
+++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c
@@ -0,0 +1,213 @@
+/* /linux/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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/init.h>
+
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+
+#include <linux/platform_data/modem.h>
+#include "modem_prj.h"
+
+static int xmm6260_on(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] xmm6260_on()\n");
+
+ if (!mc->gpio_cp_reset || !mc->gpio_cp_on || !mc->gpio_reset_req_n) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+ gpio_set_value(mc->gpio_cp_reset, 0);
+ udelay(160);
+ gpio_set_value(mc->gpio_pda_active, 0);
+ msleep(100);
+ gpio_set_value(mc->gpio_cp_reset, 1);
+ udelay(160);
+ gpio_set_value(mc->gpio_reset_req_n, 1);
+ udelay(160);
+ gpio_set_value(mc->gpio_cp_on, 1);
+ udelay(60);
+ gpio_set_value(mc->gpio_cp_on, 0);
+ msleep(20);
+ gpio_set_value(mc->gpio_pda_active, 1);
+
+ mc->phone_state = STATE_BOOTING;
+
+ return 0;
+}
+
+static int xmm6260_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] xmm6260_off()\n");
+
+ if (!mc->gpio_cp_reset || !mc->gpio_cp_on) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_cp_on, 0);
+ gpio_set_value(mc->gpio_cp_reset, 0);
+
+ mc->phone_state = STATE_OFFLINE;
+
+ return 0;
+}
+
+
+static int xmm6260_reset(struct modem_ctl *mc)
+{
+ int ret;
+
+ pr_info("[MODEM_IF] xmm6260_reset()\n");
+
+ ret = xmm6260_off(mc);
+ if (ret)
+ return ret;
+
+ msleep(100);
+
+ ret = xmm6260_on(mc);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int xmm6260_boot_on(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] xmm6260_boot_on()\n");
+
+ if (!mc->gpio_flm_uart_sel) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_flm_uart_sel, 0);
+
+ return 0;
+}
+
+static int xmm6260_boot_off(struct modem_ctl *mc)
+{
+ pr_info("[MODEM_IF] xmm6260_boot_off()\n");
+
+ if (!mc->gpio_flm_uart_sel) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return -ENXIO;
+ }
+
+ gpio_set_value(mc->gpio_flm_uart_sel, 1);
+
+ return 0;
+}
+
+static irqreturn_t phone_active_irq_handler(int irq, void *_mc)
+{
+ int phone_reset = 0;
+ int phone_active_value = 0;
+ int phone_state = 0;
+ struct modem_ctl *mc = (struct modem_ctl *)_mc;
+
+ disable_irq_nosync(mc->irq_phone_active);
+
+ if (!mc->gpio_cp_reset || !mc->gpio_phone_active ||
+ !mc->gpio_cp_dump_int) {
+ pr_err("[MODEM_IF] no gpio data\n");
+ return IRQ_HANDLED;
+ }
+
+ phone_reset = gpio_get_value(mc->gpio_cp_reset);
+ phone_active_value = gpio_get_value(mc->gpio_phone_active);
+
+ pr_info("[MODEM_IF] PA EVENT : reset =%d, pa=%d\n",
+ phone_reset, phone_active_value);
+
+ if (phone_reset && phone_active_value) {
+ phone_state = STATE_ONLINE;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, phone_state);
+ } else if (phone_reset && !phone_active_value) {
+ if ((mc->phone_state == STATE_ONLINE) &&
+ (mc->iod->link->com_state == COM_ONLINE)) {
+ phone_state = STATE_CRASH_EXIT;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod,
+ phone_state);
+ }
+ } else {
+ phone_state = STATE_OFFLINE;
+ if (mc->iod && mc->iod->modem_state_changed)
+ mc->iod->modem_state_changed(mc->iod, phone_state);
+ }
+
+ if (phone_active_value)
+ irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_LOW);
+ else
+ irq_set_irq_type(mc->irq_phone_active, IRQ_TYPE_LEVEL_HIGH);
+ enable_irq(mc->irq_phone_active);
+
+ return IRQ_HANDLED;
+}
+
+static void xmm6260_get_ops(struct modem_ctl *mc)
+{
+ mc->ops.modem_on = xmm6260_on;
+ mc->ops.modem_off = xmm6260_off;
+ mc->ops.modem_reset = xmm6260_reset;
+ mc->ops.modem_boot_on = xmm6260_boot_on;
+ mc->ops.modem_boot_off = xmm6260_boot_off;
+}
+
+int xmm6260_init_modemctl_device(struct modem_ctl *mc,
+ struct modem_data *pdata)
+{
+ int ret;
+ struct platform_device *pdev;
+
+ mc->gpio_cp_on = pdata->gpio_cp_on;
+ mc->gpio_reset_req_n = pdata->gpio_reset_req_n;
+ mc->gpio_cp_reset = pdata->gpio_cp_reset;
+ mc->gpio_pda_active = pdata->gpio_pda_active;
+ mc->gpio_phone_active = pdata->gpio_phone_active;
+ mc->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
+ mc->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
+ mc->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset;
+
+ pdev = to_platform_device(mc->dev);
+ mc->irq_phone_active = platform_get_irq(pdev, 0);
+
+ xmm6260_get_ops(mc);
+
+ ret = request_irq(mc->irq_phone_active, phone_active_irq_handler,
+ IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH,
+ "phone_active", mc);
+ if (ret) {
+ pr_err("[MODEM_IF] %s: failed to request_irq:%d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = enable_irq_wake(mc->irq_phone_active);
+ if (ret)
+ pr_err("[MODEM_IF] %s: failed to enable_irq_wake:%d\n",
+ __func__, ret);
+
+ return ret;
+}
diff --git a/drivers/misc/modem_if/modem_net_flowcontrol_device.c b/drivers/misc/modem_if/modem_net_flowcontrol_device.c
new file mode 100755
index 0000000..9397edd
--- /dev/null
+++ b/drivers/misc/modem_if/modem_net_flowcontrol_device.c
@@ -0,0 +1,115 @@
+/* /linux/drivers/misc/modem_if/modem_net_flowcontrol_device.c
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2011 Samsung Electronics.
+ *
+ * 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/kernel.h>
+#include <linux/fs.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <linux/if_arp.h>
+#include <linux/platform_data/modem.h>
+
+#include "modem_prj.h"
+
+
+#define NET_FLOWCONTROL_DEV_NAME_LEN 8
+
+static int modem_net_flowcontrol_device_open(
+ struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static int modem_net_flowcontrol_device_release(
+ struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+static long modem_net_flowcontrol_device_ioctl(
+ struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ struct net *this_net;
+ struct net_device *ndev;
+ char dev_name[NET_FLOWCONTROL_DEV_NAME_LEN];
+ u8 chan;
+
+ if (copy_from_user(&chan, (void __user *)arg, sizeof(char)))
+ return -EFAULT;
+
+ if (chan > 15)
+ return -ENODEV;
+
+ snprintf(dev_name, NET_FLOWCONTROL_DEV_NAME_LEN, "rmnet%d", (int)chan);
+ this_net = get_net_ns_by_pid(current->pid);
+ ndev = __dev_get_by_name(this_net, dev_name);
+ if (ndev == NULL) {
+ pr_err("[MODEM_IF] %s: device = %s not exist\n", __func__,
+ dev_name);
+ return -ENODEV;
+ }
+
+ switch (cmd) {
+ case IOCTL_MODEM_NET_SUSPEND:
+ netif_stop_queue(ndev);
+ pr_info("[MODEM_IF] NET SUSPEND(%s)\n", dev_name);
+ break;
+ case IOCTL_MODEM_NET_RESUME:
+ netif_wake_queue(ndev);
+ pr_info("[MODEM_IF] NET RESUME(%s)\n", dev_name);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static const struct file_operations modem_net_flowcontrol_device_fops = {
+ .owner = THIS_MODULE,
+ .open = modem_net_flowcontrol_device_open,
+ .release = modem_net_flowcontrol_device_release,
+ .unlocked_ioctl = modem_net_flowcontrol_device_ioctl,
+};
+
+static int __init modem_net_flowcontrol_device_init(void)
+{
+ int ret = 0;
+ struct io_device *net_flowcontrol_dev;
+
+ net_flowcontrol_dev = kzalloc(sizeof(struct io_device), GFP_KERNEL);
+ if (!net_flowcontrol_dev) {
+ pr_err("[MODEM_IF] net_flowcontrol_dev io device memory alloc fail\n");
+ return -ENOMEM;
+ }
+
+ net_flowcontrol_dev->miscdev.minor = MISC_DYNAMIC_MINOR;
+ net_flowcontrol_dev->miscdev.name = "modem_br";
+ net_flowcontrol_dev->miscdev.fops = &modem_net_flowcontrol_device_fops;
+
+ ret = misc_register(&net_flowcontrol_dev->miscdev);
+ if (ret)
+ pr_err("[MODEM_IF] failed to register misc br device : %s\n",
+ net_flowcontrol_dev->miscdev.name);
+
+ return ret;
+}
+
+module_init(modem_net_flowcontrol_device_init);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Samsung Modem IF Net Flowcontrol Driver");
diff --git a/drivers/misc/modem_if/modem_prj.h b/drivers/misc/modem_if/modem_prj.h
new file mode 100755
index 0000000..e728bdb
--- /dev/null
+++ b/drivers/misc/modem_if/modem_prj.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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 __MODEM_PRJ_H__
+#define __MODEM_PRJ_H__
+
+#include <linux/wait.h>
+#include <linux/miscdevice.h>
+#include <linux/skbuff.h>
+
+
+#define MAX_LINK_DEVTYPE 3
+#define MAX_RAW_DEVS 32
+#define MAX_NUM_IO_DEV (MAX_RAW_DEVS + 4)
+
+#define IOCTL_MODEM_ON _IO('o', 0x19)
+#define IOCTL_MODEM_OFF _IO('o', 0x20)
+#define IOCTL_MODEM_RESET _IO('o', 0x21)
+#define IOCTL_MODEM_BOOT_ON _IO('o', 0x22)
+#define IOCTL_MODEM_BOOT_OFF _IO('o', 0x23)
+#define IOCTL_MODEM_START _IO('o', 0x24)
+
+#define IOCTL_MODEM_SEND _IO('o', 0x25)
+#define IOCTL_MODEM_RECV _IO('o', 0x26)
+
+#define IOCTL_MODEM_STATUS _IO('o', 0x27)
+#define IOCTL_MODEM_GOTA_START _IO('o', 0x28)
+#define IOCTL_MODEM_FW_UPDATE _IO('o', 0x29)
+
+#define IOCTL_MODEM_NET_SUSPEND _IO('o', 0x30)
+#define IOCTL_MODEM_NET_RESUME _IO('o', 0x31)
+#define IOCTL_MODEM_DUMP_START _IO('o', 0x32)
+#define IOCTL_MODEM_DUMP_UPDATE _IO('o', 0x33)
+#define IOCTL_MODEM_FORCE_CRASH_EXIT _IO('o', 0x34)
+#define IOCTL_MODEM_DUMP_RESET _IO('o', 0x35)
+
+#define IPC_HEADER_MAX_SIZE 6 /* fmt 3, raw 6, rfs 6 */
+
+#define PSD_DATA_CHID_BEGIN 0x2A
+#define PSD_DATA_CHID_END 0x38
+
+#define IP6VERSION 6
+
+#define SOURCE_MAC_ADDR {0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC}
+
+/* Does modem ctl structure will use state ? or status defined below ?*/
+/* Be careful!! below sequence shouldn't be changed*/
+enum modem_state {
+ STATE_OFFLINE,
+ __UNUSED__,
+ STATE_CRASH_EXIT,
+ STATE_BOOTING,
+ STATE_ONLINE,
+ STATE_NV_REBUILDING,
+ STATE_LOADER_DONE,
+};
+
+enum {
+ COM_NONE,
+ COM_ONLINE,
+ COM_HANDSHAKE,
+ COM_BOOT,
+ COM_CRASH,
+ COM_BOOT_EBL,
+};
+
+struct header_data {
+ char hdr[IPC_HEADER_MAX_SIZE];
+ unsigned len;
+ unsigned flag_len;
+ char start; /*hdlc start header 0x7F*/
+};
+
+/* buffer type for modem image */
+struct dpram_firmware {
+ char *firmware;
+ int size;
+ int is_delta;
+};
+
+
+struct vnet {
+ struct io_device *iod;
+};
+
+struct io_device {
+ struct list_head list;
+ char *name;
+
+ wait_queue_head_t wq;
+
+ struct miscdevice miscdev;
+ struct net_device *ndev;
+
+ /* ID and Format for channel on the link */
+ unsigned id;
+ enum dev_format format;
+ enum modem_io io_typ;
+ enum modem_network net_typ;
+
+ struct sk_buff_head sk_rx_q;
+
+ /* work for each io device, when delayed work needed
+ * use this for private io device rx action
+ */
+ struct delayed_work rx_work;
+
+ /* for fragmentation data from link device */
+ struct sk_buff *skb_recv;
+ struct header_data h_data;
+
+ /* called from linkdevice when a packet arrives for this iodevice */
+ int (*recv)(struct io_device *iod, const char *data, unsigned int len);
+
+ /* inform the IO device that the modem is now online or offline or
+ * crashing or whatever...
+ */
+ void (*modem_state_changed)(struct io_device *iod, enum modem_state);
+
+ struct link_device *link;
+ struct modem_ctl *mc;
+
+ void *private_data;
+};
+#define to_io_device(misc) container_of(misc, struct io_device, miscdev)
+
+struct io_raw_devices {
+ struct io_device *raw_devices[MAX_RAW_DEVS];
+ int num_of_raw_devs;
+};
+
+struct link_device {
+ char *name;
+
+ struct sk_buff_head sk_fmt_tx_q;
+ struct sk_buff_head sk_raw_tx_q;
+
+ struct workqueue_struct *tx_wq;
+ struct workqueue_struct *tx_raw_wq;
+ struct work_struct tx_work;
+ struct delayed_work tx_delayed_work;
+
+ unsigned com_state;
+
+ /* called during init to associate an io device with this link */
+ int (*attach)(struct link_device *ld, struct io_device *iod);
+
+ /* init communication - setting link driver */
+ int (*init_comm)(struct link_device *ld, struct io_device *iod);
+ /* terminate communication */
+ void (*terminate_comm)(struct link_device *ld, struct io_device *iod);
+
+ /* called by an io_device when it has a packet to send over link
+ * - the io device is passed so the link device can look at id and
+ * format fields to determine how to route/format the packet
+ */
+ int (*send)(struct link_device *ld, struct io_device *iod,
+ struct sk_buff *skb);
+
+ int (*gota_start)(struct link_device *ld, struct io_device *iod);
+ int (*dump_start)(struct link_device *ld, struct io_device *iod);
+
+ int (*modem_update)(
+ struct link_device *ld,
+ struct io_device *iod,
+ unsigned long arg);
+ int (*dump_update)(
+ struct link_device *ld,
+ struct io_device *iod,
+ unsigned long arg);
+};
+
+struct modemctl_ops {
+ int (*modem_on) (struct modem_ctl *);
+ int (*modem_off) (struct modem_ctl *);
+ int (*modem_reset) (struct modem_ctl *);
+ int (*modem_boot_on) (struct modem_ctl *);
+ int (*modem_boot_off) (struct modem_ctl *);
+ int (*modem_force_crash_exit) (struct modem_ctl *);
+ int (*modem_dump_reset) (struct modem_ctl *);
+};
+
+struct modem_ctl {
+ struct device *dev;
+ char *name;
+
+ int phone_state;
+
+ unsigned gpio_cp_on;
+ unsigned gpio_reset_req_n;
+ unsigned gpio_cp_reset;
+ unsigned gpio_pda_active;
+ unsigned gpio_phone_active;
+ unsigned gpio_cp_dump_int;
+ unsigned gpio_flm_uart_sel;
+ unsigned gpio_cp_warm_reset;
+ unsigned gpio_cp_off;
+
+ int irq_phone_active;
+
+ struct work_struct work;
+
+#ifdef CONFIG_LTE_MODEM_CMC221
+ const struct attribute_group *group;
+ unsigned gpio_slave_wakeup;
+ unsigned gpio_host_wakeup;
+ unsigned gpio_host_active;
+ int irq_host_wakeup;
+ struct delayed_work dwork;
+ struct work_struct resume_work;
+ int wakeup_flag; /*flag for CP boot GPIO sync flag*/
+ int cpcrash_flag;
+ int crash_cnt;
+ struct completion *l2_done;
+ int irq[3];
+#endif /*CONFIG_LTE_MODEM_CMC221*/
+
+ struct modemctl_ops ops;
+ struct io_device *iod;
+};
+
+int init_io_device(struct io_device *iod);
+
+#endif
diff --git a/drivers/misc/modem_if/modem_variation.h b/drivers/misc/modem_if/modem_variation.h
new file mode 100755
index 0000000..79b9240
--- /dev/null
+++ b/drivers/misc/modem_if/modem_variation.h
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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 __MODEM_VARIATION_H__
+#define __MODEM_VARIATION_H__
+
+#define DECLARE_MODEM_INIT(type) \
+ int type ## _init_modemctl_device(struct modem_ctl *mc, \
+ struct modem_data *pdata)
+#define DECLARE_MODEM_INIT_DUMMY(type) \
+ DECLARE_MODEM_INIT(type) { return 0; }
+
+#define DECLARE_LINK_INIT(type) \
+ struct link_device *type ## _create_link_device( \
+ struct platform_device *pdev)
+#define DECLARE_LINK_INIT_DUMMY(type) \
+ DECLARE_LINK_INIT(type) { return NULL; }
+
+#define MODEM_INIT_CALL(type) type ## _init_modemctl_device
+#define LINK_INIT_CALL(type) type ## _create_link_device
+
+/* add declaration of modem & link type */
+/* modem device support */
+#ifdef CONFIG_UMTS_MODEM_XMM6260
+DECLARE_MODEM_INIT(xmm6260);
+#else
+DECLARE_MODEM_INIT_DUMMY(xmm6260)
+#endif
+
+#ifdef CONFIG_LTE_MODEM_CMC221
+DECLARE_MODEM_INIT(cmc221);
+#else
+DECLARE_MODEM_INIT_DUMMY(cmc221)
+#endif
+
+#ifdef CONFIG_CDMA_MODEM_CBP71
+DECLARE_MODEM_INIT(cbp71);
+#else
+DECLARE_MODEM_INIT_DUMMY(cbp71)
+#endif
+
+/* link device support */
+#ifdef CONFIG_UMTS_LINK_MIPI
+DECLARE_LINK_INIT(mipi);
+#else
+DECLARE_LINK_INIT_DUMMY(mipi)
+#endif
+
+#ifdef CONFIG_CDMA_LINK_DPRAM
+DECLARE_LINK_INIT(dpram);
+#else
+DECLARE_LINK_INIT_DUMMY(dpram)
+#endif
+
+#ifdef CONFIG_LTE_LINK_USB
+DECLARE_LINK_INIT(usb);
+#else
+DECLARE_LINK_INIT_DUMMY(usb)
+#endif
+
+#ifdef CONFIG_UMTS_LINK_HSIC
+DECLARE_LINK_INIT(hsic);
+#else
+DECLARE_LINK_INIT_DUMMY(hsic)
+#endif
+
+#ifdef CONFIG_UMTS_LINK_SPI
+DECLARE_LINK_INIT(spi);
+#else
+DECLARE_LINK_INIT_DUMMY(spi)
+#endif
+
+typedef int (*modem_init_call)(struct modem_ctl *, struct modem_data *);
+modem_init_call modem_init_func[] = {
+ MODEM_INIT_CALL(xmm6260),
+ MODEM_INIT_CALL(cbp71),
+ MODEM_INIT_CALL(cmc221),
+};
+
+typedef struct link_device *(*link_init_call)(struct platform_device *);
+link_init_call link_init_func[] = {
+ LINK_INIT_CALL(mipi),
+ LINK_INIT_CALL(dpram),
+ LINK_INIT_CALL(spi),
+ LINK_INIT_CALL(usb),
+ LINK_INIT_CALL(hsic),
+};
+
+static int call_modem_init_func(struct modem_ctl *mc, struct modem_data *pdata)
+{
+ if (modem_init_func[pdata->modem_type])
+ return modem_init_func[pdata->modem_type](mc, pdata);
+ else
+ return -ENOTSUPP;
+}
+
+static struct link_device *call_link_init_func(struct platform_device *pdev,
+ enum modem_link link_type)
+{
+ if (link_init_func[link_type])
+ return link_init_func[link_type](pdev);
+ else
+ return NULL;
+}
+
+#endif
+
diff --git a/drivers/misc/sec_jack.c b/drivers/misc/sec_jack.c
new file mode 100755
index 0000000..4514dea
--- /dev/null
+++ b/drivers/misc/sec_jack.c
@@ -0,0 +1,505 @@
+/* drivers/misc/sec_jack.c
+ *
+ * Copyright (C) 2010 Samsung Electronics Co.Ltd
+ *
+ * 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.
+ */
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/platform_device.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/switch.h>
+#include <linux/input.h>
+#include <linux/timer.h>
+#include <linux/wakelock.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/gpio_event.h>
+#include <linux/sec_jack.h>
+
+#define MAX_ZONE_LIMIT 10
+#define SEND_KEY_CHECK_TIME_MS 30 /* 30ms */
+#define DET_CHECK_TIME_MS 200 /* 200ms */
+#define WAKE_LOCK_TIME (HZ * 5) /* 5 sec */
+
+struct sec_jack_info {
+ struct sec_jack_platform_data *pdata;
+ struct delayed_work jack_detect_work;
+ struct work_struct buttons_work;
+ struct work_struct detect_work;
+ struct workqueue_struct *queue;
+ struct input_dev *input_dev;
+ struct wake_lock det_wake_lock;
+ struct sec_jack_zone *zone;
+ struct input_handler handler;
+ struct input_handle handle;
+ struct input_device_id ids;
+ int det_irq;
+ int dev_id;
+ int pressed;
+ int pressed_code;
+ struct platform_device *send_key_dev;
+ unsigned int cur_jack_type;
+};
+
+/* with some modifications like moving all the gpio structs inside
+ * the platform data and getting the name for the switch and
+ * gpio_event from the platform data, the driver could support more than
+ * one headset jack, but currently user space is looking only for
+ * one key file and switch for a headset so it'd be overkill and
+ * untestable so we limit to one instantiation for now.
+ */
+static atomic_t instantiated = ATOMIC_INIT(0);
+
+/* sysfs name HeadsetObserver.java looks for to track headset state
+ */
+struct switch_dev switch_jack_detection = {
+ .name = "h2w",
+};
+
+static struct gpio_event_direct_entry sec_jack_key_map[] = {
+ {
+ .code = KEY_UNKNOWN,
+ },
+};
+
+static struct gpio_event_input_info sec_jack_key_info = {
+ .info.func = gpio_event_input_func,
+ .info.no_suspend = true,
+ .type = EV_KEY,
+ .debounce_time.tv64 = SEND_KEY_CHECK_TIME_MS * NSEC_PER_MSEC,
+ .keymap = sec_jack_key_map,
+ .keymap_size = ARRAY_SIZE(sec_jack_key_map)
+};
+
+static struct gpio_event_info *sec_jack_input_info[] = {
+ &sec_jack_key_info.info,
+};
+
+static struct gpio_event_platform_data sec_jack_input_data = {
+ .name = "sec_jack",
+ .info = sec_jack_input_info,
+ .info_count = ARRAY_SIZE(sec_jack_input_info),
+};
+
+/* gpio_input driver does not support to read adc value.
+ * We use input filter to support 3-buttons of headset
+ * without changing gpio_input driver.
+ */
+static bool sec_jack_buttons_filter(struct input_handle *handle,
+ unsigned int type, unsigned int code,
+ int value)
+{
+ struct sec_jack_info *hi = handle->handler->private;
+
+ if (type != EV_KEY || code != KEY_UNKNOWN)
+ return false;
+
+ hi->pressed = value;
+
+ /* This is called in timer handler of gpio_input driver.
+ * We use workqueue to read adc value.
+ */
+ queue_work(hi->queue, &hi->buttons_work);
+
+ return true;
+}
+
+static int sec_jack_buttons_connect(struct input_handler *handler,
+ struct input_dev *dev,
+ const struct input_device_id *id)
+{
+ struct sec_jack_info *hi;
+ struct sec_jack_platform_data *pdata;
+ struct sec_jack_buttons_zone *btn_zones;
+ int err;
+ int i;
+
+ /* bind input_handler to input device related to only sec_jack */
+ if (dev->name != sec_jack_input_data.name)
+ return -ENODEV;
+
+ hi = handler->private;
+ pdata = hi->pdata;
+ btn_zones = pdata->buttons_zones;
+
+ hi->input_dev = dev;
+ hi->handle.dev = dev;
+ hi->handle.handler = handler;
+ hi->handle.open = 0;
+ hi->handle.name = "sec_jack_buttons";
+
+ err = input_register_handle(&hi->handle);
+ if (err) {
+ pr_err("%s: Failed to register sec_jack buttons handle, "
+ "error %d\n", __func__, err);
+ goto err_register_handle;
+ }
+
+ err = input_open_device(&hi->handle);
+ if (err) {
+ pr_err("%s: Failed to open input device, error %d\n",
+ __func__, err);
+ goto err_open_device;
+ }
+
+ for (i = 0; i < pdata->num_buttons_zones; i++)
+ input_set_capability(dev, EV_KEY, btn_zones[i].code);
+
+ return 0;
+
+ err_open_device:
+ input_unregister_handle(&hi->handle);
+ err_register_handle:
+
+ return err;
+}
+
+static void sec_jack_buttons_disconnect(struct input_handle *handle)
+{
+ input_close_device(handle);
+ input_unregister_handle(handle);
+}
+
+static void sec_jack_set_type(struct sec_jack_info *hi, int jack_type)
+{
+ struct sec_jack_platform_data *pdata = hi->pdata;
+
+ /* this can happen during slow inserts where we think we identified
+ * the type but then we get another interrupt and do it again
+ */
+ if (jack_type == hi->cur_jack_type) {
+ if (jack_type != SEC_HEADSET_4POLE)
+ pdata->set_micbias_state(false);
+ return;
+ }
+
+ if (jack_type == SEC_HEADSET_4POLE) {
+ /* for a 4 pole headset, enable detection of send/end key */
+ if (hi->send_key_dev == NULL)
+ /* enable to get events again */
+ hi->send_key_dev = platform_device_register_data(NULL,
+ GPIO_EVENT_DEV_NAME,
+ hi->dev_id,
+ &sec_jack_input_data,
+ sizeof(sec_jack_input_data));
+ } else {
+ /* for all other jacks, disable send/end key detection */
+ if (hi->send_key_dev != NULL) {
+ /* disable to prevent false events on next insert */
+ platform_device_unregister(hi->send_key_dev);
+ hi->send_key_dev = NULL;
+ }
+ /* micbias is left enabled for 4pole and disabled otherwise */
+ pdata->set_micbias_state(false);
+ }
+
+ hi->cur_jack_type = jack_type;
+ pr_info("%s : jack_type = %d\n", __func__, jack_type);
+
+ /* prevent suspend to allow user space to respond to switch */
+ wake_lock_timeout(&hi->det_wake_lock, WAKE_LOCK_TIME);
+
+ switch_set_state(&switch_jack_detection, jack_type);
+}
+
+static void handle_jack_not_inserted(struct sec_jack_info *hi)
+{
+ sec_jack_set_type(hi, SEC_JACK_NO_DEVICE);
+ hi->pdata->set_micbias_state(false);
+}
+
+static void determine_jack_type(struct sec_jack_info *hi)
+{
+ struct sec_jack_zone *zones = hi->pdata->zones;
+ int size = hi->pdata->num_zones;
+ int count[MAX_ZONE_LIMIT] = {0};
+ int adc;
+ int i;
+ unsigned npolarity = !hi->pdata->det_active_high;
+
+ while (gpio_get_value(hi->pdata->det_gpio) ^ npolarity) {
+ adc = hi->pdata->get_adc_value();
+ pr_debug("%s: adc = %d\n", __func__, adc);
+
+ /* determine the type of headset based on the
+ * adc value. An adc value can fall in various
+ * ranges or zones. Within some ranges, the type
+ * can be returned immediately. Within others, the
+ * value is considered unstable and we need to sample
+ * a few more types (up to the limit determined by
+ * the range) before we return the type for that range.
+ */
+ for (i = 0; i < size; i++) {
+ if (adc <= zones[i].adc_high) {
+ if (++count[i] > zones[i].check_count) {
+ sec_jack_set_type(hi,
+ zones[i].jack_type);
+ return;
+ }
+ msleep(zones[i].delay_ms);
+ break;
+ }
+ }
+ }
+ /* jack removed before detection complete */
+ pr_debug("%s : jack removed before detection complete\n", __func__);
+ handle_jack_not_inserted(hi);
+}
+
+/* thread run whenever the headset detect state changes (either insertion
+ * or removal).
+ */
+static irqreturn_t sec_jack_detect_irq(int irq, void *dev_id)
+{
+ struct sec_jack_info *hi = dev_id;
+
+ queue_work(hi->queue, &hi->detect_work);
+
+ return IRQ_HANDLED;
+}
+
+void sec_jack_detect_work(struct work_struct *work)
+{
+ struct sec_jack_info *hi =
+ container_of(work, struct sec_jack_info, detect_work);
+ struct sec_jack_platform_data *pdata = hi->pdata;
+ int time_left_ms = DET_CHECK_TIME_MS;
+ unsigned npolarity = !hi->pdata->det_active_high;
+
+ /* set mic bias to enable adc */
+ pdata->set_micbias_state(true);
+
+ /* debounce headset jack. don't try to determine the type of
+ * headset until the detect state is true for a while.
+ */
+ while (time_left_ms > 0) {
+ if (!(gpio_get_value(hi->pdata->det_gpio) ^ npolarity)) {
+ /* jack not detected. */
+ handle_jack_not_inserted(hi);
+ return;
+ }
+ msleep(10);
+ time_left_ms -= 10;
+ }
+ /* jack presence was detected the whole time, figure out which type */
+ determine_jack_type(hi);
+}
+
+/* thread run whenever the button of headset is pressed or released */
+void sec_jack_buttons_work(struct work_struct *work)
+{
+ struct sec_jack_info *hi =
+ container_of(work, struct sec_jack_info, buttons_work);
+ struct sec_jack_platform_data *pdata = hi->pdata;
+ struct sec_jack_buttons_zone *btn_zones = pdata->buttons_zones;
+ int adc;
+ int i;
+
+ /* when button is released */
+ if (hi->pressed == 0) {
+ input_report_key(hi->input_dev, hi->pressed_code, 0);
+ input_sync(hi->input_dev);
+ pr_debug("%s: keycode=%d, is released\n", __func__,
+ hi->pressed_code);
+ return;
+ }
+
+ /* when button is pressed */
+ adc = pdata->get_adc_value();
+
+ for (i = 0; i < pdata->num_buttons_zones; i++)
+ if (adc >= btn_zones[i].adc_low &&
+ adc <= btn_zones[i].adc_high) {
+ hi->pressed_code = btn_zones[i].code;
+ input_report_key(hi->input_dev, btn_zones[i].code, 1);
+ input_sync(hi->input_dev);
+ pr_debug("%s: keycode=%d, is pressed\n", __func__,
+ btn_zones[i].code);
+ return;
+ }
+
+ pr_warn("%s: key is skipped. ADC value is %d\n", __func__, adc);
+}
+
+static int sec_jack_probe(struct platform_device *pdev)
+{
+ struct sec_jack_info *hi;
+ struct sec_jack_platform_data *pdata = pdev->dev.platform_data;
+ int ret;
+
+ pr_info("%s : Registering jack driver\n", __func__);
+ if (!pdata) {
+ pr_err("%s : pdata is NULL.\n", __func__);
+ return -ENODEV;
+ }
+
+ if (!pdata->get_adc_value || !pdata->zones ||
+ !pdata->set_micbias_state || pdata->num_zones > MAX_ZONE_LIMIT) {
+ pr_err("%s : need to check pdata\n", __func__);
+ return -ENODEV;
+ }
+
+ if (atomic_xchg(&instantiated, 1)) {
+ pr_err("%s : already instantiated, can only have one\n",
+ __func__);
+ return -ENODEV;
+ }
+
+ sec_jack_key_map[0].gpio = pdata->send_end_gpio;
+
+ hi = kzalloc(sizeof(struct sec_jack_info), GFP_KERNEL);
+ if (hi == NULL) {
+ pr_err("%s : Failed to allocate memory.\n", __func__);
+ ret = -ENOMEM;
+ goto err_kzalloc;
+ }
+
+ hi->pdata = pdata;
+
+ /* make the id of our gpi_event device the same as our platform device,
+ * which makes it the responsiblity of the board file to make sure
+ * it is unique relative to other gpio_event devices
+ */
+ hi->dev_id = pdev->id;
+
+ ret = gpio_request(pdata->det_gpio, "ear_jack_detect");
+ if (ret) {
+ pr_err("%s : gpio_request failed for %d\n",
+ __func__, pdata->det_gpio);
+ goto err_gpio_request;
+ }
+
+ ret = switch_dev_register(&switch_jack_detection);
+ if (ret < 0) {
+ pr_err("%s : Failed to register switch device\n", __func__);
+ goto err_switch_dev_register;
+ }
+
+ wake_lock_init(&hi->det_wake_lock, WAKE_LOCK_SUSPEND, "sec_jack_det");
+
+ INIT_WORK(&hi->buttons_work, sec_jack_buttons_work);
+ INIT_WORK(&hi->detect_work, sec_jack_detect_work);
+ hi->queue = create_freezable_workqueue("sec_jack_wq");
+ if (hi->queue == NULL) {
+ ret = -ENOMEM;
+ pr_err("%s: Failed to create workqueue\n", __func__);
+ goto err_create_wq_failed;
+ }
+ queue_work(hi->queue, &hi->detect_work);
+
+ hi->det_irq = gpio_to_irq(pdata->det_gpio);
+
+ set_bit(EV_KEY, hi->ids.evbit);
+ hi->ids.flags = INPUT_DEVICE_ID_MATCH_EVBIT;
+ hi->handler.filter = sec_jack_buttons_filter;
+ hi->handler.connect = sec_jack_buttons_connect;
+ hi->handler.disconnect = sec_jack_buttons_disconnect;
+ hi->handler.name = "sec_jack_buttons";
+ hi->handler.id_table = &hi->ids;
+ hi->handler.private = hi;
+
+ ret = input_register_handler(&hi->handler);
+ if (ret) {
+ pr_err("%s : Failed to register_handler\n", __func__);
+ goto err_register_input_handler;
+ }
+ ret = request_irq(hi->det_irq, sec_jack_detect_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT, "sec_headset_detect", hi);
+ if (ret) {
+ pr_err("%s : Failed to request_irq.\n", __func__);
+ goto err_request_detect_irq;
+ }
+
+ /* to handle insert/removal when we're sleeping in a call */
+ ret = enable_irq_wake(hi->det_irq);
+ if (ret) {
+ pr_err("%s : Failed to enable_irq_wake.\n", __func__);
+ goto err_enable_irq_wake;
+ }
+
+ dev_set_drvdata(&pdev->dev, hi);
+
+ return 0;
+
+err_enable_irq_wake:
+ free_irq(hi->det_irq, hi);
+err_request_detect_irq:
+ input_unregister_handler(&hi->handler);
+err_register_input_handler:
+ destroy_workqueue(hi->queue);
+err_create_wq_failed:
+ wake_lock_destroy(&hi->det_wake_lock);
+ switch_dev_unregister(&switch_jack_detection);
+err_switch_dev_register:
+ gpio_free(pdata->det_gpio);
+err_gpio_request:
+ kfree(hi);
+err_kzalloc:
+ atomic_set(&instantiated, 0);
+
+ return ret;
+}
+
+static int sec_jack_remove(struct platform_device *pdev)
+{
+
+ struct sec_jack_info *hi = dev_get_drvdata(&pdev->dev);
+
+ pr_info("%s :\n", __func__);
+ disable_irq_wake(hi->det_irq);
+ free_irq(hi->det_irq, hi);
+ destroy_workqueue(hi->queue);
+ if (hi->send_key_dev) {
+ platform_device_unregister(hi->send_key_dev);
+ hi->send_key_dev = NULL;
+ }
+ input_unregister_handler(&hi->handler);
+ wake_lock_destroy(&hi->det_wake_lock);
+ switch_dev_unregister(&switch_jack_detection);
+ gpio_free(hi->pdata->det_gpio);
+ kfree(hi);
+ atomic_set(&instantiated, 0);
+
+ return 0;
+}
+
+static struct platform_driver sec_jack_driver = {
+ .probe = sec_jack_probe,
+ .remove = sec_jack_remove,
+ .driver = {
+ .name = "sec_jack",
+ .owner = THIS_MODULE,
+ },
+};
+static int __init sec_jack_init(void)
+{
+ return platform_driver_register(&sec_jack_driver);
+}
+
+static void __exit sec_jack_exit(void)
+{
+ platform_driver_unregister(&sec_jack_driver);
+}
+
+module_init(sec_jack_init);
+module_exit(sec_jack_exit);
+
+MODULE_AUTHOR("ms17.kim@samsung.com");
+MODULE_DESCRIPTION("Samsung Electronics Corp Ear-Jack detection driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/omap_hsi/hsi-char.c b/drivers/omap_hsi/hsi-char.c
index 871de30..8b59df8 100644
--- a/drivers/omap_hsi/hsi-char.c
+++ b/drivers/omap_hsi/hsi-char.c
@@ -375,6 +375,12 @@ static long hsi_char_ioctl(struct file *file,
if (copy_to_user((void __user *)arg, &state, sizeof(state)))
ret = -EFAULT;
break;
+ case CS_SET_WAKE_RX_3WIRES_MODE:
+ if (copy_from_user(&state, (void __user *)arg, sizeof(state)))
+ ret = -EFAULT;
+ else
+ if_hsi_set_wake_rx_3wires_mode(ch, state);
+ break;
case CS_GET_CAWAKELINE:
if_hsi_get_cawakeline(ch, &state);
if (copy_to_user((void __user *)arg, &state, sizeof(state)))
diff --git a/drivers/omap_hsi/hsi-if.c b/drivers/omap_hsi/hsi-if.c
index 5228b6a..f360e5a 100644
--- a/drivers/omap_hsi/hsi-if.c
+++ b/drivers/omap_hsi/hsi-if.c
@@ -262,6 +262,15 @@ void if_hsi_get_cawakeline(int ch, unsigned int *state)
hsi_ioctl(channel->dev, HSI_IOCTL_GET_CAWAKE, state);
}
+void if_hsi_set_wake_rx_3wires_mode(int ch, unsigned int state)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ hsi_ioctl(channel->dev,
+ state ? HSI_IOCTL_SET_WAKE_RX_3WIRES_MODE :
+ HSI_IOCTL_SET_WAKE_RX_4WIRES_MODE, NULL);
+}
+
int if_hsi_set_rx(int ch, struct hsi_rx_config *cfg)
{
int ret;
diff --git a/drivers/omap_hsi/hsi-if.h b/drivers/omap_hsi/hsi-if.h
index 96afdd4..fe83aa8a 100644
--- a/drivers/omap_hsi/hsi-if.h
+++ b/drivers/omap_hsi/hsi-if.h
@@ -52,6 +52,7 @@ void if_hsi_bootstrap(int ch);
void if_hsi_set_acwakeline(int ch, unsigned int state);
void if_hsi_get_acwakeline(int ch, unsigned int *state);
void if_hsi_get_cawakeline(int ch, unsigned int *state);
+void if_hsi_set_wake_rx_3wires_mode(int ch, unsigned int state);
int if_hsi_set_rx(int ch, struct hsi_rx_config *cfg);
void if_hsi_get_rx(int ch, struct hsi_rx_config *cfg);
int if_hsi_set_tx(int ch, struct hsi_tx_config *cfg);
diff --git a/drivers/omap_hsi/hsi_driver.c b/drivers/omap_hsi/hsi_driver.c
index bb07c10..2aad1ef 100644..100755
--- a/drivers/omap_hsi/hsi_driver.c
+++ b/drivers/omap_hsi/hsi_driver.c
@@ -41,7 +41,7 @@ static struct pm_qos_request_list *pm_qos_handle;
#endif
#define HSI_MODULENAME "omap_hsi"
-#define HSI_DRIVER_VERSION "0.4.1"
+#define HSI_DRIVER_VERSION "0.4.2"
#define HSI_RESETDONE_MAX_RETRIES 5 /* Max 5*L4 Read cycles waiting for */
/* reset to complete */
#define HSI_RESETDONE_NORMAL_RETRIES 1 /* Reset should complete in 1 R/W */
@@ -51,7 +51,7 @@ void hsi_save_ctx(struct hsi_dev *hsi_ctrl)
struct hsi_platform_data *pdata = hsi_ctrl->dev->platform_data;
struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
void __iomem *base = hsi_ctrl->base;
- struct port_ctx *p;
+ struct hsi_port_ctx *p;
int port;
pdata->ctx->sysconfig = hsi_inl(base, HSI_SYS_SYSCONFIG_REG);
@@ -92,7 +92,7 @@ void hsi_restore_ctx(struct hsi_dev *hsi_ctrl)
struct hsi_platform_data *pdata = hsi_ctrl->dev->platform_data;
struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
void __iomem *base = hsi_ctrl->base;
- struct port_ctx *p;
+ struct hsi_port_ctx *p;
int port;
hsi_outl(pdata->ctx->sysconfig, base, HSI_SYS_SYSCONFIG_REG);
@@ -128,7 +128,7 @@ void hsi_restore_ctx(struct hsi_dev *hsi_ctrl)
if (hsi_driver_device_is_hsi(pdev)) {
/* SW strategy for HSI fifo management can be changed here */
- hsi_fifo_mapping(hsi_ctrl, HSI_FIFO_MAPPING_DEFAULT);
+ hsi_fifo_mapping(hsi_ctrl, hsi_ctrl->fifo_mapping_strategy);
}
/* As a last step move HSR from MODE_VAL.SLEEP to the relevant mode. */
@@ -275,12 +275,31 @@ void hsi_set_pm_force_hsi_on(struct hsi_dev *hsi_ctrl)
/* HSI_TODO : use the HWMOD API : omap_hwmod_set_slave_idlemode() */
}
+/**
+* hsi_softreset - Force a SW RESET of HSI (core + DMA)
+*
+* @hsi_ctrl - reference to the hsi controller to be reset.
+*
+*/
int hsi_softreset(struct hsi_dev *hsi_ctrl)
{
unsigned int ind = 0;
+ unsigned int port;
void __iomem *base = hsi_ctrl->base;
u32 status;
+ /* HSI-C1BUG00088: i696 : HSI: Issue with SW reset
+ * No recovery from SW reset under specific circumstances
+ * If a SW RESET is done while some HSI errors are still not
+ * acknowledged, the HSR FSM is stucked. */
+ if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_i696_SW_RESET_FSM_STUCK)) {
+ for (port = 1; port <= hsi_ctrl->max_p; port++) {
+ hsi_outl_and(HSI_HSR_MODE_MODE_VAL_SLEEP, base,
+ HSI_HSR_MODE_REG(port));
+ hsi_outl(HSI_HSR_ERROR_ALL, base,
+ HSI_HSR_ERRORACK_REG(port));
+ }
+ }
/* Reseting HSI Block */
hsi_outl_or(HSI_SOFTRESET, base, HSI_SYS_SYSCONFIG_REG);
do {
@@ -324,7 +343,7 @@ int hsi_softreset(struct hsi_dev *hsi_ctrl)
static void hsi_set_ports_default(struct hsi_dev *hsi_ctrl,
struct platform_device *pd)
{
- struct port_ctx *cfg;
+ struct hsi_port_ctx *cfg;
struct hsi_platform_data *pdata = pd->dev.platform_data;
unsigned int port = 0;
void __iomem *base = hsi_ctrl->base;
@@ -358,7 +377,7 @@ static void hsi_set_ports_default(struct hsi_dev *hsi_ctrl,
if (hsi_driver_device_is_hsi(pdev)) {
/* SW strategy for HSI fifo management can be changed here */
- hsi_fifo_mapping(hsi_ctrl, HSI_FIFO_MAPPING_DEFAULT);
+ hsi_fifo_mapping(hsi_ctrl, hsi_ctrl->fifo_mapping_strategy);
hsi_outl(pdata->ctx->dll, base, HSI_HSR_DLL_REG);
}
}
@@ -408,6 +427,13 @@ static int hsi_port_channels_reset(struct hsi_port *port)
return 0;
}
+/**
+* hsi_softreset_driver - Must be called following HSI SW RESET, to re-align
+* variable states with new HW state.
+*
+* @hsi_ctrl - reference to the hsi controller to be re-aligned.
+*
+*/
void hsi_softreset_driver(struct hsi_dev *hsi_ctrl)
{
struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
@@ -424,7 +450,7 @@ void hsi_softreset_driver(struct hsi_dev *hsi_ctrl)
hsi_port_channels_reset(&hsi_ctrl->hsi_port[port]);
}
- hsi_set_pm_default(hsi_ctrl);
+ hsi_set_pm_force_hsi_on(hsi_ctrl);
/* Re-Configure HSI ports */
hsi_set_ports_default(hsi_ctrl, pd);
@@ -516,11 +542,13 @@ static int __init hsi_ports_init(struct hsi_dev *hsi_ctrl)
for (port = 0; port < hsi_ctrl->max_p; port++) {
hsi_p = &hsi_ctrl->hsi_port[port];
- hsi_p->port_number = port + 1;
+ hsi_p->flags = 0;
+ hsi_p->port_number = pdata->ctx->pctx[port].port_number;
hsi_p->hsi_controller = hsi_ctrl;
hsi_p->max_ch = hsi_driver_device_is_hsi(pd) ?
HSI_CHANNELS_MAX : HSI_SSI_CHANNELS_MAX;
hsi_p->irq = 0;
+ hsi_p->wake_rx_3_wires_mode = 0; /* 4 wires */
hsi_p->cawake_status = -1; /* Unknown */
hsi_p->cawake_off_event = false;
hsi_p->acwake_status = 0;
@@ -529,22 +557,21 @@ static int __init hsi_ports_init(struct hsi_dev *hsi_ctrl)
hsi_p->counters_on = 1;
hsi_p->reg_counters = pdata->ctx->pctx[port].hsr.counters;
spin_lock_init(&hsi_p->lock);
- err = hsi_port_channels_init(&hsi_ctrl->hsi_port[port]);
+ err = hsi_port_channels_init(hsi_p);
if (err < 0)
- goto rback1;
+ goto rback;
err = hsi_request_mpu_irq(hsi_p);
if (err < 0)
- goto rback2;
+ goto rback;
err = hsi_request_cawake_irq(hsi_p);
if (err < 0)
- goto rback3;
+ goto rback;
+ dev_info(hsi_ctrl->dev, "HSI port %d initialized\n",
+ hsi_p->port_number);
}
return 0;
-rback3:
- hsi_mpu_exit(hsi_p);
-rback2:
+rback:
hsi_ports_exit(hsi_ctrl, port + 1);
-rback1:
return err;
}
@@ -636,7 +663,7 @@ void hsi_clocks_disable_channel(struct device *dev, u8 channel_number,
}
if (hsi_is_hst_controller_busy(hsi_ctrl))
- dev_dbg(dev, "Disabling clocks with HST FSM not IDLE !\n");
+ dev_warn(dev, "Disabling clocks with HST FSM not IDLE !\n");
#ifdef K3_0_PORTING_HSI_MISSING_FEATURE
/* Allow Fclk to change */
@@ -654,6 +681,9 @@ void hsi_clocks_disable_channel(struct device *dev, u8 channel_number,
* @channel_number - channel number which requests clock to be enabled
* 0xFF means no particular channel
*
+* Returns: -EEXIST if clocks were already active
+* 0 if clocks were previously inactive
+*
* Note : there is no real HW clock management per HSI channel, this is only
* virtual to keep track of active channels and ease debug
*
@@ -728,8 +758,10 @@ static int __init hsi_controller_init(struct hsi_dev *hsi_ctrl,
return -ENXIO;
}
hsi_ctrl->max_p = pdata->num_ports;
+ hsi_ctrl->clock_enabled = false;
+ hsi_ctrl->clock_rate = 0;
hsi_ctrl->in_dma_tasklet = false;
- hsi_ctrl->fifo_mapping_strategy = HSI_FIFO_MAPPING_UNDEF;
+ hsi_ctrl->fifo_mapping_strategy = pdata->fifo_mapping_strategy;
hsi_ctrl->dev = &pd->dev;
spin_lock_init(&hsi_ctrl->lock);
err = hsi_init_gdd_chan_count(hsi_ctrl);
@@ -804,7 +836,7 @@ static int __init hsi_platform_device_probe(struct platform_device *pd)
if (err < 0)
goto rollback2;
- hsi_set_pm_default(hsi_ctrl);
+ hsi_set_pm_force_hsi_on(hsi_ctrl);
/* Configure HSI ports */
hsi_set_ports_default(hsi_ctrl, pd);
@@ -835,19 +867,31 @@ static int __init hsi_platform_device_probe(struct platform_device *pd)
/* Allow HSI to wake up the platform */
device_init_wakeup(hsi_ctrl->dev, true);
-#ifdef K3_0_PORTING_HSI_MISSING_FEATURE
/* Set the HSI FCLK to default. */
- err = omap_device_set_rate(hsi_ctrl->dev, hsi_ctrl->dev,
- pdata->default_hsi_fclk);
- if (err)
- dev_err(&pd->dev, "Cannot set HSI FClk to default value: %ld\n",
+ if (!pdata->device_scale) {
+ dev_err(&pd->dev, "%s: No platform device_scale function\n",
+ __func__);
+ err = -ENXIO;
+ goto rollback3;
+ }
+ err = pdata->device_scale(hsi_ctrl->dev, hsi_ctrl->dev,
+ pdata->default_hsi_fclk);
+ if (err == -EBUSY) {
+ dev_warn(&pd->dev, "Cannot set HSI FClk to default value: %ld. "
+ "Will retry on next open\n",
pdata->default_hsi_fclk);
-#endif
+ } else if (err) {
+ dev_err(&pd->dev, "%s: Error %d setting HSI FClk to %ld.\n",
+ __func__, err, pdata->default_hsi_fclk);
+ goto rollback3;
+ } else {
+ hsi_ctrl->clock_rate = pdata->default_hsi_fclk;
+ }
/* From here no need for HSI HW access */
hsi_clocks_disable(hsi_ctrl->dev, __func__);
- return err;
+ return 0;
rollback3:
hsi_debug_remove_ctrl(hsi_ctrl);
@@ -885,11 +929,28 @@ static int __exit hsi_platform_device_remove(struct platform_device *pd)
}
#ifdef CONFIG_SUSPEND
-static int hsi_suspend_noirq(struct device *dev)
+static int hsi_pm_prepare(struct device *dev)
+{
+ struct platform_device *pd = to_platform_device(dev);
+ struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* If HSI is busy, refuse the suspend */
+ if (hsi_ctrl->clock_enabled) {
+ dev_info(dev, "Platform prepare while HSI active\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int hsi_pm_suspend(struct device *dev)
{
struct hsi_platform_data *pdata = dev->platform_data;
struct platform_device *pd = to_platform_device(dev);
struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd);
+ unsigned int i;
dev_dbg(dev, "%s\n", __func__);
@@ -897,20 +958,43 @@ static int hsi_suspend_noirq(struct device *dev)
/* we don't want to re-enable it here. HSI interrupt shall be */
/* generated normally because HSI HW is ON. */
if (hsi_ctrl->clock_enabled) {
- dev_info(dev, "Platform Suspend while HSI active\n");
- return 0;
+ dev_info(dev, "Platform suspend while HSI active\n");
+ return -EBUSY;
}
/* Perform HSI board specific action before platform suspend */
if (pdata->board_suspend)
- pdata->board_suspend(0, device_may_wakeup(dev));
+ for (i = 0; i < hsi_ctrl->max_p; i++)
+ pdata->board_suspend(hsi_ctrl->hsi_port[i].port_number,
+ device_may_wakeup(dev));
+
+ return 0;
+}
+
+/* This callback can be useful in case an HSI interrupt occured between */
+/* ->suspend() phase and ->suspend_noirq() phase */
+static int hsi_pm_suspend_noirq(struct device *dev)
+{
+ struct platform_device *pd = to_platform_device(dev);
+ struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* If HSI is busy, refuse the suspend */
+ if (hsi_ctrl->clock_enabled) {
+ dev_info(dev, "Platform suspend_noirq while HSI active\n");
+ return -EBUSY;
+ }
return 0;
}
-static int hsi_resume_noirq(struct device *dev)
+static int hsi_pm_resume(struct device *dev)
{
struct hsi_platform_data *pdata = dev->platform_data;
+ struct platform_device *pd = to_platform_device(dev);
+ struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd);
+ unsigned int i;
dev_dbg(dev, "%s\n", __func__);
@@ -919,7 +1003,7 @@ static int hsi_resume_noirq(struct device *dev)
/* HSI IO checking in PRCM int handler is done when waking up from : */
/* - Device OFF mode (wake up from suspend) */
/* - L3INIT in RET (Idle mode) */
- /* hsi_resume_noirq is called only when system wakes up from suspend. */
+ /* hsi_resume is called only when system wakes up from suspend. */
/* So HSI IO checking in PRCM int handler and hsi_resume_noirq are */
/* redundant. We need to choose which one will schedule the tasklet */
/* Since HSI IO checking in PRCM int handler covers more cases, it is */
@@ -927,7 +1011,8 @@ static int hsi_resume_noirq(struct device *dev)
/* Perform (optional) HSI board specific action after platform wakeup */
if (pdata->board_resume)
- pdata->board_resume(0);
+ for (i = 0; i < hsi_ctrl->max_p; i++)
+ pdata->board_resume(hsi_ctrl->hsi_port[i].port_number);
return 0;
}
@@ -940,11 +1025,15 @@ static int hsi_resume_noirq(struct device *dev)
*
*
*/
+#define HSI_HSR_MODE_FRAME 0x2
+#define HSI_PORT1 0x1
int hsi_runtime_resume(struct device *dev)
{
struct platform_device *pd = to_platform_device(dev);
struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd);
struct hsi_platform_data *pdata = hsi_ctrl->dev->platform_data;
+ unsigned int i;
+
dev_dbg(dev, "%s\n", __func__);
if (hsi_ctrl->clock_enabled)
@@ -955,8 +1044,16 @@ int hsi_runtime_resume(struct device *dev)
/* Restore context */
hsi_restore_ctx(hsi_ctrl);
- /* When HSI is ON, no need for IO wakeup mechanism */
- pdata->wakeup_disable(0);
+ /* Restore HSR_MODE register value */
+ /* WARNING: works only in this configuration: */
+ /* - Flow = Synchronized */
+ /* - Mode = frame */
+ hsi_outl(HSI_HSR_MODE_FRAME, hsi_ctrl->base,
+ HSI_HSR_MODE_REG(HSI_PORT1));
+
+ /* When HSI is ON, no need for IO wakeup mechanism on any HSI port */
+ for (i = 0; i < hsi_ctrl->max_p; i++)
+ pdata->wakeup_disable(hsi_ctrl->hsi_port[i].port_number);
/* HSI device is now fully operational and _must_ be able to */
/* complete I/O operations */
@@ -977,28 +1074,32 @@ int hsi_runtime_suspend(struct device *dev)
struct platform_device *pd = to_platform_device(dev);
struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd);
struct hsi_platform_data *pdata = hsi_ctrl->dev->platform_data;
- int port;
+ int port, i;
+
dev_dbg(dev, "%s\n", __func__);
if (!hsi_ctrl->clock_enabled)
dev_warn(dev, "Warning: clock status mismatch vs runtime PM\n");
- /* Save context */
- hsi_save_ctx(hsi_ctrl);
-
- hsi_ctrl->clock_enabled = false;
-
/* Put HSR into SLEEP mode to force ACREADY to low while HSI is idle */
for (port = 1; port <= pdata->num_ports; port++) {
hsi_outl_and(HSI_HSR_MODE_MODE_VAL_SLEEP, hsi_ctrl->base,
HSI_HSR_MODE_REG(port));
}
- /* HSI is going to INA/RET/OFF, it needs IO wakeup mechanism enabled */
+ /* Save context */
+ hsi_save_ctx(hsi_ctrl);
+
+ hsi_ctrl->clock_enabled = false;
+
+ /* HSI is going to IDLE, it needs IO wakeup mechanism enabled */
if (device_may_wakeup(dev))
- pdata->wakeup_enable(0);
+ for (i = 0; i < hsi_ctrl->max_p; i++)
+ pdata->wakeup_enable(hsi_ctrl->hsi_port[i].port_number);
else
- pdata->wakeup_disable(0);
+ for (i = 0; i < hsi_ctrl->max_p; i++)
+ pdata->wakeup_disable(
+ hsi_ctrl->hsi_port[i].port_number);
/* HSI is now ready to be put in low power state */
@@ -1051,8 +1152,10 @@ MODULE_DEVICE_TABLE(platform, hsi_id_table);
#ifdef CONFIG_PM
static const struct dev_pm_ops hsi_driver_pm_ops = {
#ifdef CONFIG_SUSPEND
- .suspend_noirq = hsi_suspend_noirq,
- .resume_noirq = hsi_resume_noirq,
+ .prepare = hsi_pm_prepare,
+ .suspend = hsi_pm_suspend,
+ .suspend_noirq = hsi_pm_suspend_noirq,
+ .resume = hsi_pm_resume,
#endif
#ifdef CONFIG_PM_RUNTIME
.runtime_suspend = hsi_runtime_suspend,
@@ -1086,7 +1189,7 @@ static int __init hsi_driver_init(void)
{
int err = 0;
- pr_info(LOG_NAME "HSI DRIVER Version " HSI_DRIVER_VERSION "\n");
+ pr_info(LOG_NAME "HSI driver version " HSI_DRIVER_VERSION "\n");
/* Register the (virtual) HSI bus */
err = hsi_bus_init();
diff --git a/drivers/omap_hsi/hsi_driver.h b/drivers/omap_hsi/hsi_driver.h
index 0991d98..2720657 100644
--- a/drivers/omap_hsi/hsi_driver.h
+++ b/drivers/omap_hsi/hsi_driver.h
@@ -55,16 +55,20 @@
/* Number of DMA channels when nothing is defined for the device */
#define HSI_DMA_CHANNEL_DEFAULT 8
+/* Defines bit number for atomic operations */
+#define HSI_FLAGS_TASKLET_LOCK 0 /* prevents to disable IRQ and */
+ /* schedule tasklet more than once */
+
#define LOG_NAME "OMAP HSI: "
/* SW strategies for HSI FIFO mapping */
enum {
HSI_FIFO_MAPPING_UNDEF = 0,
+ HSI_FIFO_MAPPING_ALL_PORT1, /* ALL FIFOs mapped on port 1 */
+ HSI_FIFO_MAPPING_ALL_PORT2, /* ALL FIFOs mapped on port 2 */
HSI_FIFO_MAPPING_SSI, /* 8 FIFOs per port (SSI compatible mode) */
- HSI_FIFO_MAPPING_ALL_PORT1, /* ALL FIFOs mapped on 1st port */
};
-#define HSI_FIFO_MAPPING_DEFAULT HSI_FIFO_MAPPING_ALL_PORT1
/* Device identifying constants */
enum {
@@ -115,10 +119,12 @@ struct hsi_channel {
* struct hsi_port - hsi port driver data
* @hsi_channel: Array of channels in the port
* @hsi_controller: Reference to the HSI controller
- * @port_number: port number
+ * @flags: atomic flags (for atomic operations)
+ * @port_number: port number. Range [1,2]
* @max_ch: maximum number of channels supported on the port
* @n_irq: HSI irq line use to handle interrupts (0 or 1)
* @irq: IRQ number
+ * @wake_rx_3_wires_mode: receiver 3 wires mode (1) or 4 wires mode (0)
* @cawake_gpio: GPIO number for cawake line (-1 if none)
* @cawake_gpio_irq: IRQ number for cawake gpio events
* @cawake_status: Tracks CAWAKE line status
@@ -126,6 +132,7 @@ struct hsi_channel {
* @acwake_status: Bitmap to track ACWAKE line status per channel
* @in_int_tasklet: True if interrupt tasklet for this port is currently running
* @in_cawake_tasklet: True if CAWAKE tasklet for this port is currently running
+ * @tasklet_lock: prevents to disable IRQ and schedule tasklet more than once
* @counters_on: indicates if the HSR counters are in use or not
* @reg_counters: stores the previous counters values when deactivated
* @lock: Serialize access to the port registers and internal data
@@ -135,11 +142,12 @@ struct hsi_channel {
struct hsi_port {
struct hsi_channel hsi_channel[HSI_PORT_MAX_CH];
struct hsi_dev *hsi_controller;
- u8 flags;
- u8 port_number; /* Range [1,2] */
+ unsigned long flags;
+ u8 port_number;
u8 max_ch;
u8 n_irq;
int irq;
+ int wake_rx_3_wires_mode;
int cawake_gpio;
int cawake_gpio_irq;
int cawake_status;
@@ -151,8 +159,7 @@ struct hsi_port {
unsigned long reg_counters;
spinlock_t lock; /* access to the port registers and internal data */
struct tasklet_struct hsi_tasklet;
- struct tasklet_struct cawake_tasklet; /* SSI_TODO : need to replace */
- /* by a workqueue */
+ struct tasklet_struct cawake_tasklet;
};
/**
@@ -167,6 +174,7 @@ struct hsi_port {
* @phy_base: HSI registers base physical address
* @lock: Serializes access to internal data and regs
* @clock_enabled: Indicates if HSI Clocks are ON
+ * @clock_rate: Indicates current HSI Fclock speed
* @gdd_irq: GDD (DMA) irq number
* @fifo_mapping_strategy: Selected strategy for fifo to ports/channels mapping
* @gdd_usecount: Holds the number of ongoning DMA transfers
@@ -189,6 +197,7 @@ struct hsi_dev { /* HSI_TODO: should be later renamed into hsi_controller*/
unsigned long phy_base;
spinlock_t lock; /* Serializes access to internal data and regs */
bool clock_enabled;
+ unsigned long clock_rate;
int gdd_irq;
unsigned int fifo_mapping_strategy;
unsigned int gdd_usecount;
@@ -214,15 +223,19 @@ struct hsi_platform_data {
int (*device_enable) (struct platform_device *pdev);
int (*device_shutdown) (struct platform_device *pdev);
int (*device_idle) (struct platform_device *pdev);
+ int (*device_scale) (struct device *req_dev, struct device *target_dev,
+ unsigned long rate);
int (*wakeup_enable) (int hsi_port);
int (*wakeup_disable) (int hsi_port);
- int (*wakeup_is_from_hsi) (void);
+ bool (*wakeup_is_from_hsi) (int *hsi_port);
int (*board_suspend)(int hsi_port, bool dev_may_wakeup);
int (*board_resume)(int hsi_port);
u8 num_ports;
- struct ctrl_ctx *ctx;
+ struct hsi_ctrl_ctx *ctx;
u8 hsi_gdd_chan_count;
unsigned long default_hsi_fclk;
+ unsigned int fifo_mapping_strategy;
+ u32 errata;
};
/* HSI Bus */
@@ -240,8 +253,11 @@ bool hsi_is_hsi_port_busy(struct hsi_port *pport);
bool hsi_is_hsi_controller_busy(struct hsi_dev *hsi_ctrl);
bool hsi_is_hst_port_busy(struct hsi_port *pport);
bool hsi_is_hst_controller_busy(struct hsi_dev *hsi_ctrl);
-
+void hsi_driver_ack_interrupt(struct hsi_port *pport, u32 flag, bool backup);
+bool hsi_driver_is_interrupt_pending(struct hsi_port *pport, u32 flag,
+ bool backup);
int hsi_driver_enable_interrupt(struct hsi_port *pport, u32 flag);
+int hsi_driver_disable_interrupt(struct hsi_port *pport, u32 flag);
int hsi_driver_enable_read_interrupt(struct hsi_channel *hsi_channel,
u32 *data);
int hsi_driver_enable_write_interrupt(struct hsi_channel *hsi_channel,
@@ -395,4 +411,17 @@ static inline int hsi_clocks_enable(struct device *dev, const char *s)
return hsi_clocks_enable_channel(dev, HSI_CH_NUMBER_NONE, s);
}
+static inline int is_hsi_errata(struct hsi_dev *hsi_ctrl, unsigned int id)
+{
+ struct hsi_platform_data *pdata = dev_get_platdata(hsi_ctrl->dev);
+
+ return IS_HSI_ERRATA(pdata->errata, id);
+}
+
+#if defined(CONFIG_PM) && defined(CONFIG_ARCH_OMAP4)
+extern void omap_pm_clear_dsp_wake_up(void);
+#else
+#define static inline void omap_pm_clear_dsp_wake_up(void) { }
+#endif
+
#endif /* __HSI_DRIVER_H__ */
diff --git a/drivers/omap_hsi/hsi_driver_debugfs.c b/drivers/omap_hsi/hsi_driver_debugfs.c
index d1f32dd..703079e 100644
--- a/drivers/omap_hsi/hsi_driver_debugfs.c
+++ b/drivers/omap_hsi/hsi_driver_debugfs.c
@@ -464,7 +464,7 @@ int __init hsi_debug_add_ctrl(struct hsi_dev *hsi_ctrl)
debugfs_create_file("regs", S_IRUGO, dir,
&hsi_ctrl->hsi_port[port],
&hsi_port_regs_fops);
- debugfs_create_file("counters", S_IRUGO | S_IWUGO, dir,
+ debugfs_create_file("counters", S_IRUGO | S_IWUSR, dir,
&hsi_ctrl->hsi_port[port],
&hsi_port_counters_fops);
}
diff --git a/drivers/omap_hsi/hsi_driver_dma.c b/drivers/omap_hsi/hsi_driver_dma.c
index ad819f5..4cffd0c 100644
--- a/drivers/omap_hsi/hsi_driver_dma.c
+++ b/drivers/omap_hsi/hsi_driver_dma.c
@@ -436,24 +436,23 @@ int hsi_get_info_from_gdd_lch(struct hsi_dev *hsi_ctrl, unsigned int lch,
unsigned int *port, unsigned int *channel,
unsigned int *is_read_path)
{
- int i_ports;
- int i_chans;
+ int i, j;
int err = -1;
- for (i_ports = 0; i_ports < HSI_MAX_PORTS; i_ports++)
- for (i_chans = 0; i_chans < HSI_PORT_MAX_CH; i_chans++)
- if (hsi_ctrl->hsi_port[i_ports].
- hsi_channel[i_chans].read_data.lch == lch) {
+ for (i = 0; i < hsi_ctrl->max_p; i++)
+ for (j = 0; j < hsi_ctrl->hsi_port[i].max_ch; j++)
+ if (hsi_ctrl->hsi_port[i].
+ hsi_channel[j].read_data.lch == lch) {
*is_read_path = 1;
- *port = i_ports + 1;
- *channel = i_chans;
+ *port = i + 1;
+ *channel = j;
err = 0;
goto get_info_bk;
- } else if (hsi_ctrl->hsi_port[i_ports].
- hsi_channel[i_chans].write_data.lch == lch) {
+ } else if (hsi_ctrl->hsi_port[i].
+ hsi_channel[j].write_data.lch == lch) {
*is_read_path = 0;
- *port = i_ports + 1;
- *channel = i_chans;
+ *port = i + 1;
+ *channel = j;
err = 0;
goto get_info_bk;
}
diff --git a/drivers/omap_hsi/hsi_driver_fifo.c b/drivers/omap_hsi/hsi_driver_fifo.c
index aa33a1a..862a12d 100644
--- a/drivers/omap_hsi/hsi_driver_fifo.c
+++ b/drivers/omap_hsi/hsi_driver_fifo.c
@@ -26,7 +26,7 @@
* hsi_fifo_get_id - Get fifo index corresponding to (port, channel)
* @hsi_ctrl - HSI controler data
* @channel - channel used
- * @port - HSI port used
+ * @port - HSI port used. Range [1, 2]
*
* Returns the fifo index associated to the provided (port, channel).
* Notes: 1) The fifo <=> (port, channel) correspondance depends on the selected
@@ -39,6 +39,7 @@ int hsi_fifo_get_id(struct hsi_dev *hsi_ctrl, unsigned int channel,
{
int fifo_index = 0;
int err = 0;
+ int fifo_port; /* Range [1, 2] */
if (unlikely((channel >= HSI_CHANNELS_MAX) || (port < 1) ||
(port > 2))) {
@@ -46,8 +47,11 @@ int hsi_fifo_get_id(struct hsi_dev *hsi_ctrl, unsigned int channel,
goto fifo_id_bk;
}
- if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_ALL_PORT1) {
- if (unlikely(port != 1)) {
+ if ((hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_ALL_PORT1) ||
+ (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_ALL_PORT2)) {
+ fifo_port = (hsi_ctrl->fifo_mapping_strategy ==
+ HSI_FIFO_MAPPING_ALL_PORT1) ? 1 : 2;
+ if (unlikely(port != fifo_port)) {
err = -EINVAL;
goto fifo_id_bk;
} else {
@@ -102,6 +106,10 @@ int hsi_fifo_get_chan(struct hsi_dev *hsi_ctrl, unsigned int fifo,
if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_ALL_PORT1) {
*channel = fifo;
*port = 1;
+ } else if (hsi_ctrl->fifo_mapping_strategy ==
+ HSI_FIFO_MAPPING_ALL_PORT2) {
+ *channel = fifo;
+ *port = 2;
} else if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_SSI) {
if (fifo < 8) {
*channel = fifo;
@@ -128,7 +136,7 @@ fifo_id_bk:
* @hsi_ctrl - HSI controler data
* @mtype - mapping strategy
*
- * Returns 0 in case of success, and errocode (< 0) else
+ * Returns 0 in case of success, and error code (< 0) else
* Configures the HSI FIFO mapping registers. Several mapping strategies are
* proposed.
* Note: The mapping is identical for Read and Write path.
@@ -141,24 +149,26 @@ int hsi_fifo_mapping(struct hsi_dev *hsi_ctrl, unsigned int mtype)
int i;
unsigned int channel, port;
- if (mtype == HSI_FIFO_MAPPING_ALL_PORT1) {
+ if ((mtype == HSI_FIFO_MAPPING_ALL_PORT1) ||
+ (mtype == HSI_FIFO_MAPPING_ALL_PORT2)) {
+ port = (mtype == HSI_FIFO_MAPPING_ALL_PORT1) ? 0 : 1;
channel = 0;
for (i = 0; i < HSI_HST_FIFO_COUNT; i++) {
hsi_outl(HSI_MAPPING_ENABLE |
(channel << HSI_MAPPING_CH_NUMBER_OFFSET) |
- (0 << HSI_MAPPING_PORT_NUMBER_OFFSET) |
+ (port << HSI_MAPPING_PORT_NUMBER_OFFSET) |
HSI_HST_MAPPING_THRESH_VALUE,
base, HSI_HST_MAPPING_FIFO_REG(i));
hsi_outl(HSI_MAPPING_ENABLE |
(channel << HSI_MAPPING_CH_NUMBER_OFFSET) |
- (0 << HSI_MAPPING_PORT_NUMBER_OFFSET),
+ (port << HSI_MAPPING_PORT_NUMBER_OFFSET),
base, HSI_HSR_MAPPING_FIFO_REG(i));
channel++;
}
+
if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_UNDEF)
dev_dbg(hsi_ctrl->dev, "Fifo mapping : All FIFOs for "
- "Port1\n");
- hsi_ctrl->fifo_mapping_strategy = HSI_FIFO_MAPPING_ALL_PORT1;
+ "Port %d\n", port + 1);
} else if (mtype == HSI_FIFO_MAPPING_SSI) {
channel = 0;
port = 0;
@@ -182,14 +192,14 @@ int hsi_fifo_mapping(struct hsi_dev *hsi_ctrl, unsigned int mtype)
if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_UNDEF)
dev_dbg(hsi_ctrl->dev, "Fifo mapping : 8 FIFOs per Port"
" (SSI compatible mode)\n");
- hsi_ctrl->fifo_mapping_strategy = HSI_FIFO_MAPPING_SSI;
} else {
- hsi_ctrl->fifo_mapping_strategy = HSI_FIFO_MAPPING_UNDEF;
dev_err(hsi_ctrl->dev, "Bad Fifo strategy request : %d\n",
mtype);
err = -EINVAL;
}
+ hsi_ctrl->fifo_mapping_strategy = mtype;
+
return err;
}
@@ -318,6 +328,11 @@ u8 hsi_get_rx_fifo_occupancy(struct hsi_dev *hsi_ctrl, u8 fifo)
void __iomem *base = hsi_ctrl->base;
int hsr_mapping, mapping_words;
+ if (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev, "Invalid FIFO id %d.\n", fifo);
+ return 0;
+ }
+
hsr_mapping = hsi_inl(base, HSI_HSR_MAPPING_FIFO_REG(fifo));
mapping_words = (hsr_mapping >> HSI_HST_MAPPING_THRESH_OFFSET) & 0xF;
return mapping_words;
diff --git a/drivers/omap_hsi/hsi_driver_if.c b/drivers/omap_hsi/hsi_driver_if.c
index 19012e5..79e05cc 100644
--- a/drivers/omap_hsi/hsi_driver_if.c
+++ b/drivers/omap_hsi/hsi_driver_if.c
@@ -50,7 +50,6 @@ int hsi_set_rx_divisor(struct hsi_port *sport, struct hsr_ctx *cfg)
} else if (cfg->divisor != HSI_HSR_DIVISOR_AUTO) {
/* Divisor set mode: use counters */
/* Leave auto mode: use new counters values */
- cfg->counters = 0xFFFFF;
sport->reg_counters = cfg->counters;
sport->counters_on = 1;
hsi_outl(cfg->counters, base,
@@ -260,6 +259,7 @@ int hsi_open(struct hsi_device *dev)
struct hsi_channel *ch;
struct hsi_port *port;
struct hsi_dev *hsi_ctrl;
+ int err;
if (!dev || !dev->ch) {
pr_err(LOG_NAME "Wrong HSI device %p\n", dev);
@@ -274,26 +274,69 @@ int hsi_open(struct hsi_device *dev)
"registered\n");
return -EINVAL;
}
- port = ch->hsi_port;
- hsi_ctrl = port->hsi_controller;
-
- spin_lock_bh(&hsi_ctrl->lock);
- hsi_clocks_enable_channel(dev->device.parent, ch->channel_number,
- __func__);
-
if (ch->flags & HSI_CH_OPEN) {
dev_err(dev->device.parent,
"Port %d Channel %d already OPENED\n",
dev->n_p, dev->n_ch);
- spin_unlock_bh(&hsi_ctrl->lock);
return -EBUSY;
}
+ port = ch->hsi_port;
+ hsi_ctrl = port->hsi_controller;
+ if (!hsi_ctrl) {
+ dev_err(dev->device.parent,
+ "%s: Port %d Channel %d has no hsi controller?\n",
+ __func__, dev->n_p, dev->n_ch);
+ return -EINVAL;
+ }
+
+ if (hsi_ctrl->clock_rate == 0) {
+ struct hsi_platform_data *pdata;
+
+ pdata = dev_get_platdata(hsi_ctrl->dev);
+ if (!pdata) {
+ dev_err(dev->device.parent,
+ "%s: Port %d Channel %d has no pdata\n",
+ __func__, dev->n_p, dev->n_ch);
+ return -EINVAL;
+ }
+ if (!pdata->device_scale) {
+ dev_err(dev->device.parent,
+ "%s: Undefined platform device_scale function\n",
+ __func__);
+ return -ENXIO;
+ }
+
+ /* Retry to set the HSI FCLK to default. */
+ err = pdata->device_scale(hsi_ctrl->dev, hsi_ctrl->dev,
+ pdata->default_hsi_fclk);
+ if (err) {
+ dev_err(dev->device.parent,
+ "%s: Error %d setting HSI FClk to %ld. "
+ "Will retry on next open\n",
+ __func__, err, pdata->default_hsi_fclk);
+ return err;
+ } else {
+ dev_info(dev->device.parent, "HSI clock is now %ld\n",
+ pdata->default_hsi_fclk);
+ hsi_ctrl->clock_rate = pdata->default_hsi_fclk;
+ }
+ }
+ spin_lock_bh(&hsi_ctrl->lock);
+ hsi_clocks_enable_channel(dev->device.parent, ch->channel_number,
+ __func__);
+
/* Restart with flags cleaned up */
ch->flags = HSI_CH_OPEN;
- hsi_driver_enable_interrupt(port, HSI_CAWAKEDETECTED | HSI_ERROROCCURED
- | HSI_BREAKDETECTED);
+ if (port->wake_rx_3_wires_mode)
+ hsi_driver_enable_interrupt(port, HSI_ERROROCCURED
+ | HSI_BREAKDETECTED);
+ else
+ hsi_driver_enable_interrupt(port, HSI_CAWAKEDETECTED
+ | HSI_ERROROCCURED
+ | HSI_BREAKDETECTED);
+
/* NOTE: error and break are port events and do not need to be
* enabled for HSI extended enable register */
@@ -312,7 +355,7 @@ EXPORT_SYMBOL(hsi_open);
* @addr - pointer to a 32-bit word data to be written.
* @size - number of 32-bit word to be written.
*
- * Return 0 on sucess, a negative value on failure.
+ * Return 0 on success, a negative value on failure.
* A success value only indicates that the request has been accepted.
* Transfer is only completed when the write_done callback is called.
*
@@ -342,6 +385,13 @@ int hsi_write(struct hsi_device *dev, u32 *addr, unsigned int size)
}
ch = dev->ch;
+ if (ch->write_data.addr != NULL) {
+ dev_err(dev->device.parent, "# Invalid request - Write "
+ "operation pending port %d channel %d\n",
+ ch->hsi_port->port_number,
+ ch->channel_number);
+ return -EINVAL;
+ }
spin_lock_bh(&ch->hsi_port->hsi_controller->lock);
if (pm_runtime_suspended(dev->device.parent) ||
@@ -353,18 +403,6 @@ int hsi_write(struct hsi_device *dev, u32 *addr, unsigned int size)
hsi_clocks_enable_channel(dev->device.parent,
ch->channel_number, __func__);
- if (ch->write_data.addr != NULL) {
- dev_err(dev->device.parent, "# Invalid request - Write "
- "operation pending port %d channel %d\n",
- ch->hsi_port->port_number,
- ch->channel_number);
-
- hsi_clocks_disable_channel(dev->device.parent,
- ch->channel_number, __func__);
- spin_unlock_bh(&ch->hsi_port->hsi_controller->lock);
- return -EINVAL;
- }
-
ch->write_data.addr = addr;
ch->write_data.size = size;
ch->write_data.lch = -1;
@@ -669,7 +707,7 @@ EXPORT_SYMBOL(hsi_unpoll);
* @command - HSI I/O control command
* @arg - parameter associated to the control command. NULL, if no parameter.
*
- * Return 0 on sucess, a negative value on failure.
+ * Return 0 on success, a negative value on failure.
*
*/
int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg)
@@ -707,15 +745,14 @@ int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg)
switch (command) {
case HSI_IOCTL_ACWAKE_UP:
+ /* Wake up request to Modem (typically OMAP initiated) */
+ /* Symetrical disable will be done in HSI_IOCTL_ACWAKE_DOWN */
if (ch->flags & HSI_CH_ACWAKE) {
dev_dbg(dev->device.parent, "Duplicate ACWAKE UP\n");
err = -EPERM;
goto out;
}
- /* Wake up request to Modem (typically OMAP initiated) */
- /* Symetrical disable will be done in HSI_IOCTL_ACWAKE_DOWN */
-
ch->flags |= HSI_CH_ACWAKE;
pport->acwake_status |= BIT(channel);
@@ -839,16 +876,45 @@ int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg)
}
*(size_t *)arg = hsi_get_rx_fifo_occupancy(hsi_ctrl, fifo);
break;
- case HSI_IOCTL_SET_ACREADY_SAFEMODE:
- omap_writel(omap_readl(0x4A1000C8) | 0x7, 0x4A1000C8);
- break;
- case HSI_IOCTL_SET_ACREADY_NORMAL:
- omap_writel(omap_readl(0x4A1000C8) & 0xFFFFFFF9, 0x4A1000C8);
- case HSI_IOCTL_SET_3WIRE_MODE:
- omap_writel(0x30000, 0x4A058C08);
+ case HSI_IOCTL_SET_WAKE_RX_3WIRES_MODE:
+ dev_info(dev->device.parent,
+ "Entering RX wakeup in 3 wires mode (no CAWAKE)\n");
+ pport->wake_rx_3_wires_mode = 1;
+
+ /* HSI-C1BUG00085: ixxx: HSI wakeup issue in 3 wires mode
+ * HSI will NOT generate the Swakeup for 2nd frame if it entered
+ * IDLE after 1st received frame */
+ if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_ixxx_3WIRES_NO_SWAKEUP))
+ if (hsi_driver_device_is_hsi(to_platform_device
+ (hsi_ctrl->dev)))
+ hsi_set_pm_force_hsi_on(hsi_ctrl);
+
+ /* When WAKE is not available, ACREADY must be set to 1 at
+ * reset else remote will never have a chance to transmit. */
+ hsi_outl_or(HSI_SET_WAKE_3_WIRES | HSI_SET_WAKE_READY_LVL_1,
+ base, HSI_SYS_SET_WAKE_REG(port));
+ hsi_driver_disable_interrupt(pport, HSI_CAWAKEDETECTED);
break;
- case HSI_IOCTL_SET_4WIRE_MODE:
- omap_writel((omap_readl(0x4A058C08) & 0xFFFF), 0x4A058C08);
+ case HSI_IOCTL_SET_WAKE_RX_4WIRES_MODE:
+ dev_info(dev->device.parent,
+ "Entering RX wakeup in 4 wires mode\n");
+ pport->wake_rx_3_wires_mode = 0;
+
+ /* HSI-C1BUG00085: ixxx: HSI wakeup issue in 3 wires mode
+ * HSI will NOT generate the Swakeup for 2nd frame if it entered
+ * IDLE after 1st received frame */
+ if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_ixxx_3WIRES_NO_SWAKEUP))
+ if (hsi_driver_device_is_hsi(to_platform_device
+ (hsi_ctrl->dev)))
+ hsi_set_pm_default(hsi_ctrl);
+
+ /* Clean CA_WAKE status */
+ pport->cawake_status = -1;
+ hsi_outl(HSI_CAWAKEDETECTED, base,
+ HSI_SYS_MPU_STATUS_REG(port, pport->n_irq));
+ hsi_driver_enable_interrupt(pport, HSI_CAWAKEDETECTED);
+ hsi_outl_and(HSI_SET_WAKE_3_WIRES_MASK, base,
+ HSI_SYS_SET_WAKE_REG(port));
break;
default:
err = -ENOIOCTLCMD;
@@ -956,8 +1022,13 @@ void hsi_set_port_event_cb(struct hsi_device *dev,
spin_lock_bh(&hsi_ctrl->lock);
hsi_clocks_enable_channel(dev->device.parent, dev->ch->channel_number,
__func__);
- hsi_driver_enable_interrupt(port, HSI_CAWAKEDETECTED | HSI_ERROROCCURED
- | HSI_BREAKDETECTED);
+ if (port->wake_rx_3_wires_mode)
+ hsi_driver_enable_interrupt(port, HSI_ERROROCCURED
+ | HSI_BREAKDETECTED);
+ else
+ hsi_driver_enable_interrupt(port, HSI_CAWAKEDETECTED
+ | HSI_ERROROCCURED
+ | HSI_BREAKDETECTED);
hsi_clocks_disable_channel(dev->device.parent, dev->ch->channel_number,
__func__);
spin_unlock_bh(&hsi_ctrl->lock);
diff --git a/drivers/omap_hsi/hsi_driver_int.c b/drivers/omap_hsi/hsi_driver_int.c
index 52bbba1..ac71bc7 100644
--- a/drivers/omap_hsi/hsi_driver_int.c
+++ b/drivers/omap_hsi/hsi_driver_int.c
@@ -20,7 +20,7 @@
#include "hsi_driver.h"
#include <linux/delay.h>
-int shceduled_already_flag = 0;
+
void hsi_reset_ch_read(struct hsi_channel *ch)
{
ch->read_data.addr = NULL;
@@ -40,6 +40,16 @@ void hsi_reset_ch_write(struct hsi_channel *ch)
*/
bool hsi_is_channel_busy(struct hsi_channel *ch)
{
+ struct hsi_port *p = ch->hsi_port;
+ unsigned int port = p->port_number;
+ unsigned int channel = ch->channel_number;
+ unsigned int fifo;
+
+ /* Data in FIFO is lost during the transition to RET or OFF modes */
+ fifo = hsi_fifo_get_id(p->hsi_controller, channel, port);
+ if (hsi_get_rx_fifo_occupancy(p->hsi_controller, fifo) > 0)
+ return true;
+
if (ch->write_data.addr == NULL)
return false;
@@ -54,6 +64,7 @@ bool hsi_is_channel_busy(struct hsi_channel *ch)
/* Check if a HSI port is busy :
* - data transfer (Write) is ongoing for a given HSI channel
* - CAWAKE is high
+ * - CAWAKE is not used (receiver in 3-wires mode)
* - Currently in HSI interrupt tasklet
* - Currently in HSI CAWAKE tasklet (for SSI)
*/
@@ -73,6 +84,13 @@ bool hsi_is_hsi_port_busy(struct hsi_port *pport)
return true;
}
+ if (pport->wake_rx_3_wires_mode) {
+ dev_dbg(hsi_ctrl->dev, "Receiver Port %d in 3 wires mode,"
+ "acwake_status %d\n", pport->port_number,
+ pport->acwake_status);
+ return true;
+ }
+
if (cur_cawake) {
dev_dbg(hsi_ctrl->dev, "Port %d: WAKE status: acwake_status %d,"
"cur_cawake %d", pport->port_number,
@@ -143,7 +161,16 @@ bool hsi_is_hst_controller_busy(struct hsi_dev *hsi_ctrl)
}
-/* Enables the CAWAKE, BREAK, or ERROR interrupt for the given port */
+/* Enables the CAWAKE, BREAK, or ERROR interrupt for the given port
+ *
+ * Since these 3 interrupts ENABLE and STATUS bits are duplicated in both
+ * HSI_Pp_M_IRQr_xxx(channels [0..7]) and HSI_Pp_M_IRQrU_xxx(channels [8..15]),
+ * for convenience we always enable the interrupts for channels [0..7].
+ *
+ * BREAK and ERROR interrupts in HSI_Pp_M_IRQrU_xxx are not used.
+ * CAWAKE interrupt in HSI_Pp_M_IRQrU_xxx is used as a backup interrupt to be
+ * sure we don't miss a CAWAKE interrupt while clearing the previous one.
+ */
int hsi_driver_enable_interrupt(struct hsi_port *pport, u32 flag)
{
hsi_outl_or(flag, pport->hsi_controller->base,
@@ -152,6 +179,45 @@ int hsi_driver_enable_interrupt(struct hsi_port *pport, u32 flag)
return 0;
}
+/* Disables the CAWAKE, BREAK, or ERROR interrupt for the given port */
+int hsi_driver_disable_interrupt(struct hsi_port *pport, u32 flag)
+{
+ hsi_outl_and(~flag, pport->hsi_controller->base,
+ HSI_SYS_MPU_ENABLE_REG(pport->port_number, pport->n_irq));
+
+ return 0;
+}
+
+/* hsi_driver_ack_interrupt - Acknowledge the CAWAKE, BREAK, or ERROR interrupt
+ for the given port
+ * @backup - indicate whether we shall look for the backup status interrupt
+ * register (HSI_Pp_M_IRQrU_STATUS) or the normal status interrupt
+ * register (HSI_Pp_M_IRQr_STATUS)
+ * Backup status interrupt register is used for CAWAKE only.
+ */
+void hsi_driver_ack_interrupt(struct hsi_port *pport, u32 flag, bool backup)
+{
+ hsi_outl(flag, pport->hsi_controller->base,
+ HSI_SYS_MPU_STATUS_CH_REG(pport->port_number,
+ pport->n_irq,
+ backup ? HSI_SSI_CHANNELS_MAX :
+ 0));
+}
+
+bool hsi_driver_is_interrupt_pending(struct hsi_port *pport, u32 flag,
+ bool backup)
+{
+ u32 val;
+
+ val = hsi_inl(pport->hsi_controller->base,
+ HSI_SYS_MPU_STATUS_CH_REG(pport->port_number,
+ pport->n_irq,
+ backup ? HSI_SSI_CHANNELS_MAX :
+ 0));
+
+ return val & flag;
+}
+
/* Enables the Data Accepted Interrupt of HST for the given channel */
int hsi_driver_enable_write_interrupt(struct hsi_channel *ch, u32 * data)
{
@@ -323,7 +389,6 @@ static void hsi_do_channel_rx(struct hsi_channel *ch)
int rx_poll = 0;
int data_read = 0;
int fifo, fifo_words_avail;
- unsigned int data;
n_ch = ch->channel_number;
n_p = ch->hsi_port->port_number;
@@ -352,15 +417,18 @@ static void hsi_do_channel_rx(struct hsi_channel *ch)
}
}
+ /* Disable interrupts if not needed for polling */
+ if (!(ch->flags & HSI_CH_RX_POLL))
+ hsi_driver_disable_read_interrupt(ch);
+
/*
* Check race condition: RX transmission initiated but DMA transmission
* already started - acknowledge then ignore interrupt occurence
*/
if (ch->read_data.lch != -1) {
- dev_err(hsi_ctrl->dev,
- "race condition between rx txmn and DMA txmn %0x\n",
- ch->read_data.lch);
- hsi_driver_disable_read_interrupt(ch);
+ dev_warn(hsi_ctrl->dev,
+ "Race condition between RX Int ch %d and DMA %0x\n",
+ n_ch, ch->read_data.lch);
goto done;
}
@@ -371,11 +439,10 @@ static void hsi_do_channel_rx(struct hsi_channel *ch)
buff_offset = hsi_hsr_buffer_reg(hsi_ctrl, n_p, n_ch);
if (buff_offset >= 0) {
data_read = 1;
- data = *(ch->read_data.addr) = hsi_inl(base,
- buff_offset);
+ *(ch->read_data.addr) = hsi_inl(base, buff_offset);
}
}
- hsi_driver_disable_read_interrupt(ch);
+
hsi_reset_ch_read(ch);
done:
@@ -389,6 +456,7 @@ done:
if (data_read) {
spin_unlock(&hsi_ctrl->lock);
+ dev_dbg(hsi_ctrl->dev, "Calling ch %d read callback.\n", n_ch);
(*ch->read_done) (ch->dev, 1);
spin_lock(&hsi_ctrl->lock);
}
@@ -412,6 +480,11 @@ int hsi_do_cawake_process(struct hsi_port *pport)
struct hsi_dev *hsi_ctrl = pport->hsi_controller;
bool cawake_status = hsi_get_cawake(pport);
+ if (pport->wake_rx_3_wires_mode) {
+ dev_warn(hsi_ctrl->dev, "CAWAKE edge in RX 3 wires, exiting\n");
+ return 0;
+ }
+
/* Deal with init condition */
if (unlikely(pport->cawake_status < 0))
pport->cawake_status = !cawake_status;
@@ -431,25 +504,32 @@ int hsi_do_cawake_process(struct hsi_port *pport)
/* Check for possible mismatch (race condition) */
if (unlikely(pport->cawake_status)) {
dev_warn(hsi_ctrl->dev,
- "CAWAKE race is detected: %s.\n",
- "HI -> LOW -> HI");
+ "Missed previous CAWAKE falling edge...\n");
spin_unlock(&hsi_ctrl->lock);
hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_DOWN,
NULL);
spin_lock(&hsi_ctrl->lock);
+
+ /* In case another CAWAKE interrupt occured and caused
+ * a race condition, clear CAWAKE backup interrupt to
+ * avoid handling twice the race condition */
+ hsi_driver_ack_interrupt(pport, HSI_CAWAKEDETECTED,
+ true);
}
pport->cawake_status = 1;
- /* Force HSI to ON_ACTIVE when CAWAKE is high */
- hsi_set_pm_force_hsi_on(hsi_ctrl);
- /*
- * TODO: Use pm_qos() to set latency constraint to prevent
- * L3INIT to enter RET/OFF when CAWAKE is high.
- */
-
spin_unlock(&hsi_ctrl->lock);
hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_UP, NULL);
spin_lock(&hsi_ctrl->lock);
+
+ /*
+ * HSI - OMAP4430-2.2BUG00055: i702
+ * HSI: DSP Swakeup generated is the same than MPU Swakeup.
+ * System cannot enter in off mode due to the DSP.
+ */
+ if (is_hsi_errata(hsi_ctrl, HSI_ERRATUM_i702_PM_HSI_SWAKEUP))
+ omap_pm_clear_dsp_wake_up();
+
} else {
dev_dbg(hsi_ctrl->dev, "CAWAKE falling edge detected\n");
@@ -462,26 +542,35 @@ int hsi_do_cawake_process(struct hsi_port *pport)
}
if (unlikely(!pport->cawake_status)) {
dev_warn(hsi_ctrl->dev,
- "CAWAKE race is detected: %s.\n",
- "LOW -> HI -> LOW");
+ "Missed previous CAWAKE rising edge...\n");
spin_unlock(&hsi_ctrl->lock);
hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_UP,
NULL);
spin_lock(&hsi_ctrl->lock);
+
+ /* In case another CAWAKE interrupt occured and caused
+ * a race condition, clear CAWAKE backup interrupt to
+ * avoid handling twice the race condition */
+ hsi_driver_ack_interrupt(pport, HSI_CAWAKEDETECTED,
+ true);
}
pport->cawake_status = 0;
- /* Allow HSI HW to enter IDLE when CAWAKE is low */
- hsi_set_pm_default(hsi_ctrl);
- /*
- * TODO: Use pm_qos() to release latency constraint to allow
- * L3INIT to enter RET/OFF when CAWAKE is low
- */
-
spin_unlock(&hsi_ctrl->lock);
hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_DOWN, NULL);
spin_lock(&hsi_ctrl->lock);
}
+
+ /* If another CAWAKE event occured while previous is still processed */
+ /* do not clear the status bit */
+ cawake_status = hsi_get_cawake(pport);
+ if (cawake_status != pport->cawake_status) {
+ dev_warn(hsi_ctrl->dev, "CAWAKE line changed to %d while CAWAKE"
+ "event is still being processed\n",
+ cawake_status);
+ return -EAGAIN;
+ }
+
return 0;
}
@@ -501,7 +590,8 @@ int hsi_do_cawake_process(struct hsi_port *pport)
static u32 hsi_driver_int_proc(struct hsi_port *pport,
unsigned long status_offset,
unsigned long enable_offset, unsigned int start,
- unsigned int stop)
+ unsigned int stop,
+ bool cawake_double_int)
{
struct hsi_dev *hsi_ctrl = pport->hsi_controller;
void __iomem *base = hsi_ctrl->base;
@@ -515,6 +605,10 @@ static u32 hsi_driver_int_proc(struct hsi_port *pport,
status_reg = hsi_inl(base, status_offset);
status_reg &= hsi_inl(base, enable_offset);
+ /* Check if we need to process an additional CAWAKE interrupt */
+ if (cawake_double_int)
+ status_reg |= HSI_CAWAKEDETECTED;
+
if (pport->cawake_off_event) {
dev_dbg(hsi_ctrl->dev, "CAWAKE detected from OFF mode.\n");
} else if (!status_reg) {
@@ -528,7 +622,6 @@ static u32 hsi_driver_int_proc(struct hsi_port *pport,
if (status_reg & HSI_BREAKDETECTED) {
dev_info(hsi_ctrl->dev, "Hardware BREAK on port %d\n", port);
- hsi_outl(0, base, HSI_HSR_BREAK_REG(port));
spin_unlock(&hsi_ctrl->lock);
hsi_port_event_handler(pport, HSI_EVENT_BREAK_DETECTED, NULL);
spin_lock(&hsi_ctrl->lock);
@@ -603,20 +696,36 @@ static u32 hsi_process_int_event(struct hsi_port *pport)
unsigned int port = pport->port_number;
unsigned int irq = pport->n_irq;
u32 status_reg;
+ bool cawake_double_int = false;
+
+ /* Clear CAWAKE backup interrupt */
+ hsi_driver_ack_interrupt(pport, HSI_CAWAKEDETECTED, true);
/* Process events for channels 0..7 */
status_reg = hsi_driver_int_proc(pport,
HSI_SYS_MPU_STATUS_REG(port, irq),
HSI_SYS_MPU_ENABLE_REG(port, irq),
0,
- min(pport->max_ch, (u8) HSI_SSI_CHANNELS_MAX) - 1);
+ min(pport->max_ch, (u8) HSI_SSI_CHANNELS_MAX) - 1,
+ cawake_double_int);
+
+ /* If another CAWAKE interrupt occured while previous is still being
+ * processed, mark it for extra processing */
+ if (hsi_driver_is_interrupt_pending(pport, HSI_CAWAKEDETECTED, true) &&
+ (status_reg & HSI_CAWAKEDETECTED)) {
+ dev_warn(pport->hsi_controller->dev, "New CAWAKE interrupt "
+ "detected during interrupt processing\n");
+ /* Force processing of backup CAWAKE interrupt */
+ cawake_double_int = true;
+ }
- /* Process events for channels 8..15 */
- if (pport->max_ch > HSI_SSI_CHANNELS_MAX)
+ /* Process events for channels 8..15 or backup interrupt if needed */
+ if ((pport->max_ch > HSI_SSI_CHANNELS_MAX) || cawake_double_int)
status_reg |= hsi_driver_int_proc(pport,
HSI_SYS_MPU_U_STATUS_REG(port, irq),
HSI_SYS_MPU_U_ENABLE_REG(port, irq),
- HSI_SSI_CHANNELS_MAX, pport->max_ch - 1);
+ HSI_SSI_CHANNELS_MAX, pport->max_ch - 1,
+ cawake_double_int);
return status_reg;
}
@@ -636,24 +745,27 @@ static void do_hsi_tasklet(unsigned long hsi_port)
status_reg = hsi_process_int_event(pport);
pport->in_int_tasklet = false;
+ clear_bit(HSI_FLAGS_TASKLET_LOCK, &pport->flags);
hsi_clocks_disable(hsi_ctrl->dev, __func__);
spin_unlock(&hsi_ctrl->lock);
- shceduled_already_flag = 0;
+
enable_irq(pport->irq);
}
static irqreturn_t hsi_mpu_handler(int irq, void *p)
{
- struct hsi_port *pport = p;
- if (shceduled_already_flag == 0) {
- shceduled_already_flag = 1;
+ struct hsi_port *pport = (struct hsi_port *) p;
+
+ /* Check no other interrupt handler has already scheduled the tasklet */
+ if (test_and_set_bit(HSI_FLAGS_TASKLET_LOCK, &pport->flags))
+ return IRQ_HANDLED;
+
tasklet_hi_schedule(&pport->hsi_tasklet);
- /*
- * Disable interrupt until Bottom Half has cleared the
- * IRQ status register
- */
+
+ /* Disable interrupt until Bottom Half has cleared the IRQ status */
+ /* register */
disable_irq_nosync(pport->irq);
- }
+
return IRQ_HANDLED;
}
diff --git a/drivers/power/max17040_battery.c b/drivers/power/max17040_battery.c
index 2f2f9a6..8ca350a 100644
--- a/drivers/power/max17040_battery.c
+++ b/drivers/power/max17040_battery.c
@@ -20,6 +20,10 @@
#include <linux/power_supply.h>
#include <linux/max17040_battery.h>
#include <linux/slab.h>
+#include <linux/android_alarm.h>
+#include <linux/suspend.h>
+#include <linux/interrupt.h>
+#include <linux/reboot.h>
#define MAX17040_VCELL_MSB 0x02
#define MAX17040_VCELL_LSB 0x03
@@ -34,12 +38,21 @@
#define MAX17040_CMD_MSB 0xFE
#define MAX17040_CMD_LSB 0xFF
-#define MAX17040_DELAY 1000
-#define MAX17040_BATTERY_FULL 95
+#define MAX17040_BATTERY_FULL 100
+
+#define HAS_ALERT_INTERRUPT(ver) (ver >= 3)
+
+#define FAST_POLL (1 * 60)
+#define SLOW_POLL (10 * 60)
+
+#define STATUS_CHARGABLE 0x0
+#define STATUS_CHARGE_FULL 0x1
+#define STATUS_ABNORMAL_TEMP 0x2
+#define STATUS_CHARGE_TIMEOVER 0x3
struct max17040_chip {
struct i2c_client *client;
- struct delayed_work work;
+ struct work_struct work;
struct power_supply battery;
struct max17040_platform_data *pdata;
@@ -51,6 +64,25 @@ struct max17040_chip {
int soc;
/* State Of Charge */
int status;
+ /* Health of Battery */
+ int bat_health;
+ /* Temperature of Battery */
+ int bat_temp;
+
+ struct notifier_block pm_notifier;
+ struct wake_lock work_wake_lock;
+
+ struct alarm alarm;
+ ktime_t last_poll;
+ int slow_poll;
+ int shutdown;
+ /* chip version */
+ u16 ver;
+
+ int charger_status;
+ unsigned long chg_limit_time;
+
+ bool is_timer_flag;
};
static int max17040_get_property(struct power_supply *psy,
@@ -64,6 +96,9 @@ static int max17040_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_STATUS:
val->intval = chip->status;
break;
+ case POWER_SUPPLY_PROP_HEALTH:
+ val->intval = chip->bat_health;
+ break;
case POWER_SUPPLY_PROP_ONLINE:
val->intval = chip->online;
break;
@@ -73,17 +108,25 @@ static int max17040_get_property(struct power_supply *psy,
case POWER_SUPPLY_PROP_CAPACITY:
val->intval = chip->soc;
break;
+ case POWER_SUPPLY_PROP_TEMP:
+ if (!chip->pdata->get_bat_temp)
+ return -ENODATA;
+ val->intval = chip->bat_temp;
+ break;
+ case POWER_SUPPLY_PROP_TECHNOLOGY:
+ val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+ break;
default:
return -EINVAL;
}
return 0;
}
-static int max17040_write_reg(struct i2c_client *client, int reg, u8 value)
+static int max17040_write_reg(struct i2c_client *client, int reg, u16 val)
{
int ret;
- ret = i2c_smbus_write_byte_data(client, reg, value);
+ ret = i2c_smbus_write_word_data(client, reg, cpu_to_be16(val));
if (ret < 0)
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
@@ -91,57 +134,83 @@ static int max17040_write_reg(struct i2c_client *client, int reg, u8 value)
return ret;
}
-static int max17040_read_reg(struct i2c_client *client, int reg)
+static int max17040_read_reg(struct i2c_client *client, int reg, u16 *val)
{
int ret;
- ret = i2c_smbus_read_byte_data(client, reg);
+ ret = i2c_smbus_read_word_data(client, reg);
- if (ret < 0)
+ if (ret < 0) {
dev_err(&client->dev, "%s: err %d\n", __func__, ret);
+ *val = 0;
+ return ret;
+ }
- return ret;
+ *val = be16_to_cpu(ret);
+ return 0;
}
static void max17040_reset(struct i2c_client *client)
{
- max17040_write_reg(client, MAX17040_CMD_MSB, 0x54);
- max17040_write_reg(client, MAX17040_CMD_LSB, 0x00);
+ max17040_write_reg(client, MAX17040_CMD_MSB, 0x5400);
+
+ msleep(125);
+
+ max17040_write_reg(client, MAX17040_MODE_MSB, 0x4000);
}
static void max17040_get_vcell(struct i2c_client *client)
{
struct max17040_chip *chip = i2c_get_clientdata(client);
- u8 msb;
- u8 lsb;
+ u16 val;
- msb = max17040_read_reg(client, MAX17040_VCELL_MSB);
- lsb = max17040_read_reg(client, MAX17040_VCELL_LSB);
-
- chip->vcell = (msb << 4) + (lsb >> 4);
+ if (!max17040_read_reg(client, MAX17040_VCELL_MSB, &val))
+ chip->vcell = (val >> 4) * 1250;
+ else
+ dev_warn(&client->dev, "i2c error, not updating vcell\n");
}
+#define TO_FIXED(a,b) (((a) << 8) + (b))
+#define FIXED_TO_INT(x) ((int)((x) >> 8))
+#define FIXED_MULT(x,y) ((((u32)(x) * (u32)(y)) + (1 << 7)) >> 8)
+#define FIXED_DIV(x,y) ((((u32)(x) << 8) + ((u32)(y) >> 1)) / (u32)(y))
+
static void max17040_get_soc(struct i2c_client *client)
{
struct max17040_chip *chip = i2c_get_clientdata(client);
- u8 msb;
- u8 lsb;
+ u32 val;
+ u32 fmin_cap = TO_FIXED(chip->pdata->min_capacity, 0);
+ u16 regval;
- msb = max17040_read_reg(client, MAX17040_SOC_MSB);
- lsb = max17040_read_reg(client, MAX17040_SOC_LSB);
+ if (max17040_read_reg(client, MAX17040_SOC_MSB, &regval)) {
+ dev_warn(&client->dev, "i2c error, not updating soc\n");
+ return;
+ }
- chip->soc = msb;
+ /* convert msb.lsb to Q8.8 */
+ val = TO_FIXED(regval >> 8, regval & 0xff);
+ if (val <= fmin_cap) {
+ chip->soc = 0;
+ return;
+ }
+
+ val = FIXED_MULT(TO_FIXED(100, 0), val - fmin_cap);
+ val = FIXED_DIV(val, TO_FIXED(100, 0) - fmin_cap);
+ chip->soc = clamp(FIXED_TO_INT(val), 0, 100);
}
static void max17040_get_version(struct i2c_client *client)
{
- u8 msb;
- u8 lsb;
-
- msb = max17040_read_reg(client, MAX17040_VER_MSB);
- lsb = max17040_read_reg(client, MAX17040_VER_LSB);
+ struct max17040_chip *chip = i2c_get_clientdata(client);
+ u16 val;
- dev_info(&client->dev, "MAX17040 Fuel-Gauge Ver %d%d\n", msb, lsb);
+ if (!max17040_read_reg(client, MAX17040_VER_MSB, &val)) {
+ chip->ver = val;
+ dev_info(&client->dev, "MAX17040 Fuel-Gauge Ver %d\n", val);
+ } else {
+ dev_err(&client->dev,
+ "Error reading version, some features disabled\n");
+ }
}
static void max17040_get_online(struct i2c_client *client)
@@ -164,45 +233,276 @@ static void max17040_get_status(struct i2c_client *client)
}
if (chip->pdata->charger_online()) {
- if (chip->pdata->charger_enable())
+ if (chip->pdata->charger_enable()) {
chip->status = POWER_SUPPLY_STATUS_CHARGING;
- else
- chip->status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+ } else {
+ chip->status =
+ chip->charger_status == STATUS_CHARGE_FULL ?
+ POWER_SUPPLY_STATUS_FULL :
+ POWER_SUPPLY_STATUS_NOT_CHARGING;
+ }
} else {
chip->status = POWER_SUPPLY_STATUS_DISCHARGING;
+ chip->chg_limit_time = 0;
+ chip->charger_status = STATUS_CHARGABLE;
}
+}
+
+static void max17040_get_temp_status(struct max17040_chip *chip)
+{
+ int r;
+ int t;
+
+ if (!chip->pdata->get_bat_temp)
+ return;
- if (chip->soc > MAX17040_BATTERY_FULL)
- chip->status = POWER_SUPPLY_STATUS_FULL;
+ r = chip->pdata->get_bat_temp(&t);
+
+ if (r < 0) {
+ dev_err(&chip->client->dev,
+ "error %d reading battery temperature\n", r);
+ chip->bat_health = POWER_SUPPLY_HEALTH_UNKNOWN;
+ return;
+ }
+
+ chip->bat_temp = t;
+
+ if (chip->bat_temp >= chip->pdata->high_block_temp) {
+ chip->bat_health = POWER_SUPPLY_HEALTH_OVERHEAT;
+ } else if (chip->bat_temp <= chip->pdata->high_recover_temp &&
+ chip->bat_temp >= chip->pdata->low_recover_temp) {
+ chip->bat_health = POWER_SUPPLY_HEALTH_GOOD;
+ } else if (chip->bat_temp <= chip->pdata->low_block_temp) {
+ chip->bat_health = POWER_SUPPLY_HEALTH_COLD;
+ }
}
-static void max17040_work(struct work_struct *work)
+static void max17040_charger_update(struct max17040_chip *chip)
{
- struct max17040_chip *chip;
+ ktime_t ktime;
+ struct timespec cur_time;
- chip = container_of(work, struct max17040_chip, work.work);
+ if (!chip->pdata->is_full_charge || !chip->pdata->allow_charging)
+ return;
+
+ ktime = alarm_get_elapsed_realtime();
+ cur_time = ktime_to_timespec(ktime);
+
+ switch (chip->charger_status) {
+ case STATUS_CHARGABLE:
+ if (chip->pdata->is_full_charge() &&
+ chip->soc >= MAX17040_BATTERY_FULL &&
+ chip->vcell > chip->pdata->fully_charged_vol) {
+ chip->charger_status = STATUS_CHARGE_FULL;
+ chip->is_timer_flag = true;
+ chip->chg_limit_time = 0;
+ chip->pdata->allow_charging(0);
+ } else if (chip->chg_limit_time &&
+ cur_time.tv_sec > chip->chg_limit_time) {
+ chip->charger_status = STATUS_CHARGE_TIMEOVER;
+ chip->is_timer_flag = true;
+ chip->chg_limit_time = 0;
+ chip->pdata->allow_charging(0);
+ } else if (chip->bat_health == POWER_SUPPLY_HEALTH_OVERHEAT ||
+ chip->bat_health == POWER_SUPPLY_HEALTH_COLD) {
+ chip->charger_status = STATUS_ABNORMAL_TEMP;
+ chip->chg_limit_time = 0;
+ chip->pdata->allow_charging(0);
+ }
+ break;
+
+ case STATUS_CHARGE_FULL:
+ if (chip->vcell <= chip->pdata->recharge_vol) {
+ chip->charger_status = STATUS_CHARGABLE;
+ chip->pdata->allow_charging(1);
+ }
+ break;
+
+ case STATUS_ABNORMAL_TEMP:
+ if (chip->bat_temp <= chip->pdata->high_recover_temp &&
+ chip->bat_temp >=
+ chip->pdata->low_recover_temp) {
+ chip->charger_status = STATUS_CHARGABLE;
+ chip->pdata->allow_charging(1);
+ }
+ break;
+
+ case STATUS_CHARGE_TIMEOVER:
+ if (chip->vcell <= chip->pdata->fully_charged_vol) {
+ chip->charger_status = STATUS_CHARGABLE;
+ chip->pdata->allow_charging(1);
+ }
+ break;
+
+ default:
+ dev_err(&chip->client->dev, "%s : invalid status [%d]\n",
+ __func__, chip->charger_status);
+ }
+
+ if (!chip->chg_limit_time &&
+ chip->charger_status == STATUS_CHARGABLE) {
+ chip->chg_limit_time =
+ chip->is_timer_flag ?
+ cur_time.tv_sec + chip->pdata->limit_recharging_time :
+ cur_time.tv_sec + chip->pdata->limit_charging_time;
+ }
+
+ dev_dbg(&chip->client->dev, "%s, Charger Status : %d, Limit Time : %ld\n",
+ __func__, chip->charger_status, chip->chg_limit_time);
+}
+
+static void max17040_update(struct max17040_chip *chip)
+{
+ int prev_status = chip->status;
+ int prev_soc = chip->soc;
max17040_get_vcell(chip->client);
max17040_get_soc(chip->client);
max17040_get_online(chip->client);
+ max17040_get_temp_status(chip);
+ if (chip->pdata->charger_online())
+ max17040_charger_update(chip);
+ else
+ chip->is_timer_flag = false;
max17040_get_status(chip->client);
+ if ((chip->soc != prev_soc) || (chip->status != prev_status))
+ power_supply_changed(&chip->battery);
+
+ dev_info(&chip->client->dev, "online = %d vcell = %d soc = %d "
+ "status = %d health = %d temp = %d "
+ "charger status = %d\n", chip->online, chip->vcell,
+ chip->soc, chip->status, chip->bat_health, chip->bat_temp,
+ chip->charger_status);
+}
+
+static void max17040_program_alarm(struct max17040_chip *chip, int seconds)
+{
+ ktime_t low_interval = ktime_set(seconds - 10, 0);
+ ktime_t slack = ktime_set(20, 0);
+ ktime_t next;
- schedule_delayed_work(&chip->work, MAX17040_DELAY);
+ next = ktime_add(chip->last_poll, low_interval);
+ alarm_start_range(&chip->alarm, next, ktime_add(next, slack));
+}
+
+static void max17040_work(struct work_struct *work)
+{
+ unsigned long flags;
+ struct timespec ts;
+ struct max17040_chip *chip;
+
+ chip = container_of(work, struct max17040_chip, work);
+
+ max17040_update(chip);
+
+ chip->last_poll = alarm_get_elapsed_realtime();
+ ts = ktime_to_timespec(chip->last_poll);
+
+ local_irq_save(flags);
+ wake_unlock(&chip->work_wake_lock);
+ if (!chip->shutdown)
+ max17040_program_alarm(chip, FAST_POLL);
+ local_irq_restore(flags);
+}
+
+static void max17040_battery_alarm(struct alarm *alarm)
+{
+ struct max17040_chip *chip =
+ container_of(alarm, struct max17040_chip, alarm);
+
+ wake_lock(&chip->work_wake_lock);
+ schedule_work(&chip->work);
+
+}
+
+static void max17040_ext_power_changed(struct power_supply *psy)
+{
+ struct max17040_chip *chip = container_of(psy,
+ struct max17040_chip, battery);
+
+ wake_lock(&chip->work_wake_lock);
+ schedule_work(&chip->work);
}
static enum power_supply_property max17040_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
+ POWER_SUPPLY_PROP_HEALTH,
POWER_SUPPLY_PROP_ONLINE,
POWER_SUPPLY_PROP_VOLTAGE_NOW,
POWER_SUPPLY_PROP_CAPACITY,
+ POWER_SUPPLY_PROP_TECHNOLOGY,
+ /* must be last */
+ POWER_SUPPLY_PROP_TEMP,
};
+static int max17040_pm_notifier(struct notifier_block *notifier,
+ unsigned long pm_event,
+ void *unused)
+{
+ struct max17040_chip *chip =
+ container_of(notifier, struct max17040_chip, pm_notifier);
+
+ switch (pm_event) {
+ case PM_SUSPEND_PREPARE:
+ if (!chip->pdata->charger_enable()) {
+ cancel_work_sync(&chip->work);
+ max17040_program_alarm(chip, SLOW_POLL);
+ chip->slow_poll = 1;
+ }
+ break;
+
+ case PM_POST_SUSPEND:
+ /* We might be on a slow sample cycle. If we're
+ * resuming we should resample the battery state
+ * if it's been over a minute since we last did
+ * so, and move back to sampling every minute until
+ * we suspend again.
+ */
+ if (chip->slow_poll) {
+ max17040_program_alarm(chip, FAST_POLL);
+ chip->slow_poll = 0;
+ }
+ break;
+ }
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block max17040_pm_notifier_block = {
+ .notifier_call = max17040_pm_notifier,
+};
+
+static irqreturn_t max17040_alert(int irq, void *data)
+{
+ struct max17040_chip *chip = data;
+ struct i2c_client *client = chip->client;
+
+ max17040_get_vcell(chip->client);
+ max17040_get_soc(chip->client);
+
+ dev_info(&client->dev, "Low battery alert fired: soc=%d vcell=%d\n",
+ chip->soc, chip->vcell);
+
+ if (chip->soc != 0) {
+ dev_err(&client->dev, "false low battery alert, ignoring\n");
+ goto out;
+ }
+
+ dev_info(&client->dev, "shutting down due to low battery...\n");
+ kernel_power_off();
+
+out:
+ return IRQ_HANDLED;
+}
+
static int __devinit max17040_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct max17040_chip *chip;
int ret;
+ u16 val;
+ u16 athd;
+ int num_props = ARRAY_SIZE(max17040_battery_props);
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
return -EIO;
@@ -214,66 +514,117 @@ static int __devinit max17040_probe(struct i2c_client *client,
chip->client = client;
chip->pdata = client->dev.platform_data;
+ if (!chip->pdata->get_bat_temp)
+ num_props--;
+
i2c_set_clientdata(client, chip);
chip->battery.name = "battery";
chip->battery.type = POWER_SUPPLY_TYPE_BATTERY;
chip->battery.get_property = max17040_get_property;
chip->battery.properties = max17040_battery_props;
- chip->battery.num_properties = ARRAY_SIZE(max17040_battery_props);
+ chip->battery.num_properties = num_props;
+ chip->battery.external_power_changed = max17040_ext_power_changed;
+
+ chip->bat_health = POWER_SUPPLY_HEALTH_GOOD;
+ chip->charger_status = STATUS_CHARGABLE;
+ chip->is_timer_flag = false;
+ chip->chg_limit_time = 0;
+
+ if (!chip->pdata->high_block_temp)
+ chip->pdata->high_block_temp = 500;
+ if (!chip->pdata->high_recover_temp)
+ chip->pdata->high_recover_temp = 420;
+ if (!chip->pdata->low_block_temp)
+ chip->pdata->low_block_temp = -50;
+ if (!chip->pdata->fully_charged_vol)
+ chip->pdata->fully_charged_vol = 4150000;
+ if (!chip->pdata->recharge_vol)
+ chip->pdata->recharge_vol = 4140000;
+ if (!chip->pdata->limit_charging_time)
+ chip->pdata->limit_charging_time = 21600;
+ if (!chip->pdata->limit_recharging_time)
+ chip->pdata->limit_recharging_time = 5400;
+
+ chip->last_poll = alarm_get_elapsed_realtime();
+ alarm_init(&chip->alarm, ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,
+ max17040_battery_alarm);
+
+ wake_lock_init(&chip->work_wake_lock, WAKE_LOCK_SUSPEND,
+ "max17040-battery");
+
+ if (!chip->pdata->skip_reset)
+ max17040_reset(client);
+
+ max17040_get_version(client);
+ INIT_WORK(&chip->work, max17040_work);
ret = power_supply_register(&client->dev, &chip->battery);
if (ret) {
dev_err(&client->dev, "failed: power supply register\n");
- kfree(chip);
- return ret;
+ goto err_battery_supply_register;
}
- max17040_reset(client);
- max17040_get_version(client);
-
- INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17040_work);
- schedule_delayed_work(&chip->work, MAX17040_DELAY);
+ /* i2c-core does not support dev_pm_ops.prepare and .complete
+ * So, used pm_notifier for use android_alarm.
+ */
+ chip->pm_notifier = max17040_pm_notifier_block;
+ ret = register_pm_notifier(&chip->pm_notifier);
+ if (ret) {
+ dev_err(&client->dev, "failed: register pm notifier\n");
+ goto err_pm_notifier;
+ }
+ schedule_work(&chip->work);
+
+ if (HAS_ALERT_INTERRUPT(chip->ver) && chip->pdata->use_fuel_alert) {
+ /* setting the low SOC alert threshold */
+ if (!max17040_read_reg(client, MAX17040_RCOMP_MSB, &val)) {
+ athd = chip->pdata->min_capacity > 1 ?
+ chip->pdata->min_capacity - 1 : 0;
+ max17040_write_reg(client, MAX17040_RCOMP_MSB,
+ (val & ~0x1f) | (-athd & 0x1f));
+ } else {
+ dev_err(&client->dev,
+ "Error setting battery alert threshold\n");
+ }
+
+ /* add alert irq handler */
+ ret = request_threaded_irq(client->irq, NULL, max17040_alert,
+ IRQF_TRIGGER_FALLING, "fuel gauge alert", chip);
+ if (ret < 0) {
+ dev_err(&client->dev,
+ "request_threaded_irq() failed: %d", ret);
+ goto err_pm_notifier;
+ }
+ }
return 0;
-}
-
-static int __devexit max17040_remove(struct i2c_client *client)
-{
- struct max17040_chip *chip = i2c_get_clientdata(client);
+err_pm_notifier:
power_supply_unregister(&chip->battery);
- cancel_delayed_work(&chip->work);
+err_battery_supply_register:
+ wake_lock_destroy(&chip->work_wake_lock);
+ alarm_cancel(&chip->alarm);
kfree(chip);
- return 0;
-}
-
-#ifdef CONFIG_PM
-static int max17040_suspend(struct i2c_client *client,
- pm_message_t state)
-{
- struct max17040_chip *chip = i2c_get_clientdata(client);
-
- cancel_delayed_work(&chip->work);
- return 0;
+ return ret;
}
-static int max17040_resume(struct i2c_client *client)
+static int __devexit max17040_remove(struct i2c_client *client)
{
struct max17040_chip *chip = i2c_get_clientdata(client);
-
- schedule_delayed_work(&chip->work, MAX17040_DELAY);
+ chip->shutdown = 1;
+ unregister_pm_notifier(&chip->pm_notifier);
+ power_supply_unregister(&chip->battery);
+ alarm_cancel(&chip->alarm);
+ cancel_work_sync(&chip->work);
+ wake_lock_destroy(&chip->work_wake_lock);
+ if (HAS_ALERT_INTERRUPT(chip->ver) && chip->pdata->use_fuel_alert)
+ free_irq(client->irq, chip);
+ kfree(chip);
return 0;
}
-#else
-
-#define max17040_suspend NULL
-#define max17040_resume NULL
-
-#endif /* CONFIG_PM */
-
static const struct i2c_device_id max17040_id[] = {
{ "max17040", 0 },
{ }
@@ -286,8 +637,6 @@ static struct i2c_driver max17040_i2c_driver = {
},
.probe = max17040_probe,
.remove = __devexit_p(max17040_remove),
- .suspend = max17040_suspend,
- .resume = max17040_resume,
.id_table = max17040_id,
};
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c
index 1e01a33..5548917 100644
--- a/drivers/remoteproc/omap_remoteproc.c
+++ b/drivers/remoteproc/omap_remoteproc.c
@@ -22,6 +22,7 @@
#include <linux/platform_device.h>
#include <linux/remoteproc.h>
#include <linux/sched.h>
+#include <linux/rproc_drm.h>
#include <plat/iommu.h>
#include <plat/omap_device.h>
@@ -446,9 +447,13 @@ static inline int omap_rproc_start(struct rproc *rproc, u64 bootaddr)
int ret = 0;
if (rproc->secure_mode) {
- pr_err("TODO: Call secure service to authenticate\n");
- if (ret)
+ rproc->secure_reset = true;
+ ret = rproc_drm_invoke_service(rproc->secure_mode);
+ if (ret) {
+ dev_err(rproc->dev, "rproc_drm_invoke_service failed "
+ "for secure_enable ret = 0x%x\n", ret);
return -ENXIO;
+ }
}
#ifdef CONFIG_REMOTE_PROC_AUTOSUSPEND
@@ -509,9 +514,18 @@ static inline int omap_rproc_stop(struct rproc *rproc)
struct omap_rproc_pdata *pdata = dev->platform_data;
struct omap_rproc_timers_info *timers = pdata->timers;
int ret, i;
+
#ifdef CONFIG_REMOTE_PROC_AUTOSUSPEND
_destroy_pm_flags(rproc);
#endif
+ if (rproc->secure_reset) {
+ ret = rproc_drm_invoke_service(false);
+ if (ret)
+ dev_err(rproc->dev, "rproc_drm_invoke_service failed "
+ "for secure disable ret = 0x%x\n", ret);
+ rproc->secure_reset = false;
+ }
+
ret = omap_device_idle(pdev);
if (ret)
goto err;
diff --git a/drivers/rpmsg/rpmsg_omx.c b/drivers/rpmsg/rpmsg_omx.c
index 972a918..bd417c4 100644
--- a/drivers/rpmsg/rpmsg_omx.c
+++ b/drivers/rpmsg/rpmsg_omx.c
@@ -234,8 +234,10 @@ static void rpmsg_omx_cb(struct rpmsg_channel *rpdev, void *data, int len,
dev_dbg(&rpdev->dev, "%s: incoming msg src 0x%x type %d len %d\n",
__func__, src, hdr->type, hdr->len);
+#if 0
print_hex_dump(KERN_DEBUG, "rpmsg_omx RX: ", DUMP_PREFIX_NONE, 16, 1,
data, len, true);
+#endif
switch (hdr->type) {
case OMX_CONN_RSP:
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 247e887..897b7f4 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -508,8 +508,10 @@ int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst,
dev_dbg(dev, "TX From 0x%x, To 0x%x, Len %d, Flags %d, Unused %d\n",
msg->src, msg->dst, msg->len,
msg->flags, msg->unused);
+#if 0
print_hex_dump(KERN_DEBUG, "rpmsg_virtio TX: ", DUMP_PREFIX_NONE, 16, 1,
msg, sizeof(*msg) + msg->len, true);
+#endif
offset = ((unsigned long) msg) - ((unsigned long) vrp->rbufs);
sim_addr = vrp->sim_base + offset;
@@ -557,8 +559,10 @@ static void rpmsg_recv_done(struct virtqueue *rvq)
dev_dbg(dev, "From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Unused: %d\n",
msg->src, msg->dst, msg->len,
msg->flags, msg->unused);
+#if 0
print_hex_dump(KERN_DEBUG, "rpmsg_virtio RX: ", DUMP_PREFIX_NONE, 16, 1,
msg, sizeof(*msg) + msg->len, true);
+#endif
/* fetch the callback of the appropriate user */
spin_lock(&vrp->endpoints_lock);
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 34e3da5..530ad9a 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1332,6 +1332,22 @@ int usb_resume(struct device *dev, pm_message_t msg)
* Unbind the interfaces that will need rebinding later.
*/
} else {
+ /* If a device aborts suspend, usb_resume may be called on a
+ * device whose parent has been auto-suspended but its dpm power
+ * state in_suspend==false. So dpm doesn't try to resume the
+ * parent and the device doesn't wait for the parent to resume.
+ * Recursively resume the parents when this happens.
+ */
+ if (udev->parent && msg.event == PM_EVENT_RESUME
+ && udev->parent->state == USB_STATE_SUSPENDED) {
+ status = usb_resume(&udev->parent->dev, msg);
+ if (status) {
+ dev_err(dev, "%s: failed to resume parent\n",
+ __func__);
+ return status;
+ }
+ }
+
status = usb_resume_both(udev, msg);
if (status == 0) {
pm_runtime_disable(dev);
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a428aa0..a7c96f8 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1634,12 +1634,13 @@ void usb_disconnect(struct usb_device **pdev)
{
struct usb_device *udev = *pdev;
int i;
- struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+ struct usb_hcd *hcd;
if (!udev) {
pr_debug ("%s nodev\n", __func__);
return;
}
+ hcd = bus_to_hcd(udev->bus);
/* mark the device as inactive, so any further urb submissions for
* this device (and any of its children) will fail immediately.
@@ -2246,7 +2247,7 @@ static int check_port_resume_type(struct usb_device *udev,
* so try a reset-resume instead.
*/
else if (!(portstatus & USB_PORT_STAT_ENABLE) && !udev->reset_resume) {
- if (udev->persist_enabled)
+ if (udev->persist_enabled && !(udev->quirks & USB_QUIRK_NO_RESET_RESUME))
udev->reset_resume = 1;
else
status = -ENODEV;
@@ -2418,6 +2419,9 @@ static int finish_port_resume(struct usb_device *udev)
retry_reset_resume:
status = usb_reset_and_verify_device(udev);
+ if (udev->quirks & USB_QUIRK_NO_GET_STATUS)
+ goto done;
+
/* 10.5.4.5 says be sure devices in the tree are still there.
* For now let's assume the device didn't go crazy on resume,
* and device drivers will know about any resume quirks.
@@ -2429,7 +2433,8 @@ static int finish_port_resume(struct usb_device *udev)
status = (status > 0 ? 0 : -ENODEV);
/* If a normal resume failed, try doing a reset-resume */
- if (status && !udev->reset_resume && udev->persist_enabled) {
+ if (status && !udev->reset_resume && udev->persist_enabled &&
+ !(udev->quirks & USB_QUIRK_NO_RESET_RESUME)) {
dev_dbg(&udev->dev, "retry with reset-resume\n");
udev->reset_resume = 1;
goto retry_reset_resume;
@@ -2456,6 +2461,7 @@ static int finish_port_resume(struct usb_device *udev)
}
status = 0;
}
+done:
return status;
}
@@ -4043,3 +4049,18 @@ void usb_queue_reset_device(struct usb_interface *iface)
schedule_work(&iface->reset_ws);
}
EXPORT_SYMBOL_GPL(usb_queue_reset_device);
+
+void usb_force_disconnect(struct usb_device *udev)
+{
+ struct usb_hub *parent_hub;
+ int port1 = udev->portnum;
+
+ if (!udev->parent)
+ return;
+
+ parent_hub = hdev_to_hub(udev->parent);
+ if (!parent_hub)
+ return;
+
+ hub_port_logical_disconnect(parent_hub, port1);
+}
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index 81ce6a8..6e6bfc9 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -96,6 +96,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* INTEL VALUE SSD */
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
+ /* CMC221 LTE Modem */
+ { USB_DEVICE(0x04e8, 0x6999), .driver_info =
+ USB_QUIRK_NO_RESET_RESUME | USB_QUIRK_NO_GET_STATUS },
+
{ } /* terminating entry must be last */
};
diff --git a/drivers/usb/gadget/android.c b/drivers/usb/gadget/android.c
index 8df739c..8988772 100644
--- a/drivers/usb/gadget/android.c
+++ b/drivers/usb/gadget/android.c
@@ -55,6 +55,7 @@
#include "f_rndis.c"
#include "rndis.c"
#include "u_ether.c"
+#include "f_dm.c"
MODULE_AUTHOR("Mike Lockwood");
MODULE_DESCRIPTION("Android Composite USB Driver");
@@ -67,6 +68,9 @@ static const char longname[] = "Gadget Android";
#define VENDOR_ID 0x18D1
#define PRODUCT_ID 0x0001
+/* DM_PORT NUM : /dev/ttyGS* port number */
+#define DM_PORT_NUM 1
+
struct android_usb_function {
char *name;
void *config;
@@ -642,6 +646,17 @@ static struct android_usb_function accessory_function = {
};
+static int dm_function_bind_config(struct android_usb_function *f,
+ struct usb_configuration *c)
+{
+ return dm_bind_config(c, DM_PORT_NUM);
+}
+
+static struct android_usb_function dm_function = {
+ .name = "dm",
+ .bind_config = dm_function_bind_config,
+};
+
static struct android_usb_function *supported_functions[] = {
&adb_function,
&acm_function,
@@ -650,6 +665,7 @@ static struct android_usb_function *supported_functions[] = {
&rndis_function,
&mass_storage_function,
&accessory_function,
+ &dm_function,
NULL
};
diff --git a/drivers/usb/gadget/f_dm.c b/drivers/usb/gadget/f_dm.c
new file mode 100644
index 0000000..ca394c3
--- /dev/null
+++ b/drivers/usb/gadget/f_dm.c
@@ -0,0 +1,306 @@
+/*
+ * f_dm.c - generic USB serial function driver
+ * modified from f_serial.c and f_diag.c
+ * ttyGS*
+ *
+ * Copyright (C) 2003 Al Borchers (alborchers@steinerpoint.com)
+ * Copyright (C) 2008 by David Brownell
+ * Copyright (C) 2008 by Nokia Corporation
+ *
+ * This software is distributed under the terms of the GNU General
+ * Public License ("GPL") as published by the Free Software Foundation,
+ * either version 2 of that License or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+
+#include "u_serial.h"
+#include "gadget_chips.h"
+
+/*
+ * This function packages a simple "generic serial" port with no real
+ * control mechanisms, just raw data transfer over two bulk endpoints.
+ *
+ * Because it's not standardized, this isn't as interoperable as the
+ * CDC ACM driver. However, for many purposes it's just as functional
+ * if you can arrange appropriate host side drivers.
+ */
+
+struct dm_descs {
+ struct usb_endpoint_descriptor *in;
+ struct usb_endpoint_descriptor *out;
+};
+
+struct f_dm {
+ struct gserial port;
+ u8 data_id;
+ u8 port_num;
+
+ struct dm_descs fs;
+ struct dm_descs hs;
+};
+
+static inline struct f_dm *func_to_dm(struct usb_function *f)
+{
+ return container_of(f, struct f_dm, port.func);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* interface descriptor: */
+static struct usb_interface_descriptor dm_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ /* .bInterfaceNumber = DYNAMIC */
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 0x10,
+ .bInterfaceProtocol = 0x01,
+ /* .iInterface = DYNAMIC */
+};
+
+/* full speed support: */
+
+static struct usb_endpoint_descriptor dm_fs_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 dm_fs_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_descriptor_header *dm_fs_function[] = {
+ (struct usb_descriptor_header *) &dm_interface_desc,
+ (struct usb_descriptor_header *) &dm_fs_in_desc,
+ (struct usb_descriptor_header *) &dm_fs_out_desc,
+ NULL,
+};
+
+/* high speed support: */
+
+static struct usb_endpoint_descriptor dm_hs_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_endpoint_descriptor dm_hs_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = __constant_cpu_to_le16(512),
+};
+
+static struct usb_descriptor_header *dm_hs_function[] = {
+ (struct usb_descriptor_header *) &dm_interface_desc,
+ (struct usb_descriptor_header *) &dm_hs_in_desc,
+ (struct usb_descriptor_header *) &dm_hs_out_desc,
+ NULL,
+};
+
+/* string descriptors: */
+#define F_DM_IDX 0
+
+static struct usb_string dm_string_defs[] = {
+ [F_DM_IDX].s = "Samsung Android DM",
+ { /* ZEROES END LIST */ },
+};
+
+static struct usb_gadget_strings dm_string_table = {
+ .language = 0x0409, /* en-us */
+ .strings = dm_string_defs,
+};
+
+static struct usb_gadget_strings *dm_strings[] = {
+ &dm_string_table,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int dm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
+{
+ struct f_dm *dm = func_to_dm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+ int status;
+
+ /* we know alt == 0, so this is an activation or a reset */
+
+ if (dm->port.in->driver_data) {
+ DBG(cdev, "reset generic ttyGS%d\n", dm->port_num);
+ gserial_disconnect(&dm->port);
+ } else {
+ DBG(cdev, "activate generic ttyGS%d\n", dm->port_num);
+ }
+ dm->port.in_desc = ep_choose(cdev->gadget,
+ dm->hs.in, dm->fs.in);
+ dm->port.out_desc = ep_choose(cdev->gadget,
+ dm->hs.out, dm->fs.out);
+ status = gserial_connect(&dm->port, dm->port_num);
+
+ if (status < 0) {
+ printk(KERN_ERR "fail to activate generic ttyGS%d\n",
+ dm->port_num);
+
+ return status;
+ }
+
+ return 0;
+}
+
+static void dm_disable(struct usb_function *f)
+{
+ struct f_dm *dm = func_to_dm(f);
+ struct usb_composite_dev *cdev = f->config->cdev;
+
+ DBG(cdev, "generic ttyGS%d deactivated\n", dm->port_num);
+ gserial_disconnect(&dm->port);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* serial function driver setup/binding */
+
+static int
+dm_bind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = c->cdev;
+ struct f_dm *dm = func_to_dm(f);
+ int status;
+ struct usb_ep *ep;
+
+ /* allocate instance-specific interface IDs */
+ status = usb_interface_id(c, f);
+ if (status < 0)
+ goto fail;
+ dm->data_id = status;
+ dm_interface_desc.bInterfaceNumber = status;
+
+ status = -ENODEV;
+
+ /* allocate instance-specific endpoints */
+ ep = usb_ep_autoconfig(cdev->gadget, &dm_fs_in_desc);
+ if (!ep)
+ goto fail;
+ dm->port.in = ep;
+ ep->driver_data = cdev; /* claim */
+
+ ep = usb_ep_autoconfig(cdev->gadget, &dm_fs_out_desc);
+ if (!ep)
+ goto fail;
+ dm->port.out = ep;
+ ep->driver_data = cdev; /* claim */
+ printk(KERN_INFO "[%s] in =0x%x , out =0x%x\n", __func__,
+ dm->port.in, dm->port.out);
+
+ /* copy descriptors, and track endpoint copies */
+ f->descriptors = usb_copy_descriptors(dm_fs_function);
+
+ dm->fs.in = usb_find_endpoint(dm_fs_function,
+ f->descriptors, &dm_fs_in_desc);
+ dm->fs.out = usb_find_endpoint(dm_fs_function,
+ f->descriptors, &dm_fs_out_desc);
+
+
+ /* support all relevant hardware speeds... we expect that when
+ * hardware is dual speed, all bulk-capable endpoints work at
+ * both speeds
+ */
+ if (gadget_is_dualspeed(c->cdev->gadget)) {
+ dm_hs_in_desc.bEndpointAddress =
+ dm_fs_in_desc.bEndpointAddress;
+ dm_hs_out_desc.bEndpointAddress =
+ dm_fs_out_desc.bEndpointAddress;
+
+ /* copy descriptors, and track endpoint copies */
+ f->hs_descriptors = usb_copy_descriptors(dm_hs_function);
+
+ dm->hs.in = usb_find_endpoint(dm_hs_function,
+ f->hs_descriptors, &dm_hs_in_desc);
+ dm->hs.out = usb_find_endpoint(dm_hs_function,
+ f->hs_descriptors, &dm_hs_out_desc);
+ }
+
+ DBG(cdev, "generic ttyGS%d: %s speed IN/%s OUT/%s\n",
+ dm->port_num,
+ gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
+ dm->port.in->name, dm->port.out->name);
+ return 0;
+
+fail:
+ /* we might as well release our claims on endpoints */
+ if (dm->port.out)
+ dm->port.out->driver_data = NULL;
+ if (dm->port.in)
+ dm->port.in->driver_data = NULL;
+
+ printk(KERN_ERR "%s: can't bind, err %d\n", f->name, status);
+
+ return status;
+}
+
+static void
+dm_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ if (gadget_is_dualspeed(c->cdev->gadget))
+ usb_free_descriptors(f->hs_descriptors);
+ usb_free_descriptors(f->descriptors);
+ kfree(func_to_dm(f));
+}
+
+/*
+ * dm_bind_config - add a generic serial function to a configuration
+ * @c: the configuration to support the serial instance
+ * @port_num: /dev/ttyGS* port this interface will use
+ * Context: single threaded during gadget setup
+ *
+ * Returns zero on success, else negative errno.
+ *
+ * Caller must have called @gserial_setup() with enough ports to
+ * handle all the ones it binds. Caller is also responsible
+ * for calling @gserial_cleanup() before module unload.
+ */
+int dm_bind_config(struct usb_configuration *c, u8 port_num)
+{
+ struct f_dm *dm;
+ int status;
+
+ /* REVISIT might want instance-specific strings to help
+ * distinguish instances ...
+ */
+
+ /* maybe allocate device-global string ID */
+ if (dm_string_defs[F_DM_IDX].id == 0) {
+ status = usb_string_id(c->cdev);
+ if (status < 0)
+ return status;
+ dm_string_defs[F_DM_IDX].id = status;
+ }
+
+ /* allocate and initialize one new instance */
+ dm = kzalloc(sizeof *dm, GFP_KERNEL);
+ if (!dm)
+ return -ENOMEM;
+
+ dm->port_num = port_num;
+
+ dm->port.func.name = "dm";
+ dm->port.func.strings = dm_strings;
+ dm->port.func.bind = dm_bind;
+ dm->port.func.unbind = dm_unbind;
+ dm->port.func.set_alt = dm_set_alt;
+ dm->port.func.disable = dm_disable;
+
+ status = usb_add_function(c, &dm->port.func);
+ if (status)
+ kfree(dm);
+ return status;
+}
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 40a844c..e2fd8db 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -18,6 +18,9 @@
/* this file is part of ehci-hcd.c */
+#define DEBUG
+#define BUF_SIZE (1096)
+
#define ehci_dbg(ehci, fmt, args...) \
dev_dbg (ehci_to_hcd(ehci)->self.controller , fmt , ## args )
#define ehci_err(ehci, fmt, args...) \
@@ -121,13 +124,16 @@ static inline void dbg_hcc_params (struct ehci_hcd *ehci, char *label) {}
static void __maybe_unused
dbg_qtd (const char *label, struct ehci_hcd *ehci, struct ehci_qtd *qtd)
{
- ehci_dbg(ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
+ //ehci_dbg(ehci, "%s td %p n%08x %08x t%08x p0=%08x\n", label, qtd,
+ printk("%s %p n%08x %08x t%08x p0=%08x\n", label, qtd,
hc32_to_cpup(ehci, &qtd->hw_next),
hc32_to_cpup(ehci, &qtd->hw_alt_next),
hc32_to_cpup(ehci, &qtd->hw_token),
hc32_to_cpup(ehci, &qtd->hw_buf [0]));
if (qtd->hw_buf [1])
- ehci_dbg(ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
+ //ehci_dbg(ehci, " p1=%08x p2=%08x p3=%08x p4=%08x\n",
+ printk("%s p1=%08x p2=%08x p3=%08x p4=%08x\n",
+ label,
hc32_to_cpup(ehci, &qtd->hw_buf[1]),
hc32_to_cpup(ehci, &qtd->hw_buf[2]),
hc32_to_cpup(ehci, &qtd->hw_buf[3]),
@@ -139,7 +145,8 @@ dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
{
struct ehci_qh_hw *hw = qh->hw;
- ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
+ //ehci_dbg (ehci, "%s qh %p n%08x info %x %x qtd %x\n", label,
+ printk("%s qh %p n%08x info %x %x qtd %x\n", label,
qh, hw->hw_next, hw->hw_info1, hw->hw_info2, hw->hw_current);
dbg_qtd("overlay", ehci, (struct ehci_qtd *) &hw->hw_qtd_next);
}
@@ -467,6 +474,15 @@ static void qh_lines (
(cpu_to_hc32(ehci, QTD_TOGGLE) & hw->hw_token)
? "data1" : "data0",
(hc32_to_cpup(ehci, &hw->hw_alt_next) >> 1) & 0x0f);
+ printk ("\nqh/%p dev%d %cs ep%d %08x %08x (%08x%c %s nak%d)",
+ qh, scratch & 0x007f,
+ speed_char (scratch),
+ (scratch >> 8) & 0x000f,
+ scratch, hc32_to_cpup(ehci, &hw->hw_info2),
+ hc32_to_cpup(ehci, &hw->hw_token), mark,
+ (cpu_to_hc32(ehci, QTD_TOGGLE) & hw->hw_token)
+ ? "data1" : "data0",
+ (hc32_to_cpup(ehci, &hw->hw_alt_next) >> 1) & 0x0f);
size -= temp;
next += temp;
@@ -497,6 +513,22 @@ static void qh_lines (
(scratch >> 16) & 0x7fff,
scratch,
td->urb);
+ printk("\n\t\ttd-%p%c%s len=%d %08x\n \t\turb %p t-flag-%x t-buf-%p t-dma-%x\n",
+ td, mark, ({ char *tmp;
+ switch ((scratch>>8)&0x03) {
+ case 0: tmp = "out"; break;
+ case 1: tmp = "in"; break;
+ case 2: tmp = "setup"; break;
+ default: tmp = "?"; break;
+ } tmp;}),
+ (scratch >> 16) & 0x7fff,
+ scratch,
+ td->urb,
+ td->urb->transfer_flags,
+ td->urb->transfer_buffer,
+ td->urb->transfer_dma);
+ /* print the td */
+ dbg_qtd("\t\t", ehci, td);
if (size < temp)
temp = size;
size -= temp;
@@ -552,6 +584,43 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
return strlen(buf->output_buf);
}
+
+static char array[BUF_SIZE];
+void print_async_list(void)
+{
+ struct debug_buffer dbg_buffer, *buf;
+ extern struct usb_hcd *ghcd_omap;
+
+ memset(array, 0, BUF_SIZE);
+ buf = &dbg_buffer;
+
+ buf->fill_func = fill_async_buffer;
+ buf->bus = hcd_to_bus(ghcd_omap);
+ buf->alloc_size = BUF_SIZE;
+ buf->output_buf = array;
+
+ printk("\n EHCI registers \n");
+ printk("HCCAPBASE : %08x\n", omap_readl(0x4A064C00));
+ printk("HCSPARAMS : %08x\n", omap_readl(0x4A064C04));
+ printk("HCCPARAMS : %08x\n", omap_readl(0x4A064C08));
+ printk("USBCMD : %08x\n", omap_readl(0x4A064C10));
+ printk("USBSTS : %08x\n", omap_readl(0x4A064C14));
+ printk("USBINTR : %08x\n", omap_readl(0x4A064C18));
+ printk("FRINDEX : %08x\n", omap_readl(0x4A064C1C));
+ printk("CTRLDSSEGMENT : %08x\n", omap_readl(0x4A064C20));
+ printk("PERIODICLISTBASE : %08x\n", omap_readl(0x4A064C24));
+ printk("ASYNCLISTADDR : %08x\n", omap_readl(0x4A064C28));
+ printk("CONFIGFLAG : %08x\n", omap_readl(0x4A064C50));
+ printk("PORT0 : %08x\n", omap_readl(0x4A064C54));
+ printk("PORT1 : %08x\n", omap_readl(0x4A064C58));
+
+ printk("EHCI async list \n");
+ fill_async_buffer(buf);
+
+ //printk("%s\n", array);
+
+}
+
#define DBG_SCHED_LIMIT 64
static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
{
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index 0f3a724..848fd8f 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -28,6 +28,7 @@
/*-------------------------------------------------------------------------*/
#include <linux/usb/otg.h>
+#include <linux/gpio.h>
#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
@@ -601,6 +602,13 @@ static int check_reset_complete (
return port_status;
}
+ if (ehci->no_companion_port_handoff) {
+ /* on omap, we can't hand off companion port */
+ ehci_dbg(ehci, "port %d FS device detected - cannot handoff port\n",
+ index + 1);
+ return port_status;
+ }
+
ehci_dbg (ehci, "port %d full speed --> companion\n",
index + 1);
@@ -735,6 +743,106 @@ ehci_hub_descriptor (
/*-------------------------------------------------------------------------*/
+int omap_ehci_ulpi_write(const struct usb_hcd *hcd, u8 val, u8 reg, u8 retry_times);
+#define CM_L3INIT_HSUSBHOST_CLKCTRL (0x4A009358)
+#define L3INIT_HSUSBHOST_CLKCTRL (0x4A009358)
+#define OMAP_UHH_SYSCONFIG (0x4a064010)
+#define OMAP_UHH_SYSSTATUS (0x4a064014)
+#define OMAP_UHH_HOSTCONFIG (0x4a064040)
+
+void uhh_omap_reset_link(struct ehci_hcd *ehci)
+{
+ u32 usbcmd_backup;
+ u32 usbintr_backup;
+ u32 asynclistaddr_backup, periodiclistbase_backup;
+ u32 portsc0_backup;
+ u32 uhh_sysconfig_backup, uhh_hostconfig_backup;
+ u32 temp_reg;
+ u8 count;
+ u16 orig_val, val;
+
+ /* switch to internal 60Mhz clock */
+ temp_reg = omap_readl(L3INIT_HSUSBHOST_CLKCTRL);
+ temp_reg |= 1 << 8;
+ temp_reg &= ~(1 << 24);
+ omap_writel(temp_reg, L3INIT_HSUSBHOST_CLKCTRL);
+
+ /* Backup current registers of EHCI */
+ usbcmd_backup = ehci_readl(ehci, &ehci->regs->command);
+ ehci_writel(ehci,
+ usbcmd_backup & ~(CMD_IAAD | CMD_ASE | CMD_PSE | CMD_RUN),
+ &ehci->regs->command);
+ mdelay(3);
+ /* check controller stopped */
+ handshake(ehci, &ehci->regs->status, STS_HALT, 1, 150);
+ asynclistaddr_backup = ehci_readl(ehci, &ehci->regs->async_next);
+ periodiclistbase_backup = ehci_readl(ehci, &ehci->regs->frame_list);
+ portsc0_backup = ehci_readl(ehci, &ehci->regs->port_status[0]);
+ usbintr_backup = ehci_readl(ehci, &ehci->regs->intr_enable);
+ uhh_sysconfig_backup = omap_readl(OMAP_UHH_SYSCONFIG);
+ uhh_hostconfig_backup = omap_readl(OMAP_UHH_HOSTCONFIG);
+
+ /* Soft reset EHCI controller */
+ omap_writel(omap_readl(OMAP_UHH_SYSCONFIG) | (1<<0),
+ OMAP_UHH_SYSCONFIG);
+ /* wait for reset done */
+ count = 10;
+ while ((omap_readl(OMAP_UHH_SYSCONFIG) & (1<<0)) && count--)
+ mdelay(1);
+ if (!count)
+ pr_err("ehci:link_reset: soft-reset fail\n");
+
+ /* PHY reset via RESETB pin */
+ gpio_set_value(159, 0);
+ mdelay(2);
+ gpio_set_value(159, 1);
+ mdelay(2);
+
+ /* switch back to external 60Mhz clock */
+ temp_reg &= ~(1 << 8);
+ temp_reg |= 1 << 24;
+ omap_writel(temp_reg, L3INIT_HSUSBHOST_CLKCTRL);
+ mdelay(5);
+
+ /*soft reset ehci registers */
+ ehci_writel(ehci, (1<<1), &ehci->regs->command);
+ count = 10;
+ while ((ehci_readl(ehci, &ehci->regs->command) & (1<<1)) && count--)
+ mdelay(1);
+ if (!count)
+ pr_err("ehci:link_reset: soft-reset fail\n");
+
+ /* Restore registers after reset */
+ omap_writel(uhh_sysconfig_backup, OMAP_UHH_SYSCONFIG);
+ omap_writel(uhh_hostconfig_backup, OMAP_UHH_HOSTCONFIG);
+ ehci_writel(ehci, periodiclistbase_backup, &ehci->regs->frame_list);
+ ehci_writel(ehci, asynclistaddr_backup, &ehci->regs->async_next);
+ ehci_writel(ehci, FLAG_CF, &ehci->regs->configured_flag);
+ ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+ ehci_writel(ehci, usbcmd_backup, &ehci->regs->command);
+ mdelay(2);
+ ehci_writel(ehci, PORT_POWER, &ehci->regs->port_status[0]);
+
+ /* Put PHY in good default state */
+ if (omap_ehci_ulpi_write(ehci_to_hcd(ehci), 0x20, 0x5, 20) < 0) {
+ /* Toggle STP line */
+ orig_val = val = omap_readw(0x4A1000C4);
+ val |= 0x1F;
+ omap_writew(val, 0x4A1000C4);
+ mdelay(3);
+ omap_writew(orig_val, 0x4A1000C4);
+ omap_ehci_ulpi_write(ehci_to_hcd(ehci), 0x20, 0x5, 20);
+ }
+
+ omap_ehci_ulpi_write(ehci_to_hcd(ehci), 0x41, 0x4, 20);
+ omap_ehci_ulpi_write(ehci_to_hcd(ehci), 0x18, 0x7, 20);
+ omap_ehci_ulpi_write(ehci_to_hcd(ehci), 0x66, 0xA, 20);
+ omap_ehci_ulpi_write(ehci_to_hcd(ehci), 0x45, 0x4, 20);
+
+ handshake(ehci, &ehci->regs->port_status[0], PORT_CONNECT, 1, 2000);
+ ehci_writel(ehci, usbintr_backup, &ehci->regs->intr_enable);
+}
+
static int ehci_hub_control (
struct usb_hcd *hcd,
u16 typeReq,
@@ -914,6 +1022,7 @@ static int ehci_hub_control (
/* resume completed? */
else if (time_after_eq(jiffies,
ehci->reset_done[wIndex])) {
+
clear_bit(wIndex, &ehci->suspended_ports);
set_bit(wIndex, &ehci->port_c_suspend);
ehci->reset_done[wIndex] = 0;
@@ -929,8 +1038,20 @@ static int ehci_hub_control (
ehci_err(ehci,
"port %d resume error %d\n",
wIndex + 1, retval);
- goto error;
+
+ if (ehci->has_smsc_ulpi_bug)
+ ehci->resume_error_flag = 1;
+
+ uhh_omap_reset_link(ehci);
+ goto error;
+ }
+
+ /* restore registers value to its original state*/
+ if (ehci->resume_error_flag) {
+ omap_ehci_ulpi_write(hcd, 0x00, 0x32, 20);
+ omap_ehci_ulpi_write(hcd, 0x04, 0x39, 20);
}
+
temp &= ~(PORT_SUSPEND|PORT_RESUME|(3<<10));
}
}
@@ -1055,6 +1176,16 @@ static int ehci_hub_control (
|| (temp & PORT_RESET) != 0)
goto error;
+ /*
+ * Special workaround for resume error
+ * - Write 04h to register 32h : inserts a 2uA source current on DP
+ * - Write 14h to register 39h : enables 125kohm pull up resistors on DP
+ */
+ if (ehci->resume_error_flag) {
+ omap_ehci_ulpi_write(hcd, 0x04, 0x32, 20);
+ omap_ehci_ulpi_write(hcd, 0x14, 0x39, 20);
+ }
+
/* After above check the port must be connected.
* Set appropriate bit thus could put phy into low power
* mode if we have hostpc feature
@@ -1062,6 +1193,31 @@ static int ehci_hub_control (
temp &= ~PORT_WKCONN_E;
temp |= PORT_WKDISC_E | PORT_WKOC_E;
ehci_writel(ehci, temp | PORT_SUSPEND, status_reg);
+
+ /*
+ * Special workaround sequence:
+ * - Set suspend bit
+ * - Wait 4ms for suspend to take effect
+ * - alternatively read the line state
+ * in PORTSC
+ * - switch to internal 60 MHz clock
+ * - wait 1ms
+ * - switch back to external clock
+ */
+ {
+ u32 temp_reg;
+ mdelay(4);
+ temp_reg = omap_readl(L3INIT_HSUSBHOST_CLKCTRL);
+ temp_reg |= 1 << 8;
+ temp_reg &= ~(1 << 24);
+ omap_writel(temp_reg, L3INIT_HSUSBHOST_CLKCTRL);
+
+ mdelay(1);
+ temp_reg &= ~(1 << 8);
+ temp_reg |= 1 << 24;
+ omap_writel(temp_reg, L3INIT_HSUSBHOST_CLKCTRL);
+ }
+
if (hostpc_reg) {
spin_unlock_irqrestore(&ehci->lock, flags);
msleep(5);/* 5ms for HCD enter low pwr mode */
diff --git a/drivers/usb/host/ehci-omap.c b/drivers/usb/host/ehci-omap.c
index d534fa6..1839e56 100644..100755
--- a/drivers/usb/host/ehci-omap.c
+++ b/drivers/usb/host/ehci-omap.c
@@ -46,6 +46,7 @@
#include <plat/omap_hwmod.h>
#include <plat/usb.h>
#include <plat/clock.h>
+#include <plat/omap-pm.h>
/* EHCI Register Set */
#define EHCI_INSNREG04 (0xA0)
@@ -73,6 +74,73 @@ static inline u32 ehci_read(void __iomem *base, u32 reg)
return __raw_readl(base + reg);
}
+u8 omap_ehci_ulpi_read(const struct usb_hcd *hcd, u8 reg)
+{
+ unsigned reg_internal = 0;
+ u8 val;
+ int count = 2000;
+
+ reg_internal = ((reg) << EHCI_INSNREG05_ULPI_REGADD_SHIFT)
+ /* Write */
+ | (3 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT)
+ /* PORTn */
+ | ((1) << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT)
+ /* start ULPI access*/
+ | (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT);
+
+ ehci_write(hcd->regs, EHCI_INSNREG05_ULPI, reg_internal);
+
+ /* Wait for ULPI access completion */
+ while ((ehci_read(hcd->regs, EHCI_INSNREG05_ULPI)
+ & (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) {
+ udelay(1);
+ if (count-- == 0) {
+ pr_err("ehci: omap_ehci_ulpi_read: Error");
+ break;
+ }
+ }
+
+ val = ehci_read(hcd->regs, EHCI_INSNREG05_ULPI) & 0xFF;
+ return val;
+}
+
+int omap_ehci_ulpi_write(const struct usb_hcd *hcd, u8 val, u8 reg, u8 retry_times)
+{
+ unsigned reg_internal = 0;
+ int status = 0;
+ int count;
+
+again:
+ count = 2000;
+ reg_internal = val |
+ ((reg) << EHCI_INSNREG05_ULPI_REGADD_SHIFT)
+ /* Write */
+ | (2 << EHCI_INSNREG05_ULPI_OPSEL_SHIFT)
+ /* PORTn */
+ | ((1) << EHCI_INSNREG05_ULPI_PORTSEL_SHIFT)
+ /* start ULPI access*/
+ | (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT);
+
+ ehci_write(hcd->regs, EHCI_INSNREG05_ULPI, reg_internal);
+
+ /* Wait for ULPI access completion */
+ while ((ehci_read(hcd->regs, EHCI_INSNREG05_ULPI)
+ & (1 << EHCI_INSNREG05_ULPI_CONTROL_SHIFT))) {
+ udelay(1);
+ if (count-- == 0) {
+ if (retry_times--) {
+ ehci_write(hcd->regs, EHCI_INSNREG05_ULPI, 0);
+ goto again;
+ } else {
+ pr_err("ehci: omap_ehci_ulpi_write: Error");
+ status = -ETIMEDOUT;
+ break;
+ }
+ }
+ }
+ return status;
+}
+
static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port)
{
struct usb_hcd *hcd = dev_get_drvdata(&pdev->dev);
@@ -114,6 +182,9 @@ static void omap_ehci_soft_phy_reset(struct platform_device *pdev, u8 port)
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*/
+struct usb_hcd *ghcd_omap;
+#define USBHS_OHCI_HWMODNAME "usbhs_ohci"
+#define HCCONTROL_OFFSET (4)
static int ehci_hcd_omap_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -121,7 +192,9 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
struct resource *res;
struct usb_hcd *hcd;
void __iomem *regs;
+ void __iomem *ohci_base;
struct ehci_hcd *omap_ehci;
+ struct omap_hwmod *oh;
int ret = -ENODEV;
int irq;
int i;
@@ -162,6 +235,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
goto err_io;
}
+ ghcd_omap = hcd;
hcd->rsrc_start = res->start;
hcd->rsrc_len = resource_size(res);
hcd->regs = regs;
@@ -206,6 +280,8 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
omap_ehci = hcd_to_ehci(hcd);
omap_ehci->sbrn = 0x20;
+ omap_ehci->has_smsc_ulpi_bug = 1;
+ omap_ehci->no_companion_port_handoff = 1;
/* we know this is the memory we want, no need to ioremap again */
omap_ehci->caps = hcd->regs;
omap_ehci->regs = hcd->regs
@@ -226,6 +302,12 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
/* root ports should always stay powered */
ehci_port_power(omap_ehci, 1);
+ /* Ensure OHCI is kept in suspended state */
+ oh = omap_hwmod_lookup(USBHS_OHCI_HWMODNAME);
+ ohci_base = omap_hwmod_get_mpu_rt_va(oh);
+ __raw_writel(OHCI_USB_SUSPEND, ohci_base + HCCONTROL_OFFSET);
+ (void)__raw_readl(ohci_base + HCCONTROL_OFFSET);
+
return 0;
err_add_hcd:
@@ -300,6 +382,10 @@ static int ehci_omap_bus_suspend(struct usb_hcd *hcd)
clk_disable(clk);
}
+ omap_pm_set_min_bus_tput(dev,
+ OCP_INITIATOR_AGENT,
+ -1);
+
return ret;
}
@@ -320,6 +406,10 @@ static int ehci_omap_bus_resume(struct usb_hcd *hcd)
clk_enable(clk);
}
+ omap_pm_set_min_bus_tput(dev,
+ OCP_INITIATOR_AGENT,
+ (200*1000*4));
+
if (dev->parent) {
pm_runtime_get_sync(dev->parent);
}
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index 6c6013c..0515e69 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -111,8 +111,6 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
}
}
- /* HC must see latest qtd and qh data before we clear ACTIVE+HALT */
- wmb ();
hw->hw_token &= cpu_to_hc32(ehci, QTD_TOGGLE | QTD_STS_PING);
}
@@ -375,6 +373,17 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
retry_xacterr:
if ((token & QTD_STS_ACTIVE) == 0) {
+ /* Report Data Buffer Error: non-fatal but useful */
+ if (token & QTD_STS_DBE)
+ ehci_dbg(ehci,
+ "detected DataBufferErr for urb %p ep%d%s len %d, qtd %p [qh %p]\n",
+ urb,
+ usb_endpoint_num(&urb->ep->desc),
+ usb_endpoint_dir_in(&urb->ep->desc) ? "in" : "out",
+ urb->transfer_buffer_length,
+ qtd,
+ qh);
+
/* on STALL, error, and short reads this urb must
* complete and all its qtds must be recycled.
*/
@@ -498,11 +507,6 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh)
last = list_entry (qtd->qtd_list.prev,
struct ehci_qtd, qtd_list);
last->hw_next = qtd->hw_next;
- /*
- * Make sure the new hw_next pointer is visible
- * to the HW before freeing the old one
- */
- wmb();
}
/* remove qtd; it's recycled after possible urb completion */
@@ -729,7 +733,8 @@ qh_urb_transaction (
/*
* control requests may need a terminating data "status" ack;
- * bulk ones may need a terminating short packet (zero length).
+ * other OUT ones may need a terminating short packet
+ * (zero length).
*/
if (likely (urb->transfer_buffer_length != 0)) {
int one_more = 0;
@@ -738,7 +743,7 @@ qh_urb_transaction (
one_more = 1;
token ^= 0x0100; /* "in" <--> "out" */
token |= QTD_TOGGLE; /* force DATA1 */
- } else if (usb_pipebulk (urb->pipe)
+ } else if (usb_pipeout(urb->pipe)
&& (urb->transfer_flags & URB_ZERO_PACKET)
&& !(urb->transfer_buffer_length % maxpacket)) {
one_more = 1;
@@ -1000,12 +1005,6 @@ static void qh_link_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
head->qh_next.qh = qh;
head->hw->hw_next = dma;
- /*
- * flush qh descriptor into memory immediately,
- * see comments in qh_append_tds.
- * */
- ehci_sync_mem();
-
qh_get(qh);
qh->xacterrs = 0;
qh->qh_state = QH_STATE_LINKED;
@@ -1069,7 +1068,7 @@ static struct ehci_qh *qh_append_tds (
*/
token = qtd->hw_token;
qtd->hw_token = HALT_BIT(ehci);
- wmb ();
+
dummy = qh->dummy;
dma = dummy->qtd_dma;
@@ -1093,18 +1092,6 @@ static struct ehci_qh *qh_append_tds (
wmb ();
dummy->hw_token = token;
- /*
- * Writing to dma coherent buffer on ARM may
- * be delayed to reach memory, so HC may not see
- * hw_token of dummy qtd in time, which can cause
- * the qtd transaction to be executed very late,
- * and degrade performance a lot. ehci_sync_mem
- * is added to flush 'token' immediatelly into
- * memory, so that ehci can execute the transaction
- * ASAP.
- * */
- ehci_sync_mem();
-
urb->hcpriv = qh_get (qh);
}
}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 9706c2b..5037b33 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -137,6 +137,11 @@ struct ehci_hcd { /* one per controller */
unsigned fs_i_thresh:1; /* Intel iso scheduling */
unsigned use_dummy_qh:1; /* AMD Frame List table quirk*/
unsigned has_synopsys_hc_bug:1; /* Synopsys HC */
+ unsigned no_companion_port_handoff:1; /* Omap */
+
+ /* Transceiver QUIRKS */
+ unsigned has_smsc_ulpi_bug:1; /* Smsc */
+ unsigned resume_error_flag:1; /* Smsc */
/* required for usb32 quirk */
#define OHCI_CTRL_HCFS (3 << 6)
@@ -160,9 +165,9 @@ struct ehci_hcd { /* one per controller */
#endif
/* debug files */
-#ifdef DEBUG
+/* #ifdef DEBUG */
struct dentry *debug_dir;
-#endif
+/* #endif */
/*
* OTG controllers and transceivers need software interaction
*/
@@ -736,28 +741,13 @@ static inline u32 hc32_to_cpup (const struct ehci_hcd *ehci, const __hc32 *x)
#endif
-/*
- * Writing to dma coherent memory on ARM may be delayed via L2
- * writing buffer, so introduce the helper which can flush L2 writing
- * buffer into memory immediately, especially used to flush ehci
- * descriptor to memory.
- * */
-#ifdef CONFIG_ARM_DMA_MEM_BUFFERABLE
-static inline void ehci_sync_mem()
-{
- mb();
-}
-#else
-static inline void ehci_sync_mem()
-{
-}
-#endif
-
/*-------------------------------------------------------------------------*/
+#if 0
#ifndef DEBUG
#define STUB_DEBUG_FILES
#endif /* DEBUG */
+#endif
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index e2648ff..b6e4fcb 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -689,7 +689,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
}
musb_writew(musb->mregs, MUSB_INTRTXE, musb->epmask);
musb_writew(musb->mregs, MUSB_INTRRXE, musb->epmask & 0xfffe);
- musb_writeb(musb->mregs, MUSB_INTRUSBE, 0xf7);
+ musb_writeb(musb->mregs, MUSB_INTRUSBE, MUSB_INTR_ENABLED);
#endif
musb->port1_status &= ~(USB_PORT_STAT_LOW_SPEED
|USB_PORT_STAT_HIGH_SPEED
@@ -915,23 +915,23 @@ void musb_start(struct musb *musb)
{
void __iomem *regs = musb->mregs;
u8 devctl = musb_readb(regs, MUSB_DEVCTL);
+ u8 temp;
dev_dbg(musb->controller, "<== devctl %02x\n", devctl);
/* Set INT enable registers, enable interrupts */
musb_writew(regs, MUSB_INTRTXE, musb->epmask);
musb_writew(regs, MUSB_INTRRXE, musb->epmask & 0xfffe);
- musb_writeb(regs, MUSB_INTRUSBE, 0xf7);
+ musb_writeb(regs, MUSB_INTRUSBE, MUSB_INTR_ENABLED);
musb_writeb(regs, MUSB_TESTMODE, 0);
/* put into basic highspeed mode and start session */
- musb_writeb(regs, MUSB_POWER, MUSB_POWER_ISOUPDATE
- | MUSB_POWER_SOFTCONN
- | MUSB_POWER_HSENAB
- /* ENSUSPEND wedges tusb */
- /* | MUSB_POWER_ENSUSPEND */
- );
+ temp = MUSB_POWER_ISOUPDATE | MUSB_POWER_HSENAB;
+ /* MUSB_POWER_ENSUSPEND wedges tusb */
+ if (musb->softconnect)
+ temp |= MUSB_POWER_SOFTCONN;
+ musb_writeb(regs, MUSB_POWER, temp);
musb->is_active = 0;
devctl = musb_readb(regs, MUSB_DEVCTL);
@@ -945,7 +945,7 @@ void musb_start(struct musb *musb)
*/
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
musb->is_active = 1;
- else
+ else if (musb->xceiv->state == OTG_STATE_A_HOST)
devctl |= MUSB_DEVCTL_SESSION;
} else if (is_host_enabled(musb)) {
@@ -2048,10 +2048,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
if (!is_otg_enabled(musb) && is_host_enabled(musb)) {
struct usb_hcd *hcd = musb_to_hcd(musb);
- MUSB_HST_MODE(musb);
- musb->xceiv->default_a = 1;
- musb->xceiv->state = OTG_STATE_A_IDLE;
-
status = usb_add_hcd(musb_to_hcd(musb), -1, 0);
hcd->self.uses_pio_for_control = 1;
@@ -2063,9 +2059,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
? 'B' : 'A'));
} else /* peripheral is enabled */ {
- MUSB_DEV_MODE(musb);
- musb->xceiv->default_a = 0;
- musb->xceiv->state = OTG_STATE_B_IDLE;
status = musb_gadget_setup(musb);
@@ -2078,6 +2071,10 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
if (status < 0)
goto fail3;
+ if (is_otg_enabled(musb) || is_host_enabled(musb))
+ wake_lock_init(&musb->musb_wakelock, WAKE_LOCK_SUSPEND,
+ "musb_autosuspend_wake_lock");
+
pm_runtime_put(musb->controller);
status = musb_init_debugfs(musb);
@@ -2113,6 +2110,9 @@ fail4:
else
musb_gadget_cleanup(musb);
+ if (is_otg_enabled(musb) || is_host_enabled(musb))
+ wake_lock_destroy(&musb->musb_wakelock);
+
fail3:
if (musb->irq_wake)
device_init_wakeup(dev, 0);
@@ -2190,6 +2190,9 @@ static int __exit musb_remove(struct platform_device *pdev)
#ifndef CONFIG_MUSB_PIO_ONLY
pdev->dev.dma_mask = orig_dma_mask;
#endif
+ if (is_otg_enabled(musb) || is_host_enabled(musb))
+ wake_lock_destroy(&musb->musb_wakelock);
+
return 0;
}
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 263d31c..695b790 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -47,6 +47,7 @@
#include <linux/usb.h>
#include <linux/usb/otg.h>
#include <linux/usb/musb.h>
+#include <linux/wakelock.h>
struct musb;
struct musb_hw_ep;
@@ -386,6 +387,7 @@ struct musb {
struct musb_context_registers context;
irqreturn_t (*isr)(int, void *);
+ struct wake_lock musb_wakelock;
struct work_struct irq_work;
struct workqueue_struct *otg_notifier_wq;
u16 hwvers;
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 548338c..b3acbe7 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -634,6 +634,7 @@ static void rxstate(struct musb *musb, struct musb_request *req)
u16 len;
u16 csr = musb_readw(epio, MUSB_RXCSR);
struct musb_hw_ep *hw_ep = &musb->endpoints[epnum];
+ u8 use_mode_1;
if (hw_ep->is_shared_fifo)
musb_ep = &hw_ep->ep_in;
@@ -683,6 +684,10 @@ static void rxstate(struct musb *musb, struct musb_request *req)
if (csr & MUSB_RXCSR_RXPKTRDY) {
len = musb_readw(epio, MUSB_RXCOUNT);
+
+ /* Disable mode1 rx dma */
+ use_mode_1 = 0;
+
if (request->actual < request->length) {
#ifdef CONFIG_USB_INVENTRA_DMA
if (is_buffer_mapped(req)) {
@@ -714,37 +719,40 @@ static void rxstate(struct musb *musb, struct musb_request *req)
* then becomes usable as a runtime "use mode 1" hint...
*/
- csr |= MUSB_RXCSR_DMAENAB;
-#ifdef USE_MODE1
+ /* Experimental: Mode1 works with mass storage use cases
+ */
+ if (use_mode_1) {
csr |= MUSB_RXCSR_AUTOCLEAR;
- /* csr |= MUSB_RXCSR_DMAMODE; */
+ musb_writew(epio, MUSB_RXCSR, csr);
+ csr |= MUSB_RXCSR_DMAENAB;
+ musb_writew(epio, MUSB_RXCSR, csr);
- /* this special sequence (enabling and then
- * disabling MUSB_RXCSR_DMAMODE) is required
+ /* this special sequence is required
* to get DMAReq to activate
*/
- musb_writew(epio, MUSB_RXCSR,
- csr | MUSB_RXCSR_DMAMODE);
-#else
+ csr |= MUSB_RXCSR_DMAMODE;
+ musb_writew(epio, MUSB_RXCSR, csr);
+ csr |= MUSB_RXCSR_DMAENAB;
+ musb_writew(epio, MUSB_RXCSR, csr);
+ } else {
if (!musb_ep->hb_mult &&
musb_ep->hw_ep->rx_double_buffered)
csr |= MUSB_RXCSR_AUTOCLEAR;
-#endif
+ csr |= MUSB_RXCSR_DMAENAB;
musb_writew(epio, MUSB_RXCSR, csr);
+ }
if (request->actual < request->length) {
int transfer_size = 0;
-#ifdef USE_MODE1
+ if (use_mode_1) {
transfer_size = min(request->length - request->actual,
channel->max_len);
-#else
+ musb_ep->dma->desired_mode = 1;
+ } else {
transfer_size = min(request->length - request->actual,
(unsigned)len);
-#endif
- if (transfer_size <= musb_ep->packet_sz)
- musb_ep->dma->desired_mode = 0;
- else
- musb_ep->dma->desired_mode = 1;
+ musb_ep->dma->desired_mode = 0;
+ }
use_dma = c->channel_program(
channel,
@@ -1919,21 +1927,10 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
spin_lock_irqsave(&musb->lock, flags);
otg_set_peripheral(musb->xceiv, &musb->g);
- musb->xceiv->state = OTG_STATE_B_IDLE;
- musb->is_active = 1;
-
- /*
- * FIXME this ignores the softconnect flag. Drivers are
- * allowed hold the peripheral inactive until for example
- * userspace hooks up printer hardware or DSP codecs, so
- * hosts only see fully functional devices.
- */
if (!is_otg_enabled(musb))
musb_start(musb);
- otg_set_peripheral(musb->xceiv, &musb->g);
-
spin_unlock_irqrestore(&musb->lock, flags);
if (is_otg_enabled(musb)) {
@@ -1951,12 +1948,9 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
goto err2;
}
- if ((musb->xceiv->last_event == USB_EVENT_ID)
- && musb->xceiv->set_vbus)
- otg_set_vbus(musb->xceiv, 1);
-
hcd->self.uses_pio_for_control = 1;
}
+
if (musb->xceiv->last_event == USB_EVENT_NONE)
pm_runtime_put(musb->controller);
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 8b2473f..b60c540 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2266,6 +2266,8 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
struct musb *musb = hcd_to_musb(hcd);
u8 devctl;
+ wake_unlock(&musb->musb_wakelock);
+
if (!is_host_active(musb))
return 0;
@@ -2295,6 +2297,9 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
static int musb_bus_resume(struct usb_hcd *hcd)
{
+ struct musb *musb = hcd_to_musb(hcd);
+
+ wake_lock(&musb->musb_wakelock);
/* resuming child port does the work */
return 0;
}
diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h
index 8241070..6e3cb3f 100644
--- a/drivers/usb/musb/musb_regs.h
+++ b/drivers/usb/musb/musb_regs.h
@@ -62,6 +62,10 @@
#define MUSB_INTR_SESSREQ 0x40
#define MUSB_INTR_VBUSERROR 0x80 /* For SESSION end */
+#define MUSB_INTR_ENABLED \
+ (MUSB_INTR_SUSPEND | MUSB_INTR_RESUME | MUSB_INTR_RESET | \
+ MUSB_INTR_CONNECT | MUSB_INTR_DISCONNECT)
+
/* DEVCTL */
#define MUSB_DEVCTL_BDEVICE 0x80
#define MUSB_DEVCTL_FSDEV 0x40
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index e685787..1d22442 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -172,11 +172,7 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
if (ret && musb->xceiv->set_vbus)
otg_set_vbus(musb->xceiv, 1);
- } else {
- musb->is_active = 1;
musb->xceiv->default_a = 1;
- musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
- devctl |= MUSB_DEVCTL_SESSION;
MUSB_HST_MODE(musb);
}
} else {
@@ -189,10 +185,9 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
musb->xceiv->default_a = 0;
musb->xceiv->state = OTG_STATE_B_IDLE;
devctl &= ~MUSB_DEVCTL_SESSION;
-
+ musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
MUSB_DEV_MODE(musb);
}
- musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
dev_dbg(musb->controller, "VBUS %s, devctl %02x "
/* otg %3x conf %08x prcm %08x */ "\n",
@@ -314,6 +309,7 @@ static void musb_otg_notifier_work(struct work_struct *data_notifier_work)
}
if (data->interface_type == MUSB_INTERFACE_UTMI) {
+ omap2430_musb_set_vbus(musb, 0);
if (musb->xceiv->set_vbus)
otg_set_vbus(musb->xceiv, 0);
}
diff --git a/drivers/usb/otg/otg-wakelock.c b/drivers/usb/otg/otg-wakelock.c
index 2f11472..5725f27 100644
--- a/drivers/usb/otg/otg-wakelock.c
+++ b/drivers/usb/otg/otg-wakelock.c
@@ -90,11 +90,11 @@ static void otgwl_handle_event(unsigned long event)
switch (event) {
case USB_EVENT_VBUS:
case USB_EVENT_ENUMERATED:
+ case USB_EVENT_ID:
otgwl_hold(&vbus_lock);
break;
case USB_EVENT_NONE:
- case USB_EVENT_ID:
case USB_EVENT_CHARGER:
otgwl_temporary_hold(&vbus_lock);
break;
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 1d04ca2..acd429e 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -2394,6 +2394,13 @@ config HDMI_TI_4XXX_IP
HDMI Library Interface , for TI OMAP4/Netra IP.
See http://www.hdmi.org/ for HDMI specification.
+config SII9234
+ bool "Support Silicon Image 9234 MHL Bridge"
+ depends on I2C
+ default n
+ help
+ Enabled support for the Silicon Image 9234 MHL Bridge
+
source "drivers/video/omap/Kconfig"
source "drivers/video/omap2/Kconfig"
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 22eaeb6..bd33003 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -158,6 +158,8 @@ obj-$(CONFIG_FB_MX3) += mx3fb.o
obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o
obj-$(CONFIG_FB_MXS) += mxsfb.o
+obj-$(CONFIG_SII9234) += sii9234.o
+
# the test framebuffer is last
obj-$(CONFIG_FB_VIRTUAL) += vfb.o
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig
index 609a280..e08aeca 100644
--- a/drivers/video/omap2/displays/Kconfig
+++ b/drivers/video/omap2/displays/Kconfig
@@ -30,6 +30,13 @@ config PANEL_NEC_NL8048HL11_01B
This NEC NL8048HL11-01B panel is TFT LCD
used in the Zoom2/3/3630 sdp boards.
+config PANEL_S6E8AA0
+ tristate "S6E8AA0 DSI Panel"
+ depends on OMAP2_DSS_DSI
+ select BACKLIGHT_CLASS_DEVICE
+ help
+ S6E8AA0 video mode panel.
+
config PANEL_TAAL
tristate "Taal DSI Panel"
depends on OMAP2_DSS_DSI
diff --git a/drivers/video/omap2/displays/Makefile b/drivers/video/omap2/displays/Makefile
index 0f601ab3a..8b1dd23 100644
--- a/drivers/video/omap2/displays/Makefile
+++ b/drivers/video/omap2/displays/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_PANEL_GENERIC_DPI) += panel-generic-dpi.o
obj-$(CONFIG_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o
obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o
obj-$(CONFIG_PANEL_NEC_NL8048HL11_01B) += panel-nec-nl8048hl11-01b.o
+obj-$(CONFIG_PANEL_S6E8AA0) += panel-s6e8aa0.o
obj-$(CONFIG_PANEL_TAAL) += panel-taal.o
obj-$(CONFIG_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o
diff --git a/drivers/video/omap2/displays/panel-generic-dpi.c b/drivers/video/omap2/displays/panel-generic-dpi.c
index 611be7e..6aa2027 100644
--- a/drivers/video/omap2/displays/panel-generic-dpi.c
+++ b/drivers/video/omap2/displays/panel-generic-dpi.c
@@ -256,6 +256,31 @@ static struct panel_config generic_dpi_panels[] = {
.power_off_delay = 0,
.name = "powertip_ph480272t",
},
+ /* Samsung AMS452GN05 */
+ {
+ {
+ .x_res = 480,
+ .y_res = 800,
+
+ .pixel_clock = 25600,
+
+ .hfp = 16,
+ .hsw = 2,
+ .hbp = 16,
+
+ .vfp = 9,
+ .vsw = 2,
+ .vbp = 3,
+ },
+ .acbi = 0x0,
+ .acb = 0x0,
+ .config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS |
+ OMAP_DSS_LCD_IHS | OMAP_DSS_LCD_IPC |
+ OMAP_DSS_LCD_IEO | OMAP_DSS_LCD_ONOFF,
+ .power_on_delay = 0,
+ .power_off_delay = 0,
+ .name = "samsung_ams452gn05",
+ },
};
struct panel_drv_data {
diff --git a/drivers/video/omap2/displays/panel-s6e8aa0.c b/drivers/video/omap2/displays/panel-s6e8aa0.c
new file mode 100644
index 0000000..8fd29cba
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-s6e8aa0.c
@@ -0,0 +1,1970 @@
+/*
+ * Samsung s6e8aa0 panel support
+ *
+ * Copyright 2011 Google, Inc.
+ * Author: Erik Gilling <konkers@google.com>
+ *
+ * based on d2l panel driver by Jerry Alexander <x0135174@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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/>.
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/uaccess.h>
+
+
+#include <video/omapdss.h>
+
+#include <linux/platform_data/panel-s6e8aa0.h>
+
+#include "../dss/dss.h"
+
+/* DSI Command Virtual channel */
+#define CMD_VC_CHANNEL 1
+
+#define V1_ADJ_MAX 140
+#define V255_ADJ_MAX 430
+#define NUM_GAMMA_REGS 24
+#define NUM_DY_REGS (32)
+
+enum {
+ V1,
+ V15,
+ V35,
+ V59,
+ V87,
+ V171,
+ V255,
+ V_COUNT,
+};
+
+#define DRIVER_NAME "s6e8aa0_i2c"
+#define DEVICE_NAME "s6e8aa0_i2c"
+
+static int s6e8aa0_update(struct omap_dss_device *dssdev,
+ u16 x, u16 y, u16 w, u16 h);
+
+static struct omap_video_timings s6e8aa0_timings = {
+ .x_res = 720,
+ .y_res = 1280,
+ .pixel_clock = 79494,
+ .hfp = 158,
+ .hsw = 2,
+ .hbp = 160,
+ .vfp = 13,
+ .vsw = 1,
+ .vbp = 2,
+};
+
+static const struct s6e8aa0_gamma_adj_points default_gamma_adj_points = {
+ .v1 = BV_1,
+ .v15 = BV_15,
+ .v35 = BV_35,
+ .v59 = BV_59,
+ .v87 = BV_87,
+ .v171 = BV_171,
+};
+
+static u32 s6e8aa0_srgb_dyi_to_b[NUM_DY_REGS] = {
+ 0x0027c8ac, /* 2 */
+ 0x004f9159, /* 4 */
+ 0x00775a06, /* 6 */
+ 0x009f22b3, /* 8 */
+ 0x00f0f18e, /* 12 */
+ 0x0153936c, /* 16 */
+ 0x02569c13, /* 24 */
+ 0x03b2977b, /* 32 */
+ 0x060e496b, /* 42.5 */
+ 0x0803965b, /* 49.5 */
+ 0x0bf23e3b, /* 61 */
+ 0x10048613, /* 70.8 */
+ 0x14041743, /* 79 */
+ 0x180da7c6, /* 86.4 */
+ 0x1c05aec3, /* 93 */
+ 0x200e2a06, /* 99.2 */
+ 0x280bf0ad, /* 110.2 */
+ 0x301505aa, /* 120 */
+ 0x38108cb8, /* 128.9 */
+ 0x400a5f93, /* 137 */
+ 0x5008b39d, /* 151.7 */
+ 0x6010dbad, /* 164.8 */
+ 0x700a375a, /* 176.6 */
+ 0x801a9901, /* 187.6 */
+ 0x901075cd, /* 197.7 */
+ 0xa01b515c, /* 207.2 */
+ 0xb00de3e4, /* 216.1 */
+ 0xc01c269c, /* 224.7 */
+ 0xd01871ca, /* 232.8 */
+ 0xe010831c, /* 240.5 */
+ 0xf02ca6b8, /* 247.9 */
+ 0xffffffff, /* 255.1 */
+};
+
+struct s6e8aa0_gamma_reg_offsets {
+ s16 v[2][3][7];
+};
+
+struct s6e8aa0_data {
+ struct mutex lock;
+
+ struct omap_dss_device *dssdev;
+ struct backlight_device *bldev;
+ struct dentry *debug_dir;
+ bool enabled;
+ u8 rotate;
+ bool mirror;
+ bool use_dsi_bl;
+ unsigned int bl;
+ const struct s6e8aa0_gamma_adj_points *gamma_adj_points;
+ const u32 *dyi_to_b;
+ struct s6e8aa0_gamma_reg_offsets gamma_reg_offsets;
+ struct s6e8aa0_gamma_entry *brightness_table;
+ int brightness_table_size;
+ u32 brightness_limit[3];
+ 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 */
+
+ atomic_t do_update;
+ struct {
+ u16 x;
+ u16 y;
+ u16 w;
+ u16 h;
+ } update_region;
+
+ bool cabc_broken;
+ unsigned cabc_mode;
+
+ bool force_update;
+ struct omap_video_timings *timings;
+
+ struct panel_s6e8aa0_data *pdata;
+
+ unsigned int acl_cur;
+ bool acl_enable;
+ u8 acl_average;
+ unsigned int elvss_cur_i;
+ u8 panel_id[3];
+};
+
+const u8 s6e8aa0_mtp_unlock[] = {
+ 0xF1,
+ 0x5A,
+ 0x5A,
+};
+
+const u8 s6e8aa0_mtp_lock[] = {
+ 0xF1,
+ 0xA5,
+ 0xA5,
+};
+
+static int s6e8aa0_write_reg(struct omap_dss_device *dssdev, u8 reg, u8 val)
+{
+ u8 buf[2];
+ buf[0] = reg;
+ buf[1] = val;
+
+ return dsi_vc_dcs_write(dssdev, 1, buf, 2);
+}
+
+static int s6e8aa0_write_block(struct omap_dss_device *dssdev, const u8 *data, int len)
+{
+ // XXX: dsi_vc_dsc_write should take a const u8 *
+ return dsi_vc_dcs_write(dssdev, 1, (u8 *)data, len);
+}
+
+static int s6e8aa0_write_block_nosync(struct omap_dss_device *dssdev,
+ const u8 *data, int len)
+{
+ return dsi_vc_dcs_write_nosync(dssdev, 1, (u8 *)data, len);
+}
+
+static int s6e8aa0_read_block(struct omap_dss_device *dssdev,
+ u8 cmd, u8 *data, int len)
+{
+ return dsi_vc_dcs_read(dssdev, 1, cmd, data, len);
+}
+
+static void s6e8aa0_write_sequence(struct omap_dss_device *dssdev,
+ const struct s6e8aa0_sequence_entry *seq, int seq_len)
+{
+ while (seq_len--) {
+ if (seq->cmd_len)
+ s6e8aa0_write_block(dssdev, seq->cmd, seq->cmd_len);
+ if (seq->msleep)
+ msleep(seq->msleep);
+ seq++;
+ }
+}
+
+/***********************
+*** DUMMY FUNCTIONS ****
+***********************/
+
+static int s6e8aa0_rotate(struct omap_dss_device *dssdev, u8 rotate)
+{
+ return 0;
+}
+
+static u8 s6e8aa0_get_rotate(struct omap_dss_device *dssdev)
+{
+ return 0;
+}
+
+static int s6e8aa0_mirror(struct omap_dss_device *dssdev, bool enable)
+{
+ return 0;
+}
+
+static bool s6e8aa0_get_mirror(struct omap_dss_device *dssdev)
+{
+ return 0;
+}
+
+static void s6e8aa0_get_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ *timings = dssdev->panel.timings;
+}
+
+static void s6e8aa0_set_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ dssdev->panel.timings.x_res = timings->x_res;
+ dssdev->panel.timings.y_res = timings->y_res;
+ dssdev->panel.timings.pixel_clock = timings->pixel_clock;
+ dssdev->panel.timings.hsw = timings->hsw;
+ dssdev->panel.timings.hfp = timings->hfp;
+ dssdev->panel.timings.hbp = timings->hbp;
+ dssdev->panel.timings.vsw = timings->vsw;
+ dssdev->panel.timings.vfp = timings->vfp;
+ dssdev->panel.timings.vbp = timings->vbp;
+}
+
+static int s6e8aa0_check_timings(struct omap_dss_device *dssdev,
+ struct omap_video_timings *timings)
+{
+ return 0;
+}
+
+static void s6e8aa0_get_resolution(struct omap_dss_device *dssdev,
+ u16 *xres, u16 *yres)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+
+ if (s6->rotate == 0 || s6->rotate == 2) {
+ *xres = dssdev->panel.timings.x_res;
+ *yres = dssdev->panel.timings.y_res;
+ } else {
+ *yres = dssdev->panel.timings.x_res;
+ *xres = dssdev->panel.timings.y_res;
+ }
+}
+
+static int s6e8aa0_enable_te(struct omap_dss_device *dssdev, bool enable)
+{
+ return 0;
+}
+
+static int s6e8aa0_hw_reset(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+
+ gpio_set_value(s6->pdata->reset_gpio, 0);
+ msleep(20);
+ gpio_set_value(s6->pdata->reset_gpio, 1);
+ msleep(40);
+
+ return 0;
+}
+
+static u32 s6e8aa0_table_lookup(u32 b, int c,
+ const struct s6e8aa0_gamma_entry *table,
+ int table_size)
+{
+ int i;
+ u32 ret;
+ u32 bl = 0;
+ u32 bh = 0;
+ u32 vl = 0;
+ u32 vh;
+ u64 tmp;
+
+ if (!table_size)
+ return b;
+
+ for (i = 0; i < table_size; i++) {
+ bl = bh;
+ bh = table[i].brightness;
+ if (bh >= b)
+ break;
+ }
+ vh = table[i].v[c];
+ if (i == 0 || (b - bl) == 0) {
+ ret = vl = vh;
+ } else {
+ vl = table[i - 1].v[c];
+ tmp = (u64)vh * (b - bl) + (u64)vl * (bh - b);
+ do_div(tmp, bh - bl);
+ ret = tmp;
+ }
+ pr_debug("%s: looking for c %d, %08x, "
+ "found %08x:%08x, v %7d:%7d, ret %7d\n",
+ __func__, c, b, bl, bh, vl, vh, ret);
+ return ret;
+}
+
+static u32 s6e8aa0_raw_gamma_lookup(struct s6e8aa0_data *s6, u32 b, int c)
+{
+ struct panel_s6e8aa0_data *pdata = s6->pdata;
+ return s6e8aa0_table_lookup(b, c, pdata->gamma_table,
+ pdata->gamma_table_size);
+}
+
+static u32 s6e8aa0_gamma_lookup(struct s6e8aa0_data *s6,
+ u8 brightness, u32 val, int c)
+{
+ u32 b;
+ u32 ret;
+ u64 tmp;
+
+ tmp = val;
+ tmp *= brightness;
+ do_div(tmp, 255);
+
+ b = s6e8aa0_table_lookup(tmp, c, s6->brightness_table,
+ s6->brightness_table_size);
+
+ ret = s6e8aa0_raw_gamma_lookup(s6, b, c);
+
+ pr_debug("%s: looking for %3d %08x c %d, %08x, got %7d\n",
+ __func__, brightness, val, c, b, ret);
+
+ return ret;
+}
+
+/*
+ * V1 = V0 - V0 * ( 5 + v1_adj ) / 600
+ * V15 = V1 - (V1 - V35) * ( 20 + v15_adj ) / 320
+ * V35 = V1 - (V1 - V59) * ( 65 + v35_adj ) / 320
+ * V59 = V1 - (V1 - V87) * ( 65 + v59_adj ) / 320
+ * V87 = V1 - (V1 - V171) * ( 65 + v87_adj ) / 320
+ * V171 = V1 - (V1 - V255) * ( 65 + v171_adj ) / 320
+ * V255 = V0 - V0 * ( 100 + v255_adj ) / 600
+ *
+ * v_n_adj = v_n_reg + v_n_offset
+ */
+
+static u32 v1adj_to_v1(u8 v1_adj, u32 v0)
+{
+ return DIV_ROUND_CLOSEST((600 - 5 - v1_adj) * v0, 600);
+}
+
+static u32 v1_to_v1adj(u32 v1, u32 v0)
+{
+ return 600 - 5 - DIV_ROUND_CLOSEST(600 * v1, v0);
+}
+
+static u32 vnadj_to_vn(int n, u8 v_n_adj, u32 v1, u32 v_next)
+{
+ int base = (n == V15) ? 20 : 65;
+ return v1 - DIV_ROUND_CLOSEST((v1 - v_next) * (base + v_n_adj), 320);
+}
+
+static u32 vn_to_vnadj(int n, u32 v_n, u32 v1, u32 v_next)
+{
+ int base = (n == V15) ? 20 : 65;
+ return DIV_ROUND_CLOSEST(320 * (v1 - v_n), v1 - v_next) - base;
+}
+
+static u32 v255adj_to_v255(u16 v255_adj, u32 v0)
+{
+ return DIV_ROUND_CLOSEST((600 - 100 - v255_adj) * v0, 600);
+}
+
+static u32 v255_to_v255adj(u32 v255, u32 v0)
+{
+ return 600 - 100 - DIV_ROUND_CLOSEST(600 * v255, v0);
+}
+
+static int gamma_reg_index(int c, int i)
+{
+ return 3 * i + c;
+}
+
+static int gamma_reg_index_v255_h(int c)
+{
+ return 3 * V255 + 2 * c;
+}
+
+static int gamma_reg_index_v255_l(int c)
+{
+ return gamma_reg_index_v255_h(c) + 1;
+}
+
+static void s6e8aa0_setup_dy_regs(struct s6e8aa0_data *s6, int c,
+ u32 v0, u32 v[V_COUNT], u8 dy[NUM_DY_REGS])
+{
+ static const struct {
+ int base;
+ u32 scale;
+ } output_table[256] = {
+ [0] = { },
+ [1] = { V1, },
+ [2] = { V15, 0x100000000ULL * 47 / 52, },
+ [3] = { V15, 0x100000000ULL * 42 / 52, },
+ [4] = { V15, 0x100000000ULL * 37 / 52, },
+ [5] = { V15, 0x100000000ULL * 32 / 52, },
+ [6] = { V15, 0x100000000ULL * 27 / 52, },
+ [7] = { V15, 0x100000000ULL * 23 / 52, },
+ [8] = { V15, 0x100000000ULL * 19 / 52, },
+ [9] = { V15, 0x100000000ULL * 15 / 52, },
+ [10] = { V15, 0x100000000ULL * 12 / 52, },
+ [11] = { V15, 0x100000000ULL * 9 / 52, },
+ [12] = { V15, 0x100000000ULL * 6 / 52, },
+ [13] = { V15, 0x100000000ULL * 4 / 52, },
+ [14] = { V15, 0x100000000ULL * 2 / 52, },
+ [15] = { V15, },
+ [16] = { V35, 0x100000000ULL * 66 / 70, },
+ [17] = { V35, 0x100000000ULL * 62 / 70, },
+ [18] = { V35, 0x100000000ULL * 58 / 70, },
+ [19] = { V35, 0x100000000ULL * 54 / 70, },
+ [20] = { V35, 0x100000000ULL * 50 / 70, },
+ [21] = { V35, 0x100000000ULL * 46 / 70, },
+ [22] = { V35, 0x100000000ULL * 42 / 70, },
+ [23] = { V35, 0x100000000ULL * 38 / 70, },
+ [24] = { V35, 0x100000000ULL * 34 / 70, },
+ [25] = { V35, 0x100000000ULL * 30 / 70, },
+ [26] = { V35, 0x100000000ULL * 27 / 70, },
+ [27] = { V35, 0x100000000ULL * 24 / 70, },
+ [28] = { V35, 0x100000000ULL * 21 / 70, },
+ [29] = { V35, 0x100000000ULL * 18 / 70, },
+ [30] = { V35, 0x100000000ULL * 15 / 70, },
+ [31] = { V35, 0x100000000ULL * 12 / 70, },
+ [32] = { V35, 0x100000000ULL * 9 / 70, },
+ [33] = { V35, 0x100000000ULL * 6 / 70, },
+ [34] = { V35, 0x100000000ULL * 3 / 70, },
+ [35] = { V35, },
+ [36] = { V59, 0x100000000ULL * 23 / 24, },
+ [37] = { V59, 0x100000000ULL * 22 / 24, },
+ [38] = { V59, 0x100000000ULL * 21 / 24, },
+ [39] = { V59, 0x100000000ULL * 20 / 24, },
+ [40] = { V59, 0x100000000ULL * 19 / 24, },
+ [41] = { V59, 0x100000000ULL * 18 / 24, },
+ [42] = { V59, 0x100000000ULL * 17 / 24, },
+ [43] = { V59, 0x100000000ULL * 16 / 24, },
+ [44] = { V59, 0x100000000ULL * 15 / 24, },
+ [45] = { V59, 0x100000000ULL * 14 / 24, },
+ [46] = { V59, 0x100000000ULL * 13 / 24, },
+ [47] = { V59, 0x100000000ULL * 12 / 24, },
+ [48] = { V59, 0x100000000ULL * 11 / 24, },
+ [49] = { V59, 0x100000000ULL * 10 / 24, },
+ [50] = { V59, 0x100000000ULL * 9 / 24, },
+ [51] = { V59, 0x100000000ULL * 8 / 24, },
+ [52] = { V59, 0x100000000ULL * 7 / 24, },
+ [53] = { V59, 0x100000000ULL * 6 / 24, },
+ [54] = { V59, 0x100000000ULL * 5 / 24, },
+ [55] = { V59, 0x100000000ULL * 4 / 24, },
+ [56] = { V59, 0x100000000ULL * 3 / 24, },
+ [57] = { V59, 0x100000000ULL * 2 / 24, },
+ [58] = { V59, 0x100000000ULL * 1 / 24, },
+ [59] = { V59, },
+ [60] = { V87, 0x100000000ULL * 27 / 28, },
+ [61] = { V87, 0x100000000ULL * 26 / 28, },
+ [62] = { V87, 0x100000000ULL * 25 / 28, },
+ [63] = { V87, 0x100000000ULL * 24 / 28, },
+ [64] = { V87, 0x100000000ULL * 23 / 28, },
+ [65] = { V87, 0x100000000ULL * 22 / 28, },
+ [66] = { V87, 0x100000000ULL * 21 / 28, },
+ [67] = { V87, 0x100000000ULL * 20 / 28, },
+ [68] = { V87, 0x100000000ULL * 19 / 28, },
+ [69] = { V87, 0x100000000ULL * 18 / 28, },
+ [70] = { V87, 0x100000000ULL * 17 / 28, },
+ [71] = { V87, 0x100000000ULL * 16 / 28, },
+ [72] = { V87, 0x100000000ULL * 15 / 28, },
+ [73] = { V87, 0x100000000ULL * 14 / 28, },
+ [74] = { V87, 0x100000000ULL * 13 / 28, },
+ [75] = { V87, 0x100000000ULL * 12 / 28, },
+ [76] = { V87, 0x100000000ULL * 11 / 28, },
+ [77] = { V87, 0x100000000ULL * 10 / 28, },
+ [78] = { V87, 0x100000000ULL * 9 / 28, },
+ [79] = { V87, 0x100000000ULL * 8 / 28, },
+ [80] = { V87, 0x100000000ULL * 7 / 28, },
+ [81] = { V87, 0x100000000ULL * 6 / 28, },
+ [82] = { V87, 0x100000000ULL * 5 / 28, },
+ [83] = { V87, 0x100000000ULL * 4 / 28, },
+ [84] = { V87, 0x100000000ULL * 3 / 28, },
+ [85] = { V87, 0x100000000ULL * 2 / 28, },
+ [86] = { V87, 0x100000000ULL * 1 / 28, },
+ [87] = { V87, },
+ [88] = { V171, 0x100000000ULL * 83 / 84, },
+ [89] = { V171, 0x100000000ULL * 82 / 84, },
+ [90] = { V171, 0x100000000ULL * 81 / 84, },
+ [91] = { V171, 0x100000000ULL * 80 / 84, },
+ [92] = { V171, 0x100000000ULL * 79 / 84, },
+ [93] = { V171, 0x100000000ULL * 78 / 84, },
+ [94] = { V171, 0x100000000ULL * 77 / 84, },
+ [95] = { V171, 0x100000000ULL * 76 / 84, },
+ [96] = { V171, 0x100000000ULL * 75 / 84, },
+ [97] = { V171, 0x100000000ULL * 74 / 84, },
+ [98] = { V171, 0x100000000ULL * 73 / 84, },
+ [99] = { V171, 0x100000000ULL * 72 / 84, },
+ [100] = { V171, 0x100000000ULL * 71 / 84, },
+ [101] = { V171, 0x100000000ULL * 70 / 84, },
+ [102] = { V171, 0x100000000ULL * 69 / 84, },
+ [103] = { V171, 0x100000000ULL * 68 / 84, },
+ [104] = { V171, 0x100000000ULL * 67 / 84, },
+ [105] = { V171, 0x100000000ULL * 66 / 84, },
+ [106] = { V171, 0x100000000ULL * 65 / 84, },
+ [107] = { V171, 0x100000000ULL * 64 / 84, },
+ [108] = { V171, 0x100000000ULL * 63 / 84, },
+ [109] = { V171, 0x100000000ULL * 62 / 84, },
+ [110] = { V171, 0x100000000ULL * 61 / 84, },
+ [111] = { V171, 0x100000000ULL * 60 / 84, },
+ [112] = { V171, 0x100000000ULL * 59 / 84, },
+ [113] = { V171, 0x100000000ULL * 58 / 84, },
+ [114] = { V171, 0x100000000ULL * 57 / 84, },
+ [115] = { V171, 0x100000000ULL * 56 / 84, },
+ [116] = { V171, 0x100000000ULL * 55 / 84, },
+ [117] = { V171, 0x100000000ULL * 54 / 84, },
+ [118] = { V171, 0x100000000ULL * 53 / 84, },
+ [119] = { V171, 0x100000000ULL * 52 / 84, },
+ [120] = { V171, 0x100000000ULL * 51 / 84, },
+ [121] = { V171, 0x100000000ULL * 50 / 84, },
+ [122] = { V171, 0x100000000ULL * 49 / 84, },
+ [123] = { V171, 0x100000000ULL * 48 / 84, },
+ [124] = { V171, 0x100000000ULL * 47 / 84, },
+ [125] = { V171, 0x100000000ULL * 46 / 84, },
+ [126] = { V171, 0x100000000ULL * 45 / 84, },
+ [127] = { V171, 0x100000000ULL * 44 / 84, },
+ [128] = { V171, 0x100000000ULL * 43 / 84, },
+ [129] = { V171, 0x100000000ULL * 42 / 84, },
+ [130] = { V171, 0x100000000ULL * 41 / 84, },
+ [131] = { V171, 0x100000000ULL * 40 / 84, },
+ [132] = { V171, 0x100000000ULL * 39 / 84, },
+ [133] = { V171, 0x100000000ULL * 38 / 84, },
+ [134] = { V171, 0x100000000ULL * 37 / 84, },
+ [135] = { V171, 0x100000000ULL * 36 / 84, },
+ [136] = { V171, 0x100000000ULL * 35 / 84, },
+ [137] = { V171, 0x100000000ULL * 34 / 84, },
+ [138] = { V171, 0x100000000ULL * 33 / 84, },
+ [139] = { V171, 0x100000000ULL * 32 / 84, },
+ [140] = { V171, 0x100000000ULL * 31 / 84, },
+ [141] = { V171, 0x100000000ULL * 30 / 84, },
+ [142] = { V171, 0x100000000ULL * 29 / 84, },
+ [143] = { V171, 0x100000000ULL * 28 / 84, },
+ [144] = { V171, 0x100000000ULL * 27 / 84, },
+ [145] = { V171, 0x100000000ULL * 26 / 84, },
+ [146] = { V171, 0x100000000ULL * 25 / 84, },
+ [147] = { V171, 0x100000000ULL * 24 / 84, },
+ [148] = { V171, 0x100000000ULL * 23 / 84, },
+ [149] = { V171, 0x100000000ULL * 22 / 84, },
+ [150] = { V171, 0x100000000ULL * 21 / 84, },
+ [151] = { V171, 0x100000000ULL * 20 / 84, },
+ [152] = { V171, 0x100000000ULL * 19 / 84, },
+ [153] = { V171, 0x100000000ULL * 18 / 84, },
+ [154] = { V171, 0x100000000ULL * 17 / 84, },
+ [155] = { V171, 0x100000000ULL * 16 / 84, },
+ [156] = { V171, 0x100000000ULL * 15 / 84, },
+ [157] = { V171, 0x100000000ULL * 14 / 84, },
+ [158] = { V171, 0x100000000ULL * 13 / 84, },
+ [159] = { V171, 0x100000000ULL * 12 / 84, },
+ [160] = { V171, 0x100000000ULL * 11 / 84, },
+ [161] = { V171, 0x100000000ULL * 10 / 84, },
+ [162] = { V171, 0x100000000ULL * 9 / 84, },
+ [163] = { V171, 0x100000000ULL * 8 / 84, },
+ [164] = { V171, 0x100000000ULL * 7 / 84, },
+ [165] = { V171, 0x100000000ULL * 6 / 84, },
+ [166] = { V171, 0x100000000ULL * 5 / 84, },
+ [167] = { V171, 0x100000000ULL * 4 / 84, },
+ [168] = { V171, 0x100000000ULL * 3 / 84, },
+ [169] = { V171, 0x100000000ULL * 2 / 84, },
+ [170] = { V171, 0x100000000ULL * 1 / 84, },
+ [171] = { V171, },
+ [172] = { V255, 0x100000000ULL * 83 / 84, },
+ [173] = { V255, 0x100000000ULL * 82 / 84, },
+ [174] = { V255, 0x100000000ULL * 81 / 84, },
+ [175] = { V255, 0x100000000ULL * 80 / 84, },
+ [176] = { V255, 0x100000000ULL * 79 / 84, },
+ [177] = { V255, 0x100000000ULL * 78 / 84, },
+ [178] = { V255, 0x100000000ULL * 77 / 84, },
+ [179] = { V255, 0x100000000ULL * 76 / 84, },
+ [180] = { V255, 0x100000000ULL * 75 / 84, },
+ [181] = { V255, 0x100000000ULL * 74 / 84, },
+ [182] = { V255, 0x100000000ULL * 73 / 84, },
+ [183] = { V255, 0x100000000ULL * 72 / 84, },
+ [184] = { V255, 0x100000000ULL * 71 / 84, },
+ [185] = { V255, 0x100000000ULL * 70 / 84, },
+ [186] = { V255, 0x100000000ULL * 69 / 84, },
+ [187] = { V255, 0x100000000ULL * 68 / 84, },
+ [188] = { V255, 0x100000000ULL * 67 / 84, },
+ [189] = { V255, 0x100000000ULL * 66 / 84, },
+ [190] = { V255, 0x100000000ULL * 65 / 84, },
+ [191] = { V255, 0x100000000ULL * 64 / 84, },
+ [192] = { V255, 0x100000000ULL * 63 / 84, },
+ [193] = { V255, 0x100000000ULL * 62 / 84, },
+ [194] = { V255, 0x100000000ULL * 61 / 84, },
+ [195] = { V255, 0x100000000ULL * 60 / 84, },
+ [196] = { V255, 0x100000000ULL * 59 / 84, },
+ [197] = { V255, 0x100000000ULL * 58 / 84, },
+ [198] = { V255, 0x100000000ULL * 57 / 84, },
+ [199] = { V255, 0x100000000ULL * 56 / 84, },
+ [200] = { V255, 0x100000000ULL * 55 / 84, },
+ [201] = { V255, 0x100000000ULL * 54 / 84, },
+ [202] = { V255, 0x100000000ULL * 53 / 84, },
+ [203] = { V255, 0x100000000ULL * 52 / 84, },
+ [204] = { V255, 0x100000000ULL * 51 / 84, },
+ [205] = { V255, 0x100000000ULL * 50 / 84, },
+ [206] = { V255, 0x100000000ULL * 49 / 84, },
+ [207] = { V255, 0x100000000ULL * 48 / 84, },
+ [208] = { V255, 0x100000000ULL * 47 / 84, },
+ [209] = { V255, 0x100000000ULL * 46 / 84, },
+ [210] = { V255, 0x100000000ULL * 45 / 84, },
+ [211] = { V255, 0x100000000ULL * 44 / 84, },
+ [212] = { V255, 0x100000000ULL * 43 / 84, },
+ [213] = { V255, 0x100000000ULL * 42 / 84, },
+ [214] = { V255, 0x100000000ULL * 41 / 84, },
+ [215] = { V255, 0x100000000ULL * 40 / 84, },
+ [216] = { V255, 0x100000000ULL * 39 / 84, },
+ [217] = { V255, 0x100000000ULL * 38 / 84, },
+ [218] = { V255, 0x100000000ULL * 37 / 84, },
+ [219] = { V255, 0x100000000ULL * 36 / 84, },
+ [220] = { V255, 0x100000000ULL * 35 / 84, },
+ [221] = { V255, 0x100000000ULL * 34 / 84, },
+ [222] = { V255, 0x100000000ULL * 33 / 84, },
+ [223] = { V255, 0x100000000ULL * 32 / 84, },
+ [224] = { V255, 0x100000000ULL * 31 / 84, },
+ [225] = { V255, 0x100000000ULL * 30 / 84, },
+ [226] = { V255, 0x100000000ULL * 29 / 84, },
+ [227] = { V255, 0x100000000ULL * 28 / 84, },
+ [228] = { V255, 0x100000000ULL * 27 / 84, },
+ [229] = { V255, 0x100000000ULL * 26 / 84, },
+ [230] = { V255, 0x100000000ULL * 25 / 84, },
+ [231] = { V255, 0x100000000ULL * 24 / 84, },
+ [232] = { V255, 0x100000000ULL * 23 / 84, },
+ [233] = { V255, 0x100000000ULL * 22 / 84, },
+ [234] = { V255, 0x100000000ULL * 21 / 84, },
+ [235] = { V255, 0x100000000ULL * 20 / 84, },
+ [236] = { V255, 0x100000000ULL * 19 / 84, },
+ [237] = { V255, 0x100000000ULL * 18 / 84, },
+ [238] = { V255, 0x100000000ULL * 17 / 84, },
+ [239] = { V255, 0x100000000ULL * 16 / 84, },
+ [240] = { V255, 0x100000000ULL * 15 / 84, },
+ [241] = { V255, 0x100000000ULL * 14 / 84, },
+ [242] = { V255, 0x100000000ULL * 13 / 84, },
+ [243] = { V255, 0x100000000ULL * 12 / 84, },
+ [244] = { V255, 0x100000000ULL * 11 / 84, },
+ [245] = { V255, 0x100000000ULL * 10 / 84, },
+ [246] = { V255, 0x100000000ULL * 9 / 84, },
+ [247] = { V255, 0x100000000ULL * 8 / 84, },
+ [248] = { V255, 0x100000000ULL * 7 / 84, },
+ [249] = { V255, 0x100000000ULL * 6 / 84, },
+ [250] = { V255, 0x100000000ULL * 5 / 84, },
+ [251] = { V255, 0x100000000ULL * 4 / 84, },
+ [252] = { V255, 0x100000000ULL * 3 / 84, },
+ [253] = { V255, 0x100000000ULL * 2 / 84, },
+ [254] = { V255, 0x100000000ULL * 1 / 84, },
+ [255] = { V255, },
+ };
+
+ u8 brightness = s6->bl;
+ int last_y = 0;
+ int i;
+ int j = 0;
+ u32 vh = v0;
+ u32 vl = vh;
+ u32 vt;
+ u32 vb;
+ u64 tmp;
+ int y;
+ u32 scale;
+
+ for (i = 0; i < NUM_DY_REGS; i++) {
+ vt = s6e8aa0_gamma_lookup(s6, brightness, s6->dyi_to_b[i], c);
+ while (vl > vt && j < ARRAY_SIZE(output_table) - 1) {
+ j++;
+ vh = vl;
+ vl = v[output_table[j].base];
+ scale = output_table[j].scale;
+ if (scale) {
+ vb = v[output_table[j].base - 1];
+ tmp = vb - vl;
+ tmp *= scale;
+ tmp >>= 32;
+ vl += tmp;
+ }
+ }
+ y = j * 4;
+ if (vh > vl && vt >= vl)
+ y -= DIV_ROUND_CLOSEST(4 * (vt - vl), vh - vl);
+ pr_debug("%s: dy%d %d, v %d (vh %d @ %d, vl %d @ %d)\n",
+ __func__, i, y, vt, vh, j * 4 - 4, vl, j * 4);
+ if (y < last_y)
+ y = last_y;
+ dy[i] = y - last_y;
+ last_y = y;
+ }
+}
+
+static void s6e8aa0_setup_gamma_regs(struct s6e8aa0_data *s6, u8 gamma_regs[],
+ u8 dy_regs[3][NUM_DY_REGS + 1])
+{
+ int c, i;
+ u8 brightness = s6->bl;
+ const struct s6e8aa0_gamma_adj_points *bv = s6->gamma_adj_points;
+
+ for (c = 0; c < 3; c++) {
+ u32 adj;
+ u32 adj_min;
+ u32 adj_max;
+ s16 offset;
+ u32 v0 = s6e8aa0_gamma_lookup(s6, brightness, BV_0, c);
+ u32 v[V_COUNT];
+
+ v[V1] = s6e8aa0_gamma_lookup(s6, brightness, bv->v1, c);
+ offset = s6->gamma_reg_offsets.v[1][c][V1];
+ adj_max = min(V1_ADJ_MAX, V1_ADJ_MAX - offset);
+ adj_min = max(0, 0 - offset);
+ adj = v1_to_v1adj(v[V1], v0) - offset;
+ if (adj < adj_min || adj > adj_max) {
+ pr_debug("%s: bad adj value %d, v0 %d, v1 %d, c %d\n",
+ __func__, adj, v0, v[V1], c);
+ adj = clamp_t(int, adj, adj_min, adj_max);
+ }
+ gamma_regs[gamma_reg_index(c, V1)] = adj;
+ v[V1] = v1adj_to_v1(adj + offset, v0);
+
+ v[V255] = s6e8aa0_gamma_lookup(s6, brightness, BV_255, c);
+ offset = s6->gamma_reg_offsets.v[1][c][V255];
+ adj_max = min(V255_ADJ_MAX, V255_ADJ_MAX - offset);
+ adj_min = max(0, 0 - offset);
+ adj = v255_to_v255adj(v[V255], v0) - offset;
+ if (adj < adj_min || adj > adj_max) {
+ pr_debug("%s: bad adj value %d, v0 %d, v255 %d, c %d\n",
+ __func__, adj, v0, v[V255], c);
+ adj = clamp_t(int, adj, adj_min, adj_max);
+ }
+ gamma_regs[3 * V255 + 2 * c] = adj >> 8;
+ gamma_regs[3 * V255 + 2 * c + 1] = (adj & 0xff);
+ gamma_regs[gamma_reg_index_v255_h(c)] = adj >> 8;
+ gamma_regs[gamma_reg_index_v255_l(c)] = adj;
+ v[V255] = v255adj_to_v255(adj + offset, v0);
+
+ v[V15] = s6e8aa0_gamma_lookup(s6, brightness, bv->v15, c);
+ v[V35] = s6e8aa0_gamma_lookup(s6, brightness, bv->v35, c);
+ v[V59] = s6e8aa0_gamma_lookup(s6, brightness, bv->v59, c);
+ v[V87] = s6e8aa0_gamma_lookup(s6, brightness, bv->v87, c);
+ v[V171] = s6e8aa0_gamma_lookup(s6, brightness, bv->v171, c);
+
+ for (i = V171; i >= V15; i--) {
+ offset = s6->gamma_reg_offsets.v[1][c][i];
+ adj_max = min(255, 255 - offset);
+ adj_min = max(0, 0 - offset);
+ if (v[V1] <= v[i + 1] || v[V1] <= v[i]) {
+ adj = -1;
+ } else {
+ adj = vn_to_vnadj(i, v[i], v[V1], v[i + 1]);
+ adj -= offset;
+ }
+ if (adj < adj_min || adj > adj_max) {
+ pr_debug("%s: bad adj value %d, "
+ "vh %d, v %d, c %d\n",
+ __func__, adj, v[i + 1], v[i], c);
+ adj = clamp_t(int, adj, adj_min, adj_max);
+ }
+ gamma_regs[gamma_reg_index(c, i)] = adj;
+ v[i] = vnadj_to_vn(i, adj + offset, v[V1], v[i + 1]);
+ }
+
+ s6e8aa0_setup_dy_regs(s6, c, v0, v, dy_regs[c] + 1);
+ }
+}
+
+static void s6e8aa0_update_acl_set(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ struct panel_s6e8aa0_data *pdata = s6->pdata;
+ int i;
+ unsigned int cd;
+ unsigned int max_cd = 0;
+ const struct s6e8aa0_acl_parameters *acl;
+
+ /* Quietly return if you don't have a table */
+ if (!pdata->acl_table_size)
+ return;
+
+ max_cd = pdata->acl_table[pdata->acl_table_size - 1].cd;
+
+ cd = s6->bl * max_cd / 255;
+ if (cd > max_cd)
+ cd = max_cd;
+
+ if (s6->acl_enable) {
+ for (i = 0; i < pdata->acl_table_size; i++)
+ if (cd <= pdata->acl_table[i].cd)
+ break;
+
+ if (i == pdata->acl_table_size)
+ i = pdata->acl_table_size - 1;
+
+ acl = &pdata->acl_table[i];
+ if (s6->acl_cur != acl->acl_val) {
+ s6e8aa0_write_block_nosync(dssdev, acl->regs,
+ sizeof(acl->regs));
+ s6e8aa0_write_reg(dssdev, 0xC0,
+ 0x01 | (s6->acl_average << 4)); /* ACL ON */
+
+ s6->acl_cur = acl->acl_val;
+ }
+ } else {
+ if (s6->acl_cur != 0) {
+ s6->acl_cur = 0;
+ s6e8aa0_write_reg(dssdev, 0xC0, 0x00); /* ACL OFF */
+ }
+ }
+ pr_debug("%s : cur_acl=%d, %d\n", __func__, s6->acl_cur,
+ s6->acl_enable);
+ return;
+}
+
+static void s6e8aa0_update_elvss(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ struct panel_s6e8aa0_data *pdata = s6->pdata;
+ u8 elvss_cmd[3];
+ u8 elvss;
+ u8 limit = 0x9F;
+ unsigned int i;
+ unsigned int cd;
+ unsigned int max_cd = 0;
+
+ if (!pdata->elvss_table_size)
+ return;
+
+ elvss_cmd[0] = 0xB1;
+ elvss_cmd[1] = 0x04;
+
+ max_cd = pdata->elvss_table[pdata->elvss_table_size - 1].cd;
+ cd = s6->bl * max_cd / 255;
+
+ for (i = 0; i < pdata->elvss_table_size - 1; i++)
+ if (cd <= pdata->elvss_table[i].cd)
+ break;
+
+ if (i == s6->elvss_cur_i)
+ return;
+
+ s6->elvss_cur_i = i;
+
+ elvss = s6->panel_id[2] & 0x1F; /* ELVSS Pulse 0-4bits */
+ elvss += pdata->elvss_table[i].elvss_val;
+
+ if (elvss > limit)
+ elvss = limit;
+
+ elvss_cmd[2] = elvss;
+
+ s6e8aa0_write_block(dssdev, elvss_cmd, sizeof(elvss_cmd));
+ pr_debug("%s - brightness : %d, cd : %d, elvss : %02x\n",
+ __func__, s6->bl, cd, elvss);
+ return;
+}
+
+static int s6e8aa0_update_brightness(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ u8 gamma_regs[NUM_GAMMA_REGS + 2];
+ u8 dy_regs[3][NUM_DY_REGS + 1];
+
+ gamma_regs[0] = 0xFA;
+ gamma_regs[1] = 0x01;
+ dy_regs[0][0] = 0xb8;
+ dy_regs[1][0] = 0xb9;
+ dy_regs[2][0] = 0xba;
+
+ s6e8aa0_setup_gamma_regs(s6, gamma_regs + 2, dy_regs);
+ s6e8aa0_write_block_nosync(dssdev, gamma_regs, sizeof(gamma_regs));
+ s6e8aa0_write_block_nosync(dssdev, dy_regs[0], sizeof(dy_regs[0]));
+ s6e8aa0_write_block_nosync(dssdev, dy_regs[1], sizeof(dy_regs[1]));
+ s6e8aa0_write_block_nosync(dssdev, dy_regs[2], sizeof(dy_regs[2]));
+ s6e8aa0_write_reg(dssdev, 0xF7, 0x01);
+
+ s6e8aa0_update_acl_set(dssdev);
+ s6e8aa0_update_elvss(dssdev);
+ return 0;
+}
+
+static u64 s6e8aa0_voltage_lookup(struct s6e8aa0_data *s6, int c, u32 v)
+{
+ int i;
+ u32 vh = ~0, vl = ~0;
+ u32 bl = 0, bh = 0;
+ u64 ret;
+ struct panel_s6e8aa0_data *pdata = s6->pdata;
+
+ for (i = 0; i < pdata->gamma_table_size; i++) {
+ vh = vl;
+ vl = pdata->gamma_table[i].v[c];
+ bh = bl;
+ bl = pdata->gamma_table[i].brightness;
+ if (vl <= v)
+ break;
+ }
+ if (i == 0 || (v - vl) == 0) {
+ ret = bl;
+ } else {
+ ret = (u64)bh * (s32)(v - vl) + (u64)bl * (vh - v);
+ do_div(ret, vh - vl);
+ }
+ pr_debug("%s: looking for %7d c %d, "
+ "found %7d:%7d, b %08x:%08x, ret %08llx\n",
+ __func__, v, c, vl, vh, bl, bh, ret);
+ return ret;
+}
+
+static u64 s6e8aa0_limit_brightness(u64 bc[3], u64 bcmax)
+{
+ int c;
+ int shift;
+
+ for (c = 0; c < 3; c++)
+ if (bc[c] > bcmax)
+ bcmax = bc[c];
+
+ if (bcmax != 0xffffffff) {
+ u64 tmp;
+ pr_warn("s6e8aa: factory calibration info is out of range: scale to 0x%llx\n",
+ bcmax);
+ shift = fls(bcmax >> 32);
+ tmp = (bcmax << (32 - shift)) - 1;
+ do_div(tmp, 0xffffffff);
+ tmp++;
+ pr_warn("s6e8aa: factory calibration info is out of range: scale to 0x%llx, shift %d\n",
+ tmp, shift);
+ for (c = 0; c < 3; c++) {
+ bc[c] <<= 32 - shift;
+ do_div(bc[c], tmp);
+ }
+ }
+ return bcmax;
+}
+
+static void s6e8aa0_apply_color_adj(
+ const struct s6e8aa0_factory_calibration_info *fi, u64 bc[3])
+{
+ int c;
+ int shift = fi->color_adj.rshift;
+
+ if (!shift)
+ return;
+
+ for (c = 0; c < 3; c++) {
+ u64 b = bc[c];
+ u32 bh = b >> 32;
+ u32 bl = b;
+ u64 m = fi->color_adj.mult[c];
+ /*
+ * Calculate ((b * m) >> shift).
+ * If b is greater than 2^32, The 64 by 32 to 64 bit (b * m)
+ * multiplication can overflow, even if the end result fits,
+ * so we split it into two 32 by 32 to 64 bit operations.
+ */
+ bc[c] = ((bh * m) << (32 - shift)) + ((bl * m) >> shift);
+ }
+}
+
+static int s6e8aa0_cmp_gamma_entry(const void *pa, const void *pb)
+{
+ u32 a = ((const struct s6e8aa0_gamma_entry *)pa)->brightness;
+ u32 b = ((const struct s6e8aa0_gamma_entry *)pb)->brightness;
+ if (a > b)
+ return 1;
+ if (a < b)
+ return -1;
+ return 0;
+}
+
+static void s6e8aa0_adjust_brightness_from_mtp(struct s6e8aa0_data *s6)
+{
+ int b, c, i;
+ u32 v[2][3][V_COUNT];
+ u64 bc[3];
+ u64 bcmax;
+ struct panel_s6e8aa0_data *pdata = s6->pdata;
+ const struct s6e8aa0_gamma_reg_offsets *offset = &s6->gamma_reg_offsets;
+ struct s6e8aa0_factory_calibration_info *fi = pdata->factory_info;
+ struct s6e8aa0_gamma_entry *brightness_table;
+ int brightness_table_size = 1;
+
+ for (b = 0; b < 2; b++)
+ for (i = 0; i < V_COUNT; i++)
+ if (fi->brightness[b][i])
+ brightness_table_size++;
+
+ brightness_table = kmalloc(sizeof(*brightness_table) *
+ brightness_table_size, GFP_KERNEL);
+ if (!brightness_table) {
+ dev_err(&s6->dssdev->dev,
+ "Failed to allocate brightness table\n");
+ return;
+ }
+ brightness_table->brightness = 0;
+ for (c = 0; c < 3; c++)
+ brightness_table->v[c] = 0;
+ s6->brightness_table = brightness_table;
+ s6->brightness_table_size = brightness_table_size;
+ brightness_table++;
+
+ for (b = 0; b < 2; b++) {
+ for (c = 0; c < 3; c++) {
+ u32 v0 = s6e8aa0_raw_gamma_lookup(s6, BV_0, c);
+ v[b][c][V1] = v1adj_to_v1(fi->regs[b][c][V1] +
+ offset->v[b][c][V1], v0);
+ v[b][c][V255] = v255adj_to_v255(fi->regs[b][c][V255] +
+ offset->v[b][c][V255], v0);
+ for (i = V171; i >= V15; i--)
+ v[b][c][i] = vnadj_to_vn(i, fi->regs[b][c][i] +
+ offset->v[b][c][i],
+ v[b][c][V1], v[b][c][i + 1]);
+ }
+ }
+
+ for (b = 0; b < 2; b++)
+ for (i = 0; i < V_COUNT; i++)
+ pr_debug("%s: b %d, p %d, R %7dv, G %7dv, B %7dv\n",
+ __func__, b, i,
+ v[b][0][i], v[b][1][i], v[b][2][i]);
+
+ bcmax = 0xffffffff;
+ for (b = 0; b < 2; b++) {
+ for (i = 0; i < V_COUNT; i++) {
+ if (!fi->brightness[b][i])
+ continue;
+
+ for (c = 0; c < 3; c++)
+ bc[c] = s6e8aa0_voltage_lookup(s6, c,
+ v[b][c][i]);
+
+ s6e8aa0_apply_color_adj(fi, bc);
+ bcmax = s6e8aa0_limit_brightness(bc, bcmax);
+ }
+ }
+
+ for (b = 0; b < 2; b++) {
+ for (i = 0; i < V_COUNT; i++) {
+ if (!fi->brightness[b][i])
+ continue;
+
+ for (c = 0; c < 3; c++) {
+ bc[c] = s6e8aa0_voltage_lookup(s6, c,
+ v[b][c][i]);
+ pr_debug("s6e8aa: c%d, %d, b-%08llx, before scaling\n",
+ c, i, bc[c]);
+ }
+
+ s6e8aa0_apply_color_adj(fi, bc);
+ for (c = 0; c < 3; c++) {
+ pr_debug("s6e8aa: c%d, %d, b-%08llx, after color adj\n",
+ c, i, bc[c]);
+ }
+
+ s6e8aa0_limit_brightness(bc, bcmax);
+
+ brightness_table->brightness = fi->brightness[b][i];
+ pr_info("s6e8aa: d/b %d, p %d, b-%08x\n",
+ b, i, fi->brightness[b][i]);
+ for (c = 0; c < 3; c++) {
+ if (bc[c] > s6->brightness_limit[c])
+ s6->brightness_limit[c] = bc[c];
+ brightness_table->v[c] = bc[c];
+ pr_info("s6e8aa: c%d, %d, b-%08llx, got v %d, factory wants %d\n",
+ c, i, bc[c],
+ s6e8aa0_raw_gamma_lookup(s6, bc[c], c),
+ v[b][c][i]);
+ }
+ brightness_table++;
+ }
+ }
+ sort(s6->brightness_table + 1, s6->brightness_table_size - 1,
+ sizeof(*s6->brightness_table), s6e8aa0_cmp_gamma_entry, NULL);
+}
+
+static s16 s9_to_s16(s16 v)
+{
+ return (s16)(v << 7) >> 7;
+}
+
+static int mtp_reg_index(int c, int i)
+{
+ return c * (V_COUNT + 1) + i;
+}
+
+static void s6e8aa0_read_id_info(struct s6e8aa0_data *s6)
+{
+ struct omap_dss_device *dssdev = s6->dssdev;
+ int ret;
+ u8 cmd = 0xD1;
+
+ dsi_vc_set_max_rx_packet_size(dssdev, 1, 3);
+ ret = s6e8aa0_read_block(dssdev, cmd, s6->panel_id,
+ ARRAY_SIZE(s6->panel_id));
+ dsi_vc_set_max_rx_packet_size(dssdev, 1, 1);
+ if (ret < 0) {
+ pr_err("%s: Failed to read id data\n", __func__);
+ return;
+ }
+}
+
+static void s6e8aa0_read_mtp_info(struct s6e8aa0_data *s6, int b)
+{
+ int ret;
+ int c, i;
+ u8 mtp_data[24];
+ u8 cmd = b ? 0xD3 : 0xD4;
+ struct omap_dss_device *dssdev = s6->dssdev;
+
+ s6e8aa0_write_block(dssdev, s6e8aa0_mtp_unlock,
+ ARRAY_SIZE(s6e8aa0_mtp_unlock));
+ dsi_vc_set_max_rx_packet_size(dssdev, 1, 24);
+ ret = s6e8aa0_read_block(dssdev, cmd, mtp_data, ARRAY_SIZE(mtp_data));
+ dsi_vc_set_max_rx_packet_size(dssdev, 1, 1);
+ s6e8aa0_write_block(dssdev, s6e8aa0_mtp_lock,
+ ARRAY_SIZE(s6e8aa0_mtp_lock));
+ if (ret < 0) {
+ pr_err("%s: Failed to read mtp data\n", __func__);
+ return;
+ }
+ for (c = 0; c < 3; c++) {
+ for (i = 0; i < V255; i++)
+ s6->gamma_reg_offsets.v[b][c][i] =
+ (s8)mtp_data[mtp_reg_index(c, i)];
+
+ s6->gamma_reg_offsets.v[b][c][V255] =
+ s9_to_s16(mtp_data[mtp_reg_index(c, V255)] << 8 |
+ mtp_data[mtp_reg_index(c, V255 + 1)]);
+ }
+}
+
+static int s6e8aa0_set_brightness(struct backlight_device *bd)
+{
+ struct omap_dss_device *dssdev = dev_get_drvdata(&bd->dev);
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ int bl = bd->props.brightness;
+ int ret = 0;
+
+ if (bl == s6->bl)
+ return 0;
+
+ s6->bl = bl;
+ mutex_lock(&s6->lock);
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+ dsi_bus_lock(dssdev);
+ ret = s6e8aa0_update_brightness(dssdev);
+ dsi_bus_unlock(dssdev);
+ }
+ mutex_unlock(&s6->lock);
+ return ret;
+}
+
+static int s6e8aa0_get_brightness(struct backlight_device *bd)
+{
+ return bd->props.brightness;
+}
+
+static const struct backlight_ops s6e8aa0_backlight_ops = {
+ .get_brightness = s6e8aa0_get_brightness,
+ .update_status = s6e8aa0_set_brightness,
+};
+
+static void seq_print_gamma_regs(struct seq_file *m, const u8 gamma_regs[])
+{
+ struct s6e8aa0_data *s6 = m->private;
+ int c, i;
+ const int adj_points[] = { 1, 15, 35, 59, 87, 171, 255 };
+ const char color[] = { 'R', 'G', 'B' };
+ u8 brightness = s6->bl;
+ const struct s6e8aa0_gamma_adj_points *bv = s6->gamma_adj_points;
+ const struct s6e8aa0_gamma_reg_offsets *offset = &s6->gamma_reg_offsets;
+
+ for (c = 0; c < 3; c++) {
+ u32 adj[V_COUNT];
+ u32 vt[V_COUNT];
+ u32 v[V_COUNT];
+ u32 v0 = s6e8aa0_gamma_lookup(s6, brightness, BV_0, c);
+
+ vt[V1] = s6e8aa0_gamma_lookup(s6, brightness, bv->v1, c);
+ vt[V15] = s6e8aa0_gamma_lookup(s6, brightness, bv->v15, c);
+ vt[V35] = s6e8aa0_gamma_lookup(s6, brightness, bv->v35, c);
+ vt[V59] = s6e8aa0_gamma_lookup(s6, brightness, bv->v59, c);
+ vt[V87] = s6e8aa0_gamma_lookup(s6, brightness, bv->v87, c);
+ vt[V171] = s6e8aa0_gamma_lookup(s6, brightness, bv->v171, c);
+ vt[V255] = s6e8aa0_gamma_lookup(s6, brightness, BV_255, c);
+
+ adj[V1] = gamma_regs[gamma_reg_index(c, V1)];
+ v[V1] = v1adj_to_v1(adj[V1] + offset->v[1][c][V1], v0);
+
+ adj[V255] = gamma_regs[gamma_reg_index_v255_h(c)] << 8 |
+ gamma_regs[gamma_reg_index_v255_l(c)];
+ v[V255] = v255adj_to_v255(adj[V255] + offset->v[1][c][V255],
+ v0);
+
+ for (i = V171; i >= V15; i--) {
+ adj[i] = gamma_regs[gamma_reg_index(c, i)];
+ v[i] = vnadj_to_vn(i, adj[i] + offset->v[1][c][i],
+ v[V1], v[i + 1]);
+ }
+ seq_printf(m, "%c v0 %7d\n",
+ color[c], v0);
+ for (i = 0; i < V_COUNT; i++) {
+ seq_printf(m, "%c adj %3d (%02x) %+4d "
+ "v%-3d %7d - %7d %+8d\n",
+ color[c], adj[i], adj[i], offset->v[1][c][i],
+ adj_points[i], v[i], vt[i], v[i] - vt[i]);
+ }
+ }
+}
+
+static void seq_print_dy_regs(struct seq_file *m,
+ u8 dy_regs[3][NUM_DY_REGS + 1])
+{
+ int i, c;
+ u16 y[3] = {};
+ seq_printf(m, " R y (rv) G y (rv) B y (rv)\n");
+ for (i = 0; i < NUM_DY_REGS; i++) {
+ seq_printf(m, "%-2d:", i);
+ for (c = 0; c < 3; c++) {
+ y[c] += dy_regs[c][i + 1];
+ seq_printf(m, " %4d (%02x)", y[c], dy_regs[c][i + 1]);
+ }
+ seq_printf(m, "\n");
+ }
+}
+
+static int s6e8aa0_current_gamma_show(struct seq_file *m, void *unused)
+{
+ struct s6e8aa0_data *s6 = m->private;
+ u8 gamma_regs[NUM_GAMMA_REGS];
+ u8 dy_regs[3][NUM_DY_REGS + 1];
+
+ mutex_lock(&s6->lock);
+ s6e8aa0_setup_gamma_regs(s6, gamma_regs, dy_regs);
+ seq_printf(m, "brightness %3d:\n", s6->bl);
+ seq_print_gamma_regs(m, gamma_regs);
+ seq_printf(m, "\n");
+ seq_print_dy_regs(m, dy_regs);
+ mutex_unlock(&s6->lock);
+ return 0;
+}
+
+static int s6e8aa0_current_gamma_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, s6e8aa0_current_gamma_show, inode->i_private);
+}
+
+static int s6e8aa0_gamma_correction_show(struct seq_file *m, void *unused)
+{
+ struct s6e8aa0_data *s6 = m->private;
+ const struct s6e8aa0_gamma_entry *bte;
+ int n, c;
+
+ mutex_lock(&s6->lock);
+ n = s6->brightness_table_size;
+ bte = s6->brightness_table;
+ while (n--) {
+ seq_printf(m, "0x%08x", bte->brightness);
+ for (c = 0; c < 3; c++)
+ seq_printf(m, " 0x%08x", bte->v[c]);
+ seq_printf(m, "\n");
+ bte++;
+ }
+ seq_printf(m, "\n");
+ seq_printf(m, "0x%08x", BV_255);
+ for (c = 0; c < 3; c++)
+ seq_printf(m, " 0x%08x", s6->brightness_limit[c]);
+ seq_printf(m, "\n");
+ mutex_unlock(&s6->lock);
+ return 0;
+}
+
+static int s6e8aa0_gamma_correction_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, s6e8aa0_gamma_correction_show,
+ inode->i_private);
+}
+
+static ssize_t s6e8aa0_gamma_correction_write(struct file *file,
+ const char __user *buf,
+ size_t size, loff_t *ppos)
+{
+ struct seq_file *m = file->private_data;
+ struct s6e8aa0_data *s6 = m->private;
+ struct omap_dss_device *dssdev = s6->dssdev;
+ char sbuf[80];
+ u32 val[4] = {
+ };
+ u32 last_val[4];
+ struct s6e8aa0_gamma_entry *bt = NULL;
+ struct s6e8aa0_gamma_entry *new_bt;
+ int bt_size = 0;
+
+ int ret;
+ size_t used;
+ size_t sbuf_len = sizeof(sbuf) - 1;
+ size_t rem = size;
+ int c;
+ int i;
+
+ while (rem && val[0] != BV_255) {
+ if (sbuf_len > rem)
+ sbuf_len = rem;
+ if (copy_from_user(sbuf, buf, sbuf_len)) {
+ ret = -EFAULT;
+ goto err;
+ }
+ sbuf[sbuf_len] = '\0';
+
+ for (i = 0; i < ARRAY_SIZE(val); i++)
+ last_val[i] = val[i];
+ ret = sscanf(sbuf, "%i %i %i %i\n%n",
+ &val[0], &val[1], &val[2], &val[3], &used);
+ if (ret < 4 || !used)
+ break;
+
+ buf += used;
+ rem -= used;
+
+ if (!bt_size) {
+ for (i = 0; i < ARRAY_SIZE(val); i++) {
+ if (val[i] != 0) {
+ pr_info("%s: invalid start value %d: "
+ "0x%08x != 0\n",
+ __func__, i, val[i]);
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ } else {
+ for (i = 0; i < ARRAY_SIZE(val); i++) {
+ if (val[i] <= last_val[i]) {
+ pr_info("%s: invalid value %d: "
+ "0x%08x <= 0x%08x\n", __func__,
+ i, val[i], last_val[i]);
+ ret = -EINVAL;
+ goto err;
+ }
+ }
+ for (c = 0; c < 3; c++) {
+ if (val[c + 1] > s6->brightness_limit[c]) {
+ pr_info("%s: invalid value %d: "
+ "0x%08x > 0x%08x\n", __func__,
+ c, val[c + 1],
+ s6->brightness_limit[c]);
+ ret = -EOVERFLOW;
+ goto err;
+ }
+ }
+ }
+
+ new_bt = krealloc(bt, (bt_size + 1) * sizeof(*bt), GFP_KERNEL);
+ if (!new_bt) {
+ ret = -ENOMEM;
+ goto err;
+ }
+ bt = new_bt;
+ bt[bt_size].brightness = val[0];
+ for (c = 0; c < 3; c++)
+ bt[bt_size].v[c] = val[c + 1];
+ bt_size++;
+ }
+ if (val[0] != BV_255) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ ret = size - rem;
+ mutex_lock(&s6->lock);
+ pr_debug("%s: got new brightness_table size %d\n", __func__, bt_size);
+ swap(bt, s6->brightness_table);
+ s6->brightness_table_size = bt_size;
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
+ dsi_bus_lock(dssdev);
+ s6e8aa0_update_brightness(dssdev);
+ dsi_bus_unlock(dssdev);
+ }
+ mutex_unlock(&s6->lock);
+
+err:
+ kfree(bt);
+ return ret;
+}
+
+static ssize_t acl_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = dev_get_drvdata(dev);
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+
+ snprintf(buf, PAGE_SIZE, "%d\n", s6->acl_enable);
+
+ return strlen(buf);
+}
+
+static ssize_t acl_enable_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct omap_dss_device *dssdev = dev_get_drvdata(dev);
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ long value;
+ bool enable;
+ int rc;
+
+ rc = strict_strtol(buf, 0, &value);
+
+ if (rc < 0)
+ return rc;
+
+ enable = value;
+
+ mutex_lock(&s6->lock);
+ if (s6->acl_enable != enable) {
+ dsi_bus_lock(dssdev);
+
+ s6->acl_enable = enable;
+ s6e8aa0_update_acl_set(dssdev);
+
+ dsi_bus_unlock(dssdev);
+ }
+ mutex_unlock(&s6->lock);
+ return size;
+}
+
+static DEVICE_ATTR(acl_set, S_IRUGO|S_IWUSR,
+ acl_enable_show, acl_enable_store);
+
+
+static ssize_t acl_average_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct omap_dss_device *dssdev = dev_get_drvdata(dev);
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+
+ snprintf(buf, PAGE_SIZE, "%d\n", s6->acl_average);
+
+ return strlen(buf);
+}
+
+static ssize_t acl_average_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct omap_dss_device *dssdev = dev_get_drvdata(dev);
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ long value;
+ int rc;
+
+ rc = strict_strtol(buf, 0, &value);
+
+ if (rc < 0)
+ return rc;
+
+ if (value < 0 || value > 7)
+ return -EINVAL;
+
+ mutex_lock(&s6->lock);
+ if (s6->acl_average != value) {
+ dsi_bus_lock(dssdev);
+
+ s6->acl_average = value;
+ s6->acl_cur = 0;
+ s6e8aa0_update_acl_set(dssdev);
+
+ dsi_bus_unlock(dssdev);
+ }
+ mutex_unlock(&s6->lock);
+ return size;
+}
+
+static DEVICE_ATTR(acl_average, S_IRUGO|S_IWUSR,
+ acl_average_show, acl_average_store);
+
+static struct attribute *s6e8aa0_bl_attributes[] = {
+ &dev_attr_acl_set.attr,
+ &dev_attr_acl_average.attr,
+ NULL
+};
+
+static const struct attribute_group s6e8aa0_bl_attr_group = {
+ .attrs = s6e8aa0_bl_attributes,
+};
+
+static const struct file_operations s6e8aa0_current_gamma_fops = {
+ .open = s6e8aa0_current_gamma_open,
+ .read = seq_read,
+ .release = single_release,
+};
+
+static const struct file_operations s6e8aa0_gamma_correction_fops = {
+ .open = s6e8aa0_gamma_correction_open,
+ .read = seq_read,
+ .write = s6e8aa0_gamma_correction_write,
+ .release = single_release,
+};
+
+static int s6e8aa0_probe(struct omap_dss_device *dssdev)
+{
+ int ret = 0;
+ struct backlight_properties props = {
+ .brightness = 255,
+ .max_brightness = 255,
+ .type = BACKLIGHT_RAW,
+ };
+ struct s6e8aa0_data *s6 = NULL;
+
+ dev_dbg(&dssdev->dev, "s6e8aa0_probe\n");
+
+ if (dssdev->data == NULL) {
+ dev_err(&dssdev->dev, "no platform data!\n");
+ return -EINVAL;
+ }
+
+ dssdev->panel.config = OMAP_DSS_LCD_TFT;
+ dssdev->panel.timings = s6e8aa0_timings;
+
+ dssdev->ctrl.pixel_size = 24;
+ dssdev->panel.acbi = 0;
+ dssdev->panel.acb = 40;
+
+ s6 = kzalloc(sizeof(*s6), GFP_KERNEL);
+ if (!s6)
+ return -ENOMEM;
+
+ s6->dssdev = dssdev;
+ s6->pdata = dssdev->data;
+
+ s6->bl = props.brightness;
+
+ if (!s6->pdata->seq_display_set || !s6->pdata->seq_etc_set
+ || !s6->pdata->gamma_table) {
+ dev_err(&dssdev->dev, "Invalid platform data\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ s6->gamma_adj_points =
+ s6->pdata->gamma_adj_points ?: &default_gamma_adj_points;
+ s6->dyi_to_b = s6e8aa0_srgb_dyi_to_b;
+
+ ret = gpio_request(s6->pdata->reset_gpio, "s6e8aa0_reset");
+ if (ret < 0) {
+ dev_err(&dssdev->dev, "gpio_request %d failed!\n", s6->pdata->reset_gpio);
+ goto err;
+ }
+ gpio_direction_output(s6->pdata->reset_gpio, 1);
+
+ mutex_init(&s6->lock);
+
+ atomic_set(&s6->do_update, 0);
+
+ dev_set_drvdata(&dssdev->dev, s6);
+
+ /* Register DSI backlight control */
+ s6->bldev = backlight_device_register("s6e8aa0", &dssdev->dev, dssdev,
+ &s6e8aa0_backlight_ops, &props);
+ if (IS_ERR(s6->bldev)) {
+ ret = PTR_ERR(s6->bldev);
+ goto err_backlight_device_register;
+ }
+
+ s6->debug_dir = debugfs_create_dir("s6e8aa0", NULL);
+ if (!s6->debug_dir) {
+ dev_err(&dssdev->dev, "failed to create debug dir\n");
+ } else {
+ debugfs_create_file("current_gamma", S_IRUGO,
+ s6->debug_dir, s6, &s6e8aa0_current_gamma_fops);
+ debugfs_create_file("gamma_correction", S_IRUGO | S_IWUSR,
+ s6->debug_dir, s6, &s6e8aa0_gamma_correction_fops);
+ }
+
+ s6->acl_enable = true;
+ s6->acl_cur = 0;
+ s6->acl_average = s6->pdata->acl_average;
+ s6->elvss_cur_i = ~0;
+
+ ret = sysfs_create_group(&s6->bldev->dev.kobj, &s6e8aa0_bl_attr_group);
+ if (ret < 0) {
+ dev_err(&dssdev->dev, "failed to add sysfs entries\n");
+ goto err_backlight_device_register;
+ }
+
+ if (cpu_is_omap44xx())
+ s6->force_update = true;
+
+ dev_dbg(&dssdev->dev, "s6e8aa0_probe\n");
+ return ret;
+
+err_backlight_device_register:
+ mutex_destroy(&s6->lock);
+ gpio_free(s6->pdata->reset_gpio);
+err:
+ kfree(s6);
+
+ return ret;
+}
+
+static void s6e8aa0_remove(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ sysfs_remove_group(&s6->bldev->dev.kobj, &s6e8aa0_bl_attr_group);
+ debugfs_remove_recursive(s6->debug_dir);
+ backlight_device_unregister(s6->bldev);
+ mutex_destroy(&s6->lock);
+ gpio_free(s6->pdata->reset_gpio);
+ kfree(s6);
+}
+
+/**
+ * s6e8aa0_config - Configure S6E8AA0
+ *
+ * Initial configuration for S6E8AA0 configuration registers, PLL...
+ */
+static void s6e8aa0_config(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ struct panel_s6e8aa0_data *pdata = s6->pdata;
+ if (!s6->brightness_table) {
+ s6e8aa0_read_id_info(s6);
+ s6e8aa0_read_mtp_info(s6, 0);
+ s6e8aa0_read_mtp_info(s6, 1);
+ s6e8aa0_adjust_brightness_from_mtp(s6);
+ }
+
+ s6e8aa0_write_sequence(dssdev, pdata->seq_display_set,
+ pdata->seq_display_set_size);
+
+ s6->acl_cur = 0; /* make sure acl table and elvss value gets written */
+ s6->elvss_cur_i = ~0;
+ s6e8aa0_update_brightness(dssdev);
+
+ s6e8aa0_write_sequence(dssdev, pdata->seq_etc_set,
+ pdata->seq_etc_set_size);
+}
+
+static int s6e8aa0_power_on(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ int ret = 0;
+
+ /* At power on the first vsync has not been received yet*/
+ dssdev->first_vsync = false;
+
+ if (s6->enabled != 1) {
+ if (s6->pdata->set_power)
+ s6->pdata->set_power(true);
+
+ ret = omapdss_dsi_display_enable(dssdev);
+ if (ret) {
+ dev_err(&dssdev->dev, "failed to enable DSI\n");
+ goto err;
+ }
+
+ /* reset s6e8aa0 bridge */
+ if(!dssdev->skip_init){
+ s6e8aa0_hw_reset(dssdev);
+
+ /* XXX */
+ msleep(100);
+ s6e8aa0_config(dssdev);
+
+ dsi_video_mode_enable(dssdev, 0x3E); /* DSI_DT_PXLSTREAM_24BPP_PACKED; */
+ }
+
+ s6->enabled = 1;
+ }
+
+ if(dssdev->skip_init)
+ dssdev->skip_init = false;
+
+err:
+ return ret;
+}
+
+static void s6e8aa0_power_off(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+
+ gpio_set_value(s6->pdata->reset_gpio, 0);
+ msleep(10);
+
+ s6->enabled = 0;
+ omapdss_dsi_display_disable(dssdev, 0, 0);
+
+ if (s6->pdata->set_power)
+ s6->pdata->set_power(false);
+
+}
+
+static int s6e8aa0_start(struct omap_dss_device *dssdev)
+{
+ int r = 0;
+ unsigned long pclk;
+
+ dsi_bus_lock(dssdev);
+
+ r = s6e8aa0_power_on(dssdev);
+
+ dsi_bus_unlock(dssdev);
+
+ if (r) {
+ dev_dbg(&dssdev->dev, "enable failed\n");
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+ } else {
+ dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
+ dssdev->manager->enable(dssdev->manager);
+ }
+
+ /* fixup pclk based on pll config */
+ pclk = dispc_pclk_rate(dssdev->channel);
+ if (pclk)
+ dssdev->panel.timings.pixel_clock = (pclk + 500) / 1000;
+
+ return r;
+}
+
+static void s6e8aa0_stop(struct omap_dss_device *dssdev)
+{
+ dssdev->manager->disable(dssdev->manager);
+
+ dsi_bus_lock(dssdev);
+
+ s6e8aa0_power_off(dssdev);
+
+ dsi_bus_unlock(dssdev);
+}
+
+static void s6e8aa0_disable(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+
+ dev_dbg(&dssdev->dev, "disable\n");
+
+ mutex_lock(&s6->lock);
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ s6e8aa0_stop(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+ mutex_unlock(&s6->lock);
+}
+
+static int s6e8aa0_enable(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ int ret;
+
+ dev_dbg(&dssdev->dev, "enable\n");
+
+ mutex_lock(&s6->lock);
+ if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = s6e8aa0_start(dssdev);
+out:
+ mutex_unlock(&s6->lock);
+ return ret;
+}
+
+static void s6e8aa0_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 int s6e8aa0_update(struct omap_dss_device *dssdev,
+ u16 x, u16 y, u16 w, u16 h)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ int r;
+ dev_dbg(&dssdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
+
+ mutex_lock(&s6->lock);
+
+ dsi_bus_lock(dssdev);
+
+ if (!s6->enabled) {
+ r = 0;
+ goto err;
+ }
+
+ r = omap_dsi_prepare_update(dssdev, &x, &y, &w, &h, true);
+ if (r)
+ goto err;
+
+ /* We use VC(0) for VideoPort Data and VC(1) for commands */
+ r = omap_dsi_update(dssdev, 0, x, y, w, h, s6e8aa0_framedone_cb, dssdev);
+ if (r)
+ goto err;
+
+ dsi_bus_unlock(dssdev);
+ /* note: no bus_unlock here. unlock is in framedone_cb */
+ mutex_unlock(&s6->lock);
+ return 0;
+err:
+ dsi_bus_unlock(dssdev);
+ mutex_unlock(&s6->lock);
+ return r;
+}
+
+static int s6e8aa0_sync(struct omap_dss_device *dssdev)
+{
+ /* TODO? */
+ return 0;
+}
+
+static int s6e8aa0_set_update_mode(struct omap_dss_device *dssdev,
+ enum omap_dss_update_mode mode)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+
+ if (s6->force_update) {
+ if (mode != OMAP_DSS_UPDATE_AUTO)
+ return -EINVAL;
+ } else {
+ if (mode != OMAP_DSS_UPDATE_MANUAL)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static enum omap_dss_update_mode s6e8aa0_get_update_mode(struct omap_dss_device
+ *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+
+ if (s6->force_update)
+ return OMAP_DSS_UPDATE_AUTO;
+ else
+ return OMAP_DSS_UPDATE_MANUAL;
+}
+
+#ifdef CONFIG_PM
+static int s6e8aa0_resume(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ int ret;
+
+ dev_dbg(&dssdev->dev, "resume\n");
+
+ mutex_lock(&s6->lock);
+ if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ ret = s6e8aa0_start(dssdev);
+out:
+ mutex_unlock(&s6->lock);
+ return ret;
+}
+
+static int s6e8aa0_suspend(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ int ret = 0;
+
+ dev_dbg(&dssdev->dev, "suspend\n");
+
+ mutex_lock(&s6->lock);
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ s6e8aa0_stop(dssdev);
+ dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+out:
+ mutex_unlock(&s6->lock);
+ return ret;
+}
+#endif
+
+static struct omap_dss_driver s6e8aa0_driver = {
+ .probe = s6e8aa0_probe,
+ .remove = s6e8aa0_remove,
+
+ .enable = s6e8aa0_enable,
+ .disable = s6e8aa0_disable,
+#ifdef CONFIG_PM
+ .suspend = s6e8aa0_suspend,
+ .resume = s6e8aa0_resume,
+#endif
+
+ .set_update_mode = s6e8aa0_set_update_mode,
+ .get_update_mode = s6e8aa0_get_update_mode,
+
+ .update = s6e8aa0_update,
+ .sync = s6e8aa0_sync,
+
+ .get_resolution = s6e8aa0_get_resolution,
+ .get_recommended_bpp = omapdss_default_get_recommended_bpp,
+
+ /* dummy entry start */
+ .enable_te = s6e8aa0_enable_te,
+ .set_rotate = s6e8aa0_rotate,
+ .get_rotate = s6e8aa0_get_rotate,
+ .set_mirror = s6e8aa0_mirror,
+ .get_mirror = s6e8aa0_get_mirror,
+ /* dummy entry end */
+
+ .get_timings = s6e8aa0_get_timings,
+ .set_timings = s6e8aa0_set_timings,
+ .check_timings = s6e8aa0_check_timings,
+
+ .driver = {
+ .name = "s6e8aa0",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init s6e8aa0_init(void)
+{
+ omap_dss_register_driver(&s6e8aa0_driver);
+ return 0;
+}
+
+static void __exit s6e8aa0_exit(void)
+{
+ omap_dss_unregister_driver(&s6e8aa0_driver);
+}
+
+module_init(s6e8aa0_init);
+module_exit(s6e8aa0_exit);
diff --git a/drivers/video/sii9234.c b/drivers/video/sii9234.c
new file mode 100644
index 0000000..04fb348
--- /dev/null
+++ b/drivers/video/sii9234.c
@@ -0,0 +1,1438 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics
+ *
+ * Authors: Adam Hampson <ahampson@sta.samsung.com>
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/sii9234.h>
+#include <linux/slab.h>
+#include <linux/wait.h>
+
+#include <linux/usb/otg_id.h>
+
+#define T_SRC_VBUS_CBUS_TO_STABLE 200
+#define T_SRC_WAKE_PULSE_WIDTH_1 19
+#define T_SRC_WAKE_PULSE_WIDTH_2 60
+#define T_SRC_WAKE_TO_DISCOVER 500
+#define T_SRC_VBUS_CBUS_T0_STABLE 500
+
+/* MHL TX Addr 0x72 Registers */
+#define MHL_TX_IDL_REG 0x02
+#define MHL_TX_IDH_REG 0x03
+#define MHL_TX_REV_REG 0x04
+#define MHL_TX_SRST 0x05
+#define MHL_TX_INTR1_REG 0x71
+#define MHL_TX_INTR2_REG 0x72 /* Not Documented */
+#define MHL_TX_INTR3_REG 0x73 /* Not Documented */
+#define MHL_TX_INTR4_REG 0x74
+#define MHL_TX_INTR1_ENABLE_REG 0x75
+#define MHL_TX_INTR2_ENABLE_REG 0x76 /* Not Documented */
+#define MHL_TX_INTR3_ENABLE_REG 0x77 /* Not Documented */
+#define MHL_TX_INTR4_ENABLE_REG 0x78
+
+#define MHL_TX_INT_CTRL_REG 0x79
+#define INTR_POLARITY (1 << 1)
+#define INTR_OPEN_DRAIN (1 << 2)
+#define HPD_OUT_OVR_EN (1 << 4)
+#define HPD_OUT_OVR_VAL (1 << 5)
+#define HPD_OUT_OPEN_DRAIN (1 << 6)
+
+#define MHL_TX_TMDS_CCTRL 0x80
+
+#define MHL_TX_DISC_CTRL1_REG 0x90
+#define MHL_TX_DISC_CTRL2_REG 0x91
+#define MHL_TX_DISC_CTRL3_REG 0x92
+#define MHL_TX_DISC_CTRL4_REG 0x93 /* Not Documented */
+
+/* There doesn't seem to be any documentation for CTRL5 but it looks like
+ * it is some sort of pull up control register
+ */
+#define MHL_TX_DISC_CTRL5_REG 0x94
+#define MHL_TX_DISC_CTRL6_REG 0x95
+#define MHL_TX_DISC_CTRL7_REG 0x96
+#define MHL_TX_DISC_CTRL8_REG 0x97 /* Not Documented */
+#define MHL_TX_STAT1_REG 0x98 /* Not Documented */
+#define MHL_TX_STAT2_REG 0x99
+
+#define MHL_TX_MHLTX_CTL1_REG 0xA0
+#define MHL_TX_MHLTX_CTL2_REG 0xA1
+#define MHL_TX_MHLTX_CTL4_REG 0xA3
+#define MHL_TX_MHLTX_CTL6_REG 0xA5
+#define MHL_TX_MHLTX_CTL7_REG 0xA6
+
+/* MHL TX SYS STAT Registers
+ * Not Documented, mentioned only in reference of RSEN
+ */
+#define MHL_TX_SYSSTAT_REG 0x09
+
+/* MHL TX SYS STAT Register Bits */
+#define RSEN_STATUS (1<<2)
+
+/* MHL TX INTR4 Register Bits */
+#define RGND_READY_INT (1<<6)
+#define VBUS_LOW_INT (1<<5)
+#define CBUS_LKOUT_INT (1<<4)
+#define MHL_DISC_FAIL_INT (1<<3)
+#define MHL_EST_INT (1<<2)
+
+/* MHL TX INTR4_ENABLE 0x78 Register Bits */
+#define RGND_READY_MASK (1<<6)
+#define CBUS_LKOUT_MASK (1<<4)
+#define MHL_DISC_FAIL_MASK (1<<3)
+#define MHL_EST_MASK (1<<2)
+
+/* MHL TX INTR1 Register Bits*/
+#define HPD_CHANGE_INT (1<<6)
+#define RSEN_CHANGE_INT (1<<5)
+
+/* MHL TX INTR1_ENABLE 0x75 Register Bits*/
+#define HPD_CHANGE_INT_MASK (1<<6)
+#define RSEN_CHANGE_INT_MASK (1<<5)
+
+#define CBUS_CONFIG_REG 0x07
+
+#define CBUS_INT_STATUS_1_REG 0x08
+#define CBUS_INT_1_MASK 0x09
+
+#define CBUS_MSC_COMMAND_START 0x12
+#define START_MSC_MSG (1 << 1)
+#define START_READ_DEVCAP (1 << 2)
+#define START_WRITE_STAT_INT (1 << 3)
+#define START_WRITE_BURST (1 << 4)
+
+#define CBUS_MSC_OFFSET_REG 0x13
+#define CBUS_MSC_FIRST_DATA_OUT 0x14
+#define CBUS_MSC_SECOND_DATA_OUT 0x15
+#define CBUS_MSC_FIRST_DATA_IN 0x16
+#define CBUS_MSC_MSG_CMD_IN 0x18
+#define CBUS_MSC_MSG_DATA_IN 0x19
+#define CBUS_INT_STATUS_2_REG 0x1E
+#define CBUS_INT_2_MASK 0x1F
+#define CBUS_LINK_CONTROL_2_REG 0x31
+
+#define CBUS_INT_STATUS_2_REG 0x1E
+
+/* MHL Interrupt Registers */
+#define CBUS_MHL_INTR_REG_0 0xA0
+
+#define CBUS_MHL_INTR_REG_1 0xA1
+#define MHL_INT_EDID_CHG (1<<1)
+
+#define CBUS_MHL_INTR_REG_2 0xA2
+#define CBUS_MHL_INTR_REG_3 0xA3
+
+/* MHL Status Registers */
+#define CBUS_MHL_STATUS_REG_0 0xB0
+#define MHL_STATUS_DCAP_READY (1<<0)
+
+#define CBUS_MHL_STATUS_REG_1 0xB1
+#define CBUS_MHL_STATUS_REG_2 0xB2
+#define CBUS_MHL_STATUS_REG_3 0xB3
+
+/* CBUS INTR1 STATUS Register bits */
+#define MSC_RESP_ABORT (1<<6)
+#define MSC_REQ_ABORT (1<<5)
+#define MSC_REQ_DONE (1<<4)
+#define MSC_MSG_RECD (1<<3)
+#define CBUS_DDC_ABORT (1<<2)
+
+/* CBUS INTR1 STATUS 0x09 Enable Mask*/
+#define MSC_RESP_ABORT_MASK (1<<6)
+#define MSC_REQ_ABORT_MASK (1<<5)
+#define MSC_REQ_DONE_MASK (1<<4)
+#define MSC_MSG_RECD_MASK (1<<3)
+#define CBUS_DDC_ABORT_MASK (1<<2)
+
+/* CBUS INTR2 STATUS Register bits */
+#define WRT_STAT_RECD (1<<3)
+#define SET_INT_RECD (1<<2)
+#define WRT_BURST_RECD (1<<0)
+
+/* CBUS INTR2 STATUS 0x1F Enable Mask*/
+#define WRT_STAT_RECD_MASK (1<<3)
+#define SET_INT_RECD_MASK (1<<2)
+#define WRT_BURST_RECD_MASK (1<<0)
+
+/* CBUS Control Registers*/
+/* Retry count for all MSC commands*/
+#define MSC_RETRY_FAIL_LIM_REG 0x1D
+
+/* reason for MSC_REQ_ABORT interrupt on CBUS */
+#define MSC_REQ_ABORT_REASON_REG 0x0D
+
+#define MSC_RESP_ABORT_REASON_REG 0x0E
+
+/* MSC Requestor Abort Reason Register bits*/
+#define ABORT_BY_PEER (1<<7)
+#define UNDEF_CMD (1<<3)
+#define TIMEOUT (1<<2)
+#define PROTO_ERROR (1<<1)
+#define MAX_FAIL (1<<0)
+
+/* MSC Responder Abort Reason Register bits*/
+#define ABORT_BY_PEER (1<<7)
+#define UNDEF_CMD (1<<3)
+#define TIMEOUT (1<<2)
+
+/* Set HPD came from Downstream, not documented */
+#define SET_HPD_DOWNSTREAM (1<<6)
+
+/* MHL TX DISC1 Register Bits */
+#define DISC_EN (1<<0)
+
+/* MHL TX DISC2 Register Bits */
+#define SKIP_GND (1<<6)
+#define ATT_THRESH_SHIFT 0x04
+#define ATT_THRESH_MASK (0x03 << ATT_THRESH_SHIFT)
+#define USB_D_OEN (1<<3)
+#define DEGLITCH_TIME_MASK 0x07
+#define DEGLITCH_TIME_2MS 0
+#define DEGLITCH_TIME_4MS 1
+#define DEGLITCH_TIME_8MS 2
+#define DEGLITCH_TIME_16MS 3
+#define DEGLITCH_TIME_40MS 4
+#define DEGLITCH_TIME_50MS 5
+#define DEGLITCH_TIME_60MS 6
+#define DEGLITCH_TIME_128MS 7
+
+#define DISC_CTRL3_COMM_IMME (1<<7)
+#define DISC_CTRL3_FORCE_MHL (1<<6)
+#define DISC_CTRL3_FORCE_USB (1<<4)
+#define DISC_CTRL3_USB_EN (1<<3)
+
+/* MHL TX DISC4 0x93 Register Bits: undocumented */
+#define CBUS_DISC_PUP_SEL_SHIFT 6
+#define CBUS_DISC_PUP_SEL_MASK (3<<CBUS_DISC_PUP_SEL_SHIFT)
+#define CBUS_DISC_PUP_SEL_10K (2<<CBUS_DISC_PUP_SEL_SHIFT)
+#define CBUS_DISC_PUP_SEL_OPEN (0<<CBUS_DISC_PUP_SEL_SHIFT)
+#define CBUS_IDLE_PUP_SEL_SHIFT 4
+#define CBUS_IDLE_PUP_SEL_MASK (3<<CBUS_IDLE_PUP_SEL_SHIFT)
+#define CBUS_IDLE_PUP_SEL_OPEN (0<<CBUS_IDLE_PUP_SEL_SHIFT)
+
+/* MHL TX DISC5 0x94 Register Bits */
+#define CBUS_MHL_PUP_SEL_MASK 0x03 /* Not Documented */
+#define CBUS_MHL_PUP_SEL_5K 0x01 /* Not Documented */
+#define CBUS_MHL_PUP_SEL_OPEN 0x00
+
+/* MHL TX DISC6 0x95 Register Bits */
+#define USB_D_OVR (1<<7)
+#define USB_ID_OVR (1<<6)
+#define DVRFLT_SEL (1<<5)
+#define BLOCK_RGND_INT (1<<4)
+#define SKIP_DEG (1<<3)
+#define CI2CA_POL (1<<2)
+#define CI2CA_WKUP (1<<1)
+#define SINGLE_ATT (1<<0)
+
+/* MHL TX DISC7 0x96 Register Bits
+ *
+ * Bits 7 and 6 are labeled as reserved but seem to be related to toggling
+ * the CBUS signal when generating the wake pulse sequence.
+ */
+#define USB_D_ODN (1<<5)
+#define VBUS_CHECK (1<<2)
+#define RGND_INTP_MASK 0x03
+#define RGND_INTP_OPEN 0
+#define RGND_INTP_2K 1
+#define RGND_INTP_1K 2
+#define RGND_INTP_SHORT 3
+
+/* TPI Addr 0x7A Registers */
+#define TPI_DPD_REG 0x3D
+
+#define TPI_PD_TMDS (1<<5)
+#define TPI_PD_OSC_EN (1<<4)
+#define TPI_TCLK_PHASE (1<<3)
+#define TPI_PD_IDCK (1<<2)
+#define TPI_PD_OSC (1<<1)
+#define TPI_PD (1<<0)
+
+
+
+/* HDMI RX Registers */
+#define HDMI_RX_TMDS0_CCTRL1_REG 0x10
+#define HDMI_RX_TMDS_CLK_EN_REG 0x11
+#define HDMI_RX_TMDS_CH_EN_REG 0x12
+#define HDMI_RX_PLL_CALREFSEL_REG 0x17
+#define HDMI_RX_PLL_VCOCAL_REG 0x1A
+#define HDMI_RX_EQ_DATA0_REG 0x22
+#define HDMI_RX_EQ_DATA1_REG 0x23
+#define HDMI_RX_EQ_DATA2_REG 0x24
+#define HDMI_RX_EQ_DATA3_REG 0x25
+#define HDMI_RX_EQ_DATA4_REG 0x26
+#define HDMI_RX_TMDS_ZONE_CTRL_REG 0x4C
+#define HDMI_RX_TMDS_MODE_CTRL_REG 0x4D
+
+enum rgnd_state {
+ RGND_UNKNOWN = 0,
+ RGND_OPEN,
+ RGND_1K,
+ RGND_2K,
+ RGND_SHORT
+};
+
+enum mhl_state {
+ STATE_DISCONNECTED = 0,
+ STATE_DISCOVERY_FAILED,
+ STATE_CBUS_LOCKOUT,
+ STATE_ESTABLISHED,
+};
+
+static inline bool mhl_state_is_error(enum mhl_state state)
+{
+ return state == STATE_DISCOVERY_FAILED ||
+ state == STATE_CBUS_LOCKOUT;
+}
+
+struct sii9234_data {
+ struct sii9234_platform_data *pdata;
+ struct otg_id_notifier_block otg_id_nb;
+ wait_queue_head_t wq;
+
+ bool claimed;
+ enum mhl_state state;
+ enum rgnd_state rgnd;
+ int irq;
+ bool rsen;
+
+ struct mutex lock;
+
+ bool msc_ready;
+ struct mutex msc_lock;
+ struct completion msc_complete;
+
+ u8 devcap[16];
+};
+
+static irqreturn_t sii9234_irq_thread(int irq, void *data);
+
+static int mhl_tx_write_reg(struct sii9234_data *sii9234, unsigned int offset,
+ u8 value)
+{
+ return i2c_smbus_write_byte_data(sii9234->pdata->mhl_tx_client, offset,
+ value);
+}
+
+static int mhl_tx_read_reg(struct sii9234_data *sii9234, unsigned int offset,
+ u8 *value)
+{
+ int ret;
+
+ if (!value)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte(sii9234->pdata->mhl_tx_client, offset);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte(sii9234->pdata->mhl_tx_client);
+ if (ret < 0)
+ return ret;
+
+ *value = ret & 0x000000FF;
+
+ return 0;
+}
+
+static int mhl_tx_set_reg(struct sii9234_data *sii9234, unsigned int offset,
+ u8 mask)
+{
+ int ret;
+ u8 value;
+
+ ret = mhl_tx_read_reg(sii9234, offset, &value);
+ if (ret < 0)
+ return ret;
+
+ value |= mask;
+
+ return mhl_tx_write_reg(sii9234, offset, value);
+}
+
+static int mhl_tx_clear_reg(struct sii9234_data *sii9234, unsigned int offset,
+ u8 mask)
+{
+ int ret;
+ u8 value;
+
+ ret = mhl_tx_read_reg(sii9234, offset, &value);
+ if (ret < 0)
+ return ret;
+
+ value &= ~mask;
+
+ return mhl_tx_write_reg(sii9234, offset, value);
+}
+
+static int tpi_write_reg(struct sii9234_data *sii9234, unsigned int offset,
+ u8 value)
+{
+ return i2c_smbus_write_byte_data(sii9234->pdata->tpi_client, offset,
+ value);
+}
+
+static int tpi_read_reg(struct sii9234_data *sii9234, unsigned int offset,
+ u8 *value)
+{
+ int ret;
+
+ if (!value)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte(sii9234->pdata->tpi_client, offset);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte(sii9234->pdata->tpi_client);
+ if (ret < 0)
+ return ret;
+
+ *value = ret & 0x000000FF;
+
+ return 0;
+}
+
+static int hdmi_rx_write_reg(struct sii9234_data *sii9234, unsigned int offset,
+ u8 value)
+{
+ return i2c_smbus_write_byte_data(sii9234->pdata->hdmi_rx_client, offset,
+ value);
+}
+
+static int cbus_write_reg(struct sii9234_data *sii9234, unsigned int offset,
+ u8 value)
+{
+ return i2c_smbus_write_byte_data(sii9234->pdata->cbus_client, offset,
+ value);
+}
+
+static int cbus_read_reg(struct sii9234_data *sii9234, unsigned int offset,
+ u8 *value)
+{
+ int ret;
+
+ if (!value)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte(sii9234->pdata->cbus_client, offset);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_byte(sii9234->pdata->cbus_client);
+ if (ret < 0)
+ return ret;
+
+ *value = ret & 0x000000FF;
+
+ return 0;
+}
+
+static int cbus_set_reg(struct sii9234_data *sii9234, unsigned int offset,
+ u8 mask)
+{
+ int ret;
+ u8 value;
+
+ ret = cbus_read_reg(sii9234, offset, &value);
+ if (ret < 0)
+ return ret;
+
+ value |= mask;
+
+ return cbus_write_reg(sii9234, offset, value);
+}
+
+static int mhl_wake_toggle(struct sii9234_data *sii9234,
+ unsigned long high_period,
+ unsigned long low_period)
+{
+ int ret;
+
+ /* These bits are not documented. */
+ ret = mhl_tx_set_reg(sii9234, MHL_TX_DISC_CTRL7_REG, (1<<7) | (1<<6));
+ if (ret < 0)
+ return ret;
+
+ usleep_range(high_period * USEC_PER_MSEC, high_period * USEC_PER_MSEC);
+
+ ret = mhl_tx_clear_reg(sii9234, MHL_TX_DISC_CTRL7_REG, (1<<7) | (1<<6));
+ if (ret < 0)
+ return ret;
+
+ usleep_range(low_period * USEC_PER_MSEC, low_period * USEC_PER_MSEC);
+
+ return 0;
+}
+
+static int mhl_send_wake_pulses(struct sii9234_data *sii9234)
+{
+ int ret;
+
+ ret = mhl_wake_toggle(sii9234, T_SRC_WAKE_PULSE_WIDTH_1,
+ T_SRC_WAKE_PULSE_WIDTH_1);
+ if (ret < 0)
+ return ret;
+
+ ret = mhl_wake_toggle(sii9234, T_SRC_WAKE_PULSE_WIDTH_1,
+ T_SRC_WAKE_PULSE_WIDTH_2);
+ if (ret < 0)
+ return ret;
+
+ ret = mhl_wake_toggle(sii9234, T_SRC_WAKE_PULSE_WIDTH_1,
+ T_SRC_WAKE_PULSE_WIDTH_1);
+ if (ret < 0)
+ return ret;
+
+ ret = mhl_wake_toggle(sii9234, T_SRC_WAKE_PULSE_WIDTH_1,
+ T_SRC_WAKE_TO_DISCOVER);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+static int sii9234_cbus_reset(struct sii9234_data *sii9234)
+{
+ int ret;
+ /* Reset CBUS */
+ ret = mhl_tx_set_reg(sii9234, MHL_TX_SRST, 0x03);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(2000, 3000);
+
+ ret = mhl_tx_clear_reg(sii9234, MHL_TX_SRST, 0x03);
+ if (ret < 0)
+ return ret;
+
+ /* Adjust interrupt mask everytime reset is performed.*/
+ ret = cbus_write_reg(sii9234,
+ CBUS_INT_1_MASK,
+ MSC_RESP_ABORT_MASK |
+ MSC_REQ_ABORT_MASK |
+ MSC_REQ_DONE_MASK |
+ MSC_MSG_RECD_MASK |
+ CBUS_DDC_ABORT_MASK);
+ if (ret < 0)
+ return ret;
+
+ ret = cbus_write_reg(sii9234,
+ CBUS_INT_2_MASK,
+ WRT_STAT_RECD_MASK |
+ SET_INT_RECD_MASK);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int sii9234_cbus_init(struct sii9234_data *sii9234)
+{
+ u8 value;
+
+ cbus_write_reg(sii9234, 0x07, 0x32);
+ cbus_write_reg(sii9234, 0x40, 0x03);
+ cbus_write_reg(sii9234, 0x42, 0x06);
+ cbus_write_reg(sii9234, 0x36, 0x0C);
+
+ cbus_write_reg(sii9234, 0x3D, 0xFD);
+ cbus_write_reg(sii9234, 0x1C, 0x00);
+
+ cbus_write_reg(sii9234, 0x44, 0x02);
+
+ /* Setup our devcap*/
+ cbus_write_reg(sii9234, 0x80, 0x04);
+ cbus_write_reg(sii9234, 0x81, 0x10);
+ cbus_write_reg(sii9234, 0x82, 0x02);
+ cbus_write_reg(sii9234, 0x83, 0);
+ cbus_write_reg(sii9234, 0x84, 0);
+ cbus_write_reg(sii9234, 0x85, 0x01 | 0x02);
+ cbus_write_reg(sii9234, 0x86, 0x01);
+ cbus_write_reg(sii9234, 0x87, 0);
+ cbus_write_reg(sii9234, 0x88, (1<<2) | (1<<1) | (1<<3) | (1<<7));
+ cbus_write_reg(sii9234, 0x89, 0x0F);
+ cbus_write_reg(sii9234, 0x8A, (1<<0) | (1<<1) | (1<<2));
+ cbus_write_reg(sii9234, 0x8B, 0);
+ cbus_write_reg(sii9234, 0x8C, 0);
+ cbus_write_reg(sii9234, 0x8D, 16);
+ cbus_write_reg(sii9234, 0x8E, 0x44);
+ cbus_write_reg(sii9234, 0x8F, 0);
+
+ cbus_read_reg(sii9234, 0x31, &value);
+ value |= 0x0C;
+ cbus_write_reg(sii9234, 0x31, value);
+
+ cbus_read_reg(sii9234, 0x22, &value);
+ value &= 0x0F;
+ cbus_write_reg(sii9234, 0x22, value);
+
+ cbus_write_reg(sii9234, 0x30, 0x01);
+
+ return 0;
+}
+
+static int sii9234_power_init(struct sii9234_data *sii9234)
+{
+ int ret;
+
+ /* Force the SiI9234 into the D0 state. */
+ ret = tpi_write_reg(sii9234, TPI_DPD_REG, 0x3F);
+ if (ret < 0)
+ return ret;
+
+ /* Enable TxPLL Clock */
+ ret = hdmi_rx_write_reg(sii9234, HDMI_RX_TMDS_CLK_EN_REG, 0x01);
+ if (ret < 0)
+ return ret;
+
+ /* Enable Tx Clock Path & Equalizer*/
+ ret = hdmi_rx_write_reg(sii9234, HDMI_RX_TMDS_CH_EN_REG, 0x15);
+ if (ret < 0)
+ return ret;
+
+ /* Power Up TMDS*/
+ ret = mhl_tx_write_reg(sii9234, 0x08, 0x35);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void sii9234_hdmi_init(struct sii9234_data *sii9234)
+{
+ /* Analog PLL Control
+ * bits 5:4 = 2b00 as per characterization team.
+ */
+ hdmi_rx_write_reg(sii9234, HDMI_RX_TMDS0_CCTRL1_REG, 0xC1);
+
+ /* PLL Calrefsel */
+ hdmi_rx_write_reg(sii9234, HDMI_RX_PLL_CALREFSEL_REG, 0x03);
+
+ /* VCO Cal */
+ hdmi_rx_write_reg(sii9234, HDMI_RX_PLL_VCOCAL_REG, 0x20);
+
+ /* Auto EQ */
+ hdmi_rx_write_reg(sii9234, HDMI_RX_EQ_DATA0_REG, 0x8A);
+
+ /* Auto EQ */
+ hdmi_rx_write_reg(sii9234, HDMI_RX_EQ_DATA1_REG, 0x6A);
+
+ /* Auto EQ */
+ hdmi_rx_write_reg(sii9234, HDMI_RX_EQ_DATA2_REG, 0xAA);
+
+ /* Auto EQ */
+ hdmi_rx_write_reg(sii9234, HDMI_RX_EQ_DATA3_REG, 0xCA);
+
+ /* Auto EQ */
+ hdmi_rx_write_reg(sii9234, HDMI_RX_EQ_DATA4_REG, 0xEA);
+
+ /* Manual zone */
+ hdmi_rx_write_reg(sii9234, HDMI_RX_TMDS_ZONE_CTRL_REG, 0xA0);
+
+ /* PLL Mode Value */
+ hdmi_rx_write_reg(sii9234, HDMI_RX_TMDS_MODE_CTRL_REG, 0x00);
+
+ mhl_tx_write_reg(sii9234, MHL_TX_TMDS_CCTRL, 0x34);
+
+ hdmi_rx_write_reg(sii9234, 0x45, 0x44);
+
+ /* Rx PLL BW ~ 4MHz */
+ hdmi_rx_write_reg(sii9234, 0x31, 0x0A);
+
+ /* Analog PLL Control
+ * bits 5:4 = 2b00 as per characterization team.
+ */
+ hdmi_rx_write_reg(sii9234, HDMI_RX_TMDS0_CCTRL1_REG, 0xC1);
+}
+
+static void sii9234_mhl_tx_ctl_int(struct sii9234_data *sii9234)
+{
+ mhl_tx_write_reg(sii9234, MHL_TX_MHLTX_CTL1_REG, 0xD0);
+ mhl_tx_write_reg(sii9234, MHL_TX_MHLTX_CTL2_REG, 0xFC);
+ mhl_tx_write_reg(sii9234, MHL_TX_MHLTX_CTL4_REG, 0xEB);
+ mhl_tx_write_reg(sii9234, MHL_TX_MHLTX_CTL7_REG, 0x0C);
+}
+
+static void sii9234_power_down(struct sii9234_data *sii9234)
+{
+ if (sii9234->claimed)
+ sii9234->pdata->connect(false, NULL);
+
+ sii9234->state = STATE_DISCONNECTED;
+ sii9234->claimed = false;
+
+ tpi_write_reg(sii9234, TPI_DPD_REG, 0);
+
+ sii9234->pdata->power(0);
+ sii9234->pdata->enable(0);
+}
+
+/* toggle hpd line low for 100ms */
+static void sii9234_toggle_hpd(struct sii9234_data *sii9234)
+{
+ mhl_tx_set_reg(sii9234, MHL_TX_INT_CTRL_REG, HPD_OUT_OVR_EN);
+ mhl_tx_clear_reg(sii9234, MHL_TX_INT_CTRL_REG, HPD_OUT_OVR_VAL);
+ msleep(100);
+ mhl_tx_set_reg(sii9234, MHL_TX_INT_CTRL_REG, HPD_OUT_OVR_VAL);
+ mhl_tx_clear_reg(sii9234, MHL_TX_INT_CTRL_REG, HPD_OUT_OVR_EN);
+}
+
+/* Must call with sii9234->lock held */
+static int sii9234_msc_req_locked(struct sii9234_data *sii9234, u8 req_type,
+ u8 offset)
+{
+ int ret;
+
+ if (sii9234->state != STATE_ESTABLISHED)
+ return -ENOENT;
+
+ mutex_unlock(&sii9234->lock);
+ ret = wait_event_timeout(sii9234->wq, sii9234->msc_ready,
+ msecs_to_jiffies(2000));
+ mutex_lock(&sii9234->lock);
+ if (!sii9234->msc_ready)
+ return -EIO;
+
+ mutex_lock(&sii9234->msc_lock);
+
+ init_completion(&sii9234->msc_complete);
+
+ cbus_write_reg(sii9234, CBUS_MSC_OFFSET_REG, offset);
+ cbus_write_reg(sii9234, CBUS_MSC_COMMAND_START, req_type);
+
+ mutex_unlock(&sii9234->lock);
+ ret = wait_for_completion_timeout(&sii9234->msc_complete,
+ msecs_to_jiffies(500));
+ mutex_lock(&sii9234->lock);
+
+ mutex_unlock(&sii9234->msc_lock);
+
+ return ret ? 0 : -EIO;
+}
+
+/* Must call with sii9234->lock held */
+static int sii9234_devcap_read_locked(struct sii9234_data *sii9234, u8 offset)
+{
+ int ret;
+ u8 val;
+
+ if (offset > 0xf)
+ return -EINVAL;
+
+ ret = sii9234_msc_req_locked(sii9234, START_READ_DEVCAP, offset);
+ if (ret < 0)
+ return ret;
+
+ ret = cbus_read_reg(sii9234, CBUS_MSC_FIRST_DATA_IN, &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+static int sii9234_detection_callback(struct otg_id_notifier_block *nb)
+{
+ struct sii9234_data *sii9234 = container_of(nb, struct sii9234_data,
+ otg_id_nb);
+ int ret;
+ int i;
+ u8 value;
+ int handled = OTG_ID_UNHANDLED;
+
+ pr_debug("si9234: detection started\n");
+
+ mutex_lock(&sii9234->lock);
+ sii9234->rgnd = RGND_UNKNOWN;
+ sii9234->state = STATE_DISCONNECTED;
+ sii9234->rsen = false;
+ sii9234->msc_ready = false;
+
+ /* Set the board configuration so the SiI9234 has access to the
+ * external connector.
+ */
+ sii9234->pdata->enable(1);
+ sii9234->pdata->power(1);
+
+ ret = sii9234_power_init(sii9234);
+ if (ret < 0)
+ goto unhandled;
+
+ sii9234_hdmi_init(sii9234);
+
+ sii9234_mhl_tx_ctl_int(sii9234);
+
+ /* Enable HDCP Compliance safety*/
+ ret = mhl_tx_write_reg(sii9234, 0x2B, 0x01);
+ if (ret < 0)
+ goto unhandled;
+
+ /* CBUS discovery cycle time for each drive and float = 150us*/
+ ret = mhl_tx_read_reg(sii9234, MHL_TX_DISC_CTRL1_REG, &value);
+ if (ret < 0)
+ goto unhandled;
+
+ value &= ~(1<<2);
+ value |= (1<<3);
+
+ ret = mhl_tx_write_reg(sii9234, MHL_TX_DISC_CTRL1_REG, value);
+ if (ret < 0)
+ goto unhandled;
+
+ /* Clear bit 6 (reg_skip_rgnd) */
+ ret = mhl_tx_write_reg(sii9234, MHL_TX_DISC_CTRL2_REG,
+ (1<<7) /* Reserved Bit */ |
+ 2 << ATT_THRESH_SHIFT |
+ DEGLITCH_TIME_128MS);
+ if (ret < 0)
+ goto unhandled;
+
+ /* Changed from 66 to 65 for 94[1:0] = 01 = 5k reg_cbusmhl_pup_sel */
+ /* 1.8V CBUS VTH & GND threshold */
+ ret = mhl_tx_write_reg(sii9234, MHL_TX_DISC_CTRL5_REG, 0x75);
+ if (ret < 0)
+ goto unhandled;
+
+ /* set bit 2 and 3, which is Initiator Timeout */
+ ret = cbus_read_reg(sii9234, CBUS_LINK_CONTROL_2_REG, &value);
+ if (ret < 0)
+ goto unhandled;
+
+ value |= 0x0C;
+
+ ret = cbus_write_reg(sii9234, CBUS_LINK_CONTROL_2_REG, value);
+ if (ret < 0)
+ goto unhandled;
+
+ ret = mhl_tx_write_reg(sii9234, MHL_TX_MHLTX_CTL6_REG, 0xA0);
+ if (ret < 0)
+ goto unhandled;
+
+ /* RGND & single discovery attempt (RGND blocking) */
+ ret = mhl_tx_write_reg(sii9234, MHL_TX_DISC_CTRL6_REG, BLOCK_RGND_INT |
+ DVRFLT_SEL | SINGLE_ATT);
+ if (ret < 0)
+ goto unhandled;
+
+ /* Use VBUS path of discovery state machine*/
+ ret = mhl_tx_write_reg(sii9234, MHL_TX_DISC_CTRL8_REG, 0);
+ if (ret < 0)
+ goto unhandled;
+
+ ret = mhl_tx_set_reg(sii9234, MHL_TX_DISC_CTRL6_REG, USB_ID_OVR);
+ if (ret < 0)
+ goto unhandled;
+
+ /* To allow RGND engine to operate correctly.
+ * When moving the chip from D2 to D0 (power up, init regs)
+ * the values should be
+ * 94[1:0] = 01 reg_cbusmhl_pup_sel[1:0] should be set for 5k
+ * 93[7:6] = 10 reg_cbusdisc_pup_sel[1:0] should be
+ * set for 10k (default)
+ * 93[5:4] = 00 reg_cbusidle_pup_sel[1:0] = open (default)
+ */
+ ret = mhl_tx_set_reg(sii9234, MHL_TX_DISC_CTRL3_REG, 0x86);
+ if (ret < 0)
+ goto unhandled;
+
+ /* change from CC to 8C to match 5K*/
+ ret = mhl_tx_set_reg(sii9234, MHL_TX_DISC_CTRL4_REG, 0x8C);
+ if (ret < 0)
+ goto unhandled;
+
+ /* Configure the interrupt as active high */
+ ret = mhl_tx_clear_reg(sii9234, MHL_TX_INT_CTRL_REG, (1<<2) | (1<<1));
+ if (ret < 0)
+ goto unhandled;
+
+ msleep(25);
+
+ ret = mhl_tx_clear_reg(sii9234, MHL_TX_DISC_CTRL6_REG, USB_ID_OVR);
+ if (ret < 0)
+ goto unhandled;
+
+ ret = mhl_tx_write_reg(sii9234, MHL_TX_DISC_CTRL1_REG, 0x27);
+ if (ret < 0)
+ goto unhandled;
+
+ /* Reset CBUS */
+ ret = sii9234_cbus_reset(sii9234);
+ if (ret < 0)
+ goto unhandled;
+
+ sii9234_cbus_init(sii9234);
+
+ /* Enable Auto soft reset on SCDT = 0*/
+ ret = mhl_tx_write_reg(sii9234, 0x05, 0x04);
+
+ if (ret < 0)
+ goto unhandled;
+
+ /* HDMI Transcode mode enable*/
+ ret = mhl_tx_write_reg(sii9234, 0x0D, 0x1C);
+ if (ret < 0)
+ goto unhandled;
+
+ ret = mhl_tx_write_reg(sii9234, MHL_TX_INTR4_ENABLE_REG,
+ RGND_READY_MASK | CBUS_LKOUT_MASK |
+ MHL_DISC_FAIL_MASK | MHL_EST_MASK);
+ if (ret < 0)
+ goto unhandled;
+
+ ret = mhl_tx_write_reg(sii9234, MHL_TX_INTR1_ENABLE_REG,
+ (1<<5) | (1<<6));
+ if (ret < 0)
+ goto unhandled;
+
+
+ pr_debug("sii9234: waiting for RGND measurement\n");
+ enable_irq(sii9234->irq);
+
+ /* SiI9244 Programmer's Reference Section 2.4.3
+ * State : RGND Ready
+ */
+ mutex_unlock(&sii9234->lock);
+ ret = wait_event_timeout(sii9234->wq,
+ ((sii9234->rgnd != RGND_UNKNOWN) ||
+ mhl_state_is_error(sii9234->state)),
+ msecs_to_jiffies(2000));
+
+ mutex_lock(&sii9234->lock);
+ if (sii9234->rgnd == RGND_UNKNOWN || mhl_state_is_error(sii9234->state))
+ goto unhandled;
+
+ if (sii9234->rgnd != RGND_1K)
+ goto unhandled;
+
+ mutex_unlock(&sii9234->lock);
+
+ pr_debug("sii9234: waiting for detection\n");
+ ret = wait_event_timeout(sii9234->wq,
+ sii9234->state != STATE_DISCONNECTED,
+ msecs_to_jiffies(500));
+ mutex_lock(&sii9234->lock);
+ if (sii9234->state == STATE_DISCONNECTED)
+ goto unhandled;
+
+ if (sii9234->state == STATE_DISCOVERY_FAILED) {
+ handled = OTG_ID_PROXY_WAIT;
+ goto unhandled;
+ }
+
+ if (mhl_state_is_error(sii9234->state))
+ goto unhandled;
+
+ mutex_unlock(&sii9234->lock);
+ wait_event_timeout(sii9234->wq, sii9234->rsen, msecs_to_jiffies(400));
+ mutex_lock(&sii9234->lock);
+ if (!sii9234->rsen)
+ goto unhandled;
+
+ memset(sii9234->devcap, 0x0, sizeof(sii9234->devcap));
+ for (i = 0; i < 16; i++) {
+ ret = sii9234_devcap_read_locked(sii9234, i);
+ if (ret < 0)
+ break;
+ sii9234->devcap[i] = ret;
+ }
+
+#ifdef DEBUG
+ if (ret >= 0)
+ print_hex_dump(KERN_DEBUG, "sii9234: devcap = ", DUMP_PREFIX_NONE,
+ 16, 1, sii9234->devcap, 16, false);
+#endif
+
+ /* It's possible for devcap reading to fail but the adapter still
+ * be connected. Therefore we must keep ownership of the port
+ * as long as it's still connected.
+ */
+ if (sii9234->state != STATE_ESTABLISHED)
+ goto unhandled;
+
+ pr_info("si9234: connection established\n");
+
+ sii9234->claimed = true;
+ sii9234->pdata->connect(true, ret >= 0 ? sii9234->devcap : NULL);
+ mutex_unlock(&sii9234->lock);
+
+ return OTG_ID_HANDLED;
+
+unhandled:
+ pr_info("sii9234: Detection failed");
+ if (sii9234->state == STATE_DISCONNECTED)
+ pr_cont(" (timeout)");
+ else if (sii9234->state == STATE_DISCOVERY_FAILED)
+ pr_cont(" (discovery failed)");
+ else if (sii9234->state == STATE_CBUS_LOCKOUT)
+ pr_cont(" (cbus_lockout)");
+ pr_cont("\n");
+
+ disable_irq_nosync(sii9234->irq);
+
+ sii9234_power_down(sii9234);
+
+ mutex_unlock(&sii9234->lock);
+ return handled;
+}
+
+static void sii9234_cancel_callback(struct otg_id_notifier_block *nb)
+{
+ struct sii9234_data *sii9234 = container_of(nb, struct sii9234_data,
+ otg_id_nb);
+
+ mutex_lock(&sii9234->lock);
+ sii9234_power_down(sii9234);
+ mutex_unlock(&sii9234->lock);
+}
+
+static int sii9234_cbus_irq(struct sii9234_data *sii9234)
+{
+ u8 cbus_intr1, cbus_intr2;
+ u8 mhl_intr0, mhl_intr1;
+ u8 mhl_status0, mhl_status1, mhl_status2, mhl_status3;
+
+ int ret = 0;
+
+ cbus_read_reg(sii9234, CBUS_INT_STATUS_1_REG, &cbus_intr1);
+ cbus_read_reg(sii9234, CBUS_INT_STATUS_2_REG, &cbus_intr2);
+ cbus_read_reg(sii9234, CBUS_MHL_INTR_REG_0, &mhl_intr0);
+ cbus_read_reg(sii9234, CBUS_MHL_INTR_REG_1, &mhl_intr1);
+ cbus_read_reg(sii9234, CBUS_MHL_STATUS_REG_0, &mhl_status0);
+ cbus_read_reg(sii9234, CBUS_MHL_STATUS_REG_1, &mhl_status1);
+ cbus_read_reg(sii9234, CBUS_MHL_STATUS_REG_2, &mhl_status2);
+ cbus_read_reg(sii9234, CBUS_MHL_STATUS_REG_3, &mhl_status3);
+
+ pr_debug("sii9234: cbus_intr %02x %02x\n", cbus_intr1, cbus_intr2);
+
+ if (cbus_intr1 & MSC_RESP_ABORT)
+ pr_warn("sii9234: msc resp abort\n");
+
+ if (cbus_intr1 & MSC_REQ_ABORT)
+ pr_warn("sii9234: msc req abort\n");
+
+ if (cbus_intr1 & CBUS_DDC_ABORT)
+ pr_warn("sii9234: ddc abort\n");
+
+ if (cbus_intr1 & MSC_REQ_DONE) {
+ pr_debug("sii9234: msc request done\n");
+ complete(&sii9234->msc_complete);
+ }
+
+ if (cbus_intr1 & MSC_MSG_RECD)
+ pr_debug("sii9234: msc msg received\n");
+
+
+ if (cbus_intr2 & WRT_STAT_RECD) {
+ pr_debug("sii9234: write stat received\n");
+ sii9234->msc_ready = mhl_status0 & MHL_STATUS_DCAP_READY;
+ }
+
+ if (cbus_intr2 & SET_INT_RECD) {
+ if (mhl_intr1 & MHL_INT_EDID_CHG)
+ sii9234_toggle_hpd(sii9234);
+ }
+
+ if (cbus_intr2 & WRT_BURST_RECD)
+ pr_debug("sii9234: write burst received\n");
+
+ cbus_write_reg(sii9234, CBUS_MHL_INTR_REG_0, mhl_intr0);
+ cbus_write_reg(sii9234, CBUS_MHL_INTR_REG_1, mhl_intr1);
+ cbus_write_reg(sii9234, CBUS_INT_STATUS_1_REG, cbus_intr1);
+ cbus_write_reg(sii9234, CBUS_INT_STATUS_2_REG, cbus_intr2);
+
+ return ret;
+}
+
+static irqreturn_t sii9234_irq_thread(int irq, void *data)
+{
+ struct sii9234_data *sii9234 = data;
+ int ret;
+ u8 intr1, intr4, value;
+ u8 intr1_en, intr4_en;
+ bool release_otg = false;
+
+ mutex_lock(&sii9234->lock);
+ mhl_tx_read_reg(sii9234, MHL_TX_INTR1_REG, &intr1);
+ mhl_tx_read_reg(sii9234, MHL_TX_INTR4_REG, &intr4);
+
+ mhl_tx_read_reg(sii9234, MHL_TX_INTR1_ENABLE_REG, &intr1_en);
+ mhl_tx_read_reg(sii9234, MHL_TX_INTR4_ENABLE_REG, &intr4_en);
+ pr_debug("sii9234: irq %02x/%02x %02x/%02x\n", intr1, intr1_en,
+ intr4, intr4_en);
+
+ if (intr4 & RGND_READY_INT) {
+ ret = mhl_tx_read_reg(sii9234, MHL_TX_STAT2_REG, &value);
+ if (ret < 0) {
+ dev_err(&sii9234->pdata->mhl_tx_client->dev,
+ "STAT2 reg, err %d\n", ret);
+ goto err_exit;
+ }
+
+ switch (value & RGND_INTP_MASK) {
+ case RGND_INTP_OPEN:
+ pr_debug("RGND Open\n");
+ sii9234->rgnd = RGND_OPEN;
+ break;
+ case RGND_INTP_1K:
+ pr_debug("RGND 1K\n");
+ ret = mhl_tx_write_reg(sii9234, MHL_TX_DISC_CTRL1_REG,
+ 0x25);
+
+ ret = mhl_send_wake_pulses(sii9234);
+ sii9234->rgnd = RGND_1K;
+ break;
+ case RGND_INTP_2K:
+ pr_debug("RGND 2K\n");
+ ret = mhl_send_wake_pulses(sii9234);
+ sii9234->rgnd = RGND_2K;
+ break;
+ case RGND_INTP_SHORT:
+ pr_debug("RGND Short\n");
+ sii9234->rgnd = RGND_SHORT;
+ break;
+ };
+ }
+
+ if (intr4 & CBUS_LKOUT_INT) {
+ pr_debug("sii9234: CBUS Lockout Interrupt\n");
+ sii9234->state = STATE_CBUS_LOCKOUT;
+ }
+
+ if (intr4 & MHL_DISC_FAIL_INT)
+ sii9234->state = STATE_DISCOVERY_FAILED;
+
+ if (intr4 & MHL_EST_INT) {
+ /* discovery override */
+ ret = mhl_tx_write_reg(sii9234, MHL_TX_MHLTX_CTL1_REG, 0x10);
+
+ /* increase DDC translation layer timer (byte mode) */
+ cbus_write_reg(sii9234, 0x07, 0x32);
+ cbus_set_reg(sii9234, 0x44, 1<<1);
+
+ /* Keep the discovery enabled. Need RGND interrupt */
+ ret = mhl_tx_set_reg(sii9234, MHL_TX_DISC_CTRL1_REG, (1<<0));
+
+ sii9234->state = STATE_ESTABLISHED;
+ }
+
+ if (intr1 & HPD_CHANGE_INT) {
+ ret = cbus_read_reg(sii9234, MSC_REQ_ABORT_REASON_REG, &value);
+
+ if (value & SET_HPD_DOWNSTREAM) {
+ /* Downstream HPD Highi */
+
+ /* Do we need to send HPD upstream using
+ * Register 0x79(page0)? Is HPD need to be overriden??
+ * TODO: See if we need code for overriding HPD OUT
+ * as per Page 0,0x79 Register
+ */
+
+ /* Enable TMDS */
+ ret = mhl_tx_set_reg(sii9234, MHL_TX_TMDS_CCTRL,
+ (1<<4));
+ pr_debug("sii9234: MHL HPD High, enabled TMDS\n");
+
+ ret = mhl_tx_set_reg(sii9234, MHL_TX_INT_CTRL_REG,
+ (1<<4) | (1<<5));
+ } else {
+ /*Downstream HPD Low*/
+
+ /* Similar to above comments.
+ * TODO:Do we need to override HPD OUT value
+ * and do we need to disable TMDS here?
+ */
+
+ /* Disable TMDS */
+ ret = mhl_tx_clear_reg(sii9234, MHL_TX_TMDS_CCTRL,
+ (1<<4));
+ pr_debug("sii9234 MHL HPD low, disabled TMDS\n");
+ ret = mhl_tx_clear_reg(sii9234, MHL_TX_INT_CTRL_REG,
+ (1<<4) | (1<<5));
+ }
+ }
+
+ if (intr1 & RSEN_CHANGE_INT) {
+ ret = mhl_tx_read_reg(sii9234, MHL_TX_SYSSTAT_REG, &value);
+
+ sii9234->rsen = value & RSEN_STATUS;
+
+ if (value & RSEN_STATUS) {
+ pr_info("sii9234: MHL cable connected.. RESN High\n");
+ } else {
+ pr_info("sii9234: RSEN lost\n");
+ /* Once RSEN loss is confirmed,we need to check
+ * based on cable status and chip power status,whether
+ * it is SINK Loss(HDMI cable not connected, TV Off)
+ * or MHL cable disconnection
+ * TODO: Define the below mhl_disconnection()
+ */
+ /* mhl_disconnection(); */
+ /* Notify Disconnection to OTG */
+ if (sii9234->claimed == true) {
+ disable_irq_nosync(sii9234->irq);
+ release_otg = true;
+ }
+ sii9234_power_down(sii9234);
+ }
+ }
+
+ if (sii9234->state == STATE_ESTABLISHED) {
+ ret = sii9234_cbus_irq(sii9234);
+ if (ret < 0) {
+ if (sii9234->claimed == true) {
+ disable_irq_nosync(sii9234->irq);
+ release_otg = true;
+ }
+ sii9234_power_down(sii9234);
+ }
+ }
+
+err_exit:
+ mhl_tx_write_reg(sii9234, MHL_TX_INTR1_REG, intr1);
+ mhl_tx_write_reg(sii9234, MHL_TX_INTR4_REG, intr4);
+
+ mutex_unlock(&sii9234->lock);
+
+ pr_debug("si9234: wake_up\n");
+ wake_up(&sii9234->wq);
+
+ if (release_otg)
+ otg_id_notify();
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit sii9234_mhl_tx_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct sii9234_data *sii9234;
+ int ret;
+
+ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+ sii9234 = kzalloc(sizeof(struct sii9234_data), GFP_KERNEL);
+ if (!sii9234) {
+ dev_err(&client->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ sii9234->pdata = client->dev.platform_data;
+ sii9234->pdata->mhl_tx_client = client;
+ if (!sii9234->pdata) {
+ ret = -EINVAL;
+ goto err_exit1;
+ }
+
+ i2c_set_clientdata(client, sii9234);
+
+ sii9234->irq = client->irq;
+
+ init_waitqueue_head(&sii9234->wq);
+ mutex_init(&sii9234->lock);
+ mutex_init(&sii9234->msc_lock);
+
+ ret = request_threaded_irq(client->irq, NULL, sii9234_irq_thread,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT,
+ "sii9234", sii9234);
+ if (ret < 0)
+ goto err_exit2;
+
+ disable_irq(client->irq);
+
+ sii9234->otg_id_nb.detect = sii9234_detection_callback;
+ sii9234->otg_id_nb.cancel = sii9234_cancel_callback;
+ sii9234->otg_id_nb.priority = sii9234->pdata->prio;
+
+ plist_node_init(&sii9234->otg_id_nb.p, sii9234->pdata->prio);
+
+ ret = otg_id_register_notifier(&sii9234->otg_id_nb);
+ if (ret < 0) {
+ dev_err(&client->dev, "Unable to register notifier\n");
+ goto err_exit2;
+ }
+
+ return 0;
+
+err_exit2:
+err_exit1:
+ kfree(sii9234);
+ return ret;
+}
+
+static int __devinit sii9234_tpi_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct sii9234_platform_data *pdata = client->dev.platform_data;
+ pdata->tpi_client = client;
+ return 0;
+}
+
+static int __devinit sii9234_hdmi_rx_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct sii9234_platform_data *pdata = client->dev.platform_data;
+ pdata->hdmi_rx_client = client;
+ return 0;
+}
+
+static int __devinit sii9234_cbus_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct sii9234_platform_data *pdata = client->dev.platform_data;
+ pdata->cbus_client = client;
+ return 0;
+}
+
+static int __devexit sii9234_mhl_tx_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static int __devexit sii9234_tpi_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static int __devexit sii9234_hdmi_rx_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static int __devexit sii9234_cbus_remove(struct i2c_client *client)
+{
+ return 0;
+}
+
+static const struct i2c_device_id sii9234_mhl_tx_id[] = {
+ {"sii9234_mhl_tx", 0},
+ {}
+};
+
+static const struct i2c_device_id sii9234_tpi_id[] = {
+ {"sii9234_tpi", 0},
+ {}
+};
+
+static const struct i2c_device_id sii9234_hdmi_rx_id[] = {
+ {"sii9234_hdmi_rx", 0},
+ {}
+};
+
+static const struct i2c_device_id sii9234_cbus_id[] = {
+ {"sii9234_cbus", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, sii9234_mhl_tx_id);
+MODULE_DEVICE_TABLE(i2c, sii9234_tpi_id);
+MODULE_DEVICE_TABLE(i2c, sii9234_hdmi_rx_id);
+MODULE_DEVICE_TABLE(i2c, sii9234_cbus_id);
+
+static struct i2c_driver sii9234_mhl_tx_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "sii9234_mhl_tx",
+ },
+ .id_table = sii9234_mhl_tx_id,
+ .probe = sii9234_mhl_tx_i2c_probe,
+ .remove = __devexit_p(sii9234_mhl_tx_remove),
+ .command = NULL,
+};
+
+static struct i2c_driver sii9234_tpi_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "sii9234_tpi",
+ },
+ .id_table = sii9234_tpi_id,
+ .probe = sii9234_tpi_i2c_probe,
+ .remove = __devexit_p(sii9234_tpi_remove),
+};
+
+static struct i2c_driver sii9234_hdmi_rx_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "sii9234_hdmi_rx",
+ },
+ .id_table = sii9234_hdmi_rx_id,
+ .probe = sii9234_hdmi_rx_i2c_probe,
+ .remove = __devexit_p(sii9234_hdmi_rx_remove),
+};
+
+static struct i2c_driver sii9234_cbus_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "sii9234_cbus",
+ },
+ .id_table = sii9234_cbus_id,
+ .probe = sii9234_cbus_i2c_probe,
+ .remove = __devexit_p(sii9234_cbus_remove),
+};
+
+static int __init sii9234_init(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&sii9234_mhl_tx_i2c_driver);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_add_driver(&sii9234_tpi_i2c_driver);
+ if (ret < 0)
+ goto err_exit1;
+
+ ret = i2c_add_driver(&sii9234_hdmi_rx_i2c_driver);
+ if (ret < 0)
+ goto err_exit2;
+
+ ret = i2c_add_driver(&sii9234_cbus_i2c_driver);
+ if (ret < 0)
+ goto err_exit3;
+
+ return 0;
+
+err_exit3:
+ i2c_del_driver(&sii9234_hdmi_rx_i2c_driver);
+err_exit2:
+ i2c_del_driver(&sii9234_tpi_i2c_driver);
+err_exit1:
+ i2c_del_driver(&sii9234_mhl_tx_i2c_driver);
+ return ret;
+}
+
+static void __exit sii9234_exit(void)
+{
+ i2c_del_driver(&sii9234_cbus_i2c_driver);
+ i2c_del_driver(&sii9234_hdmi_rx_i2c_driver);
+ i2c_del_driver(&sii9234_tpi_i2c_driver);
+ i2c_del_driver(&sii9234_mhl_tx_i2c_driver);
+}
+
+module_init(sii9234_init);
+module_exit(sii9234_exit);
diff --git a/include/linux/gp2a.h b/include/linux/gp2a.h
new file mode 100644
index 0000000..084516f
--- /dev/null
+++ b/include/linux/gp2a.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 Samsung Electronics. 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., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ */
+
+
+#ifndef __LINUX_GP2A_H
+#define __LINUX_GP2A_H
+
+#include <linux/types.h>
+
+#ifdef __KERNEL__
+#define GP2A_OPT "gp2a-opt"
+struct gp2a_platform_data {
+ int p_out; /* proximity-sensor-output gpio */
+ void (*power)(bool); /* power to the chip */
+ int (*light_adc_value)(void); /* get light level from adc */
+};
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/include/linux/hsi_char.h b/include/linux/hsi_char.h
index cfa6580..7845fe2 100644
--- a/include/linux/hsi_char.h
+++ b/include/linux/hsi_char.h
@@ -39,6 +39,8 @@
#define CS_GET_TX CS_IOW(10, struct hsi_tx_config)
#define CS_SW_RESET CS_IO(11)
#define CS_GET_FIFO_OCCUPANCY CS_IOR(12, size_t)
+#define CS_GET_CAWAKELINE CS_IOR(13, unsigned int)
+#define CS_SET_WAKE_RX_3WIRES_MODE CS_IOR(14, unsigned int)
#define HSI_MODE_SLEEP 0
#define HSI_MODE_STREAM 1
@@ -50,22 +52,45 @@
#define WAKE_UP 1
#define WAKE_DOWN 0
+/**
+ * struct hsi_tx_config - HSI TX configuration data
+ * @mode: Bit transmission mode
+ * @flow: Data flow type
+ * @frame_size: frame payload size
+ * @channels: Number of active channels
+ * @divisor: Transmission bit rate divisor
+ * @arb_mode: Arbitration type for the transmit FIFOs
+ */
struct hsi_tx_config {
- __u32 mode;
- __u32 flow;
- __u32 frame_size;
- __u32 channels;
- __u32 divisor;
- __u32 arb_mode;
+ __u32 mode; /* Stream:1, Frame:2 */
+ __u32 flow; /* Synchronized:0, Pipelined:1. No Realtime support */
+ __u32 frame_size; /* HSI: 31, SSI: <= 31 */
+ __u32 channels; /* 1, 2, 4, 8, 16 (HSI only) */
+ __u32 divisor; /* For HSI: <= 0xFF, for SSI: <= 0x7F */
+ __u32 arb_mode; /* Round Robin: 0, Priority: 1 */
};
+/**
+ * struct hsi_rx_config - HSI RX configuration data
+ * @mode: Bit transmission mode
+ * @flow: Data flow type
+ * @frame_size: frame payload size
+ * @channels: Number of active channels
+ * @divisor: Transmission bit rate divisor
+ * Auto mode:ON:0x1000, OFF(SSI):0x1001)
+ * Normal range : HSI <= 255, SSI <= 127
+ * @counters: Counters settings for error generation.
+ * Use HSI_HSR_COMBINE_COUNTERS for formatting the register value
+ */
struct hsi_rx_config {
- __u32 mode;
- __u32 flow;
- __u32 frame_size;
- __u32 channels;
- __u32 divisor; /* not used for SSI */
- __u32 counters;
+ __u32 mode; /* Stream:1, Frame:2 */
+ __u32 flow; /* Synchronized:0, Pipelined:1. No Realtime support */
+ __u32 frame_size; /* HSI: 31, SSI: <= 31 */
+ __u32 channels; /* 1, 2, 4, 8, 16(HSI only) */
+ __u32 divisor; /* Normal range : HSI <= 255, SSI <= 127 */
+ __u32 counters; /* HSI: FB[31..24], TB[23..20], FT[19..0] */
+ /* SSI: FT[8..0] */
};
+
#endif /* HSI_CHAR_H */
diff --git a/include/linux/hsi_driver_if.h b/include/linux/hsi_driver_if.h
index 547b30e..6e7a0da 100644
--- a/include/linux/hsi_driver_if.h
+++ b/include/linux/hsi_driver_if.h
@@ -29,7 +29,7 @@
/* The number of ports handled by the driver (MAX:2). Reducing this value
* optimizes the driver memory footprint.
*/
-#define HSI_MAX_PORTS 1
+#define HSI_MAX_PORTS 2
/* bit-field definition for allowed controller IDs and channels */
#define ANY_HSI_CONTROLLER -1
@@ -62,10 +62,8 @@ enum {
HSI_IOCTL_GET_TX, /* Get HST configuration */
HSI_IOCTL_SW_RESET, /* Force a HSI SW RESET */
HSI_IOCTL_GET_FIFO_OCCUPANCY, /* Get amount of words in RX FIFO */
- HSI_IOCTL_SET_ACREADY_SAFEMODE,
- HSI_IOCTL_SET_ACREADY_NORMAL,
- HSI_IOCTL_SET_3WIRE_MODE,
- HSI_IOCTL_SET_4WIRE_MODE,
+ HSI_IOCTL_SET_WAKE_RX_3WIRES_MODE, /* Enable RX wakeup 3-wires mode */
+ HSI_IOCTL_SET_WAKE_RX_4WIRES_MODE, /* Enable RX wakeup 4-wires mode */
};
/* Forward references */
@@ -91,28 +89,30 @@ struct hsr_ctx {
u32 channels;
};
-struct port_ctx {
+struct hsi_port_ctx {
+ int port_number; /* Range [1, 2] */
u32 sys_mpu_enable[2];
struct hst_ctx hst;
struct hsr_ctx hsr;
+ const char *cawake_padconf_name;
+ int cawake_padconf_hsi_mode;
};
/**
- * struct ctrl_ctx - hsi controller regs context
+ * struct hsi_ctrl_ctx - hsi controller regs context
* @sysconfig: keeps HSI_SYSCONFIG reg state
* @gdd_gcr: keeps DMA_GCR reg state
* @dll: keeps HSR_DLL state
* @pctx: array of port context
*/
-struct ctrl_ctx {
+struct hsi_ctrl_ctx {
u32 sysconfig;
u32 gdd_gcr;
u32 dll;
- struct port_ctx *pctx;
+ struct hsi_port_ctx *pctx;
};
/* END DPS */
-
/**
* struct hsi_device - HSI device object (Virtual)
* @n_ctrl: associated HSI controller platform id number
diff --git a/include/linux/i2c/twl.h b/include/linux/i2c/twl.h
index 3c6e9a0..0fd6e49 100644
--- a/include/linux/i2c/twl.h
+++ b/include/linux/i2c/twl.h
@@ -458,6 +458,12 @@ static inline int twl6030_mmc_card_detect(struct device *dev, int slot)
#define TWL6030_PHOENIX_DEV_ON 0x06
/*
+ * TWL6030 PM Master module register offsets (use TWL_MODULE_PM_MASTER)
+ */
+
+#define TWL6030_VBATMIN_HI_THRESHOLD 0x05
+
+/*
* PM Slave resource module register offsets (use TWL6030_MODULE_SLAVE_RES)
*/
diff --git a/include/linux/leds-an30259a.h b/include/linux/leds-an30259a.h
new file mode 100644
index 0000000..7a97efa
--- /dev/null
+++ b/include/linux/leds-an30259a.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics Co. Ltd. All Rights Reserved.
+ *
+ * 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 _LEDS_AN30259A_H
+#define _LEDS_AN30259A_H
+
+#include <linux/ioctl.h>
+#include <linux/types.h>
+
+#define LED_LIGHT_OFF 0
+#define LED_LIGHT_ON 1
+#define LED_LIGHT_PULSE 2
+#define LED_LIGHT_SLOPE 3
+
+/*
+ * This struct gets passed to the ioctl call.
+ * If only one of struct gets passed to the ioctl then it is assumed to define
+ * the behavior for all 3 color components: R, G and B.
+ * If 3 structs are passed, then each one is assumed to describe a single color:
+ * R first, then G, then B.
+ *
+ * Requesting a color value of 0 is equivalent to requesting LED_LIGHT_OFF
+ *
+ * If only describing a single color (ie passing a single struct), then
+ * start_delay will get ignored
+ *
+ * Other parameters may get ignored depending on the requested state:
+ * LIGHT_ON only requires color
+ * LIGHT_PULSE requires color, time_on and time_off
+ *
+ * Total time for time_slope_up_1 + time_slope_up_2 + time_on as well as for
+ * time_slope_down_1 + time_slope_down_2 + time_off will be rounded up to the
+ * nearest .5 seconds.
+ *
+ * Each of the time_slope_* values will get rounded up to the nearest multiple
+ * of 4ms up to 7680ms
+ */
+
+struct an30259a_pr_control {
+ /* LED color in RGB format */
+ __u32 color;
+ /* see defines above */
+ __u32 state;
+ /* initial delay in ms */
+ __u16 start_delay;
+ /* time to reach mid_brightness_up from off in ms */
+ __u16 time_slope_up_1;
+ /* time to reach color from mid_brightness_up in ms */
+ __u16 time_slope_up_2;
+ /* time at max brightness in ms */
+ __u16 time_on;
+ /* time to reach mid_brightness_down from max brightness in ms */
+ __u16 time_slope_down_1;
+ /* time to reach off from mid_brightness_down in ms */
+ __u16 time_slope_down_2;
+ /* time off in ms */
+ __u16 time_off;
+ /* mid point brightness in 1/128 increments of color */
+ __u8 mid_brightness;
+} __packed;
+
+#define AN30259A_PR_SET_LED _IOW('S', 42, struct an30259a_pr_control)
+#define AN30259A_PR_SET_LEDS _IOW('S', 43, struct an30259a_pr_control[3])
+#define AN30259A_PR_SET_IMAX _IOW('S', 44, __u8)
+#endif /* _LEDS_AN30259A_H */
diff --git a/include/linux/max17040_battery.h b/include/linux/max17040_battery.h
index ad97b06..253d08a 100644
--- a/include/linux/max17040_battery.h
+++ b/include/linux/max17040_battery.h
@@ -14,6 +14,22 @@ struct max17040_platform_data {
int (*battery_online)(void);
int (*charger_online)(void);
int (*charger_enable)(void);
+ bool(*is_full_charge)(void);
+ int (*get_bat_temp)(int *);
+ bool skip_reset;
+ int min_capacity; /* minimum allowable capacity. The reported capacity
+ will be scaled from [<min_capacity>,100] to
+ [0,100] */
+ void (*allow_charging)(int en);
+ int high_block_temp;
+ int high_recover_temp;
+ int low_block_temp;
+ int low_recover_temp;
+ int fully_charged_vol;
+ int recharge_vol;
+ int limit_charging_time;
+ int limit_recharging_time;
+ bool use_fuel_alert;
};
#endif
diff --git a/include/linux/mpu.h b/include/linux/mpu.h
new file mode 100644
index 0000000..03fb90d
--- /dev/null
+++ b/include/linux/mpu.h
@@ -0,0 +1,330 @@
+/*
+ $License:
+ Copyright (C) 2011 InvenSense Corporation, 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 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, see <http://www.gnu.org/licenses/>.
+ $
+ */
+
+#ifndef __MPU_H_
+#define __MPU_H_
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/* Number of axes on each sensor */
+#define GYRO_NUM_AXES (3)
+#define ACCEL_NUM_AXES (3)
+#define COMPASS_NUM_AXES (3)
+
+struct mpu_read_write {
+ /* Memory address or register address depending on ioctl */
+ unsigned short address;
+ unsigned short length;
+ unsigned char *data;
+};
+
+enum mpuirq_data_type {
+ MPUIRQ_DATA_TYPE_MPU_IRQ,
+ MPUIRQ_DATA_TYPE_SLAVE_IRQ,
+ MPUIRQ_DATA_TYPE_PM_EVENT,
+ MPUIRQ_DATA_TYPE_NUM_TYPES,
+};
+
+/* User space PM event notification */
+#define MPU_PM_EVENT_SUSPEND_PREPARE (3)
+#define MPU_PM_EVENT_POST_SUSPEND (4)
+
+struct mpuirq_data {
+ int interruptcount;
+ unsigned long long irqtime;
+ int data_type;
+ long data;
+};
+
+enum ext_slave_config_key {
+ MPU_SLAVE_CONFIG_ODR_SUSPEND,
+ MPU_SLAVE_CONFIG_ODR_RESUME,
+ MPU_SLAVE_CONFIG_FSR_SUSPEND,
+ MPU_SLAVE_CONFIG_FSR_RESUME,
+ MPU_SLAVE_CONFIG_MOT_THS,
+ MPU_SLAVE_CONFIG_NMOT_THS,
+ MPU_SLAVE_CONFIG_MOT_DUR,
+ MPU_SLAVE_CONFIG_NMOT_DUR,
+ MPU_SLAVE_CONFIG_IRQ_SUSPEND,
+ MPU_SLAVE_CONFIG_IRQ_RESUME,
+ MPU_SLAVE_WRITE_REGISTERS,
+ MPU_SLAVE_READ_REGISTERS,
+ /* AMI 306 specific config keys */
+ MPU_SLAVE_PARAM,
+ MPU_SLAVE_WINDOW,
+ MPU_SLAVE_READWINPARAMS,
+ MPU_SLAVE_SEARCHOFFSET,
+ /* AKM specific config keys */
+ MPU_SLAVE_READ_SCALE,
+ /* YAS specific config keys */
+ MPU_SLAVE_OFFSET_VALS,
+ MPU_SLAVE_RANGE_CHECK,
+
+ MPU_SLAVE_CONFIG_NUM_CONFIG_KEYS,
+};
+
+/* For the MPU_SLAVE_CONFIG_IRQ_SUSPEND and MPU_SLAVE_CONFIG_IRQ_RESUME */
+enum ext_slave_config_irq_type {
+ MPU_SLAVE_IRQ_TYPE_NONE,
+ MPU_SLAVE_IRQ_TYPE_MOTION,
+ MPU_SLAVE_IRQ_TYPE_DATA_READY,
+};
+
+/* Structure for the following IOCTS's
+ * MPU_CONFIG_ACCEL
+ * MPU_CONFIG_COMPASS
+ * MPU_CONFIG_PRESSURE
+ * MPU_GET_CONFIG_ACCEL
+ * MPU_GET_CONFIG_COMPASS
+ * MPU_GET_CONFIG_PRESSURE
+ *
+ * @key one of enum ext_slave_config_key
+ * @len length of data pointed to by data
+ * @apply zero if communication with the chip is not necessary, false otherwise
+ * This flag can be used to select cached data or to refresh cashed data
+ * cache data to be pushed later or push immediately. If true and the
+ * slave is on the secondary bus the MPU will first enger bypass mode
+ * before calling the slaves .config or .get_config funcion
+ * @data pointer to the data to confgure or get
+ */
+struct ext_slave_config {
+ int key;
+ int len;
+ int apply;
+ void *data;
+};
+
+enum ext_slave_type {
+ EXT_SLAVE_TYPE_GYROSCOPE,
+ EXT_SLAVE_TYPE_ACCELEROMETER,
+ EXT_SLAVE_TYPE_COMPASS,
+ EXT_SLAVE_TYPE_PRESSURE,
+ /*EXT_SLAVE_TYPE_TEMPERATURE */
+
+ EXT_SLAVE_NUM_TYPES
+};
+
+enum ext_slave_id {
+ ID_INVALID = 0,
+
+ ACCEL_ID_LIS331,
+ ACCEL_ID_LSM303A,
+ ACCEL_ID_LIS3DH,
+ ACCEL_ID_KXSD9,
+ ACCEL_ID_KXTF9,
+ ACCEL_ID_BMA150,
+ ACCEL_ID_BMA222,
+ ACCEL_ID_BMA250,
+ ACCEL_ID_ADXL34X,
+ ACCEL_ID_MMA8450,
+ ACCEL_ID_MMA845X,
+ ACCEL_ID_MPU6050,
+
+ COMPASS_ID_AK8975,
+ COMPASS_ID_AMI30X,
+ COMPASS_ID_AMI306,
+ COMPASS_ID_YAS529,
+ COMPASS_ID_YAS530,
+ COMPASS_ID_HMC5883,
+ COMPASS_ID_LSM303M,
+ COMPASS_ID_MMC314X,
+ COMPASS_ID_HSCDTD002B,
+ COMPASS_ID_HSCDTD004A,
+
+ PRESSURE_ID_BMA085,
+};
+
+enum ext_slave_endian {
+ EXT_SLAVE_BIG_ENDIAN,
+ EXT_SLAVE_LITTLE_ENDIAN,
+ EXT_SLAVE_FS8_BIG_ENDIAN,
+ EXT_SLAVE_FS16_BIG_ENDIAN,
+};
+
+enum ext_slave_bus {
+ EXT_SLAVE_BUS_INVALID = -1,
+ EXT_SLAVE_BUS_PRIMARY = 0,
+ EXT_SLAVE_BUS_SECONDARY = 1
+};
+
+
+/**
+ * struct ext_slave_platform_data - Platform data for mpu3050 and mpu6050
+ * slave devices
+ *
+ * @get_slave_descr: Function pointer to retrieve the struct ext_slave_descr
+ * for this slave
+ * @irq: the irq number attached to the slave if any.
+ * @adapt_num: the I2C adapter number.
+ * @bus: the bus the slave is attached to: enum ext_slave_bus
+ * @address: the I2C slave address of the slave device.
+ * @orientation: the mounting matrix of the device relative to MPU.
+ * @irq_data: private data for the slave irq handler
+ * @private_data: additional data, user customizable. Not touched by the MPU
+ * driver.
+ *
+ * The orientation matricies are 3x3 rotation matricies
+ * that are applied to the data to rotate from the mounting orientation to the
+ * platform orientation. The values must be one of 0, 1, or -1 and each row and
+ * column should have exactly 1 non-zero value.
+ */
+struct ext_slave_platform_data {
+ struct ext_slave_descr *(*get_slave_descr) (void);
+ int irq;
+ int adapt_num;
+ int bus;
+ unsigned char address;
+ signed char orientation[9];
+ void *irq_data;
+ void *private_data;
+};
+
+struct fix_pnt_range {
+ long mantissa;
+ long fraction;
+};
+
+static inline long range_fixedpoint_to_long_mg(struct fix_pnt_range rng)
+{
+ return (long)(rng.mantissa * 1000 + rng.fraction / 10);
+}
+
+struct ext_slave_read_trigger {
+ unsigned char reg;
+ unsigned char value;
+};
+
+/**
+ * struct ext_slave_descr - Description of the slave device for programming.
+ *
+ * @suspend: function pointer to put the device in suspended state
+ * @resume: function pointer to put the device in running state
+ * @read: function that reads the device data
+ * @init: function used to preallocate memory used by the driver
+ * @exit: function used to free memory allocated for the driver
+ * @config: function used to configure the device
+ * @get_config:function used to get the device's configuration
+ *
+ * @name: text name of the device
+ * @type: device type. enum ext_slave_type
+ * @id: enum ext_slave_id
+ * @reg: starting register address to retrieve data.
+ * @len: length in bytes of the sensor data. Should be 6.
+ * @endian: byte order of the data. enum ext_slave_endian
+ * @range: full scale range of the slave ouput: struct fix_pnt_range
+ * @trigger: If reading data first requires writing a register this is the
+ * data to write.
+ *
+ * Defines the functions and information about the slave the mpu3050 and
+ * mpu6050 needs to use the slave device.
+ */
+struct ext_slave_descr {
+ int (*init) (void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata);
+ int (*exit) (void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata);
+ int (*suspend) (void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata);
+ int (*resume) (void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata);
+ int (*read) (void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ unsigned char *data);
+ int (*config) (void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *config);
+ int (*get_config) (void *mlsl_handle,
+ struct ext_slave_descr *slave,
+ struct ext_slave_platform_data *pdata,
+ struct ext_slave_config *config);
+
+ char *name;
+ unsigned char type;
+ unsigned char id;
+ unsigned char read_reg;
+ unsigned int read_len;
+ unsigned char endian;
+ struct fix_pnt_range range;
+ struct ext_slave_read_trigger *trigger;
+};
+
+/**
+ * struct mpu_platform_data - Platform data for the mpu driver
+ * @int_config: Bits [7:3] of the int config register.
+ * @orientation: Orientation matrix of the gyroscope
+ * @level_shifter: 0: VLogic, 1: VDD
+ * @accel: Accel platform data
+ * @compass: Compass platform data
+ * @pressure: Pressure platform data
+ *
+ * Contains platform specific information on how to configure the MPU3050 to
+ * work on this platform. The orientation matricies are 3x3 rotation matricies
+ * that are applied to the data to rotate from the mounting orientation to the
+ * platform orientation. The values must be one of 0, 1, or -1 and each row and
+ * column should have exactly 1 non-zero value.
+ */
+struct mpu_platform_data {
+ unsigned char int_config;
+ signed char orientation[GYRO_NUM_AXES * GYRO_NUM_AXES];
+ unsigned char level_shifter;
+ struct ext_slave_platform_data accel;
+ struct ext_slave_platform_data compass;
+ struct ext_slave_platform_data pressure;
+};
+
+#define MPU_IOCTL (0x81) /* Magic number for MPU Iocts */
+/* IOCTL commands for /dev/mpu */
+#define MPU_SET_MPU_CONFIG _IOWR(MPU_IOCTL, 0x00, struct mldl_cfg)
+#define MPU_GET_MPU_CONFIG _IOW(MPU_IOCTL, 0x00, struct mldl_cfg)
+
+#define MPU_SET_PLATFORM_DATA _IOWR(MPU_IOCTL, 0x01, struct mldl_cfg)
+
+#define MPU_READ _IOWR(MPU_IOCTL, 0x10, struct mpu_read_write)
+#define MPU_WRITE _IOW(MPU_IOCTL, 0x10, struct mpu_read_write)
+#define MPU_READ_MEM _IOWR(MPU_IOCTL, 0x11, struct mpu_read_write)
+#define MPU_WRITE_MEM _IOW(MPU_IOCTL, 0x11, struct mpu_read_write)
+#define MPU_READ_FIFO _IOWR(MPU_IOCTL, 0x12, struct mpu_read_write)
+#define MPU_WRITE_FIFO _IOW(MPU_IOCTL, 0x12, struct mpu_read_write)
+
+#define MPU_READ_COMPASS _IOR(MPU_IOCTL, 0x12, unsigned char)
+#define MPU_READ_ACCEL _IOR(MPU_IOCTL, 0x13, unsigned char)
+#define MPU_READ_PRESSURE _IOR(MPU_IOCTL, 0x14, unsigned char)
+
+#define MPU_CONFIG_ACCEL _IOW(MPU_IOCTL, 0x20, struct ext_slave_config)
+#define MPU_CONFIG_COMPASS _IOW(MPU_IOCTL, 0x21, struct ext_slave_config)
+#define MPU_CONFIG_PRESSURE _IOW(MPU_IOCTL, 0x22, struct ext_slave_config)
+
+#define MPU_GET_CONFIG_ACCEL _IOWR(MPU_IOCTL, 0x20, struct ext_slave_config)
+#define MPU_GET_CONFIG_COMPASS _IOWR(MPU_IOCTL, 0x21, struct ext_slave_config)
+#define MPU_GET_CONFIG_PRESSURE _IOWR(MPU_IOCTL, 0x22, struct ext_slave_config)
+
+#define MPU_SUSPEND _IO(MPU_IOCTL, 0x30)
+#define MPU_RESUME _IO(MPU_IOCTL, 0x31)
+/* Userspace PM Event response */
+#define MPU_PM_EVENT_HANDLED _IO(MPU_IOCTL, 0x32)
+
+
+#endif /* __MPU_H_ */
diff --git a/include/linux/platform_data/fsa9480.h b/include/linux/platform_data/fsa9480.h
new file mode 100644
index 0000000..ad9d70c
--- /dev/null
+++ b/include/linux/platform_data/fsa9480.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 Samsung Electronics
+ * Minkyu Kang <mk7.kang@samsung.com>
+ * Wonguk Jeong <wonguk.jeong@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef __LINUX_USB_SWITCH_FSA9480_H_
+#define __LINUX_USB_SWITCH_FSA9480_H_
+
+#include <linux/types.h>
+
+#define FSA9480_DETECT_NONE (0)
+#define FSA9480_DETECT_USB (1 << 0)
+#define FSA9480_DETECT_USB_HOST (1 << 1)
+#define FSA9480_DETECT_CHARGER (1 << 2)
+#define FSA9480_DETECT_JIG (1 << 3)
+#define FSA9480_DETECT_UART (1 << 4)
+#define FSA9480_DETECT_AV_365K (1 << 5)
+#define FSA9480_DETECT_AV_365K_CHARGER (1 << 6)
+
+#define FSA9480_DETECT_ALL (FSA9480_DETECT_USB | \
+ FSA9480_DETECT_USB_HOST | \
+ FSA9480_DETECT_CHARGER | \
+ FSA9480_DETECT_JIG | \
+ FSA9480_DETECT_UART | \
+ FSA9480_DETECT_AV_365K | \
+ FSA9480_DETECT_AV_365K_CHARGER)
+
+struct fsa9480_detect_set {
+ int prio;
+ u32 mask;
+ bool fallback;
+};
+
+struct fsa9480_platform_data {
+ int detect_time;
+
+ void (*enable)(bool enable);
+ void (*detected)(int device);
+
+ /* The FSA9480 has a bug that prevents its from detecting a detach when
+ * the cable was identified as a USB OTG cable. As a workaround we
+ * provide the FSA9480 with a GPIO that is hooked up to the USB ID
+ * signal.
+ */
+ int external_id;
+
+ struct fsa9480_detect_set *detect_sets;
+ int num_sets;
+
+ void (*mask_vbus_irq)(void);
+ void (*unmask_vbus_irq)(void);
+ bool (*vbus_present)(void);
+ int external_vbus_irq;
+ unsigned long external_vbus_flags;
+};
+
+#endif
diff --git a/include/linux/platform_data/lte_modem_bootloader.h b/include/linux/platform_data/lte_modem_bootloader.h
new file mode 100755
index 0000000..27a0450
--- /dev/null
+++ b/include/linux/platform_data/lte_modem_bootloader.h
@@ -0,0 +1,40 @@
+/* Lte modem bootloader support for Samsung Tuna Board.
+ *
+ * Copyright (C) 2011 Google, Inc.
+ * Copyright (C) 2011 Samsung Electronics.
+ *
+ * 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 __LTE_MODEM_BOOTLOADER_H
+#define __LTE_MODEM_BOOTLOADER_H
+
+#define LTE_MODEM_BOOTLOADER_DRIVER_NAME "lte_modem_bootloader"
+
+#define IOCTL_LTE_MODEM_XMIT_BOOT _IOW('o', 0x23, unsigned int)
+#define IOCTL_LTE_MODEM_LTE2AP_STATUS _IOR('o', 0x24, unsigned int)
+
+#define AIRPLAIN_MODE_TEST
+
+#ifdef AIRPLAIN_MODE_TEST
+#define IOCTL_LTE_MODEM_AIRPLAIN_ON _IOWR('o', 0x25, unsigned int)
+#define IOCTL_LTE_MODEM_AIRPLAIN_OFF _IOWR('o', 0x26, unsigned int)
+#endif
+
+struct lte_modem_bootloader_param {
+ char __user *buf;
+ int len;
+};
+
+struct lte_modem_bootloader_platform_data {
+ const char *name;
+ unsigned int gpio_lte2ap_status;
+};
+#endif/* LTE_MODEM_BOOTLOADER_H */
diff --git a/include/linux/platform_data/mms_ts.h b/include/linux/platform_data/mms_ts.h
new file mode 100644
index 0000000..2ab2c75
--- /dev/null
+++ b/include/linux/platform_data/mms_ts.h
@@ -0,0 +1,34 @@
+/*
+ * mms_ts.h - Platform data for Melfas MMS-series touch driver
+ *
+ * Copyright (C) 2011 Google Inc.
+ * Author: Dima Zavin <dima@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.
+ *
+ */
+
+#ifndef _LINUX_MMS_TOUCH_H
+#define _LINUX_MMS_TOUCH_H
+
+struct mms_ts_platform_data {
+ int max_x;
+ int max_y;
+
+ bool invert_x;
+ bool invert_y;
+
+ int gpio_sda;
+ int gpio_scl;
+ int gpio_resetb;
+ int gpio_vdd_en;
+
+ int (*mux_fw_flash)(bool to_gpios);
+ const char *fw_name;
+};
+
+#endif /* _LINUX_MMS_TOUCH_H */
diff --git a/include/linux/platform_data/modem.h b/include/linux/platform_data/modem.h
new file mode 100755
index 0000000..e1e6603
--- /dev/null
+++ b/include/linux/platform_data/modem.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2010 Samsung Electronics.
+ *
+ * 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 __MODEM_IF_H__
+#define __MODEM_IF_H__
+
+enum modem_t {
+ IMC_XMM6260,
+ VIA_CBP71,
+ SEC_CMC221,
+};
+
+enum dev_format {
+ IPC_FMT,
+ IPC_RAW,
+ IPC_RFS,
+ IPC_CMD,
+ IPC_BOOT,
+ IPC_MULTI_RAW,
+ IPC_RAMDUMP,
+};
+
+enum modem_io {
+ IODEV_MISC,
+ IODEV_NET,
+ IODEV_DUMMY,
+};
+
+enum modem_link {
+ LINKDEV_MIPI,
+ LINKDEV_DPRAM,
+ LINKDEV_SPI,
+ LINKDEV_USB,
+ LINKDEV_MAX,
+};
+
+enum modem_network {
+ UMTS_NETWORK,
+ CDMA_NETWORK,
+ LTE_NETWORK,
+};
+
+/* This structure is used in board-tuna-modem.c */
+struct modem_io_t {
+ char *name;
+ int id;
+ enum dev_format format;
+ enum modem_io io_type;
+ enum modem_link link;
+};
+
+/* platform data */
+struct modem_data {
+ char *name;
+
+ unsigned gpio_cp_on;
+ unsigned gpio_cp_off;
+ unsigned gpio_reset_req_n;
+ unsigned gpio_cp_reset;
+ unsigned gpio_pda_active;
+ unsigned gpio_phone_active;
+ unsigned gpio_cp_dump_int;
+ unsigned gpio_flm_uart_sel;
+ unsigned gpio_cp_warm_reset;
+#ifdef CONFIG_LTE_MODEM_CMC221
+ unsigned gpio_slave_wakeup;
+ unsigned gpio_host_wakeup;
+ unsigned gpio_host_active;
+ int irq_host_wakeup;
+#endif
+ /* modem component */
+ enum modem_t modem_type;
+ enum modem_link link_type;
+ enum modem_network modem_net;
+ unsigned num_iodevs;
+ struct modem_io_t *iodevs;
+};
+
+#endif
diff --git a/include/linux/platform_data/panel-s6e8aa0.h b/include/linux/platform_data/panel-s6e8aa0.h
new file mode 100644
index 0000000..553d516
--- /dev/null
+++ b/include/linux/platform_data/panel-s6e8aa0.h
@@ -0,0 +1,124 @@
+/*
+ * Samsung s6e8aa0 panel support
+ *
+ * Copyright 2011 Google, Inc.
+ * Author: Erik Gilling <konkers@google.com>
+ *
+ * based on d2l panel driver by Jerry Alexander <x0135174@ti.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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 __LINUX_PLATFORM_DATA_PANEL_S6E8AA0_H
+#define __LINUX_PLATFORM_DATA_PANEL_S6E8AA0_H
+
+struct s6e8aa0_sequence_entry {
+ const u8 *cmd;
+ int cmd_len;
+ unsigned int msleep;
+};
+
+enum {
+ BV_0 = 0x0,
+ BV_1 = 0x13E456,
+ BV_15 = 0x1390FFB,
+ BV_35 = 0x44D7CF9,
+ BV_59 = 0xB323808,
+ BV_87 = 0x186611F4,
+ BV_171 = 0x6840E4FF,
+ BV_255 = 0xFFFFFFFF,
+};
+
+struct s6e8aa0_gamma_entry {
+ u32 brightness;
+ u32 v[3];
+};
+
+struct s6e8aa0_gamma_adj_points {
+ const u32 v1;
+ const u32 v15;
+ const u32 v35;
+ const u32 v59;
+ const u32 v87;
+ const u32 v171;
+};
+
+struct s6e8aa0_color_adj {
+ u32 mult[3];
+ int rshift;
+};
+
+/**
+ * struct s6e8aa0_factory_calibration_info - factory calibration parameters
+ * @regs: Register values used during factory calibration. The first array
+ * index is 0 for dark gamma offsets and 1 for bright gamma
+ * offsets. The second array index is color (0: red, 1: green,
+ * 2: blue). The last array index is the adjusment point (0: V1,
+ * 1: V15, 2: V35, 3: V59, 4: V87, 5: V171, 6: V255).
+ * @brightness: Brightness target used for each adjustment point scaled linearly
+ * so the maximum brightness is BV_255 (0xffffffff).The first array
+ * index is 0 for dark gamma offsets and 1 for bright gamma
+ * offsets. The last array index is the adjusment point (0: V1,
+ * 1: V15, 2: V35, 3: V59, 4: V87, 5: V171, 6: V255). If the value
+ * is 0 adjustment point is ignored.
+ * @color_adj: Per color brightness multiplier.
+ */
+
+struct s6e8aa0_factory_calibration_info {
+ u16 regs[2][3][7];
+ u32 brightness[2][7];
+ struct s6e8aa0_color_adj color_adj;
+};
+
+struct s6e8aa0_acl_parameters {
+ unsigned int cd;
+ unsigned int acl_val;
+ u8 regs[29];
+};
+
+/**
+ * struct s6e8aa0_elvs_parameters -- elvss parameters
+ * @cd: look up value. if the argument cd is <= this value,
+ * then the elvss_val is added to byte 2 of the panel
+ * id to generate the elvss value passed to the controller.
+ * @elvss_val: Value to add to the elvss for this cd
+ */
+struct s6e8aa0_elvss_parameters {
+ unsigned int cd;
+ u8 elvss_val;
+};
+
+struct panel_s6e8aa0_data {
+ int reset_gpio;
+ void (* set_power)(bool enable);
+
+ const struct s6e8aa0_sequence_entry *seq_display_set;
+ int seq_display_set_size;
+ const struct s6e8aa0_sequence_entry *seq_etc_set;
+ int seq_etc_set_size;
+
+ struct s6e8aa0_factory_calibration_info *factory_info;
+
+ const struct s6e8aa0_gamma_adj_points *gamma_adj_points;
+ const struct s6e8aa0_gamma_entry *gamma_table;
+ int gamma_table_size;
+
+ const struct s6e8aa0_acl_parameters *acl_table;
+ unsigned int acl_table_size;
+ unsigned int acl_average;
+
+ const struct s6e8aa0_elvss_parameters *elvss_table;
+ int elvss_table_size;
+};
+
+#endif
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
index 0cae9f6..6c3cefb 100644
--- a/include/linux/remoteproc.h
+++ b/include/linux/remoteproc.h
@@ -246,6 +246,7 @@ enum rproc_event {
* @secure_restart: completion event notifier for the secure restart process
* @secure_mode: flag to dictate whether to enable secure loading
* @secure_ok: restart status flag to be looked up upon the event's completion
+ * @secure_reset: flag to uninstall the firewalls
*/
struct rproc {
struct list_head next;
@@ -283,6 +284,7 @@ struct rproc {
struct mutex secure_lock;
bool secure_mode;
bool secure_ok;
+ bool secure_reset;
bool halt_on_crash;
char *header;
int header_len;
diff --git a/include/linux/rproc_drm.h b/include/linux/rproc_drm.h
new file mode 100644
index 0000000..475a498
--- /dev/null
+++ b/include/linux/rproc_drm.h
@@ -0,0 +1,45 @@
+/*
+ * Remote Processor DRM Service API
+ *
+ * Copyright(c) 2011 Texas Instruments. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef RPROC_DRM_H
+#define RPROC_DRM_H
+
+#ifdef CONFIG_SECURITY_MIDDLEWARE_COMPONENT
+int rproc_drm_invoke_service(bool enable);
+#else
+static inline int rproc_drm_invoke_service(bool enable)
+{
+ return -EACCES;
+}
+#endif
+
+#endif /* RPROC_DRM_H */
diff --git a/include/linux/sec_jack.h b/include/linux/sec_jack.h
new file mode 100755
index 0000000..d8182e3
--- /dev/null
+++ b/include/linux/sec_jack.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2008 Samsung Electronics, 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 __ASM_ARCH_SEC_HEADSET_H
+#define __ASM_ARCH_SEC_HEADSET_H
+
+#ifdef __KERNEL__
+
+enum {
+ SEC_JACK_NO_DEVICE = 0x0,
+ SEC_HEADSET_4POLE = 0x01 << 0,
+ SEC_HEADSET_3POLE = 0x01 << 1,
+ SEC_TTY_DEVICE = 0x01 << 2,
+ SEC_FM_HEADSET = 0x01 << 3,
+ SEC_FM_SPEAKER = 0x01 << 4,
+ SEC_TVOUT_DEVICE = 0x01 << 5,
+ SEC_EXTRA_DOCK_SPEAKER = 0x01 << 6,
+ SEC_EXTRA_CAR_DOCK_SPEAKER = 0x01 << 7,
+ SEC_UNKNOWN_DEVICE = 0x01 << 8,
+};
+
+struct sec_jack_zone {
+ unsigned int adc_high;
+ unsigned int delay_ms;
+ unsigned int check_count;
+ unsigned int jack_type;
+};
+
+struct sec_jack_buttons_zone {
+ unsigned int code;
+ unsigned int adc_low;
+ unsigned int adc_high;
+};
+
+struct sec_jack_platform_data {
+ void (*set_micbias_state) (bool);
+ int (*get_adc_value) (void);
+ struct sec_jack_zone *zones;
+ struct sec_jack_buttons_zone *buttons_zones;
+ int num_zones;
+ int num_buttons_zones;
+ int det_gpio;
+ int send_end_gpio;
+ bool det_active_high;
+ bool send_end_active_high;
+};
+#endif
+
+#endif
diff --git a/include/linux/sii9234.h b/include/linux/sii9234.h
new file mode 100644
index 0000000..305e4f8
--- /dev/null
+++ b/include/linux/sii9234.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2011 Samsung Electronics
+ *
+ * Author: Adam Hampson <ahampson@sta.samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#ifndef _SII9234_H_
+#define _SII9234_H_
+
+#include <linux/i2c.h>
+
+#define MHL_DEVCAP_DEVSTATE 0x0
+#define MHL_DEVCAP_MHL_VERSION 0x1
+#define MHL_DEVCAP_DEV_CAT 0x2
+#define MHL_DEVCAP_ADOPTER_ID_H 0x3
+#define MHL_DEVCAP_ADOPTER_ID_L 0x4
+#define MHL_DEVCAP_VID_LINK_MODE 0x5
+#define MHL_DEVCAP_AUD_LINK_MODE 0x6
+#define MHL_DEVCAP_VIDEO_TYPE 0x7
+#define MHL_DEVCAP_LOG_DEV_MAP 0x8
+#define MHL_DEVCAP_BANDWIDTH 0x9
+#define MHL_DEVCAP_FEATURE_FLAG 0xa
+#define MHL_DEVCAP_DEVICE_ID_H 0xb
+#define MHL_DEVCAP_DEVICE_ID_L 0xc
+#define MHL_DEVCAP_SCRATHPAD_SIZE 0xd
+#define MHL_DEVCAP_INT_STAT_SIZE 0xe
+#define MHL_DEVCAP_RESERVED 0xf
+
+struct sii9234_platform_data {
+ int prio;
+ void (*enable)(bool enable);
+ void (*power)(int on);
+ void (*enable_vbus)(bool enable);
+ void (*connect)(bool connected, u8 *devcap);
+ struct i2c_client *mhl_tx_client;
+ struct i2c_client *tpi_client;
+ struct i2c_client *hdmi_rx_client;
+ struct i2c_client *cbus_client;
+};
+
+#endif /* _SII9234_H_ */
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 73c7df4..7639cab 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -1419,6 +1419,7 @@ extern int usb_string(struct usb_device *dev, int index,
/* wrappers that also update important state inside usbcore */
extern int usb_clear_halt(struct usb_device *dev, int pipe);
extern int usb_reset_configuration(struct usb_device *dev);
+extern void usb_force_disconnect(struct usb_device *udev);
extern int usb_set_interface(struct usb_device *dev, int ifnum, int alternate);
extern void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr);
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index 3e93de7..9569d4b 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -30,4 +30,10 @@
descriptor */
#define USB_QUIRK_DELAY_INIT 0x00000040
+/* device does not support reset-resume */
+#define USB_QUIRK_NO_RESET_RESUME 0x00000080
+
+/* device does not need GET_STATUS request */
+#define USB_QUIRK_NO_GET_STATUS 0x00000100
+
#endif /* __LINUX_USB_QUIRKS_H */
diff --git a/security/Kconfig b/security/Kconfig
index d4ffb55..f76afcc 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -187,7 +187,6 @@ source security/tomoyo/Kconfig
source security/apparmor/Kconfig
source security/integrity/ima/Kconfig
-
source security/smc/Kconfig
choice
diff --git a/security/smc/Kconfig b/security/smc/Kconfig
index 9fcd1f6..7a933ac 100644
--- a/security/smc/Kconfig
+++ b/security/smc/Kconfig
@@ -1,11 +1,11 @@
-config TF_MSHIELD
+config TF_ZEBRA
bool
config SECURITY_MIDDLEWARE_COMPONENT
bool "Enable SMC Driver"
depends on ARCH_OMAP3 || ARCH_OMAP4
default n
- select TF_MSHIELD
+ select TF_ZEBRA
help
This option adds kernel support for communication with the SMC
Protected Application.
diff --git a/security/smc/Makefile b/security/smc/Makefile
index 80cf430..abf0095 100644
--- a/security/smc/Makefile
+++ b/security/smc/Makefile
@@ -1,3 +1,48 @@
-ifeq ($(CONFIG_SECURITY_MIDDLEWARE_COMPONENT),y)
-obj-$(CONFIG_ARCH_OMAP4) += omap4/
+#
+# Copyright (c) 2006-2010 Trusted Logic S.A.
+# 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 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
+#
+
+ifdef S_VERSION_BUILD
+EXTRA_CFLAGS += -DS_VERSION_BUILD=$(S_VERSION_BUILD)
endif
+
+EXTRA_CFLAGS += -Iarch/arm/mach-omap2
+EXTRA_CFLAGS += -Iarch/arm/plat-omap/include/plat
+EXTRA_CFLAGS += -DCONFIG_TF_TEEC
+EXTRA_CFLAGS += -DCONFIG_TF_ION
+
+tf_driver-objs += tf_util.o
+tf_driver-objs += tf_conn.o
+tf_driver-objs += tf_device.o
+tf_driver-objs += tf_comm.o
+tf_driver-objs += tf_crypto.o
+tf_driver-objs += tf_crypto_digest.o
+tf_driver-objs += tf_crypto_aes.o
+tf_driver-objs += tf_crypto_des.o
+tf_driver-objs += tf_dma.o
+tf_driver-objs += tf_comm_mshield.o
+tf_driver-objs += tf_device_mshield.o
+tf_driver-objs += bridge_pub2sec.o
+tf_driver-objs += tf_teec.o
+
+obj-$(CONFIG_SECURITY_MIDDLEWARE_COMPONENT) += tf_driver.o
+obj-$(CONFIG_SECURITY_MIDDLEWARE_COMPONENT) += rproc_drm.o
+
+plus_sec := $(call as-instr,.arch_extension sec,+sec)
+AFLAGS_bridge_pub2sec.o :=-Wa,-march=armv7-a$(plus_sec)
diff --git a/security/smc/bridge_pub2sec.S b/security/smc/bridge_pub2sec.S
new file mode 100644
index 0000000..15cd3b7
--- /dev/null
+++ b/security/smc/bridge_pub2sec.S
@@ -0,0 +1,242 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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
+ */
+
+.text
+
+#define SMICODEPUB_IRQ_END 0xFE
+#define SMICODEPUB_FIQ_END 0xFD
+#define SMICODEPUB_RPC_END 0xFC
+
+#define PUB2SEC_NOCST 0xFF
+#define SMICODEPUB_NEWTASK 0x00
+
+/*
+ * RPC status:
+ * - 0: the secure world yielded due to an interrupt
+ * - 1: the secure world yielded on an RPC (no public thread is handling it)
+ * - 2: the secure world yielded on an RPC and the response is ready
+ */
+#define RPC_ADVANCEMENT_NONE 0
+#define RPC_ADVANCEMENT_PENDING 1
+#define RPC_ADVANCEMENT_FINISHED 2
+
+#ifdef CONFIG_ARM_ERRATA_430973
+#define INVALIDATE_BTB MCR p15, 0, R0, c7, c5, 6
+#else
+#define INVALIDATE_BTB
+#endif
+
+schedule_secure_world:
+ .global schedule_secure_world
+
+ /* Save registers */
+ push {r4-r12, lr}
+
+ /* Copy the Secure Service ID in r12 */
+ mov r12, r0
+
+ cmp r0, #SMICODEPUB_IRQ_END
+ beq return_from_irq
+
+ cmp r0, #SMICODEPUB_RPC_END
+ beq return_from_rpc
+
+ mov r6, #PUB2SEC_NOCST
+ mov r12, #SMICODEPUB_NEWTASK
+
+ b label_smc
+
+return_from_rpc:
+ ldr r9, =g_RPC_parameters
+ ldm r9, {r0-r3}
+ /* fall through */
+
+return_from_irq:
+ ldr r10, =g_secure_task_id
+ ldr r6, [r10]
+
+ b label_smc
+
+label_smc:
+ INVALIDATE_BTB
+ dsb
+ dmb
+
+#ifdef CONFIG_BENCH_SECURE_CYCLE
+ /* Come from Non Secure: activate counter 1 (write to 0 are ignored) */
+ mov r4, #0x00000002
+
+ /* Read Count Enable Set Register */
+ mcr p15, 0x0, r4, c9, c12, 1
+
+ /* Come from Non Secure: stop counter 0 (write to 0 are ignored) */
+ mov r4, #0x00000001
+
+ /* Write Count Enable Clear Register */
+ mcr p15, 0x0, r4, c9, c12, 2
+#endif
+
+ smc #0
+ b service_end
+ nop
+
+#ifdef CONFIG_BENCH_SECURE_CYCLE
+ /* Come from Secure: activate counter 0 (write to 0 are ignored) */
+ mov r4, #0x00000001
+
+ /* Write Count Enable Set Register */
+ mcr p15, 0x0, r4, c9, c12, 1
+
+ /* Come from Secure: stop counter 1 (write to 0 are ignored) */
+ mov r4, #0x00000002
+
+ /* Write Count Enable Clear Register */
+ mcr p15, 0x0, r4, c9, c12, 2
+#endif
+
+ INVALIDATE_BTB
+ ldr r8, =g_secure_task_id
+ str r6, [r8]
+
+ mov r0, #0x00
+ ldr r8, =g_service_end
+ str r0, [r8]
+
+ b schedule_secure_world_exit
+
+service_end:
+
+schedule_secure_world_exit:
+#ifdef CONFIG_BENCH_SECURE_CYCLE
+ /* Come from Secure: activate counter 0 (write to 0 are ignored) */
+ mov r4, #0x00000001
+
+ /* Write Count Enable Set Register */
+ mcr p15, 0x0, r4, c9, c12, 1
+
+ /* Come from Secure: stop counter 1 (write to 0 are ignored) */
+ mov r4, #0x00000002
+
+ /* Write Count Enable Clear Register */
+ mcr p15, 0x0, r4, c9, c12, 2
+#endif
+
+ INVALIDATE_BTB
+
+ /* Restore registers */
+ pop {r4-r12, pc}
+
+rpc_handler:
+ .global rpc_handler
+
+#ifdef CONFIG_BENCH_SECURE_CYCLE
+ /* Come from Secure: activate counter 0 (write to 0 are ignored) */
+ mov r4, #0x00000001
+
+ /* Write Count Enable Set Register */
+ mcr p15, 0x0, r4, c9, c12, 1
+
+ /* Come from Secure: stop counter 1 (write to 0 are ignored) */
+ mov r4, #0x00000002
+
+ /* Write Count Enable Clear Register */
+ mcr p15, 0x0, r4, c9, c12, 2
+#endif
+ INVALIDATE_BTB
+
+ /* g_RPC_advancement = RPC_ADVANCEMENT_PENDING */
+ ldr r8, =g_RPC_advancement
+ mov r9, #RPC_ADVANCEMENT_PENDING
+ str r9, [r8]
+
+ ldr r8, =g_RPC_parameters
+ stm r8, {r0-r3}
+
+ ldr r8, =g_secure_task_id
+ str r6, [r8]
+
+ mov r0, #0x00
+ ldr r8, =g_service_end
+ str r0, [r8]
+
+ /* Restore registers */
+ pop {r4-r12, pc}
+
+#ifdef CONFIG_BENCH_SECURE_CYCLE
+
+setup_counters:
+ .global setup_counters
+
+ push {r14}
+
+ mrc p15, 0, r2, c9, c12, 0
+ orr r2, r2, #0x3
+ mcr p15, 0, r2, c9, c12, 0
+
+ mrc p15, 0, r2, c9, c12, 1
+ orr r2, r2, #0x80000000
+ mcr p15, 0, r2, c9, c12, 1
+
+ pop {pc}
+
+run_code_speed:
+ .global run_code_speed
+
+ push {r14}
+
+ /* Reset cycle counter */
+ mov r2, #0
+ mcr p15, 0, r2, c9, c13, 0
+
+run_code_speed_loop:
+ sub r0, r0, #1
+ cmp r0, #0
+ bne run_code_speed_loop
+
+ /* Read cycle counter */
+ mrc p15, 0, r0, c9, c13, 0
+
+ pop {pc}
+
+run_data_speed:
+ .global run_data_speed
+
+ push {r14}
+
+ /* Reset cycle counter */
+ mov r2, #0
+ mcr p15, 0, r2, c9, c13, 0
+
+run_data_speed_loop:
+ sub r0, r0, #1
+ ldr r2, [r1]
+ cmp r0, #0
+ bne run_data_speed_loop
+
+ /* read cycle counter */
+ mrc p15, 0, r0, c9, c13, 0
+
+ pop {pc}
+
+#endif
+
+read_mpidr:
+ .global read_mpidr
+ mrc p15, 0, r0, c0, c0, 5
+ bx lr
diff --git a/security/smc/omap4/Makefile b/security/smc/omap4/Makefile
deleted file mode 100644
index de75cc2..0000000
--- a/security/smc/omap4/Makefile
+++ /dev/null
@@ -1,35 +0,0 @@
-#
-# Copyright (c) 2006-2010 Trusted Logic S.A.
-# 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 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
-#
-
-ifdef S_VERSION_BUILD
-EXTRA_CFLAGS += -DS_VERSION_BUILD=$(S_VERSION_BUILD)
-endif
-
-EXTRA_CFLAGS += -Iarch/arm/mach-omap2
-
-tf_driver-objs += scxlnx_util.o
-tf_driver-objs += scxlnx_device.o
-tf_driver-objs += scx_public_crypto.o
-tf_driver-objs += scx_public_crypto_Digest.o
-tf_driver-objs += scx_public_crypto_AES.o
-tf_driver-objs += scx_public_dma.o
-tf_driver-objs += scxlnx_comm_mshield.o
-
-obj-$(CONFIG_SECURITY_MIDDLEWARE_COMPONENT) += tf_driver.o
diff --git a/security/smc/omap4/scx_protocol.h b/security/smc/omap4/scx_protocol.h
deleted file mode 100644
index 80653eb..0000000
--- a/security/smc/omap4/scx_protocol.h
+++ /dev/null
@@ -1,676 +0,0 @@
-/*
- * Copyright (c) 2006-2010 Trusted Logic S.A.
- * 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
- */
-
-#ifndef __SCX_PROTOCOL_H__
-#define __SCX_PROTOCOL_H__
-
-/*----------------------------------------------------------------------------
- *
- * This header file defines the structure used in the SChannel Protocol.
- * See your Product Reference Manual for a specification of the SChannel
- * protocol.
- *---------------------------------------------------------------------------*/
-
-/*
- * The driver interface version returned by the version ioctl
- */
-#define SCX_DRIVER_INTERFACE_VERSION 0x04000000
-
-/*
- * Protocol version handling
- */
-#define SCX_S_PROTOCOL_MAJOR_VERSION (0x06)
-#define GET_PROTOCOL_MAJOR_VERSION(a) (a >> 24)
-#define GET_PROTOCOL_MINOR_VERSION(a) ((a >> 16) & 0xFF)
-
-/*
- * The size, in bytes, of the L1 Shared Buffer.
- */
-#define SCX_COMM_BUFFER_SIZE (0x1000) /* 4kB*/
-
-/*
- * The S flag of the nConfigFlags_S register.
- */
-#define SCX_CONFIG_FLAG_S (1 << 3)
-
-/*
- * The TimeSlot field of the nSyncSerial_N register.
- */
-#define SCX_SYNC_SERIAL_TIMESLOT_N (1)
-
-/*
- * nStatus_S related defines.
- */
-#define SCX_STATUS_P_MASK (0X00000001)
-#define SCX_STATUS_POWER_STATE_SHIFT (3)
-#define SCX_STATUS_POWER_STATE_MASK (0x1F << SCX_STATUS_POWER_STATE_SHIFT)
-
-/*
- * Possible power states of the POWER_STATE field of the nStatus_S register
- */
-#define SCX_POWER_MODE_COLD_BOOT (0)
-#define SCX_POWER_MODE_WARM_BOOT (1)
-#define SCX_POWER_MODE_ACTIVE (3)
-#define SCX_POWER_MODE_READY_TO_SHUTDOWN (5)
-#define SCX_POWER_MODE_READY_TO_HIBERNATE (7)
-#define SCX_POWER_MODE_WAKEUP (8)
-#define SCX_POWER_MODE_PANIC (15)
-
-/*
- * Possible nCommand values for MANAGEMENT commands
- */
-#define SCX_MANAGEMENT_HIBERNATE (1)
-#define SCX_MANAGEMENT_SHUTDOWN (2)
-#define SCX_MANAGEMENT_PREPARE_FOR_CORE_OFF (3)
-#define SCX_MANAGEMENT_RESUME_FROM_CORE_OFF (4)
-
-/*
- * The capacity of the Normal Word message queue, in number of slots.
- */
-#define SCX_N_MESSAGE_QUEUE_CAPACITY (512)
-
-/*
- * The capacity of the Secure World message answer queue, in number of slots.
- */
-#define SCX_S_ANSWER_QUEUE_CAPACITY (256)
-
-/*
- * The value of the S-timeout register indicating an infinite timeout.
- */
-#define SCX_S_TIMEOUT_0_INFINITE (0xFFFFFFFF)
-#define SCX_S_TIMEOUT_1_INFINITE (0xFFFFFFFF)
-
-/*
- * The value of the S-timeout register indicating an immediate timeout.
- */
-#define SCX_S_TIMEOUT_0_IMMEDIATE (0x0)
-#define SCX_S_TIMEOUT_1_IMMEDIATE (0x0)
-
-/*
- * Identifies the get protocol version SMC.
- */
-#define SCX_SMC_GET_PROTOCOL_VERSION (0XFFFFFFFB)
-
-/*
- * Identifies the init SMC.
- */
-#define SCX_SMC_INIT (0XFFFFFFFF)
-
-/*
- * Identifies the reset irq SMC.
- */
-#define SCX_SMC_RESET_IRQ (0xFFFFFFFE)
-
-/*
- * Identifies the SET_W3B SMC.
- */
-#define SCX_SMC_WAKE_UP (0xFFFFFFFD)
-
-/*
- * Identifies the STOP SMC.
- */
-#define SCX_SMC_STOP (0xFFFFFFFC)
-
-/*
- * Identifies the n-yield SMC.
- */
-#define SCX_SMC_N_YIELD (0X00000003)
-
-
-/* Possible stop commands for SMC_STOP */
-#define SCSTOP_HIBERNATE (0xFFFFFFE1)
-#define SCSTOP_SHUTDOWN (0xFFFFFFE2)
-
-/*
- * representation of an UUID.
- */
-struct SCX_UUID {
- u32 time_low;
- u16 time_mid;
- u16 time_hi_and_version;
- u8 clock_seq_and_node[8];
-};
-
-
-/**
- * Command parameters.
- */
-struct SCX_COMMAND_PARAM_VALUE {
- u32 a;
- u32 b;
-};
-
-struct SCX_COMMAND_PARAM_TEMP_MEMREF {
- u32 nDescriptor; /* data pointer for exchange message.*/
- u32 nSize;
- u32 nOffset;
-};
-
-struct SCX_COMMAND_PARAM_MEMREF {
- u32 hBlock;
- u32 nSize;
- u32 nOffset;
-};
-
-union SCX_COMMAND_PARAM {
- struct SCX_COMMAND_PARAM_VALUE sValue;
- struct SCX_COMMAND_PARAM_TEMP_MEMREF sTempMemref;
- struct SCX_COMMAND_PARAM_MEMREF sMemref;
-};
-
-/**
- * Answer parameters.
- */
-struct SCX_ANSWER_PARAM_VALUE {
- u32 a;
- u32 b;
-};
-
-struct SCX_ANSWER_PARAM_SIZE {
- u32 _ignored;
- u32 nSize;
-};
-
-union SCX_ANSWER_PARAM {
- struct SCX_ANSWER_PARAM_SIZE sSize;
- struct SCX_ANSWER_PARAM_VALUE sValue;
-};
-
-/*
- * Descriptor tables capacity
- */
-#define SCX_MAX_W3B_COARSE_PAGES (2)
-#define SCX_MAX_COARSE_PAGES (8)
-#define SCX_DESCRIPTOR_TABLE_CAPACITY_BIT_SHIFT (8)
-#define SCX_DESCRIPTOR_TABLE_CAPACITY \
- (1 << SCX_DESCRIPTOR_TABLE_CAPACITY_BIT_SHIFT)
-#define SCX_DESCRIPTOR_TABLE_CAPACITY_MASK \
- (SCX_DESCRIPTOR_TABLE_CAPACITY - 1)
-/* Shared memories coarse pages can map up to 1MB */
-#define SCX_MAX_COARSE_PAGE_MAPPED_SIZE \
- (PAGE_SIZE * SCX_DESCRIPTOR_TABLE_CAPACITY)
-/* Shared memories cannot exceed 8MB */
-#define SCX_MAX_SHMEM_SIZE \
- (SCX_MAX_COARSE_PAGE_MAPPED_SIZE << 3)
-
-/*
- * Buffer size for version description fields
- */
-#define SCX_DESCRIPTION_BUFFER_LENGTH 64
-
-/*
- * Shared memory type flags.
- */
-#define SCX_SHMEM_TYPE_READ (0x00000001)
-#define SCX_SHMEM_TYPE_WRITE (0x00000002)
-
-/*
- * Shared mem flags
- */
-#define SCX_SHARED_MEM_FLAG_INPUT 1
-#define SCX_SHARED_MEM_FLAG_OUTPUT 2
-#define SCX_SHARED_MEM_FLAG_INOUT 3
-
-
-/*
- * Parameter types
- */
-#define SCX_PARAM_TYPE_NONE 0x0
-#define SCX_PARAM_TYPE_VALUE_INPUT 0x1
-#define SCX_PARAM_TYPE_VALUE_OUTPUT 0x2
-#define SCX_PARAM_TYPE_VALUE_INOUT 0x3
-#define SCX_PARAM_TYPE_MEMREF_TEMP_INPUT 0x5
-#define SCX_PARAM_TYPE_MEMREF_TEMP_OUTPUT 0x6
-#define SCX_PARAM_TYPE_MEMREF_TEMP_INOUT 0x7
-#define SCX_PARAM_TYPE_MEMREF_INPUT 0xD
-#define SCX_PARAM_TYPE_MEMREF_OUTPUT 0xE
-#define SCX_PARAM_TYPE_MEMREF_INOUT 0xF
-
-#define SCX_PARAM_TYPE_MEMREF_FLAG 0x4
-#define SCX_PARAM_TYPE_REGISTERED_MEMREF_FLAG 0x8
-
-
-#define SCX_MAKE_PARAM_TYPES(t0, t1, t2, t3) \
- ((t0) | ((t1) << 4) | ((t2) << 8) | ((t3) << 12))
-#define SCX_GET_PARAM_TYPE(t, i) (((t) >> (4 * i)) & 0xF)
-
-/*
- * Login types.
- */
-#define SCX_LOGIN_PUBLIC 0x00000000
-#define SCX_LOGIN_USER 0x00000001
-#define SCX_LOGIN_GROUP 0x00000002
-#define SCX_LOGIN_APPLICATION 0x00000004
-#define SCX_LOGIN_APPLICATION_USER 0x00000005
-#define SCX_LOGIN_APPLICATION_GROUP 0x00000006
-#define SCX_LOGIN_AUTHENTICATION 0x80000000
-#define SCX_LOGIN_PRIVILEGED 0x80000002
-
-/* Login variants */
-
-#define SCX_LOGIN_VARIANT(mainType, os, variant) \
- ((mainType) | (1 << 27) | ((os) << 16) | ((variant) << 8))
-
-#define SCX_LOGIN_GET_MAIN_TYPE(type) \
- ((type) & ~SCX_LOGIN_VARIANT(0, 0xFF, 0xFF))
-
-#define SCX_LOGIN_OS_ANY 0x00
-#define SCX_LOGIN_OS_LINUX 0x01
-#define SCX_LOGIN_OS_ANDROID 0x04
-
-/* OS-independent variants */
-#define SCX_LOGIN_USER_NONE \
- SCX_LOGIN_VARIANT(SCX_LOGIN_USER, SCX_LOGIN_OS_ANY, 0xFF)
-#define SCX_LOGIN_GROUP_NONE \
- SCX_LOGIN_VARIANT(SCX_LOGIN_GROUP, SCX_LOGIN_OS_ANY, 0xFF)
-#define SCX_LOGIN_APPLICATION_USER_NONE \
- SCX_LOGIN_VARIANT(SCX_LOGIN_APPLICATION_USER, SCX_LOGIN_OS_ANY, 0xFF)
-#define SCX_LOGIN_AUTHENTICATION_BINARY_SHA1_HASH \
- SCX_LOGIN_VARIANT(SCX_LOGIN_AUTHENTICATION, SCX_LOGIN_OS_ANY, 0x01)
-#define SCX_LOGIN_PRIVILEGED_KERNEL \
- SCX_LOGIN_VARIANT(SCX_LOGIN_PRIVILEGED, SCX_LOGIN_OS_ANY, 0x01)
-
-/* Linux variants */
-#define SCX_LOGIN_USER_LINUX_EUID \
- SCX_LOGIN_VARIANT(SCX_LOGIN_USER, SCX_LOGIN_OS_LINUX, 0x01)
-#define SCX_LOGIN_GROUP_LINUX_GID \
- SCX_LOGIN_VARIANT(SCX_LOGIN_GROUP, SCX_LOGIN_OS_LINUX, 0x01)
-#define SCX_LOGIN_APPLICATION_LINUX_PATH_SHA1_HASH \
- SCX_LOGIN_VARIANT(SCX_LOGIN_APPLICATION, SCX_LOGIN_OS_LINUX, 0x01)
-#define SCX_LOGIN_APPLICATION_USER_LINUX_PATH_EUID_SHA1_HASH \
- SCX_LOGIN_VARIANT(SCX_LOGIN_APPLICATION_USER, SCX_LOGIN_OS_LINUX, 0x01)
-#define SCX_LOGIN_APPLICATION_GROUP_LINUX_PATH_GID_SHA1_HASH \
- SCX_LOGIN_VARIANT(SCX_LOGIN_APPLICATION_GROUP, SCX_LOGIN_OS_LINUX, 0x01)
-
-/* Android variants */
-#define SCX_LOGIN_USER_ANDROID_EUID \
- SCX_LOGIN_VARIANT(SCX_LOGIN_USER, SCX_LOGIN_OS_ANDROID, 0x01)
-#define SCX_LOGIN_GROUP_ANDROID_GID \
- SCX_LOGIN_VARIANT(SCX_LOGIN_GROUP, SCX_LOGIN_OS_ANDROID, 0x01)
-#define SCX_LOGIN_APPLICATION_ANDROID_UID \
- SCX_LOGIN_VARIANT(SCX_LOGIN_APPLICATION, SCX_LOGIN_OS_ANDROID, 0x01)
-#define SCX_LOGIN_APPLICATION_USER_ANDROID_UID_EUID \
- SCX_LOGIN_VARIANT(SCX_LOGIN_APPLICATION_USER, SCX_LOGIN_OS_ANDROID, \
- 0x01)
-#define SCX_LOGIN_APPLICATION_GROUP_ANDROID_UID_GID \
- SCX_LOGIN_VARIANT(SCX_LOGIN_APPLICATION_GROUP, SCX_LOGIN_OS_ANDROID, \
- 0x01)
-
-/*
- * return origins
- */
-#define SCX_ORIGIN_COMMS 2
-#define SCX_ORIGIN_TEE 3
-#define SCX_ORIGIN_TRUSTED_APP 4
-/*
- * The SCX message types.
- */
-#define SCX_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT 0x02
-#define SCX_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT 0xFD
-#define SCX_MESSAGE_TYPE_REGISTER_SHARED_MEMORY 0xF7
-#define SCX_MESSAGE_TYPE_RELEASE_SHARED_MEMORY 0xF9
-#define SCX_MESSAGE_TYPE_OPEN_CLIENT_SESSION 0xF0
-#define SCX_MESSAGE_TYPE_CLOSE_CLIENT_SESSION 0xF2
-#define SCX_MESSAGE_TYPE_INVOKE_CLIENT_COMMAND 0xF5
-#define SCX_MESSAGE_TYPE_CANCEL_CLIENT_COMMAND 0xF4
-#define SCX_MESSAGE_TYPE_MANAGEMENT 0xFE
-
-
-/*
- * The error codes
- */
-#define S_SUCCESS 0x00000000
-#define S_ERROR_NO_DATA 0xFFFF000B
-#define S_ERROR_OUT_OF_MEMORY 0xFFFF000C
-
-
-struct SCX_COMMAND_HEADER {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo;
- u32 nOperationID;
-};
-
-struct SCX_ANSWER_HEADER {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo;
- u32 nOperationID;
- u32 nErrorCode;
-};
-
-/*
- * CREATE_DEVICE_CONTEXT command message.
- */
-struct SCX_COMMAND_CREATE_DEVICE_CONTEXT {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo_RFU;
- u32 nOperationID;
- u32 nDeviceContextID;
-};
-
-/*
- * CREATE_DEVICE_CONTEXT answer message.
- */
-struct SCX_ANSWER_CREATE_DEVICE_CONTEXT {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo_RFU;
- /* an opaque Normal World identifier for the operation */
- u32 nOperationID;
- u32 nErrorCode;
- /* an opaque Normal World identifier for the device context */
- u32 hDeviceContext;
-};
-
-/*
- * DESTROY_DEVICE_CONTEXT command message.
- */
-struct SCX_COMMAND_DESTROY_DEVICE_CONTEXT {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo_RFU;
- u32 nOperationID;
- u32 hDeviceContext;
-};
-
-/*
- * DESTROY_DEVICE_CONTEXT answer message.
- */
-struct SCX_ANSWER_DESTROY_DEVICE_CONTEXT {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo_RFU;
- /* an opaque Normal World identifier for the operation */
- u32 nOperationID;
- u32 nErrorCode;
- u32 nDeviceContextID;
-};
-
-/*
- * OPEN_CLIENT_SESSION command message.
- */
-struct SCX_COMMAND_OPEN_CLIENT_SESSION {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nParamTypes;
- /* an opaque Normal World identifier for the operation */
- u32 nOperationID;
- u32 hDeviceContext;
- u32 nCancellationID;
- u64 sTimeout;
- struct SCX_UUID sDestinationUUID;
- union SCX_COMMAND_PARAM sParams[4];
- u32 nLoginType;
- /*
- * Size = 0 for public, [16] for group identification, [20] for
- * authentication
- */
- u8 sLoginData[20];
-};
-
-/*
- * OPEN_CLIENT_SESSION answer message.
- */
-struct SCX_ANSWER_OPEN_CLIENT_SESSION {
- u8 nMessageSize;
- u8 nMessageType;
- u8 nReturnOrigin;
- u8 __nReserved;
- /* an opaque Normal World identifier for the operation */
- u32 nOperationID;
- u32 nErrorCode;
- u32 hClientSession;
- union SCX_ANSWER_PARAM sAnswers[4];
-};
-
-/*
- * CLOSE_CLIENT_SESSION command message.
- */
-struct SCX_COMMAND_CLOSE_CLIENT_SESSION {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo_RFU;
- /* an opaque Normal World identifier for the operation */
- u32 nOperationID;
- u32 hDeviceContext;
- u32 hClientSession;
-};
-
-/*
- * CLOSE_CLIENT_SESSION answer message.
- */
-struct SCX_ANSWER_CLOSE_CLIENT_SESSION {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo_RFU;
- /* an opaque Normal World identifier for the operation */
- u32 nOperationID;
- u32 nErrorCode;
-};
-
-
-/*
- * REGISTER_SHARED_MEMORY command message
- */
-struct SCX_COMMAND_REGISTER_SHARED_MEMORY {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMemoryFlags;
- u32 nOperationID;
- u32 hDeviceContext;
- u32 nBlockID;
- u32 nSharedMemSize;
- u32 nSharedMemStartOffset;
- u32 nSharedMemDescriptors[SCX_MAX_COARSE_PAGES];
-};
-
-/*
- * REGISTER_SHARED_MEMORY answer message.
- */
-struct SCX_ANSWER_REGISTER_SHARED_MEMORY {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo_RFU;
- /* an opaque Normal World identifier for the operation */
- u32 nOperationID;
- u32 nErrorCode;
- u32 hBlock;
-};
-
-/*
- * RELEASE_SHARED_MEMORY command message.
- */
-struct SCX_COMMAND_RELEASE_SHARED_MEMORY {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo_RFU;
- /* an opaque Normal World identifier for the operation */
- u32 nOperationID;
- u32 hDeviceContext;
- u32 hBlock;
-};
-
-/*
- * RELEASE_SHARED_MEMORY answer message.
- */
-struct SCX_ANSWER_RELEASE_SHARED_MEMORY {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo_RFU;
- u32 nOperationID;
- u32 nErrorCode;
- u32 nBlockID;
-};
-
-/*
- * INVOKE_CLIENT_COMMAND command message.
- */
-struct SCX_COMMAND_INVOKE_CLIENT_COMMAND {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nParamTypes;
- u32 nOperationID;
- u32 hDeviceContext;
- u32 hClientSession;
- u64 sTimeout;
- u32 nCancellationID;
- u32 nClientCommandIdentifier;
- union SCX_COMMAND_PARAM sParams[4];
-};
-
-/*
- * INVOKE_CLIENT_COMMAND command answer.
- */
-struct SCX_ANSWER_INVOKE_CLIENT_COMMAND {
- u8 nMessageSize;
- u8 nMessageType;
- u8 nReturnOrigin;
- u8 __nReserved;
- u32 nOperationID;
- u32 nErrorCode;
- union SCX_ANSWER_PARAM sAnswers[4];
-};
-
-/*
- * CANCEL_CLIENT_OPERATION command message.
- */
-struct SCX_COMMAND_CANCEL_CLIENT_OPERATION {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo_RFU;
- /* an opaque Normal World identifier for the operation */
- u32 nOperationID;
- u32 hDeviceContext;
- u32 hClientSession;
- u32 nCancellationID;
-};
-
-struct SCX_ANSWER_CANCEL_CLIENT_OPERATION {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nMessageInfo_RFU;
- u32 nOperationID;
- u32 nErrorCode;
-};
-
-/*
- * MANAGEMENT command message.
- */
-struct SCX_COMMAND_MANAGEMENT {
- u8 nMessageSize;
- u8 nMessageType;
- u16 nCommand;
- u32 nOperationID;
- u32 nW3BSize;
- u32 nW3BStartOffset;
- u32 nSharedMemDescriptors[1];
-};
-
-/*
- * POWER_MANAGEMENT answer message.
- * The message does not provide message specific parameters.
- * Therefore no need to define a specific answer structure
- */
-
-/*
- * Structure for L2 messages
- */
-union SCX_COMMAND_MESSAGE {
- struct SCX_COMMAND_HEADER sHeader;
- struct SCX_COMMAND_CREATE_DEVICE_CONTEXT sCreateDeviceContextMessage;
- struct SCX_COMMAND_DESTROY_DEVICE_CONTEXT sDestroyDeviceContextMessage;
- struct SCX_COMMAND_OPEN_CLIENT_SESSION sOpenClientSessionMessage;
- struct SCX_COMMAND_CLOSE_CLIENT_SESSION sCloseClientSessionMessage;
- struct SCX_COMMAND_REGISTER_SHARED_MEMORY sRegisterSharedMemoryMessage;
- struct SCX_COMMAND_RELEASE_SHARED_MEMORY sReleaseSharedMemoryMessage;
- struct SCX_COMMAND_INVOKE_CLIENT_COMMAND sInvokeClientCommandMessage;
- struct SCX_COMMAND_CANCEL_CLIENT_OPERATION
- sCancelClientOperationMessage;
- struct SCX_COMMAND_MANAGEMENT sManagementMessage;
-};
-
-/*
- * Structure for any L2 answer
- */
-
-union SCX_ANSWER_MESSAGE {
- struct SCX_ANSWER_HEADER sHeader;
- struct SCX_ANSWER_CREATE_DEVICE_CONTEXT sCreateDeviceContextAnswer;
- struct SCX_ANSWER_OPEN_CLIENT_SESSION sOpenClientSessionAnswer;
- struct SCX_ANSWER_CLOSE_CLIENT_SESSION sCloseClientSessionAnswer;
- struct SCX_ANSWER_REGISTER_SHARED_MEMORY sRegisterSharedMemoryAnswer;
- struct SCX_ANSWER_RELEASE_SHARED_MEMORY sReleaseSharedMemoryAnswer;
- struct SCX_ANSWER_INVOKE_CLIENT_COMMAND sInvokeClientCommandAnswer;
- struct SCX_ANSWER_DESTROY_DEVICE_CONTEXT sDestroyDeviceContextAnswer;
- struct SCX_ANSWER_CANCEL_CLIENT_OPERATION sCancelClientOperationAnswer;
-};
-
-/* Structure of the Communication Buffer */
-struct SCHANNEL_C1S_BUFFER {
- u32 nConfigFlags_S;
- u32 nW3BSizeMax_S;
- u32 nReserved0;
- u32 nW3BSizeCurrent_S;
- u8 sReserved1[48];
- u8 sVersionDescription[SCX_DESCRIPTION_BUFFER_LENGTH];
- u32 nStatus_S;
- u32 sReserved2;
- u32 nSyncSerial_N;
- u32 nSyncSerial_S;
- u64 sTime_N[2];
- u64 sTimeout_S[2];
- u32 nFirstCommand;
- u32 nFirstFreeCommand;
- u32 nFirstAnswer;
- u32 nFirstFreeAnswer;
- u32 nW3BDescriptors[128];
- #ifdef CONFIG_TF_MSHIELD
- u8 sRPCTraceBuffer[140];
- u8 sRPCShortcutBuffer[180];
- #else
- u8 sReserved3[320];
- #endif
- u32 sCommandQueue[SCX_N_MESSAGE_QUEUE_CAPACITY];
- u32 sAnswerQueue[SCX_S_ANSWER_QUEUE_CAPACITY];
-};
-
-
-/*
- * SCX_VERSION_INFORMATION_BUFFER structure description
- * Description of the sVersionBuffer handed over from user space to kernel space
- * This field is filled by the driver during a CREATE_DEVICE_CONTEXT ioctl
- * and handed back to user space
- */
-struct SCX_VERSION_INFORMATION_BUFFER {
- u8 sDriverDescription[65];
- u8 sSecureWorldDescription[65];
-};
-
-
-/* The IOCTLs the driver supports */
-#include <linux/ioctl.h>
-
-#define IOCTL_SCX_GET_VERSION _IO('z', 0)
-#define IOCTL_SCX_EXCHANGE _IOWR('z', 1, union SCX_COMMAND_MESSAGE)
-#define IOCTL_SCX_GET_DESCRIPTION _IOR('z', 2, \
- struct SCX_VERSION_INFORMATION_BUFFER)
-
-#endif /* !defined(__SCX_PROTOCOL_H__) */
diff --git a/security/smc/omap4/scx_public_crypto.c b/security/smc/omap4/scx_public_crypto.c
deleted file mode 100644
index d6b751c..0000000
--- a/security/smc/omap4/scx_public_crypto.c
+++ /dev/null
@@ -1,355 +0,0 @@
-/*
- * Copyright (c) 2006-2010 Trusted Logic S.A.
- * 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 "scxlnx_defs.h"
-#include "scxlnx_util.h"
-#include "scxlnx_mshield.h"
-#include "scx_public_crypto.h"
-#include "scx_public_dma.h"
-
-#define IO_ADDRESS OMAP2_L4_IO_ADDRESS
-
-#define S_SUCCESS 0x00000000
-#define S_ERROR_GENERIC 0xFFFF0000
-#define S_ERROR_ACCESS_DENIED 0xFFFF0001
-#define S_ERROR_BAD_FORMAT 0xFFFF0005
-#define S_ERROR_BAD_PARAMETERS 0xFFFF0006
-#define S_ERROR_OUT_OF_MEMORY 0xFFFF000C
-#define S_ERROR_SHORT_BUFFER 0xFFFF0010
-#define S_ERROR_UNREACHABLE 0xFFFF3013
-#define S_ERROR_SERVICE 0xFFFF1000
-
-#define CKR_OK 0x00000000
-
-#define PUBLIC_CRYPTO_TIMEOUT_CONST 0x000FFFFF
-
-#define RPC_AES1_CODE PUBLIC_CRYPTO_HWA_AES1
-#define RPC_AES2_CODE PUBLIC_CRYPTO_HWA_AES2
-#define RPC_DES_CODE PUBLIC_CRYPTO_HWA_DES
-#define RPC_SHA_CODE PUBLIC_CRYPTO_HWA_SHA
-
-#define RPC_CRYPTO_COMMAND_MASK 0x000003c0
-
-#define RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR 0x200
-#define RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR_UNLOCK 0x000
-#define RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR_LOCK 0x001
-
-#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT 0x240
-#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_LOCK_AES1 RPC_AES1_CODE
-#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_LOCK_AES2 RPC_AES2_CODE
-#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_LOCK_DES RPC_DES_CODE
-#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_LOCK_SHA RPC_SHA_CODE
-#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_SUSPEND 0x010
-#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_UNINSTALL 0x020
-
-#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS 0x280
-#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_AES1 RPC_AES1_CODE
-#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_AES2 RPC_AES2_CODE
-#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_DES RPC_DES_CODE
-#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_SHA RPC_SHA_CODE
-#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_RESUME 0x010
-
-#define RPC_CLEAR_GLOBAL_KEY_CONTEXT 0x2c0
-#define RPC_CLEAR_GLOBAL_KEY_CONTEXT_CLEARED_AES 0x001
-#define RPC_CLEAR_GLOBAL_KEY_CONTEXT_CLEARED_DES 0x002
-
-#define ENABLE_CLOCK true
-#define DISABLE_CLOCK false
-
-/*---------------------------------------------------------------------------*/
-/*RPC IN/OUT structures for CUS implementation */
-/*---------------------------------------------------------------------------*/
-
-struct RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR_OUT {
- u32 nShortcutID;
- u32 nError;
-};
-
-struct RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR_IN {
- u32 nDeviceContextID;
- u32 hClientSession;
- u32 nCommandID;
- u32 hKeyContext;
- /**
- *The identifier of the HWA accelerator that this shortcut uses!
- *Possible values are:
- *- 1 (RPC_AES1_CODE)
- *- 2 (RPC_AES2_CODE)
- *- 4 (RPC_DES_CODE)
- *- 8 (RPC_SHA_CODE)
- **/
- u32 nHWAID;
- /**
- *This field defines the algorithm, direction, mode, key size.
- *It contains some of the bits of the corresponding "CTRL" register
- *of the accelerator.
- *
- *More precisely:
- *For AES1 accelerator, nHWA_CTRL contains the following bits:
- *- CTR (bit 6):
- * when 1, selects CTR mode.
- * when 0, selects CBC or ECB mode (according to CBC bit)
- *- CBC (bit 5)
- * when 1, selects CBC mode (but only if CTR=0)
- * when 0, selects EBC mode (but only if CTR=0)
- *- DIRECTION (bit 2)
- * 0: decryption
- * 1: encryption
- *
- *For the DES2 accelerator, nHWA_CTRL contains the following bits:
- *- CBC (bit 4): 1 for CBC, 0 for ECB
- *- DIRECTION (bit 2): 0 for decryption, 1 for encryption
- *
- *For the SHA accelerator, nHWA_CTRL contains the following bits:
- *- ALGO (bit 2:1):
- * 0x0: MD5
- * 0x1: SHA1
- * 0x2: SHA-224
- * 0x3: SHA-256
- **/
- u32 nHWA_CTRL;
- union PUBLIC_CRYPTO_OPERATION_STATE sOperationState;
-};
-
-struct RPC_LOCK_HWA_SUSPEND_SHORTCUT_OUT {
- union PUBLIC_CRYPTO_OPERATION_STATE sOperationState;
-};
-
-struct RPC_LOCK_HWA_SUSPEND_SHORTCUT_IN {
- u32 nShortcutID;
-};
-
-struct RPC_RESUME_SHORTCUT_UNLOCK_HWA_IN {
- u32 nShortcutID;
- u32 hAES1KeyContext;
- u32 hAES2KeyContext;
- u32 hDESKeyContext;
- union PUBLIC_CRYPTO_OPERATION_STATE sOperationState;
-};
-
-/*------------------------------------------------------------------------- */
-/*
- * HWA public lock or unlock one HWA according algo specified by nHWAID
- */
-void PDrvCryptoLockUnlockHWA(u32 nHWAID, bool bDoLock)
-{
- int is_sem = 0;
- struct semaphore *s = NULL;
- struct mutex *m = NULL;
- struct SCXLNX_DEVICE *dev = SCXLNXGetDevice();
-
- dprintk(KERN_INFO "PDrvCryptoLockUnlockHWA:nHWAID=0x%04X bDoLock=%d\n",
- nHWAID, bDoLock);
-
- switch (nHWAID) {
- case RPC_AES1_CODE:
- s = &dev->sAES1CriticalSection;
- is_sem = 1;
- break;
- case RPC_AES2_CODE:
- s = &dev->sAES2CriticalSection;
- is_sem = 1;
- break;
- case RPC_DES_CODE:
- m = &dev->sDESCriticalSection;
- break;
- default:
- case RPC_SHA_CODE:
- m = &dev->sSHACriticalSection;
- break;
- }
-
- if (bDoLock == LOCK_HWA) {
- dprintk(KERN_INFO "PDrvCryptoLockUnlockHWA: "
- "Wait for HWAID=0x%04X\n", nHWAID);
- if (is_sem) {
- while (down_trylock(s))
- cpu_relax();
- } else {
- while (!mutex_trylock(m))
- cpu_relax();
- }
- dprintk(KERN_INFO "PDrvCryptoLockUnlockHWA: "
- "Locked on HWAID=0x%04X\n", nHWAID);
- } else {
- if (is_sem)
- up(s);
- else
- mutex_unlock(m);
- dprintk(KERN_INFO "PDrvCryptoLockUnlockHWA: "
- "Released for HWAID=0x%04X\n", nHWAID);
- }
-}
-
-/*------------------------------------------------------------------------- */
-/**
- *Initialize the public crypto DMA channels, global HWA semaphores and handles
- */
-u32 SCXPublicCryptoInit(void)
-{
- struct SCXLNX_DEVICE *pDevice = SCXLNXGetDevice();
- u32 nError = PUBLIC_CRYPTO_OPERATION_SUCCESS;
-
- /* Initialize HWAs */
- PDrvCryptoAESInit();
- PDrvCryptoDigestInit();
-
- /*initialize the HWA semaphores */
- sema_init(&pDevice->sAES1CriticalSection, 1);
- sema_init(&pDevice->sAES2CriticalSection, 1);
- mutex_init(&pDevice->sSHACriticalSection);
-
- /*initialize the current key handle loaded in the AESn/DES HWA */
- pDevice->hAES1SecureKeyContext = 0;
- pDevice->hAES2SecureKeyContext = 0;
- pDevice->bSHAM1IsPublic = false;
-
- /*initialize the DMA semaphores */
- mutex_init(&pDevice->sm.sDMALock);
-
- /*allocate DMA buffer */
- pDevice->nDMABufferLength = PAGE_SIZE * 16;
- pDevice->pDMABuffer = dma_alloc_coherent(NULL,
- pDevice->nDMABufferLength,
- &(pDevice->pDMABufferPhys),
- GFP_KERNEL);
- if (pDevice->pDMABuffer == NULL) {
- printk(KERN_ERR
- "SCXPublicCryptoInit: Out of memory for DMA buffer\n");
- nError = S_ERROR_OUT_OF_MEMORY;
- }
-
- return nError;
-}
-
-/*------------------------------------------------------------------------- */
-/*
- *Initialize the device context CUS fields (shortcut semaphore and public CUS
- *list)
- */
-void SCXPublicCryptoInitDeviceContext(struct SCXLNX_CONNECTION *pDeviceContext)
-{
- /*initialize the CUS list in the given device context */
- spin_lock_init(&(pDeviceContext->shortcutListCriticalSectionLock));
- INIT_LIST_HEAD(&(pDeviceContext->ShortcutList));
-}
-
-/*------------------------------------------------------------------------- */
-/**
- *Terminate the public crypto (including DMA)
- */
-void SCXPublicCryptoTerminate()
-{
- struct SCXLNX_DEVICE *pDevice = SCXLNXGetDevice();
-
- if (pDevice->pDMABuffer != NULL) {
- dma_free_coherent(NULL, pDevice->nDMABufferLength,
- pDevice->pDMABuffer,
- pDevice->pDMABufferPhys);
- pDevice->pDMABuffer = NULL;
- }
-
- PDrvCryptoDigestExit();
- PDrvCryptoAESExit();
-}
-
-/*------------------------------------------------------------------------- */
-
-void SCXPublicCryptoWaitForReadyBitInfinitely(u32 *pRegister, u32 vBit)
-{
- while (!(INREG32(pRegister) & vBit))
- ;
-}
-
-/*------------------------------------------------------------------------- */
-
-u32 SCXPublicCryptoWaitForReadyBit(u32 *pRegister, u32 vBit)
-{
- u32 timeoutCounter = PUBLIC_CRYPTO_TIMEOUT_CONST;
-
- while ((!(INREG32(pRegister) & vBit)) && ((--timeoutCounter) != 0))
- ;
-
- if (timeoutCounter == 0)
- return PUBLIC_CRYPTO_ERR_TIMEOUT;
-
- return PUBLIC_CRYPTO_OPERATION_SUCCESS;
-}
-
-/*------------------------------------------------------------------------- */
-
-static DEFINE_SPINLOCK(clk_lock);
-
-void SCXPublicCryptoDisableClock(uint32_t vClockPhysAddr)
-{
- u32 *pClockReg;
- u32 val;
- unsigned long flags;
-
- dprintk(KERN_INFO "SCXPublicCryptoDisableClock: " \
- "vClockPhysAddr=0x%08X\n",
- vClockPhysAddr);
-
- /* Ensure none concurrent access when changing clock registers */
- spin_lock_irqsave(&clk_lock, flags);
-
- pClockReg = (u32 *)IO_ADDRESS(vClockPhysAddr);
-
- val = __raw_readl(pClockReg);
- val &= ~(0x3);
- __raw_writel(val, pClockReg);
-
- /* Wait for clock to be fully disabled */
- while ((__raw_readl(pClockReg) & 0x30000) == 0)
- ;
-
- spin_unlock_irqrestore(&clk_lock, flags);
-
- tf_l4sec_clkdm_allow_idle(false, true);
-}
-
-/*------------------------------------------------------------------------- */
-
-void SCXPublicCryptoEnableClock(uint32_t vClockPhysAddr)
-{
- u32 *pClockReg;
- u32 val;
- unsigned long flags;
-
- dprintk(KERN_INFO "SCXPublicCryptoEnableClock: " \
- "vClockPhysAddr=0x%08X\n",
- vClockPhysAddr);
-
- tf_l4sec_clkdm_wakeup(false, true);
-
- /* Ensure none concurrent access when changing clock registers */
- spin_lock_irqsave(&clk_lock, flags);
-
- pClockReg = (u32 *)IO_ADDRESS(vClockPhysAddr);
-
- val = __raw_readl(pClockReg);
- val |= 0x2;
- __raw_writel(val, pClockReg);
-
- /* Wait for clock to be fully enabled */
- while ((__raw_readl(pClockReg) & 0x30000) != 0)
- ;
-
- spin_unlock_irqrestore(&clk_lock, flags);
-}
-
diff --git a/security/smc/omap4/scx_public_dma.c b/security/smc/omap4/scx_public_dma.c
deleted file mode 100644
index 743c333..0000000
--- a/security/smc/omap4/scx_public_dma.c
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright (c) 2006-2010 Trusted Logic S.A.
- * 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 "scxlnx_defs.h"
-#include "scxlnx_util.h"
-#include "scx_public_dma.h"
-
-#include <asm/atomic.h>
-
-static atomic_t g_dmaEventFlag = ATOMIC_INIT(0);
-
-/*------------------------------------------------------------------------ */
-/*
- * Internal functions
- */
-
-static void scxPublicDMACallback(int lch, u16 ch_status, void *data)
-{
- atomic_inc(&g_dmaEventFlag);
-}
-
-/*------------------------------------------------------------------------ */
-/*
- * Public DMA API
- */
-
-u32 scxPublicDMARequest(int *lch)
-{
- int dma_ch_out = 0;
-
- if (lch == NULL)
- return PUBLIC_CRYPTO_ERR_BAD_PARAMETERS;
-
- if (omap_request_dma(0, "SMC Public Crypto",
- scxPublicDMACallback, NULL, &dma_ch_out) != 0)
- return PUBLIC_CRYPTO_ERR_OUT_OF_MEMORY;
-
- omap_disable_dma_irq(dma_ch_out, OMAP_DMA_DROP_IRQ |
- OMAP_DMA_BLOCK_IRQ);
-
- *lch = dma_ch_out;
-
- return PUBLIC_CRYPTO_OPERATION_SUCCESS;
-}
-
-/*------------------------------------------------------------------------ */
-/*
- * Release a DMA channel
- */
-u32 scxPublicDMARelease(int lch)
-{
- omap_free_dma(lch);
-
- return PUBLIC_CRYPTO_OPERATION_SUCCESS;
-}
-
-/*------------------------------------------------------------------------ */
-
-void scxPublicDMASetParams(int lch, struct omap_dma_channel_params *pParams)
-{
- omap_set_dma_params(lch, pParams);
-}
-
-/*------------------------------------------------------------------------ */
-
-void scxPublicDMAStart(int lch, int interruptMask)
-{
- atomic_set(&g_dmaEventFlag, 0);
- omap_enable_dma_irq(lch, interruptMask);
- omap_start_dma(lch);
-}
-
-/*------------------------------------------------------------------------ */
-
-void scxPublicDMADisableChannel(int lch)
-{
- omap_stop_dma(lch);
-}
-
-/*------------------------------------------------------------------------ */
-
-void scxPublicDMAClearChannel(int lch)
-{
- omap_clear_dma(lch);
-}
-
-/*------------------------------------------------------------------------ */
-
-void scxPublicDMAWait(int nr_of_cb)
-{
- while (atomic_read(&g_dmaEventFlag) < nr_of_cb)
- cpu_relax();
-}
-
-/*------------------------------------------------------------------------ */
-/*
- * Perform common DMA channel setup, used to factorize the code
- *
- * Output: struct omap_dma_channel_params *pDMAChannel
- * Inputs: u32 nbBlocks Number of block of the transfer
- * u32 nbElements Number of elements of the transfer
- * u32 nDstStart Destination address
- * u32 nSrcStart Source address
- * u32 nTriggerID Trigger ID
- */
-void scxPublicSetDMAChannelCommonParams(
- struct omap_dma_channel_params *pDMAChannel,
- u32 nbBlocks, u32 nbElements,
- u32 nDstStart, u32 nSrcStart, u32 nTriggerID)
-{
- pDMAChannel->data_type = OMAP_DMA_DATA_TYPE_S32;
- pDMAChannel->elem_count = nbElements;
- pDMAChannel->frame_count = nbBlocks;
- pDMAChannel->src_ei = 0;
- pDMAChannel->src_fi = 0;
- pDMAChannel->dst_ei = 0;
- pDMAChannel->dst_fi = 0;
- pDMAChannel->sync_mode = OMAP_DMA_SYNC_FRAME;
- pDMAChannel->src_start = nSrcStart;
- pDMAChannel->dst_start = nDstStart;
- pDMAChannel->trigger = nTriggerID;
-}
diff --git a/security/smc/omap4/scx_public_dma.h b/security/smc/omap4/scx_public_dma.h
deleted file mode 100644
index ddd19b2..0000000
--- a/security/smc/omap4/scx_public_dma.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2006-2010 Trusted Logic S.A.
- * 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
- */
-
-#ifndef __SCX_PUBLIC_DMA_H
-#define __SCX_PUBLIC_DMA_H
-
-#include <linux/dma-mapping.h>
-#include <plat/dma.h>
-#include <plat/dma-44xx.h>
-
-#include "scx_public_crypto.h"
-
-/*---------------------------------------------------------------------------
- * Cache management (implemented in the assembler file)
- *-------------------------------------------------------------------------- */
-
-u32 v7_dma_flush_range(u32 nVAStart, u32 nVAEnd);
-u32 v7_dma_inv_range(u32 nVAStart, u32 nVAEnd);
-
-/*-------------------------------------------------------------------------- */
-/*
- * Public DMA API
- */
-
-/*
- * CEN Masks
- */
-#define DMA_CEN_Elts_per_Frame_AES 4
-#define DMA_CEN_Elts_per_Frame_DES 2
-#define DMA_CEN_Elts_per_Frame_SHA 16
-
-/*
- * Request a DMA channel
- */
-u32 scxPublicDMARequest(int *lch);
-
-/*
- * Release a DMA channel
- */
-u32 scxPublicDMARelease(int lch);
-
-/**
- * This function waits for the DMA IRQ.
- */
-void scxPublicDMAWait(int nr_of_cb);
-
-/*
- * This function starts a DMA operation.
- *
- * lch DMA channel ID.
- * interruptMask Configures the Channel Interrupt Control Register.
- */
-void scxPublicDMAStart(int lch, int interruptMask);
-
-void scxPublicSetDMAChannelCommonParams(
- struct omap_dma_channel_params *pDMAChannel,
- u32 nbBlocks, u32 nbElements, u32 nDstStart,
- u32 nSrcStart, u32 nTriggerID);
-void scxPublicDMASetParams(int lch, struct omap_dma_channel_params *pParams);
-void scxPublicDMADisableChannel(int lch);
-void scxPublicDMAClearChannel(int lch);
-
-#endif /*__SCX_PUBLIC_DMA_H */
diff --git a/security/smc/omap4/scxlnx_comm_mshield.c b/security/smc/omap4/scxlnx_comm_mshield.c
deleted file mode 100644
index ccd2098..0000000
--- a/security/smc/omap4/scxlnx_comm_mshield.c
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (c) 2010 Trusted Logic S.A.
- * 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 <asm/div64.h>
-#include <asm/system.h>
-#include <asm/cputype.h>
-#include <linux/uaccess.h>
-#include <linux/io.h>
-#include <linux/interrupt.h>
-#include <linux/page-flags.h>
-#include <linux/pagemap.h>
-#include <linux/vmalloc.h>
-#include <linux/version.h>
-#include <linux/jiffies.h>
-#include <linux/dma-mapping.h>
-#include <linux/cpu.h>
-
-#include <asm/cacheflush.h>
-
-#include <clockdomain.h>
-
-#include "scxlnx_defs.h"
-
-#ifdef CONFIG_HAS_WAKELOCK
-static struct wake_lock g_tf_wake_lock;
-static atomic_t tf_wake_lock_count = ATOMIC_INIT(0);
-#endif
-
-static struct clockdomain *smc_l4_sec_clkdm;
-static atomic_t smc_l4_sec_clkdm_use_count = ATOMIC_INIT(0);
-
-static int __init tf_early_init(void)
-{
- smc_l4_sec_clkdm = clkdm_lookup("l4_secure_clkdm");
- if (smc_l4_sec_clkdm == NULL)
- return -EFAULT;
-
-#ifdef CONFIG_HAS_WAKELOCK
- wake_lock_init(&g_tf_wake_lock, WAKE_LOCK_SUSPEND,
- SCXLNX_DEVICE_BASE_NAME);
-#endif
-
- return 0;
-}
-early_initcall(tf_early_init);
-
-/*--------------------------------------------------------------------------
- * L4 SEC Clock domain handling
- *-------------------------------------------------------------------------- */
-
-void tf_l4sec_clkdm_wakeup(bool use_spin_lock, bool wakelock)
-{
- if (use_spin_lock)
- spin_lock(&SCXLNXGetDevice()->sm.lock);
-#ifdef CONFIG_HAS_WAKELOCK
- if (wakelock) {
- atomic_inc(&tf_wake_lock_count);
- wake_lock(&g_tf_wake_lock);
- }
-#endif
- atomic_inc(&smc_l4_sec_clkdm_use_count);
- clkdm_wakeup(smc_l4_sec_clkdm);
- if (use_spin_lock)
- spin_unlock(&SCXLNXGetDevice()->sm.lock);
-}
-
-void tf_l4sec_clkdm_allow_idle(bool use_spin_lock, bool wakeunlock)
-{
- if (use_spin_lock)
- spin_lock(&SCXLNXGetDevice()->sm.lock);
- if (atomic_dec_return(&smc_l4_sec_clkdm_use_count) == 0)
- clkdm_allow_idle(smc_l4_sec_clkdm);
-#ifdef CONFIG_HAS_WAKELOCK
- if (wakeunlock)
- if (atomic_dec_return(&tf_wake_lock_count) == 0)
- wake_unlock(&g_tf_wake_lock);
-#endif
- if (use_spin_lock)
- spin_unlock(&SCXLNXGetDevice()->sm.lock);
-}
-
diff --git a/security/smc/omap4/scxlnx_device.c b/security/smc/omap4/scxlnx_device.c
deleted file mode 100644
index cd9d56b..0000000
--- a/security/smc/omap4/scxlnx_device.c
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * Copyright (c) 2006-2010 Trusted Logic S.A.
- * 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 <asm/atomic.h>
-#include <linux/uaccess.h>
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/mm.h>
-#include <linux/page-flags.h>
-#include <linux/pm.h>
-#include <linux/sysdev.h>
-#include <linux/vmalloc.h>
-#include <linux/signal.h>
-#ifdef CONFIG_ANDROID
-#include <linux/device.h>
-#endif
-
-#include "scx_protocol.h"
-#include "scxlnx_defs.h"
-#include "scxlnx_util.h"
-#ifdef CONFIG_TF_MSHIELD
-#include <plat/cpu.h>
-#include "scx_public_crypto.h"
-#endif
-
-/* The single device supported by this driver */
-static struct SCXLNX_DEVICE g_SCXLNXDevice = {0, };
-
-/*----------------------------------------------------------------------------
- * Implementations
- *----------------------------------------------------------------------------*/
-
-struct SCXLNX_DEVICE *SCXLNXGetDevice(void)
-{
- return &g_SCXLNXDevice;
-}
-
-/*----------------------------------------------------------------------------*/
-
-static int __init register_dmcrypt_engines(void)
-{
- int ret;
-
- printk(KERN_INFO "Entered register_dmcrypt_engines");
-
- ret = SCXPublicCryptoInit();
- if (ret) {
- printk(KERN_ERR "register_dmcrypt_engines():"
- " SCXPublicCryptoInit failed, (error %d)!\n", ret);
- goto out;
- }
-
- ret = register_smc_public_crypto_aes();
- if (ret) {
- printk(KERN_ERR "register_dmcrypt_engines():"
- " regiser_smc_public_crypto_aes failed, (error %d)!\n", ret);
- goto out;
- }
-
- ret = register_smc_public_crypto_digest();
- if (ret) {
- printk(KERN_ERR "register_dmcrypt_engines():"
- " regiser_smc_public_crypto_digest failed, (error %d)!\n", ret);
- goto out;
- }
-
-out:
- return ret;
-}
-module_init(register_dmcrypt_engines);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Trusted Logic S.A.");
diff --git a/security/smc/rproc_drm.c b/security/smc/rproc_drm.c
new file mode 100644
index 0000000..b86b0b8
--- /dev/null
+++ b/security/smc/rproc_drm.c
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2011 Texas Instruments, Inc.
+ * Copyright (c) 2011 Trusted Logic S.A.
+ *
+ * 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.
+ */
+
+/*
+ * This file implements the non-secure rproc and smc interface/integration
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+
+#include "tee_client_api.h"
+#include "tf_defs.h"
+
+/* 7B1DD682-1077-4939-9755-B6192C5CC5FD */
+#define WVDRM_UUID {0x7B1DD682, 0x1077, 0x4939, \
+ {0x97, 0x55, 0xB6, 0x19, 0x2C, 0x5C, 0xC5, 0xFD} }
+
+#define WVDRM_ENTER_SECURE_PLAYBACK 0x00003000
+
+#define WVDRM_EXIT_SECURE_PLAYBACK 0x00003001
+
+enum rproc_drm_s_state {
+ RPROC_DRM_SECURE_LEAVE,
+ RPROC_DRM_SECURE_ENTER
+};
+
+static enum rproc_drm_s_state s_state;
+
+static TEEC_Result rproc_drm_initialize(TEEC_Context *teec_context,
+ TEEC_Session *teec_session)
+{
+ static const TEEC_UUID drm_uuid = WVDRM_UUID;
+ static u32 drm_gid = 1019;
+ TEEC_Result result;
+
+ result = TEEC_InitializeContext(NULL, teec_context);
+ if (result != TEEC_SUCCESS)
+ goto exit;
+
+ result = TEEC_OpenSession(teec_context, teec_session, &drm_uuid,
+ TEEC_LOGIN_PRIVILEGED, &drm_gid, NULL, NULL);
+ if (result != TEEC_SUCCESS)
+ TEEC_FinalizeContext(teec_context);
+
+exit:
+ return result;
+}
+
+static TEEC_Result rproc_drm_finalize(TEEC_Context *teec_context,
+ TEEC_Session *teec_session)
+{
+ TEEC_CloseSession(teec_session);
+ TEEC_FinalizeContext(teec_context);
+ return TEEC_SUCCESS;
+}
+
+static TEEC_Result _rproc_drm_invoke_secure_service(bool enable)
+{
+ TEEC_Result result;
+ TEEC_Operation operation;
+ TEEC_Context teec_context;
+ TEEC_Session teec_session;
+ u32 command;
+
+ result = rproc_drm_initialize(&teec_context, &teec_session);
+ if (result != TEEC_SUCCESS)
+ goto out;
+
+ operation.paramTypes = TEEC_PARAM_TYPES(TEEC_NONE, TEEC_NONE,
+ TEEC_NONE, TEEC_NONE);
+ command = (enable ? WVDRM_ENTER_SECURE_PLAYBACK :
+ WVDRM_EXIT_SECURE_PLAYBACK);
+ result = TEEC_InvokeCommand(&teec_session, command, &operation, NULL);
+ rproc_drm_finalize(&teec_context, &teec_session);
+out:
+ return result;
+}
+
+int rproc_drm_invoke_service(bool enable)
+{
+ int ret;
+
+ if ((s_state == RPROC_DRM_SECURE_ENTER && enable) ||
+ (s_state == RPROC_DRM_SECURE_LEAVE && !enable))
+ return 0;
+
+ ret = _rproc_drm_invoke_secure_service(enable);
+ s_state = (enum rproc_drm_s_state) enable;
+
+ return ret == TEEC_SUCCESS ? 0 : -EACCES;
+}
+EXPORT_SYMBOL(rproc_drm_invoke_service);
diff --git a/security/smc/s_version.h b/security/smc/s_version.h
new file mode 100644
index 0000000..a16d548
--- /dev/null
+++ b/security/smc/s_version.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2010 Trusted Logic S.A.
+ * 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
+ */
+
+#ifndef __S_VERSION_H__
+#define __S_VERSION_H__
+
+/*
+ * Usage: define S_VERSION_BUILD on the compiler's command line.
+ *
+ * Then set:
+ * - S_VERSION_OS
+ * - S_VERSION_PLATFORM
+ * - S_VERSION_MAIN
+ * - S_VERSION_ENG is optional
+ * - S_VERSION_PATCH is optional
+ * - S_VERSION_BUILD = 0 if S_VERSION_BUILD not defined or empty
+ */
+
+#define S_VERSION_OS "A" /* "A" for all Android */
+#define S_VERSION_PLATFORM "G" /* "G" for 4430 */
+
+/*
+ * This version number must be updated for each new release
+ */
+#define S_VERSION_MAIN "01.04"
+
+/*
+* If this is a patch or engineering version use the following
+* defines to set the version number. Else set these values to 0.
+*/
+#define S_VERSION_PATCH 6
+#define S_VERSION_ENG 0
+
+#ifdef S_VERSION_BUILD
+/* TRICK: detect if S_VERSION is defined but empty */
+#if 0 == S_VERSION_BUILD-0
+#undef S_VERSION_BUILD
+#define S_VERSION_BUILD 0
+#endif
+#else
+/* S_VERSION_BUILD is not defined */
+#define S_VERSION_BUILD 0
+#endif
+
+#define __STRINGIFY(X) #X
+#define __STRINGIFY2(X) __STRINGIFY(X)
+
+#if S_VERSION_ENG != 0
+#define _S_VERSION_ENG "e" __STRINGIFY2(S_VERSION_ENG)
+#else
+#define _S_VERSION_ENG ""
+#endif
+
+#if S_VERSION_PATCH != 0
+#define _S_VERSION_PATCH "p" __STRINGIFY2(S_VERSION_PATCH)
+#else
+#define _S_VERSION_PATCH ""
+#endif
+
+#if !defined(NDEBUG) || defined(_DEBUG)
+#define S_VERSION_VARIANT "D "
+#else
+#define S_VERSION_VARIANT " "
+#endif
+
+#define S_VERSION_STRING \
+ "SMC" \
+ S_VERSION_OS \
+ S_VERSION_PLATFORM \
+ S_VERSION_MAIN \
+ _S_VERSION_PATCH \
+ _S_VERSION_ENG \
+ "." __STRINGIFY2(S_VERSION_BUILD) " " \
+ S_VERSION_VARIANT
+
+#endif /* __S_VERSION_H__ */
diff --git a/security/smc/tee_client_api.h b/security/smc/tee_client_api.h
new file mode 100644
index 0000000..d57be69
--- /dev/null
+++ b/security/smc/tee_client_api.h
@@ -0,0 +1,189 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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
+ */
+
+/*
+ * This header file corresponds to V1.0 of the GlobalPlatform
+ * TEE Client API Specification
+ */
+#ifndef __TEE_CLIENT_API_H__
+#define __TEE_CLIENT_API_H__
+
+#include <linux/types.h>
+
+#ifndef TEEC_EXPORT
+#define TEEC_EXPORT
+#endif
+
+/* The header tee_client_api_imp.h must define implementation-dependent
+ types, constants and macros.
+
+ The implementation-dependent types are:
+ - TEEC_Context_IMP
+ - TEEC_Session_IMP
+ - TEEC_SharedMemory_IMP
+ - TEEC_Operation_IMP
+
+ The implementation-dependent constants are:
+ - TEEC_CONFIG_SHAREDMEM_MAX_SIZE
+ The implementation-dependent macros are:
+ - TEEC_PARAM_TYPES
+*/
+#include "tee_client_api_imp.h"
+
+/* Type definitions */
+typedef struct TEEC_Context
+{
+ TEEC_Context_IMP imp;
+} TEEC_Context;
+
+typedef struct TEEC_Session
+{
+ TEEC_Session_IMP imp;
+} TEEC_Session;
+
+typedef struct TEEC_SharedMemory
+{
+ void* buffer;
+ size_t size;
+ uint32_t flags;
+ TEEC_SharedMemory_IMP imp;
+} TEEC_SharedMemory;
+
+typedef struct
+{
+ void* buffer;
+ size_t size;
+} TEEC_TempMemoryReference;
+
+typedef struct
+{
+ TEEC_SharedMemory * parent;
+ size_t size;
+ size_t offset;
+} TEEC_RegisteredMemoryReference;
+
+typedef struct
+{
+ uint32_t a;
+ uint32_t b;
+} TEEC_Value;
+
+typedef union
+{
+ TEEC_TempMemoryReference tmpref;
+ TEEC_RegisteredMemoryReference memref;
+ TEEC_Value value;
+} TEEC_Parameter;
+
+typedef struct TEEC_Operation
+{
+ volatile uint32_t started;
+ uint32_t paramTypes;
+ TEEC_Parameter params[4];
+ TEEC_Operation_IMP imp;
+} TEEC_Operation;
+
+#define TEEC_SUCCESS ((TEEC_Result)0x00000000)
+#define TEEC_ERROR_GENERIC ((TEEC_Result)0xFFFF0000)
+#define TEEC_ERROR_ACCESS_DENIED ((TEEC_Result)0xFFFF0001)
+#define TEEC_ERROR_CANCEL ((TEEC_Result)0xFFFF0002)
+#define TEEC_ERROR_ACCESS_CONFLICT ((TEEC_Result)0xFFFF0003)
+#define TEEC_ERROR_EXCESS_DATA ((TEEC_Result)0xFFFF0004)
+#define TEEC_ERROR_BAD_FORMAT ((TEEC_Result)0xFFFF0005)
+#define TEEC_ERROR_BAD_PARAMETERS ((TEEC_Result)0xFFFF0006)
+#define TEEC_ERROR_BAD_STATE ((TEEC_Result)0xFFFF0007)
+#define TEEC_ERROR_ITEM_NOT_FOUND ((TEEC_Result)0xFFFF0008)
+#define TEEC_ERROR_NOT_IMPLEMENTED ((TEEC_Result)0xFFFF0009)
+#define TEEC_ERROR_NOT_SUPPORTED ((TEEC_Result)0xFFFF000A)
+#define TEEC_ERROR_NO_DATA ((TEEC_Result)0xFFFF000B)
+#define TEEC_ERROR_OUT_OF_MEMORY ((TEEC_Result)0xFFFF000C)
+#define TEEC_ERROR_BUSY ((TEEC_Result)0xFFFF000D)
+#define TEEC_ERROR_COMMUNICATION ((TEEC_Result)0xFFFF000E)
+#define TEEC_ERROR_SECURITY ((TEEC_Result)0xFFFF000F)
+#define TEEC_ERROR_SHORT_BUFFER ((TEEC_Result)0xFFFF0010)
+
+#define TEEC_ORIGIN_API 0x00000001
+#define TEEC_ORIGIN_COMMS 0x00000002
+#define TEEC_ORIGIN_TEE 0x00000003
+#define TEEC_ORIGIN_TRUSTED_APP 0x00000004
+
+#define TEEC_MEM_INPUT 0x00000001
+#define TEEC_MEM_OUTPUT 0x00000002
+
+#define TEEC_NONE 0x0
+#define TEEC_VALUE_INPUT 0x1
+#define TEEC_VALUE_OUTPUT 0x2
+#define TEEC_VALUE_INOUT 0x3
+#define TEEC_MEMREF_TEMP_INPUT 0x5
+#define TEEC_MEMREF_TEMP_OUTPUT 0x6
+#define TEEC_MEMREF_TEMP_INOUT 0x7
+#define TEEC_MEMREF_WHOLE 0xC
+#define TEEC_MEMREF_PARTIAL_INPUT 0xD
+#define TEEC_MEMREF_PARTIAL_OUTPUT 0xE
+#define TEEC_MEMREF_PARTIAL_INOUT 0xF
+
+#define TEEC_LOGIN_PUBLIC 0x00000000
+#define TEEC_LOGIN_USER 0x00000001
+#define TEEC_LOGIN_GROUP 0x00000002
+#define TEEC_LOGIN_APPLICATION 0x00000004
+#define TEEC_LOGIN_USER_APPLICATION 0x00000005
+#define TEEC_LOGIN_GROUP_APPLICATION 0x00000006
+
+TEEC_Result TEEC_EXPORT TEEC_InitializeContext(
+ const char* name,
+ TEEC_Context* context);
+
+void TEEC_EXPORT TEEC_FinalizeContext(
+ TEEC_Context* context);
+
+TEEC_Result TEEC_EXPORT TEEC_RegisterSharedMemory(
+ TEEC_Context* context,
+ TEEC_SharedMemory* sharedMem);
+
+TEEC_Result TEEC_EXPORT TEEC_AllocateSharedMemory(
+ TEEC_Context* context,
+ TEEC_SharedMemory* sharedMem);
+
+void TEEC_EXPORT TEEC_ReleaseSharedMemory (
+ TEEC_SharedMemory* sharedMem);
+
+TEEC_Result TEEC_EXPORT TEEC_OpenSession (
+ TEEC_Context* context,
+ TEEC_Session* session,
+ const TEEC_UUID* destination,
+ uint32_t connectionMethod,
+ void* connectionData,
+ TEEC_Operation* operation,
+ uint32_t* errorOrigin);
+
+void TEEC_EXPORT TEEC_CloseSession (
+ TEEC_Session* session);
+
+TEEC_Result TEEC_EXPORT TEEC_InvokeCommand(
+ TEEC_Session* session,
+ uint32_t commandID,
+ TEEC_Operation* operation,
+ uint32_t* errorOrigin);
+
+void TEEC_EXPORT TEEC_RequestCancellation(
+ TEEC_Operation* operation);
+
+#include "tee_client_api_ex.h"
+
+#endif /* __TEE_CLIENT_API_H__ */
diff --git a/security/smc/tee_client_api_ex.h b/security/smc/tee_client_api_ex.h
new file mode 100644
index 0000000..4988904
--- /dev/null
+++ b/security/smc/tee_client_api_ex.h
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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
+ */
+
+/*
+ * This header file contains extensions to the TEE Client API that are
+ * specific to the Trusted Foundations implementations
+ */
+#ifndef __TEE_CLIENT_API_EX_H__
+#define __TEE_CLIENT_API_EX_H__
+
+#include <linux/types.h>
+
+/* Implementation-defined login types */
+#define TEEC_LOGIN_AUTHENTICATION 0x80000000
+#define TEEC_LOGIN_PRIVILEGED 0x80000002
+
+/* Type definitions */
+
+typedef u64 TEEC_TimeLimit;
+
+void TEEC_EXPORT TEEC_GetTimeLimit(
+ TEEC_Context* context,
+ uint32_t timeout,
+ TEEC_TimeLimit* timeLimit);
+
+TEEC_Result TEEC_EXPORT TEEC_OpenSessionEx (
+ TEEC_Context* context,
+ TEEC_Session* session,
+ const TEEC_TimeLimit* timeLimit,
+ const TEEC_UUID* destination,
+ uint32_t connectionMethod,
+ void* connectionData,
+ TEEC_Operation* operation,
+ uint32_t* errorOrigin);
+
+TEEC_Result TEEC_EXPORT TEEC_InvokeCommandEx(
+ TEEC_Session* session,
+ const TEEC_TimeLimit* timeLimit,
+ uint32_t commandID,
+ TEEC_Operation* operation,
+ uint32_t* errorOrigin);
+
+#endif /* __TEE_CLIENT_API_EX_H__ */
diff --git a/security/smc/tee_client_api_imp.h b/security/smc/tee_client_api_imp.h
new file mode 100644
index 0000000..3073d63
--- /dev/null
+++ b/security/smc/tee_client_api_imp.h
@@ -0,0 +1,68 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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
+ */
+
+/*
+ * This header file defines the implementation-dependent types,
+ * constants and macros for all the Trusted Foundations implementations
+ * of the TEE Client API
+ */
+#ifndef __TEE_CLIENT_API_IMP_H__
+#define __TEE_CLIENT_API_IMP_H__
+
+#include <linux/types.h>
+
+typedef u32 TEEC_Result;
+
+typedef struct TEEC_UUID
+{
+ uint32_t time_low;
+ uint16_t time_mid;
+ uint16_t time_hi_and_version;
+ uint8_t clock_seq_and_node[8];
+} TEEC_UUID;
+
+typedef struct {
+ struct tf_connection *_connection;
+} TEEC_Context_IMP;
+
+typedef struct {
+ struct TEEC_Context* _context;
+ u32 _client_session;
+} TEEC_Session_IMP;
+
+typedef struct {
+ struct TEEC_Context* _context;
+ u32 _block;
+ bool _allocated;
+} TEEC_SharedMemory_IMP;
+
+typedef struct {
+ struct TEEC_Session* _pSession;
+} TEEC_Operation_IMP;
+
+/* There is no natural, compile-time limit on the shared memory, but a specific
+ implementation may introduce a limit (in particular on TrustZone) */
+#define TEEC_CONFIG_SHAREDMEM_MAX_SIZE ((size_t)0xFFFFFFFF)
+
+#define TEEC_PARAM_TYPES(entry0Type, entry1Type, entry2Type, entry3Type) \
+ ((entry0Type) | ((entry1Type) << 4) | \
+ ((entry2Type) << 8) | ((entry3Type) << 12))
+
+
+#endif /* __TEE_CLIENT_API_IMP_H__ */
diff --git a/security/smc/tf_comm.c b/security/smc/tf_comm.c
new file mode 100644
index 0000000..79b4034
--- /dev/null
+++ b/security/smc/tf_comm.c
@@ -0,0 +1,1746 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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 <asm/div64.h>
+#include <asm/system.h>
+#include <linux/version.h>
+#include <asm/cputype.h>
+#include <linux/interrupt.h>
+#include <linux/page-flags.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/jiffies.h>
+#include <linux/freezer.h>
+
+#include "tf_defs.h"
+#include "tf_comm.h"
+#include "tf_protocol.h"
+#include "tf_util.h"
+#include "tf_conn.h"
+
+#ifdef CONFIG_TF_ZEBRA
+#include "tf_zebra.h"
+#endif
+
+/*---------------------------------------------------------------------------
+ * Internal Constants
+ *---------------------------------------------------------------------------*/
+
+/*
+ * shared memories descriptor constants
+ */
+#define DESCRIPTOR_B_MASK (1 << 2)
+#define DESCRIPTOR_C_MASK (1 << 3)
+#define DESCRIPTOR_S_MASK (1 << 10)
+
+#define L1_COARSE_DESCRIPTOR_BASE (0x00000001)
+#define L1_COARSE_DESCRIPTOR_ADDR_MASK (0xFFFFFC00)
+#define L1_COARSE_DESCRIPTOR_V13_12_SHIFT (5)
+
+#define L2_PAGE_DESCRIPTOR_BASE (0x00000003)
+#define L2_PAGE_DESCRIPTOR_AP_APX_READ (0x220)
+#define L2_PAGE_DESCRIPTOR_AP_APX_READ_WRITE (0x30)
+
+#define L2_INIT_DESCRIPTOR_BASE (0x00000003)
+#define L2_INIT_DESCRIPTOR_V13_12_SHIFT (4)
+
+/*
+ * Reject an attempt to share a strongly-Ordered or Device memory
+ * Strongly-Ordered: TEX=0b000, C=0, B=0
+ * Shared Device: TEX=0b000, C=0, B=1
+ * Non-Shared Device: TEX=0b010, C=0, B=0
+ */
+#define L2_TEX_C_B_MASK \
+ ((1<<8) | (1<<7) | (1<<6) | (1<<3) | (1<<2))
+#define L2_TEX_C_B_STRONGLY_ORDERED \
+ ((0<<8) | (0<<7) | (0<<6) | (0<<3) | (0<<2))
+#define L2_TEX_C_B_SHARED_DEVICE \
+ ((0<<8) | (0<<7) | (0<<6) | (0<<3) | (1<<2))
+#define L2_TEX_C_B_NON_SHARED_DEVICE \
+ ((0<<8) | (1<<7) | (0<<6) | (0<<3) | (0<<2))
+
+#define CACHE_S(x) ((x) & (1 << 24))
+#define CACHE_DSIZE(x) (((x) >> 12) & 4095)
+
+#define TIME_IMMEDIATE ((u64) 0x0000000000000000ULL)
+#define TIME_INFINITE ((u64) 0xFFFFFFFFFFFFFFFFULL)
+
+/*---------------------------------------------------------------------------
+ * atomic operation definitions
+ *---------------------------------------------------------------------------*/
+
+/*
+ * Atomically updates the sync_serial_n and time_n register
+ * sync_serial_n and time_n modifications are thread safe
+ */
+void tf_set_current_time(struct tf_comm *comm)
+{
+ u32 new_sync_serial;
+ struct timeval now;
+ u64 time64;
+
+ /*
+ * lock the structure while updating the L1 shared memory fields
+ */
+ spin_lock(&comm->lock);
+
+ /* read sync_serial_n and change the TimeSlot bit field */
+ new_sync_serial =
+ tf_read_reg32(&comm->pBuffer->sync_serial_n) + 1;
+
+ do_gettimeofday(&now);
+ time64 = now.tv_sec;
+ time64 = (time64 * 1000) + (now.tv_usec / 1000);
+
+ /* Write the new time64 and nSyncSerial into shared memory */
+ tf_write_reg64(&comm->pBuffer->time_n[new_sync_serial &
+ TF_SYNC_SERIAL_TIMESLOT_N], time64);
+ tf_write_reg32(&comm->pBuffer->sync_serial_n,
+ new_sync_serial);
+
+ spin_unlock(&comm->lock);
+}
+
+/*
+ * Performs the specific read timeout operation
+ * The difficulty here is to read atomically 2 u32
+ * values from the L1 shared buffer.
+ * This is guaranteed by reading before and after the operation
+ * the timeslot given by the Secure World
+ */
+static inline void tf_read_timeout(struct tf_comm *comm, u64 *time)
+{
+ u32 sync_serial_s_initial = 0;
+ u32 sync_serial_s_final = 1;
+ u64 time64;
+
+ spin_lock(&comm->lock);
+
+ while (sync_serial_s_initial != sync_serial_s_final) {
+ sync_serial_s_initial = tf_read_reg32(
+ &comm->pBuffer->sync_serial_s);
+ time64 = tf_read_reg64(
+ &comm->pBuffer->timeout_s[sync_serial_s_initial&1]);
+
+ sync_serial_s_final = tf_read_reg32(
+ &comm->pBuffer->sync_serial_s);
+ }
+
+ spin_unlock(&comm->lock);
+
+ *time = time64;
+}
+
+/*----------------------------------------------------------------------------
+ * SIGKILL signal handling
+ *----------------------------------------------------------------------------*/
+
+static bool sigkill_pending(void)
+{
+ if (signal_pending(current)) {
+ dprintk(KERN_INFO "A signal is pending\n");
+ if (sigismember(&current->pending.signal, SIGKILL)) {
+ dprintk(KERN_INFO "A SIGKILL is pending\n");
+ return true;
+ } else if (sigismember(
+ &current->signal->shared_pending.signal, SIGKILL)) {
+ dprintk(KERN_INFO "A SIGKILL is pending (shared)\n");
+ return true;
+ }
+ }
+ return false;
+}
+
+/*----------------------------------------------------------------------------
+ * Shared memory related operations
+ *----------------------------------------------------------------------------*/
+
+struct tf_coarse_page_table *tf_alloc_coarse_page_table(
+ struct tf_coarse_page_table_allocation_context *alloc_context,
+ u32 type)
+{
+ struct tf_coarse_page_table *coarse_pg_table = NULL;
+
+ spin_lock(&(alloc_context->lock));
+
+ if (!(list_empty(&(alloc_context->free_coarse_page_tables)))) {
+ /*
+ * The free list can provide us a coarse page table
+ * descriptor
+ */
+ coarse_pg_table = list_first_entry(
+ &alloc_context->free_coarse_page_tables,
+ struct tf_coarse_page_table, list);
+ list_del(&(coarse_pg_table->list));
+
+ coarse_pg_table->parent->ref_count++;
+ } else {
+ /* no array of coarse page tables, create a new one */
+ struct tf_coarse_page_table_array *array;
+ void *page;
+ int i;
+
+ spin_unlock(&(alloc_context->lock));
+
+ /* first allocate a new page descriptor */
+ array = internal_kmalloc(sizeof(*array), GFP_KERNEL);
+ if (array == NULL) {
+ dprintk(KERN_ERR "tf_alloc_coarse_page_table(%p):"
+ " failed to allocate a table array\n",
+ alloc_context);
+ return NULL;
+ }
+
+ array->type = type;
+ INIT_LIST_HEAD(&(array->list));
+
+ /* now allocate the actual page the page descriptor describes */
+ page = (void *) internal_get_zeroed_page(GFP_KERNEL);
+ if (page == NULL) {
+ dprintk(KERN_ERR "tf_alloc_coarse_page_table(%p):"
+ " failed allocate a page\n",
+ alloc_context);
+ internal_kfree(array);
+ return NULL;
+ }
+
+ spin_lock(&(alloc_context->lock));
+
+ /* initialize the coarse page table descriptors */
+ for (i = 0; i < 4; i++) {
+ INIT_LIST_HEAD(&(array->coarse_page_tables[i].list));
+ array->coarse_page_tables[i].descriptors =
+ page + (i * SIZE_1KB);
+ array->coarse_page_tables[i].parent = array;
+
+ if (i == 0) {
+ /*
+ * the first element is kept for the current
+ * coarse page table allocation
+ */
+ coarse_pg_table =
+ &(array->coarse_page_tables[i]);
+ array->ref_count++;
+ } else {
+ /*
+ * The other elements are added to the free list
+ */
+ list_add(&(array->coarse_page_tables[i].list),
+ &(alloc_context->
+ free_coarse_page_tables));
+ }
+ }
+
+ list_add(&(array->list),
+ &(alloc_context->coarse_page_table_arrays));
+ }
+ spin_unlock(&(alloc_context->lock));
+
+ return coarse_pg_table;
+}
+
+
+void tf_free_coarse_page_table(
+ struct tf_coarse_page_table_allocation_context *alloc_context,
+ struct tf_coarse_page_table *coarse_pg_table,
+ int force)
+{
+ struct tf_coarse_page_table_array *array;
+
+ spin_lock(&(alloc_context->lock));
+
+ array = coarse_pg_table->parent;
+
+ (array->ref_count)--;
+
+ if (array->ref_count == 0) {
+ /*
+ * no coarse page table descriptor is used
+ * check if we should free the whole page
+ */
+
+ if ((array->type == TF_PAGE_DESCRIPTOR_TYPE_PREALLOCATED)
+ && (force == 0))
+ /*
+ * This is a preallocated page,
+ * add the page back to the free list
+ */
+ list_add(&(coarse_pg_table->list),
+ &(alloc_context->free_coarse_page_tables));
+ else {
+ /*
+ * None of the page's coarse page table descriptors
+ * are in use, free the whole page
+ */
+ int i;
+ u32 *descriptors;
+
+ /*
+ * remove the page's associated coarse page table
+ * descriptors from the free list
+ */
+ for (i = 0; i < 4; i++)
+ if (&(array->coarse_page_tables[i]) !=
+ coarse_pg_table)
+ list_del(&(array->
+ coarse_page_tables[i].list));
+
+ descriptors =
+ array->coarse_page_tables[0].descriptors;
+ array->coarse_page_tables[0].descriptors = NULL;
+
+ /* remove the coarse page table from the array */
+ list_del(&(array->list));
+
+ spin_unlock(&(alloc_context->lock));
+ /*
+ * Free the page.
+ * The address of the page is contained in the first
+ * element
+ */
+ internal_free_page((unsigned long) descriptors);
+ /* finaly free the array */
+ internal_kfree(array);
+
+ spin_lock(&(alloc_context->lock));
+ }
+ } else {
+ /*
+ * Some coarse page table descriptors are in use.
+ * Add the descriptor to the free list
+ */
+ list_add(&(coarse_pg_table->list),
+ &(alloc_context->free_coarse_page_tables));
+ }
+
+ spin_unlock(&(alloc_context->lock));
+}
+
+
+void tf_init_coarse_page_table_allocator(
+ struct tf_coarse_page_table_allocation_context *alloc_context)
+{
+ spin_lock_init(&(alloc_context->lock));
+ INIT_LIST_HEAD(&(alloc_context->coarse_page_table_arrays));
+ INIT_LIST_HEAD(&(alloc_context->free_coarse_page_tables));
+}
+
+void tf_release_coarse_page_table_allocator(
+ struct tf_coarse_page_table_allocation_context *alloc_context)
+{
+ spin_lock(&(alloc_context->lock));
+
+ /* now clean up the list of page descriptors */
+ while (!list_empty(&(alloc_context->coarse_page_table_arrays))) {
+ struct tf_coarse_page_table_array *page_desc;
+ u32 *descriptors;
+
+ page_desc = list_first_entry(
+ &alloc_context->coarse_page_table_arrays,
+ struct tf_coarse_page_table_array, list);
+
+ descriptors = page_desc->coarse_page_tables[0].descriptors;
+ list_del(&(page_desc->list));
+
+ spin_unlock(&(alloc_context->lock));
+
+ if (descriptors != NULL)
+ internal_free_page((unsigned long)descriptors);
+
+ internal_kfree(page_desc);
+
+ spin_lock(&(alloc_context->lock));
+ }
+
+ spin_unlock(&(alloc_context->lock));
+}
+
+/*
+ * Returns the L1 coarse page descriptor for
+ * a coarse page table located at address coarse_pg_table_descriptors
+ */
+u32 tf_get_l1_coarse_descriptor(
+ u32 coarse_pg_table_descriptors[256])
+{
+ u32 descriptor = L1_COARSE_DESCRIPTOR_BASE;
+ unsigned int info = read_cpuid(CPUID_CACHETYPE);
+
+ descriptor |= (virt_to_phys((void *) coarse_pg_table_descriptors)
+ & L1_COARSE_DESCRIPTOR_ADDR_MASK);
+
+ if (CACHE_S(info) && (CACHE_DSIZE(info) & (1 << 11))) {
+ dprintk(KERN_DEBUG "tf_get_l1_coarse_descriptor "
+ "V31-12 added to descriptor\n");
+ /* the 16k alignment restriction applies */
+ descriptor |= (DESCRIPTOR_V13_12_GET(
+ (u32)coarse_pg_table_descriptors) <<
+ L1_COARSE_DESCRIPTOR_V13_12_SHIFT);
+ }
+
+ return descriptor;
+}
+
+
+#define dprintk_desc(...)
+/*
+ * Returns the L2 descriptor for the specified user page.
+ */
+u32 tf_get_l2_descriptor_common(u32 vaddr, struct mm_struct *mm)
+{
+ pgd_t *pgd;
+ pud_t *pud;
+ pmd_t *pmd;
+ pte_t *ptep;
+ u32 *hwpte;
+ u32 tex = 0;
+ u32 descriptor = 0;
+
+ dprintk_desc(KERN_INFO "VirtAddr = %x\n", vaddr);
+ pgd = pgd_offset(mm, vaddr);
+ dprintk_desc(KERN_INFO "pgd = %x, value=%x\n", (unsigned int) pgd,
+ (unsigned int) *pgd);
+ if (pgd_none(*pgd))
+ goto error;
+ pud = pud_offset(pgd, vaddr);
+ dprintk_desc(KERN_INFO "pud = %x, value=%x\n", (unsigned int) pud,
+ (unsigned int) *pud);
+ if (pud_none(*pud))
+ goto error;
+ pmd = pmd_offset(pud, vaddr);
+ dprintk_desc(KERN_INFO "pmd = %x, value=%x\n", (unsigned int) pmd,
+ (unsigned int) *pmd);
+ if (pmd_none(*pmd))
+ goto error;
+
+ if (PMD_TYPE_SECT&(*pmd)) {
+ /* We have a section */
+ dprintk_desc(KERN_INFO "Section descr=%x\n",
+ (unsigned int)*pmd);
+ if ((*pmd) & PMD_SECT_BUFFERABLE)
+ descriptor |= DESCRIPTOR_B_MASK;
+ if ((*pmd) & PMD_SECT_CACHEABLE)
+ descriptor |= DESCRIPTOR_C_MASK;
+ if ((*pmd) & PMD_SECT_S)
+ descriptor |= DESCRIPTOR_S_MASK;
+ tex = ((*pmd) >> 12) & 7;
+ } else {
+ /* We have a table */
+ ptep = pte_offset_map(pmd, vaddr);
+ if (pte_present(*ptep)) {
+ dprintk_desc(KERN_INFO "L2 descr=%x\n",
+ (unsigned int) *ptep);
+ if ((*ptep) & L_PTE_MT_BUFFERABLE)
+ descriptor |= DESCRIPTOR_B_MASK;
+ if ((*ptep) & L_PTE_MT_WRITETHROUGH)
+ descriptor |= DESCRIPTOR_C_MASK;
+ if ((*ptep) & L_PTE_MT_DEV_SHARED)
+ descriptor |= DESCRIPTOR_S_MASK;
+
+ /*
+ * Linux's pte doesn't keep track of TEX value.
+ * Have to jump to hwpte see include/asm/pgtable.h
+ */
+ hwpte = (u32 *) (((u32) ptep) - 0x800);
+ if (((*hwpte) & L2_DESCRIPTOR_ADDR_MASK) !=
+ ((*ptep) & L2_DESCRIPTOR_ADDR_MASK))
+ goto error;
+ dprintk_desc(KERN_INFO "hw descr=%x\n", *hwpte);
+ tex = ((*hwpte) >> 6) & 7;
+ pte_unmap(ptep);
+ } else {
+ pte_unmap(ptep);
+ goto error;
+ }
+ }
+
+ descriptor |= (tex << 6);
+
+ return descriptor;
+
+error:
+ dprintk(KERN_ERR "Error occured in %s\n", __func__);
+ return 0;
+}
+
+
+/*
+ * Changes an L2 page descriptor back to a pointer to a physical page
+ */
+inline struct page *tf_l2_page_descriptor_to_page(u32 l2_page_descriptor)
+{
+ return pte_page(l2_page_descriptor & L2_DESCRIPTOR_ADDR_MASK);
+}
+
+#define TF_DEFAULT_COMMON_DESCRIPTORS 0x0000044C
+
+/*
+ * Returns the L1 descriptor for the 1KB-aligned coarse page table. The address
+ * must be in the kernel address space.
+ */
+static void tf_get_l2_page_descriptor(
+ u32 *l2_page_descriptor,
+ u32 flags, struct mm_struct *mm, struct vm_area_struct *vmas)
+{
+ u32 descriptor;
+ struct page *page;
+
+ dprintk(KERN_INFO
+ "%s *l2_page_descriptor=%x vm_flags=%lx\n",
+ __func__, *l2_page_descriptor, vmas->vm_flags);
+
+ if (*l2_page_descriptor == L2_DESCRIPTOR_FAULT)
+ return;
+
+ if (vmas->vm_flags & VM_IO) {
+ *l2_page_descriptor = L2_DESCRIPTOR_FAULT;
+ dprintk(KERN_ERR "Memory mapped I/O or similar detected\n");
+ return;
+ }
+ page = (struct page *) (*l2_page_descriptor);
+
+ descriptor = TF_DEFAULT_COMMON_DESCRIPTORS;
+ descriptor |= L2_PAGE_DESCRIPTOR_BASE;
+
+ descriptor |= (page_to_phys(page) & L2_DESCRIPTOR_ADDR_MASK);
+
+ if (!(flags & TF_SHMEM_TYPE_WRITE))
+ /* only read access */
+ descriptor |= L2_PAGE_DESCRIPTOR_AP_APX_READ;
+ else
+ /* read and write access */
+ descriptor |= L2_PAGE_DESCRIPTOR_AP_APX_READ_WRITE;
+
+
+ *l2_page_descriptor = descriptor;
+}
+
+
+/*
+ * Unlocks the physical memory pages
+ * and frees the coarse pages that need to
+ */
+void tf_cleanup_shared_memory(
+ struct tf_coarse_page_table_allocation_context *alloc_context,
+ struct tf_shmem_desc *shmem_desc,
+ u32 full_cleanup)
+{
+ u32 coarse_page_index;
+
+ dprintk(KERN_INFO "tf_cleanup_shared_memory(%p)\n",
+ shmem_desc);
+
+#ifdef DEBUG_COARSE_TABLES
+ printk(KERN_DEBUG "tf_cleanup_shared_memory "
+ "- number of coarse page tables=%d\n",
+ shmem_desc->coarse_pg_table_count);
+
+ for (coarse_page_index = 0;
+ coarse_page_index < shmem_desc->coarse_pg_table_count;
+ coarse_page_index++) {
+ u32 j;
+
+ printk(KERN_DEBUG " Descriptor=%p address=%p index=%d\n",
+ shmem_desc->coarse_pg_table[coarse_page_index],
+ shmem_desc->coarse_pg_table[coarse_page_index]->
+ descriptors,
+ coarse_page_index);
+ if (shmem_desc->coarse_pg_table[coarse_page_index] != NULL) {
+ for (j = 0;
+ j < TF_DESCRIPTOR_TABLE_CAPACITY;
+ j += 8) {
+ int k;
+ printk(KERN_DEBUG " ");
+ for (k = j; k < j + 8; k++)
+ printk(KERN_DEBUG "%p ",
+ shmem_desc->coarse_pg_table[
+ coarse_page_index]->
+ descriptors);
+ printk(KERN_DEBUG "\n");
+ }
+ }
+ }
+ printk(KERN_DEBUG "tf_cleanup_shared_memory() - done\n\n");
+#endif
+
+ /* Parse the coarse page descriptors */
+ for (coarse_page_index = 0;
+ coarse_page_index < shmem_desc->coarse_pg_table_count;
+ coarse_page_index++) {
+ u32 j;
+ u32 found = 0;
+
+ /* parse the page descriptors of the coarse page */
+ for (j = 0; j < TF_DESCRIPTOR_TABLE_CAPACITY; j++) {
+ u32 l2_page_descriptor = (u32) (shmem_desc->
+ coarse_pg_table[coarse_page_index]->
+ descriptors[j]);
+
+ if (l2_page_descriptor != L2_DESCRIPTOR_FAULT) {
+ struct page *page =
+ tf_l2_page_descriptor_to_page(
+ l2_page_descriptor);
+
+ if (!PageReserved(page))
+ SetPageDirty(page);
+ internal_page_cache_release(page);
+
+ found = 1;
+ } else if (found == 1) {
+ break;
+ }
+ }
+
+ /*
+ * Only free the coarse pages of descriptors not preallocated
+ */
+ if ((shmem_desc->type == TF_SHMEM_TYPE_REGISTERED_SHMEM) ||
+ (full_cleanup != 0))
+ tf_free_coarse_page_table(alloc_context,
+ shmem_desc->coarse_pg_table[coarse_page_index],
+ 0);
+ }
+
+ shmem_desc->coarse_pg_table_count = 0;
+ dprintk(KERN_INFO "tf_cleanup_shared_memory(%p) done\n",
+ shmem_desc);
+}
+
+/*
+ * Make sure the coarse pages are allocated. If not allocated, do it Locks down
+ * the physical memory pages
+ * Verifies the memory attributes depending on flags
+ */
+int tf_fill_descriptor_table(
+ struct tf_coarse_page_table_allocation_context *alloc_context,
+ struct tf_shmem_desc *shmem_desc,
+ u32 buffer,
+ struct vm_area_struct **vmas,
+ u32 descriptors[TF_MAX_COARSE_PAGES],
+ u32 buffer_size,
+ u32 *buffer_start_offset,
+ bool in_user_space,
+ u32 flags,
+ u32 *descriptor_count)
+{
+ u32 coarse_page_index;
+ u32 coarse_page_count;
+ u32 page_count;
+ u32 page_shift = 0;
+ int error;
+ unsigned int info = read_cpuid(CPUID_CACHETYPE);
+
+ dprintk(KERN_INFO "tf_fill_descriptor_table"
+ "(%p, buffer=0x%08X, size=0x%08X, user=%01x "
+ "flags = 0x%08x)\n",
+ shmem_desc,
+ buffer,
+ buffer_size,
+ in_user_space,
+ flags);
+
+ /*
+ * Compute the number of pages
+ * Compute the number of coarse pages
+ * Compute the page offset
+ */
+ page_count = ((buffer & ~PAGE_MASK) +
+ buffer_size + ~PAGE_MASK) >> PAGE_SHIFT;
+
+ /* check whether the 16k alignment restriction applies */
+ if (CACHE_S(info) && (CACHE_DSIZE(info) & (1 << 11)))
+ /*
+ * The 16k alignment restriction applies.
+ * Shift data to get them 16k aligned
+ */
+ page_shift = DESCRIPTOR_V13_12_GET(buffer);
+ page_count += page_shift;
+
+
+ /*
+ * Check the number of pages fit in the coarse pages
+ */
+ if (page_count > (TF_DESCRIPTOR_TABLE_CAPACITY *
+ TF_MAX_COARSE_PAGES)) {
+ dprintk(KERN_ERR "tf_fill_descriptor_table(%p): "
+ "%u pages required to map shared memory!\n",
+ shmem_desc, page_count);
+ error = -ENOMEM;
+ goto error;
+ }
+
+ /* coarse page describe 256 pages */
+ coarse_page_count = ((page_count +
+ TF_DESCRIPTOR_TABLE_CAPACITY_MASK) >>
+ TF_DESCRIPTOR_TABLE_CAPACITY_BIT_SHIFT);
+
+ /*
+ * Compute the buffer offset
+ */
+ *buffer_start_offset = (buffer & ~PAGE_MASK) |
+ (page_shift << PAGE_SHIFT);
+
+ /* map each coarse page */
+ for (coarse_page_index = 0;
+ coarse_page_index < coarse_page_count;
+ coarse_page_index++) {
+ u32 j;
+ struct tf_coarse_page_table *coarse_pg_table;
+
+ /* compute a virtual address with appropriate offset */
+ u32 buffer_offset_vaddr = buffer +
+ (coarse_page_index * TF_MAX_COARSE_PAGE_MAPPED_SIZE);
+ u32 pages_to_get;
+
+ /*
+ * Compute the number of pages left for this coarse page.
+ * Decrement page_count each time
+ */
+ pages_to_get = (page_count >>
+ TF_DESCRIPTOR_TABLE_CAPACITY_BIT_SHIFT) ?
+ TF_DESCRIPTOR_TABLE_CAPACITY : page_count;
+ page_count -= pages_to_get;
+
+ /*
+ * Check if the coarse page has already been allocated
+ * If not, do it now
+ */
+ if ((shmem_desc->type == TF_SHMEM_TYPE_REGISTERED_SHMEM)
+ || (shmem_desc->type ==
+ TF_SHMEM_TYPE_PM_HIBERNATE)) {
+ coarse_pg_table = tf_alloc_coarse_page_table(
+ alloc_context,
+ TF_PAGE_DESCRIPTOR_TYPE_NORMAL);
+
+ if (coarse_pg_table == NULL) {
+ dprintk(KERN_ERR
+ "tf_fill_descriptor_table(%p):"
+ " SCXLNXConnAllocateCoarsePageTable "
+ "failed for coarse page %d\n",
+ shmem_desc, coarse_page_index);
+ error = -ENOMEM;
+ goto error;
+ }
+
+ shmem_desc->coarse_pg_table[coarse_page_index] =
+ coarse_pg_table;
+ } else {
+ coarse_pg_table =
+ shmem_desc->coarse_pg_table[coarse_page_index];
+ }
+
+ /*
+ * The page is not necessarily filled with zeroes.
+ * Set the fault descriptors ( each descriptor is 4 bytes long)
+ */
+ memset(coarse_pg_table->descriptors, 0x00,
+ TF_DESCRIPTOR_TABLE_CAPACITY * sizeof(u32));
+
+ if (in_user_space) {
+ int pages;
+
+ /*
+ * TRICK: use pCoarsePageDescriptor->descriptors to
+ * hold the (struct page*) items before getting their
+ * physical address
+ */
+ down_read(&(current->mm->mmap_sem));
+ pages = internal_get_user_pages(
+ current,
+ current->mm,
+ buffer_offset_vaddr,
+ /*
+ * page_shift is cleared after retrieving first
+ * coarse page
+ */
+ (pages_to_get - page_shift),
+ (flags & TF_SHMEM_TYPE_WRITE) ? 1 : 0,
+ 0,
+ (struct page **) (coarse_pg_table->descriptors
+ + page_shift),
+ vmas);
+ up_read(&(current->mm->mmap_sem));
+
+ if ((pages <= 0) ||
+ (pages != (pages_to_get - page_shift))) {
+ dprintk(KERN_ERR"tf_fill_descriptor_table:"
+ " get_user_pages got %d pages while "
+ "trying to get %d pages!\n",
+ pages, pages_to_get - page_shift);
+ error = -EFAULT;
+ goto error;
+ }
+
+ for (j = page_shift;
+ j < page_shift + pages;
+ j++) {
+ /* Get the actual L2 descriptors */
+ tf_get_l2_page_descriptor(
+ &coarse_pg_table->descriptors[j],
+ flags,
+ current->mm,
+ vmas[j]);
+ /*
+ * Reject Strongly-Ordered or Device Memory
+ */
+#define IS_STRONGLY_ORDERED_OR_DEVICE_MEM(x) \
+ ((((x) & L2_TEX_C_B_MASK) == L2_TEX_C_B_STRONGLY_ORDERED) || \
+ (((x) & L2_TEX_C_B_MASK) == L2_TEX_C_B_SHARED_DEVICE) || \
+ (((x) & L2_TEX_C_B_MASK) == L2_TEX_C_B_NON_SHARED_DEVICE))
+
+ if (IS_STRONGLY_ORDERED_OR_DEVICE_MEM(
+ coarse_pg_table->
+ descriptors[j])) {
+ dprintk(KERN_ERR
+ "tf_fill_descriptor_table:"
+ " descriptor 0x%08X use "
+ "strongly-ordered or device "
+ "memory. Rejecting!\n",
+ coarse_pg_table->
+ descriptors[j]);
+ error = -EFAULT;
+ goto error;
+ }
+ }
+ } else {
+ /* Kernel-space memory */
+ for (j = page_shift;
+ j < pages_to_get;
+ j++) {
+ void *addr =
+ (void *)(buffer_offset_vaddr +
+ (j - page_shift) * PAGE_SIZE);
+ if (!is_vmalloc_addr(addr)) {
+ dprintk(KERN_ERR
+ "tf_fill_descriptor_table: "
+ "cannot handle address %p\n",
+ addr);
+ goto error;
+ }
+ struct page *page = vmalloc_to_page(addr);
+ if (page == NULL) {
+ dprintk(KERN_ERR
+ "tf_fill_descriptor_table: "
+ "cannot map %p to page\n",
+ addr);
+ goto error;
+ }
+ coarse_pg_table->descriptors[j] = (u32)page;
+ get_page(page);
+
+ /* change coarse page "page address" */
+ tf_get_l2_page_descriptor(
+ &coarse_pg_table->descriptors[j],
+ flags,
+ &init_mm,
+ vmas[j]);
+ }
+ }
+
+ dmac_flush_range((void *)coarse_pg_table->descriptors,
+ (void *)(((u32)(coarse_pg_table->descriptors)) +
+ TF_DESCRIPTOR_TABLE_CAPACITY * sizeof(u32)));
+
+ outer_clean_range(
+ __pa(coarse_pg_table->descriptors),
+ __pa(coarse_pg_table->descriptors) +
+ TF_DESCRIPTOR_TABLE_CAPACITY * sizeof(u32));
+ wmb();
+
+ /* Update the coarse page table address */
+ descriptors[coarse_page_index] =
+ tf_get_l1_coarse_descriptor(
+ coarse_pg_table->descriptors);
+
+ /*
+ * The next coarse page has no page shift, reset the
+ * page_shift
+ */
+ page_shift = 0;
+ }
+
+ *descriptor_count = coarse_page_count;
+ shmem_desc->coarse_pg_table_count = coarse_page_count;
+
+#ifdef DEBUG_COARSE_TABLES
+ printk(KERN_DEBUG "ntf_fill_descriptor_table - size=0x%08X "
+ "numberOfCoarsePages=%d\n", buffer_size,
+ shmem_desc->coarse_pg_table_count);
+ for (coarse_page_index = 0;
+ coarse_page_index < shmem_desc->coarse_pg_table_count;
+ coarse_page_index++) {
+ u32 j;
+ struct tf_coarse_page_table *coarse_page_table =
+ shmem_desc->coarse_pg_table[coarse_page_index];
+
+ printk(KERN_DEBUG " Descriptor=%p address=%p index=%d\n",
+ coarse_page_table,
+ coarse_page_table->descriptors,
+ coarse_page_index);
+ for (j = 0;
+ j < TF_DESCRIPTOR_TABLE_CAPACITY;
+ j += 8) {
+ int k;
+ printk(KERN_DEBUG " ");
+ for (k = j; k < j + 8; k++)
+ printk(KERN_DEBUG "0x%08X ",
+ coarse_page_table->descriptors[k]);
+ printk(KERN_DEBUG "\n");
+ }
+ }
+ printk(KERN_DEBUG "ntf_fill_descriptor_table() - done\n\n");
+#endif
+
+ return 0;
+
+error:
+ tf_cleanup_shared_memory(
+ alloc_context,
+ shmem_desc,
+ 0);
+
+ return error;
+}
+
+
+/*----------------------------------------------------------------------------
+ * Standard communication operations
+ *----------------------------------------------------------------------------*/
+
+u8 *tf_get_description(struct tf_comm *comm)
+{
+ if (test_bit(TF_COMM_FLAG_L1_SHARED_ALLOCATED, &(comm->flags)))
+ return comm->pBuffer->version_description;
+
+ return NULL;
+}
+
+/*
+ * Returns a non-zero value if the specified S-timeout has expired, zero
+ * otherwise.
+ *
+ * The placeholder referenced to by relative_timeout_jiffies gives the relative
+ * timeout from now in jiffies. It is set to zero if the S-timeout has expired,
+ * or to MAX_SCHEDULE_TIMEOUT if the S-timeout is infinite.
+ */
+static int tf_test_s_timeout(
+ u64 timeout,
+ signed long *relative_timeout_jiffies)
+{
+ struct timeval now;
+ u64 time64;
+
+ *relative_timeout_jiffies = 0;
+
+ /* immediate timeout */
+ if (timeout == TIME_IMMEDIATE)
+ return 1;
+
+ /* infinite timeout */
+ if (timeout == TIME_INFINITE) {
+ dprintk(KERN_DEBUG "tf_test_s_timeout: "
+ "timeout is infinite\n");
+ *relative_timeout_jiffies = MAX_SCHEDULE_TIMEOUT;
+ return 0;
+ }
+
+ do_gettimeofday(&now);
+ time64 = now.tv_sec;
+ /* will not overflow as operations are done on 64bit values */
+ time64 = (time64 * 1000) + (now.tv_usec / 1000);
+
+ /* timeout expired */
+ if (time64 >= timeout) {
+ dprintk(KERN_DEBUG "tf_test_s_timeout: timeout expired\n");
+ return 1;
+ }
+
+ /*
+ * finite timeout, compute relative_timeout_jiffies
+ */
+ /* will not overflow as time64 < timeout */
+ timeout -= time64;
+
+ /* guarantee *relative_timeout_jiffies is a valid timeout */
+ if ((timeout >> 32) != 0)
+ *relative_timeout_jiffies = MAX_JIFFY_OFFSET;
+ else
+ *relative_timeout_jiffies =
+ msecs_to_jiffies((unsigned int) timeout);
+
+ dprintk(KERN_DEBUG "tf_test_s_timeout: timeout is 0x%lx\n",
+ *relative_timeout_jiffies);
+ return 0;
+}
+
+static void tf_copy_answers(struct tf_comm *comm)
+{
+ u32 first_answer;
+ u32 first_free_answer;
+ struct tf_answer_struct *answerStructureTemp;
+
+ if (test_bit(TF_COMM_FLAG_L1_SHARED_ALLOCATED, &(comm->flags))) {
+ spin_lock(&comm->lock);
+ first_free_answer = tf_read_reg32(
+ &comm->pBuffer->first_free_answer);
+ first_answer = tf_read_reg32(
+ &comm->pBuffer->first_answer);
+
+ while (first_answer != first_free_answer) {
+ /* answer queue not empty */
+ union tf_answer sComAnswer;
+ struct tf_answer_header header;
+
+ /*
+ * the size of the command in words of 32bit, not in
+ * bytes
+ */
+ u32 command_size;
+ u32 i;
+ u32 *temp = (uint32_t *) &header;
+
+ dprintk(KERN_INFO
+ "[pid=%d] tf_copy_answers(%p): "
+ "Read answers from L1\n",
+ current->pid, comm);
+
+ /* Read the answer header */
+ for (i = 0;
+ i < sizeof(struct tf_answer_header)/sizeof(u32);
+ i++)
+ temp[i] = comm->pBuffer->answer_queue[
+ (first_answer + i) %
+ TF_S_ANSWER_QUEUE_CAPACITY];
+
+ /* Read the answer from the L1_Buffer*/
+ command_size = header.message_size +
+ sizeof(struct tf_answer_header)/sizeof(u32);
+ temp = (uint32_t *) &sComAnswer;
+ for (i = 0; i < command_size; i++)
+ temp[i] = comm->pBuffer->answer_queue[
+ (first_answer + i) %
+ TF_S_ANSWER_QUEUE_CAPACITY];
+
+ answerStructureTemp = (struct tf_answer_struct *)
+ sComAnswer.header.operation_id;
+
+ tf_dump_answer(&sComAnswer);
+
+ memcpy(answerStructureTemp->answer, &sComAnswer,
+ command_size * sizeof(u32));
+ answerStructureTemp->answer_copied = true;
+
+ first_answer += command_size;
+ tf_write_reg32(&comm->pBuffer->first_answer,
+ first_answer);
+ }
+ spin_unlock(&(comm->lock));
+ }
+}
+
+static void tf_copy_command(
+ struct tf_comm *comm,
+ union tf_command *command,
+ struct tf_connection *connection,
+ enum TF_COMMAND_STATE *command_status)
+{
+ if ((test_bit(TF_COMM_FLAG_L1_SHARED_ALLOCATED, &(comm->flags)))
+ && (command != NULL)) {
+ /*
+ * Write the message in the message queue.
+ */
+
+ if (*command_status == TF_COMMAND_STATE_PENDING) {
+ u32 command_size;
+ u32 queue_words_count;
+ u32 i;
+ u32 first_free_command;
+ u32 first_command;
+
+ spin_lock(&comm->lock);
+
+ first_command = tf_read_reg32(
+ &comm->pBuffer->first_command);
+ first_free_command = tf_read_reg32(
+ &comm->pBuffer->first_free_command);
+
+ queue_words_count = first_free_command - first_command;
+ command_size = command->header.message_size +
+ sizeof(struct tf_command_header)/sizeof(u32);
+ if ((queue_words_count + command_size) <
+ TF_N_MESSAGE_QUEUE_CAPACITY) {
+ /*
+ * Command queue is not full.
+ * If the Command queue is full,
+ * the command will be copied at
+ * another iteration
+ * of the current function.
+ */
+
+ /*
+ * Change the conn state
+ */
+ if (connection == NULL)
+ goto copy;
+
+ spin_lock(&(connection->state_lock));
+
+ if ((connection->state ==
+ TF_CONN_STATE_NO_DEVICE_CONTEXT)
+ &&
+ (command->header.message_type ==
+ TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT)) {
+
+ dprintk(KERN_INFO
+ "tf_copy_command(%p):"
+ "Conn state is DEVICE_CONTEXT_SENT\n",
+ connection);
+ connection->state =
+ TF_CONN_STATE_CREATE_DEVICE_CONTEXT_SENT;
+ } else if ((connection->state !=
+ TF_CONN_STATE_VALID_DEVICE_CONTEXT)
+ &&
+ (command->header.message_type !=
+ TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT)) {
+ /* The connection
+ * is no longer valid.
+ * We may not send any command on it,
+ * not even another
+ * DESTROY_DEVICE_CONTEXT.
+ */
+ dprintk(KERN_INFO
+ "[pid=%d] tf_copy_command(%p): "
+ "Connection no longer valid."
+ "ABORT\n",
+ current->pid, connection);
+ *command_status =
+ TF_COMMAND_STATE_ABORTED;
+ spin_unlock(
+ &(connection->state_lock));
+ spin_unlock(
+ &comm->lock);
+ return;
+ } else if (
+ (command->header.message_type ==
+ TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT) &&
+ (connection->state ==
+ TF_CONN_STATE_VALID_DEVICE_CONTEXT)
+ ) {
+ dprintk(KERN_INFO
+ "[pid=%d] tf_copy_command(%p): "
+ "Conn state is "
+ "DESTROY_DEVICE_CONTEXT_SENT\n",
+ current->pid, connection);
+ connection->state =
+ TF_CONN_STATE_DESTROY_DEVICE_CONTEXT_SENT;
+ }
+ spin_unlock(&(connection->state_lock));
+copy:
+ /*
+ * Copy the command to L1 Buffer
+ */
+ dprintk(KERN_INFO
+ "[pid=%d] tf_copy_command(%p): "
+ "Write Message in the queue\n",
+ current->pid, command);
+ tf_dump_command(command);
+
+ for (i = 0; i < command_size; i++)
+ comm->pBuffer->command_queue[
+ (first_free_command + i) %
+ TF_N_MESSAGE_QUEUE_CAPACITY] =
+ ((uint32_t *) command)[i];
+
+ *command_status =
+ TF_COMMAND_STATE_SENT;
+ first_free_command += command_size;
+
+ tf_write_reg32(
+ &comm->
+ pBuffer->first_free_command,
+ first_free_command);
+ }
+ spin_unlock(&comm->lock);
+ }
+ }
+}
+
+/*
+ * Sends the specified message through the specified communication channel.
+ *
+ * This function sends the command and waits for the answer
+ *
+ * Returns zero upon successful completion, or an appropriate error code upon
+ * failure.
+ */
+static int tf_send_recv(struct tf_comm *comm,
+ union tf_command *command,
+ struct tf_answer_struct *answerStruct,
+ struct tf_connection *connection,
+ int bKillable
+ #ifdef CONFIG_TF_ZEBRA
+ , bool *secure_is_idle
+ #endif
+ )
+{
+ int result;
+ u64 timeout;
+ signed long nRelativeTimeoutJiffies;
+ bool wait_prepared = false;
+ enum TF_COMMAND_STATE command_status = TF_COMMAND_STATE_PENDING;
+ DEFINE_WAIT(wait);
+#ifdef CONFIG_FREEZER
+ unsigned long saved_flags;
+#endif
+ dprintk(KERN_INFO "[pid=%d] tf_send_recv(%p)\n",
+ current->pid, command);
+
+#ifdef CONFIG_FREEZER
+ saved_flags = current->flags;
+ current->flags |= PF_FREEZER_NOSIG;
+#endif
+
+ /*
+ * Read all answers from the answer queue
+ */
+copy_answers:
+ tf_copy_answers(comm);
+
+ tf_copy_command(comm, command, connection, &command_status);
+
+ /*
+ * Notify all waiting threads
+ */
+ wake_up(&(comm->wait_queue));
+
+#ifdef CONFIG_FREEZER
+ if (unlikely(freezing(current))) {
+
+#ifdef CONFIG_TF_ZEBRA
+ if (!(*secure_is_idle)) {
+ if (tf_schedule_secure_world(comm, true) ==
+ STATUS_PENDING)
+ goto copy_answers;
+
+ tf_l4sec_clkdm_allow_idle(true);
+ *secure_is_idle = true;
+ }
+#endif
+
+ dprintk(KERN_INFO
+ "Entering refrigerator.\n");
+ refrigerator();
+ dprintk(KERN_INFO
+ "Left refrigerator.\n");
+ goto copy_answers;
+ }
+#endif
+
+#ifndef CONFIG_PREEMPT
+ if (need_resched())
+ schedule();
+#endif
+
+#ifdef CONFIG_TF_ZEBRA
+ /*
+ * Handle RPC (if any)
+ */
+ if (tf_rpc_execute(comm) == RPC_NON_YIELD)
+ goto schedule_secure_world;
+#endif
+
+ /*
+ * Join wait queue
+ */
+ /*dprintk(KERN_INFO "[pid=%d] tf_send_recv(%p): Prepare to wait\n",
+ current->pid, command);*/
+ prepare_to_wait(&comm->wait_queue, &wait,
+ bKillable ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+ wait_prepared = true;
+
+ /*
+ * Check if our answer is available
+ */
+ if (command_status == TF_COMMAND_STATE_ABORTED) {
+ /* Not waiting for an answer, return error code */
+ result = -EINTR;
+ dprintk(KERN_ERR "[pid=%d] tf_send_recv: "
+ "Command status is ABORTED."
+ "Exit with 0x%x\n",
+ current->pid, result);
+ goto exit;
+ }
+ if (answerStruct->answer_copied) {
+ dprintk(KERN_INFO "[pid=%d] tf_send_recv: "
+ "Received answer (type 0x%02X)\n",
+ current->pid,
+ answerStruct->answer->header.message_type);
+ result = 0;
+ goto exit;
+ }
+
+ /*
+ * Check if a signal is pending
+ */
+ if (bKillable && (sigkill_pending())) {
+ if (command_status == TF_COMMAND_STATE_PENDING)
+ /*Command was not sent. */
+ result = -EINTR;
+ else
+ /* Command was sent but no answer was received yet. */
+ result = -EIO;
+
+ dprintk(KERN_ERR "[pid=%d] tf_send_recv: "
+ "Signal Pending. Return error %d\n",
+ current->pid, result);
+ goto exit;
+ }
+
+ /*
+ * Check if secure world is schedulable. It is schedulable if at
+ * least one of the following conditions holds:
+ * + it is still initializing (TF_COMM_FLAG_L1_SHARED_ALLOCATED
+ * is not set);
+ * + there is a command in the queue;
+ * + the secure world timeout is zero.
+ */
+ if (test_bit(TF_COMM_FLAG_L1_SHARED_ALLOCATED, &(comm->flags))) {
+ u32 first_free_command;
+ u32 first_command;
+ spin_lock(&comm->lock);
+ first_command = tf_read_reg32(
+ &comm->pBuffer->first_command);
+ first_free_command = tf_read_reg32(
+ &comm->pBuffer->first_free_command);
+ spin_unlock(&comm->lock);
+ tf_read_timeout(comm, &timeout);
+ if ((first_free_command == first_command) &&
+ (tf_test_s_timeout(timeout,
+ &nRelativeTimeoutJiffies) == 0))
+ /*
+ * If command queue is empty and if timeout has not
+ * expired secure world is not schedulable
+ */
+ goto wait;
+ }
+
+ finish_wait(&comm->wait_queue, &wait);
+ wait_prepared = false;
+
+ /*
+ * Yield to the Secure World
+ */
+#ifdef CONFIG_TF_ZEBRA
+schedule_secure_world:
+ if (*secure_is_idle) {
+ tf_l4sec_clkdm_wakeup(true);
+ *secure_is_idle = false;
+ }
+#endif
+
+ result = tf_schedule_secure_world(comm, false);
+ if (result < 0)
+ goto exit;
+ goto copy_answers;
+
+wait:
+ if (bKillable && (sigkill_pending())) {
+ if (command_status == TF_COMMAND_STATE_PENDING)
+ result = -EINTR; /* Command was not sent. */
+ else
+ /* Command was sent but no answer was received yet. */
+ result = -EIO;
+
+ dprintk(KERN_ERR "[pid=%d] tf_send_recv: "
+ "Signal Pending while waiting. Return error %d\n",
+ current->pid, result);
+ goto exit;
+ }
+
+ if (nRelativeTimeoutJiffies == MAX_SCHEDULE_TIMEOUT)
+ dprintk(KERN_INFO "[pid=%d] tf_send_recv: "
+ "prepare to sleep infinitely\n", current->pid);
+ else
+ dprintk(KERN_INFO "tf_send_recv: "
+ "prepare to sleep 0x%lx jiffies\n",
+ nRelativeTimeoutJiffies);
+
+#ifdef CONFIG_TF_ZEBRA
+ if (!(*secure_is_idle)) {
+ if (tf_schedule_secure_world(comm, true) == STATUS_PENDING) {
+ finish_wait(&comm->wait_queue, &wait);
+ wait_prepared = false;
+ goto copy_answers;
+ }
+ tf_l4sec_clkdm_allow_idle(true);
+ *secure_is_idle = true;
+ }
+#endif
+
+ /* go to sleep */
+ if (schedule_timeout(nRelativeTimeoutJiffies) == 0)
+ dprintk(KERN_INFO
+ "tf_send_recv: timeout expired\n");
+ else
+ dprintk(KERN_INFO
+ "tf_send_recv: signal delivered\n");
+
+ finish_wait(&comm->wait_queue, &wait);
+ wait_prepared = false;
+ goto copy_answers;
+
+exit:
+ if (wait_prepared) {
+ finish_wait(&comm->wait_queue, &wait);
+ wait_prepared = false;
+ }
+
+#ifdef CONFIG_TF_ZEBRA
+ if ((!(*secure_is_idle)) && (result != -EIO)) {
+ if (tf_schedule_secure_world(comm, true) == STATUS_PENDING)
+ goto copy_answers;
+
+ tf_l4sec_clkdm_allow_idle(true);
+ *secure_is_idle = true;
+ }
+#endif
+
+#ifdef CONFIG_FREEZER
+ current->flags &= ~(PF_FREEZER_NOSIG);
+ current->flags |= (saved_flags & PF_FREEZER_NOSIG);
+#endif
+
+ return result;
+}
+
+/*
+ * Sends the specified message through the specified communication channel.
+ *
+ * This function sends the message and waits for the corresponding answer
+ * It may return if a signal needs to be delivered.
+ *
+ * Returns zero upon successful completion, or an appropriate error code upon
+ * failure.
+ */
+int tf_send_receive(struct tf_comm *comm,
+ union tf_command *command,
+ union tf_answer *answer,
+ struct tf_connection *connection,
+ bool bKillable)
+{
+ int error;
+ struct tf_answer_struct answerStructure;
+#ifdef CONFIG_SMP
+ long ret_affinity;
+ cpumask_t saved_cpu_mask;
+ cpumask_t local_cpu_mask = CPU_MASK_NONE;
+#endif
+#ifdef CONFIG_TF_ZEBRA
+ bool secure_is_idle = true;
+#endif
+
+ answerStructure.answer = answer;
+ answerStructure.answer_copied = false;
+
+ if (command != NULL)
+ command->header.operation_id = (u32) &answerStructure;
+
+ dprintk(KERN_INFO "tf_send_receive\n");
+
+#ifdef CONFIG_TF_ZEBRA
+ if (!test_bit(TF_COMM_FLAG_PA_AVAILABLE, &comm->flags)) {
+ dprintk(KERN_ERR "tf_send_receive(%p): "
+ "Secure world not started\n", comm);
+
+ return -EFAULT;
+ }
+#endif
+
+ if (test_bit(TF_COMM_FLAG_TERMINATING, &(comm->flags)) != 0) {
+ dprintk(KERN_DEBUG
+ "tf_send_receive: Flag Terminating is set\n");
+ return 0;
+ }
+
+#ifdef CONFIG_SMP
+ cpu_set(0, local_cpu_mask);
+ sched_getaffinity(0, &saved_cpu_mask);
+ ret_affinity = sched_setaffinity(0, &local_cpu_mask);
+ if (ret_affinity != 0)
+ dprintk(KERN_ERR "sched_setaffinity #1 -> 0x%lX", ret_affinity);
+#endif
+
+
+ /*
+ * Send the command
+ */
+ error = tf_send_recv(comm,
+ command, &answerStructure, connection, bKillable
+ #ifdef CONFIG_TF_ZEBRA
+ , &secure_is_idle
+ #endif
+ );
+
+ if (!bKillable && sigkill_pending()) {
+ if ((command->header.message_type ==
+ TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT) &&
+ (answer->create_device_context.error_code ==
+ S_SUCCESS)) {
+
+ /*
+ * CREATE_DEVICE_CONTEXT was interrupted.
+ */
+ dprintk(KERN_INFO "tf_send_receive: "
+ "sending DESTROY_DEVICE_CONTEXT\n");
+ answerStructure.answer = answer;
+ answerStructure.answer_copied = false;
+
+ command->header.message_type =
+ TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT;
+ command->header.message_size =
+ (sizeof(struct
+ tf_command_destroy_device_context) -
+ sizeof(struct tf_command_header))/sizeof(u32);
+ command->header.operation_id =
+ (u32) &answerStructure;
+ command->destroy_device_context.device_context =
+ answer->create_device_context.
+ device_context;
+
+ goto destroy_context;
+ }
+ }
+
+ if (error == 0) {
+ /*
+ * tf_send_recv returned Success.
+ */
+ if (command->header.message_type ==
+ TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT) {
+ spin_lock(&(connection->state_lock));
+ connection->state = TF_CONN_STATE_VALID_DEVICE_CONTEXT;
+ spin_unlock(&(connection->state_lock));
+ } else if (command->header.message_type ==
+ TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT) {
+ spin_lock(&(connection->state_lock));
+ connection->state = TF_CONN_STATE_NO_DEVICE_CONTEXT;
+ spin_unlock(&(connection->state_lock));
+ }
+ } else if (error == -EINTR) {
+ /*
+ * No command was sent, return failure.
+ */
+ dprintk(KERN_ERR
+ "tf_send_receive: "
+ "tf_send_recv failed (error %d) !\n",
+ error);
+ } else if (error == -EIO) {
+ /*
+ * A command was sent but its answer is still pending.
+ */
+
+ /* means bKillable is true */
+ dprintk(KERN_ERR
+ "tf_send_receive: "
+ "tf_send_recv interrupted (error %d)."
+ "Send DESTROY_DEVICE_CONTEXT.\n", error);
+
+ /* Send the DESTROY_DEVICE_CONTEXT. */
+ answerStructure.answer = answer;
+ answerStructure.answer_copied = false;
+
+ command->header.message_type =
+ TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT;
+ command->header.message_size =
+ (sizeof(struct tf_command_destroy_device_context) -
+ sizeof(struct tf_command_header))/sizeof(u32);
+ command->header.operation_id =
+ (u32) &answerStructure;
+ command->destroy_device_context.device_context =
+ connection->device_context;
+
+ error = tf_send_recv(comm,
+ command, &answerStructure, connection, false
+ #ifdef CONFIG_TF_ZEBRA
+ , &secure_is_idle
+ #endif
+ );
+ if (error == -EINTR) {
+ /*
+ * Another thread already sent
+ * DESTROY_DEVICE_CONTEXT.
+ * We must still wait for the answer
+ * to the original command.
+ */
+ command = NULL;
+ goto destroy_context;
+ } else {
+ /* An answer was received.
+ * Check if it is the answer
+ * to the DESTROY_DEVICE_CONTEXT.
+ */
+ spin_lock(&comm->lock);
+ if (answer->header.message_type !=
+ TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT) {
+ answerStructure.answer_copied = false;
+ }
+ spin_unlock(&comm->lock);
+ if (!answerStructure.answer_copied) {
+ /* Answer to DESTROY_DEVICE_CONTEXT
+ * was not yet received.
+ * Wait for the answer.
+ */
+ dprintk(KERN_INFO
+ "[pid=%d] tf_send_receive:"
+ "Answer to DESTROY_DEVICE_CONTEXT"
+ "not yet received.Retry\n",
+ current->pid);
+ command = NULL;
+ goto destroy_context;
+ }
+ }
+ }
+
+ dprintk(KERN_INFO "tf_send_receive(): Message answer ready\n");
+ goto exit;
+
+destroy_context:
+ error = tf_send_recv(comm,
+ command, &answerStructure, connection, false
+ #ifdef CONFIG_TF_ZEBRA
+ , &secure_is_idle
+ #endif
+ );
+
+ /*
+ * tf_send_recv cannot return an error because
+ * it's not killable and not within a connection
+ */
+ BUG_ON(error != 0);
+
+ /* Reset the state, so a new CREATE DEVICE CONTEXT can be sent */
+ spin_lock(&(connection->state_lock));
+ connection->state = TF_CONN_STATE_NO_DEVICE_CONTEXT;
+ spin_unlock(&(connection->state_lock));
+
+exit:
+
+#ifdef CONFIG_SMP
+ ret_affinity = sched_setaffinity(0, &saved_cpu_mask);
+ if (ret_affinity != 0)
+ dprintk(KERN_ERR "sched_setaffinity #2 -> 0x%lX", ret_affinity);
+#endif
+ return error;
+}
+
+/*----------------------------------------------------------------------------
+ * Power management
+ *----------------------------------------------------------------------------*/
+
+
+/*
+ * Handles all the power management calls.
+ * The operation is the type of power management
+ * operation to be performed.
+ *
+ * This routine will only return if a failure occured or if
+ * the required opwer management is of type "resume".
+ * "Hibernate" and "Shutdown" should lock when doing the
+ * corresponding SMC to the Secure World
+ */
+int tf_power_management(struct tf_comm *comm,
+ enum TF_POWER_OPERATION operation)
+{
+ u32 status;
+ int error = 0;
+
+ dprintk(KERN_INFO "tf_power_management(%d)\n", operation);
+
+#ifdef CONFIG_TF_ZEBRA
+ if (!test_bit(TF_COMM_FLAG_PA_AVAILABLE, &comm->flags)) {
+ dprintk(KERN_INFO "tf_power_management(%p): "
+ "succeeded (not started)\n", comm);
+
+ return 0;
+ }
+#endif
+
+ status = ((tf_read_reg32(&(comm->pBuffer->status_s))
+ & TF_STATUS_POWER_STATE_MASK)
+ >> TF_STATUS_POWER_STATE_SHIFT);
+
+ switch (operation) {
+ case TF_POWER_OPERATION_SHUTDOWN:
+ switch (status) {
+ case TF_POWER_MODE_ACTIVE:
+ error = tf_pm_shutdown(comm);
+
+ if (error) {
+ dprintk(KERN_ERR "tf_power_management(): "
+ "Failed with error code 0x%08x\n",
+ error);
+ goto error;
+ }
+ break;
+
+ default:
+ goto not_allowed;
+ }
+ break;
+
+ case TF_POWER_OPERATION_HIBERNATE:
+ switch (status) {
+ case TF_POWER_MODE_ACTIVE:
+ error = tf_pm_hibernate(comm);
+
+ if (error) {
+ dprintk(KERN_ERR "tf_power_management(): "
+ "Failed with error code 0x%08x\n",
+ error);
+ goto error;
+ }
+ break;
+
+ default:
+ goto not_allowed;
+ }
+ break;
+
+ case TF_POWER_OPERATION_RESUME:
+ error = tf_pm_resume(comm);
+
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_power_management(): "
+ "Failed with error code 0x%08x\n",
+ error);
+ goto error;
+ }
+ break;
+ }
+
+ dprintk(KERN_INFO "tf_power_management(): succeeded\n");
+ return 0;
+
+not_allowed:
+ dprintk(KERN_ERR "tf_power_management(): "
+ "Power command not allowed in current "
+ "Secure World state %d\n", status);
+ error = -ENOTTY;
+error:
+ return error;
+}
diff --git a/security/smc/tf_comm.h b/security/smc/tf_comm.h
new file mode 100644
index 0000000..48bd934
--- /dev/null
+++ b/security/smc/tf_comm.h
@@ -0,0 +1,204 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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
+ */
+
+#ifndef __TF_COMM_H__
+#define __TF_COMM_H__
+
+#include "tf_defs.h"
+#include "tf_protocol.h"
+
+/*----------------------------------------------------------------------------
+ * Misc
+ *----------------------------------------------------------------------------*/
+
+void tf_set_current_time(struct tf_comm *comm);
+
+/*
+ * Atomic accesses to 32-bit variables in the L1 Shared buffer
+ */
+static inline u32 tf_read_reg32(const u32 *comm_buffer)
+{
+ u32 result;
+
+ __asm__ __volatile__("@ tf_read_reg32\n"
+ "ldrex %0, [%1]\n"
+ : "=&r" (result)
+ : "r" (comm_buffer)
+ );
+
+ return result;
+}
+
+static inline void tf_write_reg32(void *comm_buffer, u32 value)
+{
+ u32 tmp;
+
+ __asm__ __volatile__("@ tf_write_reg32\n"
+ "1: ldrex %0, [%2]\n"
+ " strex %0, %1, [%2]\n"
+ " teq %0, #0\n"
+ " bne 1b"
+ : "=&r" (tmp)
+ : "r" (value), "r" (comm_buffer)
+ : "cc"
+ );
+}
+
+/*
+ * Atomic accesses to 64-bit variables in the L1 Shared buffer
+ */
+static inline u64 tf_read_reg64(void *comm_buffer)
+{
+ u64 result;
+
+ __asm__ __volatile__("@ tf_read_reg64\n"
+ "ldrexd %0, [%1]\n"
+ : "=&r" (result)
+ : "r" (comm_buffer)
+ );
+
+ return result;
+}
+
+static inline void tf_write_reg64(void *comm_buffer, u64 value)
+{
+ u64 tmp;
+
+ __asm__ __volatile__("@ tf_write_reg64\n"
+ "1: ldrexd %0, [%2]\n"
+ " strexd %0, %1, [%2]\n"
+ " teq %0, #0\n"
+ " bne 1b"
+ : "=&r" (tmp)
+ : "r" (value), "r" (comm_buffer)
+ : "cc"
+ );
+}
+
+/*----------------------------------------------------------------------------
+ * SMC operations
+ *----------------------------------------------------------------------------*/
+
+/* RPC return values */
+#define RPC_NO 0x00 /* No RPC to execute */
+#define RPC_YIELD 0x01 /* Yield RPC */
+#define RPC_NON_YIELD 0x02 /* non-Yield RPC */
+
+int tf_rpc_execute(struct tf_comm *comm);
+
+/*----------------------------------------------------------------------------
+ * Shared memory related operations
+ *----------------------------------------------------------------------------*/
+
+#define L1_DESCRIPTOR_FAULT (0x00000000)
+#define L2_DESCRIPTOR_FAULT (0x00000000)
+
+#define L2_DESCRIPTOR_ADDR_MASK (0xFFFFF000)
+
+#define DESCRIPTOR_V13_12_MASK (0x3 << PAGE_SHIFT)
+#define DESCRIPTOR_V13_12_GET(a) ((a & DESCRIPTOR_V13_12_MASK) >> PAGE_SHIFT)
+
+struct tf_coarse_page_table *tf_alloc_coarse_page_table(
+ struct tf_coarse_page_table_allocation_context *alloc_context,
+ u32 type);
+
+void tf_free_coarse_page_table(
+ struct tf_coarse_page_table_allocation_context *alloc_context,
+ struct tf_coarse_page_table *coarse_pg_table,
+ int force);
+
+void tf_init_coarse_page_table_allocator(
+ struct tf_coarse_page_table_allocation_context *alloc_context);
+
+void tf_release_coarse_page_table_allocator(
+ struct tf_coarse_page_table_allocation_context *alloc_context);
+
+struct page *tf_l2_page_descriptor_to_page(u32 l2_page_descriptor);
+
+u32 tf_get_l2_descriptor_common(u32 vaddr, struct mm_struct *mm);
+
+void tf_cleanup_shared_memory(
+ struct tf_coarse_page_table_allocation_context *alloc_context,
+ struct tf_shmem_desc *shmem_desc,
+ u32 full_cleanup);
+
+int tf_fill_descriptor_table(
+ struct tf_coarse_page_table_allocation_context *alloc_context,
+ struct tf_shmem_desc *shmem_desc,
+ u32 buffer,
+ struct vm_area_struct **vmas,
+ u32 descriptors[TF_MAX_COARSE_PAGES],
+ u32 buffer_size,
+ u32 *buffer_start_offset,
+ bool in_user_space,
+ u32 flags,
+ u32 *descriptor_count);
+
+/*----------------------------------------------------------------------------
+ * Standard communication operations
+ *----------------------------------------------------------------------------*/
+
+#define STATUS_PENDING 0x00000001
+
+int tf_schedule_secure_world(struct tf_comm *comm, bool prepare_exit);
+
+int tf_send_receive(
+ struct tf_comm *comm,
+ union tf_command *command,
+ union tf_answer *answer,
+ struct tf_connection *connection,
+ bool bKillable);
+
+
+/**
+ * get a pointer to the secure world description.
+ * This points directly into the L1 shared buffer
+ * and is valid only once the communication has
+ * been initialized
+ **/
+u8 *tf_get_description(struct tf_comm *comm);
+
+/*----------------------------------------------------------------------------
+ * Power management
+ *----------------------------------------------------------------------------*/
+
+enum TF_POWER_OPERATION {
+ TF_POWER_OPERATION_HIBERNATE = 1,
+ TF_POWER_OPERATION_SHUTDOWN = 2,
+ TF_POWER_OPERATION_RESUME = 3,
+};
+
+int tf_pm_hibernate(struct tf_comm *comm);
+int tf_pm_resume(struct tf_comm *comm);
+int tf_pm_shutdown(struct tf_comm *comm);
+
+int tf_power_management(struct tf_comm *comm,
+ enum TF_POWER_OPERATION operation);
+
+
+/*----------------------------------------------------------------------------
+ * Communication initialization and termination
+ *----------------------------------------------------------------------------*/
+
+int tf_init(struct tf_comm *comm);
+
+void tf_terminate(struct tf_comm *comm);
+
+
+#endif /* __TF_COMM_H__ */
diff --git a/security/smc/tf_comm_mshield.c b/security/smc/tf_comm_mshield.c
new file mode 100644
index 0000000..c36473e
--- /dev/null
+++ b/security/smc/tf_comm_mshield.c
@@ -0,0 +1,1013 @@
+/**
+ * Copyright (c) 2010 Trusted Logic S.A.
+ * 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 <asm/div64.h>
+#include <asm/system.h>
+#include <asm/cputype.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/page-flags.h>
+#include <linux/pagemap.h>
+#include <linux/vmalloc.h>
+#include <linux/version.h>
+#include <linux/jiffies.h>
+#include <linux/dma-mapping.h>
+#include <linux/cpu.h>
+
+#include <asm/cacheflush.h>
+
+#include "tf_defs.h"
+#include "tf_comm.h"
+#include "tf_util.h"
+#include "tf_conn.h"
+#include "tf_zebra.h"
+#include "tf_crypto.h"
+
+/*--------------------------------------------------------------------------
+ * Internal constants
+ *-------------------------------------------------------------------------- */
+
+/* RPC commands */
+#define RPC_CMD_YIELD 0x00
+#define RPC_CMD_INIT 0x01
+#define RPC_CMD_TRACE 0x02
+
+/* RPC return values to secure world */
+#define RPC_SUCCESS 0x00000000
+#define RPC_ERROR_BAD_PARAMETERS 0xFFFF0006
+#define RPC_ERROR_CONNECTION_PROTOCOL 0xFFFF3020
+
+/*
+ * RPC call status
+ *
+ * 0: the secure world yielded due to an interrupt
+ * 1: the secure world yielded on an RPC (no public world thread is handling it)
+ * 2: the secure world yielded on an RPC and the response to that RPC is now in
+ * place
+ */
+#define RPC_ADVANCEMENT_NONE 0
+#define RPC_ADVANCEMENT_PENDING 1
+#define RPC_ADVANCEMENT_FINISHED 2
+
+u32 g_RPC_advancement;
+u32 g_RPC_parameters[4] = {0, 0, 0, 0};
+u32 g_secure_task_id;
+u32 g_service_end;
+
+/*
+ * Secure ROMCode HAL API Identifiers
+ */
+#define API_HAL_SDP_RUNTIMEINIT_INDEX 0x04
+#define API_HAL_LM_PALOAD_INDEX 0x05
+#define API_HAL_LM_PAUNLOADALL_INDEX 0x07
+#define API_HAL_TASK_MGR_RPCINIT_INDEX 0x08
+#define API_HAL_KM_GETSECUREROMCODECRC_INDEX 0x0B
+#define API_HAL_SEC_L3_RAM_RESIZE_INDEX 0x17
+
+#define API_HAL_RET_VALUE_OK 0x0
+
+/* SE entry flags */
+#define FLAG_START_HAL_CRITICAL 0x4
+#define FLAG_IRQFIQ_MASK 0x3
+#define FLAG_IRQ_ENABLE 0x2
+#define FLAG_FIQ_ENABLE 0x1
+
+#define SMICODEPUB_IRQ_END 0xFE
+#define SMICODEPUB_FIQ_END 0xFD
+#define SMICODEPUB_RPC_END 0xFC
+
+#define SEC_RAM_SIZE_40KB 0x0000A000
+#define SEC_RAM_SIZE_48KB 0x0000C000
+#define SEC_RAM_SIZE_52KB 0x0000D000
+#define SEC_RAM_SIZE_60KB 0x0000F000
+#define SEC_RAM_SIZE_64KB 0x00010000
+
+struct tf_ns_pa_info {
+ void *certificate;
+ void *parameters;
+ void *results;
+};
+
+/*
+ * AFY: I would like to remove the L0 buffer altogether:
+ * - you can use the L1 shared buffer to pass the RPC parameters and results:
+ * I think these easily fit in 256 bytes and you can use the area at
+ * offset 0x2C0-0x3BF in the L1 shared buffer
+ */
+struct tf_init_buffer {
+ u32 init_status;
+ u32 protocol_version;
+ u32 l1_shared_buffer_descr;
+ u32 backing_store_addr;
+ u32 backext_storage_addr;
+ u32 workspace_addr;
+ u32 workspace_size;
+ u32 properties_length;
+ u8 properties_buffer[1];
+};
+
+#ifdef CONFIG_HAS_WAKELOCK
+static struct wake_lock g_tf_wake_lock;
+static u32 tf_wake_lock_count = 0;
+#endif
+
+static struct clockdomain *smc_l4_sec_clkdm;
+static u32 smc_l4_sec_clkdm_use_count = 0;
+
+static int __init tf_early_init(void)
+{
+ g_secure_task_id = 0;
+
+ dprintk(KERN_INFO "SMC early init\n");
+
+ smc_l4_sec_clkdm = clkdm_lookup("l4_secure_clkdm");
+ if (smc_l4_sec_clkdm == NULL)
+ return -EFAULT;
+
+#ifdef CONFIG_HAS_WAKELOCK
+ wake_lock_init(&g_tf_wake_lock, WAKE_LOCK_SUSPEND,
+ TF_DEVICE_BASE_NAME);
+#endif
+
+ return 0;
+}
+early_initcall(tf_early_init);
+
+/*
+ * Function responsible for formatting parameters to pass from NS world to
+ * S world
+ */
+u32 omap4_secure_dispatcher(u32 app_id, u32 flags, u32 nargs,
+ u32 arg1, u32 arg2, u32 arg3, u32 arg4)
+{
+ u32 ret;
+ unsigned long iflags;
+ u32 pub2sec_args[5] = {0, 0, 0, 0, 0};
+
+ /*dprintk(KERN_INFO "omap4_secure_dispatcher: "
+ "app_id=0x%08x, flags=0x%08x, nargs=%u\n",
+ app_id, flags, nargs);*/
+
+ /*if (nargs != 0)
+ dprintk(KERN_INFO
+ "omap4_secure_dispatcher: args=%08x, %08x, %08x, %08x\n",
+ arg1, arg2, arg3, arg4);*/
+
+ pub2sec_args[0] = nargs;
+ pub2sec_args[1] = arg1;
+ pub2sec_args[2] = arg2;
+ pub2sec_args[3] = arg3;
+ pub2sec_args[4] = arg4;
+
+ /* Make sure parameters are visible to the secure world */
+ dmac_flush_range((void *)pub2sec_args,
+ (void *)(((u32)(pub2sec_args)) + 5*sizeof(u32)));
+ outer_clean_range(__pa(pub2sec_args),
+ __pa(pub2sec_args) + 5*sizeof(u32));
+ wmb();
+
+ /*
+ * Put L4 Secure clock domain to SW_WKUP so that modules are accessible
+ */
+ tf_l4sec_clkdm_wakeup(false);
+
+ local_irq_save(iflags);
+#ifdef DEBUG
+ BUG_ON((read_mpidr() & 0x00000003) != 0);
+#endif
+ /* proc_id is always 0 */
+ ret = schedule_secure_world(app_id, 0, flags, __pa(pub2sec_args));
+ local_irq_restore(iflags);
+
+ /* Restore the HW_SUP on L4 Sec clock domain so hardware can idle */
+ tf_l4sec_clkdm_allow_idle(false);
+
+ /*dprintk(KERN_INFO "omap4_secure_dispatcher()\n");*/
+
+ return ret;
+}
+
+/* Yields the Secure World */
+int tf_schedule_secure_world(struct tf_comm *comm, bool prepare_exit)
+{
+ int status = 0;
+ int ret;
+ unsigned long iflags;
+ u32 appli_id;
+
+ tf_set_current_time(comm);
+
+ local_irq_save(iflags);
+
+ switch (g_RPC_advancement) {
+ case RPC_ADVANCEMENT_NONE:
+ /* Return from IRQ */
+ appli_id = SMICODEPUB_IRQ_END;
+ if (prepare_exit)
+ status = STATUS_PENDING;
+ break;
+ case RPC_ADVANCEMENT_PENDING:
+ /* nothing to do in this case */
+ goto exit;
+ default:
+ case RPC_ADVANCEMENT_FINISHED:
+ if (prepare_exit)
+ goto exit;
+ appli_id = SMICODEPUB_RPC_END;
+ g_RPC_advancement = RPC_ADVANCEMENT_NONE;
+ break;
+ }
+
+ g_service_end = 1;
+ /* yield to the Secure World */
+ ret = omap4_secure_dispatcher(appli_id, /* app_id */
+ 0, 0, /* flags, nargs */
+ 0, 0, 0, 0); /* arg1, arg2, arg3, arg4 */
+ if (g_service_end != 0) {
+ dprintk(KERN_ERR "Service End ret=%X\n", ret);
+
+ if (ret == 0) {
+ dmac_flush_range((void *)comm->init_shared_buffer,
+ (void *)(((u32)(comm->init_shared_buffer)) +
+ PAGE_SIZE));
+ outer_inv_range(__pa(comm->init_shared_buffer),
+ __pa(comm->init_shared_buffer) +
+ PAGE_SIZE);
+
+ ret = ((struct tf_init_buffer *)
+ (comm->init_shared_buffer))->init_status;
+
+ dprintk(KERN_ERR "SMC PA failure ret=%X\n", ret);
+ if (ret == 0)
+ ret = -EFAULT;
+ }
+ clear_bit(TF_COMM_FLAG_PA_AVAILABLE, &comm->flags);
+ omap4_secure_dispatcher(API_HAL_LM_PAUNLOADALL_INDEX,
+ FLAG_START_HAL_CRITICAL, 0, 0, 0, 0, 0);
+ status = ret;
+ }
+
+exit:
+ local_irq_restore(iflags);
+
+ return status;
+}
+
+/* Initializes the SE (SDP, SRAM resize, RPC handler) */
+static int tf_se_init(struct tf_comm *comm,
+ u32 sdp_backing_store_addr, u32 sdp_bkext_store_addr)
+{
+ int error;
+ unsigned int crc;
+
+ if (comm->se_initialized) {
+ dprintk(KERN_INFO "tf_se_init: SE already initialized... "
+ "nothing to do\n");
+ return 0;
+ }
+
+ /* Secure CRC read */
+ dprintk(KERN_INFO "tf_se_init: Secure CRC Read...\n");
+
+ crc = omap4_secure_dispatcher(API_HAL_KM_GETSECUREROMCODECRC_INDEX,
+ 0, 0, 0, 0, 0, 0);
+ printk(KERN_INFO "SMC: SecureCRC=0x%08X\n", crc);
+
+ /*
+ * Flush caches before resize, just to be sure there is no
+ * pending public data writes back to SRAM that could trigger a
+ * security violation once their address space is marked as
+ * secure.
+ */
+#define OMAP4_SRAM_PA 0x40300000
+#define OMAP4_SRAM_SIZE 0xe000
+ flush_cache_all();
+ outer_flush_range(OMAP4_SRAM_PA,
+ OMAP4_SRAM_PA + OMAP4_SRAM_SIZE);
+ wmb();
+
+ /* SRAM resize */
+ dprintk(KERN_INFO "tf_se_init: SRAM resize (52KB)...\n");
+ error = omap4_secure_dispatcher(API_HAL_SEC_L3_RAM_RESIZE_INDEX,
+ FLAG_FIQ_ENABLE | FLAG_START_HAL_CRITICAL, 1,
+ SEC_RAM_SIZE_52KB, 0, 0, 0);
+
+ if (error == API_HAL_RET_VALUE_OK) {
+ dprintk(KERN_INFO "tf_se_init: SRAM resize OK\n");
+ } else {
+ dprintk(KERN_ERR "tf_se_init: "
+ "SRAM resize failed [0x%x]\n", error);
+ goto error;
+ }
+
+ /* SDP init */
+ dprintk(KERN_INFO "tf_se_init: SDP runtime init..."
+ "(sdp_backing_store_addr=%x, sdp_bkext_store_addr=%x)\n",
+ sdp_backing_store_addr, sdp_bkext_store_addr);
+ error = omap4_secure_dispatcher(API_HAL_SDP_RUNTIMEINIT_INDEX,
+ FLAG_FIQ_ENABLE | FLAG_START_HAL_CRITICAL, 2,
+ sdp_backing_store_addr, sdp_bkext_store_addr, 0, 0);
+
+ if (error == API_HAL_RET_VALUE_OK) {
+ dprintk(KERN_INFO "tf_se_init: SDP runtime init OK\n");
+ } else {
+ dprintk(KERN_ERR "tf_se_init: "
+ "SDP runtime init failed [0x%x]\n", error);
+ goto error;
+ }
+
+ /* RPC init */
+ dprintk(KERN_INFO "tf_se_init: RPC init...\n");
+ error = omap4_secure_dispatcher(API_HAL_TASK_MGR_RPCINIT_INDEX,
+ FLAG_START_HAL_CRITICAL, 1,
+ (u32) (u32(*const) (u32, u32, u32, u32)) &rpc_handler, 0, 0, 0);
+
+ if (error == API_HAL_RET_VALUE_OK) {
+ dprintk(KERN_INFO "tf_se_init: RPC init OK\n");
+ } else {
+ dprintk(KERN_ERR "tf_se_init: "
+ "RPC init failed [0x%x]\n", error);
+ goto error;
+ }
+
+ comm->se_initialized = true;
+
+ return 0;
+
+error:
+ return -EFAULT;
+}
+
+/* Check protocol version returned by the PA */
+static u32 tf_rpc_init(struct tf_comm *comm)
+{
+ u32 protocol_version;
+ u32 rpc_error = RPC_SUCCESS;
+
+ dprintk(KERN_INFO "tf_rpc_init(%p)\n", comm);
+
+ spin_lock(&(comm->lock));
+
+ dmac_flush_range((void *)comm->init_shared_buffer,
+ (void *)(((u32)(comm->init_shared_buffer)) + PAGE_SIZE));
+ outer_inv_range(__pa(comm->init_shared_buffer),
+ __pa(comm->init_shared_buffer) + PAGE_SIZE);
+
+ protocol_version = ((struct tf_init_buffer *)
+ (comm->init_shared_buffer))->protocol_version;
+
+ if ((GET_PROTOCOL_MAJOR_VERSION(protocol_version))
+ != TF_S_PROTOCOL_MAJOR_VERSION) {
+ dprintk(KERN_ERR "SMC: Unsupported SMC Protocol PA Major "
+ "Version (0x%02x, expected 0x%02x)!\n",
+ GET_PROTOCOL_MAJOR_VERSION(protocol_version),
+ TF_S_PROTOCOL_MAJOR_VERSION);
+ rpc_error = RPC_ERROR_CONNECTION_PROTOCOL;
+ } else {
+ rpc_error = RPC_SUCCESS;
+ }
+
+ spin_unlock(&(comm->lock));
+
+ register_smc_public_crypto_digest();
+ register_smc_public_crypto_aes();
+
+ return rpc_error;
+}
+
+static u32 tf_rpc_trace(struct tf_comm *comm)
+{
+ dprintk(KERN_INFO "tf_rpc_trace(%p)\n", comm);
+
+#ifdef CONFIG_SECURE_TRACE
+ spin_lock(&(comm->lock));
+ printk(KERN_INFO "SMC PA: %s",
+ comm->pBuffer->rpc_trace_buffer);
+ spin_unlock(&(comm->lock));
+#endif
+ return RPC_SUCCESS;
+}
+
+/*
+ * Handles RPC calls
+ *
+ * Returns:
+ * - RPC_NO if there was no RPC to execute
+ * - RPC_YIELD if there was a Yield RPC
+ * - RPC_NON_YIELD if there was a non-Yield RPC
+ */
+
+int tf_rpc_execute(struct tf_comm *comm)
+{
+ u32 rpc_command;
+ u32 rpc_error = RPC_NO;
+
+#ifdef DEBUG
+ BUG_ON((read_mpidr() & 0x00000003) != 0);
+#endif
+
+ /* Lock the RPC */
+ mutex_lock(&(comm->rpc_mutex));
+
+ rpc_command = g_RPC_parameters[1];
+
+ if (g_RPC_advancement == RPC_ADVANCEMENT_PENDING) {
+ dprintk(KERN_INFO "tf_rpc_execute: "
+ "Executing CMD=0x%x\n",
+ g_RPC_parameters[1]);
+
+ switch (rpc_command) {
+ case RPC_CMD_YIELD:
+ dprintk(KERN_INFO "tf_rpc_execute: "
+ "RPC_CMD_YIELD\n");
+
+ rpc_error = RPC_YIELD;
+ g_RPC_parameters[0] = RPC_SUCCESS;
+ break;
+
+ case RPC_CMD_TRACE:
+ rpc_error = RPC_NON_YIELD;
+ g_RPC_parameters[0] = tf_rpc_trace(comm);
+ break;
+
+ default:
+ if (tf_crypto_execute_rpc(rpc_command,
+ comm->pBuffer->rpc_cus_buffer) != 0)
+ g_RPC_parameters[0] = RPC_ERROR_BAD_PARAMETERS;
+ else
+ g_RPC_parameters[0] = RPC_SUCCESS;
+ rpc_error = RPC_NON_YIELD;
+ break;
+ }
+ g_RPC_advancement = RPC_ADVANCEMENT_FINISHED;
+ }
+
+ mutex_unlock(&(comm->rpc_mutex));
+
+ dprintk(KERN_INFO "tf_rpc_execute: Return 0x%x\n",
+ rpc_error);
+
+ return rpc_error;
+}
+
+/*--------------------------------------------------------------------------
+ * L4 SEC Clock domain handling
+ *-------------------------------------------------------------------------- */
+
+static DEFINE_SPINLOCK(clk_lock);
+void tf_l4sec_clkdm_wakeup(bool wakelock)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&clk_lock, flags);
+#ifdef CONFIG_HAS_WAKELOCK
+ if (wakelock) {
+ tf_wake_lock_count++;
+ wake_lock(&g_tf_wake_lock);
+ }
+#endif
+ smc_l4_sec_clkdm_use_count++;
+ clkdm_wakeup(smc_l4_sec_clkdm);
+ spin_unlock_irqrestore(&clk_lock, flags);
+}
+
+void tf_l4sec_clkdm_allow_idle(bool wakeunlock)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&clk_lock, flags);
+ smc_l4_sec_clkdm_use_count--;
+ if (smc_l4_sec_clkdm_use_count == 0)
+ clkdm_allow_idle(smc_l4_sec_clkdm);
+#ifdef CONFIG_HAS_WAKELOCK
+ if (wakeunlock){
+ tf_wake_lock_count--;
+ if (tf_wake_lock_count == 0)
+ wake_unlock(&g_tf_wake_lock);
+ }
+#endif
+ spin_unlock_irqrestore(&clk_lock, flags);
+}
+
+/*--------------------------------------------------------------------------
+ * Power management
+ *-------------------------------------------------------------------------- */
+ /*
+ * Perform a Secure World shutdown operation.
+ * The routine does not return if the operation succeeds.
+ * the routine returns an appropriate error code if
+ * the operation fails.
+ */
+int tf_pm_shutdown(struct tf_comm *comm)
+{
+
+ int error;
+ union tf_command command;
+ union tf_answer answer;
+
+ dprintk(KERN_INFO "tf_pm_shutdown()\n");
+
+ memset(&command, 0, sizeof(command));
+
+ command.header.message_type = TF_MESSAGE_TYPE_MANAGEMENT;
+ command.header.message_size =
+ (sizeof(struct tf_command_management) -
+ sizeof(struct tf_command_header))/sizeof(u32);
+
+ command.management.command = TF_MANAGEMENT_SHUTDOWN;
+
+ error = tf_send_receive(
+ comm,
+ &command,
+ &answer,
+ NULL,
+ false);
+
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_pm_shutdown(): "
+ "tf_send_receive failed (error %d)!\n",
+ error);
+ return error;
+ }
+
+#ifdef CONFIG_TF_DRIVER_DEBUG_SUPPORT
+ if (answer.header.error_code != 0)
+ dprintk(KERN_ERR "tf_driver: shutdown failed.\n");
+ else
+ dprintk(KERN_INFO "tf_driver: shutdown succeeded.\n");
+#endif
+
+ return answer.header.error_code;
+}
+
+
+int tf_pm_hibernate(struct tf_comm *comm)
+{
+ struct tf_device *dev = tf_get_device();
+
+ dprintk(KERN_INFO "tf_pm_hibernate()\n");
+
+ /*
+ * As we enter in CORE OFF, the keys are going to be cleared.
+ * Reset the global key context.
+ * When the system leaves CORE OFF, this will force the driver to go
+ * through the secure world which will reconfigure the accelerators.
+ */
+ dev->aes1_key_context = 0;
+ dev->des_key_context = 0;
+#ifndef CONFIG_SMC_KERNEL_CRYPTO
+ dev->sham1_is_public = false;
+#endif
+ return 0;
+}
+
+#ifdef CONFIG_SMC_KERNEL_CRYPTO
+#define DELAYED_RESUME_NONE 0
+#define DELAYED_RESUME_PENDING 1
+#define DELAYED_RESUME_ONGOING 2
+
+static DEFINE_SPINLOCK(tf_delayed_resume_lock);
+static int tf_need_delayed_resume = DELAYED_RESUME_NONE;
+
+int tf_delayed_secure_resume(void)
+{
+ int ret;
+ union tf_command message;
+ union tf_answer answer;
+ struct tf_device *dev = tf_get_device();
+
+ spin_lock(&tf_delayed_resume_lock);
+ if (likely(tf_need_delayed_resume == DELAYED_RESUME_NONE)) {
+ spin_unlock(&tf_delayed_resume_lock);
+ return 0;
+ }
+
+ if (unlikely(tf_need_delayed_resume == DELAYED_RESUME_ONGOING)) {
+ spin_unlock(&tf_delayed_resume_lock);
+
+ /*
+ * Wait for the other caller to actually finish the delayed
+ * resume operation
+ */
+ while (tf_need_delayed_resume != DELAYED_RESUME_NONE)
+ cpu_relax();
+
+ return 0;
+ }
+
+ tf_need_delayed_resume = DELAYED_RESUME_ONGOING;
+ spin_unlock(&tf_delayed_resume_lock);
+
+ /*
+ * When the system leaves CORE OFF, HWA are configured as secure. We
+ * need them as public for the Linux Crypto API.
+ */
+ memset(&message, 0, sizeof(message));
+
+ message.header.message_type = TF_MESSAGE_TYPE_MANAGEMENT;
+ message.header.message_size =
+ (sizeof(struct tf_command_management) -
+ sizeof(struct tf_command_header))/sizeof(u32);
+ message.management.command =
+ TF_MANAGEMENT_RESUME_FROM_CORE_OFF;
+
+ ret = tf_send_receive(&dev->sm, &message, &answer, NULL, false);
+ if (ret) {
+ printk(KERN_ERR "tf_pm_resume(%p): "
+ "tf_send_receive failed (error %d)!\n",
+ &dev->sm, ret);
+
+ unregister_smc_public_crypto_digest();
+ unregister_smc_public_crypto_aes();
+ return ret;
+ }
+
+ if (answer.header.error_code) {
+ unregister_smc_public_crypto_digest();
+ unregister_smc_public_crypto_aes();
+ }
+
+ spin_lock(&tf_delayed_resume_lock);
+ tf_need_delayed_resume = DELAYED_RESUME_NONE;
+ spin_unlock(&tf_delayed_resume_lock);
+
+ return answer.header.error_code;
+}
+#endif
+
+int tf_pm_resume(struct tf_comm *comm)
+{
+
+ dprintk(KERN_INFO "tf_pm_resume()\n");
+ #if 0
+ {
+ void *workspace_va;
+ struct tf_device *dev = tf_get_device();
+ workspace_va = ioremap(dev->workspace_addr,
+ dev->workspace_size);
+ printk(KERN_INFO
+ "Read first word of workspace [0x%x]\n",
+ *(uint32_t *)workspace_va);
+ }
+ #endif
+
+#ifdef CONFIG_SMC_KERNEL_CRYPTO
+ spin_lock(&tf_delayed_resume_lock);
+ tf_need_delayed_resume = DELAYED_RESUME_PENDING;
+ spin_unlock(&tf_delayed_resume_lock);
+#endif
+ return 0;
+}
+
+/*--------------------------------------------------------------------------
+ * Initialization
+ *-------------------------------------------------------------------------- */
+
+int tf_init(struct tf_comm *comm)
+{
+ spin_lock_init(&(comm->lock));
+ comm->flags = 0;
+ comm->pBuffer = NULL;
+ comm->init_shared_buffer = NULL;
+
+ comm->se_initialized = false;
+
+ init_waitqueue_head(&(comm->wait_queue));
+ mutex_init(&(comm->rpc_mutex));
+
+ if (tf_crypto_init() != PUBLIC_CRYPTO_OPERATION_SUCCESS)
+ return -EFAULT;
+
+ if (omap_type() == OMAP2_DEVICE_TYPE_GP) {
+ register_smc_public_crypto_digest();
+ register_smc_public_crypto_aes();
+ }
+
+ return 0;
+}
+
+/* Start the SMC PA */
+int tf_start(struct tf_comm *comm,
+ u32 workspace_addr, u32 workspace_size,
+ u8 *pa_buffer, u32 pa_size,
+ u8 *properties_buffer, u32 properties_length)
+{
+ struct tf_init_buffer *init_shared_buffer = NULL;
+ struct tf_l1_shared_buffer *l1_shared_buffer = NULL;
+ u32 l1_shared_buffer_descr;
+ struct tf_ns_pa_info pa_info;
+ int ret;
+ u32 descr;
+ u32 sdp_backing_store_addr;
+ u32 sdp_bkext_store_addr;
+#ifdef CONFIG_SMP
+ long ret_affinity;
+ cpumask_t saved_cpu_mask;
+ cpumask_t local_cpu_mask = CPU_MASK_NONE;
+
+ /* OMAP4 Secure ROM Code can only be called from CPU0. */
+ cpu_set(0, local_cpu_mask);
+ sched_getaffinity(0, &saved_cpu_mask);
+ ret_affinity = sched_setaffinity(0, &local_cpu_mask);
+ if (ret_affinity != 0)
+ dprintk(KERN_ERR "sched_setaffinity #1 -> 0x%lX", ret_affinity);
+#endif
+
+ tf_l4sec_clkdm_wakeup(true);
+
+ workspace_size -= SZ_1M;
+ sdp_backing_store_addr = workspace_addr + workspace_size;
+ workspace_size -= 0x20000;
+ sdp_bkext_store_addr = workspace_addr + workspace_size;
+
+ /*
+ * Implementation notes:
+ *
+ * 1/ The PA buffer (pa_buffer)is now owned by this function.
+ * In case of error, it is responsible for releasing the buffer.
+ *
+ * 2/ The PA Info and PA Buffer will be freed through a RPC call
+ * at the beginning of the PA entry in the SE.
+ */
+
+ if (test_bit(TF_COMM_FLAG_PA_AVAILABLE, &comm->flags)) {
+ dprintk(KERN_ERR "tf_start(%p): "
+ "The SMC PA is already started\n", comm);
+
+ ret = -EFAULT;
+ goto error1;
+ }
+
+ if (sizeof(struct tf_l1_shared_buffer) != PAGE_SIZE) {
+ dprintk(KERN_ERR "tf_start(%p): "
+ "The L1 structure size is incorrect!\n", comm);
+ ret = -EFAULT;
+ goto error1;
+ }
+
+ ret = tf_se_init(comm, sdp_backing_store_addr,
+ sdp_bkext_store_addr);
+ if (ret != 0) {
+ dprintk(KERN_ERR "tf_start(%p): "
+ "SE initialization failed\n", comm);
+ goto error1;
+ }
+
+ init_shared_buffer =
+ (struct tf_init_buffer *)
+ internal_get_zeroed_page(GFP_KERNEL);
+ if (init_shared_buffer == NULL) {
+ dprintk(KERN_ERR "tf_start(%p): "
+ "Ouf of memory!\n", comm);
+
+ ret = -ENOMEM;
+ goto error1;
+ }
+ /* Ensure the page is mapped */
+ __set_page_locked(virt_to_page(init_shared_buffer));
+
+ l1_shared_buffer =
+ (struct tf_l1_shared_buffer *)
+ internal_get_zeroed_page(GFP_KERNEL);
+
+ if (l1_shared_buffer == NULL) {
+ dprintk(KERN_ERR "tf_start(%p): "
+ "Ouf of memory!\n", comm);
+
+ ret = -ENOMEM;
+ goto error1;
+ }
+ /* Ensure the page is mapped */
+ __set_page_locked(virt_to_page(l1_shared_buffer));
+
+ dprintk(KERN_INFO "tf_start(%p): "
+ "L0SharedBuffer={0x%08x, 0x%08x}\n", comm,
+ (u32) init_shared_buffer, (u32) __pa(init_shared_buffer));
+ dprintk(KERN_INFO "tf_start(%p): "
+ "L1SharedBuffer={0x%08x, 0x%08x}\n", comm,
+ (u32) l1_shared_buffer, (u32) __pa(l1_shared_buffer));
+
+ descr = tf_get_l2_descriptor_common((u32) l1_shared_buffer,
+ current->mm);
+ l1_shared_buffer_descr = (
+ ((u32) __pa(l1_shared_buffer) & 0xFFFFF000) |
+ (descr & 0xFFF));
+
+ pa_info.certificate = (void *) __pa(pa_buffer);
+ pa_info.parameters = (void *) __pa(init_shared_buffer);
+ pa_info.results = (void *) __pa(init_shared_buffer);
+
+ init_shared_buffer->l1_shared_buffer_descr = l1_shared_buffer_descr;
+
+ init_shared_buffer->backing_store_addr = sdp_backing_store_addr;
+ init_shared_buffer->backext_storage_addr = sdp_bkext_store_addr;
+ init_shared_buffer->workspace_addr = workspace_addr;
+ init_shared_buffer->workspace_size = workspace_size;
+
+ init_shared_buffer->properties_length = properties_length;
+ if (properties_length == 0) {
+ init_shared_buffer->properties_buffer[0] = 0;
+ } else {
+ /* Test for overflow */
+ if ((init_shared_buffer->properties_buffer +
+ properties_length
+ > init_shared_buffer->properties_buffer) &&
+ (properties_length <=
+ init_shared_buffer->properties_length)) {
+ memcpy(init_shared_buffer->properties_buffer,
+ properties_buffer,
+ properties_length);
+ } else {
+ dprintk(KERN_INFO "tf_start(%p): "
+ "Configuration buffer size from userland is "
+ "incorrect(%d, %d)\n",
+ comm, (u32) properties_length,
+ init_shared_buffer->properties_length);
+ ret = -EFAULT;
+ goto error1;
+ }
+ }
+
+ dprintk(KERN_INFO "tf_start(%p): "
+ "System Configuration (%d bytes)\n", comm,
+ init_shared_buffer->properties_length);
+ dprintk(KERN_INFO "tf_start(%p): "
+ "Starting PA (%d bytes)...\n", comm, pa_size);
+
+ /*
+ * Make sure all data is visible to the secure world
+ */
+ dmac_flush_range((void *)init_shared_buffer,
+ (void *)(((u32)init_shared_buffer) + PAGE_SIZE));
+ outer_clean_range(__pa(init_shared_buffer),
+ __pa(init_shared_buffer) + PAGE_SIZE);
+
+ dmac_flush_range((void *)pa_buffer,
+ (void *)(pa_buffer + pa_size));
+ outer_clean_range(__pa(pa_buffer),
+ __pa(pa_buffer) + pa_size);
+
+ dmac_flush_range((void *)&pa_info,
+ (void *)(((u32)&pa_info) + sizeof(struct tf_ns_pa_info)));
+ outer_clean_range(__pa(&pa_info),
+ __pa(&pa_info) + sizeof(struct tf_ns_pa_info));
+ wmb();
+
+ spin_lock(&(comm->lock));
+ comm->init_shared_buffer = init_shared_buffer;
+ comm->pBuffer = l1_shared_buffer;
+ spin_unlock(&(comm->lock));
+ init_shared_buffer = NULL;
+ l1_shared_buffer = NULL;
+
+ /*
+ * Set the OS current time in the L1 shared buffer first. The secure
+ * world uses it as itw boot reference time.
+ */
+ tf_set_current_time(comm);
+
+ /* Workaround for issue #6081 */
+ if ((omap_rev() && 0xFFF000FF) == OMAP443X_CLASS)
+ disable_nonboot_cpus();
+
+ /*
+ * Start the SMC PA
+ */
+ ret = omap4_secure_dispatcher(API_HAL_LM_PALOAD_INDEX,
+ FLAG_IRQ_ENABLE | FLAG_FIQ_ENABLE | FLAG_START_HAL_CRITICAL, 1,
+ __pa(&pa_info), 0, 0, 0);
+ if (ret != API_HAL_RET_VALUE_OK) {
+ printk(KERN_ERR "SMC: Error while loading the PA [0x%x]\n",
+ ret);
+ goto error2;
+ }
+
+ /* Loop until the first S Yield RPC is received */
+loop:
+ mutex_lock(&(comm->rpc_mutex));
+
+ if (g_RPC_advancement == RPC_ADVANCEMENT_PENDING) {
+ dprintk(KERN_INFO "tf_rpc_execute: "
+ "Executing CMD=0x%x\n",
+ g_RPC_parameters[1]);
+
+ switch (g_RPC_parameters[1]) {
+ case RPC_CMD_YIELD:
+ dprintk(KERN_INFO "tf_rpc_execute: "
+ "RPC_CMD_YIELD\n");
+ set_bit(TF_COMM_FLAG_L1_SHARED_ALLOCATED,
+ &(comm->flags));
+ g_RPC_parameters[0] = RPC_SUCCESS;
+ break;
+
+ case RPC_CMD_INIT:
+ dprintk(KERN_INFO "tf_rpc_execute: "
+ "RPC_CMD_INIT\n");
+ g_RPC_parameters[0] = tf_rpc_init(comm);
+ break;
+
+ case RPC_CMD_TRACE:
+ g_RPC_parameters[0] = tf_rpc_trace(comm);
+ break;
+
+ default:
+ g_RPC_parameters[0] = RPC_ERROR_BAD_PARAMETERS;
+ break;
+ }
+ g_RPC_advancement = RPC_ADVANCEMENT_FINISHED;
+ }
+
+ mutex_unlock(&(comm->rpc_mutex));
+
+ ret = tf_schedule_secure_world(comm, false);
+ if (ret != 0) {
+ printk(KERN_ERR "SMC: Error while loading the PA [0x%x]\n",
+ ret);
+ goto error2;
+ }
+
+ if (!test_bit(TF_COMM_FLAG_L1_SHARED_ALLOCATED, &(comm->flags)))
+ goto loop;
+
+ set_bit(TF_COMM_FLAG_PA_AVAILABLE, &comm->flags);
+ wake_up(&(comm->wait_queue));
+ ret = 0;
+
+ #if 0
+ {
+ void *workspace_va;
+ workspace_va = ioremap(workspace_addr, workspace_size);
+ printk(KERN_INFO
+ "Read first word of workspace [0x%x]\n",
+ *(uint32_t *)workspace_va);
+ }
+ #endif
+
+ /* Workaround for issue #6081 */
+ if ((omap_rev() && 0xFFF000FF) == OMAP443X_CLASS)
+ enable_nonboot_cpus();
+
+ goto exit;
+
+error2:
+ /* Workaround for issue #6081 */
+ if ((omap_rev() && 0xFFF000FF) == OMAP443X_CLASS)
+ enable_nonboot_cpus();
+
+ spin_lock(&(comm->lock));
+ l1_shared_buffer = comm->pBuffer;
+ init_shared_buffer = comm->init_shared_buffer;
+ comm->pBuffer = NULL;
+ comm->init_shared_buffer = NULL;
+ spin_unlock(&(comm->lock));
+
+error1:
+ if (init_shared_buffer != NULL) {
+ __clear_page_locked(virt_to_page(init_shared_buffer));
+ internal_free_page((unsigned long) init_shared_buffer);
+ }
+ if (l1_shared_buffer != NULL) {
+ __clear_page_locked(virt_to_page(l1_shared_buffer));
+ internal_free_page((unsigned long) l1_shared_buffer);
+ }
+
+exit:
+#ifdef CONFIG_SMP
+ ret_affinity = sched_setaffinity(0, &saved_cpu_mask);
+ if (ret_affinity != 0)
+ dprintk(KERN_ERR "sched_setaffinity #2 -> 0x%lX", ret_affinity);
+#endif
+
+ tf_l4sec_clkdm_allow_idle(true);
+
+ if (ret > 0)
+ ret = -EFAULT;
+
+ return ret;
+}
+
+void tf_terminate(struct tf_comm *comm)
+{
+ dprintk(KERN_INFO "tf_terminate(%p)\n", comm);
+
+ spin_lock(&(comm->lock));
+
+ tf_crypto_terminate();
+
+ spin_unlock(&(comm->lock));
+}
diff --git a/security/smc/tf_conn.c b/security/smc/tf_conn.c
new file mode 100644
index 0000000..4ab7a0a
--- /dev/null
+++ b/security/smc/tf_conn.c
@@ -0,0 +1,1647 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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 <asm/atomic.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/types.h>
+
+#include "s_version.h"
+
+#include "tf_protocol.h"
+#include "tf_defs.h"
+#include "tf_util.h"
+#include "tf_comm.h"
+#include "tf_conn.h"
+
+#ifdef CONFIG_TF_ZEBRA
+#include "tf_crypto.h"
+#endif
+
+/*----------------------------------------------------------------------------
+ * Management of the shared memory blocks.
+ *
+ * Shared memory blocks are the blocks registered through
+ * the commands REGISTER_SHARED_MEMORY and POWER_MANAGEMENT
+ *----------------------------------------------------------------------------*/
+
+/**
+ * Unmaps a shared memory
+ **/
+static void tf_unmap_shmem(
+ struct tf_connection *connection,
+ struct tf_shmem_desc *shmem_desc,
+ u32 full_cleanup)
+{
+ /* check shmem_desc contains a descriptor */
+ if (shmem_desc == NULL)
+ return;
+
+ dprintk(KERN_DEBUG "tf_unmap_shmem(%p)\n", shmem_desc);
+
+retry:
+ mutex_lock(&(connection->shmem_mutex));
+ if (atomic_read(&shmem_desc->ref_count) > 1) {
+ /*
+ * Shared mem still in use, wait for other operations completion
+ * before actually unmapping it.
+ */
+ dprintk(KERN_INFO "Descriptor in use\n");
+ mutex_unlock(&(connection->shmem_mutex));
+ schedule();
+ goto retry;
+ }
+
+ tf_cleanup_shared_memory(
+ &(connection->cpt_alloc_context),
+ shmem_desc,
+ full_cleanup);
+
+ list_del(&(shmem_desc->list));
+
+ if ((shmem_desc->type == TF_SHMEM_TYPE_REGISTERED_SHMEM) ||
+ (full_cleanup != 0)) {
+ internal_kfree(shmem_desc);
+
+ atomic_dec(&(connection->shmem_count));
+ } else {
+ /*
+ * This is a preallocated shared memory, add to free list
+ * Since the device context is unmapped last, it is
+ * always the first element of the free list if no
+ * device context has been created
+ */
+ shmem_desc->block_identifier = 0;
+ list_add(&(shmem_desc->list), &(connection->free_shmem_list));
+ }
+
+ mutex_unlock(&(connection->shmem_mutex));
+}
+
+
+/**
+ * Find the first available slot for a new block of shared memory
+ * and map the user buffer.
+ * Update the descriptors to L1 descriptors
+ * Update the buffer_start_offset and buffer_size fields
+ * shmem_desc is updated to the mapped shared memory descriptor
+ **/
+static int tf_map_shmem(
+ struct tf_connection *connection,
+ u32 buffer,
+ /* flags for read-write access rights on the memory */
+ u32 flags,
+ bool in_user_space,
+ u32 descriptors[TF_MAX_COARSE_PAGES],
+ u32 *buffer_start_offset,
+ u32 buffer_size,
+ struct tf_shmem_desc **shmem_desc,
+ u32 *descriptor_count)
+{
+ struct tf_shmem_desc *desc = NULL;
+ int error;
+
+ dprintk(KERN_INFO "tf_map_shmem(%p, %p, flags = 0x%08x)\n",
+ connection,
+ (void *) buffer,
+ flags);
+
+ mutex_lock(&(connection->shmem_mutex));
+
+ /*
+ * Check the list of free shared memory
+ * is not empty
+ */
+ if (list_empty(&(connection->free_shmem_list))) {
+ if (atomic_read(&(connection->shmem_count)) ==
+ TF_SHMEM_MAX_COUNT) {
+ printk(KERN_ERR "tf_map_shmem(%p):"
+ " maximum shared memories already registered\n",
+ connection);
+ error = -ENOMEM;
+ goto error;
+ }
+
+ /* no descriptor available, allocate a new one */
+
+ desc = (struct tf_shmem_desc *) internal_kmalloc(
+ sizeof(*desc), GFP_KERNEL);
+ if (desc == NULL) {
+ printk(KERN_ERR "tf_map_shmem(%p):"
+ " failed to allocate descriptor\n",
+ connection);
+ error = -ENOMEM;
+ goto error;
+ }
+
+ /* Initialize the structure */
+ desc->type = TF_SHMEM_TYPE_REGISTERED_SHMEM;
+ atomic_set(&desc->ref_count, 1);
+ INIT_LIST_HEAD(&(desc->list));
+
+ atomic_inc(&(connection->shmem_count));
+ } else {
+ /* take the first free shared memory descriptor */
+ desc = list_first_entry(&(connection->free_shmem_list),
+ struct tf_shmem_desc, list);
+ list_del(&(desc->list));
+ }
+
+ /* Add the descriptor to the used list */
+ list_add(&(desc->list), &(connection->used_shmem_list));
+
+ error = tf_fill_descriptor_table(
+ &(connection->cpt_alloc_context),
+ desc,
+ buffer,
+ connection->vmas,
+ descriptors,
+ buffer_size,
+ buffer_start_offset,
+ in_user_space,
+ flags,
+ descriptor_count);
+
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_map_shmem(%p):"
+ " tf_fill_descriptor_table failed with error "
+ "code %d!\n",
+ connection,
+ error);
+ goto error;
+ }
+ desc->pBuffer = (u8 *) buffer;
+
+ /*
+ * Successful completion.
+ */
+ *shmem_desc = desc;
+ mutex_unlock(&(connection->shmem_mutex));
+ dprintk(KERN_DEBUG "tf_map_shmem: success\n");
+ return 0;
+
+
+ /*
+ * Error handling.
+ */
+error:
+ mutex_unlock(&(connection->shmem_mutex));
+ dprintk(KERN_ERR "tf_map_shmem: failure with error code %d\n",
+ error);
+
+ tf_unmap_shmem(
+ connection,
+ desc,
+ 0);
+
+ return error;
+}
+
+
+
+/* This function is a copy of the find_vma() function
+in linux kernel 2.6.15 version with some fixes :
+ - memory block may end on vm_end
+ - check the full memory block is in the memory area
+ - guarantee NULL is returned if no memory area is found */
+struct vm_area_struct *tf_find_vma(struct mm_struct *mm,
+ unsigned long addr, unsigned long size)
+{
+ struct vm_area_struct *vma = NULL;
+
+ dprintk(KERN_INFO
+ "tf_find_vma addr=0x%lX size=0x%lX\n", addr, size);
+
+ if (mm) {
+ /* Check the cache first. */
+ /* (Cache hit rate is typically around 35%.) */
+ vma = mm->mmap_cache;
+ if (!(vma && vma->vm_end >= (addr+size) &&
+ vma->vm_start <= addr)) {
+ struct rb_node *rb_node;
+
+ rb_node = mm->mm_rb.rb_node;
+ vma = NULL;
+
+ while (rb_node) {
+ struct vm_area_struct *vma_tmp;
+
+ vma_tmp = rb_entry(rb_node,
+ struct vm_area_struct, vm_rb);
+
+ dprintk(KERN_INFO
+ "vma_tmp->vm_start=0x%lX"
+ "vma_tmp->vm_end=0x%lX\n",
+ vma_tmp->vm_start,
+ vma_tmp->vm_end);
+
+ if (vma_tmp->vm_end >= (addr+size)) {
+ vma = vma_tmp;
+ if (vma_tmp->vm_start <= addr)
+ break;
+
+ rb_node = rb_node->rb_left;
+ } else {
+ rb_node = rb_node->rb_right;
+ }
+ }
+
+ if (vma)
+ mm->mmap_cache = vma;
+ if (rb_node == NULL)
+ vma = NULL;
+ }
+ }
+ return vma;
+}
+
+static int tf_validate_shmem_and_flags(
+ u32 shmem,
+ u32 shmem_size,
+ u32 flags)
+{
+ struct vm_area_struct *vma;
+ u32 chunk;
+
+ if (shmem_size == 0)
+ /* This is always valid */
+ return 0;
+
+ if ((shmem + shmem_size) < shmem)
+ /* Overflow */
+ return -EINVAL;
+
+ down_read(&current->mm->mmap_sem);
+
+ /*
+ * When looking for a memory address, split buffer into chunks of
+ * size=PAGE_SIZE.
+ */
+ chunk = PAGE_SIZE - (shmem & (PAGE_SIZE-1));
+ if (chunk > shmem_size)
+ chunk = shmem_size;
+
+ do {
+ vma = tf_find_vma(current->mm, shmem, chunk);
+
+ if (vma == NULL) {
+ dprintk(KERN_ERR "%s: area not found\n", __func__);
+ goto error;
+ }
+
+ if (flags & TF_SHMEM_TYPE_READ)
+ if (!(vma->vm_flags & VM_READ)) {
+ dprintk(KERN_ERR "%s: no read permission\n",
+ __func__);
+ goto error;
+ }
+ if (flags & TF_SHMEM_TYPE_WRITE)
+ if (!(vma->vm_flags & VM_WRITE)) {
+ dprintk(KERN_ERR "%s: no write permission\n",
+ __func__);
+ goto error;
+ }
+
+ shmem_size -= chunk;
+ shmem += chunk;
+ chunk = (shmem_size <= PAGE_SIZE ?
+ shmem_size : PAGE_SIZE);
+ } while (shmem_size != 0);
+
+ up_read(&current->mm->mmap_sem);
+ return 0;
+
+error:
+ up_read(&current->mm->mmap_sem);
+ return -EFAULT;
+}
+
+
+static int tf_map_temp_shmem(struct tf_connection *connection,
+ struct tf_command_param_temp_memref *temp_memref,
+ u32 param_type,
+ struct tf_shmem_desc **shmem_desc)
+{
+ u32 flags;
+ u32 error = S_SUCCESS;
+ bool in_user_space = connection->owner != TF_CONNECTION_OWNER_KERNEL;
+
+ dprintk(KERN_INFO "tf_map_temp_shmem(%p, "
+ "0x%08x[size=0x%08x], offset=0x%08x)\n",
+ connection,
+ temp_memref->descriptor,
+ temp_memref->size,
+ temp_memref->offset);
+
+ switch (param_type) {
+ case TF_PARAM_TYPE_MEMREF_TEMP_INPUT:
+ flags = TF_SHMEM_TYPE_READ;
+ break;
+ case TF_PARAM_TYPE_MEMREF_TEMP_OUTPUT:
+ flags = TF_SHMEM_TYPE_WRITE;
+ break;
+ case TF_PARAM_TYPE_MEMREF_TEMP_INOUT:
+ flags = TF_SHMEM_TYPE_WRITE | TF_SHMEM_TYPE_READ;
+ break;
+ default:
+ error = -EINVAL;
+ goto error;
+ }
+
+ if (temp_memref->descriptor == 0) {
+ /* NULL tmpref */
+ temp_memref->offset = 0;
+ *shmem_desc = NULL;
+ } else if ((temp_memref->descriptor != 0) &&
+ (temp_memref->size == 0)) {
+ /* Empty tmpref */
+ temp_memref->offset = temp_memref->descriptor;
+ temp_memref->descriptor = 0;
+ temp_memref->size = 0;
+ *shmem_desc = NULL;
+ } else {
+ /* Map the temp shmem block */
+
+ u32 shared_mem_descriptors[TF_MAX_COARSE_PAGES];
+ u32 descriptorCount;
+
+ if (in_user_space) {
+ error = tf_validate_shmem_and_flags(
+ temp_memref->descriptor,
+ temp_memref->size,
+ flags);
+ if (error != 0)
+ goto error;
+ }
+
+ error = tf_map_shmem(
+ connection,
+ temp_memref->descriptor,
+ flags,
+ in_user_space,
+ shared_mem_descriptors,
+ &(temp_memref->offset),
+ temp_memref->size,
+ shmem_desc,
+ &descriptorCount);
+ temp_memref->descriptor = shared_mem_descriptors[0];
+ }
+
+error:
+ return error;
+}
+
+/*
+ * Clean up a list of shared memory descriptors.
+ */
+static void tf_shared_memory_cleanup_list(
+ struct tf_connection *connection,
+ struct list_head *shmem_desc_list)
+{
+ while (!list_empty(shmem_desc_list)) {
+ struct tf_shmem_desc *shmem_desc;
+
+ shmem_desc = list_first_entry(shmem_desc_list,
+ struct tf_shmem_desc, list);
+
+ tf_unmap_shmem(connection, shmem_desc, 1);
+ }
+}
+
+
+/*
+ * Clean up the shared memory information in the connection.
+ * Releases all allocated pages.
+ */
+static void tf_cleanup_shared_memories(struct tf_connection *connection)
+{
+ /* clean up the list of used and free descriptors.
+ * done outside the mutex, because tf_unmap_shmem already
+ * mutex()ed
+ */
+ tf_shared_memory_cleanup_list(connection,
+ &connection->used_shmem_list);
+ tf_shared_memory_cleanup_list(connection,
+ &connection->free_shmem_list);
+
+ mutex_lock(&(connection->shmem_mutex));
+
+ /* Free the Vmas page */
+ if (connection->vmas) {
+ internal_free_page((unsigned long) connection->vmas);
+ connection->vmas = NULL;
+ }
+
+ tf_release_coarse_page_table_allocator(
+ &(connection->cpt_alloc_context));
+
+ mutex_unlock(&(connection->shmem_mutex));
+}
+
+
+/*
+ * Initialize the shared memory in a connection.
+ * Allocates the minimum memory to be provided
+ * for shared memory management
+ */
+int tf_init_shared_memory(struct tf_connection *connection)
+{
+ int error;
+ int i;
+ int coarse_page_index;
+
+ /*
+ * We only need to initialize special elements and attempt to allocate
+ * the minimum shared memory descriptors we want to support
+ */
+
+ mutex_init(&(connection->shmem_mutex));
+ INIT_LIST_HEAD(&(connection->free_shmem_list));
+ INIT_LIST_HEAD(&(connection->used_shmem_list));
+ atomic_set(&(connection->shmem_count), 0);
+
+ tf_init_coarse_page_table_allocator(
+ &(connection->cpt_alloc_context));
+
+
+ /*
+ * Preallocate 3 pages to increase the chances that a connection
+ * succeeds in allocating shared mem
+ */
+ for (i = 0;
+ i < 3;
+ i++) {
+ struct tf_shmem_desc *shmem_desc =
+ (struct tf_shmem_desc *) internal_kmalloc(
+ sizeof(*shmem_desc), GFP_KERNEL);
+
+ if (shmem_desc == NULL) {
+ printk(KERN_ERR "tf_init_shared_memory(%p):"
+ " failed to pre allocate descriptor %d\n",
+ connection,
+ i);
+ error = -ENOMEM;
+ goto error;
+ }
+
+ for (coarse_page_index = 0;
+ coarse_page_index < TF_MAX_COARSE_PAGES;
+ coarse_page_index++) {
+ struct tf_coarse_page_table *coarse_pg_table;
+
+ coarse_pg_table = tf_alloc_coarse_page_table(
+ &(connection->cpt_alloc_context),
+ TF_PAGE_DESCRIPTOR_TYPE_PREALLOCATED);
+
+ if (coarse_pg_table == NULL) {
+ printk(KERN_ERR "tf_init_shared_memory(%p)"
+ ": descriptor %d coarse page %d - "
+ "tf_alloc_coarse_page_table() "
+ "failed\n",
+ connection,
+ i,
+ coarse_page_index);
+ error = -ENOMEM;
+ goto error;
+ }
+
+ shmem_desc->coarse_pg_table[coarse_page_index] =
+ coarse_pg_table;
+ }
+ shmem_desc->coarse_pg_table_count = 0;
+
+ shmem_desc->type = TF_SHMEM_TYPE_PREALLOC_REGISTERED_SHMEM;
+ atomic_set(&shmem_desc->ref_count, 1);
+
+ /*
+ * add this preallocated descriptor to the list of free
+ * descriptors Keep the device context specific one at the
+ * beginning of the list
+ */
+ INIT_LIST_HEAD(&(shmem_desc->list));
+ list_add_tail(&(shmem_desc->list),
+ &(connection->free_shmem_list));
+ }
+
+ /* allocate memory for the vmas structure */
+ connection->vmas =
+ (struct vm_area_struct **) internal_get_zeroed_page(GFP_KERNEL);
+ if (connection->vmas == NULL) {
+ printk(KERN_ERR "tf_init_shared_memory(%p):"
+ " vmas - failed to get_zeroed_page\n",
+ connection);
+ error = -ENOMEM;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ tf_cleanup_shared_memories(connection);
+ return error;
+}
+
+/*----------------------------------------------------------------------------
+ * Connection operations to the Secure World
+ *----------------------------------------------------------------------------*/
+
+int tf_create_device_context(
+ struct tf_connection *connection)
+{
+ union tf_command command;
+ union tf_answer answer;
+ int error = 0;
+
+ dprintk(KERN_INFO "tf_create_device_context(%p)\n",
+ connection);
+
+ command.create_device_context.message_type =
+ TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT;
+ command.create_device_context.message_size =
+ (sizeof(struct tf_command_create_device_context)
+ - sizeof(struct tf_command_header))/sizeof(u32);
+ command.create_device_context.operation_id = (u32) &answer;
+ command.create_device_context.device_context_id = (u32) connection;
+
+ error = tf_send_receive(
+ &connection->dev->sm,
+ &command,
+ &answer,
+ connection,
+ true);
+
+ if ((error != 0) ||
+ (answer.create_device_context.error_code != S_SUCCESS))
+ goto error;
+
+ /*
+ * CREATE_DEVICE_CONTEXT succeeded,
+ * store device context handler and update connection status
+ */
+ connection->device_context =
+ answer.create_device_context.device_context;
+ spin_lock(&(connection->state_lock));
+ connection->state = TF_CONN_STATE_VALID_DEVICE_CONTEXT;
+ spin_unlock(&(connection->state_lock));
+
+ /* successful completion */
+ dprintk(KERN_INFO "tf_create_device_context(%p):"
+ " device_context=0x%08x\n",
+ connection,
+ answer.create_device_context.device_context);
+ return 0;
+
+error:
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_create_device_context failed with "
+ "error %d\n", error);
+ } else {
+ /*
+ * We sent a DeviceCreateContext. The state is now
+ * TF_CONN_STATE_CREATE_DEVICE_CONTEXT_SENT It has to be
+ * reset if we ever want to send a DeviceCreateContext again
+ */
+ spin_lock(&(connection->state_lock));
+ connection->state = TF_CONN_STATE_NO_DEVICE_CONTEXT;
+ spin_unlock(&(connection->state_lock));
+ dprintk(KERN_ERR "tf_create_device_context failed with "
+ "error_code 0x%08X\n",
+ answer.create_device_context.error_code);
+ if (answer.create_device_context.error_code ==
+ S_ERROR_OUT_OF_MEMORY)
+ error = -ENOMEM;
+ else
+ error = -EFAULT;
+ }
+
+ return error;
+}
+
+/* Check that the current application belongs to the
+ * requested GID */
+static bool tf_check_gid(gid_t requested_gid)
+{
+ if (requested_gid == current_egid()) {
+ return true;
+ } else {
+ u32 size;
+ u32 i;
+ /* Look in the supplementary GIDs */
+ get_group_info(GROUP_INFO);
+ size = GROUP_INFO->ngroups;
+ for (i = 0; i < size; i++)
+ if (requested_gid == GROUP_AT(GROUP_INFO , i))
+ return true;
+ }
+ return false;
+}
+
+/*
+ * Opens a client session to the Secure World
+ */
+int tf_open_client_session(
+ struct tf_connection *connection,
+ union tf_command *command,
+ union tf_answer *answer)
+{
+ int error = 0;
+ struct tf_shmem_desc *shmem_desc[4] = {NULL};
+ u32 i;
+
+ dprintk(KERN_INFO "tf_open_client_session(%p)\n", connection);
+
+ /*
+ * Initialize the message size with no login data. This will be later
+ * adjusted the the cases below
+ */
+ command->open_client_session.message_size =
+ (sizeof(struct tf_command_open_client_session) - 20
+ - sizeof(struct tf_command_header))/4;
+
+ switch (command->open_client_session.login_type) {
+ case TF_LOGIN_PUBLIC:
+ /* Nothing to do */
+ break;
+
+ case TF_LOGIN_USER:
+ /*
+ * Send the EUID of the calling application in the login data.
+ * Update message size.
+ */
+ *(u32 *) &command->open_client_session.login_data =
+ current_euid();
+#ifndef CONFIG_ANDROID
+ command->open_client_session.login_type =
+ (u32) TF_LOGIN_USER_LINUX_EUID;
+#else
+ command->open_client_session.login_type =
+ (u32) TF_LOGIN_USER_ANDROID_EUID;
+#endif
+
+ /* Added one word */
+ command->open_client_session.message_size += 1;
+ break;
+
+ case TF_LOGIN_GROUP: {
+ /* Check requested GID */
+ gid_t requested_gid =
+ *(u32 *) command->open_client_session.login_data;
+
+ if (!tf_check_gid(requested_gid)) {
+ dprintk(KERN_ERR "tf_open_client_session(%p) "
+ "TF_LOGIN_GROUP: requested GID (0x%x) does "
+ "not match real eGID (0x%x)"
+ "or any of the supplementary GIDs\n",
+ connection, requested_gid, current_egid());
+ error = -EACCES;
+ goto error;
+ }
+#ifndef CONFIG_ANDROID
+ command->open_client_session.login_type =
+ TF_LOGIN_GROUP_LINUX_GID;
+#else
+ command->open_client_session.login_type =
+ TF_LOGIN_GROUP_ANDROID_GID;
+#endif
+
+ command->open_client_session.message_size += 1; /* GID */
+ break;
+ }
+
+#ifndef CONFIG_ANDROID
+ case TF_LOGIN_APPLICATION: {
+ /*
+ * Compute SHA-1 hash of the application fully-qualified path
+ * name. Truncate the hash to 16 bytes and send it as login
+ * data. Update message size.
+ */
+ u8 pSHA1Hash[SHA1_DIGEST_SIZE];
+
+ error = tf_hash_application_path_and_data(pSHA1Hash,
+ NULL, 0);
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_open_client_session: "
+ "error in tf_hash_application_path_and_data\n");
+ goto error;
+ }
+ memcpy(&command->open_client_session.login_data,
+ pSHA1Hash, 16);
+ command->open_client_session.login_type =
+ TF_LOGIN_APPLICATION_LINUX_PATH_SHA1_HASH;
+ /* 16 bytes */
+ command->open_client_session.message_size += 4;
+ break;
+ }
+#else
+ case TF_LOGIN_APPLICATION:
+ /*
+ * Send the real UID of the calling application in the login
+ * data. Update message size.
+ */
+ *(u32 *) &command->open_client_session.login_data =
+ current_uid();
+
+ command->open_client_session.login_type =
+ (u32) TF_LOGIN_APPLICATION_ANDROID_UID;
+
+ /* Added one word */
+ command->open_client_session.message_size += 1;
+ break;
+#endif
+
+#ifndef CONFIG_ANDROID
+ case TF_LOGIN_APPLICATION_USER: {
+ /*
+ * Compute SHA-1 hash of the concatenation of the application
+ * fully-qualified path name and the EUID of the calling
+ * application. Truncate the hash to 16 bytes and send it as
+ * login data. Update message size.
+ */
+ u8 pSHA1Hash[SHA1_DIGEST_SIZE];
+
+ error = tf_hash_application_path_and_data(pSHA1Hash,
+ (u8 *) &(current_euid()), sizeof(current_euid()));
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_open_client_session: "
+ "error in tf_hash_application_path_and_data\n");
+ goto error;
+ }
+ memcpy(&command->open_client_session.login_data,
+ pSHA1Hash, 16);
+ command->open_client_session.login_type =
+ TF_LOGIN_APPLICATION_USER_LINUX_PATH_EUID_SHA1_HASH;
+
+ /* 16 bytes */
+ command->open_client_session.message_size += 4;
+
+ break;
+ }
+#else
+ case TF_LOGIN_APPLICATION_USER:
+ /*
+ * Send the real UID and the EUID of the calling application in
+ * the login data. Update message size.
+ */
+ *(u32 *) &command->open_client_session.login_data =
+ current_uid();
+ *(u32 *) &command->open_client_session.login_data[4] =
+ current_euid();
+
+ command->open_client_session.login_type =
+ TF_LOGIN_APPLICATION_USER_ANDROID_UID_EUID;
+
+ /* Added two words */
+ command->open_client_session.message_size += 2;
+ break;
+#endif
+
+#ifndef CONFIG_ANDROID
+ case TF_LOGIN_APPLICATION_GROUP: {
+ /*
+ * Check requested GID. Compute SHA-1 hash of the concatenation
+ * of the application fully-qualified path name and the
+ * requested GID. Update message size
+ */
+ gid_t requested_gid;
+ u8 pSHA1Hash[SHA1_DIGEST_SIZE];
+
+ requested_gid = *(u32 *) &command->open_client_session.
+ login_data;
+
+ if (!tf_check_gid(requested_gid)) {
+ dprintk(KERN_ERR "tf_open_client_session(%p) "
+ "TF_LOGIN_APPLICATION_GROUP: requested GID (0x%x) "
+ "does not match real eGID (0x%x)"
+ "or any of the supplementary GIDs\n",
+ connection, requested_gid, current_egid());
+ error = -EACCES;
+ goto error;
+ }
+
+ error = tf_hash_application_path_and_data(pSHA1Hash,
+ &requested_gid, sizeof(u32));
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_open_client_session: "
+ "error in tf_hash_application_path_and_data\n");
+ goto error;
+ }
+
+ memcpy(&command->open_client_session.login_data,
+ pSHA1Hash, 16);
+ command->open_client_session.login_type =
+ TF_LOGIN_APPLICATION_GROUP_LINUX_PATH_GID_SHA1_HASH;
+
+ /* 16 bytes */
+ command->open_client_session.message_size += 4;
+ break;
+ }
+#else
+ case TF_LOGIN_APPLICATION_GROUP: {
+ /*
+ * Check requested GID. Send the real UID and the requested GID
+ * in the login data. Update message size.
+ */
+ gid_t requested_gid;
+
+ requested_gid = *(u32 *) &command->open_client_session.
+ login_data;
+
+ if (!tf_check_gid(requested_gid)) {
+ dprintk(KERN_ERR "tf_open_client_session(%p) "
+ "TF_LOGIN_APPLICATION_GROUP: requested GID (0x%x) "
+ "does not match real eGID (0x%x)"
+ "or any of the supplementary GIDs\n",
+ connection, requested_gid, current_egid());
+ error = -EACCES;
+ goto error;
+ }
+
+ *(u32 *) &command->open_client_session.login_data =
+ current_uid();
+ *(u32 *) &command->open_client_session.login_data[4] =
+ requested_gid;
+
+ command->open_client_session.login_type =
+ TF_LOGIN_APPLICATION_GROUP_ANDROID_UID_GID;
+
+ /* Added two words */
+ command->open_client_session.message_size += 2;
+
+ break;
+ }
+#endif
+
+ case TF_LOGIN_PRIVILEGED:
+ /* A privileged login may be performed only on behalf of the
+ kernel itself or on behalf of a process with euid=0 or
+ egid=0. */
+ if (connection->owner == TF_CONNECTION_OWNER_KERNEL) {
+ dprintk(KERN_DEBUG "tf_open_client_session: "
+ "TF_LOGIN_PRIVILEGED for kernel API\n");
+ command->open_client_session.login_type =
+ TF_LOGIN_PRIVILEGED_KERNEL;
+ } else {
+ dprintk(KERN_DEBUG "tf_open_client_session: "
+ "TF_LOGIN_PRIVILEGED for %u:%u\n",
+ current_euid(), current_egid());
+ command->open_client_session.login_type =
+ TF_LOGIN_PRIVILEGED;
+ }
+ break;
+
+ case TF_LOGIN_AUTHENTICATION: {
+ /*
+ * Compute SHA-1 hash of the application binary
+ * Send this hash as the login data (20 bytes)
+ */
+
+ u8 *hash;
+ hash = &(command->open_client_session.login_data[0]);
+
+ error = tf_get_current_process_hash(hash);
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_open_client_session: "
+ "error in tf_get_current_process_hash\n");
+ goto error;
+ }
+ command->open_client_session.login_type =
+ TF_LOGIN_AUTHENTICATION_BINARY_SHA1_HASH;
+
+ /* 20 bytes */
+ command->open_client_session.message_size += 5;
+ break;
+ }
+
+ case TF_LOGIN_PRIVILEGED_KERNEL:
+ /* A kernel login may be performed only on behalf of the
+ kernel itself. */
+ if (connection->owner == TF_CONNECTION_OWNER_KERNEL) {
+ dprintk(KERN_DEBUG "tf_open_client_session: "
+ "TF_LOGIN_PRIVILEGED_KERNEL for kernel API\n");
+ command->open_client_session.login_type =
+ TF_LOGIN_PRIVILEGED_KERNEL;
+ } else {
+ dprintk(KERN_ERR "tf_open_client_session: "
+ " user %d, group %d not allowed to open "
+ "session with TF_LOGIN_PRIVILEGED_KERNEL\n",
+ current_euid(), current_egid());
+ error = -EACCES;
+ goto error;
+ }
+ command->open_client_session.login_type =
+ TF_LOGIN_PRIVILEGED_KERNEL;
+ break;
+
+ default:
+ dprintk(KERN_ERR "tf_open_client_session: "
+ "unknown login_type(%08X)\n",
+ command->open_client_session.login_type);
+ error = -EOPNOTSUPP;
+ goto error;
+ }
+
+ /* Map the temporary memory references */
+ for (i = 0; i < 4; i++) {
+ int param_type;
+ param_type = TF_GET_PARAM_TYPE(
+ command->open_client_session.param_types, i);
+ if ((param_type & (TF_PARAM_TYPE_MEMREF_FLAG |
+ TF_PARAM_TYPE_REGISTERED_MEMREF_FLAG))
+ == TF_PARAM_TYPE_MEMREF_FLAG) {
+ /* Map temp mem ref */
+ error = tf_map_temp_shmem(connection,
+ &command->open_client_session.
+ params[i].temp_memref,
+ param_type,
+ &shmem_desc[i]);
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_open_client_session: "
+ "unable to map temporary memory block "
+ "(%08X)\n", error);
+ goto error;
+ }
+ }
+ }
+
+ /* Fill the handle of the Device Context */
+ command->open_client_session.device_context =
+ connection->device_context;
+
+ error = tf_send_receive(
+ &connection->dev->sm,
+ command,
+ answer,
+ connection,
+ true);
+
+error:
+ /* Unmap the temporary memory references */
+ for (i = 0; i < 4; i++)
+ if (shmem_desc[i] != NULL)
+ tf_unmap_shmem(connection, shmem_desc[i], 0);
+
+ if (error != 0)
+ dprintk(KERN_ERR "tf_open_client_session returns %d\n",
+ error);
+ else
+ dprintk(KERN_ERR "tf_open_client_session returns "
+ "error_code 0x%08X\n",
+ answer->open_client_session.error_code);
+
+ return error;
+}
+
+
+/*
+ * Closes a client session from the Secure World
+ */
+int tf_close_client_session(
+ struct tf_connection *connection,
+ union tf_command *command,
+ union tf_answer *answer)
+{
+ int error = 0;
+
+ dprintk(KERN_DEBUG "tf_close_client_session(%p)\n", connection);
+
+ command->close_client_session.message_size =
+ (sizeof(struct tf_command_close_client_session) -
+ sizeof(struct tf_command_header)) / 4;
+ command->close_client_session.device_context =
+ connection->device_context;
+
+ error = tf_send_receive(
+ &connection->dev->sm,
+ command,
+ answer,
+ connection,
+ true);
+
+ if (error != 0)
+ dprintk(KERN_ERR "tf_close_client_session returns %d\n",
+ error);
+ else
+ dprintk(KERN_ERR "tf_close_client_session returns "
+ "error 0x%08X\n",
+ answer->close_client_session.error_code);
+
+ return error;
+}
+
+
+/*
+ * Registers a shared memory to the Secure World
+ */
+int tf_register_shared_memory(
+ struct tf_connection *connection,
+ union tf_command *command,
+ union tf_answer *answer)
+{
+ int error = 0;
+ struct tf_shmem_desc *shmem_desc = NULL;
+ bool in_user_space = connection->owner != TF_CONNECTION_OWNER_KERNEL;
+ struct tf_command_register_shared_memory *msg =
+ &command->register_shared_memory;
+
+ dprintk(KERN_INFO "tf_register_shared_memory(%p) "
+ "%p[0x%08X][0x%08x]\n",
+ connection,
+ (void *)msg->shared_mem_descriptors[0],
+ msg->shared_mem_size,
+ (u32)msg->memory_flags);
+
+ if (in_user_space) {
+ error = tf_validate_shmem_and_flags(
+ msg->shared_mem_descriptors[0],
+ msg->shared_mem_size,
+ (u32)msg->memory_flags);
+ if (error != 0)
+ goto error;
+ }
+
+ /* Initialize message_size with no descriptors */
+ msg->message_size
+ = (sizeof(struct tf_command_register_shared_memory) -
+ sizeof(struct tf_command_header)) / 4;
+
+ /* Map the shmem block and update the message */
+ if (msg->shared_mem_size == 0) {
+ /* Empty shared mem */
+ msg->shared_mem_start_offset = msg->shared_mem_descriptors[0];
+ } else {
+ u32 descriptorCount;
+ error = tf_map_shmem(
+ connection,
+ msg->shared_mem_descriptors[0],
+ msg->memory_flags,
+ in_user_space,
+ msg->shared_mem_descriptors,
+ &(msg->shared_mem_start_offset),
+ msg->shared_mem_size,
+ &shmem_desc,
+ &descriptorCount);
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_register_shared_memory: "
+ "unable to map shared memory block\n");
+ goto error;
+ }
+ msg->message_size += descriptorCount;
+ }
+
+ /*
+ * write the correct device context handle and the address of the shared
+ * memory descriptor in the message
+ */
+ msg->device_context = connection->device_context;
+ msg->block_id = (u32)shmem_desc;
+
+ /* Send the updated message */
+ error = tf_send_receive(
+ &connection->dev->sm,
+ command,
+ answer,
+ connection,
+ true);
+
+ if ((error != 0) ||
+ (answer->register_shared_memory.error_code
+ != S_SUCCESS)) {
+ dprintk(KERN_ERR "tf_register_shared_memory: "
+ "operation failed. Unmap block\n");
+ goto error;
+ }
+
+ /* Saves the block handle returned by the secure world */
+ if (shmem_desc != NULL)
+ shmem_desc->block_identifier =
+ answer->register_shared_memory.block;
+
+ /* successful completion */
+ dprintk(KERN_INFO "tf_register_shared_memory(%p):"
+ " block_id=0x%08x block=0x%08x\n",
+ connection, msg->block_id,
+ answer->register_shared_memory.block);
+ return 0;
+
+ /* error completion */
+error:
+ tf_unmap_shmem(
+ connection,
+ shmem_desc,
+ 0);
+
+ if (error != 0)
+ dprintk(KERN_ERR "tf_register_shared_memory returns %d\n",
+ error);
+ else
+ dprintk(KERN_ERR "tf_register_shared_memory returns "
+ "error_code 0x%08X\n",
+ answer->register_shared_memory.error_code);
+
+ return error;
+}
+
+
+/*
+ * Releases a shared memory from the Secure World
+ */
+int tf_release_shared_memory(
+ struct tf_connection *connection,
+ union tf_command *command,
+ union tf_answer *answer)
+{
+ int error = 0;
+
+ dprintk(KERN_DEBUG "tf_release_shared_memory(%p)\n", connection);
+
+ command->release_shared_memory.message_size =
+ (sizeof(struct tf_command_release_shared_memory) -
+ sizeof(struct tf_command_header)) / 4;
+ command->release_shared_memory.device_context =
+ connection->device_context;
+
+ error = tf_send_receive(
+ &connection->dev->sm,
+ command,
+ answer,
+ connection,
+ true);
+
+ if ((error != 0) ||
+ (answer->release_shared_memory.error_code != S_SUCCESS))
+ goto error;
+
+ /* Use block_id to get back the pointer to shmem_desc */
+ tf_unmap_shmem(
+ connection,
+ (struct tf_shmem_desc *)
+ answer->release_shared_memory.block_id,
+ 0);
+
+ /* successful completion */
+ dprintk(KERN_INFO "tf_release_shared_memory(%p):"
+ " block_id=0x%08x block=0x%08x\n",
+ connection, answer->release_shared_memory.block_id,
+ command->release_shared_memory.block);
+ return 0;
+
+
+error:
+ if (error != 0)
+ dprintk(KERN_ERR "tf_release_shared_memory returns %d\n",
+ error);
+ else
+ dprintk(KERN_ERR "tf_release_shared_memory returns "
+ "nChannelStatus 0x%08X\n",
+ answer->release_shared_memory.error_code);
+
+ return error;
+
+}
+
+
+#ifdef CONFIG_TF_ION
+extern struct ion_device *omap_ion_device;
+#endif /* CONFIG_TF_ION */
+/*
+ * Invokes a client command to the Secure World
+ */
+int tf_invoke_client_command(
+ struct tf_connection *connection,
+ union tf_command *command,
+ union tf_answer *answer)
+{
+ int error = 0;
+ struct tf_shmem_desc *shmem_desc[4] = {NULL};
+ int i;
+#ifdef CONFIG_TF_ION
+ struct ion_handle *new_handle = NULL;
+#endif /* CONFIG_TF_ION */
+
+ dprintk(KERN_INFO "tf_invoke_client_command(%p)\n", connection);
+
+ command->release_shared_memory.message_size =
+ (sizeof(struct tf_command_invoke_client_command) -
+ sizeof(struct tf_command_header)) / 4;
+
+#ifdef CONFIG_TF_ZEBRA
+ error = tf_crypto_try_shortcuted_update(connection,
+ (struct tf_command_invoke_client_command *) command,
+ (struct tf_answer_invoke_client_command *) answer);
+ if (error == 0)
+ return error;
+#endif
+
+ /* Map the tmprefs */
+ for (i = 0; i < 4; i++) {
+ int param_type = TF_GET_PARAM_TYPE(
+ command->invoke_client_command.param_types, i);
+
+ if ((param_type & (TF_PARAM_TYPE_MEMREF_FLAG |
+ TF_PARAM_TYPE_REGISTERED_MEMREF_FLAG))
+ == TF_PARAM_TYPE_MEMREF_FLAG) {
+ /* A temporary memref: map it */
+ error = tf_map_temp_shmem(connection,
+ &command->invoke_client_command.
+ params[i].temp_memref,
+ param_type, &shmem_desc[i]);
+ if (error != 0) {
+ dprintk(KERN_ERR
+ "tf_invoke_client_command: "
+ "unable to map temporary memory "
+ "block\n (%08X)", error);
+ goto error;
+ }
+ }
+#ifdef CONFIG_TF_ION
+ else if (param_type == TF_PARAM_TYPE_MEMREF_ION_HANDLE) {
+ struct tf_command_invoke_client_command *invoke;
+ ion_phys_addr_t ion_addr;
+ size_t ion_len;
+ struct ion_buffer *buffer;
+
+ if (connection->ion_client == NULL) {
+ connection->ion_client = ion_client_create(
+ omap_ion_device,
+ (1 << ION_HEAP_TYPE_CARVEOUT),
+ "smc");
+ }
+ if (connection->ion_client == NULL) {
+ dprintk(KERN_ERR "%s(%p): "
+ "unable to create ion client\n",
+ __func__, connection);
+ error = -EFAULT;
+ goto error;
+ }
+
+ invoke = &command->invoke_client_command;
+
+ dprintk(KERN_INFO "ion_handle %x",
+ invoke->params[i].value.a);
+ buffer = ion_share(connection->ion_client,
+ (struct ion_handle *)invoke->params[i].value.a);
+ if (buffer == NULL) {
+ dprintk(KERN_ERR "%s(%p): "
+ "unable to share ion handle\n",
+ __func__, connection);
+ error = -EFAULT;
+ goto error;
+ }
+
+ dprintk(KERN_INFO "ion_buffer %p", buffer);
+ new_handle = ion_import(connection->ion_client, buffer);
+ if (new_handle == NULL) {
+ dprintk(KERN_ERR "%s(%p): "
+ "unable to import ion buffer\n",
+ __func__, connection);
+ error = -EFAULT;
+ goto error;
+ }
+
+ dprintk(KERN_INFO "new_handle %x", new_handle);
+ error = ion_phys(connection->ion_client,
+ new_handle,
+ &ion_addr,
+ &ion_len);
+ if (error) {
+ dprintk(KERN_ERR
+ "%s: unable to convert ion handle "
+ "0x%08X (error code 0x%08X)\n",
+ __func__,
+ new_handle,
+ error);
+ error = -EINVAL;
+ goto error;
+ }
+ dprintk(KERN_INFO
+ "%s: handle=0x%08x phys_add=0x%08x length=0x%08x\n",
+ __func__, invoke->params[i].value.a, ion_addr, ion_len);
+
+ invoke->params[i].value.a = (u32) ion_addr;
+ invoke->params[i].value.b = (u32) ion_len;
+
+ invoke->param_types &= ~((0xF) << (4*i));
+ invoke->param_types |=
+ TF_PARAM_TYPE_VALUE_INPUT << (4*i);
+ }
+#endif /* CONFIG_TF_ION */
+ }
+
+ command->invoke_client_command.device_context =
+ connection->device_context;
+
+ error = tf_send_receive(&connection->dev->sm, command,
+ answer, connection, true);
+
+error:
+#ifdef CONFIG_TF_ION
+ if (new_handle != NULL)
+ ion_free(connection->ion_client, new_handle);
+#endif /* CONFIG_TF_ION */
+ /* Unmap de temp mem refs */
+ for (i = 0; i < 4; i++) {
+ if (shmem_desc[i] != NULL) {
+ dprintk(KERN_INFO "tf_invoke_client_command: "
+ "UnMatemp_memref %d\n ", i);
+
+ tf_unmap_shmem(connection, shmem_desc[i], 0);
+ }
+ }
+
+ if (error != 0)
+ dprintk(KERN_ERR "tf_invoke_client_command returns %d\n",
+ error);
+ else
+ dprintk(KERN_ERR "tf_invoke_client_command returns "
+ "error_code 0x%08X\n",
+ answer->invoke_client_command.error_code);
+
+ return error;
+}
+
+
+/*
+ * Cancels a client command from the Secure World
+ */
+int tf_cancel_client_command(
+ struct tf_connection *connection,
+ union tf_command *command,
+ union tf_answer *answer)
+{
+ int error = 0;
+
+ dprintk(KERN_DEBUG "tf_cancel_client_command(%p)\n", connection);
+
+ command->cancel_client_operation.device_context =
+ connection->device_context;
+ command->cancel_client_operation.message_size =
+ (sizeof(struct tf_command_cancel_client_operation) -
+ sizeof(struct tf_command_header)) / 4;
+
+ error = tf_send_receive(
+ &connection->dev->sm,
+ command,
+ answer,
+ connection,
+ true);
+
+ if ((error != 0) ||
+ (answer->cancel_client_operation.error_code != S_SUCCESS))
+ goto error;
+
+
+ /* successful completion */
+ return 0;
+
+error:
+ if (error != 0)
+ dprintk(KERN_ERR "tf_cancel_client_command returns %d\n",
+ error);
+ else
+ dprintk(KERN_ERR "tf_cancel_client_command returns "
+ "nChannelStatus 0x%08X\n",
+ answer->cancel_client_operation.error_code);
+
+ return error;
+}
+
+
+
+/*
+ * Destroys a device context from the Secure World
+ */
+int tf_destroy_device_context(
+ struct tf_connection *connection)
+{
+ int error;
+ /*
+ * AFY: better use the specialized tf_command_destroy_device_context
+ * structure: this will save stack
+ */
+ union tf_command command;
+ union tf_answer answer;
+
+ dprintk(KERN_INFO "tf_destroy_device_context(%p)\n", connection);
+
+ BUG_ON(connection == NULL);
+
+ command.header.message_type = TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT;
+ command.header.message_size =
+ (sizeof(struct tf_command_destroy_device_context) -
+ sizeof(struct tf_command_header))/sizeof(u32);
+
+ /*
+ * fill in the device context handler
+ * it is guarantied that the first shared memory descriptor describes
+ * the device context
+ */
+ command.destroy_device_context.device_context =
+ connection->device_context;
+
+ error = tf_send_receive(
+ &connection->dev->sm,
+ &command,
+ &answer,
+ connection,
+ false);
+
+ if ((error != 0) ||
+ (answer.destroy_device_context.error_code != S_SUCCESS))
+ goto error;
+
+ spin_lock(&(connection->state_lock));
+ connection->state = TF_CONN_STATE_NO_DEVICE_CONTEXT;
+ spin_unlock(&(connection->state_lock));
+
+ /* successful completion */
+ dprintk(KERN_INFO "tf_destroy_device_context(%p)\n",
+ connection);
+ return 0;
+
+error:
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_destroy_device_context failed with "
+ "error %d\n", error);
+ } else {
+ dprintk(KERN_ERR "tf_destroy_device_context failed with "
+ "error_code 0x%08X\n",
+ answer.destroy_device_context.error_code);
+ if (answer.destroy_device_context.error_code ==
+ S_ERROR_OUT_OF_MEMORY)
+ error = -ENOMEM;
+ else
+ error = -EFAULT;
+ }
+
+ return error;
+}
+
+
+/*----------------------------------------------------------------------------
+ * Connection initialization and cleanup operations
+ *----------------------------------------------------------------------------*/
+
+/*
+ * Opens a connection to the specified device.
+ *
+ * The placeholder referenced by connection is set to the address of the
+ * new connection; it is set to NULL upon failure.
+ *
+ * Returns zero upon successful completion, or an appropriate error code upon
+ * failure.
+ */
+int tf_open(struct tf_device *dev,
+ struct file *file,
+ struct tf_connection **connection)
+{
+ int error;
+ struct tf_connection *conn = NULL;
+
+ dprintk(KERN_INFO "tf_open(%p, %p)\n", file, connection);
+
+ /*
+ * Allocate and initialize the conn.
+ * kmalloc only allocates sizeof(*conn) virtual memory
+ */
+ conn = (struct tf_connection *) internal_kmalloc(sizeof(*conn),
+ GFP_KERNEL);
+ if (conn == NULL) {
+ printk(KERN_ERR "tf_open(): "
+ "Out of memory for conn!\n");
+ error = -ENOMEM;
+ goto error;
+ }
+
+ memset(conn, 0, sizeof(*conn));
+
+ conn->state = TF_CONN_STATE_NO_DEVICE_CONTEXT;
+ conn->dev = dev;
+ spin_lock_init(&(conn->state_lock));
+ atomic_set(&(conn->pending_op_count), 0);
+ INIT_LIST_HEAD(&(conn->list));
+
+ /*
+ * Initialize the shared memory
+ */
+ error = tf_init_shared_memory(conn);
+ if (error != 0)
+ goto error;
+
+#ifdef CONFIG_TF_ZEBRA
+ /*
+ * Initialize CUS specifics
+ */
+ tf_crypto_init_cus(conn);
+#endif
+
+ /*
+ * Attach the conn to the device.
+ */
+ spin_lock(&(dev->connection_list_lock));
+ list_add(&(conn->list), &(dev->connection_list));
+ spin_unlock(&(dev->connection_list_lock));
+
+ /*
+ * Successful completion.
+ */
+
+ *connection = conn;
+
+ dprintk(KERN_INFO "tf_open(): Success (conn=%p)\n", conn);
+ return 0;
+
+ /*
+ * Error handling.
+ */
+
+error:
+ dprintk(KERN_ERR "tf_open(): Failure (error %d)\n", error);
+ /* Deallocate the descriptor pages if necessary */
+ internal_kfree(conn);
+ *connection = NULL;
+ return error;
+}
+
+
+/*
+ * Closes the specified connection.
+ *
+ * Upon return, the connection has been destroyed and cannot be used anymore.
+ *
+ * This function does nothing if connection is set to NULL.
+ */
+void tf_close(struct tf_connection *connection)
+{
+ int error;
+ enum TF_CONN_STATE state;
+
+ dprintk(KERN_DEBUG "tf_close(%p)\n", connection);
+
+ if (connection == NULL)
+ return;
+
+ /*
+ * Assumption: Linux guarantees that no other operation is in progress
+ * and that no other operation will be started when close is called
+ */
+ BUG_ON(atomic_read(&(connection->pending_op_count)) != 0);
+
+ /*
+ * Exchange a Destroy Device Context message if needed.
+ */
+ spin_lock(&(connection->state_lock));
+ state = connection->state;
+ spin_unlock(&(connection->state_lock));
+ if (state == TF_CONN_STATE_VALID_DEVICE_CONTEXT) {
+ /*
+ * A DestroyDeviceContext operation was not performed. Do it
+ * now.
+ */
+ error = tf_destroy_device_context(connection);
+ if (error != 0)
+ /* avoid cleanup if destroy device context fails */
+ goto error;
+ }
+
+ /*
+ * Clean up the shared memory
+ */
+ tf_cleanup_shared_memories(connection);
+
+#ifdef CONFIG_TF_ION
+ if (connection->ion_client != NULL)
+ ion_client_destroy(connection->ion_client);
+#endif
+
+ spin_lock(&(connection->dev->connection_list_lock));
+ list_del(&(connection->list));
+ spin_unlock(&(connection->dev->connection_list_lock));
+
+ internal_kfree(connection);
+
+ return;
+
+error:
+ dprintk(KERN_DEBUG "tf_close(%p) failed with error code %d\n",
+ connection, error);
+}
diff --git a/security/smc/tf_conn.h b/security/smc/tf_conn.h
new file mode 100644
index 0000000..d2c8261
--- /dev/null
+++ b/security/smc/tf_conn.h
@@ -0,0 +1,87 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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
+ */
+
+#ifndef __TF_CONN_H__
+#define __TF_CONN_H__
+
+#include "tf_defs.h"
+
+/*
+ * Returns a pointer to the connection referenced by the
+ * specified file.
+ */
+static inline struct tf_connection *tf_conn_from_file(
+ struct file *file)
+{
+ return file->private_data;
+}
+
+/*----------------------------------------------------------------------------
+ * Connection operations to the Secure World
+ *----------------------------------------------------------------------------*/
+
+int tf_create_device_context(
+ struct tf_connection *connection);
+
+int tf_destroy_device_context(
+ struct tf_connection *connection);
+
+int tf_open_client_session(
+ struct tf_connection *connection,
+ union tf_command *command,
+ union tf_answer *answer);
+
+int tf_close_client_session(
+ struct tf_connection *connection,
+ union tf_command *command,
+ union tf_answer *answer);
+
+int tf_register_shared_memory(
+ struct tf_connection *connection,
+ union tf_command *command,
+ union tf_answer *answer);
+
+int tf_release_shared_memory(
+ struct tf_connection *connection,
+ union tf_command *command,
+ union tf_answer *answer);
+
+int tf_invoke_client_command(
+ struct tf_connection *connection,
+ union tf_command *command,
+ union tf_answer *answer);
+
+int tf_cancel_client_command(
+ struct tf_connection *connection,
+ union tf_command *command,
+ union tf_answer *answer);
+
+/*----------------------------------------------------------------------------
+ * Connection initialization and cleanup operations
+ *----------------------------------------------------------------------------*/
+
+int tf_open(struct tf_device *dev,
+ struct file *file,
+ struct tf_connection **connection);
+
+void tf_close(
+ struct tf_connection *connection);
+
+
+#endif /* !defined(__TF_CONN_H__) */
diff --git a/security/smc/tf_crypto.c b/security/smc/tf_crypto.c
new file mode 100644
index 0000000..7edca0f
--- /dev/null
+++ b/security/smc/tf_crypto.c
@@ -0,0 +1,1278 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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 "tf_defs.h"
+#include "tf_util.h"
+#include "tf_zebra.h"
+#include "tf_crypto.h"
+#include "tf_dma.h"
+
+#define IO_ADDRESS OMAP2_L4_IO_ADDRESS
+
+#define S_SUCCESS 0x00000000
+#define S_ERROR_GENERIC 0xFFFF0000
+#define S_ERROR_ACCESS_DENIED 0xFFFF0001
+#define S_ERROR_BAD_FORMAT 0xFFFF0005
+#define S_ERROR_BAD_PARAMETERS 0xFFFF0006
+#define S_ERROR_OUT_OF_MEMORY 0xFFFF000C
+#define S_ERROR_SHORT_BUFFER 0xFFFF0010
+#define S_ERROR_UNREACHABLE 0xFFFF3013
+#define S_ERROR_SERVICE 0xFFFF1000
+
+#define CKR_OK 0x00000000
+
+#define PUBLIC_CRYPTO_TIMEOUT_CONST 0x000FFFFF
+
+#define RPC_AES1_CODE PUBLIC_CRYPTO_HWA_AES1
+#define RPC_DES_CODE PUBLIC_CRYPTO_HWA_DES
+#define RPC_SHA_CODE PUBLIC_CRYPTO_HWA_SHA
+
+#define RPC_CRYPTO_COMMAND_MASK 0x000003c0
+
+#define RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR 0x200
+#define RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR_UNLOCK 0x000
+#define RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR_LOCK 0x001
+
+#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT 0x240
+#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_LOCK_AES1 RPC_AES1_CODE
+#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_LOCK_DES RPC_DES_CODE
+#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_LOCK_SHA RPC_SHA_CODE
+#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_SUSPEND 0x010
+#define RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_UNINSTALL 0x020
+
+#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS 0x280
+#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_AES1 RPC_AES1_CODE
+#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_DES RPC_DES_CODE
+#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_SHA RPC_SHA_CODE
+#define RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_RESUME 0x010
+
+#define RPC_CLEAR_GLOBAL_KEY_CONTEXT 0x2c0
+#define RPC_CLEAR_GLOBAL_KEY_CONTEXT_CLEARED_AES 0x001
+#define RPC_CLEAR_GLOBAL_KEY_CONTEXT_CLEARED_DES 0x002
+
+#define ENABLE_CLOCK true
+#define DISABLE_CLOCK false
+
+/*---------------------------------------------------------------------------*/
+/*RPC IN/OUT structures for CUS implementation */
+/*---------------------------------------------------------------------------*/
+
+struct rpc_install_shortcut_lock_accelerator_out {
+ u32 shortcut_id;
+ u32 error;
+};
+
+struct rpc_install_shortcut_lock_accelerator_in {
+ u32 device_context_id;
+ u32 client_session;
+ u32 command_id;
+ u32 key_context;
+ /**
+ *The identifier of the HWA accelerator that this shortcut uses!
+ *Possible values are:
+ *- 1 (RPC_AES1_CODE)
+ *- 4 (RPC_DES_CODE)
+ *- 8 (RPC_SHA_CODE)
+ **/
+ u32 hwa_id;
+ /**
+ *This field defines the algorithm, direction, mode, key size.
+ *It contains some of the bits of the corresponding "CTRL" register
+ *of the accelerator.
+ *
+ *More precisely:
+ *For AES1 accelerator, hwa_ctrl contains the following bits:
+ *- CTR (bit 6):
+ * when 1, selects CTR mode.
+ * when 0, selects CBC or ECB mode (according to CBC bit)
+ *- CBC (bit 5)
+ * when 1, selects CBC mode (but only if CTR=0)
+ * when 0, selects EBC mode (but only if CTR=0)
+ *- DIRECTION (bit 2)
+ * 0: decryption
+ * 1: encryption
+ *
+ *For the DES2 accelerator, hwa_ctrl contains the following bits:
+ *- CBC (bit 4): 1 for CBC, 0 for ECB
+ *- DIRECTION (bit 2): 0 for decryption, 1 for encryption
+ *
+ *For the SHA accelerator, hwa_ctrl contains the following bits:
+ *- ALGO (bit 2:1):
+ * 0x0: MD5
+ * 0x1: SHA1
+ * 0x2: SHA-224
+ * 0x3: SHA-256
+ **/
+ u32 hwa_ctrl;
+ union tf_crypto_operation_state operation_state;
+};
+
+struct rpc_lock_hwa_suspend_shortcut_out {
+ union tf_crypto_operation_state operation_state;
+};
+
+struct rpc_lock_hwa_suspend_shortcut_in {
+ u32 shortcut_id;
+};
+
+struct rpc_resume_shortcut_unlock_hwa_in {
+ u32 shortcut_id;
+ u32 aes1_key_context;
+ u32 reserved;
+ u32 des_key_context;
+ union tf_crypto_operation_state operation_state;
+};
+
+/*------------------------------------------------------------------------- */
+/*
+ * tf_get_device_context(struct cus_context *cus)
+ * search in the all the device context (connection_list) if the CUS context
+ * specified by cus exist.
+ *
+ * If it is found, return the device context where the CUS context is.
+ * If is is not found, return NULL.
+ */
+static struct tf_connection *tf_get_device_context(
+ struct cus_context *cus)
+{
+ struct tf_connection *connection = NULL;
+ struct cus_context *cusFromList = NULL;
+ struct tf_device *dev = tf_get_device();
+
+ spin_lock(&(dev->connection_list_lock));
+ list_for_each_entry(connection, &(dev->connection_list),
+ list) {
+ spin_lock(&(connection->shortcut_list_lock));
+ list_for_each_entry(cusFromList,
+ &(connection->shortcut_list), list) {
+ if ((u32)cusFromList == (u32)cus) {
+ spin_unlock(&(connection->
+ shortcut_list_lock));
+ spin_unlock(&(dev->
+ connection_list_lock));
+ return connection;
+ }
+ }
+ spin_unlock(&(connection->
+ shortcut_list_lock));
+ }
+ spin_unlock(&(dev->connection_list_lock));
+
+ /*cus does not exist */
+ return NULL;
+}
+
+/*------------------------------------------------------------------------- */
+/*
+ * Get the shared memory from the memory block handle coming from secure.
+ * Return NULL if it does not exist.
+ */
+static struct tf_shmem_desc *tf_get_shmem_from_block_handle(
+ struct tf_connection *connection, u32 block)
+{
+ struct tf_shmem_desc *shmem_desc = NULL;
+
+ mutex_lock(&(connection->shmem_mutex));
+
+ list_for_each_entry(shmem_desc,
+ &(connection->used_shmem_list), list) {
+ if ((u32) shmem_desc->block_identifier ==
+ (u32) block) {
+ mutex_unlock(&(connection->shmem_mutex));
+ return shmem_desc;
+ }
+ }
+
+ /* block does not exist */
+ mutex_unlock(&(connection->shmem_mutex));
+
+ return NULL;
+}
+
+/*------------------------------------------------------------------------- */
+/*
+ * HWA public lock or unlock one HWA according algo specified by hwa_id
+ */
+void tf_crypto_lock_hwa(u32 hwa_id, bool do_lock)
+{
+ struct semaphore *s = NULL;
+ struct tf_device *dev = tf_get_device();
+
+ dprintk(KERN_INFO "[pid=%d] %s: hwa_id=0x%04X do_lock=%d\n",
+ current->pid, __func__, hwa_id, do_lock);
+
+ switch (hwa_id) {
+ case RPC_AES1_CODE:
+ s = &dev->aes1_sema;
+ break;
+ case RPC_DES_CODE:
+ s = &dev->des_sema;
+ break;
+ default:
+ case RPC_SHA_CODE:
+ s = &dev->sha_sema;
+ break;
+ }
+
+ if (do_lock == LOCK_HWA) {
+ dprintk(KERN_INFO "tf_crypto_lock_hwa: "
+ "Wait for HWAID=0x%04X\n", hwa_id);
+ while (down_trylock(s))
+ cpu_relax();
+ dprintk(KERN_INFO "tf_crypto_lock_hwa: "
+ "Locked on HWAID=0x%04X\n", hwa_id);
+ } else {
+ up(s);
+ dprintk(KERN_INFO "tf_crypto_lock_hwa: "
+ "Released for HWAID=0x%04X\n", hwa_id);
+ }
+}
+
+/*------------------------------------------------------------------------- */
+/*
+ * HWAs public lock or unlock HWA's specified in the HWA H/A/D fields of RPC
+ * command rpc_command
+ */
+static void tf_crypto_lock_hwas(u32 rpc_command, bool do_lock)
+{
+ dprintk(KERN_INFO
+ "tf_crypto_lock_hwas: rpc_command=0x%08x do_lock=%d\n",
+ rpc_command, do_lock);
+
+ /* perform the locks */
+ if (rpc_command & RPC_AES1_CODE)
+ tf_crypto_lock_hwa(RPC_AES1_CODE, do_lock);
+
+ if (rpc_command & RPC_DES_CODE)
+ tf_crypto_lock_hwa(RPC_DES_CODE, do_lock);
+
+ if (rpc_command & RPC_SHA_CODE)
+ tf_crypto_lock_hwa(RPC_SHA_CODE, do_lock);
+}
+
+/*------------------------------------------------------------------------- */
+/**
+ *Initialize the public crypto DMA channels, global HWA semaphores and handles
+ */
+u32 tf_crypto_init(void)
+{
+ struct tf_device *dev = tf_get_device();
+ u32 error = PUBLIC_CRYPTO_OPERATION_SUCCESS;
+
+ /* Initialize HWAs */
+ tf_aes_init();
+ tf_des_init();
+ tf_digest_init();
+
+ /*initialize the HWA semaphores */
+ sema_init(&dev->aes1_sema, 1);
+ sema_init(&dev->des_sema, 1);
+ sema_init(&dev->sha_sema, 1);
+
+ /*initialize the current key handle loaded in the AESn/DES HWA */
+ dev->aes1_key_context = 0;
+ dev->des_key_context = 0;
+ dev->sham1_is_public = false;
+
+ /*initialize the DMA semaphores */
+ mutex_init(&dev->sm.dma_mutex);
+
+ /*allocate DMA buffer */
+ dev->dma_buffer_length = PAGE_SIZE * 16;
+ dev->dma_buffer = dma_alloc_coherent(NULL,
+ dev->dma_buffer_length,
+ &(dev->dma_buffer_phys),
+ GFP_KERNEL);
+ if (dev->dma_buffer == NULL) {
+ printk(KERN_ERR
+ "tf_crypto_init: Out of memory for DMA buffer\n");
+ error = S_ERROR_OUT_OF_MEMORY;
+ }
+
+ return error;
+}
+
+/*------------------------------------------------------------------------- */
+/*
+ *Initialize the device context CUS fields (shortcut semaphore and public CUS
+ *list)
+ */
+void tf_crypto_init_cus(struct tf_connection *connection)
+{
+ /*initialize the CUS list in the given device context */
+ spin_lock_init(&(connection->shortcut_list_lock));
+ INIT_LIST_HEAD(&(connection->shortcut_list));
+}
+
+/*------------------------------------------------------------------------- */
+/**
+ *Terminate the public crypto (including DMA)
+ */
+void tf_crypto_terminate(void)
+{
+ struct tf_device *dev = tf_get_device();
+
+ if (dev->dma_buffer != NULL) {
+ dma_free_coherent(NULL, dev->dma_buffer_length,
+ dev->dma_buffer,
+ dev->dma_buffer_phys);
+ dev->dma_buffer = NULL;
+ }
+
+ tf_digest_exit();
+ tf_des_exit();
+ tf_aes_exit();
+}
+
+/*------------------------------------------------------------------------- */
+/*
+ *Perform a crypto update operation.
+ *THIS FUNCTION IS CALLED FROM THE IOCTL
+ */
+static bool tf_crypto_update(
+ struct cus_context *cus,
+ struct cus_params *params)
+{
+ bool status = true;
+ dprintk(KERN_INFO
+ "tf_crypto_update(%x): "\
+ "HWAID=0x%x, In=%p, Out=%p, Len=%u\n",
+ (uint32_t) cus, cus->hwa_id,
+ params->input_data,
+ params->output_data, params->input_data_length);
+
+ /* Enable the clock and Process Data */
+ switch (cus->hwa_id) {
+ case RPC_AES1_CODE:
+ tf_crypto_enable_clock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
+ cus->operation_state.aes.key_is_public = 0;
+ cus->operation_state.aes.CTRL = cus->hwa_ctrl;
+ status = tf_aes_update(
+ &cus->operation_state.aes,
+ params->input_data,
+ params->output_data,
+ params->input_data_length / AES_BLOCK_SIZE);
+ tf_crypto_disable_clock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
+ break;
+
+ case RPC_DES_CODE:
+ tf_crypto_enable_clock(PUBLIC_CRYPTO_DES3DES_CLOCK_REG);
+ status = tf_des_update(
+ cus->hwa_ctrl,
+ &cus->operation_state.des,
+ params->input_data,
+ params->output_data,
+ params->input_data_length / DES_BLOCK_SIZE);
+ tf_crypto_disable_clock(PUBLIC_CRYPTO_DES3DES_CLOCK_REG);
+ break;
+
+ case RPC_SHA_CODE:
+ tf_crypto_enable_clock(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
+ cus->operation_state.sha.CTRL = cus->hwa_ctrl;
+ status = tf_digest_update(
+ &cus->operation_state.sha,
+ params->input_data,
+ params->input_data_length);
+ tf_crypto_disable_clock(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
+ break;
+
+ default:
+ BUG_ON(1);
+ break;
+ }
+
+ dprintk(KERN_INFO "tf_crypto_update: Done\n");
+ return status;
+}
+
+/*------------------------------------------------------------------------- */
+
+/*
+ *Check if the command must be intercepted by a CUS or not.
+ *THIS FUNCTION IS CALLED FROM THE USER THREAD (ioctl).
+ *
+ *inputs: struct tf_connection *connection : current device context
+ * tf_command_invoke_client_command *command : the command
+ * bool incrementuse_count : specify if the use_count must be incremented
+ *output:
+ * struct cus_context **cus_ctx : the public CUS
+ * if it is shortcuted
+ *return: true or false
+ *
+ */
+static bool tf_crypto_is_shortcuted_command(
+ struct tf_connection *connection,
+ struct tf_command_invoke_client_command *command,
+ struct cus_context **cus_ctx,
+ bool incrementuse_count)
+{
+ struct tf_device *dev = tf_get_device();
+ struct cus_context *cus = NULL;
+ *cus_ctx = NULL;
+
+ dprintk(KERN_INFO "tf_crypto_is_shortcuted_command: "\
+ "connection=0x%08x, command=0x%08x, "\
+ "CltSession=0x%08x, CmdID=0x%08x\n",
+ (uint32_t) connection, (uint32_t) command,
+ (uint32_t) command->client_session,
+ command->client_command_identifier);
+
+ /*take shortcut_list_lock for the device context
+ *in which the message is sent <=> make sure that nobody is
+ *going to change data while processing */
+ spin_lock(&(connection->shortcut_list_lock));
+
+ /*lookup in the list of shortcuts attached to the device context for a
+ *shortcut context that contains the same client_session as the command
+ *and such that command_id is equal to client_command_identifier of the
+ *INVOKE_CLIENT_COMMAND message. If no such shortcut exists, take the
+ *standard path */
+ list_for_each_entry(
+ cus, &(connection->shortcut_list), list) {
+ dprintk(KERN_INFO
+ "tf_crypto_is_shortcuted_command: "\
+ "command_id = 0x%08x client_session = 0x%08x\n",
+ cus->command_id, cus->client_session);
+
+ if ((cus->client_session == command->client_session)
+ &&
+ (cus->command_id == command->
+ client_command_identifier)) {
+ dprintk(KERN_INFO
+ "tf_crypto_is_shortcuted_command: "\
+ "shortcut is identified\n");
+ /*find a CUS : check if is suspended or not */
+ if (cus->suspended) {
+ /*
+ * suspended of the shortcut context is set to
+ * true, it means that the secure world has
+ * suspended the shortcut to perform an update
+ * on its own. In this case, take the standard
+ * path. This should happen very rarely because
+ * the client and the service should generally
+ * communicate to avoid such a collision
+ */
+ dprintk(KERN_INFO "shortcut exists but "\
+ "suspended\n");
+ goto command_not_shortcutable;
+
+ } else {
+ dprintk(KERN_INFO "shortcut exists\n");
+ /*For AES and DES/3DES operations,
+ *provisionally determine if the accelerator
+ *is loaded with the appropriate key before
+ *deciding to enter the accelerator critical
+ *section. In most cases, if some other thread
+ *or the secure world is currently using the
+ *accelerator, the key won't change.
+ *So, if the key doesn't match now, it is
+ *likely not to match later on, so we'd better
+ *not try to enter the critical section in this
+ *case: */
+
+ if (cus->hwa_id == RPC_AES1_CODE &&
+ cus->
+ key_context != dev->
+ aes1_key_context) {
+ /*For AES operations, atomically read
+ *g_hAES1SSecureKeyContext and check if
+ *it is equal to key_context. If not,
+ *take the standard path <=> do not
+ *shortcut */
+ dprintk(KERN_INFO
+ "shortcut exists but AES key "\
+ "not correct\nkey_context="\
+ "0x%08x vs 0x%08x\n",
+ cus->key_context,
+ dev->
+ aes1_key_context);
+ goto command_not_shortcutable;
+
+ } else if (cus->hwa_id == RPC_DES_CODE
+ && cus->key_context !=
+ dev->
+ des_key_context) {
+ /*
+ * For DES/3DES atomically read
+ * des_key_context and check if
+ * it is equal to key_context. If not,
+ * take the standard path <=> do not
+ * shortcut
+ */
+ dprintk(KERN_INFO
+ "shortcut exists but DES key "
+ "not correct "
+ "des_key_context = 0x%08x"
+ " key_context0x%08x\n",
+ (u32)dev->
+ des_key_context,
+ (u32)cus->key_context);
+ goto command_not_shortcutable;
+ } else if (cus->hwa_id == RPC_SHA_CODE
+ && !dev->sham1_is_public) {
+ /*
+ * For digest operations, atomically
+ * read sham1_is_public and check if it
+ * is true. If not, no shortcut.
+ */
+ dprintk(KERN_INFO
+ "shortcut exists but SHAM1 "
+ "is not accessible in public");
+ goto command_not_shortcutable;
+ }
+ }
+
+ dprintk(KERN_INFO "shortcut exists and enable\n");
+
+ /*Shortcut has been found and context fits with
+ *thread => YES! the command can be shortcuted */
+
+ /*
+ *set the pointer on the corresponding session
+ *(eq CUS context)
+ */
+ *cus_ctx = cus;
+
+ /*
+ *increment use_count if required
+ */
+ if (incrementuse_count)
+ cus->use_count++;
+
+ /*
+ *release shortcut_list_lock
+ */
+ spin_unlock(&(connection->
+ shortcut_list_lock));
+ return true;
+ }
+ }
+
+ command_not_shortcutable:
+ /*
+ *release shortcut_list_lock
+ */
+ spin_unlock(&(connection->shortcut_list_lock));
+ *cus_ctx = NULL;
+ return false;
+}
+
+/*------------------------------------------------------------------------- */
+/*
+ * Pre-process the client command (crypto update operation), i.e., parse the
+ * command message (decode buffers, etc.) THIS FUNCTION IS CALLED FROM THE USER
+ * THREAD (ioctl).
+ *
+ * For incorrect messages, an error is returned and the message will be sent to
+ * secure
+ */
+static bool tf_crypto_parse_command_message(struct tf_connection *connection,
+ struct cus_context *cus,
+ struct tf_command_invoke_client_command *command,
+ struct cus_params *params)
+{
+ u32 param_type;
+ u32 input_data_length;
+ u32 output_data_length;
+ u8 *input_data;
+ u8 *output_data;
+ struct tf_shmem_desc *input_shmem = NULL;
+ struct tf_shmem_desc *output_shmem = NULL;
+
+ dprintk(KERN_INFO
+ "tf_crypto_parse_command_message(%p) : Session=0x%x\n",
+ cus, cus->client_session);
+
+ if (command->params[0].temp_memref.size == 0)
+ return false;
+
+ param_type = TF_GET_PARAM_TYPE(command->param_types, 0);
+ switch (param_type) {
+ case TF_PARAM_TYPE_MEMREF_TEMP_INPUT:
+ if (command->params[0].temp_memref.descriptor == 0)
+ return false;
+
+ input_data = (u8 *) command->params[0].temp_memref.
+ descriptor;
+ input_data_length = command->params[0].temp_memref.size;
+
+ break;
+
+ case TF_PARAM_TYPE_MEMREF_INPUT:
+ input_shmem = tf_get_shmem_from_block_handle(connection,
+ command->params[0].memref.block);
+
+ if (input_shmem == NULL)
+ return false;
+ atomic_inc(&input_shmem->ref_count);
+
+ input_data = input_shmem->pBuffer +
+ command->params[0].memref.offset;
+ input_data_length = command->params[0].memref.size;
+
+ break;
+
+ default:
+ return false;
+ }
+
+ if (cus->hwa_id != RPC_SHA_CODE) {
+ if (command->params[1].temp_memref.size == 0)
+ goto err0;
+
+ /* We need an output buffer as well */
+ param_type = TF_GET_PARAM_TYPE(command->param_types, 1);
+ switch (param_type) {
+ case TF_PARAM_TYPE_MEMREF_TEMP_OUTPUT:
+ output_data =
+ (u8 *) command->params[1].temp_memref.
+ descriptor;
+ output_data_length =
+ command->params[1].temp_memref.size;
+
+ break;
+
+ case TF_PARAM_TYPE_MEMREF_OUTPUT:
+ if (command->params[1].temp_memref.descriptor == 0)
+ return false;
+
+ output_shmem = tf_get_shmem_from_block_handle(
+ connection, command->params[1].memref.block);
+ if (output_shmem == NULL)
+ goto err0;
+ atomic_inc(&output_shmem->ref_count);
+
+ output_data = output_shmem->pBuffer +
+ command->params[1].memref.offset;
+ output_data_length = command->params[1].memref.size;
+
+ break;
+
+ default:
+ dprintk(KERN_ERR "tf_crypto_parse_command_message: "
+ "Encrypt/decrypt operations require an output "
+ "buffer\n");
+
+ goto err0;
+ }
+
+ if (output_data_length < input_data_length) {
+ dprintk(KERN_ERR "tf_crypto_parse_command_message: "
+ "Short buffer: output_data_length = %d < "
+ "input_data_length = %d\n",
+ output_data_length, input_data_length);
+ goto err1;
+ }
+ } else {
+ output_data_length = 0;
+ output_data = NULL;
+ }
+
+ /*
+ * Check if input length is compatible with the algorithm of the
+ * shortcut
+ */
+ switch (cus->hwa_id) {
+ case RPC_AES1_CODE:
+ /* Must be multiple of the AES block size */
+ if ((input_data_length % AES_BLOCK_SIZE) != 0) {
+ dprintk(KERN_ERR
+ "tf_crypto_parse_command_message(%p): "\
+ "Input Data Length invalid [%d] for AES\n",
+ cus, input_data_length);
+ goto err1;
+ }
+ break;
+ case RPC_DES_CODE:
+ /* Must be multiple of the DES block size */
+ if ((input_data_length % DES_BLOCK_SIZE) != 0) {
+ dprintk(KERN_ERR
+ "tf_crypto_parse_command_message(%p): "\
+ "Input Data Length invalid [%d] for DES\n",
+ cus, input_data_length);
+ goto err1;
+ }
+ break;
+ default:
+ /* SHA operation: no constraint on data length */
+ break;
+ }
+
+ params->input_data = input_data;
+ params->input_data_length = input_data_length;
+ params->input_shmem = input_shmem;
+ params->output_data = output_data;
+ params->output_data_length = output_data_length;
+ params->output_shmem = output_shmem;
+
+ return true;
+
+err1:
+ if (output_shmem)
+ atomic_dec(&output_shmem->ref_count);
+err0:
+ if (input_shmem)
+ atomic_dec(&input_shmem->ref_count);
+
+ return false;
+}
+
+/*------------------------------------------------------------------------- */
+
+/*
+ *Post-process the client command (crypto update operation),
+ *i.e. copy the result into the user output buffer and release the resources.
+ *THIS FUNCTION IS CALLED FROM THE USER THREAD (ioctl).
+ */
+static void tf_crypto_write_answer(
+ struct cus_context *cus,
+ struct cus_params *params,
+ struct tf_answer_invoke_client_command *answer)
+{
+ u32 error = S_SUCCESS;
+
+ dprintk(KERN_INFO
+ "tf_crypto_write_answer(%p) : Session=0x%x\n",
+ cus, cus->client_session);
+
+ /* Generate the answer */
+ answer->message_size =
+ (sizeof(struct tf_answer_invoke_client_command) -
+ sizeof(struct tf_answer_header)) / 4;
+ answer->message_type = TF_MESSAGE_TYPE_INVOKE_CLIENT_COMMAND;
+ answer->error_origin = TF_ORIGIN_TRUSTED_APP;
+ answer->operation_id = 0;
+ answer->error_code = error;
+ answer->answers[1].size.size = params->output_data_length;
+}
+
+/*------------------------------------------------------------------------- */
+
+int tf_crypto_try_shortcuted_update(struct tf_connection *connection,
+ struct tf_command_invoke_client_command *command,
+ struct tf_answer_invoke_client_command *answer)
+{
+ struct cus_context *cus = NULL;
+
+ if (tf_crypto_is_shortcuted_command(connection,
+ (struct tf_command_invoke_client_command *) command,
+ &cus, false)) {
+ u32 hwa_id = cus->hwa_id;
+
+ /* Lock HWA */
+ tf_crypto_lock_hwa(hwa_id, LOCK_HWA);
+
+ if (tf_crypto_is_shortcuted_command(connection,
+ command,
+ &cus, true)) {
+ struct cus_params cus_params;
+
+ memset(&cus_params, 0, sizeof(cus_params));
+
+ if (!tf_crypto_parse_command_message(
+ connection,
+ cus,
+ command,
+ &cus_params)) {
+ /* Decrement CUS context use count */
+ cus->use_count--;
+
+ /* Release HWA lock */
+ tf_crypto_lock_hwa(cus->hwa_id,
+ UNLOCK_HWA);
+
+ return -1;
+ }
+
+ /* Perform the update in public <=> THE shortcut */
+ if (!tf_crypto_update(cus, &cus_params)) {
+ /* Decrement CUS context use count */
+ cus->use_count--;
+
+ /* Release HWA lock */
+ tf_crypto_lock_hwa(cus->hwa_id,
+ UNLOCK_HWA);
+
+ return -1;
+ }
+
+ /* Write answer message */
+ tf_crypto_write_answer(cus,
+ &cus_params, answer);
+
+ /* Decrement registered shmems use count if needed */
+ if (cus_params.input_shmem)
+ atomic_dec(&cus_params.input_shmem->ref_count);
+ if (cus_params.output_shmem)
+ atomic_dec(&cus_params.output_shmem->ref_count);
+
+ /* Decrement CUS context use count */
+ cus->use_count--;
+
+ tf_crypto_lock_hwa(cus->hwa_id,
+ UNLOCK_HWA);
+ } else {
+ tf_crypto_lock_hwa(hwa_id, UNLOCK_HWA);
+ return -1;
+ }
+ } else {
+ return -1;
+ }
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------- */
+
+void tf_crypto_wait_for_ready_bit_infinitely(u32 *reg, u32 bit)
+{
+ while (!(INREG32(reg) & bit))
+ ;
+}
+
+/*------------------------------------------------------------------------- */
+
+u32 tf_crypto_wait_for_ready_bit(u32 *reg, u32 bit)
+{
+ u32 timeoutCounter = PUBLIC_CRYPTO_TIMEOUT_CONST;
+
+ while ((!(INREG32(reg) & bit)) && ((--timeoutCounter) != 0))
+ ;
+
+ if (timeoutCounter == 0)
+ return PUBLIC_CRYPTO_ERR_TIMEOUT;
+
+ return PUBLIC_CRYPTO_OPERATION_SUCCESS;
+}
+
+/*------------------------------------------------------------------------- */
+
+static DEFINE_SPINLOCK(clk_lock);
+
+void tf_crypto_disable_clock(uint32_t clock_paddr)
+{
+ u32 *clock_reg;
+ u32 val;
+ unsigned long flags;
+
+ dprintk(KERN_INFO "tf_crypto_disable_clock: " \
+ "clock_paddr=0x%08X\n",
+ clock_paddr);
+
+ /* Ensure none concurrent access when changing clock registers */
+ spin_lock_irqsave(&clk_lock, flags);
+
+ clock_reg = (u32 *)IO_ADDRESS(clock_paddr);
+
+ val = __raw_readl(clock_reg);
+ val &= ~(0x3);
+ __raw_writel(val, clock_reg);
+
+ /* Wait for clock to be fully disabled */
+ while ((__raw_readl(clock_reg) & 0x30000) == 0)
+ ;
+
+ spin_unlock_irqrestore(&clk_lock, flags);
+
+ tf_l4sec_clkdm_allow_idle(true);
+}
+
+/*------------------------------------------------------------------------- */
+
+void tf_crypto_enable_clock(uint32_t clock_paddr)
+{
+ u32 *clock_reg;
+ u32 val;
+ unsigned long flags;
+
+ dprintk(KERN_INFO "tf_crypto_enable_clock: " \
+ "clock_paddr=0x%08X\n",
+ clock_paddr);
+
+ tf_l4sec_clkdm_wakeup(true);
+
+ /* Ensure none concurrent access when changing clock registers */
+ spin_lock_irqsave(&clk_lock, flags);
+
+ clock_reg = (u32 *)IO_ADDRESS(clock_paddr);
+
+ val = __raw_readl(clock_reg);
+ val |= 0x2;
+ __raw_writel(val, clock_reg);
+
+ /* Wait for clock to be fully enabled */
+ while ((__raw_readl(clock_reg) & 0x30000) != 0)
+ ;
+
+ spin_unlock_irqrestore(&clk_lock, flags);
+}
+
+/*------------------------------------------------------------------------- */
+/* CUS RPCs */
+/*------------------------------------------------------------------------- */
+/*
+ * This RPC is used by the secure world to install a new shortcut. Optionally,
+ * for AES or DES/3DES operations, it can also lock the accelerator so that the
+ * secure world can install a new key in it.
+ */
+static int tf_crypto_install_shortcut_lock_hwa(
+ u32 rpc_command, void *rpc_shared_buffer)
+{
+ struct cus_context *cus = NULL;
+ struct tf_connection *connection = NULL;
+
+ /* Reference the input/ouput data */
+ struct rpc_install_shortcut_lock_accelerator_out *install_cus_out =
+ rpc_shared_buffer;
+ struct rpc_install_shortcut_lock_accelerator_in *install_cus_in =
+ rpc_shared_buffer;
+
+ dprintk(KERN_INFO "tf_crypto_install_shortcut_lock_hwa: "
+ "rpc_command=0x%08x; hwa_id=0x%08x\n",
+ rpc_command, install_cus_in->hwa_id);
+
+ connection = (struct tf_connection *)
+ install_cus_in->device_context_id;
+
+ if (connection == NULL) {
+ dprintk(KERN_INFO
+ "tf_crypto_install_shortcut_lock_hwa: "
+ "DeviceContext 0x%08x does not exist, "
+ "cannot create Shortcut\n",
+ install_cus_in->device_context_id);
+ install_cus_out->error = -1;
+ return 0;
+ }
+
+ /*
+ * Allocate a shortcut context. If the allocation fails,
+ * return S_ERROR_OUT_OF_MEMORY error code
+ */
+ cus = (struct cus_context *)
+ internal_kmalloc(sizeof(*cus), GFP_KERNEL);
+ if (cus == NULL) {
+ dprintk(KERN_ERR
+ "tf_crypto_install_shortcut_lock_hwa: "\
+ "Out of memory for public session\n");
+ install_cus_out->error = S_ERROR_OUT_OF_MEMORY;
+ return 0;
+ }
+
+ memset(cus, 0, sizeof(*cus));
+
+ /*setup the shortcut */
+ cus->magic_number = CUS_CONTEXT_MAGIC;
+ cus->client_session = install_cus_in->client_session;
+ cus->command_id = install_cus_in->command_id;
+ cus->hwa_id = install_cus_in->hwa_id;
+ cus->hwa_ctrl = install_cus_in->hwa_ctrl;
+ cus->key_context = install_cus_in->key_context;
+ cus->use_count = 0;
+ cus->suspended = false;
+
+ memcpy(&cus->operation_state,
+ &install_cus_in->operation_state,
+ sizeof(union tf_crypto_operation_state));
+
+ /*lock the shortcut_list_lock for this device context */
+ spin_lock(&connection->shortcut_list_lock);
+
+ /*Insert the shortcut in the list of shortcuts in the device context */
+ list_add(&(cus->list), &(connection->shortcut_list));
+
+ /*release shortcut_list_lock */
+ spin_unlock(&connection->shortcut_list_lock);
+
+ /*fill the output structure */
+ install_cus_out->shortcut_id = (u32) cus;
+ install_cus_out->error = S_SUCCESS;
+
+ /*If the L bit is true, then:
+ * Enter the accelerator critical section. If an update is currently in
+ * progress on the accelerator (using g_hXXXKeyContext key), this will
+ * wait until the update has completed. This is call when secure wants
+ * to install a key in HWA, once it is done secure world will release
+ * the lock. For SHA (activate shortcut is always called without LOCK
+ * fag):do nothing
+ */
+ if ((rpc_command & RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR_LOCK) != 0) {
+ /*Lock the HWA */
+ tf_crypto_lock_hwa(cus->hwa_id, LOCK_HWA);
+ }
+
+ dprintk(KERN_INFO
+ "tf_crypto_install_shortcut_lock_hwa: Done\n");
+
+ return S_SUCCESS;
+}
+
+/*------------------------------------------------------------------------- */
+
+/*
+ * This RPC is used to perform one or several of the following operations
+ * - Lock one or several accelerators for the exclusive use by the secure world,
+ * either because it is going to be switched to secure or because a new key is
+ * going to be loaded in the accelerator
+ * - Suspend a shortcut, i.e., make it temporarily unavailable to the public
+ * world. This is used when a secure update is going to be performed on the
+ * operation. The answer to the RPC then contains the operation state
+ * necessary for the secure world to do the update.
+ * - Uninstall the shortcut
+ */
+static int tf_crypto_lock_hwas_suspend_shortcut(
+ u32 rpc_command, void *rpc_shared_buffer)
+{
+ u32 target_shortcut;
+ struct cus_context *cus = NULL;
+ struct tf_connection *connection = NULL;
+
+ /*reference the input/ouput data */
+ struct rpc_lock_hwa_suspend_shortcut_out *suspend_cus_out =
+ rpc_shared_buffer;
+ struct rpc_lock_hwa_suspend_shortcut_in *suspend_cus_in =
+ rpc_shared_buffer;
+
+ dprintk(KERN_INFO
+ "tf_crypto_lock_hwas_suspend_shortcut: "\
+ "suspend_cus_in=0x%08x; shortcut_id=0x%08x\n",
+ suspend_cus_in->shortcut_id, (u32)suspend_cus_in);
+
+ target_shortcut = suspend_cus_in->shortcut_id;
+
+ /*lock HWAs */
+ tf_crypto_lock_hwas(rpc_command, LOCK_HWA);
+
+ /*if suspend_cus_in->shortcut_id != 0 and if rpc_command.S != 0,
+ then, suspend shortcut */
+ if ((target_shortcut != 0) && ((rpc_command &
+ RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_SUSPEND) != 0)) {
+ /*reference the CUSContext */
+ cus = (struct cus_context *)
+ suspend_cus_in->shortcut_id;
+
+ /*preventive check1: return if shortcut does not exist */
+ connection = tf_get_device_context(cus);
+ if (connection == NULL) {
+ dprintk(KERN_INFO
+ "tf_crypto_lock_hwas_suspend_shortcut: "\
+ "shortcut_id=0x%08x does not exist, cannot suspend "\
+ "Shortcut\n",
+ suspend_cus_in->shortcut_id);
+ return -1;
+ }
+
+loop_on_suspend:
+ /*lock shortcut_list_lock associated with the
+ *device context */
+ spin_lock(&connection->shortcut_list_lock);
+
+ /*Suspend shortcut */
+ cus->suspended = true;
+
+ if (cus->use_count != 0) {
+ /*release shortcut_list_lock */
+ spin_unlock(&connection->
+ shortcut_list_lock);
+ schedule();
+ goto loop_on_suspend;
+ }
+
+ /*Copy the operation state data stored in CUS Context into the
+ *answer to the RPC output assuming that HWA register has been
+ *saved at update time */
+ memcpy(&suspend_cus_out->operation_state,
+ &cus->operation_state,
+ sizeof(union tf_crypto_operation_state));
+
+ /*Uninstall shortcut if requiered */
+ if ((rpc_command &
+ RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT_UNINSTALL) != 0) {
+ dprintk(KERN_INFO
+ "tf_crypto_lock_hwas_suspend_shortcut:"\
+ "Uninstall 0x%08x\n",
+ target_shortcut);
+ list_del(&(cus->list));
+ /*list_del only remove the item in the list, the
+ *memory must be free afterward */
+ /*release the lock before calling internal_kfree */
+ spin_unlock(&connection->
+ shortcut_list_lock);
+ if (cus != NULL)
+ internal_kfree(cus);
+ return 0;
+ }
+
+ /*release shortcut_list_lock */
+ spin_unlock(&connection->shortcut_list_lock);
+ }
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------- */
+
+/*
+ * This RPC is used to perform one or several of the following operations:
+ * - Resume a shortcut previously suspended
+ * - Inform the public driver of the new keys installed in the DES and AES
+ * accelerators
+ * - Unlock some of the accelerators
+ */
+static int tf_crypto_resume_shortcut_unlock_hwas(
+ u32 rpc_command, void *rpc_shared_buffer)
+{
+ struct tf_device *dev = tf_get_device();
+ struct tf_connection *connection = NULL;
+ struct cus_context *cus = NULL;
+
+ /*reference the input data */
+ struct rpc_resume_shortcut_unlock_hwa_in *resume_cus_in =
+ rpc_shared_buffer;
+
+ dprintk(KERN_INFO
+ "tf_crypto_resume_shortcut_unlock_hwas\n"
+ "rpc_command=0x%08x\nshortcut_id=0x%08x\n",
+ rpc_command, resume_cus_in->shortcut_id);
+
+ /*if shortcut_id not 0 resume the shortcut and unlock HWA
+ else only unlock HWA */
+ if (resume_cus_in->shortcut_id != 0) {
+ /*reference the CUSContext */
+ cus = (struct cus_context *)
+ resume_cus_in->shortcut_id;
+
+ /*preventive check1: return if shortcut does not exist
+ *else, points to the public crypto monitor (inside the device
+ *context) */
+ connection = tf_get_device_context(cus);
+ if (connection == NULL) {
+ dprintk(KERN_INFO
+ "tf_crypto_resume_shortcut_unlock_hwas(...):"\
+ "shortcut_id 0x%08x does not exist, cannot suspend "\
+ "Shortcut\n",
+ resume_cus_in->shortcut_id);
+ return -1;
+ }
+
+ /*if S set and shortcut not yet suspended */
+ if ((cus->suspended) &&
+ ((rpc_command &
+ RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_RESUME) != 0)){
+ /*Write operation_stateData in the shortcut context */
+ memcpy(&cus->operation_state,
+ &resume_cus_in->operation_state,
+ sizeof(union tf_crypto_operation_state));
+ /*resume the shortcut */
+ cus->suspended = false;
+ }
+ }
+
+ /*
+ * If A is set: Atomically set aes1_key_context to
+ * aes1_key_context
+ */
+ if ((rpc_command &
+ RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_AES1) != 0) {
+ dev->aes1_key_context =
+ resume_cus_in->aes1_key_context;
+ }
+
+ /*
+ * If D is set:
+ * Atomically set des_key_context to des_key_context
+ */
+ if ((rpc_command &
+ RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_DES) != 0) {
+ dev->des_key_context =
+ resume_cus_in->des_key_context;
+ }
+
+ /* H is never set by the PA: Atomically set sham1_is_public to true */
+ dev->sham1_is_public = true;
+
+ /* Unlock HWAs according rpc_command */
+ tf_crypto_lock_hwas(rpc_command, UNLOCK_HWA);
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------- */
+
+/*
+ * This RPC is used to notify the public driver that the key in the AES, DES
+ * accelerators has been cleared. This happens only when the key is no longer
+ * referenced by any shortcuts. So, it is guaranteed that no-one has entered the
+ * accelerators critical section and there is no need to enter it to implement
+ * this RPC.
+ */
+static int tf_crypto_clear_global_key_context(
+ u32 rpc_command, void *rpc_shared_buffer)
+{
+ struct tf_device *dev = tf_get_device();
+
+ /*
+ * If A is set: Atomically set aes1_key_context to 0
+ */
+ if ((rpc_command &
+ RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_AES1) != 0) {
+ dev->aes1_key_context = 0;
+ }
+
+ /*
+ *If D is set: Atomically set des_key_context to 0
+ */
+ if ((rpc_command &
+ RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS_UNLOCK_DES) != 0) {
+ dev->des_key_context = 0;
+ }
+
+ return 0;
+}
+
+/*------------------------------------------------------------------------- */
+/*
+ * Execute a public crypto related RPC
+ */
+
+int tf_crypto_execute_rpc(u32 rpc_command, void *rpc_shared_buffer)
+{
+ switch (rpc_command & RPC_CRYPTO_COMMAND_MASK) {
+ case RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR:
+ dprintk(KERN_INFO "RPC_INSTALL_SHORTCUT_LOCK_ACCELERATOR\n");
+ return tf_crypto_install_shortcut_lock_hwa(
+ rpc_command, rpc_shared_buffer);
+
+ case RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT:
+ dprintk(KERN_INFO "RPC_LOCK_ACCELERATORS_SUSPEND_SHORTCUT\n");
+ return tf_crypto_lock_hwas_suspend_shortcut(
+ rpc_command, rpc_shared_buffer);
+
+ case RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS:
+ dprintk(KERN_INFO "RPC_RESUME_SHORTCUT_UNLOCK_ACCELERATORS\n");
+ return tf_crypto_resume_shortcut_unlock_hwas(
+ rpc_command, rpc_shared_buffer);
+
+ case RPC_CLEAR_GLOBAL_KEY_CONTEXT:
+ dprintk(KERN_INFO "RPC_CLEAR_GLOBAL_KEY_CONTEXT\n");
+ return tf_crypto_clear_global_key_context(
+ rpc_command, rpc_shared_buffer);
+ }
+
+ return -1;
+}
diff --git a/security/smc/omap4/scx_public_crypto.h b/security/smc/tf_crypto.h
index 984cb18..2291439 100644
--- a/security/smc/omap4/scx_public_crypto.h
+++ b/security/smc/tf_crypto.h
@@ -1,34 +1,39 @@
-/*
- * Copyright (c)2006-2008 Trusted Logic S.A.
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
* 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 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.
+ * 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
+ * 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 __SCX_PUBLIC_CRYPTO_H
-#define __SCX_PUBLIC_CRYPTO_H
+#ifndef __TF_PUBLIC_CRYPTO_H
+#define __TF_PUBLIC_CRYPTO_H
-#include "scxlnx_defs.h"
+#include "tf_defs.h"
#include <linux/io.h>
#include <mach/io.h>
#include <clockdomain.h>
+#ifdef __ASM_ARM_ARCH_OMAP_CLOCKDOMAIN_H
+#define clkdm_wakeup omap2_clkdm_wakeup
+#define clkdm_allow_idle omap2_clkdm_allow_idle
+#endif
+
/*-------------------------------------------------------------------------- */
#define PUBLIC_CRYPTO_HWA_AES1 0x1
-#define PUBLIC_CRYPTO_HWA_AES2 0x2
#define PUBLIC_CRYPTO_HWA_DES 0x4
#define PUBLIC_CRYPTO_HWA_SHA 0x8
@@ -39,7 +44,6 @@
#define PUBLIC_CRYPTO_CLKSTCTRL_CLOCK_REG 0x4A009580
#define PUBLIC_CRYPTO_AES1_CLOCK_REG 0x4A0095A0
-#define PUBLIC_CRYPTO_AES2_CLOCK_REG 0x4A0095A8
#define PUBLIC_CRYPTO_DES3DES_CLOCK_REG 0x4A0095B0
#define PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG 0x4A0095C8
@@ -110,14 +114,14 @@
/*
*The magic word.
*/
-#define CRYPTOKI_UPDATE_SHORTCUT_CONTEXT_MAGIC 0x45EF683C
+#define CUS_CONTEXT_MAGIC 0x45EF683C
/*-------------------------------------------------------------------------- */
/* CUS context structure */
/*-------------------------------------------------------------------------- */
/* State of an AES operation */
-struct PUBLIC_CRYPTO_AES_OPERATION_STATE {
+struct tf_crypto_aes_operation_state {
u32 AES_IV_0;
u32 AES_IV_1;
u32 AES_IV_2;
@@ -138,14 +142,14 @@ struct PUBLIC_CRYPTO_AES_OPERATION_STATE {
u32 key_is_public;
};
-struct PUBLIC_CRYPTO_DES_OPERATION_STATE {
+struct tf_crypto_des_operation_state {
u32 DES_IV_L;
u32 DES_IV_H;
};
#define HASH_BLOCK_BYTES_LENGTH 64
-struct PUBLIC_CRYPTO_SHA_OPERATION_STATE {
+struct tf_crypto_sha_operation_state {
/* Current digest */
u32 SHA_DIGEST_A;
u32 SHA_DIGEST_B;
@@ -157,63 +161,60 @@ struct PUBLIC_CRYPTO_SHA_OPERATION_STATE {
u32 SHA_DIGEST_H;
/* This buffer contains a partial chunk */
- u8 pChunkBuffer[HASH_BLOCK_BYTES_LENGTH];
+ u8 chunk_buffer[HASH_BLOCK_BYTES_LENGTH];
- /* Number of bytes stored in pChunkBuffer (0..64) */
- u32 nChunkLength;
+ /* Number of bytes stored in chunk_buffer (0..64) */
+ u32 chunk_length;
/*
* Total number of bytes processed so far
* (not including the partial chunk)
*/
- u32 nBytesProcessed;
+ u32 bytes_processed;
u32 CTRL;
};
-union PUBLIC_CRYPTO_OPERATION_STATE {
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE aes;
- struct PUBLIC_CRYPTO_DES_OPERATION_STATE des;
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE sha;
+union tf_crypto_operation_state {
+ struct tf_crypto_aes_operation_state aes;
+ struct tf_crypto_des_operation_state des;
+ struct tf_crypto_sha_operation_state sha;
};
/*
*Fully describes a public crypto operation
*(i.e., an operation that has a shortcut attached).
*/
-struct CRYPTOKI_UPDATE_SHORTCUT_CONTEXT {
+struct cus_context {
/*
*Identifies the public crypto operation in the list of all public
*operations.
*/
struct list_head list;
- u32 nMagicNumber; /*Must be set to
- *{CRYPTOKI_UPDATE_SHORTCUT_CONTEXT_MAGIC} */
+ u32 magic_number; /*Must be set to
+ *{CUS_CONTEXT_MAGIC} */
/*basic fields */
- u32 hClientSession;
- u32 nCommandID;
- u32 nHWAID;
- u32 nHWA_CTRL;
- u32 hKeyContext;
- union PUBLIC_CRYPTO_OPERATION_STATE sOperationState;
- u32 nUseCount;
- bool bSuspended;
+ u32 client_session;
+ u32 command_id;
+ u32 hwa_id;
+ u32 hwa_ctrl;
+ u32 key_context;
+ union tf_crypto_operation_state operation_state;
+ u32 use_count;
+ bool suspended;
};
-struct CRYPTOKI_UPDATE_PARAMS {
+struct cus_params {
/*fields for data processing of an update command */
- u32 nInputDataLength;
- u8 *pInputData;
- struct SCXLNX_SHMEM_DESC *pInputShmem;
-
- u32 nResultDataLength;
- u8 *pResultData;
- struct SCXLNX_SHMEM_DESC *pOutputShmem;
+ u32 input_data_length;
+ u8 *input_data;
+ struct tf_shmem_desc *input_shmem;
- u8 *pS2CDataBuffer;
- u32 nS2CDataBufferMaxLength;
+ u32 output_data_length;
+ u8 *output_data;
+ struct tf_shmem_desc *output_shmem;
};
/*-------------------------------------------------------------------------- */
@@ -224,46 +225,46 @@ struct CRYPTOKI_UPDATE_PARAMS {
/*
*Initialize the public crypto DMA chanels and global HWA semaphores
*/
-u32 SCXPublicCryptoInit(void);
+u32 tf_crypto_init(void);
/*
*Initialize the device context CUS fields
*(shortcut semaphore and public CUS list)
*/
-void SCXPublicCryptoInitDeviceContext(struct SCXLNX_CONNECTION *pDeviceContext);
+void tf_crypto_init_cus(struct tf_connection *connection);
/**
*Terminate the public crypto (including DMA)
*/
-void SCXPublicCryptoTerminate(void);
+void tf_crypto_terminate(void);
-int SCXPublicCryptoTryShortcutedUpdate(struct SCXLNX_CONNECTION *pConn,
- struct SCX_COMMAND_INVOKE_CLIENT_COMMAND *pMessage,
- struct SCX_ANSWER_INVOKE_CLIENT_COMMAND *pAnswer);
+int tf_crypto_try_shortcuted_update(struct tf_connection *connection,
+ struct tf_command_invoke_client_command *command,
+ struct tf_answer_invoke_client_command *answer);
-int SCXPublicCryptoExecuteRPCCommand(u32 nRPCCommand, void *pRPCSharedBuffer);
+int tf_crypto_execute_rpc(u32 rpc_command, void *rpc_shared_buffer);
/*-------------------------------------------------------------------------- */
/*
*Helper methods
*/
-u32 SCXPublicCryptoWaitForReadyBit(u32 *pRegister, u32 vBit);
-void SCXPublicCryptoWaitForReadyBitInfinitely(u32 *pRegister, u32 vBit);
+u32 tf_crypto_wait_for_ready_bit(u32 *reg, u32 bit);
+void tf_crypto_wait_for_ready_bit_infinitely(u32 *reg, u32 bit);
-void SCXPublicCryptoEnableClock(uint32_t vClockPhysAddr);
-void SCXPublicCryptoDisableClock(uint32_t vClockPhysAddr);
+void tf_crypto_enable_clock(uint32_t clock_paddr);
+void tf_crypto_disable_clock(uint32_t clock_paddr);
#define LOCK_HWA true
#define UNLOCK_HWA false
-void PDrvCryptoLockUnlockHWA(u32 nHWAID, bool bDoLock);
+void tf_crypto_lock_hwa(u32 hwa_id, bool do_lock);
/*---------------------------------------------------------------------------*/
/* AES operations */
/*---------------------------------------------------------------------------*/
-void PDrvCryptoAESInit(void);
-void PDrvCryptoAESExit(void);
+void tf_aes_init(void);
+void tf_aes_exit(void);
#ifdef CONFIG_SMC_KERNEL_CRYPTO
int register_smc_public_crypto_aes(void);
@@ -283,21 +284,21 @@ static inline void unregister_smc_public_crypto_aes(void) {}
*The AES1 accelerator is assumed loaded with the correct key
*
*AES_CTRL: defines the mode and direction
- *pAESState: defines the operation IV
- *pSrc: Input buffer to process.
- *pDest: Output buffer containing the processed data.
+ *aes_state: defines the operation IV
+ *src: Input buffer to process.
+ *dest: Output buffer containing the processed data.
*
- *nbBlocks number of block(s)to process.
+ *nb_blocks number of block(s)to process.
*/
-bool PDrvCryptoUpdateAES(struct PUBLIC_CRYPTO_AES_OPERATION_STATE *pAESState,
- u8 *pSrc, u8 *pDest, u32 nbBlocks);
+bool tf_aes_update(struct tf_crypto_aes_operation_state *aes_state,
+ u8 *src, u8 *dest, u32 nb_blocks);
/*---------------------------------------------------------------------------*/
/* DES/DES3 operations */
/*---------------------------------------------------------------------------*/
-void PDrvCryptoDESInit(void);
-void PDrvCryptoDESExit(void);
+void tf_des_init(void);
+void tf_des_exit(void);
/**
*This function performs a DES update operation.
@@ -305,21 +306,21 @@ void PDrvCryptoDESExit(void);
*The DES accelerator is assumed loaded with the correct key
*
*DES_CTRL: defines the mode and direction
- *pDESState: defines the operation IV
- *pSrc: Input buffer to process.
- *pDest: Output buffer containing the processed data.
- *nbBlocks: Number of block(s)to process.
+ *des_state: defines the operation IV
+ *src: Input buffer to process.
+ *dest: Output buffer containing the processed data.
+ *nb_blocks: Number of block(s)to process.
*/
-bool PDrvCryptoUpdateDES(u32 DES_CTRL,
- struct PUBLIC_CRYPTO_DES_OPERATION_STATE *pDESState,
- u8 *pSrc, u8 *pDest, u32 nbBlocks);
+bool tf_des_update(u32 DES_CTRL,
+ struct tf_crypto_des_operation_state *des_state,
+ u8 *src, u8 *dest, u32 nb_blocks);
/*---------------------------------------------------------------------------*/
/* Digest operations */
/*---------------------------------------------------------------------------*/
-void PDrvCryptoDigestInit(void);
-void PDrvCryptoDigestExit(void);
+void tf_digest_init(void);
+void tf_digest_exit(void);
#ifdef CONFIG_SMC_KERNEL_CRYPTO
int register_smc_public_crypto_digest(void);
@@ -337,12 +338,12 @@ static inline void unregister_smc_public_crypto_digest(void) {}
*This function performs a HASH update Operation.
*
*SHA_CTRL: defines the algorithm
- *pSHAState: State of the operation
- *pData: Input buffer to process
- *dataLength: Length in bytes of the input buffer.
+ *sha_state: State of the operation
+ *data: Input buffer to process
+ *data_length: Length in bytes of the input buffer.
*/
-void PDrvCryptoUpdateHash(
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *pSHAState,
- u8 *pData, u32 dataLength);
+bool tf_digest_update(
+ struct tf_crypto_sha_operation_state *sha_state,
+ u8 *data, u32 data_length);
-#endif /*__SCX_PUBLIC_CRYPTO_H */
+#endif /*__TF_PUBLIC_CRYPTO_H */
diff --git a/security/smc/omap4/scx_public_crypto_AES.c b/security/smc/tf_crypto_aes.c
index 96b065f..36dc522 100644
--- a/security/smc/omap4/scx_public_crypto_AES.c
+++ b/security/smc/tf_crypto_aes.c
@@ -1,26 +1,27 @@
-/*
- * Copyright (c) 2006-2010 Trusted Logic S.A.
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
* 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 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.
+ * 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
+ * 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 "scxlnx_defs.h"
-#include "scxlnx_util.h"
-#include "scx_public_crypto.h"
-#include "scx_public_dma.h"
-#include "scxlnx_mshield.h"
+#include "tf_defs.h"
+#include "tf_util.h"
+#include "tf_crypto.h"
+#include "tf_dma.h"
+#include "tf_zebra.h"
#include <linux/io.h>
#include <linux/interrupt.h>
@@ -35,7 +36,6 @@
*AES Hardware Accelerator: Base address
*/
#define AES1_REGS_HW_ADDR 0x4B501000
-#define AES2_REGS_HW_ADDR 0x4B701000
/*
*CTRL register Masks
@@ -80,7 +80,7 @@
/**
*This structure contains the registers of the AES HW accelerator.
*/
-struct AESReg_t {
+struct aes_reg {
u32 AES_KEY2_6; /* 0x00 */
u32 AES_KEY2_7; /* 0xO4 */
u32 AES_KEY2_4; /* 0x08 */
@@ -126,7 +126,7 @@ struct AESReg_t {
u32 AES_SYSSTATUS; /* 0x88 */
};
-static struct AESReg_t *pAESReg_t;
+static struct aes_reg *paes_reg;
#ifdef CONFIG_SMC_KERNEL_CRYPTO
#define FLAGS_FAST BIT(7)
@@ -158,7 +158,7 @@ struct aes_hwa_ctx {
int dma_lch_out;
dma_addr_t dma_addr_out;
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *ctx;
+ struct tf_crypto_aes_operation_state *ctx;
};
static struct aes_hwa_ctx *aes_ctx;
#endif
@@ -167,69 +167,68 @@ static struct aes_hwa_ctx *aes_ctx;
*Forward declarations
*------------------------------------------------------------------------- */
-static void PDrvCryptoUpdateAESWithDMA(u8 *pSrc, u8 *pDest,
- u32 nbBlocks);
+static bool tf_aes_update_dma(u8 *src, u8 *dest, u32 nb_blocks, bool is_kernel);
/*----------------------------------------------------------------------------
*Save HWA registers into the specified operation state structure
*--------------------------------------------------------------------------*/
-static void PDrvCryptoSaveAESRegisters(
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *pAESState)
+static void tf_aes_save_registers(
+ struct tf_crypto_aes_operation_state *aes_state)
{
- dprintk(KERN_INFO "PDrvCryptoSaveAESRegisters: \
- pAESState(%p) <- pAESReg_t(%p): CTRL=0x%08x\n",
- pAESState, pAESReg_t, pAESState->CTRL);
+ dprintk(KERN_INFO "tf_aes_save_registers: "
+ "aes_state(%p) <- paes_reg(%p): CTRL=0x%08x\n",
+ aes_state, paes_reg, aes_state->CTRL);
/*Save the IV if we are in CBC or CTR mode (not required for ECB) */
- if (!AES_CTRL_IS_MODE_ECB(pAESState->CTRL)) {
- pAESState->AES_IV_0 = INREG32(&pAESReg_t->AES_IV_IN_0);
- pAESState->AES_IV_1 = INREG32(&pAESReg_t->AES_IV_IN_1);
- pAESState->AES_IV_2 = INREG32(&pAESReg_t->AES_IV_IN_2);
- pAESState->AES_IV_3 = INREG32(&pAESReg_t->AES_IV_IN_3);
+ if (!AES_CTRL_IS_MODE_ECB(aes_state->CTRL)) {
+ aes_state->AES_IV_0 = INREG32(&paes_reg->AES_IV_IN_0);
+ aes_state->AES_IV_1 = INREG32(&paes_reg->AES_IV_IN_1);
+ aes_state->AES_IV_2 = INREG32(&paes_reg->AES_IV_IN_2);
+ aes_state->AES_IV_3 = INREG32(&paes_reg->AES_IV_IN_3);
}
}
/*----------------------------------------------------------------------------
*Restore the HWA registers from the operation state structure
*---------------------------------------------------------------------------*/
-static void PDrvCryptoRestoreAESRegisters(
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *pAESState)
+static void tf_aes_restore_registers(
+ struct tf_crypto_aes_operation_state *aes_state)
{
- struct SCXLNX_DEVICE *pDevice = SCXLNXGetDevice();
-
- dprintk(KERN_INFO "PDrvCryptoRestoreAESRegisters: \
- pAESReg_t(%p) <- pAESState(%p): CTRL=0x%08x\n",
- pAESReg_t, pAESState, pAESState->CTRL);
-
- if (pAESState->key_is_public) {
- OUTREG32(&pAESReg_t->AES_KEY1_0, pAESState->KEY1_0);
- OUTREG32(&pAESReg_t->AES_KEY1_1, pAESState->KEY1_1);
- OUTREG32(&pAESReg_t->AES_KEY1_2, pAESState->KEY1_2);
- OUTREG32(&pAESReg_t->AES_KEY1_3, pAESState->KEY1_3);
- OUTREG32(&pAESReg_t->AES_KEY1_4, pAESState->KEY1_4);
- OUTREG32(&pAESReg_t->AES_KEY1_5, pAESState->KEY1_5);
- OUTREG32(&pAESReg_t->AES_KEY1_6, pAESState->KEY1_6);
- OUTREG32(&pAESReg_t->AES_KEY1_7, pAESState->KEY1_7);
+ struct tf_device *dev = tf_get_device();
+
+ dprintk(KERN_INFO "tf_aes_restore_registers: "
+ "paes_reg(%p) <- aes_state(%p): CTRL=0x%08x\n",
+ paes_reg, aes_state, aes_state->CTRL);
+
+ if (aes_state->key_is_public) {
+ OUTREG32(&paes_reg->AES_KEY1_0, aes_state->KEY1_0);
+ OUTREG32(&paes_reg->AES_KEY1_1, aes_state->KEY1_1);
+ OUTREG32(&paes_reg->AES_KEY1_2, aes_state->KEY1_2);
+ OUTREG32(&paes_reg->AES_KEY1_3, aes_state->KEY1_3);
+ OUTREG32(&paes_reg->AES_KEY1_4, aes_state->KEY1_4);
+ OUTREG32(&paes_reg->AES_KEY1_5, aes_state->KEY1_5);
+ OUTREG32(&paes_reg->AES_KEY1_6, aes_state->KEY1_6);
+ OUTREG32(&paes_reg->AES_KEY1_7, aes_state->KEY1_7);
/*
* Make sure a potential secure key that has been overwritten by
* the previous code is reinstalled before performing other
* public crypto operations.
*/
- pDevice->hAES1SecureKeyContext = 0;
+ dev->aes1_key_context = 0;
} else {
- pAESState->CTRL |= INREG32(&pAESReg_t->AES_CTRL);
+ aes_state->CTRL |= INREG32(&paes_reg->AES_CTRL);
}
/*
* Restore the IV first if we are in CBC or CTR mode
* (not required for ECB)
*/
- if (!AES_CTRL_IS_MODE_ECB(pAESState->CTRL)) {
- OUTREG32(&pAESReg_t->AES_IV_IN_0, pAESState->AES_IV_0);
- OUTREG32(&pAESReg_t->AES_IV_IN_1, pAESState->AES_IV_1);
- OUTREG32(&pAESReg_t->AES_IV_IN_2, pAESState->AES_IV_2);
- OUTREG32(&pAESReg_t->AES_IV_IN_3, pAESState->AES_IV_3);
+ if (!AES_CTRL_IS_MODE_ECB(aes_state->CTRL)) {
+ OUTREG32(&paes_reg->AES_IV_IN_0, aes_state->AES_IV_0);
+ OUTREG32(&paes_reg->AES_IV_IN_1, aes_state->AES_IV_1);
+ OUTREG32(&paes_reg->AES_IV_IN_2, aes_state->AES_IV_2);
+ OUTREG32(&paes_reg->AES_IV_IN_3, aes_state->AES_IV_3);
}
/* Then set the CTRL register:
@@ -237,126 +236,153 @@ static void PDrvCryptoRestoreAESRegisters(
* it leads to break the HWA process (observed by experimentation)
*/
- pAESState->CTRL = (pAESState->CTRL & (3 << 3)) /* key size */
- | (pAESState->CTRL & ((1 << 2) | (1 << 5) | (1 << 6)))
+ aes_state->CTRL = (aes_state->CTRL & (3 << 3)) /* key size */
+ | (aes_state->CTRL & ((1 << 2) | (1 << 5) | (1 << 6)))
| (0x3 << 7) /* Always set CTR_WIDTH to 128-bit */;
- if ((pAESState->CTRL & 0x1FC) !=
- (INREG32(&pAESReg_t->AES_CTRL) & 0x1FC))
- OUTREG32(&pAESReg_t->AES_CTRL, pAESState->CTRL & 0x1FC);
+ if ((aes_state->CTRL & 0x1FC) !=
+ (INREG32(&paes_reg->AES_CTRL) & 0x1FC))
+ OUTREG32(&paes_reg->AES_CTRL, aes_state->CTRL & 0x1FC);
/* Set the SYSCONFIG register to 0 */
- OUTREG32(&pAESReg_t->AES_SYSCONFIG, 0);
+ OUTREG32(&paes_reg->AES_SYSCONFIG, 0);
}
/*-------------------------------------------------------------------------- */
-void PDrvCryptoAESInit(void)
+void tf_aes_init(void)
{
- pAESReg_t = omap_ioremap(AES1_REGS_HW_ADDR, SZ_1M, MT_DEVICE);
- if (pAESReg_t == NULL)
+ paes_reg = omap_ioremap(AES1_REGS_HW_ADDR, SZ_1M, MT_DEVICE);
+ if (paes_reg == NULL)
panic("Unable to remap AES1 module");
}
-void PDrvCryptoAESExit(void)
+void tf_aes_exit(void)
{
- omap_iounmap(pAESReg_t);
+ omap_iounmap(paes_reg);
}
-bool PDrvCryptoUpdateAES(struct PUBLIC_CRYPTO_AES_OPERATION_STATE *pAESState,
- u8 *pSrc, u8 *pDest, u32 nbBlocks)
+bool tf_aes_update(struct tf_crypto_aes_operation_state *aes_state,
+ u8 *src, u8 *dest, u32 nb_blocks)
{
u32 nbr_of_blocks;
- u32 vTemp;
- u8 *pProcessSrc = pSrc;
- u8 *pProcessDest = pDest;
- u32 dmaUse = PUBLIC_CRYPTO_DMA_USE_NONE;
+ u32 temp;
+ u8 *process_src;
+ u8 *process_dest;
+ u32 dma_use = PUBLIC_CRYPTO_DMA_USE_NONE;
+ bool is_kernel = false;
/*
*Choice of the processing type
*/
- if (nbBlocks * AES_BLOCK_SIZE >= DMA_TRIGGER_IRQ_AES)
- dmaUse = PUBLIC_CRYPTO_DMA_USE_IRQ;
-
- dprintk(KERN_INFO "PDrvCryptoUpdateAES: \
- pSrc=0x%08x, pDest=0x%08x, nbBlocks=0x%08x, dmaUse=0x%08x\n",
- (unsigned int)pSrc,
- (unsigned int)pDest,
- (unsigned int)nbBlocks,
- (unsigned int)dmaUse);
-
- if (nbBlocks == 0) {
- dprintk(KERN_INFO "PDrvCryptoUpdateAES: Nothing to process\n");
+ if (nb_blocks * AES_BLOCK_SIZE >= DMA_TRIGGER_IRQ_AES)
+ dma_use = PUBLIC_CRYPTO_DMA_USE_IRQ;
+
+ dprintk(KERN_INFO "tf_aes_update: "
+ "src=0x%08x, dest=0x%08x, nb_blocks=0x%08x, dma_use=0x%08x\n",
+ (unsigned int)src,
+ (unsigned int)dest,
+ (unsigned int)nb_blocks,
+ (unsigned int)dma_use);
+
+ if (aes_state->key_is_public)
+ is_kernel = true;
+
+ if (nb_blocks == 0) {
+ dprintk(KERN_INFO "tf_aes_update: Nothing to process\n");
return true;
}
- if ((AES_CTRL_GET_DIRECTION(INREG32(&pAESReg_t->AES_CTRL)) !=
- AES_CTRL_GET_DIRECTION(pAESState->CTRL)) &&
- !pAESState->key_is_public) {
+ if ((AES_CTRL_GET_DIRECTION(INREG32(&paes_reg->AES_CTRL)) !=
+ AES_CTRL_GET_DIRECTION(aes_state->CTRL)) &&
+ !aes_state->key_is_public) {
dprintk(KERN_WARNING "HWA configured for another direction\n");
return false;
}
/*Restore the registers of the accelerator from the operation state */
- PDrvCryptoRestoreAESRegisters(pAESState);
+ tf_aes_restore_registers(aes_state);
- if (dmaUse == PUBLIC_CRYPTO_DMA_USE_IRQ) {
+ if (dma_use == PUBLIC_CRYPTO_DMA_USE_IRQ) {
/* Perform the update with DMA */
- PDrvCryptoUpdateAESWithDMA(pProcessSrc,
- pProcessDest, nbBlocks);
+ if (!tf_aes_update_dma(src, dest, nb_blocks, is_kernel))
+ return false;
} else {
+ u8 buf[DMA_TRIGGER_IRQ_AES];
+
+ /*
+ * Synchronous Linux crypto API buffers are mapped in kernel
+ * space
+ */
+
+ if (is_kernel) {
+ process_src = src;
+ process_dest = dest;
+ } else {
+ if (copy_from_user(buf, src,
+ nb_blocks * AES_BLOCK_SIZE))
+ return false;
+
+ process_src = process_dest = buf;
+ }
+
for (nbr_of_blocks = 0;
- nbr_of_blocks < nbBlocks; nbr_of_blocks++) {
+ nbr_of_blocks < nb_blocks; nbr_of_blocks++) {
/*We wait for the input ready */
/*Crash the system as this should never occur */
- if (SCXPublicCryptoWaitForReadyBit(
- (u32 *)&pAESReg_t->AES_CTRL,
+ if (tf_crypto_wait_for_ready_bit(
+ (u32 *)&paes_reg->AES_CTRL,
AES_CTRL_INPUT_READY_BIT) !=
PUBLIC_CRYPTO_OPERATION_SUCCESS)
- panic("Wait too long for AES hardware \
- accelerator Input data to be ready\n");
+ panic("Wait too long for AES hardware "
+ "accelerator Input data to be ready\n");
/* We copy the 16 bytes of data src->reg */
- vTemp = (u32) BYTES_TO_LONG(pProcessSrc);
- OUTREG32(&pAESReg_t->AES_DATA_IN_0, vTemp);
- pProcessSrc += 4;
- vTemp = (u32) BYTES_TO_LONG(pProcessSrc);
- OUTREG32(&pAESReg_t->AES_DATA_IN_1, vTemp);
- pProcessSrc += 4;
- vTemp = (u32) BYTES_TO_LONG(pProcessSrc);
- OUTREG32(&pAESReg_t->AES_DATA_IN_2, vTemp);
- pProcessSrc += 4;
- vTemp = (u32) BYTES_TO_LONG(pProcessSrc);
- OUTREG32(&pAESReg_t->AES_DATA_IN_3, vTemp);
- pProcessSrc += 4;
+ temp = (u32) BYTES_TO_LONG(process_src);
+ OUTREG32(&paes_reg->AES_DATA_IN_0, temp);
+ process_src += 4;
+ temp = (u32) BYTES_TO_LONG(process_src);
+ OUTREG32(&paes_reg->AES_DATA_IN_1, temp);
+ process_src += 4;
+ temp = (u32) BYTES_TO_LONG(process_src);
+ OUTREG32(&paes_reg->AES_DATA_IN_2, temp);
+ process_src += 4;
+ temp = (u32) BYTES_TO_LONG(process_src);
+ OUTREG32(&paes_reg->AES_DATA_IN_3, temp);
+ process_src += 4;
/* We wait for the output ready */
- SCXPublicCryptoWaitForReadyBitInfinitely(
- (u32 *)&pAESReg_t->AES_CTRL,
+ tf_crypto_wait_for_ready_bit_infinitely(
+ (u32 *)&paes_reg->AES_CTRL,
AES_CTRL_OUTPUT_READY_BIT);
/* We copy the 16 bytes of data reg->dest */
- vTemp = INREG32(&pAESReg_t->AES_DATA_IN_0);
- LONG_TO_BYTE(pProcessDest, vTemp);
- pProcessDest += 4;
- vTemp = INREG32(&pAESReg_t->AES_DATA_IN_1);
- LONG_TO_BYTE(pProcessDest, vTemp);
- pProcessDest += 4;
- vTemp = INREG32(&pAESReg_t->AES_DATA_IN_2);
- LONG_TO_BYTE(pProcessDest, vTemp);
- pProcessDest += 4;
- vTemp = INREG32(&pAESReg_t->AES_DATA_IN_3);
- LONG_TO_BYTE(pProcessDest, vTemp);
- pProcessDest += 4;
+ temp = INREG32(&paes_reg->AES_DATA_IN_0);
+ LONG_TO_BYTE(process_dest, temp);
+ process_dest += 4;
+ temp = INREG32(&paes_reg->AES_DATA_IN_1);
+ LONG_TO_BYTE(process_dest, temp);
+ process_dest += 4;
+ temp = INREG32(&paes_reg->AES_DATA_IN_2);
+ LONG_TO_BYTE(process_dest, temp);
+ process_dest += 4;
+ temp = INREG32(&paes_reg->AES_DATA_IN_3);
+ LONG_TO_BYTE(process_dest, temp);
+ process_dest += 4;
}
+
+ if (!is_kernel)
+ if (copy_to_user(dest, buf,
+ nb_blocks * AES_BLOCK_SIZE))
+ return false;
}
/* Save the accelerator registers into the operation state */
- PDrvCryptoSaveAESRegisters(pAESState);
+ tf_aes_save_registers(aes_state);
- dprintk(KERN_INFO "PDrvCryptoUpdateAES: Done\n");
+ dprintk(KERN_INFO "tf_aes_update: Done\n");
return true;
}
@@ -366,13 +392,13 @@ bool PDrvCryptoUpdateAES(struct PUBLIC_CRYPTO_AES_OPERATION_STATE *pAESState,
*Static function, perform AES encryption/decryption using the DMA for data
*transfer.
*
- *inputs: pSrc : pointer of the input data to process
- * nbBlocks : number of block to process
- * dmaUse : PUBLIC_CRYPTO_DMA_USE_IRQ (use irq to monitor end of DMA)
+ *inputs: src : pointer of the input data to process
+ * nb_blocks : number of block to process
+ * dma_use : PUBLIC_CRYPTO_DMA_USE_IRQ (use irq to monitor end of DMA)
* | PUBLIC_CRYPTO_DMA_USE_POLLING (poll the end of DMA)
- *output: pDest : pointer of the output data (can be eq to pSrc)
+ *output: dest : pointer of the output data (can be eq to src)
*/
-static void PDrvCryptoUpdateAESWithDMA(u8 *pSrc, u8 *pDest, u32 nbBlocks)
+static bool tf_aes_update_dma(u8 *src, u8 *dest, u32 nb_blocks, bool is_kernel)
{
/*
*Note: The DMA only sees physical addresses !
@@ -382,31 +408,33 @@ static void PDrvCryptoUpdateAESWithDMA(u8 *pSrc, u8 *pDest, u32 nbBlocks)
int dma_ch1;
struct omap_dma_channel_params ch0_parameters;
struct omap_dma_channel_params ch1_parameters;
- u32 nLength = nbBlocks * AES_BLOCK_SIZE;
- u32 nLengthLoop = 0;
- u32 nbBlocksLoop = 0;
- struct SCXLNX_DEVICE *pDevice = SCXLNXGetDevice();
+ u32 length = nb_blocks * AES_BLOCK_SIZE;
+ u32 length_loop = 0;
+ u32 nb_blocksLoop = 0;
+ struct tf_device *dev = tf_get_device();
dprintk(KERN_INFO
- "PDrvCryptoUpdateAESWithDMA: In=0x%08x, Out=0x%08x, Len=%u\n",
- (unsigned int)pSrc,
- (unsigned int)pDest,
- (unsigned int)nLength);
+ "%s: In=0x%08x, Out=0x%08x, Len=%u\n",
+ __func__,
+ (unsigned int)src,
+ (unsigned int)dest,
+ (unsigned int)length);
/*lock the DMA */
- mutex_lock(&pDevice->sm.sDMALock);
+ while (!mutex_trylock(&dev->sm.dma_mutex))
+ cpu_relax();
- if (scxPublicDMARequest(&dma_ch0) != PUBLIC_CRYPTO_OPERATION_SUCCESS) {
- mutex_unlock(&pDevice->sm.sDMALock);
- return;
+ if (tf_dma_request(&dma_ch0) != PUBLIC_CRYPTO_OPERATION_SUCCESS) {
+ mutex_unlock(&dev->sm.dma_mutex);
+ return false;
}
- if (scxPublicDMARequest(&dma_ch1) != PUBLIC_CRYPTO_OPERATION_SUCCESS) {
- scxPublicDMARelease(dma_ch0);
- mutex_unlock(&pDevice->sm.sDMALock);
- return;
+ if (tf_dma_request(&dma_ch1) != PUBLIC_CRYPTO_OPERATION_SUCCESS) {
+ omap_free_dma(dma_ch0);
+ mutex_unlock(&dev->sm.dma_mutex);
+ return false;
}
- while (nLength > 0) {
+ while (length > 0) {
/*
* At this time, we are sure that the DMAchannels
@@ -414,52 +442,61 @@ static void PDrvCryptoUpdateAESWithDMA(u8 *pSrc, u8 *pDest, u32 nbBlocks)
*/
/*DMA used for Input and Output */
- OUTREG32(&pAESReg_t->AES_SYSCONFIG,
- INREG32(&pAESReg_t->AES_SYSCONFIG)
+ OUTREG32(&paes_reg->AES_SYSCONFIG,
+ INREG32(&paes_reg->AES_SYSCONFIG)
| AES_SYSCONFIG_DMA_REQ_OUT_EN_BIT
| AES_SYSCONFIG_DMA_REQ_IN_EN_BIT);
/*check length */
- if (nLength <= pDevice->nDMABufferLength)
- nLengthLoop = nLength;
+ if (length <= dev->dma_buffer_length)
+ length_loop = length;
else
- nLengthLoop = pDevice->nDMABufferLength;
+ length_loop = dev->dma_buffer_length;
/*The length is always a multiple of the block size */
- nbBlocksLoop = nLengthLoop / AES_BLOCK_SIZE;
+ nb_blocksLoop = length_loop / AES_BLOCK_SIZE;
/*
- *Copy the data from the input buffer into a preallocated
- *buffer which is aligned on the beginning of a page.
- *This may prevent potential issues when flushing/invalidating
- *the buffer as the cache lines are 64 bytes long.
+ * Copy the data from the user input buffer into a preallocated
+ * buffer which has correct properties from efficient DMA
+ * transfers.
*/
- memcpy(pDevice->pDMABuffer, pSrc, nLengthLoop);
+ if (!is_kernel) {
+ if (copy_from_user(
+ dev->dma_buffer, src, length_loop)) {
+ omap_free_dma(dma_ch0);
+ omap_free_dma(dma_ch1);
+ mutex_unlock(&dev->sm.dma_mutex);
+ return false;
+ }
+ } else {
+ memcpy(dev->dma_buffer, src, length_loop);
+ }
/*DMA1: Mem -> AES */
- scxPublicSetDMAChannelCommonParams(&ch0_parameters,
- nbBlocksLoop,
+ tf_dma_set_channel_common_params(&ch0_parameters,
+ nb_blocksLoop,
DMA_CEN_Elts_per_Frame_AES,
AES1_REGS_HW_ADDR + 0x60,
- (u32)pDevice->pDMABufferPhys,
+ (u32)dev->dma_buffer_phys,
OMAP44XX_DMA_AES1_P_DATA_IN_REQ);
ch0_parameters.src_amode = OMAP_DMA_AMODE_POST_INC;
ch0_parameters.dst_amode = OMAP_DMA_AMODE_CONSTANT;
ch0_parameters.src_or_dst_synch = OMAP_DMA_DST_SYNC;
- dprintk(KERN_INFO "PDrvCryptoUpdateAESWithDMA: \
- scxPublicDMASetParams(ch0)\n");
- scxPublicDMASetParams(dma_ch0, &ch0_parameters);
+ dprintk(KERN_INFO "%s: omap_set_dma_params(ch0)\n", __func__);
+ omap_set_dma_params(dma_ch0, &ch0_parameters);
- omap_set_dma_src_burst_mode(dma_ch0, OMAP_DMA_DATA_BURST_16);
- omap_set_dma_dest_burst_mode(dma_ch0, OMAP_DMA_DATA_BURST_16);
+ omap_set_dma_src_burst_mode(dma_ch0, OMAP_DMA_DATA_BURST_8);
+ omap_set_dma_dest_burst_mode(dma_ch0, OMAP_DMA_DATA_BURST_8);
+ omap_set_dma_src_data_pack(dma_ch0, 1);
/*DMA2: AES -> Mem */
- scxPublicSetDMAChannelCommonParams(&ch1_parameters,
- nbBlocksLoop,
+ tf_dma_set_channel_common_params(&ch1_parameters,
+ nb_blocksLoop,
DMA_CEN_Elts_per_Frame_AES,
- (u32)pDevice->pDMABufferPhys,
+ (u32)dev->dma_buffer_phys,
AES1_REGS_HW_ADDR + 0x60,
OMAP44XX_DMA_AES1_P_DATA_OUT_REQ);
@@ -467,36 +504,36 @@ static void PDrvCryptoUpdateAESWithDMA(u8 *pSrc, u8 *pDest, u32 nbBlocks)
ch1_parameters.dst_amode = OMAP_DMA_AMODE_POST_INC;
ch1_parameters.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
- dprintk(KERN_INFO "PDrvCryptoUpdateAESWithDMA: \
- scxPublicDMASetParams(ch1)\n");
- scxPublicDMASetParams(dma_ch1, &ch1_parameters);
+ dprintk(KERN_INFO "%s: omap_set_dma_params(ch1)\n", __func__);
+ omap_set_dma_params(dma_ch1, &ch1_parameters);
- omap_set_dma_src_burst_mode(dma_ch1, OMAP_DMA_DATA_BURST_16);
- omap_set_dma_dest_burst_mode(dma_ch1, OMAP_DMA_DATA_BURST_16);
+ omap_set_dma_src_burst_mode(dma_ch1, OMAP_DMA_DATA_BURST_8);
+ omap_set_dma_dest_burst_mode(dma_ch1, OMAP_DMA_DATA_BURST_8);
+ omap_set_dma_dest_data_pack(dma_ch1, 1);
wmb();
dprintk(KERN_INFO
- "PDrvCryptoUpdateAESWithDMA: Start DMA channel %d\n",
- (unsigned int)dma_ch1);
- scxPublicDMAStart(dma_ch1, OMAP_DMA_BLOCK_IRQ);
+ "%s: Start DMA channel %d\n",
+ __func__, (unsigned int)dma_ch1);
+ tf_dma_start(dma_ch1, OMAP_DMA_BLOCK_IRQ);
dprintk(KERN_INFO
- "PDrvCryptoUpdateAESWithDMA: Start DMA channel %d\n",
- (unsigned int)dma_ch0);
- scxPublicDMAStart(dma_ch0, OMAP_DMA_BLOCK_IRQ);
+ "%s: Start DMA channel %d\n",
+ __func__, (unsigned int)dma_ch0);
+ tf_dma_start(dma_ch0, OMAP_DMA_BLOCK_IRQ);
dprintk(KERN_INFO
- "PDrvCryptoUpdateAESWithDMA: Waiting for IRQ\n");
- scxPublicDMAWait(2);
+ "%s: Waiting for IRQ\n", __func__);
+ tf_dma_wait(2);
/*Unset DMA synchronisation requests */
- OUTREG32(&pAESReg_t->AES_SYSCONFIG,
- INREG32(&pAESReg_t->AES_SYSCONFIG)
+ OUTREG32(&paes_reg->AES_SYSCONFIG,
+ INREG32(&paes_reg->AES_SYSCONFIG)
& (~AES_SYSCONFIG_DMA_REQ_OUT_EN_BIT)
& (~AES_SYSCONFIG_DMA_REQ_IN_EN_BIT));
- scxPublicDMAClearChannel(dma_ch0);
- scxPublicDMAClearChannel(dma_ch1);
+ omap_clear_dma(dma_ch0);
+ omap_clear_dma(dma_ch1);
/*
*The dma transfer is complete
@@ -504,23 +541,35 @@ static void PDrvCryptoUpdateAESWithDMA(u8 *pSrc, u8 *pDest, u32 nbBlocks)
/*The DMA output is in the preallocated aligned buffer
*and needs to be copied to the output buffer.*/
- memcpy(pDest, pDevice->pDMABuffer, nLengthLoop);
+ if (!is_kernel) {
+ if (copy_to_user(
+ dest, dev->dma_buffer, length_loop)) {
+ omap_free_dma(dma_ch0);
+ omap_free_dma(dma_ch1);
+ mutex_unlock(&dev->sm.dma_mutex);
+ return false;
+ }
+ } else {
+ memcpy(dest, dev->dma_buffer, length_loop);
+ }
- pSrc += nLengthLoop;
- pDest += nLengthLoop;
- nLength -= nLengthLoop;
+ src += length_loop;
+ dest += length_loop;
+ length -= length_loop;
}
/*For safety reasons, let's clean the working buffer */
- memset(pDevice->pDMABuffer, 0, nLengthLoop);
+ memset(dev->dma_buffer, 0, length_loop);
/*release the DMA */
- scxPublicDMARelease(dma_ch0);
- scxPublicDMARelease(dma_ch1);
+ omap_free_dma(dma_ch0);
+ omap_free_dma(dma_ch1);
+
+ mutex_unlock(&dev->sm.dma_mutex);
- mutex_unlock(&pDevice->sm.sDMALock);
+ dprintk(KERN_INFO "%s: Success\n", __func__);
- dprintk(KERN_INFO "PDrvCryptoUpdateAESWithDMA: Success\n");
+ return true;
}
#ifdef CONFIG_SMC_KERNEL_CRYPTO
@@ -579,7 +628,7 @@ static int aes_dma_start(struct aes_hwa_ctx *ctx)
size_t count;
dma_addr_t addr_in, addr_out;
struct omap_dma_channel_params dma_params;
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *state =
+ struct tf_crypto_aes_operation_state *state =
crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(ctx->req));
if (sg_is_last(ctx->in_sg) && sg_is_last(ctx->out_sg)) {
@@ -622,14 +671,14 @@ static int aes_dma_start(struct aes_hwa_ctx *ctx)
ctx->total -= count;
- PDrvCryptoLockUnlockHWA(PUBLIC_CRYPTO_HWA_AES1, LOCK_HWA);
+ tf_crypto_lock_hwa(PUBLIC_CRYPTO_HWA_AES1, LOCK_HWA);
/* Configure HWA */
- SCXPublicCryptoEnableClock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
+ tf_crypto_enable_clock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
- PDrvCryptoRestoreAESRegisters(state);
+ tf_aes_restore_registers(state);
- OUTREG32(&pAESReg_t->AES_SYSCONFIG, INREG32(&pAESReg_t->AES_SYSCONFIG)
+ OUTREG32(&paes_reg->AES_SYSCONFIG, INREG32(&paes_reg->AES_SYSCONFIG)
| AES_SYSCONFIG_DMA_REQ_OUT_EN_BIT
| AES_SYSCONFIG_DMA_REQ_IN_EN_BIT);
@@ -657,8 +706,9 @@ static int aes_dma_start(struct aes_hwa_ctx *ctx)
omap_set_dma_params(ctx->dma_lch_in, &dma_params);
- omap_set_dma_dest_burst_mode(ctx->dma_lch_in, OMAP_DMA_DATA_BURST_16);
- omap_set_dma_src_burst_mode(ctx->dma_lch_in, OMAP_DMA_DATA_BURST_16);
+ omap_set_dma_dest_burst_mode(ctx->dma_lch_in, OMAP_DMA_DATA_BURST_8);
+ omap_set_dma_src_burst_mode(ctx->dma_lch_in, OMAP_DMA_DATA_BURST_8);
+ omap_set_dma_src_data_pack(ctx->dma_lch_in, 1);
/* OUT */
dma_params.trigger = ctx->dma_out;
@@ -670,8 +720,9 @@ static int aes_dma_start(struct aes_hwa_ctx *ctx)
omap_set_dma_params(ctx->dma_lch_out, &dma_params);
- omap_set_dma_dest_burst_mode(ctx->dma_lch_out, OMAP_DMA_DATA_BURST_16);
- omap_set_dma_src_burst_mode(ctx->dma_lch_out, OMAP_DMA_DATA_BURST_16);
+ omap_set_dma_dest_burst_mode(ctx->dma_lch_out, OMAP_DMA_DATA_BURST_8);
+ omap_set_dma_src_burst_mode(ctx->dma_lch_out, OMAP_DMA_DATA_BURST_8);
+ omap_set_dma_dest_data_pack(ctx->dma_lch_out, 1);
/* Is this really needed? */
omap_disable_dma_irq(ctx->dma_lch_in, OMAP_DMA_DROP_IRQ);
@@ -689,14 +740,14 @@ static int aes_dma_start(struct aes_hwa_ctx *ctx)
static int aes_dma_stop(struct aes_hwa_ctx *ctx)
{
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *state =
+ struct tf_crypto_aes_operation_state *state =
crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(ctx->req));
int err = 0;
size_t count;
dprintk(KERN_INFO "aes_dma_stop(%p)\n", ctx);
- PDrvCryptoSaveAESRegisters(state);
+ tf_aes_save_registers(state);
if (!AES_CTRL_IS_MODE_ECB(state->CTRL)) {
u32 *ptr = (u32 *) ctx->req->info;
@@ -707,11 +758,11 @@ static int aes_dma_stop(struct aes_hwa_ctx *ctx)
ptr[3] = state->AES_IV_3;
}
- OUTREG32(&pAESReg_t->AES_SYSCONFIG, 0);
+ OUTREG32(&paes_reg->AES_SYSCONFIG, 0);
- SCXPublicCryptoDisableClock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
+ tf_crypto_disable_clock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
- PDrvCryptoLockUnlockHWA(PUBLIC_CRYPTO_HWA_AES1, UNLOCK_HWA);
+ tf_crypto_lock_hwa(PUBLIC_CRYPTO_HWA_AES1, UNLOCK_HWA);
omap_stop_dma(ctx->dma_lch_in);
omap_stop_dma(ctx->dma_lch_out);
@@ -812,7 +863,7 @@ static void aes_dma_cleanup(struct aes_hwa_ctx *ctx)
static int aes_handle_req(struct aes_hwa_ctx *ctx)
{
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *state;
+ struct tf_crypto_aes_operation_state *state;
struct crypto_async_request *async_req, *backlog;
struct ablkcipher_request *req;
unsigned long flags;
@@ -866,7 +917,7 @@ static void aes_tasklet(unsigned long data)
}
/* Generic */
-static int aes_setkey(struct PUBLIC_CRYPTO_AES_OPERATION_STATE *state,
+static int aes_setkey(struct tf_crypto_aes_operation_state *state,
const u8 *key, unsigned int keylen)
{
u32 *ptr = (u32 *)key;
@@ -908,6 +959,9 @@ static int aes_operate(struct ablkcipher_request *req)
unsigned long flags;
int err;
+ /* Make sure AES HWA is accessible */
+ tf_delayed_secure_resume();
+
spin_lock_irqsave(&aes_ctx->lock, flags);
err = ablkcipher_enqueue_request(&aes_ctx->queue, req);
spin_unlock_irqrestore(&aes_ctx->lock, flags);
@@ -920,7 +974,7 @@ static int aes_operate(struct ablkcipher_request *req)
static int aes_encrypt(struct ablkcipher_request *req)
{
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *state =
+ struct tf_crypto_aes_operation_state *state =
crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
state->CTRL |= AES_CTRL_DIRECTION_ENCRYPT;
@@ -930,7 +984,7 @@ static int aes_encrypt(struct ablkcipher_request *req)
static int aes_decrypt(struct ablkcipher_request *req)
{
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *state =
+ struct tf_crypto_aes_operation_state *state =
crypto_ablkcipher_ctx(crypto_ablkcipher_reqtfm(req));
state->CTRL &= ~(AES_CTRL_DIRECTION_ENCRYPT);
@@ -939,53 +993,110 @@ static int aes_decrypt(struct ablkcipher_request *req)
return aes_operate(req);
}
-static int aes_single_setkey(struct crypto_tfm *tfm, const u8 *key,
- unsigned int keylen)
+static int aes_sync_operate(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
{
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *state = crypto_tfm_ctx(tfm);
+ struct crypto_tfm *tfm = crypto_blkcipher_tfm(desc->tfm);
+ struct tf_crypto_aes_operation_state *state = crypto_tfm_ctx(tfm);
+ struct blkcipher_walk walk;
+ int err;
- state->CTRL = AES_CTRL_MODE_ECB_BIT;
+ if (nbytes % AES_BLOCK_SIZE)
+ return -EINVAL;
- return aes_setkey(state, key, keylen);
+ /* Make sure AES HWA is accessible */
+ tf_delayed_secure_resume();
+
+ tf_crypto_lock_hwa(PUBLIC_CRYPTO_HWA_AES1, LOCK_HWA);
+ tf_crypto_enable_clock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
+
+ blkcipher_walk_init(&walk, dst, src, nbytes);
+ err = blkcipher_walk_virt(desc, &walk);
+
+ if (!AES_CTRL_IS_MODE_ECB(state->CTRL)) {
+ u32 *ptr = (u32 *) walk.iv;
+
+ state->AES_IV_0 = ptr[0];
+ state->AES_IV_1 = ptr[1];
+ state->AES_IV_2 = ptr[2];
+ state->AES_IV_3 = ptr[3];
+ }
+
+ while ((nbytes = walk.nbytes)) {
+ if (!tf_aes_update(state, walk.src.virt.addr,
+ walk.dst.virt.addr, nbytes / AES_BLOCK_SIZE)) {
+ err = -EINVAL;
+ break;
+ }
+
+ /* tf_aes_update processes all the data */
+ nbytes = 0;
+
+ err = blkcipher_walk_done(desc, &walk, nbytes);
+ }
+
+ if (!AES_CTRL_IS_MODE_ECB(state->CTRL)) {
+ u32 *ptr = (u32 *) walk.iv;
+
+ ptr[0] = state->AES_IV_0;
+ ptr[1] = state->AES_IV_1;
+ ptr[2] = state->AES_IV_2;
+ ptr[3] = state->AES_IV_3;
+ }
+
+ tf_crypto_disable_clock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
+ tf_crypto_lock_hwa(PUBLIC_CRYPTO_HWA_AES1, UNLOCK_HWA);
+
+ return err;
}
-static void aes_single_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
+static int aes_sync_encrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
{
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *state = crypto_tfm_ctx(tfm);
+ struct crypto_tfm *tfm = crypto_blkcipher_tfm(desc->tfm);
+ struct tf_crypto_aes_operation_state *state = crypto_tfm_ctx(tfm);
state->CTRL |= AES_CTRL_DIRECTION_ENCRYPT;
- PDrvCryptoLockUnlockHWA(PUBLIC_CRYPTO_HWA_AES1, LOCK_HWA);
+ dprintk(KERN_INFO "aes_sync_encrypt nbytes=0x%x\n", nbytes);
- SCXPublicCryptoEnableClock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
- PDrvCryptoUpdateAES(state, (u8 *) in, out, 1);
- SCXPublicCryptoDisableClock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
-
- PDrvCryptoLockUnlockHWA(PUBLIC_CRYPTO_HWA_AES1, UNLOCK_HWA);
+ return aes_sync_operate(desc, dst, src, nbytes);
}
-static void aes_single_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
+static int aes_sync_decrypt(struct blkcipher_desc *desc,
+ struct scatterlist *dst, struct scatterlist *src,
+ unsigned int nbytes)
{
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *state =
- crypto_tfm_ctx(tfm);
+ struct crypto_tfm *tfm = crypto_blkcipher_tfm(desc->tfm);
+ struct tf_crypto_aes_operation_state *state = crypto_tfm_ctx(tfm);
state->CTRL &= ~(AES_CTRL_DIRECTION_ENCRYPT);
state->CTRL |= AES_CTRL_DIRECTION_DECRYPT;
- PDrvCryptoLockUnlockHWA(PUBLIC_CRYPTO_HWA_AES1, LOCK_HWA);
-
- SCXPublicCryptoEnableClock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
- PDrvCryptoUpdateAES(state, (u8 *) in, out, 1);
- SCXPublicCryptoDisableClock(PUBLIC_CRYPTO_AES1_CLOCK_REG);
+ dprintk(KERN_INFO "aes_sync_decrypt\n");
- PDrvCryptoLockUnlockHWA(PUBLIC_CRYPTO_HWA_AES1, UNLOCK_HWA);
+ return aes_sync_operate(desc, dst, src, nbytes);
}
/* AES ECB */
+static int aes_ecb_sync_setkey(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct tf_crypto_aes_operation_state *state = crypto_tfm_ctx(tfm);
+
+ state->CTRL = AES_CTRL_MODE_ECB_BIT;
+
+ dprintk(KERN_INFO "aes_ecb_sync_setkey\n");
+
+ return aes_setkey(state, key, keylen);
+}
+
static int aes_ecb_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
unsigned int keylen)
{
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *state =
+ struct tf_crypto_aes_operation_state *state =
crypto_ablkcipher_ctx(tfm);
state->CTRL = AES_CTRL_MODE_ECB_BIT;
@@ -994,10 +1105,22 @@ static int aes_ecb_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
}
/* AES CBC */
+static int aes_cbc_sync_setkey(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct tf_crypto_aes_operation_state *state = crypto_tfm_ctx(tfm);
+
+ state->CTRL = AES_CTRL_MODE_CBC_BIT;
+
+ dprintk(KERN_INFO "aes_cbc_sync_setkey\n");
+
+ return aes_setkey(state, key, keylen);
+}
+
static int aes_cbc_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
unsigned int keylen)
{
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *state =
+ struct tf_crypto_aes_operation_state *state =
crypto_ablkcipher_ctx(tfm);
state->CTRL = AES_CTRL_MODE_CBC_BIT;
@@ -1006,10 +1129,22 @@ static int aes_cbc_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
}
/* AES CTR */
+static int aes_ctr_sync_setkey(struct crypto_tfm *tfm, const u8 *key,
+ unsigned int keylen)
+{
+ struct tf_crypto_aes_operation_state *state = crypto_tfm_ctx(tfm);
+
+ state->CTRL = AES_CTRL_MODE_CTR_BIT;
+
+ dprintk(KERN_INFO "aes_cbc_sync_setkey\n");
+
+ return aes_setkey(state, key, keylen);
+}
+
static int aes_ctr_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
unsigned int keylen)
{
- struct PUBLIC_CRYPTO_AES_OPERATION_STATE *state =
+ struct tf_crypto_aes_operation_state *state =
crypto_ablkcipher_ctx(tfm);
/* Always defaults to 128-bit counter */
@@ -1018,24 +1153,73 @@ static int aes_ctr_setkey(struct crypto_ablkcipher *tfm, const u8 *key,
return aes_setkey(state, key, keylen);
}
-static struct crypto_alg smc_aes_alg = {
- .cra_flags = CRYPTO_ALG_TYPE_CIPHER,
+static struct crypto_alg smc_aes_ecb_sync_alg = {
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
.cra_priority = 999,
- .cra_name = "aes",
- .cra_driver_name = "aes-smc",
+ .cra_name = "ecb(aes)",
+ .cra_driver_name = "aes-ecb-smc",
+ .cra_type = &crypto_blkcipher_type,
.cra_module = THIS_MODULE,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize =
- sizeof(struct PUBLIC_CRYPTO_AES_OPERATION_STATE),
+ sizeof(struct tf_crypto_aes_operation_state),
.cra_alignmask = 3,
- .cra_list = LIST_HEAD_INIT(smc_aes_alg.cra_list),
+ .cra_list = LIST_HEAD_INIT(smc_aes_ecb_sync_alg.cra_list),
.cra_u = {
- .cipher = {
- .cia_min_keysize = AES_MIN_KEY_SIZE,
- .cia_max_keysize = AES_MAX_KEY_SIZE,
- .cia_setkey = aes_single_setkey,
- .cia_encrypt = aes_single_encrypt,
- .cia_decrypt = aes_single_decrypt,
+ .blkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .setkey = aes_ecb_sync_setkey,
+ .encrypt = aes_sync_encrypt,
+ .decrypt = aes_sync_decrypt,
+ }
+ },
+};
+
+static struct crypto_alg smc_aes_cbc_sync_alg = {
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
+ .cra_priority = 999,
+ .cra_name = "cbc(aes)",
+ .cra_driver_name = "aes-cbc-smc",
+ .cra_type = &crypto_blkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize =
+ sizeof(struct tf_crypto_aes_operation_state),
+ .cra_alignmask = 3,
+ .cra_list = LIST_HEAD_INIT(smc_aes_cbc_sync_alg.cra_list),
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = PUBLIC_CRYPTO_IV_MAX_SIZE,
+ .setkey = aes_cbc_sync_setkey,
+ .encrypt = aes_sync_encrypt,
+ .decrypt = aes_sync_decrypt,
+ }
+ },
+};
+
+static struct crypto_alg smc_aes_ctr_sync_alg = {
+ .cra_flags = CRYPTO_ALG_TYPE_BLKCIPHER,
+ .cra_priority = 999,
+ .cra_name = "ctr(aes)",
+ .cra_driver_name = "aes-ctr-smc",
+ .cra_type = &crypto_blkcipher_type,
+ .cra_module = THIS_MODULE,
+ .cra_blocksize = AES_BLOCK_SIZE,
+ .cra_ctxsize =
+ sizeof(struct tf_crypto_aes_operation_state),
+ .cra_alignmask = 3,
+ .cra_list = LIST_HEAD_INIT(smc_aes_ctr_sync_alg.cra_list),
+ .cra_u = {
+ .blkcipher = {
+ .min_keysize = AES_MIN_KEY_SIZE,
+ .max_keysize = AES_MAX_KEY_SIZE,
+ .ivsize = PUBLIC_CRYPTO_IV_MAX_SIZE,
+ .setkey = aes_ctr_sync_setkey,
+ .encrypt = aes_sync_encrypt,
+ .decrypt = aes_sync_decrypt,
}
},
};
@@ -1049,7 +1233,7 @@ static struct crypto_alg smc_aes_ecb_alg = {
.cra_module = THIS_MODULE,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize =
- sizeof(struct PUBLIC_CRYPTO_AES_OPERATION_STATE),
+ sizeof(struct tf_crypto_aes_operation_state),
.cra_alignmask = 3,
.cra_list = LIST_HEAD_INIT(smc_aes_ecb_alg.cra_list),
.cra_u = {
@@ -1072,7 +1256,7 @@ static struct crypto_alg smc_aes_cbc_alg = {
.cra_type = &crypto_ablkcipher_type,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize =
- sizeof(struct PUBLIC_CRYPTO_AES_OPERATION_STATE),
+ sizeof(struct tf_crypto_aes_operation_state),
.cra_alignmask = 3,
.cra_list = LIST_HEAD_INIT(smc_aes_cbc_alg.cra_list),
.cra_u = {
@@ -1096,7 +1280,7 @@ static struct crypto_alg smc_aes_ctr_alg = {
.cra_type = &crypto_ablkcipher_type,
.cra_blocksize = AES_BLOCK_SIZE,
.cra_ctxsize =
- sizeof(struct PUBLIC_CRYPTO_AES_OPERATION_STATE),
+ sizeof(struct tf_crypto_aes_operation_state),
.cra_alignmask = 3,
.cra_list = LIST_HEAD_INIT(smc_aes_ctr_alg.cra_list),
.cra_u = {
@@ -1122,7 +1306,7 @@ int register_smc_public_crypto_aes(void)
crypto_init_queue(&aes_ctx->queue, 1);
tasklet_init(&aes_ctx->task, aes_tasklet, (unsigned long)aes_ctx);
- spin_lock_init(&aes_ctx->lock);
+ spin_lock_init(&aes_ctx->lock);
aes_ctx->dma_in = OMAP44XX_DMA_AES1_P_DATA_IN_REQ;
aes_ctx->dma_out = OMAP44XX_DMA_AES1_P_DATA_OUT_REQ;
@@ -1131,9 +1315,17 @@ int register_smc_public_crypto_aes(void)
if (ret)
goto err_dma;
- ret = crypto_register_alg(&smc_aes_alg);
+ ret = crypto_register_alg(&smc_aes_ecb_sync_alg);
if (ret)
- goto err_dma;
+ goto err_ecb_sync;
+
+ ret = crypto_register_alg(&smc_aes_cbc_sync_alg);
+ if (ret)
+ goto err_cbc_sync;
+
+ ret = crypto_register_alg(&smc_aes_ctr_sync_alg);
+ if (ret)
+ goto err_ctr_sync;
ret = crypto_register_alg(&smc_aes_ecb_alg);
if (ret)
@@ -1154,7 +1346,13 @@ err_ctr:
err_cbc:
crypto_unregister_alg(&smc_aes_ecb_alg);
err_ecb:
- crypto_unregister_alg(&smc_aes_alg);
+ crypto_unregister_alg(&smc_aes_ctr_sync_alg);
+err_ctr_sync:
+ crypto_unregister_alg(&smc_aes_cbc_sync_alg);
+err_cbc_sync:
+ crypto_unregister_alg(&smc_aes_ecb_sync_alg);
+err_ecb_sync:
+ aes_dma_cleanup(aes_ctx);
err_dma:
tasklet_kill(&aes_ctx->task);
kfree(aes_ctx);
@@ -1166,15 +1364,17 @@ void unregister_smc_public_crypto_aes(void)
if (aes_ctx == NULL)
return;
- crypto_unregister_alg(&smc_aes_alg);
+ crypto_unregister_alg(&smc_aes_ecb_sync_alg);
+ crypto_unregister_alg(&smc_aes_cbc_sync_alg);
+ crypto_unregister_alg(&smc_aes_ctr_sync_alg);
+
crypto_unregister_alg(&smc_aes_ecb_alg);
crypto_unregister_alg(&smc_aes_cbc_alg);
crypto_unregister_alg(&smc_aes_ctr_alg);
- tasklet_kill(&aes_ctx->task);
-
aes_dma_cleanup(aes_ctx);
+ tasklet_kill(&aes_ctx->task);
kfree(aes_ctx);
}
#endif
diff --git a/security/smc/tf_crypto_des.c b/security/smc/tf_crypto_des.c
new file mode 100644
index 0000000..716a60f
--- /dev/null
+++ b/security/smc/tf_crypto_des.c
@@ -0,0 +1,404 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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 "tf_defs.h"
+#include "tf_util.h"
+#include "tf_crypto.h"
+#include "tf_dma.h"
+
+#include <linux/io.h>
+#include <mach/io.h>
+
+/*
+ * DES Hardware Accelerator: Base address
+ */
+#define DES_REGS_HW_ADDR 0x480A5000
+
+/*
+ * CTRL register Masks
+ */
+#define DES_CTRL_OUTPUT_READY_BIT (1<<0)
+#define DES_CTRL_INPUT_READY_BIT (1<<1)
+
+#define DES_CTRL_GET_DIRECTION(x) (x&4)
+#define DES_CTRL_DIRECTION_DECRYPT 0
+#define DES_CTRL_DIRECTION_ENCRYPT (1<<2)
+
+#define DES_CTRL_GET_TDES(x) (x&8)
+#define DES_CTRL_TDES_DES 0
+#define DES_CTRL_TDES_TRIPLE_DES (1<<3)
+
+#define DES_CTRL_GET_MODE(x) (x&0x10)
+#define DES_CTRL_MODE_ECB 0
+#define DES_CTRL_MODE_CBC (1<<4)
+
+/*
+ * SYSCONFIG register masks
+ */
+#define DES_SYSCONFIG_DMA_REQ_IN_EN_BIT (1<<5)
+#define DES_SYSCONFIG_DMA_REQ_OUT_EN_BIT (1<<6)
+
+/*------------------------------------------------------------------------*/
+/* DES/DES3 Context */
+/*------------------------------------------------------------------------*/
+/**
+ * This structure contains the registers of the DES HW accelerator.
+ */
+struct des3_des_reg {
+ u32 DES_KEY3_L; /* DES Key 3 Low Register */
+ u32 DES_KEY3_H; /* DES Key 3 High Register */
+ u32 DES_KEY2_L; /* DES Key 2 Low Register */
+ u32 DES_KEY2_H; /* DES Key 2 High Register */
+ u32 DES_KEY1_L; /* DES Key 1 Low Register */
+ u32 DES_KEY1_H; /* DES Key 1 High Register */
+ u32 DES_IV_L; /* DES Initialization Vector Low Reg */
+ u32 DES_IV_H; /* DES Initialization Vector High Reg */
+ u32 DES_CTRL; /* DES Control Register */
+ u32 DES_LENGTH; /* DES Length Register */
+ u32 DES_DATA_L; /* DES Data Input/Output Low Register */
+ u32 DES_DATA_H; /* DES Data Input/Output High Register */
+ u32 DES_REV; /* DES Revision Register */
+ u32 DES_SYSCONFIG; /* DES Mask and Reset Register */
+ u32 DES_SYSSTATUS; /* DES System Status Register */
+};
+
+static struct des3_des_reg *des_reg;
+
+/*------------------------------------------------------------------------
+ *Forward declarations
+ *------------------------------------------------------------------------ */
+
+static bool tf_des_update_dma(u8 *src, u8 *dest, u32 nb_blocks);
+
+/*-------------------------------------------------------------------------
+ *Save HWA registers into the specified operation state structure
+ *-------------------------------------------------------------------------*/
+static void tf_des_save_registers(u32 DES_CTRL,
+ struct tf_crypto_des_operation_state *des_state)
+{
+ dprintk(KERN_INFO
+ "tf_des_save_registers in des_state=%p CTRL=0x%08x\n",
+ des_state, DES_CTRL);
+
+ /*Save the IV if we are in CBC mode */
+ if (DES_CTRL_GET_MODE(DES_CTRL) == DES_CTRL_MODE_CBC) {
+ des_state->DES_IV_L = INREG32(&des_reg->DES_IV_L);
+ des_state->DES_IV_H = INREG32(&des_reg->DES_IV_H);
+ }
+}
+
+/*-------------------------------------------------------------------------
+ *Restore the HWA registers from the operation state structure
+ *-------------------------------------------------------------------------*/
+static void tf_des_restore_registers(u32 DES_CTRL,
+ struct tf_crypto_des_operation_state *des_state)
+{
+ dprintk(KERN_INFO "tf_des_restore_registers from "
+ "des_state=%p CTRL=0x%08x\n",
+ des_state, DES_CTRL);
+
+ /*Write the IV ctx->reg */
+ if (DES_CTRL_GET_MODE(DES_CTRL) == DES_CTRL_MODE_CBC) {
+ OUTREG32(&des_reg->DES_IV_L, des_state->DES_IV_L);
+ OUTREG32(&des_reg->DES_IV_H, des_state->DES_IV_H);
+ }
+
+ /*Set the DIRECTION and CBC bits in the CTRL register.
+ *Keep the TDES from the accelerator */
+ OUTREG32(&des_reg->DES_CTRL,
+ (INREG32(&des_reg->DES_CTRL) & (1 << 3)) |
+ (DES_CTRL & ((1 << 2) | (1 << 4))));
+
+ /*Set the SYSCONFIG register to 0 */
+ OUTREG32(&des_reg->DES_SYSCONFIG, 0);
+}
+
+/*------------------------------------------------------------------------- */
+
+void tf_des_init(void)
+{
+ des_reg = omap_ioremap(DES_REGS_HW_ADDR, SZ_1M, MT_DEVICE);
+ if (des_reg == NULL)
+ panic("Unable to remap DES/3DES module");
+}
+
+void tf_des_exit(void)
+{
+ omap_iounmap(des_reg);
+}
+
+bool tf_des_update(u32 DES_CTRL,
+ struct tf_crypto_des_operation_state *des_state,
+ u8 *src, u8 *dest, u32 nb_blocks)
+{
+ u32 nbr_of_blocks;
+ u32 temp;
+ u8 *process_src;
+ u8 *process_dest;
+ u32 dma_use = PUBLIC_CRYPTO_DMA_USE_NONE;
+
+ /*
+ *Choice of the processing type
+ */
+ if (nb_blocks * DES_BLOCK_SIZE >= DMA_TRIGGER_IRQ_DES)
+ dma_use = PUBLIC_CRYPTO_DMA_USE_IRQ;
+
+ dprintk(KERN_INFO "tf_des_update: "
+ "src=0x%08x, dest=0x%08x, nb_blocks=0x%08x, dma_use=0x%08x\n",
+ (unsigned int)src, (unsigned int)dest,
+ (unsigned int)nb_blocks, (unsigned int)dma_use);
+
+ if (nb_blocks == 0) {
+ dprintk(KERN_INFO "tf_des_update: Nothing to process\n");
+ return true;
+ }
+
+ if (DES_CTRL_GET_DIRECTION(INREG32(&des_reg->DES_CTRL)) !=
+ DES_CTRL_GET_DIRECTION(DES_CTRL)) {
+ dprintk(KERN_WARNING "HWA configured for another direction\n");
+ return false;
+ }
+
+ /*Restore the registers of the accelerator from the operation state */
+ tf_des_restore_registers(DES_CTRL, des_state);
+
+ OUTREG32(&des_reg->DES_LENGTH, nb_blocks * DES_BLOCK_SIZE);
+
+ if (dma_use == PUBLIC_CRYPTO_DMA_USE_IRQ) {
+
+ /*perform the update with DMA */
+ if (!tf_des_update_dma(src, dest, nb_blocks))
+ return false;
+
+ } else {
+ u8 buf[DMA_TRIGGER_IRQ_DES];
+
+ process_src = process_dest = buf;
+
+ if (copy_from_user(buf, src, nb_blocks * DES_BLOCK_SIZE))
+ return false;
+
+ for (nbr_of_blocks = 0;
+ nbr_of_blocks < nb_blocks; nbr_of_blocks++) {
+
+ /*We wait for the input ready */
+ /*Crash the system as this should never occur */
+ if (tf_crypto_wait_for_ready_bit(
+ (u32 *)&des_reg->DES_CTRL,
+ DES_CTRL_INPUT_READY_BIT) !=
+ PUBLIC_CRYPTO_OPERATION_SUCCESS) {
+ panic("Wait too long for DES HW "
+ "accelerator Input data to be ready\n");
+ }
+
+ /*We copy the 8 bytes of data src->reg */
+ temp = (u32) BYTES_TO_LONG(process_src);
+ OUTREG32(&des_reg->DES_DATA_L, temp);
+ process_src += 4;
+ temp = (u32) BYTES_TO_LONG(process_src);
+ OUTREG32(&des_reg->DES_DATA_H, temp);
+ process_src += 4;
+
+ /*We wait for the output ready */
+ tf_crypto_wait_for_ready_bit_infinitely(
+ (u32 *)&des_reg->DES_CTRL,
+ DES_CTRL_OUTPUT_READY_BIT);
+
+ /*We copy the 8 bytes of data reg->dest */
+ temp = INREG32(&des_reg->DES_DATA_L);
+ LONG_TO_BYTE(process_dest, temp);
+ process_dest += 4;
+ temp = INREG32(&des_reg->DES_DATA_H);
+ LONG_TO_BYTE(process_dest, temp);
+ process_dest += 4;
+ }
+
+ if (copy_to_user(dest, buf, nb_blocks * DES_BLOCK_SIZE))
+ return false;
+ }
+
+ /*Save the accelerator registers into the operation state */
+ tf_des_save_registers(DES_CTRL, des_state);
+
+ dprintk(KERN_INFO "tf_des_update: Done\n");
+ return true;
+}
+
+/*------------------------------------------------------------------------- */
+/*
+ *Static function, perform DES encryption/decryption using the DMA for data
+ *transfer.
+ *
+ *inputs: src : pointer of the input data to process
+ * nb_blocks : number of block to process
+ * dma_use : PUBLIC_CRYPTO_DMA_USE_IRQ (use irq to monitor end of DMA)
+ *output: dest : pointer of the output data (can be eq to src)
+ */
+static bool tf_des_update_dma(u8 *src, u8 *dest, u32 nb_blocks)
+{
+ /*
+ *Note: The DMA only sees physical addresses !
+ */
+
+ int dma_ch0;
+ int dma_ch1;
+ struct omap_dma_channel_params ch0_parameters;
+ struct omap_dma_channel_params ch1_parameters;
+ u32 length = nb_blocks * DES_BLOCK_SIZE;
+ u32 length_loop = 0;
+ u32 nb_blocksLoop = 0;
+ struct tf_device *dev = tf_get_device();
+
+ dprintk(KERN_INFO
+ "tf_des_update_dma: In=0x%08x, Out=0x%08x, Len=%u\n",
+ (unsigned int)src, (unsigned int)dest,
+ (unsigned int)length);
+
+ /*lock the DMA */
+ mutex_lock(&dev->sm.dma_mutex);
+
+ if (tf_dma_request(&dma_ch0) != PUBLIC_CRYPTO_OPERATION_SUCCESS) {
+ mutex_unlock(&dev->sm.dma_mutex);
+ return false;
+ }
+ if (tf_dma_request(&dma_ch1) != PUBLIC_CRYPTO_OPERATION_SUCCESS) {
+ omap_free_dma(dma_ch0);
+ mutex_unlock(&dev->sm.dma_mutex);
+ return false;
+ }
+
+ while (length > 0) {
+
+ /*
+ * At this time, we are sure that the DMAchannels are available
+ * and not used by other public crypto operation
+ */
+
+ /*DMA used for Input and Output */
+ OUTREG32(&des_reg->DES_SYSCONFIG,
+ INREG32(&des_reg->DES_SYSCONFIG)
+ | DES_SYSCONFIG_DMA_REQ_OUT_EN_BIT
+ | DES_SYSCONFIG_DMA_REQ_IN_EN_BIT);
+
+ /* Check length */
+ if (length <= dev->dma_buffer_length)
+ length_loop = length;
+ else
+ length_loop = dev->dma_buffer_length;
+
+ /* The length is always a multiple of the block size */
+ nb_blocksLoop = length_loop / DES_BLOCK_SIZE;
+
+ /*
+ * Copy the data from the user input buffer into a preallocated
+ * buffer which has correct properties from efficient DMA
+ * transfers.
+ */
+ if (copy_from_user(dev->dma_buffer, src, length_loop)) {
+ omap_free_dma(dma_ch0);
+ omap_free_dma(dma_ch1);
+ mutex_unlock(&dev->sm.dma_mutex);
+ return false;
+ }
+
+ /* DMA1: Mem -> DES */
+ tf_dma_set_channel_common_params(&ch0_parameters,
+ nb_blocksLoop,
+ DMA_CEN_Elts_per_Frame_DES,
+ DES_REGS_HW_ADDR + 0x28,
+ dev->dma_buffer_phys,
+ OMAP44XX_DMA_DES_P_DATA_IN_REQ);
+
+ ch0_parameters.src_amode = OMAP_DMA_AMODE_POST_INC;
+ ch0_parameters.dst_amode = OMAP_DMA_AMODE_CONSTANT;
+ ch0_parameters.src_or_dst_synch = OMAP_DMA_DST_SYNC;
+
+ dprintk(KERN_INFO
+ "tf_des_update_dma: omap_set_dma_params(ch0)\n");
+ omap_set_dma_params(dma_ch0, &ch0_parameters);
+
+ /* DMA2: DES -> Mem */
+ tf_dma_set_channel_common_params(&ch1_parameters,
+ nb_blocksLoop,
+ DMA_CEN_Elts_per_Frame_DES,
+ dev->dma_buffer_phys,
+ DES_REGS_HW_ADDR + 0x28,
+ OMAP44XX_DMA_DES_P_DATA_OUT_REQ);
+
+ ch1_parameters.src_amode = OMAP_DMA_AMODE_CONSTANT;
+ ch1_parameters.dst_amode = OMAP_DMA_AMODE_POST_INC;
+ ch1_parameters.src_or_dst_synch = OMAP_DMA_SRC_SYNC;
+
+ dprintk(KERN_INFO "tf_des_update_dma: "
+ "omap_set_dma_params(ch1)\n");
+ omap_set_dma_params(dma_ch1, &ch1_parameters);
+
+ wmb();
+
+ dprintk(KERN_INFO
+ "tf_des_update_dma: Start DMA channel %d\n",
+ (unsigned int)dma_ch0);
+ tf_dma_start(dma_ch0, OMAP_DMA_BLOCK_IRQ);
+
+ dprintk(KERN_INFO
+ "tf_des_update_dma: Start DMA channel %d\n",
+ (unsigned int)dma_ch1);
+ tf_dma_start(dma_ch1, OMAP_DMA_BLOCK_IRQ);
+ tf_dma_wait(2);
+
+ /* Unset DMA synchronisation requests */
+ OUTREG32(&des_reg->DES_SYSCONFIG,
+ INREG32(&des_reg->DES_SYSCONFIG)
+ & (~DES_SYSCONFIG_DMA_REQ_OUT_EN_BIT)
+ & (~DES_SYSCONFIG_DMA_REQ_IN_EN_BIT));
+
+ omap_clear_dma(dma_ch0);
+ omap_clear_dma(dma_ch1);
+
+ /*
+ * The dma transfer is complete
+ */
+
+ /*The DMA output is in the preallocated aligned buffer
+ *and needs to be copied to the output buffer.*/
+ if (copy_to_user(dest, dev->dma_buffer, length_loop)) {
+ omap_free_dma(dma_ch0);
+ omap_free_dma(dma_ch1);
+ mutex_unlock(&dev->sm.dma_mutex);
+ return false;
+ }
+
+ src += length_loop;
+ dest += length_loop;
+ length -= length_loop;
+ }
+
+ /* For safety reasons, let's clean the working buffer */
+ memset(dev->dma_buffer, 0, length_loop);
+
+ /* Release the DMA */
+ omap_free_dma(dma_ch0);
+ omap_free_dma(dma_ch1);
+
+ mutex_unlock(&dev->sm.dma_mutex);
+
+ dprintk(KERN_INFO "tf_des_update_dma: Success\n");
+
+ return true;
+}
diff --git a/security/smc/omap4/scx_public_crypto_Digest.c b/security/smc/tf_crypto_digest.c
index 7a40089..d69a97f 100644
--- a/security/smc/omap4/scx_public_crypto_Digest.c
+++ b/security/smc/tf_crypto_digest.c
@@ -1,5 +1,5 @@
-/*
- * Copyright (c) 2006-2010 Trusted Logic S.A.
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
@@ -17,11 +17,11 @@
* MA 02111-1307 USA
*/
-#include "scxlnx_defs.h"
-#include "scxlnx_util.h"
-#include "scx_public_crypto.h"
-#include "scx_public_dma.h"
-#include "scxlnx_mshield.h"
+#include "tf_defs.h"
+#include "tf_util.h"
+#include "tf_crypto.h"
+#include "tf_dma.h"
+#include "tf_zebra.h"
#include <linux/io.h>
#include <mach/io.h>
@@ -65,7 +65,7 @@
/**
* This structure contains the registers of the SHA1/MD5 HW accelerator.
*/
-struct Sha1Md5Reg_t {
+struct sha1_md5_reg {
u32 ODIGEST_A; /* 0x00 Outer Digest A */
u32 ODIGEST_B; /* 0x04 Outer Digest B */
u32 ODIGEST_C; /* 0x08 Outer Digest C */
@@ -117,7 +117,7 @@ struct Sha1Md5Reg_t {
u32 IRQENABLE; /* 0x11C IRQ Enable */
};
-static struct Sha1Md5Reg_t *pSha1Md5Reg_t;
+static struct sha1_md5_reg *sha1_md5_reg;
static const u8 md5OverEmptyString[] = {
0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04,
@@ -148,111 +148,112 @@ static const u8 sha256OverEmptyString[] = {
*Forward declarations
*------------------------------------------------------------------------- */
-static void static_Hash_HwPerform64bDigest(u32 *pData,
- u32 nAlgo, u32 nBytesProcessed);
-static void static_Hash_HwPerformDmaDigest(u8 *pData, u32 nDataLength,
- u32 nAlgo, u32 nBytesProcessed);
+static void tf_digest_hw_perform_64b(u32 *data,
+ u32 algo, u32 bytes_processed);
+static bool tf_digest_hw_perform_dma(u8 *data, u32 nDataLength,
+ u32 algo, u32 bytes_processed);
-static void PDrvCryptoUpdateHashWithDMA(
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *pSHAState,
- u8 *pData, u32 dataLength);
+static bool tf_digest_update_dma(
+ struct tf_crypto_sha_operation_state *sha_state,
+ u8 *data, u32 data_length);
/*-------------------------------------------------------------------------
*Save HWA registers into the specified operation state structure
*------------------------------------------------------------------------*/
-static void PDrvCryptoSaveHashRegisters(
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *pSHAState)
+static void tf_digest_save_registers(
+ struct tf_crypto_sha_operation_state *sha_state)
{
- dprintk(KERN_INFO "PDrvCryptoSaveHashRegisters: State=%p\n",
- pSHAState);
-
- pSHAState->SHA_DIGEST_A = INREG32(&pSha1Md5Reg_t->IDIGEST_A);
- pSHAState->SHA_DIGEST_B = INREG32(&pSha1Md5Reg_t->IDIGEST_B);
- pSHAState->SHA_DIGEST_C = INREG32(&pSha1Md5Reg_t->IDIGEST_C);
- pSHAState->SHA_DIGEST_D = INREG32(&pSha1Md5Reg_t->IDIGEST_D);
- pSHAState->SHA_DIGEST_E = INREG32(&pSha1Md5Reg_t->IDIGEST_E);
- pSHAState->SHA_DIGEST_F = INREG32(&pSha1Md5Reg_t->IDIGEST_F);
- pSHAState->SHA_DIGEST_G = INREG32(&pSha1Md5Reg_t->IDIGEST_G);
- pSHAState->SHA_DIGEST_H = INREG32(&pSha1Md5Reg_t->IDIGEST_H);
+ dprintk(KERN_INFO "tf_digest_save_registers: State=%p\n",
+ sha_state);
+
+ sha_state->SHA_DIGEST_A = INREG32(&sha1_md5_reg->IDIGEST_A);
+ sha_state->SHA_DIGEST_B = INREG32(&sha1_md5_reg->IDIGEST_B);
+ sha_state->SHA_DIGEST_C = INREG32(&sha1_md5_reg->IDIGEST_C);
+ sha_state->SHA_DIGEST_D = INREG32(&sha1_md5_reg->IDIGEST_D);
+ sha_state->SHA_DIGEST_E = INREG32(&sha1_md5_reg->IDIGEST_E);
+ sha_state->SHA_DIGEST_F = INREG32(&sha1_md5_reg->IDIGEST_F);
+ sha_state->SHA_DIGEST_G = INREG32(&sha1_md5_reg->IDIGEST_G);
+ sha_state->SHA_DIGEST_H = INREG32(&sha1_md5_reg->IDIGEST_H);
}
/*-------------------------------------------------------------------------
*Restore the HWA registers from the operation state structure
*-------------------------------------------------------------------------*/
-static void PDrvCryptoRestoreHashRegisters(
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *pSHAState)
+static void tf_digest_restore_registers(
+ struct tf_crypto_sha_operation_state *sha_state)
{
- dprintk(KERN_INFO "PDrvCryptoRestoreHashRegisters: State=%p\n",
- pSHAState);
+ dprintk(KERN_INFO "tf_digest_restore_registers: State=%p\n",
+ sha_state);
- if (pSHAState->nBytesProcessed != 0) {
+ if (sha_state->bytes_processed != 0) {
/*
* Some bytes were already processed. Initialize
* previous digest
*/
- OUTREG32(&pSha1Md5Reg_t->IDIGEST_A, pSHAState->SHA_DIGEST_A);
- OUTREG32(&pSha1Md5Reg_t->IDIGEST_B, pSHAState->SHA_DIGEST_B);
- OUTREG32(&pSha1Md5Reg_t->IDIGEST_C, pSHAState->SHA_DIGEST_C);
- OUTREG32(&pSha1Md5Reg_t->IDIGEST_D, pSHAState->SHA_DIGEST_D);
- OUTREG32(&pSha1Md5Reg_t->IDIGEST_E, pSHAState->SHA_DIGEST_E);
- OUTREG32(&pSha1Md5Reg_t->IDIGEST_F, pSHAState->SHA_DIGEST_F);
- OUTREG32(&pSha1Md5Reg_t->IDIGEST_G, pSHAState->SHA_DIGEST_G);
- OUTREG32(&pSha1Md5Reg_t->IDIGEST_H, pSHAState->SHA_DIGEST_H);
+ OUTREG32(&sha1_md5_reg->IDIGEST_A, sha_state->SHA_DIGEST_A);
+ OUTREG32(&sha1_md5_reg->IDIGEST_B, sha_state->SHA_DIGEST_B);
+ OUTREG32(&sha1_md5_reg->IDIGEST_C, sha_state->SHA_DIGEST_C);
+ OUTREG32(&sha1_md5_reg->IDIGEST_D, sha_state->SHA_DIGEST_D);
+ OUTREG32(&sha1_md5_reg->IDIGEST_E, sha_state->SHA_DIGEST_E);
+ OUTREG32(&sha1_md5_reg->IDIGEST_F, sha_state->SHA_DIGEST_F);
+ OUTREG32(&sha1_md5_reg->IDIGEST_G, sha_state->SHA_DIGEST_G);
+ OUTREG32(&sha1_md5_reg->IDIGEST_H, sha_state->SHA_DIGEST_H);
}
- OUTREG32(&pSha1Md5Reg_t->SYSCONFIG, 0);
+ OUTREG32(&sha1_md5_reg->SYSCONFIG, 0);
}
/*------------------------------------------------------------------------- */
-void PDrvCryptoDigestInit(void)
+void tf_digest_init(void)
{
- pSha1Md5Reg_t = omap_ioremap(DIGEST1_REGS_HW_ADDR, SZ_1M, MT_DEVICE);
- if (pSha1Md5Reg_t == NULL)
+ sha1_md5_reg = omap_ioremap(DIGEST1_REGS_HW_ADDR, SZ_1M, MT_DEVICE);
+ if (sha1_md5_reg == NULL)
panic("Unable to remap SHA2/MD5 module");
}
-void PDrvCryptoDigestExit(void)
+void tf_digest_exit(void)
{
- omap_iounmap(pSha1Md5Reg_t);
+ omap_iounmap(sha1_md5_reg);
}
-void PDrvCryptoUpdateHash(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *pSHAState,
- u8 *pData, u32 dataLength)
+bool tf_digest_update(struct tf_crypto_sha_operation_state *sha_state,
+ u8 *data, u32 data_length)
{
- u32 dmaUse = PUBLIC_CRYPTO_DMA_USE_NONE;
+ u32 dma_use = PUBLIC_CRYPTO_DMA_USE_NONE;
/*
*Choice of the processing type
*/
- if (dataLength >= DMA_TRIGGER_IRQ_DIGEST)
- dmaUse = PUBLIC_CRYPTO_DMA_USE_IRQ;
+ if (data_length >= DMA_TRIGGER_IRQ_DIGEST)
+ dma_use = PUBLIC_CRYPTO_DMA_USE_IRQ;
- dprintk(KERN_INFO "PDrvCryptoUpdateHash : "\
- "Data=0x%08x/%u, Chunk=%u, Processed=%u, dmaUse=0x%08x\n",
- (u32)pData, (u32)dataLength,
- pSHAState->nChunkLength, pSHAState->nBytesProcessed,
- dmaUse);
+ dprintk(KERN_INFO "tf_digest_update : "\
+ "Data=0x%08x/%u, Chunk=%u, Processed=%u, dma_use=0x%08x\n",
+ (u32)data, (u32)data_length,
+ sha_state->chunk_length, sha_state->bytes_processed,
+ dma_use);
- if (dataLength == 0) {
- dprintk(KERN_INFO "PDrvCryptoUpdateHash: "\
+ if (data_length == 0) {
+ dprintk(KERN_INFO "tf_digest_update: "\
"Nothing to process\n");
- return;
+ return true;
}
- if (dmaUse != PUBLIC_CRYPTO_DMA_USE_NONE) {
+ if (dma_use != PUBLIC_CRYPTO_DMA_USE_NONE) {
/*
* Restore the registers of the accelerator from the operation
* state
*/
- PDrvCryptoRestoreHashRegisters(pSHAState);
+ tf_digest_restore_registers(sha_state);
/*perform the updates with DMA */
- PDrvCryptoUpdateHashWithDMA(pSHAState, pData, dataLength);
+ if (!tf_digest_update_dma(sha_state, data, data_length))
+ return false;
/* Save the accelerator registers into the operation state */
- PDrvCryptoSaveHashRegisters(pSHAState);
+ tf_digest_save_registers(sha_state);
} else {
/*Non-DMA transfer */
@@ -264,63 +265,66 @@ void PDrvCryptoUpdateHash(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *pSHAState,
/*Is there any data in the chunk? If yes is it possible to
*make a 64B buffer with the new data passed ? */
- if ((pSHAState->nChunkLength != 0)
- && (pSHAState->nChunkLength + dataLength >=
+ if ((sha_state->chunk_length != 0)
+ && (sha_state->chunk_length + data_length >=
HASH_BLOCK_BYTES_LENGTH)) {
u8 vLengthToComplete =
- HASH_BLOCK_BYTES_LENGTH - pSHAState->nChunkLength;
+ HASH_BLOCK_BYTES_LENGTH - sha_state->chunk_length;
/*So we fill the chunk buffer with the new data to
*complete to 64B */
- memcpy(pSHAState->pChunkBuffer + pSHAState->
- nChunkLength, pData, vLengthToComplete);
+ if (copy_from_user(
+ sha_state->chunk_buffer+sha_state->chunk_length,
+ data,
+ vLengthToComplete))
+ return false;
- if (pSHAState->nChunkLength + dataLength ==
+ if (sha_state->chunk_length + data_length ==
HASH_BLOCK_BYTES_LENGTH) {
/*We'll keep some data for the final */
- pSHAState->nChunkLength =
+ sha_state->chunk_length =
HASH_BLOCK_BYTES_LENGTH;
- dprintk(KERN_INFO "PDrvCryptoUpdateHash: "\
+ dprintk(KERN_INFO "tf_digest_update: "\
"Done: Chunk=%u; Processed=%u\n",
- pSHAState->nChunkLength,
- pSHAState->nBytesProcessed);
- return;
+ sha_state->chunk_length,
+ sha_state->bytes_processed);
+ return true;
}
/*
* Restore the registers of the accelerator from the
* operation state
*/
- PDrvCryptoRestoreHashRegisters(pSHAState);
+ tf_digest_restore_registers(sha_state);
/*Then we send this buffer to the HWA */
- static_Hash_HwPerform64bDigest(
- (u32 *)pSHAState->pChunkBuffer, pSHAState->CTRL,
- pSHAState->nBytesProcessed);
+ tf_digest_hw_perform_64b(
+ (u32 *)sha_state->chunk_buffer, sha_state->CTRL,
+ sha_state->bytes_processed);
/*
* Save the accelerator registers into the operation
* state
*/
- PDrvCryptoSaveHashRegisters(pSHAState);
+ tf_digest_save_registers(sha_state);
- pSHAState->nBytesProcessed =
- INREG32(&pSha1Md5Reg_t->DIGEST_COUNT);
+ sha_state->bytes_processed =
+ INREG32(&sha1_md5_reg->DIGEST_COUNT);
/*We have flushed the chunk so it is empty now */
- pSHAState->nChunkLength = 0;
+ sha_state->chunk_length = 0;
/*Then we have less data to process */
- pData += vLengthToComplete;
- dataLength -= vLengthToComplete;
+ data += vLengthToComplete;
+ data_length -= vLengthToComplete;
}
/*(2)We process all the 64B buffer that we can */
- if (pSHAState->nChunkLength + dataLength >=
+ if (sha_state->chunk_length + data_length >=
HASH_BLOCK_BYTES_LENGTH) {
- while (dataLength > HASH_BLOCK_BYTES_LENGTH) {
+ while (data_length > HASH_BLOCK_BYTES_LENGTH) {
u8 pTempAlignedBuffer[HASH_BLOCK_BYTES_LENGTH];
/*
@@ -328,71 +332,79 @@ void PDrvCryptoUpdateHash(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *pSHAState,
*/
/*We copy the data to process to an aligned
*buffer */
- memcpy(pTempAlignedBuffer, pData,
- HASH_BLOCK_BYTES_LENGTH);
+ if (copy_from_user(
+ pTempAlignedBuffer,
+ data,
+ HASH_BLOCK_BYTES_LENGTH))
+ return false;
/*Then we send this buffer to the hash
*hardware */
- PDrvCryptoRestoreHashRegisters(pSHAState);
- static_Hash_HwPerform64bDigest(
+ tf_digest_restore_registers(sha_state);
+ tf_digest_hw_perform_64b(
(u32 *) pTempAlignedBuffer,
- pSHAState->CTRL,
- pSHAState->nBytesProcessed);
- PDrvCryptoSaveHashRegisters(pSHAState);
+ sha_state->CTRL,
+ sha_state->bytes_processed);
+ tf_digest_save_registers(sha_state);
- pSHAState->nBytesProcessed =
- INREG32(&pSha1Md5Reg_t->DIGEST_COUNT);
+ sha_state->bytes_processed =
+ INREG32(&sha1_md5_reg->DIGEST_COUNT);
/*Then we decrease the remaining data of 64B */
- pData += HASH_BLOCK_BYTES_LENGTH;
- dataLength -= HASH_BLOCK_BYTES_LENGTH;
+ data += HASH_BLOCK_BYTES_LENGTH;
+ data_length -= HASH_BLOCK_BYTES_LENGTH;
}
}
/*(3)We look if we have some data that could not be processed
*yet because it is not large enough to fill a buffer of 64B */
- if (dataLength > 0) {
- if (pSHAState->nChunkLength + dataLength >
+ if (data_length > 0) {
+ if (sha_state->chunk_length + data_length >
HASH_BLOCK_BYTES_LENGTH) {
/*Should never be in this case !!! */
- panic("PDrvCryptoUpdateHash: nChunkLength + \
- dataLength > HASH_BLOCK_BYTES_LENGTH\n");
+ panic("tf_digest_update: chunk_length data_length > "
+ "HASH_BLOCK_BYTES_LENGTH\n");
}
/*So we fill the chunk buffer with the new data to
*complete to 64B */
- memcpy(pSHAState->pChunkBuffer + pSHAState->
- nChunkLength, pData, dataLength);
- pSHAState->nChunkLength += dataLength;
+ if (copy_from_user(
+ sha_state->chunk_buffer+sha_state->chunk_length,
+ data,
+ data_length))
+ return false;
+ sha_state->chunk_length += data_length;
}
}
- dprintk(KERN_INFO "PDrvCryptoUpdateHash: Done: "\
+ dprintk(KERN_INFO "tf_digest_update: Done: "\
"Chunk=%u; Processed=%u\n",
- pSHAState->nChunkLength, pSHAState->nBytesProcessed);
+ sha_state->chunk_length, sha_state->bytes_processed);
+
+ return true;
}
/*------------------------------------------------------------------------- */
-static void static_Hash_HwPerform64bDigest(u32 *pData,
- u32 nAlgo, u32 nBytesProcessed)
+static void tf_digest_hw_perform_64b(u32 *data,
+ u32 algo, u32 bytes_processed)
{
- u32 nAlgoConstant = 0;
+ u32 algo_constant = 0;
- OUTREG32(&pSha1Md5Reg_t->DIGEST_COUNT, nBytesProcessed);
+ OUTREG32(&sha1_md5_reg->DIGEST_COUNT, bytes_processed);
- if (nBytesProcessed == 0) {
+ if (bytes_processed == 0) {
/* No bytes processed so far. Will use the algo constant instead
of previous digest */
- nAlgoConstant = 1 << 3;
+ algo_constant = 1 << 3;
}
- OUTREG32(&pSha1Md5Reg_t->MODE,
- nAlgoConstant | (nAlgo & 0x6));
- OUTREG32(&pSha1Md5Reg_t->LENGTH, HASH_BLOCK_BYTES_LENGTH);
+ OUTREG32(&sha1_md5_reg->MODE,
+ algo_constant | (algo & 0x6));
+ OUTREG32(&sha1_md5_reg->LENGTH, HASH_BLOCK_BYTES_LENGTH);
- if (SCXPublicCryptoWaitForReadyBit(
- (u32 *)&pSha1Md5Reg_t->IRQSTATUS,
+ if (tf_crypto_wait_for_ready_bit(
+ (u32 *)&sha1_md5_reg->IRQSTATUS,
DIGEST_IRQSTATUS_INPUT_READY_BIT)
!= PUBLIC_CRYPTO_OPERATION_SUCCESS) {
/* Crash the system as this should never occur */
@@ -401,37 +413,37 @@ static void static_Hash_HwPerform64bDigest(u32 *pData,
}
/*
- *The pData buffer is a buffer of 64 bytes.
+ *The data buffer is a buffer of 64 bytes.
*/
- OUTREG32(&pSha1Md5Reg_t->DIN_0, pData[0]);
- OUTREG32(&pSha1Md5Reg_t->DIN_1, pData[1]);
- OUTREG32(&pSha1Md5Reg_t->DIN_2, pData[2]);
- OUTREG32(&pSha1Md5Reg_t->DIN_3, pData[3]);
- OUTREG32(&pSha1Md5Reg_t->DIN_4, pData[4]);
- OUTREG32(&pSha1Md5Reg_t->DIN_5, pData[5]);
- OUTREG32(&pSha1Md5Reg_t->DIN_6, pData[6]);
- OUTREG32(&pSha1Md5Reg_t->DIN_7, pData[7]);
- OUTREG32(&pSha1Md5Reg_t->DIN_8, pData[8]);
- OUTREG32(&pSha1Md5Reg_t->DIN_9, pData[9]);
- OUTREG32(&pSha1Md5Reg_t->DIN_10, pData[10]);
- OUTREG32(&pSha1Md5Reg_t->DIN_11, pData[11]);
- OUTREG32(&pSha1Md5Reg_t->DIN_12, pData[12]);
- OUTREG32(&pSha1Md5Reg_t->DIN_13, pData[13]);
- OUTREG32(&pSha1Md5Reg_t->DIN_14, pData[14]);
- OUTREG32(&pSha1Md5Reg_t->DIN_15, pData[15]);
+ OUTREG32(&sha1_md5_reg->DIN_0, data[0]);
+ OUTREG32(&sha1_md5_reg->DIN_1, data[1]);
+ OUTREG32(&sha1_md5_reg->DIN_2, data[2]);
+ OUTREG32(&sha1_md5_reg->DIN_3, data[3]);
+ OUTREG32(&sha1_md5_reg->DIN_4, data[4]);
+ OUTREG32(&sha1_md5_reg->DIN_5, data[5]);
+ OUTREG32(&sha1_md5_reg->DIN_6, data[6]);
+ OUTREG32(&sha1_md5_reg->DIN_7, data[7]);
+ OUTREG32(&sha1_md5_reg->DIN_8, data[8]);
+ OUTREG32(&sha1_md5_reg->DIN_9, data[9]);
+ OUTREG32(&sha1_md5_reg->DIN_10, data[10]);
+ OUTREG32(&sha1_md5_reg->DIN_11, data[11]);
+ OUTREG32(&sha1_md5_reg->DIN_12, data[12]);
+ OUTREG32(&sha1_md5_reg->DIN_13, data[13]);
+ OUTREG32(&sha1_md5_reg->DIN_14, data[14]);
+ OUTREG32(&sha1_md5_reg->DIN_15, data[15]);
/*
*Wait until the hash operation is finished.
*/
- SCXPublicCryptoWaitForReadyBitInfinitely(
- (u32 *)&pSha1Md5Reg_t->IRQSTATUS,
+ tf_crypto_wait_for_ready_bit_infinitely(
+ (u32 *)&sha1_md5_reg->IRQSTATUS,
DIGEST_IRQSTATUS_OUTPUT_READY_BIT);
}
/*------------------------------------------------------------------------- */
-static void static_Hash_HwPerformDmaDigest(u8 *pData, u32 nDataLength,
- u32 nAlgo, u32 nBytesProcessed)
+static bool tf_digest_hw_perform_dma(u8 *data, u32 nDataLength,
+ u32 algo, u32 bytes_processed)
{
/*
*Note: The DMA only sees physical addresses !
@@ -439,50 +451,53 @@ static void static_Hash_HwPerformDmaDigest(u8 *pData, u32 nDataLength,
int dma_ch0;
struct omap_dma_channel_params ch0_parameters;
- u32 nLengthLoop = 0;
- u32 nAlgoConstant;
- struct SCXLNX_DEVICE *pDevice = SCXLNXGetDevice();
+ u32 length_loop = 0;
+ u32 algo_constant;
+ struct tf_device *dev = tf_get_device();
dprintk(KERN_INFO
- "static_Hash_HwPerformDmaDigest: Buffer=0x%08x/%u\n",
- (u32)pData, (u32)nDataLength);
+ "tf_digest_hw_perform_dma: Buffer=0x%08x/%u\n",
+ (u32)data, (u32)nDataLength);
/*lock the DMA */
- mutex_lock(&pDevice->sm.sDMALock);
- if (scxPublicDMARequest(&dma_ch0) != PUBLIC_CRYPTO_OPERATION_SUCCESS) {
- mutex_unlock(&pDevice->sm.sDMALock);
- return;
+ mutex_lock(&dev->sm.dma_mutex);
+ if (tf_dma_request(&dma_ch0) != PUBLIC_CRYPTO_OPERATION_SUCCESS) {
+ mutex_unlock(&dev->sm.dma_mutex);
+ return false;
}
while (nDataLength > 0) {
- nAlgoConstant = 0;
- if (nBytesProcessed == 0) {
+ algo_constant = 0;
+ if (bytes_processed == 0) {
/*No bytes processed so far. Will use the algo
*constant instead of previous digest */
- nAlgoConstant = 1 << 3;
+ algo_constant = 1 << 3;
}
/*check length */
- if (nDataLength <= pDevice->nDMABufferLength)
- nLengthLoop = nDataLength;
+ if (nDataLength <= dev->dma_buffer_length)
+ length_loop = nDataLength;
else
- nLengthLoop = pDevice->nDMABufferLength;
+ length_loop = dev->dma_buffer_length;
/*
- *Copy the data from the input buffer into a preallocated
- *buffer which is aligned on the beginning of a page.
- *This may prevent potential issues when flushing/invalidating
- *the buffer as the cache lines are 64 bytes long.
+ * Copy the data from the user input buffer into a preallocated
+ * buffer which has correct properties from efficient DMA
+ * transfers.
*/
- memcpy(pDevice->pDMABuffer, pData, nLengthLoop);
+ if (copy_from_user(dev->dma_buffer, data, length_loop)) {
+ omap_free_dma(dma_ch0);
+ mutex_unlock(&dev->sm.dma_mutex);
+ return false;
+ }
/*DMA1: Mem -> HASH */
- scxPublicSetDMAChannelCommonParams(&ch0_parameters,
- nLengthLoop / HASH_BLOCK_BYTES_LENGTH,
+ tf_dma_set_channel_common_params(&ch0_parameters,
+ length_loop / HASH_BLOCK_BYTES_LENGTH,
DMA_CEN_Elts_per_Frame_SHA,
DIGEST1_REGS_HW_ADDR + 0x80,
- pDevice->pDMABufferPhys,
+ dev->dma_buffer_phys,
OMAP44XX_DMA_SHA2_DIN_P);
/*specific for Mem -> HWA */
@@ -490,55 +505,57 @@ static void static_Hash_HwPerformDmaDigest(u8 *pData, u32 nDataLength,
ch0_parameters.dst_amode = OMAP_DMA_AMODE_CONSTANT;
ch0_parameters.src_or_dst_synch = OMAP_DMA_DST_SYNC;
- scxPublicDMASetParams(dma_ch0, &ch0_parameters);
+ omap_set_dma_params(dma_ch0, &ch0_parameters);
omap_set_dma_src_burst_mode(dma_ch0, OMAP_DMA_DATA_BURST_16);
omap_set_dma_dest_burst_mode(dma_ch0, OMAP_DMA_DATA_BURST_16);
- OUTREG32(&pSha1Md5Reg_t->DIGEST_COUNT, nBytesProcessed);
- OUTREG32(&pSha1Md5Reg_t->MODE,
- nAlgoConstant | (nAlgo & 0x6));
+ OUTREG32(&sha1_md5_reg->DIGEST_COUNT, bytes_processed);
+ OUTREG32(&sha1_md5_reg->MODE,
+ algo_constant | (algo & 0x6));
/*
* Triggers operation
* Interrupt, Free Running + GO (DMA on)
*/
- OUTREG32(&pSha1Md5Reg_t->SYSCONFIG,
- INREG32(&pSha1Md5Reg_t->SYSCONFIG) |
+ OUTREG32(&sha1_md5_reg->SYSCONFIG,
+ INREG32(&sha1_md5_reg->SYSCONFIG) |
DIGEST_SYSCONFIG_PDMA_EN_BIT);
- OUTREG32(&pSha1Md5Reg_t->LENGTH, nLengthLoop);
+ OUTREG32(&sha1_md5_reg->LENGTH, length_loop);
wmb();
- scxPublicDMAStart(dma_ch0, OMAP_DMA_BLOCK_IRQ);
+ tf_dma_start(dma_ch0, OMAP_DMA_BLOCK_IRQ);
- scxPublicDMAWait(1);
+ tf_dma_wait(1);
- OUTREG32(&pSha1Md5Reg_t->SYSCONFIG, 0);
+ OUTREG32(&sha1_md5_reg->SYSCONFIG, 0);
- scxPublicDMAClearChannel(dma_ch0);
+ omap_clear_dma(dma_ch0);
- pData += nLengthLoop;
- nDataLength -= nLengthLoop;
- nBytesProcessed =
- INREG32(&pSha1Md5Reg_t->DIGEST_COUNT);
+ data += length_loop;
+ nDataLength -= length_loop;
+ bytes_processed =
+ INREG32(&sha1_md5_reg->DIGEST_COUNT);
}
/*For safety reasons, let's clean the working buffer */
- memset(pDevice->pDMABuffer, 0, nLengthLoop);
+ memset(dev->dma_buffer, 0, length_loop);
/*release the DMA */
- scxPublicDMARelease(dma_ch0);
+ omap_free_dma(dma_ch0);
- mutex_unlock(&pDevice->sm.sDMALock);
+ mutex_unlock(&dev->sm.dma_mutex);
/*
* The dma transfert is finished, now wait until the hash
* operation is finished.
*/
- SCXPublicCryptoWaitForReadyBitInfinitely(
- (u32 *)&pSha1Md5Reg_t->IRQSTATUS,
+ tf_crypto_wait_for_ready_bit_infinitely(
+ (u32 *)&sha1_md5_reg->IRQSTATUS,
DIGEST_IRQSTATUS_CONTEXT_READY_BIT);
+
+ return true;
}
/*------------------------------------------------------------------------- */
@@ -546,96 +563,101 @@ static void static_Hash_HwPerformDmaDigest(u8 *pData, u32 nDataLength,
*Static function, perform data digest using the DMA for data transfer.
*
*inputs:
- * pData : pointer of the input data to process
- * dataLength : number of byte to process
+ * data : pointer of the input data to process
+ * data_length : number of byte to process
*/
-static void PDrvCryptoUpdateHashWithDMA(
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *pSHAState,
- u8 *pData, u32 dataLength)
+static bool tf_digest_update_dma(
+ struct tf_crypto_sha_operation_state *sha_state,
+ u8 *data, u32 data_length)
{
- dprintk(KERN_INFO "PDrvCryptoUpdateHashWithDMA\n");
+ dprintk(KERN_INFO "tf_digest_update_dma\n");
- if (pSHAState->nChunkLength != 0) {
+ if (sha_state->chunk_length != 0) {
u32 vLengthToComplete;
/*Fill the chunk first */
- if (pSHAState->
- nChunkLength + dataLength <= HASH_BLOCK_BYTES_LENGTH) {
+ if (sha_state->
+ chunk_length + data_length <= HASH_BLOCK_BYTES_LENGTH) {
/*So we fill the chunk buffer with the new data */
- memcpy(pSHAState->
- pChunkBuffer + pSHAState->nChunkLength,
- pData, dataLength);
- pSHAState->nChunkLength += dataLength;
+ if (copy_from_user(sha_state->chunk_buffer +
+ sha_state->chunk_length, data,
+ data_length))
+ return false;
+ sha_state->chunk_length += data_length;
/*We'll keep some data for the final */
- return;
+ return true;
}
- vLengthToComplete = HASH_BLOCK_BYTES_LENGTH - pSHAState->
- nChunkLength;
+ vLengthToComplete = HASH_BLOCK_BYTES_LENGTH - sha_state->
+ chunk_length;
if (vLengthToComplete != 0) {
/*So we fill the chunk buffer with the new data to
*complete to 64B */
- memcpy(pSHAState->pChunkBuffer + pSHAState->
- nChunkLength, pData, vLengthToComplete);
+ if (copy_from_user(sha_state->chunk_buffer +
+ sha_state->chunk_length, data,
+ vLengthToComplete))
+ return false;
}
/*Then we send this buffer to the HWA (no DMA) */
- static_Hash_HwPerform64bDigest(
- (u32 *)pSHAState->pChunkBuffer, pSHAState->CTRL,
- pSHAState->nBytesProcessed);
+ tf_digest_hw_perform_64b(
+ (u32 *)sha_state->chunk_buffer, sha_state->CTRL,
+ sha_state->bytes_processed);
- pSHAState->nBytesProcessed =
- INREG32(&pSha1Md5Reg_t->DIGEST_COUNT);
+ sha_state->bytes_processed =
+ INREG32(&sha1_md5_reg->DIGEST_COUNT);
/*We have flushed the chunk so it is empty now */
- pSHAState->nChunkLength = 0;
+ sha_state->chunk_length = 0;
/*Update the data buffer depending of the data already
*processed */
- pData += vLengthToComplete;
- dataLength -= vLengthToComplete;
+ data += vLengthToComplete;
+ data_length -= vLengthToComplete;
}
- if (dataLength > HASH_BLOCK_BYTES_LENGTH) {
+ if (data_length > HASH_BLOCK_BYTES_LENGTH) {
/*DMA only manages data length that is multiple of 64b */
- u32 vDmaProcessSize = dataLength & 0xFFFFFFC0;
+ u32 vDmaProcessize = data_length & 0xFFFFFFC0;
- if (vDmaProcessSize == dataLength) {
+ if (vDmaProcessize == data_length) {
/*We keep one block for the final */
- vDmaProcessSize -= HASH_BLOCK_BYTES_LENGTH;
+ vDmaProcessize -= HASH_BLOCK_BYTES_LENGTH;
}
- static_Hash_HwPerformDmaDigest(pData, vDmaProcessSize,
- pSHAState->CTRL, pSHAState->nBytesProcessed);
+ if (!tf_digest_hw_perform_dma(data, vDmaProcessize,
+ sha_state->CTRL, sha_state->bytes_processed))
+ return false;
- pSHAState->nBytesProcessed =
- INREG32(&pSha1Md5Reg_t->DIGEST_COUNT);
- pData += vDmaProcessSize;
- dataLength -= vDmaProcessSize;
+ sha_state->bytes_processed =
+ INREG32(&sha1_md5_reg->DIGEST_COUNT);
+ data += vDmaProcessize;
+ data_length -= vDmaProcessize;
}
/*At that point, there is less than 64b left to process*/
- if ((dataLength == 0) || (dataLength > HASH_BLOCK_BYTES_LENGTH)) {
+ if ((data_length == 0) || (data_length > HASH_BLOCK_BYTES_LENGTH))
/*Should never be in this case !!! */
- panic("PDrvCryptoUpdateHASHWithDMA: \
- Remaining dataLength=%u\n", dataLength);
- }
+ return false;
/*We now fill the chunk buffer with the remaining data */
- memcpy(pSHAState->pChunkBuffer, pData, dataLength);
- pSHAState->nChunkLength = dataLength;
+ if (copy_from_user(sha_state->chunk_buffer, data, data_length))
+ return false;
+ sha_state->chunk_length = data_length;
+
+ return true;
}
#ifdef CONFIG_SMC_KERNEL_CRYPTO
-static void PDrvCryptoInitHash(u32 alg,
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state)
+static void tf_digest_init_operation(u32 alg,
+ struct tf_crypto_sha_operation_state *state)
{
- memset(state, 0, sizeof(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE));
+ memset(state, 0, sizeof(struct tf_crypto_sha_operation_state));
state->CTRL = alg << 1;
}
@@ -663,7 +685,7 @@ static int static_Hash_HwReadDigest(u32 algo, u8 *out)
}
for (i = 0; i < regs; i++) {
- tmp = INREG32(&pSha1Md5Reg_t->IDIGEST_A + i);
+ tmp = INREG32(&sha1_md5_reg->IDIGEST_A + i);
out[idx++] = (u8) ((tmp >> 0) & 0xff);
out[idx++] = (u8) ((tmp >> 8) & 0xff);
@@ -674,13 +696,13 @@ static int static_Hash_HwReadDigest(u32 algo, u8 *out)
return 0;
}
-static int PDrvCryptoFinalHash(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state,
+static int tf_digest_final(struct tf_crypto_sha_operation_state *state,
u8 *out)
{
- u32 *data = (u32 *) state->pChunkBuffer;
+ u32 *data = (u32 *) state->chunk_buffer;
/* Hashing an empty string? */
- if (state->nBytesProcessed + state->nChunkLength == 0) {
+ if (state->bytes_processed + state->chunk_length == 0) {
switch (DIGEST_MODE_GET_ALGO(state->CTRL)) {
case DIGEST_CTRL_ALGO_MD5:
memcpy(out, md5OverEmptyString, HASH_MD5_LENGTH);
@@ -701,20 +723,20 @@ static int PDrvCryptoFinalHash(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state,
return 0;
}
- PDrvCryptoRestoreHashRegisters(state);
+ tf_digest_restore_registers(state);
/*
* At this point, the chunk buffer should contain the last block of data
* needed for the final.
*/
- OUTREG32(&pSha1Md5Reg_t->DIGEST_COUNT, state->nBytesProcessed);
- OUTREG32(&pSha1Md5Reg_t->MODE,
+ OUTREG32(&sha1_md5_reg->DIGEST_COUNT, state->bytes_processed);
+ OUTREG32(&sha1_md5_reg->MODE,
(state->CTRL & 0x6) | 0x10 |
- (state->nBytesProcessed == 0) << 3);
- OUTREG32(&pSha1Md5Reg_t->LENGTH, state->nChunkLength);
+ (state->bytes_processed == 0) << 3);
+ OUTREG32(&sha1_md5_reg->LENGTH, state->chunk_length);
- if (SCXPublicCryptoWaitForReadyBit(
- (u32 *) &pSha1Md5Reg_t->IRQSTATUS,
+ if (tf_crypto_wait_for_ready_bit(
+ (u32 *) &sha1_md5_reg->IRQSTATUS,
DIGEST_IRQSTATUS_INPUT_READY_BIT)
!= PUBLIC_CRYPTO_OPERATION_SUCCESS) {
/* Crash the system as this should never occur */
@@ -722,26 +744,26 @@ static int PDrvCryptoFinalHash(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state,
"Input data to be ready\n");
}
- OUTREG32(&pSha1Md5Reg_t->DIN_0, data[0]);
- OUTREG32(&pSha1Md5Reg_t->DIN_1, data[1]);
- OUTREG32(&pSha1Md5Reg_t->DIN_2, data[2]);
- OUTREG32(&pSha1Md5Reg_t->DIN_3, data[3]);
- OUTREG32(&pSha1Md5Reg_t->DIN_4, data[4]);
- OUTREG32(&pSha1Md5Reg_t->DIN_5, data[5]);
- OUTREG32(&pSha1Md5Reg_t->DIN_6, data[6]);
- OUTREG32(&pSha1Md5Reg_t->DIN_7, data[7]);
- OUTREG32(&pSha1Md5Reg_t->DIN_8, data[8]);
- OUTREG32(&pSha1Md5Reg_t->DIN_9, data[9]);
- OUTREG32(&pSha1Md5Reg_t->DIN_10, data[10]);
- OUTREG32(&pSha1Md5Reg_t->DIN_11, data[11]);
- OUTREG32(&pSha1Md5Reg_t->DIN_12, data[12]);
- OUTREG32(&pSha1Md5Reg_t->DIN_13, data[13]);
- OUTREG32(&pSha1Md5Reg_t->DIN_14, data[14]);
- OUTREG32(&pSha1Md5Reg_t->DIN_15, data[15]);
+ OUTREG32(&sha1_md5_reg->DIN_0, data[0]);
+ OUTREG32(&sha1_md5_reg->DIN_1, data[1]);
+ OUTREG32(&sha1_md5_reg->DIN_2, data[2]);
+ OUTREG32(&sha1_md5_reg->DIN_3, data[3]);
+ OUTREG32(&sha1_md5_reg->DIN_4, data[4]);
+ OUTREG32(&sha1_md5_reg->DIN_5, data[5]);
+ OUTREG32(&sha1_md5_reg->DIN_6, data[6]);
+ OUTREG32(&sha1_md5_reg->DIN_7, data[7]);
+ OUTREG32(&sha1_md5_reg->DIN_8, data[8]);
+ OUTREG32(&sha1_md5_reg->DIN_9, data[9]);
+ OUTREG32(&sha1_md5_reg->DIN_10, data[10]);
+ OUTREG32(&sha1_md5_reg->DIN_11, data[11]);
+ OUTREG32(&sha1_md5_reg->DIN_12, data[12]);
+ OUTREG32(&sha1_md5_reg->DIN_13, data[13]);
+ OUTREG32(&sha1_md5_reg->DIN_14, data[14]);
+ OUTREG32(&sha1_md5_reg->DIN_15, data[15]);
/* Wait till the hash operation is finished */
- SCXPublicCryptoWaitForReadyBitInfinitely(
- (u32 *) &pSha1Md5Reg_t->IRQSTATUS,
+ tf_crypto_wait_for_ready_bit_infinitely(
+ (u32 *) &sha1_md5_reg->IRQSTATUS,
DIGEST_IRQSTATUS_OUTPUT_READY_BIT);
return static_Hash_HwReadDigest(DIGEST_MODE_GET_ALGO(state->CTRL), out);
@@ -754,17 +776,20 @@ static int PDrvCryptoFinalHash(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state,
static int digest_update(struct shash_desc *desc, const u8 *data,
unsigned int len)
{
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state = shash_desc_ctx(desc);
+ struct tf_crypto_sha_operation_state *state = shash_desc_ctx(desc);
- PDrvCryptoLockUnlockHWA(PUBLIC_CRYPTO_HWA_SHA, LOCK_HWA);
+ /* Make sure SHA/MD5 HWA is accessible */
+ tf_delayed_secure_resume();
- SCXPublicCryptoEnableClock(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
+ tf_crypto_lock_hwa(PUBLIC_CRYPTO_HWA_SHA, LOCK_HWA);
- PDrvCryptoUpdateHash(state, (u8 *) data, len);
+ tf_crypto_enable_clock(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
- SCXPublicCryptoDisableClock(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
+ tf_digest_update(state, (u8 *) data, len);
- PDrvCryptoLockUnlockHWA(PUBLIC_CRYPTO_HWA_SHA, UNLOCK_HWA);
+ tf_crypto_disable_clock(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
+
+ tf_crypto_lock_hwa(PUBLIC_CRYPTO_HWA_SHA, UNLOCK_HWA);
return 0;
}
@@ -772,24 +797,27 @@ static int digest_update(struct shash_desc *desc, const u8 *data,
static int digest_final(struct shash_desc *desc, u8 *out)
{
int ret;
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state = shash_desc_ctx(desc);
+ struct tf_crypto_sha_operation_state *state = shash_desc_ctx(desc);
+
+ /* Make sure SHA/MD5 HWA is accessible */
+ tf_delayed_secure_resume();
- PDrvCryptoLockUnlockHWA(PUBLIC_CRYPTO_HWA_SHA, LOCK_HWA);
+ tf_crypto_lock_hwa(PUBLIC_CRYPTO_HWA_SHA, LOCK_HWA);
- SCXPublicCryptoEnableClock(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
+ tf_crypto_enable_clock(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
- ret = PDrvCryptoFinalHash(state, out);
+ ret = tf_digest_final(state, out);
- SCXPublicCryptoDisableClock(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
+ tf_crypto_disable_clock(PUBLIC_CRYPTO_SHA2MD5_CLOCK_REG);
- PDrvCryptoLockUnlockHWA(PUBLIC_CRYPTO_HWA_SHA, UNLOCK_HWA);
+ tf_crypto_lock_hwa(PUBLIC_CRYPTO_HWA_SHA, UNLOCK_HWA);
return ret;
}
static int digest_import(struct shash_desc *desc, const void *in)
{
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state = shash_desc_ctx(desc);
+ struct tf_crypto_sha_operation_state *state = shash_desc_ctx(desc);
memcpy(state, in, sizeof(*state));
return 0;
@@ -797,7 +825,7 @@ static int digest_import(struct shash_desc *desc, const void *in)
static int digest_export(struct shash_desc *desc, void *out)
{
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state = shash_desc_ctx(desc);
+ struct tf_crypto_sha_operation_state *state = shash_desc_ctx(desc);
memcpy(out, state, sizeof(*state));
return 0;
@@ -806,9 +834,9 @@ static int digest_export(struct shash_desc *desc, void *out)
/* MD5 */
static int md5_init(struct shash_desc *desc)
{
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state = shash_desc_ctx(desc);
+ struct tf_crypto_sha_operation_state *state = shash_desc_ctx(desc);
- PDrvCryptoInitHash(DIGEST_CTRL_ALGO_MD5, state);
+ tf_digest_init_operation(DIGEST_CTRL_ALGO_MD5, state);
return 0;
}
@@ -820,8 +848,8 @@ static struct shash_alg smc_md5_alg = {
.final = digest_final,
.export = digest_export,
.import = digest_import,
- .descsize = sizeof(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE),
- .statesize = sizeof(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE),
+ .descsize = sizeof(struct tf_crypto_sha_operation_state),
+ .statesize = sizeof(struct tf_crypto_sha_operation_state),
.base = {
.cra_name = "md5",
.cra_driver_name = "md5-smc",
@@ -835,9 +863,9 @@ static struct shash_alg smc_md5_alg = {
/* SHA1 */
static int sha1_init(struct shash_desc *desc)
{
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state = shash_desc_ctx(desc);
+ struct tf_crypto_sha_operation_state *state = shash_desc_ctx(desc);
- PDrvCryptoInitHash(DIGEST_CTRL_ALGO_SHA1, state);
+ tf_digest_init_operation(DIGEST_CTRL_ALGO_SHA1, state);
return 0;
}
@@ -849,8 +877,8 @@ static struct shash_alg smc_sha1_alg = {
.final = digest_final,
.export = digest_export,
.import = digest_import,
- .descsize = sizeof(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE),
- .statesize = sizeof(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE),
+ .descsize = sizeof(struct tf_crypto_sha_operation_state),
+ .statesize = sizeof(struct tf_crypto_sha_operation_state),
.base = {
.cra_name = "sha1",
.cra_driver_name = "sha1-smc",
@@ -864,9 +892,9 @@ static struct shash_alg smc_sha1_alg = {
/* SHA224 */
static int sha224_init(struct shash_desc *desc)
{
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state = shash_desc_ctx(desc);
+ struct tf_crypto_sha_operation_state *state = shash_desc_ctx(desc);
- PDrvCryptoInitHash(DIGEST_CTRL_ALGO_SHA224, state);
+ tf_digest_init_operation(DIGEST_CTRL_ALGO_SHA224, state);
return 0;
}
@@ -878,8 +906,8 @@ static struct shash_alg smc_sha224_alg = {
.final = digest_final,
.export = digest_export,
.import = digest_import,
- .descsize = sizeof(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE),
- .statesize = sizeof(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE),
+ .descsize = sizeof(struct tf_crypto_sha_operation_state),
+ .statesize = sizeof(struct tf_crypto_sha_operation_state),
.base = {
.cra_name = "sha224",
.cra_driver_name = "sha224-smc",
@@ -893,9 +921,9 @@ static struct shash_alg smc_sha224_alg = {
/* SHA256 */
static int sha256_init(struct shash_desc *desc)
{
- struct PUBLIC_CRYPTO_SHA_OPERATION_STATE *state = shash_desc_ctx(desc);
+ struct tf_crypto_sha_operation_state *state = shash_desc_ctx(desc);
- PDrvCryptoInitHash(DIGEST_CTRL_ALGO_SHA256, state);
+ tf_digest_init_operation(DIGEST_CTRL_ALGO_SHA256, state);
return 0;
}
@@ -907,8 +935,8 @@ static struct shash_alg smc_sha256_alg = {
.final = digest_final,
.export = digest_export,
.import = digest_import,
- .descsize = sizeof(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE),
- .statesize = sizeof(struct PUBLIC_CRYPTO_SHA_OPERATION_STATE),
+ .descsize = sizeof(struct tf_crypto_sha_operation_state),
+ .statesize = sizeof(struct tf_crypto_sha_operation_state),
.base = {
.cra_name = "sha256",
.cra_driver_name = "sha256-smc",
diff --git a/security/smc/omap4/scxlnx_defs.h b/security/smc/tf_defs.h
index a6dcb9c..23dc7ca 100644
--- a/security/smc/omap4/scxlnx_defs.h
+++ b/security/smc/tf_defs.h
@@ -1,5 +1,5 @@
-/*
- * Copyright (c) 2006-2010 Trusted Logic S.A.
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
@@ -17,8 +17,8 @@
* MA 02111-1307 USA
*/
-#ifndef __SCXLNX_DEFS_H__
-#define __SCXLNX_DEFS_H__
+#ifndef __TF_DEFS_H__
+#define __TF_DEFS_H__
#include <asm/atomic.h>
#include <linux/version.h>
@@ -27,7 +27,6 @@
#include <linux/completion.h>
#include <linux/list.h>
#include <linux/spinlock.h>
-#include <linux/sysdev.h>
#include <linux/sysfs.h>
#include <linux/sched.h>
#include <linux/semaphore.h>
@@ -35,7 +34,12 @@
#include <linux/wakelock.h>
#endif
-#include "scx_protocol.h"
+#include "tf_protocol.h"
+
+#ifdef CONFIG_TF_ION
+#include <linux/ion.h>
+#include <linux/omap_ion.h>
+#endif
/*----------------------------------------------------------------------------*/
@@ -44,77 +48,77 @@
/*
* Maximum number of shared memory blocks that can be reigsters in a connection
*/
-#define SCXLNX_SHMEM_MAX_COUNT (64)
+#define TF_SHMEM_MAX_COUNT (64)
/*
* Describes the possible types of shared memories
*
- * SCXLNX_SHMEM_TYPE_PREALLOC_REGISTERED_SHMEM :
+ * TF_SHMEM_TYPE_PREALLOC_REGISTERED_SHMEM :
* The descriptor describes a registered shared memory.
* Its coarse pages are preallocated when initializing the
* connection
- * SCXLNX_SHMEM_TYPE_REGISTERED_SHMEM :
+ * TF_SHMEM_TYPE_REGISTERED_SHMEM :
* The descriptor describes a registered shared memory.
* Its coarse pages are not preallocated
- * SCXLNX_SHMEM_TYPE_PM_HIBERNATE :
+ * TF_SHMEM_TYPE_PM_HIBERNATE :
* The descriptor describes a power management shared memory.
*/
-enum SCXLNX_SHMEM_TYPE {
- SCXLNX_SHMEM_TYPE_PREALLOC_REGISTERED_SHMEM = 0,
- SCXLNX_SHMEM_TYPE_REGISTERED_SHMEM,
- SCXLNX_SHMEM_TYPE_PM_HIBERNATE,
+enum TF_SHMEM_TYPE {
+ TF_SHMEM_TYPE_PREALLOC_REGISTERED_SHMEM = 0,
+ TF_SHMEM_TYPE_REGISTERED_SHMEM,
+ TF_SHMEM_TYPE_PM_HIBERNATE,
};
/*
* This structure contains a pointer on a coarse page table
*/
-struct SCXLNX_COARSE_PAGE_TABLE {
+struct tf_coarse_page_table {
/*
* Identifies the coarse page table descriptor in
- * sFreeCoarsePageTables list
+ * free_coarse_page_tables list
*/
struct list_head list;
/*
* The address of the coarse page table
*/
- u32 *pDescriptors;
+ u32 *descriptors;
/*
* The address of the array containing this coarse page table
*/
- struct SCXLNX_COARSE_PAGE_TABLE_ARRAY *pParent;
+ struct tf_coarse_page_table_array *parent;
};
-#define SCXLNX_PAGE_DESCRIPTOR_TYPE_NORMAL 0
-#define SCXLNX_PAGE_DESCRIPTOR_TYPE_PREALLOCATED 1
+#define TF_PAGE_DESCRIPTOR_TYPE_NORMAL 0
+#define TF_PAGE_DESCRIPTOR_TYPE_PREALLOCATED 1
/*
* This structure describes an array of up to 4 coarse page tables
* allocated within a single 4KB page.
*/
-struct SCXLNX_COARSE_PAGE_TABLE_ARRAY {
+struct tf_coarse_page_table_array {
/*
- * identifies the element in the sCoarsePageTableArrays list
+ * identifies the element in the coarse_page_table_arrays list
*/
struct list_head list;
/*
* Type of page descriptor
- * can take any of SCXLNX_PAGE_DESCRIPTOR_TYPE_XXX value
+ * can take any of TF_PAGE_DESCRIPTOR_TYPE_XXX value
*/
- u32 nType;
+ u32 type;
- struct SCXLNX_COARSE_PAGE_TABLE sCoarsePageTables[4];
+ struct tf_coarse_page_table coarse_page_tables[4];
/*
* A counter of the number of coarse pages currently used
* the max value should be 4 (one coarse page table is 1KB while one
* page is 4KB)
*/
- u8 nReferenceCount;
+ u8 ref_count;
};
@@ -124,7 +128,7 @@ struct SCXLNX_COARSE_PAGE_TABLE_ARRAY {
* when the driver needs to allocate a new coarse page
* table.
*/
-struct SCXLNX_COARSE_PAGE_TABLE_ALLOCATION_CONTEXT {
+struct tf_coarse_page_table_allocation_context {
/*
* The spin lock protecting concurrent access to the structure.
*/
@@ -133,19 +137,19 @@ struct SCXLNX_COARSE_PAGE_TABLE_ALLOCATION_CONTEXT {
/*
* The list of allocated coarse page table arrays
*/
- struct list_head sCoarsePageTableArrays;
+ struct list_head coarse_page_table_arrays;
/*
* The list of free coarse page tables
*/
- struct list_head sFreeCoarsePageTables;
+ struct list_head free_coarse_page_tables;
};
/*
* Fully describes a shared memory block
*/
-struct SCXLNX_SHMEM_DESC {
+struct tf_shmem_desc {
/*
* Identifies the shared memory descriptor in the list of free shared
* memory descriptors
@@ -155,25 +159,25 @@ struct SCXLNX_SHMEM_DESC {
/*
* Identifies the type of shared memory descriptor
*/
- enum SCXLNX_SHMEM_TYPE nType;
+ enum TF_SHMEM_TYPE type;
/*
* The identifier of the block of shared memory, as returned by the
* Secure World.
- * This identifier is hBlock field of a REGISTER_SHARED_MEMORY answer
+ * This identifier is block field of a REGISTER_SHARED_MEMORY answer
*/
- u32 hIdentifier;
+ u32 block_identifier;
/* Client buffer */
u8 *pBuffer;
/* Up to eight coarse page table context */
- struct SCXLNX_COARSE_PAGE_TABLE *pCoarsePageTable[SCX_MAX_COARSE_PAGES];
+ struct tf_coarse_page_table *coarse_pg_table[TF_MAX_COARSE_PAGES];
- u32 nNumberOfCoarsePageTables;
+ u32 coarse_pg_table_count;
/* Reference counter */
- atomic_t nRefCnt;
+ atomic_t ref_count;
};
@@ -184,7 +188,7 @@ struct SCXLNX_SHMEM_DESC {
*
* Note that this driver supports only one instance of the Secure World
*/
-struct SCXLNX_COMM {
+struct tf_comm {
/*
* The spin lock protecting concurrent access to the structure.
*/
@@ -192,81 +196,81 @@ struct SCXLNX_COMM {
/*
* Bit vector with the following possible flags:
- * - SCXLNX_COMM_FLAG_IRQ_REQUESTED: If set, indicates that
+ * - TF_COMM_FLAG_IRQ_REQUESTED: If set, indicates that
* the IRQ has been successfuly requested.
- * - SCXLNX_COMM_FLAG_TERMINATING: If set, indicates that the
+ * - TF_COMM_FLAG_TERMINATING: If set, indicates that the
* communication with the Secure World is being terminated.
* Transmissions to the Secure World are not permitted
- * - SCXLNX_COMM_FLAG_W3B_ALLOCATED: If set, indicates that the
+ * - TF_COMM_FLAG_W3B_ALLOCATED: If set, indicates that the
* W3B buffer has been allocated.
*
* This bit vector must be accessed with the kernel's atomic bitwise
* operations.
*/
- unsigned long nFlags;
+ unsigned long flags;
/*
* The virtual address of the L1 shared buffer.
*/
- struct SCHANNEL_C1S_BUFFER *pBuffer;
+ struct tf_l1_shared_buffer *pBuffer;
/*
* The wait queue the client threads are waiting on.
*/
- wait_queue_head_t waitQueue;
+ wait_queue_head_t wait_queue;
#ifdef CONFIG_TF_TRUSTZONE
/*
* The interrupt line used by the Secure World.
*/
- int nSoftIntIrq;
+ int soft_int_irq;
/* ----- W3B ----- */
/* shared memory descriptor to identify the W3B */
- struct SCXLNX_SHMEM_DESC sW3BShmemDesc;
+ struct tf_shmem_desc w3b_shmem_desc;
/* Virtual address of the kernel allocated shared memory */
- u32 nW3BShmemVAddr;
+ u32 w3b;
/* offset of data in shared memory coarse pages */
- u32 nW3BShmemOffset;
+ u32 w3b_shmem_offset;
- u32 nW3BShmemSize;
+ u32 w3b_shmem_size;
- struct SCXLNX_COARSE_PAGE_TABLE_ALLOCATION_CONTEXT
- sW3BAllocationContext;
+ struct tf_coarse_page_table_allocation_context
+ w3b_cpt_alloc_context;
#endif
-#ifdef CONFIG_TF_MSHIELD
+#ifdef CONFIG_TF_ZEBRA
/*
* The SE SDP can only be initialized once...
*/
- int bSEInitialized;
+ int se_initialized;
/* Virtual address of the L0 communication buffer */
- void *pInitSharedBuffer;
+ void *init_shared_buffer;
/*
* Lock to be held by a client when executing an RPC
*/
- struct mutex sRPCLock;
+ struct mutex rpc_mutex;
/*
* Lock to protect concurrent accesses to DMA channels
*/
- struct mutex sDMALock;
+ struct mutex dma_mutex;
#endif
};
-#define SCXLNX_COMM_FLAG_IRQ_REQUESTED (0)
-#define SCXLNX_COMM_FLAG_PA_AVAILABLE (1)
-#define SCXLNX_COMM_FLAG_TERMINATING (2)
-#define SCXLNX_COMM_FLAG_W3B_ALLOCATED (3)
-#define SCXLNX_COMM_FLAG_L1_SHARED_ALLOCATED (4)
+#define TF_COMM_FLAG_IRQ_REQUESTED (0)
+#define TF_COMM_FLAG_PA_AVAILABLE (1)
+#define TF_COMM_FLAG_TERMINATING (2)
+#define TF_COMM_FLAG_W3B_ALLOCATED (3)
+#define TF_COMM_FLAG_L1_SHARED_ALLOCATED (4)
/*----------------------------------------------------------------------------*/
-struct SCXLNX_DEVICE_STATS {
+struct tf_device_stats {
struct kobject kobj;
struct kobj_type kobj_type;
@@ -283,89 +287,79 @@ struct SCXLNX_DEVICE_STATS {
/*
* This structure describes the information about one device handled by the
* driver. Note that the driver supports only a single device. see the global
- * variable g_SCXLNXDevice
+ * variable g_tf_dev
+
*/
-struct SCXLNX_DEVICE {
+struct tf_device {
/*
* The device number for the device.
*/
- dev_t nDevNum;
-
- /*
- * Interfaces the system device with the kernel.
- */
- struct sys_device sysdev;
+ dev_t dev_number;
/*
* Interfaces the char device with the kernel.
*/
struct cdev cdev;
-#ifdef CONFIG_TF_MSHIELD
+#ifdef CONFIG_TF_TEEC
+ struct cdev cdev_teec;
+#endif
+
+#ifdef CONFIG_TF_ZEBRA
struct cdev cdev_ctrl;
/*
* Globals for CUS
*/
/* Current key handles loaded in HWAs */
- u32 hAES1SecureKeyContext;
- u32 hAES2SecureKeyContext;
- u32 hDESSecureKeyContext;
- bool bSHAM1IsPublic;
+ u32 aes1_key_context;
+ u32 des_key_context;
+ bool sham1_is_public;
- /* Semaphores used to serialize HWA accesses */
- struct semaphore sAES1CriticalSection;
- struct semaphore sAES2CriticalSection;
- struct mutex sDESCriticalSection;
- struct mutex sSHACriticalSection;
+ /* Object used to serialize HWA accesses */
+ struct semaphore aes1_sema;
+ struct semaphore des_sema;
+ struct semaphore sha_sema;
/*
* An aligned and correctly shaped pre-allocated buffer used for DMA
* transfers
*/
- u32 nDMABufferLength;
- u8 *pDMABuffer;
- dma_addr_t pDMABufferPhys;
+ u32 dma_buffer_length;
+ u8 *dma_buffer;
+ dma_addr_t dma_buffer_phys;
/* Workspace allocated at boot time and reserved to the Secure World */
- u32 nWorkspaceAddr;
- u32 nWorkspaceSize;
+ u32 workspace_addr;
+ u32 workspace_size;
+
+ /*
+ * A Mutex to provide exclusive locking of the ioctl()
+ */
+ struct mutex dev_mutex;
#endif
/*
* Communications with the SM.
*/
- struct SCXLNX_COMM sm;
+ struct tf_comm sm;
/*
* Lists the connections attached to this device. A connection is
* created each time a user space application "opens" a file descriptor
* on the driver
*/
- struct list_head conns;
+ struct list_head connection_list;
/*
* The spin lock used to protect concurrent access to the connection
* list.
*/
- spinlock_t connsLock;
-
- struct SCXLNX_DEVICE_STATS sDeviceStats;
+ spinlock_t connection_list_lock;
- /*
- * A Mutex to provide exlusive locking of the ioctl()
- */
- struct mutex dev_mutex;
+ struct tf_device_stats stats;
};
-/* the bits of the nFlags field of the SCXLNX_DEVICE structure */
-#define SCXLNX_DEVICE_FLAG_CDEV_INITIALIZED (0)
-#define SCXLNX_DEVICE_FLAG_SYSDEV_CLASS_REGISTERED (1)
-#define SCXLNX_DEVICE_FLAG_SYSDEV_REGISTERED (2)
-#define SCXLNX_DEVICE_FLAG_CDEV_REGISTERED (3)
-#define SCXLNX_DEVICE_FLAG_CDEV_ADDED (4)
-#define SCXLNX_DEVICE_SYSFS_REGISTERED (5)
-
/*----------------------------------------------------------------------------*/
/*
* This type describes a connection state.
@@ -375,24 +369,24 @@ struct SCXLNX_DEVICE {
* Messages may be invalidated between the start of the ioctl call and the
* moment the message is sent to the Secure World.
*
- * SCXLNX_CONN_STATE_NO_DEVICE_CONTEXT :
+ * TF_CONN_STATE_NO_DEVICE_CONTEXT :
* The connection has no DEVICE_CONTEXT created and no
* CREATE_DEVICE_CONTEXT being processed by the Secure World
- * SCXLNX_CONN_STATE_CREATE_DEVICE_CONTEXT_SENT :
+ * TF_CONN_STATE_CREATE_DEVICE_CONTEXT_SENT :
* The connection has a CREATE_DEVICE_CONTEXT being processed by the Secure
* World
- * SCXLNX_CONN_STATE_VALID_DEVICE_CONTEXT :
+ * TF_CONN_STATE_VALID_DEVICE_CONTEXT :
* The connection has a DEVICE_CONTEXT created and no
* DESTROY_DEVICE_CONTEXT is being processed by the Secure World
- * SCXLNX_CONN_STATE_DESTROY_DEVICE_CONTEXT_SENT :
+ * TF_CONN_STATE_DESTROY_DEVICE_CONTEXT_SENT :
* The connection has a DESTROY_DEVICE_CONTEXT being processed by the Secure
* World
*/
-enum SCXLNX_CONN_STATE {
- SCXLNX_CONN_STATE_NO_DEVICE_CONTEXT = 0,
- SCXLNX_CONN_STATE_CREATE_DEVICE_CONTEXT_SENT,
- SCXLNX_CONN_STATE_VALID_DEVICE_CONTEXT,
- SCXLNX_CONN_STATE_DESTROY_DEVICE_CONTEXT_SENT
+enum TF_CONN_STATE {
+ TF_CONN_STATE_NO_DEVICE_CONTEXT = 0,
+ TF_CONN_STATE_CREATE_DEVICE_CONTEXT_SENT,
+ TF_CONN_STATE_VALID_DEVICE_CONTEXT,
+ TF_CONN_STATE_DESTROY_DEVICE_CONTEXT_SENT
};
@@ -408,10 +402,22 @@ enum SCXLNX_CONN_STATE {
* Note that this only covers the case where some other thread
* sent a DESTROY_DEVICE_CONTEXT command.
*/
-enum SCXLNX_COMMAND_STATE {
- SCXLNX_COMMAND_STATE_PENDING = 0,
- SCXLNX_COMMAND_STATE_SENT,
- SCXLNX_COMMAND_STATE_ABORTED
+enum TF_COMMAND_STATE {
+ TF_COMMAND_STATE_PENDING = 0,
+ TF_COMMAND_STATE_SENT,
+ TF_COMMAND_STATE_ABORTED
+};
+
+/*
+ * The origin of connection parameters such as login data and
+ * memory reference pointers.
+ *
+ * PROCESS: the calling process. All arguments must be validated.
+ * KERNEL: kernel code. All arguments can be trusted by this driver.
+ */
+enum TF_CONNECTION_OWNER {
+ TF_CONNECTION_OWNER_PROCESS = 0,
+ TF_CONNECTION_OWNER_KERNEL,
};
@@ -420,7 +426,7 @@ enum SCXLNX_COMMAND_STATE {
* A connection is created each time an application opens a file descriptor on
* the driver
*/
-struct SCXLNX_CONNECTION {
+struct tf_connection {
/*
* Identifies the connection in the list of the connections attached to
* the same device.
@@ -430,80 +436,88 @@ struct SCXLNX_CONNECTION {
/*
* State of the connection.
*/
- enum SCXLNX_CONN_STATE nState;
+ enum TF_CONN_STATE state;
/*
* A pointer to the corresponding device structure
*/
- struct SCXLNX_DEVICE *pDevice;
+ struct tf_device *dev;
/*
- * A spinlock to use to access nState
+ * A spinlock to use to access state
*/
- spinlock_t stateLock;
+ spinlock_t state_lock;
/*
* Counts the number of operations currently pending on the connection.
* (for debug only)
*/
- atomic_t nPendingOpCounter;
+ atomic_t pending_op_count;
/*
* A handle for the device context
*/
- u32 hDeviceContext;
+ u32 device_context;
/*
* Lists the used shared memory descriptors
*/
- struct list_head sUsedSharedMemoryList;
+ struct list_head used_shmem_list;
/*
* Lists the free shared memory descriptors
*/
- struct list_head sFreeSharedMemoryList;
+ struct list_head free_shmem_list;
/*
* A mutex to use to access this structure
*/
- struct mutex sharedMemoriesMutex;
+ struct mutex shmem_mutex;
/*
* Counts the number of shared memories registered.
*/
- atomic_t nShmemAllocated;
+ atomic_t shmem_count;
/*
* Page to retrieve memory properties when
* registering shared memory through REGISTER_SHARED_MEMORY
* messages
*/
- struct vm_area_struct **ppVmas;
+ struct vm_area_struct **vmas;
/*
* coarse page table allocation context
*/
- struct SCXLNX_COARSE_PAGE_TABLE_ALLOCATION_CONTEXT sAllocationContext;
+ struct tf_coarse_page_table_allocation_context cpt_alloc_context;
+
+ /* The origin of connection parameters such as login data and
+ memory reference pointers. */
+ enum TF_CONNECTION_OWNER owner;
-#ifdef CONFIG_TF_MSHIELD
+#ifdef CONFIG_TF_ZEBRA
/* Lists all the Cryptoki Update Shortcuts */
- struct list_head ShortcutList;
+ struct list_head shortcut_list;
+
+ /* Lock to protect concurrent accesses to shortcut_list */
+ spinlock_t shortcut_list_lock;
+#endif
- /* Lock to protect concurrent accesses to ShortcutList */
- spinlock_t shortcutListCriticalSectionLock;
+#ifdef CONFIG_TF_ION
+ struct ion_client *ion_client;
#endif
};
/*----------------------------------------------------------------------------*/
/*
- * The nOperationID field of a message points to this structure.
+ * The operation_id field of a message points to this structure.
* It is used to identify the thread that triggered the message transmission
* Whoever reads an answer can wake up that thread using the completion event
*/
-struct SCXLNX_ANSWER_STRUCT {
- bool bAnswerCopied;
- union SCX_ANSWER_MESSAGE *pAnswer;
+struct tf_answer_struct {
+ bool answer_copied;
+ union tf_answer *answer;
};
/*----------------------------------------------------------------------------*/
@@ -512,16 +526,16 @@ struct SCXLNX_ANSWER_STRUCT {
* The ASCII-C string representation of the base name of the devices managed by
* this driver.
*/
-#define SCXLNX_DEVICE_BASE_NAME "tf_driver"
+#define TF_DEVICE_BASE_NAME "tf_driver"
/**
* The major and minor numbers of the registered character device driver.
* Only 1 instance of the driver is supported.
*/
-#define SCXLNX_DEVICE_MINOR_NUMBER (0)
+#define TF_DEVICE_MINOR_NUMBER (0)
-struct SCXLNX_DEVICE *SCXLNXGetDevice(void);
+struct tf_device *tf_get_device(void);
#define CLEAN_CACHE_CFG_MASK (~0xC) /* 1111 0011 */
@@ -536,4 +550,4 @@ struct SCXLNX_DEVICE *SCXLNXGetDevice(void);
#define GROUP_INFO (current->group_info)
#endif
-#endif /* !defined(__SCXLNX_DEFS_H__) */
+#endif /* !defined(__TF_DEFS_H__) */
diff --git a/security/smc/tf_device.c b/security/smc/tf_device.c
new file mode 100644
index 0000000..7c2c623
--- /dev/null
+++ b/security/smc/tf_device.c
@@ -0,0 +1,728 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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 <asm/atomic.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/page-flags.h>
+#include <linux/pm.h>
+#include <linux/syscore_ops.h>
+#include <linux/vmalloc.h>
+#include <linux/signal.h>
+#ifdef CONFIG_ANDROID
+#include <linux/device.h>
+#endif
+
+#include "tf_protocol.h"
+#include "tf_defs.h"
+#include "tf_util.h"
+#include "tf_conn.h"
+#include "tf_comm.h"
+#ifdef CONFIG_TF_ZEBRA
+#include <plat/cpu.h>
+#include "tf_zebra.h"
+#endif
+
+#include "s_version.h"
+
+#ifdef CONFIG_TF_ION
+extern struct ion_device *omap_ion_device;
+#endif
+
+/*----------------------------------------------------------------------------
+ * Forward Declarations
+ *----------------------------------------------------------------------------*/
+
+/*
+ * Creates and registers the device to be managed by the specified driver.
+ *
+ * Returns zero upon successful completion, or an appropriate error code upon
+ * failure.
+ */
+static int tf_device_register(void);
+
+
+/*
+ * Implements the device Open callback.
+ */
+static int tf_device_open(
+ struct inode *inode,
+ struct file *file);
+
+
+/*
+ * Implements the device Release callback.
+ */
+static int tf_device_release(
+ struct inode *inode,
+ struct file *file);
+
+
+/*
+ * Implements the device ioctl callback.
+ */
+static long tf_device_ioctl(
+ struct file *file,
+ unsigned int ioctl_num,
+ unsigned long ioctl_param);
+
+
+/*
+ * Implements the device shutdown callback.
+ */
+static void tf_device_shutdown(void);
+
+
+/*
+ * Implements the device suspend callback.
+ */
+static int tf_device_suspend(void);
+
+
+/*
+ * Implements the device resume callback.
+ */
+static void tf_device_resume(void);
+
+
+/*---------------------------------------------------------------------------
+ * Module Parameters
+ *---------------------------------------------------------------------------*/
+
+/*
+ * The device major number used to register a unique character device driver.
+ * Let the default value be 122
+ */
+static int device_major_number = 122;
+
+module_param(device_major_number, int, 0000);
+MODULE_PARM_DESC(device_major_number,
+ "The device major number used to register a unique character "
+ "device driver");
+
+#ifdef CONFIG_TF_TRUSTZONE
+/**
+ * The softint interrupt line used by the Secure World.
+ */
+static int soft_interrupt = -1;
+
+module_param(soft_interrupt, int, 0000);
+MODULE_PARM_DESC(soft_interrupt,
+ "The softint interrupt line used by the Secure world");
+#endif
+
+#ifdef CONFIG_ANDROID
+static struct class *tf_class;
+#endif
+
+/*----------------------------------------------------------------------------
+ * Global Variables
+ *----------------------------------------------------------------------------*/
+
+/*
+ * tf_driver character device definitions.
+ * read and write methods are not defined
+ * and will return an error if used by user space
+ */
+static const struct file_operations g_tf_device_file_ops = {
+ .owner = THIS_MODULE,
+ .open = tf_device_open,
+ .release = tf_device_release,
+ .unlocked_ioctl = tf_device_ioctl,
+ .llseek = no_llseek,
+};
+
+/* The single device supported by this driver */
+static struct tf_device g_tf_dev = {0, };
+
+/*----------------------------------------------------------------------------
+ * Implementations
+ *----------------------------------------------------------------------------*/
+
+struct tf_device *tf_get_device(void)
+{
+ return &g_tf_dev;
+}
+
+/*
+ * displays the driver stats
+ */
+static ssize_t kobject_show(struct kobject *kobj,
+ struct attribute *attribute, char *buf)
+{
+ struct tf_device_stats *dev_stats = &g_tf_dev.stats;
+ u32 pages_allocated;
+ u32 pages_locked;
+ u32 memories_allocated;
+
+ memories_allocated =
+ atomic_read(&(dev_stats->stat_memories_allocated));
+ pages_allocated =
+ atomic_read(&(dev_stats->stat_pages_allocated));
+ pages_locked = atomic_read(&(dev_stats->stat_pages_locked));
+
+ /*
+ * AFY: could we add the number of context switches (call to the SMI
+ * instruction)
+ */
+
+ return snprintf(buf, PAGE_SIZE,
+ "stat.memories.allocated: %d\n"
+ "stat.pages.allocated: %d\n"
+ "stat.pages.locked: %d\n",
+ memories_allocated,
+ pages_allocated,
+ pages_locked);
+}
+
+static const struct sysfs_ops kobj_sysfs_operations = {
+ .show = kobject_show,
+};
+
+/*----------------------------------------------------------------------------*/
+
+static const struct syscore_ops g_tf_syscore_ops = {
+ .shutdown = tf_device_shutdown,
+ .suspend = tf_device_suspend,
+ .resume = tf_device_resume,
+};
+
+/*
+ * First routine called when the kernel module is loaded
+ */
+static int __init tf_device_register(void)
+{
+ int error;
+ struct tf_device *dev = &g_tf_dev;
+ struct tf_device_stats *dev_stats = &dev->stats;
+
+ dprintk(KERN_INFO "tf_device_register()\n");
+
+ /*
+ * Initialize the device
+ */
+ dev->dev_number = MKDEV(device_major_number,
+ TF_DEVICE_MINOR_NUMBER);
+ cdev_init(&dev->cdev, &g_tf_device_file_ops);
+ dev->cdev.owner = THIS_MODULE;
+
+ INIT_LIST_HEAD(&dev->connection_list);
+ spin_lock_init(&dev->connection_list_lock);
+
+ /* register the sysfs object driver stats */
+ dev_stats->kobj_type.sysfs_ops = &kobj_sysfs_operations;
+
+ dev_stats->kobj_stat_attribute.name = "info";
+ dev_stats->kobj_stat_attribute.mode = S_IRUGO;
+ dev_stats->kobj_attribute_list[0] =
+ &dev_stats->kobj_stat_attribute;
+
+ dev_stats->kobj_type.default_attrs =
+ dev_stats->kobj_attribute_list,
+ error = kobject_init_and_add(&(dev_stats->kobj),
+ &(dev_stats->kobj_type), NULL, "%s",
+ TF_DEVICE_BASE_NAME);
+ if (error) {
+ kobject_put(&dev_stats->kobj);
+ goto kobject_init_and_add_failed;
+ }
+
+ register_syscore_ops((struct syscore_ops *)&g_tf_syscore_ops);
+
+ /*
+ * Register the char device.
+ */
+ printk(KERN_INFO "Registering char device %s (%u:%u)\n",
+ TF_DEVICE_BASE_NAME,
+ MAJOR(dev->dev_number),
+ MINOR(dev->dev_number));
+ error = register_chrdev_region(dev->dev_number, 1,
+ TF_DEVICE_BASE_NAME);
+ if (error != 0) {
+ printk(KERN_ERR "tf_device_register():"
+ " register_chrdev_region failed (error %d)!\n",
+ error);
+ goto register_chrdev_region_failed;
+ }
+
+ error = cdev_add(&dev->cdev, dev->dev_number, 1);
+ if (error != 0) {
+ printk(KERN_ERR "tf_device_register(): "
+ "cdev_add failed (error %d)!\n",
+ error);
+ goto cdev_add_failed;
+ }
+
+ /*
+ * Initialize the communication with the Secure World.
+ */
+#ifdef CONFIG_TF_TRUSTZONE
+ dev->sm.soft_int_irq = soft_interrupt;
+#endif
+ error = tf_init(&g_tf_dev.sm);
+ if (error != S_SUCCESS) {
+ dprintk(KERN_ERR "tf_device_register(): "
+ "tf_init failed (error %d)!\n",
+ error);
+ goto init_failed;
+ }
+
+#ifdef CONFIG_ANDROID
+ tf_class = class_create(THIS_MODULE, TF_DEVICE_BASE_NAME);
+ device_create(tf_class, NULL,
+ dev->dev_number,
+ NULL, TF_DEVICE_BASE_NAME);
+#endif
+
+#ifdef CONFIG_TF_ZEBRA
+ /*
+ * Initializes the /dev/tf_ctrl device node.
+ */
+ error = tf_ctrl_device_register();
+ if (error)
+ goto init_failed;
+#endif
+
+#ifdef CONFIG_BENCH_SECURE_CYCLE
+ run_bogo_mips();
+ address_cache_property((unsigned long) &tf_device_register);
+#endif
+ /*
+ * Successful completion.
+ */
+
+ dprintk(KERN_INFO "tf_device_register(): Success\n");
+ return 0;
+
+ /*
+ * Error: undo all operations in the reverse order
+ */
+init_failed:
+ cdev_del(&dev->cdev);
+cdev_add_failed:
+ unregister_chrdev_region(dev->dev_number, 1);
+register_chrdev_region_failed:
+ unregister_syscore_ops((struct syscore_ops *)&g_tf_syscore_ops);
+kobject_init_and_add_failed:
+ kobject_del(&g_tf_dev.stats.kobj);
+
+ dprintk(KERN_INFO "tf_device_register(): Failure (error %d)\n",
+ error);
+ return error;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int tf_device_open(struct inode *inode, struct file *file)
+{
+ int error;
+ struct tf_device *dev = &g_tf_dev;
+ struct tf_connection *connection = NULL;
+
+ dprintk(KERN_INFO "tf_device_open(%u:%u, %p)\n",
+ imajor(inode), iminor(inode), file);
+
+ /* Dummy lseek for non-seekable driver */
+ error = nonseekable_open(inode, file);
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_device_open(%p): "
+ "nonseekable_open failed (error %d)!\n",
+ file, error);
+ goto error;
+ }
+
+#ifndef CONFIG_ANDROID
+ /*
+ * Check file flags. We only autthorize the O_RDWR access
+ */
+ if (file->f_flags != O_RDWR) {
+ dprintk(KERN_ERR "tf_device_open(%p): "
+ "Invalid access mode %u\n",
+ file, file->f_flags);
+ error = -EACCES;
+ goto error;
+ }
+#endif
+
+ /*
+ * Open a new connection.
+ */
+
+ error = tf_open(dev, file, &connection);
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_device_open(%p): "
+ "tf_open failed (error %d)!\n",
+ file, error);
+ goto error;
+ }
+
+ file->private_data = connection;
+
+ /*
+ * Send the CreateDeviceContext command to the secure
+ */
+ error = tf_create_device_context(connection);
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_device_open(%p): "
+ "tf_create_device_context failed (error %d)!\n",
+ file, error);
+ goto error1;
+ }
+
+ /*
+ * Successful completion.
+ */
+
+ dprintk(KERN_INFO "tf_device_open(%p): Success (connection=%p)\n",
+ file, connection);
+ return 0;
+
+ /*
+ * Error handling.
+ */
+
+error1:
+ tf_close(connection);
+error:
+ dprintk(KERN_INFO "tf_device_open(%p): Failure (error %d)\n",
+ file, error);
+ return error;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int tf_device_release(struct inode *inode, struct file *file)
+{
+ struct tf_connection *connection;
+
+ dprintk(KERN_INFO "tf_device_release(%u:%u, %p)\n",
+ imajor(inode), iminor(inode), file);
+
+ connection = tf_conn_from_file(file);
+ tf_close(connection);
+
+ dprintk(KERN_INFO "tf_device_release(%p): Success\n", file);
+ return 0;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static long tf_device_ioctl(struct file *file, unsigned int ioctl_num,
+ unsigned long ioctl_param)
+{
+ int result = S_SUCCESS;
+ struct tf_connection *connection;
+ union tf_command command;
+ struct tf_command_header header;
+ union tf_answer answer;
+ u32 command_size;
+ u32 answer_size;
+ void *user_answer;
+
+ dprintk(KERN_INFO "tf_device_ioctl(%p, %u, %p)\n",
+ file, ioctl_num, (void *) ioctl_param);
+
+ switch (ioctl_num) {
+ case IOCTL_TF_GET_VERSION:
+ /* ioctl is asking for the driver interface version */
+ result = TF_DRIVER_INTERFACE_VERSION;
+ goto exit;
+
+#ifdef CONFIG_TF_ION
+ case IOCTL_TF_ION_REGISTER: {
+ int ion_register;
+ /* ioctl is asking to register an ion handle */
+ if (copy_from_user(&ion_register,
+ (int *) ioctl_param,
+ sizeof(int))) {
+ dprintk(KERN_ERR "tf_device_ioctl(%p): "
+ "copy_from_user failed\n",
+ file);
+ result = -EFAULT;
+ goto exit;
+ }
+
+ connection = tf_conn_from_file(file);
+ BUG_ON(connection == NULL);
+
+ /* Initialize ION connection */
+ if (connection->ion_client == NULL) {
+ connection->ion_client = ion_client_create(
+ omap_ion_device,
+ (1 << ION_HEAP_TYPE_CARVEOUT),
+ "smc");
+ }
+
+ if (connection->ion_client == NULL) {
+ dprintk(KERN_ERR "tf_device_ioctl(%p): "
+ "unable to create ion client\n",
+ file);
+ result = -EFAULT;
+ goto exit;
+ }
+
+ /*
+ * TODO: We should use a reference count on this handle in order
+ * to not unregistered it while using it.
+ */
+ return (long)ion_import_fd(connection->ion_client, ion_register);
+ }
+
+ case IOCTL_TF_ION_UNREGISTER: {
+ int ion_register;
+ /* ioctl is asking to unregister an ion handle */
+
+ if (copy_from_user(&ion_register,
+ (int *) ioctl_param,
+ sizeof(int))) {
+ dprintk(KERN_ERR "tf_device_ioctl(%p): "
+ "copy_from_user failed\n",
+ file);
+ result = -EFAULT;
+ goto exit;
+ }
+
+ connection = tf_conn_from_file(file);
+ BUG_ON(connection == NULL);
+
+ if (connection->ion_client == NULL) {
+ dprintk(KERN_ERR "tf_device_ioctl(%p): "
+ "ion client does not exist\n",
+ file);
+ result = -EFAULT;
+ goto exit;
+ }
+
+ ion_free(connection->ion_client,
+ (struct ion_handle *) ion_register);
+
+ return S_SUCCESS;
+ }
+#endif
+
+ case IOCTL_TF_EXCHANGE:
+ /*
+ * ioctl is asking to perform a message exchange with the Secure
+ * Module
+ */
+
+ /*
+ * Make a local copy of the data from the user application
+ * This routine checks the data is readable
+ *
+ * Get the header first.
+ */
+ if (copy_from_user(&header,
+ (struct tf_command_header *)ioctl_param,
+ sizeof(struct tf_command_header))) {
+ dprintk(KERN_ERR "tf_device_ioctl(%p): "
+ "Cannot access ioctl parameter %p\n",
+ file, (void *) ioctl_param);
+ result = -EFAULT;
+ goto exit;
+ }
+
+ /* size in words of u32 */
+ command_size = header.message_size +
+ sizeof(struct tf_command_header)/sizeof(u32);
+ if (command_size > sizeof(command)/sizeof(u32)) {
+ dprintk(KERN_ERR "tf_device_ioctl(%p): "
+ "Buffer overflow: too many bytes to copy %d\n",
+ file, command_size);
+ result = -EFAULT;
+ goto exit;
+ }
+
+ if (copy_from_user(&command,
+ (union tf_command *)ioctl_param,
+ command_size * sizeof(u32))) {
+ dprintk(KERN_ERR "tf_device_ioctl(%p): "
+ "Cannot access ioctl parameter %p\n",
+ file, (void *) ioctl_param);
+ result = -EFAULT;
+ goto exit;
+ }
+
+ connection = tf_conn_from_file(file);
+ BUG_ON(connection == NULL);
+
+ /*
+ * The answer memory space address is in the operation_id field
+ */
+ user_answer = (void *) command.header.operation_id;
+
+ atomic_inc(&(connection->pending_op_count));
+
+ dprintk(KERN_WARNING "tf_device_ioctl(%p): "
+ "Sending message type 0x%08x\n",
+ file, command.header.message_type);
+
+ switch (command.header.message_type) {
+ case TF_MESSAGE_TYPE_OPEN_CLIENT_SESSION:
+ result = tf_open_client_session(connection,
+ &command, &answer);
+ break;
+
+ case TF_MESSAGE_TYPE_CLOSE_CLIENT_SESSION:
+ result = tf_close_client_session(connection,
+ &command, &answer);
+ break;
+
+ case TF_MESSAGE_TYPE_REGISTER_SHARED_MEMORY:
+ result = tf_register_shared_memory(connection,
+ &command, &answer);
+ break;
+
+ case TF_MESSAGE_TYPE_RELEASE_SHARED_MEMORY:
+ result = tf_release_shared_memory(connection,
+ &command, &answer);
+ break;
+
+ case TF_MESSAGE_TYPE_INVOKE_CLIENT_COMMAND:
+ result = tf_invoke_client_command(connection,
+ &command, &answer);
+ break;
+
+ case TF_MESSAGE_TYPE_CANCEL_CLIENT_COMMAND:
+ result = tf_cancel_client_command(connection,
+ &command, &answer);
+ break;
+
+ default:
+ dprintk(KERN_ERR "tf_device_ioctl(%p): "
+ "Incorrect message type (0x%08x)!\n",
+ connection, command.header.message_type);
+ result = -EOPNOTSUPP;
+ break;
+ }
+
+ atomic_dec(&(connection->pending_op_count));
+
+ if (result != 0) {
+ dprintk(KERN_WARNING "tf_device_ioctl(%p): "
+ "Operation returning error code 0x%08x)!\n",
+ file, result);
+ goto exit;
+ }
+
+ /*
+ * Copy the answer back to the user space application.
+ * The driver does not check this field, only copy back to user
+ * space the data handed over by Secure World
+ */
+ answer_size = answer.header.message_size +
+ sizeof(struct tf_answer_header)/sizeof(u32);
+ if (copy_to_user(user_answer,
+ &answer, answer_size * sizeof(u32))) {
+ dprintk(KERN_WARNING "tf_device_ioctl(%p): "
+ "Failed to copy back the full command "
+ "answer to %p\n", file, user_answer);
+ result = -EFAULT;
+ goto exit;
+ }
+
+ /* successful completion */
+ dprintk(KERN_INFO "tf_device_ioctl(%p): Success\n", file);
+ break;
+
+ case IOCTL_TF_GET_DESCRIPTION: {
+ /* ioctl asking for the version information buffer */
+ struct tf_version_information_buffer *pInfoBuffer;
+
+ dprintk(KERN_INFO "IOCTL_TF_GET_DESCRIPTION:(%p, %u, %p)\n",
+ file, ioctl_num, (void *) ioctl_param);
+
+ pInfoBuffer =
+ ((struct tf_version_information_buffer *) ioctl_param);
+
+ dprintk(KERN_INFO "IOCTL_TF_GET_DESCRIPTION1: "
+ "driver_description=\"%64s\"\n", S_VERSION_STRING);
+
+ if (copy_to_user(pInfoBuffer->driver_description,
+ S_VERSION_STRING,
+ strlen(S_VERSION_STRING) + 1)) {
+ dprintk(KERN_ERR "tf_device_ioctl(%p): "
+ "Fail to copy back the driver description "
+ "to %p\n",
+ file, pInfoBuffer->driver_description);
+ result = -EFAULT;
+ goto exit;
+ }
+
+ dprintk(KERN_INFO "IOCTL_TF_GET_DESCRIPTION2: "
+ "secure_world_description=\"%64s\"\n",
+ tf_get_description(&g_tf_dev.sm));
+
+ if (copy_to_user(pInfoBuffer->secure_world_description,
+ tf_get_description(&g_tf_dev.sm),
+ TF_DESCRIPTION_BUFFER_LENGTH)) {
+ dprintk(KERN_WARNING "tf_device_ioctl(%p): "
+ "Failed to copy back the secure world "
+ "description to %p\n",
+ file, pInfoBuffer->secure_world_description);
+ result = -EFAULT;
+ goto exit;
+ }
+ break;
+ }
+
+ default:
+ dprintk(KERN_ERR "tf_device_ioctl(%p): "
+ "Unknown IOCTL code 0x%08x!\n",
+ file, ioctl_num);
+ result = -EOPNOTSUPP;
+ goto exit;
+ }
+
+exit:
+ return result;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static void tf_device_shutdown(void)
+{
+ tf_power_management(&g_tf_dev.sm, TF_POWER_OPERATION_SHUTDOWN);
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int tf_device_suspend(void)
+{
+ dprintk(KERN_INFO "%s\n", __func__);
+ return tf_power_management(&g_tf_dev.sm, TF_POWER_OPERATION_HIBERNATE);
+}
+
+
+/*----------------------------------------------------------------------------*/
+
+static void tf_device_resume(void)
+{
+ tf_power_management(&g_tf_dev.sm, TF_POWER_OPERATION_RESUME);
+}
+
+
+/*----------------------------------------------------------------------------*/
+
+module_init(tf_device_register);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Trusted Logic S.A.");
diff --git a/security/smc/tf_device_mshield.c b/security/smc/tf_device_mshield.c
new file mode 100644
index 0000000..17f1451
--- /dev/null
+++ b/security/smc/tf_device_mshield.c
@@ -0,0 +1,351 @@
+/**
+ * Copyright (c) 2010 Trusted Logic S.A.
+ * 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 <asm/atomic.h>
+#include <linux/uaccess.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/page-flags.h>
+#include <linux/pm.h>
+#include <linux/sysdev.h>
+#include <linux/vmalloc.h>
+#include <linux/signal.h>
+#ifdef CONFIG_ANDROID
+#include <linux/device.h>
+#endif
+#include <linux/init.h>
+#include <linux/bootmem.h>
+
+#include "tf_protocol.h"
+#include "tf_defs.h"
+#include "tf_util.h"
+#include "tf_conn.h"
+#include "tf_comm.h"
+#include "tf_zebra.h"
+
+#include "s_version.h"
+
+#define TF_PA_CTRL_START 0x1
+#define TF_PA_CTRL_STOP 0x2
+
+#ifdef CONFIG_ANDROID
+static struct class *tf_ctrl_class;
+#endif
+
+#define TF_DEVICE_CTRL_BASE_NAME "tf_ctrl"
+
+struct tf_pa_ctrl {
+ u32 nPACommand;
+
+ u32 pa_size;
+ u8 *pa_buffer;
+
+ u32 conf_size;
+ u8 *conf_buffer;
+};
+
+static int tf_ctrl_check_omap_type(void)
+{
+ /* No need to do anything on a GP device */
+ switch (omap_type()) {
+ case OMAP2_DEVICE_TYPE_GP:
+ dprintk(KERN_INFO "SMC: Running on a GP device\n");
+ return 0;
+
+ case OMAP2_DEVICE_TYPE_EMU:
+ case OMAP2_DEVICE_TYPE_SEC:
+ /*case OMAP2_DEVICE_TYPE_TEST:*/
+ dprintk(KERN_INFO "SMC: Running on a EMU or HS device\n");
+ return 1;
+
+ default:
+ printk(KERN_ERR "SMC: unknown omap type %x\n", omap_type());
+ return -EFAULT;
+ }
+}
+
+#define IOCTL_TF_PA_CTRL _IOWR('z', 0xFF, struct tf_pa_ctrl)
+
+static long tf_ctrl_device_ioctl(struct file *file, unsigned int ioctl_num,
+ unsigned long ioctl_param)
+{
+ int result = S_SUCCESS;
+ struct tf_pa_ctrl pa_ctrl;
+ u8 *pa_buffer = NULL;
+ u8 *conf_buffer = NULL;
+ struct tf_device *dev = tf_get_device();
+
+ dprintk(KERN_INFO "tf_ctrl_device_ioctl(%p, %u, %p)\n",
+ file, ioctl_num, (void *) ioctl_param);
+
+ mutex_lock(&dev->dev_mutex);
+
+ if (ioctl_num != IOCTL_TF_PA_CTRL) {
+ dprintk(KERN_ERR "tf_ctrl_device_ioctl(%p): "
+ "ioctl number is invalid (%p)\n",
+ file, (void *)ioctl_num);
+
+ result = -EFAULT;
+ goto exit;
+ }
+
+ if ((ioctl_param & 0x3) != 0) {
+ dprintk(KERN_ERR "tf_ctrl_device_ioctl(%p): "
+ "ioctl command message pointer is not word "
+ "aligned (%p)\n",
+ file, (void *)ioctl_param);
+
+ result = -EFAULT;
+ goto exit;
+ }
+
+ if (copy_from_user(&pa_ctrl, (struct tf_pa_ctrl *)ioctl_param,
+ sizeof(struct tf_pa_ctrl))) {
+ dprintk(KERN_ERR "tf_ctrl_device_ioctl(%p): "
+ "cannot access ioctl parameter (%p)\n",
+ file, (void *)ioctl_param);
+
+ result = -EFAULT;
+ goto exit;
+ }
+
+ switch (pa_ctrl.nPACommand) {
+ case TF_PA_CTRL_START:
+ dprintk(KERN_INFO "tf_ctrl_device_ioctl(%p): "
+ "Start the SMC PA (%d bytes) with conf (%d bytes)\n",
+ file, pa_ctrl.pa_size, pa_ctrl.conf_size);
+
+ pa_buffer = (u8 *) internal_kmalloc(pa_ctrl.pa_size,
+ GFP_KERNEL);
+ if (pa_buffer == NULL) {
+ dprintk(KERN_ERR "tf_ctrl_device_ioctl(%p): "
+ "Out of memory for PA buffer\n", file);
+
+ result = -ENOMEM;
+ goto exit;
+ }
+
+ if (copy_from_user(
+ pa_buffer, pa_ctrl.pa_buffer, pa_ctrl.pa_size)) {
+ dprintk(KERN_ERR "tf_ctrl_device_ioctl(%p): "
+ "Cannot access PA buffer (%p)\n",
+ file, (void *) pa_ctrl.pa_buffer);
+
+ internal_kfree(pa_buffer);
+
+ result = -EFAULT;
+ goto exit;
+ }
+
+ if (pa_ctrl.conf_size > 0) {
+ conf_buffer = (u8 *) internal_kmalloc(
+ pa_ctrl.conf_size, GFP_KERNEL);
+ if (conf_buffer == NULL) {
+ internal_kfree(pa_buffer);
+
+ result = -ENOMEM;
+ goto exit;
+ }
+
+ if (copy_from_user(conf_buffer,
+ pa_ctrl.conf_buffer, pa_ctrl.conf_size)) {
+ internal_kfree(pa_buffer);
+ internal_kfree(conf_buffer);
+
+ result = -EFAULT;
+ goto exit;
+ }
+ }
+
+ if (dev->workspace_addr == 0) {
+ result = -ENOMEM;
+ goto exit;
+ }
+
+ result = tf_start(&dev->sm,
+ dev->workspace_addr,
+ dev->workspace_size,
+ pa_buffer,
+ pa_ctrl.pa_size,
+ conf_buffer,
+ pa_ctrl.conf_size);
+ if (result)
+ dprintk(KERN_ERR "SMC: start failed\n");
+ else
+ dprintk(KERN_INFO "SMC: started\n");
+
+ internal_kfree(pa_buffer);
+ internal_kfree(conf_buffer);
+ break;
+
+ case TF_PA_CTRL_STOP:
+ dprintk(KERN_INFO "tf_ctrl_device_ioctl(%p): "
+ "Stop the SMC PA\n", file);
+
+ result = tf_power_management(&dev->sm,
+ TF_POWER_OPERATION_SHUTDOWN);
+ if (result)
+ dprintk(KERN_WARNING "SMC: stop failed [0x%x]\n",
+ result);
+ else
+ dprintk(KERN_INFO "SMC: stopped\n");
+ break;
+
+ default:
+ result = -EOPNOTSUPP;
+ break;
+ }
+
+exit:
+ mutex_unlock(&dev->dev_mutex);
+ return result;
+}
+
+/*----------------------------------------------------------------------------*/
+
+static int tf_ctrl_device_open(struct inode *inode, struct file *file)
+{
+ int error;
+
+ dprintk(KERN_INFO "tf_ctrl_device_open(%u:%u, %p)\n",
+ imajor(inode), iminor(inode), file);
+
+ /* Dummy lseek for non-seekable driver */
+ error = nonseekable_open(inode, file);
+ if (error != 0) {
+ dprintk(KERN_ERR "tf_ctrl_device_open(%p): "
+ "nonseekable_open failed (error %d)!\n",
+ file, error);
+ goto error;
+ }
+
+#ifndef CONFIG_ANDROID
+ /*
+ * Check file flags. We only autthorize the O_RDWR access
+ */
+ if (file->f_flags != O_RDWR) {
+ dprintk(KERN_ERR "tf_ctrl_device_open(%p): "
+ "Invalid access mode %u\n",
+ file, file->f_flags);
+ error = -EACCES;
+ goto error;
+ }
+#endif
+
+ error = tf_ctrl_check_omap_type();
+ if (error <= 0)
+ return error;
+
+ /*
+ * Successful completion.
+ */
+
+ dprintk(KERN_INFO "tf_ctrl_device_open(%p): Success\n", file);
+ return 0;
+
+ /*
+ * Error handling.
+ */
+error:
+ dprintk(KERN_INFO "tf_ctrl_device_open(%p): Failure (error %d)\n",
+ file, error);
+ return error;
+}
+
+static const struct file_operations g_tf_ctrl_device_file_ops = {
+ .owner = THIS_MODULE,
+ .open = tf_ctrl_device_open,
+ .unlocked_ioctl = tf_ctrl_device_ioctl,
+ .llseek = no_llseek,
+};
+
+int __init tf_ctrl_device_register(void)
+{
+ int error;
+ struct tf_device *dev = tf_get_device();
+
+ cdev_init(&dev->cdev_ctrl, &g_tf_ctrl_device_file_ops);
+ dev->cdev_ctrl.owner = THIS_MODULE;
+
+ error = register_chrdev_region(dev->dev_number + 1, 1,
+ TF_DEVICE_CTRL_BASE_NAME);
+ if (error)
+ return error;
+
+ error = cdev_add(&dev->cdev_ctrl,
+ dev->dev_number + 1, 1);
+ if (error) {
+ cdev_del(&(dev->cdev_ctrl));
+ unregister_chrdev_region(dev->dev_number + 1, 1);
+ return error;
+ }
+
+#ifdef CONFIG_ANDROID
+ tf_ctrl_class = class_create(THIS_MODULE, TF_DEVICE_CTRL_BASE_NAME);
+ device_create(tf_ctrl_class, NULL,
+ dev->dev_number + 1,
+ NULL, TF_DEVICE_CTRL_BASE_NAME);
+#endif
+
+ mutex_init(&dev->dev_mutex);
+
+ return error;
+}
+
+static int __initdata smc_mem;
+static int __initdata smc_address;
+
+void __init tf_allocate_workspace(void)
+{
+ struct tf_device *dev = tf_get_device();
+
+ if (tf_ctrl_check_omap_type() <= 0)
+ return;
+
+ dev->workspace_size = smc_mem;
+ if (dev->workspace_size < 3*SZ_1M)
+ dev->workspace_size = 3*SZ_1M;
+
+ if (smc_address == 0)
+#if 0
+ dev->workspace_addr = (u32) __pa(__alloc_bootmem(
+ dev->workspace_size, SZ_1M, __pa(MAX_DMA_ADDRESS)));
+#else
+ dev->workspace_addr = (u32) 0xBFD00000;
+#endif
+ else
+ dev->workspace_addr = smc_address;
+
+ pr_info("SMC: Allocated workspace of 0x%x Bytes at (0x%x)\n",
+ dev->workspace_size,
+ dev->workspace_addr);
+}
+
+static int __init tf_mem_setup(char *str)
+{
+ smc_mem = memparse(str, &str);
+ if (*str == '@') {
+ str += 1;
+ get_option(&str, &smc_address);
+ }
+ return 0;
+}
+
+early_param("smc_mem", tf_mem_setup);
diff --git a/security/smc/tf_dma.c b/security/smc/tf_dma.c
new file mode 100644
index 0000000..a424dbb
--- /dev/null
+++ b/security/smc/tf_dma.c
@@ -0,0 +1,106 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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 "tf_defs.h"
+#include "tf_util.h"
+#include "tf_dma.h"
+
+#include <asm/atomic.h>
+
+static atomic_t g_dmaEventFlag = ATOMIC_INIT(0);
+
+/*------------------------------------------------------------------------ */
+/*
+ * Internal functions
+ */
+
+static void tf_dma_callback(int lch, u16 ch_status, void *data)
+{
+ atomic_inc(&g_dmaEventFlag);
+}
+
+/*------------------------------------------------------------------------ */
+/*
+ * Public DMA API
+ */
+
+u32 tf_dma_request(int *lch)
+{
+ int dma_ch_out = 0;
+
+ if (lch == NULL)
+ return PUBLIC_CRYPTO_ERR_BAD_PARAMETERS;
+
+ if (omap_request_dma(0, "SMC Public Crypto",
+ tf_dma_callback, NULL, &dma_ch_out) != 0)
+ return PUBLIC_CRYPTO_ERR_OUT_OF_MEMORY;
+
+ omap_disable_dma_irq(dma_ch_out, OMAP_DMA_DROP_IRQ |
+ OMAP_DMA_BLOCK_IRQ);
+
+ *lch = dma_ch_out;
+
+ return PUBLIC_CRYPTO_OPERATION_SUCCESS;
+}
+
+/*------------------------------------------------------------------------ */
+
+void tf_dma_start(int lch, int interrupt_mask)
+{
+ atomic_set(&g_dmaEventFlag, 0);
+ omap_enable_dma_irq(lch, interrupt_mask);
+ omap_start_dma(lch);
+}
+
+/*------------------------------------------------------------------------ */
+
+void tf_dma_wait(int nr_of_cb)
+{
+ while (atomic_read(&g_dmaEventFlag) < nr_of_cb)
+ cpu_relax();
+}
+
+/*------------------------------------------------------------------------ */
+/*
+ * Perform common DMA channel setup, used to factorize the code
+ *
+ * Output: struct omap_dma_channel_params *dma_channel
+ * Inputs: u32 nb_blocks Number of block of the transfer
+ * u32 nb_elements Number of elements of the transfer
+ * u32 dst_start Destination address
+ * u32 src_start Source address
+ * u32 trigger_id Trigger ID
+ */
+void tf_dma_set_channel_common_params(
+ struct omap_dma_channel_params *dma_channel,
+ u32 nb_blocks, u32 nb_elements,
+ u32 dst_start, u32 src_start, u32 trigger_id)
+{
+ dma_channel->data_type = OMAP_DMA_DATA_TYPE_S32;
+ dma_channel->elem_count = nb_elements;
+ dma_channel->frame_count = nb_blocks;
+ dma_channel->src_ei = 0;
+ dma_channel->src_fi = 0;
+ dma_channel->dst_ei = 0;
+ dma_channel->dst_fi = 0;
+ dma_channel->sync_mode = OMAP_DMA_SYNC_FRAME;
+ dma_channel->src_start = src_start;
+ dma_channel->dst_start = dst_start;
+ dma_channel->trigger = trigger_id;
+}
diff --git a/security/smc/tf_dma.h b/security/smc/tf_dma.h
new file mode 100644
index 0000000..3492241
--- /dev/null
+++ b/security/smc/tf_dma.h
@@ -0,0 +1,64 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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
+ */
+
+#ifndef __TF_PUBLIC_DMA_H
+#define __TF_PUBLIC_DMA_H
+
+#include <linux/dma-mapping.h>
+#include <plat/dma.h>
+#include <plat/dma-44xx.h>
+
+#include "tf_crypto.h"
+
+/*-------------------------------------------------------------------------- */
+/*
+ * Public DMA API
+ */
+
+/*
+ * CEN Masks
+ */
+#define DMA_CEN_Elts_per_Frame_AES 4
+#define DMA_CEN_Elts_per_Frame_DES 2
+#define DMA_CEN_Elts_per_Frame_SHA 16
+
+/*
+ * Request a DMA channel
+ */
+u32 tf_dma_request(int *lch);
+
+/**
+ * This function waits for the DMA IRQ.
+ */
+void tf_dma_wait(int nr_of_cb);
+
+/*
+ * This function starts a DMA operation.
+ *
+ * lch DMA channel ID.
+ * interrupt_mask Configures the Channel Interrupt Control Register.
+ */
+void tf_dma_start(int lch, int interrupt_mask);
+
+void tf_dma_set_channel_common_params(
+ struct omap_dma_channel_params *dma_channel,
+ u32 nb_blocks, u32 nb_elements, u32 dst_start,
+ u32 src_start, u32 trigger_id);
+
+#endif /*__TF_PUBLIC_DMA_H */
diff --git a/security/smc/tf_protocol.h b/security/smc/tf_protocol.h
new file mode 100644
index 0000000..e3e6485
--- /dev/null
+++ b/security/smc/tf_protocol.h
@@ -0,0 +1,674 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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
+ */
+
+#ifndef __TF_PROTOCOL_H__
+#define __TF_PROTOCOL_H__
+
+/*----------------------------------------------------------------------------
+ *
+ * This header file defines the structure used in the SChannel Protocol.
+ * See your Product Reference Manual for a specification of the SChannel
+ * protocol.
+ *---------------------------------------------------------------------------*/
+
+/*
+ * The driver interface version returned by the version ioctl
+ */
+#define TF_DRIVER_INTERFACE_VERSION 0x04000000
+
+/*
+ * Protocol version handling
+ */
+#define TF_S_PROTOCOL_MAJOR_VERSION (0x06)
+#define GET_PROTOCOL_MAJOR_VERSION(a) (a >> 24)
+#define GET_PROTOCOL_MINOR_VERSION(a) ((a >> 16) & 0xFF)
+
+/*
+ * The S flag of the config_flag_s register.
+ */
+#define TF_CONFIG_FLAG_S (1 << 3)
+
+/*
+ * The TimeSlot field of the sync_serial_n register.
+ */
+#define TF_SYNC_SERIAL_TIMESLOT_N (1)
+
+/*
+ * status_s related defines.
+ */
+#define TF_STATUS_P_MASK (0X00000001)
+#define TF_STATUS_POWER_STATE_SHIFT (3)
+#define TF_STATUS_POWER_STATE_MASK (0x1F << TF_STATUS_POWER_STATE_SHIFT)
+
+/*
+ * Possible power states of the POWER_STATE field of the status_s register
+ */
+#define TF_POWER_MODE_COLD_BOOT (0)
+#define TF_POWER_MODE_WARM_BOOT (1)
+#define TF_POWER_MODE_ACTIVE (3)
+#define TF_POWER_MODE_READY_TO_SHUTDOWN (5)
+#define TF_POWER_MODE_READY_TO_HIBERNATE (7)
+#define TF_POWER_MODE_WAKEUP (8)
+#define TF_POWER_MODE_PANIC (15)
+
+/*
+ * Possible command values for MANAGEMENT commands
+ */
+#define TF_MANAGEMENT_HIBERNATE (1)
+#define TF_MANAGEMENT_SHUTDOWN (2)
+#define TF_MANAGEMENT_PREPARE_FOR_CORE_OFF (3)
+#define TF_MANAGEMENT_RESUME_FROM_CORE_OFF (4)
+
+/*
+ * The capacity of the Normal Word message queue, in number of slots.
+ */
+#define TF_N_MESSAGE_QUEUE_CAPACITY (512)
+
+/*
+ * The capacity of the Secure World message answer queue, in number of slots.
+ */
+#define TF_S_ANSWER_QUEUE_CAPACITY (256)
+
+/*
+ * The value of the S-timeout register indicating an infinite timeout.
+ */
+#define TF_S_TIMEOUT_0_INFINITE (0xFFFFFFFF)
+#define TF_S_TIMEOUT_1_INFINITE (0xFFFFFFFF)
+
+/*
+ * The value of the S-timeout register indicating an immediate timeout.
+ */
+#define TF_S_TIMEOUT_0_IMMEDIATE (0x0)
+#define TF_S_TIMEOUT_1_IMMEDIATE (0x0)
+
+/*
+ * Identifies the get protocol version SMC.
+ */
+#define TF_SMC_GET_PROTOCOL_VERSION (0XFFFFFFFB)
+
+/*
+ * Identifies the init SMC.
+ */
+#define TF_SMC_INIT (0XFFFFFFFF)
+
+/*
+ * Identifies the reset irq SMC.
+ */
+#define TF_SMC_RESET_IRQ (0xFFFFFFFE)
+
+/*
+ * Identifies the SET_W3B SMC.
+ */
+#define TF_SMC_WAKE_UP (0xFFFFFFFD)
+
+/*
+ * Identifies the STOP SMC.
+ */
+#define TF_SMC_STOP (0xFFFFFFFC)
+
+/*
+ * Identifies the n-yield SMC.
+ */
+#define TF_SMC_N_YIELD (0X00000003)
+
+
+/* Possible stop commands for SMC_STOP */
+#define SCSTOP_HIBERNATE (0xFFFFFFE1)
+#define SCSTOP_SHUTDOWN (0xFFFFFFE2)
+
+/*
+ * representation of an UUID.
+ */
+struct tf_uuid {
+ u32 time_low;
+ u16 time_mid;
+ u16 time_hi_and_version;
+ u8 clock_seq_and_node[8];
+};
+
+
+/**
+ * Command parameters.
+ */
+struct tf_command_param_value {
+ u32 a;
+ u32 b;
+};
+
+struct tf_command_param_temp_memref {
+ u32 descriptor; /* data pointer for exchange message.*/
+ u32 size;
+ u32 offset;
+};
+
+struct tf_command_param_memref {
+ u32 block;
+ u32 size;
+ u32 offset;
+};
+
+union tf_command_param {
+ struct tf_command_param_value value;
+ struct tf_command_param_temp_memref temp_memref;
+ struct tf_command_param_memref memref;
+};
+
+/**
+ * Answer parameters.
+ */
+struct tf_answer_param_value {
+ u32 a;
+ u32 b;
+};
+
+struct tf_answer_param_size {
+ u32 _ignored;
+ u32 size;
+};
+
+union tf_answer_param {
+ struct tf_answer_param_size size;
+ struct tf_answer_param_value value;
+};
+
+/*
+ * Descriptor tables capacity
+ */
+#define TF_MAX_W3B_COARSE_PAGES (2)
+#define TF_MAX_COARSE_PAGES (8)
+#define TF_DESCRIPTOR_TABLE_CAPACITY_BIT_SHIFT (8)
+#define TF_DESCRIPTOR_TABLE_CAPACITY \
+ (1 << TF_DESCRIPTOR_TABLE_CAPACITY_BIT_SHIFT)
+#define TF_DESCRIPTOR_TABLE_CAPACITY_MASK \
+ (TF_DESCRIPTOR_TABLE_CAPACITY - 1)
+/* Shared memories coarse pages can map up to 1MB */
+#define TF_MAX_COARSE_PAGE_MAPPED_SIZE \
+ (PAGE_SIZE * TF_DESCRIPTOR_TABLE_CAPACITY)
+/* Shared memories cannot exceed 8MB */
+#define TF_MAX_SHMEM_SIZE \
+ (TF_MAX_COARSE_PAGE_MAPPED_SIZE << 3)
+
+/*
+ * Buffer size for version description fields
+ */
+#define TF_DESCRIPTION_BUFFER_LENGTH 64
+
+/*
+ * Shared memory type flags.
+ */
+#define TF_SHMEM_TYPE_READ (0x00000001)
+#define TF_SHMEM_TYPE_WRITE (0x00000002)
+
+/*
+ * Shared mem flags
+ */
+#define TF_SHARED_MEM_FLAG_INPUT 1
+#define TF_SHARED_MEM_FLAG_OUTPUT 2
+#define TF_SHARED_MEM_FLAG_INOUT 3
+
+
+/*
+ * Parameter types
+ */
+#define TF_PARAM_TYPE_NONE 0x0
+#define TF_PARAM_TYPE_VALUE_INPUT 0x1
+#define TF_PARAM_TYPE_VALUE_OUTPUT 0x2
+#define TF_PARAM_TYPE_VALUE_INOUT 0x3
+#define TF_PARAM_TYPE_MEMREF_TEMP_INPUT 0x5
+#define TF_PARAM_TYPE_MEMREF_TEMP_OUTPUT 0x6
+#define TF_PARAM_TYPE_MEMREF_TEMP_INOUT 0x7
+#define TF_PARAM_TYPE_MEMREF_ION_HANDLE 0xB
+#define TF_PARAM_TYPE_MEMREF_INPUT 0xD
+#define TF_PARAM_TYPE_MEMREF_OUTPUT 0xE
+#define TF_PARAM_TYPE_MEMREF_INOUT 0xF
+
+#define TF_PARAM_TYPE_MEMREF_FLAG 0x4
+#define TF_PARAM_TYPE_REGISTERED_MEMREF_FLAG 0x8
+
+
+#define TF_MAKE_PARAM_TYPES(t0, t1, t2, t3) \
+ ((t0) | ((t1) << 4) | ((t2) << 8) | ((t3) << 12))
+#define TF_GET_PARAM_TYPE(t, i) (((t) >> (4 * i)) & 0xF)
+
+/*
+ * Login types.
+ */
+#define TF_LOGIN_PUBLIC 0x00000000
+#define TF_LOGIN_USER 0x00000001
+#define TF_LOGIN_GROUP 0x00000002
+#define TF_LOGIN_APPLICATION 0x00000004
+#define TF_LOGIN_APPLICATION_USER 0x00000005
+#define TF_LOGIN_APPLICATION_GROUP 0x00000006
+#define TF_LOGIN_AUTHENTICATION 0x80000000
+#define TF_LOGIN_PRIVILEGED 0x80000002
+
+/* Login variants */
+
+#define TF_LOGIN_VARIANT(main_type, os, variant) \
+ ((main_type) | (1 << 27) | ((os) << 16) | ((variant) << 8))
+
+#define TF_LOGIN_GET_MAIN_TYPE(type) \
+ ((type) & ~TF_LOGIN_VARIANT(0, 0xFF, 0xFF))
+
+#define TF_LOGIN_OS_ANY 0x00
+#define TF_LOGIN_OS_LINUX 0x01
+#define TF_LOGIN_OS_ANDROID 0x04
+
+/* OS-independent variants */
+#define TF_LOGIN_USER_NONE \
+ TF_LOGIN_VARIANT(TF_LOGIN_USER, TF_LOGIN_OS_ANY, 0xFF)
+#define TF_LOGIN_GROUP_NONE \
+ TF_LOGIN_VARIANT(TF_LOGIN_GROUP, TF_LOGIN_OS_ANY, 0xFF)
+#define TF_LOGIN_APPLICATION_USER_NONE \
+ TF_LOGIN_VARIANT(TF_LOGIN_APPLICATION_USER, TF_LOGIN_OS_ANY, 0xFF)
+#define TF_LOGIN_AUTHENTICATION_BINARY_SHA1_HASH \
+ TF_LOGIN_VARIANT(TF_LOGIN_AUTHENTICATION, TF_LOGIN_OS_ANY, 0x01)
+#define TF_LOGIN_PRIVILEGED_KERNEL \
+ TF_LOGIN_VARIANT(TF_LOGIN_PRIVILEGED, TF_LOGIN_OS_ANY, 0x01)
+
+/* Linux variants */
+#define TF_LOGIN_USER_LINUX_EUID \
+ TF_LOGIN_VARIANT(TF_LOGIN_USER, TF_LOGIN_OS_LINUX, 0x01)
+#define TF_LOGIN_GROUP_LINUX_GID \
+ TF_LOGIN_VARIANT(TF_LOGIN_GROUP, TF_LOGIN_OS_LINUX, 0x01)
+#define TF_LOGIN_APPLICATION_LINUX_PATH_SHA1_HASH \
+ TF_LOGIN_VARIANT(TF_LOGIN_APPLICATION, TF_LOGIN_OS_LINUX, 0x01)
+#define TF_LOGIN_APPLICATION_USER_LINUX_PATH_EUID_SHA1_HASH \
+ TF_LOGIN_VARIANT(TF_LOGIN_APPLICATION_USER, TF_LOGIN_OS_LINUX, 0x01)
+#define TF_LOGIN_APPLICATION_GROUP_LINUX_PATH_GID_SHA1_HASH \
+ TF_LOGIN_VARIANT(TF_LOGIN_APPLICATION_GROUP, TF_LOGIN_OS_LINUX, 0x01)
+
+/* Android variants */
+#define TF_LOGIN_USER_ANDROID_EUID \
+ TF_LOGIN_VARIANT(TF_LOGIN_USER, TF_LOGIN_OS_ANDROID, 0x01)
+#define TF_LOGIN_GROUP_ANDROID_GID \
+ TF_LOGIN_VARIANT(TF_LOGIN_GROUP, TF_LOGIN_OS_ANDROID, 0x01)
+#define TF_LOGIN_APPLICATION_ANDROID_UID \
+ TF_LOGIN_VARIANT(TF_LOGIN_APPLICATION, TF_LOGIN_OS_ANDROID, 0x01)
+#define TF_LOGIN_APPLICATION_USER_ANDROID_UID_EUID \
+ TF_LOGIN_VARIANT(TF_LOGIN_APPLICATION_USER, TF_LOGIN_OS_ANDROID, \
+ 0x01)
+#define TF_LOGIN_APPLICATION_GROUP_ANDROID_UID_GID \
+ TF_LOGIN_VARIANT(TF_LOGIN_APPLICATION_GROUP, TF_LOGIN_OS_ANDROID, \
+ 0x01)
+
+/*
+ * return origins
+ */
+#define TF_ORIGIN_COMMS 2
+#define TF_ORIGIN_TEE 3
+#define TF_ORIGIN_TRUSTED_APP 4
+/*
+ * The message types.
+ */
+#define TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT 0x02
+#define TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT 0xFD
+#define TF_MESSAGE_TYPE_REGISTER_SHARED_MEMORY 0xF7
+#define TF_MESSAGE_TYPE_RELEASE_SHARED_MEMORY 0xF9
+#define TF_MESSAGE_TYPE_OPEN_CLIENT_SESSION 0xF0
+#define TF_MESSAGE_TYPE_CLOSE_CLIENT_SESSION 0xF2
+#define TF_MESSAGE_TYPE_INVOKE_CLIENT_COMMAND 0xF5
+#define TF_MESSAGE_TYPE_CANCEL_CLIENT_COMMAND 0xF4
+#define TF_MESSAGE_TYPE_MANAGEMENT 0xFE
+
+
+/*
+ * The SChannel error codes.
+ */
+#define S_SUCCESS 0x00000000
+#define S_ERROR_OUT_OF_MEMORY 0xFFFF000C
+
+
+struct tf_command_header {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info;
+ u32 operation_id;
+};
+
+struct tf_answer_header {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info;
+ u32 operation_id;
+ u32 error_code;
+};
+
+/*
+ * CREATE_DEVICE_CONTEXT command message.
+ */
+struct tf_command_create_device_context {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info_rfu;
+ u32 operation_id;
+ u32 device_context_id;
+};
+
+/*
+ * CREATE_DEVICE_CONTEXT answer message.
+ */
+struct tf_answer_create_device_context {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info_rfu;
+ /* an opaque Normal World identifier for the operation */
+ u32 operation_id;
+ u32 error_code;
+ /* an opaque Normal World identifier for the device context */
+ u32 device_context;
+};
+
+/*
+ * DESTROY_DEVICE_CONTEXT command message.
+ */
+struct tf_command_destroy_device_context {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info_rfu;
+ u32 operation_id;
+ u32 device_context;
+};
+
+/*
+ * DESTROY_DEVICE_CONTEXT answer message.
+ */
+struct tf_answer_destroy_device_context {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info_rfu;
+ /* an opaque Normal World identifier for the operation */
+ u32 operation_id;
+ u32 error_code;
+ u32 device_context_id;
+};
+
+/*
+ * OPEN_CLIENT_SESSION command message.
+ */
+struct tf_command_open_client_session {
+ u8 message_size;
+ u8 message_type;
+ u16 param_types;
+ /* an opaque Normal World identifier for the operation */
+ u32 operation_id;
+ u32 device_context;
+ u32 cancellation_id;
+ u64 timeout;
+ struct tf_uuid destination_uuid;
+ union tf_command_param params[4];
+ u32 login_type;
+ /*
+ * Size = 0 for public, [16] for group identification, [20] for
+ * authentication
+ */
+ u8 login_data[20];
+};
+
+/*
+ * OPEN_CLIENT_SESSION answer message.
+ */
+struct tf_answer_open_client_session {
+ u8 message_size;
+ u8 message_type;
+ u8 error_origin;
+ u8 __reserved;
+ /* an opaque Normal World identifier for the operation */
+ u32 operation_id;
+ u32 error_code;
+ u32 client_session;
+ union tf_answer_param answers[4];
+};
+
+/*
+ * CLOSE_CLIENT_SESSION command message.
+ */
+struct tf_command_close_client_session {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info_rfu;
+ /* an opaque Normal World identifier for the operation */
+ u32 operation_id;
+ u32 device_context;
+ u32 client_session;
+};
+
+/*
+ * CLOSE_CLIENT_SESSION answer message.
+ */
+struct tf_answer_close_client_session {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info_rfu;
+ /* an opaque Normal World identifier for the operation */
+ u32 operation_id;
+ u32 error_code;
+};
+
+
+/*
+ * REGISTER_SHARED_MEMORY command message
+ */
+struct tf_command_register_shared_memory {
+ u8 message_size;
+ u8 message_type;
+ u16 memory_flags;
+ u32 operation_id;
+ u32 device_context;
+ u32 block_id;
+ u32 shared_mem_size;
+ u32 shared_mem_start_offset;
+ u32 shared_mem_descriptors[TF_MAX_COARSE_PAGES];
+};
+
+/*
+ * REGISTER_SHARED_MEMORY answer message.
+ */
+struct tf_answer_register_shared_memory {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info_rfu;
+ /* an opaque Normal World identifier for the operation */
+ u32 operation_id;
+ u32 error_code;
+ u32 block;
+};
+
+/*
+ * RELEASE_SHARED_MEMORY command message.
+ */
+struct tf_command_release_shared_memory {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info_rfu;
+ /* an opaque Normal World identifier for the operation */
+ u32 operation_id;
+ u32 device_context;
+ u32 block;
+};
+
+/*
+ * RELEASE_SHARED_MEMORY answer message.
+ */
+struct tf_answer_release_shared_memory {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info_rfu;
+ u32 operation_id;
+ u32 error_code;
+ u32 block_id;
+};
+
+/*
+ * INVOKE_CLIENT_COMMAND command message.
+ */
+struct tf_command_invoke_client_command {
+ u8 message_size;
+ u8 message_type;
+ u16 param_types;
+ u32 operation_id;
+ u32 device_context;
+ u32 client_session;
+ u64 timeout;
+ u32 cancellation_id;
+ u32 client_command_identifier;
+ union tf_command_param params[4];
+};
+
+/*
+ * INVOKE_CLIENT_COMMAND command answer.
+ */
+struct tf_answer_invoke_client_command {
+ u8 message_size;
+ u8 message_type;
+ u8 error_origin;
+ u8 __reserved;
+ u32 operation_id;
+ u32 error_code;
+ union tf_answer_param answers[4];
+};
+
+/*
+ * CANCEL_CLIENT_OPERATION command message.
+ */
+struct tf_command_cancel_client_operation {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info_rfu;
+ /* an opaque Normal World identifier for the operation */
+ u32 operation_id;
+ u32 device_context;
+ u32 client_session;
+ u32 cancellation_id;
+};
+
+struct tf_answer_cancel_client_operation {
+ u8 message_size;
+ u8 message_type;
+ u16 message_info_rfu;
+ u32 operation_id;
+ u32 error_code;
+};
+
+/*
+ * MANAGEMENT command message.
+ */
+struct tf_command_management {
+ u8 message_size;
+ u8 message_type;
+ u16 command;
+ u32 operation_id;
+ u32 w3b_size;
+ u32 w3b_start_offset;
+ u32 shared_mem_descriptors[1];
+};
+
+/*
+ * POWER_MANAGEMENT answer message.
+ * The message does not provide message specific parameters.
+ * Therefore no need to define a specific answer structure
+ */
+
+/*
+ * Structure for L2 messages
+ */
+union tf_command {
+ struct tf_command_header header;
+ struct tf_command_create_device_context create_device_context;
+ struct tf_command_destroy_device_context destroy_device_context;
+ struct tf_command_open_client_session open_client_session;
+ struct tf_command_close_client_session close_client_session;
+ struct tf_command_register_shared_memory register_shared_memory;
+ struct tf_command_release_shared_memory release_shared_memory;
+ struct tf_command_invoke_client_command invoke_client_command;
+ struct tf_command_cancel_client_operation cancel_client_operation;
+ struct tf_command_management management;
+};
+
+/*
+ * Structure for any L2 answer
+ */
+
+union tf_answer {
+ struct tf_answer_header header;
+ struct tf_answer_create_device_context create_device_context;
+ struct tf_answer_open_client_session open_client_session;
+ struct tf_answer_close_client_session close_client_session;
+ struct tf_answer_register_shared_memory register_shared_memory;
+ struct tf_answer_release_shared_memory release_shared_memory;
+ struct tf_answer_invoke_client_command invoke_client_command;
+ struct tf_answer_destroy_device_context destroy_device_context;
+ struct tf_answer_cancel_client_operation cancel_client_operation;
+};
+
+/* Structure of the Communication Buffer */
+struct tf_l1_shared_buffer {
+ u32 config_flag_s;
+ u32 w3b_size_max_s;
+ u32 reserved0;
+ u32 w3b_size_current_s;
+ u8 reserved1[48];
+ u8 version_description[TF_DESCRIPTION_BUFFER_LENGTH];
+ u32 status_s;
+ u32 reserved2;
+ u32 sync_serial_n;
+ u32 sync_serial_s;
+ u64 time_n[2];
+ u64 timeout_s[2];
+ u32 first_command;
+ u32 first_free_command;
+ u32 first_answer;
+ u32 first_free_answer;
+ u32 w3b_descriptors[128];
+ #ifdef CONFIG_TF_ZEBRA
+ u8 rpc_trace_buffer[140];
+ u8 rpc_cus_buffer[180];
+ #else
+ u8 reserved3[320];
+ #endif
+ u32 command_queue[TF_N_MESSAGE_QUEUE_CAPACITY];
+ u32 answer_queue[TF_S_ANSWER_QUEUE_CAPACITY];
+};
+
+
+/*
+ * tf_version_information_buffer structure description
+ * Description of the sVersionBuffer handed over from user space to kernel space
+ * This field is filled by the driver during a CREATE_DEVICE_CONTEXT ioctl
+ * and handed back to user space
+ */
+struct tf_version_information_buffer {
+ u8 driver_description[65];
+ u8 secure_world_description[65];
+};
+
+
+/* The IOCTLs the driver supports */
+#include <linux/ioctl.h>
+
+#define IOCTL_TF_GET_VERSION _IO('z', 0)
+#define IOCTL_TF_EXCHANGE _IOWR('z', 1, union tf_command)
+#define IOCTL_TF_GET_DESCRIPTION _IOR('z', 2, \
+ struct tf_version_information_buffer)
+#ifdef CONFIG_TF_ION
+#define IOCTL_TF_ION_REGISTER _IOR('z', 254, int)
+#define IOCTL_TF_ION_UNREGISTER _IOR('z', 255, int)
+#endif
+
+#endif /* !defined(__TF_PROTOCOL_H__) */
diff --git a/security/smc/tf_teec.c b/security/smc/tf_teec.c
new file mode 100644
index 0000000..6e6d5b2
--- /dev/null
+++ b/security/smc/tf_teec.c
@@ -0,0 +1,624 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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
+ */
+
+#ifdef CONFIG_TF_TEEC
+
+#include <asm/atomic.h>
+#include <asm/system.h>
+#include <linux/uaccess.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/list.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/vmalloc.h>
+
+#include "tf_protocol.h"
+#include "tf_defs.h"
+#include "tf_util.h"
+#include "tf_comm.h"
+#include "tf_conn.h"
+#include "tf_teec.h"
+
+#include "tee_client_api.h"
+
+#define TF_COMMAND_BYTES(cmd) \
+ (sizeof(cmd) - sizeof(struct tf_command_header))
+#define TF_COMMAND_SIZE(cmd) \
+ (TF_COMMAND_BYTES(cmd) / sizeof(u32))
+
+/* Associate TEEC errors to POSIX/Linux errors. The matching is somewhat
+ arbitrary but one-to-one for supported error codes. */
+int TEEC_decode_error(TEEC_Result ret)
+{
+ switch (ret)
+ {
+ case TEEC_SUCCESS: return 0;
+ case TEEC_ERROR_GENERIC: return -EIO;
+ case TEEC_ERROR_ACCESS_DENIED: return -EPERM;
+ case TEEC_ERROR_CANCEL: return -ECANCELED;
+ case TEEC_ERROR_ACCESS_CONFLICT: return -EBUSY;
+ case TEEC_ERROR_EXCESS_DATA: return -E2BIG;
+ case TEEC_ERROR_BAD_FORMAT: return -EDOM;
+ case TEEC_ERROR_BAD_PARAMETERS: return -EINVAL;
+ case TEEC_ERROR_BAD_STATE: return -EBADFD;
+ case TEEC_ERROR_ITEM_NOT_FOUND: return -ENOENT;
+ case TEEC_ERROR_NOT_IMPLEMENTED: return -EPROTONOSUPPORT;
+ case TEEC_ERROR_NOT_SUPPORTED: return -ENOSYS;
+ case TEEC_ERROR_NO_DATA: return -ENODATA;
+ case TEEC_ERROR_OUT_OF_MEMORY: return -ENOMEM;
+ case TEEC_ERROR_BUSY: return -EAGAIN;
+ case TEEC_ERROR_COMMUNICATION: return -EPIPE;
+ case TEEC_ERROR_SECURITY: return -ECONNABORTED;
+ case TEEC_ERROR_SHORT_BUFFER: return -EFBIG;
+ default: return -EIO;
+ }
+}
+
+/* Associate POSIX/Linux errors to TEEC errors. The matching is somewhat
+ arbitrary, but TEEC_encode_error(TEEC_decode_error(x))==x for supported
+ error codes. */
+TEEC_Result TEEC_encode_error(int err)
+{
+ if (err >= 0) {
+ return S_SUCCESS;
+ }
+ switch (err) {
+ case 0: return TEEC_SUCCESS;
+ case -EIO: return TEEC_ERROR_GENERIC;
+ case -EPERM: return TEEC_ERROR_ACCESS_DENIED;
+ case -ECANCELED: return TEEC_ERROR_CANCEL;
+ case -EBUSY: return TEEC_ERROR_ACCESS_CONFLICT;
+ case -E2BIG: return TEEC_ERROR_EXCESS_DATA;
+ case -EDOM: return TEEC_ERROR_BAD_FORMAT;
+ case -EINVAL: return TEEC_ERROR_BAD_PARAMETERS;
+ case -EBADFD: return TEEC_ERROR_BAD_STATE;
+ case -ENOENT: return TEEC_ERROR_ITEM_NOT_FOUND;
+ case -EPROTONOSUPPORT: return TEEC_ERROR_NOT_IMPLEMENTED;
+ case -ENOSYS: return TEEC_ERROR_NOT_SUPPORTED;
+ case -ENODATA: return TEEC_ERROR_NO_DATA;
+ case -ENOMEM: return TEEC_ERROR_OUT_OF_MEMORY;
+ case -EAGAIN: return TEEC_ERROR_BUSY;
+ case -EPIPE: return TEEC_ERROR_COMMUNICATION;
+ case -ECONNABORTED: return TEEC_ERROR_SECURITY;
+ case -EFBIG: return TEEC_ERROR_SHORT_BUFFER;
+ default: return TEEC_ERROR_GENERIC;
+ }
+}
+
+/* Encode a TEEC time limit into an SChannel time limit. */
+static u64 TEEC_encode_timeout(const TEEC_TimeLimit *timeLimit)
+{
+ if (timeLimit == NULL) {
+ return (u64)-1;
+ } else {
+ return *timeLimit;
+ }
+}
+
+/* Convert a timeout into a time limit in our internal format. */
+void TEEC_GetTimeLimit(TEEC_Context* sContext,
+ uint32_t nTimeout, /*ms from now*/
+ TEEC_TimeLimit* sTimeLimit)
+{
+ /*Use the kernel time as the TEE time*/
+ struct timeval now;
+ do_gettimeofday(&now);
+ *sTimeLimit =
+ ((TEEC_TimeLimit)now.tv_sec * 1000 +
+ now.tv_usec / 1000 +
+ nTimeout);
+}
+
+#define TF_PARAM_TYPE_INPUT_FLAG 0x1
+#define TF_PARAM_TYPE_OUTPUT_FLAG 0x2
+#define TF_PARAM_TYPE_MEMREF_FLAG 0x4
+#define TF_PARAM_TYPE_REGISTERED_MEMREF_FLAG 0x8
+
+/* Update the type of a whole memref with the direction deduced from
+ the INPUT and OUTPUT flags of the memref. */
+static void TEEC_encode_whole_memref_flags(u16 *param_types,
+ unsigned i,
+ u32 flags)
+{
+ if (flags & TEEC_MEM_INPUT)
+ *param_types |= TF_PARAM_TYPE_INPUT_FLAG << (4*i);
+ if (flags & TEEC_MEM_OUTPUT)
+ *param_types |= TF_PARAM_TYPE_OUTPUT_FLAG << (4*i);
+}
+
+/* Encode the parameters and type of an operation from the TEE API format
+ into an SChannel message. */
+void TEEC_encode_parameters(u16 *param_types,
+ union tf_command_param *params,
+ TEEC_Operation *operation)
+{
+ unsigned i;
+ if (operation == NULL) {
+ *param_types = 0;
+ return;
+ }
+ *param_types = operation->paramTypes;
+ for (i = 0; i < 4; i++) {
+ unsigned ty = TF_GET_PARAM_TYPE(operation->paramTypes, i);
+ TEEC_Parameter *op = operation->params + i;
+ if (ty & TF_PARAM_TYPE_REGISTERED_MEMREF_FLAG) {
+ TEEC_SharedMemory *sm = op->memref.parent;
+ params[i].memref.block = sm->imp._block;
+ if (ty == TEEC_MEMREF_WHOLE) {
+ TEEC_encode_whole_memref_flags(param_types, i,
+ sm->flags);
+ params[i].memref.size = sm->size;
+ params[i].memref.offset = 0;
+ } else {
+ params[i].memref.size = op->memref.size;
+ params[i].memref.offset = op->memref.offset;
+ }
+ } else if (ty & TF_PARAM_TYPE_MEMREF_FLAG) {
+ /* Set up what tf_map_temp_shmem (called by
+ tf_open_client_session and
+ tf_invoke_client_command) expects:
+ .descriptor and .offset to both be set to the
+ address of the buffer. */
+ u32 address = (u32)op->tmpref.buffer;
+ params[i].temp_memref.descriptor = address;
+ params[i].temp_memref.size = op->tmpref.size;
+ params[i].temp_memref.offset = address;
+ } else if (ty & TF_PARAM_TYPE_INPUT_FLAG) {
+ params[i].value.a = op->value.a;
+ params[i].value.b = op->value.b;
+ } else {
+ /* output-only value or none, so nothing to do */
+ }
+ }
+}
+
+/* Decode updated parameters from an SChannel answer into the TEE API format. */
+void TEEC_decode_parameters(union tf_answer_param *params,
+ TEEC_Operation *operation)
+{
+ unsigned i;
+ if (operation == NULL) {
+ return;
+ }
+ for (i = 0; i < 4; i++) {
+ unsigned ty = TF_GET_PARAM_TYPE(operation->paramTypes, i);
+ TEEC_Parameter *op = operation->params + i;
+ if (!(ty & TF_PARAM_TYPE_OUTPUT_FLAG)) {
+ /* input-only or none, so nothing to do */
+ } else if (ty & TF_PARAM_TYPE_REGISTERED_MEMREF_FLAG) {
+ op->memref.size = params[i].size.size;
+ } else if (ty & TF_PARAM_TYPE_MEMREF_FLAG) {
+ op->tmpref.size = params[i].size.size;
+ } else {
+ op->value.a = params[i].value.a;
+ op->value.b = params[i].value.b;
+ }
+ }
+}
+
+/* Start a potentially-cancellable operation. */
+void TEEC_start_operation(TEEC_Context *context,
+ TEEC_Session *session,
+ TEEC_Operation *operation)
+{
+ if (operation != NULL) {
+ operation->imp._pSession = session;
+ /* Flush the assignment to imp._pSession, so that
+ RequestCancellation can read that field if started==1. */
+ barrier();
+ operation->started = 1;
+ }
+}
+
+/* Mark a potentially-cancellable operation as finished. */
+void TEEC_finish_operation(TEEC_Operation *operation)
+{
+ if (operation != NULL) {
+ operation->started = 2;
+ barrier();
+ }
+}
+
+
+
+TEEC_Result TEEC_InitializeContext(const char *name,
+ TEEC_Context *context)
+{
+ int error;
+ struct tf_connection *connection = NULL;
+
+ error = tf_open(tf_get_device(), NULL, &connection);
+ if (error != 0) {
+ dprintk(KERN_ERR "TEEC_InitializeContext(%s): "
+ "tf_open failed (error %d)!\n",
+ (name == NULL ? "(null)" : name), error);
+ goto error;
+ }
+ BUG_ON(connection == NULL);
+ connection->owner = TF_CONNECTION_OWNER_KERNEL;
+
+ error = tf_create_device_context(connection);
+ if (error != 0) {
+ dprintk(KERN_ERR "TEEC_InitializeContext(%s): "
+ "tf_create_device_context failed (error %d)!\n",
+ (name == NULL ? "(null)" : name), error);
+ goto error;
+ }
+
+ context->imp._connection = connection;
+ /*spin_lock_init(&context->imp._operations_lock);*/
+ return S_SUCCESS;
+
+error:
+ tf_close(connection);
+ return TEEC_encode_error(error);
+}
+
+void TEEC_FinalizeContext(TEEC_Context *context)
+{
+ struct tf_connection *connection = context->imp._connection;
+ dprintk(KERN_DEBUG "TEEC_FinalizeContext: connection=%p", connection);
+ tf_close(connection);
+ context->imp._connection = NULL;
+}
+
+TEEC_Result TEEC_RegisterSharedMemory(TEEC_Context* context,
+ TEEC_SharedMemory* sharedMem)
+{
+ union tf_command command_message = {{0}};
+ struct tf_command_register_shared_memory *cmd =
+ &command_message.register_shared_memory;
+ union tf_answer answer_message;
+ struct tf_answer_register_shared_memory *ans =
+ &answer_message.register_shared_memory;
+ TEEC_Result ret;
+ memset(&sharedMem->imp, 0, sizeof(sharedMem->imp));
+
+ cmd->message_size = TF_COMMAND_SIZE(*cmd);
+ cmd->message_type = TF_MESSAGE_TYPE_REGISTER_SHARED_MEMORY;
+ cmd->memory_flags = sharedMem->flags;
+ cmd->operation_id = (u32)&answer_message;
+ cmd->device_context = (u32)context;
+ /*cmd->block_id will be set by tf_register_shared_memory*/
+ cmd->shared_mem_size = sharedMem->size;
+ cmd->shared_mem_start_offset = 0;
+ cmd->shared_mem_descriptors[0] = (u32)sharedMem->buffer;
+
+ ret = TEEC_encode_error(
+ tf_register_shared_memory(context->imp._connection,
+ &command_message,
+ &answer_message));
+ if (ret == TEEC_SUCCESS) {
+ ret = ans->error_code;
+ }
+ if (ret == S_SUCCESS) {
+ sharedMem->imp._context = context;
+ sharedMem->imp._block = ans->block;
+ }
+ return ret;
+}
+
+#define TEEC_POINTER_TO_ZERO_SIZED_BUFFER ((void*)0x010)
+
+TEEC_Result TEEC_AllocateSharedMemory(TEEC_Context* context,
+ TEEC_SharedMemory* sharedMem)
+{
+ TEEC_Result ret;
+ dprintk(KERN_DEBUG "TEEC_AllocateSharedMemory: requested=%lu",
+ (unsigned long)sharedMem->size);
+ if (sharedMem->size == 0) {
+ /* Allocating 0 bytes must return a non-NULL pointer, but the
+ pointer doesn't need to be to memory that is mapped
+ anywhere. So we return a pointer into an unmapped page. */
+ sharedMem->buffer = TEEC_POINTER_TO_ZERO_SIZED_BUFFER;
+ } else {
+ sharedMem->buffer = internal_vmalloc(sharedMem->size);
+ if (sharedMem->buffer == NULL)
+ {
+ dprintk(KERN_INFO "TEEC_AllocateSharedMemory: could not allocate %lu bytes",
+ (unsigned long)sharedMem->size);
+ return TEEC_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ ret = TEEC_RegisterSharedMemory(context, sharedMem);
+ if (ret == TEEC_SUCCESS)
+ {
+ sharedMem->imp._allocated = 1;
+ }
+ else
+ {
+ internal_vfree(sharedMem->buffer);
+ sharedMem->buffer = NULL;
+ memset(&sharedMem->imp, 0, sizeof(sharedMem->imp));
+ }
+ return ret;
+}
+
+void TEEC_ReleaseSharedMemory(TEEC_SharedMemory* sharedMem)
+{
+ TEEC_Context *context = sharedMem->imp._context;
+ union tf_command command_message = {{0}};
+ struct tf_command_release_shared_memory *cmd =
+ &command_message.release_shared_memory;
+ union tf_answer answer_message;
+
+ cmd->message_size = TF_COMMAND_SIZE(*cmd);
+ cmd->message_type = TF_MESSAGE_TYPE_RELEASE_SHARED_MEMORY;
+ cmd->operation_id = (u32)&answer_message;
+ cmd->device_context = (u32)context;
+ cmd->block = sharedMem->imp._block;
+
+ tf_release_shared_memory(context->imp._connection,
+ &command_message,
+ &answer_message);
+ if (sharedMem->imp._allocated) {
+ if (sharedMem->buffer != TEEC_POINTER_TO_ZERO_SIZED_BUFFER) {
+ internal_vfree(sharedMem->buffer);
+ }
+ sharedMem->buffer = NULL;
+ sharedMem->size = 0;
+ }
+ memset(&sharedMem->imp, 0, sizeof(sharedMem->imp));
+}
+
+TEEC_Result TEEC_OpenSessionEx(TEEC_Context* context,
+ TEEC_Session* session,
+ const TEEC_TimeLimit* timeLimit,
+ const TEEC_UUID* destination,
+ u32 connectionMethod,
+ void* connectionData,
+ TEEC_Operation* operation,
+ u32* errorOrigin)
+{
+ union tf_command command_message = {{0}};
+ struct tf_command_open_client_session *cmd =
+ &command_message.open_client_session;
+ union tf_answer answer_message = {{0}};
+ struct tf_answer_open_client_session *ans =
+ &answer_message.open_client_session;
+ TEEC_Result ret;
+
+ /* Note that we set the message size to the whole size of the
+ structure. tf_open_client_session will adjust it down
+ to trim the unnecessary portion of the login_data field. */
+ cmd->message_size = TF_COMMAND_SIZE(*cmd);
+ cmd->message_type = TF_MESSAGE_TYPE_OPEN_CLIENT_SESSION;
+ cmd->operation_id = (u32)&answer_message;
+ cmd->device_context = (u32)context;
+ cmd->cancellation_id = (u32)operation;
+ cmd->timeout = TEEC_encode_timeout(timeLimit);
+ memcpy(&cmd->destination_uuid, destination,
+ sizeof(cmd->destination_uuid));
+ cmd->login_type = connectionMethod;
+ TEEC_encode_parameters(&cmd->param_types, cmd->params, operation);
+
+ switch (connectionMethod)
+ {
+ case TEEC_LOGIN_PRIVILEGED:
+ case TEEC_LOGIN_PUBLIC:
+ break;
+ case TEEC_LOGIN_APPLICATION:
+ case TEEC_LOGIN_USER:
+ case TEEC_LOGIN_USER_APPLICATION:
+ case TEEC_LOGIN_GROUP:
+ case TEEC_LOGIN_GROUP_APPLICATION:
+ default:
+ return TEEC_ERROR_NOT_IMPLEMENTED;
+ }
+
+ TEEC_start_operation(context, session, operation);
+
+ ret = TEEC_encode_error(
+ tf_open_client_session(context->imp._connection,
+ &command_message,
+ &answer_message));
+
+ TEEC_finish_operation(operation);
+ TEEC_decode_parameters(ans->answers, operation);
+ if (errorOrigin != NULL) {
+ *errorOrigin = (ret == TEEC_SUCCESS ?
+ ans->error_origin :
+ TEEC_ORIGIN_COMMS);
+ }
+
+ if (ret == TEEC_SUCCESS) {
+ ret = ans->error_code;
+ }
+ if (ret == S_SUCCESS) {
+ session->imp._client_session = ans->client_session;
+ session->imp._context = context;
+ }
+ return ret;
+}
+
+TEEC_Result TEEC_OpenSession(TEEC_Context* context,
+ TEEC_Session* session,
+ const TEEC_UUID* destination,
+ u32 connectionMethod,
+ void* connectionData,
+ TEEC_Operation* operation,
+ u32* errorOrigin)
+{
+ return TEEC_OpenSessionEx(context, session,
+ NULL, /*timeLimit*/
+ destination,
+ connectionMethod, connectionData,
+ operation, errorOrigin);
+}
+
+void TEEC_CloseSession(TEEC_Session* session)
+{
+ if (session != NULL) {
+ TEEC_Context *context = session->imp._context;
+ union tf_command command_message = {{0}};
+ struct tf_command_close_client_session *cmd =
+ &command_message.close_client_session;
+ union tf_answer answer_message;
+
+ cmd->message_size = TF_COMMAND_SIZE(*cmd);
+ cmd->message_type = TF_MESSAGE_TYPE_CLOSE_CLIENT_SESSION;
+ cmd->operation_id = (u32)&answer_message;
+ cmd->device_context = (u32)context;
+ cmd->client_session = session->imp._client_session;
+
+ tf_close_client_session(context->imp._connection,
+ &command_message,
+ &answer_message);
+
+ session->imp._client_session = 0;
+ session->imp._context = NULL;
+ }
+}
+
+TEEC_Result TEEC_InvokeCommandEx(TEEC_Session* session,
+ const TEEC_TimeLimit* timeLimit,
+ u32 commandID,
+ TEEC_Operation* operation,
+ u32* errorOrigin)
+{
+ TEEC_Context *context = session->imp._context;
+ union tf_command command_message = {{0}};
+ struct tf_command_invoke_client_command *cmd =
+ &command_message.invoke_client_command;
+ union tf_answer answer_message = {{0}};
+ struct tf_answer_invoke_client_command *ans =
+ &answer_message.invoke_client_command;
+ TEEC_Result ret;
+
+ cmd->message_size = TF_COMMAND_SIZE(*cmd);
+ cmd->message_type = TF_MESSAGE_TYPE_INVOKE_CLIENT_COMMAND;
+ cmd->operation_id = (u32)&answer_message;
+ cmd->device_context = (u32)context;
+ cmd->client_session = session->imp._client_session;
+ cmd->timeout = TEEC_encode_timeout(timeLimit);
+ cmd->cancellation_id = (u32)operation;
+ cmd->client_command_identifier = commandID;
+ TEEC_encode_parameters(&cmd->param_types, cmd->params, operation);
+
+ TEEC_start_operation(context, session, operation);
+
+ ret = TEEC_encode_error(
+ tf_invoke_client_command(context->imp._connection,
+ &command_message,
+ &answer_message));
+
+ TEEC_finish_operation(operation);
+ TEEC_decode_parameters(ans->answers, operation);
+ if (errorOrigin != NULL) {
+ *errorOrigin = (ret == TEEC_SUCCESS ?
+ ans->error_origin :
+ TEEC_ORIGIN_COMMS);
+ }
+
+ if (ret == TEEC_SUCCESS) {
+ ret = ans->error_code;
+ }
+ return ret;
+}
+
+TEEC_Result TEEC_InvokeCommand(TEEC_Session* session,
+ u32 commandID,
+ TEEC_Operation* operation,
+ u32* errorOrigin)
+{
+ return TEEC_InvokeCommandEx(session,
+ NULL, /*timeLimit*/
+ commandID,
+ operation, errorOrigin);
+}
+
+TEEC_Result TEEC_send_cancellation_message(TEEC_Context *context,
+ u32 client_session,
+ u32 cancellation_id)
+{
+ union tf_command command_message = {{0}};
+ struct tf_command_cancel_client_operation *cmd =
+ &command_message.cancel_client_operation;
+ union tf_answer answer_message = {{0}};
+ struct tf_answer_cancel_client_operation *ans =
+ &answer_message.cancel_client_operation;
+ TEEC_Result ret;
+
+ cmd->message_size = TF_COMMAND_SIZE(*cmd);
+ cmd->message_type = TF_MESSAGE_TYPE_CANCEL_CLIENT_COMMAND;
+ cmd->operation_id = (u32)&answer_message;
+ cmd->device_context = (u32)context;
+ cmd->client_session = client_session;
+ cmd->cancellation_id = cancellation_id;
+
+ ret = TEEC_encode_error(
+ tf_cancel_client_command(context->imp._connection,
+ &command_message,
+ &answer_message));
+
+ if (ret == TEEC_SUCCESS) {
+ ret = ans->error_code;
+ }
+ return ret;
+}
+
+void TEEC_RequestCancellation(TEEC_Operation* operation)
+{
+ TEEC_Result ret;
+ while (1) {
+ u32 state = operation->started;
+ switch (state) {
+ case 0: /*The operation data structure isn't initialized yet*/
+ break;
+
+ case 1: /*operation is in progress in the client*/
+ ret = TEEC_send_cancellation_message(
+ operation->imp._pSession->imp._context,
+ operation->imp._pSession->imp._client_session,
+ (u32)operation);
+ if (ret == TEEC_SUCCESS) {
+ /*The cancellation was successful*/
+ return;
+ }
+ /* The command has either not reached the secure world
+ yet or has completed already. Either way, retry. */
+ break;
+
+ case 2: /*operation has completed already*/
+ return;
+ }
+ /* Since we're busy-waiting for the operation to be started
+ or finished, yield. */
+ schedule();
+ }
+}
+
+EXPORT_SYMBOL(TEEC_encode_error);
+EXPORT_SYMBOL(TEEC_decode_error);
+EXPORT_SYMBOL(TEEC_InitializeContext);
+EXPORT_SYMBOL(TEEC_FinalizeContext);
+EXPORT_SYMBOL(TEEC_RegisterSharedMemory);
+EXPORT_SYMBOL(TEEC_AllocateSharedMemory);
+EXPORT_SYMBOL(TEEC_ReleaseSharedMemory);
+EXPORT_SYMBOL(TEEC_OpenSession);
+EXPORT_SYMBOL(TEEC_CloseSession);
+EXPORT_SYMBOL(TEEC_InvokeCommand);
+EXPORT_SYMBOL(TEEC_RequestCancellation);
+
+#endif /* defined(CONFIG_TF_TEEC) */
diff --git a/security/smc/omap4/scxlnx_util.c b/security/smc/tf_teec.h
index 90cd831..28b3287 100644
--- a/security/smc/omap4/scxlnx_util.c
+++ b/security/smc/tf_teec.h
@@ -1,5 +1,5 @@
-/*
- * Copyright (c) 2006-2010 Trusted Logic S.A.
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
@@ -16,30 +16,18 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
-#include <linux/mman.h>
-#include "scxlnx_util.h"
-void *internal_kmalloc(size_t nSize, int nPriority)
-{
- void *pResult;
- struct SCXLNX_DEVICE *pDevice = SCXLNXGetDevice();
+#ifndef __TF_TEEC_H__
+#define __TF_TEEC_H__
- pResult = kmalloc(nSize, nPriority);
+#ifdef CONFIG_TF_TEEC
- if (pResult != NULL)
- atomic_inc(
- &pDevice->sDeviceStats.stat_memories_allocated);
+#include "tf_defs.h"
+#include "tee_client_api.h"
- return pResult;
-}
+TEEC_Result TEEC_encode_error(int err);
+int TEEC_decode_error(TEEC_Result ret);
-void internal_kfree(void *pMemory)
-{
- struct SCXLNX_DEVICE *pDevice = SCXLNXGetDevice();
-
- if (pMemory != NULL)
- atomic_dec(
- &pDevice->sDeviceStats.stat_memories_allocated);
- return kfree(pMemory);
-}
+#endif /* defined(CONFIG_TF_TEEC) */
+#endif /* !defined(__TF_TEEC_H__) */
diff --git a/security/smc/tf_util.c b/security/smc/tf_util.c
new file mode 100644
index 0000000..ec9941b
--- /dev/null
+++ b/security/smc/tf_util.c
@@ -0,0 +1,1145 @@
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
+ * 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/mman.h>
+#include "tf_util.h"
+
+/*----------------------------------------------------------------------------
+ * Debug printing routines
+ *----------------------------------------------------------------------------*/
+#ifdef CONFIG_TF_DRIVER_DEBUG_SUPPORT
+
+void address_cache_property(unsigned long va)
+{
+ unsigned long pa;
+ unsigned long inner;
+ unsigned long outer;
+
+ asm volatile ("mcr p15, 0, %0, c7, c8, 0" : : "r" (va));
+ asm volatile ("mrc p15, 0, %0, c7, c4, 0" : "=r" (pa));
+
+ dprintk(KERN_INFO "VA:%x, PA:%x\n",
+ (unsigned int) va,
+ (unsigned int) pa);
+
+ if (pa & 1) {
+ dprintk(KERN_INFO "Prop Error\n");
+ return;
+ }
+
+ outer = (pa >> 2) & 3;
+ dprintk(KERN_INFO "\touter : %x", (unsigned int) outer);
+
+ switch (outer) {
+ case 3:
+ dprintk(KERN_INFO "Write-Back, no Write-Allocate\n");
+ break;
+ case 2:
+ dprintk(KERN_INFO "Write-Through, no Write-Allocate.\n");
+ break;
+ case 1:
+ dprintk(KERN_INFO "Write-Back, Write-Allocate.\n");
+ break;
+ case 0:
+ dprintk(KERN_INFO "Non-cacheable.\n");
+ break;
+ }
+
+ inner = (pa >> 4) & 7;
+ dprintk(KERN_INFO "\tinner : %x", (unsigned int)inner);
+
+ switch (inner) {
+ case 7:
+ dprintk(KERN_INFO "Write-Back, no Write-Allocate\n");
+ break;
+ case 6:
+ dprintk(KERN_INFO "Write-Through.\n");
+ break;
+ case 5:
+ dprintk(KERN_INFO "Write-Back, Write-Allocate.\n");
+ break;
+ case 3:
+ dprintk(KERN_INFO "Device.\n");
+ break;
+ case 1:
+ dprintk(KERN_INFO "Strongly-ordered.\n");
+ break;
+ case 0:
+ dprintk(KERN_INFO "Non-cacheable.\n");
+ break;
+ }
+
+ if (pa & 0x00000002)
+ dprintk(KERN_INFO "SuperSection.\n");
+ if (pa & 0x00000080)
+ dprintk(KERN_INFO "Memory is shareable.\n");
+ else
+ dprintk(KERN_INFO "Memory is non-shareable.\n");
+
+ if (pa & 0x00000200)
+ dprintk(KERN_INFO "Non-secure.\n");
+}
+
+#ifdef CONFIG_BENCH_SECURE_CYCLE
+
+#define LOOP_SIZE (100000)
+
+void run_bogo_mips(void)
+{
+ uint32_t cycles;
+ void *address = &run_bogo_mips;
+
+ dprintk(KERN_INFO "BogoMIPS:\n");
+
+ setup_counters();
+ cycles = run_code_speed(LOOP_SIZE);
+ dprintk(KERN_INFO "%u cycles with code access\n", cycles);
+ cycles = run_data_speed(LOOP_SIZE, (unsigned long)address);
+ dprintk(KERN_INFO "%u cycles to access %x\n", cycles,
+ (unsigned int) address);
+}
+
+#endif /* CONFIG_BENCH_SECURE_CYCLE */
+
+/*
+ * Dump the L1 shared buffer.
+ */
+void tf_dump_l1_shared_buffer(struct tf_l1_shared_buffer *buffer)
+{
+ dprintk(KERN_INFO
+ "buffer@%p:\n"
+ " config_flag_s=%08X\n"
+ " version_description=%64s\n"
+ " status_s=%08X\n"
+ " sync_serial_n=%08X\n"
+ " sync_serial_s=%08X\n"
+ " time_n[0]=%016llX\n"
+ " time_n[1]=%016llX\n"
+ " timeout_s[0]=%016llX\n"
+ " timeout_s[1]=%016llX\n"
+ " first_command=%08X\n"
+ " first_free_command=%08X\n"
+ " first_answer=%08X\n"
+ " first_free_answer=%08X\n\n",
+ buffer,
+ buffer->config_flag_s,
+ buffer->version_description,
+ buffer->status_s,
+ buffer->sync_serial_n,
+ buffer->sync_serial_s,
+ buffer->time_n[0],
+ buffer->time_n[1],
+ buffer->timeout_s[0],
+ buffer->timeout_s[1],
+ buffer->first_command,
+ buffer->first_free_command,
+ buffer->first_answer,
+ buffer->first_free_answer);
+}
+
+
+/*
+ * Dump the specified SChannel message using dprintk.
+ */
+void tf_dump_command(union tf_command *command)
+{
+ u32 i;
+
+ dprintk(KERN_INFO "message@%p:\n", command);
+
+ switch (command->header.message_type) {
+ case TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT\n"
+ " operation_id = 0x%08X\n"
+ " device_context_id = 0x%08X\n",
+ command->header.message_size,
+ command->header.message_type,
+ command->header.operation_id,
+ command->create_device_context.device_context_id
+ );
+ break;
+
+ case TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT\n"
+ " operation_id = 0x%08X\n"
+ " device_context = 0x%08X\n",
+ command->header.message_size,
+ command->header.message_type,
+ command->header.operation_id,
+ command->destroy_device_context.device_context);
+ break;
+
+ case TF_MESSAGE_TYPE_OPEN_CLIENT_SESSION:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "TF_MESSAGE_TYPE_OPEN_CLIENT_SESSION\n"
+ " param_types = 0x%04X\n"
+ " operation_id = 0x%08X\n"
+ " device_context = 0x%08X\n"
+ " cancellation_id = 0x%08X\n"
+ " timeout = 0x%016llX\n"
+ " destination_uuid = "
+ "%08X-%04X-%04X-%02X%02X-"
+ "%02X%02X%02X%02X%02X%02X\n",
+ command->header.message_size,
+ command->header.message_type,
+ command->open_client_session.param_types,
+ command->header.operation_id,
+ command->open_client_session.device_context,
+ command->open_client_session.cancellation_id,
+ command->open_client_session.timeout,
+ command->open_client_session.destination_uuid.
+ time_low,
+ command->open_client_session.destination_uuid.
+ time_mid,
+ command->open_client_session.destination_uuid.
+ time_hi_and_version,
+ command->open_client_session.destination_uuid.
+ clock_seq_and_node[0],
+ command->open_client_session.destination_uuid.
+ clock_seq_and_node[1],
+ command->open_client_session.destination_uuid.
+ clock_seq_and_node[2],
+ command->open_client_session.destination_uuid.
+ clock_seq_and_node[3],
+ command->open_client_session.destination_uuid.
+ clock_seq_and_node[4],
+ command->open_client_session.destination_uuid.
+ clock_seq_and_node[5],
+ command->open_client_session.destination_uuid.
+ clock_seq_and_node[6],
+ command->open_client_session.destination_uuid.
+ clock_seq_and_node[7]
+ );
+
+ for (i = 0; i < 4; i++) {
+ uint32_t *param = (uint32_t *) &command->
+ open_client_session.params[i];
+ dprintk(KERN_INFO " params[%d] = "
+ "0x%08X:0x%08X:0x%08X\n",
+ i, param[0], param[1], param[2]);
+ }
+
+ switch (TF_LOGIN_GET_MAIN_TYPE(
+ command->open_client_session.login_type)) {
+ case TF_LOGIN_PUBLIC:
+ dprintk(
+ KERN_INFO " login_type = "
+ "TF_LOGIN_PUBLIC\n");
+ break;
+ case TF_LOGIN_USER:
+ dprintk(
+ KERN_INFO " login_type = "
+ "TF_LOGIN_USER\n");
+ break;
+ case TF_LOGIN_GROUP:
+ dprintk(
+ KERN_INFO " login_type = "
+ "TF_LOGIN_GROUP\n");
+ break;
+ case TF_LOGIN_APPLICATION:
+ dprintk(
+ KERN_INFO " login_type = "
+ "TF_LOGIN_APPLICATION\n");
+ break;
+ case TF_LOGIN_APPLICATION_USER:
+ dprintk(
+ KERN_INFO " login_type = "
+ "TF_LOGIN_APPLICATION_USER\n");
+ break;
+ case TF_LOGIN_APPLICATION_GROUP:
+ dprintk(
+ KERN_INFO " login_type = "
+ "TF_LOGIN_APPLICATION_GROUP\n");
+ break;
+ case TF_LOGIN_AUTHENTICATION:
+ dprintk(
+ KERN_INFO " login_type = "
+ "TF_LOGIN_AUTHENTICATION\n");
+ break;
+ case TF_LOGIN_PRIVILEGED:
+ dprintk(
+ KERN_INFO " login_type = "
+ "TF_LOGIN_PRIVILEGED\n");
+ break;
+ case TF_LOGIN_PRIVILEGED_KERNEL:
+ dprintk(
+ KERN_INFO " login_type = "
+ "TF_LOGIN_PRIVILEGED_KERNEL\n");
+ break;
+ default:
+ dprintk(
+ KERN_ERR " login_type = "
+ "0x%08X (Unknown login type)\n",
+ command->open_client_session.login_type);
+ break;
+ }
+
+ dprintk(
+ KERN_INFO " login_data = ");
+ for (i = 0; i < 20; i++)
+ dprintk(
+ KERN_INFO "%d",
+ command->open_client_session.
+ login_data[i]);
+ dprintk("\n");
+ break;
+
+ case TF_MESSAGE_TYPE_CLOSE_CLIENT_SESSION:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "TF_MESSAGE_TYPE_CLOSE_CLIENT_SESSION\n"
+ " operation_id = 0x%08X\n"
+ " device_context = 0x%08X\n"
+ " client_session = 0x%08X\n",
+ command->header.message_size,
+ command->header.message_type,
+ command->header.operation_id,
+ command->close_client_session.device_context,
+ command->close_client_session.client_session
+ );
+ break;
+
+ case TF_MESSAGE_TYPE_REGISTER_SHARED_MEMORY:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "TF_MESSAGE_TYPE_REGISTER_SHARED_MEMORY\n"
+ " memory_flags = 0x%04X\n"
+ " operation_id = 0x%08X\n"
+ " device_context = 0x%08X\n"
+ " block_id = 0x%08X\n"
+ " shared_mem_size = 0x%08X\n"
+ " shared_mem_start_offset = 0x%08X\n"
+ " shared_mem_descriptors[0] = 0x%08X\n"
+ " shared_mem_descriptors[1] = 0x%08X\n"
+ " shared_mem_descriptors[2] = 0x%08X\n"
+ " shared_mem_descriptors[3] = 0x%08X\n"
+ " shared_mem_descriptors[4] = 0x%08X\n"
+ " shared_mem_descriptors[5] = 0x%08X\n"
+ " shared_mem_descriptors[6] = 0x%08X\n"
+ " shared_mem_descriptors[7] = 0x%08X\n",
+ command->header.message_size,
+ command->header.message_type,
+ command->register_shared_memory.memory_flags,
+ command->header.operation_id,
+ command->register_shared_memory.device_context,
+ command->register_shared_memory.block_id,
+ command->register_shared_memory.shared_mem_size,
+ command->register_shared_memory.
+ shared_mem_start_offset,
+ command->register_shared_memory.
+ shared_mem_descriptors[0],
+ command->register_shared_memory.
+ shared_mem_descriptors[1],
+ command->register_shared_memory.
+ shared_mem_descriptors[2],
+ command->register_shared_memory.
+ shared_mem_descriptors[3],
+ command->register_shared_memory.
+ shared_mem_descriptors[4],
+ command->register_shared_memory.
+ shared_mem_descriptors[5],
+ command->register_shared_memory.
+ shared_mem_descriptors[6],
+ command->register_shared_memory.
+ shared_mem_descriptors[7]);
+ break;
+
+ case TF_MESSAGE_TYPE_RELEASE_SHARED_MEMORY:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "TF_MESSAGE_TYPE_RELEASE_SHARED_MEMORY\n"
+ " operation_id = 0x%08X\n"
+ " device_context = 0x%08X\n"
+ " block = 0x%08X\n",
+ command->header.message_size,
+ command->header.message_type,
+ command->header.operation_id,
+ command->release_shared_memory.device_context,
+ command->release_shared_memory.block);
+ break;
+
+ case TF_MESSAGE_TYPE_INVOKE_CLIENT_COMMAND:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "TF_MESSAGE_TYPE_INVOKE_CLIENT_COMMAND\n"
+ " param_types = 0x%04X\n"
+ " operation_id = 0x%08X\n"
+ " device_context = 0x%08X\n"
+ " client_session = 0x%08X\n"
+ " timeout = 0x%016llX\n"
+ " cancellation_id = 0x%08X\n"
+ " client_command_identifier = 0x%08X\n",
+ command->header.message_size,
+ command->header.message_type,
+ command->invoke_client_command.param_types,
+ command->header.operation_id,
+ command->invoke_client_command.device_context,
+ command->invoke_client_command.client_session,
+ command->invoke_client_command.timeout,
+ command->invoke_client_command.cancellation_id,
+ command->invoke_client_command.
+ client_command_identifier
+ );
+
+ for (i = 0; i < 4; i++) {
+ uint32_t *param = (uint32_t *) &command->
+ open_client_session.params[i];
+ dprintk(KERN_INFO " params[%d] = "
+ "0x%08X:0x%08X:0x%08X\n", i,
+ param[0], param[1], param[2]);
+ }
+ break;
+
+ case TF_MESSAGE_TYPE_CANCEL_CLIENT_COMMAND:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "TF_MESSAGE_TYPE_CANCEL_CLIENT_COMMAND\n"
+ " operation_id = 0x%08X\n"
+ " device_context = 0x%08X\n"
+ " client_session = 0x%08X\n",
+ command->header.message_size,
+ command->header.message_type,
+ command->header.operation_id,
+ command->cancel_client_operation.device_context,
+ command->cancel_client_operation.client_session);
+ break;
+
+ case TF_MESSAGE_TYPE_MANAGEMENT:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "TF_MESSAGE_TYPE_MANAGEMENT\n"
+ " operation_id = 0x%08X\n"
+ " command = 0x%08X\n"
+ " w3b_size = 0x%08X\n"
+ " w3b_start_offset = 0x%08X\n",
+ command->header.message_size,
+ command->header.message_type,
+ command->header.operation_id,
+ command->management.command,
+ command->management.w3b_size,
+ command->management.w3b_start_offset);
+ break;
+
+ default:
+ dprintk(
+ KERN_ERR " message_type = 0x%08X "
+ "(Unknown message type)\n",
+ command->header.message_type);
+ break;
+ }
+}
+
+
+/*
+ * Dump the specified SChannel answer using dprintk.
+ */
+void tf_dump_answer(union tf_answer *answer)
+{
+ u32 i;
+ dprintk(
+ KERN_INFO "answer@%p:\n",
+ answer);
+
+ switch (answer->header.message_type) {
+ case TF_MESSAGE_TYPE_CREATE_DEVICE_CONTEXT:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "tf_answer_create_device_context\n"
+ " operation_id = 0x%08X\n"
+ " error_code = 0x%08X\n"
+ " device_context = 0x%08X\n",
+ answer->header.message_size,
+ answer->header.message_type,
+ answer->header.operation_id,
+ answer->create_device_context.error_code,
+ answer->create_device_context.device_context);
+ break;
+
+ case TF_MESSAGE_TYPE_DESTROY_DEVICE_CONTEXT:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "ANSWER_DESTROY_DEVICE_CONTEXT\n"
+ " operation_id = 0x%08X\n"
+ " error_code = 0x%08X\n"
+ " device_context_id = 0x%08X\n",
+ answer->header.message_size,
+ answer->header.message_type,
+ answer->header.operation_id,
+ answer->destroy_device_context.error_code,
+ answer->destroy_device_context.device_context_id);
+ break;
+
+
+ case TF_MESSAGE_TYPE_OPEN_CLIENT_SESSION:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "tf_answer_open_client_session\n"
+ " error_origin = 0x%02X\n"
+ " operation_id = 0x%08X\n"
+ " error_code = 0x%08X\n"
+ " client_session = 0x%08X\n",
+ answer->header.message_size,
+ answer->header.message_type,
+ answer->open_client_session.error_origin,
+ answer->header.operation_id,
+ answer->open_client_session.error_code,
+ answer->open_client_session.client_session);
+ for (i = 0; i < 4; i++) {
+ dprintk(KERN_INFO " answers[%d]=0x%08X:0x%08X\n",
+ i,
+ answer->open_client_session.answers[i].
+ value.a,
+ answer->open_client_session.answers[i].
+ value.b);
+ }
+ break;
+
+ case TF_MESSAGE_TYPE_CLOSE_CLIENT_SESSION:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "ANSWER_CLOSE_CLIENT_SESSION\n"
+ " operation_id = 0x%08X\n"
+ " error_code = 0x%08X\n",
+ answer->header.message_size,
+ answer->header.message_type,
+ answer->header.operation_id,
+ answer->close_client_session.error_code);
+ break;
+
+ case TF_MESSAGE_TYPE_REGISTER_SHARED_MEMORY:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "tf_answer_register_shared_memory\n"
+ " operation_id = 0x%08X\n"
+ " error_code = 0x%08X\n"
+ " block = 0x%08X\n",
+ answer->header.message_size,
+ answer->header.message_type,
+ answer->header.operation_id,
+ answer->register_shared_memory.error_code,
+ answer->register_shared_memory.block);
+ break;
+
+ case TF_MESSAGE_TYPE_RELEASE_SHARED_MEMORY:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "ANSWER_RELEASE_SHARED_MEMORY\n"
+ " operation_id = 0x%08X\n"
+ " error_code = 0x%08X\n"
+ " block_id = 0x%08X\n",
+ answer->header.message_size,
+ answer->header.message_type,
+ answer->header.operation_id,
+ answer->release_shared_memory.error_code,
+ answer->release_shared_memory.block_id);
+ break;
+
+ case TF_MESSAGE_TYPE_INVOKE_CLIENT_COMMAND:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "tf_answer_invoke_client_command\n"
+ " error_origin = 0x%02X\n"
+ " operation_id = 0x%08X\n"
+ " error_code = 0x%08X\n",
+ answer->header.message_size,
+ answer->header.message_type,
+ answer->invoke_client_command.error_origin,
+ answer->header.operation_id,
+ answer->invoke_client_command.error_code
+ );
+ for (i = 0; i < 4; i++) {
+ dprintk(KERN_INFO " answers[%d]=0x%08X:0x%08X\n",
+ i,
+ answer->invoke_client_command.answers[i].
+ value.a,
+ answer->invoke_client_command.answers[i].
+ value.b);
+ }
+ break;
+
+ case TF_MESSAGE_TYPE_CANCEL_CLIENT_COMMAND:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "TF_ANSWER_CANCEL_CLIENT_COMMAND\n"
+ " operation_id = 0x%08X\n"
+ " error_code = 0x%08X\n",
+ answer->header.message_size,
+ answer->header.message_type,
+ answer->header.operation_id,
+ answer->cancel_client_operation.error_code);
+ break;
+
+ case TF_MESSAGE_TYPE_MANAGEMENT:
+ dprintk(KERN_INFO
+ " message_size = 0x%02X\n"
+ " message_type = 0x%02X "
+ "TF_MESSAGE_TYPE_MANAGEMENT\n"
+ " operation_id = 0x%08X\n"
+ " error_code = 0x%08X\n",
+ answer->header.message_size,
+ answer->header.message_type,
+ answer->header.operation_id,
+ answer->header.error_code);
+ break;
+
+ default:
+ dprintk(
+ KERN_ERR " message_type = 0x%02X "
+ "(Unknown message type)\n",
+ answer->header.message_type);
+ break;
+
+ }
+}
+
+#endif /* defined(TF_DRIVER_DEBUG_SUPPORT) */
+
+/*----------------------------------------------------------------------------
+ * SHA-1 implementation
+ * This is taken from the Linux kernel source crypto/sha1.c
+ *----------------------------------------------------------------------------*/
+
+struct sha1_ctx {
+ u64 count;
+ u32 state[5];
+ u8 buffer[64];
+};
+
+static inline u32 rol(u32 value, u32 bits)
+{
+ return ((value) << (bits)) | ((value) >> (32 - (bits)));
+}
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#define blk0(i) block32[i]
+
+#define blk(i) (block32[i & 15] = rol( \
+ block32[(i + 13) & 15] ^ block32[(i + 8) & 15] ^ \
+ block32[(i + 2) & 15] ^ block32[i & 15], 1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v, w, x, y, z, i) do { \
+ z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30); } while (0)
+
+#define R1(v, w, x, y, z, i) do { \
+ z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \
+ w = rol(w, 30); } while (0)
+
+#define R2(v, w, x, y, z, i) do { \
+ z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); \
+ w = rol(w, 30); } while (0)
+
+#define R3(v, w, x, y, z, i) do { \
+ z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \
+ w = rol(w, 30); } while (0)
+
+#define R4(v, w, x, y, z, i) do { \
+ z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \
+ w = rol(w, 30); } while (0)
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+static void sha1_transform(u32 *state, const u8 *in)
+{
+ u32 a, b, c, d, e;
+ u32 block32[16];
+
+ /* convert/copy data to workspace */
+ for (a = 0; a < sizeof(block32)/sizeof(u32); a++)
+ block32[a] = ((u32) in[4 * a]) << 24 |
+ ((u32) in[4 * a + 1]) << 16 |
+ ((u32) in[4 * a + 2]) << 8 |
+ ((u32) in[4 * a + 3]);
+
+ /* Copy context->state[] to working vars */
+ a = state[0];
+ b = state[1];
+ c = state[2];
+ d = state[3];
+ e = state[4];
+
+ /* 4 rounds of 20 operations each. Loop unrolled. */
+ R0(a, b, c, d, e, 0); R0(e, a, b, c, d, 1);
+ R0(d, e, a, b, c, 2); R0(c, d, e, a, b, 3);
+ R0(b, c, d, e, a, 4); R0(a, b, c, d, e, 5);
+ R0(e, a, b, c, d, 6); R0(d, e, a, b, c, 7);
+ R0(c, d, e, a, b, 8); R0(b, c, d, e, a, 9);
+ R0(a, b, c, d, e, 10); R0(e, a, b, c, d, 11);
+ R0(d, e, a, b, c, 12); R0(c, d, e, a, b, 13);
+ R0(b, c, d, e, a, 14); R0(a, b, c, d, e, 15);
+
+ R1(e, a, b, c, d, 16); R1(d, e, a, b, c, 17);
+ R1(c, d, e, a, b, 18); R1(b, c, d, e, a, 19);
+
+ R2(a, b, c, d, e, 20); R2(e, a, b, c, d, 21);
+ R2(d, e, a, b, c, 22); R2(c, d, e, a, b, 23);
+ R2(b, c, d, e, a, 24); R2(a, b, c, d, e, 25);
+ R2(e, a, b, c, d, 26); R2(d, e, a, b, c, 27);
+ R2(c, d, e, a, b, 28); R2(b, c, d, e, a, 29);
+ R2(a, b, c, d, e, 30); R2(e, a, b, c, d, 31);
+ R2(d, e, a, b, c, 32); R2(c, d, e, a, b, 33);
+ R2(b, c, d, e, a, 34); R2(a, b, c, d, e, 35);
+ R2(e, a, b, c, d, 36); R2(d, e, a, b, c, 37);
+ R2(c, d, e, a, b, 38); R2(b, c, d, e, a, 39);
+
+ R3(a, b, c, d, e, 40); R3(e, a, b, c, d, 41);
+ R3(d, e, a, b, c, 42); R3(c, d, e, a, b, 43);
+ R3(b, c, d, e, a, 44); R3(a, b, c, d, e, 45);
+ R3(e, a, b, c, d, 46); R3(d, e, a, b, c, 47);
+ R3(c, d, e, a, b, 48); R3(b, c, d, e, a, 49);
+ R3(a, b, c, d, e, 50); R3(e, a, b, c, d, 51);
+ R3(d, e, a, b, c, 52); R3(c, d, e, a, b, 53);
+ R3(b, c, d, e, a, 54); R3(a, b, c, d, e, 55);
+ R3(e, a, b, c, d, 56); R3(d, e, a, b, c, 57);
+ R3(c, d, e, a, b, 58); R3(b, c, d, e, a, 59);
+
+ R4(a, b, c, d, e, 60); R4(e, a, b, c, d, 61);
+ R4(d, e, a, b, c, 62); R4(c, d, e, a, b, 63);
+ R4(b, c, d, e, a, 64); R4(a, b, c, d, e, 65);
+ R4(e, a, b, c, d, 66); R4(d, e, a, b, c, 67);
+ R4(c, d, e, a, b, 68); R4(b, c, d, e, a, 69);
+ R4(a, b, c, d, e, 70); R4(e, a, b, c, d, 71);
+ R4(d, e, a, b, c, 72); R4(c, d, e, a, b, 73);
+ R4(b, c, d, e, a, 74); R4(a, b, c, d, e, 75);
+ R4(e, a, b, c, d, 76); R4(d, e, a, b, c, 77);
+ R4(c, d, e, a, b, 78); R4(b, c, d, e, a, 79);
+
+ /* Add the working vars back into context.state[] */
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+ state[4] += e;
+ /* Wipe variables */
+ a = b = c = d = e = 0;
+ memset(block32, 0x00, sizeof(block32));
+}
+
+
+static void sha1_init(void *ctx)
+{
+ struct sha1_ctx *sctx = ctx;
+ static const struct sha1_ctx initstate = {
+ 0,
+ { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 },
+ { 0, }
+ };
+
+ *sctx = initstate;
+}
+
+
+static void sha1_update(void *ctx, const u8 *data, unsigned int len)
+{
+ struct sha1_ctx *sctx = ctx;
+ unsigned int i, j;
+
+ j = (sctx->count >> 3) & 0x3f;
+ sctx->count += len << 3;
+
+ if ((j + len) > 63) {
+ memcpy(&sctx->buffer[j], data, (i = 64 - j));
+ sha1_transform(sctx->state, sctx->buffer);
+ for ( ; i + 63 < len; i += 64)
+ sha1_transform(sctx->state, &data[i]);
+ j = 0;
+ } else
+ i = 0;
+ memcpy(&sctx->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+static void sha1_final(void *ctx, u8 *out)
+{
+ struct sha1_ctx *sctx = ctx;
+ u32 i, j, index, padlen;
+ u64 t;
+ u8 bits[8] = { 0, };
+ static const u8 padding[64] = { 0x80, };
+
+ t = sctx->count;
+ bits[7] = 0xff & t; t >>= 8;
+ bits[6] = 0xff & t; t >>= 8;
+ bits[5] = 0xff & t; t >>= 8;
+ bits[4] = 0xff & t; t >>= 8;
+ bits[3] = 0xff & t; t >>= 8;
+ bits[2] = 0xff & t; t >>= 8;
+ bits[1] = 0xff & t; t >>= 8;
+ bits[0] = 0xff & t;
+
+ /* Pad out to 56 mod 64 */
+ index = (sctx->count >> 3) & 0x3f;
+ padlen = (index < 56) ? (56 - index) : ((64+56) - index);
+ sha1_update(sctx, padding, padlen);
+
+ /* Append length */
+ sha1_update(sctx, bits, sizeof(bits));
+
+ /* Store state in digest */
+ for (i = j = 0; i < 5; i++, j += 4) {
+ u32 t2 = sctx->state[i];
+ out[j+3] = t2 & 0xff; t2 >>= 8;
+ out[j+2] = t2 & 0xff; t2 >>= 8;
+ out[j+1] = t2 & 0xff; t2 >>= 8;
+ out[j] = t2 & 0xff;
+ }
+
+ /* Wipe context */
+ memset(sctx, 0, sizeof(*sctx));
+}
+
+
+
+
+/*----------------------------------------------------------------------------
+ * Process identification
+ *----------------------------------------------------------------------------*/
+
+/* This function generates a processes hash table for authentication */
+int tf_get_current_process_hash(void *hash)
+{
+ int result = 0;
+ void *buffer;
+ struct mm_struct *mm;
+ struct vm_area_struct *vma;
+
+ buffer = internal_kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (buffer == NULL) {
+ dprintk(
+ KERN_ERR "tf_get_current_process_hash:"
+ " Out of memory for buffer!\n");
+ return -ENOMEM;
+ }
+
+ mm = current->mm;
+
+ down_read(&(mm->mmap_sem));
+ for (vma = mm->mmap; vma != NULL; vma = vma->vm_next) {
+ if ((vma->vm_flags & VM_EXECUTABLE) != 0 && vma->vm_file
+ != NULL) {
+ struct dentry *dentry;
+ unsigned long start;
+ unsigned long cur;
+ unsigned long end;
+ struct sha1_ctx sha1;
+
+ dentry = dget(vma->vm_file->f_dentry);
+
+ dprintk(
+ KERN_DEBUG "tf_get_current_process_hash: "
+ "Found executable VMA for inode %lu "
+ "(%lu bytes).\n",
+ dentry->d_inode->i_ino,
+ (unsigned long) (dentry->d_inode->
+ i_size));
+
+ start = do_mmap(vma->vm_file, 0,
+ dentry->d_inode->i_size,
+ PROT_READ | PROT_WRITE | PROT_EXEC,
+ MAP_PRIVATE, 0);
+ if (start < 0) {
+ dprintk(
+ KERN_ERR "tf_get_current_process_hash"
+ "Hash: do_mmap failed (error %d)!\n",
+ (int) start);
+ dput(dentry);
+ result = -EFAULT;
+ goto vma_out;
+ }
+
+ end = start + dentry->d_inode->i_size;
+
+ sha1_init(&sha1);
+ cur = start;
+ while (cur < end) {
+ unsigned long chunk;
+
+ chunk = end - cur;
+ if (chunk > PAGE_SIZE)
+ chunk = PAGE_SIZE;
+ if (copy_from_user(buffer, (const void *) cur,
+ chunk) != 0) {
+ dprintk(
+ KERN_ERR "tf_get_current_"
+ "process_hash: copy_from_user "
+ "failed!\n");
+ result = -EINVAL;
+ (void) do_munmap(mm, start,
+ dentry->d_inode->i_size);
+ dput(dentry);
+ goto vma_out;
+ }
+ sha1_update(&sha1, buffer, chunk);
+ cur += chunk;
+ }
+ sha1_final(&sha1, hash);
+ result = 0;
+
+ (void) do_munmap(mm, start, dentry->d_inode->i_size);
+ dput(dentry);
+ break;
+ }
+ }
+vma_out:
+ up_read(&(mm->mmap_sem));
+
+ internal_kfree(buffer);
+
+ if (result == -ENOENT)
+ dprintk(
+ KERN_ERR "tf_get_current_process_hash: "
+ "No executable VMA found for process!\n");
+ return result;
+}
+
+#ifndef CONFIG_ANDROID
+/* This function hashes the path of the current application.
+ * If data = NULL ,nothing else is added to the hash
+ else add data to the hash
+ */
+int tf_hash_application_path_and_data(char *buffer, void *data,
+ u32 data_len)
+{
+ int result = -ENOENT;
+ char *buffer = NULL;
+ struct mm_struct *mm;
+ struct vm_area_struct *vma;
+
+ buffer = internal_kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (buffer == NULL) {
+ result = -ENOMEM;
+ goto end;
+ }
+
+ mm = current->mm;
+
+ down_read(&(mm->mmap_sem));
+ for (vma = mm->mmap; vma != NULL; vma = vma->vm_next) {
+ if ((vma->vm_flags & VM_EXECUTABLE) != 0
+ && vma->vm_file != NULL) {
+ struct path *path;
+ char *endpath;
+ size_t pathlen;
+ struct sha1_ctx sha1;
+ u8 hash[SHA1_DIGEST_SIZE];
+
+ path = &vma->vm_file->f_path;
+
+ endpath = d_path(path, buffer, PAGE_SIZE);
+ if (IS_ERR(path)) {
+ result = PTR_ERR(endpath);
+ up_read(&(mm->mmap_sem));
+ goto end;
+ }
+ pathlen = (buffer + PAGE_SIZE) - endpath;
+
+#ifdef CONFIG_TF_DRIVER_DEBUG_SUPPORT
+ {
+ char *c;
+ dprintk(KERN_DEBUG "current process path = ");
+ for (c = endpath;
+ c < buffer + PAGE_SIZE;
+ c++)
+ dprintk("%c", *c);
+
+ dprintk(", uid=%d, euid=%d\n", current_uid(),
+ current_euid());
+ }
+#endif /* defined(CONFIG_TF_DRIVER_DEBUG_SUPPORT) */
+
+ sha1_init(&sha1);
+ sha1_update(&sha1, endpath, pathlen);
+ if (data != NULL) {
+ dprintk(KERN_INFO "current process path: "
+ "Hashing additional data\n");
+ sha1_update(&sha1, data, data_len);
+ }
+ sha1_final(&sha1, hash);
+ memcpy(buffer, hash, sizeof(hash));
+
+ result = 0;
+
+ break;
+ }
+ }
+ up_read(&(mm->mmap_sem));
+
+end:
+ if (buffer != NULL)
+ internal_kfree(buffer);
+
+ return result;
+}
+#endif /* !CONFIG_ANDROID */
+
+void *internal_kmalloc(size_t size, int priority)
+{
+ void *ptr;
+ struct tf_device *dev = tf_get_device();
+
+ ptr = kmalloc(size, priority);
+
+ if (ptr != NULL)
+ atomic_inc(
+ &dev->stats.stat_memories_allocated);
+
+ return ptr;
+}
+
+void internal_kfree(void *ptr)
+{
+ struct tf_device *dev = tf_get_device();
+
+ if (ptr != NULL)
+ atomic_dec(
+ &dev->stats.stat_memories_allocated);
+ return kfree(ptr);
+}
+
+void internal_vunmap(void *ptr)
+{
+ struct tf_device *dev = tf_get_device();
+
+ if (ptr != NULL)
+ atomic_dec(
+ &dev->stats.stat_memories_allocated);
+
+ vunmap((void *) (((unsigned int)ptr) & 0xFFFFF000));
+}
+
+void *internal_vmalloc(size_t size)
+{
+ void *ptr;
+ struct tf_device *dev = tf_get_device();
+
+ ptr = vmalloc(size);
+
+ if (ptr != NULL)
+ atomic_inc(
+ &dev->stats.stat_memories_allocated);
+
+ return ptr;
+}
+
+void internal_vfree(void *ptr)
+{
+ struct tf_device *dev = tf_get_device();
+
+ if (ptr != NULL)
+ atomic_dec(
+ &dev->stats.stat_memories_allocated);
+ return vfree(ptr);
+}
+
+unsigned long internal_get_zeroed_page(int priority)
+{
+ unsigned long result;
+ struct tf_device *dev = tf_get_device();
+
+ result = get_zeroed_page(priority);
+
+ if (result != 0)
+ atomic_inc(&dev->stats.
+ stat_pages_allocated);
+
+ return result;
+}
+
+void internal_free_page(unsigned long addr)
+{
+ struct tf_device *dev = tf_get_device();
+
+ if (addr != 0)
+ atomic_dec(
+ &dev->stats.stat_pages_allocated);
+ return free_page(addr);
+}
+
+int internal_get_user_pages(
+ struct task_struct *tsk,
+ struct mm_struct *mm,
+ unsigned long start,
+ int len,
+ int write,
+ int force,
+ struct page **pages,
+ struct vm_area_struct **vmas)
+{
+ int result;
+ struct tf_device *dev = tf_get_device();
+
+ result = get_user_pages(
+ tsk,
+ mm,
+ start,
+ len,
+ write,
+ force,
+ pages,
+ vmas);
+
+ if (result > 0)
+ atomic_add(result,
+ &dev->stats.stat_pages_locked);
+
+ return result;
+}
+
+void internal_get_page(struct page *page)
+{
+ struct tf_device *dev = tf_get_device();
+
+ atomic_inc(&dev->stats.stat_pages_locked);
+
+ get_page(page);
+}
+
+void internal_page_cache_release(struct page *page)
+{
+ struct tf_device *dev = tf_get_device();
+
+ atomic_dec(&dev->stats.stat_pages_locked);
+
+ page_cache_release(page);
+}
diff --git a/security/smc/omap4/scxlnx_util.h b/security/smc/tf_util.h
index 4569ec2..43a05da 100644
--- a/security/smc/omap4/scxlnx_util.h
+++ b/security/smc/tf_util.h
@@ -1,5 +1,5 @@
-/*
- * Copyright (c) 2006-2010 Trusted Logic S.A.
+/**
+ * Copyright (c) 2011 Trusted Logic S.A.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
@@ -16,8 +16,9 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
-#ifndef __SCXLNX_UTIL_H__
-#define __SCXLNX_UTIL_H__
+
+#ifndef __TF_UTIL_H__
+#define __TF_UTIL_H__
#include <linux/spinlock.h>
#include <linux/errno.h>
@@ -30,8 +31,8 @@
#include <linux/vmalloc.h>
#include <asm/byteorder.h>
-#include "scx_protocol.h"
-#include "scxlnx_defs.h"
+#include "tf_protocol.h"
+#include "tf_defs.h"
/*----------------------------------------------------------------------------
* Debug printing routines
@@ -39,29 +40,29 @@
#ifdef CONFIG_TF_DRIVER_DEBUG_SUPPORT
-void addressCacheProperty(unsigned long va);
+void address_cache_property(unsigned long va);
#define dprintk printk
-void SCXLNXDumpL1SharedBuffer(struct SCHANNEL_C1S_BUFFER *pBuf);
+void tf_dump_l1_shared_buffer(struct tf_l1_shared_buffer *buffer);
-void SCXLNXDumpMessage(union SCX_COMMAND_MESSAGE *pMessage);
+void tf_dump_command(union tf_command *command);
-void SCXLNXDumpAnswer(union SCX_ANSWER_MESSAGE *pAnswer);
+void tf_dump_answer(union tf_answer *answer);
-#ifdef CONFIG_SMC_BENCH_SECURE_CYCLE
-void setupCounters(void);
-void runBogoMIPS(void);
-int runCodeSpeed(unsigned int nLoop);
-int runDataSpeed(unsigned int nLoop, unsigned long nVA);
-#endif /* CONFIG_SMC_BENCH_SECURE_CYCLE */
+#ifdef CONFIG_BENCH_SECURE_CYCLE
+void setup_counters(void);
+void run_bogo_mips(void);
+int run_code_speed(unsigned int loop);
+int run_data_speed(unsigned int loop, unsigned long va);
+#endif /* CONFIG_BENCH_SECURE_CYCLE */
#else /* defined(CONFIG_TF_DRIVER_DEBUG_SUPPORT) */
#define dprintk(args...) do { ; } while (0)
-#define SCXLNXDumpL1SharedBuffer(pBuf) ((void) 0)
-#define SCXLNXDumpMessage(pMessage) ((void) 0)
-#define SCXLNXDumpAnswer(pAnswer) ((void) 0)
+#define tf_dump_l1_shared_buffer(buffer) ((void) 0)
+#define tf_dump_command(command) ((void) 0)
+#define tf_dump_answer(answer) ((void) 0)
#endif /* defined(CONFIG_TF_DRIVER_DEBUG_SUPPORT) */
@@ -71,22 +72,23 @@ int runDataSpeed(unsigned int nLoop, unsigned long nVA);
* Process identification
*----------------------------------------------------------------------------*/
-int SCXLNXConnGetCurrentProcessHash(void *pHash);
+int tf_get_current_process_hash(void *hash);
-int SCXLNXConnHashApplicationPathAndData(char *pBuffer, void *pData,
- u32 nDataLen);
+#ifndef CONFIG_ANDROID
+int tf_hash_application_path_and_data(char *buffer, void *data, u32 data_len);
+#endif /* !CONFIG_ANDROID */
/*----------------------------------------------------------------------------
* Statistic computation
*----------------------------------------------------------------------------*/
-void *internal_kmalloc(size_t nSize, int nPriority);
-void internal_kfree(void *pMemory);
-void internal_vunmap(void *pMemory);
-void *internal_vmalloc(size_t nSize);
-void internal_vfree(void *pMemory);
-unsigned long internal_get_zeroed_page(int nPriority);
-void internal_free_page(unsigned long pPage);
+void *internal_kmalloc(size_t size, int priority);
+void internal_kfree(void *ptr);
+void internal_vunmap(void *ptr);
+void *internal_vmalloc(size_t size);
+void internal_vfree(void *ptr);
+unsigned long internal_get_zeroed_page(int priority);
+void internal_free_page(unsigned long addr);
int internal_get_user_pages(
struct task_struct *tsk,
struct mm_struct *mm,
@@ -98,5 +100,4 @@ int internal_get_user_pages(
struct vm_area_struct **vmas);
void internal_get_page(struct page *page);
void internal_page_cache_release(struct page *page);
-#endif /* __SCXLNX_UTIL_H__ */
-
+#endif /* __TF_UTIL_H__ */
diff --git a/security/smc/omap4/scxlnx_mshield.h b/security/smc/tf_zebra.h
index 9457ca9..b30fe6f 100644
--- a/security/smc/omap4/scxlnx_mshield.h
+++ b/security/smc/tf_zebra.h
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2010 Trusted Logic S.A.
+ * Copyright (c) 2011 Trusted Logic S.A.
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
@@ -17,17 +17,17 @@
* MA 02111-1307 USA
*/
-#ifndef __SCXLNX_MSHIELD_H__
-#define __SCXLNX_MSHIELD_H__
+#ifndef __TF_ZEBRA_H__
+#define __TF_ZEBRA_H__
-#include "scxlnx_defs.h"
+#include "tf_defs.h"
-int SCXLNXCtrlDeviceRegister(void);
+int tf_ctrl_device_register(void);
-int SCXLNXCommStart(struct SCXLNX_COMM *pComm,
- u32 nWorkspaceAddr, u32 nWorkspaceSize,
- u8 *pPABufferVAddr, u32 nPABufferSize,
- u8 *pPropertiesBuffer, u32 nPropertiesBufferLength);
+int tf_start(struct tf_comm *comm,
+ u32 workspace_addr, u32 workspace_size,
+ u8 *pa_buffer, u32 pa_size,
+ u8 *properties_buffer, u32 properties_length);
/* Assembler entry points to/from secure */
u32 schedule_secure_world(u32 app_id, u32 proc_id, u32 flags, u32 args);
@@ -35,10 +35,10 @@ u32 rpc_handler(u32 p1, u32 p2, u32 p3, u32 p4);
u32 read_mpidr(void);
/* L4 SEC clockdomain enabling/disabling */
-void tf_l4sec_clkdm_wakeup(bool use_spin_lock, bool wakelock);
-void tf_l4sec_clkdm_allow_idle(bool use_spin_lock, bool wakeunlock);
+void tf_l4sec_clkdm_wakeup(bool wakelock);
+void tf_l4sec_clkdm_allow_idle(bool wakeunlock);
/* Delayed secure resume */
int tf_delayed_secure_resume(void);
-#endif
+#endif /* __TF_ZEBRA_H__ */
diff --git a/sound/soc/omap/Kconfig b/sound/soc/omap/Kconfig
index ffcfeee..caeccc7 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -115,6 +115,8 @@ config SND_OMAP_SOC_SDP4430
select SND_SOC_DMIC
select SND_OMAP_SOC_DMIC
select SND_OMAP_SOC_ABE_DSP
+ select SND_OMAP_SOC_MCASP
+ select SND_OMAP_SOC_SPDIF
help
Say Y if you want to add support for SoC audio on Texas Instruments
SDP4430 or PandaBoard.
diff --git a/sound/soc/omap/omap-abe.c b/sound/soc/omap/omap-abe.c
index da5ba44..b05dfe8 100644
--- a/sound/soc/omap/omap-abe.c
+++ b/sound/soc/omap/omap-abe.c
@@ -342,7 +342,7 @@ static void enable_be_port(struct snd_soc_pcm_runtime *be,
/* BT_DL connection to McBSP 1 ports */
format.f = 8000;
- format.samp_format = MONO_RSHIFTED_16;
+ format.samp_format = STEREO_RSHIFTED_16;
abe_connect_serial_port(BT_VX_DL_PORT, &format, MCBSP1_TX);
omap_abe_port_enable(abe_priv->abe,
abe_priv->port[OMAP_ABE_BE_PORT_BT_VX_DL]);
@@ -355,7 +355,7 @@ static void enable_be_port(struct snd_soc_pcm_runtime *be,
/* BT_UL connection to McBSP 1 ports */
format.f = 8000;
- format.samp_format = MONO_RSHIFTED_16;
+ format.samp_format = STEREO_RSHIFTED_16;
abe_connect_serial_port(BT_VX_UL_PORT, &format, MCBSP1_RX);
omap_abe_port_enable(abe_priv->abe,
abe_priv->port[OMAP_ABE_BE_PORT_BT_VX_UL]);
diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c
index 9fc42e7..ece0d24 100755
--- a/sound/soc/omap/sdp4430.c
+++ b/sound/soc/omap/sdp4430.c
@@ -35,6 +35,7 @@
#include <plat/mux.h>
#include <plat/mcbsp.h>
+#include <linux/gpio.h>
#include "omap-mcpdm.h"
#include "omap-abe.h"
#include "omap-abe-dsp.h"
@@ -42,10 +43,31 @@
#include "omap-mcbsp.h"
#include "../codecs/twl6040.h"
+#include "../../../arch/arm/mach-omap2/board-tuna.h"
+
+#define TUNA_MAIN_MIC_GPIO 48
+#define TUNA_SUB_MIC_GPIO 171
+
static int twl6040_power_mode;
static int mcbsp_cfg;
static struct snd_soc_codec *twl6040_codec;
+int omap4_tuna_get_type(void);
+
+static int main_mic_bias_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ gpio_set_value(TUNA_MAIN_MIC_GPIO, SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
+static int sub_mic_bias_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ gpio_set_value(TUNA_SUB_MIC_GPIO, SND_SOC_DAPM_EVENT_ON(event));
+ return 0;
+}
+
static int sdp4430_modem_mcbsp_configure(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, int flag)
{
@@ -67,18 +89,46 @@ static int sdp4430_modem_mcbsp_configure(struct snd_pcm_substream *substream,
modem_substream[substream->stream]->private_data;
if (!mcbsp_cfg) {
- /* Set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(modem_rtd->cpu_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM);
-
- if (unlikely(ret < 0)) {
- printk(KERN_ERR "can't set Modem cpu DAI configuration\n");
- goto exit;
+ if (omap4_tuna_get_type() == TUNA_TYPE_TORO) {
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(modem_rtd->cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (unlikely(ret < 0)) {
+ printk(KERN_ERR "can't set Modem cpu DAI format\n");
+ goto exit;
+ }
+
+ /* McBSP2 fclk reparented to ABE_24M_FCLK */
+ ret = snd_soc_dai_set_sysclk(modem_rtd->cpu_dai,
+ OMAP_MCBSP_SYSCLK_CLKS_FCLK,
+ 32 * 96 * params_rate(params),
+ SND_SOC_CLOCK_IN);
+ if (unlikely(ret < 0)) {
+ printk(KERN_ERR "can't set Modem cpu DAI sysclk\n");
+ goto exit;
+ }
+
+ /* assuming McBSP2 is S16_LE stereo */
+ ret = snd_soc_dai_set_clkdiv(modem_rtd->cpu_dai, 0, 96);
+ if (unlikely(ret < 0)) {
+ printk(KERN_ERR "can't set Modem cpu DAI clkdiv\n");
+ goto exit;
+ }
} else {
- mcbsp_cfg = 1;
+ /* Set cpu DAI configuration */
+ ret = snd_soc_dai_set_fmt(modem_rtd->cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM);
+
+ if (unlikely(ret < 0)) {
+ printk(KERN_ERR "can't set Modem cpu DAI configuration\n");
+ goto exit;
+ }
}
+ mcbsp_cfg = 1;
}
if (params != NULL) {
@@ -186,25 +236,28 @@ static int sdp4430_mcbsp_hw_params(struct snd_pcm_substream *substream,
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
- int ret = 0;
- unsigned int be_id;
+ int ret = 0, channels = 0;
+ unsigned int be_id, fmt;
be_id = rtd->dai_link->be_id;
- if (be_id == OMAP_ABE_DAI_MM_FM) {
- /* Set cpu DAI configuration */
- ret = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBM_CFM);
- } else if (be_id == OMAP_ABE_DAI_BT_VX) {
- ret = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_DSP_B |
- SND_SOC_DAIFMT_NB_IF |
- SND_SOC_DAIFMT_CBM_CFM);
+ if (be_id == OMAP_ABE_DAI_BT_VX) {
+ if (machine_is_tuna())
+ fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+ else
+ fmt = SND_SOC_DAIFMT_DSP_B |
+ SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_CBM_CFM;
+ } else {
+ fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
}
+ ret = snd_soc_dai_set_fmt(cpu_dai, fmt);
if (ret < 0) {
printk(KERN_ERR "can't set cpu DAI configuration\n");
return ret;
@@ -216,11 +269,25 @@ static int sdp4430_mcbsp_hw_params(struct snd_pcm_substream *substream,
*/
/* Set McBSP clock to external */
ret = snd_soc_dai_set_sysclk(cpu_dai, OMAP_MCBSP_SYSCLK_CLKS_FCLK,
- 64 * params_rate(params),
+ 32 * 96 * params_rate(params),
SND_SOC_CLOCK_IN);
if (ret < 0)
printk(KERN_ERR "can't set cpu system clock\n");
+ ret = snd_soc_dai_set_clkdiv(cpu_dai, 0, 96);
+ if (ret < 0)
+ printk(KERN_ERR "can't set McBSP cpu DAI clkdiv\n");
+
+ /*
+ * Configure McBSP internal buffer threshold
+ * for playback/record
+ */
+ channels = params_channels(params);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ omap_mcbsp_set_tx_threshold(cpu_dai->id, channels);
+ else
+ omap_mcbsp_set_rx_threshold(cpu_dai->id, channels);
+
return ret;
}
@@ -238,7 +305,7 @@ static int mcbsp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
if (be_id == OMAP_ABE_DAI_MM_FM)
channels->min = 2;
else if (be_id == OMAP_ABE_DAI_BT_VX)
- channels->min = 1;
+ channels->min = 2;
snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
SNDRV_PCM_HW_PARAM_FIRST_MASK],
SNDRV_PCM_FORMAT_S16_LE);
@@ -292,7 +359,15 @@ static const struct snd_kcontrol_new sdp4430_controls[] = {
/* SDP4430 machine DAPM */
static const struct snd_soc_dapm_widget sdp4430_twl6040_dapm_widgets[] = {
- SND_SOC_DAPM_MIC("Ext Mic", NULL),
+
+ SND_SOC_DAPM_MIC("Ext Main Mic", NULL),
+ SND_SOC_DAPM_MIC("Ext Sub Mic", NULL),
+ SND_SOC_DAPM_MICBIAS_E("Ext Main Mic Bias", SND_SOC_NOPM, 0, 0,
+ main_mic_bias_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MICBIAS_E("Ext Sub Mic Bias", SND_SOC_NOPM, 0, 0,
+ sub_mic_bias_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_SPK("Ext Spk", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
SND_SOC_DAPM_HP("Headset Stereophone", NULL),
@@ -302,9 +377,10 @@ static const struct snd_soc_dapm_widget sdp4430_twl6040_dapm_widgets[] = {
static const struct snd_soc_dapm_route audio_map[] = {
/* External Mics: MAINMIC, SUBMIC with bias*/
- {"MAINMIC", NULL, "Main Mic Bias"},
- {"SUBMIC", NULL, "Main Mic Bias"},
- {"Main Mic Bias", NULL, "Ext Mic"},
+ {"MAINMIC", NULL, "Ext Main Mic Bias"},
+ {"SUBMIC", NULL, "Ext Sub Mic Bias"},
+ {"Ext Main Mic Bias" , NULL, "Ext Main Mic"},
+ {"Ext Sub Mic Bias" , NULL, "Ext Sub Mic"},
/* External Speakers: HFL, HFR */
{"Ext Spk", NULL, "HFL"},
@@ -371,7 +447,10 @@ static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
/* SDP4430 connected pins */
- snd_soc_dapm_enable_pin(dapm, "Ext Mic");
+ if (machine_is_tuna()) {
+ snd_soc_dapm_enable_pin(dapm, "Ext Main Mic");
+ snd_soc_dapm_enable_pin(dapm, "Ext Sub Mic");
+ }
snd_soc_dapm_enable_pin(dapm, "Ext Spk");
snd_soc_dapm_enable_pin(dapm, "AFML");
snd_soc_dapm_enable_pin(dapm, "AFMR");
@@ -379,7 +458,10 @@ static int sdp4430_twl6040_init(struct snd_soc_pcm_runtime *rtd)
snd_soc_dapm_enable_pin(dapm, "Headset Stereophone");
/* allow audio paths from the audio modem to run during suspend */
- snd_soc_dapm_ignore_suspend(dapm, "Ext Mic");
+ if (machine_is_tuna()) {
+ snd_soc_dapm_ignore_suspend(dapm, "Ext Main Mic");
+ snd_soc_dapm_ignore_suspend(dapm, "Ext Sub Mic");
+ }
snd_soc_dapm_ignore_suspend(dapm, "Ext Spk");
snd_soc_dapm_ignore_suspend(dapm, "AFML");
snd_soc_dapm_ignore_suspend(dapm, "AFMR");
@@ -460,6 +542,12 @@ static int sdp4430_bt_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
+static int sdp4430_spdif_init(struct snd_soc_pcm_runtime *rtd)
+{
+ rtd->pmdown_time = 0;
+ return 0;
+}
+
static int sdp4430_stream_event(struct snd_soc_dapm_context *dapm)
{
/*
@@ -678,6 +766,17 @@ static struct snd_soc_dai_link sdp4430_dai[] = {
.ops = &sdp4430_mcpdm_ops,
.ignore_suspend = 1,
},
+ {
+ .name = "SPDIF",
+ .stream_name = "SPDIF",
+ .cpu_dai_name = "omap-mcasp-dai",
+ .codec_dai_name = "dit-hifi", /* dummy s/pdif transciever
+ * driver */
+ .platform_name = "omap-pcm-audio",
+ .ignore_suspend = 1,
+ .no_codec = 1,
+ .init = sdp4430_spdif_init,
+ },
/*
* Backend DAIs - i.e. dynamically matched interfaces, invisible to userspace.
@@ -843,20 +942,38 @@ static int __init sdp4430_soc_init(void)
{
int ret;
- if (!machine_is_omap_4430sdp() && !machine_is_omap4_panda()) {
- pr_debug("Not SDP4430 or PandaBoard!\n");
+ if (!machine_is_omap_4430sdp() && !machine_is_omap4_panda() &&
+ !machine_is_tuna()) {
+ pr_debug("Not SDP4430, PandaBoard or Tuna!\n");
return -ENODEV;
}
printk(KERN_INFO "SDP4430 SoC init\n");
+
+ if (machine_is_tuna()) {
+ ret = gpio_request(TUNA_MAIN_MIC_GPIO, "MAIN_MICBIAS_EN");
+ if (ret)
+ goto mainmic_gpio_err;
+
+ gpio_direction_output(TUNA_MAIN_MIC_GPIO, 0);
+
+ ret = gpio_request(TUNA_SUB_MIC_GPIO, "SUB_MICBIAS_EN");
+ if (ret)
+ goto submic_gpio_err;
+ gpio_direction_output(TUNA_SUB_MIC_GPIO, 0);
+ }
+
if (machine_is_omap_4430sdp())
snd_soc_sdp4430.name = "SDP4430";
else if (machine_is_omap4_panda())
snd_soc_sdp4430.name = "Panda";
+ else if (machine_is_tuna())
+ snd_soc_sdp4430.name = "Tuna";
sdp4430_snd_device = platform_device_alloc("soc-audio", -1);
if (!sdp4430_snd_device) {
printk(KERN_ERR "Platform device allocation failed\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto device_err;
}
ret = snd_soc_register_dais(&sdp4430_snd_device->dev, dai, ARRAY_SIZE(dai));
@@ -876,12 +993,23 @@ static int __init sdp4430_soc_init(void)
err:
printk(KERN_ERR "Unable to add platform device\n");
platform_device_put(sdp4430_snd_device);
+device_err:
+ if (machine_is_tuna())
+ gpio_free(TUNA_SUB_MIC_GPIO);
+submic_gpio_err:
+ if (machine_is_tuna())
+ gpio_free(TUNA_MAIN_MIC_GPIO);
+mainmic_gpio_err:
return ret;
}
module_init(sdp4430_soc_init);
static void __exit sdp4430_soc_exit(void)
{
+ if (machine_is_tuna()) {
+ gpio_free(TUNA_SUB_MIC_GPIO);
+ gpio_free(TUNA_MAIN_MIC_GPIO);
+ }
platform_device_unregister(sdp4430_snd_device);
}
module_exit(sdp4430_soc_exit);