summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/virtual/kvm/api.txt7
-rw-r--r--Makefile2
-rw-r--r--arch/arm/configs/tegra3_android_defconfig2
-rw-r--r--arch/arm/configs/tegra3_defconfig7
-rw-r--r--arch/arm/configs/tegra_cardhu_mods_ldk_defconfig297
-rw-r--r--arch/arm/configs/tegra_defconfig14
-rw-r--r--arch/arm/configs/tegra_p1852_gnu_linux_defconfig227
-rw-r--r--arch/arm/include/asm/sizes.h2
-rw-r--r--arch/arm/mach-tegra/Kconfig7
-rw-r--r--arch/arm/mach-tegra/Makefile11
-rw-r--r--arch/arm/mach-tegra/baseband-xmm-power.c36
-rw-r--r--arch/arm/mach-tegra/baseband-xmm-power2.c5
-rw-r--r--arch/arm/mach-tegra/board-cardhu-panel.c4
-rw-r--r--arch/arm/mach-tegra/board-cardhu-sensors.c2
-rw-r--r--arch/arm/mach-tegra/board-cardhu.c22
-rw-r--r--arch/arm/mach-tegra/board-enterprise-panel.c11
-rw-r--r--arch/arm/mach-tegra/board-enterprise-power.c111
-rw-r--r--arch/arm/mach-tegra/board-enterprise.c24
-rw-r--r--arch/arm/mach-tegra/board-harmony-power.c1
-rw-r--r--arch/arm/mach-tegra/board-kai-kbc.c115
-rw-r--r--arch/arm/mach-tegra/board-kai-memory.c757
-rw-r--r--arch/arm/mach-tegra/board-kai-panel.c703
-rw-r--r--arch/arm/mach-tegra/board-kai-pinmux.c550
-rw-r--r--arch/arm/mach-tegra/board-kai-power.c607
-rw-r--r--arch/arm/mach-tegra/board-kai-sdhci.c163
-rw-r--r--arch/arm/mach-tegra/board-kai.c600
-rw-r--r--arch/arm/mach-tegra/board-kai.h77
-rw-r--r--arch/arm/mach-tegra/board-touch-kai-raydium_spi.c67
-rw-r--r--arch/arm/mach-tegra/board-touch-kai-synaptics-spi.c103
-rw-r--r--arch/arm/mach-tegra/board-ventana-panel.c13
-rw-r--r--arch/arm/mach-tegra/board-ventana-sensors.c17
-rw-r--r--arch/arm/mach-tegra/board-ventana.c3
-rw-r--r--arch/arm/mach-tegra/board-whistler-panel.c11
-rw-r--r--arch/arm/mach-tegra/board-whistler-pinmux.c32
-rw-r--r--arch/arm/mach-tegra/board-whistler.c3
-rw-r--r--arch/arm/mach-tegra/board.h7
-rw-r--r--arch/arm/mach-tegra/clock.h11
-rw-r--r--arch/arm/mach-tegra/common.c74
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.c20
-rw-r--r--arch/arm/mach-tegra/devices.c20
-rw-r--r--arch/arm/mach-tegra/devices.h5
-rw-r--r--arch/arm/mach-tegra/include/mach/dc.h1
-rw-r--r--arch/arm/mach-tegra/include/mach/uncompress.h48
-rw-r--r--arch/arm/mach-tegra/latency_allowance.c2
-rw-r--r--arch/arm/mach-tegra/pcie.c4
-rw-r--r--arch/arm/mach-tegra/pm-t3.c9
-rw-r--r--arch/arm/mach-tegra/pm.c79
-rw-r--r--arch/arm/mach-tegra/tegra3_actmon.c3
-rw-r--r--arch/arm/mach-tegra/tegra3_clocks.c397
-rw-r--r--arch/arm/mach-tegra/tegra3_tsensor.c9
-rw-r--r--arch/arm/mach-tegra/usb_phy.c150
-rw-r--r--arch/arm/tools/mach-types1
-rw-r--r--arch/x86/include/asm/amd_nb.h2
-rw-r--r--arch/x86/kernel/amd_nb.c31
-rw-r--r--arch/x86/kernel/kvmclock.c5
-rw-r--r--arch/x86/kvm/i8254.c10
-rw-r--r--arch/x86/mm/mmap.c4
-rw-r--r--arch/x86/pci/Makefile3
-rw-r--r--arch/x86/pci/acpi.c18
-rw-r--r--arch/x86/pci/amd_bus.c42
-rw-r--r--drivers/gpu/drm/radeon/r100.c5
-rw-r--r--drivers/gpu/drm/radeon/r600_hdmi.c7
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c5
-rw-r--r--drivers/gpu/drm/radeon/rs600.c4
-rw-r--r--drivers/gpu/ion/ion.c26
-rw-r--r--drivers/gpu/ion/ion_iommu_heap.c71
-rw-r--r--drivers/gpu/ion/ion_priv.h4
-rw-r--r--drivers/gpu/ion/tegra/tegra_ion.c7
-rw-r--r--drivers/hid/hid-core.c2
-rw-r--r--drivers/hwmon/tegra-tsensor.c141
-rw-r--r--drivers/i2c/busses/i2c-tegra.c18
-rw-r--r--drivers/input/touchscreen/Kconfig25
-rw-r--r--drivers/input/touchscreen/Makefile2
-rw-r--r--drivers/input/touchscreen/rm31080a_ts.c1819
-rw-r--r--drivers/input/touchscreen/rm31080a_ts.h40
-rw-r--r--drivers/input/touchscreen/rmi4/Makefile18
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_bus.c315
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_dev.c428
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_driver.c1354
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_driver.h109
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_f01.c775
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_f09.c298
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_f11.c1513
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_f19.c1419
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_f34.c821
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_f54.c1347
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_i2c.c421
-rw-r--r--drivers/input/touchscreen/rmi4/rmi_spi.c869
-rw-r--r--drivers/iommu/Kconfig10
-rw-r--r--drivers/iommu/Makefile1
-rw-r--r--drivers/iommu/tegra-gart.c456
-rw-r--r--drivers/md/raid1.c11
-rw-r--r--drivers/media/video/tegra/mediaserver/tegra_mediaserver.c3
-rw-r--r--drivers/media/video/tegra/nvavp/nvavp_dev.c44
-rw-r--r--drivers/media/video/tegra/ov2710.c8
-rw-r--r--drivers/media/video/tegra/ov5650.c100
-rw-r--r--drivers/media/video/tegra/sh532u.c2
-rw-r--r--drivers/media/video/tegra/ssl3250a.c2
-rw-r--r--drivers/media/video/tegra/tegra_camera.c58
-rw-r--r--drivers/media/video/tegra/tps61050.c2
-rw-r--r--drivers/mfd/tps65910-irq.c3
-rw-r--r--drivers/mfd/tps65910.c9
-rw-r--r--drivers/misc/ti-st/st_core.c24
-rw-r--r--drivers/misc/ti-st/st_kim.c31
-rw-r--r--drivers/misc/ti-st/st_ll.c38
-rw-r--r--drivers/mmc/core/sd.c2
-rw-r--r--drivers/mmc/host/sdhci-tegra.c82
-rw-r--r--drivers/mmc/host/sdhci.c8
-rw-r--r--drivers/mmc/host/sdhci.h1
-rw-r--r--drivers/mtd/mtd_blkdevs.c3
-rw-r--r--drivers/mtd/mtdoops.c5
-rw-r--r--drivers/mtd/tests/mtd_stresstest.c7
-rw-r--r--drivers/mtd/ubi/eba.c6
-rw-r--r--drivers/mtd/ubi/ubi.h2
-rw-r--r--drivers/mtd/ubi/wl.c12
-rw-r--r--drivers/net/usb/raw_ip_net.c261
-rw-r--r--drivers/net/wireless/rtlwifi/rtl8192se/fw.c4
-rw-r--r--drivers/pci/msi.c10
-rw-r--r--drivers/platform/x86/ideapad-laptop.c2
-rw-r--r--drivers/pnp/quirks.c42
-rw-r--r--drivers/regulator/Kconfig10
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/tps62360-regulator.c472
-rw-r--r--drivers/regulator/tps65910-regulator.c125
-rw-r--r--drivers/rtc/interface.c4
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_base.c83
-rw-r--r--drivers/scsi/mpt2sas/mpt2sas_scsih.c11
-rw-r--r--drivers/spi/spi-tegra.c2
-rw-r--r--drivers/usb/class/cdc-acm.c47
-rw-r--r--drivers/usb/host/ehci-hcd.c3
-rw-r--r--drivers/usb/host/ehci-tegra.c100
-rw-r--r--drivers/usb/host/ehci.h1
-rw-r--r--drivers/usb/serial/baseband_usb_chr.c19
-rw-r--r--drivers/video/tegra/dc/dc.c45
-rw-r--r--drivers/video/tegra/dc/dc_priv.h9
-rw-r--r--drivers/video/tegra/dc/dsi.c28
-rw-r--r--drivers/video/tegra/dc/ext/dev.c9
-rw-r--r--drivers/video/tegra/dc/nvsd.c3
-rw-r--r--drivers/video/tegra/host/Makefile2
-rw-r--r--drivers/video/tegra/host/bus.c5
-rw-r--r--drivers/video/tegra/host/bus_client.c53
-rw-r--r--drivers/video/tegra/host/bus_client.h35
-rw-r--r--drivers/video/tegra/host/chip_support.h15
-rw-r--r--drivers/video/tegra/host/dev.c108
-rw-r--r--drivers/video/tegra/host/dev.h4
-rw-r--r--drivers/video/tegra/host/gr3d/gr3d.c4
-rw-r--r--drivers/video/tegra/host/gr3d/gr3d_t20.c5
-rw-r--r--drivers/video/tegra/host/gr3d/gr3d_t30.c3
-rw-r--r--drivers/video/tegra/host/gr3d/scale3d.c2
-rw-r--r--drivers/video/tegra/host/host1x/Makefile1
-rw-r--r--drivers/video/tegra/host/host1x/host1x_cdma.c3
-rw-r--r--drivers/video/tegra/host/host1x/host1x_channel.c46
-rw-r--r--drivers/video/tegra/host/host1x/host1x_cpuaccess.c54
-rw-r--r--drivers/video/tegra/host/host1x/host1x_syncpt.c23
-rw-r--r--drivers/video/tegra/host/mpe/mpe.c9
-rw-r--r--drivers/video/tegra/host/nvhost_acm.c60
-rw-r--r--drivers/video/tegra/host/nvhost_acm.h3
-rw-r--r--drivers/video/tegra/host/nvhost_cdma.c3
-rw-r--r--drivers/video/tegra/host/nvhost_cdma.h2
-rw-r--r--drivers/video/tegra/host/nvhost_channel.c64
-rw-r--r--drivers/video/tegra/host/nvhost_channel.h4
-rw-r--r--drivers/video/tegra/host/nvhost_cpuaccess.c120
-rw-r--r--drivers/video/tegra/host/nvhost_cpuaccess.h65
-rw-r--r--drivers/video/tegra/host/nvhost_syncpt.c25
-rw-r--r--drivers/video/tegra/host/nvhost_syncpt.h8
-rw-r--r--drivers/video/tegra/host/t20/t20.c13
-rw-r--r--drivers/video/tegra/host/t20/t20.h3
-rw-r--r--drivers/video/tegra/host/t30/t30.c13
-rw-r--r--drivers/watchdog/tegra_wdt.c18
-rw-r--r--drivers/xen/xenbus/xenbus_xs.c6
-rw-r--r--fs/ext4/super.c7
-rw-r--r--fs/nfs/callback_proc.c2
-rw-r--r--fs/nfs/file.c4
-rw-r--r--fs/nfs/nfs4proc.c96
-rw-r--r--fs/nfs/nfs4xdr.c31
-rw-r--r--fs/nfs/super.c43
-rw-r--r--include/linux/genalloc.h2
-rw-r--r--include/linux/i2c-tegra.h2
-rw-r--r--include/linux/memcontrol.h6
-rw-r--r--include/linux/mfd/tps65910.h33
-rw-r--r--include/linux/mmc/sdhci.h2
-rw-r--r--include/linux/nfs_xdr.h5
-rw-r--r--include/linux/nvhost.h9
-rw-r--r--include/linux/nvhost_ioctl.h11
-rw-r--r--include/linux/pci_regs.h2
-rw-r--r--include/linux/platform_data/rm31080a_ts.h8
-rw-r--r--include/linux/regulator/tps62360.h57
-rw-r--r--include/linux/rmi.h656
-rw-r--r--include/linux/sunrpc/xdr.h2
-rw-r--r--include/linux/tegra_audio.h2
-rw-r--r--include/linux/ti_wilink_st.h6
-rw-r--r--include/linux/usb/usbnet.h2
-rw-r--r--include/linux/wl12xx.h12
-rw-r--r--include/media/ov5650.h10
-rw-r--r--include/xen/interface/io/xs_wire.h3
-rw-r--r--init/do_mounts.c35
-rw-r--r--kernel/sched.c1
-rw-r--r--mm/filemap.c18
-rw-r--r--mm/memcontrol.c44
-rw-r--r--mm/slub.c5
-rw-r--r--net/mac80211/wpa.c2
-rw-r--r--net/sunrpc/xdr.c3
-rw-r--r--security/integrity/ima/ima_api.c4
-rw-r--r--security/integrity/ima/ima_queue.c17
-rw-r--r--sound/pci/hda/hda_local.h7
-rw-r--r--sound/pci/hda/hda_proc.c2
-rw-r--r--sound/pci/hda/patch_cirrus.c27
-rw-r--r--sound/pci/hda/patch_conexant.c2
-rw-r--r--sound/pci/hda/patch_sigmatel.c36
-rw-r--r--sound/pci/hda/patch_via.c5
-rw-r--r--sound/pci/ice1712/amp.c7
-rw-r--r--sound/soc/codecs/wm8753.c11
-rw-r--r--sound/soc/codecs/wm8903.c32
-rw-r--r--sound/soc/tegra/Kconfig22
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra20_das.c32
-rw-r--r--sound/soc/tegra/tegra20_das.h2
-rw-r--r--sound/soc/tegra/tegra30_ahub.c6
-rw-r--r--sound/soc/tegra/tegra30_dam.c6
-rw-r--r--sound/soc/tegra/tegra30_i2s.c8
-rw-r--r--sound/soc/tegra/tegra_aic326x.c29
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.c107
-rw-r--r--sound/soc/tegra/tegra_asoc_utils.h5
-rw-r--r--sound/soc/tegra/tegra_max98088.c53
-rw-r--r--sound/soc/tegra/tegra_max98095.c723
-rw-r--r--sound/soc/tegra/tegra_pcm.c20
-rw-r--r--sound/soc/tegra/tegra_pcm.h8
-rw-r--r--sound/soc/tegra/tegra_wm8753.c191
-rw-r--r--sound/soc/tegra/tegra_wm8903.c60
-rw-r--r--sound/usb/usx2y/usb_stream.c6
-rw-r--r--virt/kvm/assigned-dev.c93
231 files changed, 21523 insertions, 1552 deletions
diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt
index b0e4b9cd6a66..13ab8379b4eb 100644
--- a/Documentation/virtual/kvm/api.txt
+++ b/Documentation/virtual/kvm/api.txt
@@ -1131,6 +1131,13 @@ following flags are specified:
/* Depends on KVM_CAP_IOMMU */
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
+The KVM_DEV_ASSIGN_ENABLE_IOMMU flag is a mandatory option to ensure
+isolation of the device. Usages not specifying this flag are deprecated.
+
+Only PCI header type 0 devices with PCI BAR resources are supported by
+device assignment. The user requesting this ioctl must have read/write
+access to the PCI sysfs resource files associated with the device.
+
4.49 KVM_DEASSIGN_PCI_DEVICE
Capability: KVM_CAP_DEVICE_DEASSIGNMENT
diff --git a/Makefile b/Makefile
index feeb6ce10504..64562abe90a5 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
VERSION = 3
PATCHLEVEL = 1
-SUBLEVEL = 9
+SUBLEVEL = 10
EXTRAVERSION =
NAME = "Divemaster Edition"
diff --git a/arch/arm/configs/tegra3_android_defconfig b/arch/arm/configs/tegra3_android_defconfig
index 4332ea41359e..e77a095d0f6b 100644
--- a/arch/arm/configs/tegra3_android_defconfig
+++ b/arch/arm/configs/tegra3_android_defconfig
@@ -27,6 +27,7 @@ CONFIG_GPIO_PCA953X=y
CONFIG_ARCH_TEGRA_3x_SOC=y
CONFIG_MACH_CARDHU=y
CONFIG_MACH_TEGRA_ENTERPRISE=y
+CONFIG_MACH_KAI=y
CONFIG_TEGRA_PWM=y
CONFIG_TEGRA_EMC_SCALING_ENABLE=y
CONFIG_TEGRA_CLOCK_DEBUG_WRITE=y
@@ -239,6 +240,7 @@ CONFIG_INPUT_JOYSTICK=y
CONFIG_JOYSTICK_XPAD=y
CONFIG_INPUT_TOUCHSCREEN=y
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_TOUCHSCREEN_RM31080A=y
CONFIG_INPUT_MISC=y
CONFIG_INPUT_UINPUT=y
CONFIG_INPUT_GPIO=y
diff --git a/arch/arm/configs/tegra3_defconfig b/arch/arm/configs/tegra3_defconfig
index 348cf7930a97..fdbdfaf20691 100644
--- a/arch/arm/configs/tegra3_defconfig
+++ b/arch/arm/configs/tegra3_defconfig
@@ -303,11 +303,12 @@ CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
CONFIG_TEGRA_NVAVP=y
CONFIG_VIDEO_OV5650=m
CONFIG_VIDEO_OV2710=m
-CONFIG_TORCH_SSL3250A=y
-CONFIG_TORCH_TPS61050=y
-CONFIG_VIDEO_SH532U=y
+CONFIG_TORCH_SSL3250A=m
+CONFIG_TORCH_TPS61050=m
+CONFIG_VIDEO_SH532U=m
CONFIG_USB_VIDEO_CLASS=y
# CONFIG_RADIO_ADAPTERS is not set
+# CONFIG_VGA_ARB is not set
CONFIG_VIDEO_OUTPUT_CONTROL=y
CONFIG_FB=y
CONFIG_TEGRA_GRHOST=y
diff --git a/arch/arm/configs/tegra_cardhu_mods_ldk_defconfig b/arch/arm/configs/tegra_cardhu_mods_ldk_defconfig
new file mode 100644
index 000000000000..66722842964b
--- /dev/null
+++ b/arch/arm/configs/tegra_cardhu_mods_ldk_defconfig
@@ -0,0 +1,297 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_SYSVIPC=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=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_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS_ALL=y
+CONFIG_KALLSYMS_EXTRA_PASS=y
+# CONFIG_ELF_CORE is not set
+CONFIG_EMBEDDED=y
+CONFIG_SLAB=y
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_TEGRA=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_ARCH_TEGRA_3x_SOC=y
+CONFIG_TEGRA_PCI=y
+CONFIG_MACH_CARDHU=y
+CONFIG_MACH_TEGRA_ENTERPRISE=y
+CONFIG_TEGRA_EMC_SCALING_ENABLE=y
+CONFIG_TEGRA_EMC_TO_DDR_CLOCK=2
+CONFIG_USB_HOTPLUG=y
+CONFIG_ARM_ERRATA_742230=y
+CONFIG_ARM_ERRATA_743622=y
+CONFIG_ARM_ERRATA_751472=y
+CONFIG_ARM_ERRATA_752520=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_PM_RUNTIME=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=m
+CONFIG_NET_KEY=m
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_CUBIC=m
+# CONFIG_TCP_CONG_HTCP is not set
+CONFIG_TCP_CONG_VEGAS=m
+CONFIG_TCP_CONG_VENO=m
+CONFIG_TCP_CONG_ILLINOIS=m
+# CONFIG_IPV6 is not set
+# CONFIG_ANDROID_PARANOID_NETWORK is not set
+# CONFIG_NET_ACTIVITY_STATS is not set
+CONFIG_NETFILTER=y
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NF_CONNTRACK_IPV4=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_MQPRIO=m
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_MISC_DEVICES=y
+CONFIG_AD525X_DPOT=y
+CONFIG_AD525X_DPOT_I2C=y
+CONFIG_ICS932S401=y
+CONFIG_APDS9802ALS=y
+CONFIG_SENSORS_NCT1008=y
+CONFIG_ISL29003=y
+CONFIG_SENSORS_AK8975=y
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+CONFIG_R8169=y
+# CONFIG_NETDEV_10000 is not set
+CONFIG_BCM4329=m
+CONFIG_BCM4329_FW_PATH="/lib/firmware/bcm4329/fw_bcm4329.bin"
+CONFIG_BCM4329_NVRAM_PATH="/lib/firmware/bcm4329/nvram.txt"
+CONFIG_BCM4329_WIFI_CONTROL_FUNC=y
+CONFIG_USB_CATC=y
+CONFIG_USB_KAWETH=y
+CONFIG_USB_PEGASUS=y
+CONFIG_USB_RTL8150=y
+CONFIG_USB_USBNET=y
+# CONFIG_USB_NET_CDCETHER is not set
+# CONFIG_USB_NET_CDC_NCM is not set
+CONFIG_USB_NET_DM9601=y
+CONFIG_USB_NET_SMSC75XX=y
+CONFIG_USB_NET_SMSC95XX=y
+# CONFIG_USB_NET_NET1080 is not set
+CONFIG_USB_NET_MCS7830=y
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+CONFIG_INPUT_EVDEV=y
+CONFIG_KEYBOARD_GPIO=y
+CONFIG_KEYBOARD_INTERRUPT=y
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_ATMEL_MXT=y
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=y
+CONFIG_INPUT_GPIO=y
+CONFIG_INPUT_ALPS_GPIO_SCROLLWHEEL=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MUX=y
+CONFIG_I2C_MUX_PCA954x=y
+CONFIG_I2C_TEGRA=y
+CONFIG_SENSORS_LM90=y
+CONFIG_MFD_MAX8907C=y
+CONFIG_MFD_TPS6586X=y
+CONFIG_MFD_TPS6591X=y
+CONFIG_MFD_TPS80031=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_GPIO=y
+CONFIG_REGULATOR_MAX8907C=y
+CONFIG_REGULATOR_TPS6586X=y
+CONFIG_REGULATOR_TPS6591X=y
+CONFIG_REGULATOR_TPS6236X=y
+CONFIG_REGULATOR_TPS80031=y
+CONFIG_REGULATOR_GPIO_SWITCH=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_MEDIA_TUNER_SIMPLE is not set
+# CONFIG_MEDIA_TUNER_TDA8290 is not set
+# CONFIG_MEDIA_TUNER_TDA827X is not set
+# CONFIG_MEDIA_TUNER_TDA18271 is not set
+# CONFIG_MEDIA_TUNER_TDA9887 is not set
+# CONFIG_MEDIA_TUNER_TEA5761 is not set
+# CONFIG_MEDIA_TUNER_TEA5767 is not set
+# CONFIG_MEDIA_TUNER_MT20XX is not set
+# CONFIG_MEDIA_TUNER_MT2060 is not set
+# CONFIG_MEDIA_TUNER_MT2266 is not set
+# CONFIG_MEDIA_TUNER_MT2131 is not set
+# CONFIG_MEDIA_TUNER_QT1010 is not set
+# CONFIG_MEDIA_TUNER_XC2028 is not set
+# CONFIG_MEDIA_TUNER_XC5000 is not set
+# CONFIG_MEDIA_TUNER_MXL5005S is not set
+# CONFIG_MEDIA_TUNER_MXL5007T is not set
+# CONFIG_MEDIA_TUNER_MC44S803 is not set
+# CONFIG_MEDIA_TUNER_MAX2165 is not set
+# CONFIG_MEDIA_TUNER_TDA18218 is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+# CONFIG_TEGRA_AVP is not set
+# CONFIG_TEGRA_MEDIASERVER is not set
+CONFIG_TEGRA_NVAVP=y
+CONFIG_VIDEO_OV5650=m
+CONFIG_VIDEO_OV2710=m
+CONFIG_TORCH_SSL3250A=m
+CONFIG_TORCH_TPS61050=m
+CONFIG_VIDEO_SH532U=m
+# CONFIG_V4L_USB_DRIVERS is not set
+# CONFIG_RADIO_ADAPTERS is not set
+CONFIG_FB=y
+CONFIG_TEGRA_GRHOST=y
+CONFIG_TEGRA_DC=y
+CONFIG_TEGRA_NVHDCP=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_SND_SOC_TEGRA=y
+CONFIG_SND_SOC_TEGRA_WM8903=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+# CONFIG_USB_DEVICE_CLASS is not set
+CONFIG_USB_SUSPEND=y
+CONFIG_USB_OTG=y
+# CONFIG_USB_OTG_WHITELIST is not set
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_STORAGE=y
+CONFIG_USB_LIBUSUAL=y
+CONFIG_USB_GADGET=y
+CONFIG_USB_TEGRA_OTG=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_PLTFM=y
+CONFIG_MMC_SDHCI_TEGRA=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_TPS6591x=y
+CONFIG_RTC_DRV_TPS80031=y
+CONFIG_STAGING=y
+# CONFIG_STAGING_EXCLUDE_BUILD is not set
+CONFIG_IIO=y
+CONFIG_SENSORS_ISL29018=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+# CONFIG_DNOTIFY is not set
+CONFIG_FUSE_FS=m
+CONFIG_ISO9660_FS=m
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_NTFS_FS=m
+CONFIG_NTFS_RW=y
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_ROOT_NFS=y
+CONFIG_CIFS=m
+CONFIG_CIFS_EXPERIMENTAL=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_ASCII=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_UTF8=m
+CONFIG_PRINTK_TIME=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_KERNEL=y
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_SLAB=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_VM=y
+CONFIG_DEBUG_SG=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+CONFIG_FUNCTION_TRACER=y
+CONFIG_CRYPTO_AUTHENC=y
+CONFIG_CRYPTO_CBC=y
+CONFIG_CRYPTO_ECB=y
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_MD5=y
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_DES=y
+CONFIG_CRYPTO_TWOFISH=y
+CONFIG_CRYPTO_DEFLATE=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRYPTO_DEV_TEGRA_SE=y
+CONFIG_CRC_CCITT=y
diff --git a/arch/arm/configs/tegra_defconfig b/arch/arm/configs/tegra_defconfig
index a2f2d27ad593..2e5e33bf11bc 100644
--- a/arch/arm/configs/tegra_defconfig
+++ b/arch/arm/configs/tegra_defconfig
@@ -191,6 +191,7 @@ CONFIG_APDS9802ALS=y
CONFIG_SENSORS_NCT1008=y
CONFIG_ISL29003=y
CONFIG_SENSORS_AK8975=y
+CONFIG_SENSORS_NCT1008=y
CONFIG_UID_STAT=y
CONFIG_BCM4329_RFKILL=y
CONFIG_TEGRA_CRYPTO_DEV=y
@@ -278,14 +279,15 @@ CONFIG_REGULATOR_TPS6586X=y
CONFIG_MEDIA_SUPPORT=y
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
-CONFIG_VIDEO_OV5650=y
-CONFIG_VIDEO_OV2710=y
-CONFIG_VIDEO_SOC380=y
-CONFIG_TORCH_SSL3250A=y
-CONFIG_VIDEO_SH532U=y
-CONFIG_VIDEO_AD5820=y
+CONFIG_VIDEO_OV5650=m
+CONFIG_VIDEO_OV2710=m
+CONFIG_VIDEO_SOC380=m
+CONFIG_TORCH_SSL3250A=m
+CONFIG_VIDEO_SH532U=m
+CONFIG_VIDEO_AD5820=m
CONFIG_USB_VIDEO_CLASS=y
# CONFIG_RADIO_ADAPTERS is not set
+# CONFIG_VGA_ARB is not set
CONFIG_VIDEO_OUTPUT_CONTROL=y
CONFIG_FB=y
CONFIG_TEGRA_GRHOST=y
diff --git a/arch/arm/configs/tegra_p1852_gnu_linux_defconfig b/arch/arm/configs/tegra_p1852_gnu_linux_defconfig
new file mode 100644
index 000000000000..3e8a44d25e36
--- /dev/null
+++ b/arch/arm/configs/tegra_p1852_gnu_linux_defconfig
@@ -0,0 +1,227 @@
+CONFIG_EXPERIMENTAL=y
+CONFIG_CROSS_COMPILE="arm-eabi-"
+# CONFIG_LOCALVERSION_AUTO is not set
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=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_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_PANIC_TIMEOUT=10
+CONFIG_EMBEDDED=y
+# CONFIG_SYSCTL_SYSCALL is not set
+# CONFIG_ELF_CORE is not set
+CONFIG_ASHMEM=y
+CONFIG_SLAB=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_ARCH_TEGRA=y
+CONFIG_ARCH_TEGRA_3x_SOC=y
+CONFIG_TEGRA_PCI=y
+CONFIG_MACH_P1852=y
+CONFIG_TEGRA_DEBUG_UARTB=y
+CONFIG_TEGRA_PWM=y
+# CONFIG_TEGRA_CPU_DVFS is not set
+CONFIG_TEGRA_CLOCK_DEBUG_WRITE=y
+CONFIG_TEGRA_MC_PROFILE=y
+CONFIG_ARM_ERRATA_743622=y
+CONFIG_ARM_ERRATA_751472=y
+CONFIG_ARM_ERRATA_752520=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_SMP=y
+CONFIG_PREEMPT=y
+CONFIG_AEABI=y
+# CONFIG_OABI_COMPAT is not set
+CONFIG_HIGHMEM=y
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="mem=448@2048M console=ttyS0,115200n8 earlyprintk init=/bin/ash"
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=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_INET_DIAG 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_ANDROID_PARANOID_NETWORK is not set
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_MTD=y
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_CMDLINE_PARTS=y
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+CONFIG_MTD_CFI=y
+CONFIG_MTD_CFI_INTELEXT=y
+CONFIG_MTD_CFI_AMDSTD=y
+CONFIG_MTD_CFI_STAA=y
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+CONFIG_MTD_NOR_TEGRA=y
+CONFIG_MTD_NAND_TEGRA=y
+CONFIG_MTD_NAND=y
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_ANDROID_PMEM is not set
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+# CONFIG_SCSI_LOWLEVEL is not set
+CONFIG_MD=y
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=y
+CONFIG_DM_UEVENT=y
+CONFIG_NETDEVICES=y
+CONFIG_DUMMY=y
+# CONFIG_NETDEV_1000 is not set
+# CONFIG_NETDEV_10000 is not set
+# CONFIG_WLAN is not set
+CONFIG_USB_USBNET=y
+CONFIG_USB_NET_SMSC75XX=y
+CONFIG_USB_NET_SMSC95XX=y
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_NET_CDC_SUBSET is not set
+# CONFIG_USB_NET_ZAURUS is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_TEGRA=y
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_SERIO is not set
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_TEGRA=y
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_MUX=y
+CONFIG_I2C_MUX_PCA954x=y
+CONFIG_I2C_TEGRA=y
+CONFIG_SPI=y
+CONFIG_SPI_TEGRA=y
+CONFIG_SPI_SLAVE_TEGRA=y
+CONFIG_SPI_SPIDEV=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_PCA953X=y
+CONFIG_POWER_SUPPLY=y
+CONFIG_PDA_POWER=y
+CONFIG_BATTERY_BQ20Z75=y
+CONFIG_MFD_TPS6591X=y
+CONFIG_REGULATOR=y
+CONFIG_REGULATOR_FIXED_VOLTAGE=y
+CONFIG_REGULATOR_VIRTUAL_CONSUMER=y
+CONFIG_REGULATOR_TPS6591X=y
+CONFIG_REGULATOR_TPS6236X=y
+CONFIG_REGULATOR_GPIO_SWITCH=y
+CONFIG_MEDIA_SUPPORT=y
+CONFIG_VIDEO_DEV=y
+# CONFIG_TEGRA_AVP is not set
+# CONFIG_TEGRA_MEDIASERVER is not set
+CONFIG_TEGRA_NVAVP=y
+# CONFIG_VGA_ARB is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_FB=y
+CONFIG_TEGRA_GRHOST=y
+CONFIG_TEGRA_DC=y
+CONFIG_TEGRA_DC_EXTENSIONS=y
+CONFIG_NVMAP_SEARCH_GLOBAL_HANDLES=y
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_LCD_CLASS_DEVICE=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=y
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_PWM=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SOC=y
+CONFIG_TEGRA_ALSA=y
+CONFIG_SND_SOC_GENERIC_CODEC=y
+CONFIG_USB=y
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+CONFIG_USB_DEVICEFS=y
+CONFIG_USB_EHCI_HCD=y
+# CONFIG_USB_EHCI_TT_NEWSCHED is not set
+CONFIG_USB_STORAGE=y
+CONFIG_USB_LIBUSUAL=y
+CONFIG_USB_TEGRA_OTG=y
+CONFIG_MMC=y
+CONFIG_MMC_UNSAFE_RESUME=y
+CONFIG_MMC_EMBEDDED_SDIO=y
+CONFIG_MMC_PARANOID_SD_INIT=y
+# CONFIG_MMC_BLOCK_BOUNCE is not set
+CONFIG_MMC_BLOCK_DEFERRED_RESUME=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_TEGRA=y
+CONFIG_SWITCH=y
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_TEGRA=y
+CONFIG_STAGING=y
+# CONFIG_STAGING_EXCLUDE_BUILD is not set
+CONFIG_IIO=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_EXT4_FS=y
+# CONFIG_DNOTIFY is not set
+CONFIG_FUSE_FS=y
+CONFIG_VFAT_FS=y
+CONFIG_TMPFS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_ROOT_NFS=y
+# CONFIG_RPCSEC_GSS_KRB5 is not set
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_EFI_PARTITION=y
+CONFIG_NLS_CODEPAGE_437=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_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+# CONFIG_DEBUG_PREEMPT is not set
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_VM=y
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_ARM_UNWIND is not set
+CONFIG_DEBUG_LL=y
+CONFIG_EARLY_PRINTK=y
+CONFIG_CRYPTO_ECB=y
+CONFIG_CRYPTO_AES=y
+CONFIG_CRYPTO_ARC4=y
+CONFIG_CRYPTO_TWOFISH=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+CONFIG_CRC_CCITT=y
+CONFIG_LIBCRC32C=y
diff --git a/arch/arm/include/asm/sizes.h b/arch/arm/include/asm/sizes.h
index 1ec30e944084..d208fc074219 100644
--- a/arch/arm/include/asm/sizes.h
+++ b/arch/arm/include/asm/sizes.h
@@ -19,4 +19,4 @@
#include <asm-generic/sizes.h>
#define SZ_48M (SZ_32M + SZ_16M)
-#define SZ_160M (SZ_128M | SZ_32M) \ No newline at end of file
+#define SZ_160M (SZ_128M | SZ_32M)
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index 54df114bc8a9..0d2b3b0919cb 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -151,6 +151,13 @@ config MACH_TEGRA_ENTERPRISE
help
Support for NVIDIA Enterprise development platform
+config MACH_KAI
+ bool "Kai board"
+ depends on ARCH_TEGRA_3x_SOC
+ select MACH_HAS_SND_SOC_TEGRA_RT5640 if SND_SOC
+ help
+ Support for NVIDIA KAI development platform
+
choice
prompt "Tegra platform type"
default TEGRA_SILICON_PLATFORM
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index d28a72c08f8f..d6e6fbb187d8 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -158,6 +158,9 @@ obj-${CONFIG_MACH_CARDHU} += board-cardhu-sensors.o
obj-${CONFIG_MACH_CARDHU} += board-cardhu-memory.o
obj-${CONFIG_MACH_CARDHU} += board-cardhu-powermon.o
+obj-${CONFIG_MACH_KAI} += board-touch-kai-synaptics-spi.o
+obj-${CONFIG_MACH_KAI} += board-touch-kai-raydium_spi.o
+
obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise.o
obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-panel.o
obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-pinmux.o
@@ -168,6 +171,14 @@ obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-baseband.o
obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-kbc.o
obj-${CONFIG_MACH_TEGRA_ENTERPRISE} += board-enterprise-sensors.o
+obj-${CONFIG_MACH_KAI} += board-kai.o
+obj-${CONFIG_MACH_KAI} += board-kai-kbc.o
+obj-${CONFIG_MACH_KAI} += board-kai-memory.o
+obj-${CONFIG_MACH_KAI} += board-kai-panel.o
+obj-${CONFIG_MACH_KAI} += board-kai-pinmux.o
+obj-${CONFIG_MACH_KAI} += board-kai-power.o
+obj-${CONFIG_MACH_KAI} += board-kai-sdhci.o
+
obj-${CONFIG_TEGRA_BB_XMM_POWER} += baseband-xmm-power.o
obj-${CONFIG_TEGRA_BB_XMM_POWER2} += baseband-xmm-power2.o
diff --git a/arch/arm/mach-tegra/baseband-xmm-power.c b/arch/arm/mach-tegra/baseband-xmm-power.c
index 54550352d849..5d7958b63d54 100644
--- a/arch/arm/mach-tegra/baseband-xmm-power.c
+++ b/arch/arm/mach-tegra/baseband-xmm-power.c
@@ -28,6 +28,7 @@
#include <linux/uaccess.h>
#include <linux/wakelock.h>
#include <linux/usb.h>
+#include <linux/pm_runtime.h>
#include <mach/usb_phy.h>
#include "board.h"
#include "devices.h"
@@ -96,6 +97,8 @@ static struct usb_device *usbdev;
static bool CP_initiated_L2toL0;
static bool modem_power_on;
static int power_onoff;
+static int reenable_autosuspend;
+static struct work_struct autopm_resume_work;
static void baseband_xmm_power_L2_resume(void);
static int baseband_modem_power_on(struct baseband_power_platform_data *data)
@@ -130,6 +133,8 @@ static int baseband_xmm_power_on(struct platform_device *device)
pr_err("%s: !data\n", __func__);
return -EINVAL;
}
+ if (baseband_xmm_powerstate != BBXMM_PS_UNINIT)
+ return -EINVAL;
/* reset the state machine */
baseband_xmm_powerstate = BBXMM_PS_INIT;
@@ -177,6 +182,8 @@ static int baseband_xmm_power_off(struct platform_device *device)
pr_debug("%s {\n", __func__);
+ if (baseband_xmm_powerstate == BBXMM_PS_UNINIT)
+ return -EINVAL;
/* check for device / platform data */
if (!device) {
pr_err("%s: !device\n", __func__);
@@ -189,8 +196,9 @@ static int baseband_xmm_power_off(struct platform_device *device)
return -EINVAL;
}
+ ipc_ap_wake_state = IPC_AP_WAKE_UNINIT;
+
/* unregister usb host controller */
- pr_info("%s: hsic device: %x\n", __func__, data->modem.xmm.hsic_device);
if (data->hsic_unregister)
data->hsic_unregister(data->modem.xmm.hsic_device);
else
@@ -207,6 +215,7 @@ static int baseband_xmm_power_off(struct platform_device *device)
gpio_set_value(data->modem.xmm.bb_rst, 0);
mdelay(1);
+ baseband_xmm_powerstate = BBXMM_PS_UNINIT;
pr_debug("%s }\n", __func__);
return 0;
@@ -384,6 +393,10 @@ irqreturn_t baseband_xmm_power_ipc_ap_wake_irq(int irq, void *dev_id)
(baseband_power_driver_data->
modem.xmm.ipc_bb_wake, 0);
pr_debug("gpio slave wakeup done ->\n");
+ if ((reenable_autosuspend) && (usbdev)) {
+ reenable_autosuspend = false;
+ queue_work(workqueue, &autopm_resume_work);
+ }
}
baseband_xmm_set_power_status(BBXMM_PS_L0);
}
@@ -451,6 +464,22 @@ static void baseband_xmm_power_init2_work(struct work_struct *work)
}
+static void baseband_xmm_power_autopm_resume(struct work_struct *work)
+{
+ struct usb_interface *intf;
+
+ pr_debug("%s\n", __func__);
+ if (usbdev) {
+
+ usb_lock_device(usbdev);
+ intf = usb_ifnum_to_if(usbdev, 0);
+ usb_autopm_get_interface(intf);
+ usb_autopm_put_interface(intf);
+ usb_unlock_device(usbdev);
+ }
+}
+
+
/* Do the work for AP/CP initiated L2->L0 */
static void baseband_xmm_power_L2_resume(void)
{
@@ -667,7 +696,7 @@ static int baseband_xmm_power_driver_probe(struct platform_device *device)
int err;
pr_debug("%s\n", __func__);
- pr_debug("[XMM] enum_delay_ms=%d\n", enum_delay_ms);
+ pr_debug("[XMM] enum_delay_ms=%ld\n", enum_delay_ms);
/* check for platform data */
if (!data)
@@ -757,6 +786,7 @@ static int baseband_xmm_power_driver_probe(struct platform_device *device)
INIT_WORK(&init1_work, baseband_xmm_power_init1_work);
INIT_WORK(&init2_work, baseband_xmm_power_init2_work);
INIT_WORK(&L2_resume_work, baseband_xmm_power_L2_resume_work);
+ INIT_WORK(&autopm_resume_work, baseband_xmm_power_autopm_resume);
/* init state variables */
register_hsic_device = true;
@@ -838,7 +868,6 @@ static int baseband_xmm_power_driver_resume(struct platform_device *device)
/* check if modem is on */
if (power_onoff == 0) {
pr_debug("%s - flight mode - nop\n", __func__);
- baseband_xmm_set_power_status(BBXMM_PS_L3TOL0);
return 0;
}
@@ -861,6 +890,7 @@ static int baseband_xmm_power_driver_resume(struct platform_device *device)
} else {
pr_info("CP L3 -> L0\n");
}
+ reenable_autosuspend = true;
return 0;
}
diff --git a/arch/arm/mach-tegra/baseband-xmm-power2.c b/arch/arm/mach-tegra/baseband-xmm-power2.c
index dd05202ba6ec..77ba073d751c 100644
--- a/arch/arm/mach-tegra/baseband-xmm-power2.c
+++ b/arch/arm/mach-tegra/baseband-xmm-power2.c
@@ -611,7 +611,10 @@ static int baseband_xmm_power2_driver_remove(struct platform_device *device)
}
/* free work structure */
- destroy_workqueue(workqueue);
+ if (workqueue) {
+ cancel_work_sync(baseband_xmm_power2_work);
+ destroy_workqueue(workqueue);
+ }
kfree(baseband_xmm_power2_work);
baseband_xmm_power2_work = (struct baseband_xmm_power_work_t *) 0;
diff --git a/arch/arm/mach-tegra/board-cardhu-panel.c b/arch/arm/mach-tegra/board-cardhu-panel.c
index b64a453f9750..90801b0e610d 100644
--- a/arch/arm/mach-tegra/board-cardhu-panel.c
+++ b/arch/arm/mach-tegra/board-cardhu-panel.c
@@ -942,6 +942,8 @@ static struct tegra_dc_out cardhu_disp1_out = {
.parent_clk = "pll_p",
#ifndef CONFIG_TEGRA_CARDHU_DSI
+ .parent_clk_backup = "pll_d2_out0",
+
.type = TEGRA_DC_OUT_RGB,
.depth = 18,
.dither = TEGRA_DC_ORDERED_DITHER,
@@ -1074,7 +1076,7 @@ static struct ion_platform_data tegra_ion_data = {
.name = "iommu",
.base = TEGRA_SMMU_BASE,
.size = TEGRA_SMMU_SIZE,
- .dev = &tegra_iommu_device.dev,
+ .priv = &tegra_iommu_device.dev,
},
},
};
diff --git a/arch/arm/mach-tegra/board-cardhu-sensors.c b/arch/arm/mach-tegra/board-cardhu-sensors.c
index c96933760bf9..b1dd860c6cb0 100644
--- a/arch/arm/mach-tegra/board-cardhu-sensors.c
+++ b/arch/arm/mach-tegra/board-cardhu-sensors.c
@@ -655,7 +655,7 @@ static struct i2c_board_info cardhu_i2c4_nct1008_board_info[] = {
static int cardhu_nct1008_init(void)
{
int nct1008_port = -1;
- int ret;
+ int ret = 0;
if ((board_info.board_id == BOARD_E1198) ||
(board_info.board_id == BOARD_E1291) ||
diff --git a/arch/arm/mach-tegra/board-cardhu.c b/arch/arm/mach-tegra/board-cardhu.c
index bea6a6f19882..5b0f0d807bae 100644
--- a/arch/arm/mach-tegra/board-cardhu.c
+++ b/arch/arm/mach-tegra/board-cardhu.c
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-tegra/board-cardhu.c
*
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2011-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -184,10 +184,10 @@ static __initdata struct tegra_clk_init_table cardhu_clk_init_table[] = {
{ "i2s1", "pll_a_out0", 0, false},
{ "i2s3", "pll_a_out0", 0, false},
{ "spdif_out", "pll_a_out0", 0, false},
- { "d_audio", "pll_a_out0", 0, false},
- { "dam0", "pll_a_out0", 0, false},
- { "dam1", "pll_a_out0", 0, false},
- { "dam2", "pll_a_out0", 0, false},
+ { "d_audio", "clk_m", 12000000, false},
+ { "dam0", "clk_m", 12000000, false},
+ { "dam1", "clk_m", 12000000, false},
+ { "dam2", "clk_m", 12000000, false},
{ "audio1", "i2s1_sync", 0, false},
{ "audio3", "i2s3_sync", 0, false},
{ "vi_sensor", "pll_p", 150000000, false},
@@ -346,6 +346,8 @@ static void __init uart_debug_init(void)
(board_info.board_id == BOARD_E1257))
debug_port_id = 1;
}
+
+ tegra_init_debug_uart_rate();
switch (debug_port_id) {
case 0:
/* UARTA is the debug port. */
@@ -1011,14 +1013,6 @@ static void __init tegra_cardhu_init(void)
#endif
}
-static void __init cardhu_ramconsole_reserve(unsigned long size)
-{
- struct resource *res;
- long ret;
-
- tegra_ram_console_debug_reserve(SZ_1M);
-}
-
static void __init tegra_cardhu_reserve(void)
{
#if defined(CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM)
@@ -1027,7 +1021,7 @@ static void __init tegra_cardhu_reserve(void)
#else
tegra_reserve(SZ_128M, SZ_8M, SZ_8M);
#endif
- cardhu_ramconsole_reserve(SZ_1M);
+ tegra_ram_console_debug_reserve(SZ_1M);
}
MACHINE_START(CARDHU, "cardhu")
diff --git a/arch/arm/mach-tegra/board-enterprise-panel.c b/arch/arm/mach-tegra/board-enterprise-panel.c
index a94eb40b3b7d..ec7eae3b365d 100644
--- a/arch/arm/mach-tegra/board-enterprise-panel.c
+++ b/arch/arm/mach-tegra/board-enterprise-panel.c
@@ -722,9 +722,14 @@ static void enterprise_panel_early_suspend(struct early_suspend *h)
#ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
cpufreq_save_default_governor();
cpufreq_set_conservative_governor();
- cpufreq_set_conservative_governor_param(
- SET_CONSERVATIVE_GOVERNOR_UP_THRESHOLD,
- SET_CONSERVATIVE_GOVERNOR_DOWN_THRESHOLD);
+ cpufreq_set_conservative_governor_param("up_threshold",
+ SET_CONSERVATIVE_GOVERNOR_UP_THRESHOLD);
+
+ cpufreq_set_conservative_governor_param("down_threshold",
+ SET_CONSERVATIVE_GOVERNOR_DOWN_THRESHOLD);
+
+ cpufreq_set_conservative_governor_param("freq_step",
+ SET_CONSERVATIVE_GOVERNOR_FREQ_STEP);
#endif
}
diff --git a/arch/arm/mach-tegra/board-enterprise-power.c b/arch/arm/mach-tegra/board-enterprise-power.c
index fbdfa5b37961..3a6ea00286e9 100644
--- a/arch/arm/mach-tegra/board-enterprise-power.c
+++ b/arch/arm/mach-tegra/board-enterprise-power.c
@@ -30,6 +30,8 @@
#include <linux/io.h>
#include <linux/cpumask.h>
#include <linux/platform_data/tegra_bpc_mgmt.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/gpio-regulator.h>
#include <mach/edp.h>
#include <mach/iomap.h>
@@ -356,7 +358,7 @@ static struct regulator_consumer_supply fixed_reg_vdd_fuse_en_supply[] = {
};
/* LCD-D17 (GPIO M1) from T30*/
-static struct regulator_consumer_supply fixed_reg_sdmmc3_vdd_sel_supply[] = {
+static struct regulator_consumer_supply gpio_reg_sdmmc3_vdd_sel_supply[] = {
REGULATOR_SUPPLY("vddio_sdmmc3_2v85_1v8", NULL),
REGULATOR_SUPPLY("sdmmc3_compu_pu", NULL),
REGULATOR_SUPPLY("vddio_sdmmc3", NULL),
@@ -399,6 +401,70 @@ static struct regulator_consumer_supply fixed_reg_cam_ldo_1v8_en_supply[] = {
REGULATOR_SUPPLY("vdd", "7-0036"),
};
+static struct gpio_regulator_state gpio_reg_sdmmc3_vdd_sel_states[] = {
+ {
+ .gpios = 0,
+ .value = 2850000,
+ },
+ {
+ .gpios = 1,
+ .value = 1800000,
+ },
+};
+
+static struct gpio gpio_reg_sdmmc3_vdd_sel_gpios[] = {
+ {
+ .gpio = TEGRA_GPIO_PM1,
+ .flags = 0,
+ .label = "sdmmc3_vdd_sel",
+ },
+};
+
+/* Macro for defining gpio regulator device data */
+#define GPIO_REG(_id, _name, _input_supply, _active_high, \
+ _boot_state, _delay_us, _minmv, _maxmv) \
+ static struct regulator_init_data ri_data_##_name = \
+ { \
+ .supply_regulator = _input_supply, \
+ .num_consumer_supplies = \
+ ARRAY_SIZE(gpio_reg_##_name##_supply), \
+ .consumer_supplies = gpio_reg_##_name##_supply, \
+ .constraints = { \
+ .name = "gpio_reg_"#_name, \
+ .min_uV = (_minmv)*1000, \
+ .max_uV = (_maxmv)*1000, \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ }, \
+ }; \
+ static struct gpio_regulator_config gpio_reg_##_name##_pdata = \
+ { \
+ .supply_name = _input_supply, \
+ .enable_gpio = -EINVAL, \
+ .enable_high = _active_high, \
+ .enabled_at_boot = _boot_state, \
+ .startup_delay = _delay_us, \
+ .gpios = gpio_reg_##_name##_gpios, \
+ .nr_gpios = ARRAY_SIZE(gpio_reg_##_name##_gpios), \
+ .states = gpio_reg_##_name##_states, \
+ .nr_states = ARRAY_SIZE(gpio_reg_##_name##_states), \
+ .type = REGULATOR_VOLTAGE, \
+ .init_data = &ri_data_##_name, \
+ }; \
+ static struct platform_device gpio_reg_##_name##_dev = { \
+ .name = "gpio-regulator", \
+ .id = _id, \
+ .dev = { \
+ .platform_data = &gpio_reg_##_name##_pdata, \
+ }, \
+ }
+
+GPIO_REG(4, sdmmc3_vdd_sel, tps80031_rails(SMPS4),
+ true, false, 0, 1000, 3300);
+
/* Macro for defining fixed regulator sub device data */
#define FIXED_REG(_id, _name, _input_supply, _gpio_nr, _active_high, \
_millivolts, _boot_state) \
@@ -441,8 +507,6 @@ FIXED_REG(2, pmu_hdmi_5v0_en, "fixed_reg_pmu_5v15_en",
ENT_TPS80031_GPIO_SYSEN, true, 5000, 0);
FIXED_REG(3, vdd_fuse_en, "fixed_reg_pmu_3v3_en",
TEGRA_GPIO_PM0, true, 3300, 0);
-FIXED_REG(4, sdmmc3_vdd_sel, tps80031_rails(SMPS4),
- TEGRA_GPIO_PM1, true, 2850, 0);
FIXED_REG(5, cam_ldo_2v8_en, NULL,
TEGRA_GPIO_PM7, true, 2800, 0);
FIXED_REG(6, cam_ldo_1v8_en, NULL,
@@ -454,16 +518,18 @@ static struct platform_device *fixed_regs_devices[] = {
ADD_FIXED_REG(pmu_3v3_en),
ADD_FIXED_REG(pmu_hdmi_5v0_en),
ADD_FIXED_REG(vdd_fuse_en),
- ADD_FIXED_REG(sdmmc3_vdd_sel),
ADD_FIXED_REG(cam_ldo_2v8_en),
ADD_FIXED_REG(cam_ldo_1v8_en),
};
+#define ADD_GPIO_REG(_name) (&gpio_reg_##_name##_dev)
+static struct platform_device *gpio_regs_devices[] = {
+ ADD_GPIO_REG(sdmmc3_vdd_sel),
+};
+
static int __init enterprise_fixed_regulator_init(void)
{
int i;
- if (!is_enterprise_machine)
- return 0;
for (i = 0; i < ARRAY_SIZE(fixed_regs_devices); ++i) {
struct fixed_voltage_config *fixed_reg_pdata =
@@ -474,7 +540,38 @@ static int __init enterprise_fixed_regulator_init(void)
return platform_add_devices(fixed_regs_devices,
ARRAY_SIZE(fixed_regs_devices));
}
-subsys_initcall_sync(enterprise_fixed_regulator_init);
+
+static int __init enterprise_gpio_regulator_init(void)
+{
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(gpio_regs_devices); ++i) {
+ struct gpio_regulator_config *gpio_reg_pdata =
+ gpio_regs_devices[i]->dev.platform_data;
+ for (j = 0; j < gpio_reg_pdata->nr_gpios; ++j) {
+ if (gpio_reg_pdata->gpios[j].gpio < TEGRA_NR_GPIOS)
+ tegra_gpio_enable(gpio_reg_pdata->gpios[j].gpio);
+ }
+ }
+ return platform_add_devices(gpio_regs_devices,
+ ARRAY_SIZE(gpio_regs_devices));
+}
+
+static int __init enterprise_regulators_fixed_gpio_init(void)
+{
+ int ret;
+
+ if (!is_enterprise_machine)
+ return 0;
+
+ ret = enterprise_fixed_regulator_init();
+ if (ret)
+ return ret;
+
+ ret = enterprise_gpio_regulator_init();
+ return ret;
+}
+subsys_initcall_sync(enterprise_regulators_fixed_gpio_init);
static void enterprise_power_off(void)
{
diff --git a/arch/arm/mach-tegra/board-enterprise.c b/arch/arm/mach-tegra/board-enterprise.c
index f631c9b93cb2..8c194b5bdd51 100644
--- a/arch/arm/mach-tegra/board-enterprise.c
+++ b/arch/arm/mach-tegra/board-enterprise.c
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-tegra/board-enterprise.c
*
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2011-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -177,17 +177,15 @@ static __initdata struct tegra_clk_init_table enterprise_clk_init_table[] = {
{ "hda2codec_2x","pll_p", 48000000, false},
{ "pwm", "clk_32k", 32768, false},
{ "blink", "clk_32k", 32768, true},
- { "pll_a", NULL, 564480000, false},
- { "pll_a_out0", NULL, 11289600, false},
{ "i2s0", "pll_a_out0", 0, false},
{ "i2s1", "pll_a_out0", 0, false},
{ "i2s2", "pll_a_out0", 0, false},
{ "i2s3", "pll_a_out0", 0, false},
{ "spdif_out", "pll_a_out0", 0, false},
- { "d_audio", "pll_a_out0", 0, false},
- { "dam0", "pll_a_out0", 0, false},
- { "dam1", "pll_a_out0", 0, false},
- { "dam2", "pll_a_out0", 0, false},
+ { "d_audio", "clk_m", 12000000, false},
+ { "dam0", "clk_m", 12000000, false},
+ { "dam1", "clk_m", 12000000, false},
+ { "dam2", "clk_m", 12000000, false},
{ "audio0", "i2s0_sync", 0, false},
{ "audio1", "i2s1_sync", 0, false},
{ "audio2", "i2s2_sync", 0, false},
@@ -395,6 +393,8 @@ static void __init uart_debug_init(void)
unsigned long rate;
struct clk *c;
+ tegra_init_debug_uart_rate();
+
/* UARTD is the debug port. */
pr_info("Selecting UARTD as the debug console\n");
enterprise_uart_devices[3] = &debug_uartd_device;
@@ -956,14 +956,6 @@ static void __init tegra_enterprise_init(void)
enterprise_nfc_init();
}
-static void __init tegra_enterprise_ramconsole_reserve(unsigned long size)
-{
- struct resource *res;
- long ret;
-
- tegra_ram_console_debug_reserve(SZ_1M);
-}
-
static void __init tegra_enterprise_reserve(void)
{
#if defined(CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM)
@@ -971,7 +963,7 @@ static void __init tegra_enterprise_reserve(void)
#else
tegra_reserve(SZ_128M, SZ_4M, SZ_8M);
#endif
- tegra_enterprise_ramconsole_reserve(SZ_1M);
+ tegra_ram_console_debug_reserve(SZ_1M);
}
MACHINE_START(TEGRA_ENTERPRISE, "tegra_enterprise")
diff --git a/arch/arm/mach-tegra/board-harmony-power.c b/arch/arm/mach-tegra/board-harmony-power.c
index a841b37dbe2a..7d29a9c4bbfc 100644
--- a/arch/arm/mach-tegra/board-harmony-power.c
+++ b/arch/arm/mach-tegra/board-harmony-power.c
@@ -312,6 +312,7 @@ static struct tegra_suspend_platform_data harmony_suspend_data = {
int __init harmony_suspend_init(void)
{
tegra_init_suspend(&harmony_suspend_data);
+ return 0;
}
int __init harmony_regulator_init(void)
diff --git a/arch/arm/mach-tegra/board-kai-kbc.c b/arch/arm/mach-tegra/board-kai-kbc.c
new file mode 100644
index 000000000000..704789debf3d
--- /dev/null
+++ b/arch/arm/mach-tegra/board-kai-kbc.c
@@ -0,0 +1,115 @@
+/*
+ * arch/arm/mach-tegra/board-kai-kbc.c
+ * Keys configuration for Nvidia tegra3 kai platform.
+ *
+ * Copyright (C) 2012 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
+#include <linux/gpio_keys.h>
+#include <linux/interrupt_keys.h>
+#include <linux/gpio_scrollwheel.h>
+
+#include <mach/irqs.h>
+#include <mach/io.h>
+#include <mach/iomap.h>
+#include <mach/kbc.h>
+#include "board.h"
+#include "board-kai.h"
+
+#include "gpio-names.h"
+#include "devices.h"
+
+#define GPIO_KEY(_id, _gpio, _iswake) \
+ { \
+ .code = _id, \
+ .gpio = TEGRA_GPIO_##_gpio, \
+ .active_low = 1, \
+ .desc = #_id, \
+ .type = EV_KEY, \
+ .wakeup = _iswake, \
+ .debounce_interval = 10, \
+ }
+
+static struct gpio_keys_button kai_keys[] = {
+ [0] = GPIO_KEY(KEY_MENU, PR2, 0),
+ [1] = GPIO_KEY(KEY_BACK, PQ1, 0),
+ [2] = GPIO_KEY(KEY_HOME, PQ0, 0),
+ [3] = GPIO_KEY(KEY_SEARCH, PQ3, 0),
+ [4] = GPIO_KEY(KEY_VOLUMEUP, PR1, 0),
+ [5] = GPIO_KEY(KEY_VOLUMEDOWN, PR0, 0),
+};
+
+static struct gpio_keys_platform_data kai_keys_platform_data = {
+ .buttons = kai_keys,
+ .nbuttons = ARRAY_SIZE(kai_keys),
+};
+
+static struct platform_device kai_keys_device = {
+ .name = "gpio-keys",
+ .id = 0,
+ .dev = {
+ .platform_data = &kai_keys_platform_data,
+ },
+};
+
+#define INT_KEY(_id, _irq, _iswake, _deb_int) \
+ { \
+ .code = _id, \
+ .irq = _irq, \
+ .active_low = 1, \
+ .desc = #_id, \
+ .type = EV_KEY, \
+ .wakeup = _iswake, \
+ .debounce_interval = _deb_int, \
+ }
+static struct interrupt_keys_button kai_int_keys[] = {
+};
+
+static struct interrupt_keys_platform_data kai_int_keys_pdata = {
+ .int_buttons = kai_int_keys,
+ .nbuttons = ARRAY_SIZE(kai_int_keys),
+};
+
+static struct platform_device kai_int_keys_device = {
+ .name = "interrupt-keys",
+ .id = 0,
+ .dev = {
+ .platform_data = &kai_int_keys_pdata,
+ },
+};
+
+int __init kai_keys_init(void)
+{
+ int i;
+
+ pr_info("Registering gpio keys\n");
+
+ /* Enable gpio mode for other pins */
+ for (i = 0; i < kai_keys_platform_data.nbuttons; i++)
+ tegra_gpio_enable(kai_keys_platform_data.
+ buttons[i].gpio);
+
+ platform_device_register(&kai_keys_device);
+ platform_device_register(&kai_int_keys_device);
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-kai-memory.c b/arch/arm/mach-tegra/board-kai-memory.c
new file mode 100644
index 000000000000..0e300d681dd9
--- /dev/null
+++ b/arch/arm/mach-tegra/board-kai-memory.c
@@ -0,0 +1,757 @@
+/*
+ * Copyright (C) 2011 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include "board.h"
+#include "board-kai.h"
+#include "tegra3_emc.h"
+#include "fuse.h"
+
+
+static const struct tegra_emc_table kai_emc_tables_h5tc4g[] = {
+ {
+ 0x32, /* Rev 3.2 */
+ 25500, /* SDRAM frequency */
+ {
+ 0x00000001, /* EMC_RC */
+ 0x00000007, /* EMC_RFC */
+ 0x00000000, /* EMC_RAS */
+ 0x00000000, /* EMC_RP */
+ 0x00000002, /* EMC_R2W */
+ 0x0000000a, /* EMC_W2R */
+ 0x00000005, /* EMC_R2P */
+ 0x0000000b, /* EMC_W2P */
+ 0x00000000, /* EMC_RD_RCD */
+ 0x00000000, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000005, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x0000000b, /* EMC_RDV */
+ 0x000000c0, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000030, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x00000002, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000007, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x00000008, /* EMC_TXSR */
+ 0x00000008, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x00000002, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x000000c7, /* EMC_TREFBW */
+ 0x00000006, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004288, /* EMC_FBIO_CFG5 */
+ 0x007800a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS0 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS1 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS2 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS3 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS4 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS5 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS6 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQ0 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQ1 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQ2 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800211c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77fff884, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f108, /* EMC_XM2COMPPADCTRL */
+ 0x05057404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000168, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00000000, /* EMC_ZCAL_INTERVAL */
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT */
+ 0x000c000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000287, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00020001, /* MC_EMEM_ARB_CFG */
+ 0xc0000008, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06020102, /* MC_EMEM_ARB_DA_TURNS */
+ 0x000a0502, /* MC_EMEM_ARB_DA_COVERS */
+ 0x75e30303, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xe8000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x80001221, /* Mode Register 0 */
+ 0x80100003, /* Mode Register 1 */
+ 0x80200008, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 51000, /* SDRAM frequency */
+ {
+ 0x00000002, /* EMC_RC */
+ 0x0000000f, /* EMC_RFC */
+ 0x00000001, /* EMC_RAS */
+ 0x00000000, /* EMC_RP */
+ 0x00000002, /* EMC_R2W */
+ 0x0000000a, /* EMC_W2R */
+ 0x00000005, /* EMC_R2P */
+ 0x0000000b, /* EMC_W2P */
+ 0x00000000, /* EMC_RD_RCD */
+ 0x00000000, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000005, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x0000000b, /* EMC_RDV */
+ 0x00000181, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000060, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x00000002, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000007, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x00000010, /* EMC_TXSR */
+ 0x00000010, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x00000003, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x0000018e, /* EMC_TREFBW */
+ 0x00000006, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004288, /* EMC_FBIO_CFG5 */
+ 0x007800a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS0 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS1 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS2 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS3 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS4 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS5 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS6 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQ0 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQ1 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQ2 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800211c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77fff884, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f108, /* EMC_XM2COMPPADCTRL */
+ 0x05057404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000168, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00000000, /* EMC_ZCAL_INTERVAL */
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT */
+ 0x000c000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x8000040b, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00010001, /* MC_EMEM_ARB_CFG */
+ 0xc000000a, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06020102, /* MC_EMEM_ARB_DA_TURNS */
+ 0x000a0502, /* MC_EMEM_ARB_DA_COVERS */
+ 0x74e30303, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xe8000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x80001221, /* Mode Register 0 */
+ 0x80100003, /* Mode Register 1 */
+ 0x80200008, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 102000, /* SDRAM frequency */
+ {
+ 0x00000005, /* EMC_RC */
+ 0x0000001e, /* EMC_RFC */
+ 0x00000003, /* EMC_RAS */
+ 0x00000001, /* EMC_RP */
+ 0x00000002, /* EMC_R2W */
+ 0x0000000a, /* EMC_W2R */
+ 0x00000005, /* EMC_R2P */
+ 0x0000000b, /* EMC_W2P */
+ 0x00000001, /* EMC_RD_RCD */
+ 0x00000001, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000005, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x0000000b, /* EMC_RDV */
+ 0x00000303, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x000000c0, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x00000002, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000007, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x00000020, /* EMC_TXSR */
+ 0x00000020, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x00000005, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x0000031c, /* EMC_TREFBW */
+ 0x00000006, /* EMC_QUSE_EXTRA */
+ 0x00000004, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004288, /* EMC_FBIO_CFG5 */
+ 0x007800a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS0 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS1 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS2 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS3 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS4 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS5 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS6 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQ0 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQ1 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQ2 */
+ 0x000fc000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800211c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77fff884, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f108, /* EMC_XM2COMPPADCTRL */
+ 0x05057404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000168, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00000000, /* EMC_ZCAL_INTERVAL */
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT */
+ 0x000c000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000713, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000001, /* MC_EMEM_ARB_CFG */
+ 0xc0000013, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000000, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06020102, /* MC_EMEM_ARB_DA_TURNS */
+ 0x000a0503, /* MC_EMEM_ARB_DA_COVERS */
+ 0x74430504, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xe8000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x80001221, /* Mode Register 0 */
+ 0x80100003, /* Mode Register 1 */
+ 0x80200008, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 204000, /* SDRAM frequency */
+ {
+ 0x0000000a, /* EMC_RC */
+ 0x0000003d, /* EMC_RFC */
+ 0x00000007, /* EMC_RAS */
+ 0x00000002, /* EMC_RP */
+ 0x00000002, /* EMC_R2W */
+ 0x0000000a, /* EMC_W2R */
+ 0x00000005, /* EMC_R2P */
+ 0x0000000b, /* EMC_W2P */
+ 0x00000002, /* EMC_RD_RCD */
+ 0x00000002, /* EMC_WR_RCD */
+ 0x00000003, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000005, /* EMC_WDV */
+ 0x00000005, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x00000009, /* EMC_QSAFE */
+ 0x0000000b, /* EMC_RDV */
+ 0x00000607, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000181, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x00000002, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000007, /* EMC_AR2PDEN */
+ 0x0000000f, /* EMC_RW2PDEN */
+ 0x00000040, /* EMC_TXSR */
+ 0x00000040, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x0000000a, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x00000638, /* EMC_TREFBW */
+ 0x00000006, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00004288, /* EMC_FBIO_CFG5 */
+ 0x004400a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00080000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS4 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS5 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS6 */
+ 0x00080000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00080000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800211c, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77fff884, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f108, /* EMC_XM2COMPPADCTRL */
+ 0x05057404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x08000168, /* EMC_XM2QUSEPADCTRL */
+ 0x08000000, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00020000, /* EMC_ZCAL_INTERVAL */
+ 0x00000100, /* EMC_ZCAL_WAIT_CNT */
+ 0x000c000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x80000d22, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000003, /* MC_EMEM_ARB_CFG */
+ 0xc0000025, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000005, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06020102, /* MC_EMEM_ARB_DA_TURNS */
+ 0x000a0505, /* MC_EMEM_ARB_DA_COVERS */
+ 0x74040a06, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xe8000000, /* EMC_FBIO_SPARE */
+ 0xff00ff00, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x80001221, /* Mode Register 0 */
+ 0x80100003, /* Mode Register 1 */
+ 0x80200008, /* Mode Register 2 */
+ 0x00000001, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 333500, /* SDRAM frequency */
+ {
+ 0x0000000f, /* EMC_RC */
+ 0x00000063, /* EMC_RFC */
+ 0x0000000a, /* EMC_RAS */
+ 0x00000003, /* EMC_RP */
+ 0x00000003, /* EMC_R2W */
+ 0x00000008, /* EMC_W2R */
+ 0x00000002, /* EMC_R2P */
+ 0x00000009, /* EMC_W2P */
+ 0x00000003, /* EMC_RD_RCD */
+ 0x00000003, /* EMC_WR_RCD */
+ 0x00000002, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000004, /* EMC_WDV */
+ 0x00000006, /* EMC_QUSE */
+ 0x00000004, /* EMC_QRST */
+ 0x0000000a, /* EMC_QSAFE */
+ 0x0000000c, /* EMC_RDV */
+ 0x000009e9, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x0000027a, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000001, /* EMC_PDEX2WR */
+ 0x00000008, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x00000007, /* EMC_AR2PDEN */
+ 0x0000000e, /* EMC_RW2PDEN */
+ 0x00000068, /* EMC_TXSR */
+ 0x00000200, /* EMC_TXSRDLL */
+ 0x00000004, /* EMC_TCKE */
+ 0x0000000f, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000004, /* EMC_TCLKSTABLE */
+ 0x00000005, /* EMC_TCLKSTOP */
+ 0x00000a2a, /* EMC_TREFBW */
+ 0x00000000, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00007088, /* EMC_FBIO_CFG5 */
+ 0x002600a4, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x00014000, /* EMC_DLL_XFORM_DQS0 */
+ 0x00014000, /* EMC_DLL_XFORM_DQS1 */
+ 0x00014000, /* EMC_DLL_XFORM_DQS2 */
+ 0x00014000, /* EMC_DLL_XFORM_DQS3 */
+ 0x00014000, /* EMC_DLL_XFORM_DQS4 */
+ 0x00014000, /* EMC_DLL_XFORM_DQS5 */
+ 0x00014000, /* EMC_DLL_XFORM_DQS6 */
+ 0x00014000, /* EMC_DLL_XFORM_DQS7 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00000000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ0 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ1 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ2 */
+ 0x00020000, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0800013d, /* EMC_XM2DQSPADCTRL2 */
+ 0x00000000, /* EMC_XM2DQPADCTRL2 */
+ 0x77fff884, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f508, /* EMC_XM2COMPPADCTRL */
+ 0x05057404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000007, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x080001e8, /* EMC_XM2QUSEPADCTRL */
+ 0x08000021, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00020000, /* EMC_ZCAL_INTERVAL */
+ 0x00000100, /* EMC_ZCAL_WAIT_CNT */
+ 0x015c000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10000, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x800014d4, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x00000005, /* MC_EMEM_ARB_CFG */
+ 0x8000003d, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_RC */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x00000007, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x00000007, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000006, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x06030202, /* MC_EMEM_ARB_DA_TURNS */
+ 0x000b0608, /* MC_EMEM_ARB_DA_COVERS */
+ 0x70850f09, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xe8000000, /* EMC_FBIO_SPARE */
+ 0xff00ff89, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000000, /* EMC_CFG.PERIODIC_QRST */
+ 0x80000321, /* Mode Register 0 */
+ 0x80100002, /* Mode Register 1 */
+ 0x80200000, /* Mode Register 2 */
+ 0x00000000, /* EMC_CFG.DYN_SELF_REF */
+ },
+ {
+ 0x32, /* Rev 3.2 */
+ 667000, /* SDRAM frequency */
+ {
+ 0x00000020, /* EMC_RC */
+ 0x000000c7, /* EMC_RFC */
+ 0x00000017, /* EMC_RAS */
+ 0x00000007, /* EMC_RP */
+ 0x00000005, /* EMC_R2W */
+ 0x0000000c, /* EMC_W2R */
+ 0x00000003, /* EMC_R2P */
+ 0x00000011, /* EMC_W2P */
+ 0x00000007, /* EMC_RD_RCD */
+ 0x00000007, /* EMC_WR_RCD */
+ 0x00000002, /* EMC_RRD */
+ 0x00000001, /* EMC_REXT */
+ 0x00000000, /* EMC_WEXT */
+ 0x00000007, /* EMC_WDV */
+ 0x0000000a, /* EMC_QUSE */
+ 0x00000009, /* EMC_QRST */
+ 0x0000000d, /* EMC_QSAFE */
+ 0x00000012, /* EMC_RDV */
+ 0x00001412, /* EMC_REFRESH */
+ 0x00000000, /* EMC_BURST_REFRESH_NUM */
+ 0x00000504, /* EMC_PRE_REFRESH_REQ_CNT */
+ 0x00000002, /* EMC_PDEX2WR */
+ 0x0000000e, /* EMC_PDEX2RD */
+ 0x00000001, /* EMC_PCHG2PDEN */
+ 0x00000000, /* EMC_ACT2PDEN */
+ 0x0000000c, /* EMC_AR2PDEN */
+ 0x00000016, /* EMC_RW2PDEN */
+ 0x000000cf, /* EMC_TXSR */
+ 0x00000200, /* EMC_TXSRDLL */
+ 0x00000005, /* EMC_TCKE */
+ 0x0000001f, /* EMC_TFAW */
+ 0x00000000, /* EMC_TRPAB */
+ 0x00000006, /* EMC_TCLKSTABLE */
+ 0x00000007, /* EMC_TCLKSTOP */
+ 0x00001453, /* EMC_TREFBW */
+ 0x0000000b, /* EMC_QUSE_EXTRA */
+ 0x00000006, /* EMC_FBIO_CFG6 */
+ 0x00000000, /* EMC_ODT_WRITE */
+ 0x00000000, /* EMC_ODT_READ */
+ 0x00005088, /* EMC_FBIO_CFG5 */
+ 0xf00b0191, /* EMC_CFG_DIG_DLL */
+ 0x00008000, /* EMC_CFG_DIG_DLL_PERIOD */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS0 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS1 */
+ 0x00000008, /* EMC_DLL_XFORM_DQS2 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS3 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS4 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS5 */
+ 0x00000008, /* EMC_DLL_XFORM_DQS6 */
+ 0x0000000a, /* EMC_DLL_XFORM_DQS7 */
+ 0x00018000, /* EMC_DLL_XFORM_QUSE0 */
+ 0x00018000, /* EMC_DLL_XFORM_QUSE1 */
+ 0x00018000, /* EMC_DLL_XFORM_QUSE2 */
+ 0x00018000, /* EMC_DLL_XFORM_QUSE3 */
+ 0x00018000, /* EMC_DLL_XFORM_QUSE4 */
+ 0x00018000, /* EMC_DLL_XFORM_QUSE5 */
+ 0x00018000, /* EMC_DLL_XFORM_QUSE6 */
+ 0x00018000, /* EMC_DLL_XFORM_QUSE7 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS0 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS1 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS2 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS3 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS4 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS5 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS6 */
+ 0x00000000, /* EMC_DLI_TRIM_TXDQS7 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ0 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ1 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ2 */
+ 0x0000000c, /* EMC_DLL_XFORM_DQ3 */
+ 0x000002a0, /* EMC_XM2CMDPADCTRL */
+ 0x0600013d, /* EMC_XM2DQSPADCTRL2 */
+ 0x22220000, /* EMC_XM2DQPADCTRL2 */
+ 0x77fff884, /* EMC_XM2CLKPADCTRL */
+ 0x01f1f501, /* EMC_XM2COMPPADCTRL */
+ 0x07077404, /* EMC_XM2VTTGENPADCTRL */
+ 0x54000000, /* EMC_XM2VTTGENPADCTRL2 */
+ 0x080001e8, /* EMC_XM2QUSEPADCTRL */
+ 0x06000021, /* EMC_XM2DQSPADCTRL3 */
+ 0x00000802, /* EMC_CTT_TERM_CTRL */
+ 0x00020000, /* EMC_ZCAL_INTERVAL */
+ 0x00000100, /* EMC_ZCAL_WAIT_CNT */
+ 0x00f8000c, /* EMC_MRS_WAIT_CNT */
+ 0xa0f10202, /* EMC_AUTO_CAL_CONFIG */
+ 0x00000000, /* EMC_CTT */
+ 0x00000000, /* EMC_CTT_DURATION */
+ 0x800028a5, /* EMC_DYN_SELF_REF_CONTROL */
+ 0x0000000a, /* MC_EMEM_ARB_CFG */
+ 0x80000079, /* MC_EMEM_ARB_OUTSTANDING_REQ */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RCD */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_RP */
+ 0x00000010, /* MC_EMEM_ARB_TIMING_RC */
+ 0x0000000b, /* MC_EMEM_ARB_TIMING_RAS */
+ 0x0000000f, /* MC_EMEM_ARB_TIMING_FAW */
+ 0x00000001, /* MC_EMEM_ARB_TIMING_RRD */
+ 0x00000003, /* MC_EMEM_ARB_TIMING_RAP2PRE */
+ 0x0000000b, /* MC_EMEM_ARB_TIMING_WAP2PRE */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_R2R */
+ 0x00000002, /* MC_EMEM_ARB_TIMING_W2W */
+ 0x00000004, /* MC_EMEM_ARB_TIMING_R2W */
+ 0x00000008, /* MC_EMEM_ARB_TIMING_W2R */
+ 0x08040202, /* MC_EMEM_ARB_DA_TURNS */
+ 0x00130b10, /* MC_EMEM_ARB_DA_COVERS */
+ 0x70ea1f11, /* MC_EMEM_ARB_MISC0 */
+ 0x001f0000, /* MC_EMEM_ARB_RING1_THROTTLE */
+ 0xe8000000, /* EMC_FBIO_SPARE */
+ 0xff00ff49, /* EMC_CFG_RSV */
+ },
+ 0x00000040, /* EMC_ZCAL_WAIT_CNT after clock change */
+ 0x001fffff, /* EMC_AUTO_CAL_INTERVAL */
+ 0x00000001, /* EMC_CFG.PERIODIC_QRST */
+ 0x80000b71, /* Mode Register 0 */
+ 0x80100002, /* Mode Register 1 */
+ 0x80200018, /* Mode Register 2 */
+ 0x00000000, /* EMC_CFG.DYN_SELF_REF */
+ },
+};
+
+int kai_emc_init(void)
+{
+ tegra_init_emc(kai_emc_tables_h5tc4g,
+ ARRAY_SIZE(kai_emc_tables_h5tc4g));
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-kai-panel.c b/arch/arm/mach-tegra/board-kai-panel.c
new file mode 100644
index 000000000000..86a15557c3f2
--- /dev/null
+++ b/arch/arm/mach-tegra/board-kai-panel.c
@@ -0,0 +1,703 @@
+/*
+ * arch/arm/mach-tegra/board-kai-panel.c
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/resource.h>
+#include <asm/mach-types.h>
+#include <linux/platform_device.h>
+#include <linux/earlysuspend.h>
+#include <linux/pwm_backlight.h>
+#include <asm/atomic.h>
+#include <linux/nvhost.h>
+#include <mach/nvmap.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/dc.h>
+#include <mach/fb.h>
+
+#include "board.h"
+#include "board-kai.h"
+#include "devices.h"
+#include "gpio-names.h"
+
+/* kai default display board pins */
+#define kai_lvds_avdd_en TEGRA_GPIO_PH6
+#define kai_lvds_stdby TEGRA_GPIO_PG5
+#define kai_lvds_rst TEGRA_GPIO_PG7
+#define kai_lvds_shutdown TEGRA_GPIO_PN6
+#define kai_lvds_rs TEGRA_GPIO_PH1
+#define kai_lvds_lr TEGRA_GPIO_PG1
+
+/* common pins( backlight ) for all display boards */
+#define kai_bl_enb TEGRA_GPIO_PH3
+#define kai_bl_pwm TEGRA_GPIO_PH0
+#define kai_hdmi_hpd TEGRA_GPIO_PN7
+
+#ifdef CONFIG_TEGRA_DC
+static struct regulator *kai_hdmi_reg;
+static struct regulator *kai_hdmi_pll;
+static struct regulator *kai_hdmi_vddio;
+#endif
+
+static atomic_t sd_brightness = ATOMIC_INIT(255);
+
+static struct regulator *kai_lvds_reg;
+static struct regulator *kai_lvds_vdd_panel;
+
+static tegra_dc_bl_output kai_bl_output_measured = {
+ 0, 1, 2, 3, 4, 5, 6, 7,
+ 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23,
+ 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39,
+ 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 49, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 62,
+ 63, 64, 65, 66, 67, 68, 69, 70,
+ 70, 72, 73, 74, 75, 76, 77, 78,
+ 79, 80, 81, 82, 83, 84, 85, 86,
+ 87, 88, 89, 90, 91, 92, 93, 94,
+ 95, 96, 97, 98, 99, 100, 101, 102,
+ 103, 104, 105, 106, 107, 108, 110, 111,
+ 112, 113, 114, 115, 116, 117, 118, 119,
+ 120, 121, 122, 123, 124, 124, 125, 126,
+ 127, 128, 129, 130, 131, 132, 133, 133,
+ 134, 135, 136, 137, 138, 139, 140, 141,
+ 142, 143, 144, 145, 146, 147, 148, 148,
+ 149, 150, 151, 152, 153, 154, 155, 156,
+ 157, 158, 159, 160, 161, 162, 163, 164,
+ 165, 166, 167, 168, 169, 170, 171, 172,
+ 173, 174, 175, 176, 177, 179, 180, 181,
+ 182, 184, 185, 186, 187, 188, 189, 190,
+ 191, 192, 193, 194, 195, 196, 197, 198,
+ 199, 200, 201, 202, 203, 204, 205, 206,
+ 207, 208, 209, 211, 212, 213, 214, 215,
+ 216, 217, 218, 219, 220, 221, 222, 223,
+ 224, 225, 226, 227, 228, 229, 230, 231,
+ 232, 233, 234, 235, 236, 237, 238, 239,
+ 240, 241, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 254, 255
+};
+
+static p_tegra_dc_bl_output bl_output;
+
+static int kai_backlight_init(struct device *dev)
+{
+ int ret;
+
+ bl_output = kai_bl_output_measured;
+
+ if (WARN_ON(ARRAY_SIZE(kai_bl_output_measured) != 256))
+ pr_err("bl_output array does not have 256 elements\n");
+
+ tegra_gpio_disable(kai_bl_pwm);
+
+ ret = gpio_request(kai_bl_enb, "backlight_enb");
+ if (ret < 0)
+ return ret;
+
+ ret = gpio_direction_output(kai_bl_enb, 1);
+ if (ret < 0)
+ gpio_free(kai_bl_enb);
+ else
+ tegra_gpio_enable(kai_bl_enb);
+
+ return ret;
+};
+
+static void kai_backlight_exit(struct device *dev)
+{
+ /* int ret; */
+ /*ret = gpio_request(kai_bl_enb, "backlight_enb");*/
+ gpio_set_value(kai_bl_enb, 0);
+ gpio_free(kai_bl_enb);
+ tegra_gpio_disable(kai_bl_enb);
+ return;
+}
+
+static int kai_backlight_notify(struct device *unused, int brightness)
+{
+ int cur_sd_brightness = atomic_read(&sd_brightness);
+
+ /* Set the backlight GPIO pin mode to 'backlight_enable' */
+ gpio_set_value(kai_bl_enb, !!brightness);
+
+ /* SD brightness is a percentage, 8-bit value. */
+ brightness = (brightness * cur_sd_brightness) / 255;
+
+ /* Apply any backlight response curve */
+ if (brightness > 255)
+ pr_info("Error: Brightness > 255!\n");
+ else
+ brightness = bl_output[brightness];
+
+ return brightness;
+}
+
+static int kai_disp1_check_fb(struct device *dev, struct fb_info *info);
+
+static struct platform_pwm_backlight_data kai_backlight_data = {
+ .pwm_id = 0,
+ .max_brightness = 255,
+ .dft_brightness = 224,
+ .pwm_period_ns = 100000,
+ .init = kai_backlight_init,
+ .exit = kai_backlight_exit,
+ .notify = kai_backlight_notify,
+ /* Only toggle backlight on fb blank notifications for disp1 */
+ .check_fb = kai_disp1_check_fb,
+};
+
+static struct platform_device kai_backlight_device = {
+ .name = "pwm-backlight",
+ .id = -1,
+ .dev = {
+ .platform_data = &kai_backlight_data,
+ },
+};
+
+static int kai_panel_enable(void)
+{
+ if (kai_lvds_reg == NULL) {
+ kai_lvds_reg = regulator_get(NULL, "vdd_lvds");
+ if (WARN_ON(IS_ERR(kai_lvds_reg)))
+ pr_err("%s: couldn't get regulator vdd_lvds: %ld\n",
+ __func__, PTR_ERR(kai_lvds_reg));
+ else
+ regulator_enable(kai_lvds_reg);
+ }
+
+ if (kai_lvds_vdd_panel == NULL) {
+ kai_lvds_vdd_panel = regulator_get(NULL, "vdd_lcd_panel");
+ if (WARN_ON(IS_ERR(kai_lvds_vdd_panel)))
+ pr_err("%s: couldn't get regulator vdd_lcd_panel: %ld\n",
+ __func__, PTR_ERR(kai_lvds_vdd_panel));
+ else
+ regulator_enable(kai_lvds_vdd_panel);
+ }
+
+ mdelay(5);
+
+ gpio_set_value(kai_lvds_avdd_en, 1);
+ mdelay(5);
+
+ gpio_set_value(kai_lvds_stdby, 1);
+ gpio_set_value(kai_lvds_rst, 1);
+ gpio_set_value(kai_lvds_shutdown, 1);
+ gpio_set_value(kai_lvds_lr, 1);
+
+ mdelay(10);
+
+ return 0;
+}
+
+static int kai_panel_disable(void)
+{
+ gpio_set_value(kai_lvds_lr, 0);
+ gpio_set_value(kai_lvds_shutdown, 0);
+ gpio_set_value(kai_lvds_rst, 0);
+ gpio_set_value(kai_lvds_stdby, 0);
+ mdelay(5);
+
+ gpio_set_value(kai_lvds_avdd_en, 0);
+ mdelay(5);
+
+ regulator_disable(kai_lvds_reg);
+ regulator_put(kai_lvds_reg);
+ kai_lvds_reg = NULL;
+
+ regulator_disable(kai_lvds_vdd_panel);
+ regulator_put(kai_lvds_vdd_panel);
+ kai_lvds_vdd_panel = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_TEGRA_DC
+static int kai_hdmi_vddio_enable(void)
+{
+ int ret;
+ if (!kai_hdmi_vddio) {
+ kai_hdmi_vddio = regulator_get(NULL, "vdd_hdmi_con");
+ if (IS_ERR_OR_NULL(kai_hdmi_vddio)) {
+ ret = PTR_ERR(kai_hdmi_vddio);
+ pr_err("hdmi: couldn't get regulator vdd_hdmi_con\n");
+ kai_hdmi_vddio = NULL;
+ return ret;
+ }
+ }
+ ret = regulator_enable(kai_hdmi_vddio);
+ if (ret < 0) {
+ pr_err("hdmi: couldn't enable regulator vdd_hdmi_con\n");
+ regulator_put(kai_hdmi_vddio);
+ kai_hdmi_vddio = NULL;
+ return ret;
+ }
+ return ret;
+}
+
+static int kai_hdmi_vddio_disable(void)
+{
+ if (kai_hdmi_vddio) {
+ regulator_disable(kai_hdmi_vddio);
+ regulator_put(kai_hdmi_vddio);
+ kai_hdmi_vddio = NULL;
+ }
+ return 0;
+}
+
+static int kai_hdmi_enable(void)
+{
+ int ret;
+ if (!kai_hdmi_reg) {
+ kai_hdmi_reg = regulator_get(NULL, "avdd_hdmi");
+ if (IS_ERR_OR_NULL(kai_hdmi_reg)) {
+ pr_err("hdmi: couldn't get regulator avdd_hdmi\n");
+ kai_hdmi_reg = NULL;
+ return PTR_ERR(kai_hdmi_reg);
+ }
+ }
+ ret = regulator_enable(kai_hdmi_reg);
+ if (ret < 0) {
+ pr_err("hdmi: couldn't enable regulator avdd_hdmi\n");
+ return ret;
+ }
+ if (!kai_hdmi_pll) {
+ kai_hdmi_pll = regulator_get(NULL, "avdd_hdmi_pll");
+ if (IS_ERR_OR_NULL(kai_hdmi_pll)) {
+ pr_err("hdmi: couldn't get regulator avdd_hdmi_pll\n");
+ kai_hdmi_pll = NULL;
+ regulator_put(kai_hdmi_reg);
+ kai_hdmi_reg = NULL;
+ return PTR_ERR(kai_hdmi_pll);
+ }
+ }
+ ret = regulator_enable(kai_hdmi_pll);
+ if (ret < 0) {
+ pr_err("hdmi: couldn't enable regulator avdd_hdmi_pll\n");
+ return ret;
+ }
+ return 0;
+}
+
+static int kai_hdmi_disable(void)
+{
+ regulator_disable(kai_hdmi_reg);
+ regulator_put(kai_hdmi_reg);
+ kai_hdmi_reg = NULL;
+
+ regulator_disable(kai_hdmi_pll);
+ regulator_put(kai_hdmi_pll);
+ kai_hdmi_pll = NULL;
+ return 0;
+}
+
+static struct resource kai_disp1_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_GENERAL,
+ .end = INT_DISPLAY_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY_BASE,
+ .end = TEGRA_DISPLAY_BASE + TEGRA_DISPLAY_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .start = 0, /* Filled in by kai_panel_init() */
+ .end = 0, /* Filled in by kai_panel_init() */
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource kai_disp2_resources[] = {
+ {
+ .name = "irq",
+ .start = INT_DISPLAY_B_GENERAL,
+ .end = INT_DISPLAY_B_GENERAL,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "regs",
+ .start = TEGRA_DISPLAY2_BASE,
+ .end = TEGRA_DISPLAY2_BASE + TEGRA_DISPLAY2_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "fbmem",
+ .flags = IORESOURCE_MEM,
+ .start = 0,
+ .end = 0,
+ },
+ {
+ .name = "hdmi_regs",
+ .start = TEGRA_HDMI_BASE,
+ .end = TEGRA_HDMI_BASE + TEGRA_HDMI_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+#endif
+
+static struct tegra_dc_mode kai_panel_modes[] = {
+ {
+ /* 1024x600@60Hz */
+ .pclk = 51206000,
+ .h_ref_to_sync = 11,
+ .v_ref_to_sync = 1,
+ .h_sync_width = 10,
+ .v_sync_width = 5,
+ .h_back_porch = 10,
+ .v_back_porch = 15,
+ .h_active = 1024,
+ .v_active = 600,
+ .h_front_porch = 300,
+ .v_front_porch = 15,
+ },
+};
+
+static struct tegra_dc_sd_settings kai_sd_settings = {
+ .enable = 1, /* enabled by default. */
+ .use_auto_pwm = false,
+ .hw_update_delay = 0,
+ .bin_width = -1,
+ .aggressiveness = 1,
+ .phase_in_adjustments = true,
+ .use_vid_luma = false,
+ /* Default video coefficients */
+ .coeff = {5, 9, 2},
+ .fc = {0, 0},
+ /* Immediate backlight changes */
+ .blp = {1024, 255},
+ /* Gammas: R: 2.2 G: 2.2 B: 2.2 */
+ /* Default BL TF */
+ .bltf = {
+ {
+ {57, 65, 74, 83},
+ {93, 103, 114, 126},
+ {138, 151, 165, 179},
+ {194, 209, 225, 242},
+ },
+ {
+ {58, 66, 75, 84},
+ {94, 105, 116, 127},
+ {140, 153, 166, 181},
+ {196, 211, 227, 244},
+ },
+ {
+ {60, 68, 77, 87},
+ {97, 107, 119, 130},
+ {143, 156, 170, 184},
+ {199, 215, 231, 248},
+ },
+ {
+ {64, 73, 82, 91},
+ {102, 113, 124, 137},
+ {149, 163, 177, 192},
+ {207, 223, 240, 255},
+ },
+ },
+ /* Default LUT */
+ .lut = {
+ {
+ {250, 250, 250},
+ {194, 194, 194},
+ {149, 149, 149},
+ {113, 113, 113},
+ {82, 82, 82},
+ {56, 56, 56},
+ {34, 34, 34},
+ {15, 15, 15},
+ {0, 0, 0},
+ },
+ {
+ {246, 246, 246},
+ {191, 191, 191},
+ {147, 147, 147},
+ {111, 111, 111},
+ {80, 80, 80},
+ {55, 55, 55},
+ {33, 33, 33},
+ {14, 14, 14},
+ {0, 0, 0},
+ },
+ {
+ {239, 239, 239},
+ {185, 185, 185},
+ {142, 142, 142},
+ {107, 107, 107},
+ {77, 77, 77},
+ {52, 52, 52},
+ {30, 30, 30},
+ {12, 12, 12},
+ {0, 0, 0},
+ },
+ {
+ {224, 224, 224},
+ {173, 173, 173},
+ {133, 133, 133},
+ {99, 99, 99},
+ {70, 70, 70},
+ {46, 46, 46},
+ {25, 25, 25},
+ {7, 7, 7},
+ {0, 0, 0},
+ },
+ },
+ .sd_brightness = &sd_brightness,
+ .bl_device = &kai_backlight_device,
+};
+
+#ifdef CONFIG_TEGRA_DC
+static struct tegra_fb_data kai_fb_data = {
+ .win = 0,
+ .xres = 1024,
+ .yres = 600,
+ .bits_per_pixel = 32,
+ .flags = TEGRA_FB_FLIP_ON_PROBE,
+};
+
+static struct tegra_fb_data kai_hdmi_fb_data = {
+ .win = 0,
+ .xres = 1024,
+ .yres = 600,
+ .bits_per_pixel = 32,
+ .flags = TEGRA_FB_FLIP_ON_PROBE,
+};
+
+static struct tegra_dc_out kai_disp2_out = {
+ .type = TEGRA_DC_OUT_HDMI,
+ .flags = TEGRA_DC_OUT_HOTPLUG_HIGH,
+
+ .dcc_bus = 3,
+ .hotplug_gpio = kai_hdmi_hpd,
+
+ .max_pixclock = KHZ2PICOS(148500),
+
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+
+ .enable = kai_hdmi_enable,
+ .disable = kai_hdmi_disable,
+
+ .postsuspend = kai_hdmi_vddio_disable,
+ .hotplug_init = kai_hdmi_vddio_enable,
+};
+
+static struct tegra_dc_platform_data kai_disp2_pdata = {
+ .flags = 0,
+ .default_out = &kai_disp2_out,
+ .fb = &kai_hdmi_fb_data,
+ .emc_clk_rate = 300000000,
+};
+#endif
+
+static struct tegra_dc_out kai_disp1_out = {
+ .align = TEGRA_DC_ALIGN_MSB,
+ .order = TEGRA_DC_ORDER_RED_BLUE,
+ .sd_settings = &kai_sd_settings,
+ .parent_clk = "pll_p",
+
+ .type = TEGRA_DC_OUT_RGB,
+ .depth = 18,
+ .dither = TEGRA_DC_ORDERED_DITHER,
+
+ .modes = kai_panel_modes,
+ .n_modes = ARRAY_SIZE(kai_panel_modes),
+
+ .enable = kai_panel_enable,
+ .disable = kai_panel_disable,
+};
+
+#ifdef CONFIG_TEGRA_DC
+static struct tegra_dc_platform_data kai_disp1_pdata = {
+ .flags = TEGRA_DC_FLAG_ENABLED,
+ .default_out = &kai_disp1_out,
+ .emc_clk_rate = 300000000,
+ .fb = &kai_fb_data,
+};
+
+static struct nvhost_device kai_disp1_device = {
+ .name = "tegradc",
+ .id = 0,
+ .resource = kai_disp1_resources,
+ .num_resources = ARRAY_SIZE(kai_disp1_resources),
+ .dev = {
+ .platform_data = &kai_disp1_pdata,
+ },
+};
+
+static int kai_disp1_check_fb(struct device *dev, struct fb_info *info)
+{
+ return info->device == &kai_disp1_device.dev;
+}
+
+static struct nvhost_device kai_disp2_device = {
+ .name = "tegradc",
+ .id = 1,
+ .resource = kai_disp2_resources,
+ .num_resources = ARRAY_SIZE(kai_disp2_resources),
+ .dev = {
+ .platform_data = &kai_disp2_pdata,
+ },
+};
+#else
+static int kai_disp1_check_fb(struct device *dev, struct fb_info *info)
+{
+ return 0;
+}
+#endif
+
+static struct nvmap_platform_carveout kai_carveouts[] = {
+ [0] = NVMAP_HEAP_CARVEOUT_IRAM_INIT,
+ [1] = {
+ .name = "generic-0",
+ .usage_mask = NVMAP_HEAP_CARVEOUT_GENERIC,
+ .base = 0, /* Filled in by kai_panel_init() */
+ .size = 0, /* Filled in by kai_panel_init() */
+ .buddy_size = SZ_32K,
+ },
+};
+
+static struct nvmap_platform_data kai_nvmap_data = {
+ .carveouts = kai_carveouts,
+ .nr_carveouts = ARRAY_SIZE(kai_carveouts),
+};
+
+static struct platform_device kai_nvmap_device = {
+ .name = "tegra-nvmap",
+ .id = -1,
+ .dev = {
+ .platform_data = &kai_nvmap_data,
+ },
+};
+
+
+static struct platform_device *kai_gfx_devices[] __initdata = {
+ &kai_nvmap_device,
+#ifdef CONFIG_TEGRA_GRHOST
+ &tegra_grhost_device,
+#endif
+ &tegra_pwfm0_device,
+ &kai_backlight_device,
+};
+
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+/* put early_suspend/late_resume handlers here for the display in order
+ * to keep the code out of the display driver, keeping it closer to upstream
+ */
+struct early_suspend kai_panel_early_suspender;
+
+static void kai_panel_early_suspend(struct early_suspend *h)
+{
+ /* power down LCD, add use a black screen for HDMI */
+ if (num_registered_fb > 0)
+ fb_blank(registered_fb[0], FB_BLANK_POWERDOWN);
+ if (num_registered_fb > 1)
+ fb_blank(registered_fb[1], FB_BLANK_NORMAL);
+}
+
+static void kai_panel_late_resume(struct early_suspend *h)
+{
+ unsigned i;
+ for (i = 0; i < num_registered_fb; i++)
+ fb_blank(registered_fb[i], FB_BLANK_UNBLANK);
+}
+#endif
+
+int __init kai_panel_init(void)
+{
+ int err;
+ struct resource __maybe_unused *res;
+
+ kai_carveouts[1].base = tegra_carveout_start;
+ kai_carveouts[1].size = tegra_carveout_size;
+
+ gpio_request(kai_lvds_avdd_en, "lvds_avdd_en");
+ gpio_direction_output(kai_lvds_avdd_en, 1);
+ tegra_gpio_enable(kai_lvds_avdd_en);
+
+ gpio_request(kai_lvds_stdby, "lvds_stdby");
+ gpio_direction_output(kai_lvds_stdby, 1);
+ tegra_gpio_enable(kai_lvds_stdby);
+
+ gpio_request(kai_lvds_rst, "lvds_rst");
+ gpio_direction_output(kai_lvds_rst, 1);
+ tegra_gpio_enable(kai_lvds_rst);
+
+ gpio_request(kai_lvds_rs, "lvds_rs");
+ gpio_direction_output(kai_lvds_rs, 0);
+ tegra_gpio_enable(kai_lvds_rs);
+
+ gpio_request(kai_lvds_lr, "lvds_lr");
+ gpio_direction_output(kai_lvds_lr, 1);
+ tegra_gpio_enable(kai_lvds_lr);
+
+ gpio_request(kai_lvds_shutdown, "lvds_shutdown");
+ gpio_direction_output(kai_lvds_shutdown, 1);
+ tegra_gpio_enable(kai_lvds_shutdown);
+
+ tegra_gpio_enable(kai_hdmi_hpd);
+ gpio_request(kai_hdmi_hpd, "hdmi_hpd");
+ gpio_direction_input(kai_hdmi_hpd);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ kai_panel_early_suspender.suspend = kai_panel_early_suspend;
+ kai_panel_early_suspender.resume = kai_panel_late_resume;
+ kai_panel_early_suspender.level = EARLY_SUSPEND_LEVEL_DISABLE_FB;
+ register_early_suspend(&kai_panel_early_suspender);
+#endif
+
+ err = platform_add_devices(kai_gfx_devices,
+ ARRAY_SIZE(kai_gfx_devices));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+ res = nvhost_get_resource_byname(&kai_disp1_device,
+ IORESOURCE_MEM, "fbmem");
+ res->start = tegra_fb_start;
+ res->end = tegra_fb_start + tegra_fb_size - 1;
+#endif
+
+ /* Copy the bootloader fb to the fb. */
+ tegra_move_framebuffer(tegra_fb_start, tegra_bootloader_fb_start,
+ min(tegra_fb_size, tegra_bootloader_fb_size));
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_DC)
+ if (!err)
+ err = nvhost_device_register(&kai_disp1_device);
+
+ res = nvhost_get_resource_byname(&kai_disp2_device,
+ IORESOURCE_MEM, "fbmem");
+ res->start = tegra_fb2_start;
+ res->end = tegra_fb2_start + tegra_fb2_size - 1;
+ if (!err)
+ err = nvhost_device_register(&kai_disp2_device);
+#endif
+
+#if defined(CONFIG_TEGRA_GRHOST) && defined(CONFIG_TEGRA_NVAVP)
+ if (!err)
+ err = nvhost_device_register(&nvavp_device);
+#endif
+ return err;
+}
diff --git a/arch/arm/mach-tegra/board-kai-pinmux.c b/arch/arm/mach-tegra/board-kai-pinmux.c
new file mode 100644
index 000000000000..2288003d26de
--- /dev/null
+++ b/arch/arm/mach-tegra/board-kai-pinmux.c
@@ -0,0 +1,550 @@
+/*
+ * arch/arm/mach-tegra/board-kai-pinmux.c
+ *
+ * Copyright (C) 2012 NVIDIA Corporation
+ *
+ * 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 <mach/pinmux.h>
+#include "board.h"
+#include "board-kai.h"
+#include "gpio-names.h"
+
+#define DEFAULT_DRIVE(_name) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_DISABLE, \
+ .schmitt = TEGRA_SCHMITT_ENABLE, \
+ .drive = TEGRA_DRIVE_DIV_1, \
+ .pull_down = TEGRA_PULL_31, \
+ .pull_up = TEGRA_PULL_31, \
+ .slew_rising = TEGRA_SLEW_SLOWEST, \
+ .slew_falling = TEGRA_SLEW_SLOWEST, \
+ }
+/* Setting the drive strength of pins
+ * hsm: Enable High speed mode (ENABLE/DISABLE)
+ * Schimit: Enable/disable schimit (ENABLE/DISABLE)
+ * drive: low power mode (DIV_1, DIV_2, DIV_4, DIV_8)
+ * pulldn_drive - drive down (falling edge) - Driver Output Pull-Down drive
+ * strength code. Value from 0 to 31.
+ * pullup_drive - drive up (rising edge) - Driver Output Pull-Up drive
+ * strength code. Value from 0 to 31.
+ * pulldn_slew - Driver Output Pull-Up slew control code - 2bit code
+ * code 11 is least slewing of signal. code 00 is highest
+ * slewing of the signal.
+ * Value - FASTEST, FAST, SLOW, SLOWEST
+ * pullup_slew - Driver Output Pull-Down slew control code -
+ * code 11 is least slewing of signal. code 00 is highest
+ * slewing of the signal.
+ * Value - FASTEST, FAST, SLOW, SLOWEST
+ */
+#define SET_DRIVE(_name, _hsm, _schmitt, _drive, _pulldn_drive, _pullup_drive, _pulldn_slew, _pullup_slew) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_##_hsm, \
+ .schmitt = TEGRA_SCHMITT_##_schmitt, \
+ .drive = TEGRA_DRIVE_##_drive, \
+ .pull_down = TEGRA_PULL_##_pulldn_drive, \
+ .pull_up = TEGRA_PULL_##_pullup_drive, \
+ .slew_rising = TEGRA_SLEW_##_pulldn_slew, \
+ .slew_falling = TEGRA_SLEW_##_pullup_slew, \
+ }
+
+/* !!!FIXME!!!! POPULATE THIS TABLE */
+static __initdata struct tegra_drive_pingroup_config kai_drive_pinmux[] = {
+ /* DEFAULT_DRIVE(<pin_group>), */
+ /* SET_DRIVE(ATA, DISABLE, DISABLE, DIV_1, 31, 31, FAST, FAST) */
+ SET_DRIVE(DAP2, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* All I2C pins are driven to maximum drive strength */
+ /* GEN1 I2C */
+ SET_DRIVE(DBG, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* GEN2 I2C */
+ SET_DRIVE(AT5, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* CAM I2C */
+ SET_DRIVE(GME, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* DDC I2C */
+ SET_DRIVE(DDC, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* PWR_I2C */
+ SET_DRIVE(AO1, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* UART3 */
+ SET_DRIVE(UART3, DISABLE, ENABLE, DIV_1, 31, 31, FASTEST, FASTEST),
+
+ /* SDMMC1 */
+ SET_DRIVE(SDIO1, DISABLE, DISABLE, DIV_1, 46, 42, FAST, FAST),
+
+ /* SDMMC3 */
+ SET_DRIVE(SDIO3, DISABLE, DISABLE, DIV_1, 46, 42, FAST, FAST),
+
+ /* SDMMC4 */
+ SET_DRIVE(GMA, DISABLE, DISABLE, DIV_1, 9, 9, SLOWEST, SLOWEST),
+ SET_DRIVE(GMB, DISABLE, DISABLE, DIV_1, 9, 9, SLOWEST, SLOWEST),
+ SET_DRIVE(GMC, DISABLE, DISABLE, DIV_1, 9, 9, SLOWEST, SLOWEST),
+ SET_DRIVE(GMD, DISABLE, DISABLE, DIV_1, 9, 9, SLOWEST, SLOWEST),
+
+};
+
+#define DEFAULT_PINMUX(_pingroup, _mux, _pupd, _tri, _io) \
+ { \
+ .pingroup = TEGRA_PINGROUP_##_pingroup, \
+ .func = TEGRA_MUX_##_mux, \
+ .pupd = TEGRA_PUPD_##_pupd, \
+ .tristate = TEGRA_TRI_##_tri, \
+ .io = TEGRA_PIN_##_io, \
+ .lock = TEGRA_PIN_LOCK_DEFAULT, \
+ .od = TEGRA_PIN_OD_DEFAULT, \
+ .ioreset = TEGRA_PIN_IO_RESET_DEFAULT, \
+ }
+
+#define I2C_PINMUX(_pingroup, _mux, _pupd, _tri, _io, _lock, _od) \
+ { \
+ .pingroup = TEGRA_PINGROUP_##_pingroup, \
+ .func = TEGRA_MUX_##_mux, \
+ .pupd = TEGRA_PUPD_##_pupd, \
+ .tristate = TEGRA_TRI_##_tri, \
+ .io = TEGRA_PIN_##_io, \
+ .lock = TEGRA_PIN_LOCK_##_lock, \
+ .od = TEGRA_PIN_OD_##_od, \
+ .ioreset = TEGRA_PIN_IO_RESET_DEFAULT, \
+ }
+
+#define VI_PINMUX(_pingroup, _mux, _pupd, _tri, _io, _lock, _ioreset) \
+ { \
+ .pingroup = TEGRA_PINGROUP_##_pingroup, \
+ .func = TEGRA_MUX_##_mux, \
+ .pupd = TEGRA_PUPD_##_pupd, \
+ .tristate = TEGRA_TRI_##_tri, \
+ .io = TEGRA_PIN_##_io, \
+ .lock = TEGRA_PIN_LOCK_##_lock, \
+ .od = TEGRA_PIN_OD_DEFAULT, \
+ .ioreset = TEGRA_PIN_IO_RESET_##_ioreset \
+ }
+
+static __initdata struct tegra_pingroup_config kai_pinmux_common[] = {
+ /* SDMMC1 pinmux */
+ DEFAULT_PINMUX(SDMMC1_CLK, SDMMC1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_CMD, SDMMC1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT3, SDMMC1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT2, SDMMC1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT1, SDMMC1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC1_DAT0, SDMMC1, PULL_UP, NORMAL, INPUT),
+
+ /* SDMMC3 pinmux */
+ DEFAULT_PINMUX(SDMMC3_CLK, SDMMC3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_CMD, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT0, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT1, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT2, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT3, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT6, SDMMC3, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT7, SDMMC3, PULL_UP, NORMAL, INPUT),
+
+ /* SDMMC4 pinmux */
+ DEFAULT_PINMUX(SDMMC4_CLK, SDMMC4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_CMD, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT0, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT1, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT2, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT3, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT4, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT5, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT6, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_DAT7, SDMMC4, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(SDMMC4_RST_N, RSVD1, PULL_DOWN, NORMAL, INPUT),
+
+ /* I2C1 pinmux */
+ I2C_PINMUX(GEN1_I2C_SCL, I2C1, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(GEN1_I2C_SDA, I2C1, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ /* I2C2 pinmux */
+ I2C_PINMUX(GEN2_I2C_SCL, I2C2, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(GEN2_I2C_SDA, I2C2, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ /* I2C3 pinmux */
+ I2C_PINMUX(CAM_I2C_SCL, I2C3, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(CAM_I2C_SDA, I2C3, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ /* I2C4 pinmux */
+ I2C_PINMUX(DDC_SCL, I2C4, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(DDC_SDA, I2C4, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ /* Power I2C pinmux */
+ I2C_PINMUX(PWR_I2C_SCL, I2CPWR, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+ I2C_PINMUX(PWR_I2C_SDA, I2CPWR, NORMAL, NORMAL, INPUT, DISABLE, ENABLE),
+
+ /* LCD */
+ DEFAULT_PINMUX(LCD_PCLK, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_DE, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_HSYNC, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_VSYNC, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D0, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D1, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D2, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D3, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D4, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D5, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D6, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D7, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D8, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D9, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D10, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D11, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D12, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D13, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D14, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D15, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D16, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D17, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D18, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D19, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D20, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D21, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D22, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_D23, DISPLAYA, NORMAL, NORMAL, INPUT),
+
+ /* UART B : GPS */
+ DEFAULT_PINMUX(UART2_RXD, IRDA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART2_TXD, IRDA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART2_RTS_N, UARTB, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART2_CTS_N, UARTB, NORMAL, NORMAL, INPUT),
+
+ /*UART C : BT */
+ DEFAULT_PINMUX(UART3_TXD, UARTC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(UART3_RXD, UARTC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART3_CTS_N, UARTC, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(UART3_RTS_N, UARTC, NORMAL, NORMAL, OUTPUT),
+
+ /* UART D : DEBUG */
+ DEFAULT_PINMUX(GMI_A16, UARTD, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_A17, UARTD, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A18, UARTD, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_A19, UARTD, NORMAL, NORMAL, OUTPUT),
+
+ /* KBC keys */
+ DEFAULT_PINMUX(KB_COL0, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL1, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL2, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_COL3, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW0, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW1, KBC, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW2, KBC, PULL_UP, NORMAL, INPUT),
+
+ /* I2S0 : for MODEM */
+ DEFAULT_PINMUX(DAP1_FS, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_DIN, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_DOUT, I2S0, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP1_SCLK, I2S0, NORMAL, NORMAL, INPUT),
+
+ /* I2S1 : for CODEC */
+ DEFAULT_PINMUX(DAP2_FS, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_DIN, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_DOUT, I2S1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP2_SCLK, I2S1, NORMAL, NORMAL, INPUT),
+
+ /* I2S3 : for BT */
+ DEFAULT_PINMUX(DAP4_FS, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_DIN, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_DOUT, I2S3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(DAP4_SCLK, I2S3, NORMAL, NORMAL, INPUT),
+
+ /* SPI1 : touch */
+ DEFAULT_PINMUX(SPI1_MOSI, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_SCK, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_CS0_N, SPI1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI1_MISO, SPI1, NORMAL, NORMAL, INPUT),
+
+ /* SPIDIF */
+ DEFAULT_PINMUX(SPDIF_IN, SPDIF, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPDIF_OUT, SPDIF, NORMAL, NORMAL, OUTPUT),
+
+ /* FIXED FUNCTION AND CONFIGURATION */
+ DEFAULT_PINMUX(CLK_32K_OUT, BLINK, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(SYS_CLK_REQ, SYSCLK, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(OWR, OWR, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_AD4, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK1_OUT, EXTPERIPH1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK2_OUT, EXTPERIPH2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CLK3_OUT, EXTPERIPH3, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(CLK2_REQ, DAP, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(HDMI_INT, RSVD0, NORMAL, TRISTATE, INPUT),
+
+ /* GPIO */
+ /* POWER RAIL GPIO */
+ DEFAULT_PINMUX(DAP3_FS, I2S2, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD14, RSVD1, PULL_DOWN, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(SDMMC3_DAT5, SDMMC3, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW6, KBC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW7, KBC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(LCD_M1, DISPLAYA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(LCD_PWR0, DISPLAYA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(LCD_PWR1, DISPLAYA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(LCD_PWR2, DISPLAYA, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW8, KBC, NORMAL, NORMAL, OUTPUT),
+
+ /* CAMERA */
+ DEFAULT_PINMUX(CAM_MCLK, VI_ALT2, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PCC1, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB0, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB3, VGP3, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB5, VGP5, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB6, VGP6, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PBB7, I2S4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PCC2, I2S4, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW4, KBC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW5, KBC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW9, KBC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW10, KBC, NORMAL, NORMAL, OUTPUT),
+
+ /* MODEM */
+ DEFAULT_PINMUX(GPIO_PV0, RSVD, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PV1, RSVD, NORMAL, NORMAL, INPUT),
+
+ /* GPS and BT */
+ DEFAULT_PINMUX(GPIO_PU0, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU1, RSVD1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU2, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU3, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU4, PWM1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PU5, PWM2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GPIO_PU6, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(KB_ROW14, KBC, NORMAL, TRISTATE, OUTPUT),
+
+ /* LCD GPIO */
+ DEFAULT_PINMUX(GMI_AD0, RSVD1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD1, RSVD1, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD2, RSVD1, PULL_DOWN, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD3, RSVD1, PULL_DOWN, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD5, RSVD1, PULL_DOWN, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD6, RSVD1, PULL_DOWN, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD7, RSVD1, PULL_DOWN, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD8, PWM0, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD9, RSVD2, PULL_DOWN, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD11, PWM3, NORMAL, NORMAL, OUTPUT),
+
+ /* TOUCH */
+ DEFAULT_PINMUX(GMI_WAIT, RSVD1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_WP_N, RSVD1, PULL_UP, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_SDOUT, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_DC1, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_WR_N, DISPLAYA, PULL_UP, NORMAL, INPUT),
+
+ /* SDMMC */
+ DEFAULT_PINMUX(GMI_IORDY, RSVD1, PULL_UP, NORMAL, INPUT),
+
+ /* CODEC */
+ DEFAULT_PINMUX(SPI2_SCK, SPI2, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(SPI2_CS1_N, SPI2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_CS2_N, RSVD1, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(GMI_CS3_N, RSVD1, NORMAL, NORMAL, INPUT),
+
+ /* OTHERS */
+ DEFAULT_PINMUX(KB_ROW3, KBC, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_DQS, RSVD1, NORMAL, NORMAL, INPUT),
+
+ DEFAULT_PINMUX(GMI_AD15, RSVD1, PULL_UP, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(GMI_CLK, RSVD1, PULL_UP, NORMAL, INPUT),
+
+ DEFAULT_PINMUX(GMI_RST_N, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(LCD_DC0, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_CS0_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_CS1_N, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_SCK, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(LCD_SDIN, DISPLAYA, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(CRT_HSYNC, CRT, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(CRT_VSYNC, CRT, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(PEX_WAKE_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L2_PRSNT_N, PCIE, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(PEX_L2_RST_N, PCIE, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(PEX_L2_CLKREQ_N, PCIE, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(HDMI_CEC, CEC, NORMAL, NORMAL, INPUT),
+
+ DEFAULT_PINMUX(KB_ROW15, KBC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(SPI2_CS2_N, SPI2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI2_MISO, SPI2, NORMAL, NORMAL, INPUT),
+ DEFAULT_PINMUX(SPI2_MOSI, SPI2, NORMAL, NORMAL, INPUT),
+
+ DEFAULT_PINMUX(KB_ROW11, KBC, NORMAL, NORMAL, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW12, KBC, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(KB_ROW13, KBC, NORMAL, TRISTATE, OUTPUT),
+};
+
+/*Do not use for now*/
+static __initdata struct tegra_pingroup_config unused_pins_lowpower[] = {
+ DEFAULT_PINMUX(ULPI_CLK, ULPI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DATA0, ULPI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DATA1, ULPI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DATA2, ULPI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DATA3, ULPI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DATA4, ULPI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DATA5, ULPI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DATA6, ULPI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DATA7, ULPI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(ULPI_DIR, ULPI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(ULPI_NXT, ULPI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(ULPI_STP, ULPI, NORMAL, TRISTATE, OUTPUT),
+
+ DEFAULT_PINMUX(GMI_AD10, PWM2, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_AD12, RSVD1, NORMAL, TRISTATE, INPUT),
+ DEFAULT_PINMUX(GMI_AD13, RSVD1, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(CLK1_REQ, DAP, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_ADV_N, RSVD1, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS0_N, RSVD1, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS1_N, RSVD1, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS4_N, RSVD1, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS6_N, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_CS7_N, NAND, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_OE_N, RSVD1, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GMI_WR_N, RSVD1, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(PEX_L0_CLKREQ_N, PCIE, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(PEX_L0_PRSNT_N, PCIE, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(PEX_L0_RST_N, PCIE, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(PEX_L1_CLKREQ_N, PCIE, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(PEX_L1_PRSNT_N, PCIE, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(PEX_L1_RST_N, PCIE, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PV2, OWR, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(GPIO_PV3, RSVD1, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(HDMI_CEC, CEC, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(KB_COL4, KBC, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(KB_COL5, KBC, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(KB_COL6, KBC, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(KB_COL7, KBC, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(CLK3_REQ, DEV3, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_D0, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_D1, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_D10, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_D11, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_D2, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_D3, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_D4, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_D5, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_D6, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_D7, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_D8, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_D9, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_HSYNC, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_MCLK, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_PCLK, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(VI_VSYNC, VI, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(DAP3_DIN, I2S2, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(DAP3_DOUT, I2S2, NORMAL, TRISTATE, OUTPUT),
+ DEFAULT_PINMUX(DAP3_SCLK, I2S2, NORMAL, TRISTATE, OUTPUT),
+
+};
+
+/* We are disabling this code for now. */
+#define GPIO_INIT_PIN_MODE(_gpio, _is_input, _value) \
+ { \
+ .gpio_nr = _gpio, \
+ .is_input = _is_input, \
+ .value = _value, \
+ }
+
+static struct gpio_init_pin_info init_gpio_mode_kai_common[] = {
+ GPIO_INIT_PIN_MODE(TEGRA_GPIO_PDD7, false, 0),
+ GPIO_INIT_PIN_MODE(TEGRA_GPIO_PCC6, false, 0),
+};
+
+static void __init kai_gpio_init_configure(void)
+{
+ int len;
+ int i;
+ struct gpio_init_pin_info *pins_info;
+
+ len = ARRAY_SIZE(init_gpio_mode_kai_common);
+ pins_info = init_gpio_mode_kai_common;
+
+ for (i = 0; i < len; ++i) {
+ tegra_gpio_init_configure(pins_info->gpio_nr,
+ pins_info->is_input, pins_info->value);
+ pins_info++;
+ }
+}
+
+int __init kai_pinmux_init(void)
+{
+ kai_gpio_init_configure();
+
+ tegra_pinmux_config_table(kai_pinmux_common, ARRAY_SIZE(kai_pinmux_common));
+ tegra_drive_pinmux_config_table(kai_drive_pinmux,
+ ARRAY_SIZE(kai_drive_pinmux));
+
+ tegra_pinmux_config_table(unused_pins_lowpower,
+ ARRAY_SIZE(unused_pins_lowpower));
+
+ return 0;
+}
+
+#define PIN_GPIO_LPM(_name, _gpio, _is_input, _value) \
+ { \
+ .name = _name, \
+ .gpio_nr = _gpio, \
+ .is_gpio = true, \
+ .is_input = _is_input, \
+ .value = _value, \
+ }
+
+struct gpio_init_pin_info pin_lpm_kai_common[] = {
+ PIN_GPIO_LPM("GMI_CS4_N", TEGRA_GPIO_PK2, 1, 0),
+ PIN_GPIO_LPM("GMI_CS7", TEGRA_GPIO_PI6, 1, 0),
+ PIN_GPIO_LPM("GMI_CS0", TEGRA_GPIO_PJ0, 1, 0),
+ PIN_GPIO_LPM("GMI_CS1", TEGRA_GPIO_PJ2, 1, 0),
+};
+
+static void set_unused_pin_gpio(struct gpio_init_pin_info *lpm_pin_info,
+ int list_count)
+{
+ int i;
+ struct gpio_init_pin_info *pin_info;
+ int ret;
+
+ for (i = 0; i < list_count; ++i) {
+ pin_info = (struct gpio_init_pin_info *)(lpm_pin_info + i);
+ if (!pin_info->is_gpio)
+ continue;
+
+ ret = gpio_request(pin_info->gpio_nr, pin_info->name);
+ if (ret < 0) {
+ pr_err("%s() Error in gpio_request() for gpio %d\n",
+ __func__, pin_info->gpio_nr);
+ continue;
+ }
+ if (pin_info->is_input)
+ ret = gpio_direction_input(pin_info->gpio_nr);
+ else
+ ret = gpio_direction_output(pin_info->gpio_nr,
+ pin_info->value);
+ if (ret < 0) {
+ pr_err("%s() Error in setting gpio %d to in/out\n",
+ __func__, pin_info->gpio_nr);
+ gpio_free(pin_info->gpio_nr);
+ continue;
+ }
+ tegra_gpio_enable(pin_info->gpio_nr);
+ }
+}
+
+/* Initialize the pins to desired state as per power/asic/system-eng
+ * recomendation */
+int __init kai_pins_state_init(void)
+{
+ set_unused_pin_gpio(&pin_lpm_kai_common[0],
+ ARRAY_SIZE(pin_lpm_kai_common));
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-kai-power.c b/arch/arm/mach-tegra/board-kai-power.c
new file mode 100644
index 000000000000..6a65d0eb7b86
--- /dev/null
+++ b/arch/arm/mach-tegra/board-kai-power.c
@@ -0,0 +1,607 @@
+/*
+ * arch/arm/mach-tegra/board-kai-power.c
+ *
+ * Copyright (C) 2012 NVIDIA, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+#include <linux/i2c.h>
+#include <linux/pda_power.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/regulator/machine.h>
+#include <linux/mfd/max77663-core.h>
+#include <linux/regulator/max77663-regulator.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/regulator/gpio-switch-regulator.h>
+#include <linux/power/gpio-charger.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/pinmux.h>
+#include <mach/edp.h>
+
+#include "gpio-names.h"
+#include "board.h"
+#include "board-kai.h"
+#include "pm.h"
+#include "wakeups-t3.h"
+#include "tegra3_tsensor.h"
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_INTR_LOW (1 << 17)
+
+static struct regulator_consumer_supply max77663_sd0_supply[] = {
+ REGULATOR_SUPPLY("vdd_cpu", NULL),
+};
+
+static struct regulator_consumer_supply max77663_sd1_supply[] = {
+ REGULATOR_SUPPLY("vdd_core", NULL),
+};
+
+static struct regulator_consumer_supply max77663_sd2_supply[] = {
+ REGULATOR_SUPPLY("vdd_gen1v8", NULL),
+ REGULATOR_SUPPLY("avdd_hdmi_pll", NULL),
+ REGULATOR_SUPPLY("avdd_usb_pll", NULL),
+ REGULATOR_SUPPLY("avdd_osc", NULL),
+ REGULATOR_SUPPLY("vddio_sys", NULL),
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.3"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc4", NULL),
+ REGULATOR_SUPPLY("vddio_uart", NULL),
+ REGULATOR_SUPPLY("pwrdet_uart", NULL),
+ REGULATOR_SUPPLY("vddio_bb", NULL),
+ REGULATOR_SUPPLY("pwrdet_bb", NULL),
+ REGULATOR_SUPPLY("vddio_lcd_pmu", NULL),
+ REGULATOR_SUPPLY("pwrdet_lcd", NULL),
+ REGULATOR_SUPPLY("vddio_audio", NULL),
+ REGULATOR_SUPPLY("pwrdet_audio", NULL),
+ REGULATOR_SUPPLY("vddio_cam", NULL),
+ REGULATOR_SUPPLY("pwrdet_cam", NULL),
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.2"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc3", NULL),
+ REGULATOR_SUPPLY("vddio_vi", NULL),
+ REGULATOR_SUPPLY("pwrdet_vi", NULL),
+ REGULATOR_SUPPLY("vcore_nand", NULL),
+ REGULATOR_SUPPLY("pwrdet_nand", NULL),
+};
+
+static struct regulator_consumer_supply max77663_sd3_supply[] = {
+ REGULATOR_SUPPLY("vdd_ddr3l_1v35", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo0_supply[] = {
+ REGULATOR_SUPPLY("vdd_ddr_hs", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo1_supply[] = {
+};
+
+static struct regulator_consumer_supply max77663_ldo2_supply[] = {
+ REGULATOR_SUPPLY("vdd_ddr_rx", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo3_supply[] = {
+ REGULATOR_SUPPLY("vmmc", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo4_supply[] = {
+ REGULATOR_SUPPLY("vdd_rtc", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo5_supply[] = {
+ REGULATOR_SUPPLY("vdd_sensor_2v8", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo6_supply[] = {
+ REGULATOR_SUPPLY("vddio_sdmmc", "sdhci-tegra.0"),
+ REGULATOR_SUPPLY("pwrdet_sdmmc1", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo7_supply[] = {
+ REGULATOR_SUPPLY("avdd_dsi_csi", NULL),
+ REGULATOR_SUPPLY("pwrdet_mipi", NULL),
+};
+
+static struct regulator_consumer_supply max77663_ldo8_supply[] = {
+ REGULATOR_SUPPLY("avdd_plla_p_c_s", NULL),
+ REGULATOR_SUPPLY("avdd_pllm", NULL),
+ REGULATOR_SUPPLY("avdd_pllu_d", NULL),
+ REGULATOR_SUPPLY("avdd_pllu_d2", NULL),
+ REGULATOR_SUPPLY("avdd_pllx", NULL),
+};
+
+static struct max77663_regulator_fps_cfg max77663_fps_cfgs[] = {
+ {
+ .src = FPS_SRC_0,
+ .en_src = FPS_EN_SRC_EN0,
+ .time_period = FPS_TIME_PERIOD_DEF,
+ },
+ {
+ .src = FPS_SRC_1,
+ .en_src = FPS_EN_SRC_EN1,
+ .time_period = FPS_TIME_PERIOD_DEF,
+ },
+ {
+ .src = FPS_SRC_2,
+ .en_src = FPS_EN_SRC_EN0,
+ .time_period = FPS_TIME_PERIOD_DEF,
+ },
+};
+
+#define MAX77663_PDATA_INIT(_id, _min_uV, _max_uV, _supply_reg, \
+ _always_on, _boot_on, _apply_uV, \
+ _init_apply, _init_enable, _init_uV, \
+ _fps_src, _fps_pu_period, _fps_pd_period, _flags) \
+ static struct max77663_regulator_platform_data max77663_regulator_pdata_##_id = \
+ { \
+ .init_data = { \
+ .constraints = { \
+ .min_uV = _min_uV, \
+ .max_uV = _max_uV, \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = _always_on, \
+ .boot_on = _boot_on, \
+ .apply_uV = _apply_uV, \
+ }, \
+ .num_consumer_supplies = \
+ ARRAY_SIZE(max77663_##_id##_supply), \
+ .consumer_supplies = max77663_##_id##_supply, \
+ .supply_regulator = _supply_reg, \
+ }, \
+ .init_apply = _init_apply, \
+ .init_enable = _init_enable, \
+ .init_uV = _init_uV, \
+ .fps_src = _fps_src, \
+ .fps_pu_period = _fps_pu_period, \
+ .fps_pd_period = _fps_pd_period, \
+ .fps_cfgs = max77663_fps_cfgs, \
+ .flags = _flags, \
+ }
+
+MAX77663_PDATA_INIT(sd0, 600000, 3387500, NULL, 1, 0, 0,
+ 0, 0, -1, FPS_SRC_NONE, -1, -1, EN2_CTRL_SD0 | SD_FSRADE_DISABLE);
+
+MAX77663_PDATA_INIT(sd1, 800000, 1587500, NULL, 1, 0, 0,
+ 1, 1, -1, FPS_SRC_1, -1, -1, SD_FSRADE_DISABLE);
+
+MAX77663_PDATA_INIT(sd2, 600000, 3387500, NULL, 1, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(sd3, 600000, 3387500, NULL, 1, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo0, 800000, 2350000, max77663_rails(sd3), 1, 0, 0,
+ 1, 1, -1, FPS_SRC_1, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo1, 800000, 2350000, max77663_rails(sd3), 0, 0, 0,
+ 0, 0, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo2, 800000, 3950000, NULL, 1, 0, 0,
+ 1, 1, -1, FPS_SRC_1, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo3, 800000, 3950000, NULL, 1, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo4, 800000, 1587500, NULL, 0, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo5, 800000, 2800000, NULL, 0, 0, 0,
+ 1, 1, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo6, 800000, 3950000, NULL, 0, 0, 0,
+ 0, 0, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo7, 800000, 3950000, max77663_rails(sd3), 0, 0, 0,
+ 0, 0, -1, FPS_SRC_NONE, -1, -1, 0);
+
+MAX77663_PDATA_INIT(ldo8, 800000, 3950000, max77663_rails(sd3), 0, 0, 0,
+ 1, 1, -1, FPS_SRC_1, -1, -1, 0);
+
+#define MAX77663_REG(_id, _data) \
+ { \
+ .name = "max77663-regulator", \
+ .id = MAX77663_REGULATOR_ID_##_id, \
+ .platform_data = &max77663_regulator_pdata_##_data, \
+ }
+
+#define MAX77663_RTC() \
+ { \
+ .name = "max77663-rtc", \
+ .id = 0, \
+ }
+
+static struct mfd_cell max77663_subdevs[] = {
+ MAX77663_REG(SD0, sd0),
+ MAX77663_REG(SD1, sd1),
+ MAX77663_REG(SD2, sd2),
+ MAX77663_REG(SD3, sd3),
+ MAX77663_REG(LDO0, ldo0),
+ MAX77663_REG(LDO1, ldo1),
+ MAX77663_REG(LDO2, ldo2),
+ MAX77663_REG(LDO3, ldo3),
+ MAX77663_REG(LDO4, ldo4),
+ MAX77663_REG(LDO5, ldo5),
+ MAX77663_REG(LDO6, ldo6),
+ MAX77663_REG(LDO7, ldo7),
+ MAX77663_REG(LDO8, ldo8),
+ MAX77663_RTC(),
+};
+
+static struct max77663_gpio_config max77663_gpio_cfgs[] = {
+ {
+ .gpio = MAX77663_GPIO0,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_LOW,
+ .out_drv = GPIO_OUT_DRV_PUSH_PULL,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO1,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_LOW,
+ .out_drv = GPIO_OUT_DRV_PUSH_PULL,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO2,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_HIGH,
+ .out_drv = GPIO_OUT_DRV_OPEN_DRAIN,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO3,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_HIGH,
+ .out_drv = GPIO_OUT_DRV_OPEN_DRAIN,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO4,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_HIGH,
+ .out_drv = GPIO_OUT_DRV_OPEN_DRAIN,
+ .alternate = GPIO_ALT_ENABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO5,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_LOW,
+ .out_drv = GPIO_OUT_DRV_PUSH_PULL,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO6,
+ .dir = GPIO_DIR_IN,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+ {
+ .gpio = MAX77663_GPIO7,
+ .dir = GPIO_DIR_OUT,
+ .dout = GPIO_DOUT_LOW,
+ .out_drv = GPIO_OUT_DRV_PUSH_PULL,
+ .alternate = GPIO_ALT_DISABLE,
+ },
+};
+
+static struct max77663_platform_data max7763_pdata = {
+ .irq_base = MAX77663_IRQ_BASE,
+ .gpio_base = MAX77663_GPIO_BASE,
+
+ .num_gpio_cfgs = ARRAY_SIZE(max77663_gpio_cfgs),
+ .gpio_cfgs = max77663_gpio_cfgs,
+
+ .num_subdevs = ARRAY_SIZE(max77663_subdevs),
+ .sub_devices = max77663_subdevs,
+};
+
+static struct i2c_board_info __initdata max77663_regulators[] = {
+ {
+ /* The I2C address was determined by OTP factory setting */
+ I2C_BOARD_INFO("max77663", 0x3c),
+ .irq = INT_EXTERNAL_PMU,
+ .platform_data = &max7763_pdata,
+ },
+};
+
+static int __init kai_max77663_regulator_init(void)
+{
+ void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+ u32 pmc_ctrl;
+
+ /* configure the power management controller to trigger PMU
+ * interrupts when low */
+ pmc_ctrl = readl(pmc + PMC_CTRL);
+ writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
+
+ i2c_register_board_info(4, max77663_regulators,
+ ARRAY_SIZE(max77663_regulators));
+
+ return 0;
+}
+
+static struct regulator_consumer_supply gpio_switch_en_3v3_sys_supply[] = {
+ REGULATOR_SUPPLY("vdd_3v3", NULL),
+ REGULATOR_SUPPLY("vdd_3v3_devices", NULL),
+ REGULATOR_SUPPLY("debug_cons", NULL),
+ REGULATOR_SUPPLY("pwrdet_pex_ctl", NULL),
+};
+static int gpio_switch_en_3v3_sys_voltages[] = { 3300};
+
+static struct regulator_consumer_supply gpio_switch_en_avdd_hdmi_usb_supply[] = {
+ REGULATOR_SUPPLY("avdd_hdmi", NULL),
+ REGULATOR_SUPPLY("avdd_usb", NULL),
+ REGULATOR_SUPPLY("vddio_gmi", NULL),
+};
+static int gpio_switch_en_avdd_hdmi_usb_voltages[] = { 3300};
+
+static struct regulator_consumer_supply gpio_switch_en_1v8_cam_supply[] = {
+ REGULATOR_SUPPLY("vdd_1v8_cam1", NULL),
+ REGULATOR_SUPPLY("vdd_1v8_cam2", NULL),
+ REGULATOR_SUPPLY("vdd_1v8_cam3", NULL),
+};
+static int gpio_switch_en_1v8_cam_voltages[] = { 1800};
+
+static struct regulator_consumer_supply gpio_switch_en_vddio_vid_supply[] = {
+ REGULATOR_SUPPLY("vdd_hdmi_con", NULL),
+};
+static int gpio_switch_en_vddio_vid_voltages[] = { 5000};
+
+static struct regulator_consumer_supply gpio_switch_en_3v3_modem_supply[] = {
+ REGULATOR_SUPPLY("vdd_mini_card", NULL),
+};
+static int gpio_switch_en_3v3_modem_voltages[] = { 3300};
+
+static struct regulator_consumer_supply gpio_switch_en_vdd_pnl_supply[] = {
+ REGULATOR_SUPPLY("vdd_lvds", NULL),
+ REGULATOR_SUPPLY("vdd_lcd_panel", NULL),
+ REGULATOR_SUPPLY("vdd_touch", NULL),
+ REGULATOR_SUPPLY("vddio_ts", NULL),
+};
+static int gpio_switch_en_vdd_pnl_voltages[] = { 3300};
+
+static struct regulator_consumer_supply gpio_switch_en_cam3_ldo_supply[] = {
+ REGULATOR_SUPPLY("vdd_cam3", NULL),
+};
+static int gpio_switch_en_cam3_ldo_voltages[] = { 3300};
+
+static struct regulator_consumer_supply gpio_switch_en_vdd_com_supply[] = {
+ REGULATOR_SUPPLY("vdd_com_bd", NULL),
+};
+static int gpio_switch_en_vdd_com_voltages[] = { 3300};
+
+static struct regulator_consumer_supply gpio_switch_en_vdd_sdmmc1_supply[] = {
+ REGULATOR_SUPPLY("vddio_sd_slot", "sdhci-tegra.0"),
+};
+static int gpio_switch_en_vdd_sdmmc1_voltages[] = { 3300};
+
+static struct regulator_consumer_supply gpio_switch_en_3v3_fuse_supply[] = {
+ REGULATOR_SUPPLY("vpp_fuse", NULL),
+};
+static int gpio_switch_en_3v3_fuse_voltages[] = { 3300};
+
+static struct regulator_consumer_supply gpio_switch_cdc_en_supply[] = {
+ REGULATOR_SUPPLY("cdc_en", NULL),
+};
+static int gpio_switch_cdc_en_voltages[] = { 1200};
+
+/* Macro for defining gpio switch regulator sub device data */
+#define GREG_INIT(_id, _var, _name, _input_supply, _always_on, _boot_on, \
+ _gpio_nr, _active_low, _init_state, _pg, _enable, _disable) \
+ static struct gpio_switch_regulator_subdev_data gpio_pdata_##_var = \
+ { \
+ .regulator_name = "gpio-switch-"#_name, \
+ .input_supply = _input_supply, \
+ .id = _id, \
+ .gpio_nr = _gpio_nr, \
+ .pin_group = _pg, \
+ .active_low = _active_low, \
+ .init_state = _init_state, \
+ .voltages = gpio_switch_##_name##_voltages, \
+ .n_voltages = ARRAY_SIZE(gpio_switch_##_name##_voltages), \
+ .num_consumer_supplies = \
+ ARRAY_SIZE(gpio_switch_##_name##_supply), \
+ .consumer_supplies = gpio_switch_##_name##_supply, \
+ .constraints = { \
+ .valid_modes_mask = (REGULATOR_MODE_NORMAL | \
+ REGULATOR_MODE_STANDBY), \
+ .valid_ops_mask = (REGULATOR_CHANGE_MODE | \
+ REGULATOR_CHANGE_STATUS | \
+ REGULATOR_CHANGE_VOLTAGE), \
+ .always_on = _always_on, \
+ .boot_on = _boot_on, \
+ }, \
+ .enable_rail = _enable, \
+ .disable_rail = _disable, \
+ }
+
+GREG_INIT(1, en_3v3_sys, en_3v3_sys, NULL,
+ 1, 0, MAX77663_GPIO_BASE + MAX77663_GPIO3, false, 1, 0, 0, 0);
+GREG_INIT(2, en_avdd_hdmi_usb, en_avdd_hdmi_usb, "vdd_3v3_devices",
+ 1, 0, MAX77663_GPIO_BASE + MAX77663_GPIO2, false, 1, 0, 0, 0);
+GREG_INIT(3, en_1v8_cam, en_1v8_cam, "vdd_gen1v8",
+ 0, 0, TEGRA_GPIO_PS0, false, 0, 0, 0, 0);
+GREG_INIT(4, en_vddio_vid_oc, en_vddio_vid, NULL,
+ 0, 0, TEGRA_GPIO_PB2, false, 0, 0, 0, 0);
+GREG_INIT(5, en_3v3_modem, en_3v3_modem, NULL,
+ 0, 0, TEGRA_GPIO_PP0, false, 0, 0, 0, 0);
+GREG_INIT(6, en_vdd_pnl, en_vdd_pnl, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PW1, false, 0, 0, 0, 0);
+GREG_INIT(7, en_cam3_ldo, en_cam3_ldo, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PR7, false, 0, 0, 0, 0);
+GREG_INIT(8, en_vdd_com, en_vdd_com, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PD0, false, 0, 0, 0, 0);
+GREG_INIT(9, en_vdd_sdmmc1, en_vdd_sdmmc1, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PC6, false, 0, 0, 0, 0);
+GREG_INIT(10, en_3v3_fuse, en_3v3_fuse, "vdd_3v3_devices",
+ 0, 0, TEGRA_GPIO_PC1, false, 0, 0, 0, 0);
+GREG_INIT(11, cdc_en, cdc_en, "vddio_audio",
+ 0, 1, TEGRA_GPIO_PX2, false, 0, 0, 0, 0);
+
+
+#define ADD_GPIO_REG(_name) &gpio_pdata_##_name
+
+#define E1565_GPIO_REG \
+ ADD_GPIO_REG(en_3v3_sys), \
+ ADD_GPIO_REG(en_avdd_hdmi_usb), \
+ ADD_GPIO_REG(en_1v8_cam), \
+ ADD_GPIO_REG(en_vddio_vid_oc), \
+ ADD_GPIO_REG(en_3v3_modem), \
+ ADD_GPIO_REG(en_vdd_pnl), \
+ ADD_GPIO_REG(en_cam3_ldo), \
+ ADD_GPIO_REG(en_vdd_com), \
+ ADD_GPIO_REG(en_vdd_sdmmc1), \
+ ADD_GPIO_REG(en_3v3_fuse), \
+ ADD_GPIO_REG(cdc_en), \
+
+
+static struct gpio_switch_regulator_subdev_data *gswitch_subdevs[] = {
+ E1565_GPIO_REG
+};
+
+static struct gpio_switch_regulator_platform_data gswitch_pdata = {
+ .subdevs = gswitch_subdevs,
+ .num_subdevs = ARRAY_SIZE(gswitch_subdevs),
+};
+
+static struct platform_device gswitch_regulator_pdata = {
+ .name = "gpio-switch-regulator",
+ .id = -1,
+ .dev = {
+ .platform_data = &gswitch_pdata,
+ },
+};
+
+static int __init kai_max77663_gpio_switch_regulator_init(void)
+{
+ int i;
+
+ for (i = 0; i < gswitch_pdata.num_subdevs; ++i) {
+ struct gpio_switch_regulator_subdev_data *gswitch_data =
+ gswitch_pdata.subdevs[i];
+ if (gswitch_data->gpio_nr <= TEGRA_NR_GPIOS)
+ tegra_gpio_enable(gswitch_data->gpio_nr);
+ }
+
+ return platform_device_register(&gswitch_regulator_pdata);
+}
+
+int __init kai_regulator_init(void)
+{
+ void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE);
+ u32 pmc_ctrl;
+ int ret;
+
+ /* configure the power management controller to trigger PMU
+ * interrupts when low */
+
+ pmc_ctrl = readl(pmc + PMC_CTRL);
+ writel(pmc_ctrl | PMC_CTRL_INTR_LOW, pmc + PMC_CTRL);
+
+ ret = kai_max77663_regulator_init();
+ if (ret < 0)
+ return ret;
+
+ return kai_max77663_gpio_switch_regulator_init();
+}
+
+static void kai_board_suspend(int lp_state, enum suspend_stage stg)
+{
+ if ((lp_state == TEGRA_SUSPEND_LP1) && (stg == TEGRA_SUSPEND_BEFORE_CPU))
+ tegra_console_uart_suspend();
+}
+
+static void kai_board_resume(int lp_state, enum resume_stage stg)
+{
+ if ((lp_state == TEGRA_SUSPEND_LP1) && (stg == TEGRA_RESUME_AFTER_CPU))
+ tegra_console_uart_resume();
+}
+
+static struct tegra_suspend_platform_data kai_suspend_data = {
+ .cpu_timer = 2000,
+ .cpu_off_timer = 200,
+ .suspend_mode = TEGRA_SUSPEND_LP0,
+ .core_timer = 0x7e7e,
+ .core_off_timer = 0,
+ .corereq_high = true,
+ .sysclkreq_high = true,
+ .cpu_lp2_min_residency = 2000,
+ .board_suspend = kai_board_suspend,
+ .board_resume = kai_board_resume,
+};
+
+int __init kai_suspend_init(void)
+{
+ tegra_init_suspend(&kai_suspend_data);
+ return 0;
+}
+
+static void kai_power_off(void)
+{
+ int ret;
+ pr_err("kai: Powering off the device\n");
+ ret = max77663_power_off();
+ if (ret)
+ pr_err("kai: failed to power off\n");
+
+ while (1)
+ ;
+}
+
+int __init kai_power_off_init(void)
+{
+ pm_power_off = kai_power_off;
+
+ return 0;
+}
+
+static struct tegra_tsensor_pmu_data tpdata = {
+ .poweroff_reg_addr = 0x3F,
+ .poweroff_reg_data = 0x80,
+ .reset_tegra = 1,
+ .controller_type = 0,
+ .i2c_controller_id = 4,
+ .pinmux = 0,
+ .pmu_16bit_ops = 0,
+ .pmu_i2c_addr = 0x2D,
+};
+
+void __init kai_tsensor_init(void)
+{
+ tegra3_tsensor_init(&tpdata);
+}
+
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+
+int __init kai_edp_init(void)
+{
+ unsigned int regulator_mA;
+
+ regulator_mA = get_maximum_cpu_current_supported();
+ if (!regulator_mA)
+ regulator_mA = 6000; /* regular T30/s */
+ pr_info("%s: CPU regulator %d mA\n", __func__, regulator_mA);
+
+ tegra_init_cpu_edp_limits(regulator_mA);
+ return 0;
+}
+#endif
diff --git a/arch/arm/mach-tegra/board-kai-sdhci.c b/arch/arm/mach-tegra/board-kai-sdhci.c
new file mode 100644
index 000000000000..99f7736b14e7
--- /dev/null
+++ b/arch/arm/mach-tegra/board-kai-sdhci.c
@@ -0,0 +1,163 @@
+/*
+ * arch/arm/mach-tegra/board-kai-sdhci.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2012 NVIDIA Corporation.
+ *
+ * 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/resource.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/mmc/host.h>
+
+#include <asm/mach-types.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+#include <mach/sdhci.h>
+
+#include "gpio-names.h"
+#include "board.h"
+#include "board-kai.h"
+
+#define KAI_SD_CD TEGRA_GPIO_PI5
+
+static struct resource sdhci_resource0[] = {
+ [0] = {
+ .start = INT_SDMMC1,
+ .end = INT_SDMMC1,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC1_BASE,
+ .end = TEGRA_SDMMC1_BASE + TEGRA_SDMMC1_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource2[] = {
+ [0] = {
+ .start = INT_SDMMC3,
+ .end = INT_SDMMC3,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC3_BASE,
+ .end = TEGRA_SDMMC3_BASE + TEGRA_SDMMC3_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct resource sdhci_resource3[] = {
+ [0] = {
+ .start = INT_SDMMC4,
+ .end = INT_SDMMC4,
+ .flags = IORESOURCE_IRQ,
+ },
+ [1] = {
+ .start = TEGRA_SDMMC4_BASE,
+ .end = TEGRA_SDMMC4_BASE + TEGRA_SDMMC4_SIZE-1,
+ .flags = IORESOURCE_MEM,
+ },
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data2 = {
+ .mmc_data = {
+ .built_in = 1,
+ },
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+/* .tap_delay = 6,
+ .is_voltage_switch_supported = false,
+ .vdd_rail_name = NULL,
+ .slot_rail_name = NULL,
+ .vdd_max_uv = -1,
+ .vdd_min_uv = -1,
+ .max_clk = 0,
+ .is_8bit_supported = false, */
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data0 = {
+ .cd_gpio = KAI_SD_CD,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+/* .tap_delay = 6,
+ .is_voltage_switch_supported = true,
+ .vdd_rail_name = "vddio_sdmmc1",
+ .slot_rail_name = "vddio_sd_slot",
+ .vdd_max_uv = 3320000,
+ .vdd_min_uv = 3280000,
+ .max_clk = 208000000,
+ .is_8bit_supported = false, */
+};
+
+static struct tegra_sdhci_platform_data tegra_sdhci_platform_data3 = {
+ .cd_gpio = -1,
+ .wp_gpio = -1,
+ .power_gpio = -1,
+ .is_8bit = 1,
+ .tap_delay = 0x0F,
+ .mmc_data = {
+ .built_in = 1,
+ }
+/* .tap_delay = 6,
+ .is_voltage_switch_supported = false,
+ .vdd_rail_name = NULL,
+ .slot_rail_name = NULL,
+ .vdd_max_uv = -1,
+ .vdd_min_uv = -1,
+ .max_clk = 48000000,
+ .is_8bit_supported = true, */
+};
+
+static struct platform_device tegra_sdhci_device0 = {
+ .name = "sdhci-tegra",
+ .id = 0,
+ .resource = sdhci_resource0,
+ .num_resources = ARRAY_SIZE(sdhci_resource0),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data0,
+ },
+};
+
+static struct platform_device tegra_sdhci_device2 = {
+ .name = "sdhci-tegra",
+ .id = 2,
+ .resource = sdhci_resource2,
+ .num_resources = ARRAY_SIZE(sdhci_resource2),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data2,
+ },
+};
+
+static struct platform_device tegra_sdhci_device3 = {
+ .name = "sdhci-tegra",
+ .id = 3,
+ .resource = sdhci_resource3,
+ .num_resources = ARRAY_SIZE(sdhci_resource3),
+ .dev = {
+ .platform_data = &tegra_sdhci_platform_data3,
+ },
+};
+
+int __init kai_sdhci_init(void)
+{
+ platform_device_register(&tegra_sdhci_device3);
+ platform_device_register(&tegra_sdhci_device2);
+ platform_device_register(&tegra_sdhci_device0);
+
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-kai.c b/arch/arm/mach-tegra/board-kai.c
new file mode 100644
index 000000000000..408d0db17c0f
--- /dev/null
+++ b/arch/arm/mach-tegra/board-kai.c
@@ -0,0 +1,600 @@
+/*
+ * arch/arm/mach-tegra/board-kai.c
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/serial_8250.h>
+#include <linux/i2c.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/i2c-tegra.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/platform_data/tegra_usb.h>
+#include <linux/spi/spi.h>
+#include <linux/tegra_uart.h>
+#include <linux/memblock.h>
+#include <linux/spi-tegra.h>
+
+#include <mach/clk.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <mach/pinmux.h>
+#include <mach/iomap.h>
+#include <mach/io.h>
+#include <mach/i2s.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <mach/usb_phy.h>
+#include <mach/thermal.h>
+
+#include "board.h"
+#include "clock.h"
+#include "board-kai.h"
+#include "devices.h"
+#include "gpio-names.h"
+#include "fuse.h"
+#include "pm.h"
+#include "wdt-recovery.h"
+
+/* All units are in millicelsius */
+static struct tegra_thermal_data thermal_data = {
+ .temp_throttle = 85000,
+ .temp_shutdown = 90000,
+ .temp_offset = TDIODE_OFFSET, /* temps based on tdiode */
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ .edp_offset = TDIODE_OFFSET, /* edp based on tdiode */
+ .hysteresis_edp = 3000,
+#endif
+#ifdef CONFIG_TEGRA_THERMAL_SYSFS
+ .tc1 = 0,
+ .tc2 = 1,
+ .passive_delay = 2000,
+#else
+ .hysteresis_throttle = 1000,
+#endif
+};
+
+/* !!!TODO: Change for kai (Taken from Ventana) */
+static struct tegra_utmip_config utmi_phy_config[] = {
+ [0] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 15,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+ [1] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 15,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+ [2] = {
+ .hssync_start_delay = 0,
+ .idle_wait_delay = 17,
+ .elastic_limit = 16,
+ .term_range_adj = 6,
+ .xcvr_setup = 8,
+ .xcvr_setup_offset = 0,
+ .xcvr_use_fuses = 1,
+ .xcvr_lsfslew = 2,
+ .xcvr_lsrslew = 2,
+ },
+};
+
+static __initdata struct tegra_clk_init_table kai_clk_init_table[] = {
+ /* name parent rate enabled */
+ { "pll_m", NULL, 0, false},
+ { "hda", "pll_p", 108000000, false},
+ { "hda2codec_2x", "pll_p", 48000000, false},
+ { "pwm", "pll_p", 3187500, false},
+ { "blink", "clk_32k", 32768, true},
+ { "i2s1", "pll_a_out0", 0, false},
+ { "i2s3", "pll_a_out0", 0, false},
+ { "spdif_out", "pll_a_out0", 0, false},
+ { "d_audio", "pll_a_out0", 0, false},
+ { "dam0", "pll_a_out0", 0, false},
+ { "dam1", "pll_a_out0", 0, false},
+ { "dam2", "pll_a_out0", 0, false},
+ { "audio1", "i2s1_sync", 0, false},
+ { "audio3", "i2s3_sync", 0, false},
+ { "vi_sensor", "pll_p", 150000000, false},
+ { "i2c1", "pll_p", 3200000, false},
+ { "i2c2", "pll_p", 3200000, false},
+ { "i2c3", "pll_p", 3200000, false},
+ { "i2c4", "pll_p", 3200000, false},
+ { "i2c5", "pll_p", 3200000, false},
+ { NULL, NULL, 0, 0},
+};
+
+static struct tegra_i2c_platform_data kai_i2c1_platform_data = {
+ .adapter_nr = 0,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .scl_gpio = {TEGRA_GPIO_PC4, 0},
+ .sda_gpio = {TEGRA_GPIO_PC5, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static struct tegra_i2c_platform_data kai_i2c2_platform_data = {
+ .adapter_nr = 1,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .is_clkon_always = true,
+ .scl_gpio = {TEGRA_GPIO_PT5, 0},
+ .sda_gpio = {TEGRA_GPIO_PT6, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static struct tegra_i2c_platform_data kai_i2c3_platform_data = {
+ .adapter_nr = 2,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .scl_gpio = {TEGRA_GPIO_PBB1, 0},
+ .sda_gpio = {TEGRA_GPIO_PBB2, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static struct tegra_i2c_platform_data kai_i2c4_platform_data = {
+ .adapter_nr = 3,
+ .bus_count = 1,
+ .bus_clk_rate = { 100000, 0 },
+ .scl_gpio = {TEGRA_GPIO_PV4, 0},
+ .sda_gpio = {TEGRA_GPIO_PV5, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static struct tegra_i2c_platform_data kai_i2c5_platform_data = {
+ .adapter_nr = 4,
+ .bus_count = 1,
+ .bus_clk_rate = { 400000, 0 },
+ .scl_gpio = {TEGRA_GPIO_PZ6, 0},
+ .sda_gpio = {TEGRA_GPIO_PZ7, 0},
+ .arb_recovery = arb_lost_recovery,
+};
+
+static void kai_i2c_init(void)
+{
+ tegra_i2c_device1.dev.platform_data = &kai_i2c1_platform_data;
+ tegra_i2c_device2.dev.platform_data = &kai_i2c2_platform_data;
+ tegra_i2c_device3.dev.platform_data = &kai_i2c3_platform_data;
+ tegra_i2c_device4.dev.platform_data = &kai_i2c4_platform_data;
+ tegra_i2c_device5.dev.platform_data = &kai_i2c5_platform_data;
+
+ platform_device_register(&tegra_i2c_device5);
+ platform_device_register(&tegra_i2c_device4);
+ platform_device_register(&tegra_i2c_device3);
+ platform_device_register(&tegra_i2c_device2);
+ platform_device_register(&tegra_i2c_device1);
+}
+
+static struct platform_device *kai_uart_devices[] __initdata = {
+ &tegra_uarta_device,
+ &tegra_uartb_device,
+ &tegra_uartc_device,
+ &tegra_uartd_device,
+ &tegra_uarte_device,
+};
+static struct uart_clk_parent uart_parent_clk[] = {
+ [0] = {.name = "clk_m"},
+ [1] = {.name = "pll_p"},
+#ifndef CONFIG_TEGRA_PLLM_RESTRICTED
+ [2] = {.name = "pll_m"},
+#endif
+};
+
+static struct tegra_uart_platform_data kai_uart_pdata;
+static struct tegra_uart_platform_data kai_loopback_uart_pdata;
+
+static void __init uart_debug_init(void)
+{
+ int debug_port_id;
+
+ debug_port_id = get_tegra_uart_debug_port_id();
+ if (debug_port_id < 0)
+ debug_port_id = 3;
+
+ switch (debug_port_id) {
+ case 0:
+ /* UARTA is the debug port. */
+ pr_info("Selecting UARTA as the debug console\n");
+ kai_uart_devices[0] = &debug_uarta_device;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uarta");
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uarta_device.dev.platform_data))->mapbase;
+ break;
+
+ case 1:
+ /* UARTB is the debug port. */
+ pr_info("Selecting UARTB as the debug console\n");
+ kai_uart_devices[1] = &debug_uartb_device;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uartb");
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uartb_device.dev.platform_data))->mapbase;
+ break;
+
+ case 2:
+ /* UARTC is the debug port. */
+ pr_info("Selecting UARTC as the debug console\n");
+ kai_uart_devices[2] = &debug_uartc_device;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uartc");
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uartc_device.dev.platform_data))->mapbase;
+ break;
+
+ case 3:
+ /* UARTD is the debug port. */
+ pr_info("Selecting UARTD as the debug console\n");
+ kai_uart_devices[3] = &debug_uartd_device;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uartd");
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uartd_device.dev.platform_data))->mapbase;
+ break;
+
+ case 4:
+ /* UARTE is the debug port. */
+ pr_info("Selecting UARTE as the debug console\n");
+ kai_uart_devices[4] = &debug_uarte_device;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uarte");
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uarte_device.dev.platform_data))->mapbase;
+ break;
+
+ default:
+ pr_info("The debug console id %d is invalid, Assuming UARTA",
+ debug_port_id);
+ kai_uart_devices[0] = &debug_uarta_device;
+ debug_uart_clk = clk_get_sys("serial8250.0", "uarta");
+ debug_uart_port_base = ((struct plat_serial8250_port *)(
+ debug_uarta_device.dev.platform_data))->mapbase;
+ break;
+ }
+ return;
+}
+
+static void __init kai_uart_init(void)
+{
+ struct clk *c;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(uart_parent_clk); ++i) {
+ c = tegra_get_clock_by_name(uart_parent_clk[i].name);
+ if (IS_ERR_OR_NULL(c)) {
+ pr_err("Not able to get the clock for %s\n",
+ uart_parent_clk[i].name);
+ continue;
+ }
+ uart_parent_clk[i].parent_clk = c;
+ uart_parent_clk[i].fixed_clk_rate = clk_get_rate(c);
+ }
+ kai_uart_pdata.parent_clk_list = uart_parent_clk;
+ kai_uart_pdata.parent_clk_count = ARRAY_SIZE(uart_parent_clk);
+ kai_loopback_uart_pdata.parent_clk_list = uart_parent_clk;
+ kai_loopback_uart_pdata.parent_clk_count =
+ ARRAY_SIZE(uart_parent_clk);
+ kai_loopback_uart_pdata.is_loopback = true;
+ tegra_uarta_device.dev.platform_data = &kai_uart_pdata;
+ tegra_uartb_device.dev.platform_data = &kai_uart_pdata;
+ tegra_uartc_device.dev.platform_data = &kai_uart_pdata;
+ tegra_uartd_device.dev.platform_data = &kai_uart_pdata;
+ /* UARTE is used for loopback test purpose */
+ tegra_uarte_device.dev.platform_data = &kai_loopback_uart_pdata;
+
+ /* Register low speed only if it is selected */
+ if (!is_tegra_debug_uartport_hs()) {
+ uart_debug_init();
+ /* Clock enable for the debug channel */
+ if (!IS_ERR_OR_NULL(debug_uart_clk)) {
+ pr_info("The debug console clock name is %s\n",
+ debug_uart_clk->name);
+ c = tegra_get_clock_by_name("pll_p");
+ if (IS_ERR_OR_NULL(c))
+ pr_err("Not getting the parent clock pll_p\n");
+ else
+ clk_set_parent(debug_uart_clk, c);
+
+ clk_enable(debug_uart_clk);
+ clk_set_rate(debug_uart_clk, clk_get_rate(c));
+ } else {
+ pr_err("Not getting the clock %s for debug console\n",
+ debug_uart_clk->name);
+ }
+ }
+
+ platform_add_devices(kai_uart_devices,
+ ARRAY_SIZE(kai_uart_devices));
+}
+
+static struct platform_device tegra_camera = {
+ .name = "tegra_camera",
+ .id = -1,
+};
+
+static struct platform_device *kai_spi_devices[] __initdata = {
+ &tegra_spi_device4,
+ &tegra_spi_device1,
+};
+
+static struct spi_clk_parent spi_parent_clk[] = {
+ [0] = {.name = "pll_p"},
+#ifndef CONFIG_TEGRA_PLLM_RESTRICTED
+ [1] = {.name = "pll_m"},
+ [2] = {.name = "clk_m"},
+#else
+ [1] = {.name = "clk_m"},
+#endif
+};
+
+static struct tegra_spi_platform_data kai_spi_pdata = {
+ .is_dma_based = true,
+ .max_dma_buffer = (16 * 1024),
+ .is_clkon_always = false,
+ .max_rate = 100000000,
+};
+
+static void __init kai_spi_init(void)
+{
+ int i;
+ struct clk *c;
+
+ for (i = 0; i < ARRAY_SIZE(spi_parent_clk); ++i) {
+ c = tegra_get_clock_by_name(spi_parent_clk[i].name);
+ if (IS_ERR_OR_NULL(c)) {
+ pr_err("Not able to get the clock for %s\n",
+ spi_parent_clk[i].name);
+ continue;
+ }
+ spi_parent_clk[i].parent_clk = c;
+ spi_parent_clk[i].fixed_clk_rate = clk_get_rate(c);
+ }
+ kai_spi_pdata.parent_clk_list = spi_parent_clk;
+ kai_spi_pdata.parent_clk_count = ARRAY_SIZE(spi_parent_clk);
+ tegra_spi_device4.dev.platform_data = &kai_spi_pdata;
+ platform_add_devices(kai_spi_devices,
+ ARRAY_SIZE(kai_spi_devices));
+
+}
+
+static struct resource tegra_rtc_resources[] = {
+ [0] = {
+ .start = TEGRA_RTC_BASE,
+ .end = TEGRA_RTC_BASE + TEGRA_RTC_SIZE - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ [1] = {
+ .start = INT_RTC,
+ .end = INT_RTC,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static struct platform_device tegra_rtc_device = {
+ .name = "tegra_rtc",
+ .id = -1,
+ .resource = tegra_rtc_resources,
+ .num_resources = ARRAY_SIZE(tegra_rtc_resources),
+};
+
+static struct platform_device *kai_devices[] __initdata = {
+ &tegra_pmu_device,
+ &tegra_rtc_device,
+ &tegra_udc_device,
+#if defined(CONFIG_TEGRA_IOVMM_SMMU)
+ &tegra_smmu_device,
+#endif
+ &tegra_wdt_device,
+#if defined(CONFIG_TEGRA_AVP)
+ &tegra_avp_device,
+#endif
+ &tegra_camera,
+#if defined(CONFIG_CRYPTO_DEV_TEGRA_SE)
+ &tegra_se_device,
+#endif
+ &tegra_ahub_device,
+ &tegra_dam_device0,
+ &tegra_dam_device1,
+ &tegra_dam_device2,
+ &tegra_i2s_device1,
+ &tegra_i2s_device3,
+ &tegra_spdif_device,
+ &spdif_dit_device,
+ &bluetooth_dit_device,
+ &tegra_pcm_device,
+ &tegra_hda_device,
+#if defined(CONFIG_CRYPTO_DEV_TEGRA_AES)
+ &tegra_aes_device,
+#endif
+};
+
+static __initdata struct tegra_clk_init_table spi_clk_init_table[] = {
+ /* name parent rate enabled */
+ { "sbc1", "pll_p", 72000000, true},
+ { NULL, NULL, 0, 0},
+};
+
+static __initdata struct tegra_clk_init_table touch_clk_init_table[] = {
+ /* name parent rate enabled */
+ { "extern3", "pll_p", 41000000, true},
+ { "clk_out_3", "extern3", 40800000, true},
+ { NULL, NULL, 0, 0},
+};
+
+static int __init kai_touch_init(void)
+{
+ int touch_id;
+
+ tegra_gpio_enable(KAI_TS_ID1);
+ tegra_gpio_enable(KAI_TS_ID2);
+
+ gpio_request(KAI_TS_ID1, "touch-id1");
+ gpio_direction_input(KAI_TS_ID1);
+
+ gpio_request(KAI_TS_ID2, "touch-id2");
+ gpio_direction_input(KAI_TS_ID2);
+
+ touch_id = gpio_get_value(KAI_TS_ID1) << 1;
+ touch_id |= gpio_get_value(KAI_TS_ID2);
+
+ pr_info("touch-id %d\n", touch_id);
+
+ /* Disable TS_ID GPIO to save power */
+ gpio_direction_output(KAI_TS_ID1, 0);
+ tegra_pinmux_set_pullupdown(KAI_TS_ID1_PG, TEGRA_PUPD_NORMAL);
+ tegra_pinmux_set_tristate(KAI_TS_ID1_PG, TEGRA_TRI_TRISTATE);
+ gpio_direction_output(KAI_TS_ID2, 0);
+ tegra_pinmux_set_pullupdown(KAI_TS_ID2_PG, TEGRA_PUPD_NORMAL);
+ tegra_pinmux_set_tristate(KAI_TS_ID2_PG, TEGRA_TRI_TRISTATE);
+
+ switch (touch_id) {
+ case 0:
+ pr_info("Raydium PCB based touch init\n");
+ tegra_clk_init_from_table(spi_clk_init_table);
+ touch_init_raydium();
+ break;
+ case 1:
+ pr_info("Raydium On-Board touch init\n");
+ tegra_clk_init_from_table(spi_clk_init_table);
+ tegra_clk_init_from_table(touch_clk_init_table);
+ clk_enable(tegra_get_clock_by_name("clk_out_3"));
+
+ touch_init_raydium();
+ break;
+ case 3:
+ pr_info("Synaptics PCB based touch init\n");
+ touch_init_synaptics_kai();
+ break;
+ default:
+ pr_err("touch_id error, no touch %d\n", touch_id);
+ }
+ return 0;
+}
+
+static struct tegra_ehci_platform_data tegra_ehci_pdata[] = {
+ [0] = {
+ .phy_config = &utmi_phy_config[0],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ },
+ [1] = {
+ .phy_config = &utmi_phy_config[1],
+ .operating_mode = TEGRA_USB_HOST,
+ .power_down_on_bus_suspend = 1,
+ },
+};
+
+static struct tegra_otg_platform_data tegra_otg_pdata = {
+ .ehci_device = &tegra_ehci1_device,
+ .ehci_pdata = &tegra_ehci_pdata[0],
+};
+
+#ifdef CONFIG_USB_SUPPORT
+static struct usb_phy_plat_data tegra_usb_phy_pdata[] = {
+ [0] = {
+ .instance = 0,
+ .vbus_gpio = -1,
+ },
+ [1] = {
+ .instance = 1,
+ .vbus_gpio = -1,
+ },
+};
+
+static void kai_usb_init(void)
+{
+ tegra_usb_phy_init(tegra_usb_phy_pdata,
+ ARRAY_SIZE(tegra_usb_phy_pdata));
+
+ tegra_otg_device.dev.platform_data = &tegra_otg_pdata;
+ platform_device_register(&tegra_otg_device);
+
+ tegra_ehci2_device.dev.platform_data = &tegra_ehci_pdata[1];
+ platform_device_register(&tegra_ehci2_device);
+}
+#else
+static void kai_usb_init(void) { }
+#endif
+
+static void __init tegra_kai_init(void)
+{
+ tegra_thermal_init(&thermal_data);
+ tegra_clk_init_from_table(kai_clk_init_table);
+ kai_pinmux_init();
+ kai_i2c_init();
+ kai_spi_init();
+ kai_usb_init();
+#ifdef CONFIG_TEGRA_EDP_LIMITS
+ kai_edp_init();
+#endif
+ kai_uart_init();
+ kai_tsensor_init();
+ platform_add_devices(kai_devices, ARRAY_SIZE(kai_devices));
+ tegra_ram_console_debug_init();
+ kai_sdhci_init();
+ kai_regulator_init();
+ kai_suspend_init();
+ kai_power_off_init();
+ kai_touch_init();
+ kai_keys_init();
+ kai_panel_init();
+ kai_pins_state_init();
+ tegra_release_bootloader_fb();
+#ifdef CONFIG_TEGRA_WDT_RECOVERY
+ tegra_wdt_recovery_init();
+#endif
+}
+
+static void __init kai_ramconsole_reserve(unsigned long size)
+{
+ tegra_ram_console_debug_reserve(SZ_1M);
+}
+
+static void __init tegra_kai_reserve(void)
+{
+#if defined(CONFIG_NVMAP_CONVERT_CARVEOUT_TO_IOVMM)
+ /* support 1920X1200 with 24bpp */
+ tegra_reserve(0, SZ_8M + SZ_1M, SZ_8M + SZ_1M);
+#else
+ tegra_reserve(SZ_128M, SZ_8M, SZ_8M);
+#endif
+ kai_ramconsole_reserve(SZ_1M);
+}
+
+MACHINE_START(KAI, "kai")
+ .boot_params = 0x80000100,
+ .map_io = tegra_map_common_io,
+ .reserve = tegra_kai_reserve,
+ .init_early = tegra_init_early,
+ .init_irq = tegra_init_irq,
+ .timer = &tegra_timer,
+ .init_machine = tegra_kai_init,
+MACHINE_END
diff --git a/arch/arm/mach-tegra/board-kai.h b/arch/arm/mach-tegra/board-kai.h
new file mode 100644
index 000000000000..9dbced74b6ba
--- /dev/null
+++ b/arch/arm/mach-tegra/board-kai.h
@@ -0,0 +1,77 @@
+/*
+ * arch/arm/mach-tegra/board-kai.h
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef _MACH_TEGRA_BOARD_KAI_H
+#define _MACH_TEGRA_BOARD_KAI_H
+
+#include <mach/gpio.h>
+#include <mach/irqs.h>
+#include <linux/mfd/max77663-core.h>
+#include "gpio-names.h"
+
+/* Processor Board ID */
+#define BOARD_E1565 0xF41
+
+/* Board Fab version */
+#define BOARD_FAB_A00 0x0
+#define BOARD_FAB_A01 0x1
+#define BOARD_FAB_A02 0x2
+#define BOARD_FAB_A03 0x3
+#define BOARD_FAB_A04 0x4
+#define BOARD_FAB_A05 0x5
+
+/* External peripheral act as gpio */
+/* MAX77663 GPIO */
+#define MAX77663_GPIO_BASE TEGRA_NR_GPIOS
+#define MAX77663_GPIO_END (MAX77663_GPIO_BASE + MAX77663_GPIO_NR)
+
+/*****************Interrupt tables ******************/
+/* External peripheral act as interrupt controller */
+/* MAX77663 IRQs */
+#define MAX77663_IRQ_BASE TEGRA_NR_IRQS
+#define MAX77663_IRQ_END (MAX77663_IRQ_BASE + MAX77663_IRQ_NR)
+
+int kai_charge_init(void);
+int kai_regulator_init(void);
+int kai_suspend_init(void);
+int kai_sdhci_init(void);
+int kai_pinmux_init(void);
+int kai_panel_init(void);
+int kai_keys_init(void);
+int kai_pins_state_init(void);
+int kai_power_off_init(void);
+int kai_edp_init(void);
+void __init kai_tsensor_init(void);
+int __init touch_init_raydium(void);
+int __init touch_init_synaptics_kai(void);
+
+#define TOUCH_GPIO_IRQ_RAYDIUM_SPI TEGRA_GPIO_PZ3
+#define TOUCH_GPIO_RST_RAYDIUM_SPI TEGRA_GPIO_PN5
+
+#define SYNAPTICS_ATTN_GPIO TEGRA_GPIO_PZ3
+#define SYNAPTICS_RESET_GPIO TEGRA_GPIO_PN5
+
+#define KAI_TS_ID1 TEGRA_GPIO_PI7
+#define KAI_TS_ID2 TEGRA_GPIO_PC7
+#define KAI_TS_ID1_PG TEGRA_PINGROUP_GMI_WAIT
+#define KAI_TS_ID2_PG TEGRA_PINGROUP_GMI_WP_N
+
+#define TDIODE_OFFSET (10000) /* in millicelsius */
+
+#endif
diff --git a/arch/arm/mach-tegra/board-touch-kai-raydium_spi.c b/arch/arm/mach-tegra/board-touch-kai-raydium_spi.c
new file mode 100644
index 000000000000..d2c5d7e3334c
--- /dev/null
+++ b/arch/arm/mach-tegra/board-touch-kai-raydium_spi.c
@@ -0,0 +1,67 @@
+/*
+ * arch/arm/mach-tegra/board-touch-raydium_spi.c
+ *
+ * Copyright (c) 2011, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/platform_data/rm31080a_ts.h>
+
+#if defined(CONFIG_MACH_KAI)
+#include "board-kai.h"
+#endif
+
+/* Raydium touchscreen Driver data */
+/*-----------------------------------------------------*/
+
+struct rm_spi_ts_platform_data rm31080ts_data = {
+ .gpio_reset = TOUCH_GPIO_RST_RAYDIUM_SPI,
+};
+
+struct spi_board_info rm31080a_spi_board[] = {
+ {
+ .modalias = "rm_ts_spidev",
+ .bus_num = 0,
+ .chip_select = 0,
+ .irq = TEGRA_GPIO_TO_IRQ(TOUCH_GPIO_IRQ_RAYDIUM_SPI),
+ .max_speed_hz = 18*1000*1000,
+ .mode = SPI_MODE_0,
+ .platform_data = &rm31080ts_data,
+ },
+};
+
+int __init touch_init_raydium(void)
+{
+ tegra_gpio_enable(TOUCH_GPIO_IRQ_RAYDIUM_SPI);
+ gpio_request(TOUCH_GPIO_IRQ_RAYDIUM_SPI, "raydium-irq");
+ gpio_direction_input(TOUCH_GPIO_IRQ_RAYDIUM_SPI);
+
+ tegra_gpio_enable(TOUCH_GPIO_RST_RAYDIUM_SPI);
+ gpio_request(TOUCH_GPIO_RST_RAYDIUM_SPI, "raydium-reset");
+ gpio_direction_output(TOUCH_GPIO_RST_RAYDIUM_SPI, 0);
+
+ msleep(1);
+ gpio_set_value(TOUCH_GPIO_RST_RAYDIUM_SPI, 1);
+ msleep(100);
+
+ spi_register_board_info(rm31080a_spi_board,
+ ARRAY_SIZE(rm31080a_spi_board));
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-touch-kai-synaptics-spi.c b/arch/arm/mach-tegra/board-touch-kai-synaptics-spi.c
new file mode 100644
index 000000000000..b4052c277c3a
--- /dev/null
+++ b/arch/arm/mach-tegra/board-touch-kai-synaptics-spi.c
@@ -0,0 +1,103 @@
+/*
+ * arch/arm/mach-tegra/board-touch-synaptics-spi.c
+ *
+ * Copyright (C) 2010-2011 NVIDIA Corporation
+ *
+ * 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/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/spi/spi.h>
+#include <linux/rmi.h>
+#include "board.h"
+#include "board-kai.h"
+
+#define SYNAPTICS_SPI_CS 0
+#define SYNAPTICS_BUTTON_CODES {KEY_HOME, KEY_BACK,}
+
+static unsigned char synaptics_button_codes[] = SYNAPTICS_BUTTON_CODES;
+
+static struct rmi_f19_button_map synaptics_button_map = {
+ .nbuttons = ARRAY_SIZE(synaptics_button_codes),
+ .map = synaptics_button_codes,
+};
+
+static int synaptics_touchpad_gpio_setup(void)
+{
+ tegra_gpio_enable(SYNAPTICS_ATTN_GPIO);
+ gpio_request(SYNAPTICS_ATTN_GPIO, "synaptics-irq");
+ gpio_direction_input(SYNAPTICS_ATTN_GPIO);
+
+ tegra_gpio_enable(SYNAPTICS_RESET_GPIO);
+ gpio_request(SYNAPTICS_RESET_GPIO, "synaptics-reset");
+ gpio_direction_output(SYNAPTICS_RESET_GPIO, 0);
+
+ msleep(1);
+ gpio_set_value(SYNAPTICS_RESET_GPIO, 1);
+ msleep(100);
+
+ return 0;
+}
+
+static struct rmi_device_platform_data synaptics_platformdata = {
+ .driver_name = "rmi_generic",
+ .irq = SYNAPTICS_ATTN_GPIO,
+ .irq_polarity = RMI_IRQ_ACTIVE_LOW,
+ .gpio_config = synaptics_touchpad_gpio_setup,
+ .spi_data = {
+ .block_delay_us = 15,
+ .read_delay_us = 15,
+ .write_delay_us = 2,
+ },
+ .axis_align = {
+ .flip_y = true,
+ },
+ .button_map = &synaptics_button_map,
+};
+
+struct spi_board_info synaptics_2002_spi_board[] = {
+ {
+ .modalias = "rmi_spi",
+ .bus_num = 0,
+ .chip_select = 0,
+ .irq = 999, /* just to make sure this one is not being used */
+ .max_speed_hz = 1*1000*1000,
+ .mode = SPI_MODE_3,
+ .platform_data = &synaptics_platformdata,
+ },
+};
+
+int __init touch_init_synaptics_kai(void)
+{
+ pr_info("%s: registering synaptics_2002_spi_board\n", __func__);
+ pr_info(" modalias = %s\n",
+ synaptics_2002_spi_board->modalias);
+ pr_info(" bus_num = %d\n",
+ synaptics_2002_spi_board->bus_num);
+ pr_info(" chip_select = %d\n",
+ synaptics_2002_spi_board->chip_select);
+ pr_info(" irq = %d\n",
+ synaptics_2002_spi_board->irq);
+ pr_info(" max_speed_hz = %d\n",
+ synaptics_2002_spi_board->max_speed_hz);
+ pr_info(" mode = %d\n",
+ synaptics_2002_spi_board->mode);
+
+ msleep(100);
+ spi_register_board_info(synaptics_2002_spi_board,
+ ARRAY_SIZE(synaptics_2002_spi_board));
+ return 0;
+}
diff --git a/arch/arm/mach-tegra/board-ventana-panel.c b/arch/arm/mach-tegra/board-ventana-panel.c
index 5290a3da02ca..e2a54cdb2a68 100644
--- a/arch/arm/mach-tegra/board-ventana-panel.c
+++ b/arch/arm/mach-tegra/board-ventana-panel.c
@@ -352,8 +352,6 @@ struct early_suspend ventana_panel_early_suspender;
static void ventana_panel_early_suspend(struct early_suspend *h)
{
- unsigned i;
-
/* power down LCD, add use a black screen for HDMI */
if (num_registered_fb > 0)
fb_blank(registered_fb[0], FB_BLANK_POWERDOWN);
@@ -362,9 +360,14 @@ static void ventana_panel_early_suspend(struct early_suspend *h)
#ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
cpufreq_save_default_governor();
cpufreq_set_conservative_governor();
- cpufreq_set_conservative_governor_param(
- SET_CONSERVATIVE_GOVERNOR_UP_THRESHOLD,
- SET_CONSERVATIVE_GOVERNOR_DOWN_THRESHOLD);
+ cpufreq_set_conservative_governor_param("up_threshold",
+ SET_CONSERVATIVE_GOVERNOR_UP_THRESHOLD);
+
+ cpufreq_set_conservative_governor_param("down_threshold",
+ SET_CONSERVATIVE_GOVERNOR_DOWN_THRESHOLD);
+
+ cpufreq_set_conservative_governor_param("freq_step",
+ SET_CONSERVATIVE_GOVERNOR_FREQ_STEP);
#endif
}
diff --git a/arch/arm/mach-tegra/board-ventana-sensors.c b/arch/arm/mach-tegra/board-ventana-sensors.c
index 406a427d3424..85987387d15b 100644
--- a/arch/arm/mach-tegra/board-ventana-sensors.c
+++ b/arch/arm/mach-tegra/board-ventana-sensors.c
@@ -116,7 +116,6 @@ static int ventana_ov2710_power_on(void)
{
gpio_direction_output(CAMERA_CSI_MUX_SEL_GPIO, 1);
gpio_direction_output(AVDD_DSI_CSI_ENB_GPIO, 1);
- gpio_direction_output(CAM3_LDO_SHUTDN_L_GPIO, 1);
mdelay(5);
gpio_direction_output(CAM3_PWR_DN_GPIO, 0);
mdelay(5);
@@ -131,7 +130,6 @@ static int ventana_ov2710_power_off(void)
{
gpio_direction_output(CAM3_RST_L_GPIO, 0);
gpio_direction_output(CAM3_PWR_DN_GPIO, 1);
- gpio_direction_output(CAM3_LDO_SHUTDN_L_GPIO, 0);
gpio_direction_output(AVDD_DSI_CSI_ENB_GPIO, 0);
gpio_direction_output(CAMERA_CSI_MUX_SEL_GPIO, 0);
return 0;
@@ -469,15 +467,14 @@ static struct tegra_camera_gpios ventana_camera_gpio_keys[] = {
[7] = TEGRA_CAMERA_GPIO("cam2_pwdn", CAM2_PWR_DN_GPIO, false, 0),
[8] = TEGRA_CAMERA_GPIO("cam2_rst_lo", CAM2_RST_L_GPIO, false, 1),
- [9] = TEGRA_CAMERA_GPIO("cam3_ldo_shdn_lo", CAM3_LDO_SHUTDN_L_GPIO, false, 0),
- [10] = TEGRA_CAMERA_GPIO("cam3_af_pwdn_lo", CAM3_AF_PWR_DN_L_GPIO, false, 0),
- [11] = TEGRA_CAMERA_GPIO("cam3_pwdn", CAM3_PWR_DN_GPIO, false, 0),
- [12] = TEGRA_CAMERA_GPIO("cam3_rst_lo", CAM3_RST_L_GPIO, false, 1),
+ [9] = TEGRA_CAMERA_GPIO("cam3_af_pwdn_lo", CAM3_AF_PWR_DN_L_GPIO, false, 0),
+ [10] = TEGRA_CAMERA_GPIO("cam3_pwdn", CAM3_PWR_DN_GPIO, false, 0),
+ [11] = TEGRA_CAMERA_GPIO("cam3_rst_lo", CAM3_RST_L_GPIO, false, 1),
- [13] = TEGRA_CAMERA_GPIO("cam1_ldo_shdn_lo", CAM1_LDO_SHUTDN_L_GPIO, false, 0),
- [14] = TEGRA_CAMERA_GPIO("cam1_af_pwdn_lo", CAM1_AF_PWR_DN_L_GPIO, false, 0),
- [15] = TEGRA_CAMERA_GPIO("cam1_pwdn", CAM1_PWR_DN_GPIO, false, 0),
- [16] = TEGRA_CAMERA_GPIO("cam1_rst_lo", CAM1_RST_L_GPIO, false, 1),
+ [12] = TEGRA_CAMERA_GPIO("cam1_ldo_shdn_lo", CAM1_LDO_SHUTDN_L_GPIO, false, 0),
+ [13] = TEGRA_CAMERA_GPIO("cam1_af_pwdn_lo", CAM1_AF_PWR_DN_L_GPIO, false, 0),
+ [14] = TEGRA_CAMERA_GPIO("cam1_pwdn", CAM1_PWR_DN_GPIO, false, 0),
+ [15] = TEGRA_CAMERA_GPIO("cam1_rst_lo", CAM1_RST_L_GPIO, false, 1),
};
int __init ventana_camera_late_init(void)
diff --git a/arch/arm/mach-tegra/board-ventana.c b/arch/arm/mach-tegra/board-ventana.c
index 097644021ebc..21278d1f4417 100644
--- a/arch/arm/mach-tegra/board-ventana.c
+++ b/arch/arm/mach-tegra/board-ventana.c
@@ -595,7 +595,7 @@ static void __init tegra_ventana_init(void)
tegra_ehci2_device.dev.platform_data
= &ventana_ehci2_ulpi_platform_data;
platform_add_devices(ventana_devices, ARRAY_SIZE(ventana_devices));
-
+ tegra_ram_console_debug_init();
ventana_sdhci_init();
ventana_charge_init();
ventana_regulator_init();
@@ -644,6 +644,7 @@ void __init tegra_ventana_reserve(void)
pr_warn("Cannot reserve first 4K of memory for safety\n");
tegra_reserve(SZ_256M, SZ_8M + SZ_1M, SZ_16M);
+ tegra_ram_console_debug_reserve(SZ_1M);
}
MACHINE_START(VENTANA, "ventana")
diff --git a/arch/arm/mach-tegra/board-whistler-panel.c b/arch/arm/mach-tegra/board-whistler-panel.c
index e68d6bf40292..9bdde43b8925 100644
--- a/arch/arm/mach-tegra/board-whistler-panel.c
+++ b/arch/arm/mach-tegra/board-whistler-panel.c
@@ -320,9 +320,14 @@ static void whistler_panel_early_suspend(struct early_suspend *h)
#ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
cpufreq_save_default_governor();
cpufreq_set_conservative_governor();
- cpufreq_set_conservative_governor_param(
- SET_CONSERVATIVE_GOVERNOR_UP_THRESHOLD,
- SET_CONSERVATIVE_GOVERNOR_DOWN_THRESHOLD);
+ cpufreq_set_conservative_governor_param("up_threshold",
+ SET_CONSERVATIVE_GOVERNOR_UP_THRESHOLD);
+
+ cpufreq_set_conservative_governor_param("down_threshold",
+ SET_CONSERVATIVE_GOVERNOR_DOWN_THRESHOLD);
+
+ cpufreq_set_conservative_governor_param("freq_step",
+ SET_CONSERVATIVE_GOVERNOR_FREQ_STEP);
#endif
}
diff --git a/arch/arm/mach-tegra/board-whistler-pinmux.c b/arch/arm/mach-tegra/board-whistler-pinmux.c
index 081b4feb531f..22c2f984c662 100644
--- a/arch/arm/mach-tegra/board-whistler-pinmux.c
+++ b/arch/arm/mach-tegra/board-whistler-pinmux.c
@@ -22,6 +22,35 @@
#include "board-whistler.h"
#include "gpio-names.h"
+/* Setting the drive strength of pins
+ * hsm: Enable High speed mode (ENABLE/DISABLE)
+ * Schimit: Enable/disable schimit (ENABLE/DISABLE)
+ * drive: low power mode (DIV_1, DIV_2, DIV_4, DIV_8)
+ * pulldn_drive - drive down (falling edge) - Driver Output Pull-Down drive
+ * strength code. Value from 0 to 31.
+ * pullup_drive - drive up (rising edge) - Driver Output Pull-Up drive
+ * strength code. Value from 0 to 31.
+ * pulldn_slew - Driver Output Pull-Up slew control code - 2bit code
+ * code 11 is least slewing of signal. code 00 is highest
+ * slewing of the signal.
+ * Value - FASTEST, FAST, SLOW, SLOWEST
+ * pullup_slew - Driver Output Pull-Down slew control code -
+ * code 11 is least slewing of signal. code 00 is highest
+ * slewing of the signal.
+ * Value - FASTEST, FAST, SLOW, SLOWEST
+ */
+#define SET_DRIVE(_name, _hsm, _schmitt, _drive, _pulldn_drive, _pullup_drive, _pulldn_slew, _pullup_slew) \
+ { \
+ .pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
+ .hsm = TEGRA_HSM_##_hsm, \
+ .schmitt = TEGRA_SCHMITT_##_schmitt, \
+ .drive = TEGRA_DRIVE_##_drive, \
+ .pull_down = TEGRA_PULL_##_pulldn_drive, \
+ .pull_up = TEGRA_PULL_##_pullup_drive, \
+ .slew_rising = TEGRA_SLEW_##_pulldn_slew, \
+ .slew_falling = TEGRA_SLEW_##_pullup_slew, \
+ }
+
#define DEFAULT_DRIVE(_name) \
{ \
.pingroup = TEGRA_DRIVE_PINGROUP_##_name, \
@@ -34,13 +63,14 @@
.slew_falling = TEGRA_SLEW_SLOWEST, \
}
-
static __initdata struct tegra_drive_pingroup_config whistler_drive_pinmux[] = {
DEFAULT_DRIVE(DBG),
DEFAULT_DRIVE(DDC),
DEFAULT_DRIVE(VI1),
DEFAULT_DRIVE(VI2),
DEFAULT_DRIVE(SDIO1),
+ SET_DRIVE(DAP2, DISABLE, ENABLE, DIV_1, 46, 46, SLOWEST, SLOWEST),
+ SET_DRIVE(DAP3, DISABLE, ENABLE, DIV_1, 46, 46, SLOWEST, SLOWEST),
};
static __initdata struct tegra_pingroup_config whistler_pinmux[] = {
diff --git a/arch/arm/mach-tegra/board-whistler.c b/arch/arm/mach-tegra/board-whistler.c
index 031ab936d870..0a5ad7161d2d 100644
--- a/arch/arm/mach-tegra/board-whistler.c
+++ b/arch/arm/mach-tegra/board-whistler.c
@@ -573,7 +573,7 @@ static void __init tegra_whistler_init(void)
whistler_i2c_init();
whistler_uart_init();
platform_add_devices(whistler_devices, ARRAY_SIZE(whistler_devices));
-
+ tegra_ram_console_debug_init();
whistler_sdhci_init();
whistler_regulator_init();
whistler_panel_init();
@@ -603,6 +603,7 @@ void __init tegra_whistler_reserve(void)
pr_warn("Cannot reserve first 4K of memory for safety\n");
tegra_reserve(SZ_160M, SZ_8M, SZ_16M);
+ tegra_ram_console_debug_reserve(SZ_1M);
}
MACHINE_START(WHISTLER, "whistler")
diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h
index 950ccdf21afe..fc22b76160f9 100644
--- a/arch/arm/mach-tegra/board.h
+++ b/arch/arm/mach-tegra/board.h
@@ -102,13 +102,14 @@ void tegra_get_pmu_board_info(struct board_info *bi);
void tegra_get_display_board_info(struct board_info *bi);
void tegra_get_camera_board_info(struct board_info *bi);
#ifdef CONFIG_TEGRA_CONVSERVATIVE_GOV_ON_EARLYSUPSEND
-#define SET_CONSERVATIVE_GOVERNOR_UP_THRESHOLD 95
-#define SET_CONSERVATIVE_GOVERNOR_DOWN_THRESHOLD 50
+#define SET_CONSERVATIVE_GOVERNOR_UP_THRESHOLD 95
+#define SET_CONSERVATIVE_GOVERNOR_DOWN_THRESHOLD 50
+#define SET_CONSERVATIVE_GOVERNOR_FREQ_STEP 3
void cpufreq_save_default_governor(void);
void cpufreq_restore_default_governor(void);
void cpufreq_set_conservative_governor(void);
-void cpufreq_set_conservative_governor_param(int up_th, int down_th);
+void cpufreq_set_conservative_governor_param(char *name, int value);
#endif
int get_core_edp(void);
enum panel_type get_panel_type(void);
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index 41a4d3f35b4c..b47821e12274 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -6,7 +6,7 @@
* Author:
* Colin Cross <ccross@google.com>
*
- * Copyright (C) 2010-2011, NVIDIA Corporation.
+ * Copyright (C) 2010-2012, NVIDIA Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -170,6 +170,9 @@ struct clk {
unsigned long fixed_rate;
} pll;
struct {
+ unsigned long default_rate;
+ } pll_div;
+ struct {
u32 sel;
u32 reg_mask;
} mux;
@@ -295,6 +298,12 @@ struct tegra_cpufreq_table_data {
};
struct tegra_cpufreq_table_data *tegra_cpufreq_table_get(void);
unsigned long tegra_emc_to_cpu_ratio(unsigned long cpu_rate);
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+static inline int tegra_update_mselect_rate(unsigned long cpu_rate)
+{ return 0; }
+#else
+int tegra_update_mselect_rate(unsigned long cpu_rate);
+#endif
#endif
#endif
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index 2acf4bdcef94..b5c362260dd1 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -2,7 +2,7 @@
* arch/arm/mach-tegra/common.c
*
* Copyright (C) 2010 Google, Inc.
- * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (C) 2010-2012 NVIDIA Corporation
*
* Author:
* Colin Cross <ccross@android.com>
@@ -133,9 +133,9 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
{ "pll_p", NULL, 216000000, true },
{ "pll_p_out1", "pll_p", 28800000, true },
- { "pll_p_out2", "pll_p", 48000000, true },
+ { "pll_p_out2", "pll_p", 48000000, false },
{ "pll_p_out3", "pll_p", 72000000, true },
- { "pll_p_out4", "pll_p", 108000000, true },
+ { "pll_p_out4", "pll_p", 108000000, false },
{ "pll_m", "clk_m", 0, true },
{ "pll_m_out1", "pll_m", 120000000, true },
{ "sclk", "pll_c_out1", 40000000, true },
@@ -148,15 +148,16 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
{ "2d", "pll_c", 0, false },
{ "3d", "pll_c", 0, false },
#else
- { "pll_p", NULL, 408000000, true },
- { "pll_p_out1", "pll_p", 9600000, false },
+ { "pll_p", NULL, 0, true },
+ { "pll_p_out1", "pll_p", 0, false },
{ "pll_p_out2", "pll_p", 48000000, false },
- { "pll_p_out3", "pll_p", 102000000, true },
+ { "pll_p_out3", "pll_p", 0, true },
{ "pll_m_out1", "pll_m", 275000000, false },
{ "pll_p_out4", "pll_p", 102000000, false },
{ "sclk", "pll_p_out4", 102000000, true },
{ "hclk", "sclk", 102000000, true },
{ "pclk", "hclk", 51000000, true },
+ { "wake.sclk", NULL, 40000000, true },
#endif
#else
{ "pll_p", NULL, 216000000, true },
@@ -180,9 +181,12 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
{ "sdmmc1", "pll_p", 48000000, false},
{ "sdmmc3", "pll_p", 48000000, false},
{ "sdmmc4", "pll_p", 48000000, false},
+ { "pll_a", "pll_p_out1", 0, false},
+ { "pll_a_out0", "pll_a", 0, false},
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
{ "cbus", "pll_c", 416000000, false },
{ "pll_c_out1", "pll_c", 208000000, false },
+ { "mselect", "pll_p", 102000000, true },
#endif
{ NULL, NULL, 0, 0},
};
@@ -961,7 +965,7 @@ static void cpufreq_set_governor(char *governor)
struct file *scaling_gov = NULL;
mm_segment_t old_fs;
char buf[128];
- int i = 0;
+ int i;
loff_t offset = 0;
if (governor == NULL)
@@ -976,9 +980,7 @@ static void cpufreq_set_governor(char *governor)
{
sprintf(buf, cpufreq_sysfs_place_holder, i);
scaling_gov = filp_open(buf, O_RDWR, 0);
- if (IS_ERR_OR_NULL(scaling_gov)) {
- pr_err("%s. Can't open %s\n", __func__, buf);
- } else {
+ if (scaling_gov != NULL) {
if (scaling_gov->f_op != NULL &&
scaling_gov->f_op->write != NULL)
scaling_gov->f_op->write(scaling_gov,
@@ -989,6 +991,8 @@ static void cpufreq_set_governor(char *governor)
pr_err("f_op might be null\n");
filp_close(scaling_gov, NULL);
+ } else {
+ pr_err("%s. Can't open %s\n", __func__, buf);
}
}
set_fs(old_fs);
@@ -1008,9 +1012,7 @@ void cpufreq_save_default_governor(void)
buf[127] = 0;
sprintf(buf, cpufreq_sysfs_place_holder,0);
scaling_gov = filp_open(buf, O_RDONLY, 0);
- if (IS_ERR_OR_NULL(scaling_gov)) {
- pr_err("%s. Can't open %s\n", __func__, buf);
- } else {
+ if (scaling_gov != NULL) {
if (scaling_gov->f_op != NULL &&
scaling_gov->f_op->read != NULL)
scaling_gov->f_op->read(scaling_gov,
@@ -1021,6 +1023,8 @@ void cpufreq_save_default_governor(void)
pr_err("f_op might be null\n");
filp_close(scaling_gov, NULL);
+ } else {
+ pr_err("%s. Can't open %s\n", __func__, buf);
}
set_fs(old_fs);
}
@@ -1030,57 +1034,33 @@ void cpufreq_restore_default_governor(void)
cpufreq_set_governor(cpufreq_gov_default);
}
-void cpufreq_set_conservative_governor_param(int up_th, int down_th)
+void cpufreq_set_conservative_governor_param(char *name, int value)
{
struct file *gov_param = NULL;
- static char buf[128],parm[8];
- loff_t offset = 0;
mm_segment_t old_fs;
-
- if (up_th <= down_th) {
- printk(KERN_ERR "%s: up_th(%d) is lesser than down_th(%d)\n",
- __func__, up_th, down_th);
- return;
- }
+ static char buf[128], param_value[8];
+ loff_t offset = 0;
/* change to KERNEL_DS address limit */
old_fs = get_fs();
set_fs(KERNEL_DS);
- sprintf(parm, "%d", up_th);
- sprintf(buf, cpufreq_gov_conservative_param ,"up_threshold");
- gov_param = filp_open(buf, O_RDONLY, 0);
- if (IS_ERR_OR_NULL(gov_param)) {
- pr_err("%s. Can't open %s\n", __func__, buf);
- } else {
+ sprintf(param_value, "%d", value);
+ sprintf(buf, cpufreq_gov_conservative_param, name);
+ gov_param = filp_open(buf, O_RDWR, 0);
+ if (gov_param != NULL) {
if (gov_param->f_op != NULL &&
gov_param->f_op->write != NULL)
gov_param->f_op->write(gov_param,
- parm,
- strlen(parm),
+ param_value,
+ strlen(param_value),
&offset);
else
pr_err("f_op might be null\n");
filp_close(gov_param, NULL);
- }
-
- sprintf(parm, "%d", down_th);
- sprintf(buf, cpufreq_gov_conservative_param ,"down_threshold");
- gov_param = filp_open(buf, O_RDONLY, 0);
- if (IS_ERR_OR_NULL(gov_param)) {
- pr_err("%s. Can't open %s\n", __func__, buf);
} else {
- if (gov_param->f_op != NULL &&
- gov_param->f_op->write != NULL)
- gov_param->f_op->write(gov_param,
- parm,
- strlen(parm),
- &offset);
- else
- pr_err("f_op might be null\n");
-
- filp_close(gov_param, NULL);
+ pr_err("%s. Can't open %s\n", __func__, buf);
}
set_fs(old_fs);
}
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index 24ed5d229a5d..de290ce65804 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -473,8 +473,20 @@ static int tegra_update_cpu_speed(unsigned long rate)
* Vote on memory bus frequency based on cpu frequency
* This sets the minimum frequency, display or avp may request higher
*/
- if (freqs.old < freqs.new)
- clk_set_rate(emc_clk, tegra_emc_to_cpu_ratio(freqs.new));
+ if (freqs.old < freqs.new) {
+ ret = tegra_update_mselect_rate(freqs.new);
+ if (ret) {
+ pr_err("cpu-tegra: Failed to scale mselect for cpu"
+ " frequency %u kHz\n", freqs.new);
+ return ret;
+ }
+ ret = clk_set_rate(emc_clk, tegra_emc_to_cpu_ratio(freqs.new));
+ if (ret) {
+ pr_err("cpu-tegra: Failed to scale emc for cpu"
+ " frequency %u kHz\n", freqs.new);
+ return ret;
+ }
+ }
for_each_online_cpu(freqs.cpu)
cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
@@ -494,8 +506,10 @@ static int tegra_update_cpu_speed(unsigned long rate)
for_each_online_cpu(freqs.cpu)
cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
- if (freqs.old > freqs.new)
+ if (freqs.old > freqs.new) {
clk_set_rate(emc_clk, tegra_emc_to_cpu_ratio(freqs.new));
+ tegra_update_mselect_rate(freqs.new);
+ }
return 0;
}
diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c
index bb10c1089533..39bc04bd2e39 100644
--- a/arch/arm/mach-tegra/devices.c
+++ b/arch/arm/mach-tegra/devices.c
@@ -27,6 +27,7 @@
#include <linux/platform_data/tegra_usb.h>
#include <linux/tegra_avp.h>
#include <linux/nvhost.h>
+#include <linux/clk.h>
#include <asm/pmu.h>
#include <mach/irqs.h>
#include <mach/iomap.h>
@@ -1324,7 +1325,7 @@ struct platform_device tegra_das_device = {
};
#endif
-#if defined(CONFIG_TEGRA_IOVMM_GART)
+#if defined(CONFIG_TEGRA_IOVMM_GART) || defined(CONFIG_TEGRA_IOMMU_GART)
static struct resource tegra_gart_resources[] = {
[0] = {
.name = "mc",
@@ -1733,3 +1734,20 @@ struct platform_device tegra_nvmap_device = {
.name = "tegra-nvmap",
.id = -1,
};
+
+void tegra_init_debug_uart_rate(void)
+{
+ unsigned int uartclk;
+ struct clk *debug_uart_parent = clk_get_sys(NULL, "pll_p");
+
+ BUG_ON(IS_ERR(debug_uart_parent));
+ uartclk = clk_get_rate(debug_uart_parent);
+
+ debug_uarta_platform_data[0].uartclk = uartclk;
+ debug_uartb_platform_data[0].uartclk = uartclk;
+ debug_uartc_platform_data[0].uartclk = uartclk;
+ debug_uartd_platform_data[0].uartclk = uartclk;
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ debug_uarte_platform_data[0].uartclk = uartclk;
+#endif
+}
diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h
index 6d7c7cab3cdf..ff93216ce292 100644
--- a/arch/arm/mach-tegra/devices.h
+++ b/arch/arm/mach-tegra/devices.h
@@ -5,7 +5,7 @@
* Colin Cross <ccross@android.com>
* Erik Gilling <ccross@android.com>
*
- * Copyright (C) 2010-2011 NVIDIA Corporation.
+ * Copyright (C) 2010-2012 NVIDIA Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -126,4 +126,7 @@ extern struct platform_device debug_uarte_device;
extern struct nvhost_device tegra_disp1_device;
extern struct platform_device tegra_nvmap_device;
+
+void tegra_init_debug_uart_rate(void);
+
#endif
diff --git a/arch/arm/mach-tegra/include/mach/dc.h b/arch/arm/mach-tegra/include/mach/dc.h
index caca9d84c1be..4137a8f11017 100644
--- a/arch/arm/mach-tegra/include/mach/dc.h
+++ b/arch/arm/mach-tegra/include/mach/dc.h
@@ -324,6 +324,7 @@ struct tegra_dc_out {
int dcc_bus;
int hotplug_gpio;
const char *parent_clk;
+ const char *parent_clk_backup;
unsigned max_pixclock;
unsigned order;
diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h
index 9665858ab11d..4a8ac6b0b68b 100644
--- a/arch/arm/mach-tegra/include/mach/uncompress.h
+++ b/arch/arm/mach-tegra/include/mach/uncompress.h
@@ -7,7 +7,7 @@
* Colin Cross <ccross@google.com>
* Erik Gilling <konkers@google.com>
*
- * Copyright (C) 2010-2011 NVIDIA Corporation
+ * Copyright (C) 2010-2012 NVIDIA Corporation
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -65,12 +65,16 @@
#define DEBUG_UART_RST_CLR_REG 0
#define DEBUG_UART_RST_CLR_BIT 0
#endif
+#define PLLP_BASE (TEGRA_CLK_RESET_BASE + 0x0a0)
+#define PLLP_BASE_OVERRIDE (1 << 28)
+#define PLLP_BASE_DIVP_SHIFT 20
+#define PLLP_BASE_DIVP_MASK (0x7 << 20)
+#define PLLP_BASE_DIVN_SHIFT 8
+#define PLLP_BASE_DIVN_MASK (0x3FF << 8)
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
-#define DEBUG_UART_DLL 0x75
-#else
-#define DEBUG_UART_DLL 0xdd
-#endif
+#define DEBUG_UART_DLL_216 0x75
+#define DEBUG_UART_DLL_408 0xdd
+#define DEBUG_UART_DLL_204 0x6f
static void putc(int c)
{
@@ -104,6 +108,8 @@ static inline void arch_decomp_setup(void)
volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
int shift = 2;
volatile u32 *addr;
+ u8 uart_dll = DEBUG_UART_DLL_216;
+ u32 val;
if (uart == NULL)
return;
@@ -124,9 +130,37 @@ static inline void arch_decomp_setup(void)
konk_delay(5);
+ /*
+ * On Tegra2 platforms PLLP always run at 216MHz
+ * On Tegra3 platforms PLLP can run at 216MHz, 204MHz, or 408MHz
+ * Discrimantion algorithm below assumes that PLLP is configured
+ * according to h/w recomendations with update rate 1MHz or 1.2MHz
+ * depending on oscillator frequency
+ */
+ addr = (volatile u32 *)PLLP_BASE;
+ val = *addr;
+ if (val & PLLP_BASE_OVERRIDE) {
+ u32 p = (val & PLLP_BASE_DIVP_MASK) >> PLLP_BASE_DIVP_SHIFT;
+ val = (val & PLLP_BASE_DIVN_MASK) >> (PLLP_BASE_DIVN_SHIFT + p);
+ switch (val) {
+ case 170:
+ case 204:
+ uart_dll = DEBUG_UART_DLL_204;
+ break;
+ case 340:
+ case 408:
+ uart_dll = DEBUG_UART_DLL_408;
+ break;
+ case 180:
+ case 216:
+ default:
+ break;
+ }
+ }
+
/* Set up debug UART. */
uart[UART_LCR << shift] |= UART_LCR_DLAB;
- uart[UART_DLL << shift] = DEBUG_UART_DLL;
+ uart[UART_DLL << shift] = uart_dll;
uart[UART_DLM << shift] = 0x0;
uart[UART_LCR << shift] = 3;
}
diff --git a/arch/arm/mach-tegra/latency_allowance.c b/arch/arm/mach-tegra/latency_allowance.c
index ab2459bc4ca8..896e1e9ca24e 100644
--- a/arch/arm/mach-tegra/latency_allowance.c
+++ b/arch/arm/mach-tegra/latency_allowance.c
@@ -44,7 +44,7 @@
#define MC_LA_EPP_0 0x300
#define MC_LA_EPP_1 0x304
#define MC_LA_G2_0 0x308
-#define MC_LA_G2_1 0x304
+#define MC_LA_G2_1 0x30c
#define MC_LA_HC_0 0x310
#define MC_LA_HC_1 0x314
#define MC_LA_HDA_0 0x318
diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c
index c665220f5516..780fe2a5ac70 100644
--- a/arch/arm/mach-tegra/pcie.c
+++ b/arch/arm/mach-tegra/pcie.c
@@ -587,7 +587,7 @@ static int tegra_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
return INT_PCIE_INTR;
}
-static struct pci_bus __init *tegra_pcie_scan_bus(int nr,
+static struct pci_bus *tegra_pcie_scan_bus(int nr,
struct pci_sys_data *sys)
{
struct tegra_pcie_port *pp;
@@ -601,7 +601,7 @@ static struct pci_bus __init *tegra_pcie_scan_bus(int nr,
return pci_scan_bus(sys->busnr, &tegra_pcie_ops, sys);
}
-static struct hw_pci tegra_pcie_hw __initdata = {
+static struct hw_pci tegra_pcie_hw = {
.nr_controllers = MAX_PCIE_SUPPORTED_PORTS,
.setup = tegra_pcie_setup,
.scan = tegra_pcie_scan_bus,
diff --git a/arch/arm/mach-tegra/pm-t3.c b/arch/arm/mach-tegra/pm-t3.c
index 2de6f8770ba8..48f27cf01a0a 100644
--- a/arch/arm/mach-tegra/pm-t3.c
+++ b/arch/arm/mach-tegra/pm-t3.c
@@ -440,10 +440,7 @@ void tegra_lp0_cpu_mode(bool enter)
#define PMC_DPD_SAMPLE 0x20
struct tegra_io_dpd tegra_list_io_dpd[] = {
- /* sd dpd bits in dpd2 register */
- IO_DPD_INFO("sdhci-tegra.0", 1, 1), /* SDMMC1 */
- IO_DPD_INFO("sdhci-tegra.2", 1, 2), /* SDMMC3 */
- IO_DPD_INFO("sdhci-tegra.3", 1, 3), /* SDMMC4 */
+/* Empty DPD list - sd dpd entries removed */
};
struct tegra_io_dpd *tegra_io_dpd_get(struct device *dev)
@@ -474,7 +471,7 @@ void tegra_io_dpd_enable(struct tegra_io_dpd *hnd)
unsigned int dpd_status;
unsigned int dpd_enable_lsb;
- if (WARN_ON(!hnd))
+ if ((!hnd))
return;
spin_lock(&tegra_io_dpd_lock);
dpd_enable_lsb = (hnd->io_dpd_reg_index) ? APBDEV_DPD2_ENABLE_LSB :
@@ -503,7 +500,7 @@ void tegra_io_dpd_disable(struct tegra_io_dpd *hnd)
unsigned int dpd_status;
unsigned int dpd_enable_lsb;
- if (WARN_ON(!hnd))
+ if ((!hnd))
return;
spin_lock(&tegra_io_dpd_lock);
dpd_enable_lsb = (hnd->io_dpd_reg_index) ? APBDEV_DPD2_ENABLE_LSB :
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index edcb28304d41..61a93eead6da 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -33,6 +33,7 @@
#include <linux/debugfs.h>
#include <linux/delay.h>
#include <linux/suspend.h>
+#include <linux/earlysuspend.h>
#include <linux/slab.h>
#include <linux/serial_reg.h>
#include <linux/seq_file.h>
@@ -41,6 +42,7 @@
#include <linux/vmalloc.h>
#include <linux/memblock.h>
#include <linux/console.h>
+#include <linux/pm_qos_params.h>
#include <asm/cacheflush.h>
#include <asm/cpu_pm.h>
@@ -167,19 +169,15 @@ struct suspend_context tegra_sctx;
#define MC_SECURITY_SIZE 0x70
#define MC_SECURITY_CFG2 0x7c
+#define AWAKE_CPU_FREQ_MIN 100000
+static struct pm_qos_request_list awake_cpu_freq_req;
+
struct dvfs_rail *tegra_cpu_rail;
static struct dvfs_rail *tegra_core_rail;
static struct clk *tegra_pclk;
static const struct tegra_suspend_platform_data *pdata;
static enum tegra_suspend_mode current_suspend_mode = TEGRA_SUSPEND_NONE;
-static const char *tegra_suspend_name[TEGRA_MAX_SUSPEND_MODE] = {
- [TEGRA_SUSPEND_NONE] = "none",
- [TEGRA_SUSPEND_LP2] = "lp2",
- [TEGRA_SUSPEND_LP1] = "lp1",
- [TEGRA_SUSPEND_LP0] = "lp0",
-};
-
#if defined(CONFIG_TEGRA_CLUSTER_CONTROL) && INSTRUMENT_CLUSTER_SWITCH
enum tegra_cluster_switch_time_id {
tegra_cluster_switch_time_id_start = 0,
@@ -209,6 +207,13 @@ static unsigned long
#endif
#ifdef CONFIG_PM_SLEEP
+static const char *tegra_suspend_name[TEGRA_MAX_SUSPEND_MODE] = {
+ [TEGRA_SUSPEND_NONE] = "none",
+ [TEGRA_SUSPEND_LP2] = "lp2",
+ [TEGRA_SUSPEND_LP1] = "lp1",
+ [TEGRA_SUSPEND_LP0] = "lp0",
+};
+
unsigned long tegra_cpu_power_good_time(void)
{
if (WARN_ON_ONCE(!pdata))
@@ -519,7 +524,7 @@ bool tegra_set_cpu_in_lp2(int cpu)
if ((cpu == 0) && cpumask_equal(&tegra_in_lp2, cpu_online_mask))
last_cpu = true;
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
- else
+ else if (cpu == 1)
tegra2_cpu_set_resettable_soon();
#endif
@@ -763,6 +768,10 @@ static int tegra_suspend_enter(suspend_state_t state)
read_persistent_clock(&ts_entry);
ret = tegra_suspend_dram(current_suspend_mode, 0);
+ if (ret) {
+ pr_info("Aborting suspend, tegra_suspend_dram error=%d\n", ret);
+ goto abort_suspend;
+ }
read_persistent_clock(&ts_exit);
@@ -776,6 +785,7 @@ static int tegra_suspend_enter(suspend_state_t state)
tegra_dvfs_rail_pause(tegra_core_rail, delta, true);
}
+abort_suspend:
if (pdata && pdata->board_resume)
pdata->board_resume(current_suspend_mode, TEGRA_RESUME_AFTER_PERIPHERAL);
@@ -810,7 +820,13 @@ static void tegra_suspend_check_pwr_stats(void)
int tegra_suspend_dram(enum tegra_suspend_mode mode, unsigned int flags)
{
- BUG_ON(mode < 0 || mode >= TEGRA_MAX_SUSPEND_MODE);
+ int err = 0;
+
+ if (WARN_ON(mode <= TEGRA_SUSPEND_NONE ||
+ mode >= TEGRA_MAX_SUSPEND_MODE)) {
+ err = -ENXIO;
+ goto fail;
+ }
if ((mode == TEGRA_SUSPEND_LP0) && !tegra_pm_irq_lp0_allowed()) {
pr_info("LP0 not used due to unsupported wakeup events\n");
@@ -892,7 +908,8 @@ int tegra_suspend_dram(enum tegra_suspend_mode mode, unsigned int flags)
tegra_common_resume();
- return 0;
+fail:
+ return err;
}
/*
@@ -950,6 +967,11 @@ static ssize_t suspend_mode_store(struct kobject *kobj,
len = name_ptr - buf;
if (!len)
goto bad_name;
+ /* TEGRA_SUSPEND_NONE not allowed as suspend state */
+ if (!(strncmp(buf, tegra_suspend_name[TEGRA_SUSPEND_NONE], len))) {
+ pr_info("Illegal tegra suspend state: %s\n", buf);
+ goto bad_name;
+ }
for (new_mode = TEGRA_SUSPEND_NONE; \
new_mode < TEGRA_MAX_SUSPEND_MODE; ++new_mode) {
@@ -967,9 +989,7 @@ static struct kobj_attribute suspend_mode_attribute =
__ATTR(mode, 0644, suspend_mode_show, suspend_mode_store);
static struct kobject *suspend_kobj;
-#endif
-#ifdef CONFIG_PM_SLEEP
static int tegra_pm_enter_suspend(void)
{
pr_info("Entering suspend state %s\n", lp_state[current_suspend_mode]);
@@ -1005,6 +1025,9 @@ void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat)
tegra_cpu_rail = tegra_dvfs_get_rail_by_name("vdd_cpu");
tegra_core_rail = tegra_dvfs_get_rail_by_name("vdd_core");
+ pm_qos_add_request(&awake_cpu_freq_req, PM_QOS_CPU_FREQ_MIN,
+ AWAKE_CPU_FREQ_MIN);
+
tegra_pclk = clk_get_sys(NULL, "pclk");
BUG_ON(IS_ERR(tegra_pclk));
pdata = plat;
@@ -1242,3 +1265,35 @@ static int tegra_debug_uart_syscore_init(void)
return 0;
}
arch_initcall(tegra_debug_uart_syscore_init);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static struct clk *clk_wake;
+
+static void pm_early_suspend(struct early_suspend *h)
+{
+ if (clk_wake)
+ clk_disable(clk_wake);
+ pm_qos_update_request(&awake_cpu_freq_req, PM_QOS_DEFAULT_VALUE);
+}
+
+static void pm_late_resume(struct early_suspend *h)
+{
+ if (clk_wake)
+ clk_enable(clk_wake);
+ pm_qos_update_request(&awake_cpu_freq_req, (s32)AWAKE_CPU_FREQ_MIN);
+}
+
+static struct early_suspend pm_early_suspender = {
+ .suspend = pm_early_suspend,
+ .resume = pm_late_resume,
+};
+
+static int pm_init_wake_behavior(void)
+{
+ clk_wake = tegra_get_clock_by_name("wake.sclk");
+ register_early_suspend(&pm_early_suspender);
+ return 0;
+}
+
+late_initcall(pm_init_wake_behavior);
+#endif
diff --git a/arch/arm/mach-tegra/tegra3_actmon.c b/arch/arm/mach-tegra/tegra3_actmon.c
index 05cdc1f86465..c7aeb9acdd12 100644
--- a/arch/arm/mach-tegra/tegra3_actmon.c
+++ b/arch/arm/mach-tegra/tegra3_actmon.c
@@ -666,9 +666,6 @@ static int down_threshold_set(void *data, u64 val)
struct actmon_dev *dev = data;
unsigned int down_threshold = (unsigned int)val;
- if (down_threshold < 0)
- down_threshold = 0;
-
spin_lock_irqsave(&dev->lock, flags);
if (down_threshold >= dev->boost_up_threshold)
diff --git a/arch/arm/mach-tegra/tegra3_clocks.c b/arch/arm/mach-tegra/tegra3_clocks.c
index 3fe531fe2e5b..1298540c7d91 100644
--- a/arch/arm/mach-tegra/tegra3_clocks.c
+++ b/arch/arm/mach-tegra/tegra3_clocks.c
@@ -1,7 +1,7 @@
/*
* arch/arm/mach-tegra/tegra3_clocks.c
*
- * Copyright (C) 2010-2011 NVIDIA Corporation
+ * Copyright (C) 2010-2012 NVIDIA Corporation
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -305,44 +305,17 @@
#define ROUND_DIVIDER_UP 0
#define ROUND_DIVIDER_DOWN 1
-#ifdef CONFIG_TEGRA_SILICON_PLATFORM
-#define PLLP_FIXED_RATE 408000000
-#else
-#define PLLP_FIXED_RATE 216000000
-#endif
-
-/* sbus threshold must be exact factor of pll_p */
-#define SBUS_THRESHOLD_RATE (PLLP_FIXED_RATE / 2)
-
-/*
- * Backup rate targets for each CPU mode is selected below Fmax(Vmin), and
- * high enough to avoid voltage droop when CPU clock is switched between
- * backup and main clock sources. Actual backup rates will be rounded based
- * on backup source fixed frequency. Maximum stay-on-backup rate will be set
- * as a minimum of G and LP backup rates to be supported in both modes.
- */
-#define CPU_G_BACKUP_RATE_TARGET 440000000
-#define CPU_G_BACKUP_RATE_DIV \
- DIV_ROUND_UP(PLLP_FIXED_RATE, CPU_G_BACKUP_RATE_TARGET)
-#define CPU_G_BACKUP_RATE \
- (PLLP_FIXED_RATE / CPU_G_BACKUP_RATE_DIV)
-
-#define CPU_LP_BACKUP_RATE_TARGET 220000000
-#define CPU_LP_BACKUP_RATE_DIV \
- DIV_ROUND_UP(PLLP_FIXED_RATE, CPU_LP_BACKUP_RATE_TARGET)
-#define CPU_LP_BACKUP_RATE \
- (PLLP_FIXED_RATE / CPU_LP_BACKUP_RATE_DIV)
-
-#define CPU_STAY_ON_BACKUP_MAX \
- min(CPU_G_BACKUP_RATE, CPU_LP_BACKUP_RATE)
+/* PLLP default fixed rate in h/w controlled mode */
+#define PLLP_DEFAULT_FIXED_RATE 216000000
/* Threshold to engage CPU clock skipper during CPU rate change */
#define SKIPPER_ENGAGE_RATE 800000000
static bool tegra3_clk_is_parent_allowed(struct clk *c, struct clk *p);
-
+static void tegra3_pllp_init_dependencies(unsigned long pllp_rate);
static int tegra3_clk_shared_bus_update(struct clk *bus);
+static unsigned long cpu_stay_on_backup_max;
static struct clk *emc_bridge;
static bool detach_shared_bus;
@@ -391,8 +364,10 @@ static void __iomem *misc_gp_hidrev_base = IO_ADDRESS(TEGRA_APB_MISC_BASE);
/*
* Some peripheral clocks share an enable bit, so refcount the enable bits
- * in registers CLK_ENABLE_L, ... CLK_ENABLE_W
+ * in registers CLK_ENABLE_L, ... CLK_ENABLE_W, and protect refcount updates
+ * with lock
*/
+static DEFINE_SPINLOCK(periph_refcount_lock);
static int tegra_periph_clk_enable_refcount[CLK_OUT_ENB_NUM * 32];
#define clk_writel(value, reg) \
@@ -932,7 +907,7 @@ static int tegra3_cpu_clk_set_rate(struct clk *c, unsigned long rate)
}
}
- if (rate <= CPU_STAY_ON_BACKUP_MAX) {
+ if (rate <= cpu_stay_on_backup_max) {
ret = clk_set_rate(c->parent, rate);
if (ret)
pr_err("Failed to set cpu rate %lu on backup source\n",
@@ -1525,6 +1500,8 @@ static void tegra3_pll_clk_init(struct clk *c)
if (c->flags & PLL_FIXED && !(val & PLL_BASE_OVERRIDE)) {
const struct clk_pll_freq_table *sel;
unsigned long input_rate = clk_get_rate(c->parent);
+ c->u.pll.fixed_rate = PLLP_DEFAULT_FIXED_RATE;
+
for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
if (sel->input_rate == input_rate &&
sel->output_rate == c->u.pll.fixed_rate) {
@@ -1546,10 +1523,10 @@ static void tegra3_pll_clk_init(struct clk *c)
else
c->div *= (0x1 << ((val & PLL_BASE_DIVP_MASK) >>
PLL_BASE_DIVP_SHIFT));
- if (c->flags & PLL_FIXED) {
- unsigned long rate = clk_get_rate_locked(c);
- BUG_ON(rate != c->u.pll.fixed_rate);
- }
+ }
+
+ if (c->flags & PLL_FIXED) {
+ c->u.pll.fixed_rate = clk_get_rate_locked(c);
}
if (c->flags & PLLU) {
@@ -1665,6 +1642,13 @@ static int tegra3_pll_clk_set_rate(struct clk *c, unsigned long rate)
cfreq = (rate <= 1200000 * 1000) ? 1200000 : 2400000;
break;
default:
+ if (c->parent->flags & DIV_U71_FIXED) {
+ /* PLLP_OUT1 rate is not in PLLA table */
+ pr_warn("%s: failed %s ref/out rates %lu/%lu\n",
+ __func__, c->name, input_rate, rate);
+ cfreq = input_rate/(input_rate/1000000);
+ break;
+ }
pr_err("%s: Unexpected reference rate %lu\n",
__func__, input_rate);
BUG();
@@ -1736,6 +1720,26 @@ static struct clk_ops tegra_pll_ops = {
.set_rate = tegra3_pll_clk_set_rate,
};
+static void tegra3_pllp_clk_init(struct clk *c)
+{
+ tegra3_pll_clk_init(c);
+ tegra3_pllp_init_dependencies(c->u.pll.fixed_rate);
+}
+
+static void tegra3_pllp_clk_resume(struct clk *c)
+{
+ unsigned long rate = c->u.pll.fixed_rate;
+ tegra3_pll_clk_init(c);
+ BUG_ON(rate != c->u.pll.fixed_rate);
+}
+
+static struct clk_ops tegra_pllp_ops = {
+ .init = tegra3_pllp_clk_init,
+ .enable = tegra3_pll_clk_enable,
+ .disable = tegra3_pll_clk_disable,
+ .set_rate = tegra3_pll_clk_set_rate,
+};
+
static int
tegra3_plld_clk_cfg_ex(struct clk *c, enum tegra_clk_ex_param p, u32 setting)
{
@@ -1906,7 +1910,10 @@ static struct clk_ops tegra_plle_ops = {
.disable = tegra3_plle_clk_disable,
};
-/* Clock divider ops */
+/* Clock divider ops (non-atomic shared register access) */
+static DEFINE_SPINLOCK(pll_div_lock);
+
+static int tegra3_pll_div_clk_set_rate(struct clk *c, unsigned long rate);
static void tegra3_pll_div_clk_init(struct clk *c)
{
if (c->flags & DIV_U71) {
@@ -1917,6 +1924,12 @@ static void tegra3_pll_div_clk_init(struct clk *c)
if (!(val & PLL_OUT_RESET_DISABLE))
c->state = OFF;
+ if (c->u.pll_div.default_rate) {
+ int ret = tegra3_pll_div_clk_set_rate(
+ c, c->u.pll_div.default_rate);
+ if (!ret)
+ return;
+ }
divu71 = (val & PLL_OUT_RATIO_MASK) >> PLL_OUT_RATIO_SHIFT;
c->div = (divu71 + 2);
c->mul = 2;
@@ -1939,9 +1952,11 @@ static int tegra3_pll_div_clk_enable(struct clk *c)
{
u32 val;
u32 new_val;
+ unsigned long flags;
pr_debug("%s: %s\n", __func__, c->name);
if (c->flags & DIV_U71) {
+ spin_lock_irqsave(&pll_div_lock, flags);
val = clk_readl(c->reg);
new_val = val >> c->reg_shift;
new_val &= 0xFFFF;
@@ -1951,6 +1966,7 @@ static int tegra3_pll_div_clk_enable(struct clk *c)
val &= ~(0xFFFF << c->reg_shift);
val |= new_val << c->reg_shift;
clk_writel_delay(val, c->reg);
+ spin_unlock_irqrestore(&pll_div_lock, flags);
return 0;
} else if (c->flags & DIV_2) {
return 0;
@@ -1962,9 +1978,11 @@ static void tegra3_pll_div_clk_disable(struct clk *c)
{
u32 val;
u32 new_val;
+ unsigned long flags;
pr_debug("%s: %s\n", __func__, c->name);
if (c->flags & DIV_U71) {
+ spin_lock_irqsave(&pll_div_lock, flags);
val = clk_readl(c->reg);
new_val = val >> c->reg_shift;
new_val &= 0xFFFF;
@@ -1974,6 +1992,7 @@ static void tegra3_pll_div_clk_disable(struct clk *c)
val &= ~(0xFFFF << c->reg_shift);
val |= new_val << c->reg_shift;
clk_writel_delay(val, c->reg);
+ spin_unlock_irqrestore(&pll_div_lock, flags);
}
}
@@ -1983,12 +2002,14 @@ static int tegra3_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
u32 new_val;
int divider_u71;
unsigned long parent_rate = clk_get_rate(c->parent);
+ unsigned long flags;
pr_debug("%s: %s %lu\n", __func__, c->name, rate);
if (c->flags & DIV_U71) {
divider_u71 = clk_div71_get_divider(
parent_rate, rate, c->flags, ROUND_DIVIDER_UP);
if (divider_u71 >= 0) {
+ spin_lock_irqsave(&pll_div_lock, flags);
val = clk_readl(c->reg);
new_val = val >> c->reg_shift;
new_val &= 0xFFFF;
@@ -2002,6 +2023,7 @@ static int tegra3_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
clk_writel_delay(val, c->reg);
c->div = divider_u71 + 2;
c->mul = 2;
+ spin_unlock_irqrestore(&pll_div_lock, flags);
return 0;
}
} else if (c->flags & DIV_2)
@@ -2122,11 +2144,16 @@ static void tegra3_periph_clk_init(struct clk *c)
static int tegra3_periph_clk_enable(struct clk *c)
{
+ unsigned long flags;
pr_debug("%s on clock %s\n", __func__, c->name);
+ spin_lock_irqsave(&periph_refcount_lock, flags);
+
tegra_periph_clk_enable_refcount[c->u.periph.clk_num]++;
- if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] > 1)
+ if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] > 1) {
+ spin_unlock_irqrestore(&periph_refcount_lock, flags);
return 0;
+ }
clk_writel_delay(PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_ENB_SET_REG(c));
if (!(c->flags & PERIPH_NO_RESET) && !(c->flags & PERIPH_MANUAL_RESET)) {
@@ -2135,14 +2162,17 @@ static int tegra3_periph_clk_enable(struct clk *c)
clk_writel(PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_RST_CLR_REG(c));
}
}
+ spin_unlock_irqrestore(&periph_refcount_lock, flags);
return 0;
}
static void tegra3_periph_clk_disable(struct clk *c)
{
- unsigned long val;
+ unsigned long val, flags;
pr_debug("%s on clock %s\n", __func__, c->name);
+ spin_lock_irqsave(&periph_refcount_lock, flags);
+
if (c->refcnt)
tegra_periph_clk_enable_refcount[c->u.periph.clk_num]--;
@@ -2156,6 +2186,7 @@ static void tegra3_periph_clk_disable(struct clk *c)
clk_writel_delay(
PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_ENB_CLR_REG(c));
}
+ spin_unlock_irqrestore(&periph_refcount_lock, flags);
}
static void tegra3_periph_clk_reset(struct clk *c, bool assert)
@@ -2434,7 +2465,7 @@ static struct clk_ops tegra_pciex_clk_ops = {
.reset = tegra3_periph_clk_reset,
};
-/* Output clock ops */
+/* Output clock ops (non-atomic shared register access) */
static DEFINE_SPINLOCK(clk_out_lock);
@@ -2597,7 +2628,9 @@ static struct clk_ops tegra_emc_clk_ops = {
.shared_bus_update = &tegra3_clk_shared_bus_update,
};
-/* Clock doubler ops */
+/* Clock doubler ops (non-atomic shared register access) */
+static DEFINE_SPINLOCK(doubler_lock);
+
static void tegra3_clk_double_init(struct clk *c)
{
u32 val = clk_readl(c->reg);
@@ -2612,17 +2645,23 @@ static int tegra3_clk_double_set_rate(struct clk *c, unsigned long rate)
{
u32 val;
unsigned long parent_rate = clk_get_rate(c->parent);
+ unsigned long flags;
+
if (rate == parent_rate) {
+ spin_lock_irqsave(&doubler_lock, flags);
val = clk_readl(c->reg) | (0x1 << c->reg_shift);
clk_writel(val, c->reg);
c->mul = 1;
c->div = 1;
+ spin_unlock_irqrestore(&doubler_lock, flags);
return 0;
} else if (rate == 2 * parent_rate) {
+ spin_lock_irqsave(&doubler_lock, flags);
val = clk_readl(c->reg) & (~(0x1 << c->reg_shift));
clk_writel(val, c->reg);
c->mul = 2;
c->div = 1;
+ spin_unlock_irqrestore(&doubler_lock, flags);
return 0;
}
return -EINVAL;
@@ -2706,7 +2745,9 @@ static struct clk_ops tegra_audio_sync_clk_ops = {
.set_parent = tegra3_audio_sync_clk_set_parent,
};
-/* cml0 (pcie), and cml1 (sata) clock ops */
+/* cml0 (pcie), and cml1 (sata) clock ops (non-atomic shared register access) */
+static DEFINE_SPINLOCK(cml_lock);
+
static void tegra3_cml_clk_init(struct clk *c)
{
u32 val = clk_readl(c->reg);
@@ -2715,17 +2756,27 @@ static void tegra3_cml_clk_init(struct clk *c)
static int tegra3_cml_clk_enable(struct clk *c)
{
- u32 val = clk_readl(c->reg);
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cml_lock, flags);
+ val = clk_readl(c->reg);
val |= (0x1 << c->u.periph.clk_num);
clk_writel(val, c->reg);
+ spin_unlock_irqrestore(&cml_lock, flags);
return 0;
}
static void tegra3_cml_clk_disable(struct clk *c)
{
- u32 val = clk_readl(c->reg);
+ u32 val;
+ unsigned long flags;
+
+ spin_lock_irqsave(&cml_lock, flags);
+ val = clk_readl(c->reg);
val &= ~(0x1 << c->u.periph.clk_num);
clk_writel(val, c->reg);
+ spin_unlock_irqrestore(&cml_lock, flags);
}
static struct clk_ops tegra_cml_clk_ops = {
@@ -3178,7 +3229,7 @@ static struct clk_pll_freq_table tegra_pll_c_freq_table[] = {
{ 12000000, 624000000, 624, 12, 1, 8},
{ 13000000, 624000000, 624, 13, 1, 8},
- { 16800000, 600000000, 520, 14, 1, 8},
+ { 16800000, 624000000, 520, 14, 1, 8},
{ 19200000, 624000000, 520, 16, 1, 8},
{ 26000000, 624000000, 624, 26, 1, 8},
@@ -3224,7 +3275,7 @@ static struct clk tegra_pll_c = {
static struct clk tegra_pll_c_out1 = {
.name = "pll_c_out1",
.ops = &tegra_pll_div_ops,
- .flags = DIV_U71,
+ .flags = DIV_U71 | PERIPH_ON_CBUS,
.parent = &tegra_pll_c,
.reg = 0x84,
.reg_shift = 0,
@@ -3286,7 +3337,7 @@ static struct clk_pll_freq_table tegra_pll_p_freq_table[] = {
static struct clk tegra_pll_p = {
.name = "pll_p",
.flags = ENABLE_ON_INIT | PLL_FIXED | PLL_HAS_CPCON,
- .ops = &tegra_pll_ops,
+ .ops = &tegra_pllp_ops,
.reg = 0xa0,
.parent = &tegra_pll_ref,
.max_rate = 432000000,
@@ -3299,7 +3350,6 @@ static struct clk tegra_pll_p = {
.vco_max = 1400000000,
.freq_table = tegra_pll_p_freq_table,
.lock_delay = 300,
- .fixed_rate = PLLP_FIXED_RATE,
},
};
@@ -3838,9 +3888,6 @@ static struct clk tegra_clk_cclk_g = {
.reg = 0x368,
.ops = &tegra_super_ops,
.max_rate = 1700000000,
- .u.cclk = {
- .div71 = 2 * CPU_G_BACKUP_RATE_DIV - 2,
- },
};
static struct clk tegra_clk_cclk_lp = {
@@ -3850,9 +3897,6 @@ static struct clk tegra_clk_cclk_lp = {
.reg = 0x370,
.ops = &tegra_super_ops,
.max_rate = 620000000,
- .u.cclk = {
- .div71 = 2 * CPU_LP_BACKUP_RATE_DIV - 2,
- },
};
static struct clk tegra_clk_sclk = {
@@ -3861,7 +3905,7 @@ static struct clk tegra_clk_sclk = {
.reg = 0x28,
.ops = &tegra_super_ops,
.max_rate = 334000000,
- .min_rate = 40000000,
+ .min_rate = 12000000,
};
static struct clk tegra_clk_virtual_cpu_g = {
@@ -3872,7 +3916,6 @@ static struct clk tegra_clk_virtual_cpu_g = {
.u.cpu = {
.main = &tegra_pll_x,
.backup = &tegra_pll_p,
- .backup_rate = CPU_G_BACKUP_RATE,
.mode = MODE_G,
},
};
@@ -3885,7 +3928,6 @@ static struct clk tegra_clk_virtual_cpu_lp = {
.u.cpu = {
.main = &tegra_pll_x,
.backup = &tegra_pll_p,
- .backup_rate = CPU_LP_BACKUP_RATE,
.mode = MODE_LP,
},
};
@@ -3943,7 +3985,6 @@ static struct clk tegra_clk_sbus_cmplx = {
.hclk = &tegra_clk_hclk,
.sclk_low = &tegra_pll_p_out4,
.sclk_high = &tegra_pll_m_out1,
- .threshold = SBUS_THRESHOLD_RATE, /* exact factor of pll_p */
},
.rate_change_nh = &sbus_rate_change_nh,
};
@@ -4184,9 +4225,9 @@ struct clk tegra_list_clks[] = {
PERIPH_CLK("dam0", "tegra30-dam.0", NULL, 108, 0x3d8, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
PERIPH_CLK("dam1", "tegra30-dam.1", NULL, 109, 0x3dc, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
PERIPH_CLK("dam2", "tegra30-dam.2", NULL, 110, 0x3e0, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
- PERIPH_CLK("hda", "tegra30-hda", "hda", 125, 0x428, 108000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
- PERIPH_CLK("hda2codec_2x", "tegra30-hda", "hda2codec", 111, 0x3e4, 48000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
- PERIPH_CLK("hda2hdmi", "tegra30-hda", "hda2hdmi", 128, 0, 48000000, mux_clk_m, 0),
+ PERIPH_CLK("hda", "tegra30-hda", "hda", 125, 0x428, 108000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("hda2codec_2x", "tegra30-hda", "hda2codec", 111, 0x3e4, 48000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
+ PERIPH_CLK("hda2hdmi", "tegra30-hda", "hda2hdmi", 128, 0, 48000000, mux_clk_m, PERIPH_ON_APB),
PERIPH_CLK("sbc1", "spi_tegra.0", NULL, 41, 0x134, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
PERIPH_CLK("sbc2", "spi_tegra.1", NULL, 44, 0x118, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
PERIPH_CLK("sbc3", "spi_tegra.2", NULL, 46, 0x11c, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71 | PERIPH_ON_APB),
@@ -4260,6 +4301,7 @@ struct clk tegra_list_clks[] = {
PERIPH_CLK("pcie", "tegra-pcie", "pcie", 70, 0, 250000000, mux_clk_m, 0),
PERIPH_CLK("afi", "tegra-pcie", "afi", 72, 0, 250000000, mux_clk_m, 0),
PERIPH_CLK("se", "se", NULL, 127, 0x42c, 520000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71| DIV_U71_INT),
+ PERIPH_CLK("mselect", "mselect", NULL, 99, 0x3b4, 108000000, mux_pllp_clkm, MUX | DIV_U71),
SHARED_CLK("avp.sclk", "tegra-avp", "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
SHARED_CLK("bsea.sclk", "tegra-aes", "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
@@ -4267,6 +4309,7 @@ struct clk tegra_list_clks[] = {
SHARED_CLK("usb1.sclk", "tegra-ehci.0", "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
SHARED_CLK("usb2.sclk", "tegra-ehci.1", "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
SHARED_CLK("usb3.sclk", "tegra-ehci.2", "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
+ SHARED_CLK("wake.sclk", "wake_sclk", "sclk", &tegra_clk_sbus_cmplx, NULL, 0, 0),
SHARED_CLK("mon.avp", "tegra_actmon", "avp", &tegra_clk_sbus_cmplx, NULL, 0, 0),
SHARED_CLK("cap.sclk", "cap_sclk", NULL, &tegra_clk_sbus_cmplx, NULL, 0, SHARED_CEILING),
SHARED_CLK("floor.sclk", "floor_sclk", NULL, &tegra_clk_sbus_cmplx, NULL, 0, 0),
@@ -4398,10 +4441,65 @@ struct clk *tegra_ptr_clks[] = {
&tegra_clk_cbus,
};
+/*
+ * Backup rate targets for each CPU mode is selected below Fmax(Vmin), and
+ * high enough to avoid voltage droop when CPU clock is switched between
+ * backup and main clock sources. Actual backup rates will be rounded based
+ * on backup source fixed frequency. Maximum stay-on-backup rate will be set
+ * as a minimum of G and LP backup rates to be supported in both modes.
+ *
+ * Sbus threshold must be exact factor of pll_p rate.
+ */
+#define CPU_G_BACKUP_RATE_TARGET 440000000
+#define CPU_LP_BACKUP_RATE_TARGET 220000000
+
+static void tegra3_pllp_init_dependencies(unsigned long pllp_rate)
+{
+ u32 div;
+ unsigned long backup_rate;
+
+ switch (pllp_rate) {
+ case 216000000:
+ tegra_pll_p_out1.u.pll_div.default_rate = 28800000;
+ tegra_pll_p_out3.u.pll_div.default_rate = 72000000;
+ tegra_clk_sbus_cmplx.u.system.threshold = 108000000;
+ break;
+ case 408000000:
+ tegra_pll_p_out1.u.pll_div.default_rate = 9600000;
+ tegra_pll_p_out3.u.pll_div.default_rate = 102000000;
+ tegra_clk_sbus_cmplx.u.system.threshold = 204000000;
+ break;
+ case 204000000:
+ tegra_pll_p_out1.u.pll_div.default_rate = 4800000;
+ tegra_pll_p_out3.u.pll_div.default_rate = 102000000;
+ tegra_clk_sbus_cmplx.u.system.threshold = 204000000;
+ break;
+ default:
+ pr_err("tegra: PLLP rate: %lu is not supported\n", pllp_rate);
+ BUG();
+ }
+ pr_info("tegra: PLLP fixed rate: %lu\n", pllp_rate);
+
+ div = DIV_ROUND_UP(pllp_rate, CPU_G_BACKUP_RATE_TARGET);
+ backup_rate = pllp_rate / div;
+ tegra_clk_cclk_g.u.cclk.div71 = 2 * div - 2;
+ tegra_clk_virtual_cpu_g.u.cpu.backup_rate = backup_rate;
+ cpu_stay_on_backup_max = backup_rate;
+
+ div = DIV_ROUND_UP(pllp_rate, CPU_LP_BACKUP_RATE_TARGET);
+ backup_rate = pllp_rate / div;
+ tegra_clk_cclk_lp.u.cclk.div71 = 2 * div - 2;
+ tegra_clk_virtual_cpu_lp.u.cpu.backup_rate = backup_rate;
+ cpu_stay_on_backup_max = min(cpu_stay_on_backup_max, backup_rate);
+}
+
static bool tegra3_clk_is_parent_allowed(struct clk *c, struct clk *p)
{
if (c->flags & PERIPH_ON_CBUS)
return p != &tegra_pll_m;
+ else
+ return p != &tegra_pll_c;
+
return true;
}
@@ -4438,7 +4536,11 @@ void tegra_edp_throttle_cpu_now(u8 factor)
/*
* Frequency table index must be sequential starting at 0 and frequencies
- * must be ascending.
+ * must be ascending. Re-configurable PLLX is used as a source for rates
+ * above 204MHz. Rates 204MHz and below are divided down from fixed frequency
+ * PLLP that may run either at 408MHz or at 204MHz on Tegra3 silicon platforms
+ * (on FPGA platform PLLP output is reported as 216MHz, but no respective
+ * tables are provided, since there is no clock scaling on FPGA at all).
*/
static struct cpufreq_frequency_table freq_table_300MHz[] = {
@@ -4448,90 +4550,95 @@ static struct cpufreq_frequency_table freq_table_300MHz[] = {
};
static struct cpufreq_frequency_table freq_table_1p0GHz[] = {
- { 0, 102000 },
- { 1, 204000 },
- { 2, 312000 },
- { 3, 456000 },
- { 4, 608000 },
- { 5, 760000 },
- { 6, 816000 },
- { 7, 912000 },
- { 8, 1000000 },
- { 9, CPUFREQ_TABLE_END },
+ { 0, 51000 },
+ { 1, 102000 },
+ { 2, 204000 },
+ { 3, 312000 },
+ { 4, 456000 },
+ { 5, 608000 },
+ { 6, 760000 },
+ { 7, 816000 },
+ { 8, 912000 },
+ { 9, 1000000 },
+ {10, CPUFREQ_TABLE_END },
};
static struct cpufreq_frequency_table freq_table_1p3GHz[] = {
- { 0, 102000 },
- { 1, 204000 },
- { 2, 340000 },
- { 3, 475000 },
- { 4, 640000 },
- { 5, 760000 },
- { 6, 860000 },
- { 7, 1000000 },
- { 8, 1100000 },
- { 9, 1200000 },
- {10, 1300000 },
- {11, CPUFREQ_TABLE_END },
+ { 0, 51000 },
+ { 1, 102000 },
+ { 2, 204000 },
+ { 3, 340000 },
+ { 4, 475000 },
+ { 5, 640000 },
+ { 6, 760000 },
+ { 7, 860000 },
+ { 8, 1000000 },
+ { 9, 1100000 },
+ {10, 1200000 },
+ {11, 1300000 },
+ {12, CPUFREQ_TABLE_END },
};
static struct cpufreq_frequency_table freq_table_1p4GHz[] = {
- { 0, 102000 },
- { 1, 204000 },
- { 2, 370000 },
- { 3, 475000 },
- { 4, 620000 },
- { 5, 760000 },
- { 6, 860000 },
- { 7, 1000000 },
- { 8, 1100000 },
- { 9, 1200000 },
- {10, 1300000 },
- {11, 1400000 },
- {12, CPUFREQ_TABLE_END },
+ { 0, 51000 },
+ { 1, 102000 },
+ { 2, 204000 },
+ { 3, 370000 },
+ { 4, 475000 },
+ { 5, 620000 },
+ { 6, 760000 },
+ { 7, 860000 },
+ { 8, 1000000 },
+ { 9, 1100000 },
+ {10, 1200000 },
+ {11, 1300000 },
+ {12, 1400000 },
+ {13, CPUFREQ_TABLE_END },
};
static struct cpufreq_frequency_table freq_table_1p5GHz[] = {
- { 0, 102000 },
- { 1, 204000 },
- { 2, 340000 },
- { 3, 475000 },
- { 4, 640000 },
- { 5, 760000 },
- { 6, 860000 },
- { 7, 1000000 },
- { 8, 1100000 },
- { 9, 1200000 },
- {10, 1300000 },
- {11, 1400000 },
- {12, 1500000 },
- {13, CPUFREQ_TABLE_END },
+ { 0, 51000 },
+ { 1, 102000 },
+ { 2, 204000 },
+ { 3, 340000 },
+ { 4, 475000 },
+ { 5, 640000 },
+ { 6, 760000 },
+ { 7, 860000 },
+ { 8, 1000000 },
+ { 9, 1100000 },
+ {10, 1200000 },
+ {11, 1300000 },
+ {12, 1400000 },
+ {13, 1500000 },
+ {14, CPUFREQ_TABLE_END },
};
static struct cpufreq_frequency_table freq_table_1p7GHz[] = {
- { 0, 102000 },
- { 1, 204000 },
- { 2, 370000 },
- { 3, 475000 },
- { 4, 620000 },
- { 5, 760000 },
- { 6, 910000 },
- { 7, 1150000 },
- { 8, 1300000 },
- { 9, 1400000 },
- {10, 1500000 },
- {11, 1600000 },
- {12, 1700000 },
- {13, CPUFREQ_TABLE_END },
+ { 0, 51000 },
+ { 1, 102000 },
+ { 2, 204000 },
+ { 3, 370000 },
+ { 4, 475000 },
+ { 5, 620000 },
+ { 6, 760000 },
+ { 7, 910000 },
+ { 8, 1150000 },
+ { 9, 1300000 },
+ {10, 1400000 },
+ {11, 1500000 },
+ {12, 1600000 },
+ {13, 1700000 },
+ {14, CPUFREQ_TABLE_END },
};
static struct tegra_cpufreq_table_data cpufreq_tables[] = {
{ freq_table_300MHz, 0, 1 },
- { freq_table_1p0GHz, 1, 7, 2},
- { freq_table_1p3GHz, 1, 9, 2},
- { freq_table_1p4GHz, 1, 10, 2},
- { freq_table_1p5GHz, 1, 11, 2},
- { freq_table_1p7GHz, 1, 11, 2},
+ { freq_table_1p0GHz, 2, 8, 3},
+ { freq_table_1p3GHz, 2, 10, 3},
+ { freq_table_1p4GHz, 2, 11, 3},
+ { freq_table_1p5GHz, 2, 12, 3},
+ { freq_table_1p7GHz, 2, 12, 3},
};
static int clip_cpu_rate_limits(
@@ -4630,6 +4737,30 @@ unsigned long tegra_emc_to_cpu_ratio(unsigned long cpu_rate)
else
return 0; /* emc min */
}
+
+int tegra_update_mselect_rate(unsigned long cpu_rate)
+{
+ static struct clk *mselect = NULL;
+
+ unsigned long mselect_rate;
+
+ if (!mselect) {
+ mselect = tegra_get_clock_by_name("mselect");
+ if (!mselect)
+ return -ENODEV;
+ }
+
+ /* Vote on mselect frequency based on cpu frequency:
+ keep mselect at half of cpu rate up to 102 MHz;
+ cpu rate is in kHz, mselect rate is in Hz */
+ mselect_rate = DIV_ROUND_UP(cpu_rate, 2) * 1000;
+ mselect_rate = min(mselect_rate, 102000000UL);
+
+ if (mselect_rate != clk_get_rate(mselect))
+ return clk_set_rate(mselect, mselect_rate);
+
+ return 0;
+}
#endif
#ifdef CONFIG_PM_SLEEP
@@ -4829,7 +4960,7 @@ static void tegra_clk_resume(void)
tegra_emc_timing_invalidate();
tegra3_pll_clk_init(&tegra_pll_u); /* Re-init utmi parameters */
- tegra3_pll_clk_init(&tegra_pll_p); /* Fire a bug if not restored */
+ tegra3_pllp_clk_resume(&tegra_pll_p); /* Fire a bug if not restored */
}
#else
#define tegra_clk_suspend NULL
diff --git a/arch/arm/mach-tegra/tegra3_tsensor.c b/arch/arm/mach-tegra/tegra3_tsensor.c
index 4bcd81d4bc29..32fe90be1bee 100644
--- a/arch/arm/mach-tegra/tegra3_tsensor.c
+++ b/arch/arm/mach-tegra/tegra3_tsensor.c
@@ -118,12 +118,9 @@ static void tegra3_tsensor_probe_callback(struct tegra_tsensor_data *data)
if (tegra_thermal_set_device(thermal_device)) /* This should not fail */
BUG();
}
-#endif
static struct tegra_tsensor_platform_data tsensor_data = {
-#ifdef CONFIG_TEGRA_INTERNAL_TSENSOR_EDP_SUPPORT
.probe_callback = tegra3_tsensor_probe_callback,
-#endif
};
void __init tegra3_tsensor_init(struct tegra_tsensor_pmu_data *data)
@@ -189,4 +186,8 @@ labelSkipPowerOff:
labelEnd:
return;
}
-
+#else
+void __init tegra3_tsensor_init(struct tegra_tsensor_pmu_data *data)
+{
+}
+#endif
diff --git a/arch/arm/mach-tegra/usb_phy.c b/arch/arm/mach-tegra/usb_phy.c
index 5eab7ccd3dc8..32ae3744b3ae 100644
--- a/arch/arm/mach-tegra/usb_phy.c
+++ b/arch/arm/mach-tegra/usb_phy.c
@@ -1414,27 +1414,45 @@ static void utmip_phy_disable_pmc_bus_ctrl(struct tegra_usb_phy *phy)
#endif
}
-static int utmi_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd)
+static void utmi_phy_enable_obs_bus(struct tegra_usb_phy *phy,
+ enum tegra_usb_phy_port_speed port_speed)
{
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
unsigned long val;
void __iomem *base = phy->regs;
- val = readl(base + UTMIP_TX_CFG0);
- val |= UTMIP_HS_DISCON_DISABLE;
- writel(val, base + UTMIP_TX_CFG0);
-#else
- utmip_phy_disable_pmc_bus_ctrl(phy);
-#endif
- return 0;
+ /* (2LS WAR)is not required for LS and FS devices and is only for HS */
+ if (port_speed != TEGRA_USB_PHY_PORT_SPEED_HIGH) {
+ /* do not enable the OBS bus */
+ val = readl(base + UTMIP_MISC_CFG0);
+ val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
+ writel(val, base + UTMIP_MISC_CFG0);
+ return;
+ }
+ /* Force DP/DM pulldown active for Host mode */
+ val = readl(base + UTMIP_MISC_CFG0);
+ val |= FORCE_PULLDN_DM | FORCE_PULLDN_DP |
+ COMB_TERMS | ALWAYS_FREE_RUNNING_TERMS;
+ writel(val, base + UTMIP_MISC_CFG0);
+ val = readl(base + UTMIP_MISC_CFG0);
+ val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
+ if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
+ val |= UTMIP_DPDM_OBSERVE_SEL_FS_J;
+ else
+ val |= UTMIP_DPDM_OBSERVE_SEL_FS_K;
+ writel(val, base + UTMIP_MISC_CFG0);
+ udelay(1);
+
+ val = readl(base + UTMIP_MISC_CFG0);
+ val |= UTMIP_DPDM_OBSERVE;
+ writel(val, base + UTMIP_MISC_CFG0);
+ udelay(10);
}
-static int utmi_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd)
+static void utmi_phy_disable_obs_bus(struct tegra_usb_phy *phy)
{
unsigned long val;
void __iomem *base = phy->regs;
-#ifndef CONFIG_ARCH_TEGRA_2x_SOC
/* check if OBS bus is already enabled */
val = readl(base + UTMIP_MISC_CFG0);
if (val & UTMIP_DPDM_OBSERVE) {
@@ -1458,11 +1476,65 @@ static int utmi_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd)
COMB_TERMS | ALWAYS_FREE_RUNNING_TERMS);
writel(val, base + UTMIP_MISC_CFG0);
}
+}
+
+
+static int utmi_phy_preresume(struct tegra_usb_phy *phy, bool remote_wakeup)
+{
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ enum tegra_usb_phy_port_speed port_speed;
+
+ val = readl(base + UTMIP_TX_CFG0);
+ val |= UTMIP_HS_DISCON_DISABLE;
+ writel(val, base + UTMIP_TX_CFG0);
+
+ port_speed = (readl(base + USB_PORTSC1) >> 26) & 0x3;
+ utmi_phy_enable_obs_bus(phy, port_speed);
+
+#else
+ unsigned long val;
+ void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+ unsigned int inst = phy->instance;
+ void __iomem *base = phy->regs;
+ enum tegra_usb_phy_port_speed port_speed;
+
+ val = readl(pmc_base + PMC_SLEEP_CFG);
+ if (val & UTMIP_MASTER_ENABLE(inst)) {
+ if (!remote_wakeup)
+ utmip_phy_disable_pmc_bus_ctrl(phy);
+ } else {
+ port_speed = (readl(base + HOSTPC1_DEVLC) >> 25) &
+ HOSTPC1_DEVLC_PSPD_MASK;
+ utmi_phy_enable_obs_bus(phy, port_speed);
+ }
+#endif
+
+ return 0;
+}
+
+static int utmi_phy_postresume(struct tegra_usb_phy *phy, bool is_dpd)
+{
+ unsigned long val;
+ void __iomem *base = phy->regs;
+ void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+ unsigned int inst = phy->instance;
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ val = readl(pmc_base + PMC_SLEEP_CFG);
+ /* if PMC is not disabled by now then disable it */
+ if (val & UTMIP_MASTER_ENABLE(inst)) {
+ utmip_phy_disable_pmc_bus_ctrl(phy);
+ }
#else
val = readl(base + UTMIP_TX_CFG0);
val &= ~UTMIP_HS_DISCON_DISABLE;
writel(val, base + UTMIP_TX_CFG0);
#endif
+
+ utmi_phy_disable_obs_bus(phy);
+
return 0;
}
@@ -1476,7 +1548,7 @@ static int uhsic_phy_postsuspend(struct tegra_usb_phy *phy, bool is_dpd)
return 0;
}
-static int uhsic_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd)
+static int uhsic_phy_preresume(struct tegra_usb_phy *phy, bool remote_wakeup)
{
struct tegra_uhsic_config *uhsic_config = phy->config;
@@ -1524,7 +1596,6 @@ static void utmi_phy_restore_start(struct tegra_usb_phy *phy,
udelay(10);
#else
unsigned long val;
- void __iomem *base = phy->regs;
void __iomem *pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
int inst = phy->instance;
@@ -1538,34 +1609,7 @@ static void utmi_phy_restore_start(struct tegra_usb_phy *phy,
utmip_phy_disable_pmc_bus_ctrl(phy);
}
}
-
- /* (2LS WAR)is not required for LS and FS devices and is only for HS */
- if ((port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW) ||
- (port_speed == TEGRA_USB_PHY_PORT_SPEED_FULL)) {
- /* do not enable the OBS bus */
- val = readl(base + UTMIP_MISC_CFG0);
- val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
- writel(val, base + UTMIP_MISC_CFG0);
- return;
- }
- /* Force DP/DM pulldown active for Host mode */
- val = readl(base + UTMIP_MISC_CFG0);
- val |= FORCE_PULLDN_DM | FORCE_PULLDN_DP |
- COMB_TERMS | ALWAYS_FREE_RUNNING_TERMS;
- writel(val, base + UTMIP_MISC_CFG0);
- val = readl(base + UTMIP_MISC_CFG0);
- val &= ~UTMIP_DPDM_OBSERVE_SEL(~0);
- if (port_speed == TEGRA_USB_PHY_PORT_SPEED_LOW)
- val |= UTMIP_DPDM_OBSERVE_SEL_FS_J;
- else
- val |= UTMIP_DPDM_OBSERVE_SEL_FS_K;
- writel(val, base + UTMIP_MISC_CFG0);
- udelay(1);
-
- val = readl(base + UTMIP_MISC_CFG0);
- val |= UTMIP_DPDM_OBSERVE;
- writel(val, base + UTMIP_MISC_CFG0);
- udelay(10);
+ utmi_phy_enable_obs_bus(phy, port_speed);
#endif
}
@@ -1597,10 +1641,10 @@ static void utmi_phy_restore_end(struct tegra_usb_phy *phy)
}
wait_time_us--;
} while (!(val & USB_PORTSC1_RESUME));
- /* disable PMC master control */
- utmip_phy_disable_pmc_bus_ctrl(phy);
/* wait for 25 ms to port resume complete */
msleep(25);
+ /* disable PMC master control */
+ utmip_phy_disable_pmc_bus_ctrl(phy);
/* Clear PCI and SRI bits to avoid an interrupt upon resume */
val = readl(base + USB_USBSTS);
@@ -2481,7 +2525,7 @@ void tegra_usb_phy_power_off(struct tegra_usb_phy *phy, bool is_dpd)
phy->power_on = false;
}
-void tegra_usb_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd)
+void tegra_usb_phy_preresume(struct tegra_usb_phy *phy, bool remote_wakeup)
{
const tegra_phy_fp preresume[] = {
utmi_phy_preresume,
@@ -2491,7 +2535,7 @@ void tegra_usb_phy_preresume(struct tegra_usb_phy *phy, bool is_dpd)
};
if (preresume[phy->usb_phy_type])
- preresume[phy->usb_phy_type](phy, is_dpd);
+ preresume[phy->usb_phy_type](phy, remote_wakeup);
}
void tegra_usb_phy_postsuspend(struct tegra_usb_phy *phy, bool is_dpd)
@@ -2762,14 +2806,26 @@ int tegra_usb_phy_bus_idle(struct tegra_usb_phy *phy)
val = readl(base + UHSIC_PADS_CFG1);
val &= ~UHSIC_RPD_STROBE;
-#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ /* safe to enable RPU on STROBE at all times during idle */
val |= UHSIC_RPU_STROBE;
-#endif
writel(val, base + UHSIC_PADS_CFG1);
+ val = readl(base + USB_USBCMD);
+ val &= ~USB_USBCMD_RS;
+ writel(val, base + USB_USBCMD);
+
if (uhsic_config->usb_phy_ready &&
uhsic_config->usb_phy_ready())
return -EAGAIN;
+
+ /* wait for connect detect */
+ if (utmi_wait_register(base + UHSIC_STAT_CFG0,
+ UHSIC_CONNECT_DETECT, UHSIC_CONNECT_DETECT) < 0) {
+ pr_err("%s: timeout waiting for hsic connect detect\n",
+ __func__);
+ return -ETIMEDOUT;
+ }
+
}
return 0;
}
diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types
index 2ffda3616358..9e31c03f3663 100644
--- a/arch/arm/tools/mach-types
+++ b/arch/arm/tools/mach-types
@@ -1115,4 +1115,5 @@ ubisys_p9d_evp MACH_UBISYS_P9D_EVP UBISYS_P9D_EVP 3493
atdgp318 MACH_ATDGP318 ATDGP318 3494
tegra_enterprise MACH_TEGRA_ENTERPRISE TEGRA_ENTERPRISE 3512
p1852 MACH_P1852 P1852 3651
+kai MACH_KAI KAI 3897
p852 MACH_P852 P852 3667
diff --git a/arch/x86/include/asm/amd_nb.h b/arch/x86/include/asm/amd_nb.h
index 67f87f257611..78a1eff74223 100644
--- a/arch/x86/include/asm/amd_nb.h
+++ b/arch/x86/include/asm/amd_nb.h
@@ -1,6 +1,7 @@
#ifndef _ASM_X86_AMD_NB_H
#define _ASM_X86_AMD_NB_H
+#include <linux/ioport.h>
#include <linux/pci.h>
struct amd_nb_bus_dev_range {
@@ -13,6 +14,7 @@ extern const struct pci_device_id amd_nb_misc_ids[];
extern const struct amd_nb_bus_dev_range amd_nb_bus_dev_ranges[];
extern bool early_is_amd_nb(u32 value);
+extern struct resource *amd_get_mmconfig_range(struct resource *res);
extern int amd_cache_northbridges(void);
extern void amd_flush_garts(void);
extern int amd_numa_init(void);
diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c
index 4c39baa8facc..bae1efe6d515 100644
--- a/arch/x86/kernel/amd_nb.c
+++ b/arch/x86/kernel/amd_nb.c
@@ -119,6 +119,37 @@ bool __init early_is_amd_nb(u32 device)
return false;
}
+struct resource *amd_get_mmconfig_range(struct resource *res)
+{
+ u32 address;
+ u64 base, msr;
+ unsigned segn_busn_bits;
+
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
+ return NULL;
+
+ /* assume all cpus from fam10h have mmconfig */
+ if (boot_cpu_data.x86 < 0x10)
+ return NULL;
+
+ address = MSR_FAM10H_MMIO_CONF_BASE;
+ rdmsrl(address, msr);
+
+ /* mmconfig is not enabled */
+ if (!(msr & FAM10H_MMIO_CONF_ENABLE))
+ return NULL;
+
+ base = msr & (FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT);
+
+ segn_busn_bits = (msr >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) &
+ FAM10H_MMIO_CONF_BUSRANGE_MASK;
+
+ res->flags = IORESOURCE_MEM;
+ res->start = base;
+ res->end = base + (1ULL<<(segn_busn_bits + 20)) - 1;
+ return res;
+}
+
int amd_get_subcaches(int cpu)
{
struct pci_dev *link = node_to_amd_nb(amd_get_nb_id(cpu))->link;
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index c1a0188e29ae..44842d756b29 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -74,9 +74,10 @@ static cycle_t kvm_clock_read(void)
struct pvclock_vcpu_time_info *src;
cycle_t ret;
- src = &get_cpu_var(hv_clock);
+ preempt_disable_notrace();
+ src = &__get_cpu_var(hv_clock);
ret = pvclock_clocksource_read(src);
- put_cpu_var(hv_clock);
+ preempt_enable_notrace();
return ret;
}
diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c
index efad72385058..43e04d128af4 100644
--- a/arch/x86/kvm/i8254.c
+++ b/arch/x86/kvm/i8254.c
@@ -338,11 +338,15 @@ static enum hrtimer_restart pit_timer_fn(struct hrtimer *data)
return HRTIMER_NORESTART;
}
-static void create_pit_timer(struct kvm_kpit_state *ps, u32 val, int is_period)
+static void create_pit_timer(struct kvm *kvm, u32 val, int is_period)
{
+ struct kvm_kpit_state *ps = &kvm->arch.vpit->pit_state;
struct kvm_timer *pt = &ps->pit_timer;
s64 interval;
+ if (!irqchip_in_kernel(kvm))
+ return;
+
interval = muldiv64(val, NSEC_PER_SEC, KVM_PIT_FREQ);
pr_debug("create pit timer, interval is %llu nsec\n", interval);
@@ -394,13 +398,13 @@ static void pit_load_count(struct kvm *kvm, int channel, u32 val)
/* FIXME: enhance mode 4 precision */
case 4:
if (!(ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)) {
- create_pit_timer(ps, val, 0);
+ create_pit_timer(kvm, val, 0);
}
break;
case 2:
case 3:
if (!(ps->flags & KVM_PIT_FLAGS_HPET_LEGACY)){
- create_pit_timer(ps, val, 1);
+ create_pit_timer(kvm, val, 1);
}
break;
default:
diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c
index 1dab5194fd9d..f927429d07ca 100644
--- a/arch/x86/mm/mmap.c
+++ b/arch/x86/mm/mmap.c
@@ -87,9 +87,9 @@ static unsigned long mmap_rnd(void)
*/
if (current->flags & PF_RANDOMIZE) {
if (mmap_is_ia32())
- rnd = (long)get_random_int() % (1<<8);
+ rnd = get_random_int() % (1<<8);
else
- rnd = (long)(get_random_int() % (1<<28));
+ rnd = get_random_int() % (1<<28);
}
return rnd << PAGE_SHIFT;
}
diff --git a/arch/x86/pci/Makefile b/arch/x86/pci/Makefile
index 6b8759f7634e..d24d3da72926 100644
--- a/arch/x86/pci/Makefile
+++ b/arch/x86/pci/Makefile
@@ -18,8 +18,9 @@ obj-$(CONFIG_X86_NUMAQ) += numaq_32.o
obj-$(CONFIG_X86_MRST) += mrst.o
obj-y += common.o early.o
-obj-y += amd_bus.o bus_numa.o
+obj-y += bus_numa.o
+obj-$(CONFIG_AMD_NB) += amd_bus.o
obj-$(CONFIG_PCI_CNB20LE_QUIRK) += broadcom_bus.o
ifeq ($(CONFIG_PCI_DEBUG),y)
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c
index 404f21a3ff9e..f8348ab10324 100644
--- a/arch/x86/pci/acpi.c
+++ b/arch/x86/pci/acpi.c
@@ -149,7 +149,7 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
struct acpi_resource_address64 addr;
acpi_status status;
unsigned long flags;
- u64 start, end;
+ u64 start, orig_end, end;
status = resource_to_addr(acpi_res, &addr);
if (!ACPI_SUCCESS(status))
@@ -165,7 +165,21 @@ setup_resource(struct acpi_resource *acpi_res, void *data)
return AE_OK;
start = addr.minimum + addr.translation_offset;
- end = addr.maximum + addr.translation_offset;
+ orig_end = end = addr.maximum + addr.translation_offset;
+
+ /* Exclude non-addressable range or non-addressable portion of range */
+ end = min(end, (u64)iomem_resource.end);
+ if (end <= start) {
+ dev_info(&info->bridge->dev,
+ "host bridge window [%#llx-%#llx] "
+ "(ignored, not CPU addressable)\n", start, orig_end);
+ return AE_OK;
+ } else if (orig_end != end) {
+ dev_info(&info->bridge->dev,
+ "host bridge window [%#llx-%#llx] "
+ "([%#llx-%#llx] ignored, not CPU addressable)\n",
+ start, orig_end, end + 1, orig_end);
+ }
res = &info->res[info->res_num];
res->name = info->name;
diff --git a/arch/x86/pci/amd_bus.c b/arch/x86/pci/amd_bus.c
index 026e4931d162..385a940b5422 100644
--- a/arch/x86/pci/amd_bus.c
+++ b/arch/x86/pci/amd_bus.c
@@ -30,34 +30,6 @@ static struct pci_hostbridge_probe pci_probes[] __initdata = {
{ 0, 0x18, PCI_VENDOR_ID_AMD, 0x1300 },
};
-static u64 __initdata fam10h_mmconf_start;
-static u64 __initdata fam10h_mmconf_end;
-static void __init get_pci_mmcfg_amd_fam10h_range(void)
-{
- u32 address;
- u64 base, msr;
- unsigned segn_busn_bits;
-
- /* assume all cpus from fam10h have mmconf */
- if (boot_cpu_data.x86 < 0x10)
- return;
-
- address = MSR_FAM10H_MMIO_CONF_BASE;
- rdmsrl(address, msr);
-
- /* mmconfig is not enable */
- if (!(msr & FAM10H_MMIO_CONF_ENABLE))
- return;
-
- base = msr & (FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT);
-
- segn_busn_bits = (msr >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) &
- FAM10H_MMIO_CONF_BUSRANGE_MASK;
-
- fam10h_mmconf_start = base;
- fam10h_mmconf_end = base + (1ULL<<(segn_busn_bits + 20)) - 1;
-}
-
#define RANGE_NUM 16
/**
@@ -85,6 +57,9 @@ static int __init early_fill_mp_bus_info(void)
u64 val;
u32 address;
bool found;
+ struct resource fam10h_mmconf_res, *fam10h_mmconf;
+ u64 fam10h_mmconf_start;
+ u64 fam10h_mmconf_end;
if (!early_pci_allowed())
return -1;
@@ -211,12 +186,17 @@ static int __init early_fill_mp_bus_info(void)
subtract_range(range, RANGE_NUM, 0, end);
/* get mmconfig */
- get_pci_mmcfg_amd_fam10h_range();
+ fam10h_mmconf = amd_get_mmconfig_range(&fam10h_mmconf_res);
/* need to take out mmconf range */
- if (fam10h_mmconf_end) {
- printk(KERN_DEBUG "Fam 10h mmconf [%llx, %llx]\n", fam10h_mmconf_start, fam10h_mmconf_end);
+ if (fam10h_mmconf) {
+ printk(KERN_DEBUG "Fam 10h mmconf %pR\n", fam10h_mmconf);
+ fam10h_mmconf_start = fam10h_mmconf->start;
+ fam10h_mmconf_end = fam10h_mmconf->end;
subtract_range(range, RANGE_NUM, fam10h_mmconf_start,
fam10h_mmconf_end + 1);
+ } else {
+ fam10h_mmconf_start = 0;
+ fam10h_mmconf_end = 0;
}
/* mmio resource */
diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
index b94d871487e0..764249587f14 100644
--- a/drivers/gpu/drm/radeon/r100.c
+++ b/drivers/gpu/drm/radeon/r100.c
@@ -2069,6 +2069,7 @@ bool r100_gpu_is_lockup(struct radeon_device *rdev)
void r100_bm_disable(struct radeon_device *rdev)
{
u32 tmp;
+ u16 tmp16;
/* disable bus mastering */
tmp = RREG32(R_000030_BUS_CNTL);
@@ -2079,8 +2080,8 @@ void r100_bm_disable(struct radeon_device *rdev)
WREG32(R_000030_BUS_CNTL, (tmp & 0xFFFFFFFF) | 0x00000040);
tmp = RREG32(RADEON_BUS_CNTL);
mdelay(1);
- pci_read_config_word(rdev->pdev, 0x4, (u16*)&tmp);
- pci_write_config_word(rdev->pdev, 0x4, tmp & 0xFFFB);
+ pci_read_config_word(rdev->pdev, 0x4, &tmp16);
+ pci_write_config_word(rdev->pdev, 0x4, tmp16 & 0xFFFB);
mdelay(1);
}
diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c
index f5ac7e788d81..c45d92191fd8 100644
--- a/drivers/gpu/drm/radeon/r600_hdmi.c
+++ b/drivers/gpu/drm/radeon/r600_hdmi.c
@@ -196,6 +196,13 @@ static void r600_hdmi_videoinfoframe(
frame[0xD] = (right_bar >> 8);
r600_hdmi_infoframe_checksum(0x82, 0x02, 0x0D, frame);
+ /* Our header values (type, version, length) should be alright, Intel
+ * is using the same. Checksum function also seems to be OK, it works
+ * fine for audio infoframe. However calculated value is always lower
+ * by 2 in comparison to fglrx. It breaks displaying anything in case
+ * of TVs that strictly check the checksum. Hack it manually here to
+ * workaround this issue. */
+ frame[0x0] += 2;
WREG32(offset+R600_HDMI_VIDEOINFOFRAME_0,
frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24));
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index b51e15725c6e..50d105a1c747 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -224,8 +224,11 @@ int radeon_wb_init(struct radeon_device *rdev)
if (radeon_no_wb == 1)
rdev->wb.enabled = false;
else {
- /* often unreliable on AGP */
if (rdev->flags & RADEON_IS_AGP) {
+ /* often unreliable on AGP */
+ rdev->wb.enabled = false;
+ } else if (rdev->family < CHIP_R300) {
+ /* often unreliable on pre-r300 */
rdev->wb.enabled = false;
} else {
rdev->wb.enabled = true;
diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
index 0e89a9b885e9..d9b0bc4547e3 100644
--- a/drivers/gpu/drm/radeon/rs600.c
+++ b/drivers/gpu/drm/radeon/rs600.c
@@ -324,10 +324,10 @@ void rs600_hpd_fini(struct radeon_device *rdev)
void rs600_bm_disable(struct radeon_device *rdev)
{
- u32 tmp;
+ u16 tmp;
/* disable bus mastering */
- pci_read_config_word(rdev->pdev, 0x4, (u16*)&tmp);
+ pci_read_config_word(rdev->pdev, 0x4, &tmp);
pci_write_config_word(rdev->pdev, 0x4, tmp & 0xFFFB);
mdelay(1);
}
diff --git a/drivers/gpu/ion/ion.c b/drivers/gpu/ion/ion.c
index 28b8bb44212b..512ebc5cc8ea 100644
--- a/drivers/gpu/ion/ion.c
+++ b/drivers/gpu/ion/ion.c
@@ -405,6 +405,32 @@ struct scatterlist *ion_map_dma(struct ion_client *client,
return sglist;
}
+struct scatterlist *iommu_heap_remap_dma(struct ion_heap *heap,
+ struct ion_buffer *buf,
+ unsigned long addr);
+int ion_remap_dma(struct ion_client *client,
+ struct ion_handle *handle,
+ unsigned long addr)
+{
+ struct ion_buffer *buffer;
+ int ret;
+
+ mutex_lock(&client->lock);
+ if (!ion_handle_validate(client, handle)) {
+ pr_err("invalid handle passed to map_dma.\n");
+ mutex_unlock(&client->lock);
+ return -EINVAL;
+ }
+ buffer = handle->buffer;
+ mutex_lock(&buffer->lock);
+
+ ret = iommu_heap_remap_dma(buffer->heap, buffer, addr);
+
+ mutex_unlock(&buffer->lock);
+ mutex_unlock(&client->lock);
+ return ret;
+}
+
void ion_unmap_kernel(struct ion_client *client, struct ion_handle *handle)
{
struct ion_buffer *buffer;
diff --git a/drivers/gpu/ion/ion_iommu_heap.c b/drivers/gpu/ion/ion_iommu_heap.c
index f0246cb39fae..b75bb3132a2d 100644
--- a/drivers/gpu/ion/ion_iommu_heap.c
+++ b/drivers/gpu/ion/ion_iommu_heap.c
@@ -20,20 +20,17 @@
#define pr_fmt(fmt) "%s(): " fmt, __func__
#include <linux/spinlock.h>
-#include <linux/err.h>
+#include <linux/kernel.h>
#include <linux/genalloc.h>
#include <linux/io.h>
#include <linux/ion.h>
#include <linux/mm.h>
-#include <linux/platform_device.h>
-#include <linux/tegra_ion.h>
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/iommu.h>
#include <linux/highmem.h>
-#include <asm/page.h>
#include <asm/cacheflush.h>
#include "ion_priv.h"
@@ -63,7 +60,7 @@ static struct scatterlist *iommu_heap_map_dma(struct ion_heap *heap,
phys_addr_t pa;
pa = sg_phys(sg);
- BUG_ON(!ALIGN(sg->length, PAGE_SIZE));
+ BUG_ON(!IS_ALIGNED(sg->length, PAGE_SIZE));
err = iommu_map(h->domain, da, pa, 0, 0);
if (err)
goto err_out;
@@ -100,6 +97,52 @@ static void iommu_heap_unmap_dma(struct ion_heap *heap, struct ion_buffer *buf)
pr_debug("da:%p\n", buf->priv_virt);
}
+struct scatterlist *iommu_heap_remap_dma(struct ion_heap *heap,
+ struct ion_buffer *buf,
+ unsigned long addr)
+{
+ struct ion_iommu_heap *h =
+ container_of(heap, struct ion_iommu_heap, heap);
+ int err;
+ unsigned int i;
+ unsigned long da, da_to_free = (unsigned long)buf->priv_virt;
+ int npages = NUM_PAGES(buf);
+
+ BUG_ON(!buf->priv_virt);
+
+ da = gen_pool_alloc_addr(h->pool, buf->size, addr);
+ if (da == 0) {
+ pr_err("dma address alloc failed, addr=0x%lx", addr);
+ return ERR_PTR(-ENOMEM);
+ } else {
+ pr_err("iommu_heap_remap_dma passed, addr=0x%lx",
+ addr);
+ iommu_heap_unmap_dma(heap, buf);
+ gen_pool_free(h->pool, da_to_free, buf->size);
+ buf->priv_virt = (void *)da;
+ }
+ for (i = 0; i < npages; i++) {
+ phys_addr_t pa;
+
+ pa = page_to_phys(buf->pages[i]);
+ err = iommu_map(h->domain, da, pa, 0, 0);
+ if (err)
+ goto err_out;
+ da += PAGE_SIZE;
+ }
+
+ pr_debug("da:%p pa:%08x va:%p\n",
+ buf->priv_virt, page_to_phys(buf->pages[0]), buf->vaddr);
+
+ return (struct scatterlist *)buf->pages;
+
+err_out:
+ if (i-- > 0) {
+ da = (unsigned long)buf->priv_virt;
+ iommu_unmap(h->domain, da + (i << PAGE_SHIFT), 0);
+ }
+ return ERR_PTR(err);
+}
static int ion_buffer_allocate(struct ion_buffer *buf)
{
@@ -109,11 +152,10 @@ static int ion_buffer_allocate(struct ion_buffer *buf)
if (!buf->pages)
goto err_pages;
- buf->sglist = vmalloc(npages * sizeof(*buf->sglist));
+ buf->sglist = vzalloc(npages * sizeof(*buf->sglist));
if (!buf->sglist)
goto err_sgl;
- memset(buf->sglist, 0, npages * sizeof(*buf->sglist));
sg_init_table(buf->sglist, npages);
for (i = 0; i < npages; i++) {
@@ -169,10 +211,9 @@ static int iommu_heap_allocate(struct ion_heap *heap, struct ion_buffer *buf,
len = round_up(len, PAGE_SIZE);
da = gen_pool_alloc(h->pool, len);
- if (!da) {
- buf->priv_virt = (void *)ION_CARVEOUT_ALLOCATE_FAIL;
+ if (!da)
return -ENOMEM;
- }
+
buf->priv_virt = (void *)da;
buf->size = len;
@@ -205,16 +246,6 @@ static void iommu_heap_free(struct ion_buffer *buf)
container_of(heap, struct ion_iommu_heap, heap);
void *da = buf->priv_virt;
- /*
- * FIXME:
- * Buf should not be in use.
- * Forcibly remove iommu mappings, if any exists.
- * Free physical pages here.
- */
-
- if (da == (void *)ION_CARVEOUT_ALLOCATE_FAIL)
- return;
-
iommu_heap_unmap_dma(heap, buf);
ion_buffer_free(buf);
gen_pool_free(h->pool, (unsigned long)da, buf->size);
diff --git a/drivers/gpu/ion/ion_priv.h b/drivers/gpu/ion/ion_priv.h
index c8415b888e87..bfe26da9c049 100644
--- a/drivers/gpu/ion/ion_priv.h
+++ b/drivers/gpu/ion/ion_priv.h
@@ -128,6 +128,9 @@ struct ion_handle *ion_handle_create(struct ion_client *client,
void ion_handle_add(struct ion_client *client, struct ion_handle *handle);
+int ion_remap_dma(struct ion_client *client,
+ struct ion_handle *handle,
+ unsigned long addr);
/**
* struct ion_buffer - metadata for a particular buffer
* @ref: refernce count
@@ -268,6 +271,7 @@ ion_phys_addr_t ion_carveout_allocate(struct ion_heap *heap, unsigned long size,
unsigned long align);
void ion_carveout_free(struct ion_heap *heap, ion_phys_addr_t addr,
unsigned long size);
+
#ifdef CONFIG_ION_IOMMU
struct ion_heap *ion_iommu_heap_create(struct ion_platform_heap *);
void ion_iommu_heap_destroy(struct ion_heap *);
diff --git a/drivers/gpu/ion/tegra/tegra_ion.c b/drivers/gpu/ion/tegra/tegra_ion.c
index 65335d265362..f686269f2f81 100644
--- a/drivers/gpu/ion/tegra/tegra_ion.c
+++ b/drivers/gpu/ion/tegra/tegra_ion.c
@@ -572,8 +572,11 @@ void _nvmap_handle_free(struct nvmap_handle *h)
struct nvmap_handle_ref *nvmap_alloc_iovm(struct nvmap_client *client,
size_t size, size_t align, unsigned int flags, unsigned int iova_start)
{
- /* FIXME: */
- return NULL;
+ struct ion_handle *h;
+
+ h = ion_alloc(client, size, align, 0xFF);
+ ion_remap_dma(client, h, iova_start);
+ return h;
}
void nvmap_free_iovm(struct nvmap_client *client, struct nvmap_handle_ref *r)
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index f26ae3176a39..e9c8f803de1b 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -361,7 +361,7 @@ static int hid_parser_global(struct hid_parser *parser, struct hid_item *item)
case HID_GLOBAL_ITEM_TAG_REPORT_SIZE:
parser->global.report_size = item_udata(item);
- if (parser->global.report_size > 32) {
+ if (parser->global.report_size > 96) {
dbg_hid("invalid report_size %d\n",
parser->global.report_size);
return -1;
diff --git a/drivers/hwmon/tegra-tsensor.c b/drivers/hwmon/tegra-tsensor.c
index e4792cba4937..eb5d55cef4e0 100644
--- a/drivers/hwmon/tegra-tsensor.c
+++ b/drivers/hwmon/tegra-tsensor.c
@@ -105,7 +105,7 @@
#define CELSIUS_TO_MILLICELSIUS(i) ((i) * 1000)
#define get_ts_state(data) tsensor_get_reg_field(data,\
- ((data->tsensor_index << 16) | SENSOR_STATUS0), \
+ ((data->instance << 16) | SENSOR_STATUS0), \
STATUS0_STATE_SHIFT, STATE_MASK)
/* tsensor states */
@@ -181,14 +181,13 @@ struct tegra_tsensor_data {
/* clk register space */
void __iomem *clk_rst_base;
int irq;
- unsigned int int_status[TSENSOR_COUNT];
/* save configuration before suspend and restore after resume */
unsigned int config0[TSENSOR_COUNT];
unsigned int config1[TSENSOR_COUNT];
unsigned int config2[TSENSOR_COUNT];
- /* temperature readings from tsensor_index tsensor - 0/1 */
- unsigned int tsensor_index;
+ /* temperature readings from instance tsensor - 0/1 */
+ unsigned int instance;
int A_e_minus6;
int B_e_minus2;
unsigned int fuse_T1;
@@ -349,7 +348,7 @@ static void get_chip_tsensor_coeff(struct tegra_tsensor_data *data)
coeff_index = TSENSOR_COEFF_SET1;
break;
}
- if (data->tsensor_index == TSENSOR_INSTANCE1)
+ if (data->instance == TSENSOR_INSTANCE1)
coeff_index = TSENSOR_COEFF_SET2;
data->m_e_minus6 = coeff_table[coeff_index].e_minus6_m;
data->n_e_minus6 = coeff_table[coeff_index].e_minus6_n;
@@ -367,7 +366,7 @@ static int tsensor_read_counter(
const int max_loop = 50;
do {
- config0 = tsensor_readl(data, ((data->tsensor_index << 16) |
+ config0 = tsensor_readl(data, ((data->instance << 16) |
SENSOR_CFG0));
if (config0 & (1 << SENSOR_CFG0_STOP_SHIFT)) {
dev_dbg(data->hwmon_dev, "Error: tsensor "
@@ -377,10 +376,10 @@ static int tsensor_read_counter(
}
status_reg = tsensor_readl(data,
- (data->tsensor_index << 16) | SENSOR_STATUS0);
+ (data->instance << 16) | SENSOR_STATUS0);
if (status_reg & (1 <<
SENSOR_STATUS_CURR_VALID_SHIFT)) {
- *p_counter = tsensor_readl(data, (data->tsensor_index
+ *p_counter = tsensor_readl(data, (data->instance
<< 16) | SENSOR_TS_STATUS1);
break;
}
@@ -404,8 +403,8 @@ static void dump_threshold(struct tegra_tsensor_data *data)
unsigned int curr_avg;
int err;
- TH_2_1 = tsensor_readl(data, (data->tsensor_index << 16) | SENSOR_CFG1);
- TH_0_3 = tsensor_readl(data, (data->tsensor_index << 16) | SENSOR_CFG2);
+ TH_2_1 = tsensor_readl(data, (data->instance << 16) | SENSOR_CFG1);
+ TH_0_3 = tsensor_readl(data, (data->instance << 16) | SENSOR_CFG2);
dev_dbg(data->hwmon_dev, "Tsensor: TH_2_1=0x%x, "
"TH_0_3=0x%x\n", TH_2_1, TH_0_3);
err = tsensor_read_counter(data, &curr_avg);
@@ -516,25 +515,25 @@ static int get_param_values(
{
switch (indx) {
case TSENSOR_PARAM_TH1:
- *p_reg = ((data->tsensor_index << 16) | SENSOR_CFG1);
+ *p_reg = ((data->instance << 16) | SENSOR_CFG1);
*p_sft = SENSOR_CFG1_TH1_SHIFT;
*p_msk = SENSOR_CFG_X_TH_X_MASK;
snprintf(info, info_len, "TH1[%d]: ",
- data->tsensor_index);
+ data->instance);
break;
case TSENSOR_PARAM_TH2:
- *p_reg = ((data->tsensor_index << 16) | SENSOR_CFG1);
+ *p_reg = ((data->instance << 16) | SENSOR_CFG1);
*p_sft = SENSOR_CFG1_TH2_SHIFT;
*p_msk = SENSOR_CFG_X_TH_X_MASK;
snprintf(info, info_len, "TH2[%d]: ",
- data->tsensor_index);
+ data->instance);
break;
case TSENSOR_PARAM_TH3:
- *p_reg = ((data->tsensor_index << 16) | SENSOR_CFG2);
+ *p_reg = ((data->instance << 16) | SENSOR_CFG2);
*p_sft = SENSOR_CFG2_TH3_SHIFT;
*p_msk = SENSOR_CFG_X_TH_X_MASK;
snprintf(info, info_len, "TH3[%d]: ",
- data->tsensor_index);
+ data->instance);
break;
default:
return -ENOENT;
@@ -712,7 +711,7 @@ static irqreturn_t tegra_tsensor_isr(int irq, void *arg_data)
spin_lock_irqsave(&data->tsensor_lock, flags);
- val = tsensor_readl(data, (data->tsensor_index << 16) | SENSOR_STATUS0);
+ val = tsensor_readl(data, (data->instance << 16) | SENSOR_STATUS0);
if (val & TSENSOR_SENSOR_X_STATUS0_0_INTR_MASK) {
new_state = get_ts_state(data);
@@ -726,7 +725,7 @@ static irqreturn_t tegra_tsensor_isr(int irq, void *arg_data)
queue_delayed_work(data->workqueue, &data->work, 0);
}
- tsensor_writel(data, val, (data->tsensor_index << 16) | SENSOR_STATUS0);
+ tsensor_writel(data, val, (data->instance << 16) | SENSOR_STATUS0);
spin_unlock_irqrestore(&data->tsensor_lock, flags);
@@ -1324,8 +1323,6 @@ static int tsensor_config_setup(struct tegra_tsensor_data *data)
{
unsigned int config0;
unsigned int i;
- unsigned int status_reg;
- unsigned int no_resp_count;
int err = 0;
for (i = 0; i < TSENSOR_COUNT; i++) {
@@ -1347,10 +1344,11 @@ static int tsensor_config_setup(struct tegra_tsensor_data *data)
/* Set STOP bit */
/* Set M and N values */
/* Enable HW features HW_FREQ_DIV_EN, THERMAL_RST_EN */
- config0 |= (((DEFAULT_TSENSOR_M & SENSOR_CFG0_M_MASK) <<
- SENSOR_CFG0_M_SHIFT) |
+ config0 |= (
+ ((DEFAULT_TSENSOR_M & SENSOR_CFG0_M_MASK) <<
+ SENSOR_CFG0_M_SHIFT) |
((DEFAULT_TSENSOR_N & SENSOR_CFG0_N_MASK) <<
- SENSOR_CFG0_N_SHIFT) |
+ SENSOR_CFG0_N_SHIFT) |
(1 << SENSOR_CFG0_OVERFLOW_INTR) |
(1 << SENSOR_CFG0_DVFS_INTR_SHIFT) |
(1 << SENSOR_CFG0_HW_DIV2_INTR_SHIFT) |
@@ -1363,57 +1361,14 @@ static int tsensor_config_setup(struct tegra_tsensor_data *data)
tsensor_threshold_setup(data, i);
}
- for (i = 0; i < TSENSOR_COUNT; i++) {
- config0 = tsensor_readl(data, ((i << 16) | SENSOR_CFG0));
- /* Enables interrupts and clears sensor stop */
- /*
- * Interrupts not enabled as software handling is not
- * needed in rev1 driver
- */
- /* Disable sensor stop bit */
- config0 &= ~(1 << SENSOR_CFG0_STOP_SHIFT);
- tsensor_writel(data, config0, ((i << 16) | SENSOR_CFG0));
- }
-
- /* Check if counters are getting updated */
- no_resp_count = 0;
+ /* Disable sensor stop bit */
+ config0 = tsensor_readl(data, (data->instance << 16) | SENSOR_CFG0);
+ config0 &= ~(1 << SENSOR_CFG0_STOP_SHIFT);
+ tsensor_writel(data, config0, (data->instance << 16) | SENSOR_CFG0);
- for (i = 0; i < TSENSOR_COUNT; i++) {
- /* if STOP bit is set skip this check */
- config0 = tsensor_readl(data, ((i << 16) | SENSOR_CFG0));
- if (!(config0 & (1 << SENSOR_CFG0_STOP_SHIFT))) {
- unsigned int loop_count = 0;
- do {
- status_reg = tsensor_readl(data,
- (i << 16) | SENSOR_STATUS0);
- if ((status_reg & (1 <<
- SENSOR_STATUS_AVG_VALID_SHIFT)) &&
- (status_reg & (1 <<
- SENSOR_STATUS_CURR_VALID_SHIFT))) {
- msleep(21);
- loop_count++;
- if (!(loop_count % 200))
- dev_err(data->hwmon_dev,
- " Warning: Tsensor Counter "
- "sensor%d not Valid yet.\n", i);
- if (loop_count > MAX_TSENSOR_LOOP1) {
- no_resp_count++;
- break;
- }
- }
- } while (!(status_reg &
- (1 << SENSOR_STATUS_AVG_VALID_SHIFT)) ||
- (!(status_reg &
- (1 << SENSOR_STATUS_CURR_VALID_SHIFT))));
- if (no_resp_count == TSENSOR_COUNT) {
- err = -ENODEV;
- goto skip_all;
- }
- }
- }
/* initialize tsensor chip coefficients */
get_chip_tsensor_coeff(data);
-skip_all:
+
return err;
}
@@ -1475,7 +1430,7 @@ static void tsensor_set_limits(
* same as TH1. Also, caller expected to pass
* (TH1 - hysteresis) as temp argument for this case */
th1_count = tsensor_readl(data,
- ((data->tsensor_index << 16) |
+ ((data->instance << 16) |
SENSOR_CFG1));
th_count = (th1_count > th_count) ?
(th1_count - th_count) :
@@ -1495,13 +1450,13 @@ static void tsensor_set_limits(
offset = SENSOR_CFG2;
break;
}
- config = tsensor_readl(data, ((data->tsensor_index << 16) | offset));
+ config = tsensor_readl(data, ((data->instance << 16) | offset));
dev_dbg(data->hwmon_dev, "%s: old config=0x%x, sft=%d, offset=0x%x\n",
__func__, config, sft, offset);
config &= ~(SENSOR_CFG_X_TH_X_MASK << sft);
config |= ((th_count & SENSOR_CFG_X_TH_X_MASK) << sft);
dev_dbg(data->hwmon_dev, "new config=0x%x\n", config);
- tsensor_writel(data, config, ((data->tsensor_index << 16) | offset));
+ tsensor_writel(data, config, ((data->instance << 16) | offset));
}
int tsensor_thermal_set_limits(struct tegra_tsensor_data *data,
@@ -1811,22 +1766,20 @@ static int __devinit tegra_tsensor_probe(struct platform_device *pdev)
}
/* fuse revisions less than TSENSOR_FUSE_REV1
- bypass tsensor driver init */
+ bypass tsensor driver init */
/* tsensor active instance decided based on fuse revision */
err = tegra_fuse_get_revision(&reg);
if (err)
goto err6;
/* check for higher revision done first */
- /* instance 0 is used for fuse revision
- TSENSOR_FUSE_REV2 onwards */
+ /* instance 0 is used for fuse revision TSENSOR_FUSE_REV2 onwards */
if (reg >= TSENSOR_FUSE_REV2)
- data->tsensor_index = TSENSOR_INSTANCE1;
- /* instance 1 is used for fuse revision
- TSENSOR_FUSE_REV1 till
- TSENSOR_FUSE_REV2 */
+ data->instance = TSENSOR_INSTANCE1;
+ /* instance 1 is used for fuse revision TSENSOR_FUSE_REV1 till
+ TSENSOR_FUSE_REV2 */
else if (reg >= TSENSOR_FUSE_REV1)
- data->tsensor_index = TSENSOR_INSTANCE2;
- pr_info("tsensor active instance=%d\n", data->tsensor_index);
+ data->instance = TSENSOR_INSTANCE2;
+ pr_info("tsensor active instance=%d\n", data->instance);
/* tegra tsensor - setup and init */
err = tegra_tsensor_setup(pdev);
@@ -1924,13 +1877,12 @@ static int tsensor_suspend(struct platform_device *pdev,
{
struct tegra_tsensor_data *data = platform_get_drvdata(pdev);
unsigned int config0;
- int i;
+
/* set STOP bit, else OVERFLOW interrupt seen in LP1 */
- for (i = 0; i < TSENSOR_COUNT; i++) {
- config0 = tsensor_readl(data, ((i << 16) | SENSOR_CFG0));
- config0 |= (1 << SENSOR_CFG0_STOP_SHIFT);
- tsensor_writel(data, config0, ((i << 16) | SENSOR_CFG0));
- }
+ config0 = tsensor_readl(data, ((data->instance << 16) | SENSOR_CFG0));
+ config0 |= (1 << SENSOR_CFG0_STOP_SHIFT);
+ tsensor_writel(data, config0, ((data->instance << 16) | SENSOR_CFG0));
+
/* save current settings before suspend, when STOP bit is set */
save_tsensor_regs(data);
tsensor_clk_enable(data, false);
@@ -1942,17 +1894,16 @@ static int tsensor_resume(struct platform_device *pdev)
{
struct tegra_tsensor_data *data = platform_get_drvdata(pdev);
unsigned int config0;
- int i;
+
tsensor_clk_enable(data, true);
/* restore current settings before suspend, no need
* to clear STOP bit */
restore_tsensor_regs(data);
+
/* clear STOP bit, after restoring regs */
- for (i = 0; i < TSENSOR_COUNT; i++) {
- config0 = tsensor_readl(data, ((i << 16) | SENSOR_CFG0));
- config0 &= ~(1 << SENSOR_CFG0_STOP_SHIFT);
- tsensor_writel(data, config0, ((i << 16) | SENSOR_CFG0));
- }
+ config0 = tsensor_readl(data, ((data->instance << 16) | SENSOR_CFG0));
+ config0 &= ~(1 << SENSOR_CFG0_STOP_SHIFT);
+ tsensor_writel(data, config0, ((data->instance << 16) | SENSOR_CFG0));
if (data->is_edp_supported)
schedule_delayed_work(&data->work, 0);
diff --git a/drivers/i2c/busses/i2c-tegra.c b/drivers/i2c/busses/i2c-tegra.c
index d28cd4e9d3d0..9bd06f2c4250 100644
--- a/drivers/i2c/busses/i2c-tegra.c
+++ b/drivers/i2c/busses/i2c-tegra.c
@@ -176,6 +176,8 @@ struct tegra_i2c_dev {
unsigned long last_bus_clk_rate;
u16 slave_addr;
bool is_clkon_always;
+ bool is_high_speed_enable;
+ u16 hs_master_code;
int (*arb_recovery)(int scl_gpio, int sda_gpio);
struct tegra_i2c_bus busses[1];
};
@@ -415,6 +417,7 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
i2c_writel(i2c_dev, val, I2C_CNFG);
i2c_writel(i2c_dev, 0, I2C_INT_MASK);
clk_set_rate(i2c_dev->clk, i2c_dev->last_bus_clk_rate * 8);
+ i2c_writel(i2c_dev, 0x3, I2C_CLK_DIVISOR);
if (!i2c_dev->is_dvc) {
u32 sl_cfg = i2c_readl(i2c_dev, I2C_SL_CNFG);
@@ -622,6 +625,10 @@ static int tegra_i2c_xfer_msg(struct tegra_i2c_bus *i2c_bus,
i2c_dev->io_header |= I2C_HEADER_CONT_ON_NAK;
if (msg->flags & I2C_M_RD)
i2c_dev->io_header |= I2C_HEADER_READ;
+ if (i2c_dev->is_high_speed_enable) {
+ i2c_dev->io_header |= I2C_HEADER_HIGHSPEED_MODE;
+ i2c_dev->io_header |= ((i2c_dev->hs_master_code & 0x7) << I2C_HEADER_MASTER_ADDR_SHIFT);
+ }
i2c_writel(i2c_dev, i2c_dev->io_header, I2C_TX_FIFO);
if (!(msg->flags & I2C_M_RD))
@@ -693,11 +700,13 @@ static int tegra_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[],
int ret = 0;
unsigned long flags;
- if (i2c_dev->is_suspended)
- return -EBUSY;
-
rt_mutex_lock(&i2c_dev->dev_lock);
+ if (i2c_dev->is_suspended) {
+ rt_mutex_unlock(&i2c_dev->dev_lock);
+ return -EBUSY;
+ }
+
if (i2c_dev->last_mux != i2c_bus->mux) {
tegra_pinmux_set_safe_pinmux_table(i2c_dev->last_mux,
i2c_dev->last_mux_len);
@@ -840,6 +849,8 @@ static int tegra_i2c_probe(struct platform_device *pdev)
i2c_dev->last_bus_clk_rate = be32_to_cpup(prop);
}
+ i2c_dev->is_high_speed_enable = plat->is_high_speed_enable;
+ i2c_dev->last_bus_clk_rate = plat->bus_clk_rate[0] ?: 100000;
i2c_dev->msgs = NULL;
i2c_dev->msgs_num = 0;
i2c_dev->controller_enabled = false;
@@ -847,6 +858,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
spin_lock_init(&i2c_dev->clk_lock);
i2c_dev->slave_addr = plat->slave_addr;
+ i2c_dev->hs_master_code = plat->hs_master_code;
i2c_dev->is_dvc = plat->is_dvc;
i2c_dev->arb_recovery = plat->arb_recovery;
init_completion(&i2c_dev->msg_complete);
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index b438838b46a7..a8fc74f730dd 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -745,4 +745,29 @@ config TOUCHSCREEN_TPS6507X
To compile this driver as a module, choose M here: the
module will be called tps6507x_ts.
+config TOUCHSCREEN_RM31080A
+ tristate "RAYDIUM_31080A based touchscreens"
+ depends on SPI_MASTER
+ help
+ Say Y here if you have a touchscreen interface using the
+ RAYDIUM_T31080A controller, and your board-specific initialization
+ code includes that in its table of SPI devices.
+
+ If unsure, say N (but it's safe to say "Y").
+
+ To compile this driver as a module, choose M here: the
+ module will be called RAYDIUM_31080A.
+
+config TOUCHSCREEN_SYN_RMI4_SPI
+ tristate "RMI4 SPI Support"
+ depends on SPI_MASTER
+ help
+ Say Y here if you want to support RMI4 devices connect
+ to an SPI bus.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rmi-spi.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 023d75578e23..9c42d2100b5c 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -62,3 +62,5 @@ obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_WM97XX_ZYLONITE) += zylonite-wm97xx.o
obj-$(CONFIG_TOUCHSCREEN_W90X900) += w90p910_ts.o
obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o
+obj-$(CONFIG_TOUCHSCREEN_RM31080A) += rm31080a_ts.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi4/
diff --git a/drivers/input/touchscreen/rm31080a_ts.c b/drivers/input/touchscreen/rm31080a_ts.c
new file mode 100644
index 000000000000..abe4752670e0
--- /dev/null
+++ b/drivers/input/touchscreen/rm31080a_ts.c
@@ -0,0 +1,1819 @@
+/*
+
+ * Raydium RM31080(T007) touchscreen (SPI bus) - Android version
+ *
+ * Copyright (C) 2011-2012 Raydium Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ *
+ * Version : 0.03
+ */
+
+//=============================================================================
+//INCLUDED FILES
+//=============================================================================
+#include <linux/input.h> // BUS_SPI
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/sched.h> // wake_up_process()
+#include <linux/kthread.h> // kthread_create()、kthread_run()
+#include <asm/uaccess.h> // copy_to_user(),
+#include <linux/miscdevice.h>
+#include <asm/siginfo.h> // siginfo
+#include <linux/rcupdate.h> // rcu_read_lock
+#include <linux/sched.h> // find_task_by_pid_type
+#include <linux/syscalls.h> // sys_clock_gettime()
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+#include <linux/earlysuspend.h>
+#endif
+
+
+
+#include "rm31080a_ts.h"
+
+//=============================================================================
+//DEFINITIONS
+//=============================================================================
+#define ENABLE_WORK_QUEUE
+#define ENABLE_REPORT_TO_UART
+#define ENABLE_RM31080_DEEP_SLEEP
+#define ENABLE_AUTO_SCAN
+//#define ENABLE_AUTO_FREQ
+//#define ENABLE_TS_THREAD
+//#define ENABLE_KERNEL_CALC //calculate touch point by kernel layer
+//#define ENABLE_TIMER_RELEASE_TOUCH
+//#define ENABLE_SPEED_TEST_FUNCTION
+//#define ENABLE_TIMER_DEBUG
+//#define ENABLE_TEST_AVERAGE
+
+#define MAX_SPI_FREQ_HZ 50000000
+#define TS_PEN_UP_TIMEOUT msecs_to_jiffies(50)
+
+#ifdef ENABLE_RAW_DATA_QUEUE
+ #define QUEUE_COUNT 128
+ #define RAW_DATA_LENGTH 2048
+
+ #define RM_SCAN_MODE_MANUAL 0x00
+ #define RM_SCAN_MODE_PREPARE_AUTO 0x01
+ #define RM_SCAN_MODE_AUTO_SCAN 0x02
+
+
+ #define RM_NEED_NONE 0x00
+ #define RM_NEED_TO_SEND_SCAN 0x01
+ #define RM_NEED_TO_READ_RAW_DATA 0x02
+ #define RM_NEED_TO_SEND_SIGNAL 0x03
+#endif
+
+
+#ifdef ENABLE_SPEED_TEST_FUNCTION
+ //#define TEST_SPI_READ_SPEED
+ //#define TEST_INT_TO_CLEAR_INT
+#endif
+
+#ifdef ENABLE_WORK_QUEUE
+#include <linux/workqueue.h>
+#endif
+
+#ifdef ENABLE_KERNEL_CALC
+#include "raydium_ts_main.h"
+#include "raydium_ts_spi_if.h"
+#include "raydium_ts_t007.h"
+#endif
+
+//=============================================================================
+//STRUCTURE DECLARATION
+//=============================================================================
+struct rm31080a_ts_para {
+ unsigned long ulHalPID;
+ bool bInitFinish;
+ bool bCalcFinish;
+ bool bEnableScriber;
+ bool bEnableAutoScan;
+ bool bIsSuspended;
+ struct mutex mutex;
+ #ifdef ENABLE_WORK_QUEUE
+ struct workqueue_struct *rm_workqueue;
+ struct work_struct rm_work;
+ bool bIsWorkQueueExecuting;
+ #endif
+ #ifdef ENABLE_TIMER_DEBUG
+ u32 u32WaitHalCount;
+ #endif
+ #ifdef ENABLE_RAW_DATA_QUEUE
+ u8 u8ScanModeState;
+ #endif
+};
+
+struct rm31080_ts {
+ const struct rm31080_bus_ops *bops;
+ struct device *dev;
+ struct input_dev *input;
+ #ifdef ENABLE_TS_THREAD
+ struct task_struct *ts_task;
+ #endif
+ #ifdef ENABLE_TIMER_RELEASE_TOUCH
+ struct timer_list timer;
+ #endif
+ #ifdef ENABLE_TIMER_DEBUG
+ struct timer_list timer_debug;
+ #endif
+ unsigned int irq;
+ bool disabled;
+ bool suspended;
+ char phys[32];
+ struct mutex access_mutex;
+ #if defined(CONFIG_HAS_EARLYSUSPEND)
+ struct early_suspend early_suspend;
+ #endif
+};
+
+struct rm31080_bus_ops {
+ u16 bustype;
+ int (*read)(struct device *dev, u8 reg);
+ int (*multi_read)(struct device *dev, u8 first_reg, u8 count, u16 *buf);
+ int (*write)(struct device *dev, u8 reg, u16 val);
+};
+
+#ifdef ENABLE_RAW_DATA_QUEUE
+struct rm31080_queue_info {
+ u8 (*pQueue)[RAW_DATA_LENGTH];
+ u16 u16Front;
+ u16 u16Rear;
+};
+#endif
+
+//=============================================================================
+//GLOBAL VARIABLES DECLARATION
+//=============================================================================
+struct input_dev *g_input_dev;
+struct spi_device *g_spi;
+struct rm31080a_ts_para g_stTs;
+
+#ifdef ENABLE_RAW_DATA_QUEUE
+struct rm31080_queue_info g_stQ;
+#endif
+//=============================================================================
+//FUNCTION DECLARATION
+//=============================================================================
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+static void rm31080_early_suspend(struct early_suspend *es);
+static void rm31080_early_resume(struct early_suspend *es);
+#endif
+//=============================================================================
+// Description:
+// Debug function: test speed.
+// Input:
+// N/A
+// Output:
+// 1:succeed
+// 0:failed
+//=============================================================================
+#ifdef ENABLE_SPEED_TEST_FUNCTION
+void my_calc_time(int iStart)
+{
+ static volatile unsigned int u32Max = UINT_MAX;
+ #if 0
+ char tbuf[50];
+ unsigned long long t;
+ unsigned long nanosec_rem;
+ t = cpu_clock(u32Max);
+ nanosec_rem = do_div(t, 1000000000);
+ sprintf(tbuf, "<%5lu.%06lu> ",
+ (unsigned long) t,
+ nanosec_rem / 1000);
+ tbuf[14]=0;
+ printk("%s\n",tbuf);
+ #endif
+
+ static long iTimebuffer[1000];
+ static unsigned long long t1,t2;
+ unsigned long nanosec_rem;
+ static int iIndex=0;
+
+ if (iStart)
+ {
+ t1 = cpu_clock(u32Max);
+ return;
+ }
+ else
+ t2 = cpu_clock(u32Max);
+
+ t2 = t2 - t1;
+
+ nanosec_rem = do_div(t2, 1000000000);
+
+ if (t2) //more than 1 Second
+ {
+ iTimebuffer[iIndex] = 999999;
+ }
+ else
+ {
+ iTimebuffer[iIndex] = nanosec_rem/1000; //micro second
+ }
+
+ iIndex ++;
+ if (iIndex==1000)
+ {
+ for(iIndex = 0;iIndex < 1000;iIndex++)
+ {
+ printk(" %04d,%06d\n",iIndex,(u32)iTimebuffer[iIndex]);
+ }
+ iIndex =0;
+ }
+
+
+}
+#endif //ENABLE_SPEED_TEST_FUNCTION
+//=============================================================================
+// Description:
+// RM31080 spi interface.
+// Input:
+// N/A
+// Output:
+// 1:succeed
+// 0:failed
+//=============================================================================
+int rm31080_spi_read(u8 u8addr, u8 *rxbuf, size_t len)
+{
+ static DEFINE_MUTEX(lock);
+
+ int status;
+ struct spi_message message;
+ struct spi_transfer x[2];
+
+ if (!mutex_trylock(&lock))
+ {
+ printk("Raydium TS: rm31080_spi_read trylock fail\n");
+ return -EINVAL;
+ }
+
+ spi_message_init(&message);
+ memset(x, 0, sizeof x);
+
+ u8addr |= 0x80;
+ x[0].len = 1;
+ x[0].tx_buf = &u8addr;
+ spi_message_add_tail(&x[0], &message);
+
+ x[1].len = len;
+ x[1].rx_buf = rxbuf;
+ spi_message_add_tail(&x[1], &message);
+
+ status = spi_sync(g_spi, &message);
+
+ mutex_unlock(&lock);
+ return status; // 0 = succeed
+}
+
+int rm31080_spi_write(u8 *txbuf, size_t len)
+{
+ return spi_write(g_spi, txbuf, len);
+ #if 0
+ struct spi_transfer t = {
+ .tx_buf = txbuf,
+ .len = len,
+ };
+ struct spi_message m;
+
+ spi_message_init(&m);
+ spi_message_add_tail(&t, &m);
+ return spi_sync(spi, &m);
+ #endif
+}
+
+
+
+#if 0
+static int rm31080_spi_burst_read(u8 u8Addr,u8 u8Value)
+{
+ return 1;
+}
+
+static int rm31080_spi_burst_write(u8 u8Addr,u8 u8Value)
+{
+ return 1;
+}
+#endif
+static int rm31080_spi_byte_read(u8 u8Addr,u8 *pu8Value)
+{
+ int iErrorCode;
+ iErrorCode = rm31080_spi_read(u8Addr,pu8Value,1);
+ if (iErrorCode != 0)
+ {
+ return 0;//fail
+ }
+ return 1;
+}
+
+
+static int rm31080_spi_byte_write(u8 u8Addr,u8 u8Value)
+{
+ int iErrorCode;
+ u8 buf[2];
+ buf[0] = u8Addr;
+ buf[1] = u8Value;
+
+ iErrorCode = rm31080_spi_write(buf, 2);
+
+ if (iErrorCode != 0)
+ {
+ printk("rm31080_spi_write_byte failed:Reg=%x",u8Addr);
+ return 0;//fail
+ }
+ return 1;
+}
+//=============================================================================
+// Description:
+// RM31080 control functions.
+// Input:
+// N/A
+// Output:
+// 1:succeed
+// 0:failed
+//=============================================================================
+#ifdef ENABLE_RAW_DATA_QUEUE
+
+#define RM31080_REG_01 0x01
+#define RM31080_REG_02 0x02
+#define RM31080_REG_09 0x09
+#define RM31080_REG_0E 0x0E
+#define RM31080_REG_10 0x10
+#define RM31080_REG_11 0x11
+#define RM31080_REG_1F 0x1F
+#define RM31080_REG_40 0x40
+#define RM31080_REG_41 0x41
+#define RM31080_REG_80 0x80
+#define RM31080_REG_F2 0xF2
+
+#define RM31080_RAW_DATA_LENGTH 1530
+static int rm31080_ctrl_clear_int(void)
+{
+ u8 u8Flag;
+ return rm31080_spi_byte_read(RM31080_REG_F2,&u8Flag);
+}
+
+#ifdef ENABLE_AUTO_SCAN
+#if 0
+void rm31080_ctrl_auto_mode_init(void)
+{
+ WriteSensor(REG_YACTIVEH, 0x04);
+ WriteSensor(REG_YACTIVEL, 0x38);
+ WriteSensor(0x0F, 0x88);
+ //please check [0x0f]=0x88 ,if auto scan can't work.
+}
+void rm31080_ctrl_analog_init(void)
+{
+ WriteSensor(0x7F, 0x01); //Bank1
+ WriteSensor(0x48, 0x00);
+ WriteSensor(0x49, 0x80);
+ WriteSensor(0x2F, 0x80);
+ WriteSensor(0x7F, 0x00); //Bank0
+ WriteSensor(0x6B, 0xF1);
+}
+#endif //if 0
+
+void rm31080_ctrl_enter_auto_mode(void)
+{
+ #if 0
+ //1.Disable digit_filter
+ //rm31080_spi_byte_write(RM31080_REG_40, 0x0F );
+ //rm31080_spi_byte_write(RM31080_REG_41, 0xFF );
+ rm31080_spi_byte_write(RM31080_REG_1F, 0x00 | 0x05 );//REG_DIGITAL_FILTER
+ rm31080_spi_byte_write(RM31080_REG_0E, 0x38 | 0x02 );//REG_SENSING
+ rm31080_spi_byte_write(RM31080_REG_10, 0x00 ); // REG_SEQ
+
+ //2.Enable Analog_filter
+ rm31080_spi_byte_write(0x7F, 0x01);//bank 1
+ rm31080_spi_byte_write(0x09, 0x19);
+ rm31080_spi_byte_write(0x43, 0xFF);
+ rm31080_spi_byte_write(0x7F, 0x00);//bank 0
+ #endif
+
+ //3.Enable auto scan
+ rm31080_spi_byte_write(RM31080_REG_09, 0x10 | 0x40);
+}
+
+void rm31080_ctrl_leave_auto_mode(void)
+{
+ //1.Disable auto scan
+ rm31080_spi_byte_write(RM31080_REG_09, 0x00);
+
+ #if 0
+ //2.Disable Analog_filter
+ rm31080_spi_byte_write(0x7F, 0x01);//bank 1
+ rm31080_spi_byte_write(0x09, 0xD9);
+ rm31080_spi_byte_write(0x43, 0x44);
+ rm31080_spi_byte_write(0x7F, 0x00);//bank 0
+
+ //3.Enable Digit_filter
+ rm31080_spi_byte_write(RM31080_REG_1F, 0x00 | 0x07 ); //REG_DIGITAL_FILTER
+ rm31080_spi_byte_write(RM31080_REG_0E, 0x38 | 0x04 ); //REG_SENSING
+ rm31080_spi_byte_write(RM31080_REG_10, 0x10 ); //REG_SEQ
+ #endif
+
+}
+#endif //ENABLE_AUTO_SCAN
+
+#ifdef ENABLE_RM31080_DEEP_SLEEP
+static int rm31080_ctrl_suspend(void)
+{
+ //Flow designed by Roger 20110930
+ //rm31080_ts_send_signal(g_stTs.ulHalPID,RM_SIGNAL_SUSPEND);
+ g_stTs.bInitFinish = 0;
+ msleep(8);
+ rm31080_ctrl_clear_int();
+ //disable auto scan
+ rm31080_spi_byte_write(RM31080_REG_09,0x00);
+ #if 1 //by valentine
+ rm31080_spi_byte_write(RM31080_REG_10,0x14);
+ rm31080_spi_byte_write(RM31080_REG_11,0x17);
+ msleep(15);
+ #endif
+ rm31080_spi_byte_write(RM31080_REG_11,0x06);
+ return 1;
+}
+#endif
+
+static int rm31080_ctrl_scan_start(void)
+{
+ return rm31080_spi_byte_write(RM31080_REG_11,0x17);
+}
+
+static u32 rm31080_ctrl_configure(void)
+{
+ u32 u32Flag;
+
+ switch (g_stTs.u8ScanModeState)
+ {
+ case RM_SCAN_MODE_MANUAL:
+ u32Flag = RM_NEED_TO_SEND_SCAN | RM_NEED_TO_READ_RAW_DATA | RM_NEED_TO_SEND_SIGNAL;
+ break;
+ #ifdef ENABLE_AUTO_SCAN
+ case RM_SCAN_MODE_PREPARE_AUTO:
+ rm31080_ctrl_enter_auto_mode();
+ g_stTs.u8ScanModeState = RM_SCAN_MODE_AUTO_SCAN;
+ u32Flag = RM_NEED_NONE;
+ break;
+ case RM_SCAN_MODE_AUTO_SCAN:
+ rm31080_ctrl_leave_auto_mode();
+ rm31080_ctrl_scan_start(); //20111213 : fixed bug:wake up from AutoScan needs scan_start() twice
+ g_stTs.u8ScanModeState = RM_SCAN_MODE_MANUAL;
+ u32Flag = RM_NEED_TO_SEND_SCAN | RM_NEED_TO_READ_RAW_DATA | RM_NEED_TO_SEND_SIGNAL;
+ break;
+ #endif //ENABLE_AUTO_SCAN
+ default:
+ u32Flag = RM_NEED_NONE;
+ break;
+ }
+
+ return u32Flag;
+}
+
+static void rm31080_enter_manual_mode(void)
+{
+ flush_workqueue(g_stTs.rm_workqueue);
+
+ if (g_stTs.u8ScanModeState == RM_SCAN_MODE_MANUAL)
+ return;
+
+ if(g_stTs.u8ScanModeState == RM_SCAN_MODE_PREPARE_AUTO)
+ {
+ g_stTs.u8ScanModeState = RM_SCAN_MODE_MANUAL;
+ return;
+ }
+
+ if(g_stTs.u8ScanModeState == RM_SCAN_MODE_AUTO_SCAN)
+ {
+ rm31080_ctrl_leave_auto_mode();
+ g_stTs.u8ScanModeState = RM_SCAN_MODE_MANUAL;
+ msleep(10);
+ }
+
+}
+
+static int rm31080_ctrl_read_raw_data(u8 *p)
+{
+ int iRet;
+ iRet = rm31080_spi_byte_write(RM31080_REG_01,0x10);
+ if (iRet)
+ iRet = rm31080_spi_byte_write(RM31080_REG_02,0x00);
+
+ if (iRet)
+ {
+ iRet = rm31080_spi_read(RM31080_REG_80,p,RM31080_RAW_DATA_LENGTH); //return 0 =succeed
+ iRet = !iRet;
+ }
+
+ if(!iRet)
+ {
+ printk("rm31080 read raw data failed\n");
+ }
+
+ return iRet;
+}
+#endif //ENABLE_RAW_DATA_QUEUE
+//=============================================================================
+// Description:
+// Queuing functions.
+// Input:
+// N/A
+// Output:
+// 0:succeed
+// others:error code
+//=============================================================================
+#ifdef ENABLE_RAW_DATA_QUEUE
+
+static void rm31080_queue_reset(void)
+{
+ g_stQ.u16Rear = 0;
+ g_stQ.u16Front = 0;
+}
+
+static int rm31080_queue_init(void)
+{
+ rm31080_queue_reset();
+ g_stQ.pQueue = kmalloc(QUEUE_COUNT * RAW_DATA_LENGTH, GFP_KERNEL);
+ if (g_stQ.pQueue == NULL)
+ {
+ printk("rm31080_queue_init failed\n");
+ return -ENOMEM;
+ }
+ //printk("Queue Addr:%x\n",(unsigned int)(g_stQ.pQueue));
+ return 0;
+}
+
+static void rm31080_queue_free(void)
+{
+ if (!g_stQ.pQueue)
+ return;
+ kfree(g_stQ.pQueue);
+ g_stQ.pQueue = NULL;
+}
+//=============================================================================
+// Description:
+// About full/empty buffer distinction,
+// There are a number of solutions like:
+// 1.Always keep one slot open.
+// 2.Use a fill count to distinguish the two cases.
+// 3.Use read and write counts to get the fill count from.
+// 4.Use absolute indices.
+// we chose "keep one slot open" to make it simple and robust
+// and also avoid race condition.
+// Input:
+// N/A
+// Output:
+// 1:empty
+// 0:not empty
+//=============================================================================
+static int rm31080_queue_is_empty(void)
+{
+ if (g_stQ.u16Rear == g_stQ.u16Front)
+ return 1;
+ return 0;
+}
+//=============================================================================
+// Description:
+// check queue full.
+// Input:
+// N/A
+// Output:
+// 1:full
+// 0:not full
+//=============================================================================
+static int rm31080_queue_is_full(void)
+{
+ if (g_stQ.u16Rear + 1 == g_stQ.u16Front)
+ return 1;
+
+ if ((g_stQ.u16Rear == (QUEUE_COUNT - 1)) &&
+ (g_stQ.u16Front == 0))
+ return 1;
+
+ return 0;
+}
+#if 0 //don't delete, for debug
+static int rm31080_queue_get_current_count(void)
+{
+ if (g_stQ.u16Rear >= g_stQ.u16Front)
+ return g_stQ.u16Rear - g_stQ.u16Front;
+
+ return (QUEUE_COUNT - g_stQ.u16Front) + g_stQ.u16Rear;
+}
+#endif
+static void *rm31080_enqueue_start(void)
+{
+ if (!g_stQ.pQueue) //error handling for no memory
+ return NULL;
+
+ if (!rm31080_queue_is_full())
+ return &g_stQ.pQueue[g_stQ.u16Rear];
+
+ printk("rm31080 Queue full with Queue Count:%d\n",QUEUE_COUNT);
+ return NULL;
+}
+static void rm31080_enqueue_finish(void)
+{
+ if (g_stQ.u16Rear == (QUEUE_COUNT - 1)) g_stQ.u16Rear = 0;
+ else g_stQ.u16Rear ++;
+}
+static void *rm31080_dequeue_start(void)
+{
+ if (!rm31080_queue_is_empty())
+ return &g_stQ.pQueue[g_stQ.u16Front];
+
+ return NULL;
+}
+static void rm31080_dequeue_finish(void)
+{
+ if (g_stQ.u16Front == (QUEUE_COUNT - 1)) g_stQ.u16Front = 0;
+ else g_stQ.u16Front ++;
+}
+
+static long rm31080_queue_read_raw_data(u8 *p,u32 u32Len)
+{
+ u8 *pQueue;
+ u32 u32Ret;
+ pQueue = rm31080_dequeue_start();
+ if (!pQueue)
+ return 0;
+
+
+ u32Ret = copy_to_user(p, pQueue, u32Len);
+ if (u32Ret != 0)
+ return 0;
+
+ rm31080_dequeue_finish();
+ return 1;
+
+}
+#endif //ENABLE_RAW_DATA_QUEUE
+
+#ifdef ENABLE_AUTO_FREQ
+void raydium_auto_freq()
+{
+ g_stTs.bInitFinish = 0;
+ msleep(10);
+ rm31080_ctrl_clear_int();
+
+ //roger_auto_freq_detection();
+
+ g_stTs.bInitFinish = 1;
+ rm31080_ctrl_scan_start();
+
+}
+#endif //ENABLE_TEST_AUTO_FREQ
+//=============================================================================
+#ifdef ENABLE_AUTO_SCAN
+void raydium_change_scan_mode(u8 u8TouchCount)
+{
+ static u32 u32NoTouchCount = 0;
+ if (u8TouchCount)
+ {
+ u32NoTouchCount = 0;
+ return;
+ }
+ if (u32NoTouchCount < 100)
+ {
+ u32NoTouchCount++;
+ }
+ else if (g_stTs.u8ScanModeState == RM_SCAN_MODE_MANUAL)
+ {
+ #ifdef ENABLE_AUTO_FREQ
+ raydium_auto_freq();
+ #else
+ //printk("===1.perpare enter to AutoScan\n");
+ if (g_stTs.bEnableAutoScan)
+ g_stTs.u8ScanModeState = RM_SCAN_MODE_PREPARE_AUTO;
+ #endif
+ u32NoTouchCount = 0;
+ }
+}
+#endif //ENABLE_AUTO_SCAN
+//=============================================================================
+//report touch data for scriber
+//
+//=============================================================================
+#ifdef ENABLE_REPORT_TO_UART
+void raydium_report_to_uart_printf(unsigned char *ucData,unsigned char ucCount)
+{
+ unsigned char i;
+ for (i=0;i<ucCount;i++)
+ {
+ printk("%02X",ucData[i]);
+ }
+ printk("\n");
+}
+void raydium_report_to_uart(void *p)
+{
+ unsigned char ucData[1+1+(4*12)+1];//1=Tag,1=Touch count,4=(xH xL ,yH yL) ,12=max point,1=Check sum
+ rm_touch_event *spTP;
+ unsigned short usX,usY;
+ int i,j;
+
+ if (g_stTs.bEnableScriber==0)
+ return;
+
+ spTP = (rm_touch_event *)p;
+
+ ucData[0] = 0x8E;
+ ucData[1] = spTP->ucTouchCount;
+ j=2;
+ for (i=0;i<spTP->ucTouchCount;i++)
+ {
+ usX = spTP->usX[i] + 1; //1~1536
+ usY = spTP->usY[i] + 1; //1~960
+ ucData[j++] = ((usX>>8) & 0xFF) | ( spTP->ucID[i] << 3 );//add id
+ ucData[j++] = ((usX ) & 0xFF);
+ ucData[j++] = ((usY>>8) & 0xFF);
+ ucData[j++] = ((usY ) & 0xFF);
+ }
+
+ //check sum
+ ucData[j] = 0;
+ for (i=0;i<j;i++)
+ {
+ ucData[j] += ucData[i];
+ }
+ ucData[j] = 0x100 - ucData[j];
+ j++;
+
+ //print
+ raydium_report_to_uart_printf(ucData,j);
+ if (spTP->ucTouchCount==0) //send more , to avoid losing
+ {
+ raydium_report_to_uart_printf(ucData,j);
+ raydium_report_to_uart_printf(ucData,j);
+ }
+}
+#endif
+//=============================================================================
+void raydium_report_pointer(void *p)
+{
+ static unsigned char ucLastTouchCount = 0;
+ int i;
+ int iCount;
+ rm_touch_event *spTP;
+ spTP = (rm_touch_event *)p;
+
+ iCount = max(ucLastTouchCount,spTP->ucTouchCount);
+ if (iCount)
+ {
+ for (i=0;i<iCount;i++)
+ {
+ //if (i==5)break; //due to the "pointer location" can't support great than 5 points
+ if (i==10)break; //due to the "touch test" can't support great than 10 points
+
+ if (i<spTP->ucTouchCount)
+ {
+ input_report_abs(g_input_dev, ABS_MT_TRACKING_ID,spTP->ucID[i]);
+ //input_report_abs(g_input_dev, ABS_MT_TOUCH_MAJOR,10);
+ input_report_abs(g_input_dev, ABS_MT_TOUCH_MAJOR,100);
+ if (spTP->usX[i] >= (RM_INPUT_RESOLUTION_X-1))
+ input_report_abs(g_input_dev, ABS_MT_POSITION_X,(RM_INPUT_RESOLUTION_X-1)-1);//fixed bug: OS scale fail
+ else
+ input_report_abs(g_input_dev, ABS_MT_POSITION_X,spTP->usX[i]);
+
+ if (spTP->usY[i] >= (RM_INPUT_RESOLUTION_Y-1))
+ input_report_abs(g_input_dev, ABS_MT_POSITION_Y,(RM_INPUT_RESOLUTION_Y-1)-1);//fixed bug: OS scale fail
+ else
+ input_report_abs(g_input_dev, ABS_MT_POSITION_Y,spTP->usY[i]);
+ }
+ input_mt_sync(g_input_dev);
+ }
+ ucLastTouchCount = spTP->ucTouchCount;
+ input_report_key(g_input_dev, BTN_TOUCH, spTP->ucTouchCount > 0);
+ input_sync(g_input_dev);
+ #ifdef ENABLE_REPORT_TO_UART
+ raydium_report_to_uart(p);
+ #endif
+
+
+ }
+
+ #if 0
+ if (spTP->ucTouchCount) {
+ for(i=0;i<spTP->ucTouchCount;i++)
+ {
+ printk("Touch:%d:%04d,%04d:ID:%d\n",spTP->ucTouchCount,spTP->usX[i],spTP->usY[i],spTP->ucID[i]);
+ }
+ }
+ else
+ {
+ //printk("x");
+ }
+ #endif
+
+ #ifdef ENABLE_AUTO_SCAN
+ raydium_change_scan_mode(spTP->ucTouchCount);
+ #endif
+}
+
+
+//=============================================================================
+// release touch event
+//
+//=============================================================================
+#ifdef ENABLE_TIMER_RELEASE_TOUCH
+static void rm31080_touch_release(void)
+{
+ rm_touch_event stTP;
+ stTP.ucTouchCount = 0;
+ raydium_report_pointer((void*)&stTP);
+}
+
+static void rm31080_timer(unsigned long handle)
+{
+ //struct rm31080_ts *ts = (void *)handle;
+ //mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
+ //mod_timer(&ts->timer, jiffies + HZ);
+ rm31080_touch_release();
+}
+#endif //ENABLE_TIMER_RELEASE_TOUCH
+
+#ifdef ENABLE_TIMER_DEBUG
+static void rm31080_debug_timer(unsigned long handle)
+{
+ struct rm31080_ts *ts = (void *)handle;
+ mod_timer(&ts->timer_debug,jiffies + msecs_to_jiffies(10*1000));
+ printk("Wait HAL Count in 10 second:%d\n",g_stTs.u32WaitHalCount);
+ g_stTs.u32WaitHalCount = 0;
+}
+#endif //ENABLE_TIMER_DEBUG
+//=============================================================================
+
+
+//=============================================================================
+int rm31080_ts_send_signal(int pid,int iInfo)
+{
+ struct siginfo info;
+ struct task_struct *t;
+ int ret;
+
+ /* send the signal */
+ memset(&info, 0, sizeof(struct siginfo));
+ info.si_signo = RM_TS_SIGNAL;
+ info.si_code = SI_QUEUE; // this is bit of a trickery: SI_QUEUE is normally used by sigqueue from user space,
+ // and kernel space should use SI_KERNEL. But if SI_KERNEL is used the real_time data
+ // is not delivered to the user space signal handler function.
+ info.si_int = iInfo; //real time signals may have 32 bits of data.
+
+ rcu_read_lock();
+ t = find_task_by_vpid(pid);
+ if(t == NULL){
+ printk("no such pid\n");
+ rcu_read_unlock();
+ return -ENODEV;
+ }
+ rcu_read_unlock();
+ ret = send_sig_info(RM_TS_SIGNAL, &info, t); //send the signal
+ if (ret < 0) {
+ printk("error sending signal\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+
+//=============================================================================
+static void __rm31080_enable(struct rm31080_ts *ts)
+{
+ enable_irq(ts->irq);
+}
+
+static void __rm31080_disable(struct rm31080_ts *ts)
+{
+ disable_irq(ts->irq);
+
+ //if (del_timer_sync(&ts->timer))
+ // rm31080_touch_release();
+}
+
+static void vtest_toggle(struct rm31080_ts *ts, bool disable)
+{
+ mutex_lock(&ts->input->mutex);
+
+ if (!ts->suspended && ts->input->users != 0)
+ {
+
+ if (disable) {
+ if (ts->disabled)
+ __rm31080_enable(ts);
+ } else {
+ if (!ts->disabled)
+ __rm31080_disable(ts);
+ }
+ }
+
+ ts->disabled = disable;
+
+ mutex_unlock(&ts->input->mutex);
+}
+
+static ssize_t vtest_disable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rm31080_ts *ts = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%u\n", ts->disabled);
+}
+
+static ssize_t vtest_disable_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rm31080_ts *ts = dev_get_drvdata(dev);
+ unsigned long val;
+ int error;
+
+ error = strict_strtoul(buf, 10, &val);
+ if (error)
+ return error;
+
+ vtest_toggle(ts, val);
+
+ return count;
+}
+static DEVICE_ATTR(disable, 0664, vtest_disable_show, vtest_disable_store);
+static struct attribute *vtest_attributes[] = {
+ &dev_attr_disable.attr,
+ NULL
+};
+
+static const struct attribute_group vtest_attr_group = {
+ .attrs = vtest_attributes,
+};
+
+
+
+static int rm31080_input_open(struct input_dev *input)
+{
+ struct rm31080_ts *ts = input_get_drvdata(input);
+
+ /* protected by input->mutex */
+ if (!ts->disabled && !ts->suspended)
+ __rm31080_enable(ts);
+
+ return 0;
+}
+
+static void rm31080_input_close(struct input_dev* input)
+{
+ struct rm31080_ts *ts = input_get_drvdata(input);
+
+ /* protected by input->mutex */
+ if (!ts->disabled && !ts->suspended)
+ __rm31080_disable(ts);
+}
+
+
+//=============================================================================
+#ifdef ENABLE_TS_THREAD
+static int rm31080_ts_thread(void *handle)
+{
+ struct rm31080_ts *ts = (void *)handle;
+ struct task_struct *tsk = current;
+ struct sched_param param = { .sched_priority = 1 }; //sched_get_priority_min(),sched_get_priority_max()
+
+ sched_setscheduler(tsk, SCHED_FIFO, &param);
+
+ //set_freezable();
+ while (!kthread_should_stop())
+ {
+ //if (ucb->irq_pending) {
+ // ucb->irq_pending = 0;
+ // handle_pending_irq(ucb);
+ //}
+
+ #ifdef ENABLE_KERNEL_CALC
+ ts_main_calc();
+ #endif
+
+ msleep(10000);
+
+ #if 0
+ if (ts_pen_up(ucb->ac97)) {
+ ts_irq_enable(ucb->ac97);
+
+ /*
+ * If we spat out a valid sample set last time,
+ * spit out a "pen off" sample here.
+ */
+ if (valid) {
+ ts_event_release(ucb->ts_idev);
+ valid = 0;
+ }
+
+ timeout = MAX_SCHEDULE_TIMEOUT;
+ } else {
+ valid = 1;
+ ts_evt_add(ucb->ts_idev, p, x, y);
+ timeout = msecs_to_jiffies(10);
+ }
+
+ wait_event_freezable_timeout(ucb->ts_wait,
+ ucb->irq_pending || ucb->ts_restart ||
+ kthread_should_stop(), timeout);
+ #endif
+ }
+
+ /* Send the "pen off" if we are stopping with the pen still active */
+ //if (valid)
+ // ts_event_release(ucb->ts_idev);
+
+ ts->ts_task = NULL;
+ return 0;
+}
+#endif //ENABLE_TS_THREAD
+
+#ifdef ENABLE_TEST_AVERAGE //only for test
+#define _AVERAGE_COUNT 2
+s8 g_bAverageBuf[_AVERAGE_COUNT][2048];
+int test_soft_average(s8 *pSource)
+{
+ static u8 u8AverageIndex = 0;
+ static u8 u8StartAverage = 0;
+ u16 i,j;
+ s16 s16Sum;
+
+ for (i = 0; i < RM31080_RAW_DATA_LENGTH; i++) //RM31080_RAW_DATA_LENGTH =1530
+ g_bAverageBuf[u8AverageIndex][i] = pSource[i] - 0x80;
+ u8AverageIndex++;
+
+ if (u8AverageIndex == _AVERAGE_COUNT)
+ {
+ u8StartAverage = 1;
+ u8AverageIndex = 0;
+ }
+ #if 1
+ else u8StartAverage = 0;
+ #endif
+
+ if (u8StartAverage)
+ {
+ for (i = 0; i < RM31080_RAW_DATA_LENGTH; i++)
+ {
+ s16Sum = 0;
+ for (j = 0;j < _AVERAGE_COUNT; j++)
+ s16Sum += g_bAverageBuf[j][i];
+ pSource[i] = (s16Sum /_AVERAGE_COUNT) + 0x80;
+ }
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+
+#ifdef ENABLE_WORK_QUEUE
+//1.2
+static void rm_work_handler(struct work_struct *work)
+{
+ void *pKernelBuffer;
+ u32 u32Flag;
+ int iRet;
+
+
+ #if 1
+ if(g_stTs.bIsSuspended)
+ {
+ printk("rm_work_handler stops after suspend\n");
+ return;
+ }
+ #else
+ while(g_stTs.bIsSuspended)
+ {
+ msleep(1);
+ }
+ #endif
+ g_stTs.bIsWorkQueueExecuting = 1;
+
+
+ iRet = rm31080_ctrl_clear_int();
+
+
+ #ifdef TEST_INT_TO_CLEAR_INT
+ my_calc_time(0);
+ #endif
+
+ #ifdef ENABLE_TIMER_DEBUG
+ u32 u32CurrentQueueCount = rm31080_queue_get_current_count();
+ if (u32CurrentQueueCount>g_stTs.u32WaitHalCount)
+ g_stTs.u32WaitHalCount = u32CurrentQueueCount;
+ #endif
+
+
+ u32Flag = rm31080_ctrl_configure();
+
+
+
+ if(u32Flag | RM_NEED_TO_SEND_SCAN)
+ {
+ rm31080_ctrl_scan_start();
+ }
+
+ if(u32Flag | RM_NEED_TO_READ_RAW_DATA)
+ {
+ pKernelBuffer = rm31080_enqueue_start();
+ if (pKernelBuffer)
+ {
+ iRet = rm31080_ctrl_read_raw_data((u8 *)pKernelBuffer);
+ #ifdef ENABLE_TEST_AVERAGE
+ if (iRet)
+ {
+ iRet = test_soft_average((s8 *)pKernelBuffer);
+ }
+ #endif
+ if (iRet)
+ {
+ rm31080_enqueue_finish();
+ }
+ }
+ }
+
+ if(u32Flag | RM_NEED_TO_SEND_SIGNAL)
+ {
+ if(g_stTs.bCalcFinish)
+ {
+ g_stTs.bCalcFinish = 0;
+ rm31080_ts_send_signal(g_stTs.ulHalPID,RM_SIGNAL_INTR);
+ }
+ }
+ g_stTs.bIsWorkQueueExecuting = 0;
+}
+#endif
+
+static irqreturn_t rm31080_irq(int irq, void *handle)
+{
+
+ //struct rm31080_ts *ts = handle;
+ if (!g_stTs.bInitFinish)
+ {
+ //printk("Raydium : spi irq - Ignored when Init.\n");
+ return IRQ_HANDLED;
+ }
+
+ #ifdef TEST_INT_TO_CLEAR_INT
+ my_calc_time(1);
+ #endif
+
+ #ifdef ENABLE_WORK_QUEUE
+ //schedule_work(&g_stTs.rm_work);
+ queue_work(g_stTs.rm_workqueue, &g_stTs.rm_work);
+ #endif
+
+ //if (to do :touch point has been sent)
+ // mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
+
+ return IRQ_HANDLED;
+}
+//=============================================================================
+static void rm31080_init_ts_structure_part(void)
+{
+ g_stTs.bInitFinish = 0;
+ g_stTs.bCalcFinish = 0;
+ g_stTs.bEnableScriber = 0;
+ g_stTs.bIsSuspended = 0;
+ g_stTs.bEnableAutoScan = 1;//default
+
+ #ifdef ENABLE_TIMER_DEBUG
+ g_stTs.u32WaitHalCount = 0;
+ #endif
+
+ #ifdef ENABLE_RAW_DATA_QUEUE
+ g_stTs.u8ScanModeState = RM_SCAN_MODE_MANUAL;
+ #endif
+}
+static void rm31080_init_ts_structure(void)
+{
+ g_stTs.ulHalPID = 0;
+
+ memset(&g_stTs, 0, sizeof(struct rm31080a_ts_para));
+
+ #ifdef ENABLE_WORK_QUEUE
+ g_stTs.rm_workqueue = create_singlethread_workqueue("rm_work");
+ INIT_WORK(&g_stTs.rm_work,rm_work_handler);
+ g_stTs.bIsWorkQueueExecuting = 0;
+ #endif
+}
+//=============================================================================
+static void rm31080_start(struct rm31080_ts *ts)
+{
+ #ifdef ENABLE_RM31080_DEEP_SLEEP
+ struct rm_spi_ts_platform_data *pdata;
+ #endif
+
+ if (!g_stTs.bIsSuspended)
+ return;
+ g_stTs.bIsSuspended = 0;
+
+
+ #ifdef ENABLE_RM31080_DEEP_SLEEP
+ //flow designed by Roger //20110930
+ pdata = g_input_dev->dev.parent->platform_data;
+ gpio_set_value(pdata->gpio_reset, 0);
+ msleep(120);
+ gpio_set_value(pdata->gpio_reset, 1);
+ msleep(10);
+ rm31080_init_ts_structure_part();
+ rm31080_ts_send_signal(g_stTs.ulHalPID,RM_SIGNAL_RESUME);
+ //printk("Reset HW finish\n");
+ #elif defined(ENABLE_AUTO_SCAN)
+ rm31080_ctrl_clear_int();
+ rm31080_ctrl_scan_start();
+ #endif
+
+
+}
+
+static void rm31080_stop(struct rm31080_ts *ts)
+{
+
+ //printk("Raydium TS: rm31080_stop():\n");
+ int iCount;
+ if (g_stTs.bIsSuspended)
+ return;
+
+ iCount = 0;
+ while(g_stTs.bIsWorkQueueExecuting)
+ {
+ printk("Raydium TS: Work_Queue is Executing.\n");
+ msleep(1);
+ iCount++;
+ if (iCount>1000)
+ break;
+ }
+ g_stTs.bIsSuspended = 1;
+ //to do :flush_work_queue()
+
+ #ifdef ENABLE_RM31080_DEEP_SLEEP
+ rm31080_ctrl_suspend();
+ #endif
+}
+
+#ifdef CONFIG_PM
+#if 0
+static int rm31080_spi_suspend(struct spi_device *spi, pm_message_t message)
+{
+ //printk("Raydium TS: rm31080_spi_suspend():\n");
+ return 0;
+}
+
+static int rm31080_spi_resume(struct spi_device *spi)
+{
+ //printk("Raydium TS: rm31080_spi_resume():\n");
+ return 0;
+}
+#endif
+
+
+static int rm31080_suspend(struct device *dev)
+{
+ struct rm31080_ts *ts = dev_get_drvdata(dev);
+ //printk("Raydium TS: rm31080_suspend():\n");
+ rm31080_stop(ts);
+ return 0;
+}
+
+static int rm31080_resume(struct device *dev)
+{
+ struct rm31080_ts *ts = dev_get_drvdata(dev);
+ //printk("Raydium TS: rm31080_resume():\n");
+ rm31080_start(ts);
+ return 0;
+}
+
+
+#if defined(CONFIG_HAS_EARLYSUSPEND)
+static void rm31080_early_suspend(struct early_suspend *es)
+{
+ struct rm31080_ts *ts;
+ struct device *dev;
+ //printk("Raydium TS: rm31080_early_suspend():\n");
+
+ ts = container_of(es, struct rm31080_ts, early_suspend);
+ dev = ts->dev;
+
+ if (rm31080_suspend(dev) != 0)
+ {
+ dev_err(dev, "%s: failed\n", __func__);
+ }
+
+}
+
+static void rm31080_early_resume(struct early_suspend *es)
+{
+ struct rm31080_ts *ts;
+ struct device *dev;
+ //printk("Raydium TS: rm31080_early_resume():\n");
+
+ ts = container_of(es, struct rm31080_ts, early_suspend);
+ dev = ts->dev;
+
+ if (rm31080_resume(dev) != 0)
+ {
+ dev_err(dev, "%s: failed\n", __func__);
+ }
+}
+#else
+static const struct dev_pm_ops rm31080_pm_ops = {
+ .suspend = rm31080_suspend,
+ .resume = rm31080_resume,
+};
+#endif
+#endif
+
+
+struct rm31080_ts *rm31080_input_init(struct device *dev, unsigned int irq,
+ const struct rm31080_bus_ops *bops)
+{
+
+ struct rm31080_ts *ts;
+ struct input_dev *input_dev;
+ int err;
+
+ if (!irq) {
+ dev_err(dev, "no IRQ?\n");
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ ts = kzalloc(sizeof(*ts), GFP_KERNEL);//ts = kzalloc(sizeof(struct rm31080_ts), GFP_KERNEL);
+
+ input_dev = input_allocate_device();
+
+ if (!ts || !input_dev) {
+ dev_err(dev, "Failed to allocate memory\n");
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ g_input_dev = input_dev;
+
+ ts->bops = bops;
+ ts->dev = dev;
+ ts->input = input_dev;
+ ts->irq = irq;
+
+
+ #ifdef ENABLE_TIMER_RELEASE_TOUCH
+ //sample code :
+ //ts->timer.expires = jiffies + HZ;
+ //setup_timer(&ts->timer, rm31080_timer, (unsigned long) ts);
+ //mod_timer(&ts->timer, jiffies + TS_PEN_UP_TIMEOUT);
+ #endif
+ #ifdef ENABLE_TIMER_DEBUG
+ //ts->timer_debug.expires = jiffies + msecs_to_jiffies(10*1000);
+ setup_timer(&ts->timer_debug, rm31080_debug_timer, (unsigned long) ts);
+ mod_timer(&ts->timer_debug, jiffies + msecs_to_jiffies(10*1000));
+ g_stTs.u32WaitHalCount = 0;
+ #endif
+
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", dev_name(dev));
+
+ input_dev->name = "raydium_ts";
+ input_dev->phys = ts->phys;
+ input_dev->dev.parent = dev;
+ input_dev->id.bustype = bops->bustype;
+
+ input_dev->open = rm31080_input_open;
+ input_dev->close = rm31080_input_close;
+
+ input_set_drvdata(input_dev, ts);
+
+ __set_bit(EV_ABS, input_dev->evbit);
+ __set_bit(ABS_X, input_dev->absbit);
+ __set_bit(ABS_Y, input_dev->absbit);
+ __set_bit(ABS_PRESSURE, input_dev->absbit);
+
+ __set_bit(EV_KEY, input_dev->evbit);
+ __set_bit(BTN_TOUCH, input_dev->keybit);
+
+
+ /* For single touch */
+ input_set_abs_params(input_dev, ABS_X,
+ 0,
+ RM_INPUT_RESOLUTION_X-1,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_Y,
+ 0,
+ RM_INPUT_RESOLUTION_Y-1,
+ 0, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE,
+ 0, 1, 0, 0);//0, 255, 0, 0);
+
+ /* For multi touch */
+ input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, 0xFF, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_X,
+ 0, RM_INPUT_RESOLUTION_X-1, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
+ 0, RM_INPUT_RESOLUTION_Y-1, 0, 0);
+ input_set_abs_params(input_dev, ABS_MT_TRACKING_ID,
+ 0, 32, 0, 0);
+
+
+
+ #if 0
+ input_dev->id.product = (revid & 0xff);
+ input_dev->id.version = revid >> 8;
+ #endif
+
+ #if 0 //move to board-touch-raydium_spi.c
+ gpio_direction_input(irq_to_gpio(ts->irq));
+ tegra_gpio_enable(irq_to_gpio(ts->irq));
+ #endif
+
+ err = request_threaded_irq(ts->irq, NULL, rm31080_irq,
+ IRQF_TRIGGER_RISING,
+ dev_name(dev), ts);
+ if (err) {
+ dev_err(dev, "irq %d busy?\n", ts->irq);
+ goto err_free_mem;
+ }
+
+
+ mutex_init(&ts->access_mutex);
+ #if defined(CONFIG_HAS_EARLYSUSPEND)
+ ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ ts->early_suspend.suspend = rm31080_early_suspend;
+ ts->early_suspend.resume = rm31080_early_resume;
+ register_early_suspend(&ts->early_suspend);
+ #endif
+
+
+ __rm31080_disable(ts);
+
+ err = sysfs_create_group(&dev->kobj, &vtest_attr_group);
+ if (err)
+ goto err_free_irq;
+
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto err_remove_attr;
+
+ return ts;
+
+
+err_remove_attr:
+ sysfs_remove_group(&dev->kobj, &vtest_attr_group);
+err_free_irq:
+ free_irq(ts->irq, ts);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(ts);
+err_out:
+ return ERR_PTR(err);
+}
+
+static int
+dev_open(struct inode *inode, struct file *filp)
+{
+ //printk("%s():\n", __FUNCTION__);
+ return 0;
+}
+
+static int
+dev_release(struct inode *inode, struct file *filp)
+{
+ //printk("%s():\n", __FUNCTION__);
+ g_stTs.bInitFinish = 0;
+ rm31080_enter_manual_mode();
+ return 0;
+}
+
+static ssize_t
+dev_read(struct file *filp, char __user *buf, size_t count, loff_t *pos)
+{
+ unsigned long missing;
+ ssize_t status = 0;
+ u8 *pMyBuf;
+
+ pMyBuf = kmalloc(count, GFP_KERNEL);
+ if (pMyBuf == NULL)
+ return -ENOMEM;
+
+ pMyBuf[0] = buf[0];
+ #ifdef TEST_SPI_READ_SPEED
+ if (count > 1000) my_calc_time(1);
+ #endif
+ //status = spi_write_then_read(g_spi, pMyBuf, 1, pMyBuf, count);
+ status = rm31080_spi_read(pMyBuf[0],pMyBuf, count);
+
+ #ifdef TEST_SPI_READ_SPEED
+ if (count > 1000) my_calc_time(0);
+ #endif
+
+ #if 0//def TEST_INT_TO_CLEAR_INT
+ if (g_stTs.bInitFinish)
+ {
+ if (buf[0] == 0xF2 && count == 1)
+ {
+ my_calc_time(0);
+ }
+ }
+ #endif
+
+ if (status!=0)
+ {
+ printk("rm31080_spi_read() fail\n");
+ }
+
+ status = count;
+ missing = copy_to_user(buf, pMyBuf, count);
+ if (missing == status)
+ status = -EFAULT;
+ else
+ status = status - missing;
+
+ kfree(pMyBuf);
+ return status;
+}
+
+static ssize_t
+dev_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *pos)
+{
+ u8 *pMyBuf;
+ unsigned long missing;
+ ssize_t status = 0;
+
+ pMyBuf = kmalloc(count, GFP_KERNEL);
+ if (pMyBuf == NULL)
+ return -ENOMEM;
+
+ missing = copy_from_user(pMyBuf, buf, count);
+ if (missing == 0) {
+ //status = spi_write(g_spi, pMyBuf, count);
+ status = rm31080_spi_write(pMyBuf, count);
+ } else
+ status = -EFAULT;
+
+ kfree(pMyBuf);
+ return count;
+}
+
+//=============================================================================
+// Description:
+// I/O Control routin.
+// Input:
+// file:
+// cmd :
+// arg :
+// Output:
+// 1: succeed
+// 0: failed
+//=============================================================================
+static long
+dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ long ret = 1;
+ switch (cmd & 0xFFFF)
+ {
+ case RM_IOCTL_REPORT_POINT:
+ raydium_report_pointer((void *)arg);
+ break;
+ case RM_IOCTL_SET_HAL_PID:
+ g_stTs.ulHalPID = arg;
+ break;
+ case RM_IOCTL_INIT_START:
+ g_stTs.bInitFinish = 0;
+ rm31080_enter_manual_mode();
+ break;
+ case RM_IOCTL_INIT_END:
+ g_stTs.bInitFinish = 1;
+ g_stTs.bCalcFinish = 1;
+ #ifdef ENABLE_RAW_DATA_QUEUE
+ //printk("------ IOCTL init finish\n");
+ ret = rm31080_ctrl_scan_start();
+ #endif
+ break;
+ case RM_IOCTL_FINISH_CALC:
+ g_stTs.bCalcFinish = 1;
+ break;
+ case RM_IOCTL_SCRIBER_CTRL:
+ g_stTs.bEnableScriber = (bool)arg;
+ break;
+ case RM_IOCTL_AUTOSCAN_CTRL:
+ g_stTs.bEnableAutoScan = (bool)arg;
+ break;
+ #ifdef ENABLE_RAW_DATA_QUEUE
+ case RM_IOCTL_READ_RAW_DATA:
+ ret = rm31080_queue_read_raw_data((u8 *)arg,(cmd>>16) & 0xFFFF);
+ #endif
+ default:
+ break;
+ }
+ return ret;
+
+}
+static struct file_operations dev_fops = {
+ .owner = THIS_MODULE,
+ .open = dev_open,
+ .release = dev_release,
+ .read = dev_read,
+ .write = dev_write,
+ .unlocked_ioctl = dev_ioctl,
+};
+
+static struct miscdevice raydium_ts_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "raydium_ts",
+ .fops = &dev_fops,
+};
+
+
+static const struct rm31080_bus_ops rm31080_spi_bus_ops = {
+ .bustype = BUS_SPI,
+// .read = rm31080_spi_read,
+// .multi_read = rm31080_spi_multi_read,
+// .write = rm31080_spi_write,
+};
+
+
+static int __devexit rm31080_spi_remove(struct spi_device *spi)
+{
+ struct rm31080_ts *ts = spi_get_drvdata(spi);
+
+ #ifdef ENABLE_RAW_DATA_QUEUE
+ rm31080_queue_free();
+ #endif
+
+ #ifdef ENABLE_KERNEL_CALC
+ ts_main_free();
+ #endif
+
+
+ #ifdef ENABLE_TS_THREAD
+ if (ts->ts_task)
+ kthread_stop(ts->ts_task);
+ #endif
+
+ //to do :remove gpio;
+ sysfs_remove_group(&ts->dev->kobj, &vtest_attr_group);
+ free_irq(ts->irq, ts);
+ input_unregister_device(ts->input);
+ kfree(ts);
+ spi_set_drvdata(spi, NULL);
+ misc_deregister(&raydium_ts_miscdev);
+ return 0;
+}
+
+static int __devinit rm31080_spi_probe(struct spi_device *spi)
+{
+ struct rm31080_ts *ts;
+
+ rm31080_init_ts_structure();
+ rm31080_init_ts_structure_part();
+
+ if (spi->max_speed_hz > MAX_SPI_FREQ_HZ) {
+ dev_err(&spi->dev, "SPI CLK %d Hz?\n", spi->max_speed_hz);
+ return -EINVAL;
+ }
+
+
+ #if 0 //sample code: test spi
+ unsigned char buf[10];
+ unsigned int iCount =0;
+ //write:[0x50]=0x11,[0x51]=0x22,[0x52]=0x33,[0x53]=0x44
+ //you can test REG 0x50 ~ 0x5F
+ while(1)
+ {
+ buf[0]=0x50;
+ buf[1]=0x55;
+ buf[2]=0xAA;
+ buf[3]=0x00;
+ buf[4]=0xFF;
+ spi_write(spi, &buf[0], 5);
+
+ //read [0x50]~[0x53]
+ buf[5]= 0x50 | 0x80;
+ buf[6]=buf[7]=buf[8]=buf[9]=0;
+ spi_write_then_read(spi, &buf[5], 1, &buf[6], 4);
+ if ((buf[1]!=buf[6])||
+ (buf[2]!=buf[7])||
+ (buf[3]!=buf[8])||
+ (buf[4]!=buf[9]))
+ {
+ printk("Wrote Data :%x,%x,%x,%x\n",buf[1],buf[2],buf[3],buf[4]);
+ printk("Data From SPI:%x,%x,%x,%x\n",buf[6],buf[7],buf[8],buf[9]);
+ }
+ iCount++;
+ if (iCount%10000==0)
+ {
+ printk("spi ok\n");
+ }
+ };
+ #endif
+
+ #if 0 //sample code: to change setting here
+ //spi->bits_per_word = 16;
+ //spi->max_speed_hz = xxxxx;
+ err = spi_setup(spi);
+ if (err) {
+ dev_dbg(&spi->dev, "spi master doesn't support xxx\n");
+ return err;
+ }
+ #endif
+
+ ts = rm31080_input_init(&spi->dev, spi->irq, &rm31080_spi_bus_ops);
+ if (IS_ERR(ts))
+ return PTR_ERR(ts);
+ spi_set_drvdata(spi, ts);
+
+ #ifdef ENABLE_KERNEL_CALC
+ raydium_ts_set_spi(spi);
+ ts_main_init();
+ while(1)
+ {
+ raydium_ts_t007_wait_for_scan();
+ ts_main_calc();
+ raydium_ts_t007_start_scan();
+ msleep(100);
+ }
+ #else
+ g_spi = spi;
+ #endif
+
+
+ #ifdef ENABLE_TS_THREAD
+ BUG_ON(ts->ts_task);
+ ts->ts_task = kthread_run(rm31080_ts_thread, (void *)ts, "RM31080_ts");
+ if (IS_ERR(ts->ts_task)) {
+ //ret = PTR_ERR(ts->ts_task);
+ ts->ts_task = NULL;
+ }
+ #endif
+
+ if (misc_register(&raydium_ts_miscdev) != 0)
+ {
+ printk("Raydium TS: cannot register miscdev\n");
+ return 0;
+ }
+
+ #ifdef ENABLE_RAW_DATA_QUEUE
+ rm31080_queue_init();
+ #endif
+
+ return 0;
+}
+
+static struct spi_driver rm31080_spi_driver = {
+ .driver = {
+ .name = "rm_ts_spidev",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ #if !defined(CONFIG_HAS_EARLYSUSPEND)
+ .pm = &rm31080_pm_ops,
+ #endif
+ },
+ .probe = rm31080_spi_probe,
+ .remove = __devexit_p(rm31080_spi_remove),
+// .suspend = rm31080_spi_suspend,
+// .resume = rm31080_spi_resume,
+};
+
+
+static int __init rm31080_spi_init(void)
+{
+ return spi_register_driver(&rm31080_spi_driver);
+/*
+ if (iRet!=0)
+ {
+ printk("Raydium spi register failed\n");
+ return iRet;
+ }
+ printk("Raydium spi register ok\n");
+ if (misc_register(&raydium_ts_miscdev) != 0)
+ {
+ printk("Raydium TS: cannot register miscdev\n");
+ return 0;
+ }
+
+ return iRet;
+*/
+}
+
+module_init(rm31080_spi_init);
+
+static void __exit rm31080_spi_exit(void)
+{
+ #ifdef ENABLE_WORK_QUEUE
+ if (g_stTs.rm_workqueue)
+ destroy_workqueue(g_stTs.rm_workqueue);
+ #endif
+ spi_unregister_driver(&rm31080_spi_driver);
+}
+module_exit(rm31080_spi_exit);
+
+MODULE_AUTHOR("Valentine Hsu <valentine.hsu@rad-ic.com>");
+MODULE_DESCRIPTION("Raydium touchscreen SPI bus driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("spi:raydium-t007");
diff --git a/drivers/input/touchscreen/rm31080a_ts.h b/drivers/input/touchscreen/rm31080a_ts.h
new file mode 100644
index 000000000000..6d3f8d31d820
--- /dev/null
+++ b/drivers/input/touchscreen/rm31080a_ts.h
@@ -0,0 +1,40 @@
+#ifndef _RM31080A_TS_H_
+#define _RM31080A_TS_H_
+
+#include <linux/platform_data/rm31080a_ts.h>
+
+#define ENABLE_RAW_DATA_QUEUE
+
+#define RM_IOCTL_REPORT_POINT 0x1001
+#define RM_IOCTL_SET_HAL_PID 0x1002
+#define RM_IOCTL_INIT_START 0x1003
+#define RM_IOCTL_INIT_END 0x1004
+#define RM_IOCTL_FINISH_CALC 0x1005
+#define RM_IOCTL_SCRIBER_CTRL 0x1006
+#define RM_IOCTL_READ_RAW_DATA 0x1007
+#define RM_IOCTL_AUTOSCAN_CTRL 0x1008
+
+
+//#define RM_INPUT_RESOLUTION_X 1536
+//#define RM_INPUT_RESOLUTION_Y 960
+#define RM_INPUT_RESOLUTION_X 4096
+#define RM_INPUT_RESOLUTION_Y 4096
+
+
+#define RM_TS_SIGNAL 44
+#define RM_TS_MAX_POINTS 16
+
+#define RM_SIGNAL_INTR 0x00000001
+#define RM_SIGNAL_SUSPEND 0x00000002
+#define RM_SIGNAL_RESUME 0x00000003
+
+typedef struct
+{
+ unsigned char ucTouchCount;
+ unsigned char ucID[RM_TS_MAX_POINTS];
+ unsigned short usX[RM_TS_MAX_POINTS];
+ unsigned short usY[RM_TS_MAX_POINTS];
+ unsigned short usZ[RM_TS_MAX_POINTS];
+}rm_touch_event;
+
+#endif //_RM31080A_TS_H_
diff --git a/drivers/input/touchscreen/rmi4/Makefile b/drivers/input/touchscreen/rmi4/Makefile
new file mode 100644
index 000000000000..a7375ed07a39
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/Makefile
@@ -0,0 +1,18 @@
+#
+# Makefile for Synaptics Touchscreen (RMI4/SPI)
+#
+GCOV_PROFILE := y
+
+ccflags-$(CONFIG_SPI_DEBUG) := -DDEBUG
+
+#Synaptics SPI Sensor (2002)
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_bus.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_i2c.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_spi.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_driver.o rmi_f01.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f09.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f11.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f19.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f34.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_f54.o
+obj-$(CONFIG_TOUCHSCREEN_SYN_RMI4_SPI) += rmi_dev.o
diff --git a/drivers/input/touchscreen/rmi4/rmi_bus.c b/drivers/input/touchscreen/rmi4/rmi_bus.c
new file mode 100644
index 000000000000..6a269df4ff35
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_bus.c
@@ -0,0 +1,315 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/rmi.h>
+
+static struct rmi_function_list {
+ struct list_head list;
+ struct rmi_function_handler *fh;
+} rmi_supported_functions;
+
+static int rmi_bus_match(struct device *dev, struct device_driver *driver)
+{
+ struct rmi_driver *rmi_driver;
+ struct rmi_device *rmi_dev;
+ struct rmi_device_platform_data *pdata;
+
+ pr_info("in function ____%s____ \n", __func__);
+ rmi_driver = to_rmi_driver(driver);
+ rmi_dev = to_rmi_device(dev);
+ pdata = to_rmi_platform_data(rmi_dev);
+
+ pr_info(" rmi_driver->driver.name = %s\n", rmi_driver->driver.name);
+ pr_info(" device:rmi_device = 0x%x \n", rmi_dev);
+ pr_info(" device:rmi_device:rmi_device_platform_data:driver_name = %s \n", pdata->driver_name);
+ pr_info(" rmi_device:driver = 0x%x \n", rmi_dev->driver);
+
+ if (!strcmp(pdata->driver_name, rmi_driver->driver.name)) {
+ rmi_dev->driver = rmi_driver;
+ pr_info(" names match, so now rmi_device:driver = 0x%x \n",rmi_dev->driver);
+ return 1;
+ }
+ pr_info(" names DO NOT match, so return nothing \n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rmi_bus_suspend(struct device *dev)
+{
+#ifdef GENERIC_SUBSYS_PM_OPS
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+
+ if (pm && pm->suspend)
+ return pm->suspend(dev);
+#endif
+
+ return 0;
+}
+
+static int rmi_bus_resume(struct device *dev)
+{
+#ifdef GENERIC_SUBSYS_PM_OPS
+ const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
+ pr_info("in function ____%s____ \n", __func__);
+
+ if (pm && pm->resume)
+ return pm->resume(dev);
+#endif
+
+ return 0;
+}
+#endif
+
+static int rmi_bus_probe(struct device *dev)
+{
+ struct rmi_driver *driver;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ pr_info("in function ____%s____ \n", __func__);
+ driver = rmi_dev->driver;
+ if (driver && driver->probe)
+ return driver->probe(rmi_dev);
+
+ return 0;
+}
+
+static int rmi_bus_remove(struct device *dev)
+{
+ struct rmi_driver *driver;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ pr_info("in function ____%s____ \n", __func__);
+ driver = rmi_dev->driver;
+ if (driver && driver->remove)
+ return driver->remove(rmi_dev);
+
+ return 0;
+}
+
+static void rmi_bus_shutdown(struct device *dev)
+{
+ struct rmi_driver *driver;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ driver = rmi_dev->driver;
+ if (driver && driver->shutdown)
+ driver->shutdown(rmi_dev);
+}
+
+static SIMPLE_DEV_PM_OPS(rmi_bus_pm_ops,
+ rmi_bus_suspend, rmi_bus_resume);
+
+struct bus_type rmi_bus_type = {
+ .name = "rmi",
+ .match = rmi_bus_match,
+ .probe = rmi_bus_probe,
+ .remove = rmi_bus_remove,
+ .shutdown = rmi_bus_shutdown,
+ .pm = &rmi_bus_pm_ops
+};
+
+int rmi_register_phys_device(struct rmi_phys_device *phys)
+{
+ static int phys_device_num;
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+ struct rmi_device *rmi_dev;
+
+ pr_info("in function ____%s____ \n", __func__);
+
+ if (!pdata) {
+ dev_err(phys->dev, "no platform data!\n");
+ return -EINVAL;
+ }
+
+ rmi_dev = kzalloc(sizeof(struct rmi_device), GFP_KERNEL);
+ if (!rmi_dev)
+ return -ENOMEM;
+
+ rmi_dev->phys = phys;
+ rmi_dev->dev.bus = &rmi_bus_type;
+ dev_set_name(&rmi_dev->dev, "sensor%02d", phys_device_num++);
+
+ phys->rmi_dev = rmi_dev;
+ pr_info(" registering physical device:\n");
+ pr_info(" dev.init_name = \n", rmi_dev->dev.init_name);
+ pr_info(" dev.bus->name = \n", rmi_dev->dev.bus->name);
+ return device_register(&rmi_dev->dev);
+}
+EXPORT_SYMBOL(rmi_register_phys_device);
+
+void rmi_unregister_phys_device(struct rmi_phys_device *phys)
+{
+ struct rmi_device *rmi_dev = phys->rmi_dev;
+ pr_info("in function ____%s____ \n", __func__);
+
+ device_unregister(&rmi_dev->dev);
+ kfree(rmi_dev);
+}
+EXPORT_SYMBOL(rmi_unregister_phys_device);
+
+int rmi_register_driver(struct rmi_driver *driver)
+{
+ pr_info("in function ____%s____ \n", __func__);
+ driver->driver.bus = &rmi_bus_type;
+ return driver_register(&driver->driver);
+}
+EXPORT_SYMBOL(rmi_register_driver);
+
+static int __rmi_driver_remove(struct device *dev, void *data)
+{
+ struct rmi_driver *driver = data;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ if (rmi_dev->driver == driver)
+ rmi_dev->driver = NULL;
+
+ return 0;
+}
+
+void rmi_unregister_driver(struct rmi_driver *driver)
+{
+ bus_for_each_dev(&rmi_bus_type, NULL, driver, __rmi_driver_remove);
+ driver_unregister(&driver->driver);
+}
+EXPORT_SYMBOL(rmi_unregister_driver);
+
+static int __rmi_bus_fh_add(struct device *dev, void *data)
+{
+ struct rmi_driver *driver;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+ pr_info("in function ____%s____ \n", __func__);
+
+ driver = rmi_dev->driver;
+ if (driver && driver->fh_add)
+ driver->fh_add(rmi_dev, data);
+
+ return 0;
+}
+
+int rmi_register_function_driver(struct rmi_function_handler *fh)
+{
+ struct rmi_function_list *entry;
+ struct rmi_function_handler *fh_dup;
+
+ fh_dup = rmi_get_function_handler(fh->func);
+ if (fh_dup) {
+ pr_err("%s: function f%.2x already registered!\n", __func__,
+ fh->func);
+ return -EINVAL;
+ }
+
+ entry = kzalloc(sizeof(struct rmi_function_list), GFP_KERNEL);
+ if (!entry)
+ return -ENOMEM;
+
+ entry->fh = fh;
+ list_add_tail(&entry->list, &rmi_supported_functions.list);
+
+ /* notify devices of the new function handler */
+ bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_add);
+
+ return 0;
+}
+EXPORT_SYMBOL(rmi_register_function_driver);
+
+static int __rmi_bus_fh_remove(struct device *dev, void *data)
+{
+ struct rmi_driver *driver;
+ struct rmi_device *rmi_dev = to_rmi_device(dev);
+
+ pr_info("in function ____%s____ \n", __func__);
+ driver = rmi_dev->driver;
+ if (driver && driver->fh_remove)
+ driver->fh_remove(rmi_dev, data);
+
+ return 0;
+}
+
+void rmi_unregister_function_driver(struct rmi_function_handler *fh)
+{
+ struct rmi_function_list *entry, *n;
+ pr_info("in function ____%s____ \n", __func__);
+
+ /* notify devices of the removal of the function handler */
+ bus_for_each_dev(&rmi_bus_type, NULL, fh, __rmi_bus_fh_remove);
+
+ list_for_each_entry_safe(entry, n, &rmi_supported_functions.list, list)
+ if (entry->fh->func == fh->func) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+}
+EXPORT_SYMBOL(rmi_unregister_function_driver);
+
+struct rmi_function_handler *rmi_get_function_handler(int id)
+{
+ struct rmi_function_list *entry;
+ pr_info("in function ____%s____ \n", __func__);
+
+ list_for_each_entry(entry, &rmi_supported_functions.list, list)
+ if (entry->fh->func == id)
+ return entry->fh;
+
+ return NULL;
+}
+EXPORT_SYMBOL(rmi_get_function_handler);
+
+static int __init rmi_bus_init(void)
+{
+ int error;
+
+ pr_info("in function ____%s____ \n", __func__);
+ INIT_LIST_HEAD(&rmi_supported_functions.list);
+
+ error = bus_register(&rmi_bus_type);
+ if (error < 0) {
+ pr_err("%s: error registering the RMI bus: %d\n", __func__,
+ error);
+ return error;
+ }
+ pr_info("%s: successfully registered RMI bus.\n", __func__);
+
+ return 0;
+}
+
+static void __exit rmi_bus_exit(void)
+{
+ struct rmi_function_list *entry, *n;
+ pr_info("in function ____%s____ \n", __func__);
+
+ list_for_each_entry_safe(entry, n, &rmi_supported_functions.list,
+ list) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+
+ bus_unregister(&rmi_bus_type);
+}
+
+module_init(rmi_bus_init);
+module_exit(rmi_bus_exit);
+
+MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
+MODULE_DESCRIPTION("RMI bus");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_dev.c b/drivers/input/touchscreen/rmi4/rmi_dev.c
new file mode 100644
index 000000000000..fa21ef9b20fa
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_dev.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/syscalls.h>
+
+#include <linux/rmi.h>
+#include "rmi_driver.h"
+
+#define CHAR_DEVICE_NAME "rmi"
+
+#define REG_ADDR_LIMIT 0xFFFF
+
+/*store dynamically allocated major number of char device*/
+static int rmi_char_dev_major_num;
+
+
+/* file operations for RMI char device */
+
+/*
+ * rmi_char_dev_llseek: - use to setup register address
+ *
+ * @filp: file structure for seek
+ * @off: offset
+ * if whence == SEEK_SET,
+ * high 16 bits: page address
+ * low 16 bits: register address
+ *
+ * if whence == SEEK_CUR,
+ * offset from current position
+ *
+ * if whence == SEEK_END,
+ * offset from END(0xFFFF)
+ *
+ * @whence: SEEK_SET , SEEK_CUR or SEEK_END
+ */
+static loff_t rmi_char_dev_llseek(struct file *filp, loff_t off, int whence)
+{
+ loff_t newpos;
+ struct rmi_char_dev *my_char_dev = filp->private_data;
+
+ if (IS_ERR(my_char_dev)) {
+ pr_err("%s: pointer of char device is invalid", __func__);
+ return -EBADF;
+ }
+
+ mutex_lock(&(my_char_dev->mutex_file_op));
+
+ switch (whence) {
+ case SEEK_SET:
+ newpos = off;
+ break;
+
+ case SEEK_CUR:
+ newpos = filp->f_pos + off;
+ break;
+
+ case SEEK_END:
+ newpos = REG_ADDR_LIMIT + off;
+ break;
+
+ default: /* can't happen */
+ newpos = -EINVAL;
+ goto clean_up;
+ }
+
+ if (newpos < 0 || newpos > REG_ADDR_LIMIT) {
+ dev_err(my_char_dev->phys->dev, "newpos 0x%04x is invalid.\n",
+ (unsigned int)newpos);
+ newpos = -EINVAL;
+ goto clean_up;
+ }
+
+ filp->f_pos = newpos;
+
+clean_up:
+ mutex_unlock(&(my_char_dev->mutex_file_op));
+ return newpos;
+}
+
+/*
+ * rmi_char_dev_read: - use to read data from RMI stream
+ *
+ * @filp: file structure for read
+ * @buf: user-level buffer pointer
+ *
+ * @count: number of byte read
+ * @f_pos: offset (starting register address)
+ *
+ * @return number of bytes read into user buffer (buf) if succeeds
+ * negative number if error occurs.
+ */
+static ssize_t rmi_char_dev_read(struct file *filp, char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct rmi_char_dev *my_char_dev = filp->private_data;
+ ssize_t ret_value = 0;
+ unsigned char tmpbuf[count+1];
+ struct rmi_phys_device *phys;
+
+ /* limit offset to REG_ADDR_LIMIT-1 */
+ if (count > (REG_ADDR_LIMIT - *f_pos))
+ count = REG_ADDR_LIMIT - *f_pos;
+
+ if (count == 0)
+ return 0;
+
+ if (IS_ERR(my_char_dev)) {
+ pr_err("%s: pointer of char device is invalid", __func__);
+ ret_value = -EBADF;
+ return ret_value;
+ }
+
+ mutex_lock(&(my_char_dev->mutex_file_op));
+
+ phys = my_char_dev->phys;
+ /*
+ * just let it go through , because we do not know the register is FIFO
+ * register or not
+ */
+
+ ret_value = phys->read_block(phys, *f_pos, tmpbuf, count);
+
+ if (ret_value < 0)
+ goto clean_up;
+ else
+ *f_pos += ret_value;
+
+ if (copy_to_user(buf, tmpbuf, count))
+ ret_value = -EFAULT;
+
+clean_up:
+
+ mutex_unlock(&(my_char_dev->mutex_file_op));
+
+ return ret_value;
+}
+
+/*
+ * rmi_char_dev_write: - use to write data into RMI stream
+ *
+ * @filep : file structure for write
+ * @buf: user-level buffer pointer contains data to be written
+ * @count: number of byte be be written
+ * @f_pos: offset (starting register address)
+ *
+ * @return number of bytes written from user buffer (buf) if succeeds
+ * negative number if error occurs.
+ */
+static ssize_t rmi_char_dev_write(struct file *filp, const char __user *buf,
+ size_t count, loff_t *f_pos)
+{
+ struct rmi_char_dev *my_char_dev = filp->private_data;
+ ssize_t ret_value = 0;
+ unsigned char tmpbuf[count+1];
+ struct rmi_phys_device *phys;
+
+ /* limit offset to REG_ADDR_LIMIT-1 */
+ if (count > (REG_ADDR_LIMIT - *f_pos))
+ count = REG_ADDR_LIMIT - *f_pos;
+
+ if (count == 0)
+ return 0;
+
+ if (IS_ERR(my_char_dev)) {
+ pr_err("%s: pointer of char device is invalid", __func__);
+ ret_value = -EBADF;
+ return ret_value;
+ }
+
+ if (copy_from_user(tmpbuf, buf, count)) {
+ ret_value = -EFAULT;
+ return ret_value;
+ }
+
+ mutex_lock(&(my_char_dev->mutex_file_op));
+
+ phys = my_char_dev->phys;
+ /*
+ * just let it go through , because we do not know the register is FIFO
+ * register or not
+ */
+
+ ret_value = phys->write_block(phys, *f_pos, tmpbuf, count);
+
+ if (ret_value >= 0)
+ *f_pos += count;
+
+ mutex_unlock(&(my_char_dev->mutex_file_op));
+
+ return ret_value;
+}
+
+/*
+ * rmi_char_dev_open: - get a new handle for from RMI stream
+ * @inp : inode struture
+ * @filp: file structure for read/write
+ *
+ * @return 0 if succeeds
+ */
+static int rmi_char_dev_open(struct inode *inp, struct file *filp)
+{
+ /* store the device pointer to file structure */
+ struct rmi_char_dev *my_dev = container_of(inp->i_cdev,
+ struct rmi_char_dev, main_dev);
+ struct rmi_phys_device *phys = my_dev->phys;
+ int ret_value = 0;
+
+ filp->private_data = my_dev;
+
+ if (!phys)
+ return -EACCES;
+
+ mutex_lock(&(my_dev->mutex_file_op));
+ if (my_dev->ref_count < 1)
+ my_dev->ref_count++;
+ else
+ ret_value = -EACCES;
+
+ mutex_unlock(&(my_dev->mutex_file_op));
+
+ return ret_value;
+}
+
+/*
+ * rmi_char_dev_release: - release an existing handle
+ * @inp: inode structure
+ * @filp: file structure for read/write
+ *
+ * @return 0 if succeeds
+ */
+static int rmi_char_dev_release(struct inode *inp, struct file *filp)
+{
+ struct rmi_char_dev *my_dev = container_of(inp->i_cdev,
+ struct rmi_char_dev, main_dev);
+ struct rmi_phys_device *phys = my_dev->phys;
+
+ if (!phys)
+ return -EACCES;
+
+ mutex_lock(&(my_dev->mutex_file_op));
+
+ my_dev->ref_count--;
+ if (my_dev->ref_count < 0)
+ my_dev->ref_count = 0;
+
+ mutex_unlock(&(my_dev->mutex_file_op));
+
+ return 0;
+}
+
+static const struct file_operations rmi_char_dev_fops = {
+ .owner = THIS_MODULE,
+ .llseek = rmi_char_dev_llseek,
+ .read = rmi_char_dev_read,
+ .write = rmi_char_dev_write,
+ .open = rmi_char_dev_open,
+ .release = rmi_char_dev_release,
+};
+
+/*
+ * rmi_char_dev_clean_up - release memory or unregister driver
+ * @rmi_char_dev: rmi_char_dev structure
+ *
+ */
+static void rmi_char_dev_clean_up(struct rmi_char_dev *char_dev,
+ struct class *char_device_class)
+{
+ dev_t devno;
+
+ /* Get rid of our char dev entries */
+ if (char_dev) {
+ devno = char_dev->main_dev.dev;
+
+ cdev_del(&char_dev->main_dev);
+ kfree(char_dev);
+
+ if (char_device_class) {
+ device_destroy(char_device_class, devno);
+ class_unregister(char_device_class);
+ class_destroy(char_device_class);
+ }
+
+ /* cleanup_module is never called if registering failed */
+ unregister_chrdev_region(devno, 1);
+ pr_debug("%s: rmi_char_dev is removed\n", __func__);
+ }
+}
+
+/*
+ * rmi_char_devnode - return device permission
+ *
+ * @dev: char device structure
+ * @mode: file permission
+ *
+ */
+static char *rmi_char_devnode(struct device *dev, mode_t *mode)
+{
+ if (!mode)
+ return NULL;
+ /* rmi** */
+ /**mode = 0666*/
+ *mode = (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH);
+
+ return kasprintf(GFP_KERNEL, "rmi/%s", dev_name(dev));
+}
+
+/*
+ * rmi_char_dev_register - register char device (called from up-level)
+ *
+ * @phy: a pointer to an rmi_phys_devices structure
+ *
+ * @return: zero if suceeds
+ */
+int rmi_char_dev_register(struct rmi_phys_device *phys)
+{
+ struct rmi_char_dev *char_dev;
+ dev_t dev_no;
+ int err;
+ int result;
+ struct device *device_ptr;
+
+ if (rmi_char_dev_major_num) {
+ dev_no = MKDEV(rmi_char_dev_major_num, 0);
+ result = register_chrdev_region(dev_no, 1, CHAR_DEVICE_NAME);
+ } else {
+ result = alloc_chrdev_region(&dev_no, 0, 1, CHAR_DEVICE_NAME);
+ /* let kernel allocate a major for us */
+ rmi_char_dev_major_num = MAJOR(dev_no);
+ dev_info(phys->dev, "Major number of rmi_char_dev: %d\n",
+ rmi_char_dev_major_num);
+ }
+ if (result < 0)
+ return result;
+
+ char_dev = kzalloc(sizeof(struct rmi_char_dev), GFP_KERNEL);
+ if (!char_dev) {
+ dev_err(phys->dev, "Failed to allocate rmi_char_dev.\n");
+ /* unregister the char device region */
+ __unregister_chrdev(rmi_char_dev_major_num, MINOR(dev_no), 1,
+ CHAR_DEVICE_NAME);
+ return -ENOMEM;
+ }
+
+ mutex_init(&char_dev->mutex_file_op);
+
+ phys->char_dev = char_dev;
+ char_dev->phys = phys;
+
+ cdev_init(&char_dev->main_dev, &rmi_char_dev_fops);
+
+ err = cdev_add(&char_dev->main_dev, dev_no, 1);
+ if (err) {
+ dev_err(phys->dev, "Error %d adding rmi_char_dev.\n", err);
+ rmi_char_dev_clean_up(phys->char_dev,
+ phys->rmi_char_device_class);
+ return err;
+ }
+
+ /* create device node */
+ phys->rmi_char_device_class =
+ class_create(THIS_MODULE, CHAR_DEVICE_NAME);
+
+ if (IS_ERR(phys->rmi_char_device_class)) {
+ dev_err(phys->dev, "Failed to create /dev/%s.\n",
+ CHAR_DEVICE_NAME);
+ rmi_char_dev_clean_up(phys->char_dev,
+ phys->rmi_char_device_class);
+ return -ENODEV;
+ }
+ /* setup permission */
+ phys->rmi_char_device_class->devnode = rmi_char_devnode;
+
+ /* class creation */
+ device_ptr = device_create(
+ phys->rmi_char_device_class,
+ NULL, dev_no, NULL,
+ CHAR_DEVICE_NAME"%d",
+ MINOR(dev_no));
+
+ if (IS_ERR(device_ptr)) {
+ dev_err(phys->dev, "Failed to create rmi device.\n");
+ rmi_char_dev_clean_up(phys->char_dev,
+ phys->rmi_char_device_class);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(rmi_char_dev_register);
+
+/* rmi_char_dev_unregister - unregister char device (called from up-level)
+ *
+ * @phys: pointer to an rmi_phys_device structure
+ */
+
+void rmi_char_dev_unregister(struct rmi_phys_device *phys)
+{
+ /* clean up */
+ if (phys)
+ rmi_char_dev_clean_up(phys->char_dev,
+ phys->rmi_char_device_class);
+}
+EXPORT_SYMBOL(rmi_char_dev_unregister);
+
+MODULE_AUTHOR("Synaptics, Inc.");
+MODULE_DESCRIPTION("RMI4 Char Device");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_driver.c b/drivers/input/touchscreen/rmi4/rmi_driver.c
new file mode 100644
index 000000000000..6aeb3b93b2c5
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_driver.c
@@ -0,0 +1,1354 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This driver adds support for generic RMI4 devices from Synpatics. It
+ * implements the mandatory f01 RMI register and depends on the presence of
+ * other required RMI functions.
+ *
+ * The RMI4 specification can be found here (URL split after files/ for
+ * style reasons):
+ * http://www.synaptics.com/sites/default/files/
+ * 511-000136-01-Rev-E-RMI4%20Intrfacing%20Guide.pdf
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/workqueue.h>
+#include <linux/rmi.h>
+#include "rmi_driver.h"
+
+#define DELAY_DEBUG 0
+#define REGISTER_DEBUG 0
+
+#define PDT_END_SCAN_LOCATION 0x0005
+#define PDT_PROPERTIES_LOCATION 0x00EF
+#define BSR_LOCATION 0x00FE
+#define HAS_BSR_MASK 0x20
+#define HAS_NONSTANDARD_PDT_MASK 0x40
+#define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
+#define RMI4_MAX_PAGE 0xff
+#define RMI4_PAGE_SIZE 0x100
+
+#define RMI_DEVICE_RESET_CMD 0x01
+#define INITIAL_RESET_WAIT_MS 20
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void rmi_driver_early_suspend(struct early_suspend *h);
+static void rmi_driver_late_resume(struct early_suspend *h);
+#endif
+
+
+/* sysfs files for attributes for driver values. */
+static ssize_t rmi_driver_hasbsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_driver_bsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_driver_bsr_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_driver_enabled_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_driver_enabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_driver_phys_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_driver_version_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+#if REGISTER_DEBUG
+static ssize_t rmi_driver_reg_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+#endif
+
+#if DELAY_DEBUG
+static ssize_t rmi_delay_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_delay_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+#endif
+
+static struct device_attribute attrs[] = {
+ __ATTR(hasbsr, RMI_RO_ATTR,
+ rmi_driver_hasbsr_show, rmi_store_error),
+ __ATTR(bsr, RMI_RW_ATTR,
+ rmi_driver_bsr_show, rmi_driver_bsr_store),
+ __ATTR(enabled, RMI_RW_ATTR,
+ rmi_driver_enabled_show, rmi_driver_enabled_store),
+ __ATTR(phys, RMI_RO_ATTR,
+ rmi_driver_phys_show, rmi_store_error),
+#if REGISTER_DEBUG
+ __ATTR(reg, RMI_WO_ATTR,
+ rmi_show_error, rmi_driver_reg_store),
+#endif
+#if DELAY_DEBUG
+ __ATTR(delay, RMI_RW_ATTR,
+ rmi_delay_show, rmi_delay_store),
+#endif
+ __ATTR(version, RMI_RO_ATTR,
+ rmi_driver_version_show, rmi_store_error),
+};
+
+
+/*
+** ONLY needed for POLLING mode of the driver
+*/
+struct rmi_device *polled_synaptics_rmi_device = NULL;
+EXPORT_SYMBOL(polled_synaptics_rmi_device);
+
+/* Useful helper functions for u8* */
+
+void u8_set_bit(u8 *target, int pos)
+{
+ target[pos/8] |= 1<<pos%8;
+}
+
+void u8_clear_bit(u8 *target, int pos)
+{
+ target[pos/8] &= ~(1<<pos%8);
+}
+
+bool u8_is_set(u8 *target, int pos)
+{
+ return target[pos/8] & 1<<pos%8;
+}
+
+bool u8_is_any_set(u8 *target, int size)
+{
+ int i;
+ for (i = 0; i < size; i++) {
+ if (target[i])
+ return true;
+ }
+ return false;
+}
+
+void u8_or(u8 *dest, u8 *target1, u8 *target2, int size)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ dest[i] = target1[i] | target2[i];
+}
+
+void u8_and(u8 *dest, u8 *target1, u8 *target2, int size)
+{
+ int i;
+ for (i = 0; i < size; i++)
+ dest[i] = target1[i] & target2[i];
+}
+
+/* Helper fn to convert a byte array representing a short in the RMI
+ * endian-ness to a short in the native processor's specific endianness.
+ * We don't use ntohs/htons here because, well, we're not dealing with
+ * a pair of shorts. And casting dest to short* wouldn't work, because
+ * that would imply knowing the byte order of short in the first place.
+ */
+void batohs(unsigned short *dest, unsigned char *src)
+{
+ *dest = src[1] * 0x100 + src[0];
+}
+
+/* Helper function to convert a short (in host processor endianess) to
+ * a byte array in the RMI endianess for shorts. See above comment for
+ * why we dont us htons or something like that.
+ */
+void hstoba(unsigned char *dest, unsigned short src)
+{
+ dest[0] = src % 0x100;
+ dest[1] = src / 0x100;
+}
+
+static bool has_bsr(struct rmi_driver_data *data)
+{
+ return (data->pdt_props & HAS_BSR_MASK) != 0;
+}
+
+/* Utility routine to set bits in a register. */
+int rmi_set_bits(struct rmi_device *rmi_dev, unsigned short address,
+ unsigned char bits)
+{
+ unsigned char reg_contents;
+ int retval;
+
+ retval = rmi_read_block(rmi_dev, address, &reg_contents, 1);
+ if (retval)
+ return retval;
+ reg_contents = reg_contents | bits;
+ retval = rmi_write_block(rmi_dev, address, &reg_contents, 1);
+ if (retval == 1)
+ return 0;
+ else if (retval == 0)
+ return -EIO;
+ return retval;
+}
+EXPORT_SYMBOL(rmi_set_bits);
+
+/* Utility routine to clear bits in a register. */
+int rmi_clear_bits(struct rmi_device *rmi_dev, unsigned short address,
+ unsigned char bits)
+{
+ unsigned char reg_contents;
+ int retval;
+
+ retval = rmi_read_block(rmi_dev, address, &reg_contents, 1);
+ if (retval)
+ return retval;
+ reg_contents = reg_contents & ~bits;
+ retval = rmi_write_block(rmi_dev, address, &reg_contents, 1);
+ if (retval == 1)
+ return 0;
+ else if (retval == 0)
+ return -EIO;
+ return retval;
+}
+EXPORT_SYMBOL(rmi_clear_bits);
+
+static void rmi_free_function_list(struct rmi_device *rmi_dev)
+{
+ struct rmi_function_container *entry, *n;
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+ list_for_each_entry_safe(entry, n, &data->rmi_functions.list, list) {
+ kfree(entry->irq_mask);
+ list_del(&entry->list);
+ }
+}
+
+static int init_one_function(struct rmi_device *rmi_dev,
+ struct rmi_function_container *fc)
+{
+ int retval;
+
+ dev_info(&rmi_dev->dev, "Initializing F%02X.\n",
+ fc->fd.function_number);
+ dev_dbg(&rmi_dev->dev, "Initializing F%02X.\n",
+ fc->fd.function_number);
+
+ if (!fc->fh) {
+ struct rmi_function_handler *fh =
+ rmi_get_function_handler(fc->fd.function_number);
+ if (!fh) {
+ dev_dbg(&rmi_dev->dev, "No handler for F%02X.\n",
+ fc->fd.function_number);
+ return 0;
+ }
+ fc->fh = fh;
+ }
+
+ if (!fc->fh->init)
+ return 0;
+
+ dev_set_name(&(fc->dev), "fn%02x", fc->fd.function_number);
+
+ fc->dev.parent = &rmi_dev->dev;
+
+ retval = device_register(&fc->dev);
+ if (retval) {
+ dev_err(&rmi_dev->dev, "Failed device_register for F%02X.\n",
+ fc->fd.function_number);
+ return retval;
+ }
+
+ retval = fc->fh->init(fc);
+ if (retval < 0) {
+ dev_err(&rmi_dev->dev, "Failed to initialize function F%02x\n",
+ fc->fd.function_number);
+ goto error_exit;
+ }
+
+ return 0;
+
+error_exit:
+ device_unregister(&fc->dev);
+ return retval;
+}
+
+static void rmi_driver_fh_add(struct rmi_device *rmi_dev,
+ struct rmi_function_handler *fh)
+{
+ struct rmi_function_container *entry;
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+ if (fh->func == 0x01)
+ data->f01_container->fh = fh;
+ else {
+ list_for_each_entry(entry, &data->rmi_functions.list, list)
+ if (entry->fd.function_number == fh->func) {
+ entry->fh = fh;
+ init_one_function(rmi_dev, entry);
+ }
+ }
+
+}
+
+static void rmi_driver_fh_remove(struct rmi_device *rmi_dev,
+ struct rmi_function_handler *fh)
+{
+ struct rmi_function_container *entry;
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list)
+ if (entry->fd.function_number == fh->func) {
+ if (fh->remove)
+ fh->remove(entry);
+
+ entry->fh = NULL;
+ }
+}
+
+static void construct_mask(u8 *mask, int num, int pos)
+{
+ int i;
+
+ for (i = 0; i < num; i++)
+ u8_set_bit(mask, pos+i);
+}
+
+static int process_interrupt_requests(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+ struct device *dev = &rmi_dev->dev;
+ struct rmi_function_container *entry;
+ u8 irq_status[data->num_of_irq_regs];
+ u8 irq_bits[data->num_of_irq_regs];
+ int error;
+
+ error = rmi_read_block(rmi_dev,
+ data->f01_container->fd.data_base_addr + 1,
+ irq_status, data->num_of_irq_regs);
+ if (error < 0) {
+ dev_err(dev, "%s: failed to read irqs.", __func__);
+ return error;
+ }
+ /* Device control (F01) is handled before anything else. */
+ u8_and(irq_bits, irq_status, data->f01_container->irq_mask,
+ data->num_of_irq_regs);
+ if (u8_is_any_set(irq_bits, data->num_of_irq_regs))
+ data->f01_container->fh->attention(
+ data->f01_container, irq_bits);
+
+ //dev_info(dev, " irq_status = 0x%2x data->current_irq_mask = 0x%2x data->num_of_irq_regs = %d\n",
+ // irq_status[0], data->current_irq_mask[0], data->num_of_irq_regs );
+
+
+ u8_and(irq_status, irq_status, data->current_irq_mask,
+ data->num_of_irq_regs);
+
+ /* At this point, irq_status has all bits that are set in the
+ * interrupt status register and are enabled.
+ */
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list){
+ if (entry->irq_mask && entry->fh && entry->fh->attention) {
+
+ u8_and(irq_bits, irq_status, entry->irq_mask,
+ data->num_of_irq_regs);
+ if (u8_is_any_set(irq_bits, data->num_of_irq_regs)) {
+ error = entry->fh->attention(entry, irq_bits);
+ if (error < 0)
+ dev_err(dev, "%s: f%.2x"
+ " attention handler failed:"
+ " %d\n", __func__,
+ entry->fh->func, error);
+ }
+ }
+ }
+ return 0;
+}
+
+
+static int rmi_driver_irq_handler(struct rmi_device *rmi_dev, int irq)
+{
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+ /* Can get called before the driver is fully ready to deal with
+ * interrupts.
+ */
+ if (!data || !data->f01_container || !data->f01_container->fh) {
+ dev_warn(&rmi_dev->dev,
+ "Not ready to handle interrupts yet!\n");
+ return 0;
+ }
+
+ return process_interrupt_requests(rmi_dev);
+}
+
+/*
+ * Construct a function's IRQ mask. This should
+ * be called once and stored.
+ */
+static u8 *rmi_driver_irq_get_mask(struct rmi_device *rmi_dev,
+ struct rmi_function_container *fc) {
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+ /* TODO: Where should this be freed? */
+ u8 *irq_mask = kzalloc(sizeof(u8) * data->num_of_irq_regs, GFP_KERNEL);
+ if (irq_mask)
+ construct_mask(irq_mask, fc->num_of_irqs, fc->irq_pos);
+
+ return irq_mask;
+}
+
+/*
+ * This pair of functions allows functions like function 54 to request to have
+ * other interupts disabled until the restore function is called. Only one store
+ * happens at a time.
+ */
+static int rmi_driver_irq_save(struct rmi_device *rmi_dev, u8 * new_ints)
+{
+ int retval = 0;
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+ struct device *dev = &rmi_dev->dev;
+
+ mutex_lock(&data->irq_mutex);
+ if (!data->irq_stored) {
+ /* Save current enabled interupts */
+ retval = rmi_read_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->irq_mask_store, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to read enabled interrupts!",
+ __func__);
+ goto error_unlock;
+ }
+ /*
+ * Disable every interupt except for function 54
+ * TODO:Will also want to not disable function 1-like functions.
+ * No need to take care of this now, since there's no good way
+ * to identify them.
+ */
+ retval = rmi_write_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ new_ints, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to change enabled interrupts!",
+ __func__);
+ goto error_unlock;
+ }
+ memcpy(data->current_irq_mask, new_ints,
+ data->num_of_irq_regs * sizeof(u8));
+ data->irq_stored = true;
+ } else {
+ retval = -ENOSPC; /* No space to store IRQs.*/
+ dev_err(dev, "%s: Attempted to save values when"
+ " already stored!", __func__);
+ }
+
+error_unlock:
+ mutex_unlock(&data->irq_mutex);
+ return retval;
+}
+
+static int rmi_driver_irq_restore(struct rmi_device *rmi_dev)
+{
+ int retval = 0;
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+ struct device *dev = &rmi_dev->dev;
+ mutex_lock(&data->irq_mutex);
+
+ if (data->irq_stored) {
+ retval = rmi_write_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->irq_mask_store, data->num_of_irq_regs);
+ if (retval < 0) {
+ dev_err(dev, "%s: Failed to write enabled interupts!",
+ __func__);
+ goto error_unlock;
+ }
+ memcpy(data->current_irq_mask, data->irq_mask_store,
+ data->num_of_irq_regs * sizeof(u8));
+ data->irq_stored = false;
+ } else {
+ retval = -EINVAL;
+ dev_err(dev, "%s: Attempted to restore values when not stored!",
+ __func__);
+ }
+
+error_unlock:
+ mutex_unlock(&data->irq_mutex);
+ return retval;
+}
+
+static int rmi_driver_fn_01_specific(struct rmi_device *rmi_dev,
+ struct pdt_entry *pdt_ptr,
+ int *current_irq_count,
+ u16 page_start)
+{
+ struct rmi_driver_data *data = NULL;
+ struct rmi_function_container *fc = NULL;
+ int retval = 0;
+ struct device *dev = &rmi_dev->dev;
+ struct rmi_function_handler *fh =
+ rmi_get_function_handler(0x01);
+
+ data = rmi_get_driverdata(rmi_dev);
+
+ dev_info(dev, "%s: Found F01, initializing.\n", __func__);
+ if (!fh)
+ dev_dbg(dev, "%s: No function handler for F01?!", __func__);
+
+ fc = kzalloc(sizeof(struct rmi_function_container), GFP_KERNEL);
+ if (!fc) {
+ retval = -ENOMEM;
+ return retval;
+ }
+
+ copy_pdt_entry_to_fd(pdt_ptr, &fc->fd, page_start);
+ fc->num_of_irqs = pdt_ptr->interrupt_source_count;
+ fc->irq_pos = *current_irq_count;
+
+ *current_irq_count += fc->num_of_irqs;
+
+ fc->rmi_dev = rmi_dev;
+ fc->dev.parent = &fc->rmi_dev->dev;
+ fc->fh = fh;
+
+ dev_set_name(&(fc->dev), "fn%02x", fc->fd.function_number);
+
+ retval = device_register(&fc->dev);
+ if (retval) {
+ dev_err(dev, "%s: Failed device_register for F01.\n", __func__);
+ goto error_free_data;
+ }
+
+ data->f01_container = fc;
+
+ return retval;
+
+error_free_data:
+ kfree(fc);
+ return retval;
+}
+
+/*
+ * Scan the PDT for F01 so we can force a reset before anything else
+ * is done. This forces the sensor into a known state, and also
+ * forces application of any pending updates from reflashing the
+ * firmware or configuration. We have to do this before actually
+ * building the PDT because the reflash might cause various registers
+ * to move around.
+ */
+static int do_initial_reset(struct rmi_device* rmi_dev)
+{
+ struct pdt_entry pdt_entry;
+ int page;
+ struct device *dev = &rmi_dev->dev;
+ bool done = false;
+ int i;
+ int retval;
+
+ pr_info("in function ____%s____ \n", __func__);
+
+ polled_synaptics_rmi_device = rmi_dev;
+
+ for (page = 0; (page <= RMI4_MAX_PAGE) && !done; page++) {
+
+ u16 page_start = RMI4_PAGE_SIZE * page;
+ u16 pdt_start = page_start + PDT_START_SCAN_LOCATION;
+ u16 pdt_end = page_start + PDT_END_SCAN_LOCATION;
+ pr_info(" reading page = %d\n", page );
+ done = true;
+ for (i = pdt_start; i >= pdt_end; i -= sizeof(pdt_entry)) {
+
+ pr_info(" reading PDT entry %3d (block %3d)\n",
+ i%sizeof(pdt_entry), i);
+
+ retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry,
+ sizeof(pdt_entry));
+ if (retval != sizeof(pdt_entry)) {
+ dev_err(dev, "Read PDT entry at 0x%04x"
+ "failed, code = %d.\n", i, retval);
+ return retval;
+ }
+
+ if (RMI4_END_OF_PDT(pdt_entry.function_number))
+ break;
+ done = false;
+
+ if (pdt_entry.function_number == 0x01) {
+ u16 cmd_addr = page_start +
+ pdt_entry.command_base_addr;
+ u8 cmd_buf = RMI_DEVICE_RESET_CMD;
+ retval = rmi_write_block(rmi_dev, cmd_addr,
+ &cmd_buf, 1);
+ if (retval < 0) {
+ dev_err(dev, "Initial reset failed. "
+ "Code = %d.\n", retval);
+ return retval;
+ }
+ mdelay(INITIAL_RESET_WAIT_MS);
+ return 0;
+ }
+ }
+ }
+
+ dev_warn(dev, "WARNING: Failed to find F01 for initial reset.\n");
+ return -ENODEV;
+}
+
+
+static int rmi_scan_pdt(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data;
+ struct rmi_function_container *fc;
+ struct pdt_entry pdt_entry;
+ int page;
+ struct device *dev = &rmi_dev->dev;
+ int irq_count = 0;
+ bool done = false;
+ int i;
+ int retval;
+ pr_info("in function ____%s____ \n", __func__);
+ pr_info(" doing initial reset \n");
+
+ retval = do_initial_reset(rmi_dev);
+ pr_info(" back in %s \n", __func__);
+
+ if (retval)
+ dev_err(dev, "WARNING: Initial reset failed! Soldiering on.\n");
+
+ data = rmi_get_driverdata(rmi_dev);
+
+ INIT_LIST_HEAD(&data->rmi_functions.list);
+
+ /* parse the PDT */
+ for (page = 0; (page <= RMI4_MAX_PAGE) && !done; page++) {
+ u16 page_start = RMI4_PAGE_SIZE * page;
+ u16 pdt_start = page_start + PDT_START_SCAN_LOCATION;
+ u16 pdt_end = page_start + PDT_END_SCAN_LOCATION;
+
+ done = true;
+ for (i = pdt_start; i >= pdt_end; i -= sizeof(pdt_entry)) {
+ retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry,
+ sizeof(pdt_entry));
+ if (retval != sizeof(pdt_entry)) {
+ dev_err(dev, "Read PDT entry at 0x%04x"
+ "failed.\n", i);
+ goto error_exit;
+ }
+
+ if (RMI4_END_OF_PDT(pdt_entry.function_number))
+ break;
+
+ dev_dbg(dev, "%s: Found F%.2X on page 0x%02X\n",
+ __func__, pdt_entry.function_number, page);
+ done = false;
+
+ /*
+ * F01 is handled by rmi_driver. Hopefully we will get
+ * rid of the special treatment of f01 at some point
+ * in time.
+ */
+ if (pdt_entry.function_number == 0x01) {
+ retval = rmi_driver_fn_01_specific(rmi_dev,
+ &pdt_entry, &irq_count,
+ page_start);
+ if (retval)
+ goto error_exit;
+ continue;
+ }
+
+ fc = kzalloc(sizeof(struct rmi_function_container),
+ GFP_KERNEL);
+ if (!fc) {
+ dev_err(dev, "Failed to allocate function "
+ "container for F%02X.\n",
+ pdt_entry.function_number);
+ retval = -ENOMEM;
+ goto error_exit;
+ }
+
+ copy_pdt_entry_to_fd(&pdt_entry, &fc->fd, page_start);
+
+ fc->rmi_dev = rmi_dev;
+ fc->num_of_irqs = pdt_entry.interrupt_source_count;
+ fc->irq_pos = irq_count;
+ irq_count += fc->num_of_irqs;
+
+ retval = init_one_function(rmi_dev, fc);
+ if (retval < 0) {
+ dev_err(dev, "Failed to initialize F%.2x\n",
+ pdt_entry.function_number);
+ kfree(fc);
+ goto error_exit;
+ }
+
+ list_add_tail(&fc->list, &data->rmi_functions.list);
+ }
+ }
+ data->num_of_irq_regs = (irq_count + 7) / 8;
+ dev_dbg(dev, "%s: Done with PDT scan.\n", __func__);
+ return 0;
+
+error_exit:
+ return retval;
+}
+
+
+#ifdef SYNAPTICS_SENSOR_POLL
+void synaptics_sensor_poller(unsigned long data){
+ pr_info("in function ____%s____ , rmi_device= 0x%8x \n", __func__, polled_synaptics_rmi_device);
+ // msleep(10000);
+ for (;;) {
+ msleep(100);
+ rmi_driver_irq_handler(polled_synaptics_rmi_device, 0);
+ }
+
+ return;
+}
+
+struct workqueue_struct *synaptics_rmi_polling_queue = NULL;
+struct delayed_work synaptics_rmi_polling_work;
+
+#endif
+
+
+static int rmi_driver_probe(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data;
+ struct rmi_function_container *fc;
+ struct rmi_device_platform_data *pdata;
+ int error = 0;
+ struct device *dev = &rmi_dev->dev;
+ int attr_count = 0;
+
+ dev_dbg(dev, "%s: Starting probe.\n", __func__);
+
+ pdata = to_rmi_platform_data(rmi_dev);
+
+ data = kzalloc(sizeof(struct rmi_driver_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(dev, "%s: Failed to allocate driver data.\n", __func__);
+ return -ENOMEM;
+ }
+
+ rmi_set_driverdata(rmi_dev, data);
+
+ error = rmi_scan_pdt(rmi_dev);
+ if (error) {
+ dev_err(dev, "PDT scan failed with code %d.\n", error);
+ goto err_free_data;
+ }
+
+ if (!data->f01_container) {
+ dev_err(dev, "missing f01 function!\n");
+ error = -EINVAL;
+ goto err_free_data;
+ }
+
+ data->f01_container->irq_mask = kzalloc(
+ sizeof(u8) * data->num_of_irq_regs, GFP_KERNEL);
+ if (!data->f01_container->irq_mask) {
+ dev_err(dev, "Failed to allocate F01 IRQ mask.\n");
+ error = -ENOMEM;
+ goto err_free_data;
+ }
+ construct_mask(data->f01_container->irq_mask,
+ data->f01_container->num_of_irqs,
+ data->f01_container->irq_pos);
+ list_for_each_entry(fc, &data->rmi_functions.list, list)
+ fc->irq_mask = rmi_driver_irq_get_mask(rmi_dev, fc);
+
+ error = rmi_driver_f01_init(rmi_dev);
+ if (error < 0) {
+ dev_err(dev, "Failed to initialize F01.\n");
+ goto err_free_data;
+ }
+
+ error = rmi_read(rmi_dev, PDT_PROPERTIES_LOCATION,
+ (char *) &data->pdt_props);
+ if (error < 0) {
+ /* we'll print out a warning and continue since
+ * failure to get the PDT properties is not a cause to fail
+ */
+ dev_warn(dev, "Could not read PDT properties from 0x%04x. "
+ "Assuming 0x00.\n", PDT_PROPERTIES_LOCATION);
+ }
+
+ dev_dbg(dev, "%s: Creating sysfs files.", __func__);
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ error = device_create_file(dev, &attrs[attr_count]);
+ if (error < 0) {
+ dev_err(dev, "%s: Failed to create sysfs file %s.\n",
+ __func__, attrs[attr_count].attr.name);
+ goto err_free_data;
+ }
+ }
+
+ __mutex_init(&data->irq_mutex, "irq_mutex", &data->irq_key);
+ data->current_irq_mask = kzalloc(sizeof(u8) * data->num_of_irq_regs,
+ GFP_KERNEL);
+ if (!data->current_irq_mask) {
+ dev_err(dev, "Failed to allocate current_irq_mask.\n");
+ error = -ENOMEM;
+ goto err_free_data;
+ }
+ error = rmi_read_block(rmi_dev,
+ data->f01_container->fd.control_base_addr+1,
+ data->current_irq_mask, data->num_of_irq_regs);
+ if (error < 0) {
+ dev_err(dev, "%s: Failed to read current IRQ mask.\n",
+ __func__);
+ goto err_free_data;
+ }
+ data->irq_mask_store = kzalloc(sizeof(u8) * data->num_of_irq_regs,
+ GFP_KERNEL);
+ if (!data->irq_mask_store) {
+ dev_err(dev, "Failed to allocate mask store.\n");
+ error = -ENOMEM;
+ goto err_free_data;
+ }
+
+#if defined(CONFIG_RMI4_DEV)
+ if (rmi_char_dev_register(rmi_dev->phys))
+ pr_err("%s: error register char device", __func__);
+#endif /*CONFIG_RMI4_DEV*/
+
+#ifdef CONFIG_PM
+ data->pm_data = pdata->pm_data;
+ data->pre_suspend = pdata->pre_suspend;
+ data->post_resume = pdata->post_resume;
+
+ mutex_init(&data->suspend_mutex);
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ rmi_dev->early_suspend_handler.level =
+ EARLY_SUSPEND_LEVEL_BLANK_SCREEN + 1;
+ rmi_dev->early_suspend_handler.suspend = rmi_driver_early_suspend;
+ rmi_dev->early_suspend_handler.resume = rmi_driver_late_resume;
+ register_early_suspend(&rmi_dev->early_suspend_handler);
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+#endif /* CONFIG_PM */
+ data->enabled = true;
+
+ dev_info(dev, "connected RMI device manufacturer: %s product: %s\n",
+ data->manufacturer_id == 1 ? "synaptics" : "unknown",
+ data->product_id);
+
+#ifdef SYNAPTICS_SENSOR_POLL
+ synaptics_rmi_polling_queue = create_singlethread_workqueue("rmi_poll_work");
+ INIT_DELAYED_WORK_DEFERRABLE(&synaptics_rmi_polling_work, synaptics_sensor_poller);
+ pr_info("%s: setting up POLLING mode, rmi_device= 0x%8x \n", __func__, polled_synaptics_rmi_device);
+ queue_delayed_work(synaptics_rmi_polling_queue, &synaptics_rmi_polling_work, 1000);
+#endif
+ return 0;
+
+ err_free_data:
+ rmi_free_function_list(rmi_dev);
+ for (attr_count--; attr_count >= 0; attr_count--)
+ device_remove_file(dev, &attrs[attr_count]);
+ kfree(data->f01_container->irq_mask);
+ kfree(data->irq_mask_store);
+ kfree(data->current_irq_mask);
+ kfree(data);
+ return error;
+}
+
+#ifdef CONFIG_PM
+static int rmi_driver_suspend(struct device *dev)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+ struct rmi_function_container *entry;
+ int retval = 0;
+
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ mutex_lock(&data->suspend_mutex);
+ if (data->suspended)
+ goto exit;
+
+ if (data->pre_suspend) {
+ retval = data->pre_suspend(data->pm_data);
+ if (retval)
+ goto exit;
+ }
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list)
+ if (entry->fh && entry->fh->suspend) {
+ retval = entry->fh->suspend(entry);
+ if (retval < 0)
+ goto exit;
+ }
+
+ if (data->f01_container && data->f01_container->fh
+ && data->f01_container->fh->suspend) {
+ retval = data->f01_container->fh->suspend(data->f01_container);
+ if (retval < 0)
+ goto exit;
+ }
+ data->suspended = true;
+
+exit:
+ mutex_unlock(&data->suspend_mutex);
+ return retval;
+}
+
+static int rmi_driver_resume(struct device *dev)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+ struct rmi_function_container *entry;
+ int retval = 0;
+
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ mutex_lock(&data->suspend_mutex);
+ if (!data->suspended)
+ goto exit;
+
+ if (data->f01_container && data->f01_container->fh
+ && data->f01_container->fh->resume) {
+ retval = data->f01_container->fh->resume(data->f01_container);
+ if (retval < 0)
+ goto exit;
+ }
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list)
+ if (entry->fh && entry->fh->resume) {
+ retval = entry->fh->resume(entry);
+ if (retval < 0)
+ goto exit;
+ }
+
+ if (data->post_resume) {
+ retval = data->post_resume(data->pm_data);
+ if (retval)
+ goto exit;
+ }
+
+ data->suspended = false;
+
+exit:
+ mutex_unlock(&data->suspend_mutex);
+ return retval;
+}
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+static void rmi_driver_early_suspend(struct early_suspend *h)
+{
+ struct rmi_device *rmi_dev =
+ container_of(h, struct rmi_device, early_suspend_handler);
+
+ dev_dbg(&rmi_dev->dev, "Early suspend.\n");
+ rmi_driver_suspend(&rmi_dev->dev);
+}
+
+static void rmi_driver_late_resume(struct early_suspend *h)
+{
+ struct rmi_device *rmi_dev =
+ container_of(h, struct rmi_device, early_suspend_handler);
+
+ dev_dbg(&rmi_dev->dev, "Late resume.\n");
+ rmi_driver_resume(&rmi_dev->dev);
+}
+#endif /* CONFIG_HAS_EARLYSUSPEND */
+#endif /* CONFIG_PM */
+
+static int __devexit rmi_driver_remove(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+ struct rmi_function_container *entry;
+ int i;
+
+ list_for_each_entry(entry, &data->rmi_functions.list, list)
+ if (entry->fh && entry->fh->remove)
+ entry->fh->remove(entry);
+
+ rmi_free_function_list(rmi_dev);
+ for (i = 0; i < ARRAY_SIZE(attrs); i++)
+ device_remove_file(&rmi_dev->dev, &attrs[i]);
+ kfree(data->f01_container->irq_mask);
+ kfree(data->irq_mask_store);
+ kfree(data->current_irq_mask);
+ kfree(data);
+
+ return 0;
+}
+
+#ifdef UNIVERSAL_DEV_PM_OPS
+static UNIVERSAL_DEV_PM_OPS(rmi_driver_pm, rmi_driver_suspend,
+ rmi_driver_resume, NULL);
+#endif
+
+static struct rmi_driver sensor_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ // .name = "rmi-generic",
+ .name = "rmi_generic",
+#ifdef UNIVERSAL_DEV_PM_OPS
+ .pm = &rmi_driver_pm,
+#endif
+ },
+ .probe = rmi_driver_probe,
+ .irq_handler = rmi_driver_irq_handler,
+ .fh_add = rmi_driver_fh_add,
+ .fh_remove = rmi_driver_fh_remove,
+ .get_func_irq_mask = rmi_driver_irq_get_mask,
+ .store_irq_mask = rmi_driver_irq_save,
+ .restore_irq_mask = rmi_driver_irq_restore,
+ .remove = __devexit_p(rmi_driver_remove)
+};
+
+/* Utility routine to handle writes to read-only attributes. Hopefully
+ * this will never happen, but if the user does something stupid, we don't
+ * want to accept it quietly (which is what can happen if you just put NULL
+ * for the attribute's store function).
+ */
+ssize_t rmi_store_error(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ dev_warn(dev,
+ "RMI4 WARNING: Attempt to write %d characters to read-only "
+ "attribute %s.", count, attr->attr.name);
+ return -EPERM;
+}
+
+/* Utility routine to handle reads of write-only attributes. Hopefully
+ * this will never happen, but if the user does something stupid, we don't
+ * want to accept it quietly (which is what can happen if you just put NULL
+ * for the attribute's show function).
+ */
+ssize_t rmi_show_error(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ dev_warn(dev,
+ "RMI4 WARNING: Attempt to read from write-only attribute %s.",
+ attr->attr.name);
+ return -EPERM;
+}
+
+/* sysfs show and store fns for driver attributes */
+static ssize_t rmi_driver_hasbsr_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", has_bsr(data));
+}
+
+static ssize_t rmi_driver_bsr_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->bsr);
+}
+
+static ssize_t rmi_driver_bsr_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ unsigned long val;
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ /* need to convert the string data to an actual value */
+ retval = strict_strtoul(buf, 10, &val);
+ if (retval < 0) {
+ dev_err(dev, "Invalid value '%s' written to BSR.\n", buf);
+ return -EINVAL;
+ }
+
+ retval = rmi_write(rmi_dev, BSR_LOCATION, (unsigned char)val);
+ if (retval) {
+ dev_err(dev, "%s : failed to write bsr %u to 0x%x\n",
+ __func__, (unsigned int)val, BSR_LOCATION);
+ return retval;
+ }
+
+ data->bsr = val;
+
+ return count;
+}
+
+static void disable_sensor(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+
+ rmi_dev->phys->disable_device(rmi_dev->phys);
+
+ data->enabled = false;
+}
+
+static int enable_sensor(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *data = rmi_get_driverdata(rmi_dev);
+ int retval = 0;
+ pr_info("in function ____%s____ \n", __func__);
+ retval = rmi_dev->phys->enable_device(rmi_dev->phys);
+ /* non-zero means error occurred */
+ if (retval)
+ return retval;
+
+ data->enabled = true;
+
+ return 0;
+}
+
+static ssize_t rmi_driver_enabled_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->enabled);
+}
+
+static ssize_t rmi_driver_enabled_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ int new_value;
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ if (sysfs_streq(buf, "0"))
+ new_value = false;
+ else if (sysfs_streq(buf, "1"))
+ new_value = true;
+ else
+ return -EINVAL;
+
+ if (new_value) {
+ retval = enable_sensor(rmi_dev);
+ if (retval) {
+ dev_err(dev, "Failed to enable sensor, code=%d.\n",
+ retval);
+ return -EIO;
+ }
+ } else {
+ disable_sensor(rmi_dev);
+ }
+
+ return count;
+}
+
+#if REGISTER_DEBUG
+static ssize_t rmi_driver_reg_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ unsigned int address;
+ unsigned int bytes;
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *data;
+ u8 readbuf[128];
+ unsigned char outbuf[512];
+ unsigned char *bufptr = outbuf;
+ int i;
+
+ rmi_dev = to_rmi_device(dev);
+ data = rmi_get_driverdata(rmi_dev);
+
+ retval = sscanf(buf, "%x %u", &address, &bytes);
+ if (retval != 2) {
+ dev_err(dev, "Invalid input (code %d) for reg store: %s",
+ retval, buf);
+ return -EINVAL;
+ }
+ if (address < 0 || address > 0xFFFF) {
+ dev_err(dev, "Invalid address for reg store '%#06x'.\n",
+ address);
+ return -EINVAL;
+ }
+ if (bytes < 0 || bytes >= sizeof(readbuf) || address+bytes > 65535) {
+ dev_err(dev, "Invalid byte count for reg store '%d'.\n",
+ bytes);
+ return -EINVAL;
+ }
+
+ retval = rmi_read_block(rmi_dev, address, readbuf, bytes);
+ if (retval != bytes) {
+ dev_err(dev, "Failed to read %d registers at %#06x, code %d.\n",
+ bytes, address, retval);
+ return retval;
+ }
+
+ dev_info(dev, "Reading %d bytes from %#06x.\n", bytes, address);
+ for (i = 0; i < bytes; i++) {
+ retval = snprintf(bufptr, 4, "%02X ", readbuf[i]);
+ if (retval < 0) {
+ dev_err(dev, "Failed to format string. Code: %d",
+ retval);
+ return retval;
+ }
+ bufptr += retval;
+ }
+ dev_info(dev, "%s\n", outbuf);
+
+ return count;
+}
+#endif
+
+#if DELAY_DEBUG
+static ssize_t rmi_delay_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int retval;
+ struct rmi_device *rmi_dev;
+ struct rmi_device_platform_data *pdata;
+ unsigned int new_read_delay;
+ unsigned int new_write_delay;
+ unsigned int new_block_delay;
+ unsigned int new_pre_delay;
+ unsigned int new_post_delay;
+
+ retval = sscanf(buf, "%u %u %u %u %u", &new_read_delay,
+ &new_write_delay, &new_block_delay,
+ &new_pre_delay, &new_post_delay);
+ if (retval != 5) {
+ dev_err(dev, "Incorrect number of values provided for delay.");
+ return -EINVAL;
+ }
+ if (new_read_delay < 0) {
+ dev_err(dev, "Byte delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+ if (new_write_delay < 0) {
+ dev_err(dev, "Write delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+ if (new_block_delay < 0) {
+ dev_err(dev, "Block delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+ if (new_pre_delay < 0) {
+ dev_err(dev,
+ "Pre-transfer delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+ if (new_post_delay < 0) {
+ dev_err(dev,
+ "Post-transfer delay must be positive microseconds.\n");
+ return -EINVAL;
+ }
+
+ rmi_dev = to_rmi_device(dev);
+ pdata = rmi_dev->phys->dev->platform_data;
+
+ dev_info(dev, "Setting delays to %u %u %u %u %u.\n", new_read_delay,
+ new_write_delay, new_block_delay, new_pre_delay,
+ new_post_delay);
+ pdata->spi_data.read_delay_us = new_read_delay;
+ pdata->spi_data.write_delay_us = new_write_delay;
+ pdata->spi_data.block_delay_us = new_block_delay;
+ pdata->spi_data.pre_delay_us = new_pre_delay;
+ pdata->spi_data.post_delay_us = new_post_delay;
+
+ return count;
+}
+
+static ssize_t rmi_delay_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_device_platform_data *pdata;
+
+ rmi_dev = to_rmi_device(dev);
+ pdata = rmi_dev->phys->dev->platform_data;
+
+ return snprintf(buf, PAGE_SIZE, "%d %d %d %d %d\n",
+ pdata->spi_data.read_delay_us, pdata->spi_data.write_delay_us,
+ pdata->spi_data.block_delay_us,
+ pdata->spi_data.pre_delay_us, pdata->spi_data.post_delay_us);
+}
+#endif
+
+static ssize_t rmi_driver_phys_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_device *rmi_dev;
+ struct rmi_phys_info *info;
+
+ rmi_dev = to_rmi_device(dev);
+ info = &rmi_dev->phys->info;
+
+ return snprintf(buf, PAGE_SIZE, "%-5s %ld %ld %ld %ld %ld %ld %ld\n",
+ info->proto ? info->proto : "unk",
+ info->tx_count, info->tx_bytes, info->tx_errs,
+ info->rx_count, info->rx_bytes, info->rx_errs,
+ info->attn_count);
+}
+
+static ssize_t rmi_driver_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ RMI_DRIVER_VERSION_STRING);
+}
+
+static int __init rmi_driver_init(void)
+{
+ return rmi_register_driver(&sensor_driver);
+}
+
+static void __exit rmi_driver_exit(void)
+{
+ rmi_unregister_driver(&sensor_driver);
+}
+
+module_init(rmi_driver_init);
+module_exit(rmi_driver_exit);
+
+MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
+MODULE_DESCRIPTION("RMI generic driver");
diff --git a/drivers/input/touchscreen/rmi4/rmi_driver.h b/drivers/input/touchscreen/rmi4/rmi_driver.h
new file mode 100644
index 000000000000..9e9f2c098225
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_driver.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _RMI_DRIVER_H
+#define _RMI_DRIVER_H
+
+#define RMI_DRIVER_MAJOR_VERSION 1
+#define RMI_DRIVER_MINOR_VERSION 3
+#define RMI_DRIVER_SUB_MINOR_VERSION 0
+
+/* TODO: Figure out some way to construct this string in the define macro
+ * using the values defined above.
+ */
+#define RMI_DRIVER_VERSION_STRING "1.3.0"
+
+
+#define RMI_PRODUCT_ID_LENGTH 10
+#define RMI_PRODUCT_INFO_LENGTH 2
+#define RMI_DATE_CODE_LENGTH 3
+
+struct rmi_driver_data {
+ struct rmi_function_container rmi_functions;
+
+ struct rmi_function_container *f01_container;
+
+ int num_of_irq_regs;
+ u8 *current_irq_mask;
+ u8 *irq_mask_store;
+ bool irq_stored;
+ struct mutex irq_mutex;
+ struct lock_class_key irq_key;
+
+ unsigned char pdt_props;
+ unsigned char bsr;
+ bool enabled;
+
+ u8 manufacturer_id;
+ /* product id + null termination */
+ u8 product_id[RMI_PRODUCT_ID_LENGTH + 1];
+
+#ifdef CONFIG_PM
+ bool suspended;
+ struct mutex suspend_mutex;
+
+ void *pm_data;
+ int (*pre_suspend) (const void *pm_data);
+ int (*post_resume) (const void *pm_data);
+#endif
+
+ void *data;
+};
+
+struct pdt_entry {
+ u8 query_base_addr:8;
+ u8 command_base_addr:8;
+ u8 control_base_addr:8;
+ u8 data_base_addr:8;
+ u8 interrupt_source_count:3;
+ u8 bits3and4:2;
+ u8 function_version:2;
+ u8 bit7:1;
+ u8 function_number:8;
+};
+
+int rmi_driver_f01_init(struct rmi_device *rmi_dev);
+
+static inline void copy_pdt_entry_to_fd(struct pdt_entry *pdt,
+ struct rmi_function_descriptor *fd,
+ u16 page_start)
+{
+ fd->query_base_addr = pdt->query_base_addr + page_start;
+ fd->command_base_addr = pdt->command_base_addr + page_start;
+ fd->control_base_addr = pdt->control_base_addr + page_start;
+ fd->data_base_addr = pdt->data_base_addr + page_start;
+ fd->function_number = pdt->function_number;
+ fd->interrupt_source_count = pdt->interrupt_source_count;
+ fd->function_version = pdt->function_version;
+}
+
+/* Helper function to convert a short (in host processor endianess) to
+ * a byte array in the RMI endianess for shorts. See above comment for
+ * why we dont us htons or something like that.
+ */
+void hstoba(u8 *dest, u16 src);
+
+/* Helper fn to convert a byte array representing a short in the RMI
+ * endian-ness to a short in the native processor's specific endianness.
+ * We don't use ntohs/htons here because, well, we're not dealing with
+ * a pair of shorts. And casting dest to short* wouldn't work, because
+ * that would imply knowing the byte order of short in the first place.
+ */
+void batohs(u16 *dest, u8 *src);
+
+#endif
diff --git a/drivers/input/touchscreen/rmi4/rmi_f01.c b/drivers/input/touchscreen/rmi4/rmi_f01.c
new file mode 100644
index 000000000000..ef9adb0586b9
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_f01.c
@@ -0,0 +1,775 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define DEBUG
+
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include "rmi_driver.h"
+
+/* control register bits */
+#define RMI_SLEEP_MODE_NORMAL (0x00)
+#define RMI_SLEEP_MODE_SENSOR_SLEEP (0x01)
+#define RMI_SLEEP_MODE_RESERVED0 (0x02)
+#define RMI_SLEEP_MODE_RESERVED1 (0x03)
+
+#define RMI_IS_VALID_SLEEPMODE(mode) \
+ (mode >= RMI_SLEEP_MODE_NORMAL && mode <= RMI_SLEEP_MODE_RESERVED1)
+
+union f01_device_commands {
+ struct {
+ u8 reset:1;
+ u8 reserved:1;
+ };
+ u8 reg;
+};
+
+union f01_device_control {
+ struct {
+ u8 sleep_mode:2;
+ u8 nosleep:1;
+ u8 reserved:2;
+ u8 charger_input:1;
+ u8 report_rate:1;
+ u8 configured:1;
+ };
+ u8 reg;
+};
+
+union f01_device_status {
+ struct {
+ u8 status_code:4;
+ u8 reserved:2;
+ u8 flash_prog:1;
+ u8 unconfigured:1;
+ };
+ u8 reg;
+};
+
+union f01_basic_queries {
+ struct {
+ u8 manufacturer_id:8;
+
+ u8 custom_map:1;
+ u8 non_compliant:1;
+ u8 q1_bit_2:1;
+ u8 has_sensor_id:1;
+ u8 has_charger_input:1;
+ u8 has_adjustable_doze:1;
+ u8 has_adjustable_doze_holdoff:1;
+ u8 q1_bit_7:1;
+
+ u8 productinfo_1:7;
+ u8 q2_bit_7:1;
+ u8 productinfo_2:7;
+ u8 q3_bit_7:1;
+
+ u8 year:5;
+ u8 month:4;
+ u8 day:5;
+ u8 cp1:1;
+ u8 cp2:1;
+ u8 wafer_id1_lsb:8;
+ u8 wafer_id1_msb:8;
+ u8 wafer_id2_lsb:8;
+ u8 wafer_id2_msb:8;
+ u8 wafer_id3_lsb:8;
+ };
+ u8 regs[11];
+};
+
+struct f01_data {
+ union f01_device_control device_control;
+ union f01_basic_queries basic_queries;
+ union f01_device_status device_status;
+ u8 product_id[RMI_PRODUCT_ID_LENGTH+1];
+
+#ifdef CONFIG_PM
+ bool suspended;
+ bool old_nosleep;
+#endif
+};
+
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_sleepmode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_chargerinput_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_chargerinput_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_01_configured_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_unconfigured_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_flashprog_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_01_statuscode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static struct device_attribute fn_01_attrs[] = {
+ __ATTR(productinfo, RMI_RO_ATTR,
+ rmi_fn_01_productinfo_show, rmi_store_error),
+ __ATTR(productid, RMI_RO_ATTR,
+ rmi_fn_01_productid_show, rmi_store_error),
+ __ATTR(manufacturer, RMI_RO_ATTR,
+ rmi_fn_01_manufacturer_show, rmi_store_error),
+ __ATTR(datecode, RMI_RO_ATTR,
+ rmi_fn_01_datecode_show, rmi_store_error),
+
+ /* control register access */
+ __ATTR(sleepmode, RMI_RW_ATTR,
+ rmi_fn_01_sleepmode_show, rmi_fn_01_sleepmode_store),
+ __ATTR(nosleep, RMI_RW_ATTR,
+ rmi_fn_01_nosleep_show, rmi_fn_01_nosleep_store),
+ __ATTR(chargerinput, RMI_RW_ATTR,
+ rmi_fn_01_chargerinput_show, rmi_fn_01_chargerinput_store),
+ __ATTR(reportrate, RMI_RW_ATTR,
+ rmi_fn_01_reportrate_show, rmi_fn_01_reportrate_store),
+ /* We make report rate RO, since the driver uses that to look for
+ * resets. We don't want someone faking us out by changing that
+ * bit.
+ */
+ __ATTR(configured, RMI_RO_ATTR,
+ rmi_fn_01_configured_show, rmi_store_error),
+
+ /* Command register access. */
+ __ATTR(reset, RMI_WO_ATTR,
+ rmi_show_error, rmi_fn_01_reset_store),
+
+ /* STatus register access. */
+ __ATTR(unconfigured, RMI_RO_ATTR,
+ rmi_fn_01_unconfigured_show, rmi_store_error),
+ __ATTR(flashprog, RMI_RO_ATTR,
+ rmi_fn_01_flashprog_show, rmi_store_error),
+ __ATTR(statuscode, RMI_RO_ATTR,
+ rmi_fn_01_statuscode_show, rmi_store_error),
+};
+
+/* Utility routine to set the value of a bit field in a register. */
+int rmi_set_bit_field(struct rmi_device *rmi_dev,
+ unsigned short address,
+ unsigned char field_mask,
+ unsigned char bits)
+{
+ unsigned char reg_contents;
+ int retval;
+
+ retval = rmi_read(rmi_dev, address, &reg_contents);
+ if (retval)
+ return retval;
+ reg_contents = (reg_contents & ~field_mask) | bits;
+ retval = rmi_write(rmi_dev, address, reg_contents);
+ if (retval == 1)
+ return 0;
+ else if (retval == 0)
+ return -EIO;
+ return retval;
+}
+
+static ssize_t rmi_fn_01_productinfo_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "0x%02x 0x%02x\n",
+ data->basic_queries.productinfo_1,
+ data->basic_queries.productinfo_2);
+}
+
+static ssize_t rmi_fn_01_productid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%s\n", data->product_id);
+}
+
+static ssize_t rmi_fn_01_manufacturer_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "0x%02x\n",
+ data->basic_queries.manufacturer_id);
+}
+
+static ssize_t rmi_fn_01_datecode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "20%02u-%02u-%02u\n",
+ data->basic_queries.year,
+ data->basic_queries.month,
+ data->basic_queries.day);
+}
+
+static ssize_t rmi_fn_01_reset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc = NULL;
+ unsigned int reset;
+ int retval = 0;
+ /* Command register always reads as 0, so we can just use a local. */
+ union f01_device_commands commands = {};
+
+ fc = to_rmi_function_container(dev);
+
+ if (sscanf(buf, "%u", &reset) != 1)
+ return -EINVAL;
+ if (reset < 0 || reset > 1)
+ return -EINVAL;
+
+ /* Per spec, 0 has no effect, so we skip it entirely. */
+ if (reset) {
+ commands.reset = 1;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+ &commands.reg, sizeof(commands.reg));
+ if (retval < 0) {
+ dev_err(dev, "%s: failed to issue reset command, "
+ "error = %d.", __func__, retval);
+ return retval;
+ }
+ }
+
+ return count;
+}
+
+static ssize_t rmi_fn_01_sleepmode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE,
+ "%d\n", data->device_control.sleep_mode);
+}
+
+static ssize_t rmi_fn_01_sleepmode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct f01_data *data = NULL;
+ unsigned long new_value;
+ int retval;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || !RMI_IS_VALID_SLEEPMODE(new_value)) {
+ dev_err(dev, "%s: Invalid sleep mode %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Setting sleep mode to %ld.", new_value);
+ data->device_control.sleep_mode = new_value;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ if (retval >= 0)
+ retval = count;
+ else
+ dev_err(dev, "Failed to write sleep mode, code %d.\n", retval);
+ return retval;
+}
+
+static ssize_t rmi_fn_01_nosleep_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", data->device_control.nosleep);
+}
+
+static ssize_t rmi_fn_01_nosleep_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct f01_data *data = NULL;
+ unsigned long new_value;
+ int retval;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || new_value < 0 || new_value > 1) {
+ dev_err(dev, "%s: Invalid nosleep bit %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ data->device_control.nosleep = new_value;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ if (retval >= 0)
+ retval = count;
+ else
+ dev_err(dev, "Failed to write nosleep bit.\n");
+ return retval;
+}
+
+static ssize_t rmi_fn_01_chargerinput_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_control.charger_input);
+}
+
+static ssize_t rmi_fn_01_chargerinput_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct f01_data *data = NULL;
+ unsigned long new_value;
+ int retval;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || new_value < 0 || new_value > 1) {
+ dev_err(dev, "%s: Invalid chargerinput bit %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ data->device_control.charger_input = new_value;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ if (retval >= 0)
+ retval = count;
+ else
+ dev_err(dev, "Failed to write chargerinput bit.\n");
+ return retval;
+}
+
+static ssize_t rmi_fn_01_reportrate_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_control.report_rate);
+}
+
+static ssize_t rmi_fn_01_reportrate_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct f01_data *data = NULL;
+ unsigned long new_value;
+ int retval;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ retval = strict_strtoul(buf, 10, &new_value);
+ if (retval < 0 || new_value < 0 || new_value > 1) {
+ dev_err(dev, "%s: Invalid reportrate bit %s.", __func__, buf);
+ return -EINVAL;
+ }
+
+ data->device_control.report_rate = new_value;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ if (retval >= 0)
+ retval = count;
+ else
+ dev_err(dev, "Failed to write reportrate bit.\n");
+ return retval;
+}
+
+static ssize_t rmi_fn_01_configured_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_control.configured);
+}
+
+static ssize_t rmi_fn_01_unconfigured_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_status.unconfigured);
+}
+
+static ssize_t rmi_fn_01_flashprog_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ data->device_status.flash_prog);
+}
+
+static ssize_t rmi_fn_01_statuscode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct f01_data *data = NULL;
+ struct rmi_function_container *fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "0x%02x\n",
+ data->device_status.status_code);
+}
+
+int rmi_driver_f01_init(struct rmi_device *rmi_dev)
+{
+ struct rmi_driver_data *driver_data = rmi_get_driverdata(rmi_dev);
+ struct rmi_function_container *fc = driver_data->f01_container;
+ struct f01_data *data;
+ int error;
+ u8 temp;
+ int attr_count;
+
+ data = kzalloc(sizeof(struct f01_data), GFP_KERNEL);
+ if (!data) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F01 data.\n");
+ return -ENOMEM;
+ }
+ fc->data = data;
+
+ /* Set the configured bit. */
+ error = rmi_read_block(rmi_dev, fc->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to read F01 control.\n");
+ goto error_exit;
+ }
+
+ /* Sleep mode might be set as a hangover from a system crash or
+ * reboot without power cycle. If so, clear it so the sensor
+ * is certain to function.
+ */
+ if (data->device_control.sleep_mode != RMI_SLEEP_MODE_NORMAL) {
+ dev_warn(&fc->dev,
+ "WARNING: Non-zero sleep mode found. Clearing...\n");
+ data->device_control.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+ }
+
+ data->device_control.configured = 1;
+ error = rmi_write_block(rmi_dev, fc->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to write F01 control.\n");
+ goto error_exit;
+ }
+
+ /* dummy read in order to clear irqs */
+ error = rmi_read(rmi_dev, fc->fd.data_base_addr + 1, &temp);
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to read Interrupt Status.\n");
+ goto error_exit;
+ }
+
+ error = rmi_read_block(rmi_dev, fc->fd.query_base_addr,
+ data->basic_queries.regs,
+ sizeof(data->basic_queries.regs));
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to read device query registers.\n");
+ goto error_exit;
+ }
+ driver_data->manufacturer_id = data->basic_queries.manufacturer_id;
+
+ error = rmi_read_block(rmi_dev,
+ fc->fd.query_base_addr + sizeof(data->basic_queries.regs),
+ data->product_id, RMI_PRODUCT_ID_LENGTH);
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to read product ID.\n");
+ goto error_exit;
+ }
+ data->product_id[RMI_PRODUCT_ID_LENGTH] = '\0';
+ memcpy(driver_data->product_id, data->product_id,
+ sizeof(data->product_id));
+
+ error = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+ &data->device_status.reg,
+ sizeof(data->device_status.reg));
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to read device status.\n");
+ goto error_exit;
+ }
+ if (data->device_status.unconfigured) {
+ dev_err(&fc->dev,
+ "Device reset during configuration process, status: "
+ "%#02x!\n", data->device_status.status_code);
+ error = -EINVAL;
+ goto error_exit;
+ }
+ /*
+ ** attach the routines that handle sysfs interaction
+ ** Meaning: Set up sysfs device attributes.
+ */
+ for (attr_count = 0; attr_count < ARRAY_SIZE(fn_01_attrs);
+ attr_count++) {
+ if (sysfs_create_file(&fc->dev.kobj,
+ &fn_01_attrs[attr_count].attr) < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+ fn_01_attrs[attr_count].attr.name);
+ error = -ENODEV;
+ goto error_exit;
+ }
+ }
+
+ return error;
+
+ error_exit:
+ kfree(data);
+ return error;
+}
+
+#ifdef CONFIG_PM
+
+static int rmi_f01_suspend(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct rmi_driver_data *driver_data = rmi_get_driverdata(rmi_dev);
+ struct f01_data *data = driver_data->f01_container->data;
+ int retval = 0;
+
+ dev_dbg(&fc->dev, "Suspending...\n");
+ if (data->suspended)
+ return 0;
+
+ data->old_nosleep = data->device_control.nosleep;
+ data->device_control.nosleep = 0;
+ data->device_control.sleep_mode = RMI_SLEEP_MODE_SENSOR_SLEEP;
+
+ retval = rmi_write_block(rmi_dev,
+ driver_data->f01_container->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to write sleep mode. "
+ "Code: %d.\n", retval);
+ data->device_control.nosleep = data->old_nosleep;
+ data->device_control.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+ } else {
+ data->suspended = true;
+ retval = 0;
+ }
+
+ return retval;
+}
+
+static int rmi_f01_resume(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct rmi_driver_data *driver_data = rmi_get_driverdata(rmi_dev);
+ struct f01_data *data = driver_data->f01_container->data;
+ int retval = 0;
+
+ dev_dbg(&fc->dev, "Resuming...\n");
+ if (!data->suspended)
+ return 0;
+
+ data->device_control.nosleep = data->old_nosleep;
+ data->device_control.sleep_mode = RMI_SLEEP_MODE_NORMAL;
+
+ retval = rmi_write_block(rmi_dev,
+ driver_data->f01_container->fd.control_base_addr,
+ &data->device_control.reg,
+ sizeof(data->device_control.reg));
+ if (retval < 0)
+ dev_err(&fc->dev, "Failed to restore normal operation. "
+ "Code: %d.\n", retval);
+ else {
+ data->suspended = false;
+ retval = 0;
+ }
+
+ return retval;
+}
+#endif /* CONFIG_PM */
+
+static int rmi_f01_init(struct rmi_function_container *fc)
+{
+ return 0;
+}
+
+static int rmi_f01_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f01_data *data = fc->data;
+ int error;
+
+ error = rmi_read_block(rmi_dev, fc->fd.data_base_addr,
+ &data->device_status.reg,
+ sizeof(data->device_status.reg));
+ if (error < 0) {
+ dev_err(&fc->dev, "Failed to read device status.\n");
+ return error;
+ }
+
+ /* TODO: Do we handle reset here or elsewhere? */
+ if (data->device_status.unconfigured)
+ dev_warn(&rmi_dev->dev, "Reset detected! Status code: %#04x.\n",
+ data->device_status.status_code);
+ return 0;
+}
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x01,
+ .init = rmi_f01_init,
+ .attention = rmi_f01_attention,
+#ifdef CONFIG_PM
+ .suspend = rmi_f01_suspend,
+ .resume = rmi_f01_resume,
+#endif
+};
+
+static int __init rmi_f01_module_init(void)
+{
+ int error;
+
+ error = rmi_register_function_driver(&function_handler);
+ if (error < 0) {
+ pr_err("%s: register failed!\n", __func__);
+ return error;
+ }
+
+ return 0;
+}
+
+static void __exit rmi_f01_module_exit(void)
+{
+ rmi_unregister_function_driver(&function_handler);
+}
+
+module_init(rmi_f01_module_init);
+module_exit(rmi_f01_module_exit);
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_DESCRIPTION("RMI F01 module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_f09.c b/drivers/input/touchscreen/rmi4/rmi_f09.c
new file mode 100644
index 000000000000..0ec980d7db07
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_f09.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#define QUERY_BASE_INDEX 1
+#define MAX_LEN 256
+
+/* data specific to fn $09 that needs to be kept around */
+struct f09_query {
+ u8 Limit_Register_Count;
+ union {
+ struct {
+ u8 Result_Register_Count:3;
+ u8 Reserved:3;
+ u8 InternalLimits:1;
+ u8 HostTestEn:1;
+ };
+ u8 f09_bist_query1;
+ };
+};
+
+struct f09_control {
+ /* test1 */
+ u8 Test1LimitLo;
+ u8 Test1LimitHi;
+ u8 Test1LimitDiff;
+ /* test2 */
+ u8 Test2LimitLo;
+ u8 Test2LimitHi;
+ u8 Test2LimitDiff;
+};
+
+struct f09_data {
+ u8 TestNumberControl;
+ u8 Overall_BIST_Result;
+ u8 TestResult1;
+ u8 TestResult2;
+ u8 Transmitter_Number;
+
+ union {
+ struct {
+ u8 Receiver_Number:6;
+ u8 Limit_Failure_Code:2;
+ };
+ u8 f09_bist_data2;
+ };
+};
+
+struct f09_cmd {
+ union {
+ struct {
+ u8 RunBIST:1;
+ };
+ u8 f09_bist_cmd0;
+ };
+};
+
+struct rmi_fn_09_data {
+ struct f09_query query;
+};
+
+static ssize_t rmi_f09_Limit_Register_Count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_HostTestEn_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_HostTestEn_store(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, size_t count);
+
+static ssize_t rmi_f09_InternalLimits_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_Result_Register_Count_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_Overall_BIST_Result_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f09_Overall_BIST_Result_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static struct device_attribute attrs[] = {
+ __ATTR(Limit_Register_Count, RMI_RO_ATTR,
+ rmi_f09_Limit_Register_Count_show, rmi_store_error),
+ __ATTR(HostTestEn, RMI_RW_ATTR,
+ rmi_f09_HostTestEn_show, rmi_f09_HostTestEn_store),
+ __ATTR(InternalLimits, RMI_RO_ATTR,
+ rmi_f09_Limit_Register_Count_show, rmi_store_error),
+ __ATTR(Result_Register_Count, RMI_RO_ATTR,
+ rmi_f09_Result_Register_Count_show, rmi_store_error),
+};
+
+static int rmi_f09_init(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct rmi_device_platform_data *pdata;
+ struct rmi_fn_09_data *f09;
+ u8 query_base_addr;
+ int rc;
+ int i;
+ int attr_count = 0;
+ int retval = 0;
+
+ dev_info(&fc->dev, "Intializing F09 values.");
+
+ f09 = kzalloc(sizeof(struct rmi_fn_09_data), GFP_KERNEL);
+ if (!f09) {
+ dev_err(&fc->dev, "Failed to allocate rmi_fn_09_data.\n");
+ retval = -ENOMEM;
+ goto error_exit;
+ }
+ fc->data = f09;
+
+ pdata = to_rmi_platform_data(rmi_dev);
+ query_base_addr = fc->fd.query_base_addr;
+
+ /* initial all default values for f09 query here */
+ rc = rmi_read_block(rmi_dev, query_base_addr,
+ (u8 *)&f09->query, sizeof(f09->query));
+ if (rc < 0) {
+ dev_err(&fc->dev, "Failed to read query register."
+ " from 0x%04x\n", query_base_addr);
+ goto error_exit;
+ }
+
+ dev_dbg(&fc->dev, "Creating sysfs files.");
+ /* Set up sysfs device attributes. */
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ if (sysfs_create_file
+ (&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+ attrs[attr_count].attr.name);
+ retval = -ENODEV;
+ goto error_exit;
+ }
+ }
+ return 0;
+
+error_exit:
+ dev_err(&fc->dev, "An error occured in F09 init!\n");
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(&fc->dev.kobj,
+ &attrs[attr_count].attr);
+ kfree(f09);
+ return retval;
+}
+
+static void rmi_f09_remove(struct rmi_function_container *fc)
+{
+ struct rmi_fn_09_data *data = fc->data;
+ if (data) {
+ kfree(data->query.Limit_Register_Count);
+ kfree(data->query.f09_bist_query1);
+ }
+ kfree(fc->data);
+}
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x09,
+ .init = rmi_f09_init,
+ .remove = rmi_f09_remove
+};
+
+static int __init rmi_f09_module_init(void)
+{
+ int error;
+
+ error = rmi_register_function_driver(&function_handler);
+ if (error < 0) {
+ pr_err("%s: register failed!\n", __func__);
+ return error;
+ }
+
+ return 0;
+}
+
+static void rmi_f09_module_exit(void)
+{
+ rmi_unregister_function_driver(&function_handler);
+}
+
+
+static ssize_t rmi_f09_Limit_Register_Count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_09_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.Limit_Register_Count);
+}
+
+static ssize_t rmi_f09_HostTestEn_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_09_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.HostTestEn);
+}
+
+static ssize_t rmi_f09_HostTestEn_store(struct device *dev,
+ struct device_attribute *attr,
+ char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_09_data *data;
+ unsigned int new_value;
+ int result;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ if (sscanf(buf, "%u", &new_value) != 1) {
+ dev_err(dev,
+ "%s: Error - HostTestEn_store has an "
+ "invalid len.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (new_value < 0 || new_value > 1) {
+ dev_err(dev, "%s: Invalid HostTestEn bit %s.", __func__, buf);
+ return -EINVAL;
+ }
+ data->query.HostTestEn = new_value;
+ result = rmi_write(fc->rmi_dev, fc->fd.query_base_addr,
+ data->query.HostTestEn);
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write HostTestEn_store to 0x%x\n",
+ __func__, fc->fd.query_base_addr);
+ return result;
+ }
+
+ return count;
+
+}
+
+static ssize_t rmi_f09_InternalLimits_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_09_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.InternalLimits);
+}
+
+static ssize_t rmi_f09_Result_Register_Count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_09_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.Result_Register_Count);
+}
+
+module_init(rmi_f09_module_init);
+module_exit(rmi_f09_module_exit);
+
+MODULE_AUTHOR("Allie Xiong <axiong@Synaptics.com>");
+MODULE_DESCRIPTION("RMI F09 module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_f11.c b/drivers/input/touchscreen/rmi4/rmi_f11.c
new file mode 100644
index 000000000000..35bb945143de
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_f11.c
@@ -0,0 +1,1513 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/rmi.h>
+
+#define F11_MAX_NUM_OF_SENSORS 8
+#define F11_MAX_NUM_OF_FINGERS 10
+#define F11_MAX_NUM_OF_TOUCH_SHAPES 16
+
+#define F11_REL_POS_MIN -128
+#define F11_REL_POS_MAX 127
+
+#define F11_FINGER_STATE_MASK 0x03
+#define F11_FINGER_STATE_SIZE 0x02
+#define F11_FINGER_STATE_MASK_N(i) \
+ (F11_FINGER_STATE_MASK << (i%4 * F11_FINGER_STATE_SIZE))
+
+#define F11_FINGER_STATE_VAL_N(f_state, i) \
+ (f_state >> (i%4 * F11_FINGER_STATE_SIZE))
+
+#define F11_CTRL_SENSOR_MAX_X_POS_OFFSET 6
+#define F11_CTRL_SENSOR_MAX_Y_POS_OFFSET 8
+
+#define F11_CEIL(x, y) (((x) + ((y)-1)) / (y))
+
+/* By default, we'll support two fingers if we can't figure out how many we
+ * really need to handle.
+ */
+#define DEFAULT_NR_OF_FINGERS 2
+#define DEFAULT_XY_MAX 9999
+#define DEFAULT_MAX_ABS_MT_PRESSURE 255
+#define DEFAULT_MAX_ABS_MT_TOUCH 15
+#define DEFAULT_MAX_ABS_MT_ORIENTATION 1
+#define DEFAULT_MIN_ABS_MT_TRACKING_ID 1
+#define DEFAULT_MAX_ABS_MT_TRACKING_ID 10
+#define MAX_LEN 256
+
+static ssize_t rmi_fn_11_flip_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_flip_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_clip_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_clip_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_offset_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_offset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_swap_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_11_swap_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_relreport_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_11_relreport_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_11_maxPos_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f11_rezero_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+
+static struct device_attribute attrs[] = {
+ __ATTR(flip, RMI_RW_ATTR, rmi_fn_11_flip_show, rmi_fn_11_flip_store),
+ __ATTR(clip, RMI_RW_ATTR, rmi_fn_11_clip_show, rmi_fn_11_clip_store),
+ __ATTR(offset, RMI_RW_ATTR,
+ rmi_fn_11_offset_show, rmi_fn_11_offset_store),
+ __ATTR(swap, RMI_RW_ATTR, rmi_fn_11_swap_show, rmi_fn_11_swap_store),
+ __ATTR(relreport, RMI_RW_ATTR,
+ rmi_fn_11_relreport_show, rmi_fn_11_relreport_store),
+ __ATTR(maxPos, RMI_RO_ATTR, rmi_fn_11_maxPos_show, rmi_store_error),
+ __ATTR(rezero, RMI_WO_ATTR, rmi_show_error, rmi_f11_rezero_store)
+};
+
+
+union f11_2d_commands {
+ struct {
+ u8 rezero:1;
+ };
+ u8 reg;
+};
+
+
+struct f11_2d_device_query {
+ union {
+ struct {
+ u8 nbr_of_sensors:3;
+ u8 has_query9:1;
+ u8 has_query11:1;
+ };
+ u8 f11_2d_query0;
+ };
+
+ u8 f11_2d_query9;
+
+ union {
+ struct {
+ u8 has_z_tuning:1;
+ u8 has_pos_interpolation_tuning:1;
+ u8 has_w_tuning:1;
+ u8 has_pitch_info:1;
+ u8 has_default_finger_width:1;
+ u8 has_segmentation_aggressiveness:1;
+ u8 has_tx_rw_clip:1;
+ u8 has_drumming_correction:1;
+ };
+ u8 f11_2d_query11;
+ };
+};
+
+struct f11_2d_sensor_query {
+ union {
+ struct {
+ /* query1 */
+ u8 number_of_fingers:3;
+ u8 has_rel:1;
+ u8 has_abs:1;
+ u8 has_gestures:1;
+ u8 has_sensitivity_adjust:1;
+ u8 configurable:1;
+ /* query2 */
+ u8 num_of_x_electrodes:7;
+ /* query3 */
+ u8 num_of_y_electrodes:7;
+ /* query4 */
+ u8 max_electrodes:7;
+ };
+ u8 f11_2d_query1__4[4];
+ };
+
+ union {
+ struct {
+ u8 abs_data_size:3;
+ u8 has_anchored_finger:1;
+ u8 has_adj_hyst:1;
+ u8 has_dribble:1;
+ };
+ u8 f11_2d_query5;
+ };
+
+ u8 f11_2d_query6;
+
+ union {
+ struct {
+ u8 has_single_tap:1;
+ u8 has_tap_n_hold:1;
+ u8 has_double_tap:1;
+ u8 has_early_tap:1;
+ u8 has_flick:1;
+ u8 has_press:1;
+ u8 has_pinch:1;
+ u8 padding:1;
+
+ u8 has_palm_det:1;
+ u8 has_rotate:1;
+ u8 has_touch_shapes:1;
+ u8 has_scroll_zones:1;
+ u8 has_individual_scroll_zones:1;
+ u8 has_multi_finger_scroll:1;
+ };
+ u8 f11_2d_query7__8[2];
+ };
+
+ /* Empty */
+ u8 f11_2d_query9;
+
+ union {
+ struct {
+ u8 nbr_touch_shapes:5;
+ };
+ u8 f11_2d_query10;
+ };
+};
+
+struct f11_2d_data_0 {
+ u8 finger_n;
+};
+
+struct f11_2d_data_1_5 {
+ u8 x_msb;
+ u8 y_msb;
+ u8 x_lsb:4;
+ u8 y_lsb:4;
+ u8 w_y:4;
+ u8 w_x:4;
+ u8 z;
+};
+
+struct f11_2d_data_6_7 {
+ s8 delta_x;
+ s8 delta_y;
+};
+
+struct f11_2d_data_8 {
+ u8 single_tap:1;
+ u8 tap_and_hold:1;
+ u8 double_tap:1;
+ u8 early_tap:1;
+ u8 flick:1;
+ u8 press:1;
+ u8 pinch:1;
+};
+
+struct f11_2d_data_9 {
+ u8 palm_detect:1;
+ u8 rotate:1;
+ u8 shape:1;
+ u8 scrollzone:1;
+ u8 finger_count:3;
+};
+
+struct f11_2d_data_10 {
+ u8 pinch_motion;
+};
+
+struct f11_2d_data_10_12 {
+ u8 x_flick_dist;
+ u8 y_flick_dist;
+ u8 flick_time;
+};
+
+struct f11_2d_data_11_12 {
+ u8 motion;
+ u8 finger_separation;
+};
+
+struct f11_2d_data_13 {
+ u8 shape_n;
+};
+
+struct f11_2d_data_14_15 {
+ u8 horizontal;
+ u8 vertical;
+};
+
+struct f11_2d_data_14_17 {
+ u8 x_low;
+ u8 y_right;
+ u8 x_upper;
+ u8 y_left;
+};
+
+struct f11_2d_data {
+ const struct f11_2d_data_0 *f_state;
+ const struct f11_2d_data_1_5 *abs_pos;
+ const struct f11_2d_data_6_7 *rel_pos;
+ const struct f11_2d_data_8 *gest_1;
+ const struct f11_2d_data_9 *gest_2;
+ const struct f11_2d_data_10 *pinch;
+ const struct f11_2d_data_10_12 *flick;
+ const struct f11_2d_data_11_12 *rotate;
+ const struct f11_2d_data_13 *shapes;
+ const struct f11_2d_data_14_15 *multi_scroll;
+ const struct f11_2d_data_14_17 *scroll_zones;
+};
+
+struct f11_2d_sensor {
+ struct rmi_f11_2d_axis_alignment axis_align;
+ struct f11_2d_sensor_query sens_query;
+ struct f11_2d_data data;
+ u16 max_x;
+ u16 max_y;
+ u8 nbr_fingers;
+ u8 finger_tracker[F11_MAX_NUM_OF_FINGERS];
+ u8 *data_pkt;
+ int pkt_size;
+ u8 sensor_index;
+ char input_name[MAX_LEN];
+ char input_phys[MAX_LEN];
+
+ struct input_dev *input;
+ struct input_dev *mouse_input;
+};
+
+struct f11_data {
+ struct f11_2d_device_query dev_query;
+ struct rmi_f11_2d_ctrl dev_controls;
+ struct f11_2d_sensor sensors[F11_MAX_NUM_OF_SENSORS];
+};
+
+enum finger_state_values {
+ F11_NO_FINGER = 0x00,
+ F11_PRESENT = 0x01,
+ F11_INACCURATE = 0x02,
+ F11_RESERVED = 0x03
+};
+
+static void rmi_f11_rel_pos_report(struct f11_2d_sensor *sensor, u8 n_finger)
+{
+ struct f11_2d_data *data = &sensor->data;
+ struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align;
+ s8 x, y;
+ s8 temp;
+
+ x = data->rel_pos[n_finger].delta_x;
+ y = data->rel_pos[n_finger].delta_y;
+
+ x = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)x));
+ y = min(F11_REL_POS_MAX, max(F11_REL_POS_MIN, (int)y));
+
+ if (axis_align->swap_axes) {
+ temp = x;
+ x = y;
+ y = temp;
+ }
+ if (axis_align->flip_x)
+ x = min(F11_REL_POS_MAX, -x);
+ if (axis_align->flip_y)
+ y = min(F11_REL_POS_MAX, -y);
+
+ if (x || y) {
+ input_report_rel(sensor->input, REL_X, x);
+ input_report_rel(sensor->input, REL_Y, y);
+ input_report_rel(sensor->mouse_input, REL_X, x);
+ input_report_rel(sensor->mouse_input, REL_Y, y);
+ }
+ input_sync(sensor->mouse_input);
+}
+
+static void rmi_f11_abs_pos_report(struct f11_2d_sensor *sensor,
+ u8 finger_state, u8 n_finger)
+{
+ struct f11_2d_data *data = &sensor->data;
+ struct rmi_f11_2d_axis_alignment *axis_align = &sensor->axis_align;
+ int prev_state = sensor->finger_tracker[n_finger];
+ int x, y, z;
+ int w_x, w_y, w_max, w_min, orient;
+ int temp;
+ if (prev_state && !finger_state) {
+ /* this is a release */
+ x = y = z = w_max = w_min = orient = 0;
+ } else if (!prev_state && !finger_state) {
+ /* nothing to report */
+ return;
+ } else {
+ x = ((data->abs_pos[n_finger].x_msb << 4) |
+ data->abs_pos[n_finger].x_lsb);
+ y = ((data->abs_pos[n_finger].y_msb << 4) |
+ data->abs_pos[n_finger].y_lsb);
+ z = data->abs_pos[n_finger].z;
+ w_x = data->abs_pos[n_finger].w_x;
+ w_y = data->abs_pos[n_finger].w_y;
+ w_max = max(w_x, w_y);
+ w_min = min(w_x, w_y);
+
+ if (axis_align->swap_axes) {
+ temp = x;
+ x = y;
+ y = temp;
+ temp = w_x;
+ w_x = w_y;
+ w_y = temp;
+ }
+
+ orient = w_x > w_y ? 1 : 0;
+
+ if (axis_align->flip_x)
+ x = max(sensor->max_x - x, 0);
+
+ if (axis_align->flip_y)
+ y = max(sensor->max_y - y, 0);
+
+ /*
+ ** here checking if X offset or y offset are specified is
+ ** redundant. We just add the offsets or, clip the values
+ **
+ ** note: offsets need to be done before clipping occurs,
+ ** or we could get funny values that are outside
+ ** clipping boundaries.
+ */
+ x += axis_align->offset_X;
+ y += axis_align->offset_Y;
+ x = max(axis_align->clip_X_low, x);
+ y = max(axis_align->clip_Y_low, y);
+ if (axis_align->clip_X_high)
+ x = min(axis_align->clip_X_high, x);
+ if (axis_align->clip_Y_high)
+ y = min(axis_align->clip_Y_high, y);
+
+ }
+
+ pr_debug("%s: f_state[%d]:%d - x:%d y:%d z:%d w_max:%d w_min:%d\n",
+ __func__, n_finger, finger_state, x, y, z, w_max, w_min);
+
+
+#ifdef ABS_MT_PRESSURE
+ input_report_abs(sensor->input, ABS_MT_PRESSURE, z);
+#endif
+ input_report_abs(sensor->input, ABS_MT_TOUCH_MAJOR, w_max);
+ input_report_abs(sensor->input, ABS_MT_TOUCH_MINOR, w_min);
+ input_report_abs(sensor->input, ABS_MT_ORIENTATION, orient);
+ input_report_abs(sensor->input, ABS_MT_POSITION_X, x);
+ input_report_abs(sensor->input, ABS_MT_POSITION_Y, y);
+ input_report_abs(sensor->input, ABS_MT_TRACKING_ID, n_finger);
+
+ /* MT sync between fingers */
+ input_mt_sync(sensor->input);
+ sensor->finger_tracker[n_finger] = finger_state;
+}
+
+static void rmi_f11_finger_handler(struct f11_2d_sensor *sensor)
+{
+ const struct f11_2d_data_0 *f_state = sensor->data.f_state;
+ u8 finger_state;
+ u8 finger_pressed_count;
+ u8 i;
+
+ for (i = 0, finger_pressed_count = 0; i < sensor->nbr_fingers; i++) {
+ /* Possible of having 4 fingers per f_statet register */
+ finger_state = (f_state[i >> 2].finger_n &
+ F11_FINGER_STATE_MASK_N(i));
+ finger_state = F11_FINGER_STATE_VAL_N(finger_state, i);
+
+ if (finger_state == F11_RESERVED) {
+ pr_err("%s: Invalid finger state[%d]:0x%02x.", __func__,
+ i, finger_state);
+ continue;
+ } else if ((finger_state == F11_PRESENT) ||
+ (finger_state == F11_INACCURATE)) {
+ finger_pressed_count++;
+ }
+
+ if (sensor->data.abs_pos)
+ rmi_f11_abs_pos_report(sensor, finger_state, i);
+
+ if (sensor->data.rel_pos)
+ rmi_f11_rel_pos_report(sensor, i);
+ }
+ input_report_key(sensor->input, BTN_TOUCH, finger_pressed_count);
+ input_sync(sensor->input);
+}
+
+static inline int rmi_f11_2d_construct_data(struct f11_2d_sensor *sensor)
+{
+ struct f11_2d_sensor_query *query = &sensor->sens_query;
+ struct f11_2d_data *data = &sensor->data;
+ int i;
+
+ sensor->nbr_fingers = (query->number_of_fingers == 5 ? 10 :
+ query->number_of_fingers + 1);
+
+ sensor->pkt_size = F11_CEIL(sensor->nbr_fingers, 4);
+
+ if (query->has_abs)
+ sensor->pkt_size += (sensor->nbr_fingers * 5);
+
+ if (query->has_rel)
+ sensor->pkt_size += (sensor->nbr_fingers * 2);
+
+ /* Check if F11_2D_Query7 is non-zero */
+ if (query->f11_2d_query7__8[0])
+ sensor->pkt_size += sizeof(u8);
+
+ /* Check if F11_2D_Query7 or F11_2D_Query8 is non-zero */
+ if (query->f11_2d_query7__8[0] || query->f11_2d_query7__8[1])
+ sensor->pkt_size += sizeof(u8);
+
+ if (query->has_pinch || query->has_flick || query->has_rotate) {
+ sensor->pkt_size += 3;
+ if (!query->has_flick)
+ sensor->pkt_size--;
+ if (!query->has_rotate)
+ sensor->pkt_size--;
+ }
+
+ if (query->has_touch_shapes)
+ sensor->pkt_size += F11_CEIL(query->nbr_touch_shapes + 1, 8);
+
+ sensor->data_pkt = kzalloc(sensor->pkt_size, GFP_KERNEL);
+ if (!sensor->data_pkt)
+ return -ENOMEM;
+
+ data->f_state = (struct f11_2d_data_0 *)sensor->data_pkt;
+ i = F11_CEIL(sensor->nbr_fingers, 4);
+
+ if (query->has_abs) {
+ data->abs_pos = (struct f11_2d_data_1_5 *)
+ &sensor->data_pkt[i];
+ i += (sensor->nbr_fingers * 5);
+ }
+
+ if (query->has_rel) {
+ data->rel_pos = (struct f11_2d_data_6_7 *)
+ &sensor->data_pkt[i];
+ i += (sensor->nbr_fingers * 2);
+ }
+
+ if (query->f11_2d_query7__8[0]) {
+ data->gest_1 = (struct f11_2d_data_8 *)&sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->f11_2d_query7__8[0] || query->f11_2d_query7__8[1]) {
+ data->gest_2 = (struct f11_2d_data_9 *)&sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->has_pinch) {
+ data->pinch = (struct f11_2d_data_10 *)&sensor->data_pkt[i];
+ i++;
+ }
+
+ if (query->has_flick) {
+ if (query->has_pinch) {
+ data->flick = (struct f11_2d_data_10_12 *)data->pinch;
+ i += 2;
+ } else {
+ data->flick = (struct f11_2d_data_10_12 *)
+ &sensor->data_pkt[i];
+ i += 3;
+ }
+ }
+
+ if (query->has_rotate) {
+ if (query->has_flick) {
+ data->rotate = (struct f11_2d_data_11_12 *)
+ (data->flick + 1);
+ } else {
+ data->rotate = (struct f11_2d_data_11_12 *)
+ &sensor->data_pkt[i];
+ i += 2;
+ }
+ }
+
+ if (query->has_touch_shapes)
+ data->shapes = (struct f11_2d_data_13 *)&sensor->data_pkt[i];
+
+ return 0;
+}
+
+static int rmi_f11_read_control_parameters(struct rmi_device *rmi_dev,
+ struct f11_2d_device_query *query,
+ struct rmi_f11_2d_ctrl *ctrl,
+ int ctrl_base_addr) {
+ int read_address = ctrl_base_addr;
+ int error = 0;
+
+ if (ctrl->ctrl0) {
+ error = rmi_read_block(rmi_dev, read_address, &ctrl->ctrl0->reg,
+ sizeof(union rmi_f11_2d_ctrl0));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl0, code: %d.\n", error);
+ return error;
+ }
+ read_address = read_address + sizeof(union rmi_f11_2d_ctrl0);
+ }
+
+ if (ctrl->ctrl1) {
+ error = rmi_read_block(rmi_dev, read_address, &ctrl->ctrl1->reg,
+ sizeof(union rmi_f11_2d_ctrl1));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl1, code: %d.\n", error);
+ return error;
+ }
+ read_address = read_address + sizeof(union rmi_f11_2d_ctrl1);
+ }
+
+ if (ctrl->ctrl2__3) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl2__3->regs,
+ sizeof(ctrl->ctrl2__3->regs));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl2__3, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(ctrl->ctrl2__3->regs);
+ }
+
+ if (ctrl->ctrl4) {
+ error = rmi_read_block(rmi_dev, read_address, &ctrl->ctrl4->reg,
+ sizeof(ctrl->ctrl4->reg));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl4, code: %d.\n", error);
+ return error;
+ }
+ read_address = read_address + sizeof(ctrl->ctrl4->reg);
+ }
+
+ if (ctrl->ctrl5) {
+ error = rmi_read_block(rmi_dev, read_address, &ctrl->ctrl5->reg,
+ sizeof(ctrl->ctrl5->reg));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl5, code: %d.\n", error);
+ return error;
+ }
+ read_address = read_address + sizeof(ctrl->ctrl5->reg);
+ }
+
+ if (ctrl->ctrl6__7) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl6__7->regs,
+ sizeof(ctrl->ctrl6__7->regs));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl6__7, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(ctrl->ctrl6__7->regs);
+ }
+
+ if (ctrl->ctrl8__9) {
+ error = rmi_read_block(rmi_dev, read_address,
+ ctrl->ctrl8__9->regs,
+ sizeof(ctrl->ctrl8__9->regs));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read F11 ctrl8__9, code: %d.\n",
+ error);
+ return error;
+ }
+ read_address = read_address + sizeof(ctrl->ctrl8__9->regs);
+ }
+
+ return 0;
+}
+
+static int rmi_f11_initialize_control_parameters(struct rmi_device *rmi_dev,
+ struct f11_2d_device_query *query,
+ struct rmi_f11_2d_ctrl *ctrl,
+ int ctrl_base_addr) {
+ int error = 0;
+
+ ctrl->ctrl0 = kzalloc(sizeof(union rmi_f11_2d_ctrl0), GFP_KERNEL);
+ if (!ctrl->ctrl0) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl0.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ ctrl->ctrl1 = kzalloc(sizeof(union rmi_f11_2d_ctrl1), GFP_KERNEL);
+ if (!ctrl->ctrl1) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl1.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ ctrl->ctrl2__3 = kzalloc(sizeof(union rmi_f11_2d_ctrl2__3), GFP_KERNEL);
+ if (!ctrl->ctrl2__3) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl2__3.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ ctrl->ctrl4 = kzalloc(sizeof(union rmi_f11_2d_ctrl4), GFP_KERNEL);
+ if (!ctrl->ctrl4) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl4.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ ctrl->ctrl5 = kzalloc(sizeof(union rmi_f11_2d_ctrl5), GFP_KERNEL);
+ if (!ctrl->ctrl5) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl5.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ ctrl->ctrl6__7 = kzalloc(sizeof(union rmi_f11_2d_ctrl6__7), GFP_KERNEL);
+ if (!ctrl->ctrl6__7) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl6__7.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ ctrl->ctrl8__9 = kzalloc(sizeof(union rmi_f11_2d_ctrl8__9), GFP_KERNEL);
+ if (!ctrl->ctrl8__9) {
+ dev_err(&rmi_dev->dev, "Failed to allocate F11 ctrl8__9.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ return rmi_f11_read_control_parameters(rmi_dev, query,
+ ctrl, ctrl_base_addr);
+
+error_exit:
+ kfree(ctrl->ctrl0);
+ kfree(ctrl->ctrl1);
+ kfree(ctrl->ctrl2__3);
+ kfree(ctrl->ctrl4);
+ kfree(ctrl->ctrl5);
+ kfree(ctrl->ctrl6__7);
+ kfree(ctrl->ctrl8__9);
+
+ return error;
+}
+
+static inline int rmi_f11_set_control_parameters(struct rmi_device *rmi_dev,
+ struct f11_2d_sensor_query *query,
+ struct rmi_f11_2d_ctrl *ctrl,
+ int ctrl_base_addr)
+{
+ int write_address = ctrl_base_addr;
+ int error;
+
+ if (ctrl->ctrl0) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl0->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl1) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl1->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl2__3) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl2__3->regs,
+ sizeof(ctrl->ctrl2__3->regs));
+ if (error < 0)
+ return error;
+ write_address += sizeof(ctrl->ctrl2__3->regs);
+ }
+
+ if (ctrl->ctrl4) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl4->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl5) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl5->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl6__7) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl6__7->regs[0],
+ sizeof(ctrl->ctrl6__7->regs));
+ if (error < 0)
+ return error;
+ write_address += sizeof(ctrl->ctrl6__7->regs);
+ }
+
+ if (ctrl->ctrl8__9) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl8__9->regs[0],
+ sizeof(ctrl->ctrl8__9->regs));
+ if (error < 0)
+ return error;
+ write_address += sizeof(ctrl->ctrl8__9->regs);
+ }
+
+ if (ctrl->ctrl10) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl10->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl11) {
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl11->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl12 && ctrl->ctrl12_size && query->configurable) {
+ if (ctrl->ctrl12_size > query->max_electrodes) {
+ dev_err(&rmi_dev->dev,
+ "%s: invalid cfg size:%d, should be < %d.\n",
+ __func__, ctrl->ctrl12_size,
+ query->max_electrodes);
+ return -EINVAL;
+ }
+ error = rmi_write_block(rmi_dev, write_address,
+ &ctrl->ctrl12->reg,
+ ctrl->ctrl12_size);
+ if (error < 0)
+ return error;
+ write_address += ctrl->ctrl12_size;
+ }
+
+ if (ctrl->ctrl14) {
+ error = rmi_write_block(rmi_dev,
+ write_address,
+ &ctrl->ctrl0->reg,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl15) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl15,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl16) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl16,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl17) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl17,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl18) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl18,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ if (ctrl->ctrl19) {
+ error = rmi_write_block(rmi_dev, write_address,
+ ctrl->ctrl19,
+ 1);
+ if (error < 0)
+ return error;
+ write_address++;
+ }
+
+ return 0;
+}
+
+static inline int rmi_f11_get_query_parameters(struct rmi_device *rmi_dev,
+ struct f11_2d_sensor_query *query, u8 query_base_addr)
+{
+ int query_size;
+ int rc;
+
+ rc = rmi_read_block(rmi_dev, query_base_addr, query->f11_2d_query1__4,
+ sizeof(query->f11_2d_query1__4));
+ if (rc < 0)
+ return rc;
+ query_size = rc;
+
+ if (query->has_abs) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ &query->f11_2d_query5);
+ if (rc < 0)
+ return rc;
+ query_size++;
+ }
+
+ if (query->has_rel) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ &query->f11_2d_query6);
+ if (rc < 0)
+ return rc;
+ query_size++;
+ }
+
+ if (query->has_gestures) {
+ rc = rmi_read_block(rmi_dev, query_base_addr + query_size,
+ query->f11_2d_query7__8,
+ sizeof(query->f11_2d_query7__8));
+ if (rc < 0)
+ return rc;
+ query_size += sizeof(query->f11_2d_query7__8);
+ }
+
+ if (query->has_touch_shapes) {
+ rc = rmi_read(rmi_dev, query_base_addr + query_size,
+ &query->f11_2d_query10);
+ if (rc < 0)
+ return rc;
+ query_size++;
+ }
+
+ return query_size;
+}
+
+/* This operation is done in a number of places, so we have a handy routine
+ * for it.
+ */
+static void f11_set_abs_params(struct rmi_function_container *fc, int index)
+{
+ struct f11_data *instance_data = fc->data;
+ struct input_dev *input = instance_data->sensors[index].input;
+ int device_x_max =
+ instance_data->dev_controls.ctrl6__7->sensor_max_x_pos;
+ int device_y_max =
+ instance_data->dev_controls.ctrl8__9->sensor_max_y_pos;
+ int x_min, x_max, y_min, y_max;
+
+ if (instance_data->sensors[index].axis_align.swap_axes) {
+ int temp = device_x_max;
+ device_x_max = device_y_max;
+ device_y_max = temp;
+ }
+
+ /* Use the max X and max Y read from the device, or the clip values,
+ * whichever is stricter.
+ */
+ x_min = instance_data->sensors[index].axis_align.clip_X_low;
+ if (instance_data->sensors[index].axis_align.clip_X_high)
+ x_max = min((int) device_x_max,
+ instance_data->sensors[index].axis_align.clip_X_high);
+ else
+ x_max = device_x_max;
+
+ y_min = instance_data->sensors[index].axis_align.clip_Y_low;
+ if (instance_data->sensors[index].axis_align.clip_Y_high)
+ y_max = min((int) device_y_max,
+ instance_data->sensors[index].axis_align.clip_Y_high);
+ else
+ y_max = device_y_max;
+
+ dev_dbg(&fc->dev, "Set ranges X=[%d..%d] Y=[%d..%d].",
+ x_min, x_max, y_min, y_max);
+
+#ifdef ABS_MT_PRESSURE
+ input_set_abs_params(input, ABS_MT_PRESSURE, 0,
+ DEFAULT_MAX_ABS_MT_PRESSURE, 0, 0);
+#endif
+ input_set_abs_params(input, ABS_MT_TOUCH_MAJOR,
+ 0, DEFAULT_MAX_ABS_MT_TOUCH, 0, 0);
+ input_set_abs_params(input, ABS_MT_TOUCH_MINOR,
+ 0, DEFAULT_MAX_ABS_MT_TOUCH, 0, 0);
+ input_set_abs_params(input, ABS_MT_ORIENTATION,
+ 0, DEFAULT_MAX_ABS_MT_ORIENTATION, 0, 0);
+ input_set_abs_params(input, ABS_MT_TRACKING_ID,
+ DEFAULT_MIN_ABS_MT_TRACKING_ID,
+ DEFAULT_MAX_ABS_MT_TRACKING_ID, 0, 0);
+ /* TODO get max_x_pos (and y) from control registers. */
+ input_set_abs_params(input, ABS_MT_POSITION_X,
+ x_min, x_max, 0, 0);
+ input_set_abs_params(input, ABS_MT_POSITION_Y,
+ y_min, y_max, 0, 0);
+}
+
+static int rmi_f11_init(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct rmi_device_platform_data *pdata;
+ struct f11_data *f11;
+ struct input_dev *input_dev;
+ struct input_dev *input_dev_mouse;
+ u8 query_offset;
+ u8 query_base_addr;
+ u8 control_base_addr;
+ u16 max_x_pos, max_y_pos, temp;
+ int rc;
+ int i;
+ int retval = 0;
+ int attr_count = 0;
+
+ dev_info(&fc->dev, "Intializing F11 values.");
+
+ /*
+ ** init instance data, fill in values and create any sysfs files
+ */
+ f11 = kzalloc(sizeof(struct f11_data), GFP_KERNEL);
+ if (!f11)
+ return -ENOMEM;
+ fc->data = f11;
+
+ query_base_addr = fc->fd.query_base_addr;
+ control_base_addr = fc->fd.control_base_addr;
+
+ rc = rmi_read(rmi_dev, query_base_addr, &f11->dev_query.f11_2d_query0);
+ if (rc < 0)
+ goto err_free_data;
+
+ rc = rmi_f11_initialize_control_parameters(rmi_dev, &f11->dev_query,
+ &f11->dev_controls, control_base_addr);
+ if (rc < 0) {
+ dev_err(&fc->dev,
+ "Failed to initialize F11 control params.\n");
+ goto err_free_data;
+ }
+
+ query_offset = (query_base_addr + 1);
+ /* Increase with one since number of sensors is zero based */
+ for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) {
+ f11->sensors[i].sensor_index = i;
+
+ rc = rmi_f11_get_query_parameters(rmi_dev,
+ &f11->sensors[i].sens_query,
+ query_offset);
+ if (rc < 0)
+ goto err_free_data;
+
+ query_offset += rc;
+
+ pdata = to_rmi_platform_data(rmi_dev);
+ if (pdata)
+ f11->sensors[i].axis_align = pdata->axis_align;
+
+ if (pdata && pdata->f11_ctrl) {
+ rc = rmi_f11_set_control_parameters(rmi_dev,
+ &f11->sensors[i].sens_query,
+ pdata->f11_ctrl,
+ control_base_addr);
+ if (rc < 0)
+ goto err_free_data;
+ }
+
+ if (pdata && pdata->f11_ctrl &&
+ pdata->f11_ctrl->ctrl6__7 &&
+ pdata->f11_ctrl->ctrl8__9) {
+ max_x_pos = pdata->f11_ctrl->ctrl6__7->sensor_max_x_pos;
+ max_y_pos = pdata->f11_ctrl->ctrl8__9->sensor_max_y_pos;
+
+ } else {
+ rc = rmi_read_block(rmi_dev,
+ control_base_addr + F11_CTRL_SENSOR_MAX_X_POS_OFFSET,
+ (u8 *)&max_x_pos, sizeof(max_x_pos));
+ if (rc < 0)
+ goto err_free_data;
+
+ rc = rmi_read_block(rmi_dev,
+ control_base_addr + F11_CTRL_SENSOR_MAX_Y_POS_OFFSET,
+ (u8 *)&max_y_pos, sizeof(max_y_pos));
+ if (rc < 0)
+ goto err_free_data;
+ }
+
+ if (pdata->axis_align.swap_axes) {
+ temp = max_x_pos;
+ max_x_pos = max_y_pos;
+ max_y_pos = temp;
+ }
+ f11->sensors[i].max_x = max_x_pos;
+ f11->sensors[i].max_y = max_y_pos;
+
+ rc = rmi_f11_2d_construct_data(&f11->sensors[i]);
+ if (rc < 0)
+ goto err_free_data;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ rc = -ENOMEM;
+ goto err_free_data;
+ }
+
+ f11->sensors[i].input = input_dev;
+ /* TODO how to modify the dev name and
+ * phys name for input device */
+ sprintf(f11->sensors[i].input_name, "%sfn%02x",
+ dev_name(&rmi_dev->dev), fc->fd.function_number);
+ input_dev->name = f11->sensors[i].input_name;
+ sprintf(f11->sensors[i].input_phys, "%s/input0",
+ input_dev->name);
+ input_dev->phys = f11->sensors[i].input_phys;
+ input_dev->dev.parent = &rmi_dev->dev;
+ input_set_drvdata(input_dev, f11);
+
+ set_bit(EV_SYN, input_dev->evbit);
+ set_bit(EV_KEY, input_dev->evbit);
+ set_bit(EV_ABS, input_dev->evbit);
+
+ f11_set_abs_params(fc, i);
+
+ dev_dbg(&fc->dev, "%s: Sensor %d hasRel %d.\n",
+ __func__, i, f11->sensors[i].sens_query.has_rel);
+ if (f11->sensors[i].sens_query.has_rel) {
+ set_bit(EV_REL, input_dev->evbit);
+ set_bit(REL_X, input_dev->relbit);
+ set_bit(REL_Y, input_dev->relbit);
+ }
+ rc = input_register_device(input_dev);
+ if (rc < 0)
+ goto err_free_input;
+
+ if (f11->sensors[i].sens_query.has_rel) {
+ /*create input device for mouse events */
+ input_dev_mouse = input_allocate_device();
+ if (!input_dev_mouse) {
+ rc = -ENOMEM;
+ goto err_free_data;
+ }
+
+ f11->sensors[i].mouse_input = input_dev_mouse;
+ input_dev_mouse->name = "rmi_mouse";
+ input_dev_mouse->phys = "rmi_f11/input0";
+
+ input_dev_mouse->id.vendor = 0x18d1;
+ input_dev_mouse->id.product = 0x0210;
+ input_dev_mouse->id.version = 0x0100;
+
+ set_bit(EV_REL, input_dev_mouse->evbit);
+ set_bit(REL_X, input_dev_mouse->relbit);
+ set_bit(REL_Y, input_dev_mouse->relbit);
+
+ set_bit(BTN_MOUSE, input_dev_mouse->evbit);
+ /* Register device's buttons and keys */
+ set_bit(EV_KEY, input_dev_mouse->evbit);
+ set_bit(BTN_LEFT, input_dev_mouse->keybit);
+ set_bit(BTN_MIDDLE, input_dev_mouse->keybit);
+ set_bit(BTN_RIGHT, input_dev_mouse->keybit);
+
+ rc = input_register_device(input_dev_mouse);
+ if (rc < 0)
+ goto err_free_input;
+ set_bit(BTN_RIGHT, input_dev_mouse->keybit);
+ }
+
+ }
+
+ dev_info(&fc->dev, "Creating sysfs files.");
+ dev_dbg(&fc->dev, "Creating fn11 sysfs files.");
+
+ /* Set up sysfs device attributes. */
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ if (sysfs_create_file
+ (&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+ attrs[attr_count].attr.name);
+ retval = -ENODEV;
+ goto err_free_input;
+ }
+ }
+
+ dev_info(&fc->dev, "Done Creating fn11 sysfs files.");
+ return 0;
+
+err_free_input:
+ for (i = 0; i < (f11->dev_query.nbr_of_sensors + 1); i++) {
+ if (f11->sensors[i].input)
+ input_free_device(f11->sensors[i].input);
+ if (f11->sensors[i].sens_query.has_rel &&
+ f11->sensors[i].mouse_input)
+ input_free_device(f11->sensors[i].mouse_input);
+ }
+err_free_data:
+ for (attr_count--; attr_count >= 0; attr_count--)
+ device_remove_file(&fc->rmi_dev->dev, &attrs[attr_count]);
+
+ kfree(f11);
+ return rc;
+}
+
+int rmi_f11_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f11_data *f11 = fc->data;
+ u8 data_base_addr = fc->fd.data_base_addr;
+ int data_base_addr_offset = 0;
+ int error;
+ int i;
+
+ for (i = 0; i < f11->dev_query.nbr_of_sensors + 1; i++) {
+ error = rmi_read_block(rmi_dev,
+ data_base_addr + data_base_addr_offset,
+ f11->sensors[i].data_pkt,
+ f11->sensors[i].pkt_size);
+ if (error < 0)
+ return error;
+
+ rmi_f11_finger_handler(&f11->sensors[i]);
+ data_base_addr_offset += f11->sensors[i].pkt_size;
+ }
+ return 0;
+}
+
+static void rmi_f11_remove(struct rmi_function_container *fc)
+{
+ struct f11_data *data = fc->data;
+ int i;
+
+ for (i = 0; i < (data->dev_query.nbr_of_sensors + 1); i++) {
+ input_unregister_device(data->sensors[i].input);
+ if (data->sensors[i].sens_query.has_rel)
+ input_unregister_device(data->sensors[i].mouse_input);
+ }
+ kfree(fc->data);
+}
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x11,
+ .init = rmi_f11_init,
+ .attention = rmi_f11_attention,
+ .remove = rmi_f11_remove
+};
+
+static int __init rmi_f11_module_init(void)
+{
+ int error;
+
+ error = rmi_register_function_driver(&function_handler);
+ if (error < 0) {
+ pr_err("%s: register failed!\n", __func__);
+ return error;
+ }
+
+ return 0;
+}
+
+static void __exit rmi_f11_module_exit(void)
+{
+ rmi_unregister_function_driver(&function_handler);
+}
+
+static ssize_t rmi_fn_11_maxPos_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u %u\n",
+ data->sensors[0].max_x, data->sensors[0].max_y);
+}
+
+static ssize_t rmi_fn_11_flip_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u %u\n",
+ data->sensors[0].axis_align.flip_x,
+ data->sensors[0].axis_align.flip_y);
+}
+
+static ssize_t rmi_fn_11_flip_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+ unsigned int new_X, new_Y;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+
+ if (sscanf(buf, "%u %u", &new_X, &new_Y) != 2)
+ return -EINVAL;
+ if (new_X < 0 || new_X > 1 || new_Y < 0 || new_Y > 1)
+ return -EINVAL;
+ instance_data->sensors[0].axis_align.flip_x = new_X;
+ instance_data->sensors[0].axis_align.flip_y = new_Y;
+
+ return count;
+}
+
+static ssize_t rmi_fn_11_swap_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->sensors[0].axis_align.swap_axes);
+}
+
+static ssize_t rmi_fn_11_swap_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+ unsigned int newSwap;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+
+ if (sscanf(buf, "%u", &newSwap) != 1)
+ return -EINVAL;
+ if (newSwap < 0 || newSwap > 1)
+ return -EINVAL;
+ instance_data->sensors[0].axis_align.swap_axes = newSwap;
+
+ f11_set_abs_params(fc, 0);
+
+ return count;
+}
+
+static ssize_t rmi_fn_11_relreport_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->
+ sensors[0].axis_align.rel_report_enabled);
+}
+
+static ssize_t rmi_fn_11_relreport_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+ unsigned int new_value;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+
+ if (sscanf(buf, "%u", &new_value) != 1)
+ return -EINVAL;
+ if (new_value < 0 || new_value > 1)
+ return -EINVAL;
+ instance_data->sensors[0].axis_align.rel_report_enabled = new_value;
+
+ return count;
+}
+
+static ssize_t rmi_fn_11_offset_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d %d\n",
+ instance_data->sensors[0].axis_align.offset_X,
+ instance_data->sensors[0].axis_align.offset_Y);
+}
+
+static ssize_t rmi_fn_11_offset_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+ int new_X, new_Y;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+
+ if (sscanf(buf, "%d %d", &new_X, &new_Y) != 2)
+ return -EINVAL;
+ instance_data->sensors[0].axis_align.offset_X = new_X;
+ instance_data->sensors[0].axis_align.offset_Y = new_Y;
+
+ return count;
+}
+
+static ssize_t rmi_fn_11_clip_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u %u %u %u\n",
+ instance_data->sensors[0].axis_align.clip_X_low,
+ instance_data->sensors[0].axis_align.clip_X_high,
+ instance_data->sensors[0].axis_align.clip_Y_low,
+ instance_data->sensors[0].axis_align.clip_Y_high);
+}
+
+static ssize_t rmi_fn_11_clip_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f11_data *instance_data;
+ unsigned int new_X_low, new_X_high, new_Y_low, new_Y_high;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ if (sscanf(buf, "%u %u %u %u",
+ &new_X_low, &new_X_high, &new_Y_low, &new_Y_high) != 4)
+ return -EINVAL;
+ if (new_X_low < 0 || new_X_low >= new_X_high || new_Y_low < 0
+ || new_Y_low >= new_Y_high)
+ return -EINVAL;
+ instance_data->sensors[0].axis_align.clip_X_low = new_X_low;
+ instance_data->sensors[0].axis_align.clip_X_high = new_X_high;
+ instance_data->sensors[0].axis_align.clip_Y_low = new_Y_low;
+ instance_data->sensors[0].axis_align.clip_Y_high = new_Y_high;
+
+ /*
+ ** for now, we assume this is sensor index 0
+ */
+ f11_set_abs_params(fc, 0);
+
+ return count;
+}
+
+static ssize_t rmi_f11_rezero_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc = NULL;
+ unsigned int rezero;
+ int retval = 0;
+ /* Command register always reads as 0, so we can just use a local. */
+ union f11_2d_commands commands = {};
+
+ fc = to_rmi_function_container(dev);
+
+ if (sscanf(buf, "%u", &rezero) != 1)
+ return -EINVAL;
+ if (rezero < 0 || rezero > 1)
+ return -EINVAL;
+
+ /* Per spec, 0 has no effect, so we skip it entirely. */
+ if (rezero) {
+ commands.rezero = 1;
+ retval = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+ &commands.reg, sizeof(commands.reg));
+ if (retval < 0) {
+ dev_err(dev, "%s: failed to issue rezero command, "
+ "error = %d.", __func__, retval);
+ return retval;
+ }
+ }
+
+ return count;
+}
+
+
+module_init(rmi_f11_module_init);
+module_exit(rmi_f11_module_exit);
+
+MODULE_AUTHOR("Stefan Nilsson <stefan.nilsson@unixphere.com>");
+MODULE_DESCRIPTION("RMI F11 module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_f19.c b/drivers/input/touchscreen/rmi4/rmi_f19.c
new file mode 100644
index 000000000000..9ff9ff99f91f
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_f19.c
@@ -0,0 +1,1419 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/input.h>
+#include <linux/slab.h>
+
+#define QUERY_BASE_INDEX 1
+#define MAX_LEN 256
+
+struct f19_0d_query {
+ union {
+ struct {
+ u8 configurable:1;
+ u8 has_sensitivity_adjust:1;
+ u8 has_hysteresis_threshold:1;
+ };
+ u8 f19_0d_query0;
+ };
+ u8 f19_0d_query1:5;
+};
+
+struct f19_0d_control_0 {
+ union {
+ struct {
+ u8 button_usage:2;
+ u8 filter_mode:2;
+ };
+ u8 f19_0d_control0;
+ };
+};
+
+struct f19_0d_control_1 {
+ u8 int_enabled_button;
+};
+
+struct f19_0d_control_2 {
+ u8 single_button;
+};
+
+struct f19_0d_control_3_4 {
+ u8 sensor_map_button:7;
+ /*u8 sensitivity_button;*/
+};
+
+struct f19_0d_control_5 {
+ u8 sensitivity_adj;
+};
+struct f19_0d_control_6 {
+ u8 hysteresis_threshold;
+};
+
+struct f19_0d_control {
+ struct f19_0d_control_0 *general_control;
+ struct f19_0d_control_1 *button_int_enable;
+ struct f19_0d_control_2 *single_button_participation;
+ struct f19_0d_control_3_4 *sensor_map;
+ struct f19_0d_control_5 *all_button_sensitivity_adj;
+ struct f19_0d_control_6 *all_button_hysteresis_threshold;
+};
+/* data specific to fn $19 that needs to be kept around */
+struct f19_data {
+ struct f19_0d_control *button_control;
+ struct f19_0d_query button_query;
+ u8 button_rezero;
+ bool *button_down;
+ unsigned char button_count;
+ unsigned char button_data_buffer_size;
+ unsigned char *button_data_buffer;
+ unsigned char *button_map;
+ char input_name[MAX_LEN];
+ char input_phys[MAX_LEN];
+ struct input_dev *input;
+};
+
+static ssize_t rmi_f19_button_count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_f19_button_map_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_f19_button_map_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_rezero_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_rezero_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_has_hysteresis_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t rmi_f19_has_sensitivity_adjust_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t rmi_f19_configurable_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+static ssize_t rmi_f19_filter_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_filter_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_button_usage_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_button_usage_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_interrupt_enable_button_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_interrupt_enable_button_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_single_button_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_single_button_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_sensor_map_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_sensor_map_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_sensitivity_adjust_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_sensitivity_adjust_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+static ssize_t rmi_f19_hysteresis_threshold_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+static ssize_t rmi_f19_hysteresis_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+
+static struct device_attribute attrs[] = {
+ __ATTR(button_count, RMI_RO_ATTR,
+ rmi_f19_button_count_show, rmi_store_error),
+ __ATTR(button_map, RMI_RW_ATTR,
+ rmi_f19_button_map_show, rmi_f19_button_map_store),
+ __ATTR(rezero, RMI_RW_ATTR,
+ rmi_f19_rezero_show, rmi_f19_rezero_store),
+ __ATTR(has_hysteresis_threshold, RMI_RO_ATTR,
+ rmi_f19_has_hysteresis_threshold_show, rmi_store_error),
+ __ATTR(has_sensitivity_adjust, RMI_RO_ATTR,
+ rmi_f19_has_sensitivity_adjust_show, rmi_store_error),
+ __ATTR(configurable, RMI_RO_ATTR,
+ rmi_f19_configurable_show, rmi_store_error),
+ __ATTR(filter_mode, RMI_RW_ATTR,
+ rmi_f19_filter_mode_show, rmi_f19_filter_mode_store),
+ __ATTR(button_usage, RMI_RW_ATTR,
+ rmi_f19_button_usage_show, rmi_f19_button_usage_store),
+ __ATTR(interrupt_enable_button, RMI_RW_ATTR,
+ rmi_f19_interrupt_enable_button_show,
+ rmi_f19_interrupt_enable_button_store),
+ __ATTR(single_button, RMI_RW_ATTR,
+ rmi_f19_single_button_show, rmi_f19_single_button_store),
+ __ATTR(sensor_map, RMI_RW_ATTR,
+ rmi_f19_sensor_map_show, rmi_f19_sensor_map_store),
+ __ATTR(sensitivity_adjust, RMI_RW_ATTR,
+ rmi_f19_sensitivity_adjust_show,
+ rmi_f19_sensitivity_adjust_store),
+ __ATTR(hysteresis_threshold, RMI_RW_ATTR,
+ rmi_f19_hysteresis_threshold_show,
+ rmi_f19_hysteresis_threshold_store)
+};
+
+
+int rmi_f19_read_control_parameters(struct rmi_device *rmi_dev,
+ struct f19_0d_control *button_control,
+ unsigned char button_count,
+ unsigned char int_button_enabled_count,
+ u8 ctrl_base_addr)
+{
+ int error = 0;
+ int i;
+
+ if (button_control->general_control) {
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
+ (u8 *)button_control->general_control,
+ sizeof(struct f19_0d_control_0));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read f19_0d_control_0, code:"
+ " %d.\n", error);
+ return error;
+ }
+ ctrl_base_addr = ctrl_base_addr +
+ sizeof(struct f19_0d_control_0);
+ }
+
+ if (button_control->button_int_enable) {
+ for (i = 0; i < int_button_enabled_count; i++) {
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
+ (u8 *)&button_control->button_int_enable[i],
+ sizeof(struct f19_0d_control_1));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read f19_0d_control_2,"
+ " code: %d.\n", error);
+ return error;
+ }
+ ctrl_base_addr = ctrl_base_addr +
+ sizeof(struct f19_0d_control_1);
+ }
+ }
+
+ if (button_control->single_button_participation) {
+ for (i = 0; i < int_button_enabled_count; i++) {
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
+ (u8 *)&button_control->
+ single_button_participation[i],
+ sizeof(struct f19_0d_control_2));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read f19_0d_control_2,"
+ " code: %d.\n", error);
+ return error;
+ }
+ ctrl_base_addr = ctrl_base_addr +
+ sizeof(struct f19_0d_control_2);
+ }
+ }
+
+ if (button_control->sensor_map) {
+ for (i = 0; i < button_count; i++) {
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
+ (u8 *)&button_control->sensor_map[i],
+ sizeof(struct f19_0d_control_3_4));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read f19_0d_control_3_4,"
+ " code: %d.\n", error);
+ return error;
+ }
+ ctrl_base_addr = ctrl_base_addr +
+ sizeof(struct f19_0d_control_3_4);
+ }
+ }
+
+ if (button_control->all_button_sensitivity_adj) {
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
+ (u8 *)button_control->
+ all_button_sensitivity_adj,
+ sizeof(struct f19_0d_control_5));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read f19_0d_control_5,"
+ " code: %d.\n", error);
+ return error;
+ }
+ ctrl_base_addr = ctrl_base_addr +
+ sizeof(struct f19_0d_control_5);
+ }
+
+ if (button_control->all_button_hysteresis_threshold) {
+ error = rmi_read_block(rmi_dev, ctrl_base_addr,
+ (u8 *)button_control->
+ all_button_hysteresis_threshold,
+ sizeof(struct f19_0d_control_6));
+ if (error < 0) {
+ dev_err(&rmi_dev->dev,
+ "Failed to read f19_0d_control_6,"
+ " code: %d.\n", error);
+ return error;
+ }
+ ctrl_base_addr = ctrl_base_addr +
+ sizeof(struct f19_0d_control_6);
+ }
+ return 0;
+}
+
+
+int rmi_f19_initialize_control_parameters(struct rmi_device *rmi_dev,
+ struct f19_0d_control *button_control,
+ unsigned char button_count,
+ unsigned char int_button_enabled_count,
+ int control_base_addr)
+{
+ int error = 0;
+
+ button_control->general_control =
+ kzalloc(sizeof(struct f19_0d_control_0), GFP_KERNEL);
+ if (!button_control->general_control) {
+ dev_err(&rmi_dev->dev, "Failed to allocate"
+ " f19_0d_control_0.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ button_control->button_int_enable =
+ kzalloc(int_button_enabled_count *
+ sizeof(struct f19_0d_control_2), GFP_KERNEL);
+ if (!button_control->button_int_enable) {
+ dev_err(&rmi_dev->dev, "Failed to allocate f19_0d_control_1.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ button_control->single_button_participation =
+ kzalloc(int_button_enabled_count *
+ sizeof(struct f19_0d_control_2), GFP_KERNEL);
+ if (!button_control->single_button_participation) {
+ dev_err(&rmi_dev->dev, "Failed to allocate"
+ " f19_0d_control_2.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ button_control->sensor_map =
+ kzalloc(button_count *
+ sizeof(struct f19_0d_control_3_4), GFP_KERNEL);
+ if (!button_control->sensor_map) {
+ dev_err(&rmi_dev->dev, "Failed to allocate"
+ " f19_0d_control_3_4.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ button_control->all_button_sensitivity_adj =
+ kzalloc(sizeof(struct f19_0d_control_5), GFP_KERNEL);
+ if (!button_control->all_button_sensitivity_adj) {
+ dev_err(&rmi_dev->dev, "Failed to allocate"
+ " f19_0d_control_5.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+
+ button_control->all_button_hysteresis_threshold =
+ kzalloc(sizeof(struct f19_0d_control_6), GFP_KERNEL);
+ if (!button_control->all_button_hysteresis_threshold) {
+ dev_err(&rmi_dev->dev, "Failed to allocate"
+ " f19_0d_control_6.\n");
+ error = -ENOMEM;
+ goto error_exit;
+ }
+ return rmi_f19_read_control_parameters(rmi_dev, button_control,
+ button_count, int_button_enabled_count, control_base_addr);
+
+error_exit:
+ kfree(button_control->general_control);
+ kfree(button_control->button_int_enable);
+ kfree(button_control->single_button_participation);
+ kfree(button_control->sensor_map);
+ kfree(button_control->all_button_sensitivity_adj);
+ kfree(button_control->all_button_hysteresis_threshold);
+ return error;
+}
+
+static int rmi_f19_init(struct rmi_function_container *fc)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct rmi_device_platform_data *pdata;
+ struct f19_data *f19;
+ struct input_dev *input_dev;
+ u8 query_base_addr;
+ int rc;
+ int i;
+ int attr_count = 0;
+
+ dev_info(&fc->dev, "Intializing F19 values.");
+
+ f19 = kzalloc(sizeof(struct f19_data), GFP_KERNEL);
+ if (!f19) {
+ dev_err(&fc->dev, "Failed to allocate function data.\n");
+ return -ENOMEM;
+ }
+ pdata = to_rmi_platform_data(rmi_dev);
+ query_base_addr = fc->fd.query_base_addr;
+
+ /* initial all default values for f19 data here */
+ rc = rmi_read(rmi_dev, fc->fd.command_base_addr,
+ (u8 *)&f19->button_rezero);
+ if (rc < 0) {
+ dev_err(&fc->dev, "Failed to read command register.\n");
+ goto err_free_data;
+ }
+
+ f19->button_rezero = f19->button_rezero & 1;
+
+ rc = rmi_read_block(rmi_dev, query_base_addr, (u8 *)&f19->button_query,
+ sizeof(struct f19_0d_query));
+ f19->button_count = f19->button_query.f19_0d_query1;
+
+ if (rc < 0) {
+ dev_err(&fc->dev, "Failed to read query register.\n");
+ goto err_free_data;
+ }
+
+
+ /* Figure out just how much data we'll need to read. */
+ f19->button_down = kcalloc(f19->button_count,
+ sizeof(bool), GFP_KERNEL);
+ if (!f19->button_down) {
+ dev_err(&fc->dev, "Failed to allocate button state buffer.\n");
+ rc = -ENOMEM;
+ goto err_free_data;
+ }
+
+ f19->button_data_buffer_size = (f19->button_count + 7) / 8;
+ f19->button_data_buffer =
+ kcalloc(f19->button_data_buffer_size,
+ sizeof(unsigned char), GFP_KERNEL);
+ if (!f19->button_data_buffer) {
+ dev_err(&fc->dev, "Failed to allocate button data buffer.\n");
+ rc = -ENOMEM;
+ goto err_free_data;
+ }
+
+ f19->button_map = kcalloc(f19->button_count,
+ sizeof(unsigned char), GFP_KERNEL);
+ if (!f19->button_map) {
+ dev_err(&fc->dev, "Failed to allocate button map.\n");
+ rc = -ENOMEM;
+ goto err_free_data;
+ }
+
+ if (pdata) {
+ if (pdata->button_map->nbuttons != f19->button_count) {
+ dev_warn(&fc->dev,
+ "Platformdata button map size (%d) != number "
+ "of buttons on device (%d) - ignored.\n",
+ pdata->button_map->nbuttons,
+ f19->button_count);
+ } else if (!pdata->button_map->map) {
+ dev_warn(&fc->dev,
+ "Platformdata button map is missing!\n");
+ } else {
+ for (i = 0; i < pdata->button_map->nbuttons; i++)
+ f19->button_map[i] = pdata->button_map->map[i];
+ }
+ }
+
+ f19->button_control = kzalloc(sizeof(struct f19_0d_control),
+ GFP_KERNEL);
+
+ rc = rmi_f19_initialize_control_parameters(fc->rmi_dev,
+ f19->button_control, f19->button_count,
+ f19->button_data_buffer_size, fc->fd.control_base_addr);
+ if (rc < 0) {
+ dev_err(&fc->dev,
+ "Failed to initialize F19 control params.\n");
+ goto err_free_data;
+ }
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&fc->dev, "Failed to allocate input device.\n");
+ rc = -ENOMEM;
+ goto err_free_data;
+ }
+
+ f19->input = input_dev;
+ snprintf(f19->input_name, MAX_LEN, "%sfn%02x", dev_name(&rmi_dev->dev),
+ fc->fd.function_number);
+ input_dev->name = f19->input_name;
+ snprintf(f19->input_phys, MAX_LEN, "%s/input0", input_dev->name);
+ input_dev->phys = f19->input_phys;
+ input_dev->dev.parent = &rmi_dev->dev;
+ input_set_drvdata(input_dev, f19);
+
+ /* Set up any input events. */
+ set_bit(EV_SYN, input_dev->evbit);
+ set_bit(EV_KEY, input_dev->evbit);
+ /* set bits for each button... */
+ for (i = 0; i < f19->button_count; i++)
+ set_bit(f19->button_map[i], input_dev->keybit);
+ rc = input_register_device(input_dev);
+ if (rc < 0) {
+ dev_err(&fc->dev, "Failed to register input device.\n");
+ goto err_free_input;
+ }
+
+ dev_dbg(&fc->dev, "Creating sysfs files.\n");
+ /* Set up sysfs device attributes. */
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ if (sysfs_create_file
+ (&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
+ dev_err(&fc->dev,
+ "Failed to create sysfs file for %s.",
+ attrs[attr_count].attr.name);
+ rc = -ENODEV;
+ goto err_free_data;
+ }
+ }
+ fc->data = f19;
+ return 0;
+
+err_free_input:
+ input_free_device(f19->input);
+
+err_free_data:
+ if (f19) {
+ kfree(f19->button_down);
+ kfree(f19->button_data_buffer);
+ kfree(f19->button_map);
+ }
+ kfree(f19);
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(&fc->dev.kobj,
+ &attrs[attr_count].attr);
+ return rc;
+}
+
+int rmi_f19_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+ struct rmi_device *rmi_dev = fc->rmi_dev;
+ struct f19_data *f19 = fc->data;
+ u8 data_base_addr = fc->fd.data_base_addr;
+ int error;
+ int button;
+
+ /* Read the button data. */
+
+ error = rmi_read_block(rmi_dev, data_base_addr, f19->button_data_buffer,
+ f19->button_data_buffer_size);
+ if (error < 0) {
+ dev_err(&fc->dev, "%s: Failed to read button data registers.\n",
+ __func__);
+ return error;
+ }
+
+ /* Generate events for buttons that change state. */
+ for (button = 0; button < f19->button_count;
+ button++) {
+ int button_reg;
+ int button_shift;
+ bool button_status;
+
+ /* determine which data byte the button status is in */
+ button_reg = button / 7;
+ /* bit shift to get button's status */
+ button_shift = button % 8;
+ button_status =
+ ((f19->button_data_buffer[button_reg] >> button_shift)
+ & 0x01) != 0;
+
+ /* if the button state changed from the last time report it
+ * and store the new state */
+ if (button_status != f19->button_down[button]) {
+ dev_dbg(&fc->dev, "%s: Button %d (code %d) -> %d.\n",
+ __func__, button, f19->button_map[button],
+ button_status);
+ /* Generate an event here. */
+ input_report_key(f19->input, f19->button_map[button],
+ button_status);
+ f19->button_down[button] = button_status;
+ }
+ }
+
+ input_sync(f19->input); /* sync after groups of events */
+ return 0;
+}
+
+static void rmi_f19_remove(struct rmi_function_container *fc)
+{
+ struct f19_data *data = fc->data;
+ if (data) {
+ kfree(data->button_down);
+ kfree(data->button_data_buffer);
+ kfree(data->button_map);
+ input_unregister_device(data->input);
+ if (data->button_control) {
+ kfree(data->button_control->general_control);
+ kfree(data->button_control->button_int_enable);
+ kfree(data->button_control->
+ single_button_participation);
+ kfree(data->button_control->sensor_map);
+ kfree(data->button_control->
+ all_button_sensitivity_adj);
+ kfree(data->button_control->
+ all_button_hysteresis_threshold);
+ }
+ kfree(data->button_control);
+ }
+ kfree(fc->data);
+}
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x19,
+ .init = rmi_f19_init,
+ .attention = rmi_f19_attention,
+ .remove = rmi_f19_remove
+};
+
+static int __init rmi_f19_module_init(void)
+{
+ int error;
+
+ error = rmi_register_function_driver(&function_handler);
+ if (error < 0) {
+ pr_err("%s: register failed!\n", __func__);
+ return error;
+ }
+
+ return 0;
+}
+
+static void rmi_f19_module_exit(void)
+{
+ rmi_unregister_function_driver(&function_handler);
+}
+
+static ssize_t rmi_f19_filter_mode_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_control->general_control->filter_mode);
+
+}
+
+static ssize_t rmi_f19_filter_mode_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ unsigned int new_value;
+ int result;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ if (sscanf(buf, "%u", &new_value) != 1) {
+ dev_err(dev,
+ "%s: Error - filter_mode_store has an "
+ "invalid len.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (new_value < 0 || new_value > 4) {
+ dev_err(dev, "%s: Error - filter_mode_store has an "
+ "invalid value %d.\n",
+ __func__, new_value);
+ return -EINVAL;
+ }
+ data->button_control->general_control->filter_mode = new_value;
+ result = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ (u8 *)data->button_control->general_control,
+ sizeof(struct f19_0d_control_0));
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write filter_mode_store to 0x%x\n",
+ __func__, fc->fd.control_base_addr);
+ return result;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_f19_button_usage_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_control->general_control->button_usage);
+
+}
+
+static ssize_t rmi_f19_button_usage_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ unsigned int new_value;
+ int result;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ if (sscanf(buf, "%u", &new_value) != 1) {
+ dev_err(dev,
+ "%s: Error - button_usage_store has an "
+ "invalid len.\n",
+ __func__);
+ return -EINVAL;
+ }
+
+ if (new_value < 0 || new_value > 4) {
+ dev_err(dev, "%s: Error - button_usage_store has an "
+ "invalid value %d.\n",
+ __func__, new_value);
+ return -EINVAL;
+ }
+ data->button_control->general_control->button_usage = new_value;
+ result = rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr,
+ (u8 *)data->button_control->general_control,
+ sizeof(struct f19_0d_control_0));
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write button_usage_store to 0x%x\n",
+ __func__, fc->fd.control_base_addr);
+ return result;
+ }
+
+ return count;
+
+}
+
+static ssize_t rmi_f19_interrupt_enable_button_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int i, len, total_len = 0;
+ char *current_buf = buf;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ /* loop through each button map value and copy its
+ * string representation into buf */
+ for (i = 0; i < data->button_count; i++) {
+ int button_reg;
+ int button_shift;
+ int interrupt_button;
+
+ button_reg = i / 7;
+ button_shift = i % 8;
+ interrupt_button =
+ ((data->button_control->
+ button_int_enable[button_reg].int_enabled_button >>
+ button_shift) & 0x01);
+
+ /* get next button mapping value and write it to buf */
+ len = snprintf(current_buf, PAGE_SIZE - total_len,
+ "%u ", interrupt_button);
+ /* bump up ptr to next location in buf if the
+ * snprintf was valid. Otherwise issue an error
+ * and return. */
+ if (len > 0) {
+ current_buf += len;
+ total_len += len;
+ } else {
+ dev_err(dev, "%s: Failed to build interrupt button"
+ " buffer, code = %d.\n", __func__, len);
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+ }
+ }
+ len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
+ if (len > 0)
+ total_len += len;
+ else
+ dev_warn(dev, "%s: Failed to append carriage return.\n",
+ __func__);
+ return total_len;
+
+}
+
+static ssize_t rmi_f19_interrupt_enable_button_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int i;
+ int button_count = 0;
+ int retval = count;
+ int button_reg = 0;
+ int ctrl_bass_addr;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ for (i = 0; i < data->button_count && *buf != 0;
+ i++) {
+ int button_shift;
+ int button;
+
+ button_reg = i / 7;
+ button_shift = i % 8;
+ /* get next button mapping value and store and bump up to
+ * point to next item in buf */
+ sscanf(buf, "%u", &button);
+
+ if (button != 0 && button != 1) {
+ dev_err(dev,
+ "%s: Error - interrupt enable button for"
+ " button %d is not a valid value 0x%x.\n",
+ __func__, i, button);
+ return -EINVAL;
+ }
+
+ if (button_shift == 0)
+ data->button_control->button_int_enable[button_reg].
+ int_enabled_button = 0;
+ data->button_control->button_int_enable[button_reg].
+ int_enabled_button |= (button << button_shift);
+ button_count++;
+ /* bump up buf to point to next item to read */
+ while (*buf != 0) {
+ buf++;
+ if (*(buf - 1) == ' ')
+ break;
+ }
+ }
+
+ /* Make sure the button count matches */
+ if (button_count != data->button_count) {
+ dev_err(dev,
+ "%s: Error - interrupt enable button count of %d"
+ " doesn't match device button count of %d.\n",
+ __func__, button_count, data->button_count);
+ return -EINVAL;
+ }
+
+ /* write back to the control register */
+ ctrl_bass_addr = fc->fd.control_base_addr +
+ sizeof(struct f19_0d_control_0);
+ retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+ (u8 *)data->button_control->button_int_enable,
+ sizeof(struct f19_0d_control_1)*(button_reg + 1));
+ if (retval < 0) {
+ dev_err(dev, "%s : Could not write interrupt_enable_store"
+ " to 0x%x\n", __func__, ctrl_bass_addr);
+ return retval;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_f19_single_button_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int i, len, total_len = 0;
+ char *current_buf = buf;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ /* loop through each button map value and copy its
+ * string representation into buf */
+ for (i = 0; i < data->button_count; i++) {
+ int button_reg;
+ int button_shift;
+ int single_button;
+
+ button_reg = i / 7;
+ button_shift = i % 8;
+ single_button = ((data->button_control->
+ single_button_participation[button_reg].single_button
+ >> button_shift) & 0x01);
+
+ /* get next button mapping value and write it to buf */
+ len = snprintf(current_buf, PAGE_SIZE - total_len,
+ "%u ", single_button);
+ /* bump up ptr to next location in buf if the
+ * snprintf was valid. Otherwise issue an error
+ * and return. */
+ if (len > 0) {
+ current_buf += len;
+ total_len += len;
+ } else {
+ dev_err(dev, "%s: Failed to build signle button buffer"
+ ", code = %d.\n", __func__, len);
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+ }
+ }
+ len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
+ if (len > 0)
+ total_len += len;
+ else
+ dev_warn(dev, "%s: Failed to append carriage return.\n",
+ __func__);
+
+ return total_len;
+
+}
+
+static ssize_t rmi_f19_single_button_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int i;
+ int button_count = 0;
+ int retval = count;
+ int ctrl_bass_addr;
+ int button_reg = 0;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ for (i = 0; i < data->button_count && *buf != 0;
+ i++) {
+ int button_shift;
+ int button;
+
+ button_reg = i / 7;
+ button_shift = i % 8;
+ /* get next button mapping value and store and bump up to
+ * point to next item in buf */
+ sscanf(buf, "%u", &button);
+
+ if (button != 0 && button != 1) {
+ dev_err(dev,
+ "%s: Error - single button for button %d"
+ " is not a valid value 0x%x.\n",
+ __func__, i, button);
+ return -EINVAL;
+ }
+ if (button_shift == 0)
+ data->button_control->
+ single_button_participation[button_reg].
+ single_button = 0;
+ data->button_control->single_button_participation[button_reg].
+ single_button |= (button << button_shift);
+ button_count++;
+ /* bump up buf to point to next item to read */
+ while (*buf != 0) {
+ buf++;
+ if (*(buf - 1) == ' ')
+ break;
+ }
+ }
+
+ /* Make sure the button count matches */
+ if (button_count != data->button_count) {
+ dev_err(dev,
+ "%s: Error - single button count of %d doesn't match"
+ " device button count of %d.\n", __func__, button_count,
+ data->button_count);
+ return -EINVAL;
+ }
+ /* write back to the control register */
+ ctrl_bass_addr = fc->fd.control_base_addr +
+ sizeof(struct f19_0d_control_0) +
+ sizeof(struct f19_0d_control_2)*(button_reg + 1);
+ retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+ (u8 *)data->button_control->single_button_participation,
+ sizeof(struct f19_0d_control_2)*(button_reg + 1));
+ if (retval < 0) {
+ dev_err(dev, "%s : Could not write interrupt_enable_store to"
+ " 0x%x\n", __func__, ctrl_bass_addr);
+ return -EINVAL;
+ }
+ return count;
+}
+
+static ssize_t rmi_f19_sensor_map_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int i, len, total_len = 0;
+ char *current_buf = buf;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ for (i = 0; i < data->button_count; i++) {
+ len = snprintf(current_buf, PAGE_SIZE - total_len,
+ "%u ", data->button_control->sensor_map[i].
+ sensor_map_button);
+ /* bump up ptr to next location in buf if the
+ * snprintf was valid. Otherwise issue an error
+ * and return. */
+ if (len > 0) {
+ current_buf += len;
+ total_len += len;
+ } else {
+ dev_err(dev, "%s: Failed to build sensor map buffer, "
+ "code = %d.\n", __func__, len);
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+ }
+ }
+ len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
+ if (len > 0)
+ total_len += len;
+ else
+ dev_warn(dev, "%s: Failed to append carriage return.\n",
+ __func__);
+ return total_len;
+
+
+}
+
+static ssize_t rmi_f19_sensor_map_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int sensor_map;
+ int i;
+ int retval = count;
+ int button_count = 0;
+ int ctrl_bass_addr;
+ int button_reg;
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ if (data->button_query.configurable == 0) {
+ dev_err(dev,
+ "%s: Error - sensor map is not configuralbe at"
+ " run-time", __func__);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < data->button_count && *buf != 0; i++) {
+ /* get next button mapping value and store and bump up to
+ * point to next item in buf */
+ sscanf(buf, "%u", &sensor_map);
+
+ /* Make sure the key is a valid key */
+ if (sensor_map < 0 || sensor_map > 127) {
+ dev_err(dev,
+ "%s: Error - sensor map for button %d is"
+ " not a valid value 0x%x.\n",
+ __func__, i, sensor_map);
+ return -EINVAL;
+ }
+
+ data->button_control->sensor_map[i].sensor_map_button =
+ sensor_map;
+ button_count++;
+
+ /* bump up buf to point to next item to read */
+ while (*buf != 0) {
+ buf++;
+ if (*(buf - 1) == ' ')
+ break;
+ }
+ }
+
+ if (button_count != data->button_count) {
+ dev_err(dev,
+ "%s: Error - button map count of %d doesn't match device "
+ "button count of %d.\n", __func__, button_count,
+ data->button_count);
+ return -EINVAL;
+ }
+
+ /* write back to the control register */
+ button_reg = (button_count / 7) + 1;
+ ctrl_bass_addr = fc->fd.control_base_addr +
+ sizeof(struct f19_0d_control_0) +
+ sizeof(struct f19_0d_control_1)*button_reg +
+ sizeof(struct f19_0d_control_2)*button_reg;
+ retval = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+ (u8 *)data->button_control->sensor_map,
+ sizeof(struct f19_0d_control_3_4)*button_count);
+ if (retval < 0) {
+ dev_err(dev, "%s : Could not sensor_map_store to 0x%x\n",
+ __func__, ctrl_bass_addr);
+ return -EINVAL;
+ }
+ return count;
+}
+
+static ssize_t rmi_f19_sensitivity_adjust_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->button_control->
+ all_button_sensitivity_adj->sensitivity_adj);
+
+}
+
+static ssize_t rmi_f19_sensitivity_adjust_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ unsigned int new_value;
+ int len;
+ int ctrl_bass_addr;
+ int button_reg;
+
+ fc = to_rmi_function_container(dev);
+
+ data = fc->data;
+
+ if (data->button_query.configurable == 0) {
+ dev_err(dev,
+ "%s: Error - sensitivity_adjust is not"
+ " configuralbe at run-time", __func__);
+ return -EINVAL;
+ }
+
+ len = sscanf(buf, "%u", &new_value);
+ if (new_value < 0 || new_value > 31)
+ return -EINVAL;
+
+ data->button_control->all_button_sensitivity_adj->sensitivity_adj =
+ new_value;
+ /* write back to the control register */
+ button_reg = (data->button_count / 7) + 1;
+ ctrl_bass_addr = fc->fd.control_base_addr +
+ sizeof(struct f19_0d_control_0) +
+ sizeof(struct f19_0d_control_1)*button_reg +
+ sizeof(struct f19_0d_control_2)*button_reg +
+ sizeof(struct f19_0d_control_3_4)*data->button_count;
+ len = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+ (u8 *)data->button_control->all_button_sensitivity_adj,
+ sizeof(struct f19_0d_control_5));
+ if (len < 0) {
+ dev_err(dev, "%s : Could not sensitivity_adjust_store to"
+ " 0x%x\n", __func__, ctrl_bass_addr);
+ return len;
+ }
+
+ return len;
+}
+
+static ssize_t rmi_f19_hysteresis_threshold_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", data->button_control->
+ all_button_hysteresis_threshold->hysteresis_threshold);
+
+}
+static ssize_t rmi_f19_hysteresis_threshold_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ unsigned int new_value;
+ int len;
+ int ctrl_bass_addr;
+ int button_reg;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ len = sscanf(buf, "%u", &new_value);
+ if (new_value < 0 || new_value > 15) {
+ dev_err(dev, "%s: Error - hysteresis_threshold_store has an "
+ "invalid value %d.\n",
+ __func__, new_value);
+ return -EINVAL;
+ }
+ data->button_control->all_button_hysteresis_threshold->
+ hysteresis_threshold = new_value;
+ /* write back to the control register */
+ button_reg = (data->button_count / 7) + 1;
+ ctrl_bass_addr = fc->fd.control_base_addr +
+ sizeof(struct f19_0d_control_0) +
+ sizeof(struct f19_0d_control_1)*button_reg +
+ sizeof(struct f19_0d_control_2)*button_reg +
+ sizeof(struct f19_0d_control_3_4)*data->button_count+
+ sizeof(struct f19_0d_control_5);
+ len = rmi_write_block(fc->rmi_dev, ctrl_bass_addr,
+ (u8 *)data->button_control->all_button_sensitivity_adj,
+ sizeof(struct f19_0d_control_6));
+ if (len < 0) {
+ dev_err(dev, "%s : Could not write all_button hysteresis "
+ "threshold to 0x%x\n", __func__, ctrl_bass_addr);
+ return -EINVAL;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_f19_has_hysteresis_threshold_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_query.has_hysteresis_threshold);
+}
+
+static ssize_t rmi_f19_has_sensitivity_adjust_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_query.has_sensitivity_adjust);
+}
+
+static ssize_t rmi_f19_configurable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_query.configurable);
+}
+
+static ssize_t rmi_f19_rezero_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_rezero);
+
+}
+
+static ssize_t rmi_f19_rezero_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ unsigned int new_value;
+ int len;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ len = sscanf(buf, "%u", &new_value);
+ if (new_value != 0 && new_value != 1) {
+ dev_err(dev,
+ "%s: Error - rezero is not a "
+ "valid value 0x%x.\n",
+ __func__, new_value);
+ return -EINVAL;
+ }
+ data->button_rezero = new_value & 1;
+ len = rmi_write(fc->rmi_dev, fc->fd.command_base_addr,
+ data->button_rezero);
+
+ if (len < 0) {
+ dev_err(dev, "%s : Could not write rezero to 0x%x\n",
+ __func__, fc->fd.command_base_addr);
+ return -EINVAL;
+ }
+ return count;
+}
+
+static ssize_t rmi_f19_button_count_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->button_count);
+}
+
+static ssize_t rmi_f19_button_map_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ int i, len, total_len = 0;
+ char *current_buf = buf;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+ /* loop through each button map value and copy its
+ * string representation into buf */
+ for (i = 0; i < data->button_count; i++) {
+ /* get next button mapping value and write it to buf */
+ len = snprintf(current_buf, PAGE_SIZE - total_len,
+ "%u ", data->button_map[i]);
+ /* bump up ptr to next location in buf if the
+ * snprintf was valid. Otherwise issue an error
+ * and return. */
+ if (len > 0) {
+ current_buf += len;
+ total_len += len;
+ } else {
+ dev_err(dev, "%s: Failed to build button map buffer, "
+ "code = %d.\n", __func__, len);
+ return snprintf(buf, PAGE_SIZE, "unknown\n");
+ }
+ }
+ len = snprintf(current_buf, PAGE_SIZE - total_len, "\n");
+ if (len > 0)
+ total_len += len;
+ else
+ dev_warn(dev, "%s: Failed to append carriage return.\n",
+ __func__);
+ return total_len;
+}
+
+static ssize_t rmi_f19_button_map_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct f19_data *data;
+ unsigned int button;
+ int i;
+ int retval = count;
+ int button_count = 0;
+ unsigned char temp_button_map[KEY_MAX];
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ /* Do validation on the button map data passed in. Store button
+ * mappings into a temp buffer and then verify button count and
+ * data prior to clearing out old button mappings and storing the
+ * new ones. */
+ for (i = 0; i < data->button_count && *buf != 0;
+ i++) {
+ /* get next button mapping value and store and bump up to
+ * point to next item in buf */
+ sscanf(buf, "%u", &button);
+
+ /* Make sure the key is a valid key */
+ if (button > KEY_MAX) {
+ dev_err(dev,
+ "%s: Error - button map for button %d is not a"
+ " valid value 0x%x.\n", __func__, i, button);
+ retval = -EINVAL;
+ goto err_ret;
+ }
+
+ temp_button_map[i] = button;
+ button_count++;
+
+ /* bump up buf to point to next item to read */
+ while (*buf != 0) {
+ buf++;
+ if (*(buf - 1) == ' ')
+ break;
+ }
+ }
+
+ /* Make sure the button count matches */
+ if (button_count != data->button_count) {
+ dev_err(dev,
+ "%s: Error - button map count of %d doesn't match device "
+ "button count of %d.\n", __func__, button_count,
+ data->button_count);
+ retval = -EINVAL;
+ goto err_ret;
+ }
+
+ /* Clear the key bits for the old button map. */
+ for (i = 0; i < button_count; i++)
+ clear_bit(data->button_map[i], data->input->keybit);
+
+ /* Switch to the new map. */
+ memcpy(data->button_map, temp_button_map,
+ data->button_count);
+
+ /* Loop through the key map and set the key bit for the new mapping. */
+ for (i = 0; i < button_count; i++)
+ set_bit(data->button_map[i], data->input->keybit);
+
+err_ret:
+ return retval;
+}
+
+module_init(rmi_f19_module_init);
+module_exit(rmi_f19_module_exit);
+
+MODULE_AUTHOR("Vivian Ly <vly@synaptics.com>");
+MODULE_DESCRIPTION("RMI F19 module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_f34.c b/drivers/input/touchscreen/rmi4/rmi_f34.c
new file mode 100644
index 000000000000..33e84d2cfb24
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_f34.c
@@ -0,0 +1,821 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include "rmi_driver.h"
+
+/* define fn $34 commands */
+#define WRITE_FW_BLOCK 0x2
+#define ERASE_ALL 0x3
+#define READ_CONFIG_BLOCK 0x5
+#define WRITE_CONFIG_BLOCK 0x6
+#define ERASE_CONFIG 0x7
+#define ENABLE_FLASH_PROG 0xf
+
+#define STATUS_IN_PROGRESS 0xff
+#define STATUS_IDLE 0x80
+
+#define PDT_START_SCAN_LOCATION 0x00e9
+#define PDT_END_SCAN_LOCATION 0x0005
+
+#define BLK_SZ_OFF 3
+#define IMG_BLK_CNT_OFF 5
+#define CFG_BLK_CNT_OFF 7
+
+#define BLK_NUM_OFF 2
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+#define KERNEL_VERSION_ABOVE_2_6_32 1
+#endif
+
+/* data specific to fn $34 that needs to be kept around */
+struct rmi_fn_34_data {
+ unsigned char status;
+ unsigned char cmd;
+ unsigned short bootloaderid;
+ unsigned short blocksize;
+ unsigned short imageblockcount;
+ unsigned short configblockcount;
+ unsigned short blocknum;
+ bool inflashprogmode;
+};
+
+static ssize_t rmi_fn_34_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_34_cmd_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_34_cmd_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_34_data_read(struct file *data_file, struct kobject *kobj,
+ struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_34_data_write(struct file *data_file,
+ struct kobject *kobj,
+ struct bin_attribute *attributes, char *buf,
+ loff_t pos, size_t count);
+#else
+static ssize_t rmi_fn_34_data_read(struct kobject *kobj,
+ struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_34_data_write(struct kobject *kobj,
+ struct bin_attribute *attributes, char *buf,
+ loff_t pos, size_t count);
+#endif
+
+static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_34_blocksize_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_34_imageblockcount_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_34_configblockcount_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_34_blocknum_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+static ssize_t rmi_fn_34_blocknum_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_34_rescanPDT_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static struct device_attribute attrs[] = {
+ __ATTR(status, RMI_RO_ATTR,
+ rmi_fn_34_status_show, rmi_store_error),
+ /* Also, sysfs will need to have a file set up to distinguish
+ * between commands - like Config write/read, Image write/verify. */
+ __ATTR(cmd, RMI_RW_ATTR,
+ rmi_fn_34_cmd_show, rmi_fn_34_cmd_store),
+ __ATTR(bootloaderid, RMI_RW_ATTR,
+ rmi_fn_34_bootloaderid_show, rmi_fn_34_bootloaderid_store),
+ __ATTR(blocksize, RMI_RO_ATTR,
+ rmi_fn_34_blocksize_show, rmi_store_error),
+ __ATTR(imageblockcount, RMI_RO_ATTR,
+ rmi_fn_34_imageblockcount_show, rmi_store_error),
+ __ATTR(configblockcount, RMI_RO_ATTR,
+ rmi_fn_34_configblockcount_show, rmi_store_error),
+ __ATTR(blocknum, RMI_RW_ATTR,
+ rmi_fn_34_blocknum_show, rmi_fn_34_blocknum_store),
+ __ATTR(rescanPDT, RMI_WO_ATTR,
+ rmi_show_error, rmi_fn_34_rescanPDT_store)
+};
+
+struct bin_attribute dev_attr_data = {
+ .attr = {
+ .name = "data",
+ .mode = 0666},
+ .size = 0,
+ .read = rmi_fn_34_data_read,
+ .write = rmi_fn_34_data_write,
+};
+
+static int rmi_f34_init(struct rmi_function_container *fc)
+{
+ int retval = 0;
+ int attr_count = 0;
+ struct rmi_fn_34_data *f34;
+ u16 query_base_addr;
+ u16 control_base_addr;
+ unsigned char buf[2];
+
+ dev_info(&fc->dev, "Intializing f34 values.");
+
+ /* init instance data, fill in values and create any sysfs files */
+ f34 = kzalloc(sizeof(struct rmi_fn_34_data), GFP_KERNEL);
+ if (!f34) {
+ dev_err(&fc->dev, "Failed to allocate rmi_fn_34_data.\n");
+ return -ENOMEM;
+ }
+
+ fc->data = f34;
+
+ /* get the Bootloader ID and Block Size. */
+ query_base_addr = fc->fd.query_base_addr;
+ control_base_addr = fc->fd.control_base_addr;
+
+ retval = rmi_read_block(fc->rmi_dev, query_base_addr, buf,
+ ARRAY_SIZE(buf));
+
+ if (retval < 0) {
+ dev_err(&fc->dev, "Could not read bootloaderid from 0x%04x.\n",
+ query_base_addr);
+ goto exit_free_data;
+ }
+ batohs(&f34->bootloaderid, buf);
+
+ retval = rmi_read_block(fc->rmi_dev, query_base_addr + BLK_SZ_OFF, buf,
+ ARRAY_SIZE(buf));
+
+ if (retval < 0) {
+ dev_err(&fc->dev, "Could not read block size from 0x%04x, "
+ "error=%d.\n", query_base_addr + BLK_SZ_OFF, retval);
+ goto exit_free_data;
+ }
+ batohs(&f34->blocksize, buf);
+
+ /* Get firmware image block count and store it in the instance data */
+ retval = rmi_read_block(fc->rmi_dev, query_base_addr + IMG_BLK_CNT_OFF,
+ buf, ARRAY_SIZE(buf));
+
+ if (retval < 0) {
+ dev_err(&fc->dev, "Couldn't read image block count from 0x%x, "
+ "error=%d.\n", query_base_addr + IMG_BLK_CNT_OFF,
+ retval);
+ goto exit_free_data;
+ }
+ batohs(&f34->imageblockcount, buf);
+
+ /* Get config block count and store it in the instance data */
+ retval = rmi_read_block(fc->rmi_dev, query_base_addr + 7, buf,
+ ARRAY_SIZE(buf));
+
+ if (retval < 0) {
+ dev_err(&fc->dev, "Couldn't read config block count from 0x%x, "
+ "error=%d.\n", query_base_addr + CFG_BLK_CNT_OFF,
+ retval);
+ goto exit_free_data;
+ }
+ batohs(&f34->configblockcount, buf);
+
+ /* We need a sysfs file for the image/config block to write or read.
+ * Set up sysfs bin file for binary data block. Since the image is
+ * already in our format there is no need to convert the data for
+ * endianess. */
+ retval = sysfs_create_bin_file(&fc->dev.kobj,
+ &dev_attr_data);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for F34 data "
+ "(error = %d).\n", retval);
+ retval = -ENODEV;
+ goto exit_free_data;
+ }
+
+ dev_dbg(&fc->dev, "Creating sysfs files.\n");
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ if (sysfs_create_file
+ (&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+ attrs[attr_count].attr.name);
+ retval = -ENODEV;
+ goto exit_free_attrs;
+ }
+ }
+
+ return retval;
+
+exit_free_attrs:
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(&fc->dev.kobj,
+ &attrs[attr_count].attr);
+exit_free_data:
+ kfree(f34);
+ return retval;
+}
+
+static int f34_read_status(struct rmi_function_container *fc)
+{
+ struct rmi_fn_34_data *instance_data = fc->data;
+ u16 data_base_addr = fc->fd.data_base_addr;
+ u8 status;
+ int retval;
+
+ /* Read the Fn $34 status from F34_Flash_Data3 to see the previous
+ * commands status. F34_Flash_Data3 will be the address after the
+ * 2 block number registers plus blocksize Data registers.
+ * inform user space - through a sysfs param. */
+ retval = rmi_read(fc->rmi_dev,
+ data_base_addr + instance_data->blocksize +
+ BLK_NUM_OFF, &status);
+
+ if (retval < 0) {
+ dev_err(&fc->dev, "Could not read status from 0x%x\n",
+ data_base_addr + instance_data->blocksize + BLK_NUM_OFF);
+ status = 0xff; /* failure */
+ }
+
+ /* set a sysfs value that the user mode can read - only
+ * upper 4 bits are the status. successful is $80, anything
+ * else is failure */
+ instance_data->status = status & 0xf0;
+
+ /* put mode into Flash Prog Mode when we successfully do
+ * an Enable Flash Prog cmd. */
+ if ((instance_data->status == STATUS_IDLE) &&
+ (instance_data->cmd == ENABLE_FLASH_PROG))
+ instance_data->inflashprogmode = true;
+
+ return retval;
+}
+
+int rmi_f34_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+ return f34_read_status(fc);
+}
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x34,
+ .init = rmi_f34_init,
+ .attention = rmi_f34_attention
+};
+
+static ssize_t rmi_fn_34_bootloaderid_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->bootloaderid);
+}
+
+static ssize_t rmi_fn_34_bootloaderid_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int error;
+ unsigned long val;
+ unsigned char data[2];
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ u16 data_base_addr;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ /* need to convert the string data to an actual value */
+ error = strict_strtoul(buf, 10, &val);
+
+ if (error)
+ return error;
+
+ instance_data->bootloaderid = val;
+
+ /* Write the Bootloader ID key data back to the first two Block
+ * Data registers (F34_Flash_Data2.0 and F34_Flash_Data2.1). */
+ hstoba(data, (unsigned short)val);
+ data_base_addr = fc->fd.data_base_addr;
+
+ error = rmi_write_block(fc->rmi_dev,
+ data_base_addr + BLK_NUM_OFF,
+ data,
+ ARRAY_SIZE(data));
+
+ if (error < 0) {
+ dev_err(dev, "%s : Could not write bootloader id to 0x%x\n",
+ __func__, data_base_addr + BLK_NUM_OFF);
+ return error;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_fn_34_blocksize_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->blocksize);
+}
+
+static ssize_t rmi_fn_34_imageblockcount_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->imageblockcount);
+}
+
+static ssize_t rmi_fn_34_configblockcount_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->configblockcount);
+}
+
+static ssize_t rmi_fn_34_status_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ int retval;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ retval = f34_read_status(fc);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->status);
+}
+
+static ssize_t rmi_fn_34_cmd_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->cmd);
+}
+
+static ssize_t rmi_fn_34_cmd_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ unsigned long val;
+ u16 data_base_addr;
+ int error;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ data_base_addr = fc->fd.data_base_addr;
+
+ /* need to convert the string data to an actual value */
+ error = strict_strtoul(buf, 10, &val);
+ if (error)
+ return error;
+
+ /* make sure we are in Flash Prog mode for all cmds except the
+ * Enable Flash Programming cmd - otherwise we are in error */
+ if ((val != ENABLE_FLASH_PROG) && !instance_data->inflashprogmode) {
+ dev_err(dev, "%s: CANNOT SEND CMD %d TO SENSOR - "
+ "NOT IN FLASH PROG MODE\n"
+ , __func__, data_base_addr);
+ return -EINVAL;
+ }
+
+ instance_data->cmd = val;
+
+ /* Validate command value and (if necessary) write it to the command
+ * register.
+ */
+ switch (instance_data->cmd) {
+ case ENABLE_FLASH_PROG:
+ case ERASE_ALL:
+ case ERASE_CONFIG:
+ case WRITE_FW_BLOCK:
+ case READ_CONFIG_BLOCK:
+ case WRITE_CONFIG_BLOCK:
+ /* Reset the status to indicate we are in progress on a cmd. */
+ /* The status will change when the ATTN interrupt happens
+ and the status of the cmd that was issued is read from
+ the F34_Flash_Data3 register - result should be 0x80 for
+ success - any other value indicates an error */
+
+ /* Issue the command to the device. */
+ error = rmi_write(fc->rmi_dev,
+ data_base_addr + instance_data->blocksize +
+ BLK_NUM_OFF, instance_data->cmd);
+
+ if (error < 0) {
+ dev_err(dev, "%s: Could not write command 0x%02x "
+ "to 0x%04x\n", __func__, instance_data->cmd,
+ data_base_addr + instance_data->blocksize +
+ BLK_NUM_OFF);
+ return error;
+ }
+
+ if (instance_data->cmd == ENABLE_FLASH_PROG)
+ instance_data->inflashprogmode = true;
+
+ /* set status to indicate we are in progress */
+ instance_data->status = STATUS_IN_PROGRESS;
+ break;
+ default:
+ dev_dbg(dev, "%s: RMI4 function $34 - "
+ "unknown command 0x%02lx.\n", __func__, val);
+ count = -EINVAL;
+ break;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_fn_34_blocknum_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->blocknum);
+}
+
+static ssize_t rmi_fn_34_blocknum_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int error;
+ unsigned long val;
+ unsigned char data[2];
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ u16 data_base_addr;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ data_base_addr = fc->fd.data_base_addr;
+
+ /* need to convert the string data to an actual value */
+ error = strict_strtoul(buf, 10, &val);
+
+ if (error)
+ return error;
+
+ instance_data->blocknum = val;
+
+ /* Write the Block Number data back to the first two Block
+ * Data registers (F34_Flash_Data_0 and F34_Flash_Data_1). */
+ hstoba(data, (unsigned short)val);
+
+ error = rmi_write_block(fc->rmi_dev,
+ data_base_addr,
+ data,
+ ARRAY_SIZE(data));
+
+ if (error < 0) {
+ dev_err(dev, "%s : Could not write block number %u to 0x%x\n",
+ __func__, instance_data->blocknum, data_base_addr);
+ return error;
+ }
+
+ return count;
+}
+
+static ssize_t rmi_fn_34_rescanPDT_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ struct rmi_device *rmi_dev;
+ struct rmi_driver_data *driver_data;
+ struct pdt_entry pdt_entry;
+ bool fn01found = false;
+ bool fn34found = false;
+ unsigned int rescan;
+ int irq_count = 0;
+ int retval = 0;
+ int i;
+
+ /* Rescan of the PDT is needed since issuing the Flash Enable cmd
+ * the device registers for Fn$01 and Fn$34 moving around because
+ * of the change from Bootloader mode to Flash Programming mode
+ * may change to a different PDT with only Fn$01 and Fn$34 that
+ * could have addresses for query, control, data, command registers
+ * that differ from the PDT scan done at device initialization. */
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ rmi_dev = fc->rmi_dev;
+ driver_data = rmi_get_driverdata(rmi_dev);
+
+ /* Make sure we are only in Flash Programming mode - DON'T
+ * ALLOW THIS IN UI MODE. */
+ if (instance_data->cmd != ENABLE_FLASH_PROG) {
+ dev_err(dev, "%s: NOT IN FLASH PROG MODE - CAN'T RESCAN PDT.\n"
+ , __func__);
+ return -EINVAL;
+ }
+
+ /* The only good value to write to this is 1, we allow 0, but with
+ * no effect (this is consistent with the way the command bit works. */
+ if (sscanf(buf, "%u", &rescan) != 1)
+ return -EINVAL;
+ if (rescan < 0 || rescan > 1)
+ return -EINVAL;
+
+ /* 0 has no effect, so we skip it entirely. */
+ if (rescan) {
+ /* rescan the PDT - filling in Fn01 and Fn34 addresses -
+ * this is only temporary - the device will need to be reset
+ * to return the PDT to the normal values. */
+
+ /* mini-parse the PDT - we only have to get Fn$01 and Fn$34 and
+ since we are Flash Programming mode we only have page 0. */
+ for (i = PDT_START_SCAN_LOCATION; i >= PDT_END_SCAN_LOCATION;
+ i -= sizeof(pdt_entry)) {
+ retval = rmi_read_block(rmi_dev, i, (u8 *)&pdt_entry,
+ sizeof(pdt_entry));
+ if (retval != sizeof(pdt_entry)) {
+ dev_err(dev, "%s: err frm rmi_read_block pdt "
+ "entry data from PDT, "
+ "error = %d.", __func__, retval);
+ return retval;
+ }
+
+ if ((pdt_entry.function_number == 0x00) ||
+ (pdt_entry.function_number == 0xff))
+ break;
+
+ dev_dbg(dev, "%s: Found F%.2X\n",
+ __func__, pdt_entry.function_number);
+
+ /* f01 found - just fill in the new addresses in
+ * the existing fc. */
+ if (pdt_entry.function_number == 0x01) {
+ struct rmi_function_container *f01_fc =
+ driver_data->f01_container;
+ fn01found = true;
+ f01_fc->fd.query_base_addr =
+ pdt_entry.query_base_addr;
+ f01_fc->fd.command_base_addr =
+ pdt_entry.command_base_addr;
+ f01_fc->fd.control_base_addr =
+ pdt_entry.control_base_addr;
+ f01_fc->fd.data_base_addr =
+ pdt_entry.data_base_addr;
+ f01_fc->fd.function_number =
+ pdt_entry.function_number;
+ f01_fc->fd.interrupt_source_count =
+ pdt_entry.interrupt_source_count;
+ f01_fc->num_of_irqs =
+ pdt_entry.interrupt_source_count;
+ f01_fc->irq_pos = irq_count;
+
+ irq_count += f01_fc->num_of_irqs;
+
+ if (fn34found)
+ break;
+ }
+
+ /* f34 found - just fill in the new addresses in
+ * the existing fc. */
+ if (pdt_entry.function_number == 0x34) {
+ fn34found = true;
+ fc->fd.query_base_addr =
+ pdt_entry.query_base_addr;
+ fc->fd.command_base_addr =
+ pdt_entry.command_base_addr;
+ fc->fd.control_base_addr =
+ pdt_entry.control_base_addr;
+ fc->fd.data_base_addr =
+ pdt_entry.data_base_addr;
+ fc->fd.function_number =
+ pdt_entry.function_number;
+ fc->fd.interrupt_source_count =
+ pdt_entry.interrupt_source_count;
+ fc->num_of_irqs =
+ pdt_entry.interrupt_source_count;
+ fc->irq_pos = irq_count;
+
+ irq_count += fc->num_of_irqs;
+
+ if (fn01found)
+ break;
+ }
+
+ }
+
+ if (!fn01found || !fn34found) {
+ dev_err(dev, "%s: failed to find fn$01 or fn$34 trying "
+ "to do rescan PDT.\n"
+ , __func__);
+ return -EINVAL;
+ }
+ }
+
+ return count;
+}
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_34_data_read(struct file *data_file,
+ struct kobject *kobj,
+ struct bin_attribute *attributes,
+ char *buf,
+ loff_t pos,
+ size_t count)
+#else
+static ssize_t rmi_fn_34_data_read(struct kobject *kobj,
+ struct bin_attribute *attributes,
+ char *buf,
+ loff_t pos,
+ size_t count)
+#endif
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ u16 data_base_addr;
+ int error;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ data_base_addr = fc->fd.data_base_addr;
+
+ if (count != instance_data->blocksize) {
+ dev_err(dev,
+ "%s : Incorrect F34 block size %d. "
+ "Expected size %d.\n",
+ __func__, count, instance_data->blocksize);
+ return -EINVAL;
+ }
+
+ /* Read the data from flash into buf. The app layer will be blocked
+ * at reading from the sysfs file. When we return the count (or
+ * error if we fail) the app will resume. */
+ error = rmi_read_block(fc->rmi_dev, data_base_addr + BLK_NUM_OFF,
+ (unsigned char *)buf, count);
+
+ if (error < 0) {
+ dev_err(dev, "%s : Could not read data from 0x%04x\n",
+ __func__, data_base_addr + BLK_NUM_OFF);
+ return error;
+ }
+
+ return count;
+}
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_34_data_write(struct file *data_file,
+ struct kobject *kobj,
+ struct bin_attribute *attributes,
+ char *buf,
+ loff_t pos,
+ size_t count)
+#else
+static ssize_t rmi_fn_34_data_write(struct kobject *kobj,
+ struct bin_attribute *attributes,
+ char *buf,
+ loff_t pos,
+ size_t count)
+#endif
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct rmi_function_container *fc;
+ struct rmi_fn_34_data *instance_data;
+ u16 data_base_addr;
+ int error;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ data_base_addr = fc->fd.data_base_addr;
+
+ /* Write the data from buf to flash. The app layer will be
+ * blocked at writing to the sysfs file. When we return the
+ * count (or error if we fail) the app will resume. */
+
+ if (count != instance_data->blocksize) {
+ dev_err(dev,
+ "%s : Incorrect F34 block size %d. "
+ "Expected size %d.\n",
+ __func__, count, instance_data->blocksize);
+ return -EINVAL;
+ }
+
+ /* Write the data block - only if the count is non-zero */
+ if (count) {
+ error = rmi_write_block(fc->rmi_dev,
+ data_base_addr + BLK_NUM_OFF,
+ (unsigned char *)buf,
+ count);
+
+ if (error < 0) {
+ dev_err(dev, "%s : Could not write block data "
+ "to 0x%x\n", __func__,
+ data_base_addr + BLK_NUM_OFF);
+ return error;
+ }
+ }
+
+ return count;
+}
+
+static int __init rmi_f34_module_init(void)
+{
+ int error;
+
+ error = rmi_register_function_driver(&function_handler);
+ if (error < 0) {
+ pr_err("%s : register failed !\n", __func__);
+ return error;
+ }
+
+ return 0;
+}
+
+static void rmi_f34_module_exit(void)
+{
+ rmi_unregister_function_driver(&function_handler);
+}
+
+module_init(rmi_f34_module_init);
+module_exit(rmi_f34_module_exit);
+
+MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
+MODULE_DESCRIPTION("RMI f34 module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_f54.c b/drivers/input/touchscreen/rmi4/rmi_f54.c
new file mode 100644
index 000000000000..11bb0b934bef
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_f54.c
@@ -0,0 +1,1347 @@
+
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/hrtimer.h>
+#include <linux/kernel.h>
+#include <linux/rmi.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include "rmi_driver.h"
+
+/* Set this to 1 for raw hex dump of returned data. */
+#define RAW_HEX 0
+/* Set this to 1 for human readable dump of returned data. */
+#define HUMAN_READABLE 0
+/* The watchdog timer can be useful when debugging certain firmware related
+ * issues.
+ */
+#define F54_WATCHDOG 1
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2, 6, 32)
+#define KERNEL_VERSION_ABOVE_2_6_32 1
+#endif
+
+/* define fn $54 commands */
+#define GET_REPORT 1
+#define FORCE_CAL 2
+
+/* status */
+#define BUSY 1
+#define IDLE 0
+
+/* Offsets for data */
+#define RMI_F54_REPORT_DATA_OFFSET 3
+#define RMI_F54_FIFO_OFFSET 1
+#define RMI_F54_NUM_TX_OFFSET 1
+#define RMI_F54_NUM_RX_OFFSET 0
+
+/* Fixed sizes of reports */
+#define RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE 4
+#define RMI_54_HIGH_RESISTANCE_SIZE 6
+
+/* definitions for F54 Query Registers in ultra-portable unionstruct form */
+struct f54_ad_query {
+ /* query 0 */
+ u8 number_of_receiver_electrodes;
+
+ /* query 1 */
+ u8 number_of_transmitter_electrodes;
+
+ union {
+ struct {
+ /* query2 */
+ u8 f54_ad_query2_b0__1:2;
+ u8 has_baseline:1;
+ u8 has_image8:1;
+ u8 f54_ad_query2_b4__5:2;
+ u8 has_image16:1;
+ u8 f54_ad_query2_b7:1;
+ };
+ u8 f54_ad_query2;
+ };
+
+ /* query 3.0 and 3.1 */
+ u16 clock_rate;
+
+ /* query 4 */
+ u8 touch_controller_family;
+
+ /* query 5 */
+ union {
+ struct {
+ u8 has_pixel_touch_threshold_adjustment:1;
+ u8 f54_ad_query5_b1__7:7;
+ };
+ u8 f54_ad_query5;
+ };
+
+ /* query 6 */
+ union {
+ struct {
+ u8 has_sensor_assignment:1;
+ u8 has_interference_metric:1;
+ u8 has_sense_frequency_control:1;
+ u8 has_firmware_noise_mitigation:1;
+ u8 f54_ad_query6_b4:1;
+ u8 has_two_byte_report_rate:1;
+ u8 has_one_byte_report_rate:1;
+ u8 has_relaxation_control:1;
+ };
+ u8 f54_ad_query6;
+ };
+
+ /* query 7 */
+ union {
+ struct {
+ u8 curve_compensation_mode:2;
+ u8 f54_ad_query7_b2__7:6;
+ };
+ u8 f54_ad_query7;
+ };
+
+ /* query 8 */
+ union {
+ struct {
+ u8 f54_ad_query2_b0:1;
+ u8 has_iir_filter:1;
+ u8 has_cmn_removal:1;
+ u8 has_cmn_maximum:1;
+ u8 has_pixel_threshold_hysteresis:1;
+ u8 has_edge_compensation:1;
+ u8 has_perf_frequency_noisecontrol:1;
+ u8 f54_ad_query8_b7:1;
+ };
+ u8 f54_ad_query8;
+ };
+
+ u8 f54_ad_query9;
+ u8 f54_ad_query10;
+ u8 f54_ad_query11;
+
+ /* query 12 */
+ union {
+ struct {
+ u8 number_of_sensing_frequencies:4;
+ u8 f54_ad_query12_b4__7:4;
+ };
+ u8 f54_ad_query12;
+ };
+};
+
+/* define report types */
+enum f54_report_types {
+ /* The numbering should follow automatically, here for clarity */
+ F54_8BIT_IMAGE = 1,
+ F54_16BIT_IMAGE = 2,
+ F54_RAW_16BIT_IMAGE = 3,
+ F54_HIGH_RESISTANCE = 4,
+ F54_TX_TO_TX_SHORT = 5,
+ F54_RX_TO_RX1 = 7,
+ F54_TRUE_BASELINE = 9,
+ F54_FULL_RAW_CAP_MIN_MAX = 13,
+ F54_RX_OPENS1 = 14,
+ F54_TX_OPEN = 15,
+ F54_TX_TO_GROUND = 16,
+ F54_RX_TO_RX2 = 17,
+ F54_RX_OPENS2 = 18,
+ F54_FULL_RAW_CAP = 19,
+ F54_FULL_RAW_CAP_RX_COUPLING_COMP = 20
+};
+
+/* data specific to fn $54 that needs to be kept around */
+struct rmi_fn_54_data {
+ struct f54_ad_query query;
+ u8 cmd;
+ enum f54_report_types report_type;
+ u16 fifoindex;
+ signed char status;
+ bool no_auto_cal;
+ /*
+ * May need to do something to make sure this reflects what is currently
+ * in data.
+ */
+ unsigned int report_size;
+ unsigned char *report_data;
+ unsigned int bufsize;
+ struct mutex data_mutex;
+ struct lock_class_key data_key;
+ struct mutex status_mutex;
+ struct lock_class_key status_key;
+#if F54_WATCHDOG
+ struct hrtimer watchdog;
+#endif
+ struct rmi_function_container *fc;
+ struct work_struct work;
+};
+
+/* sysfs functions */
+static ssize_t rmi_fn_54_report_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_report_type_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_get_report_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_force_cal_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+#else
+static ssize_t rmi_fn_54_data_read(struct kobject *kobj,
+#endif
+ struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count);
+
+static ssize_t rmi_fn_54_num_rx_electrodes_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_num_tx_electrodes_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_image16_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_image8_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_baseline_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_clock_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_touch_controller_family_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+
+static ssize_t rmi_fn_54_has_pixel_touch_threshold_adjustment_show(
+ struct device *dev, struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_sensor_assignment_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_interference_metric_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_sense_frequency_control_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_firmware_noise_mitigation_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_two_byte_report_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_one_byte_report_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_relaxation_control_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_curve_compensation_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_iir_filter_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_cmn_removal_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_cmn_maximum_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_pixel_threshold_hysteresis_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_edge_compensation_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_has_perf_frequency_noisecontrol_show(
+ struct device *dev, struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_number_of_sensing_frequencies_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static ssize_t rmi_fn_54_fifoindex_show(struct device *dev,
+ struct device_attribute *attr, char *buf);
+
+static ssize_t rmi_fn_54_fifoindex_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+static struct device_attribute attrs[] = {
+ __ATTR(report_type, RMI_RW_ATTR,
+ rmi_fn_54_report_type_show, rmi_fn_54_report_type_store),
+ __ATTR(get_report, RMI_WO_ATTR,
+ rmi_show_error, rmi_fn_54_get_report_store),
+ __ATTR(force_cal, RMI_WO_ATTR,
+ rmi_show_error, rmi_fn_54_force_cal_store),
+ __ATTR(status, RMI_RO_ATTR,
+ rmi_fn_54_status_show, rmi_store_error),
+ __ATTR(num_rx_electrodes, RMI_RO_ATTR,
+ rmi_fn_54_num_rx_electrodes_show, rmi_store_error),
+ __ATTR(num_tx_electrodes, RMI_RO_ATTR,
+ rmi_fn_54_num_tx_electrodes_show, rmi_store_error),
+ __ATTR(has_image16, RMI_RO_ATTR,
+ rmi_fn_54_has_image16_show, rmi_store_error),
+ __ATTR(has_image8, RMI_RO_ATTR,
+ rmi_fn_54_has_image8_show, rmi_store_error),
+ __ATTR(has_baseline, RMI_RO_ATTR,
+ rmi_fn_54_has_baseline_show, rmi_store_error),
+ __ATTR(clock_rate, RMI_RO_ATTR,
+ rmi_fn_54_clock_rate_show, rmi_store_error),
+ __ATTR(touch_controller_family, RMI_RO_ATTR,
+ rmi_fn_54_touch_controller_family_show, rmi_store_error),
+ __ATTR(has_pixel_touch_threshold_adjustment, RMI_RO_ATTR,
+ rmi_fn_54_has_pixel_touch_threshold_adjustment_show
+ , rmi_store_error),
+ __ATTR(has_sensor_assignment, RMI_RO_ATTR,
+ rmi_fn_54_has_sensor_assignment_show, rmi_store_error),
+ __ATTR(has_interference_metric, RMI_RO_ATTR,
+ rmi_fn_54_has_interference_metric_show, rmi_store_error),
+ __ATTR(has_sense_frequency_control, RMI_RO_ATTR,
+ rmi_fn_54_has_sense_frequency_control_show, rmi_store_error),
+ __ATTR(has_firmware_noise_mitigation, RMI_RO_ATTR,
+ rmi_fn_54_has_firmware_noise_mitigation_show, rmi_store_error),
+ __ATTR(has_two_byte_report_rate, RMI_RO_ATTR,
+ rmi_fn_54_has_two_byte_report_rate_show, rmi_store_error),
+ __ATTR(has_one_byte_report_rate, RMI_RO_ATTR,
+ rmi_fn_54_has_one_byte_report_rate_show, rmi_store_error),
+ __ATTR(has_relaxation_control, RMI_RO_ATTR,
+ rmi_fn_54_has_relaxation_control_show, rmi_store_error),
+ __ATTR(curve_compensation_mode, RMI_RO_ATTR,
+ rmi_fn_54_curve_compensation_mode_show, rmi_store_error),
+ __ATTR(has_iir_filter, RMI_RO_ATTR,
+ rmi_fn_54_has_iir_filter_show, rmi_store_error),
+ __ATTR(has_cmn_removal, RMI_RO_ATTR,
+ rmi_fn_54_has_cmn_removal_show, rmi_store_error),
+ __ATTR(has_cmn_maximum, RMI_RO_ATTR,
+ rmi_fn_54_has_cmn_maximum_show, rmi_store_error),
+ __ATTR(has_pixel_threshold_hysteresis, RMI_RO_ATTR,
+ rmi_fn_54_has_pixel_threshold_hysteresis_show, rmi_store_error),
+ __ATTR(has_edge_compensation, RMI_RO_ATTR,
+ rmi_fn_54_has_edge_compensation_show, rmi_store_error),
+ __ATTR(has_perf_frequency_noisecontrol, RMI_RO_ATTR,
+ rmi_fn_54_has_perf_frequency_noisecontrol_show, rmi_store_error),
+ __ATTR(number_of_sensing_frequencies, RMI_RO_ATTR,
+ rmi_fn_54_number_of_sensing_frequencies_show, rmi_store_error),
+ __ATTR(no_auto_cal, RMI_RW_ATTR,
+ rmi_fn_54_no_auto_cal_show, rmi_fn_54_no_auto_cal_store),
+ __ATTR(fifoindex, RMI_RW_ATTR,
+ rmi_fn_54_fifoindex_show, rmi_fn_54_fifoindex_store),
+};
+
+struct bin_attribute dev_rep_data = {
+ .attr = {
+ .name = "rep_data",
+ .mode = RMI_RO_ATTR},
+ .size = 0,
+ .read = rmi_fn_54_data_read,
+};
+
+#if F54_WATCHDOG
+static enum hrtimer_restart clear_status(struct hrtimer *timer);
+
+static void clear_status_worker(struct work_struct *work);
+#endif
+
+static int rmi_f54_init(struct rmi_function_container *fc)
+{
+ struct rmi_fn_54_data *instance_data;
+ int retval = 0;
+ int attr_count = 0;
+
+ dev_info(&fc->dev, "Intializing F54.");
+
+ instance_data = kzalloc(sizeof(struct rmi_fn_54_data), GFP_KERNEL);
+ if (!instance_data) {
+ dev_err(&fc->dev, "Failed to allocate rmi_fn_54_data.\n");
+ retval = -ENOMEM;
+ goto error_exit;
+ }
+ fc->data = instance_data;
+ instance_data->fc = fc;
+
+#if F54_WATCHDOG
+ /* Set up watchdog timer to catch unanswered get_report commands */
+ hrtimer_init(&instance_data->watchdog, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
+ instance_data->watchdog.function = clear_status;
+
+ /* work function to do unlocking */
+ INIT_WORK(&instance_data->work, clear_status_worker);
+#endif
+
+ /* Read F54 Query Data */
+ retval = rmi_read_block(fc->rmi_dev, fc->fd.query_base_addr,
+ (u8 *)&instance_data->query, sizeof(instance_data->query));
+ if (retval < 0) {
+ dev_err(&fc->dev, "Could not read query registers"
+ " from 0x%04x\n", fc->fd.query_base_addr);
+ goto error_exit;
+ }
+
+ __mutex_init(&instance_data->data_mutex, "data_mutex",
+ &instance_data->data_key);
+
+ __mutex_init(&instance_data->status_mutex, "status_mutex",
+ &instance_data->status_key);
+
+ dev_dbg(&fc->dev, "Creating sysfs files.");
+ /* Set up sysfs device attributes. */
+ for (attr_count = 0; attr_count < ARRAY_SIZE(attrs); attr_count++) {
+ if (sysfs_create_file
+ (&fc->dev.kobj, &attrs[attr_count].attr) < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for %s.",
+ attrs[attr_count].attr.name);
+ retval = -ENODEV;
+ goto error_exit;
+ }
+ }
+ /* Binary sysfs file to report the data back */
+ retval = sysfs_create_bin_file(&fc->dev.kobj, &dev_rep_data);
+ if (retval < 0) {
+ dev_err(&fc->dev, "Failed to create sysfs file for F54 data "
+ "(error = %d).\n", retval);
+ retval = -ENODEV;
+ goto error_exit;
+ }
+ instance_data->status = IDLE;
+ return retval;
+
+error_exit:
+ dev_err(&fc->dev, "An error occured in F54 init!\n");
+ for (attr_count--; attr_count >= 0; attr_count--)
+ sysfs_remove_file(&fc->dev.kobj,
+ &attrs[attr_count].attr);
+ kfree(instance_data);
+ return retval;
+}
+
+static void set_report_size(struct rmi_fn_54_data *data)
+{
+ u8 rx = data->query.number_of_receiver_electrodes;
+ u8 tx = data->query.number_of_transmitter_electrodes;
+ switch (data->report_type) {
+ case F54_8BIT_IMAGE:
+ data->report_size = rx * tx;
+ break;
+ case F54_16BIT_IMAGE:
+ case F54_RAW_16BIT_IMAGE:
+ case F54_TRUE_BASELINE:
+ case F54_FULL_RAW_CAP:
+ case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+ data->report_size = 2 * rx * tx;
+ break;
+ case F54_HIGH_RESISTANCE:
+ data->report_size = RMI_54_HIGH_RESISTANCE_SIZE;
+ break;
+ case F54_FULL_RAW_CAP_MIN_MAX:
+ data->report_size = RMI_54_FULL_RAW_CAP_MIN_MAX_SIZE;
+ break;
+ case F54_TX_TO_TX_SHORT:
+ case F54_TX_OPEN:
+ case F54_TX_TO_GROUND:
+ data->report_size = (tx + 7) / 8;
+ break;
+ case F54_RX_TO_RX1:
+ case F54_RX_OPENS1:
+ if (rx < tx)
+ data->report_size = 2 * rx * rx;
+ else
+ data->report_size = 2 * rx * tx;
+ break;
+ case F54_RX_TO_RX2:
+ case F54_RX_OPENS2:
+ if (rx <= tx)
+ data->report_size = 0;
+ else
+ data->report_size = 2 * rx * (rx - tx);
+ break;
+ default:
+ data->report_size = 0;
+ }
+}
+
+int rmi_f54_attention(struct rmi_function_container *fc, u8 *irq_bits)
+{
+ struct rmi_driver *driver = fc->rmi_dev->driver;
+ char fifo[2];
+ struct rmi_fn_54_data *data = fc->data;
+ int error = 0;
+
+ set_report_size(data);
+ if (data->report_size == 0) {
+ dev_err(&fc->dev, "Invalid report type set in %s. "
+ "This should never happen.\n", __func__);
+ error = -EINVAL;
+ goto error_exit;
+ }
+ /*
+ * We need to ensure the buffer is big enough. A Buffer size of 0 means
+ * that the buffer has not been allocated.
+ */
+ if (data->bufsize < data->report_size) {
+ mutex_lock(&data->data_mutex);
+ if (data->bufsize > 0)
+ kfree(data->report_data);
+ data->report_data = kzalloc(data->report_size, GFP_KERNEL);
+ if (!data->report_data) {
+ dev_err(&fc->dev, "Failed to allocate report_data.\n");
+ error = -ENOMEM;
+ data->bufsize = 0;
+ mutex_unlock(&data->data_mutex);
+ goto error_exit;
+ }
+ data->bufsize = data->report_size;
+ mutex_unlock(&data->data_mutex);
+ }
+ dev_vdbg(&fc->dev, "F54 Interrupt handler is running.\nSize: %d\n",
+ data->report_size);
+ /*
+ * Read report type, fifo high, and fifo low
+ * error = rmi_read_multiple(rmifninfo->sensor,
+ * rmifninfo->function_descriptor.data_base_addr ,
+ * repfifo,3);
+ */
+ /* Write 0 to fifohi and fifolo. */
+ fifo[0] = 0;
+ fifo[1] = 0;
+ error = rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr
+ + RMI_F54_FIFO_OFFSET, fifo, sizeof(fifo));
+ if (error < 0)
+ dev_err(&fc->dev, "Failed to write fifo to zero!\n");
+ else
+ error = rmi_read_block(fc->rmi_dev,
+ fc->fd.data_base_addr + RMI_F54_REPORT_DATA_OFFSET,
+ data->report_data, data->report_size);
+ if (error < 0)
+ dev_err(&fc->dev, "F54 data read failed. Code: %d.\n", error);
+ else if (error != data->report_size) {
+ error = -EINVAL;
+ goto error_exit;
+ }
+#if RAW_HEX
+ int l;
+ /* Debugging: Print out the file in hex. */
+ pr_info("Report data (raw hex):\n");
+ for (l = 0; l < data->report_size; l += 2) {
+ pr_info("%03d: 0x%02x%02x\n", l/2,
+ data->report_data[l+1], data->report_data[l]);
+ }
+#endif
+#if HUMAN_READABLE
+ /* Debugging: Print out file in human understandable image */
+ switch (data->report_type) {
+ case F54_16BIT_IMAGE:
+ case F54_RAW_16BIT_IMAGE:
+ case F54_TRUE_BASELINE:
+ case F54_FULL_RAW_CAP:
+ case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+ pr_info("Report data (Image):\n");
+ int i, j, k;
+ char c[2];
+ short s;
+ k = 0;
+ for (i = 0; i < data->query.number_of_transmitter_electrodes;
+ i++) {
+ for (j = 0; j <
+ data->query.number_of_receiver_electrodes; j++) {
+ c[0] = data->report_data[k];
+ c[1] = data->report_data[k+1];
+ memcpy(&s, &c, 2);
+ if (s < -64)
+ printk(".");
+ else if (s < 0)
+ printk("-");
+ else if (s > 64)
+ printk("*");
+ else if (s > 0)
+ printk("+");
+ else
+ printk("0");
+ k += 2;
+ }
+ pr_info("\n");
+ }
+ pr_info("EOF\n");
+ break;
+ default:
+ pr_info("Report type %d debug image not supported",
+ data->report_type);
+ }
+#endif
+ error = IDLE;
+error_exit:
+ mutex_lock(&data->status_mutex);
+ /* Turn back on other interupts, if it
+ * appears that we turned them off. */
+ if (driver->restore_irq_mask) {
+ dev_dbg(&fc->dev, "Restoring interupts!\n");
+ driver->restore_irq_mask(fc->rmi_dev);
+ } else {
+ dev_err(&fc->dev, "No way to restore interrupts!\n");
+ }
+ data->status = error;
+ mutex_unlock(&data->status_mutex);
+ return data->status;
+}
+
+
+#if F54_WATCHDOG
+static void clear_status_worker(struct work_struct *work)
+{
+ struct rmi_fn_54_data *data = container_of(work,
+ struct rmi_fn_54_data, work);
+ struct rmi_function_container *fc = data->fc;
+ struct rmi_driver *driver = fc->rmi_dev->driver;
+ char command;
+ int result;
+
+ mutex_lock(&data->status_mutex);
+ if (data->status == BUSY) {
+ pr_info("F54 Timout Occured: Determining status.\n");
+ result = rmi_read_block(fc->rmi_dev, fc->fd.command_base_addr,
+ &command, 1);
+ if (result < 0) {
+ dev_err(&fc->dev, "Could not read get_report register "
+ "from 0x%04x\n", fc->fd.command_base_addr);
+ data->status = -ETIMEDOUT;
+ } else {
+ if (command & GET_REPORT) {
+ dev_warn(&fc->dev, "Report type unsupported!");
+ data->status = -EINVAL;
+ } else {
+ data->status = -ETIMEDOUT;
+ }
+ }
+ if (driver->restore_irq_mask) {
+ dev_dbg(&fc->dev, "Restoring interupts!\n");
+ driver->restore_irq_mask(fc->rmi_dev);
+ } else {
+ dev_err(&fc->dev, "No way to restore interrupts!\n");
+ }
+ }
+ mutex_unlock(&data->status_mutex);
+}
+
+static enum hrtimer_restart clear_status(struct hrtimer *timer)
+{
+ struct rmi_fn_54_data *data = container_of(timer,
+ struct rmi_fn_54_data, watchdog);
+ schedule_work(&(data->work));
+ return HRTIMER_NORESTART;
+}
+#endif
+
+/* Check if report_type is valid */
+static bool is_report_type_valid(enum f54_report_types reptype)
+{
+ /* Basic checks on report_type to ensure we write a valid type
+ * to the sensor.
+ * TODO: Check Query3 to see if some specific reports are
+ * available. This is currently listed as a reserved register.
+ */
+ switch (reptype) {
+ case F54_8BIT_IMAGE:
+ case F54_16BIT_IMAGE:
+ case F54_RAW_16BIT_IMAGE:
+ case F54_HIGH_RESISTANCE:
+ case F54_TX_TO_TX_SHORT:
+ case F54_RX_TO_RX1:
+ case F54_TRUE_BASELINE:
+ case F54_FULL_RAW_CAP_MIN_MAX:
+ case F54_RX_OPENS1:
+ case F54_TX_OPEN:
+ case F54_TX_TO_GROUND:
+ case F54_RX_TO_RX2:
+ case F54_RX_OPENS2:
+ case F54_FULL_RAW_CAP:
+ case F54_FULL_RAW_CAP_RX_COUPLING_COMP:
+ return true;
+ break;
+ default:
+ return false;
+ }
+}
+
+/* SYSFS file show/store functions */
+static ssize_t rmi_fn_54_report_type_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->report_type);
+}
+
+static ssize_t rmi_fn_54_report_type_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count) {
+ int result;
+ unsigned long val;
+ unsigned char data;
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ /* need to convert the string data to an actual value */
+ result = strict_strtoul(buf, 10, &val);
+ if (result)
+ return result;
+ if (!is_report_type_valid(val)) {
+ dev_err(dev, "%s : Report type %d is invalid.\n",
+ __func__, (u8) val);
+ return -EINVAL;
+ }
+ mutex_lock(&instance_data->status_mutex);
+ if (instance_data->status != BUSY) {
+ instance_data->report_type = (enum f54_report_types)val;
+ data = (char)val;
+ /* Write the Report Type back to the first Block
+ * Data registers (F54_AD_Data0). */
+ result =
+ rmi_write_block(fc->rmi_dev, fc->fd.data_base_addr,
+ &data, 1);
+ mutex_unlock(&instance_data->status_mutex);
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write report type to"
+ " 0x%x\n", __func__, fc->fd.data_base_addr);
+ return result;
+ }
+ return count;
+ } else {
+ dev_err(dev, "%s : Report type cannot be changed in the middle"
+ " of command.\n", __func__);
+ mutex_unlock(&instance_data->status_mutex);
+ return -EINVAL;
+ }
+}
+
+static ssize_t rmi_fn_54_get_report_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count) {
+ unsigned long val;
+ int error, result;
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+ struct rmi_driver *driver;
+ u8 command;
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ driver = fc->rmi_dev->driver;
+
+ /* need to convert the string data to an actual value */
+ error = strict_strtoul(buf, 10, &val);
+ if (error)
+ return error;
+ /* Do nothing if not set to 1. This prevents accidental commands. */
+ if (val != 1)
+ return count;
+ command = (unsigned char)GET_REPORT;
+ /* Basic checks on report_type to ensure we write a valid type
+ * to the sensor.
+ * TODO: Check Query3 to see if some specific reports are
+ * available. This is currently listed as a reserved register.
+ */
+ if (!is_report_type_valid(instance_data->report_type)) {
+ dev_err(dev, "%s : Report type %d is invalid.\n",
+ __func__, instance_data->report_type);
+ return -EINVAL;
+ }
+ mutex_lock(&instance_data->status_mutex);
+ if (instance_data->status != IDLE) {
+ if (instance_data->status != BUSY) {
+ dev_err(dev, "F54 status is in an abnormal state: 0x%x",
+ instance_data->status);
+ }
+ mutex_unlock(&instance_data->status_mutex);
+ return count;
+ }
+ /* Store interrupts */
+ /* Do not exit if we fail to turn off interupts. We are likely
+ * to still get useful data. The report data can, however, be
+ * corrupted, and there may be unexpected behavior.
+ */
+ dev_dbg(dev, "Storing and overriding interupts\n");
+ if (driver->store_irq_mask)
+ driver->store_irq_mask(fc->rmi_dev,
+ fc->irq_mask);
+ else
+ dev_err(dev, "No way to store interupts!\n");
+ instance_data->status = BUSY;
+
+ /* small delay to avoid race condition in firmare. This value is a bit
+ * higher than absolutely necessary. Should be removed once issue is
+ * resolved in firmware. */
+
+ mdelay(2);
+
+ /* Write the command to the command register */
+ result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+ &command, 1);
+ mutex_unlock(&instance_data->status_mutex);
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write command to 0x%x\n",
+ __func__, fc->fd.command_base_addr);
+ return result;
+ }
+#if F54_WATCHDOG
+ /* start watchdog timer */
+ hrtimer_start(&instance_data->watchdog, ktime_set(1, 0),
+ HRTIMER_MODE_REL);
+#endif
+ return count;
+}
+
+static ssize_t rmi_fn_54_force_cal_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count) {
+ unsigned long val;
+ int error, result;
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+ struct rmi_driver *driver;
+ u8 command;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ driver = fc->rmi_dev->driver;
+
+ /* need to convert the string data to an actual value */
+ error = strict_strtoul(buf, 10, &val);
+ if (error)
+ return error;
+ /* Do nothing if not set to 1. This prevents accidental commands. */
+ if (val != 1)
+ return count;
+
+ command = (unsigned char)FORCE_CAL;
+
+ if (instance_data->status == BUSY)
+ return -EBUSY;
+ /* Write the command to the command register */
+ result = rmi_write_block(fc->rmi_dev, fc->fd.command_base_addr,
+ &command, 1);
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write command to 0x%x\n",
+ __func__, fc->fd.command_base_addr);
+ return result;
+ }
+ return count;
+}
+
+static ssize_t rmi_fn_54_status_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", instance_data->status);
+}
+
+static ssize_t rmi_fn_54_num_rx_electrodes_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.number_of_receiver_electrodes);
+}
+
+static ssize_t rmi_fn_54_num_tx_electrodes_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.number_of_transmitter_electrodes);
+}
+
+static ssize_t rmi_fn_54_has_image16_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_image16);
+}
+
+static ssize_t rmi_fn_54_has_image8_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_image8);
+}
+
+static ssize_t rmi_fn_54_has_baseline_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_baseline);
+}
+
+static ssize_t rmi_fn_54_clock_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.clock_rate);
+}
+
+
+static ssize_t rmi_fn_54_touch_controller_family_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.touch_controller_family);
+}
+
+
+static ssize_t rmi_fn_54_has_pixel_touch_threshold_adjustment_show(
+ struct device *dev, struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_pixel_touch_threshold_adjustment);
+}
+
+static ssize_t rmi_fn_54_has_sensor_assignment_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_sensor_assignment);
+}
+
+static ssize_t rmi_fn_54_has_interference_metric_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_interference_metric);
+}
+
+static ssize_t rmi_fn_54_has_sense_frequency_control_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_sense_frequency_control);
+}
+
+static ssize_t rmi_fn_54_has_firmware_noise_mitigation_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_firmware_noise_mitigation);
+}
+
+static ssize_t rmi_fn_54_has_two_byte_report_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_two_byte_report_rate);
+}
+
+static ssize_t rmi_fn_54_has_one_byte_report_rate_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_one_byte_report_rate);
+}
+
+static ssize_t rmi_fn_54_has_relaxation_control_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_relaxation_control);
+}
+
+static ssize_t rmi_fn_54_curve_compensation_mode_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.curve_compensation_mode);
+}
+
+static ssize_t rmi_fn_54_has_iir_filter_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_iir_filter);
+}
+
+static ssize_t rmi_fn_54_has_cmn_removal_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_cmn_removal);
+}
+
+static ssize_t rmi_fn_54_has_cmn_maximum_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_cmn_maximum);
+}
+
+static ssize_t rmi_fn_54_has_pixel_threshold_hysteresis_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_pixel_threshold_hysteresis);
+}
+
+static ssize_t rmi_fn_54_has_edge_compensation_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_edge_compensation);
+}
+
+static ssize_t rmi_fn_54_has_perf_frequency_noisecontrol_show(
+ struct device *dev, struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.has_perf_frequency_noisecontrol);
+}
+
+static ssize_t rmi_fn_54_number_of_sensing_frequencies_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *data;
+
+ fc = to_rmi_function_container(dev);
+ data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ data->query.number_of_sensing_frequencies);
+}
+
+
+static ssize_t rmi_fn_54_no_auto_cal_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ instance_data->no_auto_cal ? 1 : 0);
+}
+
+static ssize_t rmi_fn_54_no_auto_cal_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count) {
+ int result;
+ unsigned long val;
+ unsigned char data;
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ /* need to convert the string data to an actual value */
+ result = strict_strtoul(buf, 10, &val);
+
+ /* if an error occured, return it */
+ if (result)
+ return result;
+ /* Do nothing if not 0 or 1. This prevents accidental commands. */
+ if (val > 1)
+ return count;
+ /* Read current control values */
+ result =
+ rmi_read_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1);
+
+ /* if the current control registers are already set as we want them, do
+ * nothing to them */
+ if ((data & 1) == val)
+ return count;
+ /* Write the control back to the control register (F54_AD_Ctrl0)
+ * Ignores everything but bit 0 */
+ data = (data & ~1) | (val & 0x01); /* bit mask for lowest bit */
+ result =
+ rmi_write_block(fc->rmi_dev, fc->fd.control_base_addr, &data, 1);
+ if (result < 0) {
+ dev_err(dev, "%s : Could not write control to 0x%x\n",
+ __func__, fc->fd.control_base_addr);
+ return result;
+ }
+ /* update our internal representation iff the write succeeds */
+ instance_data->no_auto_cal = (val == 1);
+ return count;
+}
+
+static ssize_t rmi_fn_54_fifoindex_show(struct device *dev,
+ struct device_attribute *attr, char *buf) {
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+ struct rmi_driver *driver;
+ unsigned char temp_buf[2];
+ int retval;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ driver = fc->rmi_dev->driver;
+
+ /* Read fifoindex from device */
+ retval = rmi_read_block(fc->rmi_dev,
+ fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET,
+ temp_buf, ARRAY_SIZE(temp_buf));
+
+ if (retval < 0) {
+ dev_err(dev, "Could not read fifoindex from 0x%04x\n",
+ fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET);
+ return retval;
+ }
+ batohs(&instance_data->fifoindex, temp_buf);
+ return snprintf(buf, PAGE_SIZE, "%u\n", instance_data->fifoindex);
+}
+static ssize_t rmi_fn_54_fifoindex_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t count)
+{
+ int error;
+ unsigned long val;
+ unsigned char data[2];
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+
+ /* need to convert the string data to an actual value */
+ error = strict_strtoul(buf, 10, &val);
+
+ if (error)
+ return error;
+
+ instance_data->fifoindex = val;
+
+ /* Write the FifoIndex back to the first data registers. */
+ hstoba(data, (unsigned short)val);
+
+ error = rmi_write_block(fc->rmi_dev,
+ fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET,
+ data,
+ ARRAY_SIZE(data));
+
+ if (error < 0) {
+ dev_err(dev, "%s : Could not write fifoindex to 0x%x\n",
+ __func__, fc->fd.data_base_addr + RMI_F54_FIFO_OFFSET);
+ return error;
+ }
+ return count;
+}
+
+/* Provide access to last report */
+#ifdef KERNEL_VERSION_ABOVE_2_6_32
+static ssize_t rmi_fn_54_data_read(struct file *data_file, struct kobject *kobj,
+#else
+static ssize_t rmi_fn_54_data_read(struct kobject *kobj,
+#endif
+ struct bin_attribute *attributes,
+ char *buf, loff_t pos, size_t count)
+{
+ struct device *dev;
+ struct rmi_function_container *fc;
+ struct rmi_fn_54_data *instance_data;
+
+ dev = container_of(kobj, struct device, kobj);
+ fc = to_rmi_function_container(dev);
+ instance_data = fc->data;
+ mutex_lock(&instance_data->data_mutex);
+ if (count < instance_data->report_size) {
+ dev_err(dev,
+ "%s: F54 report size too large for buffer: %d."
+ " Need at least: %d for Report type: %d.\n",
+ __func__, count, instance_data->report_size,
+ instance_data->report_type);
+ mutex_unlock(&instance_data->data_mutex);
+ return -EINVAL;
+ }
+ if (instance_data->report_data) {
+ /* Copy data from instance_data to buffer */
+ memcpy(buf, instance_data->report_data,
+ instance_data->report_size);
+ mutex_unlock(&instance_data->data_mutex);
+ dev_dbg(dev, "%s: Presumably successful.", __func__);
+ return instance_data->report_size;
+ } else {
+ dev_err(dev, "%s: F54 report_data does not exist!\n", __func__);
+ mutex_unlock(&instance_data->data_mutex);
+ return -EINVAL;
+ }
+}
+
+static struct rmi_function_handler function_handler = {
+ .func = 0x54,
+ .init = rmi_f54_init,
+ .attention = rmi_f54_attention
+};
+
+static int __init rmi_f54_module_init(void)
+{
+ int error;
+
+ error = rmi_register_function_driver(&function_handler);
+ if (error < 0) {
+ pr_err("%s: register failed!\n", __func__);
+ return error;
+ }
+ return 0;
+}
+
+static void rmi_f54_module_exit(void)
+{
+ rmi_unregister_function_driver(&function_handler);
+}
+
+module_init(rmi_f54_module_init);
+module_exit(rmi_f54_module_exit);
+
+MODULE_AUTHOR("Daniel Rosenberg <daniel.rosenberg@synaptics.com>");
+MODULE_DESCRIPTION("RMI F54 module");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/rmi4/rmi_i2c.c b/drivers/input/touchscreen/rmi4/rmi_i2c.c
new file mode 100644
index 000000000000..15624f9cf54b
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_i2c.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/rmi.h>
+
+#define COMMS_DEBUG 0
+
+#define IRQ_DEBUG 0
+
+#define RMI_PAGE_SELECT_REGISTER 0xff
+#define RMI_I2C_PAGE(addr) (((addr) >> 8) & 0xff)
+
+static char *phys_proto_name = "i2c";
+
+struct rmi_i2c_data {
+ struct mutex page_mutex;
+ int page;
+ int enabled;
+ int irq;
+ int irq_flags;
+ struct rmi_phys_device *phys;
+};
+
+static irqreturn_t rmi_i2c_irq_thread(int irq, void *p)
+{
+ struct rmi_phys_device *phys = p;
+ struct rmi_device *rmi_dev = phys->rmi_dev;
+ struct rmi_driver *driver = rmi_dev->driver;
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+
+#if IRQ_DEBUG
+ dev_dbg(phys->dev, "ATTN gpio, value: %d.\n",
+ gpio_get_value(irq_to_gpio(irq)));
+#endif
+ if (gpio_get_value(irq_to_gpio(irq)) == pdata->irq_polarity) {
+ phys->info.attn_count++;
+ if (driver && driver->irq_handler && rmi_dev)
+ driver->irq_handler(rmi_dev, irq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * rmi_set_page - Set RMI page
+ * @phys: The pointer to the rmi_phys_device struct
+ * @page: The new page address.
+ *
+ * RMI devices have 16-bit addressing, but some of the physical
+ * implementations (like SMBus) only have 8-bit addressing. So RMI implements
+ * a page address at 0xff of every page so we can reliable page addresses
+ * every 256 registers.
+ *
+ * The page_mutex lock must be held when this function is entered.
+ *
+ * Returns zero on success, non-zero on failure.
+ */
+static int rmi_set_page(struct rmi_phys_device *phys, unsigned int page)
+{
+ struct i2c_client *client = to_i2c_client(phys->dev);
+ struct rmi_i2c_data *data = phys->data;
+ char txbuf[2] = {RMI_PAGE_SELECT_REGISTER, page};
+ int retval;
+
+#if COMMS_DEBUG
+ dev_dbg(&client->dev, "RMI4 I2C writes 3 bytes: %02x %02x\n",
+ txbuf[0], txbuf[1]);
+#endif
+ phys->info.tx_count++;
+ phys->info.tx_bytes += sizeof(txbuf);
+ retval = i2c_master_send(client, txbuf, sizeof(txbuf));
+ if (retval != sizeof(txbuf)) {
+ phys->info.tx_errs++;
+ dev_err(&client->dev,
+ "%s: set page failed: %d.", __func__, retval);
+ return (retval < 0) ? retval : -EIO;
+ }
+ data->page = page;
+ return 0;
+}
+
+static int rmi_i2c_write_block(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+ int len)
+{
+ struct i2c_client *client = to_i2c_client(phys->dev);
+ struct rmi_i2c_data *data = phys->data;
+ u8 txbuf[len + 1];
+ int retval;
+#if COMMS_DEBUG
+ int i;
+#endif
+
+ txbuf[0] = addr & 0xff;
+ memcpy(txbuf + 1, buf, len);
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_I2C_PAGE(addr) != data->page) {
+ retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
+ if (retval < 0)
+ goto exit;
+ }
+
+#if COMMS_DEBUG
+ dev_dbg(&client->dev, "RMI4 I2C writes %d bytes: ", sizeof(txbuf));
+ for (i = 0; i < sizeof(txbuf); i++)
+ dev_dbg(&client->dev, "%02x ", txbuf[i]);
+ dev_dbg(&client->dev, "\n");
+#endif
+
+ phys->info.tx_count++;
+ phys->info.tx_bytes += sizeof(txbuf);
+ retval = i2c_master_send(client, txbuf, sizeof(txbuf));
+ if (retval < 0)
+ phys->info.tx_errs++;
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return retval;
+}
+
+static int rmi_i2c_write(struct rmi_phys_device *phys, u16 addr, u8 data)
+{
+ int retval = rmi_i2c_write_block(phys, addr, &data, 1);
+ return (retval < 0) ? retval : 0;
+}
+
+static int rmi_i2c_read_block(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+ int len)
+{
+ struct i2c_client *client = to_i2c_client(phys->dev);
+ struct rmi_i2c_data *data = phys->data;
+ u8 txbuf[1] = {addr & 0xff};
+ int retval;
+#if COMMS_DEBUG
+ int i;
+#endif
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_I2C_PAGE(addr) != data->page) {
+ retval = rmi_set_page(phys, RMI_I2C_PAGE(addr));
+ if (retval < 0)
+ goto exit;
+ }
+
+#if COMMS_DEBUG
+ dev_dbg(&client->dev, "RMI4 I2C writes 1 bytes: %02x\n", txbuf[0]);
+#endif
+ phys->info.tx_count++;
+ phys->info.tx_bytes += sizeof(txbuf);
+ retval = i2c_master_send(client, txbuf, sizeof(txbuf));
+ if (retval != sizeof(txbuf)) {
+ phys->info.tx_errs++;
+ retval = (retval < 0) ? retval : -EIO;
+ goto exit;
+ }
+
+ retval = i2c_master_recv(client, buf, len);
+
+ phys->info.rx_count++;
+ phys->info.rx_bytes += len;
+ if (retval < 0)
+ phys->info.rx_errs++;
+#if COMMS_DEBUG
+ else {
+ dev_dbg(&client->dev, "RMI4 I2C received %d bytes: ", len);
+ for (i = 0; i < len; i++)
+ dev_dbg(&client->dev, "%02x ", buf[i]);
+ dev_dbg(&client->dev, "\n");
+ }
+#endif
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return retval;
+}
+
+static int rmi_i2c_read(struct rmi_phys_device *phys, u16 addr, u8 *buf)
+{
+ int retval = rmi_i2c_read_block(phys, addr, buf, 1);
+ return (retval < 0) ? retval : 0;
+}
+
+
+static int acquire_attn_irq(struct rmi_i2c_data *data)
+{
+ return request_threaded_irq(data->irq, NULL, rmi_i2c_irq_thread,
+ data->irq_flags, dev_name(data->phys->dev), data->phys);
+}
+
+static int enable_device(struct rmi_phys_device *phys)
+{
+ int retval = 0;
+
+ struct rmi_i2c_data *data = phys->data;
+
+ if (data->enabled)
+ return 0;
+
+ retval = acquire_attn_irq(data);
+ if (retval)
+ goto error_exit;
+
+ data->enabled = true;
+ dev_dbg(phys->dev, "Physical device enabled.\n");
+ return 0;
+
+error_exit:
+ dev_err(phys->dev, "Failed to enable physical device. Code=%d.\n",
+ retval);
+ return retval;
+}
+
+
+static void disable_device(struct rmi_phys_device *phys)
+{
+ struct rmi_i2c_data *data = phys->data;
+
+ if (!data->enabled)
+ return;
+
+ disable_irq(data->irq);
+ free_irq(data->irq, data->phys);
+
+ dev_dbg(phys->dev, "Physical device disabled.\n");
+ data->enabled = false;
+}
+
+
+static int __devinit rmi_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct rmi_phys_device *rmi_phys;
+ struct rmi_i2c_data *data;
+ struct rmi_device_platform_data *pdata = client->dev.platform_data;
+ int error;
+
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "i2c_check_functionality error\n");
+ return -EIO;
+ }
+
+ rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL);
+ if (!rmi_phys)
+ return -ENOMEM;
+
+ data = kzalloc(sizeof(struct rmi_i2c_data), GFP_KERNEL);
+ if (!data) {
+ error = -ENOMEM;
+ goto err_phys;
+ }
+
+ data->enabled = true; /* We plan to come up enabled. */
+ data->irq = gpio_to_irq(pdata->irq);
+ data->irq_flags = (pdata->irq_polarity == RMI_IRQ_ACTIVE_HIGH) ?
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+ data->phys = rmi_phys;
+
+ rmi_phys->data = data;
+ rmi_phys->dev = &client->dev;
+
+ rmi_phys->write = rmi_i2c_write;
+ rmi_phys->write_block = rmi_i2c_write_block;
+ rmi_phys->read = rmi_i2c_read;
+ rmi_phys->read_block = rmi_i2c_read_block;
+ rmi_phys->enable_device = enable_device;
+ rmi_phys->disable_device = disable_device;
+
+ rmi_phys->info.proto = phys_proto_name;
+
+ mutex_init(&data->page_mutex);
+
+ /* Setting the page to zero will (a) make sure the PSR is in a
+ * known state, and (b) make sure we can talk to the device.
+ */
+ error = rmi_set_page(rmi_phys, 0);
+ if (error) {
+ dev_err(&client->dev, "Failed to set page select to 0.\n");
+ goto err_data;
+ }
+
+ if (pdata->gpio_config) {
+ error = pdata->gpio_config(&client->dev, true);
+ if (error < 0) {
+ dev_err(&client->dev, "failed to setup irq %d\n",
+ pdata->irq);
+ goto err_data;
+ }
+ }
+
+ error = rmi_register_phys_device(rmi_phys);
+ if (error) {
+ dev_err(&client->dev,
+ "failed to register physical driver at 0x%.2X.\n",
+ client->addr);
+ goto err_data;
+ }
+ i2c_set_clientdata(client, rmi_phys);
+
+ if (pdata->irq > 0) {
+ error = acquire_attn_irq(data);
+ if (error < 0) {
+ dev_err(&client->dev,
+ "request_threaded_irq failed %d\n",
+ pdata->irq);
+ goto err_unregister;
+ }
+ }
+
+#if defined(CONFIG_RMI4_DEV)
+ error = gpio_export(pdata->irq, false);
+ if (error) {
+ dev_warn(&client->dev, "%s: WARNING: Failed to "
+ "export ATTN gpio!\n", __func__);
+ error = 0;
+ } else {
+ error = gpio_export_link(&(rmi_phys->rmi_dev->dev), "attn",
+ pdata->irq);
+ if (error) {
+ dev_warn(&(rmi_phys->rmi_dev->dev), "%s: WARNING: "
+ "Failed to symlink ATTN gpio!\n", __func__);
+ error = 0;
+ } else {
+ dev_info(&(rmi_phys->rmi_dev->dev),
+ "%s: Exported GPIO %d.", __func__, pdata->irq);
+ }
+ }
+#endif /* CONFIG_RMI4_DEV */
+
+ dev_info(&client->dev, "registered rmi i2c driver at 0x%.2X.\n",
+ client->addr);
+ return 0;
+
+err_unregister:
+ rmi_unregister_phys_device(rmi_phys);
+err_data:
+ kfree(data);
+err_phys:
+ kfree(rmi_phys);
+ return error;
+}
+
+static int __devexit rmi_i2c_remove(struct i2c_client *client)
+{
+ struct rmi_phys_device *phys = i2c_get_clientdata(client);
+ struct rmi_device_platform_data *pd = client->dev.platform_data;
+
+ rmi_unregister_phys_device(phys);
+ kfree(phys->data);
+ kfree(phys);
+
+ if (pd->gpio_config)
+ pd->gpio_config(&client->dev, false);
+
+ return 0;
+}
+
+static const struct i2c_device_id rmi_id[] = {
+ { "rmi", 0 },
+ { "rmi-i2c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rmi_id);
+
+static struct i2c_driver rmi_i2c_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rmi-i2c"
+ },
+ .id_table = rmi_id,
+ .probe = rmi_i2c_probe,
+ .remove = __devexit_p(rmi_i2c_remove),
+};
+
+static int __init rmi_i2c_init(void)
+{
+ return i2c_add_driver(&rmi_i2c_driver);
+}
+
+static void __exit rmi_i2c_exit(void)
+{
+ i2c_del_driver(&rmi_i2c_driver);
+}
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_AUTHOR("Eric Andersson <eric.andersson@unixphere.com>");
+MODULE_DESCRIPTION("RMI i2c driver");
+MODULE_LICENSE("GPL");
+
+module_init(rmi_i2c_init);
+module_exit(rmi_i2c_exit);
diff --git a/drivers/input/touchscreen/rmi4/rmi_spi.c b/drivers/input/touchscreen/rmi4/rmi_spi.c
new file mode 100644
index 000000000000..41f72657f829
--- /dev/null
+++ b/drivers/input/touchscreen/rmi4/rmi_spi.c
@@ -0,0 +1,869 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/gpio.h>
+#include <linux/rmi.h>
+
+#define COMMS_DEBUG 0
+
+#define RMI_PROTOCOL_VERSION_ADDRESS 0xa0fd
+#define SPI_V2_UNIFIED_READ 0xc0
+#define SPI_V2_WRITE 0x40
+#define SPI_V2_PREPARE_SPLIT_READ 0xc8
+#define SPI_V2_EXECUTE_SPLIT_READ 0xca
+
+#define RMI_SPI_BLOCK_DELAY_US 65
+#define RMI_SPI_BYTE_DELAY_US 65
+#define RMI_SPI_WRITE_DELAY_US 0
+
+#define RMI_V1_READ_FLAG 0x80
+
+#define RMI_PAGE_SELECT_REGISTER 0x00FF
+#define RMI_SPI_PAGE(addr) (((addr) >> 8) & 0x80)
+
+static char *spi_v1_proto_name = "spi";
+static char *spi_v2_proto_name = "spiv2";
+
+struct rmi_spi_data {
+ struct mutex page_mutex;
+ int page;
+ int (*set_page) (struct rmi_phys_device *phys, u8 page);
+ bool split_read_pending;
+ int enabled;
+ int irq;
+ int irq_flags;
+ struct rmi_phys_device *phys;
+ struct completion irq_comp;
+};
+
+static irqreturn_t rmi_spi_hard_irq(int irq, void *p)
+{
+ struct rmi_phys_device *phys = p;
+ struct rmi_spi_data *data = phys->data;
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+
+ if (data->split_read_pending &&
+ gpio_get_value(irq_to_gpio(irq)) == pdata->irq_polarity) {
+ phys->info.attn_count++;
+ complete(&data->irq_comp);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t rmi_spi_irq_thread(int irq, void *p)
+{
+ struct rmi_phys_device *phys = p;
+ struct rmi_device *rmi_dev = phys->rmi_dev;
+ struct rmi_driver *driver = rmi_dev->driver;
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+
+ if (gpio_get_value(irq_to_gpio(irq)) == pdata->irq_polarity) {
+ phys->info.attn_count++;
+ if (driver && driver->irq_handler)
+ driver->irq_handler(rmi_dev, irq);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rmi_spi_xfer(struct rmi_phys_device *phys,
+ const u8 *txbuf, unsigned n_tx, u8 *rxbuf, unsigned n_rx)
+{
+ struct spi_device *client = to_spi_device(phys->dev);
+ struct rmi_spi_data *v2_data = phys->data;
+ struct rmi_device_platform_data *pdata = phys->dev->platform_data;
+ int status;
+ struct spi_message message;
+ struct spi_transfer *xfers;
+ int total_bytes = n_tx + n_rx;
+ u8 local_buf[total_bytes];
+ int xfer_count = 0;
+ int xfer_index = 0;
+ int block_delay = n_rx > 0 ? pdata->spi_data.block_delay_us : 0;
+ int byte_delay = n_rx > 1 ? pdata->spi_data.read_delay_us : 0;
+ int write_delay = n_tx > 1 ? pdata->spi_data.write_delay_us : 0;
+#if COMMS_DEBUG
+ int i;
+#endif
+ // pr_info("in function ____%s____ \n", __func__);
+
+ if (v2_data->split_read_pending) {
+ block_delay =
+ n_rx > 0 ? pdata->spi_data.split_read_block_delay_us : 0;
+ byte_delay =
+ n_tx > 1 ? pdata->spi_data.split_read_byte_delay_us : 0;
+ write_delay = 0;
+ }
+
+ if (n_tx) {
+ phys->info.tx_count++;
+ phys->info.tx_bytes += n_tx;
+ if (write_delay)
+ xfer_count += n_tx;
+ else
+ xfer_count += 1;
+ }
+
+ if (n_rx) {
+ phys->info.rx_count++;
+ phys->info.rx_bytes += n_rx;
+ if (byte_delay)
+ xfer_count += n_rx;
+ else
+ xfer_count += 1;
+ }
+
+ xfers = kcalloc(xfer_count,
+ sizeof(struct spi_transfer), GFP_KERNEL);
+ if (!xfers)
+ return -ENOMEM;
+
+ spi_message_init(&message);
+
+ if (n_tx) {
+ if (write_delay) {
+ for (xfer_index = 0; xfer_index < n_tx;
+ xfer_index++) {
+ memset(&xfers[xfer_index], 0,
+ sizeof(struct spi_transfer));
+ xfers[xfer_index].len = 1;
+ xfers[xfer_index].delay_usecs = write_delay;
+ xfers[xfer_index].cs_change = 1;
+ xfers[xfer_index].tx_buf = txbuf + xfer_index;
+ spi_message_add_tail(&xfers[xfer_index],
+ &message);
+ }
+ } else {
+ memset(&xfers[0], 0, sizeof(struct spi_transfer));
+ xfers[0].len = n_tx;
+ spi_message_add_tail(&xfers[0], &message);
+ memcpy(local_buf, txbuf, n_tx);
+ xfers[0].tx_buf = local_buf;
+ xfer_index++;
+ }
+ if (block_delay){
+ xfers[xfer_index-1].delay_usecs = block_delay;
+ xfers[xfer_index].cs_change = 1;
+ }
+ }
+ if (n_rx) {
+ if (byte_delay) {
+ int buffer_offset = n_tx;
+ for (; xfer_index < xfer_count; xfer_index++) {
+ memset(&xfers[xfer_index], 0,
+ sizeof(struct spi_transfer));
+ xfers[xfer_index].len = 1;
+ xfers[xfer_index].delay_usecs = byte_delay;
+ xfers[xfer_index].cs_change = 1;
+ xfers[xfer_index].rx_buf =
+ local_buf + buffer_offset;
+ buffer_offset++;
+ spi_message_add_tail(&xfers[xfer_index],
+ &message);
+ }
+ } else {
+ memset(&xfers[xfer_index], 0,
+ sizeof(struct spi_transfer));
+ xfers[xfer_index].len = n_rx;
+ xfers[xfer_index].rx_buf = local_buf + n_tx;
+ spi_message_add_tail(&xfers[xfer_index], &message);
+ xfer_index++;
+ }
+ }
+
+#if COMMS_DEBUG
+ if (n_tx) {
+ dev_info(&client->dev, "SPI sends %d bytes: ", n_tx);
+ for (i = 0; i < n_tx; i++)
+ // dev_info(&client->dev, "%02X ", txbuf[i]);
+ pr_info(": %02X ", txbuf[i]);
+ // dev_info(&client->dev, "\n");
+ pr_info("\n");
+ }
+#endif
+
+ /* do the i/o */
+ if (pdata->spi_data.cs_assert) {
+ status = pdata->spi_data.cs_assert(
+ pdata->spi_data.cs_assert_data, true);
+ if (!status) {
+ dev_err(phys->dev, "Failed to assert CS.");
+ /* nonzero means error */
+ status = -1;
+ goto error_exit;
+ } else
+ status = 0;
+ }
+
+ if (pdata->spi_data.pre_delay_us)
+ udelay(pdata->spi_data.pre_delay_us);
+
+ status = spi_sync(client, &message);
+
+ if (pdata->spi_data.post_delay_us)
+ udelay(pdata->spi_data.post_delay_us);
+
+ if (pdata->spi_data.cs_assert) {
+ status = pdata->spi_data.cs_assert(
+ pdata->spi_data.cs_assert_data, false);
+ if (!status) {
+ dev_err(phys->dev, "Failed to deassert CS.");
+ /* nonzero means error */
+ status = -1;
+ goto error_exit;
+ } else
+ status = 0;
+ }
+
+ if (status == 0) {
+ memcpy(rxbuf, local_buf + n_tx, n_rx);
+ status = message.status;
+ } else {
+ phys->info.tx_errs++;
+ phys->info.rx_errs++;
+ dev_err(phys->dev, "spi_sync failed with error code %d.",
+ status);
+ }
+
+#if COMMS_DEBUG
+ if (n_rx) {
+ dev_info(&client->dev, "SPI received %d bytes: ", n_rx);
+ for (i = 0; i < n_rx; i++)
+ // dev_info(&client->dev, "%02X ", rxbuf[i]);
+ pr_info(": %02X ", rxbuf[i]);
+ // dev_info(&client->dev, "\n");
+ pr_info("\n");
+ }
+#endif
+
+error_exit:
+ kfree(xfers);
+ return status;
+}
+
+static int rmi_spi_v2_write_block(struct rmi_phys_device *phys, u16 addr,
+ u8 *buf, int len)
+{
+ struct rmi_spi_data *data = phys->data;
+ u8 txbuf[len + 4];
+ int error;
+ // pr_info("in function ____%s____ \n", __func__);
+
+ txbuf[0] = SPI_V2_WRITE;
+ txbuf[1] = (addr >> 8) & 0x00FF;
+ txbuf[2] = addr & 0x00FF;
+ txbuf[3] = len;
+
+ memcpy(&txbuf[4], buf, len);
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_SPI_PAGE(addr) != data->page) {
+ error = data->set_page(phys, RMI_SPI_PAGE(addr));
+ if (error < 0)
+ goto exit;
+ }
+
+ error = rmi_spi_xfer(phys, buf, len + 4, NULL, 0);
+ if (error < 0)
+ goto exit;
+ error = len;
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return error;
+}
+
+static int rmi_spi_v2_write(struct rmi_phys_device *phys, u16 addr, u8 data)
+{
+ int error = rmi_spi_v2_write_block(phys, addr, &data, 1);
+ // pr_info("in function ____%s____ \n", __func__);
+
+ return (error == 1) ? 0 : error;
+}
+
+static int rmi_spi_v1_write_block(struct rmi_phys_device *phys, u16 addr,
+ u8 *buf, int len)
+{
+ struct rmi_spi_data *data = phys->data;
+ unsigned char txbuf[len + 2];
+ int error;
+ // pr_info("in function ____%s____ \n", __func__);
+
+ txbuf[0] = addr >> 8;
+ txbuf[1] = addr;
+ memcpy(txbuf+2, buf, len);
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_SPI_PAGE(addr) != data->page) {
+ error = data->set_page(phys, RMI_SPI_PAGE(addr));
+ if (error < 0)
+ goto exit;
+ }
+
+ error = rmi_spi_xfer(phys, txbuf, len + 2, NULL, 0);
+ if (error < 0)
+ goto exit;
+ error = len;
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return error;
+}
+
+static int rmi_spi_v1_write(struct rmi_phys_device *phys, u16 addr, u8 data)
+{
+ int error = rmi_spi_v1_write_block(phys, addr, &data, 1);
+ // pr_info("in function ____%s____ \n", __func__);
+
+ return (error == 1) ? 0 : error;
+}
+
+static int rmi_spi_v2_split_read_block(struct rmi_phys_device *phys, u16 addr,
+ u8 *buf, int len)
+{
+ struct rmi_spi_data *data = phys->data;
+ u8 txbuf[4];
+ u8 rxbuf[len + 1]; /* one extra byte for read length */
+ int error;
+ // pr_info("in function ____%s____ \n", __func__);
+
+ txbuf[0] = SPI_V2_PREPARE_SPLIT_READ;
+ txbuf[1] = (addr >> 8) & 0x00FF;
+ txbuf[2] = addr & 0x00ff;
+ txbuf[3] = len;
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_SPI_PAGE(addr) != data->page) {
+ error = data->set_page(phys, RMI_SPI_PAGE(addr));
+ if (error < 0)
+ goto exit;
+ }
+
+ data->split_read_pending = true;
+
+ error = rmi_spi_xfer(phys, txbuf, 4, NULL, 0);
+ if (error < 0) {
+ data->split_read_pending = false;
+ goto exit;
+ }
+
+ wait_for_completion(&data->irq_comp);
+
+ txbuf[0] = SPI_V2_EXECUTE_SPLIT_READ;
+ txbuf[1] = 0;
+
+ error = rmi_spi_xfer(phys, txbuf, 2, rxbuf, len + 1);
+ data->split_read_pending = false;
+ if (error < 0)
+ goto exit;
+
+ /* first byte is length */
+ if (rxbuf[0] != len) {
+ error = -EIO;
+ goto exit;
+ }
+
+ memcpy(buf, rxbuf + 1, len);
+ error = len;
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return error;
+}
+
+static int rmi_spi_v2_read_block(struct rmi_phys_device *phys, u16 addr,
+ u8 *buf, int len)
+{
+ struct rmi_spi_data *data = phys->data;
+ u8 txbuf[4];
+ int error;
+ // pr_info("in function ____%s____ \n", __func__);
+
+ txbuf[0] = SPI_V2_UNIFIED_READ;
+ txbuf[1] = (addr >> 8) & 0x00FF;
+ txbuf[2] = addr & 0x00ff;
+ txbuf[3] = len;
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_SPI_PAGE(addr) != data->page) {
+ error = data->set_page(phys, RMI_SPI_PAGE(addr));
+ if (error < 0)
+ goto exit;
+ }
+
+ error = rmi_spi_xfer(phys, txbuf, 4, buf, len);
+ if (error < 0)
+ goto exit;
+ error = len;
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return error;
+}
+
+static int rmi_spi_v2_read(struct rmi_phys_device *phys, u16 addr, u8 *buf)
+{
+ int error = rmi_spi_v2_read_block(phys, addr, buf, 1);
+
+ return (error == 1) ? 0 : error;
+}
+
+static int rmi_spi_v1_read_block(struct rmi_phys_device *phys, u16 addr,
+ u8 *buf, int len)
+{
+ struct rmi_spi_data *data = phys->data;
+ u8 txbuf[2];
+ int error;
+ // pr_info("in function ____%s____ \n", __func__);
+
+ txbuf[0] = (addr >> 8) | RMI_V1_READ_FLAG;
+ txbuf[1] = addr;
+
+ mutex_lock(&data->page_mutex);
+
+ if (RMI_SPI_PAGE(addr) != data->page) {
+ error = data->set_page(phys, RMI_SPI_PAGE(addr));
+ if (error < 0)
+ goto exit;
+ }
+
+ error = rmi_spi_xfer(phys, txbuf, 2, buf, len);
+ // pr_info(" back in function %s, rmi_spi_xfer returned %d\n", __func__, error);
+
+ if (error < 0)
+ goto exit;
+ error = len;
+
+exit:
+ mutex_unlock(&data->page_mutex);
+ return error;
+}
+
+static int rmi_spi_v1_read(struct rmi_phys_device *phys, u16 addr, u8 *buf)
+{
+ int error = rmi_spi_v1_read_block(phys, addr, buf, 1);
+ pr_info("in function ____%s____ \n", __func__);
+
+ return (error == 1) ? 0 : error;
+}
+
+#define RMI_SPI_PAGE_SELECT_WRITE_LENGTH 1
+
+static int rmi_spi_v1_set_page(struct rmi_phys_device *phys, u8 page)
+{
+ struct rmi_spi_data *data = phys->data;
+ u8 txbuf[] = {RMI_PAGE_SELECT_REGISTER >> 8,
+ RMI_PAGE_SELECT_REGISTER & 0xFF, page};
+ int error;
+ pr_info("in function ____%s____ \n", __func__);
+
+ error = rmi_spi_xfer(phys, txbuf, sizeof(txbuf), NULL, 0);
+ if (error < 0) {
+ dev_err(phys->dev, "Failed to set page select, code: %d.\n",
+ error);
+ return error;
+ }
+
+ data->page = page;
+
+ return RMI_SPI_PAGE_SELECT_WRITE_LENGTH;
+}
+
+static int rmi_spi_v2_set_page(struct rmi_phys_device *phys, u8 page)
+{
+ struct rmi_spi_data *data = phys->data;
+
+ u8 txbuf[] = {SPI_V2_WRITE, RMI_PAGE_SELECT_REGISTER >> 8,
+ RMI_PAGE_SELECT_REGISTER & 0xFF,
+ RMI_SPI_PAGE_SELECT_WRITE_LENGTH, page};
+ int error;
+ pr_info("in function ____%s____ \n", __func__);
+
+ error = rmi_spi_xfer(phys, txbuf, sizeof(txbuf), NULL, 0);
+ if (error < 0) {
+ dev_err(phys->dev, "Failed to set page select, code: %d.\n",
+ error);
+ return error;
+ }
+
+ data->page = page;
+
+ return RMI_SPI_PAGE_SELECT_WRITE_LENGTH;
+}
+
+
+static int acquire_attn_irq(struct rmi_spi_data *data)
+{
+ int retval = 0;
+ pr_info("in function ____%s____ \n", __func__);
+ pr_info(" irq = %d\n", data->irq);
+ pr_info(" rmi_spi_hard_irq = 0x%8x\n", rmi_spi_hard_irq);
+ pr_info(" rmi_spi_irq_thread = 0x%8x\n", rmi_spi_irq_thread);
+ pr_info(" data->irq_flags = 0x%8x\n", data->irq_flags);
+ pr_info(" dev_name(data->phys->dev) = %s\n", dev_name(data->phys->dev));
+ pr_info(" data->phys = 0x%8x\n", data->phys);
+
+ retval = request_threaded_irq(data->irq, rmi_spi_hard_irq,
+ rmi_spi_irq_thread, data->irq_flags,
+ dev_name(data->phys->dev), data->phys);
+
+ pr_info(" retval = = %d\n", retval);
+ return retval;
+}
+
+static int enable_device(struct rmi_phys_device *phys)
+{
+ int retval = 0;
+
+ struct rmi_spi_data *data = phys->data;
+
+ if (data->enabled) {
+ dev_info(phys->dev, "Physical device already enabled.\n");
+ return 0;
+ }
+
+ retval = acquire_attn_irq(data);
+ if (retval)
+ goto error_exit;
+
+ data->enabled = true;
+ dev_info(phys->dev, "Physical device enabled.\n");
+ return 0;
+
+error_exit:
+ dev_err(phys->dev, "Failed to enable physical device. Code=%d.\n",
+ retval);
+ return retval;
+}
+
+
+static void disable_device(struct rmi_phys_device *phys)
+{
+ struct rmi_spi_data *data = phys->data;
+
+ pr_info("in function ____%s____ \n", __func__);
+ if (!data->enabled) {
+ dev_warn(phys->dev, "Physical device already disabled.\n");
+ return;
+ }
+ disable_irq(data->irq);
+ free_irq(data->irq, data->phys);
+
+ dev_info(phys->dev, "Physical device disabled.\n");
+ data->enabled = false;
+}
+
+
+
+#define DUMMY_READ_SLEEP_US 10
+
+static int rmi_spi_check_device(struct rmi_phys_device *rmi_phys)
+{
+ u8 buf[6];
+ int error;
+ int i;
+
+ pr_info("in function ____%s____ \n", __func__);
+
+ /* Some SPI subsystems return 0 for the very first read you do. So
+ * we use this dummy read to get that out of the way.
+ */
+ error = rmi_spi_v1_read_block(rmi_phys, PDT_START_SCAN_LOCATION,
+ buf, sizeof(buf));
+ if (error < 0) {
+ dev_err(rmi_phys->dev, "dummy read failed with %d.\n", error);
+ return error;
+ }
+ udelay(DUMMY_READ_SLEEP_US);
+
+ /* Force page select to 0.
+ */
+ error = rmi_spi_v1_set_page(rmi_phys, 0x00);
+ if (error < 0)
+ return error;
+
+ /* Now read the first PDT entry. We know where this is, and if the
+ * RMI4 device is out there, these 6 bytes will be something other
+ * than all 0x00 or 0xFF. We need to check for 0x00 and 0xFF,
+ * because many (maybe all) SPI implementations will return all 0x00
+ * or all 0xFF on read if the device is not connected.
+ */
+ error = rmi_spi_v1_read_block(rmi_phys, PDT_START_SCAN_LOCATION,
+ buf, sizeof(buf));
+ if (error < 0) {
+ dev_err(rmi_phys->dev, "probe read failed with %d.\n", error);
+ return error;
+ }
+
+ dev_info(rmi_phys->dev, "probe read succeeded with %d.\n", error);
+ for (i = 0; i < sizeof(buf); i++) {
+ if (buf[i] != 0x00 && buf[i] != 0xFF)
+ return error;
+ }
+
+ dev_err(rmi_phys->dev, "probe read returned invalid block.\n");
+ return -ENODEV;
+}
+
+static int __devinit rmi_spi_probe(struct spi_device *spi)
+{
+ struct rmi_phys_device *rmi_phys;
+ struct rmi_spi_data *data;
+ struct rmi_device_platform_data *pdata = spi->dev.platform_data;
+ u8 buf[2];
+ int error;
+
+ pr_info("%s: probe for rmi_spi device\n", __func__);
+
+ if (!pdata) {
+ dev_err(&spi->dev, "no platform data\n");
+ return -EINVAL;
+ }
+
+ if (spi->master->flags & SPI_MASTER_HALF_DUPLEX)
+ return -EINVAL;
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_3;
+ error = spi_setup(spi);
+ if (error < 0) {
+ dev_err(&spi->dev, "spi_setup failed!\n");
+ return error;
+ }
+
+ rmi_phys = kzalloc(sizeof(struct rmi_phys_device), GFP_KERNEL);
+ if (!rmi_phys)
+ return -ENOMEM;
+
+ data = kzalloc(sizeof(struct rmi_spi_data), GFP_KERNEL);
+ if (!data) {
+ error = -ENOMEM;
+ goto err_phys;
+ }
+ data->enabled = true; /* We plan to come up enabled. */
+ data->irq = gpio_to_irq(pdata->irq);
+ data->irq_flags = (pdata->irq_polarity == RMI_IRQ_ACTIVE_HIGH) ?
+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
+ data->phys = rmi_phys;
+
+ rmi_phys->data = data;
+ rmi_phys->dev = &spi->dev;
+
+ rmi_phys->write = rmi_spi_v1_write;
+ rmi_phys->write_block = rmi_spi_v1_write_block;
+ rmi_phys->read = rmi_spi_v1_read;
+ rmi_phys->read_block = rmi_spi_v1_read_block;
+ rmi_phys->enable_device = enable_device;
+ rmi_phys->disable_device = disable_device;
+ data->set_page = rmi_spi_v1_set_page;
+
+ rmi_phys->info.proto = spi_v1_proto_name;
+
+ mutex_init(&data->page_mutex);
+
+ pr_info("%s: setting the driverdata on the device\n", __func__);
+
+ dev_set_drvdata(&spi->dev, rmi_phys);
+
+ pr_info("%s: done setting driverdata %s\n", __func__, dev_name(&spi->dev));
+
+
+ pdata->spi_data.block_delay_us = pdata->spi_data.block_delay_us ?
+ pdata->spi_data.block_delay_us : RMI_SPI_BLOCK_DELAY_US;
+ pdata->spi_data.read_delay_us = pdata->spi_data.read_delay_us ?
+ pdata->spi_data.read_delay_us : RMI_SPI_BYTE_DELAY_US;
+ pdata->spi_data.write_delay_us = pdata->spi_data.write_delay_us ?
+ pdata->spi_data.write_delay_us : RMI_SPI_BYTE_DELAY_US;
+ pdata->spi_data.split_read_block_delay_us =
+ pdata->spi_data.split_read_block_delay_us ?
+ pdata->spi_data.split_read_block_delay_us :
+ RMI_SPI_BLOCK_DELAY_US;
+ pdata->spi_data.split_read_byte_delay_us =
+ pdata->spi_data.split_read_byte_delay_us ?
+ pdata->spi_data.split_read_byte_delay_us :
+ RMI_SPI_BYTE_DELAY_US;
+
+ pr_info("%s configuring GPIOs\n", __func__);
+
+ if (pdata->gpio_config) {
+ error = pdata->gpio_config(&spi->dev, true);
+ if (error < 0) {
+ dev_err(&spi->dev, "Failed to setup GPIOs, code: %d.\n",
+ error);
+ goto err_data;
+ }
+ }
+
+ error = rmi_spi_check_device(rmi_phys);
+ if (error < 0)
+ goto err_data;
+
+ /* check if this is an SPI v2 device */
+ dev_info(&spi->dev, "%s: checking SPI version on RMI device\n", __func__);
+ error = rmi_spi_v1_read_block(rmi_phys, RMI_PROTOCOL_VERSION_ADDRESS,
+ buf, 2);
+
+ if (error < 0) {
+ dev_info(&spi->dev, "failed to get SPI version number!\n");
+ dev_err(&spi->dev, "failed to get SPI version number!\n");
+ goto err_data;
+ }
+
+ dev_info(&spi->dev, "SPI version is %d", buf[0]);
+
+ if (buf[0] == 1) {
+ /* SPIv2 */
+ rmi_phys->write = rmi_spi_v2_write;
+ rmi_phys->write_block = rmi_spi_v2_write_block;
+ rmi_phys->read = rmi_spi_v2_read;
+ data->set_page = rmi_spi_v2_set_page;
+
+ rmi_phys->info.proto = spi_v2_proto_name;
+
+ if (pdata->irq > 0) {
+ init_completion(&data->irq_comp);
+ rmi_phys->read_block = rmi_spi_v2_split_read_block;
+ } else {
+ rmi_phys->read_block = rmi_spi_v2_read_block;
+ }
+ } else if (buf[0] != 0) {
+ dev_err(&spi->dev, "Unrecognized SPI version %d.\n", buf[0]);
+ error = -ENODEV;
+ goto err_data;
+ }
+
+ error = rmi_register_phys_device(rmi_phys);
+ if (error) {
+ dev_err(&spi->dev, "failed to register physical driver\n");
+ goto err_data;
+ }
+
+ if (pdata->irq > 0) {
+ error = acquire_attn_irq(data);
+ if (error < 0) {
+ dev_err(&spi->dev, "request_threaded_irq failed %d\n",
+ pdata->irq);
+ goto err_unregister;
+ }
+ }
+
+#if defined(CONFIG_RMI4_DEV)
+ pr_info(" CONFIG_RMI4_DEV is defined\n");
+
+ error = gpio_export(pdata->irq, false);
+ if (error) {
+ dev_warn(&spi->dev, "WARNING: Failed to export ATTN gpio!\n");
+ error = 0;
+ } else {
+ error = gpio_export_link(&(rmi_phys->rmi_dev->dev), "attn",
+ pdata->irq);
+ if (error) {
+ dev_warn(&(rmi_phys->rmi_dev->dev), "WARNING: "
+ "Failed to symlink ATTN gpio!\n");
+ error = 0;
+ } else {
+ dev_info(&(rmi_phys->rmi_dev->dev),
+ "%s: Exported GPIO %d.", __func__, pdata->irq);
+ }
+ }
+#endif /* CONFIG_RMI4_DEV */
+
+ dev_info(&spi->dev, "registered RMI SPI driver\n");
+ return 0;
+
+err_unregister:
+ rmi_unregister_phys_device(rmi_phys);
+err_data:
+ kfree(data);
+err_phys:
+ kfree(rmi_phys);
+ return error;
+}
+
+static int __devexit rmi_spi_remove(struct spi_device *spi)
+{
+ struct rmi_phys_device *phys = dev_get_drvdata(&spi->dev);
+ struct rmi_device_platform_data *pd = spi->dev.platform_data;
+ pr_info("in function ____%s____ \n", __func__);
+
+ rmi_unregister_phys_device(phys);
+ kfree(phys->data);
+ kfree(phys);
+
+ if (pd->gpio_config)
+ pd->gpio_config(&spi->dev, false);
+
+ return 0;
+}
+
+static const struct spi_device_id rmi_id[] = {
+ { "rmi", 0 },
+ { "rmi_spi", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, rmi_id);
+
+static struct spi_driver rmi_spi_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "rmi_spi",
+ //.mod_name = "rmi_spi",
+ //.bus = &spi_bus_type,
+ },
+ .id_table = rmi_id,
+ .probe = rmi_spi_probe,
+ .remove = __devexit_p(rmi_spi_remove),
+};
+
+static int __init rmi_spi_init(void)
+{
+ pr_info("%s: registering synaptics spi driver (ref=124)\n", __func__);
+ pr_info(" driver.owner = 0x%x\n", (unsigned int)rmi_spi_driver.driver.owner);
+ pr_info(" driver.name = %s\n", rmi_spi_driver.driver.name);
+ pr_info(" id_table[0].name = %s\n", rmi_spi_driver.id_table[0].name );
+ pr_info(" id_table[1].name = %s\n", rmi_spi_driver.id_table[1].name );
+ pr_info(" probe function ptr = 0x%x\n", (unsigned int)rmi_spi_driver.probe );
+
+
+ return spi_register_driver(&rmi_spi_driver);
+}
+
+static void __exit rmi_spi_exit(void)
+{
+ spi_unregister_driver(&rmi_spi_driver);
+}
+
+MODULE_AUTHOR("Christopher Heiny <cheiny@synaptics.com>");
+MODULE_DESCRIPTION("RMI SPI driver");
+MODULE_LICENSE("GPL");
+
+module_init(rmi_spi_init);
+module_exit(rmi_spi_exit);
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index 61038cf06ec9..b394c6c5fbe7 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -152,4 +152,14 @@ config TEGRA_IOMMU_SMMU
space through the SMMU (System Memory Management Unit)
hardware included on Tegra SoCs.
+config TEGRA_IOMMU_GART
+ bool "Tegra GART IOMMU Support"
+ depends on ARCH_TEGRA_2x_SOC
+ select IOMMU_API
+ help
+ Enables support for remapping discontiguous physical memory
+ shared with the operating system into contiguous I/O virtual
+ space through the GART (Graphics Address Relocation Table)
+ hardware included on Tegra SoCs.
+
endif # IOMMU_SUPPORT
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 08fff706add3..7ad7a3bc1242 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -8,4 +8,5 @@ obj-$(CONFIG_IRQ_REMAP) += intr_remapping.o
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
obj-$(CONFIG_OMAP_IOVMM) += omap-iovmm.o
obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
+obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o
obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c
new file mode 100644
index 000000000000..c5d118f5c859
--- /dev/null
+++ b/drivers/iommu/tegra-gart.c
@@ -0,0 +1,456 @@
+/*
+ * IOMMU API for GART in Tegra20
+ *
+ * Copyright (c) 2010-2012, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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.
+ */
+
+#define pr_fmt(fmt) "%s(): " fmt, __func__
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+
+#include <asm/cacheflush.h>
+
+/* bitmap of the page sizes currently supported */
+#define GART_IOMMU_PGSIZES (SZ_4K)
+
+#define GART_CONFIG 0x24
+#define GART_ENTRY_ADDR 0x28
+#define GART_ENTRY_DATA 0x2c
+#define GART_ENTRY_PHYS_ADDR_VALID (1 << 31)
+
+#define GART_PAGE_SHIFT 12
+#define GART_PAGE_SIZE (1 << GART_PAGE_SHIFT)
+#define GART_PAGE_MASK \
+ (~(GART_PAGE_SIZE - 1) & ~GART_ENTRY_PHYS_ADDR_VALID)
+
+struct gart_client {
+ struct device *dev;
+ struct list_head list;
+};
+
+struct gart_device {
+ void __iomem *regs;
+ u32 *savedata;
+ u32 page_count; /* total remappable size */
+ dma_addr_t iovmm_base; /* offset to apply to vmm_area */
+ spinlock_t pte_lock; /* for pagetable */
+ struct list_head client;
+ spinlock_t client_lock; /* for client list */
+ struct device *dev;
+};
+
+#define GART_PTE(_pfn) \
+ (GART_ENTRY_PHYS_ADDR_VALID | ((_pfn) << PAGE_SHIFT))
+
+/*
+ * Any interaction between any block on PPSB and a block on APB or AHB
+ * must have these read-back to ensure the APB/AHB bus transaction is
+ * complete before initiating activity on the PPSB block.
+ */
+#define FLUSH_GART_REGS(gart) ((void)readl((gart)->regs + GART_CONFIG))
+
+#define for_each_gart_pte(gart, iova) \
+ for (iova = gart->iovmm_base; \
+ iova < gart->iovmm_base + GART_PAGE_SIZE * gart->page_count; \
+ iova += GART_PAGE_SIZE)
+
+static inline void gart_set_pte(struct gart_device *gart,
+ unsigned long offs, u32 pte)
+{
+ writel(offs, gart->regs + GART_ENTRY_ADDR);
+ writel(pte, gart->regs + GART_ENTRY_DATA);
+
+ dev_dbg(gart->dev, "%s %08lx:%08x\n",
+ pte ? "map" : "unmap", offs, pte & GART_PAGE_MASK);
+}
+
+static inline unsigned long gart_read_pte(struct gart_device *gart,
+ unsigned long offs)
+{
+ unsigned long pte;
+
+ writel(offs, gart->regs + GART_ENTRY_ADDR);
+ pte = readl(gart->regs + GART_ENTRY_DATA);
+
+ return pte;
+}
+
+static void do_gart_setup(struct gart_device *gart, const u32 *data)
+{
+ unsigned long iova;
+
+ for_each_gart_pte(gart, iova)
+ gart_set_pte(gart, iova, data ? *(data++) : 0);
+
+ writel(1, gart->regs + GART_CONFIG);
+ FLUSH_GART_REGS(gart);
+}
+
+#ifdef DEBUG
+static void gart_dump_table(struct gart_device *gart)
+{
+ unsigned long iova;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gart->pte_lock, flags);
+ for_each_gart_pte(gart, iova) {
+ unsigned long pte;
+
+ pte = gart_read_pte(gart, iova);
+
+ dev_dbg(gart->dev, "%s %08lx:%08lx\n",
+ (GART_ENTRY_PHYS_ADDR_VALID & pte) ? "v" : " ",
+ iova, pte & GART_PAGE_MASK);
+ }
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+}
+#else
+static inline void gart_dump_table(struct gart_device *gart)
+{
+}
+#endif
+
+static inline bool gart_iova_range_valid(struct gart_device *gart,
+ unsigned long iova, size_t bytes)
+{
+ unsigned long iova_start, iova_end, gart_start, gart_end;
+
+ iova_start = iova;
+ iova_end = iova_start + bytes - 1;
+ gart_start = gart->iovmm_base;
+ gart_end = gart_start + gart->page_count * GART_PAGE_SIZE - 1;
+
+ if (iova_start < gart_start)
+ return false;
+ if (iova_end > gart_end)
+ return false;
+ return true;
+}
+
+static int gart_iommu_domain_init(struct iommu_domain *domain)
+{
+ return 0;
+}
+
+static void gart_iommu_domain_destroy(struct iommu_domain *domain)
+{
+ struct gart_device *gart = domain->priv;
+
+ spin_lock(&gart->client_lock);
+ if (!list_empty(&gart->client)) {
+ struct gart_client *c;
+
+ list_for_each_entry(c, &gart->client, list)
+ dev_err(gart->dev,
+ "%s is still attached\n", dev_name(c->dev));
+ }
+ spin_unlock(&gart->client_lock);
+ domain->priv = NULL;
+}
+
+static int gart_iommu_attach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct gart_device *gart;
+ struct gart_client *client, *c;
+ int err = 0;
+
+ gart = dev_get_drvdata(dev->parent);
+ if (!gart)
+ return -EINVAL;
+ domain->priv = gart;
+ client = devm_kzalloc(dev, sizeof(*c), GFP_KERNEL);
+ if (!client)
+ return -ENOMEM;
+ client->dev = dev;
+
+ spin_lock(&gart->client_lock);
+ list_for_each_entry(c, &gart->client, list) {
+ if (c->dev == dev) {
+ dev_err(gart->dev,
+ "%s is already attached\n", dev_name(dev));
+ err = -EINVAL;
+ goto fail;
+ }
+ }
+ list_add(&client->list, &gart->client);
+ spin_unlock(&gart->client_lock);
+ dev_dbg(gart->dev, "Attached %s\n", dev_name(dev));
+ return 0;
+
+fail:
+ devm_kfree(dev, client);
+ spin_unlock(&gart->client_lock);
+ return err;
+}
+
+static void gart_iommu_detach_dev(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct gart_device *gart = domain->priv;
+ struct gart_client *c;
+
+ spin_lock(&gart->client_lock);
+
+ list_for_each_entry(c, &gart->client, list) {
+ if (c->dev == dev) {
+ list_del(&c->list);
+ devm_kfree(dev, c);
+ dev_dbg(gart->dev, "Detached %s\n", dev_name(dev));
+ goto out;
+ }
+ }
+ dev_err(gart->dev, "Couldn't find\n");
+out:
+ spin_unlock(&gart->client_lock);
+}
+
+static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t pa, int gfp_order, int prot)
+{
+ struct gart_device *gart = domain->priv;
+ unsigned long flags;
+ unsigned long count = (PAGE_SIZE << gfp_order) >> GART_PAGE_SHIFT;
+ int i;
+
+ if (!gart_iova_range_valid(gart, iova, count * GART_PAGE_SIZE))
+ return -EINVAL;
+
+ spin_lock_irqsave(&gart->pte_lock, flags);
+
+ for (i = 0; i < count; i++) {
+ unsigned long pfn;
+
+ pfn = __phys_to_pfn(pa);
+ if (!pfn_valid(pfn)) {
+ dev_err(gart->dev, "Invalid page: %08x\n", pa);
+ goto fail;
+ }
+
+ gart_set_pte(gart, iova, GART_PTE(pfn));
+ iova += GART_PAGE_SIZE;
+ }
+ FLUSH_GART_REGS(gart);
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+ return 0;
+
+fail:
+ while (--i >= 0) {
+ iova -= GART_PAGE_SIZE;
+ gart_set_pte(gart, iova, 0);
+ }
+ FLUSH_GART_REGS(gart);
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+ return -EINVAL;
+}
+
+static int gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
+ int gfp_order)
+{
+ int i;
+ struct gart_device *gart = domain->priv;
+ unsigned long flags;
+ unsigned long count = (PAGE_SIZE << gfp_order) >> GART_PAGE_SHIFT;
+
+ if (!gart_iova_range_valid(gart, iova, count * GART_PAGE_SIZE))
+ return -EINVAL;
+
+ spin_lock_irqsave(&gart->pte_lock, flags);
+ for (i = 0; i < count; i++) {
+ gart_set_pte(gart, iova, 0);
+ iova += GART_PAGE_SIZE;
+ }
+ FLUSH_GART_REGS(gart);
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+ return 0;
+}
+
+static phys_addr_t gart_iommu_iova_to_phys(struct iommu_domain *domain,
+ unsigned long iova)
+{
+ struct gart_device *gart = domain->priv;
+ unsigned long pte;
+ phys_addr_t pa;
+ unsigned long flags;
+
+ if (!gart_iova_range_valid(gart, iova, 0))
+ return -EINVAL;
+
+ spin_lock_irqsave(&gart->pte_lock, flags);
+ pte = gart_read_pte(gart, iova);
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+
+ pa = (pte & GART_PAGE_MASK);
+ if (!pfn_valid(__phys_to_pfn(pa))) {
+ dev_err(gart->dev, "No entry for %08lx:%08x\n", iova, pa);
+ gart_dump_table(gart);
+ return -EINVAL;
+ }
+ return pa;
+}
+
+static int gart_iommu_domain_has_cap(struct iommu_domain *domain,
+ unsigned long cap)
+{
+ return 0;
+}
+
+static struct iommu_ops gart_iommu_ops = {
+ .domain_init = gart_iommu_domain_init,
+ .domain_destroy = gart_iommu_domain_destroy,
+ .attach_dev = gart_iommu_attach_dev,
+ .detach_dev = gart_iommu_detach_dev,
+ .map = gart_iommu_map,
+ .unmap = gart_iommu_unmap,
+ .iova_to_phys = gart_iommu_iova_to_phys,
+ .domain_has_cap = gart_iommu_domain_has_cap,
+};
+
+static int tegra_gart_suspend(struct device *dev)
+{
+ struct gart_device *gart = dev_get_drvdata(dev);
+ unsigned long iova;
+ u32 *data = gart->savedata;
+ unsigned long flags;
+
+ spin_lock_irqsave(&gart->pte_lock, flags);
+ for_each_gart_pte(gart, iova)
+ *(data++) = gart_read_pte(gart, iova);
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+ return 0;
+}
+
+static int tegra_gart_resume(struct device *dev)
+{
+ struct gart_device *gart = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&gart->pte_lock, flags);
+ do_gart_setup(gart, gart->savedata);
+ spin_unlock_irqrestore(&gart->pte_lock, flags);
+ return 0;
+}
+
+static int tegra_gart_probe(struct platform_device *pdev)
+{
+ struct gart_device *gart;
+ struct resource *res, *res_remap;
+ void __iomem *gart_regs;
+ int err;
+ struct device *dev = &pdev->dev;
+
+ BUILD_BUG_ON(PAGE_SHIFT != GART_PAGE_SHIFT);
+
+ /* the GART memory aperture is required */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ res_remap = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!res || !res_remap) {
+ dev_err(dev, "GART memory aperture expected\n");
+ return -ENXIO;
+ }
+
+ gart = devm_kzalloc(dev, sizeof(*gart), GFP_KERNEL);
+ if (!gart) {
+ dev_err(dev, "failed to allocate gart_device\n");
+ return -ENOMEM;
+ }
+
+ gart_regs = devm_ioremap(dev, res->start, resource_size(res));
+ if (!gart_regs) {
+ dev_err(dev, "failed to remap GART registers\n");
+ err = -ENXIO;
+ goto fail;
+ }
+
+ gart->dev = &pdev->dev;
+ spin_lock_init(&gart->pte_lock);
+ spin_lock_init(&gart->client_lock);
+ INIT_LIST_HEAD(&gart->client);
+ gart->regs = gart_regs;
+ gart->iovmm_base = (dma_addr_t)res_remap->start;
+ gart->page_count = (resource_size(res_remap) >> GART_PAGE_SHIFT);
+
+ gart->savedata = vmalloc(sizeof(u32) * gart->page_count);
+ if (!gart->savedata) {
+ dev_err(dev, "failed to allocate context save area\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ platform_set_drvdata(pdev, gart);
+ do_gart_setup(gart, NULL);
+ return 0;
+
+fail:
+ if (gart_regs)
+ devm_iounmap(dev, gart_regs);
+ if (gart && gart->savedata)
+ vfree(gart->savedata);
+ devm_kfree(dev, gart);
+ return err;
+}
+
+static int tegra_gart_remove(struct platform_device *pdev)
+{
+ struct gart_device *gart = platform_get_drvdata(pdev);
+ struct device *dev = gart->dev;
+
+ writel(0, gart->regs + GART_CONFIG);
+ if (gart->savedata)
+ vfree(gart->savedata);
+ if (gart->regs)
+ devm_iounmap(dev, gart->regs);
+ devm_kfree(dev, gart);
+ return 0;
+}
+
+const struct dev_pm_ops tegra_gart_pm_ops = {
+ .suspend = tegra_gart_suspend,
+ .resume = tegra_gart_resume,
+};
+
+static struct platform_driver tegra_gart_driver = {
+ .probe = tegra_gart_probe,
+ .remove = tegra_gart_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "tegra_gart",
+ .pm = &tegra_gart_pm_ops,
+ },
+};
+
+static int __devinit tegra_gart_init(void)
+{
+ register_iommu(&gart_iommu_ops);
+ return platform_driver_register(&tegra_gart_driver);
+}
+
+static void __exit tegra_gart_exit(void)
+{
+ platform_driver_unregister(&tegra_gart_driver);
+}
+
+subsys_initcall(tegra_gart_init);
+module_exit(tegra_gart_exit);
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index d9587dffe533..606fc04fd76a 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -508,8 +508,17 @@ static int read_balance(conf_t *conf, r1bio_t *r1_bio, int *max_sectors)
if (test_bit(WriteMostly, &rdev->flags)) {
/* Don't balance among write-mostly, just
* use the first as a last resort */
- if (best_disk < 0)
+ if (best_disk < 0) {
+ if (is_badblock(rdev, this_sector, sectors,
+ &first_bad, &bad_sectors)) {
+ if (first_bad < this_sector)
+ /* Cannot use this */
+ continue;
+ best_good_sectors = first_bad - this_sector;
+ } else
+ best_good_sectors = sectors;
best_disk = disk;
+ }
continue;
}
/* This is a reasonable device to use. It might
diff --git a/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c b/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c
index a26b9e990ca1..f6f5b1ec8b7d 100644
--- a/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c
+++ b/drivers/media/video/tegra/mediaserver/tegra_mediaserver.c
@@ -394,7 +394,7 @@ static int mediasrv_update_block_info(
)
{
struct tegra_mediasrv_block *entry = NULL;
- struct tegra_mediasrv_block *block;
+ struct tegra_mediasrv_block *block = NULL;
int e;
list_for_each_entry(entry, &node->blocks, entry) {
@@ -470,6 +470,7 @@ static long mediasrv_unlocked_ioctl(struct file *file, unsigned int cmd,
copy_fail:
e = -EFAULT;
fail:
+ mutex_unlock(&mediasrv->lock);
return e;
}
diff --git a/drivers/media/video/tegra/nvavp/nvavp_dev.c b/drivers/media/video/tegra/nvavp/nvavp_dev.c
index 1c274297b3e1..58c9f8059a4c 100644
--- a/drivers/media/video/tegra/nvavp/nvavp_dev.c
+++ b/drivers/media/video/tegra/nvavp/nvavp_dev.c
@@ -89,6 +89,7 @@ struct nvavp_info {
struct mutex open_lock;
int refcount;
+ int initialized;
struct work_struct clock_disable_work;
@@ -145,6 +146,7 @@ static struct clk *nvavp_clk_get(struct nvavp_info *nvavp, int id)
static void nvavp_clk_ctrl(struct nvavp_info *nvavp, u32 clk_en)
{
if (clk_en && !nvavp->clk_enabled) {
+ nvhost_module_busy(nvhost_get_host(nvavp->nvhost_dev)->dev);
clk_enable(nvavp->bsev_clk);
clk_enable(nvavp->vde_clk);
clk_set_rate(nvavp->emc_clk, nvavp->emc_clk_rate);
@@ -159,6 +161,7 @@ static void nvavp_clk_ctrl(struct nvavp_info *nvavp, u32 clk_en)
clk_disable(nvavp->vde_clk);
clk_set_rate(nvavp->emc_clk, 0);
clk_set_rate(nvavp->sclk, 0);
+ nvhost_module_idle(nvhost_get_host(nvavp->nvhost_dev)->dev);
nvavp->clk_enabled = 0;
dev_dbg(&nvavp->nvhost_dev->dev, "%s: resetting emc_clk "
"and sclk\n", __func__);
@@ -289,12 +292,16 @@ static void nvavp_halt_vde(struct nvavp_info *nvavp)
clk_disable(nvavp->bsev_clk);
tegra_periph_reset_assert(nvavp->vde_clk);
clk_disable(nvavp->vde_clk);
+ nvhost_module_idle(nvhost_get_host(nvavp->nvhost_dev)->dev);
nvavp->clk_enabled = 0;
}
}
static int nvavp_reset_vde(struct nvavp_info *nvavp)
{
+ if (!nvavp->clk_enabled)
+ nvhost_module_busy(nvhost_get_host(nvavp->nvhost_dev)->dev);
+
clk_enable(nvavp->bsev_clk);
tegra_periph_reset_assert(nvavp->bsev_clk);
udelay(2);
@@ -696,6 +703,9 @@ static int nvavp_init(struct nvavp_info *nvavp)
char fw_os_file[32];
int ret = 0;
+ if (nvavp->initialized)
+ return ret;
+
#if defined(CONFIG_TEGRA_AVP_KERNEL_ON_MMU) /* Tegra2 with AVP MMU */
/* paddr is any address returned from nvmap_pin */
/* vaddr is AVP_KERNEL_VIRT_BASE */
@@ -767,12 +777,17 @@ static int nvavp_init(struct nvavp_info *nvavp)
nvavp_reset_avp(nvavp, nvavp->os_info.reset_addr);
enable_irq(nvavp->mbox_from_avp_pend_irq);
+ nvavp->initialized = 1;
+
err_exit:
return ret;
}
static void nvavp_uninit(struct nvavp_info *nvavp)
{
+ if (!nvavp->initialized)
+ return;
+
disable_irq(nvavp->mbox_from_avp_pend_irq);
cancel_work_sync(&nvavp->clock_disable_work);
@@ -784,6 +799,8 @@ static void nvavp_uninit(struct nvavp_info *nvavp)
clk_disable(nvavp->sclk);
clk_disable(nvavp->emc_clk);
+
+ nvavp->initialized = 0;
}
static int nvavp_set_clock_ioctl(struct file *filp, unsigned int cmd,
@@ -1024,7 +1041,6 @@ static int tegra_nvavp_open(struct inode *inode, struct file *filp)
filp->private_data = clientctx;
- nvhost_module_busy(nvavp->nvhost_dev->host->dev);
mutex_unlock(&nvavp->open_lock);
return ret;
@@ -1039,7 +1055,6 @@ static int tegra_nvavp_release(struct inode *inode, struct file *filp)
dev_dbg(&nvavp->nvhost_dev->dev, "%s: ++\n", __func__);
filp->private_data = NULL;
- nvhost_module_idle(nvavp->nvhost_dev->host->dev);
mutex_lock(&nvavp->open_lock);
@@ -1124,7 +1139,7 @@ static int tegra_nvavp_probe(struct nvhost_device *ndev)
memset(nvavp, 0, sizeof(*nvavp));
- nvavp->nvhost_syncpt = &ndev->host->syncpt;
+ nvavp->nvhost_syncpt = &nvhost_get_host(ndev)->syncpt;
if (!nvavp->nvhost_syncpt) {
dev_err(&ndev->dev, "cannot get syncpt handle\n");
ret = -ENOENT;
@@ -1357,18 +1372,33 @@ static int tegra_nvavp_remove(struct nvhost_device *ndev)
static int tegra_nvavp_suspend(struct nvhost_device *ndev, pm_message_t state)
{
struct nvavp_info *nvavp = nvhost_get_drvdata(ndev);
+ int ret = 0;
- if (nvavp->refcount)
- nvhost_module_idle(ndev);
- return 0;
+ mutex_lock(&nvavp->open_lock);
+
+ if (nvavp->refcount) {
+ if (nvavp_check_idle(nvavp))
+ nvavp_uninit(nvavp);
+ else
+ ret = -EBUSY;
+ }
+
+ mutex_unlock(&nvavp->open_lock);
+
+ return ret;
}
static int tegra_nvavp_resume(struct nvhost_device *ndev)
{
struct nvavp_info *nvavp = nvhost_get_drvdata(ndev);
+ mutex_lock(&nvavp->open_lock);
+
if (nvavp->refcount)
- nvhost_module_busy(ndev);
+ nvavp_init(nvavp);
+
+ mutex_unlock(&nvavp->open_lock);
+
return 0;
}
#endif
diff --git a/drivers/media/video/tegra/ov2710.c b/drivers/media/video/tegra/ov2710.c
index 11405746056f..a2e02369d8e3 100644
--- a/drivers/media/video/tegra/ov2710.c
+++ b/drivers/media/video/tegra/ov2710.c
@@ -39,6 +39,9 @@ struct ov2710_info {
static struct ov2710_reg mode_1920x1080[] = {
{0x3103, 0x93},
{0x3008, 0x82},
+ {OV2710_TABLE_WAIT_MS, 5},
+ {0x3008, 0x42},
+ {OV2710_TABLE_WAIT_MS, 5},
{0x3017, 0x7f},
{0x3018, 0xfc},
{0x3706, 0x61},
@@ -157,6 +160,7 @@ static struct ov2710_reg mode_1920x1080[] = {
{0x350b, 0x00},
{0x5001, 0x4e},
{0x5000, 0x5f},
+ {0x3008, 0x02},
{OV2710_TABLE_END, 0x0000}
};
@@ -164,6 +168,9 @@ static struct ov2710_reg mode_1920x1080[] = {
static struct ov2710_reg mode_1280x720[] = {
{0x3103, 0x93},
{0x3008, 0x82},
+ {OV2710_TABLE_WAIT_MS, 5},
+ {0x3008, 0x42},
+ {OV2710_TABLE_WAIT_MS, 5},
{0x3017, 0x7f},
{0x3018, 0xfc},
@@ -284,6 +291,7 @@ static struct ov2710_reg mode_1280x720[] = {
{0x350b, 0x00},
{0x5001, 0x4e},
{0x5000, 0x5f},
+ {0x3008, 0x02},
{OV2710_TABLE_END, 0x0000}
};
diff --git a/drivers/media/video/tegra/ov5650.c b/drivers/media/video/tegra/ov5650.c
index cc50e9141e66..3adb46a3bb80 100644
--- a/drivers/media/video/tegra/ov5650.c
+++ b/drivers/media/video/tegra/ov5650.c
@@ -1033,51 +1033,37 @@ static int ov5650_set_mode(struct ov5650_info *info, struct ov5650_mode *mode)
static int ov5650_set_frame_length(struct ov5650_info *info, u32 frame_length)
{
- struct ov5650_reg reg_list[2];
- int i = 0;
int ret;
+ struct ov5650_reg reg_list[2];
+ u8 *b_ptr = info->i2c_trans_buf;
ov5650_get_frame_length_regs(reg_list, frame_length);
- for (i = 0; i < 2; i++) {
- ret = ov5650_write_reg_helper(info, reg_list[i].addr,
- reg_list[i].val);
- if (ret)
- return ret;
- }
+ *b_ptr++ = reg_list[0].addr >> 8;
+ *b_ptr++ = reg_list[0].addr & 0xff;
+ *b_ptr++ = reg_list[0].val & 0xff;
+ *b_ptr++ = reg_list[1].val & 0xff;
+ ret = ov5650_write_bulk_reg_helper(info, 4);
- return 0;
+ return ret;
}
static int ov5650_set_coarse_time(struct ov5650_info *info, u32 coarse_time)
{
int ret;
-
struct ov5650_reg reg_list[3];
- int i = 0;
+ u8 *b_ptr = info->i2c_trans_buf;
ov5650_get_coarse_time_regs(reg_list, coarse_time);
- ret = ov5650_write_reg_helper(info, 0x3212, 0x01);
- if (ret)
- return ret;
-
- for (i = 0; i < 3; i++) {
- ret = ov5650_write_reg_helper(info, reg_list[i].addr,
- reg_list[i].val);
- if (ret)
- return ret;
- }
-
- ret = ov5650_write_reg_helper(info, 0x3212, 0x11);
- if (ret)
- return ret;
-
- ret = ov5650_write_reg_helper(info, 0x3212, 0xa1);
- if (ret)
- return ret;
+ *b_ptr++ = reg_list[0].addr >> 8;
+ *b_ptr++ = reg_list[0].addr & 0xff;
+ *b_ptr++ = reg_list[0].val & 0xff;
+ *b_ptr++ = reg_list[1].val & 0xff;
+ *b_ptr++ = reg_list[2].val & 0xff;
+ ret = ov5650_write_bulk_reg_helper(info, 5);
- return 0;
+ return ret;
}
static int ov5650_set_gain(struct ov5650_info *info, u16 gain)
@@ -1086,12 +1072,53 @@ static int ov5650_set_gain(struct ov5650_info *info, u16 gain)
struct ov5650_reg reg_list;
ov5650_get_gain_reg(&reg_list, gain);
-
ret = ov5650_write_reg_helper(info, reg_list.addr, reg_list.val);
return ret;
}
+static int ov5650_set_group_hold(struct ov5650_info *info, struct ov5650_ae *ae)
+{
+ int ret;
+ int count = 0;
+ bool groupHoldEnabled = false;
+
+ if (ae->gain_enable)
+ count++;
+ if (ae->coarse_time_enable)
+ count++;
+ if (ae->frame_length_enable)
+ count++;
+ if (count >= 2)
+ groupHoldEnabled = true;
+
+ if (groupHoldEnabled) {
+ ret = ov5650_write_reg_helper(info, 0x3212, 0x01);
+ if (ret)
+ return ret;
+ }
+
+ if (ae->gain_enable)
+ ov5650_set_gain(info, ae->gain);
+ if (ae->coarse_time_enable)
+ ov5650_set_coarse_time(info, ae->coarse_time);
+ if (ae->frame_length_enable)
+ ov5650_set_frame_length(info, ae->frame_length);
+
+ if (groupHoldEnabled) {
+ ret = ov5650_write_reg_helper(info, 0x3212, 0x11);
+ if (ret)
+ return ret;
+
+ ret = ov5650_write_reg_helper(info, 0x3212, 0xa1);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+
static int ov5650_set_binning(struct ov5650_info *info, u8 enable)
{
s32 ret;
@@ -1306,6 +1333,17 @@ static long ov5650_ioctl(struct file *file,
pr_err("%s %d %d\n", __func__, __LINE__, err);
return err;
}
+ case OV5650_IOCTL_SET_GROUP_HOLD:
+ {
+ struct ov5650_ae ae;
+ if (copy_from_user(&ae,
+ (const void __user *)arg,
+ sizeof(struct ov5650_ae))) {
+ pr_info("%s %d\n", __func__, __LINE__);
+ return -EFAULT;
+ }
+ return ov5650_set_group_hold(info, &ae);
+ }
default:
return -EINVAL;
}
diff --git a/drivers/media/video/tegra/sh532u.c b/drivers/media/video/tegra/sh532u.c
index 6723fae70ae4..7db14fcf2573 100644
--- a/drivers/media/video/tegra/sh532u.c
+++ b/drivers/media/video/tegra/sh532u.c
@@ -1685,4 +1685,4 @@ static void __exit sh532u_exit(void)
module_init(sh532u_init);
module_exit(sh532u_exit);
-
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tegra/ssl3250a.c b/drivers/media/video/tegra/ssl3250a.c
index 30c70bf1cdc5..ef2c80a883c5 100644
--- a/drivers/media/video/tegra/ssl3250a.c
+++ b/drivers/media/video/tegra/ssl3250a.c
@@ -983,4 +983,4 @@ static void __exit ssl3250a_exit(void)
module_init(ssl3250a_init);
module_exit(ssl3250a_exit);
-
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/tegra/tegra_camera.c b/drivers/media/video/tegra/tegra_camera.c
index aea6bc2517f5..fae9d31eacab 100644
--- a/drivers/media/video/tegra/tegra_camera.c
+++ b/drivers/media/video/tegra/tegra_camera.c
@@ -2,6 +2,7 @@
* drivers/media/video/tegra/tegra_camera.c
*
* Copyright (C) 2010 Google, Inc.
+ * Copyright (C) 2012 Nvidia Corp
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -144,9 +145,9 @@ static bool tegra_camera_enabled(struct tegra_camera_dev *dev)
static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev)
{
- u32 offset;
- struct clk *clk;
+ struct clk *clk, *clk_parent;
struct tegra_camera_clk_info *info = &dev->info;
+ unsigned long parent_rate, parent_div_rate, parent_div_rate_pre;
if (!info) {
dev_err(dev->dev,
@@ -165,11 +166,9 @@ static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev)
switch (info->clk_id) {
case TEGRA_CAMERA_VI_CLK:
clk = dev->vi_clk;
- offset = 0x148;
break;
case TEGRA_CAMERA_VI_SENSOR_CLK:
clk = dev->vi_sensor_clk;
- offset = 0x1a8;
break;
default:
dev_err(dev->dev,
@@ -178,24 +177,40 @@ static int tegra_camera_clk_set_rate(struct tegra_camera_dev *dev)
return -EINVAL;
}
- clk_set_rate(clk, info->rate);
+ clk_parent = clk_get_parent(clk);
+ parent_rate = clk_get_rate(clk_parent);
+ dev_dbg(dev->dev, "%s: clk_id=%d, parent_rate=%lu, clk_rate=%lu\n",
+ __func__, info->clk_id, parent_rate, info->rate);
+ parent_div_rate = parent_rate;
+ parent_div_rate_pre = parent_rate;
+
+ /*
+ * The requested clock rate from user space should be respected.
+ * This loop is to search the clock rate that is higher than requested
+ * clock.
+ */
+ while (parent_div_rate >= info->rate) {
+ parent_div_rate_pre = parent_div_rate;
+ parent_div_rate = clk_round_rate(clk, parent_div_rate-1);
+ }
- if (info->clk_id == TEGRA_CAMERA_VI_CLK) {
- u32 val = 0x2;
- void __iomem *car = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
- void __iomem *apb_misc = IO_ADDRESS(TEGRA_APB_MISC_BASE);
+ dev_dbg(dev->dev, "%s: set_rate=%lu",
+ __func__, parent_div_rate_pre);
- if (info->flag == TEGRA_CAMERA_ENABLE_PD2VI_CLK) {
- val |= TEGRA_CAMERA_PD2VI_CLK_SEL_VI_SENSOR_CLK;
- }
+ clk_set_rate(clk, parent_div_rate_pre);
- writel(val, car + offset);
-
- val = readl(apb_misc + 0x42c);
- writel(val | 0x1, apb_misc + 0x42c);
+ if (info->clk_id == TEGRA_CAMERA_VI_CLK) {
+ /*
+ * bit 25: 0 = pd2vi_Clk, 1 = vi_sensor_clk
+ * bit 24: 0 = internal clock, 1 = external clock(pd2vi_clk)
+ */
+ if (info->flag == TEGRA_CAMERA_ENABLE_PD2VI_CLK)
+ tegra_clk_cfg_ex(clk, TEGRA_CLK_VI_INP_SEL, 2);
}
info->rate = clk_get_rate(clk);
+ dev_dbg(dev->dev, "%s: get_rate=%lu",
+ __func__, info->rate);
return 0;
}
@@ -467,8 +482,15 @@ static int tegra_camera_probe(struct platform_device *pdev)
dev->reg = regulator_get(&pdev->dev, "avdd_dsi_csi");
#endif
if (IS_ERR_OR_NULL(dev->reg)) {
- dev_err(&pdev->dev, "%s: couldn't get regulator\n", __func__);
- return PTR_ERR(dev->reg);
+ if (dev->reg == ERR_PTR(-ENODEV)) {
+ dev->reg = NULL;
+ dev_info(&pdev->dev, "%s: no regulator device, overriding\n",
+ __func__);
+ } else {
+ dev_err(&pdev->dev, "%s: couldn't get regulator\n",
+ __func__);
+ return PTR_ERR(dev->reg);
+ }
}
dev->misc_dev.minor = MISC_DYNAMIC_MINOR;
diff --git a/drivers/media/video/tegra/tps61050.c b/drivers/media/video/tegra/tps61050.c
index e30ebb435f2d..def353ca9c1a 100644
--- a/drivers/media/video/tegra/tps61050.c
+++ b/drivers/media/video/tegra/tps61050.c
@@ -762,6 +762,7 @@ static long tps61050_ioctl(struct file *file,
__func__, __LINE__);
return -EFAULT;
}
+ return 0;
default:
dev_err(&info->i2c_client->dev, "%s unsupported ioctl: %x\n",
@@ -987,3 +988,4 @@ static void __exit tps61050_exit(void)
module_init(tps61050_init);
module_exit(tps61050_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c
index a56be931551c..95c0d7978bec 100644
--- a/drivers/mfd/tps65910-irq.c
+++ b/drivers/mfd/tps65910-irq.c
@@ -215,6 +215,7 @@ int tps65910_irq_init(struct tps65910 *tps65910, int irq,
int tps65910_irq_exit(struct tps65910 *tps65910)
{
- free_irq(tps65910->chip_irq, tps65910);
+ if (tps65910->chip_irq)
+ free_irq(tps65910->chip_irq, tps65910);
return 0;
}
diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c
index 6f5b8cf2f652..01cf5012a08f 100644
--- a/drivers/mfd/tps65910.c
+++ b/drivers/mfd/tps65910.c
@@ -120,7 +120,7 @@ int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask)
goto out;
}
- data &= mask;
+ data &= ~mask;
err = tps65910_i2c_write(tps65910, reg, 1, &data);
if (err)
dev_err(tps65910->dev, "write to reg %x failed\n", reg);
@@ -172,15 +172,12 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,
tps65910_gpio_init(tps65910, pmic_plat_data->gpio_base);
- ret = tps65910_irq_init(tps65910, init_data->irq, init_data);
- if (ret < 0)
- goto err;
+ tps65910_irq_init(tps65910, init_data->irq, init_data);
kfree(init_data);
return ret;
err:
- mfd_remove_devices(tps65910->dev);
kfree(tps65910);
kfree(init_data);
return ret;
@@ -190,8 +187,8 @@ static int tps65910_i2c_remove(struct i2c_client *i2c)
{
struct tps65910 *tps65910 = i2c_get_clientdata(i2c);
- mfd_remove_devices(tps65910->dev);
tps65910_irq_exit(tps65910);
+ mfd_remove_devices(tps65910->dev);
kfree(tps65910);
return 0;
diff --git a/drivers/misc/ti-st/st_core.c b/drivers/misc/ti-st/st_core.c
index ba168a7d54d4..c03590210297 100644
--- a/drivers/misc/ti-st/st_core.c
+++ b/drivers/misc/ti-st/st_core.c
@@ -338,15 +338,31 @@ void st_int_recv(void *disc_data,
/* Unknow packet? */
default:
type = *ptr;
- if (st_gdata->list[type] == NULL) {
- pr_err("chip/interface misbehavior dropping"
- " frame starting with 0x%02x", type);
- goto done;
+ /* Default case means non-HCILL packets,
+ * possibilities are packets for:
+ * (a) valid protocol - Supported Protocols within
+ * the ST_MAX_CHANNELS.
+ * (b) registered protocol - Checked by
+ * "st_gdata->list[type] == NULL)" are supported
+ * protocols only.
+ * Rules out any invalid protocol and
+ * unregistered protocols with channel ID < 16.
+ */
+
+ if ((type >= ST_MAX_CHANNELS) ||
+ (st_gdata->list[type] == NULL)) {
+ pr_err("chip/interface misbehavior "
+ "dropping frame starting "
+ "with 0x%02x", type);
+ goto done;
}
st_gdata->rx_skb = alloc_skb(
st_gdata->list[type]->max_frame_size,
GFP_ATOMIC);
+ if (!st_gdata->rx_skb)
+ goto done;
+
skb_reserve(st_gdata->rx_skb,
st_gdata->list[type]->reserve);
/* next 2 required for BT only */
diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c
index 3a3580566dfc..901f4c46f9c9 100644
--- a/drivers/misc/ti-st/st_kim.c
+++ b/drivers/misc/ti-st/st_kim.c
@@ -450,8 +450,8 @@ long st_kim_start(void *kim_data)
do {
/* platform specific enabling code here */
- if (pdata->chip_enable)
- pdata->chip_enable(kim_gdata);
+ /*if (pdata->chip_enable)
+ pdata->chip_enable();*/
/* Configure BT nShutdown to HIGH state */
gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
@@ -546,8 +546,8 @@ long st_kim_stop(void *kim_data)
gpio_set_value(kim_gdata->nshutdown, GPIO_LOW);
/* platform specific disable */
- if (pdata->chip_disable)
- pdata->chip_disable(kim_gdata);
+ /*if (pdata->chip_disable)
+ pdata->chip_disable();*/
return err;
}
@@ -683,6 +683,9 @@ static int kim_probe(struct platform_device *pdev)
struct kim_data_s *kim_gdata;
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
+ if (pdata->set_power)
+ pdata->set_power(1);
+
if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) {
/* multiple devices could exist */
st_kim_devices[pdev->id] = pdev;
@@ -714,6 +717,8 @@ static int kim_probe(struct platform_device *pdev)
return status;
}
+ tegra_gpio_enable(kim_gdata->nshutdown);
+
/* Configure nShutdown GPIO as output=0 */
status = gpio_direction_output(kim_gdata->nshutdown, 0);
if (unlikely(status)) {
@@ -776,15 +781,24 @@ static int kim_remove(struct platform_device *pdev)
kfree(kim_gdata);
kim_gdata = NULL;
+
+ if (pdata->set_power)
+ pdata->set_power(0);
+
return 0;
}
int kim_suspend(struct platform_device *pdev, pm_message_t state)
{
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
+ int ret;
- if (pdata->suspend)
- return pdata->suspend(pdev, state);
+ if (pdata->suspend) {
+ ret = pdata->suspend(pdev, state);
+ if (pdata->set_power)
+ pdata->set_power(0);
+ return ret;
+ }
return -EOPNOTSUPP;
}
@@ -793,8 +807,11 @@ int kim_resume(struct platform_device *pdev)
{
struct ti_st_plat_data *pdata = pdev->dev.platform_data;
- if (pdata->resume)
+ if (pdata->resume) {
+ if (pdata->set_power)
+ pdata->set_power(1);
return pdata->resume(pdev);
+ }
return -EOPNOTSUPP;
}
diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c
index 1ff460a8e9c7..c2908e04ecfa 100644
--- a/drivers/misc/ti-st/st_ll.c
+++ b/drivers/misc/ti-st/st_ll.c
@@ -54,33 +54,40 @@ static void ll_device_want_to_sleep(struct st_data_s *st_data)
/* communicate to platform about chip asleep */
kim_data = st_data->kim_data;
pdata = kim_data->kim_pdev->dev.platform_data;
- if (pdata->chip_asleep)
- pdata->chip_asleep(NULL);
+ /*if (pdata->chip_asleep)
+ pdata->chip_asleep();*/
}
static void ll_device_want_to_wakeup(struct st_data_s *st_data)
{
- struct kim_data_s *kim_data;
- struct ti_st_plat_data *pdata;
+ struct kim_data_s *kim_data = st_data->kim_data;
+ struct ti_st_plat_data *pdata = kim_data->kim_pdev->dev.platform_data;
/* diff actions in diff states */
switch (st_data->ll_state) {
case ST_LL_ASLEEP:
+ /* communicate to platform about chip wakeup */
+ /*if (pdata->chip_awake)
+ pdata->chip_awake();*/
+
send_ll_cmd(st_data, LL_WAKE_UP_ACK); /* send wake_ack */
break;
case ST_LL_ASLEEP_TO_AWAKE:
/* duplicate wake_ind */
- pr_err("duplicate wake_ind while waiting for Wake ack");
+ pr_debug("duplicate wake_ind while waiting for Wake ack");
+ send_ll_cmd(st_data, LL_WAKE_UP_ACK); /* send wake_ack */
break;
case ST_LL_AWAKE:
/* duplicate wake_ind */
- pr_err("duplicate wake_ind already AWAKE");
+ pr_debug("duplicate wake_ind already AWAKE");
+ send_ll_cmd(st_data, LL_WAKE_UP_ACK); /* send wake_ack */
break;
case ST_LL_AWAKE_TO_ASLEEP:
/* duplicate wake_ind */
pr_err("duplicate wake_ind");
break;
}
+
/* update state */
st_data->ll_state = ST_LL_AWAKE;
@@ -98,6 +105,12 @@ static void ll_device_want_to_wakeup(struct st_data_s *st_data)
* enable ST LL */
void st_ll_enable(struct st_data_s *ll)
{
+ struct kim_data_s *kim_data = ll->kim_data;
+ struct ti_st_plat_data *pdata = kim_data->kim_pdev->dev.platform_data;
+ /* communicate to platform about chip enable */
+ /*if (pdata->chip_enable)
+ pdata->chip_enable();*/
+
ll->ll_state = ST_LL_AWAKE;
}
@@ -105,13 +118,26 @@ void st_ll_enable(struct st_data_s *ll)
* disable ST LL */
void st_ll_disable(struct st_data_s *ll)
{
+ struct kim_data_s *kim_data = ll->kim_data;
+ struct ti_st_plat_data *pdata = kim_data->kim_pdev->dev.platform_data;
+ /* communicate to platform about chip disable */
+ /*if (pdata->chip_disable)
+ pdata->chip_disable();*/
+
ll->ll_state = ST_LL_INVALID;
}
/* called when ST Core wants to update the state */
void st_ll_wakeup(struct st_data_s *ll)
{
+ struct kim_data_s *kim_data = ll->kim_data;
+ struct ti_st_plat_data *pdata = kim_data->kim_pdev->dev.platform_data;
+
if (likely(ll->ll_state != ST_LL_AWAKE)) {
+ /* communicate to platform about chip wakeup */
+ /*if (pdata->chip_awake)
+ pdata->chip_awake();*/
+
send_ll_cmd(ll, LL_WAKE_UP_IND); /* WAKE_IND */
ll->ll_state = ST_LL_ASLEEP_TO_AWAKE;
} else {
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 92bd3737c4b0..cb2a9d4d4515 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -542,6 +542,8 @@ static int sd_set_bus_speed_mode(struct mmc_card *card, u8 *status)
mmc_hostname(card->host));
else {
mmc_set_timing(card->host, timing);
+ if (timing == MMC_TIMING_UHS_DDR50)
+ mmc_card_set_ddr_mode(card);
mmc_set_clock(card->host, card->sw_caps.uhs_max_dtr);
}
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 5b6b2ddd9ef7..a0b5e4394514 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -1,6 +1,8 @@
/*
* Copyright (C) 2010 Google, Inc.
*
+ * Copyright (c) 2012, NVIDIA CORPORATION. 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.
@@ -37,7 +39,9 @@
#define SDHCI_VENDOR_CLOCK_CNTRL_TAP_VALUE_SHIFT 16
#define SDHCI_VENDOR_MISC_CNTRL 0x120
-#define SDHCI_VENDOR_MISC_CNTRL_SDMMC_SPARE0_ENABLE_SD_3_0 0x20
+#define SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR104_SUPPORT 0x8
+#define SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR50_SUPPORT 0x10
+#define SDHCI_VENDOR_MISC_CNTRL_ENABLE_SD_3_0 0x20
#define SDMMC_AUTO_CAL_CONFIG 0x1E4
#define SDMMC_AUTO_CAL_CONFIG_AUTO_CAL_ENABLE 0x20000000
@@ -70,13 +74,17 @@ struct tegra_sdhci_hw_ops{
void (*sdhost_init)(struct sdhci_host *sdhci);
};
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
static struct tegra_sdhci_hw_ops tegra_2x_sdhci_ops = {
};
+#endif
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
static struct tegra_sdhci_hw_ops tegra_3x_sdhci_ops = {
.set_card_clock = tegra_3x_sdhci_set_card_clock,
.sdhost_init = tegra3_sdhci_post_reset_init,
};
+#endif
struct tegra_sdhci_host {
bool clk_enabled;
@@ -93,6 +101,8 @@ struct tegra_sdhci_host {
/* max clk supported by the platform */
unsigned int max_clk_limit;
struct tegra_io_dpd *dpd;
+ bool card_present;
+ bool is_rail_enabled;
};
static u32 tegra_sdhci_readl(struct sdhci_host *host, int reg)
@@ -183,7 +193,9 @@ static void tegra3_sdhci_post_reset_init(struct sdhci_host *sdhci)
/* Enable SDHOST v3.0 support */
misc_ctrl = sdhci_readw(sdhci, SDHCI_VENDOR_MISC_CNTRL);
- misc_ctrl |= SDHCI_VENDOR_MISC_CNTRL_SDMMC_SPARE0_ENABLE_SD_3_0;
+ misc_ctrl |= SDHCI_VENDOR_MISC_CNTRL_ENABLE_SD_3_0 |
+ SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR104_SUPPORT |
+ SDHCI_VENDOR_MISC_CNTRL_ENABLE_SDR50_SUPPORT;
sdhci_writew(sdhci, misc_ctrl, SDHCI_VENDOR_MISC_CNTRL);
}
@@ -268,6 +280,32 @@ static void sdhci_status_notify_cb(int card_present, void *dev_id)
static irqreturn_t carddetect_irq(int irq, void *data)
{
struct sdhci_host *sdhost = (struct sdhci_host *)data;
+ struct sdhci_pltfm_host *pltfm_host = sdhci_priv(sdhost);
+ struct tegra_sdhci_host *tegra_host = pltfm_host->priv;
+ struct platform_device *pdev = to_platform_device(mmc_dev(sdhost->mmc));
+ struct tegra_sdhci_platform_data *plat;
+
+ plat = pdev->dev.platform_data;
+
+ tegra_host->card_present = (gpio_get_value(plat->cd_gpio) == 0);
+
+ if (tegra_host->card_present) {
+ if (!tegra_host->is_rail_enabled) {
+ if (tegra_host->vdd_slot_reg)
+ regulator_enable(tegra_host->vdd_slot_reg);
+ if (tegra_host->vdd_io_reg)
+ regulator_enable(tegra_host->vdd_io_reg);
+ tegra_host->is_rail_enabled = 1;
+ }
+ } else {
+ if (tegra_host->is_rail_enabled) {
+ if (tegra_host->vdd_io_reg)
+ regulator_disable(tegra_host->vdd_io_reg);
+ if (tegra_host->vdd_slot_reg)
+ regulator_disable(tegra_host->vdd_slot_reg);
+ tegra_host->is_rail_enabled = 0;
+ }
+ }
tasklet_schedule(&sdhost->card_tasklet);
return IRQ_HANDLED;
@@ -522,6 +560,16 @@ static int tegra_sdhci_suspend(struct sdhci_host *sdhci, pm_message_t state)
regulator_disable(tegra_host->vdd_slot_reg);
if (tegra_host->vdd_io_reg)
regulator_disable(tegra_host->vdd_io_reg);
+ if (tegra_host->card_present) {
+ if (tegra_host->is_rail_enabled) {
+ if (tegra_host->vdd_io_reg)
+ regulator_disable(tegra_host->vdd_io_reg);
+ if (tegra_host->vdd_slot_reg)
+ regulator_disable(tegra_host->vdd_slot_reg);
+ tegra_host->is_rail_enabled = 0;
+ }
+ }
+
return 0;
}
@@ -532,10 +580,17 @@ static int tegra_sdhci_resume(struct sdhci_host *sdhci)
unsigned long timeout;
/* Enable the power rails if any */
- if (tegra_host->vdd_io_reg)
- regulator_enable(tegra_host->vdd_io_reg);
- if (tegra_host->vdd_slot_reg)
- regulator_enable(tegra_host->vdd_slot_reg);
+ if (tegra_host->card_present) {
+ if (!tegra_host->is_rail_enabled) {
+ if (tegra_host->vdd_slot_reg)
+ regulator_enable(tegra_host->vdd_slot_reg);
+ if (tegra_host->vdd_io_reg) {
+ regulator_enable(tegra_host->vdd_io_reg);
+ tegra_sdhci_signal_voltage_switch(sdhci, MMC_SIGNAL_VOLTAGE_330);
+ }
+ tegra_host->is_rail_enabled = 1;
+ }
+ }
/* Setting the min identification clock of freq 400KHz */
tegra_sdhci_set_clock(sdhci, 400000);
@@ -654,7 +709,10 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
tegra_gpio_enable(plat->cd_gpio);
gpio_direction_input(plat->cd_gpio);
- rc = request_irq(gpio_to_irq(plat->cd_gpio), carddetect_irq,
+ tegra_host->card_present = (gpio_get_value(plat->cd_gpio) == 0);
+
+ rc = request_threaded_irq(gpio_to_irq(plat->cd_gpio), NULL,
+ carddetect_irq,
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING,
mmc_hostname(host->mmc), host);
@@ -712,8 +770,6 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
if (rc) {
dev_err(mmc_dev(host->mmc), "%s regulator_set_voltage failed: %d",
"vddio_sdmmc", rc);
- } else {
- regulator_enable(tegra_host->vdd_io_reg);
}
}
@@ -723,7 +779,13 @@ static int __devinit sdhci_tegra_probe(struct platform_device *pdev)
"vddio_sd_slot", PTR_ERR(tegra_host->vdd_slot_reg));
tegra_host->vdd_slot_reg = NULL;
} else {
- regulator_enable(tegra_host->vdd_slot_reg);
+ if (tegra_host->card_present) {
+ if (tegra_host->vdd_slot_reg)
+ regulator_enable(tegra_host->vdd_slot_reg);
+ if (tegra_host->vdd_io_reg)
+ regulator_enable(tegra_host->vdd_io_reg);
+ tegra_host->is_rail_enabled = 1;
+ }
}
}
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 417fdd1721f2..b1dd628ac648 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -1632,6 +1632,14 @@ static int sdhci_execute_tuning(struct mmc_host *mmc)
disable_irq(host->irq);
spin_lock(&host->lock);
+ if ((host->quirks & SDHCI_QUIRK_NON_STANDARD_TUNING) &&
+ host->ops->execute_freq_tuning) {
+ err = host->ops->execute_freq_tuning(host);
+ spin_unlock(&host->lock);
+ enable_irq(host->irq);
+ return err;
+ }
+
ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
/*
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 1c12419f9d64..bdf4b1055462 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -277,6 +277,7 @@ struct sdhci_ops {
int (*resume)(struct sdhci_host *host);
int (*switch_signal_voltage)(struct sdhci_host *host,
unsigned int signal_voltage);
+ int (*execute_freq_tuning)(struct sdhci_host *sdhci);
};
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index ca385697446e..bff8d4671ad6 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -215,7 +215,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
mutex_lock(&dev->lock);
- if (dev->open++)
+ if (dev->open)
goto unlock;
kref_get(&dev->ref);
@@ -235,6 +235,7 @@ static int blktrans_open(struct block_device *bdev, fmode_t mode)
goto error_release;
unlock:
+ dev->open++;
mutex_unlock(&dev->lock);
blktrans_dev_put(dev);
return ret;
diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c
index e3e40f440323..43130e8aceac 100644
--- a/drivers/mtd/mtdoops.c
+++ b/drivers/mtd/mtdoops.c
@@ -253,6 +253,9 @@ static void find_next_position(struct mtdoops_context *cxt)
size_t retlen;
for (page = 0; page < cxt->oops_pages; page++) {
+ if (mtd->block_isbad &&
+ mtd->block_isbad(mtd, page * record_size))
+ continue;
/* Assume the page is used */
mark_page_used(cxt, page);
ret = mtd->read(mtd, page * record_size, MTDOOPS_HEADER_SIZE,
@@ -369,7 +372,7 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
/* oops_page_used is a bit field */
cxt->oops_page_used = vmalloc(DIV_ROUND_UP(mtdoops_pages,
- BITS_PER_LONG));
+ BITS_PER_LONG) * sizeof(unsigned long));
if (!cxt->oops_page_used) {
printk(KERN_ERR "mtdoops: could not allocate page array\n");
return;
diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c
index 531625fc9259..129bad2e4080 100644
--- a/drivers/mtd/tests/mtd_stresstest.c
+++ b/drivers/mtd/tests/mtd_stresstest.c
@@ -277,6 +277,12 @@ static int __init mtd_stresstest_init(void)
(unsigned long long)mtd->size, mtd->erasesize,
pgsize, ebcnt, pgcnt, mtd->oobsize);
+ if (ebcnt < 2) {
+ printk(PRINT_PREF "error: need at least 2 eraseblocks\n");
+ err = -ENOSPC;
+ goto out_put_mtd;
+ }
+
/* Read or write up 2 eraseblocks at a time */
bufsize = mtd->erasesize * 2;
@@ -315,6 +321,7 @@ out:
kfree(bbt);
vfree(writebuf);
vfree(readbuf);
+out_put_mtd:
put_mtd_device(mtd);
if (err)
printk(PRINT_PREF "error %d occurred\n", err);
diff --git a/drivers/mtd/ubi/eba.c b/drivers/mtd/ubi/eba.c
index 4be671815014..c696c9481c95 100644
--- a/drivers/mtd/ubi/eba.c
+++ b/drivers/mtd/ubi/eba.c
@@ -1028,12 +1028,14 @@ int ubi_eba_copy_leb(struct ubi_device *ubi, int from, int to,
* 'ubi_wl_put_peb()' function on the @ubi->move_mutex. In turn, we are
* holding @ubi->move_mutex and go sleep on the LEB lock. So, if the
* LEB is already locked, we just do not move it and return
- * %MOVE_CANCEL_RACE, which means that UBI will re-try, but later.
+ * %MOVE_RETRY. Note, we do not return %MOVE_CANCEL_RACE here because
+ * we do not know the reasons of the contention - it may be just a
+ * normal I/O on this LEB, so we want to re-try.
*/
err = leb_write_trylock(ubi, vol_id, lnum);
if (err) {
dbg_wl("contention on LEB %d:%d, cancel", vol_id, lnum);
- return MOVE_CANCEL_RACE;
+ return MOVE_RETRY;
}
/*
diff --git a/drivers/mtd/ubi/ubi.h b/drivers/mtd/ubi/ubi.h
index dc64c767fd21..d51d75d34446 100644
--- a/drivers/mtd/ubi/ubi.h
+++ b/drivers/mtd/ubi/ubi.h
@@ -120,6 +120,7 @@ enum {
* PEB
* MOVE_CANCEL_BITFLIPS: canceled because a bit-flip was detected in the
* target PEB
+ * MOVE_RETRY: retry scrubbing the PEB
*/
enum {
MOVE_CANCEL_RACE = 1,
@@ -127,6 +128,7 @@ enum {
MOVE_TARGET_RD_ERR,
MOVE_TARGET_WR_ERR,
MOVE_CANCEL_BITFLIPS,
+ MOVE_RETRY,
};
/**
diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c
index 42c684cf3688..0696e36b0539 100644
--- a/drivers/mtd/ubi/wl.c
+++ b/drivers/mtd/ubi/wl.c
@@ -795,7 +795,10 @@ static int wear_leveling_worker(struct ubi_device *ubi, struct ubi_work *wrk,
protect = 1;
goto out_not_moved;
}
-
+ if (err == MOVE_RETRY) {
+ scrubbing = 1;
+ goto out_not_moved;
+ }
if (err == MOVE_CANCEL_BITFLIPS || err == MOVE_TARGET_WR_ERR ||
err == MOVE_TARGET_RD_ERR) {
/*
@@ -1049,7 +1052,6 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
ubi_err("failed to erase PEB %d, error %d", pnum, err);
kfree(wl_wrk);
- kmem_cache_free(ubi_wl_entry_slab, e);
if (err == -EINTR || err == -ENOMEM || err == -EAGAIN ||
err == -EBUSY) {
@@ -1062,14 +1064,16 @@ static int erase_worker(struct ubi_device *ubi, struct ubi_work *wl_wrk,
goto out_ro;
}
return err;
- } else if (err != -EIO) {
+ }
+
+ kmem_cache_free(ubi_wl_entry_slab, e);
+ if (err != -EIO)
/*
* If this is not %-EIO, we have no idea what to do. Scheduling
* this physical eraseblock for erasure again would cause
* errors again and again. Well, lets switch to R/O mode.
*/
goto out_ro;
- }
/* It is %-EIO, the PEB went bad */
diff --git a/drivers/net/usb/raw_ip_net.c b/drivers/net/usb/raw_ip_net.c
index d480a1ff2ff8..f2e1c1dd6250 100644
--- a/drivers/net/usb/raw_ip_net.c
+++ b/drivers/net/usb/raw_ip_net.c
@@ -82,6 +82,9 @@ struct baseband_usb {
struct urb *rx_urb;
/* currently active tx urb */
struct urb *tx_urb;
+ struct usb_anchor tx_urb_deferred;
+ struct workqueue_struct *tx_workqueue;
+ struct work_struct tx_work;
} usb;
};
@@ -94,6 +97,10 @@ static struct usb_interface *g_usb_interface[MAX_INTFS];
static int usb_net_raw_ip_rx_urb_submit(struct baseband_usb *usb);
static void usb_net_raw_ip_rx_urb_comp(struct urb *urb);
+
+static int usb_net_raw_ip_tx_urb_submit(struct baseband_usb *usb,
+ struct sk_buff *skb);
+static void usb_net_raw_ip_tx_urb_work(struct work_struct *work);
static void usb_net_raw_ip_tx_urb_comp(struct urb *urb);
static int baseband_usb_driver_probe(struct usb_interface *intf,
@@ -134,7 +141,24 @@ static int baseband_usb_driver_probe(struct usb_interface *intf,
static void baseband_usb_driver_disconnect(struct usb_interface *intf)
{
+ int i;
+
pr_debug("%s intf %p\n", __func__, intf);
+
+ for (i = 0; i < max_intfs; i++) {
+ pr_debug("[%d]\n", i);
+ if (!baseband_usb_net[i])
+ continue;
+ if (baseband_usb_net[i]->usb.interface != intf) {
+ pr_debug("%p != %p\n",
+ baseband_usb_net[i]->usb.interface, intf);
+ continue;
+ }
+ /* mark interface as disconnected */
+ baseband_usb_net[i]->usb.interface
+ = (struct usb_interface *) 0;
+ }
+
}
#ifdef CONFIG_PM
@@ -399,10 +423,8 @@ static int baseband_usb_netdev_stop(struct net_device *dev)
static netdev_tx_t baseband_usb_netdev_start_xmit(
struct sk_buff *skb, struct net_device *dev)
{
- int i = 0;
- struct baseband_usb *usb = baseband_usb_net[i];
- struct urb *urb;
- unsigned char *buf;
+ int i;
+ struct baseband_usb *usb;
int err;
pr_debug("baseband_usb_netdev_start_xmit\n");
@@ -412,63 +434,29 @@ static netdev_tx_t baseband_usb_netdev_start_xmit(
pr_err("no skb\n");
return -EINVAL;
}
-
- /* allocate urb */
- urb = usb_alloc_urb(0, GFP_ATOMIC);
- if (!urb) {
- pr_err("usb_alloc_urb() failed\n");
- kfree_skb(skb);
- return -ENOMEM;
- }
- buf = kzalloc(skb->len - 14, GFP_ATOMIC);
- if (!buf) {
- pr_err("usb buffer kzalloc() failed\n");
- usb_free_urb(urb);
- kfree_skb(skb);
- return -ENOMEM;
- }
- err = skb_copy_bits(skb, 14, buf, skb->len - 14);
- if (err < 0) {
- pr_err("skb_copy_bits() failed - %d\n", err);
- kfree(buf);
- usb_free_urb(urb);
- kfree_skb(skb);
- return err;
+ if (!dev) {
+ pr_err("no net dev\n");
+ return -EINVAL;
}
- usb_fill_bulk_urb(urb, usb->usb.device, usb->usb.pipe.bulk.out,
- buf, skb->len - 14,
- usb_net_raw_ip_tx_urb_comp,
- usb);
- urb->transfer_flags = URB_ZERO_PACKET;
- /* autoresume before tx */
- err = usb_autopm_get_interface_async(usb->usb.interface);
- if (err < 0) {
- pr_err("%s: usb_autopm_get_interface(%p) failed %d\n",
- __func__, usb->usb.interface, err);
- kfree(urb->transfer_buffer);
- usb_free_urb(urb);
- kfree_skb(skb);
- return err;
+ /* find index of network device which is transmitting */
+ for (i = 0; i < max_intfs; i++) {
+ if (usb_net_raw_ip_dev[i] == dev)
+ break;
+ }
+ if (i >= max_intfs) {
+ pr_err("unknown net dev %p\n", dev);
+ return -EINVAL;
}
+ usb = baseband_usb_net[i];
/* submit tx urb */
- usb_mark_last_busy(usb->usb.device);
- usb->usb.tx_urb = urb;
- err = usb_submit_urb(urb, GFP_ATOMIC);
+ err = usb_net_raw_ip_tx_urb_submit(usb, skb);
if (err < 0) {
- pr_err("usb_submit_urb() failed - err %d\n", err);
- usb_autopm_put_interface_async(usb->usb.interface);
- usb->usb.tx_urb = (struct urb *) 0;
- kfree(urb->transfer_buffer);
- usb_free_urb(urb);
- kfree_skb(skb);
- return err;
+ pr_err("tx urb submit error\n");
+ return -EINVAL;
}
- /* free skb */
- consume_skb(skb);
-
return NETDEV_TX_OK;
}
@@ -489,8 +477,16 @@ static int usb_net_raw_ip_rx_urb_submit(struct baseband_usb *usb)
pr_debug("usb_net_raw_ip_rx_urb_submit { usb %p\n", usb);
/* check input */
+ if (!usb) {
+ pr_err("%s: !usb\n", __func__);
+ return -EINVAL;
+ }
if (usb->usb.rx_urb) {
pr_err("previous urb still active\n");
+ return -EBUSY;
+ }
+ if (!usb->usb.interface) {
+ pr_err("usb interface disconnected - not submitting rx urb\n");
return -1;
}
@@ -558,6 +554,10 @@ static void usb_net_raw_ip_rx_urb_comp(struct urb *urb)
pr_info("rx urb killed\n");
return;
}
+ if (urb->status == -EPROTO) {
+ pr_info("rx urb %p -EPROTO \n", urb);
+ return;
+ }
if (urb->status) {
pr_info("rx urb status %d\n", urb->status);
}
@@ -618,6 +618,106 @@ static void usb_net_raw_ip_rx_urb_comp(struct urb *urb)
pr_debug("usb_net_raw_ip_rx_urb_comp }\n");
}
+static int usb_net_raw_ip_tx_urb_submit(struct baseband_usb *usb,
+ struct sk_buff *skb)
+{
+ struct urb *urb;
+ unsigned char *buf;
+ int err;
+
+ pr_debug("usb_net_raw_ip_tx_urb_submit {\n");
+
+ /* check input */
+ if (!usb) {
+ pr_err("%s: !usb\n", __func__);
+ return -EINVAL;
+ }
+ if (!skb) {
+ pr_err("%s: !skb\n", __func__);
+ return -EINVAL;
+ }
+
+ /* allocate urb */
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ pr_err("usb_alloc_urb() failed\n");
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+ buf = kzalloc(skb->len - 14, GFP_ATOMIC);
+ if (!buf) {
+ pr_err("usb buffer kzalloc() failed\n");
+ usb_free_urb(urb);
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+ err = skb_copy_bits(skb, 14, buf, skb->len - 14);
+ if (err < 0) {
+ pr_err("skb_copy_bits() failed - %d\n", err);
+ kfree(buf);
+ usb_free_urb(urb);
+ kfree_skb(skb);
+ return err;
+ }
+ usb_fill_bulk_urb(urb, usb->usb.device, usb->usb.pipe.bulk.out,
+ buf, skb->len - 14,
+ usb_net_raw_ip_tx_urb_comp,
+ usb);
+ urb->transfer_flags = URB_ZERO_PACKET;
+
+ /* queue tx urb work */
+ usb_anchor_urb(urb, &usb->usb.tx_urb_deferred);
+ queue_work(usb->usb.tx_workqueue, &usb->usb.tx_work);
+
+ /* free skb */
+ consume_skb(skb);
+
+ pr_debug("usb_net_raw_ip_tx_urb_submit }\n");
+ return 0;
+}
+
+static void usb_net_raw_ip_tx_urb_work(struct work_struct *work)
+{
+ struct baseband_usb *usb
+ = container_of(work, struct baseband_usb, usb.tx_work);
+ struct urb *urb;
+ int err;
+
+ pr_debug("usb_net_raw_ip_tx_urb_work {\n");
+
+ /* check if tx urb(s) queued */
+ if (!usb->usb.tx_urb && usb_anchor_empty(&usb->usb.tx_urb_deferred)) {
+ pr_debug("%s: nothing to do!\n", __func__);
+ return;
+ }
+
+ /* submit queued tx urb(s) */
+ while ((urb = usb_get_from_anchor(&usb->usb.tx_urb_deferred))
+ != (struct urb *) 0) {
+ /* autoresume before tx */
+ usb_mark_last_busy(usb->usb.device);
+ err = usb_autopm_get_interface(usb->usb.interface);
+ if (err < 0) {
+ pr_err("%s: usb_autopm_get_interface(%p) failed %d\n",
+ __func__, usb->usb.interface, err);
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ continue;
+ }
+ /* submit tx urb */
+ err = usb_submit_urb(urb, GFP_ATOMIC);
+ if (err < 0) {
+ pr_err("usb_submit_urb() failed - err %d\n", err);
+ usb_autopm_put_interface(usb->usb.interface);
+ kfree(urb->transfer_buffer);
+ usb_free_urb(urb);
+ continue;
+ }
+ }
+
+ pr_debug("usb_net_raw_ip_tx_urb_work }\n");
+}
+
static void usb_net_raw_ip_tx_urb_comp(struct urb *urb)
{
struct baseband_usb *usb = (struct baseband_usb *) urb->context;
@@ -630,10 +730,9 @@ static void usb_net_raw_ip_tx_urb_comp(struct urb *urb)
urb->transfer_buffer = (void *) 0;
}
usb_free_urb(urb);
- usb->usb.tx_urb = (struct urb *) 0;
/* autosuspend after tx completed */
- usb_autopm_put_interface_async(usb->usb.interface);
+ usb_autopm_put_interface(usb->usb.interface);
pr_debug("usb_net_raw_ip_tx_urb_comp }\n");
}
@@ -642,6 +741,7 @@ static int usb_net_raw_ip_init(void)
{
int i;
int err;
+ char name[32];
pr_debug("usb_net_raw_ip_init {\n");
@@ -682,6 +782,19 @@ static int usb_net_raw_ip_init(void)
pr_err("submit rx failed - err %d\n", err);
goto error_exit;
}
+ /* start usb tx */
+ init_usb_anchor(&baseband_usb_net[i]->usb.tx_urb_deferred);
+ sprintf(name, "usb_net_raw_ip_tx_workqueue-%p-%d",
+ baseband_usb_net[i],
+ baseband_usb_net[i]->baseband_index);
+ baseband_usb_net[i]->usb.tx_workqueue
+ = create_singlethread_workqueue(name);
+ if (!baseband_usb_net[i]->usb.tx_workqueue) {
+ pr_err("cannot create workqueue\n");
+ goto error_exit;
+ }
+ INIT_WORK(&baseband_usb_net[i]->usb.tx_work,
+ usb_net_raw_ip_tx_urb_work);
}
pr_debug("usb_net_raw_ip_init }\n");
@@ -698,6 +811,25 @@ error_exit:
}
/* close baseband usb */
if (baseband_usb_net[i]) {
+ /* stop usb tx */
+ if (baseband_usb_net[i]->usb.tx_workqueue) {
+ destroy_workqueue(baseband_usb_net[i]
+ ->usb.tx_workqueue);
+ baseband_usb_net[i]->usb.tx_workqueue
+ = (struct workqueue_struct *) 0;
+ }
+ if (baseband_usb_net[i]->usb.tx_urb) {
+ usb_kill_urb(baseband_usb_net[i]->usb.tx_urb);
+ baseband_usb_net[i]->usb.tx_urb
+ = (struct urb *) 0;
+ }
+ /* stop usb rx */
+ if (baseband_usb_net[i]->usb.rx_urb) {
+ usb_kill_urb(baseband_usb_net[i]->usb.rx_urb);
+ baseband_usb_net[i]->usb.rx_urb
+ = (struct urb *) 0;
+ }
+ /* close usb */
baseband_usb_close(baseband_usb_net[i]);
baseband_usb_net[i] = (struct baseband_usb *) 0;
}
@@ -722,6 +854,25 @@ static void usb_net_raw_ip_exit(void)
}
/* close baseband usb */
if (baseband_usb_net[i]) {
+ /* stop usb tx */
+ if (baseband_usb_net[i]->usb.tx_workqueue) {
+ destroy_workqueue(baseband_usb_net[i]
+ ->usb.tx_workqueue);
+ baseband_usb_net[i]->usb.tx_workqueue
+ = (struct workqueue_struct *) 0;
+ }
+ if (baseband_usb_net[i]->usb.tx_urb) {
+ usb_kill_urb(baseband_usb_net[i]->usb.tx_urb);
+ baseband_usb_net[i]->usb.tx_urb
+ = (struct urb *) 0;
+ }
+ /* stop usb rx */
+ if (baseband_usb_net[i]->usb.rx_urb) {
+ usb_kill_urb(baseband_usb_net[i]->usb.rx_urb);
+ baseband_usb_net[i]->usb.rx_urb
+ = (struct urb *) 0;
+ }
+ /* close usb */
baseband_usb_close(baseband_usb_net[i]);
baseband_usb_net[i] = (struct baseband_usb *) 0;
}
diff --git a/drivers/net/wireless/rtlwifi/rtl8192se/fw.c b/drivers/net/wireless/rtlwifi/rtl8192se/fw.c
index 6f91a148c222..3fda6b1dcf46 100644
--- a/drivers/net/wireless/rtlwifi/rtl8192se/fw.c
+++ b/drivers/net/wireless/rtlwifi/rtl8192se/fw.c
@@ -196,6 +196,8 @@ static bool _rtl92s_firmware_downloadcode(struct ieee80211_hw *hw,
/* Allocate skb buffer to contain firmware */
/* info and tx descriptor info. */
skb = dev_alloc_skb(frag_length);
+ if (!skb)
+ return false;
skb_reserve(skb, extra_descoffset);
seg_ptr = (u8 *)skb_put(skb, (u32)(frag_length -
extra_descoffset));
@@ -573,6 +575,8 @@ static bool _rtl92s_firmware_set_h2c_cmd(struct ieee80211_hw *hw, u8 h2c_cmd,
len = _rtl92s_get_h2c_cmdlen(MAX_TRANSMIT_BUFFER_SIZE, 1, &cmd_len);
skb = dev_alloc_skb(len);
+ if (!skb)
+ return false;
cb_desc = (struct rtl_tcb_desc *)(skb->cb);
cb_desc->queue_index = TXCMD_QUEUE;
cb_desc->cmd_or_init = DESC_PACKET_TYPE_NORMAL;
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 2f10328bf661..e1749825008d 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -869,5 +869,15 @@ EXPORT_SYMBOL(pci_msi_enabled);
void pci_msi_init_pci_dev(struct pci_dev *dev)
{
+ int pos;
INIT_LIST_HEAD(&dev->msi_list);
+
+ /* Disable the msi hardware to avoid screaming interrupts
+ * during boot. This is the power on reset default so
+ * usually this should be a noop.
+ */
+ pos = pci_find_capability(dev, PCI_CAP_ID_MSI);
+ if (pos)
+ msi_set_enable(dev, pos, 0);
+ msix_set_enable(dev, 0);
}
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index 0c595410e788..0d94eec00f4d 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -493,6 +493,8 @@ static void ideapad_backlight_notify_power(struct ideapad_private *priv)
unsigned long power;
struct backlight_device *blightdev = priv->blightdev;
+ if (!blightdev)
+ return;
if (read_ec_data(ideapad_handle, 0x18, &power))
return;
blightdev->props.power = power ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
diff --git a/drivers/pnp/quirks.c b/drivers/pnp/quirks.c
index dfbd5a6cc58b..258fef272ea7 100644
--- a/drivers/pnp/quirks.c
+++ b/drivers/pnp/quirks.c
@@ -295,6 +295,45 @@ static void quirk_system_pci_resources(struct pnp_dev *dev)
}
}
+#ifdef CONFIG_AMD_NB
+
+#include <asm/amd_nb.h>
+
+static void quirk_amd_mmconfig_area(struct pnp_dev *dev)
+{
+ resource_size_t start, end;
+ struct pnp_resource *pnp_res;
+ struct resource *res;
+ struct resource mmconfig_res, *mmconfig;
+
+ mmconfig = amd_get_mmconfig_range(&mmconfig_res);
+ if (!mmconfig)
+ return;
+
+ list_for_each_entry(pnp_res, &dev->resources, list) {
+ res = &pnp_res->res;
+ if (res->end < mmconfig->start || res->start > mmconfig->end ||
+ (res->start == mmconfig->start && res->end == mmconfig->end))
+ continue;
+
+ dev_info(&dev->dev, FW_BUG
+ "%pR covers only part of AMD MMCONFIG area %pR; adding more reservations\n",
+ res, mmconfig);
+ if (mmconfig->start < res->start) {
+ start = mmconfig->start;
+ end = res->start - 1;
+ pnp_add_mem_resource(dev, start, end, 0);
+ }
+ if (mmconfig->end > res->end) {
+ start = res->end + 1;
+ end = mmconfig->end;
+ pnp_add_mem_resource(dev, start, end, 0);
+ }
+ break;
+ }
+}
+#endif
+
/*
* PnP Quirks
* Cards or devices that need some tweaking due to incomplete resource info
@@ -322,6 +361,9 @@ static struct pnp_fixup pnp_fixups[] = {
/* PnP resources that might overlap PCI BARs */
{"PNP0c01", quirk_system_pci_resources},
{"PNP0c02", quirk_system_pci_resources},
+#ifdef CONFIG_AMD_NB
+ {"PNP0c01", quirk_amd_mmconfig_area},
+#endif
{""}
};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 09d21505c5fb..1e4b6808332d 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -335,6 +335,16 @@ config REGULATOR_TPS65910
help
This driver supports TPS65910 voltage regulator chips.
+config REGULATOR_TPS62360
+ tristate "TI TPS62360 Power Regulator"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ This driver supports TPS62360 voltage regulator chip. This
+ regulator is meant for processor core supply. This chip is
+ high-frequency synchronous step down dc-dc converter optimized
+ for battery-powered portable applications.
+
config REGULATOR_AAT2870
tristate "AnalogicTech AAT2870 Regulators"
depends on MFD_AAT2870_CORE
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 6f54bfd94d49..49f8b39ef4ce 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_REGULATOR_ISL6271A) += isl6271a-regulator.o
obj-$(CONFIG_REGULATOR_AB8500) += ab8500.o
obj-$(CONFIG_REGULATOR_DB8500_PRCMU) += db8500-prcmu.o
obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o
+obj-$(CONFIG_REGULATOR_TPS62360) += tps62360-regulator.o
obj-$(CONFIG_REGULATOR_AAT2870) += aat2870-regulator.o
obj-$(CONFIG_REGULATOR_GPIO_SWITCH) += gpio-switch-regulator.o
obj-$(CONFIG_REGULATOR_FAN53555) += fan53555-regulator.o
diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c
new file mode 100644
index 000000000000..7eaf08275376
--- /dev/null
+++ b/drivers/regulator/tps62360-regulator.c
@@ -0,0 +1,472 @@
+/*
+ * tps62360.c -- TI tps62360
+ *
+ * Driver for processor core supply tps62360 and tps62361B
+ *
+ * Copyright (c) 2012, NVIDIA Corporation.
+ *
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind,
+ * whether express or implied; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/tps62360.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+/* Register definitions */
+#define REG_VSET0 0
+#define REG_VSET1 1
+#define REG_VSET2 2
+#define REG_VSET3 3
+#define REG_CONTROL 4
+#define REG_TEMP 5
+#define REG_RAMPCTRL 6
+#define REG_CHIPID 8
+
+enum chips {TPS62360, TPS62361};
+
+#define TPS62360_BASE_VOLTAGE 770
+#define TPS62360_N_VOLTAGES 64
+
+#define TPS62361_BASE_VOLTAGE 500
+#define TPS62361_N_VOLTAGES 128
+
+/* tps 62360 chip information */
+struct tps62360_chip {
+ const char *name;
+ struct device *dev;
+ struct regulator_desc desc;
+ struct i2c_client *client;
+ struct regulator_dev *rdev;
+ struct regmap *regmap;
+ int chip_id;
+ int vsel0_gpio;
+ int vsel1_gpio;
+ int voltage_base;
+ u8 voltage_reg_mask;
+ bool en_internal_pulldn;
+ bool en_force_pwm;
+ bool en_discharge;
+ bool valid_gpios;
+ int lru_index[4];
+ int curr_vset_vsel[4];
+ int curr_vset_id;
+};
+
+/*
+ * find_voltage_set_register: Find new voltage configuration register
+ * (VSET) id.
+ * The finding of the new VSET register will be based on the LRU mechanism.
+ * Each VSET register will have different voltage configured . This
+ * Function will look if any of the VSET register have requested voltage set
+ * or not.
+ * - If it is already there then it will make that register as most
+ * recently used and return as found so that caller need not to set
+ * the VSET register but need to set the proper gpios to select this
+ * VSET register.
+ * - If requested voltage is not found then it will use the least
+ * recently mechanism to get new VSET register for new configuration
+ * and will return not_found so that caller need to set new VSET
+ * register and then gpios (both).
+ */
+static bool find_voltage_set_register(struct tps62360_chip *tps,
+ int req_vsel, int *vset_reg_id)
+{
+ int i;
+ bool found = false;
+ int new_vset_reg = tps->lru_index[3];
+ int found_index = 3;
+ for (i = 0; i < 4; ++i) {
+ if (tps->curr_vset_vsel[tps->lru_index[i]] == req_vsel) {
+ new_vset_reg = tps->lru_index[i];
+ found_index = i;
+ found = true;
+ goto update_lru_index;
+ }
+ }
+
+update_lru_index:
+ for (i = found_index; i > 0; i--)
+ tps->lru_index[i] = tps->lru_index[i - 1];
+
+ tps->lru_index[0] = new_vset_reg;
+ *vset_reg_id = new_vset_reg;
+ return found;
+}
+
+static int tps62360_dcdc_get_voltage(struct regulator_dev *dev)
+{
+ struct tps62360_chip *tps = rdev_get_drvdata(dev);
+ int vsel;
+ unsigned int data;
+ int ret;
+
+ ret = regmap_read(tps->regmap, REG_VSET0 + tps->curr_vset_id, &data);
+ if (ret < 0) {
+ dev_err(tps->dev, "%s: Error in reading register %d\n",
+ __func__, REG_VSET0 + tps->curr_vset_id);
+ return ret;
+ }
+ vsel = (int)data & tps->voltage_reg_mask;
+ return (tps->voltage_base + vsel * 10) * 1000;
+}
+
+static int tps62360_dcdc_set_voltage(struct regulator_dev *dev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct tps62360_chip *tps = rdev_get_drvdata(dev);
+ int vsel;
+ int ret;
+ bool found = false;
+ int new_vset_id = tps->curr_vset_id;
+
+ if (max_uV < min_uV)
+ return -EINVAL;
+
+ if (min_uV >
+ ((tps->voltage_base + (tps->desc.n_voltages - 1) * 10) * 1000))
+ return -EINVAL;
+
+ if (max_uV < tps->voltage_base * 1000)
+ return -EINVAL;
+
+ vsel = DIV_ROUND_UP(min_uV - (tps->voltage_base * 1000), 10000);
+ if (selector)
+ *selector = (vsel & tps->voltage_reg_mask);
+
+ /*
+ * If gpios are available to select the VSET register then least
+ * recently used register for new configuration.
+ */
+ if (tps->valid_gpios)
+ found = find_voltage_set_register(tps, vsel, &new_vset_id);
+
+ if (!found) {
+ ret = regmap_update_bits(tps->regmap, REG_VSET0 + new_vset_id,
+ tps->voltage_reg_mask, vsel);
+ if (ret < 0) {
+ dev_err(tps->dev, "%s: Error in updating register %d\n",
+ __func__, REG_VSET0 + new_vset_id);
+ return ret;
+ }
+ tps->curr_vset_id = new_vset_id;
+ tps->curr_vset_vsel[new_vset_id] = vsel;
+ }
+
+ /* Select proper VSET register vio gpios */
+ if (tps->valid_gpios) {
+ gpio_set_value_cansleep(tps->vsel0_gpio,
+ new_vset_id & 0x1);
+ gpio_set_value_cansleep(tps->vsel1_gpio,
+ (new_vset_id >> 1) & 0x1);
+ }
+ return 0;
+}
+
+static int tps62360_dcdc_list_voltage(struct regulator_dev *dev,
+ unsigned selector)
+{
+ struct tps62360_chip *tps = rdev_get_drvdata(dev);
+
+ if ((selector < 0) || (selector >= tps->desc.n_voltages))
+ return -EINVAL;
+ return (tps->voltage_base + selector * 10) * 1000;
+}
+
+static struct regulator_ops tps62360_dcdc_ops = {
+ .get_voltage = tps62360_dcdc_get_voltage,
+ .set_voltage = tps62360_dcdc_set_voltage,
+ .list_voltage = tps62360_dcdc_list_voltage,
+};
+
+static int tps62360_init_force_pwm(struct tps62360_chip *tps,
+ struct tps62360_regulator_platform_data *pdata,
+ int vset_id)
+{
+ unsigned int data;
+ int ret;
+ ret = regmap_read(tps->regmap, REG_VSET0 + vset_id, &data);
+ if (ret < 0) {
+ dev_err(tps->dev, "%s() fails in writing reg %d\n",
+ __func__, REG_VSET0 + vset_id);
+ return ret;
+ }
+ tps->curr_vset_vsel[vset_id] = data & tps->voltage_reg_mask;
+ if (pdata->en_force_pwm)
+ data |= BIT(7);
+ else
+ data &= ~BIT(7);
+ ret = regmap_write(tps->regmap, REG_VSET0 + vset_id, data);
+ if (ret < 0)
+ dev_err(tps->dev, "%s() fails in writing reg %d\n",
+ __func__, REG_VSET0 + vset_id);
+ return ret;
+}
+
+static int tps62360_init_dcdc(struct tps62360_chip *tps,
+ struct tps62360_regulator_platform_data *pdata)
+{
+ int ret;
+ int i;
+
+ /* Initailize internal pull up/down control */
+ if (tps->en_internal_pulldn)
+ ret = regmap_write(tps->regmap, REG_CONTROL, 0xE0);
+ else
+ ret = regmap_write(tps->regmap, REG_CONTROL, 0x0);
+ if (ret < 0) {
+ dev_err(tps->dev, "%s() fails in writing reg %d\n",
+ __func__, REG_CONTROL);
+ return ret;
+ }
+
+ /* Initailize force PWM mode */
+ if (tps->valid_gpios) {
+ for (i = 0; i < 4; ++i) {
+ ret = tps62360_init_force_pwm(tps, pdata, i);
+ if (ret < 0)
+ return ret;
+ }
+ } else {
+ ret = tps62360_init_force_pwm(tps, pdata, tps->curr_vset_id);
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Reset output discharge path to reduce power consumption */
+ ret = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), 0);
+ if (ret < 0)
+ dev_err(tps->dev, "%s() fails in updating reg %d\n",
+ __func__, REG_RAMPCTRL);
+ return ret;
+}
+
+static const struct regmap_config tps62360_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+static int __devinit tps62360_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct tps62360_regulator_platform_data *pdata;
+ struct regulator_dev *rdev;
+ struct tps62360_chip *tps;
+ int ret;
+ int i;
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->dev, "%s() Err: Platform data not found\n",
+ __func__);
+ return -EIO;
+ }
+
+ tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL);
+ if (!tps) {
+ dev_err(&client->dev, "%s() Err: Memory allocation fails\n",
+ __func__);
+ return -ENOMEM;
+ }
+
+ tps->en_force_pwm = pdata->en_force_pwm;
+ tps->en_discharge = pdata->en_discharge;
+ tps->en_internal_pulldn = pdata->en_internal_pulldn;
+ tps->vsel0_gpio = pdata->vsel0_gpio;
+ tps->vsel1_gpio = pdata->vsel1_gpio;
+ tps->client = client;
+ tps->dev = &client->dev;
+ tps->name = id->name;
+ tps->voltage_base = (id->driver_data == TPS62360) ?
+ TPS62360_BASE_VOLTAGE : TPS62361_BASE_VOLTAGE;
+ tps->voltage_reg_mask = (id->driver_data == TPS62360) ? 0x3F : 0x7F;
+
+ tps->desc.name = id->name;
+ tps->desc.id = 0;
+ tps->desc.n_voltages = (id->driver_data == TPS62360) ?
+ TPS62360_N_VOLTAGES : TPS62361_N_VOLTAGES;
+ tps->desc.ops = &tps62360_dcdc_ops;
+ tps->desc.type = REGULATOR_VOLTAGE;
+ tps->desc.owner = THIS_MODULE;
+ tps->regmap = regmap_init_i2c(client, &tps62360_regmap_config);
+ if (IS_ERR(tps->regmap)) {
+ ret = PTR_ERR(tps->regmap);
+ dev_err(&client->dev, "%s() Err: Failed to allocate register"
+ "map: %d\n", __func__, ret);
+ return ret;
+ }
+ i2c_set_clientdata(client, tps);
+
+ tps->curr_vset_id = (pdata->vsel1_def_state & 1) * 2 +
+ (pdata->vsel0_def_state & 1);
+ tps->lru_index[0] = tps->curr_vset_id;
+ tps->valid_gpios = false;
+
+ if (gpio_is_valid(tps->vsel0_gpio) && gpio_is_valid(tps->vsel1_gpio)) {
+ ret = gpio_request(tps->vsel0_gpio, "tps62360-vsel0");
+ if (ret) {
+ dev_err(&client->dev,
+ "Err: Could not obtain vsel0 GPIO %d: %d\n",
+ tps->vsel0_gpio, ret);
+ goto err_gpio0;
+ }
+ ret = gpio_direction_output(tps->vsel0_gpio,
+ pdata->vsel0_def_state);
+ if (ret) {
+ dev_err(&client->dev, "Err: Could not set direction of"
+ "vsel0 GPIO %d: %d\n", tps->vsel0_gpio, ret);
+ gpio_free(tps->vsel0_gpio);
+ goto err_gpio0;
+ }
+
+ ret = gpio_request(tps->vsel1_gpio, "tps62360-vsel1");
+ if (ret) {
+ dev_err(&client->dev,
+ "Err: Could not obtain vsel1 GPIO %d: %d\n",
+ tps->vsel1_gpio, ret);
+ goto err_gpio1;
+ }
+ ret = gpio_direction_output(tps->vsel1_gpio,
+ pdata->vsel1_def_state);
+ if (ret) {
+ dev_err(&client->dev, "Err: Could not set direction of"
+ "vsel1 GPIO %d: %d\n", tps->vsel1_gpio, ret);
+ gpio_free(tps->vsel1_gpio);
+ goto err_gpio1;
+ }
+ tps->valid_gpios = true;
+
+ /*
+ * Initialize the lru index with vset_reg id
+ * The index 0 will be most recently used and
+ * set with the tps->curr_vset_id */
+ for (i = 0; i < 4; ++i)
+ tps->lru_index[i] = i;
+ tps->lru_index[0] = tps->curr_vset_id;
+ tps->lru_index[tps->curr_vset_id] = 0;
+ }
+
+ ret = tps62360_init_dcdc(tps, pdata);
+ if (ret < 0) {
+ dev_err(tps->dev, "%s() Err: Init fails with = %d\n",
+ __func__, ret);
+ goto err_init;
+ }
+
+ /* Register the regulators */
+ rdev = regulator_register(&tps->desc, &client->dev,
+ &pdata->reg_init_data, tps);
+ if (IS_ERR(rdev)) {
+ dev_err(tps->dev, "%s() Err: Failed to register %s\n",
+ __func__, id->name);
+ ret = PTR_ERR(rdev);
+ goto err_init;
+ }
+
+ tps->rdev = rdev;
+ return 0;
+
+err_init:
+ if (gpio_is_valid(tps->vsel1_gpio))
+ gpio_free(tps->vsel1_gpio);
+err_gpio1:
+ if (gpio_is_valid(tps->vsel0_gpio))
+ gpio_free(tps->vsel0_gpio);
+err_gpio0:
+ regmap_exit(tps->regmap);
+ return ret;
+}
+
+/**
+ * tps62360_remove - tps62360 driver i2c remove handler
+ * @client: i2c driver client device structure
+ *
+ * Unregister TPS driver as an i2c client device driver
+ */
+static int __devexit tps62360_remove(struct i2c_client *client)
+{
+ struct tps62360_chip *tps = i2c_get_clientdata(client);
+
+ if (gpio_is_valid(tps->vsel1_gpio))
+ gpio_free(tps->vsel1_gpio);
+
+ if (gpio_is_valid(tps->vsel0_gpio))
+ gpio_free(tps->vsel0_gpio);
+
+ regulator_unregister(tps->rdev);
+ regmap_exit(tps->regmap);
+ return 0;
+}
+
+static void tps62360_shutdown(struct i2c_client *client)
+{
+ struct tps62360_chip *tps = i2c_get_clientdata(client);
+ int st;
+
+ if (!tps->en_discharge)
+ return;
+
+ /* Configure the output discharge path */
+ st = regmap_update_bits(tps->regmap, REG_RAMPCTRL, BIT(2), BIT(2));
+ if (st < 0)
+ dev_err(tps->dev, "%s() fails in updating reg %d\n",
+ __func__, REG_RAMPCTRL);
+}
+
+static const struct i2c_device_id tps62360_id[] = {
+ {.name = "tps62360", .driver_data = TPS62360},
+ {.name = "tps62361", .driver_data = TPS62361},
+ {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tps62360_id);
+
+static struct i2c_driver tps62360_i2c_driver = {
+ .driver = {
+ .name = "tps62360",
+ .owner = THIS_MODULE,
+ },
+ .probe = tps62360_probe,
+ .remove = __devexit_p(tps62360_remove),
+ .shutdown = tps62360_shutdown,
+ .id_table = tps62360_id,
+};
+
+static int __init tps62360_init(void)
+{
+ return i2c_add_driver(&tps62360_i2c_driver);
+}
+subsys_initcall(tps62360_init);
+
+static void __exit tps62360_cleanup(void)
+{
+ i2c_del_driver(&tps62360_i2c_driver);
+}
+module_exit(tps62360_cleanup);
+
+MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
+MODULE_DESCRIPTION("TPS62360 voltage regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/regulator/tps65910-regulator.c b/drivers/regulator/tps65910-regulator.c
index 66d2d60b436a..c9c58a2d8433 100644
--- a/drivers/regulator/tps65910-regulator.c
+++ b/drivers/regulator/tps65910-regulator.c
@@ -25,30 +25,6 @@
#include <linux/gpio.h>
#include <linux/mfd/tps65910.h>
-#define TPS65910_REG_VRTC 0
-#define TPS65910_REG_VIO 1
-#define TPS65910_REG_VDD1 2
-#define TPS65910_REG_VDD2 3
-#define TPS65910_REG_VDD3 4
-#define TPS65910_REG_VDIG1 5
-#define TPS65910_REG_VDIG2 6
-#define TPS65910_REG_VPLL 7
-#define TPS65910_REG_VDAC 8
-#define TPS65910_REG_VAUX1 9
-#define TPS65910_REG_VAUX2 10
-#define TPS65910_REG_VAUX33 11
-#define TPS65910_REG_VMMC 12
-
-#define TPS65911_REG_VDDCTRL 4
-#define TPS65911_REG_LDO1 5
-#define TPS65911_REG_LDO2 6
-#define TPS65911_REG_LDO3 7
-#define TPS65911_REG_LDO4 8
-#define TPS65911_REG_LDO5 9
-#define TPS65911_REG_LDO6 10
-#define TPS65911_REG_LDO7 11
-#define TPS65911_REG_LDO8 12
-
#define TPS65910_SUPPLY_STATE_ENABLED 0x1
/* supported VIO voltages in milivolts */
@@ -107,8 +83,8 @@ struct tps_info {
const char *name;
unsigned min_uV;
unsigned max_uV;
- u8 table_len;
- const u16 *table;
+ u8 n_voltages;
+ const u16 *voltage_table;
};
static struct tps_info tps65910_regs[] = {
@@ -119,8 +95,8 @@ static struct tps_info tps65910_regs[] = {
.name = "VIO",
.min_uV = 1500000,
.max_uV = 3300000,
- .table_len = ARRAY_SIZE(VIO_VSEL_table),
- .table = VIO_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VIO_VSEL_table),
+ .voltage_table = VIO_VSEL_table,
},
{
.name = "VDD1",
@@ -136,129 +112,143 @@ static struct tps_info tps65910_regs[] = {
.name = "VDD3",
.min_uV = 5000000,
.max_uV = 5000000,
- .table_len = ARRAY_SIZE(VDD3_VSEL_table),
- .table = VDD3_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VDD3_VSEL_table),
+ .voltage_table = VDD3_VSEL_table,
},
{
.name = "VDIG1",
.min_uV = 1200000,
.max_uV = 2700000,
- .table_len = ARRAY_SIZE(VDIG1_VSEL_table),
- .table = VDIG1_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VDIG1_VSEL_table),
+ .voltage_table = VDIG1_VSEL_table,
},
{
.name = "VDIG2",
.min_uV = 1000000,
.max_uV = 1800000,
- .table_len = ARRAY_SIZE(VDIG2_VSEL_table),
- .table = VDIG2_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VDIG2_VSEL_table),
+ .voltage_table = VDIG2_VSEL_table,
},
{
.name = "VPLL",
.min_uV = 1000000,
.max_uV = 2500000,
- .table_len = ARRAY_SIZE(VPLL_VSEL_table),
- .table = VPLL_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VPLL_VSEL_table),
+ .voltage_table = VPLL_VSEL_table,
},
{
.name = "VDAC",
.min_uV = 1800000,
.max_uV = 2850000,
- .table_len = ARRAY_SIZE(VDAC_VSEL_table),
- .table = VDAC_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VDAC_VSEL_table),
+ .voltage_table = VDAC_VSEL_table,
},
{
.name = "VAUX1",
.min_uV = 1800000,
.max_uV = 2850000,
- .table_len = ARRAY_SIZE(VAUX1_VSEL_table),
- .table = VAUX1_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VAUX1_VSEL_table),
+ .voltage_table = VAUX1_VSEL_table,
},
{
.name = "VAUX2",
.min_uV = 1800000,
.max_uV = 3300000,
- .table_len = ARRAY_SIZE(VAUX2_VSEL_table),
- .table = VAUX2_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VAUX2_VSEL_table),
+ .voltage_table = VAUX2_VSEL_table,
},
{
.name = "VAUX33",
.min_uV = 1800000,
.max_uV = 3300000,
- .table_len = ARRAY_SIZE(VAUX33_VSEL_table),
- .table = VAUX33_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VAUX33_VSEL_table),
+ .voltage_table = VAUX33_VSEL_table,
},
{
.name = "VMMC",
.min_uV = 1800000,
.max_uV = 3300000,
- .table_len = ARRAY_SIZE(VMMC_VSEL_table),
- .table = VMMC_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VMMC_VSEL_table),
+ .voltage_table = VMMC_VSEL_table,
},
};
static struct tps_info tps65911_regs[] = {
{
+ .name = "VRTC",
+ },
+ {
.name = "VIO",
.min_uV = 1500000,
.max_uV = 3300000,
- .table_len = ARRAY_SIZE(VIO_VSEL_table),
- .table = VIO_VSEL_table,
+ .n_voltages = ARRAY_SIZE(VIO_VSEL_table),
+ .voltage_table = VIO_VSEL_table,
},
{
.name = "VDD1",
.min_uV = 600000,
.max_uV = 4500000,
+ .n_voltages = 73,
},
{
.name = "VDD2",
.min_uV = 600000,
.max_uV = 4500000,
+ .n_voltages = 73,
},
{
.name = "VDDCTRL",
.min_uV = 600000,
.max_uV = 1400000,
+ .n_voltages = 65,
},
{
.name = "LDO1",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 47,
},
{
.name = "LDO2",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 47,
},
{
.name = "LDO3",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 24,
},
{
.name = "LDO4",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 47,
},
{
.name = "LDO5",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 24,
},
{
.name = "LDO6",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 24,
},
{
.name = "LDO7",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 24,
},
{
.name = "LDO8",
.min_uV = 1000000,
.max_uV = 3300000,
+ .n_voltages = 24,
},
};
@@ -596,7 +586,7 @@ static int tps65910_get_voltage(struct regulator_dev *dev)
return -EINVAL;
}
- voltage = pmic->info[id]->table[value] * 1000;
+ voltage = pmic->info[id]->voltage_table[value] * 1000;
return voltage;
}
@@ -646,7 +636,7 @@ static int tps65911_get_voltage(struct regulator_dev *dev)
step_mv = 100;
break;
case TPS65910_REG_VIO:
- return pmic->info[id]->table[value] * 1000;
+ return pmic->info[id]->voltage_table[value] * 1000;
break;
default:
return -EINVAL;
@@ -664,10 +654,10 @@ static int tps65910_set_voltage_dcdc(struct regulator_dev *dev,
switch (id) {
case TPS65910_REG_VDD1:
- dcdc_mult = (selector / VDD1_2_NUM_VOLTS) + 1;
+ dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1;
if (dcdc_mult == 1)
dcdc_mult--;
- vsel = (selector % VDD1_2_NUM_VOLTS) + 3;
+ vsel = (selector % VDD1_2_NUM_VOLT_FINE) + 3;
tps65910_modify_bits(pmic, TPS65910_VDD1,
(dcdc_mult << VDD1_VGAIN_SEL_SHIFT),
@@ -675,10 +665,10 @@ static int tps65910_set_voltage_dcdc(struct regulator_dev *dev,
tps65910_reg_write(pmic, TPS65910_VDD1_OP, vsel);
break;
case TPS65910_REG_VDD2:
- dcdc_mult = (selector / VDD1_2_NUM_VOLTS) + 1;
+ dcdc_mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1;
if (dcdc_mult == 1)
dcdc_mult--;
- vsel = (selector % VDD1_2_NUM_VOLTS) + 3;
+ vsel = (selector % VDD1_2_NUM_VOLT_FINE) + 3;
tps65910_modify_bits(pmic, TPS65910_VDD2,
(dcdc_mult << VDD2_VGAIN_SEL_SHIFT),
@@ -756,9 +746,9 @@ static int tps65910_list_voltage_dcdc(struct regulator_dev *dev,
switch (id) {
case TPS65910_REG_VDD1:
case TPS65910_REG_VDD2:
- mult = (selector / VDD1_2_NUM_VOLTS) + 1;
+ mult = (selector / VDD1_2_NUM_VOLT_FINE) + 1;
volt = VDD1_2_MIN_VOLT +
- (selector % VDD1_2_NUM_VOLTS) * VDD1_2_OFFSET;
+ (selector % VDD1_2_NUM_VOLT_FINE) * VDD1_2_OFFSET;
break;
case TPS65911_REG_VDDCTRL:
volt = VDDCTRL_MIN_VOLT + (selector * VDDCTRL_OFFSET);
@@ -780,10 +770,10 @@ static int tps65910_list_voltage(struct regulator_dev *dev,
if (id < TPS65910_REG_VIO || id > TPS65910_REG_VMMC)
return -EINVAL;
- if (selector >= pmic->info[id]->table_len)
+ if (selector >= pmic->info[id]->n_voltages)
return -EINVAL;
else
- voltage = pmic->info[id]->table[selector] * 1000;
+ voltage = pmic->info[id]->voltage_table[selector] * 1000;
return voltage;
}
@@ -819,7 +809,7 @@ static int tps65911_list_voltage(struct regulator_dev *dev, unsigned selector)
step_mv = 100;
break;
case TPS65910_REG_VIO:
- return pmic->info[id]->table[selector] * 1000;
+ return pmic->info[id]->voltage_table[selector] * 1000;
default:
return -EINVAL;
}
@@ -885,8 +875,6 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
if (!pmic_plat_data)
return -EINVAL;
- reg_data = pmic_plat_data->tps65910_pmic_init_data;
-
pmic = kzalloc(sizeof(*pmic), GFP_KERNEL);
if (!pmic)
return -ENOMEM;
@@ -937,16 +925,27 @@ static __devinit int tps65910_probe(struct platform_device *pdev)
goto err_free_info;
}
- for (i = 0; i < pmic->num_regulators; i++, info++, reg_data++) {
+ for (i = 0; i < pmic->num_regulators && i < TPS65910_NUM_REGS;
+ i++, info++) {
+
+ reg_data = pmic_plat_data->tps65910_pmic_init_data[i];
+
+ /* Regulator API handles empty constraints but not NULL
+ * constraints */
+ if (!reg_data)
+ continue;
+
/* Register the regulators */
pmic->info[i] = info;
pmic->desc[i].name = info->name;
pmic->desc[i].id = i;
- pmic->desc[i].n_voltages = info->table_len;
+ pmic->desc[i].n_voltages = info->n_voltages;
if (i == TPS65910_REG_VDD1 || i == TPS65910_REG_VDD2) {
pmic->desc[i].ops = &tps65910_ops_dcdc;
+ pmic->desc[i].n_voltages = VDD1_2_NUM_VOLT_FINE *
+ VDD1_2_NUM_VOLT_COARSE;
} else if (i == TPS65910_REG_VDD3) {
if (tps65910_chip_id(tps65910) == TPS65910)
pmic->desc[i].ops = &tps65910_ops_vdd3;
diff --git a/drivers/rtc/interface.c b/drivers/rtc/interface.c
index 44e91e598f8d..a86f3013747b 100644
--- a/drivers/rtc/interface.c
+++ b/drivers/rtc/interface.c
@@ -227,11 +227,11 @@ int __rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
alarm->time.tm_hour = now.tm_hour;
/* For simplicity, only support date rollover for now */
- if (alarm->time.tm_mday == -1) {
+ if (alarm->time.tm_mday < 1 || alarm->time.tm_mday > 31) {
alarm->time.tm_mday = now.tm_mday;
missing = day;
}
- if (alarm->time.tm_mon == -1) {
+ if ((unsigned)alarm->time.tm_mon >= 12) {
alarm->time.tm_mon = now.tm_mon;
if (missing == none)
missing = month;
diff --git a/drivers/scsi/mpt2sas/mpt2sas_base.c b/drivers/scsi/mpt2sas/mpt2sas_base.c
index 39e81cd567ae..10f16a306e54 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_base.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_base.c
@@ -66,6 +66,8 @@ static MPT_CALLBACK mpt_callbacks[MPT_MAX_CALLBACKS];
#define FAULT_POLLING_INTERVAL 1000 /* in milliseconds */
+#define MAX_HBA_QUEUE_DEPTH 30000
+#define MAX_CHAIN_DEPTH 100000
static int max_queue_depth = -1;
module_param(max_queue_depth, int, 0);
MODULE_PARM_DESC(max_queue_depth, " max controller queue depth ");
@@ -2098,8 +2100,6 @@ _base_release_memory_pools(struct MPT2SAS_ADAPTER *ioc)
}
if (ioc->chain_dma_pool)
pci_pool_destroy(ioc->chain_dma_pool);
- }
- if (ioc->chain_lookup) {
free_pages((ulong)ioc->chain_lookup, ioc->chain_pages);
ioc->chain_lookup = NULL;
}
@@ -2117,9 +2117,7 @@ static int
_base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
{
struct mpt2sas_facts *facts;
- u32 queue_size, queue_diff;
u16 max_sge_elements;
- u16 num_of_reply_frames;
u16 chains_needed_per_io;
u32 sz, total_sz;
u32 retry_sz;
@@ -2146,7 +2144,8 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
max_request_credit = (max_queue_depth < facts->RequestCredit)
? max_queue_depth : facts->RequestCredit;
else
- max_request_credit = facts->RequestCredit;
+ max_request_credit = min_t(u16, facts->RequestCredit,
+ MAX_HBA_QUEUE_DEPTH);
ioc->hba_queue_depth = max_request_credit;
ioc->hi_priority_depth = facts->HighPriorityCredit;
@@ -2187,50 +2186,25 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
}
ioc->chains_needed_per_io = chains_needed_per_io;
- /* reply free queue sizing - taking into account for events */
- num_of_reply_frames = ioc->hba_queue_depth + 32;
-
- /* number of replies frames can't be a multiple of 16 */
- /* decrease number of reply frames by 1 */
- if (!(num_of_reply_frames % 16))
- num_of_reply_frames--;
-
- /* calculate number of reply free queue entries
- * (must be multiple of 16)
- */
-
- /* (we know reply_free_queue_depth is not a multiple of 16) */
- queue_size = num_of_reply_frames;
- queue_size += 16 - (queue_size % 16);
- ioc->reply_free_queue_depth = queue_size;
-
- /* reply descriptor post queue sizing */
- /* this size should be the number of request frames + number of reply
- * frames
- */
-
- queue_size = ioc->hba_queue_depth + num_of_reply_frames + 1;
- /* round up to 16 byte boundary */
- if (queue_size % 16)
- queue_size += 16 - (queue_size % 16);
-
- /* check against IOC maximum reply post queue depth */
- if (queue_size > facts->MaxReplyDescriptorPostQueueDepth) {
- queue_diff = queue_size -
- facts->MaxReplyDescriptorPostQueueDepth;
+ /* reply free queue sizing - taking into account for 64 FW events */
+ ioc->reply_free_queue_depth = ioc->hba_queue_depth + 64;
- /* round queue_diff up to multiple of 16 */
- if (queue_diff % 16)
- queue_diff += 16 - (queue_diff % 16);
-
- /* adjust hba_queue_depth, reply_free_queue_depth,
- * and queue_size
- */
- ioc->hba_queue_depth -= (queue_diff / 2);
- ioc->reply_free_queue_depth -= (queue_diff / 2);
- queue_size = facts->MaxReplyDescriptorPostQueueDepth;
+ /* align the reply post queue on the next 16 count boundary */
+ if (!ioc->reply_free_queue_depth % 16)
+ ioc->reply_post_queue_depth = ioc->reply_free_queue_depth + 16;
+ else
+ ioc->reply_post_queue_depth = ioc->reply_free_queue_depth +
+ 32 - (ioc->reply_free_queue_depth % 16);
+ if (ioc->reply_post_queue_depth >
+ facts->MaxReplyDescriptorPostQueueDepth) {
+ ioc->reply_post_queue_depth = min_t(u16,
+ (facts->MaxReplyDescriptorPostQueueDepth -
+ (facts->MaxReplyDescriptorPostQueueDepth % 16)),
+ (ioc->hba_queue_depth - (ioc->hba_queue_depth % 16)));
+ ioc->reply_free_queue_depth = ioc->reply_post_queue_depth - 16;
+ ioc->hba_queue_depth = ioc->reply_free_queue_depth - 64;
}
- ioc->reply_post_queue_depth = queue_size;
+
dinitprintk(ioc, printk(MPT2SAS_INFO_FMT "scatter gather: "
"sge_in_main_msg(%d), sge_per_chain(%d), sge_per_io(%d), "
@@ -2316,15 +2290,12 @@ _base_allocate_memory_pools(struct MPT2SAS_ADAPTER *ioc, int sleep_flag)
"depth(%d)\n", ioc->name, ioc->request,
ioc->scsiio_depth));
- /* loop till the allocation succeeds */
- do {
- sz = ioc->chain_depth * sizeof(struct chain_tracker);
- ioc->chain_pages = get_order(sz);
- ioc->chain_lookup = (struct chain_tracker *)__get_free_pages(
- GFP_KERNEL, ioc->chain_pages);
- if (ioc->chain_lookup == NULL)
- ioc->chain_depth -= 100;
- } while (ioc->chain_lookup == NULL);
+ ioc->chain_depth = min_t(u32, ioc->chain_depth, MAX_CHAIN_DEPTH);
+ sz = ioc->chain_depth * sizeof(struct chain_tracker);
+ ioc->chain_pages = get_order(sz);
+
+ ioc->chain_lookup = (struct chain_tracker *)__get_free_pages(
+ GFP_KERNEL, ioc->chain_pages);
ioc->chain_dma_pool = pci_pool_create("chain pool", ioc->pdev,
ioc->request_sz, 16, 0);
if (!ioc->chain_dma_pool) {
diff --git a/drivers/scsi/mpt2sas/mpt2sas_scsih.c b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
index 7375124b92fa..011b8648ab13 100644
--- a/drivers/scsi/mpt2sas/mpt2sas_scsih.c
+++ b/drivers/scsi/mpt2sas/mpt2sas_scsih.c
@@ -978,8 +978,8 @@ _scsih_get_chain_buffer_tracker(struct MPT2SAS_ADAPTER *ioc, u16 smid)
spin_lock_irqsave(&ioc->scsi_lookup_lock, flags);
if (list_empty(&ioc->free_chain_list)) {
spin_unlock_irqrestore(&ioc->scsi_lookup_lock, flags);
- printk(MPT2SAS_WARN_FMT "chain buffers not available\n",
- ioc->name);
+ dfailprintk(ioc, printk(MPT2SAS_WARN_FMT "chain buffers not "
+ "available\n", ioc->name));
return NULL;
}
chain_req = list_entry(ioc->free_chain_list.next,
@@ -6564,6 +6564,7 @@ _scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid,
} else
sas_target_priv_data = NULL;
raid_device->responding = 1;
+ spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
starget_printk(KERN_INFO, raid_device->starget,
"handle(0x%04x), wwid(0x%016llx)\n", handle,
(unsigned long long)raid_device->wwid);
@@ -6574,16 +6575,16 @@ _scsih_mark_responding_raid_device(struct MPT2SAS_ADAPTER *ioc, u64 wwid,
*/
_scsih_init_warpdrive_properties(ioc, raid_device);
if (raid_device->handle == handle)
- goto out;
+ return;
printk(KERN_INFO "\thandle changed from(0x%04x)!!!\n",
raid_device->handle);
raid_device->handle = handle;
if (sas_target_priv_data)
sas_target_priv_data->handle = handle;
- goto out;
+ return;
}
}
- out:
+
spin_unlock_irqrestore(&ioc->raid_device_lock, flags);
}
diff --git a/drivers/spi/spi-tegra.c b/drivers/spi/spi-tegra.c
index c6ec3d0e384b..1e1ec8310094 100644
--- a/drivers/spi/spi-tegra.c
+++ b/drivers/spi/spi-tegra.c
@@ -1352,7 +1352,7 @@ skip_dma_alloc:
ret = spi_register_master(master);
if (!tspi->is_clkon_always) {
if (tspi->clk_state) {
- clk_disable(tspi->clk);
+ pm_runtime_put_sync(&pdev->dev);
tspi->clk_state = 0;
}
}
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 98c0e79110f4..d3bb2fc7fe2b 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -170,6 +170,9 @@ static int acm_write_start(struct acm *acm, int wbn)
unsigned long flags;
struct acm_wb *wb = &acm->wb[wbn];
int rc;
+#ifdef CONFIG_PM
+ struct urb *res;
+#endif
spin_lock_irqsave(&acm->write_lock, flags);
if (!acm->dev) {
@@ -183,23 +186,32 @@ static int acm_write_start(struct acm *acm, int wbn)
usb_autopm_get_interface_async(acm->control);
if (acm->susp_count) {
#ifdef CONFIG_PM
- printk("%s buffer urb\n", __func__);
acm->transmitting++;
wb->urb->transfer_buffer = wb->buf;
wb->urb->transfer_dma = wb->dmah;
wb->urb->transfer_buffer_length = wb->len;
wb->urb->dev = acm->dev;
usb_anchor_urb(wb->urb, &acm->deferred);
-#endif
+#else
if (!acm->delayed_wb)
acm->delayed_wb = wb;
else
usb_autopm_put_interface_async(acm->control);
+#endif
spin_unlock_irqrestore(&acm->write_lock, flags);
return 0; /* A white lie */
}
usb_mark_last_busy(acm->dev);
-
+#ifdef CONFIG_PM
+ while ((res = usb_get_from_anchor(&acm->deferred))) {
+ rc = usb_submit_urb(res, GFP_ATOMIC);
+ if (rc < 0) {
+ dbg("usb_submit_urb(pending request) failed: %d", rc);
+ usb_unanchor_urb(res);
+ acm_write_done(acm, res->context);
+ }
+ }
+#endif
rc = acm_start_wb(acm, wb);
spin_unlock_irqrestore(&acm->write_lock, flags);
@@ -1305,6 +1317,7 @@ static void acm_disconnect(struct usb_interface *intf)
stop_data_traffic(acm);
+ usb_kill_anchored_urbs(&acm->deferred);
acm_write_buffers_free(acm);
usb_free_coherent(usb_dev, acm->ctrlsize, acm->ctrl_buffer,
acm->ctrl_dma);
@@ -1370,8 +1383,10 @@ static int acm_resume(struct usb_interface *intf)
struct acm *acm = usb_get_intfdata(intf);
struct acm_wb *wb;
int rv = 0;
- struct urb *res;
int cnt;
+#ifdef CONFIG_PM
+ struct urb *res;
+#endif
spin_lock_irq(&acm->read_lock);
acm->susp_count -= 1;
@@ -1384,19 +1399,21 @@ static int acm_resume(struct usb_interface *intf)
mutex_lock(&acm->mutex);
-#ifdef CONFIG_PM
- while ((res = usb_get_from_anchor(&acm->deferred))) {
- printk("%s process buffered request \n", __func__);
- rv = usb_submit_urb(res, GFP_ATOMIC);
- if (rv < 0) {
- dbg("usb_submit_urb(pending request) failed: %d", rv);
- }
- }
-#endif
-
if (acm->port.count) {
rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
spin_lock_irq(&acm->write_lock);
+#ifdef CONFIG_PM
+ while ((res = usb_get_from_anchor(&acm->deferred))) {
+ rv = usb_submit_urb(res, GFP_ATOMIC);
+ if (rv < 0) {
+ dbg("usb_submit_urb(pending request)"
+ " failed: %d", rv);
+ usb_unanchor_urb(res);
+ acm_write_done(acm, res->context);
+ }
+ }
+ spin_unlock_irq(&acm->write_lock);
+#else
if (acm->delayed_wb) {
wb = acm->delayed_wb;
acm->delayed_wb = NULL;
@@ -1405,7 +1422,7 @@ static int acm_resume(struct usb_interface *intf)
} else {
spin_unlock_irq(&acm->write_lock);
}
-
+#endif
/*
* delayed error checking because we must
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index ab94b512e0ea..1a534c70ea6e 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -906,6 +906,9 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd)
ehci->reset_done[i] = jiffies + msecs_to_jiffies(25);
ehci_dbg (ehci, "port %d remote wakeup\n", i + 1);
mod_timer(&hcd->rh_timer, ehci->reset_done[i]);
+#ifdef CONFIG_USB_EHCI_TEGRA
+ ehci->controller_remote_wakeup = true;
+#endif
}
}
diff --git a/drivers/usb/host/ehci-tegra.c b/drivers/usb/host/ehci-tegra.c
index 917796c20d8a..030b03b55dff 100644
--- a/drivers/usb/host/ehci-tegra.c
+++ b/drivers/usb/host/ehci-tegra.c
@@ -167,11 +167,14 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd)
struct ehci_regs __iomem *hw = ehci->regs;
struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
u32 val;
+ irqreturn_t irq_status;
+ bool pmc_remote_wakeup = false;
if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) &&
(tegra->ehci->has_hostpc)) {
/* check if there is any remote wake event */
if (tegra_usb_phy_is_remotewake_detected(tegra->phy)) {
+ pmc_remote_wakeup = true;
spin_lock (&ehci->lock);
usb_hcd_resume_root_hub(hcd);
spin_unlock (&ehci->lock);
@@ -196,7 +199,21 @@ static irqreturn_t tegra_ehci_irq (struct usb_hcd *hcd)
}
spin_unlock(&ehci->lock);
}
- return ehci_irq(hcd);
+
+ irq_status = ehci_irq(hcd);
+
+ if (pmc_remote_wakeup) {
+ ehci->controller_remote_wakeup = false;
+ }
+
+ if (ehci->controller_remote_wakeup) {
+ ehci->controller_remote_wakeup = false;
+ /* disable interrupts */
+ ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+ tegra_usb_phy_preresume(tegra->phy, true);
+ tegra->port_resuming = 1;
+ }
+ return irq_status;
}
static int tegra_ehci_hub_control(
@@ -219,10 +236,13 @@ static int tegra_ehci_hub_control(
unsigned selector;
struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
bool hsic = false;
+ bool do_post_resume = false;
+ mutex_lock(&tegra->tegra_ehci_hcd_mutex);
if (!tegra->host_resumed) {
if (buf)
memset (buf, 0, wLength);
+ mutex_unlock(&tegra->tegra_ehci_hcd_mutex);
return retval;
}
@@ -244,13 +264,18 @@ static int tegra_ehci_hub_control(
} else if (typeReq == GetPortStatus) {
temp = ehci_readl(ehci, status_reg);
if (tegra->port_resuming && !(temp & PORT_SUSPEND) &&
- time_after_eq(jiffies, ehci->reset_done[wIndex-1])) {
- /* Resume completed, re-enable disconnect detection */
- tegra->port_resuming = 0;
+ time_after_eq(jiffies, ehci->reset_done[wIndex-1])) {
clear_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
ehci->reset_done[wIndex-1] = 0;
+ do_post_resume = true;
+ } else if (tegra->port_resuming && (temp & PORT_RESUME) &&
+ time_after_eq(jiffies, ehci->reset_done[wIndex-1]) ) {
+ do_post_resume = true;
+ }
+
+ if (do_post_resume) {
+ tegra->port_resuming = 0;
tegra_usb_phy_postresume(tegra->phy, false);
-#ifndef CONFIG_ARCH_TEGRA_2x_SOC
if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) {
ehci->command |= CMD_RUN;
/*
@@ -259,9 +284,11 @@ static int tegra_ehci_hub_control(
*/
ehci_writel(ehci, ehci->command,
&ehci->regs->command);
+ /* Now we can safely re-enable irqs */
+ ehci_writel(ehci, INTR_MASK, &ehci->regs->intr_enable);
}
-#endif
}
+
} else if (typeReq == SetPortFeature && wValue == USB_PORT_FEAT_SUSPEND) {
temp = ehci_readl(ehci, status_reg);
if ((temp & PORT_PE) == 0 || (temp & PORT_RESET) != 0) {
@@ -286,6 +313,13 @@ static int tegra_ehci_hub_control(
set_bit((wIndex & 0xff) - 1, &ehci->suspended_ports);
+ if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) {
+ /* Disable RUN bit. */
+ ehci->command &= ~CMD_RUN;
+ ehci_writel(ehci, ehci->command,
+ &ehci->regs->command);
+ }
+
tegra_usb_phy_postsuspend(tegra->phy, false);
goto done;
@@ -318,6 +352,15 @@ static int tegra_ehci_hub_control(
tegra->port_resuming = 1;
+ if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) {
+ /* disable interrupts */
+ ehci_writel(ehci, 0, &ehci->regs->intr_enable);
+ /* Disable RUN bit. */
+ ehci->command &= ~CMD_RUN;
+ ehci_writel(ehci, ehci->command,
+ &ehci->regs->command);
+ }
+
/* Disable disconnect detection during port resume */
tegra_usb_phy_preresume(tegra->phy, false);
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
@@ -417,9 +460,12 @@ static int tegra_ehci_hub_control(
spin_unlock_irqrestore(&ehci->lock, flags);
/* Handle the hub control events here */
- return ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+ retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+ mutex_unlock(&tegra->tegra_ehci_hcd_mutex);
+ return retval;
done:
spin_unlock_irqrestore(&ehci->lock, flags);
+ mutex_unlock(&tegra->tegra_ehci_hcd_mutex);
return retval;
}
@@ -443,7 +489,11 @@ static void tegra_ehci_restart(struct usb_hcd *hcd, bool is_dpd)
ehci_writel(ehci, (u32)ehci->async->qh_dma, &ehci->regs->async_next);
/* setup the command register and set the controller in RUN mode */
ehci->command &= ~(CMD_LRESET|CMD_IAAD|CMD_PSE|CMD_ASE|CMD_RESET);
- ehci->command |= CMD_RUN;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ /* dont start RS here for HSIC, it will be set by bus_reset */
+ if (tegra->phy->usb_phy_type != TEGRA_USB_PHY_TYPE_HSIC)
+#endif
+ ehci->command |= CMD_RUN;
ehci_writel(ehci, ehci->command, &ehci->regs->command);
/* Enable the root Port Power */
@@ -509,6 +559,7 @@ static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd)
unsigned long val;
bool hsic;
bool null_ulpi;
+ bool utmip_remote_wakeup = false;
null_ulpi = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI);
hsic = (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_HSIC);
@@ -547,8 +598,7 @@ static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd)
if ((tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_UTMIP) &&
(tegra->ehci->has_hostpc) && (tegra->phy->remote_wakeup)) {
- ehci->command |= CMD_RUN;
- ehci_writel(ehci, ehci->command, &ehci->regs->command);
+ utmip_remote_wakeup = true;
}
/* Check if the phy resume from LP0. When the phy resume from LP0
@@ -611,6 +661,10 @@ static int tegra_usb_resume(struct usb_hcd *hcd, bool is_dpd)
}
tegra_ehci_phy_restore_end(tegra->phy);
+ if (utmip_remote_wakeup) {
+ ehci->command |= CMD_RUN;
+ ehci_writel(ehci, ehci->command, &ehci->regs->command);
+ }
return 0;
restart:
@@ -655,11 +709,35 @@ restart:
}
#endif
+/*
+ * Disable PHY clock valid interrupts and wait for the interrupt handler to
+ * finish.
+ *
+ * Requires a lock on tegra_ehci_hcd_mutex
+ * Must not be called with a lock on ehci->lock
+ */
+static void tegra_ehci_disable_phy_interrupt(struct usb_hcd *hcd) {
+ struct tegra_ehci_hcd *tegra;
+ u32 val;
+ if (hcd->irq >= 0) {
+ tegra = dev_get_drvdata(hcd->self.controller);
+ if (tegra->phy->hotplug) {
+ /* Disable PHY clock valid interrupts */
+ val = readl(hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET);
+ val &= ~TEGRA_USB_PHY_CLK_VALID_INT_ENB;
+ writel(val , (hcd->regs + TEGRA_USB_SUSP_CTRL_OFFSET));
+ }
+ /* Wait for the interrupt handler to finish */
+ synchronize_irq(hcd->irq);
+ }
+}
+
static void tegra_ehci_shutdown(struct usb_hcd *hcd)
{
struct tegra_ehci_hcd *tegra = dev_get_drvdata(hcd->self.controller);
mutex_lock(&tegra->tegra_ehci_hcd_mutex);
+ tegra_ehci_disable_phy_interrupt(hcd);
/* ehci_shutdown touches the USB controller registers, make sure
* controller has clocks to it */
if (!tegra->host_resumed)
@@ -709,6 +787,7 @@ static int tegra_ehci_setup(struct usb_hcd *hcd)
return retval;
ehci->sbrn = 0x20;
+ ehci->controller_remote_wakeup = false;
if (tegra->phy->usb_phy_type == TEGRA_USB_PHY_TYPE_NULL_ULPI) {
tegra_ehci_pre_reset(tegra->phy, false);
@@ -734,6 +813,7 @@ static int tegra_ehci_bus_suspend(struct usb_hcd *hcd)
int error_status = 0;
mutex_lock(&tegra->tegra_ehci_hcd_mutex);
+ tegra_ehci_disable_phy_interrupt(hcd);
/* ehci_shutdown touches the USB controller registers, make sure
* controller has clocks to it */
if (!tegra->host_resumed)
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 7f0e828631ed..e897262c6a60 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -142,6 +142,7 @@ struct ehci_hcd { /* one per controller */
unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */
#ifdef CONFIG_USB_EHCI_TEGRA
unsigned controller_resets_phy:1;
+ unsigned controller_remote_wakeup:1;
#endif
/* required for usb32 quirk */
diff --git a/drivers/usb/serial/baseband_usb_chr.c b/drivers/usb/serial/baseband_usb_chr.c
index bb2649dc4c36..6d691a40312d 100644
--- a/drivers/usb/serial/baseband_usb_chr.c
+++ b/drivers/usb/serial/baseband_usb_chr.c
@@ -51,6 +51,7 @@ module_param(baseband_usb_chr_intf, ulong, 0644);
MODULE_PARM_DESC(baseband_usb_chr_intf, "baseband (usb chr) - USB interface");
static struct baseband_usb *baseband_usb_chr;
+static bool usb_device_connection;
static atomic_t g_rx_count = ATOMIC_INIT(0);
@@ -475,6 +476,7 @@ static void baseband_ipc_close(struct baseband_ipc *ipc)
/* destroy work queue */
if (ipc->workqueue) {
pr_debug("destroy workqueue {\n");
+ cancel_work_sync(&ipc->work);
destroy_workqueue(ipc->workqueue);
ipc->workqueue = (struct workqueue_struct *) 0;
pr_debug("destroy workqueue }\n");
@@ -620,6 +622,11 @@ static int baseband_usb_chr_rx_urb_submit(struct baseband_usb *usb)
pr_debug("baseband_usb_chr_rx_urb_submit { usb %p\n", usb);
+ if (!usb_device_connection) {
+ pr_err("!!no usb device conenction!!!!!\n");
+ return -1;
+ }
+
/* check input */
if (usb->usb.rx_urb) {
pr_err("previous urb still active\n");
@@ -791,6 +798,7 @@ static int baseband_usb_driver_probe(struct usb_interface *intf,
baseband_usb_chr->usb.pipe.interrupt.in);
pr_debug("baseband_usb_chr->usb.pipe.interrupt.out %x\n",
baseband_usb_chr->usb.pipe.interrupt.out);
+ usb_device_connection = true;
/* start usb rx */
err = baseband_usb_chr_rx_urb_submit(baseband_usb_chr);
@@ -805,8 +813,19 @@ static int baseband_usb_driver_probe(struct usb_interface *intf,
static void baseband_usb_driver_disconnect(struct usb_interface *intf)
{
+ struct usb_device *usb_dev = interface_to_usbdev(intf);
pr_debug("%s(%d) { intf %p\n", __func__, __LINE__, intf);
pr_debug("%s(%d) }\n", __func__, __LINE__);
+ if (baseband_usb_chr->usb.interface != intf) {
+ pr_info("%s(%d) -ENODEV\n", __func__, __LINE__);
+ return;
+ }
+ if (baseband_usb_chr->usb.device == usb_dev) {
+ pr_info("%s: Matching usb device: Flush workqueue\n", __func__);
+ flush_workqueue(baseband_usb_chr->ipc->workqueue);
+ usb_device_connection = false;
+ }
+
}
static char baseband_usb_driver_name[32];
diff --git a/drivers/video/tegra/dc/dc.c b/drivers/video/tegra/dc/dc.c
index e542dd19d667..bdd0a02e3356 100644
--- a/drivers/video/tegra/dc/dc.c
+++ b/drivers/video/tegra/dc/dc.c
@@ -1283,7 +1283,7 @@ u32 tegra_dc_incr_syncpt_max(struct tegra_dc *dc, int i)
u32 max;
mutex_lock(&dc->lock);
- max = nvhost_syncpt_incr_max(&dc->ndev->host->syncpt,
+ max = nvhost_syncpt_incr_max(&nvhost_get_host(dc->ndev)->syncpt,
dc->syncpt[i].id, ((dc->enabled) ? 1 : 0));
dc->syncpt[i].max = max;
mutex_unlock(&dc->lock);
@@ -1297,7 +1297,8 @@ void tegra_dc_incr_syncpt_min(struct tegra_dc *dc, int i, u32 val)
if ( dc->enabled )
while (dc->syncpt[i].min < val) {
dc->syncpt[i].min++;
- nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt,
+ nvhost_syncpt_cpu_incr(
+ &nvhost_get_host(dc->ndev)->syncpt,
dc->syncpt[i].id);
}
mutex_unlock(&dc->lock);
@@ -1361,6 +1362,21 @@ static unsigned long tegra_dc_pclk_round_rate(struct tegra_dc *dc, int pclk)
return rate * 2 / div;
}
+static unsigned long tegra_dc_pclk_predict_rate(struct clk *parent, int pclk)
+{
+ unsigned long rate;
+ unsigned long div;
+
+ rate = clk_get_rate(parent);
+
+ div = DIV_ROUND_CLOSEST(rate * 2, pclk);
+
+ if (div < 2)
+ return 0;
+
+ return rate * 2 / div;
+}
+
void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
{
int pclk;
@@ -1370,6 +1386,17 @@ void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
struct clk *parent_clk =
clk_get_sys(NULL, dc->out->parent_clk ? : "pll_p");
+ if (dc->out->parent_clk_backup &&
+ (parent_clk == clk_get_sys(NULL, "pll_p"))) {
+ rate = tegra_dc_pclk_predict_rate(
+ parent_clk, dc->mode.pclk);
+ /* use pll_d as last resort */
+ if (rate < (dc->mode.pclk / 100 * 99) ||
+ rate > (dc->mode.pclk / 100 * 109))
+ parent_clk = clk_get_sys(
+ NULL, dc->out->parent_clk_backup);
+ }
+
if (clk_get_parent(clk) != parent_clk)
clk_set_parent(clk, parent_clk);
@@ -1433,7 +1460,7 @@ void tegra_dc_setup_clk(struct tegra_dc *dc, struct clk *clk)
}
}
- rate = dc->mode.pclk * 2;
+ rate = dc->mode.pclk * dc->shift_clk_div * 2;
if (rate != clk_get_rate(base_clk))
clk_set_rate(base_clk, rate);
@@ -2125,7 +2152,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *ptr)
unsigned long underflow_mask;
u32 val;
- if (!nvhost_module_powered(dc->ndev->host->dev)) {
+ if (!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev)) {
WARN(1, "IRQ when DC not powered!\n");
tegra_dc_io_start(dc);
status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
@@ -2333,7 +2360,8 @@ static void tegra_dc_init(struct tegra_dc *dc)
dc->syncpt[i].id = syncpt;
dc->syncpt[i].min = dc->syncpt[i].max =
- nvhost_syncpt_read(&dc->ndev->host->syncpt, syncpt);
+ nvhost_syncpt_read(&nvhost_get_host(dc->ndev)->syncpt,
+ syncpt);
}
print_mode(dc, &dc->mode, __func__);
@@ -2480,7 +2508,8 @@ static void _tegra_dc_controller_disable(struct tegra_dc *dc)
/* flush any pending syncpt waits */
while (dc->syncpt[i].min < dc->syncpt[i].max) {
dc->syncpt[i].min++;
- nvhost_syncpt_cpu_incr(&dc->ndev->host->syncpt,
+ nvhost_syncpt_cpu_incr(
+ &nvhost_get_host(dc->ndev)->syncpt,
dc->syncpt[i].id);
}
}
@@ -2708,6 +2737,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
dc->clk = clk;
dc->emc_clk = emc_clk;
+ dc->shift_clk_div = 1;
dc->base_res = base_res;
dc->base = base;
@@ -2719,8 +2749,7 @@ static int tegra_dc_probe(struct nvhost_device *ndev)
* The emc is a shared clock, it will be set based on
* the requirements for each user on the bus.
*/
- dc->emc_clk_rate = tegra_dc_get_default_emc_clk_rate(dc);
- clk_set_rate(emc_clk, dc->emc_clk_rate);
+ dc->emc_clk_rate = 0;
if (dc->pdata->flags & TEGRA_DC_FLAG_ENABLED)
dc->enabled = true;
diff --git a/drivers/video/tegra/dc/dc_priv.h b/drivers/video/tegra/dc/dc_priv.h
index e516bfd72c85..30b5ea996922 100644
--- a/drivers/video/tegra/dc/dc_priv.h
+++ b/drivers/video/tegra/dc/dc_priv.h
@@ -82,6 +82,7 @@ struct tegra_dc {
struct clk *emc_clk;
int emc_clk_rate;
int new_emc_clk_rate;
+ u32 shift_clk_div;
bool connected;
bool enabled;
@@ -143,25 +144,25 @@ struct tegra_dc {
static inline void tegra_dc_io_start(struct tegra_dc *dc)
{
- nvhost_module_busy(dc->ndev->host->dev);
+ nvhost_module_busy(nvhost_get_host(dc->ndev)->dev);
}
static inline void tegra_dc_io_end(struct tegra_dc *dc)
{
- nvhost_module_idle(dc->ndev->host->dev);
+ nvhost_module_idle(nvhost_get_host(dc->ndev)->dev);
}
static inline unsigned long tegra_dc_readl(struct tegra_dc *dc,
unsigned long reg)
{
- BUG_ON(!nvhost_module_powered(dc->ndev->host->dev));
+ BUG_ON(!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev));
return readl(dc->base + reg * 4);
}
static inline void tegra_dc_writel(struct tegra_dc *dc, unsigned long val,
unsigned long reg)
{
- BUG_ON(!nvhost_module_powered(dc->ndev->host->dev));
+ BUG_ON(!nvhost_module_powered(nvhost_get_host(dc->ndev)->dev));
writel(val, dc->base + reg * 4);
}
diff --git a/drivers/video/tegra/dc/dsi.c b/drivers/video/tegra/dc/dsi.c
index 5c120342f52a..2c511efc4156 100644
--- a/drivers/video/tegra/dc/dsi.c
+++ b/drivers/video/tegra/dc/dsi.c
@@ -303,14 +303,15 @@ static int tegra_dsi_syncpt(struct tegra_dc_dsi_data *dsi)
ret = 0;
dsi->syncpt_val = nvhost_syncpt_read(
- &dsi->dc->ndev->host->syncpt, dsi->syncpt_id);
+ &nvhost_get_host(dsi->dc->ndev)->syncpt,
+ dsi->syncpt_id);
val = DSI_INCR_SYNCPT_COND(OP_DONE) |
DSI_INCR_SYNCPT_INDX(dsi->syncpt_id);
tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT);
/* TODO: Use interrupt rather than polling */
- ret = nvhost_syncpt_wait(&dsi->dc->ndev->host->syncpt,
+ ret = nvhost_syncpt_wait(&nvhost_get_host(dsi->dc->ndev)->syncpt,
dsi->syncpt_id, dsi->syncpt_val + 1);
if (ret < 0) {
dev_err(&dsi->dc->ndev->dev, "DSI sync point failure\n");
@@ -945,21 +946,23 @@ static void tegra_dsi_set_dsi_clk(struct tegra_dc *dc,
{
u32 rm;
+ /* Round up to MHz */
rm = clk % 1000;
if (rm != 0)
clk -= rm;
- dc->mode.pclk = clk*1000;
+ /* Set up pixel clock */
+ dc->shift_clk_div = dsi->shift_clk_div;
+ dc->mode.pclk = (clk * 1000) / dsi->shift_clk_div;
+
+ /* Enable DSI clock */
tegra_dc_setup_clk(dc, dsi->dsi_clk);
- if (dsi->clk_ref == true)
- clk_disable(dsi->dsi_clk);
- else
+ if (!dsi->clk_ref) {
dsi->clk_ref = true;
- clk_enable(dsi->dsi_clk);
- tegra_periph_reset_deassert(dsi->dsi_clk);
-
+ clk_enable(dsi->dsi_clk);
+ tegra_periph_reset_deassert(dsi->dsi_clk);
+ }
dsi->current_dsi_clk_khz = clk_get_rate(dsi->dsi_clk) / 1000;
-
dsi->current_bit_clk_ns = 1000*1000 / (dsi->current_dsi_clk_khz * 2);
}
@@ -1742,14 +1745,15 @@ static int tegra_dsi_bta(struct tegra_dc_dsi_data *dsi)
#if DSI_USE_SYNC_POINTS
/* FIXME: Workaround for nvhost_syncpt_read */
dsi->syncpt_val = nvhost_syncpt_update_min(
- &dsi->dc->ndev->host->syncpt, dsi->syncpt_id);
+ &nvhost_get_host(dsi->dc->ndev)->syncpt,
+ dsi->syncpt_id);
val = DSI_INCR_SYNCPT_COND(OP_DONE) |
DSI_INCR_SYNCPT_INDX(dsi->syncpt_id);
tegra_dsi_writel(dsi, val, DSI_INCR_SYNCPT);
/* TODO: Use interrupt rather than polling */
- err = nvhost_syncpt_wait(&dsi->dc->ndev->host->syncpt,
+ err = nvhost_syncpt_wait(&nvhost_get_host(dsi->dc->ndev)->syncpt,
dsi->syncpt_id, dsi->syncpt_val + 1);
if (err < 0)
dev_err(&dsi->dc->ndev->dev,
diff --git a/drivers/video/tegra/dc/ext/dev.c b/drivers/video/tegra/dc/ext/dev.c
index 2148c0b18c71..ca4ca5370633 100644
--- a/drivers/video/tegra/dc/ext/dev.c
+++ b/drivers/video/tegra/dc/ext/dev.c
@@ -224,10 +224,11 @@ static int tegra_dc_ext_set_windowattr(struct tegra_dc_ext *ext,
win->stride_uv = flip_win->attr.stride_uv;
if ((s32)flip_win->attr.pre_syncpt_id >= 0) {
- nvhost_syncpt_wait_timeout(&ext->dc->ndev->host->syncpt,
- flip_win->attr.pre_syncpt_id,
- flip_win->attr.pre_syncpt_val,
- msecs_to_jiffies(500), NULL);
+ nvhost_syncpt_wait_timeout(
+ &nvhost_get_host(ext->dc->ndev)->syncpt,
+ flip_win->attr.pre_syncpt_id,
+ flip_win->attr.pre_syncpt_val,
+ msecs_to_jiffies(500), NULL);
}
diff --git a/drivers/video/tegra/dc/nvsd.c b/drivers/video/tegra/dc/nvsd.c
index 3f0ed4ab933d..2f9faf931e40 100644
--- a/drivers/video/tegra/dc/nvsd.c
+++ b/drivers/video/tegra/dc/nvsd.c
@@ -1,7 +1,7 @@
/*
* drivers/video/tegra/dc/nvsd.c
*
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
*
* This software is licensed under the terms of the GNU General Public
* License version 2, as published by the Free Software Foundation, and
@@ -20,6 +20,7 @@
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/backlight.h>
+#include <linux/platform_device.h>
#include "dc_reg.h"
#include "dc_priv.h"
diff --git a/drivers/video/tegra/host/Makefile b/drivers/video/tegra/host/Makefile
index 4fd19ac809b8..be2be9211731 100644
--- a/drivers/video/tegra/host/Makefile
+++ b/drivers/video/tegra/host/Makefile
@@ -3,12 +3,12 @@ nvhost-objs = \
nvhost_acm.o \
nvhost_syncpt.o \
nvhost_cdma.o \
- nvhost_cpuaccess.o \
nvhost_intr.o \
nvhost_channel.o \
nvhost_job.o \
dev.o \
bus.o \
+ bus_client.o \
debug.o
obj-$(CONFIG_TEGRA_GRHOST) += mpe/
diff --git a/drivers/video/tegra/host/bus.c b/drivers/video/tegra/host/bus.c
index d93e44691847..8234d0fa64c3 100644
--- a/drivers/video/tegra/host/bus.c
+++ b/drivers/video/tegra/host/bus.c
@@ -77,8 +77,6 @@ static int nvhost_drv_probe(struct device *_dev)
struct nvhost_driver *drv = to_nvhost_driver(_dev->driver);
struct nvhost_device *dev = to_nvhost_device(_dev);
- dev->host = nvhost;
-
return drv->probe(dev);
}
@@ -131,8 +129,6 @@ int nvhost_device_register(struct nvhost_device *dev)
if (!dev->dev.parent && nvhost && nvhost->dev != dev)
dev->dev.parent = &nvhost->dev->dev;
- /* Give pointer to host1x */
- dev->host = nvhost;
dev->dev.bus = &nvhost_bus_type;
if (dev->id != -1)
@@ -545,7 +541,6 @@ static int set_parent(struct device *dev, void *data)
struct nvhost_master *host = data;
if (!dev->parent && ndev != host->dev)
dev->parent = &host->dev->dev;
- ndev->host = host;
return 0;
}
diff --git a/drivers/video/tegra/host/bus_client.c b/drivers/video/tegra/host/bus_client.c
new file mode 100644
index 000000000000..3d455298af8e
--- /dev/null
+++ b/drivers/video/tegra/host/bus_client.c
@@ -0,0 +1,53 @@
+/*
+ * drivers/video/tegra/host/bus_client.c
+ *
+ * Tegra Graphics Host Client Module
+ *
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "bus_client.h"
+#include "dev.h"
+#include <linux/string.h>
+
+void nvhost_read_module_regs(struct nvhost_device *ndev,
+ u32 offset, int count, u32 *values)
+{
+ void __iomem *p = ndev->aperture + offset;
+
+ nvhost_module_busy(ndev);
+ while (count--) {
+ *(values++) = readl(p);
+ p += 4;
+ }
+ rmb();
+ nvhost_module_idle(ndev);
+}
+
+void nvhost_write_module_regs(struct nvhost_device *ndev,
+ u32 offset, int count, const u32 *values)
+{
+ void __iomem *p = ndev->aperture + offset;
+
+ nvhost_module_busy(ndev);
+ while (count--) {
+ writel(*(values++), p);
+ p += 4;
+ }
+ wmb();
+ nvhost_module_idle(ndev);
+}
diff --git a/drivers/video/tegra/host/bus_client.h b/drivers/video/tegra/host/bus_client.h
new file mode 100644
index 000000000000..dc1b6be1d8dc
--- /dev/null
+++ b/drivers/video/tegra/host/bus_client.h
@@ -0,0 +1,35 @@
+/*
+ * drivers/video/tegra/host/bus_client.h
+ *
+ * Tegra Graphics Host Cpu Register Access
+ *
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef __NVHOST_BUS_CLIENT_H
+#define __NVHOST_BUS_CLIENT_H
+
+#include <linux/types.h>
+struct nvhost_device;
+
+void nvhost_read_module_regs(struct nvhost_device *ndev,
+ u32 offset, int count, u32 *values);
+
+void nvhost_write_module_regs(struct nvhost_device *ndev,
+ u32 offset, int count, const u32 *values);
+
+#endif
diff --git a/drivers/video/tegra/host/chip_support.h b/drivers/video/tegra/host/chip_support.h
index 16ec7bfc7533..17c2116b14cf 100644
--- a/drivers/video/tegra/host/chip_support.h
+++ b/drivers/video/tegra/host/chip_support.h
@@ -3,7 +3,7 @@
*
* Tegra Graphics Host Chip Support
*
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2011-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -35,7 +35,6 @@ struct nvhost_cdma;
struct nvhost_intr;
struct push_buffer;
struct nvhost_syncpt;
-struct nvhost_cpuaccess;
struct nvhost_master;
struct dentry;
struct nvhost_job;
@@ -114,6 +113,10 @@ struct nvhost_chip_support {
int num_waitchk);
void (*debug)(struct nvhost_syncpt *);
const char * (*name)(struct nvhost_syncpt *, u32 id);
+ int (*mutex_try_lock)(struct nvhost_syncpt *,
+ unsigned int idx);
+ void (*mutex_unlock)(struct nvhost_syncpt *,
+ unsigned int idx);
} syncpt;
struct {
@@ -128,14 +131,6 @@ struct nvhost_chip_support {
void (*free_host_general_irq)(struct nvhost_intr *);
int (*request_syncpt_irq)(struct nvhost_intr_syncpt *syncpt);
} intr;
-
- struct {
- int (*mutex_try_lock)(struct nvhost_cpuaccess *,
- unsigned int idx);
- void (*mutex_unlock)(struct nvhost_cpuaccess *,
- unsigned int idx);
- } cpuaccess;
-
};
#endif /* _NVHOST_CHIP_SUPPORT_H_ */
diff --git a/drivers/video/tegra/host/dev.c b/drivers/video/tegra/host/dev.c
index c7b1ebfd60b0..4cd1e4eaf843 100644
--- a/drivers/video/tegra/host/dev.c
+++ b/drivers/video/tegra/host/dev.c
@@ -3,7 +3,7 @@
*
* Tegra Graphics Host Driver Entrypoint
*
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
*/
#include "dev.h"
+#include "bus_client.h"
#include <linux/slab.h>
#include <linux/string.h>
@@ -162,7 +163,8 @@ static int nvhost_channelopen(struct inode *inode, struct file *filp)
goto fail;
}
priv->priority = NVHOST_PRIORITY_MEDIUM;
- priv->clientid = atomic_add_return(1, &ch->dev->host->clientid);
+ priv->clientid = atomic_add_return(1,
+ &nvhost_get_host(ch->dev)->clientid);
priv->job = nvhost_job_alloc(ch, priv->hwctx, &priv->hdr,
NULL, priv->priority, priv->clientid);
@@ -528,9 +530,9 @@ static int nvhost_ctrlrelease(struct inode *inode, struct file *filp)
filp->private_data = NULL;
if (priv->mod_locks[0])
nvhost_module_idle(priv->dev->dev);
- for (i = 1; i < priv->dev->nb_mlocks; i++)
+ for (i = 1; i < priv->dev->syncpt.nb_mlocks; i++)
if (priv->mod_locks[i])
- nvhost_mutex_unlock(&priv->dev->cpuaccess, i);
+ nvhost_mutex_unlock(&priv->dev->syncpt, i);
kfree(priv->mod_locks);
kfree(priv);
return 0;
@@ -545,7 +547,7 @@ static int nvhost_ctrlopen(struct inode *inode, struct file *filp)
trace_nvhost_ctrlopen(host->dev->name);
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
- mod_locks = kzalloc(sizeof(u32)*host->nb_mlocks, GFP_KERNEL);
+ mod_locks = kzalloc(sizeof(u32) * host->syncpt.nb_mlocks, GFP_KERNEL);
if (!(priv && mod_locks)) {
kfree(priv);
@@ -604,7 +606,7 @@ static int nvhost_ioctl_ctrl_module_mutex(
struct nvhost_ctrl_module_mutex_args *args)
{
int err = 0;
- if (args->id >= ctx->dev->nb_mlocks ||
+ if (args->id >= ctx->dev->syncpt.nb_mlocks ||
args->lock > 1)
return -EINVAL;
@@ -613,51 +615,73 @@ static int nvhost_ioctl_ctrl_module_mutex(
if (args->id == 0)
nvhost_module_busy(ctx->dev->dev);
else
- err = nvhost_mutex_try_lock(&ctx->dev->cpuaccess, args->id);
+ err = nvhost_mutex_try_lock(&ctx->dev->syncpt,
+ args->id);
if (!err)
ctx->mod_locks[args->id] = 1;
} else if (!args->lock && ctx->mod_locks[args->id]) {
if (args->id == 0)
nvhost_module_idle(ctx->dev->dev);
else
- nvhost_mutex_unlock(&ctx->dev->cpuaccess, args->id);
+ nvhost_mutex_unlock(&ctx->dev->syncpt, args->id);
ctx->mod_locks[args->id] = 0;
}
return err;
}
+static struct nvhost_device *get_ndev_by_moduleid(struct nvhost_master *host,
+ u32 id)
+{
+ int i;
+
+ for (i = 0; i < host->nb_channels; i++) {
+ struct nvhost_device *ndev = host->channels[i].dev;
+ if (id == ndev->moduleid)
+ return ndev;
+ }
+ return NULL;
+}
+
static int nvhost_ioctl_ctrl_module_regrdwr(
struct nvhost_ctrl_userctx *ctx,
struct nvhost_ctrl_module_regrdwr_args *args)
{
u32 num_offsets = args->num_offsets;
u32 *offsets = args->offsets;
- void *values = args->values;
+ u32 *values = args->values;
u32 vals[64];
+ struct nvhost_device *ndev;
trace_nvhost_ioctl_ctrl_module_regrdwr(args->id,
args->num_offsets, args->write);
- if (!(args->id < ctx->dev->nb_modules) ||
- (num_offsets == 0))
+ /* Check that there is something to read and that block size is
+ * u32 aligned */
+ if (num_offsets == 0 || args->block_size & 3)
+ return -EINVAL;
+
+ ndev = get_ndev_by_moduleid(ctx->dev, args->id);
+ if (!ndev)
return -EINVAL;
while (num_offsets--) {
- u32 remaining = args->block_size;
+ int remaining = args->block_size >> 2;
u32 offs;
if (get_user(offs, offsets))
return -EFAULT;
offsets++;
while (remaining) {
- u32 batch = min(remaining, 64*sizeof(u32));
+ int batch = min(remaining, 64);
if (args->write) {
- if (copy_from_user(vals, values, batch))
+ if (copy_from_user(vals, values,
+ batch*sizeof(u32)))
return -EFAULT;
- nvhost_write_module_regs(&ctx->dev->cpuaccess,
- args->id, offs, batch, vals);
+ nvhost_write_module_regs(ndev,
+ offs, batch, vals);
} else {
- nvhost_read_module_regs(&ctx->dev->cpuaccess,
- args->id, offs, batch, vals);
- if (copy_to_user(values, vals, batch))
+ nvhost_read_module_regs(ndev,
+ offs, batch, vals);
+ if (copy_to_user(values, vals,
+ batch*sizeof(u32)))
return -EFAULT;
}
remaining -= batch;
@@ -738,14 +762,14 @@ static const struct file_operations nvhost_ctrlops = {
static void power_on_host(struct nvhost_device *dev)
{
- struct nvhost_master *host = dev->host;
+ struct nvhost_master *host = nvhost_get_drvdata(dev);
nvhost_intr_start(&host->intr, clk_get_rate(dev->clk[0]));
nvhost_syncpt_reset(&host->syncpt);
}
static int power_off_host(struct nvhost_device *dev)
{
- struct nvhost_master *host = dev->host;
+ struct nvhost_master *host = nvhost_get_drvdata(dev);
nvhost_syncpt_save(&host->syncpt);
nvhost_intr_stop(&host->intr);
return 0;
@@ -828,14 +852,8 @@ static void nvhost_remove_chip_support(struct nvhost_master *host)
kfree(host->intr.syncpt);
host->intr.syncpt = 0;
- kfree(host->cpuaccess.regs);
- host->cpuaccess.regs = 0;
-
- kfree(host->cpuaccess.reg_mem);
- host->cpuaccess.reg_mem = 0;
-
- kfree(host->cpuaccess.lock_counts);
- host->cpuaccess.lock_counts = 0;
+ kfree(host->syncpt.lock_counts);
+ host->syncpt.lock_counts = 0;
}
static int __devinit nvhost_init_chip_support(struct nvhost_master *host)
@@ -872,19 +890,12 @@ static int __devinit nvhost_init_chip_support(struct nvhost_master *host)
host->intr.syncpt = kzalloc(sizeof(struct nvhost_intr_syncpt) *
host->syncpt.nb_pts, GFP_KERNEL);
- host->cpuaccess.reg_mem = kzalloc(sizeof(struct resource *) *
- host->nb_modules, GFP_KERNEL);
-
- host->cpuaccess.regs = kzalloc(sizeof(void __iomem *) *
- host->nb_modules, GFP_KERNEL);
-
- host->cpuaccess.lock_counts = kzalloc(sizeof(atomic_t) *
- host->nb_mlocks, GFP_KERNEL);
+ host->syncpt.lock_counts = kzalloc(sizeof(atomic_t) *
+ host->syncpt.nb_mlocks, GFP_KERNEL);
if (!(host->channels && host->syncpt.min_val &&
host->syncpt.max_val && host->syncpt.base_val &&
- host->intr.syncpt && host->cpuaccess.reg_mem &&
- host->cpuaccess.regs && host->cpuaccess.lock_counts)) {
+ host->intr.syncpt && host->syncpt.lock_counts)) {
/* frees happen in the support removal phase */
return -ENOMEM;
}
@@ -894,6 +905,7 @@ static int __devinit nvhost_init_chip_support(struct nvhost_master *host)
struct nvhost_device hostdev = {
.name = "host1x",
+ .id = -1,
.finalize_poweron = power_on_host,
.prepare_poweroff = power_off_host,
.clocks = {{"host1x", UINT_MAX}, {} },
@@ -953,21 +965,17 @@ static int __devinit nvhost_probe(struct platform_device *pdev)
host->dev = &hostdev;
nvhost_bus_add_host(host);
+ /* Give pointer to host1x via driver */
+ nvhost_set_drvdata(&hostdev, host);
+
+ BUG_ON(!host_channel_op(host).init);
for (i = 0; i < host->nb_channels; i++) {
struct nvhost_channel *ch = &host->channels[i];
- BUG_ON(!host_channel_op(host).init);
- err = host_channel_op(host).init(ch, host, i);
- if (err < 0) {
- dev_err(&pdev->dev, "failed to init channel %d\n", i);
+ err = nvhost_channel_init(ch, host, i);
+ if (err)
goto fail;
- }
- ch->dev->channel = ch;
}
- err = nvhost_cpuaccess_init(&host->cpuaccess, pdev);
- if (err)
- goto fail;
-
err = nvhost_intr_init(&host->intr, intr1->start, intr0->start);
if (err)
goto fail;
@@ -982,7 +990,7 @@ static int __devinit nvhost_probe(struct platform_device *pdev)
for (i = 0; i < host->nb_channels; i++) {
struct nvhost_channel *ch = &host->channels[i];
- nvhost_module_preinit(ch->dev);
+ nvhost_module_init(ch->dev);
}
platform_set_drvdata(pdev, host);
diff --git a/drivers/video/tegra/host/dev.h b/drivers/video/tegra/host/dev.h
index 635b0c90ccf9..7b6fd99746dc 100644
--- a/drivers/video/tegra/host/dev.h
+++ b/drivers/video/tegra/host/dev.h
@@ -26,7 +26,6 @@
#include "nvhost_acm.h"
#include "nvhost_syncpt.h"
#include "nvhost_intr.h"
-#include "nvhost_cpuaccess.h"
#include "nvhost_channel.h"
#include "chip_support.h"
@@ -43,13 +42,10 @@ struct nvhost_master {
struct device *ctrl;
struct nvhost_syncpt syncpt;
struct nvmap_client *nvmap;
- struct nvhost_cpuaccess cpuaccess;
- u32 nb_mlocks;
struct nvhost_intr intr;
struct nvhost_device *dev;
struct nvhost_channel *channels;
u32 nb_channels;
- u32 nb_modules;
u32 sync_queue_size;
diff --git a/drivers/video/tegra/host/gr3d/gr3d.c b/drivers/video/tegra/host/gr3d/gr3d.c
index 709d4ece9394..9e02ace2c721 100644
--- a/drivers/video/tegra/host/gr3d/gr3d.c
+++ b/drivers/video/tegra/host/gr3d/gr3d.c
@@ -77,7 +77,7 @@ void nvhost_3dctx_restore_end(u32 *ptr)
struct nvhost_hwctx *nvhost_3dctx_alloc_common(struct nvhost_channel *ch,
bool map_restore)
{
- struct nvmap_client *nvmap = ch->dev->host->nvmap;
+ struct nvmap_client *nvmap = nvhost_get_host(ch->dev)->nvmap;
struct nvhost_hwctx *ctx;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -130,7 +130,7 @@ void nvhost_3dctx_get(struct nvhost_hwctx *ctx)
void nvhost_3dctx_free(struct kref *ref)
{
struct nvhost_hwctx *ctx = container_of(ref, struct nvhost_hwctx, ref);
- struct nvmap_client *nvmap = ctx->channel->dev->host->nvmap;
+ struct nvmap_client *nvmap = nvhost_get_host(ctx->channel->dev)->nvmap;
if (ctx->restore_virt) {
nvmap_munmap(ctx->restore, ctx->restore_virt);
diff --git a/drivers/video/tegra/host/gr3d/gr3d_t20.c b/drivers/video/tegra/host/gr3d/gr3d_t20.c
index 24d7e6d75503..c576cf7ff5a5 100644
--- a/drivers/video/tegra/host/gr3d/gr3d_t20.c
+++ b/drivers/video/tegra/host/gr3d/gr3d_t20.c
@@ -340,7 +340,8 @@ static void ctx3d_save_service(struct nvhost_hwctx *ctx)
ARRAY_SIZE(ctxsave_regs_3d_global));
wmb();
- nvhost_syncpt_cpu_incr(&ctx->channel->dev->host->syncpt, NVSYNCPT_3D);
+ nvhost_syncpt_cpu_incr(&nvhost_get_host(ctx->channel->dev)->syncpt,
+ NVSYNCPT_3D);
}
int __init nvhost_gr3d_t20_ctxhandler_init(struct nvhost_hwctx_handler *h)
@@ -350,7 +351,7 @@ int __init nvhost_gr3d_t20_ctxhandler_init(struct nvhost_hwctx_handler *h)
u32 *save_ptr;
ch = container_of(h, struct nvhost_channel, ctxhandler);
- nvmap = ch->dev->host->nvmap;
+ nvmap = nvhost_get_host(ch->dev)->nvmap;
setup_save(NULL);
diff --git a/drivers/video/tegra/host/gr3d/gr3d_t30.c b/drivers/video/tegra/host/gr3d/gr3d_t30.c
index 1fde326fc101..68bcaec36b46 100644
--- a/drivers/video/tegra/host/gr3d/gr3d_t30.c
+++ b/drivers/video/tegra/host/gr3d/gr3d_t30.c
@@ -73,6 +73,7 @@ static const struct hwctx_reginfo ctxsave_regs_3d_global[] = {
HWCTX_REGINFO(0xa02, 10, DIRECT),
HWCTX_REGINFO(0xb04, 1, DIRECT),
HWCTX_REGINFO(0xb06, 13, DIRECT),
+ HWCTX_REGINFO(0xe42, 2, DIRECT), /* HW bug workaround */
};
static const struct hwctx_reginfo ctxsave_regs_3d_perset[] = {
@@ -385,7 +386,7 @@ int __init nvhost_gr3d_t30_ctxhandler_init(struct nvhost_hwctx_handler *h)
u32 *save_ptr;
ch = container_of(h, struct nvhost_channel, ctxhandler);
- nvmap = ch->dev->host->nvmap;
+ nvmap = nvhost_get_host(ch->dev)->nvmap;
register_sets = tegra_gpu_register_sets();
BUG_ON(register_sets == 0 || register_sets > 2);
diff --git a/drivers/video/tegra/host/gr3d/scale3d.c b/drivers/video/tegra/host/gr3d/scale3d.c
index 969d60794242..2d2ba839510e 100644
--- a/drivers/video/tegra/host/gr3d/scale3d.c
+++ b/drivers/video/tegra/host/gr3d/scale3d.c
@@ -520,7 +520,7 @@ static ssize_t enable_3d_scaling_store(struct device *dev,
return count;
}
-static DEVICE_ATTR(enable_3d_scaling, S_IRUGO | S_IWUGO,
+static DEVICE_ATTR(enable_3d_scaling, S_IRUGO | S_IWUSR,
enable_3d_scaling_show, enable_3d_scaling_store);
void nvhost_scale3d_init(struct nvhost_device *d)
diff --git a/drivers/video/tegra/host/host1x/Makefile b/drivers/video/tegra/host/host1x/Makefile
index ba59d870d15b..c3214ffe147b 100644
--- a/drivers/video/tegra/host/host1x/Makefile
+++ b/drivers/video/tegra/host/host1x/Makefile
@@ -4,7 +4,6 @@ EXTRA_CFLAGS += -Idrivers/video/tegra/host
nvhost-host1x-objs = \
host1x_syncpt.o \
- host1x_cpuaccess.o \
host1x_channel.o \
host1x_intr.o \
host1x_cdma.o \
diff --git a/drivers/video/tegra/host/host1x/host1x_cdma.c b/drivers/video/tegra/host/host1x/host1x_cdma.c
index e5a72514c071..65a72801528f 100644
--- a/drivers/video/tegra/host/host1x/host1x_cdma.c
+++ b/drivers/video/tegra/host/host1x/host1x_cdma.c
@@ -3,7 +3,7 @@
*
* Tegra Graphics Host Command DMA
*
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
*/
#include <linux/slab.h>
+#include <linux/platform_device.h>
#include "nvhost_cdma.h"
#include "dev.h"
diff --git a/drivers/video/tegra/host/host1x/host1x_channel.c b/drivers/video/tegra/host/host1x/host1x_channel.c
index 18aa2da5151f..7d34f5da263e 100644
--- a/drivers/video/tegra/host/host1x/host1x_channel.c
+++ b/drivers/video/tegra/host/host1x/host1x_channel.c
@@ -52,7 +52,7 @@ int host1x_channel_submit(struct nvhost_job *job)
{
struct nvhost_hwctx *hwctx_to_save = NULL;
struct nvhost_channel *channel = job->ch;
- struct nvhost_syncpt *sp = &job->ch->dev->host->syncpt;
+ struct nvhost_syncpt *sp = &nvhost_get_host(job->ch->dev)->syncpt;
u32 user_syncpt_incrs = job->syncpt_incrs;
bool need_restore = false;
u32 syncval;
@@ -162,7 +162,7 @@ int host1x_channel_submit(struct nvhost_job *job)
/* gather restore buffer */
if (need_restore) {
nvhost_cdma_push_gather(&channel->cdma,
- channel->dev->host->nvmap,
+ nvhost_get_host(channel->dev)->nvmap,
nvmap_ref_to_handle(channel->cur_ctx->restore),
nvhost_opcode_gather(channel->cur_ctx->restore_size),
channel->cur_ctx->restore_phys);
@@ -222,7 +222,8 @@ int host1x_channel_submit(struct nvhost_job *job)
* if necessary, and to release the restore buffer)
*/
if (hwctx_to_save) {
- err = nvhost_intr_add_action(&channel->dev->host->intr,
+ err = nvhost_intr_add_action(
+ &nvhost_get_host(channel->dev)->intr,
job->syncpt_id,
syncval - job->syncpt_incrs
+ hwctx_to_save->save_thresh,
@@ -235,7 +236,8 @@ int host1x_channel_submit(struct nvhost_job *job)
if (need_restore) {
BUG_ON(!ctxrestore_waiter);
- err = nvhost_intr_add_action(&channel->dev->host->intr,
+ err = nvhost_intr_add_action(
+ &nvhost_get_host(channel->dev)->intr,
job->syncpt_id,
syncval - user_syncpt_incrs,
NVHOST_INTR_ACTION_CTXRESTORE, channel->cur_ctx,
@@ -246,8 +248,8 @@ int host1x_channel_submit(struct nvhost_job *job)
}
/* schedule a submit complete interrupt */
- err = nvhost_intr_add_action(&channel->dev->host->intr, job->syncpt_id,
- syncval,
+ err = nvhost_intr_add_action(&nvhost_get_host(channel->dev)->intr,
+ job->syncpt_id, syncval,
NVHOST_INTR_ACTION_SUBMIT_COMPLETE, channel,
completed_waiter,
NULL);
@@ -293,7 +295,7 @@ int host1x_channel_read_3d_reg(
job = nvhost_job_alloc(channel, hwctx,
NULL,
- channel->dev->host->nvmap, 0, 0);
+ nvhost_get_host(channel->dev)->nvmap, 0, 0);
if (!job) {
err = -ENOMEM;
goto done;
@@ -324,7 +326,7 @@ int host1x_channel_read_3d_reg(
}
}
- syncval = nvhost_syncpt_incr_max(&channel->dev->host->syncpt,
+ syncval = nvhost_syncpt_incr_max(&nvhost_get_host(channel->dev)->syncpt,
NVSYNCPT_3D, syncpt_incrs);
job->syncpt_id = NVSYNCPT_3D;
@@ -390,7 +392,8 @@ int host1x_channel_read_3d_reg(
* if necessary, and to release the restore buffer)
*/
if (hwctx_to_save) {
- err = nvhost_intr_add_action(&channel->dev->host->intr,
+ err = nvhost_intr_add_action(
+ &nvhost_get_host(channel->dev)->intr,
NVSYNCPT_3D,
syncval - syncpt_incrs + hwctx_to_save->save_incrs - 1,
NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save,
@@ -401,27 +404,28 @@ int host1x_channel_read_3d_reg(
}
/* Wait for FIFO to be ready */
- err = nvhost_intr_add_action(&channel->dev->host->intr, NVSYNCPT_3D,
- syncval - 2,
+ err = nvhost_intr_add_action(&nvhost_get_host(channel->dev)->intr,
+ NVSYNCPT_3D, syncval - 2,
NVHOST_INTR_ACTION_WAKEUP, &wq,
read_waiter,
&ref);
read_waiter = NULL;
WARN(err, "Failed to set wakeup interrupt");
wait_event(wq,
- nvhost_syncpt_min_cmp(&channel->dev->host->syncpt,
+ nvhost_syncpt_min_cmp(&nvhost_get_host(channel->dev)->syncpt,
NVSYNCPT_3D, syncval - 2));
- nvhost_intr_put_ref(&channel->dev->host->intr, ref);
+ nvhost_intr_put_ref(&nvhost_get_host(channel->dev)->intr, ref);
/* Read the register value from FIFO */
err = host1x_drain_read_fifo(channel->aperture,
value, 1, &pending);
/* Indicate we've read the value */
- nvhost_syncpt_cpu_incr(&channel->dev->host->syncpt, NVSYNCPT_3D);
+ nvhost_syncpt_cpu_incr(&nvhost_get_host(channel->dev)->syncpt,
+ NVSYNCPT_3D);
/* Schedule a submit complete interrupt */
- err = nvhost_intr_add_action(&channel->dev->host->intr,
+ err = nvhost_intr_add_action(&nvhost_get_host(channel->dev)->intr,
NVSYNCPT_3D, syncval,
NVHOST_INTR_ACTION_SUBMIT_COMPLETE, channel,
completed_waiter, NULL);
@@ -510,7 +514,7 @@ int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id)
job = nvhost_job_alloc(ch, hwctx_to_save,
NULL,
- ch->dev->host->nvmap, 0, 0);
+ nvhost_get_host(ch->dev)->nvmap, 0, 0);
if (IS_ERR_OR_NULL(job)) {
err = PTR_ERR(job);
mutex_unlock(&ch->submitlock);
@@ -522,7 +526,7 @@ int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id)
ch->cur_ctx = NULL;
syncpt_incrs = hwctx_to_save->save_incrs;
- syncpt_val = nvhost_syncpt_incr_max(&ch->dev->host->syncpt,
+ syncpt_val = nvhost_syncpt_incr_max(&nvhost_get_host(ch->dev)->syncpt,
syncpt_id, syncpt_incrs);
job->syncpt_id = syncpt_id;
@@ -540,7 +544,7 @@ int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id)
nvhost_job_put(job);
job = NULL;
- err = nvhost_intr_add_action(&ch->dev->host->intr, syncpt_id,
+ err = nvhost_intr_add_action(&nvhost_get_host(ch->dev)->intr, syncpt_id,
syncpt_val - syncpt_incrs + hwctx_to_save->save_thresh,
NVHOST_INTR_ACTION_CTXSAVE, hwctx_to_save,
ctx_waiter,
@@ -548,7 +552,7 @@ int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id)
ctx_waiter = NULL;
WARN(err, "Failed to set context save interrupt");
- err = nvhost_intr_add_action(&ch->dev->host->intr,
+ err = nvhost_intr_add_action(&nvhost_get_host(ch->dev)->intr,
syncpt_id, syncpt_val,
NVHOST_INTR_ACTION_WAKEUP, &wq,
wakeup_waiter,
@@ -556,10 +560,10 @@ int host1x_save_context(struct nvhost_device *dev, u32 syncpt_id)
wakeup_waiter = NULL;
WARN(err, "Failed to set wakeup interrupt");
wait_event(wq,
- nvhost_syncpt_min_cmp(&ch->dev->host->syncpt,
+ nvhost_syncpt_min_cmp(&nvhost_get_host(ch->dev)->syncpt,
syncpt_id, syncpt_val));
- nvhost_intr_put_ref(&ch->dev->host->intr, ref);
+ nvhost_intr_put_ref(&nvhost_get_host(ch->dev)->intr, ref);
nvhost_cdma_update(&ch->cdma);
diff --git a/drivers/video/tegra/host/host1x/host1x_cpuaccess.c b/drivers/video/tegra/host/host1x/host1x_cpuaccess.c
deleted file mode 100644
index 927f4ca85bdc..000000000000
--- a/drivers/video/tegra/host/host1x/host1x_cpuaccess.c
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * drivers/video/tegra/host/host1x/host1x_cpuaccess.c
- *
- * Tegra Graphics Host Cpu Register Access
- *
- * Copyright (c) 2010-2011, NVIDIA Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "nvhost_cpuaccess.h"
-#include "dev.h"
-#include "host1x_hardware.h"
-
-static int t20_cpuaccess_mutex_try_lock(struct nvhost_cpuaccess *ctx,
- unsigned int idx)
-{
- struct nvhost_master *dev = cpuaccess_to_dev(ctx);
- void __iomem *sync_regs = dev->sync_aperture;
- /* mlock registers returns 0 when the lock is aquired.
- * writing 0 clears the lock. */
- return !!readl(sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4));
-}
-
-static void t20_cpuaccess_mutex_unlock(struct nvhost_cpuaccess *ctx,
- unsigned int idx)
-{
- struct nvhost_master *dev = cpuaccess_to_dev(ctx);
- void __iomem *sync_regs = dev->sync_aperture;
-
- writel(0, sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4));
-}
-
-int nvhost_init_t20_cpuaccess_support(struct nvhost_master *host)
-{
- host->nb_modules = NVHOST_MODULE_NUM;
-
- host->op.cpuaccess.mutex_try_lock = t20_cpuaccess_mutex_try_lock;
- host->op.cpuaccess.mutex_unlock = t20_cpuaccess_mutex_unlock;
-
- return 0;
-}
diff --git a/drivers/video/tegra/host/host1x/host1x_syncpt.c b/drivers/video/tegra/host/host1x/host1x_syncpt.c
index 21390fca7e9d..622c8e049a7b 100644
--- a/drivers/video/tegra/host/host1x/host1x_syncpt.c
+++ b/drivers/video/tegra/host/host1x/host1x_syncpt.c
@@ -3,7 +3,7 @@
*
* Tegra Graphics Host Syncpoints for HOST1X
*
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
*/
#include <linux/nvhost_ioctl.h>
+#include <linux/platform_device.h>
#include "nvhost_syncpt.h"
#include "dev.h"
#include "host1x_syncpt.h"
@@ -220,6 +221,23 @@ static void t20_syncpt_debug(struct nvhost_syncpt *sp)
}
}
+static int syncpt_mutex_try_lock(struct nvhost_syncpt *sp,
+ unsigned int idx)
+{
+ void __iomem *sync_regs = syncpt_to_dev(sp)->sync_aperture;
+ /* mlock registers returns 0 when the lock is aquired.
+ * writing 0 clears the lock. */
+ return !!readl(sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4));
+}
+
+static void syncpt_mutex_unlock(struct nvhost_syncpt *sp,
+ unsigned int idx)
+{
+ void __iomem *sync_regs = syncpt_to_dev(sp)->sync_aperture;
+
+ writel(0, sync_regs + (HOST1X_SYNC_MLOCK_0 + idx * 4));
+}
+
int host1x_init_syncpt_support(struct nvhost_master *host)
{
@@ -235,10 +253,13 @@ int host1x_init_syncpt_support(struct nvhost_master *host)
host->op.syncpt.wait_check = t20_syncpt_wait_check;
host->op.syncpt.debug = t20_syncpt_debug;
host->op.syncpt.name = t20_syncpt_name;
+ host->op.syncpt.mutex_try_lock = syncpt_mutex_try_lock;
+ host->op.syncpt.mutex_unlock = syncpt_mutex_unlock;
host->syncpt.nb_pts = NV_HOST1X_SYNCPT_NB_PTS;
host->syncpt.nb_bases = NV_HOST1X_SYNCPT_NB_BASES;
host->syncpt.client_managed = NVSYNCPTS_CLIENT_MANAGED;
+ host->syncpt.nb_mlocks = NV_HOST1X_SYNC_MLOCK_NUM;
return 0;
}
diff --git a/drivers/video/tegra/host/mpe/mpe.c b/drivers/video/tegra/host/mpe/mpe.c
index 5d497221a671..471702ea48fb 100644
--- a/drivers/video/tegra/host/mpe/mpe.c
+++ b/drivers/video/tegra/host/mpe/mpe.c
@@ -439,7 +439,7 @@ static u32 *save_ram(u32 *ptr, unsigned int *pending,
static struct nvhost_hwctx *ctxmpe_alloc(struct nvhost_channel *ch)
{
- struct nvmap_client *nvmap = ch->dev->host->nvmap;
+ struct nvmap_client *nvmap = nvhost_get_host(ch->dev)->nvmap;
struct nvhost_hwctx *ctx;
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
@@ -482,7 +482,7 @@ static void ctxmpe_get(struct nvhost_hwctx *ctx)
static void ctxmpe_free(struct kref *ref)
{
struct nvhost_hwctx *ctx = container_of(ref, struct nvhost_hwctx, ref);
- struct nvmap_client *nvmap = ctx->channel->dev->host->nvmap;
+ struct nvmap_client *nvmap = nvhost_get_host(ctx->channel->dev)->nvmap;
if (ctx->restore_virt)
nvmap_munmap(ctx->restore, ctx->restore_virt);
@@ -522,7 +522,8 @@ static void ctxmpe_save_service(struct nvhost_hwctx *ctx)
IRFR_RAM_SIZE, IRFR_RAM_READ_CMD, IRFR_RAM_READ_DATA);
wmb();
- nvhost_syncpt_cpu_incr(&ctx->channel->dev->host->syncpt, NVSYNCPT_MPE);
+ nvhost_syncpt_cpu_incr(&nvhost_get_host(ctx->channel->dev)->syncpt,
+ NVSYNCPT_MPE);
}
int __init nvhost_mpe_ctxhandler_init(struct nvhost_hwctx_handler *h)
@@ -532,7 +533,7 @@ int __init nvhost_mpe_ctxhandler_init(struct nvhost_hwctx_handler *h)
u32 *save_ptr;
ch = container_of(h, struct nvhost_channel, ctxhandler);
- nvmap = ch->dev->host->nvmap;
+ nvmap = nvhost_get_host(ch->dev)->nvmap;
setup_save(NULL);
diff --git a/drivers/video/tegra/host/nvhost_acm.c b/drivers/video/tegra/host/nvhost_acm.c
index 7a0108ab5f2f..a2386a257c8f 100644
--- a/drivers/video/tegra/host/nvhost_acm.c
+++ b/drivers/video/tegra/host/nvhost_acm.c
@@ -3,7 +3,7 @@
*
* Tegra Graphics Host Automatic Clock Management
*
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -28,6 +28,7 @@
#include <linux/err.h>
#include <linux/device.h>
#include <linux/delay.h>
+#include <linux/platform_device.h>
#include <mach/powergate.h>
#include <mach/clk.h>
#include <mach/hardware.h>
@@ -339,11 +340,12 @@ void nvhost_module_remove_client(struct nvhost_device *dev, void *priv)
mutex_unlock(&client_list_lock);
}
-void nvhost_module_preinit(struct nvhost_device *dev)
+int nvhost_module_init(struct nvhost_device *dev)
{
int i = 0;
/* initialize clocks to known state */
+ INIT_LIST_HEAD(&dev->client_list);
while (dev->clocks[i].name && i < NVHOST_MODULE_MAX_CLOCKS) {
char devname[MAX_DEVID_LENGTH];
long rate = dev->clocks[i].default_rate;
@@ -357,31 +359,7 @@ void nvhost_module_preinit(struct nvhost_device *dev)
clk_enable(c);
clk_set_rate(c, rate);
clk_disable(c);
- i++;
- }
-
- if (dev->can_powergate) {
- do_powergate_locked(dev->powergate_ids[0]);
- do_powergate_locked(dev->powergate_ids[1]);
- } else {
- do_unpowergate_locked(dev->powergate_ids[0]);
- do_unpowergate_locked(dev->powergate_ids[1]);
- }
-}
-
-int nvhost_module_init(struct nvhost_device *dev)
-{
- int i = 0;
-
- nvhost_module_preinit(dev);
-
- INIT_LIST_HEAD(&dev->client_list);
- while (dev->clocks[i].name && i < NVHOST_MODULE_MAX_CLOCKS) {
- char devname[MAX_DEVID_LENGTH];
-
- snprintf(devname, MAX_DEVID_LENGTH, "tegra_%s", dev->name);
- dev->clk[i] = clk_get_sys(devname, dev->clocks[i].name);
- BUG_ON(IS_ERR_OR_NULL(dev->clk[i]));
+ dev->clk[i] = c;
i++;
}
dev->num_clks = i;
@@ -390,13 +368,16 @@ int nvhost_module_init(struct nvhost_device *dev)
init_waitqueue_head(&dev->idle_wq);
INIT_DELAYED_WORK(&dev->powerstate_down, powerstate_down_handler);
- if (dev->can_powergate)
+ /* power gate units that we can power gate */
+ if (dev->can_powergate) {
+ do_powergate_locked(dev->powergate_ids[0]);
+ do_powergate_locked(dev->powergate_ids[1]);
dev->powerstate = NVHOST_POWER_STATE_POWERGATED;
- else
+ } else {
+ do_unpowergate_locked(dev->powergate_ids[0]);
+ do_unpowergate_locked(dev->powergate_ids[1]);
dev->powerstate = NVHOST_POWER_STATE_CLOCKGATED;
-
- if (dev->init)
- dev->init(dev);
+ }
return 0;
}
@@ -425,8 +406,8 @@ static void debug_not_idle(struct nvhost_master *host)
mutex_unlock(&dev->lock);
}
- for (i = 0; i < host->nb_mlocks; i++) {
- int c = atomic_read(&host->cpuaccess.lock_counts[i]);
+ for (i = 0; i < host->syncpt.nb_mlocks; i++) {
+ int c = atomic_read(&host->syncpt.lock_counts[i]);
if (c) {
dev_warn(&host->pdev->dev,
"tegra_grhost: lock id %d: refcnt %d\n",
@@ -441,15 +422,10 @@ static void debug_not_idle(struct nvhost_master *host)
int nvhost_module_suspend(struct nvhost_device *dev, bool system_suspend)
{
int ret;
- struct nvhost_master *host;
+ struct nvhost_master *host = nvhost_get_host(dev);
- if (system_suspend) {
- host = dev->host;
- if (!is_module_idle(dev))
- debug_not_idle(host);
- } else {
- host = dev->host;
- }
+ if (system_suspend && !is_module_idle(dev))
+ debug_not_idle(host);
ret = wait_event_timeout(dev->idle_wq, is_module_idle(dev),
ACM_SUSPEND_WAIT_FOR_IDLE_TIMEOUT);
diff --git a/drivers/video/tegra/host/nvhost_acm.h b/drivers/video/tegra/host/nvhost_acm.h
index 3e1636f44176..06a11ff840f2 100644
--- a/drivers/video/tegra/host/nvhost_acm.h
+++ b/drivers/video/tegra/host/nvhost_acm.h
@@ -3,7 +3,7 @@
*
* Tegra Graphics Host Automatic Clock Management
*
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -30,7 +30,6 @@
#include <linux/nvhost.h>
/* Sets clocks and powergating state for a module */
-void nvhost_module_preinit(struct nvhost_device *ndev);
int nvhost_module_init(struct nvhost_device *ndev);
void nvhost_module_deinit(struct nvhost_device *dev);
int nvhost_module_suspend(struct nvhost_device *dev, bool system_suspend);
diff --git a/drivers/video/tegra/host/nvhost_cdma.c b/drivers/video/tegra/host/nvhost_cdma.c
index bddb421aa7f2..6446befbdd58 100644
--- a/drivers/video/tegra/host/nvhost_cdma.c
+++ b/drivers/video/tegra/host/nvhost_cdma.c
@@ -3,7 +3,7 @@
*
* Tegra Graphics Host Command DMA
*
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -322,6 +322,7 @@ void nvhost_cdma_update_sync_queue(struct nvhost_cdma *cdma,
job->syncpt_end,
job->num_slots);
+ syncpt_val += syncpt_incrs;
kfifo_skip(&cdma->sync_queue);
result = kfifo_peek(&cdma->sync_queue, &job);
}
diff --git a/drivers/video/tegra/host/nvhost_cdma.h b/drivers/video/tegra/host/nvhost_cdma.h
index 39142ca5cd83..a5ef8da29e78 100644
--- a/drivers/video/tegra/host/nvhost_cdma.h
+++ b/drivers/video/tegra/host/nvhost_cdma.h
@@ -108,7 +108,7 @@ struct nvhost_cdma {
};
#define cdma_to_channel(cdma) container_of(cdma, struct nvhost_channel, cdma)
-#define cdma_to_dev(cdma) ((cdma_to_channel(cdma))->dev->host)
+#define cdma_to_dev(cdma) nvhost_get_host(cdma_to_channel(cdma)->dev)
#define cdma_op(cdma) (cdma_to_dev(cdma)->op.cdma)
#define cdma_to_nvmap(cdma) ((cdma_to_dev(cdma))->nvmap)
#define pb_to_cdma(pb) container_of(pb, struct nvhost_cdma, push_buffer)
diff --git a/drivers/video/tegra/host/nvhost_channel.c b/drivers/video/tegra/host/nvhost_channel.c
index 6d39eba643c4..c7b0c82b3e45 100644
--- a/drivers/video/tegra/host/nvhost_channel.c
+++ b/drivers/video/tegra/host/nvhost_channel.c
@@ -3,7 +3,7 @@
*
* Tegra Graphics Host Channel
*
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -32,6 +32,57 @@
#define NVHOST_CHANNEL_LOW_PRIO_MAX_WAIT 50
+int nvhost_channel_init(struct nvhost_channel *ch,
+ struct nvhost_master *dev, int index)
+{
+ int err;
+ struct nvhost_device *ndev;
+ struct resource *r = NULL;
+ void __iomem *regs = NULL;
+ struct resource *reg_mem = NULL;
+
+ /* Link nvhost_device to nvhost_channel */
+ err = host_channel_op(dev).init(ch, dev, index);
+ if (err < 0) {
+ dev_err(&dev->dev->dev, "failed to init channel %d\n",
+ index);
+ return err;
+ }
+ ndev = ch->dev;
+ ndev->channel = ch;
+
+ /* Map IO memory related to nvhost_device */
+ if (ndev->moduleid != NVHOST_MODULE_NONE) {
+ /* First one is host1x - skip that */
+ r = platform_get_resource(dev->pdev,
+ IORESOURCE_MEM, ndev->moduleid + 1);
+ if (!r)
+ goto fail;
+
+ reg_mem = request_mem_region(r->start,
+ resource_size(r), ndev->name);
+ if (!reg_mem)
+ goto fail;
+
+ regs = ioremap(r->start, resource_size(r));
+ if (!regs)
+ goto fail;
+
+ ndev->reg_mem = reg_mem;
+ ndev->aperture = regs;
+ }
+ return 0;
+
+fail:
+ if (reg_mem)
+ release_mem_region(r->start, resource_size(r));
+ if (regs)
+ iounmap(regs);
+ dev_err(&ndev->dev, "failed to get register memory\n");
+ return -ENXIO;
+
+}
+
int nvhost_channel_submit(struct nvhost_job *job)
{
/* Low priority submits wait until sync queue is empty. Ignores result
@@ -49,12 +100,9 @@ struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch)
int err = 0;
mutex_lock(&ch->reflock);
if (ch->refcount == 0) {
- err = nvhost_module_init(ch->dev);
- if (!err) {
- err = nvhost_cdma_init(&ch->cdma);
- if (err)
- nvhost_module_deinit(ch->dev);
- }
+ if (ch->dev->init)
+ ch->dev->init(ch->dev);
+ err = nvhost_cdma_init(&ch->cdma);
} else if (ch->dev->exclusive) {
err = -EBUSY;
}
@@ -89,7 +137,7 @@ void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx)
if (ch->refcount == 1) {
channel_cdma_op(ch).stop(&ch->cdma);
nvhost_cdma_deinit(&ch->cdma);
- nvhost_module_deinit(ch->dev);
+ nvhost_module_suspend(ch->dev, false);
}
ch->refcount--;
mutex_unlock(&ch->reflock);
diff --git a/drivers/video/tegra/host/nvhost_channel.h b/drivers/video/tegra/host/nvhost_channel.h
index f6c22cac963d..251fa94ee18f 100644
--- a/drivers/video/tegra/host/nvhost_channel.h
+++ b/drivers/video/tegra/host/nvhost_channel.h
@@ -72,8 +72,8 @@ struct nvhost_channel *nvhost_getchannel(struct nvhost_channel *ch);
void nvhost_putchannel(struct nvhost_channel *ch, struct nvhost_hwctx *ctx);
int nvhost_channel_suspend(struct nvhost_channel *ch);
-#define channel_cdma_op(ch) (ch->dev->host->op.cdma)
-#define channel_op(ch) (ch->dev->host->op.channel)
+#define channel_cdma_op(ch) (nvhost_get_host(ch->dev)->op.cdma)
+#define channel_op(ch) (nvhost_get_host(ch->dev)->op.channel)
#define host_channel_op(host) (host->op.channel)
int nvhost_channel_drain_read_fifo(void __iomem *chan_regs,
diff --git a/drivers/video/tegra/host/nvhost_cpuaccess.c b/drivers/video/tegra/host/nvhost_cpuaccess.c
deleted file mode 100644
index 0c7d0a4a98dc..000000000000
--- a/drivers/video/tegra/host/nvhost_cpuaccess.c
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * drivers/video/tegra/host/nvhost_cpuaccess.c
- *
- * Tegra Graphics Host Cpu Register Access
- *
- * Copyright (c) 2010, NVIDIA Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#include "nvhost_cpuaccess.h"
-#include "dev.h"
-#include <linux/string.h>
-
-int nvhost_cpuaccess_init(struct nvhost_cpuaccess *ctx,
- struct platform_device *pdev)
-{
- struct nvhost_master *host = cpuaccess_to_dev(ctx);
- int i;
-
- for (i = 0; i < host->nb_modules; i++) {
- struct resource *mem;
- mem = platform_get_resource(pdev, IORESOURCE_MEM, i+1);
- if (!mem) {
- dev_err(&pdev->dev, "missing module memory resource\n");
- return -ENXIO;
- }
- ctx->reg_mem[i] = mem;
- ctx->regs[i] = ioremap(mem->start, resource_size(mem));
- if (!ctx->regs[i]) {
- dev_err(&pdev->dev, "failed to map module registers\n");
- return -ENXIO;
- }
- }
-
- return 0;
-}
-
-void nvhost_cpuaccess_deinit(struct nvhost_cpuaccess *ctx)
-{
- struct nvhost_master *host = cpuaccess_to_dev(ctx);
- int i;
-
- for (i = 0; i < host->nb_modules; i++) {
- iounmap(ctx->regs[i]);
- release_resource(ctx->reg_mem[i]);
- }
-}
-
-int nvhost_mutex_try_lock(struct nvhost_cpuaccess *ctx, unsigned int idx)
-{
- struct nvhost_master *dev = cpuaccess_to_dev(ctx);
- u32 reg;
- BUG_ON(!cpuaccess_op(ctx).mutex_try_lock);
-
- nvhost_module_busy(dev->dev);
- reg = cpuaccess_op(ctx).mutex_try_lock(ctx, idx);
- if (reg) {
- nvhost_module_idle(dev->dev);
- return -EBUSY;
- }
- atomic_inc(&ctx->lock_counts[idx]);
- return 0;
-}
-
-void nvhost_mutex_unlock(struct nvhost_cpuaccess *ctx, unsigned int idx)
-{
- struct nvhost_master *dev = cpuaccess_to_dev(ctx);
- BUG_ON(!cpuaccess_op(ctx).mutex_unlock);
-
- cpuaccess_op(ctx).mutex_unlock(ctx, idx);
- nvhost_module_idle(dev->dev);
- atomic_dec(&ctx->lock_counts[idx]);
-}
-
-void nvhost_read_module_regs(struct nvhost_cpuaccess *ctx, u32 module,
- u32 offset, size_t size, void *values)
-{
- struct nvhost_master *dev = cpuaccess_to_dev(ctx);
- void __iomem *p = ctx->regs[module] + offset;
- u32 *out = (u32 *)values;
- BUG_ON(size & 3);
- size >>= 2;
- nvhost_module_busy(dev->dev);
- while (size--) {
- *(out++) = readl(p);
- p += 4;
- }
- rmb();
- nvhost_module_idle(dev->dev);
-}
-
-void nvhost_write_module_regs(struct nvhost_cpuaccess *ctx, u32 module,
- u32 offset, size_t size, const void *values)
-{
- struct nvhost_master *dev = cpuaccess_to_dev(ctx);
- void __iomem *p = ctx->regs[module] + offset;
- const u32 *in = (const u32 *)values;
- BUG_ON(size & 3);
- size >>= 2;
- nvhost_module_busy(dev->dev);
- while (size--) {
- writel(*(in++), p);
- p += 4;
- }
- wmb();
- nvhost_module_idle(dev->dev);
-}
diff --git a/drivers/video/tegra/host/nvhost_cpuaccess.h b/drivers/video/tegra/host/nvhost_cpuaccess.h
deleted file mode 100644
index 2e210b7477af..000000000000
--- a/drivers/video/tegra/host/nvhost_cpuaccess.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * drivers/video/tegra/host/nvhost_cpuaccess.h
- *
- * Tegra Graphics Host Cpu Register Access
- *
- * Copyright (c) 2010, NVIDIA Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-
-#ifndef __NVHOST_CPUACCESS_H
-#define __NVHOST_CPUACCESS_H
-
-#include <linux/platform_device.h>
-#include <linux/io.h>
-
-enum nvhost_module_id {
- NVHOST_MODULE_DISPLAY_A = 0,
- NVHOST_MODULE_DISPLAY_B,
- NVHOST_MODULE_VI,
- NVHOST_MODULE_ISP,
- NVHOST_MODULE_MPE,
-#if 0
- /* TODO: [ahatala 2010-07-02] find out if these are needed */
- NVHOST_MODULE_FUSE,
- NVHOST_MODULE_APB_MISC,
- NVHOST_MODULE_CLK_RESET,
-#endif
- NVHOST_MODULE_NUM
-};
-
-struct nvhost_cpuaccess {
- struct resource **reg_mem;
- void __iomem **regs;
- atomic_t *lock_counts;
-};
-
-#define cpuaccess_to_dev(ctx) container_of(ctx, struct nvhost_master, cpuaccess)
-#define cpuaccess_op(ctx) (cpuaccess_to_dev(ctx)->op.cpuaccess)
-int nvhost_cpuaccess_init(struct nvhost_cpuaccess *ctx,
- struct platform_device *pdev);
-
-int nvhost_mutex_try_lock(struct nvhost_cpuaccess *ctx, unsigned int idx);
-
-void nvhost_mutex_unlock(struct nvhost_cpuaccess *ctx, unsigned int idx);
-
-void nvhost_read_module_regs(struct nvhost_cpuaccess *ctx, u32 module,
- u32 offset, size_t size, void *values);
-
-void nvhost_write_module_regs(struct nvhost_cpuaccess *ctx, u32 module,
- u32 offset, size_t size, const void *values);
-
-#endif
diff --git a/drivers/video/tegra/host/nvhost_syncpt.c b/drivers/video/tegra/host/nvhost_syncpt.c
index f212e618f950..04c6e917a84c 100644
--- a/drivers/video/tegra/host/nvhost_syncpt.c
+++ b/drivers/video/tegra/host/nvhost_syncpt.c
@@ -3,7 +3,7 @@
*
* Tegra Graphics Host Syncpoints
*
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
*/
#include <linux/nvhost_ioctl.h>
+#include <linux/platform_device.h>
#include "nvhost_syncpt.h"
#include "dev.h"
@@ -235,6 +236,28 @@ void nvhost_syncpt_debug(struct nvhost_syncpt *sp)
syncpt_op(sp).debug(sp);
}
+int nvhost_mutex_try_lock(struct nvhost_syncpt *sp, int idx)
+{
+ struct nvhost_master *host = syncpt_to_dev(sp);
+ u32 reg;
+
+ nvhost_module_busy(host->dev);
+ reg = syncpt_op(sp).mutex_try_lock(sp, idx);
+ if (reg) {
+ nvhost_module_idle(host->dev);
+ return -EBUSY;
+ }
+ atomic_inc(&sp->lock_counts[idx]);
+ return 0;
+}
+
+void nvhost_mutex_unlock(struct nvhost_syncpt *sp, int idx)
+{
+ syncpt_op(sp).mutex_unlock(sp, idx);
+ nvhost_module_idle(syncpt_to_dev(sp)->dev);
+ atomic_dec(&sp->lock_counts[idx]);
+}
+
/* check for old WAITs to be removed (avoiding a wrap) */
int nvhost_syncpt_wait_check(struct nvhost_syncpt *sp,
struct nvmap_client *nvmap,
diff --git a/drivers/video/tegra/host/nvhost_syncpt.h b/drivers/video/tegra/host/nvhost_syncpt.h
index 0dfb11775980..1f22a6d2742e 100644
--- a/drivers/video/tegra/host/nvhost_syncpt.h
+++ b/drivers/video/tegra/host/nvhost_syncpt.h
@@ -3,7 +3,7 @@
*
* Tegra Graphics Host Syncpoints
*
- * Copyright (c) 2010-2011, NVIDIA Corporation.
+ * Copyright (c) 2010-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -43,6 +43,8 @@ struct nvhost_syncpt {
u32 nb_pts;
u32 nb_bases;
u32 client_managed;
+ atomic_t *lock_counts;
+ u32 nb_mlocks;
};
int nvhost_syncpt_init(struct nvhost_syncpt *);
@@ -159,4 +161,8 @@ int nvhost_syncpt_wait_check(struct nvhost_syncpt *sp,
void nvhost_syncpt_debug(struct nvhost_syncpt *sp);
+int nvhost_mutex_try_lock(struct nvhost_syncpt *sp, int idx);
+
+void nvhost_mutex_unlock(struct nvhost_syncpt *sp, int idx);
+
#endif
diff --git a/drivers/video/tegra/host/t20/t20.c b/drivers/video/tegra/host/t20/t20.c
index 6983f1cc3fbd..a08728c4a0df 100644
--- a/drivers/video/tegra/host/t20/t20.c
+++ b/drivers/video/tegra/host/t20/t20.c
@@ -3,7 +3,7 @@
*
* Tegra Graphics Init for T20 Architecture Chips
*
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2011-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -56,6 +56,7 @@ struct nvhost_device devices[] = {
.modulemutexes = BIT(NVMODMUTEX_DISPLAYA) | BIT(NVMODMUTEX_DISPLAYB),
NVHOST_MODULE_NO_POWERGATE_IDS,
NVHOST_DEFAULT_CLOCKGATE_DELAY,
+ .moduleid = NVHOST_MODULE_NONE,
},
{
/* channel 1 */
@@ -69,6 +70,7 @@ struct nvhost_device devices[] = {
.clocks = {{"gr3d", UINT_MAX}, {"emc", UINT_MAX}, {} },
.powergate_ids = {TEGRA_POWERGATE_3D, -1},
NVHOST_DEFAULT_CLOCKGATE_DELAY,
+ .moduleid = NVHOST_MODULE_NONE,
},
{
/* channel 2 */
@@ -83,6 +85,7 @@ struct nvhost_device devices[] = {
{"emc", UINT_MAX} },
NVHOST_MODULE_NO_POWERGATE_IDS,
.clockgate_delay = 0,
+ .moduleid = NVHOST_MODULE_NONE,
},
{
/* channel 3 */
@@ -91,6 +94,7 @@ struct nvhost_device devices[] = {
.syncpts = 0,
NVHOST_MODULE_NO_POWERGATE_IDS,
NVHOST_DEFAULT_CLOCKGATE_DELAY,
+ .moduleid = NVHOST_MODULE_ISP,
},
{
/* channel 4 */
@@ -104,6 +108,7 @@ struct nvhost_device devices[] = {
.exclusive = true,
NVHOST_MODULE_NO_POWERGATE_IDS,
NVHOST_DEFAULT_CLOCKGATE_DELAY,
+ .moduleid = NVHOST_MODULE_VI,
},
{
/* channel 5 */
@@ -119,6 +124,7 @@ struct nvhost_device devices[] = {
.clocks = {{"mpe", UINT_MAX}, {"emc", UINT_MAX}, {} },
.powergate_ids = {TEGRA_POWERGATE_MPE, -1},
NVHOST_DEFAULT_CLOCKGATE_DELAY,
+ .moduleid = NVHOST_MODULE_MPE,
},
{
/* channel 6 */
@@ -128,6 +134,7 @@ struct nvhost_device devices[] = {
.modulemutexes = BIT(NVMODMUTEX_DSI),
NVHOST_MODULE_NO_POWERGATE_IDS,
NVHOST_DEFAULT_CLOCKGATE_DELAY,
+ .moduleid = NVHOST_MODULE_NONE,
} };
static inline void __iomem *t20_channel_aperture(void __iomem *p, int ndx)
@@ -164,7 +171,6 @@ static int t20_channel_init(struct nvhost_channel *ch,
int nvhost_init_t20_channel_support(struct nvhost_master *host)
{
- host->nb_mlocks = NV_HOST1X_SYNC_MLOCK_NUM;
host->nb_channels = NVHOST_NUMCHANNELS;
host->op.channel.init = t20_channel_init;
@@ -194,8 +200,5 @@ int nvhost_init_t20_support(struct nvhost_master *host)
err = nvhost_init_t20_intr_support(host);
if (err)
return err;
- err = nvhost_init_t20_cpuaccess_support(host);
- if (err)
- return err;
return 0;
}
diff --git a/drivers/video/tegra/host/t20/t20.h b/drivers/video/tegra/host/t20/t20.h
index 20bb13691321..4a8a8e6324fd 100644
--- a/drivers/video/tegra/host/t20/t20.h
+++ b/drivers/video/tegra/host/t20/t20.h
@@ -3,7 +3,7 @@
*
* Tegra Graphics Chip support for T20
*
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2011-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -29,7 +29,6 @@ int nvhost_init_t20_channel_support(struct nvhost_master *);
int nvhost_init_t20_debug_support(struct nvhost_master *);
int nvhost_init_t20_syncpt_support(struct nvhost_master *);
int nvhost_init_t20_intr_support(struct nvhost_master *);
-int nvhost_init_t20_cpuaccess_support(struct nvhost_master *);
int nvhost_init_t20_support(struct nvhost_master *host);
int nvhost_t20_save_context(struct nvhost_module *mod, u32 syncpt_id);
diff --git a/drivers/video/tegra/host/t30/t30.c b/drivers/video/tegra/host/t30/t30.c
index 7e1a09394c03..a6fb142793b4 100644
--- a/drivers/video/tegra/host/t30/t30.c
+++ b/drivers/video/tegra/host/t30/t30.c
@@ -3,7 +3,7 @@
*
* Tegra Graphics Init for T30 Architecture Chips
*
- * Copyright (c) 2011, NVIDIA Corporation.
+ * Copyright (c) 2011-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -22,6 +22,7 @@
#include <linux/mutex.h>
#include <mach/powergate.h>
+#include <mach/iomap.h>
#include "dev.h"
#include "t20/t20.h"
#include "t30.h"
@@ -60,6 +61,7 @@ static struct nvhost_device devices[] = {
.modulemutexes = BIT(NVMODMUTEX_DISPLAYA) | BIT(NVMODMUTEX_DISPLAYB),
NVHOST_MODULE_NO_POWERGATE_IDS,
NVHOST_DEFAULT_CLOCKGATE_DELAY,
+ .moduleid = NVHOST_MODULE_NONE,
},
{
/* channel 1 */
@@ -83,6 +85,7 @@ static struct nvhost_device devices[] = {
NVHOST_DEFAULT_CLOCKGATE_DELAY,
.can_powergate = true,
.powergate_delay = 100,
+ .moduleid = NVHOST_MODULE_NONE,
},
{
/* channel 2 */
@@ -97,6 +100,7 @@ static struct nvhost_device devices[] = {
{"emc", 300000000} },
NVHOST_MODULE_NO_POWERGATE_IDS,
.clockgate_delay = 0,
+ .moduleid = NVHOST_MODULE_NONE,
},
{
/* channel 3 */
@@ -105,6 +109,7 @@ static struct nvhost_device devices[] = {
.syncpts = 0,
NVHOST_MODULE_NO_POWERGATE_IDS,
NVHOST_DEFAULT_CLOCKGATE_DELAY,
+ .moduleid = NVHOST_MODULE_ISP,
},
{
/* channel 4 */
@@ -118,6 +123,7 @@ static struct nvhost_device devices[] = {
.exclusive = true,
NVHOST_MODULE_NO_POWERGATE_IDS,
NVHOST_DEFAULT_CLOCKGATE_DELAY,
+ .moduleid = NVHOST_MODULE_VI,
},
{
/* channel 5 */
@@ -135,6 +141,7 @@ static struct nvhost_device devices[] = {
NVHOST_DEFAULT_CLOCKGATE_DELAY,
.can_powergate = true,
.powergate_delay = 100,
+ .moduleid = NVHOST_MODULE_MPE,
},
{
/* channel 6 */
@@ -144,6 +151,7 @@ static struct nvhost_device devices[] = {
.modulemutexes = BIT(NVMODMUTEX_DSI),
NVHOST_MODULE_NO_POWERGATE_IDS,
NVHOST_DEFAULT_CLOCKGATE_DELAY,
+ .moduleid = NVHOST_MODULE_NONE,
} };
#define NVHOST_CHANNEL_BASE 0
@@ -217,8 +225,5 @@ int nvhost_init_t30_support(struct nvhost_master *host)
err = nvhost_init_t20_intr_support(host);
if (err)
return err;
- err = nvhost_init_t20_cpuaccess_support(host);
- if (err)
- return err;
return 0;
}
diff --git a/drivers/watchdog/tegra_wdt.c b/drivers/watchdog/tegra_wdt.c
index d873eccfcd4b..12266049e160 100644
--- a/drivers/watchdog/tegra_wdt.c
+++ b/drivers/watchdog/tegra_wdt.c
@@ -91,6 +91,11 @@ static void tegra_wdt_disable(struct tegra_wdt *wdt)
writel(0, wdt->wdt_timer + TIMER_PTV);
}
+static inline void tegra_wdt_ping(struct tegra_wdt *wdt)
+{
+ return;
+}
+
static irqreturn_t tegra_wdt_interrupt(int irq, void *dev_id)
{
struct tegra_wdt *wdt = dev_id;
@@ -134,15 +139,21 @@ static void tegra_wdt_set_timeout(struct tegra_wdt *wdt, int sec)
}
}
+static inline void tegra_wdt_ping(struct tegra_wdt *wdt)
+{
+ writel(WDT_CMD_START_COUNTER, wdt->wdt_source + WDT_CMD);
+}
+
static void tegra_wdt_enable(struct tegra_wdt *wdt)
{
u32 val;
+ writel(TIMER_PCR_INTR, wdt->wdt_timer + TIMER_PCR);
val = (wdt->timeout * 1000000ul) / 4;
val |= (TIMER_EN | TIMER_PERIODIC);
writel(val, wdt->wdt_timer + TIMER_PTV);
- val = WDT_CFG_TMR_SRC | WDT_CFG_PERIOD | WDT_CFG_INT_EN |
+ val = WDT_CFG_TMR_SRC | WDT_CFG_PERIOD | /*WDT_CFG_INT_EN |*/
/*WDT_CFG_SYS_RST_EN |*/ WDT_CFG_PMC2CAR_RST_EN;
writel(val, wdt->wdt_source + WDT_CFG);
writel(WDT_CMD_START_COUNTER, wdt->wdt_source + WDT_CMD);
@@ -160,7 +171,7 @@ static irqreturn_t tegra_wdt_interrupt(int irq, void *dev_id)
{
struct tegra_wdt *wdt = dev_id;
- writel(WDT_CMD_START_COUNTER, wdt->wdt_source + WDT_CMD);
+ tegra_wdt_ping(wdt);
return IRQ_HANDLED;
}
#endif
@@ -222,6 +233,9 @@ static long tegra_wdt_ioctl(struct file *file, unsigned int cmd,
return put_user(0, (int __user *)arg);
case WDIOC_KEEPALIVE:
+ spin_lock(&lock);
+ tegra_wdt_ping(wdt);
+ spin_unlock(&lock);
return 0;
case WDIOC_SETTIMEOUT:
diff --git a/drivers/xen/xenbus/xenbus_xs.c b/drivers/xen/xenbus/xenbus_xs.c
index 5534690075af..daee5db4bef8 100644
--- a/drivers/xen/xenbus/xenbus_xs.c
+++ b/drivers/xen/xenbus/xenbus_xs.c
@@ -801,6 +801,12 @@ static int process_msg(void)
goto out;
}
+ if (msg->hdr.len > XENSTORE_PAYLOAD_MAX) {
+ kfree(msg);
+ err = -EINVAL;
+ goto out;
+ }
+
body = kmalloc(msg->hdr.len + 1, GFP_NOIO | __GFP_HIGH);
if (body == NULL) {
kfree(msg);
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index 59cef1123053..40bfe8dc502d 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -1984,17 +1984,16 @@ static int ext4_fill_flex_info(struct super_block *sb)
struct ext4_group_desc *gdp = NULL;
ext4_group_t flex_group_count;
ext4_group_t flex_group;
- int groups_per_flex = 0;
+ unsigned int groups_per_flex = 0;
size_t size;
int i;
sbi->s_log_groups_per_flex = sbi->s_es->s_log_groups_per_flex;
- groups_per_flex = 1 << sbi->s_log_groups_per_flex;
-
- if (groups_per_flex < 2) {
+ if (sbi->s_log_groups_per_flex < 1 || sbi->s_log_groups_per_flex > 31) {
sbi->s_log_groups_per_flex = 0;
return 1;
}
+ groups_per_flex = 1 << sbi->s_log_groups_per_flex;
/* We allocate both existing and potentially added groups */
flex_group_count = ((sbi->s_groups_count + groups_per_flex - 1) +
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index 43926add945b..54cea8ad5a76 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -339,7 +339,7 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args)
dprintk("%s enter. slotid %d seqid %d\n",
__func__, args->csa_slotid, args->csa_sequenceid);
- if (args->csa_slotid > NFS41_BC_MAX_CALLBACKS)
+ if (args->csa_slotid >= NFS41_BC_MAX_CALLBACKS)
return htonl(NFS4ERR_BADSLOT);
slot = tbl->slots + args->csa_slotid;
diff --git a/fs/nfs/file.c b/fs/nfs/file.c
index babaf3a4d99c..b76be2fb5730 100644
--- a/fs/nfs/file.c
+++ b/fs/nfs/file.c
@@ -321,13 +321,13 @@ nfs_file_fsync(struct file *file, loff_t start, loff_t end, int datasync)
datasync);
ret = filemap_write_and_wait_range(inode->i_mapping, start, end);
- if (ret)
- return ret;
mutex_lock(&inode->i_mutex);
nfs_inc_stats(inode, NFSIOS_VFSFSYNC);
have_error = test_and_clear_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
status = nfs_commit_inode(inode, FLUSH_SYNC);
+ if (status >= 0 && ret < 0)
+ status = ret;
have_error |= test_bit(NFS_CONTEXT_ERROR_WRITE, &ctx->flags);
if (have_error)
ret = xchg(&ctx->error, 0);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 2d8a1693ed0b..003cb6955a28 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -3442,19 +3442,6 @@ static inline int nfs4_server_supports_acls(struct nfs_server *server)
*/
#define NFS4ACL_MAXPAGES (XATTR_SIZE_MAX >> PAGE_CACHE_SHIFT)
-static void buf_to_pages(const void *buf, size_t buflen,
- struct page **pages, unsigned int *pgbase)
-{
- const void *p = buf;
-
- *pgbase = offset_in_page(buf);
- p -= *pgbase;
- while (p < buf + buflen) {
- *(pages++) = virt_to_page(p);
- p += PAGE_CACHE_SIZE;
- }
-}
-
static int buf_to_pages_noslab(const void *buf, size_t buflen,
struct page **pages, unsigned int *pgbase)
{
@@ -3551,9 +3538,19 @@ out:
nfs4_set_cached_acl(inode, acl);
}
+/*
+ * The getxattr API returns the required buffer length when called with a
+ * NULL buf. The NFSv4 acl tool then calls getxattr again after allocating
+ * the required buf. On a NULL buf, we send a page of data to the server
+ * guessing that the ACL request can be serviced by a page. If so, we cache
+ * up to the page of ACL data, and the 2nd call to getxattr is serviced by
+ * the cache. If not so, we throw away the page, and cache the required
+ * length. The next getxattr call will then produce another round trip to
+ * the server, this time with the input buf of the required size.
+ */
static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t buflen)
{
- struct page *pages[NFS4ACL_MAXPAGES];
+ struct page *pages[NFS4ACL_MAXPAGES] = {NULL, };
struct nfs_getaclargs args = {
.fh = NFS_FH(inode),
.acl_pages = pages,
@@ -3568,41 +3565,60 @@ static ssize_t __nfs4_get_acl_uncached(struct inode *inode, void *buf, size_t bu
.rpc_argp = &args,
.rpc_resp = &res,
};
- struct page *localpage = NULL;
- int ret;
+ int ret = -ENOMEM, npages, i, acl_len = 0;
- if (buflen < PAGE_SIZE) {
- /* As long as we're doing a round trip to the server anyway,
- * let's be prepared for a page of acl data. */
- localpage = alloc_page(GFP_KERNEL);
- resp_buf = page_address(localpage);
- if (localpage == NULL)
- return -ENOMEM;
- args.acl_pages[0] = localpage;
- args.acl_pgbase = 0;
- args.acl_len = PAGE_SIZE;
- } else {
- resp_buf = buf;
- buf_to_pages(buf, buflen, args.acl_pages, &args.acl_pgbase);
+ npages = (buflen + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ /* As long as we're doing a round trip to the server anyway,
+ * let's be prepared for a page of acl data. */
+ if (npages == 0)
+ npages = 1;
+
+ for (i = 0; i < npages; i++) {
+ pages[i] = alloc_page(GFP_KERNEL);
+ if (!pages[i])
+ goto out_free;
+ }
+ if (npages > 1) {
+ /* for decoding across pages */
+ args.acl_scratch = alloc_page(GFP_KERNEL);
+ if (!args.acl_scratch)
+ goto out_free;
}
- ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode), &msg, &args.seq_args, &res.seq_res, 0);
+ args.acl_len = npages * PAGE_SIZE;
+ args.acl_pgbase = 0;
+ /* Let decode_getfacl know not to fail if the ACL data is larger than
+ * the page we send as a guess */
+ if (buf == NULL)
+ res.acl_flags |= NFS4_ACL_LEN_REQUEST;
+ resp_buf = page_address(pages[0]);
+
+ dprintk("%s buf %p buflen %ld npages %d args.acl_len %ld\n",
+ __func__, buf, buflen, npages, args.acl_len);
+ ret = nfs4_call_sync(NFS_SERVER(inode)->client, NFS_SERVER(inode),
+ &msg, &args.seq_args, &res.seq_res, 0);
if (ret)
goto out_free;
- if (res.acl_len > args.acl_len)
- nfs4_write_cached_acl(inode, NULL, res.acl_len);
+
+ acl_len = res.acl_len - res.acl_data_offset;
+ if (acl_len > args.acl_len)
+ nfs4_write_cached_acl(inode, NULL, acl_len);
else
- nfs4_write_cached_acl(inode, resp_buf, res.acl_len);
+ nfs4_write_cached_acl(inode, resp_buf + res.acl_data_offset,
+ acl_len);
if (buf) {
ret = -ERANGE;
- if (res.acl_len > buflen)
+ if (acl_len > buflen)
goto out_free;
- if (localpage)
- memcpy(buf, resp_buf, res.acl_len);
+ _copy_from_pages(buf, pages, res.acl_data_offset,
+ res.acl_len);
}
- ret = res.acl_len;
+ ret = acl_len;
out_free:
- if (localpage)
- __free_page(localpage);
+ for (i = 0; i < npages; i++)
+ if (pages[i])
+ __free_page(pages[i]);
+ if (args.acl_scratch)
+ __free_page(args.acl_scratch);
return ret;
}
@@ -3633,6 +3649,8 @@ static ssize_t nfs4_proc_get_acl(struct inode *inode, void *buf, size_t buflen)
nfs_zap_acl_cache(inode);
ret = nfs4_read_cached_acl(inode, buf, buflen);
if (ret != -ENOENT)
+ /* -ENOENT is returned if there is no ACL or if there is an ACL
+ * but no cached acl data, just the acl length */
return ret;
return nfs4_get_acl_uncached(inode, buf, buflen);
}
diff --git a/fs/nfs/nfs4xdr.c b/fs/nfs/nfs4xdr.c
index 1dce12f41a4f..97f987a981cc 100644
--- a/fs/nfs/nfs4xdr.c
+++ b/fs/nfs/nfs4xdr.c
@@ -2517,11 +2517,13 @@ static void nfs4_xdr_enc_getacl(struct rpc_rqst *req, struct xdr_stream *xdr,
encode_compound_hdr(xdr, req, &hdr);
encode_sequence(xdr, &args->seq_args, &hdr);
encode_putfh(xdr, args->fh, &hdr);
- replen = hdr.replen + op_decode_hdr_maxsz + nfs4_fattr_bitmap_maxsz + 1;
+ replen = hdr.replen + op_decode_hdr_maxsz + 1;
encode_getattr_two(xdr, FATTR4_WORD0_ACL, 0, &hdr);
xdr_inline_pages(&req->rq_rcv_buf, replen << 2,
args->acl_pages, args->acl_pgbase, args->acl_len);
+ xdr_set_scratch_buffer(xdr, page_address(args->acl_scratch), PAGE_SIZE);
+
encode_nops(&hdr);
}
@@ -4957,17 +4959,18 @@ decode_restorefh(struct xdr_stream *xdr)
}
static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
- size_t *acl_len)
+ struct nfs_getaclres *res)
{
- __be32 *savep;
+ __be32 *savep, *bm_p;
uint32_t attrlen,
bitmap[3] = {0};
struct kvec *iov = req->rq_rcv_buf.head;
int status;
- *acl_len = 0;
+ res->acl_len = 0;
if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
goto out;
+ bm_p = xdr->p;
if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
goto out;
if ((status = decode_attr_length(xdr, &attrlen, &savep)) != 0)
@@ -4979,18 +4982,30 @@ static int decode_getacl(struct xdr_stream *xdr, struct rpc_rqst *req,
size_t hdrlen;
u32 recvd;
+ /* The bitmap (xdr len + bitmaps) and the attr xdr len words
+ * are stored with the acl data to handle the problem of
+ * variable length bitmaps.*/
+ xdr->p = bm_p;
+ res->acl_data_offset = be32_to_cpup(bm_p) + 2;
+ res->acl_data_offset <<= 2;
+
/* We ignore &savep and don't do consistency checks on
* the attr length. Let userspace figure it out.... */
hdrlen = (u8 *)xdr->p - (u8 *)iov->iov_base;
+ attrlen += res->acl_data_offset;
recvd = req->rq_rcv_buf.len - hdrlen;
if (attrlen > recvd) {
- dprintk("NFS: server cheating in getattr"
- " acl reply: attrlen %u > recvd %u\n",
+ if (res->acl_flags & NFS4_ACL_LEN_REQUEST) {
+ /* getxattr interface called with a NULL buf */
+ res->acl_len = attrlen;
+ goto out;
+ }
+ dprintk("NFS: acl reply: attrlen %u > recvd %u\n",
attrlen, recvd);
return -EINVAL;
}
xdr_read_pages(xdr, attrlen);
- *acl_len = attrlen;
+ res->acl_len = attrlen;
} else
status = -EOPNOTSUPP;
@@ -6028,7 +6043,7 @@ nfs4_xdr_dec_getacl(struct rpc_rqst *rqstp, struct xdr_stream *xdr,
status = decode_putfh(xdr);
if (status)
goto out;
- status = decode_getacl(xdr, rqstp, &res->acl_len);
+ status = decode_getacl(xdr, rqstp, res);
out:
return status;
diff --git a/fs/nfs/super.c b/fs/nfs/super.c
index 5b19b6aabe18..c4daf4eaad9c 100644
--- a/fs/nfs/super.c
+++ b/fs/nfs/super.c
@@ -904,10 +904,24 @@ static struct nfs_parsed_mount_data *nfs_alloc_parsed_mount_data(unsigned int ve
data->auth_flavor_len = 1;
data->version = version;
data->minorversion = 0;
+ security_init_mnt_opts(&data->lsm_opts);
}
return data;
}
+static void nfs_free_parsed_mount_data(struct nfs_parsed_mount_data *data)
+{
+ if (data) {
+ kfree(data->client_address);
+ kfree(data->mount_server.hostname);
+ kfree(data->nfs_server.export_path);
+ kfree(data->nfs_server.hostname);
+ kfree(data->fscache_uniq);
+ security_free_mnt_opts(&data->lsm_opts);
+ kfree(data);
+ }
+}
+
/*
* Sanity-check a server address provided by the mount command.
*
@@ -2215,9 +2229,7 @@ static struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
data = nfs_alloc_parsed_mount_data(NFS_DEFAULT_VERSION);
mntfh = nfs_alloc_fhandle();
if (data == NULL || mntfh == NULL)
- goto out_free_fh;
-
- security_init_mnt_opts(&data->lsm_opts);
+ goto out;
/* Validate the mount data */
error = nfs_validate_mount_data(raw_data, data, mntfh, dev_name);
@@ -2229,8 +2241,6 @@ static struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
#ifdef CONFIG_NFS_V4
if (data->version == 4) {
mntroot = nfs4_try_mount(flags, dev_name, data);
- kfree(data->client_address);
- kfree(data->nfs_server.export_path);
goto out;
}
#endif /* CONFIG_NFS_V4 */
@@ -2285,13 +2295,8 @@ static struct dentry *nfs_fs_mount(struct file_system_type *fs_type,
s->s_flags |= MS_ACTIVE;
out:
- kfree(data->nfs_server.hostname);
- kfree(data->mount_server.hostname);
- kfree(data->fscache_uniq);
- security_free_mnt_opts(&data->lsm_opts);
-out_free_fh:
+ nfs_free_parsed_mount_data(data);
nfs_free_fhandle(mntfh);
- kfree(data);
return mntroot;
out_err_nosb:
@@ -2618,9 +2623,7 @@ nfs4_remote_mount(struct file_system_type *fs_type, int flags,
mntfh = nfs_alloc_fhandle();
if (data == NULL || mntfh == NULL)
- goto out_free_fh;
-
- security_init_mnt_opts(&data->lsm_opts);
+ goto out;
/* Get a volume representation */
server = nfs4_create_server(data, mntfh);
@@ -2672,13 +2675,10 @@ nfs4_remote_mount(struct file_system_type *fs_type, int flags,
s->s_flags |= MS_ACTIVE;
- security_free_mnt_opts(&data->lsm_opts);
nfs_free_fhandle(mntfh);
return mntroot;
out:
- security_free_mnt_opts(&data->lsm_opts);
-out_free_fh:
nfs_free_fhandle(mntfh);
return ERR_PTR(error);
@@ -2858,7 +2858,7 @@ static struct dentry *nfs4_mount(struct file_system_type *fs_type,
data = nfs_alloc_parsed_mount_data(4);
if (data == NULL)
- goto out_free_data;
+ goto out;
/* Validate the mount data */
error = nfs4_validate_mount_data(raw_data, data, dev_name);
@@ -2872,12 +2872,7 @@ static struct dentry *nfs4_mount(struct file_system_type *fs_type,
error = PTR_ERR(res);
out:
- kfree(data->client_address);
- kfree(data->nfs_server.export_path);
- kfree(data->nfs_server.hostname);
- kfree(data->fscache_uniq);
-out_free_data:
- kfree(data);
+ nfs_free_parsed_mount_data(data);
dprintk("<-- nfs4_mount() = %d%s\n", error,
error != 0 ? " [error]" : "");
return res;
diff --git a/include/linux/genalloc.h b/include/linux/genalloc.h
index 32034bceedaf..ae9daa293958 100644
--- a/include/linux/genalloc.h
+++ b/include/linux/genalloc.h
@@ -81,7 +81,7 @@ extern unsigned long gen_pool_alloc_addr(struct gen_pool *,
*/
static inline unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)
{
- return gen_pool_alloc_addr(pool, size);
+ return gen_pool_alloc_addr(pool, size, 0);
}
extern void gen_pool_free(struct gen_pool *, unsigned long, size_t);
extern void gen_pool_for_each_chunk(struct gen_pool *,
diff --git a/include/linux/i2c-tegra.h b/include/linux/i2c-tegra.h
index 90dcddc52f29..2b200c49acf5 100644
--- a/include/linux/i2c-tegra.h
+++ b/include/linux/i2c-tegra.h
@@ -38,6 +38,8 @@ struct tegra_i2c_platform_data {
int scl_gpio[TEGRA_I2C_MAX_BUS];
int sda_gpio[TEGRA_I2C_MAX_BUS];
int (*arb_recovery)(int scl_gpio, int sda_gpio);
+ bool is_high_speed_enable;
+ u16 hs_master_code;
};
struct tegra_i2c_slave_platform_data {
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 343bd7661f2a..b9c1c06cd7a6 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -116,6 +116,8 @@ struct zone_reclaim_stat*
mem_cgroup_get_reclaim_stat_from_page(struct page *page);
extern void mem_cgroup_print_oom_info(struct mem_cgroup *memcg,
struct task_struct *p);
+extern void mem_cgroup_replace_page_cache(struct page *oldpage,
+ struct page *newpage);
#ifdef CONFIG_CGROUP_MEM_RES_CTLR_SWAP
extern int do_swap_account;
@@ -361,6 +363,10 @@ static inline
void mem_cgroup_count_vm_event(struct mm_struct *mm, enum vm_event_item idx)
{
}
+static inline void mem_cgroup_replace_page_cache(struct page *oldpage,
+ struct page *newpage)
+{
+}
#endif /* CONFIG_CGROUP_MEM_CONT */
#if !defined(CONFIG_CGROUP_MEM_RES_CTLR) || !defined(CONFIG_DEBUG_VM)
diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h
index 82b4c8801a4f..d0cb12eba402 100644
--- a/include/linux/mfd/tps65910.h
+++ b/include/linux/mfd/tps65910.h
@@ -243,7 +243,8 @@
/*Registers VDD1, VDD2 voltage values definitions */
-#define VDD1_2_NUM_VOLTS 73
+#define VDD1_2_NUM_VOLT_FINE 73
+#define VDD1_2_NUM_VOLT_COARSE 3
#define VDD1_2_MIN_VOLT 6000
#define VDD1_2_OFFSET 125
@@ -739,6 +740,34 @@
#define TPS65910_GPIO_STS BIT(1)
#define TPS65910_GPIO_SET BIT(0)
+/* Regulator Index Definitions */
+#define TPS65910_REG_VRTC 0
+#define TPS65910_REG_VIO 1
+#define TPS65910_REG_VDD1 2
+#define TPS65910_REG_VDD2 3
+#define TPS65910_REG_VDD3 4
+#define TPS65910_REG_VDIG1 5
+#define TPS65910_REG_VDIG2 6
+#define TPS65910_REG_VPLL 7
+#define TPS65910_REG_VDAC 8
+#define TPS65910_REG_VAUX1 9
+#define TPS65910_REG_VAUX2 10
+#define TPS65910_REG_VAUX33 11
+#define TPS65910_REG_VMMC 12
+
+#define TPS65911_REG_VDDCTRL 4
+#define TPS65911_REG_LDO1 5
+#define TPS65911_REG_LDO2 6
+#define TPS65911_REG_LDO3 7
+#define TPS65911_REG_LDO4 8
+#define TPS65911_REG_LDO5 9
+#define TPS65911_REG_LDO6 10
+#define TPS65911_REG_LDO7 11
+#define TPS65911_REG_LDO8 12
+
+/* Max number of TPS65910/11 regulators */
+#define TPS65910_NUM_REGS 13
+
/**
* struct tps65910_board
* Board platform data may be used to initialize regulators.
@@ -750,7 +779,7 @@ struct tps65910_board {
int irq_base;
int vmbch_threshold;
int vmbch2_threshold;
- struct regulator_init_data *tps65910_pmic_init_data;
+ struct regulator_init_data *tps65910_pmic_init_data[TPS65910_NUM_REGS];
};
/**
diff --git a/include/linux/mmc/sdhci.h b/include/linux/mmc/sdhci.h
index 85e7850b88af..19abccaf0fb0 100644
--- a/include/linux/mmc/sdhci.h
+++ b/include/linux/mmc/sdhci.h
@@ -89,6 +89,8 @@ struct sdhci_host {
#define SDHCI_QUIRK_UNSTABLE_RO_DETECT (1ULL<<31)
/* Controller cannot report the line status in present state register */
#define SDHCI_QUIRK_NON_STD_VOLTAGE_SWITCHING (1ULL<<32)
+/* Controller doesn't follow the standard frequency tuning procedure */
+#define SDHCI_QUIRK_NON_STANDARD_TUNING (1LL<<33)
int irq; /* Device IRQ */
void __iomem *ioaddr; /* Mapped address */
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h
index 8475f340b142..5c0e10d7da7c 100644
--- a/include/linux/nfs_xdr.h
+++ b/include/linux/nfs_xdr.h
@@ -602,11 +602,16 @@ struct nfs_getaclargs {
size_t acl_len;
unsigned int acl_pgbase;
struct page ** acl_pages;
+ struct page * acl_scratch;
struct nfs4_sequence_args seq_args;
};
+/* getxattr ACL interface flags */
+#define NFS4_ACL_LEN_REQUEST 0x0001 /* zero length getxattr buffer */
struct nfs_getaclres {
size_t acl_len;
+ size_t acl_data_offset;
+ int acl_flags;
struct nfs4_sequence_res seq_res;
};
diff --git a/include/linux/nvhost.h b/include/linux/nvhost.h
index 6b3c9e366f30..55b75d048791 100644
--- a/include/linux/nvhost.h
+++ b/include/linux/nvhost.h
@@ -3,7 +3,7 @@
*
* Tegra graphics host driver
*
- * Copyright (c) 2009-2011, NVIDIA Corporation.
+ * Copyright (c) 2009-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -51,11 +51,14 @@ struct nvhost_device {
int id; /* Separates clients of same hw */
u32 num_resources; /* Number of resources following */
struct resource *resource; /* Resources (IOMEM in particular) */
+ struct resource *reg_mem;
+ void __iomem *aperture; /* Iomem mapped to kernel */
- struct nvhost_master *host; /* Access to host1x resources */
u32 syncpts; /* Bitfield of sync points used */
u32 waitbases; /* Bit field of wait bases */
u32 modulemutexes; /* Bit field of module mutexes */
+ u32 moduleid; /* Module id for user space API */
+
u32 class; /* Device class */
bool exclusive; /* True if only one user at a time */
bool keepalive; /* Do not power gate when opened */
@@ -125,6 +128,8 @@ extern int nvhost_get_irq_byname(struct nvhost_device *, const char *);
#define nvhost_get_drvdata(_dev) dev_get_drvdata(&(_dev)->dev)
#define nvhost_set_drvdata(_dev, data) dev_set_drvdata(&(_dev)->dev, (data))
+#define nvhost_get_host(_dev) ((struct nvhost_master *) \
+ dev_get_drvdata((_dev)->dev.parent))
int nvhost_bus_add_host(struct nvhost_master *host);
diff --git a/include/linux/nvhost_ioctl.h b/include/linux/nvhost_ioctl.h
index a1fc0b7cd247..3f6eabf522f1 100644
--- a/include/linux/nvhost_ioctl.h
+++ b/include/linux/nvhost_ioctl.h
@@ -3,7 +3,7 @@
*
* Tegra graphics host driver
*
- * Copyright (c) 2009-2011, NVIDIA Corporation.
+ * Copyright (c) 2009-2012, NVIDIA Corporation.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -169,6 +169,15 @@ struct nvhost_ctrl_module_mutex_args {
__u32 lock;
};
+enum nvhost_module_id {
+ NVHOST_MODULE_NONE = -1,
+ NVHOST_MODULE_DISPLAY_A = 0,
+ NVHOST_MODULE_DISPLAY_B,
+ NVHOST_MODULE_VI,
+ NVHOST_MODULE_ISP,
+ NVHOST_MODULE_MPE,
+};
+
struct nvhost_ctrl_module_regrdwr_args {
__u32 id;
__u32 num_offsets;
diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h
index e8840964aca1..dad7d9a4abce 100644
--- a/include/linux/pci_regs.h
+++ b/include/linux/pci_regs.h
@@ -392,7 +392,7 @@
#define PCI_EXP_TYPE_DOWNSTREAM 0x6 /* Downstream Port */
#define PCI_EXP_TYPE_PCI_BRIDGE 0x7 /* PCI/PCI-X Bridge */
#define PCI_EXP_TYPE_RC_END 0x9 /* Root Complex Integrated Endpoint */
-#define PCI_EXP_TYPE_RC_EC 0x10 /* Root Complex Event Collector */
+#define PCI_EXP_TYPE_RC_EC 0xa /* Root Complex Event Collector */
#define PCI_EXP_FLAGS_SLOT 0x0100 /* Slot implemented */
#define PCI_EXP_FLAGS_IRQ 0x3e00 /* Interrupt message number */
#define PCI_EXP_DEVCAP 4 /* Device capabilities */
diff --git a/include/linux/platform_data/rm31080a_ts.h b/include/linux/platform_data/rm31080a_ts.h
new file mode 100644
index 000000000000..1aba146d772f
--- /dev/null
+++ b/include/linux/platform_data/rm31080a_ts.h
@@ -0,0 +1,8 @@
+#ifndef _PLATFORM_DATA_RM31080A_TS_H_
+#define _PLATFORM_DATA_RM31080A_TS_H_
+
+struct rm_spi_ts_platform_data {
+ int gpio_reset;
+};
+
+#endif /*_PLATFORM_DATA_RM31080A_TS_H_*/
diff --git a/include/linux/regulator/tps62360.h b/include/linux/regulator/tps62360.h
new file mode 100644
index 000000000000..6a5c1b2c751e
--- /dev/null
+++ b/include/linux/regulator/tps62360.h
@@ -0,0 +1,57 @@
+/*
+ * tps62360.h -- TI tps62360
+ *
+ * Interface for regulator driver for TI TPS62360 Processor core supply
+ *
+ * Copyright (C) 2012 NVIDIA Corporation
+
+ * Author: Laxman Dewangan <ldewangan@nvidia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef __LINUX_REGULATOR_TPS62360_H
+#define __LINUX_REGULATOR_TPS62360_H
+
+#include <linux/regulator/machine.h>
+
+/*
+ * struct tps62360_regulator_platform_data - tps62360 regulator platform data.
+ *
+ * @reg_init_data: The regulator init data.
+ * @en_force_pwm: Enable force pwm or not.
+ * @en_discharge: Enable discharge the output capacitor via internal
+ * register.
+ * @en_internal_pulldn: internal pull down enable or not.
+ * @vsel0_gpio: Gpio number for vsel0. It should be -1 if this is tied with
+ * fixed logic.
+ * @vsel1_gpio: Gpio number for vsel1. It should be -1 if this is tied with
+ * fixed logic.
+ * @vsel0_def_state: Default state of vsel0. 1 if it is high else 0.
+ * @vsel1_def_state: Default state of vsel1. 1 if it is high else 0.
+ */
+struct tps62360_regulator_platform_data {
+ struct regulator_init_data reg_init_data;
+ bool en_force_pwm;
+ bool en_discharge;
+ bool en_internal_pulldn;
+ int vsel0_gpio;
+ int vsel1_gpio;
+ int vsel0_def_state;
+ int vsel1_def_state;
+};
+
+#endif /* __LINUX_REGULATOR_TPS62360_H */
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
new file mode 100644
index 000000000000..84e121d936ac
--- /dev/null
+++ b/include/linux/rmi.h
@@ -0,0 +1,656 @@
+/*
+ * Copyright (c) 2011 Synaptics Incorporated
+ * Copyright (c) 2011 Unixphere
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#ifndef _RMI_H
+#define _RMI_H
+#include <linux/kernel.h>
+#include <linux/lockdep.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/cdev.h>
+#include <linux/mutex.h>
+#include <linux/stat.h>
+#include <linux/wait.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+#include <linux/earlysuspend.h>
+#endif
+
+#ifdef CONFIG_TOUCHSCREEN_SYN_RMI4_SPI
+
+#define CONFIG_RMI4_BUS
+#define CONFIG_RMI4_SPI
+#define CONFIG_RMI4_GENERIC
+#define CONFIG_RMI4_F09
+#define CONFIG_RMI4_F11
+#define CONFIG_RMI4_F19
+#define CONFIG_RMI4_F34
+#define CONFIG_RMI4_F54
+#define CONFIG_RMI4_DEV
+
+#endif
+
+// #define SYNAPTICS_SENSOR_POLL 1
+
+/* Permissions for sysfs attributes. Since the permissions policy will change
+ * on a global basis in the future, rather than edit all sysfs attrs everywhere
+ * in the driver (and risk screwing that up in the process), we use this handy
+ * set of #defines. That way when we change the policy for sysfs permissions,
+ * we only need to change them here.
+ */
+#define RMI_RO_ATTR S_IRUGO
+#define RMI_RW_ATTR (S_IRUGO | S_IWUGO)
+#define RMI_WO_ATTR S_IWUGO
+
+#define PDT_START_SCAN_LOCATION 0x00e9
+
+enum rmi_irq_polarity {
+ RMI_IRQ_ACTIVE_LOW = 0,
+ RMI_IRQ_ACTIVE_HIGH = 1
+};
+
+/**
+ * struct rmi_f11_axis_alignmen - target axis alignment
+ * @swap_axes: set to TRUE if desired to swap x- and y-axis
+ * @flip_x: set to TRUE if desired to flip direction on x-axis
+ * @flip_y: set to TRUE if desired to flip direction on y-axis
+ */
+struct rmi_f11_2d_axis_alignment {
+ bool swap_axes;
+ bool flip_x;
+ bool flip_y;
+ int clip_X_low;
+ int clip_Y_low;
+ int clip_X_high;
+ int clip_Y_high;
+ int offset_X;
+ int offset_Y;
+ int rel_report_enabled;
+};
+
+/**
+ * RMI F11 - function control register parameters
+ * Each register that has a specific bit-field setup has an accompanied
+ * register definition so that the setting can be chosen as a one-word
+ * register setting or per-bit setting.
+ */
+union rmi_f11_2d_ctrl0 {
+ struct {
+ u8 reporting_mode:3;
+ u8 abs_pos_filt:1;
+ u8 rel_pos_filt:1;
+ u8 rel_ballistics:1;
+ u8 dribble:1;
+ u8 report_beyond_clip:1;
+ };
+ u8 reg;
+};
+
+union rmi_f11_2d_ctrl1 {
+ struct {
+ u8 palm_detect_thres:4;
+ u8 motion_sensitivity:2;
+ u8 man_track_en:1;
+ u8 man_tracked_finger:1;
+ };
+ u8 reg;
+};
+
+union rmi_f11_2d_ctrl2__3 {
+ struct {
+ u8 delta_x_threshold:8;
+ u8 delta_y_threshold:8;
+ };
+ u8 regs[2];
+};
+
+union rmi_f11_2d_ctrl4 {
+ struct {
+ u8 velocity:8;
+ };
+ u8 reg;
+};
+
+union rmi_f11_2d_ctrl5 {
+ struct {
+ u8 acceleration:8;
+ };
+ u8 reg;
+};
+
+union rmi_f11_2d_ctrl6__7 {
+ struct {
+ u16 sensor_max_x_pos:12;
+ };
+ u8 regs[2];
+};
+
+union rmi_f11_2d_ctrl8__9 {
+ struct {
+ u16 sensor_max_y_pos:12;
+ };
+ u8 regs[2];
+};
+
+union rmi_f11_2d_ctrl10 {
+ struct {
+ u8 single_tap_int_enable:1;
+ u8 tap_n_hold_int_enable:1;
+ u8 double_tap_int_enable:1;
+ u8 early_tap_int_enable:1;
+ u8 flick_int_enable:1;
+ u8 press_int_enable:1;
+ u8 pinch_int_enable:1;
+ };
+ u8 reg;
+};
+
+union rmi_f11_2d_ctrl11 {
+ struct {
+ u8 palm_detect_int_enable:1;
+ u8 rotate_int_enable:1;
+ u8 touch_shape_int_enable:1;
+ u8 scroll_zone_int_enable:1;
+ u8 multi_finger_scroll_int_enable:1;
+ };
+ u8 reg;
+};
+
+union rmi_f11_2d_ctrl12 {
+ struct {
+ u8 sensor_map:7;
+ u8 xy_sel:1;
+ };
+ u8 reg;
+};
+
+union rmi_f11_2d_ctrl14 {
+ struct {
+ u8 sens_adjustment:5;
+ u8 hyst_adjustment:3;
+ };
+ u8 reg;
+};
+
+/* The configuation is controlled as per register which means that if a register
+ * is allocated for ctrl configuration one must make sure that all the bits are
+ * set accordingly for that particular register.
+ */
+struct rmi_f11_2d_ctrl {
+ union rmi_f11_2d_ctrl0 *ctrl0;
+ union rmi_f11_2d_ctrl1 *ctrl1;
+ union rmi_f11_2d_ctrl2__3 *ctrl2__3;
+ union rmi_f11_2d_ctrl4 *ctrl4;
+ union rmi_f11_2d_ctrl5 *ctrl5;
+ union rmi_f11_2d_ctrl6__7 *ctrl6__7;
+ union rmi_f11_2d_ctrl8__9 *ctrl8__9;
+ union rmi_f11_2d_ctrl10 *ctrl10;
+ union rmi_f11_2d_ctrl11 *ctrl11;
+ union rmi_f11_2d_ctrl12 *ctrl12;
+ u8 ctrl12_size;
+ union rmi_f11_2d_ctrl14 *ctrl14;
+ u8 *ctrl15;
+ u8 *ctrl16;
+ u8 *ctrl17;
+ u8 *ctrl18;
+ u8 *ctrl19;
+};
+
+struct rmi_f19_button_map {
+ unsigned char nbuttons;
+ unsigned char *map;
+};
+
+struct rmi_device_platform_data_spi {
+ int block_delay_us;
+ int split_read_block_delay_us;
+ int read_delay_us;
+ int write_delay_us;
+ int split_read_byte_delay_us;
+ int pre_delay_us;
+ int post_delay_us;
+
+ void *cs_assert_data;
+ int (*cs_assert) (const void *cs_assert_data, const bool assert);
+};
+
+struct rmi_device_platform_data {
+ char *driver_name;
+
+ int irq;
+ enum rmi_irq_polarity irq_polarity;
+ int (*gpio_config)(void *gpio_data, bool configure);
+
+ struct rmi_device_platform_data_spi spi_data;
+
+ /* function handler pdata */
+ struct rmi_f11_2d_ctrl *f11_ctrl;
+ struct rmi_f11_2d_axis_alignment axis_align;
+ struct rmi_f19_button_map *button_map;
+
+#ifdef CONFIG_PM
+ void *pm_data;
+ int (*pre_suspend) (const void *pm_data);
+ int (*post_resume) (const void *pm_data);
+#endif
+};
+
+/**
+ * struct rmi_function_descriptor - RMI function base addresses
+ * @query_base_addr: The RMI Query base address
+ * @command_base_addr: The RMI Command base address
+ * @control_base_addr: The RMI Control base address
+ * @data_base_addr: The RMI Data base address
+ * @interrupt_source_count: The number of irqs this RMI function needs
+ * @function_number: The RMI function number
+ *
+ * This struct is used when iterating the Page Description Table. The addresses
+ * are 16-bit values to include the current page address.
+ *
+ */
+struct rmi_function_descriptor {
+ u16 query_base_addr;
+ u16 command_base_addr;
+ u16 control_base_addr;
+ u16 data_base_addr;
+ u8 interrupt_source_count;
+ u8 function_number;
+ u8 function_version;
+};
+
+struct rmi_function_container;
+struct rmi_device;
+
+/**
+ * struct rmi_function_handler - an RMI function handler
+ * @func: The RMI function number
+ * @init: Callback for RMI function init
+ * @attention: Callback for RMI function attention
+ * @suspend: Callback for function suspend, returns 0 for success.
+ * @resume: Callback for RMI function resume, returns 0 for success.
+ * @remove: Callback for RMI function removal
+ *
+ * This struct describes the interface of an RMI function. These are
+ * registered to the bus using the rmi_register_function_driver() call.
+ *
+ */
+struct rmi_function_handler {
+ int func;
+ int (*init)(struct rmi_function_container *fc);
+ int (*attention)(struct rmi_function_container *fc, u8 *irq_bits);
+#ifdef CONFIG_PM
+ int (*suspend)(struct rmi_function_container *fc);
+ int (*resume)(struct rmi_function_container *fc);
+#endif
+ void (*remove)(struct rmi_function_container *fc);
+};
+
+/**
+ * struct rmi_function_device - represent an RMI function device
+ * @dev: The device created
+ *
+ * The RMI function device implements the "psuedo" device that represents
+ * an RMI4 function like function 0x11, function 0x34, etc. and is really
+ * a placeholder to be able to create sysfs attributes for each function
+ * in order to facilitate communication between user code and RMI4 functions.
+ *
+ */
+struct rmi_function_device {
+ struct device dev;
+};
+
+/**
+ * struct rmi_function_container - an element in a function handler list
+ * @list: The list
+ * @fd: The function descriptor of the RMI function
+ * @rmi_dev: Pointer to the RMI device associated with this function container
+ * @fh: The callbacks connected to this function
+ * @num_of_irqs: The number of irqs needed by this function
+ * @irq_pos: The position in the irq bitfield this function holds
+ * @data: Private data pointer
+ *
+ */
+struct rmi_function_container {
+ struct list_head list;
+
+ struct rmi_function_descriptor fd;
+ struct rmi_device *rmi_dev;
+ struct rmi_function_handler *fh;
+ struct device dev;
+
+ int num_of_irqs;
+ int irq_pos;
+ u8 *irq_mask;
+
+ void *data;
+};
+#define to_rmi_function_container(d) \
+ container_of(d, struct rmi_function_container, dev);
+#define to_rmi_function_device(d) \
+ container_of(d, struct rmi_function_device, dev);
+
+
+#ifdef CONFIG_RMI4_DEV
+
+#define RMI_CHAR_DEV_TMPBUF_SZ 128
+#define RMI_REG_ADDR_PAGE_SELECT 0xFF
+
+struct rmi_char_dev {
+ /* mutex for file operation*/
+ struct mutex mutex_file_op;
+ /* main char dev structure */
+ struct cdev main_dev;
+
+ /* register address for RMI protocol */
+ /* filp->f_pos */
+
+ /* pointer to the corresponding phys device info for this sensor */
+ /* The phys device has the pointers to read, write, etc. */
+ struct rmi_phys_device *phys;
+ /* reference count */
+ int ref_count;
+};
+
+int rmi_char_dev_register(struct rmi_phys_device *phys);
+void rmi_char_dev_unregister(struct rmi_phys_device *phys);
+
+#endif /*CONFIG_RMI4_DEV*/
+
+
+
+/**
+ * struct rmi_driver - represents an RMI driver
+ * @driver: Device driver model driver
+ * @probe: Callback for device probe
+ * @remove: Callback for device removal
+ * @shutdown: Callback for device shutdown
+ * @irq_handler: Callback for handling irqs
+ * @fh_add: Callback for function handler add
+ * @fh_remove: Callback for function handler remove
+ * @get_func_irq_mask: Callback for calculating interrupt mask
+ * @store_irq_mask: Callback for storing and replacing interrupt mask
+ * @restore_irq_mask: Callback for restoring previously stored interrupt mask
+ * @data: Private data pointer
+ *
+ * The RMI driver implements a driver on the RMI bus.
+ *
+ */
+struct rmi_driver {
+ struct device_driver driver;
+
+ int (*probe)(struct rmi_device *rmi_dev);
+ int (*remove)(struct rmi_device *rmi_dev);
+ void (*shutdown)(struct rmi_device *rmi_dev);
+ int (*irq_handler)(struct rmi_device *rmi_dev, int irq);
+ void (*fh_add)(struct rmi_device *rmi_dev,
+ struct rmi_function_handler *fh);
+ void (*fh_remove)(struct rmi_device *rmi_dev,
+ struct rmi_function_handler *fh);
+ u8* (*get_func_irq_mask)(struct rmi_device *rmi_dev,
+ struct rmi_function_container *fc);
+ int (*store_irq_mask)(struct rmi_device *rmi_dev, u8* new_interupts);
+ int (*restore_irq_mask)(struct rmi_device *rmi_dev);
+ void *data;
+};
+#define to_rmi_driver(d) \
+ container_of(d, struct rmi_driver, driver);
+
+/** struct rmi_phys_info - diagnostic information about the RMI physical
+ * device, used in the phys sysfs file.
+ * @proto String indicating the protocol being used.
+ * @tx_count Number of transmit operations.
+ * @tx_bytes Number of bytes transmitted.
+ * @tx_errs Number of errors encountered during transmit operations.
+ * @rx_count Number of receive operations.
+ * @rx_bytes Number of bytes received.
+ * @rx_errs Number of errors encountered during receive operations.
+ * @att_count Number of times ATTN assertions have been handled.
+ */
+struct rmi_phys_info {
+ char *proto;
+ long tx_count;
+ long tx_bytes;
+ long tx_errs;
+ long rx_count;
+ long rx_bytes;
+ long rx_errs;
+ long attn_count;
+};
+
+/**
+ * struct rmi_phys_device - represent an RMI physical device
+ * @dev: Pointer to the communication device, e.g. i2c or spi
+ * @rmi_dev: Pointer to the RMI device
+ * @write: Callback for write
+ * @write_block: Callback for writing a block of data
+ * @read: Callback for read
+ * @read_block: Callback for reading a block of data
+ * @data: Private data pointer
+ *
+ * The RMI physical device implements the glue between different communication
+ * buses such as I2C and SPI.
+ *
+ */
+struct rmi_phys_device {
+ struct device *dev;
+ struct rmi_device *rmi_dev;
+
+ int (*write)(struct rmi_phys_device *phys, u16 addr, u8 data);
+ int (*write_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+ int len);
+ int (*read)(struct rmi_phys_device *phys, u16 addr, u8 *buf);
+ int (*read_block)(struct rmi_phys_device *phys, u16 addr, u8 *buf,
+ int len);
+
+ int (*enable_device) (struct rmi_phys_device *phys);
+ void (*disable_device) (struct rmi_phys_device *phys);
+
+ void *data;
+
+ struct rmi_phys_info info;
+
+#ifdef CONFIG_RMI4_DEV
+ /* pointer to attention char device and char device */
+ struct rmi_char_dev *char_dev;
+ struct class *rmi_char_device_class;
+#endif /*CONFIG_RMI4_DEV*/
+};
+
+/**
+ * struct rmi_device - represents an RMI device
+ * @dev: The device created for the RMI bus
+ * @driver: Pointer to associated driver
+ * @phys: Pointer to the physical interface
+ * @early_suspend_handler: Pointers to early_suspend and late_resume, if
+ * configured.
+ *
+ * This structs represent an RMI device.
+ *
+ */
+struct rmi_device {
+ struct device dev;
+
+ struct rmi_driver *driver;
+ struct rmi_phys_device *phys;
+
+#ifdef CONFIG_HAS_EARLYSUSPEND
+ struct early_suspend early_suspend_handler;
+#endif
+};
+#define to_rmi_device(d) container_of(d, struct rmi_device, dev);
+#define to_rmi_platform_data(d) ((d)->phys->dev->platform_data);
+
+static inline void rmi_set_driverdata(struct rmi_device *d, void *data)
+{
+ dev_set_drvdata(&d->dev, data);
+}
+
+static inline void *rmi_get_driverdata(struct rmi_device *d)
+{
+ return dev_get_drvdata(&d->dev);
+}
+
+/**
+ * rmi_read - RMI read byte
+ * @d: Pointer to an RMI device
+ * @addr: The address to read from
+ * @buf: The read buffer
+ *
+ * Reads a byte of data using the underlaying physical protocol in to buf. It
+ * returns zero or a negative error code.
+ */
+static inline int rmi_read(struct rmi_device *d, u16 addr, u8 *buf)
+{
+ return d->phys->read(d->phys, addr, buf);
+}
+
+/**
+ * rmi_read_block - RMI read block
+ * @d: Pointer to an RMI device
+ * @addr: The start address to read from
+ * @buf: The read buffer
+ * @len: Length of the read buffer
+ *
+ * Reads a block of byte data using the underlaying physical protocol in to buf.
+ * It returns the amount of bytes read or a negative error code.
+ */
+static inline int rmi_read_block(struct rmi_device *d, u16 addr, u8 *buf,
+ int len)
+{
+ return d->phys->read_block(d->phys, addr, buf, len);
+}
+
+/**
+ * rmi_write - RMI write byte
+ * @d: Pointer to an RMI device
+ * @addr: The address to write to
+ * @data: The data to write
+ *
+ * Writes a byte from buf using the underlaying physical protocol. It
+ * returns zero or a negative error code.
+ */
+static inline int rmi_write(struct rmi_device *d, u16 addr, u8 data)
+{
+ return d->phys->write(d->phys, addr, data);
+}
+
+/**
+ * rmi_write_block - RMI write block
+ * @d: Pointer to an RMI device
+ * @addr: The start address to write to
+ * @buf: The write buffer
+ * @len: Length of the write buffer
+ *
+ * Writes a block of byte data from buf using the underlaying physical protocol.
+ * It returns the amount of bytes written or a negative error code.
+ */
+static inline int rmi_write_block(struct rmi_device *d, u16 addr, u8 *buf,
+ int len)
+{
+ return d->phys->write_block(d->phys, addr, buf, len);
+}
+
+/**
+ * rmi_register_driver - register rmi driver
+ * @driver: the driver to register
+ *
+ * This function registers an RMI driver to the RMI bus.
+ */
+int rmi_register_driver(struct rmi_driver *driver);
+
+/**
+ * rmi_unregister_driver - unregister rmi driver
+ * @driver: the driver to unregister
+ *
+ * This function unregisters an RMI driver to the RMI bus.
+ */
+void rmi_unregister_driver(struct rmi_driver *driver);
+
+/**
+ * rmi_register_phys_device - register a physical device connection
+ * @phys: the physical driver to register
+ *
+ * This function registers a physical driver to the RMI bus. These drivers
+ * provide a communication layer for the drivers connected to the bus, e.g.
+ * I2C, SPI and so on.
+ */
+int rmi_register_phys_device(struct rmi_phys_device *phys);
+
+/**
+ * rmi_unregister_phys_device - unregister a physical device connection
+ * @phys: the physical driver to unregister
+ *
+ * This function unregisters a physical driver from the RMI bus.
+ */
+void rmi_unregister_phys_device(struct rmi_phys_device *phys);
+
+/**
+ * rmi_register_function_driver - register an RMI function driver
+ * @fh: the function handler to register
+ *
+ * This function registers support for a new RMI function to the bus. All
+ * drivers on the bus will be notified of the presence of the new function
+ * driver.
+ */
+int rmi_register_function_driver(struct rmi_function_handler *fh);
+
+/**
+ * rmi_unregister_function_driver - unregister an RMI function driver
+ * @fh: the function handler to unregister
+ *
+ * This function unregisters a RMI function from the RMI bus. All drivers on
+ * the bus will be notified of the removal of a function driver.
+ */
+void rmi_unregister_function_driver(struct rmi_function_handler *fh);
+
+/**
+ * rmi_get_function_handler - get a pointer to specified RMI function
+ * @id: the RMI function id
+ *
+ * This function gets the specified RMI function handler from the list of
+ * supported functions.
+ */
+struct rmi_function_handler *rmi_get_function_handler(int id);
+
+/* Utility routine to handle writes to read-only attributes. Hopefully
+ * this will never happen, but if the user does something stupid, we
+ * don't want to accept it quietly.
+ */
+ssize_t rmi_store_error(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count);
+
+/* Utility routine to handle reads to write-only attributes. Hopefully
+ * this will never happen, but if the user does something stupid, we
+ * don't want to accept it quietly.
+ */
+ssize_t rmi_show_error(struct device *dev,
+ struct device_attribute *attr,
+ char *buf);
+
+/* utility function for bit access of u8*'s */
+void u8_set_bit(u8 *target, int pos);
+void u8_clear_bit(u8 *target, int pos);
+bool u8_is_set(u8 *target, int pos);
+bool u8_is_any_set(u8 *target, int size);
+void u8_or(u8 *dest, u8* target1, u8* target2, int size);
+void u8_and(u8 *dest, u8* target1, u8* target2, int size);
+#endif
diff --git a/include/linux/sunrpc/xdr.h b/include/linux/sunrpc/xdr.h
index a20970ef9e4e..af70af333546 100644
--- a/include/linux/sunrpc/xdr.h
+++ b/include/linux/sunrpc/xdr.h
@@ -191,6 +191,8 @@ extern int xdr_decode_array2(struct xdr_buf *buf, unsigned int base,
struct xdr_array2_desc *desc);
extern int xdr_encode_array2(struct xdr_buf *buf, unsigned int base,
struct xdr_array2_desc *desc);
+extern void _copy_from_pages(char *p, struct page **pages, size_t pgbase,
+ size_t len);
/*
* Provide some simple tools for XDR buffer overflow-checking etc.
diff --git a/include/linux/tegra_audio.h b/include/linux/tegra_audio.h
index 516b5a89c4e6..6416cd01861c 100644
--- a/include/linux/tegra_audio.h
+++ b/include/linux/tegra_audio.h
@@ -75,4 +75,6 @@ struct dam_srate {
#define I2S_LOOPBACK _IOW(TEGRA_AUDIO_MAGIC, 17, unsigned int *)
#define I2S_MODE_I2S _IOW(TEGRA_AUDIO_MAGIC, 18, unsigned int *)
+extern bool tegra_is_voice_call_active();
+
#endif/*_CPCAP_AUDIO_H*/
diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h
index 2ef4385da6bf..f1aa0ecc5890 100644
--- a/include/linux/ti_wilink_st.h
+++ b/include/linux/ti_wilink_st.h
@@ -386,7 +386,12 @@ void st_ll_disable(struct st_data_s *);
* various funcs used by ST core to set/get the various PM states
* of the chip.
*/
+
+#ifdef CONFIG_TI_ST
unsigned long st_ll_getstate(struct st_data_s *);
+#else
+static inline unsigned long st_ll_getstate(struct st_data_s *ll){ return 0; }
+#endif
unsigned long st_ll_sleep_state(struct st_data_s *, unsigned char);
void st_ll_wakeup(struct st_data_s *);
@@ -443,6 +448,7 @@ struct ti_st_plat_data {
int (*chip_disable) (struct kim_data_s *);
int (*chip_asleep) (struct kim_data_s *);
int (*chip_awake) (struct kim_data_s *);
+ int (*set_power)(int);
};
#endif /* TI_WILINK_ST_H */
diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h
index a4d4163953d8..6939637e787d 100644
--- a/include/linux/usb/usbnet.h
+++ b/include/linux/usb/usbnet.h
@@ -99,7 +99,6 @@ struct driver_info {
#define FLAG_LINK_INTR 0x0800 /* updates link (carrier) status */
#define FLAG_POINTTOPOINT 0x1000 /* possibly use "usb%d" names */
-#define FLAG_RMNET 0x2000 /* use "rmnet%d" names */
/*
* Indicates to usbnet, that USB driver accumulates multiple IP packets.
@@ -107,6 +106,7 @@ struct driver_info {
*/
#define FLAG_MULTI_PACKET 0x2000
#define FLAG_RX_ASSEMBLE 0x4000 /* rx packets may span >1 frames */
+#define FLAG_RMNET 0x8000 /* use "rmnet%d" names */
/* init device ... can sleep, or cause probe() failure */
int (*bind)(struct usbnet *, struct usb_interface *);
diff --git a/include/linux/wl12xx.h b/include/linux/wl12xx.h
index 4b697395326e..3995105f3ffd 100644
--- a/include/linux/wl12xx.h
+++ b/include/linux/wl12xx.h
@@ -46,6 +46,18 @@ enum {
WL12XX_TCXOCLOCK_33_6 = 7, /* 33.6 MHz */
};
+/* TCXO clock values */
+enum {
+ WL12XX_TCXOCLOCK_19_2 = 0, /* 19.2MHz */
+ WL12XX_TCXOCLOCK_26 = 1, /* 26 MHz */
+ WL12XX_TCXOCLOCK_38_4 = 2, /* 38.4MHz */
+ WL12XX_TCXOCLOCK_52 = 3, /* 52 MHz */
+ WL12XX_TCXOCLOCK_16_368 = 4, /* 16.368 MHz */
+ WL12XX_TCXOCLOCK_32_736 = 5, /* 32.736 MHz */
+ WL12XX_TCXOCLOCK_16_8 = 6, /* 16.8 MHz */
+ WL12XX_TCXOCLOCK_33_6 = 7, /* 33.6 MHz */
+};
+
struct wl12xx_platform_data {
void (*set_power)(bool enable);
/* SDIO only: IRQ number if WLAN_IRQ line is used, 0 for SDIO IRQs */
diff --git a/include/media/ov5650.h b/include/media/ov5650.h
index 00efcec61a5f..5c4a87cfbe8d 100644
--- a/include/media/ov5650.h
+++ b/include/media/ov5650.h
@@ -29,6 +29,7 @@
#define OV5650_IOCTL_GET_STATUS _IOR('o', 5, __u8)
#define OV5650_IOCTL_SET_BINNING _IOW('o', 6, __u8)
#define OV5650_IOCTL_TEST_PATTERN _IOW('o', 7, enum ov5650_test_pattern)
+#define OV5650_IOCTL_SET_GROUP_HOLD _IOW('o', 8, struct ov5650_ae)
#define OV5650_IOCTL_SET_CAMERA_MODE _IOW('o', 10, __u32)
#define OV5650_IOCTL_SYNC_SENSORS _IOW('o', 11, __u32)
@@ -71,6 +72,15 @@ struct ov5650_mode {
__u16 gain;
};
+struct ov5650_ae {
+ __u32 frame_length;
+ __u8 frame_length_enable;
+ __u32 coarse_time;
+ __u8 coarse_time_enable;
+ __s32 gain;
+ __u8 gain_enable;
+};
+
#ifdef __KERNEL__
struct ov5650_platform_data {
int (*power_on)(void);
diff --git a/include/xen/interface/io/xs_wire.h b/include/xen/interface/io/xs_wire.h
index 99fcffb372d1..454ee2629238 100644
--- a/include/xen/interface/io/xs_wire.h
+++ b/include/xen/interface/io/xs_wire.h
@@ -84,4 +84,7 @@ struct xenstore_domain_interface {
XENSTORE_RING_IDX rsp_cons, rsp_prod;
};
+/* Violating this is very bad. See docs/misc/xenstore.txt. */
+#define XENSTORE_PAYLOAD_MAX 4096
+
#endif /* _XS_WIRE_H */
diff --git a/init/do_mounts.c b/init/do_mounts.c
index c0851a8e030c..ef6478fbb54e 100644
--- a/init/do_mounts.c
+++ b/init/do_mounts.c
@@ -360,15 +360,42 @@ out:
}
#ifdef CONFIG_ROOT_NFS
+
+#define NFSROOT_TIMEOUT_MIN 5
+#define NFSROOT_TIMEOUT_MAX 30
+#define NFSROOT_RETRY_MAX 5
+
static int __init mount_nfs_root(void)
{
char *root_dev, *root_data;
+ unsigned int timeout;
+ int try, err;
- if (nfs_root_data(&root_dev, &root_data) != 0)
- return 0;
- if (do_mount_root(root_dev, "nfs", root_mountflags, root_data) != 0)
+ err = nfs_root_data(&root_dev, &root_data);
+ if (err != 0)
return 0;
- return 1;
+
+ /*
+ * The server or network may not be ready, so try several
+ * times. Stop after a few tries in case the client wants
+ * to fall back to other boot methods.
+ */
+ timeout = NFSROOT_TIMEOUT_MIN;
+ for (try = 1; ; try++) {
+ err = do_mount_root(root_dev, "nfs",
+ root_mountflags, root_data);
+ if (err == 0)
+ return 1;
+ if (try > NFSROOT_RETRY_MAX)
+ break;
+
+ /* Wait, in case the server refused us immediately */
+ ssleep(timeout);
+ timeout <<= 1;
+ if (timeout > NFSROOT_TIMEOUT_MAX)
+ timeout = NFSROOT_TIMEOUT_MAX;
+ }
+ return 0;
}
#endif
diff --git a/kernel/sched.c b/kernel/sched.c
index 5525f209ebdf..f6cf5cbc64ba 100644
--- a/kernel/sched.c
+++ b/kernel/sched.c
@@ -8186,6 +8186,7 @@ void __init sched_init(void)
atomic_set(&nohz.load_balancer, nr_cpu_ids);
atomic_set(&nohz.first_pick_cpu, nr_cpu_ids);
atomic_set(&nohz.second_pick_cpu, nr_cpu_ids);
+ nohz.next_balance = jiffies;
#endif
/* May be allocated at isolcpus cmdline parse time */
if (cpu_isolated_map == NULL)
diff --git a/mm/filemap.c b/mm/filemap.c
index b91f3aa627fc..0eedbf85062d 100644
--- a/mm/filemap.c
+++ b/mm/filemap.c
@@ -393,24 +393,11 @@ EXPORT_SYMBOL(filemap_write_and_wait_range);
int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
{
int error;
- struct mem_cgroup *memcg = NULL;
VM_BUG_ON(!PageLocked(old));
VM_BUG_ON(!PageLocked(new));
VM_BUG_ON(new->mapping);
- /*
- * This is not page migration, but prepare_migration and
- * end_migration does enough work for charge replacement.
- *
- * In the longer term we probably want a specialized function
- * for moving the charge from old to new in a more efficient
- * manner.
- */
- error = mem_cgroup_prepare_migration(old, new, &memcg, gfp_mask);
- if (error)
- return error;
-
error = radix_tree_preload(gfp_mask & ~__GFP_HIGHMEM);
if (!error) {
struct address_space *mapping = old->mapping;
@@ -432,13 +419,12 @@ int replace_page_cache_page(struct page *old, struct page *new, gfp_t gfp_mask)
if (PageSwapBacked(new))
__inc_zone_page_state(new, NR_SHMEM);
spin_unlock_irq(&mapping->tree_lock);
+ /* mem_cgroup codes must not be called under tree_lock */
+ mem_cgroup_replace_page_cache(old, new);
radix_tree_preload_end();
if (freepage)
freepage(old);
page_cache_release(old);
- mem_cgroup_end_migration(memcg, old, new, true);
- } else {
- mem_cgroup_end_migration(memcg, old, new, false);
}
return error;
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index afde618f9895..dd81ddc64b4d 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -3355,6 +3355,50 @@ void mem_cgroup_end_migration(struct mem_cgroup *mem,
cgroup_release_and_wakeup_rmdir(&mem->css);
}
+/*
+ * At replace page cache, newpage is not under any memcg but it's on
+ * LRU. So, this function doesn't touch res_counter but handles LRU
+ * in correct way. Both pages are locked so we cannot race with uncharge.
+ */
+void mem_cgroup_replace_page_cache(struct page *oldpage,
+ struct page *newpage)
+{
+ struct mem_cgroup *memcg;
+ struct page_cgroup *pc;
+ struct zone *zone;
+ enum charge_type type = MEM_CGROUP_CHARGE_TYPE_CACHE;
+ unsigned long flags;
+
+ if (mem_cgroup_disabled())
+ return;
+
+ pc = lookup_page_cgroup(oldpage);
+ /* fix accounting on old pages */
+ lock_page_cgroup(pc);
+ memcg = pc->mem_cgroup;
+ mem_cgroup_charge_statistics(memcg, PageCgroupCache(pc), -1);
+ ClearPageCgroupUsed(pc);
+ unlock_page_cgroup(pc);
+
+ if (PageSwapBacked(oldpage))
+ type = MEM_CGROUP_CHARGE_TYPE_SHMEM;
+
+ zone = page_zone(newpage);
+ pc = lookup_page_cgroup(newpage);
+ /*
+ * Even if newpage->mapping was NULL before starting replacement,
+ * the newpage may be on LRU(or pagevec for LRU) already. We lock
+ * LRU while we overwrite pc->mem_cgroup.
+ */
+ spin_lock_irqsave(&zone->lru_lock, flags);
+ if (PageLRU(newpage))
+ del_page_from_lru_list(zone, newpage, page_lru(newpage));
+ __mem_cgroup_commit_charge(memcg, newpage, 1, pc, type);
+ if (PageLRU(newpage))
+ add_page_to_lru_list(zone, newpage, page_lru(newpage));
+ spin_unlock_irqrestore(&zone->lru_lock, flags);
+}
+
#ifdef CONFIG_DEBUG_VM
static struct page_cgroup *lookup_page_cgroup_used(struct page *page)
{
diff --git a/mm/slub.c b/mm/slub.c
index 7c54fe83a90c..f73234db904d 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -2077,6 +2077,11 @@ static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
goto new_slab;
}
+ /* must check again c->freelist in case of cpu migration or IRQ */
+ object = c->freelist;
+ if (object)
+ goto load_freelist;
+
stat(s, ALLOC_SLOWPATH);
do {
diff --git a/net/mac80211/wpa.c b/net/mac80211/wpa.c
index 7bc8702808fa..ea70837e9d80 100644
--- a/net/mac80211/wpa.c
+++ b/net/mac80211/wpa.c
@@ -105,7 +105,7 @@ ieee80211_rx_h_michael_mic_verify(struct ieee80211_rx_data *rx)
if (status->flag & RX_FLAG_MMIC_ERROR)
goto mic_fail;
- if (!(status->flag & RX_FLAG_IV_STRIPPED))
+ if (!(status->flag & RX_FLAG_IV_STRIPPED) && rx->key)
goto update_iv;
return RX_CONTINUE;
diff --git a/net/sunrpc/xdr.c b/net/sunrpc/xdr.c
index 277ebd4bf095..593f4c605305 100644
--- a/net/sunrpc/xdr.c
+++ b/net/sunrpc/xdr.c
@@ -296,7 +296,7 @@ _copy_to_pages(struct page **pages, size_t pgbase, const char *p, size_t len)
* Copies data into an arbitrary memory location from an array of pages
* The copy is assumed to be non-overlapping.
*/
-static void
+void
_copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len)
{
struct page **pgfrom;
@@ -324,6 +324,7 @@ _copy_from_pages(char *p, struct page **pages, size_t pgbase, size_t len)
} while ((len -= copy) != 0);
}
+EXPORT_SYMBOL_GPL(_copy_from_pages);
/*
* xdr_shrink_bufhead
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index da36d2c085a4..5335605571fe 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -177,8 +177,8 @@ void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
result = ima_store_template(entry, violation, inode);
- if (!result)
+ if (!result || result == -EEXIST)
iint->flags |= IMA_MEASURED;
- else
+ if (result < 0)
kfree(entry);
}
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 8e28f04a5e2e..55a6271bce7a 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -23,6 +23,8 @@
#include <linux/slab.h>
#include "ima.h"
+#define AUDIT_CAUSE_LEN_MAX 32
+
LIST_HEAD(ima_measurements); /* list of all measurements */
/* key: inode (before secure-hashing a file) */
@@ -94,7 +96,8 @@ static int ima_pcr_extend(const u8 *hash)
result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);
if (result != 0)
- pr_err("IMA: Error Communicating to TPM chip\n");
+ pr_err("IMA: Error Communicating to TPM chip, result: %d\n",
+ result);
return result;
}
@@ -106,14 +109,16 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
{
u8 digest[IMA_DIGEST_SIZE];
const char *audit_cause = "hash_added";
+ char tpm_audit_cause[AUDIT_CAUSE_LEN_MAX];
int audit_info = 1;
- int result = 0;
+ int result = 0, tpmresult = 0;
mutex_lock(&ima_extend_list_mutex);
if (!violation) {
memcpy(digest, entry->digest, sizeof digest);
if (ima_lookup_digest_entry(digest)) {
audit_cause = "hash_exists";
+ result = -EEXIST;
goto out;
}
}
@@ -128,9 +133,11 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
if (violation) /* invalidate pcr */
memset(digest, 0xff, sizeof digest);
- result = ima_pcr_extend(digest);
- if (result != 0) {
- audit_cause = "TPM error";
+ tpmresult = ima_pcr_extend(digest);
+ if (tpmresult != 0) {
+ snprintf(tpm_audit_cause, AUDIT_CAUSE_LEN_MAX, "TPM_error(%d)",
+ tpmresult);
+ audit_cause = tpm_audit_cause;
audit_info = 0;
}
out:
diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h
index 2e7ac31afa8d..f52ebe8ba0f5 100644
--- a/sound/pci/hda/hda_local.h
+++ b/sound/pci/hda/hda_local.h
@@ -476,7 +476,12 @@ static inline u32 get_wcaps(struct hda_codec *codec, hda_nid_t nid)
}
/* get the widget type from widget capability bits */
-#define get_wcaps_type(wcaps) (((wcaps) & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT)
+static inline int get_wcaps_type(unsigned int wcaps)
+{
+ if (!wcaps)
+ return -1; /* invalid type */
+ return (wcaps & AC_WCAP_TYPE) >> AC_WCAP_TYPE_SHIFT;
+}
static inline unsigned int get_wcaps_channels(u32 wcaps)
{
diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c
index 2be57b051aa2..6936c37b3059 100644
--- a/sound/pci/hda/hda_proc.c
+++ b/sound/pci/hda/hda_proc.c
@@ -54,6 +54,8 @@ static const char *get_wid_type_name(unsigned int wid_value)
[AC_WID_BEEP] = "Beep Generator Widget",
[AC_WID_VENDOR] = "Vendor Defined Widget",
};
+ if (wid_value == -1)
+ return "UNKNOWN Widget";
wid_value &= 0xf;
if (names[wid_value])
return names[wid_value];
diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c
index 4346ad2c9062..3546d38db8af 100644
--- a/sound/pci/hda/patch_cirrus.c
+++ b/sound/pci/hda/patch_cirrus.c
@@ -916,16 +916,14 @@ static void cs_automute(struct hda_codec *codec)
/* mute speakers if spdif or hp jack is plugged in */
for (i = 0; i < cfg->speaker_outs; i++) {
+ int pin_ctl = hp_present ? 0 : PIN_OUT;
+ /* detect on spdif is specific to CS421x */
+ if (spdif_present && (spec->vendor_nid == CS421X_VENDOR_NID))
+ pin_ctl = 0;
+
nid = cfg->speaker_pins[i];
snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- hp_present ? 0 : PIN_OUT);
- /* detect on spdif is specific to CS421x */
- if (spec->vendor_nid == CS421X_VENDOR_NID) {
- snd_hda_codec_write(codec, nid, 0,
- AC_VERB_SET_PIN_WIDGET_CONTROL,
- spdif_present ? 0 : PIN_OUT);
- }
+ AC_VERB_SET_PIN_WIDGET_CONTROL, pin_ctl);
}
if (spec->board_config == CS420X_MBP53 ||
spec->board_config == CS420X_MBP55 ||
@@ -1756,30 +1754,19 @@ static int build_cs421x_output(struct hda_codec *codec)
struct auto_pin_cfg *cfg = &spec->autocfg;
struct snd_kcontrol *kctl;
int err;
- char *name = "HP/Speakers";
+ char *name = "Master";
fix_volume_caps(codec, dac);
- if (!spec->vmaster_sw) {
- err = add_vmaster(codec, dac);
- if (err < 0)
- return err;
- }
err = add_mute(codec, name, 0,
HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
if (err < 0)
return err;
- err = snd_ctl_add_slave(spec->vmaster_sw, kctl);
- if (err < 0)
- return err;
err = add_volume(codec, name, 0,
HDA_COMPOSE_AMP_VAL(dac, 3, 0, HDA_OUTPUT), 0, &kctl);
if (err < 0)
return err;
- err = snd_ctl_add_slave(spec->vmaster_vol, kctl);
- if (err < 0)
- return err;
if (cfg->speaker_outs) {
err = snd_hda_ctl_add(codec, 0,
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 41fecc1e7ad8..85fe10d5858d 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -1119,8 +1119,6 @@ static const char * const cxt5045_models[CXT5045_MODELS] = {
static const struct snd_pci_quirk cxt5045_cfg_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x30d5, "HP 530", CXT5045_LAPTOP_HP530),
- SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3000, "HP DV Series",
- CXT5045_LAPTOP_HPSENSE),
SND_PCI_QUIRK(0x1179, 0xff31, "Toshiba P105", CXT5045_LAPTOP_MICSENSE),
SND_PCI_QUIRK(0x152d, 0x0753, "Benq R55E", CXT5045_BENQ),
SND_PCI_QUIRK(0x1734, 0x10ad, "Fujitsu Si1520", CXT5045_LAPTOP_MICSENSE),
diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c
index 5261fd85d864..fc3c9034f713 100644
--- a/sound/pci/hda/patch_sigmatel.c
+++ b/sound/pci/hda/patch_sigmatel.c
@@ -4306,6 +4306,27 @@ static void stac_store_hints(struct hda_codec *codec)
}
}
+static void stac_issue_unsol_events(struct hda_codec *codec, int num_pins,
+ const hda_nid_t *pins)
+{
+ while (num_pins--)
+ stac_issue_unsol_event(codec, *pins++);
+}
+
+/* fake event to set up pins */
+static void stac_fake_hp_events(struct hda_codec *codec)
+{
+ struct sigmatel_spec *spec = codec->spec;
+
+ if (spec->autocfg.hp_outs)
+ stac_issue_unsol_events(codec, spec->autocfg.hp_outs,
+ spec->autocfg.hp_pins);
+ if (spec->autocfg.line_outs &&
+ spec->autocfg.line_out_pins[0] != spec->autocfg.hp_pins[0])
+ stac_issue_unsol_events(codec, spec->autocfg.line_outs,
+ spec->autocfg.line_out_pins);
+}
+
static int stac92xx_init(struct hda_codec *codec)
{
struct sigmatel_spec *spec = codec->spec;
@@ -4356,10 +4377,7 @@ static int stac92xx_init(struct hda_codec *codec)
stac92xx_auto_set_pinctl(codec, spec->autocfg.line_out_pins[0],
AC_PINCTL_OUT_EN);
/* fake event to set up pins */
- if (cfg->hp_pins[0])
- stac_issue_unsol_event(codec, cfg->hp_pins[0]);
- else if (cfg->line_out_pins[0])
- stac_issue_unsol_event(codec, cfg->line_out_pins[0]);
+ stac_fake_hp_events(codec);
} else {
stac92xx_auto_init_multi_out(codec);
stac92xx_auto_init_hp_out(codec);
@@ -5000,19 +5018,11 @@ static void stac927x_proc_hook(struct snd_info_buffer *buffer,
#ifdef CONFIG_PM
static int stac92xx_resume(struct hda_codec *codec)
{
- struct sigmatel_spec *spec = codec->spec;
-
stac92xx_init(codec);
snd_hda_codec_resume_amp(codec);
snd_hda_codec_resume_cache(codec);
/* fake event to set up pins again to override cached values */
- if (spec->hp_detect) {
- if (spec->autocfg.hp_pins[0])
- stac_issue_unsol_event(codec, spec->autocfg.hp_pins[0]);
- else if (spec->autocfg.line_out_pins[0])
- stac_issue_unsol_event(codec,
- spec->autocfg.line_out_pins[0]);
- }
+ stac_fake_hp_events(codec);
return 0;
}
diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
index d636d9396415..c3babe3c239d 100644
--- a/sound/pci/hda/patch_via.c
+++ b/sound/pci/hda/patch_via.c
@@ -2187,7 +2187,10 @@ static int via_auto_create_loopback_switch(struct hda_codec *codec)
{
struct via_spec *spec = codec->spec;
- if (!spec->aa_mix_nid || !spec->out_mix_path.depth)
+ if (!spec->aa_mix_nid)
+ return 0; /* no loopback switching available */
+ if (!(spec->out_mix_path.depth || spec->hp_mix_path.depth ||
+ spec->speaker_path.depth))
return 0; /* no loopback switching available */
if (!via_clone_control(spec, &via_aamix_ctl_enum))
return -ENOMEM;
diff --git a/sound/pci/ice1712/amp.c b/sound/pci/ice1712/amp.c
index e328cfb7620c..e525da2673be 100644
--- a/sound/pci/ice1712/amp.c
+++ b/sound/pci/ice1712/amp.c
@@ -68,8 +68,11 @@ static int __devinit snd_vt1724_amp_init(struct snd_ice1712 *ice)
static int __devinit snd_vt1724_amp_add_controls(struct snd_ice1712 *ice)
{
- /* we use pins 39 and 41 of the VT1616 for left and right read outputs */
- snd_ac97_write_cache(ice->ac97, 0x5a, snd_ac97_read(ice->ac97, 0x5a) & ~0x8000);
+ if (ice->ac97)
+ /* we use pins 39 and 41 of the VT1616 for left and right
+ read outputs */
+ snd_ac97_write_cache(ice->ac97, 0x5a,
+ snd_ac97_read(ice->ac97, 0x5a) & ~0x8000);
return 0;
}
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index c822e119555f..d4b822d2d472 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -197,7 +197,8 @@ static int wm8753_set_dai(struct snd_kcontrol *kcontrol,
return 0;
if (codec->active)
- return -EBUSY;
+ printk(KERN_WARNING
+ "Trying to Change the Dai Mode when codec is active\n");
ioctl = snd_soc_read(codec, WM8753_IOCTL);
@@ -911,6 +912,10 @@ static int wm8753_pcm_hw_params(struct snd_pcm_substream *substream,
/* sample rate */
if (params_rate(params) * 384 == wm8753->pcmclk)
srate |= 0x80;
+
+ /* ADC and V-DAC at same sample rate */
+ srate |= 1<<8;
+
snd_soc_write(codec, WM8753_SRATE1, srate);
snd_soc_write(codec, WM8753_PCM, voice);
@@ -1130,6 +1135,10 @@ static int wm8753_i2s_hw_params(struct snd_pcm_substream *substream,
printk(KERN_ERR "wm8753 invalid MCLK or rate\n");
return coeff;
}
+
+ /* ADC and HiFi-DAC at same sample rate */
+ srate &= ~(1<<8);
+
snd_soc_write(codec, WM8753_SRATE1, srate | (coeff_div[coeff].sr << 1) |
coeff_div[coeff].usb);
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index a5193a95cea1..0218d6ce0557 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1268,9 +1268,9 @@ static int wm8903_set_dai_fmt(struct snd_soc_dai *codec_dai,
aif1 |= 0x2;
break;
case SND_SOC_DAIFMT_RIGHT_J:
- aif1 |= 0x1;
break;
case SND_SOC_DAIFMT_LEFT_J:
+ aif1 |= 0x1;
break;
default:
return -EINVAL;
@@ -1457,6 +1457,7 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
int fs = params_rate(params);
int bclk;
int bclk_div;
+ int real_bclk_div;
int i;
int dsp_config;
int clk_config;
@@ -1561,27 +1562,22 @@ static int wm8903_hw_params(struct snd_pcm_substream *substream,
* higher than the target (we need to ensure that there enough
* BCLKs to clock out the samples).
*/
- bclk_div = 0;
- best_val = ((clk_sys * 10) / bclk_divs[0].ratio) - bclk;
- i = 1;
- while (i < ARRAY_SIZE(bclk_divs)) {
- cur_val = ((clk_sys * 10) / bclk_divs[i].ratio) - bclk;
- if (cur_val < 0) /* BCLK table is sorted */
- break;
- bclk_div = i;
- best_val = cur_val;
- i++;
- }
aif2 &= ~WM8903_BCLK_DIV_MASK;
aif3 &= ~WM8903_LRCLK_RATE_MASK;
- dev_dbg(codec->dev, "BCLK ratio %d for %dHz - actual BCLK = %dHz\n",
- bclk_divs[bclk_div].ratio / 10, bclk,
- (clk_sys * 10) / bclk_divs[bclk_div].ratio);
-
- aif2 |= bclk_divs[bclk_div].div;
- aif3 |= bclk / fs;
+ bclk_div = real_bclk_div = 0;
+ cur_val = clk_sys;
+ best_val = clk_sys;
+ while(!(best_val % fs) &&
+ (cur_val >= bclk)){
+ real_bclk_div = bclk_div;
+ bclk_div++;
+ cur_val = best_val;
+ best_val /= 2;
+ }
+ aif2 |= (real_bclk_div ? 1<<real_bclk_div : 0);
+ aif3 |= cur_val / fs;
wm8903->fs = params_rate(params);
wm8903_set_deemph(codec);
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 59a6633915db..f3f185fed61f 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -171,3 +171,25 @@ config SND_SOC_TEGRA_RT5640
Say Y or M here if you want to add support for SoC audio on Tegra
boards using the ALC5640 codec. Currently, the supported boards
are Kai and Cardhu.
+
+config MACH_HAS_SND_SOC_TEGRA_MAX98095
+ bool
+ help
+ Machines that use the SND_SOC_TEGRA_MAX98095 driver should select
+ this config option, in order to allow the user to enable
+ SND_SOC_TEGRA_MAX98095.
+
+config SND_SOC_TEGRA_MAX98095
+ tristate "SoC Audio support for Tegra boards using a MAX98095 codec"
+ depends on SND_SOC_TEGRA && I2C && TEGRA_DC
+ depends on MACH_HAS_SND_SOC_TEGRA_MAX98095
+ select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
+ select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
+ select SND_SOC_TEGRA30_SPDIF if ARCH_TEGRA_3x_SOC
+ select SND_SOC_MAX98095
+ select SND_SOC_SPDIF
+ select SND_SOC_TEGRA30_DAM if ARCH_TEGRA_3x_SOC
+ help
+ Say Y or M here if you want to add support for SoC audio on Tegra
+ boards using the MAX98095 codec. Currently, only supported board is
+ Cardhu.
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index dceef1137534..766e66adb85d 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -28,6 +28,7 @@ snd-soc-tegra-wm8753-objs := tegra_wm8753.o
snd-soc-tegra-max98088-objs := tegra_max98088.o
snd-soc-tegra-aic326x-objs := tegra_aic326x.o
snd-soc-tegra-rt5640-objs := tegra_rt5640.o
+snd-soc-tegra-max98095-objs := tegra_max98095.o
obj-$(CONFIG_SND_SOC_TEGRA_WM8903) += snd-soc-tegra-wm8903.o
obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
@@ -35,3 +36,4 @@ obj-$(CONFIG_SND_SOC_TEGRA_WM8753) += snd-soc-tegra-wm8753.o
obj-$(CONFIG_SND_SOC_TEGRA_MAX98088) += snd-soc-tegra-max98088.o
obj-$(CONFIG_SND_SOC_TEGRA_TLV320AIC326X) += snd-soc-tegra-aic326x.o
obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o
+obj-$(CONFIG_SND_SOC_TEGRA_MAX98095) += snd-soc-tegra-max98095.o
diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c
index 29ce3166052c..0774d360399a 100644
--- a/sound/soc/tegra/tegra20_das.c
+++ b/sound/soc/tegra/tegra20_das.c
@@ -28,6 +28,7 @@
#include <linux/slab.h>
#include <linux/io.h>
#include <mach/iomap.h>
+#include <mach/pinmux.h>
#include <sound/soc.h>
#include "tegra20_das.h"
@@ -66,6 +67,37 @@ int tegra20_das_resume()
}
#endif
+int tegra20_das_set_tristate(int dap_id, int is_tristate)
+{
+ enum tegra_pingroup pin;
+ enum tegra_tristate tristate;
+
+ switch (dap_id) {
+ case TEGRA20_DAS_DAP_ID_1:
+ pin = TEGRA_PINGROUP_DAP1;
+ break;
+ case TEGRA20_DAS_DAP_ID_2:
+ pin = TEGRA_PINGROUP_DAP2;
+ break;
+ case TEGRA20_DAS_DAP_ID_3:
+ pin = TEGRA_PINGROUP_DAP3;
+ break;
+ case TEGRA20_DAS_DAP_ID_4:
+ pin = TEGRA_PINGROUP_DAP4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (is_tristate)
+ tristate = TEGRA_TRI_TRISTATE;
+ else
+ tristate = TEGRA_TRI_NORMAL;
+
+ tegra_pinmux_set_tristate(pin, tristate);
+}
+EXPORT_SYMBOL_GPL(tegra20_das_set_tristate);
+
int tegra20_das_connect_dap_to_dac(int dap, int dac)
{
u32 addr;
diff --git a/sound/soc/tegra/tegra20_das.h b/sound/soc/tegra/tegra20_das.h
index 1d7c57fd0092..0d58c7d1bc3f 100644
--- a/sound/soc/tegra/tegra20_das.h
+++ b/sound/soc/tegra/tegra20_das.h
@@ -143,4 +143,6 @@ extern int tegra20_das_connect_dap_to_dap(int dap_id, int other_dap_sel,
*/
extern int tegra20_das_connect_dac_to_dap(int dac_id, int dap_sel);
+extern int tegra20_das_set_tristate(int dap_id, int is_tristate);
+
#endif
diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c
index 3fceda143da8..710d9465b4b0 100644
--- a/sound/soc/tegra/tegra30_ahub.c
+++ b/sound/soc/tegra/tegra30_ahub.c
@@ -487,6 +487,7 @@ static int __devinit tegra30_ahub_probe(struct platform_device *pdev)
#ifdef CONFIG_PM
int i = 0, cache_idx_rsvd;
#endif
+ int clkm_rate;
if (ahub)
return -ENODEV;
@@ -505,6 +506,11 @@ static int __devinit tegra30_ahub_probe(struct platform_device *pdev)
ret = PTR_ERR(ahub->clk_d_audio);
goto err_free;
}
+ clkm_rate = clk_get_rate(clk_get_parent(ahub->clk_d_audio));
+ while (clkm_rate > 12000000)
+ clkm_rate >>= 1;
+
+ clk_set_rate(ahub->clk_d_audio,clkm_rate);
ahub->clk_apbif = clk_get(&pdev->dev, "apbif");
if (IS_ERR(ahub->clk_apbif)) {
diff --git a/sound/soc/tegra/tegra30_dam.c b/sound/soc/tegra/tegra30_dam.c
index 4ac81266e7cf..d308179110c9 100644
--- a/sound/soc/tegra/tegra30_dam.c
+++ b/sound/soc/tegra/tegra30_dam.c
@@ -529,6 +529,7 @@ static int __devinit tegra30_dam_probe(struct platform_device *pdev)
#ifdef CONFIG_PM
int i;
#endif
+ int clkm_rate;
if ((pdev->id < 0) ||
(pdev->id >= TEGRA30_NR_DAM_IFC)) {
@@ -552,6 +553,11 @@ static int __devinit tegra30_dam_probe(struct platform_device *pdev)
ret = PTR_ERR(dam->dam_clk);
goto err_free;
}
+ clkm_rate = clk_get_rate(clk_get_parent(dam->dam_clk));
+ while (clkm_rate > 12000000)
+ clkm_rate >>= 1;
+
+ clk_set_rate(dam->dam_clk,clkm_rate);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index c1de635765a5..9a0ed5a762ed 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -303,13 +303,11 @@ static int tegra30_i2s_hw_params(struct snd_pcm_substream *substream,
srate = params_rate(params);
if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_MASTER_ENABLE) {
- /* Final "* 2" required by Tegra hardware */
- i2sclock = srate * params_channels(params) * sample_size * 2;
+ i2sclock = srate * params_channels(params) * sample_size;
- /* Additional "* 2" is needed for FSYNC mode */
+ /* Additional "* 4" is needed for FSYNC mode */
if (i2s->reg_ctrl & TEGRA30_I2S_CTRL_FRAME_FORMAT_FSYNC)
- i2sclock *= 2;
-
+ i2sclock *= 4;
ret = clk_set_parent(i2s->clk_i2s, i2s->clk_pll_a_out0);
if (ret) {
dev_err(dev, "Can't set parent of I2S clock\n");
diff --git a/sound/soc/tegra/tegra_aic326x.c b/sound/soc/tegra/tegra_aic326x.c
index a7297a54233a..31afb907a653 100644
--- a/sound/soc/tegra/tegra_aic326x.c
+++ b/sound/soc/tegra/tegra_aic326x.c
@@ -237,6 +237,32 @@ static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream,
if (mclk < 0)
return mclk;
+
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ clk = clk_get_sys(NULL, "cdev1");
+#else
+ clk = clk_get_sys("extern1", NULL);
+#endif
+ if (IS_ERR(clk)) {
+ dev_err(card->dev, "Can't retrieve clk cdev1\n");
+ err = PTR_ERR(clk);
+ return err;
+ }
+
+ rate = clk_get_rate(clk);
+ printk("extern1 rate=%d\n",rate);
+
+#if TEGRA30_I2S_MASTER_PLAYBACK
+ daifmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS;
+#else
+ daifmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+ mclk = rate;
+#endif
+
err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
if (err < 0) {
if (!(machine->util_data.set_mclk % mclk))
@@ -249,9 +275,6 @@ static int tegra_aic326x_hw_params(struct snd_pcm_substream *substream,
tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
- daifmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS;
-
err = snd_soc_dai_set_fmt(codec_dai, daifmt);
if (err < 0) {
dev_err(card->dev, "codec_dai fmt not set\n");
diff --git a/sound/soc/tegra/tegra_asoc_utils.c b/sound/soc/tegra/tegra_asoc_utils.c
index c517bd04e2da..1f336d82bfe3 100644
--- a/sound/soc/tegra/tegra_asoc_utils.c
+++ b/sound/soc/tegra/tegra_asoc_utils.c
@@ -25,6 +25,8 @@
#include <linux/err.h>
#include <linux/kernel.h>
+#include <mach/clk.h>
+
#include "tegra_asoc_utils.h"
int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
@@ -33,6 +35,7 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
int new_baseclock;
bool clk_change;
int err;
+ bool reenable_clock;
switch (srate) {
case 11025:
@@ -73,41 +76,32 @@ int tegra_asoc_utils_set_rate(struct tegra_asoc_utils_data *data, int srate,
data->set_baseclock = 0;
data->set_mclk = 0;
- clk_disable(data->clk_cdev1);
- clk_disable(data->clk_pll_a_out0);
- clk_disable(data->clk_pll_a);
-
+ reenable_clock = false;
+ if(tegra_is_clk_enabled(data->clk_pll_a)) {
+ clk_disable(data->clk_pll_a);
+ reenable_clock = true;
+ }
err = clk_set_rate(data->clk_pll_a, new_baseclock);
if (err) {
dev_err(data->dev, "Can't set pll_a rate: %d\n", err);
return err;
}
+ if(reenable_clock)
+ clk_enable(data->clk_pll_a);
- err = clk_set_rate(data->clk_pll_a_out0, mclk);
- if (err) {
- dev_err(data->dev, "Can't set pll_a_out0 rate: %d\n", err);
- return err;
+ reenable_clock = false;
+ if(tegra_is_clk_enabled(data->clk_pll_a_out0)) {
+ clk_disable(data->clk_pll_a_out0);
+ reenable_clock = true;
}
-
- /* Don't set cdev1/extern1 rate; it's locked to pll_a_out0 */
-
- err = clk_enable(data->clk_pll_a);
- if (err) {
- dev_err(data->dev, "Can't enable pll_a: %d\n", err);
- return err;
- }
-
- err = clk_enable(data->clk_pll_a_out0);
+ err = clk_set_rate(data->clk_pll_a_out0, mclk);
if (err) {
- dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
+ dev_err(data->dev, "Can't set clk_pll_a_out0 rate: %d\n", err);
return err;
}
+ if(reenable_clock)
+ clk_enable(data->clk_pll_a_out0);
- err = clk_enable(data->clk_cdev1);
- if (err) {
- dev_err(data->dev, "Can't enable cdev1: %d\n", err);
- return err;
- }
data->set_baseclock = new_baseclock;
data->set_mclk = mclk;
@@ -130,18 +124,6 @@ int tegra_asoc_utils_clk_enable(struct tegra_asoc_utils_data *data)
{
int err;
- err = clk_enable(data->clk_pll_a);
- if (err) {
- dev_err(data->dev, "Can't enable pll_a: %d\n", err);
- return err;
- }
-
- err = clk_enable(data->clk_pll_a_out0);
- if (err) {
- dev_err(data->dev, "Can't enable pll_a_out0: %d\n", err);
- return err;
- }
-
err = clk_enable(data->clk_cdev1);
if (err) {
dev_err(data->dev, "Can't enable cdev1: %d\n", err);
@@ -155,8 +137,6 @@ EXPORT_SYMBOL_GPL(tegra_asoc_utils_clk_enable);
int tegra_asoc_utils_clk_disable(struct tegra_asoc_utils_data *data)
{
clk_disable(data->clk_cdev1);
- clk_disable(data->clk_pll_a_out0);
- clk_disable(data->clk_pll_a);
return 0;
}
EXPORT_SYMBOL_GPL(tegra_asoc_utils_clk_disable);
@@ -165,14 +145,22 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
struct device *dev)
{
int ret;
+ int rate;
data->dev = dev;
+ data->clk_pll_p_out1 = clk_get_sys(NULL, "pll_p_out1");
+ if (IS_ERR(data->clk_pll_p_out1)) {
+ dev_err(data->dev, "Can't retrieve clk pll_p_out1\n");
+ ret = PTR_ERR(data->clk_pll_p_out1);
+ goto err;
+ }
+
data->clk_pll_a = clk_get_sys(NULL, "pll_a");
if (IS_ERR(data->clk_pll_a)) {
dev_err(data->dev, "Can't retrieve clk pll_a\n");
ret = PTR_ERR(data->clk_pll_a);
- goto err;
+ goto err_put_pll_p_out1;
}
data->clk_pll_a_out0 = clk_get_sys(NULL, "pll_a_out0");
@@ -182,6 +170,13 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
goto err_put_pll_a;
}
+ data->clk_m = clk_get_sys(NULL, "clk_m");
+ if (IS_ERR(data->clk_m)) {
+ dev_err(data->dev, "Can't retrieve clk clk_m\n");
+ ret = PTR_ERR(data->clk_m);
+ goto err;
+ }
+
#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
data->clk_cdev1 = clk_get_sys(NULL, "cdev1");
#else
@@ -204,26 +199,28 @@ int tegra_asoc_utils_init(struct tegra_asoc_utils_data *data,
}
#endif
- ret = clk_enable(data->clk_pll_a);
+#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#if TEGRA30_I2S_MASTER_PLAYBACK
+ ret = clk_set_parent(data->clk_cdev1, data->clk_pll_a_out0);
if (ret) {
- dev_err(data->dev, "Can't enable clk pll_a");
+ dev_err(data->dev, "Can't set clk cdev1/extern1 parent");
goto err_put_out1;
}
+#else
+ rate = clk_get_rate(data->clk_m);
- ret = clk_enable(data->clk_pll_a_out0);
- if (ret) {
- dev_err(data->dev, "Can't enable clk pll_a_out0");
- goto err_put_out1;
- }
+ if(rate == 26000000)
+ clk_set_rate(data->clk_cdev1, 13000000);
-#if !defined(CONFIG_ARCH_TEGRA_2x_SOC)
- ret = clk_set_parent(data->clk_cdev1, data->clk_pll_a_out0);
+ ret = clk_set_parent(data->clk_cdev1, data->clk_m);
if (ret) {
dev_err(data->dev, "Can't set clk cdev1/extern1 parent");
goto err_put_out1;
}
#endif
+#endif
+
ret = clk_enable(data->clk_cdev1);
if (ret) {
dev_err(data->dev, "Can't enable clk cdev1/extern1");
@@ -255,6 +252,8 @@ err_put_pll_a_out0:
clk_put(data->clk_pll_a_out0);
err_put_pll_a:
clk_put(data->clk_pll_a);
+err_put_pll_p_out1:
+ clk_put(data->clk_pll_p_out1);
err:
return ret;
}
@@ -264,9 +263,17 @@ void tegra_asoc_utils_fini(struct tegra_asoc_utils_data *data)
{
if (!IS_ERR(data->clk_out1))
clk_put(data->clk_out1);
+
clk_put(data->clk_cdev1);
- clk_put(data->clk_pll_a_out0);
- clk_put(data->clk_pll_a);
+
+ if (!IS_ERR(data->clk_pll_a_out0))
+ clk_put(data->clk_pll_a_out0);
+
+ if (!IS_ERR(data->clk_pll_a))
+ clk_put(data->clk_pll_a);
+
+ if (!IS_ERR(data->clk_pll_p_out1))
+ clk_put(data->clk_pll_p_out1);
}
EXPORT_SYMBOL_GPL(tegra_asoc_utils_fini);
diff --git a/sound/soc/tegra/tegra_asoc_utils.h b/sound/soc/tegra/tegra_asoc_utils.h
index 72d3994a935f..1c4e521cb4ba 100644
--- a/sound/soc/tegra/tegra_asoc_utils.h
+++ b/sound/soc/tegra/tegra_asoc_utils.h
@@ -23,6 +23,9 @@
#ifndef __TEGRA_ASOC_UTILS_H__
#define __TEGRA_ASOC_UTILS_H_
+
+#define TEGRA30_I2S_MASTER_PLAYBACK 1
+
struct clk;
struct device;
@@ -32,6 +35,8 @@ struct tegra_asoc_utils_data {
struct clk *clk_pll_a_out0;
struct clk *clk_cdev1;
struct clk *clk_out1;
+ struct clk *clk_m;
+ struct clk *clk_pll_p_out1;
int set_baseclock;
int set_mclk;
int lock_count;
diff --git a/sound/soc/tegra/tegra_max98088.c b/sound/soc/tegra/tegra_max98088.c
index d9308ba54b99..61f45936e440 100644
--- a/sound/soc/tegra/tegra_max98088.c
+++ b/sound/soc/tegra/tegra_max98088.c
@@ -30,6 +30,7 @@
#include <asm/mach-types.h>
+#include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -81,6 +82,8 @@ const char *tegra_max98088_i2s_dai_name[TEGRA30_NR_I2S_IFC] = {
};
#endif
+static int g_is_call_mode;
+
struct tegra_max98088 {
struct tegra_asoc_utils_data util_data;
struct tegra_max98088_platform_data *pdata;
@@ -95,6 +98,15 @@ struct tegra_max98088 {
struct snd_soc_card *pcard;
};
+bool tegra_is_voice_call_active()
+{
+ if (g_is_call_mode)
+ return true;
+ else
+ return false;
+}
+EXPORT_SYMBOL_GPL(tegra_is_voice_call_active);
+
static int tegra_call_mode_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
@@ -156,6 +168,7 @@ static int tegra_call_mode_put(struct snd_kcontrol *kcontrol,
}
machine->is_call_mode = is_call_mode_new;
+ g_is_call_mode = machine->is_call_mode;
return 1;
}
@@ -211,8 +224,10 @@ static int tegra_max98088_hw_params(struct snd_pcm_substream *substream,
#ifndef CONFIG_ARCH_TEGRA_2x_SOC
struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
#endif
- int srate, mclk, sample_size;
+ int srate, mclk, sample_size, i2s_daifmt;
int err;
+ struct clk *clk;
+ int rate;
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
@@ -244,6 +259,32 @@ static int tegra_max98088_hw_params(struct snd_pcm_substream *substream,
break;
}
+
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+ clk = clk_get_sys(NULL, "cdev1");
+#else
+ clk = clk_get_sys("extern1", NULL);
+#endif
+ if (IS_ERR(clk)) {
+ dev_err(card->dev, "Can't retrieve clk cdev1\n");
+ err = PTR_ERR(clk);
+ return err;
+ }
+
+ rate = clk_get_rate(clk);
+ printk("extern1 rate=%d\n",rate);
+
+#if TEGRA30_I2S_MASTER_PLAYBACK
+ i2s_daifmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS;
+#else
+ i2s_daifmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+ mclk = rate;
+#endif
+
err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
if (err < 0) {
if (!(machine->util_data.set_mclk % mclk))
@@ -256,19 +297,13 @@ static int tegra_max98088_hw_params(struct snd_pcm_substream *substream,
tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
- err = snd_soc_dai_set_fmt(codec_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
+ err = snd_soc_dai_set_fmt(codec_dai,i2s_daifmt);
if (err < 0) {
dev_err(card->dev, "codec_dai fmt not set\n");
return err;
}
- err = snd_soc_dai_set_fmt(cpu_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS);
+ err = snd_soc_dai_set_fmt(cpu_dai, i2s_daifmt);
if (err < 0) {
dev_err(card->dev, "cpu_dai fmt not set\n");
return err;
diff --git a/sound/soc/tegra/tegra_max98095.c b/sound/soc/tegra/tegra_max98095.c
new file mode 100644
index 000000000000..e71691d80b45
--- /dev/null
+++ b/sound/soc/tegra/tegra_max98095.c
@@ -0,0 +1,723 @@
+/*
+ * tegra_max98095.c - Tegra machine ASoC driver for boards using MAX98095 codec.
+ *
+ * Author: Ravindra Lokhande <rlokhande@nvidia.com>
+ * Copyright (C) 2012 - NVIDIA, Inc.
+ *
+ * Based on version from Sumit Bhattacharya <sumitb@nvidia.com>
+ *
+ * Based on code copyright/by:
+ *
+ * (c) 2010, 2011, 2012 Nvidia Graphics Pvt. Ltd.
+ *
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ * Author: Graeme Gregory
+ * graeme.gregory@wolfsonmicro.com or linux@wolfsonmicro.com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <asm/mach-types.h>
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/regulator/consumer.h>
+#ifdef CONFIG_SWITCH
+#include <linux/switch.h>
+#endif
+
+#include <mach/tegra_asoc_pdata.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/max98095.h"
+
+#include "tegra_pcm.h"
+#include "tegra_asoc_utils.h"
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+#include "tegra30_ahub.h"
+#include "tegra30_i2s.h"
+#include "tegra30_dam.h"
+#endif
+
+#define DRV_NAME "tegra-snd-max98095"
+
+#define GPIO_SPKR_EN BIT(0)
+#define GPIO_HP_MUTE BIT(1)
+#define GPIO_INT_MIC_EN BIT(2)
+#define GPIO_EXT_MIC_EN BIT(3)
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+const char *tegra_max98095_i2s_dai_name[TEGRA30_NR_I2S_IFC] = {
+ "tegra30-i2s.0",
+ "tegra30-i2s.1",
+ "tegra30-i2s.2",
+ "tegra30-i2s.3",
+ "tegra30-i2s.4",
+};
+#endif
+
+struct tegra_max98095 {
+ struct tegra_asoc_utils_data util_data;
+ struct tegra_asoc_platform_data *pdata;
+ int gpio_requested;
+ bool init_done;
+ int is_call_mode;
+ int is_device_bt;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ struct codec_config codec_info[NUM_I2S_DEVICES];
+#endif
+ enum snd_soc_bias_level bias_level;
+};
+
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+static int tegra_max98095_set_dam_cif(int dam_ifc, int srate,
+ int channels, int bit_size)
+{
+ tegra30_dam_set_samplerate(dam_ifc, TEGRA30_DAM_CHOUT,
+ srate);
+ tegra30_dam_set_samplerate(dam_ifc, TEGRA30_DAM_CHIN1,
+ srate);
+ tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHIN1,
+ channels, bit_size, channels,
+ bit_size);
+ tegra30_dam_set_acif(dam_ifc, TEGRA30_DAM_CHOUT,
+ channels, bit_size, channels,
+ bit_size);
+
+ return 0;
+}
+#endif
+
+static int tegra_max98095_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_max98095 *machine = snd_soc_card_get_drvdata(card);
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+#endif
+ unsigned int srate, mclk, sample_size;
+ int err;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ sample_size = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 64000:
+ case 96000:
+ mclk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ mclk = 11289600;
+ break;
+ default:
+ mclk = 12000000;
+ break;
+ }
+
+ err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
+ if (err < 0) {
+ if (!(machine->util_data.set_mclk % mclk))
+ mclk = machine->util_data.set_mclk;
+ else {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+ }
+
+ tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
+
+ err = snd_soc_dai_set_fmt(codec_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_fmt(cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS);
+ if (err < 0) {
+ dev_err(card->dev, "cpu_dai fmt not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai, 0, mclk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ tegra_max98095_set_dam_cif(i2s->dam_ifc, srate,
+ params_channels(params), sample_size);
+#endif
+
+ return 0;
+}
+
+static int tegra_spdif_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_card *card = rtd->card;
+ struct tegra_max98095 *machine = snd_soc_card_get_drvdata(card);
+ unsigned int srate, mclk, min_mclk;
+ int err;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ mclk = 11289600;
+ break;
+ case 8000:
+ case 16000:
+ case 32000:
+ case 48000:
+ case 64000:
+ case 96000:
+ mclk = 12288000;
+ break;
+ default:
+ return -EINVAL;
+ }
+ min_mclk = 128 * srate;
+
+ err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
+ if (err < 0) {
+ if (!(machine->util_data.set_mclk % min_mclk))
+ mclk = machine->util_data.set_mclk;
+ else {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+ }
+
+ tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
+
+
+
+ return 0;
+}
+
+static int tegra_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct tegra_max98095 *machine = snd_soc_card_get_drvdata(rtd->card);
+
+ tegra_asoc_utils_lock_clk_rate(&machine->util_data, 0);
+
+ return 0;
+}
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+static int tegra_max98095_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if ((substream->stream != SNDRV_PCM_STREAM_PLAYBACK) ||
+ !(i2s->is_dam_used))
+ return 0;
+
+ /*dam configuration*/
+ if (!i2s->dam_ch_refcount)
+ i2s->dam_ifc = tegra30_dam_allocate_controller();
+
+ tegra30_dam_allocate_channel(i2s->dam_ifc, TEGRA30_DAM_CHIN1);
+ i2s->dam_ch_refcount++;
+ tegra30_dam_enable_clock(i2s->dam_ifc);
+ tegra30_dam_set_gain(i2s->dam_ifc, TEGRA30_DAM_CHIN1, 0x1000);
+
+ tegra30_ahub_set_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 +
+ (i2s->dam_ifc*2), i2s->txcif);
+
+ /*
+ *make the dam tx to i2s rx connection if this is the only client
+ *using i2s for playback
+ */
+ if (i2s->playback_ref_count == 1)
+ tegra30_ahub_set_rx_cif_source(
+ TEGRA30_AHUB_RXCIF_I2S0_RX0 + i2s->id,
+ TEGRA30_AHUB_TXCIF_DAM0_TX0 + i2s->dam_ifc);
+
+ /* enable the dam*/
+ tegra30_dam_enable(i2s->dam_ifc, TEGRA30_DAM_ENABLE,
+ TEGRA30_DAM_CHIN1);
+
+ return 0;
+}
+
+static void tegra_max98095_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if ((substream->stream != SNDRV_PCM_STREAM_PLAYBACK) ||
+ !(i2s->is_dam_used))
+ return;
+
+ /* disable the dam*/
+ tegra30_dam_enable(i2s->dam_ifc, TEGRA30_DAM_DISABLE,
+ TEGRA30_DAM_CHIN1);
+
+ /* disconnect the ahub connections*/
+ tegra30_ahub_unset_rx_cif_source(TEGRA30_AHUB_RXCIF_DAM0_RX1 +
+ (i2s->dam_ifc*2));
+
+ /* disable the dam and free the controller */
+ tegra30_dam_disable_clock(i2s->dam_ifc);
+ tegra30_dam_free_channel(i2s->dam_ifc, TEGRA30_DAM_CHIN1);
+ i2s->dam_ch_refcount--;
+ if (!i2s->dam_ch_refcount)
+ tegra30_dam_free_controller(i2s->dam_ifc);
+
+ return;
+}
+#endif
+
+static struct snd_soc_ops tegra_max98095_ops = {
+ .hw_params = tegra_max98095_hw_params,
+ .hw_free = tegra_hw_free,
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ .startup = tegra_max98095_startup,
+ .shutdown = tegra_max98095_shutdown,
+#endif
+};
+
+static struct snd_soc_ops tegra_spdif_ops = {
+ .hw_params = tegra_spdif_hw_params,
+ .hw_free = tegra_hw_free,
+};
+
+static struct snd_soc_jack tegra_max98095_hp_jack;
+
+#ifdef CONFIG_SWITCH
+static struct switch_dev wired_switch_dev = {
+ .name = "h2w",
+};
+
+/* These values are copied from WiredAccessoryObserver */
+enum headset_state {
+ BIT_NO_HEADSET = 0,
+ BIT_HEADSET = (1 << 0),
+ BIT_HEADSET_NO_MIC = (1 << 1),
+};
+
+static int headset_switch_notify(struct notifier_block *self,
+ unsigned long action, void *dev)
+{
+ int state = 0;
+
+ switch (action) {
+ case SND_JACK_HEADPHONE:
+ state |= BIT_HEADSET_NO_MIC;
+ break;
+ case SND_JACK_HEADSET:
+ state |= BIT_HEADSET;
+ break;
+ default:
+ state |= BIT_NO_HEADSET;
+ }
+
+ switch_set_state(&wired_switch_dev, state);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block headset_switch_nb = {
+ .notifier_call = headset_switch_notify,
+};
+#else
+static struct snd_soc_jack_pin tegra_max98095_hp_jack_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+};
+#endif
+
+static int tegra_max98095_event_int_spk(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct tegra_max98095 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_asoc_platform_data *pdata = machine->pdata;
+
+ if (!(machine->gpio_requested & GPIO_SPKR_EN))
+ return 0;
+
+ gpio_set_value_cansleep(pdata->gpio_spkr_en,
+ SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static int tegra_max98095_event_hp(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct tegra_max98095 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_asoc_platform_data *pdata = machine->pdata;
+
+ if (!(machine->gpio_requested & GPIO_HP_MUTE))
+ return 0;
+
+ gpio_set_value_cansleep(pdata->gpio_hp_mute,
+ !SND_SOC_DAPM_EVENT_ON(event));
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget tegra_max98095_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("Int Spk", tegra_max98095_event_int_spk),
+ SND_SOC_DAPM_OUTPUT("Earpiece"),
+ SND_SOC_DAPM_HP("Headphone Jack", tegra_max98095_event_hp),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_INPUT("Int Mic"),
+};
+
+static const struct snd_soc_dapm_route enterprise_audio_map[] = {
+ {"Int Spk", NULL, "SPKL"},
+ {"Int Spk", NULL, "SPKR"},
+ {"Earpiece", NULL, "RECL"},
+ {"Earpiece", NULL, "RECR"},
+ {"Headphone Jack", NULL, "HPL"},
+ {"Headphone Jack", NULL, "HPR"},
+ {"MICBIAS", NULL, "Mic Jack"},
+ {"MIC2", NULL, "MICBIAS"},
+ {"MICBIAS", NULL, "Int Mic"},
+ {"MIC1", NULL, "MICBIAS"},
+};
+
+static const struct snd_kcontrol_new tegra_max98095_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Int Spk"),
+ SOC_DAPM_PIN_SWITCH("Earpiece"),
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Mic Jack"),
+ SOC_DAPM_PIN_SWITCH("Int Mic"),
+};
+
+static int tegra_max98095_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = &codec->dapm;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_max98095 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_asoc_platform_data *pdata = machine->pdata;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ struct tegra30_i2s *i2s = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+#endif
+ int ret;
+
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ if (machine->codec_info[BASEBAND].i2s_id != -1)
+ i2s->is_dam_used = true;
+#endif
+
+ if (machine->init_done)
+ return 0;
+
+ machine->init_done = true;
+
+ if (gpio_is_valid(pdata->gpio_spkr_en)) {
+ ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get spkr_en gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_SPKR_EN;
+
+ gpio_direction_output(pdata->gpio_spkr_en, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_hp_mute)) {
+ ret = gpio_request(pdata->gpio_hp_mute, "hp_mute");
+ if (ret) {
+ dev_err(card->dev, "cannot get hp_mute gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_HP_MUTE;
+
+ gpio_direction_output(pdata->gpio_hp_mute, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_int_mic_en)) {
+ ret = gpio_request(pdata->gpio_int_mic_en, "int_mic_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get int_mic_en gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_INT_MIC_EN;
+
+ /* Disable int mic; enable signal is active-high */
+ gpio_direction_output(pdata->gpio_int_mic_en, 0);
+ }
+
+ if (gpio_is_valid(pdata->gpio_ext_mic_en)) {
+ ret = gpio_request(pdata->gpio_ext_mic_en, "ext_mic_en");
+ if (ret) {
+ dev_err(card->dev, "cannot get ext_mic_en gpio\n");
+ return ret;
+ }
+ machine->gpio_requested |= GPIO_EXT_MIC_EN;
+
+ /* Enable ext mic; enable signal is active-low */
+ gpio_direction_output(pdata->gpio_ext_mic_en, 0);
+ }
+
+ ret = snd_soc_add_controls(codec, tegra_max98095_controls,
+ ARRAY_SIZE(tegra_max98095_controls));
+ if (ret < 0)
+ return ret;
+
+ snd_soc_dapm_new_controls(dapm, tegra_max98095_dapm_widgets,
+ ARRAY_SIZE(tegra_max98095_dapm_widgets));
+
+ snd_soc_dapm_add_routes(dapm, enterprise_audio_map,
+ ARRAY_SIZE(enterprise_audio_map));
+
+ ret = snd_soc_jack_new(codec, "Headset Jack", SND_JACK_HEADSET,
+ &tegra_max98095_hp_jack);
+ if (ret < 0)
+ return ret;
+
+#ifdef CONFIG_SWITCH
+ snd_soc_jack_notifier_register(&tegra_max98095_hp_jack,
+ &headset_switch_nb);
+#else /*gpio based headset detection*/
+ snd_soc_jack_add_pins(&tegra_max98095_hp_jack,
+ ARRAY_SIZE(tegra_max98095_hp_jack_pins),
+ tegra_max98095_hp_jack_pins);
+#endif
+
+ /* max98095_headset_detect(codec, &tegra_max98095_hp_jack,
+ SND_JACK_HEADSET); */
+
+ snd_soc_dapm_nc_pin(dapm, "INA1");
+ snd_soc_dapm_nc_pin(dapm, "INA2");
+ snd_soc_dapm_nc_pin(dapm, "INB1");
+ snd_soc_dapm_nc_pin(dapm, "INB2");
+ snd_soc_dapm_sync(dapm);
+
+ return 0;
+}
+
+static struct snd_soc_dai_link tegra_max98095_dai[] = {
+ {
+ .name = "MAX98095",
+ .stream_name = "MAX98095 HIFI",
+ .codec_name = "max98095.4-0010",
+ .platform_name = "tegra-pcm-audio",
+ .cpu_dai_name = "tegra30-i2s.1",
+ .codec_dai_name = "HiFi",
+ .init = tegra_max98095_init,
+ .ops = &tegra_max98095_ops,
+ },
+ {
+ .name = "SPDIF",
+ .stream_name = "SPDIF PCM",
+ .codec_name = "spdif-dit.0",
+ .platform_name = "tegra-pcm-audio",
+ .cpu_dai_name = "tegra30-spdif",
+ .codec_dai_name = "dit-hifi",
+ .ops = &tegra_spdif_ops,
+ },
+};
+
+static int tegra30_soc_set_bias_level(struct snd_soc_card *card,
+ enum snd_soc_bias_level level)
+{
+ struct tegra_max98095 *machine = snd_soc_card_get_drvdata(card);
+
+ if (machine->bias_level == SND_SOC_BIAS_OFF &&
+ level != SND_SOC_BIAS_OFF)
+ tegra_asoc_utils_clk_enable(&machine->util_data);
+
+ machine->bias_level = level;
+
+ return 0;
+}
+
+static int tegra30_soc_set_bias_level_post(struct snd_soc_card *card,
+ enum snd_soc_bias_level level)
+{
+ struct tegra_max98095 *machine = snd_soc_card_get_drvdata(card);
+
+ if (level == SND_SOC_BIAS_OFF)
+ tegra_asoc_utils_clk_disable(&machine->util_data);
+
+ return 0 ;
+}
+
+static struct snd_soc_card snd_soc_tegra_max98095 = {
+ .name = "tegra-max98095",
+ .dai_link = tegra_max98095_dai,
+ .num_links = ARRAY_SIZE(tegra_max98095_dai),
+ .set_bias_level = tegra30_soc_set_bias_level,
+ .set_bias_level_post = tegra30_soc_set_bias_level_post,
+};
+
+static __devinit int tegra_max98095_driver_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &snd_soc_tegra_max98095;
+ struct tegra_max98095 *machine;
+ struct tegra_asoc_platform_data *pdata;
+ int ret;
+
+ pdata = pdev->dev.platform_data;
+ if (!pdata) {
+ dev_err(&pdev->dev, "No platform data supplied\n");
+ return -EINVAL;
+ }
+
+ machine = kzalloc(sizeof(struct tegra_max98095), GFP_KERNEL);
+ if (!machine) {
+ dev_err(&pdev->dev, "Can't allocate tegra_max98095 struct\n");
+ return -ENOMEM;
+ }
+
+ machine->pdata = pdata;
+
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ if (ret)
+ goto err_free_machine;
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+#ifdef CONFIG_SWITCH
+ /* Add h2w switch class support */
+ ret = switch_dev_register(&wired_switch_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "not able to register switch device\n");
+ goto err_fini_utils;
+ }
+#endif
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err_switch_unregister;
+ }
+
+ return 0;
+
+err_switch_unregister:
+#ifdef CONFIG_SWITCH
+ switch_dev_unregister(&wired_switch_dev);
+#endif
+err_fini_utils:
+ tegra_asoc_utils_fini(&machine->util_data);
+err_free_machine:
+ kfree(machine);
+ return ret;
+}
+
+static int __devexit tegra_max98095_driver_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_max98095 *machine = snd_soc_card_get_drvdata(card);
+ struct tegra_asoc_platform_data *pdata = machine->pdata;
+
+ snd_soc_unregister_card(card);
+
+#ifdef CONFIG_SWITCH
+ switch_dev_unregister(&wired_switch_dev);
+#endif
+
+ tegra_asoc_utils_fini(&machine->util_data);
+
+ if (machine->gpio_requested & GPIO_EXT_MIC_EN)
+ gpio_free(pdata->gpio_ext_mic_en);
+ if (machine->gpio_requested & GPIO_INT_MIC_EN)
+ gpio_free(pdata->gpio_int_mic_en);
+ if (machine->gpio_requested & GPIO_HP_MUTE)
+ gpio_free(pdata->gpio_hp_mute);
+ if (machine->gpio_requested & GPIO_SPKR_EN)
+ gpio_free(pdata->gpio_spkr_en);
+
+ kfree(machine);
+
+ return 0;
+}
+
+static struct platform_driver tegra_max98095_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = tegra_max98095_driver_probe,
+ .remove = __devexit_p(tegra_max98095_driver_remove),
+};
+
+static int __init tegra_max98095_modinit(void)
+{
+ return platform_driver_register(&tegra_max98095_driver);
+}
+module_init(tegra_max98095_modinit);
+
+static void __exit tegra_max98095_modexit(void)
+{
+ platform_driver_unregister(&tegra_max98095_driver);
+}
+module_exit(tegra_max98095_modexit);
+
+MODULE_AUTHOR("Ravindra Lokhande <rlokhande@nvidia.com>");
+MODULE_DESCRIPTION("Tegra+MAX98095 machine ASoC driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/tegra/tegra_pcm.c b/sound/soc/tegra/tegra_pcm.c
index a27f65f68325..439e412f17c6 100644
--- a/sound/soc/tegra/tegra_pcm.c
+++ b/sound/soc/tegra/tegra_pcm.c
@@ -185,15 +185,6 @@ static int tegra_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0)
goto err;
-#ifdef CONFIG_HAS_WAKELOCK
- snprintf(prtd->tegra_wake_lock_name, sizeof(prtd->tegra_wake_lock_name),
- "tegra-pcm-%s-%d",
- (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ? "out" : "in",
- substream->pcm->device);
- wake_lock_init(&prtd->tegra_wake_lock, WAKE_LOCK_SUSPEND,
- prtd->tegra_wake_lock_name);
-#endif
-
return 0;
err:
@@ -211,10 +202,6 @@ static int tegra_pcm_close(struct snd_pcm_substream *substream)
struct snd_pcm_runtime *runtime = substream->runtime;
struct tegra_runtime_data *prtd = runtime->private_data;
-#ifdef CONFIG_HAS_WAKELOCK
- wake_lock_destroy(&prtd->tegra_wake_lock);
-#endif
-
if (prtd->dma_chan)
tegra_dma_free_channel(prtd->dma_chan);
@@ -259,9 +246,6 @@ static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
/* Fall-through */
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-#ifdef CONFIG_HAS_WAKELOCK
- wake_lock(&prtd->tegra_wake_lock);
-#endif
spin_lock_irqsave(&prtd->lock, flags);
prtd->running = 1;
spin_unlock_irqrestore(&prtd->lock, flags);
@@ -276,10 +260,6 @@ static int tegra_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
spin_unlock_irqrestore(&prtd->lock, flags);
tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[0]);
tegra_dma_dequeue_req(prtd->dma_chan, &prtd->dma_req[1]);
-
-#ifdef CONFIG_HAS_WAKELOCK
- wake_unlock(&prtd->tegra_wake_lock);
-#endif
break;
default:
return -EINVAL;
diff --git a/sound/soc/tegra/tegra_pcm.h b/sound/soc/tegra/tegra_pcm.h
index 883c979268de..dbb90339fe0d 100644
--- a/sound/soc/tegra/tegra_pcm.h
+++ b/sound/soc/tegra/tegra_pcm.h
@@ -33,10 +33,6 @@
#include <mach/dma.h>
-#ifdef CONFIG_HAS_WAKELOCK
-#include <linux/wakelock.h>
-#endif
-
struct tegra_pcm_dma_params {
unsigned long addr;
unsigned long wrap;
@@ -54,10 +50,6 @@ struct tegra_runtime_data {
int dma_req_idx;
struct tegra_dma_req dma_req[2];
struct tegra_dma_channel *dma_chan;
-#ifdef CONFIG_HAS_WAKELOCK
- struct wake_lock tegra_wake_lock;
- char tegra_wake_lock_name[32];
-#endif
};
#endif
diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c
index 0b2a431d500e..1388a8d664bb 100644
--- a/sound/soc/tegra/tegra_wm8753.c
+++ b/sound/soc/tegra/tegra_wm8753.c
@@ -68,6 +68,7 @@ struct tegra_wm8753 {
struct tegra_wm8753_platform_data *pdata;
struct regulator *audio_reg;
int gpio_requested;
+ int is_call_mode;
};
static int tegra_wm8753_hw_params(struct snd_pcm_substream *substream,
@@ -277,6 +278,98 @@ static int tegra_spdif_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static int tegra_wm8753_voice_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_card *card = codec->card;
+ struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(card);
+ int srate, mclk, i2s_daifmt, sys_clk;
+ int err, pcmdiv, vxclkdiv;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 8000:
+ case 16000:
+ case 24000:
+ case 32000:
+ case 48000:
+ case 64000:
+ case 96000:
+ mclk = 12288000;
+ break;
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ mclk = 11289600;
+ break;
+ default:
+ mclk = 12000000;
+ break;
+ }
+
+ err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
+ if (err < 0) {
+ if (!(machine->util_data.set_mclk % mclk))
+ mclk = machine->util_data.set_mclk;
+ else {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+ }
+
+ tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
+
+ i2s_daifmt = SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+
+ i2s_daifmt |= SND_SOC_DAIFMT_DSP_A;
+
+ err = snd_soc_dai_set_fmt(codec_dai, i2s_daifmt);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai fmt not set\n");
+ return err;
+ }
+
+ sys_clk = machine->util_data.set_mclk;
+
+ err = snd_soc_dai_set_sysclk(codec_dai, WM8753_PCMCLK, sys_clk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_pll(codec_dai, WM8753_PLL2, 0,
+ sys_clk, 12288000);
+
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai pll not set\n");
+ return err;
+ }
+
+ if (params_rate(params) == 8000) {
+ pcmdiv = WM8753_PCM_DIV_6;
+ /* BB expecting 2048Khz bclk */
+ vxclkdiv = WM8753_VXCLK_DIV_1;
+ } else if (params_rate(params) == 16000) {
+ pcmdiv = WM8753_PCM_DIV_3;
+ /* BB expecting 2048Khz bclk */
+ vxclkdiv = WM8753_VXCLK_DIV_2;
+ } else {
+ dev_err(card->dev, "codec_dai unsupported voice rate\n");
+ return -EINVAL;
+ }
+
+ snd_soc_dai_set_clkdiv(codec_dai, WM8753_VXCLKDIV, vxclkdiv);
+ snd_soc_dai_set_clkdiv(codec_dai, WM8753_PCMDIV, pcmdiv);
+
+ return 0;
+}
+
static int tegra_hw_free(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
@@ -287,11 +380,94 @@ static int tegra_hw_free(struct snd_pcm_substream *substream)
return 0;
}
+static int tegra_wm8753_voice_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct tegra_wm8753 *machine = snd_soc_card_get_drvdata(rtd->card);
+
+ tegra_asoc_utils_lock_clk_rate(&machine->util_data, 0);
+
+ return 0;
+}
+
+static int tegra_call_mode_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ return 0;
+}
+
+static int tegra_call_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tegra_wm8753 *machine = snd_kcontrol_chip(kcontrol);
+
+ ucontrol->value.integer.value[0] = machine->is_call_mode;
+
+ return 0;
+}
+
+static int tegra_call_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct tegra_wm8753 *machine = snd_kcontrol_chip(kcontrol);
+ int is_call_mode_new = ucontrol->value.integer.value[0];
+
+ if (machine->is_call_mode == is_call_mode_new)
+ return 0;
+
+ if (is_call_mode_new) {
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_2, 1);
+ tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_3, 1);
+ tegra20_das_connect_dap_to_dap(TEGRA20_DAS_DAP_ID_2,
+ TEGRA20_DAS_DAP_SEL_DAP3, 1, 0, 0);
+ tegra20_das_connect_dap_to_dap(TEGRA20_DAS_DAP_ID_3,
+ TEGRA20_DAS_DAP_SEL_DAP2, 0, 0, 0);
+ tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_2, 0);
+ tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_3, 0);
+#endif
+ } else {
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+ tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_2, 1);
+ tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_3, 1);
+ tegra20_das_connect_dap_to_dap(TEGRA20_DAS_DAP_ID_3,
+ TEGRA20_DAS_DAP_SEL_DAP3, 0, 0, 0);
+ tegra20_das_connect_dap_to_dap(TEGRA20_DAS_DAP_ID_2,
+ TEGRA20_DAS_DAP_SEL_DAP2, 0, 0, 0);
+ tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_2, 0);
+ tegra20_das_set_tristate(TEGRA20_DAS_DAP_ID_3, 0);
+#endif
+ }
+
+ machine->is_call_mode = is_call_mode_new;
+
+ return 1;
+}
+
+struct snd_kcontrol_new tegra_call_mode_control = {
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Call Mode Switch",
+ .private_value = 0xffff,
+ .info = tegra_call_mode_info,
+ .get = tegra_call_mode_get,
+ .put = tegra_call_mode_put
+};
+
static struct snd_soc_ops tegra_wm8753_ops = {
.hw_params = tegra_wm8753_hw_params,
.hw_free = tegra_hw_free,
};
+static struct snd_soc_ops tegra_wm8753_voice_ops = {
+ .hw_params = tegra_wm8753_voice_hw_params,
+ .hw_free = tegra_wm8753_voice_hw_free,
+};
+
static struct snd_soc_ops tegra_bt_sco_ops = {
.hw_params = tegra_bt_sco_hw_params,
.hw_free = tegra_hw_free,
@@ -514,6 +690,12 @@ static int tegra_wm8753_init(struct snd_soc_pcm_runtime *rtd)
tegra_wm8753_hp_jack_pins);
#endif
+ /* Add call mode switch control */
+ ret = snd_ctl_add(codec->card->snd_card,
+ snd_ctl_new1(&tegra_call_mode_control, machine));
+ if (ret < 0)
+ return ret;
+
snd_soc_dapm_nc_pin(dapm, "ACIN");
snd_soc_dapm_nc_pin(dapm, "ACOP");
snd_soc_dapm_nc_pin(dapm, "OUT3");
@@ -555,6 +737,15 @@ static struct snd_soc_dai_link tegra_wm8753_dai[] = {
.ops = &tegra_bt_sco_ops,
},
#endif
+ {
+ .name = "VOICE CALL",
+ .stream_name = "VOICE CALL PCM",
+ .codec_name = "wm8753-codec.4-001a",
+ .platform_name = "tegra-pcm-audio",
+ .cpu_dai_name = "dit-hifi",
+ .codec_dai_name = "wm8753-voice",
+ .ops = &tegra_wm8753_voice_ops,
+ },
};
static struct snd_soc_card snd_soc_tegra_wm8753 = {
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index bdd355574066..80b05dcd654c 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -30,6 +30,7 @@
#include <asm/mach-types.h>
+#include <linux/clk.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
@@ -73,6 +74,7 @@ struct tegra_wm8903 {
#ifdef CONFIG_SWITCH
int jack_status;
#endif
+ enum snd_soc_bias_level bias_level;
};
static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream,
@@ -86,6 +88,8 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream,
struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
int srate, mclk, i2s_daifmt;
int err;
+ struct clk *clk_m;
+ int rate;
srate = params_rate(params);
switch (srate) {
@@ -98,10 +102,33 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream,
mclk = 256 * srate;
break;
}
+
+
+
+ clk_m = clk_get_sys(NULL, "clk_m");
+ if (IS_ERR(clk_m)) {
+ dev_err(card->dev, "Can't retrieve clk clk_m\n");
+ err = PTR_ERR(clk_m);
+ return err;
+ }
+ rate = clk_get_rate(clk_m);
+ printk("extern1 rate=%d\n",rate);
+
+#if TEGRA30_I2S_MASTER_PLAYBACK
/* FIXME: Codec only requires >= 3MHz if OSR==0 */
while (mclk < 6000000)
mclk *= 2;
+ i2s_daifmt = SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS;
+#else
+ mclk = rate;
+
+ i2s_daifmt = SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBM_CFM;
+#endif
+
+
err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
if (err < 0) {
if (!(machine->util_data.set_mclk % mclk))
@@ -114,9 +141,6 @@ static int tegra_wm8903_hw_params(struct snd_pcm_substream *substream,
tegra_asoc_utils_lock_clk_rate(&machine->util_data, 1);
- i2s_daifmt = SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CBS_CFS;
-
/* Use DSP mode for mono on Tegra20 */
if ((params_channels(params) != 2) &&
(machine_is_ventana() || machine_is_harmony() ||
@@ -557,6 +581,8 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
struct tegra_wm8903_platform_data *pdata = machine->pdata;
int ret;
+ machine->bias_level = SND_SOC_BIAS_STANDBY;
+
if (gpio_is_valid(pdata->gpio_spkr_en)) {
ret = gpio_request(pdata->gpio_spkr_en, "spkr_en");
if (ret) {
@@ -664,6 +690,32 @@ static int tegra_wm8903_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
+static int tegra30_soc_set_bias_level(struct snd_soc_card *card,
+ enum snd_soc_bias_level level)
+{
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+
+ if (machine->bias_level == SND_SOC_BIAS_OFF &&
+ level != SND_SOC_BIAS_OFF)
+ tegra_asoc_utils_clk_enable(&machine->util_data);
+
+ return 0;
+}
+
+static int tegra30_soc_set_bias_level_post(struct snd_soc_card *card,
+ enum snd_soc_bias_level level)
+{
+ struct tegra_wm8903 *machine = snd_soc_card_get_drvdata(card);
+
+ if (machine->bias_level != SND_SOC_BIAS_OFF &&
+ level == SND_SOC_BIAS_OFF)
+ tegra_asoc_utils_clk_disable(&machine->util_data);
+
+ machine->bias_level = level;
+
+ return 0 ;
+}
+
static struct snd_soc_dai_link tegra_wm8903_dai[] = {
{
.name = "WM8903",
@@ -699,6 +751,8 @@ static struct snd_soc_card snd_soc_tegra_wm8903 = {
.name = "tegra-wm8903",
.dai_link = tegra_wm8903_dai,
.num_links = ARRAY_SIZE(tegra_wm8903_dai),
+ //.set_bias_level = tegra30_soc_set_bias_level,
+ //.set_bias_level_post = tegra30_soc_set_bias_level_post,
};
static __devinit int tegra_wm8903_driver_probe(struct platform_device *pdev)
diff --git a/sound/usb/usx2y/usb_stream.c b/sound/usb/usx2y/usb_stream.c
index c400ade3ff08..1e7a47a86605 100644
--- a/sound/usb/usx2y/usb_stream.c
+++ b/sound/usb/usx2y/usb_stream.c
@@ -674,7 +674,7 @@ dotry:
inurb->transfer_buffer_length =
inurb->number_of_packets *
inurb->iso_frame_desc[0].length;
- preempt_disable();
+
if (u == 0) {
int now;
struct usb_device *dev = inurb->dev;
@@ -686,19 +686,17 @@ dotry:
}
err = usb_submit_urb(inurb, GFP_ATOMIC);
if (err < 0) {
- preempt_enable();
snd_printk(KERN_ERR"usb_submit_urb(sk->inurb[%i])"
" returned %i\n", u, err);
return err;
}
err = usb_submit_urb(outurb, GFP_ATOMIC);
if (err < 0) {
- preempt_enable();
snd_printk(KERN_ERR"usb_submit_urb(sk->outurb[%i])"
" returned %i\n", u, err);
return err;
}
- preempt_enable();
+
if (inurb->start_frame != outurb->start_frame) {
snd_printd(KERN_DEBUG
"u[%i] start_frames differ in:%u out:%u\n",
diff --git a/virt/kvm/assigned-dev.c b/virt/kvm/assigned-dev.c
index 4e9eaeb518c7..af7910228fb1 100644
--- a/virt/kvm/assigned-dev.c
+++ b/virt/kvm/assigned-dev.c
@@ -17,6 +17,8 @@
#include <linux/pci.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
+#include <linux/namei.h>
+#include <linux/fs.h>
#include "irq.h"
static struct kvm_assigned_dev_kernel *kvm_find_assigned_dev(struct list_head *head,
@@ -474,12 +476,76 @@ out:
return r;
}
+/*
+ * We want to test whether the caller has been granted permissions to
+ * use this device. To be able to configure and control the device,
+ * the user needs access to PCI configuration space and BAR resources.
+ * These are accessed through PCI sysfs. PCI config space is often
+ * passed to the process calling this ioctl via file descriptor, so we
+ * can't rely on access to that file. We can check for permissions
+ * on each of the BAR resource files, which is a pretty clear
+ * indicator that the user has been granted access to the device.
+ */
+static int probe_sysfs_permissions(struct pci_dev *dev)
+{
+#ifdef CONFIG_SYSFS
+ int i;
+ bool bar_found = false;
+
+ for (i = PCI_STD_RESOURCES; i <= PCI_STD_RESOURCE_END; i++) {
+ char *kpath, *syspath;
+ struct path path;
+ struct inode *inode;
+ int r;
+
+ if (!pci_resource_len(dev, i))
+ continue;
+
+ kpath = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
+ if (!kpath)
+ return -ENOMEM;
+
+ /* Per sysfs-rules, sysfs is always at /sys */
+ syspath = kasprintf(GFP_KERNEL, "/sys%s/resource%d", kpath, i);
+ kfree(kpath);
+ if (!syspath)
+ return -ENOMEM;
+
+ r = kern_path(syspath, LOOKUP_FOLLOW, &path);
+ kfree(syspath);
+ if (r)
+ return r;
+
+ inode = path.dentry->d_inode;
+
+ r = inode_permission(inode, MAY_READ | MAY_WRITE | MAY_ACCESS);
+ path_put(&path);
+ if (r)
+ return r;
+
+ bar_found = true;
+ }
+
+ /* If no resources, probably something special */
+ if (!bar_found)
+ return -EPERM;
+
+ return 0;
+#else
+ return -EINVAL; /* No way to control the device without sysfs */
+#endif
+}
+
static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
struct kvm_assigned_pci_dev *assigned_dev)
{
int r = 0, idx;
struct kvm_assigned_dev_kernel *match;
struct pci_dev *dev;
+ u8 header_type;
+
+ if (!(assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU))
+ return -EINVAL;
mutex_lock(&kvm->lock);
idx = srcu_read_lock(&kvm->srcu);
@@ -507,6 +573,18 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
r = -EINVAL;
goto out_free;
}
+
+ /* Don't allow bridges to be assigned */
+ pci_read_config_byte(dev, PCI_HEADER_TYPE, &header_type);
+ if ((header_type & PCI_HEADER_TYPE) != PCI_HEADER_TYPE_NORMAL) {
+ r = -EPERM;
+ goto out_put;
+ }
+
+ r = probe_sysfs_permissions(dev);
+ if (r)
+ goto out_put;
+
if (pci_enable_device(dev)) {
printk(KERN_INFO "%s: Could not enable PCI device\n", __func__);
r = -EBUSY;
@@ -538,16 +616,14 @@ static int kvm_vm_ioctl_assign_device(struct kvm *kvm,
list_add(&match->list, &kvm->arch.assigned_dev_head);
- if (assigned_dev->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU) {
- if (!kvm->arch.iommu_domain) {
- r = kvm_iommu_map_guest(kvm);
- if (r)
- goto out_list_del;
- }
- r = kvm_assign_device(kvm, match);
+ if (!kvm->arch.iommu_domain) {
+ r = kvm_iommu_map_guest(kvm);
if (r)
goto out_list_del;
}
+ r = kvm_assign_device(kvm, match);
+ if (r)
+ goto out_list_del;
out:
srcu_read_unlock(&kvm->srcu, idx);
@@ -587,8 +663,7 @@ static int kvm_vm_ioctl_deassign_device(struct kvm *kvm,
goto out;
}
- if (match->flags & KVM_DEV_ASSIGN_ENABLE_IOMMU)
- kvm_deassign_device(kvm, match);
+ kvm_deassign_device(kvm, match);
kvm_free_assigned_device(kvm, match);