aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm/configs/tuna_defconfig324
-rw-r--r--arch/arm/mach-omap2/Kconfig9
-rw-r--r--arch/arm/mach-omap2/Makefile13
-rw-r--r--arch/arm/mach-omap2/board-4430sdp.c2
-rw-r--r--arch/arm/mach-omap2/board-omap4panda.c7
-rw-r--r--arch/arm/mach-omap2/board-tuna-bluetooth.c295
-rw-r--r--arch/arm/mach-omap2/board-tuna-display.c304
-rw-r--r--arch/arm/mach-omap2/board-tuna-emif.c107
-rw-r--r--arch/arm/mach-omap2/board-tuna-input.c210
-rwxr-xr-xarch/arm/mach-omap2/board-tuna-modems.c631
-rw-r--r--arch/arm/mach-omap2/board-tuna-nfc.c112
-rw-r--r--arch/arm/mach-omap2/board-tuna-power.c158
-rwxr-xr-xarch/arm/mach-omap2/board-tuna-sensors.c171
-rw-r--r--arch/arm/mach-omap2/board-tuna-wifi.c367
-rwxr-xr-xarch/arm/mach-omap2/board-tuna.c889
-rw-r--r--arch/arm/mach-omap2/board-tuna.h39
-rw-r--r--arch/arm/mach-omap2/omap_hsi.c418
-rw-r--r--arch/arm/mach-omap2/omap_hwmod_44xx_data.c4
-rw-r--r--arch/arm/mach-omap2/opp4xxx_data.c10
-rw-r--r--arch/arm/mach-omap2/pm44xx.c15
-rw-r--r--arch/arm/plat-omap/include/plat/omap_hsi.h494
-rw-r--r--arch/arm/plat-omap/include/plat/uncompress.h1
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/input/misc/Kconfig7
-rw-r--r--drivers/input/misc/Makefile2
-rw-r--r--drivers/input/misc/gp2a.c667
-rw-r--r--drivers/input/touchscreen/Kconfig12
-rw-r--r--drivers/input/touchscreen/Makefile1
-rw-r--r--drivers/input/touchscreen/atmel_mxt_ts.c4
-rw-r--r--drivers/input/touchscreen/mms_ts.c893
-rw-r--r--drivers/misc/Kconfig12
-rw-r--r--drivers/misc/Makefile3
-rwxr-xr-xdrivers/misc/bmp180.c645
-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.c785
-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.c579
-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.c1312
-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.c256
-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.c265
-rw-r--r--drivers/misc/inv_mpu/slaveirq.h38
-rw-r--r--drivers/misc/inv_mpu/timerirq.c296
-rw-r--r--drivers/misc/inv_mpu/timerirq.h30
-rwxr-xr-xdrivers/misc/modem_if/Kconfig40
-rwxr-xr-xdrivers/misc/modem_if/Makefile8
-rwxr-xr-xdrivers/misc/modem_if/modem.c220
-rwxr-xr-xdrivers/misc/modem_if/modem_io_device.c774
-rwxr-xr-xdrivers/misc/modem_if/modem_link_device_mipi.c1327
-rwxr-xr-xdrivers/misc/modem_if/modem_link_device_mipi.h158
-rwxr-xr-xdrivers/misc/modem_if/modem_modemctl_device_xmm6260.c195
-rwxr-xr-xdrivers/misc/modem_if/modem_prj.h195
-rwxr-xr-xdrivers/misc/modem_if/modem_variation.h118
-rw-r--r--drivers/omap_hsi/Kconfig70
-rw-r--r--drivers/omap_hsi/Makefile21
-rw-r--r--drivers/omap_hsi/hsi-char.c556
-rw-r--r--drivers/omap_hsi/hsi-char.h35
-rw-r--r--drivers/omap_hsi/hsi-if.c672
-rw-r--r--drivers/omap_hsi/hsi-if.h69
-rw-r--r--drivers/omap_hsi/hsi-protocol-if.h187
-rw-r--r--drivers/omap_hsi/hsi_driver.c1138
-rw-r--r--drivers/omap_hsi/hsi_driver.h398
-rw-r--r--drivers/omap_hsi/hsi_driver_bus.c203
-rw-r--r--drivers/omap_hsi/hsi_driver_debugfs.c500
-rw-r--r--drivers/omap_hsi/hsi_driver_dma.c643
-rw-r--r--drivers/omap_hsi/hsi_driver_fifo.c325
-rw-r--r--drivers/omap_hsi/hsi_driver_gpio.c75
-rw-r--r--drivers/omap_hsi/hsi_driver_if.c965
-rw-r--r--drivers/omap_hsi/hsi_driver_int.c717
-rw-r--r--drivers/omap_hsi/hsi_protocol.c308
-rw-r--r--drivers/omap_hsi/hsi_protocol_cmd.c429
-rw-r--r--drivers/omap_hsi/hsi_protocol_if.c896
-rw-r--r--drivers/power/max17040_battery.c15
-rw-r--r--drivers/rpmsg/rpmsg_omx.c17
-rw-r--r--drivers/rpmsg/rpmsg_resmgr.c17
-rw-r--r--drivers/rpmsg/virtio_rpmsg_bus.c4
-rw-r--r--drivers/usb/musb/musb_core.c12
-rw-r--r--drivers/usb/musb/musb_gadget.c9
-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.c982
-rw-r--r--firmware/Makefile1
-rwxr-xr-xfirmware/melfas/mms144_ts.fw.ihex565
-rw-r--r--include/linux/gp2a.h34
-rw-r--r--include/linux/hsi_char.h71
-rw-r--r--include/linux/hsi_driver_if.h181
-rw-r--r--include/linux/mpu.h327
-rw-r--r--include/linux/platform_data/mms_ts.h33
-rwxr-xr-xinclude/linux/platform_data/modem.h90
-rw-r--r--include/linux/platform_data/panel-s6e8aa0.h69
-rw-r--r--sound/soc/omap/sdp4430.c7
135 files changed, 41672 insertions, 33 deletions
diff --git a/arch/arm/configs/tuna_defconfig b/arch/arm/configs/tuna_defconfig
new file mode 100644
index 0000000..af50ec1
--- /dev/null
+++ b/arch/arm/configs/tuna_defconfig
@@ -0,0 +1,324 @@
+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_SLUB_DEBUG is not set
+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_RESET_CLOCKS=y
+# CONFIG_ARCH_OMAP2 is not set
+# CONFIG_ARCH_OMAP3 is not set
+# CONFIG_MACH_OMAP_4430SDP is not set
+CONFIG_ARM_THUMBEE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=2
+CONFIG_PREEMPT=y
+CONFIG_HIGHMEM=y
+CONFIG_CMDLINE="console=ttyO2,115200n8 mem=512M androidboot.console=ttyO2 omap_wdt.timer_margin=30"
+CONFIG_CMDLINE_EXTEND=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_HOTPLUG=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=y
+CONFIG_CPU_FREQ_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_ONDEMAND=y
+CONFIG_CPU_FREQ_GOV_INTERACTIVE=y
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=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_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_INET_ESP=y
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# 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_SIP=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_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_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_RFKILL=y
+CONFIG_RFKILL_INPUT=y
+CONFIG_MTD=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=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_ANDROID_PMEM is not set
+CONFIG_KERNEL_DEBUGGER_CORE=y
+CONFIG_UID_STAT=y
+CONFIG_BMP180=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_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_WIFI_CONTROL_FUNC=y
+CONFIG_BCMDHD=m
+CONFIG_BCMDHD_FW_PATH="/system/vendor/firmware/fw_bcmdhd.bin"
+CONFIG_BCMDHD_WEXT=y
+CONFIG_PPP=y
+CONFIG_PPP_DEFLATE=y
+CONFIG_PPP_BSDCOMP=y
+CONFIG_PPP_MPPE=y
+CONFIG_PPPOLAC=y
+CONFIG_PPPOPNS=y
+CONFIG_INPUT_EVDEV=y
+CONFIG_INPUT_KEYRESET=y
+CONFIG_KEYBOARD_OMAP4=y
+# CONFIG_INPUT_MOUSE is not set
+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_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_GPIO=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_PWM=y
+CONFIG_TWL6030_POWEROFF=y
+CONFIG_TWL6030_MADC=y
+CONFIG_REGULATOR_TWL4030=y
+CONFIG_MEDIA_SUPPORT=y
+# CONFIG_RC_CORE is not set
+CONFIG_PVR_SGX=y
+CONFIG_ION=y
+CONFIG_ION_OMAP=y
+CONFIG_FB=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_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_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_MUSB_HDRC=y
+CONFIG_USB_MUSB_OMAP2PLUS=y
+CONFIG_USB_MUSB_PERIPHERAL=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_BLOCK_DEFERRED_RESUME=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_FS=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_DEBUG_INFO=y
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+# CONFIG_ARM_UNWIND is not set
+CONFIG_DEBUG_USER=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRC_CCITT=y
diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
index 177771b..05b559f 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 7ef7745..be6213e 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -249,6 +249,17 @@ 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-nfc.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-power.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-sensors.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-wifi.o
+obj-$(CONFIG_MACH_TUNA) += board-tuna-bluetooth.o \
+ board-tuna-emif.o
obj-$(CONFIG_MACH_OMAP3517EVM) += board-am3517evm.o \
omap_phy_internal.o \
@@ -286,3 +297,5 @@ obj-y += $(disp-m) $(disp-y)
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
diff --git a/arch/arm/mach-omap2/board-4430sdp.c b/arch/arm/mach-omap2/board-4430sdp.c
index eadd153..76adaa7 100644
--- a/arch/arm/mach-omap2/board-4430sdp.c
+++ b/arch/arm/mach-omap2/board-4430sdp.c
@@ -843,7 +843,7 @@ static __initdata struct emif_device_details emif_devices = {
.cs1_device = &lpddr2_elpida_2G_S4_dev
};
-static inline void board_serial_init(void)
+static inline void __init board_serial_init(void)
{
struct omap_board_data bdata;
bdata.flags = 0;
diff --git a/arch/arm/mach-omap2/board-omap4panda.c b/arch/arm/mach-omap2/board-omap4panda.c
index d236f82..ff80bd4 100644
--- a/arch/arm/mach-omap2/board-omap4panda.c
+++ b/arch/arm/mach-omap2/board-omap4panda.c
@@ -572,7 +572,7 @@ static struct omap_board_data serial4_data __initdata = {
.pads_cnt = ARRAY_SIZE(serial4_pads),
};
-static inline void board_serial_init(void)
+static inline void __init board_serial_init(void)
{
struct omap_board_data bdata;
bdata.flags = 0;
@@ -589,7 +589,7 @@ static inline void board_serial_init(void)
#else
#define board_mux NULL
-static inline void board_serial_init(void)
+static inline void __init board_serial_init(void)
{
omap_serial_init();
}
@@ -726,8 +726,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;
@@ -757,7 +755,6 @@ static void __init omap4_panda_init(void)
usb_musb_init(&musb_board_data);
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..ec58454
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-bluetooth.c
@@ -0,0 +1,295 @@
+/*
+ * 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/serial_core.h>
+#include <linux/wakelock.h>
+#include <asm/mach-types.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;
+
+struct bcm_bt_lpm {
+ int wake;
+ int host_wake;
+ bool rx_wake_lock_taken;
+
+ 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) {
+ 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);
+ }
+
+ 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);
+
+ gpio_set_value(BT_WAKE_GPIO, 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);
+
+void bcm_bt_rx_done_locked(struct uart_port *uport) {
+ if (bt_lpm.host_wake) {
+ // Release wake in 500 ms so that higher layers can take it.
+ wake_lock_timeout(&bt_lpm.wake_lock, HZ/2);
+ bt_lpm.rx_wake_lock_taken = true;
+ }
+}
+EXPORT_SYMBOL(bcm_bt_rx_done_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) {
+ bt_lpm.rx_wake_lock_taken = false;
+ wake_lock(&bt_lpm.wake_lock);
+ } else if (!bt_lpm.rx_wake_lock_taken) {
+ // Failsafe timeout of wakelock.
+ // If the host wake pin is asserted and no data is sent,
+ // when its deasserted we will enter this path
+ wake_lock_timeout(&bt_lpm.wake_lock, HZ/2);
+ }
+
+}
+
+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;
+ }
+
+ 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);
+
+ wake_lock_destroy(&bt_lpm.wake_lock);
+ return 0;
+}
+
+static struct platform_driver bcm4330_bluetooth_platform_driver = {
+ .probe = bcm4330_bluetooth_probe,
+ .remove = bcm4330_bluetooth_remove,
+ .driver = {
+ .name = "bcm4330_bluetooth",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init bcm4330_bluetooth_init(void)
+{
+ 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-display.c b/arch/arm/mach-omap2/board-tuna-display.c
new file mode 100644
index 0000000..3baf2c4
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-display.c
@@ -0,0 +1,304 @@
+/* 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_LUNCHBOX 35
+#define TUNA_GPIO_MLCD_RST 23
+
+static struct panel_generic_dpi_data tuna_lcd_panel = {
+ .name = "samsung_ams452gn05",
+ .platform_enable = NULL,
+ .platform_disable = NULL,
+};
+
+static struct omap_dss_device tuna_lcd_device = {
+ .name = "lcd",
+ .driver_name = "generic_dpi_panel",
+ .type = OMAP_DISPLAY_TYPE_DPI,
+ .channel = OMAP_DSS_CHANNEL_LCD2,
+ .data = &tuna_lcd_panel,
+ .phy.dpi.data_lines = 24,
+};
+
+struct regulator *tuna_oled_reg;
+
+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 (enable)
+ regulator_enable(tuna_oled_reg);
+ else
+ regulator_disable(tuna_oled_reg);
+}
+
+static const struct s6e8aa0_gamma_entry tuna_oled_gamma_table[] = {
+ { BV_0, { 4500000, 4500000, 4500000, }, },
+ { 1, { 4294200, 4407600, 4210200, }, },
+ { 0x00000400, { 3969486, 4038030, 3955093, }, },
+ { 0x000004C2, { 3964456, 4032059, 3949872, }, },
+ { 0x000005A8, { 3959356, 4026019, 3944574, }, },
+ { 0x000006BA, { 3954160, 4019879, 3939171, }, },
+ { 0x00000800, { 3948872, 4013646, 3933668, }, },
+ { 0x00000983, { 3943502, 4007331, 3928075, }, },
+ { 0x00000B50, { 3938029, 4000909, 3922368, }, },
+ { 0x00000D74, { 3932461, 3994392, 3916558, }, },
+ { 0x00001000, { 3926792, 3987772, 3910636, }, },
+ { 0x00001307, { 3921022, 3981052, 3904605, }, },
+ { 0x000016A1, { 3915146, 3974224, 3898455, }, },
+ { 0x00001AE9, { 3909163, 3967289, 3892189, }, },
+ { 0x00002000, { 3903070, 3960245, 3885801, }, },
+ { 0x0000260E, { 3896860, 3953083, 3879284, }, },
+ { 0x00002D41, { 3890532, 3945805, 3872637, }, },
+ { 0x000035D1, { 3884081, 3938403, 3865854, }, },
+ { 0x00004000, { 3877504, 3930876, 3858930, }, },
+ { 0x00004C1C, { 3870797, 3923221, 3851863, }, },
+ { 0x00005A82, { 3863956, 3915434, 3844649, }, },
+ { 0x00006BA2, { 3856976, 3907510, 3837279, }, },
+ { 0x00008000, { 3849853, 3899444, 3829750, }, },
+ { 0x00009838, { 3842582, 3891234, 3822056, }, },
+ { 0x0000B505, { 3835159, 3882874, 3814193, }, },
+ { 0x0000D745, { 3827577, 3874360, 3806153, }, },
+ { 0x00010000, { 3819832, 3865687, 3797931, }, },
+ { 0x00013070, { 3811918, 3856849, 3789519, }, },
+ { 0x00016A0A, { 3803829, 3847842, 3780912, }, },
+ { 0x0001AE8A, { 3795559, 3838659, 3772102, }, },
+ { 0x00020000, { 3787101, 3829295, 3763080, }, },
+ { 0x000260E0, { 3778447, 3819742, 3753839, }, },
+ { 0x0002D414, { 3769592, 3809996, 3744372, }, },
+ { 0x00035D14, { 3760527, 3800049, 3734667, }, },
+ { 0x00040000, { 3751244, 3789893, 3724717, }, },
+ { 0x0004C1C0, { 3741734, 3779522, 3714512, }, },
+ { 0x0005A828, { 3731990, 3768927, 3704040, }, },
+ { 0x0006BA28, { 3722000, 3758099, 3693292, }, },
+ { 0x00080000, { 3711756, 3747030, 3682254, }, },
+ { 0x0009837F, { 3701247, 3735711, 3670915, }, },
+ { 0x000B504F, { 3690462, 3724131, 3659262, }, },
+ { 0x000D7450, { 3679388, 3712280, 3647281, }, },
+ { 0x00100000, { 3668014, 3700147, 3634957, }, },
+ { 0x001306FE, { 3656325, 3687721, 3622274, }, },
+ { 0x0016A09E, { 3644309, 3674988, 3609216, }, },
+ { 0x001AE8A0, { 3631950, 3661936, 3595765, }, },
+ { 0x00200000, { 3619231, 3648550, 3581902, }, },
+ { 0x00260DFC, { 3606137, 3634817, 3567607, }, },
+ { 0x002D413D, { 3592649, 3620719, 3552859, }, },
+ { 0x0035D13F, { 3578748, 3606240, 3537634, }, },
+ { 0x00400000, { 3564413, 3591361, 3521908, }, },
+ { 0x004C1BF8, { 3549622, 3576065, 3505654, }, },
+ { 0x005A827A, { 3534351, 3560329, 3488845, }, },
+ { 0x006BA27E, { 3518576, 3544131, 3471449, }, },
+ { 0x00800000, { 3502268, 3527448, 3453434, }, },
+ { 0x009837F0, { 3485399, 3510255, 3434765, }, },
+ { 0x00B504F3, { 3467936, 3492523, 3415404, }, },
+ { 0x00D744FD, { 3449847, 3474223, 3395308, }, },
+ { 0x01000000, { 3431093, 3455322, 3374435, }, },
+ { 0x01306FE1, { 3411635, 3435786, 3352735, }, },
+ { 0x016A09E6, { 3391431, 3415578, 3330156, }, },
+ { 0x01AE89FA, { 3370432, 3394655, 3306641, }, },
+ { 0x02000000, { 3348587, 3372974, 3282127, }, },
+ { 0x0260DFC1, { 3325842, 3350485, 3256547, }, },
+ { 0x02D413CD, { 3302134, 3327135, 3229824, }, },
+ { 0x035D13F3, { 3277397, 3302865, 3201879, }, },
+ { 0x04000000, { 3251558, 3277611, 3172620, }, },
+ { 0x04C1BF83, { 3224535, 3251302, 3141948, }, },
+ { 0x05A8279A, { 3196240, 3223858, 3109753, }, },
+ { 0x06BA27E6, { 3166574, 3195192, 3075914, }, },
+ { 0x08000000, { 3135426, 3165207, 3040295, }, },
+ { 0x09837F05, { 3102676, 3133793, 3002744, }, },
+ { 0x0B504F33, { 3068187, 3100829, 2963094, }, },
+ { 0x0D744FCD, { 3031806, 3066175, 2921155, }, },
+ { 0x10000000, { 2993361, 3029675, 2876712, }, },
+ { 0x1306FE0A, { 2952659, 2991153, 2829527, }, },
+ { 0x16A09E66, { 2909480, 2950402, 2779324, }, },
+ { 0x1AE89F99, { 2863575, 2907191, 2725793, }, },
+ { 0x20000000, { 2814655, 2861246, 2668579, }, },
+ { 0x260DFC14, { 2762394, 2812251, 2607272, }, },
+ { 0x2D413CCD, { 2706412, 2759834, 2541403, }, },
+ { 0x35D13F32, { 2646266, 2703554, 2470425, }, },
+ { 0x40000000, { 2581441, 2642883, 2393706, }, },
+ { 0x4C1BF828, { 2511332, 2577183, 2310504, }, },
+ { 0x5A82799A, { 2435220, 2505675, 2219951, }, },
+ { 0x6BA27E65, { 2352250, 2427391, 2121028, }, },
+ { 0x80000000, { 2261395, 2341114, 2012536, }, },
+ { 0x9837F051, { 2161415, 2245288, 1893066, }, },
+ { 0xB504F333, { 2050800, 2137874, 1760986, }, },
+ { 0xD744FCCA, { 1927706, 2016150, 1614437, }, },
+ { 0xFFFFFFFF, { 1789879, 1876363, 1451415, }, },
+};
+
+static struct panel_s6e8aa0_data tuna_oled_data = {
+ .reset_gpio = TUNA_GPIO_MLCD_RST,
+ .set_power = tuna_oled_set_power,
+ .gamma_table = tuna_oled_gamma_table,
+ .gamma_table_size = ARRAY_SIZE(tuna_oled_gamma_table),
+ .factory_v255_regs = {
+ 0x084,
+ 0x083,
+ 0x0b7,
+ },
+};
+
+/* width: 58mm */
+/* height: 102mm */
+static struct omap_dss_device tuna_oled_device = {
+ .name = "lcd",
+ .driver_name = "s6e8aa0",
+ .type = OMAP_DISPLAY_TYPE_DSI,
+ .data = &tuna_oled_data,
+ .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,
+ },
+ .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 = 240, /* DSI_PLL_REGM */
+
+ .regm_dispc = 6, /* PLL_CLK1 (M4) */
+ .regm_dsi = 6, /* PLL_CLK2 (M5) */
+ .lp_clk_div = 14, /* LPDIV */
+
+ .dsi_fclk_src = OMAP_DSS_CLK_SRC_DSI_PLL_HSDIV_DSI,
+ },
+ },
+
+ .channel = OMAP_DSS_CHANNEL_LCD,
+};
+
+
+static struct omap_dss_device *tuna_dss_devices[] = {
+ &tuna_oled_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 omap_dss_device *prelunchbox_dss_devices[] = {
+ &tuna_lcd_device,
+};
+
+static struct omap_dss_board_info prelunchbox_dss_data = {
+ .num_devices = ARRAY_SIZE(prelunchbox_dss_devices),
+ .devices = prelunchbox_dss_devices,
+ .default_device = &tuna_lcd_device,
+};
+
+static struct omapfb_platform_data tuna_fb_pdata = {
+ .mem_desc = {
+ .region_cnt = 1,
+ .region = {
+ [0] = {
+ .size = TUNA_FB_RAM_SIZE,
+ },
+ },
+ },
+};
+
+#define MUX_DISPLAY_OUT OMAP_PIN_OUTPUT | OMAP_MUX_MODE5
+void __init omap4_tuna_display_init(void)
+{
+ struct omap_dss_board_info *dss_data;
+
+ if (omap4_tuna_get_revision() == TUNA_REV_PRE_LUNCHBOX) {
+ /* dispc2_data23 - dispc2_data0 */
+ omap_mux_init_signal("usbb2_ulpitll_stp", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("usbb2_ulpitll_dir", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("usbb2_ulpitll_nxt", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("usbb2_ulpitll_dat0", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("usbb2_ulpitll_dat1", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("usbb2_ulpitll_dat2", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu6", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu5", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("usbb2_ulpitll_dat3", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("usbb2_ulpitll_dat4", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("usbb2_ulpitll_dat5", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("usbb2_ulpitll_dat6", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("usbb2_ulpitll_dat7", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu3", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu4", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu11", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu12", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu13", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu14", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu15", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu16", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu17", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu18", MUX_DISPLAY_OUT);
+ omap_mux_init_signal("dpm_emu19", MUX_DISPLAY_OUT);
+ /* dispc2_hsync */
+ omap_mux_init_signal("dpm_emu7", MUX_DISPLAY_OUT);
+ /* dispc2_pclk */
+ omap_mux_init_signal("dpm_emu8", MUX_DISPLAY_OUT);
+ /* dispc2_vsync */
+ omap_mux_init_signal("dpm_emu9", MUX_DISPLAY_OUT);
+ /* dispc2_de */
+ omap_mux_init_signal("dpm_emu10", MUX_DISPLAY_OUT);
+
+ dss_data = &prelunchbox_dss_data;
+ } else {
+ omap4_ctrl_pad_writel(0x1F000000, OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_DSIPHY);
+ if (!omap4_tuna_final_gpios())
+ tuna_oled_data.reset_gpio = TUNA_GPIO_MLCD_RST_LUNCHBOX;
+ omap_mux_init_gpio(tuna_oled_data.reset_gpio, OMAP_PIN_OUTPUT);
+ dss_data = &tuna_dss_data;
+ }
+
+ omap_vram_set_sdram_vram(TUNA_FB_RAM_SIZE, 0);
+ omapfb_set_platform_data(&tuna_fb_pdata);
+ omap_display_init(dss_data);
+}
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..f7050ae
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-emif.c
@@ -0,0 +1,107 @@
+/*
+ * 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
+};
+
+/*
+ * 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..9ee5834
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-input.c
@@ -0,0 +1,210 @@
+/*
+ * 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 const int tuna_keymap[] = {
+ KEY(1, 1, KEY_VOLUMEDOWN),
+ KEY(2, 1, KEY_VOLUMEUP),
+};
+
+static struct matrix_keymap_data tuna_keymap_data = {
+ .keymap = tuna_keymap,
+ .keymap_size = ARRAY_SIZE(tuna_keymap),
+};
+
+static struct omap4_keypad_platform_data tuna_keypad_data = {
+ .keymap_data = &tuna_keymap_data,
+ .rows = 3,
+ .cols = 2,
+};
+
+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,
+ .type = EV_KEY,
+ .keymap = tuna_gpio_keypad_keys_map_high,
+ .keymap_size = ARRAY_SIZE(tuna_gpio_keypad_keys_map_high),
+ .flags = GPIOEDF_ACTIVE_HIGH,
+};
+
+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,
+ .type = EV_KEY,
+ .keymap = tuna_gpio_keypad_keys_map_low,
+ .keymap_size = ARRAY_SIZE(tuna_gpio_keypad_keys_map_low),
+};
+
+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 struct mxt_platform_data atmel_mxt_ts_pdata = {
+ .x_line = 19,
+ .y_line = 11,
+ .x_size = 1024,
+ .y_size = 1024,
+ .blen = 0x21,
+ .threshold = 0x28,
+ .voltage = 2800000, /* 2.8V */
+ .orient = MXT_DIAGONAL,
+ .irqflags = IRQF_TRIGGER_FALLING,
+};
+
+static struct i2c_board_info __initdata tuna_i2c3_boardinfo_pre_lunchbox[] = {
+ {
+ I2C_BOARD_INFO("atmel_mxt_ts", 0x4a),
+ .platform_data = &atmel_mxt_ts_pdata,
+ .irq = OMAP_GPIO_IRQ(GPIO_TOUCH_IRQ),
+ },
+};
+
+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 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);
+
+ if (omap4_tuna_final_gpios()) {
+ 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");
+
+ i2c_register_board_info(3, tuna_i2c3_boardinfo_final,
+ ARRAY_SIZE(tuna_i2c3_boardinfo_final));
+ }
+
+ if (omap4_tuna_get_revision() == TUNA_REV_PRE_LUNCHBOX) {
+ i2c_register_board_info(3, tuna_i2c3_boardinfo_pre_lunchbox,
+ ARRAY_SIZE(tuna_i2c3_boardinfo_pre_lunchbox));
+
+ omap_mux_init_signal("kpd_row1.kpd_row1", OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_signal("kpd_row2.kpd_row2", OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_signal("kpd_col1.kpd_col1", OMAP_PIN_OUTPUT);
+ omap4_keyboard_init(&tuna_keypad_data);
+ tuna_gpio_keypad_data.info_count = 1;
+ } else {
+ 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-modems.c b/arch/arm/mach-omap2/board-tuna-modems.c
new file mode 100755
index 0000000..d9d6374
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-modems.c
@@ -0,0 +1,631 @@
+/* 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>
+
+/* inlcude platform specific file */
+#include <mach/omap4-common.h>
+#include <linux/platform_data/modem.h>
+#include "mux.h"
+#include "board-tuna.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_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
+
+/* PROXIMA 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,
+ },
+};
+
+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)
+{
+ int err = 0;
+
+ 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_mux_init_signal("gpmc_ncs3.gpio_53", OMAP_PIN_OUTPUT);
+ omap_mux_init_signal("dpm_emu4.gpio_15", OMAP_PIN_OUTPUT);
+ omap_mux_init_signal("abe_dmic_clk1.gpio_119", OMAP_PIN_OUTPUT);
+ omap_mux_init_signal("abe_dmic_din1.gpio_120", OMAP_PIN_INPUT);
+ 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_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) {
+ err = gpio_request(gpio_reset_req_n, "RESET_REQ_N");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "RESET_REQ_N", err);
+ }
+ gpio_direction_output(gpio_reset_req_n, 0);
+ }
+
+ if (gpio_cp_on) {
+ err = gpio_request(gpio_cp_on, "CP_ON");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "CP_ON", err);
+ }
+ gpio_direction_output(gpio_cp_on, 0);
+ }
+
+ if (gpio_cp_rst) {
+ err = gpio_request(gpio_cp_rst, "CP_RST");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "CP_RST", err);
+ }
+ gpio_direction_output(gpio_cp_rst, 0);
+ }
+
+ if (gpio_pda_active) {
+ err = gpio_request(gpio_pda_active, "PDA_ACTIVE");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "PDA_ACTIVE", err);
+ }
+ gpio_direction_output(gpio_pda_active, 0);
+ }
+
+ if (gpio_phone_active) {
+ err = gpio_request(gpio_phone_active, "PHONE_ACTIVE");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "PHONE_ACTIVE", err);
+ }
+ gpio_direction_input(gpio_phone_active);
+ }
+
+ if (gpio_cp_dump_int) {
+ err = gpio_request(gpio_cp_dump_int, "CP_DUMP_INT");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "CP_DUMP_INT", err);
+ }
+ gpio_direction_input(gpio_cp_dump_int);
+ }
+
+ if (gpio_flm_uart_sel) {
+ err = gpio_request(gpio_flm_uart_sel, "GPS_UART_SEL");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "GPS_UART_SEL", err);
+ }
+ gpio_direction_output(gpio_reset_req_n, 1);
+ }
+
+ if (gpio_phone_active)
+ irq_set_irq_type(
+ OMAP_GPIO_IRQ(OMAP_GPIO_MIPI_HSI_PHONE_ACTIVE),
+ IRQ_TYPE_LEVEL_HIGH);
+
+ printk(KERN_INFO "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_rmnet0",
+ .id = 0x27,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_DPRAM,
+ },
+ [3] = {
+ .name = "cdma_rmnet1",
+ .id = 0x31,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_DPRAM,
+ },
+ [4] = {
+ .name = "cdma_rmnet2",
+ .id = 0x33,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_DPRAM,
+ },
+
+ [5] = {
+ .name = "cdma_rmnet3",
+ .id = 0x34,
+ .format = IPC_RAW,
+ .io_type = IODEV_NET,
+ .link = LINKDEV_DPRAM,
+ },
+};
+
+
+/* PROXIMA 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,
+
+ .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 cdma_modem_cfg_gpio(void)
+{
+ int err = 0;
+
+ 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;
+
+ /*TODO*/
+ /* gpio mux setting */
+
+ if (gpio_cp_rst) {
+ err = gpio_request(gpio_cp_rst, "CP_RST");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "CP_RST", err);
+ } else
+ gpio_direction_output(gpio_cp_rst, 0);
+ }
+
+ if (gpio_pda_active) {
+ err = gpio_request(gpio_pda_active, "PDA_ACTIVE");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "PDA_ACTIVE", err);
+ } else
+ gpio_direction_output(gpio_pda_active, 0);
+}
+
+ if (gpio_phone_active) {
+ err = gpio_request(gpio_phone_active, "PHONE_ACTIVE");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "PHONE_ACTIVE", err);
+ } else
+ gpio_direction_input(gpio_phone_active);
+ }
+
+ 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 = 0, /* dpram int */
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+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,
+ },
+};
+
+
+/* PROXIMA 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,
+ },
+};
+
+/*
+Proxima 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_info("[MODEM_IF] %s IN!\n", __func__);
+
+ omap_mux_init_gpio(OMAP_GPIO_221_PMIC_PWRON, OMAP_PIN_OUTPUT);
+ omap_mux_init_gpio(OMAP_GPIO_221_PMIC_PWRHOLD_OFF , OMAP_PIN_OUTPUT);
+ omap_mux_init_gpio(OMAP_GPIO_CMC_RST, OMAP_PIN_OUTPUT);
+ omap_mux_init_gpio(OMAP_GPIO_AP2CMC_INT1, OMAP_PIN_OUTPUT);
+ omap_mux_init_gpio(OMAP_GPIO_CMC2AP_INT2, OMAP_PIN_INPUT);
+ omap_mux_init_gpio(OMAP_GPIO_AP2CMC_INT2, OMAP_PIN_OUTPUT);
+ omap_mux_init_gpio(OMAP_GPIO_LTE_ACTIVE, OMAP_PIN_INPUT);
+}
+
+static void lte_modem_cfg_gpio(void)
+{
+
+ int err = 0;
+
+ unsigned gpio_cp_on = lte_modem_data.gpio_cp_on;
+ unsigned gpio_cp_rst = lte_modem_data.gpio_cp_reset;
+ /*unsigned gpio_pda_active = lte_modem_data.gpio_pda_active;*/
+ 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) {
+ err = gpio_request(gpio_cp_on, "LTE_ON");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "LTE_ON", err);
+ } else
+ gpio_direction_output(gpio_cp_on, 0);
+}
+
+
+ if (gpio_cp_rst) {
+ err = gpio_request(gpio_cp_rst, "LTE_RST");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "LTE_RST", err);
+ } else
+ gpio_direction_output(gpio_cp_rst, 0);
+ }
+/*
+ if (gpio_pda_active) {
+ err = gpio_request(gpio_pda_active, "PDA_ACTIVE");
+ if (err) {
+ printk("fail to request gpio %s : %d\n",
+ "PDA_ACTIVE", err);
+ } else
+ gpio_direction_output(gpio_pda_active, 0);
+ }
+*/
+ if (gpio_phone_active) {
+ err = gpio_request(gpio_phone_active, "LTE_ACTIVE");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "LTE_ACTIVE", err);
+ } else
+ gpio_direction_input(gpio_phone_active);
+ }
+
+#ifdef CONFIG_LTE_MODEM_CMC221
+ if (gpio_cp_off) {
+ err = gpio_request(gpio_cp_off, "LTE_OFF");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "LTE_OFF", err);
+ } else
+ gpio_direction_output(gpio_cp_off, 0);
+}
+ if (gpio_slave_wakeup) {
+ err = gpio_request(gpio_slave_wakeup, "LTE_SLAVE_WAKEUP");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "LTE_SLAVE_WAKEUP", err);
+ } else
+ gpio_direction_input(gpio_slave_wakeup);
+ }
+
+ if (gpio_host_wakeup) {
+ err = gpio_request(gpio_host_wakeup, "LTE_HOST_WAKEUP");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "LTE_HOST_WAKEUP", err);
+ } else
+ gpio_direction_input(gpio_host_wakeup);
+ }
+
+ if (gpio_host_active) {
+ err = gpio_request(gpio_host_active, "LTE_HOST_ACTIVE");
+ if (err) {
+ printk(KERN_ERR "fail to request gpio %s : %d\n",
+ "LTE_HOST_ACTIVE", err);
+ } else
+ gpio_direction_input(gpio_host_active);
+ }
+#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 = {
+ .name = "modem_if",
+ .id = 2,
+ .num_resources = ARRAY_SIZE(lte_modem_res),
+ .resource = lte_modem_res,
+ .dev = {
+ .platform_data = &lte_modem_data,
+ },
+};
+
+static int __init init_modem(void)
+{
+ printk(KERN_INFO "[MODEM_IF] init_modem\n");
+
+ switch (omap4_tuna_get_type()) {
+ case TUNA_TYPE_MAGURO: /* Proxima_HSPA */
+ /* umts gpios configuration */
+ umts_modem_cfg_gpio();
+ platform_device_register(&umts_modem);
+ break;
+
+ case TUNA_TYPE_TORO: /* Proxima_LTE */
+ /* cdma gpios configuration */
+ /* TODO not supported yet
+ cdma_modem_cfg_gpio();
+ platform_device_register(&cdma_modem);
+ */
+
+ /* lte gpios configuration */
+ /* TODO not supported yet
+ 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..6b7895e
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-nfc.c
@@ -0,0 +1,112 @@
+/* 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 "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
+
+static unsigned int nfc_power;
+
+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);
+
+void __init omap4_tuna_nfc_init(void)
+{
+ struct platform_device *pdev;
+
+ 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);
+
+ 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-power.c b/arch/arm/mach-omap2/board-tuna-power.c
new file mode 100644
index 0000000..cccfe99
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-power.c
@@ -0,0 +1,158 @@
+/* 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/pda_power.h>
+#include <linux/platform_device.h>
+
+#include <plat/cpu.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 CHG_CUR_ADJ 102
+
+#define TPS62361_GPIO 7
+
+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 = CHG_CUR_ADJ, .flags = GPIOF_OUT_INIT_LOW, .label = "charge_cur_adj" },
+};
+
+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 charger_set_charge(int state)
+{
+ gpio_set_value(GPIO_CHARGE_N, !state);
+}
+
+static int charger_is_ac_online(void)
+{
+ return !gpio_get_value(GPIO_TA_NCONNECTED);
+}
+
+static int charger_is_charging(void)
+{
+ return !gpio_get_value(GPIO_CHARGING_N);
+}
+
+static const __initdata struct resource charger_resources[] = {
+ {
+ .name = "ac",
+ .start = OMAP_GPIO_IRQ(GPIO_TA_NCONNECTED),
+ .end = OMAP_GPIO_IRQ(GPIO_TA_NCONNECTED),
+ .flags = IORESOURCE_IRQ |
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ },
+ {
+ .name = "usb",
+ .start = OMAP_GPIO_IRQ(GPIO_TA_NCONNECTED),
+ .end = OMAP_GPIO_IRQ(GPIO_TA_NCONNECTED),
+ .flags = IORESOURCE_IRQ |
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ }
+};
+
+static char *tuna_charger_supplied_to[] = {
+ "battery",
+};
+
+static const __initdata struct pda_power_pdata charger_pdata = {
+ .init = charger_init,
+ .exit = charger_exit,
+ .is_ac_online = charger_is_ac_online,
+ .is_usb_online = charger_is_ac_online,
+ .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),
+};
+
+static struct max17040_platform_data max17043_pdata = {
+ .charger_online = charger_is_ac_online,
+ .charger_enable = charger_is_charging,
+};
+
+static const __initdata struct i2c_board_info max17043_i2c[] = {
+ {
+ I2C_BOARD_INFO("max17040", (0x6C >> 1)),
+ .platform_data = &max17043_pdata,
+ }
+};
+
+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);
+ }
+
+ if (omap4_tuna_get_revision() == TUNA_REV_PRE_LUNCHBOX) {
+ charger_gpios[0].gpio = 11;
+ charger_gpios[1].gpio = 12;
+ } else if (!omap4_tuna_final_gpios()) {
+ charger_gpios[0].gpio = 159;
+ charger_gpios[1].gpio = 160;
+ }
+
+ 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);
+
+ pdev = platform_device_register_resndata(NULL, "pda-power", -1,
+ charger_resources, ARRAY_SIZE(charger_resources),
+ &charger_pdata, sizeof(charger_pdata));
+
+ i2c_register_board_info(4, max17043_i2c, ARRAY_SIZE(max17043_i2c));
+
+ omap_enable_smartreflex_on_init();
+}
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..dc59124
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-sensors.c
@@ -0,0 +1,171 @@
+/* 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 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_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,
+};
+
+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 = { 0, 1, 0,
+ 1, 0, 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),
+ },
+};
+
+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_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);
+ /* optical sensor */
+ gp2a_gpio_init();
+
+ if (omap4_tuna_get_type() == TUNA_TYPE_MAGURO &&
+ omap4_tuna_get_revision() >= 2) {
+ rotcpy(mpu_data.orientation, orientation_back_right_90);
+ rotcpy(mpu_data.accel.orientation, orientation_back_180);
+ }
+ if (omap4_tuna_get_type() == TUNA_TYPE_TORO &&
+ omap4_tuna_get_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);
+ }
+
+ i2c_register_board_info(4, tuna_sensors_i2c4_boardinfo,
+ ARRAY_SIZE(tuna_sensors_i2c4_boardinfo));
+}
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..67010b1
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna-wifi.c
@@ -0,0 +1,367 @@
+/*
+ * 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 */
+
+#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;
+}
+
+int __init tuna_init_wifi_mem(void)
+{
+ 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;
+ }
+ 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 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)
+{
+ pr_debug("%s: %d\n", __func__, on);
+ mdelay(100);
+ gpio_set_value(GPIO_WLAN_PMENA, on);
+ mdelay(200);
+
+ tuna_wifi_power_state = on;
+ return 0;
+}
+
+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 };
+
+#if 0
+static int __init parse_tag_wlan_mac(const struct tag *tag)
+{
+ unsigned char *dptr = (unsigned char *)(&tag->u);
+ unsigned size;
+#ifdef ATAG_TUNA_MAC_DEBUG
+ unsigned i;
+#endif
+
+ size = min((tag->hdr.size - 2) * sizeof(__u32), (unsigned)IFHWADDRLEN);
+#ifdef ATAG_TUNA_MAC_DEBUG
+ printk("WiFi MAC Addr [%d] = 0x%x\n", tag->hdr.size, tag->hdr.tag);
+ for(i=0;(i < size);i++) {
+ printk(" %02x", dptr[i]);
+ }
+ printk("\n");
+#endif
+ memcpy(tuna_mac_addr, dptr, size);
+ return 0;
+}
+
+__tagtable(ATAG_TUNA_MAC, parse_tag_wlan_mac);
+#endif
+
+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;
+}
+
+#if 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 */
+ {"US", "US", 69}, /* input ISO "US" to : US regrev 69 */
+ {"CA", "US", 69}, /* input ISO "CA" to : US regrev 69 */
+ {"EU", "EU", 5}, /* input ISO "EU" to : EU regrev 05 */
+ {"FR", "EU", 5},
+ {"DE", "EU", 5},
+ {"GB", "EU", 5}, /* input ISO "UK" to : EU regrev 05 */
+ {"KR", "XY", 3},
+ {"AU", "XY", 3},
+ {"CN", "XY", 3}, /* input ISO "CN" to : XY regrev 03 */
+ {"TW", "XY", 3},
+ {"AR", "XY", 3},
+};
+
+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 NULL;
+}
+#endif
+
+static struct wifi_platform_data tuna_wifi_control = {
+ .set_power = tuna_wifi_power,
+ .set_reset = tuna_wifi_reset,
+ .set_carddetect = tuna_wifi_set_carddetect,
+ .mem_prealloc = tuna_wifi_mem_prealloc,
+ .get_mac_addr = tuna_wifi_get_mac_addr,
+ .get_country_code = NULL, /* 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 100755
index 0000000..b5f20e8
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna.c
@@ -0,0 +1,889 @@
+/* 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 <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.h>
+#include <plat/common.h>
+#include <plat/cpu.h>
+#include <plat/usb.h>
+#include <plat/mmc.h>
+#include "timer-gp.h"
+
+#include "omap4-sar-layout.h"
+#include "hsmmc.h"
+#include "control.h"
+#include "mux.h"
+#include "board-tuna.h"
+
+#define TUNA_RAMCONSOLE_START (PLAT_PHYS_OFFSET + SZ_512M)
+#define TUNA_RAMCONSOLE_SIZE SZ_2M
+
+struct class *sec_class;
+EXPORT_SYMBOL(sec_class);
+
+#define GPIO_AUD_PWRON 127
+#define GPIO_AUD_PWRON_TORO_V1 20
+#define GPIO_MICBIAS_EN 48
+
+/* 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 REBOOT_FLAG_RECOVERY 0x52564352
+#define REBOOT_FLAG_FASTBOOT 0x54534146
+#define REBOOT_FLAG_NORMAL 0x4D524F4E
+
+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",
+ [0x05] = "Toro Pre-Lunchbox",
+};
+
+static const char const *omap4_tuna_hw_name_toro[] = {
+ [0x00] = "Toro Lunchbox #2",
+ [0x01] = "Toro 1st Sample",
+ [0x02] = "Toro 2nd 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];
+}
+
+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;
+
+ pr_info("Tuna HW revision: %02x (%s), cpu %s\n", tuna_hw_rev,
+ omap4_tuna_hw_rev_name(),
+ cpu_is_omap443x() ? "OMAP4430" : "OMAP4460");
+}
+
+bool omap4_tuna_final_gpios(void)
+{
+ int type = omap4_tuna_get_type();
+ int rev = omap4_tuna_get_revision();
+
+ if (type == TUNA_TYPE_TORO ||
+ (rev != TUNA_REV_PRE_LUNCHBOX && rev != TUNA_REV_LUNCHBOX))
+ return true;
+
+ return false;
+}
+
+/* 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 platform_device ramconsole_device = {
+ .name = "ram_console",
+ .id = -1,
+ .num_resources = ARRAY_SIZE(ramconsole_resources),
+ .resource = ramconsole_resources,
+};
+
+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_PIN_INPUT);
+}
+
+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,
+ },
+};
+
+#define PHYS_ADDR_DUCATI_MEM (0x80000000 + SZ_1G - (SZ_1M * 104))
+
+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_DUCATI_MEM - SZ_256M - SZ_64M,
+ .size = SZ_64M,
+ },
+ { .type = OMAP_ION_HEAP_TYPE_TILER,
+ .id = OMAP_ION_HEAP_TILER,
+ .name = "tiler",
+ .base = PHYS_ADDR_DUCATI_MEM - SZ_256M,
+ .size = SZ_256M,
+ },
+ {
+ .type = ION_HEAP_TYPE_CARVEOUT,
+ .id = OMAP_ION_HEAP_LARGE_SURFACES,
+ .name = "large_surfaces",
+ .base = 0x80000000 + SZ_512M + SZ_2M,
+ .size = SZ_32M,
+ },
+ },
+};
+
+static struct platform_device tuna_ion_device = {
+ .name = "ion-omap4",
+ .id = -1,
+ .dev = {
+ .platform_data = &tuna_ion_data,
+ },
+};
+
+static struct platform_device *tuna_devices[] __initdata = {
+ &ramconsole_device,
+ &wl1271_device,
+ &bcm4330_bluetooth_device,
+ &twl6030_madc_device,
+ &tuna_ion_device,
+};
+
+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);
+
+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_GADGET_MUSB_HDRC
+ .mode = MUSB_PERIPHERAL,
+#else
+ .mode = MUSB_OTG,
+#endif
+ .power = 100,
+};
+
+static struct twl4030_usb_data omap4_usbphy_data = {
+ .phy_init = omap4430_phy_init,
+ .phy_exit = omap4430_phy_exit,
+ .phy_power = omap4430_phy_power,
+ .phy_set_clock = omap4430_phy_set_clk,
+ .phy_suspend = omap4430_phy_suspend,
+};
+
+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_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,
+ },
+};
+
+static struct regulator_consumer_supply tuna_vaux3_supplies[] = {
+ {
+ .supply = "vlcd",
+ },
+};
+
+static struct regulator_init_data tuna_vaux3 = {
+ .constraints = {
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .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,
+ },
+};
+
+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,
+ },
+};
+
+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_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,
+ },
+};
+
+static struct regulator_init_data tuna_vusb = {
+ .constraints = {
+ .min_uV = 3300000,
+ .max_uV = 3300000,
+ .apply_uV = true,
+ .valid_modes_mask = REGULATOR_MODE_NORMAL
+ | REGULATOR_MODE_STANDBY,
+ .valid_ops_mask = REGULATOR_CHANGE_MODE
+ | REGULATOR_CHANGE_STATUS,
+ },
+};
+
+static struct regulator_init_data tuna_clk32kg = {
+ .constraints = {
+ .valid_ops_mask = REGULATOR_CHANGE_STATUS,
+ },
+};
+
+static struct twl4030_codec_audio_data twl6040_audio = {
+ /* Add audio only data */
+};
+
+static struct twl4030_codec_data twl6040_codec = {
+ .audio = &twl6040_audio,
+ .naudint_irq = OMAP44XX_IRQ_SYS_2N,
+ .irq_base = TWL6040_CODEC_IRQ_BASE,
+};
+
+static struct twl4030_platform_data tuna_twldata = {
+ .irq_base = TWL6030_IRQ_BASE,
+ .irq_end = TWL6030_IRQ_END,
+
+ /* Regulators */
+ .vmmc = &tuna_vmmc,
+ .vpp = &tuna_vpp,
+ .vana = &tuna_vana,
+ .vcxio = &tuna_vcxio,
+ .vdac = &tuna_vdac,
+ .vusb = &tuna_vusb,
+ .vaux2 = &tuna_vaux2,
+ .vaux3 = &tuna_vaux3,
+ .clk32kg = &tuna_clk32kg,
+ .usb = &omap4_usbphy_data,
+
+ /* children */
+ .codec = &twl6040_codec,
+ .madc = &twl6030_madc,
+};
+
+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_gpio(GPIO_MICBIAS_EN, OMAP_PIN_OUTPUT);
+ gpio_request(GPIO_MICBIAS_EN, "MICBIAS_EN");
+ gpio_direction_output(GPIO_MICBIAS_EN, 1);
+}
+
+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 int __init tuna_i2c_init(void)
+{
+ omap_mux_init_signal("sys_nirq1", OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_signal("i2c1_scl.i2c1_scl", OMAP_PIN_INPUT_PULLUP);
+ omap_mux_init_signal("i2c1_sda.i2c1_sda", OMAP_PIN_INPUT_PULLUP);
+
+ /*
+ * 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, NULL, 0);
+ omap_register_i2c_bus(3, 400, NULL, 0);
+ omap_register_i2c_bus(4, 400, NULL, 0);
+ 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), /* gpio_135 */
+ OMAP4_MUX(KPD_COL0, OMAP_MUX_MODE3 | OMAP_PIN_INPUT), /* gpio_173 */
+ OMAP4_MUX(GPMC_A19, OMAP_MUX_MODE3 | OMAP_PIN_INPUT), /* 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),
+ { .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 },
+};
+
+static struct omap_device_pad serial2_pads[] __initdata = {
+ OMAP_MUX_STATIC("uart2_cts.uart2_cts",
+ OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0),
+ OMAP_MUX_STATIC("uart2_rts.uart2_rts",
+ OMAP_PIN_OUTPUT | OMAP_MUX_MODE0),
+ OMAP_MUX_STATIC("uart2_rx.uart2_rx",
+ OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0),
+ OMAP_MUX_STATIC("uart2_tx.uart2_tx",
+ OMAP_PIN_OUTPUT | OMAP_MUX_MODE0),
+};
+
+static struct omap_device_pad serial3_pads[] __initdata = {
+ OMAP_MUX_STATIC("uart3_cts_rctx.uart3_cts_rctx",
+ OMAP_PIN_INPUT_PULLUP | OMAP_MUX_MODE0),
+ OMAP_MUX_STATIC("uart3_rts_sd.uart3_rts_sd",
+ OMAP_PIN_OUTPUT | OMAP_MUX_MODE0),
+ OMAP_MUX_STATIC("uart3_rx_irrx.uart3_rx_irrx",
+ OMAP_PIN_INPUT | OMAP_MUX_MODE0),
+ OMAP_MUX_STATIC("uart3_tx_irtx.uart3_tx_irtx",
+ OMAP_PIN_OUTPUT | OMAP_MUX_MODE0),
+};
+
+static struct omap_device_pad serial4_pads[] __initdata = {
+ OMAP_MUX_STATIC("uart4_rx.uart4_rx",
+ OMAP_PIN_INPUT | OMAP_MUX_MODE0),
+ OMAP_MUX_STATIC("uart4_tx.uart4_tx",
+ OMAP_PIN_OUTPUT | OMAP_MUX_MODE0),
+};
+
+static struct omap_board_data serial2_data __initdata = {
+ .id = 1,
+ .pads = serial2_pads,
+ .pads_cnt = ARRAY_SIZE(serial2_pads),
+};
+
+static struct omap_board_data serial3_data __initdata = {
+ .id = 2,
+ .pads = serial3_pads,
+ .pads_cnt = ARRAY_SIZE(serial3_pads),
+};
+
+static struct omap_board_data serial4_data __initdata = {
+ .id = 3,
+ .pads = serial4_pads,
+ .pads_cnt = ARRAY_SIZE(serial4_pads),
+};
+
+static inline void __init board_serial_init(void)
+{
+ struct omap_board_data bdata;
+ bdata.flags = 0;
+ bdata.pads = NULL;
+ bdata.pads_cnt = 0;
+ bdata.id = 0;
+ /* pass dummy data for UART1 */
+ omap_serial_init_port(&bdata);
+
+ omap_serial_init_port(&serial2_data);
+ omap_serial_init_port(&serial3_data);
+ omap_serial_init_port(&serial4_data);
+}
+#else
+#define board_mux NULL
+#define board_wkup_mux NULL
+
+static inline void __init board_serial_init(void)
+{
+ omap_serial_init();
+}
+#endif
+
+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;
+ }
+ }
+
+ /* 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 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_SOC_ATTR_RO(_name, _show) \
+ struct kobj_attribute tuna_soc_prop_attr_##_name = \
+ __ATTR(_name, S_IRUGO, _show, NULL)
+
+static TUNA_SOC_ATTR_RO(family, tuna_soc_family_show);
+static TUNA_SOC_ATTR_RO(revision, tuna_soc_revision_show);
+static TUNA_SOC_ATTR_RO(type, tuna_soc_type_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,
+ NULL,
+};
+
+static struct attribute_group tuna_soc_prop_attr_group = {
+ .attrs = tuna_soc_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(soc_kobj, &tuna_soc_prop_attr_group);
+ if (ret)
+ goto err_sysfs_create;
+ return;
+
+err_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 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();
+
+ register_reboot_notifier(&tuna_reboot_notifier);
+
+ if (omap4_tuna_final_gpios()) {
+ /* 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);
+ } else {
+ /* hsmmc d0-d7 */
+ omap_mux_init_signal("gpmc_ad0", HSMMC2_MUX);
+ omap_mux_init_signal("gpmc_ad1", HSMMC2_MUX);
+ omap_mux_init_signal("gpmc_ad2", HSMMC2_MUX);
+ omap_mux_init_signal("gpmc_ad3", HSMMC2_MUX);
+ omap_mux_init_signal("gpmc_ad4", HSMMC2_MUX);
+ omap_mux_init_signal("gpmc_ad5", HSMMC2_MUX);
+ omap_mux_init_signal("gpmc_ad6", HSMMC2_MUX);
+ omap_mux_init_signal("gpmc_ad7", HSMMC2_MUX);
+ /* hsmmc cmd */
+ omap_mux_init_signal("gpmc_nwe", HSMMC2_MUX);
+ /* hsmmc clk */
+ omap_mux_init_signal("gpmc_noe", HSMMC2_MUX);
+
+ mmc[0].mmc = 2;
+ }
+
+ if (omap4_tuna_get_revision() != TUNA_REV_PRE_LUNCHBOX) {
+ gpio_request(158, "emmc_en");
+ gpio_direction_output(158, 1);
+ omap_mux_init_gpio(158, OMAP_PIN_INPUT_PULLUP);
+ }
+
+ sec_common_init();
+ tuna_wlan_init();
+ tuna_audio_init();
+ tuna_i2c_init();
+ tuna_bt_init();
+ tuna_gsd4t_gps_init();
+ platform_add_devices(tuna_devices, ARRAY_SIZE(tuna_devices));
+ board_serial_init();
+ omap2_hsmmc_init(mmc);
+ usb_musb_init(&musb_board_data);
+ omap4_tuna_create_board_props();
+ omap4_tuna_display_init();
+ omap4_tuna_input_init();
+ omap4_tuna_nfc_init();
+ omap4_tuna_power_init();
+ omap4_tuna_sensors_init();
+#ifdef CONFIG_OMAP_HSI_DEVICE
+ if (TUNA_TYPE_MAGURO == omap4_tuna_get_type())
+ omap_hsi_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;
+
+ omap_reserve();
+ memblock_remove(TUNA_RAMCONSOLE_START, TUNA_RAMCONSOLE_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);
+ }
+}
+
+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..f1e604a
--- /dev/null
+++ b/arch/arm/mach-omap2/board-tuna.h
@@ -0,0 +1,39 @@
+/*
+ * 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_PRE_LUNCHBOX 0x5
+#define TUNA_REV_LUNCHBOX 0x0
+#define TUNA_REV_MASK 0xf
+
+#define TUNA_TYPE_TORO 0x10
+#define TUNA_TYPE_MAGURO 0x00
+#define TUNA_TYPE_MASK 0x10
+
+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_nfc_init(void);
+void omap4_tuna_power_init(void);
+void omap4_tuna_sensors_init(void);
+int tuna_wlan_init(void);
+int omap_hsi_init(void);
+void omap4_tuna_emif_init(void);
+
+extern struct mmc_platform_data tuna_wifi_data;
+
+#endif
diff --git a/arch/arm/mach-omap2/omap_hsi.c b/arch/arm/mach-omap2/omap_hsi.c
new file mode 100644
index 0000000..7c27f84
--- /dev/null
+++ b/arch/arm/mach-omap2/omap_hsi.c
@@ -0,0 +1,418 @@
+/*
+ * arch/arm/mach-omap2/hsi.c
+ *
+ * HSI device definition
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Original Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#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>
+
+#include <../drivers/omap_hsi/hsi_driver.h>
+#include "clock.h"
+#include "mux.h"
+#include "control.h"
+
+static int omap_hsi_wakeup_enable(int hsi_port);
+static int omap_hsi_wakeup_disable(int hsi_port);
+#define OMAP_HSI_PLATFORM_DEVICE_DRIVER_NAME "omap_hsi"
+#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
+
+
+/* */
+
+#define CA_WAKE_MUX_REG (0x4a1000C2)
+static int omap_mux_read_signal(const char *muxname)
+{
+ u16 val = 0;
+ val = omap_readw(CA_WAKE_MUX_REG);
+ return val;
+}
+
+static int omap_mux_enable_wakeup(const char *muxname)
+{
+ u16 val = 0;
+ val = omap_readw(CA_WAKE_MUX_REG);
+ val |= OMAP44XX_PADCONF_WAKEUPENABLE0;
+ omap_writew(val, CA_WAKE_MUX_REG);
+ return 0;
+}
+
+static int omap_mux_disable_wakeup(const char *muxname)
+{
+ u16 val = 0;
+ val = omap_readw(CA_WAKE_MUX_REG);
+ val &= ~OMAP44XX_PADCONF_WAKEUPENABLE0;
+ omap_writew(val, CA_WAKE_MUX_REG);
+ return 0;
+}
+
+/*
+ * NOTE: We abuse a little bit the struct port_ctx to use it also for
+ * initialization.
+ */
+
+
+static struct port_ctx hsi_port_ctx[] = {
+ [0] = {
+ .hst.mode = HSI_MODE_FRAME,
+ .hst.flow = HSI_FLOW_SYNCHRONIZED,
+ .hst.frame_size = HSI_FRAMESIZE_DEFAULT,
+ .hst.divisor = HSI_DIVISOR_DEFAULT,
+ .hst.channels = HSI_CHANNELS_DEFAULT,
+ .hst.arb_mode = HSI_ARBMODE_ROUNDROBIN,
+ .hsr.mode = HSI_MODE_FRAME,
+ .hsr.flow = HSI_FLOW_SYNCHRONIZED,
+ .hsr.frame_size = HSI_FRAMESIZE_DEFAULT,
+ .hsr.channels = HSI_CHANNELS_DEFAULT,
+ .hsr.divisor = HSI_DIVISOR_DEFAULT,
+ .hsr.counters = HSI_COUNTERS_FT_DEFAULT |
+ HSI_COUNTERS_TB_DEFAULT |
+ HSI_COUNTERS_FB_DEFAULT,
+ },
+};
+
+static struct ctrl_ctx hsi_ctx = {
+ .sysconfig = 0,
+ .gdd_gcr = 0,
+ .dll = 0,
+ .pctx = hsi_port_ctx,
+};
+
+static struct hsi_platform_data omap_hsi_platform_data = {
+ .num_ports = ARRAY_SIZE(hsi_port_ctx),
+ .hsi_gdd_chan_count = HSI_HSI_DMA_CHANNEL_MAX,
+ .default_hsi_fclk = HSI_DEFAULT_FCLK,
+ .ctx = &hsi_ctx,
+ .device_enable = omap_device_enable,
+ .device_idle = omap_device_idle,
+ .device_shutdown = omap_device_shutdown,
+ .wakeup_enable = omap_hsi_wakeup_enable,
+ .wakeup_disable = omap_hsi_wakeup_disable,
+ .wakeup_is_from_hsi = omap_hsi_is_io_wakeup_from_hsi,
+ .board_suspend = omap_hsi_prepare_suspend,
+};
+
+
+static struct platform_device *hsi_get_hsi_platform_device(void)
+{
+ struct device *dev;
+ struct platform_device *pdev;
+
+ /* HSI_TODO: handle platform device id (or port) (0/1) */
+ dev = bus_find_device_by_name(&platform_bus_type, NULL,
+ OMAP_HSI_PLATFORM_DEVICE_NAME);
+ if (!dev) {
+ pr_debug("Could not find platform device %s\n",
+ OMAP_HSI_PLATFORM_DEVICE_NAME);
+ return 0;
+ }
+
+ if (!dev->driver) {
+ /* Could not find driver for platform device. */
+ return 0;
+ }
+
+ pdev = to_platform_device(dev);
+
+ return pdev;
+}
+
+static struct hsi_dev *hsi_get_hsi_controller_data(struct platform_device *pd)
+{
+ struct hsi_dev *hsi_ctrl;
+
+ if (!pd)
+ return 0;
+
+ hsi_ctrl = (struct hsi_dev *) platform_get_drvdata(pd);
+ if (!hsi_ctrl) {
+ pr_err("Could not find HSI controller data\n");
+ return 0;
+ }
+
+ return hsi_ctrl;
+}
+
+/**
+* omap_hsi_is_io_pad_hsi - Indicates if IO Pad has been muxed for HSI CAWAKE
+*
+* 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)
+{
+ u16 val;
+
+ /* Check for IO pad */
+ val = omap_mux_read_signal(OMAP_HSI_PADCONF_CAWAKE_PIN);
+ if (val == -ENODEV)
+ return 0;
+
+ /* Continue only if CAWAKE is muxed */
+ if ((val & OMAP_MUX_MODE_MASK) != OMAP_HSI_PADCONF_CAWAKE_MODE)
+ return 0;
+
+ return 1;
+}
+
+/**
+* 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
+*/
+int omap_hsi_is_io_wakeup_from_hsi(void)
+{
+ u16 val;
+
+ /* Check for IO pad wakeup */
+ val = omap_mux_read_signal(OMAP_HSI_PADCONF_CAWAKE_PIN);
+ if (val == -ENODEV)
+ return 0;
+
+ /* Continue only if CAWAKE is muxed */
+ if ((val & OMAP_MUX_MODE_MASK) != OMAP_HSI_PADCONF_CAWAKE_MODE)
+ return 0;
+
+ if (val & OMAP44XX_PADCONF_WAKEUPEVENT0)
+ return 1;
+
+ return 0;
+}
+
+/**
+* omap_hsi_wakeup_enable - Enable HSI wakeup feature from RET/OFF mode
+*
+* @hsi_port - reference to the HSI port onto which enable wakeup feature.
+*
+* 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)
+{
+ 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");
+
+
+ /* TODO: handle hsi_port param and use it to find the correct Pad */
+ return ret;
+}
+
+/**
+* omap_hsi_wakeup_disable - Disable HSI wakeup feature from RET/OFF mode
+*
+* @hsi_port - reference to the HSI port onto which disable wakeup feature.
+*
+* 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)
+{
+ 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 */
+
+ return ret;
+}
+
+/**
+* omap_hsi_prepare_suspend - Prepare HSI for suspend mode
+*
+* Return value :* 0 if CAWAKE padconf has been configured properly
+* * -ENODEV if CAWAKE is not muxed on padconf.
+*
+*/
+int omap_hsi_prepare_suspend(int hsi_port, bool dev_may_wakeup)
+{
+ int ret;
+
+ if (dev_may_wakeup)
+ ret = omap_hsi_wakeup_enable(hsi_port);
+ else
+ ret = omap_hsi_wakeup_disable(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.
+*/
+int omap_hsi_wakeup(int hsi_port)
+{
+ static struct platform_device *pdev;
+ static struct hsi_dev *hsi_ctrl;
+
+ if (!pdev) {
+ pdev = hsi_get_hsi_platform_device();
+ if (!pdev)
+ return -ENODEV;
+}
+
+ if (!device_may_wakeup(&pdev->dev)) {
+ dev_info(&pdev->dev, "Modem not allowed to wakeup platform");
+ return -EPERM;
+ }
+
+ if (!hsi_ctrl) {
+ 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");
+
+ /* CAWAKE falling or rising edge detected */
+ hsi_ctrl->hsi_port->cawake_off_event = true;
+ tasklet_hi_schedule(&hsi_ctrl->hsi_port->hsi_tasklet);
+
+ /* Disable interrupt until Bottom Half has cleared */
+ /* the IRQ status register */
+ disable_irq_nosync(hsi_ctrl->hsi_port->irq);
+
+ return 0;
+}
+
+/* HSI_TODO : This requires some fine tuning & completion of
+ * activate/deactivate latency values
+ */
+static struct omap_device_pm_latency omap_hsi_latency[] = {
+ [0] = {
+ .deactivate_func = omap_device_idle_hwmods,
+ .activate_func = omap_device_enable_hwmods,
+ .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
+ },
+};
+
+/* HSI device registration */
+static int __init omap_hsi_register(struct omap_hwmod *oh, void *user)
+{
+ struct omap_device *od;
+ struct hsi_platform_data *pdata = &omap_hsi_platform_data;
+
+ if (!oh) {
+ pr_err("Could not look up %s omap_hwmod\n",
+ OMAP_HSI_HWMOD_NAME);
+ return -EEXIST;
+ }
+
+ od = omap_device_build(OMAP_HSI_PLATFORM_DEVICE_DRIVER_NAME, 0, oh,
+ pdata, sizeof(*pdata), omap_hsi_latency,
+ ARRAY_SIZE(omap_hsi_latency), false);
+ WARN(IS_ERR(od), "Can't build omap_device for %s:%s.\n",
+ OMAP_HSI_PLATFORM_DEVICE_DRIVER_NAME, oh->name);
+
+ pr_info("HSI: device registered as omap_hwmod: %s\n", oh->name);
+ return 0;
+}
+
+static void __init omap_4430hsi_pad_conf(void)
+{
+ /*
+ * HSI pad conf: hsi1_ca/ac_wake/flag/data/ready
+ * Also configure gpio_92/95/157/187 used by modem
+ */
+ /* hsi1_cawake */
+ omap_mux_init_signal("usbb1_ulpitll_clk.hsi1_cawake", \
+ OMAP_PIN_INPUT_PULLDOWN | \
+ OMAP_PIN_OFF_NONE | \
+ OMAP_PIN_OFF_WAKEUPENABLE);
+ /* hsi1_caflag */
+ omap_mux_init_signal("usbb1_ulpitll_dir.hsi1_caflag", \
+ OMAP_PIN_INPUT | \
+ OMAP_PIN_OFF_NONE);
+ /* hsi1_cadata */
+ omap_mux_init_signal("usbb1_ulpitll_stp.hsi1_cadata", \
+ OMAP_PIN_INPUT | \
+ OMAP_PIN_OFF_NONE);
+ /* hsi1_acready */
+ omap_mux_init_signal("usbb1_ulpitll_nxt.hsi1_acready", \
+ OMAP_PIN_OUTPUT | \
+ OMAP_PIN_OFF_OUTPUT_LOW);
+ /* hsi1_acwake */
+ omap_mux_init_signal("usbb1_ulpitll_dat0.hsi1_acwake", \
+ OMAP_PIN_OUTPUT | \
+ OMAP_PIN_OFF_NONE);
+ /* hsi1_acdata */
+ omap_mux_init_signal("usbb1_ulpitll_dat1.hsi1_acdata", \
+ OMAP_PIN_OUTPUT | \
+ OMAP_PIN_OFF_NONE);
+ /* hsi1_acflag */
+ omap_mux_init_signal("usbb1_ulpitll_dat2.hsi1_acflag", \
+ OMAP_PIN_OUTPUT | \
+ OMAP_PIN_OFF_NONE);
+ /* hsi1_caready */
+ omap_mux_init_signal("usbb1_ulpitll_dat3.hsi1_caready", \
+ OMAP_PIN_INPUT | \
+ OMAP_PIN_OFF_NONE);
+ /* gpio_92 */
+ omap_mux_init_signal("usbb1_ulpitll_dat4.gpio_92", \
+ OMAP_PULL_ENA);
+ /* gpio_95 */
+ omap_mux_init_signal("usbb1_ulpitll_dat7.gpio_95", \
+ OMAP_PIN_INPUT_PULLDOWN | \
+ OMAP_PIN_OFF_NONE);
+ /* gpio_157 */
+ omap_mux_init_signal("usbb2_ulpitll_clk.gpio_157", \
+ OMAP_PIN_OUTPUT | \
+ OMAP_PIN_OFF_NONE);
+ /* gpio_187 */
+ omap_mux_init_signal("sys_boot3.gpio_187", \
+ OMAP_PIN_OUTPUT | \
+ OMAP_PIN_OFF_NONE);
+}
+
+/* HSI devices registration */
+int __init omap_hsi_init(void)
+{
+ omap_4430hsi_pad_conf();
+ /* Keep this for genericity, although there is only one hwmod for HSI */
+ return omap_hwmod_for_each_by_class(OMAP_HSI_HWMOD_CLASSNAME,
+ omap_hsi_register, NULL);
+}
diff --git a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
index eadc88e..2614dd3 100644
--- a/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
+++ b/arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -2683,7 +2683,7 @@ static struct omap_hwmod_class_sysconfig omap44xx_hsi_sysc = {
SYSC_HAS_SOFTRESET | SYSS_HAS_RESET_STATUS),
.idlemodes = (SIDLE_FORCE | SIDLE_NO | SIDLE_SMART |
SIDLE_SMART_WKUP | MSTANDBY_FORCE | MSTANDBY_NO |
- MSTANDBY_SMART),
+ MSTANDBY_SMART | MSTANDBY_SMART_WKUP),
.sysc_fields = &omap_hwmod_sysc_type1,
};
@@ -5624,7 +5624,7 @@ static __initdata struct omap_hwmod *omap44xx_hwmods[] = {
&omap44xx_gpio6_hwmod,
/* hsi class */
-/* &omap44xx_hsi_hwmod, */
+ &omap44xx_hsi_hwmod,
/* gpu class */
&omap44xx_gpu_hwmod,
diff --git a/arch/arm/mach-omap2/opp4xxx_data.c b/arch/arm/mach-omap2/opp4xxx_data.c
index 0eec22a..2d70bd1 100644
--- a/arch/arm/mach-omap2/opp4xxx_data.c
+++ b/arch/arm/mach-omap2/opp4xxx_data.c
@@ -141,7 +141,10 @@ static struct omap_opp_def __initdata omap443x_opp_def_list[] = {
OPP_INITIALIZER("dsp", "dpll_iva_m4x2_ck", "iva", true, 465500000, OMAP4430_VDD_IVA_OPP100_UV),
/* DSP OPP3 - OPPTB */
OPP_INITIALIZER("dsp", "dpll_iva_m4x2_ck", "iva", false, 496000000, OMAP4430_VDD_IVA_OPPTURBO_UV),
-
+ /* HSI OPP1 - OPP50 */
+ OPP_INITIALIZER("hsi", "hsi_fck", "core", true, 96000000, OMAP4430_VDD_CORE_OPP50_UV),
+ /* HSI OPP2 - OPP100 */
+ OPP_INITIALIZER("hsi", "hsi_fck", "core", true, 96000000, OMAP4430_VDD_CORE_OPP100_UV),
/* TODO: add aess */
};
@@ -257,7 +260,6 @@ static struct omap_opp_def __initdata omap446x_opp_def_list[] = {
OPP_INITIALIZER("gpu", "dpll_per_m7x2_ck", "core", true, 307200000, OMAP4460_VDD_CORE_OPP100_UV),
/* SGX OPP3 - OPPOV */
OPP_INITIALIZER("gpu", "dpll_per_m7x2_ck", "core", true, 384000000, OMAP4460_VDD_CORE_OPP100_OV_UV),
-
/* FDIF OPP1 - OPP25 */
OPP_INITIALIZER("fdif", "fdif_fck", "core", true, 32000000, OMAP4430_VDD_CORE_OPP50_UV),
/* FDIF OPP2 - OPP50 */
@@ -270,6 +272,10 @@ static struct omap_opp_def __initdata omap446x_opp_def_list[] = {
OPP_INITIALIZER("dsp", "dpll_iva_m4x2_ck", "iva", true, 465500000, OMAP4430_VDD_IVA_OPP100_UV),
/* DSP OPP3 - OPPTB */
OPP_INITIALIZER("dsp", "dpll_iva_m4x2_ck", "iva", false, 496000000, OMAP4430_VDD_IVA_OPPTURBO_UV),
+ /* HSI OPP1 - OPP50 */
+ OPP_INITIALIZER("hsi", "hsi_fck", "core", true, 96000000, OMAP4460_VDD_CORE_OPP50_UV),
+ /* HSI OPP2 - OPP100 */
+ OPP_INITIALIZER("hsi", "hsi_fck", "core", true, 96000000, OMAP4460_VDD_CORE_OPP100_UV),
/* TODO: add aess */
};
diff --git a/arch/arm/mach-omap2/pm44xx.c b/arch/arm/mach-omap2/pm44xx.c
index fae39a6..7496b32 100644
--- a/arch/arm/mach-omap2/pm44xx.c
+++ b/arch/arm/mach-omap2/pm44xx.c
@@ -94,14 +94,14 @@ void omap4_enter_sleep(unsigned int cpu, unsigned int power_state)
omap_uart_prepare_idle(1);
omap_uart_prepare_idle(2);
omap_uart_prepare_idle(3);
- omap2_gpio_prepare_for_idle(0);
+ //omap2_gpio_prepare_for_idle(0);
omap4_trigger_ioctrl();
}
omap4_enter_lowpower(cpu, power_state);
if (core_next_state < PWRDM_POWER_ON) {
- omap2_gpio_resume_after_idle();
+ //omap2_gpio_resume_after_idle();
omap_uart_resume_idle(0);
omap_uart_resume_idle(1);
omap_uart_resume_idle(2);
@@ -304,6 +304,17 @@ 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 */
+ #define CA_WAKE_MUX_REG (0x4a1000C2)
+ #define CM_L3INIT_HSI_CLKCTRL (0x4a009338)
+ #define HSI_SYSCONFIG (0x4a058010)
+ if (omap_readw(CA_WAKE_MUX_REG) & (1<<15)) {
+ /* Enable HSI module */
+ omap_writel(omap_readl(CM_L3INIT_HSI_CLKCTRL) | 0x1, CM_L3INIT_HSI_CLKCTRL);
+ /* Put HSI in: No-standby and No-idle */
+ omap_writel( (1<<3) | (1<<12), HSI_SYSCONFIG);
+ }
omap4_trigger_ioctrl();
}
diff --git a/arch/arm/plat-omap/include/plat/omap_hsi.h b/arch/arm/plat-omap/include/plat/omap_hsi.h
new file mode 100644
index 0000000..b5a5334
--- /dev/null
+++ b/arch/arm/plat-omap/include/plat/omap_hsi.h
@@ -0,0 +1,494 @@
+/*
+ * /mach/omap_hsi.h
+ *
+ * Hardware definitions for HSI and SSI.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/* NOTE: This file defines the registers address offsets for both the
+ * SSI and HSI devices. Most of the registers share the same offset between
+ * these devices.
+ * When common or HSI only, the constants are name HSI*. Else the SSI specific
+ * constants are name HSI_SSI*
+ */
+
+#ifndef __OMAP_HSI_H__
+#define __OMAP_HSI_H__
+
+/* Set the HSI Functional Clock to 96MHz.
+ * This is to ensure HSI will function even at OPP50. */
+#define HSI_DEFAULT_FCLK 96000000 /* 96 MHz */
+
+
+#define HSI_PORT_OFFSET 0x1000
+
+/*
+ * GDD base addr : 0x48059000 (SSI)
+ * GDD base addr : 0x4A059000 (HSI)
+ */
+#define HSI_GDD_OFFSET 0x1000
+#define HSI_GDD_BASE HSI_GDD_OFFSET /* 0x9000 */
+
+/*
+ * HST base addr:
+ * port 1: 0x4805a000 (SSI) - 0x4A05a000 (HSI)
+ * port 2: 0x4805b000 (SSI) - 0x4a05b000 (HSI)
+ */
+#define HSI_HST_OFFSET 0x2000
+#define HSI_HST_BASE(port) (HSI_HST_OFFSET + (((port) - 1) *\
+ (HSI_PORT_OFFSET)))
+ /*
+ * HSR base addr:
+ * port 1: 0x4805a800 (SSI) - 0x4A05a800 (HSI)
+ * port 2: 0x4805b800 (SSI) - 0x4A05b800 (HSI)
+ */
+#define HSI_HSR_OFFSET 0x2800
+#define HSI_HSR_BASE(port) (HSI_HSR_OFFSET + (((port) - 1) *\
+ (HSI_PORT_OFFSET)))
+/*
+ * HSI SYS registers
+ */
+#define HSI_SYS_REVISION_REG 0x0000
+#define HSI_SSI_REV_MASK 0x000000ff
+#define HSI_SSI_REV_MAJOR 0xf0
+#define HSI_SSI_REV_MINOR 0x0f
+
+#define HSI_SYS_SYSCONFIG_REG 0x0010
+#define HSI_AUTOIDLE (1 << 0)
+#define HSI_SOFTRESET (1 << 1)
+#define HSI_FREE_EMU (1 << 2) /* Only for HSI */
+#define HSI_SIDLEMODE_FORCE 0
+#define HSI_SIDLEMODE_NO (1 << 3)
+#define HSI_SIDLEMODE_SMART (1 << 4)
+#define HSI_SIDLEMODE_SMART_WAKEUP (3 << 3)
+#define HSI_SIDLEMODE_MASK 0x00000018
+#define HSI_MIDLEMODE_FORCE 0
+#define HSI_MIDLEMODE_NO (1 << 12)
+#define HSI_MIDLEMODE_SMART (1 << 13)
+#define HSI_MIDLEMODE_SMART_WAKEUP (3 << 12)
+#define HSI_MIDLEMODE_MASK 0x00003000
+
+#define HSI_SYS_SYSSTATUS_REG 0x0014
+#define HSI_RESETDONE 1
+
+#define HSI_SYS_MPU_STATUS_BASE 0x0808
+#define HSI_SYS_MPU_STATUS_PORT_OFFSET 0x10
+#define HSI_SYS_MPU_STATUS_IRQ_OFFSET 8
+
+#define HSI_SYS_MPU_STATUS_REG(port, irq) \
+ (HSI_SYS_MPU_STATUS_BASE + \
+ ((((port) - 1) * HSI_SYS_MPU_STATUS_PORT_OFFSET) +\
+ ((irq) * HSI_SYS_MPU_STATUS_IRQ_OFFSET)))
+#define HSI_SYS_MPU_ENABLE_BASE 0x080c
+#define HSI_SYS_MPU_ENABLE_PORT_OFFSET 0x10
+#define HSI_SYS_MPU_ENABLE_IRQ_OFFSET 8
+
+#define HSI_SYS_MPU_ENABLE_REG(port, irq) \
+ (HSI_SYS_MPU_ENABLE_BASE + \
+ ((((port) - 1) * HSI_SYS_MPU_ENABLE_PORT_OFFSET) +\
+ ((irq) * HSI_SYS_MPU_ENABLE_IRQ_OFFSET)))
+#define HSI_HST_DATAACCEPT(channel) (((channel) < 8) ? \
+ (1 << (channel)) : \
+ (1 << ((channel) - 8)))
+#define HSI_HSR_DATAAVAILABLE(channel) ((channel) < 8 ? \
+ (1 << ((channel) + 8)) : \
+ (1 << ((channel) - 8 + 8)))
+#define HSI_HSR_DATAOVERRUN(channel) ((channel) < 8 ? \
+ (1 << ((channel) + 16)) : \
+ (1 << ((channel) - 8 + 16)))
+
+#define HSI_ERROROCCURED (1 << 24)
+#define HSI_BREAKDETECTED (1 << 25)
+#define HSI_CAWAKEDETECTED (1 << 26)
+
+#define HSI_SYS_GDD_MPU_IRQ_STATUS_REG 0x0800
+#define HSI_SYS_GDD_MPU_IRQ_ENABLE_REG 0x0804
+#define HSI_GDD_LCH(channel) (1 << (channel))
+
+
+#define HSI_SYS_WAKE_OFFSET 0x10
+#define HSI_SYS_WAKE_BASE 0x0c00
+#define HSI_SYS_WAKE_REG(port) (HSI_SYS_WAKE_BASE +\
+ (((port) - 1) * HSI_SYS_WAKE_OFFSET))
+
+#define HSI_SYS_CLEAR_WAKE_BASE 0x0c04
+#define HSI_SYS_CLEAR_WAKE_REG(port) (HSI_SYS_CLEAR_WAKE_BASE +\
+ (((port) - 1) * HSI_SYS_WAKE_OFFSET))
+
+#define HSI_SYS_SET_WAKE_BASE 0x0c08
+#define HSI_SYS_SET_WAKE_REG(port) (HSI_SYS_SET_WAKE_BASE +\
+ (((port) - 1) * HSI_SYS_WAKE_OFFSET))
+
+#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_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_CLEAR_WAKE(channel) (1 << (channel))
+#define HSI_WAKE(channel) (1 << (channel))
+
+#define HSI_SYS_HWINFO_REG 0x0004 /* only for HSI */
+
+/* Additional registers definitions (for channels 8 .. 15) for HSI */
+#define HSI_SYS_MPU_U_STATUS_BASE 0x0408
+#define HSI_SYS_MPU_U_STATUS_REG(port, irq) \
+ (HSI_SYS_MPU_U_STATUS_BASE + \
+ ((((port) - 1) * HSI_SYS_MPU_STATUS_PORT_OFFSET) +\
+ ((irq) * HSI_SYS_MPU_STATUS_IRQ_OFFSET)))
+
+#define HSI_SYS_MPU_U_ENABLE_BASE 0x040c
+#define HSI_SYS_MPU_U_ENABLE_REG(port, irq) \
+ (HSI_SYS_MPU_U_ENABLE_BASE + \
+ ((((port) - 1) * HSI_SYS_MPU_ENABLE_PORT_OFFSET) +\
+ ((irq) * HSI_SYS_MPU_ENABLE_IRQ_OFFSET)))
+
+/*
+ * HSI HST registers
+ */
+#define HSI_HST_ID_REG(port) (HSI_HST_BASE(port) + 0x0000)
+
+#define HSI_HST_MODE_REG(port) (HSI_HST_BASE(port) + 0x0004)
+#define HSI_MODE_VAL_MASK 3
+#define HSI_MODE_SLEEP 0
+#define HSI_MODE_STREAM 1
+#define HSI_MODE_FRAME 2
+#define HSI_SSI_MODE_MULTIPOINTS 3 /* SSI only */
+#define HSI_FLOW_OFFSET 2 /* HSI only */
+#define HSI_FLOW_VAL_MASK 3 /* HSI only */
+#define HSI_FLOW_SYNCHRONIZED 0 /* HSI only */
+#define HSI_FLOW_PIPELINED 1 /* HSI only */
+#define HSI_FLOW_REAL_TIME 2 /* HSI only */
+#define HSI_HST_MODE_WAKE_CTRL_AUTO (1 << 4) /* HSI only */
+#define HSI_HST_MODE_WAKE_CTRL_SW (0 << 4) /* HSI only */
+
+#define HSI_HST_FRAMESIZE_REG(port) (HSI_HST_BASE(port) + 0x0008)
+#define HSI_FRAMESIZE_DEFAULT 31
+#define HSI_FRAMESIZE_MAX 0x1f
+
+#define HSI_HST_TXSTATE_REG(port) (HSI_HST_BASE(port) + 0x000c)
+#define HSI_HST_TXSTATE_VAL_MASK 0x07
+#define HSI_HST_TXSTATE_IDLE 0
+
+#define HSI_HST_BUFSTATE_REG(port) (HSI_HST_BASE(port) + 0x0010)
+#define HSI_HST_BUFSTATE_FIFO_REG(fifo) (((fifo) < 8) ? \
+ HSI_HST_BUFSTATE_REG(1) : \
+ HSI_HST_BUFSTATE_REG(2))
+#define HSI_BUFSTATE_CHANNEL(channel) ((channel) < 8 ? \
+ (1 << (channel)) : \
+ (1 << ((channel) - 8)))
+
+#define HSI_HST_DIVISOR_REG(port) (HSI_HST_BASE(port) + 0x0018)
+#define HSI_DIVISOR_DEFAULT 1
+#define HSI_SSI_MAX_TX_DIVISOR 0x7f /* for SSI */
+#define HSI_MAX_TX_DIVISOR 0xff /* for HSI */
+
+#define HSI_HST_BREAK_REG(port) (HSI_HST_BASE(port) + 0x0020)
+#define HSI_HST_CHANNELS_REG(port) (HSI_HST_BASE(port) + 0x0024)
+#define HSI_CHANNELS_DEFAULT 4
+#define HSI_SSI_CHANNELS_MAX 8 /* for SSI */
+#define HSI_CHANNELS_MAX 16 /* for HSI */
+
+#define HSI_HST_ARBMODE_REG(port) (HSI_HST_BASE(port) + 0x0028)
+#define HSI_ARBMODE_ROUNDROBIN 0
+#define HSI_ARBMODE_PRIORITY 1
+
+#define HSI_HST_BUFFER_BASE(port) (HSI_HST_BASE(port) + 0x0080)
+#define HSI_HST_BUFFER_CH_REG(port, channel) (HSI_HST_BUFFER_BASE(port) +\
+ ((channel) * 4))
+#define HSI_HST_BUFFER_FIFO_REG(fifo) (((fifo) < 8) ? \
+ (HSI_HST_BUFFER_CH_REG(1, (fifo))) : \
+ (HSI_HST_BUFFER_CH_REG(2, (fifo) - 8)))
+
+#define HSI_HST_SWAPBUF_BASE(port) (HSI_HST_BASE(port) + 0x00c0)
+#define HSI_HST_SWAPBUF_CH_REG(port, channel) (HSI_HST_SWAPBUF_BASE(port) +\
+ ((channel) * 4))
+
+
+/* Additional registers for HSI */
+#define HSI_HST_FIFO_COUNT 16
+#define HSI_HST_FIFO_SIZE 8
+#define HSI_HST_MAPPING_FIFO_REG(fifo) (HSI_HST_BASE(1) + 0x0100 +\
+ ((fifo) * 4))
+#define HSI_MAPPING_ENABLE 1
+#define HSI_MAPPING_CH_NUMBER_OFFSET 1
+#define HSI_MAPPING_PORT_NUMBER_OFFSET 7
+#define HSI_HST_MAPPING_THRESH_OFFSET 10
+#define HSI_HST_MAPPING_THRESH_VALUE (0x0 << HSI_HST_MAPPING_THRESH_OFFSET)
+
+/*
+ * HSI HSR registers
+ */
+#define HSI_HSR_ID_REG(port) (HSI_HSR_BASE(port) + 0x0000)
+
+#define HSI_HSR_MODE_REG(port) (HSI_HSR_BASE(port) + 0x0004)
+
+#define HSI_HSR_MODE_MODE_VAL_MASK (3 << 0) /* HSI only */
+#define HSI_HSR_MODE_FLOW_VAL_MASK (3 << 2) /* HSI only */
+#define HSI_HSR_MODE_WAKE_STATUS (1 << 4) /* HSI only */
+#define HSI_HSR_MODE_MODE_VAL_SLEEP 0xFFFFFFFC /* HSI only */
+
+#define HSI_HSR_FRAMESIZE_REG(port) (HSI_HSR_BASE(port) + 0x0008)
+
+#define HSI_HSR_RXSTATE_REG(port) (HSI_HSR_BASE(port) + 0x000c)
+
+#define HSI_HSR_BUFSTATE_REG(port) (HSI_HSR_BASE(port) + 0x0010)
+#define HSI_HSR_BUFSTATE_FIFO_REG(fifo) (((fifo) < 8) ? \
+ HSI_HSR_BUFSTATE_REG(1) : \
+ HSI_HSR_BUFSTATE_REG(2))
+
+#define HSI_HSR_BREAK_REG(port) (HSI_HSR_BASE(port) + 0x001c)
+
+#define HSI_HSR_ERROR_REG(port) (HSI_HSR_BASE(port) + 0x0020)
+#define HSI_HSR_ERROR_SIG 1
+#define HSI_HSR_ERROR_FTE (1 << 1) /* HSI only */
+#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_ERRORACK_REG(port) (HSI_HSR_BASE(port) + 0x0024)
+
+#define HSI_HSR_CHANNELS_REG(port) (HSI_HSR_BASE(port) + 0x0028)
+
+#define HSI_HSR_OVERRUN_REG(port) (HSI_HSR_BASE(port) + 0x002c)
+
+#define HSI_HSR_OVERRUNACK_REG(port) (HSI_HSR_BASE(port) + 0x0030)
+
+#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 */
+#define HSI_SSI_RX_TIMEOUT_OFFSET 0 /* SSI only */
+#define HSI_SSI_RX_TIMEOUT_MASK 0x1ff /* SSI only */
+#define HSI_COUNTERS_FT_MASK 0x000fffff /* HSI only */
+#define HSI_COUNTERS_TB_MASK 0x00f00000 /* HSI only */
+#define HSI_COUNTERS_FB_MASK 0xff000000 /* HSI only */
+#define HSI_COUNTERS_FT_OFFSET 0 /* HSI only */
+#define HSI_COUNTERS_TB_OFFSET 20 /* HSI only */
+#define HSI_COUNTERS_FB_OFFSET 24 /* HSI only */
+/* Default FT value: 2 x max_bits_per_frame + 20% margin */
+#define HSI_COUNTERS_FT_DEFAULT (90 << HSI_COUNTERS_FT_OFFSET)
+#define HSI_COUNTERS_TB_DEFAULT (6 << HSI_COUNTERS_TB_OFFSET)
+#define HSI_COUNTERS_FB_DEFAULT (8 << HSI_COUNTERS_FB_OFFSET)
+#define HSI_HSR_COMBINE_COUNTERS(FB, TB, FT) \
+ (((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))
+#define SSI_SSR_COMBINE_COUNTERS(FT) \
+ ((FT << HSI_SSI_RX_TIMEOUT_OFFSET) & HSI_SSI_RX_TIMEOUT_MASK)
+
+#define HSI_HSR_BUFFER_BASE(port) (HSI_HSR_BASE(port) + 0x0080)
+#define HSI_HSR_BUFFER_CH_REG(port, channel) (HSI_HSR_BUFFER_BASE(port) +\
+ ((channel) * 4))
+#define HSI_HSR_BUFFER_FIFO_REG(fifo) (((fifo) < 8) ? \
+ (HSI_HSR_BUFFER_CH_REG(1, (fifo))) : \
+ (HSI_HSR_BUFFER_CH_REG(2, (fifo) - 8)))
+
+#define HSI_HSR_SWAPBUF_BASE(port) (HSI_HSR_BASE(port) + 0x00c0)
+#define HSI_HSR_SWAPBUF_CH_REG(port, channel) (HSI_HSR_SWAPBUF_BASE(port) +\
+ ((channel) * 4))
+
+/* Additional registers for HSI */
+#define HSI_HSR_FIFO_COUNT 16
+#define HSI_HSR_FIFO_SIZE 8
+#define HSI_HSR_MAPPING_FIFO_REG(fifo) (HSI_HSR_BASE(1) + 0x0100 +\
+ ((fifo) * 4))
+#define HSI_HSR_MAPPING_WORDS_MASK (0xf << 10)
+
+#define HSI_HSR_DLL_REG (HSI_HSR_BASE(1) + 0x0144)
+#define HSI_HSR_DLL_COCHRE 1
+#define HSI_HSR_DLL_COCHGR (1 << 4)
+#define HSI_HSR_DLL_INCO_MASK 0x0003ff00
+#define HSI_HSR_DLL_INCO_OFFSET 8
+
+#define HSI_HSR_DIVISOR_REG(port) (HSI_HSR_BASE(port) + 0x014C)
+#define HSI_HSR_DIVISOR_MASK 0xff
+#define HSI_MAX_RX_DIVISOR 0xff
+
+/*
+ * HSI GDD registers
+ */
+#define HSI_SSI_DMA_CHANNEL_MAX 8
+#define HSI_HSI_DMA_CHANNEL_MAX 16
+
+#define HSI_SSI_GDD_HW_ID_REG (HSI_GDD_BASE + 0x0000)
+
+#define HSI_SSI_GDD_PPORT_ID_REG (HSI_GDD_BASE + 0x0010)
+
+#define HSI_SSI_GDD_MPORT_ID_REG (HSI_GDD_BASE + 0x0014)
+
+#define HSI_SSI_GDD_PPORT_SR_REG (HSI_GDD_BASE + 0x0020)
+#define HSI_PPORT_ACTIVE_LCH_NUMBER_MASK 0xff
+
+#define HSI_GDD_MPORT_SR_REG (HSI_GDD_BASE + 0x0024)
+#define HSI_SSI_MPORT_ACTIVE_LCH_NUMBER_MASK 0xff
+
+#define HSI_SSI_GDD_TEST_REG (HSI_GDD_BASE + 0x0040)
+#define HSI_SSI_TEST 1
+
+#define HSI_GDD_GCR_REG (HSI_GDD_BASE + 0x0100)
+#define HSI_CLK_AUTOGATING_ON (1 << 3)
+#define HSI_SWITCH_OFF (1 << 0)
+
+#define HSI_GDD_GRST_REG (HSI_GDD_BASE + 0x0200)
+#define HSI_GDD_GRST_SWRESET 1
+
+#define HSI_GDD_CSDP_BASE (HSI_GDD_BASE + 0x0800)
+#define HSI_GDD_CSDP_OFFSET 0x40
+#define HSI_GDD_CSDP_REG(channel) (HSI_GDD_CSDP_BASE +\
+ ((channel) * HSI_GDD_CSDP_OFFSET))
+
+#define HSI_DST_BURST_EN_MASK 0xc000
+#define HSI_DST_SINGLE_ACCESS0 0
+#define HSI_DST_SINGLE_ACCESS (1 << 14)
+#define HSI_DST_BURST_4X32_BIT (2 << 14)
+#define HSI_DST_BURST_8x32_BIT (3 << 14)
+
+#define HSI_DST_MASK 0x1e00
+#define HSI_DST_MEMORY_PORT (8 << 9)
+#define HSI_DST_PERIPHERAL_PORT (9 << 9)
+
+#define HSI_SRC_BURST_EN_MASK 0x0180
+#define HSI_SRC_SINGLE_ACCESS0 0
+#define HSI_SRC_SINGLE_ACCESS (1 << 7)
+#define HSI_SRC_BURST_4x32_BIT (2 << 7)
+#define HSI_SRC_BURST_8x32_BIT (3 << 7)
+
+#define HSI_SRC_MASK 0x003c
+#define HSI_SRC_MEMORY_PORT (8 << 2)
+#define HSI_SRC_PERIPHERAL_PORT (9 << 2)
+
+#define HSI_DATA_TYPE_MASK 3
+#define HSI_DATA_TYPE_S32 2
+
+#define HSI_GDD_CCR_BASE (HSI_GDD_BASE + 0x0802)
+#define HSI_GDD_CCR_OFFSET 0x40
+#define HSI_GDD_CCR_REG(channel) (HSI_GDD_CCR_BASE +\
+ ((channel) * HSI_GDD_CCR_OFFSET))
+#define HSI_DST_AMODE_MASK (3 << 14)
+#define HSI_DST_AMODE_CONST 0
+#define HSI_DST_AMODE_POSTINC (1 << 14)
+
+#define HSI_SRC_AMODE_MASK (3 << 12)
+#define HSI_SRC_AMODE_CONST 0
+#define HSI_SRC_AMODE_POSTINC (1 << 12)
+
+#define HSI_CCR_ENABLE (1 << 7)
+
+#define HSI_CCR_SYNC_MASK 0x001f /* only for SSI */
+
+#define HSI_GDD_CCIR_BASE (HSI_GDD_BASE + 0x0804)
+#define HSI_GDD_CCIR_OFFSET 0x40
+#define HSI_GDD_CCIR_REG(channel) (HSI_GDD_CCIR_BASE +\
+ ((channel) * HSI_GDD_CCIR_OFFSET))
+
+#define HSI_BLOCK_IE (1 << 5)
+#define HSI_HALF_IE (1 << 2)
+#define HSI_TOUT_IE (1 << 0)
+
+#define HSI_GDD_CSR_BASE (HSI_GDD_BASE + 0x0806)
+#define HSI_GDD_CSR_OFFSET 0x40
+#define HSI_GDD_CSR_REG(channel) (HSI_GDD_CSR_BASE +\
+ ((channel) * HSI_GDD_CSR_OFFSET))
+
+#define HSI_CSR_SYNC (1 << 6)
+#define HSI_CSR_BLOCK (1 << 5) /* Full block is transferred */
+#define HSI_CSR_HALF (1 << 2) /* Half block is transferred */
+#define HSI_CSR_TOUT (1 << 0) /* Time-out overflow occurs */
+
+#define HSI_GDD_CSSA_BASE (HSI_GDD_BASE + 0x0808)
+#define HSI_GDD_CSSA_OFFSET 0x40
+#define HSI_GDD_CSSA_REG(channel) (HSI_GDD_CSSA_BASE +\
+ ((channel) * HSI_GDD_CSSA_OFFSET))
+
+
+#define HSI_GDD_CDSA_BASE (HSI_GDD_BASE + 0x080c)
+#define HSI_GDD_CDSA_OFFSET 0x40
+#define HSI_GDD_CDSA_REG(channel) (HSI_GDD_CDSA_BASE +\
+ ((channel) * HSI_GDD_CDSA_OFFSET))
+
+#define HSI_GDD_CEN_BASE (HSI_GDD_BASE + 0x0810)
+#define HSI_GDD_CEN_OFFSET 0x40
+#define HSI_GDD_CEN_REG(channel) (HSI_GDD_CEN_BASE +\
+ ((channel) * HSI_GDD_CEN_OFFSET))
+
+
+#define HSI_GDD_CSAC_BASE (HSI_GDD_BASE + 0x0818)
+#define HSI_GDD_CSAC_OFFSET 0x40
+#define HSI_GDD_CSAC_REG(channel) (HSI_GDD_CSAC_BASE +\
+ ((channel) * HSI_GDD_CSAC_OFFSET))
+
+#define HSI_GDD_CDAC_BASE (HSI_GDD_BASE + 0x081a)
+#define HSI_GDD_CDAC_OFFSET 0x40
+#define HSI_GDD_CDAC_REG(channel) (HSI_GDD_CDAC_BASE +\
+ ((channel) * HSI_GDD_CDAC_OFFSET))
+
+#define HSI_SSI_GDD_CLNK_CTRL_BASE (HSI_GDD_BASE + 0x0828)
+#define HSI_SSI_GDD_CLNK_CTRL_OFFSET 0x40
+#define HSI_SSI_GDD_CLNK_CTRL_REG(channel) (HSI_SSI_GDD_CLNK_CTRL_BASE +\
+ (channel * HSI_SSI_GDD_CLNK_CTRL_OFFSET))
+
+#define HSI_SSI_ENABLE_LNK (1 << 15)
+#define HSI_SSI_STOP_LNK (1 << 14)
+#define HSI_SSI_NEXT_CH_ID_MASK 0xf
+
+/*
+ * HSI Helpers
+ */
+#define HSI_SYS_MPU_ENABLE_CH_REG(port, irq, channel) \
+ (((channel) < HSI_SSI_CHANNELS_MAX) ? \
+ HSI_SYS_MPU_ENABLE_REG(port, irq) : \
+ HSI_SYS_MPU_U_ENABLE_REG(port, irq))
+
+#define HSI_SYS_MPU_STATUS_CH_REG(port, irq, channel) \
+ ((channel < HSI_SSI_CHANNELS_MAX) ? \
+ HSI_SYS_MPU_STATUS_REG(port, irq) : \
+ HSI_SYS_MPU_U_STATUS_REG(port, irq))
+/**
+ * struct omap_ssi_config - SSI board configuration
+ * @num_ports: Number of ports in use
+ * @cawake_line: Array of cawake gpio lines
+ */
+struct omap_ssi_board_config {
+ unsigned int num_ports;
+ int cawake_gpio[2];
+};
+extern int omap_ssi_config(struct omap_ssi_board_config *ssi_config);
+
+/**
+ * struct omap_hsi_config - HSI board configuration
+ * @num_ports: Number of ports in use
+ */
+struct omap_hsi_board_config {
+ unsigned int num_ports;
+};
+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_wakeup(int hsi_port);
+extern int omap_hsi_is_io_wakeup_from_hsi(void);
+#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_wakeup(void) { return -ENOSYS; }
+inline int omap_hsi_is_io_wakeup_from_hsi(void) { return -ENOSYS; }
+
+#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/drivers/Kconfig b/drivers/Kconfig
index d930c6a..1cce7f2 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -133,4 +133,6 @@ source "drivers/remoteproc/Kconfig"
source "drivers/virtio/Kconfig"
source "drivers/rpmsg/Kconfig"
+
+source "drivers/omap_hsi/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 49c39b3..2f047a4 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -128,3 +128,4 @@ obj-$(CONFIG_REMOTE_PROC) += remoteproc/
obj-$(CONFIG_DMM_OMAP) += media/
obj-$(CONFIG_TILER_OMAP) += media/
+obj-$(CONFIG_OMAP_HSI) += omap_hsi/
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..04a1de7
--- /dev/null
+++ b/drivers/input/misc/gp2a.c
@@ -0,0 +1,667 @@
+/* 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)
+
+#define ADC_BUFFER_NUM 6
+
+/* 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
+
+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;
+ int adc_value_buf[ADC_BUFFER_NUM];
+ int adc_index_count;
+ bool adc_buf_initialized;
+ 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));
+ hrtimer_start(&gp2a->timer, gp2a->light_poll_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);
+ /* mark the adc buff as not initialized
+ * so that it will be filled again on next light sensor start
+ */
+ gp2a->adc_buf_initialized = false;
+}
+
+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));
+ 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 int lightsensor_get_adcvalue(struct gp2a_data *gp2a)
+{
+ int i = 0;
+ int j = 0;
+ unsigned int adc_total = 0;
+ int adc_avr_value;
+ unsigned int adc_index = 0;
+ unsigned int adc_max = 0;
+ unsigned int adc_min = 0;
+ int value = 0;
+
+ /* get ADC */
+ value = gp2a->pdata->light_adc_value();
+ gp2a_dbgmsg("adc returned light value %d\n", value);
+
+ adc_index = (gp2a->adc_index_count++) % ADC_BUFFER_NUM;
+
+ /* ADC buffer initialize (light sensor off ---> light sensor on) */
+ if (!gp2a->adc_buf_initialized) {
+ gp2a->adc_buf_initialized = true;
+ for (j = 0; j < ADC_BUFFER_NUM; j++)
+ gp2a->adc_value_buf[j] = value;
+ } else
+ gp2a->adc_value_buf[adc_index] = value;
+
+ adc_max = gp2a->adc_value_buf[0];
+ adc_min = gp2a->adc_value_buf[0];
+
+ for (i = 0; i < ADC_BUFFER_NUM; i++) {
+ adc_total += gp2a->adc_value_buf[i];
+
+ if (adc_max < gp2a->adc_value_buf[i])
+ adc_max = gp2a->adc_value_buf[i];
+
+ if (adc_min > gp2a->adc_value_buf[i])
+ adc_min = gp2a->adc_value_buf[i];
+ }
+ adc_avr_value = (adc_total-(adc_max+adc_min))/(ADC_BUFFER_NUM-2);
+
+ if (gp2a->adc_index_count == ADC_BUFFER_NUM-1)
+ gp2a->adc_index_count = 0;
+
+ gp2a_dbgmsg("average adc light value %d\n", adc_avr_value);
+ return adc_avr_value;
+}
+
+static void gp2a_work_func_light(struct work_struct *work)
+{
+ struct gp2a_data *gp2a = container_of(work, struct gp2a_data,
+ work_light);
+ int adc = lightsensor_get_adcvalue(gp2a);
+ 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;
+
+ 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);
+
+ ret = gp2a_setup_irq(gp2a);
+ if (ret) {
+ pr_err("%s: could not setup irq\n", __func__);
+ goto err_setup_irq;
+ }
+
+ /* 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);
+
+ 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, 1, 0, 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:
+err_input_allocate_device_proximity:
+ free_irq(gp2a->irq, gp2a);
+ gpio_free(gp2a->pdata->p_out);
+err_setup_irq:
+ 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 100644
index 0000000..a0beebc
--- /dev/null
+++ b/drivers/input/touchscreen/mms_ts.c
@@ -0,0 +1,893 @@
+/*
+ * 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
+
+#define REQUIRED_FW_VERSION 0x11
+
+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 = true;
+module_param_named(flash_from_probe, mms_flash_from_probe, 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;
+};
+
+#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;
+
+ 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;
+
+ ret = i2c_smbus_read_i2c_block_data(client, MMS_INPUT_EVENT0, sz, buf);
+
+#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;
+ 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;
+
+ 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);
+}
+
+static void isp_exit_mode(struct mms_ts_info *info)
+{
+ int i;
+
+ gpio_direction_output(info->pdata->gpio_resetb, 0);
+ udelay(3);
+
+ for (i = 0; i < 10; i++)
+ isp_toggle_clk(info, 1, 0, 3);
+}
+
+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);
+ msleep(25);
+ 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;
+
+ 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);
+
+ return val;
+}
+
+static void flash_writel(struct mms_ts_info *info, u16 addr, u32 val)
+{
+ 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 */
+ udelay(40);
+ isp_toggle_clk(info, 1, 0, 10);
+ isp_toggle_clk(info, 1, 0, 10);
+ udelay(20);
+ isp_toggle_clk(info, 1, 0, 10);
+ udelay(40);
+ isp_toggle_clk(info, 1, 0, 10);
+ isp_toggle_clk(info, 1, 0, 10);
+ isp_toggle_clk(info, 1, 0, 10);
+
+ gpio_direction_output(info->pdata->gpio_sda, 0);
+ isp_exit_mode(info);
+ 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 void fw_write_image(struct mms_ts_info *info, const u8 *data, size_t len)
+{
+ u16 addr = 0;
+
+ for (addr = 0; addr < (len / 4); addr++, data += 4) {
+ udelay(40);
+ flash_writel(info, addr, get_unaligned_le32(data));
+ }
+}
+
+static bool fw_verify_image(struct mms_ts_info *info, const u8 *data,
+ size_t len)
+{
+ struct i2c_client *client = info->client;
+ u16 addr;
+ u32 val;
+ u32 correct_val;
+
+ for (addr = 0; addr < (len / 4); addr++, data += 4) {
+ udelay(40);
+ val = flash_readl(info, addr);
+ correct_val = get_unaligned_le32(data);
+ if (val == correct_val)
+ continue;
+ dev_err(&client->dev, "mismatch @ addr 0x%x: 0x%x != 0x%x\n",
+ addr, val, correct_val);
+ return false;
+ }
+ return true;
+}
+
+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);
+ fw_write_image(info, data, len);
+ usleep_range(1000, 1500);
+
+ dev_info(&client->dev, "fw verify...\n");
+ if (!fw_verify_image(info, data, len)) {
+ ret = -ENXIO;
+ goto err;
+ }
+
+ 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);
+ 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);
+ 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(20 * MSEC_PER_SEC));
+
+ if (ret > 0) {
+ ret = mms_ts_enable(info);
+ } 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;
+
+ if (!fw) {
+ dev_err(&client->dev, "could not find firmware file '%s'\n",
+ info->fw_name);
+ goto out;
+ }
+
+ i2c_lock_adapter(adapter);
+ info->pdata->mux_fw_flash(true);
+
+ ret = fw_download(info, fw->data, fw->size);
+
+ 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",
+ REQUIRED_FW_VERSION);
+ goto out;
+ }
+
+ ver = get_fw_version(info);
+ if (ver == REQUIRED_FW_VERSION)
+ dev_info(&client->dev, "fw update done. ver = 0x%02x\n", ver);
+ else
+ dev_err(&client->dev,
+ "ERROR: fw update succeeded, but fw version is still wrong (%d != %d)\n",
+ ver, REQUIRED_FW_VERSION);
+
+ 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;
+ int ver;
+
+ mms_pwr_on_reset(info);
+
+ ver = get_fw_version(info);
+ if (ver < 0) {
+ ver = 0;
+ dev_err(&client->dev,
+ "can't read version, controller dead?! forcing reflash");
+ } else if (ver == REQUIRED_FW_VERSION && !mms_force_reflash) {
+ dev_info(&client->dev,
+ "fw version 0x%02x already present\n", ver);
+ ret = mms_ts_finish_config(info);
+ goto out;
+ }
+
+ dev_info(&client->dev, "need fw update (0x%02x != 0x%02x)\n",
+ ver, REQUIRED_FW_VERSION);
+
+ if (!info->pdata || !info->pdata->mux_fw_flash) {
+ dev_err(&client->dev,
+ "fw cannot be updated, missing platform data\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (nowait) {
+ const struct firmware *fw;
+ info->fw_name = kstrdup("melfas/mms144_ts.fw", GFP_KERNEL);
+ 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 = kasprintf(GFP_KERNEL, "mms144_v%02x.fw",
+ REQUIRED_FW_VERSION);
+ 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/misc/Kconfig b/drivers/misc/Kconfig
index b575799..5e22f3b 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -494,6 +494,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
@@ -539,9 +549,11 @@ config APANIC_PLABEL
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 2d43048..42b922b 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
@@ -52,3 +53,5 @@ 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/
diff --git a/drivers/misc/bmp180.c b/drivers/misc/bmp180.c
new file mode 100755
index 0000000..6de0021
--- /dev/null
+++ b/drivers/misc/bmp180.c
@@ -0,0 +1,645 @@
+/*
+ * 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_UPBOUND (500 * 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_UPBOUND)
+ return -EINVAL;
+
+ 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/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..0ea6422
--- /dev/null
+++ b/drivers/misc/inv_mpu/accel/bma250.c
@@ -0,0 +1,785 @@
+/*
+ $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;
+ result = inv_serial_read(mlsl_handle, pdata->address,
+ BMA250_STATUS_REG, 1, data);
+ 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..1e84ca6
--- /dev/null
+++ b/drivers/misc/inv_mpu/compass/yas530.c
@@ -0,0 +1,579 @@
+/*
+ $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"
+
+/* -------------------------------------------------------------------------- */
+#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 char dx, dy1, dy2;
+static char d2, d3, d4, d5, d6, d7, d8, d9, d0;
+static char dck;
+
+/* -------------------------------------------------------------------------- */
+
+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,
+ int *xo, int *yo, int *zo)
+{
+ int sx, sy1, sy2, sy, sz;
+ int 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 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;
+
+ unsigned char dummyData = 0x00;
+ char offset[3] = { 0, 0, 0 };
+ 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 = dx * 6 - 768;
+ Cy1 = dy1 * 6 - 768;
+ Cy2 = dy2 * 6 - 768;
+ a2 = d2 - 32;
+ a3 = d3 - 8;
+ a4 = d4 - 32;
+ a5 = d5 + 38;
+ a6 = d6 - 32;
+ a7 = d7 - 64;
+ a8 = d8 - 32;
+ a9 = d9;
+ k = 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;
+ }
+
+ /* Offset Measurement and Set */
+ result = measure_and_set_offset(mlsl_handle, slave, pdata, offset);
+ if (result) {
+ LOG_RESULT_LOCATION(result);
+ return result;
+ }
+
+ 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;
+ int xyz[3];
+ short rawfixed[3];
+
+ 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]);
+
+ 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;
+
+ return result;
+}
+
+static struct ext_slave_descr yas530_descr = {
+ .init = NULL,
+ .exit = NULL,
+ .suspend = yas530_suspend,
+ .resume = yas530_resume,
+ .read = yas530_read,
+ .config = NULL,
+ .get_config = NULL,
+ .name = "yas530",
+ .type = EXT_SLAVE_TYPE_COMPASS,
+ .id = COMPASS_ID_YAS530,
+ .read_reg = 0x06,
+ .read_len = 6,
+ .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..1d93c97
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpu-dev.c
@@ -0,0 +1,1312 @@
+/*
+ $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;
+ struct timeval event_time;
+ 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;
+ }
+
+ do_gettimeofday(&event_time);
+ mpu->mpu_pm_event.interruptcount++;
+ mpu->mpu_pm_event.irqtime =
+ (((long long)event_time.tv_sec) << 32) + event_time.tv_usec;
+ mpu->mpu_pm_event.data_type = MPUIRQ_DATA_TYPE_PM_EVENT;
+ mpu->mpu_pm_event.data = mpu->event;
+
+ if (event == PM_SUSPEND_PREPARE)
+ mpu->event = MPU_PM_EVENT_SUSPEND_PREPARE;
+ if (event == PM_POST_SUSPEND)
+ mpu->event = MPU_PM_EVENT_POST_SUSPEND;
+
+ 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) + sizeof(unsigned long))
+ 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..9e0e593
--- /dev/null
+++ b/drivers/misc/inv_mpu/mpuirq.c
@@ -0,0 +1,256 @@
+/*
+ $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;
+ struct timeval irqtime;
+ 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;
+
+ do_gettimeofday(&irqtime);
+ mpuirq_data.irqtime = (((long long)irqtime.tv_sec) << 32);
+ mpuirq_data.irqtime += irqtime.tv_usec;
+ 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..77a2917
--- /dev/null
+++ b/drivers/misc/inv_mpu/slaveirq.c
@@ -0,0 +1,265 @@
+/*
+ $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;
+ struct timeval irqtime;
+ mycount++;
+
+ data->data.interruptcount++;
+
+ /* wake up (unblock) for reading data from userspace */
+ data->data_ready = 1;
+
+ do_gettimeofday(&irqtime);
+ data->data.irqtime = (((long long)irqtime.tv_sec) << 32);
+ data->data.irqtime += irqtime.tv_usec;
+ 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..2ca324f
--- /dev/null
+++ b/drivers/misc/inv_mpu/timerirq.c
@@ -0,0 +1,296 @@
+/*
+ $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;
+ struct timeval irqtime;
+
+ data->data.interruptcount++;
+
+ data->data_ready = 1;
+
+ do_gettimeofday(&irqtime);
+ data->data.irqtime = (((long long)irqtime.tv_sec) << 32);
+ data->data.irqtime += irqtime.tv_usec;
+ 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/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..3baca23
--- /dev/null
+++ b/drivers/misc/modem_if/Makefile
@@ -0,0 +1,8 @@
+obj-y += modem.o modem_io_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 \ No newline at end of file
diff --git a/drivers/misc/modem_if/modem.c b/drivers/misc/modem_if/modem.c
new file mode 100755
index 0000000..a7bfe06
--- /dev/null
+++ b/drivers/misc/modem_if/modem.c
@@ -0,0 +1,220 @@
+/* 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;
+
+ /* get platform data */
+ pdata = pdev->dev.platform_data;
+
+ modemctl->name = pdata->name;
+
+ modemctl->gpio_cp_on = pdata->gpio_cp_on;
+ modemctl->gpio_reset_req_n = pdata->gpio_reset_req_n;
+ modemctl->gpio_cp_reset = pdata->gpio_cp_reset;
+ modemctl->gpio_pda_active = pdata->gpio_pda_active;
+ modemctl->gpio_phone_active = pdata->gpio_phone_active;
+ modemctl->gpio_cp_dump_int = pdata->gpio_cp_dump_int;
+ modemctl->gpio_flm_uart_sel = pdata->gpio_flm_uart_sel;
+ modemctl->gpio_cp_warm_reset = pdata->gpio_cp_warm_reset;
+
+ modemctl->irq_phone_active = platform_get_irq(pdev, 0);
+
+#ifdef CONFIG_LTE_MODEM_CMC221
+ modemctl->gpio_cp_off = pdata->gpio_cp_off;
+ modemctl->gpio_slave_wakeup = pdata->gpio_slave_wakeup;
+ modemctl->gpio_host_active = pdata->gpio_host_active;
+ modemctl->gpio_host_wakeup = pdata->gpio_host_wakeup;
+ modemctl->irq_host_wakeup = platform_get_irq(pdev, 1);
+#endif
+ /* init modemctl device for getting modemctl operations */
+ ret = call_modem_init_func(modemctl, pdata);
+ if (ret) {
+ kfree(modemctl);
+ return NULL;
+ }
+
+ pr_info("[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)
+{
+ 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;
+
+ /* link between io device and modem control */
+ iod->mc = modemctl;
+ modemctl->iod = iod;
+
+ /* register misc device or net device */
+ ret = init_io_device(iod);
+ if (ret) {
+ kfree(iod);
+ return NULL;
+ }
+
+ pr_info("[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);
+ 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_info("[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 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,
+ .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..d663ba5
--- /dev/null
+++ b/drivers/misc/modem_if/modem_io_device.c
@@ -0,0 +1,774 @@
+/* /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/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 0x1000 /* 4*1024 */
+
+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 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;
+
+ 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;
+ 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 int rx_hdlc_head_start_check(char *buf)
+{
+ if (strncmp(buf, hdlc_start, sizeof(hdlc_start))) {
+ pr_err("[MODEM_IF] Wrong HDLC start: 0x%x\n", *buf);
+ return -EBADMSG;
+ }
+ return sizeof(hdlc_start);
+}
+
+static int rx_hdlc_tail_check(char *buf)
+{
+ if (strncmp(buf, hdlc_end, sizeof(hdlc_end))) {
+ pr_err("[MODEM_IF] Wrong HDLC end: 0x%x\n", *buf);
+ return -EBADMSG;
+ }
+ return sizeof(hdlc_end);
+}
+
+/* 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)
+ return len; /*Wrong hdlc start*/
+
+ pr_debug("[MODEM_IF] check len : %d, rest : %d (%d)\n", len,
+ rest, __LINE__);
+
+ memcpy(&hdr->start, hdlc_start, len);
+ hdr->len = 0;
+
+ /* debug print */
+ switch (iod->format) {
+ case IPC_FMT:
+ case IPC_RAW:
+ case IPC_MULTI_RAW:
+ case IPC_RFS:
+ /* TODO: print buf... */
+ break;
+
+ case IPC_CMD:
+ case IPC_BOOT:
+ default:
+ break;
+ }
+ 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;
+ struct sk_buff *skb_new;
+
+ pr_debug("[MODEM_IF] head_size : %d, data_size : %d (%d)\n", head_size,
+ data_size, __LINE__);
+
+ /* first payload data - alloc skb */
+ if (!skb) {
+ switch (iod->format) {
+ case IPC_RFS:
+ alloc_size = min(data_size, (int)rest) + head_size;
+ alloc_size = min(alloc_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;
+ default:
+ skb = alloc_skb(alloc_size, GFP_ATOMIC);
+ if (unlikely(!skb))
+ return -ENOMEM;
+ break;
+ }
+ iod->skb_recv = skb;
+ }
+
+ /* if recv packet size is larger than user space */
+ while ((rest_len > MAX_RXDATA_SIZE) && (rest > 0)) {
+ len = MAX_RXDATA_SIZE - skb->len;
+ len = min(len, (int)rest);
+ len = min(len, rest_len);
+ memcpy(skb_put(skb, len), buf, len);
+ buf += len;
+ done_len += len;
+ rest -= len;
+ rest_len -= len;
+
+ if (!rest_len)
+ break;
+
+ rx_iodev_skb(iod);
+ iod->skb_recv = NULL;
+
+ alloc_size = min(rest_len, MAX_RXDATA_SIZE);
+ skb_new = alloc_skb(alloc_size, GFP_ATOMIC);
+ if (unlikely(!skb_new))
+ return -ENOMEM;
+ skb = iod->skb_recv = skb_new;
+ }
+
+ /* copy data to skb */
+ len = min(rest, alloc_size - skb->len);
+ len = min(len, rest_len);
+ pr_debug("[MODEM_IF] rest : %d, alloc_size : %d , len : %d (%d)\n",
+ rest, alloc_size, skb->len, __LINE__);
+
+ memcpy(skb_put(skb, len), buf, len);
+ done_len += len;
+ hdr->flag_len += done_len;
+
+ 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;
+
+ 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;
+ skb->protocol = htons(ETH_P_IP);
+
+ err = netif_rx(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 int rx_multipdp(struct io_device *iod)
+{
+ int ret;
+ 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];
+
+ real_iod->skb_recv = iod->skb_recv;
+ ret = rx_iodev_skb_raw(real_iod);
+ if (ret < 0)
+ pr_err("[MODEM_IF] failed!\n");
+ return ret;
+}
+
+/* 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;
+ 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)
+ 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 && iod->skb_recv) {
+ dev_kfree_skb_any(iod->skb_recv);
+ iod->skb_recv = NULL;
+
+ /* clear headers */
+ memset(&iod->h_data, 0x00, sizeof(struct header_data));
+ }
+
+ 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:
+ /* 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;
+}
+
+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;
+ 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
+ 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_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_ON\n");
+ return iod->mc->ops.modem_on(iod->mc);
+
+ case IOCTL_MODEM_OFF:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_OFF\n");
+ return iod->mc->ops.modem_off(iod->mc);
+
+ case IOCTL_MODEM_RESET:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_RESET\n");
+ return iod->mc->ops.modem_reset(iod->mc);
+
+ case IOCTL_MODEM_BOOT_ON:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_BOOT_ON\n");
+ return iod->mc->ops.modem_boot_on(iod->mc);
+
+ case IOCTL_MODEM_BOOT_OFF:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_BOOT_OFF\n");
+ return iod->mc->ops.modem_boot_off(iod->mc);
+
+ case IOCTL_MODEM_START:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_START\n");
+ return iod->link->init_comm(iod->link, iod);
+
+ case IOCTL_MODEM_STATUS:
+ pr_info("[MODEM_IF] misc_ioctl : IOCTL_MODEM_START\n");
+ return iod->mc->phone_state;
+
+ 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 */
+
+ frame_len = count + SIZE_OF_HDLC_START + get_header_size(iod)
+ + SIZE_OF_HDLC_END;
+ skb = alloc_skb(frame_len, GFP_ATOMIC);
+ if (!skb) {
+ pr_err("[MODEM_IF] fail alloc skb (%d)\n", __LINE__);
+ return -ENOMEM;
+ }
+
+ switch (iod->format) {
+ case IPC_BOOT:
+ if (copy_from_user(skb_put(skb, count), buf, count) != 0)
+ 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)
+ 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)
+ 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) {
+ pr_err("[MODEM_IF] no data from sk_rx_q\n");
+ return 0;
+ }
+
+ if (skb->len > count) {
+ pr_err("[MODEM_IF] skb len is too big = %d,%d!(%d)",
+ count, skb->len, __LINE__);
+ dev_kfree_skb_any(skb);
+ return -EFAULT;
+ }
+ pr_debug("[MODEM_IF] skb len : %d\n", skb->len);
+
+ pktsize = skb->len;
+ if (copy_to_user(buf, skb->data, pktsize) != 0)
+ return -EFAULT;
+ 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,
+ .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;
+
+ 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) {
+ netif_stop_queue(ndev);
+ 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->hard_header_len = 0;
+ ndev->addr_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);
+
+ 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:
+ iod->ndev = alloc_netdev(0, iod->name, vnet_setup);
+ if (!iod->ndev) {
+ pr_err("failed to alloc netdev\n");
+ return -ENOMEM;
+ }
+
+ ret = register_netdev(iod->ndev);
+ if (ret)
+ free_netdev(iod->ndev);
+
+ pr_err("%s: %d(iod:0x%p)", __func__, __LINE__, iod);
+ vnet = netdev_priv(iod->ndev);
+ pr_err("%s: %d(vnet:0x%p)", __func__, __LINE__, vnet);
+ vnet->iod = iod;
+
+ break;
+
+ case IODEV_DUMMY:
+ break;
+
+ default:
+ pr_err("wrong io_type : %d\n", iod->io_typ);
+ return -EINVAL;
+ }
+
+ pr_info("[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_mipi.c b/drivers/misc/modem_if/modem_link_device_mipi.c
new file mode 100755
index 0000000..b1df755
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_mipi.c
@@ -0,0 +1,1327 @@
+/* /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:
+ return hsi_init_handshake(mipi_ld,
+ HSI_INIT_MODE_FLASHLESS_BOOT);
+
+ case IPC_RFS:
+ case IPC_RAW:
+ default:
+ return 0;
+ }
+}
+
+static int mipi_hsi_send(struct link_device *ld, struct io_device *iod,
+ struct sk_buff *skb)
+{
+ int ret;
+ u32 mipi_boot_len = 0;
+ 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_BOOT: /* sync operation */
+ 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);
+
+ ret = if_hsi_read(&mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL],
+ (const char *)&mipi_boot_len, 4);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] read len fail : %d\n", ret);
+ return ret;
+ } else
+ pr_debug("[MIPI-HSI] read len Done : %d\n",
+ mipi_boot_len);
+
+ ret = if_hsi_read(&mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL],
+ (const char *)mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL].rx_data,
+ mipi_boot_len);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] read len fail : %d\n", ret);
+ return ret;
+ } else
+ pr_debug("[MIPI-HSI] read len Done\n");
+
+ ret = iod->recv(iod, (char *)mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL].rx_data, mipi_ld->hsi_channles[
+ HSI_FLASHLESS_CHANNEL].rx_count);
+ if (ret < 0) {
+ pr_err("[MIPI-HSI] iod recv fail : %d\n", ret);
+ return ret;
+ } else
+ pr_debug("[MIPI-HSI] iod recv Done : %d\n", ret);
+
+ return mipi_boot_len;
+
+ 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 (!work_pending(&ld->tx_work))
+ 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;
+ struct sk_buff *raw_skb;
+ int send_channel = 0;
+
+ while (ld->sk_fmt_tx_q.qlen || ld->sk_raw_tx_q.qlen) {
+ pr_debug("[MIPI-HSI] fmt qlen : %d, raw qlen:%d\n",
+ ld->sk_fmt_tx_q.qlen, ld->sk_raw_tx_q.qlen);
+
+ 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);
+
+ if (ld->com_state != COM_ONLINE) {
+ pr_err("[MIPI-HSI] CP not ready\n");
+ skb_queue_head(&ld->sk_fmt_tx_q, fmt_skb);
+ return;
+ }
+
+ 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;
+ 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);
+ }
+
+ raw_skb = skb_dequeue(&ld->sk_raw_tx_q);
+ if (raw_skb) {
+ if (ld->com_state != COM_ONLINE) {
+ pr_err("[MIPI-HSI] RAW CP not ready\n");
+ skb_queue_head(&ld->sk_raw_tx_q, raw_skb);
+ return;
+ }
+
+ pr_debug("[MIPI-HSI] dequeue. raw qlen:%d\n",
+ ld->sk_raw_tx_q.qlen);
+
+ ret = if_hsi_protocol_send(mipi_ld, HSI_RAW_CHANNEL,
+ (u32 *)raw_skb->data, raw_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(raw_skb);
+ }
+ }
+}
+
+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;
+
+ ret = hsi_ioctl(channel->dev, state ?
+ HSI_IOCTL_ACWAKE_UP : HSI_IOCTL_ACWAKE_DOWN, NULL);
+ if (ret) {
+ pr_debug("[MIPI-HSI] ACWAKE(%d) setting fail : %d\n", state,
+ ret);
+ return ret;
+ }
+
+ 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 = 1; 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);
+ return ret;
+ }
+ channel->opened = 1;
+
+ channel->send_step = STEP_IDLE;
+ channel->recv_step = STEP_IDLE;
+
+ pr_info("[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_ioctl(channel->dev, HSI_IOCTL_SW_RESET, NULL);
+
+ hsi_close(channel->dev);
+ channel->opened = 0;
+
+ channel->send_step = STEP_CLOSED;
+ channel->recv_step = STEP_CLOSED;
+
+ pr_info("[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);
+
+ 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_hsi_close_channel(&mipi_ld->hsi_channles[i]);
+ ret = if_hsi_open_channel(&mipi_ld->hsi_channles[i]);
+ if (ret)
+ return ret;
+
+ 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[i].dev,
+ HSI_IOCTL_SET_ACREADY_NORMAL, NULL);
+ pr_debug("[MIPI-HSI] ACREADY_NORMAL\n");
+ }
+
+ 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);
+
+ 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:
+ case HSI_INIT_MODE_CP_RAMDUMP:
+ mipi_ld->ld.com_state = COM_BOOT;
+
+ for (i = 0; i < HSI_NUM_OF_USE_CHANNELS; i++)
+ if_hsi_close_channel(&mipi_ld->hsi_channles[i]);
+ ret = if_hsi_open_channel(
+ &mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL]);
+ if (ret)
+ return ret;
+
+ 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");
+
+ 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);
+
+ hsi_ioctl(mipi_ld->hsi_channles[HSI_FLASHLESS_CHANNEL].dev,
+ HSI_IOCTL_SET_ACREADY_NORMAL, NULL);
+ pr_debug("[MIPI-HSI] ACREADY_NORMAL\n");
+
+ pr_info("[MIPI-HSI] hsi_init_handshake Done : FLASHLESS_BOOT\n");
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+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;
+ struct mipi_link_device *mipi_ld =
+ container_of(work, struct mipi_link_device, cmd_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");
+
+ if (!list_empty(&mipi_ld->list_of_hsi_cmd)) {
+ 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);
+ } else
+ return;
+
+ do {
+ hsi_cmd = list_entry(mipi_ld->list_of_hsi_cmd.next,
+ struct if_hsi_command, list);
+ 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);
+ if_hsi_set_wakeline(channel, 0);
+ return;
+ }
+ pr_debug("[MIPI-HSI] SEND CMD : %08x\n", hsi_cmd->command);
+
+ list_del(&hsi_cmd->list);
+ kfree(hsi_cmd);
+ } while (!list_empty(&mipi_ld->list_of_hsi_cmd));
+
+ if_hsi_set_wakeline(channel, 0);
+}
+
+static int if_hsi_send_command(struct mipi_link_device *mipi_ld,
+ u32 cmd_type, int ch, u32 param)
+{
+ 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);
+
+ list_add_tail(&hsi_cmd->list, &mipi_ld->list_of_hsi_cmd);
+
+ pr_debug("[MIPI-HSI] queue_work : cmd_work\n");
+ if (!work_pending(&mipi_ld->cmd_work))
+ queue_work(mipi_ld->mipi_wq, &mipi_ld->cmd_work);
+
+ 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_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\n", cmd);
+ }
+
+ 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 : %08x\n", cmd);
+ 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 : %08x\n", cmd);
+ 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 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);
+
+ if_hsi_set_wakeline(channel, 0);
+
+ ack_timeout_cnt++;
+ if (ack_timeout_cnt < 10) {
+ if_hsi_set_wakeline(channel, 1);
+ pr_err("[MIPI-HSI] ch=%d, retry send open. cnt : %d\n",
+ channel->channel_id, ack_timeout_cnt);
+ goto retry_send;
+ }
+
+ channel->send_step = STEP_IDLE;
+ return -ETIMEDOUT;
+ }
+ 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);
+ if_hsi_set_wakeline(channel, 0);
+ channel->send_step = STEP_IDLE;
+ return -ETIMEDOUT;
+ }
+ 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);
+
+ pr_err("[MIPI-HSI] data : %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));
+
+ 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);
+
+ pr_debug("[MIPI-HSI] len:%d, id:%d, data : %08x %08x %08x %08x %08x ...\n",
+ channel->tx_count, channel->channel_id, *channel->tx_data,
+ *(channel->tx_data + 1), *(channel->tx_data + 2),
+ *(channel->tx_data + 3), *(channel->tx_data + 4));
+
+ 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];
+
+ 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 int if_hsi_read(struct if_hsi_channel *channel, const char *data,
+ unsigned int size)
+{
+ int ret;
+ unsigned long int flags;
+
+ spin_lock_irqsave(&channel->rx_state_lock, flags);
+ if (channel->rx_state & HSI_CHANNEL_RX_STATE_READING) {
+ spin_unlock_irqrestore(&channel->rx_state_lock, flags);
+ return -EBUSY;
+ }
+ channel->rx_state |= HSI_CHANNEL_RX_STATE_READING;
+ spin_unlock_irqrestore(&channel->rx_state_lock, flags);
+
+ if (size % 4)
+ size += (4 - (size % 4));
+ channel->rx_count = size;
+
+ ret = hsi_read(channel->dev, (u32 *)data, channel->rx_count / 4);
+ if (ret) {
+ pr_err("[MIPI-HSI] ch=%d, hsi_read fail : %d\n",
+ channel->channel_id, ret);
+
+ spin_lock_irqsave(&channel->rx_state_lock, flags);
+ channel->rx_state &= ~HSI_CHANNEL_RX_STATE_READING;
+ spin_unlock_irqrestore(&channel->rx_state_lock, flags);
+
+ return ret;
+ }
+
+ pr_debug("[MIPI-HSI] submit read data : 0x%x(%d)\n",
+ *(u32 *)channel->rx_data, channel->rx_count);
+
+ if (down_timeout(&channel->read_done_sem,
+ HSI_READ_DONE_TIMEOUT) < 0) {
+ pr_err("[MIPI-HSI] ch=%d, hsi_read_done timeout : %d\n",
+ channel->channel_id, channel->rx_count);
+
+ hsi_read_cancel(channel->dev);
+
+ spin_lock_irqsave(&channel->rx_state_lock, flags);
+ channel->rx_state &= ~HSI_CHANNEL_RX_STATE_READING;
+ spin_unlock_irqrestore(&channel->rx_state_lock, flags);
+
+ return -ETIMEDOUT;
+ }
+
+ if (channel->rx_count != size)
+ pr_err("[MIPI-HSI] ch:%d,read_done fail,read_size:%d,origin_size:%d\n",
+ channel->channel_id, channel->rx_count, size);
+
+ return channel->rx_count;
+}
+
+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_err("[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);
+
+ up(&channel->read_done_sem);
+ return;
+
+ case COM_CRASH:
+ case COM_NONE:
+ default:
+ pr_err("[MIPI-HSI] receive data in wrong state : 0x%x(%d)\n",
+ *channel->rx_data, channel->rx_count);
+ up(&channel->read_done_sem);
+ 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);
+ return;
+
+ default:
+ up(&channel->read_done_sem);
+ 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);
+
+ 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);
+
+ channel->recv_step = STEP_IDLE;
+
+ 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].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);
+ sema_init(&mipi_ld->hsi_channles[dev->n_ch].write_done_sem, 0);
+ sema_init(&mipi_ld->hsi_channles[dev->n_ch].read_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_info("[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 = create_singlethread_workqueue("mipi_cmd_wq");
+ if (!mipi_ld->mipi_wq) {
+ pr_err("[MIPI-HSI] fail to create work Q.\n");
+ return -ENOMEM;
+ }
+ INIT_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);
+ pr_info("[MIPI-HSI] setup_timer done\n");
+
+ /* 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;
+ }
+
+ pr_info("[MIPI-HSI] if_hsi_init() done : %d\n", ret);
+ 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);
+ 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->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);
+
+ ret = if_hsi_init(ld);
+ if (ret)
+ return NULL;
+
+ pr_info("[MODEM_IF] %s : create_io_device DONE\n", mipi_ld->ld.name);
+ 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..484c949
--- /dev/null
+++ b/drivers/misc/modem_if/modem_link_device_mipi.h
@@ -0,0 +1,158 @@
+/*
+ * 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)
+#define HSI_CLOSE_CONN_DONE_TIMEOUT (HZ)
+#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
+
+
+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,
+};
+
+
+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;
+
+ struct semaphore write_done_sem;
+ struct semaphore read_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;
+
+ struct workqueue_struct *mipi_wq;
+ struct work_struct 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;
+};
+/* 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,
+};
+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_read(struct if_hsi_channel *channel, const char *data,
+ unsigned int size);
+static int if_hsi_protocol_send(struct mipi_link_device *mipi_ld, int ch,
+ u32 *data, unsigned int len);
+
+#endif
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..d910988
--- /dev/null
+++ b/drivers/misc/modem_if/modem_modemctl_device_xmm6260.c
@@ -0,0 +1,195 @@
+/* /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);
+ 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 cp_dump_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);
+ cp_dump_value = gpio_get_value(mc->gpio_cp_dump_int);
+
+ pr_info("[MODEM_IF] PA EVENT : reset =%d, pa=%d, cp_dump=%d\n",
+ phone_reset, phone_active_value, cp_dump_value);
+
+ if (phone_reset && phone_active_value)
+ phone_state = STATE_ONLINE;
+ else if (phone_reset && !phone_active_value) {
+ if (cp_dump_value)
+ phone_state = STATE_CRASH_EXIT;
+ else
+ phone_state = STATE_CRASH_RESET;
+ } 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;
+
+ pr_info("[MODEM_IF] xmm6260_get_ops() done\n");
+}
+
+int xmm6260_init_modemctl_device(struct modem_ctl *mc)
+{
+ int ret;
+ 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] failed to irq_phone_active request_irq:%d\n"
+ , ret);
+
+ ret = enable_irq_wake(mc->irq_phone_active);
+ if (ret)
+ pr_err("[MODEM_IF] failed to irq_phone_active enable_irq_wake : %d\n",
+ ret);
+
+ pr_info("[MODEM_IF] init_modemctl_device() done\n");
+ return 0;
+}
+
diff --git a/drivers/misc/modem_if/modem_prj.h b/drivers/misc/modem_if/modem_prj.h
new file mode 100755
index 0000000..913b59c
--- /dev/null
+++ b/drivers/misc/modem_if/modem_prj.h
@@ -0,0 +1,195 @@
+/*
+ * 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)
+
+/* modem status */
+#define MODEM_OFF 0
+#define MODEM_CRASHED 1
+#define MODEM_RAMDUMP 2
+#define MODEM_POWER_ON 3
+#define MODEM_BOOTING_NORMAL 4
+#define MODEM_BOOTING_RAMDUMP 5
+#define MODEM_DUMPING 6
+#define MODEM_RUNNING 7
+
+#define IPC_HEADER_MAX_SIZE 6 /* fmt 3, raw 6, rfs 6 */
+
+/* Does modem ctl structure will use state ? or status defined below ?*/
+enum modem_state {
+ STATE_OFFLINE,
+ STATE_CRASH_RESET, /* silent reset */
+ STATE_CRASH_EXIT, /* cp ramdump */
+ STATE_BOOTING,
+ STATE_ONLINE,
+};
+
+enum {
+ COM_NONE,
+ COM_ONLINE,
+ COM_HANDSHAKE,
+ COM_BOOT,
+ COM_CRASH,
+};
+
+struct header_data {
+ char hdr[IPC_HEADER_MAX_SIZE];
+ unsigned len;
+ unsigned flag_len;
+ char start; /*hdlc start header 0x7F*/
+};
+
+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;
+
+ struct sk_buff_head sk_rx_q;
+
+ /* 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 work_struct tx_work;
+ struct delayed_work tx_delayed_work;
+
+ int irq; /* for dpram int */
+ 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);
+
+ /* 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);
+};
+
+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 *);
+};
+
+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;
+
+ int irq_phone_active;
+
+ struct work_struct work;
+
+#ifdef CONFIG_LTE_MODEM_CMC221
+ const struct attribute_group *group;
+ unsigned gpio_cp_off;
+ 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;
+ 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..937add3
--- /dev/null
+++ b/drivers/misc/modem_if/modem_variation.h
@@ -0,0 +1,118 @@
+/*
+ * 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)
+#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 *);
+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);
+ 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/omap_hsi/Kconfig b/drivers/omap_hsi/Kconfig
new file mode 100644
index 0000000..1f2862f
--- /dev/null
+++ b/drivers/omap_hsi/Kconfig
@@ -0,0 +1,70 @@
+#
+# OMAP HSI driver configuration
+#
+
+config OMAP_HSI
+ bool "OMAP HSI hardware driver"
+ depends on (ARCH_OMAP34XX || ARCH_OMAP4)
+ default n
+ ---help---
+ If you say Y here, you will enable the OMAP HSI hardware driver.
+
+ Note: This module is a unified driver specific to OMAP. Efforts are
+ underway to create a vendor independent implementation.
+
+ The MIPI HSI is a High Speed Synchronous Serial Interface and is
+ defined for communication between two Integrated Circuits (the
+ typical scenario is an application IC and cellular modem IC
+ communication). Data transaction model is peer-to-peer.
+
+ Not all features required for a production device are implemented in
+ this driver. See the documentation for more information.
+
+ This physical layer provides logical channeling and several modes of
+ operation.
+
+ The OMAP HSI driver supports either:
+ - the OMAP MIPI HSI device
+ - the OMAP SSI device
+
+choice
+ prompt "Selected device support file"
+ depends on OMAP_HSI && y
+ default OMAP_HSI_DEVICE
+ ---help---
+ Adds the device support for one of the devices handled by the HSI
+ driver.
+
+ The OMAP HSI driver supports either:
+ - the OMAP MIPI HSI device
+ - the OMAP SSI device
+
+config OMAP_HSI_DEVICE
+ bool "HSI (OMAP MIPI HSI)"
+ depends on ARCH_OMAP4
+
+config OMAP_SSI_DEVICE
+ bool "SSI (OMAP SSI)"
+ depends on ARCH_OMAP34XX
+
+endchoice
+
+#
+# OMAP HSI char device kernel configuration
+#
+
+config OMAP_HSI_CHAR
+ tristate "OMAP HSI character driver"
+ depends on OMAP_HSI
+ ---help---
+ If you say Y here, you will enable the OMAP HSI character driver.
+
+ This driver provides a simple character device interface for
+ serial communication over the HSI bus.
+
+config OMAP_HSI_PROTOCOL
+ tristate "HSI Protocol driver for Infineon Modem"
+ depends on OMAP_HSI
+ ---help---
+ If you say Y here, you will enable the HSI Protocol driver.
+ This driver supports HSI protocol for Infineon Modem.
diff --git a/drivers/omap_hsi/Makefile b/drivers/omap_hsi/Makefile
new file mode 100644
index 0000000..0f072fb
--- /dev/null
+++ b/drivers/omap_hsi/Makefile
@@ -0,0 +1,21 @@
+#
+# Makefile for HSI drivers
+#
+EXTRA_CFLAGS :=
+
+omap_hsi-objs := hsi_driver.o hsi_driver_dma.o hsi_driver_int.o \
+ hsi_driver_if.o hsi_driver_bus.o hsi_driver_gpio.o \
+ hsi_driver_fifo.o
+
+ifeq ($(CONFIG_DEBUG_FS), y)
+ omap_hsi-objs += hsi_driver_debugfs.o
+endif
+
+hsi_char-objs := hsi-char.o hsi-if.o
+
+hsi-protocol-objs := hsi_protocol.o hsi_protocol_if.o \
+ hsi_protocol_cmd.o
+
+obj-$(CONFIG_OMAP_HSI) += omap_hsi.o
+obj-$(CONFIG_OMAP_HSI_CHAR) += hsi_char.o
+obj-$(CONFIG_OMAP_HSI_PROTOCOL) += hsi-protocol.o
diff --git a/drivers/omap_hsi/hsi-char.c b/drivers/omap_hsi/hsi-char.c
new file mode 100644
index 0000000..871de30
--- /dev/null
+++ b/drivers/omap_hsi/hsi-char.c
@@ -0,0 +1,556 @@
+/*
+ * hsi-char.c
+ *
+ * HSI character device driver, implements the character device
+ * interface.
+ *
+ * Copyright (C) 2009 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Andras Domokos <andras.domokos@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/miscdevice.h>
+#include <linux/file.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <asm/mach-types.h>
+#include <linux/ioctl.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/hsi_driver_if.h>
+#include <linux/hsi_char.h>
+
+#include <plat/omap_hsi.h>
+
+#include "hsi-char.h"
+
+#define DRIVER_VERSION "0.2.1"
+#define HSI_CHAR_DEVICE_NAME "hsi_char"
+
+static unsigned int port = 1;
+module_param(port, uint, 1);
+MODULE_PARM_DESC(port, "HSI port to be probed");
+
+static unsigned int num_channels;
+static unsigned int channels_map[HSI_MAX_CHAR_DEVS] = { 0 };
+module_param_array(channels_map, uint, &num_channels, 0);
+MODULE_PARM_DESC(channels_map, "HSI channels to be probed");
+
+dev_t hsi_char_dev;
+
+struct char_queue {
+ struct list_head list;
+ u32 *data;
+ unsigned int count;
+};
+
+struct hsi_char {
+ unsigned int opened;
+ int poll_event;
+ struct list_head rx_queue;
+ struct list_head tx_queue;
+ spinlock_t lock; /* Serialize access to driver data and API */
+ struct fasync_struct *async_queue;
+ wait_queue_head_t rx_wait;
+ wait_queue_head_t tx_wait;
+ wait_queue_head_t poll_wait;
+};
+
+static struct hsi_char hsi_char_data[HSI_MAX_CHAR_DEVS];
+
+void if_hsi_notify(int ch, struct hsi_event *ev)
+{
+ struct char_queue *entry;
+
+ pr_debug("%s, ev = {0x%x, 0x%p, %u}\n", __func__, ev->event, ev->data,
+ ev->count);
+
+ spin_lock(&hsi_char_data[ch].lock);
+
+ if (!hsi_char_data[ch].opened) {
+ pr_debug("%s, device not opened\n!", __func__);
+ spin_unlock(&hsi_char_data[ch].lock);
+ return;
+ }
+
+ switch (HSI_EV_TYPE(ev->event)) {
+ case HSI_EV_IN:
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry) {
+ pr_err("HSI-CHAR: entry allocation failed.\n");
+ spin_unlock(&hsi_char_data[ch].lock);
+ return;
+ }
+ entry->data = ev->data;
+ entry->count = ev->count;
+ list_add_tail(&entry->list, &hsi_char_data[ch].rx_queue);
+ spin_unlock(&hsi_char_data[ch].lock);
+ pr_debug("%s, HSI_EV_IN\n", __func__);
+ wake_up_interruptible(&hsi_char_data[ch].rx_wait);
+ break;
+ case HSI_EV_OUT:
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry) {
+ pr_err("HSI-CHAR: entry allocation failed.\n");
+ spin_unlock(&hsi_char_data[ch].lock);
+ return;
+ }
+ entry->data = ev->data;
+ entry->count = ev->count;
+ hsi_char_data[ch].poll_event |= (POLLOUT | POLLWRNORM);
+ list_add_tail(&entry->list, &hsi_char_data[ch].tx_queue);
+ spin_unlock(&hsi_char_data[ch].lock);
+ pr_debug("%s, HSI_EV_OUT\n", __func__);
+ wake_up_interruptible(&hsi_char_data[ch].tx_wait);
+ break;
+ case HSI_EV_EXCEP:
+ hsi_char_data[ch].poll_event |= POLLPRI;
+ spin_unlock(&hsi_char_data[ch].lock);
+ pr_debug("%s, HSI_EV_EXCEP\n", __func__);
+ wake_up_interruptible(&hsi_char_data[ch].poll_wait);
+ break;
+ case HSI_EV_AVAIL:
+ hsi_char_data[ch].poll_event |= (POLLIN | POLLRDNORM);
+ spin_unlock(&hsi_char_data[ch].lock);
+ pr_debug("%s, HSI_EV_AVAIL\n", __func__);
+ wake_up_interruptible(&hsi_char_data[ch].poll_wait);
+ break;
+ default:
+ spin_unlock(&hsi_char_data[ch].lock);
+ break;
+ }
+}
+
+static int hsi_char_fasync(int fd, struct file *file, int on)
+{
+ int ch = (int)file->private_data;
+ if (fasync_helper(fd, file, on, &hsi_char_data[ch].async_queue) >= 0)
+ return 0;
+ else
+ return -EIO;
+}
+
+static unsigned int hsi_char_poll(struct file *file, poll_table * wait)
+{
+ int ch = (int)file->private_data;
+ unsigned int ret = 0;
+
+ /*printk(KERN_DEBUG "%s\n", __func__); */
+
+ poll_wait(file, &hsi_char_data[ch].poll_wait, wait);
+ poll_wait(file, &hsi_char_data[ch].tx_wait, wait);
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ ret = hsi_char_data[ch].poll_event;
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+
+ pr_debug("%s, ret = 0x%x\n", __func__, ret);
+ return ret;
+}
+
+static ssize_t hsi_char_read(struct file *file, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ch = (int)file->private_data;
+ DECLARE_WAITQUEUE(wait, current);
+ u32 *data;
+ unsigned int data_len;
+ struct char_queue *entry;
+ ssize_t ret;
+
+ /*printk(KERN_DEBUG "%s, count = %d\n", __func__, count); */
+
+ /* only 32bit data is supported for now */
+ if ((count < 4) || (count & 3))
+ return -EINVAL;
+
+ data = kmalloc(count, GFP_ATOMIC);
+
+ ret = if_hsi_read(ch, data, count);
+ if (ret < 0) {
+ kfree(data);
+ goto out2;
+ }
+
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ add_wait_queue(&hsi_char_data[ch].rx_wait, &wait);
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+
+ for (;;) {
+ data = NULL;
+ data_len = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ if (!list_empty(&hsi_char_data[ch].rx_queue)) {
+ entry = list_entry(hsi_char_data[ch].rx_queue.next,
+ struct char_queue, list);
+ data = entry->data;
+ data_len = entry->count;
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+
+ pr_debug("%s, data = 0x%p, data_len = %d\n",
+ __func__, data, data_len);
+
+ if (data_len) {
+ pr_debug("%s, RX finished\n", __func__);
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ hsi_char_data[ch].poll_event &= ~(POLLIN | POLLRDNORM);
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+ if_hsi_poll(ch);
+ break;
+ } else if (file->f_flags & O_NONBLOCK) {
+ pr_debug("%s, O_NONBLOCK\n", __func__);
+ ret = -EAGAIN;
+ goto out;
+ } else if (signal_pending(current)) {
+ pr_debug("%s, ERESTARTSYS\n", __func__);
+ ret = -EAGAIN;
+ if_hsi_cancel_read(ch);
+ /* goto out; */
+ break;
+ }
+
+ /*printk(KERN_DEBUG "%s, going to sleep...\n", __func__); */
+ schedule();
+ /*printk(KERN_DEBUG "%s, woke up\n", __func__); */
+ }
+
+ if (data_len) {
+ ret = copy_to_user((void __user *)buf, data, data_len);
+ if (!ret)
+ ret = data_len;
+ }
+
+ kfree(data);
+
+out:
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hsi_char_data[ch].rx_wait, &wait);
+
+out2:
+ /*printk(KERN_DEBUG "%s, ret = %d\n", __func__, ret); */
+ return ret;
+}
+
+static ssize_t hsi_char_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ch = (int)file->private_data;
+ DECLARE_WAITQUEUE(wait, current);
+ u32 *data;
+ unsigned int data_len = 0;
+ struct char_queue *entry;
+ ssize_t ret;
+
+ /*printk(KERN_DEBUG "%s, count = %d\n", __func__, count); */
+
+ /* only 32bit data is supported for now */
+ if ((count < 4) || (count & 3))
+ return -EINVAL;
+
+ data = kmalloc(count, GFP_ATOMIC);
+ if (!data) {
+ WARN_ON(1);
+ return -ENOMEM;
+ }
+ if (copy_from_user(data, (void __user *)buf, count)) {
+ ret = -EFAULT;
+ kfree(data);
+ goto out2;
+ } else {
+ ret = count;
+ }
+
+ ret = if_hsi_write(ch, data, count);
+ if (ret < 0) {
+ kfree(data);
+ goto out2;
+ }
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ hsi_char_data[ch].poll_event &= ~(POLLOUT | POLLWRNORM);
+ add_wait_queue(&hsi_char_data[ch].tx_wait, &wait);
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+
+ for (;;) {
+ data = NULL;
+ data_len = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ if (!list_empty(&hsi_char_data[ch].tx_queue)) {
+ entry = list_entry(hsi_char_data[ch].tx_queue.next,
+ struct char_queue, list);
+ data = entry->data;
+ data_len = entry->count;
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+
+ if (data_len) {
+ pr_debug("%s, TX finished\n", __func__);
+ ret = data_len;
+ break;
+ } else if (file->f_flags & O_NONBLOCK) {
+ pr_debug("%s, O_NONBLOCK\n", __func__);
+ ret = -EAGAIN;
+ goto out;
+ } else if (signal_pending(current)) {
+ pr_debug("%s, ERESTARTSYS\n", __func__);
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+
+ /*printk(KERN_DEBUG "%s, going to sleep...\n", __func__); */
+ schedule();
+ /*printk(KERN_DEBUG "%s, woke up\n", __func__); */
+ }
+
+ kfree(data);
+
+out:
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hsi_char_data[ch].tx_wait, &wait);
+
+out2:
+ /*printk(KERN_DEBUG "%s, ret = %d\n", __func__, ret); */
+ return ret;
+}
+
+static long hsi_char_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int ch = (int)file->private_data;
+ unsigned int state;
+ size_t occ;
+ struct hsi_rx_config rx_cfg;
+ struct hsi_tx_config tx_cfg;
+ int ret = 0;
+
+ pr_debug("%s, ch = %d, cmd = 0x%08x\n", __func__, ch, cmd);
+
+ switch (cmd) {
+ case CS_SEND_BREAK:
+ if_hsi_send_break(ch);
+ break;
+ case CS_FLUSH_RX:
+ if_hsi_flush_rx(ch);
+ break;
+ case CS_FLUSH_TX:
+ if_hsi_flush_tx(ch);
+ break;
+ case CS_SET_ACWAKELINE:
+ if (copy_from_user(&state, (void __user *)arg, sizeof(state)))
+ ret = -EFAULT;
+ else
+ if_hsi_set_acwakeline(ch, state);
+ break;
+ case CS_GET_ACWAKELINE:
+ if_hsi_get_acwakeline(ch, &state);
+ if (copy_to_user((void __user *)arg, &state, sizeof(state)))
+ ret = -EFAULT;
+ break;
+ case CS_GET_CAWAKELINE:
+ if_hsi_get_cawakeline(ch, &state);
+ if (copy_to_user((void __user *)arg, &state, sizeof(state)))
+ ret = -EFAULT;
+ break;
+ case CS_SET_RX:
+ if (copy_from_user(&rx_cfg, (void __user *)arg, sizeof(rx_cfg)))
+ ret = -EFAULT;
+ else
+ ret = if_hsi_set_rx(ch, &rx_cfg);
+ break;
+ case CS_GET_RX:
+ if_hsi_get_rx(ch, &rx_cfg);
+ if (copy_to_user((void __user *)arg, &rx_cfg, sizeof(rx_cfg)))
+ ret = -EFAULT;
+ break;
+ case CS_SET_TX:
+ if (copy_from_user(&tx_cfg, (void __user *)arg, sizeof(tx_cfg)))
+ ret = -EFAULT;
+ else
+ ret = if_hsi_set_tx(ch, &tx_cfg);
+ break;
+ case CS_GET_TX:
+ if_hsi_get_tx(ch, &tx_cfg);
+ if (copy_to_user((void __user *)arg, &tx_cfg, sizeof(tx_cfg)))
+ ret = -EFAULT;
+ break;
+ case CS_SW_RESET:
+ if_hsi_sw_reset(ch);
+ break;
+ case CS_GET_FIFO_OCCUPANCY:
+ if_hsi_get_fifo_occupancy(ch, &occ);
+ if (copy_to_user((void __user *)arg, &occ, sizeof(occ)))
+ ret = -EFAULT;
+ break;
+ default:
+ ret = -ENOIOCTLCMD;
+ break;
+ }
+
+ return ret;
+}
+
+static int hsi_char_open(struct inode *inode, struct file *file)
+{
+ int ret = 0, ch = iminor(inode);
+ int i;
+
+ for (i = 0; i < HSI_MAX_CHAR_DEVS; i++)
+ if ((channels_map[i] - 1) == ch)
+ break;
+
+ if (i == HSI_MAX_CHAR_DEVS) {
+ pr_err("HSI char open: Channel %d not found\n", ch);
+ return -ENODEV;
+ }
+
+ pr_debug("HSI char open: opening channel %d\n", ch);
+
+ spin_lock_bh(&hsi_char_data[ch].lock);
+
+ if (hsi_char_data[ch].opened) {
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+ pr_err("HSI char open: Channel %d already opened\n", ch);
+ return -EBUSY;
+ }
+
+ file->private_data = (void *)ch;
+ hsi_char_data[ch].opened++;
+ hsi_char_data[ch].poll_event = (POLLOUT | POLLWRNORM);
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+
+ ret = if_hsi_start(ch);
+
+ return ret;
+}
+
+static int hsi_char_release(struct inode *inode, struct file *file)
+{
+ int ch = (int)file->private_data;
+ struct char_queue *entry;
+ struct list_head *cursor, *next;
+
+ pr_debug("%s, ch = %d\n", __func__, ch);
+
+ if_hsi_stop(ch);
+ spin_lock_bh(&hsi_char_data[ch].lock);
+ hsi_char_data[ch].opened--;
+
+ if (!list_empty(&hsi_char_data[ch].rx_queue)) {
+ list_for_each_safe(cursor, next, &hsi_char_data[ch].rx_queue) {
+ entry = list_entry(cursor, struct char_queue, list);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ }
+
+ if (!list_empty(&hsi_char_data[ch].tx_queue)) {
+ list_for_each_safe(cursor, next, &hsi_char_data[ch].tx_queue) {
+ entry = list_entry(cursor, struct char_queue, list);
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ }
+
+ spin_unlock_bh(&hsi_char_data[ch].lock);
+
+ return 0;
+}
+
+static const struct file_operations hsi_char_fops = {
+ .owner = THIS_MODULE,
+ .read = hsi_char_read,
+ .write = hsi_char_write,
+ .poll = hsi_char_poll,
+ .unlocked_ioctl = hsi_char_ioctl,
+ .open = hsi_char_open,
+ .release = hsi_char_release,
+ .fasync = hsi_char_fasync,
+};
+
+static struct cdev hsi_char_cdev;
+
+static int __init hsi_char_init(void)
+{
+ int ret, i;
+
+ pr_info("HSI character device version " DRIVER_VERSION "\n");
+ pr_info("HSI char driver: %d channels mapped\n", num_channels);
+
+ for (i = 0; i < HSI_MAX_CHAR_DEVS; i++) {
+ init_waitqueue_head(&hsi_char_data[i].rx_wait);
+ init_waitqueue_head(&hsi_char_data[i].tx_wait);
+ init_waitqueue_head(&hsi_char_data[i].poll_wait);
+ spin_lock_init(&hsi_char_data[i].lock);
+ hsi_char_data[i].opened = 0;
+ INIT_LIST_HEAD(&hsi_char_data[i].rx_queue);
+ INIT_LIST_HEAD(&hsi_char_data[i].tx_queue);
+ }
+
+ /*printk(KERN_DEBUG "%s, devname = %s\n", __func__, devname); */
+
+ ret = if_hsi_init(port, channels_map, num_channels);
+ if (ret)
+ return ret;
+
+ ret =
+ alloc_chrdev_region(&hsi_char_dev, 0, HSI_MAX_CHAR_DEVS,
+ HSI_CHAR_DEVICE_NAME);
+ if (ret < 0) {
+ pr_err("HSI character driver: Failed to register\n");
+ return ret;
+ }
+
+ cdev_init(&hsi_char_cdev, &hsi_char_fops);
+ ret = cdev_add(&hsi_char_cdev, hsi_char_dev, HSI_MAX_CHAR_DEVS);
+ if (ret < 0) {
+ pr_err("HSI character device: Failed to add char device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static void __exit hsi_char_exit(void)
+{
+ cdev_del(&hsi_char_cdev);
+ unregister_chrdev_region(hsi_char_dev, HSI_MAX_CHAR_DEVS);
+ if_hsi_exit();
+}
+
+MODULE_AUTHOR("Andras Domokos <andras.domokos@nokia.com>");
+MODULE_AUTHOR("Sebatien Jan <s-jan@ti.com> / Texas Instruments");
+MODULE_DESCRIPTION("HSI character device");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(hsi_char_init);
+module_exit(hsi_char_exit);
diff --git a/drivers/omap_hsi/hsi-char.h b/drivers/omap_hsi/hsi-char.h
new file mode 100644
index 0000000..c4b1c4c
--- /dev/null
+++ b/drivers/omap_hsi/hsi-char.h
@@ -0,0 +1,35 @@
+/*
+ * hsi-char.h
+ *
+ * HSI character driver private declaration header file.
+ *
+ * Copyright (C) 2009 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Andras Domokos <andras.domokos@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef _HSI_CHAR_H
+#define _HSI_CHAR_H
+
+#include "hsi-if.h"
+
+/* how many char devices would be created at most */
+#define HSI_MAX_CHAR_DEVS 16
+
+/* Max HSI channel id allowed to be handled as char device. */
+/* Current range [1, 16] */
+#define HSI_MAX_CHAR_DEV_ID 16
+
+void if_hsi_notify(int ch, struct hsi_event *ev);
+
+#endif /* _HSI_CHAR_H */
diff --git a/drivers/omap_hsi/hsi-if.c b/drivers/omap_hsi/hsi-if.c
new file mode 100644
index 0000000..5228b6a
--- /dev/null
+++ b/drivers/omap_hsi/hsi-if.c
@@ -0,0 +1,672 @@
+ /*
+ * hsi-if.c
+ *
+ * Part of the HSI character driver, implements the HSI interface.
+ *
+ * Copyright (C) 2009 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Andras Domokos <andras.domokos@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <asm/mach-types.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/ktime.h>
+#include <linux/bitmap.h>
+
+#include <linux/hsi_driver_if.h>
+#include <linux/hsi_char.h>
+
+#include "hsi-char.h"
+#include "hsi-if.h"
+
+#define HSI_CHANNEL_STATE_UNAVAIL (1 << 0)
+#define HSI_CHANNEL_STATE_READING (1 << 1)
+#define HSI_CHANNEL_STATE_WRITING (1 << 2)
+
+#define PORT1 0
+#define PORT2 1
+
+#define RXCONV(dst, src) \
+ do { \
+ (dst)->mode = (src)->mode; \
+ (dst)->flow = (src)->flow; \
+ (dst)->frame_size = (src)->frame_size; \
+ (dst)->channels = (src)->channels; \
+ (dst)->divisor = (src)->divisor; \
+ (dst)->counters = (src)->counters; \
+ } while (0)
+
+#define TXCONV(dst, src) \
+ do { \
+ (dst)->mode = (src)->mode; \
+ (dst)->flow = (src)->flow; \
+ (dst)->frame_size = (src)->frame_size; \
+ (dst)->channels = (src)->channels; \
+ (dst)->divisor = (src)->divisor; \
+ (dst)->arb_mode = (src)->arb_mode; \
+ } while (0)
+
+struct if_hsi_channel {
+ struct hsi_device *dev;
+ unsigned int channel_id;
+ u32 *tx_data;
+ unsigned int tx_count; /* Number of bytes to be written */
+ u32 *rx_data;
+ unsigned int rx_count; /* Number of bytes to be read */
+ unsigned int opened;
+ unsigned int state;
+ spinlock_t lock; /* Serializes access to channel data */
+};
+
+struct if_hsi_iface {
+ struct if_hsi_channel channels[HSI_MAX_CHAR_DEVS];
+ int bootstrap;
+ unsigned long init_chan_map;
+ spinlock_t lock; /* Serializes access to HSI functional interface */
+};
+
+static void if_hsi_port_event(struct hsi_device *dev, unsigned int event,
+ void *arg);
+static int __devinit if_hsi_probe(struct hsi_device *dev);
+static int __devexit if_hsi_remove(struct hsi_device *dev);
+
+static struct hsi_device_driver if_hsi_char_driver = {
+ .ctrl_mask = ANY_HSI_CONTROLLER,
+ .probe = if_hsi_probe,
+ .remove = __devexit_p(if_hsi_remove),
+ .driver = {
+ .name = "hsi_char"},
+};
+
+static struct if_hsi_iface hsi_iface;
+
+static int if_hsi_read_on(int ch, u32 *data, unsigned int count)
+{
+ struct if_hsi_channel *channel;
+ int ret;
+
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+
+ spin_lock(&channel->lock);
+ if (channel->state & HSI_CHANNEL_STATE_READING) {
+ pr_err("Read still pending on channel %d\n", ch);
+ spin_unlock(&channel->lock);
+ return -EBUSY;
+ }
+ channel->state |= HSI_CHANNEL_STATE_READING;
+ channel->rx_data = data;
+ channel->rx_count = count;
+ spin_unlock(&channel->lock);
+
+ ret = hsi_read(channel->dev, data, count / 4);
+ dev_dbg(&channel->dev->device, "%s, ch = %d, ret = %d\n", __func__, ch,
+ ret);
+
+ return ret;
+}
+
+/* HSI char driver read done callback */
+static void if_hsi_read_done(struct hsi_device *dev, unsigned int size)
+{
+ struct if_hsi_channel *channel;
+ struct hsi_event ev;
+
+ channel = &hsi_iface.channels[dev->n_ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, dev->n_ch);
+ spin_lock(&channel->lock);
+ channel->state &= ~HSI_CHANNEL_STATE_READING;
+ ev.event = HSI_EV_IN;
+ ev.data = channel->rx_data;
+ ev.count = 4 * size; /* Convert size to number of u8, not u32 */
+ spin_unlock(&channel->lock);
+ if_hsi_notify(dev->n_ch, &ev);
+}
+
+int if_hsi_read(int ch, u32 *data, unsigned int count)
+{
+ int ret = 0;
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ ret = if_hsi_read_on(ch, data, count);
+ return ret;
+}
+
+int if_hsi_poll(int ch)
+{
+ struct if_hsi_channel *channel;
+ int ret = 0;
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ ret = hsi_poll(channel->dev);
+ return ret;
+}
+
+static int if_hsi_write_on(int ch, u32 *address, unsigned int count)
+{
+ struct if_hsi_channel *channel;
+ int ret;
+
+ channel = &hsi_iface.channels[ch];
+
+ spin_lock(&channel->lock);
+ if (channel->state & HSI_CHANNEL_STATE_WRITING) {
+ pr_err("Write still pending on channel %d\n", ch);
+ spin_unlock(&channel->lock);
+ return -EBUSY;
+ }
+
+ channel->tx_data = address;
+ channel->tx_count = count;
+ channel->state |= HSI_CHANNEL_STATE_WRITING;
+ spin_unlock(&channel->lock);
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ ret = hsi_write(channel->dev, address, count / 4);
+ return ret;
+}
+
+/* HSI char driver write done callback */
+static void if_hsi_write_done(struct hsi_device *dev, unsigned int size)
+{
+ struct if_hsi_channel *channel;
+ struct hsi_event ev;
+
+ channel = &hsi_iface.channels[dev->n_ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, dev->n_ch);
+
+ spin_lock(&channel->lock);
+ channel->state &= ~HSI_CHANNEL_STATE_WRITING;
+ ev.event = HSI_EV_OUT;
+ ev.data = channel->tx_data;
+ ev.count = 4 * size; /* Convert size to number of u8, not u32 */
+ spin_unlock(&channel->lock);
+ if_hsi_notify(dev->n_ch, &ev);
+}
+
+int if_hsi_write(int ch, u32 *data, unsigned int count)
+{
+ int ret = 0;
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ ret = if_hsi_write_on(ch, data, count);
+ return ret;
+}
+
+void if_hsi_send_break(int ch)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ hsi_ioctl(channel->dev, HSI_IOCTL_SEND_BREAK, NULL);
+}
+
+void if_hsi_flush_rx(int ch)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ hsi_ioctl(channel->dev, HSI_IOCTL_FLUSH_RX, NULL);
+}
+
+void if_hsi_flush_ch(int ch)
+{
+ /* FIXME - Check the purpose of this function */
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+}
+
+void if_hsi_flush_tx(int ch)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ hsi_ioctl(channel->dev, HSI_IOCTL_FLUSH_TX, NULL);
+}
+
+void if_hsi_get_acwakeline(int ch, unsigned int *state)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ hsi_ioctl(channel->dev, HSI_IOCTL_GET_ACWAKE, state);
+}
+
+void if_hsi_set_acwakeline(int ch, unsigned int state)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ hsi_ioctl(channel->dev,
+ state ? HSI_IOCTL_ACWAKE_UP : HSI_IOCTL_ACWAKE_DOWN, NULL);
+}
+
+void if_hsi_get_cawakeline(int ch, unsigned int *state)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ hsi_ioctl(channel->dev, HSI_IOCTL_GET_CAWAKE, state);
+}
+
+int if_hsi_set_rx(int ch, struct hsi_rx_config *cfg)
+{
+ int ret;
+ struct if_hsi_channel *channel;
+ struct hsr_ctx ctx;
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ RXCONV(&ctx, cfg);
+ ret = hsi_ioctl(channel->dev, HSI_IOCTL_SET_RX, &ctx);
+ return ret;
+}
+
+void if_hsi_get_rx(int ch, struct hsi_rx_config *cfg)
+{
+ struct if_hsi_channel *channel;
+ struct hsr_ctx ctx;
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ hsi_ioctl(channel->dev, HSI_IOCTL_GET_RX, &ctx);
+ RXCONV(cfg, &ctx);
+}
+
+int if_hsi_set_tx(int ch, struct hsi_tx_config *cfg)
+{
+ int ret;
+ struct if_hsi_channel *channel;
+ struct hst_ctx ctx;
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ TXCONV(&ctx, cfg);
+ ret = hsi_ioctl(channel->dev, HSI_IOCTL_SET_TX, &ctx);
+ return ret;
+}
+
+void if_hsi_get_tx(int ch, struct hsi_tx_config *cfg)
+{
+ struct if_hsi_channel *channel;
+ struct hst_ctx ctx;
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ hsi_ioctl(channel->dev, HSI_IOCTL_GET_TX, &ctx);
+ TXCONV(cfg, &ctx);
+}
+
+void if_hsi_sw_reset(int ch)
+{
+ struct if_hsi_channel *channel;
+ int i;
+ channel = &hsi_iface.channels[ch];
+ hsi_ioctl(channel->dev, HSI_IOCTL_SW_RESET, NULL);
+
+ spin_lock_bh(&hsi_iface.lock);
+ /* Reset HSI channel states */
+ for (i = 0; i < HSI_MAX_PORTS; i++)
+ if_hsi_char_driver.ch_mask[i] = 0;
+
+ for (i = 0; i < HSI_MAX_CHAR_DEVS; i++) {
+ channel = &hsi_iface.channels[i];
+ channel->opened = 0;
+ channel->state = HSI_CHANNEL_STATE_UNAVAIL;
+ }
+ spin_unlock_bh(&hsi_iface.lock);
+}
+
+void if_hsi_get_fifo_occupancy(int ch, size_t *occ)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ hsi_ioctl(channel->dev, HSI_IOCTL_GET_FIFO_OCCUPANCY, occ);
+}
+
+void if_hsi_cancel_read(int ch)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ if (channel->state & HSI_CHANNEL_STATE_READING)
+ hsi_read_cancel(channel->dev);
+ spin_lock(&channel->lock);
+ channel->state &= ~HSI_CHANNEL_STATE_READING;
+ spin_unlock(&channel->lock);
+}
+
+void if_hsi_cancel_write(int ch)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ if (channel->state & HSI_CHANNEL_STATE_WRITING)
+ hsi_write_cancel(channel->dev);
+ spin_lock(&channel->lock);
+ channel->state &= ~HSI_CHANNEL_STATE_WRITING;
+ spin_unlock(&channel->lock);
+}
+
+static int if_hsi_openchannel(struct if_hsi_channel *channel)
+{
+ int ret = 0;
+
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__,
+ channel->channel_id);
+ spin_lock(&channel->lock);
+
+ if (channel->state == HSI_CHANNEL_STATE_UNAVAIL) {
+ pr_err("Channel %d is not available\n", channel->channel_id);
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ if (channel->opened) {
+ pr_err("Channel %d is busy\n", channel->channel_id);
+ ret = -EBUSY;
+ goto leave;
+ }
+
+ if (!channel->dev) {
+ pr_err("Channel %d is not ready??\n", channel->channel_id);
+ ret = -ENODEV;
+ goto leave;
+ }
+ spin_unlock(&channel->lock);
+
+ ret = hsi_open(channel->dev);
+
+ spin_lock(&channel->lock);
+ if (ret < 0) {
+ pr_err("Could not open channel %d\n", channel->channel_id);
+ goto leave;
+ }
+
+ channel->opened = 1;
+
+leave:
+ spin_unlock(&channel->lock);
+ return ret;
+}
+
+static int if_hsi_closechannel(struct if_hsi_channel *channel)
+{
+ int ret = 0;
+
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__,
+ channel->channel_id);
+ spin_lock(&channel->lock);
+
+ if (!channel->opened)
+ goto leave;
+
+ if (!channel->dev) {
+ pr_err("Channel %d is not ready??\n", channel->channel_id);
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ /* Stop any pending read/write */
+ if (channel->state & HSI_CHANNEL_STATE_READING) {
+ channel->state &= ~HSI_CHANNEL_STATE_READING;
+ spin_unlock(&channel->lock);
+ hsi_read_cancel(channel->dev);
+ spin_lock(&channel->lock);
+ }
+
+ if (channel->state & HSI_CHANNEL_STATE_WRITING) {
+ channel->state &= ~HSI_CHANNEL_STATE_WRITING;
+ spin_unlock(&channel->lock);
+ hsi_write_cancel(channel->dev);
+ } else
+ spin_unlock(&channel->lock);
+
+ hsi_close(channel->dev);
+
+ spin_lock(&channel->lock);
+ channel->opened = 0;
+leave:
+ spin_unlock(&channel->lock);
+ return ret;
+}
+
+int if_hsi_start(int ch)
+{
+ struct if_hsi_channel *channel;
+ int ret = 0;
+
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+
+ spin_lock_bh(&channel->lock);
+ channel->state = 0;
+ spin_unlock_bh(&channel->lock);
+
+ ret = if_hsi_openchannel(channel);
+ if (ret < 0) {
+ pr_err("Could not open channel %d\n", ch);
+ goto error;
+ }
+
+ if_hsi_poll(ch);
+error:
+ return ret;
+}
+
+void if_hsi_stop(int ch)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+
+ if_hsi_closechannel(channel);
+}
+
+static int __devinit if_hsi_probe(struct hsi_device *dev)
+{
+ struct if_hsi_channel *channel;
+ unsigned long *address;
+ int ret = -ENXIO, port;
+
+ dev_dbg(&dev->device, "%s, port = %d, ch = %d\n", __func__, dev->n_p,
+ dev->n_ch);
+
+ for (port = 0; port < HSI_MAX_PORTS; port++) {
+ if (if_hsi_char_driver.ch_mask[port])
+ break;
+ }
+
+ if (port == HSI_MAX_PORTS)
+ return -ENXIO;
+
+ if (dev->n_ch >= HSI_MAX_CHAR_DEV_ID) {
+ pr_err("HSI char driver cannot handle channel %d\n", dev->n_ch);
+ return -ENXIO;
+ }
+
+ address = &if_hsi_char_driver.ch_mask[port];
+
+ spin_lock_bh(&hsi_iface.lock);
+ if (test_bit(dev->n_ch, address) && (dev->n_p == port)) {
+ hsi_set_read_cb(dev, if_hsi_read_done);
+ hsi_set_write_cb(dev, if_hsi_write_done);
+ hsi_set_port_event_cb(dev, if_hsi_port_event);
+ channel = &hsi_iface.channels[dev->n_ch];
+ channel->dev = dev;
+ channel->state = 0;
+ ret = 0;
+ hsi_iface.init_chan_map ^= (1 << dev->n_ch);
+ }
+ spin_unlock_bh(&hsi_iface.lock);
+
+ return ret;
+}
+
+static int __devexit if_hsi_remove(struct hsi_device *dev)
+{
+ struct if_hsi_channel *channel;
+ unsigned long *address;
+ int ret = -ENXIO, port;
+
+ dev_dbg(&dev->device, "%s, port = %d, ch = %d\n", __func__, dev->n_p,
+ dev->n_ch);
+
+ for (port = 0; port < HSI_MAX_PORTS; port++) {
+ if (if_hsi_char_driver.ch_mask[port])
+ break;
+ }
+
+ if (port == HSI_MAX_PORTS)
+ return -ENXIO;
+
+ address = &if_hsi_char_driver.ch_mask[port];
+
+ spin_lock_bh(&hsi_iface.lock);
+ if (test_bit(dev->n_ch, address) && (dev->n_p == port)) {
+ hsi_set_read_cb(dev, NULL);
+ hsi_set_write_cb(dev, NULL);
+ hsi_set_port_event_cb(dev, NULL);
+ channel = &hsi_iface.channels[dev->n_ch];
+ channel->dev = NULL;
+ channel->state = HSI_CHANNEL_STATE_UNAVAIL;
+ ret = 0;
+ }
+ spin_unlock_bh(&hsi_iface.lock);
+
+ return ret;
+}
+
+static void if_hsi_port_event(struct hsi_device *dev, unsigned int event,
+ void *arg)
+{
+ struct hsi_event ev;
+ int i;
+
+ ev.event = HSI_EV_EXCEP;
+ ev.data = (u32 *) 0;
+ ev.count = 0;
+
+ switch (event) {
+ case HSI_EVENT_BREAK_DETECTED:
+ pr_debug("%s, HWBREAK detected\n", __func__);
+ ev.data = (u32 *) HSI_HWBREAK;
+ for (i = 0; i < HSI_MAX_CHAR_DEVS; i++) {
+ if (hsi_iface.channels[i].opened)
+ if_hsi_notify(i, &ev);
+ }
+ break;
+ case HSI_EVENT_HSR_DATAAVAILABLE:
+ i = (int)arg;
+ pr_debug("%s, HSI_EVENT_HSR_DATAAVAILABLE channel = %d\n",
+ __func__, i);
+ ev.event = HSI_EV_AVAIL;
+ if (hsi_iface.channels[i].opened)
+ if_hsi_notify(i, &ev);
+ break;
+ case HSI_EVENT_CAWAKE_UP:
+ pr_debug("%s, CAWAKE up\n", __func__);
+ break;
+ case HSI_EVENT_CAWAKE_DOWN:
+ pr_debug("%s, CAWAKE down\n", __func__);
+ break;
+ case HSI_EVENT_ERROR:
+ pr_debug("%s, HSI ERROR occured\n", __func__);
+ break;
+ default:
+ pr_warning("%s, Unknown event(%d)\n", __func__, event);
+ break;
+ }
+}
+
+int __init if_hsi_init(unsigned int port, unsigned int *channels_map,
+ unsigned int num_channels)
+{
+ struct if_hsi_channel *channel;
+ int i, ret = 0;
+
+ pr_debug("%s, port = %d\n", __func__, port);
+
+ port -= 1;
+ if (port >= HSI_MAX_PORTS)
+ return -EINVAL;
+
+ hsi_iface.bootstrap = 1;
+ spin_lock_init(&hsi_iface.lock);
+
+ for (i = 0; i < HSI_MAX_PORTS; i++)
+ if_hsi_char_driver.ch_mask[i] = 0;
+
+ for (i = 0; i < HSI_MAX_CHAR_DEVS; i++) {
+ channel = &hsi_iface.channels[i];
+ channel->dev = NULL;
+ channel->opened = 0;
+ channel->state = HSI_CHANNEL_STATE_UNAVAIL;
+ channel->channel_id = i;
+ spin_lock_init(&channel->lock);
+ }
+
+ for (i = 0; (i < num_channels) && channels_map[i]; i++) {
+ pr_debug("%s, port = %d, channels_map[i] = %d\n", __func__,
+ port, channels_map[i]);
+ if ((channels_map[i] - 1) < HSI_MAX_CHAR_DEV_ID)
+ if_hsi_char_driver.ch_mask[port] |=
+ (1 << ((channels_map[i] - 1)));
+ else {
+ pr_err("Channel %d cannot be handled by the HSI "
+ "driver.\n", channels_map[i]);
+ return -EINVAL;
+ }
+
+ }
+ hsi_iface.init_chan_map = if_hsi_char_driver.ch_mask[port];
+
+ ret = hsi_register_driver(&if_hsi_char_driver);
+ if (ret)
+ pr_err("Error while registering HSI driver %d", ret);
+
+ if (hsi_iface.init_chan_map) {
+ ret = -ENXIO;
+ pr_err("HSI: Some channels could not be registered (out of "
+ "range or already registered?)\n");
+ }
+ return ret;
+}
+
+int __devexit if_hsi_exit(void)
+{
+ struct if_hsi_channel *channel;
+ unsigned long *address;
+ int i, port;
+
+ pr_debug("%s\n", __func__);
+
+ for (port = 0; port < HSI_MAX_PORTS; port++) {
+ if (if_hsi_char_driver.ch_mask[port])
+ break;
+ }
+
+ if (port == HSI_MAX_PORTS)
+ return -ENXIO;
+
+ address = &if_hsi_char_driver.ch_mask[port];
+
+ for (i = 0; i < HSI_MAX_CHAR_DEVS; i++) {
+ channel = &hsi_iface.channels[i];
+ if (channel->opened) {
+ if_hsi_set_acwakeline(i, HSI_IOCTL_ACWAKE_DOWN);
+ if_hsi_closechannel(channel);
+ }
+ }
+ hsi_unregister_driver(&if_hsi_char_driver);
+ return 0;
+}
diff --git a/drivers/omap_hsi/hsi-if.h b/drivers/omap_hsi/hsi-if.h
new file mode 100644
index 0000000..96afdd4
--- /dev/null
+++ b/drivers/omap_hsi/hsi-if.h
@@ -0,0 +1,69 @@
+/*
+ * hsi-if.h
+ *
+ * Part of the HSI character driver, private headers.
+ *
+ * Copyright (C) 2009 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Andras Domokos <andras.domokos@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef _HSI_IF_H
+#define _HSI_IF_H
+
+#define HSI_EV_MASK (0xffff << 0)
+#define HSI_EV_TYPE_MASK (0x0f << 16)
+#define HSI_EV_IN (0x01 << 16)
+#define HSI_EV_OUT (0x02 << 16)
+#define HSI_EV_EXCEP (0x03 << 16)
+#define HSI_EV_AVAIL (0x04 << 16)
+#define HSI_EV_TYPE(event) ((event) & HSI_EV_TYPE_MASK)
+
+#define HSI_HWBREAK 1
+#define HSI_ERROR 2
+
+struct hsi_event {
+ unsigned int event;
+ u32 *data;
+ unsigned int count;
+};
+
+int if_hsi_init(unsigned int port, unsigned int *channels_map,
+ unsigned int num_channels);
+int if_hsi_exit(void);
+
+int if_hsi_start(int ch);
+void if_hsi_stop(int ch);
+
+void if_hsi_send_break(int ch);
+void if_hsi_flush_rx(int ch);
+void if_hsi_flush_tx(int ch);
+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);
+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);
+void if_hsi_get_tx(int ch, struct hsi_tx_config *cfg);
+void if_hsi_sw_reset(int ch);
+void if_hsi_get_fifo_occupancy(int ch, size_t *occ);
+
+int if_hsi_read(int ch, u32 *data, unsigned int count);
+int if_hsi_poll(int ch);
+int if_hsi_write(int ch, u32 *data, unsigned int count);
+
+void if_hsi_cancel_read(int ch);
+void if_hsi_cancel_write(int ch);
+
+#endif /* _HSI_IF_H */
diff --git a/drivers/omap_hsi/hsi-protocol-if.h b/drivers/omap_hsi/hsi-protocol-if.h
new file mode 100644
index 0000000..f56ef36
--- /dev/null
+++ b/drivers/omap_hsi/hsi-protocol-if.h
@@ -0,0 +1,187 @@
+/*
+ * hsi-if.h
+ *
+ * Part of the HSI character driver, private headers.
+ *
+ * Copyright (C) 2009 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Andras Domokos <andras.domokos@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef _HSI_IF_H
+#define _HSI_IF_H
+
+#define HSI_EV_MASK (0xffff << 0)
+#define HSI_EV_TYPE_MASK (0x0f << 16)
+#define HSI_EV_IN (0x01 << 16)
+#define HSI_EV_OUT (0x02 << 16)
+#define HSI_EV_EXCEP (0x03 << 16)
+#define HSI_EV_AVAIL (0x04 << 16)
+#define HSI_EV_TYPE(event) ((event) & HSI_EV_TYPE_MASK)
+
+#define HSI_HWBREAK 1
+#define HSI_ERROR 2
+
+#define HSI_MAX_CHANNELS 16
+#define CHANNEL_MASK 0xFF
+#define HSI_LL_INVALID_CHANNEL 0xFF
+
+struct hsi_event {
+ unsigned int event;
+ u32 *data;
+ unsigned int count;
+};
+
+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 opened;
+ unsigned int state;
+ u32 *tx_buf;
+ u32 *rx_buf;
+ unsigned int tx_state;
+ unsigned int rx_state;
+ unsigned int tx_nak_count;
+ unsigned int rx_nak_count;
+ spinlock_t lock; /* Serializes access to channel data */
+};
+
+struct if_hsi_iface {
+ struct if_hsi_channel channels[HSI_MAX_CHANNELS];
+#if 0
+ int bootstrap;
+#endif
+ unsigned long init_chan_map;
+ spinlock_t lock; /* Serializes access to HSI functional interface */
+};
+
+struct if_hsi_cmd {
+ u32 tx_cmd[50];
+ u32 rx_cmd[50];
+ struct timespec tx_cmd_time[50];
+ struct timespec rx_cmd_time[50];
+};
+
+enum {
+ HSI_LL_MSG_BREAK = 0x00,
+ HSI_LL_MSG_ECHO = 0x01,
+ HSI_LL_MSG_INFO_REQ = 0x02,
+ HSI_LL_MSG_INFO = 0x03,
+ HSI_LL_MSG_CONFIGURE = 0x04,
+ HSI_LL_MSG_ALLOCATE_CH = 0x05,
+ HSI_LL_MSG_RELEASE_CH = 0x06,
+ HSI_LL_MSG_OPEN_CONN = 0x07,
+ HSI_LL_MSG_CONN_READY = 0x08,
+ HSI_LL_MSG_CONN_CLOSED = 0x09,
+ HSI_LL_MSG_CANCEL_CONN = 0x0A,
+ HSI_LL_MSG_ACK = 0x0B,
+ HSI_LL_MSG_NAK = 0x0C,
+ HSI_LL_MSG_CONF_RATE = 0x0D,
+ HSI_LL_MSG_OPEN_CONN_OCTET = 0x0E,
+ HSI_LL_MSG_INVALID = 0xFF,
+};
+
+enum {
+ HSI_LL_TX_STATE_UNDEF,
+ HSI_LL_TX_STATE_CLOSED,
+ HSI_LL_TX_STATE_IDLE,
+ HSI_LL_TX_STATE_POWER_DOWN,
+ HSI_LL_TX_STATE_ERROR,
+ HSI_LL_TX_STATE_SEND_OPEN_CONN,
+ HSI_LL_TX_STATE_WAIT_FOR_ACK,
+ HSI_LL_TX_STATE_NACK,
+ HSI_LL_TX_STATE_WAIT_FOR_CONN_READY,
+ HSI_LL_TX_STATE_SEND_CONF_RATE,
+ HSI_LL_TX_STATE_WAIT_FOR_CONF_ACK,
+ HSI_LL_TX_STATE_TX,
+ HSI_LL_TX_STATE_WAIT_FOR_CONN_CLOSED,
+ HSI_LL_TX_STATE_TO_OPEN_CONN,
+ HSI_LL_TX_STATE_TO_ACK,
+ HSI_LL_TX_STATE_TO_READY,
+ HSI_LL_TX_STATE_TO_CONF,
+ HSI_LL_TX_STATE_TO_CONF_ACK,
+ HSI_LL_TX_STATE_TO_TX,
+ HSI_LL_TX_STATE_TO_CLOSE,
+ HSI_LL_TX_STATE_SEND_BREAK,
+};
+
+enum {
+ HSI_LL_RX_STATE_UNDEF,
+ HSI_LL_RX_STATE_CLOSED,
+ HSI_LL_RX_STATE_IDLE,
+ HSI_LL_RX_STATE_POWER_DOWN,
+ HSI_LL_RX_STATE_ERROR,
+ HSI_LL_RX_STATE_BLOCKED,
+ HSI_LL_RX_STATE_SEND_ACK,
+ HSI_LL_RX_STATE_SEND_NACK,
+ HSI_LL_RX_STATE_SEND_CONN_READY,
+ HSI_LL_RX_STATE_RX,
+ HSI_LL_RX_STATE_SEND_CONN_CLOSED,
+ HSI_LL_RX_STATE_SEND_CONN_CANCEL,
+ HSI_LL_RX_STATE_WAIT_FOR_CANCEL_CONN_ACK,
+ HSI_LL_RX_STATE_SEND_CONF_ACK,
+ HSI_LL_RX_STATE_SEND_CONF_NACK,
+ HSI_LL_RX_STATE_TO_RX,
+ HSI_LL_RX_STATE_TO_ACK,
+ HSI_LL_RX_STATE_TO_NACK,
+ HSI_LL_RX_STATE_TO_CONN_READY,
+ HSI_LL_RX_STATE_TO_CONN_CLOSED,
+ HSI_LL_RX_STATE_TO_CONN_CANCEL,
+ HSI_LL_RX_STATE_TO_CONN_CANCEL_ACK,
+ HSI_LL_RX_STATE_TO_CONF_ACK,
+ HSI_LL_RX_STATE_SEND_BREAK,
+};
+
+
+int if_hsi_init(void);
+int if_hsi_exit(void);
+
+int if_hsi_start(int ch);
+void if_hsi_stop(int ch);
+
+void if_hsi_send_break(int ch);
+void if_hsi_flush_rx(int ch);
+void if_hsi_flush_tx(int ch);
+void if_hsi_bootstrap(int ch);
+void if_hsi_set_wakeline(int ch, unsigned int state);
+void if_hsi_get_wakeline(int ch, unsigned int *state);
+
+#if 0
+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);
+void if_hsi_get_tx(int ch, struct hsi_tx_config *cfg);
+#endif
+
+int if_hsi_read(int ch, u32 *data, unsigned int count);
+int if_hsi_poll(int ch);
+int if_hsi_write(int ch, u32 *data, unsigned int count);
+
+void if_hsi_cancel_read(int ch);
+void if_hsi_cancel_write(int ch);
+
+void if_notify(int ch, struct hsi_event *ev);
+int hsi_proto_read(int ch, u32 *buffer, int count);
+int hsi_proto_write(int ch, u32 *buffer, int length);
+int hsi_decode_cmd(u32 *data, u32 *cmd, u32 *ch, u32 *param);
+int protocol_create_cmd(int cmd_type, unsigned int channel, void *arg);
+int hsi_protocol_send_command(u32 cmd, u32 channel, u32 param);
+void rx_stm(u32 cmd, u32 ch, u32 param);
+#if 0
+int hsi_start_protocol(void);
+#endif
+#endif /* _HSI_IF_H */
diff --git a/drivers/omap_hsi/hsi_driver.c b/drivers/omap_hsi/hsi_driver.c
new file mode 100644
index 0000000..69c2b3d
--- /dev/null
+++ b/drivers/omap_hsi/hsi_driver.c
@@ -0,0 +1,1138 @@
+/*
+ * hsi_driver.c
+ *
+ * Implements HSI module interface, initialization, and PM related functions.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/list.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+
+#include <mach/omap4-common.h>
+#include <plat/omap_device.h>
+
+#include "hsi_driver.h"
+
+#if 0
+static struct pm_qos_request_list *pm_qos_handle;
+#endif
+
+#define HSI_MODULENAME "omap_hsi"
+#define HSI_DRIVER_VERSION "0.4.1"
+#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 */
+
+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;
+ int port;
+
+ pdata->ctx->sysconfig = hsi_inl(base, HSI_SYS_SYSCONFIG_REG);
+ pdata->ctx->gdd_gcr = hsi_inl(base, HSI_GDD_GCR_REG);
+ if (hsi_driver_device_is_hsi(pdev))
+ pdata->ctx->dll = hsi_inl(base, HSI_HSR_DLL_REG);
+
+ for (port = 1; port <= pdata->num_ports; port++) {
+ p = &pdata->ctx->pctx[port - 1];
+ /* HSI TOP */
+ p->sys_mpu_enable[0] = hsi_inl(base,
+ HSI_SYS_MPU_ENABLE_REG(port, 0));
+ p->sys_mpu_enable[1] = hsi_inl(base,
+ HSI_SYS_MPU_U_ENABLE_REG(port, 0));
+
+ /* HST */
+ p->hst.mode = hsi_inl(base, HSI_HST_MODE_REG(port));
+ if (!hsi_driver_device_is_hsi(pdev))
+ p->hst.frame_size = hsi_inl(base,
+ HSI_HST_FRAMESIZE_REG(port));
+ p->hst.divisor = hsi_inl(base, HSI_HST_DIVISOR_REG(port));
+ p->hst.channels = hsi_inl(base, HSI_HST_CHANNELS_REG(port));
+ p->hst.arb_mode = hsi_inl(base, HSI_HST_ARBMODE_REG(port));
+
+ /* HSR */
+ p->hsr.mode = hsi_inl(base, HSI_HSR_MODE_REG(port));
+ if (!hsi_driver_device_is_hsi(pdev))
+ p->hsr.frame_size = hsi_inl(base,
+ HSI_HSR_FRAMESIZE_REG(port));
+ p->hsr.divisor = hsi_inl(base, HSI_HSR_DIVISOR_REG(port));
+ p->hsr.channels = hsi_inl(base, HSI_HSR_CHANNELS_REG(port));
+ p->hsr.counters = hsi_inl(base, HSI_HSR_COUNTERS_REG(port));
+ }
+}
+
+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;
+ int port;
+
+ hsi_outl(pdata->ctx->sysconfig, base, HSI_SYS_SYSCONFIG_REG);
+ hsi_outl(pdata->ctx->gdd_gcr, base, HSI_GDD_GCR_REG);
+ if (hsi_driver_device_is_hsi(pdev))
+ hsi_outl(pdata->ctx->dll, base, HSI_HSR_DLL_REG);
+
+ for (port = 1; port <= pdata->num_ports; port++) {
+ p = &pdata->ctx->pctx[port - 1];
+ /* HSI TOP */
+ hsi_outl(p->sys_mpu_enable[0], base,
+ HSI_SYS_MPU_ENABLE_REG(port, 0));
+ hsi_outl(p->sys_mpu_enable[1], base,
+ HSI_SYS_MPU_U_ENABLE_REG(port, 0));
+
+ /* HST */
+ hsi_outl(p->hst.mode, base, HSI_HST_MODE_REG(port));
+ if (!hsi_driver_device_is_hsi(pdev))
+ hsi_outl(p->hst.frame_size, base,
+ HSI_HST_FRAMESIZE_REG(port));
+ hsi_outl(p->hst.divisor, base, HSI_HST_DIVISOR_REG(port));
+ hsi_outl(p->hst.channels, base, HSI_HST_CHANNELS_REG(port));
+ hsi_outl(p->hst.arb_mode, base, HSI_HST_ARBMODE_REG(port));
+
+ /* HSR */
+ if (!hsi_driver_device_is_hsi(pdev))
+ hsi_outl(p->hsr.frame_size, base,
+ HSI_HSR_FRAMESIZE_REG(port));
+ hsi_outl(p->hsr.divisor, base, HSI_HSR_DIVISOR_REG(port));
+ hsi_outl(p->hsr.channels, base, HSI_HSR_CHANNELS_REG(port));
+ hsi_outl(p->hsr.counters, base, HSI_HSR_COUNTERS_REG(port));
+ }
+
+ 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);
+ }
+
+ /* As a last step move HSR from MODE_VAL.SLEEP to the relevant mode. */
+ /* This will enable the ACREADY flow control mechanism. */
+ for (port = 1; port <= pdata->num_ports; port++) {
+ p = &pdata->ctx->pctx[port - 1];
+ hsi_outl(p->hsr.mode, base, HSI_HSR_MODE_REG(port));
+ }
+}
+
+
+/* NOTE: Function called in soft interrupt context (tasklet) */
+int hsi_port_event_handler(struct hsi_port *p, unsigned int event, void *arg)
+{
+ struct hsi_channel *hsi_channel;
+ int ch;
+
+
+ if (event == HSI_EVENT_HSR_DATAAVAILABLE) {
+ /* The data-available event is channel-specific and must not be
+ * broadcasted
+ */
+ hsi_channel = p->hsi_channel + (int)arg;
+ read_lock(&hsi_channel->rw_lock);
+ if ((hsi_channel->dev) && (hsi_channel->port_event))
+ hsi_channel->port_event(hsi_channel->dev, event, arg);
+ read_unlock(&hsi_channel->rw_lock);
+ } else {
+ for (ch = 0; ch < p->max_ch; ch++) {
+ hsi_channel = p->hsi_channel + ch;
+ read_lock(&hsi_channel->rw_lock);
+ if ((hsi_channel->dev) && (hsi_channel->port_event))
+ hsi_channel->port_event(hsi_channel->dev,
+ event, arg);
+ read_unlock(&hsi_channel->rw_lock);
+ }
+ }
+ return 0;
+}
+
+static void hsi_dev_release(struct device *dev)
+{
+ /* struct device kfree is already made in unregister_hsi_devices().
+ * Registering this function is necessary to avoid an error from
+ * the device_release() function.
+ */
+}
+
+/* Register a hsi_device, linked to a port and channel id */
+static int __init reg_hsi_dev_ch(struct hsi_dev *hsi_ctrl, unsigned int p,
+ unsigned int ch)
+{
+ struct hsi_device *dev;
+ struct hsi_port *port = &hsi_ctrl->hsi_port[p];
+ int err;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->n_ctrl = hsi_ctrl->id;
+ dev->n_p = p;
+ dev->n_ch = ch;
+ dev->ch = &port->hsi_channel[ch];
+ dev->device.bus = &hsi_bus_type;
+ dev->device.parent = hsi_ctrl->dev;
+ dev->device.release = hsi_dev_release;
+ if (dev->n_ctrl < 0)
+ dev_set_name(&dev->device, "omap_hsi-p%u.c%u", p, ch);
+ else
+ dev_set_name(&dev->device, "omap_hsi%d-p%u.c%u", dev->n_ctrl, p,
+ ch);
+
+ dev_dbg(hsi_ctrl->dev,
+ "reg_hsi_dev_ch, port %d, ch %d, hsi_ctrl->dev:0x%x,"
+ "&dev->device:0x%x\n",
+ p, ch, (unsigned int)hsi_ctrl->dev, (unsigned int)&dev->device);
+
+ err = device_register(&dev->device);
+ if (err >= 0) {
+ write_lock_bh(&port->hsi_channel[ch].rw_lock);
+ port->hsi_channel[ch].dev = dev;
+ write_unlock_bh(&port->hsi_channel[ch].rw_lock);
+ } else {
+ kfree(dev);
+ }
+ return err;
+}
+
+static int __init register_hsi_devices(struct hsi_dev *hsi_ctrl)
+{
+ int port;
+ int ch;
+ int err;
+
+ for (port = 0; port < hsi_ctrl->max_p; port++)
+ for (ch = 0; ch < hsi_ctrl->hsi_port[port].max_ch; ch++) {
+ err = reg_hsi_dev_ch(hsi_ctrl, port, ch);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static void __exit unregister_hsi_devices(struct hsi_dev *hsi_ctrl)
+{
+ struct hsi_port *hsi_p;
+ struct hsi_device *device;
+ unsigned int port;
+ unsigned int ch;
+
+ for (port = 0; port < hsi_ctrl->max_p; port++) {
+ hsi_p = &hsi_ctrl->hsi_port[port];
+ for (ch = 0; ch < hsi_p->max_ch; ch++) {
+ device = hsi_p->hsi_channel[ch].dev;
+ hsi_close(device);
+ device_unregister(&device->device);
+ kfree(device);
+ }
+ }
+}
+
+void hsi_set_pm_default(struct hsi_dev *hsi_ctrl)
+{
+ /* Set default SYSCONFIG PM settings */
+ hsi_outl((HSI_AUTOIDLE | HSI_SIDLEMODE_SMART_WAKEUP |
+ HSI_MIDLEMODE_SMART_WAKEUP),
+ hsi_ctrl->base, HSI_SYS_SYSCONFIG_REG);
+ hsi_outl(HSI_CLK_AUTOGATING_ON, hsi_ctrl->base, HSI_GDD_GCR_REG);
+
+ /* HSI_TODO : use the HWMOD API : omap_hwmod_set_slave_idlemode() */
+}
+
+void hsi_set_pm_force_hsi_on(struct hsi_dev *hsi_ctrl)
+{
+ /* Force HSI to ON by never acknowledging a PRCM idle request */
+ /* SIdleAck and MStandby are never asserted */
+ hsi_outl((HSI_AUTOIDLE | HSI_SIDLEMODE_NO |
+ HSI_MIDLEMODE_NO),
+ hsi_ctrl->base, HSI_SYS_SYSCONFIG_REG);
+ hsi_outl(HSI_CLK_AUTOGATING_ON, hsi_ctrl->base, HSI_GDD_GCR_REG);
+
+ /* HSI_TODO : use the HWMOD API : omap_hwmod_set_slave_idlemode() */
+}
+
+int hsi_softreset(struct hsi_dev *hsi_ctrl)
+{
+ unsigned int ind = 0;
+ void __iomem *base = hsi_ctrl->base;
+ u32 status;
+
+ /* Reseting HSI Block */
+ hsi_outl_or(HSI_SOFTRESET, base, HSI_SYS_SYSCONFIG_REG);
+ do {
+ status = hsi_inl(base, HSI_SYS_SYSSTATUS_REG);
+ ind++;
+ } while ((!(status & HSI_RESETDONE)) &&
+ (ind < HSI_RESETDONE_MAX_RETRIES));
+
+ if (ind >= HSI_RESETDONE_MAX_RETRIES) {
+ dev_err(hsi_ctrl->dev, "HSI SW_RESET failed to complete within"
+ " %d retries.\n", HSI_RESETDONE_MAX_RETRIES);
+ return -EIO;
+ } else if (ind > HSI_RESETDONE_NORMAL_RETRIES) {
+ dev_warn(hsi_ctrl->dev, "HSI SW_RESET abnormally long:"
+ " %d retries to complete.\n", ind);
+ }
+
+ ind = 0;
+ /* Reseting DMA Engine */
+ hsi_outl_or(HSI_GDD_GRST_SWRESET, base, HSI_GDD_GRST_REG);
+ do {
+ status = hsi_inl(base, HSI_GDD_GRST_REG);
+ ind++;
+ } while ((status & HSI_GDD_GRST_SWRESET) &&
+ (ind < HSI_RESETDONE_MAX_RETRIES));
+
+ if (ind >= HSI_RESETDONE_MAX_RETRIES) {
+ dev_err(hsi_ctrl->dev, "HSI DMA SW_RESET failed to complete"
+ " within %d retries.\n", HSI_RESETDONE_MAX_RETRIES);
+ return -EIO;
+ }
+
+ if (ind > HSI_RESETDONE_NORMAL_RETRIES) {
+ dev_warn(hsi_ctrl->dev, "HSI DMA SW_RESET abnormally long:"
+ " %d retries to complete.\n", ind);
+ }
+
+ return 0;
+}
+
+static void hsi_set_ports_default(struct hsi_dev *hsi_ctrl,
+ struct platform_device *pd)
+{
+ struct port_ctx *cfg;
+ struct hsi_platform_data *pdata = pd->dev.platform_data;
+ unsigned int port = 0;
+ void __iomem *base = hsi_ctrl->base;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+ for (port = 1; port <= pdata->num_ports; port++) {
+ cfg = &pdata->ctx->pctx[port - 1];
+ /* HST */
+ hsi_outl(cfg->hst.mode | cfg->hst.flow |
+ HSI_HST_MODE_WAKE_CTRL_SW, base,
+ HSI_HST_MODE_REG(port));
+ if (!hsi_driver_device_is_hsi(pdev))
+ hsi_outl(cfg->hst.frame_size, base,
+ HSI_HST_FRAMESIZE_REG(port));
+ hsi_outl(cfg->hst.divisor, base, HSI_HST_DIVISOR_REG(port));
+ hsi_outl(cfg->hst.channels, base, HSI_HST_CHANNELS_REG(port));
+ hsi_outl(cfg->hst.arb_mode, base, HSI_HST_ARBMODE_REG(port));
+
+ /* HSR */
+ hsi_outl(cfg->hsr.mode | cfg->hsr.flow, base,
+ HSI_HSR_MODE_REG(port));
+ if (!hsi_driver_device_is_hsi(pdev))
+ hsi_outl(cfg->hsr.frame_size, base,
+ HSI_HSR_FRAMESIZE_REG(port));
+ hsi_outl(cfg->hsr.channels, base, HSI_HSR_CHANNELS_REG(port));
+ if (hsi_driver_device_is_hsi(pdev))
+ hsi_outl(cfg->hsr.divisor, base,
+ HSI_HSR_DIVISOR_REG(port));
+ hsi_outl(cfg->hsr.counters, base, HSI_HSR_COUNTERS_REG(port));
+ }
+
+ 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_outl(pdata->ctx->dll, base, HSI_HSR_DLL_REG);
+ }
+}
+
+static int __init hsi_port_channels_init(struct hsi_port *port)
+{
+ struct hsi_channel *ch;
+ unsigned int ch_i;
+
+ for (ch_i = 0; ch_i < port->max_ch; ch_i++) {
+ ch = &port->hsi_channel[ch_i];
+ ch->channel_number = ch_i;
+ rwlock_init(&ch->rw_lock);
+ ch->flags = 0;
+ ch->hsi_port = port;
+ ch->read_data.addr = NULL;
+ ch->read_data.size = 0;
+ ch->read_data.lch = -1;
+ ch->write_data.addr = NULL;
+ ch->write_data.size = 0;
+ ch->write_data.lch = -1;
+ ch->dev = NULL;
+ ch->read_done = NULL;
+ ch->write_done = NULL;
+ ch->port_event = NULL;
+ }
+
+ return 0;
+}
+
+static int hsi_port_channels_reset(struct hsi_port *port)
+{
+ struct hsi_channel *ch;
+ unsigned int ch_i;
+
+ for (ch_i = 0; ch_i < port->max_ch; ch_i++) {
+ ch = &port->hsi_channel[ch_i];
+ ch->flags = 0;
+ ch->read_data.addr = NULL;
+ ch->read_data.size = 0;
+ ch->read_data.lch = -1;
+ ch->write_data.addr = NULL;
+ ch->write_data.size = 0;
+ ch->write_data.lch = -1;
+ }
+
+ return 0;
+}
+
+void hsi_softreset_driver(struct hsi_dev *hsi_ctrl)
+{
+ struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
+ struct hsi_platform_data *pdata = pd->dev.platform_data;
+ struct hsi_port *hsi_p;
+ unsigned int port;
+ u32 revision;
+
+ /* HSI port reset */
+ for (port = 0; port < hsi_ctrl->max_p; port++) {
+ hsi_p = &hsi_ctrl->hsi_port[port];
+ hsi_p->counters_on = 1;
+ hsi_p->reg_counters = pdata->ctx->pctx[port].hsr.counters;
+ hsi_port_channels_reset(&hsi_ctrl->hsi_port[port]);
+ }
+
+ hsi_set_pm_default(hsi_ctrl);
+
+ /* Re-Configure HSI ports */
+ hsi_set_ports_default(hsi_ctrl, pd);
+
+ /* Gather info from registers for the driver.(REVISION) */
+ revision = hsi_inl(hsi_ctrl->base, HSI_SYS_REVISION_REG);
+ if (hsi_driver_device_is_hsi(pd))
+ dev_info(hsi_ctrl->dev, "HSI Hardware REVISION 0x%x\n",
+ revision);
+ else
+ dev_info(hsi_ctrl->dev, "SSI Hardware REVISION %d.%d\n",
+ (revision & HSI_SSI_REV_MAJOR) >> 4,
+ (revision & HSI_SSI_REV_MINOR));
+}
+
+static int __init hsi_request_mpu_irq(struct hsi_port *hsi_p)
+{
+ struct hsi_dev *hsi_ctrl = hsi_p->hsi_controller;
+ struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
+ struct resource *mpu_irq;
+
+ if (hsi_driver_device_is_hsi(pd))
+ mpu_irq = platform_get_resource(pd, IORESOURCE_IRQ,
+ hsi_p->port_number - 1);
+ else /* SSI support 2 IRQs per port */
+ mpu_irq = platform_get_resource(pd, IORESOURCE_IRQ,
+ (hsi_p->port_number - 1) * 2);
+
+ if (!mpu_irq) {
+ dev_err(hsi_ctrl->dev, "HSI misses info for MPU IRQ on"
+ " port %d\n", hsi_p->port_number);
+ return -ENXIO;
+ }
+ hsi_p->n_irq = 0; /* We only use one irq line */
+ hsi_p->irq = mpu_irq->start;
+ return hsi_mpu_init(hsi_p, mpu_irq->name);
+}
+
+static int __init hsi_request_cawake_irq(struct hsi_port *hsi_p)
+{
+ struct hsi_dev *hsi_ctrl = hsi_p->hsi_controller;
+ struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
+ struct resource *cawake_irq;
+
+ if (hsi_driver_device_is_hsi(pd)) {
+ hsi_p->cawake_gpio = -1;
+ return 0;
+ } else {
+ cawake_irq = platform_get_resource(pd, IORESOURCE_IRQ,
+ 4 + hsi_p->port_number);
+ }
+
+ if (!cawake_irq) {
+ dev_err(hsi_ctrl->dev, "SSI device misses info for CAWAKE"
+ "IRQ on port %d\n", hsi_p->port_number);
+ return -ENXIO;
+ }
+
+ if (cawake_irq->flags & IORESOURCE_UNSET) {
+ dev_info(hsi_ctrl->dev, "No CAWAKE GPIO support\n");
+ hsi_p->cawake_gpio = -1;
+ return 0;
+ }
+
+ hsi_p->cawake_gpio_irq = cawake_irq->start;
+ hsi_p->cawake_gpio = irq_to_gpio(cawake_irq->start);
+ return hsi_cawake_init(hsi_p, cawake_irq->name);
+}
+
+static void hsi_ports_exit(struct hsi_dev *hsi_ctrl, unsigned int max_ports)
+{
+ struct hsi_port *hsi_p;
+ unsigned int port;
+
+ for (port = 0; port < max_ports; port++) {
+ hsi_p = &hsi_ctrl->hsi_port[port];
+ hsi_mpu_exit(hsi_p);
+ hsi_cawake_exit(hsi_p);
+ }
+}
+
+static int __init hsi_ports_init(struct hsi_dev *hsi_ctrl)
+{
+ struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
+ struct hsi_platform_data *pdata = pd->dev.platform_data;
+ struct hsi_port *hsi_p;
+ unsigned int port;
+ int err;
+
+ for (port = 0; port < hsi_ctrl->max_p; port++) {
+ hsi_p = &hsi_ctrl->hsi_port[port];
+ hsi_p->port_number = port + 1;
+ 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->cawake_status = -1; /* Unknown */
+ hsi_p->cawake_off_event = false;
+ hsi_p->acwake_status = 0;
+ hsi_p->in_int_tasklet = false;
+ hsi_p->in_cawake_tasklet = false;
+ 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]);
+ if (err < 0)
+ goto rback1;
+ err = hsi_request_mpu_irq(hsi_p);
+ if (err < 0)
+ goto rback2;
+ err = hsi_request_cawake_irq(hsi_p);
+ if (err < 0)
+ goto rback3;
+ }
+ return 0;
+rback3:
+ hsi_mpu_exit(hsi_p);
+rback2:
+ hsi_ports_exit(hsi_ctrl, port + 1);
+rback1:
+ return err;
+}
+
+static int __init hsi_request_gdd_irq(struct hsi_dev *hsi_ctrl)
+{
+ struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
+ struct resource *gdd_irq;
+
+ if (hsi_driver_device_is_hsi(pd))
+ gdd_irq = platform_get_resource(pd, IORESOURCE_IRQ, 2);
+ else
+ gdd_irq = platform_get_resource(pd, IORESOURCE_IRQ, 4);
+
+ if (!gdd_irq) {
+ dev_err(hsi_ctrl->dev, "HSI has no GDD IRQ resource\n");
+ return -ENXIO;
+ }
+
+ hsi_ctrl->gdd_irq = gdd_irq->start;
+ return hsi_gdd_init(hsi_ctrl, gdd_irq->name);
+}
+
+static int __init hsi_init_gdd_chan_count(struct hsi_dev *hsi_ctrl)
+{
+ struct platform_device *pd = to_platform_device(hsi_ctrl->dev);
+ u8 gdd_chan_count;
+ struct hsi_platform_data *pdata =
+ (struct hsi_platform_data *)pd->dev.platform_data;
+ int i;
+
+ if (!pdata) {
+ dev_err(hsi_ctrl->dev, "HSI has no platform data\n");
+ return -ENXIO;
+ }
+
+ gdd_chan_count = pdata->hsi_gdd_chan_count;
+
+ if (!gdd_chan_count) {
+ dev_warn(hsi_ctrl->dev, "HSI device has no GDD channel count "
+ "(use %d as default)\n",
+ HSI_DMA_CHANNEL_DEFAULT);
+ hsi_ctrl->gdd_chan_count = HSI_DMA_CHANNEL_DEFAULT;
+ } else {
+ hsi_ctrl->gdd_chan_count = gdd_chan_count;
+ /* Check that the number of channels is power of 2 */
+ for (i = 0; i < 16; i++) {
+ if (hsi_ctrl->gdd_chan_count == (1 << i))
+ break;
+ }
+ if (i >= 16)
+ dev_err(hsi_ctrl->dev, "The Number of DMA channels "
+ "shall be a power of 2! (=%d)\n",
+ hsi_ctrl->gdd_chan_count);
+ }
+ return 0;
+}
+
+/**
+* hsi_clocks_disable_channel - virtual wrapper for disabling HSI clocks for
+* a given channel
+* @dev - reference to the hsi device.
+* @channel_number - channel number which requests clock to be disabled
+* 0xFF means no particular channel
+*
+* Note : there is no real HW clock management per HSI channel, this is only
+* virtual to keep track of active channels and ease debug
+*
+* Function to be called with lock
+*/
+void hsi_clocks_disable_channel(struct device *dev, u8 channel_number,
+ const char *s)
+{
+ struct platform_device *pd = to_platform_device(dev);
+ struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd);
+
+ if (channel_number != HSI_CH_NUMBER_NONE)
+ dev_dbg(dev, "CLK: hsi_clocks_disable for "
+ "channel %d: %s\n", channel_number, s);
+ else
+ dev_dbg(dev, "CLK: hsi_clocks_disable: %s\n", s);
+
+ if (!hsi_ctrl->clock_enabled) {
+ dev_dbg(dev, "Clocks already disabled, skipping...\n");
+ return;
+ }
+ if (hsi_is_hsi_controller_busy(hsi_ctrl)) {
+ dev_dbg(dev, "Cannot disable clocks, HSI port busy\n");
+ return;
+ }
+
+ if (hsi_is_hst_controller_busy(hsi_ctrl))
+ dev_dbg(dev, "Disabling clocks with HST FSM not IDLE !\n");
+
+#ifdef K3_0_PORTING_HSI_MISSING_FEATURE
+ /* Allow Fclk to change */
+ if (dpll_cascading_blocker_release(dev) < 0)
+ dev_warn(dev, "Error releasing DPLL cascading constraint\n");
+#endif
+
+ /* HSI_TODO : this can probably be changed
+ * to return pm_runtime_put(dev);
+ */
+ pm_runtime_put_sync(dev);
+}
+
+/**
+* hsi_clocks_enable_channel - virtual wrapper for enabling HSI clocks for
+* a given channel
+* @dev - reference to the hsi device.
+* @channel_number - channel number which requests clock to be enabled
+* 0xFF means no particular channel
+*
+* Note : there is no real HW clock management per HSI channel, this is only
+* virtual to keep track of active channels and ease debug
+*
+* Function to be called with lock
+*/
+int hsi_clocks_enable_channel(struct device *dev, u8 channel_number,
+ const char *s)
+{
+ struct platform_device *pd = to_platform_device(dev);
+ struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd);
+
+ if (channel_number != HSI_CH_NUMBER_NONE)
+ dev_dbg(dev, "CLK: hsi_clocks_enable for "
+ "channel %d: %s\n", channel_number, s);
+ else
+ dev_dbg(dev, "CLK: hsi_clocks_enable: %s\n", s);
+
+ if (hsi_ctrl->clock_enabled) {
+ dev_dbg(dev, "Clocks already enabled, skipping...\n");
+ return -EEXIST;
+ }
+
+#ifdef K3_0_PORTING_HSI_MISSING_FEATURE
+ /* Prevent Fclk change */
+ if (dpll_cascading_blocker_hold(dev) < 0)
+ dev_warn(dev, "Error holding DPLL cascading constraint\n");
+#endif
+
+ return pm_runtime_get_sync(dev);
+}
+
+static int __init hsi_controller_init(struct hsi_dev *hsi_ctrl,
+ struct platform_device *pd)
+{
+ struct hsi_platform_data *pdata = pd->dev.platform_data;
+ struct resource *mem, *ioarea;
+ int err;
+
+ mem = platform_get_resource(pd, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pd->dev, "HSI device does not have "
+ "HSI IO memory region information\n");
+ return -ENXIO;
+ }
+ dev_dbg(&pd->dev, "hsi_controller_init : IORESOURCE_MEM %s [%x, %x]\n",
+ mem->name, mem->start, mem->end);
+
+ ioarea = devm_request_mem_region(&pd->dev, mem->start,
+ (mem->end - mem->start) + 1,
+ dev_name(&pd->dev));
+ if (!ioarea) {
+ dev_err(&pd->dev, "Unable to request HSI IO mem region\n");
+ return -EBUSY;
+ }
+ dev_dbg(&pd->dev, "hsi_controller_init : ioarea %s [%x, %x]\n",
+ ioarea->name, ioarea->start, ioarea->end);
+
+ hsi_ctrl->phy_base = mem->start;
+ hsi_ctrl->base = devm_ioremap(&pd->dev, mem->start,
+ (mem->end - mem->start) + 1);
+ if (!hsi_ctrl->base) {
+ dev_err(&pd->dev, "Unable to ioremap HSI base IO address\n");
+ return -ENXIO;
+ }
+ dev_dbg(&pd->dev, "hsi_controller_init : hsi_ctrl->base=%x\n",
+ (unsigned int)hsi_ctrl->base);
+
+ hsi_ctrl->id = pd->id;
+ if (pdata->num_ports > HSI_MAX_PORTS) {
+ dev_err(&pd->dev, "The HSI driver does not support enough "
+ "ports!\n");
+ return -ENXIO;
+ }
+ hsi_ctrl->max_p = pdata->num_ports;
+ hsi_ctrl->in_dma_tasklet = false;
+ hsi_ctrl->fifo_mapping_strategy = HSI_FIFO_MAPPING_UNDEF;
+ hsi_ctrl->dev = &pd->dev;
+ spin_lock_init(&hsi_ctrl->lock);
+ err = hsi_init_gdd_chan_count(hsi_ctrl);
+ if (err < 0)
+ goto rback1;
+
+ err = hsi_ports_init(hsi_ctrl);
+ if (err < 0)
+ goto rback1;
+
+ err = hsi_request_gdd_irq(hsi_ctrl);
+ if (err < 0)
+ goto rback2;
+
+ /* Everything is fine */
+ return 0;
+rback2:
+ hsi_ports_exit(hsi_ctrl, hsi_ctrl->max_p);
+rback1:
+ dev_err(&pd->dev, "Error on hsi_controller initialization\n");
+ return err;
+}
+
+static void hsi_controller_exit(struct hsi_dev *hsi_ctrl)
+{
+ hsi_gdd_exit(hsi_ctrl);
+ hsi_ports_exit(hsi_ctrl, hsi_ctrl->max_p);
+}
+
+/* HSI Platform Device probing & hsi_device registration */
+static int __init hsi_platform_device_probe(struct platform_device *pd)
+{
+ struct hsi_platform_data *pdata = pd->dev.platform_data;
+ struct hsi_dev *hsi_ctrl;
+ u32 revision;
+ int err;
+
+ dev_dbg(&pd->dev, "HSI DRIVER : hsi_platform_device_probe\n");
+
+ dev_dbg(&pd->dev, "The platform device probed is an %s\n",
+ hsi_driver_device_is_hsi(pd) ? "HSI" : "SSI");
+
+ if (!pdata) {
+ dev_err(&pd->dev, "No platform_data found on hsi device\n");
+ return -ENXIO;
+ }
+
+ hsi_ctrl = kzalloc(sizeof(*hsi_ctrl), GFP_KERNEL);
+ if (hsi_ctrl == NULL) {
+ dev_err(&pd->dev, "Could not allocate memory for"
+ " struct hsi_dev\n");
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pd, hsi_ctrl);
+ err = hsi_controller_init(hsi_ctrl, pd);
+ if (err < 0) {
+ dev_err(&pd->dev, "Could not initialize hsi controller:"
+ " %d\n", err);
+ goto rollback1;
+ }
+ /* Wakeup dependency was disabled for HSI <-> MPU PM_L3INIT_HSI_WKDEP */
+#if 0
+ omap_writel(0x141, 0x4A307338);
+#endif
+ pm_runtime_enable(hsi_ctrl->dev);
+ hsi_clocks_enable(hsi_ctrl->dev, __func__);
+
+ /* Non critical SW Reset */
+ err = hsi_softreset(hsi_ctrl);
+ if (err < 0)
+ goto rollback2;
+
+ hsi_set_pm_default(hsi_ctrl);
+
+ /* Configure HSI ports */
+ hsi_set_ports_default(hsi_ctrl, pd);
+
+ /* Gather info from registers for the driver.(REVISION) */
+ revision = hsi_inl(hsi_ctrl->base, HSI_SYS_REVISION_REG);
+ if (hsi_driver_device_is_hsi(pd))
+ dev_info(hsi_ctrl->dev, "HSI Hardware REVISION 0x%x\n",
+ revision);
+ else
+ dev_info(hsi_ctrl->dev, "SSI Hardware REVISION %d.%d\n",
+ (revision & HSI_SSI_REV_MAJOR) >> 4,
+ (revision & HSI_SSI_REV_MINOR));
+
+ err = hsi_debug_add_ctrl(hsi_ctrl);
+ if (err < 0) {
+ dev_err(&pd->dev,
+ "Could not add hsi controller to debugfs: %d\n", err);
+ goto rollback2;
+ }
+
+ err = register_hsi_devices(hsi_ctrl);
+ if (err < 0) {
+ dev_err(&pd->dev, "Could not register hsi_devices: %d\n", err);
+ goto rollback3;
+ }
+
+ /* 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",
+ pdata->default_hsi_fclk);
+#endif
+
+ /* From here no need for HSI HW access */
+ hsi_clocks_disable(hsi_ctrl->dev, __func__);
+
+ return err;
+
+rollback3:
+ hsi_debug_remove_ctrl(hsi_ctrl);
+rollback2:
+ hsi_controller_exit(hsi_ctrl);
+
+ /* From here no need for HSI HW access */
+ hsi_clocks_disable(hsi_ctrl->dev, __func__);
+
+rollback1:
+ kfree(hsi_ctrl);
+ return err;
+}
+
+static int __exit hsi_platform_device_remove(struct platform_device *pd)
+{
+ struct hsi_dev *hsi_ctrl = platform_get_drvdata(pd);
+
+ dev_dbg(&pd->dev, "HSI DRIVER : hsi_platform_device_remove\n");
+
+ if (!hsi_ctrl)
+ return 0;
+
+ unregister_hsi_devices(hsi_ctrl);
+
+ /* From here no need for HSI HW access */
+ pm_runtime_disable(hsi_ctrl->dev);
+
+ hsi_debug_remove_ctrl(hsi_ctrl);
+ hsi_controller_exit(hsi_ctrl);
+
+ kfree(hsi_ctrl);
+
+ return 0;
+}
+
+#ifdef CONFIG_SUSPEND
+static int hsi_suspend_noirq(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);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* If HSI is enabled, CAWAKE IO wakeup has been disabled and */
+ /* 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;
+ }
+
+ /* Perform HSI board specific action before platform suspend */
+ if (pdata->board_suspend)
+ pdata->board_suspend(0, device_may_wakeup(dev));
+
+ return 0;
+}
+
+static int hsi_resume_noirq(struct device *dev)
+{
+ struct hsi_platform_data *pdata = dev->platform_data;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* This function shall not schedule the tasklet, because it is */
+ /* redundant with what is already done in the PRCM interrupt handler. */
+ /* 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. */
+ /* 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 */
+ /* the winner. */
+
+ /* Perform (optional) HSI board specific action after platform wakeup */
+ if (pdata->board_resume)
+ pdata->board_resume(0);
+
+ return 0;
+}
+#endif /* CONFIG_PM_SUSPEND */
+
+#ifdef CONFIG_PM_RUNTIME
+/**
+* hsi_runtime_resume - executed by the PM core for the bus type of the device being woken up
+* @dev - reference to the hsi device.
+*
+*
+*/
+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;
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (hsi_ctrl->clock_enabled)
+ dev_warn(dev, "Warning: clock status mismatch vs runtime PM\n");
+
+ hsi_ctrl->clock_enabled = true;
+
+ /* Restore context */
+ hsi_restore_ctx(hsi_ctrl);
+
+ /* When HSI is ON, no need for IO wakeup mechanism */
+ pdata->wakeup_disable(0);
+
+ /* HSI device is now fully operational and _must_ be able to */
+ /* complete I/O operations */
+
+ return 0;
+}
+
+/**
+* hsi_runtime_suspend - Prepare HSI for low power : device will not process data and will
+ not communicate with the CPU
+* @dev - reference to the hsi device.
+*
+* Return value : -EBUSY or -EAGAIN if device is busy and still operational
+*
+*/
+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;
+ 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 */
+ if (device_may_wakeup(dev))
+ pdata->wakeup_enable(0);
+ else
+ pdata->wakeup_disable(0);
+
+ /* HSI is now ready to be put in low power state */
+
+ return 0;
+}
+
+/* Based on counters, device appears to be idle.
+ * Check if the device can be suspended.
+ */
+static int hsi_runtime_idle(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_hsi_controller_busy(hsi_ctrl)) {
+ dev_dbg(dev, "hsi_runtime_idle: HSI port busy\n");
+ return -EBUSY;
+ }
+
+ if (hsi_is_hst_controller_busy(hsi_ctrl)) {
+ dev_dbg(dev, "hsi_runtime_idle: HST FSM not IDLE !\n");
+ return -EBUSY;
+ }
+
+ /* HSI_TODO : check also the interrupt status registers.*/
+
+ return 0;
+}
+
+#endif /* CONFIG_PM_RUNTIME */
+
+int hsi_driver_device_is_hsi(struct platform_device *dev)
+{
+ struct platform_device_id *id =
+ (struct platform_device_id *)platform_get_device_id(dev);
+ return (id->driver_data == HSI_DRV_DEVICE_HSI);
+}
+
+/* List of devices supported by this driver */
+static struct platform_device_id hsi_id_table[] = {
+ {"omap_hsi", HSI_DRV_DEVICE_HSI},
+ {"omap_ssi", HSI_DRV_DEVICE_SSI},
+ {},
+};
+
+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,
+#endif
+#ifdef CONFIG_PM_RUNTIME
+ .runtime_suspend = hsi_runtime_suspend,
+ .runtime_resume = hsi_runtime_resume,
+ .runtime_idle = hsi_runtime_idle,
+#endif
+};
+
+#define HSI_DRIVER_PM_OPS_PTR (&hsi_driver_pm_ops)
+
+#else /* !CONFIG_PM */
+
+#define HSI_DRIVER_PM_OPS_PTR NULL
+
+#endif
+
+static struct platform_driver hsi_pdriver = {
+ .driver = {
+ .name = HSI_MODULENAME,
+ .owner = THIS_MODULE,
+#ifdef CONFIG_PM
+ .pm = HSI_DRIVER_PM_OPS_PTR,
+#endif
+ },
+ .id_table = hsi_id_table,
+ .remove = __exit_p(hsi_platform_device_remove),
+};
+
+/* HSI bus and platform driver registration */
+static int __init hsi_driver_init(void)
+{
+ int err = 0;
+
+ pr_info(LOG_NAME "HSI DRIVER Version " HSI_DRIVER_VERSION "\n");
+
+ /* Register the (virtual) HSI bus */
+ err = hsi_bus_init();
+ if (err < 0) {
+ pr_err(LOG_NAME "HSI bus_register err %d\n", err);
+ return err;
+ }
+
+ err = hsi_debug_init();
+ if (err < 0) {
+ pr_err(LOG_NAME "HSI Debugfs failed %d\n", err);
+ goto rback1;
+ }
+
+ /* Register the HSI platform driver */
+ err = platform_driver_probe(&hsi_pdriver, hsi_platform_device_probe);
+ if (err < 0) {
+ pr_err(LOG_NAME "Platform DRIVER register FAILED: %d\n", err);
+ goto rback2;
+ }
+
+ return 0;
+rback2:
+ hsi_debug_exit();
+rback1:
+ hsi_bus_exit();
+ return err;
+}
+
+static void __exit hsi_driver_exit(void)
+{
+ platform_driver_unregister(&hsi_pdriver);
+ hsi_debug_exit();
+ hsi_bus_exit();
+
+ pr_info(LOG_NAME "HSI DRIVER removed\n");
+}
+
+module_init(hsi_driver_init);
+module_exit(hsi_driver_exit);
+
+MODULE_ALIAS("platform:" HSI_MODULENAME);
+MODULE_AUTHOR("Carlos Chinea / Nokia");
+MODULE_AUTHOR("Sebastien JAN / Texas Instruments");
+MODULE_AUTHOR("Djamil ELAIDI / Texas Instruments");
+MODULE_DESCRIPTION("MIPI High-speed Synchronous Serial Interface (HSI) Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/omap_hsi/hsi_driver.h b/drivers/omap_hsi/hsi_driver.h
new file mode 100644
index 0000000..0991d98
--- /dev/null
+++ b/drivers/omap_hsi/hsi_driver.h
@@ -0,0 +1,398 @@
+/*
+ * hsi_driver.h
+ *
+ * Header file for the HSI driver low level interface.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef __HSI_DRIVER_H__
+#define __HSI_DRIVER_H__
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/gpio.h>
+#include <linux/notifier.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/hsi_driver_if.h>
+#include <plat/omap_hsi.h>
+
+/* Channel states */
+#define HSI_CH_OPEN 0x01
+#define HSI_CH_RX_POLL 0x10
+#define HSI_CH_ACWAKE 0x02 /* ACWAKE line status */
+
+#define HSI_CH_NUMBER_NONE 0xFF
+/*
+ * The number of channels handled by the driver in the ports, or the highest
+ * port channel number (+1) used. (MAX:8 for SSI; 16 for HSI)
+ * Reducing this value optimizes the driver memory footprint.
+ */
+#define HSI_PORT_MAX_CH HSI_CHANNELS_MAX
+
+/* Number of DMA channels when nothing is defined for the device */
+#define HSI_DMA_CHANNEL_DEFAULT 8
+
+
+#define LOG_NAME "OMAP HSI: "
+
+/* SW strategies for HSI FIFO mapping */
+enum {
+ HSI_FIFO_MAPPING_UNDEF = 0,
+ 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 {
+ HSI_DRV_DEVICE_HSI,
+ HSI_DRV_DEVICE_SSI
+};
+
+/**
+ * struct hsi_data - HSI buffer descriptor
+ * @addr: pointer to the buffer where to send or receive data
+ * @size: size in words (32 bits) of the buffer
+ * @lch: associated GDD (DMA) logical channel number, if any
+ */
+struct hsi_data {
+ u32 *addr;
+ unsigned int size;
+ int lch;
+};
+
+/**
+ * struct hsi_channel - HSI channel data
+ * @read_data: Incoming HSI buffer descriptor
+ * @write_data: Outgoing HSI buffer descriptor
+ * @hsi_port: Reference to port where the channel belongs to
+ * @flags: Tracks if channel has been open
+ * @channel_number: HSI channel number
+ * @rw_lock: Read/Write lock to serialize access to callback and hsi_device
+ * @dev: Reference to the associated hsi_device channel
+ * @write_done: Callback to signal TX completed.
+ * @read_done: Callback to signal RX completed.
+ * @port_event: Callback to signal port events (RX Error, HWBREAK, CAWAKE ...)
+ */
+struct hsi_channel {
+ struct hsi_data read_data;
+ struct hsi_data write_data;
+ struct hsi_port *hsi_port;
+ u8 flags;
+ u8 channel_number;
+ rwlock_t rw_lock;
+ struct hsi_device *dev;
+ void (*write_done) (struct hsi_device *dev, unsigned int size);
+ void (*read_done) (struct hsi_device *dev, unsigned int size);
+ void (*port_event) (struct hsi_device *dev, unsigned int event,
+ void *arg);
+};
+
+/**
+ * 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
+ * @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
+ * @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
+ * @cawake_off_event: True if CAWAKE event was detected from OFF mode
+ * @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
+ * @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
+ * @hsi_tasklet: Bottom half for interrupts when clocks are enabled
+ * @cawake_tasklet: Bottom half for cawake events
+ */
+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] */
+ u8 max_ch;
+ u8 n_irq;
+ int irq;
+ int cawake_gpio;
+ int cawake_gpio_irq;
+ int cawake_status;
+ bool cawake_off_event;
+ unsigned int acwake_status; /* HSI_TODO : fine tune init values */
+ bool in_int_tasklet;
+ bool in_cawake_tasklet;
+ int counters_on;
+ 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 hsi_dev - hsi controller driver data
+ * This structure is saved into platform_device->dev->p->driver_data
+ *
+ * @hsi_port: Array of hsi ports enabled in the controller
+ * @id: HSI controller platform id number
+ * @max_p: Number of ports enabled in the controller
+ * @hsi_clk: Reference to the HSI custom clock
+ * @base: HSI registers base virtual address
+ * @phy_base: HSI registers base physical address
+ * @lock: Serializes access to internal data and regs
+ * @clock_enabled: Indicates if HSI Clocks are ON
+ * @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
+ * @last_gdd_lch: Last used GDD logical channel
+ * @gdd_chan_count: Number of available DMA channels on the device (must be ^2)
+ * @in_dma_tasklet: True if DMA tasklet for the controller is currently running
+ * @set_min_bus_tput: (PM) callback to set minimun bus throuput
+ * @clk_notifier_register: (PM) callabck for DVFS support
+ * @clk_notifier_unregister: (PM) callabck for DVFS support
+ * @hsi_nb: (PM) Notification block for DVFS notification chain
+ * @hsi_gdd_tasklet: Bottom half for DMA Interrupts when clocks are enabled
+ * @dir: debugfs base directory
+ * @dev: Reference to the HSI platform device
+ */
+struct hsi_dev { /* HSI_TODO: should be later renamed into hsi_controller*/
+ struct hsi_port hsi_port[HSI_MAX_PORTS];
+ int id;
+ u8 max_p;
+ void __iomem *base;
+ unsigned long phy_base;
+ spinlock_t lock; /* Serializes access to internal data and regs */
+ bool clock_enabled;
+ int gdd_irq;
+ unsigned int fifo_mapping_strategy;
+ unsigned int gdd_usecount;
+ unsigned int last_gdd_lch;
+ unsigned int gdd_chan_count;
+ bool in_dma_tasklet;
+ void (*set_min_bus_tput) (struct device *dev, u8 agent_id,
+ unsigned long r);
+ struct notifier_block hsi_nb;
+ struct tasklet_struct hsi_gdd_tasklet;
+#ifdef CONFIG_DEBUG_FS
+ struct dentry *dir;
+#endif
+ struct device *dev;
+};
+
+/**
+ * struct hsi_platform_data - Board specific data
+*/
+struct hsi_platform_data {
+ void (*set_min_bus_tput) (struct device *dev, u8 agent_id,
+ unsigned long r);
+ int (*device_enable) (struct platform_device *pdev);
+ int (*device_shutdown) (struct platform_device *pdev);
+ int (*device_idle) (struct platform_device *pdev);
+ int (*wakeup_enable) (int hsi_port);
+ int (*wakeup_disable) (int hsi_port);
+ int (*wakeup_is_from_hsi) (void);
+ int (*board_suspend)(int hsi_port, bool dev_may_wakeup);
+ int (*board_resume)(int hsi_port);
+ u8 num_ports;
+ struct ctrl_ctx *ctx;
+ u8 hsi_gdd_chan_count;
+ unsigned long default_hsi_fclk;
+};
+
+/* HSI Bus */
+extern struct bus_type hsi_bus_type;
+
+int hsi_port_event_handler(struct hsi_port *p, unsigned int event, void *arg);
+int hsi_bus_init(void);
+void hsi_bus_exit(void);
+/* End HSI Bus */
+
+void hsi_reset_ch_read(struct hsi_channel *ch);
+void hsi_reset_ch_write(struct hsi_channel *ch);
+bool hsi_is_channel_busy(struct hsi_channel *ch);
+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);
+
+int hsi_driver_enable_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,
+ u32 *data);
+bool hsi_is_dma_read_int_pending(struct hsi_dev *hsi_ctrl);
+int hsi_driver_read_dma(struct hsi_channel *hsi_channel, u32 * data,
+ unsigned int count);
+int hsi_driver_write_dma(struct hsi_channel *hsi_channel, u32 * data,
+ unsigned int count);
+
+int hsi_driver_cancel_read_interrupt(struct hsi_channel *ch);
+int hsi_driver_cancel_write_interrupt(struct hsi_channel *ch);
+void hsi_driver_disable_read_interrupt(struct hsi_channel *ch);
+void hsi_driver_disable_write_interrupt(struct hsi_channel *ch);
+int hsi_driver_cancel_write_dma(struct hsi_channel *ch);
+int hsi_driver_cancel_read_dma(struct hsi_channel *ch);
+int hsi_do_cawake_process(struct hsi_port *pport);
+
+int hsi_driver_device_is_hsi(struct platform_device *dev);
+
+int hsi_mpu_init(struct hsi_port *hsi_p, const char *irq_name);
+void hsi_mpu_exit(struct hsi_port *hsi_p);
+
+int hsi_gdd_init(struct hsi_dev *hsi_ctrl, const char *irq_name);
+void hsi_gdd_exit(struct hsi_dev *hsi_ctrl);
+
+int hsi_cawake_init(struct hsi_port *port, const char *irq_name);
+void hsi_cawake_exit(struct hsi_port *port);
+
+int hsi_fifo_get_id(struct hsi_dev *hsi_ctrl, unsigned int channel,
+ unsigned int port);
+int hsi_fifo_get_chan(struct hsi_dev *hsi_ctrl, unsigned int fifo,
+ unsigned int *channel, unsigned int *port);
+int hsi_fifo_mapping(struct hsi_dev *hsi_ctrl, unsigned int mtype);
+long hsi_hst_bufstate_f_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel);
+long hsi_hsr_bufstate_f_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel);
+long hsi_hst_buffer_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel);
+long hsi_hsr_buffer_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel);
+u8 hsi_get_rx_fifo_occupancy(struct hsi_dev *hsi_ctrl, u8 fifo);
+void hsi_set_pm_force_hsi_on(struct hsi_dev *hsi_ctrl);
+void hsi_set_pm_default(struct hsi_dev *hsi_ctrl);
+int hsi_softreset(struct hsi_dev *hsi_ctrl);
+void hsi_softreset_driver(struct hsi_dev *hsi_ctrl);
+
+void hsi_clocks_disable_channel(struct device *dev, u8 channel_number,
+ const char *s);
+int hsi_clocks_enable_channel(struct device *dev, u8 channel_number,
+ const char *s);
+#ifdef CONFIG_PM_RUNTIME
+extern int hsi_runtime_resume(struct device *dev);
+extern int hsi_runtime_suspend(struct device *dev);
+#else
+static inline int hsi_runtime_resume(struct device *dev) { return -ENOSYS; }
+static inline int hsi_runtime_suspend(struct device *dev) { return -ENOSYS; }
+#endif
+void hsi_save_ctx(struct hsi_dev *hsi_ctrl);
+void hsi_restore_ctx(struct hsi_dev *hsi_ctrl);
+
+
+#ifdef CONFIG_DEBUG_FS
+int hsi_debug_init(void);
+void hsi_debug_exit(void);
+int hsi_debug_add_ctrl(struct hsi_dev *hsi_ctrl);
+void hsi_debug_remove_ctrl(struct hsi_dev *hsi_ctrl);
+#else
+#define hsi_debug_add_ctrl(hsi_ctrl) 0
+#define hsi_debug_remove_ctrl(hsi_ctrl)
+#define hsi_debug_init() 0
+#define hsi_debug_exit()
+#endif /* CONFIG_DEBUG_FS */
+
+static inline struct hsi_channel *hsi_ctrl_get_ch(struct hsi_dev *hsi_ctrl,
+ unsigned int port,
+ unsigned int channel)
+{
+ return &hsi_ctrl->hsi_port[port - 1].hsi_channel[channel];
+}
+
+/* HSI IO access */
+static inline u32 hsi_inl(void __iomem *base, u32 offset)
+{
+ return inl((unsigned int)base + offset);
+}
+
+static inline void hsi_outl(u32 data, void __iomem *base, u32 offset)
+{
+ outl(data, (unsigned int)base + offset);
+}
+
+static inline void hsi_outl_or(u32 data, void __iomem *base, u32 offset)
+{
+ u32 tmp = hsi_inl(base, offset);
+ hsi_outl((tmp | data), base, offset);
+}
+
+static inline void hsi_outl_and(u32 data, void __iomem *base, u32 offset)
+{
+ u32 tmp = hsi_inl(base, offset);
+ hsi_outl((tmp & data), base, offset);
+}
+
+static inline u16 hsi_inw(void __iomem *base, u32 offset)
+{
+ return inw((unsigned int)base + offset);
+}
+
+static inline void hsi_outw(u16 data, void __iomem *base, u32 offset)
+{
+ outw(data, (unsigned int)base + offset);
+}
+
+static inline void hsi_outw_or(u16 data, void __iomem *base, u32 offset)
+{
+ u16 tmp = hsi_inw(base, offset);
+ hsi_outw((tmp | data), base, offset);
+}
+
+static inline void hsi_outw_and(u16 data, void __iomem *base, u32 offset)
+{
+ u16 tmp = hsi_inw(base, offset);
+ hsi_outw((tmp & data), base, offset);
+}
+
+static inline int hsi_get_cawake(struct hsi_port *port)
+{
+ struct platform_device *pdev =
+ to_platform_device(port->hsi_controller->dev);
+
+ if (hsi_driver_device_is_hsi(pdev))
+ return (HSI_HSR_MODE_WAKE_STATUS ==
+ (hsi_inl(port->hsi_controller->base,
+ HSI_HSR_MODE_REG(port->port_number)) &
+ HSI_HSR_MODE_WAKE_STATUS));
+ else if (port->cawake_gpio >= 0)
+ return gpio_get_value(port->cawake_gpio);
+ else
+ return -ENXIO;
+}
+
+static inline void hsi_clocks_disable(struct device *dev, const char *s)
+{
+ hsi_clocks_disable_channel(dev, HSI_CH_NUMBER_NONE, s);
+}
+
+static inline int hsi_clocks_enable(struct device *dev, const char *s)
+{
+ return hsi_clocks_enable_channel(dev, HSI_CH_NUMBER_NONE, s);
+}
+
+#endif /* __HSI_DRIVER_H__ */
diff --git a/drivers/omap_hsi/hsi_driver_bus.c b/drivers/omap_hsi/hsi_driver_bus.c
new file mode 100644
index 0000000..4bce43d
--- /dev/null
+++ b/drivers/omap_hsi/hsi_driver_bus.c
@@ -0,0 +1,203 @@
+/*
+ * hsi_driver_bus.c
+ *
+ * Implements an HSI bus, device and driver interface.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/device.h>
+#include "hsi_driver.h"
+
+#define HSI_PREFIX "hsi:"
+
+struct bus_type hsi_bus_type;
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE + 1, "%s%s\n", HSI_PREFIX,
+ dev_name(dev));
+}
+
+static struct device_attribute hsi_dev_attrs[] = {
+ __ATTR_RO(modalias),
+ __ATTR_NULL,
+};
+
+static int hsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ add_uevent_var(env, "MODALIAS=%s%s", HSI_PREFIX, dev_name(dev));
+ return 0;
+}
+
+static int hsi_bus_match(struct device *device, struct device_driver *driver)
+{
+ struct hsi_device *dev = to_hsi_device(device);
+ struct hsi_device_driver *drv = to_hsi_device_driver(driver);
+
+ pr_debug("HSI DRIVER BUS : hsi_bus_match for ctrl:%d, port:%d, ch%d\n",
+ dev->n_ctrl, dev->n_p, dev->n_ch);
+
+ if (!test_bit(dev->n_ctrl, &drv->ctrl_mask))
+ return 0;
+
+ if (!test_bit(dev->n_ch, &drv->ch_mask[dev->n_p]))
+ return 0;
+
+ pr_info
+ ("HSI DRIVER BUS : hsi_bus_match SUCCESS : ctrl:%d (mask:%x),"
+ " port:%d, ch:%d (mask:%x)\n",
+ dev->n_ctrl, (u32) drv->ctrl_mask, dev->n_p, dev->n_ch,
+ (u32) drv->ch_mask[dev->n_p]);
+
+ return 1;
+}
+
+int hsi_bus_unreg_dev(struct device *device, void *p)
+{
+ device->release(device);
+ device_unregister(device);
+
+ return 0;
+}
+
+int __init hsi_bus_init(void)
+{
+ return bus_register(&hsi_bus_type);
+}
+
+void hsi_bus_exit(void)
+{
+ bus_for_each_dev(&hsi_bus_type, NULL, NULL, hsi_bus_unreg_dev);
+ bus_unregister(&hsi_bus_type);
+}
+
+static int hsi_bus_probe(struct device *dev)
+{
+ struct hsi_device_driver *drv;
+ int rc;
+
+ pr_debug("HSI DRIVER BUS : hsi_bus_probe\n");
+
+ if (!dev->driver)
+ return 0;
+
+ drv = to_hsi_device_driver(dev->driver);
+
+ if (!drv->probe)
+ return -ENODEV;
+
+ rc = drv->probe(to_hsi_device(dev));
+
+ return rc;
+}
+
+static int hsi_bus_remove(struct device *dev)
+{
+ struct hsi_device_driver *drv;
+ int ret;
+
+ pr_debug("HSI DRIVER BUS : hsi_bus_remove\n");
+
+ if (!dev->driver)
+ return 0;
+
+ drv = to_hsi_device_driver(dev->driver);
+ if (drv->remove) {
+ ret = drv->remove(to_hsi_device(dev));
+ } else {
+ dev->driver = NULL;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int hsi_bus_suspend(struct device *dev, pm_message_t mesg)
+{
+ struct hsi_device_driver *drv;
+
+ if (!dev->driver)
+ return 0;
+
+ drv = to_hsi_device_driver(dev->driver);
+ if (!drv->suspend)
+ return 0;
+
+ return drv->suspend(to_hsi_device(dev), mesg);
+}
+
+static int hsi_bus_resume(struct device *dev)
+{
+ struct hsi_device_driver *drv;
+
+ if (!dev->driver)
+ return 0;
+
+ drv = to_hsi_device_driver(dev->driver);
+ if (!drv->resume)
+ return 0;
+
+ return drv->resume(to_hsi_device(dev));
+}
+
+struct bus_type hsi_bus_type = {
+ .name = "hsi",
+ .dev_attrs = hsi_dev_attrs,
+ .match = hsi_bus_match,
+ .uevent = hsi_bus_uevent,
+ .probe = hsi_bus_probe,
+ .remove = hsi_bus_remove,
+ .suspend = hsi_bus_suspend,
+ .resume = hsi_bus_resume,
+};
+
+/**
+ * hsi_register_driver - Register HSI device driver
+ * @driver - reference to the HSI device driver.
+ */
+int hsi_register_driver(struct hsi_device_driver *driver)
+{
+ int ret = 0;
+
+ if (driver == NULL)
+ return -EINVAL;
+
+ driver->driver.bus = &hsi_bus_type;
+
+ ret = driver_register(&driver->driver);
+
+ if (ret == 0)
+ pr_debug("hsi: driver %s registered\n", driver->driver.name);
+
+ return ret;
+}
+EXPORT_SYMBOL(hsi_register_driver);
+
+/**
+ * hsi_unregister_driver - Unregister HSI device driver
+ * @driver - reference to the HSI device driver.
+ */
+void hsi_unregister_driver(struct hsi_device_driver *driver)
+{
+ if (driver == NULL)
+ return;
+
+ driver_unregister(&driver->driver);
+
+ pr_debug("hsi: driver %s unregistered\n", driver->driver.name);
+}
+EXPORT_SYMBOL(hsi_unregister_driver);
diff --git a/drivers/omap_hsi/hsi_driver_debugfs.c b/drivers/omap_hsi/hsi_driver_debugfs.c
new file mode 100644
index 0000000..d1f32dd
--- /dev/null
+++ b/drivers/omap_hsi/hsi_driver_debugfs.c
@@ -0,0 +1,500 @@
+/*
+ * hsi_driver_debugfs.c
+ *
+ * Implements HSI debugfs.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/err.h>
+#include <linux/list.h>
+#include <linux/uaccess.h>
+#include <linux/ctype.h>
+#include "hsi_driver.h"
+
+#define HSI_DIR_NAME_SIZE 64
+
+static struct dentry *hsi_dir;
+
+static int hsi_debug_show(struct seq_file *m, void *p)
+{
+ struct hsi_dev *hsi_ctrl = m->private;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+ hsi_clocks_enable(hsi_ctrl->dev, __func__);
+
+ seq_printf(m, "REVISION\t: 0x%08x\n",
+ hsi_inl(hsi_ctrl->base, HSI_SYS_REVISION_REG));
+ if (hsi_driver_device_is_hsi(pdev))
+ seq_printf(m, "HWINFO\t\t: 0x%08x\n",
+ hsi_inl(hsi_ctrl->base, HSI_SYS_HWINFO_REG));
+ seq_printf(m, "SYSCONFIG\t: 0x%08x\n",
+ hsi_inl(hsi_ctrl->base, HSI_SYS_SYSCONFIG_REG));
+ seq_printf(m, "SYSSTATUS\t: 0x%08x\n",
+ hsi_inl(hsi_ctrl->base, HSI_SYS_SYSSTATUS_REG));
+
+ hsi_clocks_disable(hsi_ctrl->dev, __func__);
+
+ return 0;
+}
+
+static int hsi_debug_port_show(struct seq_file *m, void *p)
+{
+ struct hsi_port *hsi_port = m->private;
+ struct hsi_dev *hsi_ctrl = hsi_port->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = hsi_port->port_number;
+ int ch, fifo;
+ long buff_offset;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+ hsi_clocks_enable(hsi_ctrl->dev, __func__);
+
+ if (hsi_port->cawake_gpio >= 0)
+ seq_printf(m, "CAWAKE\t\t: %d\n", hsi_get_cawake(hsi_port));
+
+ seq_printf(m, "WAKE\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_SYS_WAKE_REG(port)));
+ seq_printf(m, "MPU_ENABLE_IRQ%d\t: 0x%08x\n", hsi_port->n_irq,
+ hsi_inl(base,
+ HSI_SYS_MPU_ENABLE_REG(port, hsi_port->n_irq)));
+ seq_printf(m, "MPU_STATUS_IRQ%d\t: 0x%08x\n", hsi_port->n_irq,
+ hsi_inl(base,
+ HSI_SYS_MPU_STATUS_REG(port, hsi_port->n_irq)));
+ if (hsi_driver_device_is_hsi(pdev)) {
+ seq_printf(m, "MPU_U_ENABLE_IRQ%d\t: 0x%08x\n",
+ hsi_port->n_irq,
+ hsi_inl(base, HSI_SYS_MPU_U_ENABLE_REG(port,
+ hsi_port->n_irq)));
+ seq_printf(m, "MPU_U_STATUS_IRQ%d\t: 0x%08x\n", hsi_port->n_irq,
+ hsi_inl(base,
+ HSI_SYS_MPU_U_STATUS_REG(port,
+ hsi_port->n_irq)));
+ }
+ /* HST */
+ seq_printf(m, "\nHST\n===\n");
+ seq_printf(m, "MODE\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_MODE_REG(port)));
+ seq_printf(m, "FRAMESIZE\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_FRAMESIZE_REG(port)));
+ seq_printf(m, "DIVISOR\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_DIVISOR_REG(port)));
+ seq_printf(m, "CHANNELS\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_CHANNELS_REG(port)));
+ seq_printf(m, "ARBMODE\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_ARBMODE_REG(port)));
+ seq_printf(m, "TXSTATE\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_TXSTATE_REG(port)));
+ if (hsi_driver_device_is_hsi(pdev)) {
+ seq_printf(m, "BUFSTATE P1\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_BUFSTATE_REG(1)));
+ seq_printf(m, "BUFSTATE P2\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_BUFSTATE_REG(2)));
+ } else {
+ seq_printf(m, "BUFSTATE\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_BUFSTATE_REG(port)));
+ }
+ seq_printf(m, "BREAK\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HST_BREAK_REG(port)));
+ for (ch = 0; ch < 8; ch++) {
+ buff_offset = hsi_hst_buffer_reg(hsi_ctrl, port, ch);
+ if (buff_offset >= 0)
+ seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
+ hsi_inl(base, buff_offset));
+ }
+ if (hsi_driver_device_is_hsi(pdev)) {
+ for (fifo = 0; fifo < HSI_HST_FIFO_COUNT; fifo++) {
+ seq_printf(m, "FIFO MAPPING%d\t: 0x%08x\n", fifo,
+ hsi_inl(base,
+ HSI_HST_MAPPING_FIFO_REG(fifo)));
+ }
+ }
+ /* HSR */
+ seq_printf(m, "\nHSR\n===\n");
+ seq_printf(m, "MODE\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_MODE_REG(port)));
+ seq_printf(m, "FRAMESIZE\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_FRAMESIZE_REG(port)));
+ seq_printf(m, "CHANNELS\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_CHANNELS_REG(port)));
+ seq_printf(m, "COUNTERS\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_COUNTERS_REG(port)));
+ seq_printf(m, "RXSTATE\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_RXSTATE_REG(port)));
+ if (hsi_driver_device_is_hsi(pdev)) {
+ seq_printf(m, "BUFSTATE P1\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_BUFSTATE_REG(1)));
+ seq_printf(m, "BUFSTATE P2\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_BUFSTATE_REG(2)));
+ } else {
+ seq_printf(m, "BUFSTATE\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_BUFSTATE_REG(port)));
+ }
+ seq_printf(m, "BREAK\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_BREAK_REG(port)));
+ seq_printf(m, "ERROR\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_ERROR_REG(port)));
+ seq_printf(m, "ERRORACK\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_ERRORACK_REG(port)));
+ for (ch = 0; ch < 8; ch++) {
+ buff_offset = hsi_hsr_buffer_reg(hsi_ctrl, port, ch);
+ if (buff_offset >= 0)
+ seq_printf(m, "BUFFER_CH%d\t: 0x%08x\n", ch,
+ hsi_inl(base, buff_offset));
+ }
+ if (hsi_driver_device_is_hsi(pdev)) {
+ for (fifo = 0; fifo < HSI_HSR_FIFO_COUNT; fifo++) {
+ seq_printf(m, "FIFO MAPPING%d\t: 0x%08x\n", fifo,
+ hsi_inl(base,
+ HSI_HSR_MAPPING_FIFO_REG(fifo)));
+ }
+ seq_printf(m, "DLL\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_DLL_REG));
+ seq_printf(m, "DIVISOR\t: 0x%08x\n",
+ hsi_inl(base, HSI_HSR_DIVISOR_REG(port)));
+ }
+
+ hsi_clocks_disable(hsi_ctrl->dev, __func__);
+
+ return 0;
+}
+
+static int hsi_debug_gdd_show(struct seq_file *m, void *p)
+{
+ struct hsi_dev *hsi_ctrl = m->private;
+ void __iomem *base = hsi_ctrl->base;
+ int lch;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+ hsi_clocks_enable(hsi_ctrl->dev, __func__);
+
+ seq_printf(m, "GDD_MPU_STATUS\t: 0x%08x\n",
+ hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG));
+ seq_printf(m, "GDD_MPU_ENABLE\t: 0x%08x\n\n",
+ hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG));
+
+ if (!hsi_driver_device_is_hsi(pdev)) {
+ seq_printf(m, "HW_ID\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_SSI_GDD_HW_ID_REG));
+ seq_printf(m, "PPORT_ID\t: 0x%08x\n",
+ hsi_inl(base, HSI_SSI_GDD_PPORT_ID_REG));
+ seq_printf(m, "MPORT_ID\t: 0x%08x\n",
+ hsi_inl(base, HSI_SSI_GDD_MPORT_ID_REG));
+ seq_printf(m, "TEST\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_SSI_GDD_TEST_REG));
+ }
+
+ seq_printf(m, "GCR\t\t: 0x%08x\n", hsi_inl(base, HSI_GDD_GCR_REG));
+
+ for (lch = 0; lch < hsi_ctrl->gdd_chan_count; lch++) {
+ seq_printf(m, "\nGDD LCH %d\n=========\n", lch);
+ seq_printf(m, "CSDP\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CSDP_REG(lch)));
+ seq_printf(m, "CCR\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CCR_REG(lch)));
+ seq_printf(m, "CICR\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CCIR_REG(lch)));
+ seq_printf(m, "CSR\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CSR_REG(lch)));
+ seq_printf(m, "CSSA\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_GDD_CSSA_REG(lch)));
+ seq_printf(m, "CDSA\t\t: 0x%08x\n",
+ hsi_inl(base, HSI_GDD_CDSA_REG(lch)));
+ seq_printf(m, "CEN\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CEN_REG(lch)));
+ seq_printf(m, "CSAC\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CSAC_REG(lch)));
+ seq_printf(m, "CDAC\t\t: 0x%04x\n",
+ hsi_inw(base, HSI_GDD_CDAC_REG(lch)));
+ if (!hsi_driver_device_is_hsi(pdev))
+ seq_printf(m, "CLNK_CTRL\t: 0x%04x\n",
+ hsi_inw(base,
+ HSI_SSI_GDD_CLNK_CTRL_REG(lch)));
+ }
+
+ hsi_clocks_disable(hsi_ctrl->dev, __func__);
+
+ return 0;
+}
+
+static int hsi_port_counters_open(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+static int hsi_port_counters_release(struct inode *inode, struct file *file)
+{
+ return 0;
+}
+
+static loff_t hsi_port_counters_seek(struct file *file, loff_t off, int whence)
+{
+ return 0;
+}
+
+static ssize_t hsi_port_counters_read(struct file *filep, char __user * buff,
+ size_t count, loff_t *offp)
+{
+ ssize_t ret;
+ struct hsi_port *hsi_port = filep->private_data;
+ struct hsi_dev *hsi_ctrl = hsi_port->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = hsi_port->port_number;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+ char str[50];
+ unsigned long reg;
+
+ if (*offp > 0) {
+ ret = 0;
+ goto hsi_cnt_rd_bk;
+ }
+
+ hsi_clocks_enable(hsi_ctrl->dev, __func__);
+
+ reg = hsi_inl(base, HSI_HSR_COUNTERS_REG(port));
+
+ hsi_clocks_disable(hsi_ctrl->dev, __func__);
+
+ if (hsi_driver_device_is_hsi(pdev)) {
+ sprintf(str, "FT:%d, TB:%d, FB:%d\n",
+ (int)(reg & HSI_COUNTERS_FT_MASK) >>
+ HSI_COUNTERS_FT_OFFSET,
+ (int)(reg & HSI_COUNTERS_TB_MASK) >>
+ HSI_COUNTERS_TB_OFFSET,
+ (int)(reg & HSI_COUNTERS_FB_MASK) >>
+ HSI_COUNTERS_FB_OFFSET);
+ } else {
+ sprintf(str, "timeout:%d\n", (int)reg);
+ }
+
+ ret = strlen(str);
+ if (copy_to_user((void __user *)buff, str, ret)) {
+ dev_err(hsi_ctrl->dev, "copy_to_user failed\n");
+ ret = 0;
+ } else {
+ *offp = ret;
+ }
+
+hsi_cnt_rd_bk:
+ return ret;
+}
+
+/*
+ * Split the buffer `buf' into space-separated words.
+ * Return the number of words or <0 on error.
+ */
+static int hsi_debug_tokenize(char *buf, char *words[], int maxwords)
+{
+ int nwords = 0;
+
+ while (*buf) {
+ char *end;
+
+ /* Skip leading whitespace */
+ while (*buf && isspace(*buf))
+ buf++;
+ if (!*buf)
+ break; /* oh, it was trailing whitespace */
+
+ /* Run `end' over a word */
+ for (end = buf; *end && !isspace(*end); end++)
+ ;
+ /* `buf' is the start of the word, `end' is one past the end */
+
+ if (nwords == maxwords)
+ return -EINVAL; /* ran out of words[] before bytes */
+ if (*end)
+ *end++ = '\0'; /* terminate the word */
+ words[nwords++] = buf;
+ buf = end;
+ }
+ return nwords;
+}
+
+static ssize_t hsi_port_counters_write(struct file *filep,
+ const char __user *buff, size_t count,
+ loff_t *offp)
+{
+ ssize_t ret;
+ struct hsi_port *hsi_port = filep->private_data;
+ struct hsi_dev *hsi_ctrl = hsi_port->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = hsi_port->port_number;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+#define MAXWORDS 4
+ int nwords;
+ char *words[MAXWORDS];
+ char tmpbuf[256];
+ unsigned long reg, ft, tb, fb;
+
+ if (count == 0)
+ return 0;
+ if (count > sizeof(tmpbuf) - 1)
+ return -E2BIG;
+ if (copy_from_user(tmpbuf, buff, count))
+ return -EFAULT;
+ tmpbuf[count] = '\0';
+ dev_dbg(hsi_ctrl->dev, "%s: read %d bytes from userspace\n",
+ __func__, (int)count);
+
+ nwords = hsi_debug_tokenize(tmpbuf, words, MAXWORDS);
+ if (nwords < 0) {
+ dev_warn(hsi_ctrl->dev,
+ "HSI counters write usage: echo <values> > counters\n");
+ return -EINVAL;
+ }
+
+ hsi_clocks_enable(hsi_ctrl->dev, __func__);
+
+ if (hsi_driver_device_is_hsi(pdev)) {
+ if (nwords != 3) {
+ dev_warn(hsi_ctrl->dev, "HSI counters write usage: "
+ "echo \"FT TB FB\" > counters\n");
+ ret = -EINVAL;
+ goto hsi_cnt_w_bk1;
+ }
+ strict_strtoul(words[0], 0, &ft);
+ strict_strtoul(words[1], 0, &tb);
+ strict_strtoul(words[2], 0, &fb);
+ reg = ((ft << HSI_COUNTERS_FT_OFFSET & HSI_COUNTERS_FT_MASK) |
+ (tb << HSI_COUNTERS_TB_OFFSET & HSI_COUNTERS_TB_MASK) |
+ (fb << HSI_COUNTERS_FB_OFFSET & HSI_COUNTERS_FB_MASK));
+ } else {
+ if (nwords != 1) {
+ dev_warn(hsi_ctrl->dev, "HSI counters write usage: "
+ "echo \"timeout\" > counters\n");
+ ret = -EINVAL;
+ goto hsi_cnt_w_bk1;
+ }
+ strict_strtoul(words[0], 0, &reg);
+ }
+ hsi_outl(reg, base, HSI_HSR_COUNTERS_REG(port));
+ ret = count;
+ *offp += count;
+
+hsi_cnt_w_bk1:
+
+ hsi_clocks_disable(hsi_ctrl->dev, __func__);
+
+ return ret;
+}
+
+static int hsi_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hsi_debug_show, inode->i_private);
+}
+
+static int hsi_port_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hsi_debug_port_show, inode->i_private);
+}
+
+static int hsi_gdd_regs_open(struct inode *inode, struct file *file)
+{
+ return single_open(file, hsi_debug_gdd_show, inode->i_private);
+}
+
+static const struct file_operations hsi_regs_fops = {
+ .open = hsi_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations hsi_port_regs_fops = {
+ .open = hsi_port_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+static const struct file_operations hsi_port_counters_fops = {
+ .open = hsi_port_counters_open,
+ .read = hsi_port_counters_read,
+ .write = hsi_port_counters_write,
+ .llseek = hsi_port_counters_seek,
+ .release = hsi_port_counters_release,
+};
+
+static const struct file_operations hsi_gdd_regs_fops = {
+ .open = hsi_gdd_regs_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = single_release,
+};
+
+int __init hsi_debug_add_ctrl(struct hsi_dev *hsi_ctrl)
+{
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+ unsigned char dir_name[HSI_DIR_NAME_SIZE];
+ struct dentry *dir;
+ unsigned int port;
+
+ if (pdev->id < 0) {
+ hsi_ctrl->dir = debugfs_create_dir(pdev->name, hsi_dir);
+ } else {
+ snprintf(dir_name, sizeof(dir_name), "%s%d", pdev->name,
+ pdev->id);
+ hsi_ctrl->dir = debugfs_create_dir(dir_name, hsi_dir);
+ }
+ if (IS_ERR(hsi_ctrl->dir))
+ return PTR_ERR(hsi_ctrl->dir);
+
+ debugfs_create_file("regs", S_IRUGO, hsi_ctrl->dir, hsi_ctrl,
+ &hsi_regs_fops);
+
+ for (port = 0; port < hsi_ctrl->max_p; port++) {
+ snprintf(dir_name, sizeof(dir_name), "port%d", port + 1);
+ dir = debugfs_create_dir(dir_name, hsi_ctrl->dir);
+ if (IS_ERR(dir))
+ goto rback;
+ 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,
+ &hsi_ctrl->hsi_port[port],
+ &hsi_port_counters_fops);
+ }
+
+ dir = debugfs_create_dir("gdd", hsi_ctrl->dir);
+ if (IS_ERR(dir))
+ goto rback;
+ debugfs_create_file("regs", S_IRUGO, dir, hsi_ctrl, &hsi_gdd_regs_fops);
+
+ return 0;
+rback:
+ debugfs_remove_recursive(hsi_ctrl->dir);
+ return PTR_ERR(dir);
+}
+
+void hsi_debug_remove_ctrl(struct hsi_dev *hsi_ctrl)
+{
+ debugfs_remove_recursive(hsi_ctrl->dir);
+}
+
+int __init hsi_debug_init(void)
+{
+ hsi_dir = debugfs_create_dir("hsi", NULL);
+ if (IS_ERR(hsi_dir))
+ return PTR_ERR(hsi_dir);
+
+ return 0;
+}
+
+void hsi_debug_exit(void)
+{
+ debugfs_remove_recursive(hsi_dir);
+}
diff --git a/drivers/omap_hsi/hsi_driver_dma.c b/drivers/omap_hsi/hsi_driver_dma.c
new file mode 100644
index 0000000..ad819f5
--- /dev/null
+++ b/drivers/omap_hsi/hsi_driver_dma.c
@@ -0,0 +1,643 @@
+/*
+ * hsi_driver_dma.c
+ *
+ * Implements HSI low level interface driver functionality with DMA support.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/dma-mapping.h>
+#include "hsi_driver.h"
+
+#define HSI_SYNC_WRITE 0
+#define HSI_SYNC_READ 1
+#define HSI_L3_TPUT 13428 /* 13428 KiB/s => ~110 Mbit/s */
+
+static unsigned char hsi_sync_table[2][2][8] = {
+ {
+ {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08},
+ {0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00}
+ }, {
+ {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
+ {0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f}
+ }
+};
+
+/**
+ * hsi_is_dma_read_int_pending - Indicates if a DMA read interrupt is pending
+ * @hsi_ctrl - HSI controller of the GDD.
+ *
+ * Needs to be called holding the hsi_controller lock
+ *
+ * Returns true if DMA read interrupt is pending, else false
+ */
+bool hsi_is_dma_read_int_pending(struct hsi_dev *hsi_ctrl)
+{
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int gdd_lch = 0;
+ u32 status_reg = 0;
+ int i, j;
+ status_reg = hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+ status_reg &= hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ if (!status_reg)
+ return false;
+
+ /* Scan all enabled DMA channels */
+ for (gdd_lch = 0; gdd_lch < hsi_ctrl->gdd_chan_count; gdd_lch++) {
+ if (!(status_reg & HSI_GDD_LCH(gdd_lch)))
+ continue;
+ 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 == gdd_lch)
+ return true;
+ }
+ return false;
+}
+/**
+ * hsi_get_free_lch - Get a free GDD(DMA) logical channel
+ * @hsi_ctrl - HSI controller of the GDD.
+ *
+ * Needs to be called holding the hsi_controller lock
+ *
+ * Returns the logical channel number, or -EBUSY if none available
+ */
+static int hsi_get_free_lch(struct hsi_dev *hsi_ctrl)
+{
+ unsigned int enable_reg;
+ int i, lch;
+
+ enable_reg = hsi_inl(hsi_ctrl->base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ lch = hsi_ctrl->last_gdd_lch;
+ for (i = 0; i < hsi_ctrl->gdd_chan_count; i++) {
+ if (++lch >= hsi_ctrl->gdd_chan_count)
+ lch = 0;
+ if ((enable_reg & HSI_GDD_LCH(lch)) == 0) {
+ hsi_ctrl->last_gdd_lch = lch;
+ return lch;
+ }
+ }
+ return -EBUSY;
+}
+
+/**
+ * hsi_driver_write_dma - Program GDD [DMA] to write data from memory to
+ * the hsi channel buffer.
+ * @hsi_channel - pointer to the hsi_channel to write data to.
+ * @data - 32-bit word pointer to the data.
+ * @size - Number of 32bit words to be transfered.
+ *
+ * hsi_controller lock must be held before calling this function.
+ *
+ * Return 0 on success and < 0 on error.
+ */
+int hsi_driver_write_dma(struct hsi_channel *hsi_channel, u32 * data,
+ unsigned int size)
+{
+ struct hsi_dev *hsi_ctrl = hsi_channel->hsi_port->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = hsi_channel->hsi_port->port_number;
+ unsigned int channel = hsi_channel->channel_number;
+ unsigned int sync;
+ int lch;
+ dma_addr_t src_addr;
+ dma_addr_t dest_addr;
+ u16 tmp;
+ int fifo;
+
+ if ((size < 1) || (data == NULL))
+ return -EINVAL;
+
+ lch = hsi_get_free_lch(hsi_ctrl);
+ if (lch < 0) {
+ dev_err(hsi_ctrl->dev, "No free DMA channels.\n");
+ return -EBUSY; /* No free GDD logical channels. */
+ } else {
+ dev_dbg(hsi_ctrl->dev, "Allocated DMA channel %d for write on"
+ " HSI channel %d.\n", lch,
+ hsi_channel->channel_number);
+ }
+
+ /* NOTE: Getting a free gdd logical channel and
+ * reserve it must be done atomicaly. */
+ hsi_channel->write_data.lch = lch;
+
+ /* Sync is required for SSI but not for HSI */
+ sync = hsi_sync_table[HSI_SYNC_WRITE][port - 1][channel];
+
+ src_addr = dma_map_single(hsi_ctrl->dev, data, size * 4, DMA_TO_DEVICE);
+ if (unlikely(dma_mapping_error(hsi_ctrl->dev, src_addr))) {
+ dev_err(hsi_ctrl->dev, "Failed to create DMA write mapping.\n");
+ return -ENOMEM;
+ }
+
+ tmp = HSI_SRC_SINGLE_ACCESS0 |
+ HSI_SRC_MEMORY_PORT |
+ HSI_DST_SINGLE_ACCESS0 |
+ HSI_DST_PERIPHERAL_PORT | HSI_DATA_TYPE_S32;
+ hsi_outw(tmp, base, HSI_GDD_CSDP_REG(lch));
+
+ tmp = HSI_SRC_AMODE_POSTINC | HSI_DST_AMODE_CONST | sync;
+ hsi_outw(tmp, base, HSI_GDD_CCR_REG(lch));
+
+ hsi_outw((HSI_BLOCK_IE | HSI_TOUT_IE), base, HSI_GDD_CCIR_REG(lch));
+
+ if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
+ fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
+ if (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev, "No valid FIFO id for DMA "
+ "transfer to FIFO.\n");
+ return -EFAULT;
+ }
+ /* HSI CDSA register takes a FIFO ID when copying to FIFO */
+ hsi_outl(fifo, base, HSI_GDD_CDSA_REG(lch));
+ } else {
+ dest_addr = hsi_ctrl->phy_base + HSI_HST_BUFFER_CH_REG(port,
+ channel);
+ /* SSI CDSA register always takes a 32-bit address */
+ hsi_outl(dest_addr, base, HSI_GDD_CDSA_REG(lch));
+ }
+
+ /* HSI CSSA register takes a 32-bit address when copying from memory */
+ /* SSI CSSA register always takes a 32-bit address */
+ hsi_outl(src_addr, base, HSI_GDD_CSSA_REG(lch));
+ hsi_outw(size, base, HSI_GDD_CEN_REG(lch));
+
+ /* TODO : Need to clean interrupt status here to avoid spurious int */
+
+ hsi_outl_or(HSI_GDD_LCH(lch), base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ hsi_outw_or(HSI_CCR_ENABLE, base, HSI_GDD_CCR_REG(lch));
+
+ return 0;
+}
+
+/**
+ * hsi_driver_read_dma - Program GDD [DMA] to write data to memory from
+ * the hsi channel buffer.
+ * @hsi_channel - pointer to the hsi_channel to read data from.
+ * @data - 32-bit word pointer where to store the incoming data.
+ * @size - Number of 32bit words to be transfered to the buffer.
+ *
+ * hsi_controller lock must be held before calling this function.
+ *
+ * Return 0 on success and < 0 on error.
+ */
+int hsi_driver_read_dma(struct hsi_channel *hsi_channel, u32 * data,
+ unsigned int count)
+{
+ struct hsi_dev *hsi_ctrl = hsi_channel->hsi_port->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = hsi_channel->hsi_port->port_number;
+ unsigned int channel = hsi_channel->channel_number;
+ unsigned int sync;
+ int lch;
+ dma_addr_t src_addr;
+ dma_addr_t dest_addr;
+ u16 tmp;
+ int fifo;
+
+ lch = hsi_get_free_lch(hsi_ctrl);
+ if (lch < 0) {
+ dev_err(hsi_ctrl->dev, "No free DMA channels.\n");
+ return -EBUSY; /* No free GDD logical channels. */
+ } else {
+ dev_dbg(hsi_ctrl->dev, "Allocated DMA channel %d for read on"
+ " HSI channel %d.\n", lch,
+ hsi_channel->channel_number);
+ }
+
+ /* When DMA is used for Rx, disable the Rx Interrupt.
+ * (else DATAAVAILLABLE event would get triggered on first
+ * received data word)
+ * (Rx interrupt might be active for polling feature)
+ */
+#if 0
+ if (omap_readl(0x4A05A810)) {
+ dev_err(hsi_ctrl->dev,
+ "READ INTERRUPT IS PENDING DMA() but still disabling %0x\n",
+ omap_readl(0x4A05A810));
+ }
+#endif
+ hsi_driver_disable_read_interrupt(hsi_channel);
+
+ /*
+ * NOTE: Gettting a free gdd logical channel and
+ * reserve it must be done atomicaly.
+ */
+ hsi_channel->read_data.lch = lch;
+
+ /* Sync is required for SSI but not for HSI */
+ sync = hsi_sync_table[HSI_SYNC_READ][port - 1][channel];
+
+ dest_addr = dma_map_single(hsi_ctrl->dev, data, count * 4,
+ DMA_FROM_DEVICE);
+ if (unlikely(dma_mapping_error(hsi_ctrl->dev, dest_addr))) {
+ dev_err(hsi_ctrl->dev, "Failed to create DMA read mapping.\n");
+ return -ENOMEM;
+ }
+
+ tmp = HSI_DST_SINGLE_ACCESS0 |
+ HSI_DST_MEMORY_PORT |
+ HSI_SRC_SINGLE_ACCESS0 |
+ HSI_SRC_PERIPHERAL_PORT | HSI_DATA_TYPE_S32;
+ hsi_outw(tmp, base, HSI_GDD_CSDP_REG(lch));
+
+ tmp = HSI_DST_AMODE_POSTINC | HSI_SRC_AMODE_CONST | sync;
+ hsi_outw(tmp, base, HSI_GDD_CCR_REG(lch));
+
+ hsi_outw((HSI_BLOCK_IE | HSI_TOUT_IE), base, HSI_GDD_CCIR_REG(lch));
+
+ if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
+ fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
+ if (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev, "No valid FIFO id for DMA "
+ "transfer from FIFO.\n");
+ return -EFAULT;
+ }
+ /* HSI CSSA register takes a FIFO ID when copying from FIFO */
+ hsi_outl(fifo, base, HSI_GDD_CSSA_REG(lch));
+ } else{
+ src_addr = hsi_ctrl->phy_base + HSI_HSR_BUFFER_CH_REG(port,
+ channel);
+ /* SSI CSSA register always takes a 32-bit address */
+ hsi_outl(src_addr, base, HSI_GDD_CSSA_REG(lch));
+ }
+
+ /* HSI CDSA register takes a 32-bit address when copying to memory */
+ /* SSI CDSA register always takes a 32-bit address */
+ hsi_outl(dest_addr, base, HSI_GDD_CDSA_REG(lch));
+ hsi_outw(count, base, HSI_GDD_CEN_REG(lch));
+
+ /* TODO : Need to clean interrupt status here to avoid spurious int */
+
+ hsi_outl_or(HSI_GDD_LCH(lch), base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ hsi_outw_or(HSI_CCR_ENABLE, base, HSI_GDD_CCR_REG(lch));
+
+ return 0;
+}
+
+/**
+ * hsi_driver_cancel_write_dma - Cancel an ongoing GDD [DMA] write for the
+ * specified hsi channel.
+ * @hsi_ch - pointer to the hsi_channel to cancel DMA write.
+ *
+ * hsi_controller lock must be held before calling this function.
+ *
+ * Return: -ENXIO : No DMA channel found for specified HSI channel
+ * -ECANCELED : DMA cancel success, data not transfered to TX FIFO
+ * 0 : DMA transfer is already over, data already transfered to TX FIFO
+ *
+ * Note: whatever returned value, write callback will not be called after
+ * write cancel.
+ */
+int hsi_driver_cancel_write_dma(struct hsi_channel *hsi_ch)
+{
+ int lch = hsi_ch->write_data.lch;
+ unsigned int port = hsi_ch->hsi_port->port_number;
+ unsigned int channel = hsi_ch->channel_number;
+ struct hsi_dev *hsi_ctrl = hsi_ch->hsi_port->hsi_controller;
+ u16 ccr, gdd_csr;
+ long buff_offset;
+ u32 status_reg;
+ dma_addr_t dma_h;
+ size_t size;
+
+ dev_err(&hsi_ch->dev->device, "hsi_driver_cancel_write_dma( "
+ "channel %d\n", hsi_ch->channel_number);
+
+ if (lch < 0) {
+ dev_err(&hsi_ch->dev->device, "No DMA channel found for HSI "
+ "channel %d\n", hsi_ch->channel_number);
+ return -ENXIO;
+ }
+ ccr = hsi_inw(hsi_ctrl->base, HSI_GDD_CCR_REG(lch));
+ if (!(ccr & HSI_CCR_ENABLE)) {
+ dev_dbg(&hsi_ch->dev->device, "Write cancel on not "
+ "enabled logical channel %d CCR REG 0x%04X\n",
+ lch, ccr);
+ }
+ status_reg = hsi_inl(hsi_ctrl->base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+ status_reg &= hsi_inl(hsi_ctrl->base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ hsi_outw_and(~HSI_CCR_ENABLE, hsi_ctrl->base, HSI_GDD_CCR_REG(lch));
+
+ /* Clear CSR register by reading it, as it is cleared automaticaly */
+ /* by HW after SW read. */
+ gdd_csr = hsi_inw(hsi_ctrl->base, HSI_GDD_CSR_REG(lch));
+ hsi_outl_and(~HSI_GDD_LCH(lch), hsi_ctrl->base,
+ HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ hsi_outl(HSI_GDD_LCH(lch), hsi_ctrl->base,
+ HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+
+ /* Unmap DMA region */
+ dma_h = hsi_inl(hsi_ctrl->base, HSI_GDD_CSSA_REG(lch));
+ size = hsi_inw(hsi_ctrl->base, HSI_GDD_CEN_REG(lch)) * 4;
+ dma_unmap_single(hsi_ctrl->dev, dma_h, size, DMA_TO_DEVICE);
+
+ buff_offset = hsi_hst_bufstate_f_reg(hsi_ctrl, port, channel);
+ if (buff_offset >= 0)
+ hsi_outl_and(~HSI_BUFSTATE_CHANNEL(channel), hsi_ctrl->base,
+ buff_offset);
+
+ hsi_reset_ch_write(hsi_ch);
+ return status_reg & HSI_GDD_LCH(lch) ? 0 : -ECANCELED;
+}
+
+/**
+ * hsi_driver_cancel_read_dma - Cancel an ongoing GDD [DMA] read for the
+ * specified hsi channel.
+ * @hsi_ch - pointer to the hsi_channel to cancel DMA read.
+ *
+ * hsi_controller lock must be held before calling this function.
+ *
+ * Return: -ENXIO : No DMA channel found for specified HSI channel
+ * -ECANCELED : DMA cancel success, data not available at expected
+ * address.
+ * 0 : DMA transfer is already over, data already available at
+ * expected address.
+ *
+ * Note: whatever returned value, read callback will not be called after cancel.
+ */
+int hsi_driver_cancel_read_dma(struct hsi_channel *hsi_ch)
+{
+ int lch = hsi_ch->read_data.lch;
+ struct hsi_dev *hsi_ctrl = hsi_ch->hsi_port->hsi_controller;
+ u16 ccr, gdd_csr;
+ u32 status_reg;
+ dma_addr_t dma_h;
+ size_t size;
+
+ dev_err(&hsi_ch->dev->device, "hsi_driver_cancel_read_dma "
+ "channel %d\n", hsi_ch->channel_number);
+
+ /* Re-enable interrupts for polling if needed */
+ if (hsi_ch->flags & HSI_CH_RX_POLL)
+ hsi_driver_enable_read_interrupt(hsi_ch, NULL);
+
+ if (lch < 0) {
+ dev_err(&hsi_ch->dev->device, "No DMA channel found for HSI "
+ "channel %d\n", hsi_ch->channel_number);
+ return -ENXIO;
+ }
+
+ ccr = hsi_inw(hsi_ctrl->base, HSI_GDD_CCR_REG(lch));
+ if (!(ccr & HSI_CCR_ENABLE)) {
+ dev_dbg(&hsi_ch->dev->device, "Read cancel on not "
+ "enabled logical channel %d CCR REG 0x%04X\n",
+ lch, ccr);
+ }
+
+ status_reg = hsi_inl(hsi_ctrl->base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+ status_reg &= hsi_inl(hsi_ctrl->base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ hsi_outw_and(~HSI_CCR_ENABLE, hsi_ctrl->base, HSI_GDD_CCR_REG(lch));
+
+ /* Clear CSR register by reading it, as it is cleared automaticaly */
+ /* by HW after SW read */
+ gdd_csr = hsi_inw(hsi_ctrl->base, HSI_GDD_CSR_REG(lch));
+ hsi_outl_and(~HSI_GDD_LCH(lch), hsi_ctrl->base,
+ HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ hsi_outl(HSI_GDD_LCH(lch), hsi_ctrl->base,
+ HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+
+ /* Unmap DMA region - Access to the buffer is now safe */
+ dma_h = hsi_inl(hsi_ctrl->base, HSI_GDD_CDSA_REG(lch));
+ size = hsi_inw(hsi_ctrl->base, HSI_GDD_CEN_REG(lch)) * 4;
+ dma_unmap_single(hsi_ctrl->dev, dma_h, size, DMA_FROM_DEVICE);
+
+ hsi_reset_ch_read(hsi_ch);
+ return status_reg & HSI_GDD_LCH(lch) ? 0 : -ECANCELED;
+}
+
+/**
+ * hsi_get_info_from_gdd_lch - Retrieve channels information from DMA channel
+ * @hsi_ctrl - HSI device control structure
+ * @lch - DMA logical channel
+ * @port - HSI port
+ * @channel - HSI channel
+ * @is_read_path - channel is used for reading
+ *
+ * Updates the port, channel and is_read_path parameters depending on the
+ * lch DMA channel status.
+ *
+ * Return 0 on success and < 0 on error.
+ */
+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 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) {
+ *is_read_path = 1;
+ *port = i_ports + 1;
+ *channel = i_chans;
+ err = 0;
+ goto get_info_bk;
+ } else if (hsi_ctrl->hsi_port[i_ports].
+ hsi_channel[i_chans].write_data.lch == lch) {
+ *is_read_path = 0;
+ *port = i_ports + 1;
+ *channel = i_chans;
+ err = 0;
+ goto get_info_bk;
+ }
+get_info_bk:
+ return err;
+}
+
+static void do_hsi_gdd_lch(struct hsi_dev *hsi_ctrl, unsigned int gdd_lch)
+{
+ void __iomem *base = hsi_ctrl->base;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+ struct hsi_channel *ch;
+ unsigned int port;
+ unsigned int channel;
+ unsigned int is_read_path;
+ u32 gdd_csr;
+ dma_addr_t dma_h;
+ size_t size;
+ int fifo, fifo_words_avail;
+
+ if (hsi_get_info_from_gdd_lch(hsi_ctrl, gdd_lch, &port, &channel,
+ &is_read_path) < 0) {
+ dev_err(hsi_ctrl->dev, "Unable to match the DMA channel %d with"
+ " an HSI channel\n", gdd_lch);
+ return;
+ } else {
+ dev_dbg(hsi_ctrl->dev, "DMA event on gdd_lch=%d => port=%d, "
+ "channel=%d, read=%d\n", gdd_lch, port, channel,
+ is_read_path);
+ }
+
+ hsi_outl_and(~HSI_GDD_LCH(gdd_lch), base,
+ HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ /* Warning : CSR register is cleared automaticaly by HW after SW read */
+ gdd_csr = hsi_inw(base, HSI_GDD_CSR_REG(gdd_lch));
+
+ if (!(gdd_csr & HSI_CSR_TOUT)) {
+ if (is_read_path) { /* Read path */
+ dma_h = hsi_inl(base, HSI_GDD_CDSA_REG(gdd_lch));
+ size = hsi_inw(base, HSI_GDD_CEN_REG(gdd_lch)) * 4;
+ dma_sync_single_for_cpu(hsi_ctrl->dev, dma_h, size,
+ DMA_FROM_DEVICE);
+ dma_unmap_single(hsi_ctrl->dev, dma_h, size,
+ DMA_FROM_DEVICE);
+ ch = hsi_ctrl_get_ch(hsi_ctrl, port, channel);
+ hsi_reset_ch_read(ch);
+
+ dev_dbg(hsi_ctrl->dev, "Calling ch %d read callback "
+ "(size %d).\n", channel, size/4);
+ spin_unlock(&hsi_ctrl->lock);
+ ch->read_done(ch->dev, size / 4);
+ spin_lock(&hsi_ctrl->lock);
+
+ /* Check if FIFO is correctly emptied */
+ if (hsi_driver_device_is_hsi(pdev)) {
+ fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
+ if (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev, "No valid FIFO "
+ "id found for channel %d.\n",
+ channel);
+ return;
+ }
+ fifo_words_avail =
+ hsi_get_rx_fifo_occupancy(hsi_ctrl,
+ fifo);
+ if (fifo_words_avail)
+ dev_dbg(hsi_ctrl->dev,
+ "WARNING: FIFO %d not empty "
+ "after DMA copy, remaining "
+ "%d/%d frames\n",
+ fifo, fifo_words_avail,
+ HSI_HSR_FIFO_SIZE);
+ }
+ /* Re-enable interrupts for polling if needed */
+ if (ch->flags & HSI_CH_RX_POLL)
+ hsi_driver_enable_read_interrupt(ch, NULL);
+ } else { /* Write path */
+ dma_h = hsi_inl(base, HSI_GDD_CSSA_REG(gdd_lch));
+ size = hsi_inw(base, HSI_GDD_CEN_REG(gdd_lch)) * 4;
+ dma_unmap_single(hsi_ctrl->dev, dma_h, size,
+ DMA_TO_DEVICE);
+ ch = hsi_ctrl_get_ch(hsi_ctrl, port, channel);
+ hsi_reset_ch_write(ch);
+
+ dev_dbg(hsi_ctrl->dev, "Calling ch %d write callback "
+ "(size %d).\n", channel, size/4);
+ spin_unlock(&hsi_ctrl->lock);
+ ch->write_done(ch->dev, size / 4);
+ spin_lock(&hsi_ctrl->lock);
+ }
+ } else {
+ dev_err(hsi_ctrl->dev, "Time-out overflow Error on GDD transfer"
+ " on gdd channel %d\n", gdd_lch);
+ spin_unlock(&hsi_ctrl->lock);
+ /* TODO : need to perform a DMA soft reset */
+ hsi_port_event_handler(&hsi_ctrl->hsi_port[port - 1],
+ HSI_EVENT_ERROR, NULL);
+ spin_lock(&hsi_ctrl->lock);
+ }
+}
+
+static u32 hsi_process_dma_event(struct hsi_dev *hsi_ctrl)
+{
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int gdd_lch = 0;
+ u32 status_reg = 0;
+ u32 lch_served = 0;
+ unsigned int gdd_max_count = hsi_ctrl->gdd_chan_count;
+
+ status_reg = hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+ status_reg &= hsi_inl(base, HSI_SYS_GDD_MPU_IRQ_ENABLE_REG);
+ if (!status_reg) {
+ dev_dbg(hsi_ctrl->dev, "DMA : no event, exit.\n");
+ return 0;
+ }
+
+ for (gdd_lch = 0; gdd_lch < gdd_max_count; gdd_lch++) {
+ if (status_reg & HSI_GDD_LCH(gdd_lch)) {
+ do_hsi_gdd_lch(hsi_ctrl, gdd_lch);
+ lch_served |= HSI_GDD_LCH(gdd_lch);
+ }
+ }
+
+ /* Acknowledge interrupt for DMA channel */
+ hsi_outl(lch_served, base, HSI_SYS_GDD_MPU_IRQ_STATUS_REG);
+
+
+ return status_reg;
+}
+
+static void do_hsi_gdd_tasklet(unsigned long device)
+{
+ struct hsi_dev *hsi_ctrl = (struct hsi_dev *)device;
+
+ dev_dbg(hsi_ctrl->dev, "DMA Tasklet : clock_enabled=%d\n",
+ hsi_ctrl->clock_enabled);
+
+ spin_lock(&hsi_ctrl->lock);
+ hsi_clocks_enable(hsi_ctrl->dev, __func__);
+ hsi_ctrl->in_dma_tasklet = true;
+
+ hsi_process_dma_event(hsi_ctrl);
+
+ hsi_ctrl->in_dma_tasklet = false;
+ hsi_clocks_disable(hsi_ctrl->dev, __func__);
+ spin_unlock(&hsi_ctrl->lock);
+
+ enable_irq(hsi_ctrl->gdd_irq);
+}
+
+static irqreturn_t hsi_gdd_mpu_handler(int irq, void *p)
+{
+ struct hsi_dev *hsi_ctrl = p;
+
+ tasklet_hi_schedule(&hsi_ctrl->hsi_gdd_tasklet);
+
+ /* Disable interrupt until Bottom Half has cleared the IRQ status */
+ /* register */
+ disable_irq_nosync(hsi_ctrl->gdd_irq);
+
+ return IRQ_HANDLED;
+}
+
+int __init hsi_gdd_init(struct hsi_dev *hsi_ctrl, const char *irq_name)
+{
+ tasklet_init(&hsi_ctrl->hsi_gdd_tasklet, do_hsi_gdd_tasklet,
+ (unsigned long)hsi_ctrl);
+
+ dev_info(hsi_ctrl->dev, "Registering IRQ %s (%d)\n",
+ irq_name, hsi_ctrl->gdd_irq);
+
+ if (request_irq(hsi_ctrl->gdd_irq, hsi_gdd_mpu_handler,
+ IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH,
+ irq_name, hsi_ctrl) < 0) {
+ dev_err(hsi_ctrl->dev, "FAILED to request GDD IRQ %d\n",
+ hsi_ctrl->gdd_irq);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+void hsi_gdd_exit(struct hsi_dev *hsi_ctrl)
+{
+ tasklet_kill(&hsi_ctrl->hsi_gdd_tasklet);
+ free_irq(hsi_ctrl->gdd_irq, hsi_ctrl);
+}
diff --git a/drivers/omap_hsi/hsi_driver_fifo.c b/drivers/omap_hsi/hsi_driver_fifo.c
new file mode 100644
index 0000000..aa33a1a
--- /dev/null
+++ b/drivers/omap_hsi/hsi_driver_fifo.c
@@ -0,0 +1,325 @@
+/*
+ * hsi_driver_fifo.c
+ *
+ * Implements HSI module fifo management.
+ *
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/list.h>
+#include "hsi_driver.h"
+
+/**
+ * hsi_fifo_get_id - Get fifo index corresponding to (port, channel)
+ * @hsi_ctrl - HSI controler data
+ * @channel - channel used
+ * @port - HSI port used
+ *
+ * Returns the fifo index associated to the provided (port, channel).
+ * Notes: 1) The fifo <=> (port, channel) correspondance depends on the selected
+ * SW strategy for channels mapping (fifo management).
+ * 2) the mapping is identical for Read and Write path.
+ * This exclusively applies to HSI devices.
+ */
+int hsi_fifo_get_id(struct hsi_dev *hsi_ctrl, unsigned int channel,
+ unsigned int port)
+{
+ int fifo_index = 0;
+ int err = 0;
+
+ if (unlikely((channel >= HSI_CHANNELS_MAX) || (port < 1) ||
+ (port > 2))) {
+ err = -EINVAL;
+ goto fifo_id_bk;
+ }
+
+ if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_ALL_PORT1) {
+ if (unlikely(port != 1)) {
+ err = -EINVAL;
+ goto fifo_id_bk;
+ } else {
+ fifo_index = channel;
+ }
+ } else if (hsi_ctrl->fifo_mapping_strategy == HSI_FIFO_MAPPING_SSI) {
+ if (unlikely(channel >= 8)) {
+ err = -EINVAL;
+ goto fifo_id_bk;
+ } else {
+ fifo_index = channel + 8 * (port - 1);
+ }
+ } else {
+ err = -EPERM;
+ goto fifo_id_bk;
+ }
+
+fifo_id_bk:
+ if (unlikely(err < 0)) {
+ fifo_index = err;
+ dev_err(hsi_ctrl->dev, "Cannot map a FIFO to the requested "
+ "params: channel:%d, port:%d; ERR=%d\n", channel, port,
+ err);
+ }
+
+ return fifo_index;
+}
+
+/**
+ * hsi_fifo_get_chan - Get (port, channel) from a fifo index
+ * @hsi_ctrl - HSI controler data
+ * @fifo - HSI fifo used (0..HSI_HST_FIFO_COUNT)
+ * @channel - related channel if any (0..)
+ * @port - related port if any (1..2)
+ *
+ * Returns 0 in case of success, and errocode (< 0) else
+ * Notes: 1) The fifo <=> (port, channel) correspondance depends on the selected
+ * SW strategy for channels mapping (fifo management).
+ * 2) the mapping is identical for Read and Write path.
+ * This exclusively applies to HSI devices.
+ */
+int hsi_fifo_get_chan(struct hsi_dev *hsi_ctrl, unsigned int fifo,
+ unsigned int *channel, unsigned int *port)
+{
+ int err = 0;
+
+ if (unlikely(fifo >= HSI_HST_FIFO_COUNT)) {
+ err = -EINVAL;
+ goto fifo_id_bk;
+ }
+
+ 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_SSI) {
+ if (fifo < 8) {
+ *channel = fifo;
+ *port = 1;
+ } else {
+ *channel = fifo - 8;
+ *port = 2;
+ }
+ } else {
+ err = -EPERM;
+ goto fifo_id_bk;
+ }
+
+fifo_id_bk:
+ if (unlikely(err < 0))
+ dev_err(hsi_ctrl->dev, "Cannot map a channel / port to the "
+ "requested params: fifo:%d; ERR=%d\n", fifo, err);
+
+ return err;
+}
+
+/**
+ * hsi_fifo_mapping - Configures the HSI FIFO mapping registers.
+ * @hsi_ctrl - HSI controler data
+ * @mtype - mapping strategy
+ *
+ * Returns 0 in case of success, and errocode (< 0) else
+ * Configures the HSI FIFO mapping registers. Several mapping strategies are
+ * proposed.
+ * Note: The mapping is identical for Read and Write path.
+ * This exclusively applies to HSI devices.
+ */
+int hsi_fifo_mapping(struct hsi_dev *hsi_ctrl, unsigned int mtype)
+{
+ int err = 0;
+ void __iomem *base = hsi_ctrl->base;
+ int i;
+ unsigned int channel, port;
+
+ if (mtype == HSI_FIFO_MAPPING_ALL_PORT1) {
+ 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) |
+ 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),
+ 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;
+ } else if (mtype == HSI_FIFO_MAPPING_SSI) {
+ channel = 0;
+ port = 0;
+ for (i = 0; i < HSI_HST_FIFO_COUNT; i++) {
+ hsi_outl(HSI_MAPPING_ENABLE |
+ (channel << HSI_MAPPING_CH_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) |
+ (port << HSI_MAPPING_PORT_NUMBER_OFFSET),
+ base, HSI_HSR_MAPPING_FIFO_REG(i));
+ channel++;
+ if (channel == 8) {
+ channel = 0;
+ port = 1;
+ }
+ }
+
+ 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;
+ }
+
+ return err;
+}
+
+/**
+ * hsi_hst_bufstate_f_reg - Return the proper HSI_HST_BUFSTATE register offset
+ * @hsi_ctrl - HSI controler data
+ * @port - HSI port used
+ * @channel - channel used
+ *
+ * Returns the HSI_HST_BUFSTATE register offset
+ * Note: indexing of BUFSTATE registers is different on SSI and HSI:
+ * On SSI: it is linked to the ports
+ * On HSI: it is linked to the FIFOs (and depend on the SW strategy)
+ */
+long hsi_hst_bufstate_f_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel)
+{
+ int fifo;
+ if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
+ fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
+ if (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev,
+ "hsi_hst_bufstate_f_reg ERROR : %d\n", fifo);
+ return fifo;
+ } else
+ return HSI_HST_BUFSTATE_FIFO_REG(fifo);
+ } else {
+ return HSI_HST_BUFSTATE_REG(port);
+ }
+}
+
+/**
+ * hsi_hsr_bufstate_f_reg - Return the proper HSI_HSR_BUFSTATE register offset
+ * @hsi_ctrl - HSI controler data
+ * @port - HSI port used
+ * @channel - channel used
+ *
+ * Returns the HSI_HSR_BUFSTATE register offset
+ * Note: indexing of BUFSTATE registers is different on SSI and HSI:
+ * On SSI: it is linked to the ports
+ * On HSI: it is linked to the FIFOs (and depend on the SW strategy)
+ */
+long hsi_hsr_bufstate_f_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel)
+{
+ int fifo;
+ if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
+ fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
+ if (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev,
+ "hsi_hsr_bufstate_f_reg ERROR : %d\n", fifo);
+ return fifo;
+ } else
+ return HSI_HSR_BUFSTATE_FIFO_REG(fifo);
+ } else {
+ return HSI_HSR_BUFSTATE_REG(port);
+ }
+}
+
+/**
+ * hsi_hst_buffer_f_reg - Return the proper HSI_HST_BUFFER register offset
+ * @hsi_ctrl - HSI controler data
+ * @port - HSI port used
+ * @channel - channel used
+ *
+ * Returns the HSI_HST_BUFFER register offset
+ * Note: indexing of BUFFER registers is different on SSI and HSI:
+ * On SSI: it is linked to the ports
+ * On HSI: it is linked to the FIFOs (and depend on the SW strategy)
+ */
+long hsi_hst_buffer_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel)
+{
+ int fifo;
+ if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
+ fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
+ if (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev,
+ "hsi_hst_bufstate_f_reg ERROR : %d\n", fifo);
+ return fifo;
+ } else
+ return HSI_HST_BUFFER_FIFO_REG(fifo);
+ } else {
+ return HSI_HST_BUFFER_CH_REG(port, channel);
+ }
+}
+
+/**
+ * hsi_hsr_buffer_f_reg - Return the proper HSI_HSR_BUFFER register offset
+ * @hsi_ctrl - HSI controler data
+ * @port - HSI port used
+ * @channel - channel used
+ *
+ * Returns the HSI_HSR_BUFFER register offset
+ * Note: indexing of BUFFER registers is different on SSI and HSI:
+ * On SSI: it is linked to the ports
+ * On HSI: it is linked to the FIFOs (and depend on the SW strategy)
+ */
+long hsi_hsr_buffer_reg(struct hsi_dev *hsi_ctrl,
+ unsigned int port, unsigned int channel)
+{
+ int fifo;
+ if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
+ fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
+ if (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev,
+ "hsi_hsr_bufstate_f_reg ERROR : %d\n", fifo);
+ return fifo;
+ } else
+ return HSI_HSR_BUFFER_FIFO_REG(fifo);
+ } else {
+ return HSI_HSR_BUFFER_CH_REG(port, channel);
+ }
+}
+
+/**
+ * hsi_get_rx_fifo_occupancy - Return the size of data remaining
+ * in the given FIFO
+ * @hsi_ctrl - HSI controler data
+ * @fifo - FIFO to look at
+ *
+ * Returns the number of frames (32bits) remaining in the FIFO
+ */
+u8 hsi_get_rx_fifo_occupancy(struct hsi_dev *hsi_ctrl, u8 fifo)
+{
+ void __iomem *base = hsi_ctrl->base;
+ int hsr_mapping, mapping_words;
+
+ 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_gpio.c b/drivers/omap_hsi/hsi_driver_gpio.c
new file mode 100644
index 0000000..4c8810b
--- /dev/null
+++ b/drivers/omap_hsi/hsi_driver_gpio.c
@@ -0,0 +1,75 @@
+/*
+ * hsi_driver_gpio.c
+ *
+ * Implements HSI GPIO related functionality. (i.e: wake lines management)
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/gpio.h>
+#include "hsi_driver.h"
+
+static void do_hsi_cawake_tasklet(unsigned long hsi_p)
+{
+ struct hsi_port *port = (struct hsi_port *)hsi_p;
+ struct hsi_dev *hsi_ctrl = port->hsi_controller;
+
+ spin_lock(&hsi_ctrl->lock);
+ hsi_clocks_enable(hsi_ctrl->dev, __func__);
+ port->in_cawake_tasklet = true;
+
+ port->cawake_status = hsi_get_cawake(port);
+ hsi_do_cawake_process(port);
+
+ port->in_cawake_tasklet = false;
+ hsi_clocks_disable(hsi_ctrl->dev, __func__);
+ spin_unlock(&hsi_ctrl->lock);
+}
+
+static irqreturn_t hsi_cawake_isr(int irq, void *hsi_p)
+{
+ struct hsi_port *port = hsi_p;
+
+ tasklet_hi_schedule(&port->cawake_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+int __init hsi_cawake_init(struct hsi_port *port, const char *irq_name)
+{
+ tasklet_init(&port->cawake_tasklet, do_hsi_cawake_tasklet,
+ (unsigned long)port);
+
+ if (request_irq(port->cawake_gpio_irq, hsi_cawake_isr,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING, irq_name, port) < 0) {
+ dev_err(port->hsi_controller->dev,
+ "FAILED to request %s GPIO IRQ %d on port %d\n",
+ irq_name, port->cawake_gpio_irq, port->port_number);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+void hsi_cawake_exit(struct hsi_port *port)
+{
+ if (port->cawake_gpio < 0)
+ return; /* Nothing to do (case SSI with GPIO or */
+ /* HSI with IO ring wakeup */
+
+ tasklet_kill(&port->cawake_tasklet);
+ free_irq(port->cawake_gpio_irq, port);
+}
diff --git a/drivers/omap_hsi/hsi_driver_if.c b/drivers/omap_hsi/hsi_driver_if.c
new file mode 100644
index 0000000..19012e5
--- /dev/null
+++ b/drivers/omap_hsi/hsi_driver_if.c
@@ -0,0 +1,965 @@
+/*
+ * hsi_driver_if.c
+ *
+ * Implements HSI hardware driver interfaces for the upper layers.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "hsi_driver.h"
+
+#define NOT_SET (-1)
+
+/* Manage HSR divisor update
+ * A special divisor value allows switching to auto-divisor mode in Rx
+ * (but with error counters deactivated). This function implements the
+ * the transitions to/from this mode.
+ */
+int hsi_set_rx_divisor(struct hsi_port *sport, struct hsr_ctx *cfg)
+{
+ struct hsi_dev *hsi_ctrl = sport->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ int port = sport->port_number;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+ if (cfg->divisor == NOT_SET)
+ return 0;
+
+ if (hsi_driver_device_is_hsi(pdev)) {
+ if (cfg->divisor == HSI_HSR_DIVISOR_AUTO &&
+ sport->counters_on) {
+ /* auto mode: deactivate counters + set divisor = 0 */
+ sport->reg_counters = hsi_inl(base, HSI_HSR_COUNTERS_REG
+ (port));
+ sport->counters_on = 0;
+ hsi_outl(0, base, HSI_HSR_COUNTERS_REG(port));
+ hsi_outl(0, base, HSI_HSR_DIVISOR_REG(port));
+ dev_dbg(hsi_ctrl->dev, "Switched to HSR auto mode\n");
+ } 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,
+ HSI_HSR_COUNTERS_REG(port));
+ hsi_outl(cfg->divisor, base, HSI_HSR_DIVISOR_REG(port));
+ dev_dbg(hsi_ctrl->dev, "Left HSR auto mode. "
+ "Counters=0x%08x, Divisor=0x%08x\n",
+ cfg->counters, cfg->divisor);
+ }
+ } else {
+ if (cfg->divisor == HSI_HSR_DIVISOR_AUTO &&
+ sport->counters_on) {
+ /* auto mode: deactivate timeout */
+ sport->reg_counters = hsi_inl(base,
+ SSI_TIMEOUT_REG(port));
+ sport->counters_on = 0;
+ hsi_outl(0, base, SSI_TIMEOUT_REG(port));
+ dev_dbg(hsi_ctrl->dev, "Deactivated SSR timeout\n");
+ } else if (cfg->divisor == HSI_SSR_DIVISOR_USE_TIMEOUT) {
+ /* Leave auto mode: use new counters values */
+ sport->reg_counters = cfg->counters;
+ sport->counters_on = 1;
+ hsi_outl(cfg->counters, base, SSI_TIMEOUT_REG(port));
+ dev_dbg(hsi_ctrl->dev, "Left SSR auto mode. "
+ "Timeout=0x%08x\n", cfg->counters);
+ }
+ }
+
+ return 0;
+}
+
+int hsi_set_rx(struct hsi_port *sport, struct hsr_ctx *cfg)
+{
+ struct hsi_dev *hsi_ctrl = sport->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ int port = sport->port_number;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+ if (((cfg->mode & HSI_MODE_VAL_MASK) != HSI_MODE_STREAM) &&
+ ((cfg->mode & HSI_MODE_VAL_MASK) != HSI_MODE_FRAME) &&
+ ((cfg->mode & HSI_MODE_VAL_MASK) != HSI_MODE_SLEEP) &&
+ (cfg->mode != NOT_SET))
+ return -EINVAL;
+
+ if (hsi_driver_device_is_hsi(pdev)) {
+ if (((cfg->flow & HSI_FLOW_VAL_MASK) != HSI_FLOW_SYNCHRONIZED)
+ && ((cfg->flow & HSI_FLOW_VAL_MASK) != HSI_FLOW_PIPELINED)
+ && (cfg->flow != NOT_SET))
+ return -EINVAL;
+ /* HSI only supports payload size of 32bits */
+ if ((cfg->frame_size != HSI_FRAMESIZE_MAX) &&
+ (cfg->frame_size != NOT_SET))
+ return -EINVAL;
+ } else {
+ if (((cfg->flow & HSI_FLOW_VAL_MASK) != HSI_FLOW_SYNCHRONIZED)
+ && (cfg->flow != NOT_SET))
+ return -EINVAL;
+ /* HSI only supports payload size of 32bits */
+ if ((cfg->frame_size != HSI_FRAMESIZE_MAX) &&
+ (cfg->frame_size != NOT_SET))
+ return -EINVAL;
+ }
+
+ if ((cfg->channels == 0) ||
+ ((cfg->channels > sport->max_ch) && (cfg->channels != NOT_SET)))
+ return -EINVAL;
+
+ if (hsi_driver_device_is_hsi(pdev)) {
+ if ((cfg->divisor > HSI_MAX_RX_DIVISOR) &&
+ (cfg->divisor != HSI_HSR_DIVISOR_AUTO) &&
+ (cfg->divisor != NOT_SET))
+ return -EINVAL;
+ }
+
+ if ((cfg->mode != NOT_SET) && (cfg->flow != NOT_SET))
+ hsi_outl(cfg->mode | ((cfg->flow & HSI_FLOW_VAL_MASK)
+ << HSI_FLOW_OFFSET), base,
+ HSI_HSR_MODE_REG(port));
+
+ if (cfg->frame_size != NOT_SET)
+ hsi_outl(cfg->frame_size, base, HSI_HSR_FRAMESIZE_REG(port));
+
+ if (cfg->channels != NOT_SET) {
+ if ((cfg->channels & (-cfg->channels)) ^ cfg->channels)
+ return -EINVAL;
+ else
+ hsi_outl(cfg->channels, base,
+ HSI_HSR_CHANNELS_REG(port));
+ }
+
+ return hsi_set_rx_divisor(sport, cfg);
+}
+
+void hsi_get_rx(struct hsi_port *sport, struct hsr_ctx *cfg)
+{
+ struct hsi_dev *hsi_ctrl = sport->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ int port = sport->port_number;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+
+ cfg->mode = hsi_inl(base, HSI_HSR_MODE_REG(port)) & HSI_MODE_VAL_MASK;
+ cfg->flow = (hsi_inl(base, HSI_HSR_MODE_REG(port)) & HSI_FLOW_VAL_MASK)
+ >> HSI_FLOW_OFFSET;
+ cfg->frame_size = hsi_inl(base, HSI_HSR_FRAMESIZE_REG(port));
+ cfg->channels = hsi_inl(base, HSI_HSR_CHANNELS_REG(port));
+ if (hsi_driver_device_is_hsi(pdev)) {
+ cfg->divisor = hsi_inl(base, HSI_HSR_DIVISOR_REG(port));
+ cfg->counters = hsi_inl(base, HSI_HSR_COUNTERS_REG(port));
+ } else {
+ cfg->counters = hsi_inl(base, SSI_TIMEOUT_REG(port));
+ }
+}
+
+int hsi_set_tx(struct hsi_port *sport, struct hst_ctx *cfg)
+{
+ struct hsi_dev *hsi_ctrl = sport->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ int port = sport->port_number;
+ struct platform_device *pdev = to_platform_device(hsi_ctrl->dev);
+ unsigned int max_divisor = hsi_driver_device_is_hsi(pdev) ?
+ HSI_MAX_TX_DIVISOR : HSI_SSI_MAX_TX_DIVISOR;
+
+ if (((cfg->mode & HSI_MODE_VAL_MASK) != HSI_MODE_STREAM) &&
+ ((cfg->mode & HSI_MODE_VAL_MASK) != HSI_MODE_FRAME) &&
+ (cfg->mode != NOT_SET))
+ return -EINVAL;
+
+ if (hsi_driver_device_is_hsi(pdev)) {
+ if (((cfg->flow & HSI_FLOW_VAL_MASK) != HSI_FLOW_SYNCHRONIZED)
+ && ((cfg->flow & HSI_FLOW_VAL_MASK) != HSI_FLOW_PIPELINED)
+ && (cfg->flow != NOT_SET))
+ return -EINVAL;
+ /* HSI only supports payload size of 32bits */
+ if ((cfg->frame_size != HSI_FRAMESIZE_MAX) &&
+ (cfg->frame_size != NOT_SET))
+ return -EINVAL;
+ } else {
+ if (((cfg->flow & HSI_FLOW_VAL_MASK) != HSI_FLOW_SYNCHRONIZED)
+ && (cfg->flow != NOT_SET))
+ return -EINVAL;
+
+ if ((cfg->frame_size > HSI_FRAMESIZE_MAX) &&
+ (cfg->frame_size != NOT_SET))
+ return -EINVAL;
+ }
+
+ if ((cfg->channels == 0) ||
+ ((cfg->channels > sport->max_ch) && (cfg->channels != NOT_SET)))
+ return -EINVAL;
+
+ if ((cfg->divisor > max_divisor) && (cfg->divisor != NOT_SET))
+ return -EINVAL;
+
+ if ((cfg->arb_mode != HSI_ARBMODE_ROUNDROBIN) &&
+ (cfg->arb_mode != HSI_ARBMODE_PRIORITY) && (cfg->mode != NOT_SET))
+ return -EINVAL;
+
+ if ((cfg->mode != NOT_SET) && (cfg->flow != NOT_SET))
+ hsi_outl(cfg->mode | ((cfg->flow & HSI_FLOW_VAL_MASK) <<
+ HSI_FLOW_OFFSET) |
+ HSI_HST_MODE_WAKE_CTRL_SW, base,
+ HSI_HST_MODE_REG(port));
+
+ if (cfg->frame_size != NOT_SET)
+ hsi_outl(cfg->frame_size, base, HSI_HST_FRAMESIZE_REG(port));
+
+ if (cfg->channels != NOT_SET) {
+ if ((cfg->channels & (-cfg->channels)) ^ cfg->channels)
+ return -EINVAL;
+ else
+ hsi_outl(cfg->channels, base,
+ HSI_HST_CHANNELS_REG(port));
+ }
+
+ if (cfg->divisor != NOT_SET)
+ hsi_outl(cfg->divisor, base, HSI_HST_DIVISOR_REG(port));
+
+ if (cfg->arb_mode != NOT_SET)
+ hsi_outl(cfg->arb_mode, base, HSI_HST_ARBMODE_REG(port));
+
+ return 0;
+}
+
+void hsi_get_tx(struct hsi_port *sport, struct hst_ctx *cfg)
+{
+ struct hsi_dev *hsi_ctrl = sport->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ int port = sport->port_number;
+
+ cfg->mode = hsi_inl(base, HSI_HST_MODE_REG(port)) & HSI_MODE_VAL_MASK;
+ cfg->flow = (hsi_inl(base, HSI_HST_MODE_REG(port)) & HSI_FLOW_VAL_MASK)
+ >> HSI_FLOW_OFFSET;
+ cfg->frame_size = hsi_inl(base, HSI_HST_FRAMESIZE_REG(port));
+ cfg->channels = hsi_inl(base, HSI_HST_CHANNELS_REG(port));
+ cfg->divisor = hsi_inl(base, HSI_HST_DIVISOR_REG(port));
+ cfg->arb_mode = hsi_inl(base, HSI_HST_ARBMODE_REG(port));
+}
+
+/**
+ * hsi_open - open a hsi device channel.
+ * @dev - Reference to the hsi device channel to be openned.
+ *
+ * Returns 0 on success, -EINVAL on bad parameters, -EBUSY if is already opened.
+ */
+int hsi_open(struct hsi_device *dev)
+{
+ struct hsi_channel *ch;
+ struct hsi_port *port;
+ struct hsi_dev *hsi_ctrl;
+
+ if (!dev || !dev->ch) {
+ pr_err(LOG_NAME "Wrong HSI device %p\n", dev);
+ return -EINVAL;
+ }
+ dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);
+
+ ch = dev->ch;
+ if (!ch->read_done || !ch->write_done) {
+ dev_err(dev->device.parent,
+ "Trying to open with no (read/write) callbacks "
+ "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;
+ }
+
+ /* Restart with flags cleaned up */
+ ch->flags = HSI_CH_OPEN;
+
+ 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 */
+
+ hsi_clocks_disable_channel(dev->device.parent, ch->channel_number,
+ __func__);
+ spin_unlock_bh(&hsi_ctrl->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(hsi_open);
+
+/**
+ * hsi_write - write data into the hsi device channel
+ * @dev - reference to the hsi device channel to write into.
+ * @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.
+ * A success value only indicates that the request has been accepted.
+ * Transfer is only completed when the write_done callback is called.
+ *
+ */
+int hsi_write(struct hsi_device *dev, u32 *addr, unsigned int size)
+{
+ struct hsi_channel *ch;
+ int err;
+
+ if (unlikely(!dev)) {
+ pr_err(LOG_NAME "Null dev pointer in hsi_write\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(!dev->ch || !addr || (size <= 0))) {
+ dev_err(dev->device.parent,
+ "Wrong parameters hsi_device %p data %p count %d",
+ dev, addr, size);
+ return -EINVAL;
+ }
+ dev_dbg(dev->device.parent, "%s ch %d, @%x, size %d u32\n", __func__,
+ dev->n_ch, (u32) addr, size);
+
+ if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
+ dev_err(dev->device.parent, "HSI device NOT open\n");
+ return -EINVAL;
+ }
+
+ ch = dev->ch;
+
+ spin_lock_bh(&ch->hsi_port->hsi_controller->lock);
+ if (pm_runtime_suspended(dev->device.parent) ||
+ !ch->hsi_port->hsi_controller->clock_enabled)
+ dev_dbg(dev->device.parent,
+ "hsi_write with HSI clocks OFF, clock_enabled = %d\n",
+ ch->hsi_port->hsi_controller->clock_enabled);
+
+ 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;
+
+ if (size == 1)
+ err = hsi_driver_enable_write_interrupt(ch, addr);
+ else
+ err = hsi_driver_write_dma(ch, addr, size);
+
+ if (unlikely(err < 0)) {
+ ch->write_data.addr = NULL;
+ ch->write_data.size = 0;
+ dev_err(dev->device.parent, "Failed to program write\n");
+ }
+
+ spin_unlock_bh(&ch->hsi_port->hsi_controller->lock);
+
+ /* Leave clocks enabled until transfer is complete (write callback */
+ /* is called */
+ return err;
+}
+EXPORT_SYMBOL(hsi_write);
+
+/**
+ * hsi_read - read data from the hsi device channel
+ * @dev - hsi device channel reference to read data from.
+ * @addr - pointer to a 32-bit word data to store the data.
+ * @size - number of 32-bit word to be stored.
+ *
+ * Return 0 on sucess, a negative value on failure.
+ * A success value only indicates that the request has been accepted.
+ * Data is only available in the buffer when the read_done callback is called.
+ *
+ */
+int hsi_read(struct hsi_device *dev, u32 *addr, unsigned int size)
+{
+ struct hsi_channel *ch;
+ int err;
+
+ if (unlikely(!dev)) {
+ pr_err(LOG_NAME "Null dev pointer in hsi_read\n");
+ return -EINVAL;
+ }
+
+ if (unlikely(!dev->ch || !addr || (size <= 0))) {
+ dev_err(dev->device.parent, "Wrong parameters "
+ "hsi_device %p data %p count %d", dev, addr, size);
+ return -EINVAL;
+ }
+#if 0
+ if (dev->n_ch == 0)
+ dev_info(dev->device.parent, "%s ch %d, @%x, size %d u32\n",
+ __func__, dev->n_ch, (u32) addr, size);
+#endif
+ if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
+ dev_err(dev->device.parent, "HSI device NOT open\n");
+ return -EINVAL;
+ }
+
+ ch = dev->ch;
+
+ spin_lock_bh(&ch->hsi_port->hsi_controller->lock);
+ if (pm_runtime_suspended(dev->device.parent) ||
+ !ch->hsi_port->hsi_controller->clock_enabled)
+ dev_dbg(dev->device.parent,
+ "hsi_read with HSI clocks OFF, clock_enabled = %d\n",
+ ch->hsi_port->hsi_controller->clock_enabled);
+
+ hsi_clocks_enable_channel(dev->device.parent, ch->channel_number,
+ __func__);
+
+ if (ch->read_data.addr != NULL) {
+ dev_err(dev->device.parent, "# Invalid request - Read "
+ "operation pending port %d channel %d\n",
+ ch->hsi_port->port_number,
+ ch->channel_number);
+ err = -EINVAL;
+ goto done;
+ }
+
+ ch->read_data.addr = addr;
+ ch->read_data.size = size;
+ ch->read_data.lch = -1;
+
+ if (size == 1)
+ err = hsi_driver_enable_read_interrupt(ch, addr);
+ else
+ err = hsi_driver_read_dma(ch, addr, size);
+
+ if (unlikely(err < 0)) {
+ ch->read_data.addr = NULL;
+ ch->read_data.size = 0;
+ dev_err(dev->device.parent, "Failed to program read\n");
+ }
+
+done:
+ hsi_clocks_disable_channel(dev->device.parent, ch->channel_number,
+ __func__);
+ spin_unlock_bh(&ch->hsi_port->hsi_controller->lock);
+
+ return err;
+}
+EXPORT_SYMBOL(hsi_read);
+
+int __hsi_write_cancel(struct hsi_channel *ch)
+{
+ int err = -ENODATA;
+ if (ch->write_data.size == 1)
+ err = hsi_driver_cancel_write_interrupt(ch);
+ else if (ch->write_data.size > 1)
+ err = hsi_driver_cancel_write_dma(ch);
+ else
+ dev_dbg(ch->dev->device.parent, "%s : Nothing to cancel %d\n",
+ __func__, ch->write_data.size);
+ dev_err(ch->dev->device.parent, "%s : %d\n", __func__, err);
+ return err;
+}
+
+/**
+ * hsi_write_cancel - Cancel pending write request.
+ * @dev - hsi device channel where to cancel the pending write.
+ *
+ * write_done() callback will not be called after success of this function.
+ *
+ * Return: -ENXIO : No DMA channel found for specified HSI channel
+ * -ECANCELED : write cancel success, data not transfered to TX FIFO
+ * 0 : transfer is already over, data already transfered to TX FIFO
+ *
+ * Note: whatever returned value, write callback will not be called after
+ * write cancel.
+ */
+int hsi_write_cancel(struct hsi_device *dev)
+{
+ int err;
+ if (unlikely(!dev || !dev->ch)) {
+ pr_err(LOG_NAME "Wrong HSI device %p\n", dev);
+ return -ENODEV;
+ }
+ dev_err(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);
+
+ if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
+ dev_err(dev->device.parent, "HSI device NOT open\n");
+ return -ENODEV;
+ }
+
+ spin_lock_bh(&dev->ch->hsi_port->hsi_controller->lock);
+ hsi_clocks_enable_channel(dev->device.parent, dev->ch->channel_number,
+ __func__);
+
+ err = __hsi_write_cancel(dev->ch);
+
+ hsi_clocks_disable_channel(dev->device.parent, dev->ch->channel_number,
+ __func__);
+ spin_unlock_bh(&dev->ch->hsi_port->hsi_controller->lock);
+ return err;
+}
+EXPORT_SYMBOL(hsi_write_cancel);
+
+int __hsi_read_cancel(struct hsi_channel *ch)
+{
+ int err = -ENODATA;
+ if (ch->read_data.size == 1)
+ err = hsi_driver_cancel_read_interrupt(ch);
+ else if (ch->read_data.size > 1)
+ err = hsi_driver_cancel_read_dma(ch);
+ else
+ dev_dbg(ch->dev->device.parent, "%s : Nothing to cancel %d\n",
+ __func__, ch->read_data.size);
+
+ dev_err(ch->dev->device.parent, "%s : %d\n", __func__, err);
+ return err;
+}
+
+/**
+ * hsi_read_cancel - Cancel pending read request.
+ * @dev - hsi device channel where to cancel the pending read.
+ *
+ * read_done() callback will not be called after success of this function.
+ *
+ * Return: -ENXIO : No DMA channel found for specified HSI channel
+ * -ECANCELED : read cancel success, data not available at expected
+ * address.
+ * 0 : transfer is already over, data already available at expected
+ * address.
+ *
+ * Note: whatever returned value, read callback will not be called after cancel.
+ */
+int hsi_read_cancel(struct hsi_device *dev)
+{
+ int err;
+ if (unlikely(!dev || !dev->ch)) {
+ pr_err(LOG_NAME "Wrong HSI device %p\n", dev);
+ return -ENODEV;
+ }
+ dev_err(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);
+
+ if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
+ dev_err(dev->device.parent, "HSI device NOT open\n");
+ return -ENODEV;
+ }
+
+ spin_lock_bh(&dev->ch->hsi_port->hsi_controller->lock);
+ hsi_clocks_enable_channel(dev->device.parent, dev->ch->channel_number,
+ __func__);
+
+ err = __hsi_read_cancel(dev->ch);
+
+ hsi_clocks_disable_channel(dev->device.parent, dev->ch->channel_number,
+ __func__);
+ spin_unlock_bh(&dev->ch->hsi_port->hsi_controller->lock);
+ return err;
+}
+EXPORT_SYMBOL(hsi_read_cancel);
+
+/**
+ * hsi_poll - HSI poll feature, enables data interrupt on frame reception
+ * @dev - hsi device channel reference to apply the I/O control
+ * (or port associated to it)
+ *
+ * Return 0 on success, a negative value on failure.
+ *
+ */
+int hsi_poll(struct hsi_device *dev)
+{
+ struct hsi_channel *ch;
+ struct hsi_dev *hsi_ctrl;
+ int err;
+
+ if (unlikely(!dev || !dev->ch))
+ return -EINVAL;
+ dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);
+
+ if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
+ dev_err(dev->device.parent, "HSI device NOT open\n");
+ return -EINVAL;
+ }
+
+ ch = dev->ch;
+ hsi_ctrl = ch->hsi_port->hsi_controller;
+
+ spin_lock_bh(&hsi_ctrl->lock);
+ hsi_clocks_enable_channel(dev->device.parent, dev->ch->channel_number,
+ __func__);
+
+ ch->flags |= HSI_CH_RX_POLL;
+
+ err = hsi_driver_enable_read_interrupt(ch, NULL);
+
+ hsi_clocks_disable_channel(dev->device.parent, dev->ch->channel_number,
+ __func__);
+ spin_unlock_bh(&hsi_ctrl->lock);
+
+ return err;
+}
+EXPORT_SYMBOL(hsi_poll);
+
+/**
+ * hsi_unpoll - HSI poll feature, disables data interrupt on frame reception
+ * @dev - hsi device channel reference to apply the I/O control
+ * (or port associated to it)
+ *
+ * Return 0 on success, a negative value on failure.
+ *
+ */
+int hsi_unpoll(struct hsi_device *dev)
+{
+ struct hsi_channel *ch;
+ struct hsi_dev *hsi_ctrl;
+
+ if (unlikely(!dev || !dev->ch))
+ return -EINVAL;
+ dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);
+
+ if (unlikely(!(dev->ch->flags & HSI_CH_OPEN))) {
+ dev_err(dev->device.parent, "HSI device NOT open\n");
+ return -EINVAL;
+ }
+
+ ch = dev->ch;
+ hsi_ctrl = ch->hsi_port->hsi_controller;
+
+ spin_lock_bh(&hsi_ctrl->lock);
+ hsi_clocks_enable_channel(dev->device.parent, dev->ch->channel_number,
+ __func__);
+
+ ch->flags &= ~HSI_CH_RX_POLL;
+
+ hsi_driver_disable_read_interrupt(ch);
+
+ hsi_clocks_disable_channel(dev->device.parent, dev->ch->channel_number,
+ __func__);
+ spin_unlock_bh(&hsi_ctrl->lock);
+
+ return 0;
+}
+EXPORT_SYMBOL(hsi_unpoll);
+
+/**
+ * hsi_ioctl - HSI I/O control
+ * @dev - hsi device channel reference to apply the I/O control
+ * (or port associated to it)
+ * @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.
+ *
+ */
+int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg)
+{
+ struct hsi_channel *ch;
+ struct hsi_dev *hsi_ctrl;
+ struct hsi_port *pport;
+ void __iomem *base;
+ unsigned int port, channel;
+ u32 acwake;
+ int err = 0;
+ int fifo = 0;
+
+ if (unlikely((!dev) ||
+ (!dev->ch) ||
+ (!dev->ch->hsi_port) ||
+ (!dev->ch->hsi_port->hsi_controller)) ||
+ (!(dev->ch->flags & HSI_CH_OPEN))) {
+ pr_err(LOG_NAME "HSI IOCTL Invalid parameter\n");
+ return -EINVAL;
+ }
+
+ ch = dev->ch;
+ pport = ch->hsi_port;
+ hsi_ctrl = ch->hsi_port->hsi_controller;
+ port = ch->hsi_port->port_number;
+ channel = ch->channel_number;
+ base = hsi_ctrl->base;
+
+ dev_dbg(dev->device.parent, "IOCTL: ch %d, command %d\n",
+ channel, command);
+
+ spin_lock_bh(&hsi_ctrl->lock);
+ hsi_clocks_enable_channel(dev->device.parent, channel, __func__);
+
+ switch (command) {
+ case HSI_IOCTL_ACWAKE_UP:
+ 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);
+
+ /* We only claim once the wake line per channel */
+ acwake = hsi_inl(base, HSI_SYS_WAKE_REG(port));
+ if (!(acwake & HSI_WAKE(channel))) {
+ hsi_outl(HSI_SET_WAKE(channel), base,
+ HSI_SYS_SET_WAKE_REG(port));
+ }
+
+ goto out;
+ break;
+ case HSI_IOCTL_ACWAKE_DOWN:
+ /* Low power request initiation (OMAP initiated, typically */
+ /* following inactivity timeout) */
+ /* ACPU HSI block shall still be capable of receiving */
+ if (!(ch->flags & HSI_CH_ACWAKE)) {
+ dev_dbg(dev->device.parent, "Duplicate ACWAKE DOWN\n");
+ err = -EPERM;
+ goto out;
+ }
+
+ acwake = hsi_inl(base, HSI_SYS_WAKE_REG(port));
+ if (unlikely(pport->acwake_status !=
+ (acwake & HSI_WAKE_MASK))) {
+ dev_warn(dev->device.parent,
+ "ACWAKE shadow register mismatch"
+ " acwake_status: 0x%x, HSI_SYS_WAKE_REG: 0x%x",
+ pport->acwake_status, acwake);
+ pport->acwake_status = acwake & HSI_WAKE_MASK;
+ }
+ /* SSI_TODO: add safety check for SSI also */
+
+ ch->flags &= ~HSI_CH_ACWAKE;
+ pport->acwake_status &= ~BIT(channel);
+
+ /* Release the wake line per channel */
+ if ((acwake & HSI_WAKE(channel))) {
+ hsi_outl(HSI_CLEAR_WAKE(channel), base,
+ HSI_SYS_CLEAR_WAKE_REG(port));
+ }
+
+ goto out;
+ break;
+ case HSI_IOCTL_SEND_BREAK:
+ hsi_outl(1, base, HSI_HST_BREAK_REG(port));
+ /*HSI_TODO : need to deactivate clock after BREAK frames sent*/
+ /*Use interrupt ? (if TX BREAK INT exists)*/
+ break;
+ case HSI_IOCTL_GET_ACWAKE:
+ if (!arg) {
+ err = -EINVAL;
+ goto out;
+ }
+ *(u32 *)arg = hsi_inl(base, HSI_SYS_WAKE_REG(port));
+ break;
+ case HSI_IOCTL_FLUSH_RX:
+ hsi_outl(0, base, HSI_HSR_RXSTATE_REG(port));
+ break;
+ case HSI_IOCTL_FLUSH_TX:
+ hsi_outl(0, base, HSI_HST_TXSTATE_REG(port));
+ break;
+ case HSI_IOCTL_GET_CAWAKE:
+ if (!arg) {
+ err = -EINVAL;
+ goto out;
+ }
+ err = hsi_get_cawake(dev->ch->hsi_port);
+ if (err < 0) {
+ err = -ENODEV;
+ goto out;
+ }
+ *(u32 *)arg = err;
+ break;
+ case HSI_IOCTL_SET_RX:
+ if (!arg) {
+ err = -EINVAL;
+ goto out;
+ }
+ err = hsi_set_rx(dev->ch->hsi_port, (struct hsr_ctx *)arg);
+ break;
+ case HSI_IOCTL_GET_RX:
+ if (!arg) {
+ err = -EINVAL;
+ goto out;
+ }
+ hsi_get_rx(dev->ch->hsi_port, (struct hsr_ctx *)arg);
+ break;
+ case HSI_IOCTL_SET_TX:
+ if (!arg) {
+ err = -EINVAL;
+ goto out;
+ }
+ err = hsi_set_tx(dev->ch->hsi_port, (struct hst_ctx *)arg);
+ break;
+ case HSI_IOCTL_GET_TX:
+ if (!arg) {
+ err = -EINVAL;
+ goto out;
+ }
+ hsi_get_tx(dev->ch->hsi_port, (struct hst_ctx *)arg);
+ break;
+ case HSI_IOCTL_SW_RESET:
+ dev_info(dev->device.parent, "SW Reset\n");
+ err = hsi_softreset(hsi_ctrl);
+
+ /* Reset HSI config to default */
+ hsi_softreset_driver(hsi_ctrl);
+ break;
+ case HSI_IOCTL_GET_FIFO_OCCUPANCY:
+ if (!arg) {
+ err = -EINVAL;
+ goto out;
+ }
+ fifo = hsi_fifo_get_id(hsi_ctrl, channel, port);
+ if (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev, "No valid FIFO id found for "
+ "channel %d.\n", channel);
+ err = -EFAULT;
+ goto out;
+ }
+ *(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);
+ break;
+ case HSI_IOCTL_SET_4WIRE_MODE:
+ omap_writel((omap_readl(0x4A058C08) & 0xFFFF), 0x4A058C08);
+ break;
+ default:
+ err = -ENOIOCTLCMD;
+ break;
+ }
+out:
+ /* All IOCTL end by disabling the clocks, except ACWAKE high. */
+ hsi_clocks_disable_channel(dev->device.parent, channel, __func__);
+
+ spin_unlock_bh(&hsi_ctrl->lock);
+
+ return err;
+}
+EXPORT_SYMBOL(hsi_ioctl);
+
+/**
+ * hsi_close - close given hsi device channel
+ * @dev - reference to hsi device channel.
+ */
+void hsi_close(struct hsi_device *dev)
+{
+ struct hsi_dev *hsi_ctrl;
+
+ if (!dev || !dev->ch) {
+ pr_err(LOG_NAME "Trying to close wrong HSI device %p\n", dev);
+ return;
+ }
+ dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);
+
+ hsi_ctrl = dev->ch->hsi_port->hsi_controller;
+
+ spin_lock_bh(&hsi_ctrl->lock);
+ hsi_clocks_enable_channel(dev->device.parent, dev->ch->channel_number,
+ __func__);
+
+ if (dev->ch->flags & HSI_CH_OPEN) {
+ dev->ch->flags &= ~HSI_CH_OPEN;
+ __hsi_write_cancel(dev->ch);
+ __hsi_read_cancel(dev->ch);
+ }
+
+ hsi_clocks_disable_channel(dev->device.parent, dev->ch->channel_number,
+ __func__);
+ spin_unlock_bh(&hsi_ctrl->lock);
+}
+EXPORT_SYMBOL(hsi_close);
+
+/**
+ * hsi_set_read_cb - register read_done() callback.
+ * @dev - reference to hsi device channel where the callback is associated to.
+ * @read_cb - callback to signal read transfer completed.
+ * size is expressed in number of 32-bit words.
+ *
+ * NOTE: Write callback must be only set when channel is not open !
+ */
+void hsi_set_read_cb(struct hsi_device *dev,
+ void (*read_cb) (struct hsi_device *dev,
+ unsigned int size))
+{
+ dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);
+
+ dev->ch->read_done = read_cb;
+}
+EXPORT_SYMBOL(hsi_set_read_cb);
+
+/**
+ * hsi_set_read_cb - register write_done() callback.
+ * @dev - reference to hsi device channel where the callback is associated to.
+ * @write_cb - callback to signal read transfer completed.
+ * size is expressed in number of 32-bit words.
+ *
+ * NOTE: Read callback must be only set when channel is not open !
+ */
+void hsi_set_write_cb(struct hsi_device *dev,
+ void (*write_cb) (struct hsi_device *dev,
+ unsigned int size))
+{
+ dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);
+
+ dev->ch->write_done = write_cb;
+}
+EXPORT_SYMBOL(hsi_set_write_cb);
+
+/**
+ * hsi_set_port_event_cb - register port_event callback.
+ * @dev - reference to hsi device channel where the callback is associated to.
+ * @port_event_cb - callback to signal events from the channel port.
+ */
+void hsi_set_port_event_cb(struct hsi_device *dev,
+ void (*port_event_cb) (struct hsi_device *dev,
+ unsigned int event,
+ void *arg))
+{
+ struct hsi_port *port = dev->ch->hsi_port;
+ struct hsi_dev *hsi_ctrl = port->hsi_controller;
+
+ dev_dbg(dev->device.parent, "%s ch %d\n", __func__, dev->n_ch);
+
+ write_lock_bh(&dev->ch->rw_lock);
+ dev->ch->port_event = port_event_cb;
+ write_unlock_bh(&dev->ch->rw_lock);
+
+ /* Since we now have a callback registered for events, we can now */
+ /* enable the CAWAKE, ERROR and BREAK interrupts */
+ 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);
+ hsi_clocks_disable_channel(dev->device.parent, dev->ch->channel_number,
+ __func__);
+ spin_unlock_bh(&hsi_ctrl->lock);
+}
+EXPORT_SYMBOL(hsi_set_port_event_cb);
diff --git a/drivers/omap_hsi/hsi_driver_int.c b/drivers/omap_hsi/hsi_driver_int.c
new file mode 100644
index 0000000..ce67e5f
--- /dev/null
+++ b/drivers/omap_hsi/hsi_driver_int.c
@@ -0,0 +1,717 @@
+/*
+ * hsi_driver_int.c
+ *
+ * Implements HSI interrupt functionality.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "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;
+ ch->read_data.size = 0;
+ ch->read_data.lch = -1;
+}
+
+void hsi_reset_ch_write(struct hsi_channel *ch)
+{
+ ch->write_data.addr = NULL;
+ ch->write_data.size = 0;
+ ch->write_data.lch = -1;
+}
+
+/* Check if a Write (data transfer from AP to CP) is
+ * ongoing for a given HSI channel
+ */
+bool hsi_is_channel_busy(struct hsi_channel *ch)
+{
+ if (ch->write_data.addr == NULL)
+ return false;
+
+ /* Note: we do not check if there is a read pending, because incoming */
+ /* data will trigger an interrupt (FIFO or DMA), and wake up the */
+ /* platform, so no need to keep the clocks ON. */
+ return true;
+}
+
+/* Check if a HSI port is busy :
+ * - data transfer (Write) is ongoing for a given HSI channel
+ * - CAWAKE is high
+ * - Currently in HSI interrupt tasklet
+ * - Currently in HSI CAWAKE tasklet (for SSI)
+ */
+bool hsi_is_hsi_port_busy(struct hsi_port *pport)
+{
+ struct hsi_dev *hsi_ctrl = pport->hsi_controller;
+ bool cur_cawake = hsi_get_cawake(pport);
+ int ch;
+
+ if (pport->in_int_tasklet) {
+ dev_dbg(hsi_ctrl->dev, "Interrupt tasklet running\n");
+ return true;
+ }
+
+ if (pport->in_cawake_tasklet) {
+ dev_dbg(hsi_ctrl->dev, "SSI Cawake tasklet running\n");
+ return true;
+ }
+
+ if (cur_cawake) {
+ dev_dbg(hsi_ctrl->dev, "Port %d: WAKE status: acwake_status %d,"
+ "cur_cawake %d", pport->port_number,
+ pport->acwake_status, cur_cawake);
+ return true;
+ }
+
+ for (ch = 0; ch < pport->max_ch; ch++)
+ if (hsi_is_channel_busy(&pport->hsi_channel[ch])) {
+ dev_dbg(hsi_ctrl->dev, "Port %d; channel %d "
+ "busy\n", pport->port_number, ch);
+ return true;
+ }
+
+ return false;
+}
+
+/* Check if HSI controller is busy :
+ * - One of the HSI port is busy
+ * - Currently in HSI DMA tasklet
+ */
+bool hsi_is_hsi_controller_busy(struct hsi_dev *hsi_ctrl)
+{
+ int port;
+
+ if (hsi_ctrl->in_dma_tasklet) {
+ dev_dbg(hsi_ctrl->dev, "DMA tasklet running\n");
+ return true;
+ }
+
+ for (port = 0; port < hsi_ctrl->max_p; port++)
+ if (hsi_is_hsi_port_busy(&hsi_ctrl->hsi_port[port])) {
+ dev_dbg(hsi_ctrl->dev, "Port %d busy\n", port + 1);
+ return true;
+ }
+
+ dev_dbg(hsi_ctrl->dev, "No activity on HSI controller\n");
+ return false;
+}
+
+bool hsi_is_hst_port_busy(struct hsi_port *pport)
+{
+ unsigned int port = pport->port_number;
+ void __iomem *base = pport->hsi_controller->base;
+ u32 txstateval;
+
+ txstateval = hsi_inl(base, HSI_HST_TXSTATE_REG(port)) &
+ HSI_HST_TXSTATE_VAL_MASK;
+
+ if (txstateval != HSI_HST_TXSTATE_IDLE) {
+ dev_dbg(pport->hsi_controller->dev, "HST port %d busy, "
+ "TXSTATE=%d\n", port, txstateval);
+ return true;
+ }
+
+ return false;
+}
+
+bool hsi_is_hst_controller_busy(struct hsi_dev *hsi_ctrl)
+{
+ int port;
+
+ for (port = 0; port < hsi_ctrl->max_p; port++)
+ if (hsi_is_hst_port_busy(&hsi_ctrl->hsi_port[port]))
+ return true;
+
+ return false;
+}
+
+
+/* Enables the CAWAKE, BREAK, or ERROR interrupt for the given port */
+int hsi_driver_enable_interrupt(struct hsi_port *pport, u32 flag)
+{
+ hsi_outl_or(flag, pport->hsi_controller->base,
+ HSI_SYS_MPU_ENABLE_REG(pport->port_number, pport->n_irq));
+
+ return 0;
+}
+
+/* Enables the Data Accepted Interrupt of HST for the given channel */
+int hsi_driver_enable_write_interrupt(struct hsi_channel *ch, u32 * data)
+{
+ struct hsi_port *p = ch->hsi_port;
+ unsigned int port = p->port_number;
+ unsigned int channel = ch->channel_number;
+
+ hsi_outl_or(HSI_HST_DATAACCEPT(channel), p->hsi_controller->base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+
+ return 0;
+}
+
+/* Enables the Data Available Interrupt of HSR for the given channel */
+int hsi_driver_enable_read_interrupt(struct hsi_channel *ch, u32 * data)
+{
+ struct hsi_port *p = ch->hsi_port;
+ unsigned int port = p->port_number;
+ unsigned int channel = ch->channel_number;
+
+ hsi_outl_or(HSI_HSR_DATAAVAILABLE(channel), p->hsi_controller->base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+
+ return 0;
+}
+
+/**
+ * hsi_driver_cancel_write_interrupt - Cancel pending write interrupt.
+ * @dev - hsi device channel where to cancel the pending interrupt.
+ *
+ * Return: -ECANCELED : write cancel success, data not transfered to TX FIFO
+ * 0 : transfer is already over, data already transfered to TX FIFO
+ *
+ * Note: whatever returned value, write callback will not be called after
+ * write cancel.
+ */
+int hsi_driver_cancel_write_interrupt(struct hsi_channel *ch)
+{
+ struct hsi_port *p = ch->hsi_port;
+ unsigned int port = p->port_number;
+ unsigned int channel = ch->channel_number;
+ void __iomem *base = p->hsi_controller->base;
+ u32 status_reg;
+ long buff_offset;
+
+ status_reg = hsi_inl(base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+
+ if (!(status_reg & HSI_HST_DATAACCEPT(channel))) {
+ dev_dbg(&ch->dev->device, "Write cancel on not "
+ "enabled channel %d ENABLE REG 0x%08X", channel,
+ status_reg);
+ }
+ status_reg &= hsi_inl(base,
+ HSI_SYS_MPU_STATUS_CH_REG(port, p->n_irq, channel));
+
+ hsi_outl_and(~HSI_HST_DATAACCEPT(channel), base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+
+ buff_offset = hsi_hst_bufstate_f_reg(p->hsi_controller, port, channel);
+ if (buff_offset >= 0)
+ hsi_outl_and(~HSI_BUFSTATE_CHANNEL(channel), base, buff_offset);
+ hsi_reset_ch_write(ch);
+ return status_reg & HSI_HST_DATAACCEPT(channel) ? 0 : -ECANCELED;
+}
+
+/**
+ * hsi_driver_cancel_read_interrupt - Cancel pending read interrupt.
+ * @dev - hsi device channel where to cancel the pending interrupt.
+ *
+ * Return: -ECANCELED : read cancel success data not available at expected
+ * address.
+ * 0 : transfer is already over, data already available at expected
+ * address.
+ *
+ * Note: whatever returned value, read callback will not be called after cancel.
+ */
+int hsi_driver_cancel_read_interrupt(struct hsi_channel *ch)
+{
+ struct hsi_port *p = ch->hsi_port;
+ unsigned int port = p->port_number;
+ unsigned int channel = ch->channel_number;
+ void __iomem *base = p->hsi_controller->base;
+ u32 status_reg;
+
+ status_reg = hsi_inl(base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+ if (!(status_reg & HSI_HSR_DATAAVAILABLE(channel))) {
+ dev_dbg(&ch->dev->device, "Read cancel on not "
+ "enabled channel %d ENABLE REG 0x%08X", channel,
+ status_reg);
+ }
+ status_reg &= hsi_inl(base,
+ HSI_SYS_MPU_STATUS_CH_REG(port, p->n_irq, channel));
+ hsi_outl_and(~HSI_HSR_DATAAVAILABLE(channel), base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+ hsi_reset_ch_read(ch);
+ return status_reg & HSI_HSR_DATAAVAILABLE(channel) ? 0 : -ECANCELED;
+}
+
+void hsi_driver_disable_write_interrupt(struct hsi_channel *ch)
+{
+ struct hsi_port *p = ch->hsi_port;
+ unsigned int port = p->port_number;
+ unsigned int channel = ch->channel_number;
+ void __iomem *base = p->hsi_controller->base;
+
+ hsi_outl_and(~HSI_HST_DATAACCEPT(channel), base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+}
+
+void hsi_driver_disable_read_interrupt(struct hsi_channel *ch)
+{
+ struct hsi_port *p = ch->hsi_port;
+ unsigned int port = p->port_number;
+ unsigned int channel = ch->channel_number;
+ void __iomem *base = p->hsi_controller->base;
+
+ hsi_outl_and(~HSI_HSR_DATAAVAILABLE(channel), base,
+ HSI_SYS_MPU_ENABLE_CH_REG(port, p->n_irq, channel));
+}
+
+/* HST_ACCEPTED interrupt processing */
+static void hsi_do_channel_tx(struct hsi_channel *ch)
+{
+ struct hsi_dev *hsi_ctrl = ch->hsi_port->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int n_ch;
+ unsigned int n_p;
+ unsigned int irq;
+ long buff_offset;
+
+ n_ch = ch->channel_number;
+ n_p = ch->hsi_port->port_number;
+ irq = ch->hsi_port->n_irq;
+
+ dev_dbg(hsi_ctrl->dev,
+ "Data Accepted interrupt for channel %d.\n", n_ch);
+
+ hsi_driver_disable_write_interrupt(ch);
+
+ if (ch->write_data.addr == NULL) {
+ dev_err(hsi_ctrl->dev, "Error, NULL Write address.\n");
+ hsi_reset_ch_write(ch);
+
+ } else {
+ buff_offset = hsi_hst_buffer_reg(hsi_ctrl, n_p, n_ch);
+ if (buff_offset >= 0) {
+ hsi_outl(*(ch->write_data.addr), base, buff_offset);
+ ch->write_data.addr = NULL;
+ }
+ }
+
+ spin_unlock(&hsi_ctrl->lock);
+ dev_dbg(hsi_ctrl->dev, "Calling ch %d write callback.\n", n_ch);
+ (*ch->write_done) (ch->dev, 1);
+ spin_lock(&hsi_ctrl->lock);
+}
+
+/* HSR_AVAILABLE interrupt processing */
+static void hsi_do_channel_rx(struct hsi_channel *ch)
+{
+ struct hsi_dev *hsi_ctrl = ch->hsi_port->hsi_controller;
+ void __iomem *base = ch->hsi_port->hsi_controller->base;
+ unsigned int n_ch;
+ unsigned int n_p;
+ unsigned int irq;
+ long buff_offset;
+ 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;
+ irq = ch->hsi_port->n_irq;
+
+ dev_dbg(hsi_ctrl->dev,
+ "Data Available interrupt for channel %d.\n", n_ch);
+
+ /* Check if there is data in FIFO available for reading */
+ if (hsi_driver_device_is_hsi(to_platform_device(hsi_ctrl->dev))) {
+ fifo = hsi_fifo_get_id(hsi_ctrl, n_ch, n_p);
+ if (unlikely(fifo < 0)) {
+ dev_err(hsi_ctrl->dev, "No valid FIFO id found for "
+ "channel %d.\n", n_ch);
+ return;
+ }
+ fifo_words_avail = hsi_get_rx_fifo_occupancy(hsi_ctrl, fifo);
+ if (!fifo_words_avail) {
+ dev_dbg(hsi_ctrl->dev,
+ "WARNING: RX FIFO %d empty before CPU copy\n",
+ fifo);
+
+ /* Do not disable interrupt becaue another interrupt */
+ /* can still come, this time with a real frame. */
+ return;
+ }
+ }
+
+ /*
+ * 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);
+ goto done;
+ }
+
+ if (ch->flags & HSI_CH_RX_POLL)
+ rx_poll = 1;
+
+ if (ch->read_data.addr) {
+ 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);
+ }
+ }
+#if 0
+ if (omap_readl(0x4A05A810))
+ dev_err(hsi_ctrl->dev,
+ "RX BUF state is full. "
+ "Warning disabling interrupt %0x\n",
+ omap_readl(0x4A05A810));
+#endif
+ hsi_driver_disable_read_interrupt(ch);
+ hsi_reset_ch_read(ch);
+
+done:
+ if (rx_poll) {
+ spin_unlock(&hsi_ctrl->lock);
+ hsi_port_event_handler(ch->hsi_port,
+ HSI_EVENT_HSR_DATAAVAILABLE,
+ (void *)n_ch);
+ spin_lock(&hsi_ctrl->lock);
+ }
+
+ if (data_read) {
+ spin_unlock(&hsi_ctrl->lock);
+#if 0
+ dev_warn(hsi_ctrl->dev, "Read callback %d.\n", n_ch);
+ if (n_ch == 0)
+ dev_warn(hsi_ctrl->dev,
+ "Read callback %d \t DATA 0x%0x .\n",
+ n_ch, data);
+#endif
+ (*ch->read_done) (ch->dev, 1);
+ spin_lock(&hsi_ctrl->lock);
+ }
+}
+
+/**
+ * hsi_do_cawake_process - CAWAKE line management
+ * @pport - HSI port to process
+ *
+ * This function handles the CAWAKE L/H transitions and call the event callback
+ * accordingly.
+ *
+ * Returns 0 if CAWAKE event process, -EAGAIN if CAWAKE event processing is
+ * delayed due to a pending DMA interrupt.
+ * If -EAGAIN is returned, pport->hsi_tasklet has to be re-scheduled once
+ * DMA tasklet has be executed. This should be done automatically by driver.
+ *
+*/
+int hsi_do_cawake_process(struct hsi_port *pport)
+{
+ struct hsi_dev *hsi_ctrl = pport->hsi_controller;
+ bool cawake_status = hsi_get_cawake(pport);
+
+ /* Deal with init condition */
+ if (unlikely(pport->cawake_status < 0))
+ pport->cawake_status = !cawake_status;
+ dev_dbg(hsi_ctrl->dev,
+ "Interrupts are not enabled but CAWAKE has come\n: 0x%0x.\n",
+ omap_readl(0x4A05880c));
+ dev_dbg(hsi_ctrl->dev,
+ "Interrupts are not enabled but CAWAKE has come\n: 0x%0x.\n",
+ omap_readl(0x4A058804));
+
+ /* Check CAWAKE line status */
+ if (cawake_status) {
+ dev_dbg(hsi_ctrl->dev, "CAWAKE rising edge detected\n");
+
+ /* 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");
+ spin_unlock(&hsi_ctrl->lock);
+ hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_DOWN,
+ NULL);
+ spin_lock(&hsi_ctrl->lock);
+ }
+ pport->cawake_status = 1;
+ if (omap_readl(0x4A306404) != 0x0) {
+ omap_writel(0x00000002, 0x4A004400);
+ omap_writel(0x003F0703, 0x4A306400);
+ omap_writel(0x003F0700, 0x4A306400);
+ omap_writel(0x00000003, 0x4A004400);
+ }
+ /* Force HSI to ON_ACTIVE when CAWAKE is high */
+ hsi_set_pm_force_hsi_on(hsi_ctrl);
+ /* TODO: Use omap_pm_set_max_dev_wakeup_lat() 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);
+ } else {
+ dev_dbg(hsi_ctrl->dev, "CAWAKE falling edge detected\n");
+
+ /* Check for pending DMA interrupt */
+ if (hsi_is_dma_read_int_pending(hsi_ctrl)) {
+ dev_dbg(hsi_ctrl->dev, "Pending DMA Read interrupt "
+ "before CAWAKE->L, exiting "
+ "Interrupt tasklet.\n");
+ return -EAGAIN;
+ }
+ if (unlikely(!pport->cawake_status)) {
+ dev_warn(hsi_ctrl->dev,
+ "CAWAKE race is detected: %s.\n",
+ "LOW -> HI -> LOW");
+ spin_unlock(&hsi_ctrl->lock);
+ hsi_port_event_handler(pport, HSI_EVENT_CAWAKE_UP,
+ NULL);
+ spin_lock(&hsi_ctrl->lock);
+ }
+ pport->cawake_status = 0;
+
+ /* Allow HSI HW to enter IDLE when CAWAKE is low */
+ hsi_set_pm_default(hsi_ctrl);
+ /* TODO: Use omap_pm_set_max_dev_wakeup_lat() to release */
+ /* latency constraint to prevent 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);
+ }
+ return 0;
+}
+
+/**
+ * hsi_driver_int_proc - check all channels / ports for interrupts events
+ * @hsi_ctrl - HSI controler data
+ * @status_offset: interrupt status register offset
+ * @enable_offset: interrupt enable regiser offset
+ * @start: interrupt index to start on
+ * @stop: interrupt index to stop on
+ *
+ * returns the bitmap of processed events
+ *
+ * This function calls the related processing functions and triggered events.
+ * Events are cleared after corresponding function has been called.
+*/
+static u32 hsi_driver_int_proc(struct hsi_port *pport,
+ unsigned long status_offset,
+ unsigned long enable_offset, unsigned int start,
+ unsigned int stop)
+{
+ struct hsi_dev *hsi_ctrl = pport->hsi_controller;
+ void __iomem *base = hsi_ctrl->base;
+ unsigned int port = pport->port_number;
+ unsigned int channel;
+ u32 status_reg;
+ u32 hsr_err_reg;
+ u32 channels_served = 0;
+
+ /* Get events status */
+ status_reg = hsi_inl(base, status_offset);
+ status_reg &= hsi_inl(base, enable_offset);
+
+ if (pport->cawake_off_event) {
+ dev_dbg(hsi_ctrl->dev, "CAWAKE detected from OFF mode.\n");
+ } else if (!status_reg) {
+ dev_dbg(hsi_ctrl->dev, "Channels [%d,%d] : no event, exit.\n",
+ start, stop);
+ return 0;
+ } else {
+ dev_dbg(hsi_ctrl->dev, "Channels [%d,%d] : Events 0x%08x\n",
+ start, stop, status_reg);
+ }
+
+ 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);
+
+ channels_served |= HSI_BREAKDETECTED;
+ }
+
+ if (status_reg & HSI_ERROROCCURED) {
+ hsr_err_reg = hsi_inl(base, HSI_HSR_ERROR_REG(port));
+ if (hsr_err_reg & HSI_HSR_ERROR_SIG)
+ dev_err(hsi_ctrl->dev, "HSI ERROR Port %d: 0x%x: %s\n",
+ port, hsr_err_reg, "Signal Error");
+ if (hsr_err_reg & HSI_HSR_ERROR_FTE)
+ dev_err(hsi_ctrl->dev, "HSI ERROR Port %d: 0x%x: %s\n",
+ port, hsr_err_reg, "Frame Timeout Error");
+ if (hsr_err_reg & HSI_HSR_ERROR_TBE)
+ dev_err(hsi_ctrl->dev, "HSI ERROR Port %d: 0x%x: %s\n",
+ port, hsr_err_reg, "Tailing Bit Error");
+ if (hsr_err_reg & HSI_HSR_ERROR_RME)
+ dev_err(hsi_ctrl->dev, "HSI ERROR Port %d: 0x%x: %s\n",
+ port, hsr_err_reg, "RX Mapping Error");
+ if (hsr_err_reg & HSI_HSR_ERROR_TME)
+ dev_err(hsi_ctrl->dev, "HSI ERROR Port %d: 0x%x: %s\n",
+ port, hsr_err_reg, "TX Mapping Error");
+ /* Clear error event bit */
+ hsi_outl(hsr_err_reg, base, HSI_HSR_ERRORACK_REG(port));
+ if (hsr_err_reg) { /* ignore spurious errors */
+ spin_unlock(&hsi_ctrl->lock);
+ hsi_port_event_handler(pport, HSI_EVENT_ERROR, NULL);
+ spin_lock(&hsi_ctrl->lock);
+ } else
+ dev_dbg(hsi_ctrl->dev, "Spurious HSI error!\n");
+
+ channels_served |= HSI_ERROROCCURED;
+ }
+
+ for (channel = start; channel <= stop; channel++) {
+ if (status_reg & HSI_HST_DATAACCEPT(channel)) {
+ hsi_do_channel_tx(&pport->hsi_channel[channel]);
+ channels_served |= HSI_HST_DATAACCEPT(channel);
+ }
+
+ if (status_reg & HSI_HSR_DATAAVAILABLE(channel)) {
+ hsi_do_channel_rx(&pport->hsi_channel[channel]);
+ channels_served |= HSI_HSR_DATAAVAILABLE(channel);
+ }
+
+ if (status_reg & HSI_HSR_DATAOVERRUN(channel)) {
+ /*HSI_TODO : Data overrun handling*/
+ dev_err(hsi_ctrl->dev,
+ "Data overrun in real time mode !\n");
+ }
+ }
+
+ /* CAWAKE falling or rising edge detected */
+ if ((status_reg & HSI_CAWAKEDETECTED) || pport->cawake_off_event) {
+ if (hsi_do_cawake_process(pport) == -EAGAIN)
+ goto proc_done;
+
+ channels_served |= HSI_CAWAKEDETECTED;
+ pport->cawake_off_event = false;
+ }
+proc_done:
+ /* Reset status bits */
+ hsi_outl(channels_served, base, status_offset);
+
+ return channels_served;
+}
+
+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;
+
+ /* 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);
+
+ /* Process events for channels 8..15 */
+ if (pport->max_ch > HSI_SSI_CHANNELS_MAX)
+ 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);
+
+ return status_reg;
+}
+
+static void do_hsi_tasklet(unsigned long hsi_port)
+{
+ struct hsi_port *pport = (struct hsi_port *)hsi_port;
+ struct hsi_dev *hsi_ctrl = pport->hsi_controller;
+ u32 status_reg;
+
+ dev_dbg(hsi_ctrl->dev, "Int Tasklet : clock_enabled=%d\n",
+ hsi_ctrl->clock_enabled);
+#if 0
+ if (pport->cawake_off_event == true)
+ dev_info(hsi_ctrl->dev,
+ "Tasklet called from OFF/RET MODE THRU PAD CPU ID %d\n",
+ smp_processor_id());
+ else
+ dev_info(hsi_ctrl->dev,
+ "Tasklet called from ACTIVE MODE CPU ID %d\n",
+ smp_processor_id());
+#endif
+ spin_lock(&hsi_ctrl->lock);
+ hsi_clocks_enable(hsi_ctrl->dev, __func__);
+ pport->in_int_tasklet = true;
+
+ status_reg = hsi_process_int_event(pport);
+
+ pport->in_int_tasklet = false;
+ 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 0
+ printk(KERN_INFO "Tasklet called from MPU HANDLER CPU ID %d "
+ "\t STS 0x%0x \t ENB 0x%0x\n", smp_processor_id(),
+ omap_readl(0x4A058808), omap_readl(0x4A05880C));
+#endif
+ if (shceduled_already_flag == 0) {
+#if 0
+ tasklet_hi_schedule(&pport->hsi_tasklet);
+ if (TASKLET_STATE_SCHED == pport->hsi_tasklet.state) {
+ printk(KERN_INFO "MPU TASKLET ALREADY SCHEDULED RETURNING\n");
+ return IRQ_HANDLED;
+ }
+#endif
+ shceduled_already_flag = 1;
+ tasklet_hi_schedule(&pport->hsi_tasklet);
+ /* Disable interrupt until Bottom Half has cleared the */
+ /* IRQ status register */
+ disable_irq_nosync(pport->irq);
+ }
+ return IRQ_HANDLED;
+}
+
+int __init hsi_mpu_init(struct hsi_port *hsi_p, const char *irq_name)
+{
+ int err;
+
+ tasklet_init(&hsi_p->hsi_tasklet, do_hsi_tasklet, (unsigned long)hsi_p);
+
+ dev_info(hsi_p->hsi_controller->dev, "Registering IRQ %s (%d)\n",
+ irq_name, hsi_p->irq);
+ err = request_irq(hsi_p->irq, hsi_mpu_handler,
+ IRQF_NO_SUSPEND | IRQF_TRIGGER_HIGH,
+ irq_name, hsi_p);
+ if (err < 0) {
+ dev_err(hsi_p->hsi_controller->dev, "FAILED to MPU request"
+ " IRQ (%d) on port %d", hsi_p->irq, hsi_p->port_number);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+void hsi_mpu_exit(struct hsi_port *hsi_p)
+{
+ tasklet_kill(&hsi_p->hsi_tasklet);
+ free_irq(hsi_p->irq, hsi_p);
+}
diff --git a/drivers/omap_hsi/hsi_protocol.c b/drivers/omap_hsi/hsi_protocol.c
new file mode 100644
index 0000000..e1451e7
--- /dev/null
+++ b/drivers/omap_hsi/hsi_protocol.c
@@ -0,0 +1,308 @@
+/*
+ * File - hsi_protocol.c
+ *
+ * Implements HSI protocol for Infineon Modem.
+ *
+ * Copyright (C) 2011 Samsung Electronics.
+ *
+ * Author: Rupesh Gujare <rupesh.g@samsung.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if 0
+#define DEBUG 1
+#endif
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/io.h>
+
+#include "hsi-protocol-if.h"
+#include <linux/hsi_driver_if.h>
+
+#define DRIVER_VERSION "1.0"
+
+char test_data[10] = "abcdefghij";
+
+dev_t hsi_protocol_dev;
+
+struct protocol_queue {
+ struct list_head list;
+ u32 *data;
+ unsigned int count;
+};
+
+struct hsi_protocol {
+ unsigned int opened;
+ int poll_event;
+ struct list_head rx_queue;
+ struct list_head tx_queue;
+ spinlock_t lock; /* Serialize access to driver data and API */
+ struct fasync_struct *async_queue;
+ wait_queue_head_t rx_wait;
+ wait_queue_head_t tx_wait;
+ wait_queue_head_t poll_wait;
+};
+
+static struct hsi_protocol hsi_protocol_data[HSI_MAX_CHANNELS];
+
+void if_notify(int ch, struct hsi_event *ev)
+{
+ struct protocol_queue *entry;
+
+ pr_debug("%s, ev = {0x%x, 0x%p, %u}\n",
+ __func__, ev->event, ev->data, ev->count);
+
+ spin_lock(&hsi_protocol_data[ch].lock);
+
+/* Not Required */
+ /*if (!hsi_protocol_data[ch].opened) {
+ pr_debug("%s, device not opened\n!", __func__);
+ printk(KERN_INFO "%s, device not opened\n!", __func__);
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ return;
+ }*/
+
+ switch (HSI_EV_TYPE(ev->event)) {
+ case HSI_EV_IN:
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry) {
+ pr_err("HSI-CHAR: entry allocation failed.\n");
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ return;
+ }
+ entry->data = ev->data;
+ entry->count = ev->count;
+ list_add_tail(&entry->list, &hsi_protocol_data[ch].rx_queue);
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ pr_debug("%s, HSI_EV_IN\n", __func__);
+ wake_up_interruptible(&hsi_protocol_data[ch].rx_wait);
+ break;
+ case HSI_EV_OUT:
+ entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
+ if (!entry) {
+ pr_err("HSI-CHAR: entry allocation failed.\n");
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ return;
+ }
+ entry->data = ev->data;
+ entry->count = ev->count;
+ hsi_protocol_data[ch].poll_event |= (POLLOUT | POLLWRNORM);
+ list_add_tail(&entry->list, &hsi_protocol_data[ch].tx_queue);
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ pr_debug("%s, HSI_EV_OUT\n", __func__);
+ wake_up_interruptible(&hsi_protocol_data[ch].tx_wait);
+ break;
+ case HSI_EV_EXCEP:
+ hsi_protocol_data[ch].poll_event |= POLLPRI;
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ pr_debug("%s, HSI_EV_EXCEP\n", __func__);
+ wake_up_interruptible(&hsi_protocol_data[ch].poll_wait);
+ break;
+ case HSI_EV_AVAIL:
+ hsi_protocol_data[ch].poll_event |= (POLLIN | POLLRDNORM);
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ pr_debug("%s, HSI_EV_AVAIL\n", __func__);
+ wake_up_interruptible(&hsi_protocol_data[ch].poll_wait);
+ break;
+ default:
+ spin_unlock(&hsi_protocol_data[ch].lock);
+ break;
+ }
+}
+
+int hsi_proto_read(int ch, u32 *buffer, int count)
+{
+ DECLARE_WAITQUEUE(wait, current);
+ u32 *data;
+ unsigned int data_len = 0;
+ struct protocol_queue *entry;
+ int ret, recv_data = 0;
+
+ /*if (count > MAX_HSI_IPC_BUFFER)
+ count = MAX_HSI_IPC_BUFFER;
+
+ data = kmalloc(count, GFP_ATOMIC);*/
+
+ ret = if_hsi_read(ch, buffer, count);
+ if (ret < 0) {
+ pr_err("Can not submit read. READ Error\n");
+ goto out2;
+ }
+
+ spin_lock_bh(&hsi_protocol_data[ch].lock);
+ add_wait_queue(&hsi_protocol_data[ch].rx_wait, &wait);
+ spin_unlock_bh(&hsi_protocol_data[ch].lock);
+
+ for (;;) {
+ data = NULL;
+ data_len = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ spin_lock_bh(&hsi_protocol_data[ch].lock);
+ if (!list_empty(&hsi_protocol_data[ch].rx_queue)) {
+ entry = list_entry(hsi_protocol_data[ch].rx_queue.next,
+ struct protocol_queue, list);
+ data = entry->data;
+ data_len = entry->count;
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ spin_unlock_bh(&hsi_protocol_data[ch].lock);
+
+ pr_debug("%s, data = 0x%p, data_len = %d\n",
+ __func__, data, data_len);
+
+ if (data_len) {
+ pr_debug("%s, RX finished, ch-> %d, length = %d\n",
+ __func__, ch, count);
+ spin_lock_bh(&hsi_protocol_data[ch].lock);
+ hsi_protocol_data[ch].poll_event &=
+ ~(POLLIN | POLLRDNORM);
+ spin_unlock_bh(&hsi_protocol_data[ch].lock);
+ if_hsi_poll(ch);
+#if 0
+ memcpy(buffer, data, count);
+#endif
+ recv_data += data_len;
+#if 0
+ buffer += data_len;
+ if ((recv_data == count) || (recv_data >= MAX_HSI_IPC_BUFFER))
+#endif
+ break;
+ } else if (signal_pending(current)) {
+ pr_debug("%s, ERESTARTSYS\n", __func__);
+ recv_data = -EAGAIN;
+ if_hsi_cancel_read(ch);
+ /* goto out; */
+ break;
+ }
+
+ /*printk(KERN_DEBUG "%s, going to sleep...\n", __func__); */
+ schedule();
+ /*printk(KERN_DEBUG "%s, woke up\n", __func__); */
+ }
+
+/*out:*/
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hsi_protocol_data[ch].rx_wait, &wait);
+
+out2:
+ /*To Do- Set bit if data to be received is
+ * greater than 512K Bytes and return to IPC call
+ */
+
+ return recv_data;
+}
+
+int hsi_proto_write(int ch, u32 *buffer, int length)
+{
+
+ DECLARE_WAITQUEUE(wait, current);
+ u32 *data;
+ unsigned int data_len = 0, ret = -1;
+ struct protocol_queue *entry;
+
+ ret = if_hsi_write(ch, buffer, length);
+ if (ret < 0) {
+ pr_err("HSI Write ERROR %s\n", __func__);
+ goto out2;
+ } else
+ spin_lock_bh(&hsi_protocol_data[ch].lock);
+ hsi_protocol_data[ch].poll_event &= ~(POLLOUT | POLLWRNORM);
+ add_wait_queue(&hsi_protocol_data[ch].tx_wait, &wait);
+ spin_unlock_bh(&hsi_protocol_data[ch].lock);
+
+ for (;;) {
+ data = NULL;
+ data_len = 0;
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ spin_lock_bh(&hsi_protocol_data[ch].lock);
+ if (!list_empty(&hsi_protocol_data[ch].tx_queue)) {
+ entry = list_entry(hsi_protocol_data[ch].tx_queue.next,
+ struct protocol_queue, list);
+ data = entry->data;
+ data_len = entry->count;
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ spin_unlock_bh(&hsi_protocol_data[ch].lock);
+
+ if (data_len) {
+ pr_debug("%s, TX finished, data_len = %d, ch-> %d\n",
+ __func__, length, ch);
+ ret = data_len;
+ break;
+ } else if (signal_pending(current)) {
+ pr_debug("%s, ERESTARTSYS\n", __func__);
+ ret = -ERESTARTSYS;
+ goto out;
+ }
+
+ schedule();
+ }
+
+out:
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&hsi_protocol_data[ch].tx_wait, &wait);
+
+out2:
+ return ret;
+}
+EXPORT_SYMBOL(hsi_proto_write);
+
+static int __init hsi_protocol_init(void)
+{
+ int i, ret = 0;
+
+ pr_info("HSI Infineon Protocol driver version " DRIVER_VERSION "\n");
+
+ for (i = 0; i < HSI_MAX_CHANNELS; i++) {
+ init_waitqueue_head(&hsi_protocol_data[i].rx_wait);
+ init_waitqueue_head(&hsi_protocol_data[i].tx_wait);
+ init_waitqueue_head(&hsi_protocol_data[i].poll_wait);
+ spin_lock_init(&hsi_protocol_data[i].lock);
+ hsi_protocol_data[i].opened = 0;
+ INIT_LIST_HEAD(&hsi_protocol_data[i].rx_queue);
+ INIT_LIST_HEAD(&hsi_protocol_data[i].tx_queue);
+ }
+
+ printk(KERN_INFO "hsi_protocol_init : hsi_mux_setting Done.\n");
+
+ ret = if_hsi_init();
+
+ return ret;
+}
+
+
+static void __exit hsi_protocol_exit(void)
+{
+ if_hsi_exit();
+}
+
+
+MODULE_AUTHOR("Rupesh Gujare <rupesh.g@samsung.com> / Samsung Electronics");
+MODULE_DESCRIPTION("HSI Protocol for Infineon Modem");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRIVER_VERSION);
+
+module_init(hsi_protocol_init);
+module_exit(hsi_protocol_exit);
diff --git a/drivers/omap_hsi/hsi_protocol_cmd.c b/drivers/omap_hsi/hsi_protocol_cmd.c
new file mode 100644
index 0000000..d256631
--- /dev/null
+++ b/drivers/omap_hsi/hsi_protocol_cmd.c
@@ -0,0 +1,429 @@
+/*
+ * File - hsi_protocol_if_cmd.c
+ *
+ * Implements HSI protocol for Infineon Modem.
+ *
+ * Copyright (C) 2011 Samsung Electronics. All rights reserved.
+ *
+ * Author: Rupesh Gujare <rupesh.g@samsung.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+
+#include <linux/hsi_driver_if.h>
+#include "hsi-protocol-if.h"
+
+extern struct if_hsi_iface hsi_protocol_iface;
+extern wait_queue_head_t ipc_read_wait, ipc_write_wait;
+int if_hsi_openchannel(struct if_hsi_channel *channel);
+int if_hsi_closechannel(struct if_hsi_channel *channel);
+
+extern struct if_hsi_cmd hsi_cmd_history;
+extern int tx_cmd_history_p;
+extern int rx_cmd_history_p;
+
+/*Decode command from received PDU on channle 0*/
+int hsi_decode_cmd(u32 *cmd_data, u32 *cmd, u32 *ch, u32 *param)
+{
+ int ret = 0;
+ 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("Command MSG_BREAK Received.\n");
+ break;
+
+ case HSI_LL_MSG_OPEN_CONN:
+ *ch = ((data & 0x0F000000) >> 24);
+ *param = ((data & 0x00FFFF00) >> 8);
+ /*Check LRC*/
+ 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)
+ ret = -1;
+ break;
+
+ 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);
+ break;
+
+ case HSI_LL_MSG_ACK:
+ *ch = ((data & 0x0F000000) >> 24);
+ *param = (data & 0x00FFFFFF);
+ //printk(KERN_INFO "ACK Received ch=%d, param=%d\n",*ch, *param);
+ break;
+
+ case HSI_LL_MSG_CONF_RATE:
+ *ch = ((data & 0x0F000000) >> 24);
+ *param = ((data & 0x0F000000) >> 24);
+ break;
+
+ case HSI_LL_MSG_OPEN_CONN_OCTET:
+ *ch = ((data & 0x0F000000) >> 24);
+ *param = (data & 0x00FFFFFF);
+ break;
+
+ 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:
+ *cmd = HSI_LL_MSG_INVALID;
+ *ch = HSI_LL_INVALID_CHANNEL;
+ ret = -1;
+ break;
+ }
+ return ret;
+}
+
+int protocol_create_cmd(int cmd_type, unsigned int channel, void *arg)
+{
+ unsigned int command = 0;
+ int ret = 0;
+
+ switch (cmd_type) {
+ case HSI_LL_MSG_BREAK:
+ {
+ command = 0;
+ }
+ break;
+
+ case HSI_LL_MSG_OPEN_CONN:
+ {
+ unsigned int size = *(unsigned int *)arg;
+ unsigned int lcr = 0;
+
+/* if(size > 4)
+ size = (size & 0x3) ? ((size >> 2) + 1):(size >> 2);
+ else
+ size = 1;*/
+
+ command = ((HSI_LL_MSG_OPEN_CONN & 0x0000000F) << 28) |
+ ((channel & 0x000000FF) << 24) |
+ ((size & 0x0000FFFF) << 8);
+
+ lcr = ((command & 0xFF000000) >> 24) ^
+ ((command & 0x00FF0000) >> 16) ^
+ ((command & 0x0000FF00) >> 8);
+
+ command = command | (lcr & 0x000000FF);
+ }
+ break;
+
+ case HSI_LL_MSG_CONN_READY:
+ {
+ command = ((HSI_LL_MSG_CONN_READY & 0x0000000F) << 28) |
+ ((channel & 0x000000FF) << 24);
+ }
+ break;
+
+ case HSI_LL_MSG_CONN_CLOSED:
+ {
+ command = ((HSI_LL_MSG_CONN_CLOSED & 0x0000000F) << 28) |
+ ((channel & 0x000000FF) << 24);
+ }
+ break;
+
+ case HSI_LL_MSG_CANCEL_CONN:
+ {
+ unsigned int role = *(unsigned int *)arg;
+
+ command = ((HSI_LL_MSG_CANCEL_CONN & 0x0000000F) << 28) |
+ ((channel & 0x000000FF) << 24) |
+ ((role & 0x000000FF) << 16);
+ }
+ break;
+
+ case HSI_LL_MSG_ACK:
+ {
+ unsigned int echo_params = *(unsigned int *)arg;
+
+ command = ((HSI_LL_MSG_ACK & 0x0000000F) << 28) |
+ ((channel & 0x000000FF) << 24) |
+ ((echo_params & 0x00FFFFFF));
+ }
+ break;
+
+ case HSI_LL_MSG_NAK:
+ {
+ command = ((HSI_LL_MSG_NAK & 0x0000000F) << 28) |
+ ((channel & 0x000000FF) << 24);
+ }
+ break;
+
+ case HSI_LL_MSG_CONF_RATE:
+ {
+ unsigned int baud_rate = *(unsigned int *)arg;
+
+ command = ((HSI_LL_MSG_CONF_RATE & 0x0000000F) << 28) |
+ ((channel & 0x000000FF) << 24) |
+ ((baud_rate & 0x00FFFFFF));
+ }
+ break;
+
+ case HSI_LL_MSG_OPEN_CONN_OCTET:
+ {
+ unsigned int size = *(unsigned int *)arg;
+
+ command = ((HSI_LL_MSG_OPEN_CONN_OCTET & 0x0000000F) << 28) |
+ ((channel & 0x000000FF) << 24) |
+ ((size & 0x00FFFFFF));
+
+ }
+ break;
+
+ 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:
+ ret = -1;
+ break;
+ }
+ return command;
+}
+
+int set_tx_config(struct if_hsi_channel *ch, u32 mode, u32 max_channels)
+{
+ struct hst_ctx tx_config;
+ int ret;
+
+ hsi_ioctl(ch->dev, HSI_IOCTL_GET_TX, &tx_config);
+ tx_config.mode = mode;
+ tx_config.channels = max_channels;
+ ret = hsi_ioctl(ch->dev, HSI_IOCTL_SET_TX, &tx_config);
+ return ret;
+}
+
+static int saved_cmd_queue = 0;
+static u32 cmd_saved[5];
+int hsi_protocol_send_command(u32 cmd, u32 channel, u32 param)
+{
+ struct if_hsi_channel *channel_zero;
+ u32 cmd_array[4] = {0x00000000, 0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC}, ret = -1;
+
+ channel_zero = &hsi_protocol_iface.channels[0];
+ cmd_array[0] = protocol_create_cmd(cmd, channel, &param);
+ pr_debug("[%s] CMD = %08x\n",__func__, cmd_array[0]);
+ while (channel_zero->tx_state != HSI_LL_TX_STATE_IDLE) {
+ cmd_saved[saved_cmd_queue] = cmd_array[0];
+ saved_cmd_queue++;
+ pr_debug("(%s) cmd_saved : %x(%d)\n", __func__, cmd_array[0], saved_cmd_queue);
+
+ return 0;
+ }
+
+send_retry:
+
+ channel_zero->tx_state = HSI_LL_TX_STATE_TX;
+
+ // For es 2.1 ver.
+ ret = hsi_proto_write(0, cmd_array, 4);
+ if (ret < 0) {
+ pr_err("(%s) Command Write failed, CMD->%X\n", __func__, cmd_array[0]);
+ channel_zero->tx_state = HSI_LL_TX_STATE_IDLE;
+ return -1;
+ } else {
+ channel_zero->tx_state = HSI_LL_TX_STATE_IDLE;
+
+ pr_debug("[%s] CMD = %08x\n", __func__, cmd_array[0]);
+
+ hsi_cmd_history.tx_cmd[tx_cmd_history_p] = cmd_array[0];
+ hsi_cmd_history.tx_cmd_time[tx_cmd_history_p] = CURRENT_TIME;
+ tx_cmd_history_p++;
+ if (tx_cmd_history_p >= 50)
+ tx_cmd_history_p = 0;
+
+ if (saved_cmd_queue) {
+ saved_cmd_queue--;
+ cmd_array[0] = cmd_saved[saved_cmd_queue];
+
+ goto SEND_RETRY;
+ }
+
+ return 0;
+ }
+}
+
+void rx_stm(u32 cmd, u32 ch, u32 param)
+{
+ struct if_hsi_channel *channel;
+ u32 size = 0, tmp_cmd = 0, ret, i;
+ channel = &hsi_protocol_iface.channels[ch];
+
+ switch (cmd) {
+ case HSI_LL_MSG_OPEN_CONN:
+ pr_err("ERROR... OPEN_CONN Not supported. Should use OPEN_CONN_OCTECT instead.\n");
+ break;
+
+ case HSI_LL_MSG_ECHO:
+ pr_err("ERROR... HSI_LL_MSG_ECHO not supported.\n");
+ break;
+
+ case HSI_LL_MSG_CONN_CLOSED:
+ switch (channel->tx_state) {
+ case HSI_LL_TX_STATE_WAIT_FOR_CONN_CLOSED:
+ channel->tx_state = HSI_LL_TX_STATE_IDLE;
+
+ /* ACWAKE ->LOW */
+ ret = hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_ACWAKE_DOWN, NULL);
+ if (ret == 0)
+ pr_debug("ACWAKE pulled low in %s()\n", __func__);
+ else
+ pr_err("ACWAKE pulled low in %s() ERROR : %d\n", __func__, ret);
+
+ pr_debug("[%s] Received CONN_CLOSED. ch-> %d\n", __func__,ch);
+ break;
+
+ default:
+ pr_err("Wrong STATE for CONN_CLOSED\n");
+ }
+ break;
+
+ case HSI_LL_MSG_CANCEL_CONN:
+ pr_debug("Received CANCEL_CONN\n");
+ break;
+
+ case HSI_LL_MSG_ACK:
+ switch (channel->tx_state) {
+ case HSI_LL_TX_STATE_WAIT_FOR_ACK:
+ case HSI_LL_TX_STATE_SEND_OPEN_CONN:
+ //printk(KERN_INFO "ACK received %s()\n",__func__);
+
+ channel->tx_state = HSI_LL_TX_STATE_TX;
+ size = param;
+#if 0
+ // TEMP: send/read by 16 byte unit for v.11A(CP)
+ if ((size > 16) && (size % 16))
+ size += (16 - (size % 16));
+ else if (size < 16)
+ size = 16;
+#endif
+
+ // For es 2.1 ver.
+ if (size % 4)
+ size += (4 - (size % 4));
+
+ pr_debug("Writing %d bytes data on channel %d, tx_buf = %x, in %s()\n", size, ch, channel->tx_buf, __func__);
+ ret = hsi_proto_write(ch, channel->tx_buf, size);
+ channel->tx_state = HSI_LL_TX_STATE_WAIT_FOR_CONN_CLOSED;
+ wake_up_interruptible(&ipc_write_wait);
+ channel->tx_nak_count = 0;
+ break;
+
+ case HSI_LL_TX_STATE_CLOSED:/* ACK as response to CANCEL_CONN */
+ if (channel->rx_state == HSI_LL_RX_STATE_WAIT_FOR_CANCEL_CONN_ACK)
+ channel->rx_state = HSI_LL_RX_STATE_IDLE;
+ break;
+
+ case HSI_LL_TX_STATE_WAIT_FOR_CONF_ACK: /* ACK as response to CONF_RATE */
+ //TODO: SET CONF RATE
+ pr_debug("ACK Received for CONF_RATE\n");
+ break;
+
+ default:
+ pr_err("ACK Received for Unknown state\n");
+ }
+ break;
+
+ case HSI_LL_MSG_NAK:
+ switch (channel->tx_state) {
+ case HSI_LL_TX_STATE_WAIT_FOR_ACK:
+ printk(KERN_INFO "(%s) NAK received. ch->%d\n", __func__, ch);
+ //channel->tx_state = HSI_LL_TX_STATE_NACK;
+ if (channel->tx_nak_count < 10) {
+ msleep(10);
+
+ tmp_cmd = ((HSI_LL_MSG_OPEN_CONN_OCTET & 0x0000000F) << 28) |
+ ((ch & 0x000000FF) << 24);
+ for (i = 49; i >= 0; i--) {
+ if ((hsi_cmd_history.tx_cmd[i] & 0xFFF00000) == tmp_cmd)
+ break;
+ }
+ size = (hsi_cmd_history.tx_cmd[i] & 0x000FFFFF);
+
+ pr_debug("(%s) Re Send OPEN CONN ch->%d, size->%d, count->%d\n", __func__, ch, size, channel->tx_nak_count);
+
+ hsi_protocol_send_command(HSI_LL_MSG_OPEN_CONN_OCTET, ch, size);
+ channel->tx_nak_count++;
+ } else {
+ hsi_protocol_send_command(HSI_LL_MSG_BREAK, ch, size);
+ pr_debug("(%s) Sending MSG_BREAK. ch->%d\n", __func__, ch);
+ //TODO Reset All channels and inform IPC write about failure (Possibly by sending signal)
+ }
+ break;
+
+ case HSI_LL_TX_STATE_WAIT_FOR_CONF_ACK: /* NAK as response to CONF_RATE */
+ channel->tx_state = HSI_LL_TX_STATE_IDLE;
+ break;
+
+ default:
+ pr_err("ERROR - Received NAK in invalid state. state->%d\n", channel->tx_state);
+ }
+ break;
+
+ case HSI_LL_MSG_CONF_RATE:
+ //TODO: Set Conf Rate
+ pr_debug("CONF_RATE Received\n");
+ break;
+
+ case HSI_LL_MSG_OPEN_CONN_OCTET:
+ switch (channel->rx_state) {
+ /* case HSI_LL_RX_STATE_CLOSED: */
+ case HSI_LL_RX_STATE_IDLE:
+ pr_debug("OPEN_CONN_OCTET in %s(), ch-> %d\n", __func__, ch);
+ channel->rx_state = HSI_LL_RX_STATE_TO_ACK;
+ hsi_protocol_send_command(HSI_LL_MSG_ACK, ch, param);
+
+ channel->rx_count = param;
+ channel->rx_state = HSI_LL_RX_STATE_RX;
+ wake_up_interruptible(&ipc_read_wait);
+ break;
+
+ case HSI_LL_RX_STATE_BLOCKED:
+ /* TODO */
+ break;
+
+ default:
+ pr_err("OPEN_CONN_OCTET in invalid state, Current State -> %d\n", channel->rx_state);
+ pr_info("Sending NAK to channel-> %d\n", ch);
+ hsi_protocol_send_command(HSI_LL_MSG_NAK, ch, param);
+ }
+ break;
+
+ default:
+ pr_err("Invalid Command encountered in rx_state()\n");
+ }
+
+}
diff --git a/drivers/omap_hsi/hsi_protocol_if.c b/drivers/omap_hsi/hsi_protocol_if.c
new file mode 100644
index 0000000..ced5dae
--- /dev/null
+++ b/drivers/omap_hsi/hsi_protocol_if.c
@@ -0,0 +1,896 @@
+/*
+ * File - hsi_protocol_if.c
+ *
+ * Implements HSI protocol for Infineon Modem.
+ *
+ * Copyright (C) 2011 Samsung Electronics.
+ *
+ * Author: Rupesh Gujare <rupesh.g@samsung.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <asm/mach-types.h>
+#include <linux/ioctl.h>
+#include <linux/delay.h>
+#include <linux/ktime.h>
+#include <linux/bitmap.h>
+#include <linux/poll.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/proc_fs.h>
+
+#include <linux/hsi_driver_if.h>
+#include "hsi-protocol-if.h"
+
+//#define DEBUG 1
+//#define DEBUG_PHY_DATA 1
+
+#define HSI_CHANNEL_STATE_UNAVAIL (1 << 0)
+#define HSI_CHANNEL_STATE_READING (1 << 1)
+#define HSI_CHANNEL_STATE_WRITING (1 << 2)
+
+
+struct if_hsi_iface hsi_protocol_iface;
+wait_queue_head_t ipc_read_wait, ipc_write_wait;
+
+
+static void if_hsi_protocol_port_event(struct hsi_device *dev, unsigned int event,
+ void *arg);
+static int __devinit hsi_protocol_probe(struct hsi_device *dev);
+static int __devexit hsi_protocol_remove(struct hsi_device *dev);
+
+static struct hsi_device_driver if_hsi_protocol_driver = {
+ .ctrl_mask = ANY_HSI_CONTROLLER,
+ .probe = hsi_protocol_probe,
+ .remove = __devexit_p(hsi_protocol_remove),
+ .driver = {
+ .name = "hsi_protocol"
+ },
+};
+
+struct if_hsi_cmd hsi_cmd_history;
+int tx_cmd_history_p = 0;
+int rx_cmd_history_p = 0;
+
+static int if_hsi_read_on(int ch, u32 *data, unsigned int count)
+{
+ struct if_hsi_channel *channel;
+ int ret;
+
+ channel = &hsi_protocol_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+
+ spin_lock(&channel->lock);
+ if (channel->state & HSI_CHANNEL_STATE_READING) {
+ pr_err("Read still pending on channel %d\n", ch);
+ spin_unlock(&channel->lock);
+ return -EBUSY;
+ }
+ channel->state |= HSI_CHANNEL_STATE_READING;
+ channel->rx_data = data;
+ channel->rx_count = count;
+ spin_unlock(&channel->lock);
+
+ ret = hsi_read(channel->dev, data, count / 4);
+ dev_dbg(&channel->dev->device, "%s, ch = %d, ret = %d\n", __func__, ch,
+ ret);
+
+ return ret;
+}
+
+static void if_hsi_proto_read_done(struct hsi_device *dev, unsigned int size)
+{
+ struct if_hsi_channel *channel;
+ struct hsi_event ev;
+
+#ifdef DEBUG_PHY_DATA
+ u32 *tmp;
+ u32 i;
+#endif
+
+ //printk(KERN_INFO "if_hsi_proto_read_done() is called for ch-> %d\n", dev->n_ch);
+ channel = &hsi_protocol_iface.channels[dev->n_ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, dev->n_ch);
+ spin_lock(&channel->lock);
+ channel->state &= ~HSI_CHANNEL_STATE_READING;
+ ev.event = HSI_EV_IN;
+ ev.data = channel->rx_data;
+ ev.count = 4 * size;
+ spin_unlock(&channel->lock);
+
+#ifdef DEBUG_PHY_DATA
+ //Check received data -> Commented as it adds delay which causes MSG_BREAK
+ tmp = channel->rx_data;
+ printk(KERN_INFO "[%s](%d)(%d) RX = ", __func__, dev->n_ch, ev.count);
+ for (i = 0; i < ((size > 5) ? 5 : size); i++) {
+ printk(KERN_INFO "%08x ", *tmp);
+ tmp++;
+ }
+ printk(KERN_INFO "\n");
+#endif
+
+ if_notify(dev->n_ch, &ev);
+}
+
+int if_hsi_read(int ch, u32 *data, unsigned int count)
+{
+ int ret = 0;
+ struct if_hsi_channel *channel;
+ channel = &hsi_protocol_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ ret = if_hsi_read_on(ch, data, count);
+ return ret;
+}
+
+int if_hsi_poll(int ch)
+{
+ struct if_hsi_channel *channel;
+ int ret = 0;
+ channel = &hsi_protocol_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ ret = hsi_poll(channel->dev);
+ return ret;
+}
+
+static int if_hsi_write_on(int ch, u32 *address, unsigned int count)
+{
+ struct if_hsi_channel *channel;
+ int ret;
+
+ channel = &hsi_protocol_iface.channels[ch];
+
+ spin_lock(&channel->lock);
+ if (channel->state & HSI_CHANNEL_STATE_WRITING) {
+ pr_err("Write still pending on channel %d\n", ch);
+ printk(KERN_INFO "Write still pending on channel %d\n", ch);
+ spin_unlock(&channel->lock);
+ return -EBUSY;
+ }
+
+ channel->tx_data = address;
+ channel->tx_count = count;
+ channel->state |= HSI_CHANNEL_STATE_WRITING;
+ spin_unlock(&channel->lock);
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ ret = hsi_write(channel->dev, address, count / 4);
+ return ret;
+}
+
+
+static void if_hsi_proto_write_done(struct hsi_device *dev, unsigned int size)
+{
+ struct if_hsi_channel *channel;
+ struct hsi_event ev;
+
+#ifdef DEBUG_PHY_DATA
+ u32 *tmp;
+ u32 i;
+#endif
+
+ //printk(KERN_INFO "if_hsi_proto_write_done() is called for ch-> %d\n", dev->n_ch);
+ channel = &hsi_protocol_iface.channels[dev->n_ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, dev->n_ch);
+
+ spin_lock(&channel->lock);
+ channel->state &= ~HSI_CHANNEL_STATE_WRITING;
+ ev.event = HSI_EV_OUT;
+ ev.data = channel->tx_data;
+ ev.count = 4 * size;
+ spin_unlock(&channel->lock);
+
+#ifdef DEBUG_PHY_DATA
+ //Check Outgoing data, Commented as it adds delay which causes MSG_BREAK
+ tmp = channel->tx_data;
+ printk(KERN_INFO "[%s](%d)(%d) TX = ", __func__, dev->n_ch, ev.count);
+ for (i = 0; i < ((size > 5) ? 5 : size); i++) {
+ printk(KERN_INFO "%08x ", *tmp);
+ tmp++;
+ }
+ printk(KERN_INFO "\n");
+#endif
+
+ if_notify(dev->n_ch, &ev);
+
+}
+
+int if_hsi_write(int ch, u32 *data, unsigned int count)
+{
+ int ret = 0;
+ struct if_hsi_channel *channel;
+ channel = &hsi_protocol_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ ret = if_hsi_write_on(ch, data, count);
+ return ret;
+}
+
+void if_hsi_cancel_read(int ch)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_protocol_iface.channels[ch];
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__, ch);
+ if (channel->state & HSI_CHANNEL_STATE_READING)
+ hsi_read_cancel(channel->dev);
+ spin_lock(&channel->lock);
+ channel->state &= ~HSI_CHANNEL_STATE_READING;
+ spin_unlock(&channel->lock);
+}
+
+void if_hsi_set_wakeline(int ch, unsigned int state)
+{
+ struct if_hsi_channel *channel;
+ channel = &hsi_protocol_iface.channels[ch];
+ hsi_ioctl(channel->dev,
+ state ? HSI_IOCTL_ACWAKE_UP : HSI_IOCTL_ACWAKE_DOWN, NULL);
+}
+
+
+static void if_hsi_protocol_port_event(struct hsi_device *dev, unsigned int event,
+ void *arg)
+{
+ struct hsi_event ev;
+ int i;
+
+ ev.event = HSI_EV_EXCEP;
+ ev.data = (u32 *) 0;
+ ev.count = 0;
+
+
+ switch (event) {
+ case HSI_EVENT_BREAK_DETECTED:
+ pr_debug("%s, HWBREAK detected\n", __func__);
+ ev.data = (u32 *) HSI_HWBREAK;
+ for (i = 0; i < HSI_MAX_CHANNELS; i++) {
+ if (hsi_protocol_iface.channels[i].opened)
+ if_notify(i, &ev);
+ }
+ break;
+ case HSI_EVENT_HSR_DATAAVAILABLE:
+ i = (int)arg;
+ pr_debug("%s, HSI_EVENT_HSR_DATAAVAILABLE channel = %d\n",
+ __func__, i);
+ ev.event = HSI_EV_AVAIL;
+ if (hsi_protocol_iface.channels[i].opened)
+ if_notify(i, &ev);
+ break;
+ case HSI_EVENT_CAWAKE_UP:
+ pr_debug("%s, CAWAKE up\n", __func__);
+ break;
+ case HSI_EVENT_CAWAKE_DOWN:
+ pr_debug("%s, CAWAKE down\n", __func__);
+ break;
+ case HSI_EVENT_ERROR:
+ pr_debug("%s, HSI ERROR occured\n", __func__);
+ break;
+ default:
+ pr_warning("%s, Unknown event(%d)\n", __func__, event);
+ break;
+ }
+}
+
+int if_hsi_openchannel(struct if_hsi_channel *channel)
+{
+ int ret = 0;
+
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__,
+ channel->channel_id);
+ spin_lock(&channel->lock);
+
+ if (channel->state == HSI_CHANNEL_STATE_UNAVAIL) {
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ if (channel->opened) {
+ ret = -EBUSY;
+ goto leave;
+ }
+
+ if (!channel->dev) {
+ pr_err("Channel %d is not ready??\n", channel->channel_id);
+ ret = -ENODEV;
+ goto leave;
+ }
+ spin_unlock(&channel->lock);
+
+ ret = hsi_open(channel->dev);
+ spin_lock(&channel->lock);
+ if (ret < 0) {
+ pr_err("Could not open channel %d\n", channel->channel_id);
+ goto leave;
+ }
+
+ channel->opened = 1;
+ channel->tx_state = HSI_LL_TX_STATE_IDLE;
+ channel->rx_state = HSI_LL_RX_STATE_TO_CONN_READY;
+ printk(KERN_INFO "setting channel->opened=1 for channel %d\n", channel->dev->n_ch);
+leave:
+ spin_unlock(&channel->lock);
+ return ret;
+}
+
+int if_hsi_closechannel(struct if_hsi_channel *channel)
+{
+ int ret = 0;
+
+ dev_dbg(&channel->dev->device, "%s, ch = %d\n", __func__,
+ channel->channel_id);
+ spin_lock(&channel->lock);
+
+ if (!channel->opened)
+ goto leave;
+
+ if (!channel->dev) {
+ pr_err("Channel %d is not ready??\n", channel->channel_id);
+ ret = -ENODEV;
+ goto leave;
+ }
+
+ /* Stop any pending read/write */
+ if (channel->state & HSI_CHANNEL_STATE_READING) {
+ channel->state &= ~HSI_CHANNEL_STATE_READING;
+ spin_unlock(&channel->lock);
+ hsi_read_cancel(channel->dev);
+ spin_lock(&channel->lock);
+ }
+ if (channel->state & HSI_CHANNEL_STATE_WRITING) {
+ channel->state &= ~HSI_CHANNEL_STATE_WRITING;
+
+ spin_unlock(&channel->lock);
+ hsi_write_cancel(channel->dev);
+ } else
+ spin_unlock(&channel->lock);
+
+ hsi_close(channel->dev);
+
+ spin_lock(&channel->lock);
+ channel->opened = 0;
+ channel->tx_state = HSI_LL_TX_STATE_CLOSED;
+ channel->rx_state = HSI_LL_RX_STATE_CLOSED;
+leave:
+ spin_unlock(&channel->lock);
+ return ret;
+}
+
+
+/* Read Thread
+* Should be responsible for handling commands
+* Should wait on port events - waitqueue
+*
+*/
+static int hsi_read_thrd(void *data)
+{
+ u32 cmd_data[4], cmd, channel, param = 0;
+ int ret;
+
+ printk(KERN_INFO "Inside read thread\n");
+ while (1) {
+ /*Call hsi_proto_read*/
+ /*Read 16 bytes due to Modem limitation*/
+ //hsi_proto_read(0, cmd_data, (4 * 4));
+
+ // For es 2.1 ver.
+ hsi_proto_read(0, cmd_data, 4);
+
+ hsi_cmd_history.rx_cmd[rx_cmd_history_p] = cmd_data[0];
+ hsi_cmd_history.rx_cmd_time[rx_cmd_history_p] = CURRENT_TIME;
+ rx_cmd_history_p++;
+ if (rx_cmd_history_p >= 50)
+ rx_cmd_history_p = 0;
+
+ /*Decode Command*/
+ ret = hsi_decode_cmd(&cmd_data[0], &cmd, &channel, &param);
+ if (ret != 0) {
+ pr_err("Can not decode command\n");
+ } else {
+ printk(KERN_INFO "%s(),CMD Received-> %x, ch-> %d, param-> %d.\n", __func__, cmd, channel, param);
+ /*Rx State Machine*/
+ rx_stm(cmd, channel, param);
+ }
+ }
+ return 0;
+}
+
+
+int hsi_start_protocol(void)
+{
+ struct hst_ctx tx_config;
+ struct hsr_ctx rx_config;
+ int i, ret = 0;
+
+ printk(KERN_INFO "In function %s()\n", __func__);
+ /*Open All channels */
+ for (i = 0; i <= 5; i++) {
+ ret = if_hsi_openchannel(&hsi_protocol_iface.channels[i]);
+ if (ret < 0)
+ pr_err("Can not Open channel->%d . Can not start HSI protocol\n", i);
+ else
+ printk(KERN_INFO "Channel->%d Open Successful\n", i);
+
+ /*Set Rx Config*/
+ hsi_ioctl(hsi_protocol_iface.channels[i].dev, HSI_IOCTL_GET_RX, &rx_config);
+ rx_config.mode = 2;
+ rx_config.divisor = 1;
+ rx_config.channels = HSI_MAX_CHANNELS;
+ ret = hsi_ioctl(hsi_protocol_iface.channels[i].dev, HSI_IOCTL_SET_RX, &rx_config);
+ if (ret == 0)
+ printk(KERN_INFO "SET_RX Successful for ch->%d\n", i);
+
+ /*Set Tx Config*/
+ hsi_ioctl(hsi_protocol_iface.channels[i].dev, HSI_IOCTL_GET_TX, &tx_config);
+ tx_config.mode = 2;
+ tx_config.divisor = 1;
+ tx_config.channels = HSI_MAX_CHANNELS;
+ ret = hsi_ioctl(hsi_protocol_iface.channels[i].dev, HSI_IOCTL_SET_TX, &tx_config);
+ if (ret == 0)
+ printk(KERN_INFO "SET_TX Successful for ch->%d\n", i);
+ }
+ /*Make channel-0 tx_state to IDLE*/
+ hsi_protocol_iface.channels[0].tx_state = HSI_LL_TX_STATE_IDLE;
+ return ret;
+}
+EXPORT_SYMBOL(hsi_start_protocol);
+
+static int hsi_protocol_proc(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ char *p = page;
+ int len, i;
+
+ p += sprintf(p, "======= HISTORY OF CMD =======\n");
+ p += sprintf(p, " tx_cmd_history_p : %d\n", tx_cmd_history_p);
+ p += sprintf(p, " rx_cmd_history_p : %d\n", rx_cmd_history_p);
+ for (i = 0; i < 50; i++) {
+ p += sprintf(p, " [%d] tx : 0x%08x(%lu.%09lu), rx : 0x%08x(%lu.%09lu)\n",
+ i, hsi_cmd_history.tx_cmd[i], (unsigned long)hsi_cmd_history.tx_cmd_time[i].tv_sec, (unsigned long)hsi_cmd_history.tx_cmd_time[i].tv_nsec,
+ hsi_cmd_history.rx_cmd[i], (unsigned long)hsi_cmd_history.rx_cmd_time[i].tv_sec, (unsigned long)hsi_cmd_history.rx_cmd_time[i].tv_nsec);
+ }
+ p += sprintf(p, "======= HISTORY OF CMD =======\n");
+
+ len = (p - page) - off;
+ if (len < 0)
+ len = 0;
+
+ *eof = (len <= count) ? 1 : 0;
+ *start = page + off;
+
+ return len;
+}
+
+int __devexit hsi_protocol_remove(struct hsi_device *dev)
+{
+ struct if_hsi_channel *channel;
+ unsigned long *address;
+ int port, ret;
+
+ //dev_dbg(&dev->device, "%s, port = %d, ch = %d\n", __func__, dev->n_p,
+ // dev->n_ch);
+
+ for (port = 0; port < HSI_MAX_PORTS; port++) {
+ if (if_hsi_protocol_driver.ch_mask[port])
+ break;
+ }
+
+ address = &if_hsi_protocol_driver.ch_mask[port];
+
+ spin_lock_bh(&hsi_protocol_iface.lock);
+ if (test_bit(dev->n_ch, address) && (dev->n_p == port)) {
+ hsi_set_read_cb(dev, NULL);
+ hsi_set_write_cb(dev, NULL);
+ hsi_set_port_event_cb(dev, NULL);
+ channel = &hsi_protocol_iface.channels[dev->n_ch];
+ channel->dev = NULL;
+ channel->state = HSI_CHANNEL_STATE_UNAVAIL;
+ ret = 0;
+ }
+ spin_unlock_bh(&hsi_protocol_iface.lock);
+
+ return ret;
+}
+
+int __devinit hsi_protocol_probe(struct hsi_device *dev)
+{
+ struct if_hsi_channel *channel;
+ unsigned long *address;
+ int port;
+
+ printk(KERN_INFO "Inside Function %s\n", __func__);
+ for (port = 0; port < HSI_MAX_PORTS; port++) {
+ if (if_hsi_protocol_driver.ch_mask[port])
+ break;
+ }
+
+ address = &if_hsi_protocol_driver.ch_mask[port];
+
+ spin_lock_bh(&hsi_protocol_iface.lock);
+ if (test_bit(dev->n_ch, address) && (dev->n_p == port)) {
+ printk(KERN_INFO "Regestering callback functions\n");
+ hsi_set_read_cb(dev, if_hsi_proto_read_done);
+ hsi_set_write_cb(dev, if_hsi_proto_write_done);
+ hsi_set_port_event_cb(dev, if_hsi_protocol_port_event);
+ channel = &hsi_protocol_iface.channels[dev->n_ch];
+ channel->dev = dev;
+ channel->state = 0;
+ channel->rx_state = HSI_LL_RX_STATE_CLOSED;
+ channel->tx_state = HSI_LL_TX_STATE_CLOSED;
+ channel->tx_count = 0;
+ channel->rx_count = 0;
+ channel->tx_nak_count = 0;
+ channel->rx_nak_count = 0;
+ channel->rx_buf = NULL;
+ channel->tx_buf = NULL;
+ hsi_protocol_iface.init_chan_map ^= (1 << dev->n_ch);
+ }
+ spin_unlock_bh(&hsi_protocol_iface.lock);
+
+ return 0;
+
+}
+
+
+int __init if_hsi_init(void)
+{
+ struct if_hsi_channel *channel;
+ int i, ret;
+ struct proc_dir_entry *dir;
+
+ for (i = 0; i < HSI_MAX_PORTS; i++)
+ if_hsi_protocol_driver.ch_mask[i] = 0;
+
+ for (i = 0; i < HSI_MAX_CHANNELS; i++) {
+ channel = &hsi_protocol_iface.channels[i];
+ channel->dev = NULL;
+ channel->opened = 0;
+ channel->state = HSI_CHANNEL_STATE_UNAVAIL;
+ channel->channel_id = i;
+ spin_lock_init(&channel->lock);
+ }
+
+ /*Initialize waitqueue for IPC read*/
+ init_waitqueue_head(&ipc_read_wait);
+ init_waitqueue_head(&ipc_write_wait);
+
+ /*Select All Channels of PORT-1.*/
+ if_hsi_protocol_driver.ch_mask[0] = CHANNEL_MASK;
+
+ ret = hsi_register_driver(&if_hsi_protocol_driver);
+ if (ret)
+ pr_err("Error while registering HSI driver %d", ret);
+
+ dir = create_proc_read_entry("driver/hsi_cmd", 0, 0, hsi_protocol_proc, NULL);
+ if (dir == NULL)
+ printk(KERN_INFO "create_proc_read_entry Fail.\n");
+ printk(KERN_INFO "create_proc_read_entry Done.\n");
+
+ return ret;
+}
+
+int __devexit if_hsi_exit(void)
+{
+ struct if_hsi_channel *channel;
+ unsigned long *address;
+ int i, port;
+
+ pr_debug("%s\n", __func__);
+
+ for (port = 0; port < HSI_MAX_PORTS; port++) {
+ if (if_hsi_protocol_driver.ch_mask[port])
+ break;
+ }
+
+ address = &if_hsi_protocol_driver.ch_mask[port];
+
+ for (i = 0; i < HSI_MAX_CHANNELS; i++) {
+ channel = &hsi_protocol_iface.channels[i];
+ if (channel->opened) {
+ if_hsi_set_wakeline(i, HSI_IOCTL_ACWAKE_DOWN);
+ if_hsi_closechannel(channel);
+ }
+ }
+
+ hsi_unregister_driver(&if_hsi_protocol_driver);
+ return 0;
+
+}
+
+u32 initialization = 0;
+
+/*Write data to channel*/
+int write_hsi(u32 ch, u32 *data, int length)
+{
+ int ret;
+ //u32 cmd[4] = {0x00000000, 0xAAAAAAAA, 0xBBBBBBBB, 0xCCCCCCCC};
+ struct if_hsi_channel *channel;
+ struct task_struct *read_thread;
+
+ channel = &hsi_protocol_iface.channels[ch];
+ channel->tx_buf = data;
+ channel->tx_count = 0;
+
+ //cmd[0] = protocol_create_cmd(HSI_LL_MSG_OPEN_CONN_OCTET, ch, (void *)&length);
+ //printk(KERN_INFO "data ptr is %x\n", data);
+
+ if (initialization == 0) {
+
+#if 0
+ /* ACWAKE ->HIGH */
+ ret = hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_ACWAKE_UP, NULL);
+ if (ret == 0)
+ printk(KERN_INFO "ACWAKE pulled high in %s()\n", __func__);
+ else
+ printk(KERN_INFO "ACWAKE pulled high in %s() ERROR : %d\n", __func__, ret);
+#endif
+
+ /*Creating read thread*/
+ read_thread = kthread_run(hsi_read_thrd, NULL, "hsi_read_thread");
+
+ initialization++;
+ }
+ /*Wait till previous data transfer is over*/
+ while (channel->tx_state != HSI_LL_TX_STATE_IDLE) {
+ //printk(KERN_INFO "Wait 5ms previous data transfer isn't over %s()\n", __func__);
+
+ //msleep(5);
+
+ return -EAGAIN;
+ }
+
+#if 1
+ /* ACWAKE ->HIGH */
+ ret = hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_ACWAKE_UP, NULL);
+ if (ret == 0)
+ printk(KERN_INFO "ACWAKE pulled high in %s()\n", __func__);
+ else
+ printk(KERN_INFO "ACWAKE pulled high in %s() ERROR : %d\n", __func__, ret);
+#endif
+
+ channel->tx_state = HSI_LL_TX_STATE_WAIT_FOR_ACK;
+
+ //send_cmd(cmd, channel, data)
+ //ret = hsi_proto_write(0, &cmd, 4*4);
+ //printk(KERN_INFO "Write returned %d\n", ret);
+ hsi_protocol_send_command(HSI_LL_MSG_OPEN_CONN_OCTET, ch, length);
+
+ wait_event_interruptible(ipc_write_wait, channel->tx_count != 0);
+
+ return channel->tx_count;
+
+
+}
+EXPORT_SYMBOL(write_hsi);
+
+
+int read_hsi(u8 *data, u32 ch, u32 *length)
+{
+ int ret, size, tmp, actual_length;
+ struct if_hsi_channel *channel;
+
+ channel = &hsi_protocol_iface.channels[ch];
+ channel->rx_state = HSI_LL_RX_STATE_IDLE;
+
+ //printk(KERN_INFO "In read_hsi() function, Sleeping ... channel-> %d\n", ch);
+ wait_event_interruptible(ipc_read_wait, (channel->rx_count != 0));
+ //printk(KERN_INFO "In read_hsi() function, Waking Up ... channel-> %d\n", ch);
+
+ actual_length = channel->rx_count;
+ size = channel->rx_count;
+
+#if 0
+ // TEMP: send/read by 16 byte unit for v.11A(CP)
+ if ((size > 16) && (size % 16))
+ size += (16 - (size % 16));
+ else if (size < 16)
+ size = 16;
+#endif
+
+ // For es 2.1 ver.
+ if (size % 4)
+ size += (4 - (size % 4));
+
+ ret = hsi_proto_read(ch, (u32 *)data, size);
+ if (ret < 0)
+ printk(KERN_INFO "Read in IPC failed, %s()\n", __func__);
+
+ //printk(KERN_INFO "%s() read returned %d, actual_length = %d, ch-> %d\n", __func__, ret, actual_length, ch);
+ //printk(KERN_INFO "%s() sending CONN_CLOSED.\n", __func__);
+ tmp = hsi_protocol_send_command(HSI_LL_MSG_CONN_CLOSED, ch, 0);
+ //printk(KERN_INFO "%s() Sending CONN_CLOSED Finished. ret = %d\n", __func__, tmp);
+
+ *length = actual_length;
+ channel->rx_count = 0;
+
+ //printk(KERN_INFO "%s() RETURNING TO IPC with ret = %d\n", __func__, ret);
+ return ret;
+
+}
+EXPORT_SYMBOL(read_hsi);
+
+
+//========================================================//
+// ++ Flashless Boot. ++ //
+//========================================================//
+int hsi_start_protocol_single(void)
+{
+ int ret = 0;
+
+ struct hst_ctx tx_config;
+ struct hsr_ctx rx_config;
+
+ /*Open channel 0 */
+ ret = if_hsi_openchannel(&hsi_protocol_iface.channels[0]);
+ if (ret < 0) {
+ pr_err("Can not Open channel 0. Can not start HSI protocol\n");
+ goto err;
+ } else
+ printk(KERN_INFO "if_hsi_openchannel() returned %d\n", ret);
+
+
+ /*Set Tx Config*/
+ hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_GET_TX, &tx_config);
+ tx_config.mode = 2;
+ tx_config.channels = 1;
+ tx_config.divisor = 0;
+ ret = hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_SET_TX, &tx_config);
+ if (ret < 0) {
+ printk(KERN_INFO "write_hsi_direct : SET_TX Fail : %d\n", ret);
+ return ret;
+ }
+
+ hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_GET_RX, &rx_config);
+ rx_config.mode = 2;
+ rx_config.channels = 1;
+ rx_config.divisor = 0;
+ //rx_config.timeout = HZ / 2;
+ ret = hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_SET_RX, &rx_config);
+ if (ret < 0) {
+ printk(KERN_INFO "write_hsi_direct : SET_RX Fail : %d\n", ret);
+ return ret;
+ }
+
+ /* ACWAKE ->HIGH */
+ ret = hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_ACWAKE_UP, NULL);
+ if (ret == 0)
+ printk(KERN_INFO "ACWAKE pulled high in %s()\n", __func__);
+
+err:
+
+ return ret;
+}
+EXPORT_SYMBOL(hsi_start_protocol_single);
+
+int hsi_reconfigure_protocol(void)
+{
+ int ret = 0;
+
+ /* ACWAKE ->LOW */
+ ret = hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_ACWAKE_DOWN, NULL);
+ if (ret == 0)
+ printk(KERN_INFO "ACWAKE pulled low in %s()\n", __func__);
+ else
+ printk(KERN_INFO "ACWAKE down fail!! %d\n", ret);
+
+
+ /*Clse channel 0 */
+ ret = if_hsi_closechannel(&hsi_protocol_iface.channels[0]);
+ if (ret < 0) {
+ pr_err("Can not Close channel 0. Can not Stop HSI protocol for flashless\n");
+ goto err;
+ }
+
+
+ printk(KERN_INFO "(%s)(%d) hsi_start_protocol Start.\n", __func__, __LINE__);
+ hsi_start_protocol();
+ printk(KERN_INFO "(%s)(%d) hsi_start_protocol Done.\n", __func__, __LINE__);
+
+err:
+
+ return ret;
+}
+EXPORT_SYMBOL(hsi_reconfigure_protocol);
+
+int write_hsi_direct(u32 *data, int length)
+{
+ int retval = 0;
+#if 0
+ struct hst_ctx tx_config;
+
+
+ printk(KERN_INFO "write_hsi_direct : len : %d\n", length);
+ hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_GET_TX, &tx_config);
+ tx_config.mode = 2;
+ tx_config.channels = 1;
+ tx_config.divisor = 47;
+ retval = hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_SET_TX, &tx_config);
+ if (retval < 0) {
+ printk(KERN_INFO "write_hsi_direct : SET_TX Fail : %d\n", retval);
+ return retval;
+ }
+ printk(KERN_INFO "write_hsi_direct : SET_TX Successful\n");
+
+ retval = hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_ACWAKE_UP, NULL);
+ if (retval < 0) {
+ printk(KERN_INFO "write_hsi_direct : ACWAKE High Fail : %d\n", retval);
+ return retval;
+ }
+#endif
+
+#if 0
+ if ((length > 16) && (length % 4))
+ length += (4 - (length % 4));
+ else if (length < 16)
+ length = 16;
+#endif
+
+// printk(KERN_INFO "write_hsi_direct : new len : %d\n", length);
+
+ retval = hsi_proto_write(0, data, length);
+ if (retval < 0) {
+ printk(KERN_INFO "write_hsi_direct : hsi_proto_write Fail : %d\n", retval);
+ return retval;
+ }
+ //printk(KERN_INFO "write_hsi_direct : Write returned %d\n", retval);
+
+ return retval;
+}
+EXPORT_SYMBOL(write_hsi_direct);
+
+int read_hsi_direct(u32 *data, int length)
+{
+ int retval = 0;
+#if 0
+ struct hsr_ctx rx_config;
+
+
+ printk(KERN_INFO "read_hsi_direct : len : %d\n", length);
+ hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_GET_RX, &rx_config);
+ rx_config.mode = 2;
+ rx_config.channels = 1;
+ rx_config.divisor = 47;
+ retval = hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_SET_RX, &rx_config);
+ if (retval < 0) {
+ printk(KERN_INFO "read_hsi_direct : SET_RX Fail : %d\n", retval);
+ return retval;
+ }
+ printk(KERN_INFO "read_hsi_direct : SET_RX Successful\n");
+
+ retval = hsi_ioctl(hsi_protocol_iface.channels[0].dev, HSI_IOCTL_ACWAKE_UP, NULL);
+ if (retval < 0) {
+ printk(KERN_INFO "read_hsi_direct : ACWAKE High Fail : %d\n", retval);
+ return retval;
+ }
+ printk(KERN_INFO "read_hsi_direct : ACWAKE High\n");
+#endif
+
+#if 0
+ if ((length > 16) && (length % 4))
+ length += (4 - (length % 4));
+ else if (length < 16)
+ length = 16;
+#endif
+ //printk(KERN_INFO "read_hsi_direct : new len : %d\n", length);
+
+ retval = hsi_proto_read(0, data, length);
+ if (retval < 0) {
+ printk(KERN_INFO "read_hsi_direct : hsi_proto_read Fail : %d\n", retval);
+ return retval;
+ }
+ //printk(KERN_INFO "read_hsi_direct : Read returned %d\n", retval);
+
+ return retval;
+}
+EXPORT_SYMBOL(read_hsi_direct);
+
+//========================================================//
+// -- Flashless Boot. -- //
+//========================================================//
diff --git a/drivers/power/max17040_battery.c b/drivers/power/max17040_battery.c
index 2f2f9a6..61b6471 100644
--- a/drivers/power/max17040_battery.c
+++ b/drivers/power/max17040_battery.c
@@ -107,6 +107,11 @@ static void max17040_reset(struct i2c_client *client)
{
max17040_write_reg(client, MAX17040_CMD_MSB, 0x54);
max17040_write_reg(client, MAX17040_CMD_LSB, 0x00);
+
+ msleep(125);
+
+ max17040_write_reg(client, MAX17040_MODE_MSB, 0x40);
+ max17040_write_reg(client, MAX17040_MODE_LSB, 0x00);
}
static void max17040_get_vcell(struct i2c_client *client)
@@ -118,7 +123,7 @@ static void max17040_get_vcell(struct i2c_client *client)
msb = max17040_read_reg(client, MAX17040_VCELL_MSB);
lsb = max17040_read_reg(client, MAX17040_VCELL_LSB);
- chip->vcell = (msb << 4) + (lsb >> 4);
+ chip->vcell = ((msb << 4) + (lsb >> 4)) * 1250;
}
static void max17040_get_soc(struct i2c_client *client)
@@ -130,7 +135,7 @@ static void max17040_get_soc(struct i2c_client *client)
msb = max17040_read_reg(client, MAX17040_SOC_MSB);
lsb = max17040_read_reg(client, MAX17040_SOC_LSB);
- chip->soc = msb;
+ chip->soc = min(msb, (u8)100);
}
static void max17040_get_version(struct i2c_client *client)
@@ -187,7 +192,7 @@ static void max17040_work(struct work_struct *work)
max17040_get_online(chip->client);
max17040_get_status(chip->client);
- schedule_delayed_work(&chip->work, MAX17040_DELAY);
+ schedule_delayed_work(&chip->work, msecs_to_jiffies(MAX17040_DELAY));
}
static enum power_supply_property max17040_battery_props[] = {
@@ -233,7 +238,7 @@ static int __devinit max17040_probe(struct i2c_client *client,
max17040_get_version(client);
INIT_DELAYED_WORK_DEFERRABLE(&chip->work, max17040_work);
- schedule_delayed_work(&chip->work, MAX17040_DELAY);
+ schedule_delayed_work(&chip->work, msecs_to_jiffies(MAX17040_DELAY));
return 0;
}
@@ -263,7 +268,7 @@ static int max17040_resume(struct i2c_client *client)
{
struct max17040_chip *chip = i2c_get_clientdata(client);
- schedule_delayed_work(&chip->work, MAX17040_DELAY);
+ schedule_delayed_work(&chip->work, msecs_to_jiffies(MAX17040_DELAY));
return 0;
}
diff --git a/drivers/rpmsg/rpmsg_omx.c b/drivers/rpmsg/rpmsg_omx.c
index b27095f..75fd315 100644
--- a/drivers/rpmsg/rpmsg_omx.c
+++ b/drivers/rpmsg/rpmsg_omx.c
@@ -34,6 +34,7 @@
#include <linux/sched.h>
#include <linux/rpmsg.h>
#include <linux/rpmsg_omx.h>
+#include <linux/pm_qos_params.h>
#include <mach/tiler.h>
@@ -52,6 +53,7 @@ struct rpmsg_omx_service {
struct device *dev;
struct rpmsg_channel *rpdev;
int minor;
+ struct pm_qos_request_list *pm_qos;
};
struct rpmsg_omx_instance {
@@ -140,8 +142,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:
@@ -294,6 +298,9 @@ static int rpmsg_omx_open(struct inode *inode, struct file *filp)
dev_info(omxserv->dev, "local addr assigned: 0x%x\n", omx->ept->addr);
+ /* Request for CORE latency. REVISIT: Move to proper code path */
+ pm_qos_add_request(omxserv->pm_qos, PM_QOS_CPU_DMA_LATENCY, 400);
+
return 0;
}
@@ -307,6 +314,10 @@ static int rpmsg_omx_release(struct inode *inode, struct file *filp)
int use, ret;
/* todo: release resources here */
+ if (omxserv->pm_qos) {
+ pm_qos_remove_request(omxserv->pm_qos);
+ kfree(omxserv->pm_qos);
+ }
/* send a disconnect msg with the OMX instance addr */
hdr->type = OMX_DISCONNECT;
@@ -501,6 +512,12 @@ static int rpmsg_omx_probe(struct rpmsg_channel *rpdev)
goto clean_cdev;
}
+ omxserv->pm_qos = kzalloc(sizeof(struct pm_qos_request_list),
+ GFP_KERNEL);
+ if (!omxserv->pm_qos) {
+ goto clean_cdev;
+ }
+
dev_set_drvdata(&rpdev->dev, omxserv);
dev_info(omxserv->dev, "new OMX connection srv channel: %u -> %u!\n",
diff --git a/drivers/rpmsg/rpmsg_resmgr.c b/drivers/rpmsg/rpmsg_resmgr.c
index 3fbf20d..293e0ed 100644
--- a/drivers/rpmsg/rpmsg_resmgr.c
+++ b/drivers/rpmsg/rpmsg_resmgr.c
@@ -43,6 +43,8 @@
#define MHZ 1000000
#define MAX_MSG (sizeof(struct rprm_ack) + sizeof(struct rprm_sdma))
+#define RPRM_DEBUG_CONSTRAINTS
+
static struct dentry *rprm_dbg;
static char *regulator_name[] = {
@@ -76,6 +78,14 @@ static const char *rname(u32 type) {
return rnames[type];
}
+#ifdef RPRM_DEBUG_CONSTRAINTS
+static struct rprm_constraints_data test_data = {
+ .mask = 0x6,
+ .latency = 10,
+ .bandwidth = 800000,
+};
+#endif
+
struct rprm_elem {
struct list_head next;
u32 src;
@@ -532,6 +542,13 @@ static int rprm_rpres_request(struct rprm_elem *e, int type)
}
e->handle = res;
+#ifdef RPRM_DEBUG_CONSTRAINTS
+ if (!strcmp(res_name, "rpres_iss")) {
+ _set_constraints(e, &test_data);
+ e->constraints->mask = test_data.mask;
+ }
+#endif
+
return 0;
}
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
index 5dde5fc..f6ff61b 100644
--- a/drivers/rpmsg/virtio_rpmsg_bus.c
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -484,8 +484,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;
@@ -532,8 +534,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/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index f7f0872..17b1e67 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -915,6 +915,7 @@ 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);
@@ -926,12 +927,11 @@ void musb_start(struct musb *musb)
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);
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 6aeb363..9412410 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1704,7 +1704,9 @@ static int musb_gadget_pullup(struct usb_gadget *gadget, int is_on)
spin_lock_irqsave(&musb->lock, flags);
if (is_on != musb->softconnect) {
musb->softconnect = is_on;
+ pm_runtime_get_sync(musb->controller);
musb_pullup(musb, is_on);
+ pm_runtime_put(musb->controller);
}
spin_unlock_irqrestore(&musb->lock, flags);
return 0;
@@ -1917,13 +1919,6 @@ int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
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);
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..17ca5f1
--- /dev/null
+++ b/drivers/video/omap2/displays/panel-s6e8aa0.c
@@ -0,0 +1,982 @@
+/*
+ * 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/delay.h>
+#include <linux/err.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/backlight.h>
+#include <linux/fb.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+
+
+#include <video/omapdss.h>
+
+#include <linux/platform_data/panel-s6e8aa0.h>
+
+/* DSI Command Virtual channel */
+#define CMD_VC_CHANNEL 1
+
+#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 = 80842,
+ .hfp = 158,
+ .hsw = 2,
+ .hbp = 160,
+ .vfp = 13,
+ .vsw = 1,
+ .vbp = 2,
+};
+
+static const struct s6e8aa0_gamma_adj_points default_gamma_adj_points = {
+ .v0 = BV_0,
+ .v1 = BV_1,
+ .v15 = BV_15,
+ .v35 = BV_35,
+ .v59 = BV_59,
+ .v87 = BV_87,
+ .v171 = BV_171,
+ .v255 = BV_255,
+};
+
+struct s6e8aa0_gamma_reg_offsets {
+ s16 v[3][7];
+};
+
+struct s6e8aa0_data {
+ struct mutex lock;
+
+ struct omap_dss_device *dssdev;
+ struct backlight_device *bldev;
+ bool enabled;
+ u8 rotate;
+ bool mirror;
+ bool use_dsi_bl;
+ unsigned int bl;
+ const struct s6e8aa0_gamma_adj_points *gamma_adj_points;
+ struct s6e8aa0_gamma_reg_offsets gamma_reg_offsets;
+ u32 color_mult[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;
+};
+
+const u8 s6e8aa0_init_pre[] = {
+ 0xF0,
+ 0x5A,
+ 0x5A,
+};
+
+const u8 s6e8aa0_mtp_unlock[] = {
+ 0xF1,
+ 0x5A,
+ 0x5A,
+};
+
+const u8 s6e8aa0_mtp_lock[] = {
+ 0xF1,
+ 0xA5,
+ 0xA5,
+};
+
+const u8 s6e8aa0_init_panel[] = {
+ 0xF8,
+ 0x25,
+ 0x34,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x8D,
+ 0x00,
+ 0x43,
+ 0x6E,
+ 0x10,
+ 0x27,
+ 0x00,
+ 0x00,
+ 0x10,
+ 0x00,
+ 0x00,
+ 0x20,
+ 0x02,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x00,
+ 0x02,
+ 0x08,
+ 0x08,
+ 0x23,
+ 0x23,
+ 0xC0,
+ 0xC1,
+ 0x01,
+ 0x81,
+ 0xC1,
+ 0x00,
+ 0xC8,
+ 0xC1,
+ 0xD3,
+ 0x01,
+};
+
+const u8 s6e8aa0_init_display[] = {
+ 0xF2,
+ 0x80,
+ 0x03,
+ 0x0D,
+};
+
+const u8 s6e8aa0_init_post0[] = {
+ 0xF6,
+ 0x00,
+ 0x02,
+ 0x00,
+};
+
+const u8 s6e8aa0_init_post1[] = {
+ 0xB6,
+ 0x0C,
+ 0x02,
+ 0x03,
+ 0x32,
+ 0xFF,
+ 0x44,
+ 0x44,
+ 0xC0,
+ 0x00,
+};
+
+const u8 s6e8aa0_init_post2[] = {
+ 0xD9,
+ 0x14,
+ 0x40,
+ 0x0C,
+ 0xCB,
+ 0xCE,
+ 0x6E,
+ 0xC4,
+ 0x0F,
+ 0x40,
+ 0x40,
+ 0xCE,
+ 0x00,
+ 0x60,
+ 0x19,
+};
+
+static int s6e8aa0_write(struct omap_dss_device *dssdev, u8 val)
+{
+ return dsi_vc_dcs_write(dssdev, 1, &val, 1);
+}
+
+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 *
+ int ret;
+ msleep(10); // XxX: why do we have to wait
+
+ ret = dsi_vc_dcs_write(dssdev, 1, (u8 *)data, len);
+ msleep(10); // XxX: why do we have to wait
+ return ret;
+}
+
+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);
+}
+
+/***********************
+*** 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_gamma_lookup(struct s6e8aa0_data *s6,
+ u8 brightness, u32 val, int c)
+{
+ int i;
+ u32 bl = 0;
+ u32 bh = 0;
+ u32 vl = 0;
+ u32 vh;
+ u32 b;
+ u32 ret;
+ u64 tmp;
+ struct panel_s6e8aa0_data *pdata = s6->pdata;
+ const struct s6e8aa0_gamma_adj_points *bv = s6->gamma_adj_points;
+
+ if (!val) {
+ b = 0;
+ } else {
+ tmp = bv->v255 - bv->v0;
+ tmp *= brightness;
+ do_div(tmp, 255);
+
+ tmp *= s6->color_mult[c];
+ do_div(tmp, 0xffffffff);
+
+ tmp *= (val - bv->v0);
+ do_div(tmp, bv->v255 - bv->v0);
+ b = tmp + bv->v0;
+ }
+
+ for (i = 0; i < pdata->gamma_table_size; i++) {
+ bl = bh;
+ bh = pdata->gamma_table[i].brightness;
+ if (bh >= b)
+ break;
+ }
+ vh = pdata->gamma_table[i].v[c];
+ if (i == 0 || (b - bl) == 0) {
+ ret = vl = vh;
+ } else {
+ vl = pdata->gamma_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 %3d %08x c %d, %08x, "
+ "found %08x:%08x, v %7d:%7d, ret %7d\n",
+ __func__, brightness, val, c, b, bl, bh, vl, vh, ret);
+
+ return ret;
+}
+
+static void s6e8aa0_setup_gamma_regs(struct s6e8aa0_data *s6, u8 gamma_regs[])
+{
+ 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 v0 = s6e8aa0_gamma_lookup(s6, brightness, BV_0, c);
+ u32 vx[7];
+ u32 v1;
+ u32 v255;
+
+ v1 = vx[0] = s6e8aa0_gamma_lookup(s6, brightness, bv->v1, c);
+ adj = 600 - 5 - DIV_ROUND_CLOSEST(600 * v1, v0);
+ adj -= s6->gamma_reg_offsets.v[c][0];
+ if (adj > 140) {
+ pr_debug("%s: bad adj value %d, v0 %d, v1 %d, c %d\n",
+ __func__, adj, v0, v1, c);
+ if ((int)adj < 0)
+ adj = 0;
+ else
+ adj = 140;
+ }
+ gamma_regs[c] = adj;
+
+ v255 = s6e8aa0_gamma_lookup(s6, brightness, bv->v255, c);
+ vx[6] = v255;
+ adj = 600 - 100 - DIV_ROUND_CLOSEST(600 * v255, v0);
+ adj -= s6->gamma_reg_offsets.v[c][6];
+ if (adj > 380) {
+ pr_debug("%s: bad adj value %d, v0 %d, v255 %d, c %d\n",
+ __func__, adj, v0, v255, c);
+ if ((int)adj < 0)
+ adj = 0;
+ else
+ adj = 380;
+ }
+ gamma_regs[3 * 6 + 2 * c] = adj >> 8;
+ gamma_regs[3 * 6 + 2 * c + 1] = (adj & 0xff);
+
+ vx[1] = s6e8aa0_gamma_lookup(s6, brightness, bv->v15, c);
+ vx[2] = s6e8aa0_gamma_lookup(s6, brightness, bv->v35, c);
+ vx[3] = s6e8aa0_gamma_lookup(s6, brightness, bv->v59, c);
+ vx[4] = s6e8aa0_gamma_lookup(s6, brightness, bv->v87, c);
+ vx[5] = s6e8aa0_gamma_lookup(s6, brightness, bv->v171, c);
+
+ for (i = 5; i >= 1; i--) {
+ if (v1 <= vx[i + 1]) {
+ adj = -1;
+ } else {
+ adj = DIV_ROUND_CLOSEST(320 * (v1 - vx[i]),
+ v1 - vx[i + 1]) - 65;
+ adj -= s6->gamma_reg_offsets.v[c][i];
+ }
+ if (adj > 255) {
+ pr_debug("%s: bad adj value %d, "
+ "vh %d, v %d, c %d\n",
+ __func__, adj, vx[i + 1], vx[i], c);
+ if ((int)adj < 0)
+ adj = 0;
+ else
+ adj = 255;
+ }
+ gamma_regs[3 * i + c] = adj;
+ }
+ }
+}
+
+static int s6e8aa0_update_brightness(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ int ret = 0;
+ u8 gamma_regs[26];
+
+ gamma_regs[0] = 0xFA;
+ gamma_regs[1] = 0x01;
+
+ s6e8aa0_setup_gamma_regs(s6, gamma_regs + 2);
+ s6e8aa0_write_block(dssdev, gamma_regs, sizeof(gamma_regs));
+ s6e8aa0_write_reg(dssdev, 0xF7, 0x01);
+
+ return ret;
+}
+
+static u64 s6e8aa0_voltage_lookup(struct s6e8aa0_data *s6, int c, u32 v)
+{
+ int i;
+ u32 vh = ~0, vl = ~0;
+ u32 bl, 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 void s6e8aa0_adjust_brightness_from_mtp(struct s6e8aa0_data *s6)
+{
+ int c;
+ u32 v255[3];
+ u64 bc[3];
+ u64 bcmax;
+ int shift;
+ struct panel_s6e8aa0_data *pdata = s6->pdata;
+ const struct s6e8aa0_gamma_reg_offsets *offset = &s6->gamma_reg_offsets;
+ const u16 *factory_v255_regs = pdata->factory_v255_regs;
+
+ for (c = 0; c < 3; c++) {
+ int scale = s6e8aa0_gamma_lookup(s6, 255, BV_0, c);
+ v255[c] = DIV_ROUND_CLOSEST((600 - 100 - factory_v255_regs[c] -
+ offset->v[c][6]) * scale, 600);
+ bc[c] = s6e8aa0_voltage_lookup(s6, c, v255[c]);
+ }
+
+ shift = pdata->color_adj.rshift;
+ if (shift)
+ for (c = 0; c < 3; c++)
+ bc[c] = bc[c] * pdata->color_adj.mult[c] >> shift;
+
+ bcmax = 0xffffffff;
+ for (c = 0; c < 3; c++)
+ if (bc[c] > bcmax)
+ bcmax = bc[c];
+
+ if (bcmax != 0xffffffff) {
+ pr_warn("s6e8aa: factory calibration info is out of range: "
+ "scale to 0x%llx\n", bcmax);
+ bcmax += 1;
+ shift = fls(bcmax >> 32);
+ for (c = 0; c < 3; c++) {
+ bc[c] <<= 32 - shift;
+ do_div(bc[c], bcmax >> shift);
+ }
+ }
+
+ for (c = 0; c < 3; c++) {
+ s6->color_mult[c] = bc[c];
+ pr_info("s6e8aa: c%d, b-%08llx, got v %d, factory wants %d\n",
+ c, bc[c], s6e8aa0_gamma_lookup(s6, 255, BV_255, c),
+ v255[c]);
+ }
+}
+
+static s16 s9_to_s16(s16 v)
+{
+ return (s16)(v << 7) >> 7;
+}
+
+static void s6e8aa0_read_mtp_info(struct s6e8aa0_data *s6)
+{
+ int ret;
+ int c, i;
+ u8 mtp_data[24];
+ 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, 0xD3, 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 < 6; i++)
+ s6->gamma_reg_offsets.v[c][i] = (s8)mtp_data[c * 8 + i];
+
+ s6->gamma_reg_offsets.v[c][6] =
+ s9_to_s16(mtp_data[c * 8 + 6] << 8 |
+ mtp_data[c * 8 + 7]);
+ }
+}
+
+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 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->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;
+
+ 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;
+ }
+
+ 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);
+ 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);
+ if (!s6->color_mult[0]) {
+ s6e8aa0_read_mtp_info(s6);
+ s6e8aa0_adjust_brightness_from_mtp(s6);
+ }
+ s6e8aa0_write_block(dssdev, s6e8aa0_init_pre, ARRAY_SIZE(s6e8aa0_init_pre));
+
+ s6e8aa0_write(dssdev, 0x11);
+
+ s6e8aa0_write_block(dssdev, s6e8aa0_init_panel, ARRAY_SIZE(s6e8aa0_init_panel));
+ s6e8aa0_write_block(dssdev, s6e8aa0_init_display, ARRAY_SIZE(s6e8aa0_init_display));
+ s6e8aa0_update_brightness(dssdev);
+
+ s6e8aa0_write_reg(dssdev, 0xF7, 0x01);
+
+ s6e8aa0_write_block(dssdev, s6e8aa0_init_post0, ARRAY_SIZE(s6e8aa0_init_post0));
+ s6e8aa0_write_block(dssdev, s6e8aa0_init_post1, ARRAY_SIZE(s6e8aa0_init_post1));
+ s6e8aa0_write_block(dssdev, s6e8aa0_init_post2, ARRAY_SIZE(s6e8aa0_init_post1));
+
+ msleep(250); //XXX: find minimum time
+
+ s6e8aa0_write(dssdev, 0x29);
+}
+
+static int s6e8aa0_power_on(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ int ret = 0;
+
+ 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 */
+ s6e8aa0_hw_reset(dssdev);
+
+ /* XXX */
+ msleep(100);
+ s6e8aa0_config(dssdev);
+
+ dsi_video_mode_enable(dssdev, 0x3E); /* DSI_DT_PXLSTREAM_24BPP_PACKED; */
+
+ s6->enabled = 1;
+ }
+
+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)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+ int r = 0;
+
+ mutex_lock(&s6->lock);
+
+ 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);
+ }
+
+ mutex_unlock(&s6->lock);
+
+ return r;
+}
+
+static void s6e8aa0_stop(struct omap_dss_device *dssdev)
+{
+ struct s6e8aa0_data *s6 = dev_get_drvdata(&dssdev->dev);
+
+ mutex_lock(&s6->lock);
+
+ dssdev->manager->disable(dssdev->manager);
+
+ dsi_bus_lock(dssdev);
+
+ s6e8aa0_power_off(dssdev);
+
+ dsi_bus_unlock(dssdev);
+
+ mutex_unlock(&s6->lock);
+}
+
+static void s6e8aa0_disable(struct omap_dss_device *dssdev)
+{
+ dev_dbg(&dssdev->dev, "disable\n");
+
+ if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE)
+ s6e8aa0_stop(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
+}
+
+static int s6e8aa0_enable(struct omap_dss_device *dssdev)
+{
+ dev_dbg(&dssdev->dev, "enable\n");
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)
+ return -EINVAL;
+
+ return s6e8aa0_start(dssdev);
+}
+
+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)
+{
+ dev_dbg(&dssdev->dev, "resume\n");
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED)
+ return -EINVAL;
+
+ return s6e8aa0_start(dssdev);
+}
+
+static int s6e8aa0_suspend(struct omap_dss_device *dssdev)
+{
+ dev_dbg(&dssdev->dev, "suspend\n");
+
+ if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
+ return -EINVAL;
+
+ s6e8aa0_stop(dssdev);
+
+ dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
+
+ return 0;
+}
+#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/firmware/Makefile b/firmware/Makefile
index 5f43bfb..90709c5 100644
--- a/firmware/Makefile
+++ b/firmware/Makefile
@@ -141,6 +141,7 @@ fw-shipped-$(CONFIG_USB_SERIAL_XIRCOM) += keyspan_pda/xircom_pgs.fw
fw-shipped-$(CONFIG_USB_VICAM) += vicam/firmware.fw
fw-shipped-$(CONFIG_VIDEO_CPIA2) += cpia2/stv0672_vp4.bin
fw-shipped-$(CONFIG_YAM) += yam/1200.bin yam/9600.bin
+fw-shipped-$(CONFIG_TOUCHSCREEN_MMS) += melfas/mms144_ts.fw
fw-shipped-all := $(fw-shipped-y) $(fw-shipped-m) $(fw-shipped-)
diff --git a/firmware/melfas/mms144_ts.fw.ihex b/firmware/melfas/mms144_ts.fw.ihex
new file mode 100755
index 0000000..ad792bb
--- /dev/null
+++ b/firmware/melfas/mms144_ts.fw.ihex
@@ -0,0 +1,565 @@
+:10000000F01F0020C1000000D5000000D700000054
+:1000100000000000000000000000000000000000E0
+:10002000000000000000000000000000D9000000F7
+:100030000000000000000000DB000000DD00000008
+:10004000051E00000F150000351E0000831E000075
+:10005000DF000000ED000000EB150000E7000000ED
+:10006000E7000000E7000000E7000000E7000000F4
+:10007000E7000000E7000000E7000000E7000000E4
+:10008000E7000000E7000000E7000000E7000000D4
+:10009000E7000000E7000000E7000000E7000000C4
+:1000A000E7000000E7000000E7000000E7000000B4
+:1000B000E7000000E7000000E7000000E7000000A4
+:1000C0000348854602F0DCF800480047931E000014
+:1000D000F01F0020FEE7FEE7FEE7FEE7FEE7FEE793
+:1000E000FEE7FEE7FEE7FEE7FEE7FEE7FEE7FEE7E8
+:1000F0000A4603467047FEE7704700000000000014
+:100100004F435441000000000000000000000000C8
+:1001100000000000000000000000000000000000DF
+:100120005241303156413131323031313036313651
+:1001300010B55B2109019E4802F077F85B21C900E8
+:100140009C4802F072F8FF216D319B4802F06DF877
+:1001500000209A49C0439A4A0880108005234880AD
+:100160005080091D121D088010805B1EF7D110BD44
+:10017000F0B594480021415685B0934802910D2274
+:10018000192102608160002141600D46C160EA01D1
+:1001900003926822864B6A43D218019286481A223B
+:1001A0006A4310180090834934200024684346189D
+:1001B00003986200811885480F180421795EB05EAB
+:1001C000B152401A00D54042042803DD00980022B5
+:1001D000025506E000980057052802D0009A401CFE
+:1001E00010550198A300C258C80184460121101A75
+:1001F0008902884211DD74494A68AA4200DA4D60DA
+:10020000CA68A24200DACC600A68AA4200DD0D602A
+:100210008A68A24211DD8C600FE06D49884208DADD
+:10022000002A0AD101996246CA50009900220A5553
+:1002300003E0C111521A0199CA50029900290BD04A
+:10024000FF210131884204DD410262480818001490
+:100250000FE00020B8800DE0002817DD002C03D04F
+:10026000192C01D0C01104E0C0114000032101F09D
+:10027000C1FFB880641C1A2C9ADB6D1C0E2D86DB26
+:1002800051490868022803DD801E04E00020F0E7E1
+:10029000012801DD401E08608868022801DD801EFB
+:1002A00002E0012801DD401E886048680C2801DA60
+:1002B000801C02E00D2801DA401C4860C86818283C
+:1002C00001DA801C02E0192801DA401CC8604248AB
+:1002D000007800280DD041A000F0CBFE344D002462
+:1002E0006820604341191A2000F033FF641C0E2C73
+:1002F000F6DB05B0F0BDF8B500223C4B11460024FA
+:100300000D2095012E19F6187170B170A41C401EB5
+:10031000F8D1521C0E2AF2DB354F36480122D501A6
+:10032000EE19318094012418217071806170324E71
+:10033000AD196983617729832177521C112AEEDB7D
+:100340002E4E2F4B01223D46540067193980171855
+:1003500039702C4FE7193980244FD7193970A719F4
+:100360003980D7183970284FE4192180274C141987
+:100370002170521C1D2AE7DB29801F4A51833180DE
+:10038000234A5183017041771970597721480078C9
+:1003900000280CD020A000F06CFE224D0024E001CB
+:1003A00041191A2000F09DFE641C0E2CF7DBF8BDED
+:1003B00034000020E4050020BC0800208E0D002041
+:1003C000A40D00201100002000000020004100408A
+:1003D00000FCFFFF0000FEFF1B0000205265666569
+:1003E00072656E636520496D61676500402000405D
+:1003F0000040004000200040204000408048004075
+:10040000402400408040004000480040002400405C
+:10041000A04800401C000020496E74656E7369742A
+:100420007900000004410040F0B5D3B00024FF483B
+:10043000C4210668FE4800680394089404940D944F
+:10044000059406940794099412900A940B940C94C2
+:100450000E9421A801F0E9FEF648007800280CD09F
+:10046000F5A000F006FEF74F0025A801C1191A20DB
+:1004700000F0CBFD6D1C0E2DF7DBF3480321415638
+:10048000022901DA032000E00A201A90EF48761CC6
+:1004900005681396B54239DA129A521CEC4B129E3B
+:1004A00018680021361A771C002F01DD0121B143A5
+:1004B0000B1883420FDDAE01E64B3718FB180227FD
+:1004C000DF570C2F01DD00279F70E14B401C1B689C
+:1004D000CB188342F0DCDE4800680818904211DA3D
+:1004E0000026AB01DB491F1879180227CF570C2FC4
+:1004F00000DD8E700327CF570C2F00DDCE70801CDF
+:100500009042EFDB13986D1C8542C7DBCF481399EF
+:100510000068029088427EDA1298401C1B90CC48FA
+:1005200002681B98824277DA0298C0B22090029843
+:10053000C0011C90029880011F908018C549844614
+:100540004318022018561C9D51006D18C2496D18A1
+:100550000421695E002809D003AD2E5C8E4205DCC3
+:10056000295407AE209D35540BAD2A54844200DA3D
+:10057000C4B2002853D11A98814250DDB74DFF20F4
+:1005800065446E780021AF78ED781D96DE7815977A
+:100590006746AC46B24D17967D19EF78AE786D7808
+:1005A00018975F7819961D9E002E03D00121FF2E0B
+:1005B00000D23046159E002E03D0491C864200D240
+:1005C00030466646002E03D0491C844500D2304692
+:1005D000179E002E03D0491C864200D23046189E3A
+:1005E000002E03D0491C864200D23046199E002EB0
+:1005F00003D0491C864200D23046002D03D0491C4E
+:10060000854200D228463D0003D0491C874200D2D3
+:100610002846002901E094E007E000D098701B987C
+:10062000521C824201DA1F9887E702981399401CF6
+:100630000290884200DA72E70220641C1494019050
+:10064000022C7ED9019907A8425C01980BA9085C8D
+:10065000921C801C8446D10140000918814808186A
+:100660000024045F804BCD181E23EB5E1B1D9C42B3
+:1006700021DC0024045F7D4BCD180023EB5E0933A1
+:100680009C4208DD0024045F794BCD181E23EB5EED
+:1006900009339C420FDC9501764C654401232C19EB
+:1006A0000026A657744C2D191F242C570E2721AD58
+:1006B0007C43641933550025455F6C4BCC180223ED
+:1006C0001594E35E1B1D9D421EDC0025455F2346FD
+:1006D00000241C5F0934A54206DD0024045F0223C8
+:1006E000C35E09339C420FDC950164462C19614DB1
+:1006F000012365190026AE57604D6519EC560E278B
+:1007000021AD7C43641933550024045F5C4BCD1844
+:100710001E23EB5E1B1D9C4224DC0024045F594B0E
+:10072000CD180023EB5E09339C4208DD0024045FF2
+:100730004F4BCD181E23EB5E09339C4212DC012384
+:1007400000E0D0E0950164462C194A4D6519002659
+:10075000AE574D4D65191F242C570E2721AD7C43F4
+:10076000641933550024045F464BCB1802251693B9
+:100770005D5F2D1DAC421DDC0025455F00241C5F24
+:100780000934A54206DD0024045F0223C35E093359
+:100790009C420FDC950164462C19364D01236519E6
+:1007A0000026AE572D4D6519EC560E2721AD7C4322
+:1007B000641933550023C35E159C0025655F2D1D0C
+:1007C000AB4210DC950164462C192A4D01236519B2
+:1007D0000026AE57294D651900242C570E2721AD50
+:1007E0007C43641933550025455F169C0023E35E66
+:1007F0001B1D9D4210DC950164462C191D4D0123E3
+:1008000065190026AE57154D651900242C570E2783
+:1008100021AD7C43641933550024045F144BCB187D
+:100820001E21595E091D8C4241DC2FE0040000208E
+:100830000C0000201D00002047726F757020496D6C
+:100840006167650082200040100000200000002049
+:10085000080000208020004000410040402000406F
+:10086000C020004000400040603F0040803F00400A
+:10087000E03F004000200040A01F0040C01F00409B
+:10088000604000408040004020200040012394014F
+:1008900061466118FF4C0D1900242C57FE4D491973
+:1008A0001F254D570E2621A975436918635400234F
+:1008B000C35E0221415E091D8B420CDC9201F548AA
+:1008C0006244012110180023C35642560E2421A869
+:1008D00062431018195401981499401CC0B2019039
+:1008E000884200D2AEE60D2121A801F09EFC022133
+:1008F000012421A80022002908DD0E2353431B18E0
+:100900005B5C012B11D0521C8A42F6DB491C0D297D
+:10091000F0DB002501462A460D2300240C57002C4D
+:100920000FD06D1CEDB2045D0CE08356002B01D09E
+:10093000435400E042540E224A4312185218D47312
+:10094000E4E7541B0C70491C521C5B1EE5D1D349D3
+:100950000E6813998E4215DA129AD14F521CD14962
+:10096000096891420ADAB4016318DB199D7845578A
+:10097000002D00D09D70491C9142F5DB1399761C27
+:100980008E42ECDB53B0F0BDF0B595B0FFF74CFDF7
+:10099000C548C7490568C548C64A04680020C64B13
+:1009A000C64E0A900B900C900D270D900020C0436E
+:1009B00001C1002001C201C33070761C7F1EF5D139
+:1009C000BF48007800280CD0BEA000F052FBC14FF9
+:1009D0000026B001C1191A2000F017FB761C0E2E5C
+:1009E000F7DB00200490AD486D1C0068069505906B
+:1009F000A84257DAAB48641C00680E900E98A042DB
+:100A00004ADA059989018C460599C9010791614621
+:100A10000A18A349511802228A56002A39D004998B
+:100A2000914200DA0492079D41006918AA4D531EB5
+:100A30004D190421695E95009D4E2D1F7759059D26
+:100A400096004D437D199A4F361FBD519200994E25
+:100A5000121FB258002807D0182808D019283DD0F6
+:100A6000C5002D1D4D4304E08D006D4201E0C625FB
+:100A700035E0AA189D0072518F4A56597618565182
+:100A80003432D55C6D1CD5540AAAD55C8D4200DA8F
+:100A9000D154401CA042BADB05980699401C059031
+:100AA0008842ABDB04988D4940B288700790049867
+:100AB000002863DD0AA80F9080480E907E4F884E74
+:100AC0007B4C7C4D0498019001CF0090002848D0C9
+:100AD0000F9800780C2804D2012003E0D4254D4360
+:100AE000C7E7002030707F492068096848430D211E
+:100AF00001F080FB009901F07DFB002120600028BF
+:100B000000DD0146216077492868496848431A2179
+:100B100001F070FB009901F06DFBC117490F081837
+:100B2000C01028600E980078322802D96B49012045
+:100B300008702168002902DA0020206005E0694879
+:100B40000068814200DC084620602968002902DA3A
+:100B50000020286005E063484068814200DC0846C8
+:100B600028600F98761C401C0F900E98241D401C86
+:100B70000E9001982D1D401E0190A5D104980028CB
+:100B80003BD05649002048700020411E4A4C099134
+:100B900006902368544A5B101BB203935348544B8E
+:100BA0000B250021C943118001801970921C801C03
+:100BB0005B1C6D1EAC46F4D1604641004D480A9165
+:100BC000405E0B90401C59D0604680004A4D049016
+:100BD00029584A48081800900590079800282BDDEE
+:100BE00033484349334A079C002305686E1C1DD0D7
+:100BF00000268E57761C04D018E038490120487032
+:100C0000C2E70B9E1768AE1BB6463D4D0A9EAD5F10
+:100C100076467D1B76436D437519009EB54205DA15
+:100C20005EB20996344F049E0095BD51001D491CCB
+:100C3000121D5B1C641ED8D10599009888421DD0F6
+:100C400009981B498000284A0A9B0958D152194922
+:100C50000A9B0A589831CA5264390A5829490A9895
+:100C60000A5216480999425C274861464254069840
+:100C70001F49401C0690099A604688546046401CF3
+:100C800084460B2898DB0020079A0146002A63DD82
+:100C9000144A3BE000200040E01F0040000000201C
+:100CA0008020004008000020040000200C000020EC
+:100CB000480B00207C0B0020B00B0020E40B002030
+:100CC0001D00002047726F757020496D61676532A5
+:100CD0000000000082200040004100401000002081
+:100CE0005C0A0020780C0020FE0B0020140C002071
+:100CF000F10B00204C0C0020280A002010270000D7
+:100D0000620C00202A0C0020400C0020167AA34D13
+:100D1000A34A5256521C1BD1079A012A05DDA14A4B
+:100D20008B00D358039A934212DB9F4A525C002AED
+:100D30000ED19C4A8B00D458B44209DB0B2807DA49
+:100D40009A4A4700D75F7F1C7ED0401C0B28F8DBF7
+:100D5000079A491C9142DBDB954B069898709549A0
+:100D60000020D8708C468D4D9048934A93490B24AF
+:100D700063461E889B1C9C460023CB5E2F889E46A4
+:100D80000780931C16801A4673465B100B80002365
+:100D9000C35EAD1C891C801C5B1C03D0844EF378A1
+:100DA0005B1CF370641EE3D1824A60008718163220
+:100DB00082180592824A7949121913924618142210
+:100DC000804B6243085ED518401C7ED00020285618
+:100DD0000321421C032A00DC114648B20D906888AA
+:100DE000059A40B28446C01CC10F09184908490041
+:100DF000401A40B20E90002000210023305E795E40
+:100E0000D35E0290139802786046400000930492EB
+:100E1000039140190423C35E08219C460C22102331
+:100E2000415E825EC35E0D980F9102280893069280
+:100E300069DD02996046431A08466146401A00D4AB
+:100E4000034603990F9800E086E00F9A401A891A2A
+:100E500000D408460A9000990698069A401A891A02
+:100E600000D40846019004990898421A0846089947
+:100E7000401A00D402460A98109218180A904B2083
+:100E80006246C00042434B210A98C90041184810ED
+:100E900012180991129002990A9841435018099921
+:100EA00001F0A8F900B202904B200F99C000414315
+:100EB00012980A1803990A9841435018099901F0A9
+:100EC00099F900B20390642100E05CE00698484381
+:100ED000019964314A108318009A01984243981886
+:100EE00001F088F900B2009008980101081A1099E1
+:100EF0000F314A108318109A04985043181801F0C3
+:100F000079F900B20490029931800E98400040199E
+:100F10008180039939800181059A0099118081812E
+:100F20000499139AC9B2117001820E9868800D98C5
+:100F300028700D98022809DA0020C04330803880DC
+:100F40001B48C178491EC17000214170641C0B2CE4
+:100F500000DA29E715B0F0BD1B4AD7584200AF525E
+:100F60001A4FFB589837BB52144B9C52184A8400B6
+:100F7000535C5C321354174B00221A51069A521CD0
+:100F8000401C0692E4E600202870401E3080388025
+:100F9000DCE710B500F023F910BD0000FE0B0020C7
+:100FA000F10B0020B00B00205C0A00204C0C00204C
+:100FB00010000020140C0020620C00202A0C0020DD
+:100FC000400C00206A0A0020480B00207C0B002007
+:100FD000E40B0020280A0020FF4A11547047FE4904
+:100FE000085C7047FD4852218170D021C1700021FA
+:100FF00001711C21417101218171F9484122027462
+:10100000417482741121C1747047F8B5F54CE27ACD
+:10101000002A2DD00246F14E403232707070022507
+:10102000002804D0C207D20F012A02D004E0002217
+:1010300002E00B780325B37082420ADA8B5C7355A9
+:101040008B186D1C5F782B466D1CF754921C8242E6
+:10105000F4DB0220E0700025A57000F0C9FE657188
+:10106000A078E1788842FBD300F0C7FE01206071D0
+:10107000F8BDF8B5044601F0E4F8DA4DC3B2E87AF9
+:1010800000282DD01846D54EC03030707370022025
+:10109000002B04D0D907C90F012902D004E0002198
+:1010A00002E022780320B27099420ADA625C32547C
+:1010B0006218401C57780246401CB754891C99425C
+:1010C000F4DB0220E8700024AC7000F091FE6C713B
+:1010D000A878E9788842FBD300F08FFE0120687180
+:1010E000F8BDC04AD27A002A32D0F8B50246BB4FCA
+:1010F00080323A7078700123002804D0C207D20FE2
+:10110000012A02D004E0002202E00B887B80022347
+:1011100082420CDA54000D5B5E00BD535B1C641808
+:1011200064885D007C535B1C921C8242F2DBAD4CF8
+:101130000220E0700025A57000F05AFE6571A078CD
+:10114000E1788842FBD300F058FE01206071F8BDC1
+:101150007047A44AD27A002A40D0F8B502469F4F81
+:1011600080323A700123002804D0C207D20F012A2E
+:1011700002D009E0002207E00B68DC17640EE318D8
+:101180005B021B0C7B800223824216DA94000D590D
+:10119000EE17760E75196D022D0C5E00BD536418A6
+:1011A00064685B1CE5176D0E2C196402240C5D004D
+:1011B0007C535B1C921C8242E8DB8A4C0220E0706C
+:1011C0000025A57000F014FE6571A078E1788842D2
+:1011D000FBD300F012FE01206071F8BD704781481A
+:1011E000C17A002903D080490978C17370477D48CE
+:1011F00000790028FBD1F0B5002083B001460090B3
+:10120000102084467948407A864600200B2903DA6C
+:101210000B20401AC007C00F45188D420FDD744ED9
+:10122000744B75484A00B45E9F5EBC422DD1734C2E
+:10123000A45E825E944228D1491C8D42F2DC28468D
+:101240000B2D21DA6B4A6D4B6B4C4100684D565E9D
+:101250006F5EB74218D15E5E675EBE4214D14D1913
+:101260000226AE5F8D180227EF5FBE4207D1CD1870
+:101270000226AE5F091902254D5FAE4201D0411C26
+:1012800003E0801C0B28E0DB01464B0058485B4C18
+:10129000C25E5848584EC75EE45EF65ED51B0196A6
+:1012A000A61B00D57642002D00DA6D42AE4507DD63
+:1012B000B64505DD501C03D0781C01D0491C26E042
+:1012C0000B2927D0501C34D0481C05077F1C2D0F3C
+:1012D0004248A035664685552511F026354016054D
+:1012E000360FAD19604445708270C470444DED5A9C
+:1012F0000571444D6D5C45713E48C2521630C45272
+:101300006046801D84460098401C491C00900B29B3
+:1013100000DA7AE70098062148433049C87372B66C
+:10132000304C607B00281ED000BF00BF62B603B007
+:10133000F0BD481C0607360F284D203660462E5457
+:10134000019865440611F02006403805000F361854
+:101350006E70AF700198E8702948C05A287129480A
+:10136000405C6871C8E762B60098002805D001208B
+:10137000207100F03DFD002060711B480078E07393
+:1013800003B0F0BDF8B50C0014491F4F054604D05A
+:10139000E207D20F012A02D003E0002201E0387BED
+:1013A0004855A24209DAD019067BAB18CE54407BCF
+:1013B0005B185870921CA242F5DB0A4E0020307177
+:1013C00000F01BFD012171731048052D5CD033DC4A
+:1013D000002D46D0012D1BE0840D0020840C002040
+:1013E000640D00201400002013000020780C002061
+:1013F000FE0B00208E0D0020A40D0020140C0020F8
+:101400002A0C0020400C0020000000502400002086
+:101410002FD0022D0FD1022C0DD93A7B7B7B1207E6
+:10142000120D1A4302603A7BBB7B120912011201B2
+:101430001A4342607173F8BD062D2DD0A02DFAD14C
+:10144000387BF072387B1A2829D0002002460346E8
+:10145000062B58D27B441B79DB189F445432394009
+:10146000474E00243473347100F0C7FCB470F4703C
+:10147000F8BD387B0028FBD131730024347100F0B3
+:10148000BCFCB470F470F8BD3A7B0272027A092A8F
+:10149000D0D209220272CDE73A7B4272CAE702201B
+:1014A00030730024347100F0A8FCB470F470514B18
+:1014B0000020C219127B81B25A54401C0528F8DB67
+:1014C000F8BD7B7B9B0701D5B1711CE0B2711AE0BE
+:1014D0007B7B5B0701D5F17115E0F27113E07B7B3B
+:1014E0001B0701D531720EE032720CE07B7BDB060C
+:1014F00001D5717207E0727205E07B7B9B0601D516
+:10150000B17200E0B272401C0828A0DBF8BDF8B54B
+:1015100005242407207AA67A07073F0F364D012FAE
+:1015200004D0082F29D0022F3BD056E0E8780028BD
+:1015300000D000266E7000F060FC01206871A82EBB
+:1015400015D02E48815D21738019417861738178AF
+:10155000A173C178E1730179217441796174817952
+:10156000A174C079E07468780830687035E021486B
+:10157000017A2173407A6073F5E769781F48425C0D
+:1015800022734018417861738178A173C178E17347
+:1015900001792174417961748179A174C079E07411
+:1015A000E1E7A87B01280BD0082809D0E07A0107E1
+:1015B000090F05D03046FFF7E5FE00202870E870DF
+:1015C000102E04D12879012801D100202871E87853
+:1015D000002802D0A878401CA87001202072AF73A8
+:1015E0001020207200202072F8BD0120052109077B
+:1015F00008727047840D002014000020840C002025
+:10160000F0B5FFB0C8B05B21C90010A800F00DFE16
+:10161000D9480421417000F003FC00F0FDFB00F00C
+:1016200011FC00200090D548417882784808099044
+:10163000D248C17850080A90D048027948080B90E7
+:10164000CE48417950080C90CC48827948080D90DA
+:1016500050080E90CA480178490801914178827873
+:1016600049080291C37851080391027959080491FD
+:10167000437951080591817958080690BF4802784E
+:101680004808079050080890FF2000990022013078
+:10169000401AC4B2019990014618BA4976183470BC
+:1016A000029E861976183470039E86197618347057
+:1016B000049E861976183470059E86197618347043
+:1016C000069E861976183470079E861971180C705C
+:1016D00008994618AC4976183470099E8619761810
+:1016E00034700A9E8619761834700B9E8619761807
+:1016F00034700C9E8619761834700D9E86197618F3
+:1017000034700E9E801940180470521C1A2AC1DBD6
+:1017100000F086FB00F080FB00F094FB0020864682
+:1017200070463421484310A9411870461A2250438C
+:10173000964A7546831800221A20ED01AC4666468B
+:1017400055007619924D75190426AE5F0027CF5FBC
+:10175000BE4202DDAD880D801C70891C5B1C521CD2
+:10176000401EECD17146491C8E460E29D8DB0099EB
+:10177000491C49B20091142986DB7F4902224A7034
+:10178000824E019B8101345CCD187E4B641EED18A6
+:101790002C7032183424A45C029D641E4D19ED187F
+:1017A0002C706824A45C039D641E4D19ED182C70E8
+:1017B0009C24A45C049D641E4D19ED182C70D0244B
+:1017C000A45C059D641E4D19ED182C70FF24053492
+:1017D000A45C069D641E4D19ED182C70FF2439344D
+:1017E000A45C079D641E4D19EB181C70089B927E2B
+:1017F000CD18654B521EED182A700122342415466F
+:101800006543624C2C1925185C4CAE7EA75C761E95
+:101810007F08CF19FF183E70A41840356478AD7B5F
+:1018200064086D1E0C19E4182570921C072AE5DB6C
+:10183000401C1A28A4DB574E307800281BD056A035
+:10184000FFF717FC514D00241A20604341191A205C
+:10185000FFF7DBFB641C0E2CF6DB307800280AD087
+:10186000002410AD3420604341191A20FFF739FCE1
+:10187000641C0E2CF6DB7FB048B0F0BDF8B5012833
+:1018800042D000203F4A0021484C83019A18342559
+:101890003E4E4D43AD192E5C394D143E6D5C6D08C6
+:1018A0005F19384D7D192E70275C0126BE40966168
+:1018B000491CC9B20729EAD3304E334F00213424E2
+:1018C000324D4C4364192418755CA47E6D08143C99
+:1018D0005D19ED192C70354C255C0124AC409463E6
+:1018E000491CC9B20729EAD3401CC0B21A28C9D37F
+:1018F000002400F095FA00F08FFA00F0A3FA641CBF
+:10190000E4B2322CF5D3F8BD00201E4A0021274B4B
+:101910008501AC1834221D4E4A439219165C184AB0
+:10192000525C5208AF18174ABA1816701F5C012291
+:101930001646BE40A661491CC9B20729EAD3002158
+:101940003423124E4B439B191B189E7E0B4B5B5C42
+:101950005B08EF180C4BFB181E70144B1E5C1346F3
+:10196000B340A363491CC9B20729E9D3401CC0B2E4
+:101970001A28CAD3BCE7000040000040E122000062
+:10198000DA2200000010004020100040BA0D0020B4
+:10199000004100403000002044656C617920706592
+:1019A00072206368616E6E656C000000C0220000EA
+:1019B000F0B50020844683B0F7480090F74841789E
+:1019C0000120884001900023F54C604619461F46CF
+:1019D0008001421812199772D772891C0A29F8DB04
+:1019E000F04A0021545CFF2564080619EC4C3619B6
+:1019F0003570565C4D007608AD1C86193419A572F9
+:101A0000555C0124AC40E318491C0729EADBE44992
+:101A100041188E46CB616246921CD2B202920A7580
+:101A2000DE4A6346D35C01229A408A6161461929E5
+:101A300002D0002908D00CE0DB498A69009B01260E
+:101A4000DB7F9E40324303E0D5498A69019B1A43FC
+:101A50008A610023D54A19463D464418A418A57248
+:101A6000E572891C0A29F8DB00210127D04CFF25EB
+:101A7000665C76088619B6183570665C4D00760887
+:101A8000ED1C8619B618B572655C3C46AC40E3188F
+:101A9000491C0729EADBC2497446E3638018029BAC
+:101AA0000375BE486246805C874070468763604627
+:101AB000192A07D000280FD0401C84461A2882DB40
+:101AC00003B0F0BDB848826B0099CB7F01219940EB
+:101AD0000A43826303B0F0BD886B019A10438863A8
+:101AE000012084466FE710B45221480701770C218A
+:101AF0004177962181771821C177AF48AD490180A0
+:101B000000214180AD4981801322C2800181428140
+:101B1000AB4B8381AB4CC481038244828182C282FD
+:101B2000018342838183C28310BC7047F0B40921D2
+:101B3000880741831E210176122141760021A24EA1
+:101B4000A24C0F2208464B009D1928800B191870D3
+:101B500068805870891C521EF5D100218C4663465E
+:101B600000210F229D01DF014B00FC18984BE41867
+:101B70002080984B6E18F318187060805870891C7C
+:101B8000521EF1D16146491C8C461229E7DBF0BC9C
+:101B9000704710B590488221417002214170422364
+:101BA00005221207D372482393724170FFF71AFA85
+:101BB00010BD0021012080078170C160F3210174F4
+:101BC000FF214174704710B584490F200860844993
+:101BD00000200860032100F0C0F80021012000F07F
+:101BE000BCF80221084600F0B8F80121032000F0FB
+:101BF000B4F80121042000F0B0F80221052000F023
+:101C0000ACF80021062000F0A8F87648016904220B
+:101C10001143016110BDF8B50120850768720024E9
+:101C2000EC80CC202871BA2068716B4EB4700F2004
+:101C300028729D21A9727F21E972332131706A498E
+:101C400071603472654908606548046003210020B2
+:101C500000F083F80021012000F07FF802210846FF
+:101C600000F07BF80121032000F077F80121042027
+:101C700000F073F80221052000F06FF80021062023
+:101C800000F06BF857480169042211430161AC7000
+:101C9000EC60F3202874FF206874822070700220AA
+:101CA0007070422205210907CA7248228A72707038
+:101CB000FFF798F9092068831E202876122068769D
+:101CC0000020414E414B0F21420095192C80C21833
+:101CD00014706C805470801C491EF5D1002084461D
+:101CE000624600200F219501D7014200BB18384AF7
+:101CF0009B181C80374A2E18B21814705C805470E0
+:101D0000801C491EF1D16046401C84461228E7DB46
+:101D10005221480701770C214177962181771821BC
+:101D2000C1772548234901804480244981801322BA
+:101D3000C28001814281224B8381224CC481038273
+:101D400044828182C282018342838183C283FFF7FE
+:101D50002FFEFEF7EDF962B6F8BD8307FF22DB0E1A
+:101D60009A408907090E994000280BDA0007000FF6
+:101D7000083883081B489B001818C36993430B431A
+:101D8000C3617047830819489B0018180368934380
+:101D90000B43036070470000B9220000C02200001E
+:101DA00000100040DA2200004016004020100040E1
+:101DB000E122000078080000200000401304000029
+:101DC0001306000013020000004900408024004078
+:101DD00000400040002000404000004000E100E0E2
+:101DE00000E200E000ED00E0FFFF0F0000E400E093
+:101DF000FB200121890748747047FF2001218907D2
+:101E000048747047924948064161924A0121117015
+:101E10000022C275C17570478F4901200872704752
+:101E20008C49002048708C4908720906087001200E
+:101E300008707047874901204870002001218907F8
+:101E400008707047834872B64178002905D062B6A1
+:101E500082480078002805D1704700BF00BF62B6F5
+:101E600030BFF0E770B57EA0FFF703F97F4D002487
+:101E7000E00141191A20FFF734F9641C0E2CF7DB3E
+:101E800070BD7449012088700020012189074870C5
+:101E90007047FFF7C0FE6F48002444706E4F3C72DD
+:101EA0003D062C7001262E703E72FFF7CBFF69486D
+:101EB00044703C722C702E703E72FFF7C3FFFFF728
+:101EC0009FFB644844703C722C702E703E72FFF78A
+:101ED000B9FF604804705E486861EE75644900228D
+:101EE0008A5664480378002B08D00470634E624819
+:101EF00033687568B668866045600360002A60D004
+:101F0000012A04D0022AE9D100F00AF9E6E70C70B0
+:101F10000120800744720F2303720121052212075A
+:101F200051721125C570C470037241725472BFF3AF
+:101F3000608F484E7470484A1472047001701172B8
+:101F400072B67078002832D062B644480078002813
+:101F50000CD043A0FFF78DF8444F0025E801C119CC
+:101F60001A20FFF7BEF86D1C0E2DF7DB74703A488F
+:101F7000047201060C7001220A70027272B6707847
+:101F8000002819D062B63548007800280CD034A05B
+:101F9000FFF76FF8354E0025E80181191A20FFF789
+:101FA000A0F86D1C0E2DF7DBFEF7C2F896E700BF18
+:101FB00000BF62B630BFC3E700BF00BF62B630BF2C
+:101FC000DCE7244D6C702448047206063470012747
+:101FD0003770FEF7CDF8FEF78EF9AC70747077703D
+:101FE00072B6A878002823D062B6FEF7CDFCFEF7C3
+:101FF000D0FF1948077272B6687800281DD062B603
+:102000001648007800280CD015A0FFF732F8174FBB
+:102010000026F001C1191A20FFF763F8761C0E2E76
+:10202000F7DB72B6287800280CD062B62C7055E722
+:1020300000BF00BF62B630BFD2E700BF00BF62B6CC
+:1020400030BFD8E700BF00BF62B630BFE9E700008D
+:10205000A086010031000020400000401A0000204E
+:1020600052617720496D61676500000004410040BE
+:102070002000002021000020780C002024000020F7
+:10208000F0B5FFB0B8B00120FFF7F8FB544C0027C3
+:102090003620394641435348534A081889180022CC
+:1020A0001A23FE01550075192D19AD8805800D8084
+:1020B000801C891C521C5B1EF4D17F1CFFB20E2FAA
+:1020C000E6D30020FFF7DAFB0020844634214843A2
+:1020D00069464118604636225043424A1A23801806
+:1020E00000226446E70154003D193D4C2C19A48898
+:1020F0000C800025455F26B2AC1B02D5741B048002
+:1021000000E00480891C801C521C5B1EEBD16046E1
+:10211000401CC0B284460E28D8D37FB038B0F0BD82
+:10212000F8B5BD208107887230490120487004202D
+:10213000FEF755FF00242E4D012828D00420FEF77D
+:102140004EFF02282DD00420FEF749FF03281DD1A1
+:102150000120FEF744FF06460220FEF740FF36212D
+:102160004843214976004718B85B4004410E0820D7
+:10217000FEF732FFB85BC1B20920FEF72DFF002148
+:102180000420FEF729FFFFF733FE2C70F8BDFFF7A0
+:1021900077FF00210420FEF71FFFFFF729FE2C70B8
+:1021A000F8BD0120FEF71BFF06460220FEF717FFD1
+:1021B000362148430B4976004718B85B4004410E6E
+:1021C0000820FEF709FFB85BC1B20920FEF704FF43
+:1021D00000210420FEF700FFFFF70AFE2C70F8BD77
+:1021E00000410040260F00201A120020400000404D
+:1021F0001900002070B500242546002801DA0124CA
+:102200004042002901DA0125494200F022F8AC429F
+:1022100000D04042002C00D0494270BDD2B201E053
+:102220000270401C491EFBD270470022F6E710B531
+:1022300004460846114602462046FFF7EFFF2046B7
+:1022400010BD0146002000E0401C0A5C002AFBD1C2
+:10225000704730B50B46014600202022012409E0DA
+:102260000D46D5409D4205D31D469540491B254648
+:10227000954040191546521E002DF1DC30BD00007E
+:10228000064C0125064E05E02046E36807C82B43AF
+:1022900098471034B442F7D3FDF716FFE822000048
+:1022A0000823000002E008C8121F08C1002AFAD162
+:1022B0007047002001E001C1121F002AFBD17047C6
+:1022C000000102030405060A0B0C0D0E0F1011127B
+:1022D000131415191A1B1C1D1E1F020406080C0ED0
+:1022E000100305070B0D0F1108230000000000204C
+:1022F00034000000A42200003C2300003400002031
+:10230000DC140000B2220000000000000000000009
+:1023100000000000000000000000000000000000BD
+:10232000000100000000000000010000D0020000D9
+:0C233000000500000F010000000000008C
+:00000001FF
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
new file mode 100644
index 0000000..cfa6580
--- /dev/null
+++ b/include/linux/hsi_char.h
@@ -0,0 +1,71 @@
+/*
+ * hsi_char.h
+ *
+ * HSI character driver public declaration header file.
+ *
+ * Copyright (C) 2009 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Andras Domokos <andras.domokos@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef HSI_CHAR_H
+#define HSI_CHAR_H
+
+#define HSI_CHAR_BASE 'S'
+#define CS_IOW(num, dtype) _IOW(HSI_CHAR_BASE, num, dtype)
+#define CS_IOR(num, dtype) _IOR(HSI_CHAR_BASE, num, dtype)
+#define CS_IOWR(num, dtype) _IOWR(HSI_CHAR_BASE, num, dtype)
+#define CS_IO(num) _IO(HSI_CHAR_BASE, num)
+
+#define CS_SEND_BREAK CS_IO(1)
+#define CS_FLUSH_RX CS_IO(2)
+#define CS_FLUSH_TX CS_IO(3)
+#define CS_BOOTSTRAP CS_IO(4)
+#define CS_SET_ACWAKELINE CS_IOW(5, unsigned int)
+#define CS_GET_ACWAKELINE CS_IOR(6, unsigned int)
+#define CS_SET_RX CS_IOW(7, struct hsi_rx_config)
+#define CS_GET_RX CS_IOW(8, struct hsi_rx_config)
+#define CS_SET_TX CS_IOW(9, struct hsi_tx_config)
+#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 HSI_MODE_SLEEP 0
+#define HSI_MODE_STREAM 1
+#define HSI_MODE_FRAME 2
+
+#define HSI_ARBMODE_RR 0
+#define HSI_ARBMODE_PRIO 1
+
+#define WAKE_UP 1
+#define WAKE_DOWN 0
+
+struct hsi_tx_config {
+ __u32 mode;
+ __u32 flow;
+ __u32 frame_size;
+ __u32 channels;
+ __u32 divisor;
+ __u32 arb_mode;
+};
+
+struct hsi_rx_config {
+ __u32 mode;
+ __u32 flow;
+ __u32 frame_size;
+ __u32 channels;
+ __u32 divisor; /* not used for SSI */
+ __u32 counters;
+};
+
+#endif /* HSI_CHAR_H */
diff --git a/include/linux/hsi_driver_if.h b/include/linux/hsi_driver_if.h
new file mode 100644
index 0000000..547b30e
--- /dev/null
+++ b/include/linux/hsi_driver_if.h
@@ -0,0 +1,181 @@
+/*
+ * hsi_driver_if.h
+ *
+ * Header for the HSI driver low level interface.
+ *
+ * Copyright (C) 2007-2008 Nokia Corporation. All rights reserved.
+ * Copyright (C) 2009 Texas Instruments, Inc.
+ *
+ * Author: Carlos Chinea <carlos.chinea@nokia.com>
+ * Author: Sebastien JAN <s-jan@ti.com>
+ *
+ * This package is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef __HSI_DRIVER_IF_H__
+#define __HSI_DRIVER_IF_H__
+
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/clk.h>
+#include <linux/notifier.h>
+
+/* The number of ports handled by the driver (MAX:2). Reducing this value
+ * optimizes the driver memory footprint.
+ */
+#define HSI_MAX_PORTS 1
+
+/* bit-field definition for allowed controller IDs and channels */
+#define ANY_HSI_CONTROLLER -1
+
+/* HSR special divisor values set to control the auto-divisor Rx mode */
+#define HSI_HSR_DIVISOR_AUTO 0x1000 /* Activate auto Rx */
+#define HSI_SSR_DIVISOR_USE_TIMEOUT 0x1001 /* De-activate auto-Rx (SSI) */
+
+enum {
+ HSI_EVENT_BREAK_DETECTED = 0,
+ HSI_EVENT_ERROR,
+ HSI_EVENT_PRE_SPEED_CHANGE,
+ HSI_EVENT_POST_SPEED_CHANGE,
+ HSI_EVENT_CAWAKE_UP,
+ HSI_EVENT_CAWAKE_DOWN,
+ HSI_EVENT_HSR_DATAAVAILABLE,
+};
+
+enum {
+ HSI_IOCTL_ACWAKE_DOWN = 0, /* Unset HST ACWAKE line for channel */
+ HSI_IOCTL_ACWAKE_UP, /* Set HSI wakeup line (acwake) for channel */
+ HSI_IOCTL_SEND_BREAK, /* Send a HW BREAK frame in FRAME mode */
+ HSI_IOCTL_GET_ACWAKE, /* Get HST CAWAKE line status */
+ HSI_IOCTL_FLUSH_RX, /* Force the HSR to idle state */
+ HSI_IOCTL_FLUSH_TX, /* Force the HST to idle state */
+ HSI_IOCTL_GET_CAWAKE, /* Get CAWAKE (HSR) line status */
+ HSI_IOCTL_SET_RX, /* Set HSR configuration */
+ HSI_IOCTL_GET_RX, /* Get HSR configuration */
+ HSI_IOCTL_SET_TX, /* Set HST configuration */
+ 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,
+};
+
+/* Forward references */
+struct hsi_device;
+struct hsi_channel;
+
+/* DPS */
+struct hst_ctx {
+ u32 mode;
+ u32 flow;
+ u32 frame_size;
+ u32 divisor;
+ u32 arb_mode;
+ u32 channels;
+};
+
+struct hsr_ctx {
+ u32 mode;
+ u32 flow;
+ u32 frame_size;
+ u32 divisor;
+ u32 counters;
+ u32 channels;
+};
+
+struct port_ctx {
+ u32 sys_mpu_enable[2];
+ struct hst_ctx hst;
+ struct hsr_ctx hsr;
+};
+
+/**
+ * struct 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 {
+ u32 sysconfig;
+ u32 gdd_gcr;
+ u32 dll;
+ struct port_ctx *pctx;
+};
+/* END DPS */
+
+
+/**
+ * struct hsi_device - HSI device object (Virtual)
+ * @n_ctrl: associated HSI controller platform id number
+ * @n_p: port number
+ * @n_ch: channel number
+ * @ch: channel descriptor
+ * @device: associated device
+*/
+struct hsi_device {
+ int n_ctrl;
+ unsigned int n_p;
+ unsigned int n_ch;
+ struct hsi_channel *ch;
+ struct device device;
+};
+
+#define to_hsi_device(dev) container_of(dev, struct hsi_device, device)
+
+/**
+ * struct hsi_device_driver - HSI driver instance container
+ * @ctrl_mask: bit-field indicating the supported HSI device ids
+ * @ch_mask: bit-field indicating enabled channels for this port
+ * @probe: probe callback (driver registering)
+ * @remove: remove callback (driver un-registering)
+ * @suspend: suspend callback
+ * @resume: resume callback
+ * @driver: associated device_driver object
+*/
+struct hsi_device_driver {
+ unsigned long ctrl_mask;
+ unsigned long ch_mask[HSI_MAX_PORTS];
+ int (*probe) (struct hsi_device *dev);
+ int (*remove) (struct hsi_device *dev);
+ int (*suspend) (struct hsi_device *dev, pm_message_t mesg);
+ int (*resume) (struct hsi_device *dev);
+ struct device_driver driver;
+ void *priv_data;
+
+};
+
+#define to_hsi_device_driver(drv) container_of(drv, \
+ struct hsi_device_driver, \
+ driver)
+
+int hsi_register_driver(struct hsi_device_driver *driver);
+void hsi_unregister_driver(struct hsi_device_driver *driver);
+int hsi_open(struct hsi_device *dev);
+int hsi_write(struct hsi_device *dev, u32 * addr, unsigned int size);
+int hsi_write_cancel(struct hsi_device *dev);
+int hsi_read(struct hsi_device *dev, u32 * addr, unsigned int size);
+int hsi_read_cancel(struct hsi_device *dev);
+int hsi_poll(struct hsi_device *dev);
+int hsi_unpoll(struct hsi_device *dev);
+int hsi_ioctl(struct hsi_device *dev, unsigned int command, void *arg);
+void hsi_close(struct hsi_device *dev);
+void hsi_set_read_cb(struct hsi_device *dev,
+ void (*read_cb) (struct hsi_device *dev,
+ unsigned int size));
+void hsi_set_write_cb(struct hsi_device *dev,
+ void (*write_cb) (struct hsi_device *dev,
+ unsigned int size));
+void hsi_set_port_event_cb(struct hsi_device *dev,
+ void (*port_event_cb) (struct hsi_device *dev,
+ unsigned int event,
+ void *arg));
+#endif /* __HSI_DRIVER_IF_H__ */
diff --git a/include/linux/mpu.h b/include/linux/mpu.h
new file mode 100644
index 0000000..6a48513
--- /dev/null
+++ b/include/linux/mpu.h
@@ -0,0 +1,327 @@
+/*
+ $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,
+
+ 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/mms_ts.h b/include/linux/platform_data/mms_ts.h
new file mode 100644
index 0000000..ddb4683
--- /dev/null
+++ b/include/linux/platform_data/mms_ts.h
@@ -0,0 +1,33 @@
+/*
+ * 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);
+};
+
+#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..a1a404a
--- /dev/null
+++ b/include/linux/platform_data/modem.h
@@ -0,0 +1,90 @@
+/*
+ * 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,
+};
+
+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_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_cp_off;
+ unsigned gpio_slave_wakeup;
+ unsigned gpio_host_wakeup;
+ unsigned gpio_host_active;
+#endif /*CONFIG_LTE_MODEM_CMC221*/
+
+ /* 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..275ed84
--- /dev/null
+++ b/include/linux/platform_data/panel-s6e8aa0.h
@@ -0,0 +1,69 @@
+/*
+ * 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
+
+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 v0;
+ const u32 v1;
+ const u32 v15;
+ const u32 v35;
+ const u32 v59;
+ const u32 v87;
+ const u32 v171;
+ const u32 v255;
+};
+
+struct s6e8aa0_color_adj {
+ u32 mult[3];
+ int rshift;
+};
+
+struct panel_s6e8aa0_data {
+ int reset_gpio;
+ void (* set_power)(bool enable);
+
+ u16 factory_v255_regs[3];
+ struct s6e8aa0_color_adj color_adj;
+
+ const struct s6e8aa0_gamma_adj_points *gamma_adj_points;
+ const struct s6e8aa0_gamma_entry *gamma_table;
+ int gamma_table_size;
+};
+
+#endif
diff --git a/sound/soc/omap/sdp4430.c b/sound/soc/omap/sdp4430.c
index 3d6a554..24eadb5 100644
--- a/sound/soc/omap/sdp4430.c
+++ b/sound/soc/omap/sdp4430.c
@@ -722,8 +722,9 @@ 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");
@@ -731,6 +732,8 @@ static int __init sdp4430_soc_init(void)
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) {