diff options
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 @@ -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(®); 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, ¶m); + + //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, ®_contents, 1); + if (retval) + return retval; + reg_contents = reg_contents | bits; + retval = rmi_write_block(rmi_dev, address, ®_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, ®_contents, 1); + if (retval) + return retval; + reg_contents = reg_contents & ~bits; + retval = rmi_write_block(rmi_dev, address, ®_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, ®_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(®_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); |